From c38982ef644aef80c1183ddd5871c8c9fab1c40a Mon Sep 17 00:00:00 2001 From: Emmanuel Levijarvi Date: Sat, 29 Aug 2026 07:13:21 -0700 Subject: [PATCH 1/2] Capture the topic most device replies arrive on firmware_payload_capture.py subscribed to cmd/{type}/{client_id}/res/# and the event topic. Neither matches cmd/{type}/navilink-{mac}/{client_id}/res, which is where the device acknowledges control and query commands - so device status, device info and energy replies never reached the capture file, and the tool recorded 2 payloads where it should have recorded 11. Subscribing to cmd/{type}/navilink-{mac}/# as well fixes it, and has the side benefit of picking up requests and replies belonging to other clients on the same device, which is what a capture tool wants. Verified against the device: the same run now records the feature and status responses it was silently dropping. Claude-Session: https://claude.ai/code/session_01XVj9BYvuLj7Th3iFUVoeCn --- CHANGELOG.rst | 8 ++++++++ examples/advanced/firmware_payload_capture.py | 14 ++++++++++++-- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 308bc7d..ea049c4 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -88,6 +88,14 @@ Fixed ``docs/reference/protocol/device_features.rst``, which documented the wire spelling correctly all along. +- **``firmware_payload_capture.py`` captured almost nothing.** Its wildcards + covered ``cmd/{type}/{client_id}/res/#`` and the event topic, but the device + acknowledges control and query commands on + ``cmd/{type}/navilink-{mac}/{client_id}/res`` - which neither pattern + matches - so device status, device info and energy replies were all missed. + A capture run recorded 2 payloads where it should have recorded 11. It now + also subscribes to ``cmd/{type}/navilink-{mac}/#``, which additionally picks + up traffic from other clients on the same device. Version 9.3.0 (2026-08-03) ========================== diff --git a/examples/advanced/firmware_payload_capture.py b/examples/advanced/firmware_payload_capture.py index 7ab78a9..f0c8572 100755 --- a/examples/advanced/firmware_payload_capture.py +++ b/examples/advanced/firmware_payload_capture.py @@ -111,17 +111,27 @@ async def main() -> None: # --- Wildcard subscriptions to catch everything --- - # All response messages back to this client + # Everything under the device's own command path. This is where the + # device acknowledges control and query commands, on + # cmd/{type}/navilink-{mac}/{client_id}/res - status, device info and + # energy replies all land here, so a capture without this subscription + # misses most of the traffic. It also picks up requests and replies + # belonging to other clients on the same device (a Home Assistant + # integration, the vendor app), which is exactly what a capture wants. + cmd_wildcard = MqttTopicBuilder.command_topic(device_type, mac, "#") + # Query results routed back to this client specifically res_wildcard = MqttTopicBuilder.response_topic(device_type, client_id, "#") # All event messages pushed by the device evt_wildcard = MqttTopicBuilder.event_topic(device_type, mac, "#") print( - f"\nSubscribing to:\n {redact_topic(res_wildcard)}\n" + f"\nSubscribing to:\n {redact_topic(cmd_wildcard)}\n" + f" {redact_topic(res_wildcard)}\n" f" {redact_topic(evt_wildcard)}\n" ) print("Captured topics:") + await mqtt_client.subscribe(cmd_wildcard, capture.record) await mqtt_client.subscribe(res_wildcard, capture.record) await mqtt_client.subscribe(evt_wildcard, capture.record) From 7dd334be22d2c71c4088bd6b35a5e2c51a6ec50e Mon Sep 17 00:00:00 2001 From: Emmanuel Levijarvi Date: Sat, 29 Aug 2026 07:27:10 -0700 Subject: [PATCH 2/2] Describe accurately what the new wildcard adds The energy reply is routed to the client-keyed cmd/{type}/{client_id}/res/energy-usage-daily-query/rd, which the existing response wildcard already matched; only its request echo is on the device path. What the device path actually adds is the control/query acknowledgement carrying status and device info, the published requests, and other clients' traffic. Claude-Session: https://claude.ai/code/session_01XVj9BYvuLj7Th3iFUVoeCn --- CHANGELOG.rst | 11 +++++++---- examples/advanced/firmware_payload_capture.py | 17 ++++++++++------- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index ea049c4..5f48659 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -92,10 +92,13 @@ Fixed covered ``cmd/{type}/{client_id}/res/#`` and the event topic, but the device acknowledges control and query commands on ``cmd/{type}/navilink-{mac}/{client_id}/res`` - which neither pattern - matches - so device status, device info and energy replies were all missed. - A capture run recorded 2 payloads where it should have recorded 11. It now - also subscribes to ``cmd/{type}/navilink-{mac}/#``, which additionally picks - up traffic from other clients on the same device. + matches - so the device status and device info responses were missed, along + with every request published on the device path. A capture run recorded 2 + payloads where it should have recorded 11. It now also subscribes to + ``cmd/{type}/navilink-{mac}/#``, which additionally picks up traffic from + other clients on the same device. Query results the device routes back to a + client-keyed topic, such as reservations and energy usage, were already + covered by the existing response wildcard. Version 9.3.0 (2026-08-03) ========================== diff --git a/examples/advanced/firmware_payload_capture.py b/examples/advanced/firmware_payload_capture.py index f0c8572..72a1b3b 100755 --- a/examples/advanced/firmware_payload_capture.py +++ b/examples/advanced/firmware_payload_capture.py @@ -111,13 +111,16 @@ async def main() -> None: # --- Wildcard subscriptions to catch everything --- - # Everything under the device's own command path. This is where the - # device acknowledges control and query commands, on - # cmd/{type}/navilink-{mac}/{client_id}/res - status, device info and - # energy replies all land here, so a capture without this subscription - # misses most of the traffic. It also picks up requests and replies - # belonging to other clients on the same device (a Home Assistant - # integration, the vendor app), which is exactly what a capture wants. + # Everything under the device's own command path. The device + # acknowledges control and query commands on + # cmd/{type}/navilink-{mac}/{client_id}/res, which is where the status + # and device-info responses arrive, so a capture without this + # subscription misses them entirely. It also picks up the published + # requests themselves and the traffic of other clients on the same + # device (a Home Assistant integration, the vendor app), which is + # exactly what a capture wants. Query results that the device routes + # back to a client-keyed topic - reservations, energy usage - arrive + # under res_wildcard below instead. cmd_wildcard = MqttTopicBuilder.command_topic(device_type, mac, "#") # Query results routed back to this client specifically res_wildcard = MqttTopicBuilder.response_topic(device_type, client_id, "#")