Skip to content

Commit

Permalink
update broadcast based on zigbee-herdsman
Browse files Browse the repository at this point in the history
  • Loading branch information
sanyatuning committed Jul 19, 2020
1 parent b5e671c commit feac7bd
Show file tree
Hide file tree
Showing 7 changed files with 88 additions and 42 deletions.
25 changes: 23 additions & 2 deletions tests/test_application.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from unittest import mock

import pytest
from zigpy.types import EUI64, Group
from zigpy.types import EUI64, Group, BroadcastAddress
import zigpy.zdo.types as zdo_t
from zigpy.zcl.clusters.general import Groups

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

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


@pytest.mark.asyncio
async def test_broadcast(app: application.ControllerApplication):
fut = asyncio.Future()
fut.set_result(None)
app._api.request_raw = mock.MagicMock(return_value=fut)

# broadcast (0, 54, 0, 0, 0, 0, 45, b'-<\x00', <BroadcastAddress.ALL_ROUTERS_AND_COORDINATOR: 65532>)
res = await app.broadcast(
0, 54, 0, 0, 0, 0, 45, b"-<\x00", BroadcastAddress.ALL_ROUTERS_AND_COORDINATOR
)

assert 0 == len(app._api._waiters)
assert (
"SREQ ZDO mgmtPermitJoinReq tsn: 45 {'addrmode': <AddressMode.ADDR_BROADCAST: 15>, "
"'dstaddr': 0xfffc, 'duration': 60, 'tcsignificance': 0}"
== str(app._api.request_raw.call_args[0][0])
)
assert (0, "broadcast send success") == res


"""
zigpy_cc.api DEBUG <-- SREQ ZDO nodeDescReq {'dstaddr': 53322, 'nwkaddrofinterest': 0}
zigpy_cc.api DEBUG --> SRSP ZDO nodeDescReq {'status': 0}
Expand Down
6 changes: 3 additions & 3 deletions tests/test_buffalo.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,13 @@ def test_write_ieee_group():

def test_read_ieee():
data_in = Buffalo(ieeeAddr1["hex"])
actual = data_in.read_parameter(t.ParameterType.IEEEADDR, {})
actual = data_in.read_parameter("test", t.ParameterType.IEEEADDR, {})
assert ieeeAddr1["string"] == actual


def test_read_ieee2():
data_in = Buffalo(ieeeAddr2["hex"])
actual = data_in.read_parameter(t.ParameterType.IEEEADDR, {})
actual = data_in.read_parameter("test", t.ParameterType.IEEEADDR, {})
assert ieeeAddr2["string"] == actual


Expand Down Expand Up @@ -68,5 +68,5 @@ def test_list_nighbor_lqi():
data_in = Buffalo(data_out.buffer)
options = BuffaloOptions()
options.length = len(value)
act = data_in.read_parameter(t.ParameterType.LIST_NEIGHBOR_LQI, options)
act = data_in.read_parameter("test", t.ParameterType.LIST_NEIGHBOR_LQI, options)
assert value == act
9 changes: 5 additions & 4 deletions tests/test_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -368,7 +368,8 @@ def test_bind_req():
b"\x01<x'\xfe\xffW\x0b\x00\x01\x08\x00\x03\x0c%\xed\x18\x00K\x12\x00\x01", True, False)
zigpy_cc.api DEBUG waiting for 1 bindReq
zigpy_cc.api DEBUG --> SREQ ZDO bindReq tsn: 1 {
'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}
'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}
zigpy_cc.uart DEBUG Send:
b"\xfe\x17%!\x8b\xbd<x'\xfe\xffW\x0b\x00\x01\x08\x00\x03\x0c%\xed\x18\x00K\x12\x00\x01\x95"
Expand All @@ -384,7 +385,7 @@ def test_bind_req():
"'srcaddr': 00:0b:57:ff:fe:27:78:3c, "
"'srcendpoint': 1, "
"'clusterid': 8, "
"'dstaddrmode': 3, "
"'dstaddrmode': <AddressMode.ADDR_64BIT: 3>, "
"'dstaddress': 00:12:4b:00:18:ed:25:0c, "
"'dstendpoint': 1}" == str(obj)
)
Expand Down Expand Up @@ -431,7 +432,7 @@ def test_bind_req_serialize():
"srcaddr": EUI64(reversed(b"\x00\x0b\x57\xff\xfe\x27\x78\x3c")),
"srcendpoint": 1,
"clusterid": 8,
"dstaddrmode": 3,
"dstaddrmode": t.AddressMode.ADDR_64BIT,
"dstaddress": EUI64(reversed(b"\x00\x12\x4b\x00\x18\xed\x25\x0c")),
"dstendpoint": 1,
}
Expand All @@ -442,7 +443,7 @@ def test_bind_req_serialize():
"'srcaddr': 00:0b:57:ff:fe:27:78:3c, "
"'srcendpoint': 1, "
"'clusterid': 8, "
"'dstaddrmode': 3, "
"'dstaddrmode': <AddressMode.ADDR_64BIT: 3>, "
"'dstaddress': 00:12:4b:00:18:ed:25:0c, "
"'dstendpoint': 1}" == str(obj)
)
Expand Down
14 changes: 10 additions & 4 deletions zigpy_cc/buffalo.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,13 @@

import zigpy.types
from zigpy_cc.exception import TODO
from zigpy_cc.types import ParameterType
from zigpy_cc.types import AddressMode, ParameterType


class BuffaloOptions:
def __init__(self) -> None:
self.startIndex = None
self.length = None
self.is_address = False


class Buffalo:
Expand Down Expand Up @@ -68,12 +67,19 @@ def write_neighbor_lqi(self, value):
self.write(value["depth"])
self.write(value["lqi"])

def read_parameter(self, type, options):
def read_parameter(self, name, type, options):

if type == ParameterType.UINT8:
res = self.read_int()
if name.endswith("addrmode"):
res = AddressMode(res)
elif type == ParameterType.UINT16:
res = self.read_int(2)
if options.is_address:
if (
name.endswith("addr")
or name.endswith("address")
or name.endswith("addrofinterest")
):
res = zigpy.types.NWK(res)
elif type == ParameterType.UINT32:
res = self.read_int(4)
Expand Down
8 changes: 8 additions & 0 deletions zigpy_cc/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,14 @@ class Timeouts:
default = 10000


class AddressMode(t.uint8_t, enum.Enum):
ADDR_NOT_PRESENT = 0
ADDR_GROUP = 1
ADDR_16BIT = 2
ADDR_64BIT = 3
ADDR_BROADCAST = 15


class LedMode(t.uint8_t, enum.Enum):
Off = 0
On = 1
Expand Down
20 changes: 17 additions & 3 deletions zigpy_cc/zigbee/application.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
from zigpy_cc.api import API
from zigpy_cc.config import CONF_DEVICE, CONFIG_SCHEMA, SCHEMA_DEVICE
from zigpy_cc.exception import TODO, CommandError
from zigpy_cc.types import NetworkOptions, Subsystem, ZnpVersion, LedMode
from zigpy_cc.types import NetworkOptions, Subsystem, ZnpVersion, LedMode, AddressMode
from zigpy_cc.zigbee.start_znp import start_znp
from zigpy_cc.zpi_object import ZpiObject

Expand Down Expand Up @@ -178,7 +178,14 @@ async def mrequest(
)
try:
obj = ZpiObject.from_cluster(
0, profile, cluster, src_ep, 0xFF, sequence, data, group=group_id
group_id,
profile,
cluster,
src_ep or 1,
0xFF,
sequence,
data,
addr_mode=AddressMode.ADDR_GROUP,
)
waiter_id = None
waiter = self._api.create_response_waiter(obj, sequence)
Expand Down Expand Up @@ -281,10 +288,17 @@ async def broadcast(
sequence,
data,
radius=radius,
addr_mode=AddressMode.ADDR_16BIT,
)

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

except CommandError as ex:
return (
Expand All @@ -297,7 +311,7 @@ async def broadcast(
async def permit_ncp(self, time_s=60):
assert 0 <= time_s <= 254
payload = {
"addrmode": 0x0F,
"addrmode": AddressMode.ADDR_BROADCAST,
"dstaddr": BroadcastAddress.ALL_ROUTERS_AND_COORDINATOR,
"duration": time_s,
"tcsignificance": 0,
Expand Down
48 changes: 22 additions & 26 deletions zigpy_cc/zpi_object.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from zigpy_cc import uart
from zigpy_cc.buffalo import Buffalo, BuffaloOptions
from zigpy_cc.definition import Definition
from zigpy_cc.types import CommandType, ParameterType, Subsystem
from zigpy_cc.types import CommandType, ParameterType, Subsystem, AddressMode

BufferAndListTypes = [
ParameterType.BUFFER,
Expand All @@ -30,15 +30,15 @@ def __init__(
command_type,
subsystem,
command: str,
commandId,
command_id,
payload,
parameters,
sequence=None,
):
self.command_type = CommandType(command_type)
self.subsystem = Subsystem(subsystem)
self.command = command
self.command_id = commandId
self.command_id = command_id
self.payload = payload
self.parameters = parameters
self.sequence = sequence
Expand Down Expand Up @@ -101,11 +101,11 @@ def from_cluster(
data,
*,
radius=30,
group=None
addr_mode=None
):
if profile == zha.PROFILE_ID:
subsystem = Subsystem.AF
if group is None:
if addr_mode is None:
cmd = next(c for c in Definition[subsystem] if c["ID"] == 1)
else:
cmd = next(c for c in Definition[subsystem] if c["ID"] == 2)
Expand All @@ -131,12 +131,11 @@ def from_cluster(
}
elif name == "dataRequestExt":
payload = {
# 1: group
"dstaddrmode": 1,
"dstaddr": group,
"destendpoint": 0xFF,
"dstaddrmode": addr_mode,
"dstaddr": nwk,
"destendpoint": dst_ep,
"dstpanid": 0,
"srcendpoint": src_ep or 1,
"srcendpoint": src_ep,
"clusterid": cluster,
"transid": sequence,
"options": 0,
Expand All @@ -146,7 +145,9 @@ def from_cluster(
}
elif name == "mgmtPermitJoinReq":
addrmode = (
0x0F if nwk == BroadcastAddress.ALL_ROUTERS_AND_COORDINATOR else 0x02
AddressMode.ADDR_BROADCAST
if nwk == BroadcastAddress.ALL_ROUTERS_AND_COORDINATOR
else AddressMode.ADDR_16BIT
)
payload = cls.read_parameters(
bytes([addrmode]) + nwk.to_bytes(2, "little") + data[1:], parameters
Expand All @@ -168,29 +169,24 @@ def read_parameters(cls, data: bytes, parameters):
buffalo = Buffalo(data)
res = {}
length = None
startIndex = None
start_index = None
for p in parameters:
options = BuffaloOptions()
name = p["name"]
if (
name.endswith("addr")
or name.endswith("address")
or name.endswith("addrofinterest")
):
options.is_address = True
type = p["parameterType"]
if type in BufferAndListTypes:
param_type = p["parameterType"]
if param_type in BufferAndListTypes:
if isinstance(length, int):
options.length = length

if type == ParameterType.LIST_ASSOC_DEV:
if isinstance(startIndex, int):
options.startIndex = startIndex
if param_type == ParameterType.LIST_ASSOC_DEV:
if isinstance(start_index, int):
options.startIndex = start_index

res[name] = buffalo.read_parameter(name, param_type, options)

res[name] = buffalo.read_parameter(type, options)
# For LIST_ASSOC_DEV, we need to grab the startindex which is
# For LIST_ASSOC_DEV, we need to grab the start_index which is
# right before the length
startIndex = length
start_index = length
# When reading a buffer, assume that the previous parsed parameter
# contains the length of the buffer
length = res[name]
Expand Down

0 comments on commit feac7bd

Please sign in to comment.