1
0
mirror of https://github.com/esphome/esphome.git synced 2025-10-31 23:21:54 +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:
Keith Burzinski
2025-09-09 20:24:50 -05:00
committed by GitHub
parent e218f16f0f
commit d9f625e5c8
4 changed files with 220 additions and 185 deletions

View File

@@ -32,6 +32,7 @@ from esphome.const import (
CONF_FAN_WITH_COOLING, CONF_FAN_WITH_COOLING,
CONF_FAN_WITH_HEATING, CONF_FAN_WITH_HEATING,
CONF_HEAT_ACTION, CONF_HEAT_ACTION,
CONF_HEAT_COOL_MODE,
CONF_HEAT_DEADBAND, CONF_HEAT_DEADBAND,
CONF_HEAT_MODE, CONF_HEAT_MODE,
CONF_HEAT_OVERRUN, CONF_HEAT_OVERRUN,
@@ -150,7 +151,7 @@ def generate_comparable_preset(config, name):
def validate_thermostat(config): def validate_thermostat(config):
# verify corresponding action(s) exist(s) for any defined climate mode or action # verify corresponding action(s) exist(s) for any defined climate mode or action
requirements = { requirements = {
CONF_AUTO_MODE: [ CONF_HEAT_COOL_MODE: [
CONF_COOL_ACTION, CONF_COOL_ACTION,
CONF_HEAT_ACTION, CONF_HEAT_ACTION,
CONF_MIN_COOLING_OFF_TIME, CONF_MIN_COOLING_OFF_TIME,
@@ -540,6 +541,9 @@ CONFIG_SCHEMA = cv.All(
cv.Optional(CONF_FAN_ONLY_MODE): automation.validate_automation( cv.Optional(CONF_FAN_ONLY_MODE): automation.validate_automation(
single=True 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_HEAT_MODE): automation.validate_automation(single=True),
cv.Optional(CONF_OFF_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( 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) var = await climate.new_climate(config)
await cg.register_component(var, 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 ( two_points_available = CONF_HEAT_ACTION in config and (
CONF_COOL_ACTION in config CONF_COOL_ACTION in config
or (config[CONF_FAN_ONLY_COOLING] and CONF_FAN_ONLY_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] 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: if CONF_COOL_ACTION in config:
await automation.build_automation( await automation.build_automation(
var.get_cool_action_trigger(), [], config[CONF_COOL_ACTION] var.get_cool_action_trigger(), [], config[CONF_COOL_ACTION]
@@ -780,6 +778,7 @@ async def to_code(config):
await automation.build_automation( await automation.build_automation(
var.get_auto_mode_trigger(), [], config[CONF_AUTO_MODE] var.get_auto_mode_trigger(), [], config[CONF_AUTO_MODE]
) )
cg.add(var.set_supports_auto(True))
if CONF_COOL_MODE in config: if CONF_COOL_MODE in config:
await automation.build_automation( await automation.build_automation(
var.get_cool_mode_trigger(), [], config[CONF_COOL_MODE] 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] var.get_heat_mode_trigger(), [], config[CONF_HEAT_MODE]
) )
cg.add(var.set_supports_heat(True)) 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: if CONF_OFF_MODE in config:
await automation.build_automation( await automation.build_automation(
var.get_off_mode_trigger(), [], config[CONF_OFF_MODE] var.get_off_mode_trigger(), [], config[CONF_OFF_MODE]

View File

@@ -1,4 +1,6 @@
#include "thermostat_climate.h" #include "thermostat_climate.h"
#include "esphome/core/application.h"
#include "esphome/core/helpers.h"
#include "esphome/core/log.h" #include "esphome/core/log.h"
namespace esphome { namespace esphome {
@@ -64,7 +66,7 @@ void ThermostatClimate::setup() {
void ThermostatClimate::loop() { void ThermostatClimate::loop() {
for (auto &timer : this->timer_) { 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.active = false;
timer.func(); timer.func();
} }
@@ -127,26 +129,35 @@ bool ThermostatClimate::hysteresis_valid() {
return true; 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() { void ThermostatClimate::validate_target_temperature() {
if (std::isnan(this->target_temperature)) { if (std::isnan(this->target_temperature)) {
// default to the midpoint between visual min and max
this->target_temperature = this->target_temperature =
((this->get_traits().get_visual_max_temperature() - this->get_traits().get_visual_min_temperature()) / 2) + ((this->get_traits().get_visual_max_temperature() - this->get_traits().get_visual_min_temperature()) / 2) +
this->get_traits().get_visual_min_temperature(); this->get_traits().get_visual_min_temperature();
} else { } else {
// target_temperature must be between the visual minimum and the visual maximum // 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 = clamp(this->target_temperature, this->get_traits().get_visual_min_temperature(),
this->target_temperature = this->get_traits().get_visual_min_temperature(); this->get_traits().get_visual_max_temperature());
if (this->target_temperature > this->get_traits().get_visual_max_temperature())
this->target_temperature = this->get_traits().get_visual_max_temperature();
} }
} }
void ThermostatClimate::validate_target_temperatures() { void ThermostatClimate::validate_target_temperatures(const bool pin_target_temperature_high) {
if (this->supports_two_points_) { 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_low();
this->validate_target_temperature_high(); this->validate_target_temperature_high();
} else { } 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)) { if (std::isnan(this->target_temperature_low)) {
this->target_temperature_low = this->get_traits().get_visual_min_temperature(); this->target_temperature_low = this->get_traits().get_visual_min_temperature();
} else { } else {
// target_temperature_low must not be lower than the visual minimum float target_temperature_low_upper_limit =
if (this->target_temperature_low < this->get_traits().get_visual_min_temperature()) this->limit_setpoints_for_heat_cool()
this->target_temperature_low = this->get_traits().get_visual_min_temperature(); ? clamp(this->target_temperature_high - this->set_point_minimum_differential_,
// target_temperature_low must not be greater than the visual maximum minus set_point_minimum_differential_ this->get_traits().get_visual_min_temperature(), this->get_traits().get_visual_max_temperature())
if (this->target_temperature_low > : this->get_traits().get_visual_max_temperature();
this->get_traits().get_visual_max_temperature() - this->set_point_minimum_differential_) { this->target_temperature_low = clamp(this->target_temperature_low, this->get_traits().get_visual_min_temperature(),
this->target_temperature_low = target_temperature_low_upper_limit);
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_;
} }
} }
@@ -173,62 +179,64 @@ void ThermostatClimate::validate_target_temperature_high() {
if (std::isnan(this->target_temperature_high)) { if (std::isnan(this->target_temperature_high)) {
this->target_temperature_high = this->get_traits().get_visual_max_temperature(); this->target_temperature_high = this->get_traits().get_visual_max_temperature();
} else { } else {
// target_temperature_high must not be lower than the visual maximum float target_temperature_high_lower_limit =
if (this->target_temperature_high > this->get_traits().get_visual_max_temperature()) this->limit_setpoints_for_heat_cool()
this->target_temperature_high = this->get_traits().get_visual_max_temperature(); ? clamp(this->target_temperature_low + this->set_point_minimum_differential_,
// target_temperature_high must not be lower than the visual minimum plus set_point_minimum_differential_ this->get_traits().get_visual_min_temperature(), this->get_traits().get_visual_max_temperature())
if (this->target_temperature_high < : this->get_traits().get_visual_min_temperature();
this->get_traits().get_visual_min_temperature() + this->set_point_minimum_differential_) { this->target_temperature_high = clamp(this->target_temperature_high, target_temperature_high_lower_limit,
this->target_temperature_high = this->get_traits().get_visual_max_temperature());
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_;
} }
} }
void ThermostatClimate::control(const climate::ClimateCall &call) { void ThermostatClimate::control(const climate::ClimateCall &call) {
bool target_temperature_high_changed = false;
if (call.get_preset().has_value()) { if (call.get_preset().has_value()) {
// setup_complete_ blocks modifying/resetting the temps immediately after boot // setup_complete_ blocks modifying/resetting the temps immediately after boot
if (this->setup_complete_) { if (this->setup_complete_) {
this->change_preset_(*call.get_preset()); this->change_preset_(call.get_preset().value());
} else { } else {
this->preset = *call.get_preset(); this->preset = call.get_preset().value();
} }
} }
if (call.get_custom_preset().has_value()) { if (call.get_custom_preset().has_value()) {
// setup_complete_ blocks modifying/resetting the temps immediately after boot // setup_complete_ blocks modifying/resetting the temps immediately after boot
if (this->setup_complete_) { if (this->setup_complete_) {
this->change_custom_preset_(*call.get_custom_preset()); this->change_custom_preset_(call.get_custom_preset().value());
} else { } else {
this->custom_preset = *call.get_custom_preset(); this->custom_preset = call.get_custom_preset().value();
} }
} }
if (call.get_mode().has_value()) if (call.get_mode().has_value()) {
this->mode = *call.get_mode(); this->mode = call.get_mode().value();
if (call.get_fan_mode().has_value()) }
this->fan_mode = *call.get_fan_mode(); if (call.get_fan_mode().has_value()) {
if (call.get_swing_mode().has_value()) this->fan_mode = call.get_fan_mode().value();
this->swing_mode = *call.get_swing_mode(); }
if (call.get_swing_mode().has_value()) {
this->swing_mode = call.get_swing_mode().value();
}
if (this->supports_two_points_) { if (this->supports_two_points_) {
if (call.get_target_temperature_low().has_value()) { if (call.get_target_temperature_low().has_value()) {
this->target_temperature_low = *call.get_target_temperature_low(); this->target_temperature_low = call.get_target_temperature_low().value();
validate_target_temperature_low();
} }
if (call.get_target_temperature_high().has_value()) { if (call.get_target_temperature_high().has_value()) {
this->target_temperature_high = *call.get_target_temperature_high(); target_temperature_high_changed = this->target_temperature_high != call.get_target_temperature_high().value();
validate_target_temperature_high(); 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 { } else {
if (call.get_target_temperature().has_value()) { if (call.get_target_temperature().has_value()) {
this->target_temperature = *call.get_target_temperature(); this->target_temperature = call.get_target_temperature().value();
validate_target_temperature(); this->validate_target_temperature();
} }
} }
// make any changes happen // make any changes happen
refresh(); this->refresh();
} }
climate::ClimateTraits ThermostatClimate::traits() { climate::ClimateTraits ThermostatClimate::traits() {
@@ -237,47 +245,47 @@ climate::ClimateTraits ThermostatClimate::traits() {
if (this->humidity_sensor_ != nullptr) if (this->humidity_sensor_ != nullptr)
traits.set_supports_current_humidity(true); traits.set_supports_current_humidity(true);
if (supports_auto_) if (this->supports_auto_)
traits.add_supported_mode(climate::CLIMATE_MODE_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); traits.add_supported_mode(climate::CLIMATE_MODE_HEAT_COOL);
if (supports_cool_) if (this->supports_cool_)
traits.add_supported_mode(climate::CLIMATE_MODE_COOL); traits.add_supported_mode(climate::CLIMATE_MODE_COOL);
if (supports_dry_) if (this->supports_dry_)
traits.add_supported_mode(climate::CLIMATE_MODE_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); traits.add_supported_mode(climate::CLIMATE_MODE_FAN_ONLY);
if (supports_heat_) if (this->supports_heat_)
traits.add_supported_mode(climate::CLIMATE_MODE_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); 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); 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); 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); 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); 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); 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); 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); 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); 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); 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); 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); 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); 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); traits.add_supported_swing_mode(climate::CLIMATE_SWING_VERTICAL);
for (auto &it : this->preset_config_) { 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 // do not change the action if an "ON" timer is running
if ((!ignore_timers) && if ((!ignore_timers) &&
(timer_active_(thermostat::TIMER_IDLE_ON) || timer_active_(thermostat::TIMER_COOLING_ON) || (this->timer_active_(thermostat::TIMER_IDLE_ON) || this->timer_active_(thermostat::TIMER_COOLING_ON) ||
timer_active_(thermostat::TIMER_FANNING_ON) || timer_active_(thermostat::TIMER_HEATING_ON))) { this->timer_active_(thermostat::TIMER_FANNING_ON) || this->timer_active_(thermostat::TIMER_HEATING_ON))) {
return this->action; return this->action;
} }
// ensure set point(s) is/are valid before computing the 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 // everything has been validated so we can now safely compute the action
switch (this->mode) { switch (this->mode) {
// if the climate mode is OFF then the climate action must be OFF // 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; target_action = climate::CLIMATE_ACTION_HEATING;
} }
break; 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: default:
break; break;
} }
@@ -362,7 +386,7 @@ climate::ClimateAction ThermostatClimate::compute_supplemental_action_() {
} }
// ensure set point(s) is/are valid before computing the 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 // everything has been validated so we can now safely compute the action
switch (this->mode) { switch (this->mode) {
// if the climate mode is OFF then the climate action must be OFF // 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_->stop_action();
this->prev_mode_trigger_ = nullptr; this->prev_mode_trigger_ = nullptr;
} }
Trigger<> *trig = this->auto_mode_trigger_; Trigger<> *trig = this->off_mode_trigger_;
switch (mode) { switch (mode) {
case climate::CLIMATE_MODE_OFF: case climate::CLIMATE_MODE_AUTO:
trig = this->off_mode_trigger_; trig = this->auto_mode_trigger_;
break; break;
case climate::CLIMATE_MODE_HEAT_COOL: case climate::CLIMATE_MODE_HEAT_COOL:
// trig = this->auto_mode_trigger_; trig = this->heat_cool_mode_trigger_;
break; break;
case climate::CLIMATE_MODE_COOL: case climate::CLIMATE_MODE_COOL:
trig = this->cool_mode_trigger_; trig = this->cool_mode_trigger_;
@@ -673,11 +697,12 @@ void ThermostatClimate::switch_to_mode_(climate::ClimateMode mode, bool publish_
case climate::CLIMATE_MODE_DRY: case climate::CLIMATE_MODE_DRY:
trig = this->dry_mode_trigger_; trig = this->dry_mode_trigger_;
break; break;
case climate::CLIMATE_MODE_OFF:
default: default:
// we cannot report an invalid mode back to HA (even if it asked for one) // we cannot report an invalid mode back to HA (even if it asked for one)
// and must assume some valid value // and must assume some valid value
mode = climate::CLIMATE_MODE_HEAT_COOL; mode = climate::CLIMATE_MODE_OFF;
// trig = this->auto_mode_trigger_; // trig = this->off_mode_trigger_;
} }
if (trig != nullptr) { if (trig != nullptr) {
trig->trigger(); trig->trigger();
@@ -685,8 +710,9 @@ void ThermostatClimate::switch_to_mode_(climate::ClimateMode mode, bool publish_
this->mode = mode; this->mode = mode;
this->prev_mode_ = mode; this->prev_mode_ = mode;
this->prev_mode_trigger_ = trig; this->prev_mode_trigger_ = trig;
if (publish_state) if (publish_state) {
this->publish_state(); this->publish_state();
}
} }
void ThermostatClimate::switch_to_swing_mode_(climate::ClimateSwingMode swing_mode, bool 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)); (this->supplemental_action_ == climate::CLIMATE_ACTION_HEATING));
} }
void ThermostatClimate::dump_preset_config_(const char *preset_name, const ThermostatClimateTargetTempConfig &config, 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));
if (this->supports_heat_) { if (this->supports_heat_) {
if (this->supports_two_points_) { ESP_LOGCONFIG(TAG, " Default Target Temperature Low: %.1f°C",
ESP_LOGCONFIG(TAG, " %s Default Target Temperature Low: %.1f°C", preset_name, this->supports_two_points_ ? config.default_temperature_low : config.default_temperature);
config.default_temperature_low);
} else {
ESP_LOGCONFIG(TAG, " %s Default Target Temperature Low: %.1f°C", preset_name, config.default_temperature);
}
} }
if ((this->supports_cool_) || (this->supports_fan_only_)) { if ((this->supports_cool_) || (this->supports_fan_only_)) {
if (this->supports_two_points_) { ESP_LOGCONFIG(TAG, " Default Target Temperature High: %.1f°C",
ESP_LOGCONFIG(TAG, " %s Default Target Temperature High: %.1f°C", preset_name, this->supports_two_points_ ? config.default_temperature_high : config.default_temperature);
config.default_temperature_high);
} else {
ESP_LOGCONFIG(TAG, " %s Default Target Temperature High: %.1f°C", preset_name, config.default_temperature);
}
} }
if (config.mode_.has_value()) { if (config.mode_.has_value()) {
ESP_LOGCONFIG(TAG, " %s Default Mode: %s", preset_name, ESP_LOGCONFIG(TAG, " Default Mode: %s", LOG_STR_ARG(climate::climate_mode_to_string(*config.mode_)));
LOG_STR_ARG(climate::climate_mode_to_string(*config.mode_)));
} }
if (config.fan_mode_.has_value()) { 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_))); LOG_STR_ARG(climate::climate_fan_mode_to_string(*config.fan_mode_)));
} }
if (config.swing_mode_.has_value()) { 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_))); LOG_STR_ARG(climate::climate_swing_mode_to_string(*config.swing_mode_)));
} }
} }
@@ -1106,6 +1120,7 @@ ThermostatClimate::ThermostatClimate()
heat_action_trigger_(new Trigger<>()), heat_action_trigger_(new Trigger<>()),
supplemental_heat_action_trigger_(new Trigger<>()), supplemental_heat_action_trigger_(new Trigger<>()),
heat_mode_trigger_(new Trigger<>()), heat_mode_trigger_(new Trigger<>()),
heat_cool_mode_trigger_(new Trigger<>()),
auto_mode_trigger_(new Trigger<>()), auto_mode_trigger_(new Trigger<>()),
idle_action_trigger_(new Trigger<>()), idle_action_trigger_(new Trigger<>()),
off_mode_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_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_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_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_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_on_trigger() const { return this->fan_mode_on_trigger_; }
Trigger<> *ThermostatClimate::get_fan_mode_off_trigger() const { return this->fan_mode_off_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() { void ThermostatClimate::dump_config() {
LOG_CLIMATE("", "Thermostat", this); 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_) { if (this->supports_two_points_) {
ESP_LOGCONFIG(TAG, " Minimum Set Point Differential: %.1f°C", this->set_point_minimum_differential_); 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_) { if (this->supports_cool_) {
ESP_LOGCONFIG(TAG, ESP_LOGCONFIG(TAG,
" Cooling Parameters:\n" " Cooling Parameters:\n"
" Deadband: %.1f°C\n" " Deadband: %.1f°C\n"
" Overrun: %.1f°C", " Overrun: %.1f°C\n"
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,
" Minimum Off Time: %" PRIu32 "s\n" " Minimum Off Time: %" PRIu32 "s\n"
" Minimum Run Time: %" PRIu32 "s", " 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_OFF) / 1000,
this->timer_duration_(thermostat::TIMER_COOLING_ON) / 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_) { if (this->supports_heat_) {
ESP_LOGCONFIG(TAG, ESP_LOGCONFIG(TAG,
" Heating Parameters:\n" " Heating Parameters:\n"
" Deadband: %.1f°C\n" " Deadband: %.1f°C\n"
" Overrun: %.1f°C", " Overrun: %.1f°C\n"
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,
" Minimum Off Time: %" PRIu32 "s\n" " Minimum Off Time: %" PRIu32 "s\n"
" Minimum Run Time: %" PRIu32 "s", " 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_OFF) / 1000,
this->timer_duration_(thermostat::TIMER_HEATING_ON) / 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_) { if (this->supports_fan_only_) {
ESP_LOGCONFIG(TAG, ESP_LOGCONFIG(TAG,
" Fanning Minimum Off Time: %" PRIu32 "s\n" " Fan Parameters:\n"
" Fanning Minimum Run Time: %" PRIu32 "s", " 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_OFF) / 1000,
this->timer_duration_(thermostat::TIMER_FANNING_ON) / 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", ESP_LOGCONFIG(TAG, " Minimum Fan Mode Switching Time: %" PRIu32 "s",
this->timer_duration_(thermostat::TIMER_FAN_MODE) / 1000); 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, ESP_LOGCONFIG(TAG,
" Minimum Idle Time: %" PRIu32 "s\n"
" Supported MODES:\n" " Supported MODES:\n"
" AUTO: %s\n" " AUTO: %s\n"
" HEAT/COOL: %s\n" " HEAT/COOL: %s\n"
@@ -1362,8 +1381,9 @@ void ThermostatClimate::dump_config() {
" FAN_ONLY: %s\n" " FAN_ONLY: %s\n"
" FAN_ONLY_ACTION_USES_FAN_MODE_TIMER: %s\n" " FAN_ONLY_ACTION_USES_FAN_MODE_TIMER: %s\n"
" FAN_ONLY_COOLING: %s", " FAN_ONLY_COOLING: %s",
YESNO(this->supports_auto_), YESNO(this->supports_heat_cool_), YESNO(this->supports_heat_), this->timer_[thermostat::TIMER_IDLE_ON].time / 1000, YESNO(this->supports_auto_),
YESNO(this->supports_cool_), YESNO(this->supports_dry_), YESNO(this->supports_fan_only_), 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_)); YESNO(this->supports_fan_only_action_uses_fan_mode_timer_), YESNO(this->supports_fan_only_cooling_));
if (this->supports_cool_) { if (this->supports_cool_) {
ESP_LOGCONFIG(TAG, " FAN_WITH_COOLING: %s", YESNO(this->supports_fan_with_cooling_)); ESP_LOGCONFIG(TAG, " FAN_WITH_COOLING: %s", YESNO(this->supports_fan_with_cooling_));
@@ -1382,40 +1402,39 @@ void ThermostatClimate::dump_config() {
" MIDDLE: %s\n" " MIDDLE: %s\n"
" FOCUS: %s\n" " FOCUS: %s\n"
" DIFFUSE: %s\n" " DIFFUSE: %s\n"
" QUIET: %s", " QUIET: %s\n"
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,
" Supported SWING MODES:\n" " Supported SWING MODES:\n"
" BOTH: %s\n" " BOTH: %s\n"
" OFF: %s\n" " OFF: %s\n"
" HORIZONTAL: %s\n" " HORIZONTAL: %s\n"
" VERTICAL: %s\n" " VERTICAL: %s\n"
" Supports TWO SET POINTS: %s", " 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_both_), YESNO(this->supports_swing_mode_off_),
YESNO(this->supports_swing_mode_horizontal_), YESNO(this->supports_swing_mode_vertical_), YESNO(this->supports_swing_mode_horizontal_), YESNO(this->supports_swing_mode_vertical_),
YESNO(this->supports_two_points_)); YESNO(this->supports_two_points_));
ESP_LOGCONFIG(TAG, " Supported PRESETS: "); if (!this->preset_config_.empty()) {
for (auto &it : this->preset_config_) { ESP_LOGCONFIG(TAG, " Supported PRESETS:");
const auto *preset_name = LOG_STR_ARG(climate::climate_preset_to_string(it.first)); 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)); ESP_LOGCONFIG(TAG, " %s:%s", preset_name, it.first == this->default_preset_ ? " (default)" : "");
this->dump_preset_config_(preset_name, it.second, it.first == this->default_preset_); this->dump_preset_config_(preset_name, it.second);
}
} }
ESP_LOGCONFIG(TAG, " Supported CUSTOM PRESETS: "); if (!this->custom_preset_config_.empty()) {
for (auto &it : this->custom_preset_config_) { ESP_LOGCONFIG(TAG, " Supported CUSTOM PRESETS:");
const auto *preset_name = it.first.c_str(); 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)); ESP_LOGCONFIG(TAG, " %s:%s", preset_name, it.first == this->default_custom_preset_ ? " (default)" : "");
this->dump_preset_config_(preset_name, it.second, it.first == this->default_custom_preset_); 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; ThermostatClimateTargetTempConfig::ThermostatClimateTargetTempConfig() = default;

View File

@@ -6,9 +6,9 @@
#include "esphome/components/climate/climate.h" #include "esphome/components/climate/climate.h"
#include "esphome/components/sensor/sensor.h" #include "esphome/components/sensor/sensor.h"
#include <array>
#include <cinttypes> #include <cinttypes>
#include <map> #include <map>
#include <vector>
namespace esphome { namespace esphome {
namespace thermostat { namespace thermostat {
@@ -24,6 +24,7 @@ enum ThermostatClimateTimerIndex : uint8_t {
TIMER_HEATING_OFF = 7, TIMER_HEATING_OFF = 7,
TIMER_HEATING_ON = 8, TIMER_HEATING_ON = 8,
TIMER_IDLE_ON = 9, TIMER_IDLE_ON = 9,
TIMER_COUNT = 10,
}; };
enum OnBootRestoreFrom : uint8_t { enum OnBootRestoreFrom : uint8_t {
@@ -131,6 +132,7 @@ class ThermostatClimate : public climate::Climate, public Component {
Trigger<> *get_dry_mode_trigger() const; Trigger<> *get_dry_mode_trigger() const;
Trigger<> *get_fan_only_mode_trigger() const; Trigger<> *get_fan_only_mode_trigger() const;
Trigger<> *get_heat_mode_trigger() const; Trigger<> *get_heat_mode_trigger() const;
Trigger<> *get_heat_cool_mode_trigger() const;
Trigger<> *get_off_mode_trigger() const; Trigger<> *get_off_mode_trigger() const;
Trigger<> *get_fan_mode_on_trigger() const; Trigger<> *get_fan_mode_on_trigger() const;
Trigger<> *get_fan_mode_off_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!) /// Returns the fan mode that is locked in (check fan_mode_change_delayed(), first!)
climate::ClimateFanMode locked_fan_mode(); climate::ClimateFanMode locked_fan_mode();
/// Set point and hysteresis validation /// 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_temperature();
void validate_target_temperatures(); void validate_target_temperatures(bool pin_target_temperature_high);
void validate_target_temperature_low(); void validate_target_temperature_low();
void validate_target_temperature_high(); void validate_target_temperature_high();
@@ -241,12 +244,28 @@ class ThermostatClimate : public climate::Climate, public Component {
bool supplemental_cooling_required_(); bool supplemental_cooling_required_();
bool supplemental_heating_required_(); bool supplemental_heating_required_();
void dump_preset_config_(const char *preset_name, const ThermostatClimateTargetTempConfig &config, void dump_preset_config_(const char *preset_name, const ThermostatClimateTargetTempConfig &config);
bool is_default_preset);
/// Minimum allowable duration in seconds for action timers /// Minimum allowable duration in seconds for action timers
const uint8_t min_timer_duration_{1}; 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. /// Whether the controller supports auto/cooling/drying/fanning/heating.
/// ///
/// A false value for any given attribute means that the controller has no such action /// 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<> *supplemental_heat_action_trigger_{nullptr};
Trigger<> *heat_mode_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. /// 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. /// to idle when the temperature is within the thresholds/set points.
Trigger<> *auto_mode_trigger_{nullptr}; Trigger<> *auto_mode_trigger_{nullptr};
@@ -438,35 +463,21 @@ class ThermostatClimate : public climate::Climate, public Component {
Trigger<> *prev_mode_trigger_{nullptr}; Trigger<> *prev_mode_trigger_{nullptr};
Trigger<> *prev_swing_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 /// Default custom preset to use on start up
std::string default_custom_preset_{}; std::string default_custom_preset_{};
/// Climate action timers /// Climate action timers
std::vector<ThermostatClimateTimer> timer_{ std::array<ThermostatClimateTimer, TIMER_COUNT> timer_{
{false, 0, 0, std::bind(&ThermostatClimate::cooling_max_run_time_timer_callback_, this)}, ThermostatClimateTimer(false, 0, 0, std::bind(&ThermostatClimate::cooling_max_run_time_timer_callback_, this)),
{false, 0, 0, std::bind(&ThermostatClimate::cooling_off_timer_callback_, this)}, ThermostatClimateTimer(false, 0, 0, std::bind(&ThermostatClimate::cooling_off_timer_callback_, this)),
{false, 0, 0, std::bind(&ThermostatClimate::cooling_on_timer_callback_, this)}, ThermostatClimateTimer(false, 0, 0, std::bind(&ThermostatClimate::cooling_on_timer_callback_, this)),
{false, 0, 0, std::bind(&ThermostatClimate::fan_mode_timer_callback_, this)}, ThermostatClimateTimer(false, 0, 0, std::bind(&ThermostatClimate::fan_mode_timer_callback_, this)),
{false, 0, 0, std::bind(&ThermostatClimate::fanning_off_timer_callback_, this)}, ThermostatClimateTimer(false, 0, 0, std::bind(&ThermostatClimate::fanning_off_timer_callback_, this)),
{false, 0, 0, std::bind(&ThermostatClimate::fanning_on_timer_callback_, this)}, ThermostatClimateTimer(false, 0, 0, std::bind(&ThermostatClimate::fanning_on_timer_callback_, this)),
{false, 0, 0, std::bind(&ThermostatClimate::heating_max_run_time_timer_callback_, this)}, ThermostatClimateTimer(false, 0, 0, std::bind(&ThermostatClimate::heating_max_run_time_timer_callback_, this)),
{false, 0, 0, std::bind(&ThermostatClimate::heating_off_timer_callback_, this)}, ThermostatClimateTimer(false, 0, 0, std::bind(&ThermostatClimate::heating_off_timer_callback_, this)),
{false, 0, 0, std::bind(&ThermostatClimate::heating_on_timer_callback_, this)}, ThermostatClimateTimer(false, 0, 0, std::bind(&ThermostatClimate::heating_on_timer_callback_, this)),
{false, 0, 0, std::bind(&ThermostatClimate::idle_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) /// The set of standard preset configurations this thermostat supports (Eg. AWAY, ECO, etc)

View File

@@ -424,6 +424,7 @@ CONF_HEAD = "head"
CONF_HEADING = "heading" CONF_HEADING = "heading"
CONF_HEARTBEAT = "heartbeat" CONF_HEARTBEAT = "heartbeat"
CONF_HEAT_ACTION = "heat_action" CONF_HEAT_ACTION = "heat_action"
CONF_HEAT_COOL_MODE = "heat_cool_mode"
CONF_HEAT_DEADBAND = "heat_deadband" CONF_HEAT_DEADBAND = "heat_deadband"
CONF_HEAT_MODE = "heat_mode" CONF_HEAT_MODE = "heat_mode"
CONF_HEAT_OVERRUN = "heat_overrun" CONF_HEAT_OVERRUN = "heat_overrun"