mirror of
https://github.com/home-assistant/core.git
synced 2025-07-19 11:17:21 +00:00
Fix zwave_js unique ID migration logic (#47031)
This commit is contained in:
parent
7dc9071776
commit
a43f3c1a0c
@ -85,6 +85,21 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
|||||||
dev_reg = await device_registry.async_get_registry(hass)
|
dev_reg = await device_registry.async_get_registry(hass)
|
||||||
ent_reg = entity_registry.async_get(hass)
|
ent_reg = entity_registry.async_get(hass)
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def migrate_entity(platform: str, old_unique_id: str, new_unique_id: str) -> None:
|
||||||
|
"""Check if entity with old unique ID exists, and if so migrate it to new ID."""
|
||||||
|
if entity_id := ent_reg.async_get_entity_id(platform, DOMAIN, old_unique_id):
|
||||||
|
LOGGER.debug(
|
||||||
|
"Migrating entity %s from old unique ID '%s' to new unique ID '%s'",
|
||||||
|
entity_id,
|
||||||
|
old_unique_id,
|
||||||
|
new_unique_id,
|
||||||
|
)
|
||||||
|
ent_reg.async_update_entity(
|
||||||
|
entity_id,
|
||||||
|
new_unique_id=new_unique_id,
|
||||||
|
)
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def async_on_node_ready(node: ZwaveNode) -> None:
|
def async_on_node_ready(node: ZwaveNode) -> None:
|
||||||
"""Handle node ready event."""
|
"""Handle node ready event."""
|
||||||
@ -97,27 +112,50 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
|||||||
for disc_info in async_discover_values(node):
|
for disc_info in async_discover_values(node):
|
||||||
LOGGER.debug("Discovered entity: %s", disc_info)
|
LOGGER.debug("Discovered entity: %s", disc_info)
|
||||||
|
|
||||||
# This migration logic was added in 2021.3 to handle breaking change to
|
# This migration logic was added in 2021.3 to handle a breaking change to
|
||||||
# value_id format. Some time in the future, this code block
|
# the value_id format. Some time in the future, this code block
|
||||||
# (and get_old_value_id helper) can be removed.
|
# (as well as get_old_value_id helper and migrate_entity closure) can be
|
||||||
old_value_id = get_old_value_id(disc_info.primary_value)
|
# removed.
|
||||||
old_unique_id = get_unique_id(
|
value_ids = [
|
||||||
client.driver.controller.home_id, old_value_id
|
# 2021.2.* format
|
||||||
)
|
get_old_value_id(disc_info.primary_value),
|
||||||
if entity_id := ent_reg.async_get_entity_id(
|
# 2021.3.0b0 format
|
||||||
disc_info.platform, DOMAIN, old_unique_id
|
disc_info.primary_value.value_id,
|
||||||
):
|
]
|
||||||
LOGGER.debug(
|
|
||||||
"Entity %s is using old unique ID, migrating to new one", entity_id
|
new_unique_id = get_unique_id(
|
||||||
)
|
|
||||||
ent_reg.async_update_entity(
|
|
||||||
entity_id,
|
|
||||||
new_unique_id=get_unique_id(
|
|
||||||
client.driver.controller.home_id,
|
client.driver.controller.home_id,
|
||||||
disc_info.primary_value.value_id,
|
disc_info.primary_value.value_id,
|
||||||
),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
for value_id in value_ids:
|
||||||
|
old_unique_id = get_unique_id(
|
||||||
|
client.driver.controller.home_id,
|
||||||
|
f"{disc_info.primary_value.node.node_id}.{value_id}",
|
||||||
|
)
|
||||||
|
# Most entities have the same ID format, but notification binary sensors
|
||||||
|
# have a state key in their ID so we need to handle them differently
|
||||||
|
if (
|
||||||
|
disc_info.platform == "binary_sensor"
|
||||||
|
and disc_info.platform_hint == "notification"
|
||||||
|
):
|
||||||
|
for state_key in disc_info.primary_value.metadata.states:
|
||||||
|
# ignore idle key (0)
|
||||||
|
if state_key == "0":
|
||||||
|
continue
|
||||||
|
|
||||||
|
migrate_entity(
|
||||||
|
disc_info.platform,
|
||||||
|
f"{old_unique_id}.{state_key}",
|
||||||
|
f"{new_unique_id}.{state_key}",
|
||||||
|
)
|
||||||
|
|
||||||
|
# Once we've iterated through all state keys, we can move on to the
|
||||||
|
# next item
|
||||||
|
continue
|
||||||
|
|
||||||
|
migrate_entity(disc_info.platform, old_unique_id, new_unique_id)
|
||||||
|
|
||||||
async_dispatcher_send(
|
async_dispatcher_send(
|
||||||
hass, f"{DOMAIN}_{entry.entry_id}_add_{disc_info.platform}", disc_info
|
hass, f"{DOMAIN}_{entry.entry_id}_add_{disc_info.platform}", disc_info
|
||||||
)
|
)
|
||||||
|
@ -24,11 +24,6 @@ class ZwaveDiscoveryInfo:
|
|||||||
# hint for the platform about this discovered entity
|
# hint for the platform about this discovered entity
|
||||||
platform_hint: Optional[str] = ""
|
platform_hint: Optional[str] = ""
|
||||||
|
|
||||||
@property
|
|
||||||
def value_id(self) -> str:
|
|
||||||
"""Return the unique value_id belonging to primary value."""
|
|
||||||
return f"{self.node.node_id}.{self.primary_value.value_id}"
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class ZWaveValueDiscoverySchema:
|
class ZWaveValueDiscoverySchema:
|
||||||
|
@ -32,7 +32,7 @@ class ZWaveBaseEntity(Entity):
|
|||||||
self.info = info
|
self.info = info
|
||||||
self._name = self.generate_name()
|
self._name = self.generate_name()
|
||||||
self._unique_id = get_unique_id(
|
self._unique_id = get_unique_id(
|
||||||
self.client.driver.controller.home_id, self.info.value_id
|
self.client.driver.controller.home_id, self.info.primary_value.value_id
|
||||||
)
|
)
|
||||||
# entities requiring additional values, can add extra ids to this list
|
# entities requiring additional values, can add extra ids to this list
|
||||||
self.watched_value_ids = {self.info.primary_value.value_id}
|
self.watched_value_ids = {self.info.primary_value.value_id}
|
||||||
|
@ -18,7 +18,7 @@ from homeassistant.config_entries import (
|
|||||||
from homeassistant.const import STATE_UNAVAILABLE
|
from homeassistant.const import STATE_UNAVAILABLE
|
||||||
from homeassistant.helpers import device_registry, entity_registry
|
from homeassistant.helpers import device_registry, entity_registry
|
||||||
|
|
||||||
from .common import AIR_TEMPERATURE_SENSOR
|
from .common import AIR_TEMPERATURE_SENSOR, NOTIFICATION_MOTION_BINARY_SENSOR
|
||||||
|
|
||||||
from tests.common import MockConfigEntry
|
from tests.common import MockConfigEntry
|
||||||
|
|
||||||
@ -124,13 +124,15 @@ async def test_on_node_added_ready(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
async def test_unique_id_migration(hass, multisensor_6_state, client, integration):
|
async def test_unique_id_migration_v1(hass, multisensor_6_state, client, integration):
|
||||||
"""Test unique ID is migrated from old format to new."""
|
"""Test unique ID is migrated from old format to new (version 1)."""
|
||||||
ent_reg = entity_registry.async_get(hass)
|
ent_reg = entity_registry.async_get(hass)
|
||||||
|
|
||||||
|
# Migrate version 1
|
||||||
entity_name = AIR_TEMPERATURE_SENSOR.split(".")[1]
|
entity_name = AIR_TEMPERATURE_SENSOR.split(".")[1]
|
||||||
|
|
||||||
# Create entity RegistryEntry using old unique ID format
|
# Create entity RegistryEntry using old unique ID format
|
||||||
old_unique_id = f"{client.driver.controller.home_id}.52-49-00-Air temperature-00"
|
old_unique_id = f"{client.driver.controller.home_id}.52.52-49-00-Air temperature-00"
|
||||||
entity_entry = ent_reg.async_get_or_create(
|
entity_entry = ent_reg.async_get_or_create(
|
||||||
"sensor",
|
"sensor",
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
@ -155,6 +157,73 @@ async def test_unique_id_migration(hass, multisensor_6_state, client, integratio
|
|||||||
assert entity_entry.unique_id == new_unique_id
|
assert entity_entry.unique_id == new_unique_id
|
||||||
|
|
||||||
|
|
||||||
|
async def test_unique_id_migration_v2(hass, multisensor_6_state, client, integration):
|
||||||
|
"""Test unique ID is migrated from old format to new (version 2)."""
|
||||||
|
ent_reg = entity_registry.async_get(hass)
|
||||||
|
# Migrate version 2
|
||||||
|
ILLUMINANCE_SENSOR = "sensor.multisensor_6_illuminance"
|
||||||
|
entity_name = ILLUMINANCE_SENSOR.split(".")[1]
|
||||||
|
|
||||||
|
# Create entity RegistryEntry using old unique ID format
|
||||||
|
old_unique_id = f"{client.driver.controller.home_id}.52.52-49-0-Illuminance-00-00"
|
||||||
|
entity_entry = ent_reg.async_get_or_create(
|
||||||
|
"sensor",
|
||||||
|
DOMAIN,
|
||||||
|
old_unique_id,
|
||||||
|
suggested_object_id=entity_name,
|
||||||
|
config_entry=integration,
|
||||||
|
original_name=entity_name,
|
||||||
|
)
|
||||||
|
assert entity_entry.entity_id == ILLUMINANCE_SENSOR
|
||||||
|
assert entity_entry.unique_id == old_unique_id
|
||||||
|
|
||||||
|
# Add a ready node, unique ID should be migrated
|
||||||
|
node = Node(client, multisensor_6_state)
|
||||||
|
event = {"node": node}
|
||||||
|
|
||||||
|
client.driver.controller.emit("node added", event)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
# Check that new RegistryEntry is using new unique ID format
|
||||||
|
entity_entry = ent_reg.async_get(ILLUMINANCE_SENSOR)
|
||||||
|
new_unique_id = f"{client.driver.controller.home_id}.52-49-0-Illuminance-00-00"
|
||||||
|
assert entity_entry.unique_id == new_unique_id
|
||||||
|
|
||||||
|
|
||||||
|
async def test_unique_id_migration_notification_binary_sensor(
|
||||||
|
hass, multisensor_6_state, client, integration
|
||||||
|
):
|
||||||
|
"""Test unique ID is migrated from old format to new for a notification binary sensor."""
|
||||||
|
ent_reg = entity_registry.async_get(hass)
|
||||||
|
|
||||||
|
entity_name = NOTIFICATION_MOTION_BINARY_SENSOR.split(".")[1]
|
||||||
|
|
||||||
|
# Create entity RegistryEntry using old unique ID format
|
||||||
|
old_unique_id = f"{client.driver.controller.home_id}.52.52-113-00-Home Security-Motion sensor status.8"
|
||||||
|
entity_entry = ent_reg.async_get_or_create(
|
||||||
|
"binary_sensor",
|
||||||
|
DOMAIN,
|
||||||
|
old_unique_id,
|
||||||
|
suggested_object_id=entity_name,
|
||||||
|
config_entry=integration,
|
||||||
|
original_name=entity_name,
|
||||||
|
)
|
||||||
|
assert entity_entry.entity_id == NOTIFICATION_MOTION_BINARY_SENSOR
|
||||||
|
assert entity_entry.unique_id == old_unique_id
|
||||||
|
|
||||||
|
# Add a ready node, unique ID should be migrated
|
||||||
|
node = Node(client, multisensor_6_state)
|
||||||
|
event = {"node": node}
|
||||||
|
|
||||||
|
client.driver.controller.emit("node added", event)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
# Check that new RegistryEntry is using new unique ID format
|
||||||
|
entity_entry = ent_reg.async_get(NOTIFICATION_MOTION_BINARY_SENSOR)
|
||||||
|
new_unique_id = f"{client.driver.controller.home_id}.52-113-0-Home Security-Motion sensor status-Motion sensor status.8"
|
||||||
|
assert entity_entry.unique_id == new_unique_id
|
||||||
|
|
||||||
|
|
||||||
async def test_on_node_added_not_ready(
|
async def test_on_node_added_not_ready(
|
||||||
hass, multisensor_6_state, client, integration, device_registry
|
hass, multisensor_6_state, client, integration, device_registry
|
||||||
):
|
):
|
||||||
|
Loading…
x
Reference in New Issue
Block a user