mirror of
https://github.com/esphome/esphome.git
synced 2026-05-27 11:56:11 +08:00
[select] Return StringRef from current_option() (#13095)
This commit is contained in:
@@ -914,7 +914,7 @@ uint16_t APIConnection::try_send_select_state(EntityBase *entity, APIConnection
|
|||||||
bool is_single) {
|
bool is_single) {
|
||||||
auto *select = static_cast<select::Select *>(entity);
|
auto *select = static_cast<select::Select *>(entity);
|
||||||
SelectStateResponse resp;
|
SelectStateResponse resp;
|
||||||
resp.state = StringRef(select->current_option());
|
resp.state = select->current_option();
|
||||||
resp.missing_state = !select->has_state();
|
resp.missing_state = !select->has_state();
|
||||||
return fill_and_encode_entity_state(select, resp, SelectStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
|
return fill_and_encode_entity_state(select, resp, SelectStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,7 +42,8 @@ std::string MenuItemSelect::get_value_text() const {
|
|||||||
result = this->value_getter_.value()(this);
|
result = this->value_getter_.value()(this);
|
||||||
} else {
|
} else {
|
||||||
if (this->select_var_ != nullptr) {
|
if (this->select_var_ != nullptr) {
|
||||||
result = this->select_var_->current_option();
|
auto option = this->select_var_->current_option();
|
||||||
|
result.assign(option.c_str(), option.size());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -442,7 +442,8 @@ bool LD2410Component::handle_ack_data_() {
|
|||||||
ESP_LOGV(TAG, "Baud rate change");
|
ESP_LOGV(TAG, "Baud rate change");
|
||||||
#ifdef USE_SELECT
|
#ifdef USE_SELECT
|
||||||
if (this->baud_rate_select_ != nullptr) {
|
if (this->baud_rate_select_ != nullptr) {
|
||||||
ESP_LOGE(TAG, "Change baud rate to %s and reinstall", this->baud_rate_select_->current_option());
|
auto baud = this->baud_rate_select_->current_option();
|
||||||
|
ESP_LOGE(TAG, "Change baud rate to %.*s and reinstall", (int) baud.size(), baud.c_str());
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
break;
|
break;
|
||||||
@@ -766,10 +767,10 @@ void LD2410Component::set_light_out_control() {
|
|||||||
#endif
|
#endif
|
||||||
#ifdef USE_SELECT
|
#ifdef USE_SELECT
|
||||||
if (this->light_function_select_ != nullptr && this->light_function_select_->has_state()) {
|
if (this->light_function_select_ != nullptr && this->light_function_select_->has_state()) {
|
||||||
this->light_function_ = find_uint8(LIGHT_FUNCTIONS_BY_STR, this->light_function_select_->current_option());
|
this->light_function_ = find_uint8(LIGHT_FUNCTIONS_BY_STR, this->light_function_select_->current_option().c_str());
|
||||||
}
|
}
|
||||||
if (this->out_pin_level_select_ != nullptr && this->out_pin_level_select_->has_state()) {
|
if (this->out_pin_level_select_ != nullptr && this->out_pin_level_select_->has_state()) {
|
||||||
this->out_pin_level_ = find_uint8(OUT_PIN_LEVELS_BY_STR, this->out_pin_level_select_->current_option());
|
this->out_pin_level_ = find_uint8(OUT_PIN_LEVELS_BY_STR, this->out_pin_level_select_->current_option().c_str());
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
this->set_config_mode_(true);
|
this->set_config_mode_(true);
|
||||||
|
|||||||
@@ -486,7 +486,8 @@ bool LD2412Component::handle_ack_data_() {
|
|||||||
ESP_LOGV(TAG, "Baud rate change");
|
ESP_LOGV(TAG, "Baud rate change");
|
||||||
#ifdef USE_SELECT
|
#ifdef USE_SELECT
|
||||||
if (this->baud_rate_select_ != nullptr) {
|
if (this->baud_rate_select_ != nullptr) {
|
||||||
ESP_LOGW(TAG, "Change baud rate to %s and reinstall", this->baud_rate_select_->current_option());
|
auto baud = this->baud_rate_select_->current_option();
|
||||||
|
ESP_LOGW(TAG, "Change baud rate to %.*s and reinstall", (int) baud.size(), baud.c_str());
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
break;
|
break;
|
||||||
@@ -790,7 +791,7 @@ void LD2412Component::set_basic_config() {
|
|||||||
1, TOTAL_GATES, DEFAULT_PRESENCE_TIMEOUT, 0,
|
1, TOTAL_GATES, DEFAULT_PRESENCE_TIMEOUT, 0,
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_SELECT
|
#ifdef USE_SELECT
|
||||||
find_uint8(OUT_PIN_LEVELS_BY_STR, this->out_pin_level_select_->current_option()),
|
find_uint8(OUT_PIN_LEVELS_BY_STR, this->out_pin_level_select_->current_option().c_str()),
|
||||||
#else
|
#else
|
||||||
0x01, // Default value if not using select
|
0x01, // Default value if not using select
|
||||||
#endif
|
#endif
|
||||||
@@ -844,7 +845,7 @@ void LD2412Component::set_light_out_control() {
|
|||||||
#endif
|
#endif
|
||||||
#ifdef USE_SELECT
|
#ifdef USE_SELECT
|
||||||
if (this->light_function_select_ != nullptr && this->light_function_select_->has_state()) {
|
if (this->light_function_select_ != nullptr && this->light_function_select_->has_state()) {
|
||||||
this->light_function_ = find_uint8(LIGHT_FUNCTIONS_BY_STR, this->light_function_select_->current_option());
|
this->light_function_ = find_uint8(LIGHT_FUNCTIONS_BY_STR, this->light_function_select_->current_option().c_str());
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
uint8_t value[2] = {this->light_function_, this->light_threshold_};
|
uint8_t value[2] = {this->light_function_, this->light_threshold_};
|
||||||
|
|||||||
@@ -637,7 +637,8 @@ bool LD2450Component::handle_ack_data_() {
|
|||||||
ESP_LOGV(TAG, "Baud rate change");
|
ESP_LOGV(TAG, "Baud rate change");
|
||||||
#ifdef USE_SELECT
|
#ifdef USE_SELECT
|
||||||
if (this->baud_rate_select_ != nullptr) {
|
if (this->baud_rate_select_ != nullptr) {
|
||||||
ESP_LOGE(TAG, "Change baud rate to %s and reinstall", this->baud_rate_select_->current_option());
|
auto baud = this->baud_rate_select_->current_option();
|
||||||
|
ESP_LOGE(TAG, "Change baud rate to %.*s and reinstall", (int) baud.size(), baud.c_str());
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
break;
|
break;
|
||||||
@@ -718,7 +719,8 @@ bool LD2450Component::handle_ack_data_() {
|
|||||||
this->publish_zone_type();
|
this->publish_zone_type();
|
||||||
#ifdef USE_SELECT
|
#ifdef USE_SELECT
|
||||||
if (this->zone_type_select_ != nullptr) {
|
if (this->zone_type_select_ != nullptr) {
|
||||||
ESP_LOGV(TAG, "Change zone type to: %s", this->zone_type_select_->current_option());
|
auto zone = this->zone_type_select_->current_option();
|
||||||
|
ESP_LOGV(TAG, "Change zone type to: %.*s", (int) zone.size(), zone.c_str());
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
if (this->buffer_data_[10] == 0x00) {
|
if (this->buffer_data_[10] == 0x00) {
|
||||||
|
|||||||
@@ -43,7 +43,8 @@ void MQTTSelectComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryCon
|
|||||||
}
|
}
|
||||||
bool MQTTSelectComponent::send_initial_state() {
|
bool MQTTSelectComponent::send_initial_state() {
|
||||||
if (this->select_->has_state()) {
|
if (this->select_->has_state()) {
|
||||||
return this->publish_state(this->select_->current_option());
|
auto option = this->select_->current_option();
|
||||||
|
return this->publish_state(std::string(option.c_str(), option.size()));
|
||||||
} else {
|
} else {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -709,7 +709,8 @@ void PrometheusHandler::select_row_(AsyncResponseStream *stream, select::Select
|
|||||||
stream->print(ESPHOME_F("\",name=\""));
|
stream->print(ESPHOME_F("\",name=\""));
|
||||||
stream->print(relabel_name_(obj).c_str());
|
stream->print(relabel_name_(obj).c_str());
|
||||||
stream->print(ESPHOME_F("\",value=\""));
|
stream->print(ESPHOME_F("\",value=\""));
|
||||||
stream->print(obj->current_option());
|
// c_str() is safe as option values are null-terminated strings from codegen
|
||||||
|
stream->print(obj->current_option().c_str());
|
||||||
stream->print(ESPHOME_F("\"} "));
|
stream->print(ESPHOME_F("\"} "));
|
||||||
stream->print(ESPHOME_F("1.0"));
|
stream->print(ESPHOME_F("1.0"));
|
||||||
stream->print(ESPHOME_F("\n"));
|
stream->print(ESPHOME_F("\n"));
|
||||||
|
|||||||
@@ -38,7 +38,9 @@ void Select::publish_state(size_t index) {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *Select::current_option() const { return this->has_state() ? this->option_at(this->active_index_) : ""; }
|
StringRef Select::current_option() const {
|
||||||
|
return this->has_state() ? StringRef(this->option_at(this->active_index_)) : StringRef();
|
||||||
|
}
|
||||||
|
|
||||||
void Select::add_on_state_callback(std::function<void(size_t)> &&callback) {
|
void Select::add_on_state_callback(std::function<void(size_t)> &&callback) {
|
||||||
this->state_callback_.add(std::move(callback));
|
this->state_callback_.add(std::move(callback));
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
#include "esphome/core/component.h"
|
#include "esphome/core/component.h"
|
||||||
#include "esphome/core/entity_base.h"
|
#include "esphome/core/entity_base.h"
|
||||||
#include "esphome/core/helpers.h"
|
#include "esphome/core/helpers.h"
|
||||||
|
#include "esphome/core/string_ref.h"
|
||||||
#include "select_call.h"
|
#include "select_call.h"
|
||||||
#include "select_traits.h"
|
#include "select_traits.h"
|
||||||
|
|
||||||
@@ -33,8 +34,8 @@ class Select : public EntityBase {
|
|||||||
|
|
||||||
#pragma GCC diagnostic push
|
#pragma GCC diagnostic push
|
||||||
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||||
/// @deprecated Use current_option() instead. This member will be removed in ESPHome 2026.5.0.
|
/// @deprecated Use current_option() instead. This member will be removed in ESPHome 2026.7.0.
|
||||||
ESPDEPRECATED("Use current_option() instead of .state. Will be removed in 2026.5.0", "2025.11.0")
|
ESPDEPRECATED("Use current_option() instead of .state. Will be removed in 2026.7.0", "2026.1.0")
|
||||||
std::string state{};
|
std::string state{};
|
||||||
|
|
||||||
Select() = default;
|
Select() = default;
|
||||||
@@ -45,8 +46,10 @@ class Select : public EntityBase {
|
|||||||
void publish_state(const char *state);
|
void publish_state(const char *state);
|
||||||
void publish_state(size_t index);
|
void publish_state(size_t index);
|
||||||
|
|
||||||
/// Return the currently selected option (as const char* from flash).
|
/// Return the currently selected option, or empty StringRef if no state.
|
||||||
const char *current_option() const;
|
/// The returned StringRef points to string literals from codegen (static storage).
|
||||||
|
/// Traits are set once at startup and valid for the lifetime of the program.
|
||||||
|
StringRef current_option() const;
|
||||||
|
|
||||||
/// Instantiate a SelectCall object to modify this select component's state.
|
/// Instantiate a SelectCall object to modify this select component's state.
|
||||||
SelectCall make_call() { return SelectCall(this); }
|
SelectCall make_call() { return SelectCall(this); }
|
||||||
|
|||||||
@@ -1393,7 +1393,7 @@ void WebServer::handle_select_request(AsyncWebServerRequest *request, const UrlM
|
|||||||
|
|
||||||
if (request->method() == HTTP_GET && entity_match.action_is_empty) {
|
if (request->method() == HTTP_GET && entity_match.action_is_empty) {
|
||||||
auto detail = get_request_detail(request);
|
auto detail = get_request_detail(request);
|
||||||
std::string data = this->select_json_(obj, obj->has_state() ? obj->current_option() : "", detail);
|
std::string data = this->select_json_(obj, obj->has_state() ? obj->current_option() : StringRef(), detail);
|
||||||
request->send(200, "application/json", data.c_str());
|
request->send(200, "application/json", data.c_str());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -1414,17 +1414,18 @@ void WebServer::handle_select_request(AsyncWebServerRequest *request, const UrlM
|
|||||||
}
|
}
|
||||||
std::string WebServer::select_state_json_generator(WebServer *web_server, void *source) {
|
std::string WebServer::select_state_json_generator(WebServer *web_server, void *source) {
|
||||||
auto *obj = (select::Select *) (source);
|
auto *obj = (select::Select *) (source);
|
||||||
return web_server->select_json_(obj, obj->has_state() ? obj->current_option() : "", DETAIL_STATE);
|
return web_server->select_json_(obj, obj->has_state() ? obj->current_option() : StringRef(), DETAIL_STATE);
|
||||||
}
|
}
|
||||||
std::string WebServer::select_all_json_generator(WebServer *web_server, void *source) {
|
std::string WebServer::select_all_json_generator(WebServer *web_server, void *source) {
|
||||||
auto *obj = (select::Select *) (source);
|
auto *obj = (select::Select *) (source);
|
||||||
return web_server->select_json_(obj, obj->has_state() ? obj->current_option() : "", DETAIL_ALL);
|
return web_server->select_json_(obj, obj->has_state() ? obj->current_option() : StringRef(), DETAIL_ALL);
|
||||||
}
|
}
|
||||||
std::string WebServer::select_json_(select::Select *obj, const char *value, JsonDetail start_config) {
|
std::string WebServer::select_json_(select::Select *obj, StringRef value, JsonDetail start_config) {
|
||||||
json::JsonBuilder builder;
|
json::JsonBuilder builder;
|
||||||
JsonObject root = builder.root();
|
JsonObject root = builder.root();
|
||||||
|
|
||||||
set_json_icon_state_value(root, obj, "select", value, value, start_config);
|
// value points to null-terminated string literals from codegen (via current_option())
|
||||||
|
set_json_icon_state_value(root, obj, "select", value.c_str(), value.c_str(), start_config);
|
||||||
if (start_config == DETAIL_ALL) {
|
if (start_config == DETAIL_ALL) {
|
||||||
JsonArray opt = root[ESPHOME_F("option")].to<JsonArray>();
|
JsonArray opt = root[ESPHOME_F("option")].to<JsonArray>();
|
||||||
for (auto &option : obj->traits.get_options()) {
|
for (auto &option : obj->traits.get_options()) {
|
||||||
|
|||||||
@@ -627,7 +627,7 @@ class WebServer : public Controller,
|
|||||||
std::string text_json_(text::Text *obj, const std::string &value, JsonDetail start_config);
|
std::string text_json_(text::Text *obj, const std::string &value, JsonDetail start_config);
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_SELECT
|
#ifdef USE_SELECT
|
||||||
std::string select_json_(select::Select *obj, const char *value, JsonDetail start_config);
|
std::string select_json_(select::Select *obj, StringRef value, JsonDetail start_config);
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_CLIMATE
|
#ifdef USE_CLIMATE
|
||||||
std::string climate_json_(climate::Climate *obj, JsonDetail start_config);
|
std::string climate_json_(climate::Climate *obj, JsonDetail start_config);
|
||||||
|
|||||||
@@ -243,6 +243,7 @@ number:
|
|||||||
|
|
||||||
select:
|
select:
|
||||||
- platform: template
|
- platform: template
|
||||||
|
id: template_select
|
||||||
name: "Template select"
|
name: "Template select"
|
||||||
optimistic: true
|
optimistic: true
|
||||||
options:
|
options:
|
||||||
@@ -250,6 +251,37 @@ select:
|
|||||||
- two
|
- two
|
||||||
- three
|
- three
|
||||||
initial_option: two
|
initial_option: two
|
||||||
|
# Test current_option() returning std::string_view - migration guide examples
|
||||||
|
on_value:
|
||||||
|
- lambda: |-
|
||||||
|
// Migration guide: Check if select has a state
|
||||||
|
// OLD: if (id(template_select).current_option() != nullptr)
|
||||||
|
// NEW: Check with .empty()
|
||||||
|
if (!id(template_select).current_option().empty()) {
|
||||||
|
ESP_LOGI("test", "Select has state");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Migration guide: Compare option values
|
||||||
|
// OLD: if (strcmp(id(template_select).current_option(), "one") == 0)
|
||||||
|
// NEW: Direct comparison works safely even when empty
|
||||||
|
if (id(template_select).current_option() == "one") {
|
||||||
|
ESP_LOGI("test", "Option is 'one'");
|
||||||
|
}
|
||||||
|
if (id(template_select).current_option() != "two") {
|
||||||
|
ESP_LOGI("test", "Option is not 'two'");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Migration guide: Logging options
|
||||||
|
// Option 1: Using .c_str() - StringRef guarantees null-termination
|
||||||
|
ESP_LOGI("test", "Current option: %s", id(template_select).current_option().c_str());
|
||||||
|
|
||||||
|
// Option 2: Using %.*s format with size
|
||||||
|
auto option = id(template_select).current_option();
|
||||||
|
ESP_LOGI("test", "Current option (safe): %.*s", (int) option.size(), option.c_str());
|
||||||
|
|
||||||
|
// Migration guide: Store in std::string
|
||||||
|
std::string stored_option(id(template_select).current_option());
|
||||||
|
ESP_LOGI("test", "Stored: %s", stored_option.c_str());
|
||||||
|
|
||||||
lock:
|
lock:
|
||||||
- platform: template
|
- platform: template
|
||||||
|
|||||||
Reference in New Issue
Block a user