1
0
mirror of https://github.com/home-assistant/core.git synced 2026-07-12 17:18:04 +01:00

Fix ZHA device trigger cache not resolving quirks (#175895)

This commit is contained in:
TheJulianJES
2026-07-09 18:48:59 +02:00
committed by Franck Nijhof
parent c91e899283
commit 00c5bcd1d3
3 changed files with 51 additions and 8 deletions
+7 -2
View File
@@ -9,6 +9,7 @@ from yarl import URL
from zha.application.const import BAUD_RATES, RadioType
from zha.application.gateway import Gateway
from zha.application.helpers import ZHAData
from zha.quirks import DEVICE_REGISTRY
from zha.zigbee.device import get_device_automation_triggers
from zigpy.config import CONF_DATABASE, CONF_DEVICE, CONF_DEVICE_PATH
from zigpy.exceptions import NetworkSettingsInconsistent, TransientConnectionError
@@ -158,11 +159,15 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> b
zha_gateway = await Gateway.async_from_config(zha_lib_data)
# Load and cache device trigger information early
# Load and cache device trigger information early. Quirks were registered by
# `Gateway.async_from_config` above, so pass the resolver to quirk devices
# and surface quirk-defined triggers (e.g. remote button presses).
device_registry = dr.async_get(hass)
radio_mgr = ZhaRadioManager.from_config_entry(hass, config_entry)
async with radio_mgr.create_zigpy_app(connect=False) as app:
async with radio_mgr.create_zigpy_app(
connect=False, device_resolver=DEVICE_REGISTRY.resolve
) as app:
for dev in app.devices.values():
dev_entry = device_registry.async_get_device(
identifiers={(DOMAIN, str(dev.ieee))},
+16 -4
View File
@@ -1,7 +1,7 @@
"""ZHA radio manager."""
import asyncio
from collections.abc import AsyncGenerator
from collections.abc import AsyncGenerator, Callable
import contextlib
from contextlib import suppress
import copy
@@ -22,6 +22,7 @@ from zigpy.config import (
CONF_NWK_BACKUP_ENABLED,
SCHEMA_DEVICE,
)
import zigpy.device
from zigpy.exceptions import NetworkNotFormed
from homeassistant import config_entries
@@ -174,9 +175,17 @@ class ZhaRadioManager:
@contextlib.asynccontextmanager
async def create_zigpy_app(
self, *, connect: bool = True
self,
*,
connect: bool = True,
device_resolver: Callable[[zigpy.device.Device], zigpy.device.Device]
| None = None,
) -> AsyncGenerator[ControllerApplication]:
"""Connect to the radio with the current config and then clean up."""
"""Connect to the radio with the current config and then clean up.
`device_resolver` is forwarded to zigpy so devices loaded from the
database are quirk-resolved to get quirk-defined device triggers.
"""
assert self.radio_type is not None
config = get_zha_data(self.hass).yaml_config
@@ -201,7 +210,10 @@ class ZhaRadioManager:
app_config[CONF_USE_THREAD] = False
app = await self.radio_type.controller.new(
app_config, auto_form=False, start_radio=False
app_config,
auto_form=False,
start_radio=False,
device_resolver=device_resolver,
)
try:
+28 -2
View File
@@ -1,10 +1,11 @@
"""ZHA device automation trigger tests."""
from collections.abc import Callable, Coroutine
from unittest.mock import patch
from unittest.mock import AsyncMock, patch
import pytest
from zha.application.const import ATTR_ENDPOINT_ID
from zha.application.const import ATTR_ENDPOINT_ID, RadioType
from zha.quirks import DEVICE_REGISTRY
from zigpy.application import ControllerApplication
from zigpy.device import Device as ZigpyDevice
import zigpy.profiles.zha
@@ -558,3 +559,28 @@ async def test_validate_trigger_config_unloaded_bad_info(
)
assert "Unable to find trigger" in caplog.text
async def test_device_trigger_cache_built_with_quirk_resolver(
zigpy_app_controller: ControllerApplication,
setup_zha: Callable[..., Coroutine[None]],
) -> None:
"""Test the early device trigger cache is built with quirk resolution.
Regression test: without quirk resolution, quirk-defined triggers (e.g.
remote button presses) are missing whenever the cache is used as a
fallback (i.e. before ZHA has finished loading).
"""
with patch.object(
RadioType.ezsp.controller,
"new",
AsyncMock(return_value=zigpy_app_controller),
) as mock_new:
await setup_zha()
# Both the trigger cache app and the gateway app must quirk-resolve devices
assert len(mock_new.await_args_list) == 2
assert all(
call.kwargs.get("device_resolver") == DEVICE_REGISTRY.resolve
for call in mock_new.await_args_list
)