Add lock util function to set multiple usercodes ()

* Add lock util function to set multiple usercodes

* restore ignores

* Add coverage

* rename

* Update test/util/test_lock.py

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>

* Update test/util/test_lock.py

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>

* Update lock.py

* Update lock.py

---------

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
This commit is contained in:
Raman Gupta 2024-10-10 12:43:42 -04:00 committed by GitHub
parent 8ef40fa462
commit 33881bd2b2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 93 additions and 1 deletions
test/util
zwave_js_server
const/command_class
util

View File

@ -11,11 +11,16 @@ from zwave_js_server.const.command_class.lock import (
ATTR_IN_USE,
ATTR_NAME,
ATTR_USERCODE,
LOCK_USERCODE_ID_PROPERTY,
LOCK_USERCODE_PROPERTY,
LOCK_USERCODE_STATUS_PROPERTY,
CodeSlotStatus,
DoorLockCCConfigurationSetOptions,
OperationType,
)
from zwave_js_server.exceptions import NotFoundError
from zwave_js_server.model.node import Node
from zwave_js_server.model.value import SupervisionResult
from zwave_js_server.util.lock import (
clear_usercode,
get_code_slots,
@ -24,6 +29,7 @@ from zwave_js_server.util.lock import (
get_usercodes,
set_configuration,
set_usercode,
set_usercodes,
)
from .const import CODE_SLOTS
@ -102,6 +108,61 @@ async def test_set_usercode(lock_schlage_be469, mock_command, uuid4):
assert len(ack_commands) == 1
async def test_set_usercodes(
lock_schlage_be469: Node, mock_command: MockCommandProtocol, uuid4: str
) -> None:
"""Test set_usercodes utility function."""
node = lock_schlage_be469
ack_commands = mock_command(
{"command": "endpoint.invoke_cc_api", "endpoint": 0, "nodeId": node.node_id},
{"response": {"status": 255}},
)
# Test wrong types to ensure values get converted
assert await set_usercodes(node, {"1": 1234}) == SupervisionResult(
{"status": SupervisionStatus.SUCCESS}
)
assert len(ack_commands) == 1
assert ack_commands[0] == {
"command": "endpoint.invoke_cc_api",
"commandClass": 99,
"endpoint": 0,
"methodName": "setMany",
"nodeId": 20,
"messageId": uuid4,
"args": [
[
{
LOCK_USERCODE_STATUS_PROPERTY: CodeSlotStatus.ENABLED,
LOCK_USERCODE_ID_PROPERTY: 1,
LOCK_USERCODE_PROPERTY: "1234",
}
]
],
}
# Test invalid code length
with pytest.raises(ValueError):
await set_usercodes(node, {1: "123"})
# assert no new command calls
assert len(ack_commands) == 1
async def test_set_usercodes_invalid(
lock_schlage_be469: Node, mock_command: MockCommandProtocol
) -> None:
"""Test set_usercodes utility function with invalid response."""
node = lock_schlage_be469
mock_command(
{"command": "endpoint.invoke_cc_api", "endpoint": 0, "nodeId": node.node_id},
{"response": {}},
)
with pytest.raises(ValueError):
assert await set_usercodes(node, {"1": 1234})
async def test_clear_usercode(lock_schlage_be469, mock_command, uuid4):
"""Test clear_usercode utility function."""
node = lock_schlage_be469

View File

@ -118,6 +118,7 @@ LOCKED_PROPERTY = "locked"
# User Code CC constants
LOCK_USERCODE_PROPERTY = "userCode"
LOCK_USERCODE_ID_PROPERTY = "userId"
LOCK_USERCODE_STATUS_PROPERTY = "userIdStatus"
ATTR_CODE_SLOT = "code_slot"

View File

@ -14,6 +14,7 @@ from ..const.command_class.lock import (
CURRENT_BLOCK_TO_BLOCK_PROPERTY,
CURRENT_HOLD_AND_RELEASE_TIME_PROPERTY,
CURRENT_TWIST_ASSIST_PROPERTY,
LOCK_USERCODE_ID_PROPERTY,
LOCK_USERCODE_PROPERTY,
LOCK_USERCODE_STATUS_PROPERTY,
CodeSlotStatus,
@ -130,6 +131,7 @@ async def get_usercode_from_node(node: Node, code_slot: int) -> CodeSlot:
This call will populate the ValueDB and trigger value update events from the
driver.
"""
# https://zwave-js.github.io/node-zwave-js/#/api/CCs/UserCode?id=get
await node.async_invoke_cc_api(
CommandClass.USER_CODE, "get", code_slot, wait_for_result=True
)
@ -141,13 +143,40 @@ async def set_usercode(
) -> SetValueResult | None:
"""Set the usercode to index X on the lock."""
value = get_code_slot_value(node, code_slot, LOCK_USERCODE_PROPERTY)
usercode = str(usercode)
if len(str(usercode)) < 4:
if len(usercode) < 4:
raise ValueError("User code must be at least 4 digits")
return await node.async_set_value(value, usercode)
async def set_usercodes(node: Node, codes: dict[int, str]) -> SupervisionResult | None:
"""Set the usercode to index X on the lock."""
cc_api_codes = [
{
LOCK_USERCODE_ID_PROPERTY: int(code_slot),
LOCK_USERCODE_STATUS_PROPERTY: CodeSlotStatus.ENABLED,
LOCK_USERCODE_PROPERTY: str(usercode),
}
for code_slot, usercode in codes.items()
if len(str(usercode)) >= 4
]
if len(cc_api_codes) < len(codes):
raise ValueError("User codes must be at least 4 digits")
# https://zwave-js.github.io/node-zwave-js/#/api/CCs/UserCode?id=setmany
data = await node.async_invoke_cc_api(
CommandClass.USER_CODE, "setMany", cc_api_codes, wait_for_result=True
)
if not data:
raise ValueError("Received unexpected response from User Code CC setMany API")
return SupervisionResult(data)
async def clear_usercode(node: Node, code_slot: int) -> SetValueResult | None:
"""Clear a code slot on the lock."""
value = get_code_slot_value(node, code_slot, LOCK_USERCODE_STATUS_PROPERTY)
@ -197,6 +226,7 @@ async def set_configuration(
if errors:
raise ValueError("\n".join(errors))
# https://zwave-js.github.io/node-zwave-js/#/api/CCs/UserCode?id=setconfiguration
data = await endpoint.async_invoke_cc_api(
CommandClass.DOOR_LOCK, "setConfiguration", configuration.to_dict()
)