From 0083abe3b5ad93192d2ab33bf16660dc286f982e Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 1 Jul 2025 18:30:03 -0500 Subject: [PATCH 1/3] Fix regression: BK7231N devices not returning entities via API (#9283) --- esphome/components/api/api_connection.cpp | 47 ++++----- esphome/components/api/api_connection.h | 119 ++++++++++------------ 2 files changed, 72 insertions(+), 94 deletions(-) diff --git a/esphome/components/api/api_connection.cpp b/esphome/components/api/api_connection.cpp index b7624221c9..e83d508c50 100644 --- a/esphome/components/api/api_connection.cpp +++ b/esphome/components/api/api_connection.cpp @@ -1687,7 +1687,9 @@ void APIConnection::DeferredBatch::add_item(EntityBase *entity, MessageCreator c // O(n) but optimized for RAM and not performance. for (auto &item : items) { if (item.entity == entity && item.message_type == message_type) { - // Update the existing item with the new creator + // Clean up old creator before replacing + item.creator.cleanup(message_type); + // Move assign the new creator item.creator = std::move(creator); return; } @@ -1730,11 +1732,11 @@ void APIConnection::process_batch_() { return; } - size_t num_items = this->deferred_batch_.items.size(); + size_t num_items = this->deferred_batch_.size(); // Fast path for single message - allocate exact size needed if (num_items == 1) { - const auto &item = this->deferred_batch_.items[0]; + const auto &item = this->deferred_batch_[0]; // Let the creator calculate size and encode if it fits uint16_t payload_size = @@ -1764,7 +1766,8 @@ void APIConnection::process_batch_() { // Pre-calculate exact buffer size needed based on message types uint32_t total_estimated_size = 0; - for (const auto &item : this->deferred_batch_.items) { + for (size_t i = 0; i < this->deferred_batch_.size(); i++) { + const auto &item = this->deferred_batch_[i]; total_estimated_size += get_estimated_message_size(item.message_type); } @@ -1785,7 +1788,8 @@ void APIConnection::process_batch_() { uint32_t current_offset = 0; // Process items and encode directly to buffer - for (const auto &item : this->deferred_batch_.items) { + for (size_t i = 0; i < this->deferred_batch_.size(); i++) { + const auto &item = this->deferred_batch_[i]; // Try to encode message // The creator will calculate overhead to determine if the message fits uint16_t payload_size = item.creator(item.entity, this, remaining_size, false, item.message_type); @@ -1840,17 +1844,15 @@ void APIConnection::process_batch_() { // Log messages after send attempt for VV debugging // It's safe to use the buffer for logging at this point regardless of send result for (size_t i = 0; i < items_processed; i++) { - const auto &item = this->deferred_batch_.items[i]; + const auto &item = this->deferred_batch_[i]; this->log_batch_item_(item); } #endif // Handle remaining items more efficiently - if (items_processed < this->deferred_batch_.items.size()) { - // Remove processed items from the beginning - this->deferred_batch_.items.erase(this->deferred_batch_.items.begin(), - this->deferred_batch_.items.begin() + items_processed); - + if (items_processed < this->deferred_batch_.size()) { + // Remove processed items from the beginning with proper cleanup + this->deferred_batch_.remove_front(items_processed); // Reschedule for remaining items this->schedule_batch_(); } else { @@ -1861,23 +1863,16 @@ void APIConnection::process_batch_() { uint16_t APIConnection::MessageCreator::operator()(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single, uint16_t message_type) const { - if (has_tagged_string_ptr_()) { - // Handle string-based messages - switch (message_type) { #ifdef USE_EVENT - case EventResponse::MESSAGE_TYPE: { - auto *e = static_cast(entity); - return APIConnection::try_send_event_response(e, *get_string_ptr_(), conn, remaining_size, is_single); - } -#endif - default: - // Should not happen, return 0 to indicate no message - return 0; - } - } else { - // Function pointer case - return data_.ptr(entity, conn, remaining_size, is_single); + // Special case: EventResponse uses string pointer + if (message_type == EventResponse::MESSAGE_TYPE) { + auto *e = static_cast(entity); + return APIConnection::try_send_event_response(e, *data_.string_ptr, conn, remaining_size, is_single); } +#endif + + // All other message types use function pointers + return data_.function_ptr(entity, conn, remaining_size, is_single); } uint16_t APIConnection::try_send_list_info_done(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, diff --git a/esphome/components/api/api_connection.h b/esphome/components/api/api_connection.h index 410a9ad3a5..151369aa70 100644 --- a/esphome/components/api/api_connection.h +++ b/esphome/components/api/api_connection.h @@ -451,96 +451,53 @@ class APIConnection : public APIServerConnection { // Function pointer type for message encoding using MessageCreatorPtr = uint16_t (*)(EntityBase *, APIConnection *, uint32_t remaining_size, bool is_single); - // Optimized MessageCreator class using tagged pointer class MessageCreator { - // Ensure pointer alignment allows LSB tagging - static_assert(alignof(std::string *) > 1, "String pointer alignment must be > 1 for LSB tagging"); - public: // Constructor for function pointer - MessageCreator(MessageCreatorPtr ptr) { - // Function pointers are always aligned, so LSB is 0 - data_.ptr = ptr; - } + MessageCreator(MessageCreatorPtr ptr) { data_.function_ptr = ptr; } // Constructor for string state capture - explicit MessageCreator(const std::string &str_value) { - // Allocate string and tag the pointer - auto *str = new std::string(str_value); - // Set LSB to 1 to indicate string pointer - data_.tagged = reinterpret_cast(str) | 1; - } + explicit MessageCreator(const std::string &str_value) { data_.string_ptr = new std::string(str_value); } - // Destructor - ~MessageCreator() { - if (has_tagged_string_ptr_()) { - delete get_string_ptr_(); - } - } + // No destructor - cleanup must be called explicitly with message_type - // Copy constructor - MessageCreator(const MessageCreator &other) { - if (other.has_tagged_string_ptr_()) { - auto *str = new std::string(*other.get_string_ptr_()); - data_.tagged = reinterpret_cast(str) | 1; - } else { - data_ = other.data_; - } - } + // Delete copy operations - MessageCreator should only be moved + MessageCreator(const MessageCreator &other) = delete; + MessageCreator &operator=(const MessageCreator &other) = delete; // Move constructor - MessageCreator(MessageCreator &&other) noexcept : data_(other.data_) { other.data_.ptr = nullptr; } - - // Assignment operators (needed for batch deduplication) - MessageCreator &operator=(const MessageCreator &other) { - if (this != &other) { - // Clean up current string data if needed - if (has_tagged_string_ptr_()) { - delete get_string_ptr_(); - } - // Copy new data - if (other.has_tagged_string_ptr_()) { - auto *str = new std::string(*other.get_string_ptr_()); - data_.tagged = reinterpret_cast(str) | 1; - } else { - data_ = other.data_; - } - } - return *this; - } + MessageCreator(MessageCreator &&other) noexcept : data_(other.data_) { other.data_.function_ptr = nullptr; } + // Move assignment MessageCreator &operator=(MessageCreator &&other) noexcept { if (this != &other) { - // Clean up current string data if needed - if (has_tagged_string_ptr_()) { - delete get_string_ptr_(); - } - // Move data + // IMPORTANT: Caller must ensure cleanup() was called if this contains a string! + // In our usage, this happens in add_item() deduplication and vector::erase() data_ = other.data_; - // Reset other to safe state - other.data_.ptr = nullptr; + other.data_.function_ptr = nullptr; } return *this; } - // Call operator - now accepts message_type as parameter + // Call operator - uses message_type to determine union type uint16_t operator()(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single, uint16_t message_type) const; - private: - // Check if this contains a string pointer - bool has_tagged_string_ptr_() const { return (data_.tagged & 1) != 0; } - - // Get the actual string pointer (clears the tag bit) - std::string *get_string_ptr_() const { - // NOLINTNEXTLINE(performance-no-int-to-ptr) - return reinterpret_cast(data_.tagged & ~uintptr_t(1)); + // Manual cleanup method - must be called before destruction for string types + void cleanup(uint16_t message_type) { +#ifdef USE_EVENT + if (message_type == EventResponse::MESSAGE_TYPE && data_.string_ptr != nullptr) { + delete data_.string_ptr; + data_.string_ptr = nullptr; + } +#endif } - union { - MessageCreatorPtr ptr; - uintptr_t tagged; - } data_; // 4 bytes on 32-bit + private: + union Data { + MessageCreatorPtr function_ptr; + std::string *string_ptr; + } data_; // 4 bytes on 32-bit, 8 bytes on 64-bit - same as before }; // Generic batching mechanism for both state updates and entity info @@ -558,20 +515,46 @@ class APIConnection : public APIServerConnection { std::vector items; uint32_t batch_start_time{0}; + private: + // Helper to cleanup items from the beginning + void cleanup_items_(size_t count) { + for (size_t i = 0; i < count; i++) { + items[i].creator.cleanup(items[i].message_type); + } + } + + public: DeferredBatch() { // Pre-allocate capacity for typical batch sizes to avoid reallocation items.reserve(8); } + ~DeferredBatch() { + // Ensure cleanup of any remaining items + clear(); + } + // Add item to the batch void add_item(EntityBase *entity, MessageCreator creator, uint16_t message_type); // Add item to the front of the batch (for high priority messages like ping) void add_item_front(EntityBase *entity, MessageCreator creator, uint16_t message_type); + + // Clear all items with proper cleanup void clear() { + cleanup_items_(items.size()); items.clear(); batch_start_time = 0; } + + // Remove processed items from the front with proper cleanup + void remove_front(size_t count) { + cleanup_items_(count); + items.erase(items.begin(), items.begin() + count); + } + bool empty() const { return items.empty(); } + size_t size() const { return items.size(); } + const BatchItem &operator[](size_t index) const { return items[index]; } }; // DeferredBatch here (16 bytes, 4-byte aligned) From 04a46de23794bffe17c8094cdc8534c01cd61906 Mon Sep 17 00:00:00 2001 From: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com> Date: Tue, 1 Jul 2025 19:40:39 -0400 Subject: [PATCH 2/3] [esp32_rmt_led_strip] Reduce memory usage by 32x with IDF 5.3 (#8388) --- .../esp32_rmt_led_strip/led_strip.cpp | 95 +++++++++++++++---- .../esp32_rmt_led_strip/led_strip.h | 13 ++- 2 files changed, 89 insertions(+), 19 deletions(-) diff --git a/esphome/components/esp32_rmt_led_strip/led_strip.cpp b/esphome/components/esp32_rmt_led_strip/led_strip.cpp index dfdf50aa66..389c32882b 100644 --- a/esphome/components/esp32_rmt_led_strip/led_strip.cpp +++ b/esphome/components/esp32_rmt_led_strip/led_strip.cpp @@ -21,6 +21,43 @@ static const uint32_t RMT_CLK_FREQ = 80000000; static const uint8_t RMT_CLK_DIV = 2; #endif +static const size_t RMT_SYMBOLS_PER_BYTE = 8; + +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0) +static size_t IRAM_ATTR HOT encoder_callback(const void *data, size_t size, size_t symbols_written, size_t symbols_free, + rmt_symbol_word_t *symbols, bool *done, void *arg) { + auto *params = static_cast(arg); + const auto *bytes = static_cast(data); + size_t index = symbols_written / RMT_SYMBOLS_PER_BYTE; + + // convert byte to symbols + if (index < size) { + if (symbols_free < RMT_SYMBOLS_PER_BYTE) { + return 0; + } + for (int32_t i = 0; i < RMT_SYMBOLS_PER_BYTE; i++) { + if (bytes[index] & (1 << (7 - i))) { + symbols[i] = params->bit1; + } else { + symbols[i] = params->bit0; + } + } + if ((index + 1) >= size && params->reset.duration0 == 0 && params->reset.duration1 == 0) { + *done = true; + } + return RMT_SYMBOLS_PER_BYTE; + } + + // send reset + if (symbols_free < 1) { + return 0; + } + symbols[0] = params->reset; + *done = true; + return 1; +} +#endif + void ESP32RMTLEDStripLightOutput::setup() { ESP_LOGCONFIG(TAG, "Running setup"); @@ -42,10 +79,15 @@ void ESP32RMTLEDStripLightOutput::setup() { return; } +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0) + // copy of the led buffer + this->rmt_buf_ = allocator.allocate(buffer_size); +#else RAMAllocator rmt_allocator(this->use_psram_ ? 0 : RAMAllocator::ALLOC_INTERNAL); // 8 bits per byte, 1 rmt_symbol_word_t per bit + 1 rmt_symbol_word_t for reset this->rmt_buf_ = rmt_allocator.allocate(buffer_size * 8 + 1); +#endif rmt_tx_channel_config_t channel; memset(&channel, 0, sizeof(channel)); @@ -65,6 +107,18 @@ void ESP32RMTLEDStripLightOutput::setup() { return; } +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0) + rmt_simple_encoder_config_t encoder; + memset(&encoder, 0, sizeof(encoder)); + encoder.callback = encoder_callback; + encoder.arg = &this->params_; + encoder.min_chunk_size = 8; + if (rmt_new_simple_encoder(&encoder, &this->encoder_) != ESP_OK) { + ESP_LOGE(TAG, "Encoder creation failed"); + this->mark_failed(); + return; + } +#else rmt_copy_encoder_config_t encoder; memset(&encoder, 0, sizeof(encoder)); if (rmt_new_copy_encoder(&encoder, &this->encoder_) != ESP_OK) { @@ -72,6 +126,7 @@ void ESP32RMTLEDStripLightOutput::setup() { this->mark_failed(); return; } +#endif if (rmt_enable(this->channel_) != ESP_OK) { ESP_LOGE(TAG, "Enabling channel failed"); @@ -85,20 +140,20 @@ void ESP32RMTLEDStripLightOutput::set_led_params(uint32_t bit0_high, uint32_t bi float ratio = (float) RMT_CLK_FREQ / RMT_CLK_DIV / 1e09f; // 0-bit - this->bit0_.duration0 = (uint32_t) (ratio * bit0_high); - this->bit0_.level0 = 1; - this->bit0_.duration1 = (uint32_t) (ratio * bit0_low); - this->bit0_.level1 = 0; + this->params_.bit0.duration0 = (uint32_t) (ratio * bit0_high); + this->params_.bit0.level0 = 1; + this->params_.bit0.duration1 = (uint32_t) (ratio * bit0_low); + this->params_.bit0.level1 = 0; // 1-bit - this->bit1_.duration0 = (uint32_t) (ratio * bit1_high); - this->bit1_.level0 = 1; - this->bit1_.duration1 = (uint32_t) (ratio * bit1_low); - this->bit1_.level1 = 0; + this->params_.bit1.duration0 = (uint32_t) (ratio * bit1_high); + this->params_.bit1.level0 = 1; + this->params_.bit1.duration1 = (uint32_t) (ratio * bit1_low); + this->params_.bit1.level1 = 0; // reset - this->reset_.duration0 = (uint32_t) (ratio * reset_time_high); - this->reset_.level0 = 1; - this->reset_.duration1 = (uint32_t) (ratio * reset_time_low); - this->reset_.level1 = 0; + this->params_.reset.duration0 = (uint32_t) (ratio * reset_time_high); + this->params_.reset.level0 = 1; + this->params_.reset.duration1 = (uint32_t) (ratio * reset_time_low); + this->params_.reset.level1 = 0; } void ESP32RMTLEDStripLightOutput::write_state(light::LightState *state) { @@ -122,6 +177,9 @@ void ESP32RMTLEDStripLightOutput::write_state(light::LightState *state) { } delayMicroseconds(50); +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0) + memcpy(this->rmt_buf_, this->buf_, this->get_buffer_size_()); +#else size_t buffer_size = this->get_buffer_size_(); size_t size = 0; @@ -131,7 +189,7 @@ void ESP32RMTLEDStripLightOutput::write_state(light::LightState *state) { while (size < buffer_size) { uint8_t b = *psrc; for (int i = 0; i < 8; i++) { - pdest->val = b & (1 << (7 - i)) ? this->bit1_.val : this->bit0_.val; + pdest->val = b & (1 << (7 - i)) ? this->params_.bit1.val : this->params_.bit0.val; pdest++; len++; } @@ -139,17 +197,20 @@ void ESP32RMTLEDStripLightOutput::write_state(light::LightState *state) { psrc++; } - if (this->reset_.duration0 > 0 || this->reset_.duration1 > 0) { - pdest->val = this->reset_.val; + if (this->params_.reset.duration0 > 0 || this->params_.reset.duration1 > 0) { + pdest->val = this->params_.reset.val; pdest++; len++; } +#endif rmt_transmit_config_t config; memset(&config, 0, sizeof(config)); - config.loop_count = 0; - config.flags.eot_level = 0; +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0) + error = rmt_transmit(this->channel_, this->encoder_, this->rmt_buf_, this->get_buffer_size_(), &config); +#else error = rmt_transmit(this->channel_, this->encoder_, this->rmt_buf_, len * sizeof(rmt_symbol_word_t), &config); +#endif if (error != ESP_OK) { ESP_LOGE(TAG, "RMT TX error"); this->status_set_warning(); diff --git a/esphome/components/esp32_rmt_led_strip/led_strip.h b/esphome/components/esp32_rmt_led_strip/led_strip.h index c6a2b4bc9f..72ce659b4f 100644 --- a/esphome/components/esp32_rmt_led_strip/led_strip.h +++ b/esphome/components/esp32_rmt_led_strip/led_strip.h @@ -25,6 +25,12 @@ enum RGBOrder : uint8_t { ORDER_BRG, }; +struct LedParams { + rmt_symbol_word_t bit0; + rmt_symbol_word_t bit1; + rmt_symbol_word_t reset; +}; + class ESP32RMTLEDStripLightOutput : public light::AddressableLight { public: void setup() override; @@ -72,12 +78,15 @@ class ESP32RMTLEDStripLightOutput : public light::AddressableLight { uint8_t *buf_{nullptr}; uint8_t *effect_data_{nullptr}; + LedParams params_; rmt_channel_handle_t channel_{nullptr}; rmt_encoder_handle_t encoder_{nullptr}; +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0) + uint8_t *rmt_buf_{nullptr}; +#else rmt_symbol_word_t *rmt_buf_{nullptr}; - rmt_symbol_word_t bit0_, bit1_, reset_; +#endif uint32_t rmt_symbols_{48}; - uint8_t pin_; uint16_t num_leds_; bool is_rgbw_{false}; From 6e42d009fbdf67bebf433f806621c0e2280cc6a1 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 1 Jul 2025 20:26:34 -0500 Subject: [PATCH 3/3] Fix bytes field encoding in protobuf code generator --- esphome/components/api/api_pb2.cpp | 24 +++++++++++++----------- script/api_protobuf/api_protobuf.py | 6 +++++- 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/esphome/components/api/api_pb2.cpp b/esphome/components/api/api_pb2.cpp index 9793565ee5..7b14c803b2 100644 --- a/esphome/components/api/api_pb2.cpp +++ b/esphome/components/api/api_pb2.cpp @@ -3493,7 +3493,7 @@ bool SubscribeLogsResponse::decode_length(uint32_t field_id, ProtoLengthDelimite } void SubscribeLogsResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_enum(1, this->level); - buffer.encode_string(3, this->message); + buffer.encode_bytes(3, reinterpret_cast(this->message.data()), this->message.size()); buffer.encode_bool(4, this->send_failed); } void SubscribeLogsResponse::calculate_size(uint32_t &total_size) const { @@ -3529,7 +3529,9 @@ bool NoiseEncryptionSetKeyRequest::decode_length(uint32_t field_id, ProtoLengthD return false; } } -void NoiseEncryptionSetKeyRequest::encode(ProtoWriteBuffer buffer) const { buffer.encode_string(1, this->key); } +void NoiseEncryptionSetKeyRequest::encode(ProtoWriteBuffer buffer) const { + buffer.encode_bytes(1, reinterpret_cast(this->key.data()), this->key.size()); +} void NoiseEncryptionSetKeyRequest::calculate_size(uint32_t &total_size) const { ProtoSize::add_string_field(total_size, 1, this->key, false); } @@ -4266,7 +4268,7 @@ bool CameraImageResponse::decode_32bit(uint32_t field_id, Proto32Bit value) { } void CameraImageResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_fixed32(1, this->key); - buffer.encode_string(2, this->data); + buffer.encode_bytes(2, reinterpret_cast(this->data.data()), this->data.size()); buffer.encode_bool(3, this->done); } void CameraImageResponse::calculate_size(uint32_t &total_size) const { @@ -6784,7 +6786,7 @@ void BluetoothServiceData::encode(ProtoWriteBuffer buffer) const { for (auto &it : this->legacy_data) { buffer.encode_uint32(2, it, true); } - buffer.encode_string(3, this->data); + buffer.encode_bytes(3, reinterpret_cast(this->data.data()), this->data.size()); } void BluetoothServiceData::calculate_size(uint32_t &total_size) const { ProtoSize::add_string_field(total_size, 1, this->uuid, false); @@ -6858,7 +6860,7 @@ bool BluetoothLEAdvertisementResponse::decode_length(uint32_t field_id, ProtoLen } void BluetoothLEAdvertisementResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_uint64(1, this->address); - buffer.encode_string(2, this->name); + buffer.encode_bytes(2, reinterpret_cast(this->name.data()), this->name.size()); buffer.encode_sint32(3, this->rssi); for (auto &it : this->service_uuids) { buffer.encode_string(4, it, true); @@ -6959,7 +6961,7 @@ void BluetoothLERawAdvertisement::encode(ProtoWriteBuffer buffer) const { buffer.encode_uint64(1, this->address); buffer.encode_sint32(2, this->rssi); buffer.encode_uint32(3, this->address_type); - buffer.encode_string(4, this->data); + buffer.encode_bytes(4, reinterpret_cast(this->data.data()), this->data.size()); } void BluetoothLERawAdvertisement::calculate_size(uint32_t &total_size) const { ProtoSize::add_uint64_field(total_size, 1, this->address, false); @@ -7492,7 +7494,7 @@ bool BluetoothGATTReadResponse::decode_length(uint32_t field_id, ProtoLengthDeli void BluetoothGATTReadResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_uint64(1, this->address); buffer.encode_uint32(2, this->handle); - buffer.encode_string(3, this->data); + buffer.encode_bytes(3, reinterpret_cast(this->data.data()), this->data.size()); } void BluetoothGATTReadResponse::calculate_size(uint32_t &total_size) const { ProtoSize::add_uint64_field(total_size, 1, this->address, false); @@ -7551,7 +7553,7 @@ void BluetoothGATTWriteRequest::encode(ProtoWriteBuffer buffer) const { buffer.encode_uint64(1, this->address); buffer.encode_uint32(2, this->handle); buffer.encode_bool(3, this->response); - buffer.encode_string(4, this->data); + buffer.encode_bytes(4, reinterpret_cast(this->data.data()), this->data.size()); } void BluetoothGATTWriteRequest::calculate_size(uint32_t &total_size) const { ProtoSize::add_uint64_field(total_size, 1, this->address, false); @@ -7648,7 +7650,7 @@ bool BluetoothGATTWriteDescriptorRequest::decode_length(uint32_t field_id, Proto void BluetoothGATTWriteDescriptorRequest::encode(ProtoWriteBuffer buffer) const { buffer.encode_uint64(1, this->address); buffer.encode_uint32(2, this->handle); - buffer.encode_string(3, this->data); + buffer.encode_bytes(3, reinterpret_cast(this->data.data()), this->data.size()); } void BluetoothGATTWriteDescriptorRequest::calculate_size(uint32_t &total_size) const { ProtoSize::add_uint64_field(total_size, 1, this->address, false); @@ -7750,7 +7752,7 @@ bool BluetoothGATTNotifyDataResponse::decode_length(uint32_t field_id, ProtoLeng void BluetoothGATTNotifyDataResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_uint64(1, this->address); buffer.encode_uint32(2, this->handle); - buffer.encode_string(3, this->data); + buffer.encode_bytes(3, reinterpret_cast(this->data.data()), this->data.size()); } void BluetoothGATTNotifyDataResponse::calculate_size(uint32_t &total_size) const { ProtoSize::add_uint64_field(total_size, 1, this->address, false); @@ -8480,7 +8482,7 @@ bool VoiceAssistantAudio::decode_length(uint32_t field_id, ProtoLengthDelimited } } void VoiceAssistantAudio::encode(ProtoWriteBuffer buffer) const { - buffer.encode_string(1, this->data); + buffer.encode_bytes(1, reinterpret_cast(this->data.data()), this->data.size()); buffer.encode_bool(2, this->end); } void VoiceAssistantAudio::calculate_size(uint32_t &total_size) const { diff --git a/script/api_protobuf/api_protobuf.py b/script/api_protobuf/api_protobuf.py index ad8e41ba5e..15313f48ee 100755 --- a/script/api_protobuf/api_protobuf.py +++ b/script/api_protobuf/api_protobuf.py @@ -526,9 +526,13 @@ class BytesType(TypeInfo): reference_type = "std::string &" const_reference_type = "const std::string &" decode_length = "value.as_string()" - encode_func = "encode_string" + encode_func = "encode_bytes" wire_type = WireType.LENGTH_DELIMITED # Uses wire type 2 + @property + def encode_content(self) -> str: + return f"buffer.encode_bytes({self.number}, reinterpret_cast(this->{self.field_name}.data()), this->{self.field_name}.size());" + def dump(self, name: str) -> str: o = f'out.append("\'").append({name}).append("\'");' return o