Browse Source

Added night light setting, when choosing white light (#FFFFFF) at the lowest brightness level.

pull/3/head
Maurice Makaay 3 years ago
parent
commit
cd64ab7cbe
4 changed files with 130 additions and 49 deletions
  1. +29
    -0
      night_light.h
  2. +61
    -0
      rgb_light.h
  3. +6
    -6
      white_light.h
  4. +34
    -43
      yeelight_bs2_light_output.h

+ 29
- 0
night_light.h View File

@ -0,0 +1,29 @@
#pragma once
#include <array>
#include <stdexcept>
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

+ 61
- 0
rgb_light.h View File

@ -0,0 +1,61 @@
#pragma once
#include <array>
#include <stdexcept>
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

+ 6
- 6
white_light.h View File

@ -11,7 +11,7 @@ namespace yeelight_bs2 {
static const int MIRED_MAX = 153; static const int MIRED_MAX = 153;
static const int MIRED_MIN = 588; static const int MIRED_MIN = 588;
struct RgbwLevelsByTemperature {
struct RGBWLevelsByTemperature {
float from_temperature; float from_temperature;
float red; float red;
float green; float green;
@ -19,9 +19,9 @@ struct RgbwLevelsByTemperature {
float white; float white;
}; };
using RgbwLevelsTable = std::array<RgbwLevelsByTemperature, 15>;
using RGBWLevelsTable = std::array<RGBWLevelsByTemperature, 15>;
static const RgbwLevelsTable rgbw_levels_1_ {{
static const RGBWLevelsTable rgbw_levels_1_ {{
{ 501.0f, 0.873f, 0.907f, 1.000f, 0.063f }, { 501.0f, 0.873f, 0.907f, 1.000f, 0.063f },
{ 455.0f, 0.873f, 0.896f, 1.000f, 0.063f }, { 455.0f, 0.873f, 0.896f, 1.000f, 0.063f },
{ 417.0f, 0.873f, 0.891f, 1.000f, 0.068f }, { 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 } { 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 }, { 501.0f, 0.000f, 0.344f, 1.000f, 0.068f },
{ 455.0f, 0.000f, 0.237f, 1.000f, 0.093f }, { 455.0f, 0.000f, 0.237f, 1.000f, 0.093f },
{ 417.0f, 0.000f, 0.186f, 1.000f, 0.120f }, { 417.0f, 0.000f, 0.186f, 1.000f, 0.120f },
@ -98,9 +98,9 @@ protected:
return brightness; 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) if (temperature >= item.from_temperature)
return item; return item;
throw std::invalid_argument("received too low temperature"); throw std::invalid_argument("received too low temperature");


+ 34
- 43
yeelight_bs2_light_output.h View File

@ -88,7 +88,7 @@ namespace rgbww {
{ {
auto values = state->current_values; 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_state(),
values.get_red(), values.get_green(), values.get_blue(), values.get_red(), values.get_green(), values.get_blue(),
values.get_brightness(), values.get_color_temperature()); values.get_brightness(), values.get_color_temperature());
@ -134,6 +134,13 @@ namespace rgbww {
{ {
turn_on_in_white_mode_(values.get_color_temperature(), brightness); 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 else
{ {
// The RGB mode does not use the RGB values as determined by // 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 *master1_;
esphome::gpio::GPIOBinaryOutput *master2_; esphome::gpio::GPIOBinaryOutput *master2_;
esphome::rgbww::yeelight_bs2::WhiteLight white_light_; 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 #ifdef TRANSITION_TO_OFF_BUGFIX
float previous_state_ = 1; float previous_state_ = 1;
float previous_brightness_ = -1; float previous_brightness_ = -1;
@ -169,56 +178,38 @@ namespace rgbww {
master1_->turn_off(); 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) 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); 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. // Drive the LEDs.
master2_->turn_on(); master2_->turn_on();
master1_->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) void turn_on_in_white_mode_(float temperature, float brightness)


Loading…
Cancel
Save