Remove STATE_CLASS_TOTAL and last_reset from sensor (#54755)
* Remove STATE_CLASS_TOTAL * Update mill sensor * Update tests * Kill last_reset * Return ATTR_LAST_RESET to utility_meter * Update energy cost sensor * Restore last_reset for backwards compatibility * Re-add and update deprecation warning * Update tests * Fix utility_meter * Update EnergyCostSensor * Tweak * Fix rebase mistake * Fix test
This commit is contained in:
parent
85d9890447
commit
e1926caeb9
12 changed files with 68 additions and 251 deletions
|
@ -6,9 +6,8 @@ import logging
|
||||||
from typing import Any, Final, Literal, TypeVar, cast
|
from typing import Any, Final, Literal, TypeVar, cast
|
||||||
|
|
||||||
from homeassistant.components.sensor import (
|
from homeassistant.components.sensor import (
|
||||||
ATTR_LAST_RESET,
|
|
||||||
DEVICE_CLASS_MONETARY,
|
DEVICE_CLASS_MONETARY,
|
||||||
STATE_CLASS_MEASUREMENT,
|
STATE_CLASS_TOTAL_INCREASING,
|
||||||
SensorEntity,
|
SensorEntity,
|
||||||
)
|
)
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
|
@ -17,11 +16,10 @@ from homeassistant.const import (
|
||||||
ENERGY_WATT_HOUR,
|
ENERGY_WATT_HOUR,
|
||||||
VOLUME_CUBIC_METERS,
|
VOLUME_CUBIC_METERS,
|
||||||
)
|
)
|
||||||
from homeassistant.core import HomeAssistant, State, callback, split_entity_id
|
from homeassistant.core import HomeAssistant, callback, split_entity_id
|
||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
from homeassistant.helpers.event import async_track_state_change_event
|
from homeassistant.helpers.event import async_track_state_change_event
|
||||||
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
|
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType, StateType
|
||||||
import homeassistant.util.dt as dt_util
|
|
||||||
|
|
||||||
from .const import DOMAIN
|
from .const import DOMAIN
|
||||||
from .data import EnergyManager, async_get_manager
|
from .data import EnergyManager, async_get_manager
|
||||||
|
@ -203,16 +201,15 @@ class EnergyCostSensor(SensorEntity):
|
||||||
f"{config[adapter.entity_energy_key]}_{adapter.entity_id_suffix}"
|
f"{config[adapter.entity_energy_key]}_{adapter.entity_id_suffix}"
|
||||||
)
|
)
|
||||||
self._attr_device_class = DEVICE_CLASS_MONETARY
|
self._attr_device_class = DEVICE_CLASS_MONETARY
|
||||||
self._attr_state_class = STATE_CLASS_MEASUREMENT
|
self._attr_state_class = STATE_CLASS_TOTAL_INCREASING
|
||||||
self._config = config
|
self._config = config
|
||||||
self._last_energy_sensor_state: State | None = None
|
self._last_energy_sensor_state: StateType | None = None
|
||||||
self._cur_value = 0.0
|
self._cur_value = 0.0
|
||||||
|
|
||||||
def _reset(self, energy_state: State) -> None:
|
def _reset(self, energy_state: StateType) -> None:
|
||||||
"""Reset the cost sensor."""
|
"""Reset the cost sensor."""
|
||||||
self._attr_native_value = 0.0
|
self._attr_native_value = 0.0
|
||||||
self._cur_value = 0.0
|
self._cur_value = 0.0
|
||||||
self._attr_last_reset = dt_util.utcnow()
|
|
||||||
self._last_energy_sensor_state = energy_state
|
self._last_energy_sensor_state = energy_state
|
||||||
self.async_write_ha_state()
|
self.async_write_ha_state()
|
||||||
|
|
||||||
|
@ -223,7 +220,7 @@ class EnergyCostSensor(SensorEntity):
|
||||||
cast(str, self._config[self._adapter.entity_energy_key])
|
cast(str, self._config[self._adapter.entity_energy_key])
|
||||||
)
|
)
|
||||||
|
|
||||||
if energy_state is None or ATTR_LAST_RESET not in energy_state.attributes:
|
if energy_state is None:
|
||||||
return
|
return
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@ -259,7 +256,7 @@ class EnergyCostSensor(SensorEntity):
|
||||||
|
|
||||||
if self._last_energy_sensor_state is None:
|
if self._last_energy_sensor_state is None:
|
||||||
# Initialize as it's the first time all required entities are in place.
|
# Initialize as it's the first time all required entities are in place.
|
||||||
self._reset(energy_state)
|
self._reset(energy_state.state)
|
||||||
return
|
return
|
||||||
|
|
||||||
energy_unit = energy_state.attributes.get(ATTR_UNIT_OF_MEASUREMENT)
|
energy_unit = energy_state.attributes.get(ATTR_UNIT_OF_MEASUREMENT)
|
||||||
|
@ -280,19 +277,15 @@ class EnergyCostSensor(SensorEntity):
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
if (
|
if energy < float(self._last_energy_sensor_state):
|
||||||
energy_state.attributes[ATTR_LAST_RESET]
|
|
||||||
!= self._last_energy_sensor_state.attributes[ATTR_LAST_RESET]
|
|
||||||
):
|
|
||||||
# Energy meter was reset, reset cost sensor too
|
# Energy meter was reset, reset cost sensor too
|
||||||
self._reset(energy_state)
|
self._reset(0)
|
||||||
else:
|
# Update with newly incurred cost
|
||||||
# Update with newly incurred cost
|
old_energy_value = float(self._last_energy_sensor_state)
|
||||||
old_energy_value = float(self._last_energy_sensor_state.state)
|
self._cur_value += (energy - old_energy_value) * energy_price
|
||||||
self._cur_value += (energy - old_energy_value) * energy_price
|
self._attr_native_value = round(self._cur_value, 2)
|
||||||
self._attr_native_value = round(self._cur_value, 2)
|
|
||||||
|
|
||||||
self._last_energy_sensor_state = energy_state
|
self._last_energy_sensor_state = energy_state.state
|
||||||
|
|
||||||
async def async_added_to_hass(self) -> None:
|
async def async_added_to_hass(self) -> None:
|
||||||
"""Register callbacks."""
|
"""Register callbacks."""
|
||||||
|
|
|
@ -2,11 +2,10 @@
|
||||||
|
|
||||||
from homeassistant.components.sensor import (
|
from homeassistant.components.sensor import (
|
||||||
DEVICE_CLASS_ENERGY,
|
DEVICE_CLASS_ENERGY,
|
||||||
STATE_CLASS_TOTAL,
|
STATE_CLASS_TOTAL_INCREASING,
|
||||||
SensorEntity,
|
SensorEntity,
|
||||||
)
|
)
|
||||||
from homeassistant.const import ENERGY_KILO_WATT_HOUR
|
from homeassistant.const import ENERGY_KILO_WATT_HOUR
|
||||||
from homeassistant.util import dt as dt_util
|
|
||||||
|
|
||||||
from .const import CONSUMPTION_TODAY, CONSUMPTION_YEAR, DOMAIN, MANUFACTURER
|
from .const import CONSUMPTION_TODAY, CONSUMPTION_YEAR, DOMAIN, MANUFACTURER
|
||||||
|
|
||||||
|
@ -29,7 +28,7 @@ class MillHeaterEnergySensor(SensorEntity):
|
||||||
|
|
||||||
_attr_device_class = DEVICE_CLASS_ENERGY
|
_attr_device_class = DEVICE_CLASS_ENERGY
|
||||||
_attr_native_unit_of_measurement = ENERGY_KILO_WATT_HOUR
|
_attr_native_unit_of_measurement = ENERGY_KILO_WATT_HOUR
|
||||||
_attr_state_class = STATE_CLASS_TOTAL
|
_attr_state_class = STATE_CLASS_TOTAL_INCREASING
|
||||||
|
|
||||||
def __init__(self, heater, mill_data_connection, sensor_type):
|
def __init__(self, heater, mill_data_connection, sensor_type):
|
||||||
"""Initialize the sensor."""
|
"""Initialize the sensor."""
|
||||||
|
@ -45,16 +44,6 @@ class MillHeaterEnergySensor(SensorEntity):
|
||||||
"manufacturer": MANUFACTURER,
|
"manufacturer": MANUFACTURER,
|
||||||
"model": f"generation {1 if heater.is_gen1 else 2}",
|
"model": f"generation {1 if heater.is_gen1 else 2}",
|
||||||
}
|
}
|
||||||
if self._sensor_type == CONSUMPTION_TODAY:
|
|
||||||
self._attr_last_reset = dt_util.as_utc(
|
|
||||||
dt_util.now().replace(hour=0, minute=0, second=0, microsecond=0)
|
|
||||||
)
|
|
||||||
elif self._sensor_type == CONSUMPTION_YEAR:
|
|
||||||
self._attr_last_reset = dt_util.as_utc(
|
|
||||||
dt_util.now().replace(
|
|
||||||
month=1, day=1, hour=0, minute=0, second=0, microsecond=0
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
async def async_update(self):
|
async def async_update(self):
|
||||||
"""Retrieve latest state."""
|
"""Retrieve latest state."""
|
||||||
|
@ -71,15 +60,4 @@ class MillHeaterEnergySensor(SensorEntity):
|
||||||
self._attr_native_value = _state
|
self._attr_native_value = _state
|
||||||
return
|
return
|
||||||
|
|
||||||
if self.state is not None and _state < self.state:
|
|
||||||
if self._sensor_type == CONSUMPTION_TODAY:
|
|
||||||
self._attr_last_reset = dt_util.as_utc(
|
|
||||||
dt_util.now().replace(hour=0, minute=0, second=0, microsecond=0)
|
|
||||||
)
|
|
||||||
elif self._sensor_type == CONSUMPTION_YEAR:
|
|
||||||
self._attr_last_reset = dt_util.as_utc(
|
|
||||||
dt_util.now().replace(
|
|
||||||
month=1, day=1, hour=0, minute=0, second=0, microsecond=0
|
|
||||||
)
|
|
||||||
)
|
|
||||||
self._attr_native_value = _state
|
self._attr_native_value = _state
|
||||||
|
|
|
@ -218,7 +218,6 @@ class StatisticData(TypedDict, total=False):
|
||||||
mean: float
|
mean: float
|
||||||
min: float
|
min: float
|
||||||
max: float
|
max: float
|
||||||
last_reset: datetime | None
|
|
||||||
state: float
|
state: float
|
||||||
sum: float
|
sum: float
|
||||||
|
|
||||||
|
@ -242,7 +241,6 @@ class Statistics(Base): # type: ignore
|
||||||
mean = Column(Float())
|
mean = Column(Float())
|
||||||
min = Column(Float())
|
min = Column(Float())
|
||||||
max = Column(Float())
|
max = Column(Float())
|
||||||
last_reset = Column(DATETIME_TYPE)
|
|
||||||
state = Column(Float())
|
state = Column(Float())
|
||||||
sum = Column(Float())
|
sum = Column(Float())
|
||||||
|
|
||||||
|
|
|
@ -43,7 +43,6 @@ QUERY_STATISTICS = [
|
||||||
Statistics.mean,
|
Statistics.mean,
|
||||||
Statistics.min,
|
Statistics.min,
|
||||||
Statistics.max,
|
Statistics.max,
|
||||||
Statistics.last_reset,
|
|
||||||
Statistics.state,
|
Statistics.state,
|
||||||
Statistics.sum,
|
Statistics.sum,
|
||||||
]
|
]
|
||||||
|
@ -375,7 +374,6 @@ def _sorted_statistics_to_dict(
|
||||||
"mean": convert(db_state.mean, units),
|
"mean": convert(db_state.mean, units),
|
||||||
"min": convert(db_state.min, units),
|
"min": convert(db_state.min, units),
|
||||||
"max": convert(db_state.max, units),
|
"max": convert(db_state.max, units),
|
||||||
"last_reset": _process_timestamp_to_utc_isoformat(db_state.last_reset),
|
|
||||||
"state": convert(db_state.state, units),
|
"state": convert(db_state.state, units),
|
||||||
"sum": convert(db_state.sum, units),
|
"sum": convert(db_state.sum, units),
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,7 +51,7 @@ from homeassistant.helpers.typing import ConfigType, StateType
|
||||||
|
|
||||||
_LOGGER: Final = logging.getLogger(__name__)
|
_LOGGER: Final = logging.getLogger(__name__)
|
||||||
|
|
||||||
ATTR_LAST_RESET: Final = "last_reset"
|
ATTR_LAST_RESET: Final = "last_reset" # Deprecated, to be removed in 2021.11
|
||||||
ATTR_STATE_CLASS: Final = "state_class"
|
ATTR_STATE_CLASS: Final = "state_class"
|
||||||
|
|
||||||
DOMAIN: Final = "sensor"
|
DOMAIN: Final = "sensor"
|
||||||
|
@ -91,14 +91,11 @@ DEVICE_CLASSES_SCHEMA: Final = vol.All(vol.Lower, vol.In(DEVICE_CLASSES))
|
||||||
|
|
||||||
# The state represents a measurement in present time
|
# The state represents a measurement in present time
|
||||||
STATE_CLASS_MEASUREMENT: Final = "measurement"
|
STATE_CLASS_MEASUREMENT: Final = "measurement"
|
||||||
# The state represents a total amount, e.g. a value of a stock portfolio
|
|
||||||
STATE_CLASS_TOTAL: Final = "total"
|
|
||||||
# The state represents a monotonically increasing total, e.g. an amount of consumed gas
|
# The state represents a monotonically increasing total, e.g. an amount of consumed gas
|
||||||
STATE_CLASS_TOTAL_INCREASING: Final = "total_increasing"
|
STATE_CLASS_TOTAL_INCREASING: Final = "total_increasing"
|
||||||
|
|
||||||
STATE_CLASSES: Final[list[str]] = [
|
STATE_CLASSES: Final[list[str]] = [
|
||||||
STATE_CLASS_MEASUREMENT,
|
STATE_CLASS_MEASUREMENT,
|
||||||
STATE_CLASS_TOTAL,
|
|
||||||
STATE_CLASS_TOTAL_INCREASING,
|
STATE_CLASS_TOTAL_INCREASING,
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -132,7 +129,7 @@ class SensorEntityDescription(EntityDescription):
|
||||||
"""A class that describes sensor entities."""
|
"""A class that describes sensor entities."""
|
||||||
|
|
||||||
state_class: str | None = None
|
state_class: str | None = None
|
||||||
last_reset: datetime | None = None
|
last_reset: datetime | None = None # Deprecated, to be removed in 2021.11
|
||||||
native_unit_of_measurement: str | None = None
|
native_unit_of_measurement: str | None = None
|
||||||
|
|
||||||
|
|
||||||
|
@ -140,7 +137,7 @@ class SensorEntity(Entity):
|
||||||
"""Base class for sensor entities."""
|
"""Base class for sensor entities."""
|
||||||
|
|
||||||
entity_description: SensorEntityDescription
|
entity_description: SensorEntityDescription
|
||||||
_attr_last_reset: datetime | None
|
_attr_last_reset: datetime | None # Deprecated, to be removed in 2021.11
|
||||||
_attr_native_unit_of_measurement: str | None
|
_attr_native_unit_of_measurement: str | None
|
||||||
_attr_native_value: StateType = None
|
_attr_native_value: StateType = None
|
||||||
_attr_state_class: str | None
|
_attr_state_class: str | None
|
||||||
|
@ -157,7 +154,7 @@ class SensorEntity(Entity):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def last_reset(self) -> datetime | None:
|
def last_reset(self) -> datetime | None: # Deprecated, to be removed in 2021.11
|
||||||
"""Return the time when the sensor was last reset, if any."""
|
"""Return the time when the sensor was last reset, if any."""
|
||||||
if hasattr(self, "_attr_last_reset"):
|
if hasattr(self, "_attr_last_reset"):
|
||||||
return self._attr_last_reset
|
return self._attr_last_reset
|
||||||
|
@ -187,10 +184,9 @@ class SensorEntity(Entity):
|
||||||
report_issue = self._suggest_report_issue()
|
report_issue = self._suggest_report_issue()
|
||||||
_LOGGER.warning(
|
_LOGGER.warning(
|
||||||
"Entity %s (%s) with state_class %s has set last_reset. Setting "
|
"Entity %s (%s) with state_class %s has set last_reset. Setting "
|
||||||
"last_reset for entities with state_class other than 'total' is "
|
"last_reset is deprecated and will be unsupported from Home "
|
||||||
"deprecated and will be removed from Home Assistant Core 2021.10. "
|
"Assistant Core 2021.11. Please update your configuration if "
|
||||||
"Please update your configuration if state_class is manually "
|
"state_class is manually configured, otherwise %s",
|
||||||
"configured, otherwise %s",
|
|
||||||
self.entity_id,
|
self.entity_id,
|
||||||
type(self),
|
type(self),
|
||||||
self.state_class,
|
self.state_class,
|
||||||
|
|
|
@ -17,7 +17,6 @@ from homeassistant.components.sensor import (
|
||||||
DEVICE_CLASS_PRESSURE,
|
DEVICE_CLASS_PRESSURE,
|
||||||
DEVICE_CLASS_TEMPERATURE,
|
DEVICE_CLASS_TEMPERATURE,
|
||||||
STATE_CLASS_MEASUREMENT,
|
STATE_CLASS_MEASUREMENT,
|
||||||
STATE_CLASS_TOTAL,
|
|
||||||
STATE_CLASS_TOTAL_INCREASING,
|
STATE_CLASS_TOTAL_INCREASING,
|
||||||
STATE_CLASSES,
|
STATE_CLASSES,
|
||||||
)
|
)
|
||||||
|
@ -43,7 +42,6 @@ from homeassistant.const import (
|
||||||
VOLUME_CUBIC_METERS,
|
VOLUME_CUBIC_METERS,
|
||||||
)
|
)
|
||||||
from homeassistant.core import HomeAssistant, State
|
from homeassistant.core import HomeAssistant, State
|
||||||
import homeassistant.util.dt as dt_util
|
|
||||||
import homeassistant.util.pressure as pressure_util
|
import homeassistant.util.pressure as pressure_util
|
||||||
import homeassistant.util.temperature as temperature_util
|
import homeassistant.util.temperature as temperature_util
|
||||||
import homeassistant.util.volume as volume_util
|
import homeassistant.util.volume as volume_util
|
||||||
|
@ -53,11 +51,6 @@ from . import ATTR_LAST_RESET, DOMAIN
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
DEVICE_CLASS_OR_UNIT_STATISTICS = {
|
DEVICE_CLASS_OR_UNIT_STATISTICS = {
|
||||||
STATE_CLASS_TOTAL: {
|
|
||||||
DEVICE_CLASS_ENERGY: {"sum"},
|
|
||||||
DEVICE_CLASS_GAS: {"sum"},
|
|
||||||
DEVICE_CLASS_MONETARY: {"sum"},
|
|
||||||
},
|
|
||||||
STATE_CLASS_MEASUREMENT: {
|
STATE_CLASS_MEASUREMENT: {
|
||||||
DEVICE_CLASS_BATTERY: {"mean", "min", "max"},
|
DEVICE_CLASS_BATTERY: {"mean", "min", "max"},
|
||||||
DEVICE_CLASS_HUMIDITY: {"mean", "min", "max"},
|
DEVICE_CLASS_HUMIDITY: {"mean", "min", "max"},
|
||||||
|
@ -65,7 +58,7 @@ DEVICE_CLASS_OR_UNIT_STATISTICS = {
|
||||||
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"},
|
PERCENTAGE: {"mean", "min", "max"},
|
||||||
# Deprecated, support will be removed in Home Assistant 2021.10
|
# Deprecated, support will be removed in Home Assistant 2021.11
|
||||||
DEVICE_CLASS_ENERGY: {"sum"},
|
DEVICE_CLASS_ENERGY: {"sum"},
|
||||||
DEVICE_CLASS_GAS: {"sum"},
|
DEVICE_CLASS_GAS: {"sum"},
|
||||||
DEVICE_CLASS_MONETARY: {"sum"},
|
DEVICE_CLASS_MONETARY: {"sum"},
|
||||||
|
@ -73,6 +66,7 @@ DEVICE_CLASS_OR_UNIT_STATISTICS = {
|
||||||
STATE_CLASS_TOTAL_INCREASING: {
|
STATE_CLASS_TOTAL_INCREASING: {
|
||||||
DEVICE_CLASS_ENERGY: {"sum"},
|
DEVICE_CLASS_ENERGY: {"sum"},
|
||||||
DEVICE_CLASS_GAS: {"sum"},
|
DEVICE_CLASS_GAS: {"sum"},
|
||||||
|
DEVICE_CLASS_MONETARY: {"sum"},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -279,13 +273,11 @@ def compile_statistics(
|
||||||
stat["mean"] = _time_weighted_average(fstates, start, end)
|
stat["mean"] = _time_weighted_average(fstates, start, end)
|
||||||
|
|
||||||
if "sum" in wanted_statistics:
|
if "sum" in wanted_statistics:
|
||||||
last_reset = old_last_reset = None
|
|
||||||
new_state = old_state = None
|
new_state = old_state = None
|
||||||
_sum = 0
|
_sum = 0
|
||||||
last_stats = statistics.get_last_statistics(hass, 1, entity_id)
|
last_stats = statistics.get_last_statistics(hass, 1, entity_id)
|
||||||
if entity_id in last_stats:
|
if entity_id in last_stats:
|
||||||
# We have compiled history for this sensor before, use that as a starting point
|
# We have compiled history for this sensor before, use that as a starting point
|
||||||
last_reset = old_last_reset = last_stats[entity_id][0]["last_reset"]
|
|
||||||
new_state = old_state = last_stats[entity_id][0]["state"]
|
new_state = old_state = last_stats[entity_id][0]["state"]
|
||||||
_sum = last_stats[entity_id][0]["sum"]
|
_sum = last_stats[entity_id][0]["sum"]
|
||||||
|
|
||||||
|
@ -299,13 +291,7 @@ def compile_statistics(
|
||||||
continue
|
continue
|
||||||
|
|
||||||
reset = False
|
reset = False
|
||||||
if (
|
if old_state is None:
|
||||||
state_class != STATE_CLASS_TOTAL_INCREASING
|
|
||||||
and (last_reset := state.attributes.get("last_reset"))
|
|
||||||
!= old_last_reset
|
|
||||||
):
|
|
||||||
reset = True
|
|
||||||
elif old_state is None and last_reset is None:
|
|
||||||
reset = True
|
reset = True
|
||||||
elif state_class == STATE_CLASS_TOTAL_INCREASING and (
|
elif state_class == STATE_CLASS_TOTAL_INCREASING and (
|
||||||
old_state is None or (new_state is not None and fstate < new_state)
|
old_state is None or (new_state is not None and fstate < new_state)
|
||||||
|
@ -318,21 +304,14 @@ def compile_statistics(
|
||||||
_sum += new_state - old_state
|
_sum += new_state - old_state
|
||||||
# ..and update the starting point
|
# ..and update the starting point
|
||||||
new_state = fstate
|
new_state = fstate
|
||||||
old_last_reset = last_reset
|
# Force a new cycle to start at 0
|
||||||
# Force a new cycle for STATE_CLASS_TOTAL_INCREASING to start at 0
|
if old_state is not None:
|
||||||
if state_class == STATE_CLASS_TOTAL_INCREASING and old_state:
|
old_state = 0.0
|
||||||
old_state = 0
|
|
||||||
else:
|
else:
|
||||||
old_state = new_state
|
old_state = new_state
|
||||||
else:
|
else:
|
||||||
new_state = fstate
|
new_state = fstate
|
||||||
|
|
||||||
# Deprecated, will be removed in Home Assistant 2021.10
|
|
||||||
if last_reset is None and state_class == STATE_CLASS_MEASUREMENT:
|
|
||||||
# No valid updates
|
|
||||||
result.pop(entity_id)
|
|
||||||
continue
|
|
||||||
|
|
||||||
if new_state is None or old_state is None:
|
if new_state is None or old_state is None:
|
||||||
# No valid updates
|
# No valid updates
|
||||||
result.pop(entity_id)
|
result.pop(entity_id)
|
||||||
|
@ -340,8 +319,6 @@ def compile_statistics(
|
||||||
|
|
||||||
# Update the sum with the last state
|
# Update the sum with the last state
|
||||||
_sum += new_state - old_state
|
_sum += new_state - old_state
|
||||||
if last_reset is not None:
|
|
||||||
stat["last_reset"] = dt_util.parse_datetime(last_reset)
|
|
||||||
stat["sum"] = _sum
|
stat["sum"] = _sum
|
||||||
stat["state"] = new_state
|
stat["state"] = new_state
|
||||||
|
|
||||||
|
@ -365,7 +342,11 @@ def list_statistic_ids(hass: HomeAssistant, statistic_type: str | None = None) -
|
||||||
state = hass.states.get(entity_id)
|
state = hass.states.get(entity_id)
|
||||||
assert state
|
assert state
|
||||||
|
|
||||||
if "sum" in provided_statistics and ATTR_LAST_RESET not in state.attributes:
|
if (
|
||||||
|
"sum" in provided_statistics
|
||||||
|
and ATTR_LAST_RESET not in state.attributes
|
||||||
|
and state.attributes.get(ATTR_STATE_CLASS) == STATE_CLASS_MEASUREMENT
|
||||||
|
):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
native_unit = state.attributes.get(ATTR_UNIT_OF_MEASUREMENT)
|
native_unit = state.attributes.get(ATTR_UNIT_OF_MEASUREMENT)
|
||||||
|
|
|
@ -5,11 +5,7 @@ import logging
|
||||||
|
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
from homeassistant.components.sensor import (
|
from homeassistant.components.sensor import STATE_CLASS_MEASUREMENT, SensorEntity
|
||||||
ATTR_LAST_RESET,
|
|
||||||
STATE_CLASS_MEASUREMENT,
|
|
||||||
SensorEntity,
|
|
||||||
)
|
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
ATTR_UNIT_OF_MEASUREMENT,
|
ATTR_UNIT_OF_MEASUREMENT,
|
||||||
CONF_NAME,
|
CONF_NAME,
|
||||||
|
@ -58,6 +54,7 @@ ATTR_SOURCE_ID = "source"
|
||||||
ATTR_STATUS = "status"
|
ATTR_STATUS = "status"
|
||||||
ATTR_PERIOD = "meter_period"
|
ATTR_PERIOD = "meter_period"
|
||||||
ATTR_LAST_PERIOD = "last_period"
|
ATTR_LAST_PERIOD = "last_period"
|
||||||
|
ATTR_LAST_RESET = "last_reset"
|
||||||
ATTR_TARIFF = "tariff"
|
ATTR_TARIFF = "tariff"
|
||||||
|
|
||||||
DEVICE_CLASS_MAP = {
|
DEVICE_CLASS_MAP = {
|
||||||
|
@ -352,6 +349,7 @@ class UtilityMeterSensor(RestoreEntity, SensorEntity):
|
||||||
ATTR_SOURCE_ID: self._sensor_source_id,
|
ATTR_SOURCE_ID: self._sensor_source_id,
|
||||||
ATTR_STATUS: PAUSED if self._collecting is None else COLLECTING,
|
ATTR_STATUS: PAUSED if self._collecting is None else COLLECTING,
|
||||||
ATTR_LAST_PERIOD: self._last_period,
|
ATTR_LAST_PERIOD: self._last_period,
|
||||||
|
ATTR_LAST_RESET: self._last_reset.isoformat(),
|
||||||
}
|
}
|
||||||
if self._period is not None:
|
if self._period is not None:
|
||||||
state_attr[ATTR_PERIOD] = self._period
|
state_attr[ATTR_PERIOD] = self._period
|
||||||
|
@ -363,8 +361,3 @@ class UtilityMeterSensor(RestoreEntity, SensorEntity):
|
||||||
def icon(self):
|
def icon(self):
|
||||||
"""Return the icon to use in the frontend, if any."""
|
"""Return the icon to use in the frontend, if any."""
|
||||||
return ICON
|
return ICON
|
||||||
|
|
||||||
@property
|
|
||||||
def last_reset(self):
|
|
||||||
"""Return the time when the sensor was last reset."""
|
|
||||||
return self._last_reset
|
|
||||||
|
|
|
@ -7,9 +7,8 @@ import pytest
|
||||||
|
|
||||||
from homeassistant.components.energy import data
|
from homeassistant.components.energy import data
|
||||||
from homeassistant.components.sensor import (
|
from homeassistant.components.sensor import (
|
||||||
ATTR_LAST_RESET,
|
|
||||||
ATTR_STATE_CLASS,
|
ATTR_STATE_CLASS,
|
||||||
STATE_CLASS_MEASUREMENT,
|
STATE_CLASS_TOTAL_INCREASING,
|
||||||
)
|
)
|
||||||
from homeassistant.components.sensor.recorder import compile_statistics
|
from homeassistant.components.sensor.recorder import compile_statistics
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
|
@ -131,14 +130,13 @@ async def test_cost_sensor_price_entity(
|
||||||
}
|
}
|
||||||
|
|
||||||
now = dt_util.utcnow()
|
now = dt_util.utcnow()
|
||||||
last_reset = dt_util.utc_from_timestamp(0).isoformat()
|
|
||||||
|
|
||||||
# Optionally initialize dependent entities
|
# Optionally initialize dependent entities
|
||||||
if initial_energy is not None:
|
if initial_energy is not None:
|
||||||
hass.states.async_set(
|
hass.states.async_set(
|
||||||
usage_sensor_entity_id,
|
usage_sensor_entity_id,
|
||||||
initial_energy,
|
initial_energy,
|
||||||
{"last_reset": last_reset, ATTR_UNIT_OF_MEASUREMENT: ENERGY_KILO_WATT_HOUR},
|
{ATTR_UNIT_OF_MEASUREMENT: ENERGY_KILO_WATT_HOUR},
|
||||||
)
|
)
|
||||||
hass.states.async_set("sensor.energy_price", "1")
|
hass.states.async_set("sensor.energy_price", "1")
|
||||||
|
|
||||||
|
@ -148,9 +146,7 @@ async def test_cost_sensor_price_entity(
|
||||||
state = hass.states.get(cost_sensor_entity_id)
|
state = hass.states.get(cost_sensor_entity_id)
|
||||||
assert state.state == initial_cost
|
assert state.state == initial_cost
|
||||||
assert state.attributes[ATTR_DEVICE_CLASS] == DEVICE_CLASS_MONETARY
|
assert state.attributes[ATTR_DEVICE_CLASS] == DEVICE_CLASS_MONETARY
|
||||||
if initial_cost != "unknown":
|
assert state.attributes[ATTR_STATE_CLASS] == STATE_CLASS_TOTAL_INCREASING
|
||||||
assert state.attributes[ATTR_LAST_RESET] == now.isoformat()
|
|
||||||
assert state.attributes[ATTR_STATE_CLASS] == STATE_CLASS_MEASUREMENT
|
|
||||||
assert state.attributes[ATTR_UNIT_OF_MEASUREMENT] == "EUR"
|
assert state.attributes[ATTR_UNIT_OF_MEASUREMENT] == "EUR"
|
||||||
|
|
||||||
# Optional late setup of dependent entities
|
# Optional late setup of dependent entities
|
||||||
|
@ -160,7 +156,6 @@ async def test_cost_sensor_price_entity(
|
||||||
usage_sensor_entity_id,
|
usage_sensor_entity_id,
|
||||||
"0",
|
"0",
|
||||||
{
|
{
|
||||||
"last_reset": last_reset,
|
|
||||||
ATTR_UNIT_OF_MEASUREMENT: ENERGY_KILO_WATT_HOUR,
|
ATTR_UNIT_OF_MEASUREMENT: ENERGY_KILO_WATT_HOUR,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
@ -169,8 +164,7 @@ async def test_cost_sensor_price_entity(
|
||||||
state = hass.states.get(cost_sensor_entity_id)
|
state = hass.states.get(cost_sensor_entity_id)
|
||||||
assert state.state == "0.0"
|
assert state.state == "0.0"
|
||||||
assert state.attributes[ATTR_DEVICE_CLASS] == DEVICE_CLASS_MONETARY
|
assert state.attributes[ATTR_DEVICE_CLASS] == DEVICE_CLASS_MONETARY
|
||||||
assert state.attributes[ATTR_LAST_RESET] == now.isoformat()
|
assert state.attributes[ATTR_STATE_CLASS] == STATE_CLASS_TOTAL_INCREASING
|
||||||
assert state.attributes[ATTR_STATE_CLASS] == STATE_CLASS_MEASUREMENT
|
|
||||||
assert state.attributes[ATTR_UNIT_OF_MEASUREMENT] == "EUR"
|
assert state.attributes[ATTR_UNIT_OF_MEASUREMENT] == "EUR"
|
||||||
|
|
||||||
# # Unique ID temp disabled
|
# # Unique ID temp disabled
|
||||||
|
@ -182,7 +176,7 @@ async def test_cost_sensor_price_entity(
|
||||||
hass.states.async_set(
|
hass.states.async_set(
|
||||||
usage_sensor_entity_id,
|
usage_sensor_entity_id,
|
||||||
"10",
|
"10",
|
||||||
{"last_reset": last_reset, ATTR_UNIT_OF_MEASUREMENT: ENERGY_KILO_WATT_HOUR},
|
{ATTR_UNIT_OF_MEASUREMENT: ENERGY_KILO_WATT_HOUR},
|
||||||
)
|
)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
state = hass.states.get(cost_sensor_entity_id)
|
state = hass.states.get(cost_sensor_entity_id)
|
||||||
|
@ -206,7 +200,7 @@ async def test_cost_sensor_price_entity(
|
||||||
hass.states.async_set(
|
hass.states.async_set(
|
||||||
usage_sensor_entity_id,
|
usage_sensor_entity_id,
|
||||||
"14.5",
|
"14.5",
|
||||||
{"last_reset": last_reset, ATTR_UNIT_OF_MEASUREMENT: ENERGY_KILO_WATT_HOUR},
|
{ATTR_UNIT_OF_MEASUREMENT: ENERGY_KILO_WATT_HOUR},
|
||||||
)
|
)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
state = hass.states.get(cost_sensor_entity_id)
|
state = hass.states.get(cost_sensor_entity_id)
|
||||||
|
@ -218,32 +212,31 @@ async def test_cost_sensor_price_entity(
|
||||||
assert cost_sensor_entity_id in statistics
|
assert cost_sensor_entity_id in statistics
|
||||||
assert statistics[cost_sensor_entity_id]["stat"]["sum"] == 19.0
|
assert statistics[cost_sensor_entity_id]["stat"]["sum"] == 19.0
|
||||||
|
|
||||||
# Energy sensor is reset, with start point at 4kWh
|
# Energy sensor is reset, with initial state at 4kWh, 0 kWh is used as zero-point
|
||||||
last_reset = (now + timedelta(seconds=1)).isoformat()
|
|
||||||
hass.states.async_set(
|
hass.states.async_set(
|
||||||
usage_sensor_entity_id,
|
usage_sensor_entity_id,
|
||||||
"4",
|
"4",
|
||||||
{"last_reset": last_reset, ATTR_UNIT_OF_MEASUREMENT: ENERGY_KILO_WATT_HOUR},
|
{ATTR_UNIT_OF_MEASUREMENT: ENERGY_KILO_WATT_HOUR},
|
||||||
)
|
)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
state = hass.states.get(cost_sensor_entity_id)
|
state = hass.states.get(cost_sensor_entity_id)
|
||||||
assert state.state == "0.0" # 0 EUR + (4-4) kWh * 2 EUR/kWh = 0 EUR
|
assert state.state == "8.0" # 0 EUR + (4-0) kWh * 2 EUR/kWh = 8 EUR
|
||||||
|
|
||||||
# Energy use bumped to 10 kWh
|
# Energy use bumped to 10 kWh
|
||||||
hass.states.async_set(
|
hass.states.async_set(
|
||||||
usage_sensor_entity_id,
|
usage_sensor_entity_id,
|
||||||
"10",
|
"10",
|
||||||
{"last_reset": last_reset, ATTR_UNIT_OF_MEASUREMENT: ENERGY_KILO_WATT_HOUR},
|
{ATTR_UNIT_OF_MEASUREMENT: ENERGY_KILO_WATT_HOUR},
|
||||||
)
|
)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
state = hass.states.get(cost_sensor_entity_id)
|
state = hass.states.get(cost_sensor_entity_id)
|
||||||
assert state.state == "12.0" # 0 EUR + (10-4) kWh * 2 EUR/kWh = 12 EUR
|
assert state.state == "20.0" # 8 EUR + (10-4) kWh * 2 EUR/kWh = 20 EUR
|
||||||
|
|
||||||
# Check generated statistics
|
# Check generated statistics
|
||||||
await async_wait_recording_done_without_instance(hass)
|
await async_wait_recording_done_without_instance(hass)
|
||||||
statistics = await hass.loop.run_in_executor(None, _compile_statistics, hass)
|
statistics = await hass.loop.run_in_executor(None, _compile_statistics, hass)
|
||||||
assert cost_sensor_entity_id in statistics
|
assert cost_sensor_entity_id in statistics
|
||||||
assert statistics[cost_sensor_entity_id]["stat"]["sum"] == 31.0
|
assert statistics[cost_sensor_entity_id]["stat"]["sum"] == 39.0
|
||||||
|
|
||||||
|
|
||||||
async def test_cost_sensor_handle_wh(hass, hass_storage) -> None:
|
async def test_cost_sensor_handle_wh(hass, hass_storage) -> None:
|
||||||
|
@ -272,12 +265,11 @@ async def test_cost_sensor_handle_wh(hass, hass_storage) -> None:
|
||||||
}
|
}
|
||||||
|
|
||||||
now = dt_util.utcnow()
|
now = dt_util.utcnow()
|
||||||
last_reset = dt_util.utc_from_timestamp(0).isoformat()
|
|
||||||
|
|
||||||
hass.states.async_set(
|
hass.states.async_set(
|
||||||
"sensor.energy_consumption",
|
"sensor.energy_consumption",
|
||||||
10000,
|
10000,
|
||||||
{"last_reset": last_reset, "unit_of_measurement": ENERGY_WATT_HOUR},
|
{"unit_of_measurement": ENERGY_WATT_HOUR},
|
||||||
)
|
)
|
||||||
|
|
||||||
with patch("homeassistant.util.dt.utcnow", return_value=now):
|
with patch("homeassistant.util.dt.utcnow", return_value=now):
|
||||||
|
@ -290,7 +282,7 @@ async def test_cost_sensor_handle_wh(hass, hass_storage) -> None:
|
||||||
hass.states.async_set(
|
hass.states.async_set(
|
||||||
"sensor.energy_consumption",
|
"sensor.energy_consumption",
|
||||||
20000,
|
20000,
|
||||||
{"last_reset": last_reset, "unit_of_measurement": ENERGY_WATT_HOUR},
|
{"unit_of_measurement": ENERGY_WATT_HOUR},
|
||||||
)
|
)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
@ -318,12 +310,11 @@ async def test_cost_sensor_handle_gas(hass, hass_storage) -> None:
|
||||||
}
|
}
|
||||||
|
|
||||||
now = dt_util.utcnow()
|
now = dt_util.utcnow()
|
||||||
last_reset = dt_util.utc_from_timestamp(0).isoformat()
|
|
||||||
|
|
||||||
hass.states.async_set(
|
hass.states.async_set(
|
||||||
"sensor.gas_consumption",
|
"sensor.gas_consumption",
|
||||||
100,
|
100,
|
||||||
{"last_reset": last_reset, "unit_of_measurement": VOLUME_CUBIC_METERS},
|
{"unit_of_measurement": VOLUME_CUBIC_METERS},
|
||||||
)
|
)
|
||||||
|
|
||||||
with patch("homeassistant.util.dt.utcnow", return_value=now):
|
with patch("homeassistant.util.dt.utcnow", return_value=now):
|
||||||
|
@ -336,7 +327,7 @@ async def test_cost_sensor_handle_gas(hass, hass_storage) -> None:
|
||||||
hass.states.async_set(
|
hass.states.async_set(
|
||||||
"sensor.gas_consumption",
|
"sensor.gas_consumption",
|
||||||
200,
|
200,
|
||||||
{"last_reset": last_reset, "unit_of_measurement": VOLUME_CUBIC_METERS},
|
{"unit_of_measurement": VOLUME_CUBIC_METERS},
|
||||||
)
|
)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
|
|
@ -911,7 +911,6 @@ async def test_statistics_during_period(
|
||||||
"mean": approx(value),
|
"mean": approx(value),
|
||||||
"min": approx(value),
|
"min": approx(value),
|
||||||
"max": approx(value),
|
"max": approx(value),
|
||||||
"last_reset": None,
|
|
||||||
"state": None,
|
"state": None,
|
||||||
"sum": None,
|
"sum": None,
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,7 +44,6 @@ def test_compile_hourly_statistics(hass_recorder):
|
||||||
"mean": approx(14.915254237288135),
|
"mean": approx(14.915254237288135),
|
||||||
"min": approx(10.0),
|
"min": approx(10.0),
|
||||||
"max": approx(20.0),
|
"max": approx(20.0),
|
||||||
"last_reset": None,
|
|
||||||
"state": None,
|
"state": None,
|
||||||
"sum": None,
|
"sum": None,
|
||||||
}
|
}
|
||||||
|
@ -54,7 +53,6 @@ def test_compile_hourly_statistics(hass_recorder):
|
||||||
"mean": approx(20.0),
|
"mean": approx(20.0),
|
||||||
"min": approx(20.0),
|
"min": approx(20.0),
|
||||||
"max": approx(20.0),
|
"max": approx(20.0),
|
||||||
"last_reset": None,
|
|
||||||
"state": None,
|
"state": None,
|
||||||
"sum": None,
|
"sum": None,
|
||||||
}
|
}
|
||||||
|
@ -127,7 +125,6 @@ def test_rename_entity(hass_recorder):
|
||||||
"mean": approx(14.915254237288135),
|
"mean": approx(14.915254237288135),
|
||||||
"min": approx(10.0),
|
"min": approx(10.0),
|
||||||
"max": approx(20.0),
|
"max": approx(20.0),
|
||||||
"last_reset": None,
|
|
||||||
"state": None,
|
"state": None,
|
||||||
"sum": None,
|
"sum": None,
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,9 +44,8 @@ async def test_deprecated_last_reset(hass, caplog, enable_custom_integrations):
|
||||||
|
|
||||||
assert (
|
assert (
|
||||||
"Entity sensor.test (<class 'custom_components.test.sensor.MockSensor'>) "
|
"Entity sensor.test (<class 'custom_components.test.sensor.MockSensor'>) "
|
||||||
"with state_class measurement has set last_reset. Setting last_reset for "
|
"with state_class measurement has set last_reset. Setting last_reset is "
|
||||||
"entities with state_class other than 'total' is deprecated and will be "
|
"deprecated and will be unsupported from Home Assistant Core 2021.11. Please "
|
||||||
"removed from Home Assistant Core 2021.10. Please update your configuration if "
|
"update your configuration if state_class is manually configured, otherwise "
|
||||||
"state_class is manually configured, otherwise report it to the custom "
|
"report it to the custom component author."
|
||||||
"component author."
|
|
||||||
) in caplog.text
|
) in caplog.text
|
||||||
|
|
|
@ -95,7 +95,6 @@ def test_compile_hourly_statistics(
|
||||||
"mean": approx(mean),
|
"mean": approx(mean),
|
||||||
"min": approx(min),
|
"min": approx(min),
|
||||||
"max": approx(max),
|
"max": approx(max),
|
||||||
"last_reset": None,
|
|
||||||
"state": None,
|
"state": None,
|
||||||
"sum": None,
|
"sum": None,
|
||||||
}
|
}
|
||||||
|
@ -145,7 +144,6 @@ def test_compile_hourly_statistics_unsupported(hass_recorder, caplog, attributes
|
||||||
"mean": approx(16.440677966101696),
|
"mean": approx(16.440677966101696),
|
||||||
"min": approx(10.0),
|
"min": approx(10.0),
|
||||||
"max": approx(30.0),
|
"max": approx(30.0),
|
||||||
"last_reset": None,
|
|
||||||
"state": None,
|
"state": None,
|
||||||
"sum": None,
|
"sum": None,
|
||||||
}
|
}
|
||||||
|
@ -154,7 +152,6 @@ def test_compile_hourly_statistics_unsupported(hass_recorder, caplog, attributes
|
||||||
assert "Error while processing event StatisticsTask" not in caplog.text
|
assert "Error while processing event StatisticsTask" not in caplog.text
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("state_class", ["measurement", "total"])
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"device_class,unit,native_unit,factor",
|
"device_class,unit,native_unit,factor",
|
||||||
[
|
[
|
||||||
|
@ -167,7 +164,7 @@ def test_compile_hourly_statistics_unsupported(hass_recorder, caplog, attributes
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
def test_compile_hourly_sum_statistics_amount(
|
def test_compile_hourly_sum_statistics_amount(
|
||||||
hass_recorder, caplog, state_class, device_class, unit, native_unit, factor
|
hass_recorder, caplog, device_class, unit, native_unit, factor
|
||||||
):
|
):
|
||||||
"""Test compiling hourly statistics."""
|
"""Test compiling hourly statistics."""
|
||||||
zero = dt_util.utcnow()
|
zero = dt_util.utcnow()
|
||||||
|
@ -176,7 +173,7 @@ def test_compile_hourly_sum_statistics_amount(
|
||||||
setup_component(hass, "sensor", {})
|
setup_component(hass, "sensor", {})
|
||||||
attributes = {
|
attributes = {
|
||||||
"device_class": device_class,
|
"device_class": device_class,
|
||||||
"state_class": state_class,
|
"state_class": "measurement",
|
||||||
"unit_of_measurement": unit,
|
"unit_of_measurement": unit,
|
||||||
"last_reset": None,
|
"last_reset": None,
|
||||||
}
|
}
|
||||||
|
@ -209,7 +206,6 @@ def test_compile_hourly_sum_statistics_amount(
|
||||||
"max": None,
|
"max": None,
|
||||||
"mean": None,
|
"mean": None,
|
||||||
"min": None,
|
"min": None,
|
||||||
"last_reset": process_timestamp_to_utc_isoformat(zero),
|
|
||||||
"state": approx(factor * seq[2]),
|
"state": approx(factor * seq[2]),
|
||||||
"sum": approx(factor * 10.0),
|
"sum": approx(factor * 10.0),
|
||||||
},
|
},
|
||||||
|
@ -219,89 +215,6 @@ def test_compile_hourly_sum_statistics_amount(
|
||||||
"max": None,
|
"max": None,
|
||||||
"mean": None,
|
"mean": None,
|
||||||
"min": None,
|
"min": None,
|
||||||
"last_reset": process_timestamp_to_utc_isoformat(four),
|
|
||||||
"state": approx(factor * seq[5]),
|
|
||||||
"sum": approx(factor * 10.0),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"statistic_id": "sensor.test1",
|
|
||||||
"start": process_timestamp_to_utc_isoformat(zero + timedelta(hours=2)),
|
|
||||||
"max": None,
|
|
||||||
"mean": None,
|
|
||||||
"min": None,
|
|
||||||
"last_reset": process_timestamp_to_utc_isoformat(four),
|
|
||||||
"state": approx(factor * seq[8]),
|
|
||||||
"sum": approx(factor * 40.0),
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}
|
|
||||||
assert "Error while processing event StatisticsTask" not in caplog.text
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
|
||||||
"device_class,unit,native_unit,factor",
|
|
||||||
[
|
|
||||||
("energy", "kWh", "kWh", 1),
|
|
||||||
("energy", "Wh", "kWh", 1 / 1000),
|
|
||||||
("monetary", "EUR", "EUR", 1),
|
|
||||||
("monetary", "SEK", "SEK", 1),
|
|
||||||
("gas", "m³", "m³", 1),
|
|
||||||
("gas", "ft³", "m³", 0.0283168466),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
def test_compile_hourly_sum_statistics_total_no_reset(
|
|
||||||
hass_recorder, caplog, device_class, unit, native_unit, factor
|
|
||||||
):
|
|
||||||
"""Test compiling hourly statistics."""
|
|
||||||
zero = dt_util.utcnow()
|
|
||||||
hass = hass_recorder()
|
|
||||||
recorder = hass.data[DATA_INSTANCE]
|
|
||||||
setup_component(hass, "sensor", {})
|
|
||||||
attributes = {
|
|
||||||
"device_class": device_class,
|
|
||||||
"state_class": "total",
|
|
||||||
"unit_of_measurement": unit,
|
|
||||||
}
|
|
||||||
seq = [10, 15, 20, 10, 30, 40, 50, 60, 70]
|
|
||||||
|
|
||||||
four, eight, states = record_meter_states(
|
|
||||||
hass, zero, "sensor.test1", attributes, seq
|
|
||||||
)
|
|
||||||
hist = history.get_significant_states(
|
|
||||||
hass, zero - timedelta.resolution, eight + timedelta.resolution
|
|
||||||
)
|
|
||||||
assert dict(states)["sensor.test1"] == dict(hist)["sensor.test1"]
|
|
||||||
|
|
||||||
recorder.do_adhoc_statistics(period="hourly", start=zero)
|
|
||||||
wait_recording_done(hass)
|
|
||||||
recorder.do_adhoc_statistics(period="hourly", start=zero + timedelta(hours=1))
|
|
||||||
wait_recording_done(hass)
|
|
||||||
recorder.do_adhoc_statistics(period="hourly", start=zero + timedelta(hours=2))
|
|
||||||
wait_recording_done(hass)
|
|
||||||
statistic_ids = list_statistic_ids(hass)
|
|
||||||
assert statistic_ids == [
|
|
||||||
{"statistic_id": "sensor.test1", "unit_of_measurement": native_unit}
|
|
||||||
]
|
|
||||||
stats = statistics_during_period(hass, zero)
|
|
||||||
assert stats == {
|
|
||||||
"sensor.test1": [
|
|
||||||
{
|
|
||||||
"statistic_id": "sensor.test1",
|
|
||||||
"start": process_timestamp_to_utc_isoformat(zero),
|
|
||||||
"max": None,
|
|
||||||
"mean": None,
|
|
||||||
"min": None,
|
|
||||||
"last_reset": None,
|
|
||||||
"state": approx(factor * seq[2]),
|
|
||||||
"sum": approx(factor * 10.0),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"statistic_id": "sensor.test1",
|
|
||||||
"start": process_timestamp_to_utc_isoformat(zero + timedelta(hours=1)),
|
|
||||||
"max": None,
|
|
||||||
"mean": None,
|
|
||||||
"min": None,
|
|
||||||
"last_reset": None,
|
|
||||||
"state": approx(factor * seq[5]),
|
"state": approx(factor * seq[5]),
|
||||||
"sum": approx(factor * 30.0),
|
"sum": approx(factor * 30.0),
|
||||||
},
|
},
|
||||||
|
@ -311,7 +224,6 @@ def test_compile_hourly_sum_statistics_total_no_reset(
|
||||||
"max": None,
|
"max": None,
|
||||||
"mean": None,
|
"mean": None,
|
||||||
"min": None,
|
"min": None,
|
||||||
"last_reset": None,
|
|
||||||
"state": approx(factor * seq[8]),
|
"state": approx(factor * seq[8]),
|
||||||
"sum": approx(factor * 60.0),
|
"sum": approx(factor * 60.0),
|
||||||
},
|
},
|
||||||
|
@ -371,7 +283,6 @@ def test_compile_hourly_sum_statistics_total_increasing(
|
||||||
"max": None,
|
"max": None,
|
||||||
"mean": None,
|
"mean": None,
|
||||||
"min": None,
|
"min": None,
|
||||||
"last_reset": None,
|
|
||||||
"state": approx(factor * seq[2]),
|
"state": approx(factor * seq[2]),
|
||||||
"sum": approx(factor * 10.0),
|
"sum": approx(factor * 10.0),
|
||||||
},
|
},
|
||||||
|
@ -381,7 +292,6 @@ def test_compile_hourly_sum_statistics_total_increasing(
|
||||||
"max": None,
|
"max": None,
|
||||||
"mean": None,
|
"mean": None,
|
||||||
"min": None,
|
"min": None,
|
||||||
"last_reset": None,
|
|
||||||
"state": approx(factor * seq[5]),
|
"state": approx(factor * seq[5]),
|
||||||
"sum": approx(factor * 50.0),
|
"sum": approx(factor * 50.0),
|
||||||
},
|
},
|
||||||
|
@ -391,7 +301,6 @@ def test_compile_hourly_sum_statistics_total_increasing(
|
||||||
"max": None,
|
"max": None,
|
||||||
"mean": None,
|
"mean": None,
|
||||||
"min": None,
|
"min": None,
|
||||||
"last_reset": None,
|
|
||||||
"state": approx(factor * seq[8]),
|
"state": approx(factor * seq[8]),
|
||||||
"sum": approx(factor * 80.0),
|
"sum": approx(factor * 80.0),
|
||||||
},
|
},
|
||||||
|
@ -458,7 +367,6 @@ def test_compile_hourly_energy_statistics_unsupported(hass_recorder, caplog):
|
||||||
"max": None,
|
"max": None,
|
||||||
"mean": None,
|
"mean": None,
|
||||||
"min": None,
|
"min": None,
|
||||||
"last_reset": process_timestamp_to_utc_isoformat(zero),
|
|
||||||
"state": approx(20.0),
|
"state": approx(20.0),
|
||||||
"sum": approx(10.0),
|
"sum": approx(10.0),
|
||||||
},
|
},
|
||||||
|
@ -468,9 +376,8 @@ def test_compile_hourly_energy_statistics_unsupported(hass_recorder, caplog):
|
||||||
"max": None,
|
"max": None,
|
||||||
"mean": None,
|
"mean": None,
|
||||||
"min": None,
|
"min": None,
|
||||||
"last_reset": process_timestamp_to_utc_isoformat(four),
|
|
||||||
"state": approx(40.0),
|
"state": approx(40.0),
|
||||||
"sum": approx(10.0),
|
"sum": approx(30.0),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"statistic_id": "sensor.test1",
|
"statistic_id": "sensor.test1",
|
||||||
|
@ -478,9 +385,8 @@ def test_compile_hourly_energy_statistics_unsupported(hass_recorder, caplog):
|
||||||
"max": None,
|
"max": None,
|
||||||
"mean": None,
|
"mean": None,
|
||||||
"min": None,
|
"min": None,
|
||||||
"last_reset": process_timestamp_to_utc_isoformat(four),
|
|
||||||
"state": approx(70.0),
|
"state": approx(70.0),
|
||||||
"sum": approx(40.0),
|
"sum": approx(60.0),
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -541,7 +447,6 @@ def test_compile_hourly_energy_statistics_multiple(hass_recorder, caplog):
|
||||||
"max": None,
|
"max": None,
|
||||||
"mean": None,
|
"mean": None,
|
||||||
"min": None,
|
"min": None,
|
||||||
"last_reset": process_timestamp_to_utc_isoformat(zero),
|
|
||||||
"state": approx(20.0),
|
"state": approx(20.0),
|
||||||
"sum": approx(10.0),
|
"sum": approx(10.0),
|
||||||
},
|
},
|
||||||
|
@ -551,9 +456,8 @@ def test_compile_hourly_energy_statistics_multiple(hass_recorder, caplog):
|
||||||
"max": None,
|
"max": None,
|
||||||
"mean": None,
|
"mean": None,
|
||||||
"min": None,
|
"min": None,
|
||||||
"last_reset": process_timestamp_to_utc_isoformat(four),
|
|
||||||
"state": approx(40.0),
|
"state": approx(40.0),
|
||||||
"sum": approx(10.0),
|
"sum": approx(30.0),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"statistic_id": "sensor.test1",
|
"statistic_id": "sensor.test1",
|
||||||
|
@ -561,9 +465,8 @@ def test_compile_hourly_energy_statistics_multiple(hass_recorder, caplog):
|
||||||
"max": None,
|
"max": None,
|
||||||
"mean": None,
|
"mean": None,
|
||||||
"min": None,
|
"min": None,
|
||||||
"last_reset": process_timestamp_to_utc_isoformat(four),
|
|
||||||
"state": approx(70.0),
|
"state": approx(70.0),
|
||||||
"sum": approx(40.0),
|
"sum": approx(60.0),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
"sensor.test2": [
|
"sensor.test2": [
|
||||||
|
@ -573,7 +476,6 @@ def test_compile_hourly_energy_statistics_multiple(hass_recorder, caplog):
|
||||||
"max": None,
|
"max": None,
|
||||||
"mean": None,
|
"mean": None,
|
||||||
"min": None,
|
"min": None,
|
||||||
"last_reset": process_timestamp_to_utc_isoformat(zero),
|
|
||||||
"state": approx(130.0),
|
"state": approx(130.0),
|
||||||
"sum": approx(20.0),
|
"sum": approx(20.0),
|
||||||
},
|
},
|
||||||
|
@ -583,9 +485,8 @@ def test_compile_hourly_energy_statistics_multiple(hass_recorder, caplog):
|
||||||
"max": None,
|
"max": None,
|
||||||
"mean": None,
|
"mean": None,
|
||||||
"min": None,
|
"min": None,
|
||||||
"last_reset": process_timestamp_to_utc_isoformat(four),
|
|
||||||
"state": approx(45.0),
|
"state": approx(45.0),
|
||||||
"sum": approx(-95.0),
|
"sum": approx(-65.0),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"statistic_id": "sensor.test2",
|
"statistic_id": "sensor.test2",
|
||||||
|
@ -593,9 +494,8 @@ def test_compile_hourly_energy_statistics_multiple(hass_recorder, caplog):
|
||||||
"max": None,
|
"max": None,
|
||||||
"mean": None,
|
"mean": None,
|
||||||
"min": None,
|
"min": None,
|
||||||
"last_reset": process_timestamp_to_utc_isoformat(four),
|
|
||||||
"state": approx(75.0),
|
"state": approx(75.0),
|
||||||
"sum": approx(-65.0),
|
"sum": approx(-35.0),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
"sensor.test3": [
|
"sensor.test3": [
|
||||||
|
@ -605,7 +505,6 @@ def test_compile_hourly_energy_statistics_multiple(hass_recorder, caplog):
|
||||||
"max": None,
|
"max": None,
|
||||||
"mean": None,
|
"mean": None,
|
||||||
"min": None,
|
"min": None,
|
||||||
"last_reset": process_timestamp_to_utc_isoformat(zero),
|
|
||||||
"state": approx(5.0 / 1000),
|
"state": approx(5.0 / 1000),
|
||||||
"sum": approx(5.0 / 1000),
|
"sum": approx(5.0 / 1000),
|
||||||
},
|
},
|
||||||
|
@ -615,9 +514,8 @@ def test_compile_hourly_energy_statistics_multiple(hass_recorder, caplog):
|
||||||
"max": None,
|
"max": None,
|
||||||
"mean": None,
|
"mean": None,
|
||||||
"min": None,
|
"min": None,
|
||||||
"last_reset": process_timestamp_to_utc_isoformat(four),
|
|
||||||
"state": approx(50.0 / 1000),
|
"state": approx(50.0 / 1000),
|
||||||
"sum": approx(30.0 / 1000),
|
"sum": approx(50.0 / 1000),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"statistic_id": "sensor.test3",
|
"statistic_id": "sensor.test3",
|
||||||
|
@ -625,9 +523,8 @@ def test_compile_hourly_energy_statistics_multiple(hass_recorder, caplog):
|
||||||
"max": None,
|
"max": None,
|
||||||
"mean": None,
|
"mean": None,
|
||||||
"min": None,
|
"min": None,
|
||||||
"last_reset": process_timestamp_to_utc_isoformat(four),
|
|
||||||
"state": approx(90.0 / 1000),
|
"state": approx(90.0 / 1000),
|
||||||
"sum": approx(70.0 / 1000),
|
"sum": approx(90.0 / 1000),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
@ -678,7 +575,6 @@ def test_compile_hourly_statistics_unchanged(
|
||||||
"mean": approx(value),
|
"mean": approx(value),
|
||||||
"min": approx(value),
|
"min": approx(value),
|
||||||
"max": approx(value),
|
"max": approx(value),
|
||||||
"last_reset": None,
|
|
||||||
"state": None,
|
"state": None,
|
||||||
"sum": None,
|
"sum": None,
|
||||||
}
|
}
|
||||||
|
@ -710,7 +606,6 @@ def test_compile_hourly_statistics_partially_unavailable(hass_recorder, caplog):
|
||||||
"mean": approx(21.1864406779661),
|
"mean": approx(21.1864406779661),
|
||||||
"min": approx(10.0),
|
"min": approx(10.0),
|
||||||
"max": approx(25.0),
|
"max": approx(25.0),
|
||||||
"last_reset": None,
|
|
||||||
"state": None,
|
"state": None,
|
||||||
"sum": None,
|
"sum": None,
|
||||||
}
|
}
|
||||||
|
@ -767,7 +662,6 @@ def test_compile_hourly_statistics_unavailable(
|
||||||
"mean": approx(value),
|
"mean": approx(value),
|
||||||
"min": approx(value),
|
"min": approx(value),
|
||||||
"max": approx(value),
|
"max": approx(value),
|
||||||
"last_reset": None,
|
|
||||||
"state": None,
|
"state": None,
|
||||||
"sum": None,
|
"sum": None,
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue