ESPHome 2026.5.1
Loading...
Searching...
No Matches
light_color_values.h
Go to the documentation of this file.
1#pragma once
2
4#include "color_mode.h"
5#include <cmath>
6#include <cstdint>
7#include <limits>
8
9namespace esphome::light {
10
11inline static uint8_t to_uint8_scale(float x) { return static_cast<uint8_t>(roundf(x * 255.0f)); }
12
13// IEEE 754 bit patterns. Values in [0.0f, 1.0f] have bits <= ONE_F_BITS;
14// negatives have the sign bit set (→ huge unsigned). A single unsigned compare
15// replaces two soft-float __ltsf2/__gtsf2 calls on ESP8266.
16static constexpr uint32_t ONE_F_BITS = 0x3F800000u; // 1.0f
17static constexpr uint32_t NEG_ZERO_F_BITS = 0x80000000u; // -0.0f / sign-bit mask
18static_assert(sizeof(float) == sizeof(uint32_t), "float must be 32-bit");
19static_assert(std::numeric_limits<float>::is_iec559, "IEEE 754 float required");
20
21// Union pun — memcpy/bit_cast don't fold on xtensa-gcc (see api/proto.h).
22// -0.0f is numerically zero so it's reported in range (no warning, no clamp).
23inline bool float_out_of_unit_range(float x) {
24 union {
25 float f;
26 uint32_t u;
27 } pun;
28 pun.f = x;
29 return pun.u > ONE_F_BITS && pun.u != NEG_ZERO_F_BITS;
30}
31
32// Clamps to [0.0f, 1.0f] without float compares. Out of range: sign bit set
33// (negatives, -NaN, -Inf) → 0.0f; sign bit clear (>1, +NaN, +Inf) → 1.0f.
34inline float clamp_unit_float(float x) {
35 union {
36 float f;
37 uint32_t u;
38 } pun;
39 pun.f = x;
40 if (pun.u <= ONE_F_BITS)
41 return x;
42 return (pun.u & NEG_ZERO_F_BITS) ? 0.0f : 1.0f; // sign bit → negative → clamp to 0
43}
44
45// Shared anonymous union: eight unit-range floats alias unit_fields_[8] so
46// LightCall::validate_() can iterate them as a real array. GCC/Clang ext.
47#define ESPHOME_LIGHT_UNIT_FIELDS_UNION() \
48 union { \
49 struct { \
50 float brightness_; \
51 float color_brightness_; \
52 float red_; \
53 float green_; \
54 float blue_; \
55 float white_; \
56 float cold_white_; \
57 float warm_white_; \
58 }; \
59 float unit_fields_[8]; \
60 }
61
96 public:
99 : state_(0.0f),
100 brightness_(1.0f),
101 color_brightness_(1.0f),
102 red_(1.0f),
103 green_(1.0f),
104 blue_(1.0f),
105 white_(1.0f),
106 cold_white_{1.0f},
107 warm_white_{1.0f},
108 color_temperature_{0.0f},
110
111 LightColorValues(ColorMode color_mode, float state, float brightness, float color_brightness, float red, float green,
112 float blue, float white, float color_temperature, float cold_white, float warm_white) {
113 this->set_color_mode(color_mode);
114 this->set_state(state);
115 this->set_brightness(brightness);
116 this->set_color_brightness(color_brightness);
117 this->set_red(red);
118 this->set_green(green);
119 this->set_blue(blue);
120 this->set_white(white);
121 this->set_color_temperature(color_temperature);
122 this->set_cold_white(cold_white);
123 this->set_warm_white(warm_white);
124 }
125
136 static LightColorValues lerp(const LightColorValues &start, const LightColorValues &end, float completion);
137
148 if (this->color_mode_ & ColorCapability::RGB) {
149 float max_value = fmaxf(this->red_, fmaxf(this->green_, this->blue_));
150 // Assign directly to avoid redundant clamp in set_red/green/blue.
151 // Values are guaranteed in [0,1]: inputs are already clamped to [0,1],
152 // and dividing by max_value (the largest) keeps results in [0,1].
153 if (max_value == 0.0f) {
154 this->red_ = 1.0f;
155 this->green_ = 1.0f;
156 this->blue_ = 1.0f;
157 } else {
158 float inv = 1.0f / max_value;
159 this->red_ *= inv;
160 this->green_ *= inv;
161 this->blue_ *= inv;
162 }
163 }
164 }
165
167 void as_binary(bool *binary) const { *binary = this->state_ == 1.0f; }
168
170 void as_brightness(float *brightness) const { *brightness = this->state_ * this->brightness_; }
171
173 void as_rgb(float *red, float *green, float *blue) const {
174 if (this->color_mode_ & ColorCapability::RGB) {
175 float brightness = this->state_ * this->brightness_ * this->color_brightness_;
176 *red = brightness * this->red_;
177 *green = brightness * this->green_;
178 *blue = brightness * this->blue_;
179 } else {
180 *red = *green = *blue = 0;
181 }
182 }
183
185 void as_rgbw(float *red, float *green, float *blue, float *white) const {
186 this->as_rgb(red, green, blue);
188 *white = this->state_ * this->brightness_ * this->white_;
189 } else {
190 *white = 0;
191 }
192 }
193
195 void as_rgbww(float *red, float *green, float *blue, float *cold_white, float *warm_white,
196 bool constant_brightness = false) const {
197 this->as_rgb(red, green, blue);
198 this->as_cwww(cold_white, warm_white, constant_brightness);
199 }
200
202 void as_rgbct(float color_temperature_cw, float color_temperature_ww, float *red, float *green, float *blue,
203 float *color_temperature, float *white_brightness) const {
204 this->as_rgb(red, green, blue);
205 this->as_ct(color_temperature_cw, color_temperature_ww, color_temperature, white_brightness);
206 }
207
219 void as_cwww(float *cold_white, float *warm_white, bool constant_brightness = false) const {
221 const float cw_level = this->cold_white_;
222 const float ww_level = this->warm_white_;
223 const float white_level = this->state_ * this->brightness_;
224 if (!constant_brightness) {
225 *cold_white = white_level * cw_level;
226 *warm_white = white_level * ww_level;
227 } else {
228 // Just multiplying by cw_level / (cw_level + ww_level) would divide out the brightness information from the
229 // cold_white and warm_white settings (i.e. cw=0.8, ww=0.4 would be identical to cw=0.4, ww=0.2), which breaks
230 // transitions. Use the highest value as the brightness for the white channels (the alternative, using cw+ww/2,
231 // reduces to cw/2 and ww/2, which would still limit brightness to 100% of a single channel, but isn't very
232 // useful in all other aspects -- that behaviour can also be achieved by limiting the output power).
233 const float sum = cw_level > 0 || ww_level > 0 ? cw_level + ww_level : 1; // Don't divide by zero.
234 *cold_white = white_level * std::max(cw_level, ww_level) * cw_level / sum;
235 *warm_white = white_level * std::max(cw_level, ww_level) * ww_level / sum;
236 }
237 } else {
238 *cold_white = *warm_white = 0;
239 }
240 }
241
243 void as_ct(float color_temperature_cw, float color_temperature_ww, float *color_temperature,
244 float *white_brightness) const {
245 const float white_level = this->color_mode_ & ColorCapability::RGB ? this->white_ : 1;
247 *color_temperature =
248 (this->color_temperature_ - color_temperature_cw) / (color_temperature_ww - color_temperature_cw);
249 *white_brightness = this->state_ * this->brightness_ * white_level;
250 } else { // Probably won't get here but put this here anyway.
251 *white_brightness = 0;
252 }
253 }
254
256 bool operator==(const LightColorValues &rhs) const {
257 return color_mode_ == rhs.color_mode_ && state_ == rhs.state_ && brightness_ == rhs.brightness_ &&
258 color_brightness_ == rhs.color_brightness_ && red_ == rhs.red_ && green_ == rhs.green_ &&
259 blue_ == rhs.blue_ && white_ == rhs.white_ && color_temperature_ == rhs.color_temperature_ &&
260 cold_white_ == rhs.cold_white_ && warm_white_ == rhs.warm_white_;
261 }
262 bool operator!=(const LightColorValues &rhs) const { return !(rhs == *this); }
263
265 ColorMode get_color_mode() const { return this->color_mode_; }
267 void set_color_mode(ColorMode color_mode) { this->color_mode_ = color_mode; }
268
270 float get_state() const { return this->state_; }
272 bool is_on() const { return this->get_state() != 0.0f; }
274 void set_state(float state) { this->state_ = clamp_unit_float(state); }
276 void set_state(bool state) { this->state_ = state ? 1.0f : 0.0f; }
277
279 float get_brightness() const { return this->brightness_; }
281 void set_brightness(float brightness) { this->brightness_ = clamp_unit_float(brightness); }
282
284 float get_color_brightness() const { return this->color_brightness_; }
286 void set_color_brightness(float brightness) { this->color_brightness_ = clamp_unit_float(brightness); }
287
289 float get_red() const { return this->red_; }
291 void set_red(float red) { this->red_ = clamp_unit_float(red); }
292
294 float get_green() const { return this->green_; }
296 void set_green(float green) { this->green_ = clamp_unit_float(green); }
297
299 float get_blue() const { return this->blue_; }
301 void set_blue(float blue) { this->blue_ = clamp_unit_float(blue); }
302
304 float get_white() const { return white_; }
306 void set_white(float white) { this->white_ = clamp_unit_float(white); }
307
309 float get_color_temperature() const { return this->color_temperature_; }
311 void set_color_temperature(float color_temperature) { this->color_temperature_ = color_temperature; }
312
315 if (this->color_temperature_ <= 0) {
316 return this->color_temperature_;
317 }
318 return 1000000.0 / this->color_temperature_;
319 }
321 void set_color_temperature_kelvin(float color_temperature) {
322 if (color_temperature <= 0) {
323 return;
324 }
325 this->color_temperature_ = 1000000.0 / color_temperature;
326 }
327
329 float get_cold_white() const { return this->cold_white_; }
331 void set_cold_white(float cold_white) { this->cold_white_ = clamp_unit_float(cold_white); }
332
334 float get_warm_white() const { return this->warm_white_; }
336 void set_warm_white(float warm_white) { this->warm_white_ = clamp_unit_float(warm_white); }
337
338 friend class LightCall;
339
340 protected:
341 float state_;
345};
346
347} // namespace esphome::light
This class represents a requested change in a light state.
Definition light_call.h:22
This class represents the color state for a light object.
float get_state() const
Get the state of these light color values. In range from 0.0 (off) to 1.0 (on)
void set_color_mode(ColorMode color_mode)
Set the color mode of these light color values.
float get_brightness() const
Get the brightness property of these light color values. In range 0.0 to 1.0.
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 state_
ON / OFF, float for transition.
void as_rgbww(float *red, float *green, float *blue, float *cold_white, float *warm_white, bool constant_brightness=false) const
Convert these light color values to an RGBWW representation with the given parameters.
float get_color_temperature() const
Get the color temperature property of these light color values in mired.
bool operator!=(const LightColorValues &rhs) const
void as_cwww(float *cold_white, float *warm_white, bool constant_brightness=false) const
Convert these light color values to an CWWW representation with the given parameters.
void set_state(bool state)
Set the state of these light color values as a binary true/false.
void set_brightness(float brightness)
Set the brightness property of these light color values. In range 0.0 to 1.0.
float get_cold_white() const
Get the cold white property of these light color values. In range 0.0 to 1.0.
void as_rgbw(float *red, float *green, float *blue, float *white) const
Convert these light color values to an RGBW representation and write them to red, green,...
void set_blue(float blue)
Set the blue property of these light color values. In range 0.0 to 1.0.
void set_cold_white(float cold_white)
Set the cold white property of these light color values. In range 0.0 to 1.0.
void set_color_brightness(float brightness)
Set the color brightness property of these light color values. In range 0.0 to 1.0.
void set_color_temperature_kelvin(float color_temperature)
Set the color temperature property of these light color values in kelvin.
bool operator==(const LightColorValues &rhs) const
Compare this LightColorValues to rhs, return true if and only if all attributes match.
void set_warm_white(float warm_white)
Set the warm white property of these light color values. In range 0.0 to 1.0.
void as_rgb(float *red, float *green, float *blue) const
Convert these light color values to an RGB representation and write them to red, green,...
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_state(float state)
Set the state of these light color values. In range from 0.0 (off) to 1.0 (on)
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.
LightColorValues()
Construct the LightColorValues with all attributes enabled, but state set to off.
static LightColorValues lerp(const LightColorValues &start, const LightColorValues &end, float completion)
Linearly interpolate between the values in start to the values in end.
void as_binary(bool *binary) const
Convert these light color values to a binary representation and write them to binary.
float color_temperature_
Color Temperature in Mired.
LightColorValues(ColorMode color_mode, float state, float brightness, float color_brightness, float red, float green, float blue, float white, float color_temperature, float cold_white, float warm_white)
void as_ct(float color_temperature_cw, float color_temperature_ww, float *color_temperature, float *white_brightness) const
Convert these light color values to a CT+BR representation with the given parameters.
ColorMode get_color_mode() const
Get the color mode of these light color values.
void set_white(float white)
Set the white property of these light color values. In range 0.0 to 1.0.
void as_rgbct(float color_temperature_cw, float color_temperature_ww, float *red, float *green, float *blue, float *color_temperature, float *white_brightness) const
Convert these light color values to an RGB+CT+BR representation with the given parameters.
float get_red() const
Get the red property of these light color values. In range 0.0 to 1.0.
void set_green(float green)
Set the green property of these light color values. In range 0.0 to 1.0.
void set_red(float red)
Set the red property of these light color values. In range 0.0 to 1.0.
void normalize_color()
Normalize the color (RGB/W) component.
void as_brightness(float *brightness) const
Convert these light color values to a brightness-only representation and write them to brightness.
float get_color_brightness() const
Get the color brightness property of these light color values. In range 0.0 to 1.0.
float get_color_temperature_kelvin() const
Get the color temperature property of these light color values in kelvin.
bool state
Definition fan.h:2
float clamp_unit_float(float x)
FLAG_HAS_TRANSITION float
bool float_out_of_unit_range(float x)
ColorMode
Color modes are a combination of color capabilities that can be used at the same time.
Definition color_mode.h:49
@ UNKNOWN
No color mode configured (cannot be a supported mode, only active when light is off).
@ RGB
Color can be controlled using RGB format (includes a brightness control for the color).
@ COLOR_TEMPERATURE
Color temperature can be controlled.
@ WHITE
Brightness of white channel can be controlled separately from other channels.
@ COLD_WARM_WHITE
Brightness of cold and warm white output can be controlled.
uint8_t end[39]
Definition sun_gtil2.cpp:17
uint16_t x
Definition tt21100.cpp:5