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
This commit is contained in:
Alexei Chetroi 2020-12-04 18:45:09 -05:00 committed by GitHub
parent b19c705867
commit 125ceb7449
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 49 additions and 98 deletions

View File

@ -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."""

View File

@ -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

View File

@ -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)

View File

@ -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)

View File

@ -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

View File

@ -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."""

View File

@ -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."""

View File

@ -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)

View File

@ -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: