8000 Add zwave js services get_lock_usercode and get_lock_usercodes · home-assistant/core@3f1d96f · GitHub
[go: up one dir, main page]

Skip to content

Commit 3f1d96f

Browse files
Add zwave js services get_lock_usercode and get_lock_usercodes
1 parent 6b8bddf commit 3f1d96f

File tree

7 files changed

+178
-2
lines changed

7 files changed

+178
-2
lines changed

homeassistant/components/zwave_js/const.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,8 @@
9999
# service constants
100100
SERVICE_BULK_SET_PARTIAL_CONFIG_PARAMETERS = "bulk_set_partial_config_parameters"
101101
SERVICE_CLEAR_LOCK_USERCODE = "clear_lock_usercode"
102+
SERVICE_GET_LOCK_USERCODE = "get_lock_usercode"
103+
SERVICE_GET_LOCK_USERCODES = "get_lock_usercodes"
102104
SERVICE_INVOKE_CC_API = "invoke_cc_api"
103105
SERVICE_MULTICAST_SET_VALUE = "multicast_set_value"
104106
SERVICE_PING = "ping"

homeassistant/components/zwave_js/device_action.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@
4444
ATTR_WAIT_FOR_RESULT,
4545
DOMAIN,
4646
SERVICE_CLEAR_LOCK_USERCODE,
47+
SERVICE_GET_LOCK_USERCODE,
48+
SERVICE_GET_LOCK_USERCODES,
4749
SERVICE_PING,
4850
SERVICE_REFRESH_VALUE,
4951
SERVICE_RESET_METER,
@@ -60,6 +62,8 @@
6062

6163
ACTION_TYPES = {
6264
SERVICE_CLEAR_LOCK_USERCODE,
65+
SERVICE_GET_LOCK_USERCODE,
66+
SERVICE_GET_LOCK_USERCODES,
6367
SERVICE_PING,
6468
SERVICE_REFRESH_VALUE,
6569
SERVICE_RESET_METER,
@@ -76,6 +80,21 @@
7680
}
7781
)
7882

