working title recovery from playlist and error correction
This commit is contained in:
+10
-1
@@ -15,6 +15,15 @@ class MessageHeaderWrapper(BaseModel):
|
||||
def to_datetime(cls, value: str) -> datetime:
|
||||
return datetime.fromtimestamp(int(value)/1000)
|
||||
|
||||
def __eq__(self, other):
|
||||
if not isinstance(other, MessageHeaderWrapper):
|
||||
return NotImplemented
|
||||
|
||||
return (
|
||||
self.model_dump(exclude={"Timestamp"})
|
||||
== other.model_dump(exclude={"Timestamp"})
|
||||
)
|
||||
|
||||
class SMSMessage(BaseModel):
|
||||
model_config = ConfigDict(arbitrary_types_allowed=True)
|
||||
path: ClassVar[List[str]]
|
||||
@@ -162,4 +171,4 @@ type SPLMetadataElem = SPLMetadata
|
||||
class SPLList(SMSMessage):
|
||||
path = ['show','playlist','info','list']
|
||||
|
||||
SPLMetadata: List[SPLMetadataElem] | None = None
|
||||
SPLMetadata: List[SPLMetadataElem]
|
||||
|
||||
+65
-34
@@ -2,14 +2,16 @@ import logging
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
from typing import Any, List, Optional, Type, TypeVar
|
||||
from typing import Dict, List, Optional, Type, TypeVar
|
||||
from uuid import UUID
|
||||
|
||||
import projrequest
|
||||
from commands import ShowStatus, SMSMessage, SPLList, StorageInfo
|
||||
from commands import ShowStatus, SMSMessage, SPLList, SPLMetadata, StorageInfo
|
||||
|
||||
from paho.mqtt import client as mqtt_client
|
||||
from paho.mqtt.enums import CallbackAPIVersion
|
||||
from pydantic import BaseModel, Field
|
||||
from requests import Timeout
|
||||
from utils import SignalHandler
|
||||
|
||||
T = TypeVar("T", bound=SMSMessage)
|
||||
@@ -50,10 +52,10 @@ def main() -> int:
|
||||
mqtt.connect(host=env['MQTT_HOST'], port=int(env['MQTT_PORT']))
|
||||
mqtt.loop_start()
|
||||
except Exception as e:
|
||||
LOGGER.error(e)
|
||||
LOGGER.error("%s", e)
|
||||
return 1
|
||||
finally:
|
||||
LOGGER.info(f"MQTT Connected: [{env['MQTT_HOST']}:{env['MQTT_PORT']}]")
|
||||
LOGGER.info("MQTT Connected: [%s:%s]", env['MQTT_HOST'], env['MQTT_PORT'])
|
||||
|
||||
##############################
|
||||
########## MAIN LOOP #########
|
||||
@@ -64,7 +66,7 @@ def main() -> int:
|
||||
ip=env['PROJECTOR_IP'],
|
||||
username=env['PROJECTOR_USER'],
|
||||
password=env['PROJECTOR_PASSWORD']
|
||||
)
|
||||
)
|
||||
|
||||
sony_device: HAdevice = HAdevice (
|
||||
identifiers=["sony_digitalcinema_projector"],
|
||||
@@ -162,10 +164,10 @@ def main() -> int:
|
||||
))
|
||||
|
||||
sensors.append(HAsensor (
|
||||
name="Play Title",
|
||||
unique_id=f"{sony_device.short_name}_play_title",
|
||||
state_topic=f"({sony_device.short_name})/play_title",
|
||||
value_template="{{ value_json.PlayTitle }}",
|
||||
name="Event Title",
|
||||
unique_id=f"{sony_device.short_name}_event_title",
|
||||
state_topic=f"({sony_device.short_name})/show_info",
|
||||
value_template="{{ value_json.ShowStatusDetail.CurrentEventTitle }}",
|
||||
icon="mdi:movie",
|
||||
device=sony_device,
|
||||
request=ShowStatus
|
||||
@@ -176,31 +178,60 @@ def main() -> int:
|
||||
payload=s.model_dump_json(indent=2),
|
||||
retain=True)
|
||||
|
||||
while handler.running:
|
||||
try:
|
||||
now:float = time.time()
|
||||
tt: Any = None
|
||||
ret: None | SMSMessage = None
|
||||
spls: SPLList | None = projector.request(SPLList)
|
||||
for s in sensors:
|
||||
if tt != s.request:
|
||||
tt = s.request
|
||||
ret = projector.request(s.request)
|
||||
if ret:
|
||||
mqtt.publish(topic=s.state_topic,
|
||||
payload=ret.model_dump_json(
|
||||
indent=2,
|
||||
exclude={"MessageHeader"},exclude_unset=True),
|
||||
retain=True)
|
||||
playlists: SPLList | None = None
|
||||
states: Dict[Type, SMSMessage | None] = {s.request: None for s in sensors}
|
||||
|
||||
last: float = time.time()
|
||||
cycle_time: float = last - now
|
||||
LOGGER.debug(f"Cycle Time: {cycle_time:4.3f}")
|
||||
if cycle_time < INTERVAL:
|
||||
time.sleep(INTERVAL-cycle_time)
|
||||
while handler.running:
|
||||
now:float = time.time()
|
||||
try:
|
||||
resp: None | SMSMessage = None
|
||||
# update projector states
|
||||
for s in states:
|
||||
resp = projector.request(s)
|
||||
# no responde
|
||||
if resp is None:
|
||||
continue
|
||||
|
||||
# request playlist list (hehehe)
|
||||
if playlists is None:
|
||||
playlists = projector.request(SPLList)
|
||||
|
||||
# find playlist title
|
||||
if isinstance(resp, ShowStatus) and isinstance(playlists, SPLList):
|
||||
playlist_id: UUID = resp.ShowStatusDetail.Id
|
||||
playlist: List[SPLMetadata] = list(filter(lambda p: p.Id == playlist_id, playlists.SPLMetadata))
|
||||
if len(playlist):
|
||||
resp.ShowStatusDetail.CurrentEventTitle = playlist.pop().Title
|
||||
else:
|
||||
resp.ShowStatusDetail.CurrentEventTitle = "Unknown or CPL"
|
||||
|
||||
# continue after update of playlist title in case nothing else changed
|
||||
if resp == states[s]:
|
||||
continue
|
||||
|
||||
# update sensor states
|
||||
states[s] = resp
|
||||
# send only one message for sensor with this type of request
|
||||
for s in sensors:
|
||||
if isinstance(resp, s.request):
|
||||
sts: SMSMessage = resp
|
||||
mqtt.publish(topic=s.state_topic,
|
||||
payload=sts.model_dump_json(
|
||||
indent=2,
|
||||
exclude={"MessageHeader"},
|
||||
exclude_unset=True),
|
||||
retain=True)
|
||||
break
|
||||
except Timeout as te:
|
||||
LOGGER.debug("Projector is probably shutdown: %s", te)
|
||||
except Exception as e:
|
||||
print(f"Unexpected exception: [{e}]")
|
||||
LOGGER.error("Unexpected exception: [%s]", e)
|
||||
return 1
|
||||
last: float = time.time()
|
||||
cycle_time: float = last - now
|
||||
LOGGER.debug("Cycle Time: %4.3f", cycle_time)
|
||||
if cycle_time < INTERVAL:
|
||||
time.sleep(INTERVAL-cycle_time)
|
||||
mqtt.disconnect()
|
||||
return 0
|
||||
|
||||
@@ -228,9 +259,9 @@ if __name__ == "__main__":
|
||||
cl.setFormatter(formatter)
|
||||
LOGGER.addHandler(cl)
|
||||
|
||||
LOGGER.warning(f"Routermon started on: {time.asctime()}")
|
||||
LOGGER.info(f"Routermon BUILD: {env.get("VER", "Test")}")
|
||||
|
||||
LOGGER.warning("Projmon started on: %s", time.asctime())
|
||||
LOGGER.info("Projmon BUILD: %s", env.get("VER", "Test"))
|
||||
|
||||
while main():
|
||||
LOGGER.error("Main thread exited unexpectedly")
|
||||
time.sleep(15)
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
import requests
|
||||
import urllib3
|
||||
|
||||
# Suppress only the single warning from urllib3.
|
||||
urllib3.disable_warnings(category=urllib3.exceptions.InsecureRequestWarning)
|
||||
import xmltodict
|
||||
from requests.auth import HTTPBasicAuth
|
||||
from os import getcwd
|
||||
import json
|
||||
from typing import Any, Dict, List, Type, TypeVar
|
||||
|
||||
import xmltodict
|
||||
from commands import SMSMessage
|
||||
from requests.auth import HTTPBasicAuth
|
||||
|
||||
from typing import Dict, List, Any, Type, TypeVar
|
||||
T = TypeVar("T", bound=SMSMessage)
|
||||
|
||||
TEST = False
|
||||
@@ -25,7 +25,6 @@ class ProjectorConnection():
|
||||
self.username = username
|
||||
self.password = password
|
||||
self.auth = HTTPBasicAuth(username=self.username, password=self.password)
|
||||
pass
|
||||
|
||||
def get(self, path: List[str], params: Dict[str, str] | None = None) -> Dict[str, Any]:
|
||||
response: requests.Response = requests.get(
|
||||
@@ -53,8 +52,8 @@ class ProjectorConnection():
|
||||
rv = self.get(cls.path, cls.params)
|
||||
if isinstance(rv, dict) and rv.items():
|
||||
rv = rv['SMSMessage']
|
||||
type: str = rv['MessageHeader']['Type']
|
||||
rv.update(**rv['MessageBody'][type])
|
||||
tt: str = rv['MessageHeader']['Type']
|
||||
rv.update(**rv['MessageBody'][tt])
|
||||
del rv['MessageBody']
|
||||
del rv['@xmlns']
|
||||
#print(json.dumps(rv, indent = 2))
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
<!--
|
||||
{
|
||||
"GET": {
|
||||
"scheme": "https",
|
||||
"host": "192.168.31.10",
|
||||
"filename": "/status/alert/log",
|
||||
"query": {
|
||||
"formatDate": "true"
|
||||
},
|
||||
"remote": {
|
||||
"Indirizzo": "192.168.31.10:443"
|
||||
}
|
||||
}
|
||||
}
|
||||
-->
|
||||
|
||||
<SMSMessage xmlns="http://xmlns.sony.net/d-cinema/sms/2007b">
|
||||
<MessageHeader>
|
||||
<Id>-1</Id>
|
||||
<Type>EventLog</Type>
|
||||
<Timestamp>1787752196399</Timestamp>
|
||||
<Source>LSM-100v2</Source>
|
||||
</MessageHeader>
|
||||
<MessageBody>
|
||||
<EventLog>
|
||||
<LogEventItem>
|
||||
<Time>1787750400</Time>
|
||||
<Index>0</Index>
|
||||
<Level>ERROR</Level>
|
||||
<Code>3050</Code>
|
||||
<Message>Failed to connect to CP750.</Message>
|
||||
<TimeForFormatDate>2026-08-26T15:20:00.000+02:00</TimeForFormatDate>
|
||||
</LogEventItem>
|
||||
<LogEventItem>
|
||||
<Time>1787750421</Time>
|
||||
<Index>1</Index>
|
||||
<Level>WARN</Level>
|
||||
<Code>3014</Code>
|
||||
<Message>Projector not connected</Message>
|
||||
<TimeForFormatDate>2026-08-26T15:20:21.000+02:00</TimeForFormatDate>
|
||||
</LogEventItem>
|
||||
<LogEventItem>
|
||||
<Time>1787750421</Time>
|
||||
<Index>2</Index>
|
||||
<Level>WARN</Level>
|
||||
<Code>3015</Code>
|
||||
<Message>PCIe cable not connected</Message>
|
||||
<TimeForFormatDate>2026-08-26T15:20:21.000+02:00</TimeForFormatDate>
|
||||
</LogEventItem>
|
||||
</EventLog>
|
||||
</MessageBody>
|
||||
</SMSMessage>
|
||||
@@ -0,0 +1,40 @@
|
||||
<!--
|
||||
{
|
||||
"GET": {
|
||||
"scheme": "https",
|
||||
"host": "192.168.31.10",
|
||||
"filename": "/command/projector/maintenance/lamp/info/get",
|
||||
"query": {
|
||||
"lampId": "05"
|
||||
},
|
||||
"remote": {
|
||||
"Indirizzo": "192.168.31.10:443"
|
||||
}
|
||||
}
|
||||
}
|
||||
-->
|
||||
|
||||
<SMSMessage xmlns="http://xmlns.sony.net/d-cinema/sms/2007b">
|
||||
<MessageHeader>
|
||||
<Id>-1</Id>
|
||||
<Type>PrjLampInformation</Type>
|
||||
<Timestamp>1787752366456</Timestamp>
|
||||
<Source>LSM-100v2</Source>
|
||||
</MessageHeader>
|
||||
<MessageBody>
|
||||
<PrjLampInformation>
|
||||
<ProjectorId></ProjectorId>
|
||||
<LampId></LampId>
|
||||
<HoursOfUse></HoursOfUse>
|
||||
<LampSerialNumber></LampSerialNumber>
|
||||
<LampMaximumWattage>0</LampMaximumWattage>
|
||||
<LampWattage>-1</LampWattage>
|
||||
<LampLuminanceDecayRatio></LampLuminanceDecayRatio>
|
||||
<LampPower></LampPower>
|
||||
<LampSelect></LampSelect>
|
||||
<LuminanceShutterControl></LuminanceShutterControl>
|
||||
<LampOn></LampOn>
|
||||
<LampStatus></LampStatus>
|
||||
</PrjLampInformation>
|
||||
</MessageBody>
|
||||
</SMSMessage>
|
||||
Reference in New Issue
Block a user