mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-31 07:03:55 +00:00 
			
		
		
		
	[thermostat] General clean-up, optimization, properly support "auto" mode (#10561)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
This commit is contained in:
		| @@ -32,6 +32,7 @@ from esphome.const import ( | ||||
|     CONF_FAN_WITH_COOLING, | ||||
|     CONF_FAN_WITH_HEATING, | ||||
|     CONF_HEAT_ACTION, | ||||
|     CONF_HEAT_COOL_MODE, | ||||
|     CONF_HEAT_DEADBAND, | ||||
|     CONF_HEAT_MODE, | ||||
|     CONF_HEAT_OVERRUN, | ||||
| @@ -150,7 +151,7 @@ def generate_comparable_preset(config, name): | ||||
| def validate_thermostat(config): | ||||
|     # verify corresponding action(s) exist(s) for any defined climate mode or action | ||||
|     requirements = { | ||||
|         CONF_AUTO_MODE: [ | ||||
|         CONF_HEAT_COOL_MODE: [ | ||||
|             CONF_COOL_ACTION, | ||||
|             CONF_HEAT_ACTION, | ||||
|             CONF_MIN_COOLING_OFF_TIME, | ||||
| @@ -540,6 +541,9 @@ CONFIG_SCHEMA = cv.All( | ||||
|             cv.Optional(CONF_FAN_ONLY_MODE): automation.validate_automation( | ||||
|                 single=True | ||||
|             ), | ||||
|             cv.Optional(CONF_HEAT_COOL_MODE): automation.validate_automation( | ||||
|                 single=True | ||||
|             ), | ||||
|             cv.Optional(CONF_HEAT_MODE): automation.validate_automation(single=True), | ||||
|             cv.Optional(CONF_OFF_MODE): automation.validate_automation(single=True), | ||||
|             cv.Optional(CONF_FAN_MODE_ON_ACTION): automation.validate_automation( | ||||
| @@ -644,7 +648,6 @@ async def to_code(config): | ||||
|     var = await climate.new_climate(config) | ||||
|     await cg.register_component(var, config) | ||||
|  | ||||
|     heat_cool_mode_available = CONF_HEAT_ACTION in config and CONF_COOL_ACTION in config | ||||
|     two_points_available = CONF_HEAT_ACTION in config and ( | ||||
|         CONF_COOL_ACTION in config | ||||
|         or (config[CONF_FAN_ONLY_COOLING] and CONF_FAN_ONLY_ACTION in config) | ||||
| @@ -739,11 +742,6 @@ async def to_code(config): | ||||
|         var.get_idle_action_trigger(), [], config[CONF_IDLE_ACTION] | ||||
|     ) | ||||
|  | ||||
|     if heat_cool_mode_available is True: | ||||
|         cg.add(var.set_supports_heat_cool(True)) | ||||
|     else: | ||||
|         cg.add(var.set_supports_heat_cool(False)) | ||||
|  | ||||
|     if CONF_COOL_ACTION in config: | ||||
|         await automation.build_automation( | ||||
|             var.get_cool_action_trigger(), [], config[CONF_COOL_ACTION] | ||||
| @@ -780,6 +778,7 @@ async def to_code(config): | ||||
|         await automation.build_automation( | ||||
|             var.get_auto_mode_trigger(), [], config[CONF_AUTO_MODE] | ||||
|         ) | ||||
|         cg.add(var.set_supports_auto(True)) | ||||
|     if CONF_COOL_MODE in config: | ||||
|         await automation.build_automation( | ||||
|             var.get_cool_mode_trigger(), [], config[CONF_COOL_MODE] | ||||
| @@ -800,6 +799,11 @@ async def to_code(config): | ||||
|             var.get_heat_mode_trigger(), [], config[CONF_HEAT_MODE] | ||||
|         ) | ||||
|         cg.add(var.set_supports_heat(True)) | ||||
|     if CONF_HEAT_COOL_MODE in config: | ||||
|         await automation.build_automation( | ||||
|             var.get_heat_cool_mode_trigger(), [], config[CONF_HEAT_COOL_MODE] | ||||
|         ) | ||||
|         cg.add(var.set_supports_heat_cool(True)) | ||||
|     if CONF_OFF_MODE in config: | ||||
|         await automation.build_automation( | ||||
|             var.get_off_mode_trigger(), [], config[CONF_OFF_MODE] | ||||
|   | ||||
| @@ -1,4 +1,6 @@ | ||||
| #include "thermostat_climate.h" | ||||
| #include "esphome/core/application.h" | ||||
| #include "esphome/core/helpers.h" | ||||
| #include "esphome/core/log.h" | ||||
|  | ||||
| namespace esphome { | ||||
| @@ -64,7 +66,7 @@ void ThermostatClimate::setup() { | ||||
|  | ||||
| void ThermostatClimate::loop() { | ||||
|   for (auto &timer : this->timer_) { | ||||
|     if (timer.active && (timer.started + timer.time < millis())) { | ||||
|     if (timer.active && (timer.started + timer.time < App.get_loop_component_start_time())) { | ||||
|       timer.active = false; | ||||
|       timer.func(); | ||||
|     } | ||||
| @@ -127,26 +129,35 @@ bool ThermostatClimate::hysteresis_valid() { | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| bool ThermostatClimate::limit_setpoints_for_heat_cool() { | ||||
|   return this->mode == climate::CLIMATE_MODE_HEAT_COOL || | ||||
|          (this->mode == climate::CLIMATE_MODE_AUTO && this->supports_heat_cool_); | ||||
| } | ||||
|  | ||||
| void ThermostatClimate::validate_target_temperature() { | ||||
|   if (std::isnan(this->target_temperature)) { | ||||
|     // default to the midpoint between visual min and max | ||||
|     this->target_temperature = | ||||
|         ((this->get_traits().get_visual_max_temperature() - this->get_traits().get_visual_min_temperature()) / 2) + | ||||
|         this->get_traits().get_visual_min_temperature(); | ||||
|   } else { | ||||
|     // target_temperature must be between the visual minimum and the visual maximum | ||||
|     if (this->target_temperature < this->get_traits().get_visual_min_temperature()) | ||||
|       this->target_temperature = this->get_traits().get_visual_min_temperature(); | ||||
|     if (this->target_temperature > this->get_traits().get_visual_max_temperature()) | ||||
|       this->target_temperature = this->get_traits().get_visual_max_temperature(); | ||||
|     this->target_temperature = clamp(this->target_temperature, this->get_traits().get_visual_min_temperature(), | ||||
|                                      this->get_traits().get_visual_max_temperature()); | ||||
|   } | ||||
| } | ||||
|  | ||||
| void ThermostatClimate::validate_target_temperatures() { | ||||
|   if (this->supports_two_points_) { | ||||
| void ThermostatClimate::validate_target_temperatures(const bool pin_target_temperature_high) { | ||||
|   if (!this->supports_two_points_) { | ||||
|     this->validate_target_temperature(); | ||||
|   } else if (pin_target_temperature_high) { | ||||
|     // if target_temperature_high is set less than target_temperature_low, move down target_temperature_low | ||||
|     this->validate_target_temperature_low(); | ||||
|     this->validate_target_temperature_high(); | ||||
|   } else { | ||||
|     this->validate_target_temperature(); | ||||
|     // if target_temperature_low is set greater than target_temperature_high, move up target_temperature_high | ||||
|     this->validate_target_temperature_high(); | ||||
|     this->validate_target_temperature_low(); | ||||
|   } | ||||
| } | ||||
|  | ||||
| @@ -154,18 +165,13 @@ void ThermostatClimate::validate_target_temperature_low() { | ||||
|   if (std::isnan(this->target_temperature_low)) { | ||||
|     this->target_temperature_low = this->get_traits().get_visual_min_temperature(); | ||||
|   } else { | ||||
|     // target_temperature_low must not be lower than the visual minimum | ||||
|     if (this->target_temperature_low < this->get_traits().get_visual_min_temperature()) | ||||
|       this->target_temperature_low = this->get_traits().get_visual_min_temperature(); | ||||
|     // target_temperature_low must not be greater than the visual maximum minus set_point_minimum_differential_ | ||||
|     if (this->target_temperature_low > | ||||
|         this->get_traits().get_visual_max_temperature() - this->set_point_minimum_differential_) { | ||||
|       this->target_temperature_low = | ||||
|           this->get_traits().get_visual_max_temperature() - this->set_point_minimum_differential_; | ||||
|     } | ||||
|     // if target_temperature_low is set greater than target_temperature_high, move up target_temperature_high | ||||
|     if (this->target_temperature_low > this->target_temperature_high - this->set_point_minimum_differential_) | ||||
|       this->target_temperature_high = this->target_temperature_low + this->set_point_minimum_differential_; | ||||
|     float target_temperature_low_upper_limit = | ||||
|         this->limit_setpoints_for_heat_cool() | ||||
|             ? clamp(this->target_temperature_high - this->set_point_minimum_differential_, | ||||
|                     this->get_traits().get_visual_min_temperature(), this->get_traits().get_visual_max_temperature()) | ||||
|             : this->get_traits().get_visual_max_temperature(); | ||||
|     this->target_temperature_low = clamp(this->target_temperature_low, this->get_traits().get_visual_min_temperature(), | ||||
|                                          target_temperature_low_upper_limit); | ||||
|   } | ||||
| } | ||||
|  | ||||
| @@ -173,62 +179,64 @@ void ThermostatClimate::validate_target_temperature_high() { | ||||
|   if (std::isnan(this->target_temperature_high)) { | ||||
|     this->target_temperature_high = this->get_traits().get_visual_max_temperature(); | ||||
|   } else { | ||||
|     // target_temperature_high must not be lower than the visual maximum | ||||
|     if (this->target_temperature_high > this->get_traits().get_visual_max_temperature()) | ||||
|       this->target_temperature_high = this->get_traits().get_visual_max_temperature(); | ||||
|     // target_temperature_high must not be lower than the visual minimum plus set_point_minimum_differential_ | ||||
|     if (this->target_temperature_high < | ||||
|         this->get_traits().get_visual_min_temperature() + this->set_point_minimum_differential_) { | ||||
|       this->target_temperature_high = | ||||
|           this->get_traits().get_visual_min_temperature() + this->set_point_minimum_differential_; | ||||
|     } | ||||
|     // if target_temperature_high is set less than target_temperature_low, move down target_temperature_low | ||||
|     if (this->target_temperature_high < this->target_temperature_low + this->set_point_minimum_differential_) | ||||
|       this->target_temperature_low = this->target_temperature_high - this->set_point_minimum_differential_; | ||||
|     float target_temperature_high_lower_limit = | ||||
|         this->limit_setpoints_for_heat_cool() | ||||
|             ? clamp(this->target_temperature_low + this->set_point_minimum_differential_, | ||||
|                     this->get_traits().get_visual_min_temperature(), this->get_traits().get_visual_max_temperature()) | ||||
|             : this->get_traits().get_visual_min_temperature(); | ||||
|     this->target_temperature_high = clamp(this->target_temperature_high, target_temperature_high_lower_limit, | ||||
|                                           this->get_traits().get_visual_max_temperature()); | ||||
|   } | ||||
| } | ||||
|  | ||||
| void ThermostatClimate::control(const climate::ClimateCall &call) { | ||||
|   bool target_temperature_high_changed = false; | ||||
|  | ||||
|   if (call.get_preset().has_value()) { | ||||
|     // setup_complete_ blocks modifying/resetting the temps immediately after boot | ||||
|     if (this->setup_complete_) { | ||||
|       this->change_preset_(*call.get_preset()); | ||||
|       this->change_preset_(call.get_preset().value()); | ||||
|     } else { | ||||
|       this->preset = *call.get_preset(); | ||||
|       this->preset = call.get_preset().value(); | ||||
|     } | ||||
|   } | ||||
|   if (call.get_custom_preset().has_value()) { | ||||
|     // setup_complete_ blocks modifying/resetting the temps immediately after boot | ||||
|     if (this->setup_complete_) { | ||||
|       this->change_custom_preset_(*call.get_custom_preset()); | ||||
|       this->change_custom_preset_(call.get_custom_preset().value()); | ||||
|     } else { | ||||
|       this->custom_preset = *call.get_custom_preset(); | ||||
|       this->custom_preset = call.get_custom_preset().value(); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   if (call.get_mode().has_value()) | ||||
|     this->mode = *call.get_mode(); | ||||
|   if (call.get_fan_mode().has_value()) | ||||
|     this->fan_mode = *call.get_fan_mode(); | ||||
|   if (call.get_swing_mode().has_value()) | ||||
|     this->swing_mode = *call.get_swing_mode(); | ||||
|   if (call.get_mode().has_value()) { | ||||
|     this->mode = call.get_mode().value(); | ||||
|   } | ||||
|   if (call.get_fan_mode().has_value()) { | ||||
|     this->fan_mode = call.get_fan_mode().value(); | ||||
|   } | ||||
|   if (call.get_swing_mode().has_value()) { | ||||
|     this->swing_mode = call.get_swing_mode().value(); | ||||
|   } | ||||
|   if (this->supports_two_points_) { | ||||
|     if (call.get_target_temperature_low().has_value()) { | ||||
|       this->target_temperature_low = *call.get_target_temperature_low(); | ||||
|       validate_target_temperature_low(); | ||||
|       this->target_temperature_low = call.get_target_temperature_low().value(); | ||||
|     } | ||||
|     if (call.get_target_temperature_high().has_value()) { | ||||
|       this->target_temperature_high = *call.get_target_temperature_high(); | ||||
|       validate_target_temperature_high(); | ||||
|       target_temperature_high_changed = this->target_temperature_high != call.get_target_temperature_high().value(); | ||||
|       this->target_temperature_high = call.get_target_temperature_high().value(); | ||||
|     } | ||||
|     // ensure the two set points are valid and adjust one of them if necessary | ||||
|     this->validate_target_temperatures(target_temperature_high_changed || | ||||
|                                        (this->prev_mode_ == climate::CLIMATE_MODE_COOL)); | ||||
|   } else { | ||||
|     if (call.get_target_temperature().has_value()) { | ||||
|       this->target_temperature = *call.get_target_temperature(); | ||||
|       validate_target_temperature(); | ||||
|       this->target_temperature = call.get_target_temperature().value(); | ||||
|       this->validate_target_temperature(); | ||||
|     } | ||||
|   } | ||||
|   // make any changes happen | ||||
|   refresh(); | ||||
|   this->refresh(); | ||||
| } | ||||
|  | ||||
| climate::ClimateTraits ThermostatClimate::traits() { | ||||
| @@ -237,47 +245,47 @@ climate::ClimateTraits ThermostatClimate::traits() { | ||||
|   if (this->humidity_sensor_ != nullptr) | ||||
|     traits.set_supports_current_humidity(true); | ||||
|  | ||||
|   if (supports_auto_) | ||||
|   if (this->supports_auto_) | ||||
|     traits.add_supported_mode(climate::CLIMATE_MODE_AUTO); | ||||
|   if (supports_heat_cool_) | ||||
|   if (this->supports_heat_cool_) | ||||
|     traits.add_supported_mode(climate::CLIMATE_MODE_HEAT_COOL); | ||||
|   if (supports_cool_) | ||||
|   if (this->supports_cool_) | ||||
|     traits.add_supported_mode(climate::CLIMATE_MODE_COOL); | ||||
|   if (supports_dry_) | ||||
|   if (this->supports_dry_) | ||||
|     traits.add_supported_mode(climate::CLIMATE_MODE_DRY); | ||||
|   if (supports_fan_only_) | ||||
|   if (this->supports_fan_only_) | ||||
|     traits.add_supported_mode(climate::CLIMATE_MODE_FAN_ONLY); | ||||
|   if (supports_heat_) | ||||
|   if (this->supports_heat_) | ||||
|     traits.add_supported_mode(climate::CLIMATE_MODE_HEAT); | ||||
|  | ||||
|   if (supports_fan_mode_on_) | ||||
|   if (this->supports_fan_mode_on_) | ||||
|     traits.add_supported_fan_mode(climate::CLIMATE_FAN_ON); | ||||
|   if (supports_fan_mode_off_) | ||||
|   if (this->supports_fan_mode_off_) | ||||
|     traits.add_supported_fan_mode(climate::CLIMATE_FAN_OFF); | ||||
|   if (supports_fan_mode_auto_) | ||||
|   if (this->supports_fan_mode_auto_) | ||||
|     traits.add_supported_fan_mode(climate::CLIMATE_FAN_AUTO); | ||||
|   if (supports_fan_mode_low_) | ||||
|   if (this->supports_fan_mode_low_) | ||||
|     traits.add_supported_fan_mode(climate::CLIMATE_FAN_LOW); | ||||
|   if (supports_fan_mode_medium_) | ||||
|   if (this->supports_fan_mode_medium_) | ||||
|     traits.add_supported_fan_mode(climate::CLIMATE_FAN_MEDIUM); | ||||
|   if (supports_fan_mode_high_) | ||||
|   if (this->supports_fan_mode_high_) | ||||
|     traits.add_supported_fan_mode(climate::CLIMATE_FAN_HIGH); | ||||
|   if (supports_fan_mode_middle_) | ||||
|   if (this->supports_fan_mode_middle_) | ||||
|     traits.add_supported_fan_mode(climate::CLIMATE_FAN_MIDDLE); | ||||
|   if (supports_fan_mode_focus_) | ||||
|   if (this->supports_fan_mode_focus_) | ||||
|     traits.add_supported_fan_mode(climate::CLIMATE_FAN_FOCUS); | ||||
|   if (supports_fan_mode_diffuse_) | ||||
|   if (this->supports_fan_mode_diffuse_) | ||||
|     traits.add_supported_fan_mode(climate::CLIMATE_FAN_DIFFUSE); | ||||
|   if (supports_fan_mode_quiet_) | ||||
|   if (this->supports_fan_mode_quiet_) | ||||
|     traits.add_supported_fan_mode(climate::CLIMATE_FAN_QUIET); | ||||
|  | ||||
|   if (supports_swing_mode_both_) | ||||
|   if (this->supports_swing_mode_both_) | ||||
|     traits.add_supported_swing_mode(climate::CLIMATE_SWING_BOTH); | ||||
|   if (supports_swing_mode_horizontal_) | ||||
|   if (this->supports_swing_mode_horizontal_) | ||||
|     traits.add_supported_swing_mode(climate::CLIMATE_SWING_HORIZONTAL); | ||||
|   if (supports_swing_mode_off_) | ||||
|   if (this->supports_swing_mode_off_) | ||||
|     traits.add_supported_swing_mode(climate::CLIMATE_SWING_OFF); | ||||
|   if (supports_swing_mode_vertical_) | ||||
|   if (this->supports_swing_mode_vertical_) | ||||
|     traits.add_supported_swing_mode(climate::CLIMATE_SWING_VERTICAL); | ||||
|  | ||||
|   for (auto &it : this->preset_config_) { | ||||
| @@ -300,13 +308,13 @@ climate::ClimateAction ThermostatClimate::compute_action_(const bool ignore_time | ||||
|   } | ||||
|   // do not change the action if an "ON" timer is running | ||||
|   if ((!ignore_timers) && | ||||
|       (timer_active_(thermostat::TIMER_IDLE_ON) || timer_active_(thermostat::TIMER_COOLING_ON) || | ||||
|        timer_active_(thermostat::TIMER_FANNING_ON) || timer_active_(thermostat::TIMER_HEATING_ON))) { | ||||
|       (this->timer_active_(thermostat::TIMER_IDLE_ON) || this->timer_active_(thermostat::TIMER_COOLING_ON) || | ||||
|        this->timer_active_(thermostat::TIMER_FANNING_ON) || this->timer_active_(thermostat::TIMER_HEATING_ON))) { | ||||
|     return this->action; | ||||
|   } | ||||
|  | ||||
|   // ensure set point(s) is/are valid before computing the action | ||||
|   this->validate_target_temperatures(); | ||||
|   this->validate_target_temperatures(this->prev_mode_ == climate::CLIMATE_MODE_COOL); | ||||
|   // everything has been validated so we can now safely compute the action | ||||
|   switch (this->mode) { | ||||
|     // if the climate mode is OFF then the climate action must be OFF | ||||
| @@ -340,6 +348,22 @@ climate::ClimateAction ThermostatClimate::compute_action_(const bool ignore_time | ||||
|         target_action = climate::CLIMATE_ACTION_HEATING; | ||||
|       } | ||||
|       break; | ||||
|     case climate::CLIMATE_MODE_AUTO: | ||||
|       if (this->supports_two_points_) { | ||||
|         if (this->cooling_required_() && this->heating_required_()) { | ||||
|           // this is bad and should never happen, so just stop. | ||||
|           // target_action = climate::CLIMATE_ACTION_IDLE; | ||||
|         } else if (this->cooling_required_()) { | ||||
|           target_action = climate::CLIMATE_ACTION_COOLING; | ||||
|         } else if (this->heating_required_()) { | ||||
|           target_action = climate::CLIMATE_ACTION_HEATING; | ||||
|         } | ||||
|       } else if (this->supports_cool_ && this->cooling_required_()) { | ||||
|         target_action = climate::CLIMATE_ACTION_COOLING; | ||||
|       } else if (this->supports_heat_ && this->heating_required_()) { | ||||
|         target_action = climate::CLIMATE_ACTION_HEATING; | ||||
|       } | ||||
|       break; | ||||
|     default: | ||||
|       break; | ||||
|   } | ||||
| @@ -362,7 +386,7 @@ climate::ClimateAction ThermostatClimate::compute_supplemental_action_() { | ||||
|   } | ||||
|  | ||||
|   // ensure set point(s) is/are valid before computing the action | ||||
|   this->validate_target_temperatures(); | ||||
|   this->validate_target_temperatures(this->prev_mode_ == climate::CLIMATE_MODE_COOL); | ||||
|   // everything has been validated so we can now safely compute the action | ||||
|   switch (this->mode) { | ||||
|     // if the climate mode is OFF then the climate action must be OFF | ||||
| @@ -653,13 +677,13 @@ void ThermostatClimate::switch_to_mode_(climate::ClimateMode mode, bool publish_ | ||||
|     this->prev_mode_trigger_->stop_action(); | ||||
|     this->prev_mode_trigger_ = nullptr; | ||||
|   } | ||||
|   Trigger<> *trig = this->auto_mode_trigger_; | ||||
|   Trigger<> *trig = this->off_mode_trigger_; | ||||
|   switch (mode) { | ||||
|     case climate::CLIMATE_MODE_OFF: | ||||
|       trig = this->off_mode_trigger_; | ||||
|     case climate::CLIMATE_MODE_AUTO: | ||||
|       trig = this->auto_mode_trigger_; | ||||
|       break; | ||||
|     case climate::CLIMATE_MODE_HEAT_COOL: | ||||
|       // trig = this->auto_mode_trigger_; | ||||
|       trig = this->heat_cool_mode_trigger_; | ||||
|       break; | ||||
|     case climate::CLIMATE_MODE_COOL: | ||||
|       trig = this->cool_mode_trigger_; | ||||
| @@ -673,11 +697,12 @@ void ThermostatClimate::switch_to_mode_(climate::ClimateMode mode, bool publish_ | ||||
|     case climate::CLIMATE_MODE_DRY: | ||||
|       trig = this->dry_mode_trigger_; | ||||
|       break; | ||||
|     case climate::CLIMATE_MODE_OFF: | ||||
|     default: | ||||
|       // we cannot report an invalid mode back to HA (even if it asked for one) | ||||
|       //  and must assume some valid value | ||||
|       mode = climate::CLIMATE_MODE_HEAT_COOL; | ||||
|       // trig = this->auto_mode_trigger_; | ||||
|       mode = climate::CLIMATE_MODE_OFF; | ||||
|       // trig = this->off_mode_trigger_; | ||||
|   } | ||||
|   if (trig != nullptr) { | ||||
|     trig->trigger(); | ||||
| @@ -685,8 +710,9 @@ void ThermostatClimate::switch_to_mode_(climate::ClimateMode mode, bool publish_ | ||||
|   this->mode = mode; | ||||
|   this->prev_mode_ = mode; | ||||
|   this->prev_mode_trigger_ = trig; | ||||
|   if (publish_state) | ||||
|   if (publish_state) { | ||||
|     this->publish_state(); | ||||
|   } | ||||
| } | ||||
|  | ||||
| void ThermostatClimate::switch_to_swing_mode_(climate::ClimateSwingMode swing_mode, bool publish_state) { | ||||
| @@ -958,37 +984,25 @@ bool ThermostatClimate::supplemental_heating_required_() { | ||||
|           (this->supplemental_action_ == climate::CLIMATE_ACTION_HEATING)); | ||||
| } | ||||
|  | ||||
| void ThermostatClimate::dump_preset_config_(const char *preset_name, const ThermostatClimateTargetTempConfig &config, | ||||
|                                             bool is_default_preset) { | ||||
|   ESP_LOGCONFIG(TAG, "      %s Is Default: %s", preset_name, YESNO(is_default_preset)); | ||||
|  | ||||
| void ThermostatClimate::dump_preset_config_(const char *preset_name, const ThermostatClimateTargetTempConfig &config) { | ||||
|   if (this->supports_heat_) { | ||||
|     if (this->supports_two_points_) { | ||||
|       ESP_LOGCONFIG(TAG, "      %s Default Target Temperature Low: %.1f°C", preset_name, | ||||
|                     config.default_temperature_low); | ||||
|     } else { | ||||
|       ESP_LOGCONFIG(TAG, "      %s Default Target Temperature Low: %.1f°C", preset_name, config.default_temperature); | ||||
|     } | ||||
|     ESP_LOGCONFIG(TAG, "      Default Target Temperature Low: %.1f°C", | ||||
|                   this->supports_two_points_ ? config.default_temperature_low : config.default_temperature); | ||||
|   } | ||||
|   if ((this->supports_cool_) || (this->supports_fan_only_)) { | ||||
|     if (this->supports_two_points_) { | ||||
|       ESP_LOGCONFIG(TAG, "      %s Default Target Temperature High: %.1f°C", preset_name, | ||||
|                     config.default_temperature_high); | ||||
|     } else { | ||||
|       ESP_LOGCONFIG(TAG, "      %s Default Target Temperature High: %.1f°C", preset_name, config.default_temperature); | ||||
|     } | ||||
|     ESP_LOGCONFIG(TAG, "      Default Target Temperature High: %.1f°C", | ||||
|                   this->supports_two_points_ ? config.default_temperature_high : config.default_temperature); | ||||
|   } | ||||
|  | ||||
|   if (config.mode_.has_value()) { | ||||
|     ESP_LOGCONFIG(TAG, "      %s Default Mode: %s", preset_name, | ||||
|                   LOG_STR_ARG(climate::climate_mode_to_string(*config.mode_))); | ||||
|     ESP_LOGCONFIG(TAG, "      Default Mode: %s", LOG_STR_ARG(climate::climate_mode_to_string(*config.mode_))); | ||||
|   } | ||||
|   if (config.fan_mode_.has_value()) { | ||||
|     ESP_LOGCONFIG(TAG, "      %s Default Fan Mode: %s", preset_name, | ||||
|     ESP_LOGCONFIG(TAG, "      Default Fan Mode: %s", | ||||
|                   LOG_STR_ARG(climate::climate_fan_mode_to_string(*config.fan_mode_))); | ||||
|   } | ||||
|   if (config.swing_mode_.has_value()) { | ||||
|     ESP_LOGCONFIG(TAG, "      %s Default Swing Mode: %s", preset_name, | ||||
|     ESP_LOGCONFIG(TAG, "      Default Swing Mode: %s", | ||||
|                   LOG_STR_ARG(climate::climate_swing_mode_to_string(*config.swing_mode_))); | ||||
|   } | ||||
| } | ||||
| @@ -1106,6 +1120,7 @@ ThermostatClimate::ThermostatClimate() | ||||
|       heat_action_trigger_(new Trigger<>()), | ||||
|       supplemental_heat_action_trigger_(new Trigger<>()), | ||||
|       heat_mode_trigger_(new Trigger<>()), | ||||
|       heat_cool_mode_trigger_(new Trigger<>()), | ||||
|       auto_mode_trigger_(new Trigger<>()), | ||||
|       idle_action_trigger_(new Trigger<>()), | ||||
|       off_mode_trigger_(new Trigger<>()), | ||||
| @@ -1274,6 +1289,7 @@ Trigger<> *ThermostatClimate::get_cool_mode_trigger() const { return this->cool_ | ||||
| Trigger<> *ThermostatClimate::get_dry_mode_trigger() const { return this->dry_mode_trigger_; } | ||||
| Trigger<> *ThermostatClimate::get_fan_only_mode_trigger() const { return this->fan_only_mode_trigger_; } | ||||
| Trigger<> *ThermostatClimate::get_heat_mode_trigger() const { return this->heat_mode_trigger_; } | ||||
| Trigger<> *ThermostatClimate::get_heat_cool_mode_trigger() const { return this->heat_cool_mode_trigger_; } | ||||
| Trigger<> *ThermostatClimate::get_off_mode_trigger() const { return this->off_mode_trigger_; } | ||||
| Trigger<> *ThermostatClimate::get_fan_mode_on_trigger() const { return this->fan_mode_on_trigger_; } | ||||
| Trigger<> *ThermostatClimate::get_fan_mode_off_trigger() const { return this->fan_mode_off_trigger_; } | ||||
| @@ -1295,52 +1311,55 @@ Trigger<> *ThermostatClimate::get_preset_change_trigger() const { return this->p | ||||
| void ThermostatClimate::dump_config() { | ||||
|   LOG_CLIMATE("", "Thermostat", this); | ||||
|  | ||||
|   ESP_LOGCONFIG(TAG, | ||||
|                 "  On boot, restore from: %s\n" | ||||
|                 "  Use Start-up Delay: %s", | ||||
|                 this->on_boot_restore_from_ == thermostat::DEFAULT_PRESET ? "DEFAULT_PRESET" : "MEMORY", | ||||
|                 YESNO(this->use_startup_delay_)); | ||||
|   if (this->supports_two_points_) { | ||||
|     ESP_LOGCONFIG(TAG, "  Minimum Set Point Differential: %.1f°C", this->set_point_minimum_differential_); | ||||
|   } | ||||
|   ESP_LOGCONFIG(TAG, "  Use Start-up Delay: %s", YESNO(this->use_startup_delay_)); | ||||
|   if (this->supports_cool_) { | ||||
|     ESP_LOGCONFIG(TAG, | ||||
|                   "  Cooling Parameters:\n" | ||||
|                   "    Deadband: %.1f°C\n" | ||||
|                   "    Overrun: %.1f°C", | ||||
|                   this->cooling_deadband_, this->cooling_overrun_); | ||||
|     if ((this->supplemental_cool_delta_ > 0) || (this->timer_duration_(thermostat::TIMER_COOLING_MAX_RUN_TIME) > 0)) { | ||||
|       ESP_LOGCONFIG(TAG, | ||||
|                     "    Supplemental Delta: %.1f°C\n" | ||||
|                     "    Maximum Run Time: %" PRIu32 "s", | ||||
|                     this->supplemental_cool_delta_, | ||||
|                     this->timer_duration_(thermostat::TIMER_COOLING_MAX_RUN_TIME) / 1000); | ||||
|     } | ||||
|     ESP_LOGCONFIG(TAG, | ||||
|                   "    Overrun: %.1f°C\n" | ||||
|                   "    Minimum Off Time: %" PRIu32 "s\n" | ||||
|                   "    Minimum Run Time: %" PRIu32 "s", | ||||
|                   this->cooling_deadband_, this->cooling_overrun_, | ||||
|                   this->timer_duration_(thermostat::TIMER_COOLING_OFF) / 1000, | ||||
|                   this->timer_duration_(thermostat::TIMER_COOLING_ON) / 1000); | ||||
|     if ((this->supplemental_cool_delta_ > 0) || (this->timer_duration_(thermostat::TIMER_COOLING_MAX_RUN_TIME) > 0)) { | ||||
|       ESP_LOGCONFIG(TAG, | ||||
|                     "    Maximum Run Time: %" PRIu32 "s\n" | ||||
|                     "    Supplemental Delta: %.1f°C", | ||||
|                     this->timer_duration_(thermostat::TIMER_COOLING_MAX_RUN_TIME) / 1000, | ||||
|                     this->supplemental_cool_delta_); | ||||
|     } | ||||
|   } | ||||
|   if (this->supports_heat_) { | ||||
|     ESP_LOGCONFIG(TAG, | ||||
|                   "  Heating Parameters:\n" | ||||
|                   "    Deadband: %.1f°C\n" | ||||
|                   "    Overrun: %.1f°C", | ||||
|                   this->heating_deadband_, this->heating_overrun_); | ||||
|     if ((this->supplemental_heat_delta_ > 0) || (this->timer_duration_(thermostat::TIMER_HEATING_MAX_RUN_TIME) > 0)) { | ||||
|       ESP_LOGCONFIG(TAG, | ||||
|                     "    Supplemental Delta: %.1f°C\n" | ||||
|                     "    Maximum Run Time: %" PRIu32 "s", | ||||
|                     this->supplemental_heat_delta_, | ||||
|                     this->timer_duration_(thermostat::TIMER_HEATING_MAX_RUN_TIME) / 1000); | ||||
|     } | ||||
|     ESP_LOGCONFIG(TAG, | ||||
|                   "    Overrun: %.1f°C\n" | ||||
|                   "    Minimum Off Time: %" PRIu32 "s\n" | ||||
|                   "    Minimum Run Time: %" PRIu32 "s", | ||||
|                   this->heating_deadband_, this->heating_overrun_, | ||||
|                   this->timer_duration_(thermostat::TIMER_HEATING_OFF) / 1000, | ||||
|                   this->timer_duration_(thermostat::TIMER_HEATING_ON) / 1000); | ||||
|     if ((this->supplemental_heat_delta_ > 0) || (this->timer_duration_(thermostat::TIMER_HEATING_MAX_RUN_TIME) > 0)) { | ||||
|       ESP_LOGCONFIG(TAG, | ||||
|                     "    Maximum Run Time: %" PRIu32 "s\n" | ||||
|                     "    Supplemental Delta: %.1f°C", | ||||
|                     this->timer_duration_(thermostat::TIMER_HEATING_MAX_RUN_TIME) / 1000, | ||||
|                     this->supplemental_heat_delta_); | ||||
|     } | ||||
|   } | ||||
|   if (this->supports_fan_only_) { | ||||
|     ESP_LOGCONFIG(TAG, | ||||
|                   "  Fanning Minimum Off Time: %" PRIu32 "s\n" | ||||
|                   "  Fanning Minimum Run Time: %" PRIu32 "s", | ||||
|                   "  Fan Parameters:\n" | ||||
|                   "    Minimum Off Time: %" PRIu32 "s\n" | ||||
|                   "    Minimum Run Time: %" PRIu32 "s", | ||||
|                   this->timer_duration_(thermostat::TIMER_FANNING_OFF) / 1000, | ||||
|                   this->timer_duration_(thermostat::TIMER_FANNING_ON) / 1000); | ||||
|   } | ||||
| @@ -1351,8 +1370,8 @@ void ThermostatClimate::dump_config() { | ||||
|     ESP_LOGCONFIG(TAG, "  Minimum Fan Mode Switching Time: %" PRIu32 "s", | ||||
|                   this->timer_duration_(thermostat::TIMER_FAN_MODE) / 1000); | ||||
|   } | ||||
|   ESP_LOGCONFIG(TAG, "  Minimum Idle Time: %" PRIu32 "s", this->timer_[thermostat::TIMER_IDLE_ON].time / 1000); | ||||
|   ESP_LOGCONFIG(TAG, | ||||
|                 "  Minimum Idle Time: %" PRIu32 "s\n" | ||||
|                 "  Supported MODES:\n" | ||||
|                 "    AUTO: %s\n" | ||||
|                 "    HEAT/COOL: %s\n" | ||||
| @@ -1362,8 +1381,9 @@ void ThermostatClimate::dump_config() { | ||||
|                 "    FAN_ONLY: %s\n" | ||||
|                 "    FAN_ONLY_ACTION_USES_FAN_MODE_TIMER: %s\n" | ||||
|                 "    FAN_ONLY_COOLING: %s", | ||||
|                 YESNO(this->supports_auto_), YESNO(this->supports_heat_cool_), YESNO(this->supports_heat_), | ||||
|                 YESNO(this->supports_cool_), YESNO(this->supports_dry_), YESNO(this->supports_fan_only_), | ||||
|                 this->timer_[thermostat::TIMER_IDLE_ON].time / 1000, YESNO(this->supports_auto_), | ||||
|                 YESNO(this->supports_heat_cool_), YESNO(this->supports_heat_), YESNO(this->supports_cool_), | ||||
|                 YESNO(this->supports_dry_), YESNO(this->supports_fan_only_), | ||||
|                 YESNO(this->supports_fan_only_action_uses_fan_mode_timer_), YESNO(this->supports_fan_only_cooling_)); | ||||
|   if (this->supports_cool_) { | ||||
|     ESP_LOGCONFIG(TAG, "    FAN_WITH_COOLING: %s", YESNO(this->supports_fan_with_cooling_)); | ||||
| @@ -1382,40 +1402,39 @@ void ThermostatClimate::dump_config() { | ||||
|                 "    MIDDLE: %s\n" | ||||
|                 "    FOCUS: %s\n" | ||||
|                 "    DIFFUSE: %s\n" | ||||
|                 "    QUIET: %s", | ||||
|                 YESNO(this->supports_fan_mode_on_), YESNO(this->supports_fan_mode_off_), | ||||
|                 YESNO(this->supports_fan_mode_auto_), YESNO(this->supports_fan_mode_low_), | ||||
|                 YESNO(this->supports_fan_mode_medium_), YESNO(this->supports_fan_mode_high_), | ||||
|                 YESNO(this->supports_fan_mode_middle_), YESNO(this->supports_fan_mode_focus_), | ||||
|                 YESNO(this->supports_fan_mode_diffuse_), YESNO(this->supports_fan_mode_quiet_)); | ||||
|   ESP_LOGCONFIG(TAG, | ||||
|                 "    QUIET: %s\n" | ||||
|                 "  Supported SWING MODES:\n" | ||||
|                 "    BOTH: %s\n" | ||||
|                 "    OFF: %s\n" | ||||
|                 "    HORIZONTAL: %s\n" | ||||
|                 "    VERTICAL: %s\n" | ||||
|                 "  Supports TWO SET POINTS: %s", | ||||
|                 YESNO(this->supports_fan_mode_on_), YESNO(this->supports_fan_mode_off_), | ||||
|                 YESNO(this->supports_fan_mode_auto_), YESNO(this->supports_fan_mode_low_), | ||||
|                 YESNO(this->supports_fan_mode_medium_), YESNO(this->supports_fan_mode_high_), | ||||
|                 YESNO(this->supports_fan_mode_middle_), YESNO(this->supports_fan_mode_focus_), | ||||
|                 YESNO(this->supports_fan_mode_diffuse_), YESNO(this->supports_fan_mode_quiet_), | ||||
|                 YESNO(this->supports_swing_mode_both_), YESNO(this->supports_swing_mode_off_), | ||||
|                 YESNO(this->supports_swing_mode_horizontal_), YESNO(this->supports_swing_mode_vertical_), | ||||
|                 YESNO(this->supports_two_points_)); | ||||
|  | ||||
|   ESP_LOGCONFIG(TAG, "  Supported PRESETS: "); | ||||
|   for (auto &it : this->preset_config_) { | ||||
|     const auto *preset_name = LOG_STR_ARG(climate::climate_preset_to_string(it.first)); | ||||
|  | ||||
|     ESP_LOGCONFIG(TAG, "    Supports %s: %s", preset_name, YESNO(true)); | ||||
|     this->dump_preset_config_(preset_name, it.second, it.first == this->default_preset_); | ||||
|   if (!this->preset_config_.empty()) { | ||||
|     ESP_LOGCONFIG(TAG, "  Supported PRESETS:"); | ||||
|     for (auto &it : this->preset_config_) { | ||||
|       const auto *preset_name = LOG_STR_ARG(climate::climate_preset_to_string(it.first)); | ||||
|       ESP_LOGCONFIG(TAG, "    %s:%s", preset_name, it.first == this->default_preset_ ? " (default)" : ""); | ||||
|       this->dump_preset_config_(preset_name, it.second); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   ESP_LOGCONFIG(TAG, "  Supported CUSTOM PRESETS: "); | ||||
|   for (auto &it : this->custom_preset_config_) { | ||||
|     const auto *preset_name = it.first.c_str(); | ||||
|  | ||||
|     ESP_LOGCONFIG(TAG, "    Supports %s: %s", preset_name, YESNO(true)); | ||||
|     this->dump_preset_config_(preset_name, it.second, it.first == this->default_custom_preset_); | ||||
|   if (!this->custom_preset_config_.empty()) { | ||||
|     ESP_LOGCONFIG(TAG, "  Supported CUSTOM PRESETS:"); | ||||
|     for (auto &it : this->custom_preset_config_) { | ||||
|       const auto *preset_name = it.first.c_str(); | ||||
|       ESP_LOGCONFIG(TAG, "    %s:%s", preset_name, it.first == this->default_custom_preset_ ? " (default)" : ""); | ||||
|       this->dump_preset_config_(preset_name, it.second); | ||||
|     } | ||||
|   } | ||||
|   ESP_LOGCONFIG(TAG, "  On boot, restore from: %s", | ||||
|                 this->on_boot_restore_from_ == thermostat::DEFAULT_PRESET ? "DEFAULT_PRESET" : "MEMORY"); | ||||
| } | ||||
|  | ||||
| ThermostatClimateTargetTempConfig::ThermostatClimateTargetTempConfig() = default; | ||||
|   | ||||
| @@ -6,9 +6,9 @@ | ||||
| #include "esphome/components/climate/climate.h" | ||||
| #include "esphome/components/sensor/sensor.h" | ||||
|  | ||||
| #include <array> | ||||
| #include <cinttypes> | ||||
| #include <map> | ||||
| #include <vector> | ||||
|  | ||||
| namespace esphome { | ||||
| namespace thermostat { | ||||
| @@ -24,6 +24,7 @@ enum ThermostatClimateTimerIndex : uint8_t { | ||||
|   TIMER_HEATING_OFF = 7, | ||||
|   TIMER_HEATING_ON = 8, | ||||
|   TIMER_IDLE_ON = 9, | ||||
|   TIMER_COUNT = 10, | ||||
| }; | ||||
|  | ||||
| enum OnBootRestoreFrom : uint8_t { | ||||
| @@ -131,6 +132,7 @@ class ThermostatClimate : public climate::Climate, public Component { | ||||
|   Trigger<> *get_dry_mode_trigger() const; | ||||
|   Trigger<> *get_fan_only_mode_trigger() const; | ||||
|   Trigger<> *get_heat_mode_trigger() const; | ||||
|   Trigger<> *get_heat_cool_mode_trigger() const; | ||||
|   Trigger<> *get_off_mode_trigger() const; | ||||
|   Trigger<> *get_fan_mode_on_trigger() const; | ||||
|   Trigger<> *get_fan_mode_off_trigger() const; | ||||
| @@ -163,9 +165,10 @@ class ThermostatClimate : public climate::Climate, public Component { | ||||
|   /// Returns the fan mode that is locked in (check fan_mode_change_delayed(), first!) | ||||
|   climate::ClimateFanMode locked_fan_mode(); | ||||
|   /// Set point and hysteresis validation | ||||
|   bool hysteresis_valid();  // returns true if valid | ||||
|   bool hysteresis_valid();               // returns true if valid | ||||
|   bool limit_setpoints_for_heat_cool();  // returns true if set points should be further limited within visual range | ||||
|   void validate_target_temperature(); | ||||
|   void validate_target_temperatures(); | ||||
|   void validate_target_temperatures(bool pin_target_temperature_high); | ||||
|   void validate_target_temperature_low(); | ||||
|   void validate_target_temperature_high(); | ||||
|  | ||||
| @@ -241,12 +244,28 @@ class ThermostatClimate : public climate::Climate, public Component { | ||||
|   bool supplemental_cooling_required_(); | ||||
|   bool supplemental_heating_required_(); | ||||
|  | ||||
|   void dump_preset_config_(const char *preset_name, const ThermostatClimateTargetTempConfig &config, | ||||
|                            bool is_default_preset); | ||||
|   void dump_preset_config_(const char *preset_name, const ThermostatClimateTargetTempConfig &config); | ||||
|  | ||||
|   /// Minimum allowable duration in seconds for action timers | ||||
|   const uint8_t min_timer_duration_{1}; | ||||
|  | ||||
|   /// Store previously-known states | ||||
|   /// | ||||
|   /// These are used to determine when a trigger/action needs to be called | ||||
|   climate::ClimateFanMode prev_fan_mode_{climate::CLIMATE_FAN_ON}; | ||||
|   climate::ClimateMode prev_mode_{climate::CLIMATE_MODE_OFF}; | ||||
|   climate::ClimateSwingMode prev_swing_mode_{climate::CLIMATE_SWING_OFF}; | ||||
|  | ||||
|   /// The current supplemental action | ||||
|   climate::ClimateAction supplemental_action_{climate::CLIMATE_ACTION_OFF}; | ||||
|  | ||||
|   /// Default standard preset to use on start up | ||||
|   climate::ClimatePreset default_preset_{}; | ||||
|  | ||||
|   /// If set to DEFAULT_PRESET then the default preset is always used. When MEMORY prior | ||||
|   /// state will attempt to be restored if possible | ||||
|   OnBootRestoreFrom on_boot_restore_from_{OnBootRestoreFrom::MEMORY}; | ||||
|  | ||||
|   /// Whether the controller supports auto/cooling/drying/fanning/heating. | ||||
|   /// | ||||
|   /// A false value for any given attribute means that the controller has no such action | ||||
| @@ -362,9 +381,15 @@ class ThermostatClimate : public climate::Climate, public Component { | ||||
|   Trigger<> *supplemental_heat_action_trigger_{nullptr}; | ||||
|   Trigger<> *heat_mode_trigger_{nullptr}; | ||||
|  | ||||
|   /// The trigger to call when the controller should switch to heat/cool mode. | ||||
|   /// | ||||
|   /// In heat/cool mode, the controller will enable heating/cooling as necessary and switch | ||||
|   /// to idle when the temperature is within the thresholds/set points. | ||||
|   Trigger<> *heat_cool_mode_trigger_{nullptr}; | ||||
|  | ||||
|   /// The trigger to call when the controller should switch to auto mode. | ||||
|   /// | ||||
|   /// In auto mode, the controller will enable heating/cooling as necessary and switch | ||||
|   /// In auto mode, the controller will enable heating/cooling as supported/necessary and switch | ||||
|   /// to idle when the temperature is within the thresholds/set points. | ||||
|   Trigger<> *auto_mode_trigger_{nullptr}; | ||||
|  | ||||
| @@ -438,35 +463,21 @@ class ThermostatClimate : public climate::Climate, public Component { | ||||
|   Trigger<> *prev_mode_trigger_{nullptr}; | ||||
|   Trigger<> *prev_swing_mode_trigger_{nullptr}; | ||||
|  | ||||
|   /// If set to DEFAULT_PRESET then the default preset is always used. When MEMORY prior | ||||
|   /// state will attempt to be restored if possible | ||||
|   OnBootRestoreFrom on_boot_restore_from_{OnBootRestoreFrom::MEMORY}; | ||||
|  | ||||
|   /// Store previously-known states | ||||
|   /// | ||||
|   /// These are used to determine when a trigger/action needs to be called | ||||
|   climate::ClimateAction supplemental_action_{climate::CLIMATE_ACTION_OFF}; | ||||
|   climate::ClimateFanMode prev_fan_mode_{climate::CLIMATE_FAN_ON}; | ||||
|   climate::ClimateMode prev_mode_{climate::CLIMATE_MODE_OFF}; | ||||
|   climate::ClimateSwingMode prev_swing_mode_{climate::CLIMATE_SWING_OFF}; | ||||
|  | ||||
|   /// Default standard preset to use on start up | ||||
|   climate::ClimatePreset default_preset_{}; | ||||
|   /// Default custom preset to use on start up | ||||
|   std::string default_custom_preset_{}; | ||||
|  | ||||
|   /// Climate action timers | ||||
|   std::vector<ThermostatClimateTimer> timer_{ | ||||
|       {false, 0, 0, std::bind(&ThermostatClimate::cooling_max_run_time_timer_callback_, this)}, | ||||
|       {false, 0, 0, std::bind(&ThermostatClimate::cooling_off_timer_callback_, this)}, | ||||
|       {false, 0, 0, std::bind(&ThermostatClimate::cooling_on_timer_callback_, this)}, | ||||
|       {false, 0, 0, std::bind(&ThermostatClimate::fan_mode_timer_callback_, this)}, | ||||
|       {false, 0, 0, std::bind(&ThermostatClimate::fanning_off_timer_callback_, this)}, | ||||
|       {false, 0, 0, std::bind(&ThermostatClimate::fanning_on_timer_callback_, this)}, | ||||
|       {false, 0, 0, std::bind(&ThermostatClimate::heating_max_run_time_timer_callback_, this)}, | ||||
|       {false, 0, 0, std::bind(&ThermostatClimate::heating_off_timer_callback_, this)}, | ||||
|       {false, 0, 0, std::bind(&ThermostatClimate::heating_on_timer_callback_, this)}, | ||||
|       {false, 0, 0, std::bind(&ThermostatClimate::idle_on_timer_callback_, this)}, | ||||
|   std::array<ThermostatClimateTimer, TIMER_COUNT> timer_{ | ||||
|       ThermostatClimateTimer(false, 0, 0, std::bind(&ThermostatClimate::cooling_max_run_time_timer_callback_, this)), | ||||
|       ThermostatClimateTimer(false, 0, 0, std::bind(&ThermostatClimate::cooling_off_timer_callback_, this)), | ||||
|       ThermostatClimateTimer(false, 0, 0, std::bind(&ThermostatClimate::cooling_on_timer_callback_, this)), | ||||
|       ThermostatClimateTimer(false, 0, 0, std::bind(&ThermostatClimate::fan_mode_timer_callback_, this)), | ||||
|       ThermostatClimateTimer(false, 0, 0, std::bind(&ThermostatClimate::fanning_off_timer_callback_, this)), | ||||
|       ThermostatClimateTimer(false, 0, 0, std::bind(&ThermostatClimate::fanning_on_timer_callback_, this)), | ||||
|       ThermostatClimateTimer(false, 0, 0, std::bind(&ThermostatClimate::heating_max_run_time_timer_callback_, this)), | ||||
|       ThermostatClimateTimer(false, 0, 0, std::bind(&ThermostatClimate::heating_off_timer_callback_, this)), | ||||
|       ThermostatClimateTimer(false, 0, 0, std::bind(&ThermostatClimate::heating_on_timer_callback_, this)), | ||||
|       ThermostatClimateTimer(false, 0, 0, std::bind(&ThermostatClimate::idle_on_timer_callback_, this)), | ||||
|   }; | ||||
|  | ||||
|   /// The set of standard preset configurations this thermostat supports (Eg. AWAY, ECO, etc) | ||||
|   | ||||
| @@ -424,6 +424,7 @@ CONF_HEAD = "head" | ||||
| CONF_HEADING = "heading" | ||||
| CONF_HEARTBEAT = "heartbeat" | ||||
| CONF_HEAT_ACTION = "heat_action" | ||||
| CONF_HEAT_COOL_MODE = "heat_cool_mode" | ||||
| CONF_HEAT_DEADBAND = "heat_deadband" | ||||
| CONF_HEAT_MODE = "heat_mode" | ||||
| CONF_HEAT_OVERRUN = "heat_overrun" | ||||
|   | ||||
		Reference in New Issue
	
	Block a user