diff --git a/homeassistant/components/matter/lock.py b/homeassistant/components/matter/lock.py index 7df6d84c794..a5f625f9e73 100644 --- a/homeassistant/components/matter/lock.py +++ b/homeassistant/components/matter/lock.py @@ -33,6 +33,26 @@ class MatterLock(MatterEntity, LockEntity): features: int | None = None + @property + def code_format(self) -> str | None: + """Regex for code format or None if no code is required.""" + if self.get_matter_attribute_value( + clusters.DoorLock.Attributes.RequirePINforRemoteOperation + ): + min_pincode_length = int( + self.get_matter_attribute_value( + clusters.DoorLock.Attributes.MinPINCodeLength + ) + ) + max_pincode_length = int( + self.get_matter_attribute_value( + clusters.DoorLock.Attributes.MaxPINCodeLength + ) + ) + return f"^\\d{{{min_pincode_length},{max_pincode_length}}}$" + + return None + @property def supports_door_position_sensor(self) -> bool: """Return True if the lock supports door position sensor.""" diff --git a/tests/components/matter/test_door_lock.py b/tests/components/matter/test_door_lock.py index 072658044d8..003bfa3cf39 100644 --- a/tests/components/matter/test_door_lock.py +++ b/tests/components/matter/test_door_lock.py @@ -11,7 +11,7 @@ from homeassistant.components.lock import ( STATE_UNLOCKED, STATE_UNLOCKING, ) -from homeassistant.const import STATE_UNKNOWN +from homeassistant.const import ATTR_CODE, STATE_UNKNOWN from homeassistant.core import HomeAssistant from .common import ( @@ -104,3 +104,46 @@ async def test_lock( state = hass.states.get("lock.mock_door_lock") assert state assert state.state == STATE_UNKNOWN + + +# This tests needs to be adjusted to remove lingering tasks +@pytest.mark.parametrize("expected_lingering_tasks", [True]) +async def test_lock_requires_pin( + hass: HomeAssistant, + matter_client: MagicMock, + door_lock: MatterNode, +) -> None: + """Test door lock with PINCode.""" + + code = "1234567" + + # set RequirePINforRemoteOperation + set_node_attribute(door_lock, 1, 257, 51, True) + # set door state to unlocked + set_node_attribute(door_lock, 1, 257, 0, 2) + + with pytest.raises(ValueError): + # Lock door using invalid code format + await trigger_subscription_callback(hass, matter_client) + await hass.services.async_call( + "lock", + "lock", + {"entity_id": "lock.mock_door_lock", ATTR_CODE: "1234"}, + blocking=True, + ) + + # Lock door using valid code + await trigger_subscription_callback(hass, matter_client) + await hass.services.async_call( + "lock", + "lock", + {"entity_id": "lock.mock_door_lock", ATTR_CODE: code}, + blocking=True, + ) + assert matter_client.send_device_command.call_count == 1 + assert matter_client.send_device_command.call_args == call( + node_id=door_lock.node_id, + endpoint_id=1, + command=clusters.DoorLock.Commands.LockDoor(code.encode()), + timed_request_timeout_ms=1000, + )