diff --git a/homeassistant/components/zwave_js/__init__.py b/homeassistant/components/zwave_js/__init__.py index 637ea0fe08a..519771c1a49 100644 --- a/homeassistant/components/zwave_js/__init__.py +++ b/homeassistant/components/zwave_js/__init__.py @@ -48,7 +48,8 @@ from .const import ( ZWAVE_JS_EVENT, ) 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 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) 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 def async_on_node_ready(node: ZwaveNode) -> None: """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) # 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 - # (as well as get_old_value_id helper and migrate_entity closure) can be - # removed. - 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) - + # the value_id format. Some time in the future, this call (as well as the + # helper functions) can be removed. + async_migrate_discovered_value(ent_reg, client, disc_info) async_dispatcher_send( hass, f"{DOMAIN}_{entry.entry_id}_add_{disc_info.platform}", disc_info ) diff --git a/homeassistant/components/zwave_js/climate.py b/homeassistant/components/zwave_js/climate.py index cc449b89e91..325cf14b379 100644 --- a/homeassistant/components/zwave_js/climate.py +++ b/homeassistant/components/zwave_js/climate.py @@ -125,18 +125,10 @@ class ZWaveClimate(ZWaveBaseEntity, ClimateEntity): ) self._setpoint_values: Dict[ThermostatSetpointType, ZwaveValue] = {} 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( THERMOSTAT_SETPOINT_PROPERTY, command_class=CommandClass.THERMOSTAT_SETPOINT, 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, ) # Use the first found setpoint value to always determine the temperature unit diff --git a/homeassistant/components/zwave_js/entity.py b/homeassistant/components/zwave_js/entity.py index d0ed9eb5291..c061abc4d0d 100644 --- a/homeassistant/components/zwave_js/entity.py +++ b/homeassistant/components/zwave_js/entity.py @@ -169,7 +169,6 @@ class ZWaveBaseEntity(Entity): command_class: Optional[int] = None, endpoint: Optional[int] = None, value_property_key: Optional[int] = None, - value_property_key_name: Optional[str] = None, add_to_watched_value_ids: bool = True, check_all_endpoints: bool = False, ) -> Optional[ZwaveValue]: @@ -188,7 +187,6 @@ class ZWaveBaseEntity(Entity): value_property, endpoint=endpoint, property_key=value_property_key, - property_key_name=value_property_key_name, ) return_value = self.info.node.values.get(value_id) @@ -203,7 +201,6 @@ class ZWaveBaseEntity(Entity): value_property, endpoint=endpoint_.index, property_key=value_property_key, - property_key_name=value_property_key_name, ) return_value = self.info.node.values.get(value_id) if return_value: diff --git a/homeassistant/components/zwave_js/helpers.py b/homeassistant/components/zwave_js/helpers.py index 9582b7ee054..16baeb816c2 100644 --- a/homeassistant/components/zwave_js/helpers.py +++ b/homeassistant/components/zwave_js/helpers.py @@ -3,7 +3,6 @@ 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 @@ -13,16 +12,6 @@ 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.""" diff --git a/homeassistant/components/zwave_js/light.py b/homeassistant/components/zwave_js/light.py index d9c31210bea..b501ecb58e7 100644 --- a/homeassistant/components/zwave_js/light.py +++ b/homeassistant/components/zwave_js/light.py @@ -228,7 +228,6 @@ class ZwaveLight(ZWaveBaseEntity, LightEntity): "targetColor", CommandClass.SWITCH_COLOR, value_property_key=None, - value_property_key_name=None, ) if combined_color_val and isinstance(combined_color_val.value, dict): colors_dict = {} @@ -252,7 +251,6 @@ class ZwaveLight(ZWaveBaseEntity, LightEntity): "targetColor", CommandClass.SWITCH_COLOR, value_property_key=property_key.key, - value_property_key_name=property_key.name, ) if target_zwave_value is None: # guard for unsupported color @@ -318,31 +316,26 @@ class ZwaveLight(ZWaveBaseEntity, LightEntity): "currentColor", CommandClass.SWITCH_COLOR, value_property_key=ColorComponent.RED.value.key, - value_property_key_name=ColorComponent.RED.value.name, ) green_val = self.get_zwave_value( "currentColor", CommandClass.SWITCH_COLOR, value_property_key=ColorComponent.GREEN.value.key, - value_property_key_name=ColorComponent.GREEN.value.name, ) blue_val = self.get_zwave_value( "currentColor", CommandClass.SWITCH_COLOR, value_property_key=ColorComponent.BLUE.value.key, - value_property_key_name=ColorComponent.BLUE.value.name, ) ww_val = self.get_zwave_value( "currentColor", CommandClass.SWITCH_COLOR, value_property_key=ColorComponent.WARM_WHITE.value.key, - value_property_key_name=ColorComponent.WARM_WHITE.value.name, ) cw_val = self.get_zwave_value( "currentColor", CommandClass.SWITCH_COLOR, value_property_key=ColorComponent.COLD_WHITE.value.key, - value_property_key_name=ColorComponent.COLD_WHITE.value.name, ) # prefer the (new) combined color property # https://github.com/zwave-js/node-zwave-js/pull/1782 @@ -350,7 +343,6 @@ class ZwaveLight(ZWaveBaseEntity, LightEntity): "currentColor", CommandClass.SWITCH_COLOR, value_property_key=None, - value_property_key_name=None, ) if combined_color_val and isinstance(combined_color_val.value, dict): multi_color = combined_color_val.value diff --git a/homeassistant/components/zwave_js/manifest.json b/homeassistant/components/zwave_js/manifest.json index c812515a179..2a6f036fa80 100644 --- a/homeassistant/components/zwave_js/manifest.json +++ b/homeassistant/components/zwave_js/manifest.json @@ -3,7 +3,7 @@ "name": "Z-Wave JS", "config_flow": true, "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"], "dependencies": ["http", "websocket_api"] } diff --git a/homeassistant/components/zwave_js/migrate.py b/homeassistant/components/zwave_js/migrate.py new file mode 100644 index 00000000000..49c18073de5 --- /dev/null +++ b/homeassistant/components/zwave_js/migrate.py @@ -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 diff --git a/requirements_all.txt b/requirements_all.txt index 465aaadd454..99854ac3d9f 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2394,4 +2394,4 @@ zigpy==0.32.0 zm-py==0.5.2 # homeassistant.components.zwave_js -zwave-js-server-python==0.20.1 +zwave-js-server-python==0.21.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 06fb55c9f0b..4517457935b 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1231,4 +1231,4 @@ zigpy-znp==0.4.0 zigpy==0.32.0 # homeassistant.components.zwave_js -zwave-js-server-python==0.20.1 +zwave-js-server-python==0.21.0 diff --git a/tests/components/zwave_js/test_api.py b/tests/components/zwave_js/test_api.py index 9760e1a06b7..78bac5518a0 100644 --- a/tests/components/zwave_js/test_api.py +++ b/tests/components/zwave_js/test_api.py @@ -71,7 +71,7 @@ async def test_websocket_api(hass, integration, multisensor_6, hass_ws_client): result = msg["result"] assert len(result) == 61 - key = "52-112-0-2-00-00" + key = "52-112-0-2" assert result[key]["property"] == 2 assert result[key]["metadata"]["type"] == "number" assert result[key]["configuration_value_type"] == "enumerated" diff --git a/tests/components/zwave_js/test_climate.py b/tests/components/zwave_js/test_climate.py index ea75f10328c..fe3e0708acc 100644 --- a/tests/components/zwave_js/test_climate.py +++ b/tests/components/zwave_js/test_climate.py @@ -405,7 +405,7 @@ async def test_setpoint_thermostat(hass, client, climate_danfoss_lc_13, integrat assert state 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_PRESET_MODE] == PRESET_NONE @@ -432,6 +432,7 @@ async def test_setpoint_thermostat(hass, client, climate_danfoss_lc_13, integrat "commandClassName": "Thermostat Setpoint", "property": "setpoint", "propertyName": "setpoint", + "propertyKey": 1, "propertyKeyName": "Heating", "ccVersion": 2, "metadata": { @@ -441,7 +442,7 @@ async def test_setpoint_thermostat(hass, client, climate_danfoss_lc_13, integrat "unit": "\u00b0C", "ccSpecific": {"setpointType": 1}, }, - "value": 25, + "value": 14, } assert args["value"] == 21.5 @@ -459,6 +460,7 @@ async def test_setpoint_thermostat(hass, client, climate_danfoss_lc_13, integrat "commandClass": 67, "endpoint": 0, "property": "setpoint", + "propertyKey": 1, "propertyKeyName": "Heating", "propertyName": "setpoint", "newValue": 23, diff --git a/tests/components/zwave_js/test_init.py b/tests/components/zwave_js/test_init.py index 6f60bbc0300..cd2017f2c66 100644 --- a/tests/components/zwave_js/test_init.py +++ b/tests/components/zwave_js/test_init.py @@ -160,7 +160,7 @@ async def test_unique_id_migration_dupes( # 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" + new_unique_id = f"{client.driver.controller.home_id}.52-49-0-Air temperature" assert entity_entry.unique_id == new_unique_id 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 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 @@ -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 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 @@ -262,7 +402,7 @@ async def test_unique_id_migration_notification_binary_sensor( # 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" + 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 diff --git a/tests/fixtures/zwave_js/climate_danfoss_lc_13_state.json b/tests/fixtures/zwave_js/climate_danfoss_lc_13_state.json index 90410998597..8574674714f 100644 --- a/tests/fixtures/zwave_js/climate_danfoss_lc_13_state.json +++ b/tests/fixtures/zwave_js/climate_danfoss_lc_13_state.json @@ -4,11 +4,25 @@ "status": 1, "ready": true, "deviceClass": { - "basic": {"key": 4, "label":"Routing Slave"}, - "generic": {"key": 8, "label":"Thermostat"}, - "specific": {"key": 4, "label":"Setpoint Thermostat"}, - "mandatorySupportedCCs": [], - "mandatoryControlCCs": [] + "basic": { + "key": 4, + "label": "Routing Slave" + }, + "generic": { + "key": 8, + "label": "Thermostat" + }, + "specific": { + "key": 4, + "label": "Setpoint Thermostat" + }, + "mandatorySupportedCCs": [ + 114, + 143, + 67, + 134 + ], + "mandatoryControlledCCs": [] }, "isListening": false, "isFrequentListening": false, @@ -22,6 +36,7 @@ "productType": 5, "firmwareVersion": "1.1", "deviceConfig": { + "filename": "/usr/src/app/node_modules/@zwave-js/config/config/devices/0x0002/lc-13.json", "manufacturerId": 2, "manufacturer": "Danfoss", "label": "LC-13", @@ -66,19 +81,76 @@ 14 ], "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": [ { "nodeId": 5, "index": 0 } ], - "commandClasses": [], "values": [ { "endpoint": 0, "commandClass": 67, "commandClassName": "Thermostat Setpoint", "property": "setpoint", + "propertyKey": 1, "propertyName": "setpoint", "propertyKeyName": "Heating", "ccVersion": 2, @@ -91,7 +163,7 @@ "setpointType": 1 } }, - "value": 25 + "value": 14 }, { "endpoint": 0, @@ -262,7 +334,7 @@ "unit": "%", "label": "Battery level" }, - "value": 53 + "value": 49 }, { "endpoint": 0, @@ -361,4 +433,4 @@ ] } ] -} \ No newline at end of file +} diff --git a/tests/fixtures/zwave_js/climate_heatit_z_trm3_state.json b/tests/fixtures/zwave_js/climate_heatit_z_trm3_state.json index 0dc040c6cb2..b26b69be9ad 100644 --- a/tests/fixtures/zwave_js/climate_heatit_z_trm3_state.json +++ b/tests/fixtures/zwave_js/climate_heatit_z_trm3_state.json @@ -837,6 +837,7 @@ "commandClassName": "Thermostat Setpoint", "property": "setpoint", "propertyName": "setpoint", + "propertyKey": 1, "propertyKeyName": "Heating", "ccVersion": 3, "metadata": {