153 lines
		
	
	
		
			4.8 KiB
		
	
	
	
		
			Python
		
	
	
			
		
		
	
	
			153 lines
		
	
	
		
			4.8 KiB
		
	
	
	
		
			Python
		
	
	
"""Config flow for flume integration."""
 | 
						|
from collections.abc import Mapping
 | 
						|
import logging
 | 
						|
import os
 | 
						|
from typing import Any
 | 
						|
 | 
						|
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 homeassistant.data_entry_flow import FlowResult
 | 
						|
 | 
						|
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) -> None:
 | 
						|
        """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, entry_data: Mapping[str, Any]) -> FlowResult:
 | 
						|
        """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."""
 |