1
+ from __future__ import annotations
2
+
1
3
import asyncio
2
4
import logging
3
5
4
6
import zigpy .zdo
5
7
import zigpy .device
6
8
import zigpy .zdo .types as zdo_t
9
+ import zigpy .application
7
10
8
11
import zigpy_znp .types as t
9
12
import zigpy_znp .commands as c
13
+ import zigpy_znp .zigbee .application as znp_app
10
14
11
15
LOGGER = logging .getLogger (__name__ )
12
16
@@ -40,31 +44,61 @@ def model(self):
40
44
41
45
return f"{ model } , Z-Stack { version } (build { self .application ._zstack_build_id } )"
42
46
47
+ def request (
48
+ self ,
49
+ profile ,
50
+ cluster ,
51
+ src_ep ,
52
+ dst_ep ,
53
+ sequence ,
54
+ data ,
55
+ expect_reply = True ,
56
+ # Extend the default timeout
57
+ timeout = 2 * zigpy .device .APS_REPLY_TIMEOUT ,
58
+ use_ieee = False ,
59
+ ):
60
+ """
61
+ Normal `zigpy.device.Device:request` except its default timeout is longer.
62
+ """
63
+
64
+ return super ().request (
65
+ profile ,
66
+ cluster ,
67
+ src_ep ,
68
+ dst_ep ,
69
+ sequence ,
70
+ data ,
71
+ expect_reply = expect_reply ,
72
+ timeout = timeout ,
73
+ use_ieee = use_ieee ,
74
+ )
75
+
43
76
44
77
class ZNPZDOEndpoint (zigpy .zdo .ZDO ):
45
78
@property
46
- def app (self ):
79
+ def app (self ) -> zigpy . application . ControllerApplication :
47
80
return self .device .application
48
81
49
- async def async_handle_mgmt_permit_joining_req (
50
- self ,
51
- hdr : zdo_t .ZDOHeader ,
52
- PermitDuration : t .uint8_t ,
53
- TC_Significant : t .Bool ,
54
- * ,
55
- dst_addressing ,
82
+ def _send_loopback_reply (
83
+ self , command_id : zdo_t .ZDOCmd , * , tsn : t .uint8_t , ** kwargs
56
84
):
57
- # Joins *must* be sent via a ZDO command. Otherwise, Z-Stack will not actually
58
- # permit the coordinator to send the network key while routers will.
59
- await self .app ._znp .request_callback_rsp (
60
- request = c .ZDO .MgmtPermitJoinReq .Req (
61
- AddrMode = t .AddrMode .NWK ,
62
- Dst = 0x0000 ,
63
- Duration = PermitDuration ,
64
- TCSignificance = TC_Significant ,
65
- ),
66
- RspStatus = t .Status .SUCCESS ,
67
- callback = c .ZDO .MgmtPermitJoinRsp .Callback (Src = 0x0000 , partial = True ),
85
+ """
86
+ Constructs and sends back a loopback ZDO response.
87
+ """
88
+
89
+ message = t .uint8_t (tsn ).serialize () + self ._serialize (
90
+ command_id , * kwargs .values ()
91
+ )
92
+
93
+ LOGGER .debug ("Sending loopback reply %s (%s), tsn=%s" , command_id , kwargs , tsn )
94
+
95
+ self .app .handle_message (
96
+ sender = self .app .zigpy_device ,
97
+ profile = znp_app .ZDO_PROFILE ,
98
+ cluster = command_id ,
99
+ src_ep = znp_app .ZDO_ENDPOINT ,
100
+ dst_ep = znp_app .ZDO_ENDPOINT ,
101
+ message = message ,
68
102
)
69
103
70
104
def handle_mgmt_nwk_update_req (
@@ -83,7 +117,7 @@ def handle_mgmt_nwk_update_req(
83
117
async def async_handle_mgmt_nwk_update_req (
84
118
self , hdr : zdo_t .ZDOHeader , NwkUpdate : zdo_t .NwkUpdate , * , dst_addressing
85
119
):
86
- # Energy scans are handled properly by Z-Stack
120
+ # Energy scans are handled properly by Z-Stack, no need to do anything
87
121
if NwkUpdate .ScanDuration not in (
88
122
zdo_t .NwkUpdate .CHANNEL_CHANGE_REQ ,
89
123
zdo_t .NwkUpdate .CHANNEL_MASK_MANAGER_ADDR_CHANGE_REQ ,
@@ -97,6 +131,15 @@ async def async_handle_mgmt_nwk_update_req(
97
131
== NwkUpdate .ScanChannels
98
132
):
99
133
LOGGER .warning ("NWK update request is ignored when channel does not change" )
134
+ self ._send_loopback_reply (
135
+ zdo_t .ZDOCmd .Mgmt_NWK_Update_rsp ,
136
+ Status = zdo_t .Status .SUCCESS ,
137
+ ScannedChannels = t .Channels .NO_CHANNELS ,
138
+ TotalTransmissions = 0 ,
139
+ TransmissionFailures = 0 ,
140
+ EnergyValues = [],
141
+ tsn = hdr .tsn ,
142
+ )
100
143
return
101
144
102
145
await self .app ._znp .request (
@@ -128,3 +171,13 @@ async def async_handle_mgmt_nwk_update_req(
128
171
f" { self .app .state .network_information .nwk_update_id } instead of being"
129
172
f" set to { NwkUpdate .nwkUpdateId } "
130
173
)
174
+
175
+ self ._send_loopback_reply (
176
+ zdo_t .ZDOCmd .Mgmt_NWK_Update_rsp ,
177
+ Status = zdo_t .Status .SUCCESS ,
178
+ ScannedChannels = t .Channels .NO_CHANNELS ,
179
+ TotalTransmissions = 0 ,
180
+ TransmissionFailures = 0 ,
181
+ EnergyValues = [],
182
+ tsn = hdr .tsn ,
183
+ )
0 commit comments