From 737f23a0bdb8a1c09bb0dee9709d09cb4c5403c4 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 24 Nov 2025 10:23:11 -0600 Subject: [PATCH] [light] Dynamically disable loop when idle to reduce CPU overhead (#11881) --- esphome/components/light/light_state.cpp | 24 ++++++++++++++++++++++++ esphome/components/light/light_state.h | 3 +++ 2 files changed, 27 insertions(+) diff --git a/esphome/components/light/light_state.cpp b/esphome/components/light/light_state.cpp index 36b2af03a..9cde9077d 100644 --- a/esphome/components/light/light_state.cpp +++ b/esphome/components/light/light_state.cpp @@ -23,6 +23,9 @@ void LightState::setup() { effect->init_internal(this); } + // Start with loop disabled if idle - respects any effects/transitions set up during initialization + this->disable_loop_if_idle_(); + // When supported color temperature range is known, initialize color temperature setting within bounds. auto traits = this->get_traits(); float min_mireds = traits.get_min_mireds(); @@ -125,6 +128,9 @@ void LightState::loop() { this->is_transformer_active_ = false; this->transformer_ = nullptr; this->target_state_reached_callback_.call(); + + // Disable loop if idle (no transformer and no effect) + this->disable_loop_if_idle_(); } } @@ -132,6 +138,8 @@ void LightState::loop() { if (this->next_write_) { this->next_write_ = false; this->output_->write_state(this); + // Disable loop if idle (no transformer and no effect) + this->disable_loop_if_idle_(); } } @@ -227,6 +235,8 @@ void LightState::start_effect_(uint32_t effect_index) { this->active_effect_index_ = effect_index; auto *effect = this->get_active_effect_(); effect->start_internal(); + // Enable loop while effect is active + this->enable_loop(); } LightEffect *LightState::get_active_effect_() { if (this->active_effect_index_ == 0) { @@ -241,6 +251,8 @@ void LightState::stop_effect_() { effect->stop(); } this->active_effect_index_ = 0; + // Disable loop if idle (no effect and no transformer) + this->disable_loop_if_idle_(); } void LightState::start_transition_(const LightColorValues &target, uint32_t length, bool set_remote_values) { @@ -250,6 +262,8 @@ void LightState::start_transition_(const LightColorValues &target, uint32_t leng if (set_remote_values) { this->remote_values = target; } + // Enable loop while transition is active + this->enable_loop(); } void LightState::start_flash_(const LightColorValues &target, uint32_t length, bool set_remote_values) { @@ -265,6 +279,8 @@ void LightState::start_flash_(const LightColorValues &target, uint32_t length, b if (set_remote_values) { this->remote_values = target; }; + // Enable loop while flash is active + this->enable_loop(); } void LightState::set_immediately_(const LightColorValues &target, bool set_remote_values) { @@ -276,6 +292,14 @@ void LightState::set_immediately_(const LightColorValues &target, bool set_remot } this->output_->update_state(this); this->next_write_ = true; + this->enable_loop(); +} + +void LightState::disable_loop_if_idle_() { + // Only disable loop if both transformer and effect are inactive, and no pending writes + if (this->transformer_ == nullptr && this->get_active_effect_() == nullptr && !this->next_write_) { + this->disable_loop(); + } } void LightState::save_remote_values_() { diff --git a/esphome/components/light/light_state.h b/esphome/components/light/light_state.h index 06519cdc1..ad8922b46 100644 --- a/esphome/components/light/light_state.h +++ b/esphome/components/light/light_state.h @@ -255,6 +255,9 @@ class LightState : public EntityBase, public Component { /// Internal method to save the current remote_values to the preferences void save_remote_values_(); + /// Disable loop if neither transformer nor effect is active + void disable_loop_if_idle_(); + /// Store the output to allow effects to have more access. LightOutput *output_; /// The currently active transformer for this light (transition/flash).