From 397f94ee5023184f977a86bc8537bbe126b963b2 Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Tue, 12 Jul 2022 20:06:13 +0200 Subject: [PATCH] Migrate DSMR to use keys for entity unique ID (#74895) --- homeassistant/components/dsmr/__init__.py | 91 ++++++++++++++- homeassistant/components/dsmr/sensor.py | 128 ++++++++++++++------- tests/components/dsmr/test_init.py | 133 ++++++++++++++++++++++ tests/components/dsmr/test_sensor.py | 6 +- 4 files changed, 311 insertions(+), 47 deletions(-) create mode 100644 tests/components/dsmr/test_init.py diff --git a/homeassistant/components/dsmr/__init__.py b/homeassistant/components/dsmr/__init__.py index 044cfd14e64..f3546fc0a00 100644 --- a/homeassistant/components/dsmr/__init__.py +++ b/homeassistant/components/dsmr/__init__.py @@ -1,15 +1,29 @@ """The dsmr component.""" +from __future__ import annotations + from asyncio import CancelledError from contextlib import suppress +from typing import Any from homeassistant.config_entries import ConfigEntry -from homeassistant.core import HomeAssistant +from homeassistant.core import HomeAssistant, callback +from homeassistant.helpers import entity_registry as er -from .const import DATA_TASK, DOMAIN, PLATFORMS +from .const import CONF_DSMR_VERSION, DATA_TASK, DOMAIN, PLATFORMS async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set up DSMR from a config entry.""" + + @callback + def _async_migrate_entity_entry( + entity_entry: er.RegistryEntry, + ) -> dict[str, Any] | None: + """Migrate DSMR entity entry.""" + return async_migrate_entity_entry(entry, entity_entry) + + await er.async_migrate_entries(hass, entry.entry_id, _async_migrate_entity_entry) + hass.data.setdefault(DOMAIN, {}) hass.data[DOMAIN][entry.entry_id] = {} @@ -38,3 +52,76 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: async def async_update_options(hass: HomeAssistant, entry: ConfigEntry) -> None: """Update options.""" await hass.config_entries.async_reload(entry.entry_id) + + +@callback +def async_migrate_entity_entry( + config_entry: ConfigEntry, entity_entry: er.RegistryEntry +) -> dict[str, Any] | None: + """Migrate DSMR entity entries. + + - Migrates unique ID for sensors based on entity description name to key. + """ + + # Replace names with keys in unique ID + for old, new in ( + ("Power_Consumption", "current_electricity_usage"), + ("Power_Production", "current_electricity_delivery"), + ("Power_Tariff", "electricity_active_tariff"), + ("Energy_Consumption_(tarif_1)", "electricity_used_tariff_1"), + ("Energy_Consumption_(tarif_2)", "electricity_used_tariff_2"), + ("Energy_Production_(tarif_1)", "electricity_delivered_tariff_1"), + ("Energy_Production_(tarif_2)", "electricity_delivered_tariff_2"), + ("Power_Consumption_Phase_L1", "instantaneous_active_power_l1_positive"), + ("Power_Consumption_Phase_L3", "instantaneous_active_power_l3_positive"), + ("Power_Consumption_Phase_L2", "instantaneous_active_power_l2_positive"), + ("Power_Production_Phase_L1", "instantaneous_active_power_l1_negative"), + ("Power_Production_Phase_L2", "instantaneous_active_power_l2_negative"), + ("Power_Production_Phase_L3", "instantaneous_active_power_l3_negative"), + ("Short_Power_Failure_Count", "short_power_failure_count"), + ("Long_Power_Failure_Count", "long_power_failure_count"), + ("Voltage_Sags_Phase_L1", "voltage_sag_l1_count"), + ("Voltage_Sags_Phase_L2", "voltage_sag_l2_count"), + ("Voltage_Sags_Phase_L3", "voltage_sag_l3_count"), + ("Voltage_Swells_Phase_L1", "voltage_swell_l1_count"), + ("Voltage_Swells_Phase_L2", "voltage_swell_l2_count"), + ("Voltage_Swells_Phase_L3", "voltage_swell_l3_count"), + ("Voltage_Phase_L1", "instantaneous_voltage_l1"), + ("Voltage_Phase_L2", "instantaneous_voltage_l2"), + ("Voltage_Phase_L3", "instantaneous_voltage_l3"), + ("Current_Phase_L1", "instantaneous_current_l1"), + ("Current_Phase_L2", "instantaneous_current_l2"), + ("Current_Phase_L3", "instantaneous_current_l3"), + ("Max_power_per_phase", "belgium_max_power_per_phase"), + ("Max_current_per_phase", "belgium_max_current_per_phase"), + ("Energy_Consumption_(total)", "electricity_imported_total"), + ("Energy_Production_(total)", "electricity_exported_total"), + ): + if entity_entry.unique_id.endswith(old): + return {"new_unique_id": entity_entry.unique_id.replace(old, new)} + + # Replace unique ID for gas sensors, based on DSMR version + old = "Gas_Consumption" + if entity_entry.unique_id.endswith(old): + dsmr_version = config_entry.data[CONF_DSMR_VERSION] + if dsmr_version in {"4", "5", "5L"}: + return { + "new_unique_id": entity_entry.unique_id.replace( + old, "hourly_gas_meter_reading" + ) + } + if dsmr_version == "5B": + return { + "new_unique_id": entity_entry.unique_id.replace( + old, "belgium_5min_gas_meter_reading" + ) + } + if dsmr_version == "2.2": + return { + "new_unique_id": entity_entry.unique_id.replace( + old, "gas_meter_reading" + ) + } + + # No migration needed + return None diff --git a/homeassistant/components/dsmr/sensor.py b/homeassistant/components/dsmr/sensor.py index 1337f209d5d..6acb652c6e5 100644 --- a/homeassistant/components/dsmr/sensor.py +++ b/homeassistant/components/dsmr/sensor.py @@ -59,7 +59,16 @@ UNIT_CONVERSION = {"m3": VOLUME_CUBIC_METERS} @dataclass -class DSMRSensorEntityDescription(SensorEntityDescription): +class DSMRSensorEntityDescriptionMixin: + """Mixin for required keys.""" + + obis_reference: str + + +@dataclass +class DSMRSensorEntityDescription( + SensorEntityDescription, DSMRSensorEntityDescriptionMixin +): """Represents an DSMR Sensor.""" dsmr_versions: set[str] | None = None @@ -68,211 +77,239 @@ class DSMRSensorEntityDescription(SensorEntityDescription): SENSORS: tuple[DSMRSensorEntityDescription, ...] = ( DSMRSensorEntityDescription( - key=obis_references.CURRENT_ELECTRICITY_USAGE, + key="current_electricity_usage", name="Power Consumption", + obis_reference=obis_references.CURRENT_ELECTRICITY_USAGE, device_class=SensorDeviceClass.POWER, force_update=True, state_class=SensorStateClass.MEASUREMENT, ), DSMRSensorEntityDescription( - key=obis_references.CURRENT_ELECTRICITY_DELIVERY, + key="electricity_delivery", name="Power Production", + obis_reference=obis_references.CURRENT_ELECTRICITY_DELIVERY, device_class=SensorDeviceClass.POWER, force_update=True, state_class=SensorStateClass.MEASUREMENT, ), DSMRSensorEntityDescription( - key=obis_references.ELECTRICITY_ACTIVE_TARIFF, + key="electricity_active_tariff", name="Power Tariff", + obis_reference=obis_references.ELECTRICITY_ACTIVE_TARIFF, dsmr_versions={"2.2", "4", "5", "5B", "5L"}, icon="mdi:flash", ), DSMRSensorEntityDescription( - key=obis_references.ELECTRICITY_USED_TARIFF_1, + key="electricity_used_tariff_1", name="Energy Consumption (tarif 1)", + obis_reference=obis_references.ELECTRICITY_USED_TARIFF_1, dsmr_versions={"2.2", "4", "5", "5B", "5L"}, device_class=SensorDeviceClass.ENERGY, force_update=True, state_class=SensorStateClass.TOTAL_INCREASING, ), DSMRSensorEntityDescription( - key=obis_references.ELECTRICITY_USED_TARIFF_2, + key="electricity_used_tariff_2", name="Energy Consumption (tarif 2)", + obis_reference=obis_references.ELECTRICITY_USED_TARIFF_2, dsmr_versions={"2.2", "4", "5", "5B", "5L"}, force_update=True, device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL_INCREASING, ), DSMRSensorEntityDescription( - key=obis_references.ELECTRICITY_DELIVERED_TARIFF_1, + key="electricity_delivered_tariff_1", name="Energy Production (tarif 1)", + obis_reference=obis_references.ELECTRICITY_DELIVERED_TARIFF_1, dsmr_versions={"2.2", "4", "5", "5B", "5L"}, force_update=True, device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL_INCREASING, ), DSMRSensorEntityDescription( - key=obis_references.ELECTRICITY_DELIVERED_TARIFF_2, + key="electricity_delivered_tariff_2", name="Energy Production (tarif 2)", + obis_reference=obis_references.ELECTRICITY_DELIVERED_TARIFF_2, dsmr_versions={"2.2", "4", "5", "5B", "5L"}, force_update=True, device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL_INCREASING, ), DSMRSensorEntityDescription( - key=obis_references.INSTANTANEOUS_ACTIVE_POWER_L1_POSITIVE, + key="instantaneous_active_power_l1_positive", name="Power Consumption Phase L1", + obis_reference=obis_references.INSTANTANEOUS_ACTIVE_POWER_L1_POSITIVE, device_class=SensorDeviceClass.POWER, entity_registry_enabled_default=False, state_class=SensorStateClass.MEASUREMENT, ), DSMRSensorEntityDescription( - key=obis_references.INSTANTANEOUS_ACTIVE_POWER_L2_POSITIVE, + key="instantaneous_active_power_l2_positive", name="Power Consumption Phase L2", + obis_reference=obis_references.INSTANTANEOUS_ACTIVE_POWER_L2_POSITIVE, device_class=SensorDeviceClass.POWER, entity_registry_enabled_default=False, state_class=SensorStateClass.MEASUREMENT, ), DSMRSensorEntityDescription( - key=obis_references.INSTANTANEOUS_ACTIVE_POWER_L3_POSITIVE, + key="instantaneous_active_power_l3_positive", name="Power Consumption Phase L3", + obis_reference=obis_references.INSTANTANEOUS_ACTIVE_POWER_L3_POSITIVE, device_class=SensorDeviceClass.POWER, entity_registry_enabled_default=False, state_class=SensorStateClass.MEASUREMENT, ), DSMRSensorEntityDescription( - key=obis_references.INSTANTANEOUS_ACTIVE_POWER_L1_NEGATIVE, + key="instantaneous_active_power_l1_negative", name="Power Production Phase L1", + obis_reference=obis_references.INSTANTANEOUS_ACTIVE_POWER_L1_NEGATIVE, device_class=SensorDeviceClass.POWER, entity_registry_enabled_default=False, state_class=SensorStateClass.MEASUREMENT, ), DSMRSensorEntityDescription( - key=obis_references.INSTANTANEOUS_ACTIVE_POWER_L2_NEGATIVE, + key="instantaneous_active_power_l2_negative", name="Power Production Phase L2", + obis_reference=obis_references.INSTANTANEOUS_ACTIVE_POWER_L2_NEGATIVE, device_class=SensorDeviceClass.POWER, entity_registry_enabled_default=False, state_class=SensorStateClass.MEASUREMENT, ), DSMRSensorEntityDescription( - key=obis_references.INSTANTANEOUS_ACTIVE_POWER_L3_NEGATIVE, + key="instantaneous_active_power_l3_negative", name="Power Production Phase L3", + obis_reference=obis_references.INSTANTANEOUS_ACTIVE_POWER_L3_NEGATIVE, device_class=SensorDeviceClass.POWER, entity_registry_enabled_default=False, state_class=SensorStateClass.MEASUREMENT, ), DSMRSensorEntityDescription( - key=obis_references.SHORT_POWER_FAILURE_COUNT, + key="short_power_failure_count", name="Short Power Failure Count", + obis_reference=obis_references.SHORT_POWER_FAILURE_COUNT, dsmr_versions={"2.2", "4", "5", "5B", "5L"}, entity_registry_enabled_default=False, icon="mdi:flash-off", entity_category=EntityCategory.DIAGNOSTIC, ), DSMRSensorEntityDescription( - key=obis_references.LONG_POWER_FAILURE_COUNT, + key="long_power_failure_count", name="Long Power Failure Count", + obis_reference=obis_references.LONG_POWER_FAILURE_COUNT, dsmr_versions={"2.2", "4", "5", "5B", "5L"}, entity_registry_enabled_default=False, icon="mdi:flash-off", entity_category=EntityCategory.DIAGNOSTIC, ), DSMRSensorEntityDescription( - key=obis_references.VOLTAGE_SAG_L1_COUNT, + key="voltage_sag_l1_count", name="Voltage Sags Phase L1", + obis_reference=obis_references.VOLTAGE_SAG_L1_COUNT, dsmr_versions={"2.2", "4", "5", "5B", "5L"}, entity_registry_enabled_default=False, entity_category=EntityCategory.DIAGNOSTIC, ), DSMRSensorEntityDescription( - key=obis_references.VOLTAGE_SAG_L2_COUNT, + key="voltage_sag_l2_count", name="Voltage Sags Phase L2", + obis_reference=obis_references.VOLTAGE_SAG_L2_COUNT, dsmr_versions={"2.2", "4", "5", "5B", "5L"}, entity_registry_enabled_default=False, entity_category=EntityCategory.DIAGNOSTIC, ), DSMRSensorEntityDescription( - key=obis_references.VOLTAGE_SAG_L3_COUNT, + key="voltage_sag_l3_count", name="Voltage Sags Phase L3", + obis_reference=obis_references.VOLTAGE_SAG_L3_COUNT, dsmr_versions={"2.2", "4", "5", "5B", "5L"}, entity_registry_enabled_default=False, entity_category=EntityCategory.DIAGNOSTIC, ), DSMRSensorEntityDescription( - key=obis_references.VOLTAGE_SWELL_L1_COUNT, + key="voltage_swell_l1_count", name="Voltage Swells Phase L1", + obis_reference=obis_references.VOLTAGE_SWELL_L1_COUNT, dsmr_versions={"2.2", "4", "5", "5B", "5L"}, entity_registry_enabled_default=False, icon="mdi:pulse", entity_category=EntityCategory.DIAGNOSTIC, ), DSMRSensorEntityDescription( - key=obis_references.VOLTAGE_SWELL_L2_COUNT, + key="voltage_swell_l2_count", name="Voltage Swells Phase L2", + obis_reference=obis_references.VOLTAGE_SWELL_L2_COUNT, dsmr_versions={"2.2", "4", "5", "5B", "5L"}, entity_registry_enabled_default=False, icon="mdi:pulse", entity_category=EntityCategory.DIAGNOSTIC, ), DSMRSensorEntityDescription( - key=obis_references.VOLTAGE_SWELL_L3_COUNT, + key="voltage_swell_l3_count", name="Voltage Swells Phase L3", + obis_reference=obis_references.VOLTAGE_SWELL_L3_COUNT, dsmr_versions={"2.2", "4", "5", "5B", "5L"}, entity_registry_enabled_default=False, icon="mdi:pulse", entity_category=EntityCategory.DIAGNOSTIC, ), DSMRSensorEntityDescription( - key=obis_references.INSTANTANEOUS_VOLTAGE_L1, + key="instantaneous_voltage_l1", name="Voltage Phase L1", + obis_reference=obis_references.INSTANTANEOUS_VOLTAGE_L1, device_class=SensorDeviceClass.VOLTAGE, entity_registry_enabled_default=False, state_class=SensorStateClass.MEASUREMENT, entity_category=EntityCategory.DIAGNOSTIC, ), DSMRSensorEntityDescription( - key=obis_references.INSTANTANEOUS_VOLTAGE_L2, + key="instantaneous_voltage_l2", name="Voltage Phase L2", + obis_reference=obis_references.INSTANTANEOUS_VOLTAGE_L2, device_class=SensorDeviceClass.VOLTAGE, entity_registry_enabled_default=False, state_class=SensorStateClass.MEASUREMENT, entity_category=EntityCategory.DIAGNOSTIC, ), DSMRSensorEntityDescription( - key=obis_references.INSTANTANEOUS_VOLTAGE_L3, + key="instantaneous_voltage_l3", name="Voltage Phase L3", + obis_reference=obis_references.INSTANTANEOUS_VOLTAGE_L3, device_class=SensorDeviceClass.VOLTAGE, entity_registry_enabled_default=False, state_class=SensorStateClass.MEASUREMENT, entity_category=EntityCategory.DIAGNOSTIC, ), DSMRSensorEntityDescription( - key=obis_references.INSTANTANEOUS_CURRENT_L1, + key="instantaneous_current_l1", name="Current Phase L1", + obis_reference=obis_references.INSTANTANEOUS_CURRENT_L1, device_class=SensorDeviceClass.CURRENT, entity_registry_enabled_default=False, state_class=SensorStateClass.MEASUREMENT, entity_category=EntityCategory.DIAGNOSTIC, ), DSMRSensorEntityDescription( - key=obis_references.INSTANTANEOUS_CURRENT_L2, + key="instantaneous_current_l2", name="Current Phase L2", + obis_reference=obis_references.INSTANTANEOUS_CURRENT_L2, device_class=SensorDeviceClass.CURRENT, entity_registry_enabled_default=False, state_class=SensorStateClass.MEASUREMENT, entity_category=EntityCategory.DIAGNOSTIC, ), DSMRSensorEntityDescription( - key=obis_references.INSTANTANEOUS_CURRENT_L3, + key="instantaneous_current_l3", name="Current Phase L3", + obis_reference=obis_references.INSTANTANEOUS_CURRENT_L3, device_class=SensorDeviceClass.CURRENT, entity_registry_enabled_default=False, state_class=SensorStateClass.MEASUREMENT, entity_category=EntityCategory.DIAGNOSTIC, ), DSMRSensorEntityDescription( - key=obis_references.BELGIUM_MAX_POWER_PER_PHASE, + key="belgium_max_power_per_phase", name="Max power per phase", + obis_reference=obis_references.BELGIUM_MAX_POWER_PER_PHASE, dsmr_versions={"5B"}, device_class=SensorDeviceClass.POWER, entity_registry_enabled_default=False, @@ -280,8 +317,9 @@ SENSORS: tuple[DSMRSensorEntityDescription, ...] = ( entity_category=EntityCategory.DIAGNOSTIC, ), DSMRSensorEntityDescription( - key=obis_references.BELGIUM_MAX_CURRENT_PER_PHASE, + key="belgium_max_current_per_phase", name="Max current per phase", + obis_reference=obis_references.BELGIUM_MAX_CURRENT_PER_PHASE, dsmr_versions={"5B"}, device_class=SensorDeviceClass.POWER, entity_registry_enabled_default=False, @@ -289,24 +327,27 @@ SENSORS: tuple[DSMRSensorEntityDescription, ...] = ( entity_category=EntityCategory.DIAGNOSTIC, ), DSMRSensorEntityDescription( - key=obis_references.ELECTRICITY_IMPORTED_TOTAL, + key="electricity_imported_total", name="Energy Consumption (total)", + obis_reference=obis_references.ELECTRICITY_IMPORTED_TOTAL, dsmr_versions={"5L", "5S", "Q3D"}, force_update=True, device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL_INCREASING, ), DSMRSensorEntityDescription( - key=obis_references.ELECTRICITY_EXPORTED_TOTAL, + key="electricity_exported_total", name="Energy Production (total)", + obis_reference=obis_references.ELECTRICITY_EXPORTED_TOTAL, dsmr_versions={"5L", "5S", "Q3D"}, force_update=True, device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL_INCREASING, ), DSMRSensorEntityDescription( - key=obis_references.HOURLY_GAS_METER_READING, + key="hourly_gas_meter_reading", name="Gas Consumption", + obis_reference=obis_references.HOURLY_GAS_METER_READING, dsmr_versions={"4", "5", "5L"}, is_gas=True, force_update=True, @@ -314,8 +355,9 @@ SENSORS: tuple[DSMRSensorEntityDescription, ...] = ( state_class=SensorStateClass.TOTAL_INCREASING, ), DSMRSensorEntityDescription( - key=obis_references.BELGIUM_5MIN_GAS_METER_READING, + key="belgium_5min_gas_meter_reading", name="Gas Consumption", + obis_reference=obis_references.BELGIUM_5MIN_GAS_METER_READING, dsmr_versions={"5B"}, is_gas=True, force_update=True, @@ -323,8 +365,9 @@ SENSORS: tuple[DSMRSensorEntityDescription, ...] = ( state_class=SensorStateClass.TOTAL_INCREASING, ), DSMRSensorEntityDescription( - key=obis_references.GAS_METER_READING, + key="gas_meter_reading", name="Gas Consumption", + obis_reference=obis_references.GAS_METER_READING, dsmr_versions={"2.2"}, is_gas=True, force_update=True, @@ -492,25 +535,23 @@ class DSMREntity(SensorEntity): identifiers={(DOMAIN, device_serial)}, name=device_name, ) - self._attr_unique_id = f"{device_serial}_{entity_description.name}".replace( - " ", "_" - ) + self._attr_unique_id = f"{device_serial}_{entity_description.key}" @callback def update_data(self, telegram: dict[str, DSMRObject]) -> None: """Update data.""" self.telegram = telegram - if self.hass and self.entity_description.key in self.telegram: + if self.hass and self.entity_description.obis_reference in self.telegram: self.async_write_ha_state() def get_dsmr_object_attr(self, attribute: str) -> str | None: """Read attribute from last received telegram for this DSMR object.""" # Make sure telegram contains an object for this entities obis - if self.entity_description.key not in self.telegram: + if self.entity_description.obis_reference not in self.telegram: return None # Get the attribute value if the object has it - dsmr_object = self.telegram[self.entity_description.key] + dsmr_object = self.telegram[self.entity_description.obis_reference] attr: str | None = getattr(dsmr_object, attribute) return attr @@ -520,7 +561,10 @@ class DSMREntity(SensorEntity): if (value := self.get_dsmr_object_attr("value")) is None: return None - if self.entity_description.key == obis_references.ELECTRICITY_ACTIVE_TARIFF: + if ( + self.entity_description.obis_reference + == obis_references.ELECTRICITY_ACTIVE_TARIFF + ): return self.translate_tariff(value, self._entry.data[CONF_DSMR_VERSION]) with suppress(TypeError): diff --git a/tests/components/dsmr/test_init.py b/tests/components/dsmr/test_init.py new file mode 100644 index 00000000000..914a9f6bdaf --- /dev/null +++ b/tests/components/dsmr/test_init.py @@ -0,0 +1,133 @@ +"""Tests for the DSMR integration.""" +from unittest.mock import MagicMock + +import pytest + +from homeassistant.components.dsmr.const import DOMAIN +from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN +from homeassistant.core import HomeAssistant +from homeassistant.helpers import entity_registry as er + +from tests.common import MockConfigEntry + + +@pytest.mark.parametrize( + "dsmr_version,old_unique_id,new_unique_id", + [ + ("5", "1234_Power_Consumption", "1234_current_electricity_usage"), + ("5", "1234_Power_Production", "1234_current_electricity_delivery"), + ("5", "1234_Power_Tariff", "1234_electricity_active_tariff"), + ("5", "1234_Energy_Consumption_(tarif_1)", "1234_electricity_used_tariff_1"), + ("5", "1234_Energy_Consumption_(tarif_2)", "1234_electricity_used_tariff_2"), + ( + "5", + "1234_Energy_Production_(tarif_1)", + "1234_electricity_delivered_tariff_1", + ), + ( + "5", + "1234_Energy_Production_(tarif_2)", + "1234_electricity_delivered_tariff_2", + ), + ( + "5", + "1234_Power_Consumption_Phase_L1", + "1234_instantaneous_active_power_l1_positive", + ), + ( + "5", + "1234_Power_Consumption_Phase_L2", + "1234_instantaneous_active_power_l2_positive", + ), + ( + "5", + "1234_Power_Consumption_Phase_L3", + "1234_instantaneous_active_power_l3_positive", + ), + ( + "5", + "1234_Power_Production_Phase_L1", + "1234_instantaneous_active_power_l1_negative", + ), + ( + "5", + "1234_Power_Production_Phase_L2", + "1234_instantaneous_active_power_l2_negative", + ), + ( + "5", + "1234_Power_Production_Phase_L3", + "1234_instantaneous_active_power_l3_negative", + ), + ("5", "1234_Short_Power_Failure_Count", "1234_short_power_failure_count"), + ("5", "1234_Long_Power_Failure_Count", "1234_long_power_failure_count"), + ("5", "1234_Voltage_Sags_Phase_L1", "1234_voltage_sag_l1_count"), + ("5", "1234_Voltage_Sags_Phase_L2", "1234_voltage_sag_l2_count"), + ("5", "1234_Voltage_Sags_Phase_L3", "1234_voltage_sag_l3_count"), + ("5", "1234_Voltage_Swells_Phase_L1", "1234_voltage_swell_l1_count"), + ("5", "1234_Voltage_Swells_Phase_L2", "1234_voltage_swell_l2_count"), + ("5", "1234_Voltage_Swells_Phase_L3", "1234_voltage_swell_l3_count"), + ("5", "1234_Voltage_Phase_L1", "1234_instantaneous_voltage_l1"), + ("5", "1234_Voltage_Phase_L2", "1234_instantaneous_voltage_l2"), + ("5", "1234_Voltage_Phase_L3", "1234_instantaneous_voltage_l3"), + ("5", "1234_Current_Phase_L1", "1234_instantaneous_current_l1"), + ("5", "1234_Current_Phase_L2", "1234_instantaneous_current_l2"), + ("5", "1234_Current_Phase_L3", "1234_instantaneous_current_l3"), + ("5B", "1234_Max_power_per_phase", "1234_belgium_max_power_per_phase"), + ("5B", "1234_Max_current_per_phase", "1234_belgium_max_current_per_phase"), + ("5L", "1234_Energy_Consumption_(total)", "1234_electricity_imported_total"), + ("5L", "1234_Energy_Production_(total)", "1234_electricity_exported_total"), + ("5L", "1234_Energy_Production_(total)", "1234_electricity_exported_total"), + ("5", "1234_Gas_Consumption", "1234_hourly_gas_meter_reading"), + ("5B", "1234_Gas_Consumption", "1234_belgium_5min_gas_meter_reading"), + ("2.2", "1234_Gas_Consumption", "1234_gas_meter_reading"), + ], +) +async def test_migrate_unique_id( + hass: HomeAssistant, + dsmr_connection_fixture: tuple[MagicMock, MagicMock, MagicMock], + dsmr_version: str, + old_unique_id: str, + new_unique_id: str, +) -> None: + """Test migration of unique_id.""" + mock_entry = MockConfigEntry( + domain=DOMAIN, + unique_id="/dev/ttyUSB0", + data={ + "port": "/dev/ttyUSB0", + "dsmr_version": dsmr_version, + "precision": 4, + "reconnect_interval": 30, + "serial_id": "1234", + "serial_id_gas": "5678", + }, + options={ + "time_between_update": 0, + }, + ) + + mock_entry.add_to_hass(hass) + + entity_registry = er.async_get(hass) + entity: er.RegistryEntry = entity_registry.async_get_or_create( + suggested_object_id="my_sensor", + disabled_by=None, + domain=SENSOR_DOMAIN, + platform=DOMAIN, + unique_id=old_unique_id, + config_entry=mock_entry, + ) + assert entity.unique_id == old_unique_id + + assert await hass.config_entries.async_setup(mock_entry.entry_id) + await hass.async_block_till_done() + + assert ( + entity_registry.async_get_entity_id(SENSOR_DOMAIN, DOMAIN, old_unique_id) + is None + ) + assert ( + entity_registry.async_get_entity_id(SENSOR_DOMAIN, DOMAIN, new_unique_id) + == "sensor.my_sensor" + ) diff --git a/tests/components/dsmr/test_sensor.py b/tests/components/dsmr/test_sensor.py index 4502f61586a..b006765bde7 100644 --- a/tests/components/dsmr/test_sensor.py +++ b/tests/components/dsmr/test_sensor.py @@ -79,11 +79,11 @@ async def test_default_setup(hass, dsmr_connection_fixture): entry = registry.async_get("sensor.power_consumption") assert entry - assert entry.unique_id == "1234_Power_Consumption" + assert entry.unique_id == "1234_current_electricity_usage" entry = registry.async_get("sensor.gas_consumption") assert entry - assert entry.unique_id == "5678_Gas_Consumption" + assert entry.unique_id == "5678_gas_meter_reading" telegram_callback = connection_factory.call_args_list[0][0][2] @@ -157,7 +157,7 @@ async def test_setup_only_energy(hass, dsmr_connection_fixture): entry = registry.async_get("sensor.power_consumption") assert entry - assert entry.unique_id == "1234_Power_Consumption" + assert entry.unique_id == "1234_current_electricity_usage" entry = registry.async_get("sensor.gas_consumption") assert not entry