[select] Add zero-copy support for API select commands (#12329)

This commit is contained in:
J. Nick Koston
2025-12-08 16:37:51 +01:00
committed by GitHub
parent d635892ecf
commit 801d1135ab
9 changed files with 27 additions and 24 deletions

View File

@@ -1195,7 +1195,7 @@ message SelectCommandRequest {
option (base_class) = "CommandProtoMessage";
fixed32 key = 1;
string state = 2;
string state = 2 [(pointer_to_buffer) = true];
uint32 device_id = 3 [(field_ifdef) = "USE_DEVICES"];
}

View File

@@ -902,7 +902,7 @@ uint16_t APIConnection::try_send_select_info(EntityBase *entity, APIConnection *
}
void APIConnection::select_command(const SelectCommandRequest &msg) {
ENTITY_COMMAND_MAKE_CALL(select::Select, select, select)
call.set_option(msg.state);
call.set_option(reinterpret_cast<const char *>(msg.state), msg.state_len);
call.perform();
}
#endif

View File

@@ -1569,9 +1569,12 @@ bool SelectCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt value) {
}
bool SelectCommandRequest::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
switch (field_id) {
case 2:
this->state = value.as_string();
case 2: {
// Use raw data directly to avoid allocation
this->state = value.data();
this->state_len = value.size();
break;
}
default:
return false;
}

View File

@@ -1604,11 +1604,12 @@ class SelectStateResponse final : public StateResponseProtoMessage {
class SelectCommandRequest final : public CommandProtoMessage {
public:
static constexpr uint8_t MESSAGE_TYPE = 54;
static constexpr uint8_t ESTIMATED_SIZE = 18;
static constexpr uint8_t ESTIMATED_SIZE = 28;
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *message_name() const override { return "select_command_request"; }
#endif
std::string state{};
const uint8_t *state{nullptr};
uint16_t state_len{0};
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif

View File

@@ -1453,7 +1453,9 @@ void SelectStateResponse::dump_to(std::string &out) const {
void SelectCommandRequest::dump_to(std::string &out) const {
MessageDumpHelper helper(out, "SelectCommandRequest");
dump_field(out, "key", this->key);
dump_field(out, "state", this->state);
out.append(" state: ");
out.append(format_hex_pretty(this->state, this->state_len));
out.append("\n");
#ifdef USE_DEVICES
dump_field(out, "device_id", this->device_id);
#endif

View File

@@ -56,12 +56,10 @@ size_t Select::size() const {
return options.size();
}
optional<size_t> Select::index_of(const std::string &option) const { return this->index_of(option.c_str()); }
optional<size_t> Select::index_of(const char *option) const {
optional<size_t> Select::index_of(const char *option, size_t len) const {
const auto &options = traits.get_options();
for (size_t i = 0; i < options.size(); i++) {
if (strcmp(options[i], option) == 0) {
if (strncmp(options[i], option, len) == 0 && options[i][len] == '\0') {
return i;
}
}

View File

@@ -62,8 +62,9 @@ class Select : public EntityBase {
size_t size() const;
/// Find the (optional) index offset of the provided option value.
optional<size_t> index_of(const std::string &option) const;
optional<size_t> index_of(const char *option) const;
optional<size_t> index_of(const char *option, size_t len) const;
optional<size_t> index_of(const std::string &option) const { return this->index_of(option.data(), option.size()); }
optional<size_t> index_of(const char *option) const { return this->index_of(option, strlen(option)); }
/// Return the (optional) index offset of the currently active option.
optional<size_t> active_index() const;

View File

@@ -6,9 +6,7 @@ namespace esphome::select {
static const char *const TAG = "select";
SelectCall &SelectCall::set_option(const std::string &option) { return this->with_option(option); }
SelectCall &SelectCall::set_option(const char *option) { return this->with_option(option); }
SelectCall &SelectCall::set_option(const char *option, size_t len) { return this->with_option(option, len); }
SelectCall &SelectCall::set_index(size_t index) { return this->with_index(index); }
@@ -32,12 +30,10 @@ SelectCall &SelectCall::with_cycle(bool cycle) {
return *this;
}
SelectCall &SelectCall::with_option(const std::string &option) { return this->with_option(option.c_str()); }
SelectCall &SelectCall::with_option(const char *option) {
SelectCall &SelectCall::with_option(const char *option, size_t len) {
this->operation_ = SELECT_OP_SET;
// Find the option index - this validates the option exists
this->index_ = this->parent_->index_of(option);
this->index_ = this->parent_->index_of(option, len);
return *this;
}

View File

@@ -20,8 +20,9 @@ class SelectCall {
explicit SelectCall(Select *parent) : parent_(parent) {}
void perform();
SelectCall &set_option(const std::string &option);
SelectCall &set_option(const char *option);
SelectCall &set_option(const char *option, size_t len);
SelectCall &set_option(const std::string &option) { return this->set_option(option.data(), option.size()); }
SelectCall &set_option(const char *option) { return this->set_option(option, strlen(option)); }
SelectCall &set_index(size_t index);
SelectCall &select_next(bool cycle);
@@ -31,8 +32,9 @@ class SelectCall {
SelectCall &with_operation(SelectOperation operation);
SelectCall &with_cycle(bool cycle);
SelectCall &with_option(const std::string &option);
SelectCall &with_option(const char *option);
SelectCall &with_option(const char *option, size_t len);
SelectCall &with_option(const std::string &option) { return this->with_option(option.data(), option.size()); }
SelectCall &with_option(const char *option) { return this->with_option(option, strlen(option)); }
SelectCall &with_index(size_t index);
protected: