Home Assistant Unofficial Reference 2024.12.1
config_flow.py
Go to the documentation of this file.
1 """Config flow for Sonarr."""
2 
3 from __future__ import annotations
4 
5 from collections.abc import Mapping
6 import logging
7 from typing import Any
8 
9 from aiopyarr import ArrAuthenticationException, ArrException
10 from aiopyarr.models.host_configuration import PyArrHostConfiguration
11 from aiopyarr.sonarr_client import SonarrClient
12 import voluptuous as vol
13 import yarl
14 
15 from homeassistant.config_entries import (
16  SOURCE_REAUTH,
17  ConfigEntry,
18  ConfigFlow,
19  ConfigFlowResult,
20  OptionsFlow,
21 )
22 from homeassistant.const import CONF_API_KEY, CONF_URL, CONF_VERIFY_SSL
23 from homeassistant.core import HomeAssistant, callback
24 from homeassistant.helpers.aiohttp_client import async_get_clientsession
25 
26 from .const import (
27  CONF_UPCOMING_DAYS,
28  CONF_WANTED_MAX_ITEMS,
29  DEFAULT_UPCOMING_DAYS,
30  DEFAULT_VERIFY_SSL,
31  DEFAULT_WANTED_MAX_ITEMS,
32  DOMAIN,
33 )
34 
35 _LOGGER = logging.getLogger(__name__)
36 
37 
38 async def _validate_input(hass: HomeAssistant, data: dict[str, Any]) -> None:
39  """Validate the user input allows us to connect.
40 
41  Data has the keys from DATA_SCHEMA with values provided by the user.
42  """
43  host_configuration = PyArrHostConfiguration(
44  api_token=data[CONF_API_KEY],
45  url=data[CONF_URL],
46  verify_ssl=data[CONF_VERIFY_SSL],
47  )
48 
49  sonarr = SonarrClient(
50  host_configuration=host_configuration,
51  session=async_get_clientsession(hass),
52  )
53 
54  await sonarr.async_get_system_status()
55 
56 
57 class SonarrConfigFlow(ConfigFlow, domain=DOMAIN):
58  """Handle a config flow for Sonarr."""
59 
60  VERSION = 2
61 
62  @staticmethod
63  @callback
64  def async_get_options_flow(config_entry: ConfigEntry) -> SonarrOptionsFlowHandler:
65  """Get the options flow for this handler."""
67 
68  async def async_step_reauth(
69  self, entry_data: Mapping[str, Any]
70  ) -> ConfigFlowResult:
71  """Handle configuration by re-auth."""
72  return await self.async_step_reauth_confirmasync_step_reauth_confirm()
73 
75  self, user_input: dict[str, Any] | None = None
76  ) -> ConfigFlowResult:
77  """Confirm reauth dialog."""
78  if user_input is None:
79  return self.async_show_formasync_show_formasync_show_form(
80  step_id="reauth_confirm",
81  description_placeholders={
82  "url": self._get_reauth_entry_get_reauth_entry().data[CONF_URL]
83  },
84  errors={},
85  )
86 
87  return await self.async_step_userasync_step_userasync_step_user()
88 
89  async def async_step_user(
90  self, user_input: dict[str, Any] | None = None
91  ) -> ConfigFlowResult:
92  """Handle a flow initiated by the user."""
93  errors = {}
94 
95  if user_input is not None:
96  # aiopyarr defaults to the service port if one isn't given
97  # this is counter to standard practice where http = 80
98  # and https = 443.
99  if CONF_URL in user_input:
100  url = yarl.URL(user_input[CONF_URL])
101  user_input[CONF_URL] = f"{url.scheme}://{url.host}:{url.port}{url.path}"
102 
103  if self.sourcesourcesourcesource == SOURCE_REAUTH:
104  user_input = {**self._get_reauth_entry_get_reauth_entry().data, **user_input}
105 
106  if CONF_VERIFY_SSL not in user_input:
107  user_input[CONF_VERIFY_SSL] = DEFAULT_VERIFY_SSL
108 
109  try:
110  await _validate_input(self.hass, user_input)
111  except ArrAuthenticationException:
112  errors = {"base": "invalid_auth"}
113  except ArrException:
114  errors = {"base": "cannot_connect"}
115  except Exception:
116  _LOGGER.exception("Unexpected exception")
117  return self.async_abortasync_abortasync_abort(reason="unknown")
118  else:
119  if self.sourcesourcesourcesource == SOURCE_REAUTH:
120  return self.async_update_reload_and_abortasync_update_reload_and_abort(
121  self._get_reauth_entry_get_reauth_entry(), data=user_input
122  )
123 
124  parsed = yarl.URL(user_input[CONF_URL])
125 
126  return self.async_create_entryasync_create_entryasync_create_entry(
127  title=parsed.host or "Sonarr", data=user_input
128  )
129 
130  data_schema = self._get_user_data_schema_get_user_data_schema()
131  return self.async_show_formasync_show_formasync_show_form(
132  step_id="user",
133  data_schema=vol.Schema(data_schema),
134  errors=errors,
135  )
136 
137  def _get_user_data_schema(self) -> dict[vol.Marker, type]:
138  """Get the data schema to display user form."""
139  if self.sourcesourcesourcesource == SOURCE_REAUTH:
140  return {vol.Required(CONF_API_KEY): str}
141 
142  data_schema: dict[vol.Marker, type] = {
143  vol.Required(CONF_URL): str,
144  vol.Required(CONF_API_KEY): str,
145  }
146 
147  if self.show_advanced_optionsshow_advanced_options:
148  data_schema[vol.Optional(CONF_VERIFY_SSL, default=DEFAULT_VERIFY_SSL)] = (
149  bool
150  )
151 
152  return data_schema
153 
154 
156  """Handle Sonarr client options."""
157 
158  async def async_step_init(
159  self, user_input: dict[str, int] | None = None
160  ) -> ConfigFlowResult:
161  """Manage Sonarr options."""
162  if user_input is not None:
163  return self.async_create_entryasync_create_entry(title="", data=user_input)
164 
165  options = {
166  vol.Optional(
167  CONF_UPCOMING_DAYS,
168  default=self.config_entryconfig_entryconfig_entry.options.get(
169  CONF_UPCOMING_DAYS, DEFAULT_UPCOMING_DAYS
170  ),
171  ): int,
172  vol.Optional(
173  CONF_WANTED_MAX_ITEMS,
174  default=self.config_entryconfig_entryconfig_entry.options.get(
175  CONF_WANTED_MAX_ITEMS, DEFAULT_WANTED_MAX_ITEMS
176  ),
177  ): int,
178  }
179 
180  return self.async_show_formasync_show_form(step_id="init", data_schema=vol.Schema(options))
ConfigFlowResult async_step_user(self, dict[str, Any]|None user_input=None)
Definition: config_flow.py:91
ConfigFlowResult async_step_reauth_confirm(self, dict[str, Any]|None user_input=None)
Definition: config_flow.py:76
ConfigFlowResult async_step_reauth(self, Mapping[str, Any] entry_data)
Definition: config_flow.py:70
SonarrOptionsFlowHandler async_get_options_flow(ConfigEntry config_entry)
Definition: config_flow.py:64
ConfigFlowResult async_step_init(self, dict[str, int]|None user_input=None)
Definition: config_flow.py:160
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)
ConfigFlowResult async_step_user(self, dict[str, Any]|None user_input=None)
ConfigFlowResult async_abort(self, *str reason, Mapping[str, str]|None description_placeholders=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)
bool show_advanced_options(self)
_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)
str|None source(self)
_FlowResultT async_abort(self, *str reason, Mapping[str, str]|None description_placeholders=None)
None _validate_input(HomeAssistant hass, dict[str, Any] data)
Definition: config_flow.py:38
aiohttp.ClientSession async_get_clientsession(HomeAssistant hass, bool verify_ssl=True, socket.AddressFamily family=socket.AF_UNSPEC, ssl_util.SSLCipherList ssl_cipher=ssl_util.SSLCipherList.PYTHON_DEFAULT)