Reorganize ZHA device availability code (#108856)

* Correct ZHA device availability at startup

* don't set available property from gateway

* cleanup
This commit is contained in:
David F. Mulcahey 2024-01-27 07:17:05 -05:00 committed by GitHub
parent 677b06f502
commit 950660b953
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 28 additions and 21 deletions

View File

@ -11,7 +11,7 @@ import time
from typing import TYPE_CHECKING, Any, Self from typing import TYPE_CHECKING, Any, Self
from zigpy import types from zigpy import types
import zigpy.device from zigpy.device import Device as ZigpyDevice
import zigpy.exceptions import zigpy.exceptions
from zigpy.profiles import PROFILES from zigpy.profiles import PROFILES
import zigpy.quirks import zigpy.quirks
@ -124,22 +124,23 @@ class ZHADevice(LogMixin):
zha_gateway: ZHAGateway, zha_gateway: ZHAGateway,
) -> None: ) -> None:
"""Initialize the gateway.""" """Initialize the gateway."""
self.hass = hass self.hass: HomeAssistant = hass
self._zigpy_device = zigpy_device self._zigpy_device: ZigpyDevice = zigpy_device
self._zha_gateway = zha_gateway self._zha_gateway: ZHAGateway = zha_gateway
self._available = False self._available_signal: str = f"{self.name}_{self.ieee}_{SIGNAL_AVAILABLE}"
self._available_signal = f"{self.name}_{self.ieee}_{SIGNAL_AVAILABLE}" self._checkins_missed_count: int = 0
self._checkins_missed_count = 0
self.unsubs: list[Callable[[], None]] = [] self.unsubs: list[Callable[[], None]] = []
self.quirk_applied = isinstance(self._zigpy_device, zigpy.quirks.CustomDevice) self.quirk_applied: bool = isinstance(
self.quirk_class = ( self._zigpy_device, zigpy.quirks.CustomDevice
)
self.quirk_class: str = (
f"{self._zigpy_device.__class__.__module__}." f"{self._zigpy_device.__class__.__module__}."
f"{self._zigpy_device.__class__.__name__}" f"{self._zigpy_device.__class__.__name__}"
) )
self.quirk_id = getattr(self._zigpy_device, ATTR_QUIRK_ID, None) self.quirk_id: str | None = getattr(self._zigpy_device, ATTR_QUIRK_ID, None)
if self.is_mains_powered: if self.is_mains_powered:
self.consider_unavailable_time = async_get_zha_config_value( self.consider_unavailable_time: int = async_get_zha_config_value(
self._zha_gateway.config_entry, self._zha_gateway.config_entry,
ZHA_OPTIONS, ZHA_OPTIONS,
CONF_CONSIDER_UNAVAILABLE_MAINS, CONF_CONSIDER_UNAVAILABLE_MAINS,
@ -152,7 +153,10 @@ class ZHADevice(LogMixin):
CONF_CONSIDER_UNAVAILABLE_BATTERY, CONF_CONSIDER_UNAVAILABLE_BATTERY,
CONF_DEFAULT_CONSIDER_UNAVAILABLE_BATTERY, CONF_DEFAULT_CONSIDER_UNAVAILABLE_BATTERY,
) )
self._available: bool = self.is_coordinator or (
self.last_seen is not None
and time.time() - self.last_seen < self.consider_unavailable_time
)
self._zdo_handler: ZDOClusterHandler = ZDOClusterHandler(self) self._zdo_handler: ZDOClusterHandler = ZDOClusterHandler(self)
self._power_config_ch: ClusterHandler | None = None self._power_config_ch: ClusterHandler | None = None
self._identify_ch: ClusterHandler | None = None self._identify_ch: ClusterHandler | None = None
@ -408,7 +412,6 @@ class ZHADevice(LogMixin):
hass: HomeAssistant, hass: HomeAssistant,
zigpy_dev: zigpy.device.Device, zigpy_dev: zigpy.device.Device,
gateway: ZHAGateway, gateway: ZHAGateway,
restored: bool = False,
) -> Self: ) -> Self:
"""Create new device.""" """Create new device."""
zha_dev = cls(hass, zigpy_dev, gateway) zha_dev = cls(hass, zigpy_dev, gateway)

View File

