Compare commits

...
2 Commits
Author SHA1 Message Date
Obbart d2287c5e81 fixed recurse for MART 2026-09-28 22:58:43 +02:00
Obbart d35b7d53fd consolidate commands topic 2026-09-18 14:39:25 +02:00
5 changed files with 34 additions and 31 deletions
+3 -1
View File
@@ -67,7 +67,9 @@
"MQTT_USER": "projmon",
"MQTT_PASSWORD": "projmon",
"MQTT_HOST": "192.168.31.26",
"MQTT_PORT": "1883"
"MQTT_PORT": "1883",
"MQTT_COMMAND_TOPIC": "projmon/commands",
"MQTT_RESPONSE_TOPIC": "projmon/responses"
}
}
]
+2
View File
@@ -171,6 +171,8 @@ services:
- MQTT_PASSWORD=projmon
- MQTT_HOST=192.168.31.26
- MQTT_PORT=1883
- MQTT_COMMAND_TOPIC=projmon/commands
- MQTT_RESPONSE_TOPIC=projmon/responses
- LOG_FILE=/tmp/projmon.log
- LOG_FILE_LVL=WARNING
- LOG_CLI_LVL=INFO
+11 -14
View File
@@ -66,8 +66,7 @@ def initialize_ftp_servers(file_path: str) -> FTPServers:
name="MART",
host="192.168.31.15",
port=21,
notify_filters=["TLR", "TRL", "FTR"],
cwd='TRAILER',
notify_filters=["FTR"],
recurse=True
))
@@ -77,7 +76,7 @@ def initialize_ftp_servers(file_path: str) -> FTPServers:
port=21,
username="dcpftp",
password="dcpftp",
notify_filters=["TLR", "TRL", "FTR"]
notify_filters=["FTR"]
))
save_ftp_servers_to_json(dcp_servers, file_path)
@@ -101,7 +100,7 @@ def list_new_directories(ftp_server: FTPServer) -> DirDescriptors:
with ftp_server.connect() as ftp_connection:
if ftp_server.cwd:
ftp_connection.cwd(ftp_server.cwd)
new_dirs: DirDescriptors = {}
curr_dirs: DirDescriptors = {}
try:
# main method, retrieve directories with details
for d in ftp_connection.mlsd(): # List directory contents
@@ -110,28 +109,26 @@ def list_new_directories(ftp_server: FTPServer) -> DirDescriptors:
modified_time_str: str = dir_info.get('modify', '')
modified_time: datetime = datetime.strptime(modified_time_str, '%Y%m%d%H%M%S') if modified_time_str else datetime.now()
unique: str = dir_info.get('unique', str(datetime.now().timestamp()))
new_dirs[unique] = FTPDirInfo(name=dir_name, modified_time=modified_time)
curr_dirs[unique] = FTPDirInfo(name=dir_name, modified_time=modified_time)
except error_perm:
# revert to simple directory listing mode
for d in ftp_connection.nlst():
dir_name = d
unique = uuid5(namespace=NAMESPACE_OID, name=dir_name).hex.upper()[:11]
new_dirs[unique] = FTPDirInfo(name=dir_name)
curr_dirs[unique] = FTPDirInfo(name=dir_name)
diff_dirs: DirDescriptors = { k:v for k,v in new_dirs.items() if k not in ftp_server.dirs }
# recurse only in difference directory so it is faster
if ftp_server.recurse:
base_dir: str = ftp_connection.pwd()
recurse_dirs: DirDescriptors = copy.deepcopy(diff_dirs)
recurse_dirs: DirDescriptors = copy.deepcopy(curr_dirs)
for u, d in recurse_dirs.items():
ftp_connection.cwd(base_dir)
ftp_connection.cwd(d.name)
explore_dir(ftp_connection=ftp_connection, dir_descriptors=diff_dirs)
del diff_dirs[u]
new_dirs |= diff_dirs
explore_dir(ftp_connection=ftp_connection, dir_descriptors=curr_dirs)
del curr_dirs[u]
ftp_server.dirs = new_dirs # Update the server's directory list with the new directories
diff_dirs: DirDescriptors = { k:v for k,v in curr_dirs.items() if k not in ftp_server.dirs }
ftp_server.dirs = curr_dirs # Update the server's directory list with the new directories
return diff_dirs # Return the difference
def check_new_titles(mqtt: mqtt_client.Client, topic: str, file_path: str, logger: Logger) -> None:
@@ -182,4 +179,4 @@ if __name__ == "__main__":
this_dirs: DirDescriptors = {}
explore_dir(ftp_connection, this_dirs)
print(this_dirs)
-1
View File
@@ -26,7 +26,6 @@ class HAbutton(HAentity):
payload_press: str = ""
device_class: Optional[str] = None
icon: Optional[str] = None
callback: Optional[Callable[[mqtt_client.Client, Any, mqtt_client.MQTTMessage], None]] = Field(exclude=True, default=None)
class HAdevice(BaseModel):
identifiers: List[str]
+18 -15
View File
@@ -28,6 +28,8 @@ INTERVAL = int(env['INTERVAL'])
DCP_INTERVAL = int(env['DCP_INTERVAL'])
DCP_SERVER_FILE = str(env['DCP_SERVER_FILE'])
DCP_NEW_TOPIC = str(env['DCP_NEW_TOPIC'])
MQTT_COMMAND_TOPIC = str(env['MQTT_COMMAND_TOPIC'])
MQTT_RESPONSE_TOPIC = str(env['MQTT_RESPONSE_TOPIC'])
projector: projrequest.ProjectorConnection = projrequest.ProjectorConnection(
ip=env['PROJECTOR_IP'],
@@ -48,9 +50,12 @@ def on_shutwown(client: mqtt_client.Client, userdata: Any, msg: mqtt_client.MQTT
def on_command(client: mqtt_client.Client, userdata: Any, msg: mqtt_client.MQTTMessage) -> None:
try:
content: Dict[str, Any] = json.loads(msg.payload.decode('utf-8'))
cmd: str = content["cmd"]
params: str = content["params"]
cmd: str = content.get("cmd", "")
params: Dict[str, Any] = content.get("params", {})
match cmd:
case "shutdown":
LOGGER.info("Shutting down projector")
projector.shutdown()
case "ingest":
notify: NotificationResponse = NotificationResponse.model_validate(params)
with open(DCP_SERVER_FILE, 'r') as fd:
@@ -72,13 +77,14 @@ def on_command(client: mqtt_client.Client, userdata: Any, msg: mqtt_client.MQTTM
data=SMSMessage(SMSMessage=ingest_command)
)
if not error_response.MessageBody:
LOGGER.warning("Invalid error response: [%s]", error_response.model_dump_json())
LOGGER.warning("Invalid error response:\n[%s]", error_response.model_dump_json())
return
if error_response.MessageBody.ErrorResponse.Code != 100:
LOGGER.error("Unable to ingest Title [%s]: [%s]", dir_name, error_response.MessageBody.ErrorResponse.Message)
else:
LOGGER.info("Started ingest of Title: [%s] from [%s]", dir_name, this_server.name)
case _:
LOGGER.error(f"Invalid or empty command: [{cmd}]")
except json.JSONDecodeError as de:
LOGGER.error("Unable to decode command json: [%s]", de)
except KeyError as ke:
@@ -117,11 +123,10 @@ def main() -> int:
sony_device.entities.append(HAbutton (
name="Power Off",
unique_id=f"{sony_device.short_name}_power_off",
command_topic=f"{sony_device.short_name}/shutdown",
command_topic=MQTT_COMMAND_TOPIC,
icon="mdi:power",
payload_press="shutdown",
payload_press=json.dumps({"cmd":"shutdown", "params":{}}),
entity_type="button",
callback=on_shutwown,
device=sony_device
))
@@ -304,16 +309,11 @@ def main() -> int:
mqtt.publish(topic=f"{DISCOVERY_PREFIX}/{s.entity_type}/{s.unique_id}/config",
payload=s.model_dump_json(indent=1, exclude_none=True),
retain=True)
# subscrie to command topic if this is a button
if isinstance(s, HAbutton):
mqtt.subscribe(topic=s.command_topic)
if s.callback is not None:
mqtt.message_callback_add(sub=s.command_topic, callback=s.callback)
time.sleep(0.2)
# Subscrie to MQTT topics for generic commands
mqtt.subscribe(topic=f"{sony_device.short_name}/commands")
mqtt.message_callback_add(sub=f"{sony_device.short_name}/commands", callback=on_command)
# Subscribe to MQTT topics for generic commands from buttons and other entities
mqtt.subscribe(topic=MQTT_COMMAND_TOPIC)
mqtt.message_callback_add(sub=MQTT_COMMAND_TOPIC, callback=on_command)
playlists: SPLList | None = None
while handler.running:
@@ -367,6 +367,8 @@ def main() -> int:
last_cls = sens.request
except (Timeout, ConnectionError) as ce:
LOGGER.debug("Projector is probably shutdown: [%s]", ce)
except KeyError as ke:
LOGGER.error("Key not found in dictionary: [%s]", ke)
except Exception as e:
LOGGER.error("Unexpected exception: [%s]", e)
return 1
@@ -384,6 +386,7 @@ def main() -> int:
LOGGER.debug("Cycle Time: %4.3f", cycle_time)
if cycle_time < INTERVAL:
time.sleep(INTERVAL-cycle_time)
mqtt.disconnect()
return 0