Skip to content

Commit 13781ea

Browse files
committed
Always send ZDO.MgmtPermitJoinReq.Req when permitting joins
1 parent 201f4ea commit 13781ea

File tree

4 files changed

+71
-33
lines changed

4 files changed

+71
-33
lines changed

tests/application/test_joining.py

Lines changed: 26 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -29,47 +29,51 @@ async def test_permit_join(device, fixed_joining_bug, mocker, make_application):
2929

3030
app, znp_server = make_application(server_cls=device)
3131

32-
# Handle us opening joins on the coordinator
3332
permit_join_coordinator = znp_server.reply_once_to(
34-
request=zdo_request_matcher(
35-
dst_addr=t.AddrModeAddress(t.AddrMode.NWK, 0x0000),
36-
command_id=zdo_t.ZDOCmd.Mgmt_Permit_Joining_req,
37-
TSN=7,
38-
zdo_PermitDuration=10,
39-
zdo_TC_Significant=0,
33+
request=c.ZDO.MgmtPermitJoinReq.Req(
34+
AddrMode=t.AddrMode.NWK, Dst=0x0000, Duration=10, partial=True
4035
),
4136
responses=[
42-
c.AF.DataRequestExt.Rsp(Status=t.Status.SUCCESS),
37+
c.ZDO.MgmtPermitJoinReq.Rsp(Status=t.Status.SUCCESS),
4338
c.ZDO.MgmtPermitJoinRsp.Callback(Src=0x0000, Status=t.ZDOStatus.SUCCESS),
4439
],
4540
)
4641

4742
# Handle the ZDO broadcast sent by Zigpy
48-
permit_join_broadcast = znp_server.reply_once_to(
43+
permit_join_broadcast_raw = znp_server.reply_once_to(
4944
request=zdo_request_matcher(
5045
dst_addr=t.AddrModeAddress(t.AddrMode.Broadcast, 0xFFFC),
5146
command_id=zdo_t.ZDOCmd.Mgmt_Permit_Joining_req,
52-
TSN=8 if not fixed_joining_bug else 7,
47+
TSN=6,
5348
zdo_PermitDuration=10,
5449
zdo_TC_Significant=0,
5550
),
5651
responses=[
5752
c.AF.DataRequestExt.Rsp(Status=t.Status.SUCCESS),
53+
],
54+
)
55+
56+
# And the duplicate one using the MT command
57+
permit_join_broadcast = znp_server.reply_once_to(
58+
request=c.ZDO.MgmtPermitJoinReq.Req(
59+
AddrMode=t.AddrMode.Broadcast, Dst=0xFFFC, Duration=10, partial=True
60+
),
61+
responses=[
62+
c.ZDO.MgmtPermitJoinReq.Rsp(Status=t.Status.SUCCESS),
5863
c.ZDO.MgmtPermitJoinRsp.Callback(Src=0x0000, Status=t.ZDOStatus.SUCCESS),
5964
],
6065
)
6166

6267
await app.startup(auto_form=False)
6368
await app.permit(time_s=10)
6469

65-
if fixed_joining_bug:
66-
await permit_join_broadcast
70+
await permit_join_broadcast
71+
await permit_join_broadcast_raw
6772

68-
# Joins should not have been opened on the coordinator
73+
if fixed_joining_bug:
6974
assert not permit_join_coordinator.done()
7075
else:
71-
await permit_join_coordinator
72-
await permit_join_broadcast
76+
assert permit_join_coordinator.done()
7377

7478
await app.shutdown()
7579

@@ -80,15 +84,11 @@ async def test_join_coordinator(device, make_application):
8084

8185
# Handle us opening joins on the coordinator
8286
permit_join_coordinator = znp_server.reply_once_to(
83-
request=zdo_request_matcher(
84-
dst_addr=t.AddrModeAddress(t.AddrMode.NWK, 0x0000),
85-
command_id=zdo_t.ZDOCmd.Mgmt_Permit_Joining_req,
86-
TSN=7,
87-
zdo_PermitDuration=60,
88-
zdo_TC_Significant=0,
87+
request=c.ZDO.MgmtPermitJoinReq.Req(
88+
AddrMode=t.AddrMode.NWK, Dst=0x0000, Duration=60, partial=True
8989
),
9090
responses=[
91-
c.AF.DataRequestExt.Rsp(Status=t.Status.SUCCESS),
91+
c.ZDO.MgmtPermitJoinReq.Rsp(Status=t.Status.SUCCESS),
9292
c.ZDO.MgmtPermitJoinRsp.Callback(Src=0x0000, Status=t.ZDOStatus.SUCCESS),
9393
],
9494
)
@@ -328,7 +328,7 @@ async def test_unknown_device_discovery(device, make_application, mocker):
328328
request=zdo_request_matcher(
329329
dst_addr=t.AddrModeAddress(t.AddrMode.NWK, existing_nwk + 1),
330330
command_id=zdo_t.ZDOCmd.IEEE_addr_req,
331-
TSN=7,
331+
TSN=6,
332332
zdo_NWKAddrOfInterest=existing_nwk + 1,
333333
zdo_RequestType=c.zdo.AddrRequestType.SINGLE,
334334
zdo_StartIndex=0,
@@ -340,7 +340,7 @@ async def test_unknown_device_discovery(device, make_application, mocker):
340340
IsBroadcast=t.Bool.false,
341341
ClusterId=zdo_t.ZDOCmd.IEEE_addr_rsp,
342342
SecurityUse=0,
343-
TSN=7,
343+
TSN=6,
344344
MacDst=0x0000,
345345
Data=serialize_zdo_command(
346346
command_id=zdo_t.ZDOCmd.IEEE_addr_rsp,
@@ -387,7 +387,7 @@ async def test_unknown_device_discovery(device, make_application, mocker):
387387
request=zdo_request_matcher(
388388
dst_addr=t.AddrModeAddress(t.AddrMode.NWK, new_nwk),
389389
command_id=zdo_t.ZDOCmd.IEEE_addr_req,
390-
TSN=8,
390+
TSN=7,
391391
zdo_NWKAddrOfInterest=new_nwk,
392392
zdo_RequestType=c.zdo.AddrRequestType.SINGLE,
393393
zdo_StartIndex=0,
@@ -399,7 +399,7 @@ async def test_unknown_device_discovery(device, make_application, mocker):
399399
IsBroadcast=t.Bool.false,
400400
ClusterId=zdo_t.ZDOCmd.IEEE_addr_rsp,
401401
SecurityUse=0,
402-
TSN=8,
402+
TSN=7,
403403
MacDst=0x0000,
404404
Data=serialize_zdo_command(
405405
command_id=zdo_t.ZDOCmd.IEEE_addr_rsp,

tests/application/test_requests.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ async def test_zigpy_request(device, make_application):
4848
app, znp_server = make_application(device)
4949
await app.startup(auto_form=False)
5050

51-
TSN = 7
51+
TSN = 6
5252

5353
device = app.add_initialized_device(ieee=t.EUI64(range(8)), nwk=0xAABB)
5454

@@ -108,7 +108,7 @@ async def test_zigpy_request_failure(device, make_application, mocker):
108108
app, znp_server = make_application(device)
109109
await app.startup(auto_form=False)
110110

111-
TSN = 7
111+
TSN = 6
112112

113113
device = app.add_initialized_device(ieee=t.EUI64(range(8)), nwk=0xAABB)
114114

@@ -552,7 +552,7 @@ def set_route_discovered(req):
552552
request=zdo_request_matcher(
553553
dst_addr=t.AddrModeAddress(t.AddrMode.NWK, device.nwk),
554554
command_id=zdo_t.ZDOCmd.Active_EP_req,
555-
TSN=7,
555+
TSN=6,
556556
zdo_NWKAddrOfInterest=device.nwk,
557557
),
558558
responses=[
@@ -567,7 +567,7 @@ def set_route_discovered(req):
567567
IsBroadcast=t.Bool.false,
568568
ClusterId=zdo_t.ZDOCmd.Active_EP_rsp,
569569
SecurityUse=0,
570-
TSN=7,
570+
TSN=6,
571571
MacDst=device.nwk,
572572
Data=serialize_zdo_command(
573573
command_id=zdo_t.ZDOCmd.Active_EP_rsp,

tests/conftest.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -567,6 +567,20 @@ def _default_nib(self):
567567
nwkUpdateId=0,
568568
)
569569

570+
@reply_to(
571+
c.ZDO.MgmtPermitJoinReq.Req(AddrMode=t.AddrMode.NWK, Dst=0x0000, partial=True)
572+
)
573+
@reply_to(
574+
c.ZDO.MgmtPermitJoinReq.Req(
575+
AddrMode=t.AddrMode.Broadcast, Dst=0xFFFC, partial=True
576+
)
577+
)
578+
def permit_join(self, request):
579+
return [
580+
c.ZDO.MgmtPermitJoinReq.Rsp(Status=t.Status.SUCCESS),
581+
c.ZDO.MgmtPermitJoinRsp.Callback(Src=0x0000, Status=t.ZDOStatus.SUCCESS),
582+
]
583+
570584
@reply_to(c.AF.DataRequestExt.Req(partial=True, DstEndpoint=0))
571585
def on_zdo_request(self, req):
572586
kwargs = deserialize_zdo_command(req.ClusterId, req.Data[1:])

zigpy_znp/zigbee/application.py

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -528,7 +528,7 @@ async def mrequest(
528528
data=data,
529529
)
530530

531-
async def permit(self, time_s=60, node=None):
531+
async def permit(self, time_s: int = 60, node: t.EUI64 = None):
532532
"""
533533
Permit joining the network via a specific node or via all router nodes.
534534
"""
@@ -542,9 +542,18 @@ async def permit(self, time_s=60, node=None):
542542
#
543543
# Fixed in https://github.com/Koenkk/Z-Stack-firmware/commit/efac5ee46b9b437
544544
if time_s == 0 or self._zstack_build_id < 20210708 or node == self.ieee:
545-
response = await self.zigpy_device.zdo.Mgmt_Permit_Joining_req(time_s, 0)
545+
response = await self._znp.request_callback_rsp(
546+
request=c.ZDO.MgmtPermitJoinReq.Req(
547+
AddrMode=t.AddrMode.NWK,
548+
Dst=0x0000,
549+
Duration=time_s,
550+
TCSignificance=1,
551+
),
552+
RspStatus=t.Status.SUCCESS,
553+
callback=c.ZDO.MgmtPermitJoinRsp.Callback(Src=0x0000, partial=True),
554+
)
546555

547-
if response[0] != t.Status.SUCCESS:
556+
if response.Status != t.Status.SUCCESS:
548557
raise RuntimeError(f"Failed to permit joins on coordinator: {response}")
549558

550559
await super().permit(time_s=time_s, node=node)
@@ -660,6 +669,7 @@ def _bind_callbacks(self) -> None:
660669
c.ZDO.NodeDescRsp,
661670
c.ZDO.SimpleDescRsp,
662671
c.ZDO.ActiveEpRsp,
672+
c.ZDO.MgmtLqiRsp,
663673
]:
664674
self._znp.callback_for_response(
665675
ignored_msg.Callback(partial=True),
@@ -1283,6 +1293,20 @@ async def _send_request_raw(
12831293
Data=data,
12841294
)
12851295

1296+
# XXX: Joins *must* be sent via a ZDO command. Otherwise, Z-Stack will not
1297+
# actually permit the coordinator to send the network key.
1298+
if dst_ep == ZDO_ENDPOINT and cluster == zdo_t.ZDOCmd.Mgmt_Permit_Joining_req:
1299+
await self._znp.request_callback_rsp(
1300+
request=c.ZDO.MgmtPermitJoinReq.Req(
1301+
AddrMode=dst_addr.mode,
1302+
Dst=dst_addr.address,
1303+
Duration=data[1],
1304+
TCSignificance=data[2],
1305+
),
1306+
RspStatus=t.Status.SUCCESS,
1307+
callback=c.ZDO.MgmtPermitJoinRsp.Callback(Src=0x0000, partial=True),
1308+
)
1309+
12861310
if dst_addr.mode == t.AddrMode.Broadcast or dst_ep == ZDO_ENDPOINT:
12871311
# Broadcasts and ZDO requests will not receive a confirmation
12881312
response = await self._znp.request(

0 commit comments

Comments
 (0)