ESPHome  2024.4.0
tuya_light.cpp
Go to the documentation of this file.
1 #include "esphome/core/log.h"
2 #include "tuya_light.h"
3 #include "esphome/core/helpers.h"
4 
5 namespace esphome {
6 namespace tuya {
7 
8 static 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();
23  call.set_color_temperature(this->cold_white_temperature_ +
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  float red, green, blue;
61  switch (*this->color_type_) {
63  case TuyaColorType::RGB: {
64  auto rgb = parse_hex<uint32_t>(datapoint.value_string.substr(0, 6));
65  if (!rgb.has_value())
66  return;
67 
68  red = (*rgb >> 16) / 255.0f;
69  green = ((*rgb >> 8) & 0xff) / 255.0f;
70  blue = (*rgb & 0xff) / 255.0f;
71  break;
72  }
73  case TuyaColorType::HSV: {
74  auto hue = parse_hex<uint16_t>(datapoint.value_string.substr(0, 4));
75  auto saturation = parse_hex<uint16_t>(datapoint.value_string.substr(4, 4));
76  auto value = parse_hex<uint16_t>(datapoint.value_string.substr(8, 4));
77  if (!hue.has_value() || !saturation.has_value() || !value.has_value())
78  return;
79 
80  hsv_to_rgb(*hue, float(*saturation) / 1000, float(*value) / 1000, red, green, blue);
81  break;
82  }
83  }
84 
85  float current_red, current_green, current_blue;
86  this->state_->current_values_as_rgb(&current_red, &current_green, &current_blue);
87  if (red == current_red && green == current_green && blue == current_blue)
88  return;
89  auto rgb_call = this->state_->make_call();
90  rgb_call.set_rgb(red, green, blue);
91  rgb_call.perform();
92  });
93  }
94 
97  }
98 }
99 
101  ESP_LOGCONFIG(TAG, "Tuya Dimmer:");
102  if (this->dimmer_id_.has_value()) {
103  ESP_LOGCONFIG(TAG, " Dimmer has datapoint ID %u", *this->dimmer_id_);
104  }
105  if (this->switch_id_.has_value()) {
106  ESP_LOGCONFIG(TAG, " Switch has datapoint ID %u", *this->switch_id_);
107  }
108  if (this->color_id_.has_value()) {
109  ESP_LOGCONFIG(TAG, " Color has datapoint ID %u", *this->color_id_);
110  }
111 }
112 
114  auto traits = light::LightTraits();
115  if (this->color_temperature_id_.has_value() && this->dimmer_id_.has_value()) {
116  if (this->color_id_.has_value()) {
117  if (this->color_interlock_) {
118  traits.set_supported_color_modes({light::ColorMode::RGB, light::ColorMode::COLOR_TEMPERATURE});
119  } else {
120  traits.set_supported_color_modes(
122  }
123  } else
124  traits.set_supported_color_modes({light::ColorMode::COLOR_TEMPERATURE});
125  traits.set_min_mireds(this->cold_white_temperature_);
126  traits.set_max_mireds(this->warm_white_temperature_);
127  } else if (this->color_id_.has_value()) {
128  if (this->dimmer_id_.has_value()) {
129  if (this->color_interlock_) {
130  traits.set_supported_color_modes({light::ColorMode::RGB, light::ColorMode::WHITE});
131  } else {
132  traits.set_supported_color_modes({light::ColorMode::RGB_WHITE});
133  }
134  } else
135  traits.set_supported_color_modes({light::ColorMode::RGB});
136  } else if (this->dimmer_id_.has_value()) {
137  traits.set_supported_color_modes({light::ColorMode::BRIGHTNESS});
138  } else {
139  traits.set_supported_color_modes({light::ColorMode::ON_OFF});
140  }
141  return traits;
142 }
143 
145 
147  float red = 0.0f, green = 0.0f, blue = 0.0f;
148  float color_temperature = 0.0f, brightness = 0.0f;
149 
150  if (this->color_id_.has_value()) {
151  if (this->color_temperature_id_.has_value()) {
152  state->current_values_as_rgbct(&red, &green, &blue, &color_temperature, &brightness);
153  } else if (this->dimmer_id_.has_value()) {
154  state->current_values_as_rgbw(&red, &green, &blue, &brightness);
155  } else {
156  state->current_values_as_rgb(&red, &green, &blue);
157  }
158  } else if (this->color_temperature_id_.has_value()) {
159  state->current_values_as_ct(&color_temperature, &brightness);
160  } else {
161  state->current_values_as_brightness(&brightness);
162  }
163 
164  if (!state->current_values.is_on() && this->switch_id_.has_value()) {
165  this->parent_->set_boolean_datapoint_value(*this->switch_id_, false);
166  return;
167  }
168 
169  if (brightness > 0.0f || !color_interlock_) {
170  if (this->color_temperature_id_.has_value()) {
171  uint32_t color_temp_int = static_cast<uint32_t>(roundf(color_temperature * this->color_temperature_max_value_));
172  if (this->color_temperature_invert_) {
173  color_temp_int = this->color_temperature_max_value_ - color_temp_int;
174  }
175  this->parent_->set_integer_datapoint_value(*this->color_temperature_id_, color_temp_int);
176  }
177 
178  if (this->dimmer_id_.has_value()) {
179  auto brightness_int = static_cast<uint32_t>(brightness * this->max_value_);
180  brightness_int = std::max(brightness_int, this->min_value_);
181 
182  this->parent_->set_integer_datapoint_value(*this->dimmer_id_, brightness_int);
183  }
184  }
185 
186  if (this->color_id_.has_value() && (brightness == 0.0f || !color_interlock_)) {
187  std::string color_value;
188  switch (*this->color_type_) {
189  case TuyaColorType::RGB: {
190  char buffer[7];
191  sprintf(buffer, "%02X%02X%02X", int(red * 255), int(green * 255), int(blue * 255));
192  color_value = buffer;
193  break;
194  }
195  case TuyaColorType::HSV: {
196  int hue;
197  float saturation, value;
198  rgb_to_hsv(red, green, blue, hue, saturation, value);
199  char buffer[13];
200  sprintf(buffer, "%04X%04X%04X", hue, int(saturation * 1000), int(value * 1000));
201  color_value = buffer;
202  break;
203  }
204  case TuyaColorType::RGBHSV: {
205  int hue;
206  float saturation, value;
207  rgb_to_hsv(red, green, blue, hue, saturation, value);
208  char buffer[15];
209  sprintf(buffer, "%02X%02X%02X%04X%02X%02X", int(red * 255), int(green * 255), int(blue * 255), hue,
210  int(saturation * 255), int(value * 255));
211  color_value = buffer;
212  break;
213  }
214  }
215  this->parent_->set_string_datapoint_value(*this->color_id_, color_value);
216  }
217 
218  if (this->switch_id_.has_value()) {
219  this->parent_->set_boolean_datapoint_value(*this->switch_id_, true);
220  }
221 }
222 
223 } // namespace tuya
224 } // namespace esphome
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:478
This class represents the communication layer between the front-end MQTT layer and the hardware outpu...
Definition: light_state.h:34
optional< uint8_t > min_value_datapoint_id_
Definition: tuya_light.h:55
bool is_on() const
Get the binary true/false state of these light color values.
optional< TuyaColorType > color_type_
Definition: tuya_light.h:58
LightColorValues current_values
The current values of the light as outputted to the light.
Definition: light_state.h:65
light::LightTraits get_traits() override
Definition: tuya_light.cpp:113
RGB color output and a separate white output.
LightCall & set_rgb(float red, float green, float blue)
Set the RGB color of the light by RGB values.
Definition: light_call.cpp:678
Color temperature can be controlled.
bool has_value() const
Definition: optional.h:87
void register_listener(uint8_t datapoint_id, const std::function< void(TuyaDatapoint)> &func)
Definition: tuya.cpp:667
uint32_t color_temperature_max_value_
Definition: tuya_light.h:62
optional< uint8_t > color_temperature_id_
Definition: tuya_light.h:59
void write_state(light::LightState *state) override
Definition: tuya_light.cpp:146
void current_values_as_rgb(float *red, float *green, float *blue, bool color_interlock=false)
optional< uint8_t > dimmer_id_
Definition: tuya_light.h:54
const char *const TAG
Definition: spi.cpp:8
void current_values_as_rgbct(float *red, float *green, float *blue, float *color_temperature, float *white_brightness)
std::string value_string
Definition: tuya.h:39
void setup_state(light::LightState *state) override
Definition: tuya_light.cpp:144
float state_
ON / OFF, float for transition.
void dump_config() override
Definition: tuya_light.cpp:100
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:455
optional< uint8_t > color_id_
Definition: tuya_light.h:57
void current_values_as_brightness(float *brightness)
Light can be turned on/off.
RGB color output and a separate white output with controllable color temperature. ...
Master brightness of the light can be controlled.
void current_values_as_ct(float *color_temperature, float *white_brightness)
White output only (use only if the light also has another color mode such as RGB).
This class is used to represent the capabilities of a light.
Definition: light_traits.h:11
void set_boolean_datapoint_value(uint8_t datapoint_id, bool value)
Definition: tuya.cpp:539
void set_integer_datapoint_value(uint8_t datapoint_id, uint32_t value)
Definition: tuya.cpp:543
void set_string_datapoint_value(uint8_t datapoint_id, const std::string &value)
Definition: tuya.cpp:547
This is a workaround until we can figure out a way to get the tflite-micro idf component code availab...
Definition: a01nyub.cpp:7
Color can be controlled using RGB format (includes a brightness control for the color).
void setup() override
Definition: tuya_light.cpp:10
light::LightState * state_
Definition: tuya_light.h:67
optional< uint8_t > switch_id_
Definition: tuya_light.h:56
void current_values_as_rgbw(float *red, float *green, float *blue, float *white, bool color_interlock=false)
bool state
Definition: fan.h:34