Skip to content

Commit 074f33f

Browse files
committed
Use UTIL.AssocFindDevice to determine alignment and add new structs
1 parent a4338e5 commit 074f33f

File tree

5 files changed

+59
-18
lines changed

5 files changed

+59
-18
lines changed

tests/conftest.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -738,6 +738,10 @@ def update_channel():
738738
def zdo_route_check(self, request):
739739
return c.ZDO.ExtRouteChk.Rsp(Status=c.zdo.RoutingStatus.SUCCESS)
740740

741+
@reply_to(c.UTIL.AssocFindDevice.Req(Index=0))
742+
def assoc_find_dev_responder(self, req):
743+
return req.Rsp(Device=t.Bytes(b"\xFF" * (36 if self.align_structs else 28)))
744+
741745

742746
class BaseZStack1CC2531(BaseZStackDevice):
743747
align_structs = False

tests/tools/test_nvram.py

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -115,11 +115,6 @@ async def test_nvram_write(device, make_znp_server, tmp_path, mocker):
115115
# This already exists
116116
znp_server._nvram[ExNvIds.LEGACY][OsalNvIds.HAS_CONFIGURED_ZSTACK3] = b"\xBB"
117117

118-
# XXX: empty NVRAM breaks current alignment autodetection method (NWKKEY is missing)
119-
async def replacement(self, *args, **kwargs):
120-
self.align_structs = znp_server.align_structs
121-
122-
mocker.patch("zigpy_znp.nvram.NVRAMHelper.determine_alignment", new=replacement)
123118
await nvram_write([znp_server._port_path, "-i", str(backup_file)])
124119

125120
nvram_obj = dump_nvram(znp_server)

zigpy_znp/commands/util.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -399,7 +399,8 @@ class UTIL(t.CommandsBase, subsystem=t.Subsystem.UTIL):
399399
req_schema=(
400400
t.Param("Index", t.uint8_t, "Nth active entry in the device list"),
401401
),
402-
rsp_schema=(t.Param("Device", Device, "associated_devices_t structure"),),
402+
# XXX: The struct is not packed when sent: `write(&struct, sizeof(struct))`
403+
rsp_schema=(t.Param("Device", t.Bytes, "associated_devices_t structure"),),
403404
)
404405

405406
# a proxy call to the AssocGetWithAddress() function

zigpy_znp/nvram.py

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -25,21 +25,19 @@ async def determine_alignment(self) -> None:
2525
structs are read/written.
2626
"""
2727

28-
# TODO: Figure out a way to use `c.UTIL.AssocFindDevice.Req(Index=0)`!
29-
# This is the only (known) MT command to just send a struct's in-memory
30-
# representation over serial.
28+
# This is the only known MT command to respond with a struct's in-memory
29+
# representation over serial.
30+
LOGGER.debug("Detecting struct alignment")
31+
rsp = await self.znp.request(c.UTIL.AssocFindDevice.Req(Index=0))
3132

32-
# NWKKEY is almost always present, regardless of adapter state.
33-
# It is an 8-bit sequence number, a 16 byte key, and a 32-bit frame counter.
34-
# This is at least 1 + 16 + 4 = 21 bytes, or 24 if you have to 32-bit align.
35-
value = await self.osal_read(nvids.OsalNvIds.NWKKEY, item_type=t.Bytes)
36-
37-
if len(value) == 24:
38-
self.align_structs = True
39-
elif len(value) == 21:
33+
if len(rsp.Device) == 28:
4034
self.align_structs = False
35+
elif len(rsp.Device) == 36:
36+
# `AssociatedDevice` has an extra member at the end in Z-Stack 3.30 but
37+
# the struct does not change in size due to padding.
38+
self.align_structs = True
4139
else:
42-
raise ValueError(f"Unexpected value for NWKKEY: {value!r}")
40+
raise ValueError(f"Cannot determine alignment from struct: {rsp!r}")
4341

4442
LOGGER.debug("Detected struct alignment: %s", self.align_structs)
4543

zigpy_znp/types/structs.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,3 +217,46 @@ class APSLinkKeyTable(
217217
basic.LVList, length_type=basic.uint16_t, item_type=APSLinkKeyTableEntry
218218
):
219219
pass
220+
221+
222+
class LinkInfo(cstruct.CStruct):
223+
# Counter of transmission success/failures
224+
txCounter: basic.uint8_t
225+
# Average of sending rssi values if link staus is enabled
226+
# i.e. NWK_LINK_STATUS_PERIOD is defined as non zero
227+
txCost: basic.uint8_t
228+
# average of received rssi values.
229+
# needs to be converted to link cost (1-7) before use
230+
rxLqi: basic.uint8_t
231+
# security key sequence number
232+
inKeySeqNum: basic.uint8_t
233+
# security frame counter..
234+
inFrmCntr: basic.uint32_t
235+
# higher values indicate more failures
236+
txFailure: basic.uint16_t
237+
238+
239+
class AgingEndDevice(cstruct.CStruct):
240+
endDevCfg: basic.uint8_t
241+
deviceTimeout: basic.uint32_t
242+
243+
244+
class BaseAssociatedDevice(cstruct.CStruct):
245+
shortAddr: basic.uint16_t
246+
addrIdx: basic.uint16_t
247+
nodeRelation: basic.uint8_t
248+
devStatus: basic.uint8_t
249+
assocCnt: basic.uint8_t
250+
age: basic.uint8_t
251+
linkInfo: LinkInfo
252+
endDev: AgingEndDevice
253+
timeoutCounter: basic.uint32_t
254+
keepaliveRcv: named.Bool
255+
256+
257+
class AssociatedDeviceZStack1(BaseAssociatedDevice):
258+
pass
259+
260+
261+
class AssociatedDeviceZStack3(BaseAssociatedDevice):
262+
ctrl: basic.uint8_t # This member was added

0 commit comments

Comments
 (0)