diff --git a/homeassistant/components/shelly/button.py b/homeassistant/components/shelly/button.py index af34119290b..fbc46160f1c 100644 --- a/homeassistant/components/shelly/button.py +++ b/homeassistant/components/shelly/button.py @@ -9,6 +9,7 @@ from typing import TYPE_CHECKING, Any, Final from aioshelly.const import BLU_TRV_IDENTIFIER, MODEL_BLU_GATEWAY_G3, RPC_GENERATIONS from aioshelly.exceptions import DeviceConnectionError, InvalidAuthError, RpcCallError +from aioshelly.rpc_device import RpcDevice from homeassistant.components.button import ( DOMAIN as BUTTON_PLATFORM, @@ -22,13 +23,13 @@ from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers import entity_registry as er from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback from homeassistant.helpers.update_coordinator import CoordinatorEntity -from homeassistant.util import slugify from .const import DOMAIN, LOGGER, SHELLY_GAS_MODELS from .coordinator import ShellyBlockCoordinator, ShellyConfigEntry, ShellyRpcCoordinator from .entity import get_entity_block_device_info, get_entity_rpc_device_info from .utils import ( async_remove_orphaned_entities, + format_ble_addr, get_blu_trv_device_info, get_device_entry_gen, get_rpc_entity_name, @@ -112,12 +113,10 @@ def async_migrate_unique_ids( if not entity_entry.entity_id.startswith("button"): return None - device_name = slugify(coordinator.device.name) - for key in ("reboot", "self_test", "mute", "unmute"): - old_unique_id = f"{device_name}_{key}" + old_unique_id = f"{coordinator.mac}_{key}" if entity_entry.unique_id == old_unique_id: - new_unique_id = f"{coordinator.mac}_{key}" + new_unique_id = f"{coordinator.mac}-{key}" LOGGER.debug( "Migrating unique_id for %s entity from [%s] to [%s]", entity_entry.entity_id, @@ -130,6 +129,26 @@ def async_migrate_unique_ids( ) } + if blutrv_key_ids := get_rpc_key_ids(coordinator.device.status, BLU_TRV_IDENTIFIER): + assert isinstance(coordinator.device, RpcDevice) + for _id in blutrv_key_ids: + key = f"{BLU_TRV_IDENTIFIER}:{_id}" + ble_addr: str = coordinator.device.config[key]["addr"] + old_unique_id = f"{ble_addr}_calibrate" + if entity_entry.unique_id == old_unique_id: + new_unique_id = f"{format_ble_addr(ble_addr)}-{key}-calibrate" + LOGGER.debug( + "Migrating unique_id for %s entity from [%s] to [%s]", + entity_entry.entity_id, + old_unique_id, + new_unique_id, + ) + return { + "new_unique_id": entity_entry.unique_id.replace( + old_unique_id, new_unique_id + ) + } + return None @@ -264,7 +283,7 @@ class ShellyButton(ShellyBaseButton): """Initialize Shelly button.""" super().__init__(coordinator, description) - self._attr_unique_id = f"{coordinator.mac}_{description.key}" + self._attr_unique_id = f"{coordinator.mac}-{description.key}" if isinstance(coordinator, ShellyBlockCoordinator): self._attr_device_info = get_entity_block_device_info(coordinator) else: @@ -297,7 +316,7 @@ class ShellyBluTrvButton(ShellyBaseButton): ble_addr: str = config["addr"] fw_ver = coordinator.device.status[key].get("fw_ver") - self._attr_unique_id = f"{ble_addr}_{description.key}" + self._attr_unique_id = f"{format_ble_addr(ble_addr)}-{key}-{description.key}" self._attr_device_info = get_blu_trv_device_info( config, ble_addr, coordinator.mac, fw_ver ) diff --git a/homeassistant/components/shelly/utils.py b/homeassistant/components/shelly/utils.py index ab510b660e2..962a314f8eb 100644 --- a/homeassistant/components/shelly/utils.py +++ b/homeassistant/components/shelly/utils.py @@ -918,3 +918,8 @@ def remove_empty_sub_devices(hass: HomeAssistant, entry: ConfigEntry) -> None: dev_reg.async_update_device( device.id, remove_config_entry_id=entry.entry_id ) + + +def format_ble_addr(ble_addr: str) -> str: + """Format BLE address to use in unique_id.""" + return ble_addr.replace(":", "").upper() diff --git a/tests/components/shelly/snapshots/test_button.ambr b/tests/components/shelly/snapshots/test_button.ambr index e3755bd5dd5..af19860f546 100644 --- a/tests/components/shelly/snapshots/test_button.ambr +++ b/tests/components/shelly/snapshots/test_button.ambr @@ -30,7 +30,7 @@ 'suggested_object_id': None, 'supported_features': 0, 'translation_key': 'calibrate', - 'unique_id': 'f8:44:77:25:f0:dd_calibrate', + 'unique_id': 'F8447725F0DD-blutrv:200-calibrate', 'unit_of_measurement': None, }) # --- @@ -78,7 +78,7 @@ 'suggested_object_id': None, 'supported_features': 0, 'translation_key': None, - 'unique_id': '123456789ABC_reboot', + 'unique_id': '123456789ABC-reboot', 'unit_of_measurement': None, }) # --- diff --git a/tests/components/shelly/snapshots/test_devices.ambr b/tests/components/shelly/snapshots/test_devices.ambr index 9dcda321057..74c50691ce8 100644 --- a/tests/components/shelly/snapshots/test_devices.ambr +++ b/tests/components/shelly/snapshots/test_devices.ambr @@ -422,7 +422,7 @@ 'suggested_object_id': None, 'supported_features': 0, 'translation_key': None, - 'unique_id': '123456789ABC_reboot', + 'unique_id': '123456789ABC-reboot', 'unit_of_measurement': None, }) # --- @@ -1672,7 +1672,7 @@ 'suggested_object_id': None, 'supported_features': 0, 'translation_key': None, - 'unique_id': '123456789ABC_reboot', + 'unique_id': '123456789ABC-reboot', 'unit_of_measurement': None, }) # --- @@ -2935,7 +2935,7 @@ 'suggested_object_id': None, 'supported_features': 0, 'translation_key': None, - 'unique_id': '123456789ABC_reboot', + 'unique_id': '123456789ABC-reboot', 'unit_of_measurement': None, }) # --- diff --git a/tests/components/shelly/test_button.py b/tests/components/shelly/test_button.py index 6b8403ec392..fe220b5b3d7 100644 --- a/tests/components/shelly/test_button.py +++ b/tests/components/shelly/test_button.py @@ -33,7 +33,7 @@ async def test_block_button( assert state.state == STATE_UNKNOWN assert (entry := entity_registry.async_get(entity_id)) - assert entry.unique_id == "123456789ABC_reboot" + assert entry.unique_id == "123456789ABC-reboot" await hass.services.async_call( BUTTON_DOMAIN, @@ -136,9 +136,9 @@ async def test_rpc_button_reauth_error( @pytest.mark.parametrize( ("gen", "old_unique_id", "new_unique_id", "migration"), [ - (2, "test_name_reboot", "123456789ABC_reboot", True), - (1, "test_name_reboot", "123456789ABC_reboot", True), - (2, "123456789ABC_reboot", "123456789ABC_reboot", False), + (2, "123456789ABC_reboot", "123456789ABC-reboot", True), + (1, "123456789ABC_reboot", "123456789ABC-reboot", True), + (2, "123456789ABC-reboot", "123456789ABC-reboot", False), ], ) async def test_migrate_unique_id( @@ -379,3 +379,34 @@ async def test_wall_display_virtual_button( blocking=True, ) mock_rpc_device.button_trigger.assert_called_once_with(200, "single_push") + + +async def test_migrate_unique_id_blu_trv( + hass: HomeAssistant, + mock_blu_trv: Mock, + entity_registry: EntityRegistry, + caplog: pytest.LogCaptureFixture, +) -> None: + """Test migration of unique_id for BLU TRV button.""" + entry = await init_integration(hass, 3, model=MODEL_BLU_GATEWAY_G3, skip_setup=True) + + old_unique_id = "f8:44:77:25:f0:dd_calibrate" + + entity = entity_registry.async_get_or_create( + suggested_object_id="trv_name_calibrate", + disabled_by=None, + domain=BUTTON_DOMAIN, + platform=DOMAIN, + unique_id=old_unique_id, + config_entry=entry, + ) + assert entity.unique_id == old_unique_id + + await hass.config_entries.async_setup(entry.entry_id) + await hass.async_block_till_done() + + entity_entry = entity_registry.async_get("button.trv_name_calibrate") + assert entity_entry + assert entity_entry.unique_id == "F8447725F0DD-blutrv:200-calibrate" + + assert "Migrating unique_id for button.trv_name_calibrate" in caplog.text