mirror of
https://github.com/esphome/esphome.git
synced 2025-10-22 19:53:46 +01:00
[light] Use bitmask instead of std::set for color modes
This commit is contained in:
@@ -506,7 +506,7 @@ message ListEntitiesLightResponse {
|
|||||||
string name = 3;
|
string name = 3;
|
||||||
reserved 4; // Deprecated: was string unique_id
|
reserved 4; // Deprecated: was string unique_id
|
||||||
|
|
||||||
repeated ColorMode supported_color_modes = 12 [(container_pointer) = "std::set<light::ColorMode>"];
|
repeated ColorMode supported_color_modes = 12 [(fixed_vector) = true];
|
||||||
// next four supports_* are for legacy clients, newer clients should use color modes
|
// next four supports_* are for legacy clients, newer clients should use color modes
|
||||||
// Deprecated in API version 1.6
|
// Deprecated in API version 1.6
|
||||||
bool legacy_supports_brightness = 5 [deprecated=true];
|
bool legacy_supports_brightness = 5 [deprecated=true];
|
||||||
|
@@ -477,7 +477,11 @@ uint16_t APIConnection::try_send_light_info(EntityBase *entity, APIConnection *c
|
|||||||
auto *light = static_cast<light::LightState *>(entity);
|
auto *light = static_cast<light::LightState *>(entity);
|
||||||
ListEntitiesLightResponse msg;
|
ListEntitiesLightResponse msg;
|
||||||
auto traits = light->get_traits();
|
auto traits = light->get_traits();
|
||||||
msg.supported_color_modes = &traits.get_supported_color_modes_for_api_();
|
const auto &color_modes_mask = traits.get_supported_color_modes();
|
||||||
|
msg.supported_color_modes.init(color_modes_mask.size());
|
||||||
|
for (auto mode : color_modes_mask) {
|
||||||
|
msg.supported_color_modes.push_back(static_cast<enums::ColorMode>(mode));
|
||||||
|
}
|
||||||
if (traits.supports_color_capability(light::ColorCapability::COLOR_TEMPERATURE) ||
|
if (traits.supports_color_capability(light::ColorCapability::COLOR_TEMPERATURE) ||
|
||||||
traits.supports_color_capability(light::ColorCapability::COLD_WARM_WHITE)) {
|
traits.supports_color_capability(light::ColorCapability::COLD_WARM_WHITE)) {
|
||||||
msg.min_mireds = traits.get_min_mireds();
|
msg.min_mireds = traits.get_min_mireds();
|
||||||
|
@@ -471,7 +471,7 @@ void ListEntitiesLightResponse::encode(ProtoWriteBuffer buffer) const {
|
|||||||
buffer.encode_string(1, this->object_id_ref_);
|
buffer.encode_string(1, this->object_id_ref_);
|
||||||
buffer.encode_fixed32(2, this->key);
|
buffer.encode_fixed32(2, this->key);
|
||||||
buffer.encode_string(3, this->name_ref_);
|
buffer.encode_string(3, this->name_ref_);
|
||||||
for (const auto &it : *this->supported_color_modes) {
|
for (auto &it : this->supported_color_modes) {
|
||||||
buffer.encode_uint32(12, static_cast<uint32_t>(it), true);
|
buffer.encode_uint32(12, static_cast<uint32_t>(it), true);
|
||||||
}
|
}
|
||||||
buffer.encode_float(9, this->min_mireds);
|
buffer.encode_float(9, this->min_mireds);
|
||||||
@@ -492,8 +492,8 @@ void ListEntitiesLightResponse::calculate_size(ProtoSize &size) const {
|
|||||||
size.add_length(1, this->object_id_ref_.size());
|
size.add_length(1, this->object_id_ref_.size());
|
||||||
size.add_fixed32(1, this->key);
|
size.add_fixed32(1, this->key);
|
||||||
size.add_length(1, this->name_ref_.size());
|
size.add_length(1, this->name_ref_.size());
|
||||||
if (!this->supported_color_modes->empty()) {
|
if (!this->supported_color_modes.empty()) {
|
||||||
for (const auto &it : *this->supported_color_modes) {
|
for (const auto &it : this->supported_color_modes) {
|
||||||
size.add_uint32_force(1, static_cast<uint32_t>(it));
|
size.add_uint32_force(1, static_cast<uint32_t>(it));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -790,7 +790,7 @@ class ListEntitiesLightResponse final : public InfoResponseProtoMessage {
|
|||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
const char *message_name() const override { return "list_entities_light_response"; }
|
const char *message_name() const override { return "list_entities_light_response"; }
|
||||||
#endif
|
#endif
|
||||||
const std::set<light::ColorMode> *supported_color_modes{};
|
FixedVector<enums::ColorMode> supported_color_modes{};
|
||||||
float min_mireds{0.0f};
|
float min_mireds{0.0f};
|
||||||
float max_mireds{0.0f};
|
float max_mireds{0.0f};
|
||||||
std::vector<std::string> effects{};
|
std::vector<std::string> effects{};
|
||||||
|
@@ -913,7 +913,7 @@ void ListEntitiesLightResponse::dump_to(std::string &out) const {
|
|||||||
dump_field(out, "object_id", this->object_id_ref_);
|
dump_field(out, "object_id", this->object_id_ref_);
|
||||||
dump_field(out, "key", this->key);
|
dump_field(out, "key", this->key);
|
||||||
dump_field(out, "name", this->name_ref_);
|
dump_field(out, "name", this->name_ref_);
|
||||||
for (const auto &it : *this->supported_color_modes) {
|
for (const auto &it : this->supported_color_modes) {
|
||||||
dump_field(out, "supported_color_modes", static_cast<enums::ColorMode>(it), 4);
|
dump_field(out, "supported_color_modes", static_cast<enums::ColorMode>(it), 4);
|
||||||
}
|
}
|
||||||
dump_field(out, "min_mireds", this->min_mireds);
|
dump_field(out, "min_mireds", this->min_mireds);
|
||||||
|
@@ -104,5 +104,132 @@ 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));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Bitmask for storing a set of ColorMode values efficiently.
|
||||||
|
/// Replaces std::set<ColorMode> to eliminate red-black tree overhead (~586 bytes).
|
||||||
|
class ColorModeMask {
|
||||||
|
public:
|
||||||
|
constexpr ColorModeMask() = default;
|
||||||
|
|
||||||
|
/// Support initializer list syntax: {ColorMode::RGB, ColorMode::WHITE}
|
||||||
|
constexpr ColorModeMask(std::initializer_list<ColorMode> modes) {
|
||||||
|
for (auto mode : modes) {
|
||||||
|
this->add(mode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr void add(ColorMode mode) { this->mask_ |= (1 << mode_to_bit(mode)); }
|
||||||
|
|
||||||
|
constexpr bool contains(ColorMode mode) const { return (this->mask_ & (1 << mode_to_bit(mode))) != 0; }
|
||||||
|
|
||||||
|
constexpr size_t size() const {
|
||||||
|
// Count set bits
|
||||||
|
uint16_t n = this->mask_;
|
||||||
|
size_t count = 0;
|
||||||
|
while (n) {
|
||||||
|
count += n & 1;
|
||||||
|
n >>= 1;
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Iterator support for API encoding
|
||||||
|
class Iterator {
|
||||||
|
public:
|
||||||
|
using iterator_category = std::forward_iterator_tag;
|
||||||
|
using value_type = ColorMode;
|
||||||
|
using difference_type = std::ptrdiff_t;
|
||||||
|
using pointer = const ColorMode *;
|
||||||
|
using reference = ColorMode;
|
||||||
|
|
||||||
|
constexpr Iterator(uint16_t mask, int bit) : mask_(mask), bit_(bit) { advance_to_next_set_bit(); }
|
||||||
|
|
||||||
|
constexpr ColorMode operator*() const { return bit_to_mode(bit_); }
|
||||||
|
|
||||||
|
constexpr Iterator &operator++() {
|
||||||
|
++bit_;
|
||||||
|
advance_to_next_set_bit();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr bool operator==(const Iterator &other) const { return bit_ == other.bit_; }
|
||||||
|
|
||||||
|
constexpr bool operator!=(const Iterator &other) const { return !(*this == other); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
constexpr void advance_to_next_set_bit() {
|
||||||
|
while (bit_ < 16 && !(mask_ & (1 << bit_))) {
|
||||||
|
++bit_;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t mask_;
|
||||||
|
int bit_;
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr Iterator begin() const { return Iterator(mask_, 0); }
|
||||||
|
constexpr Iterator end() const { return Iterator(mask_, 16); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
uint16_t mask_{0};
|
||||||
|
|
||||||
|
/// Map ColorMode enum values to bit positions (0-9)
|
||||||
|
static constexpr int mode_to_bit(ColorMode mode) {
|
||||||
|
// Using switch instead of lookup table to avoid RAM usage on ESP8266
|
||||||
|
// The compiler optimizes this efficiently
|
||||||
|
switch (mode) {
|
||||||
|
case ColorMode::UNKNOWN:
|
||||||
|
return 0;
|
||||||
|
case ColorMode::ON_OFF:
|
||||||
|
return 1;
|
||||||
|
case ColorMode::BRIGHTNESS:
|
||||||
|
return 2;
|
||||||
|
case ColorMode::WHITE:
|
||||||
|
return 3;
|
||||||
|
case ColorMode::COLOR_TEMPERATURE:
|
||||||
|
return 4;
|
||||||
|
case ColorMode::COLD_WARM_WHITE:
|
||||||
|
return 5;
|
||||||
|
case ColorMode::RGB:
|
||||||
|
return 6;
|
||||||
|
case ColorMode::RGB_WHITE:
|
||||||
|
return 7;
|
||||||
|
case ColorMode::RGB_COLOR_TEMPERATURE:
|
||||||
|
return 8;
|
||||||
|
case ColorMode::RGB_COLD_WARM_WHITE:
|
||||||
|
return 9;
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr ColorMode bit_to_mode(int bit) {
|
||||||
|
// Using switch instead of lookup table to avoid RAM usage on ESP8266
|
||||||
|
switch (bit) {
|
||||||
|
case 0:
|
||||||
|
return ColorMode::UNKNOWN;
|
||||||
|
case 1:
|
||||||
|
return ColorMode::ON_OFF;
|
||||||
|
case 2:
|
||||||
|
return ColorMode::BRIGHTNESS;
|
||||||
|
case 3:
|
||||||
|
return ColorMode::WHITE;
|
||||||
|
case 4:
|
||||||
|
return ColorMode::COLOR_TEMPERATURE;
|
||||||
|
case 5:
|
||||||
|
return ColorMode::COLD_WARM_WHITE;
|
||||||
|
case 6:
|
||||||
|
return ColorMode::RGB;
|
||||||
|
case 7:
|
||||||
|
return ColorMode::RGB_WHITE;
|
||||||
|
case 8:
|
||||||
|
return ColorMode::RGB_COLOR_TEMPERATURE;
|
||||||
|
case 9:
|
||||||
|
return ColorMode::RGB_COLD_WARM_WHITE;
|
||||||
|
default:
|
||||||
|
return ColorMode::UNKNOWN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace light
|
} // namespace light
|
||||||
} // namespace esphome
|
} // namespace esphome
|
||||||
|
@@ -425,10 +425,10 @@ 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.
|
||||||
std::set<ColorMode> suitable_modes = this->get_suitable_color_modes_();
|
ColorModeMask suitable_modes = this->get_suitable_color_modes_();
|
||||||
|
|
||||||
// Don't change if the current mode is suitable.
|
// Don't change if the current mode is suitable.
|
||||||
if (suitable_modes.count(current_mode) > 0) {
|
if (suitable_modes.contains(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;
|
||||||
@@ -436,7 +436,7 @@ ColorMode LightCall::compute_color_mode_() {
|
|||||||
|
|
||||||
// Use the preferred suitable mode.
|
// Use the preferred suitable mode.
|
||||||
for (auto mode : suitable_modes) {
|
for (auto mode : suitable_modes) {
|
||||||
if (supported_modes.count(mode) == 0)
|
if (!supported_modes.contains(mode))
|
||||||
continue;
|
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(),
|
||||||
@@ -451,7 +451,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;
|
||||||
}
|
}
|
||||||
std::set<ColorMode> LightCall::get_suitable_color_modes_() {
|
ColorModeMask LightCall::get_suitable_color_modes_() {
|
||||||
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 =
|
||||||
|
@@ -1,7 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "light_color_values.h"
|
#include "light_color_values.h"
|
||||||
#include <set>
|
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
|
|
||||||
@@ -187,7 +186,7 @@ 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 for this light call.
|
||||||
std::set<ColorMode> get_suitable_color_modes_();
|
ColorModeMask get_suitable_color_modes_();
|
||||||
/// 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_();
|
||||||
|
|
||||||
|
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
#include "esphome/core/helpers.h"
|
#include "esphome/core/helpers.h"
|
||||||
#include "color_mode.h"
|
#include "color_mode.h"
|
||||||
#include <set>
|
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
|
|
||||||
@@ -19,12 +18,15 @@ class LightTraits {
|
|||||||
public:
|
public:
|
||||||
LightTraits() = default;
|
LightTraits() = default;
|
||||||
|
|
||||||
const std::set<ColorMode> &get_supported_color_modes() const { return this->supported_color_modes_; }
|
const ColorModeMask &get_supported_color_modes() const { return this->supported_color_modes_; }
|
||||||
void set_supported_color_modes(std::set<ColorMode> supported_color_modes) {
|
void set_supported_color_modes(ColorModeMask supported_color_modes) {
|
||||||
this->supported_color_modes_ = std::move(supported_color_modes);
|
this->supported_color_modes_ = supported_color_modes;
|
||||||
|
}
|
||||||
|
void set_supported_color_modes(std::initializer_list<ColorMode> modes) {
|
||||||
|
this->supported_color_modes_ = ColorModeMask(modes);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool supports_color_mode(ColorMode color_mode) const { return this->supported_color_modes_.count(color_mode); }
|
bool supports_color_mode(ColorMode color_mode) const { return this->supported_color_modes_.contains(color_mode); }
|
||||||
bool supports_color_capability(ColorCapability color_capability) const {
|
bool supports_color_capability(ColorCapability color_capability) const {
|
||||||
for (auto mode : this->supported_color_modes_) {
|
for (auto mode : this->supported_color_modes_) {
|
||||||
if (mode & color_capability)
|
if (mode & color_capability)
|
||||||
@@ -59,17 +61,7 @@ class LightTraits {
|
|||||||
void set_max_mireds(float max_mireds) { this->max_mireds_ = max_mireds; }
|
void set_max_mireds(float max_mireds) { this->max_mireds_ = max_mireds; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
#ifdef USE_API
|
ColorModeMask supported_color_modes_{};
|
||||||
// The API connection is a friend class to access internal methods
|
|
||||||
friend class api::APIConnection;
|
|
||||||
// This method returns a reference to the internal color modes set.
|
|
||||||
// It is used by the API to avoid copying data when encoding messages.
|
|
||||||
// Warning: Do not use this method outside of the API connection code.
|
|
||||||
// It returns a reference to internal data that can be invalidated.
|
|
||||||
const std::set<ColorMode> &get_supported_color_modes_for_api_() const { return this->supported_color_modes_; }
|
|
||||||
#endif
|
|
||||||
|
|
||||||
std::set<ColorMode> supported_color_modes_{};
|
|
||||||
float min_mireds_{0};
|
float min_mireds_{0};
|
||||||
float max_mireds_{0};
|
float max_mireds_{0};
|
||||||
};
|
};
|
||||||
|
Reference in New Issue
Block a user