mirror of
https://github.com/esphome/esphome.git
synced 2024-11-10 01:07:45 +01:00
Thermostat preset with modes (#3298)
* Rework HOME/AWAY support to being driven via a map of ClimatePreset/ThermostatClimateTargetTempConfig This opens up to theoretically being able to support other presets (ECO, SLEEP, etc) * Add support for additional presets Configuration takes the form; ``` climate: platform: preset ... preset: [eco | away | boost | comfort | home | sleep | activity]: default_target_temperature_low: 20 default_target_temperature_high: 24 ``` These will be available in the Home Assistant UI and, like the existing Home/Away config will reset the temperature in line with these defaults when selected. The existing away_config/home_config is still respected (although preset->home/preset->away will be applied after them and override them if both styles are specified) * Add support for specifying MODE, FAN_MODE and SWING_MODE on a preset When switching presets these will implicitly flow through to the controller. However calls to climate.control which specify any of these will take precedence even when changing the mode (think of the preset version as the default for that preset) * Add `preset_change` mode trigger When defined this trigger will fire when the preset for the thermostat has been changed. The intent of this is similar to `auto_mode` - it's not intended to be used to control the preset's state (eg. communicate with the physical thermostat) but instead might be used to update a visual indicator, for instance. * Apply lint, clang-format, and clang-tidy fixes * Additional clang-format fixes * Wrap log related strings in LOG_STR_ARG * Add support for custom presets This also changes the configuration syntax to; ```yaml preset: # Standard preset - name: [eco | away | boost | comfort | home | sleep | activity] default_target_temperature_low: 20 ... # Custom preset - name: My custom preset default_target_temperature_low: 18 ``` For the end user there is no difference between a custom and built in preset. For developers custom presets are set via `climate.control` `custom_preset` property instead of the `preset` * Lint/clang-format/clang-tidy fixes * Additional lint/clang-format/clang-tidy fixes * Clang-tidy changes * Sort imports * Improve configuration validation for presets - Unify temperature validation across default, away, and preset configuration - Validate modes for presets have the required actions * Trigger a refresh after changing internals of the thermostat * Apply formatting fixes * Validate mode, fan_mode, and swing_mode on presets * Add preset temperature validation against visual min/max configuration * Apply code formatting fixes * Fix preset temperature validation
This commit is contained in:
parent
cd35ead890
commit
adb7aa6950
3 changed files with 373 additions and 95 deletions
|
@ -14,6 +14,7 @@ from esphome.const import (
|
||||||
CONF_DEFAULT_TARGET_TEMPERATURE_LOW,
|
CONF_DEFAULT_TARGET_TEMPERATURE_LOW,
|
||||||
CONF_DRY_ACTION,
|
CONF_DRY_ACTION,
|
||||||
CONF_DRY_MODE,
|
CONF_DRY_MODE,
|
||||||
|
CONF_FAN_MODE,
|
||||||
CONF_FAN_MODE_ON_ACTION,
|
CONF_FAN_MODE_ON_ACTION,
|
||||||
CONF_FAN_MODE_OFF_ACTION,
|
CONF_FAN_MODE_OFF_ACTION,
|
||||||
CONF_FAN_MODE_AUTO_ACTION,
|
CONF_FAN_MODE_AUTO_ACTION,
|
||||||
|
@ -37,6 +38,7 @@ from esphome.const import (
|
||||||
CONF_IDLE_ACTION,
|
CONF_IDLE_ACTION,
|
||||||
CONF_MAX_COOLING_RUN_TIME,
|
CONF_MAX_COOLING_RUN_TIME,
|
||||||
CONF_MAX_HEATING_RUN_TIME,
|
CONF_MAX_HEATING_RUN_TIME,
|
||||||
|
CONF_MAX_TEMPERATURE,
|
||||||
CONF_MIN_COOLING_OFF_TIME,
|
CONF_MIN_COOLING_OFF_TIME,
|
||||||
CONF_MIN_COOLING_RUN_TIME,
|
CONF_MIN_COOLING_RUN_TIME,
|
||||||
CONF_MIN_FAN_MODE_SWITCHING_TIME,
|
CONF_MIN_FAN_MODE_SWITCHING_TIME,
|
||||||
|
@ -45,7 +47,11 @@ from esphome.const import (
|
||||||
CONF_MIN_HEATING_OFF_TIME,
|
CONF_MIN_HEATING_OFF_TIME,
|
||||||
CONF_MIN_HEATING_RUN_TIME,
|
CONF_MIN_HEATING_RUN_TIME,
|
||||||
CONF_MIN_IDLE_TIME,
|
CONF_MIN_IDLE_TIME,
|
||||||
|
CONF_MIN_TEMPERATURE,
|
||||||
|
CONF_NAME,
|
||||||
|
CONF_MODE,
|
||||||
CONF_OFF_MODE,
|
CONF_OFF_MODE,
|
||||||
|
CONF_PRESET,
|
||||||
CONF_SENSOR,
|
CONF_SENSOR,
|
||||||
CONF_SET_POINT_MINIMUM_DIFFERENTIAL,
|
CONF_SET_POINT_MINIMUM_DIFFERENTIAL,
|
||||||
CONF_STARTUP_DELAY,
|
CONF_STARTUP_DELAY,
|
||||||
|
@ -55,11 +61,15 @@ from esphome.const import (
|
||||||
CONF_SUPPLEMENTAL_HEATING_DELTA,
|
CONF_SUPPLEMENTAL_HEATING_DELTA,
|
||||||
CONF_SWING_BOTH_ACTION,
|
CONF_SWING_BOTH_ACTION,
|
||||||
CONF_SWING_HORIZONTAL_ACTION,
|
CONF_SWING_HORIZONTAL_ACTION,
|
||||||
|
CONF_SWING_MODE,
|
||||||
CONF_SWING_OFF_ACTION,
|
CONF_SWING_OFF_ACTION,
|
||||||
CONF_SWING_VERTICAL_ACTION,
|
CONF_SWING_VERTICAL_ACTION,
|
||||||
CONF_TARGET_TEMPERATURE_CHANGE_ACTION,
|
CONF_TARGET_TEMPERATURE_CHANGE_ACTION,
|
||||||
|
CONF_VISUAL,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
CONF_PRESET_CHANGE = "preset_change"
|
||||||
|
|
||||||
CODEOWNERS = ["@kbx81"]
|
CODEOWNERS = ["@kbx81"]
|
||||||
|
|
||||||
climate_ns = cg.esphome_ns.namespace("climate")
|
climate_ns = cg.esphome_ns.namespace("climate")
|
||||||
|
@ -82,6 +92,38 @@ CLIMATE_MODES = {
|
||||||
}
|
}
|
||||||
validate_climate_mode = cv.enum(CLIMATE_MODES, upper=True)
|
validate_climate_mode = cv.enum(CLIMATE_MODES, upper=True)
|
||||||
|
|
||||||
|
ClimatePreset = climate_ns.enum("ClimatePreset")
|
||||||
|
|
||||||
|
PRESET_CONFIG_SCHEMA = cv.Schema(
|
||||||
|
{
|
||||||
|
cv.GenerateID(): cv.declare_id(ThermostatClimateTargetTempConfig),
|
||||||
|
cv.Required(CONF_NAME): cv.string_strict,
|
||||||
|
cv.Optional(CONF_MODE): validate_climate_mode,
|
||||||
|
cv.Optional(CONF_DEFAULT_TARGET_TEMPERATURE_HIGH): cv.temperature,
|
||||||
|
cv.Optional(CONF_DEFAULT_TARGET_TEMPERATURE_LOW): cv.temperature,
|
||||||
|
cv.Optional(CONF_FAN_MODE): cv.templatable(climate.validate_climate_fan_mode),
|
||||||
|
cv.Optional(CONF_SWING_MODE): cv.templatable(
|
||||||
|
climate.validate_climate_swing_mode
|
||||||
|
),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def validate_temperature_preset(preset, root_config, name, requirements):
|
||||||
|
# verify temperature settings for the provided preset / default / away configuration
|
||||||
|
for config_temp, req_actions in requirements.items():
|
||||||
|
for req_action in req_actions:
|
||||||
|
# verify corresponding default target temperature exists when a given climate action exists
|
||||||
|
if config_temp not in preset and req_action in root_config:
|
||||||
|
raise cv.Invalid(
|
||||||
|
f"{config_temp} must be defined in {name} config when using {req_action}"
|
||||||
|
)
|
||||||
|
# if a given climate action is NOT defined, it should not have a default target temperature
|
||||||
|
if config_temp in preset and req_action not in root_config:
|
||||||
|
raise cv.Invalid(
|
||||||
|
f"{config_temp} is defined in {name} config with no {req_action}"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
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
|
||||||
|
@ -235,33 +277,22 @@ def validate_thermostat(config):
|
||||||
CONF_DEFAULT_TARGET_TEMPERATURE_LOW: [CONF_HEAT_ACTION],
|
CONF_DEFAULT_TARGET_TEMPERATURE_LOW: [CONF_HEAT_ACTION],
|
||||||
}
|
}
|
||||||
|
|
||||||
for config_temp, req_actions in requirements.items():
|
# Validate temperature requirements for default configuraation
|
||||||
for req_action in req_actions:
|
validate_temperature_preset(config, config, "default", requirements)
|
||||||
# verify corresponding default target temperature exists when a given climate action exists
|
|
||||||
if config_temp not in config and req_action in config:
|
|
||||||
raise cv.Invalid(
|
|
||||||
f"{config_temp} must be defined when using {req_action}"
|
|
||||||
)
|
|
||||||
# if a given climate action is NOT defined, it should not have a default target temperature
|
|
||||||
if config_temp in config and req_action not in config:
|
|
||||||
raise cv.Invalid(f"{config_temp} is defined with no {req_action}")
|
|
||||||
|
|
||||||
|
# Validate temperature requirements for away configuration
|
||||||
if CONF_AWAY_CONFIG in config:
|
if CONF_AWAY_CONFIG in config:
|
||||||
away = config[CONF_AWAY_CONFIG]
|
away = config[CONF_AWAY_CONFIG]
|
||||||
for config_temp, req_actions in requirements.items():
|
validate_temperature_preset(away, config, "away", requirements)
|
||||||
for req_action in req_actions:
|
|
||||||
# verify corresponding default target temperature exists when a given climate action exists
|
# Validate temperature requirements for presets
|
||||||
if config_temp not in away and req_action in config:
|
if CONF_PRESET in config:
|
||||||
raise cv.Invalid(
|
for preset_config in config[CONF_PRESET]:
|
||||||
f"{config_temp} must be defined in away configuration when using {req_action}"
|
validate_temperature_preset(
|
||||||
)
|
preset_config, config, preset_config[CONF_NAME], requirements
|
||||||
# if a given climate action is NOT defined, it should not have a default target temperature
|
|
||||||
if config_temp in away and req_action not in config:
|
|
||||||
raise cv.Invalid(
|
|
||||||
f"{config_temp} is defined in away configuration with no {req_action}"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# verify default climate mode is valid given above configuration
|
# Verify default climate mode is valid given above configuration
|
||||||
default_mode = config[CONF_DEFAULT_MODE]
|
default_mode = config[CONF_DEFAULT_MODE]
|
||||||
requirements = {
|
requirements = {
|
||||||
"HEAT_COOL": [CONF_COOL_ACTION, CONF_HEAT_ACTION],
|
"HEAT_COOL": [CONF_COOL_ACTION, CONF_HEAT_ACTION],
|
||||||
|
@ -270,13 +301,108 @@ def validate_thermostat(config):
|
||||||
"DRY": [CONF_DRY_ACTION],
|
"DRY": [CONF_DRY_ACTION],
|
||||||
"FAN_ONLY": [CONF_FAN_ONLY_ACTION],
|
"FAN_ONLY": [CONF_FAN_ONLY_ACTION],
|
||||||
"AUTO": [CONF_COOL_ACTION, CONF_HEAT_ACTION],
|
"AUTO": [CONF_COOL_ACTION, CONF_HEAT_ACTION],
|
||||||
}.get(default_mode, [])
|
"OFF": [],
|
||||||
for req in requirements:
|
}
|
||||||
|
actions_for_default_mode = requirements.get(default_mode, [])
|
||||||
|
for req in actions_for_default_mode:
|
||||||
if req not in config:
|
if req not in config:
|
||||||
raise cv.Invalid(
|
raise cv.Invalid(
|
||||||
f"{CONF_DEFAULT_MODE} is set to {default_mode} but {req} is not present in the configuration"
|
f"{CONF_DEFAULT_MODE} is set to {default_mode} but {req} is not present in the configuration"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Verify that the modes for presets are valid given the configuration
|
||||||
|
if CONF_PRESET in config:
|
||||||
|
# Preset temperature vs Visual temperature validation
|
||||||
|
|
||||||
|
# Default visual configuration from climate_traits.h
|
||||||
|
visual_min_temperature = 10.0
|
||||||
|
visual_max_temperature = 30.0
|
||||||
|
if CONF_VISUAL in config:
|
||||||
|
visual_config = config[CONF_VISUAL]
|
||||||
|
|
||||||
|
if CONF_MIN_TEMPERATURE in visual_config:
|
||||||
|
visual_min_temperature = visual_config[CONF_MIN_TEMPERATURE]
|
||||||
|
|
||||||
|
if CONF_MAX_TEMPERATURE in visual_config:
|
||||||
|
visual_max_temperature = visual_config[CONF_MAX_TEMPERATURE]
|
||||||
|
|
||||||
|
for preset_config in config[CONF_PRESET]:
|
||||||
|
if CONF_DEFAULT_TARGET_TEMPERATURE_LOW in preset_config:
|
||||||
|
preset_min_temperature = preset_config[
|
||||||
|
CONF_DEFAULT_TARGET_TEMPERATURE_LOW
|
||||||
|
]
|
||||||
|
if preset_min_temperature < visual_min_temperature:
|
||||||
|
raise cv.Invalid(
|
||||||
|
f"{CONF_DEFAULT_TARGET_TEMPERATURE_LOW} for {preset_config[CONF_NAME]} is set to {preset_min_temperature} which is less than the visual minimum temperature of {visual_min_temperature}"
|
||||||
|
)
|
||||||
|
|
||||||
|
if CONF_DEFAULT_TARGET_TEMPERATURE_HIGH in preset_config:
|
||||||
|
preset_max_temperature = preset_config[
|
||||||
|
CONF_DEFAULT_TARGET_TEMPERATURE_HIGH
|
||||||
|
]
|
||||||
|
if preset_max_temperature > visual_max_temperature:
|
||||||
|
raise cv.Invalid(
|
||||||
|
f"{CONF_DEFAULT_TARGET_TEMPERATURE_HIGH} for {preset_config[CONF_NAME]} is set to {preset_max_temperature} which is more than the visual maximum temperature of {visual_max_temperature}"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Mode validation
|
||||||
|
for preset_config in config[CONF_PRESET]:
|
||||||
|
if CONF_MODE not in preset_config:
|
||||||
|
continue
|
||||||
|
|
||||||
|
mode = preset_config[CONF_MODE]
|
||||||
|
|
||||||
|
for req in requirements[mode]:
|
||||||
|
if req not in config:
|
||||||
|
raise cv.Invalid(
|
||||||
|
f"{CONF_MODE} is set to {mode} for {preset_config[CONF_NAME]} but {req} is not present in the configuration"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Fan mode requirements
|
||||||
|
requirements = {
|
||||||
|
"ON": [CONF_FAN_MODE_ON_ACTION],
|
||||||
|
"OFF": [CONF_FAN_MODE_OFF_ACTION],
|
||||||
|
"AUTO": [CONF_FAN_MODE_AUTO_ACTION],
|
||||||
|
"LOW": [CONF_FAN_MODE_LOW_ACTION],
|
||||||
|
"MEDIUM": [CONF_FAN_MODE_MEDIUM_ACTION],
|
||||||
|
"HIGH": [CONF_FAN_MODE_HIGH_ACTION],
|
||||||
|
"MIDDLE": [CONF_FAN_MODE_MIDDLE_ACTION],
|
||||||
|
"FOCUS": [CONF_FAN_MODE_FOCUS_ACTION],
|
||||||
|
"DIFFUSE": [CONF_FAN_MODE_DIFFUSE_ACTION],
|
||||||
|
}
|
||||||
|
|
||||||
|
for preset_config in config[CONF_PRESET]:
|
||||||
|
if CONF_FAN_MODE not in preset_config:
|
||||||
|
continue
|
||||||
|
|
||||||
|
fan_mode = preset_config[CONF_FAN_MODE]
|
||||||
|
|
||||||
|
for req in requirements[fan_mode]:
|
||||||
|
if req not in config:
|
||||||
|
raise cv.Invalid(
|
||||||
|
f"{CONF_FAN_MODE} is set to {fan_mode} for {preset_config[CONF_NAME]} but {req} is not present in the configuration"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Swing mode requirements
|
||||||
|
requirements = {
|
||||||
|
"OFF": [CONF_SWING_OFF_ACTION],
|
||||||
|
"BOTH": [CONF_SWING_BOTH_ACTION],
|
||||||
|
"VERTICAL": [CONF_SWING_VERTICAL_ACTION],
|
||||||
|
"HORIZONTAL": [CONF_SWING_HORIZONTAL_ACTION],
|
||||||
|
}
|
||||||
|
|
||||||
|
for preset_config in config[CONF_PRESET]:
|
||||||
|
if CONF_SWING_MODE not in preset_config:
|
||||||
|
continue
|
||||||
|
|
||||||
|
swing_mode = preset_config[CONF_SWING_MODE]
|
||||||
|
|
||||||
|
for req in requirements[swing_mode]:
|
||||||
|
if req not in config:
|
||||||
|
raise cv.Invalid(
|
||||||
|
f"{CONF_SWING_MODE} is set to {swing_mode} for {preset_config[CONF_NAME]} but {req} is not present in the configuration"
|
||||||
|
)
|
||||||
|
|
||||||
if config[CONF_FAN_WITH_COOLING] is True and CONF_FAN_ONLY_ACTION not in config:
|
if config[CONF_FAN_WITH_COOLING] is True and CONF_FAN_ONLY_ACTION not in config:
|
||||||
raise cv.Invalid(
|
raise cv.Invalid(
|
||||||
f"{CONF_FAN_ONLY_ACTION} must be defined to use {CONF_FAN_WITH_COOLING}"
|
f"{CONF_FAN_ONLY_ACTION} must be defined to use {CONF_FAN_WITH_COOLING}"
|
||||||
|
@ -415,6 +541,10 @@ CONFIG_SCHEMA = cv.All(
|
||||||
cv.Optional(CONF_DEFAULT_TARGET_TEMPERATURE_LOW): cv.temperature,
|
cv.Optional(CONF_DEFAULT_TARGET_TEMPERATURE_LOW): cv.temperature,
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
|
cv.Optional(CONF_PRESET): cv.ensure_list(PRESET_CONFIG_SCHEMA),
|
||||||
|
cv.Optional(CONF_PRESET_CHANGE): automation.validate_automation(
|
||||||
|
single=True
|
||||||
|
),
|
||||||
}
|
}
|
||||||
).extend(cv.COMPONENT_SCHEMA),
|
).extend(cv.COMPONENT_SCHEMA),
|
||||||
cv.has_at_least_one_key(
|
cv.has_at_least_one_key(
|
||||||
|
@ -531,7 +661,7 @@ async def to_code(config):
|
||||||
cg.add(var.set_supports_fan_with_heating(config[CONF_FAN_WITH_HEATING]))
|
cg.add(var.set_supports_fan_with_heating(config[CONF_FAN_WITH_HEATING]))
|
||||||
|
|
||||||
cg.add(var.set_use_startup_delay(config[CONF_STARTUP_DELAY]))
|
cg.add(var.set_use_startup_delay(config[CONF_STARTUP_DELAY]))
|
||||||
cg.add(var.set_normal_config(normal_config))
|
cg.add(var.set_preset_config(ClimatePreset.CLIMATE_PRESET_HOME, normal_config))
|
||||||
|
|
||||||
await automation.build_automation(
|
await automation.build_automation(
|
||||||
var.get_idle_action_trigger(), [], config[CONF_IDLE_ACTION]
|
var.get_idle_action_trigger(), [], config[CONF_IDLE_ACTION]
|
||||||
|
@ -694,4 +824,55 @@ async def to_code(config):
|
||||||
away_config = ThermostatClimateTargetTempConfig(
|
away_config = ThermostatClimateTargetTempConfig(
|
||||||
away[CONF_DEFAULT_TARGET_TEMPERATURE_LOW]
|
away[CONF_DEFAULT_TARGET_TEMPERATURE_LOW]
|
||||||
)
|
)
|
||||||
cg.add(var.set_away_config(away_config))
|
cg.add(var.set_preset_config(ClimatePreset.CLIMATE_PRESET_AWAY, away_config))
|
||||||
|
|
||||||
|
if CONF_PRESET in config:
|
||||||
|
for preset_config in config[CONF_PRESET]:
|
||||||
|
|
||||||
|
name = preset_config[CONF_NAME]
|
||||||
|
standard_preset = None
|
||||||
|
if name.upper() in climate.CLIMATE_PRESETS:
|
||||||
|
standard_preset = climate.CLIMATE_PRESETS[name.upper()]
|
||||||
|
|
||||||
|
if two_points_available is True:
|
||||||
|
preset_target_config = ThermostatClimateTargetTempConfig(
|
||||||
|
preset_config[CONF_DEFAULT_TARGET_TEMPERATURE_LOW],
|
||||||
|
preset_config[CONF_DEFAULT_TARGET_TEMPERATURE_HIGH],
|
||||||
|
)
|
||||||
|
elif CONF_DEFAULT_TARGET_TEMPERATURE_HIGH in preset_config:
|
||||||
|
preset_target_config = ThermostatClimateTargetTempConfig(
|
||||||
|
preset_config[CONF_DEFAULT_TARGET_TEMPERATURE_HIGH]
|
||||||
|
)
|
||||||
|
elif CONF_DEFAULT_TARGET_TEMPERATURE_LOW in preset_config:
|
||||||
|
preset_target_config = ThermostatClimateTargetTempConfig(
|
||||||
|
preset_config[CONF_DEFAULT_TARGET_TEMPERATURE_LOW]
|
||||||
|
)
|
||||||
|
|
||||||
|
preset_target_variable = cg.new_variable(
|
||||||
|
preset_config[CONF_ID], preset_target_config
|
||||||
|
)
|
||||||
|
|
||||||
|
if CONF_MODE in preset_config:
|
||||||
|
cg.add(preset_target_variable.set_mode(preset_config[CONF_MODE]))
|
||||||
|
|
||||||
|
if CONF_FAN_MODE in preset_config:
|
||||||
|
cg.add(
|
||||||
|
preset_target_variable.set_fan_mode(preset_config[CONF_FAN_MODE])
|
||||||
|
)
|
||||||
|
|
||||||
|
if CONF_SWING_MODE in preset_config:
|
||||||
|
cg.add(
|
||||||
|
preset_target_variable.set_swing_mode(
|
||||||
|
preset_config[CONF_SWING_MODE]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
if standard_preset is not None:
|
||||||
|
cg.add(var.set_preset_config(standard_preset, preset_target_variable))
|
||||||
|
else:
|
||||||
|
cg.add(var.set_custom_preset_config(name, preset_target_variable))
|
||||||
|
|
||||||
|
if CONF_PRESET_CHANGE in config:
|
||||||
|
await automation.build_automation(
|
||||||
|
var.get_preset_change_trigger(), [], config[CONF_PRESET_CHANGE]
|
||||||
|
)
|
||||||
|
|
|
@ -32,7 +32,7 @@ void ThermostatClimate::setup() {
|
||||||
} else {
|
} else {
|
||||||
// restore from defaults, change_away handles temps for us
|
// restore from defaults, change_away handles temps for us
|
||||||
this->mode = this->default_mode_;
|
this->mode = this->default_mode_;
|
||||||
this->change_away_(false);
|
this->change_preset_(climate::CLIMATE_PRESET_HOME);
|
||||||
}
|
}
|
||||||
// refresh the climate action based on the restored settings, we'll publish_state() later
|
// refresh the climate action based on the restored settings, we'll publish_state() later
|
||||||
this->switch_to_action_(this->compute_action_(), false);
|
this->switch_to_action_(this->compute_action_(), false);
|
||||||
|
@ -162,11 +162,20 @@ void ThermostatClimate::control(const climate::ClimateCall &call) {
|
||||||
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_away_(*call.get_preset() == climate::CLIMATE_PRESET_AWAY);
|
this->change_preset_(*call.get_preset());
|
||||||
} else {
|
} else {
|
||||||
this->preset = *call.get_preset();
|
this->preset = *call.get_preset();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (call.get_custom_preset().has_value()) {
|
||||||
|
// setup_complete_ blocks modifying/resetting the temps immediately after boot
|
||||||
|
if (this->setup_complete_) {
|
||||||
|
this->change_custom_preset_(*call.get_custom_preset());
|
||||||
|
} else {
|
||||||
|
this->custom_preset = *call.get_custom_preset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (call.get_mode().has_value())
|
if (call.get_mode().has_value())
|
||||||
this->mode = *call.get_mode();
|
this->mode = *call.get_mode();
|
||||||
if (call.get_fan_mode().has_value())
|
if (call.get_fan_mode().has_value())
|
||||||
|
@ -236,8 +245,12 @@ climate::ClimateTraits ThermostatClimate::traits() {
|
||||||
if (supports_swing_mode_vertical_)
|
if (supports_swing_mode_vertical_)
|
||||||
traits.add_supported_swing_mode(climate::CLIMATE_SWING_VERTICAL);
|
traits.add_supported_swing_mode(climate::CLIMATE_SWING_VERTICAL);
|
||||||
|
|
||||||
if (supports_away_)
|
for (auto &it : this->preset_config_) {
|
||||||
traits.set_supported_presets({climate::CLIMATE_PRESET_HOME, climate::CLIMATE_PRESET_AWAY});
|
traits.add_supported_preset(it.first);
|
||||||
|
}
|
||||||
|
for (auto &it : this->custom_preset_config_) {
|
||||||
|
traits.add_supported_custom_preset(it.first);
|
||||||
|
}
|
||||||
|
|
||||||
traits.set_supports_two_point_target_temperature(this->supports_two_points_);
|
traits.set_supports_two_point_target_temperature(this->supports_two_points_);
|
||||||
traits.set_supports_action(true);
|
traits.set_supports_action(true);
|
||||||
|
@ -910,30 +923,112 @@ bool ThermostatClimate::supplemental_heating_required_() {
|
||||||
(this->supplemental_action_ == climate::CLIMATE_ACTION_HEATING));
|
(this->supplemental_action_ == climate::CLIMATE_ACTION_HEATING));
|
||||||
}
|
}
|
||||||
|
|
||||||
void ThermostatClimate::change_away_(bool away) {
|
void ThermostatClimate::dump_preset_config_(const std::string &preset,
|
||||||
if (!away) {
|
const ThermostatClimateTargetTempConfig &config) {
|
||||||
|
const auto *preset_name = preset.c_str();
|
||||||
|
|
||||||
|
if (this->supports_heat_) {
|
||||||
if (this->supports_two_points_) {
|
if (this->supports_two_points_) {
|
||||||
this->target_temperature_low = this->normal_config_.default_temperature_low;
|
ESP_LOGCONFIG(TAG, " %s Default Target Temperature Low: %.1f°C", preset_name,
|
||||||
this->target_temperature_high = this->normal_config_.default_temperature_high;
|
config.default_temperature_low);
|
||||||
} else
|
|
||||||
this->target_temperature = this->normal_config_.default_temperature;
|
|
||||||
} else {
|
} else {
|
||||||
if (this->supports_two_points_) {
|
ESP_LOGCONFIG(TAG, " %s Default Target Temperature Low: %.1f°C", preset_name, config.default_temperature);
|
||||||
this->target_temperature_low = this->away_config_.default_temperature_low;
|
}
|
||||||
this->target_temperature_high = this->away_config_.default_temperature_high;
|
}
|
||||||
} else
|
if ((this->supports_cool_) || (this->supports_fan_only_)) {
|
||||||
this->target_temperature = this->away_config_.default_temperature;
|
if (this->supports_two_points_) {
|
||||||
|
ESP_LOGCONFIG(TAG, " %s Default Target Temperature High: %.1f°C", preset_name,
|
||||||
|
config.default_temperature_high);
|
||||||
|
} else {
|
||||||
|
ESP_LOGCONFIG(TAG, " %s Default Target Temperature High: %.1f°C", preset_name, config.default_temperature);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.mode_.has_value()) {
|
||||||
|
ESP_LOGCONFIG(TAG, " %s Default Mode: %s", preset_name,
|
||||||
|
LOG_STR_ARG(climate::climate_mode_to_string(*config.mode_)));
|
||||||
|
}
|
||||||
|
if (config.fan_mode_.has_value()) {
|
||||||
|
ESP_LOGCONFIG(TAG, " %s Default Fan Mode: %s", preset_name,
|
||||||
|
LOG_STR_ARG(climate::climate_fan_mode_to_string(*config.fan_mode_)));
|
||||||
|
}
|
||||||
|
if (config.swing_mode_.has_value()) {
|
||||||
|
ESP_LOGCONFIG(TAG, " %s Default Swing Mode: %s", preset_name,
|
||||||
|
LOG_STR_ARG(climate::climate_swing_mode_to_string(*config.swing_mode_)));
|
||||||
}
|
}
|
||||||
this->preset = away ? climate::CLIMATE_PRESET_AWAY : climate::CLIMATE_PRESET_HOME;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ThermostatClimate::set_normal_config(const ThermostatClimateTargetTempConfig &normal_config) {
|
void ThermostatClimate::change_preset_(climate::ClimatePreset preset) {
|
||||||
this->normal_config_ = normal_config;
|
auto config = this->preset_config_.find(preset);
|
||||||
|
|
||||||
|
if (config != this->preset_config_.end()) {
|
||||||
|
ESP_LOGI(TAG, "Switching to preset %s", LOG_STR_ARG(climate::climate_preset_to_string(preset)));
|
||||||
|
this->change_preset_internal_(config->second);
|
||||||
|
|
||||||
|
this->custom_preset.reset();
|
||||||
|
this->preset = preset;
|
||||||
|
} else {
|
||||||
|
ESP_LOGE(TAG, "Preset %s is not configured, ignoring.", LOG_STR_ARG(climate::climate_preset_to_string(preset)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ThermostatClimate::set_away_config(const ThermostatClimateTargetTempConfig &away_config) {
|
void ThermostatClimate::change_custom_preset_(const std::string &custom_preset) {
|
||||||
this->supports_away_ = true;
|
auto config = this->custom_preset_config_.find(custom_preset);
|
||||||
this->away_config_ = away_config;
|
|
||||||
|
if (config != this->custom_preset_config_.end()) {
|
||||||
|
ESP_LOGI(TAG, "Switching to custom preset %s", custom_preset.c_str());
|
||||||
|
this->change_preset_internal_(config->second);
|
||||||
|
|
||||||
|
this->preset.reset();
|
||||||
|
this->custom_preset = custom_preset;
|
||||||
|
} else {
|
||||||
|
ESP_LOGE(TAG, "Custom Preset %s is not configured, ignoring.", custom_preset.c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ThermostatClimate::change_preset_internal_(const ThermostatClimateTargetTempConfig &config) {
|
||||||
|
if (this->supports_two_points_) {
|
||||||
|
this->target_temperature_low = config.default_temperature_low;
|
||||||
|
this->target_temperature_high = config.default_temperature_high;
|
||||||
|
} else {
|
||||||
|
this->target_temperature = config.default_temperature;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note: The mode, fan_mode, and swing_mode can all be defined on the preset but if the climate.control call
|
||||||
|
// also specifies them then the control's version will override these for that call
|
||||||
|
if (config.mode_.has_value()) {
|
||||||
|
this->mode = *config.mode_;
|
||||||
|
ESP_LOGV(TAG, "Setting mode to %s", LOG_STR_ARG(climate::climate_mode_to_string(*config.mode_)));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.fan_mode_.has_value()) {
|
||||||
|
this->fan_mode = *config.fan_mode_;
|
||||||
|
ESP_LOGV(TAG, "Setting fan mode to %s", LOG_STR_ARG(climate::climate_fan_mode_to_string(*config.fan_mode_)));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.swing_mode_.has_value()) {
|
||||||
|
ESP_LOGV(TAG, "Setting swing mode to %s", LOG_STR_ARG(climate::climate_swing_mode_to_string(*config.swing_mode_)));
|
||||||
|
this->swing_mode = *config.swing_mode_;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fire any preset changed trigger if defined
|
||||||
|
if (this->preset != preset) {
|
||||||
|
Trigger<> *trig = this->preset_change_trigger_;
|
||||||
|
assert(trig != nullptr);
|
||||||
|
trig->trigger();
|
||||||
|
}
|
||||||
|
|
||||||
|
this->refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ThermostatClimate::set_preset_config(climate::ClimatePreset preset,
|
||||||
|
const ThermostatClimateTargetTempConfig &config) {
|
||||||
|
this->preset_config_[preset] = config;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ThermostatClimate::set_custom_preset_config(const std::string &name,
|
||||||
|
const ThermostatClimateTargetTempConfig &config) {
|
||||||
|
this->custom_preset_config_[name] = config;
|
||||||
}
|
}
|
||||||
|
|
||||||
ThermostatClimate::ThermostatClimate()
|
ThermostatClimate::ThermostatClimate()
|
||||||
|
@ -963,7 +1058,8 @@ ThermostatClimate::ThermostatClimate()
|
||||||
swing_mode_off_trigger_(new Trigger<>()),
|
swing_mode_off_trigger_(new Trigger<>()),
|
||||||
swing_mode_horizontal_trigger_(new Trigger<>()),
|
swing_mode_horizontal_trigger_(new Trigger<>()),
|
||||||
swing_mode_vertical_trigger_(new Trigger<>()),
|
swing_mode_vertical_trigger_(new Trigger<>()),
|
||||||
temperature_change_trigger_(new Trigger<>()) {}
|
temperature_change_trigger_(new Trigger<>()),
|
||||||
|
preset_change_trigger_(new Trigger<>()) {}
|
||||||
|
|
||||||
void ThermostatClimate::set_default_mode(climate::ClimateMode default_mode) { this->default_mode_ = default_mode; }
|
void ThermostatClimate::set_default_mode(climate::ClimateMode default_mode) { this->default_mode_ = default_mode; }
|
||||||
void ThermostatClimate::set_set_point_minimum_differential(float differential) {
|
void ThermostatClimate::set_set_point_minimum_differential(float differential) {
|
||||||
|
@ -1112,23 +1208,11 @@ Trigger<> *ThermostatClimate::get_swing_mode_off_trigger() const { return this->
|
||||||
Trigger<> *ThermostatClimate::get_swing_mode_horizontal_trigger() const { return this->swing_mode_horizontal_trigger_; }
|
Trigger<> *ThermostatClimate::get_swing_mode_horizontal_trigger() const { return this->swing_mode_horizontal_trigger_; }
|
||||||
Trigger<> *ThermostatClimate::get_swing_mode_vertical_trigger() const { return this->swing_mode_vertical_trigger_; }
|
Trigger<> *ThermostatClimate::get_swing_mode_vertical_trigger() const { return this->swing_mode_vertical_trigger_; }
|
||||||
Trigger<> *ThermostatClimate::get_temperature_change_trigger() const { return this->temperature_change_trigger_; }
|
Trigger<> *ThermostatClimate::get_temperature_change_trigger() const { return this->temperature_change_trigger_; }
|
||||||
|
Trigger<> *ThermostatClimate::get_preset_change_trigger() const { return this->preset_change_trigger_; }
|
||||||
|
|
||||||
void ThermostatClimate::dump_config() {
|
void ThermostatClimate::dump_config() {
|
||||||
LOG_CLIMATE("", "Thermostat", this);
|
LOG_CLIMATE("", "Thermostat", this);
|
||||||
if (this->supports_heat_) {
|
|
||||||
if (this->supports_two_points_) {
|
|
||||||
ESP_LOGCONFIG(TAG, " Default Target Temperature Low: %.1f°C", this->normal_config_.default_temperature_low);
|
|
||||||
} else {
|
|
||||||
ESP_LOGCONFIG(TAG, " Default Target Temperature Low: %.1f°C", this->normal_config_.default_temperature);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ((this->supports_cool_) || (this->supports_fan_only_ && this->supports_fan_only_cooling_)) {
|
|
||||||
if (this->supports_two_points_) {
|
|
||||||
ESP_LOGCONFIG(TAG, " Default Target Temperature High: %.1f°C", this->normal_config_.default_temperature_high);
|
|
||||||
} else {
|
|
||||||
ESP_LOGCONFIG(TAG, " Default Target Temperature High: %.1f°C", this->normal_config_.default_temperature);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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, " Start-up Delay Enabled: %s", YESNO(this->use_startup_delay_));
|
ESP_LOGCONFIG(TAG, " Start-up Delay Enabled: %s", YESNO(this->use_startup_delay_));
|
||||||
|
@ -1194,24 +1278,21 @@ void ThermostatClimate::dump_config() {
|
||||||
ESP_LOGCONFIG(TAG, " Supports SWING MODE HORIZONTAL: %s", YESNO(this->supports_swing_mode_horizontal_));
|
ESP_LOGCONFIG(TAG, " Supports SWING MODE HORIZONTAL: %s", YESNO(this->supports_swing_mode_horizontal_));
|
||||||
ESP_LOGCONFIG(TAG, " Supports SWING MODE VERTICAL: %s", YESNO(this->supports_swing_mode_vertical_));
|
ESP_LOGCONFIG(TAG, " Supports SWING MODE VERTICAL: %s", YESNO(this->supports_swing_mode_vertical_));
|
||||||
ESP_LOGCONFIG(TAG, " Supports TWO SET POINTS: %s", YESNO(this->supports_two_points_));
|
ESP_LOGCONFIG(TAG, " Supports TWO SET POINTS: %s", YESNO(this->supports_two_points_));
|
||||||
ESP_LOGCONFIG(TAG, " Supports AWAY mode: %s", YESNO(this->supports_away_));
|
|
||||||
if (this->supports_away_) {
|
ESP_LOGCONFIG(TAG, " Supported PRESETS: ");
|
||||||
if (this->supports_heat_) {
|
for (auto &it : this->preset_config_) {
|
||||||
if (this->supports_two_points_) {
|
const auto *preset_name = LOG_STR_ARG(climate::climate_preset_to_string(it.first));
|
||||||
ESP_LOGCONFIG(TAG, " Away Default Target Temperature Low: %.1f°C",
|
|
||||||
this->away_config_.default_temperature_low);
|
ESP_LOGCONFIG(TAG, " Supports %s: %s", preset_name, YESNO(true));
|
||||||
} else {
|
this->dump_preset_config_(preset_name, it.second);
|
||||||
ESP_LOGCONFIG(TAG, " Away Default Target Temperature Low: %.1f°C", this->away_config_.default_temperature);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ((this->supports_cool_) || (this->supports_fan_only_)) {
|
|
||||||
if (this->supports_two_points_) {
|
|
||||||
ESP_LOGCONFIG(TAG, " Away Default Target Temperature High: %.1f°C",
|
|
||||||
this->away_config_.default_temperature_high);
|
|
||||||
} else {
|
|
||||||
ESP_LOGCONFIG(TAG, " Away Default Target Temperature High: %.1f°C", this->away_config_.default_temperature);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ESP_LOGCONFIG(TAG, " Supported CUSTOM PRESETS: ");
|
||||||
|
for (auto &it : this->custom_preset_config_) {
|
||||||
|
const auto *preset_name = it.first.c_str();
|
||||||
|
|
||||||
|
ESP_LOGCONFIG(TAG, " Supports %s: %s", preset_name, YESNO(true));
|
||||||
|
this->dump_preset_config_(preset_name, it.second);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
#include "esphome/core/automation.h"
|
#include "esphome/core/automation.h"
|
||||||
#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 <map>
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace thermostat {
|
namespace thermostat {
|
||||||
|
@ -34,6 +35,10 @@ struct ThermostatClimateTargetTempConfig {
|
||||||
ThermostatClimateTargetTempConfig(float default_temperature);
|
ThermostatClimateTargetTempConfig(float default_temperature);
|
||||||
ThermostatClimateTargetTempConfig(float default_temperature_low, float default_temperature_high);
|
ThermostatClimateTargetTempConfig(float default_temperature_low, float default_temperature_high);
|
||||||
|
|
||||||
|
void set_fan_mode(climate::ClimateFanMode fan_mode) { this->fan_mode_ = fan_mode; }
|
||||||
|
void set_swing_mode(climate::ClimateSwingMode swing_mode) { this->swing_mode_ = swing_mode; }
|
||||||
|
void set_mode(climate::ClimateMode mode) { this->mode_ = mode; }
|
||||||
|
|
||||||
float default_temperature{NAN};
|
float default_temperature{NAN};
|
||||||
float default_temperature_low{NAN};
|
float default_temperature_low{NAN};
|
||||||
float default_temperature_high{NAN};
|
float default_temperature_high{NAN};
|
||||||
|
@ -41,6 +46,9 @@ struct ThermostatClimateTargetTempConfig {
|
||||||
float cool_overrun_{NAN};
|
float cool_overrun_{NAN};
|
||||||
float heat_deadband_{NAN};
|
float heat_deadband_{NAN};
|
||||||
float heat_overrun_{NAN};
|
float heat_overrun_{NAN};
|
||||||
|
optional<climate::ClimateFanMode> fan_mode_{};
|
||||||
|
optional<climate::ClimateSwingMode> swing_mode_{};
|
||||||
|
optional<climate::ClimateMode> mode_{};
|
||||||
};
|
};
|
||||||
|
|
||||||
class ThermostatClimate : public climate::Climate, public Component {
|
class ThermostatClimate : public climate::Climate, public Component {
|
||||||
|
@ -94,8 +102,8 @@ class ThermostatClimate : public climate::Climate, public Component {
|
||||||
void set_supports_swing_mode_vertical(bool supports_swing_mode_vertical);
|
void set_supports_swing_mode_vertical(bool supports_swing_mode_vertical);
|
||||||
void set_supports_two_points(bool supports_two_points);
|
void set_supports_two_points(bool supports_two_points);
|
||||||
|
|
||||||
void set_normal_config(const ThermostatClimateTargetTempConfig &normal_config);
|
void set_preset_config(climate::ClimatePreset preset, const ThermostatClimateTargetTempConfig &config);
|
||||||
void set_away_config(const ThermostatClimateTargetTempConfig &away_config);
|
void set_custom_preset_config(const std::string &name, const ThermostatClimateTargetTempConfig &config);
|
||||||
|
|
||||||
Trigger<> *get_cool_action_trigger() const;
|
Trigger<> *get_cool_action_trigger() const;
|
||||||
Trigger<> *get_supplemental_cool_action_trigger() const;
|
Trigger<> *get_supplemental_cool_action_trigger() const;
|
||||||
|
@ -124,6 +132,7 @@ class ThermostatClimate : public climate::Climate, public Component {
|
||||||
Trigger<> *get_swing_mode_off_trigger() const;
|
Trigger<> *get_swing_mode_off_trigger() const;
|
||||||
Trigger<> *get_swing_mode_vertical_trigger() const;
|
Trigger<> *get_swing_mode_vertical_trigger() const;
|
||||||
Trigger<> *get_temperature_change_trigger() const;
|
Trigger<> *get_temperature_change_trigger() const;
|
||||||
|
Trigger<> *get_preset_change_trigger() const;
|
||||||
/// Get current hysteresis values
|
/// Get current hysteresis values
|
||||||
float cool_deadband();
|
float cool_deadband();
|
||||||
float cool_overrun();
|
float cool_overrun();
|
||||||
|
@ -149,8 +158,14 @@ class ThermostatClimate : public climate::Climate, public Component {
|
||||||
/// Override control to change settings of the climate device.
|
/// Override control to change settings of the climate device.
|
||||||
void control(const climate::ClimateCall &call) override;
|
void control(const climate::ClimateCall &call) override;
|
||||||
|
|
||||||
/// Change the away setting, will reset target temperatures to defaults.
|
/// Change to a provided preset setting; will reset temperature, mode, fan, and swing modes accordingly
|
||||||
void change_away_(bool away);
|
void change_preset_(climate::ClimatePreset preset);
|
||||||
|
/// Change to a provided custom preset setting; will reset temperature, mode, fan, and swing modes accordingly
|
||||||
|
void change_custom_preset_(const std::string &custom_preset);
|
||||||
|
|
||||||
|
/// Applies the temperature, mode, fan, and swing modes of the provded config.
|
||||||
|
/// This is agnostic of custom vs built in preset
|
||||||
|
void change_preset_internal_(const ThermostatClimateTargetTempConfig &config);
|
||||||
|
|
||||||
/// Return the traits of this controller.
|
/// Return the traits of this controller.
|
||||||
climate::ClimateTraits traits() override;
|
climate::ClimateTraits traits() override;
|
||||||
|
@ -210,6 +225,8 @@ 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 std::string &preset_name, const ThermostatClimateTargetTempConfig &config);
|
||||||
|
|
||||||
/// The sensor used for getting the current temperature
|
/// The sensor used for getting the current temperature
|
||||||
sensor::Sensor *sensor_{nullptr};
|
sensor::Sensor *sensor_{nullptr};
|
||||||
|
|
||||||
|
@ -267,11 +284,6 @@ class ThermostatClimate : public climate::Climate, public Component {
|
||||||
/// A false value means that the controller has no such support.
|
/// A false value means that the controller has no such support.
|
||||||
bool supports_two_points_{false};
|
bool supports_two_points_{false};
|
||||||
|
|
||||||
/// Whether the controller supports an "away" mode
|
|
||||||
///
|
|
||||||
/// A false value means that the controller has no such mode.
|
|
||||||
bool supports_away_{false};
|
|
||||||
|
|
||||||
/// Flags indicating if maximum allowable run time was exceeded
|
/// Flags indicating if maximum allowable run time was exceeded
|
||||||
bool cooling_max_runtime_exceeded_{false};
|
bool cooling_max_runtime_exceeded_{false};
|
||||||
bool heating_max_runtime_exceeded_{false};
|
bool heating_max_runtime_exceeded_{false};
|
||||||
|
@ -368,6 +380,9 @@ class ThermostatClimate : public climate::Climate, public Component {
|
||||||
/// The trigger to call when the target temperature(s) change(es).
|
/// The trigger to call when the target temperature(s) change(es).
|
||||||
Trigger<> *temperature_change_trigger_{nullptr};
|
Trigger<> *temperature_change_trigger_{nullptr};
|
||||||
|
|
||||||
|
/// The triggr to call when the preset mode changes
|
||||||
|
Trigger<> *preset_change_trigger_{nullptr};
|
||||||
|
|
||||||
/// A reference to the trigger that was previously active.
|
/// A reference to the trigger that was previously active.
|
||||||
///
|
///
|
||||||
/// This is so that the previous trigger can be stopped before enabling a new one
|
/// This is so that the previous trigger can be stopped before enabling a new one
|
||||||
|
@ -409,10 +424,6 @@ class ThermostatClimate : public climate::Climate, public Component {
|
||||||
/// 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};
|
||||||
|
|
||||||
/// Temperature data for normal/home and away modes
|
|
||||||
ThermostatClimateTargetTempConfig normal_config_{};
|
|
||||||
ThermostatClimateTargetTempConfig away_config_{};
|
|
||||||
|
|
||||||
/// Climate action timers
|
/// Climate action timers
|
||||||
std::vector<ThermostatClimateTimer> timer_{
|
std::vector<ThermostatClimateTimer> timer_{
|
||||||
{"cool_run", false, 0, std::bind(&ThermostatClimate::cooling_max_run_time_timer_callback_, this)},
|
{"cool_run", false, 0, std::bind(&ThermostatClimate::cooling_max_run_time_timer_callback_, this)},
|
||||||
|
@ -425,6 +436,11 @@ class ThermostatClimate : public climate::Climate, public Component {
|
||||||
{"heat_off", false, 0, std::bind(&ThermostatClimate::heating_off_timer_callback_, this)},
|
{"heat_off", false, 0, std::bind(&ThermostatClimate::heating_off_timer_callback_, this)},
|
||||||
{"heat_on", false, 0, std::bind(&ThermostatClimate::heating_on_timer_callback_, this)},
|
{"heat_on", false, 0, std::bind(&ThermostatClimate::heating_on_timer_callback_, this)},
|
||||||
{"idle_on", false, 0, std::bind(&ThermostatClimate::idle_on_timer_callback_, this)}};
|
{"idle_on", false, 0, std::bind(&ThermostatClimate::idle_on_timer_callback_, this)}};
|
||||||
|
|
||||||
|
/// The set of standard preset configurations this thermostat supports (Eg. AWAY, ECO, etc)
|
||||||
|
std::map<climate::ClimatePreset, ThermostatClimateTargetTempConfig> preset_config_{};
|
||||||
|
/// The set of custom preset configurations this thermostat supports (eg. "My Custom Preset")
|
||||||
|
std::map<std::string, ThermostatClimateTargetTempConfig> custom_preset_config_{};
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace thermostat
|
} // namespace thermostat
|
||||||
|
|
Loading…
Reference in a new issue