ESPHome 2026.3.0
Loading...
Searching...
No Matches
tuya_light.cpp
Go to the documentation of this file.
1#include "esphome/core/log.h"
2#include "tuya_light.h"
4
5namespace esphome {
6namespace tuya {
7
8static const char *const TAG = "tuya.light";
9
11 if (this->color_temperature_id_.has_value()) {
12 this->parent_->register_listener(*this->color_temperature_id_, [this](const TuyaDatapoint &datapoint) {
13 if (this->state_->current_values != this->state_->remote_values) {
14 ESP_LOGD(TAG, "Light is transitioning, datapoint change ignored");
15 return;
16 }
17
18 auto datapoint_value = datapoint.value_uint;
19 if (this->color_temperature_invert_) {
20 datapoint_value = this->color_temperature_max_value_ - datapoint_value;
21 }
22 auto call = this->state_->make_call();
25 (float(datapoint_value) / this->color_temperature_max_value_));
26 call.perform();
27 });
28 }
29 if (this->dimmer_id_.has_value()) {
30 this->parent_->register_listener(*this->dimmer_id_, [this](const TuyaDatapoint &datapoint) {
31 if (this->state_->current_values != this->state_->remote_values) {
32 ESP_LOGD(TAG, "Light is transitioning, datapoint change ignored");
33 return;
34 }
35
36 auto call = this->state_->make_call();
37 call.set_brightness(float(datapoint.value_uint) / this->max_value_);
38 call.perform();
39 });
40 }
41 if (switch_id_.has_value()) {
42 this->parent_->register_listener(*this->switch_id_, [this](const TuyaDatapoint &datapoint) {
43 if (this->state_->current_values != this->state_->remote_values) {
44 ESP_LOGD(TAG, "Light is transitioning, datapoint change ignored");
45 return;
46 }
47
48 auto call = this->state_->make_call();
49 call.set_state(datapoint.value_bool);
50 call.perform();
51 });
52 }
53 if (color_id_.has_value()) {
54 this->parent_->register_listener(*this->color_id_, [this](const TuyaDatapoint &datapoint) {
55 if (this->state_->current_values != this->state_->remote_values) {
56 ESP_LOGD(TAG, "Light is transitioning, datapoint change ignored");
57 return;
58 }
59
60 if (!this->color_type_.has_value())
61 return;
62
63 float red, green, blue;
64 switch (*this->color_type_) {
66 case TuyaColorType::RGB: {
67 auto rgb = parse_hex<uint32_t>(datapoint.value_string.substr(0, 6));
68 if (!rgb.has_value())
69 return;
70
71 red = (*rgb >> 16) / 255.0f;
72 green = ((*rgb >> 8) & 0xff) / 255.0f;
73 blue = (*rgb & 0xff) / 255.0f;
74 break;
75 }
76 case TuyaColorType::HSV: {
77 auto hue = parse_hex<uint16_t>(datapoint.value_string.substr(0, 4));
78 auto saturation = parse_hex<uint16_t>(datapoint.value_string.substr(4, 4));
79 auto value = parse_hex<uint16_t>(datapoint.value_string.substr(8, 4));
80 if (!hue.has_value() || !saturation.has_value() || !value.has_value())
81 return;
82
83 hsv_to_rgb(*hue, float(*saturation) / 1000, float(*value) / 1000, red, green, blue);
84 break;
85 }
86 }
87
88 float current_red, current_green, current_blue;
89 this->state_->current_values_as_rgb(&current_red, &current_green, &current_blue);
90 if (red == current_red && green == current_green && blue == current_blue)
91 return;
92 auto rgb_call = this->state_->make_call();
93 rgb_call.set_rgb(red, green, blue);
94 rgb_call.perform();
95 });
96 }
97
98 if (min_value_datapoint_id_.has_value()) {
100 }
101}
102
104 ESP_LOGCONFIG(TAG, "Tuya Dimmer:");
105 if (this->dimmer_id_.has_value()) {
106 ESP_LOGCONFIG(TAG, " Dimmer has datapoint ID %u", *this->dimmer_id_);
107 }
108 if (this->switch_id_.has_value()) {
109 ESP_LOGCONFIG(TAG, " Switch has datapoint ID %u", *this->switch_id_);
110 }
111 if (this->color_id_.has_value()) {
112 ESP_LOGCONFIG(TAG, " Color has datapoint ID %u", *this->color_id_);
113 }
114}
115
117 auto traits = light::LightTraits();
118 if (this->color_temperature_id_.has_value() && this->dimmer_id_.has_value()) {
119 if (this->color_id_.has_value()) {
120 if (this->color_interlock_) {
121 traits.set_supported_color_modes({light::ColorMode::RGB, light::ColorMode::COLOR_TEMPERATURE});
122 } else {
123 traits.set_supported_color_modes(
125 }
126 } else {
127 traits.set_supported_color_modes({light::ColorMode::COLOR_TEMPERATURE});
128 }
129 traits.set_min_mireds(this->cold_white_temperature_);
130 traits.set_max_mireds(this->warm_white_temperature_);
131 } else if (this->color_id_.has_value()) {
132 if (this->dimmer_id_.has_value()) {
133 if (this->color_interlock_) {
134 traits.set_supported_color_modes({light::ColorMode::RGB, light::ColorMode::WHITE});
135 } else {
136 traits.set_supported_color_modes({light::ColorMode::RGB_WHITE});
137 }
138 } else {
139 traits.set_supported_color_modes({light::ColorMode::RGB});
140 }
141 } else if (this->dimmer_id_.has_value()) {
142 traits.set_supported_color_modes({light::ColorMode::BRIGHTNESS});
143 } else {
144 traits.set_supported_color_modes({light::ColorMode::ON_OFF});
145 }
146 return traits;
147}
148
150
152 float red = 0.0f, green = 0.0f, blue = 0.0f;
153 float color_temperature = 0.0f, brightness = 0.0f;
154
155 if (this->color_id_.has_value()) {
156 if (this->color_temperature_id_.has_value()) {
157 state->current_values_as_rgbct(&red, &green, &blue, &color_temperature, &brightness);
158 } else if (this->dimmer_id_.has_value()) {
159 state->current_values_as_rgbw(&red, &green, &blue, &brightness);
160 } else {
161 state->current_values_as_rgb(&red, &green, &blue);
162 }
163 } else if (this->color_temperature_id_.has_value()) {
164 state->current_values_as_ct(&color_temperature, &brightness);
165 } else {
166 state->current_values_as_brightness(&brightness);
167 }
168
169 if (!state->current_values.is_on() && this->switch_id_.has_value()) {
170 this->parent_->set_boolean_datapoint_value(*this->switch_id_, false);
171 return;
172 }
173
174 if (brightness > 0.0f || !color_interlock_) {
175 if (this->color_temperature_id_.has_value()) {
176 uint32_t color_temp_int = static_cast<uint32_t>(roundf(color_temperature * this->color_temperature_max_value_));
177 if (this->color_temperature_invert_) {
178 color_temp_int = this->color_temperature_max_value_ - color_temp_int;
179 }
180 this->parent_->set_integer_datapoint_value(*this->color_temperature_id_, color_temp_int);
181 }
182
183 if (this->dimmer_id_.has_value()) {
184 auto brightness_int = static_cast<uint32_t>(brightness * this->max_value_);
185 brightness_int = std::max(brightness_int, this->min_value_);
186
187 this->parent_->set_integer_datapoint_value(*this->dimmer_id_, brightness_int);
188 }
189 }
190
191 if (this->color_id_.has_value() && this->color_type_.has_value() && (brightness == 0.0f || !color_interlock_)) {
192 std::string color_value;
193 switch (*this->color_type_) {
194 case TuyaColorType::RGB: {
195 char buffer[7];
196 const char *format_str = this->color_type_lowercase_ ? "%02x%02x%02x" : "%02X%02X%02X";
197 snprintf(buffer, sizeof(buffer), format_str, int(red * 255), int(green * 255), int(blue * 255));
198 color_value = buffer;
199 break;
200 }
201 case TuyaColorType::HSV: {
202 int hue;
203 float saturation, value;
204 rgb_to_hsv(red, green, blue, hue, saturation, value);
205 char buffer[13];
206 const char *format_str = this->color_type_lowercase_ ? "%04x%04x%04x" : "%04X%04X%04X";
207 snprintf(buffer, sizeof(buffer), format_str, hue, int(saturation * 1000), int(value * 1000));
208 color_value = buffer;
209 break;
210 }
212 int hue;
213 float saturation, value;
214 rgb_to_hsv(red, green, blue, hue, saturation, value);
215 char buffer[15];
216 const char *format_str = this->color_type_lowercase_ ? "%02x%02x%02x%04x%02x%02x" : "%02X%02X%02X%04X%02X%02X";
217 snprintf(buffer, sizeof(buffer), format_str, int(red * 255), int(green * 255), int(blue * 255), hue,
218 int(saturation * 255), int(value * 255));
219 color_value = buffer;
220 break;
221 }
222 }
223 this->parent_->set_string_datapoint_value(*this->color_id_, color_value);
224 }
225
226 if (this->switch_id_.has_value()) {
228 }
229}
230
231} // namespace tuya
232} // namespace esphome
LightCall & set_color_temperature(optional< float > color_temperature)
Set the color temperature of the light in mireds for CWWW or RGBWW lights.
LightCall & set_rgb(float red, float green, float blue)
Set the RGB color of the light by RGB values.
LightCall & set_brightness(optional< float > brightness)
Set the target brightness of the light from 0.0 (fully off) to 1.0 (fully on)
LightCall & set_state(optional< bool > state)
Set the binary ON/OFF state of the light.
float state_
ON / OFF, float for transition.
This class represents the communication layer between the front-end MQTT layer and the hardware outpu...
Definition light_state.h:93
void current_values_as_rgb(float *red, float *green, float *blue)
void current_values_as_rgbct(float *red, float *green, float *blue, float *color_temperature, float *white_brightness)
void current_values_as_ct(float *color_temperature, float *white_brightness)
void current_values_as_rgbw(float *red, float *green, float *blue, float *white)
LightColorValues current_values
The current values of the light as outputted to the light.
This class is used to represent the capabilities of a light.
void set_string_datapoint_value(uint8_t datapoint_id, const std::string &value)
Definition tuya.cpp:626
void set_boolean_datapoint_value(uint8_t datapoint_id, bool value)
Definition tuya.cpp:618
void register_listener(uint8_t datapoint_id, const std::function< void(TuyaDatapoint)> &func)
Definition tuya.cpp:747
void set_integer_datapoint_value(uint8_t datapoint_id, uint32_t value)
Definition tuya.cpp:622
void setup() override
optional< uint8_t > min_value_datapoint_id_
Definition tuya_light.h:52
void setup_state(light::LightState *state) override
void dump_config() override
light::LightTraits get_traits() override
optional< TuyaColorType > color_type_
Definition tuya_light.h:55
optional< uint8_t > switch_id_
Definition tuya_light.h:53
optional< uint8_t > color_temperature_id_
Definition tuya_light.h:56
void write_state(light::LightState *state) override
optional< uint8_t > dimmer_id_
Definition tuya_light.h:51
optional< uint8_t > color_id_
Definition tuya_light.h:54
uint32_t color_temperature_max_value_
Definition tuya_light.h:59
light::LightState * state_
Definition tuya_light.h:65
bool state
Definition fan.h:2
@ ON_OFF
Only on/off control.
@ BRIGHTNESS
Dimmable light.
@ RGB_WHITE
RGB color output and a separate white output.
@ RGB_COLOR_TEMPERATURE
RGB color output and a separate white output with controllable color temperature.
@ RGB
RGB color output.
@ COLOR_TEMPERATURE
Controllable color temperature output.
@ WHITE
White output only (use only if the light also has another color mode such as RGB).
const char *const TAG
Definition spi.cpp:7
Providing packet encoding functions for exchanging data with a remote host.
Definition a01nyub.cpp:7
void rgb_to_hsv(float red, float green, float blue, int &hue, float &saturation, float &value)
Convert red, green and blue (all 0-1) values to hue (0-360), saturation (0-1) and value (0-1).
Definition helpers.cpp:720
size_t parse_hex(const char *str, size_t length, uint8_t *data, size_t count)
Parse bytes from a hex-encoded string into a byte array.
Definition helpers.cpp:294
void hsv_to_rgb(int hue, float saturation, float value, float &red, float &green, float &blue)
Convert hue (0-360), saturation (0-1) and value (0-1) to red, green and blue (all 0-1).
Definition helpers.cpp:743
static void uint32_t
std::string value_string
Definition tuya.h:39