mirror of
https://github.com/esphome/esphome.git
synced 2025-10-23 20:23:50 +01:00
preen
This commit is contained in:
@@ -104,6 +104,9 @@ constexpr ColorModeHelper operator|(ColorModeHelper lhs, ColorMode rhs) {
|
|||||||
return static_cast<ColorMode>(static_cast<uint8_t>(lhs) | static_cast<uint8_t>(rhs));
|
return static_cast<ColorMode>(static_cast<uint8_t>(lhs) | static_cast<uint8_t>(rhs));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Type alias for raw color mode bitmask values
|
||||||
|
using color_mode_bitmask_t = uint16_t;
|
||||||
|
|
||||||
/// Bitmask for storing a set of ColorMode values efficiently.
|
/// Bitmask for storing a set of ColorMode values efficiently.
|
||||||
/// Replaces std::set<ColorMode> to eliminate red-black tree overhead (~586 bytes).
|
/// Replaces std::set<ColorMode> to eliminate red-black tree overhead (~586 bytes).
|
||||||
class ColorModeMask {
|
class ColorModeMask {
|
||||||
@@ -143,7 +146,7 @@ class ColorModeMask {
|
|||||||
using pointer = const ColorMode *;
|
using pointer = const ColorMode *;
|
||||||
using reference = ColorMode;
|
using reference = ColorMode;
|
||||||
|
|
||||||
constexpr Iterator(uint16_t mask, int bit) : mask_(mask), bit_(bit) { advance_to_next_set_bit_(); }
|
constexpr Iterator(color_mode_bitmask_t mask, int bit) : mask_(mask), bit_(bit) { advance_to_next_set_bit_(); }
|
||||||
|
|
||||||
constexpr ColorMode operator*() const { return bit_to_mode(bit_); }
|
constexpr ColorMode operator*() const { return bit_to_mode(bit_); }
|
||||||
|
|
||||||
@@ -159,52 +162,92 @@ class ColorModeMask {
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
constexpr void advance_to_next_set_bit_() {
|
constexpr void advance_to_next_set_bit_() {
|
||||||
while (bit_ < 16 && !(mask_ & (1 << bit_))) {
|
while (bit_ < MAX_BIT_INDEX && !(mask_ & (1 << bit_))) {
|
||||||
++bit_;
|
++bit_;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t mask_;
|
color_mode_bitmask_t mask_;
|
||||||
int bit_;
|
int bit_;
|
||||||
};
|
};
|
||||||
|
|
||||||
constexpr Iterator begin() const { return Iterator(mask_, 0); }
|
constexpr Iterator begin() const { return Iterator(mask_, 0); }
|
||||||
constexpr Iterator end() const { return Iterator(mask_, 16); }
|
constexpr Iterator end() const { return Iterator(mask_, MAX_BIT_INDEX); }
|
||||||
|
|
||||||
/// Get the raw bitmask value for API encoding
|
/// Get the raw bitmask value for API encoding
|
||||||
constexpr uint16_t get_mask() const { return this->mask_; }
|
constexpr color_mode_bitmask_t get_mask() const { return this->mask_; }
|
||||||
|
|
||||||
|
/// Find the first set bit in a bitmask and return the corresponding ColorMode
|
||||||
|
/// Used for optimizing compute_color_mode_() intersection logic
|
||||||
|
static constexpr ColorMode first_mode_from_mask(color_mode_bitmask_t mask) {
|
||||||
|
// Find the position of the first set bit (least significant bit)
|
||||||
|
int bit = 0;
|
||||||
|
while (bit < MAX_BIT_INDEX && !(mask & (1 << bit))) {
|
||||||
|
++bit;
|
||||||
|
}
|
||||||
|
return bit_to_mode(bit);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check if a ColorMode is present in a raw bitmask value
|
||||||
|
/// Useful for checking intersection results without creating a temporary ColorModeMask
|
||||||
|
static constexpr bool mask_contains(color_mode_bitmask_t mask, ColorMode mode) {
|
||||||
|
return (mask & (1 << mode_to_bit(mode))) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Build a bitmask of modes that match the given capability requirements
|
||||||
|
/// @param require_caps Capabilities that must be present in the mode
|
||||||
|
/// @param exclude_caps Capabilities that must not be present in the mode (for none case)
|
||||||
|
/// @return Raw bitmask value
|
||||||
|
static constexpr color_mode_bitmask_t build_mask_matching(uint8_t require_caps, uint8_t exclude_caps = 0) {
|
||||||
|
color_mode_bitmask_t mask = 0;
|
||||||
|
// Check each mode to see if it matches the requirements
|
||||||
|
// Skip UNKNOWN (bit 0), iterate through actual color modes (bits 1-9)
|
||||||
|
for (int bit = 1; bit < COLOR_MODE_COUNT; ++bit) {
|
||||||
|
ColorMode mode = bit_to_mode(bit);
|
||||||
|
uint8_t mode_val = static_cast<uint8_t>(mode);
|
||||||
|
// Mode matches if it has all required caps and none of the excluded caps
|
||||||
|
if ((mode_val & require_caps) == require_caps && (exclude_caps == 0 || (mode_val & exclude_caps) == 0)) {
|
||||||
|
mask |= (1 << bit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return mask;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Using uint16_t instead of uint32_t for more efficient iteration (fewer bits to scan).
|
// Using uint16_t instead of uint32_t for more efficient iteration (fewer bits to scan).
|
||||||
// Currently only 10 ColorMode values exist, so 16 bits is sufficient.
|
// Currently only 10 ColorMode values exist, so 16 bits is sufficient.
|
||||||
// Can be changed to uint32_t if more than 16 color modes are needed in the future.
|
// Can be changed to uint32_t if more than 16 color modes are needed in the future.
|
||||||
// Note: Due to struct padding, uint16_t and uint32_t result in same LightTraits size (12 bytes).
|
// Note: Due to struct padding, uint16_t and uint32_t result in same LightTraits size (12 bytes).
|
||||||
uint16_t mask_{0};
|
color_mode_bitmask_t mask_{0};
|
||||||
|
|
||||||
|
// Constants for ColorMode count and bit range
|
||||||
|
static constexpr int COLOR_MODE_COUNT = 10; // UNKNOWN through RGB_COLD_WARM_WHITE
|
||||||
|
static constexpr int MAX_BIT_INDEX = sizeof(color_mode_bitmask_t) * 8; // Number of bits in bitmask type
|
||||||
|
|
||||||
/// Map ColorMode enum values to bit positions (0-9)
|
/// Map ColorMode enum values to bit positions (0-9)
|
||||||
static constexpr int mode_to_bit(ColorMode mode) {
|
static constexpr int mode_to_bit(ColorMode mode) {
|
||||||
// Using switch instead of lookup table to avoid RAM usage on ESP8266
|
// Using switch instead of lookup table to avoid RAM usage on ESP8266
|
||||||
// The compiler optimizes this efficiently
|
// The compiler optimizes this efficiently
|
||||||
switch (mode) {
|
switch (mode) {
|
||||||
case ColorMode::UNKNOWN:
|
case ColorMode::UNKNOWN: // 0
|
||||||
return 0;
|
return 0;
|
||||||
case ColorMode::ON_OFF:
|
case ColorMode::ON_OFF: // 1
|
||||||
return 1;
|
return 1;
|
||||||
case ColorMode::BRIGHTNESS:
|
case ColorMode::BRIGHTNESS: // 3
|
||||||
return 2;
|
return 2;
|
||||||
case ColorMode::WHITE:
|
case ColorMode::WHITE: // 7
|
||||||
return 3;
|
return 3;
|
||||||
case ColorMode::COLOR_TEMPERATURE:
|
case ColorMode::COLOR_TEMPERATURE: // 11
|
||||||
return 4;
|
return 4;
|
||||||
case ColorMode::COLD_WARM_WHITE:
|
case ColorMode::COLD_WARM_WHITE: // 19
|
||||||
return 5;
|
return 5;
|
||||||
case ColorMode::RGB:
|
case ColorMode::RGB: // 35
|
||||||
return 6;
|
return 6;
|
||||||
case ColorMode::RGB_WHITE:
|
case ColorMode::RGB_WHITE: // 39
|
||||||
return 7;
|
return 7;
|
||||||
case ColorMode::RGB_COLOR_TEMPERATURE:
|
case ColorMode::RGB_COLOR_TEMPERATURE: // 47
|
||||||
return 8;
|
return 8;
|
||||||
case ColorMode::RGB_COLD_WARM_WHITE:
|
case ColorMode::RGB_COLD_WARM_WHITE: // 51
|
||||||
return 9;
|
return 9;
|
||||||
default:
|
default:
|
||||||
return 0;
|
return 0;
|
||||||
@@ -215,25 +258,25 @@ class ColorModeMask {
|
|||||||
// Using switch instead of lookup table to avoid RAM usage on ESP8266
|
// Using switch instead of lookup table to avoid RAM usage on ESP8266
|
||||||
switch (bit) {
|
switch (bit) {
|
||||||
case 0:
|
case 0:
|
||||||
return ColorMode::UNKNOWN;
|
return ColorMode::UNKNOWN; // 0
|
||||||
case 1:
|
case 1:
|
||||||
return ColorMode::ON_OFF;
|
return ColorMode::ON_OFF; // 1
|
||||||
case 2:
|
case 2:
|
||||||
return ColorMode::BRIGHTNESS;
|
return ColorMode::BRIGHTNESS; // 3
|
||||||
case 3:
|
case 3:
|
||||||
return ColorMode::WHITE;
|
return ColorMode::WHITE; // 7
|
||||||
case 4:
|
case 4:
|
||||||
return ColorMode::COLOR_TEMPERATURE;
|
return ColorMode::COLOR_TEMPERATURE; // 11
|
||||||
case 5:
|
case 5:
|
||||||
return ColorMode::COLD_WARM_WHITE;
|
return ColorMode::COLD_WARM_WHITE; // 19
|
||||||
case 6:
|
case 6:
|
||||||
return ColorMode::RGB;
|
return ColorMode::RGB; // 35
|
||||||
case 7:
|
case 7:
|
||||||
return ColorMode::RGB_WHITE;
|
return ColorMode::RGB_WHITE; // 39
|
||||||
case 8:
|
case 8:
|
||||||
return ColorMode::RGB_COLOR_TEMPERATURE;
|
return ColorMode::RGB_COLOR_TEMPERATURE; // 47
|
||||||
case 9:
|
case 9:
|
||||||
return ColorMode::RGB_COLD_WARM_WHITE;
|
return ColorMode::RGB_COLD_WARM_WHITE; // 51
|
||||||
default:
|
default:
|
||||||
return ColorMode::UNKNOWN;
|
return ColorMode::UNKNOWN;
|
||||||
}
|
}
|
||||||
|
@@ -425,20 +425,19 @@ ColorMode LightCall::compute_color_mode_() {
|
|||||||
// If no color mode is specified, we try to guess the color mode. This is needed for backward compatibility to
|
// If no color mode is specified, we try to guess the color mode. This is needed for backward compatibility to
|
||||||
// pre-colormode clients and automations, but also for the MQTT API, where HA doesn't let us know which color mode
|
// pre-colormode clients and automations, but also for the MQTT API, where HA doesn't let us know which color mode
|
||||||
// was used for some reason.
|
// was used for some reason.
|
||||||
ColorModeMask suitable_modes = this->get_suitable_color_modes_();
|
// Compute intersection of suitable and supported modes using bitwise AND
|
||||||
|
color_mode_bitmask_t intersection = this->get_suitable_color_modes_mask_() & supported_modes.get_mask();
|
||||||
|
|
||||||
// Don't change if the current mode is suitable.
|
// Don't change if the current mode is in the intersection (suitable AND supported)
|
||||||
if (suitable_modes.contains(current_mode)) {
|
if (ColorModeMask::mask_contains(intersection, current_mode)) {
|
||||||
ESP_LOGI(TAG, "'%s': color mode not specified; retaining %s", this->parent_->get_name().c_str(),
|
ESP_LOGI(TAG, "'%s': color mode not specified; retaining %s", this->parent_->get_name().c_str(),
|
||||||
LOG_STR_ARG(color_mode_to_human(current_mode)));
|
LOG_STR_ARG(color_mode_to_human(current_mode)));
|
||||||
return current_mode;
|
return current_mode;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use the preferred suitable mode.
|
// Use the preferred suitable mode.
|
||||||
for (auto mode : suitable_modes) {
|
if (intersection != 0) {
|
||||||
if (!supported_modes.contains(mode))
|
ColorMode mode = ColorModeMask::first_mode_from_mask(intersection);
|
||||||
continue;
|
|
||||||
|
|
||||||
ESP_LOGI(TAG, "'%s': color mode not specified; using %s", this->parent_->get_name().c_str(),
|
ESP_LOGI(TAG, "'%s': color mode not specified; using %s", this->parent_->get_name().c_str(),
|
||||||
LOG_STR_ARG(color_mode_to_human(mode)));
|
LOG_STR_ARG(color_mode_to_human(mode)));
|
||||||
return mode;
|
return mode;
|
||||||
@@ -451,7 +450,7 @@ ColorMode LightCall::compute_color_mode_() {
|
|||||||
LOG_STR_ARG(color_mode_to_human(color_mode)));
|
LOG_STR_ARG(color_mode_to_human(color_mode)));
|
||||||
return color_mode;
|
return color_mode;
|
||||||
}
|
}
|
||||||
ColorModeMask LightCall::get_suitable_color_modes_() {
|
color_mode_bitmask_t LightCall::get_suitable_color_modes_mask_() {
|
||||||
bool has_white = this->has_white() && this->white_ > 0.0f;
|
bool has_white = this->has_white() && this->white_ > 0.0f;
|
||||||
bool has_ct = this->has_color_temperature();
|
bool has_ct = this->has_color_temperature();
|
||||||
bool has_cwww =
|
bool has_cwww =
|
||||||
@@ -459,39 +458,27 @@ ColorModeMask LightCall::get_suitable_color_modes_() {
|
|||||||
bool has_rgb = (this->has_color_brightness() && this->color_brightness_ > 0.0f) ||
|
bool has_rgb = (this->has_color_brightness() && this->color_brightness_ > 0.0f) ||
|
||||||
(this->has_red() || this->has_green() || this->has_blue());
|
(this->has_red() || this->has_green() || this->has_blue());
|
||||||
|
|
||||||
// Build key from flags: [rgb][cwww][ct][white]
|
// Build required capabilities mask
|
||||||
#define KEY(white, ct, cwww, rgb) ((white) << 0 | (ct) << 1 | (cwww) << 2 | (rgb) << 3)
|
uint8_t require_caps = static_cast<uint8_t>(ColorCapability::ON_OFF | ColorCapability::BRIGHTNESS);
|
||||||
|
if (has_rgb)
|
||||||
|
require_caps |= static_cast<uint8_t>(ColorCapability::RGB);
|
||||||
|
if (has_white)
|
||||||
|
require_caps |= static_cast<uint8_t>(ColorCapability::WHITE);
|
||||||
|
if (has_ct)
|
||||||
|
require_caps |= static_cast<uint8_t>(ColorCapability::COLOR_TEMPERATURE);
|
||||||
|
if (has_cwww)
|
||||||
|
require_caps |= static_cast<uint8_t>(ColorCapability::COLD_WARM_WHITE);
|
||||||
|
|
||||||
uint8_t key = KEY(has_white, has_ct, has_cwww, has_rgb);
|
// If no specific color parameters set, exclude modes with color capabilities
|
||||||
|
uint8_t exclude_caps = 0;
|
||||||
switch (key) {
|
if (!has_rgb && !has_white && !has_ct && !has_cwww) {
|
||||||
case KEY(true, false, false, false): // white only
|
// For "none" case, we want all modes but don't exclude anything
|
||||||
return {ColorMode::WHITE, ColorMode::RGB_WHITE, ColorMode::RGB_COLOR_TEMPERATURE, ColorMode::COLD_WARM_WHITE,
|
// Just require ON_OFF + BRIGHTNESS which all modes have
|
||||||
ColorMode::RGB_COLD_WARM_WHITE};
|
return ColorModeMask::build_mask_matching(
|
||||||
case KEY(false, true, false, false): // ct only
|
static_cast<uint8_t>(ColorCapability::ON_OFF | ColorCapability::BRIGHTNESS), exclude_caps);
|
||||||
return {ColorMode::COLOR_TEMPERATURE, ColorMode::RGB_COLOR_TEMPERATURE, ColorMode::COLD_WARM_WHITE,
|
|
||||||
ColorMode::RGB_COLD_WARM_WHITE};
|
|
||||||
case KEY(true, true, false, false): // white + ct
|
|
||||||
return {ColorMode::COLD_WARM_WHITE, ColorMode::RGB_COLOR_TEMPERATURE, ColorMode::RGB_COLD_WARM_WHITE};
|
|
||||||
case KEY(false, false, true, false): // cwww only
|
|
||||||
return {ColorMode::COLD_WARM_WHITE, ColorMode::RGB_COLD_WARM_WHITE};
|
|
||||||
case KEY(false, false, false, false): // none
|
|
||||||
return {ColorMode::RGB_WHITE, ColorMode::RGB_COLOR_TEMPERATURE, ColorMode::RGB_COLD_WARM_WHITE, ColorMode::RGB,
|
|
||||||
ColorMode::WHITE, ColorMode::COLOR_TEMPERATURE, ColorMode::COLD_WARM_WHITE};
|
|
||||||
case KEY(true, false, false, true): // rgb + white
|
|
||||||
return {ColorMode::RGB_WHITE, ColorMode::RGB_COLOR_TEMPERATURE, ColorMode::RGB_COLD_WARM_WHITE};
|
|
||||||
case KEY(false, true, false, true): // rgb + ct
|
|
||||||
case KEY(true, true, false, true): // rgb + white + ct
|
|
||||||
return {ColorMode::RGB_COLOR_TEMPERATURE, ColorMode::RGB_COLD_WARM_WHITE};
|
|
||||||
case KEY(false, false, true, true): // rgb + cwww
|
|
||||||
return {ColorMode::RGB_COLD_WARM_WHITE};
|
|
||||||
case KEY(false, false, false, true): // rgb only
|
|
||||||
return {ColorMode::RGB, ColorMode::RGB_WHITE, ColorMode::RGB_COLOR_TEMPERATURE, ColorMode::RGB_COLD_WARM_WHITE};
|
|
||||||
default:
|
|
||||||
return {}; // conflicting flags
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#undef KEY
|
return ColorModeMask::build_mask_matching(require_caps, exclude_caps);
|
||||||
}
|
}
|
||||||
|
|
||||||
LightCall &LightCall::set_effect(const std::string &effect) {
|
LightCall &LightCall::set_effect(const std::string &effect) {
|
||||||
|
@@ -185,8 +185,8 @@ class LightCall {
|
|||||||
|
|
||||||
//// Compute the color mode that should be used for this call.
|
//// Compute the color mode that should be used for this call.
|
||||||
ColorMode compute_color_mode_();
|
ColorMode compute_color_mode_();
|
||||||
/// Get potential color modes for this light call.
|
/// Get potential color modes bitmask for this light call.
|
||||||
ColorModeMask get_suitable_color_modes_();
|
color_mode_bitmask_t get_suitable_color_modes_mask_();
|
||||||
/// Some color modes also can be set using non-native parameters, transform those calls.
|
/// Some color modes also can be set using non-native parameters, transform those calls.
|
||||||
void transform_parameters_();
|
void transform_parameters_();
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user