Implement announce as control method and allow async methods

This commit is contained in:
Edward Brown 2025-11-24 12:04:32 +00:00
parent 21100a5096
commit caa19f016d
4 changed files with 46 additions and 27 deletions

View file

@ -2,7 +2,6 @@ import aiomqtt
import asyncio
from fmtr.tools import mqtt, Constants
from fmtr.tools.json_tools import to_json
from haco import constants
from haco.obs import logger
@ -35,22 +34,16 @@ class ClientHaco(mqtt.Client):
payload = message.payload.decode()
logger.info(f"{message.topic.value}{Constants.ARROW}{payload}")
topic_command = self.device.subscriptions[message.topic.value]
echo_val = topic_command.wrap_back(message)
echo_val = await topic_command.wrap_back(message)
# Echo back as new state
topic_state = topic_command.state
if topic_state:
await topic_state.wrap_back(echo_val)
async def start(self):
while True:
try:
async with self:
for topic, data in self.device.announce.items():
data_json = to_json(data)
logger.info(f'Announcing {topic} with {data_json}')
await self.publish(topic, payload=data_json, retain=True)
await self.device.announce()
await self.publish(self.will.topic, self.ONLINE, retain=True)

View file

@ -3,8 +3,10 @@ from __future__ import annotations
from dataclasses import dataclass, field
from typing import TYPE_CHECKING
from fmtr.tools.json_tools import to_json
from haco.base import Base
from haco.capabilities import Capability
from haco.obs import logger
from haco.utils import sanitize_name
if TYPE_CHECKING:
@ -21,7 +23,7 @@ class Control(Base):
unique_id: str | None = field(default=None, init=False)
availability_topic: str | None = field(default=None, init=False)
announce: dict | None = field(default=None, metadata=dict(exclude=True))
# announce: dict | None = field(default=None, metadata=dict(exclude=True))
subscriptions: dict | None = field(default=None, metadata=dict(exclude=True), init=False)
def __post_init__(self):
@ -36,7 +38,7 @@ class Control(Base):
self.unique_id = self.get_unique_id()
self.availability_topic = self.get_availability_topic()
self.subscriptions = self.get_subscriptions()
self.announce = self.get_announce()
#self.announce = self.get_announce()
@property
def parent(self):
@ -47,7 +49,6 @@ class Control(Base):
for cap in self.capabilities:
data |= cap.announce
data = {self.announce_topic: data}
return data
@classmethod
@ -86,13 +87,16 @@ class Control(Base):
async def initialise(self):
...
async def announce2(self):
async def announce(self):
"""
Entities must be able to announce themselves to Home Assistant, e.g. when a pulldown changes its options, etc.
"""
await self.device.client.publish(self.announce_topic, self.announce)
topic = self.announce_topic
data_json = to_json(self.get_announce())
logger.info(f'Announcing {topic} with {data_json}')
await self.device.client.publish(topic, data_json, retain=True)
if __name__ == "__main__":

View file

@ -23,7 +23,7 @@ class Device(Base):
controls: List[Control] = field(default_factory=list, metadata=dict(exclude=True))
parent: ClientHaco | None = field(metadata=dict(exclude=True), init=False)
announce: dict | None = field(default=None, metadata=dict(exclude=True), init=False)
# announce: dict | None = field(default=None, metadata=dict(exclude=True), init=False)
subscriptions: dict | None = field(default=None, metadata=dict(exclude=True), init=False)
@ -34,7 +34,7 @@ class Device(Base):
for control in self.controls:
control.set_parent(self)
self.announce = self.get_announce()
#self.announce = self.get_announce()
self.subscriptions = self.get_subscriptions()
self
@ -47,12 +47,10 @@ class Device(Base):
def name_san(self) -> str:
return sanitize_name(self.name)
def get_announce(self):
async def announce(self):
for control in self.controls:
await control.announce()
data = {}
for capability in self.controls:
data |= capability.announce
return data
@property
def client(self):

View file

@ -4,6 +4,7 @@ from dataclasses import dataclass, field
from functools import cached_property
from typing import ClassVar, TYPE_CHECKING
from fmtr.tools import aio
from haco.obs import logger
if TYPE_CHECKING:
@ -76,11 +77,19 @@ class AnnounceTopicState(AnnounceTopic):
async def wrap_back(self, value):
method = getattr(self.capability.control, self.callback_name, None)
is_async = aio.is_async(method)
# test
try:
value_raw = method(value)
if is_async:
value_raw = await method(value)
else:
value_raw = method(value)
except NotImplementedError:
logger.warning(f'No method implemented for {self.topic}')
logger.warning(f'No method implemented for {self.__class__.__name__}.{self.callback_name}: {self.topic}')
return
await self.capability.control.device.client.publish(self.topic, value_raw)
@ -94,16 +103,31 @@ class AnnounceTopicCommand(AnnounceTopic):
def state(self):
return self.capability.state
def wrap_back(self, message):
async def wrap_back(self, message):
method = getattr(self.capability.control, self.callback_name, None)
is_async = aio.is_async(method)
#test
try:
value = method(message.payload.decode('utf-8'))
value = message.payload.decode('utf-8')
if is_async:
value_raw = await method(value)
else:
value_raw = method(value)
except NotImplementedError:
logger.warning(f'No method implemented for {self.topic}')
return
return value
# Echo back as new state
topic_state = self.state
if topic_state:
await topic_state.wrap_back(value_raw)
def get_subscriptions(self):
return {self.topic: self}