Re-add state_class total to sensor (#55103)

* Re-add state_class total to sensor

* Make energy cost sensor enforce state_class total_increasing

* Bump deprecation of last_reset for state_class measurement

* Correct rebase mistakes
This commit is contained in:
Erik Montnemery 2021-09-06 18:28:58 +02:00 committed by GitHub
parent 2634949999
commit b99a22cd4d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 101 additions and 10 deletions

View File

@ -96,11 +96,14 @@ DEVICE_CLASSES_SCHEMA: Final = vol.All(vol.Lower, vol.In(DEVICE_CLASSES))
# The state represents a measurement in present time
STATE_CLASS_MEASUREMENT: Final = "measurement"
# The state represents a total amount, e.g. net energy consumption
STATE_CLASS_TOTAL: Final = "total"
# The state represents a monotonically increasing total, e.g. an amount of consumed gas
STATE_CLASS_TOTAL_INCREASING: Final = "total_increasing"
STATE_CLASSES: Final[list[str]] = [
STATE_CLASS_MEASUREMENT,
STATE_CLASS_TOTAL,
STATE_CLASS_TOTAL_INCREASING,
]
@ -214,9 +217,10 @@ class SensorEntity(Entity):
report_issue = self._suggest_report_issue()
_LOGGER.warning(
"Entity %s (%s) with state_class %s has set last_reset. Setting "
"last_reset is deprecated and will be unsupported from Home "
"Assistant Core 2021.11. Please update your configuration if "
"state_class is manually configured, otherwise %s",
"last_reset for entities with state_class other than 'total' is "
"deprecated and will be removed from Home Assistant Core 2021.11. "
"Please update your configuration if state_class is manually "
"configured, otherwise %s",
self.entity_id,
type(self),
self.state_class,

View File

@ -15,6 +15,7 @@ from homeassistant.components.sensor import (
DEVICE_CLASS_PRESSURE,
DEVICE_CLASS_TEMPERATURE,
STATE_CLASS_MEASUREMENT,
STATE_CLASS_TOTAL,
STATE_CLASS_TOTAL_INCREASING,
STATE_CLASSES,
)
@ -56,10 +57,12 @@ DEVICE_CLASS_STATISTICS: dict[str, dict[str, set[str]]] = {
DEVICE_CLASS_GAS: {"sum"},
DEVICE_CLASS_MONETARY: {"sum"},
},
STATE_CLASS_TOTAL: {},
STATE_CLASS_TOTAL_INCREASING: {},
}
DEFAULT_STATISTICS = {
STATE_CLASS_MEASUREMENT: {"mean", "min", "max"},
STATE_CLASS_TOTAL: {"sum"},
STATE_CLASS_TOTAL_INCREASING: {"sum"},
}
@ -389,7 +392,7 @@ def compile_statistics( # noqa: C901
for fstate, state in fstates:
# Deprecated, will be removed in Home Assistant 2021.10
# Deprecated, will be removed in Home Assistant 2021.11
if (
"last_reset" not in state.attributes
and state_class == STATE_CLASS_MEASUREMENT

View File

@ -8,6 +8,7 @@ import pytest
from homeassistant.components.energy import data
from homeassistant.components.sensor import (
ATTR_STATE_CLASS,
STATE_CLASS_TOTAL,
STATE_CLASS_TOTAL_INCREASING,
)
from homeassistant.components.sensor.recorder import compile_statistics
@ -357,7 +358,7 @@ async def test_cost_sensor_handle_gas(hass, hass_storage) -> None:
assert state.state == "50.0"
@pytest.mark.parametrize("state_class", [None])
@pytest.mark.parametrize("state_class", [None, STATE_CLASS_TOTAL])
async def test_cost_sensor_wrong_state_class(
hass, hass_storage, caplog, state_class
) -> None:

View File

@ -45,10 +45,11 @@ async def test_deprecated_last_reset(hass, caplog, enable_custom_integrations):
assert (
"Entity sensor.test (<class 'custom_components.test.sensor.MockSensor'>) "
"with state_class measurement has set last_reset. Setting last_reset is "
"deprecated and will be unsupported from Home Assistant Core 2021.11. Please "
"update your configuration if state_class is manually configured, otherwise "
"report it to the custom component author."
"with state_class measurement has set last_reset. Setting last_reset for "
"entities with state_class other than 'total' is deprecated and will be "
"removed from Home Assistant Core 2021.11. Please update your configuration if "
"state_class is manually configured, otherwise report it to the custom "
"component author."
) in caplog.text

View File

@ -191,7 +191,7 @@ def test_compile_hourly_statistics_unsupported(hass_recorder, caplog, attributes
assert "Error while processing event StatisticsTask" not in caplog.text
@pytest.mark.parametrize("state_class", ["measurement"])
@pytest.mark.parametrize("state_class", ["measurement", "total"])
@pytest.mark.parametrize(
"device_class,unit,native_unit,factor",
[
@ -349,6 +349,88 @@ def test_compile_hourly_sum_statistics_amount_reset_every_state_change(
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", "", "", 1),
("gas", "ft³", "", 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]),
"sum": approx(factor * 30.0),
},
{
"statistic_id": "sensor.test1",
"start": process_timestamp_to_utc_isoformat(zero + timedelta(hours=2)),
"max": None,
"mean": None,
"min": None,
"last_reset": None,
"state": approx(factor * seq[8]),
"sum": approx(factor * 60.0),
},
]
}
assert "Error while processing event StatisticsTask" not in caplog.text
@pytest.mark.parametrize(
"device_class,unit,native_unit,factor",
[