From cef6418a5071ef5f95c4cebaaac449805cde04f3 Mon Sep 17 00:00:00 2001 From: TheJulianJES Date: Tue, 28 Jul 2026 20:53:02 +0200 Subject: [PATCH 1/2] Honor the `APS_Encryption` transmit option Maps `zigpy.types.TransmitOptions.APS_Encryption` onto EmberZNet's `APS_OPTION_ENCRYPTION` APS frame option, so packets that require APS encryption (e.g. the Zigbee Direct Configuration cluster) are actually sent encrypted with the link key shared with the destination. --- bellows/zigbee/application.py | 4 ++++ tests/test_application.py | 17 +++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/bellows/zigbee/application.py b/bellows/zigbee/application.py index f64254ac..25792776 100644 --- a/bellows/zigbee/application.py +++ b/bellows/zigbee/application.py @@ -1000,6 +1000,10 @@ async def send_packet(self, packet: zigpy.types.ZigbeePacket) -> None: else: aps_frame.options |= t.EmberApsOption.APS_OPTION_ENABLE_ROUTE_DISCOVERY + if zigpy.types.TransmitOptions.APS_Encryption in packet.tx_options: + # APS encryption uses the link key shared with the destination node + aps_frame.options |= t.EmberApsOption.APS_OPTION_ENCRYPTION + extended_timeout = packet.extended_timeout # EmberZNet requires retrying to enable APS ACKs diff --git a/tests/test_application.py b/tests/test_application.py index 9170747b..c5355d2e 100644 --- a/tests/test_application.py +++ b/tests/test_application.py @@ -990,6 +990,23 @@ async def test_send_packet_unicast_force_route_discovery(app, packet): ) +async def test_send_packet_unicast_aps_encryption(app, packet): + await _test_send_packet_unicast( + app, + packet.replace( + tx_options=( + zigpy.types.TransmitOptions.ACK + | zigpy.types.TransmitOptions.APS_Encryption + ) + ), + options=( + t.EmberApsOption.APS_OPTION_ENABLE_ROUTE_DISCOVERY + | t.EmberApsOption.APS_OPTION_ENCRYPTION + | t.EmberApsOption.APS_OPTION_RETRY + ), + ) + + async def test_send_packet_unicast_unexpected_failure(app, packet): with pytest.raises(zigpy.exceptions.DeliveryError): await _test_send_packet_unicast(app, packet, status=t.EmberStatus.ERR_FATAL) From 6401d18502851141d245598511730aea5b4ae181 Mon Sep 17 00:00:00 2001 From: TheJulianJES Date: Tue, 28 Jul 2026 20:55:49 +0200 Subject: [PATCH 2/2] Drop the redundant APS encryption comment --- bellows/zigbee/application.py | 1 - 1 file changed, 1 deletion(-) diff --git a/bellows/zigbee/application.py b/bellows/zigbee/application.py index 25792776..bee1ca54 100644 --- a/bellows/zigbee/application.py +++ b/bellows/zigbee/application.py @@ -1001,7 +1001,6 @@ async def send_packet(self, packet: zigpy.types.ZigbeePacket) -> None: aps_frame.options |= t.EmberApsOption.APS_OPTION_ENABLE_ROUTE_DISCOVERY if zigpy.types.TransmitOptions.APS_Encryption in packet.tx_options: - # APS encryption uses the link key shared with the destination node aps_frame.options |= t.EmberApsOption.APS_OPTION_ENCRYPTION extended_timeout = packet.extended_timeout