mirror of
https://github.com/home-assistant/core.git
synced 2025-07-19 03:07:37 +00:00
Enforce device class for gas and energy sensors used by energy dashboard (#56218)
* Enforce device class for gas and energy sensors used by energy dashboard * Adjust tests
This commit is contained in:
parent
aaa62dadec
commit
bac55b78fe
@ -1,12 +1,13 @@
|
|||||||
"""Validate the energy preferences provide valid data."""
|
"""Validate the energy preferences provide valid data."""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from collections.abc import Sequence
|
from collections.abc import Mapping, Sequence
|
||||||
import dataclasses
|
import dataclasses
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from homeassistant.components import recorder, sensor
|
from homeassistant.components import recorder, sensor
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
|
ATTR_DEVICE_CLASS,
|
||||||
ENERGY_KILO_WATT_HOUR,
|
ENERGY_KILO_WATT_HOUR,
|
||||||
ENERGY_WATT_HOUR,
|
ENERGY_WATT_HOUR,
|
||||||
STATE_UNAVAILABLE,
|
STATE_UNAVAILABLE,
|
||||||
@ -19,14 +20,16 @@ from homeassistant.core import HomeAssistant, callback, valid_entity_id
|
|||||||
from . import data
|
from . import data
|
||||||
from .const import DOMAIN
|
from .const import DOMAIN
|
||||||
|
|
||||||
ENERGY_USAGE_UNITS = (ENERGY_KILO_WATT_HOUR, ENERGY_WATT_HOUR)
|
ENERGY_USAGE_DEVICE_CLASSES = (sensor.DEVICE_CLASS_ENERGY,)
|
||||||
|
ENERGY_USAGE_UNITS = {
|
||||||
|
sensor.DEVICE_CLASS_ENERGY: (ENERGY_KILO_WATT_HOUR, ENERGY_WATT_HOUR)
|
||||||
|
}
|
||||||
ENERGY_UNIT_ERROR = "entity_unexpected_unit_energy"
|
ENERGY_UNIT_ERROR = "entity_unexpected_unit_energy"
|
||||||
GAS_USAGE_UNITS = (
|
GAS_USAGE_DEVICE_CLASSES = (sensor.DEVICE_CLASS_ENERGY, sensor.DEVICE_CLASS_GAS)
|
||||||
ENERGY_WATT_HOUR,
|
GAS_USAGE_UNITS = {
|
||||||
ENERGY_KILO_WATT_HOUR,
|
sensor.DEVICE_CLASS_ENERGY: (ENERGY_WATT_HOUR, ENERGY_KILO_WATT_HOUR),
|
||||||
VOLUME_CUBIC_METERS,
|
sensor.DEVICE_CLASS_GAS: (VOLUME_CUBIC_METERS, VOLUME_CUBIC_FEET),
|
||||||
VOLUME_CUBIC_FEET,
|
}
|
||||||
)
|
|
||||||
GAS_UNIT_ERROR = "entity_unexpected_unit_gas"
|
GAS_UNIT_ERROR = "entity_unexpected_unit_gas"
|
||||||
|
|
||||||
|
|
||||||
@ -59,7 +62,8 @@ class EnergyPreferencesValidation:
|
|||||||
def _async_validate_usage_stat(
|
def _async_validate_usage_stat(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
stat_value: str,
|
stat_value: str,
|
||||||
allowed_units: Sequence[str],
|
allowed_device_classes: Sequence[str],
|
||||||
|
allowed_units: Mapping[str, Sequence[str]],
|
||||||
unit_error: str,
|
unit_error: str,
|
||||||
result: list[ValidationIssue],
|
result: list[ValidationIssue],
|
||||||
) -> None:
|
) -> None:
|
||||||
@ -106,19 +110,29 @@ def _async_validate_usage_stat(
|
|||||||
ValidationIssue("entity_negative_state", stat_value, current_value)
|
ValidationIssue("entity_negative_state", stat_value, current_value)
|
||||||
)
|
)
|
||||||
|
|
||||||
unit = state.attributes.get("unit_of_measurement")
|
device_class = state.attributes.get(ATTR_DEVICE_CLASS)
|
||||||
|
if device_class not in allowed_device_classes:
|
||||||
|
result.append(
|
||||||
|
ValidationIssue(
|
||||||
|
"entity_unexpected_device_class",
|
||||||
|
stat_value,
|
||||||
|
device_class,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
unit = state.attributes.get("unit_of_measurement")
|
||||||
|
|
||||||
if unit not in allowed_units:
|
if device_class and unit not in allowed_units.get(device_class, []):
|
||||||
result.append(ValidationIssue(unit_error, stat_value, unit))
|
result.append(ValidationIssue(unit_error, stat_value, unit))
|
||||||
|
|
||||||
state_class = state.attributes.get("state_class")
|
state_class = state.attributes.get(sensor.ATTR_STATE_CLASS)
|
||||||
|
|
||||||
supported_state_classes = [
|
allowed_state_classes = [
|
||||||
sensor.STATE_CLASS_MEASUREMENT,
|
sensor.STATE_CLASS_MEASUREMENT,
|
||||||
sensor.STATE_CLASS_TOTAL,
|
sensor.STATE_CLASS_TOTAL,
|
||||||
sensor.STATE_CLASS_TOTAL_INCREASING,
|
sensor.STATE_CLASS_TOTAL_INCREASING,
|
||||||
]
|
]
|
||||||
if state_class not in supported_state_classes:
|
if state_class not in allowed_state_classes:
|
||||||
result.append(
|
result.append(
|
||||||
ValidationIssue(
|
ValidationIssue(
|
||||||
"entity_unexpected_state_class_total_increasing",
|
"entity_unexpected_state_class_total_increasing",
|
||||||
@ -236,6 +250,7 @@ async def async_validate(hass: HomeAssistant) -> EnergyPreferencesValidation:
|
|||||||
_async_validate_usage_stat(
|
_async_validate_usage_stat(
|
||||||
hass,
|
hass,
|
||||||
flow["stat_energy_from"],
|
flow["stat_energy_from"],
|
||||||
|
ENERGY_USAGE_DEVICE_CLASSES,
|
||||||
ENERGY_USAGE_UNITS,
|
ENERGY_USAGE_UNITS,
|
||||||
ENERGY_UNIT_ERROR,
|
ENERGY_UNIT_ERROR,
|
||||||
source_result,
|
source_result,
|
||||||
@ -258,6 +273,7 @@ async def async_validate(hass: HomeAssistant) -> EnergyPreferencesValidation:
|
|||||||
_async_validate_usage_stat(
|
_async_validate_usage_stat(
|
||||||
hass,
|
hass,
|
||||||
flow["stat_energy_to"],
|
flow["stat_energy_to"],
|
||||||
|
ENERGY_USAGE_DEVICE_CLASSES,
|
||||||
ENERGY_USAGE_UNITS,
|
ENERGY_USAGE_UNITS,
|
||||||
ENERGY_UNIT_ERROR,
|
ENERGY_UNIT_ERROR,
|
||||||
source_result,
|
source_result,
|
||||||
@ -282,6 +298,7 @@ async def async_validate(hass: HomeAssistant) -> EnergyPreferencesValidation:
|
|||||||
_async_validate_usage_stat(
|
_async_validate_usage_stat(
|
||||||
hass,
|
hass,
|
||||||
source["stat_energy_from"],
|
source["stat_energy_from"],
|
||||||
|
GAS_USAGE_DEVICE_CLASSES,
|
||||||
GAS_USAGE_UNITS,
|
GAS_USAGE_UNITS,
|
||||||
GAS_UNIT_ERROR,
|
GAS_UNIT_ERROR,
|
||||||
source_result,
|
source_result,
|
||||||
@ -304,6 +321,7 @@ async def async_validate(hass: HomeAssistant) -> EnergyPreferencesValidation:
|
|||||||
_async_validate_usage_stat(
|
_async_validate_usage_stat(
|
||||||
hass,
|
hass,
|
||||||
source["stat_energy_from"],
|
source["stat_energy_from"],
|
||||||
|
ENERGY_USAGE_DEVICE_CLASSES,
|
||||||
ENERGY_USAGE_UNITS,
|
ENERGY_USAGE_UNITS,
|
||||||
ENERGY_UNIT_ERROR,
|
ENERGY_UNIT_ERROR,
|
||||||
source_result,
|
source_result,
|
||||||
@ -313,6 +331,7 @@ async def async_validate(hass: HomeAssistant) -> EnergyPreferencesValidation:
|
|||||||
_async_validate_usage_stat(
|
_async_validate_usage_stat(
|
||||||
hass,
|
hass,
|
||||||
source["stat_energy_from"],
|
source["stat_energy_from"],
|
||||||
|
ENERGY_USAGE_DEVICE_CLASSES,
|
||||||
ENERGY_USAGE_UNITS,
|
ENERGY_USAGE_UNITS,
|
||||||
ENERGY_UNIT_ERROR,
|
ENERGY_UNIT_ERROR,
|
||||||
source_result,
|
source_result,
|
||||||
@ -320,6 +339,7 @@ async def async_validate(hass: HomeAssistant) -> EnergyPreferencesValidation:
|
|||||||
_async_validate_usage_stat(
|
_async_validate_usage_stat(
|
||||||
hass,
|
hass,
|
||||||
source["stat_energy_to"],
|
source["stat_energy_to"],
|
||||||
|
ENERGY_USAGE_DEVICE_CLASSES,
|
||||||
ENERGY_USAGE_UNITS,
|
ENERGY_USAGE_UNITS,
|
||||||
ENERGY_UNIT_ERROR,
|
ENERGY_UNIT_ERROR,
|
||||||
source_result,
|
source_result,
|
||||||
@ -331,6 +351,7 @@ async def async_validate(hass: HomeAssistant) -> EnergyPreferencesValidation:
|
|||||||
_async_validate_usage_stat(
|
_async_validate_usage_stat(
|
||||||
hass,
|
hass,
|
||||||
device["stat_consumption"],
|
device["stat_consumption"],
|
||||||
|
ENERGY_USAGE_DEVICE_CLASSES,
|
||||||
ENERGY_USAGE_UNITS,
|
ENERGY_USAGE_UNITS,
|
||||||
ENERGY_UNIT_ERROR,
|
ENERGY_UNIT_ERROR,
|
||||||
device_result,
|
device_result,
|
||||||
|
@ -45,7 +45,11 @@ async def test_validation(hass, mock_energy_manager):
|
|||||||
hass.states.async_set(
|
hass.states.async_set(
|
||||||
f"sensor.{key}",
|
f"sensor.{key}",
|
||||||
"123",
|
"123",
|
||||||
{"unit_of_measurement": "kWh", "state_class": "total_increasing"},
|
{
|
||||||
|
"device_class": "energy",
|
||||||
|
"unit_of_measurement": "kWh",
|
||||||
|
"state_class": "total_increasing",
|
||||||
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
await mock_energy_manager.async_update(
|
await mock_energy_manager.async_update(
|
||||||
@ -142,7 +146,11 @@ async def test_validation_device_consumption_entity_unexpected_unit(
|
|||||||
hass.states.async_set(
|
hass.states.async_set(
|
||||||
"sensor.unexpected_unit",
|
"sensor.unexpected_unit",
|
||||||
"10.10",
|
"10.10",
|
||||||
{"unit_of_measurement": "beers", "state_class": "total_increasing"},
|
{
|
||||||
|
"device_class": "energy",
|
||||||
|
"unit_of_measurement": "beers",
|
||||||
|
"state_class": "total_increasing",
|
||||||
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
assert (await validate.async_validate(hass)).as_dict() == {
|
assert (await validate.async_validate(hass)).as_dict() == {
|
||||||
@ -194,7 +202,11 @@ async def test_validation_solar(hass, mock_energy_manager):
|
|||||||
hass.states.async_set(
|
hass.states.async_set(
|
||||||
"sensor.solar_production",
|
"sensor.solar_production",
|
||||||
"10.10",
|
"10.10",
|
||||||
{"unit_of_measurement": "beers", "state_class": "total_increasing"},
|
{
|
||||||
|
"device_class": "energy",
|
||||||
|
"unit_of_measurement": "beers",
|
||||||
|
"state_class": "total_increasing",
|
||||||
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
assert (await validate.async_validate(hass)).as_dict() == {
|
assert (await validate.async_validate(hass)).as_dict() == {
|
||||||
@ -227,12 +239,20 @@ async def test_validation_battery(hass, mock_energy_manager):
|
|||||||
hass.states.async_set(
|
hass.states.async_set(
|
||||||
"sensor.battery_import",
|
"sensor.battery_import",
|
||||||
"10.10",
|
"10.10",
|
||||||
{"unit_of_measurement": "beers", "state_class": "total_increasing"},
|
{
|
||||||
|
"device_class": "energy",
|
||||||
|
"unit_of_measurement": "beers",
|
||||||
|
"state_class": "total_increasing",
|
||||||
|
},
|
||||||
)
|
)
|
||||||
hass.states.async_set(
|
hass.states.async_set(
|
||||||
"sensor.battery_export",
|
"sensor.battery_export",
|
||||||
"10.10",
|
"10.10",
|
||||||
{"unit_of_measurement": "beers", "state_class": "total_increasing"},
|
{
|
||||||
|
"device_class": "energy",
|
||||||
|
"unit_of_measurement": "beers",
|
||||||
|
"state_class": "total_increasing",
|
||||||
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
assert (await validate.async_validate(hass)).as_dict() == {
|
assert (await validate.async_validate(hass)).as_dict() == {
|
||||||
@ -282,12 +302,20 @@ async def test_validation_grid(hass, mock_energy_manager, mock_is_entity_recorde
|
|||||||
hass.states.async_set(
|
hass.states.async_set(
|
||||||
"sensor.grid_consumption_1",
|
"sensor.grid_consumption_1",
|
||||||
"10.10",
|
"10.10",
|
||||||
{"unit_of_measurement": "beers", "state_class": "total_increasing"},
|
{
|
||||||
|
"device_class": "energy",
|
||||||
|
"unit_of_measurement": "beers",
|
||||||
|
"state_class": "total_increasing",
|
||||||
|
},
|
||||||
)
|
)
|
||||||
hass.states.async_set(
|
hass.states.async_set(
|
||||||
"sensor.grid_production_1",
|
"sensor.grid_production_1",
|
||||||
"10.10",
|
"10.10",
|
||||||
{"unit_of_measurement": "beers", "state_class": "total_increasing"},
|
{
|
||||||
|
"device_class": "energy",
|
||||||
|
"unit_of_measurement": "beers",
|
||||||
|
"state_class": "total_increasing",
|
||||||
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
assert (await validate.async_validate(hass)).as_dict() == {
|
assert (await validate.async_validate(hass)).as_dict() == {
|
||||||
@ -324,12 +352,20 @@ async def test_validation_grid_price_not_exist(hass, mock_energy_manager):
|
|||||||
hass.states.async_set(
|
hass.states.async_set(
|
||||||
"sensor.grid_consumption_1",
|
"sensor.grid_consumption_1",
|
||||||
"10.10",
|
"10.10",
|
||||||
{"unit_of_measurement": "kWh", "state_class": "total_increasing"},
|
{
|
||||||
|
"device_class": "energy",
|
||||||
|
"unit_of_measurement": "kWh",
|
||||||
|
"state_class": "total_increasing",
|
||||||
|
},
|
||||||
)
|
)
|
||||||
hass.states.async_set(
|
hass.states.async_set(
|
||||||
"sensor.grid_production_1",
|
"sensor.grid_production_1",
|
||||||
"10.10",
|
"10.10",
|
||||||
{"unit_of_measurement": "kWh", "state_class": "total_increasing"},
|
{
|
||||||
|
"device_class": "energy",
|
||||||
|
"unit_of_measurement": "kWh",
|
||||||
|
"state_class": "total_increasing",
|
||||||
|
},
|
||||||
)
|
)
|
||||||
await mock_energy_manager.async_update(
|
await mock_energy_manager.async_update(
|
||||||
{
|
{
|
||||||
@ -402,7 +438,11 @@ async def test_validation_grid_price_errors(
|
|||||||
hass.states.async_set(
|
hass.states.async_set(
|
||||||
"sensor.grid_consumption_1",
|
"sensor.grid_consumption_1",
|
||||||
"10.10",
|
"10.10",
|
||||||
{"unit_of_measurement": "kWh", "state_class": "total_increasing"},
|
{
|
||||||
|
"device_class": "energy",
|
||||||
|
"unit_of_measurement": "kWh",
|
||||||
|
"state_class": "total_increasing",
|
||||||
|
},
|
||||||
)
|
)
|
||||||
hass.states.async_set(
|
hass.states.async_set(
|
||||||
"sensor.grid_price_1",
|
"sensor.grid_price_1",
|
||||||
@ -454,18 +494,50 @@ async def test_validation_gas(hass, mock_energy_manager, mock_is_entity_recorded
|
|||||||
"stat_energy_from": "sensor.gas_consumption_2",
|
"stat_energy_from": "sensor.gas_consumption_2",
|
||||||
"stat_cost": "sensor.gas_cost_2",
|
"stat_cost": "sensor.gas_cost_2",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"type": "gas",
|
||||||
|
"stat_energy_from": "sensor.gas_consumption_3",
|
||||||
|
"stat_cost": "sensor.gas_cost_2",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "gas",
|
||||||
|
"stat_energy_from": "sensor.gas_consumption_4",
|
||||||
|
"stat_cost": "sensor.gas_cost_2",
|
||||||
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
hass.states.async_set(
|
hass.states.async_set(
|
||||||
"sensor.gas_consumption_1",
|
"sensor.gas_consumption_1",
|
||||||
"10.10",
|
"10.10",
|
||||||
{"unit_of_measurement": "beers", "state_class": "total_increasing"},
|
{
|
||||||
|
"device_class": "energy",
|
||||||
|
"unit_of_measurement": "beers",
|
||||||
|
"state_class": "total_increasing",
|
||||||
|
},
|
||||||
)
|
)
|
||||||
hass.states.async_set(
|
hass.states.async_set(
|
||||||
"sensor.gas_consumption_2",
|
"sensor.gas_consumption_2",
|
||||||
"10.10",
|
"10.10",
|
||||||
{"unit_of_measurement": "kWh", "state_class": "total_increasing"},
|
{
|
||||||
|
"device_class": "energy",
|
||||||
|
"unit_of_measurement": "kWh",
|
||||||
|
"state_class": "total_increasing",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
hass.states.async_set(
|
||||||
|
"sensor.gas_consumption_3",
|
||||||
|
"10.10",
|
||||||
|
{
|
||||||
|
"device_class": "gas",
|
||||||
|
"unit_of_measurement": "m³",
|
||||||
|
"state_class": "total_increasing",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
hass.states.async_set(
|
||||||
|
"sensor.gas_consumption_4",
|
||||||
|
"10.10",
|
||||||
|
{"unit_of_measurement": "beers", "state_class": "total_increasing"},
|
||||||
)
|
)
|
||||||
hass.states.async_set(
|
hass.states.async_set(
|
||||||
"sensor.gas_cost_2",
|
"sensor.gas_cost_2",
|
||||||
@ -488,6 +560,14 @@ async def test_validation_gas(hass, mock_energy_manager, mock_is_entity_recorded
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
[],
|
[],
|
||||||
|
[],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"type": "entity_unexpected_device_class",
|
||||||
|
"identifier": "sensor.gas_consumption_4",
|
||||||
|
"value": None,
|
||||||
|
},
|
||||||
|
],
|
||||||
],
|
],
|
||||||
"device_consumption": [],
|
"device_consumption": [],
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user