From 125ceb7449f5e2ee650fe06977105ae1b8fc2155 Mon Sep 17 00:00:00 2001 From: Alexei Chetroi Date: Fri, 4 Dec 2020 18:45:09 -0500 Subject: [PATCH] Refactor ZHA core channel initialization (#43953) * Cleanup Basic channnel Remove unused methods. * Refactor async_configure() method Split async_configure() into async_configure() and async_configure_channel_specfici() * Refactor async_initilize() method Split into two different methods and configure channel specifics via async_configure_channel_specific() * Fixes --- homeassistant/components/zha/binary_sensor.py | 12 ++---- .../components/zha/core/channels/base.py | 17 +++++--- .../components/zha/core/channels/closures.py | 10 ----- .../components/zha/core/channels/general.py | 43 ++++--------------- .../zha/core/channels/homeautomation.py | 16 +++---- .../components/zha/core/channels/hvac.py | 3 +- .../components/zha/core/channels/lighting.py | 12 +++--- .../components/zha/core/channels/security.py | 22 ++++------ .../zha/core/channels/smartenergy.py | 12 +++--- 9 files changed, 49 insertions(+), 98 deletions(-) diff --git a/homeassistant/components/zha/binary_sensor.py b/homeassistant/components/zha/binary_sensor.py index ba95a0e4bc6..48f35e035f0 100644 --- a/homeassistant/components/zha/binary_sensor.py +++ b/homeassistant/components/zha/binary_sensor.py @@ -73,13 +73,9 @@ class BinarySensor(ZhaEntity, BinarySensorEntity): self._channel = channels[0] self._device_class = self.DEVICE_CLASS - async def get_device_class(self): - """Get the HA device class from the channel.""" - async def async_added_to_hass(self): """Run when about to be added to hass.""" await super().async_added_to_hass() - await self.get_device_class() self.async_accept_signal( self._channel, SIGNAL_ATTR_UPDATED, self.async_set_state ) @@ -168,10 +164,10 @@ class IASZone(BinarySensor): SENSOR_ATTR = "zone_status" - async def get_device_class(self) -> None: - """Get the HA device class from the channel.""" - zone_type = await self._channel.get_attribute_value("zone_type") - self._device_class = CLASS_MAPPING.get(zone_type) + @property + def device_class(self) -> str: + """Return device class from component DEVICE_CLASSES.""" + return CLASS_MAPPING.get(self._channel.cluster.get("zone_type")) async def async_update(self): """Attempt to retrieve on off state from the binary sensor.""" diff --git a/homeassistant/components/zha/core/channels/base.py b/homeassistant/components/zha/core/channels/base.py index c6019c10843..2dbd1629487 100644 --- a/homeassistant/components/zha/core/channels/base.py +++ b/homeassistant/components/zha/core/channels/base.py @@ -187,29 +187,36 @@ class ZigbeeChannel(LogMixin): str(ex), ) - async def async_configure(self): + async def async_configure(self) -> None: """Set cluster binding and attribute reporting.""" if not self._ch_pool.skip_configuration: await self.bind() if self.cluster.is_server: await self.configure_reporting() + ch_specific_cfg = getattr(self, "async_configure_channel_specific", None) + if ch_specific_cfg: + await ch_specific_cfg() self.debug("finished channel configuration") else: self.debug("skipping channel configuration") self._status = ChannelStatus.CONFIGURED - async def async_initialize(self, from_cache): + async def async_initialize(self, from_cache: bool) -> None: """Initialize channel.""" if not from_cache and self._ch_pool.skip_configuration: self._status = ChannelStatus.INITIALIZED return self.debug("initializing channel: from_cache: %s", from_cache) - attributes = [] - for report_config in self._report_config: - attributes.append(report_config["attr"]) + attributes = [cfg["attr"] for cfg in self._report_config] if attributes: await self.get_attributes(attributes, from_cache=from_cache) + + ch_specific_init = getattr(self, "async_initialize_channel_specific", None) + if ch_specific_init: + await ch_specific_init(from_cache=from_cache) + + self.debug("finished channel configuration") self._status = ChannelStatus.INITIALIZED @callback diff --git a/homeassistant/components/zha/core/channels/closures.py b/homeassistant/components/zha/core/channels/closures.py index 0760427d46b..0326f18ac69 100644 --- a/homeassistant/components/zha/core/channels/closures.py +++ b/homeassistant/components/zha/core/channels/closures.py @@ -35,11 +35,6 @@ class DoorLockChannel(ZigbeeChannel): f"{self.unique_id}_{SIGNAL_ATTR_UPDATED}", attrid, attr_name, value ) - async def async_initialize(self, from_cache): - """Initialize channel.""" - await self.get_attribute_value(self._value_attribute, from_cache=from_cache) - await super().async_initialize(from_cache) - @registries.ZIGBEE_CHANNEL_REGISTRY.register(closures.Shade.cluster_id) class Shade(ZigbeeChannel): @@ -85,8 +80,3 @@ class WindowCovering(ZigbeeChannel): self.async_send_signal( f"{self.unique_id}_{SIGNAL_ATTR_UPDATED}", attrid, attr_name, value ) - - async def async_initialize(self, from_cache): - """Initialize channel.""" - await self.get_attribute_value(self._value_attribute, from_cache=from_cache) - await super().async_initialize(from_cache) diff --git a/homeassistant/components/zha/core/channels/general.py b/homeassistant/components/zha/core/channels/general.py index dc06d01e596..fa4883ae5a2 100644 --- a/homeassistant/components/zha/core/channels/general.py +++ b/homeassistant/components/zha/core/channels/general.py @@ -1,6 +1,6 @@ """General channels module for Zigbee Home Automation.""" import asyncio -from typing import Any, List, Optional +from typing import Any, Coroutine, List, Optional import zigpy.exceptions import zigpy.zcl.clusters.general as general @@ -19,7 +19,7 @@ from ..const import ( SIGNAL_SET_LEVEL, SIGNAL_UPDATE_DEVICE, ) -from .base import ChannelStatus, ClientChannel, ZigbeeChannel, parse_and_log_command +from .base import ClientChannel, ZigbeeChannel, parse_and_log_command @registries.ZIGBEE_CHANNEL_REGISTRY.register(general.Alarms.cluster_id) @@ -71,21 +71,6 @@ class BasicChannel(ZigbeeChannel): 6: "Emergency mains and transfer switch", } - async def async_configure(self): - """Configure this channel.""" - await super().async_configure() - await self.async_initialize(False) - - async def async_initialize(self, from_cache): - """Initialize channel.""" - if not self._ch_pool.skip_configuration or from_cache: - await self.get_attribute_value("power_source", from_cache=from_cache) - await super().async_initialize(from_cache) - - def get_power_source(self): - """Get the power source.""" - return self.cluster.get("power_source") - @registries.ZIGBEE_CHANNEL_REGISTRY.register(general.BinaryInput.cluster_id) class BinaryInput(ZigbeeChannel): @@ -189,11 +174,6 @@ class LevelControlChannel(ZigbeeChannel): """Dispatch level change.""" self.async_send_signal(f"{self.unique_id}_{command}", level) - async def async_initialize(self, from_cache): - """Initialize channel.""" - await self.get_attribute_value(self.CURRENT_LEVEL, from_cache=from_cache) - await super().async_initialize(from_cache) - @registries.ZIGBEE_CHANNEL_REGISTRY.register(general.MultistateInput.cluster_id) class MultistateInput(ZigbeeChannel): @@ -284,12 +264,9 @@ class OnOffChannel(ZigbeeChannel): ) self._state = bool(value) - async def async_initialize(self, from_cache): + async def async_initialize_channel_specific(self, from_cache: bool) -> None: """Initialize channel.""" - await super().async_initialize(from_cache) - state = await self.get_attribute_value(self.ON_OFF, from_cache=True) - if state is not None: - self._state = bool(state) + self._state = self.on_off async def async_update(self): """Initialize channel.""" @@ -338,7 +315,7 @@ class PollControl(ZigbeeChannel): CHECKIN_FAST_POLL_TIMEOUT = 2 * 4 # 2s LONG_POLL = 6 * 4 # 6s - async def async_configure(self) -> None: + async def async_configure_channel_specific(self) -> None: """Configure channel: set check-in interval.""" try: res = await self.cluster.write_attributes( @@ -347,7 +324,6 @@ class PollControl(ZigbeeChannel): self.debug("%ss check-in interval set: %s", self.CHECKIN_INTERVAL / 4, res) except (asyncio.TimeoutError, zigpy.exceptions.ZigbeeException) as ex: self.debug("Couldn't set check-in interval: %s", ex) - await super().async_configure() @callback def cluster_command( @@ -375,16 +351,13 @@ class PowerConfigurationChannel(ZigbeeChannel): {"attr": "battery_percentage_remaining", "config": REPORT_CONFIG_BATTERY_SAVE}, ) - async def async_initialize(self, from_cache): - """Initialize channel.""" + def async_initialize_channel_specific(self, from_cache: bool) -> Coroutine: + """Initialize channel specific attrs.""" attributes = [ "battery_size", - "battery_percentage_remaining", - "battery_voltage", "battery_quantity", ] - await self.get_attributes(attributes, from_cache=from_cache) - self._status = ChannelStatus.INITIALIZED + return self.get_attributes(attributes, from_cache=from_cache) @registries.ZIGBEE_CHANNEL_REGISTRY.register(general.PowerProfile.cluster_id) diff --git a/homeassistant/components/zha/core/channels/homeautomation.py b/homeassistant/components/zha/core/channels/homeautomation.py index 03812be0548..5b3a4778fcd 100644 --- a/homeassistant/components/zha/core/channels/homeautomation.py +++ b/homeassistant/components/zha/core/channels/homeautomation.py @@ -1,5 +1,5 @@ """Home automation channels module for Zigbee Home Automation.""" -from typing import Optional +from typing import Coroutine, Optional import zigpy.zcl.clusters.homeautomation as homeautomation @@ -62,23 +62,17 @@ class ElectricalMeasurementChannel(ZigbeeChannel): result, ) - async def async_initialize(self, from_cache): - """Initialize channel.""" - await self.fetch_config(True) - await super().async_initialize(from_cache) + def async_initialize_channel_specific(self, from_cache: bool) -> Coroutine: + """Initialize channel specific attributes.""" - async def fetch_config(self, from_cache): - """Fetch config from device and updates format specifier.""" - - # prime the cache - await self.get_attributes( + return self.get_attributes( [ "ac_power_divisor", "power_divisor", "ac_power_multiplier", "power_multiplier", ], - from_cache=from_cache, + from_cache=True, ) @property diff --git a/homeassistant/components/zha/core/channels/hvac.py b/homeassistant/components/zha/core/channels/hvac.py index ac832aacc61..f8f04414fa1 100644 --- a/homeassistant/components/zha/core/channels/hvac.py +++ b/homeassistant/components/zha/core/channels/hvac.py @@ -362,7 +362,7 @@ class ThermostatChannel(ZigbeeChannel): ) @retryable_req(delays=(1, 1, 3)) - async def async_initialize(self, from_cache): + async def async_initialize_channel_specific(self, from_cache: bool) -> None: """Initialize channel.""" cached = [a for a, cached in self._init_attrs.items() if cached] @@ -370,7 +370,6 @@ class ThermostatChannel(ZigbeeChannel): await self._chunk_attr_read(cached, cached=True) await self._chunk_attr_read(uncached, cached=False) - await super().async_initialize(from_cache) async def async_set_operation_mode(self, mode) -> bool: """Set Operation mode.""" diff --git a/homeassistant/components/zha/core/channels/lighting.py b/homeassistant/components/zha/core/channels/lighting.py index 16223582c33..c8827e20e01 100644 --- a/homeassistant/components/zha/core/channels/lighting.py +++ b/homeassistant/components/zha/core/channels/lighting.py @@ -1,5 +1,5 @@ """Lighting channels module for Zigbee Home Automation.""" -from typing import Optional +from typing import Coroutine, Optional import zigpy.zcl.clusters.lighting as lighting @@ -75,15 +75,13 @@ class ColorChannel(ZigbeeChannel): """Return the warmest color_temp that this channel supports.""" return self.cluster.get("color_temp_physical_max", self.MAX_MIREDS) - async def async_configure(self) -> None: + def async_configure_channel_specific(self) -> Coroutine: """Configure channel.""" - await self.fetch_color_capabilities(False) - await super().async_configure() + return self.fetch_color_capabilities(False) - async def async_initialize(self, from_cache: bool) -> None: + def async_initialize_channel_specific(self, from_cache: bool) -> Coroutine: """Initialize channel.""" - await self.fetch_color_capabilities(True) - await super().async_initialize(from_cache) + return self.fetch_color_capabilities(True) async def fetch_color_capabilities(self, from_cache: bool) -> None: """Get the color configuration.""" diff --git a/homeassistant/components/zha/core/channels/security.py b/homeassistant/components/zha/core/channels/security.py index e37987bc821..7c600d98401 100644 --- a/homeassistant/components/zha/core/channels/security.py +++ b/homeassistant/components/zha/core/channels/security.py @@ -5,6 +5,7 @@ For more details about this component, please refer to the documentation at https://home-assistant.io/integrations/zha/ """ import asyncio +from typing import Coroutine from zigpy.exceptions import ZigbeeException import zigpy.zcl.clusters.security as security @@ -20,7 +21,7 @@ from ..const import ( WARNING_DEVICE_STROBE_HIGH, WARNING_DEVICE_STROBE_YES, ) -from .base import ZigbeeChannel +from .base import ChannelStatus, ZigbeeChannel @registries.ZIGBEE_CHANNEL_REGISTRY.register(security.IasAce.cluster_id) @@ -155,14 +156,10 @@ class IASZoneChannel(ZigbeeChannel): str(ex), ) - try: - self.debug("Sending pro-active IAS enroll response") - await self._cluster.enroll_response(0, 0) - except ZigbeeException as ex: - self.debug( - "Failed to send pro-active IAS enroll response: %s", - str(ex), - ) + self.debug("Sending pro-active IAS enroll response") + self._cluster.create_catching_task(self._cluster.enroll_response(0, 0)) + + self._status = ChannelStatus.CONFIGURED self.debug("finished IASZoneChannel configuration") @callback @@ -177,8 +174,7 @@ class IASZoneChannel(ZigbeeChannel): value, ) - async def async_initialize(self, from_cache): + def async_initialize_channel_specific(self, from_cache: bool) -> Coroutine: """Initialize channel.""" - attributes = ["zone_status", "zone_state"] - await self.get_attributes(attributes, from_cache=from_cache) - await super().async_initialize(from_cache) + attributes = ["zone_status", "zone_state", "zone_type"] + return self.get_attributes(attributes, from_cache=from_cache) diff --git a/homeassistant/components/zha/core/channels/smartenergy.py b/homeassistant/components/zha/core/channels/smartenergy.py index 792b9413294..0bd7159cf97 100644 --- a/homeassistant/components/zha/core/channels/smartenergy.py +++ b/homeassistant/components/zha/core/channels/smartenergy.py @@ -1,5 +1,5 @@ """Smart energy channels module for Zigbee Home Automation.""" -from typing import Union +from typing import Coroutine, Union import zigpy.zcl.clusters.smartenergy as smartenergy @@ -96,15 +96,13 @@ class Metering(ZigbeeChannel): """Return multiplier for the value.""" return self.cluster.get("multiplier") - async def async_configure(self) -> None: + def async_configure_channel_specific(self) -> Coroutine: """Configure channel.""" - await self.fetch_config(False) - await super().async_configure() + return self.fetch_config(False) - async def async_initialize(self, from_cache: bool) -> None: + def async_initialize_channel_specific(self, from_cache: bool) -> Coroutine: """Initialize channel.""" - await self.fetch_config(True) - await super().async_initialize(from_cache) + return self.fetch_config(True) @callback def attribute_updated(self, attrid: int, value: int) -> None: