From c5dd5e18c0443b332dddfbbf4a8ff03374c5c070 Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Wed, 2 Mar 2022 16:53:13 +0100 Subject: [PATCH] Improve binary sensor group when member is unknown or unavailable (#67468) --- .../components/group/binary_sensor.py | 14 +++++++++-- tests/components/group/test_binary_sensor.py | 24 ++++++++++++++++--- 2 files changed, 33 insertions(+), 5 deletions(-) diff --git a/homeassistant/components/group/binary_sensor.py b/homeassistant/components/group/binary_sensor.py index 9c0301d97e6..de0c3d393ca 100644 --- a/homeassistant/components/group/binary_sensor.py +++ b/homeassistant/components/group/binary_sensor.py @@ -17,6 +17,7 @@ from homeassistant.const import ( CONF_UNIQUE_ID, STATE_ON, STATE_UNAVAILABLE, + STATE_UNKNOWN, ) from homeassistant.core import Event, HomeAssistant, callback import homeassistant.helpers.config_validation as cv @@ -80,7 +81,6 @@ class BinarySensorGroup(GroupEntity, BinarySensorEntity): self._attr_extra_state_attributes = {ATTR_ENTITY_ID: entity_ids} self._attr_unique_id = unique_id self._device_class = device_class - self._state: str | None = None self.mode = any if mode: self.mode = all @@ -106,13 +106,23 @@ class BinarySensorGroup(GroupEntity, BinarySensorEntity): def async_update_group_state(self) -> None: """Query all members and determine the binary sensor group state.""" all_states = [self.hass.states.get(x) for x in self._entity_ids] + + # filtered_states are members currently in the state machine filtered_states: list[str] = [x.state for x in all_states if x is not None] + + # Set group as unavailable if all members are unavailable self._attr_available = any( state != STATE_UNAVAILABLE for state in filtered_states ) - if STATE_UNAVAILABLE in filtered_states: + + valid_state = self.mode( + state not in (STATE_UNKNOWN, STATE_UNAVAILABLE) for state in filtered_states + ) + if not valid_state: + # Set as unknown if any / all member is not unknown or unavailable self._attr_is_on = None else: + # Set as ON if any / all member is ON states = list(map(lambda x: x == STATE_ON, filtered_states)) state = self.mode(states) self._attr_is_on = state diff --git a/tests/components/group/test_binary_sensor.py b/tests/components/group/test_binary_sensor.py index 0a85c793aaa..a0872b11f16 100644 --- a/tests/components/group/test_binary_sensor.py +++ b/tests/components/group/test_binary_sensor.py @@ -95,6 +95,16 @@ async def test_state_reporting_all(hass): hass.states.get("binary_sensor.binary_sensor_group").state == STATE_UNAVAILABLE ) + hass.states.async_set("binary_sensor.test1", STATE_ON) + hass.states.async_set("binary_sensor.test2", STATE_UNKNOWN) + await hass.async_block_till_done() + assert hass.states.get("binary_sensor.binary_sensor_group").state == STATE_UNKNOWN + + hass.states.async_set("binary_sensor.test1", STATE_UNKNOWN) + hass.states.async_set("binary_sensor.test2", STATE_UNKNOWN) + await hass.async_block_till_done() + assert hass.states.get("binary_sensor.binary_sensor_group").state == STATE_UNKNOWN + async def test_state_reporting_any(hass): """Test the state reporting.""" @@ -116,11 +126,10 @@ async def test_state_reporting_any(hass): await hass.async_start() await hass.async_block_till_done() - # binary sensors have state off if unavailable hass.states.async_set("binary_sensor.test1", STATE_ON) hass.states.async_set("binary_sensor.test2", STATE_UNAVAILABLE) await hass.async_block_till_done() - assert hass.states.get("binary_sensor.binary_sensor_group").state == STATE_UNKNOWN + assert hass.states.get("binary_sensor.binary_sensor_group").state == STATE_ON hass.states.async_set("binary_sensor.test1", STATE_ON) hass.states.async_set("binary_sensor.test2", STATE_OFF) @@ -137,7 +146,6 @@ async def test_state_reporting_any(hass): await hass.async_block_till_done() assert hass.states.get("binary_sensor.binary_sensor_group").state == STATE_ON - # binary sensors have state off if unavailable hass.states.async_set("binary_sensor.test1", STATE_UNAVAILABLE) hass.states.async_set("binary_sensor.test2", STATE_UNAVAILABLE) await hass.async_block_till_done() @@ -149,3 +157,13 @@ async def test_state_reporting_any(hass): entry = entity_registry.async_get("binary_sensor.binary_sensor_group") assert entry assert entry.unique_id == "unique_identifier" + + hass.states.async_set("binary_sensor.test1", STATE_ON) + hass.states.async_set("binary_sensor.test2", STATE_UNKNOWN) + await hass.async_block_till_done() + assert hass.states.get("binary_sensor.binary_sensor_group").state == STATE_ON + + hass.states.async_set("binary_sensor.test1", STATE_UNKNOWN) + hass.states.async_set("binary_sensor.test2", STATE_UNKNOWN) + await hass.async_block_till_done() + assert hass.states.get("binary_sensor.binary_sensor_group").state == STATE_UNKNOWN