From 1a847a390dc8e71eaf12c68718a792dbd3369902 Mon Sep 17 00:00:00 2001 From: puddly <32534428+puddly@users.noreply.github.com> Date: Thu, 4 Nov 2021 17:20:15 -0400 Subject: [PATCH 1/3] Load key data at startup --- .gitignore | 2 ++ setup.py | 2 +- zigpy_deconz/api.py | 29 ++++++++++++++++++++++++----- zigpy_deconz/zigbee/application.py | 26 ++++++++++++++++++++++++++ 4 files changed, 53 insertions(+), 6 deletions(-) diff --git a/.gitignore b/.gitignore index ddd8f0e..edb452b 100644 --- a/.gitignore +++ b/.gitignore @@ -71,3 +71,5 @@ ENV/ # Visual Studio Code .vscode + +.DS_Store diff --git a/setup.py b/setup.py index 650908c..f35e125 100644 --- a/setup.py +++ b/setup.py @@ -21,6 +21,6 @@ author_email="schmidt.d@aon.at", license="GPL-3.0", packages=find_packages(exclude=["tests"]), - install_requires=["pyserial-asyncio", "zigpy>=0.37.0"], + install_requires=["pyserial-asyncio", "zigpy>=0.40.0"], tests_require=["pytest", "pytest-asyncio", "asynctest"], ) diff --git a/zigpy_deconz/api.py b/zigpy_deconz/api.py index 71c846a..32096ba 100644 --- a/zigpy_deconz/api.py +++ b/zigpy_deconz/api.py @@ -10,7 +10,7 @@ import serial from zigpy.config import CONF_DEVICE_PATH import zigpy.exceptions -from zigpy.types import APSStatus, Channels +from zigpy.types import APSStatus, Bool, Channels, KeyData from zigpy_deconz.exception import APIException, CommandError import zigpy_deconz.types as t @@ -179,15 +179,24 @@ class NetworkParameter(t.uint8_t, enum.Enum): aps_extended_panid = 0x0B trust_center_address = 0x0E security_mode = 0x10 + use_predefined_nwk_panid = 0x15 network_key = 0x18 + link_key = 0x19 current_channel = 0x1C permit_join = 0x21 protocol_version = 0x22 nwk_update_id = 0x24 watchdog_ttl = 0x26 + nwk_frame_counter = 0x27 + app_zdp_response_handling = 0x28 -NETWORK_PARAMETER_SCHEMA = { +# Some parameters use a different schema for requests than they do for responses +NETWORK_PARAMETER_SCHEMA_REQ = { + NetworkParameter.link_key: (t.EUI64,), +} + +NETWORK_PARAMETER_SCHEMA_RSP = { NetworkParameter.mac_address: (t.EUI64,), NetworkParameter.nwk_panid: (t.PanId,), NetworkParameter.nwk_address: (t.NWK,), @@ -197,12 +206,16 @@ class NetworkParameter(t.uint8_t, enum.Enum): NetworkParameter.aps_extended_panid: (t.ExtendedPanId,), NetworkParameter.trust_center_address: (t.EUI64,), NetworkParameter.security_mode: (t.uint8_t,), + NetworkParameter.use_predefined_nwk_panid: (Bool,), NetworkParameter.network_key: (t.uint8_t, t.Key), + NetworkParameter.link_key: (t.EUI64, KeyData), NetworkParameter.current_channel: (t.uint8_t,), NetworkParameter.permit_join: (t.uint8_t,), NetworkParameter.protocol_version: (t.uint16_t,), NetworkParameter.nwk_update_id: (t.uint8_t,), NetworkParameter.watchdog_ttl: (t.uint32_t,), + NetworkParameter.nwk_frame_counter: (t.uint32_t,), + NetworkParameter.app_zdp_response_handling: (t.uint16_t,), } @@ -416,9 +429,15 @@ async def read_parameter(self, id_, *args): except (KeyError, ValueError): raise KeyError("Unknown parameter id: %s" % (id_,)) - data = t.serialize(args, NETWORK_PARAMETER_SCHEMA[param]) + if param in NETWORK_PARAMETER_SCHEMA_REQ: + req_schema = NETWORK_PARAMETER_SCHEMA_REQ[param] + rsp_schema = NETWORK_PARAMETER_SCHEMA_RSP[param] + else: + req_schema = rsp_schema = NETWORK_PARAMETER_SCHEMA_RSP[param] + + data = t.serialize(args, req_schema) r = await self._command(Command.read_parameter, 1 + len(data), param, data) - data = t.deserialize(r[2], NETWORK_PARAMETER_SCHEMA[param])[0] + data = t.deserialize(r[2], rsp_schema)[0] LOGGER.debug("Read parameter %s response: %s", param.name, data) return data @@ -439,7 +458,7 @@ def write_parameter(self, id_, *args): except (KeyError, ValueError): raise KeyError("Unknown parameter id: %s write request" % (id_,)) - v = t.serialize(args, NETWORK_PARAMETER_SCHEMA[param]) + v = t.serialize(args, NETWORK_PARAMETER_SCHEMA_RSP[param]) length = len(v) + 1 return self._command(Command.write_parameter, length, param, v) diff --git a/zigpy_deconz/zigbee/application.py b/zigpy_deconz/zigbee/application.py index 2ab0e49..1b8691c 100644 --- a/zigpy_deconz/zigbee/application.py +++ b/zigpy_deconz/zigbee/application.py @@ -93,11 +93,37 @@ async def startup(self, auto_form=False): NetworkParameter.channel_mask ] await self._api[NetworkParameter.aps_extended_panid] + + if self.state.network_information.network_key is None: + self.state.network_information.network_key = zigpy.state.Key() + + ( + _, + self.state.network_information.network_key.key, + ) = await self._api.read_parameter(NetworkParameter.network_key, 0) + self.state.network_information.network_key.seq = 0 + self.state.network_information.network_key.rx_counter = None + self.state.network_information.network_key.partner_ieee = None + + try: + (self.state.network_information.network_key.tx_counter,) = await self._api[ + NetworkParameter.nwk_frame_counter + ] + except zigpy_deconz.exception.CommandError as ex: + assert ex.status == Status.UNSUPPORTED + self.state.network_information.network_key.tx_counter = None + if self.state.network_information.tc_link_key is None: self.state.network_information.tc_link_key = zigpy.state.Key() + (self.state.network_information.tc_link_key.partner_ieee,) = await self._api[ NetworkParameter.trust_center_address ] + _, self.state.network_information.tc_link_key = await self._api.read_parameter( + NetworkParameter.link_key, + self.state.network_information.tc_link_key.partner_ieee, + ) + (self.state.network_information.security_level,) = await self._api[ NetworkParameter.security_mode ] From 159da76a4c613130f42aca2720c929d3859f389b Mon Sep 17 00:00:00 2001 From: puddly <32534428+puddly@users.noreply.github.com> Date: Thu, 4 Nov 2021 17:44:53 -0400 Subject: [PATCH 2/3] Add unit tests --- tests/test_application.py | 32 ++++++++++++++++++++++++++---- zigpy_deconz/api.py | 4 ++-- zigpy_deconz/zigbee/application.py | 5 ++++- 3 files changed, 34 insertions(+), 7 deletions(-) diff --git a/tests/test_application.py b/tests/test_application.py index db3bd3c..1b36dd6 100644 --- a/tests/test_application.py +++ b/tests/test_application.py @@ -7,7 +7,7 @@ import zigpy.config import zigpy.device import zigpy.neighbor -from zigpy.types import EUI64 +from zigpy.types import EUI64, Channels import zigpy.zdo.types as zdo_t from zigpy_deconz import types as t @@ -253,10 +253,34 @@ async def _version(): app._api._proto_ver = protocol_ver return [version] + params = { + deconz_api.NetworkParameter.aps_designed_coordinator: [designed_coord], + deconz_api.NetworkParameter.nwk_address: [designed_coord], + deconz_api.NetworkParameter.protocol_version: [protocol_ver], + deconz_api.NetworkParameter.mac_address: [EUI64([0x01] * 8)], + deconz_api.NetworkParameter.nwk_address: [0x0000], + deconz_api.NetworkParameter.nwk_panid: [0x1234], + deconz_api.NetworkParameter.nwk_extended_panid: [EUI64([0x02] * 8)], + deconz_api.NetworkParameter.channel_mask: [Channels.CHANNEL_25], + deconz_api.NetworkParameter.aps_extended_panid: [EUI64([0x02] * 8)], + deconz_api.NetworkParameter.network_key: [0, t.Key([0x03] * 16)], + deconz_api.NetworkParameter.trust_center_address: [EUI64([0x04] * 8)], + deconz_api.NetworkParameter.link_key: [ + EUI64([0x04] * 8), + t.Key(b"ZigBeeAlliance09"), + ], + deconz_api.NetworkParameter.security_mode: [3], + deconz_api.NetworkParameter.current_channel: [25], + deconz_api.NetworkParameter.nwk_update_id: [0], + } + async def _read_param(param, *args): - if param == deconz_api.NetworkParameter.mac_address: - return (t.EUI64([0x01] * 8),) - return (designed_coord,) + try: + return params[param] + except KeyError: + raise zigpy_deconz.exception.CommandError( + deconz_api.Status.UNSUPPORTED, "Unsupported" + ) app._reset_watchdog = AsyncMock() app.form_network = AsyncMock() diff --git a/zigpy_deconz/api.py b/zigpy_deconz/api.py index 32096ba..1ead8db 100644 --- a/zigpy_deconz/api.py +++ b/zigpy_deconz/api.py @@ -10,7 +10,7 @@ import serial from zigpy.config import CONF_DEVICE_PATH import zigpy.exceptions -from zigpy.types import APSStatus, Bool, Channels, KeyData +from zigpy.types import APSStatus, Bool, Channels from zigpy_deconz.exception import APIException, CommandError import zigpy_deconz.types as t @@ -208,7 +208,7 @@ class NetworkParameter(t.uint8_t, enum.Enum): NetworkParameter.security_mode: (t.uint8_t,), NetworkParameter.use_predefined_nwk_panid: (Bool,), NetworkParameter.network_key: (t.uint8_t, t.Key), - NetworkParameter.link_key: (t.EUI64, KeyData), + NetworkParameter.link_key: (t.EUI64, t.Key), NetworkParameter.current_channel: (t.uint8_t,), NetworkParameter.permit_join: (t.uint8_t,), NetworkParameter.protocol_version: (t.uint16_t,), diff --git a/zigpy_deconz/zigbee/application.py b/zigpy_deconz/zigbee/application.py index 1b8691c..e87383e 100644 --- a/zigpy_deconz/zigbee/application.py +++ b/zigpy_deconz/zigbee/application.py @@ -119,7 +119,10 @@ async def startup(self, auto_form=False): (self.state.network_information.tc_link_key.partner_ieee,) = await self._api[ NetworkParameter.trust_center_address ] - _, self.state.network_information.tc_link_key = await self._api.read_parameter( + ( + _, + self.state.network_information.tc_link_key.key, + ) = await self._api.read_parameter( NetworkParameter.link_key, self.state.network_information.tc_link_key.partner_ieee, ) From ab4304f7bdf3611bda685a8117876aae17ffbadb Mon Sep 17 00:00:00 2001 From: puddly <32534428+puddly@users.noreply.github.com> Date: Thu, 4 Nov 2021 18:02:25 -0400 Subject: [PATCH 3/3] Revert unnecessary `_REQ`/`_RSP` schema additions --- zigpy_deconz/api.py | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/zigpy_deconz/api.py b/zigpy_deconz/api.py index 1ead8db..756f4bf 100644 --- a/zigpy_deconz/api.py +++ b/zigpy_deconz/api.py @@ -191,12 +191,7 @@ class NetworkParameter(t.uint8_t, enum.Enum): app_zdp_response_handling = 0x28 -# Some parameters use a different schema for requests than they do for responses -NETWORK_PARAMETER_SCHEMA_REQ = { - NetworkParameter.link_key: (t.EUI64,), -} - -NETWORK_PARAMETER_SCHEMA_RSP = { +NETWORK_PARAMETER_SCHEMA = { NetworkParameter.mac_address: (t.EUI64,), NetworkParameter.nwk_panid: (t.PanId,), NetworkParameter.nwk_address: (t.NWK,), @@ -429,15 +424,9 @@ async def read_parameter(self, id_, *args): except (KeyError, ValueError): raise KeyError("Unknown parameter id: %s" % (id_,)) - if param in NETWORK_PARAMETER_SCHEMA_REQ: - req_schema = NETWORK_PARAMETER_SCHEMA_REQ[param] - rsp_schema = NETWORK_PARAMETER_SCHEMA_RSP[param] - else: - req_schema = rsp_schema = NETWORK_PARAMETER_SCHEMA_RSP[param] - - data = t.serialize(args, req_schema) + data = t.serialize(args, NETWORK_PARAMETER_SCHEMA[param]) r = await self._command(Command.read_parameter, 1 + len(data), param, data) - data = t.deserialize(r[2], rsp_schema)[0] + data = t.deserialize(r[2], NETWORK_PARAMETER_SCHEMA[param])[0] LOGGER.debug("Read parameter %s response: %s", param.name, data) return data @@ -458,7 +447,7 @@ def write_parameter(self, id_, *args): except (KeyError, ValueError): raise KeyError("Unknown parameter id: %s write request" % (id_,)) - v = t.serialize(args, NETWORK_PARAMETER_SCHEMA_RSP[param]) + v = t.serialize(args, NETWORK_PARAMETER_SCHEMA[param]) length = len(v) + 1 return self._command(Command.write_parameter, length, param, v)