Home Assistant Unofficial Reference 2024.12.1
config_flow.py
Go to the documentation of this file.
1 """Configflow for the emoncms integration."""
2 
3 from __future__ import annotations
4 
5 from typing import Any
6 
7 from pyemoncms import EmoncmsClient
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, CONF_URL
17 from homeassistant.core import callback
18 from homeassistant.helpers.aiohttp_client import async_get_clientsession
19 from homeassistant.helpers.selector import selector
20 from homeassistant.helpers.typing import ConfigType
21 
22 from .const import (
23  CONF_MESSAGE,
24  CONF_ONLY_INCLUDE_FEEDID,
25  CONF_SUCCESS,
26  DOMAIN,
27  FEED_ID,
28  FEED_NAME,
29  FEED_TAG,
30  LOGGER,
31 )
32 
33 
34 def get_options(feeds: list[dict[str, Any]]) -> list[dict[str, Any]]:
35  """Build the selector options with the feed list."""
36  return [
37  {
38  "value": feed[FEED_ID],
39  "label": f"{feed[FEED_ID]}|{feed[FEED_TAG]}|{feed[FEED_NAME]}",
40  }
41  for feed in feeds
42  ]
43 
44 
45 def sensor_name(url: str) -> str:
46  """Return sensor name."""
47  sensorip = url.rsplit("//", maxsplit=1)[-1]
48  return f"emoncms@{sensorip}"
49 
50 
51 async def get_feed_list(
52  emoncms_client: EmoncmsClient,
53 ) -> dict[str, Any]:
54  """Check connection to emoncms and return feed list if successful."""
55  return await emoncms_client.async_request("/feed/list.json")
56 
57 
58 class EmoncmsConfigFlow(ConfigFlow, domain=DOMAIN):
59  """emoncms integration UI config flow."""
60 
61  url: str
62  api_key: str
63  include_only_feeds: list | None = None
64  dropdown: dict = {}
65 
66  @staticmethod
67  @callback
69  config_entry: ConfigEntry,
70  ) -> EmoncmsOptionsFlow:
71  """Get the options flow for this handler."""
72  return EmoncmsOptionsFlow(config_entry)
73 
74  async def async_step_user(
75  self, user_input: dict[str, Any] | None = None
76  ) -> ConfigFlowResult:
77  """Initiate a flow via the UI."""
78  errors: dict[str, str] = {}
79  description_placeholders = {}
80 
81  if user_input is not None:
82  self.urlurl = user_input[CONF_URL]
83  self.api_keyapi_key = user_input[CONF_API_KEY]
84  self._async_abort_entries_match_async_abort_entries_match(
85  {
86  CONF_API_KEY: self.api_keyapi_key,
87  CONF_URL: self.urlurl,
88  }
89  )
90  emoncms_client = EmoncmsClient(
91  self.urlurl, self.api_keyapi_key, session=async_get_clientsession(self.hass)
92  )
93  result = await get_feed_list(emoncms_client)
94  if not result[CONF_SUCCESS]:
95  errors["base"] = "api_error"
96  description_placeholders = {"details": result[CONF_MESSAGE]}
97  else:
98  self.include_only_feedsinclude_only_feeds = user_input.get(CONF_ONLY_INCLUDE_FEEDID)
99  await self.async_set_unique_idasync_set_unique_id(await emoncms_client.async_get_uuid())
100  self._abort_if_unique_id_configured_abort_if_unique_id_configured()
101  options = get_options(result[CONF_MESSAGE])
102  self.dropdowndropdown = {
103  "options": options,
104  "mode": "dropdown",
105  "multiple": True,
106  }
107  return await self.async_step_choose_feedsasync_step_choose_feeds()
108  return self.async_show_formasync_show_formasync_show_form(
109  step_id="user",
110  data_schema=self.add_suggested_values_to_schemaadd_suggested_values_to_schema(
111  vol.Schema(
112  {
113  vol.Required(CONF_URL): str,
114  vol.Required(CONF_API_KEY): str,
115  }
116  ),
117  user_input,
118  ),
119  errors=errors,
120  description_placeholders=description_placeholders,
121  )
122 
124  self,
125  user_input: dict[str, Any] | None = None,
126  ) -> ConfigFlowResult:
127  """Choose feeds to import."""
128  errors: dict[str, str] = {}
129  include_only_feeds: list = []
130  if user_input or self.include_only_feedsinclude_only_feeds is not None:
131  if self.include_only_feedsinclude_only_feeds is not None:
132  include_only_feeds = self.include_only_feedsinclude_only_feeds
133  elif user_input:
134  include_only_feeds = user_input[CONF_ONLY_INCLUDE_FEEDID]
135  return self.async_create_entryasync_create_entryasync_create_entry(
136  title=sensor_name(self.urlurl),
137  data={
138  CONF_URL: self.urlurl,
139  CONF_API_KEY: self.api_keyapi_key,
140  CONF_ONLY_INCLUDE_FEEDID: include_only_feeds,
141  },
142  )
143  return self.async_show_formasync_show_formasync_show_form(
144  step_id="choose_feeds",
145  data_schema=vol.Schema(
146  {
147  vol.Required(
148  CONF_ONLY_INCLUDE_FEEDID,
149  default=include_only_feeds,
150  ): selector({"select": self.dropdowndropdown}),
151  }
152  ),
153  errors=errors,
154  )
155 
156  async def async_step_import(self, import_info: ConfigType) -> ConfigFlowResult:
157  """Import config from yaml."""
158  url = import_info[CONF_URL]
159  api_key = import_info[CONF_API_KEY]
160  include_only_feeds = None
161  if import_info.get(CONF_ONLY_INCLUDE_FEEDID) is not None:
162  include_only_feeds = list(map(str, import_info[CONF_ONLY_INCLUDE_FEEDID]))
163  config = {
164  CONF_API_KEY: api_key,
165  CONF_ONLY_INCLUDE_FEEDID: include_only_feeds,
166  CONF_URL: url,
167  }
168  LOGGER.debug(config)
169  result = await self.async_step_userasync_step_userasync_step_user(config)
170  if errors := result.get("errors"):
171  return self.async_abortasync_abortasync_abort(reason=errors["base"])
172  return result
173 
174 
176  """Emoncms Options flow handler."""
177 
178  def __init__(self, config_entry: ConfigEntry) -> None:
179  """Initialize emoncms options flow."""
180  self._url_url = config_entry.data[CONF_URL]
181  self._api_key_api_key = config_entry.data[CONF_API_KEY]
182 
183  async def async_step_init(
184  self, user_input: dict[str, Any] | None = None
185  ) -> ConfigFlowResult:
186  """Manage the options."""
187  errors: dict[str, str] = {}
188  description_placeholders = {}
189  include_only_feeds = self.config_entryconfig_entryconfig_entry.options.get(
190  CONF_ONLY_INCLUDE_FEEDID,
191  self.config_entryconfig_entryconfig_entry.data.get(CONF_ONLY_INCLUDE_FEEDID, []),
192  )
193  options: list = include_only_feeds
194  emoncms_client = EmoncmsClient(
195  self._url_url,
196  self._api_key_api_key,
197  session=async_get_clientsession(self.hass),
198  )
199  result = await get_feed_list(emoncms_client)
200  if not result[CONF_SUCCESS]:
201  errors["base"] = "api_error"
202  description_placeholders = {"details": result[CONF_MESSAGE]}
203  else:
204  options = get_options(result[CONF_MESSAGE])
205  dropdown = {"options": options, "mode": "dropdown", "multiple": True}
206  if user_input:
207  include_only_feeds = user_input[CONF_ONLY_INCLUDE_FEEDID]
208  return self.async_create_entryasync_create_entry(
209  data={
210  CONF_ONLY_INCLUDE_FEEDID: include_only_feeds,
211  },
212  )
213 
214  return self.async_show_formasync_show_form(
215  step_id="init",
216  data_schema=vol.Schema(
217  {
218  vol.Required(
219  CONF_ONLY_INCLUDE_FEEDID, default=include_only_feeds
220  ): selector({"select": dropdown}),
221  }
222  ),
223  errors=errors,
224  description_placeholders=description_placeholders,
225  )
ConfigFlowResult async_step_choose_feeds(self, dict[str, Any]|None user_input=None)
Definition: config_flow.py:126
ConfigFlowResult async_step_import(self, ConfigType import_info)
Definition: config_flow.py:156
ConfigFlowResult async_step_user(self, dict[str, Any]|None user_input=None)
Definition: config_flow.py:76
EmoncmsOptionsFlow async_get_options_flow(ConfigEntry config_entry)
Definition: config_flow.py:70
ConfigFlowResult async_step_init(self, dict[str, Any]|None user_input=None)
Definition: config_flow.py:185
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)
None _async_abort_entries_match(self, dict[str, Any]|None match_dict=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)
_FlowResultT async_abort(self, *str reason, Mapping[str, str]|None description_placeholders=None)
dict[str, Any] get_feed_list(EmoncmsClient emoncms_client)
Definition: config_flow.py:53
list[dict[str, Any]] get_options(list[dict[str, Any]] feeds)
Definition: config_flow.py:34
aiohttp.ClientSession async_get_clientsession(HomeAssistant hass, bool verify_ssl=True, socket.AddressFamily family=socket.AF_UNSPEC, ssl_util.SSLCipherList ssl_cipher=ssl_util.SSLCipherList.PYTHON_DEFAULT)
Selector selector(Any config)
Definition: selector.py:41