Split files in light component (#1893)

This commit is contained in:
Oxan van Leeuwen
2021-06-14 18:01:56 +02:00
committed by GitHub
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 -260
View File
@@ -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;
@@ -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
@@ -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
View 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
@@ -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
View 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
@@ -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
View 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
File diff suppressed because it is too large Load Diff
+160
View 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
File diff suppressed because it is too large Load Diff
+38 -178
View File
@@ -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_;
};
+2
View File
@@ -16,6 +16,8 @@
//
// Modified by Otto Winter on 18.05.18
#include <algorithm>
namespace esphome {
// type for nullopt