Home Assistant Unofficial Reference 2024.12.1
config_flow.py
Go to the documentation of this file.
1 """Config flow for Twitch."""
2 
3 from __future__ import annotations
4 
5 from collections.abc import Mapping
6 import logging
7 from typing import Any, cast
8 
9 from twitchAPI.helper import first
10 from twitchAPI.twitch import Twitch
11 
12 from homeassistant.config_entries import SOURCE_REAUTH, ConfigFlowResult
13 from homeassistant.const import CONF_ACCESS_TOKEN, CONF_TOKEN
14 from homeassistant.helpers import config_entry_oauth2_flow
15 from homeassistant.helpers.config_entry_oauth2_flow import LocalOAuth2Implementation
16 
17 from .const import CONF_CHANNELS, DOMAIN, LOGGER, OAUTH_SCOPES
18 
19 
21  config_entry_oauth2_flow.AbstractOAuth2FlowHandler, domain=DOMAIN
22 ):
23  """Config flow to handle Twitch OAuth2 authentication."""
24 
25  DOMAIN = DOMAIN
26 
27  def __init__(self) -> None:
28  """Initialize flow."""
29  super().__init__()
30  self.data: dict[str, Any] = {}
31 
32  @property
33  def logger(self) -> logging.Logger:
34  """Return logger."""
35  return LOGGER
36 
37  @property
38  def extra_authorize_data(self) -> dict[str, Any]:
39  """Extra data that needs to be appended to the authorize url."""
40  return {"scope": " ".join([scope.value for scope in OAUTH_SCOPES])}
41 
43  self,
44  data: dict[str, Any],
45  ) -> ConfigFlowResult:
46  """Handle the initial step."""
47  implementation = cast(
48  LocalOAuth2Implementation,
49  self.flow_impl,
50  )
51 
52  client = Twitch(
53  app_id=implementation.client_id,
54  authenticate_app=False,
55  )
56  client.auto_refresh_auth = False
57  await client.set_user_authentication(
58  data[CONF_TOKEN][CONF_ACCESS_TOKEN], scope=OAUTH_SCOPES
59  )
60  user = await first(client.get_users())
61  assert user
62 
63  user_id = user.id
64 
65  await self.async_set_unique_id(user_id)
66  if self.source != SOURCE_REAUTH:
67  self._abort_if_unique_id_configured()
68 
69  channels = [
70  channel.broadcaster_login
71  async for channel in await client.get_followed_channels(user_id)
72  ]
73 
74  return self.async_create_entry(
75  title=user.display_name, data=data, options={CONF_CHANNELS: channels}
76  )
77 
78  reauth_entry = self._get_reauth_entry()
79  self._abort_if_unique_id_mismatch(
80  reason="wrong_account",
81  description_placeholders={
82  "title": reauth_entry.title,
83  "username": str(reauth_entry.unique_id),
84  },
85  )
86 
87  new_channels = reauth_entry.options[CONF_CHANNELS]
88  # Since we could not get all channels at import, we do it at the reauth
89  # immediately after.
90  if "imported" in reauth_entry.data:
91  channels = [
92  channel.broadcaster_login
93  async for channel in await client.get_followed_channels(user_id)
94  ]
95  options = list(set(channels) - set(new_channels))
96  new_channels = [*new_channels, *options]
97 
98  return self.async_update_reload_and_abort(
99  reauth_entry,
100  data=data,
101  options={CONF_CHANNELS: new_channels},
102  )
103 
104  async def async_step_reauth(
105  self, entry_data: Mapping[str, Any]
106  ) -> ConfigFlowResult:
107  """Perform reauth upon an API authentication error."""
108  return await self.async_step_reauth_confirmasync_step_reauth_confirm()
109 
111  self, user_input: dict[str, Any] | None = None
112  ) -> ConfigFlowResult:
113  """Confirm reauth dialog."""
114  if user_input is None:
115  return self.async_show_form(step_id="reauth_confirm")
116  return await self.async_step_user()
ConfigFlowResult async_step_reauth(self, Mapping[str, Any] entry_data)
Definition: config_flow.py:106
ConfigFlowResult async_step_reauth_confirm(self, dict[str, Any]|None user_input=None)
Definition: config_flow.py:112
ConfigFlowResult async_oauth_create_entry(self, dict[str, Any] data)
Definition: config_flow.py:45