ESPHome 2026.6.0-dev
Loading...
Searching...
No Matches
automation.h
Go to the documentation of this file.
1#pragma once
2
4#include "light_state.h"
5#include "addressable_light.h"
6
7namespace esphome::light {
8
9enum class LimitMode { CLAMP, DO_NOTHING };
10
11template<bool HasTransitionLength, typename... Ts> class ToggleAction : public Action<Ts...> {
12 public:
14
15 template<typename V> void set_transition_length(V value) requires(HasTransitionLength) {
16 this->transition_length_ = value;
17 }
18
19 void play(const Ts &...x) override {
20 auto call = this->state_->toggle();
21 if constexpr (HasTransitionLength) {
22 call.set_transition_length(this->transition_length_.optional_value(x...));
23 }
24 call.perform();
25 }
26
27 protected:
29 struct NoTransition {};
30 [[no_unique_address]] std::conditional_t<HasTransitionLength, TemplatableFn<uint32_t, Ts...>, NoTransition>
32};
33
34// All configured fields are baked into a single stateless lambda whose
35// constants live in flash. The action only stores one function pointer
36// plus one parent pointer, regardless of how many fields the user set.
37// Trigger args are forwarded to the apply function so user lambdas
38// (e.g. `brightness: !lambda "return x;"`) keep working.
39//
40// Trigger args are normalized to `const std::remove_cvref_t<Ts> &...` so
41// the codegen can emit a matching parameter list for both the apply lambda
42// and any inner field lambdas without producing invalid C++ source text
43// (e.g. `const T & &` if Ts already carries a reference, or `const const
44// T &` if Ts already carries a const). This keeps trigger args no-copy
45// regardless of whether the trigger supplies `T`, `T &`, or `const T &`.
46template<typename... Ts> class LightControlAction : public Action<Ts...> {
47 public:
48 using ApplyFn = void (*)(LightState *, LightCall &, const std::remove_cvref_t<Ts> &...);
50
51 void play(const Ts &...x) override {
52 auto call = this->parent_->make_call();
53 this->apply_(this->parent_, call, x...);
54 call.perform();
55 }
56
57 protected:
60};
61
62template<bool HasTransitionLength, typename... Ts> class DimRelativeAction : public Action<Ts...> {
63 public:
64 explicit DimRelativeAction(LightState *parent) : parent_(parent) {}
65
66 TEMPLATABLE_VALUE(float, relative_brightness)
67
68 template<typename V> void set_transition_length(V value) requires(HasTransitionLength) {
69 this->transition_length_ = value;
70 }
71
72 void play(const Ts &...x) override {
73 auto call = this->parent_->make_call();
74 float rel = this->relative_brightness_.value(x...);
75 float cur;
77 if ((limit_mode_ == LimitMode::DO_NOTHING) && ((cur < min_brightness_) || (cur > max_brightness_))) {
78 return;
79 }
80 float new_brightness = clamp(cur + rel, min_brightness_, max_brightness_);
81 call.set_state(new_brightness != 0.0f);
82 call.set_brightness(new_brightness);
83
84 if constexpr (HasTransitionLength) {
85 call.set_transition_length(this->transition_length_.optional_value(x...));
86 }
87 call.perform();
88 }
89
90 void set_min_max_brightness(float min, float max) {
91 this->min_brightness_ = min;
92 this->max_brightness_ = max;
93 }
94
95 void set_limit_mode(LimitMode limit_mode) { this->limit_mode_ = limit_mode; }
96
97 protected:
99 float min_brightness_{0.0};
100 float max_brightness_{1.0};
102 struct NoTransition {};
103 [[no_unique_address]] std::conditional_t<HasTransitionLength, TemplatableFn<uint32_t, Ts...>, NoTransition>
105};
106
107// Cycle through the light's configured effects. `Forward` selects direction
108// at compile time so the chosen branch is the only one that gets instantiated
109// per action site. `include_none` is runtime so a single set of templates
110// covers both the "wrap through None" and "skip None" variants.
111template<bool Forward, typename... Ts> class LightEffectCycleAction : public Action<Ts...> {
112 public:
113 explicit LightEffectCycleAction(LightState *parent) : parent_(parent) {}
114
115 void set_include_none(bool include_none) { this->include_none_ = include_none; }
116
117 void play(const Ts &...) override {
118 size_t count = this->parent_->get_effect_count();
119 if (count == 0) {
120 return;
121 }
122 uint32_t current = this->parent_->get_current_effect_index();
123 uint32_t next;
124 if (this->include_none_) {
125 uint32_t total = static_cast<uint32_t>(count) + 1;
126 if constexpr (Forward) {
127 next = (current + 1) % total;
128 } else {
129 next = (current + total - 1) % total;
130 }
131 } else {
132 if constexpr (Forward) {
133 next = (current % static_cast<uint32_t>(count)) + 1;
134 } else {
135 next = (current <= 1) ? static_cast<uint32_t>(count) : current - 1;
136 }
137 }
138 auto call = this->parent_->turn_on();
139 call.set_effect(next);
140 call.perform();
141 }
142
143 protected:
145 bool include_none_{false};
146};
147
148template<typename... Ts> class LightIsOnCondition : public Condition<Ts...> {
149 public:
151 bool check(const Ts &...x) override { return this->state_->current_values.is_on(); }
152
153 protected:
155};
156template<typename... Ts> class LightIsOffCondition : public Condition<Ts...> {
157 public:
159 bool check(const Ts &...x) override { return !this->state_->current_values.is_on(); }
160
161 protected:
163};
164
166 public:
167 explicit LightTurnOnTrigger(LightState *a_light) : light_(a_light) {
168 a_light->add_remote_values_listener(this);
169 this->last_on_ = a_light->current_values.is_on();
170 }
171
173 // using the remote value because of transitions we need to trigger as early as possible
174 auto is_on = this->light_->remote_values.is_on();
175 // only trigger when going from off to on
176 auto should_trigger = is_on && !this->last_on_;
177 // Set new state immediately so that trigger() doesn't devolve
178 // into infinite loop
179 this->last_on_ = is_on;
180 if (should_trigger) {
181 this->trigger();
182 }
183 }
184
185 protected:
188};
189
191 public:
192 explicit LightTurnOffTrigger(LightState *a_light) : light_(a_light) {
194 }
195
197 auto is_on = this->light_->current_values.is_on();
198 // only trigger when going from on to off
199 if (!is_on) {
200 this->trigger();
201 }
202 }
203
204 protected:
206};
207
209 public:
210 explicit LightStateTrigger(LightState *a_light) { a_light->add_remote_values_listener(this); }
211
212 void on_light_remote_values_update() override { this->trigger(); }
213};
214
215// This is slightly ugly, but we can't log in headers, and can't make this a static method on AddressableSet
216// due to the template. It's just a temporary warning anyway.
217void addressableset_warn_about_scale(const char *field);
218
219template<typename... Ts> class AddressableSet : public Action<Ts...> {
220 public:
221 explicit AddressableSet(LightState *parent) : parent_(parent) {}
222
223 TEMPLATABLE_VALUE(int32_t, range_from)
224 TEMPLATABLE_VALUE(int32_t, range_to)
225 TEMPLATABLE_VALUE(float, color_brightness)
226 TEMPLATABLE_VALUE(float, red)
227 TEMPLATABLE_VALUE(float, green)
228 TEMPLATABLE_VALUE(float, blue)
229 TEMPLATABLE_VALUE(float, white)
230
231 void play(const Ts &...x) override {
232 auto *out = (AddressableLight *) this->parent_->get_output();
233 int32_t range_from = interpret_index(this->range_from_.value_or(x..., 0), out->size());
234 if (range_from < 0 || range_from >= out->size())
235 range_from = 0;
236
237 int32_t range_to = interpret_index(this->range_to_.value_or(x..., out->size() - 1) + 1, out->size());
238 if (range_to < 0 || range_to >= out->size())
239 range_to = out->size();
240
241 uint8_t color_brightness =
242 to_uint8_scale(this->color_brightness_.value_or(x..., this->parent_->remote_values.get_color_brightness()));
243 auto range = out->range(range_from, range_to);
244 if (this->red_.has_value())
245 range.set_red(esp_scale8(to_uint8_compat(this->red_.value(x...), "red"), color_brightness));
246 if (this->green_.has_value())
247 range.set_green(esp_scale8(to_uint8_compat(this->green_.value(x...), "green"), color_brightness));
248 if (this->blue_.has_value())
249 range.set_blue(esp_scale8(to_uint8_compat(this->blue_.value(x...), "blue"), color_brightness));
250 if (this->white_.has_value())
251 range.set_white(to_uint8_compat(this->white_.value(x...), "white"));
252 out->schedule_show();
253 }
254
255 protected:
257
258 // Historically, this action required uint8_t (0-255) for RGBW values from lambdas. Keep compatibility.
259 static inline uint8_t to_uint8_compat(float value, const char *field) {
260 if (value > 1.0f) {
262 return static_cast<uint8_t>(value);
263 }
264 return to_uint8_scale(value);
265 }
266};
267
268} // namespace esphome::light
virtual void play(const Ts &...x)=0
Base class for all automation conditions.
Definition automation.h:438
Function-pointer-only templatable storage (4 bytes on 32-bit).
Definition automation.h:19
void trigger(const Ts &...x) ESPHOME_ALWAYS_INLINE
Definition automation.h:461
static uint8_t to_uint8_compat(float value, const char *field)
Definition automation.h:259
AddressableSet(LightState *parent)
Definition automation.h:221
TEMPLATABLE_VALUE(int32_t, range_from) TEMPLATABLE_VALUE(int32_t
void play(const Ts &...x) override
Definition automation.h:72
TEMPLATABLE_VALUE(float, relative_brightness) template< typename V > void set_transition_length(V value)
Definition automation.h:66
void set_limit_mode(LimitMode limit_mode)
Definition automation.h:95
std::conditional_t< HasTransitionLength, TemplatableFn< uint32_t, Ts... >, NoTransition > transition_length_
Definition automation.h:104
DimRelativeAction(LightState *parent)
Definition automation.h:64
void set_min_max_brightness(float min, float max)
Definition automation.h:90
This class represents a requested change in a light state.
Definition light_call.h:22
LightCall & set_effect(optional< std::string > effect)
Set the effect of the light by its name.
bool is_on() const
Get the binary true/false state of these light color values.
void as_brightness(float *brightness) const
Convert these light color values to a brightness-only representation and write them to brightness.
LightControlAction(LightState *parent, ApplyFn apply)
Definition automation.h:49
void(*)(LightState *, LightCall &, const std::remove_cvref_t< Ts > &...) ApplyFn
Definition automation.h:48
void play(const Ts &...x) override
Definition automation.h:51
LightEffectCycleAction(LightState *parent)
Definition automation.h:113
void play(const Ts &...) override
Definition automation.h:117
void set_include_none(bool include_none)
Definition automation.h:115
bool check(const Ts &...x) override
Definition automation.h:159
LightIsOffCondition(LightState *state)
Definition automation.h:158
LightIsOnCondition(LightState *state)
Definition automation.h:150
bool check(const Ts &...x) override
Definition automation.h:151
Listener interface for light remote value changes.
Definition light_state.h:31
This class represents the communication layer between the front-end MQTT layer and the hardware outpu...
Definition light_state.h:93
void add_remote_values_listener(LightRemoteValuesListener *listener)
Add a listener for remote values changes.
LightColorValues remote_values
The remote color values reported to the frontend.
uint32_t get_current_effect_index() const
Get the currently active effect index (0 = no effect, 1+ = effect index).
LightOutput * get_output() const
Get the light output associated with this object.
void add_target_state_reached_listener(LightTargetStateReachedListener *listener)
Add a listener for target state reached.
LightCall turn_on()
Make a light state call.
LightColorValues current_values
The current values of the light as outputted to the light.
size_t get_effect_count() const
Get the total number of effects available for this light.
void on_light_remote_values_update() override
Definition automation.h:212
LightStateTrigger(LightState *a_light)
Definition automation.h:210
Listener interface for light target state reached.
Definition light_state.h:42
void on_light_target_state_reached() override
Definition automation.h:196
LightTurnOffTrigger(LightState *a_light)
Definition automation.h:192
LightTurnOnTrigger(LightState *a_light)
Definition automation.h:167
void on_light_remote_values_update() override
Definition automation.h:172
std::conditional_t< HasTransitionLength, TemplatableFn< uint32_t, Ts... >, NoTransition > transition_length_
Definition automation.h:31
void play(const Ts &...x) override
Definition automation.h:19
void set_transition_length(V value)
Definition automation.h:15
ToggleAction(LightState *state)
Definition automation.h:13
void apply(Climate *climate)
Apply these settings to the climate device.
bool state
Definition fan.h:2
Range range
Definition msa3xx.h:0
void addressableset_warn_about_scale(const char *field)
Definition automation.cpp:8
int32_t HOT interpret_index(int32_t index, int32_t size)
if(written< 0)
Definition helpers.h:1047
uint16_t x
Definition tt21100.cpp:5