From 68d547595ee2177319af3e2572e07da4798331c6 Mon Sep 17 00:00:00 2001 From: Oxan van Leeuwen Date: Sun, 19 Sep 2021 18:31:20 +0200 Subject: [PATCH] Light transition fixes (#2320) --- esphome/components/light/addressable_light.cpp | 11 ++++++----- esphome/components/light/light_state.cpp | 3 +++ esphome/components/light/transformers.h | 8 +++++++- 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/esphome/components/light/addressable_light.cpp b/esphome/components/light/addressable_light.cpp index a8fa2cd7ac..599d43d8b1 100644 --- a/esphome/components/light/addressable_light.cpp +++ b/esphome/components/light/addressable_light.cpp @@ -62,10 +62,13 @@ void AddressableLightTransformer::start() { } optional AddressableLightTransformer::apply() { - // Don't try to transition over running effects, instead immediately use the target values. write_state() and the - // effects pick up the change from current_values. + float smoothed_progress = LightTransitionTransformer::smoothed_progress(this->get_progress_()); + + // When running an output-buffer modifying effect, don't try to transition individual LEDs, but instead just fade the + // LightColorValues. write_state() then picks up the change in brightness, and the color change is picked up by the + // effects which respect it. if (this->light_.is_effect_active()) - return this->target_values_; + return LightColorValues::lerp(this->get_start_values(), this->get_target_values(), smoothed_progress); // Use a specialized transition for addressable lights: instead of using a unified transition for // all LEDs, we use the current state of each LED as the start. @@ -75,8 +78,6 @@ optional AddressableLightTransformer::apply() { // Instead, we "fake" the look of the LERP by using an exponential average over time and using // dynamically-calculated alpha values to match the look. - float smoothed_progress = LightTransitionTransformer::smoothed_progress(this->get_progress_()); - float denom = (1.0f - smoothed_progress); float alpha = denom == 0.0f ? 0.0f : (smoothed_progress - this->last_transition_progress_) / denom; diff --git a/esphome/components/light/light_state.cpp b/esphome/components/light/light_state.cpp index d7f539a8d8..398546a09f 100644 --- a/esphome/components/light/light_state.cpp +++ b/esphome/components/light/light_state.cpp @@ -121,6 +121,9 @@ void LightState::loop() { } if (this->transformer_->is_finished()) { + // if the transition has written directly to the output, current_values is outdated, so update it + this->current_values = this->transformer_->get_target_values(); + this->transformer_->stop(); this->transformer_ = nullptr; this->target_state_reached_callback_.call(); diff --git a/esphome/components/light/transformers.h b/esphome/components/light/transformers.h index d501d53f72..90646f4e61 100644 --- a/esphome/components/light/transformers.h +++ b/esphome/components/light/transformers.h @@ -12,12 +12,18 @@ namespace light { class LightTransitionTransformer : public LightTransformer { public: void start() override { - // When turning light on from off state, use colors from target state. + // When turning light on from off state, use target state and only increase brightness from zero. if (!this->start_values_.is_on() && this->target_values_.is_on()) { this->start_values_ = LightColorValues(this->target_values_); this->start_values_.set_brightness(0.0f); } + // When turning light off from on state, use source state and only decrease brightness to zero. + if (this->start_values_.is_on() && !this->target_values_.is_on()) { + this->target_values_ = LightColorValues(this->start_values_); + this->target_values_.set_brightness(0.0f); + } + // When changing color mode, go through off state, as color modes are orthogonal and there can't be two active. if (this->start_values_.get_color_mode() != this->target_values_.get_color_mode()) { this->changing_color_mode_ = true;