Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix locking/unlocking transition state in Matter lock platform #121099

Merged
merged 3 commits into from
Jul 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 18 additions & 12 deletions homeassistant/components/matter/lock.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,9 @@ async def send_device_command(

async def async_lock(self, **kwargs: Any) -> None:
"""Lock the lock with pin if needed."""
# optimistically signal locking to state machine
self._attr_is_locking = True
self.async_write_ha_state()
code: str | None = kwargs.get(ATTR_CODE)
code_bytes = code.encode() if code else None
await self.send_device_command(
Expand All @@ -98,6 +101,9 @@ async def async_lock(self, **kwargs: Any) -> None:

async def async_unlock(self, **kwargs: Any) -> None:
"""Unlock the lock with pin if needed."""
# optimistically signal unlocking to state machine
self._attr_is_unlocking = True
self.async_write_ha_state()
code: str | None = kwargs.get(ATTR_CODE)
code_bytes = code.encode() if code else None
if self.supports_unbolt:
Expand All @@ -114,6 +120,9 @@ async def async_unlock(self, **kwargs: Any) -> None:

async def async_open(self, **kwargs: Any) -> None:
"""Open the door latch."""
# optimistically signal unlocking to state machine
self._attr_is_unlocking = True
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we use _attr_is_opening?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we have that?

Copy link
Member

@MartinHjelmare MartinHjelmare Jul 5, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@cached_property
def is_opening(self) -> bool | None:
"""Return true if the lock is opening."""
return self._attr_is_opening

@final
@property
def state(self) -> str | None:
"""Return the state."""
if self.is_jammed:
return STATE_JAMMED
if self.is_opening:
return STATE_OPENING

https://developers.home-assistant.io/docs/core/entity/lock#properties

self.async_write_ha_state()
code: str | None = kwargs.get(ATTR_CODE)
code_bytes = code.encode() if code else None
await self.send_device_command(
Expand All @@ -135,26 +144,23 @@ def _update_from_device(self) -> None:
clusters.DoorLock.Attributes.LockState
)

# always reset the optimisically (un)locking state on state update
self._attr_is_locking = False
self._attr_is_unlocking = False

LOGGER.debug("Lock state: %s for %s", lock_state, self.entity_id)

if lock_state is clusters.DoorLock.Enums.DlLockState.kLocked:
self._attr_is_locked = True
self._attr_is_locking = False
self._attr_is_unlocking = False
elif lock_state is clusters.DoorLock.Enums.DlLockState.kUnlocked:
elif lock_state in (
clusters.DoorLock.Enums.DlLockState.kUnlocked,
clusters.DoorLock.Enums.DlLockState.kUnlatched,
clusters.DoorLock.Enums.DlLockState.kNotFullyLocked,
):
self._attr_is_locked = False
self._attr_is_locking = False
self._attr_is_unlocking = False
elif lock_state is clusters.DoorLock.Enums.DlLockState.kNotFullyLocked:
if self.is_locked is True:
self._attr_is_unlocking = True
elif self.is_locked is False:
self._attr_is_locking = True
else:
# According to the matter docs a null state can happen during device startup.
self._attr_is_locked = None
self._attr_is_locking = None
self._attr_is_unlocking = None

if self.supports_door_position_sensor:
door_state = self.get_matter_attribute_value(
Expand Down
10 changes: 4 additions & 6 deletions tests/components/matter/test_door_lock.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,11 @@

from homeassistant.components.lock import (
STATE_LOCKED,
STATE_LOCKING,
STATE_OPEN,
STATE_UNLOCKED,
STATE_UNLOCKING,
LockEntityFeature,
)
from homeassistant.const import ATTR_CODE, STATE_UNKNOWN
from homeassistant.const import ATTR_CODE, STATE_LOCKING, STATE_UNKNOWN
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ServiceValidationError
import homeassistant.helpers.entity_registry as er
Expand Down Expand Up @@ -68,14 +66,14 @@ async def test_lock(

state = hass.states.get("lock.mock_door_lock_lock")
assert state
assert state.state == STATE_LOCKED
assert state.state == STATE_LOCKING

set_node_attribute(door_lock, 1, 257, 0, 0)
await trigger_subscription_callback(hass, matter_client)

state = hass.states.get("lock.mock_door_lock_lock")
assert state
assert state.state == STATE_UNLOCKING
assert state.state == STATE_UNLOCKED

set_node_attribute(door_lock, 1, 257, 0, 2)
await trigger_subscription_callback(hass, matter_client)
Expand All @@ -89,7 +87,7 @@ async def test_lock(

state = hass.states.get("lock.mock_door_lock_lock")
assert state
assert state.state == STATE_LOCKING
assert state.state == STATE_UNLOCKED

set_node_attribute(door_lock, 1, 257, 0, None)
await trigger_subscription_callback(hass, matter_client)
Expand Down