diff --git a/custom_components/versatile_thermostat/config_flow.py b/custom_components/versatile_thermostat/config_flow.py index de67f6b8..28a885c9 100644 --- a/custom_components/versatile_thermostat/config_flow.py +++ b/custom_components/versatile_thermostat/config_flow.py @@ -235,6 +235,14 @@ async def validate_input(self, data: dict) -> None: except ServiceConfigurationError as err: raise ServiceConfigurationError(conf) from err + motion_presets = [CONF_MOTION_PRESET, CONF_NO_MOTION_PRESET] + for conf in motion_presets: + preset = data.get(CONF_MOTION_PRESET, None) + if preset and ( + preset not in data.get(CONF_ACTIVE_PRESETS, CONF_PRESETS_SELECTIONABLE) + ): + raise InvalidPresets(conf) + def check_config_complete(self, infos) -> bool: """True if the config is now complete (ie all mandatory attributes are set)""" is_central_config = ( @@ -424,12 +432,7 @@ async def async_step_menu(self, user_input: dict | None = None) -> FlowResult: ): menu_options.append("tpi") - if self._infos[CONF_THERMOSTAT_TYPE] in [ - CONF_THERMOSTAT_SWITCH, - CONF_THERMOSTAT_VALVE, - CONF_THERMOSTAT_CLIMATE, - ]: - menu_options.append("presets") + menu_options.append("presets") if ( is_central_config @@ -606,14 +609,31 @@ async def async_step_presets(self, user_input: dict | None = None) -> FlowResult next_step = self.async_step_menu # advanced schema = STEP_PRESETS_DATA_SCHEMA + if user_input is not None and not user_input.get( + CONF_USE_PRESETS_CENTRAL_CONFIG, False + ): + next_step = self.async_step_presets_select # In Central config -> display the next step immedialty if self._infos[CONF_THERMOSTAT_TYPE] == CONF_THERMOSTAT_CENTRAL_CONFIG: # Call directly the next step, we have nothing to display here - return await self.async_step_window() # = self.async_step_window + return await self.async_step_presets_select() # = self.async_step_window return await self.generic_step("presets", schema, user_input, next_step) + async def async_step_presets_select( + self, user_input: dict | None = None + ) -> FlowResult: + """Handle disabling presets""" + _LOGGER.debug( + "Into ConfigFlow.async_step_presets_select user_input=%s", user_input + ) + + next_step = self.async_step_menu # advanced + schema = STEP_PRESETS_SELECT_SCHEMA + + return await self.generic_step("presets_select", schema, user_input, next_step) + async def async_step_window(self, user_input: dict | None = None) -> FlowResult: """Handle the window sensor flow steps""" _LOGGER.debug("Into ConfigFlow.async_step_window user_input=%s", user_input) diff --git a/custom_components/versatile_thermostat/config_schema.py b/custom_components/versatile_thermostat/config_schema.py index 4c7b2959..dc0e1a98 100644 --- a/custom_components/versatile_thermostat/config_schema.py +++ b/custom_components/versatile_thermostat/config_schema.py @@ -216,6 +216,17 @@ } ) +STEP_PRESETS_SELECT_SCHEMA = vol.Schema( # pylint: disable=invalid-name + { + vol.Required( + CONF_ACTIVE_PRESETS, default=CONF_PRESETS_SELECTIONABLE + ): selector.SelectSelector( + selector.SelectSelectorConfig( + options=CONF_PRESETS_SELECTIONABLE, multiple=True, mode="list" + ) + ) + } +) STEP_WINDOW_DATA_SCHEMA = vol.Schema( # pylint: disable=invalid-name { diff --git a/custom_components/versatile_thermostat/const.py b/custom_components/versatile_thermostat/const.py index 44f48380..8ead44a2 100644 --- a/custom_components/versatile_thermostat/const.py +++ b/custom_components/versatile_thermostat/const.py @@ -115,6 +115,7 @@ CONF_AUTO_FAN_HIGH = "auto_fan_high" CONF_AUTO_FAN_TURBO = "auto_fan_turbo" CONF_STEP_TEMPERATURE = "step_temperature" +CONF_ACTIVE_PRESETS = "active_presets" # Deprecated CONF_HEATER = "heater_entity_id" @@ -164,7 +165,7 @@ ] # For explicit typing purpose only -TYPE_AUTO_START_STOP_LEVELS = Literal[ # pylint: disable=invalid-name +TYPE_AUTO_START_STOP_LEVELS = [ # pylint: disable=invalid-name AUTO_START_STOP_LEVEL_FAST, AUTO_START_STOP_LEVEL_MEDIUM, AUTO_START_STOP_LEVEL_SLOW, @@ -175,7 +176,7 @@ HVAC_OFF_REASON_MANUAL = "manual" HVAC_OFF_REASON_AUTO_START_STOP = "auto_start_stop" HVAC_OFF_REASON_WINDOW_DETECTION = "window_detection" -HVAC_OFF_REASONS = Literal[ # pylint: disable=invalid-name +HVAC_OFF_REASONS = [ # pylint: disable=invalid-name HVAC_OFF_REASON_MANUAL, HVAC_OFF_REASON_AUTO_START_STOP, HVAC_OFF_REASON_WINDOW_DETECTION, @@ -500,6 +501,8 @@ class ServiceConfigurationError(HomeAssistantError): class ConfigurationNotCompleteError(HomeAssistantError): """Error the configuration is not complete""" +class InvalidPresets(HomeAssistantError): + """Error a disabled preset has been selected""" class overrides: # pylint: disable=invalid-name """An annotation to inform overrides""" diff --git a/custom_components/versatile_thermostat/number.py b/custom_components/versatile_thermostat/number.py index af9b169f..15bea42e 100644 --- a/custom_components/versatile_thermostat/number.py +++ b/custom_components/versatile_thermostat/number.py @@ -31,6 +31,8 @@ from .commons import VersatileThermostatBaseEntity from .const import ( + CONF_ACTIVE_PRESETS, + CONF_PRESETS_SELECTIONABLE, DOMAIN, DEVICE_MANUFACTURER, CONF_NAME, @@ -40,6 +42,7 @@ CONF_TEMP_MAX, CONF_STEP_TEMPERATURE, CONF_AC_MODE, + PRESET_AC_SUFFIX, PRESET_FROST_PROTECTION, PRESET_ECO_AC, PRESET_COMFORT_AC, @@ -98,12 +101,20 @@ async def async_setup_entry( # is_central_boiler = entry.data.get(CONF_USE_CENTRAL_BOILER_FEATURE) entities = [] - + presets = entry.data.get(CONF_ACTIVE_PRESETS, CONF_PRESETS_SELECTIONABLE) + _LOGGER.debug(presets) + ac_presets = presets.copy() + ac_presets.remove(PRESET_FROST_PROTECTION) + _LOGGER.debug(ac_presets) + ac_presets = [ac_preset + PRESET_AC_SUFFIX for ac_preset in ac_presets] + _LOGGER.debug(presets) + ac_presets.extend(presets) + _LOGGER.debug(presets) if vt_type != CONF_THERMOSTAT_CENTRAL_CONFIG: # Creates non central temperature entities if not entry.data.get(CONF_USE_PRESETS_CENTRAL_CONFIG, False): if entry.data.get(CONF_AC_MODE, False): - for preset in CONF_PRESETS_WITH_AC_VALUES: + for preset in ac_presets: _LOGGER.debug( "%s - configuring Number non central, AC, non AWAY for preset %s", name, @@ -111,11 +122,17 @@ async def async_setup_entry( ) entities.append( TemperatureNumber( - hass, unique_id, name, preset, True, False, entry.data + hass, + unique_id, + name, + preset + PRESET_TEMP_SUFFIX, + True, + False, + entry.data, ) ) else: - for preset in CONF_PRESETS_VALUES: + for preset in presets: _LOGGER.debug( "%s - configuring Number non central, non AC, non AWAY for preset %s", name, @@ -123,7 +140,13 @@ async def async_setup_entry( ) entities.append( TemperatureNumber( - hass, unique_id, name, preset, False, False, entry.data + hass, + unique_id, + name, + preset + PRESET_TEMP_SUFFIX, + False, + False, + entry.data, ) ) @@ -131,7 +154,7 @@ async def async_setup_entry( CONF_USE_PRESENCE_FEATURE, False ) is True and not entry.data.get(CONF_USE_PRESENCE_CENTRAL_CONFIG, False): if entry.data.get(CONF_AC_MODE, False): - for preset in CONF_PRESETS_AWAY_WITH_AC_VALUES: + for preset in ac_presets: _LOGGER.debug( "%s - configuring Number non central, AC, AWAY for preset %s", name, @@ -139,11 +162,17 @@ async def async_setup_entry( ) entities.append( TemperatureNumber( - hass, unique_id, name, preset, True, True, entry.data + hass, + unique_id, + name, + preset + PRESET_AWAY_SUFFIX + PRESET_TEMP_SUFFIX, + True, + True, + entry.data, ) ) else: - for preset in CONF_PRESETS_AWAY_VALUES: + for preset in presets: _LOGGER.debug( "%s - configuring Number non central, non AC, AWAY for preset %s", name, @@ -151,7 +180,13 @@ async def async_setup_entry( ) entities.append( TemperatureNumber( - hass, unique_id, name, preset, False, True, entry.data + hass, + unique_id, + name, + preset + PRESET_AWAY_SUFFIX + PRESET_TEMP_SUFFIX, + False, + True, + entry.data, ) ) @@ -161,7 +196,7 @@ async def async_setup_entry( entities.append( ActivateBoilerThresholdNumber(hass, unique_id, name, entry.data) ) - for preset in CONF_PRESETS_WITH_AC_VALUES: + for preset in ac_presets: _LOGGER.debug( "%s - configuring Number central, AC, non AWAY for preset %s", name, @@ -169,17 +204,27 @@ async def async_setup_entry( ) entities.append( CentralConfigTemperatureNumber( - hass, unique_id, name, preset, True, False, entry.data + hass, + unique_id, + name, + preset + PRESET_TEMP_SUFFIX, + True, + False, + entry.data, ) ) - - for preset in CONF_PRESETS_AWAY_WITH_AC_VALUES: _LOGGER.debug( "%s - configuring Number central, AC, AWAY for preset %s", name, preset ) entities.append( CentralConfigTemperatureNumber( - hass, unique_id, name, preset, True, True, entry.data + hass, + unique_id, + name, + preset + PRESET_AWAY_SUFFIX + PRESET_TEMP_SUFFIX, + True, + True, + entry.data, ) )