diff --git a/night_light.h b/night_light.h new file mode 100644 index 0000000..561b62a --- /dev/null +++ b/night_light.h @@ -0,0 +1,29 @@ +#pragma once + +#include +#include + +namespace esphome { +namespace rgbww { +namespace yeelight_bs2 { + +class NightLight +{ +public: + // Based on measurements using the original device firmware. + //float red = 0.968f; + //float green = 0.960f; + //float blue = 0.978f; + float red = 0.968f; + float green = 0.968f; + float blue = 0.972f; + float white = 0.0f; + + void set_color(float red, float green, float blue, float brightness, float state) + { + } +}; + +} // namespace yeelight_bs2 +} // namespace rgbww +} // namespace esphome diff --git a/rgb_light.h b/rgb_light.h new file mode 100644 index 0000000..d7ce915 --- /dev/null +++ b/rgb_light.h @@ -0,0 +1,61 @@ +#pragma once + +#include +#include + +namespace esphome { +namespace rgbww { +namespace yeelight_bs2 { + +class RGBLight +{ +public: + float red = 0; + float green = 0; + float blue = 0; + float white = 0; + + void set_color(float red, float green, float blue, float brightness, float state) + { + // Overall, the RGB colors are very usable when simply scaling the + // RGB channels with the brightness, but around the white point, + // the color is a bit on the red side of the spectrum. The following + // scaling was created to fix that. + // These functions were created, based on actual measurements while + // using the original firmware. + auto b = brightness * 100.0f; + auto red_w = 1.00f - (-0.0000121426 * b * b - 0.147576 * b + 93.2335) / 100.0f; + auto green_w = 1.00f - (-0.0000242425 * b * b - 0.340449 * b + 88.4423) / 100.0f; + auto blue_w = 1.00f - (-0.0000085869 * b * b - 0.109649 * b + 94.2026) / 100.0f; + + // For colors that are not around the white point, we can scale the + // RGB channels with the requested brightness, resulting in a very + // usable color. Not 100% the same as the original firmware, but + // sometimes even better IMO. + auto red_c = red * brightness; + auto green_c = green * brightness; + auto blue_c = blue * brightness; + + // The actual RGB values are a weighed mix of the above two. + // The closer to the white point, the more the white point + // value applies. + auto level_red = (red_w * ((green+blue)/2)) + (red_c * (1-(green+blue)/2)); + auto level_green = (green_w * ((red+blue)/2)) + (green_c * (1-(red+blue)/2)); + auto level_blue = (blue_w * ((red+green)/2)) + (blue_c * (1-(red+green)/2)); + if (red == 1 && green == 1 && blue == 1) { + level_red = red_w; + level_green = green_w; + level_blue = blue_w; + } + + // Invert the signal. The LEDs in the lamp's circuit are brighter + // when the pwm levels on the GPIO pins are lower. + this->red = 1.0f - level_red; + this->green = 1.0f - level_green; + this->blue = 1.0f - level_blue; + } +}; + +} // namespace yeelight_bs2 +} // namespace rgbww +} // namespace esphome diff --git a/white_light.h b/white_light.h index 4285737..8c7dce3 100644 --- a/white_light.h +++ b/white_light.h @@ -11,7 +11,7 @@ namespace yeelight_bs2 { static const int MIRED_MAX = 153; static const int MIRED_MIN = 588; -struct RgbwLevelsByTemperature { +struct RGBWLevelsByTemperature { float from_temperature; float red; float green; @@ -19,9 +19,9 @@ struct RgbwLevelsByTemperature { float white; }; -using RgbwLevelsTable = std::array; +using RGBWLevelsTable = std::array; -static const RgbwLevelsTable rgbw_levels_1_ {{ +static const RGBWLevelsTable rgbw_levels_1_ {{ { 501.0f, 0.873f, 0.907f, 1.000f, 0.063f }, { 455.0f, 0.873f, 0.896f, 1.000f, 0.063f }, { 417.0f, 0.873f, 0.891f, 1.000f, 0.068f }, @@ -39,7 +39,7 @@ static const RgbwLevelsTable rgbw_levels_1_ {{ { 153.0f, 1.000f, 0.873f, 0.892f, 0.088f } }}; -static const RgbwLevelsTable rgbw_levels_100_ {{ +static const RGBWLevelsTable rgbw_levels_100_ {{ { 501.0f, 0.000f, 0.344f, 1.000f, 0.068f }, { 455.0f, 0.000f, 0.237f, 1.000f, 0.093f }, { 417.0f, 0.000f, 0.186f, 1.000f, 0.120f }, @@ -98,9 +98,9 @@ protected: return brightness; } - RgbwLevelsByTemperature lookup_in_table_(RgbwLevelsTable table, float temperature) + RGBWLevelsByTemperature lookup_in_table_(RGBWLevelsTable table, float temperature) { - for (RgbwLevelsByTemperature& item : table) + for (RGBWLevelsByTemperature& item : table) if (temperature >= item.from_temperature) return item; throw std::invalid_argument("received too low temperature"); diff --git a/yeelight_bs2_light_output.h b/yeelight_bs2_light_output.h index 778ffd4..109243b 100644 --- a/yeelight_bs2_light_output.h +++ b/yeelight_bs2_light_output.h @@ -88,7 +88,7 @@ namespace rgbww { { auto values = state->current_values; - ESP_LOGD(TAG, "B = State %f, RGB %f %f %f, BRI %f, TEMP %f", + ESP_LOGD(TAG, "write_state: STATE %f, RGB %f %f %f, BRI %f, TEMP %f", values.get_state(), values.get_red(), values.get_green(), values.get_blue(), values.get_brightness(), values.get_color_temperature()); @@ -134,6 +134,13 @@ namespace rgbww { { turn_on_in_white_mode_(values.get_color_temperature(), brightness); } + else if ( + values.get_red() == 1 && + values.get_green() == 1 && + values.get_blue() == 1 && + brightness < 0.012f) { + turn_on_in_night_light_mode_(); + } else { // The RGB mode does not use the RGB values as determined by @@ -154,6 +161,8 @@ namespace rgbww { esphome::gpio::GPIOBinaryOutput *master1_; esphome::gpio::GPIOBinaryOutput *master2_; esphome::rgbww::yeelight_bs2::WhiteLight white_light_; + esphome::rgbww::yeelight_bs2::RGBLight rgb_light_; + esphome::rgbww::yeelight_bs2::NightLight night_light_; #ifdef TRANSITION_TO_OFF_BUGFIX float previous_state_ = 1; float previous_brightness_ = -1; @@ -169,56 +178,38 @@ namespace rgbww { master1_->turn_off(); } + void turn_on_in_night_light_mode_() + { + ESP_LOGD(TAG, "Activate Night light feature"); + + night_light_.set_color(1, 1, 1, 0.01, 1); + + ESP_LOGD(TAG, "New LED state : RGBW %f, %f, %f, %f", night_light_.red, night_light_.green, night_light_.blue, night_light_.white); + + // Drive the LEDs. + master2_->turn_on(); + master1_->turn_on(); + red_->set_level(night_light_.red); + green_->set_level(night_light_.green); + blue_->set_level(night_light_.blue); + white_->set_level(night_light_.white); + } + void turn_on_in_rgb_mode_(float red, float green, float blue, float brightness, float state) { ESP_LOGD(TAG, "Activate RGB %f, %f, %f, BRIGHTNESS %f", red, green, blue, brightness); - // The brightness must be at least 3/100 to light up the LEDs. - // During transitions (where state is a fraction between 0 and 1, - // indicating the transition progress) we don't apply this to - // get smoother transitioning when turning on the light. - if (state == 1 && brightness < 0.03f) - brightness = 0.03f; - - // Apply proper color mixing around the RGB white point. - // Overall, the RGB colors are very usable when simply scaling the - // RGB channels with the brightness, but around the white point, - // the color is a bit on the red side of the spectrum. The following - // scaling was created to fix that. - auto red_w = (0.07f + brightness*(0.57f - 0.07f)) * red; - auto green_w = (0.13f + brightness*(1.00f - 0.13f)) * green; - auto blue_w = (0.06f + brightness*(0.45f - 0.06f)) * blue; - - // For other colors, we can simply scale the RGB channels with the - // requested brightness, resulting in a very usable color. Not 100% - // the same as the original firmware, but sometimes even better IMO. - auto red_c = red * brightness; - auto green_c = green * brightness; - auto blue_c = blue * brightness; - - // The actual RGB values are a weighed mix of the above two. - // The closer to the white point, the more the white point - // value applies. - auto level_red = (red_w * ((green+blue)/2)) + (red_c * (1-(green+blue)/2)); - auto level_green = (green_w * ((red+blue)/2)) + (green_c * (1-(red+blue)/2)); - auto level_blue = (blue_w * ((red+green)/2)) + (blue_c * (1-(red+green)/2)); - - // Invert the signal. The LEDs in the lamp's circuit are brighter - // when the pwm levels on the GPIO pins are lower. - level_red = 1.0f - level_red; - level_green = 1.0f - level_green; - level_blue = 1.0f - level_blue; - - ESP_LOGD(TAG, "New LED state : RGBW %f, %f, %f, off", level_red, level_green, level_blue); + rgb_light_.set_color(red, green, blue, brightness, state); + + ESP_LOGD(TAG, "New LED state : RGBW %f, %f, %f, off", rgb_light_.red, rgb_light_.green, rgb_light_.blue); // Drive the LEDs. master2_->turn_on(); master1_->turn_on(); - red_->set_level(level_red); - green_->set_level(level_green); - blue_->set_level(level_blue); - white_->set_level(0); - + red_->set_level(rgb_light_.red); + green_->set_level(rgb_light_.green); + blue_->set_level(rgb_light_.blue); + white_->set_level(rgb_light_.white); } void turn_on_in_white_mode_(float temperature, float brightness)