ESPHome  2024.10.2
light_state.cpp
Go to the documentation of this file.
1 #include "esphome/core/log.h"
2 #include "light_state.h"
3 #include "light_output.h"
4 #include "transformers.h"
5 
6 namespace esphome {
7 namespace light {
8 
9 static const char *const TAG = "light";
10 
11 LightState::LightState(LightOutput *output) : output_(output) {}
12 
14 LightCall LightState::turn_on() { return this->make_call().set_state(true); }
15 LightCall LightState::turn_off() { return this->make_call().set_state(false); }
18 
19 struct LightStateRTCState {
20  ColorMode color_mode{ColorMode::UNKNOWN};
21  bool state{false};
22  float brightness{1.0f};
23  float color_brightness{1.0f};
24  float red{1.0f};
25  float green{1.0f};
26  float blue{1.0f};
27  float white{1.0f};
28  float color_temp{1.0f};
29  float cold_white{1.0f};
30  float warm_white{1.0f};
31  uint32_t effect{0};
32 };
33 
35  ESP_LOGCONFIG(TAG, "Setting up light '%s'...", this->get_name().c_str());
36 
37  this->output_->setup_state(this);
38  for (auto *effect : this->effects_) {
39  effect->init_internal(this);
40  }
41 
42  // When supported color temperature range is known, initialize color temperature setting within bounds.
43  float min_mireds = this->get_traits().get_min_mireds();
44  if (min_mireds > 0) {
45  this->remote_values.set_color_temperature(min_mireds);
46  this->current_values.set_color_temperature(min_mireds);
47  }
48 
49  auto call = this->make_call();
50  LightStateRTCState recovered{};
51  switch (this->restore_mode_) {
56  this->rtc_ = global_preferences->make_preference<LightStateRTCState>(this->get_object_id_hash());
57  // Attempt to load from preferences, else fall back to default values
58  if (!this->rtc_.load(&recovered)) {
59  recovered.state = false;
62  recovered.state = true;
63  }
66  // Inverted restore state
67  recovered.state = !recovered.state;
68  }
69  break;
72  this->rtc_ = global_preferences->make_preference<LightStateRTCState>(this->get_object_id_hash());
73  this->rtc_.load(&recovered);
74  recovered.state = (this->restore_mode_ == LIGHT_RESTORE_AND_ON);
75  break;
76  case LIGHT_ALWAYS_OFF:
77  recovered.state = false;
78  break;
79  case LIGHT_ALWAYS_ON:
80  recovered.state = true;
81  break;
82  }
83 
84  call.set_color_mode_if_supported(recovered.color_mode);
85  call.set_state(recovered.state);
86  call.set_brightness_if_supported(recovered.brightness);
87  call.set_color_brightness_if_supported(recovered.color_brightness);
88  call.set_red_if_supported(recovered.red);
89  call.set_green_if_supported(recovered.green);
90  call.set_blue_if_supported(recovered.blue);
91  call.set_white_if_supported(recovered.white);
92  call.set_color_temperature_if_supported(recovered.color_temp);
93  call.set_cold_white_if_supported(recovered.cold_white);
94  call.set_warm_white_if_supported(recovered.warm_white);
95  if (recovered.effect != 0) {
96  call.set_effect(recovered.effect);
97  } else {
98  call.set_transition_length_if_supported(0);
99  }
100  call.perform();
101 }
103  ESP_LOGCONFIG(TAG, "Light '%s'", this->get_name().c_str());
104  if (this->get_traits().supports_color_capability(ColorCapability::BRIGHTNESS)) {
105  ESP_LOGCONFIG(TAG, " Default Transition Length: %.1fs", this->default_transition_length_ / 1e3f);
106  ESP_LOGCONFIG(TAG, " Gamma Correct: %.2f", this->gamma_correct_);
107  }
108  if (this->get_traits().supports_color_capability(ColorCapability::COLOR_TEMPERATURE)) {
109  ESP_LOGCONFIG(TAG, " Min Mireds: %.1f", this->get_traits().get_min_mireds());
110  ESP_LOGCONFIG(TAG, " Max Mireds: %.1f", this->get_traits().get_max_mireds());
111  }
112 }
114  // Apply effect (if any)
115  auto *effect = this->get_active_effect_();
116  if (effect != nullptr) {
117  effect->apply();
118  }
119 
120  // Apply transformer (if any)
121  if (this->transformer_ != nullptr) {
122  auto values = this->transformer_->apply();
123  this->is_transformer_active_ = true;
124  if (values.has_value()) {
125  this->current_values = *values;
126  this->output_->update_state(this);
127  this->next_write_ = true;
128  }
129 
130  if (this->transformer_->is_finished()) {
131  // if the transition has written directly to the output, current_values is outdated, so update it
132  this->current_values = this->transformer_->get_target_values();
133 
134  this->transformer_->stop();
135  this->is_transformer_active_ = false;
136  this->transformer_ = nullptr;
137  this->target_state_reached_callback_.call();
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  }
146 }
147 
149 
151 
152 LightOutput *LightState::get_output() const { return this->output_; }
154  if (this->active_effect_index_ > 0) {
155  return this->effects_[this->active_effect_index_ - 1]->get_name();
156  } else {
157  return "None";
158  }
159 }
160 
161 void LightState::add_new_remote_values_callback(std::function<void()> &&send_callback) {
162  this->remote_values_callback_.add(std::move(send_callback));
163 }
164 void LightState::add_new_target_state_reached_callback(std::function<void()> &&send_callback) {
165  this->target_state_reached_callback_.add(std::move(send_callback));
166 }
167 
168 void LightState::set_default_transition_length(uint32_t default_transition_length) {
169  this->default_transition_length_ = default_transition_length;
170 }
172 void LightState::set_flash_transition_length(uint32_t flash_transition_length) {
173  this->flash_transition_length_ = flash_transition_length;
174 }
177 void LightState::set_restore_mode(LightRestoreMode restore_mode) { this->restore_mode_ = restore_mode; }
178 bool LightState::supports_effects() { return !this->effects_.empty(); }
179 const std::vector<LightEffect *> &LightState::get_effects() const { return this->effects_; }
180 void LightState::add_effects(const std::vector<LightEffect *> &effects) {
181  this->effects_.reserve(this->effects_.size() + effects.size());
182  for (auto *effect : effects) {
183  this->effects_.push_back(effect);
184  }
185 }
186 
187 void LightState::current_values_as_binary(bool *binary) { this->current_values.as_binary(binary); }
189  this->current_values.as_brightness(brightness, this->gamma_correct_);
190 }
191 void LightState::current_values_as_rgb(float *red, float *green, float *blue, bool color_interlock) {
192  auto traits = this->get_traits();
193  this->current_values.as_rgb(red, green, blue, this->gamma_correct_, false);
194 }
195 void LightState::current_values_as_rgbw(float *red, float *green, float *blue, float *white, bool color_interlock) {
196  auto traits = this->get_traits();
197  this->current_values.as_rgbw(red, green, blue, white, this->gamma_correct_, false);
198 }
199 void LightState::current_values_as_rgbww(float *red, float *green, float *blue, float *cold_white, float *warm_white,
200  bool constant_brightness) {
201  this->current_values.as_rgbww(red, green, blue, cold_white, warm_white, this->gamma_correct_, constant_brightness);
202 }
203 void LightState::current_values_as_rgbct(float *red, float *green, float *blue, float *color_temperature,
204  float *white_brightness) {
205  auto traits = this->get_traits();
206  this->current_values.as_rgbct(traits.get_min_mireds(), traits.get_max_mireds(), red, green, blue, color_temperature,
207  white_brightness, this->gamma_correct_);
208 }
209 void LightState::current_values_as_cwww(float *cold_white, float *warm_white, bool constant_brightness) {
210  auto traits = this->get_traits();
211  this->current_values.as_cwww(cold_white, warm_white, this->gamma_correct_, constant_brightness);
212 }
213 void LightState::current_values_as_ct(float *color_temperature, float *white_brightness) {
214  auto traits = this->get_traits();
215  this->current_values.as_ct(traits.get_min_mireds(), traits.get_max_mireds(), color_temperature, white_brightness,
216  this->gamma_correct_);
217 }
218 
220 
221 void LightState::start_effect_(uint32_t effect_index) {
222  this->stop_effect_();
223  if (effect_index == 0)
224  return;
225 
226  this->active_effect_index_ = effect_index;
227  auto *effect = this->get_active_effect_();
228  effect->start_internal();
229 }
231  if (this->active_effect_index_ == 0) {
232  return nullptr;
233  } else {
234  return this->effects_[this->active_effect_index_ - 1];
235  }
236 }
238  auto *effect = this->get_active_effect_();
239  if (effect != nullptr) {
240  effect->stop();
241  }
242  this->active_effect_index_ = 0;
243 }
244 
245 void LightState::start_transition_(const LightColorValues &target, uint32_t length, bool set_remote_values) {
247  this->transformer_->setup(this->current_values, target, length);
248 
249  if (set_remote_values) {
250  this->remote_values = target;
251  }
252 }
253 
254 void LightState::start_flash_(const LightColorValues &target, uint32_t length, bool set_remote_values) {
255  LightColorValues end_colors = this->remote_values;
256  // If starting a flash if one is already happening, set end values to end values of current flash
257  // Hacky but works
258  if (this->transformer_ != nullptr)
259  end_colors = this->transformer_->get_start_values();
260 
261  this->transformer_ = make_unique<LightFlashTransformer>(*this);
262  this->transformer_->setup(end_colors, target, length);
263 
264  if (set_remote_values) {
265  this->remote_values = target;
266  };
267 }
268 
269 void LightState::set_immediately_(const LightColorValues &target, bool set_remote_values) {
270  this->is_transformer_active_ = false;
271  this->transformer_ = nullptr;
272  this->current_values = target;
273  if (set_remote_values) {
274  this->remote_values = target;
275  }
276  this->output_->update_state(this);
277  this->next_write_ = true;
278 }
279 
281  LightStateRTCState saved;
282  saved.color_mode = this->remote_values.get_color_mode();
283  switch (this->restore_mode_) {
286  saved.state = (this->restore_mode_ == LIGHT_RESTORE_AND_ON);
287  break;
288  default:
289  saved.state = this->remote_values.is_on();
290  break;
291  }
292  saved.brightness = this->remote_values.get_brightness();
293  saved.color_brightness = this->remote_values.get_color_brightness();
294  saved.red = this->remote_values.get_red();
295  saved.green = this->remote_values.get_green();
296  saved.blue = this->remote_values.get_blue();
297  saved.white = this->remote_values.get_white();
298  saved.color_temp = this->remote_values.get_color_temperature();
299  saved.cold_white = this->remote_values.get_cold_white();
300  saved.warm_white = this->remote_values.get_warm_white();
301  saved.effect = this->active_effect_index_;
302  this->rtc_.save(&saved);
303 }
304 
305 } // namespace light
306 } // namespace esphome
void current_values_as_rgbww(float *red, float *green, float *blue, float *cold_white, float *warm_white, bool constant_brightness=false)
float gamma_correct_
Gamma correction factor for the light.
Definition: light_state.h:212
ColorMode
Color modes are a combination of color capabilities that can be used at the same time.
Definition: color_mode.h:49
ESPPreferenceObject rtc_
Object used to store the persisted values of the light.
Definition: light_state.h:191
void set_flash_transition_length(uint32_t flash_transition_length)
Set the flash transition length.
float get_warm_white() const
Get the warm white property of these light color values. In range 0.0 to 1.0.
uint32_t get_flash_transition_length() const
void publish_state()
Publish the currently active state to the frontend.
std::unique_ptr< LightTransformer > transformer_
The currently active transformer for this light (transition/flash).
Definition: light_state.h:186
void set_immediately_(const LightColorValues &target, bool set_remote_values)
Internal method to set the color values to target immediately (with no transition).
bool is_on() const
Get the binary true/false state of these light color values.
uint32_t default_transition_length_
Default transition length for all transitions in ms.
Definition: light_state.h:208
Interface to write LightStates to hardware.
Definition: light_output.h:12
LightColorValues current_values
The current values of the light as outputted to the light.
Definition: light_state.h:65
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...
std::string get_effect_name()
Return the name of the current effect, or if no effect is active "None".
LightCall turn_on()
Make a light state call.
Definition: light_state.cpp:14
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.
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.
float get_cold_white() const
Get the cold white property of these light color values. In range 0.0 to 1.0.
void start_effect_(uint32_t effect_index)
Internal method to start an effect with the given index.
LightRestoreMode restore_mode_
Restore mode of the light.
Definition: light_state.h:214
LightOutput * get_output() const
Get the light output associated with this object.
float get_min_mireds() const
Definition: light_traits.h:49
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.
bool supports_effects()
Return whether the light has any effects that meet the trait requirements.
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.
float get_red() const
Get the red property of these light color values. In range 0.0 to 1.0.
virtual std::unique_ptr< LightTransformer > create_default_transition()
Return the default transformer used for transitions.
Definition: light_output.cpp:7
void add_new_target_state_reached_callback(std::function< void()> &&send_callback)
The callback is called once the state of current_values and remote_values are equal (when the transit...
Color temperature can be controlled.
void setup() override
Load state from preferences.
Definition: light_state.cpp:34
void dump_config() override
void current_values_as_binary(bool *binary)
The result of all the current_values_as_* methods have gamma correction applied.
bool save(const T *src)
Definition: preferences.h:21
bool next_write_
Whether the light value should be written in the next cycle.
Definition: light_state.h:188
void current_values_as_rgb(float *red, float *green, float *blue, bool color_interlock=false)
float get_blue() const
Get the blue property of these light color values. In range 0.0 to 1.0.
uint32_t flash_transition_length_
Transition length to use for flash transitions.
Definition: light_state.h:210
LightState(LightOutput *output)
Definition: light_state.cpp:11
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...
LightOutput * output_
Store the output to allow effects to have more access.
Definition: light_state.h:182
float gamma_correct(float value, float gamma)
Applies gamma correction of gamma to value.
Definition: helpers.cpp:537
This class represents the color state for a light object.
void current_values_as_rgbct(float *red, float *green, float *blue, float *color_temperature, float *white_brightness)
LightEffect * get_active_effect_()
Internal method to get the currently active effect.
float get_white() const
Get the white property of these light color values. In range 0.0 to 1.0.
ESPPreferences * global_preferences
float get_color_temperature() const
Get the color temperature property of these light color values in mired.
void add_effects(const std::vector< LightEffect *> &effects)
Add effects for this light state.
void current_values_as_brightness(float *brightness)
uint32_t get_default_transition_length() const
void set_gamma_correct(float gamma_correct)
Set the gamma correction factor.
const std::vector< LightEffect * > & get_effects() const
Get all effects for this light state.
This class represents a requested change in a light state.
Definition: light_call.h:14
ColorMode get_color_mode() const
Get the color mode of these light color values.
LightCall & set_state(optional< bool > state)
Set the binary ON/OFF state of the light.
Definition: light_call.cpp:552
std::vector< LightEffect * > effects_
List of effects for this light.
Definition: light_state.h:216
Master brightness of the light can be controlled.
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)
float get_setup_priority() const override
Shortly after HARDWARE.
CallbackManager< void()> remote_values_callback_
Callback to call when new values for the frontend are available.
Definition: light_state.h:200
No color mode configured (cannot be a supported mode, only active when light is off).
virtual void update_state(LightState *state)
Called on every update of the current values of the associated LightState, can optionally be used to ...
Definition: light_output.h:24
This class is used to represent the capabilities of a light.
Definition: light_traits.h:11
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.
void add_new_remote_values_callback(std::function< void()> &&send_callback)
This lets front-end components subscribe to light change events.
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.
const float HARDWARE
For components that deal with hardware and are very important like GPIO switch.
Definition: component.cpp:18
void set_restore_mode(LightRestoreMode restore_mode)
Set the restore mode of this light.
virtual ESPPreferenceObject make_preference(size_t length, uint32_t type, bool in_flash)=0
void set_color_temperature(float color_temperature)
Set the color temperature property of these light color values in mired.
void save_remote_values_()
Internal method to save the current remote_values to the preferences.
void as_binary(bool *binary) const
Convert these light color values to a binary representation and write them to binary.
virtual void setup_state(LightState *state)
Definition: light_output.h:20
uint16_t length
Definition: tt21100.cpp:12
CallbackManager< void()> target_state_reached_callback_
Callback to call when the state of current_values and remote_values are equal This should be called o...
Definition: light_state.h:205
Implementation of SPI Controller mode.
Definition: a01nyub.cpp:7
uint32_t active_effect_index_
Value for storing the index of the currently active effect. 0 if no effect is active.
Definition: light_state.h:184
LightColorValues remote_values
The remote color values reported to the frontend.
Definition: light_state.h:77
virtual LightTraits get_traits()=0
Return the LightTraits of this LightOutput.
float get_color_brightness() const
Get the color brightness property of these light color values. In range 0.0 to 1.0.
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...
bool is_transformer_active()
Indicator if a transformer (e.g.
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, blue.
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 stop_effect_()
Internal method to stop the current effect (if one is active).
uint32_t get_object_id_hash()
Definition: entity_base.cpp:76
float get_green() const
Get the green property of these light color values. In range 0.0 to 1.0.
const StringRef & get_name() const
Definition: entity_base.cpp:10
float get_brightness() const
Get the brightness property of these light color values. In range 0.0 to 1.0.
void current_values_as_rgbw(float *red, float *green, float *blue, float *white, bool color_interlock=false)
bool state
Definition: fan.h:34