Home Assistant Unofficial Reference 2024.12.1
schema.py
Go to the documentation of this file.
1 """Voluptuous schemas for the KNX integration."""
2 
3 from __future__ import annotations
4 
5 from abc import ABC
6 from collections import OrderedDict
7 from typing import ClassVar, Final
8 
9 import voluptuous as vol
10 from xknx.devices.climate import FanSpeedMode, SetpointShiftMode
11 from xknx.dpt import DPTBase, DPTNumeric
12 from xknx.dpt.dpt_20 import HVACControllerMode, HVACOperationMode
13 from xknx.exceptions import ConversionError, CouldNotParseTelegram
14 
16  DEVICE_CLASSES_SCHEMA as BINARY_SENSOR_DEVICE_CLASSES_SCHEMA,
17 )
18 from homeassistant.components.climate import FAN_OFF, HVACMode
20  DEVICE_CLASSES_SCHEMA as COVER_DEVICE_CLASSES_SCHEMA,
21 )
22 from homeassistant.components.number import NumberMode
24  CONF_STATE_CLASS,
25  DEVICE_CLASSES_SCHEMA as SENSOR_DEVICE_CLASSES_SCHEMA,
26  STATE_CLASSES_SCHEMA,
27 )
29  DEVICE_CLASSES_SCHEMA as SWITCH_DEVICE_CLASSES_SCHEMA,
30 )
31 from homeassistant.components.text import TextMode
32 from homeassistant.const import (
33  CONF_DEVICE_CLASS,
34  CONF_ENTITY_CATEGORY,
35  CONF_ENTITY_ID,
36  CONF_EVENT,
37  CONF_MODE,
38  CONF_NAME,
39  CONF_PAYLOAD,
40  CONF_TYPE,
41  CONF_VALUE_TEMPLATE,
42  Platform,
43 )
45 from homeassistant.helpers.entity import ENTITY_CATEGORIES_SCHEMA
46 
47 from .const import (
48  CONF_INVERT,
49  CONF_KNX_EXPOSE,
50  CONF_PAYLOAD_LENGTH,
51  CONF_RESET_AFTER,
52  CONF_RESPOND_TO_READ,
53  CONF_STATE_ADDRESS,
54  CONF_SYNC_STATE,
55  KNX_ADDRESS,
56  ColorTempModes,
57  FanZeroMode,
58 )
59 from .validation import (
60  backwards_compatible_xknx_climate_enum_member,
61  dpt_base_type_validator,
62  ga_list_validator,
63  ga_validator,
64  numeric_type_validator,
65  sensor_type_validator,
66  string_type_validator,
67  sync_state_validator,
68 )
69 
70 
71 ##################
72 # KNX SUB VALIDATORS
73 ##################
74 def number_limit_sub_validator(entity_config: OrderedDict) -> OrderedDict:
75  """Validate a number entity configurations dependent on configured value type."""
76  value_type = entity_config[CONF_TYPE]
77  min_config: float | None = entity_config.get(NumberSchema.CONF_MIN)
78  max_config: float | None = entity_config.get(NumberSchema.CONF_MAX)
79  step_config: float | None = entity_config.get(NumberSchema.CONF_STEP)
80  dpt_class = DPTNumeric.parse_transcoder(value_type)
81 
82  if dpt_class is None:
83  raise vol.Invalid(f"'type: {value_type}' is not a valid numeric sensor type.")
84  # Infinity is not supported by Home Assistant frontend so user defined
85  # config is required if if xknx DPTNumeric subclass defines it as limit.
86  if min_config is None and dpt_class.value_min == float("-inf"):
87  raise vol.Invalid(f"'min' key required for value type '{value_type}'")
88  if min_config is not None and min_config < dpt_class.value_min:
89  raise vol.Invalid(
90  f"'min: {min_config}' undercuts possible minimum"
91  f" of value type '{value_type}': {dpt_class.value_min}"
92  )
93 
94  if max_config is None and dpt_class.value_max == float("inf"):
95  raise vol.Invalid(f"'max' key required for value type '{value_type}'")
96  if max_config is not None and max_config > dpt_class.value_max:
97  raise vol.Invalid(
98  f"'max: {max_config}' exceeds possible maximum"
99  f" of value type '{value_type}': {dpt_class.value_max}"
100  )
101 
102  if step_config is not None and step_config < dpt_class.resolution:
103  raise vol.Invalid(
104  f"'step: {step_config}' undercuts possible minimum step"
105  f" of value type '{value_type}': {dpt_class.resolution}"
106  )
107 
108  return entity_config
109 
110 
111 def _max_payload_value(payload_length: int) -> int:
112  if payload_length == 0:
113  return 0x3F
114  return int(256**payload_length) - 1
115 
116 
117 def button_payload_sub_validator(entity_config: OrderedDict) -> OrderedDict:
118  """Validate a button entity payload configuration."""
119  if _type := entity_config.get(CONF_TYPE):
120  _payload = entity_config[ButtonSchema.CONF_VALUE]
121  if (transcoder := DPTBase.parse_transcoder(_type)) is None:
122  raise vol.Invalid(f"'type: {_type}' is not a valid sensor type.")
123  entity_config[CONF_PAYLOAD_LENGTH] = transcoder.payload_length
124  try:
125  _dpt_payload = transcoder.to_knx(_payload)
126  _raw_payload = transcoder.validate_payload(_dpt_payload)
127  except (ConversionError, CouldNotParseTelegram) as ex:
128  raise vol.Invalid(
129  f"'payload: {_payload}' not valid for 'type: {_type}'"
130  ) from ex
131  entity_config[CONF_PAYLOAD] = int.from_bytes(_raw_payload, byteorder="big")
132  return entity_config
133 
134  _payload = entity_config[CONF_PAYLOAD]
135  _payload_length = entity_config[CONF_PAYLOAD_LENGTH]
136  if _payload > (max_payload := _max_payload_value(_payload_length)):
137  raise vol.Invalid(
138  f"'payload: {_payload}' exceeds possible maximum for "
139  f"payload_length {_payload_length}: {max_payload}"
140  )
141  return entity_config
142 
143 
144 def select_options_sub_validator(entity_config: OrderedDict) -> OrderedDict:
145  """Validate a select entity options configuration."""
146  options_seen = set()
147  payloads_seen = set()
148  payload_length = entity_config[CONF_PAYLOAD_LENGTH]
149 
150  for opt in entity_config[SelectSchema.CONF_OPTIONS]:
151  option = opt[SelectSchema.CONF_OPTION]
152  payload = opt[CONF_PAYLOAD]
153  if payload > (max_payload := _max_payload_value(payload_length)):
154  raise vol.Invalid(
155  f"'payload: {payload}' for 'option: {option}' exceeds possible"
156  f" maximum of 'payload_length: {payload_length}': {max_payload}"
157  )
158  if option in options_seen:
159  raise vol.Invalid(f"duplicate item for 'option' not allowed: {option}")
160  options_seen.add(option)
161  if payload in payloads_seen:
162  raise vol.Invalid(f"duplicate item for 'payload' not allowed: {payload}")
163  payloads_seen.add(payload)
164  return entity_config
165 
166 
167 #########
168 # EVENT
169 #########
170 
171 
173  """Voluptuous schema for KNX events."""
174 
175  KNX_EVENT_FILTER_SCHEMA = vol.Schema(
176  {
177  vol.Required(KNX_ADDRESS): vol.All(cv.ensure_list, [cv.string]),
178  vol.Optional(CONF_TYPE): dpt_base_type_validator,
179  }
180  )
181 
182  SCHEMA = {
183  vol.Optional(CONF_EVENT, default=[]): vol.All(
184  cv.ensure_list, [KNX_EVENT_FILTER_SCHEMA]
185  )
186  }
187 
188 
189 #############
190 # PLATFORMS
191 #############
192 
193 
194 class KNXPlatformSchema(ABC):
195  """Voluptuous schema for KNX platform entity configuration."""
196 
197  PLATFORM: ClassVar[Platform | str]
198  ENTITY_SCHEMA: ClassVar[vol.Schema | vol.All | vol.Any]
199 
200  @classmethod
201  def platform_node(cls) -> dict[vol.Optional, vol.All]:
202  """Return a schema node for the platform."""
203  return {
204  vol.Optional(str(cls.PLATFORM)): vol.All(
205  cv.ensure_list, [cls.ENTITY_SCHEMA]
206  )
207  }
208 
209 
211  """Voluptuous schema for KNX binary sensors."""
212 
213  PLATFORM = Platform.BINARY_SENSOR
214 
215  CONF_STATE_ADDRESS = CONF_STATE_ADDRESS
216  CONF_SYNC_STATE = CONF_SYNC_STATE
217  CONF_INVERT = CONF_INVERT
218  CONF_IGNORE_INTERNAL_STATE = "ignore_internal_state"
219  CONF_CONTEXT_TIMEOUT = "context_timeout"
220  CONF_RESET_AFTER = CONF_RESET_AFTER
221 
222  DEFAULT_NAME = "KNX Binary Sensor"
223 
224  ENTITY_SCHEMA = vol.All(
225  vol.Schema(
226  {
227  vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
228  vol.Optional(CONF_SYNC_STATE, default=True): sync_state_validator,
229  vol.Optional(CONF_IGNORE_INTERNAL_STATE, default=False): cv.boolean,
230  vol.Optional(CONF_INVERT, default=False): cv.boolean,
231  vol.Required(CONF_STATE_ADDRESS): ga_list_validator,
232  vol.Optional(CONF_CONTEXT_TIMEOUT): vol.All(
233  vol.Coerce(float), vol.Range(min=0, max=10)
234  ),
235  vol.Optional(CONF_DEVICE_CLASS): BINARY_SENSOR_DEVICE_CLASSES_SCHEMA,
236  vol.Optional(CONF_RESET_AFTER): cv.positive_float,
237  vol.Optional(CONF_ENTITY_CATEGORY): ENTITY_CATEGORIES_SCHEMA,
238  }
239  ),
240  )
241 
242 
244  """Voluptuous schema for KNX buttons."""
245 
246  PLATFORM = Platform.BUTTON
247 
248  CONF_VALUE = "value"
249  DEFAULT_NAME = "KNX Button"
250 
251  payload_or_value_msg = f"Please use only one of `{CONF_PAYLOAD}` or `{CONF_VALUE}`"
252  length_or_type_msg = (
253  f"Please use only one of `{CONF_PAYLOAD_LENGTH}` or `{CONF_TYPE}`"
254  )
255 
256  ENTITY_SCHEMA = vol.All(
257  vol.Schema(
258  {
259  vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
260  vol.Required(KNX_ADDRESS): ga_validator,
261  vol.Exclusive(
262  CONF_PAYLOAD, "payload_or_value", msg=payload_or_value_msg
263  ): object,
264  vol.Exclusive(
265  CONF_VALUE, "payload_or_value", msg=payload_or_value_msg
266  ): object,
267  vol.Exclusive(
268  CONF_PAYLOAD_LENGTH, "length_or_type", msg=length_or_type_msg
269  ): object,
270  vol.Exclusive(
271  CONF_TYPE, "length_or_type", msg=length_or_type_msg
272  ): object,
273  vol.Optional(CONF_ENTITY_CATEGORY): ENTITY_CATEGORIES_SCHEMA,
274  }
275  ),
276  vol.Any(
277  vol.Schema(
278  # encoded value
279  {
280  vol.Required(CONF_VALUE): vol.Any(int, float, str),
281  vol.Required(CONF_TYPE): sensor_type_validator,
282  },
283  extra=vol.ALLOW_EXTRA,
284  ),
285  vol.Schema(
286  # raw payload - default is DPT 1 style True
287  {
288  vol.Optional(CONF_PAYLOAD, default=1): cv.positive_int,
289  vol.Optional(CONF_PAYLOAD_LENGTH, default=0): vol.All(
290  vol.Coerce(int), vol.Range(min=0, max=14)
291  ),
292  vol.Optional(CONF_VALUE): None,
293  vol.Optional(CONF_TYPE): None,
294  },
295  extra=vol.ALLOW_EXTRA,
296  ),
297  ),
298  # calculate raw CONF_PAYLOAD and CONF_PAYLOAD_LENGTH
299  # from CONF_VALUE and CONF_TYPE if given and check payload size
300  button_payload_sub_validator,
301  )
302 
303 
305  """Voluptuous schema for KNX climate devices."""
306 
307  PLATFORM = Platform.CLIMATE
308 
309  CONF_ACTIVE_STATE_ADDRESS = "active_state_address"
310  CONF_SETPOINT_SHIFT_ADDRESS = "setpoint_shift_address"
311  CONF_SETPOINT_SHIFT_STATE_ADDRESS = "setpoint_shift_state_address"
312  CONF_SETPOINT_SHIFT_MODE = "setpoint_shift_mode"
313  CONF_SETPOINT_SHIFT_MAX = "setpoint_shift_max"
314  CONF_SETPOINT_SHIFT_MIN = "setpoint_shift_min"
315  CONF_TEMPERATURE_ADDRESS = "temperature_address"
316  CONF_TEMPERATURE_STEP = "temperature_step"
317  CONF_TARGET_TEMPERATURE_ADDRESS = "target_temperature_address"
318  CONF_TARGET_TEMPERATURE_STATE_ADDRESS = "target_temperature_state_address"
319  CONF_OPERATION_MODE_ADDRESS = "operation_mode_address"
320  CONF_OPERATION_MODE_STATE_ADDRESS = "operation_mode_state_address"
321  CONF_CONTROLLER_STATUS_ADDRESS = "controller_status_address"
322  CONF_CONTROLLER_STATUS_STATE_ADDRESS = "controller_status_state_address"
323  CONF_CONTROLLER_MODE_ADDRESS = "controller_mode_address"
324  CONF_CONTROLLER_MODE_STATE_ADDRESS = "controller_mode_state_address"
325  CONF_COMMAND_VALUE_STATE_ADDRESS = "command_value_state_address"
326  CONF_HEAT_COOL_ADDRESS = "heat_cool_address"
327  CONF_HEAT_COOL_STATE_ADDRESS = "heat_cool_state_address"
328  CONF_OPERATION_MODE_FROST_PROTECTION_ADDRESS = (
329  "operation_mode_frost_protection_address"
330  )
331  CONF_OPERATION_MODE_NIGHT_ADDRESS = "operation_mode_night_address"
332  CONF_OPERATION_MODE_COMFORT_ADDRESS = "operation_mode_comfort_address"
333  CONF_OPERATION_MODE_STANDBY_ADDRESS = "operation_mode_standby_address"
334  CONF_OPERATION_MODES = "operation_modes"
335  CONF_CONTROLLER_MODES = "controller_modes"
336  CONF_DEFAULT_CONTROLLER_MODE = "default_controller_mode"
337  CONF_ON_OFF_ADDRESS = "on_off_address"
338  CONF_ON_OFF_STATE_ADDRESS = "on_off_state_address"
339  CONF_ON_OFF_INVERT = "on_off_invert"
340  CONF_MIN_TEMP = "min_temp"
341  CONF_MAX_TEMP = "max_temp"
342  CONF_FAN_SPEED_ADDRESS = "fan_speed_address"
343  CONF_FAN_SPEED_STATE_ADDRESS = "fan_speed_state_address"
344  CONF_FAN_MAX_STEP = "fan_max_step"
345  CONF_FAN_SPEED_MODE = "fan_speed_mode"
346  CONF_FAN_ZERO_MODE = "fan_zero_mode"
347  CONF_HUMIDITY_STATE_ADDRESS = "humidity_state_address"
348 
349  DEFAULT_NAME = "KNX Climate"
350  DEFAULT_SETPOINT_SHIFT_MODE = "DPT6010"
351  DEFAULT_SETPOINT_SHIFT_MAX = 6
352  DEFAULT_SETPOINT_SHIFT_MIN = -6
353  DEFAULT_TEMPERATURE_STEP = 0.1
354  DEFAULT_ON_OFF_INVERT = False
355  DEFAULT_FAN_SPEED_MODE = "percent"
356 
357  ENTITY_SCHEMA = vol.All(
358  vol.Schema(
359  {
360  vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
361  vol.Optional(
362  CONF_SETPOINT_SHIFT_MAX, default=DEFAULT_SETPOINT_SHIFT_MAX
363  ): vol.All(int, vol.Range(min=0, max=32)),
364  vol.Optional(
365  CONF_SETPOINT_SHIFT_MIN, default=DEFAULT_SETPOINT_SHIFT_MIN
366  ): vol.All(int, vol.Range(min=-32, max=0)),
367  vol.Optional(
368  CONF_TEMPERATURE_STEP, default=DEFAULT_TEMPERATURE_STEP
369  ): vol.All(float, vol.Range(min=0, max=2)),
370  vol.Required(CONF_TEMPERATURE_ADDRESS): ga_list_validator,
371  vol.Required(CONF_TARGET_TEMPERATURE_STATE_ADDRESS): ga_list_validator,
372  vol.Optional(CONF_TARGET_TEMPERATURE_ADDRESS): ga_list_validator,
373  vol.Inclusive(
374  CONF_SETPOINT_SHIFT_ADDRESS,
375  "setpoint_shift",
376  msg=(
377  "'setpoint_shift_address' and 'setpoint_shift_state_address' "
378  "are required for setpoint_shift configuration"
379  ),
380  ): ga_list_validator,
381  vol.Inclusive(
382  CONF_SETPOINT_SHIFT_STATE_ADDRESS,
383  "setpoint_shift",
384  msg=(
385  "'setpoint_shift_address' and 'setpoint_shift_state_address' "
386  "are required for setpoint_shift configuration"
387  ),
388  ): ga_list_validator,
389  vol.Optional(CONF_SETPOINT_SHIFT_MODE): vol.Maybe(
390  vol.All(vol.Upper, cv.enum(SetpointShiftMode))
391  ),
392  vol.Optional(CONF_ACTIVE_STATE_ADDRESS): ga_list_validator,
393  vol.Optional(CONF_COMMAND_VALUE_STATE_ADDRESS): ga_list_validator,
394  vol.Optional(CONF_OPERATION_MODE_ADDRESS): ga_list_validator,
395  vol.Optional(CONF_OPERATION_MODE_STATE_ADDRESS): ga_list_validator,
396  vol.Optional(CONF_CONTROLLER_STATUS_ADDRESS): ga_list_validator,
397  vol.Optional(CONF_CONTROLLER_STATUS_STATE_ADDRESS): ga_list_validator,
398  vol.Optional(CONF_CONTROLLER_MODE_ADDRESS): ga_list_validator,
399  vol.Optional(CONF_CONTROLLER_MODE_STATE_ADDRESS): ga_list_validator,
400  vol.Optional(CONF_HEAT_COOL_ADDRESS): ga_list_validator,
401  vol.Optional(CONF_HEAT_COOL_STATE_ADDRESS): ga_list_validator,
402  vol.Optional(
403  CONF_OPERATION_MODE_FROST_PROTECTION_ADDRESS
404  ): ga_list_validator,
405  vol.Optional(CONF_OPERATION_MODE_NIGHT_ADDRESS): ga_list_validator,
406  vol.Optional(CONF_OPERATION_MODE_COMFORT_ADDRESS): ga_list_validator,
407  vol.Optional(CONF_OPERATION_MODE_STANDBY_ADDRESS): ga_list_validator,
408  vol.Optional(CONF_ON_OFF_ADDRESS): ga_list_validator,
409  vol.Optional(CONF_ON_OFF_STATE_ADDRESS): ga_list_validator,
410  vol.Optional(
411  CONF_ON_OFF_INVERT, default=DEFAULT_ON_OFF_INVERT
412  ): cv.boolean,
413  vol.Optional(CONF_OPERATION_MODES): vol.All(
414  cv.ensure_list,
416  ),
417  vol.Optional(CONF_CONTROLLER_MODES): vol.All(
418  cv.ensure_list,
419  [backwards_compatible_xknx_climate_enum_member(HVACControllerMode)],
420  ),
421  vol.Optional(
422  CONF_DEFAULT_CONTROLLER_MODE, default=HVACMode.HEAT
423  ): vol.Coerce(HVACMode),
424  vol.Optional(CONF_MIN_TEMP): vol.Coerce(float),
425  vol.Optional(CONF_MAX_TEMP): vol.Coerce(float),
426  vol.Optional(CONF_ENTITY_CATEGORY): ENTITY_CATEGORIES_SCHEMA,
427  vol.Optional(CONF_FAN_SPEED_ADDRESS): ga_list_validator,
428  vol.Optional(CONF_FAN_SPEED_STATE_ADDRESS): ga_list_validator,
429  vol.Optional(CONF_FAN_MAX_STEP, default=3): cv.byte,
430  vol.Optional(
431  CONF_FAN_SPEED_MODE, default=DEFAULT_FAN_SPEED_MODE
432  ): vol.All(vol.Upper, cv.enum(FanSpeedMode)),
433  vol.Optional(CONF_FAN_ZERO_MODE, default=FAN_OFF): vol.Coerce(
434  FanZeroMode
435  ),
436  vol.Optional(CONF_HUMIDITY_STATE_ADDRESS): ga_list_validator,
437  }
438  ),
439  )
440 
441 
443  """Voluptuous schema for KNX covers."""
444 
445  PLATFORM = Platform.COVER
446 
447  CONF_MOVE_LONG_ADDRESS = "move_long_address"
448  CONF_MOVE_SHORT_ADDRESS = "move_short_address"
449  CONF_STOP_ADDRESS = "stop_address"
450  CONF_POSITION_ADDRESS = "position_address"
451  CONF_POSITION_STATE_ADDRESS = "position_state_address"
452  CONF_ANGLE_ADDRESS = "angle_address"
453  CONF_ANGLE_STATE_ADDRESS = "angle_state_address"
454  CONF_TRAVELLING_TIME_DOWN = "travelling_time_down"
455  CONF_TRAVELLING_TIME_UP = "travelling_time_up"
456  CONF_INVERT_UPDOWN = "invert_updown"
457  CONF_INVERT_POSITION = "invert_position"
458  CONF_INVERT_ANGLE = "invert_angle"
459 
460  DEFAULT_TRAVEL_TIME = 25
461  DEFAULT_NAME = "KNX Cover"
462 
463  ENTITY_SCHEMA = vol.All(
464  vol.Schema(
465  {
466  vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
467  vol.Optional(CONF_MOVE_LONG_ADDRESS): ga_list_validator,
468  vol.Optional(CONF_MOVE_SHORT_ADDRESS): ga_list_validator,
469  vol.Optional(CONF_STOP_ADDRESS): ga_list_validator,
470  vol.Optional(CONF_POSITION_ADDRESS): ga_list_validator,
471  vol.Optional(CONF_POSITION_STATE_ADDRESS): ga_list_validator,
472  vol.Optional(CONF_ANGLE_ADDRESS): ga_list_validator,
473  vol.Optional(CONF_ANGLE_STATE_ADDRESS): ga_list_validator,
474  vol.Optional(
475  CONF_TRAVELLING_TIME_DOWN, default=DEFAULT_TRAVEL_TIME
476  ): cv.positive_float,
477  vol.Optional(
478  CONF_TRAVELLING_TIME_UP, default=DEFAULT_TRAVEL_TIME
479  ): cv.positive_float,
480  vol.Optional(CONF_INVERT_UPDOWN, default=False): cv.boolean,
481  vol.Optional(CONF_INVERT_POSITION, default=False): cv.boolean,
482  vol.Optional(CONF_INVERT_ANGLE, default=False): cv.boolean,
483  vol.Optional(CONF_DEVICE_CLASS): COVER_DEVICE_CLASSES_SCHEMA,
484  vol.Optional(CONF_ENTITY_CATEGORY): ENTITY_CATEGORIES_SCHEMA,
485  }
486  ),
487  vol.Any(
488  vol.Schema(
489  {vol.Required(CONF_MOVE_LONG_ADDRESS): object},
490  extra=vol.ALLOW_EXTRA,
491  ),
492  vol.Schema(
493  {vol.Required(CONF_POSITION_ADDRESS): object},
494  extra=vol.ALLOW_EXTRA,
495  ),
496  msg=(
497  f"At least one of '{CONF_MOVE_LONG_ADDRESS}' or"
498  f" '{CONF_POSITION_ADDRESS}' is required."
499  ),
500  ),
501  )
502 
503 
505  """Voluptuous schema for KNX date."""
506 
507  PLATFORM = Platform.DATE
508 
509  DEFAULT_NAME = "KNX Date"
510 
511  ENTITY_SCHEMA = vol.Schema(
512  {
513  vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
514  vol.Optional(CONF_RESPOND_TO_READ, default=False): cv.boolean,
515  vol.Optional(CONF_SYNC_STATE, default=True): sync_state_validator,
516  vol.Required(KNX_ADDRESS): ga_list_validator,
517  vol.Optional(CONF_STATE_ADDRESS): ga_list_validator,
518  vol.Optional(CONF_ENTITY_CATEGORY): ENTITY_CATEGORIES_SCHEMA,
519  }
520  )
521 
522 
524  """Voluptuous schema for KNX date."""
525 
526  PLATFORM = Platform.DATETIME
527 
528  DEFAULT_NAME = "KNX DateTime"
529 
530  ENTITY_SCHEMA = vol.Schema(
531  {
532  vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
533  vol.Optional(CONF_RESPOND_TO_READ, default=False): cv.boolean,
534  vol.Optional(CONF_SYNC_STATE, default=True): sync_state_validator,
535  vol.Required(KNX_ADDRESS): ga_list_validator,
536  vol.Optional(CONF_STATE_ADDRESS): ga_list_validator,
537  vol.Optional(CONF_ENTITY_CATEGORY): ENTITY_CATEGORIES_SCHEMA,
538  }
539  )
540 
541 
543  """Voluptuous schema for KNX exposures."""
544 
545  PLATFORM = CONF_KNX_EXPOSE
546 
547  CONF_KNX_EXPOSE_TYPE = CONF_TYPE
548  CONF_KNX_EXPOSE_ATTRIBUTE = "attribute"
549  CONF_KNX_EXPOSE_BINARY = "binary"
550  CONF_KNX_EXPOSE_COOLDOWN = "cooldown"
551  CONF_KNX_EXPOSE_DEFAULT = "default"
552  CONF_TIME = "time"
553  CONF_DATE = "date"
554  CONF_DATETIME = "datetime"
555  EXPOSE_TIME_TYPES: Final = [CONF_TIME, CONF_DATE, CONF_DATETIME]
556 
557  EXPOSE_TIME_SCHEMA = vol.Schema(
558  {
559  vol.Required(CONF_KNX_EXPOSE_TYPE): vol.All(
560  cv.string, str.lower, vol.In(EXPOSE_TIME_TYPES)
561  ),
562  vol.Required(KNX_ADDRESS): ga_validator,
563  }
564  )
565  EXPOSE_SENSOR_SCHEMA = vol.Schema(
566  {
567  vol.Optional(CONF_KNX_EXPOSE_COOLDOWN, default=0): cv.positive_float,
568  vol.Optional(CONF_RESPOND_TO_READ, default=True): cv.boolean,
569  vol.Required(CONF_KNX_EXPOSE_TYPE): vol.Any(
570  CONF_KNX_EXPOSE_BINARY, sensor_type_validator
571  ),
572  vol.Required(KNX_ADDRESS): ga_validator,
573  vol.Required(CONF_ENTITY_ID): cv.entity_id,
574  vol.Optional(CONF_KNX_EXPOSE_ATTRIBUTE): cv.string,
575  vol.Optional(CONF_KNX_EXPOSE_DEFAULT): cv.match_all,
576  vol.Optional(CONF_VALUE_TEMPLATE): cv.template,
577  }
578  )
579  ENTITY_SCHEMA = vol.Any(EXPOSE_SENSOR_SCHEMA, EXPOSE_TIME_SCHEMA)
580 
581 
583  """Voluptuous schema for KNX fans."""
584 
585  PLATFORM = Platform.FAN
586 
587  CONF_STATE_ADDRESS = CONF_STATE_ADDRESS
588  CONF_OSCILLATION_ADDRESS = "oscillation_address"
589  CONF_OSCILLATION_STATE_ADDRESS = "oscillation_state_address"
590  CONF_MAX_STEP = "max_step"
591 
592  DEFAULT_NAME = "KNX Fan"
593 
594  ENTITY_SCHEMA = vol.Schema(
595  {
596  vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
597  vol.Required(KNX_ADDRESS): ga_list_validator,
598  vol.Optional(CONF_STATE_ADDRESS): ga_list_validator,
599  vol.Optional(CONF_OSCILLATION_ADDRESS): ga_list_validator,
600  vol.Optional(CONF_OSCILLATION_STATE_ADDRESS): ga_list_validator,
601  vol.Optional(CONF_MAX_STEP): cv.byte,
602  vol.Optional(CONF_ENTITY_CATEGORY): ENTITY_CATEGORIES_SCHEMA,
603  }
604  )
605 
606 
608  """Voluptuous schema for KNX lights."""
609 
610  PLATFORM = Platform.LIGHT
611 
612  CONF_STATE_ADDRESS = CONF_STATE_ADDRESS
613  CONF_BRIGHTNESS_ADDRESS = "brightness_address"
614  CONF_BRIGHTNESS_STATE_ADDRESS = "brightness_state_address"
615  CONF_COLOR_ADDRESS = "color_address"
616  CONF_COLOR_STATE_ADDRESS = "color_state_address"
617  CONF_COLOR_TEMP_ADDRESS = "color_temperature_address"
618  CONF_COLOR_TEMP_STATE_ADDRESS = "color_temperature_state_address"
619  CONF_COLOR_TEMP_MODE = "color_temperature_mode"
620  CONF_HUE_ADDRESS = "hue_address"
621  CONF_HUE_STATE_ADDRESS = "hue_state_address"
622  CONF_RGBW_ADDRESS = "rgbw_address"
623  CONF_RGBW_STATE_ADDRESS = "rgbw_state_address"
624  CONF_SATURATION_ADDRESS = "saturation_address"
625  CONF_SATURATION_STATE_ADDRESS = "saturation_state_address"
626  CONF_XYY_ADDRESS = "xyy_address"
627  CONF_XYY_STATE_ADDRESS = "xyy_state_address"
628  CONF_MIN_KELVIN = "min_kelvin"
629  CONF_MAX_KELVIN = "max_kelvin"
630 
631  DEFAULT_NAME = "KNX Light"
632  DEFAULT_COLOR_TEMP_MODE = "absolute"
633  DEFAULT_MIN_KELVIN = 2700 # 370 mireds
634  DEFAULT_MAX_KELVIN = 6000 # 166 mireds
635 
636  CONF_INDIVIDUAL_COLORS = "individual_colors"
637  CONF_RED = "red"
638  CONF_GREEN = "green"
639  CONF_BLUE = "blue"
640  CONF_WHITE = "white"
641 
642  _hs_color_inclusion_msg = (
643  "'hue_address', 'saturation_address' and 'brightness_address'"
644  " are required for hs_color configuration"
645  )
646  HS_COLOR_SCHEMA = {
647  vol.Optional(CONF_HUE_ADDRESS): ga_list_validator,
648  vol.Optional(CONF_HUE_STATE_ADDRESS): ga_list_validator,
649  vol.Optional(CONF_SATURATION_ADDRESS): ga_list_validator,
650  vol.Optional(CONF_SATURATION_STATE_ADDRESS): ga_list_validator,
651  }
652 
653  INDIVIDUAL_COLOR_SCHEMA = vol.Schema(
654  {
655  vol.Optional(KNX_ADDRESS): ga_list_validator,
656  vol.Optional(CONF_STATE_ADDRESS): ga_list_validator,
657  vol.Required(CONF_BRIGHTNESS_ADDRESS): ga_list_validator,
658  vol.Optional(CONF_BRIGHTNESS_STATE_ADDRESS): ga_list_validator,
659  }
660  )
661 
662  ENTITY_SCHEMA = vol.All(
663  vol.Schema(
664  {
665  vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
666  vol.Optional(KNX_ADDRESS): ga_list_validator,
667  vol.Optional(CONF_STATE_ADDRESS): ga_list_validator,
668  vol.Optional(CONF_BRIGHTNESS_ADDRESS): ga_list_validator,
669  vol.Optional(CONF_BRIGHTNESS_STATE_ADDRESS): ga_list_validator,
670  vol.Exclusive(CONF_INDIVIDUAL_COLORS, "color"): {
671  vol.Inclusive(
672  CONF_RED,
673  "individual_colors",
674  msg=(
675  "'red', 'green' and 'blue' are required for individual"
676  " colors configuration"
677  ),
678  ): INDIVIDUAL_COLOR_SCHEMA,
679  vol.Inclusive(
680  CONF_GREEN,
681  "individual_colors",
682  msg=(
683  "'red', 'green' and 'blue' are required for individual"
684  " colors configuration"
685  ),
686  ): INDIVIDUAL_COLOR_SCHEMA,
687  vol.Inclusive(
688  CONF_BLUE,
689  "individual_colors",
690  msg=(
691  "'red', 'green' and 'blue' are required for individual"
692  " colors configuration"
693  ),
694  ): INDIVIDUAL_COLOR_SCHEMA,
695  vol.Optional(CONF_WHITE): INDIVIDUAL_COLOR_SCHEMA,
696  },
697  vol.Exclusive(CONF_COLOR_ADDRESS, "color"): ga_list_validator,
698  vol.Optional(CONF_COLOR_STATE_ADDRESS): ga_list_validator,
699  vol.Optional(CONF_COLOR_TEMP_ADDRESS): ga_list_validator,
700  vol.Optional(CONF_COLOR_TEMP_STATE_ADDRESS): ga_list_validator,
701  vol.Optional(
702  CONF_COLOR_TEMP_MODE, default=DEFAULT_COLOR_TEMP_MODE
703  ): vol.All(vol.Upper, cv.enum(ColorTempModes)),
704  **HS_COLOR_SCHEMA,
705  vol.Exclusive(CONF_RGBW_ADDRESS, "color"): ga_list_validator,
706  vol.Optional(CONF_RGBW_STATE_ADDRESS): ga_list_validator,
707  vol.Exclusive(CONF_XYY_ADDRESS, "color"): ga_list_validator,
708  vol.Optional(CONF_XYY_STATE_ADDRESS): ga_list_validator,
709  vol.Optional(CONF_MIN_KELVIN, default=DEFAULT_MIN_KELVIN): vol.All(
710  vol.Coerce(int), vol.Range(min=1)
711  ),
712  vol.Optional(CONF_MAX_KELVIN, default=DEFAULT_MAX_KELVIN): vol.All(
713  vol.Coerce(int), vol.Range(min=1)
714  ),
715  vol.Optional(CONF_ENTITY_CATEGORY): ENTITY_CATEGORIES_SCHEMA,
716  }
717  ),
718  vol.Any(
719  vol.Schema(
720  {vol.Required(KNX_ADDRESS): object},
721  extra=vol.ALLOW_EXTRA,
722  ),
723  vol.Schema( # brightness addresses are required in INDIVIDUAL_COLOR_SCHEMA
724  {vol.Required(CONF_INDIVIDUAL_COLORS): object},
725  extra=vol.ALLOW_EXTRA,
726  ),
727  msg="either 'address' or 'individual_colors' is required",
728  ),
729  vol.Any(
730  vol.Schema( # 'brightness' is non-optional for hs-color
731  {
732  vol.Inclusive(
733  CONF_BRIGHTNESS_ADDRESS, "hs_color", msg=_hs_color_inclusion_msg
734  ): object,
735  vol.Inclusive(
736  CONF_HUE_ADDRESS, "hs_color", msg=_hs_color_inclusion_msg
737  ): object,
738  vol.Inclusive(
739  CONF_SATURATION_ADDRESS, "hs_color", msg=_hs_color_inclusion_msg
740  ): object,
741  },
742  extra=vol.ALLOW_EXTRA,
743  ),
744  vol.Schema( # hs-colors not used
745  {
746  vol.Optional(CONF_HUE_ADDRESS): None,
747  vol.Optional(CONF_SATURATION_ADDRESS): None,
748  },
749  extra=vol.ALLOW_EXTRA,
750  ),
751  msg=_hs_color_inclusion_msg,
752  ),
753  )
754 
755 
757  """Voluptuous schema for KNX notifications."""
758 
759  PLATFORM = Platform.NOTIFY
760 
761  DEFAULT_NAME = "KNX Notify"
762 
763  ENTITY_SCHEMA = vol.Schema(
764  {
765  vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
766  vol.Optional(CONF_TYPE, default="latin_1"): string_type_validator,
767  vol.Required(KNX_ADDRESS): ga_validator,
768  vol.Optional(CONF_ENTITY_CATEGORY): ENTITY_CATEGORIES_SCHEMA,
769  }
770  )
771 
772 
774  """Voluptuous schema for KNX numbers."""
775 
776  PLATFORM = Platform.NUMBER
777 
778  CONF_MAX = "max"
779  CONF_MIN = "min"
780  CONF_STEP = "step"
781  DEFAULT_NAME = "KNX Number"
782 
783  ENTITY_SCHEMA = vol.All(
784  vol.Schema(
785  {
786  vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
787  vol.Optional(CONF_RESPOND_TO_READ, default=False): cv.boolean,
788  vol.Optional(CONF_MODE, default=NumberMode.AUTO): vol.Coerce(
789  NumberMode
790  ),
791  vol.Required(CONF_TYPE): numeric_type_validator,
792  vol.Required(KNX_ADDRESS): ga_list_validator,
793  vol.Optional(CONF_STATE_ADDRESS): ga_list_validator,
794  vol.Optional(CONF_MAX): vol.Coerce(float),
795  vol.Optional(CONF_MIN): vol.Coerce(float),
796  vol.Optional(CONF_STEP): cv.positive_float,
797  vol.Optional(CONF_ENTITY_CATEGORY): ENTITY_CATEGORIES_SCHEMA,
798  }
799  ),
800  number_limit_sub_validator,
801  )
802 
803 
805  """Voluptuous schema for KNX scenes."""
806 
807  PLATFORM = Platform.SCENE
808 
809  CONF_SCENE_NUMBER = "scene_number"
810 
811  DEFAULT_NAME = "KNX SCENE"
812  ENTITY_SCHEMA = vol.Schema(
813  {
814  vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
815  vol.Required(KNX_ADDRESS): ga_list_validator,
816  vol.Required(CONF_SCENE_NUMBER): vol.All(
817  vol.Coerce(int), vol.Range(min=1, max=64)
818  ),
819  vol.Optional(CONF_ENTITY_CATEGORY): ENTITY_CATEGORIES_SCHEMA,
820  }
821  )
822 
823 
825  """Voluptuous schema for KNX selects."""
826 
827  PLATFORM = Platform.SELECT
828 
829  CONF_OPTION = "option"
830  CONF_OPTIONS = "options"
831  DEFAULT_NAME = "KNX Select"
832 
833  ENTITY_SCHEMA = vol.All(
834  vol.Schema(
835  {
836  vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
837  vol.Optional(CONF_SYNC_STATE, default=True): sync_state_validator,
838  vol.Optional(CONF_RESPOND_TO_READ, default=False): cv.boolean,
839  vol.Required(CONF_PAYLOAD_LENGTH): vol.All(
840  vol.Coerce(int), vol.Range(min=0, max=14)
841  ),
842  vol.Required(CONF_OPTIONS): [
843  {
844  vol.Required(CONF_OPTION): vol.Coerce(str),
845  vol.Required(CONF_PAYLOAD): cv.positive_int,
846  }
847  ],
848  vol.Required(KNX_ADDRESS): ga_list_validator,
849  vol.Optional(CONF_STATE_ADDRESS): ga_list_validator,
850  vol.Optional(CONF_ENTITY_CATEGORY): ENTITY_CATEGORIES_SCHEMA,
851  }
852  ),
853  select_options_sub_validator,
854  )
855 
856 
858  """Voluptuous schema for KNX sensors."""
859 
860  PLATFORM = Platform.SENSOR
861 
862  CONF_ALWAYS_CALLBACK = "always_callback"
863  CONF_STATE_ADDRESS = CONF_STATE_ADDRESS
864  CONF_SYNC_STATE = CONF_SYNC_STATE
865  DEFAULT_NAME = "KNX Sensor"
866 
867  ENTITY_SCHEMA = vol.Schema(
868  {
869  vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
870  vol.Optional(CONF_SYNC_STATE, default=True): sync_state_validator,
871  vol.Optional(CONF_ALWAYS_CALLBACK, default=False): cv.boolean,
872  vol.Optional(CONF_STATE_CLASS): STATE_CLASSES_SCHEMA,
873  vol.Required(CONF_TYPE): sensor_type_validator,
874  vol.Required(CONF_STATE_ADDRESS): ga_list_validator,
875  vol.Optional(CONF_DEVICE_CLASS): SENSOR_DEVICE_CLASSES_SCHEMA,
876  vol.Optional(CONF_ENTITY_CATEGORY): ENTITY_CATEGORIES_SCHEMA,
877  }
878  )
879 
880 
882  """Voluptuous schema for KNX switches."""
883 
884  PLATFORM = Platform.SWITCH
885 
886  CONF_INVERT = CONF_INVERT
887  CONF_STATE_ADDRESS = CONF_STATE_ADDRESS
888 
889  DEFAULT_NAME = "KNX Switch"
890  ENTITY_SCHEMA = vol.Schema(
891  {
892  vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
893  vol.Optional(CONF_INVERT, default=False): cv.boolean,
894  vol.Optional(CONF_RESPOND_TO_READ, default=False): cv.boolean,
895  vol.Required(KNX_ADDRESS): ga_list_validator,
896  vol.Optional(CONF_STATE_ADDRESS): ga_list_validator,
897  vol.Optional(CONF_DEVICE_CLASS): SWITCH_DEVICE_CLASSES_SCHEMA,
898  vol.Optional(CONF_ENTITY_CATEGORY): ENTITY_CATEGORIES_SCHEMA,
899  }
900  )
901 
902 
904  """Voluptuous schema for KNX text."""
905 
906  PLATFORM = Platform.TEXT
907 
908  DEFAULT_NAME = "KNX Text"
909 
910  ENTITY_SCHEMA = vol.Schema(
911  {
912  vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
913  vol.Optional(CONF_RESPOND_TO_READ, default=False): cv.boolean,
914  vol.Optional(CONF_TYPE, default="latin_1"): string_type_validator,
915  vol.Optional(CONF_MODE, default=TextMode.TEXT): vol.Coerce(TextMode),
916  vol.Required(KNX_ADDRESS): ga_list_validator,
917  vol.Optional(CONF_STATE_ADDRESS): ga_list_validator,
918  vol.Optional(CONF_ENTITY_CATEGORY): ENTITY_CATEGORIES_SCHEMA,
919  }
920  )
921 
922 
924  """Voluptuous schema for KNX time."""
925 
926  PLATFORM = Platform.TIME
927 
928  DEFAULT_NAME = "KNX Time"
929 
930  ENTITY_SCHEMA = vol.Schema(
931  {
932  vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
933  vol.Optional(CONF_RESPOND_TO_READ, default=False): cv.boolean,
934  vol.Optional(CONF_SYNC_STATE, default=True): sync_state_validator,
935  vol.Required(KNX_ADDRESS): ga_list_validator,
936  vol.Optional(CONF_STATE_ADDRESS): ga_list_validator,
937  vol.Optional(CONF_ENTITY_CATEGORY): ENTITY_CATEGORIES_SCHEMA,
938  }
939  )
940 
941 
943  """Voluptuous schema for KNX weather station."""
944 
945  PLATFORM = Platform.WEATHER
946 
947  CONF_SYNC_STATE = CONF_SYNC_STATE
948  CONF_KNX_TEMPERATURE_ADDRESS = "address_temperature"
949  CONF_KNX_BRIGHTNESS_SOUTH_ADDRESS = "address_brightness_south"
950  CONF_KNX_BRIGHTNESS_EAST_ADDRESS = "address_brightness_east"
951  CONF_KNX_BRIGHTNESS_WEST_ADDRESS = "address_brightness_west"
952  CONF_KNX_BRIGHTNESS_NORTH_ADDRESS = "address_brightness_north"
953  CONF_KNX_WIND_SPEED_ADDRESS = "address_wind_speed"
954  CONF_KNX_WIND_BEARING_ADDRESS = "address_wind_bearing"
955  CONF_KNX_RAIN_ALARM_ADDRESS = "address_rain_alarm"
956  CONF_KNX_FROST_ALARM_ADDRESS = "address_frost_alarm"
957  CONF_KNX_WIND_ALARM_ADDRESS = "address_wind_alarm"
958  CONF_KNX_DAY_NIGHT_ADDRESS = "address_day_night"
959  CONF_KNX_AIR_PRESSURE_ADDRESS = "address_air_pressure"
960  CONF_KNX_HUMIDITY_ADDRESS = "address_humidity"
961 
962  DEFAULT_NAME = "KNX Weather Station"
963 
964  ENTITY_SCHEMA = vol.All(
965  vol.Schema(
966  {
967  vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
968  vol.Optional(CONF_SYNC_STATE, default=True): sync_state_validator,
969  vol.Required(CONF_KNX_TEMPERATURE_ADDRESS): ga_list_validator,
970  vol.Optional(CONF_KNX_BRIGHTNESS_SOUTH_ADDRESS): ga_list_validator,
971  vol.Optional(CONF_KNX_BRIGHTNESS_EAST_ADDRESS): ga_list_validator,
972  vol.Optional(CONF_KNX_BRIGHTNESS_WEST_ADDRESS): ga_list_validator,
973  vol.Optional(CONF_KNX_BRIGHTNESS_NORTH_ADDRESS): ga_list_validator,
974  vol.Optional(CONF_KNX_WIND_SPEED_ADDRESS): ga_list_validator,
975  vol.Optional(CONF_KNX_WIND_BEARING_ADDRESS): ga_list_validator,
976  vol.Optional(CONF_KNX_RAIN_ALARM_ADDRESS): ga_list_validator,
977  vol.Optional(CONF_KNX_FROST_ALARM_ADDRESS): ga_list_validator,
978  vol.Optional(CONF_KNX_WIND_ALARM_ADDRESS): ga_list_validator,
979  vol.Optional(CONF_KNX_DAY_NIGHT_ADDRESS): ga_list_validator,
980  vol.Optional(CONF_KNX_AIR_PRESSURE_ADDRESS): ga_list_validator,
981  vol.Optional(CONF_KNX_HUMIDITY_ADDRESS): ga_list_validator,
982  vol.Optional(CONF_ENTITY_CATEGORY): ENTITY_CATEGORIES_SCHEMA,
983  }
984  ),
985  )
dict[vol.Optional, vol.All] platform_node(cls)
Definition: schema.py:201
OrderedDict number_limit_sub_validator(OrderedDict entity_config)
KNX SUB VALIDATORS.
Definition: schema.py:74
OrderedDict select_options_sub_validator(OrderedDict entity_config)
Definition: schema.py:144
OrderedDict button_payload_sub_validator(OrderedDict entity_config)
Definition: schema.py:117
int _max_payload_value(int payload_length)
Definition: schema.py:111
vol.All backwards_compatible_xknx_climate_enum_member(type[Enum] enumClass)
Definition: validation.py:110