138 lines
3.8 KiB
Python
138 lines
3.8 KiB
Python
import uasyncio as asyncio
|
|
|
|
from homie.constants import STRING, T_SET, TRUE, FALSE
|
|
from homie.validator import payload_is_valid
|
|
|
|
|
|
class BaseProperty:
|
|
def __init__(
|
|
self,
|
|
id,
|
|
name=None,
|
|
settable=False,
|
|
retained=True,
|
|
unit=None,
|
|
datatype=STRING,
|
|
format=None,
|
|
default=None,
|
|
restore=True,
|
|
on_message=None,
|
|
):
|
|
self._value = default
|
|
|
|
self.id = id
|
|
self.name = name
|
|
self.settable = settable
|
|
self.retained = retained
|
|
self.unit = unit
|
|
self.datatype = datatype
|
|
self.format = format
|
|
self.restore = restore
|
|
self.on_message = on_message
|
|
|
|
self.topic = None
|
|
self.node = None
|
|
|
|
# Keep for backward compatibility
|
|
@property
|
|
def data(self):
|
|
return self._value
|
|
|
|
# Keep for backward compatibility
|
|
@data.setter
|
|
def data(self, value):
|
|
self.value = value
|
|
|
|
@property
|
|
def value(self):
|
|
return self._value
|
|
|
|
@value.setter
|
|
def value(self, value):
|
|
""" Set value if changed and publish to mqtt """
|
|
if value != self._value:
|
|
self._value = value
|
|
self.publish()
|
|
|
|
def set_topic(self):
|
|
self.topic = "{}/{}/{}".format(
|
|
self.node.device.dtopic,
|
|
self.node.id,
|
|
self.id
|
|
)
|
|
|
|
def publish(self):
|
|
asyncio.create_task(
|
|
self.node.device.publish(
|
|
self.topic,
|
|
self.value,
|
|
self.retained
|
|
)
|
|
)
|
|
|
|
async def subscribe(self):
|
|
# Restore from topic with retained message on device start
|
|
if self.restore and self.node.device.first_start is True:
|
|
self.node.device.callback_topics[self.topic] = self.restore_handler
|
|
await self.node.device.subscribe(self.topic)
|
|
|
|
# Subscribe to settable (/set) topics
|
|
if self.settable is True:
|
|
topic = "{}/set".format(self.topic)
|
|
self.node.device.callback_topics[topic] = self.message_handler
|
|
await self.node.device.subscribe(topic)
|
|
|
|
def restore_handler(self, topic, payload, retained):
|
|
""" Gets called when the property should be restored from mqtt """
|
|
# Retained messages are not allowed on /set topics
|
|
if topic.endswith(T_SET):
|
|
return
|
|
|
|
# Unsubscribe from topic and remove the callback handler
|
|
asyncio.create_task(self.node.device.unsubscribe(topic))
|
|
del self.node.device.callback_topics[topic]
|
|
|
|
if payload_is_valid(self, payload):
|
|
if payload != self._value:
|
|
if self.on_message:
|
|
self.on_message(topic, payload, retained)
|
|
|
|
self._value = payload
|
|
|
|
def message_handler(self, topic, payload, retained):
|
|
""" Gets called when the property receive a message on /set topic """
|
|
# No reatained messages allowed on /set topics
|
|
if retained:
|
|
return
|
|
|
|
if payload_is_valid(self, payload):
|
|
if self.on_message:
|
|
self.on_message(topic, payload, retained)
|
|
|
|
self.value = payload
|
|
|
|
async def publish_properties(self):
|
|
topic = self.topic
|
|
publish = self.node.device.publish
|
|
|
|
await publish("{}/$name".format(topic), self.name)
|
|
await publish("{}/$datatype".format(topic), self.datatype)
|
|
|
|
if self.format is not None:
|
|
await publish("{}/$format".format(topic), self.format)
|
|
|
|
if self.settable is True:
|
|
await publish("{}/$settable".format(topic), TRUE)
|
|
|
|
if self.retained is False:
|
|
await publish("{}/$retained".format(topic), FALSE)
|
|
|
|
if self.unit is not None:
|
|
await publish("{}/$unit".format(topic), self.unit)
|
|
|
|
|
|
HomieProperty = BaseProperty
|
|
|
|
# Keep for backward compatibility
|
|
HomieNodeProperty = BaseProperty
|