From ae1bcd5fef18d3e829f0dd7dbdf769db65916877 Mon Sep 17 00:00:00 2001 From: "David F. Mulcahey" Date: Thu, 6 Jun 2019 08:31:03 -0400 Subject: [PATCH] Use node descriptor from Zigpy for ZHA (#24316) * use zigpy node descriptor * cleanup --- .../components/zha/core/channels/__init__.py | 50 ------------------- .../components/zha/core/channels/general.py | 4 +- homeassistant/components/zha/core/const.py | 2 + homeassistant/components/zha/core/device.py | 50 ++++++++++++------- homeassistant/components/zha/core/gateway.py | 9 +--- homeassistant/components/zha/core/store.py | 15 ------ homeassistant/components/zha/entity.py | 3 +- tests/components/zha/common.py | 2 + 8 files changed, 40 insertions(+), 95 deletions(-) diff --git a/homeassistant/components/zha/core/channels/__init__.py b/homeassistant/components/zha/core/channels/__init__.py index 1845ae8e999..3eb24050195 100644 --- a/homeassistant/components/zha/core/channels/__init__.py +++ b/homeassistant/components/zha/core/channels/__init__.py @@ -22,10 +22,6 @@ from ..const import ( ) from ..registries import CLUSTER_REPORT_CONFIGS -NODE_DESCRIPTOR_REQUEST = 0x0002 -MAINS_POWERED = 1 -BATTERY_OR_UNKNOWN = 0 - ZIGBEE_CHANNEL_REGISTRY = {} _LOGGER = logging.getLogger(__name__) @@ -268,11 +264,6 @@ class AttributeListeningChannel(ZigbeeChannel): class ZDOChannel: """Channel for ZDO events.""" - POWER_SOURCES = { - MAINS_POWERED: 'Mains', - BATTERY_OR_UNKNOWN: 'Battery or Unknown' - } - def __init__(self, cluster, device): """Initialize ZDOChannel.""" self.name = ZDO_CHANNEL @@ -281,8 +272,6 @@ class ZDOChannel: self._status = ChannelStatus.CREATED self._unique_id = "{}_ZDO".format(device.name) self._cluster.add_listener(self) - self.power_source = None - self.manufacturer_code = None @property def unique_id(self): @@ -314,49 +303,10 @@ class ZDOChannel: entry = self._zha_device.gateway.zha_storage.async_get_or_create( self._zha_device) _LOGGER.debug("entry loaded from storage: %s", entry) - if entry is not None: - self.power_source = entry.power_source - self.manufacturer_code = entry.manufacturer_code - - if self.power_source is None: - self.power_source = BATTERY_OR_UNKNOWN - - if self.manufacturer_code is None and not from_cache: - # this should always be set. This is from us not doing - # this previously so lets set it up so users don't have - # to reconfigure every device. - await self.async_get_node_descriptor(False) - entry = self._zha_device.gateway.zha_storage.async_update( - self._zha_device) - _LOGGER.debug("entry after getting node desc in init: %s", entry) self._status = ChannelStatus.INITIALIZED - async def async_get_node_descriptor(self, from_cache): - """Request the node descriptor from the device.""" - from zigpy.zdo.types import Status - - if from_cache: - return - - node_descriptor = await self._cluster.request( - NODE_DESCRIPTOR_REQUEST, - self._cluster.device.nwk, tries=3, delay=2) - - def get_bit(byteval, idx): - return int(((byteval & (1 << idx)) != 0)) - - if node_descriptor is not None and\ - node_descriptor[0] == Status.SUCCESS: - mac_capability_flags = node_descriptor[2].mac_capability_flags - - self.power_source = get_bit(mac_capability_flags, 2) - self.manufacturer_code = node_descriptor[2].manufacturer_code - - _LOGGER.debug("node descriptor: %s", node_descriptor) - async def async_configure(self): """Configure channel.""" - await self.async_get_node_descriptor(False) self._status = ChannelStatus.CONFIGURED diff --git a/homeassistant/components/zha/core/channels/general.py b/homeassistant/components/zha/core/channels/general.py index 470cd6b38cf..3f08a738a13 100644 --- a/homeassistant/components/zha/core/channels/general.py +++ b/homeassistant/components/zha/core/channels/general.py @@ -8,7 +8,7 @@ import logging from homeassistant.core import callback from homeassistant.helpers.dispatcher import async_dispatcher_send from homeassistant.helpers.event import async_call_later -from . import ZigbeeChannel, parse_and_log_command, MAINS_POWERED +from . import ZigbeeChannel, parse_and_log_command from ..helpers import get_attr_id_by_name from ..const import ( SIGNAL_ATTR_UPDATED, SIGNAL_MOVE_LEVEL, SIGNAL_SET_LEVEL, @@ -87,7 +87,7 @@ class OnOffChannel(ZigbeeChannel): async def async_update(self): """Initialize channel.""" - from_cache = not self.device.power_source == MAINS_POWERED + from_cache = not self.device.is_mains_powered _LOGGER.debug( "%s is attempting to update onoff state - from cache: %s", self._unique_id, diff --git a/homeassistant/components/zha/core/const.py b/homeassistant/components/zha/core/const.py index 193780c9124..9e42f6343a1 100644 --- a/homeassistant/components/zha/core/const.py +++ b/homeassistant/components/zha/core/const.py @@ -104,6 +104,8 @@ QUIRK_APPLIED = 'quirk_applied' QUIRK_CLASS = 'quirk_class' MANUFACTURER_CODE = 'manufacturer_code' POWER_SOURCE = 'power_source' +MAINS_POWERED = 'Mains' +BATTERY_OR_UNKNOWN = 'Battery or Unknown' BELLOWS = 'bellows' ZHA = 'homeassistant.components.zha' diff --git a/homeassistant/components/zha/core/device.py b/homeassistant/components/zha/core/device.py index 1a619dff981..85373517aa2 100644 --- a/homeassistant/components/zha/core/device.py +++ b/homeassistant/components/zha/core/device.py @@ -17,9 +17,10 @@ from .const import ( ATTR_CLUSTER_ID, ATTR_ATTRIBUTE, ATTR_VALUE, ATTR_COMMAND, SERVER, ATTR_COMMAND_TYPE, ATTR_ARGS, CLIENT_COMMANDS, SERVER_COMMANDS, ATTR_ENDPOINT_ID, IEEE, MODEL, NAME, UNKNOWN, QUIRK_APPLIED, - QUIRK_CLASS, ZDO_CHANNEL, MANUFACTURER_CODE, POWER_SOURCE + QUIRK_CLASS, ZDO_CHANNEL, MANUFACTURER_CODE, POWER_SOURCE, MAINS_POWERED, + BATTERY_OR_UNKNOWN ) -from .channels import EventRelayChannel, ZDOChannel +from .channels import EventRelayChannel _LOGGER = logging.getLogger(__name__) @@ -68,7 +69,6 @@ class ZHADevice: self._zigpy_device.__class__.__module__, self._zigpy_device.__class__.__name__ ) - self._power_source = None self.status = DeviceStatus.CREATED @property @@ -91,6 +91,13 @@ class ZHADevice: """Return model for device.""" return self._model + @property + def manufacturer_code(self): + """Return the manufacturer code for the device.""" + if self._zigpy_device.node_desc.is_valid: + return self._zigpy_device.node_desc.manufacturer_code + return None + @property def nwk(self): """Return nwk for device.""" @@ -112,20 +119,29 @@ class ZHADevice: return self._zigpy_device.last_seen @property - def manufacturer_code(self): - """Return manufacturer code for device.""" - if ZDO_CHANNEL in self.cluster_channels: - return self.cluster_channels.get(ZDO_CHANNEL).manufacturer_code - return None + def is_mains_powered(self): + """Return true if device is mains powered.""" + return self._zigpy_device.node_desc.is_mains_powered @property def power_source(self): """Return the power source for the device.""" - if self._power_source is not None: - return self._power_source - if ZDO_CHANNEL in self.cluster_channels: - return self.cluster_channels.get(ZDO_CHANNEL).power_source - return None + return MAINS_POWERED if self.is_mains_powered else BATTERY_OR_UNKNOWN + + @property + def is_router(self): + """Return true if this is a routing capable device.""" + return self._zigpy_device.node_desc.is_router + + @property + def is_coordinator(self): + """Return true if this device represents the coordinator.""" + return self._zigpy_device.node_desc.is_coordinator + + @property + def is_end_device(self): + """Return true if this device is an end device.""" + return self._zigpy_device.node_desc.is_end_device @property def gateway(self): @@ -151,10 +167,6 @@ class ZHADevice: """Set availability from restore and prevent signals.""" self._available = available - def set_power_source(self, power_source): - """Set the power source.""" - self._power_source = power_source - def update_available(self, available): """Set sensor availability.""" if self._available != available and available: @@ -183,7 +195,7 @@ class ZHADevice: QUIRK_APPLIED: self.quirk_applied, QUIRK_CLASS: self.quirk_class, MANUFACTURER_CODE: self.manufacturer_code, - POWER_SOURCE: ZDOChannel.POWER_SOURCES.get(self.power_source) + POWER_SOURCE: self.power_source } def add_cluster_channel(self, cluster_channel): @@ -256,7 +268,7 @@ class ZHADevice: _LOGGER.debug( '%s: power source: %s', self.name, - ZDOChannel.POWER_SOURCES.get(self.power_source) + self.power_source ) self.status = DeviceStatus.INITIALIZED _LOGGER.debug('%s: completed initialization', self.name) diff --git a/homeassistant/components/zha/core/gateway.py b/homeassistant/components/zha/core/gateway.py index 740cd450181..f8458848fc2 100644 --- a/homeassistant/components/zha/core/gateway.py +++ b/homeassistant/components/zha/core/gateway.py @@ -18,7 +18,6 @@ from homeassistant.helpers.dispatcher import async_dispatcher_send from homeassistant.helpers.entity_component import EntityComponent from ..api import async_get_device_info -from .channels import MAINS_POWERED, ZDOChannel from .const import ( ADD_DEVICE_RELAY_LOGGERS, ATTR_MANUFACTURER, BELLOWS, CONF_BAUDRATE, CONF_DATABASE, CONF_RADIO_TYPE, CONF_USB_PATH, CONTROLLER, CURRENT, @@ -234,7 +233,6 @@ class ZHAGateway: if not is_new_join: entry = self.zha_storage.async_get_or_create(zha_device) zha_device.async_update_last_seen(entry.last_seen) - zha_device.set_power_source(entry.power_source) return zha_device @callback @@ -290,16 +288,13 @@ class ZHAGateway: # configure the device await zha_device.async_configure() zha_device.update_available(True) - elif zha_device.power_source is not None\ - and zha_device.power_source == MAINS_POWERED: + elif zha_device.is_mains_powered: # the device isn't a battery powered device so we should be able # to update it now _LOGGER.debug( "attempting to request fresh state for %s %s", zha_device.name, - "with power source: {}".format( - ZDOChannel.POWER_SOURCES.get(zha_device.power_source) - ) + "with power source: {}".format(zha_device.power_source) ) await zha_device.async_initialize(from_cache=False) else: diff --git a/homeassistant/components/zha/core/store.py b/homeassistant/components/zha/core/store.py index f3547cea8a4..c14345e89dd 100644 --- a/homeassistant/components/zha/core/store.py +++ b/homeassistant/components/zha/core/store.py @@ -26,8 +26,6 @@ class ZhaDeviceEntry: name = attr.ib(type=str, default=None) ieee = attr.ib(type=str, default=None) - power_source = attr.ib(type=int, default=None) - manufacturer_code = attr.ib(type=int, default=None) last_seen = attr.ib(type=float, default=None) @@ -46,8 +44,6 @@ class ZhaDeviceStorage: device_entry = ZhaDeviceEntry( name=device.name, ieee=str(device.ieee), - power_source=device.power_source, - manufacturer_code=device.manufacturer_code, last_seen=device.last_seen ) @@ -85,13 +81,6 @@ class ZhaDeviceStorage: old = self.devices[ieee_str] changes = {} - - if device.power_source != old.power_source: - changes['power_source'] = device.power_source - - if device.manufacturer_code != old.manufacturer_code: - changes['manufacturer_code'] = device.manufacturer_code - changes['last_seen'] = device.last_seen new = self.devices[ieee_str] = attr.evolve(old, **changes) @@ -109,8 +98,6 @@ class ZhaDeviceStorage: devices[device['ieee']] = ZhaDeviceEntry( name=device['name'], ieee=device['ieee'], - power_source=device['power_source'], - manufacturer_code=device['manufacturer_code'], last_seen=device['last_seen'] if 'last_seen' in device else None ) @@ -135,8 +122,6 @@ class ZhaDeviceStorage: { 'name': entry.name, 'ieee': entry.ieee, - 'power_source': entry.power_source, - 'manufacturer_code': entry.manufacturer_code, 'last_seen': entry.last_seen } for entry in self.devices.values() ] diff --git a/homeassistant/components/zha/entity.py b/homeassistant/components/zha/entity.py index d894ef5d7a3..36df8aada2b 100644 --- a/homeassistant/components/zha/entity.py +++ b/homeassistant/components/zha/entity.py @@ -14,7 +14,6 @@ from .core.const import ( DOMAIN, ATTR_MANUFACTURER, DATA_ZHA, DATA_ZHA_BRIDGE_ID, MODEL, NAME, SIGNAL_REMOVE ) -from .core.channels import MAINS_POWERED _LOGGER = logging.getLogger(__name__) @@ -157,7 +156,7 @@ class ZhaEntity(RestoreEntity, entity.Entity): time.time() - self._zha_device.last_seen < RESTART_GRACE_PERIOD): self.async_set_available(True) - if self.zha_device.power_source != MAINS_POWERED: + if not self.zha_device.is_mains_powered: # mains powered devices will get real time state self.async_restore_last_state(last_state) self._zha_device.set_available(True) diff --git a/tests/components/zha/common.py b/tests/components/zha/common.py index cd2eb53c3fe..4cc7dec1edf 100644 --- a/tests/components/zha/common.py +++ b/tests/components/zha/common.py @@ -82,6 +82,8 @@ class FakeDevice: self.initializing = False self.manufacturer = manufacturer self.model = model + from zigpy.zdo.types import NodeDescriptor + self.node_desc = NodeDescriptor() def make_device(in_cluster_ids, out_cluster_ids, device_type, ieee,