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:
@@ -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]
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
Reference in New Issue
Block a user