diff --git a/homeassistant/components/knx/binary_sensor.py b/homeassistant/components/knx/binary_sensor.py index 3b2f4b7a75a..a3271e605af 100644 --- a/homeassistant/components/knx/binary_sensor.py +++ b/homeassistant/components/knx/binary_sensor.py @@ -6,7 +6,7 @@ from typing import Any from xknx import XKNX from xknx.devices import BinarySensor as XknxBinarySensor -from homeassistant.components.binary_sensor import DEVICE_CLASSES, BinarySensorEntity +from homeassistant.components.binary_sensor import BinarySensorEntity from homeassistant.const import CONF_DEVICE_CLASS, CONF_NAME from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -64,9 +64,7 @@ class KNXBinarySensor(KnxEntity, BinarySensorEntity): @property def device_class(self) -> str | None: """Return the class of this sensor.""" - if self._device_class in DEVICE_CLASSES: - return self._device_class - return None + return self._device_class @property def is_on(self) -> bool: diff --git a/homeassistant/components/knx/cover.py b/homeassistant/components/knx/cover.py index 86fba34a87a..58d627ecb5e 100644 --- a/homeassistant/components/knx/cover.py +++ b/homeassistant/components/knx/cover.py @@ -12,7 +12,6 @@ from homeassistant.components.cover import ( ATTR_POSITION, ATTR_TILT_POSITION, DEVICE_CLASS_BLIND, - DEVICE_CLASSES, SUPPORT_CLOSE, SUPPORT_CLOSE_TILT, SUPPORT_OPEN, @@ -127,7 +126,7 @@ class KNXCover(KnxEntity, CoverEntity): @property def device_class(self) -> str | None: """Return the class of this device, from component DEVICE_CLASSES.""" - if self._device_class in DEVICE_CLASSES: + if self._device_class: return self._device_class if self._device.supports_angle: return DEVICE_CLASS_BLIND diff --git a/homeassistant/components/knx/schema.py b/homeassistant/components/knx/schema.py index 4fd0aaaa3b9..3ac0ec84e1d 100644 --- a/homeassistant/components/knx/schema.py +++ b/homeassistant/components/knx/schema.py @@ -5,10 +5,15 @@ from typing import Any import voluptuous as vol from xknx.devices.climate import SetpointShiftMode +from xknx.dpt import DPTBase from xknx.exceptions import CouldNotParseAddress from xknx.io import DEFAULT_MCAST_PORT from xknx.telegram.address import IndividualAddress, parse_device_group_address +from homeassistant.components.binary_sensor import ( + DEVICE_CLASSES as BINARY_SENSOR_DEVICE_CLASSES, +) +from homeassistant.components.cover import DEVICE_CLASSES as COVER_DEVICE_CLASSES from homeassistant.const import ( CONF_DEVICE_CLASS, CONF_ENTITY_ID, @@ -44,7 +49,8 @@ def ga_validator(value: Any) -> str | int: except CouldNotParseAddress: pass raise vol.Invalid( - f"value '{value}' is not a valid KNX group address '
//', '
/' or '' (eg.'1/2/3', '9/234', '123'), nor xknx internal address 'i-'." + f"value '{value}' is not a valid KNX group address '
//', '
/' " + "or '' (eg.'1/2/3', '9/234', '123'), nor xknx internal address 'i-'." ) @@ -56,15 +62,20 @@ ia_validator = vol.Any( msg="value does not match pattern for KNX individual address '..' (eg.'1.1.100')", ) + +def sensor_type_validator(value: Any) -> str | int: + """Validate that value is parsable as sensor type.""" + if isinstance(value, (str, int)) and DPTBase.parse_transcoder(value) is not None: + return value + raise vol.Invalid(f"value '{value}' is not a valid sensor type.") + + sync_state_validator = vol.Any( vol.All(vol.Coerce(int), vol.Range(min=2, max=1440)), cv.boolean, cv.matches_regex(r"^(init|expire|every)( \d*)?$"), ) -sensor_type_validator = vol.Any(int, str) - - ############## # CONNECTION ############## @@ -119,7 +130,7 @@ class BinarySensorSchema: vol.Optional(CONF_CONTEXT_TIMEOUT): vol.All( vol.Coerce(float), vol.Range(min=0, max=10) ), - vol.Optional(CONF_DEVICE_CLASS): cv.string, + vol.Optional(CONF_DEVICE_CLASS): vol.In(BINARY_SENSOR_DEVICE_CLASSES), vol.Optional(CONF_RESET_AFTER): cv.positive_float, } ), @@ -222,10 +233,10 @@ class ClimateSchema: CONF_ON_OFF_INVERT, default=DEFAULT_ON_OFF_INVERT ): cv.boolean, vol.Optional(CONF_OPERATION_MODES): vol.All( - cv.ensure_list, [vol.In({**PRESET_MODES})] + cv.ensure_list, [vol.In(PRESET_MODES)] ), vol.Optional(CONF_CONTROLLER_MODES): vol.All( - cv.ensure_list, [vol.In({**CONTROLLER_MODES})] + cv.ensure_list, [vol.In(CONTROLLER_MODES)] ), vol.Optional(CONF_MIN_TEMP): vol.Coerce(float), vol.Optional(CONF_MAX_TEMP): vol.Coerce(float), @@ -280,7 +291,7 @@ class CoverSchema: ): cv.positive_float, vol.Optional(CONF_INVERT_POSITION, default=False): cv.boolean, vol.Optional(CONF_INVERT_ANGLE, default=False): cv.boolean, - vol.Optional(CONF_DEVICE_CLASS): cv.string, + vol.Optional(CONF_DEVICE_CLASS): vol.In(COVER_DEVICE_CLASSES), } ), ) @@ -291,6 +302,7 @@ class ExposeSchema: CONF_KNX_EXPOSE_TYPE = CONF_TYPE CONF_KNX_EXPOSE_ATTRIBUTE = "attribute" + CONF_KNX_EXPOSE_BINARY = "binary" CONF_KNX_EXPOSE_DEFAULT = "default" EXPOSE_TIME_TYPES = [ "time", @@ -308,14 +320,16 @@ class ExposeSchema: ) EXPOSE_SENSOR_SCHEMA = vol.Schema( { - vol.Required(CONF_KNX_EXPOSE_TYPE): sensor_type_validator, + vol.Required(CONF_KNX_EXPOSE_TYPE): vol.Any( + CONF_KNX_EXPOSE_BINARY, sensor_type_validator + ), vol.Required(KNX_ADDRESS): ga_validator, vol.Required(CONF_ENTITY_ID): cv.entity_id, vol.Optional(CONF_KNX_EXPOSE_ATTRIBUTE): cv.string, vol.Optional(CONF_KNX_EXPOSE_DEFAULT): cv.match_all, } ) - SCHEMA = vol.Any(EXPOSE_TIME_SCHEMA, EXPOSE_SENSOR_SCHEMA) + SCHEMA = vol.Any(EXPOSE_SENSOR_SCHEMA, EXPOSE_TIME_SCHEMA) class FanSchema: