"""Config flow for the Riedel RRCS integration.""" from __future__ import annotations import logging from typing import Any import voluptuous as vol from homeassistant.config_entries import ( ConfigEntry, ConfigFlow, ConfigFlowResult, OptionsFlow, ) from homeassistant.const import CONF_HOST, CONF_PORT, CONF_SCAN_INTERVAL from homeassistant.core import callback from homeassistant.helpers.aiohttp_client import async_get_clientsession from homeassistant.helpers import selector from .const import ( CONF_CALLBACK_PORT, CONF_DISCOVER_GPIO, CONF_GPIO_ENTITIES, CONF_NOTIFICATIONS, CONF_POLL_GPIO, CONF_RPC_PATH, CONF_TIMEOUT, CONF_TRANSKEY_PREFIX, DEFAULT_DISCOVER_GPIO, DEFAULT_NOTIFICATIONS, DEFAULT_PATH, DEFAULT_POLL_GPIO, DEFAULT_PORT, DEFAULT_SCAN_INTERVAL, DEFAULT_TIMEOUT, DEFAULT_TRANSKEY_PREFIX, DOMAIN, ) from .rrcs import RRCSClient, RRCSError, parse_gpio_config _LOGGER = logging.getLogger(__name__) STEP_USER_SCHEMA = vol.Schema( { vol.Required(CONF_HOST): str, vol.Required(CONF_PORT, default=DEFAULT_PORT): vol.Coerce(int), vol.Optional(CONF_RPC_PATH, default=DEFAULT_PATH): str, vol.Optional(CONF_TIMEOUT, default=DEFAULT_TIMEOUT): vol.Coerce(int), vol.Optional(CONF_TRANSKEY_PREFIX, default=DEFAULT_TRANSKEY_PREFIX): vol.All( str, vol.Length(min=1, max=1) ), } ) class RRCSConfigFlow(ConfigFlow, domain=DOMAIN): """Handle the config flow.""" 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: host = user_input[CONF_HOST] port = user_input[CONF_PORT] await self.async_set_unique_id(f"{host}:{port}") self._abort_if_unique_id_configured() client = RRCSClient( session=async_get_clientsession(self.hass), host=host, port=port, path=user_input.get(CONF_RPC_PATH, DEFAULT_PATH), timeout=user_input.get(CONF_TIMEOUT, DEFAULT_TIMEOUT), transkey_prefix=user_input.get( CONF_TRANSKEY_PREFIX, DEFAULT_TRANSKEY_PREFIX ), ) try: version = await client.get_version() except RRCSError as err: _LOGGER.debug("RRCS connection test failed: %s", err) errors["base"] = "cannot_connect" else: title = f"RRCS {host}" if version: title = f"{title} ({version})" return self.async_create_entry(title=title, data=user_input) return self.async_show_form( step_id="user", data_schema=STEP_USER_SCHEMA, errors=errors ) @staticmethod @callback def async_get_options_flow(config_entry: ConfigEntry) -> RRCSOptionsFlow: """Return the options flow.""" return RRCSOptionsFlow(config_entry) class RRCSOptionsFlow(OptionsFlow): """Handle the options flow.""" def __init__(self, config_entry: ConfigEntry) -> None: """Initialise the options flow.""" self._entry = config_entry async def async_step_init( self, user_input: dict[str, Any] | None = None ) -> ConfigFlowResult: """Manage the options.""" errors: dict[str, str] = {} if user_input is not None: try: parse_gpio_config(user_input.get(CONF_GPIO_ENTITIES)) except ValueError as err: _LOGGER.debug("Invalid GPIO list: %s", err) errors[CONF_GPIO_ENTITIES] = "invalid_gpio_list" else: return self.async_create_entry(data=user_input) current = {**self._entry.data, **self._entry.options} schema = vol.Schema( { vol.Optional( CONF_SCAN_INTERVAL, default=current.get(CONF_SCAN_INTERVAL, DEFAULT_SCAN_INTERVAL), ): vol.All(vol.Coerce(int), vol.Range(min=5, max=3600)), vol.Optional( CONF_NOTIFICATIONS, default=current.get(CONF_NOTIFICATIONS, DEFAULT_NOTIFICATIONS), ): bool, vol.Optional( CONF_CALLBACK_PORT, description={"suggested_value": current.get(CONF_CALLBACK_PORT)}, ): vol.Any(None, vol.Coerce(int)), vol.Optional( CONF_DISCOVER_GPIO, default=current.get(CONF_DISCOVER_GPIO, DEFAULT_DISCOVER_GPIO), ): bool, vol.Optional( CONF_POLL_GPIO, default=current.get(CONF_POLL_GPIO, DEFAULT_POLL_GPIO), ): bool, vol.Optional( CONF_GPIO_ENTITIES, description={ "suggested_value": current.get(CONF_GPIO_ENTITIES, "") }, ): selector.TextSelector( selector.TextSelectorConfig(multiline=True) ), vol.Optional( CONF_TIMEOUT, default=current.get(CONF_TIMEOUT, DEFAULT_TIMEOUT) ): vol.All(vol.Coerce(int), vol.Range(min=1, max=120)), } ) return self.async_show_form(step_id="init", data_schema=schema, errors=errors)