"""Adds config flow for GarbageCollection.""" from __future__ import annotations import logging # import uuid from collections.abc import Mapping from typing import Any, Dict, cast import voluptuous as vol from homeassistant.const import ATTR_HIDDEN, CONF_ENTITIES, CONF_NAME from homeassistant.core import callback from homeassistant.helpers import selector from homeassistant.helpers.schema_config_entry_flow import ( SchemaConfigFlowHandler, SchemaFlowError, SchemaFlowFormStep, SchemaFlowMenuStep, SchemaOptionsFlowHandler, ) from . import const, helpers _LOGGER = logging.getLogger(__name__) async def _validate_config( _: SchemaConfigFlowHandler | SchemaOptionsFlowHandler, data: Any ) -> Any: """Validate config.""" if const.CONF_DATE in data: try: helpers.month_day_text(data[const.CONF_DATE]) except vol.Invalid as exc: raise SchemaFlowError("month_day") from exc return data def required( key: str, options: Dict[str, Any], default: Any | None = None ) -> vol.Required: """Return vol.Required.""" if isinstance(options, dict) and key in options: suggested_value = options[key] elif default is not None: suggested_value = default else: return vol.Required(key) return vol.Required(key, description={"suggested_value": suggested_value}) def optional( key: str, options: Dict[str, Any], default: Any | None = None ) -> vol.Optional: """Return vol.Optional.""" if isinstance(options, dict) and key in options: suggested_value = options[key] elif default is not None: suggested_value = default else: return vol.Optional(key) return vol.Optional(key, description={"suggested_value": suggested_value}) async def general_config_schema( handler: SchemaConfigFlowHandler | SchemaOptionsFlowHandler, ) -> vol.Schema: """Generate config schema.""" return vol.Schema( { optional(CONF_NAME, handler.options): selector.TextSelector(), required( const.CONF_FREQUENCY, handler.options, const.DEFAULT_FREQUENCY ): selector.SelectSelector( selector.SelectSelectorConfig(options=const.FREQUENCY_OPTIONS) ), optional( const.CONF_ICON_NORMAL, handler.options, const.DEFAULT_ICON_NORMAL ): selector.IconSelector(), optional( const.CONF_ICON_TODAY, handler.options, const.DEFAULT_ICON_TODAY ): selector.IconSelector(), optional( const.CONF_ICON_TOMORROW, handler.options, const.DEFAULT_ICON_TOMORROW ): selector.IconSelector(), optional(const.CONF_EXPIRE_AFTER, handler.options): selector.TimeSelector(), optional( const.CONF_VERBOSE_STATE, handler.options, const.DEFAULT_VERBOSE_STATE ): bool, optional(ATTR_HIDDEN, handler.options, False): bool, optional(const.CONF_MANUAL, handler.options, False): bool, optional( const.CONF_MOVE_COUNTRY_HOLIDAYS, handler.options, const.DEFAULT_HOLIDAY_IN_WEEK_MOVE, ): bool, optional( const.CONF_HOLIDAY_DATES, handler.options, const.DEFAULT_HOLIDAY_DATES, ): selector.TextSelector(), } ) async def general_options_schema( handler: SchemaConfigFlowHandler | SchemaOptionsFlowHandler, ) -> vol.Schema: """Generate options schema.""" return vol.Schema( { required( const.CONF_FREQUENCY, handler.options, const.DEFAULT_FREQUENCY ): selector.SelectSelector( selector.SelectSelectorConfig(options=const.FREQUENCY_OPTIONS) ), optional( const.CONF_ICON_NORMAL, handler.options, const.DEFAULT_ICON_NORMAL ): selector.IconSelector(), optional( const.CONF_ICON_TODAY, handler.options, const.DEFAULT_ICON_TODAY ): selector.IconSelector(), optional( const.CONF_ICON_TOMORROW, handler.options, const.DEFAULT_ICON_TOMORROW ): selector.IconSelector(), optional(const.CONF_EXPIRE_AFTER, handler.options): selector.TimeSelector(), optional( const.CONF_VERBOSE_STATE, handler.options, const.DEFAULT_VERBOSE_STATE ): bool, optional(ATTR_HIDDEN, handler.options, False): bool, optional(const.CONF_MANUAL, handler.options, False): bool, optional( const.CONF_MOVE_COUNTRY_HOLIDAYS, handler.options, const.DEFAULT_HOLIDAY_IN_WEEK_MOVE, ): bool, optional( const.CONF_HOLIDAY_DATES, handler.options, const.DEFAULT_HOLIDAY_DATES, ): selector.TextSelector(), } ) async def detail_config_schema( handler: SchemaConfigFlowHandler | SchemaOptionsFlowHandler, ) -> vol.Schema: """Generate options schema.""" options_schema: Dict[vol.Optional | vol.Required, Any] = {} if handler.options[const.CONF_FREQUENCY] in const.ANNUAL_FREQUENCY: # "annual" options_schema[ required(const.CONF_DATE, handler.options) ] = selector.TextSelector() elif handler.options[const.CONF_FREQUENCY] in const.GROUP_FREQUENCY: # "group" options_schema[ required(CONF_ENTITIES, handler.options) ] = selector.EntitySelector( selector.EntitySelectorConfig( domain="sensor", integration=const.DOMAIN, multiple=True ), ) elif handler.options[const.CONF_FREQUENCY] not in const.BLANK_FREQUENCY: # everything else except "blank" and every-n-days if handler.options[const.CONF_FREQUENCY] not in const.DAILY_FREQUENCY: options_schema[ required(const.CONF_COLLECTION_DAYS, handler.options) ] = selector.SelectSelector( selector.SelectSelectorConfig( options=const.WEEKDAY_OPTIONS, multiple=True, mode=selector.SelectSelectorMode.LIST, ) ) # everything else except "blank" options_schema[ optional(const.CONF_FIRST_MONTH, handler.options, const.DEFAULT_FIRST_MONTH) ] = selector.SelectSelector( selector.SelectSelectorConfig(options=const.MONTH_OPTIONS) ) options_schema[ optional(const.CONF_LAST_MONTH, handler.options, const.DEFAULT_LAST_MONTH) ] = selector.SelectSelector( selector.SelectSelectorConfig(options=const.MONTH_OPTIONS) ) if handler.options[const.CONF_FREQUENCY] in const.MONTHLY_FREQUENCY: # "monthly" options_schema[ optional(const.CONF_WEEKDAY_ORDER_NUMBER, handler.options) ] = selector.SelectSelector( selector.SelectSelectorConfig( options=const.ORDER_OPTIONS, multiple=True, mode=selector.SelectSelectorMode.LIST, ) ) options_schema[ optional(const.CONF_FORCE_WEEK_NUMBERS, handler.options) ] = selector.BooleanSelector() if handler.options[const.CONF_FREQUENCY] in const.WEEKLY_DAILY_MONTHLY: # "every-n-weeks", "every-n-days", "monthly" uom = {"every-n-weeks": "weeks", "every-n-days": "days", "monthly": "month"} options_schema[ required(const.CONF_PERIOD, handler.options) ] = selector.NumberSelector( selector.NumberSelectorConfig( min=1, max=1000, mode=selector.NumberSelectorMode.BOX, unit_of_measurement=uom[handler.options[const.CONF_FREQUENCY]], ) ) if handler.options[const.CONF_FREQUENCY] in const.WEEKLY_FREQUENCY_X: # every-n-weeks options_schema[ required( const.CONF_FIRST_WEEK, handler.options, const.DEFAULT_FIRST_WEEK ) ] = selector.NumberSelector( selector.NumberSelectorConfig( min=1, max=52, mode=selector.NumberSelectorMode.BOX, unit_of_measurement="weeks", ) ) if handler.options[const.CONF_FREQUENCY] in const.DAILY_FREQUENCY: # every-n-days options_schema[ required(const.CONF_FIRST_DATE, handler.options, helpers.now().date()) ] = selector.DateSelector() if handler.options.get(const.CONF_VERBOSE_STATE, False): # "verbose_state" options_schema[ required( const.CONF_VERBOSE_FORMAT, handler.options, const.DEFAULT_VERBOSE_FORMAT ) ] = selector.TextSelector() options_schema[ required(const.CONF_DATE_FORMAT, handler.options, const.DEFAULT_DATE_FORMAT) ] = selector.TextSelector() return vol.Schema(options_schema) async def choose_details_step(_: dict[str, Any]) -> str: """Return next step_id for options flow.""" return "detail" CONFIG_FLOW: Dict[str, SchemaFlowFormStep | SchemaFlowMenuStep] = { "user": SchemaFlowFormStep(general_config_schema, next_step=choose_details_step), "detail": SchemaFlowFormStep( detail_config_schema, validate_user_input=_validate_config ), } OPTIONS_FLOW: Dict[str, SchemaFlowFormStep | SchemaFlowMenuStep] = { "init": SchemaFlowFormStep(general_options_schema, next_step=choose_details_step), "detail": SchemaFlowFormStep( detail_config_schema, validate_user_input=_validate_config ), } # mypy: ignore-errors class GarbageCollectionConfigFlowHandler(SchemaConfigFlowHandler, domain=const.DOMAIN): """Handle a config or options flow for GarbageCollection.""" config_flow = CONFIG_FLOW options_flow = OPTIONS_FLOW VERSION = const.CONFIG_VERSION @callback def async_config_entry_title(self, options: Mapping[str, Any]) -> str: """Return config entry title. The options parameter contains config entry options, which is the union of user input from the config flow steps. """ return cast(str, options["name"]) if "name" in options else ""