WIP: "off" color temperature for (rgb)ct lights

Some (rgb)ct lights require a certain color temperature to be set to
actually turn off (the white component, in case of rgbct lights), this
allows configuring that.
This commit is contained in:
Mara Sophie Grosch 2024-05-26 00:11:14 +02:00
parent 964410bd64
commit 945c41aa3b
8 changed files with 67 additions and 11 deletions

View file

@ -13,11 +13,13 @@ class CTLightOutput : public light::LightOutput {
void set_brightness(output::FloatOutput *brightness) { brightness_ = brightness; }
void set_cold_white_temperature(float cold_white_temperature) { cold_white_temperature_ = cold_white_temperature; }
void set_warm_white_temperature(float warm_white_temperature) { warm_white_temperature_ = warm_white_temperature; }
void set_off_color_temperature(float off_color_temperature) { off_color_temperature_ = off_color_temperature; }
light::LightTraits get_traits() override {
auto traits = light::LightTraits();
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_);
traits.set_off_mireds(this->off_color_temperature_);
return traits;
}
void write_state(light::LightState *state) override {
@ -32,6 +34,7 @@ class CTLightOutput : public light::LightOutput {
output::FloatOutput *brightness_;
float cold_white_temperature_;
float warm_white_temperature_;
float off_color_temperature_{0};
};
} // namespace color_temperature

View file

@ -7,6 +7,9 @@ from esphome.const import (
CONF_OUTPUT_ID,
CONF_COLD_WHITE_COLOR_TEMPERATURE,
CONF_WARM_WHITE_COLOR_TEMPERATURE,
CONF_OFF_COLOR_TEMPERATURE,
CONF_COLD_WHITE,
CONF_WARM_WHITE,
)
CODEOWNERS = ["@jesserockz"]
@ -22,6 +25,12 @@ CONFIG_SCHEMA = cv.All(
cv.Required(CONF_BRIGHTNESS): cv.use_id(output.FloatOutput),
cv.Required(CONF_COLD_WHITE_COLOR_TEMPERATURE): cv.color_temperature,
cv.Required(CONF_WARM_WHITE_COLOR_TEMPERATURE): cv.color_temperature,
cv.Optional(CONF_OFF_COLOR_TEMPERATURE): cv.enum(
{
CONF_COLD_WHITE: None,
CONF_WARM_WHITE: None,
}
),
}
),
light.validate_color_temperature_channels,
@ -40,3 +49,13 @@ async def to_code(config):
cg.add(var.set_cold_white_temperature(config[CONF_COLD_WHITE_COLOR_TEMPERATURE]))
cg.add(var.set_warm_white_temperature(config[CONF_WARM_WHITE_COLOR_TEMPERATURE]))
if CONF_OFF_COLOR_TEMPERATURE in config:
if config[CONF_OFF_COLOR_TEMPERATURE] == CONF_COLD_WHITE:
cg.add(
var.set_off_color_temperature(config[CONF_COLD_WHITE_COLOR_TEMPERATURE])
)
elif config[CONF_OFF_COLOR_TEMPERATURE] == CONF_WARM_WHITE:
cg.add(
var.set_off_color_temperature(config[CONF_WARM_WHITE_COLOR_TEMPERATURE])
)

View file

