ESPHome Light Effects?

Hey Liminal! I haven’t tried any effects for a non-addressable light yet, but maybe I should. Would a ‘breathing red’ raise and lower brightness over 2 seconds or so? I’ll see what I can do. Dashdrum

OK, here’s my first crack at a Breathing Red effect. I’m sure others could do it better, but it’s a start. Note that you can control the length of each “breath” by changing the step_limit and update_interval (not less than 25ms or so). Experiment and have fun!

This code would go below the effects: line in the light definition.

      - lambda:
          name: Breathing Red
          update_interval: 50ms
          lambda: |-
            static int step = 0;
            const int step_limit = 80;
            float brightness;
        
            auto call = id(led_mirror).turn_on();
            call.set_rgb(1,0,0);
      
            brightness = (float)step / ((float)step_limit / 2.0f);
      
            if(brightness > 1.0f){
            	brightness = 1.0f - (brightness - 1.0f );
            }
      
            call.set_brightness(brightness);
            call.set_white(0.0);
            call.set_transition_length(0);
            call.perform();
            
            step = ++step % step_limit; 

Thank you!

I actually ended up getting one from here that works ok

I just tried to compare the two, but I really have no idea LOL, they look so different to the untrained eye?

Different approaches, same basic result. (I like theirs better)

1 Like

Is it possible to change the brightness of each LED individually?

Yes it is, if they are addressable

Hi there, thought I’d share my (what I consider to be) improved rainbow effect.

lambda: |-
            uint8_t led_change = 24; //(higher is more change) the difference in hue for each led down the strip
            float speed = 7; //(lower is faster) the speed the first led colour changes at (therefore affecting all)
            
            if (initial_run) {
              it.all() = Color(0, 0, 0);
            }
            
            unsigned long time = millis() / speed;
            int repetitions = time / 1529;
            uint16_t hue = time - (1529 * repetitions);
            
            for (int i = 0; i < it.size(); i++) {
              if (hue >= 0 && hue < 255) {
                uint8_t green = hue;
                it[i] = Color(255, green, 0);
              } else if (hue >= 255 && hue < 510) {
                uint8_t red = hue - 255;
                it[i] = Color((255 - red), 255, 0);
              } else if (hue >= 510 && hue < 765) {
                uint8_t blue = hue - 510;
                it[i] = Color(0, 255, blue);
              } else if (hue >= 765 && hue < 1020) {
                uint8_t green = hue - 765;
                it[i] = Color(0, (255 - green), 255);
              } else if (hue >= 1020 && hue < 1275) {
                uint8_t red = hue - 1020;
                it[i] = Color(red, 0, 255);
              } else if (hue >= 1275 && hue < 1530) {
                uint8_t blue = hue - 1275;
                it[i] = Color(255, 0, (255 - blue));
              }
              hue+=led_change;
              if (hue >= 1530) {
                hue-=1530;
              }
            }

The default rainbow effect I found to be too dull in the mixed led colours and only bright on the single led colours. This effect focuses on making sure all colours are equally shown (imo).

I’m also currently working on an effect that uses the currently chosen colour in Home Assistant and (not sure if this is the right term) waves colours similar to it.

lambda: |-
            uint8_t led_change = 15; //(higher is more change) the difference in hue for each led down the strip
            uint8_t hue_variation = 180; //(higher is more variation) the amount the hue changes from the current colour
            int speed = 7; //(lower is faster) the speed the first led colour changes at (therefore affecting all)
            
            if (initial_run) {
              it.all() = Color(0, 0, 0);
            }
            
            Color pickedColor = current_color;
            uint16_t pickedHue = 1500;
            if (pickedColor.b == 0) {
              if (pickedColor.g < 255) {
                pickedHue = 1000 + pickedColor.g;
              } else {
                pickedHue = 1255 + (255 - pickedColor.r);
              }
            } else if (pickedColor.r == 0) {
              if (pickedColor.b < 255) {
                pickedHue = 1510 + pickedColor.b;
              } else {
                pickedHue = 1765 + (255 - pickedColor.g);
              }
            } else if (pickedColor.g == 0) {
              if (pickedColor.r < 255) {
                pickedHue = 2020 + pickedColor.r;
              } else {
                pickedHue = 2275 + (255 - pickedColor.b);
              }
            }
            
            uint16_t minHue = pickedHue - hue_variation;
            uint16_t maxHue = pickedHue + hue_variation;
            
            unsigned long time = millis() / speed;
            int repetitions = time / (hue_variation * 2);
            uint16_t hue = minHue + (time - ((hue_variation * 2) * repetitions));
            
            bool backwards = false;
            if ((repetitions % 2) == 0) {
              backwards = true;
            } else {
              backwards = false;
            }
            if (backwards) {
              hue = minHue + (maxHue- hue);
            }
            
            for (int i = 0; i < it.size(); i++) {
              if (hue >= 1000 && hue < 1255) {
                uint8_t green = hue - 1000;
                it[i] = Color(255, green, 0);
              } else if (hue >= 1255 && hue < 1510) {
                uint8_t red = hue - 1255;
                it[i] = Color((255 - red), 255, 0);
              } else if (hue >= 1510 && hue < 1765) {
                uint8_t blue = hue - 1510;
                it[i] = Color(0, 255, blue);
              } else if (hue >= 1765 && hue < 2020) {
                uint8_t green = hue - 1765;
                it[i] = Color(0, (255 - green), 255);
              } else if (hue >= 2020 && hue < 2275) {
                uint8_t red = hue - 2020;
                it[i] = Color(red, 0, 255);
              } else if (hue >= 2275 && hue < 2530) {
                uint8_t blue = hue - 2275;
                it[i] = Color(255, 0, (255 - blue));
              } else if (hue >= 745 && hue < 1000) { 
                uint8_t blue = hue - 745;
                it[i] = Color(255, 0, (255 - blue));
              } else if (hue >= 2530 && hue < 2785) { 
                uint8_t green = hue - 2530;
                it[i] = Color(255, green, 0);
              } else {
                it[i] = Color(255, 255, 255);
              }
              hue+=led_change;
              if (hue >= maxHue) {
                hue-=(hue_variation * 2);
              }
            }

Currently the colours seems to only fade correctly on the first led and I can’t figure out how to properly do it for the rest of the strip :frowning:

With both effects, changing the variable with the comments will change certain aspects about the effect.

Side note: I’m not sure if this is a well known thing or not, but I still wish I had known it sooner; The addressable lambda section for FastLED lights is extremely similar to how you would write code for a FastLED light on an Arduino using the Arduino IDE. Means that most FastLED functions (like beatsin16() for example) can be used within the lambda.

4 Likes

Hi @7h30n3

thanks for sharing your Wipe In/Out effects!
Now I want to use them in the on_turn_on and on_turn_off actions of the light component.
Unfortunately only the on_turn_on works, but I guess that because an effect in general is for the light to be on, it is not compatible to call a light effect for the on_turn_off :frowning:

Do you know how could I have the Wipe Out effect when turning off the led strip?

The code:

light:
  - platform: neopixelbus
    type: GRB
    variant: LC8812
    pin: GPIO26
    num_leds: 29
    id: "neopixel_bookshelf"
    name: "Neopixel Bookshelf"
    on_turn_on:
      - light.turn_on:
          id: "neopixel_bookshelf"
          effect: "Wipe In"
    on_turn_off:
      - light.turn_off:
          id: "neopixel_bookshelf"
          effect: "Wipe Out"
    effects:
      - addressable_lambda:
          name: "Wipe In"
          update_interval: 20ms
          lambda: |-
            static int x = 0;
            if (initial_run) {
              x = 0;
              it.all() = ESPColor(0,0,0);
            }
            if (x < it.size()) {
              it[x] = current_color;
              x += 1;
            }  
      - addressable_lambda:
          name: "Wipe Out"
          update_interval: 20ms
          lambda: |-
            static int x = 0;
            if (initial_run) {
              x = it.size();
            }
            if (x > 0) {
              x -= 1; 
              it[x] = ESPColor(0,0,0);
            }     

The error:

Failed config

light.neopixelbus: [source bookshelf.yaml:41]
  platform: neopixelbus
  type: GRB
  variant: LC8812
  pin: GPIO26
  num_leds: 29
  id: neopixel_bookshelf
  name: Neopixel Bookshelf
  on_turn_on: 
    - light.turn_on: 
        id: neopixel_bookshelf
        effect: Wipe In
  on_turn_off:  [source bookshelf.yaml:53]
    - [source bookshelf.yaml:53]
      light.turn_off:  [source bookshelf.yaml:54]
        id: neopixel_bookshelf
        
        [effect] is an invalid option for [light.turn_off].
        effect: Wipe Out [source bookshelf.yaml:55]
  effects: 
    - addressable_lambda: 
        name: Wipe In
        update_interval: 20ms
        lambda: |-
          static int x = 0;
          if (initial_run) {
            x = 0;
            it.all() = ESPColor(0,0,0);
          }
          if (x < it.size()) {
            it[x] = current_color;
            x += 1;
          }  
    - addressable_lambda: 
        name: Wipe Out
        update_interval: 20ms
        lambda: |-
          static int x = 0;
          if (initial_run) {
            x = it.size();
          }
          if (x > 0) {
            x -= 1; 
            it[x] = ESPColor(0,0,0);
          }     

Thanks for the help!

Hey @juangrados You’re right, you only can use effects with light.turn_on.
So according to your case for light.turn_off I did it like that:

    on_turn_off:
      - light.turn_on:
          id: "neopixel_bookshelf"
          effect: "Wipe Out"
      - delay: 1s
      - light.turn_off:
          id: "neopixel_bookshelf"

I hope this helps.

Hi @7h30n3 thanks for the suggestion, unfortunately it does not work.
The ESP crashes, I think when call the “on_turn_off” it really first of all turns off the led’s, and then performs the actions, but the first action is light.turn_on it self with an effect, which it does not like.

Here is the log as reference:

[12:14:32][D][light:093]: 'Neopixel Bookshelf' Setting:
[12:14:32][D][light:102]:   State: OFF
[12:14:32][D][light:132]:   Transition Length: 1.0s
[12:14:33][D][light:093]: 'Neopixel Bookshelf' Setting:
[12:14:33][D][light:102]:   State: ON
[12:14:33][D][light:155]:   Effect: 'Wipe Out'
[12:14:33]Guru Meditation Error: Core  1 panic'ed (LoadProhibited). Exception was unhandled.
[12:14:33]Core 1 register dump:
[12:14:33]PC      : 0x400d8d7a  PS      : 0x00060930  A0      : 0x800d8df1  A1      : 0x3ffb1ec0  
WARNING Decoded 0x400d8d7a: esphome::light::LightState::loop() at /Users/juangrados/Documents/MyProjects/IoT/EspHome/bookshelf/bookshelf/src/esphome/components/light/light_color_values.h:237
[12:14:34]A2      : 0x3ffb8dd4  A3      : 0x3ffb87b0  A4      : 0x3ffb87b0  A5      : 0x3ffb1ed0  
[12:14:34]A6      : 0x00000003  A7      : 0x00060323  A8      : 0x800d8d71  A9      : 0x3ffb1ea0  
[12:14:34]A10     : 0x00000000  A11     : 0x3ffb8e3c  A12     : 0x00000020  A13     : 0x3ffb8e5c  
[12:14:34]A14     : 0x3f800000  A15     : 0x3f800000  SAR     : 0x0000000a  EXCCAUSE: 0x0000001c  
[12:14:34]EXCVADDR: 0x00000000  LBEG    : 0x4000c46c  LEND    : 0x4000c477  LCOUNT  : 0x00000000  
[12:14:34]
[12:14:34]ELF file SHA256: 0000000000000000
[12:14:34]
[12:14:34]Backtrace: 0x400d8d7a:0x3ffb1ec0 0x400d8dee:0x3ffb1f00 0x40164aa5:0x3ffb1f20 0x40164b39:0x3ffb1f40 0x400dd2aa:0x3ffb1f60 0x400df9aa:0x3ffb1f90 0x400ebc79:0x3ffb1fb0 0x40089cbe:0x3ffb1fd0
WARNING Found stack trace! Trying to decode it
WARNING Decoded 0x400d8d7a: esphome::light::LightState::loop() at /Users/juangrados/Documents/MyProjects/IoT/EspHome/bookshelf/bookshelf/src/esphome/components/light/light_color_values.h:237
WARNING Decoded 0x400d8dee: non-virtual thunk to esphome::light::LightState::loop()
WARNING Decoded 0x40164aa5: esphome::Component::call_loop() at /Users/juangrados/Documents/MyProjects/IoT/EspHome/bookshelf/bookshelf/src/esphome/core/component.cpp:160
WARNING Decoded 0x40164b39: esphome::Component::call() at /Users/juangrados/Documents/MyProjects/IoT/EspHome/bookshelf/bookshelf/src/esphome/core/component.cpp:160
WARNING Decoded 0x400dd2aa: esphome::Application::loop() at /Users/juangrados/Documents/MyProjects/IoT/EspHome/bookshelf/bookshelf/src/esphome/core/application.cpp:72
WARNING Decoded 0x400df9aa: loop() at /Users/juangrados/Documents/MyProjects/IoT/EspHome/bookshelf/bookshelf/src/esphome/components/light/light_output.h:18
WARNING Decoded 0x400ebc79: loopTask(void*) at /Users/juangrados/.platformio/packages/framework-arduinoespressif32/cores/esp32/main.cpp:23
WARNING Decoded 0x40089cbe: vPortTaskWrapper at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/freertos/port.c:355 (discriminator 1)
[12:14:34]
[12:14:34]Rebooting...

