mirror of
https://github.com/home-assistant/core.git
synced 2025-09-08 06:11:31 +02:00
Add Matter lock event changed_by (#149861)
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
This commit is contained in:
@@ -6,6 +6,7 @@ import asyncio
|
|||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from chip.clusters import Objects as clusters
|
from chip.clusters import Objects as clusters
|
||||||
|
from matter_server.common.models import EventType, MatterNodeEvent
|
||||||
|
|
||||||
from homeassistant.components.lock import (
|
from homeassistant.components.lock import (
|
||||||
LockEntity,
|
LockEntity,
|
||||||
@@ -22,6 +23,22 @@ from .entity import MatterEntity
|
|||||||
from .helpers import get_matter
|
from .helpers import get_matter
|
||||||
from .models import MatterDiscoverySchema
|
from .models import MatterDiscoverySchema
|
||||||
|
|
||||||
|
DOOR_LOCK_OPERATION_SOURCE = {
|
||||||
|
# mapping from operation source id's to textual representation
|
||||||
|
0: "Unspecified",
|
||||||
|
1: "Manual", # [Optional]
|
||||||
|
2: "Proprietary Remote", # [Optional]
|
||||||
|
3: "Keypad", # [Optional]
|
||||||
|
4: "Auto", # [Optional]
|
||||||
|
5: "Button", # [Optional]
|
||||||
|
6: "Schedule", # [HDSCH]
|
||||||
|
7: "Remote", # [M]
|
||||||
|
8: "RFID", # [RID]
|
||||||
|
9: "Biometric", # [USR]
|
||||||
|
10: "Aliro", # [Aliro]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
DoorLockFeature = clusters.DoorLock.Bitmaps.Feature
|
DoorLockFeature = clusters.DoorLock.Bitmaps.Feature
|
||||||
|
|
||||||
|
|
||||||
@@ -41,6 +58,52 @@ class MatterLock(MatterEntity, LockEntity):
|
|||||||
_feature_map: int | None = None
|
_feature_map: int | None = None
|
||||||
_optimistic_timer: asyncio.TimerHandle | None = None
|
_optimistic_timer: asyncio.TimerHandle | None = None
|
||||||
_platform_translation_key = "lock"
|
_platform_translation_key = "lock"
|
||||||
|
_attr_changed_by = "Unknown"
|
||||||
|
|
||||||
|
async def async_added_to_hass(self) -> None:
|
||||||
|
"""Subscribe to events."""
|
||||||
|
await super().async_added_to_hass()
|
||||||
|
# subscribe to NodeEvent events
|
||||||
|
self._unsubscribes.append(
|
||||||
|
self.matter_client.subscribe_events(
|
||||||
|
callback=self._on_matter_node_event,
|
||||||
|
event_filter=EventType.NODE_EVENT,
|
||||||
|
node_filter=self._endpoint.node.node_id,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def _on_matter_node_event(
|
||||||
|
self,
|
||||||
|
event: EventType,
|
||||||
|
node_event: MatterNodeEvent,
|
||||||
|
) -> None:
|
||||||
|
"""Call on NodeEvent."""
|
||||||
|
if (node_event.endpoint_id != self._endpoint.endpoint_id) or (
|
||||||
|
node_event.cluster_id != clusters.DoorLock.id
|
||||||
|
):
|
||||||
|
return
|
||||||
|
|
||||||
|
LOGGER.debug(
|
||||||
|
"Received node_event: event type %s, event id %s for %s with data %s",
|
||||||
|
event,
|
||||||
|
node_event.event_id,
|
||||||
|
self.entity_id,
|
||||||
|
node_event.data,
|
||||||
|
)
|
||||||
|
|
||||||
|
# handle the DoorLock events
|
||||||
|
node_event_data: dict[str, int] = node_event.data or {}
|
||||||
|
match node_event.event_id:
|
||||||
|
case (
|
||||||
|
clusters.DoorLock.Events.LockOperation.event_id
|
||||||
|
): # Lock cluster event 2
|
||||||
|
# update the changed_by attribute to indicate lock operation source
|
||||||
|
operation_source: int = node_event_data.get("operationSource", -1)
|
||||||
|
self._attr_changed_by = DOOR_LOCK_OPERATION_SOURCE.get(
|
||||||
|
operation_source, "Unknown"
|
||||||
|
)
|
||||||
|
self.async_write_ha_state()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def code_format(self) -> str | None:
|
def code_format(self) -> str | None:
|
||||||
|
@@ -37,6 +37,7 @@
|
|||||||
# name: test_locks[door_lock][lock.mock_door_lock-state]
|
# name: test_locks[door_lock][lock.mock_door_lock-state]
|
||||||
StateSnapshot({
|
StateSnapshot({
|
||||||
'attributes': ReadOnlyDict({
|
'attributes': ReadOnlyDict({
|
||||||
|
'changed_by': 'Unknown',
|
||||||
'friendly_name': 'Mock Door Lock',
|
'friendly_name': 'Mock Door Lock',
|
||||||
'supported_features': <LockEntityFeature: 0>,
|
'supported_features': <LockEntityFeature: 0>,
|
||||||
}),
|
}),
|
||||||
@@ -86,6 +87,7 @@
|
|||||||
# name: test_locks[door_lock_with_unbolt][lock.mock_door_lock-state]
|
# name: test_locks[door_lock_with_unbolt][lock.mock_door_lock-state]
|
||||||
StateSnapshot({
|
StateSnapshot({
|
||||||
'attributes': ReadOnlyDict({
|
'attributes': ReadOnlyDict({
|
||||||
|
'changed_by': 'Unknown',
|
||||||
'friendly_name': 'Mock Door Lock',
|
'friendly_name': 'Mock Door Lock',
|
||||||
'supported_features': <LockEntityFeature: 1>,
|
'supported_features': <LockEntityFeature: 1>,
|
||||||
}),
|
}),
|
||||||
|
@@ -4,10 +4,11 @@ from unittest.mock import MagicMock, call
|
|||||||
|
|
||||||
from chip.clusters import Objects as clusters
|
from chip.clusters import Objects as clusters
|
||||||
from matter_server.client.models.node import MatterNode
|
from matter_server.client.models.node import MatterNode
|
||||||
|
from matter_server.common.models import EventType, MatterNodeEvent
|
||||||
import pytest
|
import pytest
|
||||||
from syrupy.assertion import SnapshotAssertion
|
from syrupy.assertion import SnapshotAssertion
|
||||||
|
|
||||||
from homeassistant.components.lock import LockEntityFeature, LockState
|
from homeassistant.components.lock import ATTR_CHANGED_BY, LockEntityFeature, LockState
|
||||||
from homeassistant.const import ATTR_CODE, STATE_UNKNOWN, Platform
|
from homeassistant.const import ATTR_CODE, STATE_UNKNOWN, Platform
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.exceptions import ServiceValidationError
|
from homeassistant.exceptions import ServiceValidationError
|
||||||
@@ -112,6 +113,26 @@ async def test_lock(
|
|||||||
state = hass.states.get("lock.mock_door_lock")
|
state = hass.states.get("lock.mock_door_lock")
|
||||||
assert state.attributes["supported_features"] & LockEntityFeature.OPEN
|
assert state.attributes["supported_features"] & LockEntityFeature.OPEN
|
||||||
|
|
||||||
|
# test handling of a node LockOperation event
|
||||||
|
await trigger_subscription_callback(
|
||||||
|
hass,
|
||||||
|
matter_client,
|
||||||
|
EventType.NODE_EVENT,
|
||||||
|
MatterNodeEvent(
|
||||||
|
node_id=matter_node.node_id,
|
||||||
|
endpoint_id=1,
|
||||||
|
cluster_id=257,
|
||||||
|
event_id=2,
|
||||||
|
event_number=0,
|
||||||
|
priority=1,
|
||||||
|
timestamp=0,
|
||||||
|
timestamp_type=0,
|
||||||
|
data={"operationSource": 3},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
state = hass.states.get("lock.mock_door_lock")
|
||||||
|
assert state.attributes[ATTR_CHANGED_BY] == "Keypad"
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("node_fixture", ["door_lock"])
|
@pytest.mark.parametrize("node_fixture", ["door_lock"])
|
||||||
async def test_lock_requires_pin(
|
async def test_lock_requires_pin(
|
||||||
|
Reference in New Issue
Block a user