@ -165,10 +165,11 @@ class LightColorValues {
}
/// Convert these light color values to an RGB+CT+BR representation with the given parameters.
void as_rgbct(float color_temperature_cw, float color_temperature_ww, float *red, float *green, float *blue,
float *color_temperature, float *white_brightness, float gamma = 0) const {
void as_rgbct(float color_temperature_cw, float color_temperature_ww, float color_temperature_off, float *red,
float *green, float *blue, float *color_temperature, float *white_brightness, float gamma = 0) const {
this->as_rgb(red, green, blue, gamma);
this->as_ct(color_temperature_cw, color_temperature_ww, color_temperature, white_brightness, gamma);
this->as_ct(color_temperature_cw, color_temperature_ww, color_temperature_off, color_temperature, white_brightness,
gamma);
}
/// Convert these light color values to an CWWW representation with the given parameters.
@ -196,16 +197,23 @@ class LightColorValues {
}
/// Convert these light color values to a CT+BR representation with the given parameters.
void as_ct(float color_temperature_cw, float color_temperature_ww, float *color_temperature, float *white_brightness,
float gamma = 0) const {
void as_ct(float color_temperature_cw, float color_temperature_ww, float color_temperature_off,
float *color_temperature, float *white_brightness, float gamma = 0) const {
const float white_level = this->color_mode_ & ColorCapability::RGB ? this->white_ : 1;
float output_color_temperature = color_temperature_off;
if (this->color_mode_ & ColorCapability::COLOR_TEMPERATURE) {
*color_temperature =
(this->color_temperature_ - color_temperature_cw) / (color_temperature_ww - color_temperature_cw);
*white_brightness = gamma_correct(this->state_ * this->brightness_ * white_level, gamma);
if (*white_brightness != 0.0f) {
output_color_temperature = this->color_temperature_;
}
} else { // Probably won't get here but put this here anyway.
*white_brightness = 0;
}
*color_temperature =
(output_color_temperature - color_temperature_cw) / (color_temperature_ww - color_temperature_cw);
}
/// Compare this LightColorValues to rhs, return true if and only if all attributes match.

View file

@ -203,8 +203,8 @@ void LightState::current_values_as_rgbww(float *red, float *green, float *blue,
void LightState::current_values_as_rgbct(float *red, float *green, float *blue, float *color_temperature,
float *white_brightness) {
auto traits = this->get_traits();
this->current_values.as_rgbct(traits.get_min_mireds(), traits.get_max_mireds(), red, green, blue, color_temperature,
white_brightness, this->gamma_correct_);
this->current_values.as_rgbct(traits.get_min_mireds(), traits.get_max_mireds(), traits.get_off_mireds(), red, green,
blue, color_temperature, white_brightness, this->gamma_correct_);
}
void LightState::current_values_as_cwww(float *cold_white, float *warm_white, bool constant_brightness) {
auto traits = this->get_traits();
@ -212,8 +212,8 @@ void LightState::current_values_as_cwww(float *cold_white, float *warm_white, bo
}
void LightState::current_values_as_ct(float *color_temperature, float *white_brightness) {
auto traits = this->get_traits();
this->current_values.as_ct(traits.get_min_mireds(), traits.get_max_mireds(), color_temperature, white_brightness,
this->gamma_correct_);
this->current_values.as_ct(traits.get_min_mireds(), traits.get_max_mireds(), traits.get_off_mireds(),
color_temperature, white_brightness, this->gamma_correct_);
}
bool LightState::is_transformer_active() { return this->is_transformer_active_; }

View file

@ -50,11 +50,14 @@ class LightTraits {
void set_min_mireds(float min_mireds) { this->min_mireds_ = min_mireds; }
float get_max_mireds() const { return this->max_mireds_; }
void set_max_mireds(float max_mireds) { this->max_mireds_ = max_mireds; }
float get_off_mireds() const { return this->off_mireds_; }
void set_off_mireds(float off_mireds) { this->off_mireds_ = off_mireds; }
protected:
std::set<ColorMode> supported_color_modes_{};
float min_mireds_{0};
float max_mireds_{0};
float off_mireds_{0};
};
} // namespace light

View file

@ -10,6 +10,9 @@ from esphome.const import (
CONF_OUTPUT_ID,
CONF_COLD_WHITE_COLOR_TEMPERATURE,
CONF_WARM_WHITE_COLOR_TEMPERATURE,
CONF_OFF_COLOR_TEMPERATURE,
CONF_COLD_WHITE,
CONF_WARM_WHITE,
)
CODEOWNERS = ["@jesserockz"]
@ -30,6 +33,12 @@ CONFIG_SCHEMA = cv.All(
cv.Required(CONF_WHITE_BRIGHTNESS): cv.use_id(output.FloatOutput),
cv.Required(CONF_COLD_WHITE_COLOR_TEMPERATURE): cv.color_temperature,
cv.Required(CONF_WARM_WHITE_COLOR_TEMPERATURE): cv.color_temperature,
cv.Optional(CONF_OFF_COLOR_TEMPERATURE): cv.enum(
{
CONF_COLD_WHITE: None,
CONF_WARM_WHITE: None,
}
),
cv.Optional(CONF_COLOR_INTERLOCK, default=False): cv.boolean,
}
),
@ -56,4 +65,14 @@ async def to_code(config):
cg.add(var.set_cold_white_temperature(config[CONF_COLD_WHITE_COLOR_TEMPERATURE]))
cg.add(var.set_warm_white_temperature(config[CONF_WARM_WHITE_COLOR_TEMPERATURE]))
if CONF_OFF_COLOR_TEMPERATURE in config:
if config[CONF_OFF_COLOR_TEMPERATURE] == CONF_COLD_WHITE:
cg.add(
var.set_off_color_temperature(config[CONF_COLD_WHITE_COLOR_TEMPERATURE])
)
elif config[CONF_OFF_COLOR_TEMPERATURE] == CONF_WARM_WHITE:
cg.add(
var.set_off_color_temperature(config[CONF_WARM_WHITE_COLOR_TEMPERATURE])
)
cg.add(var.set_color_interlock(config[CONF_COLOR_INTERLOCK]))

View file

@ -19,6 +19,7 @@ class RGBCTLightOutput : public light::LightOutput {
void set_cold_white_temperature(float cold_white_temperature) { cold_white_temperature_ = cold_white_temperature; }
void set_warm_white_temperature(float warm_white_temperature) { warm_white_temperature_ = warm_white_temperature; }
void set_off_color_temperature(float off_color_temperature) { off_color_temperature_ = off_color_temperature; }
void set_color_interlock(bool color_interlock) { color_interlock_ = color_interlock; }
light::LightTraits get_traits() override {
@ -29,6 +30,7 @@ class RGBCTLightOutput : public light::LightOutput {
traits.set_supported_color_modes({light::ColorMode::RGB_COLOR_TEMPERATURE, light::ColorMode::COLOR_TEMPERATURE});
traits.set_min_mireds(this->cold_white_temperature_);
traits.set_max_mireds(this->warm_white_temperature_);
traits.set_off_mireds(this->off_color_temperature_);
return traits;
}
void write_state(light::LightState *state) override {
@ -51,6 +53,7 @@ class RGBCTLightOutput : public light::LightOutput {
output::FloatOutput *white_brightness_;
float cold_white_temperature_;
float warm_white_temperature_;
float off_color_temperature_{0};
bool color_interlock_{true};
};

View file

@ -516,6 +516,7 @@ CONF_NUM_SCANS = "num_scans"
CONF_NUMBER = "number"
CONF_NUMBER_DATAPOINT = "number_datapoint"
CONF_OE_PIN = "oe_pin"
CONF_OFF_COLOR_TEMPERATURE = "off_color_temperature"
CONF_OFF_MODE = "off_mode"
CONF_OFF_SPEED_CYCLE = "off_speed_cycle"
CONF_OFFSET = "offset"