diff --git a/homeassistant/components/poolside/__init__.py b/homeassistant/components/poolside/__init__.py index 4d45aeee01bd0a..5d3c8b07e851a4 100644 --- a/homeassistant/components/poolside/__init__.py +++ b/homeassistant/components/poolside/__init__.py @@ -132,11 +132,11 @@ def _async_register_devices( """Register the controller hub and its pool device sub-devices up front. Pool devices may carry no entities until their InformationFields state - arrives, and via_device needs the hub to already exist, so both are + arrives, and they are linked to the hub by its device id, so both are created explicitly instead of as a side effect of entity setup. """ device_registry = dr.async_get(hass) - device_registry.async_get_or_create( + hub = device_registry.async_get_or_create( config_entry_id=entry.entry_id, identifiers={(DOMAIN, client.controller_uuid)}, name=site.name, @@ -147,7 +147,7 @@ def _async_register_devices( device_registry.async_get_or_create( config_entry_id=entry.entry_id, identifiers={(DOMAIN, device.uuid)}, - via_device=(DOMAIN, client.controller_uuid), + via_device_id=hub.id, name=device.name, manufacturer="Poolside", model=device.device_type, diff --git a/tests/components/poolside/test_pool_devices.py b/tests/components/poolside/test_pool_devices.py index 960fa537125e4a..ca55b49335e5b3 100644 --- a/tests/components/poolside/test_pool_devices.py +++ b/tests/components/poolside/test_pool_devices.py @@ -2,6 +2,7 @@ import json from typing import Any +from unittest.mock import patch from aiopoolside import PoolsideCommandError, PoolsideDevice import pytest @@ -937,3 +938,37 @@ async def test_setup_succeeds_without_pool_device_support( assert mock_config_entry.state is ConfigEntryState.LOADED assert mock_config_entry.runtime_data.pool_devices == [] + + +async def test_pool_devices_linked_by_device_id( + hass: HomeAssistant, + mock_config_entry: MockConfigEntry, + mock_poolside_client: FakePoolsideClient, + device_registry: dr.DeviceRegistry, +) -> None: + """Pool devices are linked to the controller by its device id. + + `via_device` (by identifier) is deprecated and stops working in 2027.8. + """ + calls: list[dict[str, Any]] = [] + original = device_registry.async_get_or_create + + def record(**kwargs: Any) -> dr.DeviceEntry: + calls.append(kwargs) + return original(**kwargs) + + with patch.object(device_registry, "async_get_or_create", side_effect=record): + await setup_entry(hass, mock_config_entry) + + controller = device_registry.async_get_device({(DOMAIN, TEST_CONTROLLER_UUID)}) + assert controller is not None + # The explicit registration that links the pump (entities attaching to it + # register it again, without a link). + [pump_call] = [ + c + for c in calls + if c["identifiers"] == {(DOMAIN, PUMP_UUID)} + and ("via_device" in c or "via_device_id" in c) + ] + assert "via_device" not in pump_call + assert pump_call["via_device_id"] == controller.id