From d0ed1be92b84d733972e67c49e94c9f274297cb4 Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Sat, 1 Aug 2026 16:54:27 +0200 Subject: [PATCH] Fix via_device race in solarlog (#177764) --- homeassistant/components/solarlog/__init__.py | 16 ++++++++++-- homeassistant/components/solarlog/entity.py | 9 +++++-- tests/components/solarlog/test_sensor.py | 25 +++++++++++++++++++ 3 files changed, 46 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/solarlog/__init__.py b/homeassistant/components/solarlog/__init__.py index f3f971360f9f..f95025566501 100644 --- a/homeassistant/components/solarlog/__init__.py +++ b/homeassistant/components/solarlog/__init__.py @@ -8,10 +8,10 @@ from solarlog_cli.solarlog_connector import SolarLogConnector from homeassistant.const import CONF_HOST, CONF_TIMEOUT, Platform from homeassistant.core import HomeAssistant -from homeassistant.helpers import entity_registry as er +from homeassistant.helpers import device_registry as dr, entity_registry as er from homeassistant.helpers.aiohttp_client import async_create_clientsession -from .const import CONF_HAS_PWD, DEFAULT_TIMEOUT +from .const import CONF_HAS_PWD, DEFAULT_TIMEOUT, DOMAIN from .coordinator import ( SolarLogBasicDataCoordinator, SolarlogConfigEntry, @@ -77,6 +77,18 @@ async def async_setup_entry(hass: HomeAssistant, entry: SolarlogConfigEntry) -> entry.runtime_data.device_data_coordinator = device_coordinator await device_coordinator.async_config_entry_first_refresh() + # Register the controller device so inverter entities can resolve it as + # their via_device parent when they are added. + device_registry = dr.async_get(hass) + device_registry.async_get_or_create( + config_entry_id=entry.entry_id, + identifiers={(DOMAIN, entry.entry_id)}, + manufacturer="Solar-Log", + model="Controller", + name="SolarLog", + configuration_url=solarlog.host, + ) + await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS) return True diff --git a/homeassistant/components/solarlog/entity.py b/homeassistant/components/solarlog/entity.py index e0ed33a70579..3ae9d8ccf258 100644 --- a/homeassistant/components/solarlog/entity.py +++ b/homeassistant/components/solarlog/entity.py @@ -1,6 +1,7 @@ """Entities for SolarLog integration.""" from homeassistant.components.sensor import SensorEntityDescription +from homeassistant.helpers import device_registry as dr from homeassistant.helpers.device_registry import DeviceInfo from homeassistant.helpers.update_coordinator import CoordinatorEntity from homeassistant.util import slugify @@ -57,8 +58,12 @@ class SolarLogInverterEntity(CoordinatorEntity[SolarLogDeviceDataCoordinator]): manufacturer="Solar-Log", model="Inverter", identifiers={(DOMAIN, name)}, - name=coordinator.solarlog.device_name(device_id), - via_device=(DOMAIN, coordinator.config_entry.entry_id), + name=device_name, + via_device_id=dr.async_get_device_id_by_identifier( + coordinator.hass, + (DOMAIN, coordinator.config_entry.entry_id), + config_entry_id=coordinator.config_entry.entry_id, + ), ) self.device_id = device_id self.entity_description = description diff --git a/tests/components/solarlog/test_sensor.py b/tests/components/solarlog/test_sensor.py index 3ae9a34b759d..b34f0ce83219 100644 --- a/tests/components/solarlog/test_sensor.py +++ b/tests/components/solarlog/test_sensor.py @@ -12,6 +12,7 @@ from solarlog_cli.solarlog_exceptions import ( from solarlog_cli.solarlog_models import InverterData from syrupy.assertion import SnapshotAssertion +from homeassistant.components.solarlog.const import DOMAIN from homeassistant.const import STATE_UNAVAILABLE, Platform from homeassistant.core import HomeAssistant from homeassistant.helpers.device_registry import DeviceRegistry @@ -78,6 +79,30 @@ async def test_add_remove_entities( assert hass.states.get("sensor.inverter_3_consumption_year").state == "0.454" +@pytest.mark.usefixtures("mock_solarlog_connector") +async def test_inverter_via_device_id( + hass: HomeAssistant, + mock_config_entry: MockConfigEntry, + device_registry: DeviceRegistry, + entity_registry: EntityRegistry, +) -> None: + """Test inverter devices are linked to the controller device via via_device_id.""" + await setup_platform(hass, mock_config_entry, [Platform.SENSOR]) + + controller_device = device_registry.async_get_device_by_identifier( + (DOMAIN, mock_config_entry.entry_id), mock_config_entry.entry_id + ) + assert controller_device is not None + + entity = entity_registry.async_get("sensor.inverter_1_consumption_year") + assert entity is not None + assert entity.device_id is not None + inverter_device = device_registry.async_get(entity.device_id) + assert inverter_device is not None + + assert inverter_device.via_device_id == controller_device.id + + @pytest.mark.parametrize( "exception", [