diff --git a/homeassistant/components/knx/binary_sensor.py b/homeassistant/components/knx/binary_sensor.py index 7a3de7e1ec0..5ed0e55765d 100644 --- a/homeassistant/components/knx/binary_sensor.py +++ b/homeassistant/components/knx/binary_sensor.py @@ -9,6 +9,7 @@ from xknx.devices import BinarySensor as XknxBinarySensor from homeassistant.components.binary_sensor import BinarySensorEntity from homeassistant.const import ( CONF_DEVICE_CLASS, + CONF_ENTITY_CATEGORY, CONF_NAME, STATE_ON, STATE_UNAVAILABLE, @@ -64,6 +65,7 @@ class KNXBinarySensor(KnxEntity, BinarySensorEntity, RestoreEntity): reset_after=config.get(BinarySensorSchema.CONF_RESET_AFTER), ) ) + self._attr_entity_category = config.get(CONF_ENTITY_CATEGORY) self._attr_device_class = config.get(CONF_DEVICE_CLASS) self._attr_force_update = self._device.ignore_internal_state self._attr_unique_id = str(self._device.remote_value.group_address_state) diff --git a/homeassistant/components/knx/climate.py b/homeassistant/components/knx/climate.py index 22289738711..2099270f036 100644 --- a/homeassistant/components/knx/climate.py +++ b/homeassistant/components/knx/climate.py @@ -17,7 +17,12 @@ from homeassistant.components.climate.const import ( SUPPORT_PRESET_MODE, SUPPORT_TARGET_TEMPERATURE, ) -from homeassistant.const import ATTR_TEMPERATURE, CONF_NAME, TEMP_CELSIUS +from homeassistant.const import ( + ATTR_TEMPERATURE, + CONF_ENTITY_CATEGORY, + CONF_NAME, + TEMP_CELSIUS, +) from homeassistant.core import HomeAssistant, callback from homeassistant.helpers import entity_registry as er from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -176,6 +181,7 @@ class KNXClimate(KnxEntity, ClimateEntity): def __init__(self, xknx: XKNX, config: ConfigType) -> None: """Initialize of a KNX climate device.""" super().__init__(_create_climate(xknx, config)) + self._attr_entity_category = config.get(CONF_ENTITY_CATEGORY) self._attr_supported_features = SUPPORT_TARGET_TEMPERATURE if self.preset_modes: self._attr_supported_features |= SUPPORT_PRESET_MODE diff --git a/homeassistant/components/knx/cover.py b/homeassistant/components/knx/cover.py index 85cd6c9a60f..8eb906d1ba0 100644 --- a/homeassistant/components/knx/cover.py +++ b/homeassistant/components/knx/cover.py @@ -23,7 +23,7 @@ from homeassistant.components.cover import ( SUPPORT_STOP_TILT, CoverEntity, ) -from homeassistant.const import CONF_DEVICE_CLASS, CONF_NAME +from homeassistant.const import CONF_DEVICE_CLASS, CONF_ENTITY_CATEGORY, CONF_NAME from homeassistant.core import HomeAssistant, callback from homeassistant.helpers import entity_registry as er from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -109,6 +109,7 @@ class KNXCover(KnxEntity, CoverEntity): ) ) self._unsubscribe_auto_updater: Callable[[], None] | None = None + self._attr_entity_category = config.get(CONF_ENTITY_CATEGORY) self._attr_device_class = config.get(CONF_DEVICE_CLASS) or ( DEVICE_CLASS_BLIND if self._device.supports_angle else None diff --git a/homeassistant/components/knx/fan.py b/homeassistant/components/knx/fan.py index 21ede700fdd..a000cdec973 100644 --- a/homeassistant/components/knx/fan.py +++ b/homeassistant/components/knx/fan.py @@ -8,7 +8,7 @@ from xknx import XKNX from xknx.devices import Fan as XknxFan from homeassistant.components.fan import SUPPORT_OSCILLATE, SUPPORT_SET_SPEED, FanEntity -from homeassistant.const import CONF_NAME +from homeassistant.const import CONF_ENTITY_CATEGORY, CONF_NAME from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType @@ -65,6 +65,7 @@ class KNXFan(KnxEntity, FanEntity): ) # FanSpeedMode.STEP if max_step is set self._step_range: tuple[int, int] | None = (1, max_step) if max_step else None + self._attr_entity_category = config.get(CONF_ENTITY_CATEGORY) self._attr_unique_id = str(self._device.speed.group_address) diff --git a/homeassistant/components/knx/light.py b/homeassistant/components/knx/light.py index b807ad1335d..bee96270c36 100644 --- a/homeassistant/components/knx/light.py +++ b/homeassistant/components/knx/light.py @@ -23,7 +23,7 @@ from homeassistant.components.light import ( COLOR_MODE_XY, LightEntity, ) -from homeassistant.const import CONF_NAME +from homeassistant.const import CONF_ENTITY_CATEGORY, CONF_NAME from homeassistant.core import HomeAssistant, callback from homeassistant.helpers import entity_registry as er from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -242,6 +242,7 @@ class KNXLight(KnxEntity, LightEntity): self._attr_min_mireds = color_util.color_temperature_kelvin_to_mired( self._max_kelvin ) + self._attr_entity_category = config.get(CONF_ENTITY_CATEGORY) self._attr_unique_id = self._device_unique_id() def _device_unique_id(self) -> str: diff --git a/homeassistant/components/knx/number.py b/homeassistant/components/knx/number.py index c48c11fa998..2fc8e1af244 100644 --- a/homeassistant/components/knx/number.py +++ b/homeassistant/components/knx/number.py @@ -8,6 +8,7 @@ from xknx.devices import NumericValue from homeassistant.components.number import NumberEntity from homeassistant.const import ( + CONF_ENTITY_CATEGORY, CONF_MODE, CONF_NAME, CONF_TYPE, @@ -74,6 +75,7 @@ class KNXNumber(KnxEntity, NumberEntity, RestoreEntity): NumberSchema.CONF_STEP, self._device.sensor_value.dpt_class.resolution, ) + self._attr_entity_category = config.get(CONF_ENTITY_CATEGORY) self._attr_unique_id = str(self._device.sensor_value.group_address) self._attr_unit_of_measurement = self._device.unit_of_measurement() self._device.sensor_value.value = max(0, self._attr_min_value) diff --git a/homeassistant/components/knx/scene.py b/homeassistant/components/knx/scene.py index 9bd32c99e41..b09dc678be3 100644 --- a/homeassistant/components/knx/scene.py +++ b/homeassistant/components/knx/scene.py @@ -7,7 +7,7 @@ from xknx import XKNX from xknx.devices import Scene as XknxScene from homeassistant.components.scene import Scene -from homeassistant.const import CONF_NAME +from homeassistant.const import CONF_ENTITY_CATEGORY, CONF_NAME from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType @@ -49,6 +49,7 @@ class KNXScene(KnxEntity, Scene): scene_number=config[SceneSchema.CONF_SCENE_NUMBER], ) ) + self._attr_entity_category = config.get(CONF_ENTITY_CATEGORY) self._attr_unique_id = ( f"{self._device.scene_value.group_address}_{self._device.scene_number}" ) diff --git a/homeassistant/components/knx/schema.py b/homeassistant/components/knx/schema.py index 653084f3e3a..0e54a9abbc5 100644 --- a/homeassistant/components/knx/schema.py +++ b/homeassistant/components/knx/schema.py @@ -22,6 +22,7 @@ from homeassistant.components.number.const import MODE_AUTO, MODE_BOX, MODE_SLID from homeassistant.components.sensor import CONF_STATE_CLASS, STATE_CLASSES_SCHEMA from homeassistant.const import ( CONF_DEVICE_CLASS, + CONF_ENTITY_CATEGORY, CONF_ENTITY_ID, CONF_HOST, CONF_MODE, @@ -30,6 +31,7 @@ from homeassistant.const import ( CONF_TYPE, ) import homeassistant.helpers.config_validation as cv +from homeassistant.helpers.entity import ENTITY_CATEGORIES_SCHEMA from .const import ( CONF_INVERT, @@ -253,6 +255,7 @@ class BinarySensorSchema(KNXPlatformSchema): ), vol.Optional(CONF_DEVICE_CLASS): vol.In(BINARY_SENSOR_DEVICE_CLASSES), vol.Optional(CONF_RESET_AFTER): cv.positive_float, + vol.Optional(CONF_ENTITY_CATEGORY): ENTITY_CATEGORIES_SCHEMA, } ), ) @@ -371,6 +374,7 @@ class ClimateSchema(KNXPlatformSchema): ): vol.In(HVAC_MODES), vol.Optional(CONF_MIN_TEMP): vol.Coerce(float), vol.Optional(CONF_MAX_TEMP): vol.Coerce(float), + vol.Optional(CONF_ENTITY_CATEGORY): ENTITY_CATEGORIES_SCHEMA, } ), ) @@ -425,6 +429,7 @@ class CoverSchema(KNXPlatformSchema): vol.Optional(CONF_INVERT_POSITION, default=False): cv.boolean, vol.Optional(CONF_INVERT_ANGLE, default=False): cv.boolean, vol.Optional(CONF_DEVICE_CLASS): vol.In(COVER_DEVICE_CLASSES), + vol.Optional(CONF_ENTITY_CATEGORY): ENTITY_CATEGORIES_SCHEMA, } ), ) @@ -487,6 +492,7 @@ class FanSchema(KNXPlatformSchema): vol.Optional(CONF_OSCILLATION_ADDRESS): ga_list_validator, vol.Optional(CONF_OSCILLATION_STATE_ADDRESS): ga_list_validator, vol.Optional(CONF_MAX_STEP): cv.byte, + vol.Optional(CONF_ENTITY_CATEGORY): ENTITY_CATEGORIES_SCHEMA, } ) @@ -590,6 +596,7 @@ class LightSchema(KNXPlatformSchema): vol.Optional(CONF_MAX_KELVIN, default=DEFAULT_MAX_KELVIN): vol.All( vol.Coerce(int), vol.Range(min=1) ), + vol.Optional(CONF_ENTITY_CATEGORY): ENTITY_CATEGORIES_SCHEMA, } ), vol.Any( @@ -669,6 +676,7 @@ class NumberSchema(KNXPlatformSchema): vol.Optional(CONF_MAX): vol.Coerce(float), vol.Optional(CONF_MIN): vol.Coerce(float), vol.Optional(CONF_STEP): cv.positive_float, + vol.Optional(CONF_ENTITY_CATEGORY): ENTITY_CATEGORIES_SCHEMA, } ), number_limit_sub_validator, @@ -690,6 +698,7 @@ class SceneSchema(KNXPlatformSchema): vol.Required(CONF_SCENE_NUMBER): vol.All( vol.Coerce(int), vol.Range(min=1, max=64) ), + vol.Optional(CONF_ENTITY_CATEGORY): ENTITY_CATEGORIES_SCHEMA, } ) @@ -722,6 +731,7 @@ class SelectSchema(KNXPlatformSchema): ], vol.Required(KNX_ADDRESS): ga_list_validator, vol.Optional(CONF_STATE_ADDRESS): ga_list_validator, + vol.Optional(CONF_ENTITY_CATEGORY): ENTITY_CATEGORIES_SCHEMA, } ), select_options_sub_validator, @@ -746,6 +756,7 @@ class SensorSchema(KNXPlatformSchema): vol.Optional(CONF_STATE_CLASS): STATE_CLASSES_SCHEMA, vol.Required(CONF_TYPE): sensor_type_validator, vol.Required(CONF_STATE_ADDRESS): ga_list_validator, + vol.Optional(CONF_ENTITY_CATEGORY): ENTITY_CATEGORIES_SCHEMA, } ) @@ -766,6 +777,7 @@ class SwitchSchema(KNXPlatformSchema): vol.Optional(CONF_RESPOND_TO_READ, default=False): cv.boolean, vol.Required(KNX_ADDRESS): ga_list_validator, vol.Optional(CONF_STATE_ADDRESS): ga_list_validator, + vol.Optional(CONF_ENTITY_CATEGORY): ENTITY_CATEGORIES_SCHEMA, } ) @@ -812,6 +824,7 @@ class WeatherSchema(KNXPlatformSchema): vol.Optional(CONF_KNX_DAY_NIGHT_ADDRESS): ga_list_validator, vol.Optional(CONF_KNX_AIR_PRESSURE_ADDRESS): ga_list_validator, vol.Optional(CONF_KNX_HUMIDITY_ADDRESS): ga_list_validator, + vol.Optional(CONF_ENTITY_CATEGORY): ENTITY_CATEGORIES_SCHEMA, } ), ) diff --git a/homeassistant/components/knx/select.py b/homeassistant/components/knx/select.py index 90e0203a8be..e548ad27c8a 100644 --- a/homeassistant/components/knx/select.py +++ b/homeassistant/components/knx/select.py @@ -5,7 +5,12 @@ from xknx import XKNX from xknx.devices import Device as XknxDevice, RawValue from homeassistant.components.select import SelectEntity -from homeassistant.const import CONF_NAME, STATE_UNAVAILABLE, STATE_UNKNOWN +from homeassistant.const import ( + CONF_ENTITY_CATEGORY, + CONF_NAME, + STATE_UNAVAILABLE, + STATE_UNKNOWN, +) from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.restore_state import RestoreEntity @@ -66,6 +71,7 @@ class KNXSelect(KnxEntity, SelectEntity, RestoreEntity): } self._attr_options = list(self._option_payloads) self._attr_current_option = None + self._attr_entity_category = config.get(CONF_ENTITY_CATEGORY) self._attr_unique_id = str(self._device.remote_value.group_address) async def async_added_to_hass(self) -> None: diff --git a/homeassistant/components/knx/sensor.py b/homeassistant/components/knx/sensor.py index c1f68e4c376..fb64f65968b 100644 --- a/homeassistant/components/knx/sensor.py +++ b/homeassistant/components/knx/sensor.py @@ -11,7 +11,7 @@ from homeassistant.components.sensor import ( DEVICE_CLASSES, SensorEntity, ) -from homeassistant.const import CONF_NAME, CONF_TYPE +from homeassistant.const import CONF_ENTITY_CATEGORY, CONF_NAME, CONF_TYPE from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType, StateType @@ -65,6 +65,7 @@ class KNXSensor(KnxEntity, SensorEntity): else None ) self._attr_force_update = self._device.always_callback + self._attr_entity_category = config.get(CONF_ENTITY_CATEGORY) self._attr_unique_id = str(self._device.sensor_value.group_address_state) self._attr_native_unit_of_measurement = self._device.unit_of_measurement() self._attr_state_class = config.get(CONF_STATE_CLASS) diff --git a/homeassistant/components/knx/switch.py b/homeassistant/components/knx/switch.py index cca0ffde853..c775ce70d32 100644 --- a/homeassistant/components/knx/switch.py +++ b/homeassistant/components/knx/switch.py @@ -7,7 +7,13 @@ from xknx import XKNX from xknx.devices import Switch as XknxSwitch from homeassistant.components.switch import SwitchEntity -from homeassistant.const import CONF_NAME, STATE_ON, STATE_UNAVAILABLE, STATE_UNKNOWN +from homeassistant.const import ( + CONF_ENTITY_CATEGORY, + CONF_NAME, + STATE_ON, + STATE_UNAVAILABLE, + STATE_UNKNOWN, +) from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.restore_state import RestoreEntity @@ -52,6 +58,7 @@ class KNXSwitch(KnxEntity, SwitchEntity, RestoreEntity): invert=config[SwitchSchema.CONF_INVERT], ) ) + self._attr_entity_category = config.get(CONF_ENTITY_CATEGORY) self._attr_unique_id = str(self._device.switch.group_address) async def async_added_to_hass(self) -> None: diff --git a/homeassistant/components/knx/weather.py b/homeassistant/components/knx/weather.py index 4a55f81ff72..13ebd2480e3 100644 --- a/homeassistant/components/knx/weather.py +++ b/homeassistant/components/knx/weather.py @@ -5,7 +5,7 @@ from xknx import XKNX from xknx.devices import Weather as XknxWeather from homeassistant.components.weather import WeatherEntity -from homeassistant.const import CONF_NAME, TEMP_CELSIUS +from homeassistant.const import CONF_ENTITY_CATEGORY, CONF_NAME, TEMP_CELSIUS from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType @@ -78,6 +78,7 @@ class KNXWeather(KnxEntity, WeatherEntity): """Initialize of a KNX sensor.""" super().__init__(_create_weather(xknx, config)) self._attr_unique_id = str(self._device._temperature.group_address_state) + self._attr_entity_category = config.get(CONF_ENTITY_CATEGORY) @property def temperature(self) -> float | None: diff --git a/tests/components/knx/test_binary_sensor.py b/tests/components/knx/test_binary_sensor.py index 811c8ab1341..9adc7205543 100644 --- a/tests/components/knx/test_binary_sensor.py +++ b/tests/components/knx/test_binary_sensor.py @@ -4,8 +4,17 @@ from unittest.mock import patch from homeassistant.components.knx.const import CONF_STATE_ADDRESS, CONF_SYNC_STATE from homeassistant.components.knx.schema import BinarySensorSchema -from homeassistant.const import CONF_NAME, STATE_OFF, STATE_ON +from homeassistant.const import ( + CONF_ENTITY_CATEGORY, + CONF_NAME, + ENTITY_CATEGORY_DIAGNOSTIC, + STATE_OFF, + STATE_ON, +) from homeassistant.core import HomeAssistant, State +from homeassistant.helpers.entity_registry import ( + async_get_registry as async_get_entity_registry, +) from homeassistant.util import dt from .conftest import KNXTestKit @@ -13,6 +22,29 @@ from .conftest import KNXTestKit from tests.common import async_capture_events, async_fire_time_changed +async def test_binary_sensor_entity_category(hass: HomeAssistant, knx: KNXTestKit): + """Test KNX binary sensor entity category.""" + await knx.setup_integration( + { + BinarySensorSchema.PLATFORM_NAME: [ + { + CONF_NAME: "test_normal", + CONF_STATE_ADDRESS: "1/1/1", + CONF_ENTITY_CATEGORY: ENTITY_CATEGORY_DIAGNOSTIC, + }, + ] + } + ) + assert len(hass.states.async_all()) == 1 + + await knx.assert_read("1/1/1") + await knx.receive_response("1/1/1", True) + + registry = await async_get_entity_registry(hass) + entity = registry.async_get("binary_sensor.test_normal") + assert entity.entity_category == ENTITY_CATEGORY_DIAGNOSTIC + + async def test_binary_sensor(hass: HomeAssistant, knx: KNXTestKit): """Test KNX binary sensor and inverted binary_sensor.""" await knx.setup_integration(