From f985fafbf5abcd121d256c619efc4df320073cfc Mon Sep 17 00:00:00 2001 From: Stefan Agner Date: Fri, 28 Aug 2026 17:47:15 +0200 Subject: [PATCH] Don't attach a device to iotawatt entities without a unique ID (#180505) Co-authored-by: Claude Fable 5 --- homeassistant/components/iotawatt/sensor.py | 18 ++++------- tests/components/iotawatt/test_sensor.py | 36 ++++++++++++++++++++- 2 files changed, 41 insertions(+), 13 deletions(-) diff --git a/homeassistant/components/iotawatt/sensor.py b/homeassistant/components/iotawatt/sensor.py index 0ca5ecafbaab..444e7e010ba1 100644 --- a/homeassistant/components/iotawatt/sensor.py +++ b/homeassistant/components/iotawatt/sensor.py @@ -164,10 +164,16 @@ class IotaWattSensor(CoordinatorEntity[IotawattUpdater], SensorEntity): self._key = key data = self._sensor_data + # An entity without a unique ID cannot be attached to a device. if data.getType() == "Input": self._attr_unique_id = ( f"{data.hub_mac_address}-input-{data.getChannel()}-{data.getUnit()}" ) + self._attr_device_info = dr.DeviceInfo( + connections={(dr.CONNECTION_NETWORK_MAC, data.hub_mac_address)}, + manufacturer="IoTaWatt", + model="IoTaWatt", + ) self.entity_description = entity_description @property @@ -181,18 +187,6 @@ class IotaWattSensor(CoordinatorEntity[IotawattUpdater], SensorEntity): """Return name of the entity.""" return self._sensor_data.getName() - @property - @override - def device_info(self) -> dr.DeviceInfo: - """Return device info.""" - return dr.DeviceInfo( - connections={ - (dr.CONNECTION_NETWORK_MAC, self._sensor_data.hub_mac_address) - }, - manufacturer="IoTaWatt", - model="IoTaWatt", - ) - @callback @override def _handle_coordinator_update(self) -> None: diff --git a/tests/components/iotawatt/test_sensor.py b/tests/components/iotawatt/test_sensor.py index dbb11fe4258b..1d07e416976b 100644 --- a/tests/components/iotawatt/test_sensor.py +++ b/tests/components/iotawatt/test_sensor.py @@ -4,6 +4,7 @@ from datetime import timedelta from unittest.mock import MagicMock from freezegun.api import FrozenDateTimeFactory +import pytest from homeassistant.components.iotawatt.const import DOMAIN from homeassistant.components.sensor import ( @@ -19,11 +20,12 @@ from homeassistant.const import ( UnitOfPower, ) from homeassistant.core import HomeAssistant +from homeassistant.helpers import device_registry as dr, entity_registry as er from homeassistant.setup import async_setup_component from . import INPUT_SENSOR, OUTPUT_SENSOR -from tests.common import async_fire_time_changed +from tests.common import MockConfigEntry, async_fire_time_changed async def test_sensor_type_input( @@ -88,3 +90,35 @@ async def test_sensor_type_output( await hass.async_block_till_done() assert hass.states.get("sensor.my_watthour_sensor") is None + + +async def test_output_sensor_not_attached_to_device( + hass: HomeAssistant, + device_registry: dr.DeviceRegistry, + entity_registry: er.EntityRegistry, + caplog: pytest.LogCaptureFixture, + mock_iotawatt: MagicMock, + entry: MockConfigEntry, +) -> None: + """Test only sensors with a unique ID are attached to the device.""" + mock_iotawatt.getSensors.return_value["sensors"] = { + "my_sensor_key": INPUT_SENSOR, + "my_watthour_sensor_key": OUTPUT_SENSOR, + } + assert await async_setup_component(hass, DOMAIN, {}) + await hass.async_block_till_done() + + device = device_registry.async_get_device_by_connection( + (dr.CONNECTION_NETWORK_MAC, "mock-mac"), entry.entry_id + ) + assert device is not None + + input_entry = entity_registry.async_get("sensor.test_device_my_sensor") + assert input_entry is not None + assert input_entry.device_id == device.id + + # Outputs have no unique ID, hence no registry entry to attach a device to. + assert hass.states.get("sensor.my_watthour_sensor") is not None + assert entity_registry.async_get("sensor.my_watthour_sensor") is None + + assert "attempts to attach a device to an entity" not in caplog.text