Bump zwave-js-server-python to 0.21.0 (#47408)
Co-authored-by: Tobias Sauerwein <cgtobi@users.noreply.github.com>
This commit is contained in:
parent
a1faba29f0
commit
ee69e93b46
14 changed files with 352 additions and 118 deletions
|
@ -48,7 +48,8 @@ from .const import (
|
||||||
ZWAVE_JS_EVENT,
|
ZWAVE_JS_EVENT,
|
||||||
)
|
)
|
||||||
from .discovery import async_discover_values
|
from .discovery import async_discover_values
|
||||||
from .helpers import get_device_id, get_old_value_id, get_unique_id
|
from .helpers import get_device_id
|
||||||
|
from .migrate import async_migrate_discovered_value
|
||||||
from .services import ZWaveServices
|
from .services import ZWaveServices
|
||||||
|
|
||||||
CONNECT_TIMEOUT = 10
|
CONNECT_TIMEOUT = 10
|
||||||
|
@ -98,31 +99,6 @@ 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,
|
|
||||||
)
|
|
||||||
try:
|
|
||||||
ent_reg.async_update_entity(
|
|
||||||
entity_id,
|
|
||||||
new_unique_id=new_unique_id,
|
|
||||||
)
|
|
||||||
except ValueError:
|
|
||||||
LOGGER.debug(
|
|
||||||
(
|
|
||||||
"Entity %s can't be migrated because the unique ID is taken. "
|
|
||||||
"Cleaning it up since it is likely no longer valid."
|
|
||||||
),
|
|
||||||
entity_id,
|
|
||||||
)
|
|
||||||
ent_reg.async_remove(entity_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."""
|
||||||
|
@ -136,49 +112,9 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||||
LOGGER.debug("Discovered entity: %s", disc_info)
|
LOGGER.debug("Discovered entity: %s", disc_info)
|
||||||
|
|
||||||
# This migration logic was added in 2021.3 to handle a breaking change to
|
# This migration logic was added in 2021.3 to handle a breaking change to
|
||||||
# the value_id format. Some time in the future, this code block
|
# the value_id format. Some time in the future, this call (as well as the
|
||||||
# (as well as get_old_value_id helper and migrate_entity closure) can be
|
# helper functions) can be removed.
|
||||||
# removed.
|
async_migrate_discovered_value(ent_reg, client, disc_info)
|
||||||
value_ids = [
|
|
||||||
# 2021.2.* format
|
|
||||||
get_old_value_id(disc_info.primary_value),
|
|
||||||
# 2021.3.0b0 format
|
|
||||||
disc_info.primary_value.value_id,
|
|
||||||
]
|
|
||||||
|
|
||||||
new_unique_id = get_unique_id(
|
|
||||||
client.driver.controller.home_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
|
||||||
)
|
)
|
||||||
|
|
|
@ -125,18 +125,10 @@ class ZWaveClimate(ZWaveBaseEntity, ClimateEntity):
|
||||||
)
|
)
|
||||||
self._setpoint_values: Dict[ThermostatSetpointType, ZwaveValue] = {}
|
self._setpoint_values: Dict[ThermostatSetpointType, ZwaveValue] = {}
|
||||||
for enum in ThermostatSetpointType:
|
for enum in ThermostatSetpointType:
|
||||||
# Some devices don't include a property key so we need to check for value
|
|
||||||
# ID's, both with and without the property key
|
|
||||||
self._setpoint_values[enum] = self.get_zwave_value(
|
self._setpoint_values[enum] = self.get_zwave_value(
|
||||||
THERMOSTAT_SETPOINT_PROPERTY,
|
THERMOSTAT_SETPOINT_PROPERTY,
|
||||||
command_class=CommandClass.THERMOSTAT_SETPOINT,
|
command_class=CommandClass.THERMOSTAT_SETPOINT,
|
||||||
value_property_key=enum.value.key,
|
value_property_key=enum.value.key,
|
||||||
value_property_key_name=enum.value.name,
|
|
||||||
add_to_watched_value_ids=True,
|
|
||||||
) or self.get_zwave_value(
|
|
||||||
THERMOSTAT_SETPOINT_PROPERTY,
|
|
||||||
command_class=CommandClass.THERMOSTAT_SETPOINT,
|
|
||||||
value_property_key_name=enum.value.name,
|
|
||||||
add_to_watched_value_ids=True,
|
add_to_watched_value_ids=True,
|
||||||
)
|
)
|
||||||
# Use the first found setpoint value to always determine the temperature unit
|
# Use the first found setpoint value to always determine the temperature unit
|
||||||
|
|
|
@ -169,7 +169,6 @@ class ZWaveBaseEntity(Entity):
|
||||||
command_class: Optional[int] = None,
|
command_class: Optional[int] = None,
|
||||||
endpoint: Optional[int] = None,
|
endpoint: Optional[int] = None,
|
||||||
value_property_key: Optional[int] = None,
|
value_property_key: Optional[int] = None,
|
||||||
value_property_key_name: Optional[str] = None,
|
|
||||||
add_to_watched_value_ids: bool = True,
|
add_to_watched_value_ids: bool = True,
|
||||||
check_all_endpoints: bool = False,
|
check_all_endpoints: bool = False,
|
||||||
) -> Optional[ZwaveValue]:
|
) -> Optional[ZwaveValue]:
|
||||||
|
@ -188,7 +187,6 @@ class ZWaveBaseEntity(Entity):
|
||||||
value_property,
|
value_property,
|
||||||
endpoint=endpoint,
|
endpoint=endpoint,
|
||||||
property_key=value_property_key,
|
property_key=value_property_key,
|
||||||
property_key_name=value_property_key_name,
|
|
||||||
)
|
)
|
||||||
return_value = self.info.node.values.get(value_id)
|
return_value = self.info.node.values.get(value_id)
|
||||||
|
|
||||||
|
@ -203,7 +201,6 @@ class ZWaveBaseEntity(Entity):
|
||||||
value_property,
|
value_property,
|
||||||
endpoint=endpoint_.index,
|
endpoint=endpoint_.index,
|
||||||
property_key=value_property_key,
|
property_key=value_property_key,
|
||||||
property_key_name=value_property_key_name,
|
|
||||||
)
|
)
|
||||||
return_value = self.info.node.values.get(value_id)
|
return_value = self.info.node.values.get(value_id)
|
||||||
if return_value:
|
if return_value:
|
||||||
|
|
|
@ -3,7 +3,6 @@ from typing import List, Tuple, cast
|
||||||
|
|
||||||
from zwave_js_server.client import Client as ZwaveClient
|
from zwave_js_server.client import Client as ZwaveClient
|
||||||
from zwave_js_server.model.node import Node as ZwaveNode
|
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.config_entries import ConfigEntry
|
||||||
from homeassistant.core import HomeAssistant, callback
|
from homeassistant.core import HomeAssistant, callback
|
||||||
|
@ -13,16 +12,6 @@ from homeassistant.helpers.entity_registry import async_get as async_get_ent_reg
|
||||||
from .const import DATA_CLIENT, DOMAIN
|
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
|
@callback
|
||||||
def get_unique_id(home_id: str, value_id: str) -> str:
|
def get_unique_id(home_id: str, value_id: str) -> str:
|
||||||
"""Get unique ID from home ID and value ID."""
|
"""Get unique ID from home ID and value ID."""
|
||||||
|
|
|
@ -228,7 +228,6 @@ class ZwaveLight(ZWaveBaseEntity, LightEntity):
|
||||||
"targetColor",
|
"targetColor",
|
||||||
CommandClass.SWITCH_COLOR,
|
CommandClass.SWITCH_COLOR,
|
||||||
value_property_key=None,
|
value_property_key=None,
|
||||||
value_property_key_name=None,
|
|
||||||
)
|
)
|
||||||
if combined_color_val and isinstance(combined_color_val.value, dict):
|
if combined_color_val and isinstance(combined_color_val.value, dict):
|
||||||
colors_dict = {}
|
colors_dict = {}
|
||||||
|
@ -252,7 +251,6 @@ class ZwaveLight(ZWaveBaseEntity, LightEntity):
|
||||||
"targetColor",
|
"targetColor",
|
||||||
CommandClass.SWITCH_COLOR,
|
CommandClass.SWITCH_COLOR,
|
||||||
value_property_key=property_key.key,
|
value_property_key=property_key.key,
|
||||||
value_property_key_name=property_key.name,
|
|
||||||
)
|
)
|
||||||
if target_zwave_value is None:
|
if target_zwave_value is None:
|
||||||
# guard for unsupported color
|
# guard for unsupported color
|
||||||
|
@ -318,31 +316,26 @@ class ZwaveLight(ZWaveBaseEntity, LightEntity):
|
||||||
"currentColor",
|
"currentColor",
|
||||||
CommandClass.SWITCH_COLOR,
|
CommandClass.SWITCH_COLOR,
|
||||||
value_property_key=ColorComponent.RED.value.key,
|
value_property_key=ColorComponent.RED.value.key,
|
||||||
value_property_key_name=ColorComponent.RED.value.name,
|
|
||||||
)
|
)
|
||||||
green_val = self.get_zwave_value(
|
green_val = self.get_zwave_value(
|
||||||
"currentColor",
|
"currentColor",
|
||||||
CommandClass.SWITCH_COLOR,
|
CommandClass.SWITCH_COLOR,
|
||||||
value_property_key=ColorComponent.GREEN.value.key,
|
value_property_key=ColorComponent.GREEN.value.key,
|
||||||
value_property_key_name=ColorComponent.GREEN.value.name,
|
|
||||||
)
|
)
|
||||||
blue_val = self.get_zwave_value(
|
blue_val = self.get_zwave_value(
|
||||||
"currentColor",
|
"currentColor",
|
||||||
CommandClass.SWITCH_COLOR,
|
CommandClass.SWITCH_COLOR,
|
||||||
value_property_key=ColorComponent.BLUE.value.key,
|
value_property_key=ColorComponent.BLUE.value.key,
|
||||||
value_property_key_name=ColorComponent.BLUE.value.name,
|
|
||||||
)
|
)
|
||||||
ww_val = self.get_zwave_value(
|
ww_val = self.get_zwave_value(
|
||||||
"currentColor",
|
"currentColor",
|
||||||
CommandClass.SWITCH_COLOR,
|
CommandClass.SWITCH_COLOR,
|
||||||
value_property_key=ColorComponent.WARM_WHITE.value.key,
|
value_property_key=ColorComponent.WARM_WHITE.value.key,
|
||||||
value_property_key_name=ColorComponent.WARM_WHITE.value.name,
|
|
||||||
)
|
)
|
||||||
cw_val = self.get_zwave_value(
|
cw_val = self.get_zwave_value(
|
||||||
"currentColor",
|
"currentColor",
|
||||||
CommandClass.SWITCH_COLOR,
|
CommandClass.SWITCH_COLOR,
|
||||||
value_property_key=ColorComponent.COLD_WHITE.value.key,
|
value_property_key=ColorComponent.COLD_WHITE.value.key,
|
||||||
value_property_key_name=ColorComponent.COLD_WHITE.value.name,
|
|
||||||
)
|
)
|
||||||
# prefer the (new) combined color property
|
# prefer the (new) combined color property
|
||||||
# https://github.com/zwave-js/node-zwave-js/pull/1782
|
# https://github.com/zwave-js/node-zwave-js/pull/1782
|
||||||
|
@ -350,7 +343,6 @@ class ZwaveLight(ZWaveBaseEntity, LightEntity):
|
||||||
"currentColor",
|
"currentColor",
|
||||||
CommandClass.SWITCH_COLOR,
|
CommandClass.SWITCH_COLOR,
|
||||||
value_property_key=None,
|
value_property_key=None,
|
||||||
value_property_key_name=None,
|
|
||||||
)
|
)
|
||||||
if combined_color_val and isinstance(combined_color_val.value, dict):
|
if combined_color_val and isinstance(combined_color_val.value, dict):
|
||||||
multi_color = combined_color_val.value
|
multi_color = combined_color_val.value
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
"name": "Z-Wave JS",
|
"name": "Z-Wave JS",
|
||||||
"config_flow": true,
|
"config_flow": true,
|
||||||
"documentation": "https://www.home-assistant.io/integrations/zwave_js",
|
"documentation": "https://www.home-assistant.io/integrations/zwave_js",
|
||||||
"requirements": ["zwave-js-server-python==0.20.1"],
|
"requirements": ["zwave-js-server-python==0.21.0"],
|
||||||
"codeowners": ["@home-assistant/z-wave"],
|
"codeowners": ["@home-assistant/z-wave"],
|
||||||
"dependencies": ["http", "websocket_api"]
|
"dependencies": ["http", "websocket_api"]
|
||||||
}
|
}
|
||||||
|
|
113
homeassistant/components/zwave_js/migrate.py
Normal file
113
homeassistant/components/zwave_js/migrate.py
Normal file
|
@ -0,0 +1,113 @@
|
||||||
|
"""Functions used to migrate unique IDs for Z-Wave JS entities."""
|
||||||
|
import logging
|
||||||
|
from typing import List
|
||||||
|
|
||||||
|
from zwave_js_server.client import Client as ZwaveClient
|
||||||
|
from zwave_js_server.model.value import Value as ZwaveValue
|
||||||
|
|
||||||
|
from homeassistant.core import callback
|
||||||
|
from homeassistant.helpers.entity_registry import EntityRegistry
|
||||||
|
|
||||||
|
from .const import DOMAIN
|
||||||
|
from .discovery import ZwaveDiscoveryInfo
|
||||||
|
from .helpers import get_unique_id
|
||||||
|
|
||||||
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def async_migrate_entity(
|
||||||
|
ent_reg: EntityRegistry, 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,
|
||||||
|
)
|
||||||
|
try:
|
||||||
|
ent_reg.async_update_entity(
|
||||||
|
entity_id,
|
||||||
|
new_unique_id=new_unique_id,
|
||||||
|
)
|
||||||
|
except ValueError:
|
||||||
|
_LOGGER.debug(
|
||||||
|
(
|
||||||
|
"Entity %s can't be migrated because the unique ID is taken. "
|
||||||
|
"Cleaning it up since it is likely no longer valid."
|
||||||
|
),
|
||||||
|
entity_id,
|
||||||
|
)
|
||||||
|
ent_reg.async_remove(entity_id)
|
||||||
|
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def async_migrate_discovered_value(
|
||||||
|
ent_reg: EntityRegistry, client: ZwaveClient, disc_info: ZwaveDiscoveryInfo
|
||||||
|
) -> None:
|
||||||
|
"""Migrate unique ID for entity/entities tied to discovered value."""
|
||||||
|
new_unique_id = get_unique_id(
|
||||||
|
client.driver.controller.home_id,
|
||||||
|
disc_info.primary_value.value_id,
|
||||||
|
)
|
||||||
|
|
||||||
|
# 2021.2.*, 2021.3.0b0, and 2021.3.0 formats
|
||||||
|
for value_id in get_old_value_ids(disc_info.primary_value):
|
||||||
|
old_unique_id = get_unique_id(
|
||||||
|
client.driver.controller.home_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
|
||||||
|
|
||||||
|
async_migrate_entity(
|
||||||
|
ent_reg,
|
||||||
|
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
|
||||||
|
|
||||||
|
async_migrate_entity(ent_reg, disc_info.platform, old_unique_id, new_unique_id)
|
||||||
|
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def get_old_value_ids(value: ZwaveValue) -> List[str]:
|
||||||
|
"""Get old value IDs so we can migrate entity unique ID."""
|
||||||
|
value_ids = []
|
||||||
|
|
||||||
|
# Pre 2021.3.0 value ID
|
||||||
|
command_class = value.command_class
|
||||||
|
endpoint = value.endpoint or "00"
|
||||||
|
property_ = value.property_
|
||||||
|
property_key_name = value.property_key_name or "00"
|
||||||
|
value_ids.append(
|
||||||
|
f"{value.node.node_id}.{value.node.node_id}-{command_class}-{endpoint}-"
|
||||||
|
f"{property_}-{property_key_name}"
|
||||||
|
)
|
||||||
|
|
||||||
|
endpoint = "00" if value.endpoint is None else value.endpoint
|
||||||
|
property_key = "00" if value.property_key is None else value.property_key
|
||||||
|
property_key_name = value.property_key_name or "00"
|
||||||
|
|
||||||
|
value_id = (
|
||||||
|
f"{value.node.node_id}-{command_class}-{endpoint}-"
|
||||||
|
f"{property_}-{property_key}-{property_key_name}"
|
||||||
|
)
|
||||||
|
# 2021.3.0b0 and 2021.3.0 value IDs
|
||||||
|
value_ids.extend([f"{value.node.node_id}.{value_id}", value_id])
|
||||||
|
|
||||||
|
return value_ids
|
|
@ -2394,4 +2394,4 @@ zigpy==0.32.0
|
||||||
zm-py==0.5.2
|
zm-py==0.5.2
|
||||||
|
|
||||||
# homeassistant.components.zwave_js
|
# homeassistant.components.zwave_js
|
||||||
zwave-js-server-python==0.20.1
|
zwave-js-server-python==0.21.0
|
||||||
|
|
|
@ -1231,4 +1231,4 @@ zigpy-znp==0.4.0
|
||||||
zigpy==0.32.0
|
zigpy==0.32.0
|
||||||
|
|
||||||
# homeassistant.components.zwave_js
|
# homeassistant.components.zwave_js
|
||||||
zwave-js-server-python==0.20.1
|
zwave-js-server-python==0.21.0
|
||||||
|
|
|
@ -71,7 +71,7 @@ async def test_websocket_api(hass, integration, multisensor_6, hass_ws_client):
|
||||||
result = msg["result"]
|
result = msg["result"]
|
||||||
|
|
||||||
assert len(result) == 61
|
assert len(result) == 61
|
||||||
key = "52-112-0-2-00-00"
|
key = "52-112-0-2"
|
||||||
assert result[key]["property"] == 2
|
assert result[key]["property"] == 2
|
||||||
assert result[key]["metadata"]["type"] == "number"
|
assert result[key]["metadata"]["type"] == "number"
|
||||||
assert result[key]["configuration_value_type"] == "enumerated"
|
assert result[key]["configuration_value_type"] == "enumerated"
|
||||||
|
|
|
@ -405,7 +405,7 @@ async def test_setpoint_thermostat(hass, client, climate_danfoss_lc_13, integrat
|
||||||
|
|
||||||
assert state
|
assert state
|
||||||
assert state.state == HVAC_MODE_HEAT
|
assert state.state == HVAC_MODE_HEAT
|
||||||
assert state.attributes[ATTR_TEMPERATURE] == 25
|
assert state.attributes[ATTR_TEMPERATURE] == 14
|
||||||
assert state.attributes[ATTR_HVAC_MODES] == [HVAC_MODE_HEAT]
|
assert state.attributes[ATTR_HVAC_MODES] == [HVAC_MODE_HEAT]
|
||||||
assert state.attributes[ATTR_PRESET_MODE] == PRESET_NONE
|
assert state.attributes[ATTR_PRESET_MODE] == PRESET_NONE
|
||||||
|
|
||||||
|
@ -432,6 +432,7 @@ async def test_setpoint_thermostat(hass, client, climate_danfoss_lc_13, integrat
|
||||||
"commandClassName": "Thermostat Setpoint",
|
"commandClassName": "Thermostat Setpoint",
|
||||||
"property": "setpoint",
|
"property": "setpoint",
|
||||||
"propertyName": "setpoint",
|
"propertyName": "setpoint",
|
||||||
|
"propertyKey": 1,
|
||||||
"propertyKeyName": "Heating",
|
"propertyKeyName": "Heating",
|
||||||
"ccVersion": 2,
|
"ccVersion": 2,
|
||||||
"metadata": {
|
"metadata": {
|
||||||
|
@ -441,7 +442,7 @@ async def test_setpoint_thermostat(hass, client, climate_danfoss_lc_13, integrat
|
||||||
"unit": "\u00b0C",
|
"unit": "\u00b0C",
|
||||||
"ccSpecific": {"setpointType": 1},
|
"ccSpecific": {"setpointType": 1},
|
||||||
},
|
},
|
||||||
"value": 25,
|
"value": 14,
|
||||||
}
|
}
|
||||||
assert args["value"] == 21.5
|
assert args["value"] == 21.5
|
||||||
|
|
||||||
|
@ -459,6 +460,7 @@ async def test_setpoint_thermostat(hass, client, climate_danfoss_lc_13, integrat
|
||||||
"commandClass": 67,
|
"commandClass": 67,
|
||||||
"endpoint": 0,
|
"endpoint": 0,
|
||||||
"property": "setpoint",
|
"property": "setpoint",
|
||||||
|
"propertyKey": 1,
|
||||||
"propertyKeyName": "Heating",
|
"propertyKeyName": "Heating",
|
||||||
"propertyName": "setpoint",
|
"propertyName": "setpoint",
|
||||||
"newValue": 23,
|
"newValue": 23,
|
||||||
|
|
|
@ -160,7 +160,7 @@ async def test_unique_id_migration_dupes(
|
||||||
|
|
||||||
# Check that new RegistryEntry is using new unique ID format
|
# Check that new RegistryEntry is using new unique ID format
|
||||||
entity_entry = ent_reg.async_get(AIR_TEMPERATURE_SENSOR)
|
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"
|
new_unique_id = f"{client.driver.controller.home_id}.52-49-0-Air temperature"
|
||||||
assert entity_entry.unique_id == new_unique_id
|
assert entity_entry.unique_id == new_unique_id
|
||||||
|
|
||||||
assert ent_reg.async_get(f"{AIR_TEMPERATURE_SENSOR}_1") is None
|
assert ent_reg.async_get(f"{AIR_TEMPERATURE_SENSOR}_1") is None
|
||||||
|
@ -195,7 +195,7 @@ async def test_unique_id_migration_v1(hass, multisensor_6_state, client, integra
|
||||||
|
|
||||||
# Check that new RegistryEntry is using new unique ID format
|
# Check that new RegistryEntry is using new unique ID format
|
||||||
entity_entry = ent_reg.async_get(AIR_TEMPERATURE_SENSOR)
|
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"
|
new_unique_id = f"{client.driver.controller.home_id}.52-49-0-Air temperature"
|
||||||
assert entity_entry.unique_id == new_unique_id
|
assert entity_entry.unique_id == new_unique_id
|
||||||
|
|
||||||
|
|
||||||
|
@ -228,7 +228,147 @@ async def test_unique_id_migration_v2(hass, multisensor_6_state, client, integra
|
||||||
|
|
||||||
# Check that new RegistryEntry is using new unique ID format
|
# Check that new RegistryEntry is using new unique ID format
|
||||||
entity_entry = ent_reg.async_get(ILLUMINANCE_SENSOR)
|
entity_entry = ent_reg.async_get(ILLUMINANCE_SENSOR)
|
||||||
new_unique_id = f"{client.driver.controller.home_id}.52-49-0-Illuminance-00-00"
|
new_unique_id = f"{client.driver.controller.home_id}.52-49-0-Illuminance"
|
||||||
|
assert entity_entry.unique_id == new_unique_id
|
||||||
|
|
||||||
|
|
||||||
|
async def test_unique_id_migration_v3(hass, multisensor_6_state, client, integration):
|
||||||
|
"""Test unique ID is migrated from old format to new (version 3)."""
|
||||||
|
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-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"
|
||||||
|
assert entity_entry.unique_id == new_unique_id
|
||||||
|
|
||||||
|
|
||||||
|
async def test_unique_id_migration_property_key_v1(
|
||||||
|
hass, hank_binary_switch_state, client, integration
|
||||||
|
):
|
||||||
|
"""Test unique ID with property key is migrated from old format to new (version 1)."""
|
||||||
|
ent_reg = entity_registry.async_get(hass)
|
||||||
|
|
||||||
|
SENSOR_NAME = "sensor.smart_plug_with_two_usb_ports_value_electric_consumed"
|
||||||
|
entity_name = SENSOR_NAME.split(".")[1]
|
||||||
|
|
||||||
|
# Create entity RegistryEntry using old unique ID format
|
||||||
|
old_unique_id = f"{client.driver.controller.home_id}.32.32-50-00-value-W_Consumed"
|
||||||
|
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 == SENSOR_NAME
|
||||||
|
assert entity_entry.unique_id == old_unique_id
|
||||||
|
|
||||||
|
# Add a ready node, unique ID should be migrated
|
||||||
|
node = Node(client, hank_binary_switch_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(SENSOR_NAME)
|
||||||
|
new_unique_id = f"{client.driver.controller.home_id}.32-50-0-value-66049"
|
||||||
|
assert entity_entry.unique_id == new_unique_id
|
||||||
|
|
||||||
|
|
||||||
|
async def test_unique_id_migration_property_key_v2(
|
||||||
|
hass, hank_binary_switch_state, client, integration
|
||||||
|
):
|
||||||
|
"""Test unique ID with property key is migrated from old format to new (version 2)."""
|
||||||
|
ent_reg = entity_registry.async_get(hass)
|
||||||
|
|
||||||
|
SENSOR_NAME = "sensor.smart_plug_with_two_usb_ports_value_electric_consumed"
|
||||||
|
entity_name = SENSOR_NAME.split(".")[1]
|
||||||
|
|
||||||
|
# Create entity RegistryEntry using old unique ID format
|
||||||
|
old_unique_id = (
|
||||||
|
f"{client.driver.controller.home_id}.32.32-50-0-value-66049-W_Consumed"
|
||||||
|
)
|
||||||
|
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 == SENSOR_NAME
|
||||||
|
assert entity_entry.unique_id == old_unique_id
|
||||||
|
|
||||||
|
# Add a ready node, unique ID should be migrated
|
||||||
|
node = Node(client, hank_binary_switch_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(SENSOR_NAME)
|
||||||
|
new_unique_id = f"{client.driver.controller.home_id}.32-50-0-value-66049"
|
||||||
|
assert entity_entry.unique_id == new_unique_id
|
||||||
|
|
||||||
|
|
||||||
|
async def test_unique_id_migration_property_key_v3(
|
||||||
|
hass, hank_binary_switch_state, client, integration
|
||||||
|
):
|
||||||
|
"""Test unique ID with property key is migrated from old format to new (version 3)."""
|
||||||
|
ent_reg = entity_registry.async_get(hass)
|
||||||
|
|
||||||
|
SENSOR_NAME = "sensor.smart_plug_with_two_usb_ports_value_electric_consumed"
|
||||||
|
entity_name = SENSOR_NAME.split(".")[1]
|
||||||
|
|
||||||
|
# Create entity RegistryEntry using old unique ID format
|
||||||
|
old_unique_id = f"{client.driver.controller.home_id}.32-50-0-value-66049-W_Consumed"
|
||||||
|
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 == SENSOR_NAME
|
||||||
|
assert entity_entry.unique_id == old_unique_id
|
||||||
|
|
||||||
|
# Add a ready node, unique ID should be migrated
|
||||||
|
node = Node(client, hank_binary_switch_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(SENSOR_NAME)
|
||||||
|
new_unique_id = f"{client.driver.controller.home_id}.32-50-0-value-66049"
|
||||||
assert entity_entry.unique_id == new_unique_id
|
assert entity_entry.unique_id == new_unique_id
|
||||||
|
|
||||||
|
|
||||||
|
@ -262,7 +402,7 @@ async def test_unique_id_migration_notification_binary_sensor(
|
||||||
|
|
||||||
# Check that new RegistryEntry is using new unique ID format
|
# Check that new RegistryEntry is using new unique ID format
|
||||||
entity_entry = ent_reg.async_get(NOTIFICATION_MOTION_BINARY_SENSOR)
|
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"
|
new_unique_id = f"{client.driver.controller.home_id}.52-113-0-Home Security-Motion sensor status.8"
|
||||||
assert entity_entry.unique_id == new_unique_id
|
assert entity_entry.unique_id == new_unique_id
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -4,11 +4,25 @@
|
||||||
"status": 1,
|
"status": 1,
|
||||||
"ready": true,
|
"ready": true,
|
||||||
"deviceClass": {
|
"deviceClass": {
|
||||||
"basic": {"key": 4, "label":"Routing Slave"},
|
"basic": {
|
||||||
"generic": {"key": 8, "label":"Thermostat"},
|
"key": 4,
|
||||||
"specific": {"key": 4, "label":"Setpoint Thermostat"},
|
"label": "Routing Slave"
|
||||||
"mandatorySupportedCCs": [],
|
},
|
||||||
"mandatoryControlCCs": []
|
"generic": {
|
||||||
|
"key": 8,
|
||||||
|
"label": "Thermostat"
|
||||||
|
},
|
||||||
|
"specific": {
|
||||||
|
"key": 4,
|
||||||
|
"label": "Setpoint Thermostat"
|
||||||
|
},
|
||||||
|
"mandatorySupportedCCs": [
|
||||||
|
114,
|
||||||
|
143,
|
||||||
|
67,
|
||||||
|
134
|
||||||
|
],
|
||||||
|
"mandatoryControlledCCs": []
|
||||||
},
|
},
|
||||||
"isListening": false,
|
"isListening": false,
|
||||||
"isFrequentListening": false,
|
"isFrequentListening": false,
|
||||||
|
@ -22,6 +36,7 @@
|
||||||
"productType": 5,
|
"productType": 5,
|
||||||
"firmwareVersion": "1.1",
|
"firmwareVersion": "1.1",
|
||||||
"deviceConfig": {
|
"deviceConfig": {
|
||||||
|
"filename": "/usr/src/app/node_modules/@zwave-js/config/config/devices/0x0002/lc-13.json",
|
||||||
"manufacturerId": 2,
|
"manufacturerId": 2,
|
||||||
"manufacturer": "Danfoss",
|
"manufacturer": "Danfoss",
|
||||||
"label": "LC-13",
|
"label": "LC-13",
|
||||||
|
@ -66,19 +81,76 @@
|
||||||
14
|
14
|
||||||
],
|
],
|
||||||
"interviewAttempts": 1,
|
"interviewAttempts": 1,
|
||||||
|
"interviewStage": 7,
|
||||||
|
"commandClasses": [
|
||||||
|
{
|
||||||
|
"id": 67,
|
||||||
|
"name": "Thermostat Setpoint",
|
||||||
|
"version": 2,
|
||||||
|
"isSecure": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 70,
|
||||||
|
"name": "Climate Control Schedule",
|
||||||
|
"version": 1,
|
||||||
|
"isSecure": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 114,
|
||||||
|
"name": "Manufacturer Specific",
|
||||||
|
"version": 1,
|
||||||
|
"isSecure": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 117,
|
||||||
|
"name": "Protection",
|
||||||
|
"version": 2,
|
||||||
|
"isSecure": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 128,
|
||||||
|
"name": "Battery",
|
||||||
|
"version": 1,
|
||||||
|
"isSecure": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 129,
|
||||||
|
"name": "Clock",
|
||||||
|
"version": 1,
|
||||||
|
"isSecure": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 132,
|
||||||
|
"name": "Wake Up",
|
||||||
|
"version": 1,
|
||||||
|
"isSecure": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 134,
|
||||||
|
"name": "Version",
|
||||||
|
"version": 1,
|
||||||
|
"isSecure": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 143,
|
||||||
|
"name": "Multi Command",
|
||||||
|
"version": 1,
|
||||||
|
"isSecure": false
|
||||||
|
}
|
||||||
|
],
|
||||||
"endpoints": [
|
"endpoints": [
|
||||||
{
|
{
|
||||||
"nodeId": 5,
|
"nodeId": 5,
|
||||||
"index": 0
|
"index": 0
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"commandClasses": [],
|
|
||||||
"values": [
|
"values": [
|
||||||
{
|
{
|
||||||
"endpoint": 0,
|
"endpoint": 0,
|
||||||
"commandClass": 67,
|
"commandClass": 67,
|
||||||
"commandClassName": "Thermostat Setpoint",
|
"commandClassName": "Thermostat Setpoint",
|
||||||
"property": "setpoint",
|
"property": "setpoint",
|
||||||
|
"propertyKey": 1,
|
||||||
"propertyName": "setpoint",
|
"propertyName": "setpoint",
|
||||||
"propertyKeyName": "Heating",
|
"propertyKeyName": "Heating",
|
||||||
"ccVersion": 2,
|
"ccVersion": 2,
|
||||||
|
@ -91,7 +163,7 @@
|
||||||
"setpointType": 1
|
"setpointType": 1
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"value": 25
|
"value": 14
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"endpoint": 0,
|
"endpoint": 0,
|
||||||
|
@ -262,7 +334,7 @@
|
||||||
"unit": "%",
|
"unit": "%",
|
||||||
"label": "Battery level"
|
"label": "Battery level"
|
||||||
},
|
},
|
||||||
"value": 53
|
"value": 49
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"endpoint": 0,
|
"endpoint": 0,
|
||||||
|
|
|
@ -837,6 +837,7 @@
|
||||||
"commandClassName": "Thermostat Setpoint",
|
"commandClassName": "Thermostat Setpoint",
|
||||||
"property": "setpoint",
|
"property": "setpoint",
|
||||||
"propertyName": "setpoint",
|
"propertyName": "setpoint",
|
||||||
|
"propertyKey": 1,
|
||||||
"propertyKeyName": "Heating",
|
"propertyKeyName": "Heating",
|
||||||
"ccVersion": 3,
|
"ccVersion": 3,
|
||||||
"metadata": {
|
"metadata": {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue