Home Assistant Unofficial Reference 2024.12.1
config_flow.py
Go to the documentation of this file.
1 """Config flow for WS66i 6-Zone Amplifier integration."""
2 
3 from __future__ import annotations
4 
5 import logging
6 from typing import Any
7 
8 from pyws66i import WS66i, get_ws66i
9 import voluptuous as vol
10 
11 from homeassistant.config_entries import (
12  ConfigEntry,
13  ConfigFlow,
14  ConfigFlowResult,
15  OptionsFlow,
16 )
17 from homeassistant.const import CONF_IP_ADDRESS
18 from homeassistant.core import HomeAssistant, callback
19 from homeassistant.exceptions import HomeAssistantError
20 
21 from .const import (
22  CONF_SOURCE_1,
23  CONF_SOURCE_2,
24  CONF_SOURCE_3,
25  CONF_SOURCE_4,
26  CONF_SOURCE_5,
27  CONF_SOURCE_6,
28  CONF_SOURCES,
29  DOMAIN,
30  INIT_OPTIONS_DEFAULT,
31 )
32 
33 _LOGGER = logging.getLogger(__name__)
34 
35 SOURCES = [
36  CONF_SOURCE_1,
37  CONF_SOURCE_2,
38  CONF_SOURCE_3,
39  CONF_SOURCE_4,
40  CONF_SOURCE_5,
41  CONF_SOURCE_6,
42 ]
43 
44 OPTIONS_SCHEMA = {vol.Optional(source): str for source in SOURCES}
45 
46 DATA_SCHEMA = vol.Schema({vol.Required(CONF_IP_ADDRESS): str})
47 
48 FIRST_ZONE = 11
49 
50 
51 @callback
52 def _sources_from_config(data: dict[str, str]) -> dict[str, str]:
53  sources_config = {
54  str(idx + 1): data.get(source) for idx, source in enumerate(SOURCES)
55  }
56 
57  return {
58  index: name.strip()
59  for index, name in sources_config.items()
60  if (name is not None and name.strip() != "")
61  }
62 
63 
64 def _verify_connection(ws66i: WS66i) -> bool:
65  """Verify a connection can be made to the WS66i."""
66  try:
67  ws66i.open()
68  except ConnectionError as err:
69  raise CannotConnect from err
70 
71  # Connection successful. Verify correct port was opened
72  # Test on FIRST_ZONE because this zone will always be valid
73  ret_val = ws66i.zone_status(FIRST_ZONE)
74 
75  ws66i.close()
76 
77  return bool(ret_val)
78 
79 
80 async def validate_input(
81  hass: HomeAssistant, input_data: dict[str, Any]
82 ) -> dict[str, Any]:
83  """Validate the user input.
84 
85  Data has the keys from DATA_SCHEMA with values provided by the user.
86  """
87  ws66i: WS66i = get_ws66i(input_data[CONF_IP_ADDRESS])
88 
89  is_valid: bool = await hass.async_add_executor_job(_verify_connection, ws66i)
90  if not is_valid:
91  raise CannotConnect("Not a valid WS66i connection")
92 
93  # Return info that you want to store in the config entry.
94  return {CONF_IP_ADDRESS: input_data[CONF_IP_ADDRESS]}
95 
96 
97 class WS66iConfigFlow(ConfigFlow, domain=DOMAIN):
98  """Handle a config flow for WS66i 6-Zone Amplifier."""
99 
100  VERSION = 1
101 
102  async def async_step_user(
103  self, user_input: dict[str, Any] | None = None
104  ) -> ConfigFlowResult:
105  """Handle the initial step."""
106  errors = {}
107  if user_input is not None:
108  try:
109  info = await validate_input(self.hass, user_input)
110  except CannotConnect:
111  errors["base"] = "cannot_connect"
112  except Exception:
113  _LOGGER.exception("Unexpected exception")
114  errors["base"] = "unknown"
115  else:
116  # Data is valid. Create a config entry.
117  return self.async_create_entryasync_create_entryasync_create_entry(
118  title="WS66i Amp",
119  data=info,
120  options={CONF_SOURCES: INIT_OPTIONS_DEFAULT},
121  )
122 
123  return self.async_show_formasync_show_formasync_show_form(
124  step_id="user", data_schema=DATA_SCHEMA, errors=errors
125  )
126 
127  @staticmethod
128  @callback
130  config_entry: ConfigEntry,
131  ) -> Ws66iOptionsFlowHandler:
132  """Define the config flow to handle options."""
133  return Ws66iOptionsFlowHandler()
134 
135 
136 @callback
138  index: int, source: str, previous_sources: dict[str, str]
139 ) -> vol.Required:
140  return vol.Required(
141  source, description={"suggested_value": previous_sources[str(index)]}
142  )
143 
144 
146  """Handle a WS66i options flow."""
147 
148  async def async_step_init(
149  self, user_input: dict[str, str] | None = None
150  ) -> ConfigFlowResult:
151  """Manage the options."""
152  if user_input is not None:
153  return self.async_create_entryasync_create_entry(
154  title="Source Names",
155  data={CONF_SOURCES: _sources_from_config(user_input)},
156  )
157 
158  # Fill form with previous source names
159  previous_sources = self.config_entryconfig_entryconfig_entry.options[CONF_SOURCES]
160  options = {
161  _key_for_source(idx + 1, source, previous_sources): str
162  for idx, source in enumerate(SOURCES)
163  }
164 
165  return self.async_show_formasync_show_form(
166  step_id="init",
167  data_schema=vol.Schema(options),
168  )
169 
170 
172  """Error to indicate we cannot connect."""
Ws66iOptionsFlowHandler async_get_options_flow(ConfigEntry config_entry)
Definition: config_flow.py:131
ConfigFlowResult async_step_user(self, dict[str, Any]|None user_input=None)
Definition: config_flow.py:104
ConfigFlowResult async_step_init(self, dict[str, str]|None user_input=None)
Definition: config_flow.py:150
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)
ConfigFlowResult 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)
None config_entry(self, ConfigEntry value)
_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_create_entry(self, *str|None title=None, Mapping[str, Any] data, str|None description=None, Mapping[str, str]|None description_placeholders=None)
vol.Required _key_for_source(int index, str source, dict[str, str] previous_sources)
Definition: config_flow.py:139
dict[str, Any] validate_input(HomeAssistant hass, dict[str, Any] input_data)
Definition: config_flow.py:82
dict[str, str] _sources_from_config(dict[str, str] data)
Definition: config_flow.py:52