Add additional characteristics to the statistics integration (#62631)

* Improve config checking, add device_class timestamp

* Improve warning message
This commit is contained in:
Thomas Dietrich 2022-05-04 15:57:56 +02:00 committed by GitHub
parent 1df99badcf
commit f2d6a06a6a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 90 additions and 2 deletions

View file

@ -64,8 +64,12 @@ STAT_CHANGE = "change"
STAT_CHANGE_SAMPLE = "change_sample"
STAT_CHANGE_SECOND = "change_second"
STAT_COUNT = "count"
STAT_COUNT_BINARY_ON = "count_on"
STAT_COUNT_BINARY_OFF = "count_off"
STAT_DATETIME_NEWEST = "datetime_newest"
STAT_DATETIME_OLDEST = "datetime_oldest"
STAT_DATETIME_VALUE_MAX = "datetime_value_max"
STAT_DATETIME_VALUE_MIN = "datetime_value_min"
STAT_DISTANCE_95P = "distance_95_percent_of_values"
STAT_DISTANCE_99P = "distance_99_percent_of_values"
STAT_DISTANCE_ABSOLUTE = "distance_absolute"
@ -99,6 +103,8 @@ STATS_NUMERIC_SUPPORT = {
STAT_COUNT,
STAT_DATETIME_NEWEST,
STAT_DATETIME_OLDEST,
STAT_DATETIME_VALUE_MAX,
STAT_DATETIME_VALUE_MIN,
STAT_DISTANCE_95P,
STAT_DISTANCE_99P,
STAT_DISTANCE_ABSOLUTE,
@ -118,18 +124,26 @@ STATS_BINARY_SUPPORT = {
STAT_AVERAGE_STEP,
STAT_AVERAGE_TIMELESS,
STAT_COUNT,
STAT_COUNT_BINARY_ON,
STAT_COUNT_BINARY_OFF,
STAT_DATETIME_NEWEST,
STAT_DATETIME_OLDEST,
STAT_MEAN,
}
STATS_NOT_A_NUMBER = {
STAT_DATETIME_NEWEST,
STAT_DATETIME_OLDEST,
STAT_DATETIME_VALUE_MAX,
STAT_DATETIME_VALUE_MIN,
STAT_QUANTILES,
}
STATS_DATETIME = {
STAT_DATETIME_NEWEST,
STAT_DATETIME_OLDEST,
STAT_DATETIME_VALUE_MAX,
STAT_DATETIME_VALUE_MIN,
}
# Statistics which retain the unit of the source entity
@ -351,7 +365,7 @@ class StatisticsSensor(SensorEntity):
except ValueError:
self.attributes[STAT_SOURCE_VALUE_VALID] = False
_LOGGER.error(
"%s: parsing error, expected number and received %s",
"%s: parsing error. Expected number or binary state, but received '%s'",
self.entity_id,
new_state.state,
)
@ -370,7 +384,11 @@ class StatisticsSensor(SensorEntity):
unit = base_unit
elif self._state_characteristic in STATS_NOT_A_NUMBER:
unit = None
elif self._state_characteristic == STAT_COUNT:
elif self._state_characteristic in (
STAT_COUNT,
STAT_COUNT_BINARY_ON,
STAT_COUNT_BINARY_OFF,
):
unit = None
elif self._state_characteristic == STAT_VARIANCE:
unit = base_unit + "²"
@ -614,6 +632,16 @@ class StatisticsSensor(SensorEntity):
return self.ages[0]
return None
def _stat_datetime_value_max(self) -> datetime | None:
if len(self.states) > 0:
return self.ages[self.states.index(max(self.states))]
return None
def _stat_datetime_value_min(self) -> datetime | None:
if len(self.states) > 0:
return self.ages[self.states.index(min(self.states))]
return None
def _stat_distance_95_percent_of_values(self) -> StateType:
if len(self.states) >= 2:
return 2 * 1.96 * cast(float, self._stat_standard_deviation())
@ -704,6 +732,18 @@ class StatisticsSensor(SensorEntity):
def _stat_binary_count(self) -> StateType:
return len(self.states)
def _stat_binary_count_on(self) -> StateType:
return self.states.count(True)
def _stat_binary_count_off(self) -> StateType:
return self.states.count(False)
def _stat_binary_datetime_newest(self) -> datetime | None:
return self._stat_datetime_newest()
def _stat_binary_datetime_oldest(self) -> datetime | None:
return self._stat_datetime_oldest()
def _stat_binary_mean(self) -> StateType:
if len(self.states) > 0:
return 100.0 / len(self.states) * self.states.count(True)

View file

@ -687,6 +687,22 @@ async def test_state_characteristics(hass: HomeAssistant):
"value_9": (start_datetime + timedelta(minutes=1)).isoformat(),
"unit": None,
},
{
"source_sensor_domain": "sensor",
"name": "datetime_value_max",
"value_0": STATE_UNKNOWN,
"value_1": (start_datetime + timedelta(minutes=9)).isoformat(),
"value_9": (start_datetime + timedelta(minutes=2)).isoformat(),
"unit": None,
},
{
"source_sensor_domain": "sensor",
"name": "datetime_value_min",
"value_0": STATE_UNKNOWN,
"value_1": (start_datetime + timedelta(minutes=9)).isoformat(),
"value_9": (start_datetime + timedelta(minutes=5)).isoformat(),
"unit": None,
},
{
"source_sensor_domain": "sensor",
"name": "distance_95_percent_of_values",
@ -811,6 +827,38 @@ async def test_state_characteristics(hass: HomeAssistant):
"value_9": len(VALUES_BINARY),
"unit": None,
},
{
"source_sensor_domain": "binary_sensor",
"name": "count_on",
"value_0": 0,
"value_1": 1,
"value_9": VALUES_BINARY.count("on"),
"unit": None,
},
{
"source_sensor_domain": "binary_sensor",
"name": "count_off",
"value_0": 0,
"value_1": 0,
"value_9": VALUES_BINARY.count("off"),
"unit": None,
},
{
"source_sensor_domain": "binary_sensor",
"name": "datetime_newest",
"value_0": STATE_UNKNOWN,
"value_1": (start_datetime + timedelta(minutes=9)).isoformat(),
"value_9": (start_datetime + timedelta(minutes=9)).isoformat(),
"unit": None,
},
{
"source_sensor_domain": "binary_sensor",
"name": "datetime_oldest",
"value_0": STATE_UNKNOWN,
"value_1": (start_datetime + timedelta(minutes=9)).isoformat(),
"value_9": (start_datetime + timedelta(minutes=1)).isoformat(),
"unit": None,
},
{
"source_sensor_domain": "binary_sensor",
"name": "mean",