"""Config flow for Flipr integration.""" from __future__ import annotations import logging from flipr_api import FliprAPIRestClient from requests.exceptions import HTTPError, Timeout import voluptuous as vol from homeassistant import config_entries from homeassistant.const import CONF_EMAIL, CONF_PASSWORD from homeassistant.data_entry_flow import FlowResult from .const import CONF_FLIPR_ID, DOMAIN _LOGGER = logging.getLogger(__name__) class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): """Handle a config flow for Flipr.""" VERSION = 1 _username: str _password: str _flipr_id: str = "" _possible_flipr_ids: list[str] async def async_step_user( self, user_input: dict[str, str] | None = None ) -> FlowResult: """Handle the initial step.""" if user_input is None: return self._show_setup_form() self._username = user_input[CONF_EMAIL] self._password = user_input[CONF_PASSWORD] errors = {} if not self._flipr_id: try: flipr_ids = await self._authenticate_and_search_flipr() except HTTPError: errors["base"] = "invalid_auth" except (Timeout, ConnectionError): errors["base"] = "cannot_connect" except Exception as exception: # pylint: disable=broad-except errors["base"] = "unknown" _LOGGER.exception(exception) if not errors and not flipr_ids: # No flipr_id found. Tell the user with an error message. errors["base"] = "no_flipr_id_found" if errors: return self._show_setup_form(errors) if len(flipr_ids) == 1: self._flipr_id = flipr_ids[0] else: # If multiple flipr found (rare case), we ask the user to choose one in a select box. # The user will have to run config_flow as many times as many fliprs he has. self._possible_flipr_ids = flipr_ids return await self.async_step_flipr_id() # Check if already configured await self.async_set_unique_id(self._flipr_id) self._abort_if_unique_id_configured() return self.async_create_entry( title=self._flipr_id, data={ CONF_EMAIL: self._username, CONF_PASSWORD: self._password, CONF_FLIPR_ID: self._flipr_id, }, ) def _show_setup_form(self, errors=None): """Show the setup form to the user.""" return self.async_show_form( step_id="user", data_schema=vol.Schema( {vol.Required(CONF_EMAIL): str, vol.Required(CONF_PASSWORD): str} ), errors=errors, ) async def _authenticate_and_search_flipr(self) -> list[str]: """Validate the username and password provided and searches for a flipr id.""" # Instantiates the flipr API that does not require async since it is has no network access. client = FliprAPIRestClient(self._username, self._password) flipr_ids = await self.hass.async_add_executor_job(client.search_flipr_ids) return flipr_ids async def async_step_flipr_id( self, user_input: dict[str, str] | None = None ) -> FlowResult: """Handle the initial step.""" if not user_input: # Creation of a select with the proposal of flipr ids values found by API. flipr_ids_for_form = {} for flipr_id in self._possible_flipr_ids: flipr_ids_for_form[flipr_id] = f"{flipr_id}" return self.async_show_form( step_id="flipr_id", data_schema=vol.Schema( { vol.Required(CONF_FLIPR_ID): vol.All( vol.Coerce(str), vol.In(flipr_ids_for_form) ) } ), ) # Get chosen flipr_id. self._flipr_id = user_input[CONF_FLIPR_ID] return await self.async_step_user( { CONF_EMAIL: self._username, CONF_PASSWORD: self._password, CONF_FLIPR_ID: self._flipr_id, } )