Home Assistant Unofficial Reference 2024.12.1
config_flow.py
Go to the documentation of this file.
1 """Adds config flow for Scrape integration."""
2 
3 from __future__ import annotations
4 
5 from collections.abc import Mapping
6 from typing import Any, cast
7 import uuid
8 
9 import voluptuous as vol
10 
11 from homeassistant.components.rest import create_rest_data_from_config
12 from homeassistant.components.rest.data import DEFAULT_TIMEOUT
13 from homeassistant.components.rest.schema import DEFAULT_METHOD, METHODS
15  CONF_STATE_CLASS,
16  DOMAIN as SENSOR_DOMAIN,
17  SensorDeviceClass,
18  SensorStateClass,
19 )
20 from homeassistant.const import (
21  CONF_ATTRIBUTE,
22  CONF_AUTHENTICATION,
23  CONF_DEVICE_CLASS,
24  CONF_HEADERS,
25  CONF_METHOD,
26  CONF_NAME,
27  CONF_PASSWORD,
28  CONF_PAYLOAD,
29  CONF_RESOURCE,
30  CONF_TIMEOUT,
31  CONF_UNIQUE_ID,
32  CONF_UNIT_OF_MEASUREMENT,
33  CONF_USERNAME,
34  CONF_VALUE_TEMPLATE,
35  CONF_VERIFY_SSL,
36  HTTP_BASIC_AUTHENTICATION,
37  HTTP_DIGEST_AUTHENTICATION,
38  UnitOfTemperature,
39 )
40 from homeassistant.core import async_get_hass
41 from homeassistant.helpers import config_validation as cv, entity_registry as er
43  SchemaCommonFlowHandler,
44  SchemaConfigFlowHandler,
45  SchemaFlowError,
46  SchemaFlowFormStep,
47  SchemaFlowMenuStep,
48 )
50  BooleanSelector,
51  NumberSelector,
52  NumberSelectorConfig,
53  NumberSelectorMode,
54  ObjectSelector,
55  SelectSelector,
56  SelectSelectorConfig,
57  SelectSelectorMode,
58  TemplateSelector,
59  TextSelector,
60  TextSelectorConfig,
61  TextSelectorType,
62 )
63 from homeassistant.helpers.trigger_template_entity import CONF_AVAILABILITY
64 
65 from . import COMBINED_SCHEMA
66 from .const import (
67  CONF_ENCODING,
68  CONF_INDEX,
69  CONF_SELECT,
70  DEFAULT_ENCODING,
71  DEFAULT_NAME,
72  DEFAULT_VERIFY_SSL,
73  DOMAIN,
74 )
75 
76 RESOURCE_SETUP = {
77  vol.Required(CONF_RESOURCE): TextSelector(
78  TextSelectorConfig(type=TextSelectorType.URL)
79  ),
80  vol.Optional(CONF_METHOD, default=DEFAULT_METHOD): SelectSelector(
81  SelectSelectorConfig(options=METHODS, mode=SelectSelectorMode.DROPDOWN)
82  ),
83  vol.Optional(CONF_PAYLOAD): ObjectSelector(),
84  vol.Optional(CONF_AUTHENTICATION): SelectSelector(
86  options=[HTTP_BASIC_AUTHENTICATION, HTTP_DIGEST_AUTHENTICATION],
87  mode=SelectSelectorMode.DROPDOWN,
88  )
89  ),
90  vol.Optional(CONF_USERNAME): TextSelector(),
91  vol.Optional(CONF_PASSWORD): TextSelector(
92  TextSelectorConfig(type=TextSelectorType.PASSWORD)
93  ),
94  vol.Optional(CONF_HEADERS): ObjectSelector(),
95  vol.Optional(CONF_VERIFY_SSL, default=DEFAULT_VERIFY_SSL): BooleanSelector(),
96  vol.Optional(CONF_TIMEOUT, default=DEFAULT_TIMEOUT): NumberSelector(
97  NumberSelectorConfig(min=0, step=1, mode=NumberSelectorMode.BOX)
98  ),
99  vol.Optional(CONF_ENCODING, default=DEFAULT_ENCODING): TextSelector(),
100 }
101 
102 SENSOR_SETUP = {
103  vol.Required(CONF_SELECT): TextSelector(),
104  vol.Optional(CONF_INDEX, default=0): NumberSelector(
105  NumberSelectorConfig(min=0, step=1, mode=NumberSelectorMode.BOX)
106  ),
107  vol.Optional(CONF_ATTRIBUTE): TextSelector(),
108  vol.Optional(CONF_VALUE_TEMPLATE): TemplateSelector(),
109  vol.Optional(CONF_AVAILABILITY): TemplateSelector(),
110  vol.Optional(CONF_DEVICE_CLASS): SelectSelector(
112  options=[
113  cls.value for cls in SensorDeviceClass if cls != SensorDeviceClass.ENUM
114  ],
115  mode=SelectSelectorMode.DROPDOWN,
116  translation_key="device_class",
117  sort=True,
118  )
119  ),
120  vol.Optional(CONF_STATE_CLASS): SelectSelector(
122  options=[cls.value for cls in SensorStateClass],
123  mode=SelectSelectorMode.DROPDOWN,
124  translation_key="state_class",
125  sort=True,
126  )
127  ),
128  vol.Optional(CONF_UNIT_OF_MEASUREMENT): SelectSelector(
130  options=[cls.value for cls in UnitOfTemperature],
131  custom_value=True,
132  mode=SelectSelectorMode.DROPDOWN,
133  translation_key="unit_of_measurement",
134  sort=True,
135  )
136  ),
137 }
138 
139 
141  handler: SchemaCommonFlowHandler, user_input: dict[str, Any]
142 ) -> dict[str, Any]:
143  """Validate rest setup."""
144  hass = async_get_hass()
145  rest_config: dict[str, Any] = COMBINED_SCHEMA(user_input)
146  try:
147  rest = create_rest_data_from_config(hass, rest_config)
148  await rest.async_update()
149  except Exception as err:
150  raise SchemaFlowError("resource_error") from err
151  if rest.data is None:
152  raise SchemaFlowError("resource_error")
153  return user_input
154 
155 
157  handler: SchemaCommonFlowHandler, user_input: dict[str, Any]
158 ) -> dict[str, Any]:
159  """Validate sensor input."""
160  user_input[CONF_INDEX] = int(user_input[CONF_INDEX])
161  user_input[CONF_UNIQUE_ID] = str(uuid.uuid1())
162 
163  # Standard behavior is to merge the result with the options.
164  # In this case, we want to add a sub-item so we update the options directly.
165  sensors: list[dict[str, Any]] = handler.options.setdefault(SENSOR_DOMAIN, [])
166  sensors.append(user_input)
167  return {}
168 
169 
171  handler: SchemaCommonFlowHandler, user_input: dict[str, Any]
172 ) -> dict[str, Any]:
173  """Store sensor index in flow state."""
174  handler.flow_state["_idx"] = int(user_input[CONF_INDEX])
175  return {}
176 
177 
178 async def get_select_sensor_schema(handler: SchemaCommonFlowHandler) -> vol.Schema:
179  """Return schema for selecting a sensor."""
180  return vol.Schema(
181  {
182  vol.Required(CONF_INDEX): vol.In(
183  {
184  str(index): config[CONF_NAME]
185  for index, config in enumerate(handler.options[SENSOR_DOMAIN])
186  },
187  )
188  }
189  )
190 
191 
193  handler: SchemaCommonFlowHandler,
194 ) -> dict[str, Any]:
195  """Return suggested values for sensor editing."""
196  idx: int = handler.flow_state["_idx"]
197  return dict(handler.options[SENSOR_DOMAIN][idx])
198 
199 
201  handler: SchemaCommonFlowHandler, user_input: dict[str, Any]
202 ) -> dict[str, Any]:
203  """Update edited sensor."""
204  user_input[CONF_INDEX] = int(user_input[CONF_INDEX])
205 
206  # Standard behavior is to merge the result with the options.
207  # In this case, we want to add a sub-item so we update the options directly,
208  # including popping omitted optional schema items.
209  idx: int = handler.flow_state["_idx"]
210  handler.options[SENSOR_DOMAIN][idx].update(user_input)
211  for key in DATA_SCHEMA_EDIT_SENSOR.schema:
212  if isinstance(key, vol.Optional) and key not in user_input:
213  # Key not present, delete keys old value (if present) too
214  handler.options[SENSOR_DOMAIN][idx].pop(key, None)
215  return {}
216 
217 
218 async def get_remove_sensor_schema(handler: SchemaCommonFlowHandler) -> vol.Schema:
219  """Return schema for sensor removal."""
220  return vol.Schema(
221  {
222  vol.Required(CONF_INDEX): cv.multi_select(
223  {
224  str(index): config[CONF_NAME]
225  for index, config in enumerate(handler.options[SENSOR_DOMAIN])
226  },
227  )
228  }
229  )
230 
231 
233  handler: SchemaCommonFlowHandler, user_input: dict[str, Any]
234 ) -> dict[str, Any]:
235  """Validate remove sensor."""
236  removed_indexes: set[str] = set(user_input[CONF_INDEX])
237 
238  # Standard behavior is to merge the result with the options.
239  # In this case, we want to remove sub-items so we update the options directly.
240  entity_registry = er.async_get(handler.parent_handler.hass)
241  sensors: list[dict[str, Any]] = []
242  sensor: dict[str, Any]
243  for index, sensor in enumerate(handler.options[SENSOR_DOMAIN]):
244  if str(index) not in removed_indexes:
245  sensors.append(sensor)
246  elif entity_id := entity_registry.async_get_entity_id(
247  SENSOR_DOMAIN, DOMAIN, sensor[CONF_UNIQUE_ID]
248  ):
249  entity_registry.async_remove(entity_id)
250  handler.options[SENSOR_DOMAIN] = sensors
251  return {}
252 
253 
254 DATA_SCHEMA_RESOURCE = vol.Schema(RESOURCE_SETUP)
255 DATA_SCHEMA_EDIT_SENSOR = vol.Schema(SENSOR_SETUP)
256 DATA_SCHEMA_SENSOR = vol.Schema(
257  {
258  vol.Optional(CONF_NAME, default=DEFAULT_NAME): TextSelector(),
259  **SENSOR_SETUP,
260  }
261 )
262 
263 CONFIG_FLOW = {
264  "user": SchemaFlowFormStep(
265  schema=DATA_SCHEMA_RESOURCE,
266  next_step="sensor",
267  validate_user_input=validate_rest_setup,
268  ),
269  "sensor": SchemaFlowFormStep(
270  schema=DATA_SCHEMA_SENSOR,
271  validate_user_input=validate_sensor_setup,
272  ),
273 }
274 OPTIONS_FLOW = {
275  "init": SchemaFlowMenuStep(
276  ["resource", "add_sensor", "select_edit_sensor", "remove_sensor"]
277  ),
278  "resource": SchemaFlowFormStep(
279  DATA_SCHEMA_RESOURCE,
280  validate_user_input=validate_rest_setup,
281  ),
282  "add_sensor": SchemaFlowFormStep(
283  DATA_SCHEMA_SENSOR,
284  suggested_values=None,
285  validate_user_input=validate_sensor_setup,
286  ),
287  "select_edit_sensor": SchemaFlowFormStep(
288  get_select_sensor_schema,
289  suggested_values=None,
290  validate_user_input=validate_select_sensor,
291  next_step="edit_sensor",
292  ),
293  "edit_sensor": SchemaFlowFormStep(
294  DATA_SCHEMA_EDIT_SENSOR,
295  suggested_values=get_edit_sensor_suggested_values,
296  validate_user_input=validate_sensor_edit,
297  ),
298  "remove_sensor": SchemaFlowFormStep(
299  get_remove_sensor_schema,
300  suggested_values=None,
301  validate_user_input=validate_remove_sensor,
302  ),
303 }
304 
305 
307  """Handle a config flow for Scrape."""
308 
309  config_flow = CONFIG_FLOW
310  options_flow = OPTIONS_FLOW
311 
312  def async_config_entry_title(self, options: Mapping[str, Any]) -> str:
313  """Return config entry title."""
314  return cast(str, options[CONF_RESOURCE])
IssData update(pyiss.ISS iss)
Definition: __init__.py:33
RestData create_rest_data_from_config(HomeAssistant hass, ConfigType config)
Definition: __init__.py:190
vol.Schema get_remove_sensor_schema(SchemaCommonFlowHandler handler)
Definition: config_flow.py:218
dict[str, Any] validate_sensor_setup(SchemaCommonFlowHandler handler, dict[str, Any] user_input)
Definition: config_flow.py:158
dict[str, Any] validate_remove_sensor(SchemaCommonFlowHandler handler, dict[str, Any] user_input)
Definition: config_flow.py:234
dict[str, Any] validate_select_sensor(SchemaCommonFlowHandler handler, dict[str, Any] user_input)
Definition: config_flow.py:172
dict[str, Any] validate_sensor_edit(SchemaCommonFlowHandler handler, dict[str, Any] user_input)
Definition: config_flow.py:202
dict[str, Any] get_edit_sensor_suggested_values(SchemaCommonFlowHandler handler)
Definition: config_flow.py:194
dict[str, Any] validate_rest_setup(SchemaCommonFlowHandler handler, dict[str, Any] user_input)
Definition: config_flow.py:142
vol.Schema get_select_sensor_schema(SchemaCommonFlowHandler handler)
Definition: config_flow.py:178
HomeAssistant async_get_hass()
Definition: core.py:286