Home Assistant Unofficial Reference 2024.12.1
config_flow.py
Go to the documentation of this file.
1 """Config flow for Steam integration."""
2 
3 from __future__ import annotations
4 
5 from collections.abc import Iterator, Mapping
6 from typing import Any
7 
8 import steam
9 import voluptuous as vol
10 
11 from homeassistant.config_entries import (
12  SOURCE_REAUTH,
13  ConfigFlow,
14  ConfigFlowResult,
15  OptionsFlow,
16 )
17 from homeassistant.const import CONF_API_KEY, Platform
18 from homeassistant.core import callback
19 from homeassistant.helpers import config_validation as cv, entity_registry as er
20 
21 from . import SteamConfigEntry
22 from .const import CONF_ACCOUNT, CONF_ACCOUNTS, DOMAIN, LOGGER, PLACEHOLDERS
23 
24 # To avoid too long request URIs, the amount of ids to request is limited
25 MAX_IDS_TO_REQUEST = 275
26 
27 
28 def validate_input(user_input: dict[str, str]) -> dict[str, str | int]:
29  """Handle common flow input validation."""
30  steam.api.key.set(user_input[CONF_API_KEY])
31  interface = steam.api.interface("ISteamUser")
32  names = interface.GetPlayerSummaries(steamids=user_input[CONF_ACCOUNT])
33  return names["response"]["players"]["player"][0]
34 
35 
36 class SteamFlowHandler(ConfigFlow, domain=DOMAIN):
37  """Handle a config flow for Steam."""
38 
39  @staticmethod
40  @callback
42  config_entry: SteamConfigEntry,
43  ) -> SteamOptionsFlowHandler:
44  """Get the options flow for this handler."""
45  return SteamOptionsFlowHandler(config_entry)
46 
47  async def async_step_user(
48  self, user_input: dict[str, Any] | None = None
49  ) -> ConfigFlowResult:
50  """Handle a flow initiated by the user."""
51  errors = {}
52  if user_input is None and self.sourcesourcesourcesource == SOURCE_REAUTH:
53  user_input = {CONF_ACCOUNT: self._get_reauth_entry_get_reauth_entry().data[CONF_ACCOUNT]}
54  elif user_input is not None:
55  try:
56  res = await self.hass.async_add_executor_job(validate_input, user_input)
57  if res is not None:
58  name = str(res["personaname"])
59  else:
60  errors["base"] = "invalid_account"
61  except (steam.api.HTTPError, steam.api.HTTPTimeoutError) as ex:
62  errors["base"] = "cannot_connect"
63  if "403" in str(ex):
64  errors["base"] = "invalid_auth"
65  except Exception as ex: # noqa: BLE001
66  LOGGER.exception("Unknown exception: %s", ex)
67  errors["base"] = "unknown"
68  if not errors:
69  entry = await self.async_set_unique_idasync_set_unique_id(user_input[CONF_ACCOUNT])
70  if entry and self.sourcesourcesourcesource == SOURCE_REAUTH:
71  self.hass.config_entries.async_update_entry(entry, data=user_input)
72  await self.hass.config_entries.async_reload(entry.entry_id)
73  return self.async_abortasync_abortasync_abort(reason="reauth_successful")
74  self._abort_if_unique_id_configured_abort_if_unique_id_configured()
75  return self.async_create_entryasync_create_entryasync_create_entry(
76  title=name,
77  data=user_input,
78  options={CONF_ACCOUNTS: {user_input[CONF_ACCOUNT]: name}},
79  )
80  user_input = user_input or {}
81  return self.async_show_formasync_show_formasync_show_form(
82  step_id="user",
83  data_schema=vol.Schema(
84  {
85  vol.Required(
86  CONF_API_KEY, default=user_input.get(CONF_API_KEY) or ""
87  ): str,
88  vol.Required(
89  CONF_ACCOUNT, default=user_input.get(CONF_ACCOUNT) or ""
90  ): str,
91  }
92  ),
93  errors=errors,
94  description_placeholders=PLACEHOLDERS,
95  )
96 
97  async def async_step_reauth(
98  self, entry_data: Mapping[str, Any]
99  ) -> ConfigFlowResult:
100  """Handle a reauthorization flow request."""
101  return await self.async_step_reauth_confirmasync_step_reauth_confirm()
102 
104  self, user_input: dict[str, str] | None = None
105  ) -> ConfigFlowResult:
106  """Confirm reauth dialog."""
107  if user_input is not None:
108  return await self.async_step_userasync_step_userasync_step_user()
109 
110  self._set_confirm_only_set_confirm_only()
111  return self.async_show_formasync_show_formasync_show_form(
112  step_id="reauth_confirm", description_placeholders=PLACEHOLDERS
113  )
114 
115 
116 def _batch_ids(ids: list[str]) -> Iterator[list[str]]:
117  for i in range(0, len(ids), MAX_IDS_TO_REQUEST):
118  yield ids[i : i + MAX_IDS_TO_REQUEST]
119 
120 
122  """Handle Steam client options."""
123 
124  def __init__(self, entry: SteamConfigEntry) -> None:
125  """Initialize options flow."""
126  self.optionsoptions = dict(entry.options)
127 
128  async def async_step_init(
129  self, user_input: dict[str, dict[str, str]] | None = None
130  ) -> ConfigFlowResult:
131  """Manage Steam options."""
132  if user_input is not None:
133  await self.hass.config_entries.async_unload(self.config_entryconfig_entryconfig_entry.entry_id)
134  for _id in self.optionsoptions[CONF_ACCOUNTS]:
135  if _id not in user_input[CONF_ACCOUNTS] and (
136  entity_id := er.async_get(self.hass).async_get_entity_id(
137  Platform.SENSOR, DOMAIN, f"sensor.steam_{_id}"
138  )
139  ):
140  er.async_get(self.hass).async_remove(entity_id)
141  channel_data = {
142  CONF_ACCOUNTS: {
143  _id: name
144  for _id, name in self.optionsoptions[CONF_ACCOUNTS].items()
145  if _id in user_input[CONF_ACCOUNTS]
146  }
147  }
148  await self.hass.config_entries.async_reload(self.config_entryconfig_entryconfig_entry.entry_id)
149  return self.async_create_entryasync_create_entry(title="", data=channel_data)
150  error = None
151  try:
152  users = {
153  name["steamid"]: name["personaname"]
154  for name in await self.hass.async_add_executor_job(self.get_accountsget_accounts)
155  }
156  if not users:
157  error = {"base": "unauthorized"}
158 
159  except steam.api.HTTPTimeoutError:
160  users = self.optionsoptions[CONF_ACCOUNTS]
161 
162  options = {
163  vol.Required(
164  CONF_ACCOUNTS,
165  default=set(self.optionsoptions[CONF_ACCOUNTS]),
166  ): cv.multi_select(users | self.optionsoptions[CONF_ACCOUNTS]),
167  }
168  self.optionsoptions[CONF_ACCOUNTS] = users | self.optionsoptions[CONF_ACCOUNTS]
169 
170  return self.async_show_formasync_show_form(
171  step_id="init", data_schema=vol.Schema(options), errors=error
172  )
173 
174  def get_accounts(self) -> list[dict[str, str | int]]:
175  """Get accounts."""
176  interface = steam.api.interface("ISteamUser")
177  try:
178  friends = interface.GetFriendList(
179  steamid=self.config_entryconfig_entryconfig_entry.data[CONF_ACCOUNT]
180  )
181  _users_str = [user["steamid"] for user in friends["friendslist"]["friends"]]
182  except steam.api.HTTPError:
183  return []
184  names = []
185  for id_batch in _batch_ids(_users_str):
186  names.extend(
187  interface.GetPlayerSummaries(steamids=id_batch)["response"]["players"][
188  "player"
189  ]
190  )
191  return names
ConfigFlowResult async_step_reauth_confirm(self, dict[str, str]|None user_input=None)
Definition: config_flow.py:105
ConfigFlowResult async_step_reauth(self, Mapping[str, Any] entry_data)
Definition: config_flow.py:99
ConfigFlowResult async_step_user(self, dict[str, Any]|None user_input=None)
Definition: config_flow.py:49
SteamOptionsFlowHandler async_get_options_flow(SteamConfigEntry config_entry)
Definition: config_flow.py:43
ConfigFlowResult async_step_init(self, dict[str, dict[str, str]]|None user_input=None)
Definition: config_flow.py:130
None _abort_if_unique_id_configured(self, dict[str, Any]|None updates=None, bool reload_on_update=True, *str error="already_configured")
ConfigEntry|None async_set_unique_id(self, str|None unique_id=None, *bool raise_on_progress=True)
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_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)
None async_remove(self)
str
_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)
dict[str, str|int] validate_input(dict[str, str] user_input)
Definition: config_flow.py:28
Iterator[list[str]] _batch_ids(list[str] ids)
Definition: config_flow.py:116