summaryrefslogtreecommitdiff
path: root/src/home/mqtt/module
diff options
context:
space:
mode:
Diffstat (limited to 'src/home/mqtt/module')
-rw-r--r--src/home/mqtt/module/diagnostics.py64
-rw-r--r--src/home/mqtt/module/inverter.py195
-rw-r--r--src/home/mqtt/module/ota.py77
-rw-r--r--src/home/mqtt/module/relay.py92
-rw-r--r--src/home/mqtt/module/temphum.py82
5 files changed, 0 insertions, 510 deletions
diff --git a/src/home/mqtt/module/diagnostics.py b/src/home/mqtt/module/diagnostics.py
deleted file mode 100644
index 5db5e99..0000000
--- a/src/home/mqtt/module/diagnostics.py
+++ /dev/null
@@ -1,64 +0,0 @@
-from .._payload import MqttPayload, MqttPayloadCustomField
-from .._node import MqttNode, MqttModule
-from typing import Optional
-
-MODULE_NAME = 'MqttDiagnosticsModule'
-
-
-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
-
-
-class MqttDiagnosticsModule(MqttModule):
- def on_connect(self, mqtt: MqttNode):
- super().on_connect(mqtt)
- for topic in ('diag', 'd1ag', 'stat', 'stat1'):
- mqtt.subscribe_module(topic, self)
-
- def handle_payload(self, mqtt: MqttNode, topic: str, payload: bytes) -> Optional[MqttPayload]:
- message = None
- if topic in ('stat', 'diag'):
- message = DiagnosticsPayload.unpack(payload)
- elif topic in ('stat1', 'd1ag'):
- message = InitialDiagnosticsPayload.unpack(payload)
- if message:
- self._logger.debug(message)
- return message
diff --git a/src/home/mqtt/module/inverter.py b/src/home/mqtt/module/inverter.py
deleted file mode 100644
index d927a06..0000000
--- a/src/home/mqtt/module/inverter.py
+++ /dev/null
@@ -1,195 +0,0 @@
-import time
-import json
-import datetime
-try:
- import inverterd
-except:
- pass
-
-from typing import Optional
-from .._module import MqttModule
-from .._node import MqttNode
-from .._payload import MqttPayload, bit_field
-try:
- from home.database import InverterDatabase
-except:
- pass
-
-_mult_10 = lambda n: int(n*10)
-_div_10 = lambda n: n/10
-
-
-MODULE_NAME = 'MqttInverterModule'
-
-STATUS_TOPIC = 'status'
-GENERATION_TOPIC = 'generation'
-
-
-class MqttInverterStatusPayload(MqttPayload):
- # 46 bytes
- FORMAT = 'IHHHHHHBHHHHHBHHHHHHHH'
-
- PACKER = {
- 'grid_voltage': _mult_10,
- 'grid_freq': _mult_10,
- 'ac_output_voltage': _mult_10,
- 'ac_output_freq': _mult_10,
- 'battery_voltage': _mult_10,
- 'battery_voltage_scc': _mult_10,
- 'battery_voltage_scc2': _mult_10,
- 'pv1_input_voltage': _mult_10,
- 'pv2_input_voltage': _mult_10
- }
- UNPACKER = {
- 'grid_voltage': _div_10,
- 'grid_freq': _div_10,
- 'ac_output_voltage': _div_10,
- 'ac_output_freq': _div_10,
- 'battery_voltage': _div_10,
- 'battery_voltage_scc': _div_10,
- 'battery_voltage_scc2': _div_10,
- 'pv1_input_voltage': _div_10,
- 'pv2_input_voltage': _div_10
- }
-
- time: int
- grid_voltage: float
- grid_freq: float
- ac_output_voltage: float
- ac_output_freq: float
- ac_output_apparent_power: int
- ac_output_active_power: int
- output_load_percent: int
- battery_voltage: float
- battery_voltage_scc: float
- battery_voltage_scc2: float
- battery_discharge_current: int
- battery_charge_current: int
- battery_capacity: int
- inverter_heat_sink_temp: int
- mppt1_charger_temp: int
- mppt2_charger_temp: int
- pv1_input_power: int
- pv2_input_power: int
- pv1_input_voltage: float
- pv2_input_voltage: float
-
- # H
- mppt1_charger_status: bit_field(0, 16, 2)
- mppt2_charger_status: bit_field(0, 16, 2)
- battery_power_direction: bit_field(0, 16, 2)
- dc_ac_power_direction: bit_field(0, 16, 2)
- line_power_direction: bit_field(0, 16, 2)
- load_connected: bit_field(0, 16, 1)
-
-
-class MqttInverterGenerationPayload(MqttPayload):
- # 8 bytes
- FORMAT = 'II'
-
- time: int
- wh: int
-
-
-class MqttInverterModule(MqttModule):
- _status_poll_freq: int
- _generation_poll_freq: int
- _inverter: Optional[inverterd.Client]
- _database: Optional[InverterDatabase]
- _gen_prev: float
-
- def __init__(self, status_poll_freq=0, generation_poll_freq=0):
- super().__init__(tick_interval=status_poll_freq)
- self._status_poll_freq = status_poll_freq
- self._generation_poll_freq = generation_poll_freq
-
- # this defines whether this is a publisher or a subscriber
- if status_poll_freq > 0:
- self._inverter = inverterd.Client()
- self._inverter.connect()
- self._inverter.format(inverterd.Format.SIMPLE_JSON)
- self._database = None
- else:
- self._inverter = None
- self._database = InverterDatabase()
-
- self._gen_prev = 0
-
- def on_connect(self, mqtt: MqttNode):
- super().on_connect(mqtt)
- if not self._inverter:
- mqtt.subscribe_module(STATUS_TOPIC, self)
- mqtt.subscribe_module(GENERATION_TOPIC, self)
-
- def tick(self):
- if not self._inverter:
- return
-
- # read status
- now = time.time()
- try:
- raw = self._inverter.exec('get-status')
- except inverterd.InverterError as e:
- self._logger.error(f'inverter error: {str(e)}')
- # TODO send to server
- return
-
- data = json.loads(raw)['data']
- status = MqttInverterStatusPayload(time=round(now), **data)
- self._mqtt_node_ref.publish(STATUS_TOPIC, status.pack())
-
- # read today's generation stat
- now = time.time()
- if self._gen_prev == 0 or now - self._gen_prev >= self._generation_poll_freq:
- self._gen_prev = now
- today = datetime.date.today()
- try:
- raw = self._inverter.exec('get-day-generated', (today.year, today.month, today.day))
- except inverterd.InverterError as e:
- self._logger.error(f'inverter error: {str(e)}')
- # TODO send to server
- return
-
- data = json.loads(raw)['data']
- gen = MqttInverterGenerationPayload(time=round(now), wh=data['wh'])
- self._mqtt_node_ref.publish(GENERATION_TOPIC, gen.pack())
-
- def handle_payload(self, mqtt: MqttNode, topic: str, payload: bytes) -> Optional[MqttPayload]:
- home_id = 1 # legacy compat
-
- if topic == STATUS_TOPIC:
- s = MqttInverterStatusPayload.unpack(payload)
- self._database.add_status(home_id=home_id,
- client_time=s.time,
- grid_voltage=int(s.grid_voltage*10),
- grid_freq=int(s.grid_freq * 10),
- ac_output_voltage=int(s.ac_output_voltage * 10),
- ac_output_freq=int(s.ac_output_freq * 10),
- ac_output_apparent_power=s.ac_output_apparent_power,
- ac_output_active_power=s.ac_output_active_power,
- output_load_percent=s.output_load_percent,
- battery_voltage=int(s.battery_voltage * 10),
- battery_voltage_scc=int(s.battery_voltage_scc * 10),
- battery_voltage_scc2=int(s.battery_voltage_scc2 * 10),
- battery_discharge_current=s.battery_discharge_current,
- battery_charge_current=s.battery_charge_current,
- battery_capacity=s.battery_capacity,
- inverter_heat_sink_temp=s.inverter_heat_sink_temp,
- mppt1_charger_temp=s.mppt1_charger_temp,
- mppt2_charger_temp=s.mppt2_charger_temp,
- pv1_input_power=s.pv1_input_power,
- pv2_input_power=s.pv2_input_power,
- pv1_input_voltage=int(s.pv1_input_voltage * 10),
- pv2_input_voltage=int(s.pv2_input_voltage * 10),
- mppt1_charger_status=s.mppt1_charger_status,
- mppt2_charger_status=s.mppt2_charger_status,
- battery_power_direction=s.battery_power_direction,
- dc_ac_power_direction=s.dc_ac_power_direction,
- line_power_direction=s.line_power_direction,
- load_connected=s.load_connected)
- return s
-
- elif topic == GENERATION_TOPIC:
- gen = MqttInverterGenerationPayload.unpack(payload)
- self._database.add_generation(home_id, gen.time, gen.wh)
- return gen
diff --git a/src/home/mqtt/module/ota.py b/src/home/mqtt/module/ota.py
deleted file mode 100644
index cd34332..0000000
--- a/src/home/mqtt/module/ota.py
+++ /dev/null
@@ -1,77 +0,0 @@
-import hashlib
-
-from typing import Optional
-from .._payload import MqttPayload
-from .._node import MqttModule, MqttNode
-
-MODULE_NAME = 'MqttOtaModule'
-
-
-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 MqttOtaModule(MqttModule):
- _ota_request: Optional[tuple[str, int]]
-
- def __init__(self, *args, **kwargs):
- super().__init__(*args, **kwargs)
- self._ota_request = None
-
- def on_connect(self, mqtt: MqttNode):
- super().on_connect(mqtt)
- mqtt.subscribe_module("otares", self)
-
- if self._ota_request is not None:
- filename, qos = self._ota_request
- self._ota_request = None
- self.do_push_ota(self._mqtt_node_ref.secret, filename, qos)
-
- def handle_payload(self, mqtt: MqttNode, topic: str, payload: bytes) -> Optional[MqttPayload]:
- if topic == 'otares':
- message = OtaResultPayload.unpack(payload)
- self._logger.debug(message)
- return message
-
- def do_push_ota(self, secret: str, filename: str, qos: int):
- payload = OtaPayload(secret=secret, filename=filename)
- self._mqtt_node_ref.publish('ota',
- payload=payload.pack(),
- qos=qos)
-
- def push_ota(self,
- filename: str,
- qos: int):
- if not self._initialized:
- self._ota_request = (filename, qos)
- else:
- self.do_push_ota(filename, qos)
diff --git a/src/home/mqtt/module/relay.py b/src/home/mqtt/module/relay.py
deleted file mode 100644
index e968031..0000000
--- a/src/home/mqtt/module/relay.py
+++ /dev/null
@@ -1,92 +0,0 @@
-import datetime
-
-from typing import Optional
-from .. import MqttModule, MqttPayload, MqttNode
-
-MODULE_NAME = 'MqttRelayModule'
-
-
-class MqttPowerSwitchPayload(MqttPayload):
- FORMAT = '=12sB'
- PACKER = {
- 'state': lambda n: int(n),
- 'secret': lambda s: s.encode('utf-8')
- }
- UNPACKER = {
- 'state': lambda n: bool(n),
- 'secret': lambda s: s.decode('utf-8')
- }
-
- secret: str
- state: bool
-
-
-class MqttPowerStatusPayload(MqttPayload):
- FORMAT = '=B'
- PACKER = {
- 'opened': lambda n: int(n),
- }
- UNPACKER = {
- 'opened': lambda n: bool(n),
- }
-
- opened: bool
-
-
-class MqttRelayState:
- enabled: bool
- update_time: datetime.datetime
- rssi: int
- fw_version: int
- ever_updated: bool
-
- def __init__(self):
- self.ever_updated = False
- self.enabled = False
- self.rssi = 0
-
- def update(self,
- enabled: bool,
- rssi: int,
- fw_version=None):
- self.ever_updated = True
- self.enabled = enabled
- self.rssi = rssi
- self.update_time = datetime.datetime.now()
- if fw_version:
- self.fw_version = fw_version
-
-
-class MqttRelayModule(MqttModule):
- _legacy_topics: bool
-
- def __init__(self, legacy_topics=False, *args, **kwargs):
- super().__init__(*args, **kwargs)
- self._legacy_topics = legacy_topics
-
- def on_connect(self, mqtt: MqttNode):
- super().on_connect(mqtt)
- mqtt.subscribe_module(self._get_switch_topic(), self)
- mqtt.subscribe_module('relay/status', self)
-
- def switchpower(self,
- enable: bool):
- payload = MqttPowerSwitchPayload(secret=self._mqtt_node_ref.secret,
- state=enable)
- self._mqtt_node_ref.publish(self._get_switch_topic(),
- payload=payload.pack())
-
- def handle_payload(self, mqtt: MqttNode, topic: str, payload: bytes) -> Optional[MqttPayload]:
- message = None
-
- if topic == self._get_switch_topic():
- message = MqttPowerSwitchPayload.unpack(payload)
- elif topic == 'relay/status':
- message = MqttPowerStatusPayload.unpack(payload)
-
- if message is not None:
- self._logger.debug(message)
- return message
-
- def _get_switch_topic(self) -> str:
- return 'relay/power' if self._legacy_topics else 'relay/switch'
diff --git a/src/home/mqtt/module/temphum.py b/src/home/mqtt/module/temphum.py
deleted file mode 100644
index fd02cca..0000000
--- a/src/home/mqtt/module/temphum.py
+++ /dev/null
@@ -1,82 +0,0 @@
-from .._node import MqttNode
-from .._module import MqttModule
-from .._payload import MqttPayload
-from typing import Optional
-from ...temphum import BaseSensor
-
-two_digits_precision = lambda x: round(x, 2)
-
-MODULE_NAME = 'MqttTempHumModule'
-DATA_TOPIC = 'temphum/data'
-
-
-class MqttTemphumDataPayload(MqttPayload):
- FORMAT = '=ddb'
- UNPACKER = {
- 'temp': two_digits_precision,
- 'rh': two_digits_precision
- }
-
- temp: float
- rh: float
- error: int
-
-
-# class MqttTempHumNodes(HashableEnum):
-# KBN_SH_HALL = auto()
-# KBN_SH_BATHROOM = auto()
-# KBN_SH_LIVINGROOM = auto()
-# KBN_SH_BEDROOM = auto()
-#
-# KBN_BH_2FL = auto()
-# KBN_BH_2FL_STREET = auto()
-# KBN_BH_1FL_LIVINGROOM = auto()
-# KBN_BH_1FL_BEDROOM = auto()
-# KBN_BH_1FL_BATHROOM = auto()
-#
-# KBN_NH_1FL_INV = auto()
-# KBN_NH_1FL_CENTER = auto()
-# KBN_NH_1LF_KT = auto()
-# KBN_NH_1FL_DS = auto()
-# KBN_NH_1FS_EZ = auto()
-#
-# SPB_FLAT120_CABINET = auto()
-
-
-class MqttTempHumModule(MqttModule):
- def __init__(self,
- sensor: Optional[BaseSensor] = None,
- write_to_database=False,
- *args, **kwargs):
- if sensor is not None:
- kwargs['tick_interval'] = 10
- super().__init__(*args, **kwargs)
- self._sensor = sensor
-
- def on_connect(self, mqtt: MqttNode):
- super().on_connect(mqtt)
- mqtt.subscribe_module(DATA_TOPIC, self)
-
- def tick(self):
- if not self._sensor:
- return
-
- error = 0
- temp = 0
- rh = 0
- try:
- temp = self._sensor.temperature()
- rh = self._sensor.humidity()
- except:
- error = 1
- pld = MqttTemphumDataPayload(temp=temp, rh=rh, error=error)
- self._mqtt_node_ref.publish(DATA_TOPIC, pld.pack())
-
- def handle_payload(self,
- mqtt: MqttNode,
- topic: str,
- payload: bytes) -> Optional[MqttPayload]:
- if topic == DATA_TOPIC:
- message = MqttTemphumDataPayload.unpack(payload)
- self._logger.debug(message)
- return message