Home Assistant Unofficial Reference 2024.12.1
config_flow.py
Go to the documentation of this file.
1 """Config flow for LastFm."""
2 
3 from __future__ import annotations
4 
5 from typing import Any
6 
7 from pylast import LastFMNetwork, PyLastError, User, WSError
8 import voluptuous as vol
9 
10 from homeassistant.config_entries import (
11  ConfigEntry,
12  ConfigFlow,
13  ConfigFlowResult,
14  OptionsFlow,
15 )
16 from homeassistant.const import CONF_API_KEY
17 from homeassistant.core import callback
19  SelectOptionDict,
20  SelectSelector,
21  SelectSelectorConfig,
22 )
23 
24 from .const import CONF_MAIN_USER, CONF_USERS, DOMAIN
25 
26 PLACEHOLDERS = {"api_account_url": "https://www.last.fm/api/account/create"}
27 
28 CONFIG_SCHEMA: vol.Schema = vol.Schema(
29  {
30  vol.Required(CONF_API_KEY): str,
31  vol.Required(CONF_MAIN_USER): str,
32  }
33 )
34 
35 
36 def get_lastfm_user(api_key: str, username: str) -> tuple[User, dict[str, str]]:
37  """Get and validate lastFM User."""
38  user = LastFMNetwork(api_key=api_key).get_user(username)
39  errors = {}
40  try:
41  user.get_playcount()
42  except WSError as error:
43  if error.details == "User not found":
44  errors["base"] = "invalid_account"
45  elif (
46  error.details
47  == "Invalid API key - You must be granted a valid key by last.fm"
48  ):
49  errors["base"] = "invalid_auth"
50  else:
51  errors["base"] = "unknown"
52  except Exception: # noqa: BLE001
53  errors["base"] = "unknown"
54  return user, errors
55 
56 
58  api_key: str, usernames: list[str]
59 ) -> tuple[list[str], dict[str, str]]:
60  """Validate list of users. Return tuple of valid users and errors."""
61  valid_users = []
62  errors = {}
63  for username in usernames:
64  _, lastfm_errors = get_lastfm_user(api_key, username)
65  if lastfm_errors:
66  errors = lastfm_errors
67  else:
68  valid_users.append(username)
69  return valid_users, errors
70 
71 
72 class LastFmConfigFlowHandler(ConfigFlow, domain=DOMAIN):
73  """Config flow handler for LastFm."""
74 
75  data: dict[str, Any] = {}
76 
77  @staticmethod
78  @callback
80  config_entry: ConfigEntry,
81  ) -> LastFmOptionsFlowHandler:
82  """Get the options flow for this handler."""
84 
85  async def async_step_user(
86  self, user_input: dict[str, Any] | None = None
87  ) -> ConfigFlowResult:
88  """Initialize user input."""
89  errors: dict[str, str] = {}
90  if user_input is not None:
91  self.datadata = user_input.copy()
92  _, errors = get_lastfm_user(
93  self.datadata[CONF_API_KEY], self.datadata[CONF_MAIN_USER]
94  )
95  if not errors:
96  return await self.async_step_friendsasync_step_friends()
97  return self.async_show_formasync_show_formasync_show_form(
98  step_id="user",
99  errors=errors,
100  description_placeholders=PLACEHOLDERS,
101  data_schema=self.add_suggested_values_to_schemaadd_suggested_values_to_schema(CONFIG_SCHEMA, user_input),
102  )
103 
105  self, user_input: dict[str, Any] | None = None
106  ) -> ConfigFlowResult:
107  """Form to select other users and friends."""
108  errors: dict[str, str] = {}
109  if user_input is not None:
110  users, errors = validate_lastfm_users(
111  self.datadata[CONF_API_KEY], user_input[CONF_USERS]
112  )
113  user_input[CONF_USERS] = users
114  if not errors:
115  return self.async_create_entryasync_create_entryasync_create_entry(
116  title="LastFM",
117  data={},
118  options={
119  CONF_API_KEY: self.datadata[CONF_API_KEY],
120  CONF_MAIN_USER: self.datadata[CONF_MAIN_USER],
121  CONF_USERS: [
122  self.datadata[CONF_MAIN_USER],
123  *user_input[CONF_USERS],
124  ],
125  },
126  )
127  try:
128  main_user, _ = get_lastfm_user(
129  self.datadata[CONF_API_KEY], self.datadata[CONF_MAIN_USER]
130  )
131  friends_response = await self.hass.async_add_executor_job(
132  main_user.get_friends
133  )
134  friends = [
135  SelectOptionDict(value=friend.name, label=friend.get_name(True))
136  for friend in friends_response
137  ]
138  except PyLastError:
139  friends = []
140  return self.async_show_formasync_show_formasync_show_form(
141  step_id="friends",
142  errors=errors,
143  data_schema=self.add_suggested_values_to_schemaadd_suggested_values_to_schema(
144  vol.Schema(
145  {
146  vol.Required(CONF_USERS): SelectSelector(
148  options=friends, custom_value=True, multiple=True
149  )
150  ),
151  }
152  ),
153  user_input or {CONF_USERS: []},
154  ),
155  )
156 
157 
159  """LastFm Options flow handler."""
160 
161  async def async_step_init(
162  self, user_input: dict[str, Any] | None = None
163  ) -> ConfigFlowResult:
164  """Initialize form."""
165  errors: dict[str, str] = {}
166  options = self.config_entryconfig_entryconfig_entry.options
167  if user_input is not None:
168  users, errors = validate_lastfm_users(
169  options[CONF_API_KEY], user_input[CONF_USERS]
170  )
171  user_input[CONF_USERS] = users
172  if not errors:
173  return self.async_create_entryasync_create_entry(
174  title="LastFM",
175  data={
176  **options,
177  CONF_USERS: user_input[CONF_USERS],
178  },
179  )
180  if options[CONF_MAIN_USER]:
181  try:
182  main_user, _ = get_lastfm_user(
183  options[CONF_API_KEY],
184  options[CONF_MAIN_USER],
185  )
186  friends_response = await self.hass.async_add_executor_job(
187  main_user.get_friends
188  )
189  friends = [
190  SelectOptionDict(value=friend.name, label=friend.get_name(True))
191  for friend in friends_response
192  ]
193  except PyLastError:
194  friends = []
195  else:
196  friends = []
197  return self.async_show_formasync_show_form(
198  step_id="init",
199  errors=errors,
200  data_schema=self.add_suggested_values_to_schemaadd_suggested_values_to_schema(
201  vol.Schema(
202  {
203  vol.Required(CONF_USERS): SelectSelector(
205  options=friends, custom_value=True, multiple=True
206  )
207  ),
208  }
209  ),
210  user_input or options,
211  ),
212  )
LastFmOptionsFlowHandler async_get_options_flow(ConfigEntry config_entry)
Definition: config_flow.py:81
ConfigFlowResult async_step_user(self, dict[str, Any]|None user_input=None)
Definition: config_flow.py:87
ConfigFlowResult async_step_friends(self, dict[str, Any]|None user_input=None)
Definition: config_flow.py:106
ConfigFlowResult async_step_init(self, dict[str, Any]|None user_input=None)
Definition: config_flow.py:163
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_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)
vol.Schema add_suggested_values_to_schema(self, vol.Schema data_schema, Mapping[str, Any]|None suggested_values)
_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)
tuple[list[str], dict[str, str]] validate_lastfm_users(str api_key, list[str] usernames)
Definition: config_flow.py:59
tuple[User, dict[str, str]] get_lastfm_user(str api_key, str username)
Definition: config_flow.py:36