Compare commits

...
Author SHA1 Message Date
J. Nick Koston 9180658d8d Merge branch 'light-led-strip-helpers' into light-led-strip-helpers-migrate 2026-10-22 15:53:42 -10:00
J. Nick Koston 54de6dded4 [light] Drop the effect pointer too when view() falls back to the scratch pixel 2026-10-22 15:53:42 -10:00
J. Nick Koston 02f3f8249a [light] Drop stale comments and redundant includes in the LED strip drivers 2026-10-22 15:48:48 -10:00
J. Nick Koston 0cf6cbd1ec Merge branch 'light-led-strip-helpers' into light-led-strip-helpers-migrate 2026-10-22 15:48:02 -10:00
J. Nick Koston b8e109aa2e [light] Trim the view() comment and a repeated scratch test 2026-10-22 15:47:36 -10:00
J. Nick Koston cbce5542f9 Merge branch 'light-led-strip-helpers' into light-led-strip-helpers-migrate 2026-10-22 15:34:40 -10:00
J. Nick Koston 1233e5b7a1 [light] Name the throttle_show_ test after the helper 2026-10-22 15:34:37 -10:00
J. Nick Koston e7a653cc5f [light] Use the throttle_show_ name in the drivers 2026-10-22 15:34:37 -10:00
J. Nick Koston 79b8276c70 Merge branch 'light-led-strip-helpers' into light-led-strip-helpers-migrate 2026-10-22 15:34:29 -10:00
J. Nick Koston 62310edd98 [light] Rename refresh_too_soon_ to throttle_show_ 2026-10-22 15:34:28 -10:00
J. Nick Koston 716b5ff166 [light] Clear the effect data with an inline guarded memset in each driver 2026-10-22 15:22:09 -10:00
J. Nick Koston d403b74954 Merge branch 'light-led-strip-helpers' into light-led-strip-helpers-migrate 2026-10-22 15:21:44 -10:00
J. Nick Koston 6bae24e771 [light] Drop zero_effect_data_, a guarded memset is not worth a helper 2026-10-22 15:21:43 -10:00
J. Nick Koston 136500ee95 [i2s_clockless_led_strip] Keep the effect data after the color data in one block 2026-10-22 15:20:19 -10:00
J. Nick Koston 13c97ad7d4 [light] Inline the LED strip traits and fall back to pixel 0 of the scratch 2026-10-22 15:16:26 -10:00
J. Nick Koston 3c32d960e9 Merge branch 'light-led-strip-helpers' into light-led-strip-helpers-migrate 2026-10-22 15:16:26 -10:00
J. Nick Koston 7763b1ee3d Merge branch 'light-led-strip-helpers' into light-led-strip-helpers-migrate 2026-10-22 15:14:06 -10:00
J. Nick Koston 333436e82c [light] Shrink the LED strip scratch pixel to four bytes 2026-10-22 15:14:05 -10:00
J. Nick Koston ec62b755db [light] Use the shared LED strip helpers in the five addressable drivers 2026-10-22 15:13:47 -10:00
J. Nick Koston e878d76e3a [light] Add shared LED strip traits, null-safe pixel view and refresh gate helpers 2026-10-22 15:03:48 -10:00
13 changed files with 172 additions and 159 deletions

No files matched your search

