From e9920ff73d3f3a218a795e056ef91031ff663e53 Mon Sep 17 00:00:00 2001 From: Raman Gupta <7243222+raman325@users.noreply.github.com> Date: Thu, 23 Nov 2023 06:51:29 -0500 Subject: [PATCH] Add select entity for zwave_js Door Lock CC (#104292) * Add select entity for zwave_js Door Lock CC * fix test --- .../components/zwave_js/discovery.py | 9 +++++- homeassistant/components/zwave_js/select.py | 28 +++++++++++++++++-- tests/components/zwave_js/test_discovery.py | 1 + tests/components/zwave_js/test_init.py | 4 +-- tests/components/zwave_js/test_select.py | 27 ++++++++++++++++++ 5 files changed, 64 insertions(+), 5 deletions(-) diff --git a/homeassistant/components/zwave_js/discovery.py b/homeassistant/components/zwave_js/discovery.py index 39d8c0e8855..ab1d0660cca 100644 --- a/homeassistant/components/zwave_js/discovery.py +++ b/homeassistant/components/zwave_js/discovery.py @@ -664,7 +664,14 @@ DISCOVERY_SCHEMAS = [ # locks # Door Lock CC ZWaveDiscoverySchema( - platform=Platform.LOCK, primary_value=DOOR_LOCK_CURRENT_MODE_SCHEMA + platform=Platform.LOCK, + primary_value=DOOR_LOCK_CURRENT_MODE_SCHEMA, + allow_multi=True, + ), + ZWaveDiscoverySchema( + platform=Platform.SELECT, + primary_value=DOOR_LOCK_CURRENT_MODE_SCHEMA, + hint="door_lock", ), # Only discover the Lock CC if the Door Lock CC isn't also present on the node ZWaveDiscoverySchema( diff --git a/homeassistant/components/zwave_js/select.py b/homeassistant/components/zwave_js/select.py index 3956004336a..e838949d3e1 100644 --- a/homeassistant/components/zwave_js/select.py +++ b/homeassistant/components/zwave_js/select.py @@ -5,7 +5,8 @@ from typing import cast from zwave_js_server.client import Client as ZwaveClient from zwave_js_server.const import TARGET_VALUE_PROPERTY, CommandClass -from zwave_js_server.const.command_class.sound_switch import ToneID +from zwave_js_server.const.command_class.lock import TARGET_MODE_PROPERTY +from zwave_js_server.const.command_class.sound_switch import TONE_ID_PROPERTY, ToneID from zwave_js_server.model.driver import Driver from homeassistant.components.select import DOMAIN as SELECT_DOMAIN, SelectEntity @@ -46,6 +47,8 @@ async def async_setup_entry( entities.append( ZWaveConfigParameterSelectEntity(config_entry, driver, info) ) + elif info.platform_hint == "door_lock": + entities.append(ZWaveDoorLockSelectEntity(config_entry, driver, info)) else: entities.append(ZwaveSelectEntity(config_entry, driver, info)) async_add_entities(entities) @@ -95,6 +98,27 @@ class ZwaveSelectEntity(ZWaveBaseEntity, SelectEntity): await self._async_set_value(self.info.primary_value, int(key)) +class ZWaveDoorLockSelectEntity(ZwaveSelectEntity): + """Representation of a Z-Wave door lock CC mode select entity.""" + + def __init__( + self, config_entry: ConfigEntry, driver: Driver, info: ZwaveDiscoveryInfo + ) -> None: + """Initialize a ZWaveDoorLockSelectEntity entity.""" + super().__init__(config_entry, driver, info) + self._target_value = self.get_zwave_value(TARGET_MODE_PROPERTY) + + async def async_select_option(self, option: str) -> None: + """Change the selected option.""" + assert self._target_value is not None + key = next( + key + for key, val in self.info.primary_value.metadata.states.items() + if val == option + ) + await self._async_set_value(self._target_value, int(key)) + + class ZWaveConfigParameterSelectEntity(ZwaveSelectEntity): """Representation of a Z-Wave config parameter select.""" @@ -125,7 +149,7 @@ class ZwaveDefaultToneSelectEntity(ZWaveBaseEntity, SelectEntity): """Initialize a ZwaveDefaultToneSelectEntity entity.""" super().__init__(config_entry, driver, info) self._tones_value = self.get_zwave_value( - "toneId", command_class=CommandClass.SOUND_SWITCH + TONE_ID_PROPERTY, command_class=CommandClass.SOUND_SWITCH ) # Entity class attributes diff --git a/tests/components/zwave_js/test_discovery.py b/tests/components/zwave_js/test_discovery.py index cbaa27c2a91..569e36d3b5c 100644 --- a/tests/components/zwave_js/test_discovery.py +++ b/tests/components/zwave_js/test_discovery.py @@ -87,6 +87,7 @@ async def test_lock_popp_electric_strike_lock_control( hass.states.get("binary_sensor.node_62_the_current_status_of_the_door") is not None ) + assert hass.states.get("select.node_62_current_lock_mode") is not None async def test_fortrez_ssa3_siren( diff --git a/tests/components/zwave_js/test_init.py b/tests/components/zwave_js/test_init.py index c57e3b1f868..bf015a70676 100644 --- a/tests/components/zwave_js/test_init.py +++ b/tests/components/zwave_js/test_init.py @@ -967,7 +967,7 @@ async def test_removed_device( # Check how many entities there are ent_reg = er.async_get(hass) entity_entries = er.async_entries_for_config_entry(ent_reg, integration.entry_id) - assert len(entity_entries) == 92 + assert len(entity_entries) == 93 # Remove a node and reload the entry old_node = driver.controller.nodes.pop(13) @@ -979,7 +979,7 @@ async def test_removed_device( device_entries = dr.async_entries_for_config_entry(dev_reg, integration.entry_id) assert len(device_entries) == 2 entity_entries = er.async_entries_for_config_entry(ent_reg, integration.entry_id) - assert len(entity_entries) == 61 + assert len(entity_entries) == 62 assert ( dev_reg.async_get_device(identifiers={get_device_id(driver, old_node)}) is None ) diff --git a/tests/components/zwave_js/test_select.py b/tests/components/zwave_js/test_select.py index c63f0c429fd..1cbdb8799f3 100644 --- a/tests/components/zwave_js/test_select.py +++ b/tests/components/zwave_js/test_select.py @@ -320,3 +320,30 @@ async def test_config_parameter_select( state = hass.states.get(select_entity_id) assert state assert state.state == "Normal" + + +async def test_lock_popp_electric_strike_lock_control_select( + hass: HomeAssistant, client, lock_popp_electric_strike_lock_control, integration +) -> None: + """Test that the Popp Electric Strike Lock Control select entity.""" + LOCK_SELECT_ENTITY = "select.node_62_current_lock_mode" + state = hass.states.get(LOCK_SELECT_ENTITY) + assert state + assert state.state == "Unsecured" + await hass.services.async_call( + "select", + "select_option", + {"entity_id": LOCK_SELECT_ENTITY, "option": "UnsecuredWithTimeout"}, + blocking=True, + ) + + assert len(client.async_send_command.call_args_list) == 1 + args = client.async_send_command.call_args[0][0] + assert args["command"] == "node.set_value" + assert args["nodeId"] == lock_popp_electric_strike_lock_control.node_id + assert args["valueId"] == { + "endpoint": 0, + "commandClass": 98, + "property": "targetMode", + } + assert args["value"] == 1