From 7b32290b60172d19bea5f74387f86e68090d9eeb Mon Sep 17 00:00:00 2001 From: Alexei Chetroi Date: Tue, 10 May 2022 16:53:44 -0400 Subject: [PATCH 1/4] 0.31.0.dev0 version bump --- bellows/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bellows/__init__.py b/bellows/__init__.py index 84a46c57..9e048e2e 100644 --- a/bellows/__init__.py +++ b/bellows/__init__.py @@ -1,5 +1,5 @@ MAJOR_VERSION = 0 -MINOR_VERSION = 30 +MINOR_VERSION = 31 PATCH_VERSION = "0.dev0" __short_version__ = f"{MAJOR_VERSION}.{MINOR_VERSION}" __version__ = f"{__short_version__}.{PATCH_VERSION}" From f7b074e30ff660b0d775b70bf2b6f069db0df8af Mon Sep 17 00:00:00 2001 From: tube0013 Date: Thu, 9 Jun 2022 17:55:29 -0400 Subject: [PATCH 2/4] Add EmberZnet 7.1.0 - Protocol v9 (#462) * Start Work on v9 * cleanups * Update __init__.py * Add sl_Status * Remove chunk defs * Update test_ezsp_v9.py * update tests and spelling * For fixes * remove overrideCurrentChannel command no longer in sdk Source and was causing a duplicate frame ID --- bellows/ezsp/__init__.py | 5 +- bellows/ezsp/v9/__init__.py | 58 +++ bellows/ezsp/v9/commands.py | 690 ++++++++++++++++++++++++++++ bellows/ezsp/v9/config.py | 267 +++++++++++ bellows/ezsp/v9/types/__init__.py | 5 + bellows/ezsp/v9/types/named.py | 717 ++++++++++++++++++++++++++++++ bellows/ezsp/v9/types/struct.py | 204 +++++++++ bellows/types/named.py | 575 ++++++++++++++++++++++++ bellows/types/struct.py | 22 + tests/test_ezsp_v9.py | 320 +++++++++++++ 10 files changed, 2861 insertions(+), 2 deletions(-) create mode 100644 bellows/ezsp/v9/__init__.py create mode 100644 bellows/ezsp/v9/commands.py create mode 100644 bellows/ezsp/v9/config.py create mode 100644 bellows/ezsp/v9/types/__init__.py create mode 100644 bellows/ezsp/v9/types/named.py create mode 100644 bellows/ezsp/v9/types/struct.py create mode 100644 tests/test_ezsp_v9.py diff --git a/bellows/ezsp/__init__.py b/bellows/ezsp/__init__.py index c07cf397..d2132488 100644 --- a/bellows/ezsp/__init__.py +++ b/bellows/ezsp/__init__.py @@ -21,9 +21,9 @@ import bellows.types as t import bellows.uart -from . import v4, v5, v6, v7, v8 +from . import v4, v5, v6, v7, v8, v9 -EZSP_LATEST = v8.EZSP_VERSION +EZSP_LATEST = v9.EZSP_VERSION PROBE_TIMEOUT = 3 NETWORK_OPS_TIMEOUT = 10 LOGGER = logging.getLogger(__name__) @@ -40,6 +40,7 @@ class EZSP: v6.EZSP_VERSION: v6.EZSPv6, v7.EZSP_VERSION: v7.EZSPv7, v8.EZSP_VERSION: v8.EZSPv8, + v9.EZSP_VERSION: v9.EZSPv9, } def __init__(self, device_config: Dict): diff --git a/bellows/ezsp/v9/__init__.py b/bellows/ezsp/v9/__init__.py new file mode 100644 index 00000000..fc9b5b97 --- /dev/null +++ b/bellows/ezsp/v9/__init__.py @@ -0,0 +1,58 @@ +""""EZSP Protocol version 8 protocol handler.""" +import asyncio +import logging +from typing import Tuple + +import voluptuous + +import bellows.config + +from . import commands, config, types as v9_types +from .. import protocol + +EZSP_VERSION = 9 +LOGGER = logging.getLogger(__name__) + + +class EZSPv9(protocol.ProtocolHandler): + """EZSP Version 8 Protocol version handler.""" + + COMMANDS = commands.COMMANDS + SCHEMAS = { + bellows.config.CONF_EZSP_CONFIG: voluptuous.Schema(config.EZSP_SCHEMA), + bellows.config.CONF_EZSP_POLICIES: voluptuous.Schema(config.EZSP_POLICIES_SCH), + } + types = v9_types + + def _ezsp_frame_tx(self, name: str) -> bytes: + """Serialize the frame id.""" + cmd_id = self.COMMANDS[name][0] + hdr = [self._seq, 0x00, 0x01] + return bytes(hdr) + self.types.uint16_t(cmd_id).serialize() + + def _ezsp_frame_rx(self, data: bytes) -> Tuple[int, int, bytes]: + """Handler for received data frame.""" + seq, data = data[0], data[3:] + frame_id, data = self.types.uint16_t.deserialize(data) + + return seq, frame_id, data + + async def pre_permit(self, time_s: int) -> None: + """Temporarily change TC policy while allowing new joins.""" + wild_card_ieee = v9_types.EmberEUI64([0xFF] * 8) + tc_link_key = v9_types.EmberKeyData(b"ZigBeeAlliance09") + await self.addTransientLinkKey(wild_card_ieee, tc_link_key) + await self.setPolicy( + v9_types.EzspPolicyId.TRUST_CENTER_POLICY, + v9_types.EzspDecisionBitmask.ALLOW_JOINS + | v9_types.EzspDecisionBitmask.ALLOW_UNSECURED_REJOINS, + ) + await asyncio.sleep(time_s + 2) + await self.setPolicy( + v9_types.EzspPolicyId.TRUST_CENTER_POLICY, + self.tc_policy, + ) + + async def set_source_routing(self) -> None: + """Enable source routing on NCP.""" + await self.setSourceRouteDiscoveryMode(1) diff --git a/bellows/ezsp/v9/commands.py b/bellows/ezsp/v9/commands.py new file mode 100644 index 00000000..5dbaed7a --- /dev/null +++ b/bellows/ezsp/v9/commands.py @@ -0,0 +1,690 @@ +from . import types as t + +COMMANDS = { + # 4. Configuration frames + "version": (0x0000, (t.uint8_t,), (t.uint8_t, t.uint8_t, t.uint16_t)), + "getConfigurationValue": (0x0052, (t.EzspConfigId,), (t.EzspStatus, t.uint16_t)), + "setConfigurationValue": (0x0053, (t.EzspConfigId, t.uint16_t), (t.EzspStatus,)), + "addEndpoint": ( + 0x0002, + ( + t.uint8_t, + t.uint16_t, + t.uint16_t, + t.uint8_t, + t.uint8_t, + t.uint8_t, + t.List(t.uint16_t), + t.List(t.uint16_t), + ), + (t.EzspStatus,), + ), + "setPolicy": (0x0055, (t.EzspPolicyId, t.uint16_t), (t.EzspStatus,)), + "getPolicy": (0x0056, (t.EzspPolicyId,), (t.EzspStatus, t.EzspDecisionId)), + "sendPanIdUpdate": (0x0057, (t.EmberPanId,), (t.Bool,)), + "getValue": (0x00AA, (t.EzspValueId,), (t.EzspStatus, t.LVBytes)), + "getExtendedValue": ( + 0x0003, + (t.EzspExtendedValueId, t.uint32_t), + (t.EzspStatus, t.LVBytes), + ), + "setValue": (0x00AB, (t.EzspValueId, t.LVBytes), (t.EzspStatus,)), + # 5. Utilities Frames + "nop": (0x0005, (), ()), + "echo": (0x0081, (t.LVBytes,), (t.LVBytes,)), + "invalidCommand": (0x0058, (), (t.EzspStatus,)), + "callback": (0x0006, (), ()), + "noCallbacks": (0x0007, (), ()), + "setToken": (0x0009, (t.uint8_t, t.fixed_list(8, t.uint8_t)), (t.EmberStatus,)), + "getToken": (0x000A, (t.uint8_t,), (t.EmberStatus, t.fixed_list(8, t.uint8_t))), + "getMfgToken": (0x000B, (t.EzspMfgTokenId,), (t.LVBytes,)), + "setMfgToken": (0x000C, (t.EzspMfgTokenId, t.LVBytes), (t.EmberStatus,)), + "stackTokenChangedHandler": (0x000D, (), (t.uint16_t,)), + "getRandomNumber": (0x0049, (), (t.EmberStatus, t.uint16_t)), + "setTimer": ( + 0x000E, + (t.uint8_t, t.uint16_t, t.EmberEventUnits, t.Bool), + (t.EmberStatus,), + ), + "getTimer": (0x004E, (t.uint8_t,), (t.uint16_t, t.EmberEventUnits, t.Bool)), + "timerHandler": (0x000F, (), (t.uint8_t,)), + "debugWrite": (0x0012, (t.Bool, t.LVBytes), (t.EmberStatus,)), + "readAndClearCounters": ( + 0x0065, + (), + (t.fixed_list(len(t.EmberCounterType), t.uint16_t),), + ), + "readCounters": ( + 0x00F1, + (), + (t.fixed_list(len(t.EmberCounterType), t.uint16_t),), + ), + "counterRolloverHandler": (0x00F2, (), (t.EmberCounterType,)), + "delayTest": (0x009D, (t.uint16_t,), ()), + "getLibraryStatus": (0x0001, (t.EmberLibraryId,), (t.EmberLibraryStatus,)), + "getXncpInfo": (0x0013, (), (t.EmberStatus, t.uint16_t, t.uint16_t)), + "customFrame": (0x0047, (t.LVBytes,), (t.EmberStatus, t.LVBytes)), + "customFrameHandler": (0x0054, (), (t.LVBytes,)), + "getEui64": (0x0026, (), (t.EmberEUI64,)), + "getNodeId": (0x0027, (), (t.EmberNodeId,)), + "getPhyInterfaceCount": (0x00FC, (), (t.uint8_t,)), + "getTrueRandomEntropySource": (0x004F, (), (t.EmberEntropySource,)), + "networkInit": (0x0017, (t.EmberNetworkInitStruct,), (t.EmberStatus,)), + # 6. Networking Frames + "setManufacturerCode": (0x0015, (t.uint16_t,), ()), + "setPowerDescriptor": (0x0016, (t.uint16_t,), ()), + "networkState": (0x0018, (), (t.EmberNetworkStatus,)), + "stackStatusHandler": (0x0019, (), (t.EmberStatus,)), + "startScan": ( + 0x001A, + (t.EzspNetworkScanType, t.uint32_t, t.uint8_t), + (t.sl_Status,), + ), + "energyScanResultHandler": (0x0048, (), (t.uint8_t, t.int8s)), + "networkFoundHandler": (0x001B, (), (t.EmberZigbeeNetwork, t.uint8_t, t.int8s)), + "scanCompleteHandler": (0x001C, (), (t.uint8_t, t.EmberStatus)), + "unusedPanIdFoundHandler": (0x00D2, (), (t.uint8_t, t.EmberStatus)), + "findUnusedPanId": (0x00D3, (t.Channels, t.uint8_t), (t.EmberStatus,)), + "stopScan": (0x001D, (), (t.EmberStatus,)), + "formNetwork": (0x001E, (t.EmberNetworkParameters,), (t.EmberStatus,)), + "joinNetwork": ( + 0x001F, + (t.EmberNodeType, t.EmberNetworkParameters), + (t.EmberStatus,), + ), + "joinNetworkDirectly": ( + 0x003B, + (t.EmberNodeType, t.EmberBeaconData, t.int8s, t.Bool), + (t.EmberStatus,), + ), + "leaveNetwork": (0x0020, (), (t.EmberStatus,)), + "findAndRejoinNetwork": (0x0021, (t.Bool, t.uint32_t), (t.EmberStatus,)), + "permitJoining": (0x0022, (t.uint8_t,), (t.EmberStatus,)), + "childJoinHandler": ( + 0x0023, + (), + (t.uint8_t, t.Bool, t.EmberNodeId, t.EmberEUI64, t.EmberNodeType), + ), + "energyScanRequest": ( + 0x009C, + (t.EmberNodeId, t.Channels, t.uint8_t, t.uint16_t), + (t.EmberStatus,), + ), + "getNetworkParameters": ( + 0x0028, + (), + (t.EmberStatus, t.EmberNodeType, t.EmberNetworkParameters), + ), + "getRadioParameters": ( + 0x00FD, + (t.uint8_t,), + (t.EmberStatus, t.EmberNodeType, t.EmberNetworkParameters), + ), + "getParentChildParameters": (0x0029, (), (t.uint8_t, t.EmberEUI64, t.EmberNodeId)), + "getChildData": ( + 0x004A, + (t.uint8_t,), + (t.EmberStatus, t.EmberNodeId, t.EmberEUI64, t.EmberNodeType), + ), + "setChildData": ( + 0x00AC, + (t.uint8_t,), + (t.EmberStatus, t.EmberNodeId, t.EmberEUI64, t.EmberNodeType), + ), + "getSourceRouteTableTotalSize": (0x00C3, (), (t.uint8_t,)), + "getSourceRouteTableFilledSize": (0x00C2, (), (t.uint8_t,)), + "getSourceRouteTableEntry": ( + 0x00C1, + (t.uint8_t,), + (t.EmberStatus, t.EmberNodeId, t.uint8_t), + ), + "getNeighbor": (0x0079, (t.uint8_t,), (t.EmberStatus, t.EmberNeighborTableEntry)), + "getNeighborFrameCounter": (0x003E, (t.EmberEUI64,), (t.EmberStatus, t.uint32_t)), + "setNeighborFrameCounter": (0x00AD, (t.EmberEUI64,), (t.EmberStatus, t.uint32_t)), + "setRoutingShortcutThreshold": (0x00D0, (t.uint8_t,), (t.EmberStatus,)), + "getRoutingShortcutThreshold": (0x00D1, (), (t.uint8_t,)), + "neighborCount": (0x007A, (), (t.uint8_t,)), + "getRouteTableEntry": ( + 0x007B, + (t.uint8_t,), + (t.EmberStatus, t.EmberRouteTableEntry), + ), + "setRadioPower": (0x0099, (t.int8s,), (t.EmberStatus,)), + "setRadioChannel": (0x009A, (t.uint8_t,), (t.EmberStatus,)), + "setRadioIeee802154CcaMode": (0x0095, (t.uint8_t,), (t.EmberStatus,)), + "setConcentrator": ( + 0x0010, + (t.Bool, t.uint16_t, t.uint16_t, t.uint16_t, t.uint8_t, t.uint8_t, t.uint8_t), + (t.EmberStatus,), + ), + "setBrokenRouteErrorCode": (0x0011, (t.uint8_t,), (t.EmberStatus,)), + "multiPhyStart": ( + 0x00F8, + (t.uint8_t, t.uint8_t, t.uint8_t, t.int8s, t.EmberMultiPhyNwkConfig), + (t.EmberStatus,), + ), + "multiPhyStop": (0x00F9, (t.uint8_t,), (t.EmberStatus,)), + "multiPhySetRadioPower": (0x00FA, (t.uint8_t, t.int8s), (t.EmberStatus,)), + "sendLinkPowerDeltaRequest": (0x00F7, (), (t.EmberStatus,)), + "multiPhySetRadioChannel": ( + 0x00FB, + (t.uint8_t, t.uint8_t, t.uint8_t), + (t.EmberStatus,), + ), + "getDutyCycleState": (0x0035, (), (t.EmberStatus, t.EmberDutyCycleState)), + "setDutyCycleLimitsInStack": ( + 0x0040, + (t.EmberDutyCycleLimits,), + (t.EmberStatus, t.EmberDutyCycleState), + ), + "getDutyCycleLimits": (0x004B, (), (t.EmberStatus, t.EmberDutyCycleLimits)), + "getCurrentDutyCycle": ( + 0x004C, + (t.uint8_t,), + (t.EmberStatus, t.fixed_list(134, t.uint8_t)), + ), + "dutyCycleHandler": ( + 0x004D, + (), + ( + t.uint8_t, + t.uint8_t, + t.EmberDutyCycleState, + t.uint8_t, + t.EmberPerDeviceDutyCycle, + ), + ), + "getFirstBeacon": (0x003D, (), (t.EmberStatus, t.EmberBeaconIterator)), + "getNextBeacon": (0x0004, (), (t.EmberStatus, t.EmberBeaconData)), + "getNumStoredBeacons": (0x0008, (), (t.uint8_t,)), + "clearStoredBeacons": (0x003C, (), ()), + "setLogicalAndRadioChannel": (0x00B9, (t.uint8_t,), (t.EmberStatus,)), + "getLogicalChannel": (0x00BA, (), (t.uint8_t,)), + # 7. Binding Frames + "clearBindingTable": (0x002A, (), (t.EmberStatus,)), + "setBinding": (0x002B, (t.uint8_t, t.EmberBindingTableEntry), (t.EmberStatus,)), + "getBinding": (0x002C, (t.uint8_t,), (t.EmberStatus, t.EmberBindingTableEntry)), + "deleteBinding": (0x002D, (t.uint8_t,), (t.EmberStatus,)), + "bindingIsActive": (0x002E, (t.uint8_t,), (t.Bool,)), + "getBindingRemoteNodeId": (0x002F, (t.uint8_t,), (t.EmberNodeId,)), + "setBindingRemoteNodeId": (0x0030, (t.uint8_t, t.EmberNodeId), ()), + "remoteSetBindingHandler": ( + 0x0031, + (), + (t.EmberBindingTableEntry, t.uint8_t, t.EmberStatus), + ), + "remoteDeleteBindingHandler": (0x0032, (), (t.uint8_t, t.EmberStatus)), + # 8. Messaging Frames + "maximumPayloadLength": (0x0033, (), (t.uint8_t,)), + "sendUnicast": ( + 0x0034, + ( + t.EmberOutgoingMessageType, + t.EmberNodeId, + t.EmberApsFrame, + t.uint8_t, + t.LVBytes, + ), + (t.EmberStatus, t.uint8_t), + ), + "sendBroadcast": ( + 0x0036, + (t.EmberNodeId, t.EmberApsFrame, t.uint8_t, t.uint8_t, t.LVBytes), + (t.EmberStatus, t.uint8_t), + ), + "proxyBroadcast": ( + 0x0037, + ( + t.EmberNodeId, + t.EmberNodeId, + t.uint8_t, + t.EmberApsFrame, + t.uint8_t, + t.uint8_t, + t.LVBytes, + ), + (t.EmberStatus, t.uint8_t), + ), + "sendMulticast": ( + 0x0038, + (t.EmberApsFrame, t.uint8_t, t.uint8_t, t.uint8_t, t.LVBytes), + (t.EmberStatus, t.uint8_t), + ), + "sendMulticastWithAlias": ( + 0x3A, + ( + t.EmberApsFrame, + t.uint8_t, + t.uint8_t, + t.uint16_t, + t.uint8_t, + t.uint8_t, + t.LVBytes, + ), + (t.EmberStatus, t.uint8_t), + ), + "sendReply": ( + 0x0039, + (t.EmberNodeId, t.EmberApsFrame, t.LVBytes), + (t.EmberStatus,), + ), + "messageSentHandler": ( + 0x003F, + (), + ( + t.EmberOutgoingMessageType, + t.uint16_t, + t.EmberApsFrame, + t.uint8_t, + t.EmberStatus, + t.LVBytes, + ), + ), + "sendManyToOneRouteRequest": (0x0041, (t.uint16_t, t.uint8_t), (t.EmberStatus,)), + "pollForData": ( + 0x0042, + (t.uint16_t, t.EmberEventUnits, t.uint8_t), + (t.EmberStatus,), + ), + "pollCompleteHandler": (0x0043, (), (t.EmberStatus,)), + "pollHandler": (0x0044, (), (t.EmberNodeId,)), + "incomingSenderEui64Handler": (0x0062, (), (t.EmberEUI64,)), + "incomingMessageHandler": ( + 0x0045, + (), + ( + t.EmberIncomingMessageType, + t.EmberApsFrame, + t.uint8_t, + t.int8s, + t.EmberNodeId, + t.uint8_t, + t.uint8_t, + t.LVBytes, + ), + ), + "incomingRouteRecordHandler": ( + 0x0059, + (), + (t.EmberNodeId, t.EmberEUI64, t.uint8_t, t.int8s, t.LVList(t.EmberNodeId)), + ), + "changeSourceRouteHandler": (0x00C4, (), (t.EmberNodeId, t.EmberNodeId, t.Bool)), + "setSourceRouteDiscoveryMode": (0x005A, (t.uint8_t,), (t.uint32_t,)), + "incomingManyToOneRouteRequestHandler": ( + 0x007D, + (), + (t.EmberNodeId, t.EmberEUI64, t.uint8_t), + ), + "incomingRouteErrorHandler": (0x0080, (), (t.EmberStatus, t.EmberNodeId)), + "unicastCurrentNetworkKey": ( + 0x0050, + (t.EmberNodeId, t.EmberEUI64, t.EmberNodeId), + (t.EmberStatus,), + ), + "addressTableEntryIsActive": (0x005B, (t.uint8_t,), (t.Bool,)), + "setAddressTableRemoteEui64": (0x005C, (t.uint8_t, t.EmberEUI64), (t.EmberStatus,)), + "setAddressTableRemoteNodeId": (0x005D, (t.uint8_t, t.EmberNodeId), ()), + "getAddressTableRemoteEui64": (0x005E, (t.uint8_t,), (t.EmberEUI64,)), + "getAddressTableRemoteNodeId": (0x005F, (t.uint8_t,), (t.EmberNodeId,)), + "setExtendedTimeout": (0x007E, (t.EmberEUI64, t.Bool), ()), + "getExtendedTimeout": (0x007F, (t.EmberEUI64,), (t.Bool,)), + "replaceAddressTableEntry": ( + 0x0082, + (t.uint8_t, t.EmberEUI64, t.EmberNodeId, t.Bool), + (t.EmberStatus, t.EmberEUI64, t.EmberNodeId, t.Bool), + ), + "lookupNodeIdByEui64": (0x0060, (t.EmberEUI64,), (t.EmberNodeId,)), + "lookupEui64ByNodeId": (0x0061, (t.EmberNodeId,), (t.EmberStatus, t.EmberEUI64)), + "getMulticastTableEntry": ( + 0x0063, + (t.uint8_t,), + (t.EmberStatus, t.EmberMulticastTableEntry), + ), + "setMulticastTableEntry": ( + 0x0064, + (t.uint8_t, t.EmberMulticastTableEntry), + (t.EmberStatus,), + ), + "idConflictHandler": (0x007C, (), (t.EmberNodeId,)), + "writeNodeData": (0x00FE, (t.Bool,), (t.EmberStatus,)), + "sendRawMessage": (0x0096, (t.LVBytes,), (t.EmberStatus,)), + "sendRawMessageExtended": ( + 0x0051, + (t.LVBytes, t.uint8_t, t.Bool), + (t.EmberStatus,), + ), + "macPassthroughMessageHandler": ( + 0x0097, + (), + (t.EmberMacPassthroughType, t.uint8_t, t.int8s, t.LVBytes), + ), + "macFilterMatchMessageHandler": ( + 0x0046, + (), + (t.uint8_t, t.EmberMacPassthroughType, t.uint8_t, t.int8s, t.LVBytes), + ), + "rawTransmitCompleteHandler": (0x0098, (), (t.EmberStatus,)), + "setMacPollFailureWaitTime": (0x00F4, (t.uint8_t,), ()), + "setBeaconClassificationParams": ( + 0x00EF, + (), + (t.EmberStatus, t.EmberBeaconClassificationParams), + ), + "getBeaconClassificationParams": ( + 0x00F3, + (), + (t.EmberStatus, t.EmberBeaconClassificationParams), + ), + # 9. Security Frames + "setInitialSecurityState": ( + 0x0068, + (t.EmberInitialSecurityState,), + (t.EmberStatus,), + ), + "getCurrentSecurityState": ( + 0x0069, + (), + (t.EmberStatus, t.EmberCurrentSecurityState), + ), + "getKey": (0x006A, (t.EmberKeyType,), (t.EmberStatus, t.EmberKeyStruct)), + "switchNetworkKeyHandler": (0x006E, (), (t.uint8_t,)), + "getKeyTableEntry": (0x0071, (t.uint8_t,), (t.EmberStatus, t.EmberKeyStruct)), + "setKeyTableEntry": ( + 0x0072, + (t.uint8_t, t.EmberEUI64, t.Bool, t.EmberKeyData), + (t.EmberStatus,), + ), + "findKeyTableEntry": (0x0075, (t.EmberEUI64, t.Bool), (t.uint8_t,)), + "addOrUpdateKeyTableEntry": ( + 0x0066, + (t.EmberEUI64, t.Bool, t.EmberKeyData), + (t.EmberStatus,), + ), + "sendTrustCenterLinkKey": (0x0067, (t.EmberNodeId, t.EmberEUI64), (t.EmberStatus,)), + "eraseKeyTableEntry": (0x0076, (t.uint8_t,), (t.EmberStatus,)), + "clearKeyTable": (0x00B1, (), (t.EmberStatus,)), + "requestLinkKey": (0x0014, (t.EmberEUI64,), (t.EmberStatus,)), + "updateTcLinkKey": (0x006C, (t.uint8_t,), (t.EmberStatus,)), + "zigbeeKeyEstablishmentHandler": (0x009B, (), (t.EmberEUI64, t.EmberKeyStatus)), + "addTransientLinkKey": (0x00AF, (t.EmberEUI64, t.EmberKeyData), (t.EmberStatus,)), + "clearTransientLinkKeys": (0x006B, (), ()), + "getTransientLinkKey": ( + 0x00CE, + (t.EmberEUI64,), + (t.EmberStatus, t.EmberTransientKeyData), + ), + "getTransientKeyTableEntry": ( + 0x006D, + (t.uint8_t,), + (t.EmberStatus, t.EmberTransientKeyData), + ), + # 10. Trust Center Frames + "trustCenterJoinHandler": ( + 0x0024, + (), + ( + t.EmberNodeId, + t.EmberEUI64, + t.EmberDeviceUpdate, + t.EmberJoinDecision, + t.EmberNodeId, + ), + ), + "broadcastNextNetworkKey": (0x0073, (t.EmberKeyData,), (t.EmberStatus,)), + "broadcastNetworkKeySwitch": (0x0074, (), (t.EmberStatus,)), + "becomeTrustCenter": (0x0077, (t.EmberKeyData,), (t.EmberStatus,)), + "aesMmoHash": ( + 0x006F, + (t.EmberAesMmoHashContext, t.Bool, t.LVBytes), + (t.EmberStatus, t.EmberAesMmoHashContext), + ), + "removeDevice": ( + 0x00A8, + (t.EmberNodeId, t.EmberEUI64, t.EmberEUI64), + (t.EmberStatus,), + ), + "unicastNwkKeyUpdate": ( + 0x00A9, + (t.EmberNodeId, t.EmberEUI64, t.EmberKeyData), + (t.EmberStatus,), + ), + # 11. Certificate Based Key Exchange (CBKE) Frames + "generateCbkeKeys": (0x00A4, (), (t.EmberStatus,)), + "generateCbkeKeysHandler": (0x009E, (), (t.EmberStatus, t.EmberPublicKeyData)), + "calculateSmacs": ( + 0x009F, + (t.Bool, t.EmberCertificateData, t.EmberPublicKeyData), + (t.EmberStatus,), + ), + "calculateSmacsHandler": ( + 0x00A0, + (), + (t.EmberStatus, t.EmberSmacData, t.EmberSmacData), + ), + "generateCbkeKeys283k1": (0x00E8, (), (t.EmberStatus,)), + "generateCbkeKeysHandler283k1": ( + 0x00E9, + (), + (t.EmberStatus, t.EmberPublicKey283k1Data), + ), + "calculateSmacs283k1": ( + 0x00EA, + (t.Bool, t.EmberCertificate283k1Data, t.EmberPublicKey283k1Data), + (t.EmberStatus,), + ), + "calculateSmacsHandler283k1": ( + 0x00EB, + (), + (t.EmberStatus, t.EmberSmacData, t.EmberSmacData), + ), + "clearTemporaryDataMaybeStoreLinkKey": (0x00A1, (t.Bool,), (t.EmberStatus,)), + "clearTemporaryDataMaybeStoreLinkKey283k1": (0x00EE, (t.Bool,), (t.EmberStatus,)), + "getCertificate": (0x00A5, (), (t.EmberStatus, t.EmberCertificateData)), + "getCertificate283k1": (0x00EC, (), (t.EmberStatus, t.EmberCertificate283k1Data)), + "dsaSign": (0x00A6, (t.LVBytes,), (t.EmberStatus,)), # Deprecated + "dsaSignHandler": (0x00A7, (), (t.EmberStatus, t.LVBytes)), # Deprecated + "dsaVerify": ( + 0x00A3, + (t.EmberMessageDigest, t.EmberCertificateData, t.EmberSignatureData), + (t.EmberStatus,), + ), + "dsaVerifyHandler": (0x0078, (), (t.EmberStatus,)), + "dsaVerify283k1": ( + 0x00B0, + (t.EmberMessageDigest, t.EmberCertificate283k1Data, t.EmberSignature283k1Data), + (t.EmberStatus,), + ), + "setPreinstalledCbkeData": ( + 0x00A2, + (t.EmberPublicKeyData, t.EmberCertificateData, t.EmberPrivateKeyData), + (t.EmberStatus,), + ), + "setPreinstalledCbkeData283k1": ( + 0x00ED, + ( + t.EmberPublicKey283k1Data, + t.EmberCertificate283k1Data, + t.EmberPrivateKey283k1Data, + ), + (t.EmberStatus,), + ), + # 12. Mfglib Frames + "mfglibStart": (0x0083, (t.Bool,), (t.EmberStatus,)), + "mfglibEnd": (0x0084, (), (t.EmberStatus,)), + "mfglibStartTone": (0x0085, (), (t.EmberStatus,)), + "mfglibStopTone": (0x0086, (), (t.EmberStatus,)), + "mfglibStartStream": (0x0087, (), (t.EmberStatus,)), + "mfglibStopStream": (0x0088, (), (t.EmberStatus,)), + "mfglibSendPacket": (0x0089, (t.LVBytes,), (t.EmberStatus,)), + "mfglibSetChannel": (0x008A, (t.uint8_t,), (t.EmberStatus,)), + "mfglibGetChannel": (0x008B, (), (t.uint8_t,)), + "mfglibSetPower": (0x008C, (t.uint16_t, t.int8s), (t.EmberStatus,)), + "mfglibGetPower": (0x008D, (), (t.int8s,)), + "mfglibRxHandler": (0x008E, (), (t.uint8_t, t.int8s, t.LVBytes)), + # 13. Bootloader Frames + "launchStandaloneBootloader": (0x008F, (t.uint8_t,), (t.EmberStatus,)), + "sendBootloadMessage": ( + 0x0090, + (t.Bool, t.EmberEUI64, t.LVBytes), + (t.EmberStatus,), + ), + "getStandaloneBootloaderVersionPlatMicroPhy": ( + 0x0091, + (), + (t.uint16_t, t.uint8_t, t.uint8_t, t.uint8_t), + ), + "incomingBootloadMessageHandler": ( + 0x0092, + (), + (t.EmberEUI64, t.uint8_t, t.int8s, t.LVBytes), + ), + "bootloadTransmitCompleteHandler": (0x0093, (), (t.EmberStatus, t.LVBytes)), + "aesEncrypt": ( + 0x0094, + (t.fixed_list(16, t.uint8_t), t.fixed_list(16, t.uint8_t)), + (t.fixed_list(16, t.uint8_t),), + ), + # 14. ZLL Frames + "zllNetworkOps": ( + 0x00B2, + (t.EmberZllNetwork, t.EzspZllNetworkOperation, t.int8s), + (t.EmberStatus,), + ), + "zllSetInitialSecurityState": ( + 0x00B3, + (t.EmberKeyData, t.EmberZllInitialSecurityState), + (t.EmberStatus,), + ), + "zllSetSecurityStateWithoutKey": ( + 0x00CF, + (t.EmberZllInitialSecurityState,), + (t.EmberStatus,), + ), + "zllStartScan": (0x00B4, (t.Channels, t.int8s, t.EmberNodeType), (t.EmberStatus,)), + "zllSetRxOnWhenIdle": (0x00B5, (t.uint32_t,), (t.EmberStatus,)), + "zllNetworkFoundHandler": ( + 0x00B6, + (), + (t.EmberZllNetwork, t.Bool, t.EmberZllDeviceInfoRecord, t.uint8_t, t.int8s), + ), + "zllScanCompleteHandler": (0x00B7, (), (t.EmberStatus,)), + "zllAddressAssignmentHandler": ( + 0x00B8, + (), + (t.EmberZllAddressAssignment, t.uint8_t, t.int8s), + ), + "zllTouchLinkTargetHandler": (0x00BB, (), (t.EmberZllNetwork,)), + "zllGetTokens": ( + 0x00BC, + (), + (t.EmberTokTypeStackZllData, t.EmberTokTypeStackZllSecurity), + ), + "zllSetDataToken": (0x00BD, (t.EmberTokTypeStackZllData,), ()), + "zllSetNonZllNetwork": (0x00BF, (), ()), + "isZllNetwork": (0x00BE, (), (t.Bool,)), + "zllSetRadioIdleMode": (0x00D4, (t.EmberRadioPowerMode,), ()), + "setZllNodeType": (0x00D5, (t.EmberNodeType,), ()), + "setZllAdditionalState": (0x00D6, (t.uint16_t,), ()), + "zllOperationInProgress": (0x00D7, (), (t.Bool,)), + "zllRxOnWhenIdleGetActive": (0x00D8, (), (t.Bool,)), + "getZllPrimaryChannelMask": (0x00D9, (), (t.Channels,)), + "getZllSecondaryChannelMask": (0x00DA, (), (t.Channels,)), + "setZllPrimaryChannelMask": (0x00DB, (t.Channels,), ()), + "setZllSecondaryChannelMask": (0x00DC, (t.Channels,), ()), + "zllClearTokens": (0x0025, (), ()), + # 15 WWAH Frames + "setParentClassificationEnabled": (0x00E7, (t.Bool,), ()), + "getParentClassificationEnabled": (0x00F0, (), (t.Bool,)), + "setLongUpTime": (0x00E3, (t.Bool,), ()), + "setHubConnectivity": (0x00E4, (t.Bool,), ()), + "isUpTimeLong": (0x00E5, (), (t.Bool,)), + "isHubConnected": (0x00E6, (), (t.Bool,)), + # 16 Green Power Frames + "gpProxyTableProcessGpPairing": ( + 0x00C9, + ( + t.uint32_t, + t.EmberGpAddress, + t.uint8_t, + t.uint16_t, + t.uint16_t, + t.uint16_t, + t.EmberEUI64, + t.EmberKeyData, + ), + (), + ), + "dGpSend": ( + 0x00C6, + (t.Bool, t.Bool, t.EmberGpAddress, t.uint8_t, t.LVBytes, t.uint8_t, t.uint16_t), + (t.EmberStatus,), + ), + "dGpSentHandler": (0x00C7, (), (t.EmberStatus, t.uint8_t)), + "gpepIncomingMessageHandler": ( + 0x00C5, + (), + ( + t.EmberStatus, + t.uint8_t, + t.uint8_t, + t.EmberGpAddress, + t.EmberGpSecurityLevel, + t.EmberGpKeyType, + t.Bool, + t.Bool, + t.uint32_t, + t.uint8_t, + t.uint32_t, + t.EmberGpSinkListEntry, + t.LVBytes, + ), + ), + "gpProxyTableGetEntry": ( + 0x00C8, + (t.uint8_t,), + (t.EmberStatus, t.EmberGpProxyTableEntry), + ), + "gpProxyTableLookup": (0x00C0, (t.EmberGpAddress,), (t.uint8_t,)), + "gpSinkTableGetEntry": ( + 0x00DD, + (t.uint8_t,), + (t.EmberStatus, t.EmberGpSinkTableEntry), + ), + "gpSinkTableLookup": (0x00DE, (t.EmberGpAddress,), (t.uint8_t,)), + "gpSinkTableSetEntry": ( + 0x00DF, + (t.uint8_t, t.EmberGpSinkTableEntry), + (t.EmberStatus,), + ), + "gpSinkTableRemoveEntry": (0x00E0, (t.uint8_t,), ()), + "gpSinkTableFindOrAllocateEntry": (0x00E1, (t.EmberGpAddress,), (t.uint8_t,)), + "gpSinkTableClearAll": (0x00E2, (), ()), + "gpSinkTableInit": (0x0070, (), ()), + # 17 Secure EZSP Frames + "setSecurityKey": ( + 0x00CA, + (t.EmberKeyData, t.SecureEzspSecurityType), + (t.EzspStatus,), + ), + "setSecurityParameters": ( + 0x00CB, + (t.SecureEzspSecurityLevel, t.SecureEzspRandomNumber), + (t.EzspStatus, t.SecureEzspRandomNumber), + ), + "resetToFactoryDefaults": (0x00CC, (), (t.EzspStatus,)), + "getSecurityKeyStatus": (0x00CD, (), (t.EzspStatus, t.SecureEzspSecurityType)), + # 18 Token Interface Frames + "getTokenCount": (0x0100, (), (t.uint8_t,)), + "getTokenInfo": (0x0101, (t.uint8_t,), (t.EmberStatus)), + "getTokenData": ( + 0x0102, + (t.uint32_t, t.uint32_t), + (t.EmberStatus, t.EmberTokenData), + ), + "setTokenData": ( + 0x0103, + (t.uint32_t, t.uint32_t, t.EmberTokenData), + (t.EmberStatus), + ), + "resetNode": (0x0104, (), ()), +} diff --git a/bellows/ezsp/v9/config.py b/bellows/ezsp/v9/config.py new file mode 100644 index 00000000..8031d7ac --- /dev/null +++ b/bellows/ezsp/v9/config.py @@ -0,0 +1,267 @@ +import voluptuous as vol + +from bellows.config import cv_uint16 +import bellows.multicast + +from ..v4.config import EZSP_POLICIES_SHARED +from .types import ( + EmberZdoConfigurationFlags, + EzspConfigId, + EzspDecisionBitmask, + EzspPolicyId, +) + +EZSP_SCHEMA = { + # The number of packet buffers available to the stack. When set to the special + # value 0xFF, the NCP will allocate all remaining configuration RAM towards + # packet buffers, such that the resulting count will be the largest whole number + # of packet buffers that can fit into the available memory + vol.Optional(EzspConfigId.CONFIG_PACKET_BUFFER_COUNT.name, default=0xFF): vol.All( + int, vol.Range(min=1, max=255) + ), + # The maximum number of router neighbors the stack can keep track of. A neighbor + # is a node within radio range + vol.Optional(EzspConfigId.CONFIG_NEIGHBOR_TABLE_SIZE.name): vol.All( + int, vol.Range(min=8, max=26) + ), + # + # The maximum number of APS retried messages the stack can be transmitting at + # any time + vol.Optional(EzspConfigId.CONFIG_APS_UNICAST_MESSAGE_COUNT.name): vol.All( + int, vol.Range(min=0, max=255) + ), + # + # The maximum number of non-volatile bindings supported by the stack + vol.Optional(EzspConfigId.CONFIG_BINDING_TABLE_SIZE.name): vol.All( + int, vol.Range(min=0, max=32) + ), + # + # The maximum number of EUI64 to network address associations that the stack can + # maintain for the application. (Note: The total number of such address + # associations maintained by the NCP is the sum of the value of this setting and + # the value of EZSP_CONFIG_TRUST_CENTER_ADDRESS_CA CHE_SIZE) + vol.Optional(EzspConfigId.CONFIG_ADDRESS_TABLE_SIZE.name, default=16): vol.All( + int, vol.Range(min=0) + ), + # + # The maximum number of multicast groups that the device may be a member of + vol.Optional( + EzspConfigId.CONFIG_MULTICAST_TABLE_SIZE.name, + default=bellows.multicast.Multicast.TABLE_SIZE, + ): vol.All(int, vol.Range(min=0, max=255)), + # + # The maximum number of destinations to which a node can route messages. This + # includes both messages originating at this node and those relayed for others + vol.Optional(EzspConfigId.CONFIG_ROUTE_TABLE_SIZE.name): vol.All( + int, vol.Range(min=0, max=255) + ), + # + # The number of simultaneous route discoveries that a node will support + vol.Optional(EzspConfigId.CONFIG_DISCOVERY_TABLE_SIZE.name): vol.All( + int, vol.Range(min=0, max=255) + ), + # + # Specifies the stack profile + vol.Optional(EzspConfigId.CONFIG_STACK_PROFILE.name, default=2): vol.All( + int, vol.Range(min=0, max=255) + ), + # + # The security level used for security at the MAC and network layers. The + # supported values are 0 (no security) and 5 (payload is encrypted and a + # four-byte MIC is used for authentication) + vol.Optional(EzspConfigId.CONFIG_SECURITY_LEVEL.name, default=5): vol.All( + int, vol.Range(min=0, max=5) + ), + # + # The maximum number of hops for a message + vol.Optional(EzspConfigId.CONFIG_MAX_HOPS.name): vol.All( + int, vol.Range(min=0, max=30) + ), + # + # The maximum number of end device children that a router will support + vol.Optional(EzspConfigId.CONFIG_MAX_END_DEVICE_CHILDREN.name): vol.All( + int, vol.Range(min=0, max=64) + ), + # + # The maximum amount of time that the MAC will hold a message for indirect + # transmission to a child + vol.Optional( + EzspConfigId.CONFIG_INDIRECT_TRANSMISSION_TIMEOUT.name, default=7680 + ): vol.All(int, vol.Range(min=0, max=30000)), + # + # The maximum amount of time that an end device child can wait between polls. + # If no poll is heard within this timeout, then the parent removes the end + # device from its tables. The timeout corresponding to a value of zero is 10 + # seconds. The timeout corresponding to a nonzero value N is 2^N minutes, + # ranging from 2^1 = 2 minutes to 2^14 = 16384 minutes + vol.Optional(EzspConfigId.CONFIG_END_DEVICE_POLL_TIMEOUT.name, default=8): vol.All( + int, vol.Range(min=0, max=14) + ), + # + # Enables boost power mode and/or the alternate transmitter output + vol.Optional(EzspConfigId.CONFIG_TX_POWER_MODE.name): vol.All( + int, vol.Range(min=0, max=3) + ), + # + # 0: Allow this node to relay messages. 1: Prevent this node from relaying + # messages + vol.Optional(EzspConfigId.CONFIG_DISABLE_RELAY.name): vol.All( + int, vol.Range(min=0, max=1) + ), + # + # The maximum number of EUI64 to network address associations that the Trust + # Center can maintain. These address cache entries are reserved for and reused + # by the Trust Center when processing device join/rejoin authentications. This + # cache size limits the number of overlapping joins the Trust Center can process + # within a narrow time window (e.g. two seconds), and thus should be set to the + # maximum number of near simultaneous joins the Trust Center is expected to + # accommodate. (Note: The total number of such address associations maintained + # by the NCP is the sum of the value of this setting and the value of + # EZSP_CONFIG_ADDRESS_TABLE_SIZE) + vol.Optional( + EzspConfigId.CONFIG_TRUST_CENTER_ADDRESS_CACHE_SIZE.name, default=2 + ): vol.All(int, vol.Range(min=0)), + # + # The size of the source route table + vol.Optional(EzspConfigId.CONFIG_SOURCE_ROUTE_TABLE_SIZE.name, default=16): vol.All( + int, vol.Range(min=0) + ), + # + # The number of blocks of a fragmented message that can be sent in a single + # window + vol.Optional(EzspConfigId.CONFIG_FRAGMENT_WINDOW_SIZE.name): vol.All( + int, vol.Range(min=0, max=8) + ), + # + # The time (ms) the stack will wait between sending blocks of a fragmented + # message + vol.Optional(EzspConfigId.CONFIG_FRAGMENT_DELAY_MS.name): vol.All( + int, vol.Range(min=0) + ), + # + # The size of the Key Table used for storing individual link keys (if the device + # is a Trust Center) or Application Link Keys (if the device is a normal node) + vol.Optional(EzspConfigId.CONFIG_KEY_TABLE_SIZE.name): vol.All( + int, vol.Range(min=0) + ), + # + # The APS ACK timeout value (ms). The stack waits this amount of time between + # resends of APS retried messages + vol.Optional(EzspConfigId.CONFIG_APS_ACK_TIMEOUT.name): vol.All( + int, vol.Range(min=0) + ), + # + # The duration of an active scan 15/4 scan duration units. This also controls + # the jitter used when responding to a beacon request + vol.Optional(EzspConfigId.CONFIG_ACTIVE_SCAN_DURATION.name): vol.All( + int, vol.Range(min=0, max=6) + ), + # + # The time (seoonds) the coordinator will wait for a second end device bind + # request to arrive + vol.Optional(EzspConfigId.CONFIG_END_DEVICE_BIND_TIMEOUT.name): vol.All( + int, vol.Range(min=1) + ), + # + # The number of PAN id conflict reports that must be received by the network + # manager within one minute to trigger a PAN id change + vol.Optional( + EzspConfigId.CONFIG_PAN_ID_CONFLICT_REPORT_THRESHOLD.name, default=2 + ): vol.All(int, vol.Range(min=1, max=63)), + # + # The timeout value in minutes for how long the Trust Center or a normal node + # waits for the ZigBee Request Key to complete. On the Trust Center this + # controls whether or not the device buffers the request, waiting for a matching + # pair of ZigBee Request Key. If the value is non-zero, the Trust Center buffers + # and waits for that amount of time. If the value is zero, the Trust Center does + # not buffer the request and immediately responds to the request. Zero is the + # most compliant behavior + vol.Optional(EzspConfigId.CONFIG_REQUEST_KEY_TIMEOUT.name): vol.All( + int, vol.Range(min=0, max=10) + ), + # + # This value indicates the size of the runtime modifiable certificate table. + # Normally certificates are stored in MFG tokens but this table can be used to + # field upgrade devices with new Smart Energy certificates. This value cannot be + # set, it can only be queried + vol.Optional(EzspConfigId.CONFIG_CERTIFICATE_TABLE_SIZE.name): vol.All( + int, vol.Range(min=0, max=1) + ), + # + # This is a bitmask that controls which incoming ZDO request messages are passed + # to the application. The bits are defined in the EmberZdoConfigurationFlags + # enumeration. To see if the application is required to send a ZDO response in + # reply to an incoming message, the application must check the APS options + # bitfield within the incomingMessageHandler callback to see if the + # EMBER_APS_OPTION_ZDO_RESPONSE_REQUIRED flag is set + vol.Optional( + EzspConfigId.CONFIG_APPLICATION_ZDO_FLAGS.name, + default=EmberZdoConfigurationFlags.APP_RECEIVES_SUPPORTED_ZDO_REQUESTS + | EmberZdoConfigurationFlags.APP_HANDLES_UNSUPPORTED_ZDO_REQUESTS, + ): vol.All(int, vol.Range(min=0, max=255)), + # + # The maximum number of broadcasts during a single broadcast timeout period + vol.Optional(EzspConfigId.CONFIG_BROADCAST_TABLE_SIZE.name): vol.All( + int, vol.Range(min=15, max=254) + ), + # + # The size of the MAC filter list table + vol.Optional(EzspConfigId.CONFIG_MAC_FILTER_TABLE_SIZE.name): vol.All( + int, vol.Range(min=0, max=254) + ), + # + # The number of supported networks + vol.Optional(EzspConfigId.CONFIG_SUPPORTED_NETWORKS.name, default=1): vol.All( + int, vol.Range(min=1, max=2) + ), + # + # Whether multicasts are sent to the RxOnWhenIdle=true address (0xFFFD) or the + # sleepy broadcast address (0xFFFF). The RxOnWhenIdle=true address is the ZigBee + # compliant destination for multicasts. 0=false, 1=true + vol.Optional(EzspConfigId.CONFIG_SEND_MULTICASTS_TO_SLEEPY_ADDRESS.name): vol.All( + int, vol.Range(min=0, max=1) + ), + # + # ZLL group address initial configuration + vol.Optional(EzspConfigId.CONFIG_ZLL_GROUP_ADDRESSES.name): vol.All( + int, vol.Range(min=0, max=255) + ), + # + # ZLL RSSI threshold initial configuration + vol.Optional(EzspConfigId.CONFIG_ZLL_RSSI_THRESHOLD.name): vol.All( + int, vol.Range(min=-128, max=127) + ), + # + # Toggles the MTORR flow control in the stack. 0=false, 1=true + vol.Optional(EzspConfigId.CONFIG_MTORR_FLOW_CONTROL.name): vol.All( + int, vol.Range(min=0, max=1) + ), + # + # The amount of time a trust center will store a transient key + # with which a device can use to join the network + vol.Optional(EzspConfigId.CONFIG_TRANSIENT_KEY_TIMEOUT_S.name): vol.All( + int, vol.Range(min=0, max=65535) + ), + vol.Optional(EzspConfigId.CONFIG_RETRY_QUEUE_SIZE.name): vol.All( + int, vol.Range(min=0) + ), + vol.Optional(EzspConfigId.CONFIG_NEW_BROADCAST_ENTRY_THRESHOLD.name): vol.All( + int, vol.Range(min=0) + ), + vol.Optional(EzspConfigId.CONFIG_BROADCAST_MIN_ACKS_NEEDED.name): vol.All( + int, vol.Range(min=0) + ), + vol.Optional( + EzspConfigId.CONFIG_TC_REJOINS_USING_WELL_KNOWN_KEY_TIMEOUT_S.name, default=90 + ): vol.All(int, vol.Range(min=0)), +} + +EZSP_POLICIES_SCH = { + vol.Optional( + EzspPolicyId.TRUST_CENTER_POLICY.name, + default=EzspDecisionBitmask.ALLOW_JOINS + | EzspDecisionBitmask.IGNORE_UNSECURED_REJOINS, + ): cv_uint16, + **EZSP_POLICIES_SHARED, + **{vol.Optional(policy.name): cv_uint16 for policy in EzspPolicyId}, +} diff --git a/bellows/ezsp/v9/types/__init__.py b/bellows/ezsp/v9/types/__init__.py new file mode 100644 index 00000000..d51e9473 --- /dev/null +++ b/bellows/ezsp/v9/types/__init__.py @@ -0,0 +1,5 @@ +from bellows.types import deserialize, serialize # noqa: F401, F403 +from bellows.types.basic import * # noqa: F401,F403 + +from .named import * # noqa: F401, F403 +from .struct import * # noqa: F401, F403 diff --git a/bellows/ezsp/v9/types/named.py b/bellows/ezsp/v9/types/named.py new file mode 100644 index 00000000..2a95b49e --- /dev/null +++ b/bellows/ezsp/v9/types/named.py @@ -0,0 +1,717 @@ +"""Protocol version 9 named types.""" + +import bellows.types.basic as basic +from bellows.types.named import ( # noqa: F401, F403 + Bool, + Channels, + EmberApsOption, + EmberBindingType, + EmberCertificate283k1Data, + EmberCertificateData, + EmberConcentratorType, + EmberConfigTxPowerMode, + EmberCurrentSecurityBitmask, + EmberEUI64, + EmberEventUnits, + EmberGpKeyType, + EmberGpSecurityLevel, + EmberIncomingMessageType, + EmberInitialSecurityBitmask, + EmberJoinDecision, + EmberKeyData, + EmberKeyStatus, + EmberKeyStructBitmask, + EmberLibraryId, + EmberLibraryStatus, + EmberMacPassthroughType, + EmberMessageDigest, + EmberMulticastId, + EmberNetworkStatus, + EmberNodeId, + EmberNodeType, + EmberOutgoingMessageType, + EmberPanId, + EmberPrivateKey283k1Data, + EmberPrivateKeyData, + EmberPublicKey283k1Data, + EmberPublicKeyData, + EmberSignature283k1Data, + EmberSignatureData, + EmberSmacData, + EmberStatus, + EmberZdoConfigurationFlags, + EmberZllKeyIndex, + EmberZllState, + ExtendedPanId, + EzspEndpointFlags, + EzspExtendedValueId, + EzspMfgTokenId, + EzspNetworkScanType, + EzspSourceRouteOverheadInformation, + EzspStatus, + EzspZllNetworkOperation, + sl_Status, +) + + +class EzspConfigId(basic.enum8): + # Identifies a configuration value. + + # The number of packet buffers available to the stack. When set to the + # special value 0xFF, the NCP will allocate all remaining configuration RAM + # towards packet buffers, such that the resulting count will be the largest + # whole number of packet buffers that can fit into the available memory. + CONFIG_PACKET_BUFFER_COUNT = 0x01 + # The maximum number of router neighbors the stack can keep track of. A + # neighbor is a node within radio range. + CONFIG_NEIGHBOR_TABLE_SIZE = 0x02 + # The maximum number of APS retried messages the stack can be transmitting + # at any time. + CONFIG_APS_UNICAST_MESSAGE_COUNT = 0x03 + # The maximum number of non-volatile bindings supported by the stack. + CONFIG_BINDING_TABLE_SIZE = 0x04 + # The maximum number of EUI64 to network address associations that the + # stack can maintain for the application. (Note, the total number of such + # address associations maintained by the NCP is the sum of the value of + # this setting and the value of ::CONFIG_TRUST_CENTER_ADDRESS_CACHE_SIZE). + CONFIG_ADDRESS_TABLE_SIZE = 0x05 + # The maximum number of multicast groups that the device may be a member + # of. + CONFIG_MULTICAST_TABLE_SIZE = 0x06 + # The maximum number of destinations to which a node can route messages. + # This includes both messages originating at this node and those relayed + # for others. + CONFIG_ROUTE_TABLE_SIZE = 0x07 + # The number of simultaneous route discoveries that a node will support. + CONFIG_DISCOVERY_TABLE_SIZE = 0x08 + # Specifies the stack profile. + CONFIG_STACK_PROFILE = 0x0C + # The security level used for security at the MAC and network layers. The + # supported values are 0 (no security) and 5 (payload is encrypted and a + # four-byte MIC is used for authentication). + CONFIG_SECURITY_LEVEL = 0x0D + # The maximum number of hops for a message. + CONFIG_MAX_HOPS = 0x10 + # The maximum number of end device children that a router will support. + CONFIG_MAX_END_DEVICE_CHILDREN = 0x11 + # The maximum amount of time that the MAC will hold a message for indirect + # transmission to a child. + CONFIG_INDIRECT_TRANSMISSION_TIMEOUT = 0x12 + # The maximum amount of time that an end device child can wait between + # polls. If no poll is heard within this timeout, then the parent removes + # the end device from its tables. + CONFIG_END_DEVICE_POLL_TIMEOUT = 0x13 + # Enables boost power mode and/or the alternate transmitter output. + CONFIG_TX_POWER_MODE = 0x17 + # 0: Allow this node to relay messages. 1: Prevent this node from relaying + # messages. + CONFIG_DISABLE_RELAY = 0x18 + # The maximum number of EUI64 to network address associations that the + # Trust Center can maintain. These address cache entries are reserved for + # and reused by the Trust Center when processing device join/rejoin + # authentications. This cache size limits the number of overlapping joins + # the Trust Center can process within a narrow time window (e.g. two + # seconds), and thus should be set to the maximum number of near + # simultaneous joins the Trust Center is expected to accommodate. (Note, + # the total number of such address associations maintained by the NCP is + # the sum of the value of this setting and the value of + # ::CONFIG_ADDRESS_TABLE_SIZE.) + CONFIG_TRUST_CENTER_ADDRESS_CACHE_SIZE = 0x19 + # The size of the source route table. + CONFIG_SOURCE_ROUTE_TABLE_SIZE = 0x1A + # The number of blocks of a fragmented message that can be sent in a single + # window. + CONFIG_FRAGMENT_WINDOW_SIZE = 0x1C + # The time the stack will wait (in milliseconds) between sending blocks of + # a fragmented message. + CONFIG_FRAGMENT_DELAY_MS = 0x1D + # The size of the Key Table used for storing individual link keys (if the + # device is a Trust Center) or Application Link Keys (if the device is a + # normal node). + CONFIG_KEY_TABLE_SIZE = 0x1E + # The APS ACK timeout value. The stack waits this amount of time between + # resends of APS retried messages. + CONFIG_APS_ACK_TIMEOUT = 0x1F + # The duration of an active scan, in the units used by the 15.4 scan + # parameter (((1 << duration) + 1) * 15ms). This also controls the jitter + # used when responding to a beacon request. + CONFIG_ACTIVE_SCAN_DURATION = 0x20 + # The time the coordinator will wait (in seconds) for a second end device + # bind request to arrive. + CONFIG_END_DEVICE_BIND_TIMEOUT = 0x21 + # The number of PAN id conflict reports that must be received by the + # network manager within one minute to trigger a PAN id change. + CONFIG_PAN_ID_CONFLICT_REPORT_THRESHOLD = 0x22 + # The timeout value in minutes for how long the Trust Center or a normal + # node waits for the ZigBee Request Key to complete. On the Trust Center + # this controls whether or not the device buffers the request, waiting for + # a matching pair of ZigBee Request Key. If the value is non-zero, the + # Trust Center buffers and waits for that amount of time. If the value is + # zero, the Trust Center does not buffer the request and immediately + # responds to the request. Zero is the most compliant behavior. + CONFIG_REQUEST_KEY_TIMEOUT = 0x24 + # This value indicates the size of the runtime modifiable certificate + # table. Normally certificates are stored in MFG tokens but this table can + # be used to field upgrade devices with new Smart Energy certificates. + # This value cannot be set, it can only be queried. + CONFIG_CERTIFICATE_TABLE_SIZE = 0x29 + # This is a bitmask that controls which incoming ZDO request messages are + # passed to the application. The bits are defined in the + # EmberZdoConfigurationFlags enumeration. To see if the application is + # required to send a ZDO response in reply to an incoming message, the + # application must check the APS options bitfield within the + # incomingMessageHandler callback to see if the + # APS_OPTION_ZDO_RESPONSE_REQUIRED flag is set. + CONFIG_APPLICATION_ZDO_FLAGS = 0x2A + # The maximum number of broadcasts during a single broadcast timeout + # period. + CONFIG_BROADCAST_TABLE_SIZE = 0x2B + # The size of the MAC filter list table. + CONFIG_MAC_FILTER_TABLE_SIZE = 0x2C + # The number of supported networks. + CONFIG_SUPPORTED_NETWORKS = 0x2D + # Whether multicasts are sent to the RxOnWhenIdle=true address (0xFFFD) or + # the sleepy broadcast address (0xFFFF). The RxOnWhenIdle=true address is + # the ZigBee compliant destination for multicasts. + CONFIG_SEND_MULTICASTS_TO_SLEEPY_ADDRESS = 0x2E + # ZLL group address initial configuration. + CONFIG_ZLL_GROUP_ADDRESSES = 0x2F + # ZLL rssi threshold initial configuration. + CONFIG_ZLL_RSSI_THRESHOLD = 0x30 + # Toggles the mtorr flow control in the stack. + # The maximum number of pairings supported by the stack. Controllers + # must support at least one pairing table entry while targets must + # support at least five. + CONFIG_MTORR_FLOW_CONTROL = 0x33 + # Setting the retry queue size. + CONFIG_RETRY_QUEUE_SIZE = 0x34 + # Setting the new broadcast entry threshold. + CONFIG_NEW_BROADCAST_ENTRY_THRESHOLD = 0x35 + # The length of time, in seconds, that a trust center will store a + # transient link key that a device can use to join its network. A transient + # key is added with a call to emberAddTransientLinkKey. After the transient + # key is added, it will be removed once this amount of time has passed. A + # joining device will not be able to use that key to join until it is added + # again on the trust center. The default value is 300 seconds, i.e., 5 + # minutes. + CONFIG_TRANSIENT_KEY_TIMEOUT_S = 0x36 + # The number of passive acknowledgements to record from neighbors before we stop + # re-transmitting broadcasts + CONFIG_BROADCAST_MIN_ACKS_NEEDED = 0x37 + # The length of time, in seconds, that a trust center will allow a Trust Center + # (insecure) rejoin for a device that is using the well-known link key. This timeout + # takes effect once rejoins using the well-known key has been allowed. This command + # updates the emAllowTcRejoinsUsingWellKnownKeyTimeoutSec value. + CONFIG_TC_REJOINS_USING_WELL_KNOWN_KEY_TIMEOUT_S = 0x38 + # Valid range of a CTUNE value is 0x0000-0x01FF. Higher order bits (0xFE00) of the + # 16-bit value are ignored. + CONFIG_CTUNE_VALUE = 0x39 + # To configure non trust center node to assume a concentrator type of the trust + # center it join to, until it receive many-to-one route request from the trust + # center. For the trust center node, concentrator type is configured from the + # concentrator plugin. The stack by default assumes trust center be a low RAM + # concentrator that make other devices send route record to the trust center even + # without receiving a many-to-one route request. The default concentrator type can + # be changed by setting appropriate EmberAssumeTrustCenterConcentratorType config + # value. + CONFIG_ASSUME_TC_CONCENTRATOR_TYPE = 0x40 + # This is green power proxy table size. This value is readonly and cannot be set at + # runtime. + CONFIG_GP_PROXY_TABLE_SIZE = 0x41 + # This is green power sink table size. This value is readonly and cannot be set at + # runtime. + EZSP_CONFIG_GP_SINK_TABLE_SIZE = 0x42 + + +class EzspValueId(basic.enum8): + # Identifies a value. + + # The contents of the node data stack token. + VALUE_TOKEN_STACK_NODE_DATA = 0x00 + # The types of MAC passthrough messages that the host wishes to receive. + VALUE_MAC_PASSTHROUGH_FLAGS = 0x01 + # The source address used to filter legacy EmberNet messages when the + # MAC_PASSTHROUGH_EMBERNET_SOURCE flag is set in VALUE_MAC_PASSTHROUGH_FLAGS. + VALUE_EMBERNET_PASSTHROUGH_SOURCE_ADDRESS = 0x02 + # The number of available message buffers. + VALUE_FREE_BUFFERS = 0x03 + # Selects sending synchronous callbacks in ezsp-uart. + VALUE_UART_SYNCH_CALLBACKS = 0x04 + # The maximum incoming transfer size for the local node. + VALUE_MAXIMUM_INCOMING_TRANSFER_SIZE = 0x05 + # The maximum outgoing transfer size for the local node. + VALUE_MAXIMUM_OUTGOING_TRANSFER_SIZE = 0x06 + # A boolean indicating whether stack tokens are written to persistent + # storage as they change. + VALUE_STACK_TOKEN_WRITING = 0x07 + # A read-only value indicating whether the stack is currently performing a rejoin. + VALUE_STACK_IS_PERFORMING_REJOIN = 0x08 + # A list of EmberMacFilterMatchData values. + VALUE_MAC_FILTER_LIST = 0x09 + # The Ember Extended Security Bitmask. + VALUE_EXTENDED_SECURITY_BITMASK = 0x0A + # The node short ID. + VALUE_NODE_SHORT_ID = 0x0B + # The descriptor capability of the local node. + VALUE_DESCRIPTOR_CAPABILITY = 0x0C + # The stack device request sequence number of the local node. + VALUE_STACK_DEVICE_REQUEST_SEQUENCE_NUMBER = 0x0D + # Enable or disable radio hold-off. + VALUE_RADIO_HOLD_OFF = 0x0E + # The flags field associated with the endpoint data. + VALUE_ENDPOINT_FLAGS = 0x0F + # Enable/disable the Mfg security config key settings. + VALUE_MFG_SECURITY_CONFIG = 0x10 + # Retrieves the version information from the stack on the NCP. + VALUE_VERSION_INFO = 0x11 + # This will get/set the rejoin reason noted by the host for a subsequent call to + # emberFindAndRejoinNetwork(). After a call to emberFindAndRejoinNetwork() the + # host's rejoin reason will be set to REJOIN_REASON_NONE. The NCP will store the + # rejoin reason used by the call to emberFindAndRejoinNetwork() + VALUE_NEXT_HOST_REJOIN_REASON = 0x12 + # This is the reason that the last rejoin took place. This value may only be + # retrieved, not set. The rejoin may have been initiated by the stack (NCP) or the + # application (host). If a host initiated a rejoin the reason will be set by default + # to REJOIN_DUE_TO_APP_EVENT_1. If the application wishes to denote its own rejoin + # reasons it can do so by calling ezspSetValue(VALUE_HOST_REJOIN_REASON, + # REJOIN_DUE_TO_APP_EVENT_X). X is a number corresponding to one of the app events + # defined. If the NCP initiated a rejoin it will record this value internally for + # retrieval by ezspGetValue(VALUE_REAL_REJOIN_REASON). + VALUE_LAST_REJOIN_REASON = 0x13 + # The next ZigBee sequence number. + VALUE_NEXT_ZIGBEE_SEQUENCE_NUMBER = 0x14 + # CCA energy detect threshold for radio. + VALUE_CCA_THRESHOLD = 0x15 + # The threshold value for a counter + VALUE_SET_COUNTER_THRESHOLD = 0x17 + # Resets all counters thresholds to 0xFF + VALUE_RESET_COUNTER_THRESHOLDS = 0x18 + # Clears all the counters + VALUE_CLEAR_COUNTERS = 0x19 + # The node's new certificate signed by the CA. + EZSP_VALUE_CERTIFICATE_283K1 = 0x1A + # The Certificate Authority's public key. + EZSP_VALUE_PUBLIC_KEY_283K1 = 0x1B + # The node's new static private key. + EZSP_VALUE_PRIVATE_KEY_283K1 = 0x1C + # The NWK layer security frame counter value + VALUE_NWK_FRAME_COUNTER = 0x23 + # The APS layer security frame counter value + VALUE_APS_FRAME_COUNTER = 0x24 + # Sets the device type to use on the next rejoin using device type + VALUE_RETRY_DEVICE_TYPE = 0x25 + # Setting this byte enables R21 behavior on the NCP. + VALUE_ENABLE_R21_BEHAVIOR = 0x29 + # Configure the antenna mode(0-primary,1-secondary,2- toggle on tx ack fail). + VALUE_ANTENNA_MODE = 0x30 + # Enable or disable packet traffic arbitration. + VALUE_ENABLE_PTA = 0x31 + # Set packet traffic arbitration configuration options. + VALUE_PTA_OPTIONS = 0x32 + # Configure manufacturing library options(0-non-CSMA transmits,1-CSMA transmits). + VALUE_MFGLIB_OPTIONS = 0x33 + # Sets the flag to use either negotiated power by link power delta (LPD) or fixed + # power value provided by user while forming/joining a network for packet + # transmissions on subghz interface. This is mainly for testing purposes. + VALUE_USE_NEGOTIATED_POWER_BY_LPD = 0x34 + # Set packet traffic arbitration configuration PWM options. + VALUE_PTA_PWM_OPTIONS = 0x35 + # Set packet traffic arbitration directional priority pulse width in microseconds. + VALUE_PTA_DIRECTIONAL_PRIORITY_PULSE_WIDTH = 0x36 + # Set packet traffic arbitration phy select timeout(ms) + VALUE_PTA_PHY_SELECT_TIMEOUT = 0x37 + # Configure the RX antenna mode: (0-do not switch; 1- primary; 2-secondary; + # 3-RX antenna diversity). + VALUE_ANTENNA_RX_MODE = 0x38 + # Configure the timeout to wait for the network key before failing a join. + VALUE_NWK_KEY_TIMEOUT = 0x39 + # The number of failed CSMA attempts due to failed CCA made by the MAC before + # continuing transmission with CCA disabled. This is the same as calling the + # emberForceTxAfterFailedCca(uint8_t csmaAttempts) API. A value of 0 disables the + # feature + VALUE_FORCE_TX_AFTER_FAILED_CCA_ATTEMPTS = 0x3A + # The length of time, in seconds, that a trust center will store a transient link + # key that a device can use to join its network. A transient key is added with a + # call to emberAddTransientLinkKey. After the transient key is added, it will be + # removed once this amount of time has passed. A joining device will not be able to + # use that key to join until it is added again on the trust center. The default + # value is 300 seconds (5 minutes). + VALUE_TRANSIENT_KEY_TIMEOUT_S = 0x3B + # Cumulative energy usage metric since the last value reset of the coulomb counter + # plugin. Setting this value will reset the coulomb counter. + VALUE_COULOMB_COUNTER_USAGE = 0x3C + # When scanning, configure the maximum number of beacons to store in cache. Each + # beacon consumes one packet buffer in RAM. + VALUE_MAX_BEACONS_TO_STORE = 0x3D + # Set the mask to filter out unacceptable child timeout options on a router. + VALUE_END_DEVICE_TIMEOUT_OPTIONS_MASK = 0x3E + # The end device keep alive mode supported by the parent. + VALUE_END_DEVICE_KEEP_ALIVE_SUPPORT_MODE = 0x3F + # Return the active radio config. + VALUE_ACTIVE_RADIO_CONFIG = 0x41 + # Timeout in milliseconds to store entries in the transient device table. + # If the devices are not authenticated before the timeout, the entry shall be + # purged. + VALUE_TRANSIENT_DEVICE_TIMEOUT = 0x43 + + +class EzspPolicyId(basic.enum8): + # Identifies a policy. + + # Controls trust center behavior. + TRUST_CENTER_POLICY = 0x00 + # Controls how external binding modification requests are handled. + BINDING_MODIFICATION_POLICY = 0x01 + # Controls whether the Host supplies unicast replies. + UNICAST_REPLIES_POLICY = 0x02 + # Controls whether pollHandler callbacks are generated. + POLL_HANDLER_POLICY = 0x03 + # Controls whether the message contents are included in the + # messageSentHandler callback. + MESSAGE_CONTENTS_IN_CALLBACK_POLICY = 0x04 + # Controls whether the Trust Center will respond to Trust Center link key + # requests. + TC_KEY_REQUEST_POLICY = 0x05 + # Controls whether the Trust Center will respond to application link key + # requests. + APP_KEY_REQUEST_POLICY = 0x06 + # Controls whether ZigBee packets that appear invalid are automatically + # dropped by the stack. A counter will be incremented when this occurs. + PACKET_VALIDATE_LIBRARY_POLICY = 0x07 + # Controls whether the stack will process ZLL messages. + ZLL_POLICY = 0x08 + # Controls whether Trust Center (insecure) rejoins for devices using the well-known + # link key are accepted. If rejoining using the well-known key is allowed, it is + # disabled again after emAllowTcRejoinsUsingWellKnownKeyTimeoutSec seconds. + TC_REJOINS_USING_WELL_KNOWN_KEY_POLICY = 0x09 + + +class EzspDecisionBitmask(basic.bitmap16): + """EZSP Decision bitmask.""" + + # Disallow joins and rejoins. + DEFAULT_CONFIGURATION = 0x0000 + # Send the network key to all joining devices. + ALLOW_JOINS = 0x0001 + # Send the network key to all rejoining devices. + ALLOW_UNSECURED_REJOINS = 0x0002 + # Send the network key in the clear. + SEND_KEY_IN_CLEAR = 0x0004 + # Do nothing for unsecured rejoins. + IGNORE_UNSECURED_REJOINS = 0x0008 + # Allow joins if there is an entry in the transient key table. + JOINS_USE_INSTALL_CODE_KEY = 0x0010 + # Delay sending the network key to a new joining device. + DEFER_JOINS = 0x0020 + + +class EzspDecisionId(basic.enum8): + # Identifies a policy decision. + + # BINDING_MODIFICATION_POLICY default decision. Do not allow the local + # binding table to be changed by remote nodes. + DISALLOW_BINDING_MODIFICATION = 0x10 + # BINDING_MODIFICATION_POLICY decision. Allow remote nodes to change + # the local binding table. + ALLOW_BINDING_MODIFICATION = 0x11 + # BINDING_MODIFICATION_POLICY decision. Allows remote nodes to set local + # binding entries only if the entries correspond to endpoints defined on + # the device, and for output clusters bound to those endpoints. + CHECK_BINDING_MODIFICATIONS_ARE_VALID_ENDPOINT_CLUSTERS = 0x12 + # UNICAST_REPLIES_POLICY default decision. The NCP will automatically send + # an empty reply (containing no payload) for every unicast received. + HOST_WILL_NOT_SUPPLY_REPLY = 0x20 + # UNICAST_REPLIES_POLICY decision. The NCP will only send a reply if it + # receives a sendReply command from the Host. + HOST_WILL_SUPPLY_REPLY = 0x21 + # POLL_HANDLER_POLICY default decision. Do not inform the Host when a child polls. + POLL_HANDLER_IGNORE = 0x30 + # POLL_HANDLER_POLICY decision. Generate a pollHandler callback when a child polls. + POLL_HANDLER_CALLBACK = 0x31 + # MESSAGE_CONTENTS_IN_CALLBACK_POLICY default decision. Include only the + # message tag in the messageSentHandler callback. + MESSAGE_TAG_ONLY_IN_CALLBACK = 0x40 + # MESSAGE_CONTENTS_IN_CALLBACK_POLICY decision. Include both the message + # tag and the message contents in the messageSentHandler callback. + MESSAGE_TAG_AND_CONTENTS_IN_CALLBACK = 0x41 + # TC_KEY_REQUEST_POLICY decision. When the Trust Center receives a request + # for a Trust Center link key, it will be ignored. + DENY_TC_KEY_REQUESTS = 0x50 + # TC_KEY_REQUEST_POLICY decision. When the Trust Center receives a request for a + # Trust Center link key, it will reply to it with the corresponding key. + ALLOW_TC_KEY_REQUESTS_AND_SEND_CURRENT_KEY = 0x51 + # TC_KEY_REQUEST_POLICY decision. When the Trust Center receives a request + # for a Trust Center link key, it will generate a key to send to the joiner. + ALLOW_TC_KEY_REQUEST_AND_GENERATE_NEW_KEY = 0x52 + # APP_KEY_REQUEST_POLICY decision. When the Trust Center receives a request + # for an application link key, it will be ignored. + DENY_APP_KEY_REQUESTS = 0x60 + # APP_KEY_REQUEST_POLICY decision. When the Trust Center receives a request + # for an application link key, it will randomly generate a key and send it + # to both partners. + ALLOW_APP_KEY_REQUESTS = 0x61 + # Indicates that packet validate library checks are enabled on the NCP. + PACKET_VALIDATE_LIBRARY_CHECKS_ENABLED = 0x62 + # Indicates that packet validate library checks are NOT enabled on the NCP. + PACKET_VALIDATE_LIBRARY_CHECKS_DISABLED = 0x63 + + +class EmberKeyType(basic.enum8): + # Describes the type of ZigBee security key. + + # A shared key between the Trust Center and a device. + TRUST_CENTER_LINK_KEY = 0x01 + # A shared secret used for deriving keys between the Trust Center and a + # device + CURRENT_NETWORK_KEY = 0x03 + # The alternate Network Key that was previously in use, or the newer key + # that will be switched to. + NEXT_NETWORK_KEY = 0x04 + # An Application Link Key shared with another (non-Trust Center) device. + APPLICATION_LINK_KEY = 0x05 + + +class EmberDeviceUpdate(basic.enum8): + # The status of the device update. + + STANDARD_SECURITY_SECURED_REJOIN = 0x0 + STANDARD_SECURITY_UNSECURED_JOIN = 0x1 + DEVICE_LEFT = 0x2 + STANDARD_SECURITY_UNSECURED_REJOIN = 0x3 + + +class EmberCounterType(basic.enum8): + # Defines the events reported to the application by the + # readAndClearCounters command. + + # The MAC received a broadcast. + COUNTER_MAC_RX_BROADCAST = 0 + # The MAC transmitted a broadcast. + COUNTER_MAC_TX_BROADCAST = 1 + # The MAC received a unicast. + COUNTER_MAC_RX_UNICAST = 2 + # The MAC successfully transmitted a unicast. + COUNTER_MAC_TX_UNICAST_SUCCESS = 3 + # The MAC retried a unicast. + COUNTER_MAC_TX_UNICAST_RETRY = 4 + # The MAC unsuccessfully transmitted a unicast. + COUNTER_MAC_TX_UNICAST_FAILED = 5 + # The APS layer received a data broadcast. + COUNTER_APS_DATA_RX_BROADCAST = 6 + # The APS layer transmitted a data broadcast. + COUNTER_APS_DATA_TX_BROADCAST = 7 + # The APS layer received a data unicast. + COUNTER_APS_DATA_RX_UNICAST = 8 + # The APS layer successfully transmitted a data unicast. + COUNTER_APS_DATA_TX_UNICAST_SUCCESS = 9 + # The APS layer retried a data unicast. + COUNTER_APS_DATA_TX_UNICAST_RETRY = 10 + # The APS layer unsuccessfully transmitted a data unicast. + COUNTER_APS_DATA_TX_UNICAST_FAILED = 11 + # The network layer successfully submitted a new route discovery to the MAC. + COUNTER_ROUTE_DISCOVERY_INITIATED = 12 + # An entry was added to the neighbor table. + COUNTER_NEIGHBOR_ADDED = 13 + # An entry was removed from the neighbor table. + COUNTER_NEIGHBOR_REMOVED = 14 + # A neighbor table entry became stale because it had not been heard from. + COUNTER_NEIGHBOR_STALE = 15 + # A node joined or rejoined to the network via this node. + COUNTER_JOIN_INDICATION = 16 + # An entry was removed from the child table. + COUNTER_CHILD_REMOVED = 17 + # EZSP-UART only. An overflow error occurred in the UART. + COUNTER_ASH_OVERFLOW_ERROR = 18 + # EZSP-UART only. A framing error occurred in the UART. + COUNTER_ASH_FRAMING_ERROR = 19 + # EZSP-UART only. An overrun error occurred in the UART. + COUNTER_ASH_OVERRUN_ERROR = 20 + # A message was dropped at the network layer because the NWK frame counter + # was not higher than the last message seen from that source. + COUNTER_NWK_FRAME_COUNTER_FAILURE = 21 + # A message was dropped at the APS layer because the APS frame counter was + # not higher than the last message seen from that source. + COUNTER_APS_FRAME_COUNTER_FAILURE = 22 + # Utility counter for general debugging use. + COUNTER_UTILITY = 23 + # A message was dropped at the APS layer because it had APS encryption but + # the key associated with the sender has not been authenticated, and thus + # the key is not authorized for use in APS data messages. + COUNTER_APS_LINK_KEY_NOT_AUTHORIZED = 24 + # A NWK encrypted message was received but dropped because decryption + # failed. + COUNTER_NWK_DECRYPTION_FAILURE = 25 + # An APS encrypted message was received but dropped because decryption + # failed. + COUNTER_APS_DECRYPTION_FAILURE = 26 + # The number of times we failed to allocate a set of linked packet buffers. + # This doesn't necessarily mean that the packet buffer count was 0 at the + # time, but that the number requested was greater than the number free. + COUNTER_ALLOCATE_PACKET_BUFFER_FAILURE = 27 + # The number of relayed unicast packets. + COUNTER_RELAYED_UNICAST = 28 + # The number of times we dropped a packet due to reaching + # the preset PHY to MAC queue limit (emMaxPhyToMacQueueLength). + COUNTER_PHY_TO_MAC_QUEUE_LIMIT_REACHED = 29 + # The number of times we dropped a packet due to the + # packet-validate library checking a packet and rejecting it + # due to length or other formatting problems. + COUNTER_PACKET_VALIDATE_LIBRARY_DROPPED_COUNT = 30 + # The number of times the NWK retry queue is full and a + # new message failed to be added. + COUNTER_TYPE_NWK_RETRY_OVERFLOW = 31 + # The number of times the PHY layer was unable to transmit + # due to a failed CCA. + COUNTER_PHY_CCA_FAIL_COUNT = 32 + # The number of times a NWK broadcast was dropped because + # the broadcast table was full. + COUNTER_BROADCAST_TABLE_FULL = 33 + # The number of low priority packet traffic arbitration requests. + COUNTER_PTA_LO_PRI_REQUESTED = 34 + # The number of high priority packet traffic arbitration requests. + COUNTER_PTA_HI_PRI_REQUESTED = 35 + # The number of low priority packet traffic arbitration requests denied. + COUNTER_PTA_LO_PRI_DENIED = 36 + # The number of high priority packet traffic arbitration requests denied. + COUNTER_PTA_HI_PRI_DENIED = 37 + # The number of aborted low priority packet traffic arbitration transmissions. + COUNTER_PTA_LO_PRI_TX_ABORTED = 38 + # The number of aborted high priority packet traffic arbitration transmissions. + COUNTER_PTA_HI_PRI_TX_ABORTED = 39 + # The number of times an address conflict has caused node_id change, and an address + # conflict error is sent + COUNTER_ADDRESS_CONFLICT_SENT = 40 + + +class EmberJoinMethod(basic.enum8): + # The type of method used for joining. + + # Normally devices use MAC Association to join a network, which respects + # the "permit joining" flag in the MAC Beacon. For mobile nodes this value + # causes the device to use an Ember Mobile Node Join, which is functionally + # equivalent to a MAC association. This value should be used by default. + USE_MAC_ASSOCIATION = 0x0 + # For those networks where the "permit joining" flag is never turned on, + # they will need to use a ZigBee NWK Rejoin. This value causes the rejoin + # to be sent without NWK security and the Trust Center will be asked to + # send the NWK key to the device. The NWK key sent to the device can be + # encrypted with the device's corresponding Trust Center link key. That is + # determined by the ::EmberJoinDecision on the Trust Center returned by the + # ::emberTrustCenterJoinHandler(). For a mobile node this value will cause + # it to use an Ember Mobile node rejoin, which is functionally equivalent. + USE_NWK_REJOIN = 0x1 + # For those networks where the "permit joining" flag is never turned on, + # they will need to use a NWK Rejoin. If those devices have been + # preconfigured with the NWK key (including sequence number) they can use a + # secured rejoin. This is only necessary for end devices since they need a + # parent. Routers can simply use the ::USE_NWK_COMMISSIONING join method + # below. + USE_NWK_REJOIN_HAVE_NWK_KEY = 0x2 + # For those networks where all network and security information is known + # ahead of time, a router device may be commissioned such that it does not + # need to send any messages to begin communicating on the network. + USE_CONFIGURED_NWK_STATE = 0x3 + + +class EmberNetworkInitBitmask(basic.bitmap16): + # Bitmask options for emberNetworkInit(). + + # No options for Network Init + NETWORK_INIT_NO_OPTIONS = 0x0000 + # Save parent info (node ID and EUI64) in a token during joining/rejoin, + # and restore on reboot. + NETWORK_INIT_PARENT_INFO_IN_TOKEN = 0x0001 + # Send a rejoin request as an end device on reboot if parent information is + # persisted. ZB3 end devices must rejoin on reboot. + NETWORK_INIT_END_DEVICE_REJOIN_ON_REBOOT = 0x0002 + + +EmberNetworkInitStruct = EmberNetworkInitBitmask + + +class EmberMultiPhyNwkConfig(basic.enum8): + """Network configuration for the desired radio interface for multi phy network.""" + + # Enable broadcast support on Routers + BROADCAST_SUPPORT = 0x01 + + +class EmberDutyCycleState(basic.enum8): + """Duty cycle states.""" + + # No Duty cycle tracking or metrics are taking place + DUTY_CYCLE_TRACKING_OFF = 0 + # Duty Cycle is tracked and has not exceeded any thresholds. + DUTY_CYCLE_LBT_NORMAL = 1 + # We have exceeded the limited threshold of our total duty cycle allotment. + DUTY_CYCLE_LBT_LIMITED_THRESHOLD_REACHED = 2 + # We have exceeded the critical threshold of our total duty cycle allotment. + DUTY_CYCLE_LBT_CRITICAL_THRESHOLD_REACHED = 3 + # We have reached the suspend limit and are blocking all outbound transmissions. + DUTY_CYCLE_LBT_SUSPEND_LIMIT_REACHED = 4 + + +class EmberRadioPowerMode(basic.enum8): + """Radio power mode.""" + + # The radio receiver is switched on. + RADIO_POWER_MODE_RX_ON = 0 + # The radio receiver is switched off. + RADIO_POWER_MODE_OFF = 1 + + +class EmberEntropySource(basic.enum8): + """Entropy sources.""" + + # Entropy source error + ENTROPY_SOURCE_ERROR = 0 + # Entropy source is the radio. + ENTROPY_SOURCE_RADIO = 1 + # Entropy source is the TRNG powered by mbed TLS. + ENTROPY_SOURCE_MBEDTLS_TRNG = 2 + # Entropy source is powered by mbed TLS, the source is not TRNG. + ENTROPY_SOURCE_MBEDTLS = 3 + + +class EmberDutyCycleHectoPct(basic.uint16_t): + """ "The percent of duty cycle for a limit. + + Duty Cycle, Limits, and Thresholds are reported in units of Percent * 100 + (i.e. 10000 = 100.00%, 1 = 0.01%)""" + + +class EmberGpProxyTableEntryStatus(basic.uint8_t): + """The proxy table entry status.""" + + +class EmberGpSecurityFrameCounter(basic.uint32_t): + """The security frame counter""" + + +class EmberGpSinkTableEntryStatus(basic.uint8_t): + """The sink table entry status.""" + + +class SecureEzspSecurityType(basic.uint32_t): + """Security type of the Secure EZSP Protocol.""" + + +class SecureEzspSecurityLevel(basic.uint8_t): + """Security level of the Secure EZSP Protocol.""" + + +class SecureEzspRandomNumber(basic.fixed_list(16, basic.uint8_t)): + """Randomly generated 64-bit number. + + Both NCP and Host contribute this number to create the Session ID, + which is used in the nonce. + """ + + +class SecureEzspSessionId(basic.fixed_list(8, basic.uint8_t)): + """Generated 64-bit Session ID, using random numbers from Host and NCP. + + It is generated at each reboot (during negotiation phase). Having both sides + contribute to the value prevents one side from choosing a number that might have + been previously used (either because of a bug or by malicious intent). + """ diff --git a/bellows/ezsp/v9/types/struct.py b/bellows/ezsp/v9/types/struct.py new file mode 100644 index 00000000..0ba6a006 --- /dev/null +++ b/bellows/ezsp/v9/types/struct.py @@ -0,0 +1,204 @@ +"""Protocol version 8 specific structs.""" + +import bellows.types.basic as basic +from bellows.types.struct import ( # noqa: F401 + EmberAesMmoHashContext, + EmberApsFrame, + EmberBindingTableEntry, + EmberCurrentSecurityState, + EmberGpAddress, + EmberInitialSecurityState, + EmberMulticastTableEntry, + EmberNeighborTableEntry, + EmberNetworkParameters, + EmberRouteTableEntry, + EmberTokenData, + EmberTokenInfo, + EmberTokTypeStackZllData, + EmberTokTypeStackZllSecurity, + EmberZigbeeNetwork, + EmberZllAddressAssignment, + EmberZllDeviceInfoRecord, + EmberZllInitialSecurityState, + EmberZllNetwork, + EmberZllSecurityAlgorithmData, + EzspStruct, +) + +from . import named + + +class EmberBeaconData(EzspStruct): + """Beacon data structure.""" + + # The channel of the received beacon. + channel: basic.uint8_t + # The LQI of the received beacon. + lqi: basic.uint8_t + # The RSSI of the received beacon. + rssi: basic.int8s + # The depth of the received beacon. + depth: basic.uint8_t + # The network update ID of the received beacon. + nwkUpdateId: basic.uint8_t + # The power level of the received beacon. This field is valid only if the beacon is + # an enhanced beacon + power: basic.int8s + # The TC connectivity and long uptime from capacity field. + parentPriority: basic.int8s + # The PAN ID of the received beacon. + panId: named.EmberPanId + # The extended PAN ID of the received beacon. + extendedPanId: named.ExtendedPanId + # The sender of the received beacon. + sender: named.EmberNodeId + # Whether or not the beacon is enhanced. + enhanced: named.Bool + # Whether the beacon is advertising permit join. + permitJoin: named.Bool + # Whether the beacon is advertising capacity. + hasCapacity: named.Bool + + +class EmberBeaconIterator(EzspStruct): + """Defines an iterator that is used to loop over cached beacons. Do not write to + fields denoted as Private. + """ + + # The retrieved beacon. + beacon: EmberBeaconData + # (Private) The index of the retrieved beacon. + index: basic.uint8_t + + +class EmberBeaconClassificationParams(EzspStruct): + """The parameters related to beacon prioritization.""" + + # The minimum RSSI value for receiving packets that is used in some beacon + # prioritization algorithms. + minRssiForReceivingPkts: basic.int8s + # The beacon classification mask that identifies which beacon prioritization + # algorithm to pick and defines the relevant parameters. + beaconClassificationMask: basic.uint16_t + + +class EmberKeyStruct(EzspStruct): + # A structure containing a key and its associated data. + # A bitmask indicating the presence of data within the various fields + # in the structure. + bitmask: named.EmberKeyStructBitmask + # The type of the key. + type: named.EmberKeyType + # The actual key data. + key: named.EmberKeyData + # The outgoing frame counter associated with the key. + outgoingFrameCounter: basic.uint32_t + # The frame counter of the partner device associated with the key. + incomingFrameCounter: basic.uint32_t + # The sequence number associated with the key. + sequenceNumber: basic.uint8_t + # The IEEE address of the partner device also in possession of the key. + partnerEUI64: named.EmberEUI64 + + +class EmberGpSinkListEntry(EzspStruct): + # A sink list entry + # The sink list type. + type: basic.uint8_t + # The EUI64 of the target sink. + sinkEUI: named.EmberEUI64 + # The short address of the target sink. + sinkNodeId: named.EmberNodeId + + +class EmberGpProxyTableEntry(EzspStruct): + """The internal representation of a proxy table entry.""" + + # The link key to be used to secure this pairing link. + securityLinkKey: named.EmberKeyData + # Internal status of the proxy table entry. + status: named.EmberGpProxyTableEntryStatus + # The tunneling options + # (this contains both options and extendedOptions from the spec). + options: basic.uint32_t + # The addressing info of the GPD. + gpd: EmberGpAddress + # The assigned alias for the GPD. + assignedAlias: named.EmberNodeId + # The security options field. + securityOptions: basic.uint8_t + # The security frame counter of the GPD. + gpdSecurityFrameCounter: named.EmberGpSecurityFrameCounter + # The list of sinks (hardcoded to 2 which is the spec minimum). + sinkList: basic.fixed_list(2, EmberGpSinkListEntry) + # The groupcast radius. + groupcastRadius: basic.uint8_t + # The search counter + searchCounter: basic.uint8_t + + +class EmberGpSinkTableEntry(EzspStruct): + """The internal representation of a sink table entry.""" + + # Internal status of the sink table entry + status: named.EmberGpSinkTableEntryStatus + # The tunneling options + # (this contains both options and extendedOptions from the spec). + options: basic.uint32_t + # The addressing info of the GPD. + gpd: EmberGpAddress + # The device id for the GPD. + deviceId: basic.uint8_t + # The list of sinks (hardcoded to 2 which is the spec minimum). + sinkList: basic.fixed_list(2, EmberGpSinkListEntry) + # The assigned alias for the GPD. + assignedAlias: named.EmberNodeId + # The groupcast radius. + groupcastRadius: basic.uint8_t + # The security options field. + securityOptions: basic.uint8_t + # The security frame counter of the GPD. + gpdSecurityFrameCounter: named.EmberGpSecurityFrameCounter + # The key to use for GPD. + gpdKey: named.EmberKeyData + + +class EmberDutyCycleLimits(EzspStruct): + """A structure containing duty cycle limit configurations. + + All limits are absolute, and are required to be as follows: + suspLimit > critThresh > limitThresh + For example: + suspLimit = 250 (2.5%), critThresh = 180 (1.8%), limitThresh 100 (1.00%). + """ + + # The vendor identifier field shall contain the vendor identifier of the node. + vendorId: basic.uint16_t + # The vendor string field shall contain the vendor string of the node. + vendorString: basic.fixed_list(7, basic.uint8_t) + + +class EmberPerDeviceDutyCycle(EzspStruct): + """A structure containing per device overall duty cycle consumed + + up to the suspend limit). + """ + + # Node Id of device whose duty cycle is reported. + nodeId: named.EmberNodeId + # Amount of overall duty cycle consumed (up to suspend limit). + dutyCycleConsumed: named.EmberDutyCycleHectoPct + + +class EmberTransientKeyData(EzspStruct): + """The transient key data structure. Added in ver. 5. Revised in ver 8""" + + # The IEEE address paired with the transient link key. + eui64: named.EmberEUI64 + # The key data structure matching the transient key. + keyData: named.EmberKeyData + # This bitmask indicates whether various fields in the structure contain valid data. + bitmask: named.EmberKeyStructBitmask + # The number of seconds remaining before the key is automatically timed out of the + # transient key table. + remainingTimeSeconds: basic.uint16_t diff --git a/bellows/types/named.py b/bellows/types/named.py index 30ea9a9e..65cabbfc 100644 --- a/bellows/types/named.py +++ b/bellows/types/named.py @@ -39,6 +39,11 @@ class EmberMulticastId(basic.HexRepr, basic.uint16_t): _hex_len = 4 +class EmberLibraryId(basic.uint8_t): + # The presence and status of the Ember id. + pass + + class EmberLibraryStatus(basic.uint8_t): # The presence and status of the Ember library. pass @@ -311,6 +316,8 @@ class EzspStatus(basic.enum8): ASH_TX = 0x84 # ASH RX ASH_RX = 0x85 + # Failed to connect to CPC daemon or failed to open CPC endpoint + CPC_ERROR_INIT = 0x86 # No reset or error NO_ERROR = 0xFF @@ -1182,3 +1189,571 @@ class EmberSignature283k1Data(basic.fixed_list(72, basic.uint8_t)): class EmberMessageDigest(basic.fixed_list(16, basic.uint8_t)): """The calculated digest of a message""" + + +class sl_Status(basic.enum32): + # SL Status Codes. + # + # Status Defines + # Generic Errors + # No error. + SL_STATUS_OK = 0x0000 + # Generic error. + SL_STATUS_FAIL = 0x0001 + # State Errors + # Generic invalid state error. + SL_STATUS_INVALID_STATE = 0x0002 + # Module is not ready for requested operation. + SL_STATUS_NOT_READY = 0x0003 + # Module is busy and cannot carry out requested operation. + SL_STATUS_BUSY = 0x0004 + # Operation is in progress and not yet complete (pass or fail). + SL_STATUS_IN_PROGRESS = 0x0005 + # Operation aborted. + SL_STATUS_ABORT = 0x0006 + # Operation timed out. + SL_STATUS_TIMEOUT = 0x0007 + # Operation not allowed per permissions. + SL_STATUS_PERMISSION = 0x0008 + # Non-blocking operation would block. + SL_STATUS_WOULD_BLOCK = 0x0009 + # Operation/module is Idle, cannot carry requested operation. + SL_STATUS_IDLE = 0x000A + # Operation cannot be done while construct is waiting. + SL_STATUS_IS_WAITING = 0x000B + # No task/construct waiting/pending for that action/event. + SL_STATUS_NONE_WAITING = 0x000C + # Operation cannot be done while construct is suspended. + SL_STATUS_SUSPENDED = 0x000D + # Feature not available due to software configuration. + SL_STATUS_NOT_AVAILABLE = 0x000E + # Feature not supported. + SL_STATUS_NOT_SUPPORTED = 0x000F + # Initialization failed. + SL_STATUS_INITIALIZATION = 0x0010 + # Module has not been initialized. + SL_STATUS_NOT_INITIALIZED = 0x0011 + # Module has already been initialized. + SL_STATUS_ALREADY_INITIALIZED = 0x0012 + # Object/construct has been deleted. + SL_STATUS_DELETED = 0x0013 + # Illegal call from ISR. + SL_STATUS_ISR = 0x0014 + # Illegal call because network is up. + SL_STATUS_NETWORK_UP = 0x0015 + # Illegal call because network is down. + SL_STATUS_NETWORK_DOWN = 0x0016 + # Failure due to not being joined in a network. + SL_STATUS_NOT_JOINED = 0x0017 + # Invalid operation as there are no beacons. + SL_STATUS_NO_BEACONS = 0x0018 + # Allocation/ownership Errors + # Generic allocation error. + SL_STATUS_ALLOCATION_FAILED = 0x0019 + # No more resource available to perform the operation. + SL_STATUS_NO_MORE_RESOURCE = 0x001A + # Item/list/queue is empty. + SL_STATUS_EMPTY = 0x001B + # Item/list/queue is full. + SL_STATUS_FULL = 0x001C + # Item would overflow. + SL_STATUS_WOULD_OVERFLOW = 0x001D + # Item/list/queue has been overflowed. + SL_STATUS_HAS_OVERFLOWED = 0x001E + # Generic ownership error. + SL_STATUS_OWNERSHIP = 0x001F + # Already/still owning resource. + SL_STATUS_IS_OWNER = 0x0020 + # Invalid Parameters Errors + # Generic invalid argument or consequence of invalid argument. + SL_STATUS_INVALID_PARAMETER = 0x0021 + # Invalid null pointer received as argument. + SL_STATUS_NULL_POINTER = 0x0022 + # Invalid configuration provided. + SL_STATUS_INVALID_CONFIGURATION = 0x0023 + # Invalid mode. + SL_STATUS_INVALID_MODE = 0x0024 + # Invalid handle. + SL_STATUS_INVALID_HANDLE = 0x0025 + # Invalid type for operation. + SL_STATUS_INVALID_TYPE = 0x0026 + # Invalid index. + SL_STATUS_INVALID_INDEX = 0x0027 + # Invalid range. + SL_STATUS_INVALID_RANGE = 0x0028 + # Invalid key. + SL_STATUS_INVALID_KEY = 0x0029 + # Invalid credentials. + SL_STATUS_INVALID_CREDENTIALS = 0x002A + # Invalid count. + SL_STATUS_INVALID_COUNT = 0x002B + # Invalid signature / verification failed. + SL_STATUS_INVALID_SIGNATURE = 0x002C + # Item could not be found. + SL_STATUS_NOT_FOUND = 0x002D + # Item already exists. + SL_STATUS_ALREADY_EXISTS = 0x002E + # IO/Communication Errors + # Generic I/O failure. + SL_STATUS_IO = 0x002F + # I/O failure due to timeout. + SL_STATUS_IO_TIMEOUT = 0x0030 + # Generic transmission error. + SL_STATUS_TRANSMIT = 0x0031 + # Transmit underflowed. + SL_STATUS_TRANSMIT_UNDERFLOW = 0x0032 + # Transmit is incomplete. + SL_STATUS_TRANSMIT_INCOMPLETE = 0x0033 + # Transmit is busy. + SL_STATUS_TRANSMIT_BUSY = 0x0034 + # Generic reception error. + SL_STATUS_RECEIVE = 0x0035 + # Failed to read on/via given object. + SL_STATUS_OBJECT_READ = 0x0036 + # Failed to write on/via given object. + SL_STATUS_OBJECT_WRITE = 0x0037 + # Message is too long. + SL_STATUS_MESSAGE_TOO_LONG = 0x0038 + # EEPROM/Flash Errors + SL_STATUS_EEPROM_MFG_VERSION_MISMATCH = 0x0039 + SL_STATUS_EEPROM_STACK_VERSION_MISMATCH = 0x003A + # Flash write is inhibited. + SL_STATUS_FLASH_WRITE_INHIBITED = 0x003B + # Flash verification failed. + SL_STATUS_FLASH_VERIFY_FAILED = 0x003C + # Flash programming failed. + SL_STATUS_FLASH_PROGRAM_FAILED = 0x003D + # Flash erase failed. + SL_STATUS_FLASH_ERASE_FAILED = 0x003E + # MAC Errors + SL_STATUS_MAC_NO_DATA = 0x003F + SL_STATUS_MAC_NO_ACK_RECEIVED = 0x0040 + SL_STATUS_MAC_INDIRECT_TIMEOUT = 0x0041 + SL_STATUS_MAC_UNKNOWN_HEADER_TYPE = 0x0042 + SL_STATUS_MAC_ACK_HEADER_TYPE = 0x0043 + SL_STATUS_MAC_COMMAND_TRANSMIT_FAILURE = 0x0044 + # CLI_STORAGE Errors + # Error in open NVM + SL_STATUS_CLI_STORAGE_NVM_OPEN_ERROR = 0x0045 + # Security status codes + # Image checksum is not valid. + SL_STATUS_SECURITY_IMAGE_CHECKSUM_ERROR = 0x0046 + # Decryption failed + SL_STATUS_SECURITY_DECRYPT_ERROR = 0x0047 + # Command status codes + # Command was not recognized + SL_STATUS_COMMAND_IS_INVALID = 0x0048 + # Command or parameter maximum length exceeded + SL_STATUS_COMMAND_TOO_LONG = 0x0049 + # Data received does not form a complete command + SL_STATUS_COMMAND_INCOMPLETE = 0x004A + # Misc Errors + # Bus error, e.g. invalid DMA address + SL_STATUS_BUS_ERROR = 0x004B + # Unified MAC Errors + SL_STATUS_CCA_FAILURE = 0x004C # + # Scan errors + SL_STATUS_MAC_SCANNING = 0x004D + SL_STATUS_MAC_INCORRECT_SCAN_TYPE = 0x004E + SL_STATUS_INVALID_CHANNEL_MASK = 0x004F + SL_STATUS_BAD_SCAN_DURATION = 0x0050 + # Bluetooth status codes + # Bonding procedure can't be started because device has no space + # left for bond. + SL_STATUS_BT_OUT_OF_BONDS = 0x0402 + # Unspecified error + SL_STATUS_BT_UNSPECIFIED = 0x0403 + # Hardware failure + SL_STATUS_BT_HARDWARE = 0x0404 + # The bonding does not exist. + SL_STATUS_BT_NO_BONDING = 0x0406 + # Error using crypto functions + SL_STATUS_BT_CRYPTO = 0x0407 + # Data was corrupted. + SL_STATUS_BT_DATA_CORRUPTED = 0x0408 + # Invalid periodic advertising sync handle + SL_STATUS_BT_INVALID_SYNC_HANDLE = 0x040A + # Bluetooth cannot be used on this hardware + SL_STATUS_BT_INVALID_MODULE_ACTION = 0x040B + # Error received from radio + SL_STATUS_BT_RADIO = 0x040C + # Returned when remote disconnects the connection-oriented channel by sending + # disconnection request. + SL_STATUS_BT_L2CAP_REMOTE_DISCONNECTED = 0x040D + # Returned when local host disconnect the connection-oriented channel by sending + # disconnection request. + SL_STATUS_BT_L2CAP_LOCAL_DISCONNECTED = 0x040E + # Returned when local host did not find a connection-oriented channel with given + # destination CID. + SL_STATUS_BT_L2CAP_CID_NOT_EXIST = 0x040F + # Returned when connection-oriented channel disconnected due to LE connection is dropped. + SL_STATUS_BT_L2CAP_LE_DISCONNECTED = 0x0410 + # Returned when connection-oriented channel disconnected due to remote end send data + # even without credit. + SL_STATUS_BT_L2CAP_FLOW_CONTROL_VIOLATED = 0x0412 + # Returned when connection-oriented channel disconnected due to remote end send flow + # control credits exceed 65535. + SL_STATUS_BT_L2CAP_FLOW_CONTROL_CREDIT_OVERFLOWED = 0x0413 + # Returned when connection-oriented channel has run out of flow control credit and + # local application still trying to send data. + SL_STATUS_BT_L2CAP_NO_FLOW_CONTROL_CREDIT = 0x0414 + # Returned when connection-oriented channel has not received connection response message + # within maximum timeout. + SL_STATUS_BT_L2CAP_CONNECTION_REQUEST_TIMEOUT = 0x0415 + # Returned when local host received a connection-oriented channel connection response + # with an invalid destination CID. + SL_STATUS_BT_L2CAP_INVALID_CID = 0x0416 + # Returned when local host application tries to send a command which is not suitable + # for L2CAP channel's current state. + SL_STATUS_BT_L2CAP_WRONG_STATE = 0x0417 + # Flash reserved for PS store is full + SL_STATUS_BT_PS_STORE_FULL = 0x041B + # PS key not found + SL_STATUS_BT_PS_KEY_NOT_FOUND = 0x041C + # Mismatched or insufficient security level + SL_STATUS_BT_APPLICATION_MISMATCHED_OR_INSUFFICIENT_SECURITY = 0x041D + # Encrypion/decryption operation failed. + SL_STATUS_BT_APPLICATION_ENCRYPTION_DECRYPTION_ERROR = 0x041E + # Bluetooth controller status codes + # Connection does not exist, or connection open request was cancelled. + SL_STATUS_BT_CTRL_UNKNOWN_CONNECTION_IDENTIFIER = 0x1002 + # Pairing or authentication failed due to incorrect results in the pairing or + # authentication procedure. This could be due to an incorrect PIN or Link Key + SL_STATUS_BT_CTRL_AUTHENTICATION_FAILURE = 0x1005 + # Pairing failed because of missing PIN, or authentication failed because of missing Key + SL_STATUS_BT_CTRL_PIN_OR_KEY_MISSING = 0x1006 + # Controller is out of memory. + SL_STATUS_BT_CTRL_MEMORY_CAPACITY_EXCEEDED = 0x1007 + # Link supervision timeout has expired. + SL_STATUS_BT_CTRL_CONNECTION_TIMEOUT = 0x1008 + # Controller is at limit of connections it can support. + SL_STATUS_BT_CTRL_CONNECTION_LIMIT_EXCEEDED = 0x1009 + # The Synchronous Connection Limit to a Device Exceeded error code indicates that + # the Controller has reached the limit to the number of synchronous connections that + # can be achieved to a device. + SL_STATUS_BT_CTRL_SYNCHRONOUS_CONNECTION_LIMIT_EXCEEDED = 0x100A + # The ACL Connection Already Exists error code indicates that an attempt to create + # a new ACL Connection to a device when there is already a connection to this device. + SL_STATUS_BT_CTRL_ACL_CONNECTION_ALREADY_EXISTS = 0x100B + # Command requested cannot be executed because the Controller is in a state where + # it cannot process this command at this time. + SL_STATUS_BT_CTRL_COMMAND_DISALLOWED = 0x100C + # The Connection Rejected Due To Limited Resources error code indicates that an + # incoming connection was rejected due to limited resources. + SL_STATUS_BT_CTRL_CONNECTION_REJECTED_DUE_TO_LIMITED_RESOURCES = 0x100D + # The Connection Rejected Due To Security Reasons error code indicates that a + # connection was rejected due to security requirements not being fulfilled, like + # authentication or pairing. + SL_STATUS_BT_CTRL_CONNECTION_REJECTED_DUE_TO_SECURITY_REASONS = 0x100E + # The Connection was rejected because this device does not accept the BD_ADDR. + # This may be because the device will only accept connections from specific BD_ADDRs. + SL_STATUS_BT_CTRL_CONNECTION_REJECTED_DUE_TO_UNACCEPTABLE_BD_ADDR = 0x100F + # The Connection Accept Timeout has been exceeded for this connection attempt. + SL_STATUS_BT_CTRL_CONNECTION_ACCEPT_TIMEOUT_EXCEEDED = 0x1010 + # A feature or parameter value in the HCI command is not supported. + SL_STATUS_BT_CTRL_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE = 0x1011 + # Command contained invalid parameters. + SL_STATUS_BT_CTRL_INVALID_COMMAND_PARAMETERS = 0x1012 + # User on the remote device terminated the connection. + SL_STATUS_BT_CTRL_REMOTE_USER_TERMINATED = 0x1013 + # The remote device terminated the connection because of low resources + SL_STATUS_BT_CTRL_REMOTE_DEVICE_TERMINATED_CONNECTION_DUE_TO_LOW_RESOURCES = 0x1014 + # Remote Device Terminated Connection due to Power Off + SL_STATUS_BT_CTRL_REMOTE_POWERING_OFF = 0x1015 + # Local device terminated the connection. + SL_STATUS_BT_CTRL_CONNECTION_TERMINATED_BY_LOCAL_HOST = 0x1016 + # The Controller is disallowing an authentication or pairing procedure because + # too little time has elapsed since the last authentication or pairing attempt failed. + SL_STATUS_BT_CTRL_REPEATED_ATTEMPTS = 0x1017 + # The device does not allow pairing. This can be for example, when a device only + # allows pairing during a certain time window after some user input allows pairing + SL_STATUS_BT_CTRL_PAIRING_NOT_ALLOWED = 0x1018 + # The remote device does not support the feature associated with the issued command. + SL_STATUS_BT_CTRL_UNSUPPORTED_REMOTE_FEATURE = 0x101A + # No other error code specified is appropriate to use. + SL_STATUS_BT_CTRL_UNSPECIFIED_ERROR = 0x101F + # Connection terminated due to link-layer procedure timeout. + SL_STATUS_BT_CTRL_LL_RESPONSE_TIMEOUT = 0x1022 + # LL procedure has collided with the same transaction or procedure that is already + # in progress. + SL_STATUS_BT_CTRL_LL_PROCEDURE_COLLISION = 0x1023 + # The requested encryption mode is not acceptable at this time. + SL_STATUS_BT_CTRL_ENCRYPTION_MODE_NOT_ACCEPTABLE = 0x1025 + # Link key cannot be changed because a fixed unit key is being used. + SL_STATUS_BT_CTRL_LINK_KEY_CANNOT_BE_CHANGED = 0x1026 + # LMP PDU or LL PDU that includes an instant cannot be performed because the instan + # when this would have occurred has passed. + SL_STATUS_BT_CTRL_INSTANT_PASSED = 0x1028 + # It was not possible to pair as a unit key was requested and it is not supported. + SL_STATUS_BT_CTRL_PAIRING_WITH_UNIT_KEY_NOT_SUPPORTED = 0x1029 + # LMP transaction was started that collides with an ongoing transaction. + SL_STATUS_BT_CTRL_DIFFERENT_TRANSACTION_COLLISION = 0x102A + # The Controller cannot perform channel assessment because it is not supported. + SL_STATUS_BT_CTRL_CHANNEL_ASSESSMENT_NOT_SUPPORTED = 0x102E + # The HCI command or LMP PDU sent is only possible on an encrypted link. + SL_STATUS_BT_CTRL_INSUFFICIENT_SECURITY = 0x102F + # A parameter value requested is outside the mandatory range of parameters for the + # given HCI command or LMP PDU. + SL_STATUS_BT_CTRL_PARAMETER_OUT_OF_MANDATORY_RANGE = 0x1030 + # The IO capabilities request or response was rejected because the sending Host does + # not support Secure Simple Pairing even though the receiving Link Manager does. + SL_STATUS_BT_CTRL_SIMPLE_PAIRING_NOT_SUPPORTED_BY_HOST = 0x1037 + # The Host is busy with another pairing operation and unable to support the requested + # pairing. The receiving device should retry pairing again later. + SL_STATUS_BT_CTRL_HOST_BUSY_PAIRING = 0x1038 + # The Controller could not calculate an appropriate value for the Channel selection operation. + SL_STATUS_BT_CTRL_CONNECTION_REJECTED_DUE_TO_NO_SUITABLE_CHANNEL_FOUND = 0x1039 + # Operation was rejected because the controller is busy and unable to process the request. + SL_STATUS_BT_CTRL_CONTROLLER_BUSY = 0x103A + # Remote device terminated the connection because of an unacceptable connection interval. + SL_STATUS_BT_CTRL_UNACCEPTABLE_CONNECTION_INTERVAL = 0x103B + # Ddvertising for a fixed duration completed or, for directed advertising, that advertising + # completed without a connection being created. + SL_STATUS_BT_CTRL_ADVERTISING_TIMEOUT = 0x103C + # Connection was terminated because the Message Integrity Check (MIC) failed on a + # received packet. + SL_STATUS_BT_CTRL_CONNECTION_TERMINATED_DUE_TO_MIC_FAILURE = 0x103D + # LL initiated a connection but the connection has failed to be established. Controller did not receive + # any packets from remote end. + SL_STATUS_BT_CTRL_CONNECTION_FAILED_TO_BE_ESTABLISHED = 0x103E + # The MAC of the 802.11 AMP was requested to connect to a peer, but the connection failed. + SL_STATUS_BT_CTRL_MAC_CONNECTION_FAILED = 0x103F + # The master, at this time, is unable to make a coarse adjustment to the piconet clock, + # using the supplied parameters. Instead the master will attempt to move the clock using clock dragging. + SL_STATUS_BT_CTRL_COARSE_CLOCK_ADJUSTMENT_REJECTED_BUT_WILL_TRY_TO_ADJUST_USING_CLOCK_DRAGGING = ( + 0x1040 + ) + # A command was sent from the Host that should identify an Advertising or Sync handle, but the + # Advertising or Sync handle does not exist. + SL_STATUS_BT_CTRL_UNKNOWN_ADVERTISING_IDENTIFIER = 0x1042 + # Number of operations requested has been reached and has indicated the completion of the activity + # (e.g., advertising or scanning). + SL_STATUS_BT_CTRL_LIMIT_REACHED = 0x1043 + # A request to the Controller issued by the Host and still pending was successfully canceled. + SL_STATUS_BT_CTRL_OPERATION_CANCELLED_BY_HOST = 0x1044 + # An attempt was made to send or receive a packet that exceeds the maximum allowed packet l + SL_STATUS_BT_CTRL_PACKET_TOO_LONG = 0x1045 + # Bluetooth attribute status codes + # The attribute handle given was not valid on this server + SL_STATUS_BT_ATT_INVALID_HANDLE = 0x1101 + # The attribute cannot be read + SL_STATUS_BT_ATT_READ_NOT_PERMITTED = 0x1102 + # The attribute cannot be written + SL_STATUS_BT_ATT_WRITE_NOT_PERMITTED = 0x1103 + # The attribute PDU was invalid + SL_STATUS_BT_ATT_INVALID_PDU = 0x1104 + # The attribute requires authentication before it can be read or written. + SL_STATUS_BT_ATT_INSUFFICIENT_AUTHENTICATION = 0x1105 + # Attribute Server does not support the request received from the client. + SL_STATUS_BT_ATT_REQUEST_NOT_SUPPORTED = 0x1106 + # Offset specified was past the end of the attribute + SL_STATUS_BT_ATT_INVALID_OFFSET = 0x1107 + # The attribute requires authorization before it can be read or written. + SL_STATUS_BT_ATT_INSUFFICIENT_AUTHORIZATION = 0x1108 + # Too many prepare writes have been queued + SL_STATUS_BT_ATT_PREPARE_QUEUE_FULL = 0x1109 + # No attribute found within the given attribute handle range. + SL_STATUS_BT_ATT_ATT_NOT_FOUND = 0x110A + # The attribute cannot be read or written using the Read Blob Request + SL_STATUS_BT_ATT_ATT_NOT_LONG = 0x110B + # The Encryption Key Size used for encrypting this link is insufficient. + SL_STATUS_BT_ATT_INSUFFICIENT_ENC_KEY_SIZE = 0x110C + # The attribute value length is invalid for the operation + SL_STATUS_BT_ATT_INVALID_ATT_LENGTH = 0x110D + # The attribute request that was requested has encountered an error that was unlikely, and + # therefore could not be completed as requested. + SL_STATUS_BT_ATT_UNLIKELY_ERROR = 0x110E + # The attribute requires encryption before it can be read or written. + SL_STATUS_BT_ATT_INSUFFICIENT_ENCRYPTION = 0x110F + # The attribute type is not a supported grouping attribute as defined by a higher layer + # specification. + SL_STATUS_BT_ATT_UNSUPPORTED_GROUP_TYPE = 0x1110 + # Insufficient Resources to complete the request + SL_STATUS_BT_ATT_INSUFFICIENT_RESOURCES = 0x1111 + # The server requests the client to rediscover the database. + SL_STATUS_BT_ATT_OUT_OF_SYNC = 0x1112 + # The attribute parameter value was not allowed. + SL_STATUS_BT_ATT_VALUE_NOT_ALLOWED = 0x1113 + # When this is returned in a BGAPI response, the application tried to read or write the + # value of a user attribute from the GATT databa + SL_STATUS_BT_ATT_APPLICATION = 0x1180 + # The requested write operation cannot be fulfilled for reasons other than permissions. + SL_STATUS_BT_ATT_WRITE_REQUEST_REJECTED = 0x11FC + # The Client Characteristic Configuration descriptor is not configured according to the + # requirements of the profile or service. + SL_STATUS_BT_ATT_CLIENT_CHARACTERISTIC_CONFIGURATION_DESCRIPTOR_IMPROPERLY_CONFIGURED = ( + 0x11FD + ) + # The profile or service request cannot be serviced because an operation that has been + # previously triggered is still in progress. + SL_STATUS_BT_ATT_PROCEDURE_ALREADY_IN_PROGRESS = 0x11FE + # The attribute value is out of range as defined by a profile or service specification. + SL_STATUS_BT_ATT_OUT_OF_RANGE = 0x11FF + # Bluetooth Security Manager Protocol status codes + # The user input of passkey failed, for example, the user cancelled the operation + SL_STATUS_BT_SMP_PASSKEY_ENTRY_FAILED = 0x1201 + # Out of Band data is not available for authentication + SL_STATUS_BT_SMP_OOB_NOT_AVAILABLE = 0x1202 + # The pairing procedure cannot be performed as authentication requirements cannot be + # met due to IO capabilities of one or both devices + SL_STATUS_BT_SMP_AUTHENTICATION_REQUIREMENTS = 0x1203 + # The confirm value does not match the calculated compare value + SL_STATUS_BT_SMP_CONFIRM_VALUE_FAILED = 0x1204 + # Pairing is not supported by the device + SL_STATUS_BT_SMP_PAIRING_NOT_SUPPORTED = 0x1205 + # The resultant encryption key size is insufficient for the security requirements of this device + SL_STATUS_BT_SMP_ENCRYPTION_KEY_SIZE = 0x1206 + # The SMP command received is not supported on this device + SL_STATUS_BT_SMP_COMMAND_NOT_SUPPORTED = 0x1207 + # Pairing failed due to an unspecified reason + SL_STATUS_BT_SMP_UNSPECIFIED_REASON = 0x1208 + # Pairing or authentication procedure is disallowed because too little time has elapsed + # since last pairing request or security request + SL_STATUS_BT_SMP_REPEATED_ATTEMPTS = 0x1209 + # The Invalid Parameters error code indicates: the command length is invalid or a parameter + # is outside of the specified range. + SL_STATUS_BT_SMP_INVALID_PARAMETERS = 0x120A + # Indicates to the remote device that the DHKey Check value received doesn't match the one + # calculated by the local device. + SL_STATUS_BT_SMP_DHKEY_CHECK_FAILED = 0x120B + # Indicates that the confirm values in the numeric comparison protocol do not match. + SL_STATUS_BT_SMP_NUMERIC_COMPARISON_FAILED = 0x120C + # Indicates that the pairing over the LE transport failed due to a Pairing Request + # sent over the BR/EDR transport in process. + SL_STATUS_BT_SMP_BREDR_PAIRING_IN_PROGRESS = 0x120D + # Indicates that the BR/EDR Link Key generated on the BR/EDR transport cannot be used + # to derive and distribute keys for the LE transport. + SL_STATUS_BT_SMP_CROSS_TRANSPORT_KEY_DERIVATION_GENERATION_NOT_ALLOWED = 0x120E + # Indicates that the device chose not to accept a distributed key. + SL_STATUS_BT_SMP_KEY_REJECTED = 0x120F + # Bluetooth Mesh status codes + # Returned when trying to add a key or some other unique resource with an ID which already exists + SL_STATUS_BT_MESH_ALREADY_EXISTS = 0x0501 + # Returned when trying to manipulate a key or some other resource with an ID which does not exist + SL_STATUS_BT_MESH_DOES_NOT_EXIST = 0x0502 + # Returned when an operation cannot be executed because a pre-configured limit for keys, + # key bindings, elements, models, virtual addresses, provisioned devices, or provisioning sessions is reached + SL_STATUS_BT_MESH_LIMIT_REACHED = 0x0503 + # Returned when trying to use a reserved address or add a "pre-provisioned" device + # using an address already used by some other device + SL_STATUS_BT_MESH_INVALID_ADDRESS = 0x0504 + # In a BGAPI response, the user supplied malformed data; in a BGAPI event, the remote + # end responded with malformed or unrecognized data + SL_STATUS_BT_MESH_MALFORMED_DATA = 0x0505 + # An attempt was made to initialize a subsystem that was already initialized. + SL_STATUS_BT_MESH_ALREADY_INITIALIZED = 0x0506 + # An attempt was made to use a subsystem that wasn't initialized yet. Call the + # subsystem's init function first. + SL_STATUS_BT_MESH_NOT_INITIALIZED = 0x0507 + # Returned when trying to establish a friendship as a Low Power Node, but no acceptable + # friend offer message was received. + SL_STATUS_BT_MESH_NO_FRIEND_OFFER = 0x0508 + # Provisioning link was unexpectedly closed before provisioning was complete. + SL_STATUS_BT_MESH_PROV_LINK_CLOSED = 0x0509 + # An unrecognized provisioning PDU was received. + SL_STATUS_BT_MESH_PROV_INVALID_PDU = 0x050A + # A provisioning PDU with wrong length or containing field values that are out of + # bounds was received. + SL_STATUS_BT_MESH_PROV_INVALID_PDU_FORMAT = 0x050B + # An unexpected (out of sequence) provisioning PDU was received. + SL_STATUS_BT_MESH_PROV_UNEXPECTED_PDU = 0x050C + # The computed confirmation value did not match the expected value. + SL_STATUS_BT_MESH_PROV_CONFIRMATION_FAILED = 0x050D + # Provisioning could not be continued due to insufficient resources. + SL_STATUS_BT_MESH_PROV_OUT_OF_RESOURCES = 0x050E + # The provisioning data block could not be decrypted. + SL_STATUS_BT_MESH_PROV_DECRYPTION_FAILED = 0x050F + # An unexpected error happened during provisioning. + SL_STATUS_BT_MESH_PROV_UNEXPECTED_ERROR = 0x0510 + # Device could not assign unicast addresses to all of its elements. + SL_STATUS_BT_MESH_PROV_CANNOT_ASSIGN_ADDR = 0x0511 + # Returned when trying to reuse an address of a previously deleted device before an + # IV Index Update has been executed. + SL_STATUS_BT_MESH_ADDRESS_TEMPORARILY_UNAVAILABLE = 0x0512 + # Returned when trying to assign an address that is used by one of the devices in the + # Device Database, or by the Provisioner itself. + SL_STATUS_BT_MESH_ADDRESS_ALREADY_USED = 0x0513 + # Application key or publish address are not set + SL_STATUS_BT_MESH_PUBLISH_NOT_CONFIGURED = 0x0514 + # Application key is not bound to a model + SL_STATUS_BT_MESH_APP_KEY_NOT_BOUND = 0x0515 + # Bluetooth Mesh foundation status codes + # Returned when address in request was not valid + SL_STATUS_BT_MESH_FOUNDATION_INVALID_ADDRESS = 0x1301 + # Returned when model identified is not found for a given element + SL_STATUS_BT_MESH_FOUNDATION_INVALID_MODEL = 0x1302 + # Returned when the key identified by AppKeyIndex is not stored in the node + SL_STATUS_BT_MESH_FOUNDATION_INVALID_APP_KEY = 0x1303 + # Returned when the key identified by NetKeyIndex is not stored in the node + SL_STATUS_BT_MESH_FOUNDATION_INVALID_NET_KEY = 0x1304 + # Returned when The node cannot serve the request due to insufficient resources + SL_STATUS_BT_MESH_FOUNDATION_INSUFFICIENT_RESOURCES = 0x1305 + # Returned when the key identified is already stored in the node and the new + # NetKey value is different + SL_STATUS_BT_MESH_FOUNDATION_KEY_INDEX_EXISTS = 0x1306 + # Returned when the model does not support the publish mechanism + SL_STATUS_BT_MESH_FOUNDATION_INVALID_PUBLISH_PARAMS = 0x1307 + # Returned when the model does not support the subscribe mechanism + SL_STATUS_BT_MESH_FOUNDATION_NOT_SUBSCRIBE_MODEL = 0x1308 + # Returned when storing of the requested parameters failed + SL_STATUS_BT_MESH_FOUNDATION_STORAGE_FAILURE = 0x1309 + # Returned when requested setting is not supported + SL_STATUS_BT_MESH_FOUNDATION_NOT_SUPPORTED = 0x130A + # Returned when the requested update operation cannot be performed due to general constraints + SL_STATUS_BT_MESH_FOUNDATION_CANNOT_UPDATE = 0x130B + # Returned when the requested delete operation cannot be performed due to general constraints + SL_STATUS_BT_MESH_FOUNDATION_CANNOT_REMOVE = 0x130C + # Returned when the requested bind operation cannot be performed due to general constraints + SL_STATUS_BT_MESH_FOUNDATION_CANNOT_BIND = 0x130D + # Returned when The node cannot start advertising with Node Identity or Proxy since the + # maximum number of parallel advertising is reached + SL_STATUS_BT_MESH_FOUNDATION_TEMPORARILY_UNABLE = 0x130E + # Returned when the requested state cannot be set + SL_STATUS_BT_MESH_FOUNDATION_CANNOT_SET = 0x130F + # Returned when an unspecified error took place + SL_STATUS_BT_MESH_FOUNDATION_UNSPECIFIED = 0x1310 + # Returned when the NetKeyIndex and AppKeyIndex combination is not valid for a Config AppKey Update + SL_STATUS_BT_MESH_FOUNDATION_INVALID_BINDING = 0x1311 + # Wi-Fi Errors + # Invalid firmware keyset + SL_STATUS_WIFI_INVALID_KEY = 0x0B01 + # The firmware download took too long + SL_STATUS_WIFI_FIRMWARE_DOWNLOAD_TIMEOUT = 0x0B02 + # Unknown request ID or wrong interface ID used + SL_STATUS_WIFI_UNSUPPORTED_MESSAGE_ID = 0x0B03 + # The request is successful but some parameters have been ignored + SL_STATUS_WIFI_WARNING = 0x0B04 + # No Packets waiting to be received + SL_STATUS_WIFI_NO_PACKET_TO_RECEIVE = 0x0B05 + # The sleep mode is granted + SL_STATUS_WIFI_SLEEP_GRANTED = 0x0B08 + # The WFx does not go back to sleep + SL_STATUS_WIFI_SLEEP_NOT_GRANTED = 0x0B09 + # The SecureLink MAC key was not found + SL_STATUS_WIFI_SECURE_LINK_MAC_KEY_ERROR = 0x0B10 + # The SecureLink MAC key is already installed in OTP + SL_STATUS_WIFI_SECURE_LINK_MAC_KEY_ALREADY_BURNED = 0x0B11 + # The SecureLink MAC key cannot be installed in RAM + SL_STATUS_WIFI_SECURE_LINK_RAM_MODE_NOT_ALLOWED = 0x0B12 + # The SecureLink MAC key installation failed + SL_STATUS_WIFI_SECURE_LINK_FAILED_UNKNOWN_MODE = 0x0B13 + # SecureLink key (re)negotiation failed + SL_STATUS_WIFI_SECURE_LINK_EXCHANGE_FAILED = 0x0B14 + # The device is in an inappropriate state to perform the request + SL_STATUS_WIFI_WRONG_STATE = 0x0B18 + # The request failed due to regulatory limitations + SL_STATUS_WIFI_CHANNEL_NOT_ALLOWED = 0x0B19 + # The connection request failed because no suitable AP was found + SL_STATUS_WIFI_NO_MATCHING_AP = 0x0B1A + # The connection request was aborted by host + SL_STATUS_WIFI_CONNECTION_ABORTED = 0x0B1B + # The connection request failed because of a timeout + SL_STATUS_WIFI_CONNECTION_TIMEOUT = 0x0B1C + # The connection request failed because the AP rejected the device + SL_STATUS_WIFI_CONNECTION_REJECTED_BY_AP = 0x0B1D + # The connection request failed because the WPA handshake did not complete successfully + SL_STATUS_WIFI_CONNECTION_AUTH_FAILURE = 0x0B1E + # The request failed because the retry limit was exceeded + SL_STATUS_WIFI_RETRY_EXCEEDED = 0x0B1F + # The request failed because the MSDU life time was exceeded + SL_STATUS_WIFI_TX_LIFETIME_EXCEEDED = 0x0B20 diff --git a/bellows/types/struct.py b/bellows/types/struct.py index d2251683..37b17691 100644 --- a/bellows/types/struct.py +++ b/bellows/types/struct.py @@ -552,6 +552,28 @@ class EmberZllAddressAssignment(EzspStruct): freeGroupIdMax: named.EmberMulticastId +class EmberTokenData(EzspStruct): + # Token Data + # Token data size in bytes + size: basic.uint32_t + # Token data pointer + data: basic.uint8_t + + +class EmberTokenInfo(EzspStruct): + # Information of a token in the token table + # NVM3 key of the token + nvm3Key: basic.uint32_t + # Token is a counter type + isCnt: named.Bool + # Token is an indexed token + isIdx: named.Bool + # Size of the token + size: basic.uint8_t + # Array size of the token + arraSize: basic.uint8_t + + class EmberTokTypeStackZllData(EzspStruct): # Public API for ZLL stack data token. # Token bitmask. diff --git a/tests/test_ezsp_v9.py b/tests/test_ezsp_v9.py new file mode 100644 index 00000000..4cb499fd --- /dev/null +++ b/tests/test_ezsp_v9.py @@ -0,0 +1,320 @@ +import pytest + +import bellows.ezsp.v9 + +from .async_mock import AsyncMock, MagicMock, patch + +pytestmark = pytest.mark.asyncio + + +@pytest.fixture +def ezsp_f(): + """EZSP v9 protocol handler.""" + return bellows.ezsp.v9.EZSPv9(MagicMock(), MagicMock()) + + +def test_ezsp_frame(ezsp_f): + ezsp_f._seq = 0x22 + data = ezsp_f._ezsp_frame("version", 8) + assert data == b"\x22\x00\x01\x00\x00\x08" + + +def test_ezsp_frame_rx(ezsp_f): + """Test receiving a version frame.""" + ezsp_f(b"\x01\x01\x80\x00\x00\x01\x02\x34\x12") + assert ezsp_f._handle_callback.call_count == 1 + assert ezsp_f._handle_callback.call_args[0][0] == "version" + assert ezsp_f._handle_callback.call_args[0][1] == [0x01, 0x02, 0x1234] + + +@pytest.mark.asyncio +async def test_set_source_routing(ezsp_f): + """Test setting source routing.""" + with patch.object( + ezsp_f, "setSourceRouteDiscoveryMode", new=AsyncMock() + ) as src_mock: + await ezsp_f.set_source_routing() + assert src_mock.await_count == 1 + + +@pytest.mark.asyncio +async def test_pre_permit(ezsp_f): + """Test pre permit.""" + p1 = patch.object(ezsp_f, "setPolicy", new=AsyncMock()) + p2 = patch.object(ezsp_f, "addTransientLinkKey", new=AsyncMock()) + with p1 as pre_permit_mock, p2 as tclk_mock: + await ezsp_f.pre_permit(-1.9) + assert pre_permit_mock.await_count == 2 + assert tclk_mock.await_count == 1 + + +def test_command_frames(ezsp_f): + """Test alphabetical list of frames matches the commands.""" + assert set(ezsp_f.COMMANDS) == set(command_frames) + for name, frame_id in command_frames.items(): + assert ezsp_f.COMMANDS[name][0] == frame_id + assert ezsp_f.COMMANDS_BY_ID[frame_id][0] == name + + +command_frames = { + "addEndpoint": 0x0002, + "addOrUpdateKeyTableEntry": 0x0066, + "addTransientLinkKey": 0x00AF, + "addressTableEntryIsActive": 0x005B, + "aesEncrypt": 0x0094, + "aesMmoHash": 0x006F, + "becomeTrustCenter": 0x0077, + "bindingIsActive": 0x002E, + "bootloadTransmitCompleteHandler": 0x0093, + "broadcastNetworkKeySwitch": 0x0074, + "broadcastNextNetworkKey": 0x0073, + "calculateSmacs": 0x009F, + "calculateSmacs283k1": 0x00EA, + "calculateSmacsHandler": 0x00A0, + "calculateSmacsHandler283k1": 0x00EB, + "callback": 0x0006, + "changeSourceRouteHandler": 0x00C4, + "childJoinHandler": 0x0023, + "clearBindingTable": 0x002A, + "clearKeyTable": 0x00B1, + "clearStoredBeacons": 0x003C, + "clearTemporaryDataMaybeStoreLinkKey": 0x00A1, + "clearTemporaryDataMaybeStoreLinkKey283k1": 0x00EE, + "clearTransientLinkKeys": 0x006B, + "counterRolloverHandler": 0x00F2, + "customFrame": 0x0047, + "customFrameHandler": 0x0054, + "dGpSend": 0x00C6, + "dGpSentHandler": 0x00C7, + "debugWrite": 0x0012, + "delayTest": 0x009D, + "deleteBinding": 0x002D, + "dsaSign": 0x00A6, + "dsaSignHandler": 0x00A7, + "dsaVerify": 0x00A3, + "dsaVerify283k1": 0x00B0, + "dsaVerifyHandler": 0x0078, + "dutyCycleHandler": 0x004D, + "echo": 0x0081, + "energyScanRequest": 0x009C, + "energyScanResultHandler": 0x0048, + "eraseKeyTableEntry": 0x0076, + "findAndRejoinNetwork": 0x0021, + "findKeyTableEntry": 0x0075, + "findUnusedPanId": 0x00D3, + "formNetwork": 0x001E, + "generateCbkeKeys": 0x00A4, + "generateCbkeKeys283k1": 0x00E8, + "generateCbkeKeysHandler": 0x009E, + "generateCbkeKeysHandler283k1": 0x00E9, + "getAddressTableRemoteEui64": 0x005E, + "getAddressTableRemoteNodeId": 0x005F, + "getBinding": 0x002C, + "getBeaconClassificationParams": 0x00F3, + "getBindingRemoteNodeId": 0x002F, + "getCertificate": 0x00A5, + "getCertificate283k1": 0x00EC, + "getChildData": 0x004A, + "setChildData": 0x00AC, + "getConfigurationValue": 0x0052, + "getCurrentDutyCycle": 0x004C, + "getCurrentSecurityState": 0x0069, + "getEui64": 0x0026, + "getDutyCycleLimits": 0x004B, + "getDutyCycleState": 0x0035, + "getExtendedTimeout": 0x007F, + "getExtendedValue": 0x0003, + "getFirstBeacon": 0x003D, + "getKey": 0x006A, + "getKeyTableEntry": 0x0071, + "getLibraryStatus": 0x0001, + "getLogicalChannel": 0x00BA, + "getMfgToken": 0x000B, + "getMulticastTableEntry": 0x0063, + "getNeighbor": 0x0079, + "getNeighborFrameCounter": 0x003E, + "setNeighborFrameCounter": 0x00AD, + "getNetworkParameters": 0x0028, + "getNextBeacon": 0x0004, + "getNodeId": 0x0027, + "getNumStoredBeacons": 0x0008, + "getParentChildParameters": 0x0029, + "getParentClassificationEnabled": 0x00F0, + "getPhyInterfaceCount": 0x00FC, + "getPolicy": 0x0056, + "getRadioParameters": 0x00FD, + "setRadioIeee802154CcaMode": 0x0095, + "getRandomNumber": 0x0049, + "getRouteTableEntry": 0x007B, + "getRoutingShortcutThreshold": 0x00D1, + "getSecurityKeyStatus": 0x00CD, + "getSourceRouteTableEntry": 0x00C1, + "getSourceRouteTableFilledSize": 0x00C2, + "getSourceRouteTableTotalSize": 0x00C3, + "getStandaloneBootloaderVersionPlatMicroPhy": 0x0091, + "getTimer": 0x004E, + "getToken": 0x000A, + "getTokenCount": 0x0100, + "getTokenInfo": 0x0101, + "getTokenData": 0x0102, + "setTokenData": 0x0103, + "resetNode": 0x0104, + "getTransientKeyTableEntry": 0x006D, + "getTransientLinkKey": 0x00CE, + "getTrueRandomEntropySource": 0x004F, + "getValue": 0x00AA, + "getXncpInfo": 0x0013, + "getZllPrimaryChannelMask": 0x00D9, + "getZllSecondaryChannelMask": 0x00DA, + "gpProxyTableGetEntry": 0x00C8, + "gpProxyTableLookup": 0x00C0, + "gpProxyTableProcessGpPairing": 0x00C9, + "gpSinkTableClearAll": 0x00E2, + "gpSinkTableFindOrAllocateEntry": 0x00E1, + "gpSinkTableGetEntry": 0x00DD, + "gpSinkTableInit": 0x0070, + "gpSinkTableLookup": 0x00DE, + "gpSinkTableRemoveEntry": 0x00E0, + "gpSinkTableSetEntry": 0x00DF, + "gpepIncomingMessageHandler": 0x00C5, + "idConflictHandler": 0x007C, + "incomingBootloadMessageHandler": 0x0092, + "incomingManyToOneRouteRequestHandler": 0x007D, + "incomingMessageHandler": 0x0045, + "incomingRouteErrorHandler": 0x0080, + "incomingRouteRecordHandler": 0x0059, + "incomingSenderEui64Handler": 0x0062, + "invalidCommand": 0x0058, + "isHubConnected": 0x00E6, + "isUpTimeLong": 0x00E5, + "isZllNetwork": 0x00BE, + "joinNetwork": 0x001F, + "joinNetworkDirectly": 0x003B, + "launchStandaloneBootloader": 0x008F, + "leaveNetwork": 0x0020, + "lookupEui64ByNodeId": 0x0061, + "lookupNodeIdByEui64": 0x0060, + "macFilterMatchMessageHandler": 0x0046, + "macPassthroughMessageHandler": 0x0097, + "maximumPayloadLength": 0x0033, + "messageSentHandler": 0x003F, + "mfglibEnd": 0x0084, + "mfglibGetChannel": 0x008B, + "mfglibGetPower": 0x008D, + "mfglibRxHandler": 0x008E, + "mfglibSendPacket": 0x0089, + "mfglibSetChannel": 0x008A, + "mfglibSetPower": 0x008C, + "mfglibStart": 0x0083, + "mfglibStartStream": 0x0087, + "mfglibStartTone": 0x0085, + "mfglibStopStream": 0x0088, + "mfglibStopTone": 0x0086, + "multiPhySetRadioChannel": 0x00FB, + "multiPhySetRadioPower": 0x00FA, + "multiPhyStart": 0x00F8, + "multiPhyStop": 0x00F9, + "neighborCount": 0x007A, + "networkFoundHandler": 0x001B, + "networkInit": 0x0017, + "networkState": 0x0018, + "noCallbacks": 0x0007, + "nop": 0x0005, + "permitJoining": 0x0022, + "pollCompleteHandler": 0x0043, + "pollForData": 0x0042, + "pollHandler": 0x0044, + "proxyBroadcast": 0x0037, + "rawTransmitCompleteHandler": 0x0098, + "readAndClearCounters": 0x0065, + "readCounters": 0x00F1, + "remoteDeleteBindingHandler": 0x0032, + "remoteSetBindingHandler": 0x0031, + "removeDevice": 0x00A8, + "replaceAddressTableEntry": 0x0082, + "requestLinkKey": 0x0014, + "resetToFactoryDefaults": 0x00CC, + "scanCompleteHandler": 0x001C, + "sendBootloadMessage": 0x0090, + "sendBroadcast": 0x0036, + "sendLinkPowerDeltaRequest": 0x00F7, + "sendManyToOneRouteRequest": 0x0041, + "sendMulticast": 0x0038, + "sendMulticastWithAlias": 0x003A, + "sendPanIdUpdate": 0x0057, + "sendRawMessage": 0x0096, + "sendRawMessageExtended": 0x0051, + "sendReply": 0x0039, + "sendTrustCenterLinkKey": 0x0067, + "sendUnicast": 0x0034, + "setAddressTableRemoteEui64": 0x005C, + "setAddressTableRemoteNodeId": 0x005D, + "setBeaconClassificationParams": 0x00EF, + "setBinding": 0x002B, + "setBindingRemoteNodeId": 0x0030, + "setBrokenRouteErrorCode": 0x0011, + "setChildData": 0x00AC, + "setConcentrator": 0x0010, + "setConfigurationValue": 0x0053, + "setDutyCycleLimitsInStack": 0x0040, + "setExtendedTimeout": 0x007E, + "setHubConnectivity": 0x00E4, + "setInitialSecurityState": 0x0068, + "setKeyTableEntry": 0x0072, + "setLogicalAndRadioChannel": 0x00B9, + "setLongUpTime": 0x00E3, + "setMacPollFailureWaitTime": 0x00F4, + "setManufacturerCode": 0x0015, + "setMfgToken": 0x000C, + "setMulticastTableEntry": 0x0064, + "setNeighborFrameCounter": 0x00AD, + "setParentClassificationEnabled": 0x00E7, + "setPolicy": 0x0055, + "setPowerDescriptor": 0x0016, + "setPreinstalledCbkeData": 0x00A2, + "setPreinstalledCbkeData283k1": 0x00ED, + "setRadioChannel": 0x009A, + "setRadioIeee802154CcaMode": 0x0095, + "setRadioPower": 0x0099, + "setRoutingShortcutThreshold": 0x00D0, + "setSecurityKey": 0x00CA, + "setSecurityParameters": 0x00CB, + "setSourceRouteDiscoveryMode": 0x005A, + "setTimer": 0x000E, + "setToken": 0x0009, + "setValue": 0x00AB, + "setZllAdditionalState": 0x00D6, + "setZllNodeType": 0x00D5, + "setZllPrimaryChannelMask": 0x00DB, + "setZllSecondaryChannelMask": 0x00DC, + "stackStatusHandler": 0x0019, + "stackTokenChangedHandler": 0x000D, + "startScan": 0x001A, + "stopScan": 0x001D, + "switchNetworkKeyHandler": 0x006E, + "timerHandler": 0x000F, + "trustCenterJoinHandler": 0x0024, + "unicastCurrentNetworkKey": 0x0050, + "unicastNwkKeyUpdate": 0x00A9, + "unusedPanIdFoundHandler": 0x00D2, + "updateTcLinkKey": 0x006C, + "version": 0x0000, + "writeNodeData": 0x00FE, + "zigbeeKeyEstablishmentHandler": 0x009B, + "zllAddressAssignmentHandler": 0x00B8, + "zllClearTokens": 0x0025, + "zllGetTokens": 0x00BC, + "zllNetworkFoundHandler": 0x00B6, + "zllNetworkOps": 0x00B2, + "zllOperationInProgress": 0x00D7, + "zllRxOnWhenIdleGetActive": 0x00D8, + "zllScanCompleteHandler": 0x00B7, + "zllSetDataToken": 0x00BD, + "zllSetInitialSecurityState": 0x00B3, + "zllSetNonZllNetwork": 0x00BF, + "zllSetRadioIdleMode": 0x00D4, + "zllSetRxOnWhenIdle": 0x00B5, + "zllSetSecurityStateWithoutKey": 0x00CF, + "zllStartScan": 0x00B4, + "zllTouchLinkTargetHandler": 0x00BB, +} From 766a6d614a2a50aa6888f22653becffa701c6e62 Mon Sep 17 00:00:00 2001 From: puddly <32534428+puddly@users.noreply.github.com> Date: Mon, 27 Jun 2022 14:50:59 -0400 Subject: [PATCH 3/4] Implement new zigpy network state (#445) * [WIP] Implement new zigpy network state * [WIP] Implement writing network settings * Set NWK and APS frame counters * Change key table size only if the new keys don't fit * Style cleanup * Directly create `EZSPCoordinator` instead of using a quirk * Use zigpy types when creating network state objects * Allow `load_network_info` to be called before/after `start_network` * Allow `node_info.ieee` to be `None` * Use `t.EUI64.UNKNOWN` instead of `None` to represent an unset IEEE addr * Do not leak EZSP types into zigpy * Fix network formation when a hashed TCLK is generated * Joins were not being properly permitting after fcf0b49bc141fd81 * Use the new zigpy `add_endpoint` method * Call `register_endpoints` when connecting * Always leave the current network during formation * Get some unit tests passing * Add unit tests for `bellows.zigbee.util` * Increase `bellows.uart` test coverage to 100% * Ignore `.DS_Store` files * Use new `pytest-asyncio` behavior * Newer versions of EmberZNet use a different response for `getChildData` * Use zigpy data types when populating `state` * Correct the TCLK's `partner_ieee` to use the node's IEEE address * Unit test `load_network_info` Foo * Assume the TCLK is always the well-known key * Add a few more unknown bitfields * Refactor code dealing with keys and add some comments * Write initial unit tests for `write_network_info` * Remove ignored flake8 errors from unit tests * Only use one pair of `network_info` and `node_info` fixtures * Test new code in `bellows.zigbee.util` * Add unit test for when `getMfgToken` is missing * Unit test `_ensure_network_running` * Fix startup delay caused by slow coordinator initialization * Key table size can't be adjusted * Unit test `getChildData` for EZSP v6 * Unit test `leaveNetwork` failing with `INVALID_CALL` * Add the coordinator to the zigpy device dictionary before initialization * Do not back up the address tables on EZSPv4, it is unstable * Do not set frame counters when using EZSPv4 * Fix UART port close on RSTACK message during startup When a RSTACK message is processed right after the UART has been opened, it causes EZSP.enter_failed_state() getting called at a point where the application callbacks are not registered yet. In that case the UART will get closed and it won't get opened again. Bellows is stuck with a closed transport. Avoid this issue by not closing the port in case there is no application callback registered yet. Typically, it is unlikely that a RSTACK message arrives right when the port gets opened (the race window is very narrow). However, with hardware flow control opening the port leads to RTS signal to get asserted which causes the radio to send pending messages, e.g. resets caused by EmberZNet watchdog. Note: With hardware flow control this is only the case if the tty "hupcl" option is set. The option is set by default, but cleared by tools like GNU screen. This option makes sure that the RTS signal is deasserted while the port is closed. Pyserial/bellows does not change the state of that option. * Unit test EZSP v4 changes * Fix tests * Include radio library metadata in network info * Fix unit tests broken by new `metadata` key * Do not set the `HAVE_TRUST_CENTER_EUI64` bit when forming a network * Bump minimum required zigpy version to 0.47.0 * Fix unit tests for Python 3.7 Co-authored-by: Stefan Agner --- .gitignore | 6 +- bellows/ezsp/v7/commands.py | 2 +- bellows/ezsp/v7/types/struct.py | 22 ++ bellows/ezsp/v8/commands.py | 2 +- bellows/ezsp/v8/types/struct.py | 22 ++ bellows/types/named.py | 30 ++ bellows/types/struct.py | 15 - bellows/zigbee/application.py | 416 ++++++++++++++++------ bellows/zigbee/util.py | 109 ++++-- requirements_test.txt | 2 +- setup.cfg | 6 + setup.py | 2 +- tests/test_application.py | 136 +++++-- tests/test_application_network_state.py | 451 ++++++++++++++++++++++++ tests/test_cli.py | 2 +- tests/test_ezsp.py | 2 - tests/test_ezsp_protocol.py | 6 - tests/test_ezsp_v5.py | 2 - tests/test_ezsp_v8.py | 4 - tests/test_ezsp_v9.py | 4 - tests/test_multicast.py | 11 - tests/test_thread.py | 2 - tests/test_types.py | 24 -- tests/test_uart.py | 11 +- tests/test_util.py | 145 ++++++++ 25 files changed, 1184 insertions(+), 250 deletions(-) create mode 100644 tests/test_application_network_state.py create mode 100644 tests/test_util.py diff --git a/.gitignore b/.gitignore index faf75845..15f03246 100644 --- a/.gitignore +++ b/.gitignore @@ -70,4 +70,8 @@ ENV/ .*.swp # Visual Studio Code -.vscode \ No newline at end of file +.vscode + + +# macOS +.DS_Store \ No newline at end of file diff --git a/bellows/ezsp/v7/commands.py b/bellows/ezsp/v7/commands.py index 3da7314b..4c0d89bd 100644 --- a/bellows/ezsp/v7/commands.py +++ b/bellows/ezsp/v7/commands.py @@ -137,7 +137,7 @@ "getChildData": ( 0x4A, (t.uint8_t,), - (t.EmberStatus, t.EmberNodeId, t.EmberEUI64, t.EmberNodeType), + (t.EmberStatus, t.EmberChildData), ), "getSourceRouteTableTotalSize": (0xC3, (), (t.uint8_t,)), "getSourceRouteTableFilledSize": (0xC2, (), (t.uint8_t,)), diff --git a/bellows/ezsp/v7/types/struct.py b/bellows/ezsp/v7/types/struct.py index 7a454846..be0259dc 100644 --- a/bellows/ezsp/v7/types/struct.py +++ b/bellows/ezsp/v7/types/struct.py @@ -189,3 +189,25 @@ class EmberPerDeviceDutyCycle(EzspStruct): nodeId: named.EmberNodeId # Amount of overall duty cycle consumed (up to suspend limit). dutyCycleConsumed: named.EmberDutyCycleHectoPct + + +class EmberChildData(EzspStruct): + """A structure containing a child node's data.""" + + # The EUI64 of the child + eui64: named.EmberEUI64 + # The node type of the child + type: named.EmberNodeType + # The short address of the child + id: named.EmberNodeId + # The phy of the child + phy: basic.uint8_t + # The power of the child + power: basic.uint8_t + # The timeout of the child + timeout: basic.uint8_t + + # The GPD's EUI64. + # gpdIeeeAddress: named.EmberEUI64 + # The GPD's source ID. + # sourceId: basic.uint32_t diff --git a/bellows/ezsp/v8/commands.py b/bellows/ezsp/v8/commands.py index 0c6a5925..ea2b07cc 100644 --- a/bellows/ezsp/v8/commands.py +++ b/bellows/ezsp/v8/commands.py @@ -134,7 +134,7 @@ "getChildData": ( 0x004A, (t.uint8_t,), - (t.EmberStatus, t.EmberNodeId, t.EmberEUI64, t.EmberNodeType), + (t.EmberStatus, t.EmberChildData), ), "getSourceRouteTableTotalSize": (0x00C3, (), (t.uint8_t,)), "getSourceRouteTableFilledSize": (0x00C2, (), (t.uint8_t,)), diff --git a/bellows/ezsp/v8/types/struct.py b/bellows/ezsp/v8/types/struct.py index 8a0fd328..f1a56628 100644 --- a/bellows/ezsp/v8/types/struct.py +++ b/bellows/ezsp/v8/types/struct.py @@ -202,3 +202,25 @@ class EmberTransientKeyData(EzspStruct): # The number of seconds remaining before the key is automatically timed out of the # transient key table. remainingTimeSeconds: basic.uint16_t + + +class EmberChildData(EzspStruct): + """A structure containing a child node's data.""" + + # The EUI64 of the child + eui64: named.EmberEUI64 + # The node type of the child + type: named.EmberNodeType + # The short address of the child + id: named.EmberNodeId + # The phy of the child + phy: basic.uint8_t + # The power of the child + power: basic.uint8_t + # The timeout of the child + timeout: basic.uint8_t + + # The GPD's EUI64. + # gpdIeeeAddress: named.EmberEUI64 + # The GPD's source ID. + # sourceId: basic.uint32_t diff --git a/bellows/types/named.py b/bellows/types/named.py index 65cabbfc..7fc1792a 100644 --- a/bellows/types/named.py +++ b/bellows/types/named.py @@ -610,6 +610,9 @@ class EmberStatus(basic.enum8): # An index was passed into the function that was larger than the valid # range. INDEX_OUT_OF_RANGE = 0xB1 + # The passed key data is not valid. A key of all zeros or all F's are reserved + # values and cannot be used. + KEY_INVALID = 0xB2 # There are no empty entries left in the table. TABLE_FULL = 0xB4 # The requested table entry has been erased and contains no valid data. @@ -970,6 +973,8 @@ class EmberCurrentSecurityBitmask(basic.bitmap16): GLOBAL_LINK_KEY = 0x0004 # This denotes that the node has a Trust Center Link Key. HAVE_TRUST_CENTER_LINK_KEY = 0x0010 + # TODO: 0x0020 is unknown + # TODO: 0x0040 is unknown # This denotes that the Trust Center is using a Hashed Link Key. TRUST_CENTER_USES_HASHED_LINK_KEY = 0x0084 @@ -994,6 +999,12 @@ class EmberKeyStructBitmask(basic.bitmap16): # hears a device announce from the partner indicating it is not # an 'RX on when idle' device. KEY_PARTNER_IS_SLEEPY = 0x0020 + # This indicates that the transient key which is being added is unconfirmed. This + # bit is set when we add a transient key while the EmberTcLinkKeyRequestPolicy is + # EMBER_ALLOW_TC_LINK_KEY_REQUEST_AND_GENERATE_NEW_KEY + UNCONFIRMED_TRANSIENT_KEY = 0x0040 + + # TODO: 0x0080 is unknown class EmberKeyStatus(basic.enum8): @@ -1757,3 +1768,22 @@ class sl_Status(basic.enum32): SL_STATUS_WIFI_RETRY_EXCEEDED = 0x0B1F # The request failed because the MSDU life time was exceeded SL_STATUS_WIFI_TX_LIFETIME_EXCEEDED = 0x0B20 + + +class EmberDistinguishedNodeId(basic.enum16): + """A distinguished network ID that will never be assigned to any node""" + + # This value is used when getting the remote node ID from the address or binding + # tables. It indicates that the address or binding table entry is currently in use + # and network address discovery is underway. + DISCOVERY_ACTIVE = 0xFFFC + + # This value is used when getting the remote node ID from the address or binding + # tables. It indicates that the address or binding table entry is currently in use + # but the node ID corresponding to the EUI64 in the table is currently unknown. + UNKNOWN = 0xFFFD + + # This value is used when setting or getting the remote node ID in the address table + # or getting the remote node ID from the binding table. It indicates that the + # address or binding table entry is not in use. + TABLE_ENTRY_UNUSED = 0xFFFF diff --git a/bellows/types/struct.py b/bellows/types/struct.py index 37b17691..2c1b1387 100644 --- a/bellows/types/struct.py +++ b/bellows/types/struct.py @@ -2,8 +2,6 @@ import inspect import typing -import zigpy.state as app_state - from . import basic, named NoneType = type(None) @@ -282,19 +280,6 @@ class EmberNetworkParameters(EzspStruct): # method. channels: named.Channels - @property - def zigpy_network_information(self) -> app_state.NetworkInformation: - """Convert to NetworkInformation.""" - r = app_state.NetworkInformation( - self.extendedPanId, - app_state.t.PanId(self.panId), - self.nwkUpdateId, - app_state.t.NWK(self.nwkManagerId), - self.radioChannel, - channel_mask=self.channels, - ) - return r - class EmberZigbeeNetwork(EzspStruct): # The parameters of a ZigBee network. diff --git a/bellows/zigbee/application.py b/bellows/zigbee/application.py index 3e5f119d..384fcffe 100644 --- a/bellows/zigbee/application.py +++ b/bellows/zigbee/application.py @@ -9,12 +9,14 @@ import zigpy.application import zigpy.config import zigpy.device -from zigpy.quirks import CustomDevice, CustomEndpoint -import zigpy.state as app_state -from zigpy.types import Addressing, BroadcastAddress +import zigpy.endpoint +from zigpy.exceptions import FormationFailure, NetworkNotFormed +import zigpy.state +import zigpy.types import zigpy.util import zigpy.zdo.types as zdo_t +import bellows from bellows.config import ( CONF_PARAM_SRC_RTG, CONF_PARAM_UNK_DEV, @@ -26,7 +28,7 @@ from bellows.ezsp.v8.types.named import EmberDeviceUpdate import bellows.multicast import bellows.types as t -import bellows.zigbee.util +import bellows.zigbee.util as util APS_ACK_TIMEOUT = 120 COUNTER_EZSP_BUFFERS = "EZSP_FREE_BUFFERS" @@ -97,25 +99,17 @@ def multicast(self): """Return EZSP MulticastController.""" return self._multicast - async def add_endpoint( - self, - endpoint=1, - profile_id=zigpy.profiles.zha.PROFILE_ID, - device_id=0xBEEF, - app_flags=0x00, - input_clusters=[], - output_clusters=[], - ): + async def add_endpoint(self, descriptor: zdo_t.SimpleDescriptor) -> None: """Add endpoint.""" res = await self._ezsp.addEndpoint( - endpoint, - profile_id, - device_id, - app_flags, - len(input_clusters), - len(output_clusters), - input_clusters, - output_clusters, + descriptor.endpoint, + descriptor.profile, + descriptor.device_type, + descriptor.device_version, + len(descriptor.input_clusters), + len(descriptor.output_clusters), + descriptor.input_clusters, + descriptor.output_clusters, ) LOGGER.debug("Ezsp adding endpoint: %s", res) @@ -127,8 +121,16 @@ async def cleanup_tc_link_key(self, ieee: t.EmberEUI64) -> None: status = await self._ezsp.eraseKeyTableEntry(index) LOGGER.debug("Cleaned up TC link key for %s device: %s", ieee, status) - async def startup(self, auto_form=False): - """Perform a complete application startup""" + async def _get_board_info(self) -> tuple[str, str, str] | tuple[None, None, None]: + """Get the board info, handling errors when `getMfgToken` is not supported.""" + try: + return await self._ezsp.get_board_info() + except EzspError as exc: + LOGGER.info("EZSP Radio does not support getMfgToken command: %r", exc) + + return None, None, None + + async def connect(self): self._ezsp = await bellows.ezsp.EZSP.initialize(self.config) ezsp = self._ezsp @@ -141,47 +143,37 @@ async def startup(self, auto_form=False): self._in_flight_msg = asyncio.Semaphore(count) LOGGER.debug("APS_UNICAST_MESSAGE_COUNT is set to %s", count) - await self.add_endpoint( - output_clusters=[zigpy.zcl.clusters.security.IasZone.cluster_id] - ) + await self.register_endpoints() - try: - brd_manuf, brd_name, version = await self._ezsp.get_board_info() - LOGGER.info("EZSP Radio manufacturer: %s", brd_manuf) - LOGGER.info("EZSP Radio board name: %s", brd_name) - LOGGER.info("EmberZNet version: %s", version) - except EzspError as exc: - LOGGER.info("EZSP Radio does not support getMfgToken command: %s", str(exc)) + brd_manuf, brd_name, version = await self._get_board_info() + LOGGER.info("EZSP Radio manufacturer: %s", brd_manuf) + LOGGER.info("EZSP Radio board name: %s", brd_name) + LOGGER.info("EmberZNet version: %s", version) - v = await ezsp.networkInit() - if v[0] != t.EmberStatus.SUCCESS: - if not auto_form: - raise ControllerError("Could not initialize network") - await self.form_network() + async def _ensure_network_running(self) -> bool: + """ + Ensures the network is currently running and returns whether or not the network + was started. + """ + (state,) = await self._ezsp.networkState() - status, node_type, nwk_params = await ezsp.getNetworkParameters() - assert status == t.EmberStatus.SUCCESS # TODO: Better check - if node_type != t.EmberNodeType.COORDINATOR: - if not auto_form: - raise ControllerError("Network not configured as coordinator") + if state == self._ezsp.types.EmberNetworkStatus.JOINED_NETWORK: + return False - LOGGER.info( - "Leaving current network as %s and forming new network", node_type.name - ) - (status,) = await self._ezsp.leaveNetwork() - assert status == t.EmberStatus.NETWORK_DOWN - await self.form_network() - status, node_type, nwk_params = await ezsp.getNetworkParameters() - assert status == t.EmberStatus.SUCCESS + (init_status,) = await self._ezsp.networkInit() + if init_status != t.EmberStatus.SUCCESS: + raise NetworkNotFormed(f"Failed to init network: {init_status!r}") + + return True + + async def start_network(self): + ezsp = self._ezsp + + await self._ensure_network_running() - LOGGER.info("Node type: %s, Network parameters: %s", node_type, nwk_params) await ezsp.update_policies(self.config) - (nwk,) = await ezsp.getNodeId() - (ieee,) = await ezsp.getEui64() + await self.load_network_info(load_devices=False) - node_info = app_state.NodeInfo(nwk, ieee, node_type.zdo_logical_type) - self.state.node_information = node_info - self.state.network_information = nwk_params.zigpy_network_information for cnt_group in self.state.counters: cnt_group.reset() @@ -193,54 +185,257 @@ async def startup(self, auto_form=False): self.controller_event.set() self._watchdog_task = asyncio.create_task(self._watchdog()) - self.handle_join(self.nwk, self.ieee, 0) - LOGGER.debug("EZSP nwk=0x%04x, IEEE=%s", self._nwk, str(self._ieee)) + ezsp_device = EZSPCoordinator( + application=self, + ieee=self.state.node_info.ieee, + nwk=self.state.node_info.nwk, + ) + self.devices[self.state.node_info.ieee] = ezsp_device - await self.multicast.startup(self.get_device(self.ieee)) + # The coordinator device does not respond to attribute reads + ezsp_device.endpoints[1] = EZSPCoordinator.EZSPEndpoint(ezsp_device, 1) + ezsp_device.model = ezsp_device.endpoints[1].model + ezsp_device.manufacturer = ezsp_device.endpoints[1].manufacturer + await ezsp_device.schedule_initialize() - async def shutdown(self): - """Shutdown and cleanup ControllerApplication.""" - LOGGER.info("Shutting down ControllerApplication") - self.controller_event.clear() - if self._watchdog_task and not self._watchdog_task.done(): - LOGGER.debug("Cancelling watchdog") - self._watchdog_task.cancel() - if self._reset_task and not self._reset_task.done(): - self._reset_task.cancel() - if self._ezsp is not None: - self._ezsp.close() + await self.multicast.startup(ezsp_device) + + async def load_network_info(self, *, load_devices=False) -> None: + ezsp = self._ezsp + + await self._ensure_network_running() + + status, node_type, nwk_params = await ezsp.getNetworkParameters() + assert status == t.EmberStatus.SUCCESS + + if node_type != t.EmberNodeType.COORDINATOR: + raise NetworkNotFormed("Device not configured as coordinator") + + (nwk,) = await ezsp.getNodeId() + (ieee,) = await ezsp.getEui64() + + self.state.node_info = zigpy.state.NodeInfo( + nwk=zigpy.types.NWK(nwk), + ieee=zigpy.types.EUI64(ieee), + logical_type=node_type.zdo_logical_type, + ) + + (status, security_level) = await ezsp.getConfigurationValue( + ezsp.types.EzspConfigId.CONFIG_SECURITY_LEVEL + ) + assert status == t.EmberStatus.SUCCESS + security_level = zigpy.types.uint8_t(security_level) + + (status, network_key) = await ezsp.getKey( + ezsp.types.EmberKeyType.CURRENT_NETWORK_KEY + ) + assert status == t.EmberStatus.SUCCESS + + (status, ezsp_tc_link_key) = await ezsp.getKey( + ezsp.types.EmberKeyType.TRUST_CENTER_LINK_KEY + ) + assert status == t.EmberStatus.SUCCESS + tc_link_key = util.ezsp_key_to_zigpy_key(ezsp_tc_link_key, ezsp) + + (status, state) = await ezsp.getCurrentSecurityState() + assert status == t.EmberStatus.SUCCESS + + stack_specific = {} + + if ( + ezsp.types.EmberCurrentSecurityBitmask.TRUST_CENTER_USES_HASHED_LINK_KEY + in state.bitmask + ): + stack_specific["ezsp"] = {"hashed_tclk": tc_link_key.key.serialize().hex()} + tc_link_key.key = zigpy.types.KeyData(b"ZigBeeAlliance09") + + # The TCLK IEEE address is returned as `FF:FF:FF:FF:FF:FF:FF:FF` + if self.state.node_info.logical_type == zdo_t.LogicalType.Coordinator: + tc_link_key.partner_ieee = self.state.node_info.ieee + + brd_manuf, brd_name, version = await self._get_board_info() + + self.state.network_info = zigpy.state.NetworkInfo( + source=f"bellows@{bellows.__version__}", + extended_pan_id=zigpy.types.ExtendedPanId(nwk_params.extendedPanId), + pan_id=zigpy.types.PanId(nwk_params.panId), + nwk_update_id=zigpy.types.uint8_t(nwk_params.nwkUpdateId), + nwk_manager_id=zigpy.types.NWK(nwk_params.nwkManagerId), + channel=zigpy.types.uint8_t(nwk_params.radioChannel), + channel_mask=zigpy.types.Channels(nwk_params.channels), + security_level=zigpy.types.uint8_t(security_level), + network_key=util.ezsp_key_to_zigpy_key(network_key, ezsp), + tc_link_key=tc_link_key, + key_table=[], + children=[], + nwk_addresses={}, + stack_specific=stack_specific, + metadata={ + "ezsp": { + "manufacturer": brd_manuf, + "board": brd_name, + "version": version, + "stack_version": ezsp.ezsp_version, + } + }, + ) - async def form_network(self): - nwk = self.config[zigpy.config.CONF_NWK] + if not load_devices: + return + + for idx in range(0, 192): + (status, key) = await ezsp.getKeyTableEntry(idx) + + if status == t.EmberStatus.INDEX_OUT_OF_RANGE: + break + elif status == t.EmberStatus.TABLE_ENTRY_ERASED: + continue + + assert status == t.EmberStatus.SUCCESS + + self.state.network_info.key_table.append( + util.ezsp_key_to_zigpy_key(key, ezsp) + ) + + for idx in range(0, 255 + 1): + (status, *rsp) = await ezsp.getChildData(idx) + + if status == t.EmberStatus.NOT_JOINED: + continue + + if ezsp.ezsp_version >= 7: + nwk = rsp[0].id + eui64 = rsp[0].eui64 + node_type = rsp[0].type + else: + nwk, eui64, node_type = rsp + + self.state.network_info.children.append(eui64) + self.state.network_info.nwk_addresses[eui64] = nwk + + # v4 can crash when getAddressTableRemoteNodeId(32) is received + # Error code: undefined_0x8a + if ezsp.ezsp_version == 4: + return + + for idx in range(0, 255 + 1): + (nwk,) = await ezsp.getAddressTableRemoteNodeId(idx) + (eui64,) = await ezsp.getAddressTableRemoteEui64(idx) + + # Ignore invalid NWK entries + if nwk in t.EmberDistinguishedNodeId.__members__.values(): + continue + + self.state.network_info.nwk_addresses[ + zigpy.types.EUI64(eui64) + ] = zigpy.types.NWK(nwk) + + async def write_network_info( + self, *, network_info: zigpy.state.NetworkInfo, node_info: zigpy.state.NodeInfo + ) -> None: + ezsp = self._ezsp + + try: + (status,) = await ezsp.leaveNetwork() + if status != t.EmberStatus.NETWORK_DOWN: + raise FormationFailure("Couldn't leave network") + except bellows.exception.EzspError: + pass + + stack_specific = network_info.stack_specific.get("ezsp", {}) + (current_eui64,) = await ezsp.getEui64() + + if ( + node_info.ieee != zigpy.types.EUI64.UNKNOWN + and node_info.ieee != current_eui64 + ): + should_update_eui64 = stack_specific.get( + "i_understand_i_can_update_eui64_only_once_and_i_still_want_to_do_it" + ) - pan_id = nwk[zigpy.config.CONF_NWK_PAN_ID] - if pan_id is None: - pan_id = int.from_bytes(os.urandom(2), byteorder="little") + if should_update_eui64: + new_ncp_eui64 = t.EmberEUI64(node_info.ieee) + (status,) = await ezsp.setMfgToken( + t.EzspMfgTokenId.MFG_CUSTOM_EUI_64, new_ncp_eui64.serialize() + ) + assert status == t.EmberStatus.SUCCESS + else: + LOGGER.warning( + "Current node's IEEE address (%s) does not match the backup's (%s)", + current_eui64, + node_info.ieee, + ) - extended_pan_id = nwk[zigpy.config.CONF_NWK_EXTENDED_PAN_ID] - if extended_pan_id is None: - extended_pan_id = t.EmberEUI64([t.uint8_t(0)] * 8) + use_hashed_tclk = ezsp.ezsp_version > 4 - hashed_tclk = self._ezsp.ezsp_version > 4 - initial_security_state = bellows.zigbee.util.zha_security( - nwk, controller=True, hashed_tclk=hashed_tclk + if use_hashed_tclk and not stack_specific.get("hashed_tclk"): + # Generate a random default + network_info.stack_specific.setdefault("ezsp", {})[ + "hashed_tclk" + ] = os.urandom(16).hex() + + initial_security_state = util.zha_security( + network_info=network_info, + node_info=node_info, + use_hashed_tclk=use_hashed_tclk, ) - v = await self._ezsp.setInitialSecurityState(initial_security_state) - assert v[0] == t.EmberStatus.SUCCESS # TODO: Better check + (status,) = await ezsp.setInitialSecurityState(initial_security_state) + assert status == t.EmberStatus.SUCCESS + + # Clear the key table + (status,) = await ezsp.clearKeyTable() + assert status == t.EmberStatus.SUCCESS + + # Write APS link keys + for key in network_info.key_table: + ember_key = util.zigpy_key_to_ezsp_key(key, ezsp) + + # XXX: is there no way to set the outgoing frame counter or seq? + (status,) = await ezsp.addOrUpdateKeyTableEntry( + ember_key.partnerEUI64, True, ember_key.key + ) + if status != t.EmberStatus.SUCCESS: + LOGGER.warning("Couldn't add %s key: %s", key, status) + + if ezsp.ezsp_version > 4: + # Set NWK frame counter + (status,) = await ezsp.setValue( + ezsp.types.EzspValueId.VALUE_NWK_FRAME_COUNTER, + t.uint32_t(network_info.network_key.tx_counter).serialize(), + ) + assert status == t.EmberStatus.SUCCESS + + # Set APS frame counter + (status,) = await ezsp.setValue( + ezsp.types.EzspValueId.VALUE_APS_FRAME_COUNTER, + t.uint32_t(network_info.tc_link_key.tx_counter).serialize(), + ) + assert status == t.EmberStatus.SUCCESS + + # Set the network settings parameters = t.EmberNetworkParameters() - parameters.panId = t.EmberPanId(pan_id) - parameters.extendedPanId = extended_pan_id + parameters.panId = t.EmberPanId(network_info.pan_id) + parameters.extendedPanId = t.EmberEUI64(network_info.extended_pan_id) parameters.radioTxPower = t.uint8_t(8) - parameters.radioChannel = t.uint8_t(nwk[zigpy.config.CONF_NWK_CHANNEL]) + parameters.radioChannel = t.uint8_t(network_info.channel) parameters.joinMethod = t.EmberJoinMethod.USE_MAC_ASSOCIATION - parameters.nwkManagerId = t.EmberNodeId(0) - parameters.nwkUpdateId = t.uint8_t(nwk[zigpy.config.CONF_NWK_UPDATE_ID]) - parameters.channels = nwk[zigpy.config.CONF_NWK_CHANNELS] + parameters.nwkManagerId = t.EmberNodeId(network_info.nwk_manager_id) + parameters.nwkUpdateId = t.uint8_t(network_info.nwk_update_id) + parameters.channels = t.Channels(network_info.channel_mask) - await self._ezsp.formNetwork(parameters) - await self._ezsp.setValue( - self._ezsp.types.EzspValueId.VALUE_STACK_TOKEN_WRITING, 1 - ) + await ezsp.formNetwork(parameters) + await ezsp.setValue(ezsp.types.EzspValueId.VALUE_STACK_TOKEN_WRITING, 1) + + async def disconnect(self): + # TODO: how do you shut down the stack? + self.controller_event.clear() + if self._watchdog_task and not self._watchdog_task.done(): + LOGGER.debug("Cancelling watchdog") + self._watchdog_task.cancel() + if self._reset_task and not self._reset_task.done(): + self._reset_task.cancel() + if self._ezsp is not None: + self._ezsp.close() async def force_remove(self, dev): # This should probably be delivered to the parent device instead @@ -277,13 +472,17 @@ def _handle_frame( ) -> None: if message_type == t.EmberIncomingMessageType.INCOMING_BROADCAST: self.state.counters[COUNTERS_CTRL][COUNTER_RX_BCAST].increment() - dst_addressing = Addressing.nwk(0xFFFE, aps_frame.destinationEndpoint) + dst_addressing = zigpy.types.Addressing.nwk( + 0xFFFE, aps_frame.destinationEndpoint + ) elif message_type == t.EmberIncomingMessageType.INCOMING_MULTICAST: self.state.counters[COUNTERS_CTRL][COUNTER_RX_MCAST].increment() - dst_addressing = Addressing.group(aps_frame.groupId) + dst_addressing = zigpy.types.Addressing.group(aps_frame.groupId) elif message_type == t.EmberIncomingMessageType.INCOMING_UNICAST: self.state.counters[COUNTERS_CTRL][COUNTER_RX_UNICAST].increment() - dst_addressing = Addressing.nwk(self.nwk, aps_frame.destinationEndpoint) + dst_addressing = zigpy.types.Addressing.nwk( + self.state.node_info.nwk, aps_frame.destinationEndpoint + ) else: dst_addressing = None @@ -653,7 +852,7 @@ async def broadcast( radius, sequence, data, - broadcast_address=BroadcastAddress.RX_ON_WHEN_IDLE, + broadcast_address=zigpy.types.BroadcastAddress.RX_ON_WHEN_IDLE, ): """Submit and send data out as an unicast transmission. @@ -796,10 +995,10 @@ def handle_route_error(self, status: t.EmberStatus, nwk: t.EmberNodeId) -> None: dev.relays = None -class EZSPCoordinator(CustomDevice): +class EZSPCoordinator(zigpy.device.Device): """Zigpy Device representing Coordinator.""" - class EZSPEndpoint(CustomEndpoint): + class EZSPEndpoint(zigpy.endpoint.Endpoint): @property def manufacturer(self) -> str: """Manufacturer.""" @@ -836,20 +1035,3 @@ async def remove_from_group(self, grp_id: int) -> t.EmberStatus: app.groups[grp_id].remove_member(self) return status - - signature = { - "endpoints": { - 1: { - "profile_id": 0x0104, - "device_type": 0xBEEF, - "input_clusters": [], - "output_clusters": [zigpy.zcl.clusters.security.IasZone.cluster_id], - } - } - } - - replacement = { - "endpoints": {1: (EZSPEndpoint, {})}, - "manufacturer": "Silicon Labs", - "model": "EZSP", - } diff --git a/bellows/zigbee/util.py b/bellows/zigbee/util.py index ac0b9c41..1c615418 100644 --- a/bellows/zigbee/util.py +++ b/bellows/zigbee/util.py @@ -1,39 +1,96 @@ -import os -from typing import Any, Dict +import logging -import zigpy.config +import zigpy.state +import zigpy.types as zigpy_t +import zigpy.zdo.types as zdo_t import bellows.types as t +LOGGER = logging.getLogger(__name__) -def zha_security( - config: Dict[str, Any], controller: bool = False, hashed_tclk: bool = True -) -> None: +def zha_security( + *, + network_info: zigpy.state.NetworkInfo, + node_info: zigpy.state.NodeInfo, + use_hashed_tclk: bool, +) -> t.EmberInitialSecurityState: + """Construct an `EmberInitialSecurityState` out of zigpy network state.""" isc = t.EmberInitialSecurityState() isc.bitmask = ( t.EmberInitialSecurityBitmask.HAVE_PRECONFIGURED_KEY | t.EmberInitialSecurityBitmask.REQUIRE_ENCRYPTED_KEY + | t.EmberInitialSecurityBitmask.TRUST_CENTER_GLOBAL_LINK_KEY + | t.EmberInitialSecurityBitmask.HAVE_NETWORK_KEY ) - isc.preconfiguredKey = t.EmberKeyData(config[zigpy.config.CONF_NWK_TC_LINK_KEY]) - nwk_key = config[zigpy.config.CONF_NWK_KEY] - if nwk_key is None: - nwk_key = os.urandom(16) - isc.networkKey = t.EmberKeyData(nwk_key) - isc.networkKeySequenceNumber = t.uint8_t(config[zigpy.config.CONF_NWK_KEY_SEQ]) - tc_addr = config[zigpy.config.CONF_NWK_TC_ADDRESS] - if tc_addr is None: - tc_addr = [0x00] * 8 - isc.preconfiguredTrustCenterEui64 = t.EmberEUI64(tc_addr) - - if controller: - isc.bitmask |= ( - t.EmberInitialSecurityBitmask.TRUST_CENTER_GLOBAL_LINK_KEY - | t.EmberInitialSecurityBitmask.HAVE_NETWORK_KEY + isc.networkKey = t.EmberKeyData(network_info.network_key.key) + isc.networkKeySequenceNumber = t.uint8_t(network_info.network_key.seq) + + if ( + node_info.logical_type != zdo_t.LogicalType.Coordinator + and network_info.tc_link_key.partner_ieee != zigpy_t.EUI64.UNKNOWN + ): + isc.bitmask |= t.EmberInitialSecurityBitmask.HAVE_TRUST_CENTER_EUI64 + isc.preconfiguredTrustCenterEui64 = t.EmberEUI64( + network_info.tc_link_key.partner_ieee + ) + else: + isc.preconfiguredTrustCenterEui64 = t.EmberEUI64([0x00] * 8) + + if use_hashed_tclk: + if network_info.tc_link_key.key != zigpy_t.KeyData(b"ZigBeeAlliance09"): + LOGGER.warning("Only the well-known TC Link Key is supported") + + isc.bitmask |= t.EmberInitialSecurityBitmask.TRUST_CENTER_USES_HASHED_LINK_KEY + isc.preconfiguredKey, _ = t.EmberKeyData.deserialize( + bytes.fromhex(network_info.stack_specific["ezsp"]["hashed_tclk"]) ) - if hashed_tclk: - isc.preconfiguredKey = t.EmberKeyData(os.urandom(16)) - isc.bitmask |= ( - t.EmberInitialSecurityBitmask.TRUST_CENTER_USES_HASHED_LINK_KEY - ) + else: + isc.preconfiguredKey = t.EmberKeyData(network_info.tc_link_key.key) + return isc + + +def ezsp_key_to_zigpy_key(key, ezsp) -> zigpy.state.Key: + """Convert an `EmberKeyStruct` into a zigpy `Key`.""" + zigpy_key = zigpy.state.Key() + zigpy_key.key = zigpy_t.KeyData(key.key) + + if ezsp.types.EmberKeyStructBitmask.KEY_HAS_SEQUENCE_NUMBER in key.bitmask: + zigpy_key.seq = key.sequenceNumber + + if ezsp.types.EmberKeyStructBitmask.KEY_HAS_OUTGOING_FRAME_COUNTER in key.bitmask: + zigpy_key.tx_counter = key.outgoingFrameCounter + + if ezsp.types.EmberKeyStructBitmask.KEY_HAS_INCOMING_FRAME_COUNTER in key.bitmask: + zigpy_key.rx_counter = key.incomingFrameCounter + + if ezsp.types.EmberKeyStructBitmask.KEY_HAS_PARTNER_EUI64 in key.bitmask: + zigpy_key.partner_ieee = key.partnerEUI64 + + return zigpy_key + + +def zigpy_key_to_ezsp_key(zigpy_key: zigpy.state.Key, ezsp): + """Convert a zigpy `Key` into a `EmberKeyStruct`.""" + key = ezsp.types.EmberKeyStruct() + key.key = ezsp.types.EmberKeyData(zigpy_key.key) + key.bitmask = ezsp.types.EmberKeyStructBitmask(0) + + if zigpy_key.seq is not None: + key.sequenceNumber = t.uint8_t(zigpy_key.seq) + key.bitmask |= ezsp.types.EmberKeyStructBitmask.KEY_HAS_SEQUENCE_NUMBER + + if zigpy_key.tx_counter is not None: + key.outgoingFrameCounter = t.uint32_t(zigpy_key.tx_counter) + key.bitmask |= ezsp.types.EmberKeyStructBitmask.KEY_HAS_OUTGOING_FRAME_COUNTER + + if zigpy_key.rx_counter is not None: + key.outgoingFrameCounter = t.uint32_t(zigpy_key.rx_counter) + key.bitmask |= ezsp.types.EmberKeyStructBitmask.KEY_HAS_INCOMING_FRAME_COUNTER + + if zigpy_key.partner_ieee is not None: + key.partnerEUI64 = t.EmberEUI64(zigpy_key.partner_ieee) + key.bitmask |= ezsp.types.EmberKeyStructBitmask.KEY_HAS_PARTNER_EUI64 + + return key diff --git a/requirements_test.txt b/requirements_test.txt index beb7dcb7..6b9647b0 100644 --- a/requirements_test.txt +++ b/requirements_test.txt @@ -4,6 +4,6 @@ asynctest coveralls==2.1.2 pre-commit pytest -pytest-asyncio +pytest-asyncio>=0.17 pytest-cov pytest-timeout diff --git a/setup.cfg b/setup.cfg index 3f801541..51ea8f48 100644 --- a/setup.cfg +++ b/setup.cfg @@ -12,6 +12,9 @@ ignore = E501, D202 +per-file-ignores = + tests/*:F811,F401,F403 + [isort] profile = black # will group `import x` and `from x import` of the same module. @@ -19,3 +22,6 @@ force_sort_within_sections = true known_first_party = bellows,tests forced_separate = tests combine_as_imports = true + +[tool:pytest] +asyncio_mode = auto diff --git a/setup.py b/setup.py index 72a87189..38eaaea0 100644 --- a/setup.py +++ b/setup.py @@ -22,7 +22,7 @@ "pyserial", "pyserial-asyncio", "voluptuous", - "zigpy>=0.37.0", + "zigpy>=0.47.0", ], dependency_links=[ "https://codeload.github.com/rcloran/pure-pcapy-3/zip/master", diff --git a/tests/test_application.py b/tests/test_application.py index 59c71bbb..677f371d 100644 --- a/tests/test_application.py +++ b/tests/test_application.py @@ -3,8 +3,7 @@ import pytest import zigpy.config -from zigpy.device import Device -from zigpy.zcl.clusters import security +import zigpy.exceptions import zigpy.zdo.types as zdo_t import bellows.config as config @@ -21,8 +20,6 @@ from .async_mock import AsyncMock, MagicMock, PropertyMock, patch, sentinel -pytestmark = pytest.mark.asyncio - APP_CONFIG = { config.CONF_DEVICE: { config.CONF_DEVICE_PATH: "/dev/null", @@ -99,7 +96,9 @@ def ieee(init=0): @patch("zigpy.device.Device._initialize", new=AsyncMock()) @patch("bellows.zigbee.application.ControllerApplication._watchdog", new=AsyncMock()) -async def _test_startup(app, nwk_type, ieee, auto_form=False, init=0, ezsp_version=4): +async def _test_startup( + app, nwk_type, ieee, auto_form=False, init=0, ezsp_version=4, board_info=True +): nwk_params = bellows.types.struct.EmberNetworkParameters( extendedPanId=t.ExtendedPanId.convert("aa:bb:cc:dd:ee:ff:aa:bb"), panId=t.EmberPanId(0x55AA), @@ -117,6 +116,7 @@ async def mock_leave(*args, **kwargs): app._in_flight_msg = None ezsp_mock = MagicMock() + ezsp_mock.types = ezsp_t7 type(ezsp_mock).ezsp_version = PropertyMock(return_value=ezsp_version) ezsp_mock.initialize = AsyncMock(return_value=ezsp_mock) ezsp_mock.connect = AsyncMock() @@ -126,20 +126,63 @@ async def mock_leave(*args, **kwargs): ezsp_mock.setConfigurationValue = AsyncMock(return_value=t.EmberStatus.SUCCESS) ezsp_mock.networkInit = AsyncMock(return_value=[init]) ezsp_mock.getNetworkParameters = AsyncMock(return_value=[0, nwk_type, nwk_params]) - ezsp_mock.get_board_info = AsyncMock( - return_value=("Mock Manufacturer", "Mock board", "Mock version") - ) + + if board_info: + ezsp_mock.get_board_info = AsyncMock( + return_value=("Mock Manufacturer", "Mock board", "Mock version") + ) + else: + ezsp_mock.get_board_info = AsyncMock(side_effect=EzspError("Not supported")) + ezsp_mock.setPolicy = AsyncMock(return_value=[t.EmberStatus.SUCCESS]) ezsp_mock.getMfgToken = AsyncMock(return_value=(b"Some token\xff",)) ezsp_mock.getNodeId = AsyncMock(return_value=[t.EmberNodeId(0x0000)]) ezsp_mock.getEui64 = AsyncMock(return_value=[ieee]) ezsp_mock.getValue = AsyncMock(return_value=(0, b"\x01" * 6)) ezsp_mock.leaveNetwork = AsyncMock(side_effect=mock_leave) - app.form_network = AsyncMock() ezsp_mock.reset = AsyncMock() ezsp_mock.version = AsyncMock() ezsp_mock.getConfigurationValue = AsyncMock(return_value=(0, 1)) ezsp_mock.update_policies = AsyncMock() + ezsp_mock.networkState = AsyncMock( + return_value=[ezsp_mock.types.EmberNetworkStatus.JOINED_NETWORK] + ) + ezsp_mock.getKey = AsyncMock( + return_value=[ + t.EmberStatus.SUCCESS, + t.EmberKeyStruct( + bitmask=t.EmberKeyStructBitmask.KEY_HAS_SEQUENCE_NUMBER + | t.EmberKeyStructBitmask.KEY_HAS_OUTGOING_FRAME_COUNTER, + type=t.EmberKeyType.CURRENT_NETWORK_KEY, + key=t.EmberKeyData(b"ActualNetworkKey"), + outgoingFrameCounter=t.uint32_t(0x12345678), + incomingFrameCounter=t.uint32_t(0x00000000), + sequenceNumber=t.uint8_t(1), + partnerEUI64=t.EmberEUI64.convert("ff:ff:ff:ff:ff:ff:ff:ff"), + ), + ] + ) + + ezsp_mock.getCurrentSecurityState = AsyncMock( + return_value=[ + t.EmberStatus.SUCCESS, + t.EmberCurrentSecurityState( + bitmask=t.EmberCurrentSecurityBitmask.TRUST_CENTER_USES_HASHED_LINK_KEY, + trustCenterLongAddress=t.EmberEUI64.convert("ff:ff:ff:ff:ff:ff:ff:ff"), + ), + ] + ) + ezsp_mock.pre_permit = AsyncMock() + app.permit = AsyncMock() + + def form_network(): + ezsp_mock.getNetworkParameters.return_value = [ + 0, + t.EmberNodeType.COORDINATOR, + nwk_params, + ] + + app.form_network = AsyncMock(side_effect=form_network) p1 = patch.object(bellows.ezsp, "EZSP", new=ezsp_mock) p2 = patch.object(bellows.multicast.Multicast, "startup") @@ -156,8 +199,8 @@ async def test_startup(app, ieee): async def test_startup_nwk_params(app, ieee): assert app.pan_id == 0xFFFE assert app.extended_pan_id == t.ExtendedPanId.convert("ff:ff:ff:ff:ff:ff:ff:ff") - assert app.channel is None - assert app.channels is None + assert app.channel == 0 + assert app.channels == t.Channels.NO_CHANNELS assert app.nwk_update_id == 0 await _test_startup(app, t.EmberNodeType.COORDINATOR, ieee) @@ -189,7 +232,7 @@ async def test_startup_ezsp_ver8(app, ieee): async def test_startup_no_status(app, ieee): """Test when NCP is not a coordinator and not auto forming.""" - with pytest.raises(ControllerError): + with pytest.raises(zigpy.exceptions.NetworkNotFormed): await _test_startup( app, t.EmberNodeType.UNKNOWN_DEVICE, ieee, auto_form=False, init=1 ) @@ -204,7 +247,7 @@ async def test_startup_no_status_form(app, ieee): async def test_startup_end(app, ieee): """Test when NCP is a End Device and not auto forming.""" - with pytest.raises(ControllerError): + with pytest.raises(zigpy.exceptions.NetworkNotFormed): await _test_startup( app, t.EmberNodeType.SLEEPY_END_DEVICE, ieee, auto_form=False ) @@ -215,14 +258,12 @@ async def test_startup_end_form(app, ieee): await _test_startup(app, t.EmberNodeType.SLEEPY_END_DEVICE, ieee, auto_form=True) -async def test_form_network(app): - f = asyncio.Future() - f.set_result([0]) - app._ezsp.setInitialSecurityState.side_effect = [f] - app._ezsp.formNetwork = AsyncMock() - app._ezsp.setValue = AsyncMock() +async def test_startup_no_board_info(app, ieee, caplog): + """Test when NCP does not support `get_board_info`.""" + with caplog.at_level(logging.INFO): + await _test_startup(app, t.EmberNodeType.COORDINATOR, ieee, board_info=False) - await app.form_network() + assert "EZSP Radio does not support getMfgToken command" in caplog.text def _frame_handler( @@ -1048,12 +1089,12 @@ async def test_shutdown(app): @pytest.fixture def coordinator(app, ieee): - dev = Device(app, ieee, 0x0000) - ep = dev.add_endpoint(1) - ep.profile_id = 0x0104 - ep.device_type = 0xBEEF - ep.add_output_cluster(security.IasZone.cluster_id) - return bellows.zigbee.application.EZSPCoordinator(app, ieee, 0x0000, dev) + dev = bellows.zigbee.application.EZSPCoordinator(app, ieee, 0x0000) + dev.endpoints[1] = bellows.zigbee.application.EZSPCoordinator.EZSPEndpoint(dev, 1) + dev.model = dev.endpoints[1].model + dev.manufacturer = dev.endpoints[1].manufacturer + + return dev async def test_ezsp_add_to_group(coordinator): @@ -1447,7 +1488,7 @@ async def test_set_mfg_id(ieee, expected_mfg_id, app, ezsp_mock): sentinel.parent, ], ) - await asyncio.sleep(0.03) + await asyncio.sleep(0.20) if expected_mfg_id is not None: assert ezsp_mock.setManufacturerCode.await_count == 2 assert ezsp_mock.setManufacturerCode.await_args_list[0][0][0] == expected_mfg_id @@ -1457,3 +1498,44 @@ async def test_set_mfg_id(ieee, expected_mfg_id, app, ezsp_mock): ) else: assert ezsp_mock.setManufacturerCode.await_count == 0 + + +async def test_ensure_network_running_joined(app): + ezsp = app._ezsp + ezsp.networkState = AsyncMock( + return_value=[ezsp.types.EmberNetworkStatus.JOINED_NETWORK] + ) + + rsp = await app._ensure_network_running() + + assert not rsp + + ezsp.networkInit.assert_not_called() + + +async def test_ensure_network_running_not_joined_failure(app): + ezsp = app._ezsp + ezsp.networkState = AsyncMock( + return_value=[ezsp.types.EmberNetworkStatus.NO_NETWORK] + ) + ezsp.networkInit = AsyncMock(return_value=[ezsp.types.EmberStatus.INVALID_CALL]) + + with pytest.raises(zigpy.exceptions.NetworkNotFormed): + await app._ensure_network_running() + + ezsp.networkState.assert_called_once() + ezsp.networkInit.assert_called_once() + + +async def test_ensure_network_running_not_joined_success(app): + ezsp = app._ezsp + ezsp.networkState = AsyncMock( + return_value=[ezsp.types.EmberNetworkStatus.NO_NETWORK] + ) + ezsp.networkInit = AsyncMock(return_value=[ezsp.types.EmberStatus.SUCCESS]) + + rsp = await app._ensure_network_running() + assert rsp + + ezsp.networkState.assert_called_once() + ezsp.networkInit.assert_called_once() diff --git a/tests/test_application_network_state.py b/tests/test_application_network_state.py new file mode 100644 index 00000000..74d1fff7 --- /dev/null +++ b/tests/test_application_network_state.py @@ -0,0 +1,451 @@ +import pytest +import zigpy.state +import zigpy.types as zigpy_t +import zigpy.zdo.types as zdo_t + +import bellows +from bellows.exception import EzspError +import bellows.types as t + +from tests.async_mock import AsyncMock, PropertyMock +from tests.test_application import app, ezsp_mock + + +@pytest.fixture +def node_info(): + return zigpy.state.NodeInfo( + nwk=zigpy_t.NWK(0x0000), + ieee=zigpy_t.EUI64.convert("00:12:4b:00:1c:a1:b8:46"), + logical_type=zdo_t.LogicalType.Coordinator, + ) + + +@pytest.fixture +def network_info(node_info): + return zigpy.state.NetworkInfo( + extended_pan_id=zigpy_t.ExtendedPanId.convert("bd:27:0b:38:37:95:dc:87"), + pan_id=zigpy_t.PanId(0x9BB0), + nwk_update_id=18, + nwk_manager_id=zigpy_t.NWK(0x0000), + channel=zigpy_t.uint8_t(15), + channel_mask=zigpy_t.Channels.ALL_CHANNELS, + security_level=zigpy_t.uint8_t(5), + network_key=zigpy.state.Key( + key=zigpy_t.KeyData.convert("2ccade06b3090c310315b3d574d3c85a"), + seq=108, + tx_counter=118785, + ), + tc_link_key=zigpy.state.Key( + key=zigpy_t.KeyData(b"ZigBeeAlliance09"), + partner_ieee=node_info.ieee, + tx_counter=8712428, + ), + key_table=[ + zigpy.state.Key( + key=zigpy_t.KeyData.convert( + "85:7C:05:00:3E:76:1A:F9:68:9A:49:41:6A:60:5C:76" + ), + tx_counter=3792973670, + rx_counter=1083290572, + seq=147, + partner_ieee=zigpy_t.EUI64.convert("CC:CC:CC:FF:FE:E6:8E:CA"), + ), + zigpy.state.Key( + key=zigpy_t.KeyData.convert( + "CA:02:E8:BB:75:7C:94:F8:93:39:D3:9C:B3:CD:A7:BE" + ), + tx_counter=2597245184, + rx_counter=824424412, + seq=19, + partner_ieee=zigpy_t.EUI64.convert("EC:1B:BD:FF:FE:2F:41:A4"), + ), + ], + children=[zigpy_t.EUI64.convert("00:0B:57:FF:FE:2B:D4:57")], + # If exposed by the stack, NWK addresses of other connected devices on the network + nwk_addresses={ + # Two routers + zigpy_t.EUI64.convert("CC:CC:CC:FF:FE:E6:8E:CA"): zigpy_t.NWK(0x44CB), + zigpy_t.EUI64.convert("EC:1B:BD:FF:FE:2F:41:A4"): zigpy_t.NWK(0x0702), + # Child device + zigpy_t.EUI64.convert("00:0b:57:ff:fe:2b:d4:57"): zigpy_t.NWK(0xC06B), + }, + stack_specific={"ezsp": {"hashed_tclk": "abcdabcdabcdabcdabcdabcdabcdabcd"}}, + source=f"bellows@{bellows.__version__}", + ) + + +def _mock_app_for_load(app): + """Mock methods on the application and EZSP objects to run network state code.""" + ezsp = app._ezsp + + app._ensure_network_running = AsyncMock() + ezsp.getNetworkParameters = AsyncMock( + return_value=[ + t.EmberStatus.SUCCESS, + t.EmberNodeType.COORDINATOR, + t.EmberNetworkParameters( + extendedPanId=t.ExtendedPanId.convert("bd:27:0b:38:37:95:dc:87"), + panId=t.EmberPanId(0x9BB0), + radioTxPower=8, + radioChannel=15, + joinMethod=t.EmberJoinMethod.USE_MAC_ASSOCIATION, + nwkManagerId=t.EmberNodeId(0x0000), + nwkUpdateId=18, + channels=t.Channels.ALL_CHANNELS, + ), + ] + ) + + ezsp.getNodeId = AsyncMock(return_value=[t.EmberNodeId(0x0000)]) + ezsp.getEui64 = AsyncMock( + return_value=[t.EmberEUI64.convert("00:12:4b:00:1c:a1:b8:46")] + ) + ezsp.getConfigurationValue = AsyncMock(return_value=[t.EmberStatus.SUCCESS, 5]) + + def get_key(key_type): + key = { + ezsp.types.EmberKeyType.CURRENT_NETWORK_KEY: ezsp.types.EmberKeyStruct( + bitmask=( + t.EmberKeyStructBitmask.KEY_HAS_OUTGOING_FRAME_COUNTER + | t.EmberKeyStructBitmask.KEY_HAS_SEQUENCE_NUMBER + ), + type=ezsp.types.EmberKeyType.CURRENT_NETWORK_KEY, + key=t.EmberKeyData(bytes.fromhex("2ccade06b3090c310315b3d574d3c85a")), + outgoingFrameCounter=118785, + incomingFrameCounter=0, + sequenceNumber=108, + partnerEUI64=t.EmberEUI64.convert("00:00:00:00:00:00:00:00"), + ), + ezsp.types.EmberKeyType.TRUST_CENTER_LINK_KEY: ezsp.types.EmberKeyStruct( + bitmask=( + t.EmberKeyStructBitmask.KEY_IS_AUTHORIZED + | t.EmberKeyStructBitmask.KEY_HAS_PARTNER_EUI64 + | t.EmberKeyStructBitmask.KEY_HAS_OUTGOING_FRAME_COUNTER + ), + type=ezsp.types.EmberKeyType.TRUST_CENTER_LINK_KEY, + key=t.EmberKeyData(bytes.fromhex("abcdabcdabcdabcdabcdabcdabcdabcd")), + outgoingFrameCounter=8712428, + incomingFrameCounter=0, + sequenceNumber=0, + partnerEUI64=t.EmberEUI64.convert("00:12:4b:00:1c:a1:b8:46"), + ), + }[key_type] + + return (t.EmberStatus.SUCCESS, key) + + ezsp.getKey = AsyncMock(side_effect=get_key) + ezsp.getCurrentSecurityState = AsyncMock( + return_value=( + t.EmberStatus.SUCCESS, + ezsp.types.EmberCurrentSecurityState( + bitmask=( + t.EmberCurrentSecurityBitmask.TRUST_CENTER_USES_HASHED_LINK_KEY + | 64 + | 32 + | t.EmberCurrentSecurityBitmask.HAVE_TRUST_CENTER_LINK_KEY + | t.EmberCurrentSecurityBitmask.GLOBAL_LINK_KEY + ), + trustCenterLongAddress=t.EmberEUI64.convert("00:12:4b:00:1c:a1:b8:46"), + ), + ) + ) + + +async def test_load_network_info_no_devices(app, network_info, node_info): + """Test `load_network_info(load_devices=False)`""" + _mock_app_for_load(app) + + await app.load_network_info(load_devices=False) + + assert app.state.node_info == node_info + assert app.state.network_info == network_info.replace( + key_table=[], + children=[], + nwk_addresses={}, + metadata=app.state.network_info.metadata, + ) + + +@pytest.mark.parametrize("ezsp_ver", [4, 6, 7]) +async def test_load_network_info_with_devices(app, network_info, node_info, ezsp_ver): + """Test `load_network_info(load_devices=True)`""" + _mock_app_for_load(app) + + def get_child_data_v6(index): + if index == 0: + status = t.EmberStatus.SUCCESS + else: + status = t.EmberStatus.NOT_JOINED + + return ( + status, + t.EmberNodeId(0xC06B), + t.EmberEUI64.convert("00:0b:57:ff:fe:2b:d4:57"), + t.EmberNodeType.SLEEPY_END_DEVICE, + ) + + def get_child_data_v7(index): + if index == 0: + status = t.EmberStatus.SUCCESS + else: + status = t.EmberStatus.NOT_JOINED + + return ( + status, + app._ezsp.types.EmberChildData( + eui64=t.EmberEUI64.convert("00:0b:57:ff:fe:2b:d4:57"), + type=t.EmberNodeType.SLEEPY_END_DEVICE, + id=t.EmberNodeId(0xC06B), + phy=0, + power=128, + timeout=3, + ), + ) + + app._ezsp.ezsp_version = ezsp_ver + app._ezsp.getChildData = AsyncMock( + side_effect={ + 7: get_child_data_v7, + 6: get_child_data_v6, + 4: get_child_data_v6, + }[ezsp_ver] + ) + + def get_key_table_entry(index): + if index == 0: + return ( + t.EmberStatus.SUCCESS, + app._ezsp.types.EmberKeyStruct( + bitmask=( + t.EmberKeyStructBitmask.KEY_IS_AUTHORIZED + | t.EmberKeyStructBitmask.KEY_HAS_PARTNER_EUI64 + | t.EmberKeyStructBitmask.KEY_HAS_INCOMING_FRAME_COUNTER + | t.EmberKeyStructBitmask.KEY_HAS_OUTGOING_FRAME_COUNTER + ), + type=app._ezsp.types.EmberKeyType.APPLICATION_LINK_KEY, + key=t.EmberKeyData( + bytes.fromhex("857C05003E761AF9689A49416A605C76") + ), + outgoingFrameCounter=3792973670, + incomingFrameCounter=1083290572, + sequenceNumber=147, + partnerEUI64=t.EmberEUI64.convert("CC:CC:CC:FF:FE:E6:8E:CA"), + ), + ) + elif index == 1: + return ( + t.EmberStatus.SUCCESS, + app._ezsp.types.EmberKeyStruct( + bitmask=( + t.EmberKeyStructBitmask.KEY_IS_AUTHORIZED + | t.EmberKeyStructBitmask.KEY_HAS_PARTNER_EUI64 + | t.EmberKeyStructBitmask.KEY_HAS_INCOMING_FRAME_COUNTER + | t.EmberKeyStructBitmask.KEY_HAS_OUTGOING_FRAME_COUNTER + ), + type=app._ezsp.types.EmberKeyType.APPLICATION_LINK_KEY, + key=t.EmberKeyData( + bytes.fromhex("CA02E8BB757C94F89339D39CB3CDA7BE") + ), + outgoingFrameCounter=2597245184, + incomingFrameCounter=824424412, + sequenceNumber=19, + partnerEUI64=t.EmberEUI64.convert("EC:1B:BD:FF:FE:2F:41:A4"), + ), + ) + elif index >= 12: + status = t.EmberStatus.INDEX_OUT_OF_RANGE + else: + status = t.EmberStatus.TABLE_ENTRY_ERASED + + return ( + status, + app._ezsp.types.EmberKeyStruct( + bitmask=t.EmberKeyStructBitmask(244), + type=app._ezsp.types.EmberKeyType(0x46), + key=t.EmberKeyData(bytes.fromhex("b8a11c004b1200cdabcdabcdabcdabcd")), + outgoingFrameCounter=8192, + incomingFrameCounter=0, + sequenceNumber=0, + partnerEUI64=t.EmberEUI64.convert("00:12:4b:00:1c:a1:b8:46"), + ), + ) + + app._ezsp.getKeyTableEntry = AsyncMock(side_effect=get_key_table_entry) + + def get_addr_table_node_id(index): + return ( + { + 16: t.EmberNodeId(0x44CB), + 17: t.EmberNodeId(0x0702), + }.get(index, t.EmberNodeId(0xFFFF)), + ) + + app._ezsp.getAddressTableRemoteNodeId = AsyncMock( + side_effect=get_addr_table_node_id + ) + + def get_addr_table_eui64(index): + if index < 16: + return (t.EmberEUI64.convert("ff:ff:ff:ff:ff:ff:ff:ff"),) + elif 16 <= index <= 17: + return ( + { + 16: t.EmberEUI64.convert("cc:cc:cc:ff:fe:e6:8e:ca"), + 17: t.EmberEUI64.convert("ec:1b:bd:ff:fe:2f:41:a4"), + }[index], + ) + else: + return (t.EmberEUI64.convert("00:00:00:00:00:00:00:00"),) + + app._ezsp.getAddressTableRemoteEui64 = AsyncMock(side_effect=get_addr_table_eui64) + + await app.load_network_info(load_devices=True) + + nwk_addresses = network_info.nwk_addresses + + # v4 doesn't support address table reads so the only known addresses are from the + # `getChildData` call + if ezsp_ver == 4: + nwk_addresses = { + ieee: addr + for ieee, addr in network_info.nwk_addresses.items() + if ieee in network_info.children + } + else: + nwk_addresses = network_info.nwk_addresses + + # EZSP doesn't provide a command to set the key sequence number + assert app.state.network_info == network_info.replace( + key_table=[key.replace(seq=0) for key in network_info.key_table], + nwk_addresses=nwk_addresses, + metadata=app.state.network_info.metadata, + ) + assert app.state.node_info == node_info + + # No crash-prone calls were made + if ezsp_ver == 4: + app._ezsp.getAddressTableRemoteNodeId.assert_not_called() + app._ezsp.getAddressTableRemoteEui64.assert_not_called() + + +def _mock_app_for_write(app, network_info, node_info, ezsp_ver=None): + ezsp = app._ezsp + + ezsp.leaveNetwork = AsyncMock(return_value=[t.EmberStatus.NETWORK_DOWN]) + ezsp.getEui64 = AsyncMock( + return_value=[t.EmberEUI64.convert("00:12:4b:00:1c:a1:b8:46")] + ) + + ezsp.setInitialSecurityState = AsyncMock(return_value=[t.EmberStatus.SUCCESS]) + ezsp.clearKeyTable = AsyncMock(return_value=[t.EmberStatus.SUCCESS]) + ezsp.getConfigurationValue = AsyncMock( + return_value=[t.EmberStatus.SUCCESS, t.uint8_t(200)] + ) + ezsp.addOrUpdateKeyTableEntry = AsyncMock( + side_effect=[ + # Only the first one succeeds + (t.EmberStatus.SUCCESS,), + ] + + [ + # The rest will fail + (t.EmberStatus.TABLE_FULL,), + ] + * 20 + ) + + if ezsp_ver is not None: + ezsp.ezsp_version = ezsp_ver + + if ezsp_ver == 4: + ezsp.setValue = AsyncMock(return_value=[t.EmberStatus.BAD_ARGUMENT]) + else: + ezsp.setValue = AsyncMock(return_value=[t.EmberStatus.SUCCESS]) + + ezsp.formNetwork = AsyncMock(return_value=[t.EmberStatus.SUCCESS]) + ezsp.setValue = AsyncMock(return_value=[t.EmberStatus.SUCCESS]) + ezsp.setMfgToken = AsyncMock(return_value=[t.EmberStatus.SUCCESS]) + + +async def test_write_network_info_failed_leave1(app, network_info, node_info): + _mock_app_for_write(app, network_info, node_info) + + app._ezsp.leaveNetwork.return_value = [t.EmberStatus.BAD_ARGUMENT] + + with pytest.raises(zigpy.exceptions.FormationFailure): + await app.write_network_info(network_info=network_info, node_info=node_info) + + +async def test_write_network_info_failed_leave2(app, network_info, node_info): + _mock_app_for_write(app, network_info, node_info) + + app._ezsp.leaveNetwork.side_effect = EzspError("failed to leave network") + + await app.write_network_info(network_info=network_info, node_info=node_info) + + +@pytest.mark.parametrize("ezsp_ver", [4, 7]) +async def test_write_network_info(app, network_info, node_info, ezsp_ver): + _mock_app_for_write(app, network_info, node_info, ezsp_ver) + + await app.write_network_info(network_info=network_info, node_info=node_info) + + +async def test_write_network_info_write_new_eui64(app, network_info, node_info): + _mock_app_for_write(app, network_info, node_info) + + # Differs from what is in `node_info` + app._ezsp.getEui64.return_value = [t.EmberEUI64.convert("AA:AA:AA:AA:AA:AA:AA:AA")] + + await app.write_network_info( + network_info=network_info.replace( + stack_specific={ + "ezsp": { + "i_understand_i_can_update_eui64_only_once_and_i_still_want_to_do_it": True, + **network_info.stack_specific["ezsp"], + } + } + ), + node_info=node_info, + ) + + # The EUI64 is written + expected_eui64 = t.EmberEUI64(node_info.ieee).serialize() + app._ezsp.setMfgToken.assert_called_once_with( + t.EzspMfgTokenId.MFG_CUSTOM_EUI_64, expected_eui64 + ) + + +async def test_write_network_info_dont_write_new_eui64(app, network_info, node_info): + _mock_app_for_write(app, network_info, node_info) + + # Differs from what is in `node_info` + app._ezsp.getEui64.return_value = [t.EmberEUI64.convert("AA:AA:AA:AA:AA:AA:AA:AA")] + + await app.write_network_info( + # We don't provide the magic key so nothing is written + network_info=network_info, + node_info=node_info, + ) + + # The EUI64 is *not* written + app._ezsp.setMfgToken.assert_not_called() + + +async def test_write_network_info_generate_hashed_tclk(app, network_info, node_info): + _mock_app_for_write(app, network_info, node_info) + + seen_keys = set() + + for i in range(10): + app._ezsp.setInitialSecurityState.reset_mock() + + await app.write_network_info( + network_info=network_info.replace(stack_specific={}), + node_info=node_info, + ) + + call = app._ezsp.setInitialSecurityState.mock_calls[0] + seen_keys.add(tuple(call[1][0].preconfiguredKey)) + + # A new hashed key is randomly generated each time if none is provided + assert len(seen_keys) == 10 diff --git a/tests/test_cli.py b/tests/test_cli.py index ecf8d4f2..d7aca4c1 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -1,3 +1,3 @@ -import bellows.cli # noqa: F401 +import bellows.cli # Just being able to import is a small test... diff --git a/tests/test_ezsp.py b/tests/test_ezsp.py index 77b2d252..6597f42f 100644 --- a/tests/test_ezsp.py +++ b/tests/test_ezsp.py @@ -15,8 +15,6 @@ config.CONF_DEVICE_BAUDRATE: 115200, } -pytestmark = pytest.mark.asyncio - @pytest.fixture async def ezsp_f(): diff --git a/tests/test_ezsp_protocol.py b/tests/test_ezsp_protocol.py index 754ad2ba..94595378 100644 --- a/tests/test_ezsp_protocol.py +++ b/tests/test_ezsp_protocol.py @@ -8,8 +8,6 @@ from .async_mock import AsyncMock, MagicMock, patch -pytestmark = pytest.mark.asyncio - class _DummyProtocolHandler(bellows.ezsp.v4.EZSPv4): """Protocol handler mock.""" @@ -29,7 +27,6 @@ def prot_hndl(): return _DummyProtocolHandler(MagicMock(), MagicMock()) -@pytest.mark.asyncio async def test_command(prot_hndl): coro = prot_hndl.command("nop") prot_hndl._awaiting[prot_hndl._seq - 1][2].set_result(True) @@ -73,7 +70,6 @@ def test_receive_reply_invalid_command(prot_hndl): assert prot_hndl.cb_mock.call_count == 0 -@pytest.mark.asyncio async def test_cfg_initialize(prot_hndl, caplog): """Test initialization.""" @@ -94,7 +90,6 @@ async def test_cfg_initialize(prot_hndl, caplog): assert "Couldn't set" in caplog.text -@pytest.mark.asyncio async def test_update_policies(prot_hndl): """Test update_policies.""" @@ -107,7 +102,6 @@ async def test_update_policies(prot_hndl): await prot_hndl.update_policies({"ezsp_policies": {}}) -@pytest.mark.asyncio @pytest.mark.parametrize( "status, raw, expected_value", ( diff --git a/tests/test_ezsp_v5.py b/tests/test_ezsp_v5.py index 41a78396..714bc25d 100644 --- a/tests/test_ezsp_v5.py +++ b/tests/test_ezsp_v5.py @@ -4,8 +4,6 @@ from .async_mock import AsyncMock, MagicMock, patch -pytestmark = pytest.mark.asyncio - @pytest.fixture def ezsp_f(): diff --git a/tests/test_ezsp_v8.py b/tests/test_ezsp_v8.py index 6ea14672..a39f9384 100644 --- a/tests/test_ezsp_v8.py +++ b/tests/test_ezsp_v8.py @@ -4,8 +4,6 @@ from .async_mock import AsyncMock, MagicMock, patch -pytestmark = pytest.mark.asyncio - @pytest.fixture def ezsp_f(): @@ -27,7 +25,6 @@ def test_ezsp_frame_rx(ezsp_f): assert ezsp_f._handle_callback.call_args[0][1] == [0x01, 0x02, 0x1234] -@pytest.mark.asyncio async def test_set_source_routing(ezsp_f): """Test setting source routing.""" with patch.object( @@ -37,7 +34,6 @@ async def test_set_source_routing(ezsp_f): assert src_mock.await_count == 1 -@pytest.mark.asyncio async def test_pre_permit(ezsp_f): """Test pre permit.""" p1 = patch.object(ezsp_f, "setPolicy", new=AsyncMock()) diff --git a/tests/test_ezsp_v9.py b/tests/test_ezsp_v9.py index 4cb499fd..f4876b4c 100644 --- a/tests/test_ezsp_v9.py +++ b/tests/test_ezsp_v9.py @@ -4,8 +4,6 @@ from .async_mock import AsyncMock, MagicMock, patch -pytestmark = pytest.mark.asyncio - @pytest.fixture def ezsp_f(): @@ -27,7 +25,6 @@ def test_ezsp_frame_rx(ezsp_f): assert ezsp_f._handle_callback.call_args[0][1] == [0x01, 0x02, 0x1234] -@pytest.mark.asyncio async def test_set_source_routing(ezsp_f): """Test setting source routing.""" with patch.object( @@ -37,7 +34,6 @@ async def test_set_source_routing(ezsp_f): assert src_mock.await_count == 1 -@pytest.mark.asyncio async def test_pre_permit(ezsp_f): """Test pre permit.""" p1 = patch.object(ezsp_f, "setPolicy", new=AsyncMock()) diff --git a/tests/test_multicast.py b/tests/test_multicast.py index 5ae884c1..47d5a6ee 100644 --- a/tests/test_multicast.py +++ b/tests/test_multicast.py @@ -7,8 +7,6 @@ from .async_mock import AsyncMock, MagicMock, sentinel -pytestmark = pytest.mark.asyncio - CUSTOM_SIZE = 12 @@ -25,7 +23,6 @@ def multicast(ezsp_f): return m -@pytest.mark.asyncio async def test_initialize(multicast): group_id = 0x0200 mct = active_multicasts = 4 @@ -49,7 +46,6 @@ async def mock_get(*args): assert len(multicast._available) == CUSTOM_SIZE - active_multicasts -@pytest.mark.asyncio async def test_initialize_fail_configured_size(multicast): multicast._ezsp.getConfigurationValue.return_value = t.EmberStatus.ERR_FATAL, 16 @@ -59,7 +55,6 @@ async def test_initialize_fail_configured_size(multicast): assert len(multicast._available) == 0 -@pytest.mark.asyncio async def test_initialize_fail(multicast): group_id = 0x0200 @@ -79,7 +74,6 @@ async def mock_get(*args): assert len(multicast._available) == 0 -@pytest.mark.asyncio async def test_startup(multicast): coordinator = MagicMock() @@ -107,7 +101,6 @@ async def mock_set(*args): return multicast.subscribe(group_id) -@pytest.mark.asyncio async def test_subscribe(multicast): grp_id = 0x0200 multicast._available.add(1) @@ -128,7 +121,6 @@ async def test_subscribe(multicast): assert grp_id in multicast._multicast -@pytest.mark.asyncio async def test_subscribe_fail(multicast): grp_id = 0x0200 multicast._available.add(1) @@ -142,7 +134,6 @@ async def test_subscribe_fail(multicast): assert len(multicast._available) == 1 -@pytest.mark.asyncio async def test_subscribe_no_avail(multicast): grp_id = 0x0200 @@ -161,7 +152,6 @@ async def mock_set(*args): return multicast.unsubscribe(group_id) -@pytest.mark.asyncio async def test_unsubscribe(multicast): grp_id = 0x0200 multicast._available.add(1) @@ -185,7 +175,6 @@ async def test_unsubscribe(multicast): assert len(multicast._available) == 1 -@pytest.mark.asyncio async def test_unsubscribe_fail(multicast): grp_id = 0x0200 multicast._available.add(1) diff --git a/tests/test_thread.py b/tests/test_thread.py index fa51cfee..561c7cc1 100644 --- a/tests/test_thread.py +++ b/tests/test_thread.py @@ -7,8 +7,6 @@ from bellows.thread import EventLoopThread, ThreadsafeProxy -pytestmark = pytest.mark.asyncio - async def test_thread_start(monkeypatch): current_loop = asyncio.get_event_loop() diff --git a/tests/test_types.py b/tests/test_types.py index e94785ab..ac2533af 100644 --- a/tests/test_types.py +++ b/tests/test_types.py @@ -1,5 +1,4 @@ import pytest -import zigpy.types as zt import zigpy.zdo.types as zdo_t import bellows.types as t @@ -222,26 +221,3 @@ def test_ember_node_type_to_zdo_logical_type(node_type, logical_type): node_type = t.EmberNodeType(node_type) assert node_type.zdo_logical_type == zdo_t.LogicalType(logical_type) - - -def test_ember_network_params_to_network_info(): - """Coverage for Ember Network param to zigpy network information converter.""" - - network_params = t.EmberNetworkParameters( - t.ExtendedPanId.convert("ff:7b:aa:bb:cc:dd:ee:ff"), - panId=0xD539, - radioTxPower=8, - radioChannel=20, - joinMethod=t.EmberJoinMethod.USE_MAC_ASSOCIATION, - nwkManagerId=0x1234, - nwkUpdateId=22, - channels=t.Channels.ALL_CHANNELS, - ) - network_info = network_params.zigpy_network_information - assert network_info.extended_pan_id == t.ExtendedPanId.convert( - "ff:7b:aa:bb:cc:dd:ee:ff" - ) - assert network_info.pan_id == zt.PanId(0xD539) - assert network_info.nwk_update_id == 22 - assert network_info.nwk_manager_id == zt.NWK(0x1234) - assert network_info.channel == 20 diff --git a/tests/test_uart.py b/tests/test_uart.py index 25d0c639..7d27ccf4 100644 --- a/tests/test_uart.py +++ b/tests/test_uart.py @@ -9,10 +9,9 @@ from .async_mock import AsyncMock, MagicMock, sentinel -pytestmark = pytest.mark.asyncio - -async def test_connect(monkeypatch): +@pytest.mark.parametrize("flow_control", [conf.CONF_FLOW_CONTROL_DEFAULT, "hardware"]) +async def test_connect(flow_control, monkeypatch): appmock = MagicMock() transport = MagicMock() @@ -24,7 +23,11 @@ async def mockconnect(loop, protocol_factory, **kwargs): monkeypatch.setattr(serial_asyncio, "create_serial_connection", mockconnect) gw = await uart.connect( conf.SCHEMA_DEVICE( - {conf.CONF_DEVICE_PATH: "/dev/serial", conf.CONF_DEVICE_BAUDRATE: 115200} + { + conf.CONF_DEVICE_PATH: "/dev/serial", + conf.CONF_DEVICE_BAUDRATE: 115200, + conf.CONF_FLOW_CONTROL: flow_control, + } ), appmock, use_thread=False, diff --git a/tests/test_util.py b/tests/test_util.py new file mode 100644 index 00000000..725b505f --- /dev/null +++ b/tests/test_util.py @@ -0,0 +1,145 @@ +import logging + +import pytest +import zigpy.state +import zigpy.types as t +import zigpy.zdo.types as zdo_t + +import bellows.types as bellows_t +import bellows.zigbee.util as util + +from tests.test_application import ezsp_mock +from tests.test_application_network_state import network_info, node_info + + +@pytest.fixture +def zigpy_key(network_info, node_info): + return network_info.network_key.replace( + rx_counter=1234567, + partner_ieee=node_info.ieee, + ) + + +@pytest.fixture +def ezsp_key(ezsp_mock, network_info, node_info, zigpy_key): + ezsp = ezsp_mock + return ezsp.types.EmberKeyStruct( + bitmask=( + ezsp.types.EmberKeyStructBitmask.KEY_HAS_SEQUENCE_NUMBER + | ezsp.types.EmberKeyStructBitmask.KEY_HAS_OUTGOING_FRAME_COUNTER + | ezsp.types.EmberKeyStructBitmask.KEY_HAS_INCOMING_FRAME_COUNTER + | ezsp.types.EmberKeyStructBitmask.KEY_HAS_PARTNER_EUI64 + ), + key=ezsp.types.EmberKeyData(network_info.network_key.key), + sequenceNumber=zigpy_key.seq, + outgoingFrameCounter=zigpy_key.tx_counter, + incomingFrameCounter=zigpy_key.rx_counter, + partnerEUI64=bellows_t.EmberEUI64(node_info.ieee), + ) + + +def test_zha_security_normal(network_info, node_info): + security = util.zha_security( + network_info=network_info, node_info=node_info, use_hashed_tclk=True + ) + + assert security.preconfiguredTrustCenterEui64 == bellows_t.EmberEUI64([0x00] * 8) + assert ( + bellows_t.EmberInitialSecurityBitmask.HAVE_TRUST_CENTER_EUI64 + not in security.bitmask + ) + + assert ( + security.preconfiguredKey.serialize().hex() + == network_info.stack_specific["ezsp"]["hashed_tclk"] + ) + assert ( + bellows_t.EmberInitialSecurityBitmask.TRUST_CENTER_USES_HASHED_LINK_KEY + in security.bitmask + ) + + +def test_zha_security_router(network_info, node_info): + security = util.zha_security( + network_info=network_info, + node_info=node_info.replace(logical_type=zdo_t.LogicalType.Router), + use_hashed_tclk=False, + ) + + assert security.preconfiguredTrustCenterEui64 == bellows_t.EmberEUI64( + network_info.tc_link_key.partner_ieee + ) + assert ( + bellows_t.EmberInitialSecurityBitmask.HAVE_TRUST_CENTER_EUI64 + in security.bitmask + ) + + assert security.preconfiguredKey == network_info.tc_link_key.key + assert ( + bellows_t.EmberInitialSecurityBitmask.TRUST_CENTER_USES_HASHED_LINK_KEY + not in security.bitmask + ) + + +def test_zha_security_router_unknown_tclk_partner_ieee(network_info, node_info): + security = util.zha_security( + network_info=network_info.replace( + tc_link_key=network_info.tc_link_key.replace(partner_ieee=t.EUI64.UNKNOWN) + ), + node_info=node_info.replace(logical_type=zdo_t.LogicalType.Router), + use_hashed_tclk=False, + ) + + # Not set, since we don't know it + assert security.preconfiguredTrustCenterEui64 == bellows_t.EmberEUI64([0x00] * 8) + assert ( + bellows_t.EmberInitialSecurityBitmask.HAVE_TRUST_CENTER_EUI64 + not in security.bitmask + ) + + +def test_zha_security_replace_missing_tc_partner_addr(network_info, node_info): + security = util.zha_security( + network_info=network_info.replace( + tc_link_key=network_info.tc_link_key.replace(partner_ieee=t.EUI64.UNKNOWN) + ), + node_info=node_info, + use_hashed_tclk=True, + ) + + assert node_info.ieee != t.EUI64.UNKNOWN + assert security.preconfiguredTrustCenterEui64 == bellows_t.EmberEUI64([0x00] * 8) + + +def test_zha_security_hashed_nonstandard_tclk_warning(network_info, node_info, caplog): + # Nothing should be logged normally + with caplog.at_level(logging.WARNING): + util.zha_security( + network_info=network_info, + node_info=node_info, + use_hashed_tclk=True, + ) + + assert "Only the well-known TC Link Key is supported" not in caplog.text + + # But it will be when a non-standard TCLK is used along with TCLK hashing + with caplog.at_level(logging.WARNING): + util.zha_security( + network_info=network_info.replace( + tc_link_key=network_info.tc_link_key.replace( + key=t.KeyData(b"ANonstandardTCLK") + ) + ), + node_info=node_info, + use_hashed_tclk=True, + ) + + assert "Only the well-known TC Link Key is supported" in caplog.text + + +def test_ezsp_key_to_zigpy_key(zigpy_key, ezsp_key, ezsp_mock): + return util.ezsp_key_to_zigpy_key(ezsp_key, ezsp_mock) == zigpy_key + + +def test_zigpy_key_to_ezsp_key(zigpy_key, ezsp_key, ezsp_mock): + return util.zigpy_key_to_ezsp_key(zigpy_key, ezsp_mock) == ezsp_key From bf9439e89decc3aef6f7bd4925d01d6c79dd01d7 Mon Sep 17 00:00:00 2001 From: puddly <32534428+puddly@users.noreply.github.com> Date: Mon, 27 Jun 2022 15:07:27 -0400 Subject: [PATCH 4/4] 0.31.0 version bump --- bellows/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bellows/__init__.py b/bellows/__init__.py index 9e048e2e..0d2322ef 100644 --- a/bellows/__init__.py +++ b/bellows/__init__.py @@ -1,5 +1,5 @@ MAJOR_VERSION = 0 MINOR_VERSION = 31 -PATCH_VERSION = "0.dev0" +PATCH_VERSION = "0" __short_version__ = f"{MAJOR_VERSION}.{MINOR_VERSION}" __version__ = f"{__short_version__}.{PATCH_VERSION}"