[fan] Use register_apply_action for fan.turn_on (#19473)

This commit is contained in:
J. Nick Koston
2026-09-23 17:25:33 -05:00
committed by GitHub
parent 22efbd9778
commit a47271b1f1
3 changed files with 7 additions and 77 deletions
+6 -49
View File
@@ -31,19 +31,17 @@ from esphome.const import (
CONF_TRIGGER_ID,
CONF_WEB_SERVER,
)
from esphome.core import CORE, CoroPriority, Lambda, coroutine_with_priority
from esphome.core import CORE, CoroPriority, coroutine_with_priority
from esphome.core.entity_helpers import (
entity_duplicate_validator,
queue_entity_register,
setup_entity,
)
from esphome.cpp_generator import LambdaExpression
IS_PLATFORM_COMPONENT = True
fan_ns = cg.esphome_ns.namespace("fan")
Fan = fan_ns.class_("Fan", cg.EntityBase)
FanCall = fan_ns.class_("FanCall")
FanDirection = fan_ns.enum("FanDirection", is_class=True)
FAN_DIRECTION_ENUM = {
@@ -63,7 +61,6 @@ RESTORE_MODES = {
}
# Actions
TurnOnAction = fan_ns.class_("TurnOnAction", automation.Action)
TurnOffAction = fan_ns.class_("TurnOffAction", automation.Action)
ToggleAction = fan_ns.class_("ToggleAction", automation.Action)
CycleSpeedAction = fan_ns.class_("CycleSpeedAction", automation.Action)
@@ -332,9 +329,8 @@ async def fan_turn_off_to_code(config, action_id, template_arg, args):
return cg.new_Pvariable(action_id, template_arg, paren)
@automation.register_action(
automation.register_apply_action(
"fan.turn_on",
TurnOnAction,
maybe_simple_id(
{
cv.Required(CONF_ID): cv.use_id(Fan),
@@ -345,50 +341,11 @@ async def fan_turn_off_to_code(config, action_id, template_arg, args):
),
}
),
synchronous=True,
automation.ApplyField(CONF_OSCILLATING, "set_oscillating", cg.bool_),
automation.ApplyField(CONF_SPEED, "set_speed", cg.int_),
automation.ApplyField(CONF_DIRECTION, "set_direction", FanDirection),
call="turn_on",
)
async def fan_turn_on_to_code(config, action_id, template_arg, args):
paren = await cg.get_variable(config[CONF_ID])
# All configured fields are folded into a single stateless lambda whose
# constants live in flash; the action stores only a function pointer.
FIELDS = (
(CONF_OSCILLATING, "set_oscillating", cg.bool_),
(CONF_SPEED, "set_speed", cg.int_),
(CONF_DIRECTION, "set_direction", FanDirection),
)
# Normalize trigger args to `const std::remove_cvref_t<T> &` so the
# apply lambda and any inner field lambdas (generated below via
# `process_lambda`) share one parameter spelling that's well-formed for
# any T (value, ref, or const-ref). Matches TurnOnAction::ApplyFn.
normalized_args = [
(cg.RawExpression(f"const std::remove_cvref_t<{cg.safe_exp(t)}> &"), n)
for t, n in args
]
fwd_args = ", ".join(name for _, name in args)
body_lines: list[str] = []
for conf_key, setter, type_ in FIELDS:
if (value := config.get(conf_key)) is None:
continue
if isinstance(value, Lambda):
inner = await cg.process_lambda(value, normalized_args, return_type=type_)
body_lines.append(f"call.{setter}(({inner})({fwd_args}));")
else:
body_lines.append(f"call.{setter}({cg.safe_exp(value)});")
apply_args = [
(FanCall.operator("ref"), "call"),
*normalized_args,
]
apply_lambda = LambdaExpression(
["\n".join(body_lines)],
apply_args,
capture="",
return_type=cg.void,
)
return cg.new_Pvariable(action_id, template_arg, paren, apply_lambda)
@automation.register_action(
-27
View File
@@ -6,33 +6,6 @@
namespace esphome::fan {
// All configured fields are baked into a single stateless lambda whose
// constants live in flash. The action only stores one function pointer
// plus one parent pointer, regardless of how many fields the user set.
// Trigger args are forwarded to the apply function so user lambdas
// (e.g. `speed: !lambda "return x;"`) keep working.
//
// Trigger args are normalized to `const std::remove_cvref_t<Ts> &...` so
// the codegen can emit a matching parameter list for both the apply lambda
// and any inner field lambdas without producing invalid C++ source text
// (e.g. `const T & &` if Ts already carries a reference, or `const const
// T &` if Ts already carries a const). This keeps trigger args no-copy
// regardless of whether the trigger supplies `T`, `T &`, or `const T &`.
template<typename... Ts> class TurnOnAction final : public Action<Ts...> {
public:
using ApplyFn = void (*)(FanCall &, const std::remove_cvref_t<Ts> &...);
TurnOnAction(Fan *state, ApplyFn apply) : state_(state), apply_(apply) {}
void play(const Ts &...x) override {
auto call = this->state_->turn_on();
this->apply_(call, x...);
call.perform();
}
Fan *state_;
ApplyFn apply_;
};
template<typename... Ts> class TurnOffAction final : public Action<Ts...> {
public:
explicit TurnOffAction(Fan *state) : state_(state) {}
+1 -1
View File
@@ -67,7 +67,7 @@ binary_sensor:
return false;
# Exercise fan.turn_on with various field combinations so the
# TurnOnAction codegen paths get build coverage.
# register_apply_action codegen paths get build coverage.
button:
- platform: template
name: "Fan Speed Only"