Preliminary support for Matter cover (#90262)
Preliminary support for Matter cover, curtain tilt support has not been added yet.
This commit is contained in:
parent
e962dd64cf
commit
a9e14cd8d7
4 changed files with 1017 additions and 0 deletions
153
homeassistant/components/matter/cover.py
Normal file
153
homeassistant/components/matter/cover.py
Normal file
|
@ -0,0 +1,153 @@
|
|||
"""Matter cover."""
|
||||
from __future__ import annotations
|
||||
|
||||
from enum import IntEnum
|
||||
from typing import Any
|
||||
|
||||
from chip.clusters import Objects as clusters
|
||||
|
||||
from homeassistant.components.cover import (
|
||||
ATTR_POSITION,
|
||||
CoverEntity,
|
||||
CoverEntityDescription,
|
||||
CoverEntityFeature,
|
||||
)
|
||||
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
|
||||
|
||||
# The MASK used for extracting bits 0 to 1 of the byte.
|
||||
OPERATIONAL_STATUS_MASK = 0b11
|
||||
|
||||
|
||||
class OperationalStatus(IntEnum):
|
||||
"""Currently ongoing operations enumeration for coverings, as defined in the Matter spec."""
|
||||
|
||||
COVERING_IS_CURRENTLY_NOT_MOVING = 0b00
|
||||
COVERING_IS_CURRENTLY_OPENING = 0b01
|
||||
COVERING_IS_CURRENTLY_CLOSING = 0b10
|
||||
RESERVED = 0b11
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config_entry: ConfigEntry,
|
||||
async_add_entities: AddEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up Matter Cover from Config Entry."""
|
||||
matter = get_matter(hass)
|
||||
matter.register_platform_handler(Platform.COVER, async_add_entities)
|
||||
|
||||
|
||||
class MatterCover(MatterEntity, CoverEntity):
|
||||
"""Representation of a Matter Cover."""
|
||||
|
||||
entity_description: CoverEntityDescription
|
||||
_attr_supported_features = (
|
||||
CoverEntityFeature.OPEN
|
||||
| CoverEntityFeature.CLOSE
|
||||
| CoverEntityFeature.STOP
|
||||
| CoverEntityFeature.SET_POSITION
|
||||
)
|
||||
|
||||
@property
|
||||
def current_cover_position(self) -> int:
|
||||
"""Return the current position of cover."""
|
||||
if self._attr_current_cover_position:
|
||||
current_position = self._attr_current_cover_position
|
||||
else:
|
||||
current_position = self.get_matter_attribute_value(
|
||||
clusters.WindowCovering.Attributes.CurrentPositionLiftPercentage
|
||||
)
|
||||
|
||||
assert current_position is not None
|
||||
|
||||
return current_position
|
||||
|
||||
@property
|
||||
def is_closed(self) -> bool:
|
||||
"""Return true if cover is closed, else False."""
|
||||
return self.current_cover_position == 0
|
||||
|
||||
async def async_stop_cover(self, **kwargs: Any) -> None:
|
||||
"""Stop the cover movement."""
|
||||
await self.send_device_command(clusters.WindowCovering.Commands.StopMotion())
|
||||
|
||||
async def async_open_cover(self, **kwargs: Any) -> None:
|
||||
"""Open the cover."""
|
||||
await self.send_device_command(clusters.WindowCovering.Commands.UpOrOpen())
|
||||
|
||||
async def async_close_cover(self, **kwargs: Any) -> None:
|
||||
"""Close the cover."""
|
||||
await self.send_device_command(clusters.WindowCovering.Commands.DownOrClose())
|
||||
|
||||
async def async_set_cover_position(self, **kwargs: Any) -> None:
|
||||
"""Set the cover to a specific position."""
|
||||
position = kwargs[ATTR_POSITION]
|
||||
await self.send_device_command(
|
||||
clusters.WindowCovering.Commands.GoToLiftValue(position)
|
||||
)
|
||||
|
||||
async def send_device_command(self, command: Any) -> None:
|
||||
"""Send device command."""
|
||||
await self.matter_client.send_device_command(
|
||||
node_id=self._endpoint.node.node_id,
|
||||
endpoint_id=self._endpoint.endpoint_id,
|
||||
command=command,
|
||||
)
|
||||
|
||||
@callback
|
||||
def _update_from_device(self) -> None:
|
||||
"""Update from device."""
|
||||
operational_status = self.get_matter_attribute_value(
|
||||
clusters.WindowCovering.Attributes.OperationalStatus
|
||||
)
|
||||
|
||||
assert operational_status is not None
|
||||
|
||||
LOGGER.debug(
|
||||
"Operational status %s for %s",
|
||||
f"{operational_status:#010b}",
|
||||
self.entity_id,
|
||||
)
|
||||
|
||||
state = operational_status & OPERATIONAL_STATUS_MASK
|
||||
match state:
|
||||
case OperationalStatus.COVERING_IS_CURRENTLY_OPENING:
|
||||
self._attr_is_opening = True
|
||||
self._attr_is_closing = False
|
||||
case OperationalStatus.COVERING_IS_CURRENTLY_CLOSING:
|
||||
self._attr_is_opening = False
|
||||
self._attr_is_closing = True
|
||||
case _:
|
||||
self._attr_is_opening = False
|
||||
self._attr_is_closing = False
|
||||
|
||||
self._attr_current_cover_position = self.get_matter_attribute_value(
|
||||
clusters.WindowCovering.Attributes.CurrentPositionLiftPercentage
|
||||
)
|
||||
LOGGER.debug(
|
||||
"Current position: %s for %s",
|
||||
self._attr_current_cover_position,
|
||||
self.entity_id,
|
||||
)
|
||||
|
||||
|
||||
# Discovery schema(s) to map Matter Attributes to HA entities
|
||||
DISCOVERY_SCHEMAS = [
|
||||
MatterDiscoverySchema(
|
||||
platform=Platform.COVER,
|
||||
entity_description=CoverEntityDescription(key="MatterCover"),
|
||||
entity_class=MatterCover,
|
||||
required_attributes=(
|
||||
clusters.WindowCovering.Attributes.CurrentPositionLiftPercentage,
|
||||
clusters.WindowCovering.Attributes.OperationalStatus,
|
||||
),
|
||||
),
|
||||
]
|
|
@ -10,6 +10,7 @@ from homeassistant.const import Platform
|
|||
from homeassistant.core import callback
|
||||
|
||||
from .binary_sensor import DISCOVERY_SCHEMAS as BINARY_SENSOR_SCHEMAS
|
||||
from .cover import DISCOVERY_SCHEMAS as COVER_SCHEMAS
|
||||
from .light import DISCOVERY_SCHEMAS as LIGHT_SCHEMAS
|
||||
from .lock import DISCOVERY_SCHEMAS as LOCK_SCHEMAS
|
||||
from .models import MatterDiscoverySchema, MatterEntityInfo
|
||||
|
@ -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.COVER: COVER_SCHEMAS,
|
||||
Platform.LIGHT: LIGHT_SCHEMAS,
|
||||
Platform.LOCK: LOCK_SCHEMAS,
|
||||
Platform.SENSOR: SENSOR_SCHEMAS,
|
||||
|
|
721
tests/components/matter/fixtures/nodes/window-covering.json
Normal file
721
tests/components/matter/fixtures/nodes/window-covering.json
Normal file
|
@ -0,0 +1,721 @@
|
|||
{
|
||||
"node_id": 1,
|
||||
"date_commissioned": "2023-03-29T08:23:30.740085",
|
||||
"last_interview": "2023-03-29T08:23:30.740087",
|
||||
"interview_version": 2,
|
||||
"available": true,
|
||||
"attributes": {
|
||||
"0/29/0": [
|
||||
{
|
||||
"type": 22,
|
||||
"revision": 1
|
||||
}
|
||||
],
|
||||
"0/29/1": [
|
||||
29,
|
||||
30,
|
||||
31,
|
||||
40,
|
||||
42,
|
||||
43,
|
||||
44,
|
||||
45,
|
||||
48,
|
||||
49,
|
||||
50,
|
||||
51,
|
||||
54,
|
||||
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,
|
||||
65531,
|
||||
65532,
|
||||
65533
|
||||
],
|
||||
"0/30/0": [],
|
||||
"0/30/65532": 0,
|
||||
"0/30/65533": 1,
|
||||
"0/30/65528": [],
|
||||
"0/30/65529": [],
|
||||
"0/30/65531": [
|
||||
0,
|
||||
65528,
|
||||
65529,
|
||||
65531,
|
||||
65532,
|
||||
65533
|
||||
],
|
||||
"0/31/0": [
|
||||
{
|
||||
"privilege": 5,
|
||||
"authMode": 2,
|
||||
"subjects": [
|
||||
112233
|
||||
],
|
||||
"targets": null,
|
||||
"fabricIndex": 2
|
||||
}
|
||||
],
|
||||
"0/31/1": [],
|
||||
"0/31/2": 4,
|
||||
"0/31/3": 3,
|
||||
"0/31/4": 3,
|
||||
"0/31/65532": 0,
|
||||
"0/31/65533": 1,
|
||||
"0/31/65528": [],
|
||||
"0/31/65529": [],
|
||||
"0/31/65531": [
|
||||
0,
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
4,
|
||||
65528,
|
||||
65529,
|
||||
65531,
|
||||
65532,
|
||||
65533
|
||||
],
|
||||
"0/40/0": 1,
|
||||
"0/40/1": "Eliteu",
|
||||
"0/40/2": 4895,
|
||||
"0/40/3": "Longan link WNCV DA01",
|
||||
"0/40/4": 12288,
|
||||
"0/40/5": "",
|
||||
"0/40/6": "XX",
|
||||
"0/40/7": 1,
|
||||
"0/40/8": "1.0",
|
||||
"0/40/9": 1,
|
||||
"0/40/10": "v1.0",
|
||||
"0/40/11": "20200101",
|
||||
"0/40/12": "",
|
||||
"0/40/13": "",
|
||||
"0/40/14": "",
|
||||
"0/40/15": "3c70c712bd34e54acebd1a8371f56f7d",
|
||||
"0/40/16": false,
|
||||
"0/40/17": true,
|
||||
"0/40/18": "7630EF9998EDF03C",
|
||||
"0/40/19": {
|
||||
"caseSessionsPerFabric": 3,
|
||||
"subscriptionsPerFabric": 3
|
||||
},
|
||||
"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,
|
||||
65531,
|
||||
65532,
|
||||
65533
|
||||
],
|
||||
"0/42/0": [],
|
||||
"0/42/1": true,
|
||||
"0/42/2": 1,
|
||||
"0/42/3": null,
|
||||
"0/42/65532": 0,
|
||||
"0/42/65533": 1,
|
||||
"0/42/65528": [],
|
||||
"0/42/65529": [
|
||||
0
|
||||
],
|
||||
"0/42/65531": [
|
||||
0,
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
65528,
|
||||
65529,
|
||||
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,
|
||||
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,
|
||||
65531,
|
||||
65532,
|
||||
65533
|
||||
],
|
||||
"0/45/0": 0,
|
||||
"0/45/65532": 0,
|
||||
"0/45/65533": 1,
|
||||
"0/45/65528": [],
|
||||
"0/45/65529": [],
|
||||
"0/45/65531": [
|
||||
0,
|
||||
65528,
|
||||
65529,
|
||||
65531,
|
||||
65532,
|
||||
65533
|
||||
],
|
||||
"0/48/0": 0,
|
||||
"0/48/1": {
|
||||
"failSafeExpiryLengthSeconds": 60,
|
||||
"maxCumulativeFailsafeSeconds": 900
|
||||
},
|
||||
"0/48/2": 0,
|
||||
"0/48/3": 0,
|
||||
"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,
|
||||
65531,
|
||||
65532,
|
||||
65533
|
||||
],
|
||||
"0/49/0": 1,
|
||||
"0/49/1": [
|
||||
{
|
||||
"networkID": "TE9OR0FOLUlPVA==",
|
||||
"connected": true
|
||||
}
|
||||
],
|
||||
"0/49/2": 10,
|
||||
"0/49/3": 30,
|
||||
"0/49/4": true,
|
||||
"0/49/5": 0,
|
||||
"0/49/6": "TE9OR0FOLUlPVA==",
|
||||
"0/49/7": null,
|
||||
"0/49/65532": 1,
|
||||
"0/49/65533": 1,
|
||||
"0/49/65528": [
|
||||
1,
|
||||
5,
|
||||
7
|
||||
],
|
||||
"0/49/65529": [
|
||||
0,
|
||||
2,
|
||||
4,
|
||||
6,
|
||||
8
|
||||
],
|
||||
"0/49/65531": [
|
||||
0,
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
4,
|
||||
5,
|
||||
6,
|
||||
7,
|
||||
65528,
|
||||
65529,
|
||||
65531,
|
||||
65532,
|
||||
65533
|
||||
],
|
||||
"0/50/65532": 0,
|
||||
"0/50/65533": 1,
|
||||
"0/50/65528": [
|
||||
1
|
||||
],
|
||||
"0/50/65529": [
|
||||
0
|
||||
],
|
||||
"0/50/65531": [
|
||||
65528,
|
||||
65529,
|
||||
65531,
|
||||
65532,
|
||||
65533
|
||||
],
|
||||
"0/51/0": [
|
||||
{
|
||||
"name": "WIFI_STA_DEF",
|
||||
"isOperational": true,
|
||||
"offPremiseServicesReachableIPv4": null,
|
||||
"offPremiseServicesReachableIPv6": null,
|
||||
"hardwareAddress": "hPcDB5/k",
|
||||
"IPv4Addresses": [
|
||||
"wKgIhg=="
|
||||
],
|
||||
"IPv6Addresses": [
|
||||
"/oAAAAAAAACG9wP//gef5A==",
|
||||
"JA4DsgZ+bsCG9wP//gef5A=="
|
||||
],
|
||||
"type": 1
|
||||
}
|
||||
],
|
||||
"0/51/1": 35,
|
||||
"0/51/2": 123,
|
||||
"0/51/3": 0,
|
||||
"0/51/4": 6,
|
||||
"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,
|
||||
65531,
|
||||
65532,
|
||||
65533
|
||||
],
|
||||
"0/54/0": "mJfMGB1w",
|
||||
"0/54/1": 0,
|
||||
"0/54/2": 3,
|
||||
"0/54/3": 1,
|
||||
"0/54/4": -36,
|
||||
"0/54/65532": 0,
|
||||
"0/54/65533": 1,
|
||||
"0/54/65528": [],
|
||||
"0/54/65529": [],
|
||||
"0/54/65531": [
|
||||
0,
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
4,
|
||||
65528,
|
||||
65529,
|
||||
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,
|
||||
65531,
|
||||
65532,
|
||||
65533
|
||||
],
|
||||
"0/62/0": [
|
||||
{
|
||||
"noc": "FTABAQEkAgE3AyQTAhgmBIAigScmBYAlTTo3BiQVASQRARgkBwEkCAEwCUEE5Rw88GvXEUXr+cPYgKd00rIWyiHM8eu4Bhrzf1v83yBI2Qa+pwfOsKyvzxiuHLMfzhdC3gre4najpimi8AsX+TcKNQEoARgkAgE2AwQCBAEYMAQUWh6NlHAMbG5gz+vqlF51fulr3z8wBRR+D1hE33RhFC/mJWrhhZs6SVStQBgwC0DD5IxVgOrftUA47K1bQHaCNuWqIxf/8oMfcI0nMvTtXApwbBAJI/LjjCwMZJVFBE3W/FC6dQWSEuF8ES745tLBGA==",
|
||||
"icac": "FTABAQEkAgE3AyQUARgmBIAigScmBYAlTTo3BiQTAhgkBwEkCAEwCUEEzpstYxy3lXF69g6H2vQ6uoqkdUsppJ4NcSyQcXQ8sQrF5HuzoVnDpevHfy0GAWHbXfE4VI0laTHvm/Wkj037ZjcKNQEpARgkAmAwBBR+D1hE33RhFC/mJWrhhZs6SVStQDAFFFCCK5NYv6CrD5/0S26zXBUwG0WBGDALQI5YKo3C3xvdqCrho2yZIJVJpJY2n9V/tmh7ESBBOHrY0b+K8Pf7hKhd5V0vzbCCbkhv1BNEne+lhcS2N6qhMNgY",
|
||||
"fabricIndex": 2
|
||||
}
|
||||
],
|
||||
"0/62/1": [
|
||||
{
|
||||
"rootPublicKey": "BFLMrM1satBpU0DN4sri/S4AVo/ugmZCndBfPO33Q+ZCKDZzNhMOB014+hZs0KL7vPssavT7Tb9nt0W+kpeAe0U=",
|
||||
"vendorId": 65521,
|
||||
"fabricId": 1,
|
||||
"nodeId": 1,
|
||||
"label": "",
|
||||
"fabricIndex": 2
|
||||
}
|
||||
],
|
||||
"0/62/2": 5,
|
||||
"0/62/3": 2,
|
||||
"0/62/4": [
|
||||
"FTABAQAkAgE3AycUBZIG4P1iqI0kFQEYJgRBkLUrJgXBw5YtNwYnFAWSBuD9YqiNJBUBGCQHASQIATAJQQRruztKRDFfiVjMY19sSsnKqBZJlZrQ/ClUtTYatvOZxbTC53iCqhwHaIJthMWs7ICwtSX1Vr5lGkzDXQjH/oQ6Nwo1ASkBGCQCYDAEFJd2wRMLYsFFA1PRCdMviVipH3OWMAUUl3bBEwtiwUUDU9EJ0y+JWKkfc5YYMAtASJa3FJ84kws+OOWNEMgRvcZA/d0AJVmmoqoWrorxxfpVKujZuN8Kc193rwBckfxd69s3OS1y8HCZTtooCemIpBg=",
|
||||
"FTABAQEkAgE3AyQUARgmBIAigScmBYAlTTo3BiQUARgkBwEkCAEwCUEEUsyszWxq0GlTQM3iyuL9LgBWj+6CZkKd0F887fdD5kIoNnM2Ew4HTXj6FmzQovu8+yxq9PtNv2e3Rb6Sl4B7RTcKNQEpARgkAmAwBBRQgiuTWL+gqw+f9Etus1wVMBtFgTAFFFCCK5NYv6CrD5/0S26zXBUwG0WBGDALQFyHXux9szIosC1gP+/1/7BX3PfGaX2GF172oHSAoMXnLJ7OawkzgWIykEj7oRIjKv3XRR27y3KhV83817SfCOkY"
|
||||
],
|
||||
"0/62/5": 2,
|
||||
"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,
|
||||
65531,
|
||||
65532,
|
||||
65533
|
||||
],
|
||||
"0/63/0": [],
|
||||
"0/63/1": [],
|
||||
"0/63/2": 3,
|
||||
"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,
|
||||
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,
|
||||
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,
|
||||
65531,
|
||||
65532,
|
||||
65533
|
||||
],
|
||||
"1/3/0": 0,
|
||||
"1/3/1": 2,
|
||||
"1/3/65532": 0,
|
||||
"1/3/65533": 4,
|
||||
"1/3/65528": [],
|
||||
"1/3/65529": [
|
||||
0,
|
||||
64
|
||||
],
|
||||
"1/3/65531": [
|
||||
0,
|
||||
1,
|
||||
65528,
|
||||
65529,
|
||||
65531,
|
||||
65532,
|
||||
65533
|
||||
],
|
||||
"1/4/0": 128,
|
||||
"1/4/65532": 1,
|
||||
"1/4/65533": 4,
|
||||
"1/4/65528": [
|
||||
0,
|
||||
1,
|
||||
2,
|
||||
3
|
||||
],
|
||||
"1/4/65529": [
|
||||
0,
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
4,
|
||||
5
|
||||
],
|
||||
"1/4/65531": [
|
||||
0,
|
||||
65528,
|
||||
65529,
|
||||
65531,
|
||||
65532,
|
||||
65533
|
||||
],
|
||||
"1/29/0": [
|
||||
{
|
||||
"type": 514,
|
||||
"revision": 1
|
||||
}
|
||||
],
|
||||
"1/29/1": [
|
||||
3,
|
||||
4,
|
||||
29,
|
||||
30,
|
||||
64,
|
||||
65,
|
||||
258
|
||||
],
|
||||
"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,
|
||||
65531,
|
||||
65532,
|
||||
65533
|
||||
],
|
||||
"1/30/0": [],
|
||||
"1/30/65532": 0,
|
||||
"1/30/65533": 1,
|
||||
"1/30/65528": [],
|
||||
"1/30/65529": [],
|
||||
"1/30/65531": [
|
||||
0,
|
||||
65528,
|
||||
65529,
|
||||
65531,
|
||||
65532,
|
||||
65533
|
||||
],
|
||||
"1/64/0": [
|
||||
{
|
||||
"label": "room",
|
||||
"value": "bedroom 2"
|
||||
},
|
||||
{
|
||||
"label": "orientation",
|
||||
"value": "North"
|
||||
},
|
||||
{
|
||||
"label": "floor",
|
||||
"value": "2"
|
||||
},
|
||||
{
|
||||
"label": "direction",
|
||||
"value": "up"
|
||||
}
|
||||
],
|
||||
"1/64/65532": 0,
|
||||
"1/64/65533": 1,
|
||||
"1/64/65528": [],
|
||||
"1/64/65529": [],
|
||||
"1/64/65531": [
|
||||
0,
|
||||
65528,
|
||||
65529,
|
||||
65531,
|
||||
65532,
|
||||
65533
|
||||
],
|
||||
"1/65/0": [],
|
||||
"1/65/65532": 0,
|
||||
"1/65/65533": 1,
|
||||
"1/65/65528": [],
|
||||
"1/65/65529": [],
|
||||
"1/65/65531": [
|
||||
0,
|
||||
65528,
|
||||
65529,
|
||||
65531,
|
||||
65532,
|
||||
65533
|
||||
],
|
||||
"1/258/0": 0,
|
||||
"1/258/1": 0,
|
||||
"1/258/3": 0,
|
||||
"1/258/5": 0,
|
||||
"1/258/7": 11,
|
||||
"1/258/8": 100,
|
||||
"1/258/10": 0,
|
||||
"1/258/11": 0,
|
||||
"1/258/13": 0,
|
||||
"1/258/14": 4900,
|
||||
"1/258/16": 0,
|
||||
"1/258/17": 65535,
|
||||
"1/258/23": 0,
|
||||
"1/258/65532": 13,
|
||||
"1/258/65533": 5,
|
||||
"1/258/65528": [],
|
||||
"1/258/65529": [
|
||||
0,
|
||||
1,
|
||||
2,
|
||||
4,
|
||||
5,
|
||||
18,
|
||||
19
|
||||
],
|
||||
"1/258/65531": [
|
||||
0,
|
||||
1,
|
||||
3,
|
||||
5,
|
||||
7,
|
||||
8,
|
||||
10,
|
||||
11,
|
||||
13,
|
||||
14,
|
||||
16,
|
||||
17,
|
||||
23,
|
||||
65528,
|
||||
65529,
|
||||
65531,
|
||||
65532,
|
||||
65533
|
||||
]
|
||||
}
|
||||
}
|
141
tests/components/matter/test_cover.py
Normal file
141
tests/components/matter/test_cover.py
Normal file
|
@ -0,0 +1,141 @@
|
|||
"""Test Matter covers."""
|
||||
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.cover import (
|
||||
STATE_CLOSED,
|
||||
STATE_CLOSING,
|
||||
STATE_OPEN,
|
||||
STATE_OPENING,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from .common import (
|
||||
set_node_attribute,
|
||||
setup_integration_with_node_fixture,
|
||||
trigger_subscription_callback,
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture(name="window_covering")
|
||||
async def window_covering_fixture(
|
||||
hass: HomeAssistant, matter_client: MagicMock
|
||||
) -> MatterNode:
|
||||
"""Fixture for a window covering node."""
|
||||
return await setup_integration_with_node_fixture(
|
||||
hass, "window-covering", matter_client
|
||||
)
|
||||
|
||||
|
||||
# This tests needs to be adjusted to remove lingering tasks
|
||||
@pytest.mark.parametrize("expected_lingering_tasks", [True])
|
||||
async def test_cover(
|
||||
hass: HomeAssistant,
|
||||
matter_client: MagicMock,
|
||||
window_covering: MatterNode,
|
||||
) -> None:
|
||||
"""Test window covering."""
|
||||
await hass.services.async_call(
|
||||
"cover",
|
||||
"close_cover",
|
||||
{
|
||||
"entity_id": "cover.longan_link_wncv_da01",
|
||||
},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
assert matter_client.send_device_command.call_count == 1
|
||||
assert matter_client.send_device_command.call_args == call(
|
||||
node_id=window_covering.node_id,
|
||||
endpoint_id=1,
|
||||
command=clusters.WindowCovering.Commands.DownOrClose(),
|
||||
)
|
||||
matter_client.send_device_command.reset_mock()
|
||||
|
||||
await hass.services.async_call(
|
||||
"cover",
|
||||
"stop_cover",
|
||||
{
|
||||
"entity_id": "cover.longan_link_wncv_da01",
|
||||
},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
assert matter_client.send_device_command.call_count == 1
|
||||
assert matter_client.send_device_command.call_args == call(
|
||||
node_id=window_covering.node_id,
|
||||
endpoint_id=1,
|
||||
command=clusters.WindowCovering.Commands.StopMotion(),
|
||||
)
|
||||
matter_client.send_device_command.reset_mock()
|
||||
|
||||
await hass.services.async_call(
|
||||
"cover",
|
||||
"open_cover",
|
||||
{
|
||||
"entity_id": "cover.longan_link_wncv_da01",
|
||||
},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
assert matter_client.send_device_command.call_count == 1
|
||||
assert matter_client.send_device_command.call_args == call(
|
||||
node_id=window_covering.node_id,
|
||||
endpoint_id=1,
|
||||
command=clusters.WindowCovering.Commands.UpOrOpen(),
|
||||
)
|
||||
matter_client.send_device_command.reset_mock()
|
||||
|
||||
await hass.services.async_call(
|
||||
"cover",
|
||||
"set_cover_position",
|
||||
{
|
||||
"entity_id": "cover.longan_link_wncv_da01",
|
||||
"position": 50,
|
||||
},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
assert matter_client.send_device_command.call_count == 1
|
||||
assert matter_client.send_device_command.call_args == call(
|
||||
node_id=window_covering.node_id,
|
||||
endpoint_id=1,
|
||||
command=clusters.WindowCovering.Commands.GoToLiftValue(50),
|
||||
)
|
||||
matter_client.send_device_command.reset_mock()
|
||||
|
||||
set_node_attribute(window_covering, 1, 258, 8, 30)
|
||||
set_node_attribute(window_covering, 1, 258, 10, 2)
|
||||
await trigger_subscription_callback(hass, matter_client)
|
||||
|
||||
state = hass.states.get("cover.longan_link_wncv_da01")
|
||||
assert state
|
||||
assert state.state == STATE_CLOSING
|
||||
|
||||
set_node_attribute(window_covering, 1, 258, 8, 0)
|
||||
set_node_attribute(window_covering, 1, 258, 10, 0)
|
||||
await trigger_subscription_callback(hass, matter_client)
|
||||
|
||||
state = hass.states.get("cover.longan_link_wncv_da01")
|
||||
assert state
|
||||
assert state.state == STATE_CLOSED
|
||||
|
||||
set_node_attribute(window_covering, 1, 258, 8, 50)
|
||||
set_node_attribute(window_covering, 1, 258, 10, 1)
|
||||
await trigger_subscription_callback(hass, matter_client)
|
||||
|
||||
state = hass.states.get("cover.longan_link_wncv_da01")
|
||||
assert state
|
||||
assert state.state == STATE_OPENING
|
||||
|
||||
set_node_attribute(window_covering, 1, 258, 8, 100)
|
||||
set_node_attribute(window_covering, 1, 258, 10, 0)
|
||||
await trigger_subscription_callback(hass, matter_client)
|
||||
|
||||
state = hass.states.get("cover.longan_link_wncv_da01")
|
||||
assert state
|
||||
assert state.attributes["current_position"] == 100
|
||||
assert state.state == STATE_OPEN
|
Loading…
Add table
Reference in a new issue