esphome / feature-requests

ESPHome Feature Request Tracker
https://esphome.io/
417 stars 26 forks source link

Lambda `call.set_hs` #429

Open Joshfindit opened 5 years ago

Joshfindit commented 5 years ago

Describe the problem you have/What new integration you would like

Would like to be able to call call.set_hs(hue, 100); where hue is 0-360 repeating. Side note: I'm not sure if a while loop will lock up the device so this may be a wild goose chase

Please describe your use case for this integration and alternatives you've tried:

Working on a "non-addressable RGB" rainbow effect using lambda.

Additional context

https://esphome.io/components/light/rgb.html https://esphome.io/components/light/index.html https://esphome.io/api/classesphome_1_1light_1_1_light_call.html

OttoWinter commented 5 years ago

I guess HS support could be added to HS, yes - the HS->RGB algorithm would just need to be implemented.

Would like to be able to call call.set_hs(hue, 100); where hue is 0-360 repeating.

What do you mean by "repeating"? If you mean that the call would cycle through colors over time, no, that would be an effect's responsibility.

Side note: I'm not sure if a while loop will lock up the device so this may be a wild goose chase

Everything is executed in a single thread (with some exceptions), so yes an endless loop will lock up the device. See also how other effects handle it.

ashp8i commented 3 years ago

Would be great to have hs support so I could use a binary sensor to control hue value and which is 0-360 (angles of a circle) and keeps repeating i.e. when it reaches 360 = 0 and loops again, in terms of endless loop it would not run code non-stop but rather the code allows the value to perform a second revolution by reseting to 0 at 360 degrees.

what else would be required to help add this feature?

https://github.com/FastLED/FastLED/wiki/FastLED-HSV-Colors

ckuethe commented 3 years ago

I too would like to see this - it looks like all that needs to happen is to hoist ESPHSVColor::to_rgb() out of the addressable light class and make it part of the light class.

I've currently got a nasty implementation as a lambda, repeated for every effect that needs to do an HSV-to-RGB conversion, but it'd be so much nicer if I could say call.set_hsv(hue, 1.0, 1.0):

      - lambda:
          name: Color Fade (10s)
          update_interval: 500ms
          lambda: |-
            static int hue = 0, step = 20;
            float red = 0, green = 0, blue = 0;

            const float saturation = 1, value = 1;
            float c = saturation * value;

            float x = c * (1.0 - fabsf( fmodf((float(hue) / 60.0), 2.0) - 1.0));

            if (hue < 60){
                red = c;
                green = x;
                blue = 0;
            } else if (hue < 120){
                red = x;
                green = c;
                blue = 0;
            } else if (hue < 180){
                red = 0;
                green = c;
                blue = x;
            } else if (hue < 240){
                red = 0; green = x; blue = c;
            } else if (hue < 300) {
                red = x; green = 0; blue = c;
            } else { // if (hue <= 360) {
                red = c; green = 0; blue = x;
            }

            ESP_LOGI("colorcycle", "hue:%d x:%.3f r:%.3f g:%.3f b:%.3f", hue, x, red, green, blue);
            hue = (hue + step) % 360;

            auto call = id(${devname}_rgb).turn_on();
            call.set_transition_length(500);
            call.set_brightness(1.0);
            call.set_rgb(red, green, blue);
            call.perform();
Joshfindit commented 3 years ago

I guess HS support could be added to HS, yes - the HS->RGB algorithm would just need to be implemented.

Would like to be able to call call.set_hs(hue, 100); where hue is 0-360 repeating.

What do you mean by "repeating"? If you mean that the call would cycle through colors over time, no, that would be an effect's responsibility.

Sorry, I see how that was unclear and I missed the question until now. @ashp8i used better wording. I just mean that like the angles: 360 is the same hue as 0 so that slowly going from 0-360, resetting back to 0 then continuing to 360 would produce a single smooth transition.

In that case, set_hs(hue, 100) would be called 720 times. (More or less, depending on if the range was 0-360, 1-360, or 0-359)

ckuethe commented 3 years ago

Here's a slightly less terrible implementation. Or maybe it's terrible in different ways, eg. the use of globals.

# My RGB Light YAML
esphome:
  name: ${devname}
  platform: ESP8266
  board: d1_mini
  includes:
    - ./custom_includes/hsv2rgb.h

# other stuff...

light:
  - platform: rgb
    name: ${friendlyname} RGB
    id: ${devname}_rgb
    red: ${devname}_output_r
    green: ${devname}_output_g
    blue: ${devname}_output_b

    effects:
      - lambda:
          name: Color Fade (1m)
          update_interval: 1s
          lambda: |-
            int step = 6, trans = 1000;
            auto call = id(${devname}_rgb).turn_on();
            hsv_to_rgb(gHue, 1.0, 1.0, call, trans);
            gHue = (gHue + step) % 360;

      - lambda:
          name: Color Fade (2m)
          update_interval: 1s
          lambda: |-
            int step = 3, trans = 1000;
            auto call = id(${devname}_rgb).turn_on();
            hsv_to_rgb(gHue, 1.0, 1.0, call, trans);
            gHue = (gHue + step) % 360;
// hsv2rgb.h
#include "esphome.h"

using namespace esphome;
using namespace light;

// so I can share this between effects
uint16_t gHue = 0;

void hsv_to_rgb(uint16_t hue, float sat, float val, LightCall led, int transition){
    float c = sat * val;
    float x = c * (1.0 - fabsf( fmodf((float(hue) / 60.0), 2.0) - 1.0));
    float red = 0, green = 0, blue = 0;

    switch (hue/60) {
    case 0:
        red = c;
        green = x;
        blue = 0;
        break;
    case 1:
        red = x;
        green = c;
        blue = 0;
        break;
    case 2:
        red = 0;
        green = c;
        blue = x;
        break;
    case 3:
        red = 0;
        green = x;
        blue = c;
        break;
    case 4:
        red = x;
        green = 0;
        blue = c;
        break;
    case 5:
        red = c;
        green = 0;
        blue = x;
        break;
    default:
        break;
    }

    led.set_brightness(1.0);
    led.set_transition_length(transition);
    led.set_rgb(red, green, blue);
    led.perform();
}