Browse Source

Some code cleanup as preparation for the next bits of development for transition handling.

pull/3/head
Maurice Makaay 3 years ago
parent
commit
e8ae6a9765
3 changed files with 110 additions and 122 deletions
  1. +1
    -1
      color_rgb_light.h
  2. +2
    -2
      color_white_light.h
  3. +107
    -119
      light.h

+ 1
- 1
color_rgb_light.h View File

@ -251,7 +251,7 @@ public:
float blue = 0; float blue = 0;
float white = 0; float white = 0;
void set_color(float red, float green, float blue, float brightness, float state) {
void set_color(float red, float green, float blue, float brightness) {
// Determine the ring level for the color. This is a value between // Determine the ring level for the color. This is a value between
// 0 and 7, determining in what ring of the RGB circle the requested // 0 and 7, determining in what ring of the RGB circle the requested
// color resides. // color resides.


+ 2
- 2
color_white_light.h View File

@ -84,9 +84,9 @@ public:
protected: protected:
float clamp_temperature_(float temperature) float clamp_temperature_(float temperature)
{ {
if (temperature < MIRED_MAX)
if (temperature > MIRED_MAX)
temperature = MIRED_MAX; temperature = MIRED_MAX;
else if (temperature > MIRED_MIN)
else if (temperature < MIRED_MIN)
temperature = MIRED_MIN; temperature = MIRED_MIN;
return temperature; return temperature;
} }


+ 107
- 119
light.h View File

@ -36,6 +36,65 @@ namespace bs2 {
virtual float get_transformer_progress() = 0; virtual float get_transformer_progress() = 0;
}; };
/// This class translates LightColorValues into GPIO duty cycles
/// for representing a requested light color.
class ColorTranslator : public DutyCycles {
public:
void set_light_color_values(light::LightColorValues values) {
// The light is turned off.
if (values.get_state() == 0.0f || values.get_brightness() == 0.0f) {
red = 0.0f;
green = 0.0f;
blue = 0.0f;
white = 0.0f;
return;
}
// At the lowest brightness setting, switch to night light mode.
// In the Yeelight integration in Home Assistant, this feature is
// exposed trough a separate switch. I have found that the switch
// is both confusing and made me run into issues when automating
// the lights.
// I don't simply check for a brightness at or below 0.01 (1%),
// because the lowest brightness setting from Home Assistant
// turns up as 0.011765 in here (which is 3/255).
if (values.get_brightness() < 0.012f) {
// TODO make the color implementations return a DutyCycles object?
// TODO Use polymorphic color classes?
red = night_light_.red;
green = night_light_.green;
blue = night_light_.blue;
white = night_light_.white;
return;
}
// White light mode: temperature + brightness.
if (values.get_white() > 0.0f) {
auto temperature = values.get_color_temperature();
white_light_.set_color(temperature, values.get_brightness());
red = white_light_.red;
green = white_light_.green;
blue = white_light_.blue;
white = white_light_.white;
return;
}
// RGB color mode: red, green, blue + brightness.
rgb_light_.set_color(
values.get_red(), values.get_green(), values.get_blue(),
values.get_brightness());
red = rgb_light_.red;
green = rgb_light_.green;
blue = rgb_light_.blue;
white = rgb_light_.white;
}
protected:
ColorWhiteLight white_light_;
ColorRGBLight rgb_light_;
ColorNightLight night_light_;
};
/// This class is used to handle color transition requirements. /// This class is used to handle color transition requirements.
/// ///
/// When using the default ESPHome logic, transitioning is done by /// When using the default ESPHome logic, transitioning is done by
@ -44,9 +103,16 @@ namespace bs2 {
/// translate these properties into light outputs on every step of the /// translate these properties into light outputs on every step of the
/// way. While this does work, it does not work nicely. /// way. While this does work, it does not work nicely.
/// ///
/// This class will take care of transitioning LEDC light outputs
/// instead, which is what the original firmware in the device does as
/// well. This makes transitions a lot cleaner.
/// For example, when transitioning from warm to cold white light,
/// the color temperature would be transitioned from the old value to
/// the new value. While doing so, the transition hits the middle
/// white light setting, which shows up as a bright flash in the
/// middle of the transition. The original firmware however, shows a
/// smooth transition from warm to cold white light, without any flash.
///
/// This class handles transitions by not varying the light properties
/// over time, but by transitioning the LEDC duty cycle output levels
/// over time. This matches the behavior of the original firmware.
class TransitionHandler { class TransitionHandler {
public: public:
TransitionHandler(LightStateDataExposer *exposer) : exposer_(exposer) {} TransitionHandler(LightStateDataExposer *exposer) : exposer_(exposer) {}
@ -97,22 +163,11 @@ namespace bs2 {
}; };
/// An implementation of the LightOutput interface for the Yeelight /// An implementation of the LightOutput interface for the Yeelight
/// Bedside Lamp 2.
/// Bedside Lamp 2. The function of this class is to translate a
/// required light state into actual physicial GPIO output signals
/// to drive the device's LED circuitry.
class YeelightBS2LightOutput : public Component, public light::LightOutput { class YeelightBS2LightOutput : public Component, public light::LightOutput {
public: public:
light::LightTraits get_traits() override
{
auto traits = light::LightTraits();
traits.set_supports_rgb(true);
traits.set_supports_color_temperature(true);
traits.set_supports_brightness(true);
traits.set_supports_rgb_white_value(false);
traits.set_supports_color_interlock(true);
traits.set_min_mireds(MIRED_MIN);
traits.set_max_mireds(MIRED_MAX);
return traits;
}
/// Set the LEDC output for the red LED circuitry channel. /// Set the LEDC output for the red LED circuitry channel.
void set_red_output(ledc::LEDCOutput *red) { void set_red_output(ledc::LEDCOutput *red) {
red_ = red; red_ = red;
@ -145,18 +200,36 @@ namespace bs2 {
master2_ = master2; master2_ = master2;
} }
void write_state(light::LightState *state)
/// Returns a LightTraits object, which is used to explain to the
/// outside world (e.g. Home Assistant) what features are supported
/// by this device.
light::LightTraits get_traits() override
{ {
if (transition_->handle()) {
ESP_LOGD(TAG, "HANDLE transition!");
}
auto traits = light::LightTraits();
traits.set_supports_rgb(true);
traits.set_supports_color_temperature(true);
traits.set_supports_brightness(true);
traits.set_supports_rgb_white_value(false);
traits.set_supports_color_interlock(true);
traits.set_min_mireds(MIRED_MIN);
traits.set_max_mireds(MIRED_MAX);
return traits;
}
/// Tranlates a requested light state into physicial GPIO outputs.
void write_state(light::LightState *state)
{
auto values = state->current_values; auto values = state->current_values;
// Power down the light when its state is 'off'. // Power down the light when its state is 'off'.
if (values.get_state() == 0) if (values.get_state() == 0)
{ {
turn_off_();
red_->set_level(1.0f);
green_->set_level(1.0f);
blue_->set_level(1.0f);
white_->set_level(0.0f);
master2_->turn_off();
master1_->turn_off();
#ifdef TRANSITION_TO_OFF_BUGFIX #ifdef TRANSITION_TO_OFF_BUGFIX
previous_state_ = -1; previous_state_ = -1;
previous_brightness_ = 0; previous_brightness_ = 0;
@ -164,10 +237,13 @@ namespace bs2 {
return; return;
} }
auto brightness = values.get_brightness();
if (transition_->handle()) {
ESP_LOGD(TAG, "HANDLE transition!");
}
#ifdef TRANSITION_TO_OFF_BUGFIX #ifdef TRANSITION_TO_OFF_BUGFIX
// Remember the brightness that is used when the light is fully ON. // Remember the brightness that is used when the light is fully ON.
auto brightness = values.get_brightness();
if (values.get_state() == 1) { if (values.get_state() == 1) {
previous_brightness_ = brightness; previous_brightness_ = brightness;
} }
@ -183,36 +259,13 @@ namespace bs2 {
previous_state_ = values.get_state(); previous_state_ = values.get_state();
#endif #endif
// At the lowest brightness setting, switch to night light mode.
// In the Yeelight integration in Home Assistant, this feature is
// exposed trough a separate switch. I have found that the switch
// is both confusing and made me run into issues when automating
// the lights.
// I don't simply check for a brightness at or below 0.01 (1%),
// because the lowest brightness setting from Home Assistant
// turns up as 0.011765 in here (which is 3/255).
if (brightness < 0.012f && values.get_state() == 1) {
turn_on_in_night_light_mode_();
return;
}
// Leave it to the default tooling to figure out the basics.
// Because of color interlocking, there are two possible outcomes:
// - red, green, blue zero -> white light color temperature mode
// - cwhite, wwhite zero -> RGB mode
float red, green, blue, cwhite, wwhite;
state->current_values_as_rgbww(
&red, &green, &blue, &cwhite, &wwhite, true, false);
if (cwhite > 0 || wwhite > 0) {
turn_on_in_white_mode_(
values.get_color_temperature(), brightness);
}
else {
turn_on_in_rgb_mode_(
values.get_red(), values.get_green(), values.get_blue(),
brightness, values.get_state());
}
duty_cycles_.set_light_color_values(values);
master2_->turn_on();
master1_->turn_on();
red_->set_level(duty_cycles_.red);
green_->set_level(duty_cycles_.green);
blue_->set_level(duty_cycles_.blue);
white_->set_level(duty_cycles_.white);
} }
protected: protected:
@ -222,10 +275,8 @@ namespace bs2 {
ledc::LEDCOutput *white_; ledc::LEDCOutput *white_;
esphome::gpio::GPIOBinaryOutput *master1_; esphome::gpio::GPIOBinaryOutput *master1_;
esphome::gpio::GPIOBinaryOutput *master2_; esphome::gpio::GPIOBinaryOutput *master2_;
ColorWhiteLight white_light_;
ColorRGBLight rgb_light_;
ColorNightLight night_light_;
TransitionHandler *transition_; TransitionHandler *transition_;
ColorTranslator duty_cycles_; // TODO move to own class DefaultHandler
#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;
@ -238,69 +289,6 @@ namespace bs2 {
void set_light_state_data_exposer(LightStateDataExposer *exposer) { void set_light_state_data_exposer(LightStateDataExposer *exposer) {
transition_ = new TransitionHandler(exposer); transition_ = new TransitionHandler(exposer);
} }
void turn_off_()
{
red_->set_level(1);
green_->set_level(1);
blue_->set_level(1);
white_->set_level(0);
master2_->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)
{
ESP_LOGD(TAG, "Activate RGB %f, %f, %f, BRIGHTNESS %f", red, green, blue, brightness);
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(rgb_light_.red);
green_->set_level(rgb_light_.green);
blue_->set_level(rgb_light_.blue);
white_->turn_off();
}
void turn_on_in_white_mode_(float temperature, float brightness)
{
ESP_LOGD(TAG, "Activate TEMPERATURE %f, BRIGHTNESS %f",
temperature, brightness);
white_light_.set_color(temperature, brightness);
ESP_LOGD(TAG, "New LED state : RGBW %f, %f, %f, %f",
white_light_.red, white_light_.green, white_light_.blue,
white_light_.white);
master2_->turn_on();
master1_->turn_on();
red_->set_level(white_light_.red);
green_->set_level(white_light_.green);
blue_->set_level(white_light_.blue);
white_->set_level(white_light_.white);
}
}; };
class YeelightBS2LightState : public light::LightState, public LightStateDataExposer class YeelightBS2LightState : public light::LightState, public LightStateDataExposer


Loading…
Cancel
Save