mirror of
https://github.com/home-assistant/core.git
synced 2025-07-25 22:27:07 +00:00
Create stats for all sensors that have % unit and are measurement (#53576)
This commit is contained in:
parent
3488053648
commit
f71980a634
@ -23,6 +23,7 @@ from homeassistant.const import (
|
|||||||
DEVICE_CLASS_POWER,
|
DEVICE_CLASS_POWER,
|
||||||
ENERGY_KILO_WATT_HOUR,
|
ENERGY_KILO_WATT_HOUR,
|
||||||
ENERGY_WATT_HOUR,
|
ENERGY_WATT_HOUR,
|
||||||
|
PERCENTAGE,
|
||||||
POWER_KILO_WATT,
|
POWER_KILO_WATT,
|
||||||
POWER_WATT,
|
POWER_WATT,
|
||||||
PRESSURE_BAR,
|
PRESSURE_BAR,
|
||||||
@ -44,7 +45,7 @@ from . import ATTR_LAST_RESET, DOMAIN
|
|||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
DEVICE_CLASS_STATISTICS = {
|
DEVICE_CLASS_OR_UNIT_STATISTICS = {
|
||||||
DEVICE_CLASS_BATTERY: {"mean", "min", "max"},
|
DEVICE_CLASS_BATTERY: {"mean", "min", "max"},
|
||||||
DEVICE_CLASS_ENERGY: {"sum"},
|
DEVICE_CLASS_ENERGY: {"sum"},
|
||||||
DEVICE_CLASS_HUMIDITY: {"mean", "min", "max"},
|
DEVICE_CLASS_HUMIDITY: {"mean", "min", "max"},
|
||||||
@ -52,6 +53,7 @@ DEVICE_CLASS_STATISTICS = {
|
|||||||
DEVICE_CLASS_POWER: {"mean", "min", "max"},
|
DEVICE_CLASS_POWER: {"mean", "min", "max"},
|
||||||
DEVICE_CLASS_PRESSURE: {"mean", "min", "max"},
|
DEVICE_CLASS_PRESSURE: {"mean", "min", "max"},
|
||||||
DEVICE_CLASS_TEMPERATURE: {"mean", "min", "max"},
|
DEVICE_CLASS_TEMPERATURE: {"mean", "min", "max"},
|
||||||
|
PERCENTAGE: {"mean", "min", "max"},
|
||||||
}
|
}
|
||||||
|
|
||||||
# Normalized units which will be stored in the statistics table
|
# Normalized units which will be stored in the statistics table
|
||||||
@ -102,13 +104,19 @@ def _get_entities(hass: HomeAssistant) -> list[tuple[str, str]]:
|
|||||||
entity_ids = []
|
entity_ids = []
|
||||||
|
|
||||||
for state in all_sensors:
|
for state in all_sensors:
|
||||||
device_class = state.attributes.get(ATTR_DEVICE_CLASS)
|
if state.attributes.get(ATTR_STATE_CLASS) != STATE_CLASS_MEASUREMENT:
|
||||||
state_class = state.attributes.get(ATTR_STATE_CLASS)
|
|
||||||
if not state_class or state_class != STATE_CLASS_MEASUREMENT:
|
|
||||||
continue
|
continue
|
||||||
if not device_class or device_class not in DEVICE_CLASS_STATISTICS:
|
|
||||||
continue
|
if (
|
||||||
entity_ids.append((state.entity_id, device_class))
|
key := state.attributes.get(ATTR_DEVICE_CLASS)
|
||||||
|
) in DEVICE_CLASS_OR_UNIT_STATISTICS:
|
||||||
|
entity_ids.append((state.entity_id, key))
|
||||||
|
|
||||||
|
if (
|
||||||
|
key := state.attributes.get(ATTR_UNIT_OF_MEASUREMENT)
|
||||||
|
) in DEVICE_CLASS_OR_UNIT_STATISTICS:
|
||||||
|
entity_ids.append((state.entity_id, key))
|
||||||
|
|
||||||
return entity_ids
|
return entity_ids
|
||||||
|
|
||||||
|
|
||||||
@ -158,12 +166,12 @@ def _time_weighted_average(
|
|||||||
|
|
||||||
|
|
||||||
def _normalize_states(
|
def _normalize_states(
|
||||||
entity_history: list[State], device_class: str, entity_id: str
|
entity_history: list[State], key: str, entity_id: str
|
||||||
) -> tuple[str | None, list[tuple[float, State]]]:
|
) -> tuple[str | None, list[tuple[float, State]]]:
|
||||||
"""Normalize units."""
|
"""Normalize units."""
|
||||||
unit = None
|
unit = None
|
||||||
|
|
||||||
if device_class not in UNIT_CONVERSIONS:
|
if key not in UNIT_CONVERSIONS:
|
||||||
# We're not normalizing this device class, return the state as they are
|
# We're not normalizing this device class, return the state as they are
|
||||||
fstates = [
|
fstates = [
|
||||||
(float(el.state), el) for el in entity_history if _is_number(el.state)
|
(float(el.state), el) for el in entity_history if _is_number(el.state)
|
||||||
@ -182,15 +190,15 @@ def _normalize_states(
|
|||||||
fstate = float(state.state)
|
fstate = float(state.state)
|
||||||
unit = state.attributes.get(ATTR_UNIT_OF_MEASUREMENT)
|
unit = state.attributes.get(ATTR_UNIT_OF_MEASUREMENT)
|
||||||
# Exclude unsupported units from statistics
|
# Exclude unsupported units from statistics
|
||||||
if unit not in UNIT_CONVERSIONS[device_class]:
|
if unit not in UNIT_CONVERSIONS[key]:
|
||||||
if entity_id not in WARN_UNSUPPORTED_UNIT:
|
if entity_id not in WARN_UNSUPPORTED_UNIT:
|
||||||
WARN_UNSUPPORTED_UNIT.add(entity_id)
|
WARN_UNSUPPORTED_UNIT.add(entity_id)
|
||||||
_LOGGER.warning("%s has unknown unit %s", entity_id, unit)
|
_LOGGER.warning("%s has unknown unit %s", entity_id, unit)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
fstates.append((UNIT_CONVERSIONS[device_class][unit](fstate), state))
|
fstates.append((UNIT_CONVERSIONS[key][unit](fstate), state))
|
||||||
|
|
||||||
return DEVICE_CLASS_UNITS[device_class], fstates
|
return DEVICE_CLASS_UNITS[key], fstates
|
||||||
|
|
||||||
|
|
||||||
def compile_statistics(
|
def compile_statistics(
|
||||||
@ -209,14 +217,14 @@ def compile_statistics(
|
|||||||
hass, start - datetime.timedelta.resolution, end, [i[0] for i in entities]
|
hass, start - datetime.timedelta.resolution, end, [i[0] for i in entities]
|
||||||
)
|
)
|
||||||
|
|
||||||
for entity_id, device_class in entities:
|
for entity_id, key in entities:
|
||||||
wanted_statistics = DEVICE_CLASS_STATISTICS[device_class]
|
wanted_statistics = DEVICE_CLASS_OR_UNIT_STATISTICS[key]
|
||||||
|
|
||||||
if entity_id not in history_list:
|
if entity_id not in history_list:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
entity_history = history_list[entity_id]
|
entity_history = history_list[entity_id]
|
||||||
unit, fstates = _normalize_states(entity_history, device_class, entity_id)
|
unit, fstates = _normalize_states(entity_history, key, entity_id)
|
||||||
|
|
||||||
if not fstates:
|
if not fstates:
|
||||||
continue
|
continue
|
||||||
@ -288,8 +296,8 @@ def list_statistic_ids(hass: HomeAssistant, statistic_type: str | None = None) -
|
|||||||
|
|
||||||
statistic_ids = {}
|
statistic_ids = {}
|
||||||
|
|
||||||
for entity_id, device_class in entities:
|
for entity_id, key in entities:
|
||||||
provided_statistics = DEVICE_CLASS_STATISTICS[device_class]
|
provided_statistics = DEVICE_CLASS_OR_UNIT_STATISTICS[key]
|
||||||
|
|
||||||
if statistic_type is not None and statistic_type not in provided_statistics:
|
if statistic_type is not None and statistic_type not in provided_statistics:
|
||||||
continue
|
continue
|
||||||
@ -302,14 +310,14 @@ def list_statistic_ids(hass: HomeAssistant, statistic_type: str | None = None) -
|
|||||||
|
|
||||||
native_unit = state.attributes.get(ATTR_UNIT_OF_MEASUREMENT)
|
native_unit = state.attributes.get(ATTR_UNIT_OF_MEASUREMENT)
|
||||||
|
|
||||||
if device_class not in UNIT_CONVERSIONS:
|
if key not in UNIT_CONVERSIONS:
|
||||||
statistic_ids[entity_id] = native_unit
|
statistic_ids[entity_id] = native_unit
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if native_unit not in UNIT_CONVERSIONS[device_class]:
|
if native_unit not in UNIT_CONVERSIONS[key]:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
statistics_unit = DEVICE_CLASS_UNITS[device_class]
|
statistics_unit = DEVICE_CLASS_UNITS[key]
|
||||||
statistic_ids[entity_id] = statistics_unit
|
statistic_ids[entity_id] = statistics_unit
|
||||||
|
|
||||||
return statistic_ids
|
return statistic_ids
|
||||||
|
@ -44,6 +44,7 @@ TEMPERATURE_SENSOR_ATTRIBUTES = {
|
|||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"device_class,unit,native_unit,mean,min,max",
|
"device_class,unit,native_unit,mean,min,max",
|
||||||
[
|
[
|
||||||
|
(None, "%", "%", 16.440677, 10, 30),
|
||||||
("battery", "%", "%", 16.440677, 10, 30),
|
("battery", "%", "%", 16.440677, 10, 30),
|
||||||
("battery", None, None, 16.440677, 10, 30),
|
("battery", None, None, 16.440677, 10, 30),
|
||||||
("humidity", "%", "%", 16.440677, 10, 30),
|
("humidity", "%", "%", 16.440677, 10, 30),
|
||||||
|
Loading…
x
Reference in New Issue
Block a user