From e301b8d0e0af68502adb73952c79abae36b20522 Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Wed, 7 Jan 2026 21:44:10 -0600 Subject: [PATCH] [thermostat] Allow `heat_cool_mode` without an automation (#13069) Co-authored-by: J. Nick Koston --- esphome/components/thermostat/climate.py | 25 ++++++++++++++++++------ tests/components/thermostat/common.yaml | 1 + 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/esphome/components/thermostat/climate.py b/esphome/components/thermostat/climate.py index a3c155aac0..f7c1298d68 100644 --- a/esphome/components/thermostat/climate.py +++ b/esphome/components/thermostat/climate.py @@ -153,6 +153,19 @@ def generate_comparable_preset(config, name): return comparable_preset +def validate_heat_cool_mode(value) -> list: + """Validate heat_cool_mode - accepts either True or an automation.""" + if value is True: + # Convert True to empty automation list + return [] + if value is False: + raise cv.Invalid( + "heat_cool_mode cannot be 'false'. Specify 'true' to enable the mode or provide an automation" + ) + # Otherwise validate as automation + return automation.validate_automation(single=True)(value) + + def validate_thermostat(config): # verify corresponding action(s) exist(s) for any defined climate mode or action requirements = { @@ -554,9 +567,7 @@ CONFIG_SCHEMA = cv.All( cv.Optional(CONF_FAN_ONLY_MODE): automation.validate_automation( single=True ), - cv.Optional(CONF_HEAT_COOL_MODE): automation.validate_automation( - single=True - ), + cv.Optional(CONF_HEAT_COOL_MODE): validate_heat_cool_mode, cv.Optional(CONF_HEAT_MODE): automation.validate_automation(single=True), cv.Optional(CONF_OFF_MODE): automation.validate_automation(single=True), cv.Optional(CONF_FAN_MODE_ON_ACTION): automation.validate_automation( @@ -828,9 +839,11 @@ async def to_code(config): ) 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] - ) + # Build automation only if user provided actions (not just `true`) + if config[CONF_HEAT_COOL_MODE]: + await automation.build_automation( + var.get_heat_cool_mode_trigger(), [], config[CONF_HEAT_COOL_MODE] + ) cg.add(var.set_supports_heat_cool(True)) if CONF_OFF_MODE in config: await automation.build_automation( diff --git a/tests/components/thermostat/common.yaml b/tests/components/thermostat/common.yaml index 4aa87c0ac3..63bd174e14 100644 --- a/tests/components/thermostat/common.yaml +++ b/tests/components/thermostat/common.yaml @@ -41,6 +41,7 @@ climate: - logger.log: dry_mode fan_only_mode: - logger.log: fan_only_mode + heat_cool_mode: true fan_mode_auto_action: - logger.log: fan_mode_auto_action fan_mode_on_action: