Home Assistant Unofficial Reference 2024.12.1
config_flow.py
Go to the documentation of this file.
1 """Config flow for the Home Assistant Yellow integration."""
2 
3 from __future__ import annotations
4 
5 from abc import ABC, abstractmethod
6 import asyncio
7 import logging
8 from typing import Any, final
9 
10 import aiohttp
11 from universal_silabs_flasher.const import ApplicationType
12 import voluptuous as vol
13 
15  HassioAPIError,
16  async_get_yellow_settings,
17  async_reboot_host,
18  async_set_yellow_settings,
19 )
21  BaseFirmwareConfigFlow,
22  BaseFirmwareOptionsFlow,
23 )
25  OptionsFlowHandler as MultiprotocolOptionsFlowHandler,
26  SerialPortSettings as MultiprotocolSerialPortSettings,
27 )
28 from homeassistant.config_entries import (
29  SOURCE_HARDWARE,
30  ConfigEntry,
31  ConfigFlowResult,
32  OptionsFlow,
33 )
34 from homeassistant.core import callback
35 from homeassistant.helpers import discovery_flow, selector
36 
37 from .const import DOMAIN, FIRMWARE, RADIO_DEVICE, ZHA_DOMAIN, ZHA_HW_DISCOVERY_DATA
38 from .hardware import BOARD_NAME
39 
40 _LOGGER = logging.getLogger(__name__)
41 
42 STEP_HW_SETTINGS_SCHEMA = vol.Schema(
43  {
44  vol.Required("disk_led"): selector.BooleanSelector(),
45  vol.Required("heartbeat_led"): selector.BooleanSelector(),
46  vol.Required("power_led"): selector.BooleanSelector(),
47  }
48 )
49 
50 
52  """Handle a config flow for Home Assistant Yellow."""
53 
54  VERSION = 1
55  MINOR_VERSION = 2
56 
57  def __init__(self, *args: Any, **kwargs: Any) -> None:
58  """Instantiate config flow."""
59  super().__init__(*args, **kwargs)
60 
61  self._device_device = RADIO_DEVICE
62 
63  @staticmethod
64  @callback
66  config_entry: ConfigEntry,
67  ) -> OptionsFlow:
68  """Return the options flow."""
69  firmware_type = ApplicationType(config_entry.data[FIRMWARE])
70 
71  if firmware_type is ApplicationType.CPC:
73 
74  return HomeAssistantYellowOptionsFlowHandler(config_entry)
75 
76  async def async_step_system(
77  self, data: dict[str, Any] | None = None
78  ) -> ConfigFlowResult:
79  """Handle the initial step."""
80  # We do not actually use any portion of `BaseFirmwareConfigFlow` beyond this
81  await self._probe_firmware_type_probe_firmware_type()
82 
83  # Kick off ZHA hardware discovery automatically if Zigbee firmware is running
84  if self._probed_firmware_type_probed_firmware_type is ApplicationType.EZSP:
85  discovery_flow.async_create_flow(
86  self.hass,
87  ZHA_DOMAIN,
88  context={"source": SOURCE_HARDWARE},
89  data=ZHA_HW_DISCOVERY_DATA,
90  )
91 
92  return self._async_flow_finished_async_flow_finished_async_flow_finished()
93 
94  def _async_flow_finished(self) -> ConfigFlowResult:
95  """Create the config entry."""
96  return self.async_create_entryasync_create_entryasync_create_entry(
97  title=BOARD_NAME,
98  data={
99  # Assume the firmware type is EZSP if we cannot probe it
100  FIRMWARE: (self._probed_firmware_type_probed_firmware_type or ApplicationType.EZSP).value,
101  },
102  )
103 
104 
106  """Base Home Assistant Yellow options flow shared between firmware and multi-PAN."""
107 
108  _hw_settings: dict[str, bool] | None = None
109 
110  @abstractmethod
111  async def async_step_main_menu(self, _: None = None) -> ConfigFlowResult:
112  """Show the main menu."""
113 
114  @final
115  async def async_step_init(
116  self, user_input: dict[str, Any] | None = None
117  ) -> ConfigFlowResult:
118  """Manage the options flow."""
119  return await self.async_step_main_menuasync_step_main_menu()
120 
121  @final
122  async def async_step_on_supervisor(
123  self, user_input: dict[str, Any] | None = None
124  ) -> ConfigFlowResult:
125  """Handle logic when on Supervisor host."""
126  return await self.async_step_main_menuasync_step_main_menu()
127 
128  async def async_step_hardware_settings(
129  self, user_input: dict[str, Any] | None = None
130  ) -> ConfigFlowResult:
131  """Handle hardware settings."""
132 
133  if user_input is not None:
134  if self._hw_settings_hw_settings == user_input:
135  return self.async_create_entryasync_create_entry(data={})
136  try:
137  async with asyncio.timeout(10):
138  await async_set_yellow_settings(self.hass, user_input)
139  except (aiohttp.ClientError, TimeoutError, HassioAPIError) as err:
140  _LOGGER.warning("Failed to write hardware settings", exc_info=err)
141  return self.async_abortasync_abort(reason="write_hw_settings_error")
142  return await self.async_step_reboot_menu()
143 
144  try:
145  async with asyncio.timeout(10):
146  self._hw_settings_hw_settings: dict[str, bool] = await async_get_yellow_settings(
147  self.hass
148  )
149  except (aiohttp.ClientError, TimeoutError, HassioAPIError) as err:
150  _LOGGER.warning("Failed to read hardware settings", exc_info=err)
151  return self.async_abortasync_abort(reason="read_hw_settings_error")
152 
153  schema = self.add_suggested_values_to_schemaadd_suggested_values_to_schema(
154  STEP_HW_SETTINGS_SCHEMA, self._hw_settings_hw_settings
155  )
156 
157  return self.async_show_formasync_show_form(step_id="hardware_settings", data_schema=schema)
158 
159  async def async_step_reboot_menu(
160  self, user_input: dict[str, Any] | None = None
161  ) -> ConfigFlowResult:
162  """Confirm reboot host."""
163  return self.async_show_menuasync_show_menu(
164  step_id="reboot_menu",
165  menu_options=[
166  "reboot_now",
167  "reboot_later",
168  ],
169  )
170 
171  async def async_step_reboot_now(
172  self, user_input: dict[str, Any] | None = None
173  ) -> ConfigFlowResult:
174  """Reboot now."""
175  await async_reboot_host(self.hass)
176  return self.async_create_entryasync_create_entry(data={})
177 
178  async def async_step_reboot_later(
179  self, user_input: dict[str, Any] | None = None
180  ) -> ConfigFlowResult:
181  """Reboot later."""
182  return self.async_create_entryasync_create_entry(data={})
183 
184 
186  BaseHomeAssistantYellowOptionsFlow, MultiprotocolOptionsFlowHandler
187 ):
188  """Handle a multi-PAN options flow for Home Assistant Yellow."""
189 
190  async def async_step_main_menu(self, _: None = None) -> ConfigFlowResult:
191  """Show the main menu."""
192  return self.async_show_menuasync_show_menu(
193  step_id="main_menu",
194  menu_options=[
195  "hardware_settings",
196  "multipan_settings",
197  ],
198  )
199 
200  async def async_step_multipan_settings(
201  self, user_input: dict[str, Any] | None = None
202  ) -> ConfigFlowResult:
203  """Handle multipan settings."""
204  return await MultiprotocolOptionsFlowHandler.async_step_on_supervisor(
205  self, user_input
206  )
207 
208  async def _async_serial_port_settings(
209  self,
210  ) -> MultiprotocolSerialPortSettings:
211  """Return the radio serial port settings."""
212  return MultiprotocolSerialPortSettings(
213  device=RADIO_DEVICE,
214  baudrate="115200",
215  flow_control=True,
216  )
217 
218  async def _async_zha_physical_discovery(self) -> dict[str, Any]:
219  """Return ZHA discovery data when multiprotocol FW is not used.
220 
221  Passed to ZHA do determine if the ZHA config entry is connected to the radio
222  being migrated.
223  """
224  return {"hw": ZHA_HW_DISCOVERY_DATA}
225 
226  def _zha_name(self) -> str:
227  """Return the ZHA name."""
228  return "Yellow Multiprotocol"
229 
230  def _hardware_name(self) -> str:
231  """Return the name of the hardware."""
232  return BOARD_NAME
233 
234  async def async_step_flashing_complete(
235  self, user_input: dict[str, Any] | None = None
236  ) -> ConfigFlowResult:
237  """Finish flashing and update the config entry."""
238  self.hass.config_entries.async_update_entry(
239  entry=self.config_entryconfig_entryconfig_entry,
240  data={
241  **self.config_entryconfig_entryconfig_entry.data,
242  FIRMWARE: ApplicationType.EZSP.value,
243  },
244  )
245 
246  return await super().async_step_flashing_complete(user_input)
247 
248 
250  BaseHomeAssistantYellowOptionsFlow, BaseFirmwareOptionsFlow
251 ):
252  """Handle a firmware options flow for Home Assistant Yellow."""
253 
254  def __init__(self, *args: Any, **kwargs: Any) -> None:
255  """Instantiate options flow."""
256  super().__init__(*args, **kwargs)
257 
258  self._hardware_name_hardware_name = BOARD_NAME
259  self._device_device = RADIO_DEVICE
260 
261  # Regenerate the translation placeholders
262  self._get_translation_placeholders_get_translation_placeholders()
263 
264  async def async_step_main_menu(self, _: None = None) -> ConfigFlowResult:
265  """Show the main menu."""
266  return self.async_show_menuasync_show_menu(
267  step_id="main_menu",
268  menu_options=[
269  "hardware_settings",
270  "firmware_settings",
271  ],
272  )
273 
275  self, user_input: dict[str, Any] | None = None
276  ) -> ConfigFlowResult:
277  """Handle firmware configuration settings."""
278  return await super().async_step_pick_firmware()
279 
280  def _async_flow_finished(self) -> ConfigFlowResult:
281  """Create the config entry."""
282  assert self._probed_firmware_type_probed_firmware_type_probed_firmware_type is not None
283 
284  self.hass.config_entries.async_update_entry(
285  entry=self.config_entryconfig_entryconfig_entry,
286  data={
287  **self.config_entryconfig_entryconfig_entry.data,
288  FIRMWARE: self._probed_firmware_type_probed_firmware_type_probed_firmware_type.value,
289  },
290  )
291 
292  return self.async_create_entryasync_create_entry(title="", data={})
ConfigFlowResult async_step_pick_firmware(self, dict[str, Any]|None user_input=None)
ConfigFlowResult async_step_system(self, dict[str, Any]|None data=None)
Definition: config_flow.py:78
ConfigFlowResult async_step_firmware_settings(self, dict[str, Any]|None user_input=None)
Definition: config_flow.py:276
ConfigFlowResult async_create_entry(self, *str title, Mapping[str, Any] data, str|None description=None, Mapping[str, str]|None description_placeholders=None, Mapping[str, Any]|None options=None)
None config_entry(self, ConfigEntry value)
vol.Schema add_suggested_values_to_schema(self, vol.Schema data_schema, Mapping[str, Any]|None suggested_values)
_FlowResultT async_show_form(self, *str|None step_id=None, vol.Schema|None data_schema=None, dict[str, str]|None errors=None, Mapping[str, str]|None description_placeholders=None, bool|None last_step=None, str|None preview=None)
_FlowResultT async_show_menu(self, *str|None step_id=None, Container[str] menu_options, Mapping[str, str]|None description_placeholders=None)
_FlowResultT async_create_entry(self, *str|None title=None, Mapping[str, Any] data, str|None description=None, Mapping[str, str]|None description_placeholders=None)
_FlowResultT async_abort(self, *str reason, Mapping[str, str]|None description_placeholders=None)
dict async_set_yellow_settings(HomeAssistant hass, dict[str, bool] settings)
Definition: handler.py:125
dict async_reboot_host(HomeAssistant hass)
Definition: handler.py:137
dict[str, bool] async_get_yellow_settings(HomeAssistant hass)
Definition: handler.py:116