@@ -293,14 +293,9 @@ void BekenSPILEDStripLightOutput::set_led_params(uint8_t bit0, uint8_t bit1, uin
}
void BekenSPILEDStripLightOutput::write_state(light::LightState *state) {
// protect from refreshing too often
uint32_t now = micros();
if (this->max_refresh_rate_.has_value() && *this->max_refresh_rate_ != 0 &&
(now - this->last_refresh_) < *this->max_refresh_rate_) {
// try again next loop iteration, so that this change won't get lost
this->schedule_show();
if (this->throttle_show_(now, this->last_refresh_, this->max_refresh_rate_))
return;
}
this->last_refresh_ = now;
this->mark_shown_();
@@ -369,17 +364,6 @@ void BekenSPILEDStripLightOutput::write_state(light::LightState *state) {
this->status_clear_warning();
}
light::ESPColorView BekenSPILEDStripLightOutput::get_view_internal(int32_t index) const {
const light::ChannelColors &colors = this->channel_colors_;
uint8_t *led = this->buf_ + (index * colors.bytes_per_led());
return {led + colors.r,
led + colors.g,
led + colors.b,
colors.has_white() ? led + colors.w : nullptr,
&this->effect_data_[index],
&this->correction_};
}
void BekenSPILEDStripLightOutput::dump_config() {
ESP_LOGCONFIG(TAG,
"Beken SPI LED Strip:\n"
@@ -390,7 +374,7 @@ void BekenSPILEDStripLightOutput::dump_config() {
" Channel colors: %s\n"
" Max refresh rate: %" PRIu32 "\n"
" Number of LEDs: %u",
this->channel_colors_.to_string(channel_colors), this->max_refresh_rate_.value_or(0), this->num_leds_);
this->channel_colors_.to_string(channel_colors), this->max_refresh_rate_, this->num_leds_);
}
float BekenSPILEDStripLightOutput::get_setup_priority() const { return setup_priority::HARDWARE; }
@@ -18,15 +18,7 @@ class BekenSPILEDStripLightOutput final : public light::AddressableLight {
float get_setup_priority() const override;
int32_t size() const override { return this->num_leds_; }
light::LightTraits get_traits() override {
auto traits = light::LightTraits();
if (this->channel_colors_.has_white()) {
traits.set_supported_color_modes({light::ColorMode::RGB_WHITE, light::ColorMode::WHITE});
} else {
traits.set_supported_color_modes({light::ColorMode::RGB});
}
return traits;
}
light::LightTraits get_traits() override { return this->channel_colors_.traits(); }
void set_pin(uint8_t pin) { this->pin_ = pin; }
void set_num_leds(uint16_t num_leds) { this->num_leds_ = num_leds; }
@@ -38,14 +30,16 @@ class BekenSPILEDStripLightOutput final : public light::AddressableLight {
void set_led_params(uint8_t bit0, uint8_t bit1, uint32_t spi_frequency);
void clear_effect_data() override {
for (int i = 0; i < this->size(); i++)
this->effect_data_[i] = 0;
if (this->effect_data_ != nullptr)
memset(this->effect_data_, 0, this->num_leds_);
}
void dump_config() override;
protected:
light::ESPColorView get_view_internal(int32_t index) const override;
light::ESPColorView get_view_internal(int32_t index) const override {
return this->channel_colors_.view(this->buf_, this->effect_data_, index, &this->correction_);
}
size_t get_buffer_size_() const { return this->num_leds_ * this->channel_colors_.bytes_per_led(); }
@@ -62,7 +56,7 @@ class BekenSPILEDStripLightOutput final : public light::AddressableLight {
light::ChannelColors channel_colors_{0, 1, 2, light::ChannelColors::NO_WHITE};
uint32_t last_refresh_{0};
optional<uint32_t> max_refresh_rate_{};
uint32_t max_refresh_rate_{0};
};
} // namespace esphome::beken_spi_led_strip
@@ -157,14 +157,9 @@ void ESP32RMTLEDStripLightOutput::set_led_params(uint32_t bit0_high, uint32_t bi
}
void ESP32RMTLEDStripLightOutput::write_state(light::LightState *state) {
// protect from refreshing too often
uint32_t now = micros();
auto rate = this->max_refresh_rate_.value_or(0);
if (rate != 0 && (now - this->last_refresh_) < rate) {
// try again next loop iteration, so that this change won't get lost
this->schedule_show();
if (this->throttle_show_(now, this->last_refresh_, this->max_refresh_rate_))
return;
}
this->last_refresh_ = now;
this->mark_shown_();
@@ -217,17 +212,6 @@ void ESP32RMTLEDStripLightOutput::write_state(light::LightState *state) {
}
}
light::ESPColorView ESP32RMTLEDStripLightOutput::get_view_internal(int32_t index) const {
const light::ChannelColors &colors = this->channel_colors_;
uint8_t *led = this->buf_ + (index * colors.bytes_per_led());
return {led + colors.r,
led + colors.g,
led + colors.b,
colors.has_white() ? led + colors.w : nullptr,
&this->effect_data_[index],
&this->correction_};
}
void ESP32RMTLEDStripLightOutput::dump_config() {
ESP_LOGCONFIG(TAG,
"ESP32 RMT LED Strip:\n"
@@ -239,7 +223,7 @@ void ESP32RMTLEDStripLightOutput::dump_config() {
" Channel colors: %s\n"
" Max refresh rate: %" PRIu32 "\n"
" Number of LEDs: %u",
this->channel_colors_.to_string(channel_colors), this->max_refresh_rate_.value_or(0), this->num_leds_);
this->channel_colors_.to_string(channel_colors), this->max_refresh_rate_, this->num_leds_);
}
float ESP32RMTLEDStripLightOutput::get_setup_priority() const { return setup_priority::HARDWARE; }
@@ -29,15 +29,7 @@ class ESP32RMTLEDStripLightOutput final : public light::AddressableLight {
float get_setup_priority() const override;
int32_t size() const override { return this->num_leds_; }
light::LightTraits get_traits() override {
auto traits = light::LightTraits();
if (this->channel_colors_.has_white()) {
traits.set_supported_color_modes({light::ColorMode::RGB_WHITE, light::ColorMode::WHITE});
} else {
traits.set_supported_color_modes({light::ColorMode::RGB});
}
return traits;
}
light::LightTraits get_traits() override { return this->channel_colors_.traits(); }
void set_pin(uint8_t pin) { this->pin_ = pin; }
void set_inverted(bool inverted) { this->invert_out_ = inverted; }
@@ -55,14 +47,16 @@ class ESP32RMTLEDStripLightOutput final : public light::AddressableLight {
void set_rmt_symbols(uint32_t rmt_symbols) { this->rmt_symbols_ = rmt_symbols; }
void clear_effect_data() override {
for (int i = 0; i < this->size(); i++)
this->effect_data_[i] = 0;
if (this->effect_data_ != nullptr)
memset(this->effect_data_, 0, this->num_leds_);
}
void dump_config() override;
protected:
light::ESPColorView get_view_internal(int32_t index) const override;
light::ESPColorView get_view_internal(int32_t index) const override {
return this->channel_colors_.view(this->buf_, this->effect_data_, index, &this->correction_);
}
size_t get_buffer_size_() const { return this->num_leds_ * this->channel_colors_.bytes_per_led(); }
@@ -86,7 +80,7 @@ class ESP32RMTLEDStripLightOutput final : public light::AddressableLight {
light::ChannelColors channel_colors_{0, 1, 2, light::ChannelColors::NO_WHITE};
uint32_t last_refresh_{0};
optional<uint32_t> max_refresh_rate_{};
uint32_t max_refresh_rate_{0};
};
} // namespace esphome::esp32_rmt_led_strip
@@ -149,17 +149,12 @@ bool I2SClocklessLedStrip::allocate_buffers_() {
RAMAllocator<uint8_t> allocator(RAMAllocator<uint8_t>::ALLOC_INTERNAL);
this->i2s_data_ = allocator.allocate(i2s_data_bytes);
if (this->i2s_data_ != nullptr) {
this->color_data_ = allocator.allocate(this->color_data_bytes_);
// The effect data follows the color data
this->color_data_ = allocator.allocate(this->color_data_bytes_ + this->num_leds_);
if (this->color_data_ != nullptr) {
this->effect_data_ = allocator.allocate(this->num_leds_);
if (this->effect_data_ != nullptr) {
memset(this->i2s_data_, 0, i2s_data_bytes);
memset(this->color_data_, 0, this->color_data_bytes_);
memset(this->effect_data_, 0, this->num_leds_);
return true;
}
allocator.deallocate(this->color_data_, this->color_data_bytes_);
this->color_data_ = nullptr;
memset(this->i2s_data_, 0, i2s_data_bytes);
memset(this->color_data_, 0, this->color_data_bytes_ + this->num_leds_);
return true;
}
allocator.deallocate(this->i2s_data_, i2s_data_bytes);
this->i2s_data_ = nullptr;
@@ -167,16 +162,6 @@ bool I2SClocklessLedStrip::allocate_buffers_() {
return false;
}
light::LightTraits I2SClocklessLedStrip::get_traits() {
auto traits = light::LightTraits();
if (this->channel_colors_.has_white()) {
traits.set_supported_color_modes({light::ColorMode::RGB_WHITE, light::ColorMode::WHITE});
} else {
traits.set_supported_color_modes({light::ColorMode::RGB});
}
return traits;
}
void I2SClocklessLedStrip::write_state(light::LightState *state) {
if (this->is_failed())
return;
@@ -236,19 +221,6 @@ bool IRAM_ATTR HOT I2SClocklessLedStrip::i2s_on_sent_callback(i2s_chan_handle_t
return false;
}
void I2SClocklessLedStrip::clear_effect_data() { memset(this->effect_data_, 0, this->num_leds_); }
light::ESPColorView I2SClocklessLedStrip::get_view_internal(int32_t index) const {
const light::ChannelColors &colors = this->channel_colors_;
uint8_t *led = this->color_data_ + (index * colors.bytes_per_led());
return {led + colors.r,
led + colors.g,
led + colors.b,
colors.has_white() ? led + colors.w : nullptr,
&this->effect_data_[index],
&this->correction_};
}
} // namespace esphome::i2s_clockless_led_strip
#endif
@@ -8,6 +8,7 @@
#include <atomic>
#include <cstdint>
#include <cstring>
#include "esphome/core/component.h"
#include "esphome/core/log.h"
@@ -27,20 +28,27 @@ class I2SClocklessLedStrip final : public light::AddressableLight {
float get_setup_priority() const override;
void setup() override;
light::LightTraits get_traits() override;
light::LightTraits get_traits() override { return this->channel_colors_.traits(); }
int32_t size() const override { return this->num_leds_; }
void clear_effect_data() override;
void clear_effect_data() override {
if (uint8_t *effect = this->effect_data_())
memset(effect, 0, this->num_leds_);
}
void write_state(light::LightState *state) override;
protected:
static bool i2s_on_sent_callback(i2s_chan_handle_t handle, i2s_event_data_t *event, void *user_ctx);
bool allocate_buffers_();
light::ESPColorView get_view_internal(int32_t index) const override;
uint8_t *effect_data_() const {
return this->color_data_ == nullptr ? nullptr : this->color_data_ + this->color_data_bytes_;
}
light::ESPColorView get_view_internal(int32_t index) const override {
return this->channel_colors_.view(this->color_data_, this->effect_data_(), index, &this->correction_);
}
const size_t color_data_bytes_;
uint8_t *effect_data_{nullptr};
uint8_t *color_data_{nullptr};
uint8_t *i2s_data_{nullptr};
size_t i2s_data_sent_{0};
@@ -134,6 +134,13 @@ class AddressableLight : public LightOutput, public Component {
#endif
}
virtual ESPColorView get_view_internal(int32_t index) const = 0;
// Queues the frame for the next loop and returns true until interval_us has passed since last_us
bool throttle_show_(uint32_t now_us, uint32_t last_us, uint32_t interval_us) {
if (now_us - last_us >= interval_us)
return false;
this->schedule_show();
return true;
}
#ifdef USE_LIGHT_POWER_ESTIMATION
friend class AddressableLightState;
+27
View File
@@ -2,6 +2,9 @@
#include <cstdint>
#include "esp_color_view.h"
#include "light_traits.h"
namespace esphome::light {
/// Which byte of an addressable LED's data carries each colour.
@@ -36,6 +39,30 @@ struct ChannelColors {
buf[this->bytes_per_led()] = '\0';
return buf;
}
/// Colour modes of a strip with this channel order.
LightTraits traits() const {
LightTraits traits;
if (this->has_white()) {
traits.set_supported_color_modes({ColorMode::RGB_WHITE, ColorMode::WHITE});
} else {
traits.set_supported_color_modes({ColorMode::RGB});
}
return traits;
}
/// View of LED `index` in `buf`; a scratch pixel while `buf` or `effect_data` is unallocated (failed setup()).
ESPColorView view(uint8_t *buf, uint8_t *effect_data, int32_t index, const ESPColorCorrection *correction) const {
static uint8_t scratch[4];
if (buf == nullptr || effect_data == nullptr) {
buf = scratch;
effect_data = nullptr;
index = 0;
}
uint8_t *led = buf + index * this->bytes_per_led();
return {led + this->r, led + this->g, led + this->b, this->has_white() ? led + this->w : nullptr,
effect_data + index, correction};
}
};
} // namespace esphome::light
@@ -141,32 +141,13 @@ void LN882xSPILEDStripLightOutput::dump_config() {
float LN882xSPILEDStripLightOutput::get_setup_priority() const { return setup_priority::HARDWARE; }
light::LightTraits LN882xSPILEDStripLightOutput::get_traits() {
auto traits = light::LightTraits();
if (this->channel_colors_.has_white()) {
traits.set_supported_color_modes({light::ColorMode::RGB_WHITE, light::ColorMode::WHITE});
} else {
traits.set_supported_color_modes({light::ColorMode::RGB});
}
return traits;
}
void LN882xSPILEDStripLightOutput::clear_effect_data() {
if (this->effect_data_ != nullptr)
memset(this->effect_data_, 0, this->num_leds_);
}
void LN882xSPILEDStripLightOutput::write_state(light::LightState *state) {
if (this->is_failed())
return;
// protect from refreshing too often
const uint32_t now = micros();
if (this->max_refresh_rate_ != 0 && (now - this->last_refresh_) < this->max_refresh_rate_) {
// try again next loop iteration, so that this change won't get lost
this->schedule_show();
if (this->throttle_show_(now, this->last_refresh_, this->max_refresh_rate_))
return;
}
if (spi_dma_busy()) {
// Busy sending the last frame, try again later.
@@ -191,20 +172,6 @@ void LN882xSPILEDStripLightOutput::write_state(light::LightState *state) {
spi_dma_transmit(this->dma_data_, this->dma_data_bytes_);
}
light::ESPColorView LN882xSPILEDStripLightOutput::get_view_internal(int32_t index) const {
const light::ChannelColors &colors = this->channel_colors_;
// After a failed setup() effects and partitions still write pixels, so they get a scratch pixel.
static uint8_t scratch[5];
const bool ready = this->color_data_ != nullptr;
uint8_t *led = ready ? this->color_data_ + (index * colors.bytes_per_led()) : scratch;
return {led + colors.r,
led + colors.g,
led + colors.b,
colors.has_white() ? led + colors.w : nullptr,
ready ? &this->effect_data_[index] : scratch + 4,
&this->correction_};
}
} // namespace esphome::ln882x_spi_led_strip
#endif // USE_LN882X
@@ -23,16 +23,21 @@ class LN882xSPILEDStripLightOutput final : public light::AddressableLight {
void dump_config() override;
float get_setup_priority() const override;
light::LightTraits get_traits() override;
light::LightTraits get_traits() override { return this->channel_colors_.traits(); }
int32_t size() const override { return this->num_leds_; }
void clear_effect_data() override;
void clear_effect_data() override {
if (this->effect_data_ != nullptr)
memset(this->effect_data_, 0, this->num_leds_);
}
void write_state(light::LightState *state) override;
/// Set a maximum refresh rate in µs as some lights do not like being updated too often.
void set_max_refresh_rate(uint32_t interval_us) { this->max_refresh_rate_ = interval_us; }
protected:
light::ESPColorView get_view_internal(int32_t index) const override;
light::ESPColorView get_view_internal(int32_t index) const override {
return this->channel_colors_.view(this->color_data_, this->effect_data_, index, &this->correction_);
}
const size_t color_data_bytes_;
const size_t dma_data_bytes_;
@@ -141,17 +141,6 @@ void RP2040PIOLEDStripLightOutput::write_state(light::LightState *state) {
dma_channel_transfer_from_buffer_now(this->dma_chan_, this->buf_, this->get_buffer_size_());
}
light::ESPColorView RP2040PIOLEDStripLightOutput::get_view_internal(int32_t index) const {
const light::ChannelColors &colors = this->channel_colors_;
uint8_t *led = this->buf_ + (index * colors.bytes_per_led());
return {led + colors.r,
led + colors.g,
led + colors.b,
colors.has_white() ? led + colors.w : nullptr,
&this->effect_data_[index],
&this->correction_};
}
void RP2040PIOLEDStripLightOutput::dump_config() {
char channel_colors[5];
ESP_LOGCONFIG(TAG,
@@ -37,13 +37,7 @@ class RP2040PIOLEDStripLightOutput final : public light::AddressableLight {
float get_setup_priority() const override;
int32_t size() const override { return this->num_leds_; }
light::LightTraits get_traits() override {
auto traits = light::LightTraits();
this->channel_colors_.has_white()
? traits.set_supported_color_modes({light::ColorMode::RGB_WHITE, light::ColorMode::WHITE})
: traits.set_supported_color_modes({light::ColorMode::RGB});
return traits;
}
light::LightTraits get_traits() override { return this->channel_colors_.traits(); }
void set_pin(uint8_t pin) { this->pin_ = pin; }
void set_num_leds(uint32_t num_leds) { this->num_leds_ = num_leds; }
void set_channel_colors(light::ChannelColors channel_colors) { this->channel_colors_ = channel_colors; }
@@ -56,15 +50,16 @@ class RP2040PIOLEDStripLightOutput final : public light::AddressableLight {
void set_chipset(Chipset chipset) { this->chipset_ = chipset; };
void clear_effect_data() override {
for (int i = 0; i < this->size(); i++) {
this->effect_data_[i] = 0;
}
if (this->effect_data_ != nullptr)
memset(this->effect_data_, 0, this->num_leds_);
}
void dump_config() override;
protected:
light::ESPColorView get_view_internal(int32_t index) const override;
light::ESPColorView get_view_internal(int32_t index) const override {
return this->channel_colors_.view(this->buf_, this->effect_data_, index, &this->correction_);
}
size_t get_buffer_size_() const { return this->num_leds_ * this->channel_colors_.bytes_per_led(); }
@@ -84,7 +79,6 @@ class RP2040PIOLEDStripLightOutput final : public light::AddressableLight {
light::ChannelColors channel_colors_{0, 1, 2, light::ChannelColors::NO_WHITE};
Chipset chipset_{CHIPSET_CUSTOM};
uint32_t last_refresh_{0};
float max_refresh_rate_;
const pio_program_t *program_;
@@ -0,0 +1,88 @@
#include <gtest/gtest.h>
#include <array>
#include <cstdint>
#include "esphome/components/light/addressable_light.h"
#include "esphome/components/light/channel_colors.h"
#include "esphome/components/light/light_state.h"
namespace esphome::light::testing {
namespace {
constexpr ChannelColors GRB{1, 0, 2, ChannelColors::NO_WHITE};
constexpr ChannelColors GRBW{1, 0, 2, 3};
// Exposes the protected strip helpers
class HelperStrip : public AddressableLight {
public:
int32_t size() const override { return 1; }
void clear_effect_data() override {}
LightTraits get_traits() override { return {}; }
void write_state(LightState *state) override {}
using AddressableLight::throttle_show_;
protected:
ESPColorView get_view_internal(int32_t index) const override {
return GRB.view(nullptr, nullptr, index, &this->correction_);
}
};
} // namespace
TEST(LedStripHelpers, TraitsFollowTheWhiteChannel) {
const auto rgb = GRB.traits().get_supported_color_modes();
EXPECT_TRUE(rgb.count(ColorMode::RGB));
EXPECT_EQ(rgb.size(), 1u);
const auto rgbw = GRBW.traits().get_supported_color_modes();
EXPECT_TRUE(rgbw.count(ColorMode::RGB_WHITE));
EXPECT_TRUE(rgbw.count(ColorMode::WHITE));
EXPECT_EQ(rgbw.size(), 2u);
}
TEST(LedStripHelpers, ViewWritesTheChannelOrder) {
ESPColorCorrection correction;
std::array<uint8_t, 8> buf{};
std::array<uint8_t, 2> effect{};
GRBW.view(buf.data(), effect.data(), 1, &correction) = Color(10, 20, 30, 40);
GRBW.view(buf.data(), effect.data(), 1, &correction).set_effect_data(7);
EXPECT_EQ(buf, (std::array<uint8_t, 8>{0, 0, 0, 0, 20, 10, 30, 40}));
EXPECT_EQ(effect[1], 7);
std::array<uint8_t, 6> rgb_buf{};
auto view = GRB.view(rgb_buf.data(), effect.data(), 1, &correction);
view = Color(10, 20, 30, 40);
EXPECT_EQ(rgb_buf, (std::array<uint8_t, 6>{0, 0, 0, 20, 10, 30}));
EXPECT_EQ(view.get_white_raw(), 0);
}
TEST(LedStripHelpers, ViewFallsBackToScratchBeforeSetup) {
ESPColorCorrection correction;
std::array<uint8_t, 4> buf{};
// Any index is safe without a buffer, as is a buffer without effect data
GRBW.view(nullptr, nullptr, 1000, &correction) = Color(1, 2, 3, 4);
auto scratch = GRBW.view(nullptr, nullptr, 1000, &correction);
scratch.set_effect_data(5);
EXPECT_EQ(scratch.get_effect_data(), 0);
GRBW.view(buf.data(), nullptr, 0, &correction) = Color(1, 2, 3, 4);
EXPECT_EQ(buf, (std::array<uint8_t, 4>{}));
// Effect data without a buffer is left alone too
std::array<uint8_t, 4> effect{};
GRBW.view(nullptr, effect.data(), 3, &correction).set_effect_data(5);
EXPECT_EQ(effect, (std::array<uint8_t, 4>{}));
}
TEST(LedStripHelpers, ThrottleShowHoldsTheFrame) {
HelperStrip strip;
LightState state(&strip);
strip.setup_state(&state);
EXPECT_FALSE(strip.throttle_show_(1000, 0, 0));
EXPECT_TRUE(strip.throttle_show_(1000, 500, 600));
EXPECT_FALSE(strip.throttle_show_(1100, 500, 600));
// micros() wraps
EXPECT_TRUE(strip.throttle_show_(100, UINT32_MAX - 99, 300));
EXPECT_FALSE(strip.throttle_show_(200, UINT32_MAX - 99, 300));
}
} // namespace esphome::light::testing