From f68d5779868b595e812d817c124feb56e09948a0 Mon Sep 17 00:00:00 2001 From: irtimaled Date: Sun, 19 Feb 2023 13:50:46 -0800 Subject: [PATCH] Add configurable color datapoint (#4383) * Add configurable color datapoint * Lint fixes * Review comments * Linting --- esphome/components/tuya/light/__init__.py | 30 +++-- esphome/components/tuya/light/tuya_light.cpp | 116 +++++++++++-------- esphome/components/tuya/light/tuya_light.h | 14 ++- 3 files changed, 97 insertions(+), 63 deletions(-) diff --git a/esphome/components/tuya/light/__init__.py b/esphome/components/tuya/light/__init__.py index b983e3f84e..d806060018 100644 --- a/esphome/components/tuya/light/__init__.py +++ b/esphome/components/tuya/light/__init__.py @@ -23,9 +23,23 @@ CONF_COLOR_TEMPERATURE_INVERT = "color_temperature_invert" CONF_COLOR_TEMPERATURE_MAX_VALUE = "color_temperature_max_value" CONF_RGB_DATAPOINT = "rgb_datapoint" CONF_HSV_DATAPOINT = "hsv_datapoint" +CONF_COLOR_DATAPOINT = "color_datapoint" +CONF_COLOR_TYPE = "color_type" + +TuyaColorType = tuya_ns.enum("TuyaColorType") + +COLOR_TYPES = { + "RGB": TuyaColorType.RGB, + "HSV": TuyaColorType.HSV, + "RGBHSV": TuyaColorType.RGBHSV, +} TuyaLight = tuya_ns.class_("TuyaLight", light.LightOutput, cg.Component) +COLOR_CONFIG_ERROR = ( + "This option has been removed, use color_datapoint and color_type instead." +) + CONFIG_SCHEMA = cv.All( light.BRIGHTNESS_ONLY_LIGHT_SCHEMA.extend( { @@ -34,8 +48,10 @@ CONFIG_SCHEMA = cv.All( cv.Optional(CONF_DIMMER_DATAPOINT): cv.uint8_t, cv.Optional(CONF_MIN_VALUE_DATAPOINT): cv.uint8_t, cv.Optional(CONF_SWITCH_DATAPOINT): cv.uint8_t, - cv.Exclusive(CONF_RGB_DATAPOINT, "color"): cv.uint8_t, - cv.Exclusive(CONF_HSV_DATAPOINT, "color"): cv.uint8_t, + cv.Optional(CONF_RGB_DATAPOINT): cv.invalid(COLOR_CONFIG_ERROR), + cv.Optional(CONF_HSV_DATAPOINT): cv.invalid(COLOR_CONFIG_ERROR), + cv.Inclusive(CONF_COLOR_DATAPOINT, "color"): cv.uint8_t, + cv.Inclusive(CONF_COLOR_TYPE, "color"): cv.enum(COLOR_TYPES, upper=True), cv.Optional(CONF_COLOR_INTERLOCK, default=False): cv.boolean, cv.Inclusive( CONF_COLOR_TEMPERATURE_DATAPOINT, "color_temperature" @@ -61,8 +77,7 @@ CONFIG_SCHEMA = cv.All( cv.has_at_least_one_key( CONF_DIMMER_DATAPOINT, CONF_SWITCH_DATAPOINT, - CONF_RGB_DATAPOINT, - CONF_HSV_DATAPOINT, + CONF_COLOR_DATAPOINT, ), ) @@ -78,10 +93,9 @@ async def to_code(config): cg.add(var.set_min_value_datapoint_id(config[CONF_MIN_VALUE_DATAPOINT])) if CONF_SWITCH_DATAPOINT in config: cg.add(var.set_switch_id(config[CONF_SWITCH_DATAPOINT])) - if CONF_RGB_DATAPOINT in config: - cg.add(var.set_rgb_id(config[CONF_RGB_DATAPOINT])) - elif CONF_HSV_DATAPOINT in config: - cg.add(var.set_hsv_id(config[CONF_HSV_DATAPOINT])) + if CONF_COLOR_DATAPOINT in config: + cg.add(var.set_color_id(config[CONF_COLOR_DATAPOINT])) + cg.add(var.set_color_type(config[CONF_COLOR_TYPE])) if CONF_COLOR_TEMPERATURE_DATAPOINT in config: cg.add(var.set_color_temperature_id(config[CONF_COLOR_TEMPERATURE_DATAPOINT])) cg.add(var.set_color_temperature_invert(config[CONF_COLOR_TEMPERATURE_INVERT])) diff --git a/esphome/components/tuya/light/tuya_light.cpp b/esphome/components/tuya/light/tuya_light.cpp index b75e85bc14..869e20871d 100644 --- a/esphome/components/tuya/light/tuya_light.cpp +++ b/esphome/components/tuya/light/tuya_light.cpp @@ -50,38 +50,39 @@ void TuyaLight::setup() { call.perform(); }); } - if (rgb_id_.has_value()) { - this->parent_->register_listener(*this->rgb_id_, [this](const TuyaDatapoint &datapoint) { - auto red = parse_hex(datapoint.value_string.substr(0, 2)); - auto green = parse_hex(datapoint.value_string.substr(2, 2)); - auto blue = parse_hex(datapoint.value_string.substr(4, 2)); - if (red.has_value() && green.has_value() && blue.has_value()) { - if (this->state_->current_values != this->state_->remote_values) { - ESP_LOGD(TAG, "Light is transitioning, datapoint change ignored"); - return; - } - - auto call = this->state_->make_call(); - call.set_rgb(float(*red) / 255, float(*green) / 255, float(*blue) / 255); - call.perform(); + if (color_id_.has_value()) { + this->parent_->register_listener(*this->color_id_, [this](const TuyaDatapoint &datapoint) { + if (this->state_->current_values != this->state_->remote_values) { + ESP_LOGD(TAG, "Light is transitioning, datapoint change ignored"); + return; } - }); - } else if (hsv_id_.has_value()) { - this->parent_->register_listener(*this->hsv_id_, [this](const TuyaDatapoint &datapoint) { - auto hue = parse_hex(datapoint.value_string.substr(0, 4)); - auto saturation = parse_hex(datapoint.value_string.substr(4, 4)); - auto value = parse_hex(datapoint.value_string.substr(8, 4)); - if (hue.has_value() && saturation.has_value() && value.has_value()) { - if (this->state_->current_values != this->state_->remote_values) { - ESP_LOGD(TAG, "Light is transitioning, datapoint change ignored"); - return; - } - float red, green, blue; - hsv_to_rgb(*hue, float(*saturation) / 1000, float(*value) / 1000, red, green, blue); - auto call = this->state_->make_call(); - call.set_rgb(red, green, blue); - call.perform(); + switch (*this->color_type_) { + case TuyaColorType::RGBHSV: + case TuyaColorType::RGB: { + auto red = parse_hex(datapoint.value_string.substr(0, 2)); + auto green = parse_hex(datapoint.value_string.substr(2, 2)); + auto blue = parse_hex(datapoint.value_string.substr(4, 2)); + if (red.has_value() && green.has_value() && blue.has_value()) { + auto rgb_call = this->state_->make_call(); + rgb_call.set_rgb(float(*red) / 255, float(*green) / 255, float(*blue) / 255); + rgb_call.perform(); + } + break; + } + case TuyaColorType::HSV: { + auto hue = parse_hex(datapoint.value_string.substr(0, 4)); + auto saturation = parse_hex(datapoint.value_string.substr(4, 4)); + auto value = parse_hex(datapoint.value_string.substr(8, 4)); + if (hue.has_value() && saturation.has_value() && value.has_value()) { + float red, green, blue; + hsv_to_rgb(*hue, float(*saturation) / 1000, float(*value) / 1000, red, green, blue); + auto rgb_call = this->state_->make_call(); + rgb_call.set_rgb(red, green, blue); + rgb_call.perform(); + } + break; + } } }); } @@ -98,17 +99,15 @@ void TuyaLight::dump_config() { if (this->switch_id_.has_value()) { ESP_LOGCONFIG(TAG, " Switch has datapoint ID %u", *this->switch_id_); } - if (this->rgb_id_.has_value()) { - ESP_LOGCONFIG(TAG, " RGB has datapoint ID %u", *this->rgb_id_); - } else if (this->hsv_id_.has_value()) { - ESP_LOGCONFIG(TAG, " HSV has datapoint ID %u", *this->hsv_id_); + if (this->color_id_.has_value()) { + ESP_LOGCONFIG(TAG, " Color has datapoint ID %u", *this->color_id_); } } light::LightTraits TuyaLight::get_traits() { auto traits = light::LightTraits(); if (this->color_temperature_id_.has_value() && this->dimmer_id_.has_value()) { - if (this->rgb_id_.has_value() || this->hsv_id_.has_value()) { + if (this->color_id_.has_value()) { if (this->color_interlock_) { traits.set_supported_color_modes({light::ColorMode::RGB, light::ColorMode::COLOR_TEMPERATURE}); } else { @@ -119,7 +118,7 @@ light::LightTraits TuyaLight::get_traits() { traits.set_supported_color_modes({light::ColorMode::COLOR_TEMPERATURE}); traits.set_min_mireds(this->cold_white_temperature_); traits.set_max_mireds(this->warm_white_temperature_); - } else if (this->rgb_id_.has_value() || this->hsv_id_.has_value()) { + } else if (this->color_id_.has_value()) { if (this->dimmer_id_.has_value()) { if (this->color_interlock_) { traits.set_supported_color_modes({light::ColorMode::RGB, light::ColorMode::WHITE}); @@ -142,7 +141,7 @@ void TuyaLight::write_state(light::LightState *state) { float red = 0.0f, green = 0.0f, blue = 0.0f; float color_temperature = 0.0f, brightness = 0.0f; - if (this->rgb_id_.has_value() || this->hsv_id_.has_value()) { + if (this->color_id_.has_value()) { if (this->color_temperature_id_.has_value()) { state->current_values_as_rgbct(&red, &green, &blue, &color_temperature, &brightness); } else if (this->dimmer_id_.has_value()) { @@ -178,21 +177,36 @@ void TuyaLight::write_state(light::LightState *state) { } } - if (brightness == 0.0f || !color_interlock_) { - if (this->rgb_id_.has_value()) { - char buffer[7]; - sprintf(buffer, "%02X%02X%02X", int(red * 255), int(green * 255), int(blue * 255)); - std::string rgb_value = buffer; - this->parent_->set_string_datapoint_value(*this->rgb_id_, rgb_value); - } else if (this->hsv_id_.has_value()) { - int hue; - float saturation, value; - rgb_to_hsv(red, green, blue, hue, saturation, value); - char buffer[13]; - sprintf(buffer, "%04X%04X%04X", hue, int(saturation * 1000), int(value * 1000)); - std::string hsv_value = buffer; - this->parent_->set_string_datapoint_value(*this->hsv_id_, hsv_value); + if (this->color_id_.has_value() && (brightness == 0.0f || !color_interlock_)) { + std::string color_value; + switch (*this->color_type_) { + case TuyaColorType::RGB: { + char buffer[7]; + sprintf(buffer, "%02X%02X%02X", int(red * 255), int(green * 255), int(blue * 255)); + color_value = buffer; + break; + } + case TuyaColorType::HSV: { + int hue; + float saturation, value; + rgb_to_hsv(red, green, blue, hue, saturation, value); + char buffer[13]; + sprintf(buffer, "%04X%04X%04X", hue, int(saturation * 1000), int(value * 1000)); + color_value = buffer; + break; + } + case TuyaColorType::RGBHSV: { + int hue; + float saturation, value; + rgb_to_hsv(red, green, blue, hue, saturation, value); + char buffer[15]; + sprintf(buffer, "%02X%02X%02X%04X%02X%02X", int(red * 255), int(green * 255), int(blue * 255), hue, + int(saturation * 255), int(value * 255)); + color_value = buffer; + break; + } } + this->parent_->set_string_datapoint_value(*this->color_id_, color_value); } if (this->switch_id_.has_value()) { diff --git a/esphome/components/tuya/light/tuya_light.h b/esphome/components/tuya/light/tuya_light.h index 3d9f25271c..bd9920f18f 100644 --- a/esphome/components/tuya/light/tuya_light.h +++ b/esphome/components/tuya/light/tuya_light.h @@ -7,6 +7,12 @@ namespace esphome { namespace tuya { +enum TuyaColorType { + RGB, + HSV, + RGBHSV, +}; + class TuyaLight : public Component, public light::LightOutput { public: void setup() override; @@ -16,8 +22,8 @@ class TuyaLight : public Component, public light::LightOutput { this->min_value_datapoint_id_ = min_value_datapoint_id; } void set_switch_id(uint8_t switch_id) { this->switch_id_ = switch_id; } - void set_rgb_id(uint8_t rgb_id) { this->rgb_id_ = rgb_id; } - void set_hsv_id(uint8_t hsv_id) { this->hsv_id_ = hsv_id; } + void set_color_id(uint8_t color_id) { this->color_id_ = color_id; } + void set_color_type(TuyaColorType color_type) { this->color_type_ = color_type; } void set_color_temperature_id(uint8_t color_temperature_id) { this->color_temperature_id_ = color_temperature_id; } void set_color_temperature_invert(bool color_temperature_invert) { this->color_temperature_invert_ = color_temperature_invert; @@ -48,8 +54,8 @@ class TuyaLight : public Component, public light::LightOutput { optional dimmer_id_{}; optional min_value_datapoint_id_{}; optional switch_id_{}; - optional rgb_id_{}; - optional hsv_id_{}; + optional color_id_{}; + optional color_type_{}; optional color_temperature_id_{}; uint32_t min_value_ = 0; uint32_t max_value_ = 255;