ESPHome 2026.11.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 DimRelativeAction final : public Action<Ts...> {
12 public:
13 explicit DimRelativeAction(LightState *parent) : parent_(parent) {}
14
15 TEMPLATABLE_VALUE(float, relative_brightness)
16
17 template<typename V>
18 void set_transition_length(V value)
19 requires(HasTransitionLength)
20 {
21 this->transition_length_ = value;
22 }
23
24 void play(const Ts &...x) override {
25 auto call = this->parent_->make_call();
26 float rel = this->relative_brightness_.value(x...);
27 float cur;
29 if ((limit_mode_ == LimitMode::DO_NOTHING) && ((cur < min_brightness_) || (cur > max_brightness_))) {
30 return;
31 }
32 float new_brightness = clamp(cur + rel, min_brightness_, max_brightness_);
33 call.set_state(new_brightness != 0.0f);
34 call.set_brightness(new_brightness);
35
36 if constexpr (HasTransitionLength) {
37 call.set_transition_length(this->transition_length_.optional_value(x...));
38 }
39 call.perform();
40 }
41
42 void set_min_max_brightness(float min, float max) {
43 this->min_brightness_ = min;
44 this->max_brightness_ = max;
45 }
46
47 void set_limit_mode(LimitMode limit_mode) { this->limit_mode_ = limit_mode; }
48
49 protected:
51 float min_brightness_{0.0};
52 float max_brightness_{1.0};
54 struct NoTransition {};
55 [[no_unique_address]] std::conditional_t<HasTransitionLength, TemplatableFn<uint32_t, Ts...>, NoTransition>
57};
58
59// Cycle through the light's configured effects. `Forward` selects direction
60// at compile time so the chosen branch is the only one that gets instantiated
61// per action site. `include_none` is runtime so a single set of templates
62// covers both the "wrap through None" and "skip None" variants.
63template<bool Forward, typename... Ts> class LightEffectCycleAction final : public Action<Ts...> {
64 public:
65 explicit LightEffectCycleAction(LightState *parent) : parent_(parent) {}
66
67 void set_include_none(bool include_none) { this->include_none_ = include_none; }
68
69 void play(const Ts &...) override {
70 size_t count = this->parent_->get_effect_count();
71 if (count == 0) {
72 return;
73 }
74 uint32_t current = this->parent_->get_current_effect_index();
76 if (this->include_none_) {
77 uint32_t total = static_cast<uint32_t>(count) + 1;
78 if constexpr (Forward) {
79 next = (current + 1) % total;
80 } else {
81 next = (current + total - 1) % total;
82 }
83 } else {
84 if constexpr (Forward) {
85 next = (current % static_cast<uint32_t>(count)) + 1;
86 } else {
87 next = (current <= 1) ? static_cast<uint32_t>(count) : current - 1;
88 }
89 }
90 auto call = this->parent_->turn_on();
91 call.set_effect(next);
92 call.perform();
93 }
94
95 protected:
97 bool include_none_{false};
98};
99
101 public:
102 explicit LightTurnOnTrigger(LightState *a_light) : light_(a_light) {
103 a_light->add_remote_values_listener(this);
104 this->last_on_ = a_light->current_values.is_on();
105 }
106
108 // using the remote value because of transitions we need to trigger as early as possible
109 auto is_on = this->light_->remote_values.is_on();
110 // only trigger when going from off to on
111 auto should_trigger = is_on && !this->last_on_;
112 // Set new state immediately so that trigger() doesn't devolve
113 // into infinite loop
114 this->last_on_ = is_on;
115 if (should_trigger) {
116 this->trigger();
117 }
118 }
119
120 protected:
123};
124
126 public:
127 explicit LightTurnOffTrigger(LightState *a_light) : light_(a_light) {
129 }
130
132 auto is_on = this->light_->current_values.is_on();
133 // only trigger when going from on to off
134 if (!is_on) {
135 this->trigger();
136 }
137 }
138
139 protected:
141};
142
144 public:
145 explicit LightStateTrigger(LightState *a_light) { a_light->add_remote_values_listener(this); }
146
147 void on_light_remote_values_update() override { this->trigger(); }
148};
149
150// This is slightly ugly, but we can't log in headers, and can't make this a static method on AddressableSet
151// due to the template. It's just a temporary warning anyway.
152void addressableset_warn_about_scale(const char *field);
153
154template<typename... Ts> class AddressableSet final : public Action<Ts...> {
155 public:
156 explicit AddressableSet(LightState *parent) : parent_(parent) {}
157
158 TEMPLATABLE_VALUE(int32_t, range_from)
159 TEMPLATABLE_VALUE(int32_t, range_to)
160 TEMPLATABLE_VALUE(float, color_brightness)
161 TEMPLATABLE_VALUE(float, red)
162 TEMPLATABLE_VALUE(float, green)
163 TEMPLATABLE_VALUE(float, blue)
164 TEMPLATABLE_VALUE(float, white)
165
166 void play(const Ts &...x) override {
167 auto *out = (AddressableLight *) this->parent_->get_output();
168 int32_t range_from = interpret_index(this->range_from_.value_or(x..., 0), out->size());
169 if (range_from < 0 || range_from >= out->size())
170 range_from = 0;
171
172 int32_t range_to = interpret_index(this->range_to_.value_or(x..., out->size() - 1) + 1, out->size());
173 if (range_to < 0 || range_to >= out->size())
174 range_to = out->size();
175
176 uint8_t color_brightness =
177 to_uint8_scale(this->color_brightness_.value_or(x..., this->parent_->remote_values.get_color_brightness()));
178 auto range = out->range(range_from, range_to);
179 if (this->red_.has_value())
180 range.set_red(esp_scale8(to_uint8_compat(this->red_.value(x...), "red"), color_brightness));
181 if (this->green_.has_value())
182 range.set_green(esp_scale8(to_uint8_compat(this->green_.value(x...), "green"), color_brightness));
183 if (this->blue_.has_value())
184 range.set_blue(esp_scale8(to_uint8_compat(this->blue_.value(x...), "blue"), color_brightness));
185 if (this->white_.has_value())
186 range.set_white(to_uint8_compat(this->white_.value(x...), "white"));
187 out->schedule_show();
188 }
189
190 protected:
192
193 // Historically, this action required uint8_t (0-255) for RGBW values from lambdas. Keep compatibility.
194 static inline uint8_t to_uint8_compat(float value, const char *field) {
195 if (value > 1.0f) {
197 return static_cast<uint8_t>(value);
198 }
199 return to_uint8_scale(value);
200 }
201};
202
203} // namespace esphome::light
virtual void play(const Ts &...x)=0
Function-pointer-only templatable storage (4 bytes on 32-bit).
Definition automation.h:20
void trigger(const Ts &...x) ESPHOME_ALWAYS_INLINE
Definition automation.h:556
static uint8_t to_uint8_compat(float value, const char *field)
Definition automation.h:194
AddressableSet(LightState *parent)
Definition automation.h:156
TEMPLATABLE_VALUE(int32_t, range_from) TEMPLATABLE_VALUE(int32_t
void play(const Ts &...x) override
Definition automation.h:24
TEMPLATABLE_VALUE(float, relative_brightness) template< typename V > void set_transition_length(V value)
Definition automation.h:15
void set_limit_mode(LimitMode limit_mode)
Definition automation.h:47
std::conditional_t< HasTransitionLength, TemplatableFn< uint32_t, Ts... >, NoTransition > transition_length_
Definition automation.h:56
DimRelativeAction(LightState *parent)
Definition automation.h:13
void set_min_max_brightness(float min, float max)
Definition automation.h:42
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.
LightEffectCycleAction(LightState *parent)
Definition automation.h:65
void play(const Ts &...) override
Definition automation.h:69
void set_include_none(bool include_none)
Definition automation.h:67
Listener interface for light remote value changes.
Definition light_state.h:32
This class represents the communication layer between the front-end MQTT layer and the hardware outpu...
Definition light_state.h:68
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:147
LightStateTrigger(LightState *a_light)
Definition automation.h:145
Listener interface for light target state reached.
Definition light_state.h:43
void on_light_target_state_reached() override
Definition automation.h:131
LightTurnOffTrigger(LightState *a_light)
Definition automation.h:127
LightTurnOnTrigger(LightState *a_light)
Definition automation.h:102
void on_light_remote_values_update() override
Definition automation.h:107
NameCopy * next
std::vector< uint8_t > & out
Definition gif.cpp:49
unsigned count
Definition gif.cpp:51
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:1264
uint16_t x
Definition tt21100.cpp:5