83+
GET_LOCK_USERCODE_SCHEMA = cv.DEVICE_ACTION_BASE_SCHEMA.extend(
84+
{
85+
vol.Required(CONF_TYPE): SERVICE_GET_LOCK_USERCODE,
86+
vol.Required(CONF_ENTITY_ID): cv.entity_id_or_uuid,
87+
vol.Required(ATTR_CODE_SLOT): vol.Coerce(int),
88+
}
89+
)
90+
91+
GET_LOCK_USERCODES_SCHEMA = cv.DEVICE_ACTION_BASE_SCHEMA.extend(
92+
{
93+
vol.Required(CONF_TYPE): SERVICE_GET_LOCK_USERCODES,
94+
vol.Required(CONF_ENTITY_ID): cv.entity_id_or_uuid,
95+
}
96+
)
97+
7998
PING_SCHEMA = cv.DEVICE_ACTION_BASE_SCHEMA.extend(
8099
{
81100
vol.Required(CONF_TYPE): SERVICE_PING,
@@ -133,6 +152,8 @@
133152

134153
_ACTION_SCHEMA = vol.Any(
135154
CLEAR_LOCK_USERCODE_SCHEMA,
155+
GET_LOCK_USERCODE_SCHEMA,
156+
GET_LOCK_USERCODES_SCHEMA,
136157
PING_SCHEMA,
137158
REFRESH_VALUE_SCHEMA,
138159
RESET_METER_SCHEMA,
@@ -205,6 +226,8 @@ async def async_get_actions(
205226
if entry.domain == LOCK_DOMAIN:
206227
actions.extend(
207228
[
229+
{**entity_action, CONF_TYPE: SERVICE_GET_LOCK_USERCODE},
230+
{**entity_action, CONF_TYPE: SERVICE_GET_LOCK_USERCODES},
208231
{**entity_action, CONF_TYPE: SERVICE_SET_LOCK_USERCODE},
209232
{**entity_action, CONF_TYPE: SERVICE_CLEAR_LOCK_USERCODE},
210233
]
@@ -272,6 +295,8 @@ async def async_call_action_from_config(
272295
# Entity services (including refresh value which is a fake entity service) expect
273296
# just an entity ID
274297
if action_type in (
298+
SERVICE_GET_LOCK_USERCODE,
299+
SERVICE_GET_LOCK_USERCODES,
275300
SERVICE_REFRESH_VALUE,
276301
SERVICE_SET_LOCK_USERCODE,
277302
SERVICE_CLEAR_LOCK_USERCODE,
@@ -300,6 +325,15 @@ async def async_get_action_capabilities(
300325
)
301326
}
302327

328+
if action_type == SERVICE_GET_LOCK_USERCODE:
329+
return {
330+
"extra_fields": vol.Schema(
331+
{
332+
vol.Required(ATTR_CODE_SLOT): cv.string,
333+
}
334+
)
335+
}
336+
303337
if action_type == SERVICE_SET_LOCK_USERCODE:
304338
return {
305339
"extra_fields": vol.Schema(

homeassistant/components/zwave_js/icons.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@
5959
"services": {
6060
"bulk_set_partial_config_parameters": "mdi:cogs",
6161
"clear_lock_usercode": "mdi:eraser",
62+
"get_lock_usercode": "mdi:lock-percent-outline",
63+
"get_lock_usercodes": "mdi:lock-percent",
6264
"invoke_cc_api": "mdi:api",
6365
"multicast_set_value": "mdi:list-box",
6466
"ping": "mdi:crosshairs-gps",

homeassistant/components/zwave_js/lock.py

Lines changed: 68 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,28 @@
1717
OperationType,
1818
)
1919
from zwave_js_server.exceptions import BaseZwaveJSServerError
20-
from zwave_js_server.util.lock import clear_usercode, set_configuration, set_usercode
20+
from zwave_js_server.util.lock import (
21+
clear_usercode,
22+
get_usercode,
23+
get_usercodes,
24+
set_configuration,
25+
set_usercode,
26+
)
2127

2228
from homeassistant.components.lock import DOMAIN as LOCK_DOMAIN, LockEntity
2329
from homeassistant.config_entries import ConfigEntry
2430
from homeassistant.const import STATE_LOCKED, STATE_UNLOCKED
25-
from homeassistant.core import HomeAssistant, callback
31+
from homeassistant.core import (
32+
HomeAssistant,
33+
ServiceResponse,
34+
SupportsResponse,
35+
callback,
36+
)
2637
from homeassistant.exceptions import HomeAssistantError
2738
from homeassistant.helpers import config_validation as cv, entity_platform
2839
from homeassistant.helpers.dispatcher import async_dispatcher_connect
2940
from homeassistant.helpers.entity_platform import AddEntitiesCallback
41+
from homeassistant.util.json import JsonValueType
3042

3143
from .const import (
3244
ATTR_AUTO_RELOCK_TIME,
@@ -39,6 +51,8 @@
3951
DOMAIN,
4052
LOGGER,
4153
SERVICE_CLEAR_LOCK_USERCODE,
54+
SERVICE_GET_LOCK_USERCODE,
55+
SERVICE_GET_LOCK_USERCODES,
4256
SERVICE_SET_LOCK_CONFIGURATION,
4357
SERVICE_SET_LOCK_USERCODE,
4458
)
@@ -86,6 +100,22 @@ def async_add_lock(info: ZwaveDiscoveryInfo) -> None:
86100

87101
platform = entity_platform.async_get_current_platform()
88102

103+
platform.async_register_entity_service(
104+
SERVICE_GET_LOCK_USERCODE,
105+
{
106+
vol.Required(ATTR_CODE_SLOT): vol.Coerce(int),
107+
},
108+
"async_get_lock_usercode",
109+
supports_response=SupportsResponse.ONLY,
110+
)
111+
112+
platform.async_register_entity_service(
113+
SERVICE_GET_LOCK_USERCODES,
114+
{},
115+
"async_get_lock_usercodes",
116+
supports_response=SupportsResponse.ONLY,
117+
)
118+
89119
platform.async_register_entity_service(
90120
SERVICE_SET_LOCK_USERCODE,
91121
{
@@ -161,6 +191,42 @@ async def async_unlock(self, **kwargs: Any) -> None:
161191
"""Unlock the lock."""
162192
await self._set_lock_state(STATE_UNLOCKED)
163193

194+
async def async_get_lock_usercode(self, code_slot: int) -> ServiceResponse:
195+
"""Get the usercode at index X on the lock."""
196+
try:
197+
code_slot_obj = get_usercode(self.info.node, code_slot)
198+
except BaseZwaveJSServerError as err:
199+
raise HomeAssistantError(
200+
f"Unable to get usercode for lock {self.entity_id} code_slot "
201+
f"{code_slot}: {err}"
202+
) from err
203+
LOGGER.debug(
204+
"User code at slot %s on lock %s retrieved", code_slot, self.entity_id
205+
)
206+
usercode: str | None = code_slot_obj.get(ATTR_USERCODE) # type: ignore[assignment]
207+
return {"usercode": usercode}
208+
209+
async def async_get_lock_usercodes(self) -> ServiceResponse:
210+
"""Get a dictionary of code slots to usercodes for the lock.
211+
212+
Code slots without usercodes are ignored.
213+
"""
214+
try:
215+
code_slots = get_usercodes(self.info.node)
216+
except BaseZwaveJSServerError as err:
217+
raise HomeAssistantError(
218+
f"Unable to get usercodes for lock {self.entity_id}: {err}"
219+
) from err
220+
LOGGER.debug("User codes for lock %s retrieved", self.entity_id)
221+
usercodes: dict[str, JsonValueType] = {
222+
str(code_slot.get(ATTR_CODE_SLOT)): code_slot.get(ATTR_USERCODE) # type: ignore[misc]
223+
for code_slot in code_slots
224+
if ATTR_CODE_SLOT in code_slot
225+
and ATTR_USERCODE in code_slot
226+
and code_slot.get(ATTR_USERCODE)
227+
}
228+
return {"usercodes": usercodes}
229+
164230
async def async_set_lock_usercode(self, code_slot: int, usercode: str) -> None:
165231
"""Set the usercode to index X on the lock."""
166232
try:

homeassistant/components/zwave_js/services.yaml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,24 @@ clear_lock_usercode:
1212
selector:
1313
text:
1414

15+
get_lock_usercode:
16+
target:
17+
entity:
18+
domain: lock
19+
integration: zwave_js
20+
fields:
21+
code_slot:
22+
required: true
23+
example: 1
24+
selector:
25+
text:
26+
27+
get_lock_usercodes:
28+
target:
29+
entity:
30+
domain: lock
31+
integration: zwave_js
32+
1533
set_lock_usercode:
1634
target:
1735
entity:

homeassistant/components/zwave_js/strings.json

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,8 @@
6969
"device_automation": {
7070
"action_type": {
7171
"clear_lock_usercode": "Clear usercode on {entity_name}",
72+
"get_lock_usercode": "Get a usercode for {entity_name}",
73+
"get_lock_usercodes": "Get all usercodes for {entity_name}",
7274
"ping": "Ping device",
7375
"refresh_value": "Refresh the value(s) for {entity_name}",
7476
"reset_meter": "Reset meters on {subtype}",
@@ -290,6 +292,21 @@
290292
},
291293
"name": "Clear lock user code"
292294
},
295+
"get_lock_usercode": {
296+
"description": "Get a user code for a lock.",
297+
"fields": {
298+
"code_slot": {
299+
"description": "Code slot to get the code.",
300+
"name": "[%key:component::zwave_js::services::clear_lock_usercode::fields::code_slot::name%]"
301+
}
302+
},
303+
"name": "Get lock user code"
304+
},
305+
"get_lock_usercodes": {
306+
"description": "Get all user codes for a lock.",
307+
"fields": {},
308+
"name": "Get lock user codes"
309+
},
293310
"invoke_cc_api": {
294311
"description": "Calls a Command Class API on a node. Some Command Classes can't be fully controlled via the `set_value` service and require direct calls to the Command Class API.",
295312
"fields": {

tests/components/zwave_js/test_lock.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
ATTR_LOCK_TIMEOUT,
2121
ATTR_OPERATION_TYPE,
2222
DOMAIN as ZWAVE_JS_DOMAIN,
23+
SERVICE_GET_LOCK_USERCODE,
24+
SERVICE_GET_LOCK_USERCODES,
2325
)
2426
from homeassistant.components.zwave_js.helpers import ZwaveValueMatcher
2527
from homeassistant.components.zwave_js.lock import (
@@ -146,6 +148,41 @@ async def test_door_lock(
146148

147149
client.async_send_command.reset_mock()
148150

151+
# Test get usercode service
152+
response = await hass.services.async_call(
153+
ZWAVE_JS_DOMAIN,
154+
SERVICE_GET_LOCK_USERCODE,
155+
{
156+
ATTR_ENTITY_ID: SCHLAGE_BE469_LOCK_ENTITY,
157+
ATTR_CODE_SLOT: 1,
158+
},
159+
blocking=True,
160+
return_response=True,
161+
)
162+
163+
assert response[SCHLAGE_BE469_LOCK_ENTITY]["usercode"] == "**********"
164+
165+
client.async_send_command.reset_mock()
166+
167+
# Test get usercodes service
168+
response = await hass.services.async_call(
169+
ZWAVE_JS_DOMAIN,
170+
SERVICE_GET_LOCK_USERCODES,
171+
{
172+
ATTR_ENTITY_ID: SCHLAGE_BE469_LOCK_ENTITY,
173+
},
174+
blocking=True,
175+
return_response=True,
176+
)
177+
178+
assert response[SCHLAGE_BE469_LOCK_ENTITY]["usercodes"] == {
179+
"1": "**********",
180+
"2": "**********",
181+
"3": "**********",
182+
}
183+
184+
client.async_send_command.reset_mock()
185+
149186
# Test clear usercode
150187
await hass.services.async_call(
151188
ZWAVE_JS_DOMAIN,

0 commit comments

Comments
 (0)
0