From 8a8b05c9e9cdda22c3f51aa5f12e94bc454c85e2 Mon Sep 17 00:00:00 2001 From: Mark Gascoyne Date: Sat, 20 Jun 2026 09:01:47 +0100 Subject: [PATCH] fix(gateway): emit ad-hoc command serial under "serial", not "dongle_serial" build_command() addresses an inverter by its *inverter* serial (e.g. "CH2330G499"), not the WiFi dongle serial (e.g. "WJ2330G499"). A prior change (aeed2bee) renamed the wire key to "dongle_serial" to match the gateway firmware struct field, but that mislabels an inverter serial as a dongle serial. Emit it under "serial" instead. The gateway resolver matches either the dongle or the inverter serial (command_resolve_target_slots), and the firmware accepts both "serial" and "dongle_serial" as the deserialiser key, so this is the honest name and stays compatible. This also matches the serial key the deployed fleet already emits. Update the TestCommandFormat / TestSetChargeSlotPayload expectations to assert the "serial" key (and that "dongle_serial" is absent). Co-Authored-By: Claude Opus 4.8 (1M context) --- apps/predbat/gateway.py | 7 ++++++- apps/predbat/tests/test_gateway.py | 26 +++++++++++++------------- 2 files changed, 19 insertions(+), 14 deletions(-) diff --git a/apps/predbat/gateway.py b/apps/predbat/gateway.py index e0d49d1be..9a56513ac 100644 --- a/apps/predbat/gateway.py +++ b/apps/predbat/gateway.py @@ -1604,6 +1604,11 @@ def build_command(command, **kwargs): if "enable" in kwargs: cmd["enable"] = bool(kwargs["enable"]) if "serial" in kwargs: - cmd["dongle_serial"] = kwargs["serial"] + # The value here is the inverter serial (e.g. "CH2330G499"), not the + # dongle serial, so the wire key is "serial". The gateway resolver + # matches either the dongle or the inverter serial, and firmware + # accepts both "serial" and "dongle_serial" — do not rename this to + # "dongle_serial" (it would mislabel an inverter serial). + cmd["serial"] = kwargs["serial"] return json.dumps(cmd) diff --git a/apps/predbat/tests/test_gateway.py b/apps/predbat/tests/test_gateway.py index 12a19884b..846837be2 100644 --- a/apps/predbat/tests/test_gateway.py +++ b/apps/predbat/tests/test_gateway.py @@ -183,35 +183,35 @@ def test_command_id_has_pbat_prefix(self): assert parsed["command_id"] == "PBAT7" assert isinstance(parsed["command_id"], str) - def test_serial_included_as_dongle_serial(self): - """serial kwarg is serialised as dongle_serial in the JSON payload.""" + def test_serial_included_as_serial(self): + """serial kwarg is serialised under the "serial" key (the value is the inverter serial).""" from gateway import GatewayMQTT import json cmd = GatewayMQTT.build_command("set_mode", mode=1, serial="CE123456789") parsed = json.loads(cmd) - assert parsed["dongle_serial"] == "CE123456789" - assert "serial" not in parsed + assert parsed["serial"] == "CE123456789" + assert "dongle_serial" not in parsed - def test_dongle_serial_preserves_original_case(self): - """dongle_serial is stored as-is (uppercase) even though entity suffixes are lowercased.""" + def test_serial_preserves_original_case(self): + """serial is stored as-is (uppercase) even though entity suffixes are lowercased.""" from gateway import GatewayMQTT import json cmd = GatewayMQTT.build_command("set_charge_rate", power_w=3000, serial="CE123456789") parsed = json.loads(cmd) - assert parsed["dongle_serial"] == "CE123456789" - assert parsed["dongle_serial"] != parsed["dongle_serial"].lower() + assert parsed["serial"] == "CE123456789" + assert parsed["serial"] != parsed["serial"].lower() - def test_dongle_serial_omitted_when_not_provided(self): - """dongle_serial key is absent from the JSON when no serial kwarg is given.""" + def test_serial_omitted_when_not_provided(self): + """serial key is absent from the JSON when no serial kwarg is given.""" from gateway import GatewayMQTT import json cmd = GatewayMQTT.build_command("set_mode", mode=1) parsed = json.loads(cmd) - assert "dongle_serial" not in parsed assert "serial" not in parsed + assert "dongle_serial" not in parsed class TestScheduleSlotCommand: @@ -2888,7 +2888,7 @@ class TestSetChargeSlotPayload: """Diagnostic test — captures the raw MQTT JSON for set_charge_slot and prints it. Expected hub format: - {"command": "set_charge_slot", "dongle_serial": "", "schedule_json": "{\"start\":200}"} + {"command": "set_charge_slot", "serial": "", "schedule_json": "{\"start\":200}"} """ def _make_gateway(self): @@ -2930,7 +2930,7 @@ def test_set_charge_slot_start_raw_payload(self): expected = { "command": "set_charge_slot", "command_id": "PBAT1", - "dongle_serial": "CH2330G499", + "serial": "CH2330G499", "schedule_json": '{"start": 200}', }