from __future__ import annotations from typing import TYPE_CHECKING, Any from homeassistant.config_entries import ConfigFlowResult from homeassistant.const import ( CONF_NAME, CONF_UNIQUE_ID, CONF_UNIT_OF_MEASUREMENT, UnitOfEnergy, UnitOfPower, UnitOfTime, ) from homeassistant.helpers import selector from homeassistant.helpers.schema_config_entry_flow import SchemaFlowError import voluptuous as vol from custom_components.powercalc.const import ( CONF_CREATE_UTILITY_METERS, CONF_DAILY_ENERGY_VALUE, CONF_DAILY_FIXED_ENERGY, CONF_GROUP, CONF_ON_TIME, CONF_UPDATE_FREQUENCY, CONF_VALUE, CONF_VALUE_TEMPLATE, SensorType, ) from custom_components.powercalc.flow_helper.common import ( PowercalcFormStep, Step, fill_schema_defaults, unwrap_choose_selector, wrap_choose_selector, ) from custom_components.powercalc.flow_helper.schema import SCHEMA_UTILITY_METER_TOGGLE from custom_components.powercalc.sensors.daily_energy import DEFAULT_DAILY_UPDATE_FREQUENCY if TYPE_CHECKING: from custom_components.powercalc.config_flow import PowercalcConfigFlow, PowercalcOptionsFlow DAILY_ENERGY_VALUE_CHOICES: dict[str, list[str] | str] = { CONF_VALUE_TEMPLATE: CONF_VALUE_TEMPLATE, CONF_VALUE: CONF_VALUE, } SCHEMA_DAILY_ENERGY_OPTIONS = vol.Schema( { vol.Optional(CONF_DAILY_ENERGY_VALUE): selector.ChooseSelector( selector.ChooseSelectorConfig( choices={ CONF_VALUE: {"selector": {"number": {"mode": "box", "step": "any"}}}, CONF_VALUE_TEMPLATE: {"selector": {"template": {}}}, }, translation_key=CONF_DAILY_ENERGY_VALUE, ), ), vol.Optional( CONF_UNIT_OF_MEASUREMENT, default=UnitOfEnergy.KILO_WATT_HOUR, ): vol.In( [UnitOfEnergy.KILO_WATT_HOUR, UnitOfPower.WATT], ), vol.Optional(CONF_ON_TIME): selector.DurationSelector( selector.DurationSelectorConfig(enable_day=False), ), vol.Optional( CONF_UPDATE_FREQUENCY, default=DEFAULT_DAILY_UPDATE_FREQUENCY, ): selector.NumberSelector( selector.NumberSelectorConfig( min=10, unit_of_measurement=UnitOfTime.SECONDS, mode=selector.NumberSelectorMode.BOX, ), ), }, ) SCHEMA_DAILY_ENERGY = vol.Schema( { vol.Required(CONF_NAME): selector.TextSelector(), **SCHEMA_UTILITY_METER_TOGGLE.schema, }, ).extend(SCHEMA_DAILY_ENERGY_OPTIONS.schema) def daily_energy_choice_key_from_validated_value(value: object) -> str: """Infer the daily energy config key from a validated ChooseSelector value.""" return CONF_VALUE_TEMPLATE if isinstance(value, str) else CONF_VALUE def build_daily_energy_config(user_input: dict[str, Any], schema: vol.Schema) -> dict[str, Any]: """Build the config under daily_energy: key.""" user_input = unwrap_choose_selector( dict(user_input), CONF_DAILY_ENERGY_VALUE, daily_energy_choice_key_from_validated_value, ) config: dict[str, Any] = { CONF_DAILY_FIXED_ENERGY: {}, } schema_keys = {key.schema if isinstance(key, vol.Marker) else key for key in schema.schema} schema_keys.discard(CONF_DAILY_ENERGY_VALUE) schema_keys |= {CONF_VALUE, CONF_VALUE_TEMPLATE} for key, val in user_input.items(): if key in schema_keys and val is not None: if key in {CONF_CREATE_UTILITY_METERS, CONF_GROUP, CONF_NAME, CONF_UNIQUE_ID}: config[str(key)] = val continue config[CONF_DAILY_FIXED_ENERGY][str(key)] = val return config class DailyEnergyConfigFlow: def __init__(self, flow: PowercalcConfigFlow) -> None: self.flow: PowercalcConfigFlow = flow async def async_step_daily_energy( self, user_input: dict[str, Any] | None = None, ) -> ConfigFlowResult: """Handle the flow for daily energy sensor.""" self.flow.selected_sensor_type = SensorType.DAILY_ENERGY def _validate(user_input: dict[str, Any]) -> dict[str, Any]: unwrapped = unwrap_choose_selector( dict(user_input), CONF_DAILY_ENERGY_VALUE, daily_energy_choice_key_from_validated_value, ) if CONF_VALUE not in unwrapped and CONF_VALUE_TEMPLATE not in unwrapped: raise SchemaFlowError("daily_energy_mandatory") return build_daily_energy_config(unwrapped, SCHEMA_DAILY_ENERGY) return await self.flow.handle_form_step( PowercalcFormStep( step=Step.DAILY_ENERGY, schema=SCHEMA_DAILY_ENERGY, validate_user_input=_validate, next_step=Step.ASSIGN_GROUPS, ), user_input, ) class DailyEnergyOptionsFlow: def __init__(self, flow: PowercalcOptionsFlow) -> None: self.flow: PowercalcOptionsFlow = flow async def async_step_daily_energy(self, user_input: dict[str, Any] | None = None) -> ConfigFlowResult: """Handle the daily energy options flow.""" form_data = wrap_choose_selector( dict(self.flow.sensor_config[CONF_DAILY_FIXED_ENERGY]), CONF_DAILY_ENERGY_VALUE, DAILY_ENERGY_VALUE_CHOICES, ) schema = fill_schema_defaults( SCHEMA_DAILY_ENERGY_OPTIONS, form_data, ) if user_input is not None: user_input = unwrap_choose_selector( dict(user_input), CONF_DAILY_ENERGY_VALUE, daily_energy_choice_key_from_validated_value, ) return await self.flow.async_handle_options_step(user_input, schema, Step.DAILY_ENERGY)