Matter cover position improvements (#92278)

This commit is contained in:
Diego Rodríguez Royo 2023-05-31 16:08:01 +02:00 committed by GitHub
parent c8c368340d
commit 3cf8ae64c8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 130 additions and 52 deletions

View File

@ -2,6 +2,7 @@
from __future__ import annotations
from enum import IntEnum
from math import floor
from typing import Any
from chip.clusters import Objects as clusters
@ -59,9 +60,18 @@ class MatterCover(MatterEntity, CoverEntity):
entity_description: CoverEntityDescription
@property
def is_closed(self) -> bool:
"""Return true if cover is closed, else False."""
return self.current_cover_position == 0
def is_closed(self) -> bool | None:
"""Return true if cover is closed, if there is no position report, return None."""
if not self._entity_info.endpoint.has_attribute(
None, clusters.WindowCovering.Attributes.CurrentPositionLiftPercent100ths
):
return None
return (
self.current_cover_position == 0
if self.current_cover_position is not None
else None
)
async def async_stop_cover(self, **kwargs: Any) -> None:
"""Stop the cover movement."""
@ -126,37 +136,45 @@ class MatterCover(MatterEntity, CoverEntity):
self._attr_is_opening = False
self._attr_is_closing = False
# current position is inverted in matter (100 is closed, 0 is open)
current_cover_position = self.get_matter_attribute_value(
clusters.WindowCovering.Attributes.CurrentPositionLiftPercentage
)
self._attr_current_cover_position = (
100 - current_cover_position if current_cover_position is not None else None
)
if self._entity_info.endpoint.has_attribute(
None, clusters.WindowCovering.Attributes.CurrentPositionLiftPercent100ths
):
# current position is inverted in matter (100 is closed, 0 is open)
current_cover_position = self.get_matter_attribute_value(
clusters.WindowCovering.Attributes.CurrentPositionLiftPercent100ths
)
self._attr_current_cover_position = (
100 - floor(current_cover_position / 100)
if current_cover_position is not None
else None
)
LOGGER.debug(
"Current position for %s - raw: %s - corrected: %s",
self.entity_id,
current_cover_position,
self.current_cover_position,
)
LOGGER.debug(
"Current position for %s - raw: %s - corrected: %s",
self.entity_id,
current_cover_position,
self.current_cover_position,
)
# current tilt position is inverted in matter (100 is closed, 0 is open)
current_cover_tilt_position = self.get_matter_attribute_value(
clusters.WindowCovering.Attributes.CurrentPositionTiltPercentage
)
self._attr_current_cover_tilt_position = (
100 - current_cover_tilt_position
if current_cover_tilt_position is not None
else None
)
if self._entity_info.endpoint.has_attribute(
None, clusters.WindowCovering.Attributes.CurrentPositionTiltPercent100ths
):
# current tilt position is inverted in matter (100 is closed, 0 is open)
current_cover_tilt_position = self.get_matter_attribute_value(
clusters.WindowCovering.Attributes.CurrentPositionTiltPercent100ths
)
self._attr_current_cover_tilt_position = (
100 - floor(current_cover_tilt_position / 100)
if current_cover_tilt_position is not None
else None
)
LOGGER.debug(
"Current tilt position for %s - raw: %s - corrected: %s",
self.entity_id,
current_cover_tilt_position,
self.current_cover_tilt_position,
)
LOGGER.debug(
"Current tilt position for %s - raw: %s - corrected: %s",
self.entity_id,
current_cover_tilt_position,
self.current_cover_tilt_position,
)
# map matter type to HA deviceclass
device_type: clusters.WindowCovering.Enums.Type = (
@ -188,8 +206,8 @@ DISCOVERY_SCHEMAS = [
clusters.WindowCovering.Attributes.Type,
),
absent_attributes=(
clusters.WindowCovering.Attributes.CurrentPositionLiftPercentage,
clusters.WindowCovering.Attributes.CurrentPositionTiltPercentage,
clusters.WindowCovering.Attributes.CurrentPositionLiftPercent100ths,
clusters.WindowCovering.Attributes.CurrentPositionTiltPercent100ths,
),
),
MatterDiscoverySchema(
@ -199,10 +217,10 @@ DISCOVERY_SCHEMAS = [
required_attributes=(
clusters.WindowCovering.Attributes.OperationalStatus,
clusters.WindowCovering.Attributes.Type,
clusters.WindowCovering.Attributes.CurrentPositionLiftPercentage,
clusters.WindowCovering.Attributes.CurrentPositionLiftPercent100ths,
),
absent_attributes=(
clusters.WindowCovering.Attributes.CurrentPositionTiltPercentage,
clusters.WindowCovering.Attributes.CurrentPositionTiltPercent100ths,
),
),
MatterDiscoverySchema(
@ -212,10 +230,10 @@ DISCOVERY_SCHEMAS = [
required_attributes=(
clusters.WindowCovering.Attributes.OperationalStatus,
clusters.WindowCovering.Attributes.Type,
clusters.WindowCovering.Attributes.CurrentPositionTiltPercentage,
clusters.WindowCovering.Attributes.CurrentPositionTiltPercent100ths,
),
absent_attributes=(
clusters.WindowCovering.Attributes.CurrentPositionLiftPercentage,
clusters.WindowCovering.Attributes.CurrentPositionLiftPercent100ths,
),
),
MatterDiscoverySchema(
@ -227,8 +245,8 @@ DISCOVERY_SCHEMAS = [
required_attributes=(
clusters.WindowCovering.Attributes.OperationalStatus,
clusters.WindowCovering.Attributes.Type,
clusters.WindowCovering.Attributes.CurrentPositionLiftPercentage,
clusters.WindowCovering.Attributes.CurrentPositionTiltPercentage,
clusters.WindowCovering.Attributes.CurrentPositionLiftPercent100ths,
clusters.WindowCovering.Attributes.CurrentPositionTiltPercent100ths,
),
),
]

View File

@ -1,4 +1,5 @@
"""Test Matter covers."""
from math import floor
from unittest.mock import MagicMock, call
from chip.clusters import Objects as clusters
@ -177,6 +178,14 @@ async def test_cover_lift_only(
matter_client,
)
set_node_attribute(window_covering, 1, 258, 14, None)
set_node_attribute(window_covering, 1, 258, 10, 0b000000)
await trigger_subscription_callback(hass, matter_client)
state = hass.states.get(entity_id)
assert state
assert state.state == "unknown"
set_node_attribute(window_covering, 1, 258, 65529, [0, 1, 2])
await trigger_subscription_callback(hass, matter_client)
@ -224,17 +233,17 @@ async def test_cover_position_aware_lift(
)
assert state.attributes["supported_features"] & mask == mask
for position in (0, 99):
set_node_attribute(window_covering, 1, 258, 8, position)
for position in (0, 9999):
set_node_attribute(window_covering, 1, 258, 14, position)
set_node_attribute(window_covering, 1, 258, 10, 0b000000)
await trigger_subscription_callback(hass, matter_client)
state = hass.states.get(entity_id)
assert state
assert state.attributes["current_position"] == 100 - position
assert state.attributes["current_position"] == 100 - floor(position / 100)
assert state.state == STATE_OPEN
set_node_attribute(window_covering, 1, 258, 8, 100)
set_node_attribute(window_covering, 1, 258, 14, 10000)
set_node_attribute(window_covering, 1, 258, 10, 0b000000)
await trigger_subscription_callback(hass, matter_client)
@ -377,14 +386,16 @@ async def test_cover_position_aware_tilt(
)
assert state.attributes["supported_features"] & mask == mask
for tilt_position in (0, 99, 100):
set_node_attribute(window_covering, 1, 258, 9, tilt_position)
for tilt_position in (0, 9999, 10000):
set_node_attribute(window_covering, 1, 258, 15, tilt_position)
set_node_attribute(window_covering, 1, 258, 10, 0b000000)
await trigger_subscription_callback(hass, matter_client)
state = hass.states.get(entity_id)
assert state
assert state.attributes["current_tilt_position"] == 100 - tilt_position
assert state.attributes["current_tilt_position"] == 100 - floor(
tilt_position / 100
)
async def test_cover_full_features(
@ -411,8 +422,8 @@ async def test_cover_full_features(
)
assert state.attributes["supported_features"] & mask == mask
set_node_attribute(window_covering, 1, 258, 8, 100)
set_node_attribute(window_covering, 1, 258, 9, 100)
set_node_attribute(window_covering, 1, 258, 14, 10000)
set_node_attribute(window_covering, 1, 258, 15, 10000)
set_node_attribute(window_covering, 1, 258, 10, 0b000000)
await trigger_subscription_callback(hass, matter_client)
@ -420,8 +431,8 @@ async def test_cover_full_features(
assert state
assert state.state == STATE_CLOSED
set_node_attribute(window_covering, 1, 258, 8, 50)
set_node_attribute(window_covering, 1, 258, 9, 100)
set_node_attribute(window_covering, 1, 258, 14, 5000)
set_node_attribute(window_covering, 1, 258, 15, 10000)
set_node_attribute(window_covering, 1, 258, 10, 0b000000)
await trigger_subscription_callback(hass, matter_client)
@ -429,11 +440,60 @@ async def test_cover_full_features(
assert state
assert state.state == STATE_OPEN
set_node_attribute(window_covering, 1, 258, 8, 100)
set_node_attribute(window_covering, 1, 258, 9, 50)
set_node_attribute(window_covering, 1, 258, 14, 10000)
set_node_attribute(window_covering, 1, 258, 15, 5000)
set_node_attribute(window_covering, 1, 258, 10, 0b000000)
await trigger_subscription_callback(hass, matter_client)
state = hass.states.get(entity_id)
assert state
assert state.state == STATE_CLOSED
set_node_attribute(window_covering, 1, 258, 14, 5000)
set_node_attribute(window_covering, 1, 258, 15, 5000)
set_node_attribute(window_covering, 1, 258, 10, 0b000000)
await trigger_subscription_callback(hass, matter_client)
state = hass.states.get(entity_id)
assert state
assert state.state == STATE_OPEN
set_node_attribute(window_covering, 1, 258, 14, 5000)
set_node_attribute(window_covering, 1, 258, 15, None)
set_node_attribute(window_covering, 1, 258, 10, 0b000000)
await trigger_subscription_callback(hass, matter_client)
state = hass.states.get(entity_id)
assert state
assert state.state == STATE_OPEN
set_node_attribute(window_covering, 1, 258, 14, None)
set_node_attribute(window_covering, 1, 258, 15, 5000)
set_node_attribute(window_covering, 1, 258, 10, 0b000000)
await trigger_subscription_callback(hass, matter_client)
state = hass.states.get(entity_id)
assert state
assert state.state == "unknown"
set_node_attribute(window_covering, 1, 258, 14, 10000)
set_node_attribute(window_covering, 1, 258, 15, None)
set_node_attribute(window_covering, 1, 258, 10, 0b000000)
await trigger_subscription_callback(hass, matter_client)
state = hass.states.get(entity_id)
assert state
assert state.state == STATE_CLOSED
set_node_attribute(window_covering, 1, 258, 14, None)
set_node_attribute(window_covering, 1, 258, 15, 10000)
set_node_attribute(window_covering, 1, 258, 10, 0b000000)
await trigger_subscription_callback(hass, matter_client)
state = hass.states.get(entity_id)
assert state
assert state.state == "unknown"
set_node_attribute(window_covering, 1, 258, 14, None)
set_node_attribute(window_covering, 1, 258, 15, None)
set_node_attribute(window_covering, 1, 258, 10, 0b000000)
await trigger_subscription_callback(hass, matter_client)
state = hass.states.get(entity_id)
assert state
assert state.state == "unknown"