If you have any idea of what to try let me know. I will keep trying on my side too.

Thanks!

Ok, given that the problem was the light turning off and then on it self for the effect, I better moved the actions of the automation to a template switch, that way there is not conflict for the led on_turn_off:

switch:
  - platform: template
    name: "Bookshelf switch"
    lambda: |-
      if (id(neopixel_bookshelf).current_values.is_on()) {
        return true;
      } else {
        return false;
      }
    turn_on_action:
      - light.turn_on:
          id: "neopixel_bookshelf"
          brightness: 100% 
      - light.turn_on:
          id: "neopixel_bookshelf"
          effect: "Wipe In"
      - delay: 2s
      - light.turn_on:
          id: "neopixel_bookshelf"
          brightness: 100%
          red: 100%
          green: 100%
          blue: 100%
      - light.turn_on:
          id: "neopixel_bookshelf"
          effect: "None"
    turn_off_action:
      - light.turn_on:
          id: "neopixel_bookshelf"
          effect: "Wipe Out"
      - delay: 2s
      - light.turn_off:
          id: "neopixel_bookshelf"


light:
  - platform: neopixelbus
    type: GRB
    variant: LC8812
    pin: GPIO26
    num_leds: 29
    id: "neopixel_bookshelf"
    effects:
      - addressable_lambda:
          name: "Wipe In"
          update_interval: 20ms
          lambda: |-
            static int x = 0;
            if (initial_run) {
              x = 0;
              it.all() = COLOR_BLACK;
            }
            if (x < it.size()) {
              it[x-1] = COLOR_WHITE;
              x += 1;
            }  
      - addressable_lambda:
          name: "Wipe Out"
          update_interval: 20ms
          lambda: |-
            static int x = 0;
            if (initial_run) {
              x = it.size();
            }
            if (x > 0) {
              x -= 1; 
              it[x] = COLOR_BLACK;
            }     

It is almost perfect… the switch imported in Home Assistant has a funny effect when turning off the light, when toggling the switch off, the led strip goes off with the nice Wipe Out effect, but then for a second the switch in HA goes On again and immediately Off.
Not sure why but ok, al least the effects work now :slight_smile:

Btw. for some reason I had to subtract 1 from the array of led’s in the Wipe In effect because the first Led wasn’t turning on, with this change now all led turn on, I am also not sure why haha, I guess the x is getting shifted by 1 for some reason somewhere…

Ok final version.

The shift by one was still giving problems but when I increased the num_leds by one more than my strip it now works fine with the original limits for the effects.
Also I included one more condition for the state switch so I do not have the momentary turn On on the HA dashboard.

Thanks for the help.

switch:
  - platform: template
    name: "Bookshelf switch"
    lambda: |-
      if (id(neopixel_bookshelf).get_effect_name() == "Wipe Out") {
        return false;
      } else if (id(neopixel_bookshelf).current_values.is_on()) {
        return true;
      } else {
        return false;
      }
    turn_on_action:
      - light.turn_on:
          id: "neopixel_bookshelf"
          brightness: 100% 
      - light.turn_on:
          id: "neopixel_bookshelf"
          effect: "Wipe In"
      - delay: 2s
      - light.turn_on:
          id: "neopixel_bookshelf"
          brightness: 100%
          red: 100%
          green: 100%
          blue: 100%
      - light.turn_on:
          id: "neopixel_bookshelf"
          effect: "None"
    turn_off_action:
      - light.turn_on:
          id: "neopixel_bookshelf"
          effect: "Wipe Out"


