diff --git a/esphome/components/api/api.proto b/esphome/components/api/api.proto index 2534ad0b1..50af5061c 100644 --- a/esphome/components/api/api.proto +++ b/esphome/components/api/api.proto @@ -579,7 +579,7 @@ message LightCommandRequest { bool has_flash_length = 16; uint32 flash_length = 17; bool has_effect = 18; - string effect = 19; + string effect = 19 [(pointer_to_buffer) = true]; uint32 device_id = 28 [(field_ifdef) = "USE_DEVICES"]; } diff --git a/esphome/components/api/api_connection.cpp b/esphome/components/api/api_connection.cpp index 4b1061028..09b311c1e 100644 --- a/esphome/components/api/api_connection.cpp +++ b/esphome/components/api/api_connection.cpp @@ -533,7 +533,7 @@ void APIConnection::light_command(const LightCommandRequest &msg) { if (msg.has_flash_length) call.set_flash_length(msg.flash_length); if (msg.has_effect) - call.set_effect(msg.effect); + call.set_effect(reinterpret_cast(msg.effect), msg.effect_len); call.perform(); } #endif diff --git a/esphome/components/api/api_pb2.cpp b/esphome/components/api/api_pb2.cpp index 128f82fe7..4a89ee78e 100644 --- a/esphome/components/api/api_pb2.cpp +++ b/esphome/components/api/api_pb2.cpp @@ -611,9 +611,12 @@ bool LightCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt value) { } bool LightCommandRequest::decode_length(uint32_t field_id, ProtoLengthDelimited value) { switch (field_id) { - case 19: - this->effect = value.as_string(); + case 19: { + // Use raw data directly to avoid allocation + this->effect = value.data(); + this->effect_len = value.size(); break; + } default: return false; } diff --git a/esphome/components/api/api_pb2.h b/esphome/components/api/api_pb2.h index 49f1ea3c5..f23a62fc3 100644 --- a/esphome/components/api/api_pb2.h +++ b/esphome/components/api/api_pb2.h @@ -840,7 +840,7 @@ class LightStateResponse final : public StateResponseProtoMessage { class LightCommandRequest final : public CommandProtoMessage { public: static constexpr uint8_t MESSAGE_TYPE = 32; - static constexpr uint8_t ESTIMATED_SIZE = 112; + static constexpr uint8_t ESTIMATED_SIZE = 122; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "light_command_request"; } #endif @@ -869,7 +869,8 @@ class LightCommandRequest final : public CommandProtoMessage { bool has_flash_length{false}; uint32_t flash_length{0}; bool has_effect{false}; - std::string effect{}; + const uint8_t *effect{nullptr}; + uint16_t effect_len{0}; #ifdef HAS_PROTO_MESSAGE_DUMP void dump_to(std::string &out) const override; #endif diff --git a/esphome/components/api/api_pb2_dump.cpp b/esphome/components/api/api_pb2_dump.cpp index ca69d1ff0..5e271f41c 100644 --- a/esphome/components/api/api_pb2_dump.cpp +++ b/esphome/components/api/api_pb2_dump.cpp @@ -999,7 +999,9 @@ void LightCommandRequest::dump_to(std::string &out) const { dump_field(out, "has_flash_length", this->has_flash_length); dump_field(out, "flash_length", this->flash_length); dump_field(out, "has_effect", this->has_effect); - dump_field(out, "effect", this->effect); + out.append(" effect: "); + out.append(format_hex_pretty(this->effect, this->effect_len)); + out.append("\n"); #ifdef USE_DEVICES dump_field(out, "device_id", this->device_id); #endif diff --git a/esphome/components/light/light_call.cpp b/esphome/components/light/light_call.cpp index dca586173..8161e8b81 100644 --- a/esphome/components/light/light_call.cpp +++ b/esphome/components/light/light_call.cpp @@ -504,8 +504,8 @@ color_mode_bitmask_t LightCall::get_suitable_color_modes_mask_() { #undef KEY } -LightCall &LightCall::set_effect(const std::string &effect) { - if (strcasecmp(effect.c_str(), "none") == 0) { +LightCall &LightCall::set_effect(const char *effect, size_t len) { + if (len == 4 && strncasecmp(effect, "none", 4) == 0) { this->set_effect(0); return *this; } @@ -513,15 +513,16 @@ LightCall &LightCall::set_effect(const std::string &effect) { bool found = false; for (uint32_t i = 0; i < this->parent_->effects_.size(); i++) { LightEffect *e = this->parent_->effects_[i]; + const char *name = e->get_name(); - if (strcasecmp(effect.c_str(), e->get_name()) == 0) { + if (strncasecmp(effect, name, len) == 0 && name[len] == '\0') { this->set_effect(i + 1); found = true; break; } } if (!found) { - ESP_LOGW(TAG, "'%s': no such effect '%s'", this->parent_->get_name().c_str(), effect.c_str()); + ESP_LOGW(TAG, "'%s': no such effect '%.*s'", this->parent_->get_name().c_str(), (int) len, effect); } return *this; } diff --git a/esphome/components/light/light_call.h b/esphome/components/light/light_call.h index 6931b58b9..0926ab610 100644 --- a/esphome/components/light/light_call.h +++ b/esphome/components/light/light_call.h @@ -129,7 +129,9 @@ class LightCall { /// Set the effect of the light by its name. LightCall &set_effect(optional effect); /// Set the effect of the light by its name. - LightCall &set_effect(const std::string &effect); + LightCall &set_effect(const std::string &effect) { return this->set_effect(effect.data(), effect.size()); } + /// Set the effect of the light by its name and length (zero-copy from API). + LightCall &set_effect(const char *effect, size_t len); /// Set the effect of the light by its internal index number (only for internal use). LightCall &set_effect(uint32_t effect_number); LightCall &set_effect(optional effect_number);