Fix via_device race in hydrawise

This commit is contained in:
Erik
2026-08-03 13:12:52 +02:00
parent 82ee383dc4
commit a743211c84
3 changed files with 56 additions and 3 deletions
+28 -2
View File
@@ -3,10 +3,11 @@
from pydrawise import auth, hybrid
from homeassistant.const import CONF_API_KEY, CONF_PASSWORD, CONF_USERNAME, Platform
from homeassistant.core import HomeAssistant
from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import ConfigEntryAuthFailed
from homeassistant.helpers import device_registry as dr
from .const import APP_ID
from .const import APP_ID, DOMAIN, MANUFACTURER
from .coordinator import (
HydrawiseConfigEntry,
HydrawiseMainDataUpdateCoordinator,
@@ -46,6 +47,31 @@ async def async_setup_entry(
water_use_coordinator = HydrawiseWaterUseDataUpdateCoordinator(
hass, config_entry, hydrawise, main_coordinator
)
device_registry = dr.async_get(hass)
@callback
def _async_register_controller_devices() -> None:
"""Register controller devices so children can resolve via_device_id.
Controllers can appear on later coordinator updates, so this runs on
every update before the zone-tracking listener, keeping the via_device
parents registered before their child entities are constructed.
"""
for controller in main_coordinator.data.controllers.values():
device_registry.async_get_or_create(
config_entry_id=config_entry.entry_id,
identifiers={(DOMAIN, str(controller.id))},
manufacturer=MANUFACTURER,
model=controller.hardware.model.description,
name=controller.name,
)
_async_register_controller_devices()
config_entry.async_on_unload(
main_coordinator.async_add_listener(_async_register_controller_devices)
)
# async_track_zones is registered first on water_use_coordinator,
# so the water-use coordinator's data is in sync before
# callbacks below construct entities for newly added zones.
+8 -1
View File
@@ -5,6 +5,7 @@ from typing import override
from pydrawise.schema import Controller, Sensor, Zone
from homeassistant.core import callback
from homeassistant.helpers import device_registry as dr
from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.entity import EntityDescription
from homeassistant.helpers.update_coordinator import CoordinatorEntity
@@ -47,7 +48,13 @@ class HydrawiseEntity(CoordinatorEntity[HydrawiseDataUpdateCoordinator]):
manufacturer=MANUFACTURER,
)
if zone_id is not None or sensor_id is not None:
self._attr_device_info["via_device"] = (DOMAIN, str(controller.id))
self._attr_device_info["via_device_id"] = (
dr.async_get_device_id_by_identifier(
self.coordinator.hass,
(DOMAIN, str(controller.id)),
config_entry_id=self.coordinator.config_entry.entry_id,
)
)
self._update_attrs()
@property
+20
View File
@@ -2,6 +2,8 @@
from unittest.mock import Mock
import pytest
from homeassistant.components.hydrawise.const import DOMAIN
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
@@ -38,3 +40,21 @@ def test_controller_in_device_registry(
assert device is not None
assert device.name == "Home Controller"
assert device.manufacturer == "Hydrawise"
@pytest.mark.usefixtures("mock_pydrawise")
def test_zone_via_device_links_to_controller(
device_registry: dr.DeviceRegistry,
mock_added_config_entry: ConfigEntry,
) -> None:
"""Test that a zone device links to its controller via via_device_id."""
controller = device_registry.async_get_device_by_identifier(
(DOMAIN, "52496"), mock_added_config_entry.entry_id
)
assert controller is not None
zone = device_registry.async_get_device_by_identifier(
(DOMAIN, "5965394"), mock_added_config_entry.entry_id
)
assert zone is not None
assert zone.via_device_id == controller.id