Skip to content

Fix joins through a specific node timing out #150

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 2, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ repos:
- id: debug-statements

- repo: https://github.com/psf/black
rev: 22.1.0
rev: 22.3.0
hooks:
- id: black

Expand Down
47 changes: 38 additions & 9 deletions tests/application/test_joining.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,8 @@
)


@pytest.mark.parametrize(
"device,fixed_joining_bug",
[(d, False) for d in FORMED_DEVICES] + [(FormedLaunchpadCC26X2R1, True)],
)
async def test_permit_join(device, fixed_joining_bug, mocker, make_application):
if fixed_joining_bug:
mocker.patch.object(device, "code_revision", 20210708)

@pytest.mark.parametrize("device", FORMED_DEVICES)
async def test_permit_join(device, mocker, make_application):
app, znp_server = make_application(server_cls=device)

permit_join_coordinator = znp_server.reply_once_to(
Expand Down Expand Up @@ -70,7 +64,7 @@ async def test_permit_join(device, fixed_joining_bug, mocker, make_application):
await permit_join_broadcast
await permit_join_broadcast_raw

if fixed_joining_bug:
if device.code_revision >= 20210708:
assert not permit_join_coordinator.done()
else:
assert permit_join_coordinator.done()
Expand Down Expand Up @@ -101,6 +95,41 @@ async def test_join_coordinator(device, make_application):
await app.shutdown()


@pytest.mark.parametrize("device", [FormedLaunchpadCC26X2R1])
async def test_join_device(device, make_application):
ieee = t.EUI64.convert("EC:1B:BD:FF:FE:54:4F:40")
nwk = 0x1234

app, znp_server = make_application(server_cls=device)
device = app.add_initialized_device(ieee=ieee, nwk=nwk)

permit_join = znp_server.reply_once_to(
request=c.ZDO.MgmtPermitJoinReq.Req(
AddrMode=t.AddrMode.NWK, Dst=nwk, Duration=60, partial=True
),
responses=[
c.ZDO.MgmtPermitJoinReq.Rsp(Status=t.Status.SUCCESS),
c.ZDO.MgmtPermitJoinRsp.Callback(Src=nwk, Status=t.ZDOStatus.SUCCESS),
c.ZDO.MsgCbIncoming.Callback(
Src=nwk,
IsBroadcast=t.Bool.false,
ClusterId=32822,
SecurityUse=0,
TSN=6,
MacDst=0x0000,
Data=b"\x00",
),
],
)

await app.startup(auto_form=False)
await app.permit(node=ieee)

await permit_join

await app.shutdown()


@pytest.mark.parametrize("device", FORMED_ZSTACK3_DEVICES)
@pytest.mark.parametrize("permit_result", [None, asyncio.TimeoutError()])
async def test_permit_join_with_key(device, permit_result, make_application, mocker):
Expand Down
5 changes: 4 additions & 1 deletion tests/application/test_requests.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,14 @@
)


@pytest.mark.parametrize("device", FORMED_DEVICES)
@pytest.mark.parametrize("device", [FormedLaunchpadCC26X2R1])
async def test_chosen_dst_endpoint(device, make_application, mocker):
app, znp_server = make_application(device)
await app.startup(auto_form=False)

build = mocker.patch.object(type(app), "_zstack_build_id", mocker.PropertyMock())
build.return_value = 20200708

cluster = mocker.Mock()
cluster.endpoint.endpoint_id = 2
cluster.endpoint.profile_id = zigpy.profiles.zll.PROFILE_ID
Expand Down
6 changes: 3 additions & 3 deletions tests/application/test_startup.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,23 +20,23 @@

DEV_NETWORK_SETTINGS = {
FormedLaunchpadCC26X2R1: (
"CC1352/CC2652, Z-Stack 3.30+ (build 20200805)",
f"CC1352/CC2652, Z-Stack 3.30+ (build {FormedLaunchpadCC26X2R1.code_revision})",
15,
t.Channels.from_channel_list([15]),
0x4402,
t.EUI64.convert("A2:BA:38:A8:B5:E6:83:A0"),
t.KeyData.convert("4C:4E:72:B8:41:22:51:79:9A:BF:35:25:12:88:CA:83"),
),
FormedZStack3CC2531: (
"CC2531, Z-Stack 3.0.x (build 20190425)",
f"CC2531, Z-Stack 3.0.x (build {FormedZStack3CC2531.code_revision})",
15,
t.Channels.from_channel_list([15]),
0xB6AB,
t.EUI64.convert("62:92:32:46:3C:77:2D:B2"),
t.KeyData.convert("6D:DE:24:EA:E2:85:52:B6:DE:29:56:EB:05:85:1A:FA"),
),
FormedZStack1CC2531: (
"CC2531, Z-Stack Home 1.2 (build 20190608)",
f"CC2531, Z-Stack Home 1.2 (build {FormedZStack1CC2531.code_revision})",
11,
t.Channels.from_channel_list([11]),
0x1A62,
Expand Down
2 changes: 1 addition & 1 deletion tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -1111,7 +1111,7 @@ def connection_made(self):
class BaseLaunchpadCC26X2R1(BaseZStack3Device):
version = 3.30
align_structs = True
code_revision = 20200805
code_revision = 20220219

def _create_network_nvram(self):
super()._create_network_nvram()
Expand Down
2 changes: 1 addition & 1 deletion zigpy_znp/types/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -345,7 +345,7 @@ def __init__(self, *, partial=False, **params):
and not issubclass(param.type, enum.Enum),

isinstance(value, bytes)
and issubclass(param.type, (t.ShortBytes, t.LongBytes)),
and issubclass(param.type, (t.ShortBytes, t.LongBytes, t.Bytes)),

isinstance(value, list) and issubclass(param.type, list),
isinstance(value, bool) and issubclass(param.type, t.Bool),
Expand Down
11 changes: 10 additions & 1 deletion zigpy_znp/zigbee/application.py
Original file line number Diff line number Diff line change
Expand Up @@ -1236,6 +1236,13 @@ async def _send_request_raw(
# addressing another device. The router will receive the ZDO request and a
# device will try to join, but Z-Stack will never send the network key.
if cluster == zdo_t.ZDOCmd.Mgmt_Permit_Joining_req:
if dst_addr.mode == t.AddrMode.Broadcast:
# The coordinator responds to broadcasts
permit_addr = 0x0000
else:
# Otherwise, the destination device responds
permit_addr = dst_addr.address

await self._znp.request_callback_rsp(
request=c.ZDO.MgmtPermitJoinReq.Req(
AddrMode=dst_addr.mode,
Expand All @@ -1244,7 +1251,9 @@ async def _send_request_raw(
TCSignificance=data[2],
),
RspStatus=t.Status.SUCCESS,
callback=c.ZDO.MgmtPermitJoinRsp.Callback(Src=0x0000, partial=True),
callback=c.ZDO.MgmtPermitJoinRsp.Callback(
Src=permit_addr, partial=True
),
)
# Internally forward ZDO requests destined for the coordinator back to zigpy
# so we can send internal Z-Stack requests when necessary
Expand Down