diff --git a/.gitignore b/.gitignore index fbc73c23..00e45661 100644 --- a/.gitignore +++ b/.gitignore @@ -41,10 +41,9 @@ htmlcov/ .coverage .coverage.* .cache -nosetests.xml coverage.xml *,cover -.hypothesis/ +.pytest_cache/ # Translations *.mo diff --git a/README.md b/README.md index bb80a660..0225af0d 100644 --- a/README.md +++ b/README.md @@ -3,8 +3,8 @@ [![Build Status](https://travis-ci.org/rcloran/bellows.svg?branch=master)](https://travis-ci.org/rcloran/bellows) [![Coverage](https://coveralls.io/repos/github/rcloran/bellows/badge.svg?branch=master)](https://coveralls.io/github/rcloran/bellows?branch=master) -`bellows` is a Python 3 project to implement ZigBee support for EmberZNet -devices using the EZSP protocol. +`bellows` is a Python 3 project to implement support for EmberZNet devices +using the EZSP protocol. The goal is to use this project to add support for the ZigBee Network Coprocessor (NCP) in devices like the [Linear/Nortek/GoControl HubZ/QuickStick diff --git a/bellows/cli/application.py b/bellows/cli/application.py index 10887f3b..f65b6f33 100644 --- a/bellows/cli/application.py +++ b/bellows/cli/application.py @@ -7,7 +7,7 @@ import bellows.ezsp import bellows.zigbee.application -import bellows.zigbee.endpoint +import zigpy.endpoint from . import opts from . import util from .main import main @@ -100,7 +100,7 @@ def print_clusters(title, clusters): for epid, ep in dev.endpoints.items(): if epid == 0: continue - if ep.status == bellows.zigbee.endpoint.Status.NEW: + if ep.status == zigpy.endpoint.Status.NEW: click.echo(" %s: Uninitialized") else: click.echo( diff --git a/bellows/types/basic.py b/bellows/types/basic.py index 0d41a896..ca190c61 100644 --- a/bellows/types/basic.py +++ b/bellows/types/basic.py @@ -1,6 +1,3 @@ -import struct - - class int_t(int): # noqa: N801 _signed = True @@ -83,24 +80,6 @@ class uint64_t(uint_t): # noqa: N801 _size = 8 -class Single(float): - def serialize(self): - return struct.pack(' 0x%04x)", ieee, dev.nwk, nwk) - dev.nwk = nwk - elif dev.initializing or dev.status == bellows.zigbee.device.Status.ENDPOINTS_INIT: - LOGGER.debug("Skip initialization for existing device %s", ieee) - return - else: - dev = self.add_device(ieee, nwk) - - self.listener_event('device_joined', dev) - dev.schedule_initialize() - - def _handle_leave(self, nwk, ieee, *args): - LOGGER.info("Device 0x%04x (%s) left the network", nwk, ieee) - dev = self.devices.get(ieee, None) - if dev is not None: - self.listener_event('device_left', dev) + self.handle_message(True, sender, aps_frame.profileId, aps_frame.clusterId, aps_frame.sourceEndpoint, aps_frame.destinationEndpoint, tsn, command_id, args) def _handle_frame_failure(self, message_type, destination, aps_frame, message_tag, status, message): try: @@ -273,20 +212,31 @@ def _handle_frame_sent(self, message_type, destination, aps_frame, message_tag, except asyncio.futures.InvalidStateError as exc: LOGGER.debug("Invalid state on future - probably duplicate response: %s", exc) - @bellows.zigbee.util.retryable_request + @zigpy.util.retryable_request @asyncio.coroutine - def request(self, nwk, aps_frame, data, expect_reply=True, timeout=10): - seq = aps_frame.sequence - assert seq not in self._pending + def request(self, nwk, profile, cluster, src_ep, dst_ep, sequence, data, expect_reply=True, timeout=10): + assert sequence not in self._pending send_fut = asyncio.Future() reply_fut = None if expect_reply: reply_fut = asyncio.Future() - self._pending[seq] = (send_fut, reply_fut) + self._pending[sequence] = (send_fut, reply_fut) + + aps_frame = t.EmberApsFrame() + aps_frame.profileId = t.uint16_t(profile) + aps_frame.clusterId = t.uint16_t(cluster) + aps_frame.sourceEndpoint = t.uint8_t(src_ep) + aps_frame.destinationEndpoint = t.uint8_t(dst_ep) + aps_frame.options = t.EmberApsOption( + t.EmberApsOption.APS_OPTION_RETRY | + t.EmberApsOption.APS_OPTION_ENABLE_ROUTE_DISCOVERY + ) + aps_frame.groupId = t.uint16_t(0) + aps_frame.sequence = t.uint8_t(sequence) - v = yield from self._ezsp.sendUnicast(self.direct, nwk, aps_frame, seq, data) + v = yield from self._ezsp.sendUnicast(self.direct, nwk, aps_frame, sequence, data) if v[0] != 0: - self._pending.pop(seq) + self._pending.pop(sequence) send_fut.cancel() if expect_reply: reply_fut.cancel() @@ -307,7 +257,7 @@ def permit_with_key(self, node, code, time_s=60): if type(node) is not t.EmberEUI64: node = t.EmberEUI64([t.uint8_t(p) for p in node]) - key = bellows.zigbee.util.convert_install_code(code) + key = zigpy.util.convert_install_code(code) if key is None: raise Exception("Invalid install code") @@ -323,26 +273,3 @@ def permit_with_key(self, node, code, time_s=60): raise Exception("Failed to change policy to allow generation of new trust center keys") return self._ezsp.permitJoining(time_s, True) - - def get_sequence(self): - self._send_sequence = (self._send_sequence + 1) % 256 - return self._send_sequence - - def get_device(self, ieee=None, nwk=None): - if ieee is not None: - return self.devices[ieee] - - for dev in self.devices.values(): - # TODO: Make this not terrible - if dev.nwk == nwk: - return dev - - raise KeyError - - @property - def ieee(self): - return self._ieee - - @property - def nwk(self): - return self._nwk diff --git a/bellows/zigbee/device.py b/bellows/zigbee/device.py deleted file mode 100644 index e371ce72..00000000 --- a/bellows/zigbee/device.py +++ /dev/null @@ -1,131 +0,0 @@ -import asyncio -import enum -import logging - -import bellows.types as t -import bellows.zigbee.endpoint -import bellows.zigbee.util as zutil -import bellows.zigbee.zdo as zdo - - -LOGGER = logging.getLogger(__name__) - - -class Status(enum.IntEnum): - """The status of a Device""" - # No initialization done - NEW = 0 - # ZDO endpoint discovery done - ZDO_INIT = 1 - # Endpoints initialized - ENDPOINTS_INIT = 2 - - -class Device(zutil.LocalLogMixin): - """A device on the network""" - - def __init__(self, application, ieee, nwk): - self._application = application - self._ieee = ieee - self.nwk = nwk - self.zdo = zdo.ZDO(self) - self.endpoints = {0: self.zdo} - self.lqi = None - self.rssi = None - self.status = Status.NEW - self.initializing = False - - def schedule_initialize(self): - if self.initializing: - LOGGER.debug("Canceling old initialize call") - self._init_handle.cancel() - else: - self.initializing = True - loop = asyncio.get_event_loop() - self._init_handle = loop.call_soon(asyncio.async, self._initialize()) - - @asyncio.coroutine - def _initialize(self): - if self.status == Status.NEW: - self.info("Discovering endpoints") - try: - epr = yield from self.zdo.request(0x0005, self.nwk, tries=3, delay=2) - if epr[0] != 0: - raise Exception("Endpoint request failed: %s", epr) - except Exception as exc: - self.initializing = False - self.warn("Failed ZDO request during device initialization: %s", exc) - return - - self.info("Discovered endpoints: %s", epr[2]) - - for endpoint_id in epr[2]: - self.add_endpoint(endpoint_id) - - self.status = Status.ZDO_INIT - - for endpoint_id in self.endpoints.keys(): - if endpoint_id == 0: # ZDO - continue - yield from self.endpoints[endpoint_id].initialize() - - self.status = Status.ENDPOINTS_INIT - self.initializing = False - self._application.listener_event('device_initialized', self) - - def add_endpoint(self, endpoint_id): - ep = bellows.zigbee.endpoint.Endpoint(self, endpoint_id) - self.endpoints[endpoint_id] = ep - return ep - - def get_aps(self, profile, cluster, endpoint): - f = t.EmberApsFrame() - f.profileId = t.uint16_t(profile) - f.clusterId = t.uint16_t(cluster) - f.sourceEndpoint = t.uint8_t(endpoint) - f.destinationEndpoint = t.uint8_t(endpoint) - f.options = t.EmberApsOption( - t.EmberApsOption.APS_OPTION_RETRY | - t.EmberApsOption.APS_OPTION_ENABLE_ROUTE_DISCOVERY - ) - f.groupId = t.uint16_t(0) - f.sequence = t.uint8_t(self._application.get_sequence()) - return f - - def request(self, aps, data): - return self._application.request(self.nwk, aps, data) - - def handle_message(self, is_reply, aps_frame, tsn, command_id, args): - try: - endpoint = self.endpoints[aps_frame.destinationEndpoint] - except KeyError: - self.warn( - "Message on unknown endpoint %s", - aps_frame.destinationEndpoint, - ) - return - - return endpoint.handle_message(is_reply, aps_frame, tsn, command_id, args) - - def reply(self, aps, data): - return self._application.request(self.nwk, aps, data, False) - - def radio_details(self, lqi, rssi): - self.lqi = lqi - self.rssi = rssi - - def log(self, lvl, msg, *args): - msg = '[0x%04x] ' + msg - args = (self.nwk, ) + args - return LOGGER.log(lvl, msg, *args) - - @property - def application(self): - return self._application - - @property - def ieee(self): - return self._ieee - - def __getitem__(self, key): - return self.endpoints[key] diff --git a/bellows/zigbee/endpoint.py b/bellows/zigbee/endpoint.py deleted file mode 100644 index 56265367..00000000 --- a/bellows/zigbee/endpoint.py +++ /dev/null @@ -1,143 +0,0 @@ -import asyncio -import enum -import logging - -import bellows.zigbee.appdb -import bellows.zigbee.profiles -import bellows.zigbee.util as zutil -import bellows.zigbee.zcl - -LOGGER = logging.getLogger(__name__) - - -class Status(enum.IntEnum): - """The status of an Endpoint""" - # No initialization is done - NEW = 0 - # Endpoint information (device type, clusters, etc) init done - ZDO_INIT = 1 - - -class Endpoint(zutil.LocalLogMixin, zutil.ListenableMixin): - """An endpoint on a device on the network""" - def __init__(self, device, endpoint_id): - self._device = device - self._endpoint_id = endpoint_id - self.in_clusters = {} - self.out_clusters = {} - self._cluster_attr = {} - self.status = Status.NEW - self._listeners = {} - - @asyncio.coroutine - def initialize(self): - if self.status == Status.ZDO_INIT: - return - - self.info("Discovering endpoint information") - try: - sdr = yield from self._device.zdo.request( - 0x0004, - self._device.nwk, - self._endpoint_id, - tries=3, - delay=2, - ) - if sdr[0] != 0: - raise Exception("Failed to retrieve service descriptor: %s", sdr) - except Exception as exc: - self.warn("Failed ZDO request during device initialization: %s", exc) - return - - self.info("Discovered endpoint information: %s", sdr[2]) - sd = sdr[2] - self.profile_id = sd.profile - self.device_type = sd.device_type - try: - if self.profile_id == 260: - self.device_type = bellows.zigbee.profiles.zha.DeviceType(self.device_type) - elif self.profile_id == 49246: - self.device_type = bellows.zigbee.profiles.zll.DeviceType(self.device_type) - except ValueError: - pass - - for cluster in sd.input_clusters: - self.add_input_cluster(cluster) - for cluster in sd.output_clusters: - self.add_output_cluster(cluster) - - self.status = Status.ZDO_INIT - - def add_input_cluster(self, cluster_id): - """Adds an endpoint's input cluster - - (a server cluster supported by the device) - """ - if cluster_id in self.in_clusters: - return self.in_clusters[cluster_id] - - cluster = bellows.zigbee.zcl.Cluster.from_id(self, cluster_id) - self.in_clusters[cluster_id] = cluster - if hasattr(cluster, 'ep_attribute'): - self._cluster_attr[cluster.ep_attribute] = cluster - - listener = bellows.zigbee.appdb.ClusterPersistingListener( - self._device.application._dblistener, - cluster, - ) - cluster.add_listener(listener) - - return cluster - - def add_output_cluster(self, cluster_id): - """Adds an endpoint's output cluster - - (a client cluster supported by the device) - """ - if cluster_id in self.out_clusters: - return self.out_clusters[cluster_id] - - cluster = bellows.zigbee.zcl.Cluster.from_id(self, cluster_id) - self.out_clusters[cluster_id] = cluster - return cluster - - def get_aps(self, cluster): - assert self.status != Status.NEW - return self._device.get_aps( - profile=self.profile_id, - cluster=cluster, - endpoint=self._endpoint_id, - ) - - def handle_message(self, is_reply, aps_frame, tsn, command_id, args): - handler = None - if aps_frame.clusterId in self.in_clusters: - handler = self.in_clusters[aps_frame.clusterId].handle_message - elif aps_frame.clusterId in self.out_clusters: - handler = self.out_clusters[aps_frame.clusterId].handle_message - else: - self.warn("Message on unknown cluster 0x%04x", aps_frame.clusterId) - self.listener_event("unknown_cluster_message", is_reply, - command_id, args) - return - - handler(is_reply, aps_frame, tsn, command_id, args) - - def log(self, lvl, msg, *args): - msg = '[0x%04x:%s] ' + msg - args = (self._device.nwk, self._endpoint_id) + args - return LOGGER.log(lvl, msg, *args) - - @property - def device(self): - return self._device - - @property - def endpoint_id(self): - return self._endpoint_id - - def __getattr__(self, name): - try: - return self._cluster_attr[name] - except KeyError: - raise AttributeError diff --git a/bellows/zigbee/exceptions.py b/bellows/zigbee/exceptions.py deleted file mode 100644 index ad8b4a42..00000000 --- a/bellows/zigbee/exceptions.py +++ /dev/null @@ -1,6 +0,0 @@ -class ZigbeeException(Exception): - """Base exception class""" - - -class DeliveryError(ZigbeeException): - """Message delivery failed in some way""" diff --git a/bellows/zigbee/profiles/__init__.py b/bellows/zigbee/profiles/__init__.py deleted file mode 100644 index 42fe2811..00000000 --- a/bellows/zigbee/profiles/__init__.py +++ /dev/null @@ -1,7 +0,0 @@ -from . import zha -from . import zll - -PROFILES = { - zha.PROFILE_ID: zha, - zll.PROFILE_ID: zll, -} diff --git a/bellows/zigbee/profiles/zha.py b/bellows/zigbee/profiles/zha.py deleted file mode 100644 index 668b08f5..00000000 --- a/bellows/zigbee/profiles/zha.py +++ /dev/null @@ -1,68 +0,0 @@ -import enum - - -PROFILE_ID = 260 - - -class DeviceType(enum.IntEnum): - # Generic - ON_OFF_SWITCH = 0x0000 - LEVEL_CONTROL_SWITCH = 0x0001 - ON_OFF_OUTPUT = 0x0002 - LEVEL_CONTROLLABLE_OUTPUT = 0x0003 - SCENE_SELECTOR = 0x0004 - CONFIGURATION_TOOL = 0x0005 - REMOTE_CONTROL = 0x0006 - COMBINED_INTERFACE = 0x0007 - RANGE_EXTENDER = 0x0008 - MAIN_POWER_OUTLET = 0x0009 - DOOR_LOCK = 0x000A - DOOR_LOCK_CONTROLLER = 0x000B - SIMPLE_SENSOR = 0x000C - CONSUMPTION_AWARENESS_DEVICE = 0x000D - HOME_GATEWAY = 0x0050 - SMART_PLUG = 0x0051 - WHITE_GOODS = 0x0052 - METER_INTERFACE = 0x0053 - # Lighting - ON_OFF_LIGHT = 0x0100 - DIMMABLE_LIGHT = 0x0101 - COLOR_DIMMABLE_LIGHT = 0x0102 - ON_OFF_LIGHT_SWITCH = 0x0103 - DIMMER_SWITCH = 0x0104 - COLOR_DIMMER_SWITCH = 0x0105 - LIGHT_SENSOR = 0x0106 - OCCUPANCY_SENSOR = 0x0107 - # Closure - SHADE = 0x0200 - SHADE_CONTROLLER = 0x0201 - WINDOW_COVERING_DEVICE = 0x0202 - WINDOW_COVERING_CONTROLLER = 0x0203 - # HVAC - HEATING_COOLING_UNIT = 0x0300 - THERMOSTAT = 0x0301 - TEMPERATURE_SENSOR = 0x0302 - PUMP = 0x0303 - PUMP_CONTROLLER = 0x0304 - PRESSURE_SENSOR = 0x0305 - FLOW_SENSOR = 0x0306 - MINI_SPLIT_AC = 0x0307 - # Intruder Alarm Systems - IAS_CONTROL = 0x0400 # IAS Control and Indicating Equipment - IAS_ANCILLARY_CONTROL = 0x0401 # IAS Ancillary Control Equipment - IAS_ZONE = 0x0402 - IAS_WARNING_DEVICE = 0x0403 - - -CLUSTERS = { - # Generic - DeviceType.ON_OFF_SWITCH: ([0x0004, 0x0005, 0x0006], []), - DeviceType.SMART_PLUG: ([0x0004, 0x0005, 0x0006], []), - # Lighting - DeviceType.ON_OFF_LIGHT: ([0x0004, 0x0005, 0x0006, 0x0008], []), - DeviceType.DIMMABLE_LIGHT: ([0x0004, 0x0005, 0x0006, 0x0008], []), - DeviceType.COLOR_DIMMABLE_LIGHT: ([0x0004, 0x0005, 0x0006, 0x0008, 0x0300], []), - DeviceType.ON_OFF_LIGHT_SWITCH: ([0x0004, 0x0005, 0x0006], []), - DeviceType.DIMMER_SWITCH: ([0x0004, 0x0005, 0x0006, 0x0008], []), - DeviceType.COLOR_DIMMER_SWITCH: ([0x0004, 0x0005, 0x0006, 0x0008, 0x0300], []), -} diff --git a/bellows/zigbee/profiles/zll.py b/bellows/zigbee/profiles/zll.py deleted file mode 100644 index b31eacc7..00000000 --- a/bellows/zigbee/profiles/zll.py +++ /dev/null @@ -1,39 +0,0 @@ -import enum - - -PROFILE_ID = 49246 - - -class DeviceType(enum.IntEnum): - ON_OFF_LIGHT = 0x0000 - ON_OFF_PLUGIN_UNIT = 0x0010 - DIMMABLE_LIGHT = 0x0100 - DIMMABLE_PLUGIN_UNIT = 0x0110 - COLOR_LIGHT = 0x0200 - EXTENDED_COLOR_LIGHT = 0x0210 - COLOR_TEMPERATURE_LIGHT = 0x0220 - - COLOR_CONTROLLER = 0x0800 - COLOR_SCENE_CONTROLLER = 0x0810 - CONTROLLER = 0x0820 - SCENE_CONTROLLER = 0x0830 - CONTROL_BRIDGE = 0x0840 - ON_OFF_SENSOR = 0x0850 - - -CLUSTERS = { - DeviceType.ON_OFF_LIGHT: ([0x0004, 0x0005, 0x0006, 0x0008, 0x1000], []), - DeviceType.ON_OFF_PLUGIN_UNIT: ([0x0004, 0x0005, 0x0006, 0x0008, 0x1000], []), - DeviceType.DIMMABLE_LIGHT: ([0x0004, 0x0005, 0x0006, 0x0008, 0x1000], []), - DeviceType.DIMMABLE_PLUGIN_UNIT: ([0x0004, 0x0005, 0x0006, 0x0008, 0x1000], []), - DeviceType.COLOR_LIGHT: ([0x0004, 0x0005, 0x0006, 0x0008, 0x0300, 0x1000], []), - DeviceType.EXTENDED_COLOR_LIGHT: ([0x0004, 0x0005, 0x0006, 0x0008, 0x0300, 0x1000], []), - DeviceType.COLOR_TEMPERATURE_LIGHT: ([0x0004, 0x0005, 0x0006, 0x0008, 0x0300, 0x1000], []), - - DeviceType.COLOR_CONTROLLER: ([], [0x0004, 0x0006, 0x0008, 0x0300]), - DeviceType.COLOR_SCENE_CONTROLLER: ([], [0x0004, 0x0005, 0x0006, 0x0008, 0x0300]), - DeviceType.CONTROLLER: ([], [0x0004, 0x0006, 0x0008]), - DeviceType.SCENE_CONTROLLER: ([], [0x0004, 0x0005, 0x0006, 0x0008]), - DeviceType.CONTROL_BRIDGE: ([], [0x0004, 0x0005, 0x0006, 0x0008, 0x0300]), - DeviceType.ON_OFF_SENSOR: ([], [0x0004, 0x0005, 0x0006, 0x0008, 0x0300]), -} diff --git a/bellows/zigbee/util.py b/bellows/zigbee/util.py index d37940fa..6227901e 100644 --- a/bellows/zigbee/util.py +++ b/bellows/zigbee/util.py @@ -1,45 +1,6 @@ -import asyncio -import functools -import logging import os -from crccheck.crc import CrcX25 -from Crypto.Cipher import AES import bellows.types as t -from bellows.zigbee.exceptions import DeliveryError - -LOGGER = logging.getLogger(__name__) - - -class ListenableMixin: - def add_listener(self, listener): - id_ = id(listener) - while id_ in self._listeners: - id_ += 1 - self._listeners[id_] = listener - return id_ - - def listener_event(self, method_name, *args): - for listener in self._listeners.values(): - try: - method = getattr(listener, method_name) - method(*args) - except Exception as e: - LOGGER.warning("Error calling listener.%s: %s", method_name, e) - - -class LocalLogMixin: - def debug(self, msg, *args): - return self.log(logging.DEBUG, msg, *args) - - def info(self, msg, *args): - return self.log(logging.INFO, msg, *args) - - def warn(self, msg, *args): - return self.log(logging.WARNING, msg, *args) - - def error(self, msg, *args): - return self.log(logging.ERROR, msg, *args) def zha_security(controller=False): @@ -73,124 +34,3 @@ def zha_security(controller=False): ) isc.networkKey = random_key return isc - - -@asyncio.coroutine -def retry(func, retry_exceptions, tries=3, delay=0.1): - """Retry a function in case of exception - - Only exceptions in `retry_exceptions` will be retried. - """ - while True: - try: - r = yield from func() - return r - except retry_exceptions: - if tries <= 1: - raise - tries -= 1 - yield from asyncio.sleep(delay) - - -def retryable(retry_exceptions, tries=1, delay=0.1): - """Return a decorator which makes a function able to be retried - - This adds "tries" and "delay" keyword arguments to the function. Only - exceptions in `retry_exceptions` will be retried. - """ - def decorator(func): - nonlocal tries, delay - - @functools.wraps(func) - def wrapper(*args, tries=tries, delay=delay, **kwargs): - if tries <= 1: - return func(*args, **kwargs) - return retry( - functools.partial(func, *args, **kwargs), - retry_exceptions, - tries=tries, - delay=delay, - ) - return wrapper - return decorator - - -retryable_request = retryable((DeliveryError, asyncio.TimeoutError)) - - -def aes_mmo_hash_update(length, result, data): - while len(data) >= AES.block_size: - # Encrypt - aes = AES.new(bytes(result), AES.MODE_ECB) - result = bytearray(aes.encrypt(bytes(data[:AES.block_size]))) - - # XOR - for i in range(AES.block_size): - result[i] ^= bytes(data[:AES.block_size])[i] - - data = data[AES.block_size:] - length += AES.block_size - - return (length, result) - - -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) - - key = t.EmberKeyData() - key.contents = t.fixed_list(16, t.uint8_t)( - [t.uint8_t(c) for c in result] - ) - return key - - -def convert_install_code(code): - if len(code) < 8: - return None - - real_crc = bytes([code[-1], code[-2]]) - crc = CrcX25() - crc.process(code[:len(code) - 2]) - if real_crc != crc.finalbytes(): - return None - - return aes_mmo_hash(code) diff --git a/bellows/zigbee/zcl/__init__.py b/bellows/zigbee/zcl/__init__.py deleted file mode 100644 index 0bcabcea..00000000 --- a/bellows/zigbee/zcl/__init__.py +++ /dev/null @@ -1,335 +0,0 @@ -import asyncio -import functools -import logging - -import bellows.types as t -from bellows.zigbee import util -from bellows.zigbee.zcl import foundation - - -LOGGER = logging.getLogger(__name__) - - -def deserialize(cluster_id, data): - frame_control, data = data[0], data[1:] - frame_type = frame_control & 0b0011 - direction = (frame_control & 0b1000) >> 3 - if frame_control & 0b0100: - # Manufacturer specific value present - data = data[2:] - tsn, command_id, data = data[0], data[1], data[2:] - - is_reply = bool(direction) - - if frame_type == 1: - # Cluster command - if cluster_id not in Cluster._registry: - LOGGER.debug("Ignoring unknown cluster ID 0x%04x", - cluster_id) - return tsn, command_id + 256, is_reply, data - cluster = Cluster._registry[cluster_id] - # Cluster-specific command - - if direction: - commands = cluster.client_commands - else: - commands = cluster.server_commands - - try: - schema = commands[command_id][1] - is_reply = commands[command_id][2] - except KeyError: - LOGGER.warning("Unknown cluster-specific command %s", command_id) - return tsn, command_id + 256, is_reply, data - - # Bad hack to differentiate foundation vs cluster - command_id = command_id + 256 - else: - # General command - try: - schema = foundation.COMMANDS[command_id][1] - is_reply = foundation.COMMANDS[command_id][2] - except KeyError: - LOGGER.warning("Unknown foundation command %s", command_id) - return tsn, command_id, is_reply, data - - value, data = t.deserialize(data, schema) - if data != b'': - # TODO: Seems sane to check, but what should we do? - LOGGER.warning("Data remains after deserializing ZCL frame") - - return tsn, command_id, is_reply, value - - -class Registry(type): - def __init__(cls, name, bases, nmspc): # noqa: N805 - super(Registry, cls).__init__(name, bases, nmspc) - if hasattr(cls, 'cluster_id'): - cls._registry[cls.cluster_id] = cls - if hasattr(cls, 'cluster_id_range'): - cls._registry_range[cls.cluster_id_range] = cls - if hasattr(cls, 'attributes'): - cls._attridx = {} - for attrid, (attrname, datatype) in cls.attributes.items(): - cls._attridx[attrname] = attrid - if hasattr(cls, 'server_commands'): - cls._server_command_idx = {} - for command_id, details in cls.server_commands.items(): - command_name, schema, is_reply = details - cls._server_command_idx[command_name] = command_id - if hasattr(cls, 'client_commands'): - cls._client_command_idx = {} - for command_id, details in cls.client_commands.items(): - command_name, schema, is_reply = details - cls._client_command_idx[command_name] = command_id - - -class Cluster(util.ListenableMixin, util.LocalLogMixin, metaclass=Registry): - """A cluster on an endpoint""" - _registry = {} - _registry_range = {} - _server_command_idx = {} - _client_command_idx = {} - - def __init__(self, endpoint): - self._endpoint = endpoint - self._attr_cache = {} - self._listeners = {} - - @classmethod - def from_id(cls, endpoint, cluster_id): - if cluster_id in cls._registry: - return cls._registry[cluster_id](endpoint) - else: - for cluster_id_range, cluster in cls._registry_range.items(): - if cluster_id_range[0] <= cluster_id <= cluster_id_range[1]: - c = cluster(endpoint) - c.cluster_id = cluster_id - return c - - LOGGER.warning("Unknown cluster %s", cluster_id) - c = cls(endpoint) - c.cluster_id = cluster_id - return c - - @util.retryable_request - def request(self, general, command_id, schema, *args): - if len(schema) != len(args): - self.error("Schema and args lengths do not match in request") - error = asyncio.Future() - error.set_exception(ValueError("Wrong number of parameters for request, expected %d argument(s)" % len(schema))) - return error - - aps = self._endpoint.get_aps(self.cluster_id) - if general: - frame_control = 0x00 - else: - frame_control = 0x01 - data = bytes([frame_control, aps.sequence, command_id]) - data += t.serialize(args, schema) - - return self._endpoint.device.request(aps, data) - - def reply(self, command_id, schema, *args): - if len(schema) != len(args): - self.error("Schema and args lengths do not match in reply") - error = asyncio.Future() - error.set_exception(ValueError("Wrong number of parameters for reply, expected %d argument(s)" % len(schema))) - return error - - aps = self._endpoint.get_aps(self.cluster_id) - frame_control = 0b1001 # Cluster reply command - data = bytes([frame_control, aps.sequence, command_id]) - data += t.serialize(args, schema) - - return self._endpoint.device.reply(aps, data) - - def handle_message(self, is_reply, aps_frame, tsn, command_id, args): - if is_reply: - self.debug("Unexpected ZCL reply 0x%04x: %s", command_id, args) - return - - self.debug("ZCL request 0x%04x: %s", command_id, args) - if command_id <= 0xff: - self.listener_event('zdo_command', aps_frame, tsn, command_id, args) - else: - # Unencapsulate bad hack - command_id -= 256 - self.listener_event('cluster_command', aps_frame, tsn, command_id, args) - self.handle_cluster_request(aps_frame, tsn, command_id, args) - return - - if command_id == 0x0a: # Report attributes - valuestr = ", ".join([ - "%s=%s" % (a.attrid, a.value.value) for a in args[0] - ]) - self.debug("Attribute report received: %s", valuestr) - for attr in args[0]: - self._update_attribute(attr.attrid, attr.value.value) - else: - self.debug("No handler for general command %s", command_id) - - def handle_cluster_request(self, aps_frame, tsn, command_id, args): - self.debug("No handler for cluster command %s", command_id) - - @asyncio.coroutine - def read_attributes_raw(self, attributes): - schema = foundation.COMMANDS[0x00][1] - attributes = [t.uint16_t(a) for a in attributes] - v = yield from self.request(True, 0x00, schema, attributes) - return v - - @asyncio.coroutine - def read_attributes(self, attributes, allow_cache=False, raw=False): - if raw: - assert len(attributes) == 1 - success, failure = {}, {} - attribute_ids = [] - orig_attributes = {} - for attribute in attributes: - if isinstance(attribute, str): - attrid = self._attridx[attribute] - else: - attrid = attribute - attribute_ids.append(attrid) - orig_attributes[attrid] = attribute - - to_read = [] - if allow_cache: - for idx, attribute in enumerate(attribute_ids): - if attribute in self._attr_cache: - success[attributes[idx]] = self._attr_cache[attribute] - else: - to_read.append(attribute) - else: - to_read = attribute_ids - - if not to_read: - if raw: - return success[attributes[0]] - return success, failure - - result = yield from self.read_attributes_raw(to_read) - if not isinstance(result[0], list): - for attrid in to_read: - orig_attribute = orig_attributes[attrid] - failure[orig_attribute] = result[0] # Assume default response - else: - for record in result[0]: - orig_attribute = orig_attributes[record.attrid] - if record.status == 0: - self._update_attribute(record.attrid, record.value.value) - success[orig_attribute] = record.value.value - else: - failure[orig_attribute] = record.status - - if raw: - # KeyError is an appropriate exception here, I think. - return success[attributes[0]] - return success, failure - - def write_attributes(self, attributes, is_report=False): - 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(0x01, schema, args) - else: - schema = foundation.COMMANDS[0x02][1] - return self.request(True, 0x02, schema, args) - - def bind(self): - return self._endpoint.device.zdo.bind(self._endpoint.endpoint_id, self.cluster_id) - - def unbind(self): - return self._endpoint.device.zdo.unbind(self._endpoint.endpoint_id, self.cluster_id) - - def configure_reporting(self, attribute, min_interval, max_interval, reportable_change): - schema = foundation.COMMANDS[0x06][1] - cfg = foundation.AttributeReportingConfig() - cfg.direction = 0 - cfg.attrid = attribute - cfg.datatype = foundation.DATA_TYPE_IDX.get( - self.attributes.get(attribute, (None, None))[1], - None) - cfg.min_interval = min_interval - cfg.max_interval = max_interval - cfg.reportable_change = reportable_change - return self.request(True, 0x06, schema, [cfg]) - - def command(self, command, *args): - schema = self.server_commands[command][1] - return self.request(False, command, schema, *args) - - def client_command(self, command, *args): - schema = self.client_commands[command][1] - return self.reply(command, schema, *args) - - @property - def name(self): - return self.__class__.__name__ - - @property - def endpoint(self): - return self._endpoint - - @property - def commands(self): - return list(self._server_command_idx.keys()) - - def _update_attribute(self, attrid, value): - self._attr_cache[attrid] = value - self.listener_event('attribute_updated', attrid, value) - - def log(self, lvl, msg, *args): - msg = '[0x%04x:%s:0x%04x] ' + msg - args = ( - self._endpoint.device.nwk, - self._endpoint.endpoint_id, - self.cluster_id, - ) + args - return LOGGER.log(lvl, msg, *args) - - def __getattr__(self, name): - if name in self._client_command_idx: - return functools.partial( - self.client_command, - self._client_command_idx[name], - ) - elif name in self._server_command_idx: - return functools.partial( - self.command, - self._server_command_idx[name], - ) - else: - raise AttributeError("No such command name: %s" % (name, )) - - def __getitem__(self, key): - return self.read_attributes([key], allow_cache=True, raw=True) - - -# Import to populate the registry -from . import clusters # noqa: F401, F402 diff --git a/bellows/zigbee/zcl/clusters/__init__.py b/bellows/zigbee/zcl/clusters/__init__.py deleted file mode 100644 index 48f46cc6..00000000 --- a/bellows/zigbee/zcl/clusters/__init__.py +++ /dev/null @@ -1,12 +0,0 @@ -# flake8: noqa -from . import closures -from . import general -from . import homeautomation -from . import hvac -from . import lighting -from . import lightlink -from . import manufacturer_specific -from . import measurement -from . import protocol -from . import security -from . import smartenergy diff --git a/bellows/zigbee/zcl/clusters/closures.py b/bellows/zigbee/zcl/clusters/closures.py deleted file mode 100644 index e5a98f05..00000000 --- a/bellows/zigbee/zcl/clusters/closures.py +++ /dev/null @@ -1,169 +0,0 @@ -"""Closures Functional Domain""" - -import bellows.types as t -from bellows.zigbee.zcl import Cluster - - -class Shade(Cluster): - """Attributes and commands for configuring a shade""" - cluster_id = 0x0100 - name = 'Shade Configuration' - ep_attribute = 'shade' - attributes = { - # Shade Information - 0x0000: ('physical_closed_limit', t.uint16_t), - 0x0001: ('motor_step_size', t.uint8_t), - 0x0002: ('status', t.uint8_t), # bitmap8 - # Shade Settings - 0x0010: ('losed_limit', t.uint16_t), - 0x0012: ('mode', t.uint8_t), # enum8 - } - server_commands = {} - client_commands = {} - - -class DoorLock(Cluster): - cluster_id = 0x0101 - name = 'Door Lock' - ep_attribute = 'door_lock' - attributes = { - 0x0000: ('lock_state', t.uint8_t), # enum8 - 0x0002: ('actuator_enabled', t.Bool), - 0x0003: ('door_state', t.uint8_t), # enum8 - 0x0004: ('door_open_events', t.uint32_t), - 0x0005: ('door_closed_events', t.uint32_t), - 0x0006: ('open_period', t.uint16_t), - 0x0010: ('num_of_lock_records_supported', t.uint16_t), - 0x0011: ('num_of_total_users_supported', t.uint16_t), - 0x0012: ('num_of_pin_users_supported', t.uint16_t), - 0x0013: ('num_of_rfid_users_supported', t.uint16_t), - 0x0014: ('num_of_week_day_schedules_supported_per_user', t.uint8_t), - 0x0015: ('num_of_year_day_schedules_supported_per_user', t.uint8_t), - 0x0016: ('num_of_holiday_scheduleds_supported', t.uint8_t), - 0x0017: ('max_pin_len', t.uint8_t), - 0x0018: ('min_pin_len', t.uint8_t), - 0x0019: ('max_rfid_len', t.uint8_t), - 0x001a: ('min_rfid_len', t.uint8_t), - 0x0020: ('enable__logging', t.Bool), - 0x0021: ('language', t.LVBytes), - 0x0022: ('led_settings', t.uint8_t), - 0x0023: ('auto_relock_time', t.uint32_t), - 0x0024: ('sound_volume', t.uint8_t), - 0x0025: ('operating_mode', t.uint32_t), - 0x0026: ('lock_type', t.uint16_t), # bitmap16 - 0x0027: ('default_configuration_register', t.uint16_t), # bitmap16 - 0x0028: ('enable_local_programming', t.Bool), - 0x0029: ('enable_one_touch_locking', t.Bool), - 0x002a: ('enable_inside_status_led', t.Bool), - 0x002b: ('enable_privacy_mode_button', t.Bool), - 0x0030: ('wrong_code_entry_limit', t.uint8_t), - 0x0031: ('user_code_temporary_disable_time', t.uint8_t), - 0x0032: ('send_pin_ota', t.Bool), - 0x0033: ('require_pin_for_rf_operation', t.Bool), - 0x0034: ('zigbee_security_level', t.uint8_t), - 0x0040: ('alarm_mask', t.uint16_t), # bitmap16 - 0x0041: ('keypad_operation_event_mask', t.uint16_t), # bitmap16 - 0x0042: ('rf_operation_event_mask', t.uint16_t), # bitmap16 - 0x0043: ('manual_operation_event_mask', t.uint16_t), # bitmap16 - 0x0044: ('rfid_operation_event_mask', t.uint16_t), # bitmap16 - 0x0045: ('keypad_programming_event_mask', t.uint16_t), # bitmap16 - 0x0046: ('rf_programming_event_mask', t.uint16_t), # bitmap16 - 0x0047: ('rfid_programming_event_mask', t.uint16_t), # bitmap16 - } - server_commands = { - 0x0000: ('lock_door', (), False), - 0x0001: ('unlock_door', (), False), - 0x0002: ('toggle_door', (), False), - 0x0003: ('unlock_with_timeout', (), False), - 0x0004: ('get_log_record', (), False), - 0x0005: ('set_pin_code', (), False), - 0x0006: ('get_pin_code', (), False), - 0x0007: ('clear_pin_code', (), False), - 0x0008: ('clear_all_pin_codes', (), False), - 0x0009: ('set_user_status', (), False), - 0x000a: ('get_user_status', (), False), - 0x000b: ('set_week_day_schedule', (), False), - 0x000c: ('get_week_day_schedule', (), False), - 0x000d: ('clear_week_day_schedule', (), False), - 0x000e: ('set_year_day_schedule', (), False), - 0x000f: ('get_year_day_schedule', (), False), - 0x0010: ('clear_year_day_schedule', (), False), - 0x0011: ('set_holiday_schedule', (), False), - 0x0012: ('get_holiday_schedule', (), False), - 0x0013: ('clear_holiday_schedule', (), False), - 0x0014: ('set_user_type', (), False), - 0x0015: ('get_user_type', (), False), - 0x0016: ('set_rfid_code', (), False), - 0x0017: ('get_rfid_code', (), False), - 0x0018: ('clear_rfid_code', (), False), - 0x0019: ('clear_all_rfid_codes', (), False), - } - client_commands = { - 0x0000: ('lock_door_response', (), True), - 0x0001: ('unlock_door_response', (), True), - 0x0002: ('toggle_door_response', (), True), - 0x0003: ('unlock_with_timeout_response', (), True), - 0x0004: ('get_log_record_response', (), True), - 0x0005: ('set_pin_ode_response', (), True), - 0x0006: ('get_pin_code_response', (), True), - 0x0007: ('clear_pin_code_response', (), True), - 0x0008: ('clear_all_pin_codes_response', (), True), - 0x0009: ('set_user_status_response', (), True), - 0x000a: ('get_user_status_response', (), True), - 0x000b: ('set_week_day_schedule_response', (), True), - 0x000c: ('get_week_day_schedule_response', (), True), - 0x000d: ('clear_week_day_schedule_response', (), True), - 0x000e: ('set_year_day_schedule_response', (), True), - 0x000f: ('get_year_day_schedule_response', (), True), - 0x0010: ('clear_year_day_schedule_response', (), True), - 0x0011: ('set_holiday_schedule_response', (), True), - 0x0012: ('get_holiday_schedule_response', (), True), - 0x0013: ('clear_holiday_schedule_response', (), True), - 0x0014: ('set_user_type_response', (), True), - 0x0015: ('get_user_type_response', (), True), - 0x0016: ('set_rfid_code_response', (), True), - 0x0017: ('get_rfid_code_response', (), True), - 0x0018: ('clear_rfid_code_response', (), True), - 0x0019: ('clear_all_rfid_codes_response', (), True), - 0x0020: ('operation_event_notification', (), False), - 0x0021: ('programming_event_notification', (), False), - } - - -class WindowCovering(Cluster): - cluster_id = 0x0102 - name = 'Window Covering' - ep_attribute = 'window_covering' - attributes = { - # Window Covering Information - 0x0000: ('window_covering_type', t.uint8_t), # enum8 - 0x0001: ('physical_close_limit_lift_cm', t.uint16_t), - 0x0002: ('physical_close_limit_tilt_ddegree', t.uint16_t), - 0x0003: ('current_position_lift_cm', t.uint16_t), - 0x0004: ('current_position_tilt_ddegree', t.uint16_t), - 0x0005: ('num_of_actuation_lift', t.uint16_t), - 0x0007: ('config_status', t.uint8_t), # bitmap8 - 0x0008: ('current_position_lift_percentage', t.uint8_t), - 0x0009: ('current_position_tilt_percentage', t.uint8_t), - # Window Covering Settings - 0x0010: ('installed_open_limit_lift_cm', t.uint16_t), - 0x0011: ('installed_closed_limit_lift_cm', t.uint16_t), - 0x0012: ('installed_open_limit_tilt_ddegree', t.uint16_t), - 0x0013: ('installed_closed_limit_tilt_ddegree', t.uint16_t), - 0x0014: ('velocity_lift', t.uint16_t), - 0x0015: ('acceleration_time_lift', t.uint16_t), - 0x0016: ('num_of_actuation_tilt', t.uint16_t), - 0x0017: ('window_covering_mode', t.uint8_t), # bitmap8 - 0x0018: ('intermediate_setpoints_lift', t.LVBytes), - 0x0019: ('intermediate_setpoints_tilt', t.LVBytes), - } - server_commands = { - 0x0000: ('up_open', (), False), - 0x0001: ('down_close', (), False), - 0x0002: ('stop', (), False), - 0x0004: ('go_to_lift_value', (), False), - 0x0005: ('go_to_lift_percentage', (), False), - 0x0007: ('go_to_tilt_value', (), False), - 0x0008: ('go_to_tilt_percentage', (), False), - } - client_commands = {} diff --git a/bellows/zigbee/zcl/clusters/general.py b/bellows/zigbee/zcl/clusters/general.py deleted file mode 100644 index 03b65f55..00000000 --- a/bellows/zigbee/zcl/clusters/general.py +++ /dev/null @@ -1,772 +0,0 @@ -"""General Functional Domain""" - -from datetime import datetime -import bellows.types as t -from bellows.zigbee.zcl import Cluster -from bellows.zigbee.zcl import foundation - - -class Basic(Cluster): - """ Attributes for determining basic information about a - device, setting user device information such as location, - and enabling a device. - """ - cluster_id = 0x0000 - ep_attribute = 'basic' - attributes = { - # Basic Device Information - 0x0000: ('zcl_version', t.uint8_t), - 0x0001: ('app_version', t.uint8_t), - 0x0002: ('stack_version', t.uint8_t), - 0x0003: ('hw_version', t.uint8_t), - 0x0004: ('manufacturer', t.LVBytes), - 0x0005: ('model', t.LVBytes), - 0x0006: ('date_code', t.LVBytes), - 0x0007: ('power_source', t.uint8_t), # enum8 - 0x0008: ('app_profile_version', t.uint8_t), # enum8 - # Basic Device Settings - 0x0010: ('location_desc', t.LVBytes), - 0x0011: ('physical_env', t.uint8_t), # enum8 - 0x0012: ('device_enabled', t.Bool), - 0x0013: ('alarm_mask', t.uint8_t), # bitmap8 - 0x0014: ('disable_local_config', t.uint8_t), # bitmap8 - 0x4000: ('sw_build_id', t.LVBytes), - } - server_commands = { - 0x0000: ('reset_fact_default', (), False), - } - client_commands = {} - - -class PowerConfiguration(Cluster): - """ Attributes for determining more detailed information - about a device’s power source(s), and for configuring - under/over voltage alarms.""" - cluster_id = 0x0001 - name = 'Power Configuration' - ep_attribute = 'power' - attributes = { - # Mains Information - 0x0000: ('mains_voltage', t.uint16_t), - 0x0001: ('mains_frequency', t.uint8_t), - # Mains Settings - 0x0010: ('mains_alarm_mask', t.uint8_t), # bitmap8 - 0x0011: ('mains_volt_min_thres', t.uint16_t), - 0x0012: ('mains_volt_max_thres', t.uint16_t), - 0x0013: ('mains_voltage_dwell_trip_point', t.uint16_t), - # Battery Information - 0x0020: ('battery_voltage', t.uint8_t), - 0x0021: ('battery_percentage_remaining', t.uint8_t), - # Battery Settings - 0x0030: ('battery_volt', t.LVBytes), - 0x0031: ('battery_size', t.uint8_t), # enum8 - 0x0032: ('battery_a_hr_rating', t.uint16_t), - 0x0033: ('battery_quantity', t.uint8_t), - 0x0034: ('battery_rated_voltage', t.uint8_t), - 0x0035: ('battery_alarm_mask', t.uint8_t), # bitmap8 - 0x0036: ('battery_volt_min_thres', t.uint8_t), - 0x0037: ('battery_volt_thres1', t.uint16_t), - 0x0038: ('battery_volt_thres2', t.uint16_t), - 0x0039: ('battery_volt_thres3', t.uint16_t), - 0x003a: ('battery_percent_min_thres', t.uint8_t), - 0x003b: ('battery_percent_thres1', t.uint8_t), - 0x003c: ('battery_percent_thres2', t.uint8_t), - 0x003d: ('battery_percent_thres3', t.uint8_t), - 0x003e: ('battery_alarm_state', t.uint32_t), # bitmap32 - # Battery 2 Information - 0x0040: ('battery_2_voltage', t.uint8_t), - 0x0041: ('battery_2_percentage_remaining', t.uint8_t), - # Battery 2 Settings - 0x0050: ('battery_2_volt', t.LVBytes), - 0x0051: ('battery_2_size', t.uint8_t), # enum8 - 0x0052: ('battery_2_a_hr_rating', t.uint16_t), - 0x0053: ('battery_2_quantity', t.uint8_t), - 0x0054: ('battery_2_rated_voltage', t.uint8_t), - 0x0055: ('battery_2_alarm_mask', t.uint8_t), # bitmap8 - 0x0056: ('battery_2_volt_min_thres', t.uint8_t), - 0x0057: ('battery_2_volt_thres1', t.uint16_t), - 0x0058: ('battery_2_volt_thres2', t.uint16_t), - 0x0059: ('battery_2_volt_thres3', t.uint16_t), - 0x005a: ('battery_2_percent_min_thres', t.uint8_t), - 0x005b: ('battery_2_percent_thres1', t.uint8_t), - 0x005c: ('battery_2_percent_thres2', t.uint8_t), - 0x005d: ('battery_2_percent_thres3', t.uint8_t), - 0x005e: ('battery_2_alarm_state', t.uint32_t), # bitmap32 - # Battery 3 Information - 0x0060: ('battery_3_voltage', t.uint8_t), - 0x0061: ('battery_3_percentage_remaining', t.uint8_t), - # Battery 3 Settings - 0x0070: ('battery_3_volt', t.LVBytes), - 0x0071: ('battery_3_size', t.uint8_t), # enum8 - 0x0072: ('battery_3_a_hr_rating', t.uint16_t), - 0x0073: ('battery_3_quantity', t.uint8_t), - 0x0074: ('battery_3_rated_voltage', t.uint8_t), - 0x0075: ('battery_3_alarm_mask', t.uint8_t), # bitmap8 - 0x0076: ('battery_3_volt_min_thres', t.uint8_t), - 0x0077: ('battery_3_volt_thres1', t.uint16_t), - 0x0078: ('battery_3_volt_thres2', t.uint16_t), - 0x0079: ('battery_3_volt_thres3', t.uint16_t), - 0x007a: ('battery_3_percent_min_thres', t.uint8_t), - 0x007b: ('battery_3_percent_thres1', t.uint8_t), - 0x007c: ('battery_3_percent_thres2', t.uint8_t), - 0x007d: ('battery_3_percent_thres3', t.uint8_t), - 0x007e: ('battery_3_alarm_state', t.uint32_t), # bitmap32 - } - server_commands = {} - client_commands = {} - - -class DeviceTemperature(Cluster): - """Attributes for determining information about a device’s - internal temperature, and for configuring under/over - temperature alarms.""" - cluster_id = 0x0002 - name = 'Device Temperature' - ep_attribute = 'device_temperature' - attributes = { - # Device Temperature Information - 0x0000: ('current_temperature', t.int16s), - 0x0001: ('min_temp_experienced', t.int16s), - 0x0002: ('max_temp_experienced', t.int16s), - 0x0003: ('over_temp_total_dwell', t.uint16_t), - # Device Temperature Settings - 0x0010: ('dev_temp_alarm_mask', t.uint8_t), # bitmap8 - 0x0011: ('low_temp_thres', t.int16s), - 0x0012: ('high_temp_thres', t.int16s), - 0x0013: ('low_temp_dwell_trip_point', t.uint24_t), - 0x0014: ('high_temp_dwell_trip_point', t.uint24_t), - } - server_commands = {} - client_commands = {} - - -class Identify(Cluster): - """Attributes and commands for putting a device into - Identification mode (e.g. flashing a light)""" - cluster_id = 0x0003 - ep_attribute = 'identify' - attributes = { - 0x0000: ('identify_time', t.uint16_t), - 0x0001: ('identify_commission_state', t.uint8_t), # bitmap8 - } - server_commands = { - 0x0000: ('identify', (t.uint16_t, ), False), - 0x0001: ('identify_query', (), False), - 0x0002: ('ezmode_invoke', (t.uint8_t, ), False), # bitmap8 - 0x0003: ('update_commission_state', (t.uint8_t, ), False), # bitmap8 - 0x0040: ('trigger_effect', (), False), - } - client_commands = { - 0x0000: ('identify_query_response', (t.uint16_t, ), True), - } - - -class Groups(Cluster): - """Attributes and commands for group configuration and - manipulation.""" - cluster_id = 0x0004 - ep_attribute = 'groups' - attributes = { - 0x0000: ('name_support', t.uint8_t), # bitmap8 - } - server_commands = { - 0x0000: ('add', (t.uint16_t, t.LVBytes), False), - 0x0001: ('view', (t.uint16_t, ), False), - 0x0002: ('get_membership', (t.LVList(t.uint16_t), ), False), - 0x0003: ('remove', (t.uint16_t, ), False), - 0x0004: ('remove_all', (), False), - 0x0005: ('add_if_identifying', (t.uint16_t, t.LVBytes), False), - } - client_commands = { - 0x0000: ('add_response', (t.uint8_t, t.uint16_t), True), - 0x0001: ('view_response', (t.uint8_t, t.uint16_t, t.LVBytes), True), - 0x0002: ('get_membership_response', (t.uint8_t, t.LVList(t.uint16_t)), True), - 0x0003: ('remove_response', (t.uint8_t, t.uint16_t), True), - } - - -class Scenes(Cluster): - """Attributes and commands for scene configuration and - manipulation.""" - cluster_id = 0x0005 - ep_attribute = 'scenes' - attributes = { - # Scene Management Information - 0x0000: ('count', t.uint8_t), - 0x0001: ('current_scene', t.uint8_t), - 0x0002: ('current_group', t.uint16_t), - 0x0003: ('scene_valid', t.Bool), - 0x0004: ('name_support', t.uint8_t), # bitmap8 - 0x0005: ('last_configured_by', t.EmberEUI64), - } - server_commands = { - 0x0000: ('add', (t.uint16_t, t.uint8_t, t.uint16_t, t.LVBytes, ), False), # + extension field sets - 0x0001: ('view', (t.uint16_t, t.uint8_t), False), - 0x0002: ('remove', (t.uint16_t, t.uint8_t), False), - 0x0003: ('remove_all', (t.uint16_t, ), False), - 0x0004: ('store', (t.uint16_t, t.uint8_t), False), - 0x0005: ('recall', (t.uint16_t, t.uint8_t), False), - 0x0006: ('get_scene_membership', (t.uint16_t, ), False), - 0x0040: ('enhanced_add', (), False), - 0x0041: ('enhanced_view', (), False), - 0x0042: ('copy', (), False), - } - client_commands = { - 0x0000: ('add_response', (t.uint8_t, t.uint16_t, t.uint8_t), True), - 0x0001: ('view_response', (t.uint8_t, t.uint16_t, t.uint8_t, ), True), # + 3 more optionals - 0x0002: ('remove_response', (t.uint8_t, t.uint16_t, t.uint8_t), True), - 0x0003: ('remove_all_response', (t.uint8_t, t.uint16_t), True), - 0x0004: ('store_response', (t.uint8_t, t.uint16_t, t.uint8_t), True), - 0x0006: ('get_scene_membership_response', (t.uint8_t, t.uint8_t, t.uint16_t, t.LVList(t.uint8_t)), True), - 0x0040: ('enhanced_add_response', (), True), - 0x0041: ('enhanced_view_response', (), True), - 0x0042: ('copy_response', (), True), - } - - -class OnOff(Cluster): - """Attributes and commands for switching devices between - ‘On’ and ‘Off’ states. """ - cluster_id = 0x0006 - name = 'On/Off' - ep_attribute = 'on_off' - attributes = { - 0x0000: ('on_off', t.Bool), - 0x4000: ('global_scene_control', t.Bool), - 0x4001: ('on_time', t.uint16_t), - 0x4002: ('off_wait_time', t.uint16_t), - } - server_commands = { - 0x0000: ('off', (), False), - 0x0001: ('on', (), False), - 0x0002: ('toggle', (), False), - 0x0040: ('off_with_effect', (), False), - 0x0041: ('on_with_recall_global_scene', (), False), - 0x0042: ('on_with_timed_off', (), False), - } - client_commands = {} - - -class OnOffConfiguration(Cluster): - """Attributes and commands for configuring On/Off - switching devices""" - cluster_id = 0x0007 - name = 'On/Off Switch Configuration' - ep_attribute = 'on_off_config' - attributes = { - # Switch Information - 0x0000: ('switch_type', t.uint8_t), # enum8 - # Switch Settings - 0x0010: ('switch_actions', t.uint8_t), # enum8 - } - server_commands = {} - client_commands = {} - - -class LevelControl(Cluster): - """Attributes and commands for controlling devices that - can be set to a level between fully ‘On’ and fully ‘Off’.""" - cluster_id = 0x0008 - name = 'Level control' - ep_attribute = 'level' - attributes = { - 0x0000: ('current_level', t.uint8_t), - 0x0001: ('remaining_time', t.uint16_t), - 0x0010: ('on_off_transition_time', t.uint16_t), - 0x0011: ('on_level', t.uint8_t), - 0x0012: ('on_transition_time', t.uint16_t), - 0x0013: ('off_transition_time', t.uint16_t), - 0x0014: ('default_move_rate', t.uint8_t), - } - server_commands = { - 0x0000: ('move_to_level', (t.uint8_t, t.uint16_t), False), - 0x0001: ('move', (t.uint8_t, t.uint8_t), False), - 0x0002: ('step', (t.uint8_t, t.uint8_t, t.uint16_t), False), - 0x0003: ('stop', (), False), - 0x0004: ('move_to_level_with_on_off', (t.uint8_t, t.uint16_t), False), - 0x0005: ('move_with_on_off', (t.uint8_t, t.uint8_t), False), - 0x0006: ('step_with_on_off', (t.uint8_t, t.uint8_t, t.uint16_t), False), - 0x0007: ('stop', (), False), - } - client_commands = {} - - -class Alarms(Cluster): - """ Attributes and commands for sending notifications and - configuring alarm functionality.""" - cluster_id = 0x0009 - ep_attribute = 'alarms' - attributes = { - # Alarm Information - 0x0000: ('alarm_count', t.uint16_t), - } - server_commands = { - 0x0000: ('reset', (t.uint8_t, t.uint16_t), False), - 0x0001: ('reset_all', (), False), - 0x0002: ('get_alarm', (), False), - 0x0003: ('reset_log', (), False), - 0x0004: ('publish_event_log', (), False), - } - client_commands = { - 0x0000: ('alarm', (t.uint8_t, t.uint16_t), False), - 0x0001: ('get_alarm_response', (t.uint8_t, t.uint8_t, t.uint16_t, t.uint32_t), True), - 0x0002: ('get_event_log', (), False), - } - - -class Time(Cluster): - """ Attributes and commands that provide a basic interface - to a real-time clock.""" - cluster_id = 0x000a - ep_attribute = 'time' - attributes = { - 0x0000: ('time', t.uint32_t), - 0x0001: ('time_status', t.uint8_t), # bitmap8 - 0x0002: ('time_zone', t.int32s), - 0x0003: ('dst_start', t.uint32_t), - 0x0004: ('dst_end', t.uint32_t), - 0x0005: ('dst_shift', t.int32s), - 0x0006: ('standard_time', t.uint32_t), - 0x0007: ('local_time', t.uint32_t), - 0x0008: ('last_set_time', t.uint32_t), - 0x0009: ('valid_until_time', t.uint32_t), - } - server_commands = {} - client_commands = {} - - def handle_cluster_request(self, aps_frame, tsn, command_id, *args): - if command_id == 0: - data = {} - for attr in args[0][0]: - if attr == 0: - epoch = datetime(2000, 1, 1, 0, 0, 0, 0) - diff = datetime.utcnow() - epoch - data[attr] = diff.total_seconds() - elif attr == 1: - data[attr] = 7 - elif attr == 2: - diff = datetime.fromtimestamp(0) - datetime.utcfromtimestamp(0) - data[attr] = diff.total_seconds() - - self.write_attributes(data, True) - - -class RSSILocation(Cluster): - """Attributes and commands that provide a means for - exchanging location information and channel parameters - among devices.""" - cluster_id = 0x000b - ep_attribute = 'rssi_location' - attributes = { - # Location Information - 0x0000: ('type', t.uint8_t), - 0x0001: ('method', t.uint8_t), # enum8 - 0x0002: ('age', t.uint16_t), - 0x0003: ('quality_measure', t.uint8_t), - 0x0004: ('num_of_devices', t.uint8_t), - # Location Settings - 0x0010: ('coordinate1', t.int16s), - 0x0011: ('coordinate2', t.int16s), - 0x0012: ('coordinate3', t.int16s), - 0x0013: ('power', t.int16s), - 0x0014: ('path_loss_exponent', t.uint16_t), - 0x0015: ('reporting_period', t.uint16_t), - 0x0016: ('calc_period', t.uint16_t), - 0x0017: ('num_rssi_measurements', t.uint16_t), - } - server_commands = { - 0x0000: ('set_absolute_location', (t.int16s, t.int16s, t.int16s, t.int8s, t.uint16_t), False), - 0x0001: ('set_dev_config', (t.int16s, t.uint16_t, t.uint16_t, t.uint8_t, t.uint16_t), False), - 0x0002: ('get_dev_config', (t.EmberEUI64, ), False), - 0x0003: ('get_location_data', (t.uint8_t, t.uint64_t, t.EmberEUI64), False), - 0x0004: ('rssi_response', (t.EmberEUI64, t.int16s, t.int16s, t.int16s, t.int8s, t.uint8_t), True), - 0x0005: ('send_pings', (t.EmberEUI64, t.uint8_t, t.uint16_t), False), - 0x0006: ('anchor_node_announce', (t.EmberEUI64, t.int16s, t.int16s, t.int16s), False), - } - - class NeighborInfo(t.EzspStruct): - _fields = [ - ('neighbor', t.EmberEUI64), - ('x', t.int16s), - ('y', t.int16s), - ('z', t.int16s), - ('rssi', t.int8s), - ('num_measurements', t.uint8_t), - ] - - client_commands = { - 0x0000: ('dev_config_response', (t.uint8_t, t.int16s, t.uint16_t, t.uint16_t, t.uint8_t, t.uint16_t), True), - 0x0001: ('location_data_response', (t.uint8_t, t.uint8_t, t.int16s, t.int16s, t.int16s, t.uint16_t, t.uint8_t, t.uint8_t, t.uint16_t), True), - 0x0002: ('location_data_notification', (), False), - 0x0003: ('compact_location_data_notification', (), False), - 0x0004: ('rssi_ping', (t.uint8_t, ), False), # data8 - 0x0005: ('rssi_req', (), False), - 0x0006: ('report_rssi_measurements', (t.EmberEUI64, t.LVList(NeighborInfo), ), False), - 0x0007: ('request_own_location', (t.EmberEUI64, ), False), - } - - -class AnalogInput(Cluster): - cluster_id = 0x000c - ep_attribute = 'analog_input' - attributes = { - 0x001c: ('description', t.LVBytes), - 0x0041: ('max_present_value', t.Single), - 0x0045: ('min_present_value', t.Single), - 0x0051: ('out_of_service', t.Bool), - 0x0055: ('present_value', t.Single), - 0x0067: ('reliability', t.uint8_t), # enum8 - 0x006a: ('resolution', t.Single), - 0x006f: ('status_flags', t.uint8_t), # bitmap8 - 0x0075: ('engineering_units', t.uint16_t), # enum16 - 0x0100: ('application_type', t.uint32_t), - } - server_commands = {} - client_commands = {} - - -class AnalogOutput(Cluster): - cluster_id = 0x000d - ep_attribute = 'analog_output' - attributes = { - 0x001c: ('description', t.LVBytes), - 0x0041: ('max_present_value', t.Single), - 0x0045: ('min_present_value', t.Single), - 0x0051: ('out_of_service', t.Bool), - 0x0055: ('present_value', t.Single), - # 0x0057: ('priority_array', TODO.array), # Array of 16 structures of (boolean, single precision) - 0x0067: ('reliability', t.uint8_t), # enum8 - 0x0068: ('relinquish_default', t.Single), - 0x006a: ('resolution', t.Single), - 0x006f: ('status_flags', t.uint8_t), # bitmap8 - 0x0075: ('engineering_units', t.uint16_t), # enum16 - 0x0100: ('application_type', t.uint32_t), - } - server_commands = {} - client_commands = {} - - -class AnalogValue(Cluster): - cluster_id = 0x000e - ep_attribute = 'analog_value' - attributes = { - 0x001c: ('description', t.LVBytes), - 0x0051: ('out_of_service', t.Bool), - 0x0055: ('present_value', t.Single), - # 0x0057: ('priority_array', TODO.array), # Array of 16 structures of (boolean, single precision) - 0x0067: ('reliability', t.uint8_t), # enum8 - 0x0068: ('relinquish_default', t.Single), - 0x006f: ('status_flags', t.uint8_t), # bitmap8 - 0x0075: ('engineering_units', t.uint16_t), # enum16 - 0x0100: ('application_type', t.uint32_t), - } - server_commands = {} - client_commands = {} - - -class BinaryInput(Cluster): - cluster_id = 0x000f - name = 'Binary Input (Basic)' - ep_attribute = 'binary_input' - attributes = { - 0x0004: ('active_text', t.LVBytes), - 0x001c: ('description', t.LVBytes), - 0x002e: ('inactive_text', t.LVBytes), - 0x0051: ('out_of_service', t.Bool), - 0x0054: ('polarity', t.uint8_t), # enum8 - 0x0055: ('present_value', t.Single), - 0x0067: ('reliability', t.uint8_t), # enum8 - 0x006f: ('status_flags', t.uint8_t), # bitmap8 - 0x0100: ('application_type', t.uint32_t), - } - server_commands = {} - client_commands = {} - - -class BinaryOutput(Cluster): - cluster_id = 0x0010 - ep_attribute = 'binary_output' - attributes = { - 0x0004: ('active_text', t.LVBytes), - 0x001c: ('description', t.LVBytes), - 0x002e: ('inactive_text', t.LVBytes), - 0x0042: ('minimum_off_time', t.uint32_t), - 0x0043: ('minimum_on_time', t.uint32_t), - 0x0051: ('out_of_service', t.Bool), - 0x0054: ('polarity', t.uint8_t), # enum8 - 0x0055: ('present_value', t.Single), - # 0x0057: ('priority_array', TODO.array), # Array of 16 structures of (boolean, single precision) - 0x0067: ('reliability', t.uint8_t), # enum8 - 0x0068: ('relinquish_default', t.Single), - 0x006f: ('status_flags', t.uint8_t), # bitmap8 - 0x0100: ('application_type', t.uint32_t), - } - server_commands = {} - client_commands = {} - - -class BinaryValue(Cluster): - cluster_id = 0x0011 - ep_attribute = 'binary_value' - attributes = { - 0x0004: ('active_text', t.LVBytes), - 0x001c: ('description', t.LVBytes), - 0x002e: ('inactive_text', t.LVBytes), - 0x0042: ('minimum_off_time', t.uint32_t), - 0x0043: ('minimum_on_time', t.uint32_t), - 0x0051: ('out_of_service', t.Bool), - 0x0055: ('present_value', t.Single), - # 0x0057: ('priority_array', TODO.array), # Array of 16 structures of (boolean, single precision) - 0x0067: ('reliability', t.uint8_t), # enum8 - 0x0068: ('relinquish_default', t.Single), - 0x006f: ('status_flags', t.uint8_t), # bitmap8 - 0x0100: ('application_type', t.uint32_t), - } - server_commands = {} - client_commands = {} - - -class MultistateInput(Cluster): - cluster_id = 0x0012 - ep_attribute = 'multistate_input' - attributes = { - 0x000e: ('state_text', t.List(t.LVBytes)), - 0x001c: ('description', t.LVBytes), - 0x004a: ('number_of_states', t.uint16_t), - 0x0051: ('out_of_service', t.Bool), - 0x0055: ('present_value', t.Single), - # 0x0057: ('priority_array', TODO.array), # Array of 16 structures of (boolean, single precision) - 0x0067: ('reliability', t.uint8_t), # enum8 - 0x006f: ('status_flags', t.uint8_t), # bitmap8 - 0x0100: ('application_type', t.uint32_t), - } - server_commands = {} - client_commands = {} - - -class MultistateOutput(Cluster): - cluster_id = 0x0013 - ep_attribute = 'multistate_output' - attributes = { - 0x000e: ('state_text', t.List(t.LVBytes)), - 0x001c: ('description', t.LVBytes), - 0x004a: ('number_of_states', t.uint16_t), - 0x0051: ('out_of_service', t.Bool), - 0x0055: ('present_value', t.Single), - # 0x0057: ('priority_array', TODO.array), # Array of 16 structures of (boolean, single precision) - 0x0067: ('reliability', t.uint8_t), # enum8 - 0x0068: ('relinquish_default', t.Single), - 0x006f: ('status_flags', t.uint8_t), # bitmap8 - 0x0100: ('application_type', t.uint32_t), - } - server_commands = {} - client_commands = {} - - -class MultistateValue(Cluster): - cluster_id = 0x0014 - ep_attribute = 'multistate_value' - attributes = { - 0x000e: ('state_text', t.List(t.LVBytes)), - 0x001c: ('description', t.LVBytes), - 0x004a: ('number_of_states', t.uint16_t), - 0x0051: ('out_of_service', t.Bool), - 0x0055: ('present_value', t.Single), - # 0x0057: ('priority_array', TODO.array), # Array of 16 structures of (boolean, single precision) - 0x0067: ('reliability', t.uint8_t), # enum8 - 0x0068: ('relinquish_default', t.Single), - 0x006f: ('status_flags', t.uint8_t), # bitmap8 - 0x0100: ('application_type', t.uint32_t), - } - server_commands = {} - client_commands = {} - - -class Commissioning(Cluster): - """Attributes and commands for commissioning and - managing a ZigBee device.""" - cluster_id = 0x0015 - ep_attribute = 'commissioning' - attributes = { - # Startup Parameters - 0x0000: ('short_address', t.uint16_t), - 0x0001: ('extended_pan_id', t.EmberEUI64), - 0x0002: ('pan_id', t.uint16_t), - 0x0003: ('channelmask', t.uint32_t), # bitmap32 - 0x0004: ('protocol_version', t.uint8_t), - 0x0005: ('stack_profile', t.uint8_t), - 0x0006: ('startup_control', t.uint8_t), # enum8 - 0x0010: ('trust_center_address', t.EmberEUI64), - 0x0011: ('trust_center_master_key', t.fixed_list(16, t.uint8_t)), - 0x0012: ('network_key', t.fixed_list(16, t.uint8_t)), - 0x0013: ('use_insecure_join', t.Bool), - 0x0014: ('preconfigured_link_key', t.fixed_list(16, t.uint8_t)), - 0x0015: ('network_key_seq_num', t.uint8_t), - 0x0016: ('network_key_type', t.uint8_t), # enum8 - 0x0017: ('network_manager_address', t.uint16_t), - # Join Parameters - 0x0020: ('scan_attempts', t.uint8_t), - 0x0021: ('time_between_scans', t.uint16_t), - 0x0022: ('rejoin_interval', t.uint16_t), - 0x0023: ('max_rejoin_interval', t.uint16_t), - # End Device Parameters - 0x0030: ('indirect_poll_rate', t.uint16_t), - 0x0031: ('parent_retry_threshold', t.uint8_t), - # Concentrator Parameters - 0x0040: ('concentrator_flag', t.Bool), - 0x0041: ('concentrator_radius', t.uint8_t), - 0x0042: ('concentrator_discovery_time', t.uint8_t), - } - server_commands = { - 0x0000: ('restart_device', (t.uint8_t, t.uint8_t, t.uint8_t), False), - 0x0001: ('save_startup_parameters', (t.uint8_t, t.uint8_t), False), - 0x0002: ('restore_startup_parameters', (t.uint8_t, t.uint8_t), False), - 0x0003: ('reset_startup_parameters', (t.uint8_t, t.uint8_t), False), - } - client_commands = { - 0x0000: ('restart_device_response', (t.uint8_t, ), True), - 0x0001: ('save_startup_params_response', (t.uint8_t, ), True), - 0x0002: ('restore_startup_params_response', (t.uint8_t, ), True), - 0x0003: ('reset_startup_params_response', (t.uint8_t, ), True), - } - - -class Partition(Cluster): - cluster_id = 0x0016 - ep_attribute = 'partition' - attributes = {} - server_commands = {} - client_commands = {} - - -class Ota(Cluster): - cluster_id = 0x0019 - ep_attribute = 'ota' - attributes = { - 0x0000: ('upgrade_server_id', t.EmberEUI64), - 0x0001: ('file_offset', t.uint32_t), - 0x0002: ('current_file_version', t.uint32_t), - 0x0003: ('current_zigbee_stack_version', t.uint16_t), - 0x0004: ('downloaded_file_version', t.uint32_t), - 0x0005: ('downloaded_zigbee_stack_version', t.uint16_t), - 0x0006: ('image_upgrade_status', t.uint8_t), # enum8 - 0x0007: ('manufacturer_id', t.uint16_t), - 0x0008: ('image_type_id', t.uint16_t), - 0x0009: ('minimum_block_req_delay', t.uint16_t), - 0x000a: ('image_stamp', t.uint32_t), - } - server_commands = { - 0x0001: ('query_next_image', (t.uint8_t, t.uint16_t, t.uint16_t, t.uint32_t, t.uint16_t), False), - 0x0003: ('image_block', (t.uint8_t, t.uint16_t, t.uint16_t, t.uint32_t, t.uint32_t, t.uint8_t, t.EmberEUI64, t.uint16_t), False), - 0x0004: ('image_page', (t.uint8_t, t.uint16_t, t.uint16_t, t.uint32_t, t.uint32_t, t.uint8_t, t.uint16_t, t.uint16_t, t.EmberEUI64), False), - 0x0006: ('upgrade_end', (foundation.Status, t.uint16_t, t.uint16_t, t.uint32_t), False), - 0x0008: ('query_specific_file', (t.EmberEUI64, t.uint16_t, t.uint16_t, t.uint32_t, t.uint16_t), False), - } - client_commands = { - 0x0000: ('image_notify', (t.uint8_t, t.uint8_t, t.uint16_t, t.uint16_t, t.uint32_t), False), - 0x0002: ('query_next_image_response', (foundation.Status, t.uint16_t, t.uint16_t, t.uint32_t, t.uint32_t), True), - 0x0005: ('image_block_response', (foundation.Status, t.uint16_t, t.uint16_t, t.uint32_t, t.uint32_t, t.LVBytes), True), - 0x0007: ('upgrade_end_response', (t.uint16_t, t.uint16_t, t.uint32_t, t.uint32_t, t.uint32_t), True), - 0x0009: ('query_specific_file_response', (foundation.Status, t.uint16_t, t.uint16_t, t.uint32_t, t.uint32_t), True), - } - - -class PowerProfile(Cluster): - cluster_id = 0x001a - ep_attribute = 'power_profile' - attributes = { - 0x0000: ('total_profile_num', t.uint8_t), - 0x0001: ('multiple_scheduling', t.uint8_t), - 0x0002: ('energy_formatting', t.uint8_t), # bitmap8 - 0x0003: ('energy_remote', t.Bool), - 0x0004: ('schedule_mode', t.uint8_t), # bitmap8 - } - - class ScheduleRecord(t.EzspStruct): - _fields = [ - ('phase_id', t.uint8_t), - ('scheduled_time', t.uint16_t), - ] - - class PowerProfilePhase(t.EzspStruct): - _fields = [ - ('energy_phase_id', t.uint8_t), - ('macro_phase_id', t.uint8_t), - ('expected_duration', t.uint16_t), - ('peak_power', t.uint16_t), - ('energy', t.uint16_t), - ] - - class PowerProfile(t.EzspStruct): - _fields = [ - ('power_profile_id', t.uint8_t), - ('energy_phase_id', t.uint8_t), - ('power_profile_remote_control', t.Bool), - ('power_profile_state', t.uint8_t), - ] - - server_commands = { - 0x0000: ('power_profile_request', (t.uint8_t, ), False), - 0x0001: ('power_profile_state_request', (), False), - 0x0002: ('get_power_profile_price_response', (t.uint8_t, t.uint16_t, t.uint32_t, t.uint8_t), True), - 0x0003: ('get_overall_schedule_price_response', (t.uint16_t, t.uint32_t, t.uint8_t), True), - 0x0004: ('energy_phases_schedule_notification', (t.uint8_t, t.LVList(ScheduleRecord)), False), - 0x0005: ('energy_phases_schedule_response', (t.uint8_t, t.LVList(ScheduleRecord)), True), - 0x0006: ('power_profile_schedule_constraints_request', (t.uint8_t, ), False), - 0x0007: ('energy_phases_schedule_state_request', (t.uint8_t, ), False), - 0x0008: ('get_power_profile_price_extended_response', (t.uint8_t, t.uint16_t, t.uint32_t, t.uint8_t), True), - } - client_commands = { - 0x0000: ('power_profile_notification', (t.uint8_t, t.uint8_t, t.LVList(PowerProfilePhase)), False), - 0x0001: ('power_profile_response', (t.uint8_t, t.uint8_t, t.LVList(PowerProfilePhase)), True), - 0x0002: ('power_profile_state_response', (t.LVList(PowerProfile), ), True), - 0x0003: ('get_power_profile_price', (t.uint8_t, ), False), - 0x0004: ('power_profile_state_notification', (t.LVList(PowerProfile), ), False), - 0x0005: ('get_overall_schedule_price', (), False), - 0x0006: ('energy_phases_schedule_request', (), False), - 0x0007: ('energy_phases_schedule_state_response', (t.uint8_t, t.uint8_t), True), - 0x0008: ('energy_phases_schedule_state_notification', (t.uint8_t, t.uint8_t), False), - 0x0009: ('power_profile_schedule_constraints_notification', (t.uint8_t, t.uint16_t, t.uint16_t), False), - 0x000a: ('power_profile_schedule_constraints_response', (t.uint8_t, t.uint16_t, t.uint16_t), True), - 0x000b: ('get_power_profile_price_extended', (t.uint8_t, t.uint8_t, t.uint16_t), False), - } - - -class ApplianceControl(Cluster): - cluster_id = 0x001b - ep_attribute = 'appliance_control' - attributes = {} - server_commands = {} - client_commands = {} - - -class PollControl(Cluster): - cluster_id = 0x0020 - name = "Poll Control" - ep_attribute = 'poll_control' - attributes = { - 0x0000: ('checkin_interval', t.uint32_t), - 0x0001: ('long_poll_interval', t.uint32_t), - 0x0002: ('short_poll_interval', t.uint16_t), - 0x0003: ('fast_poll_timeout', t.uint16_t), - 0x0004: ('checkin_interval_min', t.uint32_t), - 0x0005: ('long_poll_interval_min', t.uint32_t), - 0x0006: ('fast_poll_timeout_max', t.uint16_t), - } - server_commands = { - 0x0000: ('checkin_response', (t.uint8_t, t.uint16_t), True), - 0x0001: ('fast_poll_stop', (), False), - 0x0002: ('set_long_poll_interval', (t.uint32_t, ), False), - 0x0003: ('set_short_poll_interval', (t.uint16_t, ), False), - } - client_commands = { - 0x0000: ('checkin', (), False), - } - - -class GreenPowerProxy(Cluster): - cluster_id = 0x0021 - ep_attribute = 'green_power' - attributes = {} - server_commands = {} - client_commands = {} diff --git a/bellows/zigbee/zcl/clusters/homeautomation.py b/bellows/zigbee/zcl/clusters/homeautomation.py deleted file mode 100644 index 17a01420..00000000 --- a/bellows/zigbee/zcl/clusters/homeautomation.py +++ /dev/null @@ -1,280 +0,0 @@ -import bellows.types as t -from bellows.zigbee.zcl import Cluster - - -class ApplianceIdentification(Cluster): - cluster_id = 0x0b00 - name = 'Appliance Identification' - ep_attribute = 'appliance_id' - attributes = { - 0x0000: ('basic_identification', t.uint56_t), - 0x0010: ('company_name', t.LVBytes), - 0x0011: ('company_id', t.uint16_t), - 0x0012: ('brand_name', t.LVBytes), - 0x0013: ('brand_id', t.uint16_t), - 0x0014: ('model', t.LVBytes), - 0x0015: ('part_number', t.LVBytes), - 0x0016: ('product_revision', t.LVBytes), - 0x0017: ('software_revision', t.LVBytes), - 0x0018: ('product_type_name', t.LVBytes), - 0x0019: ('product_type_id', t.uint16_t), - 0x001a: ('ceced_specification_version', t.uint8_t), - } - server_commands = {} - client_commands = {} - - -class MeterIdentification(Cluster): - cluster_id = 0x0b01 - name = 'Meter Identification' - ep_attribute = 'meter_id' - attributes = { - 0x0000: ('company_name', t.LVBytes), - 0x0001: ('meter_type_id', t.uint16_t), - 0x0004: ('data_quality_id', t.uint16_t), - 0x0005: ('customer_name', t.LVBytes), - 0x0006: ('model', t.LVBytes), - 0x0007: ('part_number', t.LVBytes), - 0x0008: ('product_revision', t.LVBytes), - 0x000a: ('software_revision', t.LVBytes), - 0x000b: ('utility_name', t.LVBytes), - 0x000c: ('pod', t.LVBytes), - 0x000d: ('available_power', t.int24s), - 0x000e: ('power_threshold', t.int24s), - } - server_commands = {} - client_commands = {} - - -class ApplianceEventAlerts(Cluster): - cluster_id = 0x0b02 - name = 'Appliance Event Alerts' - ep_attribute = 'appliance_event' - attributes = {} - server_commands = { - 0x0000: ('get_alerts', (), False), - } - client_commands = { - 0x0000: ('get_alarts_response', (), True), - 0x0001: ('alerts_notification', (), False), - 0x0002: ('event_notification', (), False), - } - - -class ApplianceStatistics(Cluster): - cluster_id = 0x0b03 - name = 'Appliance Statistics' - ep_attribute = 'appliance_stats' - attributes = { - 0x0000: ('log_max_size', t.uint32_t), - 0x0001: ('log_queue_max_size', t.uint8_t), - } - server_commands = { - 0x0000: ('log', (), False), - 0x0001: ('log_queue', (), False), - } - client_commands = { - 0x0000: ('log_notification', (), False), - 0x0001: ('log_response', (), True), - 0x0002: ('log_queue_response', (), True), - 0x0003: ('statistics_available', (), False), - } - - -class ElectricalMeasurement(Cluster): - cluster_id = 0x0b04 - name = 'Electrical Measurement' - ep_attribute = 'electrical_measurement' - attributes = { - # Basic Information - 0x0000: ('measurement_type', t.uint32_t), # bitmap32 - # DC Measurement - 0x0100: ('dc_voltage', t.int16s), - 0x0101: ('dc_voltage_min', t.int16s), - 0x0102: ('dcvoltagemax', t.int16s), - 0x0103: ('dc_current', t.int16s), - 0x0104: ('dc_current_min', t.int16s), - 0x0105: ('dc_current_max', t.int16s), - 0x0106: ('dc_power', t.int16s), - 0x0107: ('dc_power_min', t.int16s), - 0x0108: ('dc_power_max', t.int16s), - # DC Formatting - 0x0200: ('dc_voltage_multiplier', t.uint16_t), - 0x0201: ('dc_voltage_divisor', t.uint16_t), - 0x0202: ('dc_current_multiplier', t.uint16_t), - 0x0203: ('dc_current_divisor', t.uint16_t), - 0x0204: ('dc_power_multiplier', t.uint16_t), - 0x0205: ('dc_power_divisor', t.uint16_t), - # AC (Non-phase Specific) Measurements - 0x0300: ('ac_frequency', t.uint16_t), - 0x0301: ('ac_frequency_min', t.uint16_t), - 0x0302: ('ac_frequency_max', t.uint16_t), - 0x0303: ('neutral_current', t.uint16_t), - 0x0304: ('total_active_power', t.int32s), - 0x0305: ('total_reactive_power', t.int32s), - 0x0306: ('total_apparent_power', t.uint32_t), - 0x0307: ('meas1st_harmonic_current', t.int16s), - 0x0308: ('meas3rd_harmonic_current', t.int16s), - 0x0309: ('meas5th_harmonic_current', t.int16s), - 0x030a: ('meas7th_harmonic_current', t.int16s), - 0x030b: ('meas9th_harmonic_current', t.int16s), - 0x030c: ('meas11th_harmonic_current', t.int16s), - 0x030d: ('meas_phase1st_harmonic_current', t.int16s), - 0x030e: ('meas_phase3rd_harmonic_current', t.int16s), - 0x030f: ('meas_phase5th_harmonic_current', t.int16s), - 0x0310: ('meas_phase7th_harmonic_current', t.int16s), - 0x0311: ('meas_phase9th_harmonic_current', t.int16s), - 0x0312: ('meas_phase11th_harmonic_current', t.int16s), - # AC (Non-phase specific) Formatting - 0x0400: ('ac_frequency_multiplier', t.uint16_t), - 0x0401: ('ac_frequency_divisor', t.uint16_t), - 0x0402: ('power_multiplier', t.uint32_t), - 0x0403: ('power_divisor', t.uint32_t), - 0x0404: ('harmonic_current_multiplier', t.int8s), - 0x0405: ('phase_harmonic_current_multiplier', t.int8s), - # AC (Single Phase or Phase A) Measurements - 0x0500: ('instantaneous_voltage', t.int16s), - 0x0501: ('instantaneous_line_current', t.uint16_t), - 0x0502: ('instantaneous_active_current', t.int16s), - 0x0503: ('instantaneous_reactive_current', t.int16s), - 0x0504: ('instantaneous_power', t.int16s), - 0x0505: ('rms_voltage', t.uint16_t), - 0x0506: ('rms_voltage_min', t.uint16_t), - 0x0507: ('rms_voltage_max', t.uint16_t), - 0x0508: ('rms_current', t.uint16_t), - 0x0509: ('rms_current_min', t.uint16_t), - 0x050a: ('rms_current_max', t.uint16_t), - 0x050b: ('active_power', t.int16s), - 0x050c: ('active_power_min', t.int16s), - 0x050d: ('active_power_max', t.int16s), - 0x050e: ('reactive_power', t.int16s), - 0x050f: ('apparent_power', t.uint16_t), - 0x0510: ('power_factor', t.int8s), - 0x0511: ('average_rms_voltage_meas_period', t.uint16_t), - 0x0512: ('average_rms_over_voltage_counter', t.uint16_t), - 0x0513: ('average_rms_under_voltage_counter', t.uint16_t), - 0x0514: ('rms_extreme_over_voltage_period', t.uint16_t), - 0x0515: ('rms_extreme_under_voltage_period', t.uint16_t), - 0x0516: ('rms_voltage_sag_period', t.uint16_t), - 0x0517: ('rms_voltage_swell_period', t.uint16_t), - # AC Formatting - 0x0600: ('ac_voltage_multiplier', t.uint16_t), - 0x0601: ('ac_voltage_divisor', t.uint16_t), - 0x0602: ('ac_current_multiplier', t.uint16_t), - 0x0603: ('ac_current_divisor', t.uint16_t), - 0x0604: ('ac_power_multiplier', t.uint16_t), - 0x0605: ('ac_power_divisor', t.uint16_t), - # DC Manufacturer Threshold Alarms - 0x0700: ('dc_overload_alarms_mask', t.uint8_t), # bitmap8 - 0x0701: ('dc_voltage_overload', t.int16s), - 0x0702: ('dc_current_overload', t.int16s), - # AC Manufacturer Threshold Alarms - 0x0800: ('ac_alarms_mask', t.uint16_t), # bitmap16 - 0x0801: ('ac_voltage_overload', t.int16s), - 0x0802: ('ac_current_overload', t.int16s), - 0x0803: ('ac_active_power_overload', t.int16s), - 0x0804: ('ac_reactive_power_overload', t.int16s), - 0x0805: ('average_rms_over_voltage', t.int16s), - 0x0806: ('average_rms_under_voltage', t.int16s), - 0x0807: ('rms_extreme_over_voltage', t.int16s), - 0x0808: ('rms_extreme_under_voltage', t.int16s), - 0x0809: ('rms_voltage_sag', t.int16s), - 0x080a: ('rms_voltage_swell', t.int16s), - # AC Phase B Measurements - 0x0901: ('line_current_ph_b', t.uint16_t), - 0x0902: ('active_current_ph_b', t.int16s), - 0x0903: ('reactive_current_ph_b', t.int16s), - 0x0905: ('rms_voltage_ph_b', t.uint16_t), - 0x0906: ('rms_voltage_min_ph_b', t.uint16_t), - 0x0907: ('rms_voltage_max_ph_b', t.uint16_t), - 0x0908: ('rms_current_ph_b', t.uint16_t), - 0x0909: ('rms_current_min_ph_b', t.uint16_t), - 0x090a: ('rms_current_max_ph_b', t.uint16_t), - 0x090b: ('active_power_ph_b', t.int16s), - 0x090c: ('active_power_min_ph_b', t.int16s), - 0x090d: ('active_power_max_ph_b', t.int16s), - 0x090e: ('reactive_power_ph_b', t.int16s), - 0x090f: ('apparent_power_ph_b', t.uint16_t), - 0x0910: ('power_factor_ph_b', t.int8s), - 0x0911: ('average_rms_voltage_measure_period_ph_b', t.uint16_t), - 0x0912: ('average_rms_over_voltage_counter_ph_b', t.uint16_t), - 0x0913: ('average_under_voltage_counter_ph_b', t.uint16_t), - 0x0914: ('rms_extreme_over_voltage_period_ph_b', t.uint16_t), - 0x0915: ('rms_extreme_under_voltage_period_ph_b', t.uint16_t), - 0x0916: ('rms_voltage_sag_period_ph_b', t.uint16_t), - 0x0917: ('rms_voltage_swell_period_ph_b', t.uint16_t), - # AC Phase C Measurements - 0x0a01: ('line_current_ph_c', t.uint16_t), - 0x0a02: ('active_current_ph_c', t.int16s), - 0x0a03: ('reactive_current_ph_c', t.int16s), - 0x0a05: ('rms_voltage_ph_c', t.uint16_t), - 0x0a06: ('rms_voltage_min_ph_c', t.uint16_t), - 0x0a07: ('rms_voltage_max_ph_c', t.uint16_t), - 0x0a08: ('rms_current_ph_c', t.uint16_t), - 0x0a09: ('rms_current_min_ph_c', t.uint16_t), - 0x0a0a: ('rms_current_max_ph_c', t.uint16_t), - 0x0a0b: ('active_power_ph_c', t.int16s), - 0x0a0c: ('active_power_min_ph_c', t.int16s), - 0x0a0d: ('active_power_max_ph_c', t.int16s), - 0x0a0e: ('reactive_power_ph_c', t.int16s), - 0x0a0f: ('apparent_power_ph_c', t.uint16_t), - 0x0a10: ('power_factor_ph_c', t.int8s), - 0x0a11: ('average_rms_voltage_meas_period_ph_c', t.uint16_t), - 0x0a12: ('average_rms_over_voltage_counter_ph_c', t.uint16_t), - 0x0a13: ('average_under_voltage_counter_ph_c', t.uint16_t), - 0x0a14: ('rms_extreme_over_voltage_period_ph_c', t.uint16_t), - 0x0a15: ('rms_extreme_under_voltage_period_ph_c', t.uint16_t), - 0x0a16: ('rms_voltage_sag_period_ph_c', t.uint16_t), - 0x0a17: ('rms_voltage_swell_period_ph__c', t.uint16_t), - } - server_commands = { - 0x0000: ('get_profile_info', (), False), - 0x0001: ('get_measurement_profile', (), False), - } - client_commands = { - 0x0000: ('get_profile_info_response', (), True), - 0x0001: ('get_measurement_profile_response', (), True), - } - - -class Diagnostic(Cluster): - cluster_id = 0x0b05 - ep_attribute = 'diagnostic' - attributes = { - # Hardware Information - 0x0000: ('number_of_resets', t.uint16_t), - 0x0001: ('persistent_memory_writes', t.uint16_t), - # Stack/Network Information - 0x0100: ('mac_rx_bcast', t.uint32_t), - 0x0101: ('mac_tx_bcast', t.uint32_t), - 0x0102: ('mac_rx_ucast', t.uint32_t), - 0x0103: ('mac_tx_ucast', t.uint32_t), - 0x0104: ('mac_tx_ucast_retry', t.uint16_t), - 0x0105: ('mac_tx_ucast_fail', t.uint16_t), - 0x0106: ('aps_rx_bcast', t.uint16_t), - 0x0107: ('aps_tx_bcast', t.uint16_t), - 0x0108: ('aps_rx_ucast', t.uint16_t), - 0x0109: ('aps_tx_ucast_success', t.uint16_t), - 0x010a: ('aps_tx_ucast_retry', t.uint16_t), - 0x010b: ('aps_tx_ucast_fail', t.uint16_t), - 0x010c: ('route_disc_initiated', t.uint16_t), - 0x010d: ('neighbor_added', t.uint16_t), - 0x010e: ('neighbor_removed', t.uint16_t), - 0x010f: ('neighbor_stale', t.uint16_t), - 0x0110: ('join_indication', t.uint16_t), - 0x0111: ('child_moved', t.uint16_t), - 0x0112: ('nwk_fc_failure', t.uint16_t), - 0x0113: ('aps_fc_failure', t.uint16_t), - 0x0114: ('aps_unauthorized_key', t.uint16_t), - 0x0115: ('nwk_decrypt_failures', t.uint16_t), - 0x0116: ('aps_decrypt_failures', t.uint16_t), - 0x0117: ('packet_buffer_allocate_failures', t.uint16_t), - 0x0118: ('relayed_ucast', t.uint16_t), - 0x0119: ('phy_to_mac_queue_limit_reached', t.uint16_t), - 0x011a: ('packet_validate_drop_count', t.uint16_t), - 0x011b: ('average_mac_retry_per_aps_message_sent', t.uint16_t), - 0x011c: ('last_message_lqi', t.uint8_t), - 0x011d: ('last_message_rssi', t.int8s), - } - server_commands = {} - client_commands = {} diff --git a/bellows/zigbee/zcl/clusters/hvac.py b/bellows/zigbee/zcl/clusters/hvac.py deleted file mode 100644 index 6f521905..00000000 --- a/bellows/zigbee/zcl/clusters/hvac.py +++ /dev/null @@ -1,159 +0,0 @@ -"""HVAC Functional Domain""" - -import bellows.types as t -from bellows.zigbee.zcl import Cluster - - -class Pump(Cluster): - """An interface for configuring and controlling pumps.""" - cluster_id = 0x0200 - name = 'Pump Configuration and Control' - ep_attribute = 'pump' - attributes = { - # Pump Information - 0x0000: ('max_pressure', t.int16s), - 0x0001: ('max_speed', t.uint16_t), - 0x0002: ('max_flow', t.uint16_t), - 0x0003: ('min_const_pressure', t.int16s), - 0x0004: ('max_const_pressure', t.int16s), - 0x0005: ('min_comp_pressure', t.int16s), - 0x0006: ('max_comp_pressure', t.int16s), - 0x0007: ('min_const_speed', t.uint16_t), - 0x0008: ('max_const_speed', t.uint16_t), - 0x0009: ('min_const_flow', t.uint16_t), - 0x000a: ('max_const_flow', t.uint16_t), - 0x000b: ('min_const_temp', t.int16s), - 0x000c: ('max_const_temp', t.int16s), - # Pump Dynamic Information - 0x0010: ('pump_status', t.uint16_t), # bitmap16 - 0x0011: ('effective_operation_mode', t.uint8_t), # enum8 - 0x0012: ('effective_control_mode', t.uint8_t), # enum8 - 0x0013: ('capacity', t.int16s), - 0x0014: ('speed', t.uint16_t), - 0x0015: ('lifetime_running_hours', t.uint24_t), - 0x0016: ('power', t.uint24_t), - 0x0017: ('lifetime_energy_consumed', t.uint32_t), - # Pump Settings - 0x0020: ('operation_mode', t.uint8_t), # enum8 - 0x0021: ('control_mode', t.uint8_t), # enum8 - 0x0022: ('alarm_mask', t.uint16_t), # bitmap16 - } - server_commands = {} - client_commands = {} - - -class Thermostat(Cluster): - """An interface for configuring and controlling the - functionality of a thermostat.""" - cluster_id = 0x0201 - ep_attribute = 'thermostat' - attributes = { - # Thermostat Information - 0x0000: ('local_temp', t.int16s), - 0x0001: ('outdoor_temp', t.int16s), - 0x0002: ('ocupancy', t.uint8_t), # bitmap8 - 0x0003: ('abs_min_heat_setpoint_limit', t.int16s), - 0x0004: ('abs_max_heat_setpoint_limit', t.int16s), - 0x0005: ('abs_min_cool_setpoint_limit', t.int16s), - 0x0006: ('abs_max_cool_setpoint_limit', t.int16s), - 0x0007: ('pi_cooling_demand', t.uint8_t), - 0x0008: ('pi_heating_demand', t.uint8_t), - 0x0009: ('system_type_config', t.uint8_t), # bitmap8 - # Thermostat Settings - 0x0010: ('local_temperature_calibration', t.int8s), - 0x0011: ('occupied_cooling_setpoint', t.int16s), - 0x0012: ('occupied_heating_setpoint', t.int16s), - 0x0013: ('unoccupied_cooling_setpoint', t.int16s), - 0x0014: ('unoccupied_heating_setpoint', t.int16s), - 0x0015: ('min_heat_setpoint_limit', t.int16s), - 0x0016: ('max_heat_setpoint_limit', t.int16s), - 0x0017: ('min_cool_setpoint_limit', t.int16s), - 0x0018: ('max_cool_setpoint_limit', t.int16s), - 0x0019: ('min_setpoint_dead_band', t.int8s), - 0x001a: ('remote_sensing', t.uint8_t), # bitmap8 - 0x001b: ('ctrl_seqe_of_oper', t.uint8_t), # enum8 - 0x001c: ('system_mode', t.uint8_t), # enum8 - 0x001d: ('alarm_mask', t.uint8_t), # bitmap8 - 0x001e: ('running_mode', t.uint8_t), # enum8 - # ... - 0x0020: ('start_of_week', t.uint8_t), # enum8 - 0x0021: ('number_of_weekly_trans', t.uint8_t), - 0x0022: ('number_of_daily_trans', t.uint8_t), - 0x0023: ('temp_setpoint_hold', t.uint8_t), # enum8 - 0x0024: ('temp_setpoint_hold_duration', t.uint16_t), - 0x0025: ('programing_oper_mode', t.uint8_t), # bitmap8 - 0x0029: ('running_state', t.uint16_t), # bitmap16 - 0x0030: ('setpoint_change_source', t.uint8_t), # enum8 - 0x0031: ('setpoint_change_amount', t.int16s), - 0x0032: ('setpoint_change_source_time_stamp', t.uint32_t), - 0x0040: ('ac_type', t.uint8_t), # enum8 - 0x0041: ('ac_capacity', t.uint16_t), - 0x0042: ('ac_refrigerant_type', t.uint8_t), # enum8 - 0x0043: ('ac_conpressor_type', t.uint8_t), # enum8 - 0x0044: ('ac_error_code', t.uint32_t), # bitmap32 - 0x0045: ('ac_louver_position', t.uint8_t), # enum8 - 0x0046: ('ac_coll_temp', t.int16s), - 0x0047: ('ac_capacity_format', t.uint8_t), # enum8 - } - server_commands = { - 0x0000: ('setpoint_raise_lower', (), False), - 0x0001: ('set_weekly_schedule', (), False), - 0x0002: ('get_weekly_schedule', (), False), - 0x0003: ('clear_weekly_schedule', (), False), - 0x0004: ('get_relay_status_log', (), False), - } - client_commands = { - 0x0000: ('get_weekly_schedule_response', (), True), - 0x0001: ('get_relay_status_log_response', (), True), - } - - -class Fan(Cluster): - """ An interface for controlling a fan in a heating / - cooling system.""" - cluster_id = 0x0202 - name = 'Fan Control' - ep_attribute = 'fan' - attributes = { - # Fan Control Status - 0x0000: ('fan_mode', t.uint8_t), # enum8 - 0x0001: ('fan_mode_sequence', t.uint8_t), # enum8 - } - server_commands = {} - client_commands = {} - - -class Dehumidification(Cluster): - """An interface for controlling dehumidification.""" - cluster_id = 0x0203 - ep_attribute = 'dehumidification' - attributes = { - # Dehumidification Information - 0x0000: ('relative_humidity', t.uint8_t), - 0x0001: ('dehumid_cooling', t.uint8_t), - # Dehumidification Settings - 0x0010: ('rh_dehumid_setpoint', t.uint8_t), - 0x0011: ('relative_humidity_mode', t.uint8_t), # enum8 - 0x0012: ('dehumid_lockout', t.uint8_t), # enum8 - 0x0013: ('dehumid_hysteresis', t.uint8_t), - 0x0014: ('dehumid_max_cool', t.uint8_t), - 0x0015: ('relative_humid_display', t.uint8_t), # enum8 - } - server_commands = {} - client_commands = {} - - -class UserInterface(Cluster): - """An interface for configuring the user interface of a - thermostat (which may be remote from the - thermostat).""" - cluster_id = 0x0204 - name = 'Thermostat User Interface Configuration' - ep_attribute = 'thermostat_ui' - attributes = { - 0x0000: ('temp_display_mode', t.uint8_t), # enum8 - 0x0001: ('keypad_lockout', t.uint8_t), # enum8 - 0x0002: ('programming_visibility', t.uint8_t), # enum8 - } - server_commands = {} - client_commands = {} diff --git a/bellows/zigbee/zcl/clusters/lighting.py b/bellows/zigbee/zcl/clusters/lighting.py deleted file mode 100644 index b5d0d543..00000000 --- a/bellows/zigbee/zcl/clusters/lighting.py +++ /dev/null @@ -1,119 +0,0 @@ -"""Lighting Functional Domain""" - -import bellows.types as t -from bellows.zigbee.zcl import Cluster - - -class Color(Cluster): - """Attributes and commands for controlling the color - properties of a color-capable light""" - cluster_id = 0x0300 - name = 'Color Control' - ep_attribute = 'light_color' - attributes = { - # Color Information - 0x0000: ('current_hue', t.uint8_t), - 0x0001: ('current_saturation', t.uint8_t), - 0x0002: ('remaining_time', t.uint16_t), - 0x0003: ('current_x', t.uint16_t), - 0x0004: ('current_y', t.uint16_t), - 0x0005: ('drift_compensation', t.uint8_t), # enum8 - 0x0006: ('compensation_text', t.LVBytes), - 0x0007: ('color_temperature', t.uint16_t), - 0x0008: ('color_mode', t.uint8_t), # enum8 - # Defined Primaries Information - 0x0010: ('num_primaries', t.uint8_t), - 0x0011: ('primary1_x', t.uint16_t), - 0x0012: ('primary1_y', t.uint16_t), - 0x0013: ('primary1_intensity', t.uint8_t), - 0x0015: ('primary2_x', t.uint16_t), - 0x0016: ('primary2_y', t.uint16_t), - 0x0017: ('primary2_intensity', t.uint8_t), - 0x0019: ('primary3_x', t.uint16_t), - 0x001a: ('primary3_y', t.uint16_t), - 0x001b: ('primary3_intensity', t.uint8_t), - # Additional Defined Primaries Information - 0x0020: ('primary4_x', t.uint16_t), - 0x0021: ('primary4_y', t.uint16_t), - 0x0022: ('primary4_intensity', t.uint8_t), - 0x0024: ('primary5_x', t.uint16_t), - 0x0025: ('primary5_y', t.uint16_t), - 0x0026: ('primary5_intensity', t.uint8_t), - 0x0028: ('primary6_x', t.uint16_t), - 0x0029: ('primary6_y', t.uint16_t), - 0x002a: ('primary6_intensity', t.uint8_t), - # Defined Color Point Settings - 0x0030: ('white_point_x', t.uint16_t), - 0x0031: ('white_point_y', t.uint16_t), - 0x0032: ('color_point_r_x', t.uint16_t), - 0x0033: ('color_point_r_y', t.uint16_t), - 0x0034: ('color_point_r_intensity', t.uint8_t), - 0x0036: ('color_point_g_x', t.uint16_t), - 0x0037: ('color_point_g_y', t.uint16_t), - 0x0038: ('color_point_g_intensity', t.uint8_t), - 0x003a: ('color_point_b_x', t.uint16_t), - 0x003b: ('color_point_b_y', t.uint16_t), - 0x003c: ('color_point_b_intensity', t.uint8_t), - # ... - 0x4000: ('enhanced_current_hue', t.uint16_t), - 0x4001: ('enhanced_color_mode', t.uint8_t), # enum8 - 0x4002: ('color_loop_active', t.uint8_t), - 0x4003: ('color_loop_direction', t.uint8_t), - 0x4004: ('color_loop_time', t.uint16_t), - 0x4005: ('color_loop_start_enhanced_hue', t.uint16_t), - 0x4006: ('color_loop_stored_enhanced_hue', t.uint16_t), - 0x400a: ('color_capabilities', t.uint16_t), - 0x400b: ('color_temp_physical_min', t.uint16_t), - 0x400c: ('color_temp_physical_max', t.uint16_t), - } - server_commands = { - 0x0000: ('move_to_hue', (t.uint8_t, t.uint8_t, t.uint16_t), False), # hue, direction, duration - 0x0001: ('move_hue', (t.uint8_t, t.uint8_t), False), # move mode, rate - 0x0002: ('step_hue', (t.uint8_t, t.uint8_t, t.uint8_t), False), # mode, size, duration - 0x0003: ('move_to_saturation', (t.uint8_t, t.uint16_t), False), # saturation, duration - 0x0004: ('move_saturation', (t.uint8_t, t.uint8_t), False), # mode, rate - 0x0005: ('step_saturation', (t.uint8_t, t.uint8_t, t.uint8_t), False), # mode, size, duration - 0x0006: ('move_to_hue_and_saturation', (t.uint8_t, t.uint8_t, t.uint16_t), False), # hue, saturation, duration - 0x0007: ('move_to_color', (t.uint16_t, t.uint16_t, t.uint16_t), False), # x, y, duration - 0x0008: ('move_color', (t.uint16_t, t.uint16_t), False), # ratex, ratey - 0x0009: ('step_color', (t.uint16_t, t.uint16_t, t.uint16_t), False), # stepx, stepy, duration - 0x000a: ('move_to_color_temp', (t.uint16_t, t.uint16_t), False), # temperature, duration - 0x0040: ('enhanced_move_to_hue', (), False), - 0x0041: ('enhanced_move_hue', (), False), - 0x0042: ('enhanced_step_hue', (), False), - 0x0043: ('enhanced_move_to_hue_and_saturation', (), False), - 0x0044: ('color_loop_set', (), False), - 0x0047: ('stop_move_step', (), False), - } - client_commands = {} - - -class Ballast(Cluster): - """Attributes and commands for configuring a lighting - ballast""" - cluster_id = 0x0301 - ep_attribute = 'light_ballast' - attributes = { - # Ballast Information - 0x0000: ('physical_min_level', t.uint8_t), - 0x0001: ('physical_max_level', t.uint8_t), - 0x0002: ('ballast_status', t.uint8_t), # bitmap8 - # Ballast Settings - 0x0010: ('min_level', t.uint8_t), - 0x0011: ('max_level', t.uint8_t), - 0x0012: ('power_on_level', t.uint8_t), - 0x0013: ('power_on_fade_time', t.uint16_t), - 0x0014: ('intrinsic_ballast_factor', t.uint8_t), - 0x0015: ('ballast_factor_adjustment', t.uint8_t), - # Lamp Information - 0x0020: ('lamp_quantity', t.uint8_t), - # Lamp Settings - 0x0030: ('lamp_type', t.LVBytes), - 0x0031: ('lamp_manufacturer', t.LVBytes), - 0x0032: ('lamp_rated_hours', t.uint24_t), - 0x0033: ('lamp_burn_hours', t.uint24_t), - 0x0034: ('lamp_alarm_mode', t.uint8_t), # bitmap8 - 0x0035: ('lamp_burn_hours_trip_point', t.uint24_t), - } - server_commands = {} - client_commands = {} diff --git a/bellows/zigbee/zcl/clusters/lightlink.py b/bellows/zigbee/zcl/clusters/lightlink.py deleted file mode 100644 index b59d37fc..00000000 --- a/bellows/zigbee/zcl/clusters/lightlink.py +++ /dev/null @@ -1,9 +0,0 @@ -from bellows.zigbee.zcl import Cluster - - -class LightLink(Cluster): - cluster_id = 0x1000 - ep_attribute = 'lightlink' - attributes = {} - server_commands = {} - client_commands = {} diff --git a/bellows/zigbee/zcl/clusters/manufacturer_specific.py b/bellows/zigbee/zcl/clusters/manufacturer_specific.py deleted file mode 100644 index d62931e9..00000000 --- a/bellows/zigbee/zcl/clusters/manufacturer_specific.py +++ /dev/null @@ -1,10 +0,0 @@ -from bellows.zigbee.zcl import Cluster - - -class ManufacturerSpecificCluster(Cluster): - cluster_id_range = (0xfc00, 0xffff) - ep_attribute = 'manufacturer_specific' - name = "Manufacturer Specific" - attributes = {} - server_commands = {} - client_commands = {} diff --git a/bellows/zigbee/zcl/clusters/measurement.py b/bellows/zigbee/zcl/clusters/measurement.py deleted file mode 100644 index 2e025e0c..00000000 --- a/bellows/zigbee/zcl/clusters/measurement.py +++ /dev/null @@ -1,118 +0,0 @@ -"""Measurement & Sensing Functional Domain""" - -import bellows.types as t -from bellows.zigbee.zcl import Cluster - - -class IlluminanceMeasurement(Cluster): - cluster_id = 0x0400 - name = 'Illuminance Measurement' - ep_attribute = 'illuminance' - attributes = { - # Illuminance Measurement Information - 0x0000: ('measured_value', t.uint16_t), - 0x0001: ('min_measured_value', t.uint16_t), - 0x0002: ('max_measured_value', t.uint16_t), - 0x0003: ('tolerance', t.uint16_t), - 0x0004: ('light_sensor_type', t.uint8_t), # enum8 - } - server_commands = {} - client_commands = {} - - -class IlluminanceLevelSensing(Cluster): - cluster_id = 0x0401 - name = 'Illuminance Level Sensing' - ep_attribute = 'illuminance_level' - attributes = { - # Illuminance Level Sensing Information - 0x0000: ('level_status', t.uint8_t), # enum8 - 0x0001: ('light_sensor_type', t.uint8_t), # enum8 - # Illuminance Level Sensing Settings - 0x0010: ('illuminance_target_level', t.uint16_t), - } - server_commands = {} - client_commands = {} - - -class TemperatureMeasurement(Cluster): - cluster_id = 0x0402 - name = 'Temperature Measurement' - ep_attribute = 'temperature' - attributes = { - # Temperature Measurement Information - 0x0000: ('measured_value', t.int16s), - 0x0001: ('min_measured_value', t.int16s), - 0x0002: ('max_measured_value', t.int16s), - 0x0003: ('tolerance', t.uint16_t), - # 0x0010: ('min_percent_change', UNKNOWN), - # 0x0011: ('min_absolute_change', UNKNOWN), - } - server_commands = {} - client_commands = {} - - -class PressureMeasurement(Cluster): - cluster_id = 0x0403 - name = 'Pressure Measurement' - ep_attribute = 'pressure' - attributes = { - # Pressure Measurement Information - 0x0000: ('measured_value', t.int16s), - 0x0001: ('min_measured_value', t.int16s), - 0x0002: ('max_measured_value', t.int16s), - 0x0003: ('tolerance', t.uint16_t), - } - server_commands = {} - client_commands = {} - - -class FlowMeasurement(Cluster): - cluster_id = 0x0404 - name = 'Flow Measurement' - ep_attribute = 'flow' - attributes = { - # Flow Measurement Information - 0x0000: ('measured_value', t.uint16_t), - 0x0001: ('min_measured_value', t.uint16_t), - 0x0002: ('max_measured_value', t.uint16_t), - 0x0003: ('tolerance', t.uint16_t), - } - server_commands = {} - client_commands = {} - - -class RelativeHumidity(Cluster): - cluster_id = 0x0405 - name = 'Relative Humidity Measurement' - ep_attribute = 'humidity' - attributes = { - # Relative Humidity Measurement Information - 0x0000: ('measured_value', t.uint16_t), - 0x0001: ('min_measured_value', t.uint16_t), - 0x0002: ('max_measured_value', t.uint16_t), - 0x0003: ('tolerance', t.uint16_t), - } - server_commands = {} - client_commands = {} - - -class OccupancySensing(Cluster): - cluster_id = 0x0406 - name = 'Occupancy Sensing' - ep_attribute = 'occupancy' - attributes = { - # Occupancy Sensor Information - 0x0000: ('occupancy', t.uint8_t), # bitmap8 - 0x0001: ('occupancy_sensor_type', t.uint8_t), # enum8 - # PIR Configuration - 0x0010: ('pir_o_to_u_delay', t.uint16_t), - 0x0011: ('pir_u_to_o_delay', t.uint16_t), - 0x0012: ('pir_u_to_o_threshold', t.uint8_t), - # Ultrasonic Configuration - 0x0020: ('ultrasonic_o_to_u_delay', t.uint16_t), - 0x0021: ('ultrasonic_u_to_o_delay', t.uint16_t), - 0x0022: ('ultrasonic_u_to_o_threshold', t.uint8_t), - } - server_commands = {} - client_commands = {} diff --git a/bellows/zigbee/zcl/clusters/protocol.py b/bellows/zigbee/zcl/clusters/protocol.py deleted file mode 100644 index e0d5be36..00000000 --- a/bellows/zigbee/zcl/clusters/protocol.py +++ /dev/null @@ -1,350 +0,0 @@ -"""Protocol Interfaces Functional Domain""" - -import bellows.types as t -from bellows.zigbee.zcl import Cluster - - -class DateTime(t.EzspStruct): - _fields = [ - ('date', t.uint32_t), - ('time', t.uint32_t), - ] - - -class GenericTunnel(Cluster): - cluster_id = 0x0600 - ep_attribute = 'generic_tunnel' - attributes = { - 0x0001: ('max_income_trans_size', t.uint16_t), - 0x0002: ('max_outgo_trans_size', t.uint16_t), - 0x0003: ('protocol_addr', t.LVBytes), - } - server_commands = { - 0x0000: ('match_protocol_addr', (), False), - } - client_commands = { - 0x0000: ('match_protocol_addr_response', (), True), - 0x0001: ('advertise_protocol_address', (), False), - } - - -class BacnetProtocolTunnel(Cluster): - cluster_id = 0x0601 - ep_attribute = 'bacnet_tunnel' - attributes = {} - server_commands = { - 0x0000: ('transfer_npdu', (t.LVBytes, ), False), - } - client_commands = {} - - -class AnalogInputRegular(Cluster): - cluster_id = 0x0602 - ep_attribute = 'bacnet_regular_analog_input' - attributes = { - 0x0016: ('cov_increment', t.Single), - 0x001f: ('device_type', t.LVBytes), - 0x004b: ('object_id', t.fixed_list(4, t.uint8_t)), - 0x004d: ('object_name', t.LVBytes), - 0x004f: ('object_type', t.uint16_t), # enum16 - 0x0076: ('update_interval', t.uint8_t), - 0x00a8: ('profile_name', t.LVBytes), - } - server_commands = {} - client_commands = {} - - -class AnalogInputExtended(Cluster): - cluster_id = 0x0603 - ep_attribute = 'bacnet_extended_analog_input' - attributes = { - 0x0000: ('acked_transitions', t.uint8_t), # bitmap8 - 0x0011: ('notification_class', t.uint16_t), - 0x0019: ('deadband', t.Single), - 0x0023: ('event_enable', t.uint8_t), # bitmap8 - 0x0024: ('event_state', t.uint8_t), # enum8 - 0x002d: ('high_limit', t.Single), - 0x0034: ('limit_enable', t.uint8_t), # bitmap8 - 0x003b: ('low_limit', t.Single), - 0x0048: ('notify_type', t.uint8_t), # enum8 - 0x0071: ('time_delay', t.uint8_t), - # 0x0082: ('event_time_stamps', TODO.array), # Array[3] of (16-bit unsigned integer, time of day, or structure of (date, time of day)) - } - server_commands = { - 0x0000: ('transfer_apdu', (), False), - 0x0001: ('connect_req', (), False), - 0x0002: ('disconnect_req', (), False), - 0x0003: ('connect_status_noti', (), False), - } - client_commands = {} - - -class AnalogOutputRegular(Cluster): - cluster_id = 0x0604 - ep_attribute = 'bacnet_regular_analog_output' - attributes = { - 0x0016: ('cov_increment', t.Single), - 0x001f: ('device_type', t.LVBytes), - 0x004b: ('object_id', t.fixed_list(4, t.uint8_t)), - 0x004d: ('object_name', t.LVBytes), - 0x004f: ('object_type', t.uint16_t), # enum16 - 0x0076: ('update_interval', t.uint8_t), - 0x00a8: ('profile_name', t.LVBytes), - } - server_commands = {} - client_commands = {} - - -class AnalogOutputExtended(Cluster): - cluster_id = 0x0605 - ep_attribute = 'bacnet_extended_analog_output' - attributes = { - 0x0000: ('acked_transitions', t.uint8_t), # bitmap8 - 0x0011: ('notification_class', t.uint16_t), - 0x0019: ('deadband', t.Single), - 0x0023: ('event_enable', t.uint8_t), # bitmap8 - 0x0024: ('event_state', t.uint8_t), # enum8 - 0x002d: ('high_limit', t.Single), - 0x0034: ('limit_enable', t.uint8_t), # bitmap8 - 0x003b: ('low_limit', t.Single), - 0x0048: ('notify_type', t.uint8_t), # enum8 - 0x0071: ('time_delay', t.uint8_t), - # 0x0082: ('event_time_stamps', TODO.array), # Array[3] of (16-bit unsigned integer, time of day, or structure of (date, time of day)) - } - server_commands = {} - client_commands = {} - - -class AnalogValueRegular(Cluster): - cluster_id = 0x0606 - ep_attribute = 'bacnet_regular_analog_value' - attributes = { - 0x0016: ('cov_increment', t.Single), - 0x004b: ('object_id', t.fixed_list(4, t.uint8_t)), - 0x004d: ('object_name', t.LVBytes), - 0x004f: ('object_type', t.uint16_t), # enum16 - 0x00a8: ('profile_name', t.LVBytes), - } - server_commands = {} - client_commands = {} - - -class AnalogValueExtended(Cluster): - cluster_id = 0x0607 - ep_attribute = 'bacnet_extended_analog_value' - attributes = { - 0x0000: ('acked_transitions', t.uint8_t), # bitmap8 - 0x0011: ('notification_class', t.uint16_t), - 0x0019: ('deadband', t.Single), - 0x0023: ('event_enable', t.uint8_t), # bitmap8 - 0x0024: ('event_state', t.uint8_t), # enum8 - 0x002d: ('high_limit', t.Single), - 0x0034: ('limit_enable', t.uint8_t), # bitmap8 - 0x003b: ('low_limit', t.Single), - 0x0048: ('notify_type', t.uint8_t), # enum8 - 0x0071: ('time_delay', t.uint8_t), - # 0x0082: ('event_time_stamps', TODO.array), # Array[3] of (16-bit unsigned integer, time of day, or structure of (date, time of day)) - } - server_commands = {} - client_commands = {} - - -class BinaryInputRegular(Cluster): - cluster_id = 0x0608 - ep_attribute = 'bacnet_regular_binary_input' - attributes = { - 0x000f: ('change_of_state_count', t.uint32_t), - 0x0010: ('change_of_state_time', DateTime), - 0x001f: ('device_type', t.LVBytes), - 0x0021: ('elapsed_active_time', t.uint32_t), - 0x004b: ('object_id', t.fixed_list(4, t.uint8_t)), - 0x004d: ('object_name', t.LVBytes), - 0x004f: ('object_type', t.uint16_t), # enum16 - 0x0072: ('time_of_at_reset', DateTime), - 0x0073: ('time_of_sc_reset', DateTime), - 0x00a8: ('profile_name', t.LVBytes), - } - server_commands = {} - client_commands = {} - - -class BinaryInputExtended(Cluster): - cluster_id = 0x0609 - ep_attribute = 'bacnet_extended_binary_input' - attributes = { - 0x0000: ('acked_transitions', t.uint8_t), # bitmap8 - 0x0006: ('alarm_value', t.Bool), - 0x0011: ('notification_class', t.uint16_t), - 0x0023: ('event_enable', t.uint8_t), # bitmap8 - 0x0024: ('event_state', t.uint8_t), # enum8 - 0x0048: ('notify_type', t.uint8_t), # enum8 - 0x0071: ('time_delay', t.uint8_t), - # 0x0082: ('event_time_stamps', TODO.array), # Array[3] of (16-bit unsigned integer, time of day, or structure of (date, time of day)) - } - server_commands = {} - client_commands = {} - - -class BinaryOutputRegular(Cluster): - cluster_id = 0x060a - ep_attribute = 'bacnet_regular_binary_output' - attributes = { - 0x000f: ('change_of_state_count', t.uint32_t), - 0x0010: ('change_of_state_time', DateTime), - 0x001f: ('device_type', t.LVBytes), - 0x0021: ('elapsed_active_time', t.uint32_t), - 0x0028: ('feed_back_value', t.uint8_t), # enum8 - 0x004b: ('object_id', t.fixed_list(4, t.uint8_t)), - 0x004d: ('object_name', t.LVBytes), - 0x004f: ('object_type', t.uint16_t), # enum16 - 0x0072: ('time_of_at_reset', DateTime), - 0x0073: ('time_of_sc_reset', DateTime), - 0x00a8: ('profile_name', t.LVBytes), - } - server_commands = {} - client_commands = {} - - -class BinaryOutputExtended(Cluster): - cluster_id = 0x060b - ep_attribute = 'bacnet_extended_binary_output' - attributes = { - 0x0000: ('acked_transitions', t.uint8_t), # bitmap8 - 0x0011: ('notification_class', t.uint16_t), - 0x0023: ('event_enable', t.uint8_t), # bitmap8 - 0x0024: ('event_state', t.uint8_t), # enum8 - 0x0048: ('notify_type', t.uint8_t), # enum8 - 0x0071: ('time_delay', t.uint8_t), - # 0x0082: ('event_time_stamps', TODO.array), # Array[3] of (16-bit unsigned integer, time of day, or structure of (date, time of day)) - } - server_commands = {} - client_commands = {} - - -class BinaryValueRegular(Cluster): - cluster_id = 0x060c - ep_attribute = 'bacnet_regular_binary_value' - attributes = { - 0x000f: ('change_of_state_count', t.uint32_t), - 0x0010: ('change_of_state_time', DateTime), - 0x0021: ('elapsed_active_time', t.uint32_t), - 0x004b: ('object_id', t.fixed_list(4, t.uint8_t)), - 0x004d: ('object_name', t.LVBytes), - 0x004f: ('object_type', t.uint16_t), # enum16 - 0x0072: ('time_of_at_reset', DateTime), - 0x0073: ('time_of_sc_reset', DateTime), - 0x00a8: ('profile_name', t.LVBytes), - } - server_commands = {} - client_commands = {} - - -class BinaryValueExtended(Cluster): - cluster_id = 0x060d - ep_attribute = 'bacnet_extended_binary_value' - attributes = { - 0x0000: ('acked_transitions', t.uint8_t), # bitmap8 - 0x0006: ('alarm_value', t.Bool), - 0x0011: ('notification_class', t.uint16_t), - 0x0023: ('event_enable', t.uint8_t), # bitmap8 - 0x0024: ('event_state', t.uint8_t), # enum8 - 0x0048: ('notify_type', t.uint8_t), # enum8 - 0x0071: ('time_delay', t.uint8_t), - # 0x0082: ('event_time_stamps', TODO.array), # Array[3] of (16-bit unsigned integer, time of day, or structure of (date, time of day)) - } - server_commands = {} - client_commands = {} - - -class MultistateInputRegular(Cluster): - cluster_id = 0x060e - ep_attribute = 'bacnet_regular_multistate_input' - attributes = { - 0x001f: ('device_type', t.LVBytes), - 0x004b: ('object_id', t.fixed_list(4, t.uint8_t)), - 0x004d: ('object_name', t.LVBytes), - 0x004f: ('object_type', t.uint16_t), # enum16 - 0x00a8: ('profile_name', t.LVBytes), - } - server_commands = {} - client_commands = {} - - -class MultistateInputExtended(Cluster): - cluster_id = 0x060f - ep_attribute = 'bacnet_extended_multistate_input' - attributes = { - 0x0000: ('acked_transitions', t.uint8_t), # bitmap8 - 0x0006: ('alarm_value', t.uint16_t), - 0x0011: ('notification_class', t.uint16_t), - 0x0023: ('event_enable', t.uint8_t), # bitmap8 - 0x0024: ('event_state', t.uint8_t), # enum8 - 0x0025: ('fault_values', t.uint16_t), - 0x0048: ('notify_type', t.uint8_t), # enum8 - 0x0071: ('time_delay', t.uint8_t), - # 0x0082: ('event_time_stamps', TODO.array), # Array[3] of (16-bit unsigned integer, time of day, or structure of (date, time of day)) - } - server_commands = {} - client_commands = {} - - -class MultistateOutputRegular(Cluster): - cluster_id = 0x0610 - ep_attribute = 'bacnet_regular_multistate_output' - attributes = { - 0x001f: ('device_type', t.LVBytes), - 0x0028: ('feed_back_value', t.uint8_t), # enum8 - 0x004b: ('object_id', t.fixed_list(4, t.uint8_t)), - 0x004d: ('object_name', t.LVBytes), - 0x004f: ('object_type', t.uint16_t), # enum16 - 0x00a8: ('profile_name', t.LVBytes), - } - server_commands = {} - client_commands = {} - - -class MultistateOutputExtended(Cluster): - cluster_id = 0x0611 - ep_attribute = 'bacnet_extended_multistate_output' - attributes = { - 0x0000: ('acked_transitions', t.uint8_t), # bitmap8 - 0x0011: ('notification_class', t.uint16_t), - 0x0023: ('event_enable', t.uint8_t), # bitmap8 - 0x0024: ('event_state', t.uint8_t), # enum8 - 0x0048: ('notify_type', t.uint8_t), # enum8 - 0x0071: ('time_delay', t.uint8_t), - # 0x0082: ('event_time_stamps', TODO.array), # Array[3] of (16-bit unsigned integer, time of day, or structure of (date, time of day)) - } - server_commands = {} - client_commands = {} - - -class MultistateValueRegular(Cluster): - cluster_id = 0x0612 - ep_attribute = 'bacnet_regular_multistate_value' - attributes = { - 0x004b: ('object_id', t.fixed_list(4, t.uint8_t)), - 0x004d: ('object_name', t.LVBytes), - 0x004f: ('object_type', t.uint16_t), # enum16 - 0x00a8: ('profile_name', t.LVBytes), - } - server_commands = {} - client_commands = {} - - -class MultistateValueExtended(Cluster): - cluster_id = 0x0613 - ep_attribute = 'bacnet_extended_multistate_value' - attributes = { - 0x0000: ('acked_transitions', t.uint8_t), # bitmap8 - 0x0006: ('alarm_value', t.uint16_t), - 0x0011: ('notification_class', t.uint16_t), - 0x0023: ('event_enable', t.uint8_t), # bitmap8 - 0x0024: ('event_state', t.uint8_t), # enum8 - 0x0025: ('fault_values', t.uint16_t), - 0x0048: ('notify_type', t.uint8_t), # enum8 - 0x0071: ('time_delay', t.uint8_t), - # 0x0082: ('event_time_stamps', TODO.array), # Array[3] of (16-bit unsigned integer, time of day, or structure of (date, time of day)) - } - server_commands = {} - client_commands = {} diff --git a/bellows/zigbee/zcl/clusters/security.py b/bellows/zigbee/zcl/clusters/security.py deleted file mode 100644 index 716f828c..00000000 --- a/bellows/zigbee/zcl/clusters/security.py +++ /dev/null @@ -1,74 +0,0 @@ -"""Security and Safety Functional Domain""" - -import bellows.types as t -from bellows.zigbee.zcl import Cluster - - -class IasZone(Cluster): - cluster_id = 0x0500 - name = 'IAS Zone' - ep_attribute = 'ias_zone' - attributes = { - # Zone Information - 0x0000: ('zone_state', t.uint8_t), # enum8 - 0x0001: ('zone_type', t.uint16_t), # enum16 - 0x0002: ('zone_status', t.uint16_t), # bitmap16 - # Zone Settings - 0x0010: ('cie_addr', t.EmberEUI64), - 0x0011: ('zone_id', t.uint8_t), - 0x0012: ('num_zone_sensitivity_levels_supported', t.uint8_t), - 0x0013: ('current_zone_sensitivity_level', t.uint8_t), - } - server_commands = { - 0x0000: ('enroll_response', (t.uint8_t, t.uint8_t), True), - 0x0001: ('init_normal_op_mode', (), False), - 0x0002: ('init_test_mode', (), False), - } - client_commands = { - 0x0000: ('status_change_notification', (t.uint16_t, t.uint8_t, t.uint8_t, t.uint16_t), False), - 0x0001: ('enroll', (), False), - } - - -class IasAce(Cluster): - cluster_id = 0x0501 - name = 'IAS Ancillary Control Equipment' - ep_attribute = 'ias_ace' - attributes = {} - server_commands = { - 0x0000: ('arm', (), False), - 0x0001: ('bypass', (), False), - 0x0002: ('emergency', (), False), - 0x0003: ('fire', (), False), - 0x0004: ('panic', (), False), - 0x0005: ('get_zone_id_map', (), False), - 0x0006: ('get_zone_info', (), False), - 0x0007: ('get_panel_status', (), False), - 0x0008: ('get_bypassed_zone_list', (), False), - 0x0009: ('get_zone_status', (), False), - } - client_commands = { - 0x0000: ('arm_response', (), True), - 0x0001: ('get_zone_id_map_response', (), True), - 0x0002: ('get_zone_info_response', (), True), - 0x0003: ('zone_status_changed', (), False), - 0x0004: ('panel_status_changed', (), False), - 0x0005: ('panel_status_response', (), True), - 0x0006: ('set_bypassed_zone_list', (), False), - 0x0007: ('bypass_response', (), True), - 0x0008: ('get_zone_status_response', (), True), - } - - -class IasWd(Cluster): - cluster_id = 0x0502 - name = 'IAS Warning Device' - ep_attribute = 'ias_wd' - attributes = { - 0x0000: ('max_duration', t.uint16_t), - } - server_commands = { - 0x0000: ('start_warning', (), False), - 0x0001: ('squawk', (), False), - } - client_commands = {} diff --git a/bellows/zigbee/zcl/clusters/smartenergy.py b/bellows/zigbee/zcl/clusters/smartenergy.py deleted file mode 100644 index 68071edd..00000000 --- a/bellows/zigbee/zcl/clusters/smartenergy.py +++ /dev/null @@ -1,236 +0,0 @@ -import bellows.types as t -from bellows.zigbee.zcl import Cluster - - -class Price(Cluster): - cluster_id = 0x0700 - ep_attribute = 'smartenergy_price' - attributes = {} - server_commands = {} - client_commands = {} - - -class Drlc(Cluster): - cluster_id = 0x0701 - ep_attribute = 'smartenergy_drlc' - attributes = {} - server_commands = {} - client_commands = {} - - -class Metering(Cluster): - cluster_id = 0x0702 - ep_attribute = 'smartenergy_metering' - attributes = { - 0x0000: ('current_summ_delivered', t.uint48_t), - 0x0001: ('current_summ_received', t.uint48_t), - 0x0002: ('current_max_demand_delivered', t.uint48_t), - 0x0003: ('current_max_demand_received', t.uint48_t), - 0x0004: ('dft_summ', t.uint48_t), - 0x0005: ('daily_freeze_time', t.uint16_t), - 0x0006: ('power_factor', t.int8s), - 0x0007: ('reading_snapshot_time', t.uint32_t), - 0x0008: ('current_max_demand_deliverd_time', t.uint32_t), - 0x0009: ('current_max_demand_received_time', t.uint32_t), - 0x000a: ('default_update_period', t.uint8_t), - 0x000b: ('fast_poll_update_period', t.uint8_t), - 0x000c: ('current_block_period_consump_delivered', t.uint48_t), - 0x000d: ('daily_consump_target', t.uint24_t), - 0x000e: ('current_block', t.uint8_t), # enum8 - 0x000f: ('profile_interval_period', t.uint8_t), # enum8 - # 0x0010: ('interval_read_reporting_period', UNKNOWN), - 0x0011: ('preset_reading_time', t.uint16_t), - 0x0012: ('volume_per_report', t.uint16_t), - 0x0013: ('flow_restriction', t.uint8_t), - 0x0014: ('supply_status', t.uint8_t), # enum8 - 0x0015: ('current_in_energy_carrier_summ', t.uint48_t), - 0x0016: ('current_out_energy_carrier_summ', t.uint48_t), - 0x0017: ('inlet_tempreature', t.int24s), - 0x0018: ('outlet_tempreature', t.int24s), - 0x0019: ('control_tempreature', t.int24s), - 0x001a: ('current_in_energy_carrier_demand', t.int24s), - 0x001b: ('current_out_energy_carrier_demand', t.int24s), - 0x001d: ('current_block_period_consump_received', t.uint48_t), - 0x001e: ('current_block_received', t.uint48_t), - # 0x0100: ('change_reporting_profile', UNKNOWN), - 0x0100: ('current_tier1_summ_delivered', t.uint48_t), - 0x0101: ('current_tier1_summ_received', t.uint48_t), - 0x0102: ('current_tier2_summ_delivered', t.uint48_t), - 0x0103: ('current_tier2_summ_received', t.uint48_t), - 0x0104: ('current_tier3_summ_delivered', t.uint48_t), - 0x0105: ('current_tier3_summ_received', t.uint48_t), - 0x0106: ('current_tier4_summ_delivered', t.uint48_t), - 0x0107: ('current_tier4_summ_received', t.uint48_t), - 0x0108: ('current_tier5_summ_delivered', t.uint48_t), - 0x0109: ('current_tier5_summ_received', t.uint48_t), - 0x010a: ('current_tier6_summ_delivered', t.uint48_t), - 0x010b: ('current_tier6_summ_received', t.uint48_t), - 0x010c: ('current_tier7_summ_delivered', t.uint48_t), - 0x010d: ('current_tier7_summ_received', t.uint48_t), - 0x010e: ('current_tier8_summ_delivered', t.uint48_t), - 0x010f: ('current_tier8_summ_received', t.uint48_t), - 0x0110: ('current_tier9_summ_delivered', t.uint48_t), - 0x0111: ('current_tier9_summ_received', t.uint48_t), - 0x0112: ('current_tier10_summ_delivered', t.uint48_t), - 0x0113: ('current_tier10_summ_received', t.uint48_t), - 0x0114: ('current_tier11_summ_delivered', t.uint48_t), - 0x0115: ('current_tier11_summ_received', t.uint48_t), - 0x0116: ('current_tier12_summ_delivered', t.uint48_t), - 0x0117: ('current_tier12_summ_received', t.uint48_t), - 0x0118: ('current_tier13_summ_delivered', t.uint48_t), - 0x0119: ('current_tier13_summ_received', t.uint48_t), - 0x011a: ('current_tier14_summ_delivered', t.uint48_t), - 0x011b: ('current_tier14_summ_received', t.uint48_t), - 0x011c: ('current_tier15_summ_delivered', t.uint48_t), - 0x011d: ('current_tier15_summ_received', t.uint48_t), - 0x0200: ('status', t.uint8_t), # bitmap8 - 0x0201: ('remaining_batt_life', t.uint8_t), - 0x0202: ('hours_in_operation', t.uint24_t), - 0x0203: ('hours_in_fault', t.uint24_t), - 0x0204: ('extended_status', t.uint64_t), # bitmap64 - 0x0300: ('unit_of_measure', t.uint8_t), # enum8 - 0x0301: ('multiplier', t.uint24_t), - 0x0302: ('divisor', t.uint24_t), - 0x0303: ('summa_formatting', t.uint8_t), # bitmap8 - 0x0304: ('demand_formatting', t.uint8_t), # bitmap8 - 0x0305: ('historical_consump_formatting', t.uint8_t), # bitmap8 - 0x0306: ('metering_device_type', t.uint8_t), # bitmap8 - 0x0307: ('site_id', t.LVBytes), - 0x0308: ('meter_serial_number', t.LVBytes), - 0x0309: ('energy_carrier_unit_of_meas', t.uint8_t), # enum8 - 0x030a: ('energy_carrier_summ_formatting', t.uint8_t), # bitmap8 - 0x030b: ('energy_carrier_demand_formatting', t.uint8_t), # bitmap8 - 0x030c: ('temperature_unit_of_meas', t.uint8_t), # enum8 - 0x030d: ('temperature_formatting', t.uint8_t), # bitmap8 - 0x030e: ('module_serial_number', t.LVBytes), - 0x030f: ('operating_tariff_level', t.LVBytes), - 0x0400: ('instantaneous_demand', t.int24s), - 0x0401: ('currentday_consump_delivered', t.uint24_t), - 0x0402: ('currentday_consump_received', t.uint24_t), - 0x0403: ('previousday_consump_delivered', t.uint24_t), - 0x0404: ('previousday_consump_received', t.uint24_t), - 0x0405: ('cur_part_profile_int_start_time_delivered', t.uint32_t), - 0x0406: ('cur_part_profile_int_start_time_received', t.uint32_t), - 0x0407: ('cur_part_profile_int_value_delivered', t.uint24_t), - 0x0408: ('cur_part_profile_int_value_received', t.uint24_t), - 0x0409: ('current_day_max_pressure', t.uint48_t), - 0x040a: ('current_day_min_pressure', t.uint48_t), - 0x040b: ('previous_day_max_pressure', t.uint48_t), - 0x040c: ('previous_day_min_pressure', t.uint48_t), - 0x040d: ('current_day_max_demand', t.int24s), - 0x040e: ('previous_day_max_demand', t.int24s), - 0x040f: ('current_month_max_demand', t.int24s), - 0x0410: ('current_year_max_demand', t.int24s), - 0x0411: ('currentday_max_energy_carr_demand', t.int24s), - 0x0412: ('previousday_max_energy_carr_demand', t.int24s), - 0x0413: ('cur_month_max_energy_carr_demand', t.int24s), - 0x0414: ('cur_month_min_energy_carr_demand', t.int24s), - 0x0415: ('cur_year_max_energy_carr_demand', t.int24s), - 0x0416: ('cur_year_min_energy_carr_demand', t.int24s), - 0x0500: ('max_number_of_periods_delivered', t.uint8_t), - 0x0600: ('current_demand_delivered', t.uint24_t), - 0x0601: ('demand_limit', t.uint24_t), - 0x0602: ('demand_integration_period', t.uint8_t), - 0x0603: ('number_of_demand_subintervals', t.uint8_t), - 0x0604: ('demand_limit_arm_duration', t.uint16_t), - 0x0800: ('generic_alarm_mask', t.uint16_t), # bitmap16 - 0x0801: ('electricity_alarm_mask', t.uint32_t), # bitmap32 - 0x0802: ('gen_flow_pressure_alarm_mask', t.uint16_t), # bitmap16 - 0x0803: ('water_specific_alarm_mask', t.uint16_t), # bitmap16 - 0x0804: ('heat_cool_specific_alarm_mask', t.uint16_t), # bitmap16 - 0x0805: ('gas_specific_alarm_mask', t.uint16_t), # bitmap16 - 0x0806: ('extended_generic_alarm_mask', t.uint48_t), # bitmap48 - 0x0807: ('manufacture_alarm_mask', t.uint16_t), # bitmap16 - 0x0a00: ('bill_to_date', t.uint32_t), - 0x0a01: ('bill_to_date_time_stamp', t.uint32_t), - 0x0a02: ('projected_bill', t.uint32_t), - 0x0a03: ('projected_bill_time_stamp', t.uint32_t), - } - server_commands = { - 0x0000: ('get_profile', (), False), - 0x0001: ('req_mirror', (), False), - 0x0002: ('mirror_rem', (), False), - 0x0003: ('req_fast_poll_mode', (), False), - 0x0004: ('get_snapshot', (), False), - 0x0005: ('take_snapshot', (), False), - 0x0006: ('mirror_report_attr_response', (), True), - } - client_commands = { - 0x0000: ('get_profile_response', (), True), - 0x0001: ('req_mirror_response', (), True), - 0x0002: ('mirror_rem_response', (), True), - 0x0003: ('req_fast_poll_mode_response', (), True), - 0x0004: ('get_snapshot_response', (), True), - } - - -class Messaging(Cluster): - cluster_id = 0x0703 - ep_attribute = 'smartenergy_messaging' - attributes = {} - server_commands = {} - client_commands = {} - - -class Tunneling(Cluster): - cluster_id = 0x0704 - ep_attribute = 'smartenergy_tunneling' - attributes = {} - server_commands = {} - client_commands = {} - - -class Prepayment(Cluster): - cluster_id = 0x0705 - ep_attribute = 'smartenergy_prepayment' - attributes = {} - server_commands = {} - client_commands = {} - - -class EnergyManagement(Cluster): - cluster_id = 0x0706 - ep_attribute = 'smartenergy_energy_management' - attributes = {} - server_commands = {} - client_commands = {} - - -class Calendar(Cluster): - cluster_id = 0x0707 - ep_attribute = 'smartenergy_calendar' - attributes = {} - server_commands = {} - client_commands = {} - - -class DeviceManagement(Cluster): - cluster_id = 0x0708 - ep_attribute = 'smartenergy_device_management' - attributes = {} - server_commands = {} - client_commands = {} - - -class Events(Cluster): - cluster_id = 0x0709 - ep_attribute = 'smartenergy_events' - attributes = {} - server_commands = {} - client_commands = {} - - -class MduPairing(Cluster): - cluster_id = 0x070a - ep_attribute = 'smartenergy_mdu_pairing' - attributes = {} - server_commands = {} - client_commands = {} - - -class KeyEstablishment(Cluster): - cluster_id = 0x0800 - ep_attribute = 'smartenergy_key_establishment' - attributes = {} - server_commands = {} - client_commands = {} diff --git a/bellows/zigbee/zcl/foundation.py b/bellows/zigbee/zcl/foundation.py deleted file mode 100644 index e5990e1c..00000000 --- a/bellows/zigbee/zcl/foundation.py +++ /dev/null @@ -1,254 +0,0 @@ -import enum - -import bellows.types as t - - -class Status(t.uint8_t, enum.Enum): - SUCCESS = 0x00 # Operation was successful. - FAILURE = 0x01 # Operation was not successful - NOT_AUTHORIZED = 0x7e # The sender of the command does not have - RESERVED_FIELD_NOT_ZERO = 0x7f # A reserved field/subfield/bit contains a - MALFORMED_COMMAND = 0x80 # The command appears to contain the wrong - UNSUP_CLUSTER_COMMAND = 0x81 # The specified cluster command is not - UNSUP_GENERAL_COMMAND = 0x82 # The specified general ZCL command is not - UNSUP_MANUF_CLUSTER_COMMAND = 0x83 # A manufacturer specific unicast, - UNSUP_MANUF_GENERAL_COMMAND = 0x84 # A manufacturer specific unicast, ZCL - INVALID_FIELD = 0x85 # At least one field of the command contains an - UNSUPPORTED_ATTRIBUTE = 0x86 # The specified attribute does not exist on - INVALID_VALUE = 0x87 # Out of range error, or set to a reserved value. - READ_ONLY = 0x88 # Attempt to write a read only attribute. - INSUFFICIENT_SPACE = 0x89 # An operation (e.g. an attempt to create an - DUPLICATE_EXISTS = 0x8a # An attempt to create an entry in a table failed - NOT_FOUND = 0x8b # The requested information (e.g. table entry) - UNREPORTABLE_ATTRIBUTE = 0x8c # Periodic reports cannot be issued for this - INVALID_DATA_TYPE = 0x8d # The data type given for an attribute is - INVALID_SELECTOR = 0x8e # The selector for an attribute is incorrect. - WRITE_ONLY = 0x8f # A request has been made to read an attribute - INCONSISTENT_STARTUP_STATE = 0x90 # Setting the requested values would put - DEFINED_OUT_OF_BAND = 0x91 # An attempt has been made to write an - INCONSISTENT = 0x92 # The supplied values (e.g., contents of table cells) are inconsistent - ACTION_DENIED = 0x93 # The credentials presented by the device sending the - TIMEOUT = 0x94 # The exchange was aborted due to excessive response time - ABORT = 0x95 # Failed case when a client or a server decides to abort the upgrade process - INVALID_IMAGE = 0x96 # Invalid OTA upgrade image (ex. failed signature - WAIT_FOR_DATA = 0x97 # Server does not have data block available yet - NO_IMAGE_AVAILABLE = 0x98 # No OTA upgrade image available for a particular client - REQUIRE_MORE_IMAGE = 0x99 # The client still requires more OTA upgrade image - NOTIFICATION_PENDING = 0x9a # The command has been received and is being processed - HARDWARE_FAILURE = 0xc0 # An operation was unsuccessful due to a - SOFTWARE_FAILURE = 0xc1 # An operation was unsuccessful due to a - CALIBRATION_ERROR = 0xc2 # An error occurred during calibratio - UNSUPPORTED_CLUSTER = 0xc3 # The cluster is not supported - - -class Analog: - pass - - -class Discrete: - pass - - -DATA_TYPES = { - 0x00: ('No data', None, None), - 0x08: ('General', t.fixed_list(1, t.uint8_t), Discrete), - 0x09: ('General', t.fixed_list(2, t.uint8_t), Discrete), - 0x0a: ('General', t.fixed_list(3, t.uint8_t), Discrete), - 0x0b: ('General', t.fixed_list(4, t.uint8_t), Discrete), - 0x0c: ('General', t.fixed_list(5, t.uint8_t), Discrete), - 0x0d: ('General', t.fixed_list(6, t.uint8_t), Discrete), - 0x0e: ('General', t.fixed_list(7, t.uint8_t), Discrete), - 0x0f: ('General', t.fixed_list(8, t.uint8_t), Discrete), - 0x10: ('Boolean', t.Bool, Discrete), - 0x18: ('Bitmap', t.uint8_t, Discrete), - 0x19: ('Bitmap', t.uint16_t, Discrete), - 0x1a: ('Bitmap', t.uint24_t, Discrete), - 0x1b: ('Bitmap', t.uint32_t, Discrete), - 0x1c: ('Bitmap', t.uint40_t, Discrete), - 0x1d: ('Bitmap', t.uint48_t, Discrete), - 0x1e: ('Bitmap', t.uint56_t, Discrete), - 0x1f: ('Bitmap', t.uint64_t, Discrete), - 0x20: ('Unsigned Integer', t.uint8_t, Analog), - 0x21: ('Unsigned Integer', t.uint16_t, Analog), - 0x22: ('Unsigned Integer', t.uint24_t, Analog), - 0x23: ('Unsigned Integer', t.uint32_t, Analog), - 0x24: ('Unsigned Integer', t.uint40_t, Analog), - 0x25: ('Unsigned Integer', t.uint48_t, Analog), - 0x26: ('Unsigned Integer', t.uint56_t, Analog), - 0x27: ('Unsigned Integer', t.uint64_t, Analog), - 0x28: ('Signed Integer', t.int8s, Analog), - 0x29: ('Signed Integer', t.int16s, Analog), - 0x2a: ('Signed Integer', t.int24s, Analog), - 0x2b: ('Signed Integer', t.int32s, Analog), - 0x2c: ('Signed Integer', t.int40s, Analog), - 0x2d: ('Signed Integer', t.int48s, Analog), - 0x2e: ('Signed Integer', t.int56s, Analog), - 0x2f: ('Signed Integer', t.int64s, Analog), - 0x30: ('Enumeration', t.uint8_t, Discrete), - 0x31: ('Enumeration', t.uint16_t, Discrete), - # 0x38: ('Floating point', t.Half, Analog), - 0x39: ('Floating point', t.Single, Analog), - 0x3a: ('Floating point', t.Double, Analog), - 0x41: ('Octet string', t.LVBytes, Discrete), - 0x42: ('Character string', t.LVBytes, Discrete), - # 0x43: ('Long octet string', ), - # 0x44: ('Long character string', ), - # 0x48: ('Array', ), - # 0x4c: ('Structure', ), - # 0x50: ('Set', ), - # 0x51: ('Bag', ), - 0xe0: ('Time of day', t.uint32_t, Analog), - 0xe1: ('Date', t.uint32_t, Analog), - 0xe2: ('UTCTime', t.uint32_t, Analog), - 0xe8: ('Cluster ID', t.uint16_t, Discrete), - 0xe9: ('Attribute ID', t.uint16_t, Discrete), - 0xea: ('BACNet OID', t.uint32_t, Discrete), - 0xf0: ('IEEE address', t.EmberEUI64, Discrete), - 0xf1: ('128-bit security key', t.fixed_list(16, t.uint16_t), Discrete), - 0xff: ('Unknown', None, None), -} - -DATA_TYPE_IDX = { - t: tidx - for tidx, (tname, t, ad) in DATA_TYPES.items() - if ad is Analog -} -DATA_TYPE_IDX[t.uint32_t] = 0x23 -DATA_TYPE_IDX[t.EmberEUI64] = 0xf0 -DATA_TYPE_IDX[t.Bool] = 0x10 - - -class TypeValue(): - def serialize(self): - return self.type.to_bytes(1, 'little') + self.value.serialize() - - @classmethod - def deserialize(cls, data): - self = cls() - self.type, data = data[0], data[1:] - actual_type = DATA_TYPES[self.type][1] - self.value, data = actual_type.deserialize(data) - return self, data - - -class ReadAttributeRecord(): - @classmethod - def deserialize(cls, data): - r = cls() - r.attrid, data = int.from_bytes(data[:2], 'little'), data[2:] - r.status, data = data[0], data[1:] - if r.status == 0: - r.value, data = TypeValue.deserialize(data) - - return r, data - - def serialize(self): - r = t.uint16_t(self.attrid).serialize() - r += t.uint8_t(self.status).serialize() - if self.status == 0: - r += self.value.serialize() - - return r - - def __repr__(self): - r = ' device.Status.NEW - assert 1 in dev.endpoints - assert 2 in dev.endpoints - - -def test_initialize_fail(dev): - loop = asyncio.get_event_loop() - - @asyncio.coroutine - def mockrequest(req, nwk, tries=None, delay=None): - return [1] - - dev.zdo.request = mockrequest - loop.run_until_complete(dev._initialize()) - - assert dev.status == device.Status.NEW - - -def test_aps(dev): - f = dev.get_aps(1, 2, 3) - assert f.profileId == 1 - assert f.clusterId == 2 - assert f.sourceEndpoint == 3 - assert f.destinationEndpoint == 3 - - -def test_request(dev): - aps = dev.get_aps(1, 2, 3) - dev.request(aps, b'') - app_mock = dev._application - assert app_mock.request.call_count == 1 - assert app_mock.get_sequence.call_count == 1 - - -def test_radio_details(dev): - dev.radio_details(1, 2) - assert dev.lqi == 1 - assert dev.rssi == 2 - - -def test_handle_request_no_endpoint(dev): - f = dev.get_aps(1, 2, 3) - dev.handle_message(False, f, 1, 0, []) - - -def test_handle_request(dev): - f = dev.get_aps(1, 2, 3) - ep = dev.add_endpoint(3) - ep.handle_message = mock.MagicMock() - dev.handle_message(False, f, 1, 0, []) - assert ep.handle_message.call_count == 1 - - -def test_endpoint_getitem(dev): - ep = dev.add_endpoint(3) - assert dev[3] is ep - - with pytest.raises(KeyError): - dev[1] diff --git a/tests/test_endpoint.py b/tests/test_endpoint.py deleted file mode 100644 index 3ffcaa2f..00000000 --- a/tests/test_endpoint.py +++ /dev/null @@ -1,137 +0,0 @@ -import asyncio -from unittest import mock - -import pytest - -import bellows.types as t -from bellows.zigbee import device, endpoint -from bellows.zigbee.zdo import types - - -@pytest.fixture -def ep(): - dev = mock.MagicMock() - return endpoint.Endpoint(dev, 1) - - -def _test_initialize(ep, profile): - loop = asyncio.get_event_loop() - - @asyncio.coroutine - def mockrequest(req, nwk, epid, tries=None, delay=None): - sd = types.SimpleDescriptor() - sd.endpoint = 1 - sd.profile = profile - sd.device_type = 0xff - sd.input_clusters = [5] - sd.output_clusters = [6] - return [0, None, sd] - - ep._device.zdo.request = mockrequest - loop.run_until_complete(ep.initialize()) - - assert ep.status > endpoint.Status.NEW - assert 5 in ep.in_clusters - assert 6 in ep.out_clusters - - -def test_initialize_zha(ep): - return _test_initialize(ep, 260) - - -def test_initialize_zll(ep): - return _test_initialize(ep, 49246) - - -def test_initialize_fail(ep): - loop = asyncio.get_event_loop() - - @asyncio.coroutine - def mockrequest(req, nwk, epid, tries=None, delay=None): - return [1, None, None] - - ep._device.zdo.request = mockrequest - loop.run_until_complete(ep.initialize()) - - assert ep.status == endpoint.Status.NEW - - -def test_reinitialize(ep): - _test_initialize(ep, 260) - assert ep.profile_id == 260 - ep.profile_id = 10 - _test_initialize(ep, 260) - assert ep.profile_id == 10 - - -def test_add_input_cluster(ep): - ep.add_input_cluster(0) - assert 0 in ep.in_clusters - - -def test_add_output_cluster(ep): - ep.add_output_cluster(0) - assert 0 in ep.out_clusters - - -def test_multiple_add_input_cluster(ep): - ep.add_input_cluster(0) - assert ep.in_clusters[0].cluster_id is 0 - ep.in_clusters[0].cluster_id = 1 - assert ep.in_clusters[0].cluster_id is 1 - ep.add_input_cluster(0) - assert ep.in_clusters[0].cluster_id is 1 - - -def test_multiple_add_output_cluster(ep): - ep.add_output_cluster(0) - assert ep.out_clusters[0].cluster_id is 0 - ep.out_clusters[0].cluster_id = 1 - assert ep.out_clusters[0].cluster_id is 1 - ep.add_output_cluster(0) - assert ep.out_clusters[0].cluster_id is 1 - - -def test_get_aps(): - app_mock = mock.MagicMock() - ieee = t.EmberEUI64(map(t.uint8_t, [0, 1, 2, 3, 4, 5, 6, 7])) - dev = device.Device(app_mock, ieee, 65535) - ep = endpoint.Endpoint(dev, 55) - ep.status = endpoint.Status.ZDO_INIT - ep.profile_id = 99 - aps = ep.get_aps(255) - assert aps.profileId == 99 - assert aps.clusterId == 255 - assert aps.sourceEndpoint == 55 - assert aps.destinationEndpoint == 55 - - -def test_handle_message(ep): - c = ep.add_input_cluster(0) - c.handle_message = mock.MagicMock() - f = t.EmberApsFrame() - f.clusterId = 0 - ep.handle_message(False, f, 0, 1, []) - c.handle_message.assert_called_once_with(False, f, 0, 1, []) - - -def test_handle_message_output(ep): - c = ep.add_output_cluster(0) - c.handle_message = mock.MagicMock() - f = t.EmberApsFrame() - f.clusterId = 0 - ep.handle_message(False, f, 0, 1, []) - c.handle_message.assert_called_once_with(False, f, 0, 1, []) - - -def test_handle_request_unknown(ep): - f = t.EmberApsFrame() - f.clusterId = 99 - ep.handle_message(False, f, 0, 0, []) - - -def test_cluster_attr(ep): - with pytest.raises(AttributeError): - ep.basic - ep.add_input_cluster(0) - ep.basic diff --git a/tests/test_types.py b/tests/test_types.py index 283d96a2..a6e9eb90 100644 --- a/tests/test_types.py +++ b/tests/test_types.py @@ -33,25 +33,13 @@ def test_list(): assert t.List(t.uint8_t).deserialize(b'\x0123') == (expected, b'') -def test_single(): - v = t.Single(1.25) - ser = v.serialize() - assert t.Single.deserialize(ser) == (1.25, b'') - - -def test_double(): - v = t.Double(1.25) - ser = v.serialize() - assert t.Double.deserialize(ser) == (1.25, b'') - - def test_struct(): class TestStruct(t.EzspStruct): _fields = [('a', t.uint8_t), ('b', t.uint8_t)] ts = TestStruct() - ts.a = 0xaa - ts.b = 0xbb + ts.a = t.uint8_t(0xaa) + ts.b = t.uint8_t(0xbb) ts2 = TestStruct(ts) assert ts2.a == ts.a assert ts2.b == ts.b @@ -60,6 +48,16 @@ class TestStruct(t.EzspStruct): assert 'TestStruct' in r assert r.startswith('<') and r.endswith('>') + s = ts2.serialize() + assert s == b'\xaa\xbb' + def test_str(): assert str(t.EzspStatus.deserialize(b'\0')[0]) == 'EzspStatus.SUCCESS' + + +def test_ember_eui64(): + ser = b'\x00\x01\x02\x03\x04\x05\x06\x07' + eui64, data = t.EmberEUI64.deserialize(ser) + assert data == b'' + assert eui64.serialize() == ser diff --git a/tests/test_zcl.py b/tests/test_zcl.py deleted file mode 100644 index 64006090..00000000 --- a/tests/test_zcl.py +++ /dev/null @@ -1,296 +0,0 @@ -import asyncio -from unittest import mock - -import pytest - -import bellows.types as t -import bellows.zigbee.zcl as zcl - - -@pytest.fixture -def aps(): - return t.EmberApsFrame() - - -def test_deserialize_general(): - tsn, command_id, is_reply, args = zcl.deserialize(0, b'\x00\x01\x00') - assert tsn == 1 - assert command_id == 0 - assert is_reply is False - - -def test_deserialize_general_unknown(): - tsn, command_id, is_reply, args = zcl.deserialize(0, b'\x00\x01\xff') - assert tsn == 1 - assert command_id == 255 - assert is_reply is False - - -def test_deserialize_cluster(): - tsn, command_id, is_reply, args = zcl.deserialize(0, b'\x01\x01\x00xxx') - assert tsn == 1 - assert command_id == 256 - assert is_reply is False - - -def test_deserialize_cluster_client(): - tsn, command_id, is_reply, args = zcl.deserialize(3, b'\x09\x01\x00AB') - assert tsn == 1 - assert command_id == 256 - assert is_reply is True - assert args == [0x4241] - - -def test_deserialize_cluster_unknown(): - tsn, command_id, is_reply, args = zcl.deserialize(0xff00, b'\x05\x00\x00\x01\x00') - assert tsn == 1 - assert command_id == 256 - assert is_reply is False - - -def test_deserialize_cluster_command_unknown(): - tsn, command_id, is_reply, args = zcl.deserialize(0, b'\x01\x01\xff') - assert tsn == 1 - assert command_id == 255 + 256 - assert is_reply is False - - -def test_unknown_cluster(): - c = zcl.Cluster.from_id(None, 999) - assert isinstance(c, zcl.Cluster) - assert c.cluster_id == 999 - - -def test_manufacturer_specific_cluster(): - import bellows.zigbee.zcl.clusters.manufacturer_specific as ms - c = zcl.Cluster.from_id(None, 0xfc00) - assert isinstance(c, ms.ManufacturerSpecificCluster) - assert hasattr(c, 'cluster_id') - c = zcl.Cluster.from_id(None, 0xffff) - assert isinstance(c, ms.ManufacturerSpecificCluster) - assert hasattr(c, 'cluster_id') - - -@pytest.fixture -def cluster(aps): - epmock = mock.MagicMock() - aps.clusterId = 0 - aps.sequence = 123 - epmock.get_aps.return_value = aps - return zcl.Cluster.from_id(epmock, 0) - - -@pytest.fixture -def client_cluster(aps): - epmock = mock.MagicMock() - aps.clusterId = 3 - aps.sequence = 123 - epmock.get_aps.return_value = aps - return zcl.Cluster.from_id(epmock, 3) - - -def test_request_general(cluster): - cluster.request(True, 0, []) - assert cluster._endpoint.device.request.call_count == 1 - - -def test_reply_general(cluster): - cluster.reply(0, []) - assert cluster._endpoint.device.reply.call_count == 1 - - -def test_attribute_report(cluster): - attr = zcl.foundation.Attribute() - attr.attrid = 4 - attr.value = zcl.foundation.TypeValue() - attr.value.value = 1 - cluster.handle_message(False, aps, 0, 0x0a, [[attr]]) - assert cluster._attr_cache[4] == 1 - - -def test_handle_request_unknown(cluster, aps): - cluster.handle_message(False, aps, 0, 0xff, []) - - -def test_handle_cluster_request(cluster, aps): - cluster.handle_message(False, aps, 0, 256, []) - - -def test_handle_unexpected_reply(cluster, aps): - cluster.handle_message(True, aps, 0, 0, []) - - -def _mk_rar(attrid, value, status=0): - r = zcl.foundation.ReadAttributeRecord() - r.attrid = attrid - r.status = status - r.value = zcl.foundation.TypeValue() - r.value.value = value - return r - - -def test_read_attributes_uncached(cluster): - @asyncio.coroutine - def mockrequest(foundation, command, schema, args): - assert foundation is True - assert command == 0 - rar0 = _mk_rar(0, 99) - rar4 = _mk_rar(4, b'Manufacturer') - rar99 = _mk_rar(99, None, 1) - return [[rar0, rar4, rar99]] - cluster.request = mockrequest - loop = asyncio.get_event_loop() - success, failure = loop.run_until_complete(cluster.read_attributes( - [0, "manufacturer", 99], - )) - assert success[0] == 99 - assert success["manufacturer"] == b'Manufacturer' - assert failure[99] == 1 - - -def test_read_attributes_cached(cluster): - cluster.request = mock.MagicMock() - cluster._attr_cache[0] = 99 - cluster._attr_cache[4] = b'Manufacturer' - loop = asyncio.get_event_loop() - success, failure = loop.run_until_complete(cluster.read_attributes( - [0, "manufacturer"], - allow_cache=True, - )) - assert cluster.request.call_count == 0 - assert success[0] == 99 - assert success["manufacturer"] == b'Manufacturer' - assert failure == {} - - -def test_read_attributes_mixed_cached(cluster): - @asyncio.coroutine - def mockrequest(foundation, command, schema, args): - assert foundation is True - assert command == 0 - rar5 = _mk_rar(5, b'Model') - return [[rar5]] - - cluster.request = mockrequest - cluster._attr_cache[0] = 99 - cluster._attr_cache[4] = b'Manufacturer' - loop = asyncio.get_event_loop() - success, failure = loop.run_until_complete(cluster.read_attributes( - [0, "manufacturer", "model"], - allow_cache=True, - )) - assert success[0] == 99 - assert success["manufacturer"] == b'Manufacturer' - assert success["model"] == b'Model' - assert failure == {} - - -def test_read_attributes_default_response(cluster): - @asyncio.coroutine - def mockrequest(foundation, command, schema, args): - assert foundation is True - assert command == 0 - return [0xc1] - - cluster.request = mockrequest - loop = asyncio.get_event_loop() - success, failure = loop.run_until_complete(cluster.read_attributes( - [0, 5, 23], - allow_cache=False, - )) - assert success == {} - assert failure == {0: 0xc1, 5: 0xc1, 23: 0xc1} - - -def test_item_access_attributes(cluster): - @asyncio.coroutine - def mockrequest(foundation, command, schema, args): - assert foundation is True - assert command == 0 - rar5 = _mk_rar(5, b'Model') - return [[rar5]] - - cluster.request = mockrequest - cluster._attr_cache[0] = 99 - - @asyncio.coroutine - def inner(): - v = yield from cluster['model'] - assert v == b'Model' - v = yield from cluster['zcl_version'] - assert v == 99 - with pytest.raises(KeyError): - v = yield from cluster[99] - - loop = asyncio.get_event_loop() - loop.run_until_complete(inner()) - - -def test_write_attributes(cluster): - cluster.write_attributes({0: 5, 'app_version': 4}) - assert cluster._endpoint.device.request.call_count == 1 - - -def test_write_wrong_attribute(cluster): - cluster.write_attributes({0xff: 5}) - assert cluster._endpoint.device.request.call_count == 1 - - -def test_write_attributes_wrong_type(cluster): - cluster.write_attributes({18: 2}) - assert cluster._endpoint.device.request.call_count == 1 - - -def test_write_attributes_report(cluster): - cluster.write_attributes({0: 5}, is_report=True) - assert cluster._endpoint.device.reply.call_count == 1 - - -def test_bind(cluster): - cluster.bind() - - -def test_unbind(cluster): - cluster.unbind() - - -def test_configure_reporting(cluster): - cluster.configure_reporting(0, 10, 20, 1) - - -def test_command(cluster): - cluster.command(0x00) - assert cluster._endpoint.device.request.call_count == 1 - - -def test_command_attr(cluster): - cluster.reset_fact_default() - assert cluster._endpoint.device.request.call_count == 1 - - -def test_client_command_attr(client_cluster): - client_cluster.identify_query_response(0) - assert client_cluster._endpoint.device.reply.call_count == 1 - - -def test_command_invalid_attr(cluster): - with pytest.raises(AttributeError): - cluster.no_such_command() - - -def test_invalid_arguments_cluster_command(cluster): - res = cluster.command(0x00, 1) - assert type(res.exception()) == ValueError - - -def test_invalid_arguments_cluster_client_command(client_cluster): - res = client_cluster.client_command(0, 0, 0) - assert type(res.exception()) == ValueError - - -def test_name(cluster): - assert cluster.name == 'Basic' - - -def test_commands(cluster): - assert cluster.commands == ["reset_fact_default"] diff --git a/tests/test_zcl_clusters.py b/tests/test_zcl_clusters.py deleted file mode 100644 index 1399f1d8..00000000 --- a/tests/test_zcl_clusters.py +++ /dev/null @@ -1,85 +0,0 @@ -import re - -from unittest import mock -import bellows.zigbee.endpoint -import bellows.zigbee.zcl as zcl - - -def test_registry(): - for cluster_id, cluster in zcl.Cluster._registry.items(): - assert 0 <= getattr(cluster, 'cluster_id', -1) <= 65535 - assert cluster_id == cluster.cluster_id - assert issubclass(cluster, zcl.Cluster) - - -def test_attributes(): - for cluster_id, cluster in zcl.Cluster._registry.items(): - for attrid, attrspec in cluster.attributes.items(): - assert 0 <= attrid <= 0xffff - assert isinstance(attrspec, tuple) - assert isinstance(attrspec[0], str) - assert hasattr(attrspec[1], 'serialize') - assert hasattr(attrspec[1], 'deserialize') - - -def _test_commands(cmdattr): - for cluster_id, cluster in zcl.Cluster._registry.items(): - print(cluster.cluster_id) - for cmdid, cmdspec in getattr(cluster, cmdattr).items(): - assert 0 <= cmdid <= 0xff - assert isinstance(cmdspec, tuple), "Cluster %s" % (cluster_id, ) - assert len(cmdspec) == 3 - assert isinstance(cmdspec[0], str) - assert isinstance(cmdspec[1], tuple) - assert isinstance(cmdspec[2], bool) - for t in cmdspec[1]: - assert hasattr(t, 'serialize') - assert hasattr(t, 'deserialize') - - -def test_server_commands(): - _test_commands('server_commands') - - -def test_client_commands(): - _test_commands('client_commands') - - -def test_ep_attributes(): - seen = set() - for cluster_id, cluster in zcl.Cluster._registry.items(): - assert isinstance(cluster.ep_attribute, str) - assert re.match('^[a-z_]+$', cluster.ep_attribute) - assert cluster.ep_attribute not in seen - seen.add(cluster.ep_attribute) - - ep = bellows.zigbee.endpoint.Endpoint(None, 1) - assert not hasattr(ep, cluster.ep_attribute) - - -def test_time_cluster(): - ep = mock.MagicMock() - ep.device = mock.MagicMock() - t = zcl.Cluster._registry[0x000a](ep) - - aps_frame = 0 - tsn = 0 - - t.handle_cluster_request(aps_frame, tsn, 1, [[0]]) - assert ep.device.reply.call_count == 0 - - t.handle_cluster_request(aps_frame, tsn, 0, [[0]]) - assert ep.device.reply.call_count == 1 - assert ep.device.reply.call_args[0][1][3] == 0 - - t.handle_cluster_request(aps_frame, tsn, 0, [[1]]) - assert ep.device.reply.call_count == 2 - assert ep.device.reply.call_args[0][1][3] == 1 - - t.handle_cluster_request(aps_frame, tsn, 0, [[2]]) - assert ep.device.reply.call_count == 3 - assert ep.device.reply.call_args[0][1][3] == 2 - - t.handle_cluster_request(aps_frame, tsn, 0, [[0, 1, 2]]) - assert ep.device.reply.call_count == 4 - assert ep.device.reply.call_args[0][1][3] == 0 diff --git a/tests/test_zcl_foundation.py b/tests/test_zcl_foundation.py deleted file mode 100644 index 9cd64625..00000000 --- a/tests/test_zcl_foundation.py +++ /dev/null @@ -1,64 +0,0 @@ -import bellows.types as t -from bellows.zigbee.zcl import foundation - - -def test_typevalue(): - tv = foundation.TypeValue() - tv.type = 0x20 - tv.value = t.uint8_t(99) - ser = tv.serialize() - - tv2, data = foundation.TypeValue.deserialize(ser) - assert data == b'' - assert tv2.type == tv.type - assert tv2.value == tv.value - - -def test_read_attribute_record(): - orig = b'\x00\x00\x00\x20\x99' - rar, data = foundation.ReadAttributeRecord.deserialize(orig) - assert data == b'' - assert rar.status == 0 - assert isinstance(rar.value, foundation.TypeValue) - assert isinstance(rar.value.value, t.uint8_t) - assert rar.value.value == 0x99 - - r = repr(rar) - assert len(r) > 5 - assert r.startswith('<') and r.endswith('>') - - ser = rar.serialize() - assert ser == orig - - -def test_attribute_reporting_config_0(): - arc = foundation.AttributeReportingConfig() - arc.direction = 0 - arc.attrid = 99 - arc.datatype = 0x20 - arc.min_interval = 10 - arc.max_interval = 20 - arc.reportable_change = 30 - ser = arc.serialize() - - arc2, data = foundation.AttributeReportingConfig.deserialize(ser) - assert data == b'' - assert arc2.direction == arc.direction - assert arc2.attrid == arc.attrid - assert arc2.datatype == arc.datatype - assert arc2.min_interval == arc.min_interval - assert arc2.max_interval == arc.max_interval - assert arc.reportable_change == arc.reportable_change - - -def test_attribute_reporting_config_1(): - arc = foundation.AttributeReportingConfig() - arc.direction = 1 - arc.attrid = 99 - arc.timeout = 0x7e - ser = arc.serialize() - - arc2, data = foundation.AttributeReportingConfig.deserialize(ser) - assert data == b'' - assert arc2.direction == arc.direction - assert arc2.timeout == arc.timeout diff --git a/tests/test_zdo.py b/tests/test_zdo.py deleted file mode 100644 index 19656d87..00000000 --- a/tests/test_zdo.py +++ /dev/null @@ -1,117 +0,0 @@ -from unittest import mock - -import pytest - -import bellows.types as t -import bellows.zigbee.device -import bellows.zigbee.zdo as zdo - - -def test_commands(): - for cmdid, cmdspec in zdo.types.CLUSTERS.items(): - assert 0 <= cmdid <= 0xffff - assert isinstance(cmdspec, tuple) - assert isinstance(cmdspec[0], str) - for paramname, paramtype in zip(cmdspec[1], cmdspec[2]): - assert isinstance(paramname, str) - assert hasattr(paramtype, 'serialize') - assert hasattr(paramtype, 'deserialize') - - -def test_deserialize(): - tsn, command_id, is_reply, args = zdo.deserialize(2, b'\x01\x02\x03xx') - assert tsn == 1 - assert is_reply is False - assert args == [0x0302] - - -def test_deserialize_unknown(): - tsn, command_id, is_reply, args = zdo.deserialize(0x0100, b'\x01') - assert tsn == 1 - assert is_reply is False - - -@pytest.fixture -def zdo_f(): - app = mock.MagicMock() - app.ieee = t.EmberEUI64(map(t.uint8_t, [8, 9, 10, 11, 12, 13, 14, 15])) - app.get_sequence = mock.MagicMock(return_value=123) - ieee = t.EmberEUI64(map(t.uint8_t, [0, 1, 2, 3, 4, 5, 6, 7])) - dev = bellows.zigbee.device.Device(app, ieee, 65535) - return zdo.ZDO(dev) - - -def test_request(zdo_f): - zdo_f.request(2, 65535) - app_mock = zdo_f._device._application - assert app_mock.request.call_count == 1 - assert app_mock.get_sequence.call_count == 1 - - -def test_bind(zdo_f): - zdo_f.bind(1, 1026) - app_mock = zdo_f._device._application - assert app_mock.request.call_count == 1 - assert app_mock.request.call_args[0][1].clusterId == 0x0021 - - -def test_unbind(zdo_f): - zdo_f.unbind(1, 1026) - app_mock = zdo_f._device._application - assert app_mock.request.call_count == 1 - assert app_mock.request.call_args[0][1].clusterId == 0x0022 - - -def test_leave(zdo_f): - zdo_f.leave() - app_mock = zdo_f._device._application - assert app_mock.request.call_count == 1 - assert app_mock.request.call_args[0][1].clusterId == 0x0034 - - -def _handle_match_desc(zdo_f, profile): - zdo_f.reply = mock.MagicMock() - aps = t.EmberApsFrame() - zdo_f.handle_message(False, aps, 123, 0x0006, [None, profile, [], []]) - assert zdo_f.reply.call_count == 1 - - -def test_handle_match_desc_zha(zdo_f): - return _handle_match_desc(zdo_f, 260) - - -def test_handle_match_desc_generic(zdo_f): - return _handle_match_desc(zdo_f, 0) - - -def test_handle_addr(zdo_f): - aps = t.EmberApsFrame() - nwk = zdo_f._device.application.nwk - zdo_f.reply = mock.MagicMock() - zdo_f.handle_message(False, aps, 234, 0x0001, [nwk]) - assert zdo_f.reply.call_count == 1 - - -def test_handle_announce(zdo_f): - dev = zdo_f._device - zdo_f.listener_event = mock.MagicMock() - dev._application.devices.pop(dev.ieee) - aps = t.EmberApsFrame() - zdo_f.handle_message(False, aps, 111, 0x0013, [0, dev.ieee, dev.nwk]) - assert zdo_f.listener_event.call_count == 1 - - -def test_handle_permit_join(zdo_f): - aps = t.EmberApsFrame() - zdo_f.listener_event = mock.MagicMock() - zdo_f.handle_message(False, aps, 111, 0x0036, [100, 1]) - assert zdo_f.listener_event.call_count == 1 - - -def test_handle_unsupported(zdo_f): - aps = t.EmberApsFrame() - zdo_f.handle_message(False, aps, 321, 0xffff, []) - - -def test_device_accessor(zdo_f): - assert zdo_f.device.nwk == 65535 diff --git a/tests/test_zdo_types.py b/tests/test_zdo_types.py deleted file mode 100644 index 9caf9cb5..00000000 --- a/tests/test_zdo_types.py +++ /dev/null @@ -1,69 +0,0 @@ -import pytest - -import bellows.types as t -import bellows.zigbee.zdo.types as types - - -def test_multi_address_3(): - ma = types.MultiAddress() - ma.addrmode = 3 - ma.ieee = t.EmberEUI64(map(t.uint8_t, [0, 1, 2, 3, 4, 5, 6, 7])) - ma.endpoint = 1 - ser = ma.serialize() - - ma2, data = types.MultiAddress.deserialize(ser) - assert data == b'' - assert ma2.addrmode == ma.addrmode - assert ma2.ieee == ma.ieee - assert ma2.endpoint == ma.endpoint - - -def test_multi_address_1(): - ma = types.MultiAddress() - ma.addrmode = 1 - ma.nwk = 123 - ser = ma.serialize() - - ma2, data = types.MultiAddress.deserialize(ser) - assert data == b'' - assert ma2.addrmode == ma.addrmode - assert ma2.nwk == ma.nwk - - -def test_multi_address_invalid(): - ma = types.MultiAddress() - ma.addrmode = 255 - with pytest.raises(ValueError): - ma.serialize() - - with pytest.raises(ValueError): - types.MultiAddress.deserialize(b'\xffnot read') - - -def test_node_descriptor(): - data = b'\x00\x00\x01\x02\x02\x03\x04\x04\x05\x05\x06\x06\x07\xff' - nd, data = types.NodeDescriptor.deserialize(data) - - assert data == b'\xff' - - -def test_size_prefixed_simple_descriptor(): - sd = types.SizePrefixedSimpleDescriptor() - sd.endpoint = t.uint8_t(1) - sd.profile = t.uint16_t(2) - sd.device_type = t.uint16_t(3) - sd.device_version = t.uint8_t(4) - sd.input_clusters = t.LVList(t.uint16_t)([t.uint16_t(5), t.uint16_t(6)]) - sd.output_clusters = t.LVList(t.uint16_t)([t.uint16_t(7), t.uint16_t(8)]) - - ser = sd.serialize() - assert ser[0] == len(ser) - 1 - - sd2, data = types.SizePrefixedSimpleDescriptor.deserialize(ser) - assert sd.input_clusters == sd2.input_clusters - assert sd.output_clusters == sd2.output_clusters - - -def test_empty_size_prefixed_simple_descriptor(): - r = types.SizePrefixedSimpleDescriptor.deserialize(b'\x00') - assert r == (None, b'') diff --git a/tests/test_zigbee_util.py b/tests/test_zigbee_util.py deleted file mode 100644 index 7ecee02d..00000000 --- a/tests/test_zigbee_util.py +++ /dev/null @@ -1,154 +0,0 @@ -import asyncio -from unittest import mock - -import pytest - -from bellows.zigbee import util - - -class Listenable(util.ListenableMixin): - def __init__(self): - self._listeners = {} - - -def test_listenable(): - listen = Listenable() - listener = mock.MagicMock() - listen.add_listener(listener) - listen.add_listener(listener) - - broken_listener = mock.MagicMock() - broken_listener.event.side_effect = Exception() - listen.add_listener(broken_listener) - - listen.listener_event('event') - assert listener.event.call_count == 2 - assert broken_listener.event.call_count == 1 - - -class Logger(util.LocalLogMixin): - log = mock.MagicMock() - - -def test_log(): - log = Logger() - log.debug("Test debug") - log.info("Test info") - log.warn("Test warn") - log.error("Test error") - - -def test_zha_security_end_device(): - util.zha_security(controller=False) - - -def test_zha_security_controller(): - util.zha_security(controller=True) - - -def _test_retry(exception, retry_exceptions, n): - counter = 0 - - @asyncio.coroutine - def count(): - nonlocal counter - counter += 1 - if counter <= n: - exc = exception() - exc._counter = counter - raise exc - - loop = asyncio.get_event_loop() - loop.run_until_complete(util.retry(count, retry_exceptions)) - return counter - - -def test_retry_no_retries(): - counter = _test_retry(Exception, Exception, 0) - assert counter == 1 - - -def test_retry_always(): - with pytest.raises(ValueError) as exc_info: - _test_retry(ValueError, (IndexError, ValueError), 999) - assert exc_info.value._counter == 3 - - -def test_retry_once(): - counter = _test_retry(ValueError, ValueError, 1) - assert counter == 2 - - -def _test_retryable(exception, retry_exceptions, n, tries=3, delay=0.001): - counter = 0 - - @util.retryable(retry_exceptions) - @asyncio.coroutine - def count(x, y, z): - assert x == y == z == 9 - nonlocal counter - counter += 1 - if counter <= n: - exc = exception() - exc._counter = counter - raise exc - - loop = asyncio.get_event_loop() - loop.run_until_complete(count(9, 9, 9, tries=tries, delay=delay)) - return counter - - -def test_retryable_no_retry(): - counter = _test_retryable(Exception, Exception, 0, 0, 0) - assert counter == 1 - - -def test_retryable_exception_no_retry(): - with pytest.raises(Exception) as exc_info: - _test_retryable(Exception, Exception, 1, 0, 0) - assert exc_info.value._counter == 1 - - -def test_retryable_no_retries(): - counter = _test_retryable(Exception, Exception, 0) - assert counter == 1 - - -def test_retryable_always(): - with pytest.raises(ValueError) as exc_info: - _test_retryable(ValueError, (IndexError, ValueError), 999) - assert exc_info.value._counter == 3 - - -def test_retryable_once(): - counter = _test_retryable(ValueError, ValueError, 1) - assert counter == 2 - - -def test_zigbee_security_hash(): - message = bytes([0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x4A, 0xF7]) - key = util.aes_mmo_hash(message) - assert key.contents == [0x41, 0x61, 0x8F, 0xC0, 0xC8, 0x3B, 0x0E, 0x14, 0xA5, 0x89, 0x95, 0x4B, 0x16, 0xE3, 0x14, 0x66] - - message = bytes([0x7A, 0x93, 0x97, 0x23, 0xA5, 0xC6, 0x39, 0xB2, 0x69, 0x16, 0x18, 0x02, 0x81, 0x9B]) - key = util.aes_mmo_hash(message) - assert key.contents == [0xF9, 0x39, 0x03, 0x72, 0x16, 0x85, 0xFD, 0x32, 0x9D, 0x26, 0x84, 0x9B, 0x90, 0xF2, 0x95, 0x9A] - - message = bytes([0x83, 0xFE, 0xD3, 0x40, 0x7A, 0x93, 0x97, 0x23, 0xA5, 0xC6, 0x39, 0xB2, 0x69, 0x16, 0x18, 0x02, 0xAE, 0xBB]) - key = util.aes_mmo_hash(message) - assert key.contents == [0x33, 0x3C, 0x23, 0x68, 0x60, 0x79, 0x46, 0x8E, 0xB2, 0x7B, 0xA2, 0x4B, 0xD9, 0xC7, 0xE5, 0x64] - - -def test_convert_install_code(): - message = bytes([0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x4A, 0xF7]) - key = util.convert_install_code(message) - assert key.contents == [0x41, 0x61, 0x8F, 0xC0, 0xC8, 0x3B, 0x0E, 0x14, 0xA5, 0x89, 0x95, 0x4B, 0x16, 0xE3, 0x14, 0x66] - - -def test_fail_convert_install_code(): - key = util.convert_install_code(bytes([])) - assert key is None - - message = bytes([0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0xFF, 0xFF]) - key = util.convert_install_code(message) - assert key is None diff --git a/tests/util.py b/tests/util.py deleted file mode 100644 index 3346602c..00000000 --- a/tests/util.py +++ /dev/null @@ -1,3 +0,0 @@ -class MockApplication(): - def get_sequence(self): - return 123