Skip to content

Commit 2146213

Browse files
committed
lock api connected
1 parent 7779a7c commit 2146213

File tree

3 files changed

+72
-9
lines changed

3 files changed

+72
-9
lines changed

tests/test_lock.py

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
from unittest.mock import patch
44

5+
import pytest
56
import zigpy.profiles.zha
67
from zigpy.zcl.clusters import closures, general
78
import zigpy.zcl.foundation as zcl_f
@@ -17,6 +18,7 @@
1718
send_attributes_report,
1819
update_attribute_cache,
1920
)
21+
from tests.conftest import CombinedGateways
2022
from zha.application import Platform
2123
from zha.application.gateway import Gateway
2224
from zha.application.platforms import PlatformEntity
@@ -39,9 +41,14 @@
3941
}
4042

4143

42-
async def test_lock(zha_gateway: Gateway) -> None:
44+
@pytest.mark.parametrize(
45+
"gateway_type",
46+
["zha_gateway", "ws_gateway"],
47+
)
48+
async def test_lock(zha_gateways: CombinedGateways, gateway_type: str) -> None:
4349
"""Test zha lock platform."""
4450

51+
zha_gateway = getattr(zha_gateways, gateway_type)
4552
zigpy_device = create_mock_zigpy_device(zha_gateway, ZIGPY_LOCK)
4653
zha_device = await join_zigpy_device(zha_gateway, zigpy_device)
4754
cluster = zigpy_device.endpoints[1].door_lock
@@ -205,8 +212,15 @@ async def async_disable_user_code(
205212
assert cluster.request.call_args[0][4] == closures.DoorLock.UserStatus.Disabled
206213

207214

208-
async def test_lock_state_restoration(zha_gateway: Gateway) -> None:
215+
@pytest.mark.parametrize(
216+
"gateway_type",
217+
["zha_gateway", "ws_gateway"],
218+
)
219+
async def test_lock_state_restoration(
220+
zha_gateways: CombinedGateways, gateway_type: str
221+
) -> None:
209222
"""Test the lock state restoration."""
223+
zha_gateway = getattr(zha_gateways, gateway_type)
210224
zigpy_device = create_mock_zigpy_device(zha_gateway, ZIGPY_LOCK)
211225
zha_device = await join_zigpy_device(zha_gateway, zigpy_device)
212226

@@ -215,7 +229,9 @@ async def test_lock_state_restoration(zha_gateway: Gateway) -> None:
215229
assert entity.state["is_locked"] is False
216230

217231
entity.restore_external_state_attributes(state=STATE_LOCKED)
232+
await zha_gateway.async_block_till_done() # needed for WS commands
218233
assert entity.state["is_locked"] is True
219234

220235
entity.restore_external_state_attributes(state=STATE_UNLOCKED)
236+
await zha_gateway.async_block_till_done() # needed for WS commands
221237
assert entity.state["is_locked"] is False

zha/application/platforms/lock/__init__.py

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from __future__ import annotations
44

55
from abc import ABC, abstractmethod
6+
import asyncio
67
import functools
78
from typing import TYPE_CHECKING, Any, Literal
89

@@ -127,27 +128,29 @@ async def async_unlock(self) -> None:
127128
self._state = STATE_UNLOCKED
128129
self.maybe_emit_state_changed_event()
129130

130-
async def async_set_lock_user_code(self, code_slot: int, user_code: str) -> None:
131+
async def async_set_lock_user_code(
132+
self, code_slot: int, user_code: str, **kwargs
133+
) -> None:
131134
"""Set the user_code to index X on the lock."""
132135
if self._doorlock_cluster_handler:
133136
await self._doorlock_cluster_handler.async_set_user_code(
134137
code_slot, user_code
135138
)
136139
self.debug("User code at slot %s set", code_slot)
137140

138-
async def async_enable_lock_user_code(self, code_slot: int) -> None:
141+
async def async_enable_lock_user_code(self, code_slot: int, **kwargs) -> None:
139142
"""Enable user_code at index X on the lock."""
140143
if self._doorlock_cluster_handler:
141144
await self._doorlock_cluster_handler.async_enable_user_code(code_slot)
142145
self.debug("User code at slot %s enabled", code_slot)
143146

144-
async def async_disable_lock_user_code(self, code_slot: int) -> None:
147+
async def async_disable_lock_user_code(self, code_slot: int, **kwargs) -> None:
145148
"""Disable user_code at index X on the lock."""
146149
if self._doorlock_cluster_handler:
147150
await self._doorlock_cluster_handler.async_disable_user_code(code_slot)
148151
self.debug("User code at slot %s disabled", code_slot)
149152

150-
async def async_clear_lock_user_code(self, code_slot: int) -> None:
153+
async def async_clear_lock_user_code(self, code_slot: int, **kwargs) -> None:
151154
"""Clear the user_code at index X on the lock."""
152155
if self._doorlock_cluster_handler:
153156
await self._doorlock_cluster_handler.async_clear_user_code(code_slot)
@@ -163,12 +166,11 @@ def handle_cluster_handler_attribute_updated(
163166
self.maybe_emit_state_changed_event()
164167

165168
def restore_external_state_attributes(
166-
self,
167-
*,
168-
state: Literal["locked", "unlocked"] | None,
169+
self, *, state: Literal["locked", "unlocked"] | None, **kwargs
169170
) -> None:
170171
"""Restore extra state attributes that are stored outside of the ZCL cache."""
171172
self._state = state
173+
self.maybe_emit_state_changed_event()
172174

173175

174176
class WebSocketClientLockEntity(
@@ -183,6 +185,7 @@ def __init__(
183185
) -> None:
184186
"""Initialize the ZHA lock entity."""
185187
super().__init__(entity_info, device)
188+
self._tasks: list[asyncio.Task] = []
186189

187190
@property
188191
def is_locked(self) -> bool:
@@ -191,25 +194,47 @@ def is_locked(self) -> bool:
191194

192195
async def async_lock(self) -> None:
193196
"""Lock the lock."""
197+
await self._device.gateway.locks.lock(self.info_object)
194198

195199
async def async_unlock(self) -> None:
196200
"""Unlock the lock."""
201+
await self._device.gateway.locks.unlock(self.info_object)
197202

198203
async def async_set_lock_user_code(self, code_slot: int, user_code: str) -> None:
199204
"""Set the user_code to index X on the lock."""
205+
await self._device.gateway.locks.set_user_lock_code(
206+
self.info_object, code_slot, user_code
207+
)
200208

201209
async def async_enable_lock_user_code(self, code_slot: int) -> None:
202210
"""Enable user_code at index X on the lock."""
211+
await self._device.gateway.locks.enable_user_lock_code(
212+
self.info_object, code_slot
213+
)
203214

204215
async def async_disable_lock_user_code(self, code_slot: int) -> None:
205216
"""Disable user_code at index X on the lock."""
217+
await self._device.gateway.locks.disable_user_lock_code(
218+
self.info_object, code_slot
219+
)
206220

207221
async def async_clear_lock_user_code(self, code_slot: int) -> None:
208222
"""Clear the user_code at index X on the lock."""
223+
await self._device.gateway.locks.clear_user_lock_code(
224+
self.info_object, code_slot
225+
)
209226

210227
def restore_external_state_attributes(
211228
self,
212229
*,
213230
state: Literal["locked", "unlocked"] | None,
214231
) -> None:
215232
"""Restore extra state attributes that are stored outside of the ZCL cache."""
233+
task = asyncio.create_task(
234+
self._device.gateway.locks.restore_external_state_attributes(
235+
self.info_object,
236+
state=state,
237+
)
238+
)
239+
self._tasks.append(task)
240+
task.add_done_callback(self._tasks.remove)

zha/application/platforms/lock/websocket_api.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,27 @@ async def clear_user_lock_code(
128128
)
129129

130130

131+
class LockRestoreExternalStateAttributesCommand(PlatformEntityCommand):
132+
"""Restore external state attributes command."""
133+
134+
command: Literal[APICommands.LOCK_RESTORE_EXTERNAL_STATE_ATTRIBUTES] = (
135+
APICommands.LOCK_RESTORE_EXTERNAL_STATE_ATTRIBUTES
136+
)
137+
platform: str = Platform.LOCK
138+
state: Literal["locked", "unlocked"] | None
139+
140+
141+
@decorators.websocket_command(LockRestoreExternalStateAttributesCommand)
142+
@decorators.async_response
143+
async def restore_lock_external_state_attributes(
144+
server: Server, client: Client, command: LockRestoreExternalStateAttributesCommand
145+
) -> None:
146+
"""Restore externally preserved state for locks."""
147+
await execute_platform_entity_command(
148+
server, client, command, "restore_external_state_attributes"
149+
)
150+
151+
131152
def load_api(server: Server) -> None:
132153
"""Load the api command handlers."""
133154
register_api_command(server, lock)
@@ -136,3 +157,4 @@ def load_api(server: Server) -> None:
136157
register_api_command(server, enable_user_lock_code)
137158
register_api_command(server, disable_user_lock_code)
138159
register_api_command(server, clear_user_lock_code)
160+
register_api_command(server, restore_lock_external_state_attributes)

0 commit comments

Comments
 (0)