Home Assistant Unofficial Reference 2024.12.1
config_flow.py
Go to the documentation of this file.
1 """Config flow for the Amber Electric integration."""
2 
3 from __future__ import annotations
4 
5 import amberelectric
6 from amberelectric.models.site import Site
7 from amberelectric.models.site_status import SiteStatus
8 import voluptuous as vol
9 
10 from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
11 from homeassistant.const import CONF_API_TOKEN
13  SelectOptionDict,
14  SelectSelector,
15  SelectSelectorConfig,
16  SelectSelectorMode,
17 )
18 
19 from .const import CONF_SITE_ID, CONF_SITE_NAME, DOMAIN
20 
21 API_URL = "https://app.amber.com.au/developers"
22 
23 
24 def generate_site_selector_name(site: Site) -> str:
25  """Generate the name to show in the site drop down in the configuration flow."""
26  # For some reason the generated API key returns this as any, not a string. Thanks pydantic
27  nmi = str(site.nmi)
28  if site.status == SiteStatus.CLOSED:
29  if site.closed_on is None:
30  return f"{nmi} (Closed)"
31  return f"{nmi} (Closed: {site.closed_on.isoformat()})"
32  if site.status == SiteStatus.PENDING:
33  return f"{nmi} (Pending)"
34  return nmi
35 
36 
37 def filter_sites(sites: list[Site]) -> list[Site]:
38  """Deduplicates the list of sites."""
39  filtered: list[Site] = []
40  filtered_nmi: set[str] = set()
41 
42  for site in sorted(sites, key=lambda site: site.status):
43  if site.status == SiteStatus.ACTIVE or site.nmi not in filtered_nmi:
44  filtered.append(site)
45  filtered_nmi.add(site.nmi)
46 
47  return filtered
48 
49 
50 class AmberElectricConfigFlow(ConfigFlow, domain=DOMAIN):
51  """Handle a config flow."""
52 
53  VERSION = 1
54 
55  def __init__(self) -> None:
56  """Initialize the config flow."""
57  self._errors_errors: dict[str, str] = {}
58  self._sites_sites: list[Site] | None = None
59  self._api_token_api_token: str | None = None
60 
61  def _fetch_sites(self, token: str) -> list[Site] | None:
62  configuration = amberelectric.Configuration(access_token=token)
63  api_client = amberelectric.ApiClient(configuration)
64  api = amberelectric.AmberApi(api_client)
65 
66  try:
67  sites: list[Site] = filter_sites(api.get_sites())
68  except amberelectric.ApiException as api_exception:
69  if api_exception.status == 403:
70  self._errors_errors[CONF_API_TOKEN] = "invalid_api_token"
71  else:
72  self._errors_errors[CONF_API_TOKEN] = "unknown_error"
73  return None
74 
75  if len(sites) == 0:
76  self._errors_errors[CONF_API_TOKEN] = "no_site"
77  return None
78  return sites
79 
80  async def async_step_user(
81  self, user_input: dict[str, str] | None = None
82  ) -> ConfigFlowResult:
83  """Step when user initializes a integration."""
84  self._errors_errors = {}
85  self._sites_sites = None
86  self._api_token_api_token = None
87 
88  if user_input is not None:
89  token = user_input[CONF_API_TOKEN]
90  self._sites_sites = await self.hass.async_add_executor_job(
91  self._fetch_sites_fetch_sites, token
92  )
93 
94  if self._sites_sites is not None:
95  self._api_token_api_token = token
96  return await self.async_step_siteasync_step_site()
97 
98  else:
99  user_input = {CONF_API_TOKEN: ""}
100 
101  return self.async_show_formasync_show_formasync_show_form(
102  step_id="user",
103  description_placeholders={"api_url": API_URL},
104  data_schema=vol.Schema(
105  {
106  vol.Required(
107  CONF_API_TOKEN, default=user_input[CONF_API_TOKEN]
108  ): str,
109  }
110  ),
111  errors=self._errors_errors,
112  )
113 
114  async def async_step_site(
115  self, user_input: dict[str, str] | None = None
116  ) -> ConfigFlowResult:
117  """Step to select site."""
118  self._errors_errors = {}
119 
120  assert self._sites_sites is not None
121  assert self._api_token_api_token is not None
122 
123  if user_input is not None:
124  site_id = user_input[CONF_SITE_ID]
125  name = user_input.get(CONF_SITE_NAME, site_id)
126  return self.async_create_entryasync_create_entryasync_create_entry(
127  title=name,
128  data={CONF_SITE_ID: site_id, CONF_API_TOKEN: self._api_token_api_token},
129  )
130 
131  return self.async_show_formasync_show_formasync_show_form(
132  step_id="site",
133  data_schema=vol.Schema(
134  {
135  vol.Required(CONF_SITE_ID): SelectSelector(
137  options=[
139  value=site.id,
140  label=generate_site_selector_name(site),
141  )
142  for site in self._sites_sites
143  ],
144  mode=SelectSelectorMode.DROPDOWN,
145  )
146  ),
147  vol.Optional(CONF_SITE_NAME): str,
148  }
149  ),
150  errors=self._errors_errors,
151  )
ConfigFlowResult async_step_site(self, dict[str, str]|None user_input=None)
Definition: config_flow.py:116
ConfigFlowResult async_step_user(self, dict[str, str]|None user_input=None)
Definition: config_flow.py:82
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)
_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)