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

View file

@ -7,6 +7,9 @@ from esphome.const import (
CONF_OUTPUT_ID, CONF_OUTPUT_ID,
CONF_COLD_WHITE_COLOR_TEMPERATURE, CONF_COLD_WHITE_COLOR_TEMPERATURE,
CONF_WARM_WHITE_COLOR_TEMPERATURE, CONF_WARM_WHITE_COLOR_TEMPERATURE,
CONF_OFF_COLOR_TEMPERATURE,
CONF_COLD_WHITE,
CONF_WARM_WHITE,
) )
CODEOWNERS = ["@jesserockz"] CODEOWNERS = ["@jesserockz"]
@ -22,6 +25,12 @@ CONFIG_SCHEMA = cv.All(
cv.Required(CONF_BRIGHTNESS): cv.use_id(output.FloatOutput), cv.Required(CONF_BRIGHTNESS): cv.use_id(output.FloatOutput),
cv.Required(CONF_COLD_WHITE_COLOR_TEMPERATURE): cv.color_temperature, cv.Required(CONF_COLD_WHITE_COLOR_TEMPERATURE): cv.color_temperature,
cv.Required(CONF_WARM_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, 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_cold_white_temperature(config[CONF_COLD_WHITE_COLOR_TEMPERATURE]))
cg.add(var.set_warm_white_temperature(config[CONF_WARM_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. /// 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, void as_rgbct(float color_temperature_cw, float color_temperature_ww, float color_temperature_off, float *red,
float *color_temperature, float *white_brightness, float gamma = 0) const { float *green, float *blue, float *color_temperature, float *white_brightness, float gamma = 0) const {
this->as_rgb(red, green, blue, gamma); 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. /// 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. /// 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, void as_ct(float color_temperature_cw, float color_temperature_ww, float color_temperature_off,
float gamma = 0) const { float *color_temperature, float *white_brightness, float gamma = 0) const {
const float white_level = this->color_mode_ & ColorCapability::RGB ? this->white_ : 1; 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) { 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); *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. } else { // Probably won't get here but put this here anyway.
*white_brightness = 0; *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. /// 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, void LightState::current_values_as_rgbct(float *red, float *green, float *blue, float *color_temperature,
float *white_brightness) { float *white_brightness) {
auto traits = this->get_traits(); auto traits = this->get_traits();
this->current_values.as_rgbct(traits.get_min_mireds(), traits.get_max_mireds(), red, green, blue, color_temperature, this->current_values.as_rgbct(traits.get_min_mireds(), traits.get_max_mireds(), traits.get_off_mireds(), red, green,
white_brightness, this->gamma_correct_); blue, color_temperature, white_brightness, this->gamma_correct_);
} }
void LightState::current_values_as_cwww(float *cold_white, float *warm_white, bool constant_brightness) { void LightState::current_values_as_cwww(float *cold_white, float *warm_white, bool constant_brightness) {
auto traits = this->get_traits(); 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) { void LightState::current_values_as_ct(float *color_temperature, float *white_brightness) {
auto traits = this->get_traits(); auto traits = this->get_traits();
this->current_values.as_ct(traits.get_min_mireds(), traits.get_max_mireds(), color_temperature, white_brightness, this->current_values.as_ct(traits.get_min_mireds(), traits.get_max_mireds(), traits.get_off_mireds(),
this->gamma_correct_); color_temperature, white_brightness, this->gamma_correct_);
} }
bool LightState::is_transformer_active() { return this->is_transformer_active_; } 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; } void set_min_mireds(float min_mireds) { this->min_mireds_ = min_mireds; }
float get_max_mireds() const { return this->max_mireds_; } float get_max_mireds() const { return this->max_mireds_; }
void set_max_mireds(float max_mireds) { this->max_mireds_ = 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: protected:
std::set<ColorMode> supported_color_modes_{}; std::set<ColorMode> supported_color_modes_{};
float min_mireds_{0}; float min_mireds_{0};
float max_mireds_{0}; float max_mireds_{0};
float off_mireds_{0};
}; };
} // namespace light } // namespace light

View file

@ -10,6 +10,9 @@ from esphome.const import (
CONF_OUTPUT_ID, CONF_OUTPUT_ID,
CONF_COLD_WHITE_COLOR_TEMPERATURE, CONF_COLD_WHITE_COLOR_TEMPERATURE,
CONF_WARM_WHITE_COLOR_TEMPERATURE, CONF_WARM_WHITE_COLOR_TEMPERATURE,
CONF_OFF_COLOR_TEMPERATURE,
CONF_COLD_WHITE,
CONF_WARM_WHITE,
) )
CODEOWNERS = ["@jesserockz"] CODEOWNERS = ["@jesserockz"]
@ -30,6 +33,12 @@ CONFIG_SCHEMA = cv.All(
cv.Required(CONF_WHITE_BRIGHTNESS): cv.use_id(output.FloatOutput), cv.Required(CONF_WHITE_BRIGHTNESS): cv.use_id(output.FloatOutput),
cv.Required(CONF_COLD_WHITE_COLOR_TEMPERATURE): cv.color_temperature, cv.Required(CONF_COLD_WHITE_COLOR_TEMPERATURE): cv.color_temperature,
cv.Required(CONF_WARM_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, 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_cold_white_temperature(config[CONF_COLD_WHITE_COLOR_TEMPERATURE]))
cg.add(var.set_warm_white_temperature(config[CONF_WARM_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])) 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_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_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; } void set_color_interlock(bool color_interlock) { color_interlock_ = color_interlock; }
light::LightTraits get_traits() override { 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_supported_color_modes({light::ColorMode::RGB_COLOR_TEMPERATURE, light::ColorMode::COLOR_TEMPERATURE});
traits.set_min_mireds(this->cold_white_temperature_); traits.set_min_mireds(this->cold_white_temperature_);
traits.set_max_mireds(this->warm_white_temperature_); traits.set_max_mireds(this->warm_white_temperature_);
traits.set_off_mireds(this->off_color_temperature_);
return traits; return traits;
} }
void write_state(light::LightState *state) override { void write_state(light::LightState *state) override {
@ -51,6 +53,7 @@ class RGBCTLightOutput : public light::LightOutput {
output::FloatOutput *white_brightness_; output::FloatOutput *white_brightness_;
float cold_white_temperature_; float cold_white_temperature_;
float warm_white_temperature_; float warm_white_temperature_;
float off_color_temperature_{0};
bool color_interlock_{true}; bool color_interlock_{true};
}; };

View file

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