async def _async_group_binding_operation(self, group_id, operation, cluster_bindings): """Create or remove a direct zigbee binding between a device and a group.""" zdo = self._zigpy_device.zdo op_msg = "0x%04x: %s %s, ep: %s, cluster: %s to group: 0x%04x" destination_address = zdo_types.MultiAddress() destination_address.addrmode = types.uint8_t(1) destination_address.nwk = types.uint16_t(group_id) tasks = [] for cluster_binding in cluster_bindings: if cluster_binding.endpoint_id == 0: continue if (cluster_binding.id in self._zigpy_device.endpoints[ cluster_binding.endpoint_id].out_clusters): op_params = ( self.nwk, operation.name, str(self.ieee), cluster_binding.endpoint_id, cluster_binding.id, group_id, ) zdo.debug(f"processing {op_msg}", *op_params) tasks.append(( zdo.request( operation, self.ieee, cluster_binding.endpoint_id, cluster_binding.id, destination_address, ), op_msg, op_params, )) res = await asyncio.gather(*(t[0] for t in tasks), return_exceptions=True) for outcome, log_msg in zip(res, tasks): if isinstance(outcome, Exception): fmt = f"{log_msg[1]} failed: %s" else: fmt = f"{log_msg[1]} completed: %s" zdo.debug(fmt, *(log_msg[2] + (outcome, )))
def test_security_iaswd_squawk_strobe(): """Test strobe of squawk command class of IasWD cluster.""" for strobe in sec.IasWd.Squawk.Strobe: for mode in range(16): for level in range(4): raw = mode << 4 | level raw |= strobe.value << 3 data = types.uint8_t(raw).serialize() squawk, _ = sec.IasWd.Squawk.deserialize(data) assert squawk.serialize() == data assert squawk == raw assert squawk.strobe == strobe.value assert squawk.strobe == strobe assert squawk.strobe.name == strobe.name squawk.strobe = strobe assert squawk.serialize() == data assert squawk.strobe == strobe
def serialize(self, *, _only_dir_and_attrid: bool = False) -> bytes: r = ReportingDirection(self.direction).serialize() r += t.uint16_t(self.attrid).serialize() if _only_dir_and_attrid: return r if self.direction == ReportingDirection.ReceiveReports: r += t.uint16_t(self.timeout).serialize() else: r += t.uint8_t(self.datatype).serialize() r += t.uint16_t(self.min_interval).serialize() r += t.uint16_t(self.max_interval).serialize() datatype = DATA_TYPES.get(self.datatype, None) if datatype and datatype[2] is Analog: datatype = datatype[1] r += datatype(self.reportable_change).serialize() return r
def test_security_iaswd_warning_mode_2(): """Test warning command class of IasWD cluster.""" def _test(data, raw, mode, name): warning, _ = sec.IasWd.Warning.deserialize(data) assert warning.serialize() == data assert warning == raw assert warning.mode == mode assert warning.mode.name == name warning.mode = mode assert warning.serialize() == data assert warning.mode == mode for mode in sec.IasWd.Warning.WarningMode: for other in range(16): raw = mode << 4 | other data = types.uint8_t(raw).serialize() _test(data, raw, mode.value, mode.name)
def test_typevalue(): tv = foundation.TypeValue() tv.type = 0x20 tv.value = t.uint8_t(99) ser = tv.serialize() r = repr(tv) assert r.startswith("<") and r.endswith(">") assert "type=uint8_t" in r assert "value=99" in r tv2, data = foundation.TypeValue.deserialize(ser) assert data == b"" assert tv2.type == tv.type assert tv2.value == tv.value tv3 = foundation.TypeValue(tv2) assert tv3.type == tv.type assert tv3.value == tv.value
def aes_mmo_hash(data): result_len = 0 remaining_length = 0 length = len(data) result = bytearray([0] * AES.block_size) temp = bytearray([0] * AES.block_size) if (data and length > 0): remaining_length = length & (AES.block_size - 1) if (length >= AES.block_size): # Mask out the lower byte since hash update will hash # everything except the last piece, if the last piece # is less than 16 bytes. hashed_length = (length & ~(AES.block_size - 1)) (result_len, result) = aes_mmo_hash_update(result_len, result, data) data = data[hashed_length:] for i in range(remaining_length): temp[i] = data[i] # Per the spec, Concatenate a 1 bit followed by all zero bits # (previous memset() on temp[] set the rest of the bits to zero) temp[remaining_length] = 0x80 result_len += remaining_length # If appending the bit string will push us beyond the 16-byte boundary # we must hash that block and append another 16-byte block. if ((AES.block_size - remaining_length) < 3): (result_len, result) = aes_mmo_hash_update(result_len, result, temp) # Since this extra data is due to the concatenation, # we remove that length. We want the length of data only # and not the padding. result_len -= AES.block_size temp = bytearray([0] * AES.block_size) bit_size = result_len * 8 temp[AES.block_size - 2] = (bit_size >> 8) & 0xFF temp[AES.block_size - 1] = (bit_size) & 0xFF (result_len, result) = aes_mmo_hash_update(result_len, result, temp) return t.KeyData([t.uint8_t(c) for c in result])
async def bind_group(app, listener, ieee, cmd, data, service): from zigpy.zdo.types import MultiAddress from zigpy import types as t LOGGER.debug("running 'bind group' command: %s", service) if ieee is None: LOGGER.error("missing ieee") return src_dev = app.get_device(ieee=ieee) if not data: LOGGER.error("missing cmd_data") return group_id = int(data, base=16) zdo = src_dev.zdo src_cls = [6, 8, 768] # find src ep_id dst_addr = MultiAddress() dst_addr.addrmode = t.uint8_t(1) dst_addr.nwk = t.uint16_t(group_id) for src_cluster in src_cls: src_epid = None for ep_id, ep in src_dev.endpoints.items(): if ep_id == 0: continue if src_cluster in ep.out_clusters: src_epid = ep_id break if not src_epid: LOGGER.debug("0x%04x: skipping %s cluster as non present", src_dev.nwk, src_cluster) continue LOGGER.debug( "0x%04x: binding %s, ep: %s, cluster: %s", src_dev.nwk, str(src_dev.ieee), src_epid, src_cluster, ) res = await zdo.request(ZDOCmd.Bind_req, src_dev.ieee, src_epid, src_cluster, dst_addr) LOGGER.debug("0x%04x: binding group 0x%04x: %s", src_dev.nwk, group_id, res)
def test_security_iaswd_warning_mode(raw, mode, name): """Test warning command class of IasWD cluster.""" def _test(warning, data): assert warning.serialize() == data assert warning == raw assert warning.mode == mode assert warning.mode.name == name warning.mode = mode assert warning.serialize() == data assert warning.mode == mode data = types.uint8_t(raw).serialize() _test(sec.IasWd.Warning(raw), data) extra = b"The rest of the owl\xaa\x55" warn, rest = sec.IasWd.Warning.deserialize(data + extra) assert rest == extra _test(warn, data) repr(warn)
def test_security_iaswd_squawk_mode(raw, mode, name): """Test squawk command class of IasWD cluster.""" def _test(squawk, data): assert squawk.serialize() == data assert squawk == raw assert squawk.mode == mode assert squawk.mode.name == name squawk.mode = mode assert squawk.serialize() == data assert squawk.mode == mode data = types.uint8_t(raw).serialize() _test(sec.IasWd.Squawk(raw), data) extra = b"The rest of the owl\xaa\x55" squawk, rest = sec.IasWd.Squawk.deserialize(data + extra) assert rest == extra _test(squawk, data) repr(squawk)
def _custom_endpoint_init(self, node_config, *argv): """set node_config based obn Lumi device_type.""" config = {} selector = node_config.get('template', None) if not selector: selector = argv[0] _LOGGER.debug(" selector: %s", selector) config = { "config_report": [ [0xfc02, 0x0010, 1, 1800, t.uint8_t(1), 0x1241], [0xfc02, 0x0012, 1, 1800, t.uint16_t(1), 0x1241], [0xfc02, 0x0013, 1, 1800, t.uint16_t(1), 0x1241], [0xfc02, 0x0014, 1, 1800, t.uint16_t(1), 0x1241], ], "in_cluster": [0x0000, 0x0402, 0x0500, 0xfc02], "out_cluster": [], "type": "binary_sensor", } node_config.update(config)
def write_attributes(self, attributes, manufacturer=None): args = [] for attrid, value in attributes.items(): if isinstance(attrid, str): attrid = self._attridx[attrid] if attrid not in self.attributes: self.error("%d is not a valid attribute id", attrid) continue a = foundation.Attribute(attrid, foundation.TypeValue()) try: python_type = self.attributes[attrid][1] a.value.type = t.uint8_t(foundation.DATA_TYPE_IDX[python_type]) a.value.value = python_type(value) args.append(a) except ValueError as e: self.error(str(e)) return self._write_attributes(args, manufacturer=manufacturer)
def test_zdo_header(): tsn = t.uint8_t(0xAA) cmd_id = 0x55 data = tsn.serialize() extra = b"abcdefExtraDataHere" hdr, rest = types.ZDOHeader.deserialize(cmd_id, data + extra) assert rest == extra assert hdr.tsn == tsn assert hdr.command_id == cmd_id assert hdr.is_reply is False hdr.command_id = types.ZDOCmd.Bind_rsp assert hdr.is_reply is True assert hdr.serialize() == data new_tsn = 0xBB hdr.tsn = new_tsn assert isinstance(hdr.tsn, t.uint8_t) assert hdr.tsn == new_tsn
def write_attributes(self, attributes, is_report=False, manufacturer=None): args = [] for attrid, value in attributes.items(): if isinstance(attrid, str): attrid = self._attridx[attrid] if attrid not in self.attributes: self.error("%d is not a valid attribute id", attrid) continue if is_report: a = foundation.ReadAttributeRecord() a.status = 0 else: a = foundation.Attribute() a.attrid = t.uint16_t(attrid) a.value = foundation.TypeValue() try: python_type = self.attributes[attrid][1] a.value.type = t.uint8_t(foundation.DATA_TYPE_IDX[python_type]) a.value.value = python_type(value) args.append(a) except ValueError as e: self.error(str(e)) if is_report: schema = foundation.COMMANDS[0x01][1] return self.reply(True, 0x01, schema, args, manufacturer=manufacturer) else: schema = foundation.COMMANDS[0x02][1] return self.request(True, 0x02, schema, args, manufacturer=manufacturer)
async def set_channel(app, listener, ieee, cmd, data, service, params, event_data): ch = t.uint8_t(data) assert 11 << ch << 26 ch_mask = zigpy.types.Channels(1 << ch) LOGGER.info("Setting EZSP channel to: %s/%s", ch, ch_mask) aps_frame = bellows.types.EmberApsFrame( profileId=0x0000, clusterId=zigpy.zdo.types.ZDOCmd.Mgmt_NWK_Update_req, sourceEndpoint=0x00, destinationEndpoint=0x00, options=bellows.types.EmberApsOption.APS_OPTION_NONE, groupId=0x0000, sequence=0xDE, ) status, _, network_params = await app._ezsp.getNetworkParameters() if status != bellows.types.EmberStatus.SUCCESS: msg = "Couldn't get network parameters, abort channel change: %s" % ( status) event_data["errors"].append(msg) LOGGER.error(msg) return payload = b"\xDE" + ch_mask.serialize() + b"\xFE" payload += network_params.nwkUpdateId.serialize() status, _ = await app._ezsp.sendBroadcast( zigpy.types.BroadcastAddress.ALL_DEVICES, aps_frame, 0x00, 0x01, payload, ) assert status == bellows.types.EmberStatus.SUCCESS res = await app._ezsp.setRadioChannel(ch) LOGGER.info("Set channel status: %s", res)
def handle_match_desc_req( self, hdr: types.ZDOHeader, addr: t.NWK, profile: int, in_clusters: list, out_cluster: list, dst_addressing: t.Addressing.Group | t.Addressing.IEEE | t.Addressing.NWK | None = None, ): """Handle ZDO Match_desc_req request.""" local_addr = self._device.application.nwk if profile != zigpy.profiles.zha.PROFILE_ID: self.create_catching_task( self.Match_Desc_rsp(0, local_addr, [], tsn=hdr.tsn)) return self.create_catching_task( self.Match_Desc_rsp(0, local_addr, [t.uint8_t(1)], tsn=hdr.tsn))
def read_attributes_rsp(self, attributes, manufacturer=None): args = [] for attrid, value in attributes.items(): if isinstance(attrid, str): attrid = self._attridx[attrid] a = foundation.ReadAttributeRecord( attrid, foundation.Status.UNSUPPORTED_ATTRIBUTE, foundation.TypeValue() ) args.append(a) if value is None: continue try: a.status = foundation.Status.SUCCESS python_type = self.attributes[attrid][1] a.value.type = t.uint8_t(foundation.DATA_TYPE_IDX[python_type]) a.value.value = python_type(value) except ValueError as e: a.status = foundation.Status.UNSUPPORTED_ATTRIBUTE self.error(str(e)) return self._read_attributes_rsp(args, manufacturer=manufacturer)
def test_typevalue(): tv = foundation.TypeValue() tv.type = 0x20 tv.value = t.uint8_t(99) ser = tv.serialize() r = repr(tv) assert r.startswith("TypeValue(") and r.endswith(")") assert "type=uint8_t" in r assert "value=99" in r tv2, data = foundation.TypeValue.deserialize(ser) assert data == b"" assert tv2.type == tv.type assert tv2.value == tv.value tv3 = foundation.TypeValue(tv2) assert tv3.type == tv.type assert tv3.value == tv.value tv4 = foundation.TypeValue() tv4.type = 0x42 tv4.value = t.CharacterString("test") assert "CharacterString" in str(tv4) assert "'test'" in str(tv4)
async def mockrequest(foundation, command, schema, args, manufacturer=None, **kwargs): assert foundation is True assert command == 0 result = [] for attr_id, value in zip(args, attributes[tuple(args)]): if isinstance(value, BaseException): raise value elif value is None: rar = _mk_rar(attr_id, None, status=1) else: raw_attr_value = t.uint8_t(len(value)).serialize() + value rar = _mk_rar(attr_id, t.CharacterString.deserialize(raw_attr_value)[0]) result.append(rar) return [result]
async def bind_ieee(app, listener, ieee, cmd, data, service, params, event_data): from zigpy import types as t from zigpy.zdo.types import MultiAddress if ieee is None or not data: LOGGER.error("missing ieee") return LOGGER.debug("running 'bind ieee' command: %s", service) src_dev = app.get_device(ieee=ieee) dst_dev = await u.get_device(app, listener, data) zdo = src_dev.zdo src_out_clusters = [ 0x0006, # OnOff 0x0008, # Level 0x0300, # Color Control ] src_in_clusters = [ 0x0402, # Temperature ] # TODO: Filter according to params[p.CLUSTER_ID] results = {} for src_out_cluster in src_out_clusters: src_endpoints = [ ep_id for ep_id, ep in src_dev.endpoints.items() if ep_id != 0 and src_out_cluster in ep.out_clusters ] LOGGER.debug( "0x%04x: got the %s endpoints for %s cluster", src_dev.nwk, src_endpoints, src_out_cluster, ) if not src_endpoints: LOGGER.debug( "0x%04x: skipping %0x04X cluster as non present", src_dev.nwk, src_out_cluster, ) continue dst_addr = MultiAddress() dst_addr.addrmode = t.uint8_t(3) dst_addr.ieee = dst_dev.ieee # find dest ep dst_epid = None for ep_id, ep in dst_dev.endpoints.items(): if ep_id == 0: continue if src_out_cluster in ep.in_clusters: dst_epid = ep_id break if not dst_epid: continue dst_addr.endpoint = t.uint8_t(dst_epid) for src_ep in src_endpoints: LOGGER.debug( "0x%04x: binding %s, ep: %s, cluster: 0x%04X to %s dev %s ep", src_dev.nwk, str(src_dev.ieee), src_ep, src_out_cluster, str(dst_dev.ieee), dst_epid, ) res = await zdo.request( ZDOCmd.Bind_req, src_dev.ieee, src_ep, src_out_cluster, dst_addr, ) LOGGER.debug( "0x%04x: binding ieee %s: %s", src_dev.nwk, str(dst_dev.ieee), res, ) for src_in_cluster in src_in_clusters: src_endpoints = [ ep_id for ep_id, ep in src_dev.endpoints.items() if ep_id != 0 and src_in_cluster in ep.in_clusters ] LOGGER.debug( "0x%04x: got the %s endpoints for %s cluster", src_dev.nwk, src_endpoints, src_in_cluster, ) if not src_endpoints: LOGGER.debug( "0x%04x: skipping %0x04X cluster as non present", src_dev.nwk, src_in_cluster, ) continue dst_addr = MultiAddress() dst_addr.addrmode = t.uint8_t(3) dst_addr.ieee = dst_dev.ieee # find dest ep dst_epid = None for ep_id, ep in dst_dev.endpoints.items(): if ep_id == 0: continue if src_in_cluster in ep.out_clusters: dst_epid = ep_id break if not dst_epid: continue dst_addr.endpoint = t.uint8_t(dst_epid) for src_ep in src_endpoints: LOGGER.debug( "0x%04x: binding %s, ep: %s, cluster: 0x%04X to %s dev %s ep", src_dev.nwk, str(src_dev.ieee), src_ep, src_in_cluster, str(dst_dev.ieee), dst_epid, ) if src_ep not in results: results[src_ep] = [] bind_result = { "src_endpoint_id": src_ep, "dst_endpoint_id": dst_epid, "cluster_id": src_in_cluster, } res = await zdo.request(ZDOCmd.Bind_req, src_dev.ieee, src_ep, src_in_cluster, dst_addr) bind_result["result"] = res results[src_ep] = bind_result LOGGER.debug( "0x%04x: binding ieee %s: %s", src_dev.nwk, str(dst_dev.ieee), res, ) event_data["result"] = results
def tsn(self, value: t.uint8_t) -> None: """Setter for tsn.""" self._tsn = t.uint8_t(value)
def handle_active_ep_req(self, addr): # reply with 1 endpoint just in case someone asks (e.g. deCONZ seems to query over and over again until it gets a reply) self.reply(0x8005, 0, self._device.application.nwk, [t.uint8_t(1)])
def convert_ieee(ieee_str): """Convert given ieee string to EUI64.""" from zigpy.types import EUI64, uint8_t return EUI64([uint8_t(p, base=16) for p in ieee_str.split(':')])
def serialize(self) -> bytes: return t.uint8_t(self.value).serialize()
def serialize(self): r = Status(self.status).serialize() if self.status != Status.SUCCESS: r += t.uint8_t(self.direction).serialize() r += t.uint16_t(self.attrid).serialize() return r
def convert_ieee(s): ieee = [t.uint8_t(p, base=16) for p in s.split(b':')] return t.EUI64(ieee)
def __init__(self, value: int = 0) -> None: self.value = t.uint8_t(value)
def tsn(self, value: t.uint8_t) -> None: """Set TSN.""" self._tsn = t.uint8_t(value)
def __init__(self, command_id: t.uint16_t = 0x0000, tsn: t.uint8_t = 0) -> None: self._command_id = ZDOCmd(command_id) self._tsn = t.uint8_t(tsn)
def handle_match_desc(self, addr, profile, in_clusters, out_clusters): local_addr = self._device.application.nwk if profile != 260: return self.Match_Desc_rsp(0, local_addr, []) return self.Match_Desc_rsp(0, local_addr, [t.uint8_t(1)])
async def bind_ieee(app, listener, ieee, cmd, data, service): from zigpy import types as t from zigpy.zdo.types import MultiAddress if ieee is None or not data: LOGGER.error("missing ieee") return LOGGER.debug("running 'bind ieee' command: %s", service) src_dev = app.get_device(ieee=ieee) dst_ieee = t.EUI64([t.uint8_t(p, base=16) for p in data.split(":")]) dst_dev = app.get_device(ieee=dst_ieee) zdo = src_dev.zdo src_clusters = [6, 8, 768] for src_cluster in src_clusters: src_endpoints = [ ep_id for ep_id, ep in src_dev.endpoints.items() if ep_id != 0 and src_cluster in ep.out_clusters ] LOGGER.debug( "0x%04x: got the %s endpoints for %s cluster", src_dev.nwk, src_endpoints, src_cluster, ) if not src_endpoints: LOGGER.debug("0x%04x: skipping %s cluster as non present", src_dev.nwk, src_cluster) continue dst_addr = MultiAddress() dst_addr.addrmode = t.uint8_t(3) dst_addr.ieee = dst_dev.ieee # find dest ep dst_epid = None for ep_id, ep in dst_dev.endpoints.items(): if ep_id == 0: continue if src_cluster in ep.in_clusters: dst_epid = ep_id break if not dst_epid: continue dst_addr.endpoint = t.uint8_t(dst_epid) for src_ep in src_endpoints: LOGGER.debug( "0x%04x: binding %s, ep: %s, cluster: %s to %s dev %s ep", src_dev.nwk, str(src_dev.ieee), src_ep, src_cluster, str(dst_dev.ieee), dst_epid, ) res = await zdo.request(ZDOCmd.Bind_req, src_dev.ieee, src_ep, src_cluster, dst_addr) LOGGER.debug("0x%04x: binding ieee %s: %s", src_dev.nwk, str(dst_dev.ieee), res)
async def set_channel(app, listener, ieee, cmd, data, service): ch = t.uint8_t(data) LOGGER.info("Setting EZSP channel to: %s", ch) res = await app._ezsp.setRadioChannel(ch) LOGGER.info("Writing attrs status: %s", res)