diff --git a/homeassistant/components/group/media_player.py b/homeassistant/components/group/media_player.py index 46c019dbc7c..e0cbf84a693 100644 --- a/homeassistant/components/group/media_player.py +++ b/homeassistant/components/group/media_player.py @@ -103,6 +103,8 @@ async def async_setup_entry( class MediaPlayerGroup(MediaPlayerEntity): """Representation of a Media Group.""" + _attr_available: bool = False + def __init__(self, unique_id: str | None, name: str, entities: list[str]) -> None: """Initialize a Media Group entity.""" self._name = name @@ -390,19 +392,29 @@ class MediaPlayerGroup(MediaPlayerEntity): @callback def async_update_state(self) -> None: """Query all members and determine the media group state.""" - states = [self.hass.states.get(entity) for entity in self._entities] - states_values = [state.state for state in states if state is not None] - off_values = STATE_OFF, STATE_UNAVAILABLE, STATE_UNKNOWN + states = [ + state.state + for entity_id in self._entities + if (state := self.hass.states.get(entity_id)) is not None + ] - if states_values: - if states_values.count(states_values[0]) == len(states_values): - self._state = states_values[0] - elif any(state for state in states_values if state not in off_values): + # Set group as unavailable if all members are unavailable or missing + self._attr_available = any(state != STATE_UNAVAILABLE for state in states) + + 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 else: self._state = STATE_OFF - else: - self._state = None supported_features = 0 if self._features[KEY_CLEAR_PLAYLIST]: diff --git a/tests/components/group/test_media_player.py b/tests/components/group/test_media_player.py index 85e75ffcba6..5d0d52ae3ac 100644 --- a/tests/components/group/test_media_player.py +++ b/tests/components/group/test_media_player.py @@ -126,8 +126,24 @@ async def test_state_reporting(hass): await hass.async_start() await hass.async_block_till_done() - # Initial state with no group member in the state machine -> unknown - assert hass.states.get("media_player.media_group").state == STATE_UNKNOWN + # Initial state with no group member in the state machine -> unavailable + 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 idle -> idle @@ -156,30 +172,18 @@ async def test_state_reporting(hass): await hass.async_block_till_done() assert hass.states.get("media_player.media_group").state == STATE_ON + # Otherwise off 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_2", STATE_OFF) await hass.async_block_till_done() assert hass.states.get("media_player.media_group").state == STATE_OFF - # Otherwise off - 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 + # All group members removed from the state machine -> unavailable hass.states.async_remove("media_player.player_1") hass.states.async_remove("media_player.player_2") 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):