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:
parent
2634949999
commit
b99a22cd4d
5 changed files with 101 additions and 10 deletions
|
@ -96,11 +96,14 @@ 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. net energy consumption
|
||||||
|
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,
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -214,9 +217,10 @@ 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 is deprecated and will be unsupported from Home "
|
"last_reset for entities with state_class other than 'total' is "
|
||||||
"Assistant Core 2021.11. Please update your configuration if "
|
"deprecated and will be removed from Home Assistant Core 2021.11. "
|
||||||
"state_class is manually configured, otherwise %s",
|
"Please update your configuration if state_class is manually "
|
||||||
|
"configured, otherwise %s",
|
||||||
self.entity_id,
|
self.entity_id,
|
||||||
type(self),
|
type(self),
|
||||||
self.state_class,
|
self.state_class,
|
||||||
|
|
|
@ -15,6 +15,7 @@ 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,
|
||||||
)
|
)
|
||||||
|
@ -56,10 +57,12 @@ DEVICE_CLASS_STATISTICS: dict[str, dict[str, set[str]]] = {
|
||||||
DEVICE_CLASS_GAS: {"sum"},
|
DEVICE_CLASS_GAS: {"sum"},
|
||||||
DEVICE_CLASS_MONETARY: {"sum"},
|
DEVICE_CLASS_MONETARY: {"sum"},
|
||||||
},
|
},
|
||||||
|
STATE_CLASS_TOTAL: {},
|
||||||
STATE_CLASS_TOTAL_INCREASING: {},
|
STATE_CLASS_TOTAL_INCREASING: {},
|
||||||
}
|
}
|
||||||
DEFAULT_STATISTICS = {
|
DEFAULT_STATISTICS = {
|
||||||
STATE_CLASS_MEASUREMENT: {"mean", "min", "max"},
|
STATE_CLASS_MEASUREMENT: {"mean", "min", "max"},
|
||||||
|
STATE_CLASS_TOTAL: {"sum"},
|
||||||
STATE_CLASS_TOTAL_INCREASING: {"sum"},
|
STATE_CLASS_TOTAL_INCREASING: {"sum"},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -389,7 +392,7 @@ def compile_statistics( # noqa: C901
|
||||||
|
|
||||||
for fstate, state in fstates:
|
for fstate, state in fstates:
|
||||||
|
|
||||||
# Deprecated, will be removed in Home Assistant 2021.10
|
# Deprecated, will be removed in Home Assistant 2021.11
|
||||||
if (
|
if (
|
||||||
"last_reset" not in state.attributes
|
"last_reset" not in state.attributes
|
||||||
and state_class == STATE_CLASS_MEASUREMENT
|
and state_class == STATE_CLASS_MEASUREMENT
|
||||||
|
|
|
@ -8,6 +8,7 @@ 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_STATE_CLASS,
|
ATTR_STATE_CLASS,
|
||||||
|
STATE_CLASS_TOTAL,
|
||||||
STATE_CLASS_TOTAL_INCREASING,
|
STATE_CLASS_TOTAL_INCREASING,
|
||||||
)
|
)
|
||||||
from homeassistant.components.sensor.recorder import compile_statistics
|
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"
|
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(
|
async def test_cost_sensor_wrong_state_class(
|
||||||
hass, hass_storage, caplog, state_class
|
hass, hass_storage, caplog, state_class
|
||||||
) -> None:
|
) -> None:
|
||||||
|
|
|
@ -45,10 +45,11 @@ 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 is "
|
"with state_class measurement has set last_reset. Setting last_reset for "
|
||||||
"deprecated and will be unsupported from Home Assistant Core 2021.11. Please "
|
"entities with state_class other than 'total' is deprecated and will be "
|
||||||
"update your configuration if state_class is manually configured, otherwise "
|
"removed from Home Assistant Core 2021.11. Please update your configuration if "
|
||||||
"report it to the custom component author."
|
"state_class is manually configured, otherwise report it to the custom "
|
||||||
|
"component author."
|
||||||
) in caplog.text
|
) in caplog.text
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -191,7 +191,7 @@ 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"])
|
@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",
|
||||||
[
|
[
|
||||||
|
@ -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
|
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]),
|
||||||
|
"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(
|
@pytest.mark.parametrize(
|
||||||
"device_class,unit,native_unit,factor",
|
"device_class,unit,native_unit,factor",
|
||||||
[
|
[
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue