mirror of
https://github.com/home-assistant/core.git
synced 2025-04-24 09:17:53 +00:00
Mqtt cover toggle add stop function (#59233)
* Change existing toggle to add new function * Fixed using old property method to using actual protected variable. * Adding service tests to new cover toggle function * Working on comments from Pull Request 59233 * Adjust existing tests to fit new fake cover setup * MockCover is calling state method of MockEntity but should call it from CoverEntity * using different entity to get back test coverage
This commit is contained in:
parent
01fe69511f
commit
eec84ad71e
@ -187,6 +187,8 @@ class CoverEntity(Entity):
|
||||
_attr_is_opening: bool | None = None
|
||||
_attr_state: None = None
|
||||
|
||||
_cover_is_last_toggle_direction_open = True
|
||||
|
||||
@property
|
||||
def current_cover_position(self) -> int | None:
|
||||
"""Return current position of cover.
|
||||
@ -208,8 +210,10 @@ class CoverEntity(Entity):
|
||||
def state(self) -> str | None:
|
||||
"""Return the state of the cover."""
|
||||
if self.is_opening:
|
||||
self._cover_is_last_toggle_direction_open = True
|
||||
return STATE_OPENING
|
||||
if self.is_closing:
|
||||
self._cover_is_last_toggle_direction_open = False
|
||||
return STATE_CLOSING
|
||||
|
||||
if (closed := self.is_closed) is None:
|
||||
@ -285,17 +289,23 @@ class CoverEntity(Entity):
|
||||
|
||||
def toggle(self, **kwargs: Any) -> None:
|
||||
"""Toggle the entity."""
|
||||
if self.is_closed:
|
||||
self.open_cover(**kwargs)
|
||||
else:
|
||||
self.close_cover(**kwargs)
|
||||
fns = {
|
||||
"open": self.open_cover,
|
||||
"close": self.close_cover,
|
||||
"stop": self.stop_cover,
|
||||
}
|
||||
function = self._get_toggle_function(fns)
|
||||
function(**kwargs)
|
||||
|
||||
async def async_toggle(self, **kwargs):
|
||||
"""Toggle the entity."""
|
||||
if self.is_closed:
|
||||
await self.async_open_cover(**kwargs)
|
||||
else:
|
||||
await self.async_close_cover(**kwargs)
|
||||
fns = {
|
||||
"open": self.async_open_cover,
|
||||
"close": self.async_close_cover,
|
||||
"stop": self.async_stop_cover,
|
||||
}
|
||||
function = self._get_toggle_function(fns)
|
||||
await function(**kwargs)
|
||||
|
||||
def set_cover_position(self, **kwargs):
|
||||
"""Move the cover to a specific position."""
|
||||
@ -363,6 +373,17 @@ class CoverEntity(Entity):
|
||||
else:
|
||||
await self.async_close_cover_tilt(**kwargs)
|
||||
|
||||
def _get_toggle_function(self, fns):
|
||||
if SUPPORT_STOP | self.supported_features and (
|
||||
self.is_closing or self.is_opening
|
||||
):
|
||||
return fns["stop"]
|
||||
if self.is_closed:
|
||||
return fns["open"]
|
||||
if self._cover_is_last_toggle_direction_open:
|
||||
return fns["close"]
|
||||
return fns["open"]
|
||||
|
||||
|
||||
class CoverDevice(CoverEntity):
|
||||
"""Representation of a cover (for backwards compatibility)."""
|
||||
|
@ -110,7 +110,7 @@ async def test_get_action_capabilities(
|
||||
"""Test we get the expected capabilities from a cover action."""
|
||||
platform = getattr(hass.components, f"test.{DOMAIN}")
|
||||
platform.init()
|
||||
ent = platform.ENTITIES[0]
|
||||
ent = platform.ENTITIES[2]
|
||||
|
||||
config_entry = MockConfigEntry(domain="test", data={})
|
||||
config_entry.add_to_hass(hass)
|
||||
@ -126,7 +126,7 @@ async def test_get_action_capabilities(
|
||||
await hass.async_block_till_done()
|
||||
|
||||
actions = await async_get_device_automations(hass, "action", device_entry.id)
|
||||
assert len(actions) == 3 # open, close, stop
|
||||
assert len(actions) == 4 # open, close, stop, set_position
|
||||
for action in actions:
|
||||
capabilities = await async_get_device_automation_capabilities(
|
||||
hass, "action", action
|
||||
|
@ -196,7 +196,7 @@ async def test_get_condition_capabilities_set_tilt_pos(
|
||||
"""Test we get the expected capabilities from a cover condition."""
|
||||
platform = getattr(hass.components, f"test.{DOMAIN}")
|
||||
platform.init()
|
||||
ent = platform.ENTITIES[2]
|
||||
ent = platform.ENTITIES[3]
|
||||
|
||||
config_entry = MockConfigEntry(domain="test", data={})
|
||||
config_entry.add_to_hass(hass)
|
||||
@ -487,7 +487,7 @@ async def test_if_tilt_position(hass, calls, caplog, enable_custom_integrations)
|
||||
"""Test for tilt position conditions."""
|
||||
platform = getattr(hass.components, f"test.{DOMAIN}")
|
||||
platform.init()
|
||||
ent = platform.ENTITIES[2]
|
||||
ent = platform.ENTITIES[3]
|
||||
assert await async_setup_component(hass, DOMAIN, {DOMAIN: {CONF_PLATFORM: "test"}})
|
||||
await hass.async_block_till_done()
|
||||
|
||||
|
@ -228,7 +228,7 @@ async def test_get_trigger_capabilities_set_tilt_pos(
|
||||
"""Test we get the expected capabilities from a cover trigger."""
|
||||
platform = getattr(hass.components, f"test.{DOMAIN}")
|
||||
platform.init()
|
||||
ent = platform.ENTITIES[2]
|
||||
ent = platform.ENTITIES[3]
|
||||
|
||||
config_entry = MockConfigEntry(domain="test", data={})
|
||||
config_entry.add_to_hass(hass)
|
||||
|
@ -1,5 +1,116 @@
|
||||
"""The tests for Cover."""
|
||||
import homeassistant.components.cover as cover
|
||||
from homeassistant.const import (
|
||||
ATTR_ENTITY_ID,
|
||||
CONF_PLATFORM,
|
||||
SERVICE_TOGGLE,
|
||||
STATE_CLOSED,
|
||||
STATE_CLOSING,
|
||||
STATE_OPEN,
|
||||
STATE_OPENING,
|
||||
)
|
||||
from homeassistant.setup import async_setup_component
|
||||
|
||||
|
||||
async def test_services(hass, enable_custom_integrations):
|
||||
"""Test the provided services."""
|
||||
platform = getattr(hass.components, "test.cover")
|
||||
|
||||
platform.init()
|
||||
assert await async_setup_component(
|
||||
hass, cover.DOMAIN, {cover.DOMAIN: {CONF_PLATFORM: "test"}}
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
# ent1 = cover without tilt and position
|
||||
# ent2 = cover with position but no tilt
|
||||
# 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
|
||||
|
||||
# Test init all covers should be open
|
||||
assert is_open(hass, ent1)
|
||||
assert is_open(hass, ent2)
|
||||
assert is_open(hass, ent3)
|
||||
assert is_open(hass, ent4)
|
||||
assert is_open(hass, ent5)
|
||||
|
||||
# call basic toggle services
|
||||
await call_service(hass, SERVICE_TOGGLE, ent1)
|
||||
await call_service(hass, SERVICE_TOGGLE, ent2)
|
||||
await call_service(hass, SERVICE_TOGGLE, ent3)
|
||||
await call_service(hass, SERVICE_TOGGLE, ent4)
|
||||
await call_service(hass, SERVICE_TOGGLE, ent5)
|
||||
|
||||
# entities without stop should be closed and with stop should be closing
|
||||
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)
|
||||
|
||||
# call basic toggle services and set different cover position states
|
||||
await call_service(hass, SERVICE_TOGGLE, ent1)
|
||||
set_cover_position(ent2, 0)
|
||||
await call_service(hass, SERVICE_TOGGLE, ent2)
|
||||
await call_service(hass, SERVICE_TOGGLE, ent3)
|
||||
await call_service(hass, SERVICE_TOGGLE, ent4)
|
||||
set_cover_position(ent5, 15)
|
||||
await call_service(hass, SERVICE_TOGGLE, ent5)
|
||||
|
||||
# entities should be in correct state depending on the SUPPORT_STOP feature and cover position
|
||||
assert is_open(hass, ent1)
|
||||
assert is_closed(hass, ent2)
|
||||
assert is_open(hass, ent3)
|
||||
assert is_open(hass, ent4)
|
||||
assert is_open(hass, ent5)
|
||||
|
||||
# call basic toggle services
|
||||
await call_service(hass, SERVICE_TOGGLE, ent1)
|
||||
await call_service(hass, SERVICE_TOGGLE, ent2)
|
||||
await call_service(hass, SERVICE_TOGGLE, ent3)
|
||||
await call_service(hass, SERVICE_TOGGLE, ent4)
|
||||
await call_service(hass, SERVICE_TOGGLE, ent5)
|
||||
|
||||
# entities should be in correct state depending on the SUPPORT_STOP feature and cover position
|
||||
assert is_closed(hass, ent1)
|
||||
assert is_opening(hass, ent2)
|
||||
assert is_closed(hass, ent3)
|
||||
assert is_closed(hass, ent4)
|
||||
assert is_opening(hass, ent5)
|
||||
|
||||
|
||||
def call_service(hass, service, ent):
|
||||
"""Call any service on entity."""
|
||||
return hass.services.async_call(
|
||||
cover.DOMAIN, service, {ATTR_ENTITY_ID: ent.entity_id}, blocking=True
|
||||
)
|
||||
|
||||
|
||||
def set_cover_position(ent, position) -> None:
|
||||
"""Set a position value to a cover."""
|
||||
ent._values["current_cover_position"] = position
|
||||
|
||||
|
||||
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)
|
||||
|
||||
|
||||
def is_opening(hass, ent):
|
||||
"""Return if the cover is closed based on the statemachine."""
|
||||
return hass.states.is_state(ent.entity_id, STATE_OPENING)
|
||||
|
||||
|
||||
def is_closed(hass, ent):
|
||||
"""Return if the cover is closed based on the statemachine."""
|
||||
return hass.states.is_state(ent.entity_id, STATE_CLOSED)
|
||||
|
||||
|
||||
def is_closing(hass, ent):
|
||||
"""Return if the cover is closed based on the statemachine."""
|
||||
return hass.states.is_state(ent.entity_id, STATE_CLOSING)
|
||||
|
||||
|
||||
def test_deprecated_base_class(caplog):
|
||||
|
@ -14,6 +14,7 @@ from homeassistant.components.cover import (
|
||||
SUPPORT_STOP_TILT,
|
||||
CoverEntity,
|
||||
)
|
||||
from homeassistant.const import STATE_CLOSED, STATE_CLOSING, STATE_OPEN, STATE_OPENING
|
||||
|
||||
from tests.common import MockEntity
|
||||
|
||||
@ -32,27 +33,53 @@ def init(empty=False):
|
||||
name="Simple cover",
|
||||
is_on=True,
|
||||
unique_id="unique_cover",
|
||||
supports_tilt=False,
|
||||
supported_features=SUPPORT_OPEN | SUPPORT_CLOSE,
|
||||
),
|
||||
MockCover(
|
||||
name="Set position cover",
|
||||
is_on=True,
|
||||
unique_id="unique_set_pos_cover",
|
||||
current_cover_position=50,
|
||||
supports_tilt=False,
|
||||
supported_features=SUPPORT_OPEN
|
||||
| SUPPORT_CLOSE
|
||||
| SUPPORT_STOP
|
||||
| SUPPORT_SET_POSITION,
|
||||
),
|
||||
MockCover(
|
||||
name="Simple tilt cover",
|
||||
is_on=True,
|
||||
unique_id="unique_tilt_cover",
|
||||
supported_features=SUPPORT_OPEN
|
||||
| SUPPORT_CLOSE
|
||||
| SUPPORT_OPEN_TILT
|
||||
| SUPPORT_CLOSE_TILT,
|
||||
),
|
||||
MockCover(
|
||||
name="Set tilt position cover",
|
||||
is_on=True,
|
||||
unique_id="unique_set_pos_tilt_cover",
|
||||
current_cover_tilt_position=50,
|
||||
supports_tilt=True,
|
||||
supported_features=SUPPORT_OPEN
|
||||
| SUPPORT_CLOSE
|
||||
| SUPPORT_OPEN_TILT
|
||||
| SUPPORT_CLOSE_TILT
|
||||
| SUPPORT_STOP_TILT
|
||||
| SUPPORT_SET_TILT_POSITION,
|
||||
),
|
||||
MockCover(
|
||||
name="Tilt cover",
|
||||
name="All functions cover",
|
||||
is_on=True,
|
||||
unique_id="unique_tilt_cover",
|
||||
supports_tilt=True,
|
||||
unique_id="unique_all_functions_cover",
|
||||
current_cover_position=50,
|
||||
current_cover_tilt_position=50,
|
||||
supported_features=SUPPORT_OPEN
|
||||
| SUPPORT_CLOSE
|
||||
| SUPPORT_STOP
|
||||
| SUPPORT_SET_POSITION
|
||||
| SUPPORT_OPEN_TILT
|
||||
| SUPPORT_CLOSE_TILT
|
||||
| SUPPORT_STOP_TILT
|
||||
| SUPPORT_SET_TILT_POSITION,
|
||||
),
|
||||
]
|
||||
)
|
||||
@ -71,8 +98,54 @@ class MockCover(MockEntity, CoverEntity):
|
||||
@property
|
||||
def is_closed(self):
|
||||
"""Return if the cover is closed or not."""
|
||||
if self.supported_features & SUPPORT_STOP:
|
||||
return self.current_cover_position == 0
|
||||
|
||||
if "state" in self._values:
|
||||
return self._values["state"] == STATE_CLOSED
|
||||
return False
|
||||
|
||||
@property
|
||||
def is_opening(self):
|
||||
"""Return if the cover is opening or not."""
|
||||
if self.supported_features & SUPPORT_STOP:
|
||||
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 & SUPPORT_STOP:
|
||||
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 & SUPPORT_STOP:
|
||||
self._values["state"] = STATE_OPENING
|
||||
else:
|
||||
self._values["state"] = STATE_OPEN
|
||||
|
||||
def close_cover(self, **kwargs) -> None:
|
||||
"""Close cover."""
|
||||
if self.supported_features & SUPPORT_STOP:
|
||||
self._values["state"] = STATE_CLOSING
|
||||
else:
|
||||
self._values["state"] = STATE_CLOSED
|
||||
|
||||
def stop_cover(self, **kwargs) -> None:
|
||||
"""Stop cover."""
|
||||
self._values["state"] = STATE_CLOSED if self.is_closed else STATE_OPEN
|
||||
|
||||
@property
|
||||
def state(self):
|
||||
"""Fake State."""
|
||||
return CoverEntity.state.fget(self)
|
||||
|
||||
@property
|
||||
def current_cover_position(self):
|
||||
"""Return current position of cover."""
|
||||
@ -82,26 +155,3 @@ class MockCover(MockEntity, CoverEntity):
|
||||
def current_cover_tilt_position(self):
|
||||
"""Return current position of cover tilt."""
|
||||
return self._handle("current_cover_tilt_position")
|
||||
|
||||
@property
|
||||
def supported_features(self):
|
||||
"""Flag supported features."""
|
||||
supported_features = SUPPORT_OPEN | SUPPORT_CLOSE | SUPPORT_STOP
|
||||
|
||||
if self._handle("supports_tilt"):
|
||||
supported_features |= (
|
||||
SUPPORT_OPEN_TILT | SUPPORT_CLOSE_TILT | SUPPORT_STOP_TILT
|
||||
)
|
||||
|
||||
if self.current_cover_position is not None:
|
||||
supported_features |= SUPPORT_SET_POSITION
|
||||
|
||||
if self.current_cover_tilt_position is not None:
|
||||
supported_features |= (
|
||||
SUPPORT_OPEN_TILT
|
||||
| SUPPORT_CLOSE_TILT
|
||||
| SUPPORT_STOP_TILT
|
||||
| SUPPORT_SET_TILT_POSITION
|
||||
)
|
||||
|
||||
return supported_features
|
||||
|
Loading…
x
Reference in New Issue
Block a user