diff --git a/CHANGELOG.md b/CHANGELOG.md index c8c8430f6..ff064e564 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ Versions from 0.40 and up ## Ongoing +- Correct climate and water_heater unique_id's via PR [#1098](https://github.com/plugwise/plugwise-beta/pull/1098) - Implement @overload and improved snapshot-formatting as required for the Next HA version, via PR [#1096](https://github.com/plugwise/plugwise-beta/pull/1096) ## v0.65.0 diff --git a/custom_components/plugwise/__init__.py b/custom_components/plugwise/__init__.py index 989c23a09..050088521 100644 --- a/custom_components/plugwise/__init__.py +++ b/custom_components/plugwise/__init__.py @@ -2,7 +2,7 @@ from typing import Any -from homeassistant.const import CONF_TIMEOUT, Platform +from homeassistant.const import ATTR_NAME, CONF_TIMEOUT, Platform from homeassistant.core import HomeAssistant, callback from homeassistant.helpers import device_registry as dr, entity_registry as er from homeassistant.helpers.typing import ConfigType @@ -41,7 +41,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: PlugwiseConfigEntry) -> ) # pw-beta - cooldown, update_interval as extra await coordinator.async_config_entry_first_refresh() - await async_migrate_sensor_entities(hass, coordinator) + await async_migrate_entities(hass, coordinator) entry.runtime_data = coordinator @@ -98,19 +98,19 @@ def async_migrate_entity_entry(entry: er.RegistryEntry) -> dict[str, Any] | None # No migration needed return None -async def async_migrate_sensor_entities( +async def async_migrate_entities( hass: HomeAssistant, coordinator: PlugwiseDataUpdateCoordinator, ) -> None: - """Migrate Sensors if needed.""" + """Migrate entities if needed.""" ent_reg = er.async_get(hass) - # Migrate opentherm_outdoor_temperature - # to opentherm_outdoor_air_temperature sensor for device_id, device in coordinator.data.items(): - if device["dev_class"] != "heater_central": + if device["dev_class"] not in ("climate", "heater_central"): continue + # Migrate opentherm_outdoor_temperature + # to opentherm_outdoor_air_temperature sensor old_unique_id = f"{device_id}-outdoor_temperature" if entity_id := ent_reg.async_get_entity_id( Platform.SENSOR, DOMAIN, old_unique_id @@ -119,6 +119,16 @@ async def async_migrate_sensor_entities( # Upstream remove LOGGER debug ent_reg.async_update_entity(entity_id, new_unique_id=new_unique_id) + # Migrate id-climate to id-device-name + old_unique_id = f"{device_id}-climate" + if entity_id := ent_reg.async_get_entity_id( + Platform.CLIMATE, DOMAIN, old_unique_id + ): + new_unique_id = f"{device_id}-{device[ATTR_NAME]}".lower() + # Upstream remove LOGGER debug + ent_reg.async_update_entity(entity_id, new_unique_id=new_unique_id) + + # pw-beta only - revert adding CONF_TIMEOUT to config_entry in v0.53.3 async def async_migrate_entry(hass: HomeAssistant, entry: PlugwiseConfigEntry) -> bool: """Migrate back to v1.1 config entry.""" diff --git a/custom_components/plugwise/climate.py b/custom_components/plugwise/climate.py index 4dc857e04..a7395a662 100644 --- a/custom_components/plugwise/climate.py +++ b/custom_components/plugwise/climate.py @@ -143,7 +143,8 @@ def __init__( self._attr_target_temperature_step = max( self.device.get(THERMOSTAT, {}).get(RESOLUTION, 0.5), 0.1 ) - self._attr_unique_id = f"{device_id}-climate" + entity_name = f"{self.device[ATTR_NAME]}".lower() + self._attr_unique_id = f"{device_id}-{entity_name}" # Determine supported features self._attr_supported_features = ClimateEntityFeature.TARGET_TEMPERATURE diff --git a/custom_components/plugwise/water_heater.py b/custom_components/plugwise/water_heater.py index 8b3beade2..d7758a242 100644 --- a/custom_components/plugwise/water_heater.py +++ b/custom_components/plugwise/water_heater.py @@ -72,7 +72,9 @@ def __init__( ) -> None: """Initialise the water_heater.""" super().__init__(coordinator, device_id) - self._attr_unique_id = f"{device_id}-water_heater" + + entity_name = f"{self.device[ATTR_NAME]}".lower() + self._attr_unique_id = f"{device_id}-{entity_name}" max_dhw_temp_bounds = self.device.get(MAX_DHW_TEMP, {}) if max_dhw_temp_bounds: diff --git a/tests/components/plugwise/snapshots/test_climate.ambr b/tests/components/plugwise/snapshots/test_climate.ambr index 60b6db166..ffcda5cee 100644 --- a/tests/components/plugwise/snapshots/test_climate.ambr +++ b/tests/components/plugwise/snapshots/test_climate.ambr @@ -48,7 +48,7 @@ 'suggested_object_id': None, 'supported_features': , 'translation_key': 'plugwise', - 'unique_id': 'f871b8c4d63549319221e294e4f88074-climate', + 'unique_id': 'f871b8c4d63549319221e294e4f88074-bathroom', 'unit_of_measurement': None, }) # --- @@ -134,7 +134,7 @@ 'suggested_object_id': None, 'supported_features': , 'translation_key': 'plugwise', - 'unique_id': 'f2bf9048bef64cc5b6d5110154e33c81-climate', + 'unique_id': 'f2bf9048bef64cc5b6d5110154e33c81-living room', 'unit_of_measurement': None, }) # --- @@ -219,7 +219,7 @@ 'suggested_object_id': None, 'supported_features': , 'translation_key': 'plugwise', - 'unique_id': '08963fec7c53423ca5680aa4cb502c63-climate', + 'unique_id': '08963fec7c53423ca5680aa4cb502c63-badkamer', 'unit_of_measurement': None, }) # --- @@ -303,7 +303,7 @@ 'suggested_object_id': None, 'supported_features': , 'translation_key': 'plugwise', - 'unique_id': '12493538af164a409c6a1c79e38afe1c-climate', + 'unique_id': '12493538af164a409c6a1c79e38afe1c-bios', 'unit_of_measurement': None, }) # --- @@ -386,7 +386,7 @@ 'suggested_object_id': None, 'supported_features': , 'translation_key': 'plugwise', - 'unique_id': '446ac08dd04d4eff8ac57489757b7314-climate', + 'unique_id': '446ac08dd04d4eff8ac57489757b7314-garage', 'unit_of_measurement': None, }) # --- @@ -469,7 +469,7 @@ 'suggested_object_id': None, 'supported_features': , 'translation_key': 'plugwise', - 'unique_id': '82fa13f017d240daa0d0ea1775420f24-climate', + 'unique_id': '82fa13f017d240daa0d0ea1775420f24-jessie', 'unit_of_measurement': None, }) # --- @@ -553,7 +553,7 @@ 'suggested_object_id': None, 'supported_features': , 'translation_key': 'plugwise', - 'unique_id': 'c50f167537524366a5af7aa3942feb1e-climate', + 'unique_id': 'c50f167537524366a5af7aa3942feb1e-woonkamer', 'unit_of_measurement': None, }) # --- @@ -637,7 +637,7 @@ 'suggested_object_id': None, 'supported_features': , 'translation_key': 'plugwise', - 'unique_id': '3cb70739631c4d17a86b8b12e8a5161b-climate', + 'unique_id': '3cb70739631c4d17a86b8b12e8a5161b-anna', 'unit_of_measurement': None, }) # --- @@ -722,7 +722,7 @@ 'suggested_object_id': None, 'supported_features': , 'translation_key': 'plugwise', - 'unique_id': '1e5e55b958ac445583602f767cb45942-climate', + 'unique_id': '1e5e55b958ac445583602f767cb45942-anna', 'unit_of_measurement': None, }) # --- @@ -806,7 +806,7 @@ 'suggested_object_id': None, 'supported_features': , 'translation_key': 'plugwise', - 'unique_id': '3cb70739631c4d17a86b8b12e8a5161b-climate', + 'unique_id': '3cb70739631c4d17a86b8b12e8a5161b-anna', 'unit_of_measurement': None, }) # --- diff --git a/tests/components/plugwise/snapshots/test_water_heater.ambr b/tests/components/plugwise/snapshots/test_water_heater.ambr index 634cca64c..2f3ef30d5 100644 --- a/tests/components/plugwise/snapshots/test_water_heater.ambr +++ b/tests/components/plugwise/snapshots/test_water_heater.ambr @@ -39,7 +39,7 @@ 'suggested_object_id': None, 'supported_features': , 'translation_key': None, - 'unique_id': 'e4684553153b44afbef2200885f379dc-water_heater', + 'unique_id': 'e4684553153b44afbef2200885f379dc-opentherm', 'unit_of_measurement': None, }) # --- @@ -108,7 +108,7 @@ 'suggested_object_id': None, 'supported_features': , 'translation_key': None, - 'unique_id': 'cd0e6156b1f04d5f952349ffbe397481-water_heater', + 'unique_id': 'cd0e6156b1f04d5f952349ffbe397481-opentherm', 'unit_of_measurement': None, }) # --- diff --git a/tests/components/plugwise/test_init.py b/tests/components/plugwise/test_init.py index bd5dbf89f..5bee97c70 100644 --- a/tests/components/plugwise/test_init.py +++ b/tests/components/plugwise/test_init.py @@ -37,6 +37,7 @@ HA_PLUGWISE_SMILE_ASYNC_UPDATE = ( "homeassistant.components.plugwise.coordinator.Smile.async_update" ) +CLIMATE_ID = "06aecb3d00354375924f50c47af36bd2" # ThermoZone device_id for migration HEATER_ID = "1cbf783bb11e4a7c8a6843dee3a86927" # Opentherm device_id for migration PLUG_ID = "cd0ddb54ef694e11ac18ed1cbce5dbbd" # VCR device_id for migration SECONDARY_ID = ( @@ -213,7 +214,7 @@ async def check_migration( ), ], ) -async def test_migrate_unique_id_temperature( +async def test_migrate_temperature_unique_id( hass: HomeAssistant, mock_config_entry: MockConfigEntry, entitydata: dict, @@ -221,7 +222,7 @@ async def test_migrate_unique_id_temperature( new_unique_id: str, mock_smile_anna: MagicMock, ) -> None: - """Test migration of unique_id.""" + """Test migration of sensor unique_id.""" await check_migration( hass, mock_config_entry, entitydata, old_unique_id, new_unique_id ) @@ -254,7 +255,7 @@ async def test_migrate_unique_id_temperature( ), ], ) -async def test_migrate_unique_id_relay( +async def test_migrate_relay_unique_id( hass: HomeAssistant, mock_config_entry: MockConfigEntry, entitydata: dict, @@ -262,7 +263,37 @@ async def test_migrate_unique_id_relay( new_unique_id: str, mock_smile_adam: MagicMock, ) -> None: - """Test migration of unique_id.""" + """Test migration of binary_sensor and switch unique_ids.""" + await check_migration( + hass, mock_config_entry, entitydata, old_unique_id, new_unique_id + ) + + +@pytest.mark.parametrize( + ("entitydata", "old_unique_id", "new_unique_id"), + [ + ( + { + "domain": Platform.CLIMATE, + "platform": DOMAIN, + "unique_id": f"{CLIMATE_ID}-climate", + "suggested_object_id": f"{CLIMATE_ID}-climate", + "disabled_by": None, + }, + f"{CLIMATE_ID}-climate", + f"{CLIMATE_ID}-slaapkamer", + ) + ], +) +async def test_migrate_climate_unique_id( + hass: HomeAssistant, + mock_config_entry: MockConfigEntry, + entitydata: dict, + old_unique_id: str, + new_unique_id: str, + mock_smile_adam_jip: MagicMock, +) -> None: + """Test migration of climate unique_id.""" await check_migration( hass, mock_config_entry, entitydata, old_unique_id, new_unique_id )