"""Config flow for VLC media player Telnet integration.""" from __future__ import annotations from collections.abc import Mapping import logging from typing import Any from aiovlc.client import Client from aiovlc.exceptions import AuthError, ConnectError import voluptuous as vol from homeassistant import core, exceptions from homeassistant.components.hassio import HassioServiceInfo from homeassistant.config_entries import ConfigEntry, ConfigFlow from homeassistant.const import CONF_HOST, CONF_NAME, CONF_PASSWORD, CONF_PORT from homeassistant.data_entry_flow import FlowResult from .const import DEFAULT_PORT, DOMAIN _LOGGER = logging.getLogger(__name__) def user_form_schema(user_input: dict[str, Any] | None) -> vol.Schema: """Return user form schema.""" user_input = user_input or {} return vol.Schema( { vol.Required(CONF_PASSWORD): str, vol.Optional( CONF_HOST, default=user_input.get(CONF_HOST, "localhost") ): str, vol.Optional( CONF_PORT, default=user_input.get(CONF_PORT, DEFAULT_PORT) ): int, } ) STEP_REAUTH_DATA_SCHEMA = vol.Schema({vol.Required(CONF_PASSWORD): str}) async def vlc_connect(vlc: Client) -> None: """Connect to VLC.""" await vlc.connect() await vlc.login() await vlc.disconnect() async def validate_input( hass: core.HomeAssistant, data: dict[str, Any] ) -> dict[str, str]: """Validate the user input allows us to connect.""" vlc = Client( password=data[CONF_PASSWORD], host=data[CONF_HOST], port=data[CONF_PORT], ) try: await vlc_connect(vlc) except ConnectError as err: raise CannotConnect from err except AuthError as err: raise InvalidAuth from err # CONF_NAME is only present in the imported YAML data. return {"title": data.get(CONF_NAME) or data[CONF_HOST]} class VLCTelnetConfigFlow(ConfigFlow, domain=DOMAIN): """Handle a config flow for VLC media player Telnet.""" VERSION = 1 entry: ConfigEntry | None = None hassio_discovery: dict[str, Any] | None = None async def async_step_user( self, user_input: dict[str, Any] | None = None ) -> FlowResult: """Handle the initial step.""" if user_input is None: return self.async_show_form( step_id="user", data_schema=user_form_schema(user_input) ) self._async_abort_entries_match( {CONF_HOST: user_input[CONF_HOST], CONF_PORT: user_input[CONF_PORT]} ) errors = {} try: info = await validate_input(self.hass, user_input) except CannotConnect: errors["base"] = "cannot_connect" except InvalidAuth: errors["base"] = "invalid_auth" except Exception: # pylint: disable=broad-except _LOGGER.exception("Unexpected exception") errors["base"] = "unknown" else: return self.async_create_entry(title=info["title"], data=user_input) return self.async_show_form( step_id="user", data_schema=user_form_schema(user_input), errors=errors ) async def async_step_reauth(self, entry_data: Mapping[str, Any]) -> FlowResult: """Handle reauth flow.""" self.entry = self.hass.config_entries.async_get_entry(self.context["entry_id"]) assert self.entry self.context["title_placeholders"] = {"host": self.entry.data[CONF_HOST]} return await self.async_step_reauth_confirm() async def async_step_reauth_confirm( self, user_input: dict[str, Any] | None = None ) -> FlowResult: """Handle reauth confirm.""" assert self.entry errors = {} if user_input is not None: try: await validate_input(self.hass, {**self.entry.data, **user_input}) except CannotConnect: errors["base"] = "cannot_connect" except InvalidAuth: errors["base"] = "invalid_auth" except Exception: # pylint: disable=broad-except _LOGGER.exception("Unexpected exception") errors["base"] = "unknown" else: self.hass.config_entries.async_update_entry( self.entry, data={ **self.entry.data, CONF_PASSWORD: user_input[CONF_PASSWORD], }, ) self.hass.async_create_task( self.hass.config_entries.async_reload(self.entry.entry_id) ) return self.async_abort(reason="reauth_successful") return self.async_show_form( step_id="reauth_confirm", description_placeholders={CONF_HOST: self.entry.data[CONF_HOST]}, data_schema=STEP_REAUTH_DATA_SCHEMA, errors=errors, ) async def async_step_hassio(self, discovery_info: HassioServiceInfo) -> FlowResult: """Handle the discovery step via hassio.""" await self.async_set_unique_id("hassio") self._abort_if_unique_id_configured(discovery_info.config) self.hassio_discovery = discovery_info.config self.context["title_placeholders"] = {"host": discovery_info.config[CONF_HOST]} return await self.async_step_hassio_confirm() async def async_step_hassio_confirm( self, user_input: dict[str, Any] | None = None ) -> FlowResult: """Confirm Supervisor discovery.""" assert self.hassio_discovery if user_input is None: return self.async_show_form( step_id="hassio_confirm", description_placeholders={"addon": self.hassio_discovery["addon"]}, ) self.hassio_discovery.pop("addon") try: info = await validate_input(self.hass, self.hassio_discovery) except CannotConnect: return self.async_abort(reason="cannot_connect") except InvalidAuth: return self.async_abort(reason="invalid_auth") except Exception: # pylint: disable=broad-except _LOGGER.exception("Unexpected exception") return self.async_abort(reason="unknown") else: return self.async_create_entry( title=info["title"], data=self.hassio_discovery ) class CannotConnect(exceptions.HomeAssistantError): """Error to indicate we cannot connect.""" class InvalidAuth(exceptions.HomeAssistantError): """Error to indicate there is invalid auth."""