core/homeassistant/components/pyload/config_flow.py

227 lines
7.3 KiB
Python

"""Config flow for pyLoad integration."""
from __future__ import annotations
from collections.abc import Mapping
import logging
from typing import Any
from aiohttp import CookieJar
from pyloadapi.api import PyLoadAPI
from pyloadapi.exceptions import CannotConnect, InvalidAuth, ParserError
import voluptuous as vol
from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
from homeassistant.const import (
CONF_HOST,
CONF_NAME,
CONF_PASSWORD,
CONF_PORT,
CONF_SSL,
CONF_USERNAME,
CONF_VERIFY_SSL,
)
from homeassistant.core import HomeAssistant
from homeassistant.helpers.aiohttp_client import async_create_clientsession
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.selector import (
TextSelector,
TextSelectorConfig,
TextSelectorType,
)
from .const import DEFAULT_HOST, DEFAULT_NAME, DEFAULT_PORT, DOMAIN
_LOGGER = logging.getLogger(__name__)
STEP_USER_DATA_SCHEMA = vol.Schema(
{
vol.Required(CONF_HOST): str,
vol.Required(CONF_PORT, default=DEFAULT_PORT): cv.port,
vol.Required(CONF_SSL, default=False): cv.boolean,
vol.Required(CONF_VERIFY_SSL, default=True): bool,
vol.Required(CONF_USERNAME): TextSelector(
TextSelectorConfig(
type=TextSelectorType.TEXT,
autocomplete="username",
),
),
vol.Required(CONF_PASSWORD): TextSelector(
TextSelectorConfig(
type=TextSelectorType.PASSWORD,
autocomplete="current-password",
),
),
}
)
REAUTH_SCHEMA = vol.Schema(
{
vol.Required(CONF_USERNAME): TextSelector(
TextSelectorConfig(
type=TextSelectorType.TEXT,
autocomplete="username",
),
),
vol.Required(CONF_PASSWORD): TextSelector(
TextSelectorConfig(
type=TextSelectorType.PASSWORD,
autocomplete="current-password",
),
),
}
)
async def validate_input(hass: HomeAssistant, user_input: dict[str, Any]) -> None:
"""Validate the user input and try to connect to PyLoad."""
session = async_create_clientsession(
hass,
user_input[CONF_VERIFY_SSL],
cookie_jar=CookieJar(unsafe=True),
)
url = (
f"{"https" if user_input[CONF_SSL] else "http"}://"
f"{user_input[CONF_HOST]}:{user_input[CONF_PORT]}/"
)
pyload = PyLoadAPI(
session,
api_url=url,
username=user_input[CONF_USERNAME],
password=user_input[CONF_PASSWORD],
)
await pyload.login()
class PyLoadConfigFlow(ConfigFlow, domain=DOMAIN):
"""Handle a config flow for pyLoad."""
VERSION = 1
async def async_step_user(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
"""Handle the initial step."""
errors: dict[str, str] = {}
if user_input is not None:
self._async_abort_entries_match(
{CONF_HOST: user_input[CONF_HOST], CONF_PORT: user_input[CONF_PORT]}
)
try:
await validate_input(self.hass, user_input)
except (CannotConnect, ParserError):
errors["base"] = "cannot_connect"
except InvalidAuth:
errors["base"] = "invalid_auth"
except Exception:
_LOGGER.exception("Unexpected exception")
errors["base"] = "unknown"
else:
title = user_input.pop(CONF_NAME, DEFAULT_NAME)
return self.async_create_entry(title=title, data=user_input)
return self.async_show_form(
step_id="user",
data_schema=self.add_suggested_values_to_schema(
STEP_USER_DATA_SCHEMA, user_input
),
errors=errors,
)
async def async_step_import(self, import_data: dict[str, Any]) -> ConfigFlowResult:
"""Import config from yaml."""
config = {
CONF_NAME: import_data.get(CONF_NAME),
CONF_HOST: import_data.get(CONF_HOST, DEFAULT_HOST),
CONF_PASSWORD: import_data.get(CONF_PASSWORD, ""),
CONF_PORT: import_data.get(CONF_PORT, DEFAULT_PORT),
CONF_SSL: import_data.get(CONF_SSL, False),
CONF_USERNAME: import_data.get(CONF_USERNAME, ""),
CONF_VERIFY_SSL: False,
}
result = await self.async_step_user(config)
if errors := result.get("errors"):
return self.async_abort(reason=errors["base"])
return result
async def async_step_reauth(
self, entry_data: Mapping[str, Any]
) -> ConfigFlowResult:
"""Perform reauth upon an API authentication error."""
return await self.async_step_reauth_confirm()
async def async_step_reauth_confirm(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
"""Dialog that informs the user that reauth is required."""
errors = {}
reauth_entry = self._get_reauth_entry()
if user_input is not None:
new_input = reauth_entry.data | user_input
try:
await validate_input(self.hass, new_input)
except (CannotConnect, ParserError):
errors["base"] = "cannot_connect"
except InvalidAuth:
errors["base"] = "invalid_auth"
except Exception:
_LOGGER.exception("Unexpected exception")
errors["base"] = "unknown"
else:
return self.async_update_reload_and_abort(reauth_entry, data=new_input)
return self.async_show_form(
step_id="reauth_confirm",
data_schema=self.add_suggested_values_to_schema(
REAUTH_SCHEMA,
{
CONF_USERNAME: user_input[CONF_USERNAME]
if user_input is not None
else reauth_entry.data[CONF_USERNAME]
},
),
description_placeholders={CONF_NAME: reauth_entry.data[CONF_USERNAME]},
errors=errors,
)
async def async_step_reconfigure(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
"""Handle the reconfiguration flow."""
errors = {}
reconfig_entry = self._get_reconfigure_entry()
if user_input is not None:
try:
await validate_input(self.hass, user_input)
except (CannotConnect, ParserError):
errors["base"] = "cannot_connect"
except InvalidAuth:
errors["base"] = "invalid_auth"
except Exception:
_LOGGER.exception("Unexpected exception")
errors["base"] = "unknown"
else:
return self.async_update_reload_and_abort(
reconfig_entry,
data=user_input,
reload_even_if_entry_is_unchanged=False,
)
return self.async_show_form(
step_id="reconfigure",
data_schema=self.add_suggested_values_to_schema(
STEP_USER_DATA_SCHEMA,
user_input or reconfig_entry.data,
),
description_placeholders={CONF_NAME: reconfig_entry.data[CONF_USERNAME]},
errors=errors,
)