core/homeassistant/components/flume/config_flow.py

150 lines
4.7 KiB
Python

"""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."""