"""Config flow for flume integration.""" import logging import os from pyflume import FlumeAuth, FlumeDeviceList from requests.exceptions import RequestException import voluptuous as vol from homeassistant import config_entries, core, exceptions from homeassistant.const import ( CONF_CLIENT_ID, CONF_CLIENT_SECRET, CONF_PASSWORD, CONF_USERNAME, ) from .const import BASE_TOKEN_FILENAME, DOMAIN _LOGGER = logging.getLogger(__name__) # If flume ever implements a login page for oauth # we can use the oauth2 support built into Home Assistant. # # Currently they only implement the token endpoint # DATA_SCHEMA = vol.Schema( { vol.Required(CONF_USERNAME): str, vol.Required(CONF_PASSWORD): str, vol.Required(CONF_CLIENT_ID): str, vol.Required(CONF_CLIENT_SECRET): str, } ) def _validate_input(hass: core.HomeAssistant, data: dict, clear_token_file: bool): """Validate in the executor.""" flume_token_full_path = hass.config.path( f"{BASE_TOKEN_FILENAME}-{data[CONF_USERNAME]}" ) if clear_token_file and os.path.exists(flume_token_full_path): os.unlink(flume_token_full_path) return FlumeDeviceList( FlumeAuth( data[CONF_USERNAME], data[CONF_PASSWORD], data[CONF_CLIENT_ID], data[CONF_CLIENT_SECRET], flume_token_file=flume_token_full_path, ) ) async def validate_input( hass: core.HomeAssistant, data: dict, clear_token_file: bool = False ): """Validate the user input allows us to connect. Data has the keys from DATA_SCHEMA with values provided by the user. """ try: flume_devices = await hass.async_add_executor_job( _validate_input, hass, data, clear_token_file ) except RequestException as err: raise CannotConnect from err except Exception as err: _LOGGER.exception("Auth exception") raise InvalidAuth from err if not flume_devices or not flume_devices.device_list: raise CannotConnect # Return info that you want to store in the config entry. return {"title": data[CONF_USERNAME]} class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): """Handle a config flow for flume.""" VERSION = 1 def __init__(self): """Init flume config flow.""" self._reauth_unique_id = None async def async_step_user(self, user_input=None): """Handle the initial step.""" errors = {} if user_input is not None: await self.async_set_unique_id(user_input[CONF_USERNAME]) self._abort_if_unique_id_configured() try: info = await validate_input(self.hass, user_input) return self.async_create_entry(title=info["title"], data=user_input) except CannotConnect: errors["base"] = "cannot_connect" except InvalidAuth: errors[CONF_PASSWORD] = "invalid_auth" return self.async_show_form( step_id="user", data_schema=DATA_SCHEMA, errors=errors ) async def async_step_reauth(self, user_input=None): """Handle reauth.""" self._reauth_unique_id = self.context["unique_id"] return await self.async_step_reauth_confirm() async def async_step_reauth_confirm(self, user_input=None): """Handle reauth input.""" errors = {} existing_entry = await self.async_set_unique_id(self._reauth_unique_id) if user_input is not None: new_data = {**existing_entry.data, CONF_PASSWORD: user_input[CONF_PASSWORD]} try: await validate_input(self.hass, new_data, clear_token_file=True) except CannotConnect: errors["base"] = "cannot_connect" except InvalidAuth: errors[CONF_PASSWORD] = "invalid_auth" else: self.hass.config_entries.async_update_entry( existing_entry, data=new_data ) await self.hass.config_entries.async_reload(existing_entry.entry_id) return self.async_abort(reason="reauth_successful") return self.async_show_form( description_placeholders={ CONF_USERNAME: existing_entry.data[CONF_USERNAME] }, step_id="reauth_confirm", data_schema=vol.Schema( { vol.Required(CONF_PASSWORD): str, } ), errors=errors, ) class CannotConnect(exceptions.HomeAssistantError): """Error to indicate we cannot connect.""" class InvalidAuth(exceptions.HomeAssistantError): """Error to indicate there is invalid auth."""