Migrate zwave_js entities to use new unique ID format (#46979)

* migrate zwave_js entities to use new unique ID format

* remove extra param from helper

* add comment to remove migration logic in the future

* update comment

* use instance attribute instead of calling functino on every state update
This commit is contained in:
Raman Gupta 2021-02-24 09:41:10 -05:00 committed by GitHub
parent 44293a3738
commit 424526db7e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 78 additions and 4 deletions

View File

@ -43,7 +43,7 @@ from .const import (
ZWAVE_JS_EVENT,
)
from .discovery import async_discover_values
from .helpers import get_device_id
from .helpers import get_device_id, get_old_value_id, get_unique_id
from .services import ZWaveServices
LOGGER = logging.getLogger(__package__)
@ -83,6 +83,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Set up Z-Wave JS from a config entry."""
client = ZwaveClient(entry.data[CONF_URL], async_get_clientsession(hass))
dev_reg = await device_registry.async_get_registry(hass)
ent_reg = entity_registry.async_get(hass)
@callback
def async_on_node_ready(node: ZwaveNode) -> None:
@ -95,6 +96,28 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
# run discovery on all node values and create/update entities
for disc_info in async_discover_values(node):
LOGGER.debug("Discovered entity: %s", disc_info)
# This migration logic was added in 2021.3 to handle breaking change to
# value_id format. Some time in the future, this code block
# (and get_old_value_id helper) can be removed.
old_value_id = get_old_value_id(disc_info.primary_value)
old_unique_id = get_unique_id(
client.driver.controller.home_id, old_value_id
)
if entity_id := ent_reg.async_get_entity_id(
disc_info.platform, DOMAIN, old_unique_id
):
LOGGER.debug(
"Entity %s is using old unique ID, migrating to new one", entity_id
)
ent_reg.async_update_entity(
entity_id,
new_unique_id=get_unique_id(
client.driver.controller.home_id,
disc_info.primary_value.value_id,
),
)
async_dispatcher_send(
hass, f"{DOMAIN}_{entry.entry_id}_add_{disc_info.platform}", disc_info
)
@ -193,7 +216,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
DATA_UNSUBSCRIBE: unsubscribe_callbacks,
}
services = ZWaveServices(hass, entity_registry.async_get(hass))
services = ZWaveServices(hass, ent_reg)
services.async_register()
# Set up websocket API

View File

@ -13,7 +13,7 @@ from homeassistant.helpers.entity import Entity
from .const import DOMAIN
from .discovery import ZwaveDiscoveryInfo
from .helpers import get_device_id
from .helpers import get_device_id, get_unique_id
LOGGER = logging.getLogger(__name__)
@ -31,6 +31,9 @@ class ZWaveBaseEntity(Entity):
self.client = client
self.info = info
self._name = self.generate_name()
self._unique_id = get_unique_id(
self.client.driver.controller.home_id, self.info.value_id
)
# entities requiring additional values, can add extra ids to this list
self.watched_value_ids = {self.info.primary_value.value_id}
@ -128,7 +131,7 @@ class ZWaveBaseEntity(Entity):
@property
def unique_id(self) -> str:
"""Return the unique_id of the entity."""
return f"{self.client.driver.controller.home_id}.{self.info.value_id}"
return self._unique_id
@property
def available(self) -> bool:

View File

@ -3,6 +3,7 @@ from typing import List, Tuple, cast
from zwave_js_server.client import Client as ZwaveClient
from zwave_js_server.model.node import Node as ZwaveNode
from zwave_js_server.model.value import Value as ZwaveValue
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant, callback
@ -12,6 +13,22 @@ from homeassistant.helpers.entity_registry import async_get as async_get_ent_reg
from .const import DATA_CLIENT, DOMAIN
@callback
def get_old_value_id(value: ZwaveValue) -> str:
"""Get old value ID so we can migrate entity unique ID."""
command_class = value.command_class
endpoint = value.endpoint or "00"
property_ = value.property_
property_key_name = value.property_key_name or "00"
return f"{value.node.node_id}-{command_class}-{endpoint}-{property_}-{property_key_name}"
@callback
def get_unique_id(home_id: str, value_id: str) -> str:
"""Get unique ID from home ID and value ID."""
return f"{home_id}.{value_id}"
@callback
def get_device_id(client: ZwaveClient, node: ZwaveNode) -> Tuple[str, str]:
"""Get device registry identifier for Z-Wave node."""

View File

@ -124,6 +124,37 @@ async def test_on_node_added_ready(
)
async def test_unique_id_migration(hass, multisensor_6_state, client, integration):
"""Test unique ID is migrated from old format to new."""
ent_reg = entity_registry.async_get(hass)
entity_name = AIR_TEMPERATURE_SENSOR.split(".")[1]
# Create entity RegistryEntry using old unique ID format
old_unique_id = f"{client.driver.controller.home_id}.52-49-00-Air temperature-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 == AIR_TEMPERATURE_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(AIR_TEMPERATURE_SENSOR)
new_unique_id = f"{client.driver.controller.home_id}.52-49-0-Air temperature-00-00"
assert entity_entry.unique_id == new_unique_id
async def test_on_node_added_not_ready(
hass, multisensor_6_state, client, integration, device_registry
):