1 """Functions used to migrate unique IDs for Z-Wave JS entities."""
3 from __future__
import annotations
5 from dataclasses
import dataclass
8 from zwave_js_server.model.driver
import Driver
9 from zwave_js_server.model.node
import Node
10 from zwave_js_server.model.value
import Value
as ZwaveValue
16 from .const
import DOMAIN
17 from .discovery
import ZwaveDiscoveryInfo
18 from .helpers
import get_unique_id, get_valueless_base_unique_id
20 _LOGGER = logging.getLogger(__name__)
25 """Class to represent a Value ID."""
30 property_key: str |
None =
None
34 """Get a ValueID from a unique ID.
36 This also works for Notification CC Binary Sensors which have their
39 return ValueID.from_string_id(unique_id.split(
".")[1])
43 """Get a ValueID from a string representation of the value ID."""
44 parts = value_id_str.split(
"-")
45 property_key = parts[4]
if len(parts) > 4
else None
46 return ValueID(parts[1], parts[2], parts[3], property_key=property_key)
49 """Return whether two value IDs are the same excluding endpoint."""
54 and self.endpoint != other.endpoint
61 ent_reg: er.EntityRegistry,
62 registered_unique_ids: set[str],
64 device: dr.DeviceEntry,
67 """Migrate existing entity if current one can't be found and an old one exists."""
69 if ent_reg.async_get_entity_id(platform, DOMAIN, unique_id):
72 value_id = ValueID.from_unique_id(unique_id)
76 existing_entity_entries: list[er.RegistryEntry] = []
77 for entry
in er.async_entries_for_device(ent_reg, device.id):
80 if entry.domain != platform
or entry.unique_id
in registered_unique_ids:
84 old_ent_value_id = ValueID.from_unique_id(entry.unique_id)
89 if value_id.is_same_value_different_endpoints(old_ent_value_id):
90 existing_entity_entries.append(entry)
92 if len(existing_entity_entries) > 1:
96 if not existing_entity_entries:
99 entry = existing_entity_entries[0]
100 state = hass.states.get(entry.entity_id)
102 if not state
or state.state == STATE_UNAVAILABLE:
108 ent_reg: er.EntityRegistry,
113 """Check if entity with old unique ID exists, and if so migrate it to new ID."""
114 if not (entity_id := ent_reg.async_get_entity_id(platform, DOMAIN, old_unique_id)):
118 "Migrating entity %s from old unique ID '%s' to new unique ID '%s'",
124 ent_reg.async_update_entity(entity_id, new_unique_id=new_unique_id)
128 "Entity %s can't be migrated because the unique ID is taken; "
129 "Cleaning it up since it is likely no longer valid"
133 ent_reg.async_remove(entity_id)
139 ent_reg: er.EntityRegistry,
140 registered_unique_ids: set[str],
141 device: dr.DeviceEntry,
143 disc_info: ZwaveDiscoveryInfo,
145 """Migrate unique ID for entity/entities tied to discovered value."""
147 new_unique_id =
get_unique_id(driver, disc_info.primary_value.value_id)
151 if new_unique_id
in registered_unique_ids:
164 disc_info.platform == Platform.BINARY_SENSOR
165 and disc_info.platform_hint ==
"notification"
167 for state_key
in disc_info.primary_value.metadata.states:
172 new_bin_sensor_unique_id = f
"{new_unique_id}.{state_key}"
176 if new_bin_sensor_unique_id
in registered_unique_ids:
180 for old_unique_id
in old_unique_ids:
184 f
"{old_unique_id}.{state_key}",
185 new_bin_sensor_unique_id,
192 registered_unique_ids,
195 new_bin_sensor_unique_id,
197 registered_unique_ids.add(new_bin_sensor_unique_id)
203 for old_unique_id
in old_unique_ids:
205 ent_reg, disc_info.platform, old_unique_id, new_unique_id
210 hass, ent_reg, registered_unique_ids, disc_info.platform, device, new_unique_id
212 registered_unique_ids.add(new_unique_id)
217 hass: HomeAssistant, driver: Driver, node: Node, key_map: dict[str, str]
219 """Migrate statistics sensors to new unique IDs.
221 - Migrate camel case keys in unique IDs to snake keys.
223 ent_reg = er.async_get(hass)
224 base_unique_id = f
"{get_valueless_base_unique_id(driver, node)}.statistics"
225 for new_key, old_key
in key_map.items():
226 if new_key == old_key:
228 old_unique_id = f
"{base_unique_id}_{old_key}"
229 new_unique_id = f
"{base_unique_id}_{new_key}"
235 """Get old value IDs so we can migrate entity unique ID."""
239 command_class = value.command_class
240 endpoint = value.endpoint
or "00"
241 property_ = value.property_
242 property_key_name = value.property_key_name
or "00"
244 f
"{value.node.node_id}.{value.node.node_id}-{command_class}-{endpoint}-"
245 f
"{property_}-{property_key_name}"
248 endpoint =
"00" if value.endpoint
is None else value.endpoint
249 property_key =
"00" if value.property_key
is None else value.property_key
250 property_key_name = value.property_key_name
or "00"
253 f
"{value.node.node_id}-{command_class}-{endpoint}-"
254 f
"{property_}-{property_key}-{property_key_name}"
257 value_ids.extend([f
"{value.node.node_id}.{value_id}", value_id])
bool is_same_value_different_endpoints(self, ValueID other)
ValueID from_string_id(str value_id_str)
ValueID from_unique_id(str unique_id)
str get_unique_id(dict[str, Any] data)
None async_migrate_discovered_value(HomeAssistant hass, er.EntityRegistry ent_reg, set[str] registered_unique_ids, dr.DeviceEntry device, Driver driver, ZwaveDiscoveryInfo disc_info)
None async_migrate_unique_id(er.EntityRegistry ent_reg, Platform platform, str old_unique_id, str new_unique_id)
None async_migrate_statistics_sensors(HomeAssistant hass, Driver driver, Node node, dict[str, str] key_map)
None async_migrate_old_entity(HomeAssistant hass, er.EntityRegistry ent_reg, set[str] registered_unique_ids, Platform platform, dr.DeviceEntry device, str unique_id)
list[str] get_old_value_ids(ZwaveValue value)