mirror of
https://github.com/esphome/esphome.git
synced 2024-12-22 13:34:54 +01:00
Split files in light component (#1893)
This commit is contained in:
parent
9ad9d64ac7
commit
0efc1f06f2
14 changed files with 1334 additions and 1231 deletions
|
@ -6,158 +6,6 @@ namespace light {
|
|||
|
||||
static const char *const TAG = "light.addressable";
|
||||
|
||||
Color ESPHSVColor::to_rgb() const {
|
||||
// based on FastLED's hsv rainbow to rgb
|
||||
const uint8_t hue = this->hue;
|
||||
const uint8_t sat = this->saturation;
|
||||
const uint8_t val = this->value;
|
||||
// upper 3 hue bits are for branch selection, lower 5 are for values
|
||||
const uint8_t offset8 = (hue & 0x1F) << 3; // 0..248
|
||||
// third of the offset, 255/3 = 85 (actually only up to 82; 164)
|
||||
const uint8_t third = esp_scale8(offset8, 85);
|
||||
const uint8_t two_thirds = esp_scale8(offset8, 170);
|
||||
Color rgb(255, 255, 255, 0);
|
||||
switch (hue >> 5) {
|
||||
case 0b000:
|
||||
rgb.r = 255 - third;
|
||||
rgb.g = third;
|
||||
rgb.b = 0;
|
||||
break;
|
||||
case 0b001:
|
||||
rgb.r = 171;
|
||||
rgb.g = 85 + third;
|
||||
rgb.b = 0;
|
||||
break;
|
||||
case 0b010:
|
||||
rgb.r = 171 - two_thirds;
|
||||
rgb.g = 170 + third;
|
||||
rgb.b = 0;
|
||||
break;
|
||||
case 0b011:
|
||||
rgb.r = 0;
|
||||
rgb.g = 255 - third;
|
||||
rgb.b = third;
|
||||
break;
|
||||
case 0b100:
|
||||
rgb.r = 0;
|
||||
rgb.g = 171 - two_thirds;
|
||||
rgb.b = 85 + two_thirds;
|
||||
break;
|
||||
case 0b101:
|
||||
rgb.r = third;
|
||||
rgb.g = 0;
|
||||
rgb.b = 255 - third;
|
||||
break;
|
||||
case 0b110:
|
||||
rgb.r = 85 + third;
|
||||
rgb.g = 0;
|
||||
rgb.b = 171 - third;
|
||||
break;
|
||||
case 0b111:
|
||||
rgb.r = 170 + third;
|
||||
rgb.g = 0;
|
||||
rgb.b = 85 - third;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
// low saturation -> add uniform color to orig. hue
|
||||
// high saturation -> use hue directly
|
||||
// scales with square of saturation
|
||||
// (r,g,b) = (r,g,b) * sat + (1 - sat)^2
|
||||
rgb *= sat;
|
||||
const uint8_t desat = 255 - sat;
|
||||
rgb += esp_scale8(desat, desat);
|
||||
// (r,g,b) = (r,g,b) * val
|
||||
rgb *= val;
|
||||
return rgb;
|
||||
}
|
||||
|
||||
void ESPRangeView::set(const Color &color) {
|
||||
for (int32_t i = this->begin_; i < this->end_; i++) {
|
||||
(*this->parent_)[i] = color;
|
||||
}
|
||||
}
|
||||
ESPColorView ESPRangeView::operator[](int32_t index) const {
|
||||
index = interpret_index(index, this->size()) + this->begin_;
|
||||
return (*this->parent_)[index];
|
||||
}
|
||||
ESPRangeIterator ESPRangeView::begin() { return {*this, this->begin_}; }
|
||||
ESPRangeIterator ESPRangeView::end() { return {*this, this->end_}; }
|
||||
void ESPRangeView::set_red(uint8_t red) {
|
||||
for (auto c : *this)
|
||||
c.set_red(red);
|
||||
}
|
||||
void ESPRangeView::set_green(uint8_t green) {
|
||||
for (auto c : *this)
|
||||
c.set_green(green);
|
||||
}
|
||||
void ESPRangeView::set_blue(uint8_t blue) {
|
||||
for (auto c : *this)
|
||||
c.set_blue(blue);
|
||||
}
|
||||
void ESPRangeView::set_white(uint8_t white) {
|
||||
for (auto c : *this)
|
||||
c.set_white(white);
|
||||
}
|
||||
void ESPRangeView::set_effect_data(uint8_t effect_data) {
|
||||
for (auto c : *this)
|
||||
c.set_effect_data(effect_data);
|
||||
}
|
||||
void ESPRangeView::fade_to_white(uint8_t amnt) {
|
||||
for (auto c : *this)
|
||||
c.fade_to_white(amnt);
|
||||
}
|
||||
void ESPRangeView::fade_to_black(uint8_t amnt) {
|
||||
for (auto c : *this)
|
||||
c.fade_to_black(amnt);
|
||||
}
|
||||
void ESPRangeView::lighten(uint8_t delta) {
|
||||
for (auto c : *this)
|
||||
c.lighten(delta);
|
||||
}
|
||||
void ESPRangeView::darken(uint8_t delta) {
|
||||
for (auto c : *this)
|
||||
c.darken(delta);
|
||||
}
|
||||
ESPRangeView &ESPRangeView::operator=(const ESPRangeView &rhs) { // NOLINT
|
||||
// If size doesn't match, error (todo warning)
|
||||
if (rhs.size() != this->size())
|
||||
return *this;
|
||||
|
||||
if (this->parent_ != rhs.parent_) {
|
||||
for (int32_t i = 0; i < this->size(); i++)
|
||||
(*this)[i].set(rhs[i].get());
|
||||
return *this;
|
||||
}
|
||||
|
||||
// If both equal, already done
|
||||
if (rhs.begin_ == this->begin_)
|
||||
return *this;
|
||||
|
||||
if (rhs.begin_ > this->begin_) {
|
||||
// Copy from left
|
||||
for (int32_t i = 0; i < this->size(); i++) {
|
||||
(*this)[i].set(rhs[i].get());
|
||||
}
|
||||
} else {
|
||||
// Copy from right
|
||||
for (int32_t i = this->size() - 1; i >= 0; i--) {
|
||||
(*this)[i].set(rhs[i].get());
|
||||
}
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
ESPColorView ESPRangeIterator::operator*() const { return this->range_.parent_->get(this->i_); }
|
||||
|
||||
int32_t HOT interpret_index(int32_t index, int32_t size) {
|
||||
if (index < 0)
|
||||
return size + index;
|
||||
return index;
|
||||
}
|
||||
|
||||
void AddressableLight::call_setup() {
|
||||
this->setup();
|
||||
|
||||
|
@ -254,23 +102,5 @@ void AddressableLight::write_state(LightState *state) {
|
|||
this->schedule_show();
|
||||
}
|
||||
|
||||
void ESPColorCorrection::calculate_gamma_table(float gamma) {
|
||||
for (uint16_t i = 0; i < 256; i++) {
|
||||
// corrected = val ^ gamma
|
||||
auto corrected = static_cast<uint8_t>(roundf(255.0f * gamma_correct(i / 255.0f, gamma)));
|
||||
this->gamma_table_[i] = corrected;
|
||||
}
|
||||
if (gamma == 0.0f) {
|
||||
for (uint16_t i = 0; i < 256; i++)
|
||||
this->gamma_reverse_table_[i] = i;
|
||||
return;
|
||||
}
|
||||
for (uint16_t i = 0; i < 256; i++) {
|
||||
// val = corrected ^ (1/gamma)
|
||||
auto uncorrected = static_cast<uint8_t>(roundf(255.0f * powf(i / 255.0f, 1.0f / gamma)));
|
||||
this->gamma_reverse_table_[i] = uncorrected;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace light
|
||||
} // namespace esphome
|
||||
|
|
|
@ -3,6 +3,9 @@
|
|||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/defines.h"
|
||||
#include "esphome/core/color.h"
|
||||
#include "esp_color_correction.h"
|
||||
#include "esp_color_view.h"
|
||||
#include "esp_range_view.h"
|
||||
#include "light_output.h"
|
||||
#include "light_state.h"
|
||||
|
||||
|
@ -15,266 +18,6 @@ namespace light {
|
|||
|
||||
using ESPColor = Color;
|
||||
|
||||
struct ESPHSVColor {
|
||||
union {
|
||||
struct {
|
||||
union {
|
||||
uint8_t hue;
|
||||
uint8_t h;
|
||||
};
|
||||
union {
|
||||
uint8_t saturation;
|
||||
uint8_t s;
|
||||
};
|
||||
union {
|
||||
uint8_t value;
|
||||
uint8_t v;
|
||||
};
|
||||
};
|
||||
uint8_t raw[3];
|
||||
};
|
||||
inline ESPHSVColor() ALWAYS_INLINE : h(0), s(0), v(0) { // NOLINT
|
||||
}
|
||||
inline ESPHSVColor(uint8_t hue, uint8_t saturation, uint8_t value) ALWAYS_INLINE : hue(hue),
|
||||
saturation(saturation),
|
||||
value(value) {}
|
||||
Color to_rgb() const;
|
||||
};
|
||||
|
||||
class ESPColorCorrection {
|
||||
public:
|
||||
ESPColorCorrection() : max_brightness_(255, 255, 255, 255) {}
|
||||
void set_max_brightness(const Color &max_brightness) { this->max_brightness_ = max_brightness; }
|
||||
void set_local_brightness(uint8_t local_brightness) { this->local_brightness_ = local_brightness; }
|
||||
void calculate_gamma_table(float gamma);
|
||||
inline Color color_correct(Color color) const ALWAYS_INLINE {
|
||||
// corrected = (uncorrected * max_brightness * local_brightness) ^ gamma
|
||||
return Color(this->color_correct_red(color.red), this->color_correct_green(color.green),
|
||||
this->color_correct_blue(color.blue), this->color_correct_white(color.white));
|
||||
}
|
||||
inline uint8_t color_correct_red(uint8_t red) const ALWAYS_INLINE {
|
||||
uint8_t res = esp_scale8(esp_scale8(red, this->max_brightness_.red), this->local_brightness_);
|
||||
return this->gamma_table_[res];
|
||||
}
|
||||
inline uint8_t color_correct_green(uint8_t green) const ALWAYS_INLINE {
|
||||
uint8_t res = esp_scale8(esp_scale8(green, this->max_brightness_.green), this->local_brightness_);
|
||||
return this->gamma_table_[res];
|
||||
}
|
||||
inline uint8_t color_correct_blue(uint8_t blue) const ALWAYS_INLINE {
|
||||
uint8_t res = esp_scale8(esp_scale8(blue, this->max_brightness_.blue), this->local_brightness_);
|
||||
return this->gamma_table_[res];
|
||||
}
|
||||
inline uint8_t color_correct_white(uint8_t white) const ALWAYS_INLINE {
|
||||
// do not scale white value with brightness
|
||||
uint8_t res = esp_scale8(white, this->max_brightness_.white);
|
||||
return this->gamma_table_[res];
|
||||
}
|
||||
inline Color color_uncorrect(Color color) const ALWAYS_INLINE {
|
||||
// uncorrected = corrected^(1/gamma) / (max_brightness * local_brightness)
|
||||
return Color(this->color_uncorrect_red(color.red), this->color_uncorrect_green(color.green),
|
||||
this->color_uncorrect_blue(color.blue), this->color_uncorrect_white(color.white));
|
||||
}
|
||||
inline uint8_t color_uncorrect_red(uint8_t red) const ALWAYS_INLINE {
|
||||
if (this->max_brightness_.red == 0 || this->local_brightness_ == 0)
|
||||
return 0;
|
||||
uint16_t uncorrected = this->gamma_reverse_table_[red] * 255UL;
|
||||
uint8_t res = ((uncorrected / this->max_brightness_.red) * 255UL) / this->local_brightness_;
|
||||
return res;
|
||||
}
|
||||
inline uint8_t color_uncorrect_green(uint8_t green) const ALWAYS_INLINE {
|
||||
if (this->max_brightness_.green == 0 || this->local_brightness_ == 0)
|
||||
return 0;
|
||||
uint16_t uncorrected = this->gamma_reverse_table_[green] * 255UL;
|
||||
uint8_t res = ((uncorrected / this->max_brightness_.green) * 255UL) / this->local_brightness_;
|
||||
return res;
|
||||
}
|
||||
inline uint8_t color_uncorrect_blue(uint8_t blue) const ALWAYS_INLINE {
|
||||
if (this->max_brightness_.blue == 0 || this->local_brightness_ == 0)
|
||||
return 0;
|
||||
uint16_t uncorrected = this->gamma_reverse_table_[blue] * 255UL;
|
||||
uint8_t res = ((uncorrected / this->max_brightness_.blue) * 255UL) / this->local_brightness_;
|
||||
return res;
|
||||
}
|
||||
inline uint8_t color_uncorrect_white(uint8_t white) const ALWAYS_INLINE {
|
||||
if (this->max_brightness_.white == 0)
|
||||
return 0;
|
||||
uint16_t uncorrected = this->gamma_reverse_table_[white] * 255UL;
|
||||
uint8_t res = uncorrected / this->max_brightness_.white;
|
||||
return res;
|
||||
}
|
||||
|
||||
protected:
|
||||
uint8_t gamma_table_[256];
|
||||
uint8_t gamma_reverse_table_[256];
|
||||
Color max_brightness_;
|
||||
uint8_t local_brightness_{255};
|
||||
};
|
||||
|
||||
class ESPColorSettable {
|
||||
public:
|
||||
virtual void set(const Color &color) = 0;
|
||||
virtual void set_red(uint8_t red) = 0;
|
||||
virtual void set_green(uint8_t green) = 0;
|
||||
virtual void set_blue(uint8_t blue) = 0;
|
||||
virtual void set_white(uint8_t white) = 0;
|
||||
virtual void set_effect_data(uint8_t effect_data) = 0;
|
||||
virtual void fade_to_white(uint8_t amnt) = 0;
|
||||
virtual void fade_to_black(uint8_t amnt) = 0;
|
||||
virtual void lighten(uint8_t delta) = 0;
|
||||
virtual void darken(uint8_t delta) = 0;
|
||||
void set(const ESPHSVColor &color) { this->set_hsv(color); }
|
||||
void set_hsv(const ESPHSVColor &color) {
|
||||
Color rgb = color.to_rgb();
|
||||
this->set_rgb(rgb.r, rgb.g, rgb.b);
|
||||
}
|
||||
void set_rgb(uint8_t red, uint8_t green, uint8_t blue) {
|
||||
this->set_red(red);
|
||||
this->set_green(green);
|
||||
this->set_blue(blue);
|
||||
}
|
||||
void set_rgbw(uint8_t red, uint8_t green, uint8_t blue, uint8_t white) {
|
||||
this->set_rgb(red, green, blue);
|
||||
this->set_white(white);
|
||||
}
|
||||
};
|
||||
|
||||
class ESPColorView : public ESPColorSettable {
|
||||
public:
|
||||
ESPColorView(uint8_t *red, uint8_t *green, uint8_t *blue, uint8_t *white, uint8_t *effect_data,
|
||||
const ESPColorCorrection *color_correction)
|
||||
: red_(red),
|
||||
green_(green),
|
||||
blue_(blue),
|
||||
white_(white),
|
||||
effect_data_(effect_data),
|
||||
color_correction_(color_correction) {}
|
||||
ESPColorView &operator=(const Color &rhs) {
|
||||
this->set(rhs);
|
||||
return *this;
|
||||
}
|
||||
ESPColorView &operator=(const ESPHSVColor &rhs) {
|
||||
this->set_hsv(rhs);
|
||||
return *this;
|
||||
}
|
||||
void set(const Color &color) override { this->set_rgbw(color.r, color.g, color.b, color.w); }
|
||||
void set_red(uint8_t red) override { *this->red_ = this->color_correction_->color_correct_red(red); }
|
||||
void set_green(uint8_t green) override { *this->green_ = this->color_correction_->color_correct_green(green); }
|
||||
void set_blue(uint8_t blue) override { *this->blue_ = this->color_correction_->color_correct_blue(blue); }
|
||||
void set_white(uint8_t white) override {
|
||||
if (this->white_ == nullptr)
|
||||
return;
|
||||
*this->white_ = this->color_correction_->color_correct_white(white);
|
||||
}
|
||||
void set_effect_data(uint8_t effect_data) override {
|
||||
if (this->effect_data_ == nullptr)
|
||||
return;
|
||||
*this->effect_data_ = effect_data;
|
||||
}
|
||||
void fade_to_white(uint8_t amnt) override { this->set(this->get().fade_to_white(amnt)); }
|
||||
void fade_to_black(uint8_t amnt) override { this->set(this->get().fade_to_black(amnt)); }
|
||||
void lighten(uint8_t delta) override { this->set(this->get().lighten(delta)); }
|
||||
void darken(uint8_t delta) override { this->set(this->get().darken(delta)); }
|
||||
Color get() const { return Color(this->get_red(), this->get_green(), this->get_blue(), this->get_white()); }
|
||||
uint8_t get_red() const { return this->color_correction_->color_uncorrect_red(*this->red_); }
|
||||
uint8_t get_red_raw() const { return *this->red_; }
|
||||
uint8_t get_green() const { return this->color_correction_->color_uncorrect_green(*this->green_); }
|
||||
uint8_t get_green_raw() const { return *this->green_; }
|
||||
uint8_t get_blue() const { return this->color_correction_->color_uncorrect_blue(*this->blue_); }
|
||||
uint8_t get_blue_raw() const { return *this->blue_; }
|
||||
uint8_t get_white() const {
|
||||
if (this->white_ == nullptr)
|
||||
return 0;
|
||||
return this->color_correction_->color_uncorrect_white(*this->white_);
|
||||
}
|
||||
uint8_t get_white_raw() const {
|
||||
if (this->white_ == nullptr)
|
||||
return 0;
|
||||
return *this->white_;
|
||||
}
|
||||
uint8_t get_effect_data() const {
|
||||
if (this->effect_data_ == nullptr)
|
||||
return 0;
|
||||
return *this->effect_data_;
|
||||
}
|
||||
void raw_set_color_correction(const ESPColorCorrection *color_correction) {
|
||||
this->color_correction_ = color_correction;
|
||||
}
|
||||
|
||||
protected:
|
||||
uint8_t *const red_;
|
||||
uint8_t *const green_;
|
||||
uint8_t *const blue_;
|
||||
uint8_t *const white_;
|
||||
uint8_t *const effect_data_;
|
||||
const ESPColorCorrection *color_correction_;
|
||||
};
|
||||
|
||||
class AddressableLight;
|
||||
|
||||
int32_t interpret_index(int32_t index, int32_t size);
|
||||
|
||||
class ESPRangeIterator;
|
||||
|
||||
class ESPRangeView : public ESPColorSettable {
|
||||
public:
|
||||
ESPRangeView(AddressableLight *parent, int32_t begin, int32_t an_end) : parent_(parent), begin_(begin), end_(an_end) {
|
||||
if (this->end_ < this->begin_) {
|
||||
this->end_ = this->begin_;
|
||||
}
|
||||
}
|
||||
|
||||
ESPColorView operator[](int32_t index) const;
|
||||
ESPRangeIterator begin();
|
||||
ESPRangeIterator end();
|
||||
|
||||
void set(const Color &color) override;
|
||||
ESPRangeView &operator=(const Color &rhs) {
|
||||
this->set(rhs);
|
||||
return *this;
|
||||
}
|
||||
ESPRangeView &operator=(const ESPColorView &rhs) {
|
||||
this->set(rhs.get());
|
||||
return *this;
|
||||
}
|
||||
ESPRangeView &operator=(const ESPHSVColor &rhs) {
|
||||
this->set_hsv(rhs);
|
||||
return *this;
|
||||
}
|
||||
ESPRangeView &operator=(const ESPRangeView &rhs);
|
||||
void set_red(uint8_t red) override;
|
||||
void set_green(uint8_t green) override;
|
||||
void set_blue(uint8_t blue) override;
|
||||
void set_white(uint8_t white) override;
|
||||
void set_effect_data(uint8_t effect_data) override;
|
||||
void fade_to_white(uint8_t amnt) override;
|
||||
void fade_to_black(uint8_t amnt) override;
|
||||
void lighten(uint8_t delta) override;
|
||||
void darken(uint8_t delta) override;
|
||||
int32_t size() const { return this->end_ - this->begin_; }
|
||||
|
||||
protected:
|
||||
friend ESPRangeIterator;
|
||||
|
||||
AddressableLight *parent_;
|
||||
int32_t begin_;
|
||||
int32_t end_;
|
||||
};
|
||||
|
||||
class ESPRangeIterator {
|
||||
public:
|
||||
ESPRangeIterator(const ESPRangeView &range, int32_t i) : range_(range), i_(i) {}
|
||||
ESPRangeIterator operator++() {
|
||||
this->i_++;
|
||||
return *this;
|
||||
}
|
||||
bool operator!=(const ESPRangeIterator &other) const { return this->i_ != other.i_; }
|
||||
ESPColorView operator*() const;
|
||||
|
||||
protected:
|
||||
ESPRangeView range_;
|
||||
int32_t i_;
|
||||
};
|
||||
|
||||
class AddressableLight : public LightOutput, public Component {
|
||||
public:
|
||||
virtual int32_t size() const = 0;
|
||||
|
|
26
esphome/components/light/esp_color_correction.cpp
Normal file
26
esphome/components/light/esp_color_correction.cpp
Normal file
|
@ -0,0 +1,26 @@
|
|||
#include "esp_color_correction.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace light {
|
||||
|
||||
void ESPColorCorrection::calculate_gamma_table(float gamma) {
|
||||
for (uint16_t i = 0; i < 256; i++) {
|
||||
// corrected = val ^ gamma
|
||||
auto corrected = static_cast<uint8_t>(roundf(255.0f * gamma_correct(i / 255.0f, gamma)));
|
||||
this->gamma_table_[i] = corrected;
|
||||
}
|
||||
if (gamma == 0.0f) {
|
||||
for (uint16_t i = 0; i < 256; i++)
|
||||
this->gamma_reverse_table_[i] = i;
|
||||
return;
|
||||
}
|
||||
for (uint16_t i = 0; i < 256; i++) {
|
||||
// val = corrected ^ (1/gamma)
|
||||
auto uncorrected = static_cast<uint8_t>(roundf(255.0f * powf(i / 255.0f, 1.0f / gamma)));
|
||||
this->gamma_reverse_table_[i] = uncorrected;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace light
|
||||
} // namespace esphome
|
78
esphome/components/light/esp_color_correction.h
Normal file
78
esphome/components/light/esp_color_correction.h
Normal file
|
@ -0,0 +1,78 @@
|
|||
#pragma once
|
||||
|
||||
#include "esphome/core/color.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace light {
|
||||
|
||||
class ESPColorCorrection {
|
||||
public:
|
||||
ESPColorCorrection() : max_brightness_(255, 255, 255, 255) {}
|
||||
void set_max_brightness(const Color &max_brightness) { this->max_brightness_ = max_brightness; }
|
||||
void set_local_brightness(uint8_t local_brightness) { this->local_brightness_ = local_brightness; }
|
||||
void calculate_gamma_table(float gamma);
|
||||
inline Color color_correct(Color color) const ALWAYS_INLINE {
|
||||
// corrected = (uncorrected * max_brightness * local_brightness) ^ gamma
|
||||
return Color(this->color_correct_red(color.red), this->color_correct_green(color.green),
|
||||
this->color_correct_blue(color.blue), this->color_correct_white(color.white));
|
||||
}
|
||||
inline uint8_t color_correct_red(uint8_t red) const ALWAYS_INLINE {
|
||||
uint8_t res = esp_scale8(esp_scale8(red, this->max_brightness_.red), this->local_brightness_);
|
||||
return this->gamma_table_[res];
|
||||
}
|
||||
inline uint8_t color_correct_green(uint8_t green) const ALWAYS_INLINE {
|
||||
uint8_t res = esp_scale8(esp_scale8(green, this->max_brightness_.green), this->local_brightness_);
|
||||
return this->gamma_table_[res];
|
||||
}
|
||||
inline uint8_t color_correct_blue(uint8_t blue) const ALWAYS_INLINE {
|
||||
uint8_t res = esp_scale8(esp_scale8(blue, this->max_brightness_.blue), this->local_brightness_);
|
||||
return this->gamma_table_[res];
|
||||
}
|
||||
inline uint8_t color_correct_white(uint8_t white) const ALWAYS_INLINE {
|
||||
// do not scale white value with brightness
|
||||
uint8_t res = esp_scale8(white, this->max_brightness_.white);
|
||||
return this->gamma_table_[res];
|
||||
}
|
||||
inline Color color_uncorrect(Color color) const ALWAYS_INLINE {
|
||||
// uncorrected = corrected^(1/gamma) / (max_brightness * local_brightness)
|
||||
return Color(this->color_uncorrect_red(color.red), this->color_uncorrect_green(color.green),
|
||||
this->color_uncorrect_blue(color.blue), this->color_uncorrect_white(color.white));
|
||||
}
|
||||
inline uint8_t color_uncorrect_red(uint8_t red) const ALWAYS_INLINE {
|
||||
if (this->max_brightness_.red == 0 || this->local_brightness_ == 0)
|
||||
return 0;
|
||||
uint16_t uncorrected = this->gamma_reverse_table_[red] * 255UL;
|
||||
uint8_t res = ((uncorrected / this->max_brightness_.red) * 255UL) / this->local_brightness_;
|
||||
return res;
|
||||
}
|
||||
inline uint8_t color_uncorrect_green(uint8_t green) const ALWAYS_INLINE {
|
||||
if (this->max_brightness_.green == 0 || this->local_brightness_ == 0)
|
||||
return 0;
|
||||
uint16_t uncorrected = this->gamma_reverse_table_[green] * 255UL;
|
||||
uint8_t res = ((uncorrected / this->max_brightness_.green) * 255UL) / this->local_brightness_;
|
||||
return res;
|
||||
}
|
||||
inline uint8_t color_uncorrect_blue(uint8_t blue) const ALWAYS_INLINE {
|
||||
if (this->max_brightness_.blue == 0 || this->local_brightness_ == 0)
|
||||
return 0;
|
||||
uint16_t uncorrected = this->gamma_reverse_table_[blue] * 255UL;
|
||||
uint8_t res = ((uncorrected / this->max_brightness_.blue) * 255UL) / this->local_brightness_;
|
||||
return res;
|
||||
}
|
||||
inline uint8_t color_uncorrect_white(uint8_t white) const ALWAYS_INLINE {
|
||||
if (this->max_brightness_.white == 0)
|
||||
return 0;
|
||||
uint16_t uncorrected = this->gamma_reverse_table_[white] * 255UL;
|
||||
uint8_t res = uncorrected / this->max_brightness_.white;
|
||||
return res;
|
||||
}
|
||||
|
||||
protected:
|
||||
uint8_t gamma_table_[256];
|
||||
uint8_t gamma_reverse_table_[256];
|
||||
Color max_brightness_;
|
||||
uint8_t local_brightness_{255};
|
||||
};
|
||||
|
||||
} // namespace light
|
||||
} // namespace esphome
|
110
esphome/components/light/esp_color_view.h
Normal file
110
esphome/components/light/esp_color_view.h
Normal file
|
@ -0,0 +1,110 @@
|
|||
#pragma once
|
||||
|
||||
#include "esphome/core/color.h"
|
||||
#include "esp_hsv_color.h"
|
||||
#include "esp_color_correction.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace light {
|
||||
|
||||
class ESPColorSettable {
|
||||
public:
|
||||
virtual void set(const Color &color) = 0;
|
||||
virtual void set_red(uint8_t red) = 0;
|
||||
virtual void set_green(uint8_t green) = 0;
|
||||
virtual void set_blue(uint8_t blue) = 0;
|
||||
virtual void set_white(uint8_t white) = 0;
|
||||
virtual void set_effect_data(uint8_t effect_data) = 0;
|
||||
virtual void fade_to_white(uint8_t amnt) = 0;
|
||||
virtual void fade_to_black(uint8_t amnt) = 0;
|
||||
virtual void lighten(uint8_t delta) = 0;
|
||||
virtual void darken(uint8_t delta) = 0;
|
||||
void set(const ESPHSVColor &color) { this->set_hsv(color); }
|
||||
void set_hsv(const ESPHSVColor &color) {
|
||||
Color rgb = color.to_rgb();
|
||||
this->set_rgb(rgb.r, rgb.g, rgb.b);
|
||||
}
|
||||
void set_rgb(uint8_t red, uint8_t green, uint8_t blue) {
|
||||
this->set_red(red);
|
||||
this->set_green(green);
|
||||
this->set_blue(blue);
|
||||
}
|
||||
void set_rgbw(uint8_t red, uint8_t green, uint8_t blue, uint8_t white) {
|
||||
this->set_rgb(red, green, blue);
|
||||
this->set_white(white);
|
||||
}
|
||||
};
|
||||
|
||||
class ESPColorView : public ESPColorSettable {
|
||||
public:
|
||||
ESPColorView(uint8_t *red, uint8_t *green, uint8_t *blue, uint8_t *white, uint8_t *effect_data,
|
||||
const ESPColorCorrection *color_correction)
|
||||
: red_(red),
|
||||
green_(green),
|
||||
blue_(blue),
|
||||
white_(white),
|
||||
effect_data_(effect_data),
|
||||
color_correction_(color_correction) {}
|
||||
ESPColorView &operator=(const Color &rhs) {
|
||||
this->set(rhs);
|
||||
return *this;
|
||||
}
|
||||
ESPColorView &operator=(const ESPHSVColor &rhs) {
|
||||
this->set_hsv(rhs);
|
||||
return *this;
|
||||
}
|
||||
void set(const Color &color) override { this->set_rgbw(color.r, color.g, color.b, color.w); }
|
||||
void set_red(uint8_t red) override { *this->red_ = this->color_correction_->color_correct_red(red); }
|
||||
void set_green(uint8_t green) override { *this->green_ = this->color_correction_->color_correct_green(green); }
|
||||
void set_blue(uint8_t blue) override { *this->blue_ = this->color_correction_->color_correct_blue(blue); }
|
||||
void set_white(uint8_t white) override {
|
||||
if (this->white_ == nullptr)
|
||||
return;
|
||||
*this->white_ = this->color_correction_->color_correct_white(white);
|
||||
}
|
||||
void set_effect_data(uint8_t effect_data) override {
|
||||
if (this->effect_data_ == nullptr)
|
||||
return;
|
||||
*this->effect_data_ = effect_data;
|
||||
}
|
||||
void fade_to_white(uint8_t amnt) override { this->set(this->get().fade_to_white(amnt)); }
|
||||
void fade_to_black(uint8_t amnt) override { this->set(this->get().fade_to_black(amnt)); }
|
||||
void lighten(uint8_t delta) override { this->set(this->get().lighten(delta)); }
|
||||
void darken(uint8_t delta) override { this->set(this->get().darken(delta)); }
|
||||
Color get() const { return Color(this->get_red(), this->get_green(), this->get_blue(), this->get_white()); }
|
||||
uint8_t get_red() const { return this->color_correction_->color_uncorrect_red(*this->red_); }
|
||||
uint8_t get_red_raw() const { return *this->red_; }
|
||||
uint8_t get_green() const { return this->color_correction_->color_uncorrect_green(*this->green_); }
|
||||
uint8_t get_green_raw() const { return *this->green_; }
|
||||
uint8_t get_blue() const { return this->color_correction_->color_uncorrect_blue(*this->blue_); }
|
||||
uint8_t get_blue_raw() const { return *this->blue_; }
|
||||
uint8_t get_white() const {
|
||||
if (this->white_ == nullptr)
|
||||
return 0;
|
||||
return this->color_correction_->color_uncorrect_white(*this->white_);
|
||||
}
|
||||
uint8_t get_white_raw() const {
|
||||
if (this->white_ == nullptr)
|
||||
return 0;
|
||||
return *this->white_;
|
||||
}
|
||||
uint8_t get_effect_data() const {
|
||||
if (this->effect_data_ == nullptr)
|
||||
return 0;
|
||||
return *this->effect_data_;
|
||||
}
|
||||
void raw_set_color_correction(const ESPColorCorrection *color_correction) {
|
||||
this->color_correction_ = color_correction;
|
||||
}
|
||||
|
||||
protected:
|
||||
uint8_t *const red_;
|
||||
uint8_t *const green_;
|
||||
uint8_t *const blue_;
|
||||
uint8_t *const white_;
|
||||
uint8_t *const effect_data_;
|
||||
const ESPColorCorrection *color_correction_;
|
||||
};
|
||||
|
||||
} // namespace light
|
||||
} // namespace esphome
|
74
esphome/components/light/esp_hsv_color.cpp
Normal file
74
esphome/components/light/esp_hsv_color.cpp
Normal file
|
@ -0,0 +1,74 @@
|
|||
#include "esp_hsv_color.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace light {
|
||||
|
||||
Color ESPHSVColor::to_rgb() const {
|
||||
// based on FastLED's hsv rainbow to rgb
|
||||
const uint8_t hue = this->hue;
|
||||
const uint8_t sat = this->saturation;
|
||||
const uint8_t val = this->value;
|
||||
// upper 3 hue bits are for branch selection, lower 5 are for values
|
||||
const uint8_t offset8 = (hue & 0x1F) << 3; // 0..248
|
||||
// third of the offset, 255/3 = 85 (actually only up to 82; 164)
|
||||
const uint8_t third = esp_scale8(offset8, 85);
|
||||
const uint8_t two_thirds = esp_scale8(offset8, 170);
|
||||
Color rgb(255, 255, 255, 0);
|
||||
switch (hue >> 5) {
|
||||
case 0b000:
|
||||
rgb.r = 255 - third;
|
||||
rgb.g = third;
|
||||
rgb.b = 0;
|
||||
break;
|
||||
case 0b001:
|
||||
rgb.r = 171;
|
||||
rgb.g = 85 + third;
|
||||
rgb.b = 0;
|
||||
break;
|
||||
case 0b010:
|
||||
rgb.r = 171 - two_thirds;
|
||||
rgb.g = 170 + third;
|
||||
rgb.b = 0;
|
||||
break;
|
||||
case 0b011:
|
||||
rgb.r = 0;
|
||||
rgb.g = 255 - third;
|
||||
rgb.b = third;
|
||||
break;
|
||||
case 0b100:
|
||||
rgb.r = 0;
|
||||
rgb.g = 171 - two_thirds;
|
||||
rgb.b = 85 + two_thirds;
|
||||
break;
|
||||
case 0b101:
|
||||
rgb.r = third;
|
||||
rgb.g = 0;
|
||||
rgb.b = 255 - third;
|
||||
break;
|
||||
case 0b110:
|
||||
rgb.r = 85 + third;
|
||||
rgb.g = 0;
|
||||
rgb.b = 171 - third;
|
||||
break;
|
||||
case 0b111:
|
||||
rgb.r = 170 + third;
|
||||
rgb.g = 0;
|
||||
rgb.b = 85 - third;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
// low saturation -> add uniform color to orig. hue
|
||||
// high saturation -> use hue directly
|
||||
// scales with square of saturation
|
||||
// (r,g,b) = (r,g,b) * sat + (1 - sat)^2
|
||||
rgb *= sat;
|
||||
const uint8_t desat = 255 - sat;
|
||||
rgb += esp_scale8(desat, desat);
|
||||
// (r,g,b) = (r,g,b) * val
|
||||
rgb *= val;
|
||||
return rgb;
|
||||
}
|
||||
|
||||
} // namespace light
|
||||
} // namespace esphome
|
36
esphome/components/light/esp_hsv_color.h
Normal file
36
esphome/components/light/esp_hsv_color.h
Normal file
|
@ -0,0 +1,36 @@
|
|||
#pragma once
|
||||
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/color.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace light {
|
||||
|
||||
struct ESPHSVColor {
|
||||
union {
|
||||
struct {
|
||||
union {
|
||||
uint8_t hue;
|
||||
uint8_t h;
|
||||
};
|
||||
union {
|
||||
uint8_t saturation;
|
||||
uint8_t s;
|
||||
};
|
||||
union {
|
||||
uint8_t value;
|
||||
uint8_t v;
|
||||
};
|
||||
};
|
||||
uint8_t raw[3];
|
||||
};
|
||||
inline ESPHSVColor() ALWAYS_INLINE : h(0), s(0), v(0) { // NOLINT
|
||||
}
|
||||
inline ESPHSVColor(uint8_t hue, uint8_t saturation, uint8_t value) ALWAYS_INLINE : hue(hue),
|
||||
saturation(saturation),
|
||||
value(value) {}
|
||||
Color to_rgb() const;
|
||||
};
|
||||
|
||||
} // namespace light
|
||||
} // namespace esphome
|
96
esphome/components/light/esp_range_view.cpp
Normal file
96
esphome/components/light/esp_range_view.cpp
Normal file
|
@ -0,0 +1,96 @@
|
|||
#include "esp_range_view.h"
|
||||
#include "addressable_light.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace light {
|
||||
|
||||
int32_t HOT interpret_index(int32_t index, int32_t size) {
|
||||
if (index < 0)
|
||||
return size + index;
|
||||
return index;
|
||||
}
|
||||
|
||||
ESPColorView ESPRangeView::operator[](int32_t index) const {
|
||||
index = interpret_index(index, this->size()) + this->begin_;
|
||||
return (*this->parent_)[index];
|
||||
}
|
||||
ESPRangeIterator ESPRangeView::begin() { return {*this, this->begin_}; }
|
||||
ESPRangeIterator ESPRangeView::end() { return {*this, this->end_}; }
|
||||
|
||||
void ESPRangeView::set(const Color &color) {
|
||||
for (int32_t i = this->begin_; i < this->end_; i++) {
|
||||
(*this->parent_)[i] = color;
|
||||
}
|
||||
}
|
||||
|
||||
void ESPRangeView::set_red(uint8_t red) {
|
||||
for (auto c : *this)
|
||||
c.set_red(red);
|
||||
}
|
||||
void ESPRangeView::set_green(uint8_t green) {
|
||||
for (auto c : *this)
|
||||
c.set_green(green);
|
||||
}
|
||||
void ESPRangeView::set_blue(uint8_t blue) {
|
||||
for (auto c : *this)
|
||||
c.set_blue(blue);
|
||||
}
|
||||
void ESPRangeView::set_white(uint8_t white) {
|
||||
for (auto c : *this)
|
||||
c.set_white(white);
|
||||
}
|
||||
void ESPRangeView::set_effect_data(uint8_t effect_data) {
|
||||
for (auto c : *this)
|
||||
c.set_effect_data(effect_data);
|
||||
}
|
||||
|
||||
void ESPRangeView::fade_to_white(uint8_t amnt) {
|
||||
for (auto c : *this)
|
||||
c.fade_to_white(amnt);
|
||||
}
|
||||
void ESPRangeView::fade_to_black(uint8_t amnt) {
|
||||
for (auto c : *this)
|
||||
c.fade_to_black(amnt);
|
||||
}
|
||||
void ESPRangeView::lighten(uint8_t delta) {
|
||||
for (auto c : *this)
|
||||
c.lighten(delta);
|
||||
}
|
||||
void ESPRangeView::darken(uint8_t delta) {
|
||||
for (auto c : *this)
|
||||
c.darken(delta);
|
||||
}
|
||||
ESPRangeView &ESPRangeView::operator=(const ESPRangeView &rhs) { // NOLINT
|
||||
// If size doesn't match, error (todo warning)
|
||||
if (rhs.size() != this->size())
|
||||
return *this;
|
||||
|
||||
if (this->parent_ != rhs.parent_) {
|
||||
for (int32_t i = 0; i < this->size(); i++)
|
||||
(*this)[i].set(rhs[i].get());
|
||||
return *this;
|
||||
}
|
||||
|
||||
// If both equal, already done
|
||||
if (rhs.begin_ == this->begin_)
|
||||
return *this;
|
||||
|
||||
if (rhs.begin_ > this->begin_) {
|
||||
// Copy from left
|
||||
for (int32_t i = 0; i < this->size(); i++) {
|
||||
(*this)[i].set(rhs[i].get());
|
||||
}
|
||||
} else {
|
||||
// Copy from right
|
||||
for (int32_t i = this->size() - 1; i >= 0; i--) {
|
||||
(*this)[i].set(rhs[i].get());
|
||||
}
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
ESPColorView ESPRangeIterator::operator*() const { return this->range_.parent_->get(this->i_); }
|
||||
|
||||
} // namespace light
|
||||
} // namespace esphome
|
75
esphome/components/light/esp_range_view.h
Normal file
75
esphome/components/light/esp_range_view.h
Normal file
|
@ -0,0 +1,75 @@
|
|||
#pragma once
|
||||
|
||||
#include "esp_color_view.h"
|
||||
#include "esp_hsv_color.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace light {
|
||||
|
||||
int32_t interpret_index(int32_t index, int32_t size);
|
||||
|
||||
class AddressableLight;
|
||||
class ESPRangeIterator;
|
||||
|
||||
class ESPRangeView : public ESPColorSettable {
|
||||
public:
|
||||
ESPRangeView(AddressableLight *parent, int32_t begin, int32_t end)
|
||||
: parent_(parent), begin_(begin), end_(end < begin ? begin : end) {}
|
||||
|
||||
int32_t size() const { return this->end_ - this->begin_; }
|
||||
ESPColorView operator[](int32_t index) const;
|
||||
ESPRangeIterator begin();
|
||||
ESPRangeIterator end();
|
||||
|
||||
void set(const Color &color) override;
|
||||
void set(const ESPHSVColor &color) { this->set(color.to_rgb()); }
|
||||
void set_red(uint8_t red) override;
|
||||
void set_green(uint8_t green) override;
|
||||
void set_blue(uint8_t blue) override;
|
||||
void set_white(uint8_t white) override;
|
||||
void set_effect_data(uint8_t effect_data) override;
|
||||
|
||||
void fade_to_white(uint8_t amnt) override;
|
||||
void fade_to_black(uint8_t amnt) override;
|
||||
void lighten(uint8_t delta) override;
|
||||
void darken(uint8_t delta) override;
|
||||
|
||||
ESPRangeView &operator=(const Color &rhs) {
|
||||
this->set(rhs);
|
||||
return *this;
|
||||
}
|
||||
ESPRangeView &operator=(const ESPColorView &rhs) {
|
||||
this->set(rhs.get());
|
||||
return *this;
|
||||
}
|
||||
ESPRangeView &operator=(const ESPHSVColor &rhs) {
|
||||
this->set_hsv(rhs);
|
||||
return *this;
|
||||
}
|
||||
ESPRangeView &operator=(const ESPRangeView &rhs);
|
||||
|
||||
protected:
|
||||
friend ESPRangeIterator;
|
||||
|
||||
AddressableLight *parent_;
|
||||
int32_t begin_;
|
||||
int32_t end_;
|
||||
};
|
||||
|
||||
class ESPRangeIterator {
|
||||
public:
|
||||
ESPRangeIterator(const ESPRangeView &range, int32_t i) : range_(range), i_(i) {}
|
||||
ESPRangeIterator operator++() {
|
||||
this->i_++;
|
||||
return *this;
|
||||
}
|
||||
bool operator!=(const ESPRangeIterator &other) const { return this->i_ != other.i_; }
|
||||
ESPColorView operator*() const;
|
||||
|
||||
protected:
|
||||
ESPRangeView range_;
|
||||
int32_t i_;
|
||||
};
|
||||
|
||||
} // namespace light
|
||||
} // namespace esphome
|
522
esphome/components/light/light_call.cpp
Normal file
522
esphome/components/light/light_call.cpp
Normal file
|
@ -0,0 +1,522 @@
|
|||
#include "light_call.h"
|
||||
#include "light_state.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace light {
|
||||
|
||||
static const char *TAG = "light";
|
||||
|
||||
#ifdef USE_JSON
|
||||
LightCall &LightCall::parse_color_json(JsonObject &root) {
|
||||
if (root.containsKey("state")) {
|
||||
auto val = parse_on_off(root["state"]);
|
||||
switch (val) {
|
||||
case PARSE_ON:
|
||||
this->set_state(true);
|
||||
break;
|
||||
case PARSE_OFF:
|
||||
this->set_state(false);
|
||||
break;
|
||||
case PARSE_TOGGLE:
|
||||
this->set_state(!this->parent_->remote_values.is_on());
|
||||
break;
|
||||
case PARSE_NONE:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (root.containsKey("brightness")) {
|
||||
this->set_brightness(float(root["brightness"]) / 255.0f);
|
||||
}
|
||||
|
||||
if (root.containsKey("color")) {
|
||||
JsonObject &color = root["color"];
|
||||
if (color.containsKey("r")) {
|
||||
this->set_red(float(color["r"]) / 255.0f);
|
||||
}
|
||||
if (color.containsKey("g")) {
|
||||
this->set_green(float(color["g"]) / 255.0f);
|
||||
}
|
||||
if (color.containsKey("b")) {
|
||||
this->set_blue(float(color["b"]) / 255.0f);
|
||||
}
|
||||
}
|
||||
|
||||
if (root.containsKey("white_value")) {
|
||||
this->set_white(float(root["white_value"]) / 255.0f);
|
||||
}
|
||||
|
||||
if (root.containsKey("color_temp")) {
|
||||
this->set_color_temperature(float(root["color_temp"]));
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
LightCall &LightCall::parse_json(JsonObject &root) {
|
||||
this->parse_color_json(root);
|
||||
|
||||
if (root.containsKey("flash")) {
|
||||
auto length = uint32_t(float(root["flash"]) * 1000);
|
||||
this->set_flash_length(length);
|
||||
}
|
||||
|
||||
if (root.containsKey("transition")) {
|
||||
auto length = uint32_t(float(root["transition"]) * 1000);
|
||||
this->set_transition_length(length);
|
||||
}
|
||||
|
||||
if (root.containsKey("effect")) {
|
||||
const char *effect = root["effect"];
|
||||
this->set_effect(effect);
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
#endif
|
||||
|
||||
void LightCall::perform() {
|
||||
// use remote values for fallback
|
||||
const char *name = this->parent_->get_name().c_str();
|
||||
if (this->publish_) {
|
||||
ESP_LOGD(TAG, "'%s' Setting:", name);
|
||||
}
|
||||
|
||||
LightColorValues v = this->validate_();
|
||||
|
||||
if (this->publish_) {
|
||||
// Only print state when it's being changed
|
||||
bool current_state = this->parent_->remote_values.is_on();
|
||||
if (this->state_.value_or(current_state) != current_state) {
|
||||
ESP_LOGD(TAG, " State: %s", ONOFF(v.is_on()));
|
||||
}
|
||||
|
||||
if (this->brightness_.has_value()) {
|
||||
ESP_LOGD(TAG, " Brightness: %.0f%%", v.get_brightness() * 100.0f);
|
||||
}
|
||||
|
||||
if (this->color_temperature_.has_value()) {
|
||||
ESP_LOGD(TAG, " Color Temperature: %.1f mireds", v.get_color_temperature());
|
||||
}
|
||||
|
||||
if (this->red_.has_value() || this->green_.has_value() || this->blue_.has_value()) {
|
||||
ESP_LOGD(TAG, " Red=%.0f%%, Green=%.0f%%, Blue=%.0f%%", v.get_red() * 100.0f, v.get_green() * 100.0f,
|
||||
v.get_blue() * 100.0f);
|
||||
}
|
||||
if (this->white_.has_value()) {
|
||||
ESP_LOGD(TAG, " White Value: %.0f%%", v.get_white() * 100.0f);
|
||||
}
|
||||
}
|
||||
|
||||
if (this->has_flash_()) {
|
||||
// FLASH
|
||||
if (this->publish_) {
|
||||
ESP_LOGD(TAG, " Flash Length: %.1fs", *this->flash_length_ / 1e3f);
|
||||
}
|
||||
|
||||
this->parent_->start_flash_(v, *this->flash_length_);
|
||||
} else if (this->has_transition_()) {
|
||||
// TRANSITION
|
||||
if (this->publish_) {
|
||||
ESP_LOGD(TAG, " Transition Length: %.1fs", *this->transition_length_ / 1e3f);
|
||||
}
|
||||
|
||||
// Special case: Transition and effect can be set when turning off
|
||||
if (this->has_effect_()) {
|
||||
if (this->publish_) {
|
||||
ESP_LOGD(TAG, " Effect: 'None'");
|
||||
}
|
||||
this->parent_->stop_effect_();
|
||||
}
|
||||
|
||||
this->parent_->start_transition_(v, *this->transition_length_);
|
||||
|
||||
} else if (this->has_effect_()) {
|
||||
// EFFECT
|
||||
auto effect = this->effect_;
|
||||
const char *effect_s;
|
||||
if (effect == 0)
|
||||
effect_s = "None";
|
||||
else
|
||||
effect_s = this->parent_->effects_[*this->effect_ - 1]->get_name().c_str();
|
||||
|
||||
if (this->publish_) {
|
||||
ESP_LOGD(TAG, " Effect: '%s'", effect_s);
|
||||
}
|
||||
|
||||
this->parent_->start_effect_(*this->effect_);
|
||||
|
||||
// Also set light color values when starting an effect
|
||||
// For example to turn off the light
|
||||
this->parent_->set_immediately_(v, true);
|
||||
} else {
|
||||
// INSTANT CHANGE
|
||||
this->parent_->set_immediately_(v, this->publish_);
|
||||
}
|
||||
|
||||
if (!this->has_transition_()) {
|
||||
this->parent_->target_state_reached_callback_.call();
|
||||
}
|
||||
if (this->publish_) {
|
||||
this->parent_->publish_state();
|
||||
}
|
||||
if (this->save_) {
|
||||
this->parent_->save_remote_values_();
|
||||
}
|
||||
}
|
||||
|
||||
LightColorValues LightCall::validate_() {
|
||||
// use remote values for fallback
|
||||
auto *name = this->parent_->get_name().c_str();
|
||||
auto traits = this->parent_->get_traits();
|
||||
|
||||
// Brightness exists check
|
||||
if (this->brightness_.has_value() && !traits.get_supports_brightness()) {
|
||||
ESP_LOGW(TAG, "'%s' - This light does not support setting brightness!", name);
|
||||
this->brightness_.reset();
|
||||
}
|
||||
|
||||
// Transition length possible check
|
||||
if (this->transition_length_.has_value() && *this->transition_length_ != 0 && !traits.get_supports_brightness()) {
|
||||
ESP_LOGW(TAG, "'%s' - This light does not support transitions!", name);
|
||||
this->transition_length_.reset();
|
||||
}
|
||||
|
||||
// RGB exists check
|
||||
if (this->red_.has_value() || this->green_.has_value() || this->blue_.has_value()) {
|
||||
if (!traits.get_supports_rgb()) {
|
||||
ESP_LOGW(TAG, "'%s' - This light does not support setting RGB color!", name);
|
||||
this->red_.reset();
|
||||
this->green_.reset();
|
||||
this->blue_.reset();
|
||||
}
|
||||
}
|
||||
|
||||
// White value exists check
|
||||
if (this->white_.has_value() && !traits.get_supports_rgb_white_value()) {
|
||||
ESP_LOGW(TAG, "'%s' - This light does not support setting white value!", name);
|
||||
this->white_.reset();
|
||||
}
|
||||
|
||||
// Color temperature exists check
|
||||
if (this->color_temperature_.has_value() && !traits.get_supports_color_temperature()) {
|
||||
ESP_LOGW(TAG, "'%s' - This light does not support setting color temperature!", name);
|
||||
this->color_temperature_.reset();
|
||||
}
|
||||
|
||||
// If white channel is specified, set RGB to white color (when interlock is enabled)
|
||||
if (this->white_.has_value()) {
|
||||
if (traits.get_supports_color_interlock()) {
|
||||
if (!this->red_.has_value() && !this->green_.has_value() && !this->blue_.has_value()) {
|
||||
this->red_ = optional<float>(1.0f);
|
||||
this->green_ = optional<float>(1.0f);
|
||||
this->blue_ = optional<float>(1.0f);
|
||||
}
|
||||
// make white values binary aka 0.0f or 1.0f... this allows brightness to do its job
|
||||
if (*this->white_ > 0.0f) {
|
||||
this->white_ = optional<float>(1.0f);
|
||||
} else {
|
||||
this->white_ = optional<float>(0.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
// If only a color channel is specified, set white channel to 100% for white, otherwise 0% (when interlock is enabled)
|
||||
else if (this->red_.has_value() || this->green_.has_value() || this->blue_.has_value()) {
|
||||
if (traits.get_supports_color_interlock()) {
|
||||
if (*this->red_ == 1.0f && *this->green_ == 1.0f && *this->blue_ == 1.0f) {
|
||||
this->white_ = optional<float>(1.0f);
|
||||
} else {
|
||||
this->white_ = optional<float>(0.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
// If only a color temperature is specified, change to white light
|
||||
else if (this->color_temperature_.has_value()) {
|
||||
this->red_ = optional<float>(1.0f);
|
||||
this->green_ = optional<float>(1.0f);
|
||||
this->blue_ = optional<float>(1.0f);
|
||||
|
||||
// if setting color temperature from color (i.e. switching to white light), set White to 100%
|
||||
auto cv = this->parent_->remote_values;
|
||||
bool was_color = cv.get_red() != 1.0f || cv.get_blue() != 1.0f || cv.get_green() != 1.0f;
|
||||
if (traits.get_supports_color_interlock() || was_color) {
|
||||
this->white_ = optional<float>(1.0f);
|
||||
}
|
||||
}
|
||||
|
||||
#define VALIDATE_RANGE_(name_, upper_name) \
|
||||
if (name_##_.has_value()) { \
|
||||
auto val = *name_##_; \
|
||||
if (val < 0.0f || val > 1.0f) { \
|
||||
ESP_LOGW(TAG, "'%s' - %s value %.2f is out of range [0.0 - 1.0]!", name, upper_name, val); \
|
||||
name_##_ = clamp(val, 0.0f, 1.0f); \
|
||||
} \
|
||||
}
|
||||
#define VALIDATE_RANGE(name, upper_name) VALIDATE_RANGE_(name, upper_name)
|
||||
|
||||
// Range checks
|
||||
VALIDATE_RANGE(brightness, "Brightness")
|
||||
VALIDATE_RANGE(red, "Red")
|
||||
VALIDATE_RANGE(green, "Green")
|
||||
VALIDATE_RANGE(blue, "Blue")
|
||||
VALIDATE_RANGE(white, "White")
|
||||
|
||||
auto v = this->parent_->remote_values;
|
||||
if (this->state_.has_value())
|
||||
v.set_state(*this->state_);
|
||||
if (this->brightness_.has_value())
|
||||
v.set_brightness(*this->brightness_);
|
||||
|
||||
if (this->red_.has_value())
|
||||
v.set_red(*this->red_);
|
||||
if (this->green_.has_value())
|
||||
v.set_green(*this->green_);
|
||||
if (this->blue_.has_value())
|
||||
v.set_blue(*this->blue_);
|
||||
if (this->white_.has_value())
|
||||
v.set_white(*this->white_);
|
||||
|
||||
if (this->color_temperature_.has_value())
|
||||
v.set_color_temperature(*this->color_temperature_);
|
||||
|
||||
v.normalize_color(traits);
|
||||
|
||||
// Flash length check
|
||||
if (this->has_flash_() && *this->flash_length_ == 0) {
|
||||
ESP_LOGW(TAG, "'%s' - Flash length must be greater than zero!", name);
|
||||
this->flash_length_.reset();
|
||||
}
|
||||
|
||||
// validate transition length/flash length/effect not used at the same time
|
||||
bool supports_transition = traits.get_supports_brightness();
|
||||
|
||||
// If effect is already active, remove effect start
|
||||
if (this->has_effect_() && *this->effect_ == this->parent_->active_effect_index_) {
|
||||
this->effect_.reset();
|
||||
}
|
||||
|
||||
// validate effect index
|
||||
if (this->has_effect_() && *this->effect_ > this->parent_->effects_.size()) {
|
||||
ESP_LOGW(TAG, "'%s' Invalid effect index %u", name, *this->effect_);
|
||||
this->effect_.reset();
|
||||
}
|
||||
|
||||
if (this->has_effect_() && (this->has_transition_() || this->has_flash_())) {
|
||||
ESP_LOGW(TAG, "'%s' - Effect cannot be used together with transition/flash!", name);
|
||||
this->transition_length_.reset();
|
||||
this->flash_length_.reset();
|
||||
}
|
||||
|
||||
if (this->has_flash_() && this->has_transition_()) {
|
||||
ESP_LOGW(TAG, "'%s' - Flash cannot be used together with transition!", name);
|
||||
this->transition_length_.reset();
|
||||
}
|
||||
|
||||
if (!this->has_transition_() && !this->has_flash_() && (!this->has_effect_() || *this->effect_ == 0) &&
|
||||
supports_transition) {
|
||||
// nothing specified and light supports transitions, set default transition length
|
||||
this->transition_length_ = this->parent_->default_transition_length_;
|
||||
}
|
||||
|
||||
if (this->transition_length_.value_or(0) == 0) {
|
||||
// 0 transition is interpreted as no transition (instant change)
|
||||
this->transition_length_.reset();
|
||||
}
|
||||
|
||||
if (this->has_transition_() && !supports_transition) {
|
||||
ESP_LOGW(TAG, "'%s' - Light does not support transitions!", name);
|
||||
this->transition_length_.reset();
|
||||
}
|
||||
|
||||
// If not a flash and turning the light off, then disable the light
|
||||
// Do not use light color values directly, so that effects can set 0% brightness
|
||||
// Reason: When user turns off the light in frontend, the effect should also stop
|
||||
if (!this->has_flash_() && !this->state_.value_or(v.is_on())) {
|
||||
if (this->has_effect_()) {
|
||||
ESP_LOGW(TAG, "'%s' - Cannot start an effect when turning off!", name);
|
||||
this->effect_.reset();
|
||||
} else if (this->parent_->active_effect_index_ != 0) {
|
||||
// Auto turn off effect
|
||||
this->effect_ = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Disable saving for flashes
|
||||
if (this->has_flash_())
|
||||
this->save_ = false;
|
||||
|
||||
return v;
|
||||
}
|
||||
LightCall &LightCall::set_effect(const std::string &effect) {
|
||||
if (strcasecmp(effect.c_str(), "none") == 0) {
|
||||
this->set_effect(0);
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool found = false;
|
||||
for (uint32_t i = 0; i < this->parent_->effects_.size(); i++) {
|
||||
LightEffect *e = this->parent_->effects_[i];
|
||||
|
||||
if (strcasecmp(effect.c_str(), e->get_name().c_str()) == 0) {
|
||||
this->set_effect(i + 1);
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
ESP_LOGW(TAG, "'%s' - No such effect '%s'", this->parent_->get_name().c_str(), effect.c_str());
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
LightCall &LightCall::from_light_color_values(const LightColorValues &values) {
|
||||
this->set_state(values.is_on());
|
||||
this->set_brightness_if_supported(values.get_brightness());
|
||||
this->set_red_if_supported(values.get_red());
|
||||
this->set_green_if_supported(values.get_green());
|
||||
this->set_blue_if_supported(values.get_blue());
|
||||
this->set_white_if_supported(values.get_white());
|
||||
this->set_color_temperature_if_supported(values.get_color_temperature());
|
||||
return *this;
|
||||
}
|
||||
LightCall &LightCall::set_transition_length_if_supported(uint32_t transition_length) {
|
||||
if (this->parent_->get_traits().get_supports_brightness())
|
||||
this->set_transition_length(transition_length);
|
||||
return *this;
|
||||
}
|
||||
LightCall &LightCall::set_brightness_if_supported(float brightness) {
|
||||
if (this->parent_->get_traits().get_supports_brightness())
|
||||
this->set_brightness(brightness);
|
||||
return *this;
|
||||
}
|
||||
LightCall &LightCall::set_red_if_supported(float red) {
|
||||
if (this->parent_->get_traits().get_supports_rgb())
|
||||
this->set_red(red);
|
||||
return *this;
|
||||
}
|
||||
LightCall &LightCall::set_green_if_supported(float green) {
|
||||
if (this->parent_->get_traits().get_supports_rgb())
|
||||
this->set_green(green);
|
||||
return *this;
|
||||
}
|
||||
LightCall &LightCall::set_blue_if_supported(float blue) {
|
||||
if (this->parent_->get_traits().get_supports_rgb())
|
||||
this->set_blue(blue);
|
||||
return *this;
|
||||
}
|
||||
LightCall &LightCall::set_white_if_supported(float white) {
|
||||
if (this->parent_->get_traits().get_supports_rgb_white_value())
|
||||
this->set_white(white);
|
||||
return *this;
|
||||
}
|
||||
LightCall &LightCall::set_color_temperature_if_supported(float color_temperature) {
|
||||
if (this->parent_->get_traits().get_supports_color_temperature())
|
||||
this->set_color_temperature(color_temperature);
|
||||
return *this;
|
||||
}
|
||||
LightCall &LightCall::set_state(optional<bool> state) {
|
||||
this->state_ = state;
|
||||
return *this;
|
||||
}
|
||||
LightCall &LightCall::set_state(bool state) {
|
||||
this->state_ = state;
|
||||
return *this;
|
||||
}
|
||||
LightCall &LightCall::set_transition_length(optional<uint32_t> transition_length) {
|
||||
this->transition_length_ = transition_length;
|
||||
return *this;
|
||||
}
|
||||
LightCall &LightCall::set_transition_length(uint32_t transition_length) {
|
||||
this->transition_length_ = transition_length;
|
||||
return *this;
|
||||
}
|
||||
LightCall &LightCall::set_flash_length(optional<uint32_t> flash_length) {
|
||||
this->flash_length_ = flash_length;
|
||||
return *this;
|
||||
}
|
||||
LightCall &LightCall::set_flash_length(uint32_t flash_length) {
|
||||
this->flash_length_ = flash_length;
|
||||
return *this;
|
||||
}
|
||||
LightCall &LightCall::set_brightness(optional<float> brightness) {
|
||||
this->brightness_ = brightness;
|
||||
return *this;
|
||||
}
|
||||
LightCall &LightCall::set_brightness(float brightness) {
|
||||
this->brightness_ = brightness;
|
||||
return *this;
|
||||
}
|
||||
LightCall &LightCall::set_red(optional<float> red) {
|
||||
this->red_ = red;
|
||||
return *this;
|
||||
}
|
||||
LightCall &LightCall::set_red(float red) {
|
||||
this->red_ = red;
|
||||
return *this;
|
||||
}
|
||||
LightCall &LightCall::set_green(optional<float> green) {
|
||||
this->green_ = green;
|
||||
return *this;
|
||||
}
|
||||
LightCall &LightCall::set_green(float green) {
|
||||
this->green_ = green;
|
||||
return *this;
|
||||
}
|
||||
LightCall &LightCall::set_blue(optional<float> blue) {
|
||||
this->blue_ = blue;
|
||||
return *this;
|
||||
}
|
||||
LightCall &LightCall::set_blue(float blue) {
|
||||
this->blue_ = blue;
|
||||
return *this;
|
||||
}
|
||||
LightCall &LightCall::set_white(optional<float> white) {
|
||||
this->white_ = white;
|
||||
return *this;
|
||||
}
|
||||
LightCall &LightCall::set_white(float white) {
|
||||
this->white_ = white;
|
||||
return *this;
|
||||
}
|
||||
LightCall &LightCall::set_color_temperature(optional<float> color_temperature) {
|
||||
this->color_temperature_ = color_temperature;
|
||||
return *this;
|
||||
}
|
||||
LightCall &LightCall::set_color_temperature(float color_temperature) {
|
||||
this->color_temperature_ = color_temperature;
|
||||
return *this;
|
||||
}
|
||||
LightCall &LightCall::set_effect(optional<std::string> effect) {
|
||||
if (effect.has_value())
|
||||
this->set_effect(*effect);
|
||||
return *this;
|
||||
}
|
||||
LightCall &LightCall::set_effect(uint32_t effect_number) {
|
||||
this->effect_ = effect_number;
|
||||
return *this;
|
||||
}
|
||||
LightCall &LightCall::set_effect(optional<uint32_t> effect_number) {
|
||||
this->effect_ = effect_number;
|
||||
return *this;
|
||||
}
|
||||
LightCall &LightCall::set_publish(bool publish) {
|
||||
this->publish_ = publish;
|
||||
return *this;
|
||||
}
|
||||
LightCall &LightCall::set_save(bool save) {
|
||||
this->save_ = save;
|
||||
return *this;
|
||||
}
|
||||
LightCall &LightCall::set_rgb(float red, float green, float blue) {
|
||||
this->set_red(red);
|
||||
this->set_green(green);
|
||||
this->set_blue(blue);
|
||||
return *this;
|
||||
}
|
||||
LightCall &LightCall::set_rgbw(float red, float green, float blue, float white) {
|
||||
this->set_rgb(red, green, blue);
|
||||
this->set_white(white);
|
||||
return *this;
|
||||
}
|
||||
|
||||
} // namespace light
|
||||
} // namespace esphome
|
160
esphome/components/light/light_call.h
Normal file
160
esphome/components/light/light_call.h
Normal file
|
@ -0,0 +1,160 @@
|
|||
#pragma once
|
||||
|
||||
#include "esphome/core/optional.h"
|
||||
#include "light_color_values.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace light {
|
||||
|
||||
class LightState;
|
||||
|
||||
/** This class represents a requested change in a light state.
|
||||
*/
|
||||
class LightCall {
|
||||
public:
|
||||
explicit LightCall(LightState *parent) : parent_(parent) {}
|
||||
|
||||
/// Set the binary ON/OFF state of the light.
|
||||
LightCall &set_state(optional<bool> state);
|
||||
/// Set the binary ON/OFF state of the light.
|
||||
LightCall &set_state(bool state);
|
||||
/** Set the transition length of this call in milliseconds.
|
||||
*
|
||||
* This argument is ignored for starting flashes and effects.
|
||||
*
|
||||
* Defaults to the default transition length defined in the light configuration.
|
||||
*/
|
||||
LightCall &set_transition_length(optional<uint32_t> transition_length);
|
||||
/** Set the transition length of this call in milliseconds.
|
||||
*
|
||||
* This argument is ignored for starting flashes and effects.
|
||||
*
|
||||
* Defaults to the default transition length defined in the light configuration.
|
||||
*/
|
||||
LightCall &set_transition_length(uint32_t transition_length);
|
||||
/// Set the transition length property if the light supports transitions.
|
||||
LightCall &set_transition_length_if_supported(uint32_t transition_length);
|
||||
/// Start and set the flash length of this call in milliseconds.
|
||||
LightCall &set_flash_length(optional<uint32_t> flash_length);
|
||||
/// Start and set the flash length of this call in milliseconds.
|
||||
LightCall &set_flash_length(uint32_t flash_length);
|
||||
/// Set the target brightness of the light from 0.0 (fully off) to 1.0 (fully on)
|
||||
LightCall &set_brightness(optional<float> brightness);
|
||||
/// Set the target brightness of the light from 0.0 (fully off) to 1.0 (fully on)
|
||||
LightCall &set_brightness(float brightness);
|
||||
/// Set the brightness property if the light supports brightness.
|
||||
LightCall &set_brightness_if_supported(float brightness);
|
||||
/** Set the red RGB value of the light from 0.0 to 1.0.
|
||||
*
|
||||
* Note that this only controls the color of the light, not its brightness.
|
||||
*/
|
||||
LightCall &set_red(optional<float> red);
|
||||
/** Set the red RGB value of the light from 0.0 to 1.0.
|
||||
*
|
||||
* Note that this only controls the color of the light, not its brightness.
|
||||
*/
|
||||
LightCall &set_red(float red);
|
||||
/// Set the red property if the light supports RGB.
|
||||
LightCall &set_red_if_supported(float red);
|
||||
/** Set the green RGB value of the light from 0.0 to 1.0.
|
||||
*
|
||||
* Note that this only controls the color of the light, not its brightness.
|
||||
*/
|
||||
LightCall &set_green(optional<float> green);
|
||||
/** Set the green RGB value of the light from 0.0 to 1.0.
|
||||
*
|
||||
* Note that this only controls the color of the light, not its brightness.
|
||||
*/
|
||||
LightCall &set_green(float green);
|
||||
/// Set the green property if the light supports RGB.
|
||||
LightCall &set_green_if_supported(float green);
|
||||
/** Set the blue RGB value of the light from 0.0 to 1.0.
|
||||
*
|
||||
* Note that this only controls the color of the light, not its brightness.
|
||||
*/
|
||||
LightCall &set_blue(optional<float> blue);
|
||||
/** Set the blue RGB value of the light from 0.0 to 1.0.
|
||||
*
|
||||
* Note that this only controls the color of the light, not its brightness.
|
||||
*/
|
||||
LightCall &set_blue(float blue);
|
||||
/// Set the blue property if the light supports RGB.
|
||||
LightCall &set_blue_if_supported(float blue);
|
||||
/// Set the white value value of the light from 0.0 to 1.0 for RGBW[W] lights.
|
||||
LightCall &set_white(optional<float> white);
|
||||
/// Set the white value value of the light from 0.0 to 1.0 for RGBW[W] lights.
|
||||
LightCall &set_white(float white);
|
||||
/// Set the white property if the light supports RGB.
|
||||
LightCall &set_white_if_supported(float white);
|
||||
/// Set the color temperature of the light in mireds for CWWW or RGBWW lights.
|
||||
LightCall &set_color_temperature(optional<float> color_temperature);
|
||||
/// Set the color temperature of the light in mireds for CWWW or RGBWW lights.
|
||||
LightCall &set_color_temperature(float color_temperature);
|
||||
/// Set the color_temperature property if the light supports color temperature.
|
||||
LightCall &set_color_temperature_if_supported(float color_temperature);
|
||||
/// Set the effect of the light by its name.
|
||||
LightCall &set_effect(optional<std::string> effect);
|
||||
/// Set the effect of the light by its name.
|
||||
LightCall &set_effect(const std::string &effect);
|
||||
/// Set the effect of the light by its internal index number (only for internal use).
|
||||
LightCall &set_effect(uint32_t effect_number);
|
||||
LightCall &set_effect(optional<uint32_t> effect_number);
|
||||
/// Set whether this light call should trigger a publish state.
|
||||
LightCall &set_publish(bool publish);
|
||||
/// Set whether this light call should trigger a save state to recover them at startup..
|
||||
LightCall &set_save(bool save);
|
||||
|
||||
/** Set the RGB color of the light by RGB values.
|
||||
*
|
||||
* Please note that this only changes the color of the light, not the brightness.
|
||||
*
|
||||
* @param red The red color value from 0.0 to 1.0.
|
||||
* @param green The green color value from 0.0 to 1.0.
|
||||
* @param blue The blue color value from 0.0 to 1.0.
|
||||
* @return The light call for chaining setters.
|
||||
*/
|
||||
LightCall &set_rgb(float red, float green, float blue);
|
||||
/** Set the RGBW color of the light by RGB values.
|
||||
*
|
||||
* Please note that this only changes the color of the light, not the brightness.
|
||||
*
|
||||
* @param red The red color value from 0.0 to 1.0.
|
||||
* @param green The green color value from 0.0 to 1.0.
|
||||
* @param blue The blue color value from 0.0 to 1.0.
|
||||
* @param white The white color value from 0.0 to 1.0.
|
||||
* @return The light call for chaining setters.
|
||||
*/
|
||||
LightCall &set_rgbw(float red, float green, float blue, float white);
|
||||
#ifdef USE_JSON
|
||||
LightCall &parse_color_json(JsonObject &root);
|
||||
LightCall &parse_json(JsonObject &root);
|
||||
#endif
|
||||
LightCall &from_light_color_values(const LightColorValues &values);
|
||||
|
||||
void perform();
|
||||
|
||||
protected:
|
||||
/// Validate all properties and return the target light color values.
|
||||
LightColorValues validate_();
|
||||
|
||||
bool has_transition_() { return this->transition_length_.has_value(); }
|
||||
bool has_flash_() { return this->flash_length_.has_value(); }
|
||||
bool has_effect_() { return this->effect_.has_value(); }
|
||||
|
||||
LightState *parent_;
|
||||
optional<bool> state_;
|
||||
optional<uint32_t> transition_length_;
|
||||
optional<uint32_t> flash_length_;
|
||||
optional<float> brightness_;
|
||||
optional<float> red_;
|
||||
optional<float> green_;
|
||||
optional<float> blue_;
|
||||
optional<float> white_;
|
||||
optional<float> color_temperature_;
|
||||
optional<uint32_t> effect_;
|
||||
bool publish_{true};
|
||||
bool save_{true};
|
||||
};
|
||||
|
||||
} // namespace light
|
||||
} // namespace esphome
|
|
@ -7,80 +7,13 @@ namespace light {
|
|||
|
||||
static const char *const TAG = "light";
|
||||
|
||||
void LightState::start_transition_(const LightColorValues &target, uint32_t length) {
|
||||
this->transformer_ = make_unique<LightTransitionTransformer>(millis(), length, this->current_values, target);
|
||||
this->remote_values = this->transformer_->get_remote_values();
|
||||
}
|
||||
|
||||
void LightState::start_flash_(const LightColorValues &target, uint32_t length) {
|
||||
LightColorValues end_colors = this->current_values;
|
||||
// If starting a flash if one is already happening, set end values to end values of current flash
|
||||
// Hacky but works
|
||||
if (this->transformer_ != nullptr)
|
||||
end_colors = this->transformer_->get_end_values();
|
||||
this->transformer_ = make_unique<LightFlashTransformer>(millis(), length, end_colors, target);
|
||||
this->remote_values = this->transformer_->get_remote_values();
|
||||
}
|
||||
|
||||
LightState::LightState(const std::string &name, LightOutput *output) : Nameable(name), output_(output) {}
|
||||
|
||||
void LightState::set_immediately_(const LightColorValues &target, bool set_remote_values) {
|
||||
this->transformer_ = nullptr;
|
||||
this->current_values = target;
|
||||
if (set_remote_values) {
|
||||
this->remote_values = target;
|
||||
}
|
||||
this->next_write_ = true;
|
||||
}
|
||||
|
||||
LightColorValues LightState::get_current_values() { return this->current_values; }
|
||||
|
||||
void LightState::publish_state() {
|
||||
this->remote_values_callback_.call();
|
||||
this->next_write_ = true;
|
||||
}
|
||||
|
||||
LightColorValues LightState::get_remote_values() { return this->remote_values; }
|
||||
|
||||
std::string LightState::get_effect_name() {
|
||||
if (this->active_effect_index_ > 0)
|
||||
return this->effects_[this->active_effect_index_ - 1]->get_name();
|
||||
else
|
||||
return "None";
|
||||
}
|
||||
|
||||
void LightState::start_effect_(uint32_t effect_index) {
|
||||
this->stop_effect_();
|
||||
if (effect_index == 0)
|
||||
return;
|
||||
|
||||
this->active_effect_index_ = effect_index;
|
||||
auto *effect = this->get_active_effect_();
|
||||
effect->start_internal();
|
||||
}
|
||||
|
||||
bool LightState::supports_effects() { return !this->effects_.empty(); }
|
||||
void LightState::set_transformer_(std::unique_ptr<LightTransformer> transformer) {
|
||||
this->transformer_ = std::move(transformer);
|
||||
}
|
||||
void LightState::stop_effect_() {
|
||||
auto *effect = this->get_active_effect_();
|
||||
if (effect != nullptr) {
|
||||
effect->stop();
|
||||
}
|
||||
this->active_effect_index_ = 0;
|
||||
}
|
||||
|
||||
void LightState::set_default_transition_length(uint32_t default_transition_length) {
|
||||
this->default_transition_length_ = default_transition_length;
|
||||
}
|
||||
#ifdef USE_JSON
|
||||
void LightState::dump_json(JsonObject &root) {
|
||||
if (this->supports_effects())
|
||||
root["effect"] = this->get_effect_name();
|
||||
this->remote_values.dump_json(root, this->output_->get_traits());
|
||||
}
|
||||
#endif
|
||||
LightTraits LightState::get_traits() { return this->output_->get_traits(); }
|
||||
LightCall LightState::turn_on() { return this->make_call().set_state(true); }
|
||||
LightCall LightState::turn_off() { return this->make_call().set_state(false); }
|
||||
LightCall LightState::toggle() { return this->make_call().set_state(!this->remote_values.is_on()); }
|
||||
LightCall LightState::make_call() { return LightCall(this); }
|
||||
|
||||
struct LightStateRTCState {
|
||||
bool state{false};
|
||||
|
@ -144,6 +77,17 @@ void LightState::setup() {
|
|||
}
|
||||
call.perform();
|
||||
}
|
||||
void LightState::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "Light '%s'", this->get_name().c_str());
|
||||
if (this->get_traits().get_supports_brightness()) {
|
||||
ESP_LOGCONFIG(TAG, " Default Transition Length: %.1fs", this->default_transition_length_ / 1e3f);
|
||||
ESP_LOGCONFIG(TAG, " Gamma Correct: %.2f", this->gamma_correct_);
|
||||
}
|
||||
if (this->get_traits().get_supports_color_temperature()) {
|
||||
ESP_LOGCONFIG(TAG, " Min Mireds: %.1f", this->get_traits().get_min_mireds());
|
||||
ESP_LOGCONFIG(TAG, " Max Mireds: %.1f", this->get_traits().get_max_mireds());
|
||||
}
|
||||
}
|
||||
void LightState::loop() {
|
||||
// Apply effect (if any)
|
||||
auto *effect = this->get_active_effect_();
|
||||
|
@ -171,7 +115,47 @@ void LightState::loop() {
|
|||
this->next_write_ = false;
|
||||
}
|
||||
}
|
||||
LightTraits LightState::get_traits() { return this->output_->get_traits(); }
|
||||
|
||||
float LightState::get_setup_priority() const { return setup_priority::HARDWARE - 1.0f; }
|
||||
uint32_t LightState::hash_base() { return 1114400283; }
|
||||
|
||||
LightColorValues LightState::get_current_values() { return this->current_values; }
|
||||
LightColorValues LightState::get_remote_values() { return this->remote_values; }
|
||||
|
||||
void LightState::publish_state() {
|
||||
this->remote_values_callback_.call();
|
||||
this->next_write_ = true;
|
||||
}
|
||||
|
||||
LightOutput *LightState::get_output() const { return this->output_; }
|
||||
std::string LightState::get_effect_name() {
|
||||
if (this->active_effect_index_ > 0)
|
||||
return this->effects_[this->active_effect_index_ - 1]->get_name();
|
||||
else
|
||||
return "None";
|
||||
}
|
||||
|
||||
void LightState::add_new_remote_values_callback(std::function<void()> &&send_callback) {
|
||||
this->remote_values_callback_.add(std::move(send_callback));
|
||||
}
|
||||
void LightState::add_new_target_state_reached_callback(std::function<void()> &&send_callback) {
|
||||
this->target_state_reached_callback_.add(std::move(send_callback));
|
||||
}
|
||||
|
||||
#ifdef USE_JSON
|
||||
void LightState::dump_json(JsonObject &root) {
|
||||
if (this->supports_effects())
|
||||
root["effect"] = this->get_effect_name();
|
||||
this->remote_values.dump_json(root, this->output_->get_traits());
|
||||
}
|
||||
#endif
|
||||
|
||||
void LightState::set_default_transition_length(uint32_t default_transition_length) {
|
||||
this->default_transition_length_ = default_transition_length;
|
||||
}
|
||||
void LightState::set_gamma_correct(float gamma_correct) { this->gamma_correct_ = gamma_correct; }
|
||||
void LightState::set_restore_mode(LightRestoreMode restore_mode) { this->restore_mode_ = restore_mode; }
|
||||
bool LightState::supports_effects() { return !this->effects_.empty(); }
|
||||
const std::vector<LightEffect *> &LightState::get_effects() const { return this->effects_; }
|
||||
void LightState::add_effects(const std::vector<LightEffect *> &effects) {
|
||||
this->effects_.reserve(this->effects_.size() + effects.size());
|
||||
|
@ -179,551 +163,7 @@ void LightState::add_effects(const std::vector<LightEffect *> &effects) {
|
|||
this->effects_.push_back(effect);
|
||||
}
|
||||
}
|
||||
LightCall LightState::turn_on() { return this->make_call().set_state(true); }
|
||||
LightCall LightState::turn_off() { return this->make_call().set_state(false); }
|
||||
LightCall LightState::toggle() { return this->make_call().set_state(!this->remote_values.is_on()); }
|
||||
LightCall LightState::make_call() { return LightCall(this); }
|
||||
uint32_t LightState::hash_base() { return 1114400283; }
|
||||
void LightState::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "Light '%s'", this->get_name().c_str());
|
||||
if (this->get_traits().get_supports_brightness()) {
|
||||
ESP_LOGCONFIG(TAG, " Default Transition Length: %.1fs", this->default_transition_length_ / 1e3f);
|
||||
ESP_LOGCONFIG(TAG, " Gamma Correct: %.2f", this->gamma_correct_);
|
||||
}
|
||||
if (this->get_traits().get_supports_color_temperature()) {
|
||||
ESP_LOGCONFIG(TAG, " Min Mireds: %.1f", this->get_traits().get_min_mireds());
|
||||
ESP_LOGCONFIG(TAG, " Max Mireds: %.1f", this->get_traits().get_max_mireds());
|
||||
}
|
||||
}
|
||||
#ifdef USE_MQTT_LIGHT
|
||||
MQTTJSONLightComponent *LightState::get_mqtt() const { return this->mqtt_; }
|
||||
void LightState::set_mqtt(MQTTJSONLightComponent *mqtt) { this->mqtt_ = mqtt; }
|
||||
#endif
|
||||
|
||||
#ifdef USE_JSON
|
||||
LightCall &LightCall::parse_color_json(JsonObject &root) {
|
||||
if (root.containsKey("state")) {
|
||||
auto val = parse_on_off(root["state"]);
|
||||
switch (val) {
|
||||
case PARSE_ON:
|
||||
this->set_state(true);
|
||||
break;
|
||||
case PARSE_OFF:
|
||||
this->set_state(false);
|
||||
break;
|
||||
case PARSE_TOGGLE:
|
||||
this->set_state(!this->parent_->remote_values.is_on());
|
||||
break;
|
||||
case PARSE_NONE:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (root.containsKey("brightness")) {
|
||||
this->set_brightness(float(root["brightness"]) / 255.0f);
|
||||
}
|
||||
|
||||
if (root.containsKey("color")) {
|
||||
JsonObject &color = root["color"];
|
||||
if (color.containsKey("r")) {
|
||||
this->set_red(float(color["r"]) / 255.0f);
|
||||
}
|
||||
if (color.containsKey("g")) {
|
||||
this->set_green(float(color["g"]) / 255.0f);
|
||||
}
|
||||
if (color.containsKey("b")) {
|
||||
this->set_blue(float(color["b"]) / 255.0f);
|
||||
}
|
||||
}
|
||||
|
||||
if (root.containsKey("white_value")) {
|
||||
this->set_white(float(root["white_value"]) / 255.0f);
|
||||
}
|
||||
|
||||
if (root.containsKey("color_temp")) {
|
||||
this->set_color_temperature(float(root["color_temp"]));
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
LightCall &LightCall::parse_json(JsonObject &root) {
|
||||
this->parse_color_json(root);
|
||||
|
||||
if (root.containsKey("flash")) {
|
||||
auto length = uint32_t(float(root["flash"]) * 1000);
|
||||
this->set_flash_length(length);
|
||||
}
|
||||
|
||||
if (root.containsKey("transition")) {
|
||||
auto length = uint32_t(float(root["transition"]) * 1000);
|
||||
this->set_transition_length(length);
|
||||
}
|
||||
|
||||
if (root.containsKey("effect")) {
|
||||
const char *effect = root["effect"];
|
||||
this->set_effect(effect);
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
#endif
|
||||
|
||||
void LightCall::perform() {
|
||||
// use remote values for fallback
|
||||
const char *name = this->parent_->get_name().c_str();
|
||||
if (this->publish_) {
|
||||
ESP_LOGD(TAG, "'%s' Setting:", name);
|
||||
}
|
||||
|
||||
LightColorValues v = this->validate_();
|
||||
|
||||
if (this->publish_) {
|
||||
// Only print state when it's being changed
|
||||
bool current_state = this->parent_->remote_values.is_on();
|
||||
if (this->state_.value_or(current_state) != current_state) {
|
||||
ESP_LOGD(TAG, " State: %s", ONOFF(v.is_on()));
|
||||
}
|
||||
|
||||
if (this->brightness_.has_value()) {
|
||||
ESP_LOGD(TAG, " Brightness: %.0f%%", v.get_brightness() * 100.0f);
|
||||
}
|
||||
|
||||
if (this->color_temperature_.has_value()) {
|
||||
ESP_LOGD(TAG, " Color Temperature: %.1f mireds", v.get_color_temperature());
|
||||
}
|
||||
|
||||
if (this->red_.has_value() || this->green_.has_value() || this->blue_.has_value()) {
|
||||
ESP_LOGD(TAG, " Red=%.0f%%, Green=%.0f%%, Blue=%.0f%%", v.get_red() * 100.0f, v.get_green() * 100.0f,
|
||||
v.get_blue() * 100.0f);
|
||||
}
|
||||
if (this->white_.has_value()) {
|
||||
ESP_LOGD(TAG, " White Value: %.0f%%", v.get_white() * 100.0f);
|
||||
}
|
||||
}
|
||||
|
||||
if (this->has_flash_()) {
|
||||
// FLASH
|
||||
if (this->publish_) {
|
||||
ESP_LOGD(TAG, " Flash Length: %.1fs", *this->flash_length_ / 1e3f);
|
||||
}
|
||||
|
||||
this->parent_->start_flash_(v, *this->flash_length_);
|
||||
} else if (this->has_transition_()) {
|
||||
// TRANSITION
|
||||
if (this->publish_) {
|
||||
ESP_LOGD(TAG, " Transition Length: %.1fs", *this->transition_length_ / 1e3f);
|
||||
}
|
||||
|
||||
// Special case: Transition and effect can be set when turning off
|
||||
if (this->has_effect_()) {
|
||||
if (this->publish_) {
|
||||
ESP_LOGD(TAG, " Effect: 'None'");
|
||||
}
|
||||
this->parent_->stop_effect_();
|
||||
}
|
||||
|
||||
this->parent_->start_transition_(v, *this->transition_length_);
|
||||
|
||||
} else if (this->has_effect_()) {
|
||||
// EFFECT
|
||||
auto effect = this->effect_;
|
||||
const char *effect_s;
|
||||
if (effect == 0)
|
||||
effect_s = "None";
|
||||
else
|
||||
effect_s = this->parent_->effects_[*this->effect_ - 1]->get_name().c_str();
|
||||
|
||||
if (this->publish_) {
|
||||
ESP_LOGD(TAG, " Effect: '%s'", effect_s);
|
||||
}
|
||||
|
||||
this->parent_->start_effect_(*this->effect_);
|
||||
|
||||
// Also set light color values when starting an effect
|
||||
// For example to turn off the light
|
||||
this->parent_->set_immediately_(v, true);
|
||||
} else {
|
||||
// INSTANT CHANGE
|
||||
this->parent_->set_immediately_(v, this->publish_);
|
||||
}
|
||||
|
||||
if (!this->has_transition_()) {
|
||||
this->parent_->target_state_reached_callback_.call();
|
||||
}
|
||||
if (this->publish_) {
|
||||
this->parent_->publish_state();
|
||||
}
|
||||
|
||||
if (this->save_) {
|
||||
LightStateRTCState saved;
|
||||
saved.state = v.is_on();
|
||||
saved.brightness = v.get_brightness();
|
||||
saved.red = v.get_red();
|
||||
saved.green = v.get_green();
|
||||
saved.blue = v.get_blue();
|
||||
saved.white = v.get_white();
|
||||
saved.color_temp = v.get_color_temperature();
|
||||
saved.effect = this->parent_->active_effect_index_;
|
||||
this->parent_->rtc_.save(&saved);
|
||||
}
|
||||
}
|
||||
|
||||
LightColorValues LightCall::validate_() {
|
||||
// use remote values for fallback
|
||||
auto *name = this->parent_->get_name().c_str();
|
||||
auto traits = this->parent_->get_traits();
|
||||
|
||||
// Brightness exists check
|
||||
if (this->brightness_.has_value() && !traits.get_supports_brightness()) {
|
||||
ESP_LOGW(TAG, "'%s' - This light does not support setting brightness!", name);
|
||||
this->brightness_.reset();
|
||||
}
|
||||
|
||||
// Transition length possible check
|
||||
if (this->transition_length_.has_value() && *this->transition_length_ != 0 && !traits.get_supports_brightness()) {
|
||||
ESP_LOGW(TAG, "'%s' - This light does not support transitions!", name);
|
||||
this->transition_length_.reset();
|
||||
}
|
||||
|
||||
// RGB exists check
|
||||
if (this->red_.has_value() || this->green_.has_value() || this->blue_.has_value()) {
|
||||
if (!traits.get_supports_rgb()) {
|
||||
ESP_LOGW(TAG, "'%s' - This light does not support setting RGB color!", name);
|
||||
this->red_.reset();
|
||||
this->green_.reset();
|
||||
this->blue_.reset();
|
||||
}
|
||||
}
|
||||
|
||||
// White value exists check
|
||||
if (this->white_.has_value() && !traits.get_supports_rgb_white_value()) {
|
||||
ESP_LOGW(TAG, "'%s' - This light does not support setting white value!", name);
|
||||
this->white_.reset();
|
||||
}
|
||||
|
||||
// Color temperature exists check
|
||||
if (this->color_temperature_.has_value() && !traits.get_supports_color_temperature()) {
|
||||
ESP_LOGW(TAG, "'%s' - This light does not support setting color temperature!", name);
|
||||
this->color_temperature_.reset();
|
||||
}
|
||||
|
||||
// If white channel is specified, set RGB to white color (when interlock is enabled)
|
||||
if (this->white_.has_value()) {
|
||||
if (traits.get_supports_color_interlock()) {
|
||||
if (!this->red_.has_value() && !this->green_.has_value() && !this->blue_.has_value()) {
|
||||
this->red_ = optional<float>(1.0f);
|
||||
this->green_ = optional<float>(1.0f);
|
||||
this->blue_ = optional<float>(1.0f);
|
||||
}
|
||||
// make white values binary aka 0.0f or 1.0f... this allows brightness to do its job
|
||||
if (*this->white_ > 0.0f) {
|
||||
this->white_ = optional<float>(1.0f);
|
||||
} else {
|
||||
this->white_ = optional<float>(0.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
// If only a color channel is specified, set white channel to 100% for white, otherwise 0% (when interlock is enabled)
|
||||
else if (this->red_.has_value() || this->green_.has_value() || this->blue_.has_value()) {
|
||||
if (traits.get_supports_color_interlock()) {
|
||||
if (*this->red_ == 1.0f && *this->green_ == 1.0f && *this->blue_ == 1.0f) {
|
||||
this->white_ = optional<float>(1.0f);
|
||||
} else {
|
||||
this->white_ = optional<float>(0.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
// If only a color temperature is specified, change to white light
|
||||
else if (this->color_temperature_.has_value()) {
|
||||
this->red_ = optional<float>(1.0f);
|
||||
this->green_ = optional<float>(1.0f);
|
||||
this->blue_ = optional<float>(1.0f);
|
||||
|
||||
// if setting color temperature from color (i.e. switching to white light), set White to 100%
|
||||
auto cv = this->parent_->remote_values;
|
||||
bool was_color = cv.get_red() != 1.0f || cv.get_blue() != 1.0f || cv.get_green() != 1.0f;
|
||||
if (traits.get_supports_color_interlock() || was_color) {
|
||||
this->white_ = optional<float>(1.0f);
|
||||
}
|
||||
}
|
||||
|
||||
#define VALIDATE_RANGE_(name_, upper_name) \
|
||||
if (name_##_.has_value()) { \
|
||||
auto val = *name_##_; \
|
||||
if (val < 0.0f || val > 1.0f) { \
|
||||
ESP_LOGW(TAG, "'%s' - %s value %.2f is out of range [0.0 - 1.0]!", name, upper_name, val); \
|
||||
name_##_ = clamp(val, 0.0f, 1.0f); \
|
||||
} \
|
||||
}
|
||||
#define VALIDATE_RANGE(name, upper_name) VALIDATE_RANGE_(name, upper_name)
|
||||
|
||||
// Range checks
|
||||
VALIDATE_RANGE(brightness, "Brightness")
|
||||
VALIDATE_RANGE(red, "Red")
|
||||
VALIDATE_RANGE(green, "Green")
|
||||
VALIDATE_RANGE(blue, "Blue")
|
||||
VALIDATE_RANGE(white, "White")
|
||||
|
||||
auto v = this->parent_->remote_values;
|
||||
if (this->state_.has_value())
|
||||
v.set_state(*this->state_);
|
||||
if (this->brightness_.has_value())
|
||||
v.set_brightness(*this->brightness_);
|
||||
|
||||
if (this->red_.has_value())
|
||||
v.set_red(*this->red_);
|
||||
if (this->green_.has_value())
|
||||
v.set_green(*this->green_);
|
||||
if (this->blue_.has_value())
|
||||
v.set_blue(*this->blue_);
|
||||
if (this->white_.has_value())
|
||||
v.set_white(*this->white_);
|
||||
|
||||
if (this->color_temperature_.has_value())
|
||||
v.set_color_temperature(*this->color_temperature_);
|
||||
|
||||
v.normalize_color(traits);
|
||||
|
||||
// Flash length check
|
||||
if (this->has_flash_() && *this->flash_length_ == 0) {
|
||||
ESP_LOGW(TAG, "'%s' - Flash length must be greater than zero!", name);
|
||||
this->flash_length_.reset();
|
||||
}
|
||||
|
||||
// validate transition length/flash length/effect not used at the same time
|
||||
bool supports_transition = traits.get_supports_brightness();
|
||||
|
||||
// If effect is already active, remove effect start
|
||||
if (this->has_effect_() && *this->effect_ == this->parent_->active_effect_index_) {
|
||||
this->effect_.reset();
|
||||
}
|
||||
|
||||
// validate effect index
|
||||
if (this->has_effect_() && *this->effect_ > this->parent_->effects_.size()) {
|
||||
ESP_LOGW(TAG, "'%s' Invalid effect index %u", name, *this->effect_);
|
||||
this->effect_.reset();
|
||||
}
|
||||
|
||||
if (this->has_effect_() && (this->has_transition_() || this->has_flash_())) {
|
||||
ESP_LOGW(TAG, "'%s' - Effect cannot be used together with transition/flash!", name);
|
||||
this->transition_length_.reset();
|
||||
this->flash_length_.reset();
|
||||
}
|
||||
|
||||
if (this->has_flash_() && this->has_transition_()) {
|
||||
ESP_LOGW(TAG, "'%s' - Flash cannot be used together with transition!", name);
|
||||
this->transition_length_.reset();
|
||||
}
|
||||
|
||||
if (!this->has_transition_() && !this->has_flash_() && (!this->has_effect_() || *this->effect_ == 0) &&
|
||||
supports_transition) {
|
||||
// nothing specified and light supports transitions, set default transition length
|
||||
this->transition_length_ = this->parent_->default_transition_length_;
|
||||
}
|
||||
|
||||
if (this->transition_length_.value_or(0) == 0) {
|
||||
// 0 transition is interpreted as no transition (instant change)
|
||||
this->transition_length_.reset();
|
||||
}
|
||||
|
||||
if (this->has_transition_() && !supports_transition) {
|
||||
ESP_LOGW(TAG, "'%s' - Light does not support transitions!", name);
|
||||
this->transition_length_.reset();
|
||||
}
|
||||
|
||||
// If not a flash and turning the light off, then disable the light
|
||||
// Do not use light color values directly, so that effects can set 0% brightness
|
||||
// Reason: When user turns off the light in frontend, the effect should also stop
|
||||
if (!this->has_flash_() && !this->state_.value_or(v.is_on())) {
|
||||
if (this->has_effect_()) {
|
||||
ESP_LOGW(TAG, "'%s' - Cannot start an effect when turning off!", name);
|
||||
this->effect_.reset();
|
||||
} else if (this->parent_->active_effect_index_ != 0) {
|
||||
// Auto turn off effect
|
||||
this->effect_ = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Disable saving for flashes
|
||||
if (this->has_flash_())
|
||||
this->save_ = false;
|
||||
|
||||
return v;
|
||||
}
|
||||
LightCall &LightCall::set_effect(const std::string &effect) {
|
||||
if (strcasecmp(effect.c_str(), "none") == 0) {
|
||||
this->set_effect(0);
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool found = false;
|
||||
for (uint32_t i = 0; i < this->parent_->effects_.size(); i++) {
|
||||
LightEffect *e = this->parent_->effects_[i];
|
||||
|
||||
if (strcasecmp(effect.c_str(), e->get_name().c_str()) == 0) {
|
||||
this->set_effect(i + 1);
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
ESP_LOGW(TAG, "'%s' - No such effect '%s'", this->parent_->get_name().c_str(), effect.c_str());
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
LightCall &LightCall::from_light_color_values(const LightColorValues &values) {
|
||||
this->set_state(values.is_on());
|
||||
this->set_brightness_if_supported(values.get_brightness());
|
||||
this->set_red_if_supported(values.get_red());
|
||||
this->set_green_if_supported(values.get_green());
|
||||
this->set_blue_if_supported(values.get_blue());
|
||||
this->set_white_if_supported(values.get_white());
|
||||
this->set_color_temperature_if_supported(values.get_color_temperature());
|
||||
return *this;
|
||||
}
|
||||
LightCall &LightCall::set_transition_length_if_supported(uint32_t transition_length) {
|
||||
if (this->parent_->get_traits().get_supports_brightness())
|
||||
this->set_transition_length(transition_length);
|
||||
return *this;
|
||||
}
|
||||
LightCall &LightCall::set_brightness_if_supported(float brightness) {
|
||||
if (this->parent_->get_traits().get_supports_brightness())
|
||||
this->set_brightness(brightness);
|
||||
return *this;
|
||||
}
|
||||
LightCall &LightCall::set_red_if_supported(float red) {
|
||||
if (this->parent_->get_traits().get_supports_rgb())
|
||||
this->set_red(red);
|
||||
return *this;
|
||||
}
|
||||
LightCall &LightCall::set_green_if_supported(float green) {
|
||||
if (this->parent_->get_traits().get_supports_rgb())
|
||||
this->set_green(green);
|
||||
return *this;
|
||||
}
|
||||
LightCall &LightCall::set_blue_if_supported(float blue) {
|
||||
if (this->parent_->get_traits().get_supports_rgb())
|
||||
this->set_blue(blue);
|
||||
return *this;
|
||||
}
|
||||
LightCall &LightCall::set_white_if_supported(float white) {
|
||||
if (this->parent_->get_traits().get_supports_rgb_white_value())
|
||||
this->set_white(white);
|
||||
return *this;
|
||||
}
|
||||
LightCall &LightCall::set_color_temperature_if_supported(float color_temperature) {
|
||||
if (this->parent_->get_traits().get_supports_color_temperature())
|
||||
this->set_color_temperature(color_temperature);
|
||||
return *this;
|
||||
}
|
||||
LightCall &LightCall::set_state(optional<bool> state) {
|
||||
this->state_ = state;
|
||||
return *this;
|
||||
}
|
||||
LightCall &LightCall::set_state(bool state) {
|
||||
this->state_ = state;
|
||||
return *this;
|
||||
}
|
||||
LightCall &LightCall::set_transition_length(optional<uint32_t> transition_length) {
|
||||
this->transition_length_ = transition_length;
|
||||
return *this;
|
||||
}
|
||||
LightCall &LightCall::set_transition_length(uint32_t transition_length) {
|
||||
this->transition_length_ = transition_length;
|
||||
return *this;
|
||||
}
|
||||
LightCall &LightCall::set_flash_length(optional<uint32_t> flash_length) {
|
||||
this->flash_length_ = flash_length;
|
||||
return *this;
|
||||
}
|
||||
LightCall &LightCall::set_flash_length(uint32_t flash_length) {
|
||||
this->flash_length_ = flash_length;
|
||||
return *this;
|
||||
}
|
||||
LightCall &LightCall::set_brightness(optional<float> brightness) {
|
||||
this->brightness_ = brightness;
|
||||
return *this;
|
||||
}
|
||||
LightCall &LightCall::set_brightness(float brightness) {
|
||||
this->brightness_ = brightness;
|
||||
return *this;
|
||||
}
|
||||
LightCall &LightCall::set_red(optional<float> red) {
|
||||
this->red_ = red;
|
||||
return *this;
|
||||
}
|
||||
LightCall &LightCall::set_red(float red) {
|
||||
this->red_ = red;
|
||||
return *this;
|
||||
}
|
||||
LightCall &LightCall::set_green(optional<float> green) {
|
||||
this->green_ = green;
|
||||
return *this;
|
||||
}
|
||||
LightCall &LightCall::set_green(float green) {
|
||||
this->green_ = green;
|
||||
return *this;
|
||||
}
|
||||
LightCall &LightCall::set_blue(optional<float> blue) {
|
||||
this->blue_ = blue;
|
||||
return *this;
|
||||
}
|
||||
LightCall &LightCall::set_blue(float blue) {
|
||||
this->blue_ = blue;
|
||||
return *this;
|
||||
}
|
||||
LightCall &LightCall::set_white(optional<float> white) {
|
||||
this->white_ = white;
|
||||
return *this;
|
||||
}
|
||||
LightCall &LightCall::set_white(float white) {
|
||||
this->white_ = white;
|
||||
return *this;
|
||||
}
|
||||
LightCall &LightCall::set_color_temperature(optional<float> color_temperature) {
|
||||
this->color_temperature_ = color_temperature;
|
||||
return *this;
|
||||
}
|
||||
LightCall &LightCall::set_color_temperature(float color_temperature) {
|
||||
this->color_temperature_ = color_temperature;
|
||||
return *this;
|
||||
}
|
||||
LightCall &LightCall::set_effect(optional<std::string> effect) {
|
||||
if (effect.has_value())
|
||||
this->set_effect(*effect);
|
||||
return *this;
|
||||
}
|
||||
LightCall &LightCall::set_effect(uint32_t effect_number) {
|
||||
this->effect_ = effect_number;
|
||||
return *this;
|
||||
}
|
||||
LightCall &LightCall::set_effect(optional<uint32_t> effect_number) {
|
||||
this->effect_ = effect_number;
|
||||
return *this;
|
||||
}
|
||||
LightCall &LightCall::set_publish(bool publish) {
|
||||
this->publish_ = publish;
|
||||
return *this;
|
||||
}
|
||||
LightCall &LightCall::set_save(bool save) {
|
||||
this->save_ = save;
|
||||
return *this;
|
||||
}
|
||||
LightCall &LightCall::set_rgb(float red, float green, float blue) {
|
||||
this->set_red(red);
|
||||
this->set_green(green);
|
||||
this->set_blue(blue);
|
||||
return *this;
|
||||
}
|
||||
LightCall &LightCall::set_rgbw(float red, float green, float blue, float white) {
|
||||
this->set_rgb(red, green, blue);
|
||||
this->set_white(white);
|
||||
return *this;
|
||||
}
|
||||
|
||||
float LightState::get_setup_priority() const { return setup_priority::HARDWARE - 1.0f; }
|
||||
LightOutput *LightState::get_output() const { return this->output_; }
|
||||
void LightState::set_gamma_correct(float gamma_correct) { this->gamma_correct_ = gamma_correct; }
|
||||
void LightState::current_values_as_binary(bool *binary) { this->current_values.as_binary(binary); }
|
||||
void LightState::current_values_as_brightness(float *brightness) {
|
||||
this->current_values.as_brightness(brightness, this->gamma_correct_);
|
||||
|
@ -748,19 +188,70 @@ void LightState::current_values_as_cwww(float *cold_white, float *warm_white, bo
|
|||
this->current_values.as_cwww(traits.get_min_mireds(), traits.get_max_mireds(), cold_white, warm_white,
|
||||
this->gamma_correct_, constant_brightness);
|
||||
}
|
||||
void LightState::add_new_remote_values_callback(std::function<void()> &&send_callback) {
|
||||
this->remote_values_callback_.add(std::move(send_callback));
|
||||
}
|
||||
void LightState::add_new_target_state_reached_callback(std::function<void()> &&send_callback) {
|
||||
this->target_state_reached_callback_.add(std::move(send_callback));
|
||||
}
|
||||
|
||||
void LightState::start_effect_(uint32_t effect_index) {
|
||||
this->stop_effect_();
|
||||
if (effect_index == 0)
|
||||
return;
|
||||
|
||||
this->active_effect_index_ = effect_index;
|
||||
auto *effect = this->get_active_effect_();
|
||||
effect->start_internal();
|
||||
}
|
||||
LightEffect *LightState::get_active_effect_() {
|
||||
if (this->active_effect_index_ == 0)
|
||||
return nullptr;
|
||||
else
|
||||
return this->effects_[this->active_effect_index_ - 1];
|
||||
}
|
||||
void LightState::stop_effect_() {
|
||||
auto *effect = this->get_active_effect_();
|
||||
if (effect != nullptr) {
|
||||
effect->stop();
|
||||
}
|
||||
this->active_effect_index_ = 0;
|
||||
}
|
||||
|
||||
void LightState::start_transition_(const LightColorValues &target, uint32_t length) {
|
||||
this->transformer_ = make_unique<LightTransitionTransformer>(millis(), length, this->current_values, target);
|
||||
this->remote_values = this->transformer_->get_remote_values();
|
||||
}
|
||||
|
||||
void LightState::start_flash_(const LightColorValues &target, uint32_t length) {
|
||||
LightColorValues end_colors = this->current_values;
|
||||
// If starting a flash if one is already happening, set end values to end values of current flash
|
||||
// Hacky but works
|
||||
if (this->transformer_ != nullptr)
|
||||
end_colors = this->transformer_->get_end_values();
|
||||
this->transformer_ = make_unique<LightFlashTransformer>(millis(), length, end_colors, target);
|
||||
this->remote_values = this->transformer_->get_remote_values();
|
||||
}
|
||||
|
||||
void LightState::set_immediately_(const LightColorValues &target, bool set_remote_values) {
|
||||
this->transformer_ = nullptr;
|
||||
this->current_values = target;
|
||||
if (set_remote_values) {
|
||||
this->remote_values = target;
|
||||
}
|
||||
this->next_write_ = true;
|
||||
}
|
||||
|
||||
void LightState::set_transformer_(std::unique_ptr<LightTransformer> transformer) {
|
||||
this->transformer_ = std::move(transformer);
|
||||
}
|
||||
|
||||
void LightState::save_remote_values_() {
|
||||
LightStateRTCState saved;
|
||||
saved.state = this->remote_values.is_on();
|
||||
saved.brightness = this->remote_values.get_brightness();
|
||||
saved.red = this->remote_values.get_red();
|
||||
saved.green = this->remote_values.get_green();
|
||||
saved.blue = this->remote_values.get_blue();
|
||||
saved.white = this->remote_values.get_white();
|
||||
saved.color_temp = this->remote_values.get_color_temperature();
|
||||
saved.effect = this->active_effect_index_;
|
||||
this->rtc_.save(&saved);
|
||||
}
|
||||
|
||||
} // namespace light
|
||||
} // namespace esphome
|
||||
|
|
|
@ -5,161 +5,15 @@
|
|||
#include "esphome/core/preferences.h"
|
||||
#include "light_effect.h"
|
||||
#include "light_color_values.h"
|
||||
#include "light_call.h"
|
||||
#include "light_traits.h"
|
||||
#include "light_transformer.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace light {
|
||||
|
||||
class LightState;
|
||||
class LightOutput;
|
||||
|
||||
class LightCall {
|
||||
public:
|
||||
explicit LightCall(LightState *parent) : parent_(parent) {}
|
||||
|
||||
/// Set the binary ON/OFF state of the light.
|
||||
LightCall &set_state(optional<bool> state);
|
||||
/// Set the binary ON/OFF state of the light.
|
||||
LightCall &set_state(bool state);
|
||||
/** Set the transition length of this call in milliseconds.
|
||||
*
|
||||
* This argument is ignored for starting flashes and effects.
|
||||
*
|
||||
* Defaults to the default transition length defined in the light configuration.
|
||||
*/
|
||||
LightCall &set_transition_length(optional<uint32_t> transition_length);
|
||||
/** Set the transition length of this call in milliseconds.
|
||||
*
|
||||
* This argument is ignored for starting flashes and effects.
|
||||
*
|
||||
* Defaults to the default transition length defined in the light configuration.
|
||||
*/
|
||||
LightCall &set_transition_length(uint32_t transition_length);
|
||||
/// Set the transition length property if the light supports transitions.
|
||||
LightCall &set_transition_length_if_supported(uint32_t transition_length);
|
||||
/// Start and set the flash length of this call in milliseconds.
|
||||
LightCall &set_flash_length(optional<uint32_t> flash_length);
|
||||
/// Start and set the flash length of this call in milliseconds.
|
||||
LightCall &set_flash_length(uint32_t flash_length);
|
||||
/// Set the target brightness of the light from 0.0 (fully off) to 1.0 (fully on)
|
||||
LightCall &set_brightness(optional<float> brightness);
|
||||
/// Set the target brightness of the light from 0.0 (fully off) to 1.0 (fully on)
|
||||
LightCall &set_brightness(float brightness);
|
||||
/// Set the brightness property if the light supports brightness.
|
||||
LightCall &set_brightness_if_supported(float brightness);
|
||||
/** Set the red RGB value of the light from 0.0 to 1.0.
|
||||
*
|
||||
* Note that this only controls the color of the light, not its brightness.
|
||||
*/
|
||||
LightCall &set_red(optional<float> red);
|
||||
/** Set the red RGB value of the light from 0.0 to 1.0.
|
||||
*
|
||||
* Note that this only controls the color of the light, not its brightness.
|
||||
*/
|
||||
LightCall &set_red(float red);
|
||||
/// Set the red property if the light supports RGB.
|
||||
LightCall &set_red_if_supported(float red);
|
||||
/** Set the green RGB value of the light from 0.0 to 1.0.
|
||||
*
|
||||
* Note that this only controls the color of the light, not its brightness.
|
||||
*/
|
||||
LightCall &set_green(optional<float> green);
|
||||
/** Set the green RGB value of the light from 0.0 to 1.0.
|
||||
*
|
||||
* Note that this only controls the color of the light, not its brightness.
|
||||
*/
|
||||
LightCall &set_green(float green);
|
||||
/// Set the green property if the light supports RGB.
|
||||
LightCall &set_green_if_supported(float green);
|
||||
/** Set the blue RGB value of the light from 0.0 to 1.0.
|
||||
*
|
||||
* Note that this only controls the color of the light, not its brightness.
|
||||
*/
|
||||
LightCall &set_blue(optional<float> blue);
|
||||
/** Set the blue RGB value of the light from 0.0 to 1.0.
|
||||
*
|
||||
* Note that this only controls the color of the light, not its brightness.
|
||||
*/
|
||||
LightCall &set_blue(float blue);
|
||||
/// Set the blue property if the light supports RGB.
|
||||
LightCall &set_blue_if_supported(float blue);
|
||||
/// Set the white value value of the light from 0.0 to 1.0 for RGBW[W] lights.
|
||||
LightCall &set_white(optional<float> white);
|
||||
/// Set the white value value of the light from 0.0 to 1.0 for RGBW[W] lights.
|
||||
LightCall &set_white(float white);
|
||||
/// Set the white property if the light supports RGB.
|
||||
LightCall &set_white_if_supported(float white);
|
||||
/// Set the color temperature of the light in mireds for CWWW or RGBWW lights.
|
||||
LightCall &set_color_temperature(optional<float> color_temperature);
|
||||
/// Set the color temperature of the light in mireds for CWWW or RGBWW lights.
|
||||
LightCall &set_color_temperature(float color_temperature);
|
||||
/// Set the color_temperature property if the light supports color temperature.
|
||||
LightCall &set_color_temperature_if_supported(float color_temperature);
|
||||
/// Set the effect of the light by its name.
|
||||
LightCall &set_effect(optional<std::string> effect);
|
||||
/// Set the effect of the light by its name.
|
||||
LightCall &set_effect(const std::string &effect);
|
||||
/// Set the effect of the light by its internal index number (only for internal use).
|
||||
LightCall &set_effect(uint32_t effect_number);
|
||||
LightCall &set_effect(optional<uint32_t> effect_number);
|
||||
/// Set whether this light call should trigger a publish state.
|
||||
LightCall &set_publish(bool publish);
|
||||
/// Set whether this light call should trigger a save state to recover them at startup..
|
||||
LightCall &set_save(bool save);
|
||||
|
||||
/** Set the RGB color of the light by RGB values.
|
||||
*
|
||||
* Please note that this only changes the color of the light, not the brightness.
|
||||
*
|
||||
* @param red The red color value from 0.0 to 1.0.
|
||||
* @param green The green color value from 0.0 to 1.0.
|
||||
* @param blue The blue color value from 0.0 to 1.0.
|
||||
* @return The light call for chaining setters.
|
||||
*/
|
||||
LightCall &set_rgb(float red, float green, float blue);
|
||||
/** Set the RGBW color of the light by RGB values.
|
||||
*
|
||||
* Please note that this only changes the color of the light, not the brightness.
|
||||
*
|
||||
* @param red The red color value from 0.0 to 1.0.
|
||||
* @param green The green color value from 0.0 to 1.0.
|
||||
* @param blue The blue color value from 0.0 to 1.0.
|
||||
* @param white The white color value from 0.0 to 1.0.
|
||||
* @return The light call for chaining setters.
|
||||
*/
|
||||
LightCall &set_rgbw(float red, float green, float blue, float white);
|
||||
#ifdef USE_JSON
|
||||
LightCall &parse_color_json(JsonObject &root);
|
||||
LightCall &parse_json(JsonObject &root);
|
||||
#endif
|
||||
LightCall &from_light_color_values(const LightColorValues &values);
|
||||
|
||||
void perform();
|
||||
|
||||
protected:
|
||||
/// Validate all properties and return the target light color values.
|
||||
LightColorValues validate_();
|
||||
|
||||
bool has_transition_() { return this->transition_length_.has_value(); }
|
||||
bool has_flash_() { return this->flash_length_.has_value(); }
|
||||
bool has_effect_() { return this->effect_.has_value(); }
|
||||
|
||||
LightState *parent_;
|
||||
optional<bool> state_;
|
||||
optional<uint32_t> transition_length_;
|
||||
optional<uint32_t> flash_length_;
|
||||
optional<float> brightness_;
|
||||
optional<float> red_;
|
||||
optional<float> green_;
|
||||
optional<float> blue_;
|
||||
optional<float> white_;
|
||||
optional<float> color_temperature_;
|
||||
optional<uint32_t> effect_;
|
||||
bool publish_{true};
|
||||
bool save_{true};
|
||||
};
|
||||
|
||||
enum LightRestoreMode {
|
||||
LIGHT_RESTORE_DEFAULT_OFF,
|
||||
LIGHT_RESTORE_DEFAULT_ON,
|
||||
|
@ -204,14 +58,6 @@ class LightState : public Nameable, public Component {
|
|||
*/
|
||||
LightColorValues current_values;
|
||||
|
||||
/// Deprecated method to access current_values.
|
||||
ESPDEPRECATED("get_current_values() is deprecated, please use .current_values instead.")
|
||||
LightColorValues get_current_values();
|
||||
|
||||
/// Deprecated method to access remote_values.
|
||||
ESPDEPRECATED("get_remote_values() is deprecated, please use .remote_values instead.")
|
||||
LightColorValues get_remote_values();
|
||||
|
||||
/** The remote color values reported to the frontend.
|
||||
*
|
||||
* These are different from the "current" values: For example transitions will
|
||||
|
@ -222,6 +68,14 @@ class LightState : public Nameable, public Component {
|
|||
*/
|
||||
LightColorValues remote_values;
|
||||
|
||||
/// Deprecated method to access current_values.
|
||||
ESPDEPRECATED("get_current_values() is deprecated, please use .current_values instead.")
|
||||
LightColorValues get_current_values();
|
||||
|
||||
/// Deprecated method to access remote_values.
|
||||
ESPDEPRECATED("get_remote_values() is deprecated, please use .remote_values instead.")
|
||||
LightColorValues get_remote_values();
|
||||
|
||||
/// Publish the currently active state to the frontend.
|
||||
void publish_state();
|
||||
|
||||
|
@ -231,29 +85,22 @@ class LightState : public Nameable, public Component {
|
|||
/// Return the name of the current effect, or if no effect is active "None".
|
||||
std::string get_effect_name();
|
||||
|
||||
/** This lets front-end components subscribe to light change events.
|
||||
*
|
||||
* This is different from add_new_current_values_callback in that it only sends events for start
|
||||
* and end values. For example, with transitions it will only send a single callback whereas
|
||||
* the callback passed in add_new_current_values_callback will be called every loop() cycle when
|
||||
* a transition is active
|
||||
*
|
||||
* Note the callback should get the output values through get_remote_values().
|
||||
/**
|
||||
* This lets front-end components subscribe to light change events. This callback is called once
|
||||
* when the remote color values are changed.
|
||||
*
|
||||
* @param send_callback The callback.
|
||||
*/
|
||||
void add_new_remote_values_callback(std::function<void()> &&send_callback);
|
||||
|
||||
/**
|
||||
* The callback is called once the state of current_values and remote_values are equal
|
||||
* The callback is called once the state of current_values and remote_values are equal (when the
|
||||
* transition is finished).
|
||||
*
|
||||
* @param send_callback
|
||||
*/
|
||||
void add_new_target_state_reached_callback(std::function<void()> &&send_callback);
|
||||
|
||||
/// Return whether the light has any effects that meet the trait requirements.
|
||||
bool supports_effects();
|
||||
|
||||
#ifdef USE_JSON
|
||||
/// Dump the state of this light as JSON.
|
||||
void dump_json(JsonObject &root);
|
||||
|
@ -265,10 +112,17 @@ class LightState : public Nameable, public Component {
|
|||
/// Set the gamma correction factor
|
||||
void set_gamma_correct(float gamma_correct);
|
||||
float get_gamma_correct() const { return this->gamma_correct_; }
|
||||
void set_restore_mode(LightRestoreMode restore_mode) { restore_mode_ = restore_mode; }
|
||||
|
||||
/// Set the restore mode of this light
|
||||
void set_restore_mode(LightRestoreMode restore_mode);
|
||||
|
||||
/// Return whether the light has any effects that meet the trait requirements.
|
||||
bool supports_effects();
|
||||
|
||||
/// Get all effects for this light state.
|
||||
const std::vector<LightEffect *> &get_effects() const;
|
||||
|
||||
/// Add effects for this light state.
|
||||
void add_effects(const std::vector<LightEffect *> &effects);
|
||||
|
||||
void current_values_as_binary(bool *binary);
|
||||
|
@ -293,6 +147,8 @@ class LightState : public Nameable, public Component {
|
|||
|
||||
/// Internal method to start an effect with the given index
|
||||
void start_effect_(uint32_t effect_index);
|
||||
/// Internal method to get the currently active effect
|
||||
LightEffect *get_active_effect_();
|
||||
/// Internal method to stop the current effect (if one is active).
|
||||
void stop_effect_();
|
||||
/// Internal method to start a transition to the target color with the given length.
|
||||
|
@ -307,18 +163,21 @@ class LightState : public Nameable, public Component {
|
|||
/// Internal method to start a transformer.
|
||||
void set_transformer_(std::unique_ptr<LightTransformer> transformer);
|
||||
|
||||
LightEffect *get_active_effect_();
|
||||
/// Internal method to save the current remote_values to the preferences
|
||||
void save_remote_values_();
|
||||
|
||||
/// Object used to store the persisted values of the light.
|
||||
ESPPreferenceObject rtc_;
|
||||
/// Restore mode of the light.
|
||||
LightRestoreMode restore_mode_;
|
||||
/// Default transition length for all transitions in ms.
|
||||
uint32_t default_transition_length_{};
|
||||
/// Store the output to allow effects to have more access.
|
||||
LightOutput *output_;
|
||||
/// Value for storing the index of the currently active effect. 0 if no effect is active
|
||||
uint32_t active_effect_index_{};
|
||||
/// The currently active transformer for this light (transition/flash).
|
||||
std::unique_ptr<LightTransformer> transformer_{nullptr};
|
||||
/// Whether the light value should be written in the next cycle.
|
||||
bool next_write_{true};
|
||||
|
||||
/// Object used to store the persisted values of the light.
|
||||
ESPPreferenceObject rtc_;
|
||||
|
||||
/** Callback to call when new values for the frontend are available.
|
||||
*
|
||||
* "Remote values" are light color values that are reported to the frontend and have a lower
|
||||
|
@ -333,11 +192,12 @@ class LightState : public Nameable, public Component {
|
|||
*/
|
||||
CallbackManager<void()> target_state_reached_callback_{};
|
||||
|
||||
LightOutput *output_; ///< Store the output to allow effects to have more access.
|
||||
/// Whether the light value should be written in the next cycle.
|
||||
bool next_write_{true};
|
||||
/// Default transition length for all transitions in ms.
|
||||
uint32_t default_transition_length_{};
|
||||
/// Gamma correction factor for the light.
|
||||
float gamma_correct_{};
|
||||
/// Restore mode of the light.
|
||||
LightRestoreMode restore_mode_;
|
||||
/// List of effects for this light.
|
||||
std::vector<LightEffect *> effects_;
|
||||
};
|
||||
|
|
|
@ -16,6 +16,8 @@
|
|||
//
|
||||
// Modified by Otto Winter on 18.05.18
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace esphome {
|
||||
|
||||
// type for nullopt
|
||||
|
|
Loading…
Reference in a new issue