From cece6454e4688ad06dc23fbbbaccaaed1961ab3c Mon Sep 17 00:00:00 2001 From: "David F. Mulcahey" Date: Wed, 20 Feb 2019 10:33:29 -0500 Subject: [PATCH] Fix bug in ZHA and tweak non sensor channel logic (#21234) * fix race condition and prevent profiles from stealing channels * fix battery voltage --- .../components/zha/core/channels/general.py | 2 +- homeassistant/components/zha/core/device.py | 10 +++++++++ homeassistant/components/zha/core/gateway.py | 21 +++++++++++-------- 3 files changed, 23 insertions(+), 10 deletions(-) diff --git a/homeassistant/components/zha/core/channels/general.py b/homeassistant/components/zha/core/channels/general.py index bc015ae47f0..a29b23d340b 100644 --- a/homeassistant/components/zha/core/channels/general.py +++ b/homeassistant/components/zha/core/channels/general.py @@ -199,4 +199,4 @@ class PowerConfigurationChannel(ZigbeeChannel): await self.get_attribute_value( 'battery_percentage_remaining', from_cache=from_cache) await self.get_attribute_value( - 'active_power', from_cache=from_cache) + 'battery_voltage', from_cache=from_cache) diff --git a/homeassistant/components/zha/core/device.py b/homeassistant/components/zha/core/device.py index 3a012ed7895..12bb397fbc3 100644 --- a/homeassistant/components/zha/core/device.py +++ b/homeassistant/components/zha/core/device.py @@ -5,6 +5,7 @@ For more details about this component, please refer to the documentation at https://home-assistant.io/components/zha/ """ import asyncio +from enum import Enum import logging from homeassistant.helpers.dispatcher import ( @@ -23,6 +24,13 @@ from .channels.general import BasicChannel _LOGGER = logging.getLogger(__name__) +class DeviceStatus(Enum): + """Status of a device.""" + + CREATED = 1 + INITIALIZED = 2 + + class ZHADevice: """ZHA Zigbee device object.""" @@ -61,6 +69,7 @@ class ZHADevice: self._zigpy_device.__class__.__name__ ) self.power_source = None + self.status = DeviceStatus.CREATED @property def name(self): @@ -186,6 +195,7 @@ class ZHADevice: self.name, BasicChannel.POWER_SOURCES.get(self.power_source) ) + self.status = DeviceStatus.INITIALIZED _LOGGER.debug('%s: completed initialization', self.name) async def _execute_channel_tasks(self, task_name, *args): diff --git a/homeassistant/components/zha/core/gateway.py b/homeassistant/components/zha/core/gateway.py index cd549afc819..a50bfeae1be 100644 --- a/homeassistant/components/zha/core/gateway.py +++ b/homeassistant/components/zha/core/gateway.py @@ -23,7 +23,7 @@ from .const import ( REPORT_CONFIG_ASAP, REPORT_CONFIG_DEFAULT, REPORT_CONFIG_MIN_INT, REPORT_CONFIG_MAX_INT, REPORT_CONFIG_OP, SIGNAL_REMOVE, NO_SENSOR_CLUSTERS, POWER_CONFIGURATION_CHANNEL) -from .device import ZHADevice +from .device import ZHADevice, DeviceStatus from ..device_entity import ZhaDeviceEntity from .channels import ( AttributeListeningChannel, EventRelayChannel, ZDOChannel @@ -139,7 +139,9 @@ class ZHAGateway: """Update device that has just become available.""" if sender.ieee in self.devices: device = self.devices[sender.ieee] - device.update_available(True) + # avoid a race condition during new joins + if device.status is DeviceStatus.INITIALIZED: + device.update_available(True) async def async_device_initialized(self, device, is_new_join): """Handle device joined and basic information discovered (async).""" @@ -323,6 +325,14 @@ async def _handle_single_cluster_matches(hass, endpoint, zha_device, cluster_match_tasks = [] event_channel_tasks = [] for cluster in endpoint.in_clusters.values(): + # don't let profiles prevent these channels from being created + if cluster.cluster_id in NO_SENSOR_CLUSTERS: + cluster_match_tasks.append(_handle_channel_only_cluster_match( + zha_device, + cluster, + is_new_join, + )) + if cluster.cluster_id not in profile_clusters[0]: cluster_match_tasks.append(_handle_single_cluster_match( hass, @@ -333,13 +343,6 @@ async def _handle_single_cluster_matches(hass, endpoint, zha_device, is_new_join, )) - if cluster.cluster_id in NO_SENSOR_CLUSTERS: - cluster_match_tasks.append(_handle_channel_only_cluster_match( - zha_device, - cluster, - is_new_join, - )) - for cluster in endpoint.out_clusters.values(): if cluster.cluster_id not in profile_clusters[1]: cluster_match_tasks.append(_handle_single_cluster_match(