[thermostat] Allow a dry-only thermostat without cool_action (#20682)

This commit is contained in:
Panoplian
2026-10-13 12:08:13 -10:00
committed by GitHub
parent a97ce3a201
commit 6562c49686
4 changed files with 104 additions and 6 deletions
+11 -6
View File
@@ -240,12 +240,6 @@ def validate_thermostat(config: ConfigType) -> ConfigType:
CONF_SUPPLEMENTAL_HEATING_ACTION,
CONF_SUPPLEMENTAL_HEATING_DELTA,
],
CONF_MIN_COOLING_OFF_TIME: [
CONF_COOL_ACTION,
],
CONF_MIN_COOLING_RUN_TIME: [
CONF_COOL_ACTION,
],
CONF_MIN_FANNING_OFF_TIME: [
CONF_FAN_ONLY_ACTION,
],
@@ -284,6 +278,17 @@ def validate_thermostat(config: ConfigType) -> ConfigType:
f"{req_trigger} must be defined to use {config_trigger}"
)
# Dry mode runs on the cooling timers, so a dehumidifier needs no cool_action.
for timer in (CONF_MIN_COOLING_OFF_TIME, CONF_MIN_COOLING_RUN_TIME):
if (
timer in config
and CONF_COOL_ACTION not in config
and CONF_DRY_ACTION not in config
):
raise cv.Invalid(
f"{CONF_COOL_ACTION} or {CONF_DRY_ACTION} must be defined to use {timer}"
)
if CONF_FAN_ONLY_ACTION in config:
# determine validation requirements based on fan_only_action_uses_fan_mode_timer setting
if config[CONF_FAN_ONLY_ACTION_USES_FAN_MODE_TIMER] is True:
@@ -0,0 +1,27 @@
---
esphome:
name: test
logger:
esp32:
board: esp32dev
sensor:
- platform: template
id: temp_sensor
lambda: return 21.0;
climate:
- platform: thermostat
id: heater
name: Heater
sensor: temp_sensor
min_idle_time: 30s
min_heating_off_time: 300s
min_heating_run_time: 300s
min_cooling_off_time: 300s
heat_action:
- logger.log: heat
idle_action:
- logger.log: idle
@@ -0,0 +1,30 @@
---
esphome:
name: test
logger:
esp32:
board: esp32dev
sensor:
- platform: template
id: temp_sensor
lambda: return 21.0;
climate:
- platform: thermostat
id: dehumidifier
name: Dehumidifier
sensor: temp_sensor
min_idle_time: 0s
min_cooling_off_time: 0s
min_cooling_run_time: 0s
min_fanning_off_time: 0s
min_fanning_run_time: 0s
idle_action:
- logger.log: idle
fan_only_action:
- logger.log: fan only
dry_action:
- logger.log: dry
@@ -0,0 +1,36 @@
"""Tests for the thermostat config validation."""
from collections.abc import Callable
from pathlib import Path
import pytest
from esphome.config import read_config
from esphome.core import CORE
def test_dry_only_config_is_valid(
generate_main: Callable[[str | Path], str],
component_config_path: Callable[[str], Path],
) -> None:
"""A dehumidifier has dry mode but no cool_action; dry mode uses the cooling timers."""
main_cpp = generate_main(component_config_path("dry_only.yaml"))
assert "dehumidifier->set_cooling_minimum_off_time_in_sec(0);" in main_cpp
assert "dehumidifier->set_cooling_minimum_run_time_in_sec(0);" in main_cpp
assert "dehumidifier->set_supports_dry(true);" in main_cpp
assert "dehumidifier->set_supports_cool(" not in main_cpp
def test_cooling_timer_needs_cool_or_dry_action(
component_config_path: Callable[[str], Path],
capsys: pytest.CaptureFixture[str],
) -> None:
"""A cooling timer with neither cool_action nor dry_action is still rejected."""
CORE.config_path = component_config_path("cooling_timer_without_action.yaml")
assert read_config({}) is None
captured = capsys.readouterr()
assert (
"cool_action or dry_action must be defined to use min_cooling_off_time"
in captured.out
)