mirror of
https://github.com/home-assistant/core.git
synced 2025-07-15 09:17:10 +00:00
Ensure mqtt sensor unit of measurement validation for state class measurement_angle
(#145648)
This commit is contained in:
parent
ddf611bfdf
commit
192aa76cd7
@ -39,6 +39,7 @@ from homeassistant.components.light import (
|
|||||||
from homeassistant.components.sensor import (
|
from homeassistant.components.sensor import (
|
||||||
CONF_STATE_CLASS,
|
CONF_STATE_CLASS,
|
||||||
DEVICE_CLASS_UNITS,
|
DEVICE_CLASS_UNITS,
|
||||||
|
STATE_CLASS_UNITS,
|
||||||
SensorDeviceClass,
|
SensorDeviceClass,
|
||||||
SensorStateClass,
|
SensorStateClass,
|
||||||
)
|
)
|
||||||
@ -640,6 +641,13 @@ def validate_sensor_platform_config(
|
|||||||
):
|
):
|
||||||
errors[CONF_UNIT_OF_MEASUREMENT] = "invalid_uom"
|
errors[CONF_UNIT_OF_MEASUREMENT] = "invalid_uom"
|
||||||
|
|
||||||
|
if (
|
||||||
|
(state_class := config.get(CONF_STATE_CLASS)) is not None
|
||||||
|
and state_class in STATE_CLASS_UNITS
|
||||||
|
and config.get(CONF_UNIT_OF_MEASUREMENT) not in STATE_CLASS_UNITS[state_class]
|
||||||
|
):
|
||||||
|
errors[CONF_UNIT_OF_MEASUREMENT] = "invalid_uom_for_state_class"
|
||||||
|
|
||||||
return errors
|
return errors
|
||||||
|
|
||||||
|
|
||||||
@ -676,11 +684,19 @@ class PlatformField:
|
|||||||
@callback
|
@callback
|
||||||
def unit_of_measurement_selector(user_data: dict[str, Any | None]) -> Selector:
|
def unit_of_measurement_selector(user_data: dict[str, Any | None]) -> Selector:
|
||||||
"""Return a context based unit of measurement selector."""
|
"""Return a context based unit of measurement selector."""
|
||||||
|
|
||||||
|
if (state_class := user_data.get(CONF_STATE_CLASS)) in STATE_CLASS_UNITS:
|
||||||
|
return SelectSelector(
|
||||||
|
SelectSelectorConfig(
|
||||||
|
options=[str(uom) for uom in STATE_CLASS_UNITS[state_class]],
|
||||||
|
sort=True,
|
||||||
|
custom_value=True,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
if (
|
if (
|
||||||
user_data is None
|
device_class := user_data.get(CONF_DEVICE_CLASS)
|
||||||
or (device_class := user_data.get(CONF_DEVICE_CLASS)) is None
|
) is None or device_class not in DEVICE_CLASS_UNITS:
|
||||||
or device_class not in DEVICE_CLASS_UNITS
|
|
||||||
):
|
|
||||||
return TEXT_SELECTOR
|
return TEXT_SELECTOR
|
||||||
return SelectSelector(
|
return SelectSelector(
|
||||||
SelectSelectorConfig(
|
SelectSelectorConfig(
|
||||||
|
@ -14,6 +14,7 @@ from homeassistant.components.sensor import (
|
|||||||
DEVICE_CLASS_UNITS,
|
DEVICE_CLASS_UNITS,
|
||||||
DEVICE_CLASSES_SCHEMA,
|
DEVICE_CLASSES_SCHEMA,
|
||||||
ENTITY_ID_FORMAT,
|
ENTITY_ID_FORMAT,
|
||||||
|
STATE_CLASS_UNITS,
|
||||||
STATE_CLASSES_SCHEMA,
|
STATE_CLASSES_SCHEMA,
|
||||||
RestoreSensor,
|
RestoreSensor,
|
||||||
SensorDeviceClass,
|
SensorDeviceClass,
|
||||||
@ -117,6 +118,17 @@ def validate_sensor_state_and_device_class_config(config: ConfigType) -> ConfigT
|
|||||||
f"got `{CONF_DEVICE_CLASS}` '{device_class}'"
|
f"got `{CONF_DEVICE_CLASS}` '{device_class}'"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if (
|
||||||
|
(state_class := config.get(CONF_STATE_CLASS)) is not None
|
||||||
|
and state_class in STATE_CLASS_UNITS
|
||||||
|
and (unit_of_measurement := config.get(CONF_UNIT_OF_MEASUREMENT))
|
||||||
|
not in STATE_CLASS_UNITS[state_class]
|
||||||
|
):
|
||||||
|
raise vol.Invalid(
|
||||||
|
f"The unit of measurement '{unit_of_measurement}' is not valid "
|
||||||
|
f"together with state class '{state_class}'"
|
||||||
|
)
|
||||||
|
|
||||||
if (device_class := config.get(CONF_DEVICE_CLASS)) is None or (
|
if (device_class := config.get(CONF_DEVICE_CLASS)) is None or (
|
||||||
unit_of_measurement := config.get(CONF_UNIT_OF_MEASUREMENT)
|
unit_of_measurement := config.get(CONF_UNIT_OF_MEASUREMENT)
|
||||||
) is None:
|
) is None:
|
||||||
|
@ -644,6 +644,7 @@
|
|||||||
"invalid_template": "Invalid template",
|
"invalid_template": "Invalid template",
|
||||||
"invalid_supported_color_modes": "Invalid supported color modes selection",
|
"invalid_supported_color_modes": "Invalid supported color modes selection",
|
||||||
"invalid_uom": "The unit of measurement \"{unit_of_measurement}\" is not supported by the selected device class, please either remove the device class, select a device class which supports \"{unit_of_measurement}\", or pick a supported unit of measurement from the list",
|
"invalid_uom": "The unit of measurement \"{unit_of_measurement}\" is not supported by the selected device class, please either remove the device class, select a device class which supports \"{unit_of_measurement}\", or pick a supported unit of measurement from the list",
|
||||||
|
"invalid_uom_for_state_class": "The unit of measurement \"{unit_of_measurement}\" is not supported by the selected state class, please either remove the state class, select a state class which supports \"{unit_of_measurement}\", or pick a supported unit of measurement from the list",
|
||||||
"invalid_url": "Invalid URL",
|
"invalid_url": "Invalid URL",
|
||||||
"last_reset_not_with_state_class_total": "The last reset value template option should be used with state class 'Total' only",
|
"last_reset_not_with_state_class_total": "The last reset value template option should be used with state class 'Total' only",
|
||||||
"max_below_min_kelvin": "Max Kelvin value should be greater than min Kelvin value",
|
"max_below_min_kelvin": "Max Kelvin value should be greater than min Kelvin value",
|
||||||
|
@ -3038,7 +3038,15 @@ async def test_migrate_of_incompatible_config_entry(
|
|||||||
{
|
{
|
||||||
"state_class": "measurement",
|
"state_class": "measurement",
|
||||||
},
|
},
|
||||||
(),
|
(
|
||||||
|
(
|
||||||
|
{
|
||||||
|
"state_class": "measurement_angle",
|
||||||
|
"unit_of_measurement": "deg",
|
||||||
|
},
|
||||||
|
{"unit_of_measurement": "invalid_uom_for_state_class"},
|
||||||
|
),
|
||||||
|
),
|
||||||
{
|
{
|
||||||
"state_topic": "test-topic",
|
"state_topic": "test-topic",
|
||||||
},
|
},
|
||||||
|
@ -995,6 +995,32 @@ async def test_invalid_state_class(
|
|||||||
assert "expected SensorStateClass or one of" in caplog.text
|
assert "expected SensorStateClass or one of" in caplog.text
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"hass_config",
|
||||||
|
[
|
||||||
|
{
|
||||||
|
mqtt.DOMAIN: {
|
||||||
|
sensor.DOMAIN: {
|
||||||
|
"name": "test",
|
||||||
|
"state_topic": "test-topic",
|
||||||
|
"state_class": "measurement_angle",
|
||||||
|
"unit_of_measurement": "deg",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
)
|
||||||
|
async def test_invalid_state_class_with_unit_of_measurement(
|
||||||
|
mqtt_mock_entry: MqttMockHAClientGenerator, caplog: pytest.LogCaptureFixture
|
||||||
|
) -> None:
|
||||||
|
"""Test state_class option with invalid unit of measurement."""
|
||||||
|
assert await mqtt_mock_entry()
|
||||||
|
assert (
|
||||||
|
"The unit of measurement 'deg' is not valid together with state class 'measurement_angle'"
|
||||||
|
in caplog.text
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
("hass_config", "error_logged"),
|
("hass_config", "error_logged"),
|
||||||
[
|
[
|
||||||
|
Loading…
x
Reference in New Issue
Block a user