Home Assistant Unofficial Reference 2024.12.1
schema.py
Go to the documentation of this file.
1 """Schema for config entries."""
2 
3 from __future__ import annotations
4 
5 from typing import Any
6 
7 import voluptuous as vol
8 
9 from homeassistant.components.cover import DEVICE_CLASSES_SCHEMA
10 from homeassistant.const import CONF_DEFAULT, CONF_HOST, CONF_NAME, CONF_PORT, CONF_TYPE
11 from homeassistant.helpers import config_validation as cv
12 
13 from .const import (
14  ACTIVE_INIT,
15  ACTIVE_OFF,
16  ACTIVE_ON,
17  CONF_ACTIVE,
18  CONF_AREA,
19  CONF_AUTO_DISCOVER,
20  CONF_CHANNEL,
21  CONF_CHANNEL_COVER,
22  CONF_CLOSE_PRESET,
23  CONF_DEVICE_CLASS,
24  CONF_DURATION,
25  CONF_FADE,
26  CONF_LEVEL,
27  CONF_NO_DEFAULT,
28  CONF_OPEN_PRESET,
29  CONF_POLL_TIMER,
30  CONF_PRESET,
31  CONF_ROOM_OFF,
32  CONF_ROOM_ON,
33  CONF_STOP_PRESET,
34  CONF_TEMPLATE,
35  CONF_TILT_TIME,
36  DEFAULT_CHANNEL_TYPE,
37  DEFAULT_NAME,
38  DEFAULT_PORT,
39  DEFAULT_TEMPLATES,
40 )
41 
42 
43 def num_string(value: str | int) -> str:
44  """Test if value is a string of digits, aka an integer."""
45  new_value = str(value)
46  if new_value.isdigit():
47  return new_value
48  raise vol.Invalid("Not a string with numbers")
49 
50 
51 CHANNEL_DATA_SCHEMA = vol.Schema(
52  {
53  vol.Optional(CONF_NAME): cv.string,
54  vol.Optional(CONF_FADE): vol.Coerce(float),
55  vol.Optional(CONF_TYPE, default=DEFAULT_CHANNEL_TYPE): vol.Any(
56  "light", "switch"
57  ),
58  }
59 )
60 
61 CHANNEL_SCHEMA = vol.Schema({num_string: CHANNEL_DATA_SCHEMA})
62 
63 PRESET_DATA_SCHEMA = vol.Schema(
64  {
65  vol.Optional(CONF_NAME): cv.string,
66  vol.Optional(CONF_FADE): vol.Coerce(float),
67  vol.Optional(CONF_LEVEL): vol.Coerce(float),
68  }
69 )
70 
71 PRESET_SCHEMA = vol.Schema({num_string: vol.Any(PRESET_DATA_SCHEMA, None)})
72 
73 TEMPLATE_ROOM_SCHEMA = vol.Schema(
74  {vol.Optional(CONF_ROOM_ON): num_string, vol.Optional(CONF_ROOM_OFF): num_string}
75 )
76 
77 TEMPLATE_TIMECOVER_SCHEMA = vol.Schema(
78  {
79  vol.Optional(CONF_CHANNEL_COVER): num_string,
80  vol.Optional(CONF_DEVICE_CLASS): DEVICE_CLASSES_SCHEMA,
81  vol.Optional(CONF_OPEN_PRESET): num_string,
82  vol.Optional(CONF_CLOSE_PRESET): num_string,
83  vol.Optional(CONF_STOP_PRESET): num_string,
84  vol.Optional(CONF_DURATION): vol.Coerce(float),
85  vol.Optional(CONF_TILT_TIME): vol.Coerce(float),
86  }
87 )
88 
89 TEMPLATE_DATA_SCHEMA = vol.Any(TEMPLATE_ROOM_SCHEMA, TEMPLATE_TIMECOVER_SCHEMA)
90 
91 TEMPLATE_SCHEMA = vol.Schema({str: TEMPLATE_DATA_SCHEMA})
92 
93 
94 def validate_area(config: dict[str, Any]) -> dict[str, Any]:
95  """Validate that template parameters are only used if area is using the relevant template."""
96  conf_set = set()
97  for configs in DEFAULT_TEMPLATES.values():
98  for conf in configs:
99  conf_set.add(conf)
100  if config.get(CONF_TEMPLATE):
101  for conf in DEFAULT_TEMPLATES[config[CONF_TEMPLATE]]:
102  conf_set.remove(conf)
103  for conf in conf_set:
104  if config.get(conf):
105  raise vol.Invalid(
106  f"{conf} should not be part of area {config[CONF_NAME]} config"
107  )
108  return config
109 
110 
111 AREA_DATA_SCHEMA = vol.Schema(
112  vol.All(
113  {
114  vol.Required(CONF_NAME): cv.string,
115  vol.Optional(CONF_TEMPLATE): vol.In(DEFAULT_TEMPLATES),
116  vol.Optional(CONF_FADE): vol.Coerce(float),
117  vol.Optional(CONF_NO_DEFAULT): cv.boolean,
118  vol.Optional(CONF_CHANNEL): CHANNEL_SCHEMA,
119  vol.Optional(CONF_PRESET): PRESET_SCHEMA,
120  # the next ones can be part of the templates
121  vol.Optional(CONF_ROOM_ON): num_string,
122  vol.Optional(CONF_ROOM_OFF): num_string,
123  vol.Optional(CONF_CHANNEL_COVER): num_string,
124  vol.Optional(CONF_DEVICE_CLASS): DEVICE_CLASSES_SCHEMA,
125  vol.Optional(CONF_OPEN_PRESET): num_string,
126  vol.Optional(CONF_CLOSE_PRESET): num_string,
127  vol.Optional(CONF_STOP_PRESET): num_string,
128  vol.Optional(CONF_DURATION): vol.Coerce(float),
129  vol.Optional(CONF_TILT_TIME): vol.Coerce(float),
130  },
131  validate_area,
132  )
133 )
134 
135 AREA_SCHEMA = vol.Schema({num_string: vol.Any(AREA_DATA_SCHEMA, None)})
136 
137 PLATFORM_DEFAULTS_SCHEMA = vol.Schema({vol.Optional(CONF_FADE): vol.Coerce(float)})
138 
139 
140 BRIDGE_SCHEMA = vol.Schema(
141  {
142  vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
143  vol.Required(CONF_HOST): cv.string,
144  vol.Optional(CONF_PORT, default=DEFAULT_PORT): int,
145  vol.Optional(CONF_AUTO_DISCOVER, default=False): vol.Coerce(bool),
146  vol.Optional(CONF_POLL_TIMER, default=1.0): vol.Coerce(float),
147  vol.Optional(CONF_AREA): AREA_SCHEMA,
148  vol.Optional(CONF_DEFAULT): PLATFORM_DEFAULTS_SCHEMA,
149  vol.Optional(CONF_ACTIVE, default=False): vol.Any(
150  ACTIVE_ON, ACTIVE_OFF, ACTIVE_INIT, cv.boolean
151  ),
152  vol.Optional(CONF_PRESET): PRESET_SCHEMA,
153  vol.Optional(CONF_TEMPLATE): TEMPLATE_SCHEMA,
154  }
155 )
dict[str, Any] validate_area(dict[str, Any] config)
Definition: schema.py:94