Skip to content

Commit feac7bd

Browse files
committed
update broadcast based on zigbee-herdsman
1 parent b5e671c commit feac7bd

File tree

7 files changed

+88
-42
lines changed

7 files changed

+88
-42
lines changed

tests/test_application.py

+23-2
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from unittest import mock
44

55
import pytest
6-
from zigpy.types import EUI64, Group
6+
from zigpy.types import EUI64, Group, BroadcastAddress
77
import zigpy.zdo.types as zdo_t
88
from zigpy.zcl.clusters.general import Groups
99

@@ -186,13 +186,34 @@ async def test_mrequest(app: application.ControllerApplication):
186186

187187
assert 1 == len(app._api._waiters)
188188
assert (
189-
"SREQ AF dataRequestExt tsn: 39 {'dstaddrmode': 1, 'dstaddr': 0x0002, 'destendpoint': 255, 'dstpanid': 0, "
189+
"SREQ AF dataRequestExt tsn: 39 {'dstaddrmode': <AddressMode.ADDR_GROUP: 1>, "
190+
"'dstaddr': 0x0002, 'destendpoint': 255, 'dstpanid': 0, "
190191
"'srcendpoint': 1, 'clusterid': 4, 'transid': 39, 'options': 0, 'radius': 30, 'len': 3, "
191192
"'data': b\"\\x01'\\x00\"}" == str(app._api.request_raw.call_args[0][0])
192193
)
193194
assert (0, "message send success") == res
194195

195196

197+
@pytest.mark.asyncio
198+
async def test_broadcast(app: application.ControllerApplication):
199+
fut = asyncio.Future()
200+
fut.set_result(None)
201+
app._api.request_raw = mock.MagicMock(return_value=fut)
202+
203+
# broadcast (0, 54, 0, 0, 0, 0, 45, b'-<\x00', <BroadcastAddress.ALL_ROUTERS_AND_COORDINATOR: 65532>)
204+
res = await app.broadcast(
205+
0, 54, 0, 0, 0, 0, 45, b"-<\x00", BroadcastAddress.ALL_ROUTERS_AND_COORDINATOR
206+
)
207+
208+
assert 0 == len(app._api._waiters)
209+
assert (
210+
"SREQ ZDO mgmtPermitJoinReq tsn: 45 {'addrmode': <AddressMode.ADDR_BROADCAST: 15>, "
211+
"'dstaddr': 0xfffc, 'duration': 60, 'tcsignificance': 0}"
212+
== str(app._api.request_raw.call_args[0][0])
213+
)
214+
assert (0, "broadcast send success") == res
215+
216+
196217
"""
197218
zigpy_cc.api DEBUG <-- SREQ ZDO nodeDescReq {'dstaddr': 53322, 'nwkaddrofinterest': 0}
198219
zigpy_cc.api DEBUG --> SRSP ZDO nodeDescReq {'status': 0}

tests/test_buffalo.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -34,13 +34,13 @@ def test_write_ieee_group():
3434

3535
def test_read_ieee():
3636
data_in = Buffalo(ieeeAddr1["hex"])
37-
actual = data_in.read_parameter(t.ParameterType.IEEEADDR, {})
37+
actual = data_in.read_parameter("test", t.ParameterType.IEEEADDR, {})
3838
assert ieeeAddr1["string"] == actual
3939

4040

4141
def test_read_ieee2():
4242
data_in = Buffalo(ieeeAddr2["hex"])
43-
actual = data_in.read_parameter(t.ParameterType.IEEEADDR, {})
43+
actual = data_in.read_parameter("test", t.ParameterType.IEEEADDR, {})
4444
assert ieeeAddr2["string"] == actual
4545

4646

@@ -68,5 +68,5 @@ def test_list_nighbor_lqi():
6868
data_in = Buffalo(data_out.buffer)
6969
options = BuffaloOptions()
7070
options.length = len(value)
71-
act = data_in.read_parameter(t.ParameterType.LIST_NEIGHBOR_LQI, options)
71+
act = data_in.read_parameter("test", t.ParameterType.LIST_NEIGHBOR_LQI, options)
7272
assert value == act

tests/test_types.py

+5-4
Original file line numberDiff line numberDiff line change
@@ -368,7 +368,8 @@ def test_bind_req():
368368
b"\x01<x'\xfe\xffW\x0b\x00\x01\x08\x00\x03\x0c%\xed\x18\x00K\x12\x00\x01", True, False)
369369
zigpy_cc.api DEBUG waiting for 1 bindReq
370370
zigpy_cc.api DEBUG --> SREQ ZDO bindReq tsn: 1 {
371-
'dstaddr': 0xbd8b, 'srcaddr': 00:0b:57:ff:fe:27:78:3c, 'srcendpoint': 1, 'clusterid': 8, 'dstaddrmode': 3, 'dstaddress': 00:12:4b:00:18:ed:25:0c, 'dstendpoint': 1}
371+
'dstaddr': 0xbd8b, 'srcaddr': 00:0b:57:ff:fe:27:78:3c, 'srcendpoint': 1, 'clusterid': 8,
372+
'dstaddrmode': 3, 'dstaddress': 00:12:4b:00:18:ed:25:0c, 'dstendpoint': 1}
372373
zigpy_cc.uart DEBUG Send:
373374
b"\xfe\x17%!\x8b\xbd<x'\xfe\xffW\x0b\x00\x01\x08\x00\x03\x0c%\xed\x18\x00K\x12\x00\x01\x95"
374375
@@ -384,7 +385,7 @@ def test_bind_req():
384385
"'srcaddr': 00:0b:57:ff:fe:27:78:3c, "
385386
"'srcendpoint': 1, "
386387
"'clusterid': 8, "
387-
"'dstaddrmode': 3, "
388+
"'dstaddrmode': <AddressMode.ADDR_64BIT: 3>, "
388389
"'dstaddress': 00:12:4b:00:18:ed:25:0c, "
389390
"'dstendpoint': 1}" == str(obj)
390391
)
@@ -431,7 +432,7 @@ def test_bind_req_serialize():
431432
"srcaddr": EUI64(reversed(b"\x00\x0b\x57\xff\xfe\x27\x78\x3c")),
432433
"srcendpoint": 1,
433434
"clusterid": 8,
434-
"dstaddrmode": 3,
435+
"dstaddrmode": t.AddressMode.ADDR_64BIT,
435436
"dstaddress": EUI64(reversed(b"\x00\x12\x4b\x00\x18\xed\x25\x0c")),
436437
"dstendpoint": 1,
437438
}
@@ -442,7 +443,7 @@ def test_bind_req_serialize():
442443
"'srcaddr': 00:0b:57:ff:fe:27:78:3c, "
443444
"'srcendpoint': 1, "
444445
"'clusterid': 8, "
445-
"'dstaddrmode': 3, "
446+
"'dstaddrmode': <AddressMode.ADDR_64BIT: 3>, "
446447
"'dstaddress': 00:12:4b:00:18:ed:25:0c, "
447448
"'dstendpoint': 1}" == str(obj)
448449
)

zigpy_cc/buffalo.py

+10-4
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,13 @@
22

33
import zigpy.types
44
from zigpy_cc.exception import TODO
5-
from zigpy_cc.types import ParameterType
5+
from zigpy_cc.types import AddressMode, ParameterType
66

77

88
class BuffaloOptions:
99
def __init__(self) -> None:
1010
self.startIndex = None
1111
self.length = None
12-
self.is_address = False
1312

1413

1514
class Buffalo:
@@ -68,12 +67,19 @@ def write_neighbor_lqi(self, value):
6867
self.write(value["depth"])
6968
self.write(value["lqi"])
7069

71-
def read_parameter(self, type, options):
70+
def read_parameter(self, name, type, options):
71+
7272
if type == ParameterType.UINT8:
7373
res = self.read_int()
74+
if name.endswith("addrmode"):
75+
res = AddressMode(res)
7476
elif type == ParameterType.UINT16:
7577
res = self.read_int(2)
76-
if options.is_address:
78+
if (
79+
name.endswith("addr")
80+
or name.endswith("address")
81+
or name.endswith("addrofinterest")
82+
):
7783
res = zigpy.types.NWK(res)
7884
elif type == ParameterType.UINT32:
7985
res = self.read_int(4)

zigpy_cc/types.py

+8
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,14 @@ class Timeouts:
1818
default = 10000
1919

2020

21+
class AddressMode(t.uint8_t, enum.Enum):
22+
ADDR_NOT_PRESENT = 0
23+
ADDR_GROUP = 1
24+
ADDR_16BIT = 2
25+
ADDR_64BIT = 3
26+
ADDR_BROADCAST = 15
27+
28+
2129
class LedMode(t.uint8_t, enum.Enum):
2230
Off = 0
2331
On = 1

zigpy_cc/zigbee/application.py

+17-3
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
from zigpy_cc.api import API
1717
from zigpy_cc.config import CONF_DEVICE, CONFIG_SCHEMA, SCHEMA_DEVICE
1818
from zigpy_cc.exception import TODO, CommandError
19-
from zigpy_cc.types import NetworkOptions, Subsystem, ZnpVersion, LedMode
19+
from zigpy_cc.types import NetworkOptions, Subsystem, ZnpVersion, LedMode, AddressMode
2020
from zigpy_cc.zigbee.start_znp import start_znp
2121
from zigpy_cc.zpi_object import ZpiObject
2222

@@ -178,7 +178,14 @@ async def mrequest(
178178
)
179179
try:
180180
obj = ZpiObject.from_cluster(
181-
0, profile, cluster, src_ep, 0xFF, sequence, data, group=group_id
181+
group_id,
182+
profile,
183+
cluster,
184+
src_ep or 1,
185+
0xFF,
186+
sequence,
187+
data,
188+
addr_mode=AddressMode.ADDR_GROUP,
182189
)
183190
waiter_id = None
184191
waiter = self._api.create_response_waiter(obj, sequence)
@@ -281,10 +288,17 @@ async def broadcast(
281288
sequence,
282289
data,
283290
radius=radius,
291+
addr_mode=AddressMode.ADDR_16BIT,
284292
)
285293

286294
async with self._semaphore:
287295
await self._api.request_raw(obj)
296+
"""
297+
As a broadcast command is not confirmed and thus immediately returns
298+
(contrary to network address requests) we will give the
299+
command some time to 'settle' in the network.
300+
"""
301+
await asyncio.sleep(0.2)
288302

289303
except CommandError as ex:
290304
return (
@@ -297,7 +311,7 @@ async def broadcast(
297311
async def permit_ncp(self, time_s=60):
298312
assert 0 <= time_s <= 254
299313
payload = {
300-
"addrmode": 0x0F,
314+
"addrmode": AddressMode.ADDR_BROADCAST,
301315
"dstaddr": BroadcastAddress.ALL_ROUTERS_AND_COORDINATOR,
302316
"duration": time_s,
303317
"tcsignificance": 0,

zigpy_cc/zpi_object.py

+22-26
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
from zigpy_cc import uart
55
from zigpy_cc.buffalo import Buffalo, BuffaloOptions
66
from zigpy_cc.definition import Definition
7-
from zigpy_cc.types import CommandType, ParameterType, Subsystem
7+
from zigpy_cc.types import CommandType, ParameterType, Subsystem, AddressMode
88

99
BufferAndListTypes = [
1010
ParameterType.BUFFER,
@@ -30,15 +30,15 @@ def __init__(
3030
command_type,
3131
subsystem,
3232
command: str,
33-
commandId,
33+
command_id,
3434
payload,
3535
parameters,
3636
sequence=None,
3737
):
3838
self.command_type = CommandType(command_type)
3939
self.subsystem = Subsystem(subsystem)
4040
self.command = command
41-
self.command_id = commandId
41+
self.command_id = command_id
4242
self.payload = payload
4343
self.parameters = parameters
4444
self.sequence = sequence
@@ -101,11 +101,11 @@ def from_cluster(
101101
data,
102102
*,
103103
radius=30,
104-
group=None
104+
addr_mode=None
105105
):
106106
if profile == zha.PROFILE_ID:
107107
subsystem = Subsystem.AF
108-
if group is None:
108+
if addr_mode is None:
109109
cmd = next(c for c in Definition[subsystem] if c["ID"] == 1)
110110
else:
111111
cmd = next(c for c in Definition[subsystem] if c["ID"] == 2)
@@ -131,12 +131,11 @@ def from_cluster(
131131
}
132132
elif name == "dataRequestExt":
133133
payload = {
134-
# 1: group
135-
"dstaddrmode": 1,
136-
"dstaddr": group,
137-
"destendpoint": 0xFF,
134+
"dstaddrmode": addr_mode,
135+
"dstaddr": nwk,
136+
"destendpoint": dst_ep,
138137
"dstpanid": 0,
139-
"srcendpoint": src_ep or 1,
138+
"srcendpoint": src_ep,
140139
"clusterid": cluster,
141140
"transid": sequence,
142141
"options": 0,
@@ -146,7 +145,9 @@ def from_cluster(
146145
}
147146
elif name == "mgmtPermitJoinReq":
148147
addrmode = (
149-
0x0F if nwk == BroadcastAddress.ALL_ROUTERS_AND_COORDINATOR else 0x02
148+
AddressMode.ADDR_BROADCAST
149+
if nwk == BroadcastAddress.ALL_ROUTERS_AND_COORDINATOR
150+
else AddressMode.ADDR_16BIT
150151
)
151152
payload = cls.read_parameters(
152153
bytes([addrmode]) + nwk.to_bytes(2, "little") + data[1:], parameters
@@ -168,29 +169,24 @@ def read_parameters(cls, data: bytes, parameters):
168169
buffalo = Buffalo(data)
169170
res = {}
170171
length = None
171-
startIndex = None
172+
start_index = None
172173
for p in parameters:
173174
options = BuffaloOptions()
174175
name = p["name"]
175-
if (
176-
name.endswith("addr")
177-
or name.endswith("address")
178-
or name.endswith("addrofinterest")
179-
):
180-
options.is_address = True
181-
type = p["parameterType"]
182-
if type in BufferAndListTypes:
176+
param_type = p["parameterType"]
177+
if param_type in BufferAndListTypes:
183178
if isinstance(length, int):
184179
options.length = length
185180

186-
if type == ParameterType.LIST_ASSOC_DEV:
187-
if isinstance(startIndex, int):
188-
options.startIndex = startIndex
181+
if param_type == ParameterType.LIST_ASSOC_DEV:
182+
if isinstance(start_index, int):
183+
options.startIndex = start_index
184+
185+
res[name] = buffalo.read_parameter(name, param_type, options)
189186

190-
res[name] = buffalo.read_parameter(type, options)
191-
# For LIST_ASSOC_DEV, we need to grab the startindex which is
187+
# For LIST_ASSOC_DEV, we need to grab the start_index which is
192188
# right before the length
193-
startIndex = length
189+
start_index = length
194190
# When reading a buffer, assume that the previous parsed parameter
195191
# contains the length of the buffer
196192
length = res[name]

0 commit comments

Comments
 (0)