summaryrefslogtreecommitdiff
path: root/src/home/mqtt/module
diff options
context:
space:
mode:
authorEvgeny Zinoviev <me@ch1p.io>2023-06-07 22:41:22 +0300
committerEvgeny Zinoviev <me@ch1p.io>2023-06-07 22:41:22 +0300
commit5aad97192dc5941147d91d28486eaff5a61afeb0 (patch)
tree45953b3e884d128f7c92387b773db47b0b603a56 /src/home/mqtt/module
parentae8070b2dd8c05dc6ed057d733f6d57f396ad100 (diff)
parentebe7990bf3dd475f1963a0c1eaaa5f230f3d5d48 (diff)
Merge branch 'mqtt-refactoring' of ch1p.io:homekit into mqtt-refactoring
Diffstat (limited to 'src/home/mqtt/module')
-rw-r--r--src/home/mqtt/module/inverter.py128
-rw-r--r--src/home/mqtt/module/temphum.py25
2 files changed, 144 insertions, 9 deletions
diff --git a/src/home/mqtt/module/inverter.py b/src/home/mqtt/module/inverter.py
index 9cf2978..d927a06 100644
--- a/src/home/mqtt/module/inverter.py
+++ b/src/home/mqtt/module/inverter.py
@@ -1,13 +1,31 @@
-import struct
+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
-class Status(MqttPayload):
+MODULE_NAME = 'MqttInverterModule'
+
+STATUS_TOPIC = 'status'
+GENERATION_TOPIC = 'generation'
+
+
+class MqttInverterStatusPayload(MqttPayload):
# 46 bytes
FORMAT = 'IHHHHHHBHHHHHBHHHHHHHH'
@@ -65,7 +83,7 @@ class Status(MqttPayload):
load_connected: bit_field(0, 16, 1)
-class Generation(MqttPayload):
+class MqttInverterGenerationPayload(MqttPayload):
# 8 bytes
FORMAT = 'II'
@@ -73,5 +91,105 @@ class Generation(MqttPayload):
wh: int
-class MqttInverterModule(MqttNode):
- pass
+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/temphum.py b/src/home/mqtt/module/temphum.py
index 5501f91..83ae34d 100644
--- a/src/home/mqtt/module/temphum.py
+++ b/src/home/mqtt/module/temphum.py
@@ -9,6 +9,7 @@ from ...temphum import BaseSensor
two_digits_precision = lambda x: round(x, 2)
MODULE_NAME = 'MqttTempHumModule'
+DATA_TOPIC = 'temphum/data'
class MqttTemphumDataPayload(MqttPayload):
@@ -45,22 +46,38 @@ class MqttTemphumDataPayload(MqttPayload):
class MqttTempHumModule(MqttModule):
- def __init__(self, sensor: Optional[BaseSensor] = None, *args, **kwargs):
+ def __init__(self,
+ sensor: Optional[BaseSensor] = None,
+ *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('temphum/data', self)
+ mqtt.subscribe_module(DATA_TOPIC, self)
def tick(self):
- pass
+ 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 == 'temphum/data':
+ if topic == DATA_TOPIC:
message = MqttTemphumDataPayload.unpack(payload)
self._logger.debug(message)
return message