mirror of
https://github.com/Electric-Special/ha-core.git
synced 2026-03-21 02:03:27 +01:00
Add type option "first_available" to sensor group in group component (#155525)
This commit is contained in:
@@ -39,6 +39,7 @@ from .valve import async_create_preview_valve
|
||||
|
||||
_STATISTIC_MEASURES = [
|
||||
"last",
|
||||
"first_available",
|
||||
"max",
|
||||
"mean",
|
||||
"median",
|
||||
|
||||
@@ -68,6 +68,8 @@ ATTR_MEAN = "mean"
|
||||
ATTR_MEDIAN = "median"
|
||||
ATTR_LAST = "last"
|
||||
ATTR_LAST_ENTITY_ID = "last_entity_id"
|
||||
ATTR_FIRST_AVAILABLE = "first_available"
|
||||
ATTR_FIRST_AVAILABLE_ENTITY_ID = "first_available_entity_id"
|
||||
ATTR_RANGE = "range"
|
||||
ATTR_STDEV = "stdev"
|
||||
ATTR_SUM = "sum"
|
||||
@@ -78,6 +80,7 @@ SENSOR_TYPES = {
|
||||
ATTR_MEAN: "mean",
|
||||
ATTR_MEDIAN: "median",
|
||||
ATTR_LAST: "last",
|
||||
ATTR_FIRST_AVAILABLE: "first_available",
|
||||
ATTR_RANGE: "range",
|
||||
ATTR_STDEV: "stdev",
|
||||
ATTR_SUM: "sum",
|
||||
@@ -255,6 +258,19 @@ def calc_last(
|
||||
return attributes, last
|
||||
|
||||
|
||||
def calc_first_available(
|
||||
sensor_values: list[tuple[str, float, State]],
|
||||
) -> tuple[dict[str, str | None], float | None]:
|
||||
"""Calculate first available value."""
|
||||
first_available_entity_id: str | None = None
|
||||
first_available: float | None = None
|
||||
if sensor_values:
|
||||
first_available_entity_id, first_available, _ = sensor_values[0]
|
||||
|
||||
attributes = {ATTR_FIRST_AVAILABLE_ENTITY_ID: first_available_entity_id}
|
||||
return attributes, first_available
|
||||
|
||||
|
||||
def calc_range(
|
||||
sensor_values: list[tuple[str, float, State]],
|
||||
) -> tuple[dict[str, str | None], float]:
|
||||
@@ -309,6 +325,7 @@ CALC_TYPES: dict[
|
||||
"mean": calc_mean,
|
||||
"median": calc_median,
|
||||
"last": calc_last,
|
||||
"first_available": calc_first_available,
|
||||
"range": calc_range,
|
||||
"stdev": calc_stdev,
|
||||
"sum": calc_sum,
|
||||
|
||||
@@ -280,6 +280,7 @@
|
||||
"selector": {
|
||||
"type": {
|
||||
"options": {
|
||||
"first_available": "First available",
|
||||
"last": "Most recently updated",
|
||||
"max": "Maximum",
|
||||
"mean": "Arithmetic mean",
|
||||
|
||||
@@ -12,6 +12,7 @@ import pytest
|
||||
from homeassistant import config as hass_config
|
||||
from homeassistant.components.group import DOMAIN
|
||||
from homeassistant.components.group.sensor import (
|
||||
ATTR_FIRST_AVAILABLE_ENTITY_ID,
|
||||
ATTR_LAST_ENTITY_ID,
|
||||
ATTR_MAX_ENTITY_ID,
|
||||
ATTR_MIN_ENTITY_ID,
|
||||
@@ -86,6 +87,11 @@ def set_or_remove_state(
|
||||
("mean", MEAN, {}),
|
||||
("median", MEDIAN, {}),
|
||||
("last", VALUES[2], {ATTR_LAST_ENTITY_ID: "sensor.test_3"}),
|
||||
(
|
||||
"first_available",
|
||||
VALUES[0],
|
||||
{ATTR_FIRST_AVAILABLE_ENTITY_ID: "sensor.test_1"},
|
||||
),
|
||||
("range", RANGE, {}),
|
||||
("stdev", STDEV, {}),
|
||||
("sum", SUM_VALUE, {}),
|
||||
@@ -861,6 +867,62 @@ async def test_last_sensor(hass: HomeAssistant) -> None:
|
||||
assert state.attributes.get("last_entity_id") == entity_id
|
||||
|
||||
|
||||
async def test_first_available_sensor(hass: HomeAssistant) -> None:
|
||||
"""Test the first available sensor."""
|
||||
config = {
|
||||
SENSOR_DOMAIN: {
|
||||
"platform": DOMAIN,
|
||||
"name": "test_first_available",
|
||||
"type": "first_available",
|
||||
"entities": ["sensor.test_1", "sensor.test_2", "sensor.test_3"],
|
||||
"unique_id": "very_unique_id_first_available_sensor",
|
||||
"ignore_non_numeric": True,
|
||||
}
|
||||
}
|
||||
|
||||
assert await async_setup_component(hass, "sensor", config)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
entity_ids = config["sensor"]["entities"]
|
||||
|
||||
# Ensure that while sensor states are being set
|
||||
# the group will always point to the first available sensor.
|
||||
|
||||
for entity_id, value in dict(zip(entity_ids, VALUES, strict=False)).items():
|
||||
hass.states.async_set(entity_id, value)
|
||||
await hass.async_block_till_done()
|
||||
state = hass.states.get("sensor.test_first_available")
|
||||
assert str(float(VALUES[0])) == state.state
|
||||
assert entity_ids[0] == state.attributes.get("first_available_entity_id")
|
||||
|
||||
# If the second sensor of the group becomes unavailable
|
||||
# then the first one should still be taken.
|
||||
|
||||
hass.states.async_set(entity_ids[1], STATE_UNAVAILABLE)
|
||||
await hass.async_block_till_done()
|
||||
state = hass.states.get("sensor.test_first_available")
|
||||
assert str(float(VALUES[0])) == state.state
|
||||
assert entity_ids[0] == state.attributes.get("first_available_entity_id")
|
||||
|
||||
# If the first sensor of the group becomes now unavailable
|
||||
# then the third one should be taken.
|
||||
|
||||
hass.states.async_set(entity_ids[0], STATE_UNAVAILABLE)
|
||||
await hass.async_block_till_done()
|
||||
state = hass.states.get("sensor.test_first_available")
|
||||
assert str(float(VALUES[2])) == state.state
|
||||
assert entity_ids[2] == state.attributes.get("first_available_entity_id")
|
||||
|
||||
# If all sensors of the group become unavailable
|
||||
# then the group should also be unavailable.
|
||||
|
||||
hass.states.async_set(entity_ids[2], STATE_UNAVAILABLE)
|
||||
await hass.async_block_till_done()
|
||||
state = hass.states.get("sensor.test_first_available")
|
||||
assert state.state == STATE_UNAVAILABLE
|
||||
assert state.attributes.get("first_available_entity_id") is None
|
||||
|
||||
|
||||
async def test_sensors_attributes_added_when_entity_info_available(
|
||||
hass: HomeAssistant,
|
||||
) -> None:
|
||||
|
||||
Reference in New Issue
Block a user