@ -223,7 +223,7 @@ class ZHAGateway:
zha_data.gateway = self zha_data.gateway = self
self.coordinator_zha_device = self._async_get_or_create_device( self.coordinator_zha_device = self._async_get_or_create_device(
self._find_coordinator_device(), restored=True self._find_coordinator_device()
) )
self.async_load_devices() self.async_load_devices()
@ -264,11 +264,10 @@ class ZHAGateway:
"""Restore ZHA devices from zigpy application state.""" """Restore ZHA devices from zigpy application state."""
for zigpy_device in self.application_controller.devices.values(): for zigpy_device in self.application_controller.devices.values():
zha_device = self._async_get_or_create_device(zigpy_device, restored=True) zha_device = self._async_get_or_create_device(zigpy_device)
delta_msg = "not known" delta_msg = "not known"
if zha_device.last_seen is not None: if zha_device.last_seen is not None:
delta = round(time.time() - zha_device.last_seen) delta = round(time.time() - zha_device.last_seen)
zha_device.available = delta < zha_device.consider_unavailable_time
delta_msg = f"{str(timedelta(seconds=delta))} ago" delta_msg = f"{str(timedelta(seconds=delta))} ago"
_LOGGER.debug( _LOGGER.debug(
( (
@ -622,11 +621,11 @@ class ZHAGateway:
@callback @callback
def _async_get_or_create_device( def _async_get_or_create_device(
self, zigpy_device: zigpy.device.Device, restored: bool = False self, zigpy_device: zigpy.device.Device
) -> ZHADevice: ) -> ZHADevice:
"""Get or create a ZHA device.""" """Get or create a ZHA device."""
if (zha_device := self._devices.get(zigpy_device.ieee)) is None: if (zha_device := self._devices.get(zigpy_device.ieee)) is None:
zha_device = ZHADevice.new(self.hass, zigpy_device, self, restored) zha_device = ZHADevice.new(self.hass, zigpy_device, self)
self._devices[zigpy_device.ieee] = zha_device self._devices[zigpy_device.ieee] = zha_device
device_registry = dr.async_get(self.hass) device_registry = dr.async_get(self.hass)

View File

@ -25,6 +25,7 @@ import zigpy.zdo.types as zdo_t
import homeassistant.components.zha.core.const as zha_const import homeassistant.components.zha.core.const as zha_const
import homeassistant.components.zha.core.device as zha_core_device import homeassistant.components.zha.core.device as zha_core_device
from homeassistant.components.zha.core.gateway import ZHAGateway
from homeassistant.components.zha.core.helpers import get_zha_gateway from homeassistant.components.zha.core.helpers import get_zha_gateway
from homeassistant.helpers import restore_state from homeassistant.helpers import restore_state
from homeassistant.setup import async_setup_component from homeassistant.setup import async_setup_component
@ -381,7 +382,7 @@ def zha_device_joined_restored(request):
@pytest.fixture @pytest.fixture
def zha_device_mock( def zha_device_mock(
hass, zigpy_device_mock hass, config_entry, zigpy_device_mock
) -> Callable[..., zha_core_device.ZHADevice]: ) -> Callable[..., zha_core_device.ZHADevice]:
"""Return a ZHA Device factory.""" """Return a ZHA Device factory."""
@ -409,7 +410,11 @@ def zha_device_mock(
zigpy_device = zigpy_device_mock( zigpy_device = zigpy_device_mock(
endpoints, ieee, manufacturer, model, node_desc, patch_cluster=patch_cluster endpoints, ieee, manufacturer, model, node_desc, patch_cluster=patch_cluster
) )
zha_device = zha_core_device.ZHADevice(hass, zigpy_device, MagicMock()) zha_device = zha_core_device.ZHADevice(
hass,
zigpy_device,
ZHAGateway(hass, {}, config_entry),
)
return zha_device return zha_device
return _zha_device return _zha_device

View File

@ -365,7 +365,7 @@ async def test_startup_concurrency_limit(
zigpy.zdo.types.NodeDescriptor.MACCapabilityFlags.MainsPowered zigpy.zdo.types.NodeDescriptor.MACCapabilityFlags.MainsPowered
) )
zha_gateway._async_get_or_create_device(zigpy_dev, restored=True) zha_gateway._async_get_or_create_device(zigpy_dev)
# Keep track of request concurrency during initialization # Keep track of request concurrency during initialization
current_concurrency = 0 current_concurrency = 0