From 1ab9357840c799375badb1114051bdbb677f4d96 Mon Sep 17 00:00:00 2001
From: Sergiy Maysak <sergey.maysak@gmail.com>
Date: Thu, 30 Nov 2023 13:14:46 +0200
Subject: [PATCH] Fix wirelesstag unique_id to use uuid instead of tag_id
 (#104394)

Co-authored-by: Robert Resch <robert@resch.dev>
---
 .../components/wirelesstag/__init__.py        | 16 ++++++++++
 .../components/wirelesstag/binary_sensor.py   | 12 ++++---
 .../components/wirelesstag/sensor.py          | 18 +++++++----
 .../components/wirelesstag/switch.py          | 32 +++++++++++--------
 4 files changed, 54 insertions(+), 24 deletions(-)

diff --git a/homeassistant/components/wirelesstag/__init__.py b/homeassistant/components/wirelesstag/__init__.py
index 06fbfa3621e..f95337dbaf4 100644
--- a/homeassistant/components/wirelesstag/__init__.py
+++ b/homeassistant/components/wirelesstag/__init__.py
@@ -5,6 +5,7 @@ from requests.exceptions import ConnectTimeout, HTTPError
 import voluptuous as vol
 from wirelesstagpy import WirelessTags
 from wirelesstagpy.exceptions import WirelessTagsException
+from wirelesstagpy.sensortag import SensorTag
 
 from homeassistant.components import persistent_notification
 from homeassistant.const import (
@@ -17,6 +18,7 @@ from homeassistant.const import (
     UnitOfElectricPotential,
 )
 from homeassistant.core import HomeAssistant
+from homeassistant.helpers import entity_registry as er
 import homeassistant.helpers.config_validation as cv
 from homeassistant.helpers.dispatcher import dispatcher_send
 from homeassistant.helpers.entity import Entity
@@ -126,6 +128,20 @@ class WirelessTagPlatform:
         self.api.start_monitoring(push_callback)
 
 
+def async_migrate_unique_id(hass: HomeAssistant, tag: SensorTag, domain: str, key: str):
+    """Migrate old unique id to new one with use of tag's uuid."""
+    registry = er.async_get(hass)
+    new_unique_id = f"{tag.uuid}_{key}"
+
+    if registry.async_get_entity_id(domain, DOMAIN, new_unique_id):
+        return
+
+    old_unique_id = f"{tag.tag_id}_{key}"
+    if entity_id := registry.async_get_entity_id(domain, DOMAIN, old_unique_id):
+        _LOGGER.debug("Updating unique id for %s %s", key, entity_id)
+        registry.async_update_entity(entity_id, new_unique_id=new_unique_id)
+
+
 def setup(hass: HomeAssistant, config: ConfigType) -> bool:
     """Set up the Wireless Sensor Tag component."""
     conf = config[DOMAIN]
diff --git a/homeassistant/components/wirelesstag/binary_sensor.py b/homeassistant/components/wirelesstag/binary_sensor.py
index 711c2987735..64a1097bcab 100644
--- a/homeassistant/components/wirelesstag/binary_sensor.py
+++ b/homeassistant/components/wirelesstag/binary_sensor.py
@@ -4,7 +4,7 @@ from __future__ import annotations
 import voluptuous as vol
 
 from homeassistant.components.binary_sensor import PLATFORM_SCHEMA, BinarySensorEntity
-from homeassistant.const import CONF_MONITORED_CONDITIONS, STATE_OFF, STATE_ON
+from homeassistant.const import CONF_MONITORED_CONDITIONS, STATE_OFF, STATE_ON, Platform
 from homeassistant.core import HomeAssistant, callback
 import homeassistant.helpers.config_validation as cv
 from homeassistant.helpers.dispatcher import async_dispatcher_connect
@@ -15,6 +15,7 @@ from . import (
     DOMAIN as WIRELESSTAG_DOMAIN,
     SIGNAL_BINARY_EVENT_UPDATE,
     WirelessTagBaseSensor,
+    async_migrate_unique_id,
 )
 
 # On means in range, Off means out of range
@@ -72,10 +73,10 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
 )
 
 
-def setup_platform(
+async def async_setup_platform(
     hass: HomeAssistant,
     config: ConfigType,
-    add_entities: AddEntitiesCallback,
+    async_add_entities: AddEntitiesCallback,
     discovery_info: DiscoveryInfoType | None = None,
 ) -> None:
     """Set up the platform for a WirelessTags."""
@@ -87,9 +88,10 @@ def setup_platform(
         allowed_sensor_types = tag.supported_binary_events_types
         for sensor_type in config[CONF_MONITORED_CONDITIONS]:
             if sensor_type in allowed_sensor_types:
+                async_migrate_unique_id(hass, tag, Platform.BINARY_SENSOR, sensor_type)
                 sensors.append(WirelessTagBinarySensor(platform, tag, sensor_type))
 
-    add_entities(sensors, True)
+    async_add_entities(sensors, True)
 
 
 class WirelessTagBinarySensor(WirelessTagBaseSensor, BinarySensorEntity):
@@ -100,7 +102,7 @@ class WirelessTagBinarySensor(WirelessTagBaseSensor, BinarySensorEntity):
         super().__init__(api, tag)
         self._sensor_type = sensor_type
         self._name = f"{self._tag.name} {self.event.human_readable_name}"
-        self._attr_unique_id = f"{self.tag_id}_{self._sensor_type}"
+        self._attr_unique_id = f"{self._uuid}_{self._sensor_type}"
 
     async def async_added_to_hass(self) -> None:
         """Register callbacks."""
diff --git a/homeassistant/components/wirelesstag/sensor.py b/homeassistant/components/wirelesstag/sensor.py
index fd9a7898f92..8ae20031723 100644
--- a/homeassistant/components/wirelesstag/sensor.py
+++ b/homeassistant/components/wirelesstag/sensor.py
@@ -12,14 +12,19 @@ from homeassistant.components.sensor import (
     SensorEntityDescription,
     SensorStateClass,
 )
-from homeassistant.const import CONF_MONITORED_CONDITIONS
+from homeassistant.const import CONF_MONITORED_CONDITIONS, Platform
 from homeassistant.core import HomeAssistant, callback
 import homeassistant.helpers.config_validation as cv
 from homeassistant.helpers.dispatcher import async_dispatcher_connect
 from homeassistant.helpers.entity_platform import AddEntitiesCallback
 from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
 
-from . import DOMAIN as WIRELESSTAG_DOMAIN, SIGNAL_TAG_UPDATE, WirelessTagBaseSensor
+from . import (
+    DOMAIN as WIRELESSTAG_DOMAIN,
+    SIGNAL_TAG_UPDATE,
+    WirelessTagBaseSensor,
+    async_migrate_unique_id,
+)
 
 _LOGGER = logging.getLogger(__name__)
 
@@ -68,10 +73,10 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
 )
 
 
-def setup_platform(
+async def async_setup_platform(
     hass: HomeAssistant,
     config: ConfigType,
-    add_entities: AddEntitiesCallback,
+    async_add_entities: AddEntitiesCallback,
     discovery_info: DiscoveryInfoType | None = None,
 ) -> None:
     """Set up the sensor platform."""
@@ -83,9 +88,10 @@ def setup_platform(
             if key not in tag.allowed_sensor_types:
                 continue
             description = SENSOR_TYPES[key]
+            async_migrate_unique_id(hass, tag, Platform.SENSOR, description.key)
             sensors.append(WirelessTagSensor(platform, tag, description))
 
-    add_entities(sensors, True)
+    async_add_entities(sensors, True)
 
 
 class WirelessTagSensor(WirelessTagBaseSensor, SensorEntity):
@@ -100,7 +106,7 @@ class WirelessTagSensor(WirelessTagBaseSensor, SensorEntity):
         self._sensor_type = description.key
         self.entity_description = description
         self._name = self._tag.name
-        self._attr_unique_id = f"{self.tag_id}_{self._sensor_type}"
+        self._attr_unique_id = f"{self._uuid}_{self._sensor_type}"
 
         # I want to see entity_id as:
         # sensor.wirelesstag_bedroom_temperature
diff --git a/homeassistant/components/wirelesstag/switch.py b/homeassistant/components/wirelesstag/switch.py
index df0f72aca18..7f4008623b1 100644
--- a/homeassistant/components/wirelesstag/switch.py
+++ b/homeassistant/components/wirelesstag/switch.py
@@ -10,13 +10,17 @@ from homeassistant.components.switch import (
     SwitchEntity,
     SwitchEntityDescription,
 )
-from homeassistant.const import CONF_MONITORED_CONDITIONS
+from homeassistant.const import CONF_MONITORED_CONDITIONS, Platform
 from homeassistant.core import HomeAssistant
 import homeassistant.helpers.config_validation as cv
 from homeassistant.helpers.entity_platform import AddEntitiesCallback
 from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
 
-from . import DOMAIN as WIRELESSTAG_DOMAIN, WirelessTagBaseSensor
+from . import (
+    DOMAIN as WIRELESSTAG_DOMAIN,
+    WirelessTagBaseSensor,
+    async_migrate_unique_id,
+)
 
 SWITCH_TYPES: tuple[SwitchEntityDescription, ...] = (
     SwitchEntityDescription(
@@ -52,10 +56,10 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
 )
 
 
-def setup_platform(
+async def async_setup_platform(
     hass: HomeAssistant,
     config: ConfigType,
-    add_entities: AddEntitiesCallback,
+    async_add_entities: AddEntitiesCallback,
     discovery_info: DiscoveryInfoType | None = None,
 ) -> None:
     """Set up switches for a Wireless Sensor Tags."""
@@ -63,15 +67,17 @@ def setup_platform(
 
     tags = platform.load_tags()
     monitored_conditions = config[CONF_MONITORED_CONDITIONS]
-    entities = [
-        WirelessTagSwitch(platform, tag, description)
-        for tag in tags.values()
-        for description in SWITCH_TYPES
-        if description.key in monitored_conditions
-        and description.key in tag.allowed_monitoring_types
-    ]
+    entities = []
+    for tag in tags.values():
+        for description in SWITCH_TYPES:
+            if (
+                description.key in monitored_conditions
+                and description.key in tag.allowed_monitoring_types
+            ):
+                async_migrate_unique_id(hass, tag, Platform.SWITCH, description.key)
+                entities.append(WirelessTagSwitch(platform, tag, description))
 
-    add_entities(entities, True)
+    async_add_entities(entities, True)
 
 
 class WirelessTagSwitch(WirelessTagBaseSensor, SwitchEntity):
@@ -82,7 +88,7 @@ class WirelessTagSwitch(WirelessTagBaseSensor, SwitchEntity):
         super().__init__(api, tag)
         self.entity_description = description
         self._name = f"{self._tag.name} {description.name}"
-        self._attr_unique_id = f"{self.tag_id}_{description.key}"
+        self._attr_unique_id = f"{self._uuid}_{description.key}"
 
     def turn_on(self, **kwargs: Any) -> None:
         """Turn on the switch."""