From c7b4ee5c601c3e2431a4769506f121d6496bc7cc Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 13 Apr 2026 22:40:17 -1000 Subject: [PATCH] fix when on_boot at 800 --- .../components/light/addressable_light.cpp | 49 ++++++++++++------- esphome/components/light/addressable_light.h | 4 +- 2 files changed, 33 insertions(+), 20 deletions(-) diff --git a/esphome/components/light/addressable_light.cpp b/esphome/components/light/addressable_light.cpp index 5f5f123fe7..d2f5913f4b 100644 --- a/esphome/components/light/addressable_light.cpp +++ b/esphome/components/light/addressable_light.cpp @@ -59,23 +59,11 @@ void AddressableLightTransformer::start() { this->light_.correction_.set_local_brightness(255); this->target_color_ *= to_uint8_scale(end_values.get_brightness() * end_values.get_state()); - // When every LED starts at the same color (the common case: plain turn_on/turn_off on a uniform - // strip), interpolate math-only against a single start color. Avoiding the per-step read-back - // through the 8-bit stored byte prevents gamma round-trip quantization from stalling the fade - // at low values (e.g. gamma 2.8 pre-gamma values <27 round to stored 0, freezing progress). - this->uniform_start_color_.reset(); - if (this->light_.size() > 0) { - Color first = this->light_[0].get(); - bool uniform = true; - for (int32_t i = 1; i < this->light_.size(); i++) { - if (this->light_[i].get() != first) { - uniform = false; - break; - } - } - if (uniform) - this->uniform_start_color_ = first; - } + // Uniformity scan is deferred to the first apply() call. start() can run before the underlying + // LED output's setup() has allocated its frame buffer (e.g. on_boot at priority > HARDWARE + // triggering a transition), and reading through ESPColorView would deref a null buffer. + this->uniform_start_scanned_ = false; + this->uniform_start_is_uniform_ = false; } inline constexpr uint8_t subtract_scaled_difference(uint8_t a, uint8_t b, int32_t scale) { @@ -115,7 +103,30 @@ optional AddressableLightTransformer::apply() { // non-linear when applying small deltas. if (smoothed_progress > this->last_transition_progress_ && this->last_transition_progress_ < 1.f) { - if (this->uniform_start_color_.has_value()) { + // Lazy uniformity scan: deferred from start() so the LED output's setup() has run and the + // frame buffer is valid. When every LED already has the same color (the common case: plain + // turn_on/turn_off on a uniform strip), interpolate math-only against a single start color. + // Avoiding the per-step read-back through the 8-bit stored byte prevents gamma round-trip + // quantization from stalling the fade at low values (e.g. gamma 2.8 pre-gamma values <27 + // round to stored 0, freezing progress). + if (!this->uniform_start_scanned_) { + this->uniform_start_scanned_ = true; + if (this->light_.size() > 0) { + Color first = this->light_[0].get(); + bool uniform = true; + for (int32_t i = 1; i < this->light_.size(); i++) { + if (this->light_[i].get() != first) { + uniform = false; + break; + } + } + if (uniform) { + this->uniform_start_color_ = first; + this->uniform_start_is_uniform_ = true; + } + } + } + if (this->uniform_start_is_uniform_) { // All LEDs started at the same color: compute the interpolated value once and write it to // every LED. No read-back, so each LED's stored byte advances through every gamma threshold // as smoothed_progress crosses it, instead of stalling at 0 for low pre-gamma values. @@ -125,7 +136,7 @@ optional AddressableLightTransformer::apply() { // via its read-back. Concurrent per-LED mutation during a transition isn't a pattern we // support, so this is acceptable. // lerp(start, target, progress) via existing helper: target - (target-start)*(1-progress). - const Color &start = *this->uniform_start_color_; + const Color &start = this->uniform_start_color_; int32_t remaining = int32_t(256.f * (1.f - smoothed_progress)); uint8_t r = subtract_scaled_difference(this->target_color_.red, start.red, remaining); uint8_t g = subtract_scaled_difference(this->target_color_.green, start.green, remaining); diff --git a/esphome/components/light/addressable_light.h b/esphome/components/light/addressable_light.h index c4eabbad2e..0202ad380a 100644 --- a/esphome/components/light/addressable_light.h +++ b/esphome/components/light/addressable_light.h @@ -115,7 +115,9 @@ class AddressableLightTransformer : public LightTransformer { AddressableLight &light_; float last_transition_progress_{0.0f}; Color target_color_{}; - optional uniform_start_color_{}; + Color uniform_start_color_{}; + bool uniform_start_scanned_{false}; + bool uniform_start_is_uniform_{false}; }; } // namespace esphome::light