Home Assistant Unofficial Reference 2024.12.1
config_flow.py
Go to the documentation of this file.
1 """Config flow for solarlog integration."""
2 
3 from collections.abc import Mapping
4 import logging
5 from typing import Any
6 from urllib.parse import ParseResult, urlparse
7 
8 from solarlog_cli.solarlog_connector import SolarLogConnector
9 from solarlog_cli.solarlog_exceptions import (
10  SolarLogAuthenticationError,
11  SolarLogConnectionError,
12  SolarLogError,
13 )
14 import voluptuous as vol
15 
16 from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
17 from homeassistant.const import CONF_HOST, CONF_NAME, CONF_PASSWORD
18 from homeassistant.util import slugify
19 
20 from .const import CONF_HAS_PWD, DEFAULT_HOST, DEFAULT_NAME, DOMAIN
21 
22 _LOGGER = logging.getLogger(__name__)
23 
24 
25 class SolarLogConfigFlow(ConfigFlow, domain=DOMAIN):
26  """Handle a config flow for solarlog."""
27 
28  VERSION = 1
29  MINOR_VERSION = 3
30 
31  def __init__(self) -> None:
32  """Initialize the config flow."""
33  self._errors_errors: dict = {}
34  self._user_input_user_input: dict = {}
35 
36  def _parse_url(self, host: str) -> str:
37  """Return parsed host url."""
38  url = urlparse(host, "http")
39  netloc = url.netloc or url.path
40  path = url.path if url.netloc else ""
41  url = ParseResult("http", netloc, path, *url[3:])
42  return url.geturl()
43 
44  async def _test_connection(self, host: str) -> bool:
45  """Check if we can connect to the Solar-Log device."""
46  solarlog = SolarLogConnector(host)
47  try:
48  await solarlog.test_connection()
49  except SolarLogConnectionError:
50  self._errors_errors = {CONF_HOST: "cannot_connect"}
51  return False
52  except SolarLogError:
53  self._errors_errors = {CONF_HOST: "unknown"}
54  return False
55  finally:
56  await solarlog.client.close()
57 
58  return True
59 
60  async def _test_extended_data(self, host: str, pwd: str = "") -> bool:
61  """Check if we get extended data from Solar-Log device."""
62  response: bool = False
63  solarlog = SolarLogConnector(host, password=pwd)
64  try:
65  response = await solarlog.test_extended_data_available()
66  except SolarLogAuthenticationError:
67  self._errors_errors = {CONF_HOST: "password_error"}
68  response = False
69  except SolarLogError:
70  self._errors_errors = {CONF_HOST: "unknown"}
71  response = False
72  finally:
73  await solarlog.client.close()
74 
75  return response
76 
77  async def async_step_user(
78  self, user_input: dict[str, Any] | None = None
79  ) -> ConfigFlowResult:
80  """Step when user initializes a integration."""
81  self._errors_errors = {}
82  if user_input is not None:
83  user_input[CONF_HOST] = self._parse_url_parse_url(user_input[CONF_HOST])
84 
85  self._async_abort_entries_match_async_abort_entries_match({CONF_HOST: user_input[CONF_HOST]})
86 
87  user_input[CONF_NAME] = slugify(user_input[CONF_NAME])
88 
89  if await self._test_connection_test_connection(user_input[CONF_HOST]):
90  if user_input[CONF_HAS_PWD]:
91  self._user_input_user_input = user_input
92  return await self.async_step_passwordasync_step_password()
93 
94  return self.async_create_entryasync_create_entryasync_create_entry(
95  title=user_input[CONF_NAME], data=user_input
96  )
97  else:
98  user_input = {CONF_NAME: DEFAULT_NAME, CONF_HOST: DEFAULT_HOST}
99 
100  return self.async_show_formasync_show_formasync_show_form(
101  step_id="user",
102  data_schema=vol.Schema(
103  {
104  vol.Required(CONF_NAME, default=user_input[CONF_NAME]): str,
105  vol.Required(CONF_HOST, default=user_input[CONF_HOST]): str,
106  vol.Required(CONF_HAS_PWD, default=False): bool,
107  }
108  ),
109  errors=self._errors_errors,
110  )
111 
113  self, user_input: dict[str, Any] | None = None
114  ) -> ConfigFlowResult:
115  """Step when user sets password ."""
116  self._errors_errors = {}
117  if user_input is not None:
118  if await self._test_extended_data_test_extended_data(
119  self._user_input_user_input[CONF_HOST], user_input[CONF_PASSWORD]
120  ):
121  self._user_input_user_input |= user_input
122  return self.async_create_entryasync_create_entryasync_create_entry(
123  title=self._user_input_user_input[CONF_NAME], data=self._user_input_user_input
124  )
125  else:
126  user_input = {CONF_PASSWORD: ""}
127 
128  return self.async_show_formasync_show_formasync_show_form(
129  step_id="password",
130  data_schema=vol.Schema(
131  {
132  vol.Required(CONF_PASSWORD): str,
133  }
134  ),
135  errors=self._errors_errors,
136  )
137 
139  self, user_input: dict[str, Any] | None = None
140  ) -> ConfigFlowResult:
141  """Handle a reconfiguration flow initialized by the user."""
142  reconfigure_entry = self._get_reconfigure_entry_get_reconfigure_entry()
143  if user_input is not None:
144  if not user_input[CONF_HAS_PWD] or user_input.get(CONF_PASSWORD, "") == "":
145  user_input[CONF_PASSWORD] = ""
146  user_input[CONF_HAS_PWD] = False
147  return self.async_update_reload_and_abortasync_update_reload_and_abort(
148  reconfigure_entry, data_updates=user_input
149  )
150 
151  if await self._test_extended_data_test_extended_data(
152  reconfigure_entry.data[CONF_HOST], user_input.get(CONF_PASSWORD, "")
153  ):
154  # if password has been provided, only save if extended data is available
155  return self.async_update_reload_and_abortasync_update_reload_and_abort(
156  reconfigure_entry,
157  data_updates=user_input,
158  )
159 
160  return self.async_show_formasync_show_formasync_show_form(
161  step_id="reconfigure",
162  data_schema=vol.Schema(
163  {
164  vol.Optional(
165  CONF_HAS_PWD, default=reconfigure_entry.data[CONF_HAS_PWD]
166  ): bool,
167  vol.Optional(CONF_PASSWORD): str,
168  }
169  ),
170  )
171 
172  async def async_step_reauth(
173  self, entry_data: Mapping[str, Any]
174  ) -> ConfigFlowResult:
175  """Handle flow upon an API authentication error."""
176  return await self.async_step_reauth_confirmasync_step_reauth_confirm()
177 
179  self, user_input: dict[str, Any] | None = None
180  ) -> ConfigFlowResult:
181  """Handle reauthorization flow."""
182  reauth_entry = self._get_reauth_entry_get_reauth_entry()
183  if user_input and await self._test_extended_data_test_extended_data(
184  reauth_entry.data[CONF_HOST], user_input.get(CONF_PASSWORD, "")
185  ):
186  return self.async_update_reload_and_abortasync_update_reload_and_abort(
187  reauth_entry, data_updates=user_input
188  )
189 
190  data_schema = vol.Schema(
191  {
192  vol.Optional(
193  CONF_HAS_PWD, default=reauth_entry.data[CONF_HAS_PWD]
194  ): bool,
195  vol.Optional(CONF_PASSWORD): str,
196  }
197  )
198  return self.async_show_formasync_show_formasync_show_form(
199  step_id="reauth_confirm",
200  data_schema=data_schema,
201  errors=self._errors_errors,
202  )
ConfigFlowResult async_step_password(self, dict[str, Any]|None user_input=None)
Definition: config_flow.py:114
ConfigFlowResult async_step_reauth(self, Mapping[str, Any] entry_data)
Definition: config_flow.py:174
ConfigFlowResult async_step_reconfigure(self, dict[str, Any]|None user_input=None)
Definition: config_flow.py:140
ConfigFlowResult async_step_reauth_confirm(self, dict[str, Any]|None user_input=None)
Definition: config_flow.py:180
ConfigFlowResult async_step_user(self, dict[str, Any]|None user_input=None)
Definition: config_flow.py:79
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_update_reload_and_abort(self, ConfigEntry entry, *str|None|UndefinedType unique_id=UNDEFINED, str|UndefinedType title=UNDEFINED, Mapping[str, Any]|UndefinedType data=UNDEFINED, Mapping[str, Any]|UndefinedType data_updates=UNDEFINED, Mapping[str, Any]|UndefinedType options=UNDEFINED, str|UndefinedType reason=UNDEFINED, bool reload_even_if_entry_is_unchanged=True)
None _async_abort_entries_match(self, dict[str, Any]|None match_dict=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)
_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)