1
0
mirror of https://github.com/esphome/esphome.git synced 2025-01-19 04:20:56 +00:00

Migrate ESPColor to Color (#1551)

* Migrate ESPColor to Color

* color.h constructor fix

* Updated componets to use Color
Added a using for ESPColor

* Lint fixes

* Fixed value error

* Update display components to use colorutil

* Updated to latest PR comments

* Fixed COLOR_WHITE

* Moved esp_scale to color_utils

* Rename color_utils to display_color_utils
This commit is contained in:
SenexCrenshaw 2021-03-02 09:08:57 -05:00 committed by GitHub
parent b17e0c298e
commit ac25b138f5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 272 additions and 369 deletions

View File

@ -42,11 +42,11 @@ void AdalightLightEffect::reset_frame_(light::AddressableLight &it) {
void AdalightLightEffect::blank_all_leds_(light::AddressableLight &it) { void AdalightLightEffect::blank_all_leds_(light::AddressableLight &it) {
for (int led = it.size(); led-- > 0;) { for (int led = it.size(); led-- > 0;) {
it[led].set(light::ESPColor::BLACK); it[led].set(COLOR_BLACK);
} }
} }
void AdalightLightEffect::apply(light::AddressableLight &it, const light::ESPColor &current_color) { void AdalightLightEffect::apply(light::AddressableLight &it, const Color &current_color) {
const uint32_t now = millis(); const uint32_t now = millis();
if (now - this->last_ack_ >= ADALIGHT_ACK_INTERVAL) { if (now - this->last_ack_ >= ADALIGHT_ACK_INTERVAL) {
@ -130,7 +130,7 @@ AdalightLightEffect::Frame AdalightLightEffect::parse_frame_(light::AddressableL
for (int led = 0; led < accepted_led_count; led++, led_data += 3) { for (int led = 0; led < accepted_led_count; led++, led_data += 3) {
auto white = std::min(std::min(led_data[0], led_data[1]), led_data[2]); auto white = std::min(std::min(led_data[0], led_data[1]), led_data[2]);
it[led].set(light::ESPColor(led_data[0], led_data[1], led_data[2], white)); it[led].set(Color(led_data[0], led_data[1], led_data[2], white));
} }
return CONSUMED; return CONSUMED;

View File

@ -16,7 +16,7 @@ class AdalightLightEffect : public light::AddressableLightEffect, public uart::U
public: public:
void start() override; void start() override;
void stop() override; void stop() override;
void apply(light::AddressableLight &it, const light::ESPColor &current_color) override; void apply(light::AddressableLight &it, const Color &current_color) override;
protected: protected:
enum Frame { enum Frame {

View File

@ -2,22 +2,58 @@ from esphome import config_validation as cv
from esphome import codegen as cg from esphome import codegen as cg
from esphome.const import CONF_BLUE, CONF_GREEN, CONF_ID, CONF_RED, CONF_WHITE from esphome.const import CONF_BLUE, CONF_GREEN, CONF_ID, CONF_RED, CONF_WHITE
ColorStruct = cg.esphome_ns.struct('Color') ColorStruct = cg.esphome_ns.struct("Color")
MULTI_CONF = True MULTI_CONF = True
CONFIG_SCHEMA = cv.Schema({
CONF_RED_INT = "red_int"
CONF_GREEN_INT = "green_int"
CONF_BLUE_INT = "blue_int"
CONF_WHITE_INT = "white_int"
CONFIG_SCHEMA = cv.Schema(
{
cv.Required(CONF_ID): cv.declare_id(ColorStruct), cv.Required(CONF_ID): cv.declare_id(ColorStruct),
cv.Optional(CONF_RED, default=0.0): cv.percentage, cv.Exclusive(CONF_RED, 'red'): cv.percentage,
cv.Optional(CONF_GREEN, default=0.0): cv.percentage, cv.Exclusive(CONF_RED_INT, 'red'): cv.uint8_t,
cv.Optional(CONF_BLUE, default=0.0): cv.percentage, cv.Exclusive(CONF_GREEN, 'green'): cv.percentage,
cv.Optional(CONF_WHITE, default=0.0): cv.percentage, cv.Exclusive(CONF_GREEN_INT, 'green'): cv.uint8_t,
}).extend(cv.COMPONENT_SCHEMA) cv.Exclusive(CONF_BLUE, 'blue'): cv.percentage,
cv.Exclusive(CONF_BLUE_INT, 'blue'): cv.uint8_t,
cv.Exclusive(CONF_WHITE, 'white'): cv.percentage,
cv.Exclusive(CONF_WHITE_INT, 'white'): cv.uint8_t,
}
).extend(cv.COMPONENT_SCHEMA)
def to_code(config): def to_code(config):
r = 0
if CONF_RED in config:
r = int(config[CONF_RED]*255)
elif CONF_RED_INT in config:
r = config[CONF_RED_INT]
g = 0
if CONF_GREEN in config:
g = int(config[CONF_GREEN]*255)
elif CONF_GREEN_INT in config:
g = config[CONF_GREEN_INT]
b = 0
if CONF_BLUE in config:
b = int(config[CONF_BLUE]*255)
elif CONF_BLUE_INT in config:
b = config[CONF_BLUE_INT]
w = 0
if CONF_WHITE in config:
w = int(config[CONF_WHITE]*255)
elif CONF_WHITE_INT in config:
w = config[CONF_WHITE_INT]
cg.variable(config[CONF_ID], cg.StructInitializer( cg.variable(config[CONF_ID], cg.StructInitializer(
ColorStruct, ColorStruct,
('r', config[CONF_RED]), ('r', r),
('g', config[CONF_GREEN]), ('g', g),
('b', config[CONF_BLUE]), ('b', b),
('w', config[CONF_WHITE]))) ('w', w)))

View File

@ -3,7 +3,7 @@
#include "esphome/core/component.h" #include "esphome/core/component.h"
#include "esphome/core/defines.h" #include "esphome/core/defines.h"
#include "esphome/core/automation.h" #include "esphome/core/automation.h"
#include "esphome/core/color.h" #include "display_color_utils.h"
#ifdef USE_TIME #ifdef USE_TIME
#include "esphome/components/time/real_time_clock.h" #include "esphome/components/time/real_time_clock.h"

View File

@ -0,0 +1,110 @@
#pragma once
#include "esphome/core/color.h"
namespace esphome {
namespace display {
enum ColorOrder : uint8_t { COLOR_ORDER_RGB = 0, COLOR_ORDER_BGR = 1, COLOR_ORDER_GRB = 2 };
enum ColorBitness : uint8_t { COLOR_BITNESS_888 = 0, COLOR_BITNESS_565 = 1, COLOR_BITNESS_332 = 2 };
inline static uint8_t esp_scale(uint8_t i, uint8_t scale, uint8_t max_value = 255) { return (max_value * i / scale); }
class ColorUtil {
public:
static Color to_color(uint32_t colorcode, ColorOrder color_order,
ColorBitness color_bitness = ColorBitness::COLOR_BITNESS_888, bool right_bit_aligned = true) {
uint8_t first_color, second_color, third_color;
uint8_t first_bits = 0;
uint8_t second_bits = 0;
uint8_t third_bits = 0;
switch (color_bitness) {
case COLOR_BITNESS_888:
first_bits = 8;
second_bits = 8;
third_bits = 8;
break;
case COLOR_BITNESS_565:
first_bits = 5;
second_bits = 6;
third_bits = 5;
break;
case COLOR_BITNESS_332:
first_bits = 3;
second_bits = 3;
third_bits = 2;
break;
}
first_color = right_bit_aligned ? esp_scale(((colorcode >> (second_bits + third_bits)) & ((1 << first_bits) - 1)),
((1 << first_bits) - 1))
: esp_scale(((colorcode >> 16) & 0xFF), (1 << first_bits) - 1);
second_color = right_bit_aligned
? esp_scale(((colorcode >> third_bits) & ((1 << second_bits) - 1)), ((1 << second_bits) - 1))
: esp_scale(((colorcode >> 8) & 0xFF), ((1 << second_bits) - 1));
third_color = (right_bit_aligned ? esp_scale(((colorcode >> 0) & 0xFF), ((1 << third_bits) - 1))
: esp_scale(((colorcode >> 0) & 0xFF), (1 << third_bits) - 1));
Color color_return;
switch (color_order) {
case COLOR_ORDER_RGB:
color_return.r = first_color;
color_return.g = second_color;
color_return.b = third_color;
break;
case COLOR_ORDER_BGR:
color_return.b = first_color;
color_return.g = second_color;
color_return.r = third_color;
break;
case COLOR_ORDER_GRB:
color_return.g = first_color;
color_return.r = second_color;
color_return.b = third_color;
break;
}
return color_return;
}
static uint8_t color_to_332(Color color, ColorOrder color_order = ColorOrder::COLOR_ORDER_RGB) {
uint16_t red_color, green_color, blue_color;
red_color = esp_scale8(color.red, ((1 << 3) - 1));
green_color = esp_scale8(color.green, ((1 << 3) - 1));
blue_color = esp_scale8(color.blue, (1 << 2) - 1);
switch (color_order) {
case COLOR_ORDER_RGB:
return red_color << 5 | green_color << 2 | blue_color;
case COLOR_ORDER_BGR:
return blue_color << 6 | green_color << 3 | red_color;
case COLOR_ORDER_GRB:
return green_color << 5 | red_color << 2 | blue_color;
}
return 0;
}
static uint16_t color_to_565(Color color, ColorOrder color_order = ColorOrder::COLOR_ORDER_RGB) {
uint16_t red_color, green_color, blue_color;
red_color = esp_scale8(color.red, ((1 << 5) - 1));
green_color = esp_scale8(color.green, ((1 << 6) - 1));
blue_color = esp_scale8(color.blue, (1 << 5) - 1);
switch (color_order) {
case COLOR_ORDER_RGB:
return red_color << 11 | green_color << 5 | blue_color;
case COLOR_ORDER_BGR:
return blue_color << 11 | green_color << 5 | red_color;
case COLOR_ORDER_GRB:
return green_color << 10 | red_color << 5 | blue_color;
}
return 0;
}
static uint32_t color_to_grayscale4(Color color) {
uint32_t gs4 = esp_scale8(color.white, 15);
return gs4;
}
};
} // namespace display
} // namespace esphome

View File

@ -40,7 +40,7 @@ void E131AddressableLightEffect::stop() {
AddressableLightEffect::stop(); AddressableLightEffect::stop();
} }
void E131AddressableLightEffect::apply(light::AddressableLight &it, const light::ESPColor &current_color) { void E131AddressableLightEffect::apply(light::AddressableLight &it, const Color &current_color) {
// ignore, it is run by `E131Component::update()` // ignore, it is run by `E131Component::update()`
} }
@ -63,22 +63,22 @@ bool E131AddressableLightEffect::process_(int universe, const E131Packet &packet
case E131_MONO: case E131_MONO:
for (; output_offset < output_end; output_offset++, input_data++) { for (; output_offset < output_end; output_offset++, input_data++) {
auto output = (*it)[output_offset]; auto output = (*it)[output_offset];
output.set(light::ESPColor(input_data[0], input_data[0], input_data[0], input_data[0])); output.set(Color(input_data[0], input_data[0], input_data[0], input_data[0]));
} }
break; break;
case E131_RGB: case E131_RGB:
for (; output_offset < output_end; output_offset++, input_data += 3) { for (; output_offset < output_end; output_offset++, input_data += 3) {
auto output = (*it)[output_offset]; auto output = (*it)[output_offset];
output.set(light::ESPColor(input_data[0], input_data[1], input_data[2], output.set(
(input_data[0] + input_data[1] + input_data[2]) / 3)); Color(input_data[0], input_data[1], input_data[2], (input_data[0] + input_data[1] + input_data[2]) / 3));
} }
break; break;
case E131_RGBW: case E131_RGBW:
for (; output_offset < output_end; output_offset++, input_data += 4) { for (; output_offset < output_end; output_offset++, input_data += 4) {
auto output = (*it)[output_offset]; auto output = (*it)[output_offset];
output.set(light::ESPColor(input_data[0], input_data[1], input_data[2], input_data[3])); output.set(Color(input_data[0], input_data[1], input_data[2], input_data[3]));
} }
break; break;
} }

View File

@ -18,7 +18,7 @@ class E131AddressableLightEffect : public light::AddressableLightEffect {
public: public:
void start() override; void start() override;
void stop() override; void stop() override;
void apply(light::AddressableLight &it, const light::ESPColor &current_color) override; void apply(light::AddressableLight &it, const Color &current_color) override;
public: public:
int get_data_per_universe() const; int get_data_per_universe() const;

View File

@ -130,7 +130,7 @@ uint8_t ILI9341Display::convert_to_8bit_color_(uint16_t color_16bit) {
} }
void ILI9341Display::fill(Color color) { void ILI9341Display::fill(Color color) {
auto color565 = color.to_rgb_565(); auto color565 = display::ColorUtil::color_to_565(color);
memset(this->buffer_, convert_to_8bit_color_(color565), this->get_buffer_length_()); memset(this->buffer_, convert_to_8bit_color_(color565), this->get_buffer_length_());
this->x_low_ = 0; this->x_low_ = 0;
this->y_low_ = 0; this->y_low_ = 0;
@ -142,7 +142,7 @@ void ILI9341Display::fill_internal_(Color color) {
this->set_addr_window_(0, 0, this->get_width_internal(), this->get_height_internal()); this->set_addr_window_(0, 0, this->get_width_internal(), this->get_height_internal());
this->start_data_(); this->start_data_();
auto color565 = color.to_rgb_565(); auto color565 = display::ColorUtil::color_to_565(color);
for (uint32_t i = 0; i < (this->get_width_internal()) * (this->get_height_internal()); i++) { for (uint32_t i = 0; i < (this->get_width_internal()) * (this->get_height_internal()); i++) {
this->write_byte(color565 >> 8); this->write_byte(color565 >> 8);
this->write_byte(color565); this->write_byte(color565);
@ -162,7 +162,7 @@ void HOT ILI9341Display::draw_absolute_pixel_internal(int x, int y, Color color)
this->y_high_ = (y > this->y_high_) ? y : this->y_high_; this->y_high_ = (y > this->y_high_) ? y : this->y_high_;
uint32_t pos = (y * width_) + x; uint32_t pos = (y * width_) + x;
auto color565 = color.to_rgb_565(); auto color565 = display::ColorUtil::color_to_565(color);
buffer_[pos] = convert_to_8bit_color_(color565); buffer_[pos] = convert_to_8bit_color_(color565);
} }

View File

@ -6,10 +6,7 @@ namespace light {
static const char *TAG = "light.addressable"; static const char *TAG = "light.addressable";
const ESPColor ESPColor::BLACK = ESPColor(0, 0, 0, 0); Color ESPHSVColor::to_rgb() const {
const ESPColor ESPColor::WHITE = ESPColor(255, 255, 255, 255);
ESPColor ESPHSVColor::to_rgb() const {
// based on FastLED's hsv rainbow to rgb // based on FastLED's hsv rainbow to rgb
const uint8_t hue = this->hue; const uint8_t hue = this->hue;
const uint8_t sat = this->saturation; const uint8_t sat = this->saturation;
@ -19,7 +16,7 @@ ESPColor ESPHSVColor::to_rgb() const {
// third of the offset, 255/3 = 85 (actually only up to 82; 164) // third of the offset, 255/3 = 85 (actually only up to 82; 164)
const uint8_t third = esp_scale8(offset8, 85); const uint8_t third = esp_scale8(offset8, 85);
const uint8_t two_thirds = esp_scale8(offset8, 170); const uint8_t two_thirds = esp_scale8(offset8, 170);
ESPColor rgb(255, 255, 255, 0); Color rgb(255, 255, 255, 0);
switch (hue >> 5) { switch (hue >> 5) {
case 0b000: case 0b000:
rgb.r = 255 - third; rgb.r = 255 - third;
@ -76,7 +73,7 @@ ESPColor ESPHSVColor::to_rgb() const {
return rgb; return rgb;
} }
void ESPRangeView::set(const ESPColor &color) { void ESPRangeView::set(const Color &color) {
for (int32_t i = this->begin_; i < this->end_; i++) { for (int32_t i = this->begin_; i < this->end_; i++) {
(*this->parent_)[i] = color; (*this->parent_)[i] = color;
} }
@ -179,12 +176,12 @@ void AddressableLight::call_setup() {
#endif #endif
} }
ESPColor esp_color_from_light_color_values(LightColorValues val) { Color esp_color_from_light_color_values(LightColorValues val) {
auto r = static_cast<uint8_t>(roundf(val.get_red() * 255.0f)); auto r = static_cast<uint8_t>(roundf(val.get_red() * 255.0f));
auto g = static_cast<uint8_t>(roundf(val.get_green() * 255.0f)); auto g = static_cast<uint8_t>(roundf(val.get_green() * 255.0f));
auto b = static_cast<uint8_t>(roundf(val.get_blue() * 255.0f)); auto b = static_cast<uint8_t>(roundf(val.get_blue() * 255.0f));
auto w = static_cast<uint8_t>(roundf(val.get_white() * val.get_state() * 255.0f)); auto w = static_cast<uint8_t>(roundf(val.get_white() * val.get_state() * 255.0f));
return ESPColor(r, g, b, w); return Color(r, g, b, w);
} }
void AddressableLight::write_state(LightState *state) { void AddressableLight::write_state(LightState *state) {
@ -219,7 +216,7 @@ void AddressableLight::write_state(LightState *state) {
this->last_transition_progress_ = new_progress; this->last_transition_progress_ = new_progress;
auto end_values = state->transformer_->get_end_values(); auto end_values = state->transformer_->get_end_values();
ESPColor target_color = esp_color_from_light_color_values(end_values); Color target_color = esp_color_from_light_color_values(end_values);
// our transition will handle brightness, disable brightness in correction. // our transition will handle brightness, disable brightness in correction.
this->correction_.set_local_brightness(255); this->correction_.set_local_brightness(255);
@ -247,7 +244,7 @@ void AddressableLight::write_state(LightState *state) {
if (alpha8 != 0) { if (alpha8 != 0) {
uint8_t inv_alpha8 = 255 - alpha8; uint8_t inv_alpha8 = 255 - alpha8;
ESPColor add = target_color * alpha8; Color add = target_color * alpha8;
for (auto led : *this) for (auto led : *this)
led = add + led.get() * inv_alpha8; led = add + led.get() * inv_alpha8;

View File

@ -2,6 +2,7 @@
#include "esphome/core/component.h" #include "esphome/core/component.h"
#include "esphome/core/defines.h" #include "esphome/core/defines.h"
#include "esphome/core/color.h"
#include "light_output.h" #include "light_output.h"
#include "light_state.h" #include "light_state.h"
@ -12,151 +13,7 @@
namespace esphome { namespace esphome {
namespace light { namespace light {
inline static uint8_t esp_scale8(uint8_t i, uint8_t scale) { return (uint16_t(i) * (1 + uint16_t(scale))) / 256; } using ESPColor = Color;
struct ESPColor {
union {
struct {
union {
uint8_t r;
uint8_t red;
};
union {
uint8_t g;
uint8_t green;
};
union {
uint8_t b;
uint8_t blue;
};
union {
uint8_t w;
uint8_t white;
};
};
uint8_t raw[4];
uint32_t raw_32;
};
inline ESPColor() ALWAYS_INLINE : r(0), g(0), b(0), w(0) {} // NOLINT
inline ESPColor(uint8_t red, uint8_t green, uint8_t blue, uint8_t white) ALWAYS_INLINE : r(red),
g(green),
b(blue),
w(white) {}
inline ESPColor(uint8_t red, uint8_t green, uint8_t blue) ALWAYS_INLINE : r(red), g(green), b(blue), w(0) {}
inline ESPColor(uint32_t colorcode) ALWAYS_INLINE : r((colorcode >> 16) & 0xFF),
g((colorcode >> 8) & 0xFF),
b((colorcode >> 0) & 0xFF),
w((colorcode >> 24) & 0xFF) {}
inline ESPColor(const ESPColor &rhs) ALWAYS_INLINE {
this->r = rhs.r;
this->g = rhs.g;
this->b = rhs.b;
this->w = rhs.w;
}
inline bool is_on() ALWAYS_INLINE { return this->raw_32 != 0; }
inline ESPColor &operator=(const ESPColor &rhs) ALWAYS_INLINE {
this->r = rhs.r;
this->g = rhs.g;
this->b = rhs.b;
this->w = rhs.w;
return *this;
}
inline ESPColor &operator=(uint32_t colorcode) ALWAYS_INLINE {
this->w = (colorcode >> 24) & 0xFF;
this->r = (colorcode >> 16) & 0xFF;
this->g = (colorcode >> 8) & 0xFF;
this->b = (colorcode >> 0) & 0xFF;
return *this;
}
inline uint8_t &operator[](uint8_t x) ALWAYS_INLINE { return this->raw[x]; }
inline ESPColor operator*(uint8_t scale) const ALWAYS_INLINE {
return ESPColor(esp_scale8(this->red, scale), esp_scale8(this->green, scale), esp_scale8(this->blue, scale),
esp_scale8(this->white, scale));
}
inline ESPColor &operator*=(uint8_t scale) ALWAYS_INLINE {
this->red = esp_scale8(this->red, scale);
this->green = esp_scale8(this->green, scale);
this->blue = esp_scale8(this->blue, scale);
this->white = esp_scale8(this->white, scale);
return *this;
}
inline ESPColor operator*(const ESPColor &scale) const ALWAYS_INLINE {
return ESPColor(esp_scale8(this->red, scale.red), esp_scale8(this->green, scale.green),
esp_scale8(this->blue, scale.blue), esp_scale8(this->white, scale.white));
}
inline ESPColor &operator*=(const ESPColor &scale) ALWAYS_INLINE {
this->red = esp_scale8(this->red, scale.red);
this->green = esp_scale8(this->green, scale.green);
this->blue = esp_scale8(this->blue, scale.blue);
this->white = esp_scale8(this->white, scale.white);
return *this;
}
inline ESPColor operator+(const ESPColor &add) const ALWAYS_INLINE {
ESPColor ret;
if (uint8_t(add.r + this->r) < this->r)
ret.r = 255;
else
ret.r = this->r + add.r;
if (uint8_t(add.g + this->g) < this->g)
ret.g = 255;
else
ret.g = this->g + add.g;
if (uint8_t(add.b + this->b) < this->b)
ret.b = 255;
else
ret.b = this->b + add.b;
if (uint8_t(add.w + this->w) < this->w)
ret.w = 255;
else
ret.w = this->w + add.w;
return ret;
}
inline ESPColor &operator+=(const ESPColor &add) ALWAYS_INLINE { return *this = (*this) + add; }
inline ESPColor operator+(uint8_t add) const ALWAYS_INLINE { return (*this) + ESPColor(add, add, add, add); }
inline ESPColor &operator+=(uint8_t add) ALWAYS_INLINE { return *this = (*this) + add; }
inline ESPColor operator-(const ESPColor &subtract) const ALWAYS_INLINE {
ESPColor ret;
if (subtract.r > this->r)
ret.r = 0;
else
ret.r = this->r - subtract.r;
if (subtract.g > this->g)
ret.g = 0;
else
ret.g = this->g - subtract.g;
if (subtract.b > this->b)
ret.b = 0;
else
ret.b = this->b - subtract.b;
if (subtract.w > this->w)
ret.w = 0;
else
ret.w = this->w - subtract.w;
return ret;
}
inline ESPColor &operator-=(const ESPColor &subtract) ALWAYS_INLINE { return *this = (*this) - subtract; }
inline ESPColor operator-(uint8_t subtract) const ALWAYS_INLINE {
return (*this) - ESPColor(subtract, subtract, subtract, subtract);
}
inline ESPColor &operator-=(uint8_t subtract) ALWAYS_INLINE { return *this = (*this) - subtract; }
static ESPColor random_color() {
uint32_t rand = random_uint32();
uint8_t w = rand >> 24;
uint8_t r = rand >> 16;
uint8_t g = rand >> 8;
uint8_t b = rand >> 0;
const uint16_t max_rgb = std::max(r, std::max(g, b));
return ESPColor(uint8_t((uint16_t(r) * 255U / max_rgb)), uint8_t((uint16_t(g) * 255U / max_rgb)),
uint8_t((uint16_t(b) * 255U / max_rgb)), w);
}
ESPColor fade_to_white(uint8_t amnt) const { return ESPColor(255, 255, 255, 255) - (*this * amnt); }
ESPColor fade_to_black(uint8_t amnt) const { return *this * amnt; }
ESPColor lighten(uint8_t delta) const { return *this + delta; }
ESPColor darken(uint8_t delta) const { return *this - delta; }
static const ESPColor BLACK;
static const ESPColor WHITE;
};
struct ESPHSVColor { struct ESPHSVColor {
union { union {
@ -181,18 +38,18 @@ struct ESPHSVColor {
inline ESPHSVColor(uint8_t hue, uint8_t saturation, uint8_t value) ALWAYS_INLINE : hue(hue), inline ESPHSVColor(uint8_t hue, uint8_t saturation, uint8_t value) ALWAYS_INLINE : hue(hue),
saturation(saturation), saturation(saturation),
value(value) {} value(value) {}
ESPColor to_rgb() const; Color to_rgb() const;
}; };
class ESPColorCorrection { class ESPColorCorrection {
public: public:
ESPColorCorrection() : max_brightness_(255, 255, 255, 255) {} ESPColorCorrection() : max_brightness_(255, 255, 255, 255) {}
void set_max_brightness(const ESPColor &max_brightness) { this->max_brightness_ = max_brightness; } 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 set_local_brightness(uint8_t local_brightness) { this->local_brightness_ = local_brightness; }
void calculate_gamma_table(float gamma); void calculate_gamma_table(float gamma);
inline ESPColor color_correct(ESPColor color) const ALWAYS_INLINE { inline Color color_correct(Color color) const ALWAYS_INLINE {
// corrected = (uncorrected * max_brightness * local_brightness) ^ gamma // corrected = (uncorrected * max_brightness * local_brightness) ^ gamma
return ESPColor(this->color_correct_red(color.red), this->color_correct_green(color.green), 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)); this->color_correct_blue(color.blue), this->color_correct_white(color.white));
} }
inline uint8_t color_correct_red(uint8_t red) const ALWAYS_INLINE { inline uint8_t color_correct_red(uint8_t red) const ALWAYS_INLINE {
@ -212,9 +69,9 @@ class ESPColorCorrection {
uint8_t res = esp_scale8(white, this->max_brightness_.white); uint8_t res = esp_scale8(white, this->max_brightness_.white);
return this->gamma_table_[res]; return this->gamma_table_[res];
} }
inline ESPColor color_uncorrect(ESPColor color) const ALWAYS_INLINE { inline Color color_uncorrect(Color color) const ALWAYS_INLINE {
// uncorrected = corrected^(1/gamma) / (max_brightness * local_brightness) // uncorrected = corrected^(1/gamma) / (max_brightness * local_brightness)
return ESPColor(this->color_uncorrect_red(color.red), this->color_uncorrect_green(color.green), 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)); this->color_uncorrect_blue(color.blue), this->color_uncorrect_white(color.white));
} }
inline uint8_t color_uncorrect_red(uint8_t red) const ALWAYS_INLINE { inline uint8_t color_uncorrect_red(uint8_t red) const ALWAYS_INLINE {
@ -249,13 +106,13 @@ class ESPColorCorrection {
protected: protected:
uint8_t gamma_table_[256]; uint8_t gamma_table_[256];
uint8_t gamma_reverse_table_[256]; uint8_t gamma_reverse_table_[256];
ESPColor max_brightness_; Color max_brightness_;
uint8_t local_brightness_{255}; uint8_t local_brightness_{255};
}; };
class ESPColorSettable { class ESPColorSettable {
public: public:
virtual void set(const ESPColor &color) = 0; virtual void set(const Color &color) = 0;
virtual void set_red(uint8_t red) = 0; virtual void set_red(uint8_t red) = 0;
virtual void set_green(uint8_t green) = 0; virtual void set_green(uint8_t green) = 0;
virtual void set_blue(uint8_t blue) = 0; virtual void set_blue(uint8_t blue) = 0;
@ -267,7 +124,7 @@ class ESPColorSettable {
virtual void darken(uint8_t delta) = 0; virtual void darken(uint8_t delta) = 0;
void set(const ESPHSVColor &color) { this->set_hsv(color); } void set(const ESPHSVColor &color) { this->set_hsv(color); }
void set_hsv(const ESPHSVColor &color) { void set_hsv(const ESPHSVColor &color) {
ESPColor rgb = color.to_rgb(); Color rgb = color.to_rgb();
this->set_rgb(rgb.r, rgb.g, rgb.b); this->set_rgb(rgb.r, rgb.g, rgb.b);
} }
void set_rgb(uint8_t red, uint8_t green, uint8_t blue) { void set_rgb(uint8_t red, uint8_t green, uint8_t blue) {
@ -291,7 +148,7 @@ class ESPColorView : public ESPColorSettable {
white_(white), white_(white),
effect_data_(effect_data), effect_data_(effect_data),
color_correction_(color_correction) {} color_correction_(color_correction) {}
ESPColorView &operator=(const ESPColor &rhs) { ESPColorView &operator=(const Color &rhs) {
this->set(rhs); this->set(rhs);
return *this; return *this;
} }
@ -299,7 +156,7 @@ class ESPColorView : public ESPColorSettable {
this->set_hsv(rhs); this->set_hsv(rhs);
return *this; return *this;
} }
void set(const ESPColor &color) override { this->set_rgbw(color.r, color.g, color.b, color.w); } 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_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_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_blue(uint8_t blue) override { *this->blue_ = this->color_correction_->color_correct_blue(blue); }
@ -317,7 +174,7 @@ class ESPColorView : public ESPColorSettable {
void fade_to_black(uint8_t amnt) override { this->set(this->get().fade_to_black(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 lighten(uint8_t delta) override { this->set(this->get().lighten(delta)); }
void darken(uint8_t delta) override { this->set(this->get().darken(delta)); } void darken(uint8_t delta) override { this->set(this->get().darken(delta)); }
ESPColor get() const { return ESPColor(this->get_red(), this->get_green(), this->get_blue(), this->get_white()); } 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() const { return this->color_correction_->color_uncorrect_red(*this->red_); }
uint8_t get_red_raw() const { return *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() const { return this->color_correction_->color_uncorrect_green(*this->green_); }
@ -370,8 +227,8 @@ class ESPRangeView : public ESPColorSettable {
ESPRangeIterator begin(); ESPRangeIterator begin();
ESPRangeIterator end(); ESPRangeIterator end();
void set(const ESPColor &color) override; void set(const Color &color) override;
ESPRangeView &operator=(const ESPColor &rhs) { ESPRangeView &operator=(const Color &rhs) {
this->set(rhs); this->set(rhs);
return *this; return *this;
} }
@ -454,7 +311,7 @@ class AddressableLight : public LightOutput, public Component {
void set_effect_active(bool effect_active) { this->effect_active_ = effect_active; } void set_effect_active(bool effect_active) { this->effect_active_ = effect_active; }
void write_state(LightState *state) override; void write_state(LightState *state) override;
void set_correction(float red, float green, float blue, float white = 1.0f) { void set_correction(float red, float green, float blue, float white = 1.0f) {
this->correction_.set_max_brightness(ESPColor(uint8_t(roundf(red * 255.0f)), uint8_t(roundf(green * 255.0f)), this->correction_.set_max_brightness(Color(uint8_t(roundf(red * 255.0f)), uint8_t(roundf(green * 255.0f)),
uint8_t(roundf(blue * 255.0f)), uint8_t(roundf(white * 255.0f)))); uint8_t(roundf(blue * 255.0f)), uint8_t(roundf(white * 255.0f))));
} }
void setup_state(LightState *state) override { void setup_state(LightState *state) override {

View File

@ -34,12 +34,12 @@ class AddressableLightEffect : public LightEffect {
this->start(); this->start();
} }
void stop() override { this->get_addressable_()->set_effect_active(false); } void stop() override { this->get_addressable_()->set_effect_active(false); }
virtual void apply(AddressableLight &it, const ESPColor &current_color) = 0; virtual void apply(AddressableLight &it, const Color &current_color) = 0;
void apply() override { void apply() override {
LightColorValues color = this->state_->remote_values; LightColorValues color = this->state_->remote_values;
// not using any color correction etc. that will be handled by the addressable layer // not using any color correction etc. that will be handled by the addressable layer
ESPColor current_color = Color current_color =
ESPColor(static_cast<uint8_t>(color.get_red() * 255), static_cast<uint8_t>(color.get_green() * 255), Color(static_cast<uint8_t>(color.get_red() * 255), static_cast<uint8_t>(color.get_green() * 255),
static_cast<uint8_t>(color.get_blue() * 255), static_cast<uint8_t>(color.get_white() * 255)); static_cast<uint8_t>(color.get_blue() * 255), static_cast<uint8_t>(color.get_white() * 255));
this->apply(*this->get_addressable_(), current_color); this->apply(*this->get_addressable_(), current_color);
} }
@ -51,11 +51,11 @@ class AddressableLightEffect : public LightEffect {
class AddressableLambdaLightEffect : public AddressableLightEffect { class AddressableLambdaLightEffect : public AddressableLightEffect {
public: public:
AddressableLambdaLightEffect(const std::string &name, AddressableLambdaLightEffect(const std::string &name,
const std::function<void(AddressableLight &, ESPColor, bool initial_run)> &f, const std::function<void(AddressableLight &, Color, bool initial_run)> &f,
uint32_t update_interval) uint32_t update_interval)
: AddressableLightEffect(name), f_(f), update_interval_(update_interval) {} : AddressableLightEffect(name), f_(f), update_interval_(update_interval) {}
void start() override { this->initial_run_ = true; } void start() override { this->initial_run_ = true; }
void apply(AddressableLight &it, const ESPColor &current_color) override { void apply(AddressableLight &it, const Color &current_color) override {
const uint32_t now = millis(); const uint32_t now = millis();
if (now - this->last_run_ >= this->update_interval_) { if (now - this->last_run_ >= this->update_interval_) {
this->last_run_ = now; this->last_run_ = now;
@ -65,7 +65,7 @@ class AddressableLambdaLightEffect : public AddressableLightEffect {
} }
protected: protected:
std::function<void(AddressableLight &, ESPColor, bool initial_run)> f_; std::function<void(AddressableLight &, Color, bool initial_run)> f_;
uint32_t update_interval_; uint32_t update_interval_;
uint32_t last_run_{0}; uint32_t last_run_{0};
bool initial_run_; bool initial_run_;
@ -74,7 +74,7 @@ class AddressableLambdaLightEffect : public AddressableLightEffect {
class AddressableRainbowLightEffect : public AddressableLightEffect { class AddressableRainbowLightEffect : public AddressableLightEffect {
public: public:
explicit AddressableRainbowLightEffect(const std::string &name) : AddressableLightEffect(name) {} explicit AddressableRainbowLightEffect(const std::string &name) : AddressableLightEffect(name) {}
void apply(AddressableLight &it, const ESPColor &current_color) override { void apply(AddressableLight &it, const Color &current_color) override {
ESPHSVColor hsv; ESPHSVColor hsv;
hsv.value = 255; hsv.value = 255;
hsv.saturation = 240; hsv.saturation = 240;
@ -106,7 +106,7 @@ class AddressableColorWipeEffect : public AddressableLightEffect {
void set_colors(const std::vector<AddressableColorWipeEffectColor> &colors) { this->colors_ = colors; } void set_colors(const std::vector<AddressableColorWipeEffectColor> &colors) { this->colors_ = colors; }
void set_add_led_interval(uint32_t add_led_interval) { this->add_led_interval_ = add_led_interval; } void set_add_led_interval(uint32_t add_led_interval) { this->add_led_interval_ = add_led_interval; }
void set_reverse(bool reverse) { this->reverse_ = reverse; } void set_reverse(bool reverse) { this->reverse_ = reverse; }
void apply(AddressableLight &it, const ESPColor &current_color) override { void apply(AddressableLight &it, const Color &current_color) override {
const uint32_t now = millis(); const uint32_t now = millis();
if (now - this->last_add_ < this->add_led_interval_) if (now - this->last_add_ < this->add_led_interval_)
return; return;
@ -116,7 +116,7 @@ class AddressableColorWipeEffect : public AddressableLightEffect {
else else
it.shift_right(1); it.shift_right(1);
const AddressableColorWipeEffectColor color = this->colors_[this->at_color_]; const AddressableColorWipeEffectColor color = this->colors_[this->at_color_];
const ESPColor esp_color = ESPColor(color.r, color.g, color.b, color.w); const Color esp_color = Color(color.r, color.g, color.b, color.w);
if (this->reverse_) if (this->reverse_)
it[-1] = esp_color; it[-1] = esp_color;
else else
@ -126,7 +126,7 @@ class AddressableColorWipeEffect : public AddressableLightEffect {
this->at_color_ = (this->at_color_ + 1) % this->colors_.size(); this->at_color_ = (this->at_color_ + 1) % this->colors_.size();
AddressableColorWipeEffectColor &new_color = this->colors_[this->at_color_]; AddressableColorWipeEffectColor &new_color = this->colors_[this->at_color_];
if (new_color.random) { if (new_color.random) {
ESPColor c = ESPColor::random_color(); Color c = Color::random_color();
new_color.r = c.r; new_color.r = c.r;
new_color.g = c.g; new_color.g = c.g;
new_color.b = c.b; new_color.b = c.b;
@ -148,8 +148,8 @@ class AddressableScanEffect : public AddressableLightEffect {
explicit AddressableScanEffect(const std::string &name) : AddressableLightEffect(name) {} explicit AddressableScanEffect(const std::string &name) : AddressableLightEffect(name) {}
void set_move_interval(uint32_t move_interval) { this->move_interval_ = move_interval; } void set_move_interval(uint32_t move_interval) { this->move_interval_ = move_interval; }
void set_scan_width(uint32_t scan_width) { this->scan_width_ = scan_width; } void set_scan_width(uint32_t scan_width) { this->scan_width_ = scan_width; }
void apply(AddressableLight &it, const ESPColor &current_color) override { void apply(AddressableLight &it, const Color &current_color) override {
it.all() = ESPColor::BLACK; it.all() = COLOR_BLACK;
for (auto i = 0; i < this->scan_width_; i++) { for (auto i = 0; i < this->scan_width_; i++) {
it[this->at_led_ + i] = current_color; it[this->at_led_ + i] = current_color;
@ -181,7 +181,7 @@ class AddressableScanEffect : public AddressableLightEffect {
class AddressableTwinkleEffect : public AddressableLightEffect { class AddressableTwinkleEffect : public AddressableLightEffect {
public: public:
explicit AddressableTwinkleEffect(const std::string &name) : AddressableLightEffect(name) {} explicit AddressableTwinkleEffect(const std::string &name) : AddressableLightEffect(name) {}
void apply(AddressableLight &addressable, const ESPColor &current_color) override { void apply(AddressableLight &addressable, const Color &current_color) override {
const uint32_t now = millis(); const uint32_t now = millis();
uint8_t pos_add = 0; uint8_t pos_add = 0;
if (now - this->last_progress_ > this->progress_interval_) { if (now - this->last_progress_ > this->progress_interval_) {
@ -199,7 +199,7 @@ class AddressableTwinkleEffect : public AddressableLightEffect {
else else
view.set_effect_data(new_pos); view.set_effect_data(new_pos);
} else { } else {
view = ESPColor::BLACK; view = COLOR_BLACK;
} }
} }
while (random_float() < this->twinkle_probability_) { while (random_float() < this->twinkle_probability_) {
@ -221,7 +221,7 @@ class AddressableTwinkleEffect : public AddressableLightEffect {
class AddressableRandomTwinkleEffect : public AddressableLightEffect { class AddressableRandomTwinkleEffect : public AddressableLightEffect {
public: public:
explicit AddressableRandomTwinkleEffect(const std::string &name) : AddressableLightEffect(name) {} explicit AddressableRandomTwinkleEffect(const std::string &name) : AddressableLightEffect(name) {}
void apply(AddressableLight &it, const ESPColor &current_color) override { void apply(AddressableLight &it, const Color &current_color) override {
const uint32_t now = millis(); const uint32_t now = millis();
uint8_t pos_add = 0; uint8_t pos_add = 0;
if (now - this->last_progress_ > this->progress_interval_) { if (now - this->last_progress_ > this->progress_interval_) {
@ -237,7 +237,7 @@ class AddressableRandomTwinkleEffect : public AddressableLightEffect {
if (color == 0) { if (color == 0) {
view = current_color * sine; view = current_color * sine;
} else { } else {
view = ESPColor(((color >> 2) & 1) * sine, ((color >> 1) & 1) * sine, ((color >> 0) & 1) * sine); view = Color(((color >> 2) & 1) * sine, ((color >> 1) & 1) * sine, ((color >> 0) & 1) * sine);
} }
const uint8_t new_x = x + pos_add; const uint8_t new_x = x + pos_add;
if (new_x > 0b11111) if (new_x > 0b11111)
@ -245,7 +245,7 @@ class AddressableRandomTwinkleEffect : public AddressableLightEffect {
else else
view.set_effect_data((new_x << 3) | color); view.set_effect_data((new_x << 3) | color);
} else { } else {
view = ESPColor(0, 0, 0, 0); view = Color(0, 0, 0, 0);
} }
} }
while (random_float() < this->twinkle_probability_) { while (random_float() < this->twinkle_probability_) {
@ -270,9 +270,9 @@ class AddressableFireworksEffect : public AddressableLightEffect {
explicit AddressableFireworksEffect(const std::string &name) : AddressableLightEffect(name) {} explicit AddressableFireworksEffect(const std::string &name) : AddressableLightEffect(name) {}
void start() override { void start() override {
auto &it = *this->get_addressable_(); auto &it = *this->get_addressable_();
it.all() = ESPColor::BLACK; it.all() = COLOR_BLACK;
} }
void apply(AddressableLight &it, const ESPColor &current_color) override { void apply(AddressableLight &it, const Color &current_color) override {
const uint32_t now = millis(); const uint32_t now = millis();
if (now - this->last_update_ < this->update_interval_) if (now - this->last_update_ < this->update_interval_)
return; return;
@ -280,7 +280,7 @@ class AddressableFireworksEffect : public AddressableLightEffect {
// "invert" the fade out parameter so that higher values make fade out faster // "invert" the fade out parameter so that higher values make fade out faster
const uint8_t fade_out_mult = 255u - this->fade_out_rate_; const uint8_t fade_out_mult = 255u - this->fade_out_rate_;
for (auto view : it) { for (auto view : it) {
ESPColor target = view.get() * fade_out_mult; Color target = view.get() * fade_out_mult;
if (target.r < 64) if (target.r < 64)
target *= 170; target *= 170;
view = target; view = target;
@ -294,7 +294,7 @@ class AddressableFireworksEffect : public AddressableLightEffect {
if (random_float() < this->spark_probability_) { if (random_float() < this->spark_probability_) {
const size_t pos = random_uint32() % it.size(); const size_t pos = random_uint32() % it.size();
if (this->use_random_color_) { if (this->use_random_color_) {
it[pos] = ESPColor::random_color(); it[pos] = Color::random_color();
} else { } else {
it[pos] = current_color; it[pos] = current_color;
} }
@ -316,7 +316,7 @@ class AddressableFireworksEffect : public AddressableLightEffect {
class AddressableFlickerEffect : public AddressableLightEffect { class AddressableFlickerEffect : public AddressableLightEffect {
public: public:
explicit AddressableFlickerEffect(const std::string &name) : AddressableLightEffect(name) {} explicit AddressableFlickerEffect(const std::string &name) : AddressableLightEffect(name) {}
void apply(AddressableLight &it, const ESPColor &current_color) override { void apply(AddressableLight &it, const Color &current_color) override {
const uint32_t now = millis(); const uint32_t now = millis();
const uint8_t intensity = this->intensity_; const uint8_t intensity = this->intensity_;
const uint8_t inv_intensity = 255 - intensity; const uint8_t inv_intensity = 255 - intensity;

View File

@ -1,6 +1,7 @@
import esphome.codegen as cg import esphome.codegen as cg
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome import automation from esphome import automation
from esphome.const import CONF_NAME, CONF_LAMBDA, CONF_UPDATE_INTERVAL, CONF_TRANSITION_LENGTH, \ from esphome.const import CONF_NAME, CONF_LAMBDA, CONF_UPDATE_INTERVAL, CONF_TRANSITION_LENGTH, \
CONF_COLORS, CONF_STATE, CONF_DURATION, CONF_BRIGHTNESS, CONF_RED, CONF_GREEN, CONF_BLUE, \ CONF_COLORS, CONF_STATE, CONF_DURATION, CONF_BRIGHTNESS, CONF_RED, CONF_GREEN, CONF_BLUE, \
CONF_WHITE, CONF_ALPHA, CONF_INTENSITY, CONF_SPEED, CONF_WIDTH, CONF_NUM_LEDS, CONF_RANDOM, \ CONF_WHITE, CONF_ALPHA, CONF_INTENSITY, CONF_SPEED, CONF_WIDTH, CONF_NUM_LEDS, CONF_RANDOM, \
@ -11,7 +12,7 @@ from .types import LambdaLightEffect, RandomLightEffect, StrobeLightEffect, \
FlickerLightEffect, AddressableRainbowLightEffect, AddressableColorWipeEffect, \ FlickerLightEffect, AddressableRainbowLightEffect, AddressableColorWipeEffect, \
AddressableColorWipeEffectColor, AddressableScanEffect, AddressableTwinkleEffect, \ AddressableColorWipeEffectColor, AddressableScanEffect, AddressableTwinkleEffect, \
AddressableRandomTwinkleEffect, AddressableFireworksEffect, AddressableFlickerEffect, \ AddressableRandomTwinkleEffect, AddressableFireworksEffect, AddressableFlickerEffect, \
AutomationLightEffect, ESPColor AutomationLightEffect, Color
CONF_ADD_LED_INTERVAL = 'add_led_interval' CONF_ADD_LED_INTERVAL = 'add_led_interval'
CONF_REVERSE = 'reverse' CONF_REVERSE = 'reverse'
@ -162,7 +163,7 @@ def flicker_effect_to_code(config, effect_id):
} }
) )
def addressable_lambda_effect_to_code(config, effect_id): def addressable_lambda_effect_to_code(config, effect_id):
args = [(AddressableLightRef, 'it'), (ESPColor, 'current_color'), (bool, 'initial_run')] args = [(AddressableLightRef, 'it'), (Color, 'current_color'), (bool, 'initial_run')]
lambda_ = yield cg.process_lambda(config[CONF_LAMBDA], args, return_type=cg.void) lambda_ = yield cg.process_lambda(config[CONF_LAMBDA], args, return_type=cg.void)
var = cg.new_Pvariable(effect_id, config[CONF_NAME], lambda_, var = cg.new_Pvariable(effect_id, config[CONF_NAME], lambda_,
config[CONF_UPDATE_INTERVAL]) config[CONF_UPDATE_INTERVAL])

View File

@ -10,7 +10,7 @@ LightOutput = light_ns.class_('LightOutput')
AddressableLight = light_ns.class_('AddressableLight', cg.Component) AddressableLight = light_ns.class_('AddressableLight', cg.Component)
AddressableLightRef = AddressableLight.operator('ref') AddressableLightRef = AddressableLight.operator('ref')
ESPColor = light_ns.class_('ESPColor') Color = cg.esphome_ns.class_('Color')
LightColorValues = light_ns.class_('LightColorValues') LightColorValues = light_ns.class_('LightColorValues')
# Actions # Actions

View File

@ -2,6 +2,7 @@
#include "esphome/core/component.h" #include "esphome/core/component.h"
#include "esphome/core/helpers.h" #include "esphome/core/helpers.h"
#include "esphome/core/color.h"
#include "esphome/components/light/light_output.h" #include "esphome/components/light/light_output.h"
#include "esphome/components/light/addressable_light.h" #include "esphome/components/light/addressable_light.h"
@ -73,7 +74,7 @@ class NeoPixelBusLightOutputBase : public light::AddressableLight {
// ========== INTERNAL METHODS ========== // ========== INTERNAL METHODS ==========
void setup() override { void setup() override {
for (int i = 0; i < this->size(); i++) { for (int i = 0; i < this->size(); i++) {
(*this)[i] = light::ESPColor(0, 0, 0, 0); (*this)[i] = Color(0, 0, 0, 0);
} }
this->effect_data_ = new uint8_t[this->size()]; this->effect_data_ = new uint8_t[this->size()];

View File

@ -162,7 +162,7 @@ size_t SSD1322::get_buffer_length_() {
void HOT SSD1322::draw_absolute_pixel_internal(int x, int y, Color color) { void HOT SSD1322::draw_absolute_pixel_internal(int x, int y, Color color) {
if (x >= this->get_width_internal() || x < 0 || y >= this->get_height_internal() || y < 0) if (x >= this->get_width_internal() || x < 0 || y >= this->get_height_internal() || y < 0)
return; return;
uint32_t color4 = color.to_grayscale4(); uint32_t color4 = display::ColorUtil::color_to_grayscale4(color);
// where should the bits go in the big buffer array? math... // where should the bits go in the big buffer array? math...
uint16_t pos = (x / SSD1322_PIXELSPERBYTE) + (y * this->get_width_internal() / SSD1322_PIXELSPERBYTE); uint16_t pos = (x / SSD1322_PIXELSPERBYTE) + (y * this->get_width_internal() / SSD1322_PIXELSPERBYTE);
uint8_t shift = (1u - (x % SSD1322_PIXELSPERBYTE)) * SSD1322_COLORSHIFT; uint8_t shift = (1u - (x % SSD1322_PIXELSPERBYTE)) * SSD1322_COLORSHIFT;
@ -174,7 +174,7 @@ void HOT SSD1322::draw_absolute_pixel_internal(int x, int y, Color color) {
this->buffer_[pos] |= color4; this->buffer_[pos] |= color4;
} }
void SSD1322::fill(Color color) { void SSD1322::fill(Color color) {
const uint32_t color4 = color.to_grayscale4(); const uint32_t color4 = display::ColorUtil::color_to_grayscale4(color);
uint8_t fill = (color4 & SSD1322_COLORMASK) | ((color4 & SSD1322_COLORMASK) << SSD1322_COLORSHIFT); uint8_t fill = (color4 & SSD1322_COLORMASK) | ((color4 & SSD1322_COLORMASK) << SSD1322_COLORSHIFT);
for (uint32_t i = 0; i < this->get_buffer_length_(); i++) for (uint32_t i = 0; i < this->get_buffer_length_(); i++)
this->buffer_[i] = fill; this->buffer_[i] = fill;

View File

@ -192,7 +192,7 @@ size_t SSD1325::get_buffer_length_() {
void HOT SSD1325::draw_absolute_pixel_internal(int x, int y, Color color) { void HOT SSD1325::draw_absolute_pixel_internal(int x, int y, Color color) {
if (x >= this->get_width_internal() || x < 0 || y >= this->get_height_internal() || y < 0) if (x >= this->get_width_internal() || x < 0 || y >= this->get_height_internal() || y < 0)
return; return;
uint32_t color4 = color.to_grayscale4(); uint32_t color4 = display::ColorUtil::color_to_grayscale4(color);
// where should the bits go in the big buffer array? math... // where should the bits go in the big buffer array? math...
uint16_t pos = (x / SSD1325_PIXELSPERBYTE) + (y * this->get_width_internal() / SSD1325_PIXELSPERBYTE); uint16_t pos = (x / SSD1325_PIXELSPERBYTE) + (y * this->get_width_internal() / SSD1325_PIXELSPERBYTE);
uint8_t shift = (x % SSD1325_PIXELSPERBYTE) * SSD1325_COLORSHIFT; uint8_t shift = (x % SSD1325_PIXELSPERBYTE) * SSD1325_COLORSHIFT;
@ -204,7 +204,7 @@ void HOT SSD1325::draw_absolute_pixel_internal(int x, int y, Color color) {
this->buffer_[pos] |= color4; this->buffer_[pos] |= color4;
} }
void SSD1325::fill(Color color) { void SSD1325::fill(Color color) {
const uint32_t color4 = color.to_grayscale4(); const uint32_t color4 = display::ColorUtil::color_to_grayscale4(color);
uint8_t fill = (color4 & SSD1325_COLORMASK) | ((color4 & SSD1325_COLORMASK) << SSD1325_COLORSHIFT); uint8_t fill = (color4 & SSD1325_COLORMASK) | ((color4 & SSD1325_COLORMASK) << SSD1325_COLORSHIFT);
for (uint32_t i = 0; i < this->get_buffer_length_(); i++) for (uint32_t i = 0; i < this->get_buffer_length_(); i++)
this->buffer_[i] = fill; this->buffer_[i] = fill;

View File

@ -136,7 +136,7 @@ size_t SSD1327::get_buffer_length_() {
void HOT SSD1327::draw_absolute_pixel_internal(int x, int y, Color color) { void HOT SSD1327::draw_absolute_pixel_internal(int x, int y, Color color) {
if (x >= this->get_width_internal() || x < 0 || y >= this->get_height_internal() || y < 0) if (x >= this->get_width_internal() || x < 0 || y >= this->get_height_internal() || y < 0)
return; return;
uint32_t color4 = color.to_grayscale4(); uint32_t color4 = display::ColorUtil::color_to_grayscale4(color);
// where should the bits go in the big buffer array? math... // where should the bits go in the big buffer array? math...
uint16_t pos = (x / SSD1327_PIXELSPERBYTE) + (y * this->get_width_internal() / SSD1327_PIXELSPERBYTE); uint16_t pos = (x / SSD1327_PIXELSPERBYTE) + (y * this->get_width_internal() / SSD1327_PIXELSPERBYTE);
uint8_t shift = (x % SSD1327_PIXELSPERBYTE) * SSD1327_COLORSHIFT; uint8_t shift = (x % SSD1327_PIXELSPERBYTE) * SSD1327_COLORSHIFT;
@ -148,7 +148,7 @@ void HOT SSD1327::draw_absolute_pixel_internal(int x, int y, Color color) {
this->buffer_[pos] |= color4; this->buffer_[pos] |= color4;
} }
void SSD1327::fill(Color color) { void SSD1327::fill(Color color) {
const uint32_t color4 = color.to_grayscale4(); const uint32_t color4 = display::ColorUtil::color_to_grayscale4(color);
uint8_t fill = (color4 & SSD1327_COLORMASK) | ((color4 & SSD1327_COLORMASK) << SSD1327_COLORSHIFT); uint8_t fill = (color4 & SSD1327_COLORMASK) | ((color4 & SSD1327_COLORMASK) << SSD1327_COLORSHIFT);
for (uint32_t i = 0; i < this->get_buffer_length_(); i++) for (uint32_t i = 0; i < this->get_buffer_length_(); i++)
this->buffer_[i] = fill; this->buffer_[i] = fill;

View File

@ -123,14 +123,14 @@ size_t SSD1331::get_buffer_length_() {
void HOT SSD1331::draw_absolute_pixel_internal(int x, int y, Color color) { void HOT SSD1331::draw_absolute_pixel_internal(int x, int y, Color color) {
if (x >= this->get_width_internal() || x < 0 || y >= this->get_height_internal() || y < 0) if (x >= this->get_width_internal() || x < 0 || y >= this->get_height_internal() || y < 0)
return; return;
const uint32_t color565 = color.to_rgb_565(); const uint32_t color565 = display::ColorUtil::color_to_565(color);
// where should the bits go in the big buffer array? math... // where should the bits go in the big buffer array? math...
uint16_t pos = (x + y * this->get_width_internal()) * SSD1331_BYTESPERPIXEL; uint16_t pos = (x + y * this->get_width_internal()) * SSD1331_BYTESPERPIXEL;
this->buffer_[pos++] = (color565 >> 8) & 0xff; this->buffer_[pos++] = (color565 >> 8) & 0xff;
this->buffer_[pos] = color565 & 0xff; this->buffer_[pos] = color565 & 0xff;
} }
void SSD1331::fill(Color color) { void SSD1331::fill(Color color) {
const uint32_t color565 = color.to_rgb_565(); const uint32_t color565 = display::ColorUtil::color_to_565(color);
for (uint32_t i = 0; i < this->get_buffer_length_(); i++) for (uint32_t i = 0; i < this->get_buffer_length_(); i++)
if (i & 1) { if (i & 1) {
this->buffer_[i] = color565 & 0xff; this->buffer_[i] = color565 & 0xff;

View File

@ -151,14 +151,14 @@ size_t SSD1351::get_buffer_length_() {
void HOT SSD1351::draw_absolute_pixel_internal(int x, int y, Color color) { void HOT SSD1351::draw_absolute_pixel_internal(int x, int y, Color color) {
if (x >= this->get_width_internal() || x < 0 || y >= this->get_height_internal() || y < 0) if (x >= this->get_width_internal() || x < 0 || y >= this->get_height_internal() || y < 0)
return; return;
const uint32_t color565 = color.to_rgb_565(); const uint32_t color565 = display::ColorUtil::color_to_565(color);
// where should the bits go in the big buffer array? math... // where should the bits go in the big buffer array? math...
uint16_t pos = (x + y * this->get_width_internal()) * SSD1351_BYTESPERPIXEL; uint16_t pos = (x + y * this->get_width_internal()) * SSD1351_BYTESPERPIXEL;
this->buffer_[pos++] = (color565 >> 8) & 0xff; this->buffer_[pos++] = (color565 >> 8) & 0xff;
this->buffer_[pos] = color565 & 0xff; this->buffer_[pos] = color565 & 0xff;
} }
void SSD1351::fill(Color color) { void SSD1351::fill(Color color) {
const uint32_t color565 = color.to_rgb_565(); const uint32_t color565 = display::ColorUtil::color_to_565(color);
for (uint32_t i = 0; i < this->get_buffer_length_(); i++) for (uint32_t i = 0; i < this->get_buffer_length_(); i++)
if (i & 1) { if (i & 1) {
this->buffer_[i] = color565 & 0xff; this->buffer_[i] = color565 & 0xff;

View File

@ -308,11 +308,11 @@ void HOT ST7735::draw_absolute_pixel_internal(int x, int y, Color color) {
return; return;
if (this->eightbitcolor_) { if (this->eightbitcolor_) {
const uint32_t color332 = color.to_332(); const uint32_t color332 = display::ColorUtil::color_to_332(color);
uint16_t pos = (x + y * this->get_width_internal()); uint16_t pos = (x + y * this->get_width_internal());
this->buffer_[pos] = color332; this->buffer_[pos] = color332;
} else { } else {
const uint32_t color565 = color.to_565(); const uint32_t color565 = display::ColorUtil::color_to_565(color);
uint16_t pos = (x + y * this->get_width_internal()) * 2; uint16_t pos = (x + y * this->get_width_internal()) * 2;
this->buffer_[pos++] = (color565 >> 8) & 0xff; this->buffer_[pos++] = (color565 >> 8) & 0xff;
this->buffer_[pos] = color565 & 0xff; this->buffer_[pos] = color565 & 0xff;
@ -444,9 +444,11 @@ void HOT ST7735::write_display_data_() {
if (this->eightbitcolor_) { if (this->eightbitcolor_) {
for (int line = 0; line < this->get_buffer_length(); line = line + this->get_width_internal()) { for (int line = 0; line < this->get_buffer_length(); line = line + this->get_width_internal()) {
for (int index = 0; index < this->get_width_internal(); ++index) { for (int index = 0; index < this->get_width_internal(); ++index) {
auto color = Color(this->buffer_[index + line], Color::ColorOrder::COLOR_ORDER_RGB, auto color332 = display::ColorUtil::to_color(this->buffer_[index + line], display::ColorOrder::COLOR_ORDER_RGB,
Color::ColorBitness::COLOR_BITNESS_332, true) display::ColorBitness::COLOR_BITNESS_332, true);
.to_565();
auto color = display::ColorUtil::color_to_565(color332);
this->write_byte((color >> 8) & 0xff); this->write_byte((color >> 8) & 0xff);
this->write_byte(color & 0xff); this->write_byte(color & 0xff);
} }

View File

@ -263,7 +263,7 @@ void HOT ST7789V::draw_absolute_pixel_internal(int x, int y, Color color) {
if (x >= this->get_width_internal() || x < 0 || y >= this->get_height_internal() || y < 0) if (x >= this->get_width_internal() || x < 0 || y >= this->get_height_internal() || y < 0)
return; return;
auto color565 = color.to_rgb_565(); auto color565 = display::ColorUtil::color_to_565(color);
uint16_t pos = (x + y * this->get_width_internal()) * 2; uint16_t pos = (x + y * this->get_width_internal()) * 2;
this->buffer_[pos++] = (color565 >> 8) & 0xff; this->buffer_[pos++] = (color565 >> 8) & 0xff;

View File

@ -40,11 +40,11 @@ void WLEDLightEffect::stop() {
void WLEDLightEffect::blank_all_leds_(light::AddressableLight &it) { void WLEDLightEffect::blank_all_leds_(light::AddressableLight &it) {
for (int led = it.size(); led-- > 0;) { for (int led = it.size(); led-- > 0;) {
it[led].set(light::ESPColor::BLACK); it[led].set(COLOR_BLACK);
} }
} }
void WLEDLightEffect::apply(light::AddressableLight &it, const light::ESPColor &current_color) { void WLEDLightEffect::apply(light::AddressableLight &it, const Color &current_color) {
// Init UDP lazily // Init UDP lazily
if (!udp_) { if (!udp_) {
udp_.reset(new WiFiUDP()); udp_.reset(new WiFiUDP());
@ -152,7 +152,7 @@ bool WLEDLightEffect::parse_warls_frame_(light::AddressableLight &it, const uint
uint8_t b = payload[3]; uint8_t b = payload[3];
if (led < max_leds) { if (led < max_leds) {
it[led].set(light::ESPColor(r, g, b)); it[led].set(Color(r, g, b));
} }
} }
@ -174,7 +174,7 @@ bool WLEDLightEffect::parse_drgb_frame_(light::AddressableLight &it, const uint8
uint8_t b = payload[2]; uint8_t b = payload[2];
if (led < max_leds) { if (led < max_leds) {
it[led].set(light::ESPColor(r, g, b)); it[led].set(Color(r, g, b));
} }
} }
@ -197,7 +197,7 @@ bool WLEDLightEffect::parse_drgbw_frame_(light::AddressableLight &it, const uint
uint8_t w = payload[3]; uint8_t w = payload[3];
if (led < max_leds) { if (led < max_leds) {
it[led].set(light::ESPColor(r, g, b, w)); it[led].set(Color(r, g, b, w));
} }
} }
@ -228,7 +228,7 @@ bool WLEDLightEffect::parse_dnrgb_frame_(light::AddressableLight &it, const uint
uint8_t b = payload[2]; uint8_t b = payload[2];
if (led < max_leds) { if (led < max_leds) {
it[led].set(light::ESPColor(r, g, b)); it[led].set(Color(r, g, b));
} }
} }

View File

@ -18,7 +18,7 @@ class WLEDLightEffect : public light::AddressableLightEffect {
public: public:
void start() override; void start() override;
void stop() override; void stop() override;
void apply(light::AddressableLight &it, const light::ESPColor &current_color) override; void apply(light::AddressableLight &it, const Color &current_color) override;
void set_port(uint16_t port) { this->port_ = port; } void set_port(uint16_t port) { this->port_ = port; }
protected: protected:

View File

@ -6,7 +6,6 @@
namespace esphome { namespace esphome {
inline static uint8_t esp_scale8(uint8_t i, uint8_t scale) { return (uint16_t(i) * (1 + uint16_t(scale))) / 256; } inline static uint8_t esp_scale8(uint8_t i, uint8_t scale) { return (uint16_t(i) * (1 + uint16_t(scale))) / 256; }
inline static uint8_t esp_scale(uint8_t i, uint8_t scale, uint8_t max_value = 255) { return (max_value * i / scale); }
struct Color { struct Color {
union { union {
@ -31,75 +30,19 @@ struct Color {
uint8_t raw[4]; uint8_t raw[4];
uint32_t raw_32; uint32_t raw_32;
}; };
enum ColorOrder : uint8_t { COLOR_ORDER_RGB = 0, COLOR_ORDER_BGR = 1, COLOR_ORDER_GRB = 2 };
enum ColorBitness : uint8_t { COLOR_BITNESS_888 = 0, COLOR_BITNESS_565 = 1, COLOR_BITNESS_332 = 2 };
inline Color() ALWAYS_INLINE : r(0), g(0), b(0), w(0) {} // NOLINT inline Color() ALWAYS_INLINE : r(0), g(0), b(0), w(0) {} // NOLINT
inline Color(float red, float green, float blue) ALWAYS_INLINE : r(uint8_t(red * 255)), inline Color(uint8_t red, uint8_t green, uint8_t blue) ALWAYS_INLINE : r(red), g(green), b(blue), w(0) {}
g(uint8_t(green * 255)),
b(uint8_t(blue * 255)), inline Color(uint8_t red, uint8_t green, uint8_t blue, uint8_t white) ALWAYS_INLINE : r(red),
w(0) {} g(green),
inline Color(float red, float green, float blue, float white) ALWAYS_INLINE : r(uint8_t(red * 255)), b(blue),
g(uint8_t(green * 255)), w(white) {}
b(uint8_t(blue * 255)),
w(uint8_t(white * 255)) {}
inline Color(uint32_t colorcode) ALWAYS_INLINE : r((colorcode >> 16) & 0xFF), inline Color(uint32_t colorcode) ALWAYS_INLINE : r((colorcode >> 16) & 0xFF),
g((colorcode >> 8) & 0xFF), g((colorcode >> 8) & 0xFF),
b((colorcode >> 0) & 0xFF), b((colorcode >> 0) & 0xFF),
w((colorcode >> 24) & 0xFF) {} w((colorcode >> 24) & 0xFF) {}
inline Color(uint32_t colorcode, ColorOrder color_order, ColorBitness color_bitness = ColorBitness::COLOR_BITNESS_888,
bool right_bit_aligned = true) {
uint8_t first_color, second_color, third_color;
uint8_t first_bits = 0;
uint8_t second_bits = 0;
uint8_t third_bits = 0;
switch (color_bitness) {
case COLOR_BITNESS_888:
first_bits = 8;
second_bits = 8;
third_bits = 8;
break;
case COLOR_BITNESS_565:
first_bits = 5;
second_bits = 6;
third_bits = 5;
break;
case COLOR_BITNESS_332:
first_bits = 3;
second_bits = 3;
third_bits = 2;
break;
}
first_color = right_bit_aligned ? esp_scale(((colorcode >> (second_bits + third_bits)) & ((1 << first_bits) - 1)),
((1 << first_bits) - 1))
: esp_scale(((colorcode >> 16) & 0xFF), (1 << first_bits) - 1);
second_color = right_bit_aligned
? esp_scale(((colorcode >> third_bits) & ((1 << second_bits) - 1)), ((1 << second_bits) - 1))
: esp_scale(((colorcode >> 8) & 0xFF), ((1 << second_bits) - 1));
third_color = (right_bit_aligned ? esp_scale(((colorcode >> 0) & 0xFF), ((1 << third_bits) - 1))
: esp_scale(((colorcode >> 0) & 0xFF), (1 << third_bits) - 1));
switch (color_order) {
case COLOR_ORDER_RGB:
this->r = first_color;
this->g = second_color;
this->b = third_color;
break;
case COLOR_ORDER_BGR:
this->b = first_color;
this->g = second_color;
this->r = third_color;
break;
case COLOR_ORDER_GRB:
this->g = first_color;
this->r = second_color;
this->b = third_color;
break;
}
}
inline bool is_on() ALWAYS_INLINE { return this->raw_32 != 0; } inline bool is_on() ALWAYS_INLINE { return this->raw_32 != 0; }
inline Color &operator=(const Color &rhs) ALWAYS_INLINE { inline Color &operator=(const Color &rhs) ALWAYS_INLINE {
this->r = rhs.r; this->r = rhs.r;
@ -187,65 +130,21 @@ struct Color {
} }
inline Color &operator-=(uint8_t subtract) ALWAYS_INLINE { return *this = (*this) - subtract; } inline Color &operator-=(uint8_t subtract) ALWAYS_INLINE { return *this = (*this) - subtract; }
static Color random_color() { static Color random_color() {
float r = float(random_uint32()) / float(UINT32_MAX); uint32_t rand = random_uint32();
float g = float(random_uint32()) / float(UINT32_MAX); uint8_t w = rand >> 24;
float b = float(random_uint32()) / float(UINT32_MAX); uint8_t r = rand >> 16;
float w = float(random_uint32()) / float(UINT32_MAX); uint8_t g = rand >> 8;
return Color(r, g, b, w); uint8_t b = rand >> 0;
const uint16_t max_rgb = std::max(r, std::max(g, b));
return Color(uint8_t((uint16_t(r) * 255U / max_rgb)), uint8_t((uint16_t(g) * 255U / max_rgb)),
uint8_t((uint16_t(b) * 255U / max_rgb)), w);
} }
Color fade_to_white(uint8_t amnt) { return Color(1, 1, 1, 1) - (*this * amnt); } Color fade_to_white(uint8_t amnt) { return Color(255, 255, 255, 255) - (*this * amnt); }
Color fade_to_black(uint8_t amnt) { return *this * amnt; } Color fade_to_black(uint8_t amnt) { return *this * amnt; }
Color lighten(uint8_t delta) { return *this + delta; } Color lighten(uint8_t delta) { return *this + delta; }
Color darken(uint8_t delta) { return *this - delta; } Color darken(uint8_t delta) { return *this - delta; }
uint8_t to_332(ColorOrder color_order = ColorOrder::COLOR_ORDER_RGB) const {
uint16_t red_color, green_color, blue_color;
red_color = esp_scale8(this->red, ((1 << 3) - 1));
green_color = esp_scale8(this->green, ((1 << 3) - 1));
blue_color = esp_scale8(this->blue, (1 << 2) - 1);
switch (color_order) {
case COLOR_ORDER_RGB:
return red_color << 5 | green_color << 2 | blue_color;
case COLOR_ORDER_BGR:
return blue_color << 6 | green_color << 3 | red_color;
case COLOR_ORDER_GRB:
return green_color << 5 | red_color << 2 | blue_color;
}
return 0;
}
uint16_t to_565(ColorOrder color_order = ColorOrder::COLOR_ORDER_RGB) const {
uint16_t red_color, green_color, blue_color;
red_color = esp_scale8(this->red, ((1 << 5) - 1));
green_color = esp_scale8(this->green, ((1 << 6) - 1));
blue_color = esp_scale8(this->blue, (1 << 5) - 1);
switch (color_order) {
case COLOR_ORDER_RGB:
return red_color << 11 | green_color << 5 | blue_color;
case COLOR_ORDER_BGR:
return blue_color << 11 | green_color << 5 | red_color;
case COLOR_ORDER_GRB:
return green_color << 10 | red_color << 5 | blue_color;
}
return 0;
}
uint32_t to_rgb_565() const {
uint32_t color565 =
(esp_scale8(this->red, 31) << 11) | (esp_scale8(this->green, 63) << 5) | (esp_scale8(this->blue, 31) << 0);
return color565;
}
uint32_t to_bgr_565() const {
uint32_t color565 =
(esp_scale8(this->blue, 31) << 11) | (esp_scale8(this->green, 63) << 5) | (esp_scale8(this->red, 31) << 0);
return color565;
}
uint32_t to_grayscale4() const {
uint32_t gs4 = esp_scale8(this->white, 15);
return gs4;
}
}; };
static const Color COLOR_BLACK(0, 0, 0); static const Color COLOR_BLACK(0, 0, 0);
static const Color COLOR_WHITE(1, 1, 1); static const Color COLOR_WHITE(255, 255, 255, 255);
}; // namespace esphome }; // namespace esphome

View File

@ -1693,7 +1693,7 @@ interval:
color: color:
- id: kbx_red - id: kbx_red
red: 100% red: 100%
green: 1% green_int: 123
blue: 2% blue: 2%
- id: kbx_blue - id: kbx_blue
red: 0% red: 0%