1
0
mirror of https://github.com/home-assistant/core.git synced 2026-04-02 08:26:41 +01:00

KNX: add config for device_class and unit_of_measurement for yaml number entities (#165083)

This commit is contained in:
Matthias Alphart
2026-03-10 19:27:48 +01:00
committed by GitHub
parent 6845e8b880
commit 11bc00038e
4 changed files with 74 additions and 5 deletions

View File

@@ -120,6 +120,19 @@ class KnxYamlNumber(_KnxNumber, KnxYamlEntity):
value_type=config[CONF_TYPE],
),
)
dpt_string = self._device.sensor_value.dpt_class.dpt_number_str()
dpt_info = get_supported_dpts()[dpt_string]
self._attr_device_class = config.get(
CONF_DEVICE_CLASS,
try_parse_enum(
# sensor device classes should, with some exceptions ("enum" etc.), align with number device classes
NumberDeviceClass,
dpt_info["sensor_device_class"],
),
)
self._attr_entity_category = config.get(CONF_ENTITY_CATEGORY)
self._attr_mode = config[CONF_MODE]
self._attr_native_max_value = config.get(
NumberConf.MAX,
self._device.sensor_value.dpt_class.value_max,
@@ -128,14 +141,16 @@ class KnxYamlNumber(_KnxNumber, KnxYamlEntity):
NumberConf.MIN,
self._device.sensor_value.dpt_class.value_min,
)
self._attr_mode = config[CONF_MODE]
self._attr_native_step = config.get(
NumberConf.STEP,
self._device.sensor_value.dpt_class.resolution,
)
self._attr_entity_category = config.get(CONF_ENTITY_CATEGORY)
self._attr_native_unit_of_measurement = config.get(
CONF_UNIT_OF_MEASUREMENT,
dpt_info["unit"],
)
self._attr_unique_id = str(self._device.sensor_value.group_address)
self._attr_native_unit_of_measurement = self._device.unit_of_measurement()
self._device.sensor_value.value = max(0, self._attr_native_min_value)

View File

@@ -20,7 +20,10 @@ from homeassistant.components.climate import FAN_OFF, HVACMode
from homeassistant.components.cover import (
DEVICE_CLASSES_SCHEMA as COVER_DEVICE_CLASSES_SCHEMA,
)
from homeassistant.components.number import NumberMode
from homeassistant.components.number import (
DEVICE_CLASSES_SCHEMA as NUMBER_DEVICE_CLASSES_SCHEMA,
NumberMode,
)
from homeassistant.components.sensor import (
CONF_STATE_CLASS as CONF_SENSOR_STATE_CLASS,
DEVICE_CLASSES_SCHEMA as SENSOR_DEVICE_CLASSES_SCHEMA,
@@ -39,6 +42,7 @@ from homeassistant.const import (
CONF_NAME,
CONF_PAYLOAD,
CONF_TYPE,
CONF_UNIT_OF_MEASUREMENT,
CONF_VALUE_TEMPLATE,
Platform,
)
@@ -787,6 +791,8 @@ class NumberSchema(KNXPlatformSchema):
vol.Optional(NumberConf.MAX): vol.Coerce(float),
vol.Optional(NumberConf.MIN): vol.Coerce(float),
vol.Optional(NumberConf.STEP): cv.positive_float,
vol.Optional(CONF_DEVICE_CLASS): NUMBER_DEVICE_CLASSES_SCHEMA,
vol.Optional(CONF_UNIT_OF_MEASUREMENT): cv.string,
vol.Optional(CONF_ENTITY_CATEGORY): ENTITY_CATEGORIES_SCHEMA,
}
),

View File

@@ -7,7 +7,10 @@ from homeassistant.components.knx.dpt import (
_sensor_state_class_overrides,
_sensor_unit_overrides,
)
from homeassistant.components.knx.schema import _sensor_attribute_sub_validator
from homeassistant.components.knx.schema import (
_number_limit_sub_validator,
_sensor_attribute_sub_validator,
)
@pytest.mark.parametrize(
@@ -31,3 +34,9 @@ def test_dpt_default_device_classes(dpt: str) -> None:
# UI validation works the same way, but uses different schema for config
{"type": dpt}
)
number_config = {"type": dpt}
if dpt.startswith("14"):
# DPT 14 has infinite range which isn't supported by HA
# this test shall still check for correct device_class and unit_of_measurement
number_config |= {"min": -500000, "max": 500000}
assert _number_limit_sub_validator(number_config)

View File

@@ -1,5 +1,6 @@
"""Test KNX number."""
import logging
from typing import Any
import pytest
@@ -111,6 +112,44 @@ async def test_number_restore_and_respond(hass: HomeAssistant, knx: KNXTestKit)
assert state.state == "9000.96"
@pytest.mark.parametrize(
"attribute_config",
[
{"device_class": "energy"}, # invalid with uom of temperature DPT
{"device_class": "energy", "unit_of_measurement": "invalid"},
{"device_class": "invalid"},
],
)
async def test_number_yaml_attribute_validation(
hass: HomeAssistant,
caplog: pytest.LogCaptureFixture,
knx: KNXTestKit,
attribute_config: dict[str, Any],
) -> None:
"""Test creating a number with invalid unit or device_class."""
with caplog.at_level(logging.ERROR):
await knx.setup_integration(
{
NumberSchema.PLATFORM: {
CONF_NAME: "test",
KNX_ADDRESS: "1/1/1",
CONF_TYPE: "9.001", # temperature 2 byte float
**attribute_config,
}
}
)
assert len(caplog.messages) == 2
record = caplog.records[0]
assert record.levelname == "ERROR"
assert "Invalid config for 'knx': " in record.message
record = caplog.records[1]
assert record.levelname == "ERROR"
assert "Setup failed for 'knx': Invalid config." in record.message
assert hass.states.get("number.test") is None
@pytest.mark.parametrize(
("knx_config", "set_value", "expected_telegram", "expected_state"),
[