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 __future__ import annotations
from enum import IntEnum from enum import IntEnum
from math import floor
from typing import Any from typing import Any
from chip.clusters import Objects as clusters from chip.clusters import Objects as clusters
@ -59,9 +60,18 @@ class MatterCover(MatterEntity, CoverEntity):
entity_description: CoverEntityDescription entity_description: CoverEntityDescription
@property @property
def is_closed(self) -> bool: def is_closed(self) -> bool | None:
"""Return true if cover is closed, else False.""" """Return true if cover is closed, if there is no position report, return None."""
return self.current_cover_position == 0 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: async def async_stop_cover(self, **kwargs: Any) -> None:
"""Stop the cover movement.""" """Stop the cover movement."""
@ -126,12 +136,17 @@ class MatterCover(MatterEntity, CoverEntity):
self._attr_is_opening = False self._attr_is_opening = False
self._attr_is_closing = False self._attr_is_closing = False
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 position is inverted in matter (100 is closed, 0 is open)
current_cover_position = self.get_matter_attribute_value( current_cover_position = self.get_matter_attribute_value(
clusters.WindowCovering.Attributes.CurrentPositionLiftPercentage clusters.WindowCovering.Attributes.CurrentPositionLiftPercent100ths
) )
self._attr_current_cover_position = ( self._attr_current_cover_position = (
100 - current_cover_position if current_cover_position is not None else None 100 - floor(current_cover_position / 100)
if current_cover_position is not None
else None
) )
LOGGER.debug( LOGGER.debug(
@ -141,12 +156,15 @@ class MatterCover(MatterEntity, CoverEntity):
self.current_cover_position, self.current_cover_position,
) )
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 tilt position is inverted in matter (100 is closed, 0 is open)
current_cover_tilt_position = self.get_matter_attribute_value( current_cover_tilt_position = self.get_matter_attribute_value(
clusters.WindowCovering.Attributes.CurrentPositionTiltPercentage clusters.WindowCovering.Attributes.CurrentPositionTiltPercent100ths
) )
self._attr_current_cover_tilt_position = ( self._attr_current_cover_tilt_position = (
100 - current_cover_tilt_position 100 - floor(current_cover_tilt_position / 100)
if current_cover_tilt_position is not None if current_cover_tilt_position is not None
else None else None
) )
@ -188,8 +206,8 @@ DISCOVERY_SCHEMAS = [
clusters.WindowCovering.Attributes.Type, clusters.WindowCovering.Attributes.Type,
), ),
absent_attributes=( absent_attributes=(
clusters.WindowCovering.Attributes.CurrentPositionLiftPercentage, clusters.WindowCovering.Attributes.CurrentPositionLiftPercent100ths,
clusters.WindowCovering.Attributes.CurrentPositionTiltPercentage, clusters.WindowCovering.Attributes.CurrentPositionTiltPercent100ths,
), ),
), ),
MatterDiscoverySchema( MatterDiscoverySchema(
@ -199,10 +217,10 @@ DISCOVERY_SCHEMAS = [
required_attributes=( required_attributes=(
clusters.WindowCovering.Attributes.OperationalStatus, clusters.WindowCovering.Attributes.OperationalStatus,
clusters.WindowCovering.Attributes.Type, clusters.WindowCovering.Attributes.Type,
clusters.WindowCovering.Attributes.CurrentPositionLiftPercentage, clusters.WindowCovering.Attributes.CurrentPositionLiftPercent100ths,
), ),
absent_attributes=( absent_attributes=(
clusters.WindowCovering.Attributes.CurrentPositionTiltPercentage, clusters.WindowCovering.Attributes.CurrentPositionTiltPercent100ths,
), ),
), ),
MatterDiscoverySchema( MatterDiscoverySchema(
@ -212,10 +230,10 @@ DISCOVERY_SCHEMAS = [
required_attributes=( required_attributes=(
clusters.WindowCovering.Attributes.OperationalStatus, clusters.WindowCovering.Attributes.OperationalStatus,
clusters.WindowCovering.Attributes.Type, clusters.WindowCovering.Attributes.Type,
clusters.WindowCovering.Attributes.CurrentPositionTiltPercentage, clusters.WindowCovering.Attributes.CurrentPositionTiltPercent100ths,
), ),
absent_attributes=( absent_attributes=(
clusters.WindowCovering.Attributes.CurrentPositionLiftPercentage, clusters.WindowCovering.Attributes.CurrentPositionLiftPercent100ths,
), ),
), ),
MatterDiscoverySchema( MatterDiscoverySchema(
@ -227,8 +245,8 @@ DISCOVERY_SCHEMAS = [
required_attributes=( required_attributes=(
clusters.WindowCovering.Attributes.OperationalStatus, clusters.WindowCovering.Attributes.OperationalStatus,
clusters.WindowCovering.Attributes.Type, clusters.WindowCovering.Attributes.Type,
clusters.WindowCovering.Attributes.CurrentPositionLiftPercentage, clusters.WindowCovering.Attributes.CurrentPositionLiftPercent100ths,
clusters.WindowCovering.Attributes.CurrentPositionTiltPercentage, clusters.WindowCovering.Attributes.CurrentPositionTiltPercent100ths,
), ),
), ),
] ]

View File

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