From 418ae738702d44be75e9d070c748d673e2b0bc24 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 23 Sep 2026 12:18:55 +0100 Subject: [PATCH] [template] Use register_apply_action for climate.template.publish (#19495) --- .../components/template/climate/__init__.py | 76 ++++++------------- .../components/template/climate/automation.h | 57 -------------- .../template/climate/template_climate.h | 3 +- tests/components/template/common-base.yaml | 4 + 4 files changed, 30 insertions(+), 110 deletions(-) delete mode 100644 esphome/components/template/climate/automation.h diff --git a/esphome/components/template/climate/__init__.py b/esphome/components/template/climate/__init__.py index c39ea8f80e..11ffaf282c 100644 --- a/esphome/components/template/climate/__init__.py +++ b/esphome/components/template/climate/__init__.py @@ -28,8 +28,6 @@ from esphome.const import ( CONF_TARGET_TEMPERATURE_HIGH, CONF_TARGET_TEMPERATURE_LOW, ) -from esphome.core import ID -from esphome.cpp_generator import MockObj, TemplateArgsType from esphome.types import ConfigType from .. import template_ns @@ -53,11 +51,6 @@ CONF_SET_PRESET_ACTION = "set_preset_action" CONF_SET_CUSTOM_PRESET_ACTION = "set_custom_preset_action" TemplateClimate = template_ns.class_("TemplateClimate", climate.Climate, cg.Component) -TemplateClimatePublishAction = template_ns.class_( - "TemplateClimatePublishAction", - automation.Action, - cg.Parented.template(TemplateClimate), -) TemplateClimateRestoreMode = template_ns.enum( "TemplateClimateRestoreMode", is_class=True @@ -416,50 +409,31 @@ CLIMATE_TEMPLATE_PUBLISH_ACTION_SCHEMA = cv.All( ) -@automation.register_action( +automation.register_apply_action( "climate.template.publish", - TemplateClimatePublishAction, CLIMATE_TEMPLATE_PUBLISH_ACTION_SCHEMA, - synchronous=True, + automation.ApplyField( + CONF_CURRENT_TEMPERATURE, "current_temperature = {}", cg.float_ + ), + automation.ApplyField(CONF_CURRENT_HUMIDITY, "current_humidity = {}", cg.float_), + automation.ApplyField(CONF_TARGET_TEMPERATURE, "set_target_temperature", cg.float_), + automation.ApplyField( + CONF_TARGET_TEMPERATURE_LOW, "set_target_temperature_low", cg.float_ + ), + automation.ApplyField( + CONF_TARGET_TEMPERATURE_HIGH, "set_target_temperature_high", cg.float_ + ), + automation.ApplyField(CONF_TARGET_HUMIDITY, "set_target_humidity", cg.float_), + automation.ApplyField(CONF_MODE, "set_mode", climate.ClimateMode), + automation.ApplyField(CONF_ACTION, "action = {}", climate.ClimateAction), + automation.ApplyField(CONF_FAN_MODE, "set_fan_mode", climate.ClimateFanMode), + automation.ApplyField( + CONF_CUSTOM_FAN_MODE, "set_custom_fan_mode(StringRef({}))", cg.std_string + ), + automation.ApplyField(CONF_SWING_MODE, "set_swing_mode", climate.ClimateSwingMode), + automation.ApplyField(CONF_PRESET, "set_preset", climate.ClimatePreset), + automation.ApplyField( + CONF_CUSTOM_PRESET, "set_custom_preset(StringRef({}))", cg.std_string + ), + automation.ApplyCall("publish_state()"), ) -async def climate_template_publish_to_code( - config: ConfigType, - action_id: ID, - template_arg: cg.TemplateArguments, - args: TemplateArgsType, -) -> MockObj: - var = cg.new_Pvariable(action_id, template_arg) - await cg.register_parented(var, config[CONF_ID]) - - if (v := config.get(CONF_CURRENT_TEMPERATURE)) is not None: - cg.add(var.set_current_temperature(await cg.templatable(v, args, cg.float_))) - if (v := config.get(CONF_CURRENT_HUMIDITY)) is not None: - cg.add(var.set_current_humidity(await cg.templatable(v, args, cg.float_))) - if (v := config.get(CONF_TARGET_TEMPERATURE)) is not None: - cg.add(var.set_target_temperature(await cg.templatable(v, args, cg.float_))) - if (v := config.get(CONF_TARGET_TEMPERATURE_LOW)) is not None: - cg.add(var.set_target_temperature_low(await cg.templatable(v, args, cg.float_))) - if (v := config.get(CONF_TARGET_TEMPERATURE_HIGH)) is not None: - cg.add( - var.set_target_temperature_high(await cg.templatable(v, args, cg.float_)) - ) - if (v := config.get(CONF_TARGET_HUMIDITY)) is not None: - cg.add(var.set_target_humidity(await cg.templatable(v, args, cg.float_))) - if (v := config.get(CONF_MODE)) is not None: - cg.add(var.set_mode(await cg.templatable(v, args, climate.ClimateMode))) - if (v := config.get(CONF_ACTION)) is not None: - cg.add(var.set_action(await cg.templatable(v, args, climate.ClimateAction))) - if (v := config.get(CONF_FAN_MODE)) is not None: - cg.add(var.set_fan_mode(await cg.templatable(v, args, climate.ClimateFanMode))) - if (v := config.get(CONF_CUSTOM_FAN_MODE)) is not None: - cg.add(var.set_custom_fan_mode(await cg.templatable(v, args, cg.std_string))) - if (v := config.get(CONF_SWING_MODE)) is not None: - cg.add( - var.set_swing_mode(await cg.templatable(v, args, climate.ClimateSwingMode)) - ) - if (v := config.get(CONF_PRESET)) is not None: - cg.add(var.set_preset(await cg.templatable(v, args, climate.ClimatePreset))) - if (v := config.get(CONF_CUSTOM_PRESET)) is not None: - cg.add(var.set_custom_preset(await cg.templatable(v, args, cg.std_string))) - - return var diff --git a/esphome/components/template/climate/automation.h b/esphome/components/template/climate/automation.h deleted file mode 100644 index 49a79ace2f..0000000000 --- a/esphome/components/template/climate/automation.h +++ /dev/null @@ -1,57 +0,0 @@ -#pragma once - -#include "template_climate.h" -#include "esphome/core/automation.h" - -namespace esphome::template_ { - -template -class TemplateClimatePublishAction final : public Action, public Parented { - public: - TEMPLATABLE_VALUE(float, current_temperature) - TEMPLATABLE_VALUE(float, current_humidity) - TEMPLATABLE_VALUE(float, target_temperature) - TEMPLATABLE_VALUE(float, target_temperature_low) - TEMPLATABLE_VALUE(float, target_temperature_high) - TEMPLATABLE_VALUE(float, target_humidity) - TEMPLATABLE_VALUE(climate::ClimateMode, mode) - TEMPLATABLE_VALUE(climate::ClimateAction, action) - TEMPLATABLE_VALUE(climate::ClimateFanMode, fan_mode) - TEMPLATABLE_VALUE(std::string, custom_fan_mode) - TEMPLATABLE_VALUE(climate::ClimateSwingMode, swing_mode) - TEMPLATABLE_VALUE(climate::ClimatePreset, preset) - TEMPLATABLE_VALUE(std::string, custom_preset) - - void play(const Ts &...x) override { - if (this->current_temperature_.has_value()) - this->parent_->current_temperature = this->current_temperature_.value(x...); - if (this->current_humidity_.has_value()) - this->parent_->current_humidity = this->current_humidity_.value(x...); - if (this->target_temperature_.has_value()) - this->parent_->set_target_temperature(this->target_temperature_.value(x...)); - if (this->target_temperature_low_.has_value()) - this->parent_->set_target_temperature_low(this->target_temperature_low_.value(x...)); - if (this->target_temperature_high_.has_value()) - this->parent_->set_target_temperature_high(this->target_temperature_high_.value(x...)); - if (this->target_humidity_.has_value()) - this->parent_->set_target_humidity(this->target_humidity_.value(x...)); - if (this->mode_.has_value()) - this->parent_->set_mode(this->mode_.value(x...)); - if (this->action_.has_value()) - this->parent_->action = this->action_.value(x...); - if (this->fan_mode_.has_value()) - this->parent_->set_fan_mode(this->fan_mode_.value(x...)); - if (this->custom_fan_mode_.has_value()) - this->parent_->set_custom_fan_mode(StringRef(this->custom_fan_mode_.value(x...))); - if (this->swing_mode_.has_value()) - this->parent_->set_swing_mode(this->swing_mode_.value(x...)); - if (this->preset_.has_value()) - this->parent_->set_preset(this->preset_.value(x...)); - if (this->custom_preset_.has_value()) - this->parent_->set_custom_preset(StringRef(this->custom_preset_.value(x...))); - - this->parent_->publish_state(); - } -}; - -} // namespace esphome::template_ diff --git a/esphome/components/template/climate/template_climate.h b/esphome/components/template/climate/template_climate.h index 5448488c34..4cdf213315 100644 --- a/esphome/components/template/climate/template_climate.h +++ b/esphome/components/template/climate/template_climate.h @@ -50,8 +50,7 @@ class TemplateClimate final : public climate::Climate, public Component { Trigger *get_set_preset_trigger() { return &this->set_preset_trigger_; } Trigger *get_set_custom_preset_trigger() { return &this->set_custom_preset_trigger_; } - // Used by TemplateClimatePublishAction, which is not a Climate subclass and so cannot reach the - // protected setters, and by codegen to apply `initial_state:` before setup() runs. + // Public so the climate.template.publish action and the initial_state codegen can set them. void set_target_temperature(float value) { this->target_temperature = value; } void set_target_temperature_low(float value) { this->target_temperature_low = value; } void set_target_temperature_high(float value) { this->target_temperature_high = value; } diff --git a/tests/components/template/common-base.yaml b/tests/components/template/common-base.yaml index a87e2e980a..8c07b3a2b7 100644 --- a/tests/components/template/common-base.yaml +++ b/tests/components/template/common-base.yaml @@ -45,6 +45,10 @@ esphome: id: template_climate_custom_modes custom_fan_mode: "turbo" custom_preset: "eco_plus" + - climate.template.publish: + id: template_climate_custom_modes + custom_fan_mode: !lambda return std::string("turbo"); + custom_preset: !lambda return std::string("eco_plus"); # Test C++ API: set_template() with stateless lambda (no captures) # NOTE: set_template() is not intended to be a public API, but we test it to ensure it doesn't break.