mirror of
https://github.com/esphome/esphome.git
synced 2026-01-20 01:49:11 -07:00
Compare commits
7 Commits
dev
...
select_fix
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3291488a8b | ||
|
|
75550b39f4 | ||
|
|
02e1ed2130 | ||
|
|
2948264917 | ||
|
|
660411ac42 | ||
|
|
88e3f02c9c | ||
|
|
f3f419077b |
@@ -1143,7 +1143,7 @@ message ListEntitiesSelectResponse {
|
|||||||
reserved 4; // Deprecated: was string unique_id
|
reserved 4; // Deprecated: was string unique_id
|
||||||
|
|
||||||
string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON"];
|
string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON"];
|
||||||
repeated string options = 6 [(container_pointer) = "std::vector"];
|
repeated string options = 6 [(container_pointer) = "FixedVector"];
|
||||||
bool disabled_by_default = 7;
|
bool disabled_by_default = 7;
|
||||||
EntityCategory entity_category = 8;
|
EntityCategory entity_category = 8;
|
||||||
uint32 device_id = 9 [(field_ifdef) = "USE_DEVICES"];
|
uint32 device_id = 9 [(field_ifdef) = "USE_DEVICES"];
|
||||||
|
|||||||
@@ -1534,7 +1534,7 @@ class ListEntitiesSelectResponse final : public InfoResponseProtoMessage {
|
|||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
const char *message_name() const override { return "list_entities_select_response"; }
|
const char *message_name() const override { return "list_entities_select_response"; }
|
||||||
#endif
|
#endif
|
||||||
const std::vector<std::string> *options{};
|
const FixedVector<std::string> *options{};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(ProtoSize &size) const override;
|
void calculate_size(ProtoSize &size) const override;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
|
|||||||
@@ -9,7 +9,8 @@ static const char *const TAG = "copy.select";
|
|||||||
void CopySelect::setup() {
|
void CopySelect::setup() {
|
||||||
source_->add_on_state_callback([this](const std::string &value, size_t index) { this->publish_state(value); });
|
source_->add_on_state_callback([this](const std::string &value, size_t index) { this->publish_state(value); });
|
||||||
|
|
||||||
traits.set_options(source_->traits.get_options());
|
// Copy options from source select
|
||||||
|
this->traits.copy_options(source_->traits.get_options());
|
||||||
|
|
||||||
if (source_->has_state())
|
if (source_->has_state())
|
||||||
this->publish_state(source_->state);
|
this->publish_state(source_->state);
|
||||||
|
|||||||
@@ -300,11 +300,11 @@ void LvSelectable::set_selected_text(const std::string &text, lv_anim_enable_t a
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void LvSelectable::set_options(std::vector<std::string> options) {
|
void LvSelectable::set_options(std::initializer_list<std::string> options) {
|
||||||
auto index = this->get_selected_index();
|
auto index = this->get_selected_index();
|
||||||
if (index >= options.size())
|
if (index >= options.size())
|
||||||
index = options.size() - 1;
|
index = options.size() - 1;
|
||||||
this->options_ = std::move(options);
|
this->options_ = options;
|
||||||
this->set_option_string(join_string(this->options_).c_str());
|
this->set_option_string(join_string(this->options_).c_str());
|
||||||
lv_event_send(this->obj, LV_EVENT_REFRESH, nullptr);
|
lv_event_send(this->obj, LV_EVENT_REFRESH, nullptr);
|
||||||
this->set_selected_index(index, LV_ANIM_OFF);
|
this->set_selected_index(index, LV_ANIM_OFF);
|
||||||
|
|||||||
@@ -358,12 +358,12 @@ class LvSelectable : public LvCompound {
|
|||||||
virtual void set_selected_index(size_t index, lv_anim_enable_t anim) = 0;
|
virtual void set_selected_index(size_t index, lv_anim_enable_t anim) = 0;
|
||||||
void set_selected_text(const std::string &text, lv_anim_enable_t anim);
|
void set_selected_text(const std::string &text, lv_anim_enable_t anim);
|
||||||
std::string get_selected_text();
|
std::string get_selected_text();
|
||||||
std::vector<std::string> get_options() { return this->options_; }
|
const FixedVector<std::string> &get_options() { return this->options_; }
|
||||||
void set_options(std::vector<std::string> options);
|
void set_options(std::initializer_list<std::string> options);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void set_option_string(const char *options) = 0;
|
virtual void set_option_string(const char *options) = 0;
|
||||||
std::vector<std::string> options_{};
|
FixedVector<std::string> options_{};
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifdef USE_LVGL_DROPDOWN
|
#ifdef USE_LVGL_DROPDOWN
|
||||||
|
|||||||
@@ -53,7 +53,10 @@ class LVGLSelect : public select::Select, public Component {
|
|||||||
this->widget_->set_selected_text(value, this->anim_);
|
this->widget_->set_selected_text(value, this->anim_);
|
||||||
this->publish();
|
this->publish();
|
||||||
}
|
}
|
||||||
void set_options_() { this->traits.set_options(this->widget_->get_options()); }
|
void set_options_() {
|
||||||
|
// Copy options from lvgl widget to select traits
|
||||||
|
this->traits.copy_options(this->widget_->get_options());
|
||||||
|
}
|
||||||
|
|
||||||
LvSelectable *widget_;
|
LvSelectable *widget_;
|
||||||
lv_anim_enable_t anim_;
|
lv_anim_enable_t anim_;
|
||||||
|
|||||||
@@ -3,9 +3,9 @@
|
|||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace select {
|
namespace select {
|
||||||
|
|
||||||
void SelectTraits::set_options(std::vector<std::string> options) { this->options_ = std::move(options); }
|
void SelectTraits::set_options(std::initializer_list<std::string> options) { this->options_ = options; }
|
||||||
|
|
||||||
const std::vector<std::string> &SelectTraits::get_options() const { return this->options_; }
|
const FixedVector<std::string> &SelectTraits::get_options() const { return this->options_; }
|
||||||
|
|
||||||
} // namespace select
|
} // namespace select
|
||||||
} // namespace esphome
|
} // namespace esphome
|
||||||
|
|||||||
@@ -1,18 +1,21 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <initializer_list>
|
||||||
|
#include "esphome/core/helpers.h"
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace select {
|
namespace select {
|
||||||
|
|
||||||
class SelectTraits {
|
class SelectTraits {
|
||||||
public:
|
public:
|
||||||
void set_options(std::vector<std::string> options);
|
void set_options(std::initializer_list<std::string> options);
|
||||||
const std::vector<std::string> &get_options() const;
|
const FixedVector<std::string> &get_options() const;
|
||||||
|
/// Copy options from another SelectTraits (for copy_select, lvgl)
|
||||||
|
void copy_options(const FixedVector<std::string> &other) { this->options_.copy_from(other); }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
std::vector<std::string> options_;
|
FixedVector<std::string> options_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace select
|
} // namespace select
|
||||||
|
|||||||
@@ -378,14 +378,18 @@ async def to_code(config):
|
|||||||
# Track if any network uses Enterprise authentication
|
# Track if any network uses Enterprise authentication
|
||||||
has_eap = False
|
has_eap = False
|
||||||
|
|
||||||
def add_sta(ap, network):
|
# Build all WiFiAP objects
|
||||||
ip_config = network.get(CONF_MANUAL_IP, config.get(CONF_MANUAL_IP))
|
networks = config.get(CONF_NETWORKS, [])
|
||||||
cg.add(var.add_sta(wifi_network(network, ap, ip_config)))
|
if networks:
|
||||||
|
wifi_aps = []
|
||||||
|
for network in networks:
|
||||||
|
if CONF_EAP in network:
|
||||||
|
has_eap = True
|
||||||
|
ip_config = network.get(CONF_MANUAL_IP, config.get(CONF_MANUAL_IP))
|
||||||
|
wifi_aps.append(wifi_network(network, WiFiAP(), ip_config))
|
||||||
|
|
||||||
for network in config.get(CONF_NETWORKS, []):
|
# Set all WiFi networks at once
|
||||||
if CONF_EAP in network:
|
cg.add(var.set_stas(wifi_aps))
|
||||||
has_eap = True
|
|
||||||
cg.with_local_variable(network[CONF_ID], WiFiAP(), add_sta, network)
|
|
||||||
|
|
||||||
if CONF_AP in config:
|
if CONF_AP in config:
|
||||||
conf = config[CONF_AP]
|
conf = config[CONF_AP]
|
||||||
|
|||||||
@@ -330,11 +330,8 @@ float WiFiComponent::get_loop_priority() const {
|
|||||||
return 10.0f; // before other loop components
|
return 10.0f; // before other loop components
|
||||||
}
|
}
|
||||||
|
|
||||||
void WiFiComponent::add_sta(const WiFiAP &ap) { this->sta_.push_back(ap); }
|
void WiFiComponent::set_stas(const std::initializer_list<WiFiAP> &aps) { this->sta_ = aps; }
|
||||||
void WiFiComponent::set_sta(const WiFiAP &ap) {
|
void WiFiComponent::set_sta(const WiFiAP &ap) { this->set_stas({ap}); }
|
||||||
this->clear_sta();
|
|
||||||
this->add_sta(ap);
|
|
||||||
}
|
|
||||||
void WiFiComponent::clear_sta() { this->sta_.clear(); }
|
void WiFiComponent::clear_sta() { this->sta_.clear(); }
|
||||||
void WiFiComponent::save_wifi_sta(const std::string &ssid, const std::string &password) {
|
void WiFiComponent::save_wifi_sta(const std::string &ssid, const std::string &password) {
|
||||||
SavedWifiSettings save{}; // zero-initialized - all bytes set to \0, guaranteeing null termination
|
SavedWifiSettings save{}; // zero-initialized - all bytes set to \0, guaranteeing null termination
|
||||||
|
|||||||
@@ -219,7 +219,7 @@ class WiFiComponent : public Component {
|
|||||||
|
|
||||||
void set_sta(const WiFiAP &ap);
|
void set_sta(const WiFiAP &ap);
|
||||||
WiFiAP get_sta() { return this->selected_ap_; }
|
WiFiAP get_sta() { return this->selected_ap_; }
|
||||||
void add_sta(const WiFiAP &ap);
|
void set_stas(const std::initializer_list<WiFiAP> &aps);
|
||||||
void clear_sta();
|
void clear_sta();
|
||||||
|
|
||||||
#ifdef USE_WIFI_AP
|
#ifdef USE_WIFI_AP
|
||||||
@@ -393,7 +393,7 @@ class WiFiComponent : public Component {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
std::string use_address_;
|
std::string use_address_;
|
||||||
std::vector<WiFiAP> sta_;
|
FixedVector<WiFiAP> sta_;
|
||||||
std::vector<WiFiSTAPriority> sta_priorities_;
|
std::vector<WiFiSTAPriority> sta_priorities_;
|
||||||
wifi_scan_vector_t<WiFiScanResult> scan_result_;
|
wifi_scan_vector_t<WiFiScanResult> scan_result_;
|
||||||
WiFiAP selected_ap_;
|
WiFiAP selected_ap_;
|
||||||
|
|||||||
@@ -194,12 +194,8 @@ template<typename T> class FixedVector {
|
|||||||
size_ = 0;
|
size_ = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
// Helper to assign from initializer list (shared by constructor and assignment operator)
|
||||||
FixedVector() = default;
|
void assign_from_initializer_list_(std::initializer_list<T> init_list) {
|
||||||
|
|
||||||
/// Constructor from initializer list - allocates exact size needed
|
|
||||||
/// This enables brace initialization: FixedVector<int> v = {1, 2, 3};
|
|
||||||
FixedVector(std::initializer_list<T> init_list) {
|
|
||||||
init(init_list.size());
|
init(init_list.size());
|
||||||
size_t idx = 0;
|
size_t idx = 0;
|
||||||
for (const auto &item : init_list) {
|
for (const auto &item : init_list) {
|
||||||
@@ -209,9 +205,17 @@ template<typename T> class FixedVector {
|
|||||||
size_ = init_list.size();
|
size_ = init_list.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
FixedVector() = default;
|
||||||
|
|
||||||
|
/// Constructor from initializer list - allocates exact size needed
|
||||||
|
/// This enables brace initialization: FixedVector<int> v = {1, 2, 3};
|
||||||
|
FixedVector(std::initializer_list<T> init_list) { assign_from_initializer_list_(init_list); }
|
||||||
|
|
||||||
~FixedVector() { cleanup_(); }
|
~FixedVector() { cleanup_(); }
|
||||||
|
|
||||||
// Disable copy operations (avoid accidental expensive copies)
|
// Disable copy operations (avoid accidental expensive copies)
|
||||||
|
// Use copy_from() for explicit copying when needed (e.g., copy_select)
|
||||||
FixedVector(const FixedVector &) = delete;
|
FixedVector(const FixedVector &) = delete;
|
||||||
FixedVector &operator=(const FixedVector &) = delete;
|
FixedVector &operator=(const FixedVector &) = delete;
|
||||||
|
|
||||||
@@ -234,6 +238,28 @@ template<typename T> class FixedVector {
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Assignment from initializer list - avoids temporary and move overhead
|
||||||
|
/// This enables: FixedVector<int> v; v = {1, 2, 3};
|
||||||
|
FixedVector &operator=(std::initializer_list<T> init_list) {
|
||||||
|
cleanup_();
|
||||||
|
reset_();
|
||||||
|
assign_from_initializer_list_(init_list);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Explicitly copy another FixedVector
|
||||||
|
/// This method exists instead of operator= to make copying intentional and visible.
|
||||||
|
/// Copying is expensive on embedded systems, so we require explicit opt-in.
|
||||||
|
/// Use cases: copy_select (copying source options), lvgl (copying widget options)
|
||||||
|
void copy_from(const FixedVector &other) {
|
||||||
|
cleanup_();
|
||||||
|
reset_();
|
||||||
|
init(other.size());
|
||||||
|
for (const auto &item : other) {
|
||||||
|
push_back(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Allocate capacity - can be called multiple times to reinit
|
// Allocate capacity - can be called multiple times to reinit
|
||||||
void init(size_t n) {
|
void init(size_t n) {
|
||||||
cleanup_();
|
cleanup_();
|
||||||
@@ -292,6 +318,11 @@ template<typename T> class FixedVector {
|
|||||||
return data_[size_ - 1];
|
return data_[size_ - 1];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Access first element (no bounds checking - matches std::vector behavior)
|
||||||
|
/// Caller must ensure vector is not empty (size() > 0)
|
||||||
|
T &front() { return data_[0]; }
|
||||||
|
const T &front() const { return data_[0]; }
|
||||||
|
|
||||||
/// Access last element (no bounds checking - matches std::vector behavior)
|
/// Access last element (no bounds checking - matches std::vector behavior)
|
||||||
/// Caller must ensure vector is not empty (size() > 0)
|
/// Caller must ensure vector is not empty (size() > 0)
|
||||||
T &back() { return data_[size_ - 1]; }
|
T &back() { return data_[size_ - 1]; }
|
||||||
@@ -305,6 +336,12 @@ template<typename T> class FixedVector {
|
|||||||
T &operator[](size_t i) { return data_[i]; }
|
T &operator[](size_t i) { return data_[i]; }
|
||||||
const T &operator[](size_t i) const { return data_[i]; }
|
const T &operator[](size_t i) const { return data_[i]; }
|
||||||
|
|
||||||
|
/// Access element with bounds checking (matches std::vector behavior)
|
||||||
|
/// Returns reference to element at index i
|
||||||
|
/// Behavior for out of bounds access matches std::vector::at() (undefined on embedded)
|
||||||
|
T &at(size_t i) { return data_[i]; }
|
||||||
|
const T &at(size_t i) const { return data_[i]; }
|
||||||
|
|
||||||
// Iterator support for range-based for loops
|
// Iterator support for range-based for loops
|
||||||
T *begin() { return data_; }
|
T *begin() { return data_; }
|
||||||
T *end() { return data_ + size_; }
|
T *end() { return data_ + size_; }
|
||||||
|
|||||||
@@ -12,5 +12,8 @@ esphome:
|
|||||||
- logger.log: "Failed to connect to WiFi!"
|
- logger.log: "Failed to connect to WiFi!"
|
||||||
|
|
||||||
wifi:
|
wifi:
|
||||||
ssid: MySSID
|
networks:
|
||||||
password: password1
|
- ssid: MySSID
|
||||||
|
password: password1
|
||||||
|
- ssid: MySSID2
|
||||||
|
password: password2
|
||||||
|
|||||||
Reference in New Issue
Block a user