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,37 +136,45 @@ class MatterCover(MatterEntity, CoverEntity):
self._attr_is_opening = False self._attr_is_opening = False
self._attr_is_closing = False self._attr_is_closing = False
# current position is inverted in matter (100 is closed, 0 is open) if self._entity_info.endpoint.has_attribute(
current_cover_position = self.get_matter_attribute_value( None, clusters.WindowCovering.Attributes.CurrentPositionLiftPercent100ths
clusters.WindowCovering.Attributes.CurrentPositionLiftPercentage ):
) # current position is inverted in matter (100 is closed, 0 is open)
self._attr_current_cover_position = ( current_cover_position = self.get_matter_attribute_value(
100 - current_cover_position if current_cover_position is not None else None 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( LOGGER.debug(
"Current position for %s - raw: %s - corrected: %s", "Current position for %s - raw: %s - corrected: %s",
self.entity_id, self.entity_id,
current_cover_position, current_cover_position,
self.current_cover_position, self.current_cover_position,
) )
# current tilt position is inverted in matter (100 is closed, 0 is open) if self._entity_info.endpoint.has_attribute(
current_cover_tilt_position = self.get_matter_attribute_value( None, clusters.WindowCovering.Attributes.CurrentPositionTiltPercent100ths
clusters.WindowCovering.Attributes.CurrentPositionTiltPercentage ):
) # current tilt position is inverted in matter (100 is closed, 0 is open)
self._attr_current_cover_tilt_position = ( current_cover_tilt_position = self.get_matter_attribute_value(
100 - current_cover_tilt_position clusters.WindowCovering.Attributes.CurrentPositionTiltPercent100ths
if current_cover_tilt_position is not None )
else None 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( LOGGER.debug(
"Current tilt position for %s - raw: %s - corrected: %s", "Current tilt position for %s - raw: %s - corrected: %s",
self.entity_id, self.entity_id,
current_cover_tilt_position, current_cover_tilt_position,
self.current_cover_tilt_position, self.current_cover_tilt_position,
) )
# map matter type to HA deviceclass # map matter type to HA deviceclass
device_type: clusters.WindowCovering.Enums.Type = ( device_type: clusters.WindowCovering.Enums.Type = (
@ -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"