Add check that sensor state classes are used only with valid unit of measurements (#141444)

This commit is contained in:
Robert Resch 2025-03-26 13:52:00 +01:00 committed by GitHub
parent aa493ff97d
commit f842640249
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 51 additions and 0 deletions

View File

@ -44,6 +44,7 @@ from .const import ( # noqa: F401
DEVICE_CLASSES_SCHEMA,
DOMAIN,
NON_NUMERIC_DEVICE_CLASSES,
STATE_CLASS_UNITS,
STATE_CLASSES,
STATE_CLASSES_SCHEMA,
UNIT_CONVERTERS,
@ -713,6 +714,18 @@ class SensorEntity(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
report_issue,
)
# Validate unit of measurement used for sensors with a state class
if (
state_class
and (units := STATE_CLASS_UNITS.get(state_class)) is not None
and native_unit_of_measurement not in units
):
raise ValueError(
f"Sensor {self.entity_id} ({type(self)}) is using native unit of "
f"measurement '{native_unit_of_measurement}' which is not a valid unit "
f"for the state class ('{state_class}') it is using; expected one of {units};"
)
return value
def _display_precision_or_none(self) -> int | None:

View File

@ -699,3 +699,8 @@ DEVICE_CLASS_STATE_CLASSES: dict[SensorDeviceClass, set[SensorStateClass]] = {
SensorDeviceClass.WIND_DIRECTION: {SensorStateClass.MEASUREMENT_ANGLE},
SensorDeviceClass.WIND_SPEED: {SensorStateClass.MEASUREMENT},
}
STATE_CLASS_UNITS: dict[SensorStateClass | str, set[type[StrEnum] | str | None]] = {
SensorStateClass.MEASUREMENT_ANGLE: {DEGREE},
}

View File

@ -24,6 +24,7 @@ from homeassistant.components.sensor import (
async_rounded_state,
async_update_suggested_units,
)
from homeassistant.components.sensor.const import STATE_CLASS_UNITS
from homeassistant.config_entries import ConfigEntry, ConfigFlow
from homeassistant.const import (
ATTR_UNIT_OF_MEASUREMENT,
@ -2005,6 +2006,7 @@ async def test_non_numeric_device_class_with_unit_of_measurement(
SensorDeviceClass.VOLUME,
SensorDeviceClass.WATER,
SensorDeviceClass.WEIGHT,
SensorDeviceClass.WIND_DIRECTION,
SensorDeviceClass.WIND_SPEED,
],
)
@ -2035,6 +2037,37 @@ async def test_device_classes_with_invalid_unit_of_measurement(
) in caplog.text
@pytest.mark.parametrize(
"state_class",
[SensorStateClass.MEASUREMENT_ANGLE],
)
async def test_state_classes_with_invalid_unit_of_measurement(
hass: HomeAssistant,
caplog: pytest.LogCaptureFixture,
state_class: SensorStateClass,
) -> None:
"""Test error when unit of measurement is not valid for used state class."""
entity0 = MockSensor(
name="Test",
native_value="1.0",
state_class=state_class,
native_unit_of_measurement="INVALID!",
)
setup_test_component_platform(hass, sensor.DOMAIN, [entity0])
units = {
str(unit) if unit else "no unit of measurement"
for unit in STATE_CLASS_UNITS.get(state_class, set())
}
assert await async_setup_component(hass, "sensor", {"sensor": {"platform": "test"}})
await hass.async_block_till_done()
assert (
f"Sensor sensor.test ({entity0.__class__}) is using native unit of "
"measurement 'INVALID!' which is not a valid unit "
f"for the state class ('{state_class}') it is using; expected one of {units};"
) in caplog.text
@pytest.mark.parametrize(
("device_class", "state_class", "unit"),
[