|
|
@ -1,136 +0,0 @@ |
|
|
|
#pragma once |
|
|
|
|
|
|
|
#include "../common.h" |
|
|
|
#include "color_instant_handler.h" |
|
|
|
#include "interfaces.h" |
|
|
|
#include "gpio_outputs.h" |
|
|
|
|
|
|
|
namespace esphome { |
|
|
|
namespace xiaomi { |
|
|
|
namespace bslamp2 { |
|
|
|
|
|
|
|
/** |
|
|
|
* This class is used to handle specific light color transition requirements |
|
|
|
* for the device. |
|
|
|
* |
|
|
|
* When using the default ESPHome logic, transitioning is done by |
|
|
|
* transitioning all light properties linearly from the original values to |
|
|
|
* the new values, and letting the light output object translate these |
|
|
|
* properties into light outputs on every step of the way. While this does |
|
|
|
* work, it does not work nicely. |
|
|
|
* |
|
|
|
* 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 ColorTransitionHandler : public GPIOOutputs { |
|
|
|
public: |
|
|
|
ColorTransitionHandler(LightStateTransformerInspector *inspector) : transformer_(inspector) {} |
|
|
|
|
|
|
|
light::LightColorValues get_end_values() { return end_light_values_; } |
|
|
|
|
|
|
|
bool set_light_color_values(light::LightColorValues values) { |
|
|
|
if (!light_state_has_active_transition_()) { |
|
|
|
// Remember the last active light color values. When a transition |
|
|
|
// is detected, we'll use these as the starting point. It is not |
|
|
|
// possible to use the current values at that point, because the |
|
|
|
// transition is already in progress by the time the transition |
|
|
|
// is detected. |
|
|
|
start_light_values_ = values; |
|
|
|
|
|
|
|
active_ = false; |
|
|
|
return false; |
|
|
|
} |
|
|
|
|
|
|
|
// When a fresh transition is started, then compute the GPIO outputs |
|
|
|
// to use for both the start and end point. This transition handler |
|
|
|
// will then transition linearly between these two. |
|
|
|
if (is_fresh_transition_()) { |
|
|
|
start_->set_light_color_values(start_light_values_); |
|
|
|
end_light_values_ = transformer_->get_end_values(); |
|
|
|
end_->set_light_color_values(end_light_values_); |
|
|
|
active_ = true; |
|
|
|
} |
|
|
|
// When a transition is modified, then use the current GPIO outputs |
|
|
|
// as the new starting point. |
|
|
|
else if (is_modified_transition_()) { |
|
|
|
this->copy_to(start_); |
|
|
|
end_light_values_ = transformer_->get_end_values(); |
|
|
|
end_->set_light_color_values(end_light_values_); |
|
|
|
} |
|
|
|
|
|
|
|
light_mode = end_->light_mode; |
|
|
|
progress_ = transformer_->get_progress(); |
|
|
|
|
|
|
|
// Determine required GPIO outputs for current transition progress. |
|
|
|
|
|
|
|
// In night light mode, do not use actual transitions. Transitioning |
|
|
|
// between colors at the very low LED output levels of the night light, |
|
|
|
// results in light drops, which are plain ugly to watch. |
|
|
|
if (light_mode == "night") { |
|
|
|
red = end_->red; |
|
|
|
green = end_->green; |
|
|
|
blue = end_->blue; |
|
|
|
white = end_->white; |
|
|
|
} |
|
|
|
// In other light modes, apply smooth transitioning. |
|
|
|
else { |
|
|
|
auto smoothed = light::LightTransitionTransformer::smoothed_progress(progress_); |
|
|
|
red = esphome::lerp(smoothed, start_->red, end_->red); |
|
|
|
green = esphome::lerp(smoothed, start_->green, end_->green); |
|
|
|
blue = esphome::lerp(smoothed, start_->blue, end_->blue); |
|
|
|
white = esphome::lerp(smoothed, start_->white, end_->white); |
|
|
|
} |
|
|
|
|
|
|
|
return true; |
|
|
|
} |
|
|
|
|
|
|
|
protected: |
|
|
|
bool active_ = false; |
|
|
|
float progress_ = 0.0f; |
|
|
|
LightStateTransformerInspector *transformer_; |
|
|
|
light::LightColorValues start_light_values_; |
|
|
|
light::LightColorValues end_light_values_; |
|
|
|
GPIOOutputs *start_ = new ColorInstantHandler(); |
|
|
|
GPIOOutputs *end_ = new ColorInstantHandler(); |
|
|
|
|
|
|
|
/** |
|
|
|
* Checks if the LightState currently has an active LightTransformer. |
|
|
|
*/ |
|
|
|
bool light_state_has_active_transition_() { |
|
|
|
if (!transformer_->is_active()) |
|
|
|
return false; |
|
|
|
if (!transformer_->is_transition()) |
|
|
|
return false; |
|
|
|
return true; |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* Checks if a fresh transitioning is started. |
|
|
|
* A transitioning is fresh when no existing transition is active. |
|
|
|
*/ |
|
|
|
bool is_fresh_transition_() { return active_ == false; } |
|
|
|
|
|
|
|
/** |
|
|
|
* Checks if a new end state is set, while an existing transition |
|
|
|
* is active. This might be detected in two ways: |
|
|
|
* - the end color has been updated |
|
|
|
* - the progress has been reverted |
|
|
|
*/ |
|
|
|
bool is_modified_transition_() { |
|
|
|
auto new_end_light_values = transformer_->get_end_values(); |
|
|
|
auto new_progress = transformer_->get_progress(); |
|
|
|
return (new_end_light_values != end_light_values_ || new_progress < progress_); |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
} // namespace bslamp2 |
|
|
|
} // namespace xiaomi |
|
|
|
} // namespace esphome |