Home Assistant Unofficial Reference 2024.12.1
config_flow.py
Go to the documentation of this file.
1 """Config flow for Tellduslive."""
2 
3 import asyncio
4 import logging
5 import os
6 from typing import Any
7 
8 from tellduslive import Session, supports_local_api
9 import voluptuous as vol
10 
11 from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
12 from homeassistant.const import CONF_HOST
13 from homeassistant.util.json import load_json_object
14 
15 from .const import (
16  APPLICATION_NAME,
17  CLOUD_NAME,
18  DOMAIN,
19  KEY_SCAN_INTERVAL,
20  KEY_SESSION,
21  NOT_SO_PRIVATE_KEY,
22  PUBLIC_KEY,
23  SCAN_INTERVAL,
24  TELLDUS_CONFIG_FILE,
25 )
26 
27 KEY_TOKEN = "token"
28 KEY_TOKEN_SECRET = "token_secret"
29 
30 _LOGGER = logging.getLogger(__name__)
31 
32 
33 class FlowHandler(ConfigFlow, domain=DOMAIN):
34  """Handle a config flow."""
35 
36  VERSION = 1
37 
38  _session: Session
39 
40  def __init__(self) -> None:
41  """Init config flow."""
42  self._hosts_hosts = [CLOUD_NAME]
43  self._host_host = None
44  self._scan_interval_scan_interval = SCAN_INTERVAL
45 
46  def _get_auth_url(self) -> str | None:
47  self._session_session = Session(
48  public_key=PUBLIC_KEY,
49  private_key=NOT_SO_PRIVATE_KEY,
50  host=self._host_host,
51  application=APPLICATION_NAME,
52  )
53  return self._session_session.authorize_url
54 
55  async def async_step_user(
56  self, user_input: dict[str, Any] | None = None
57  ) -> ConfigFlowResult:
58  """Let user select host or cloud."""
59  if self._async_current_entries_async_current_entries():
60  return self.async_abortasync_abortasync_abort(reason="already_setup")
61 
62  if user_input is not None or len(self._hosts_hosts) == 1:
63  if user_input is not None and user_input[CONF_HOST] != CLOUD_NAME:
64  self._host_host = user_input[CONF_HOST]
65  return await self.async_step_authasync_step_auth()
66 
67  return self.async_show_formasync_show_formasync_show_form(
68  step_id="user",
69  data_schema=vol.Schema(
70  {vol.Required(CONF_HOST): vol.In(list(self._hosts_hosts))}
71  ),
72  )
73 
74  async def async_step_auth(
75  self, user_input: dict[str, Any] | None = None
76  ) -> ConfigFlowResult:
77  """Handle the submitted configuration."""
78  errors = {}
79  if user_input is not None:
80  if await self.hass.async_add_executor_job(self._session_session.authorize):
81  host = self._host_host or CLOUD_NAME
82  if self._host_host:
83  session = {CONF_HOST: host, KEY_TOKEN: self._session_session.access_token}
84  else:
85  session = {
86  KEY_TOKEN: self._session_session.access_token,
87  KEY_TOKEN_SECRET: self._session_session.access_token_secret,
88  }
89  return self.async_create_entryasync_create_entryasync_create_entry(
90  title=host,
91  data={
92  CONF_HOST: host,
93  KEY_SCAN_INTERVAL: self._scan_interval_scan_interval.total_seconds(),
94  KEY_SESSION: session,
95  },
96  )
97  errors["base"] = "invalid_auth"
98 
99  try:
100  async with asyncio.timeout(10):
101  auth_url = await self.hass.async_add_executor_job(self._get_auth_url_get_auth_url)
102  if not auth_url:
103  return self.async_abortasync_abortasync_abort(reason="unknown_authorize_url_generation")
104  except TimeoutError:
105  return self.async_abortasync_abortasync_abort(reason="authorize_url_timeout")
106  except Exception:
107  _LOGGER.exception("Unexpected error generating auth url")
108  return self.async_abortasync_abortasync_abort(reason="unknown_authorize_url_generation")
109 
110  _LOGGER.debug("Got authorization URL %s", auth_url)
111  return self.async_show_formasync_show_formasync_show_form(
112  step_id="auth",
113  errors=errors,
114  description_placeholders={
115  "app_name": APPLICATION_NAME,
116  "auth_url": auth_url,
117  },
118  )
119 
121  self,
122  discovery_info: list[str], # type: ignore[override]
123  ) -> ConfigFlowResult:
124  """Run when a Tellstick is discovered."""
125  await self._async_handle_discovery_without_unique_id_async_handle_discovery_without_unique_id()
126 
127  _LOGGER.debug("Discovered tellstick device: %s", discovery_info)
128  if supports_local_api(discovery_info[1]):
129  _LOGGER.debug("%s support local API", discovery_info[1])
130  self._hosts_hosts.append(discovery_info[0])
131 
132  return await self.async_step_userasync_step_userasync_step_user()
133 
134  async def async_step_import(self, import_data: dict[str, Any]) -> ConfigFlowResult:
135  """Import a config entry."""
136  if self._async_current_entries_async_current_entries():
137  return self.async_abortasync_abortasync_abort(reason="already_setup")
138 
139  self._scan_interval_scan_interval = import_data[KEY_SCAN_INTERVAL]
140  if import_data[CONF_HOST] != DOMAIN:
141  self._hosts_hosts.append(import_data[CONF_HOST])
142 
143  if not await self.hass.async_add_executor_job(
144  os.path.isfile, self.hass.config.path(TELLDUS_CONFIG_FILE)
145  ):
146  return await self.async_step_userasync_step_userasync_step_user()
147 
148  conf = await self.hass.async_add_executor_job(
149  load_json_object, self.hass.config.path(TELLDUS_CONFIG_FILE)
150  )
151  host = next(iter(conf))
152 
153  if import_data[CONF_HOST] != host:
154  return await self.async_step_userasync_step_userasync_step_user()
155 
156  host = CLOUD_NAME if host == "tellduslive" else host
157  return self.async_create_entryasync_create_entryasync_create_entry(
158  title=host,
159  data={
160  CONF_HOST: host,
161  KEY_SCAN_INTERVAL: self._scan_interval_scan_interval.total_seconds(),
162  KEY_SESSION: next(iter(conf.values())),
163  },
164  )
ConfigFlowResult async_step_auth(self, dict[str, Any]|None user_input=None)
Definition: config_flow.py:76
ConfigFlowResult async_step_user(self, dict[str, Any]|None user_input=None)
Definition: config_flow.py:57
ConfigFlowResult async_step_discovery(self, list[str] discovery_info)
Definition: config_flow.py:123
ConfigFlowResult async_step_import(self, dict[str, Any] import_data)
Definition: config_flow.py:134
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)
list[ConfigEntry] _async_current_entries(self, bool|None include_ignore=None)
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)
_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)
_FlowResultT async_abort(self, *str reason, Mapping[str, str]|None description_placeholders=None)