mirror of
https://github.com/esphome/esphome.git
synced 2024-12-22 13:34:54 +01:00
Add configurable color datapoint (#4383)
* Add configurable color datapoint * Lint fixes * Review comments * Linting
This commit is contained in:
parent
5c49730cb9
commit
f68d577986
3 changed files with 97 additions and 63 deletions
|
@ -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]))
|
||||
|
|
|
@ -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<uint8_t>(datapoint.value_string.substr(0, 2));
|
||||
auto green = parse_hex<uint8_t>(datapoint.value_string.substr(2, 2));
|
||||
auto blue = parse_hex<uint8_t>(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<uint16_t>(datapoint.value_string.substr(0, 4));
|
||||
auto saturation = parse_hex<uint16_t>(datapoint.value_string.substr(4, 4));
|
||||
auto value = parse_hex<uint16_t>(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<uint8_t>(datapoint.value_string.substr(0, 2));
|
||||
auto green = parse_hex<uint8_t>(datapoint.value_string.substr(2, 2));
|
||||
auto blue = parse_hex<uint8_t>(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<uint16_t>(datapoint.value_string.substr(0, 4));
|
||||
auto saturation = parse_hex<uint16_t>(datapoint.value_string.substr(4, 4));
|
||||
auto value = parse_hex<uint16_t>(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()) {
|
||||
|
|
|
@ -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<uint8_t> dimmer_id_{};
|
||||
optional<uint8_t> min_value_datapoint_id_{};
|
||||
optional<uint8_t> switch_id_{};
|
||||
optional<uint8_t> rgb_id_{};
|
||||
optional<uint8_t> hsv_id_{};
|
||||
optional<uint8_t> color_id_{};
|
||||
optional<TuyaColorType> color_type_{};
|
||||
optional<uint8_t> color_temperature_id_{};
|
||||
uint32_t min_value_ = 0;
|
||||
uint32_t max_value_ = 255;
|
||||
|
|
Loading…
Reference in a new issue