ESPHome 2025.10.0-dev
Loading...
Searching...
No Matches
addressable_light_effect.h
Go to the documentation of this file.
1#pragma once
2
3#include <utility>
4#include <vector>
5
9
10namespace esphome {
11namespace light {
12
13inline static int16_t sin16_c(uint16_t theta) {
14 static const uint16_t BASE[] = {0, 6393, 12539, 18204, 23170, 27245, 30273, 32137};
15 static const uint8_t SLOPE[] = {49, 48, 44, 38, 31, 23, 14, 4};
16 uint16_t offset = (theta & 0x3FFF) >> 3; // 0..2047
17 if (theta & 0x4000)
18 offset = 2047 - offset;
19 uint8_t section = offset / 256; // 0..7
20 uint16_t b = BASE[section];
21 uint8_t m = SLOPE[section];
22 uint8_t secoffset8 = uint8_t(offset) / 2;
23 uint16_t mx = m * secoffset8;
24 int16_t y = mx + b;
25 if (theta & 0x8000)
26 return -y;
27 return y;
28}
29inline static uint8_t half_sin8(uint8_t v) { return sin16_c(uint16_t(v) * 128u) >> 8; }
30
32 public:
33 explicit AddressableLightEffect(const std::string &name) : LightEffect(name) {}
34 void start_internal() override {
37 this->start();
38 }
39 void stop() override { this->get_addressable_()->set_effect_active(false); }
40 virtual void apply(AddressableLight &it, const Color &current_color) = 0;
41 void apply() override {
42 // not using any color correction etc. that will be handled by the addressable layer through ESPColorCorrection
44 this->apply(*this->get_addressable_(), current_color);
45 }
46
49 uint32_t get_effect_index() const { return this->get_index(); }
50
52 bool is_current_effect() const { return this->is_active() && this->get_addressable_()->is_effect_active(); }
53
54 protected:
56};
57
59 public:
60 AddressableLambdaLightEffect(const std::string &name,
61 std::function<void(AddressableLight &, Color, bool initial_run)> f,
62 uint32_t update_interval)
63 : AddressableLightEffect(name), f_(std::move(f)), update_interval_(update_interval) {}
64 void start() override { this->initial_run_ = true; }
65 void apply(AddressableLight &it, const Color &current_color) override {
66 const uint32_t now = millis();
67 if (now - this->last_run_ >= this->update_interval_ || this->initial_run_) {
68 this->last_run_ = now;
69 this->f_(it, current_color, this->initial_run_);
70 this->initial_run_ = false;
71 it.schedule_show();
72 }
73 }
74
75 protected:
76 std::function<void(AddressableLight &, Color, bool initial_run)> f_;
80};
81
83 public:
84 explicit AddressableRainbowLightEffect(const std::string &name) : AddressableLightEffect(name) {}
85 void apply(AddressableLight &it, const Color &current_color) override {
86 ESPHSVColor hsv;
87 hsv.value = 255;
88 hsv.saturation = 240;
89 uint16_t hue = (millis() * this->speed_) % 0xFFFF;
90 const uint16_t add = 0xFFFF / this->width_;
91 for (auto var : it) {
92 hsv.hue = hue >> 8;
93 var = hsv;
94 hue += add;
95 }
96 it.schedule_show();
97 }
98 void set_speed(uint32_t speed) { this->speed_ = speed; }
99 void set_width(uint16_t width) { this->width_ = width; }
100
101 protected:
103 uint16_t width_{50};
104};
105
107 uint8_t r, g, b, w;
108 bool random;
109 size_t num_leds;
111};
112
114 public:
115 explicit AddressableColorWipeEffect(const std::string &name) : AddressableLightEffect(name) {}
116 void set_colors(const std::vector<AddressableColorWipeEffectColor> &colors) { this->colors_ = colors; }
117 void set_add_led_interval(uint32_t add_led_interval) { this->add_led_interval_ = add_led_interval; }
118 void set_reverse(bool reverse) { this->reverse_ = reverse; }
119 void apply(AddressableLight &it, const Color &current_color) override {
120 const uint32_t now = millis();
122 return;
123 this->last_add_ = now;
124 if (this->reverse_) {
125 it.shift_left(1);
126 } else {
127 it.shift_right(1);
128 }
129 const AddressableColorWipeEffectColor &color = this->colors_[this->at_color_];
130 Color esp_color = Color(color.r, color.g, color.b, color.w);
131 if (color.gradient) {
132 size_t next_color_index = (this->at_color_ + 1) % this->colors_.size();
133 const AddressableColorWipeEffectColor &next_color = this->colors_[next_color_index];
134 const Color next_esp_color = Color(next_color.r, next_color.g, next_color.b, next_color.w);
135 uint8_t gradient = 255 * ((float) this->leds_added_ / color.num_leds);
136 esp_color = esp_color.gradient(next_esp_color, gradient);
137 }
138 if (this->reverse_) {
139 it[-1] = esp_color;
140 } else {
141 it[0] = esp_color;
142 }
143 if (++this->leds_added_ >= color.num_leds) {
144 this->leds_added_ = 0;
145 this->at_color_ = (this->at_color_ + 1) % this->colors_.size();
146 AddressableColorWipeEffectColor &new_color = this->colors_[this->at_color_];
147 if (new_color.random) {
149 new_color.r = c.r;
150 new_color.g = c.g;
151 new_color.b = c.b;
152 }
153 }
154 it.schedule_show();
155 }
156
157 protected:
158 std::vector<AddressableColorWipeEffectColor> colors_;
159 size_t at_color_{0};
162 size_t leds_added_{0};
163 bool reverse_{};
164};
165
167 public:
168 explicit AddressableScanEffect(const std::string &name) : AddressableLightEffect(name) {}
169 void set_move_interval(uint32_t move_interval) { this->move_interval_ = move_interval; }
170 void set_scan_width(uint32_t scan_width) { this->scan_width_ = scan_width; }
171 void apply(AddressableLight &it, const Color &current_color) override {
172 const uint32_t now = millis();
173 if (now - this->last_move_ < this->move_interval_)
174 return;
175
176 if (direction_) {
177 this->at_led_++;
178 if (this->at_led_ == it.size() - this->scan_width_)
179 this->direction_ = false;
180 } else {
181 this->at_led_--;
182 if (this->at_led_ == 0)
183 this->direction_ = true;
184 }
185 this->last_move_ = now;
186
187 it.all() = Color::BLACK;
188 for (uint32_t i = 0; i < this->scan_width_; i++) {
189 it[this->at_led_ + i] = current_color;
190 }
191
192 it.schedule_show();
193 }
194
195 protected:
200 bool direction_{true};
201};
202
204 public:
205 explicit AddressableTwinkleEffect(const std::string &name) : AddressableLightEffect(name) {}
206 void apply(AddressableLight &addressable, const Color &current_color) override {
207 const uint32_t now = millis();
208 uint8_t pos_add = 0;
209 if (now - this->last_progress_ > this->progress_interval_) {
210 const uint32_t pos_add32 = (now - this->last_progress_) / this->progress_interval_;
211 pos_add = pos_add32;
212 this->last_progress_ += pos_add32 * this->progress_interval_;
213 }
214 for (auto view : addressable) {
215 if (view.get_effect_data() != 0) {
216 const uint8_t sine = half_sin8(view.get_effect_data());
217 view = current_color * sine;
218 const uint8_t new_pos = view.get_effect_data() + pos_add;
219 if (new_pos < view.get_effect_data()) {
220 view.set_effect_data(0);
221 } else {
222 view.set_effect_data(new_pos);
223 }
224 } else {
225 view = Color::BLACK;
226 }
227 }
228 while (random_float() < this->twinkle_probability_) {
229 const size_t pos = random_uint32() % addressable.size();
230 if (addressable[pos].get_effect_data() != 0)
231 continue;
232 addressable[pos].set_effect_data(1);
233 }
234 addressable.schedule_show();
235 }
236 void set_twinkle_probability(float twinkle_probability) { this->twinkle_probability_ = twinkle_probability; }
237 void set_progress_interval(uint32_t progress_interval) { this->progress_interval_ = progress_interval; }
238
239 protected:
243};
244
246 public:
247 explicit AddressableRandomTwinkleEffect(const std::string &name) : AddressableLightEffect(name) {}
248 void apply(AddressableLight &it, const Color &current_color) override {
249 const uint32_t now = millis();
250 uint8_t pos_add = 0;
251 if (now - this->last_progress_ > this->progress_interval_) {
252 pos_add = (now - this->last_progress_) / this->progress_interval_;
253 this->last_progress_ = now;
254 }
255 uint8_t subsine = ((8 * (now - this->last_progress_)) / this->progress_interval_) & 0b111;
256 for (auto view : it) {
257 if (view.get_effect_data() != 0) {
258 const uint8_t x = (view.get_effect_data() >> 3) & 0b11111;
259 const uint8_t color = view.get_effect_data() & 0b111;
260 const uint16_t sine = half_sin8((x << 3) | subsine);
261 if (color == 0) {
262 view = current_color * sine;
263 } else {
264 view = Color(((color >> 2) & 1) * sine, ((color >> 1) & 1) * sine, ((color >> 0) & 1) * sine);
265 }
266 const uint8_t new_x = x + pos_add;
267 if (new_x > 0b11111) {
268 view.set_effect_data(0);
269 } else {
270 view.set_effect_data((new_x << 3) | color);
271 }
272 } else {
273 view = Color(0, 0, 0, 0);
274 }
275 }
276 while (random_float() < this->twinkle_probability_) {
277 const size_t pos = random_uint32() % it.size();
278 if (it[pos].get_effect_data() != 0)
279 continue;
280 const uint8_t color = random_uint32() & 0b111;
281 it[pos].set_effect_data(0b1000 | color);
282 }
283 it.schedule_show();
284 }
285 void set_twinkle_probability(float twinkle_probability) { this->twinkle_probability_ = twinkle_probability; }
286 void set_progress_interval(uint32_t progress_interval) { this->progress_interval_ = progress_interval; }
287
288 protected:
292};
293
295 public:
296 explicit AddressableFireworksEffect(const std::string &name) : AddressableLightEffect(name) {}
297 void start() override {
298 auto &it = *this->get_addressable_();
299 it.all() = Color::BLACK;
300 }
301 void apply(AddressableLight &it, const Color &current_color) override {
302 const uint32_t now = millis();
304 return;
305 this->last_update_ = now;
306 // "invert" the fade out parameter so that higher values make fade out faster
307 const uint8_t fade_out_mult = 255u - this->fade_out_rate_;
308 for (auto view : it) {
309 Color target = view.get() * fade_out_mult;
310 if (target.r < 64)
311 target *= 170;
312 view = target;
313 }
314 int last = it.size() - 1;
315 it[0].set(it[0].get() + (it[1].get() * 128));
316 for (int i = 1; i < last; i++) {
317 it[i] = (it[i - 1].get() * 64) + it[i].get() + (it[i + 1].get() * 64);
318 }
319 it[last] = it[last].get() + (it[last - 1].get() * 128);
320 if (random_float() < this->spark_probability_) {
321 const size_t pos = random_uint32() % it.size();
322 if (this->use_random_color_) {
323 it[pos] = Color::random_color();
324 } else {
325 it[pos] = current_color;
326 }
327 }
328 it.schedule_show();
329 }
330 void set_update_interval(uint32_t update_interval) { this->update_interval_ = update_interval; }
331 void set_spark_probability(float spark_probability) { this->spark_probability_ = spark_probability; }
332 void set_use_random_color(bool random_color) { this->use_random_color_ = random_color; }
333 void set_fade_out_rate(uint8_t fade_out_rate) { this->fade_out_rate_ = fade_out_rate; }
334
335 protected:
336 uint8_t fade_out_rate_{};
341};
342
344 public:
345 explicit AddressableFlickerEffect(const std::string &name) : AddressableLightEffect(name) {}
346 void apply(AddressableLight &it, const Color &current_color) override {
347 const uint32_t now = millis();
348 const uint8_t intensity = this->intensity_;
349 const uint8_t inv_intensity = 255 - intensity;
351 return;
352
353 this->last_update_ = now;
354 uint32_t rng_state = random_uint32();
355 for (auto var : it) {
356 rng_state = (rng_state * 0x9E3779B9) + 0x9E37;
357 const uint8_t flicker = (rng_state & 0xFF) % intensity;
358 // scale down by random factor
359 var = var.get() * (255 - flicker);
360
361 // slowly fade back to "real" value
362 var = (var.get() * inv_intensity) + (current_color * intensity);
363 }
364 it.schedule_show();
365 }
366 void set_update_interval(uint32_t update_interval) { this->update_interval_ = update_interval; }
367 void set_intensity(float intensity) { this->intensity_ = to_uint8_scale(intensity); }
368
369 protected:
372 uint8_t intensity_{13};
373};
374
375} // namespace light
376} // namespace esphome
uint8_t m
Definition bl0906.h:1
void set_colors(const std::vector< AddressableColorWipeEffectColor > &colors)
void apply(AddressableLight &it, const Color &current_color) override
void set_add_led_interval(uint32_t add_led_interval)
std::vector< AddressableColorWipeEffectColor > colors_
void apply(AddressableLight &it, const Color &current_color) override
void apply(AddressableLight &it, const Color &current_color) override
void set_update_interval(uint32_t update_interval)
AddressableLambdaLightEffect(const std::string &name, std::function< void(AddressableLight &, Color, bool initial_run)> f, uint32_t update_interval)
void apply(AddressableLight &it, const Color &current_color) override
std::function< void(AddressableLight &, Color, bool initial_run)> f_
uint32_t get_effect_index() const
Get effect index specifically for addressable effects.
virtual void apply(AddressableLight &it, const Color &current_color)=0
bool is_current_effect() const
Check if this is the currently running addressable effect.
virtual void clear_effect_data()=0
ESPColorView get(int32_t index)
void set_effect_active(bool effect_active)
virtual int32_t size() const =0
void apply(AddressableLight &it, const Color &current_color) override
void apply(AddressableLight &it, const Color &current_color) override
void apply(AddressableLight &it, const Color &current_color) override
void set_progress_interval(uint32_t progress_interval)
void set_twinkle_probability(float twinkle_probability)
void apply(AddressableLight &addressable, const Color &current_color) override
virtual void start()
Initialize this LightEffect. Will be called once after creation.
uint32_t get_index() const
Get the index of this effect in the parent light's effect list.
bool is_active() const
Check if this effect is currently active.
LightColorValues remote_values
The remote color values reported to the frontend.
LightOutput * get_output() const
Get the light output associated with this object.
Color color_from_light_color_values(LightColorValues val)
Convert the color information from a LightColorValues object to a Color object (does not apply bright...
FLAG_HAS_TRANSITION float
Providing packet encoding functions for exchanging data with a remote host.
Definition a01nyub.cpp:7
float random_float()
Return a random float between 0 and 1.
Definition helpers.cpp:156
uint32_t random_uint32()
Return a random 32-bit unsigned integer.
Definition helpers.cpp:17
uint32_t IRAM_ATTR HOT millis()
Definition core.cpp:28
static Color random_color()
Definition color.h:157
uint8_t g
Definition color.h:25
Color gradient(const Color &to_color, uint8_t amnt)
Definition color.h:168
uint8_t b
Definition color.h:29
uint8_t r
Definition color.h:21
static const Color BLACK
Definition color.h:183
uint16_t x
Definition tt21100.cpp:5
uint16_t y
Definition tt21100.cpp:6