Modify behavior of media_player groups (#74056)

This commit is contained in:
Erik Montnemery 2022-06-28 10:22:46 +02:00 committed by GitHub
parent c19a8ef8e0
commit 9a613aeb96
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 42 additions and 26 deletions

View file

@ -103,6 +103,8 @@ async def async_setup_entry(
class MediaPlayerGroup(MediaPlayerEntity): class MediaPlayerGroup(MediaPlayerEntity):
"""Representation of a Media Group.""" """Representation of a Media Group."""
_attr_available: bool = False
def __init__(self, unique_id: str | None, name: str, entities: list[str]) -> None: def __init__(self, unique_id: str | None, name: str, entities: list[str]) -> None:
"""Initialize a Media Group entity.""" """Initialize a Media Group entity."""
self._name = name self._name = name
@ -390,19 +392,29 @@ class MediaPlayerGroup(MediaPlayerEntity):
@callback @callback
def async_update_state(self) -> None: def async_update_state(self) -> None:
"""Query all members and determine the media group state.""" """Query all members and determine the media group state."""
states = [self.hass.states.get(entity) for entity in self._entities] states = [
states_values = [state.state for state in states if state is not None] state.state
off_values = STATE_OFF, STATE_UNAVAILABLE, STATE_UNKNOWN for entity_id in self._entities
if (state := self.hass.states.get(entity_id)) is not None
]
if states_values: # Set group as unavailable if all members are unavailable or missing
if states_values.count(states_values[0]) == len(states_values): self._attr_available = any(state != STATE_UNAVAILABLE for state in states)
self._state = states_values[0]
elif any(state for state in states_values if state not in off_values): valid_state = any(
state not in (STATE_UNKNOWN, STATE_UNAVAILABLE) for state in states
)
if not valid_state:
# Set as unknown if all members are unknown or unavailable
self._state = None
else:
off_values = (STATE_OFF, STATE_UNAVAILABLE, STATE_UNKNOWN)
if states.count(states[0]) == len(states):
self._state = states[0]
elif any(state for state in states if state not in off_values):
self._state = STATE_ON self._state = STATE_ON
else: else:
self._state = STATE_OFF self._state = STATE_OFF
else:
self._state = None
supported_features = 0 supported_features = 0
if self._features[KEY_CLEAR_PLAYLIST]: if self._features[KEY_CLEAR_PLAYLIST]:

View file

@ -126,8 +126,24 @@ async def test_state_reporting(hass):
await hass.async_start() await hass.async_start()
await hass.async_block_till_done() await hass.async_block_till_done()
# Initial state with no group member in the state machine -> unknown # Initial state with no group member in the state machine -> unavailable
assert hass.states.get("media_player.media_group").state == STATE_UNKNOWN assert hass.states.get("media_player.media_group").state == STATE_UNAVAILABLE
# All group members unavailable -> unavailable
hass.states.async_set("media_player.player_1", STATE_UNAVAILABLE)
hass.states.async_set("media_player.player_2", STATE_UNAVAILABLE)
await hass.async_block_till_done()
assert hass.states.get("media_player.media_group").state == STATE_UNAVAILABLE
# The group state is unknown if all group members are unknown or unavailable.
for state_1 in (
STATE_UNAVAILABLE,
STATE_UNKNOWN,
):
hass.states.async_set("media_player.player_1", state_1)
hass.states.async_set("media_player.player_2", STATE_UNKNOWN)
await hass.async_block_till_done()
assert hass.states.get("media_player.media_group").state == STATE_UNKNOWN
# All group members buffering -> buffering # All group members buffering -> buffering
# All group members idle -> idle # All group members idle -> idle
@ -156,30 +172,18 @@ async def test_state_reporting(hass):
await hass.async_block_till_done() await hass.async_block_till_done()
assert hass.states.get("media_player.media_group").state == STATE_ON assert hass.states.get("media_player.media_group").state == STATE_ON
# Otherwise off
for state_1 in (STATE_OFF, STATE_UNAVAILABLE, STATE_UNKNOWN): for state_1 in (STATE_OFF, STATE_UNAVAILABLE, STATE_UNKNOWN):
hass.states.async_set("media_player.player_1", state_1) hass.states.async_set("media_player.player_1", state_1)
hass.states.async_set("media_player.player_2", STATE_OFF) hass.states.async_set("media_player.player_2", STATE_OFF)
await hass.async_block_till_done() await hass.async_block_till_done()
assert hass.states.get("media_player.media_group").state == STATE_OFF assert hass.states.get("media_player.media_group").state == STATE_OFF
# Otherwise off # All group members removed from the state machine -> unavailable
for state_1 in (STATE_OFF, STATE_UNKNOWN):
hass.states.async_set("media_player.player_1", state_1)
hass.states.async_set("media_player.player_2", STATE_UNAVAILABLE)
await hass.async_block_till_done()
assert hass.states.get("media_player.media_group").state == STATE_OFF
for state_1 in (STATE_OFF, STATE_UNAVAILABLE):
hass.states.async_set("media_player.player_1", state_1)
hass.states.async_set("media_player.player_2", STATE_UNKNOWN)
await hass.async_block_till_done()
assert hass.states.get("media_player.media_group").state == STATE_OFF
# All group members removed from the state machine -> unknown
hass.states.async_remove("media_player.player_1") hass.states.async_remove("media_player.player_1")
hass.states.async_remove("media_player.player_2") hass.states.async_remove("media_player.player_2")
await hass.async_block_till_done() await hass.async_block_till_done()
assert hass.states.get("media_player.media_group").state == STATE_UNKNOWN assert hass.states.get("media_player.media_group").state == STATE_UNAVAILABLE
async def test_supported_features(hass): async def test_supported_features(hass):