diff --git a/homeassistant/components/sense/__init__.py b/homeassistant/components/sense/__init__.py index fbd49cecb399..95f9165f8cfd 100644 --- a/homeassistant/components/sense/__init__.py +++ b/homeassistant/components/sense/__init__.py @@ -15,10 +15,12 @@ from homeassistant.config_entries import ConfigEntry from homeassistant.const import CONF_TIMEOUT, Platform from homeassistant.core import HomeAssistant from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady +from homeassistant.helpers import device_registry as dr from homeassistant.helpers.aiohttp_client import async_get_clientsession from .const import ( ACTIVE_UPDATE_RATE, + DOMAIN, SENSE_CONNECT_EXCEPTIONS, SENSE_TIMEOUT_EXCEPTIONS, SENSE_WEBSOCKET_EXCEPTIONS, @@ -117,6 +119,19 @@ async def async_setup_entry(hass: HomeAssistant, entry: SenseConfigEntry) -> boo rt=realtime_coordinator, ) + # Register the monitor device up front so child devices can reference it via + # via_device_id; child entities are added by concurrently-loaded platforms + # before any of them registers the monitor device. + sense_monitor_id = gateway.sense_monitor_id + dr.async_get(hass).async_get_or_create( + config_entry_id=entry.entry_id, + name=f"Sense {sense_monitor_id}", + identifiers={(DOMAIN, sense_monitor_id)}, + model="Sense", + manufacturer="Sense Labs, Inc.", + configuration_url="https://home.sense.com", + ) + await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS) return True diff --git a/homeassistant/components/sense/entity.py b/homeassistant/components/sense/entity.py index 35c556a51f2c..b9ae487ebb38 100644 --- a/homeassistant/components/sense/entity.py +++ b/homeassistant/components/sense/entity.py @@ -3,6 +3,7 @@ from sense_energy import ASyncSenseable from sense_energy.sense_api import SenseDevice +from homeassistant.helpers import device_registry as dr from homeassistant.helpers.device_registry import DeviceInfo from homeassistant.helpers.update_coordinator import CoordinatorEntity @@ -67,5 +68,9 @@ class SenseDeviceEntity(CoordinatorEntity[SenseCoordinator]): model="Sense", manufacturer="Sense Labs, Inc.", configuration_url="https://home.sense.com", - via_device=(DOMAIN, sense_monitor_id), + via_device_id=dr.async_get_device_id_by_identifier( + coordinator.hass, + (DOMAIN, sense_monitor_id), + config_entry_id=coordinator.config_entry.entry_id, + ), ) diff --git a/tests/components/sense/test_init.py b/tests/components/sense/test_init.py index 0575044cdc17..d50b23a2e363 100644 --- a/tests/components/sense/test_init.py +++ b/tests/components/sense/test_init.py @@ -12,12 +12,40 @@ from sense_energy import ( SenseWebsocketException, ) +from homeassistant.components.sense.const import DOMAIN from homeassistant.config_entries import ConfigEntryState from homeassistant.core import HomeAssistant +from homeassistant.helpers import device_registry as dr + +from .const import DEVICE_1_ID, MONITOR_ID from tests.common import MockConfigEntry +@pytest.mark.usefixtures("mock_sense") +async def test_device_via_device_link( + hass: HomeAssistant, + config_entry: MockConfigEntry, + device_registry: dr.DeviceRegistry, +) -> None: + """Test child devices link to the monitor device via via_device_id.""" + config_entry.add_to_hass(hass) + + assert await hass.config_entries.async_setup(config_entry.entry_id) + await hass.async_block_till_done() + + monitor_device = device_registry.async_get_device_by_identifier( + (DOMAIN, MONITOR_ID), config_entry.entry_id + ) + assert monitor_device is not None + + child_device = device_registry.async_get_device_by_identifier( + (DOMAIN, f"{MONITOR_ID}:{DEVICE_1_ID}"), config_entry.entry_id + ) + assert child_device is not None + assert child_device.via_device_id == monitor_device.id + + @pytest.mark.parametrize( "exception", [