core/homeassistant/components/pyload/config_flow.py

272 lines
8.9 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 import CannotConnect, InvalidAuth, ParserError, PyLoadAPI
import voluptuous as vol
from yarl import URL
from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
from homeassistant.const import (
CONF_NAME,
CONF_PASSWORD,
CONF_URL,
CONF_USERNAME,
CONF_VERIFY_SSL,
)
from homeassistant.core import HomeAssistant
from homeassistant.helpers.aiohttp_client import async_create_clientsession
from homeassistant.helpers.selector import (
TextSelector,
TextSelectorConfig,
TextSelectorType,
)
from homeassistant.helpers.service_info.hassio import HassioServiceInfo
from .const import DEFAULT_NAME, DOMAIN
_LOGGER = logging.getLogger(__name__)
STEP_USER_DATA_SCHEMA = vol.Schema(
{
vol.Required(CONF_URL): TextSelector(
TextSelectorConfig(
type=TextSelectorType.URL,
autocomplete="url",
),
),
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),
)
pyload = PyLoadAPI(
session,
api_url=URL(user_input[CONF_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
MINOR_VERSION = 1
_hassio_discovery: HassioServiceInfo | None = None
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:
url = URL(user_input[CONF_URL]).human_repr()
self._async_abort_entries_match({CONF_URL: url})
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 = DEFAULT_NAME
return self.async_create_entry(
title=title,
data={
**user_input,
CONF_URL: url,
},
)
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_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:
try:
await validate_input(self.hass, {**reauth_entry.data, **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(
reauth_entry, data_updates=user_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,
CONF_URL: URL(user_input[CONF_URL]).human_repr(),
},
reload_even_if_entry_is_unchanged=False,
)
suggested_values = user_input if user_input else reconfig_entry.data
return self.async_show_form(
step_id="reconfigure",
data_schema=self.add_suggested_values_to_schema(
STEP_USER_DATA_SCHEMA,
suggested_values,
),
description_placeholders={CONF_NAME: reconfig_entry.data[CONF_USERNAME]},
errors=errors,
)
async def async_step_hassio(
self, discovery_info: HassioServiceInfo
) -> ConfigFlowResult:
"""Prepare configuration for pyLoad add-on.
This flow is triggered by the discovery component.
"""
url = URL(discovery_info.config[CONF_URL]).human_repr()
self._async_abort_entries_match({CONF_URL: url})
await self.async_set_unique_id(discovery_info.uuid)
self._abort_if_unique_id_configured(updates={CONF_URL: url})
discovery_info.config[CONF_URL] = url
self._hassio_discovery = discovery_info
return await self.async_step_hassio_confirm()
async def async_step_hassio_confirm(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
"""Confirm Supervisor discovery."""
assert self._hassio_discovery
errors: dict[str, str] = {}
data = {**self._hassio_discovery.config, CONF_VERIFY_SSL: False}
if user_input is not None:
data.update(user_input)
try:
await validate_input(self.hass, data)
except (CannotConnect, ParserError):
_LOGGER.debug("Cannot connect", exc_info=True)
errors["base"] = "cannot_connect"
except InvalidAuth:
errors["base"] = "invalid_auth"
except Exception:
_LOGGER.exception("Unexpected exception")
errors["base"] = "unknown"
else:
if user_input is None:
self._set_confirm_only()
return self.async_show_form(
step_id="hassio_confirm",
description_placeholders=self._hassio_discovery.config,
)
return self.async_create_entry(title=self._hassio_discovery.slug, data=data)
return self.async_show_form(
step_id="hassio_confirm",
data_schema=self.add_suggested_values_to_schema(
data_schema=REAUTH_SCHEMA, suggested_values=data
),
description_placeholders=self._hassio_discovery.config,
errors=errors if user_input is not None else None,
)