Skip to content

Commit

Permalink
Use last correct state of member energy sensor in group, so it won't …
Browse files Browse the repository at this point in the history
…become unavailable
  • Loading branch information
bramstroker committed Jan 21, 2023
1 parent 1c0686c commit e3277a2
Show file tree
Hide file tree
Showing 2 changed files with 63 additions and 17 deletions.
43 changes: 26 additions & 17 deletions custom_components/powercalc/sensors/group.py
Expand Up @@ -89,13 +89,13 @@ async def create_group_sensors(
def _get_filtered_entity_ids_by_class(
all_entities: list, default_filters: list[Callable], class_name
) -> list[str]:
filters = default_filters.copy()
filters.append(lambda elm: not isinstance(elm, GroupedSensor))
filters.append(lambda elm: isinstance(elm, class_name))
filter_list = default_filters.copy()
filter_list.append(lambda elm: not isinstance(elm, GroupedSensor))
filter_list.append(lambda elm: isinstance(elm, class_name))
return [
x.entity_id
for x in filter(
lambda x: all(f(x) for f in filters),
lambda x: all(f(x) for f in filter_list),
all_entities,
)
]
Expand Down Expand Up @@ -380,6 +380,7 @@ def __init__(
self.unit_converter: BaseUnitConverter | None = None
if hasattr(self, "get_unit_converter"):
self.unit_converter = self.get_unit_converter()
self.last_states: dict[str, State] = {}

async def async_added_to_hass(self) -> None:
"""Register state listeners."""
Expand Down Expand Up @@ -424,26 +425,32 @@ def on_state_change(self, event) -> None:

all_states = [self.hass.states.get(entity_id) for entity_id in self._entities]
states: list[State] = list(filter(None, all_states))
available_states = [
state
for state in states
if state and state.state not in [STATE_UNKNOWN, STATE_UNAVAILABLE]
]
unavailable_entities = [
state.entity_id
for state in states
if state and state.state == STATE_UNAVAILABLE
]
if unavailable_entities and isinstance(self, GroupedEnergySensor):
_LOGGER.warning(
"%s: One or more members of the group are unavailable, setting group to unavailable (%s)",
self.entity_id,
",".join(unavailable_entities),
)
self._attr_available = False
self.async_schedule_update_ha_state(True)
return
for entity_id in unavailable_entities:
last_state = self.last_states.get(entity_id)
if last_state:
available_states.append(last_state)
unavailable_entities.remove(entity_id)

available_states = [
state
for state in states
if state and state.state not in [STATE_UNKNOWN, STATE_UNAVAILABLE]
]
if unavailable_entities:
_LOGGER.warning(
"%s: One or more members of the group are unavailable, setting group to unavailable (%s)",
self.entity_id,
",".join(unavailable_entities),
)
self._attr_available = False
self.async_schedule_update_ha_state(True)
return

apply_unit_conversions = AwesomeVersion(HA_VERSION) >= AwesomeVersion(
"2022.10.0"
Expand Down Expand Up @@ -484,6 +491,8 @@ def _get_state_values(
value, unit_of_measurement, self._attr_native_unit_of_measurement
)
values.append(Decimal(value))

self.last_states[state.entity_id] = state
return values

def _remove_incompatible_unit_entities(
Expand Down
37 changes: 37 additions & 0 deletions tests/sensors/test_group.py
Expand Up @@ -332,6 +332,43 @@ async def test_group_unavailable_when_members_unavailable(hass: HomeAssistant):
assert energy_state.state == STATE_UNAVAILABLE


async def test_energy_group_available_when_members_temporarily_unavailable(hass: HomeAssistant) -> None:
"""
When any of the member sensors of a grouped energy sensor become unavailable,
we try to use the last know correct state value of the member sensor
"""
await create_input_booleans(hass, ["test1", "test2"])
await run_powercalc_setup(
hass,
{
CONF_CREATE_GROUP: "TestGroup",
CONF_ENTITIES: [
get_simple_fixed_config("input_boolean.test1", 50),
get_simple_fixed_config("input_boolean.test2", 50),
],
},
)

hass.states.async_set("sensor.test1_energy", "1.0")
hass.states.async_set("sensor.test2_energy", "2.0")
await hass.async_block_till_done()

energy_state = hass.states.get("sensor.testgroup_energy")
assert energy_state.state == "3.0000"

hass.states.async_set("sensor.test1_energy", STATE_UNAVAILABLE)
await hass.async_block_till_done()

energy_state = hass.states.get("sensor.testgroup_energy")
assert energy_state.state == "3.0000"

hass.states.async_set("sensor.test2_energy", "2.2")
await hass.async_block_till_done()

energy_state = hass.states.get("sensor.testgroup_energy")
assert energy_state.state == "3.2000"


async def test_hide_members(hass: HomeAssistant):
entity_reg = er.async_get(hass)
await create_input_booleans(hass, ["one", "two"])
Expand Down

0 comments on commit e3277a2

Please sign in to comment.