diff --git a/prpd_usb/faker.py b/prpd_usb/faker.py index 14d58aa..b1a9f6f 100644 --- a/prpd_usb/faker.py +++ b/prpd_usb/faker.py @@ -18,6 +18,7 @@ h("5a0070020000e1b7a5"), { "PR37SB": h("5aff7002001649dd0000024248450000024af0430882021c0226052404b3a5"), "PR50SB": h("5aff7002001671d4000001a971b6000001aaf04309b0020801d60522150ea5")} + ), ( h("5a00710200001db6a5"), { "PR37SB": h("5aff7102000c009b278c00a10c63013c33ef84f9a5"), diff --git a/prpd_usb/output/mqtt_openwb.py b/prpd_usb/output/mqtt_openwb.py index 3f686b0..d188e92 100644 --- a/prpd_usb/output/mqtt_openwb.py +++ b/prpd_usb/output/mqtt_openwb.py @@ -7,7 +7,8 @@ Direct = namedtuple('Direct', ['key']) Map = namedtuple('Map', ['keys', 'function']) -MultiMap = namedtuple('Map', ['keys', 'functions']) +MultiMap = namedtuple('MultiMap', ['keys', 'functions']) +FunctionCall = namedtuple('FunctionCall', ['keys', 'function']) def _neg(value): return value * -1 @@ -18,12 +19,17 @@ def _sum(*values): def _m_sum(*values): return [sum(values)] -MAPPING = { +def _calc_pv_power_pr37sb(current_1, current_2, voltage_1, voltage_2): + return current_1 * voltage_1 + current_2 * voltage_2 + +DEFAULT_MAPPING = { "openWB/set/evu/W": Map(keys=(('grid', 'power_w_phase_1'), ('grid', 'power_w_phase_2'), ('grid', 'power_w_phase_3')), function=_sum), "openWB/set/evu/APhase1": Direct(('grid', 'current_phase_1')), "openWB/set/evu/APhase2": Direct(('grid', 'current_phase_2')), "openWB/set/evu/APhase3": Direct(('grid', 'current_phase_3')), "openWB/set/evu/WhImported": Map(keys=(('grid', 'total_phase_1'), ('grid', 'total_phase_2'), ('grid', 'total_phase_3')), function=_sum), + # TODO: WhExported should be more complicated: + # this should be (at least) the total_solar - total_battery_consumed "openWB/set/evu/WhExported": Map(keys=(('solar', 'total_phase_1'), ('solar', 'total_phase_2'), ('solar', 'total_phase_3')), function=_sum), "openWB/set/evu/VPhase1": Direct(('grid', 'voltage_phase_1')), "openWB/set/evu/VPhase2": Direct(('grid', 'voltage_phase_2')), @@ -39,17 +45,34 @@ def _m_sum(*values): "openWB/set/houseBattery/%Soc": Direct(('battery', 'soc')), } +MAPPING_BY_MODEL = { + "PR37SB": { + "openWB/set/evu/WhExported": Direct(('solar', 'total')), + "openWB/set/pv/1/W": FunctionCall(keys=(('solar', 'current_string_1'), ('solar', 'current_string_2'), ('solar', 'voltage_string_1'), ('solar', 'voltage_string_2'),), function=_calc_pv_power_pr37sb), + "openWB/set/pv/1/WhCounter": Direct(('solar', 'total')) + } +} -def transform_to_openwb(prpd_data): +def transform_to_openwb(prpd_data, model): messages = [] data = {} for command, field, _time, value in prpd_data: data[(command.name, field.name)] = value - for topic, mapping in MAPPING.items(): + from pprint import pprint + pprint(data) + + mapping = DEFAULT_MAPPING.copy() + if model in MAPPING_BY_MODEL: + mapping.update(MAPPING_BY_MODEL[model]) + + for topic, mapping in mapping.items(): if isinstance(mapping, Direct): payload = data[mapping.key] + elif isinstance(mapping, FunctionCall): + values = [data[k] for k in mapping.keys] + payload = mapping.function(*values) elif isinstance(mapping, MultiMap): values = [data[k] for k in mapping.keys] for function in mapping.functions: @@ -74,7 +97,7 @@ def main(prpd_reader, args): while True: data = prpd_reader.read() - messages = transform_to_openwb(data) + messages = transform_to_openwb(data, prpd_reader.model) publish.multiple(messages, hostname=args.mqtt_hostname, port=args.mqtt_port, auth=auth) time.sleep(args.mqtt_interval) diff --git a/prpd_usb/prpd.py b/prpd_usb/prpd.py index de69895..b40b7ac 100644 --- a/prpd_usb/prpd.py +++ b/prpd_usb/prpd.py @@ -74,6 +74,10 @@ def init(self, model=None): self._model = MODELS[model] logger.info(f"Identified model {self._model}") + @property + def model(self): + return self._model + def read(self): with self.lock: for command in self._get_commands(): diff --git a/tests/test_openwb.py b/tests/test_openwb.py index 5bfab75..b26f994 100644 --- a/tests/test_openwb.py +++ b/tests/test_openwb.py @@ -9,7 +9,7 @@ def test_openwb_37bi(): prpd = _PrPd(SerialFaker("PR37Bi")) prpd.init() data = prpd.read() - messages = transform_to_openwb(data) + messages = transform_to_openwb(data, "PR37Bi") assert messages == [ {"payload": "18", "topic": "openWB/set/evu/W"}, {"payload": "1.17", "topic": "openWB/set/evu/APhase1"}, @@ -28,3 +28,27 @@ def test_openwb_37bi(): {"payload": "5545710", "topic": "openWB/set/houseBattery/WhExported"}, {"payload": "76", "topic": "openWB/set/houseBattery/%Soc"}, ] + +def test_openwb_37sb(): + prpd = _PrPd(SerialFaker("PR37SB")) + prpd.init() + data = prpd.read() + messages = transform_to_openwb(data, "PR37SB") + assert messages == [ + {'payload': '-16', 'topic': 'openWB/set/evu/W'}, + {'payload': '1.06', 'topic': 'openWB/set/evu/APhase1'}, + {'payload': '2.9', 'topic': 'openWB/set/evu/APhase2'}, + {'payload': '3.79', 'topic': 'openWB/set/evu/APhase3'}, + {'payload': '17090500', 'topic': 'openWB/set/evu/WhImported'}, + {'payload': '20722671', 'topic': 'openWB/set/evu/WhExported'}, + {'payload': '227.20000000000002', 'topic': 'openWB/set/evu/VPhase1'}, + {'payload': '227.0', 'topic': 'openWB/set/evu/VPhase2'}, + {'payload': '227.4', 'topic': 'openWB/set/evu/VPhase3'}, + {'payload': '50.01', 'topic': 'openWB/set/evu/HzFrequenz'}, + {'payload': '2177.0987999999998', 'topic': 'openWB/set/pv/1/W'}, + {'payload': '20722671', 'topic': 'openWB/set/pv/1/WhCounter'}, + {'payload': '502', 'topic': 'openWB/set/houseBattery/W'}, + {'payload': '5806854', 'topic': 'openWB/set/houseBattery/WhImported'}, + {'payload': '4497664', 'topic': 'openWB/set/houseBattery/WhExported'}, + {'payload': '78', 'topic': 'openWB/set/houseBattery/%Soc'}, + ]