implemented buttons and projector shutdown

This commit is contained in:
2026-09-04 13:55:51 +02:00
parent 68b4e10db3
commit b91703f39d
3 changed files with 68 additions and 26 deletions
+1
View File
@@ -47,6 +47,7 @@
"cwd": "${workspaceFolder}",
"console": "integratedTerminal",
"env": {
"PYTHONPATH": "${workspaceFolder}",
"INTERVAL" : "15",
"LOG_FILE": "/tmp/upsmon.log",
"LOG_FILE_LVL": "WARNING",
+62 -24
View File
@@ -2,7 +2,7 @@ import logging
import os
import sys
import time
from typing import Dict, List, Optional, Type, TypeVar, Set, Any
from typing import List, Optional, Type, TypeVar, Set, Any, Callable
from uuid import UUID
import projrequest
@@ -22,28 +22,48 @@ LOGGER: logging.Logger
DISCOVERY_PREFIX = "homeassistant"
class HAsensor(BaseModel):
class HAentity(BaseModel):
name: str
unique_id: str
state_topic: str
device: Any
entity_type: str = Field(exclude=True, default="sensor")
class HAsensor(HAentity):
value_template: Optional[str] = None
unit_of_measurement: Optional[str] = None
state_topic: str
device_class: Optional[str] = None
state_class: Optional[str] = None
icon: Optional[str] = None
device: Any
sensor_type: str = Field(exclude = True, default="sensor")
request: Type = Field(exclude=True)
class HAbutton(HAentity):
command_topic: str
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]
name: str
manufacturer: str
model: str
via_device: str | None = None
sensors: List[HAsensor] = Field(exclude=True, default_factory=list)
entities: List[HAentity] = Field(exclude=True, default_factory=list)
short_name: str = Field(exclude=True)
def on_shutwown(client: mqtt_client.Client, userdata: Any, msg: mqtt_client.MQTTMessage) -> None:
LOGGER.info("Shutdown command received")
if msg and msg.payload.decode() == "shutdown":
LOGGER.info("Shutting down projector")
projector: projrequest.ProjectorConnection = projrequest.ProjectorConnection(
ip=env['PROJECTOR_IP'],
username=env['PROJECTOR_USER'],
password=env['PROJECTOR_PASSWORD']
)
projector.shutdown()
##################
###### MAIN ######
##################
@@ -79,7 +99,18 @@ def main() -> int:
short_name="sony_projector"
)
sony_device.sensors.append(HAsensor (
sony_device.entities.append(HAbutton (
name="Power Off",
unique_id=f"{sony_device.short_name}_power_off",
command_topic=f"{sony_device.short_name}/commands",
icon="mdi:power",
payload_press="shutdown",
entity_type="button",
callback=on_shutwown,
device=sony_device
))
sony_device.entities.append(HAsensor (
name="Free DCP Storage",
unique_id=f"{sony_device.short_name}_free_storage",
state_topic=f"{sony_device.short_name}/storage_info",
@@ -92,7 +123,7 @@ def main() -> int:
device=sony_device
))
sony_device.sensors.append(HAsensor (
sony_device.entities.append(HAsensor (
name="Used DCP Storage",
unique_id=f"{sony_device.short_name}_used_storage",
state_topic=f"{sony_device.short_name}/storage_info",
@@ -105,7 +136,7 @@ def main() -> int:
device=sony_device
))
sony_device.sensors.append(HAsensor (
sony_device.entities.append(HAsensor (
name="Play State",
unique_id=f"{sony_device.short_name}_play_state",
state_topic=f"{sony_device.short_name}/show_info",
@@ -115,7 +146,7 @@ def main() -> int:
device=sony_device
))
sony_device.sensors.append(HAsensor (
sony_device.entities.append(HAsensor (
name="Playback Mode",
unique_id=f"{sony_device.short_name}_playback_mode",
state_topic=f"{sony_device.short_name}/show_info",
@@ -125,7 +156,7 @@ def main() -> int:
device=sony_device
))
sony_device.sensors.append(HAsensor (
sony_device.entities.append(HAsensor (
name="Total Duration",
unique_id=f"{sony_device.short_name}_total_duration",
state_topic=f"{sony_device.short_name}/show_info",
@@ -138,7 +169,7 @@ def main() -> int:
device=sony_device
))
sony_device.sensors.append(HAsensor (
sony_device.entities.append(HAsensor (
name="Elapsed Time",
unique_id=f"{sony_device.short_name}_elapsed_time",
state_topic=f"{sony_device.short_name}/show_info",
@@ -151,7 +182,7 @@ def main() -> int:
device=sony_device
))
sony_device.sensors.append(HAsensor (
sony_device.entities.append(HAsensor (
name="Remaining Time",
unique_id=f"{sony_device.short_name}_remaining_time",
state_topic=f"{sony_device.short_name}/show_info",
@@ -164,7 +195,7 @@ def main() -> int:
device=sony_device
))
sony_device.sensors.append(HAsensor (
sony_device.entities.append(HAsensor (
name="Event Title",
unique_id=f"{sony_device.short_name}_event_title",
state_topic=f"{sony_device.short_name}/show_info",
@@ -188,7 +219,7 @@ def main() -> int:
via_device=sony_device.identifiers[0]
)
lamp_device.sensors.append(HAsensor (
lamp_device.entities.append(HAsensor (
name=f"Hours of Use",
unique_id=f"{lamp_device.short_name}_hours",
state_topic=f"{sony_device.short_name}/{lamp_device.short_name}/state",
@@ -201,7 +232,7 @@ def main() -> int:
device=lamp_device
))
lamp_device.sensors.append(HAsensor (
lamp_device.entities.append(HAsensor (
name=f"Power",
unique_id=f"{lamp_device.short_name}_power",
state_topic=f"{sony_device.short_name}/{lamp_device.short_name}/state",
@@ -214,7 +245,7 @@ def main() -> int:
device=lamp_device
))
lamp_device.sensors.append(HAsensor (
lamp_device.entities.append(HAsensor (
name=f"Status",
unique_id=f"{lamp_device.short_name}_status",
state_topic=f"{sony_device.short_name}/{lamp_device.short_name}/state",
@@ -225,14 +256,14 @@ def main() -> int:
device=lamp_device
))
lamp_device.sensors.append(HAsensor (
lamp_device.entities.append(HAsensor (
name=f"Lamp ON",
unique_id=f"{lamp_device.short_name}_lampon",
state_topic=f"{sony_device.short_name}/{lamp_device.short_name}/state",
value_template="{{ value_json.LampOn }}",
device_class="power",
icon="mdi:lightbulb-on-outline",
sensor_type="binary_sensor",
entity_type="binary_sensor",
request=PrjLampInformation,
device=lamp_device
))
@@ -242,10 +273,16 @@ def main() -> int:
all_devices: List[HAdevice] = [sony_device, *lamp_devices]
for d in all_devices:
for s in d.sensors:
mqtt.publish(topic=f"{DISCOVERY_PREFIX}/{s.sensor_type}/{s.unique_id}/config",
for s in d.entities:
# Publish config mesasge
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)
playlists: SPLList | None = None
@@ -257,12 +294,13 @@ def main() -> int:
# update projector states
lamp_num: int = 0
for d in all_devices:
states: Set[Type] = set([s.request for s in d.sensors])
sensors: List[HAsensor] = [x for x in d.entities if isinstance(x, HAsensor)]
states: Set[Type] = set([s.request for s in sensors])
for s in states:
# include lamp id in the requests parameters
if s == PrjLampInformation:
resp = projector.request(s, reqParams={"lampId": lamp_num})
resp = projector.request(s, addlParams={"lampId": lamp_num})
lamp_num += 1
else:
resp = projector.request(s)
@@ -286,7 +324,7 @@ def main() -> int:
# send only one message for sensor with this type of request
last_cls: Type = SMSMessage
# filter sensors of this device per this type of request
for sens in filter(lambda x: x.request == s, d.sensors):
for sens in filter(lambda x: x.request == s, sensors):
if last_cls == sens.request:
continue
if isinstance(resp, SMSMessage):
+5 -2
View File
@@ -43,13 +43,13 @@ class ProjectorConnection():
return xmltodict.parse(response.text.replace("%2F", "/").replace("%3A", ":")) # Common containert for all messages
return {}
def request(self, cls: Type[T], reqParams: Dict[str, Any] = {}) -> T | None:
def request(self, cls: Type[T], addlParams: Dict[str, Any] = {}) -> T | None:
rv: dict
if TEST:
with open(f'{getcwd()}/projmon/xmlresponses/showstatus.xml', 'r') as fd:
rv = xmltodict.parse(fd.read().replace("%2F", "/").replace("%3A", ":"))
else:
rv = self.get(cls.path, cls.params | reqParams)
rv = self.get(cls.path, cls.params | addlParams)
if isinstance(rv, dict) and rv.items():
rv = rv['SMSMessage']
tt: str = rv['MessageHeader']['Type']
@@ -59,3 +59,6 @@ class ProjectorConnection():
#print(json.dumps(rv, indent = 2))
return cls.model_validate(rv)
return None
def shutdown(self) -> None:
self.get(['command', 'sms', 'shutdown'])