ESPHome 2026.1.0-dev
Loading...
Searching...
No Matches
light_state.cpp
Go to the documentation of this file.
1#include "light_state.h"
4#include "esphome/core/log.h"
5#include "light_output.h"
6#include "transformers.h"
7
8namespace esphome::light {
9
10static const char *const TAG = "light";
11
12LightState::LightState(LightOutput *output) : output_(output) {}
13
19
21 this->output_->setup_state(this);
22 for (auto *effect : this->effects_) {
23 effect->init_internal(this);
24 }
25
26 // Start with loop disabled if idle - respects any effects/transitions set up during initialization
28
29 // When supported color temperature range is known, initialize color temperature setting within bounds.
30 auto traits = this->get_traits();
31 float min_mireds = traits.get_min_mireds();
32 if (min_mireds > 0) {
33 this->remote_values.set_color_temperature(min_mireds);
34 this->current_values.set_color_temperature(min_mireds);
35 }
36
37 auto call = this->make_call();
38 LightStateRTCState recovered{};
39 if (this->initial_state_.has_value()) {
40 recovered = *this->initial_state_;
41 }
42 switch (this->restore_mode_) {
48 // Attempt to load from preferences, else fall back to default values
49 if (!this->rtc_.load(&recovered)) {
50 recovered.state = (this->restore_mode_ == LIGHT_RESTORE_DEFAULT_ON ||
54 // Inverted restore state
55 recovered.state = !recovered.state;
56 }
57 break;
61 this->rtc_.load(&recovered);
62 recovered.state = (this->restore_mode_ == LIGHT_RESTORE_AND_ON);
63 break;
65 recovered.state = false;
66 break;
67 case LIGHT_ALWAYS_ON:
68 recovered.state = true;
69 break;
70 }
71
72 call.set_color_mode_if_supported(recovered.color_mode);
73 call.set_state(recovered.state);
74 call.set_brightness_if_supported(recovered.brightness);
75 call.set_color_brightness_if_supported(recovered.color_brightness);
76 call.set_red_if_supported(recovered.red);
77 call.set_green_if_supported(recovered.green);
78 call.set_blue_if_supported(recovered.blue);
79 call.set_white_if_supported(recovered.white);
80 call.set_color_temperature_if_supported(recovered.color_temp);
81 call.set_cold_white_if_supported(recovered.cold_white);
82 call.set_warm_white_if_supported(recovered.warm_white);
83 if (recovered.effect != 0) {
84 call.set_effect(recovered.effect);
85 } else {
86 call.set_transition_length_if_supported(0);
87 }
88 call.perform();
89}
91 ESP_LOGCONFIG(TAG, "Light '%s'", this->get_name().c_str());
92 auto traits = this->get_traits();
93 if (traits.supports_color_capability(ColorCapability::BRIGHTNESS)) {
94 ESP_LOGCONFIG(TAG,
95 " Default Transition Length: %.1fs\n"
96 " Gamma Correct: %.2f",
97 this->default_transition_length_ / 1e3f, this->gamma_correct_);
98 }
99 if (traits.supports_color_capability(ColorCapability::COLOR_TEMPERATURE)) {
100 ESP_LOGCONFIG(TAG,
101 " Min Mireds: %.1f\n"
102 " Max Mireds: %.1f",
103 traits.get_min_mireds(), traits.get_max_mireds());
104 }
105}
107 // Apply effect (if any)
108 auto *effect = this->get_active_effect_();
109 if (effect != nullptr) {
110 effect->apply();
111 }
112
113 // Apply transformer (if any)
114 if (this->transformer_ != nullptr) {
115 auto values = this->transformer_->apply();
116 this->is_transformer_active_ = true;
117 if (values.has_value()) {
118 this->current_values = *values;
119 this->output_->update_state(this);
120 this->next_write_ = true;
121 }
122
123 if (this->transformer_->is_finished()) {
124 // if the transition has written directly to the output, current_values is outdated, so update it
125 this->current_values = this->transformer_->get_target_values();
126
127 this->transformer_->stop();
128 this->is_transformer_active_ = false;
129 this->transformer_ = nullptr;
131 for (auto *listener : *this->target_state_reached_listeners_) {
132 listener->on_light_target_state_reached();
133 }
134 }
135
136 // Disable loop if idle (no transformer and no effect)
137 this->disable_loop_if_idle_();
138 }
139 }
140
141 // Write state to the light
142 if (this->next_write_) {
143 this->next_write_ = false;
144 this->output_->write_state(this);
145 // Disable loop if idle (no transformer and no effect)
146 this->disable_loop_if_idle_();
147 }
148}
149
151
153 if (this->remote_values_listeners_) {
154 for (auto *listener : *this->remote_values_listeners_) {
155 listener->on_light_remote_values_update();
156 }
157 }
158#if defined(USE_LIGHT) && defined(USE_CONTROLLER_REGISTRY)
160#endif
161}
162
164
165static constexpr const char *EFFECT_NONE = "None";
166static constexpr auto EFFECT_NONE_REF = StringRef::from_lit("None");
167
169 if (this->active_effect_index_ > 0) {
170 return this->effects_[this->active_effect_index_ - 1]->get_name();
171 }
172 return EFFECT_NONE;
173}
174
176 if (this->active_effect_index_ > 0) {
177 return StringRef(this->effects_[this->active_effect_index_ - 1]->get_name());
178 }
179 return EFFECT_NONE_REF;
180}
181
183 if (!this->remote_values_listeners_) {
184 this->remote_values_listeners_ = make_unique<std::vector<LightRemoteValuesListener *>>();
185 }
186 this->remote_values_listeners_->push_back(listener);
187}
190 this->target_state_reached_listeners_ = make_unique<std::vector<LightTargetStateReachedListener *>>();
191 }
192 this->target_state_reached_listeners_->push_back(listener);
193}
194
196 this->default_transition_length_ = default_transition_length;
197}
200 this->flash_transition_length_ = flash_transition_length;
201}
204void LightState::set_restore_mode(LightRestoreMode restore_mode) { this->restore_mode_ = restore_mode; }
205void LightState::set_initial_state(const LightStateRTCState &initial_state) { this->initial_state_ = initial_state; }
206bool LightState::supports_effects() { return !this->effects_.empty(); }
208void LightState::add_effects(const std::initializer_list<LightEffect *> &effects) {
209 // Called once from Python codegen during setup with all effects from YAML config
210 this->effects_ = effects;
211}
212
215 this->current_values.as_brightness(brightness, this->gamma_correct_);
216}
217void LightState::current_values_as_rgb(float *red, float *green, float *blue, bool color_interlock) {
218 this->current_values.as_rgb(red, green, blue, this->gamma_correct_, false);
219}
220void LightState::current_values_as_rgbw(float *red, float *green, float *blue, float *white, bool color_interlock) {
221 this->current_values.as_rgbw(red, green, blue, white, this->gamma_correct_, false);
222}
223void LightState::current_values_as_rgbww(float *red, float *green, float *blue, float *cold_white, float *warm_white,
224 bool constant_brightness) {
225 this->current_values.as_rgbww(red, green, blue, cold_white, warm_white, this->gamma_correct_, constant_brightness);
226}
227void LightState::current_values_as_rgbct(float *red, float *green, float *blue, float *color_temperature,
228 float *white_brightness) {
229 auto traits = this->get_traits();
230 this->current_values.as_rgbct(traits.get_min_mireds(), traits.get_max_mireds(), red, green, blue, color_temperature,
231 white_brightness, this->gamma_correct_);
232}
233void LightState::current_values_as_cwww(float *cold_white, float *warm_white, bool constant_brightness) {
234 this->current_values.as_cwww(cold_white, warm_white, this->gamma_correct_, constant_brightness);
235}
236void LightState::current_values_as_ct(float *color_temperature, float *white_brightness) {
237 auto traits = this->get_traits();
238 this->current_values.as_ct(traits.get_min_mireds(), traits.get_max_mireds(), color_temperature, white_brightness,
239 this->gamma_correct_);
240}
241
243
245 this->stop_effect_();
246 if (effect_index == 0)
247 return;
248
249 this->active_effect_index_ = effect_index;
250 auto *effect = this->get_active_effect_();
251 effect->start_internal();
252 // Enable loop while effect is active
253 this->enable_loop();
254}
256 if (this->active_effect_index_ == 0) {
257 return nullptr;
258 } else {
259 return this->effects_[this->active_effect_index_ - 1];
260 }
261}
263 auto *effect = this->get_active_effect_();
264 if (effect != nullptr) {
265 effect->stop();
266 }
267 this->active_effect_index_ = 0;
268 // Disable loop if idle (no effect and no transformer)
269 this->disable_loop_if_idle_();
270}
271
272void LightState::start_transition_(const LightColorValues &target, uint32_t length, bool set_remote_values) {
274 this->transformer_->setup(this->current_values, target, length);
275
276 if (set_remote_values) {
277 this->remote_values = target;
278 }
279 // Enable loop while transition is active
280 this->enable_loop();
281}
282
283void LightState::start_flash_(const LightColorValues &target, uint32_t length, bool set_remote_values) {
284 LightColorValues end_colors = this->remote_values;
285 // If starting a flash if one is already happening, set end values to end values of current flash
286 // Hacky but works
287 if (this->transformer_ != nullptr)
288 end_colors = this->transformer_->get_start_values();
289
290 this->transformer_ = make_unique<LightFlashTransformer>(*this);
291 this->transformer_->setup(end_colors, target, length);
292
293 if (set_remote_values) {
294 this->remote_values = target;
295 };
296 // Enable loop while flash is active
297 this->enable_loop();
298}
299
300void LightState::set_immediately_(const LightColorValues &target, bool set_remote_values) {
301 this->is_transformer_active_ = false;
302 this->transformer_ = nullptr;
303 this->current_values = target;
304 if (set_remote_values) {
305 this->remote_values = target;
306 }
307 this->output_->update_state(this);
308 this->schedule_write_();
309}
310
312 // Only disable loop if both transformer and effect are inactive, and no pending writes
313 if (this->transformer_ == nullptr && this->get_active_effect_() == nullptr && !this->next_write_) {
314 this->disable_loop();
315 }
316}
317
319 LightStateRTCState saved;
321 switch (this->restore_mode_) {
324 saved.state = (this->restore_mode_ == LIGHT_RESTORE_AND_ON);
325 break;
326 default:
327 saved.state = this->remote_values.is_on();
328 break;
329 }
332 saved.red = this->remote_values.get_red();
333 saved.green = this->remote_values.get_green();
334 saved.blue = this->remote_values.get_blue();
335 saved.white = this->remote_values.get_white();
339 saved.effect = this->active_effect_index_;
340 this->rtc_.save(&saved);
341}
342
343} // namespace esphome::light
void enable_loop()
Enable this component's loop.
void disable_loop()
Disable this component's loop.
static void notify_light_update(light::LightState *obj)
bool save(const T *src)
Definition preferences.h:21
virtual ESPPreferenceObject make_preference(size_t length, uint32_t type, bool in_flash)=0
const StringRef & get_name() const
uint32_t get_preference_hash()
Get a unique hash for storing preferences/settings for this entity.
Fixed-capacity vector - allocates once at runtime, never reallocates This avoids std::vector template...
Definition helpers.h:184
StringRef is a reference to a string owned by something else.
Definition string_ref.h:22
static constexpr StringRef from_lit(const CharT(&s)[N])
Definition string_ref.h:46
This class represents a requested change in a light state.
Definition light_call.h:21
LightCall & set_state(optional< bool > state)
Set the binary ON/OFF state of the light.
This class represents the color state for a light object.
float get_brightness() const
Get the brightness property of these light color values. In range 0.0 to 1.0.
void as_ct(float color_temperature_cw, float color_temperature_ww, float *color_temperature, float *white_brightness, float gamma=0) const
Convert these light color values to a CT+BR representation with the given parameters.
float get_blue() const
Get the blue property of these light color values. In range 0.0 to 1.0.
float get_white() const
Get the white property of these light color values. In range 0.0 to 1.0.
float get_color_temperature() const
Get the color temperature property of these light color values in mired.
void as_rgb(float *red, float *green, float *blue, float gamma=0, bool color_interlock=false) const
Convert these light color values to an RGB representation and write them to red, green,...
float get_cold_white() const
Get the cold white property of these light color values. In range 0.0 to 1.0.
void as_cwww(float *cold_white, float *warm_white, float gamma=0, bool constant_brightness=false) const
Convert these light color values to an CWWW representation with the given parameters.
bool is_on() const
Get the binary true/false state of these light color values.
float get_green() const
Get the green property of these light color values. In range 0.0 to 1.0.
void set_color_temperature(float color_temperature)
Set the color temperature property of these light color values in mired.
float get_warm_white() const
Get the warm white property of these light color values. In range 0.0 to 1.0.
void as_binary(bool *binary) const
Convert these light color values to a binary representation and write them to binary.
void as_rgbw(float *red, float *green, float *blue, float *white, float gamma=0, bool color_interlock=false) const
Convert these light color values to an RGBW representation and write them to red, green,...
ColorMode get_color_mode() const
Get the color mode of these light color values.
float get_red() const
Get the red property of these light color values. In range 0.0 to 1.0.
float get_color_brightness() const
Get the color brightness property of these light color values. In range 0.0 to 1.0.
void as_brightness(float *brightness, float gamma=0) const
Convert these light color values to a brightness-only representation and write them to brightness.
void as_rgbct(float color_temperature_cw, float color_temperature_ww, float *red, float *green, float *blue, float *color_temperature, float *white_brightness, float gamma=0) const
Convert these light color values to an RGB+CT+BR representation with the given parameters.
void as_rgbww(float *red, float *green, float *blue, float *cold_white, float *warm_white, float gamma=0, bool constant_brightness=false) const
Convert these light color values to an RGBWW representation with the given parameters.
Interface to write LightStates to hardware.
virtual void write_state(LightState *state)=0
Called from loop() every time the light state has changed, and should should write the new state to h...
virtual std::unique_ptr< LightTransformer > create_default_transition()
Return the default transformer used for transitions.
virtual LightTraits get_traits()=0
Return the LightTraits of this LightOutput.
virtual void update_state(LightState *state)
Called on every update of the current values of the associated LightState, can optionally be used to ...
virtual void setup_state(LightState *state)
Listener interface for light remote value changes.
Definition light_state.h:29
void add_remote_values_listener(LightRemoteValuesListener *listener)
Add a listener for remote values changes.
float gamma_correct_
Gamma correction factor for the light.
FixedVector< LightEffect * > effects_
List of effects for this light.
void start_effect_(uint32_t effect_index)
Internal method to start an effect with the given index.
void stop_effect_()
Internal method to stop the current effect (if one is active).
void current_values_as_rgbct(float *red, float *green, float *blue, float *color_temperature, float *white_brightness)
void disable_loop_if_idle_()
Disable loop if neither transformer nor effect is active.
std::unique_ptr< LightTransformer > transformer_
The currently active transformer for this light (transition/flash).
void dump_config() override
LightColorValues remote_values
The remote color values reported to the frontend.
LightOutput * get_output() const
Get the light output associated with this object.
void set_flash_transition_length(uint32_t flash_transition_length)
Set the flash transition length.
LightState(LightOutput *output)
void current_values_as_binary(bool *binary)
The result of all the current_values_as_* methods have gamma correction applied.
void save_remote_values_()
Internal method to save the current remote_values to the preferences.
void add_target_state_reached_listener(LightTargetStateReachedListener *listener)
Add a listener for target state reached.
LightCall turn_on()
Make a light state call.
uint32_t get_default_transition_length() const
void set_immediately_(const LightColorValues &target, bool set_remote_values)
Internal method to set the color values to target immediately (with no transition).
optional< LightStateRTCState > initial_state_
Initial state of the light.
uint32_t get_flash_transition_length() const
std::string get_effect_name()
Return the name of the current effect, or if no effect is active "None".
void current_values_as_ct(float *color_temperature, float *white_brightness)
void current_values_as_cwww(float *cold_white, float *warm_white, bool constant_brightness=false)
uint32_t flash_transition_length_
Transition length to use for flash transitions.
void publish_state()
Publish the currently active state to the frontend.
void current_values_as_rgbww(float *red, float *green, float *blue, float *cold_white, float *warm_white, bool constant_brightness=false)
void set_initial_state(const LightStateRTCState &initial_state)
Set the initial state of this light.
void current_values_as_brightness(float *brightness)
StringRef get_effect_name_ref()
Return the name of the current effect as StringRef (for API usage)
void setup() override
Load state from preferences.
uint32_t active_effect_index_
Value for storing the index of the currently active effect. 0 if no effect is active.
void start_flash_(const LightColorValues &target, uint32_t length, bool set_remote_values)
Internal method to start a flash for the specified amount of time.
LightRestoreMode restore_mode_
Restore mode of the light.
const FixedVector< LightEffect * > & get_effects() const
Get all effects for this light state.
void add_effects(const std::initializer_list< LightEffect * > &effects)
Add effects for this light state.
void current_values_as_rgb(float *red, float *green, float *blue, bool color_interlock=false)
void schedule_write_()
Schedule a write to the light output and enable the loop to process it.
bool supports_effects()
Return whether the light has any effects that meet the trait requirements.
void current_values_as_rgbw(float *red, float *green, float *blue, float *white, bool color_interlock=false)
bool next_write_
Whether the light value should be written in the next cycle.
void start_transition_(const LightColorValues &target, uint32_t length, bool set_remote_values)
Internal method to start a transition to the target color with the given length.
float get_setup_priority() const override
Shortly after HARDWARE.
void set_restore_mode(LightRestoreMode restore_mode)
Set the restore mode of this light.
LightOutput * output_
Store the output to allow effects to have more access.
ESPPreferenceObject rtc_
Object used to store the persisted values of the light.
uint32_t default_transition_length_
Default transition length for all transitions in ms.
LightColorValues current_values
The current values of the light as outputted to the light.
LightEffect * get_active_effect_()
Internal method to get the currently active effect.
bool is_transformer_active()
Indicator if a transformer (e.g.
std::unique_ptr< std::vector< LightRemoteValuesListener * > > remote_values_listeners_
Listeners for remote values changes.
std::unique_ptr< std::vector< LightTargetStateReachedListener * > > target_state_reached_listeners_
Listeners for target state reached.
void set_gamma_correct(float gamma_correct)
Set the gamma correction factor.
void set_default_transition_length(uint32_t default_transition_length)
Set the default transition length, i.e. the transition length when no transition is provided.
Listener interface for light target state reached.
Definition light_state.h:40
This class is used to represent the capabilities of a light.
@ LIGHT_RESTORE_INVERTED_DEFAULT_ON
Definition light_state.h:51
@ LIGHT_RESTORE_DEFAULT_OFF
Definition light_state.h:46
@ LIGHT_RESTORE_INVERTED_DEFAULT_OFF
Definition light_state.h:50
@ BRIGHTNESS
Master brightness of the light can be controlled.
@ COLOR_TEMPERATURE
Color temperature can be controlled.
const float HARDWARE
For components that deal with hardware and are very important like GPIO switch.
Definition component.cpp:80
float gamma_correct(float value, float gamma)
Applies gamma correction of gamma to value.
Definition helpers.cpp:595
ESPPreferences * global_preferences
uint16_t length
Definition tt21100.cpp:0