From 2f833643c3ba050c434bb660543777260eaf9573 Mon Sep 17 00:00:00 2001 From: bedge117 <77640477+bedge117@users.noreply.github.com> Date: Fri, 4 Sep 2026 22:03:34 -0500 Subject: [PATCH] Fix gpepIncomingMessageHandler schema for EZSP v13+ The inherited schema predates the EmberZNet 7.x wire format, so every incoming Green Power frame failed to parse and was dropped before reaching the application. Redefine the handler for EZSP v13 with the layout 7.x NCPs emit: applicationId, 8-byte GPD id union, and endpoint, followed by the security, frame counter, command, MIC, proxy table index, and payload fields. Verified against frames captured from an EmberZNet 7.5.1 NCP receiving an EnOcean PTM 215Z switch; one such frame is added as a regression test. --- bellows/ezsp/v13/commands.py | 23 +++++++++++++++++++++++ tests/test_ezsp_v13.py | 19 +++++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/bellows/ezsp/v13/commands.py b/bellows/ezsp/v13/commands.py index f87664aec..26e79640c 100644 --- a/bellows/ezsp/v13/commands.py +++ b/bellows/ezsp/v13/commands.py @@ -34,6 +34,29 @@ "number_of_entries": t.uint8_t, }, ), + # Redefined: EmberZNet 7.x serializes EmberGpAddress as applicationId, + # an 8-byte id union, then endpoint, which the inherited schema predates. + "gpepIncomingMessageHandler": ( + 0x00C5, + {}, + { + "status": t.uint8_t, + "gpdLink": t.uint8_t, + "sequenceNumber": t.uint8_t, + "applicationId": t.uint8_t, + "gpdIdUnion": t.FixedList[t.uint8_t, 8], + "endpoint": t.uint8_t, + "gpdfSecurityLevel": t.uint8_t, + "gpdfSecurityKeyType": t.uint8_t, + "autoCommissioning": t.Bool, + "bidirectionalInfo": t.uint8_t, + "gpdSecurityFrameCounter": t.uint32_t, + "gpdCommandId": t.uint8_t, + "mic": t.uint32_t, + "proxyTableIndex": t.uint8_t, + "gpdCommandPayload": t.LVBytes, + }, + ), # The following commands are redefined because `SecurityManagerContext` changed "exportKey": ( 0x0114, diff --git a/tests/test_ezsp_v13.py b/tests/test_ezsp_v13.py index 69c5909ff..8a05d891d 100644 --- a/tests/test_ezsp_v13.py +++ b/tests/test_ezsp_v13.py @@ -227,3 +227,22 @@ async def test_factory_reset(ezsp_f) -> None: assert ezsp_f.tokenFactoryReset.mock_calls == [ call(excludeOutgoingFC=False, excludeBootCounter=False) ] + + +def test_gpep_incoming_message_handler_rx(ezsp_f): + """Test receiving a gpepIncomingMessageHandler frame from an EmberZNet 7.x NCP.""" + ezsp_f( + b"\x03\x01\x80\xc5\x00" + + bytes.fromhex("7fdbc1009eea43009eea43000002010000c116000010153064adff00") + ) + assert ezsp_f._handle_callback.call_count == 1 + name, args = ezsp_f._handle_callback.call_args[0] + assert name == "gpepIncomingMessageHandler" + assert args[3] == 0x00 # applicationId: source ID addressing + assert bytes(args[4][:4]) == bytes.fromhex("9eea4300") # sourceId 0x0043EA9E + assert args[6] == 0x02 # gpdfSecurityLevel: full frame counter + MIC + assert args[7] == 0x01 # gpdfSecurityKeyType + assert args[10] == 0x16C1 # gpdSecurityFrameCounter + assert args[11] == 0x10 # gpdCommandId + assert args[13] == 0xFF # proxyTableIndex: not in proxy table + assert args[14] == b"" # gpdCommandPayload