Add support for unavailable to cover groups (#74053)
This commit is contained in:
parent
af71c250d5
commit
ae63cd8677
2 changed files with 49 additions and 32 deletions
|
@ -35,6 +35,8 @@ from homeassistant.const import (
|
|||
STATE_CLOSING,
|
||||
STATE_OPEN,
|
||||
STATE_OPENING,
|
||||
STATE_UNAVAILABLE,
|
||||
STATE_UNKNOWN,
|
||||
)
|
||||
from homeassistant.core import Event, HomeAssistant, State, callback
|
||||
from homeassistant.helpers import config_validation as cv, entity_registry as er
|
||||
|
@ -98,6 +100,7 @@ async def async_setup_entry(
|
|||
class CoverGroup(GroupEntity, CoverEntity):
|
||||
"""Representation of a CoverGroup."""
|
||||
|
||||
_attr_available: bool = False
|
||||
_attr_is_closed: bool | None = None
|
||||
_attr_is_opening: bool | None = False
|
||||
_attr_is_closing: bool | None = False
|
||||
|
@ -267,29 +270,38 @@ class CoverGroup(GroupEntity, CoverEntity):
|
|||
"""Update state and attributes."""
|
||||
self._attr_assumed_state = False
|
||||
|
||||
states = [
|
||||
state.state
|
||||
for entity_id in self._entities
|
||||
if (state := self.hass.states.get(entity_id)) is not None
|
||||
]
|
||||
|
||||
valid_state = any(
|
||||
state not in (STATE_UNKNOWN, STATE_UNAVAILABLE) for state in states
|
||||
)
|
||||
|
||||
# Set group as unavailable if all members are unavailable or missing
|
||||
self._attr_available = any(state != STATE_UNAVAILABLE for state in states)
|
||||
|
||||
self._attr_is_closed = True
|
||||
self._attr_is_closing = False
|
||||
self._attr_is_opening = False
|
||||
has_valid_state = False
|
||||
for entity_id in self._entities:
|
||||
if not (state := self.hass.states.get(entity_id)):
|
||||
continue
|
||||
if state.state == STATE_OPEN:
|
||||
self._attr_is_closed = False
|
||||
has_valid_state = True
|
||||
continue
|
||||
if state.state == STATE_CLOSED:
|
||||
has_valid_state = True
|
||||
continue
|
||||
if state.state == STATE_CLOSING:
|
||||
self._attr_is_closing = True
|
||||
has_valid_state = True
|
||||
continue
|
||||
if state.state == STATE_OPENING:
|
||||
self._attr_is_opening = True
|
||||
has_valid_state = True
|
||||
continue
|
||||
if not has_valid_state:
|
||||
if not valid_state:
|
||||
# Set as unknown if all members are unknown or unavailable
|
||||
self._attr_is_closed = None
|
||||
|
||||
position_covers = self._covers[KEY_POSITION]
|
||||
|
|
|
@ -109,32 +109,36 @@ async def test_state(hass, setup_comp):
|
|||
Otherwise, the group state is closed.
|
||||
"""
|
||||
state = hass.states.get(COVER_GROUP)
|
||||
# No entity has a valid state -> group state unknown
|
||||
assert state.state == STATE_UNKNOWN
|
||||
# No entity has a valid state -> group state unavailable
|
||||
assert state.state == STATE_UNAVAILABLE
|
||||
assert state.attributes[ATTR_FRIENDLY_NAME] == DEFAULT_NAME
|
||||
assert ATTR_ENTITY_ID not in state.attributes
|
||||
assert ATTR_ASSUMED_STATE not in state.attributes
|
||||
assert state.attributes[ATTR_SUPPORTED_FEATURES] == 0
|
||||
assert ATTR_CURRENT_POSITION not in state.attributes
|
||||
assert ATTR_CURRENT_TILT_POSITION not in state.attributes
|
||||
|
||||
# Test group members exposed as attribute
|
||||
hass.states.async_set(DEMO_COVER, STATE_UNKNOWN, {})
|
||||
await hass.async_block_till_done()
|
||||
state = hass.states.get(COVER_GROUP)
|
||||
assert state.attributes[ATTR_ENTITY_ID] == [
|
||||
DEMO_COVER,
|
||||
DEMO_COVER_POS,
|
||||
DEMO_COVER_TILT,
|
||||
DEMO_TILT,
|
||||
]
|
||||
assert ATTR_ASSUMED_STATE not in state.attributes
|
||||
assert state.attributes[ATTR_SUPPORTED_FEATURES] == 0
|
||||
assert ATTR_CURRENT_POSITION not in state.attributes
|
||||
assert ATTR_CURRENT_TILT_POSITION not in state.attributes
|
||||
|
||||
# The group state is unavailable if all group members are unavailable.
|
||||
hass.states.async_set(DEMO_COVER, STATE_UNAVAILABLE, {})
|
||||
hass.states.async_set(DEMO_COVER_POS, STATE_UNAVAILABLE, {})
|
||||
hass.states.async_set(DEMO_COVER_TILT, STATE_UNAVAILABLE, {})
|
||||
hass.states.async_set(DEMO_TILT, STATE_UNAVAILABLE, {})
|
||||
await hass.async_block_till_done()
|
||||
state = hass.states.get(COVER_GROUP)
|
||||
assert state.state == STATE_UNAVAILABLE
|
||||
|
||||
# The group state is unknown if all group members are unknown or unavailable.
|
||||
for state_1 in (STATE_UNAVAILABLE, STATE_UNKNOWN):
|
||||
for state_2 in (STATE_UNAVAILABLE, STATE_UNKNOWN):
|
||||
for state_3 in (STATE_UNAVAILABLE, STATE_UNKNOWN):
|
||||
hass.states.async_set(DEMO_COVER, state_1, {})
|
||||
hass.states.async_set(DEMO_COVER_POS, state_2, {})
|
||||
hass.states.async_set(DEMO_COVER_TILT, state_3, {})
|
||||
hass.states.async_set(DEMO_TILT, STATE_UNAVAILABLE, {})
|
||||
await hass.async_block_till_done()
|
||||
state = hass.states.get(COVER_GROUP)
|
||||
assert state.state == STATE_UNKNOWN
|
||||
|
||||
for state_1 in (STATE_UNAVAILABLE, STATE_UNKNOWN):
|
||||
for state_2 in (STATE_UNAVAILABLE, STATE_UNKNOWN):
|
||||
for state_3 in (STATE_UNAVAILABLE, STATE_UNKNOWN):
|
||||
|
@ -233,28 +237,23 @@ async def test_state(hass, setup_comp):
|
|||
state = hass.states.get(COVER_GROUP)
|
||||
assert state.state == STATE_CLOSED
|
||||
|
||||
# All group members removed from the state machine -> unknown
|
||||
# All group members removed from the state machine -> unavailable
|
||||
hass.states.async_remove(DEMO_COVER)
|
||||
hass.states.async_remove(DEMO_COVER_POS)
|
||||
hass.states.async_remove(DEMO_COVER_TILT)
|
||||
hass.states.async_remove(DEMO_TILT)
|
||||
await hass.async_block_till_done()
|
||||
state = hass.states.get(COVER_GROUP)
|
||||
assert state.state == STATE_UNKNOWN
|
||||
assert state.state == STATE_UNAVAILABLE
|
||||
|
||||
|
||||
@pytest.mark.parametrize("config_count", [(CONFIG_ATTRIBUTES, 1)])
|
||||
async def test_attributes(hass, setup_comp):
|
||||
"""Test handling of state attributes."""
|
||||
state = hass.states.get(COVER_GROUP)
|
||||
assert state.state == STATE_UNKNOWN
|
||||
assert state.state == STATE_UNAVAILABLE
|
||||
assert state.attributes[ATTR_FRIENDLY_NAME] == DEFAULT_NAME
|
||||
assert state.attributes[ATTR_ENTITY_ID] == [
|
||||
DEMO_COVER,
|
||||
DEMO_COVER_POS,
|
||||
DEMO_COVER_TILT,
|
||||
DEMO_TILT,
|
||||
]
|
||||
assert ATTR_ENTITY_ID not in state.attributes
|
||||
assert ATTR_ASSUMED_STATE not in state.attributes
|
||||
assert state.attributes[ATTR_SUPPORTED_FEATURES] == 0
|
||||
assert ATTR_CURRENT_POSITION not in state.attributes
|
||||
|
@ -266,6 +265,12 @@ async def test_attributes(hass, setup_comp):
|
|||
|
||||
state = hass.states.get(COVER_GROUP)
|
||||
assert state.state == STATE_CLOSED
|
||||
assert state.attributes[ATTR_ENTITY_ID] == [
|
||||
DEMO_COVER,
|
||||
DEMO_COVER_POS,
|
||||
DEMO_COVER_TILT,
|
||||
DEMO_TILT,
|
||||
]
|
||||
|
||||
# Set entity as opening
|
||||
hass.states.async_set(DEMO_COVER, STATE_OPENING, {})
|
||||
|
|
Loading…
Add table
Reference in a new issue