light:
  - platform: neopixelbus
    type: GRB
    variant: LC8812
    pin: GPIO26
    num_leds: 30
    id: "neopixel_bookshelf"
    effects:
      - addressable_lambda:
          name: "Wipe In"
          update_interval: 20ms
          lambda: |-
            static int x = 0;
            if (initial_run) {
              x = 0;
              it.all() = COLOR_BLACK;
            }
            if (x < it.size()) {
              it[x] = COLOR_WHITE;
              x += 1;
            }  
      - addressable_lambda:
          name: "Wipe Out"
          update_interval: 20ms
          lambda: |-
            static int x = 0;
            if (initial_run) {
              x = it.size();
            }
            if (x > 0) {
              x -= 1; 
              it[x] = COLOR_BLACK;
            }     

Hi Mitch -
I’m trying to use your lambda for a rainbow affect, and getting a problem.


Any clues on how I need to define “it” ?

it is automatically defined in an addressable_lambda.

I don’t know what was the quality of the WLED Home Assistant integration when you wrote this, but i’ve just recently installed it and it works like a charm with HA!
I can’t relate to any of the CONS you mentioned.

1 Like

I think the fact that you replied to a nearly 3 year old post should give you an idea.

I’m looking for a more “knight rider” like scan effect for platform: neopixelbus with a WS2812 30 LED strip. The Addressable Scan Eeffect in light platform is close but I would like the moving scanner to have different intensities of red from middle to edge rather than the defined # of LED’s all at the same brightness moving left-right-left that the built-in scan effect activates. I.e something like 5 LEDS at 33%, 66%, 100%, 66%, 30%. Possible? Maybe using Addressable Lambda Effect??

In addition the my light effects above: ESPHome Light Effects? - #34 by 7h30n3 I created some new ones.

- addressable_lambda:
    name: "Dual Disco" // Two random colors with a gradient in between
    update_interval: 500ms
    lambda: |-
      static uint8_t gradientSize;
      static uint8_t gradientStart;
      if (initial_run) {
        gradientSize = ceil(it.size()/5);
        gradientStart = it.size()/2 - gradientSize/2;
        return;
      }
      Color firstColor = Color::random_color();
      Color secondColor = Color::random_color();
      it.range(0, gradientStart) = firstColor;
      for (int i = 0; i < gradientSize; i++) {
        uint8_t amnt = ((float)i / gradientSize) * 255;
        it[gradientStart + i] = firstColor.gradient(secondColor, amnt);
      }
      it.range(it.size()/2 + gradientSize/2, it.size()) = secondColor;
- addressable_lambda:
    name: "Psychodelic" //By-product of the creating the "Voice" effect
    update_interval: 120ms
    lambda: |-
      static uint16_t middlePos = it.size()/2;
      static uint16_t amplitudeSize;
 
      amplitudeSize = random_uint32() % it.size()/2;
      it.all() = Color::random_color();
      it.range(middlePos-amplitudeSize, middlePos+amplitudeSize) = Color::random_color();
- addressable_lambda:
    name: "Voice" // Simulates the amplitudes of an audio output. Useful effect during voice outputs
    update_interval: 80ms
    lambda: |-
      static uint16_t middlePos = it.size()/2;
      static uint16_t amplitudeSize;
      static uint16_t capSize;

      if (initial_run) {
        capSize = ceil(it.size()/4);
        return;
      }

      amplitudeSize = max((int)(random_uint32() % it.size()/2) - capSize, 0);
      it.all() = Color::BLACK;
      
      it.range(middlePos-amplitudeSize, middlePos+amplitudeSize) = current_color;

      for (int i = 0; i <= capSize; i++) {
        uint8_t amnt = ((float)i / capSize) * 255;
        it[middlePos - amplitudeSize - i] = current_color.gradient(Color::BLACK, amnt);
        it[middlePos + amplitudeSize + i] = current_color.gradient(Color::BLACK, amnt);
      }
2 Likes

Added to the collection.