Prevent toggle from calling stop on covers which do not support it (#106848)
* Prevent toggle from calling stop on covers which do not support it * Update homeassistant/components/cover/__init__.py --------- Co-authored-by: Erik Montnemery <erik@montnemery.com>
This commit is contained in:
parent
3a36117c08
commit
3c53693fe3
3 changed files with 52 additions and 16 deletions
|
@ -481,7 +481,7 @@ class CoverEntity(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
|
|||
def _get_toggle_function(
|
||||
self, fns: dict[str, Callable[_P, _R]]
|
||||
) -> Callable[_P, _R]:
|
||||
if CoverEntityFeature.STOP | self.supported_features and (
|
||||
if self.supported_features & CoverEntityFeature.STOP and (
|
||||
self.is_closing or self.is_opening
|
||||
):
|
||||
return fns["stop"]
|
||||
|
|
|
@ -34,7 +34,8 @@ async def test_services(hass: HomeAssistant, enable_custom_integrations: None) -
|
|||
# ent3 = cover with simple tilt functions and no position
|
||||
# ent4 = cover with all tilt functions but no position
|
||||
# ent5 = cover with all functions
|
||||
ent1, ent2, ent3, ent4, ent5 = platform.ENTITIES
|
||||
# ent6 = cover with only open/close, but also reports opening/closing
|
||||
ent1, ent2, ent3, ent4, ent5, ent6 = platform.ENTITIES
|
||||
|
||||
# Test init all covers should be open
|
||||
assert is_open(hass, ent1)
|
||||
|
@ -42,6 +43,7 @@ async def test_services(hass: HomeAssistant, enable_custom_integrations: None) -
|
|||
assert is_open(hass, ent3)
|
||||
assert is_open(hass, ent4)
|
||||
assert is_open(hass, ent5)
|
||||
assert is_open(hass, ent6)
|
||||
|
||||
# call basic toggle services
|
||||
await call_service(hass, SERVICE_TOGGLE, ent1)
|
||||
|
@ -49,13 +51,15 @@ async def test_services(hass: HomeAssistant, enable_custom_integrations: None) -
|
|||
await call_service(hass, SERVICE_TOGGLE, ent3)
|
||||
await call_service(hass, SERVICE_TOGGLE, ent4)
|
||||
await call_service(hass, SERVICE_TOGGLE, ent5)
|
||||
await call_service(hass, SERVICE_TOGGLE, ent6)
|
||||
|
||||
# entities without stop should be closed and with stop should be closing
|
||||
# entities should be either closed or closing, depending on if they report transitional states
|
||||
assert is_closed(hass, ent1)
|
||||
assert is_closing(hass, ent2)
|
||||
assert is_closed(hass, ent3)
|
||||
assert is_closed(hass, ent4)
|
||||
assert is_closing(hass, ent5)
|
||||
assert is_closing(hass, ent6)
|
||||
|
||||
# call basic toggle services and set different cover position states
|
||||
await call_service(hass, SERVICE_TOGGLE, ent1)
|
||||
|
@ -65,6 +69,7 @@ async def test_services(hass: HomeAssistant, enable_custom_integrations: None) -
|
|||
await call_service(hass, SERVICE_TOGGLE, ent4)
|
||||
set_cover_position(ent5, 15)
|
||||
await call_service(hass, SERVICE_TOGGLE, ent5)
|
||||
await call_service(hass, SERVICE_TOGGLE, ent6)
|
||||
|
||||
# entities should be in correct state depending on the SUPPORT_STOP feature and cover position
|
||||
assert is_open(hass, ent1)
|
||||
|
@ -72,6 +77,7 @@ async def test_services(hass: HomeAssistant, enable_custom_integrations: None) -
|
|||
assert is_open(hass, ent3)
|
||||
assert is_open(hass, ent4)
|
||||
assert is_open(hass, ent5)
|
||||
assert is_opening(hass, ent6)
|
||||
|
||||
# call basic toggle services
|
||||
await call_service(hass, SERVICE_TOGGLE, ent1)
|
||||
|
@ -79,6 +85,7 @@ async def test_services(hass: HomeAssistant, enable_custom_integrations: None) -
|
|||
await call_service(hass, SERVICE_TOGGLE, ent3)
|
||||
await call_service(hass, SERVICE_TOGGLE, ent4)
|
||||
await call_service(hass, SERVICE_TOGGLE, ent5)
|
||||
await call_service(hass, SERVICE_TOGGLE, ent6)
|
||||
|
||||
# entities should be in correct state depending on the SUPPORT_STOP feature and cover position
|
||||
assert is_closed(hass, ent1)
|
||||
|
@ -86,6 +93,12 @@ async def test_services(hass: HomeAssistant, enable_custom_integrations: None) -
|
|||
assert is_closed(hass, ent3)
|
||||
assert is_closed(hass, ent4)
|
||||
assert is_opening(hass, ent5)
|
||||
assert is_closing(hass, ent6)
|
||||
|
||||
# Without STOP but still reports opening/closing has a 4th possible toggle state
|
||||
set_state(ent6, STATE_CLOSED)
|
||||
await call_service(hass, SERVICE_TOGGLE, ent6)
|
||||
assert is_opening(hass, ent6)
|
||||
|
||||
|
||||
def call_service(hass, service, ent):
|
||||
|
@ -100,6 +113,11 @@ def set_cover_position(ent, position) -> None:
|
|||
ent._values["current_cover_position"] = position
|
||||
|
||||
|
||||
def set_state(ent, state) -> None:
|
||||
"""Set the state of a cover."""
|
||||
ent._values["state"] = state
|
||||
|
||||
|
||||
def is_open(hass, ent):
|
||||
"""Return if the cover is closed based on the statemachine."""
|
||||
return hass.states.is_state(ent.entity_id, STATE_OPEN)
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
Call init before using it in your tests to ensure clean test data.
|
||||
"""
|
||||
from typing import Any
|
||||
|
||||
from homeassistant.components.cover import CoverEntity, CoverEntityFeature
|
||||
from homeassistant.const import STATE_CLOSED, STATE_CLOSING, STATE_OPEN, STATE_OPENING
|
||||
|
||||
|
@ -70,6 +72,13 @@ def init(empty=False):
|
|||
| CoverEntityFeature.STOP_TILT
|
||||
| CoverEntityFeature.SET_TILT_POSITION,
|
||||
),
|
||||
MockCover(
|
||||
name="Simple with opening/closing cover",
|
||||
is_on=True,
|
||||
unique_id="unique_opening_closing_cover",
|
||||
supported_features=CoverEntityFeature.OPEN | CoverEntityFeature.CLOSE,
|
||||
reports_opening_closing=True,
|
||||
),
|
||||
]
|
||||
)
|
||||
|
||||
|
@ -84,50 +93,59 @@ async def async_setup_platform(
|
|||
class MockCover(MockEntity, CoverEntity):
|
||||
"""Mock Cover class."""
|
||||
|
||||
def __init__(
|
||||
self, reports_opening_closing: bool | None = None, **values: Any
|
||||
) -> None:
|
||||
"""Initialize a mock cover entity."""
|
||||
|
||||
super().__init__(**values)
|
||||
self._reports_opening_closing = (
|
||||
reports_opening_closing
|
||||
if reports_opening_closing is not None
|
||||
else CoverEntityFeature.STOP in self.supported_features
|
||||
)
|
||||
|
||||
@property
|
||||
def is_closed(self):
|
||||
"""Return if the cover is closed or not."""
|
||||
if self.supported_features & CoverEntityFeature.STOP:
|
||||
return self.current_cover_position == 0
|
||||
if "state" in self._values and self._values["state"] == STATE_CLOSED:
|
||||
return True
|
||||
|
||||
if "state" in self._values:
|
||||
return self._values["state"] == STATE_CLOSED
|
||||
return False
|
||||
return self.current_cover_position == 0
|
||||
|
||||
@property
|
||||
def is_opening(self):
|
||||
"""Return if the cover is opening or not."""
|
||||
if self.supported_features & CoverEntityFeature.STOP:
|
||||
if "state" in self._values:
|
||||
return self._values["state"] == STATE_OPENING
|
||||
if "state" in self._values:
|
||||
return self._values["state"] == STATE_OPENING
|
||||
|
||||
return False
|
||||
|
||||
@property
|
||||
def is_closing(self):
|
||||
"""Return if the cover is closing or not."""
|
||||
if self.supported_features & CoverEntityFeature.STOP:
|
||||
if "state" in self._values:
|
||||
return self._values["state"] == STATE_CLOSING
|
||||
if "state" in self._values:
|
||||
return self._values["state"] == STATE_CLOSING
|
||||
|
||||
return False
|
||||
|
||||
def open_cover(self, **kwargs) -> None:
|
||||
"""Open cover."""
|
||||
if self.supported_features & CoverEntityFeature.STOP:
|
||||
if self._reports_opening_closing:
|
||||
self._values["state"] = STATE_OPENING
|
||||
else:
|
||||
self._values["state"] = STATE_OPEN
|
||||
|
||||
def close_cover(self, **kwargs) -> None:
|
||||
"""Close cover."""
|
||||
if self.supported_features & CoverEntityFeature.STOP:
|
||||
if self._reports_opening_closing:
|
||||
self._values["state"] = STATE_CLOSING
|
||||
else:
|
||||
self._values["state"] = STATE_CLOSED
|
||||
|
||||
def stop_cover(self, **kwargs) -> None:
|
||||
"""Stop cover."""
|
||||
assert CoverEntityFeature.STOP in self.supported_features
|
||||
self._values["state"] = STATE_CLOSED if self.is_closed else STATE_OPEN
|
||||
|
||||
@property
|
||||
|
|
Loading…
Add table
Reference in a new issue