diff options
Diffstat (limited to 'src')
-rwxr-xr-x | src/esp_mqtt_util.py | 42 | ||||
-rw-r--r-- | src/home/mqtt/__init__.py | 5 | ||||
-rw-r--r-- | src/home/mqtt/esp.py | 106 | ||||
-rw-r--r-- | src/home/mqtt/mqtt.py | 2 | ||||
-rw-r--r-- | src/home/mqtt/payload/__init__.py | 2 | ||||
-rw-r--r-- | src/home/mqtt/payload/base_payload.py | 28 | ||||
-rw-r--r-- | src/home/mqtt/payload/esp.py | 78 | ||||
-rw-r--r-- | src/home/mqtt/payload/inverter.py | 6 | ||||
-rw-r--r-- | src/home/mqtt/payload/relay.py | 90 | ||||
-rw-r--r-- | src/home/mqtt/payload/sensors.py | 4 | ||||
-rw-r--r-- | src/home/mqtt/payload/temphum.py | 14 | ||||
-rw-r--r-- | src/home/mqtt/relay.py | 107 | ||||
-rw-r--r-- | src/home/mqtt/temphum.py | 33 | ||||
-rwxr-xr-x | src/inverter_mqtt_receiver.py | 7 | ||||
-rwxr-xr-x | src/inverter_mqtt_sender.py | 6 | ||||
-rwxr-xr-x | src/polaris_kettle_bot.py | 6 | ||||
-rwxr-xr-x | src/polaris_kettle_util.py | 6 | ||||
-rwxr-xr-x | src/pump_mqtt_bot.py | 22 | ||||
-rwxr-xr-x | src/relay_mqtt_bot.py | 26 | ||||
-rwxr-xr-x | src/relay_mqtt_http_proxy.py | 19 | ||||
-rwxr-xr-x | src/relay_mqtt_util.py | 45 | ||||
-rwxr-xr-x | src/sensors_mqtt_receiver.py | 6 | ||||
-rwxr-xr-x | src/sensors_mqtt_sender.py | 6 | ||||
-rwxr-xr-x[-rw-r--r--] | src/temphum.py | 1 |
24 files changed, 387 insertions, 280 deletions
diff --git a/src/esp_mqtt_util.py b/src/esp_mqtt_util.py new file mode 100755 index 0000000..263128c --- /dev/null +++ b/src/esp_mqtt_util.py @@ -0,0 +1,42 @@ +#!/usr/bin/env python3 +from typing import Optional +from argparse import ArgumentParser +from enum import Enum + +from home.config import config +from home.mqtt import MqttRelay +from home.mqtt.esp import MqttEspBase +from home.mqtt.temphum import MqttTempHum +from home.mqtt.esp import MqttEspDevice + +mqtt_client: Optional[MqttEspBase] = None + + +class NodeType(Enum): + RELAY = 'relay' + TEMPHUM = 'temphum' + + +if __name__ == '__main__': + parser = ArgumentParser() + parser.add_argument('--device-id', type=str, required=True) + parser.add_argument('--type', type=str, required=True, + choices=[i.name.lower() for i in NodeType]) + + config.load('mqtt_util', parser=parser) + arg = parser.parse_args() + + mqtt_node_type = NodeType(arg.type) + devices = MqttEspDevice(id=arg.device_id) + + if mqtt_node_type == NodeType.RELAY: + mqtt_client = MqttRelay(devices=devices) + elif mqtt_node_type == NodeType.TEMPHUM: + mqtt_client = MqttTempHum(devices=devices) + + mqtt_client.set_message_callback(lambda device_id, payload: print(payload)) + mqtt_client.configure_tls() + try: + mqtt_client.connect_and_loop() + except KeyboardInterrupt: + mqtt_client.disconnect() diff --git a/src/home/mqtt/__init__.py b/src/home/mqtt/__init__.py index c9a6c6e..982e2b6 100644 --- a/src/home/mqtt/__init__.py +++ b/src/home/mqtt/__init__.py @@ -1,3 +1,4 @@ -from .mqtt import MQTTBase +from .mqtt import MqttBase from .util import poll_tick -from .relay import MQTTRelay, MQTTRelayState, MQTTRelayDevice
\ No newline at end of file +from .relay import MqttRelay, MqttRelayState +from .temphum import MqttTempHum
\ No newline at end of file diff --git a/src/home/mqtt/esp.py b/src/home/mqtt/esp.py new file mode 100644 index 0000000..56ced83 --- /dev/null +++ b/src/home/mqtt/esp.py @@ -0,0 +1,106 @@ +import re +import paho.mqtt.client as mqtt + +from .mqtt import MqttBase +from typing import Optional, Union +from .payload.esp import ( + OTAPayload, + OTAResultPayload, + DiagnosticsPayload, + InitialDiagnosticsPayload +) + + +class MqttEspDevice: + id: str + secret: Optional[str] + + def __init__(self, id: str, secret: Optional[str] = None): + self.id = id + self.secret = secret + + +class MqttEspBase(MqttBase): + _devices: list[MqttEspDevice] + _message_callback: Optional[callable] + _ota_publish_callback: Optional[callable] + + TOPIC_LEAF = 'esp' + + def __init__(self, + devices: Union[MqttEspDevice, list[MqttEspDevice]], + subscribe_to_updates=True): + super().__init__(clean_session=True) + if not isinstance(devices, list): + devices = [devices] + self._devices = devices + self._message_callback = None + self._ota_publish_callback = None + self._subscribe_to_updates = subscribe_to_updates + self._ota_mid = None + + def on_connect(self, client: mqtt.Client, userdata, flags, rc): + super().on_connect(client, userdata, flags, rc) + + if self._subscribe_to_updates: + for device in self._devices: + topic = f'hk/{device.id}/{self.TOPIC_LEAF}/#' + self._logger.debug(f"subscribing to {topic}") + client.subscribe(topic, qos=1) + + def on_publish(self, client: mqtt.Client, userdata, mid): + if self._ota_mid is not None and mid == self._ota_mid and self._ota_publish_callback: + self._ota_publish_callback() + + def set_message_callback(self, callback: callable): + self._message_callback = callback + + def on_message(self, client: mqtt.Client, userdata, msg): + try: + match = re.match(self.get_mqtt_topics(), msg.topic) + self._logger.debug(f'topic: {msg.topic}') + if not match: + return + + device_id = match.group(1) + subtopic = match.group(2) + + # try: + next(d for d in self._devices if d.id == device_id) + # except StopIteration:h + # return + + message = None + if subtopic == 'stat': + message = DiagnosticsPayload.unpack(msg.payload) + elif subtopic == 'stat1': + message = InitialDiagnosticsPayload.unpack(msg.payload) + elif subtopic == 'otares': + message = OTAResultPayload.unpack(msg.payload) + + if message and self._message_callback: + self._message_callback(device_id, message) + return True + + except Exception as e: + self._logger.exception(str(e)) + + def push_ota(self, + device_id, + filename: str, + publish_callback: callable, + qos: int): + device = next(d for d in self._devices if d.id == device_id) + assert device.secret is not None, 'device secret not specified' + + self._ota_publish_callback = publish_callback + payload = OTAPayload(secret=device.secret, filename=filename) + publish_result = self._client.publish(f'hk/{device.id}/{self.TOPIC_LEAF}/admin/ota', + payload=payload.pack(), + qos=qos) + self._ota_mid = publish_result.mid + self._client.loop_write() + + @classmethod + def get_mqtt_topics(cls, additional_topics: Optional[list[str]] = None): + return rf'^hk/(.*?)/{cls.TOPIC_LEAF}/(stat|stat1|otares'+('|'+('|'.join(additional_topics)) if additional_topics else '')+')$'
\ No newline at end of file diff --git a/src/home/mqtt/mqtt.py b/src/home/mqtt/mqtt.py index 9dd973b..4acd4f6 100644 --- a/src/home/mqtt/mqtt.py +++ b/src/home/mqtt/mqtt.py @@ -13,7 +13,7 @@ def username_and_password() -> Tuple[str, str]: return username, password -class MQTTBase: +class MqttBase: def __init__(self, clean_session=True): self._client = mqtt.Client(client_id=config['mqtt']['client_id'], protocol=mqtt.MQTTv311, diff --git a/src/home/mqtt/payload/__init__.py b/src/home/mqtt/payload/__init__.py index 9fcaf3e..eee6709 100644 --- a/src/home/mqtt/payload/__init__.py +++ b/src/home/mqtt/payload/__init__.py @@ -1 +1 @@ -from .base_payload import MQTTPayload
\ No newline at end of file +from .base_payload import MqttPayload
\ No newline at end of file diff --git a/src/home/mqtt/payload/base_payload.py b/src/home/mqtt/payload/base_payload.py index 108e0c0..1abd898 100644 --- a/src/home/mqtt/payload/base_payload.py +++ b/src/home/mqtt/payload/base_payload.py @@ -5,7 +5,21 @@ import re from typing import Optional, Tuple -class MQTTPayload(abc.ABC): +def pldstr(self) -> str: + attrs = [] + for field in self.__class__.__annotations__: + if hasattr(self, field): + attr = getattr(self, field) + attrs.append(f'{field}={attr}') + if attrs: + attrs_s = ' ' + attrs_s += ', '.join(attrs) + else: + attrs_s = '' + return f'<%s{attrs_s}>' % (self.__class__.__name__,) + + +class MqttPayload(abc.ABC): FORMAT = '' PACKER = {} UNPACKER = {} @@ -70,7 +84,7 @@ class MQTTPayload(abc.ABC): bf_number = -1 i += 1 - if issubclass(field_type, MQTTPayloadCustomField): + if issubclass(field_type, MqttPayloadCustomField): kwargs[field] = field_type.unpack(data[i]) else: kwargs[field] = cls._unpack_field(field, data[i]) @@ -87,15 +101,18 @@ class MQTTPayload(abc.ABC): @classmethod def _unpack_field(cls, name, val): - if isinstance(val, MQTTPayloadCustomField): + if isinstance(val, MqttPayloadCustomField): return if cls.UNPACKER and name in cls.UNPACKER: return cls.UNPACKER[name](val) else: return val + def __str__(self): + return pldstr(self) -class MQTTPayloadCustomField(abc.ABC): + +class MqttPayloadCustomField(abc.ABC): def __init__(self, **kwargs): for field in self.__class__.__annotations__: setattr(self, field, kwargs[field]) @@ -109,6 +126,9 @@ class MQTTPayloadCustomField(abc.ABC): def unpack(cls, *args, **kwargs): pass + def __str__(self): + return pldstr(self) + def bit_field(seq_no: int, total_bits: int, bits: int): return type(f'MQTTPayloadBitField_{seq_no}_{total_bits}_{bits}', (object,), { diff --git a/src/home/mqtt/payload/esp.py b/src/home/mqtt/payload/esp.py new file mode 100644 index 0000000..171cdb9 --- /dev/null +++ b/src/home/mqtt/payload/esp.py @@ -0,0 +1,78 @@ +import hashlib + +from .base_payload import MqttPayload, MqttPayloadCustomField + + +class OTAResultPayload(MqttPayload): + FORMAT = '=BB' + result: int + error_code: int + + +class OTAPayload(MqttPayload): + secret: str + filename: str + + # structure of returned data: + # + # uint8_t[len(secret)] secret; + # uint8_t[16] md5; + # *uint8_t data + + def pack(self): + buf = bytearray(self.secret.encode()) + m = hashlib.md5() + with open(self.filename, 'rb') as fd: + content = fd.read() + m.update(content) + buf.extend(m.digest()) + buf.extend(content) + return buf + + def unpack(cls, buf: bytes): + raise RuntimeError(f'{cls.__class__.__name__}.unpack: not implemented') + # secret = buf[:12].decode() + # filename = buf[12:].decode() + # return OTAPayload(secret=secret, filename=filename) + + +class DiagnosticsFlags(MqttPayloadCustomField): + state: bool + config_changed_value_present: bool + config_changed: bool + + @staticmethod + def unpack(flags: int): + # _logger.debug(f'StatFlags.unpack: flags={flags}') + state = flags & 0x1 + ccvp = (flags >> 1) & 0x1 + cc = (flags >> 2) & 0x1 + # _logger.debug(f'StatFlags.unpack: state={state}') + return DiagnosticsFlags(state=(state == 1), + config_changed_value_present=(ccvp == 1), + config_changed=(cc == 1)) + + def __index__(self): + bits = 0 + bits |= (int(self.state) & 0x1) + bits |= (int(self.config_changed_value_present) & 0x1) << 1 + bits |= (int(self.config_changed) & 0x1) << 2 + return bits + + +class InitialDiagnosticsPayload(MqttPayload): + FORMAT = '=IBbIB' + + ip: int + fw_version: int + rssi: int + free_heap: int + flags: DiagnosticsFlags + + +class DiagnosticsPayload(MqttPayload): + FORMAT = '=bIB' + + rssi: int + free_heap: int + flags: DiagnosticsFlags diff --git a/src/home/mqtt/payload/inverter.py b/src/home/mqtt/payload/inverter.py index 1d4099c..09388df 100644 --- a/src/home/mqtt/payload/inverter.py +++ b/src/home/mqtt/payload/inverter.py @@ -1,13 +1,13 @@ import struct -from .base_payload import MQTTPayload, bit_field +from .base_payload import MqttPayload, bit_field from typing import Tuple _mult_10 = lambda n: int(n*10) _div_10 = lambda n: n/10 -class Status(MQTTPayload): +class Status(MqttPayload): # 46 bytes FORMAT = 'IHHHHHHBHHHHHBHHHHHHHH' @@ -65,7 +65,7 @@ class Status(MQTTPayload): load_connected: bit_field(0, 16, 1) -class Generation(MQTTPayload): +class Generation(MqttPayload): # 8 bytes FORMAT = 'II' diff --git a/src/home/mqtt/payload/relay.py b/src/home/mqtt/payload/relay.py index 1a38201..4902991 100644 --- a/src/home/mqtt/payload/relay.py +++ b/src/home/mqtt/payload/relay.py @@ -1,53 +1,13 @@ -import hashlib +from .base_payload import MqttPayload +from .esp import ( + OTAResultPayload, + OTAPayload, + InitialDiagnosticsPayload, + DiagnosticsPayload +) -from .base_payload import MQTTPayload, MQTTPayloadCustomField - -# _logger = logging.getLogger(__name__) - -class StatFlags(MQTTPayloadCustomField): - state: bool - config_changed_value_present: bool - config_changed: bool - - @staticmethod - def unpack(flags: int): - # _logger.debug(f'StatFlags.unpack: flags={flags}') - state = flags & 0x1 - ccvp = (flags >> 1) & 0x1 - cc = (flags >> 2) & 0x1 - # _logger.debug(f'StatFlags.unpack: state={state}') - return StatFlags(state=(state == 1), - config_changed_value_present=(ccvp == 1), - config_changed=(cc == 1)) - - def __index__(self): - bits = 0 - bits |= (int(self.state) & 0x1) - bits |= (int(self.config_changed_value_present) & 0x1) << 1 - bits |= (int(self.config_changed) & 0x1) << 2 - return bits - - -class InitialStatPayload(MQTTPayload): - FORMAT = '=IBbIB' - - ip: int - fw_version: int - rssi: int - free_heap: int - flags: StatFlags - - -class StatPayload(MQTTPayload): - FORMAT = '=bIB' - - rssi: int - free_heap: int - flags: StatFlags - - -class PowerPayload(MQTTPayload): +class PowerPayload(MqttPayload): FORMAT = '=12sB' PACKER = { 'state': lambda n: int(n), @@ -60,37 +20,3 @@ class PowerPayload(MQTTPayload): secret: str state: bool - - -class OTAResultPayload(MQTTPayload): - FORMAT = '=BB' - result: int - error_code: int - - -class OTAPayload(MQTTPayload): - secret: str - filename: str - - # structure of returned data: - # - # uint8_t[len(secret)] secret; - # uint8_t[16] md5; - # *uint8_t data - - def pack(self): - buf = bytearray(self.secret.encode()) - m = hashlib.md5() - with open(self.filename, 'rb') as fd: - content = fd.read() - m.update(content) - buf.extend(m.digest()) - buf.extend(content) - return buf - - def unpack(cls, buf: bytes): - raise RuntimeError(f'{cls.__class__.__name__}.unpack: not implemented') - # secret = buf[:12].decode() - # filename = buf[12:].decode() - # return OTAPayload(secret=secret, filename=filename) - diff --git a/src/home/mqtt/payload/sensors.py b/src/home/mqtt/payload/sensors.py index 3ecc243..f99b307 100644 --- a/src/home/mqtt/payload/sensors.py +++ b/src/home/mqtt/payload/sensors.py @@ -1,10 +1,10 @@ -from .base_payload import MQTTPayload +from .base_payload import MqttPayload _mult_100 = lambda n: int(n*100) _div_100 = lambda n: n/100 -class Temperature(MQTTPayload): +class Temperature(MqttPayload): FORMAT = 'IhH' PACKER = { 'temp': _mult_100, diff --git a/src/home/mqtt/payload/temphum.py b/src/home/mqtt/payload/temphum.py new file mode 100644 index 0000000..5b45ecb --- /dev/null +++ b/src/home/mqtt/payload/temphum.py @@ -0,0 +1,14 @@ +from .base_payload import MqttPayload + +two_digits_precision = lambda x: round(x, 2) + + +class TempHumDataPayload(MqttPayload): + FORMAT = '=dd' + UNPACKER = { + 'temp': two_digits_precision, + 'rh': two_digits_precision + } + + temp: float + rh: float diff --git a/src/home/mqtt/relay.py b/src/home/mqtt/relay.py index 53d43e4..a90f19c 100644 --- a/src/home/mqtt/relay.py +++ b/src/home/mqtt/relay.py @@ -2,83 +2,43 @@ import paho.mqtt.client as mqtt import re import datetime -from .mqtt import MQTTBase -from typing import Optional, Union from .payload.relay import ( - InitialStatPayload, - StatPayload, PowerPayload, - OTAPayload, - OTAResultPayload ) +from .esp import MqttEspBase -class MQTTRelayDevice: - id: str - secret: Optional[str] +class MqttRelay(MqttEspBase): + TOPIC_LEAF = 'relay' - def __init__(self, id: str, secret: Optional[str] = None): - self.id = id - self.secret = secret - - -class MQTTRelay(MQTTBase): - _devices: list[MQTTRelayDevice] - _message_callback: Optional[callable] - _ota_publish_callback: Optional[callable] - - def __init__(self, - devices: Union[MQTTRelayDevice, list[MQTTRelayDevice]], - subscribe_to_updates=True): - super().__init__(clean_session=True) - if not isinstance(devices, list): - devices = [devices] - self._devices = devices - self._message_callback = None - self._ota_publish_callback = None - self._subscribe_to_updates = subscribe_to_updates - self._ota_mid = None - - def on_connect(self, client: mqtt.Client, userdata, flags, rc): - super().on_connect(client, userdata, flags, rc) - - if self._subscribe_to_updates: - for device in self._devices: - topic = f'hk/{device.id}/relay/#' - self._logger.debug(f"subscribing to {topic}") - client.subscribe(topic, qos=1) + def set_power(self, device_id, enable: bool, secret=None): + device = next(d for d in self._devices if d.id == device_id) + secret = secret if secret else device.secret - def on_publish(self, client: mqtt.Client, userdata, mid): - if self._ota_mid is not None and mid == self._ota_mid and self._ota_publish_callback: - self._ota_publish_callback() + assert secret is not None, 'device secret not specified' - def set_message_callback(self, callback: callable): - self._message_callback = callback + payload = PowerPayload(secret=secret, + state=enable) + self._client.publish(f'hk/{device.id}/{self.TOPIC_LEAF}/power', + payload=payload.pack(), + qos=1) + self._client.loop_write() def on_message(self, client: mqtt.Client, userdata, msg): + if super().on_message(client, userdata, msg): + return + try: - match = re.match(r'^hk/(.*?)/relay/(stat|stat1|power|otares)$', msg.topic) - self._logger.debug(f'topic: {msg.topic}') + match = re.match(self.get_mqtt_topics(['power']), msg.topic) if not match: return device_id = match.group(1) subtopic = match.group(2) - try: - next(d for d in self._devices if d.id == device_id) - except StopIteration: - return - message = None - if subtopic == 'stat': - message = StatPayload.unpack(msg.payload) - elif subtopic == 'stat1': - message = InitialStatPayload.unpack(msg.payload) - elif subtopic == 'power': + if subtopic == 'power': message = PowerPayload.unpack(msg.payload) - elif subtopic == 'otares': - message = OTAResultPayload.unpack(msg.payload) if message and self._message_callback: self._message_callback(device_id, message) @@ -86,37 +46,8 @@ class MQTTRelay(MQTTBase): except Exception as e: self._logger.exception(str(e)) - def set_power(self, device_id, enable: bool, secret=None): - device = next(d for d in self._devices if d.id == device_id) - secret = secret if secret else device.secret - - assert secret is not None, 'device secret not specified' - - payload = PowerPayload(secret=secret, - state=enable) - self._client.publish(f'hk/{device.id}/relay/power', - payload=payload.pack(), - qos=1) - self._client.loop_write() - - def push_ota(self, - device_id, - filename: str, - publish_callback: callable, - qos: int): - device = next(d for d in self._devices if d.id == device_id) - assert device.secret is not None, 'device secret not specified' - - self._ota_publish_callback = publish_callback - payload = OTAPayload(secret=device.secret, filename=filename) - publish_result = self._client.publish(f'hk/{device.id}/relay/admin/ota', - payload=payload.pack(), - qos=qos) - self._ota_mid = publish_result.mid - self._client.loop_write() - -class MQTTRelayState: +class MqttRelayState: enabled: bool update_time: datetime.datetime rssi: int diff --git a/src/home/mqtt/temphum.py b/src/home/mqtt/temphum.py new file mode 100644 index 0000000..b9b2eb9 --- /dev/null +++ b/src/home/mqtt/temphum.py @@ -0,0 +1,33 @@ +import paho.mqtt.client as mqtt +import re + +from .payload.temphum import ( + TempHumDataPayload +) +from .esp import MqttEspBase + + +class MqttTempHum(MqttEspBase): + TOPIC_LEAF = 'temphum' + + def on_message(self, client: mqtt.Client, userdata, msg): + if super().on_message(client, userdata, msg): + return + + try: + match = re.match(self.get_mqtt_topics(['data']), msg.topic) + if not match: + return + + device_id = match.group(1) + subtopic = match.group(2) + + message = None + if subtopic == 'data': + message = TempHumDataPayload.unpack(msg.payload) + + if message and self._message_callback: + self._message_callback(device_id, message) + + except Exception as e: + self._logger.exception(str(e)) diff --git a/src/inverter_mqtt_receiver.py b/src/inverter_mqtt_receiver.py index a7018f2..d40647e 100755 --- a/src/inverter_mqtt_receiver.py +++ b/src/inverter_mqtt_receiver.py @@ -1,15 +1,14 @@ #!/usr/bin/env python3 import paho.mqtt.client as mqtt import re -import logging -from home.mqtt import MQTTBase +from home.mqtt import MqttBase from home.mqtt.payload.inverter import Status, Generation from home.database import InverterDatabase from home.config import config -class MQTTReceiver(MQTTBase): +class MqttReceiver(MqttBase): def __init__(self): super().__init__(clean_session=False) self.database = InverterDatabase() @@ -70,6 +69,6 @@ class MQTTReceiver(MQTTBase): if __name__ == '__main__': config.load('inverter_mqtt_receiver') - server = MQTTReceiver() + server = MqttReceiver() server.connect_and_loop() diff --git a/src/inverter_mqtt_sender.py b/src/inverter_mqtt_sender.py index 74191a2..fb2a2d8 100755 --- a/src/inverter_mqtt_sender.py +++ b/src/inverter_mqtt_sender.py @@ -5,11 +5,11 @@ import json import inverterd from home.config import config -from home.mqtt import MQTTBase, poll_tick +from home.mqtt import MqttBase, poll_tick from home.mqtt.payload.inverter import Status, Generation -class MQTTClient(MQTTBase): +class MqttClient(MqttBase): def __init__(self): super().__init__() @@ -66,7 +66,7 @@ class MQTTClient(MQTTBase): if __name__ == '__main__': config.load('inverter_mqtt_sender') - client = MQTTClient() + client = MqttClient() client.configure_tls() client.connect_and_loop(loop_forever=False) client.poll_inverter()
\ No newline at end of file diff --git a/src/polaris_kettle_bot.py b/src/polaris_kettle_bot.py index 2e5256d..088707d 100755 --- a/src/polaris_kettle_bot.py +++ b/src/polaris_kettle_bot.py @@ -10,7 +10,7 @@ import paho.mqtt.client as mqtt from home.telegram import bot from home.api.types import BotType -from home.mqtt import MQTTBase +from home.mqtt import MqttBase from home.config import config from home.util import chunks from syncleo import ( @@ -204,7 +204,7 @@ class KettleInfo: class KettleController(threading.Thread, - MQTTBase, + MqttBase, DeviceListener, IncomingMessageListener, KettleInfoListener, @@ -224,7 +224,7 @@ class KettleController(threading.Thread, def __init__(self): # basic setup - MQTTBase.__init__(self, clean_session=False) + MqttBase.__init__(self, clean_session=False) threading.Thread.__init__(self) self._logger = logging.getLogger(self.__class__.__name__) diff --git a/src/polaris_kettle_util.py b/src/polaris_kettle_util.py index 61c1c7d..81326dd 100755 --- a/src/polaris_kettle_util.py +++ b/src/polaris_kettle_util.py @@ -8,7 +8,7 @@ import paho.mqtt.client as mqtt from typing import Optional from argparse import ArgumentParser from queue import SimpleQueue -from home.mqtt import MQTTBase +from home.mqtt import MqttBase from home.config import config from syncleo import ( Kettle, @@ -21,7 +21,7 @@ logger = logging.getLogger(__name__) control_tasks = SimpleQueue() -class MQTTServer(MQTTBase): +class MqttServer(MqttBase): def __init__(self): super().__init__(clean_session=False) @@ -78,7 +78,7 @@ def main(): arg = config.load('polaris_kettle_util', use_cli=True, parser=parser) if arg.mode == 'mqtt': - server = MQTTServer() + server = MqttServer() try: server.connect_and_loop(loop_forever=True) except KeyboardInterrupt: diff --git a/src/pump_mqtt_bot.py b/src/pump_mqtt_bot.py index accafcb..d3b6de4 100755 --- a/src/pump_mqtt_bot.py +++ b/src/pump_mqtt_bot.py @@ -8,10 +8,10 @@ from telegram import ReplyKeyboardMarkup, User from home.config import config from home.telegram import bot from home.telegram._botutil import user_any_name -from home.api.types import BotType -from home.mqtt import MQTTRelay, MQTTRelayState, MQTTRelayDevice -from home.mqtt.payload import MQTTPayload -from home.mqtt.payload.relay import InitialStatPayload, StatPayload +from home.mqtt.esp import MqttEspDevice +from home.mqtt import MqttRelay, MqttRelayState +from home.mqtt.payload import MqttPayload +from home.mqtt.payload.relay import InitialDiagnosticsPayload, DiagnosticsPayload config.load('pump_mqtt_bot') @@ -70,8 +70,8 @@ bot.lang.en( ) -mqtt_relay: Optional[MQTTRelay] = None -relay_state = MQTTRelayState() +mqtt_relay: Optional[MqttRelay] = None +relay_state = MqttRelayState() class UserAction(Enum): @@ -79,10 +79,10 @@ class UserAction(Enum): OFF = 'off' -def on_mqtt_message(home_id, message: MQTTPayload): - if isinstance(message, InitialStatPayload) or isinstance(message, StatPayload): +def on_mqtt_message(home_id, message: MqttPayload): + if isinstance(message, InitialDiagnosticsPayload) or isinstance(message, DiagnosticsPayload): kwargs = dict(rssi=message.rssi, enabled=message.flags.state) - if isinstance(message, InitialStatPayload): + if isinstance(message, InitialDiagnosticsPayload): kwargs['fw_version'] = message.fw_version relay_state.update(**kwargs) @@ -157,8 +157,8 @@ def markup(ctx: Optional[bot.Context]) -> Optional[ReplyKeyboardMarkup]: if __name__ == '__main__': - mqtt_relay = MQTTRelay(devices=MQTTRelayDevice(id=config['mqtt']['home_id'], - secret=config['mqtt']['home_secret'])) + mqtt_relay = MqttRelay(devices=MqttEspDevice(id=config['mqtt']['home_id'], + secret=config['mqtt']['home_secret'])) mqtt_relay.set_message_callback(on_mqtt_message) mqtt_relay.configure_tls() mqtt_relay.connect_and_loop(loop_forever=False) diff --git a/src/relay_mqtt_bot.py b/src/relay_mqtt_bot.py index 33b7e06..ebbff82 100755 --- a/src/relay_mqtt_bot.py +++ b/src/relay_mqtt_bot.py @@ -6,10 +6,10 @@ from functools import partial from home.config import config from home.telegram import bot -from home.api.types import BotType -from home.mqtt import MQTTRelay, MQTTRelayState, MQTTRelayDevice -from home.mqtt.payload import MQTTPayload -from home.mqtt.payload.relay import InitialStatPayload, StatPayload +from home.mqtt import MqttRelay, MqttRelayState +from home.mqtt.esp import MqttEspDevice +from home.mqtt.payload import MqttPayload +from home.mqtt.payload.relay import InitialDiagnosticsPayload, DiagnosticsPayload config.load('relay_mqtt_bot') @@ -34,8 +34,8 @@ status_emoji = { 'on': '✅', 'off': '❌' } -mqtt_relay: Optional[MQTTRelay] = None -relay_states: dict[str, MQTTRelayState] = {} +mqtt_relay: Optional[MqttRelay] = None +relay_states: dict[str, MqttRelayState] = {} class UserAction(Enum): @@ -43,13 +43,13 @@ class UserAction(Enum): OFF = 'off' -def on_mqtt_message(home_id, message: MQTTPayload): - if isinstance(message, InitialStatPayload) or isinstance(message, StatPayload): +def on_mqtt_message(home_id, message: MqttPayload): + if isinstance(message, InitialDiagnosticsPayload) or isinstance(message, DiagnosticsPayload): kwargs = dict(rssi=message.rssi, enabled=message.flags.state) - if isinstance(message, InitialStatPayload): + if isinstance(message, InitialDiagnosticsPayload): kwargs['fw_version'] = message.fw_version if home_id not in relay_states: - relay_states[home_id] = MQTTRelayState() + relay_states[home_id] = MqttRelayState() relay_states[home_id].update(**kwargs) @@ -87,8 +87,8 @@ def markup(ctx: Optional[bot.Context]) -> Optional[ReplyKeyboardMarkup]: if __name__ == '__main__': devices = [] for device_id, data in config['relays'].items(): - devices.append(MQTTRelayDevice(id=device_id, - secret=data['secret'])) + devices.append(MqttEspDevice(id=device_id, + secret=data['secret'])) labels = data['labels'] bot.lang.ru(**{device_id: labels['ru']}) bot.lang.en(**{device_id: labels['en']}) @@ -101,7 +101,7 @@ if __name__ == '__main__': messages.append(f'{type_emoji}{status_emoji[action.value]} {labels[_lang]}') bot.handler(texts=messages)(partial(enable_handler if action == UserAction.ON else disable_handler, device_id)) - mqtt_relay = MQTTRelay(devices=devices) + mqtt_relay = MqttRelay(devices=devices) mqtt_relay.set_message_callback(on_mqtt_message) mqtt_relay.configure_tls() mqtt_relay.connect_and_loop(loop_forever=False) diff --git a/src/relay_mqtt_http_proxy.py b/src/relay_mqtt_http_proxy.py index 51a4e21..098facc 100755 --- a/src/relay_mqtt_http_proxy.py +++ b/src/relay_mqtt_http_proxy.py @@ -1,20 +1,21 @@ #!/usr/bin/env python3 from home import http from home.config import config -from home.mqtt import MQTTRelay, MQTTRelayDevice, MQTTRelayState -from home.mqtt.payload import MQTTPayload -from home.mqtt.payload.relay import InitialStatPayload, StatPayload +from home.mqtt import MqttRelay, MqttRelayState +from home.mqtt.esp import MqttEspDevice +from home.mqtt.payload import MqttPayload +from home.mqtt.payload.relay import InitialDiagnosticsPayload, DiagnosticsPayload from typing import Optional -mqtt_relay: Optional[MQTTRelay] = None -relay_states: dict[str, MQTTRelayState] = {} +mqtt_relay: Optional[MqttRelay] = None +relay_states: dict[str, MqttRelayState] = {} -def on_mqtt_message(device_id, message: MQTTPayload): - if isinstance(message, InitialStatPayload) or isinstance(message, StatPayload): +def on_mqtt_message(device_id, message: MqttPayload): + if isinstance(message, InitialDiagnosticsPayload) or isinstance(message, DiagnosticsPayload): kwargs = dict(rssi=message.rssi, enabled=message.flags.state) if device_id not in relay_states: - relay_states[device_id] = MQTTRelayState() + relay_states[device_id] = MqttRelayState() relay_states[device_id].update(**kwargs) @@ -54,7 +55,7 @@ class RelayMqttHttpProxy(http.HTTPServer): if __name__ == '__main__': config.load('relay_mqtt_http_proxy') - mqtt_relay = MQTTRelay(devices=[MQTTRelayDevice(id=device_id) for device_id in config.get('relay.devices')]) + mqtt_relay = MqttRelay(devices=[MqttEspDevice(id=device_id) for device_id in config.get('relay.devices')]) mqtt_relay.configure_tls() mqtt_relay.set_message_callback(on_mqtt_message) mqtt_relay.connect_and_loop(loop_forever=False) diff --git a/src/relay_mqtt_util.py b/src/relay_mqtt_util.py deleted file mode 100755 index 45d2405..0000000 --- a/src/relay_mqtt_util.py +++ /dev/null @@ -1,45 +0,0 @@ -#!/usr/bin/env python3 -from typing import Optional -from argparse import ArgumentParser - -from home.config import config -from home.mqtt import MQTTRelay, MQTTRelayDevice -from home.mqtt.payload import MQTTPayload -from home.mqtt.payload.relay import ( - InitialStatPayload, StatPayload, OTAResultPayload -) - -mqtt_relay: Optional[MQTTRelay] = None - - -def on_mqtt_message(device_id, p: MQTTPayload): - message = None - - if isinstance(p, InitialStatPayload) or isinstance(p, StatPayload): - message = f'[stat] state={"on" if p.flags.state else "off"}' - message += f' rssi={p.rssi}' - message += f' free_heap={p.free_heap}' - if isinstance(p, InitialStatPayload): - message += f' fw={p.fw_version}' - - elif isinstance(p, OTAResultPayload): - message = f'[otares] result={p.result} error_code={p.error_code}' - - if message: - print(message) - - -if __name__ == '__main__': - parser = ArgumentParser() - parser.add_argument('--device-id', type=str, required=True) - - config.load('relay_mqtt_util', parser=parser) - arg = parser.parse_args() - - mqtt_relay = MQTTRelay(devices=MQTTRelayDevice(id=arg.device_id)) - mqtt_relay.set_message_callback(on_mqtt_message) - mqtt_relay.configure_tls() - try: - mqtt_relay.connect_and_loop() - except KeyboardInterrupt: - mqtt_relay.disconnect() diff --git a/src/sensors_mqtt_receiver.py b/src/sensors_mqtt_receiver.py index 9637690..a377ddd 100755 --- a/src/sensors_mqtt_receiver.py +++ b/src/sensors_mqtt_receiver.py @@ -2,7 +2,7 @@ import paho.mqtt.client as mqtt import re -from home.mqtt import MQTTBase +from home.mqtt import MqttBase from home.config import config from home.mqtt.payload.sensors import Temperature from home.api.types import TemperatureSensorLocation @@ -16,7 +16,7 @@ def get_sensor_type(sensor: str) -> TemperatureSensorLocation: raise ValueError(f'unexpected sensor value: {sensor}') -class MQTTServer(MQTTBase): +class MqttServer(MqttBase): def __init__(self): super().__init__(clean_session=False) self.database = SensorsDatabase() @@ -49,5 +49,5 @@ class MQTTServer(MQTTBase): if __name__ == '__main__': config.load('sensors_mqtt_receiver') - server = MQTTServer() + server = MqttServer() server.connect_and_loop() diff --git a/src/sensors_mqtt_sender.py b/src/sensors_mqtt_sender.py index 2cf2717..87a28ca 100755 --- a/src/sensors_mqtt_sender.py +++ b/src/sensors_mqtt_sender.py @@ -3,12 +3,12 @@ import time import json from home.util import parse_addr, MySimpleSocketClient -from home.mqtt import MQTTBase, poll_tick +from home.mqtt import MqttBase, poll_tick from home.mqtt.payload.sensors import Temperature from home.config import config -class MQTTClient(MQTTBase): +class MqttClient(MqttBase): def __init__(self): super().__init__(self) self._home_id = config['mqtt']['home_id'] @@ -52,7 +52,7 @@ class MQTTClient(MQTTBase): if __name__ == '__main__': config.load('sensors_mqtt_sender') - client = MQTTClient() + client = MqttClient() client.configure_tls() client.connect_and_loop(loop_forever=False) client.poll() diff --git a/src/temphum.py b/src/temphum.py index dc0b7dd..0f90835 100644..100755 --- a/src/temphum.py +++ b/src/temphum.py @@ -1,3 +1,4 @@ +#!/usr/bin/env python3 from argparse import ArgumentParser from home.temphum import SensorType, create_sensor |