Add door lock device type to matter integration (#89277)
* Adds base code for matter lock * Adds basic matter door lock support * Adds matter lock fixture * Adds tests for matter lock * Addresses feedback * Added logic to handle inter states of matter lock * Addesses feedback
This commit is contained in:
parent
cbe85126cb
commit
5e5ace9c4e
6 changed files with 769 additions and 5 deletions
|
@ -11,6 +11,7 @@ from homeassistant.core import callback
|
|||
|
||||
from .binary_sensor import DISCOVERY_SCHEMAS as BINARY_SENSOR_SCHEMAS
|
||||
from .light import DISCOVERY_SCHEMAS as LIGHT_SCHEMAS
|
||||
from .lock import DISCOVERY_SCHEMAS as LOCK_SCHEMAS
|
||||
from .models import MatterDiscoverySchema, MatterEntityInfo
|
||||
from .sensor import DISCOVERY_SCHEMAS as SENSOR_SCHEMAS
|
||||
from .switch import DISCOVERY_SCHEMAS as SWITCH_SCHEMAS
|
||||
|
@ -18,6 +19,7 @@ from .switch import DISCOVERY_SCHEMAS as SWITCH_SCHEMAS
|
|||
DISCOVERY_SCHEMAS: dict[Platform, list[MatterDiscoverySchema]] = {
|
||||
Platform.BINARY_SENSOR: BINARY_SENSOR_SCHEMAS,
|
||||
Platform.LIGHT: LIGHT_SCHEMAS,
|
||||
Platform.LOCK: LOCK_SCHEMAS,
|
||||
Platform.SENSOR: SENSOR_SCHEMAS,
|
||||
Platform.SWITCH: SWITCH_SCHEMAS,
|
||||
}
|
||||
|
|
|
@ -372,7 +372,10 @@ DISCOVERY_SCHEMAS = [
|
|||
clusters.ColorControl.Attributes.CurrentY,
|
||||
clusters.ColorControl.Attributes.ColorTemperatureMireds,
|
||||
),
|
||||
# restrict device type to prevent discovery in switch platform
|
||||
not_device_type=(device_types.OnOffPlugInUnit,),
|
||||
# restrict device type to prevent discovery by the wrong platform
|
||||
not_device_type=(
|
||||
device_types.OnOffPlugInUnit,
|
||||
device_types.DoorLock,
|
||||
),
|
||||
),
|
||||
]
|
||||
|
|
141
homeassistant/components/matter/lock.py
Normal file
141
homeassistant/components/matter/lock.py
Normal file
|
@ -0,0 +1,141 @@
|
|||
"""Matter lock."""
|
||||
from __future__ import annotations
|
||||
|
||||
from enum import IntFlag
|
||||
from typing import Any
|
||||
|
||||
from chip.clusters import Objects as clusters
|
||||
|
||||
from homeassistant.components.lock import LockEntity, LockEntityDescription
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import Platform
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
|
||||
from .const import LOGGER
|
||||
from .entity import MatterEntity
|
||||
from .helpers import get_matter
|
||||
from .models import MatterDiscoverySchema
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config_entry: ConfigEntry,
|
||||
async_add_entities: AddEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up Matter lock from Config Entry."""
|
||||
matter = get_matter(hass)
|
||||
matter.register_platform_handler(Platform.LOCK, async_add_entities)
|
||||
|
||||
|
||||
class MatterLock(MatterEntity, LockEntity):
|
||||
"""Representation of a Matter lock."""
|
||||
|
||||
features: int | None = None
|
||||
|
||||
@property
|
||||
def supports_door_position_sensor(self) -> bool:
|
||||
"""Return True if the lock supports door position sensor."""
|
||||
if self.features is None:
|
||||
return False
|
||||
|
||||
return bool(self.features & DoorLockFeature.kDoorPositionSensor)
|
||||
|
||||
async def send_device_command(
|
||||
self,
|
||||
command: clusters.ClusterCommand,
|
||||
timed_request_timeout_ms: int = 1000,
|
||||
) -> None:
|
||||
"""Send a command to the device."""
|
||||
await self.matter_client.send_device_command(
|
||||
node_id=self._endpoint.node.node_id,
|
||||
endpoint_id=self._endpoint.endpoint_id,
|
||||
command=command,
|
||||
timed_request_timeout_ms=timed_request_timeout_ms,
|
||||
)
|
||||
|
||||
async def async_lock(self, **kwargs: Any) -> None:
|
||||
"""Lock the lock with pin if needed."""
|
||||
await self.send_device_command(command=clusters.DoorLock.Commands.LockDoor())
|
||||
|
||||
async def async_unlock(self, **kwargs: Any) -> None:
|
||||
"""Unlock the lock with pin if needed."""
|
||||
await self.send_device_command(command=clusters.DoorLock.Commands.UnlockDoor())
|
||||
|
||||
@callback
|
||||
def _update_from_device(self) -> None:
|
||||
"""Update the entity from the device."""
|
||||
|
||||
if self.features is None:
|
||||
self.features = int(
|
||||
self.get_matter_attribute_value(clusters.DoorLock.Attributes.FeatureMap)
|
||||
)
|
||||
|
||||
lock_state = self.get_matter_attribute_value(
|
||||
clusters.DoorLock.Attributes.LockState
|
||||
)
|
||||
|
||||
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:
|
||||
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(
|
||||
clusters.DoorLock.Attributes.DoorState
|
||||
)
|
||||
|
||||
assert door_state is not None
|
||||
|
||||
LOGGER.debug("Door state: %s for %s", door_state, self.entity_id)
|
||||
|
||||
self._attr_is_jammed = (
|
||||
door_state is clusters.DoorLock.Enums.DlDoorState.kDoorJammed
|
||||
)
|
||||
|
||||
|
||||
class DoorLockFeature(IntFlag):
|
||||
"""Temp enum that represents the features of a door lock.
|
||||
|
||||
Should be replaced by the library provided one once that is released.
|
||||
"""
|
||||
|
||||
kPinCredential = 0x1
|
||||
kRfidCredential = 0x2
|
||||
kFingerCredentials = 0x4
|
||||
kLogging = 0x8
|
||||
kWeekDayAccessSchedules = 0x10
|
||||
kDoorPositionSensor = 0x20
|
||||
kFaceCredentials = 0x40
|
||||
kCredentialsOverTheAirAccess = 0x80
|
||||
kUser = 0x100
|
||||
kNotification = 0x200
|
||||
kYearDayAccessSchedules = 0x400
|
||||
kHolidaySchedules = 0x800
|
||||
|
||||
|
||||
DISCOVERY_SCHEMAS = [
|
||||
MatterDiscoverySchema(
|
||||
platform=Platform.LOCK,
|
||||
entity_description=LockEntityDescription(key="MatterLock"),
|
||||
entity_class=MatterLock,
|
||||
required_attributes=(clusters.DoorLock.Attributes.LockState,),
|
||||
optional_attributes=(clusters.DoorLock.Attributes.DoorState,),
|
||||
),
|
||||
]
|
|
@ -67,8 +67,11 @@ DISCOVERY_SCHEMAS = [
|
|||
),
|
||||
entity_class=MatterSwitch,
|
||||
required_attributes=(clusters.OnOff.Attributes.OnOff,),
|
||||
# restrict device type to prevent discovery by light
|
||||
# platform which also uses OnOff cluster
|
||||
not_device_type=(device_types.OnOffLight, device_types.DimmableLight),
|
||||
# restrict device type to prevent discovery by the wrong platform
|
||||
not_device_type=(
|
||||
device_types.OnOffLight,
|
||||
device_types.DimmableLight,
|
||||
device_types.DoorLock,
|
||||
),
|
||||
),
|
||||
]
|
||||
|
|
509
tests/components/matter/fixtures/nodes/door-lock.json
Normal file
509
tests/components/matter/fixtures/nodes/door-lock.json
Normal file
|
@ -0,0 +1,509 @@
|
|||
{
|
||||
"node_id": 1,
|
||||
"date_commissioned": "2023-03-07T09:06:06.059454",
|
||||
"last_interview": "2023-03-07T09:06:06.059456",
|
||||
"interview_version": 2,
|
||||
"available": true,
|
||||
"attributes": {
|
||||
"0/29/0": [
|
||||
{
|
||||
"type": 22,
|
||||
"revision": 1
|
||||
}
|
||||
],
|
||||
"0/29/1": [
|
||||
29, 31, 40, 42, 43, 44, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 60, 62,
|
||||
63, 64, 65
|
||||
],
|
||||
"0/29/2": [41],
|
||||
"0/29/3": [1],
|
||||
"0/29/65532": 0,
|
||||
"0/29/65533": 1,
|
||||
"0/29/65528": [],
|
||||
"0/29/65529": [],
|
||||
"0/29/65531": [0, 1, 2, 3, 65528, 65529, 65530, 65531, 65532, 65533],
|
||||
"0/31/0": [
|
||||
{
|
||||
"privilege": 5,
|
||||
"authMode": 2,
|
||||
"subjects": [112233],
|
||||
"targets": null,
|
||||
"fabricIndex": 1
|
||||
}
|
||||
],
|
||||
"0/31/1": [],
|
||||
"0/31/2": 4,
|
||||
"0/31/3": 3,
|
||||
"0/31/4": 4,
|
||||
"0/31/65532": 0,
|
||||
"0/31/65533": 1,
|
||||
"0/31/65528": [],
|
||||
"0/31/65529": [],
|
||||
"0/31/65531": [0, 1, 2, 3, 4, 65528, 65529, 65530, 65531, 65532, 65533],
|
||||
"0/40/0": 1,
|
||||
"0/40/1": "TEST_VENDOR",
|
||||
"0/40/2": 65521,
|
||||
"0/40/3": "Mock Door Lock",
|
||||
"0/40/4": 32769,
|
||||
"0/40/5": "Mock Door Lock",
|
||||
"0/40/6": "**REDACTED**",
|
||||
"0/40/7": 0,
|
||||
"0/40/8": "TEST_VERSION",
|
||||
"0/40/9": 1,
|
||||
"0/40/10": "1.0",
|
||||
"0/40/11": "20200101",
|
||||
"0/40/12": "",
|
||||
"0/40/13": "",
|
||||
"0/40/14": "",
|
||||
"0/40/15": "TEST_SN",
|
||||
"0/40/16": false,
|
||||
"0/40/17": true,
|
||||
"0/40/18": "mock-door-lock",
|
||||
"0/40/19": {
|
||||
"caseSessionsPerFabric": 3,
|
||||
"subscriptionsPerFabric": 65535
|
||||
},
|
||||
"0/40/65532": 0,
|
||||
"0/40/65533": 1,
|
||||
"0/40/65528": [],
|
||||
"0/40/65529": [],
|
||||
"0/40/65531": [
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
|
||||
65528, 65529, 65530, 65531, 65532, 65533
|
||||
],
|
||||
"0/42/0": [],
|
||||
"0/42/1": true,
|
||||
"0/42/2": 0,
|
||||
"0/42/3": 0,
|
||||
"0/42/65532": 0,
|
||||
"0/42/65533": 1,
|
||||
"0/42/65528": [],
|
||||
"0/42/65529": [0],
|
||||
"0/42/65531": [0, 1, 2, 3, 65528, 65529, 65530, 65531, 65532, 65533],
|
||||
"0/43/0": "en-US",
|
||||
"0/43/1": [
|
||||
"en-US",
|
||||
"de-DE",
|
||||
"fr-FR",
|
||||
"en-GB",
|
||||
"es-ES",
|
||||
"zh-CN",
|
||||
"it-IT",
|
||||
"ja-JP"
|
||||
],
|
||||
"0/43/65532": 0,
|
||||
"0/43/65533": 1,
|
||||
"0/43/65528": [],
|
||||
"0/43/65529": [],
|
||||
"0/43/65531": [0, 1, 65528, 65529, 65530, 65531, 65532, 65533],
|
||||
"0/44/0": 0,
|
||||
"0/44/1": 0,
|
||||
"0/44/2": [0, 1, 2, 3, 4, 5, 6, 8, 9, 10, 11, 7],
|
||||
"0/44/65532": 0,
|
||||
"0/44/65533": 1,
|
||||
"0/44/65528": [],
|
||||
"0/44/65529": [],
|
||||
"0/44/65531": [0, 1, 2, 65528, 65529, 65530, 65531, 65532, 65533],
|
||||
"0/46/0": [0, 1],
|
||||
"0/46/65532": 0,
|
||||
"0/46/65533": 1,
|
||||
"0/46/65528": [],
|
||||
"0/46/65529": [],
|
||||
"0/46/65531": [0, 65528, 65529, 65530, 65531, 65532, 65533],
|
||||
"0/47/0": 1,
|
||||
"0/47/1": 0,
|
||||
"0/47/2": "USB",
|
||||
"0/47/6": 0,
|
||||
"0/47/65532": 1,
|
||||
"0/47/65533": 1,
|
||||
"0/47/65528": [],
|
||||
"0/47/65529": [],
|
||||
"0/47/65531": [0, 1, 2, 6, 65528, 65529, 65530, 65531, 65532, 65533],
|
||||
"0/48/0": 0,
|
||||
"0/48/1": {
|
||||
"failSafeExpiryLengthSeconds": 60,
|
||||
"maxCumulativeFailsafeSeconds": 900
|
||||
},
|
||||
"0/48/2": 0,
|
||||
"0/48/3": 2,
|
||||
"0/48/4": true,
|
||||
"0/48/65532": 0,
|
||||
"0/48/65533": 1,
|
||||
"0/48/65528": [1, 3, 5],
|
||||
"0/48/65529": [0, 2, 4],
|
||||
"0/48/65531": [0, 1, 2, 3, 4, 65528, 65529, 65530, 65531, 65532, 65533],
|
||||
"0/49/0": 1,
|
||||
"0/49/1": [],
|
||||
"0/49/2": 10,
|
||||
"0/49/3": 20,
|
||||
"0/49/4": true,
|
||||
"0/49/5": null,
|
||||
"0/49/6": null,
|
||||
"0/49/7": null,
|
||||
"0/49/65532": 2,
|
||||
"0/49/65533": 1,
|
||||
"0/49/65528": [1, 5, 7],
|
||||
"0/49/65529": [0, 3, 4, 6, 8],
|
||||
"0/49/65531": [
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 65528, 65529, 65530, 65531, 65532, 65533
|
||||
],
|
||||
"0/50/65532": 0,
|
||||
"0/50/65533": 1,
|
||||
"0/50/65528": [1],
|
||||
"0/50/65529": [0],
|
||||
"0/50/65531": [65528, 65529, 65530, 65531, 65532, 65533],
|
||||
"0/51/0": [
|
||||
{
|
||||
"name": "eth0",
|
||||
"isOperational": true,
|
||||
"offPremiseServicesReachableIPv4": null,
|
||||
"offPremiseServicesReachableIPv6": null,
|
||||
"hardwareAddress": "/mQDt/2Q",
|
||||
"IPv4Addresses": ["CjwBaQ=="],
|
||||
"IPv6Addresses": [
|
||||
"/VqgxiAxQib8ZAP//rf9kA==",
|
||||
"IAEEcLs7AAb8ZAP//rf9kA==",
|
||||
"/oAAAAAAAAD8ZAP//rf9kA=="
|
||||
],
|
||||
"type": 2
|
||||
},
|
||||
{
|
||||
"name": "lo",
|
||||
"isOperational": true,
|
||||
"offPremiseServicesReachableIPv4": null,
|
||||
"offPremiseServicesReachableIPv6": null,
|
||||
"hardwareAddress": "AAAAAAAA",
|
||||
"IPv4Addresses": ["fwAAAQ=="],
|
||||
"IPv6Addresses": ["AAAAAAAAAAAAAAAAAAAAAQ=="],
|
||||
"type": 0
|
||||
}
|
||||
],
|
||||
"0/51/1": 1,
|
||||
"0/51/2": 25,
|
||||
"0/51/3": 0,
|
||||
"0/51/4": 0,
|
||||
"0/51/5": [],
|
||||
"0/51/6": [],
|
||||
"0/51/7": [],
|
||||
"0/51/8": false,
|
||||
"0/51/65532": 0,
|
||||
"0/51/65533": 1,
|
||||
"0/51/65528": [],
|
||||
"0/51/65529": [0],
|
||||
"0/51/65531": [
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 65528, 65529, 65530, 65531, 65532, 65533
|
||||
],
|
||||
"0/52/0": [
|
||||
{
|
||||
"id": 26957,
|
||||
"name": "26957",
|
||||
"stackFreeCurrent": null,
|
||||
"stackFreeMinimum": null,
|
||||
"stackSize": null
|
||||
},
|
||||
{
|
||||
"id": 26956,
|
||||
"name": "26956",
|
||||
"stackFreeCurrent": null,
|
||||
"stackFreeMinimum": null,
|
||||
"stackSize": null
|
||||
},
|
||||
{
|
||||
"id": 26955,
|
||||
"name": "26955",
|
||||
"stackFreeCurrent": null,
|
||||
"stackFreeMinimum": null,
|
||||
"stackSize": null
|
||||
},
|
||||
{
|
||||
"id": 26953,
|
||||
"name": "26953",
|
||||
"stackFreeCurrent": null,
|
||||
"stackFreeMinimum": null,
|
||||
"stackSize": null
|
||||
},
|
||||
{
|
||||
"id": 26952,
|
||||
"name": "26952",
|
||||
"stackFreeCurrent": null,
|
||||
"stackFreeMinimum": null,
|
||||
"stackSize": null
|
||||
}
|
||||
],
|
||||
"0/52/1": 351120,
|
||||
"0/52/2": 529520,
|
||||
"0/52/3": 529520,
|
||||
"0/52/65532": 1,
|
||||
"0/52/65533": 1,
|
||||
"0/52/65528": [],
|
||||
"0/52/65529": [0],
|
||||
"0/52/65531": [0, 1, 2, 3, 65528, 65529, 65530, 65531, 65532, 65533],
|
||||
"0/53/0": null,
|
||||
"0/53/1": null,
|
||||
"0/53/2": null,
|
||||
"0/53/3": null,
|
||||
"0/53/4": null,
|
||||
"0/53/5": null,
|
||||
"0/53/6": 0,
|
||||
"0/53/7": [],
|
||||
"0/53/8": [],
|
||||
"0/53/9": null,
|
||||
"0/53/10": null,
|
||||
"0/53/11": null,
|
||||
"0/53/12": null,
|
||||
"0/53/13": null,
|
||||
"0/53/14": 0,
|
||||
"0/53/15": 0,
|
||||
"0/53/16": 0,
|
||||
"0/53/17": 0,
|
||||
"0/53/18": 0,
|
||||
"0/53/19": 0,
|
||||
"0/53/20": 0,
|
||||
"0/53/21": 0,
|
||||
"0/53/22": 0,
|
||||
"0/53/23": 0,
|
||||
"0/53/24": 0,
|
||||
"0/53/25": 0,
|
||||
"0/53/26": 0,
|
||||
"0/53/27": 0,
|
||||
"0/53/28": 0,
|
||||
"0/53/29": 0,
|
||||
"0/53/30": 0,
|
||||
"0/53/31": 0,
|
||||
"0/53/32": 0,
|
||||
"0/53/33": 0,
|
||||
"0/53/34": 0,
|
||||
"0/53/35": 0,
|
||||
"0/53/36": 0,
|
||||
"0/53/37": 0,
|
||||
"0/53/38": 0,
|
||||
"0/53/39": 0,
|
||||
"0/53/40": 0,
|
||||
"0/53/41": 0,
|
||||
"0/53/42": 0,
|
||||
"0/53/43": 0,
|
||||
"0/53/44": 0,
|
||||
"0/53/45": 0,
|
||||
"0/53/46": 0,
|
||||
"0/53/47": 0,
|
||||
"0/53/48": 0,
|
||||
"0/53/49": 0,
|
||||
"0/53/50": 0,
|
||||
"0/53/51": 0,
|
||||
"0/53/52": 0,
|
||||
"0/53/53": 0,
|
||||
"0/53/54": 0,
|
||||
"0/53/55": 0,
|
||||
"0/53/56": null,
|
||||
"0/53/57": null,
|
||||
"0/53/58": null,
|
||||
"0/53/59": null,
|
||||
"0/53/60": null,
|
||||
"0/53/61": null,
|
||||
"0/53/62": [],
|
||||
"0/53/65532": 15,
|
||||
"0/53/65533": 1,
|
||||
"0/53/65528": [],
|
||||
"0/53/65529": [0],
|
||||
"0/53/65531": [
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
|
||||
21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38,
|
||||
39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56,
|
||||
57, 58, 59, 60, 61, 62, 65528, 65529, 65530, 65531, 65532, 65533
|
||||
],
|
||||
"0/54/0": null,
|
||||
"0/54/1": null,
|
||||
"0/54/2": 3,
|
||||
"0/54/3": null,
|
||||
"0/54/4": null,
|
||||
"0/54/5": null,
|
||||
"0/54/6": null,
|
||||
"0/54/7": null,
|
||||
"0/54/8": null,
|
||||
"0/54/9": null,
|
||||
"0/54/10": null,
|
||||
"0/54/11": null,
|
||||
"0/54/12": null,
|
||||
"0/54/65532": 3,
|
||||
"0/54/65533": 1,
|
||||
"0/54/65528": [],
|
||||
"0/54/65529": [0],
|
||||
"0/54/65531": [
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 65528, 65529, 65530, 65531,
|
||||
65532, 65533
|
||||
],
|
||||
"0/55/0": null,
|
||||
"0/55/1": false,
|
||||
"0/55/2": 823,
|
||||
"0/55/3": 969,
|
||||
"0/55/4": 0,
|
||||
"0/55/5": 0,
|
||||
"0/55/6": 0,
|
||||
"0/55/7": null,
|
||||
"0/55/8": 25,
|
||||
"0/55/65532": 3,
|
||||
"0/55/65533": 1,
|
||||
"0/55/65528": [],
|
||||
"0/55/65529": [0],
|
||||
"0/55/65531": [
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 65528, 65529, 65530, 65531, 65532, 65533
|
||||
],
|
||||
"0/60/0": 0,
|
||||
"0/60/1": null,
|
||||
"0/60/2": null,
|
||||
"0/60/65532": 0,
|
||||
"0/60/65533": 1,
|
||||
"0/60/65528": [],
|
||||
"0/60/65529": [0, 1, 2],
|
||||
"0/60/65531": [0, 1, 2, 65528, 65529, 65530, 65531, 65532, 65533],
|
||||
"0/62/0": [
|
||||
{
|
||||
"noc": "FTABAQEkAgE3AyQTAhgmBIAigScmBYAlTTo3BiQVASQRARgkBwEkCAEwCUEE55h6CbNLPZH/uM3/rDdA+jeuuD2QSPN8gBeEB0bmGJqWz/gCT4/ySB77rK3XiwVWVAmJhJ/eMcTIA0XXWMqKPDcKNQEoARgkAgE2AwQCBAEYMAQUqnKiC76YFhcTHt4AQ/kAbtrZ2MowBRSL6EWyWm8+uC0Puc2/BncMqYbpmhgwC0AA05Z+y1mcyHUeOFJ5kyDJJMN/oNCwN5h8UpYN/868iuQArr180/fbaN1+db9lab4D2lf0HK7wgHIR3HsOa2w9GA==",
|
||||
"icac": "FTABAQEkAgE3AyQUARgmBIAigScmBYAlTTo3BiQTAhgkBwEkCAEwCUEE5R1DrUQE/L8tx95WR1g1dZJf4d+6LEB7JAYZN/nw9ZBUg5VOHDrB1xIw5KguYJzt10K+0KqQBBEbuwW+wLLobTcKNQEpARgkAmAwBBSL6EWyWm8+uC0Puc2/BncMqYbpmjAFFM0I6fPFzfOv2IWbX1huxb3eW0fqGDALQHXLE0TgIDW6XOnvtsOJCyKoENts8d4TQWBgTKviv1LF/+MS9eFYi+kO+1Idq5mVgwN+lH7eyecShQR0iqq6WLUY",
|
||||
"fabricIndex": 1
|
||||
}
|
||||
],
|
||||
"0/62/1": [
|
||||
{
|
||||
"rootPublicKey": "BJ/jL2MdDrdq9TahKSa5c/dBc166NRCU0W9l7hK2kcuVtN915DLqiS+RAJ2iPEvWK5FawZHF/QdKLZmTkZHudxY=",
|
||||
"vendorId": 65521,
|
||||
"fabricId": 1,
|
||||
"nodeId": 1,
|
||||
"label": "",
|
||||
"fabricIndex": 1
|
||||
}
|
||||
],
|
||||
"0/62/2": 16,
|
||||
"0/62/3": 1,
|
||||
"0/62/4": [
|
||||
"FTABAQEkAgE3AyQUARgmBIAigScmBYAlTTo3BiQUARgkBwEkCAEwCUEEn+MvYx0Ot2r1NqEpJrlz90FzXro1EJTRb2XuEraRy5W033XkMuqJL5EAnaI8S9YrkVrBkcX9B0otmZORke53FjcKNQEpARgkAmAwBBTNCOnzxc3zr9iFm19YbsW93ltH6jAFFM0I6fPFzfOv2IWbX1huxb3eW0fqGDALQILjpR3BTSHHl6DQtvwzWkjmA+i5jjXdc3qjemFGFjFVAnV6dPLQo7tctC8Y0uL4ZNERga2/NZAt1gRD72S0YR4Y"
|
||||
],
|
||||
"0/62/5": 1,
|
||||
"0/62/65532": 0,
|
||||
"0/62/65533": 1,
|
||||
"0/62/65528": [1, 3, 5, 8],
|
||||
"0/62/65529": [0, 2, 4, 6, 7, 9, 10, 11],
|
||||
"0/62/65531": [0, 1, 2, 3, 4, 5, 65528, 65529, 65530, 65531, 65532, 65533],
|
||||
"0/63/0": [],
|
||||
"0/63/1": [],
|
||||
"0/63/2": 4,
|
||||
"0/63/3": 3,
|
||||
"0/63/65532": 0,
|
||||
"0/63/65533": 1,
|
||||
"0/63/65528": [2, 5],
|
||||
"0/63/65529": [0, 1, 3, 4],
|
||||
"0/63/65531": [0, 1, 2, 3, 65528, 65529, 65530, 65531, 65532, 65533],
|
||||
"0/64/0": [
|
||||
{
|
||||
"label": "room",
|
||||
"value": "bedroom 2"
|
||||
},
|
||||
{
|
||||
"label": "orientation",
|
||||
"value": "North"
|
||||
},
|
||||
{
|
||||
"label": "floor",
|
||||
"value": "2"
|
||||
},
|
||||
{
|
||||
"label": "direction",
|
||||
"value": "up"
|
||||
}
|
||||
],
|
||||
"0/64/65532": 0,
|
||||
"0/64/65533": 1,
|
||||
"0/64/65528": [],
|
||||
"0/64/65529": [],
|
||||
"0/64/65531": [0, 65528, 65529, 65530, 65531, 65532, 65533],
|
||||
"0/65/0": [],
|
||||
"0/65/65532": 0,
|
||||
"0/65/65533": 1,
|
||||
"0/65/65528": [],
|
||||
"0/65/65529": [],
|
||||
"0/65/65531": [0, 65528, 65529, 65530, 65531, 65532, 65533],
|
||||
"1/3/0": 0,
|
||||
"1/3/1": 0,
|
||||
"1/3/65532": 0,
|
||||
"1/3/65533": 4,
|
||||
"1/3/65528": [],
|
||||
"1/3/65529": [0],
|
||||
"1/3/65531": [0, 1, 65528, 65529, 65530, 65531, 65532, 65533],
|
||||
"1/6/0": false,
|
||||
"1/6/16384": true,
|
||||
"1/6/16385": 0,
|
||||
"1/6/16386": 0,
|
||||
"1/6/16387": 0,
|
||||
"1/6/65532": 0,
|
||||
"1/6/65533": 4,
|
||||
"1/6/65528": [],
|
||||
"1/6/65529": [0, 1, 2],
|
||||
"1/6/65531": [
|
||||
0, 16384, 16385, 16386, 16387, 65528, 65529, 65530, 65531, 65532, 65533
|
||||
],
|
||||
"1/29/0": [
|
||||
{
|
||||
"type": 10,
|
||||
"revision": 1
|
||||
}
|
||||
],
|
||||
"1/29/1": [3, 6, 29, 47, 257],
|
||||
"1/29/2": [],
|
||||
"1/29/3": [],
|
||||
"1/29/65532": 0,
|
||||
"1/29/65533": 1,
|
||||
"1/29/65528": [],
|
||||
"1/29/65529": [],
|
||||
"1/29/65531": [0, 1, 2, 3, 65528, 65529, 65530, 65531, 65532, 65533],
|
||||
"1/47/0": 1,
|
||||
"1/47/1": 1,
|
||||
"1/47/2": "Battery",
|
||||
"1/47/14": 0,
|
||||
"1/47/15": false,
|
||||
"1/47/16": 0,
|
||||
"1/47/19": "",
|
||||
"1/47/65532": 10,
|
||||
"1/47/65533": 1,
|
||||
"1/47/65528": [],
|
||||
"1/47/65529": [],
|
||||
"1/47/65531": [
|
||||
0, 1, 2, 14, 15, 16, 19, 65528, 65529, 65530, 65531, 65532, 65533
|
||||
],
|
||||
"1/257/0": 1,
|
||||
"1/257/1": 0,
|
||||
"1/257/2": true,
|
||||
"1/257/3": 1,
|
||||
"1/257/17": 10,
|
||||
"1/257/18": 10,
|
||||
"1/257/19": 10,
|
||||
"1/257/20": 10,
|
||||
"1/257/21": 10,
|
||||
"1/257/22": 10,
|
||||
"1/257/23": 8,
|
||||
"1/257/24": 6,
|
||||
"1/257/25": 20,
|
||||
"1/257/26": 10,
|
||||
"1/257/27": 1,
|
||||
"1/257/28": 5,
|
||||
"1/257/33": "en",
|
||||
"1/257/35": 60,
|
||||
"1/257/36": 0,
|
||||
"1/257/37": 0,
|
||||
"1/257/38": 65526,
|
||||
"1/257/41": false,
|
||||
"1/257/43": false,
|
||||
"1/257/48": 3,
|
||||
"1/257/49": 10,
|
||||
"1/257/51": false,
|
||||
"1/257/65532": 3507,
|
||||
"1/257/65533": 6,
|
||||
"1/257/65528": [12, 15, 18, 28, 35, 37],
|
||||
"1/257/65529": [
|
||||
0, 1, 3, 11, 12, 13, 14, 15, 16, 17, 18, 19, 26, 27, 29, 34, 36, 38
|
||||
],
|
||||
"1/257/65531": [
|
||||
0, 1, 2, 3, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 33, 35, 36,
|
||||
37, 38, 41, 43, 48, 49, 51, 65528, 65529, 65530, 65531, 65532, 65533
|
||||
]
|
||||
}
|
||||
}
|
106
tests/components/matter/test_door_lock.py
Normal file
106
tests/components/matter/test_door_lock.py
Normal file
|
@ -0,0 +1,106 @@
|
|||
"""Test Matter locks."""
|
||||
from unittest.mock import MagicMock, call
|
||||
|
||||
from chip.clusters import Objects as clusters
|
||||
from matter_server.client.models.node import MatterNode
|
||||
import pytest
|
||||
|
||||
from homeassistant.components.lock import (
|
||||
STATE_LOCKED,
|
||||
STATE_LOCKING,
|
||||
STATE_UNLOCKED,
|
||||
STATE_UNLOCKING,
|
||||
)
|
||||
from homeassistant.const import STATE_UNKNOWN
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from .common import (
|
||||
set_node_attribute,
|
||||
setup_integration_with_node_fixture,
|
||||
trigger_subscription_callback,
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture(name="door_lock")
|
||||
async def door_lock_fixture(
|
||||
hass: HomeAssistant, matter_client: MagicMock
|
||||
) -> MatterNode:
|
||||
"""Fixture for a door lock node."""
|
||||
return await setup_integration_with_node_fixture(hass, "door-lock", matter_client)
|
||||
|
||||
|
||||
# This tests needs to be adjusted to remove lingering tasks
|
||||
@pytest.mark.parametrize("expected_lingering_tasks", [True])
|
||||
async def test_lock(
|
||||
hass: HomeAssistant,
|
||||
matter_client: MagicMock,
|
||||
door_lock: MatterNode,
|
||||
) -> None:
|
||||
"""Test door lock."""
|
||||
await hass.services.async_call(
|
||||
"lock",
|
||||
"unlock",
|
||||
{
|
||||
"entity_id": "lock.mock_door_lock",
|
||||
},
|
||||
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.UnlockDoor(),
|
||||
timed_request_timeout_ms=1000,
|
||||
)
|
||||
matter_client.send_device_command.reset_mock()
|
||||
|
||||
await hass.services.async_call(
|
||||
"lock",
|
||||
"lock",
|
||||
{
|
||||
"entity_id": "lock.mock_door_lock",
|
||||
},
|
||||
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(),
|
||||
timed_request_timeout_ms=1000,
|
||||
)
|
||||
matter_client.send_device_command.reset_mock()
|
||||
|
||||
state = hass.states.get("lock.mock_door_lock")
|
||||
assert state
|
||||
assert state.state == STATE_LOCKED
|
||||
|
||||
set_node_attribute(door_lock, 1, 257, 0, 0)
|
||||
await trigger_subscription_callback(hass, matter_client)
|
||||
|
||||
state = hass.states.get("lock.mock_door_lock")
|
||||
assert state
|
||||
assert state.state == STATE_UNLOCKING
|
||||
|
||||
set_node_attribute(door_lock, 1, 257, 0, 2)
|
||||
await trigger_subscription_callback(hass, matter_client)
|
||||
|
||||
state = hass.states.get("lock.mock_door_lock")
|
||||
assert state
|
||||
assert state.state == STATE_UNLOCKED
|
||||
|
||||
set_node_attribute(door_lock, 1, 257, 0, 0)
|
||||
await trigger_subscription_callback(hass, matter_client)
|
||||
|
||||
state = hass.states.get("lock.mock_door_lock")
|
||||
assert state
|
||||
assert state.state == STATE_LOCKING
|
||||
|
||||
set_node_attribute(door_lock, 1, 257, 0, None)
|
||||
await trigger_subscription_callback(hass, matter_client)
|
||||
|
||||
state = hass.states.get("lock.mock_door_lock")
|
||||
assert state
|
||||
assert state.state == STATE_UNKNOWN
|
Loading…
Add table
Reference in a new issue