"""Configflow for the emoncms integration.""" from __future__ import annotations from typing import Any from pyemoncms import EmoncmsClient import voluptuous as vol from homeassistant.config_entries import ( ConfigEntry, ConfigFlow, ConfigFlowResult, OptionsFlow, ) from homeassistant.const import CONF_API_KEY, CONF_URL from homeassistant.core import callback from homeassistant.helpers.aiohttp_client import async_get_clientsession from homeassistant.helpers.selector import selector from homeassistant.helpers.typing import ConfigType from .const import ( CONF_MESSAGE, CONF_ONLY_INCLUDE_FEEDID, CONF_SUCCESS, DOMAIN, FEED_ID, FEED_NAME, FEED_TAG, LOGGER, ) def get_options(feeds: list[dict[str, Any]]) -> list[dict[str, Any]]: """Build the selector options with the feed list.""" return [ { "value": feed[FEED_ID], "label": f"{feed[FEED_ID]}|{feed[FEED_TAG]}|{feed[FEED_NAME]}", } for feed in feeds ] def sensor_name(url: str) -> str: """Return sensor name.""" sensorip = url.rsplit("//", maxsplit=1)[-1] return f"emoncms@{sensorip}" async def get_feed_list( emoncms_client: EmoncmsClient, ) -> dict[str, Any]: """Check connection to emoncms and return feed list if successful.""" return await emoncms_client.async_request("/feed/list.json") class EmoncmsConfigFlow(ConfigFlow, domain=DOMAIN): """emoncms integration UI config flow.""" url: str api_key: str include_only_feeds: list | None = None dropdown: dict = {} @staticmethod @callback def async_get_options_flow( config_entry: ConfigEntry, ) -> EmoncmsOptionsFlow: """Get the options flow for this handler.""" return EmoncmsOptionsFlow(config_entry) async def async_step_user( self, user_input: dict[str, Any] | None = None ) -> ConfigFlowResult: """Initiate a flow via the UI.""" errors: dict[str, str] = {} description_placeholders = {} if user_input is not None: self.url = user_input[CONF_URL] self.api_key = user_input[CONF_API_KEY] self._async_abort_entries_match( { CONF_API_KEY: self.api_key, CONF_URL: self.url, } ) emoncms_client = EmoncmsClient( self.url, self.api_key, session=async_get_clientsession(self.hass) ) result = await get_feed_list(emoncms_client) if not result[CONF_SUCCESS]: errors["base"] = "api_error" description_placeholders = {"details": result[CONF_MESSAGE]} else: self.include_only_feeds = user_input.get(CONF_ONLY_INCLUDE_FEEDID) await self.async_set_unique_id(await emoncms_client.async_get_uuid()) self._abort_if_unique_id_configured() options = get_options(result[CONF_MESSAGE]) self.dropdown = { "options": options, "mode": "dropdown", "multiple": True, } return await self.async_step_choose_feeds() return self.async_show_form( step_id="user", data_schema=self.add_suggested_values_to_schema( vol.Schema( { vol.Required(CONF_URL): str, vol.Required(CONF_API_KEY): str, } ), user_input, ), errors=errors, description_placeholders=description_placeholders, ) async def async_step_choose_feeds( self, user_input: dict[str, Any] | None = None, ) -> ConfigFlowResult: """Choose feeds to import.""" errors: dict[str, str] = {} include_only_feeds: list = [] if user_input or self.include_only_feeds is not None: if self.include_only_feeds is not None: include_only_feeds = self.include_only_feeds elif user_input: include_only_feeds = user_input[CONF_ONLY_INCLUDE_FEEDID] return self.async_create_entry( title=sensor_name(self.url), data={ CONF_URL: self.url, CONF_API_KEY: self.api_key, CONF_ONLY_INCLUDE_FEEDID: include_only_feeds, }, ) return self.async_show_form( step_id="choose_feeds", data_schema=vol.Schema( { vol.Required( CONF_ONLY_INCLUDE_FEEDID, default=include_only_feeds, ): selector({"select": self.dropdown}), } ), errors=errors, ) async def async_step_import(self, import_info: ConfigType) -> ConfigFlowResult: """Import config from yaml.""" url = import_info[CONF_URL] api_key = import_info[CONF_API_KEY] include_only_feeds = None if import_info.get(CONF_ONLY_INCLUDE_FEEDID) is not None: include_only_feeds = list(map(str, import_info[CONF_ONLY_INCLUDE_FEEDID])) config = { CONF_API_KEY: api_key, CONF_ONLY_INCLUDE_FEEDID: include_only_feeds, CONF_URL: url, } LOGGER.debug(config) result = await self.async_step_user(config) if errors := result.get("errors"): return self.async_abort(reason=errors["base"]) return result class EmoncmsOptionsFlow(OptionsFlow): """Emoncms Options flow handler.""" def __init__(self, config_entry: ConfigEntry) -> None: """Initialize emoncms options flow.""" self._url = config_entry.data[CONF_URL] self._api_key = config_entry.data[CONF_API_KEY] async def async_step_init( self, user_input: dict[str, Any] | None = None ) -> ConfigFlowResult: """Manage the options.""" errors: dict[str, str] = {} description_placeholders = {} include_only_feeds = self.config_entry.options.get( CONF_ONLY_INCLUDE_FEEDID, self.config_entry.data.get(CONF_ONLY_INCLUDE_FEEDID, []), ) options: list = include_only_feeds emoncms_client = EmoncmsClient( self._url, self._api_key, session=async_get_clientsession(self.hass), ) result = await get_feed_list(emoncms_client) if not result[CONF_SUCCESS]: errors["base"] = "api_error" description_placeholders = {"details": result[CONF_MESSAGE]} else: options = get_options(result[CONF_MESSAGE]) dropdown = {"options": options, "mode": "dropdown", "multiple": True} if user_input: include_only_feeds = user_input[CONF_ONLY_INCLUDE_FEEDID] return self.async_create_entry( data={ CONF_ONLY_INCLUDE_FEEDID: include_only_feeds, }, ) return self.async_show_form( step_id="init", data_schema=vol.Schema( { vol.Required( CONF_ONLY_INCLUDE_FEEDID, default=include_only_feeds ): selector({"select": dropdown}), } ), errors=errors, description_placeholders=description_placeholders, )