diff --git a/homeassistant/components/dsmr/sensor.py b/homeassistant/components/dsmr/sensor.py index f2b88c6c598..e05785b8b26 100644 --- a/homeassistant/components/dsmr/sensor.py +++ b/homeassistant/components/dsmr/sensor.py @@ -20,6 +20,7 @@ from dsmr_parser.objects import DSMRObject, MbusDevice, Telegram import serial from homeassistant.components.sensor import ( + DOMAIN as SENSOR_DOMAIN, SensorDeviceClass, SensorEntity, SensorEntityDescription, @@ -456,23 +457,29 @@ def rename_old_gas_to_mbus( if entity.unique_id.endswith( "belgium_5min_gas_meter_reading" ) or entity.unique_id.endswith("hourly_gas_meter_reading"): - try: - ent_reg.async_update_entity( - entity.entity_id, - new_unique_id=mbus_device_id, - ) - except ValueError: + if ent_reg.async_get_entity_id( + SENSOR_DOMAIN, DOMAIN, mbus_device_id + ): LOGGER.debug( "Skip migration of %s because it already exists", entity.entity_id, ) - else: - LOGGER.debug( - "Migrated entity %s from unique id %s to %s", - entity.entity_id, - entity.unique_id, - mbus_device_id, - ) + continue + new_device = dev_reg.async_get_or_create( + config_entry_id=entry.entry_id, + identifiers={(DOMAIN, mbus_device_id)}, + ) + ent_reg.async_update_entity( + entity.entity_id, + new_unique_id=mbus_device_id, + device_id=new_device.id, + ) + LOGGER.debug( + "Migrated entity %s from unique id %s to %s", + entity.entity_id, + entity.unique_id, + mbus_device_id, + ) # Cleanup old device dev_entities = er.async_entries_for_device( ent_reg, device_id, include_disabled_entities=True diff --git a/tests/components/dsmr/test_mbus_migration.py b/tests/components/dsmr/test_mbus_migration.py index 7c7d182aa97..8c090690beb 100644 --- a/tests/components/dsmr/test_mbus_migration.py +++ b/tests/components/dsmr/test_mbus_migration.py @@ -10,6 +10,7 @@ from dsmr_parser.obis_references import ( MBUS_METER_READING, ) from dsmr_parser.objects import CosemObject, MBusObject, Telegram +import pytest from homeassistant.components.dsmr.const import DOMAIN from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN @@ -102,6 +103,17 @@ async def test_migrate_gas_to_mbus( # after receiving telegram entities need to have the chance to be created await hass.async_block_till_done() + # Check a new device is created and the old device has been removed + assert len(device_registry.devices) == 1 + assert not device_registry.async_get(device.id) + new_entity = entity_registry.async_get("sensor.gas_meter_reading") + new_device = device_registry.async_get(new_entity.device_id) + new_dev_entities = er.async_entries_for_device( + entity_registry, new_device.id, include_disabled_entities=True + ) + assert new_dev_entities == [new_entity] + + # Check no entities are connected to the old device dev_entities = er.async_entries_for_device( entity_registry, device.id, include_disabled_entities=True ) @@ -202,6 +214,17 @@ async def test_migrate_hourly_gas_to_mbus( # after receiving telegram entities need to have the chance to be created await hass.async_block_till_done() + # Check a new device is created and the old device has been removed + assert len(device_registry.devices) == 1 + assert not device_registry.async_get(device.id) + new_entity = entity_registry.async_get("sensor.gas_meter_reading") + new_device = device_registry.async_get(new_entity.device_id) + new_dev_entities = er.async_entries_for_device( + entity_registry, new_device.id, include_disabled_entities=True + ) + assert new_dev_entities == [new_entity] + + # Check no entities are connected to the old device dev_entities = er.async_entries_for_device( entity_registry, device.id, include_disabled_entities=True ) @@ -302,6 +325,18 @@ async def test_migrate_gas_with_devid_to_mbus( # after receiving telegram entities need to have the chance to be created await hass.async_block_till_done() + # Check a new device is not created and the old device has not been removed + assert len(device_registry.devices) == 1 + assert device_registry.async_get(device.id) + new_entity = entity_registry.async_get("sensor.gas_meter_reading") + new_device = device_registry.async_get(new_entity.device_id) + assert new_device.id == device.id + # Check entities are still connected to the old device + dev_entities = er.async_entries_for_device( + entity_registry, device.id, include_disabled_entities=True + ) + assert dev_entities == [new_entity] + assert ( entity_registry.async_get_entity_id(SENSOR_DOMAIN, DOMAIN, old_unique_id) is None @@ -319,6 +354,7 @@ async def test_migrate_gas_to_mbus_exists( entity_registry: er.EntityRegistry, device_registry: dr.DeviceRegistry, dsmr_connection_fixture: tuple[MagicMock, MagicMock, MagicMock], + caplog: pytest.LogCaptureFixture, ) -> None: """Test migration of unique_id.""" (connection_factory, transport, protocol) = dsmr_connection_fixture @@ -380,7 +416,7 @@ async def test_migrate_gas_to_mbus_exists( telegram = Telegram() telegram.add( MBUS_DEVICE_TYPE, - CosemObject((0, 0), [{"value": "003", "unit": ""}]), + CosemObject((0, 1), [{"value": "003", "unit": ""}]), "MBUS_DEVICE_TYPE", ) telegram.add( @@ -414,7 +450,32 @@ async def test_migrate_gas_to_mbus_exists( # after receiving telegram entities need to have the chance to be created await hass.async_block_till_done() + # Check a new device is not created and the old device has not been removed + assert len(device_registry.devices) == 2 + assert device_registry.async_get(device.id) + assert device_registry.async_get(device2.id) + entity = entity_registry.async_get("sensor.gas_meter_reading") + dev_entities = er.async_entries_for_device( + entity_registry, device.id, include_disabled_entities=True + ) + assert dev_entities == [entity] + entity2 = entity_registry.async_get("sensor.gas_meter_reading_alt") + dev2_entities = er.async_entries_for_device( + entity_registry, device2.id, include_disabled_entities=True + ) + assert dev2_entities == [entity2] + assert ( entity_registry.async_get_entity_id(SENSOR_DOMAIN, DOMAIN, old_unique_id) == "sensor.gas_meter_reading" ) + assert ( + entity_registry.async_get_entity_id( + SENSOR_DOMAIN, DOMAIN, "37464C4F32313139303333373331" + ) + == "sensor.gas_meter_reading_alt" + ) + assert ( + "Skip migration of sensor.gas_meter_reading because it already exists" + in caplog.text + )