Clarify cover toggle logic; prevent opening when already open (#107920)

Co-authored-by: Erik Montnemery <erik@montnemery.com>
This commit is contained in:
vexofp 2024-04-18 08:58:16 -04:00 committed by GitHub
parent 3299bc5ddc
commit 8ba1340c2e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 27 additions and 3 deletions

View file

@ -480,15 +480,30 @@ class CoverEntity(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
def _get_toggle_function( def _get_toggle_function(
self, fns: dict[str, Callable[_P, _R]] self, fns: dict[str, Callable[_P, _R]]
) -> Callable[_P, _R]: ) -> Callable[_P, _R]:
# If we are opening or closing and we support stopping, then we should stop
if self.supported_features & CoverEntityFeature.STOP and ( if self.supported_features & CoverEntityFeature.STOP and (
self.is_closing or self.is_opening self.is_closing or self.is_opening
): ):
return fns["stop"] return fns["stop"]
if self.is_closed:
# If we are fully closed or in the process of closing, then we should open
if self.is_closed or self.is_closing:
return fns["open"] return fns["open"]
if self._cover_is_last_toggle_direction_open:
# If we are fully open or in the process of opening, then we should close
if self.current_cover_position == 100 or self.is_opening:
return fns["close"] return fns["close"]
return fns["open"]
# We are any of:
# * fully open but do not report `current_cover_position`
# * stopped partially open
# * either opening or closing, but do not report them
# If we previously reported opening/closing, we should move in the opposite direction.
# Otherwise, we must assume we are (partially) open and should always close.
# Note: _cover_is_last_toggle_direction_open will always remain True if we never report opening/closing.
return (
fns["close"] if self._cover_is_last_toggle_direction_open else fns["open"]
)
# These can be removed if no deprecated constant are in this module anymore # These can be removed if no deprecated constant are in this module anymore

View file

@ -108,6 +108,15 @@ async def test_services(
await call_service(hass, SERVICE_TOGGLE, ent6) await call_service(hass, SERVICE_TOGGLE, ent6)
assert is_opening(hass, ent6) assert is_opening(hass, ent6)
# After the unusual state transition: closing -> fully open, toggle should close
set_state(ent5, STATE_OPEN)
await call_service(hass, SERVICE_TOGGLE, ent5) # Start closing
assert is_closing(hass, ent5)
set_state(ent5, STATE_OPEN) # Unusual state transition from closing -> fully open
set_cover_position(ent5, 100)
await call_service(hass, SERVICE_TOGGLE, ent5) # Should close, not open
assert is_closing(hass, ent5)
def call_service(hass, service, ent): def call_service(hass, service, ent):
"""Call any service on entity.""" """Call any service on entity."""