From 85995975d85e28cefc168216088e26b0b6f6e4c4 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 5 Feb 2026 12:02:05 +0100 Subject: [PATCH 01/14] tweak --- esphome/components/api/api_connection.cpp | 12 +++++------- esphome/components/api/api_connection.h | 4 +++- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/esphome/components/api/api_connection.cpp b/esphome/components/api/api_connection.cpp index e4e8333cc7..dc665da27c 100644 --- a/esphome/components/api/api_connection.cpp +++ b/esphome/components/api/api_connection.cpp @@ -331,12 +331,8 @@ uint16_t APIConnection::encode_message_to_buffer(ProtoMessage &msg, uint8_t mess std::vector &shared_buf = conn->parent_->get_shared_buffer_ref(); if (conn->flags_.batch_first_message) { - // First message - clear flag + // First message - buffer already prepared by caller, just clear flag conn->flags_.batch_first_message = false; - // If buffer not prepped by caller (batch pre-reserves with size == header_padding), prep now - if (shared_buf.size() != header_padding) { - conn->prepare_first_message_buffer(shared_buf, header_padding, total_calculated_size); - } } else { // Batch message second or later // Add padding for previous message footer + this message header @@ -1863,6 +1859,7 @@ void APIConnection::process_batch_() { // Fast path for single message - allocate exact size needed if (num_items == 1) { const auto &item = this->deferred_batch_[0]; + this->prepare_first_message_buffer(shared_buf, item.estimated_size); // Let dispatch_message_ calculate size and encode if it fits uint16_t payload_size = this->dispatch_message_(item, std::numeric_limits::max(), true); @@ -1902,9 +1899,10 @@ void APIConnection::process_batch_() { total_estimated_size += item.estimated_size; } - // Calculate total overhead for all messages - // Reserve based on estimated size (much more accurate than 24-byte worst-case) + // Prepare buffer with total estimated size for all messages (already cleared above) shared_buf.reserve(total_estimated_size); + shared_buf.resize(header_padding); + this->flags_.batch_first_message = true; size_t items_processed = 0; uint16_t remaining_size = std::numeric_limits::max(); diff --git a/esphome/components/api/api_connection.h b/esphome/components/api/api_connection.h index 2c9202b561..5f7975d5e7 100644 --- a/esphome/components/api/api_connection.h +++ b/esphome/components/api/api_connection.h @@ -653,9 +653,11 @@ class APIConnection final : public APIServerConnection { bool send_message_smart_(EntityBase *entity, uint8_t message_type, uint8_t estimated_size, uint8_t aux_data_index = DeferredBatch::AUX_DATA_UNUSED) { if (this->should_send_immediately_(message_type) && this->helper_->can_write_without_blocking()) { + auto &shared_buf = this->parent_->get_shared_buffer_ref(); + this->prepare_first_message_buffer(shared_buf, estimated_size); DeferredBatch::BatchItem item{entity, message_type, estimated_size, aux_data_index}; if (this->dispatch_message_(item, MAX_BATCH_PACKET_SIZE, true) && - this->send_buffer(ProtoWriteBuffer{&this->parent_->get_shared_buffer_ref()}, message_type)) { + this->send_buffer(ProtoWriteBuffer{&shared_buf}, message_type)) { #ifdef HAS_PROTO_MESSAGE_DUMP this->log_batch_item_(item); #endif From 161f5eb731071bf48c84f92ed00ba913f06b4e25 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 5 Feb 2026 12:02:57 +0100 Subject: [PATCH 02/14] tweak --- esphome/components/api/api_connection.cpp | 17 +++++++++++++++++ esphome/components/api/api_connection.h | 16 +--------------- 2 files changed, 18 insertions(+), 15 deletions(-) diff --git a/esphome/components/api/api_connection.cpp b/esphome/components/api/api_connection.cpp index dc665da27c..30e794dab7 100644 --- a/esphome/components/api/api_connection.cpp +++ b/esphome/components/api/api_connection.cpp @@ -1828,6 +1828,23 @@ void APIConnection::DeferredBatch::add_item_front(EntityBase *entity, uint8_t me } } +bool APIConnection::send_message_smart_(EntityBase *entity, uint8_t message_type, uint8_t estimated_size, + uint8_t aux_data_index) { + if (this->should_send_immediately_(message_type) && this->helper_->can_write_without_blocking()) { + auto &shared_buf = this->parent_->get_shared_buffer_ref(); + this->prepare_first_message_buffer(shared_buf, estimated_size); + DeferredBatch::BatchItem item{entity, message_type, estimated_size, aux_data_index}; + if (this->dispatch_message_(item, MAX_BATCH_PACKET_SIZE, true) && + this->send_buffer(ProtoWriteBuffer{&shared_buf}, message_type)) { +#ifdef HAS_PROTO_MESSAGE_DUMP + this->log_batch_item_(item); +#endif + return true; + } + } + return this->schedule_message_(entity, message_type, estimated_size, aux_data_index); +} + bool APIConnection::schedule_batch_() { if (!this->flags_.batch_scheduled) { this->flags_.batch_scheduled = true; diff --git a/esphome/components/api/api_connection.h b/esphome/components/api/api_connection.h index 5f7975d5e7..2acad621b3 100644 --- a/esphome/components/api/api_connection.h +++ b/esphome/components/api/api_connection.h @@ -651,21 +651,7 @@ class APIConnection final : public APIServerConnection { // Tries immediate send if should_send_immediately_() returns true and buffer has space // Falls back to batching if immediate send fails or isn't applicable bool send_message_smart_(EntityBase *entity, uint8_t message_type, uint8_t estimated_size, - uint8_t aux_data_index = DeferredBatch::AUX_DATA_UNUSED) { - if (this->should_send_immediately_(message_type) && this->helper_->can_write_without_blocking()) { - auto &shared_buf = this->parent_->get_shared_buffer_ref(); - this->prepare_first_message_buffer(shared_buf, estimated_size); - DeferredBatch::BatchItem item{entity, message_type, estimated_size, aux_data_index}; - if (this->dispatch_message_(item, MAX_BATCH_PACKET_SIZE, true) && - this->send_buffer(ProtoWriteBuffer{&shared_buf}, message_type)) { -#ifdef HAS_PROTO_MESSAGE_DUMP - this->log_batch_item_(item); -#endif - return true; - } - } - return this->schedule_message_(entity, message_type, estimated_size, aux_data_index); - } + uint8_t aux_data_index = DeferredBatch::AUX_DATA_UNUSED); // Helper function to schedule a deferred message with known message type bool schedule_message_(EntityBase *entity, uint8_t message_type, uint8_t estimated_size, From 55116a7462fafba686ffe5e9da43997db4482787 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 5 Feb 2026 12:09:03 +0100 Subject: [PATCH 03/14] fixes --- esphome/components/api/api_connection.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/esphome/components/api/api_connection.h b/esphome/components/api/api_connection.h index 2acad621b3..ae8e9ae709 100644 --- a/esphome/components/api/api_connection.h +++ b/esphome/components/api/api_connection.h @@ -277,6 +277,13 @@ class APIConnection final : public APIServerConnection { shared_buf.resize(header_padding); } + // Convenience overload - computes frame overhead internally + void prepare_first_message_buffer(std::vector &shared_buf, size_t payload_size) { + const uint8_t header_padding = this->helper_->frame_header_padding(); + const uint8_t footer_size = this->helper_->frame_footer_size(); + this->prepare_first_message_buffer(shared_buf, header_padding, payload_size + header_padding + footer_size); + } + bool try_to_clear_buffer(bool log_out_of_space); bool send_buffer(ProtoWriteBuffer buffer, uint8_t message_type) override; From 0d093666082bd4373106edfbf95c32ba6e675b47 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 5 Feb 2026 13:41:21 +0100 Subject: [PATCH 04/14] reduce --- esphome/components/api/api_connection.cpp | 36 ++++++++--------------- 1 file changed, 13 insertions(+), 23 deletions(-) diff --git a/esphome/components/api/api_connection.cpp b/esphome/components/api/api_connection.cpp index 30e794dab7..9b724a6407 100644 --- a/esphome/components/api/api_connection.cpp +++ b/esphome/components/api/api_connection.cpp @@ -1873,11 +1873,22 @@ void APIConnection::process_batch_() { auto &shared_buf = this->parent_->get_shared_buffer_ref(); size_t num_items = this->deferred_batch_.size(); + // Cache these values to avoid repeated virtual calls + const uint8_t header_padding = this->helper_->frame_header_padding(); + const uint8_t footer_size = this->helper_->frame_footer_size(); + + // Pre-calculate exact buffer size needed based on message types + uint32_t total_estimated_size = num_items * (header_padding + footer_size); + for (size_t i = 0; i < this->deferred_batch_.size(); i++) { + const auto &item = this->deferred_batch_[i]; + total_estimated_size += item.estimated_size; + } + + this->prepare_first_message_buffer(shared_buf, header_padding, total_estimated_size); + // Fast path for single message - allocate exact size needed if (num_items == 1) { const auto &item = this->deferred_batch_[0]; - this->prepare_first_message_buffer(shared_buf, item.estimated_size); - // Let dispatch_message_ calculate size and encode if it fits uint16_t payload_size = this->dispatch_message_(item, std::numeric_limits::max(), true); @@ -1901,29 +1912,8 @@ void APIConnection::process_batch_() { alignas(MessageInfo) char message_info_storage[MAX_MESSAGES_PER_BATCH * sizeof(MessageInfo)]; MessageInfo *message_info = reinterpret_cast(message_info_storage); size_t message_count = 0; - - // Cache these values to avoid repeated virtual calls - const uint8_t header_padding = this->helper_->frame_header_padding(); - const uint8_t footer_size = this->helper_->frame_footer_size(); - - // Initialize buffer and tracking variables - shared_buf.clear(); - - // Pre-calculate exact buffer size needed based on message types - uint32_t total_estimated_size = num_items * (header_padding + footer_size); - for (size_t i = 0; i < this->deferred_batch_.size(); i++) { - const auto &item = this->deferred_batch_[i]; - total_estimated_size += item.estimated_size; - } - - // Prepare buffer with total estimated size for all messages (already cleared above) - shared_buf.reserve(total_estimated_size); - shared_buf.resize(header_padding); - this->flags_.batch_first_message = true; - size_t items_processed = 0; uint16_t remaining_size = std::numeric_limits::max(); - // Track where each message's header padding begins in the buffer // For plaintext: this is where the 6-byte header padding starts // For noise: this is where the 7-byte header padding starts From f75db5106f202276185ae940bbfcb320f93a13ab Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 5 Feb 2026 13:44:08 +0100 Subject: [PATCH 05/14] reduce --- esphome/components/api/api_connection.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/esphome/components/api/api_connection.cpp b/esphome/components/api/api_connection.cpp index 9b724a6407..002d96d2e3 100644 --- a/esphome/components/api/api_connection.cpp +++ b/esphome/components/api/api_connection.cpp @@ -1879,9 +1879,8 @@ void APIConnection::process_batch_() { // Pre-calculate exact buffer size needed based on message types uint32_t total_estimated_size = num_items * (header_padding + footer_size); - for (size_t i = 0; i < this->deferred_batch_.size(); i++) { - const auto &item = this->deferred_batch_[i]; - total_estimated_size += item.estimated_size; + for (size_t i = 0; i < num_items; i++) { + total_estimated_size += this->deferred_batch_[i].estimated_size; } this->prepare_first_message_buffer(shared_buf, header_padding, total_estimated_size); From ef6f93a40caf4e40011f8035b5324df772fe7d54 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 5 Feb 2026 13:54:00 +0100 Subject: [PATCH 06/14] cleanup --- esphome/components/api/api_connection.cpp | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/esphome/components/api/api_connection.cpp b/esphome/components/api/api_connection.cpp index 002d96d2e3..c7b85477e1 100644 --- a/esphome/components/api/api_connection.cpp +++ b/esphome/components/api/api_connection.cpp @@ -1910,7 +1910,6 @@ void APIConnection::process_batch_() { // Stack-allocated array for message info alignas(MessageInfo) char message_info_storage[MAX_MESSAGES_PER_BATCH * sizeof(MessageInfo)]; MessageInfo *message_info = reinterpret_cast(message_info_storage); - size_t message_count = 0; size_t items_processed = 0; uint16_t remaining_size = std::numeric_limits::max(); // Track where each message's header padding begins in the buffer @@ -1938,10 +1937,7 @@ void APIConnection::process_batch_() { // This avoids default-constructing all MAX_MESSAGES_PER_BATCH elements // Explicit destruction is not needed because MessageInfo is trivially destructible, // as ensured by the static_assert in its definition. - new (&message_info[message_count++]) MessageInfo(item.message_type, current_offset, proto_payload_size); - - // Update tracking variables - items_processed++; + new (&message_info[items_processed++]) MessageInfo(item.message_type, current_offset, proto_payload_size); // After first message, set remaining size to MAX_BATCH_PACKET_SIZE to avoid fragmentation if (items_processed == 1) { remaining_size = MAX_BATCH_PACKET_SIZE; @@ -1964,7 +1960,7 @@ void APIConnection::process_batch_() { // Send all collected messages APIError err = this->helper_->write_protobuf_messages(ProtoWriteBuffer{&shared_buf}, - std::span(message_info, message_count)); + std::span(message_info, items_processed)); if (err != APIError::OK && err != APIError::WOULD_BLOCK) { this->fatal_error_with_log_(LOG_STR("Batch write failed"), err); } From 8300e9ca879100630581dcfc0d2ebf201ca50004 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 5 Feb 2026 14:02:25 +0100 Subject: [PATCH 07/14] Update esphome/components/api/api_connection.cpp Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- esphome/components/api/api_connection.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/api/api_connection.cpp b/esphome/components/api/api_connection.cpp index c7b85477e1..cfaff74d09 100644 --- a/esphome/components/api/api_connection.cpp +++ b/esphome/components/api/api_connection.cpp @@ -1885,7 +1885,7 @@ void APIConnection::process_batch_() { this->prepare_first_message_buffer(shared_buf, header_padding, total_estimated_size); - // Fast path for single message - allocate exact size needed + // Fast path for single message - buffer already allocated above if (num_items == 1) { const auto &item = this->deferred_batch_[0]; // Let dispatch_message_ calculate size and encode if it fits From 282b4755321e3cef23e1228c451c93d33a2c0528 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 5 Feb 2026 14:08:23 +0100 Subject: [PATCH 08/14] tweak --- esphome/components/api/api_connection.h | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/esphome/components/api/api_connection.h b/esphome/components/api/api_connection.h index ae8e9ae709..7d802875f5 100644 --- a/esphome/components/api/api_connection.h +++ b/esphome/components/api/api_connection.h @@ -257,13 +257,8 @@ class APIConnection final : public APIServerConnection { void on_no_setup_connection() override; ProtoWriteBuffer create_buffer(uint32_t reserve_size) override { // FIXME: ensure no recursive writes can happen - - // Get header padding size - used for both reserve and insert - uint8_t header_padding = this->helper_->frame_header_padding(); - // Get shared buffer from parent server std::vector &shared_buf = this->parent_->get_shared_buffer_ref(); - this->prepare_first_message_buffer(shared_buf, header_padding, - reserve_size + header_padding + this->helper_->frame_footer_size()); + this->prepare_first_message_buffer(shared_buf, reserve_size); return {&shared_buf}; } From 35dc54f2429a34ab243dfb200389728408d9b259 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 5 Feb 2026 14:13:54 +0100 Subject: [PATCH 09/14] cleanup --- esphome/components/api/api_connection.cpp | 8 ++++++ esphome/components/api/api_connection.h | 7 +---- esphome/components/api/proto.h | 31 +++++------------------ 3 files changed, 16 insertions(+), 30 deletions(-) diff --git a/esphome/components/api/api_connection.cpp b/esphome/components/api/api_connection.cpp index cfaff74d09..c377645f51 100644 --- a/esphome/components/api/api_connection.cpp +++ b/esphome/components/api/api_connection.cpp @@ -1768,6 +1768,14 @@ bool APIConnection::try_to_clear_buffer(bool log_out_of_space) { } return false; } +bool APIConnection::send_message_(const ProtoMessage &msg, uint8_t message_type) { + ProtoSize size; + msg.calculate_size(size); + std::vector &shared_buf = this->parent_->get_shared_buffer_ref(); + this->prepare_first_message_buffer(shared_buf, size.get_size()); + msg.encode({&shared_buf}); + return this->send_buffer({&shared_buf}, message_type); +} bool APIConnection::send_buffer(ProtoWriteBuffer buffer, uint8_t message_type) { const bool is_log_message = (message_type == SubscribeLogsResponse::MESSAGE_TYPE); diff --git a/esphome/components/api/api_connection.h b/esphome/components/api/api_connection.h index 7d802875f5..156ccdb57c 100644 --- a/esphome/components/api/api_connection.h +++ b/esphome/components/api/api_connection.h @@ -255,12 +255,7 @@ class APIConnection final : public APIServerConnection { void on_fatal_error() override; void on_no_setup_connection() override; - ProtoWriteBuffer create_buffer(uint32_t reserve_size) override { - // FIXME: ensure no recursive writes can happen - std::vector &shared_buf = this->parent_->get_shared_buffer_ref(); - this->prepare_first_message_buffer(shared_buf, reserve_size); - return {&shared_buf}; - } + bool send_message_(const ProtoMessage &msg, uint8_t message_type) override; void prepare_first_message_buffer(std::vector &shared_buf, size_t header_padding, size_t total_size) { shared_buf.clear(); diff --git a/esphome/components/api/proto.h b/esphome/components/api/proto.h index 552b4a4625..2614af294e 100644 --- a/esphome/components/api/proto.h +++ b/esphome/components/api/proto.h @@ -957,32 +957,15 @@ class ProtoService { virtual bool is_connection_setup() = 0; virtual void on_fatal_error() = 0; virtual void on_no_setup_connection() = 0; - /** - * Create a buffer with a reserved size. - * @param reserve_size The number of bytes to pre-allocate in the buffer. This is a hint - * to optimize memory usage and avoid reallocations during encoding. - * Implementations should aim to allocate at least this size. - * @return A ProtoWriteBuffer object with the reserved size. - */ - virtual ProtoWriteBuffer create_buffer(uint32_t reserve_size) = 0; virtual bool send_buffer(ProtoWriteBuffer buffer, uint8_t message_type) = 0; virtual void read_message(uint32_t msg_size, uint32_t msg_type, const uint8_t *msg_data) = 0; - - // Optimized method that pre-allocates buffer based on message size - bool send_message_(const ProtoMessage &msg, uint8_t message_type) { - ProtoSize size; - msg.calculate_size(size); - uint32_t msg_size = size.get_size(); - - // Create a pre-sized buffer - auto buffer = this->create_buffer(msg_size); - - // Encode message into the buffer - msg.encode(buffer); - - // Send the buffer - return this->send_buffer(buffer, message_type); - } + /** + * Send a protobuf message by calculating its size, allocating a buffer, encoding, and sending. + * @param msg The protobuf message to send. + * @param message_type The message type identifier. + * @return True if the message was sent successfully, false otherwise. + */ + virtual bool send_message_(const ProtoMessage &msg, uint8_t message_type) = 0; // Authentication helper methods inline bool check_connection_setup_() { From 9d43a9326e37364e925e3cfb088dd14ffc444a82 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 5 Feb 2026 14:24:58 +0100 Subject: [PATCH 10/14] tidy --- esphome/components/api/api_connection.cpp | 10 +++++++--- esphome/components/api/api_connection.h | 2 +- esphome/components/api/api_pb2_service.h | 8 -------- esphome/components/api/proto.h | 2 +- script/api_protobuf/api_protobuf.py | 9 --------- 5 files changed, 9 insertions(+), 22 deletions(-) diff --git a/esphome/components/api/api_connection.cpp b/esphome/components/api/api_connection.cpp index c377645f51..b3c008d71c 100644 --- a/esphome/components/api/api_connection.cpp +++ b/esphome/components/api/api_connection.cpp @@ -1035,7 +1035,7 @@ void APIConnection::try_send_camera_image_() { msg.device_id = camera::Camera::instance()->get_device_id(); #endif - if (!this->send_message_(msg, CameraImageResponse::MESSAGE_TYPE)) { + if (!this->send_message(msg, CameraImageResponse::MESSAGE_TYPE)) { return; // Send failed, try again later } this->image_reader_->consume_data(to_send); @@ -1443,7 +1443,7 @@ bool APIConnection::try_send_log_message(int level, const char *tag, const char SubscribeLogsResponse msg; msg.level = static_cast(level); msg.set_message(reinterpret_cast(line), message_len); - return this->send_message_(msg, SubscribeLogsResponse::MESSAGE_TYPE); + return this->send_message(msg, SubscribeLogsResponse::MESSAGE_TYPE); } void APIConnection::complete_authentication_() { @@ -1768,7 +1768,11 @@ bool APIConnection::try_to_clear_buffer(bool log_out_of_space) { } return false; } -bool APIConnection::send_message_(const ProtoMessage &msg, uint8_t message_type) { +bool APIConnection::send_message(const ProtoMessage &msg, uint8_t message_type) { +#ifdef HAS_PROTO_MESSAGE_DUMP + DumpBuffer dump_buf; + this->log_send_message_(msg.message_name(), msg.dump_to(dump_buf)); +#endif ProtoSize size; msg.calculate_size(size); std::vector &shared_buf = this->parent_->get_shared_buffer_ref(); diff --git a/esphome/components/api/api_connection.h b/esphome/components/api/api_connection.h index 156ccdb57c..14cafe184e 100644 --- a/esphome/components/api/api_connection.h +++ b/esphome/components/api/api_connection.h @@ -255,7 +255,7 @@ class APIConnection final : public APIServerConnection { void on_fatal_error() override; void on_no_setup_connection() override; - bool send_message_(const ProtoMessage &msg, uint8_t message_type) override; + bool send_message(const ProtoMessage &msg, uint8_t message_type) override; void prepare_first_message_buffer(std::vector &shared_buf, size_t header_padding, size_t total_size) { shared_buf.clear(); diff --git a/esphome/components/api/api_pb2_service.h b/esphome/components/api/api_pb2_service.h index 80a61c1041..dff89a95cf 100644 --- a/esphome/components/api/api_pb2_service.h +++ b/esphome/components/api/api_pb2_service.h @@ -18,14 +18,6 @@ class APIServerConnectionBase : public ProtoService { public: #endif - bool send_message(const ProtoMessage &msg, uint8_t message_type) { -#ifdef HAS_PROTO_MESSAGE_DUMP - DumpBuffer dump_buf; - this->log_send_message_(msg.message_name(), msg.dump_to(dump_buf)); -#endif - return this->send_message_(msg, message_type); - } - virtual void on_hello_request(const HelloRequest &value){}; virtual void on_disconnect_request(const DisconnectRequest &value){}; diff --git a/esphome/components/api/proto.h b/esphome/components/api/proto.h index 2614af294e..f651c6e7d5 100644 --- a/esphome/components/api/proto.h +++ b/esphome/components/api/proto.h @@ -965,7 +965,7 @@ class ProtoService { * @param message_type The message type identifier. * @return True if the message was sent successfully, false otherwise. */ - virtual bool send_message_(const ProtoMessage &msg, uint8_t message_type) = 0; + virtual bool send_message(const ProtoMessage &msg, uint8_t message_type) = 0; // Authentication helper methods inline bool check_connection_setup_() { diff --git a/script/api_protobuf/api_protobuf.py b/script/api_protobuf/api_protobuf.py index 8baf6acf11..2df38b0eb0 100755 --- a/script/api_protobuf/api_protobuf.py +++ b/script/api_protobuf/api_protobuf.py @@ -2842,15 +2842,6 @@ static const char *const TAG = "api.service"; hpp += " public:\n" hpp += "#endif\n\n" - # Add non-template send_message method - hpp += " bool send_message(const ProtoMessage &msg, uint8_t message_type) {\n" - hpp += "#ifdef HAS_PROTO_MESSAGE_DUMP\n" - hpp += " DumpBuffer dump_buf;\n" - hpp += " this->log_send_message_(msg.message_name(), msg.dump_to(dump_buf));\n" - hpp += "#endif\n" - hpp += " return this->send_message_(msg, message_type);\n" - hpp += " }\n\n" - # Add logging helper method implementations to cpp cpp += "#ifdef HAS_PROTO_MESSAGE_DUMP\n" cpp += ( From 857c8be1c90bae40613134a3649e4fa69de79b64 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 5 Feb 2026 14:32:05 +0100 Subject: [PATCH 11/14] Revert "tidy" This reverts commit 9d43a9326e37364e925e3cfb088dd14ffc444a82. --- esphome/components/api/api_connection.cpp | 10 +++------- esphome/components/api/api_connection.h | 2 +- esphome/components/api/api_pb2_service.h | 8 ++++++++ esphome/components/api/proto.h | 2 +- script/api_protobuf/api_protobuf.py | 9 +++++++++ 5 files changed, 22 insertions(+), 9 deletions(-) diff --git a/esphome/components/api/api_connection.cpp b/esphome/components/api/api_connection.cpp index b3c008d71c..c377645f51 100644 --- a/esphome/components/api/api_connection.cpp +++ b/esphome/components/api/api_connection.cpp @@ -1035,7 +1035,7 @@ void APIConnection::try_send_camera_image_() { msg.device_id = camera::Camera::instance()->get_device_id(); #endif - if (!this->send_message(msg, CameraImageResponse::MESSAGE_TYPE)) { + if (!this->send_message_(msg, CameraImageResponse::MESSAGE_TYPE)) { return; // Send failed, try again later } this->image_reader_->consume_data(to_send); @@ -1443,7 +1443,7 @@ bool APIConnection::try_send_log_message(int level, const char *tag, const char SubscribeLogsResponse msg; msg.level = static_cast(level); msg.set_message(reinterpret_cast(line), message_len); - return this->send_message(msg, SubscribeLogsResponse::MESSAGE_TYPE); + return this->send_message_(msg, SubscribeLogsResponse::MESSAGE_TYPE); } void APIConnection::complete_authentication_() { @@ -1768,11 +1768,7 @@ bool APIConnection::try_to_clear_buffer(bool log_out_of_space) { } return false; } -bool APIConnection::send_message(const ProtoMessage &msg, uint8_t message_type) { -#ifdef HAS_PROTO_MESSAGE_DUMP - DumpBuffer dump_buf; - this->log_send_message_(msg.message_name(), msg.dump_to(dump_buf)); -#endif +bool APIConnection::send_message_(const ProtoMessage &msg, uint8_t message_type) { ProtoSize size; msg.calculate_size(size); std::vector &shared_buf = this->parent_->get_shared_buffer_ref(); diff --git a/esphome/components/api/api_connection.h b/esphome/components/api/api_connection.h index 14cafe184e..156ccdb57c 100644 --- a/esphome/components/api/api_connection.h +++ b/esphome/components/api/api_connection.h @@ -255,7 +255,7 @@ class APIConnection final : public APIServerConnection { void on_fatal_error() override; void on_no_setup_connection() override; - bool send_message(const ProtoMessage &msg, uint8_t message_type) override; + bool send_message_(const ProtoMessage &msg, uint8_t message_type) override; void prepare_first_message_buffer(std::vector &shared_buf, size_t header_padding, size_t total_size) { shared_buf.clear(); diff --git a/esphome/components/api/api_pb2_service.h b/esphome/components/api/api_pb2_service.h index dff89a95cf..80a61c1041 100644 --- a/esphome/components/api/api_pb2_service.h +++ b/esphome/components/api/api_pb2_service.h @@ -18,6 +18,14 @@ class APIServerConnectionBase : public ProtoService { public: #endif + bool send_message(const ProtoMessage &msg, uint8_t message_type) { +#ifdef HAS_PROTO_MESSAGE_DUMP + DumpBuffer dump_buf; + this->log_send_message_(msg.message_name(), msg.dump_to(dump_buf)); +#endif + return this->send_message_(msg, message_type); + } + virtual void on_hello_request(const HelloRequest &value){}; virtual void on_disconnect_request(const DisconnectRequest &value){}; diff --git a/esphome/components/api/proto.h b/esphome/components/api/proto.h index f651c6e7d5..2614af294e 100644 --- a/esphome/components/api/proto.h +++ b/esphome/components/api/proto.h @@ -965,7 +965,7 @@ class ProtoService { * @param message_type The message type identifier. * @return True if the message was sent successfully, false otherwise. */ - virtual bool send_message(const ProtoMessage &msg, uint8_t message_type) = 0; + virtual bool send_message_(const ProtoMessage &msg, uint8_t message_type) = 0; // Authentication helper methods inline bool check_connection_setup_() { diff --git a/script/api_protobuf/api_protobuf.py b/script/api_protobuf/api_protobuf.py index 2df38b0eb0..8baf6acf11 100755 --- a/script/api_protobuf/api_protobuf.py +++ b/script/api_protobuf/api_protobuf.py @@ -2842,6 +2842,15 @@ static const char *const TAG = "api.service"; hpp += " public:\n" hpp += "#endif\n\n" + # Add non-template send_message method + hpp += " bool send_message(const ProtoMessage &msg, uint8_t message_type) {\n" + hpp += "#ifdef HAS_PROTO_MESSAGE_DUMP\n" + hpp += " DumpBuffer dump_buf;\n" + hpp += " this->log_send_message_(msg.message_name(), msg.dump_to(dump_buf));\n" + hpp += "#endif\n" + hpp += " return this->send_message_(msg, message_type);\n" + hpp += " }\n\n" + # Add logging helper method implementations to cpp cpp += "#ifdef HAS_PROTO_MESSAGE_DUMP\n" cpp += ( From 11bdc66a352264a05bf92ae4b75fbabc711c395f Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 5 Feb 2026 14:37:25 +0100 Subject: [PATCH 12/14] make clang-tidy happy --- esphome/components/api/api_connection.cpp | 6 +++--- esphome/components/api/api_connection.h | 2 +- esphome/components/api/api_pb2_service.h | 2 +- esphome/components/api/proto.h | 3 ++- script/api_protobuf/api_protobuf.py | 2 +- 5 files changed, 8 insertions(+), 7 deletions(-) diff --git a/esphome/components/api/api_connection.cpp b/esphome/components/api/api_connection.cpp index c377645f51..2aa5956f24 100644 --- a/esphome/components/api/api_connection.cpp +++ b/esphome/components/api/api_connection.cpp @@ -1035,7 +1035,7 @@ void APIConnection::try_send_camera_image_() { msg.device_id = camera::Camera::instance()->get_device_id(); #endif - if (!this->send_message_(msg, CameraImageResponse::MESSAGE_TYPE)) { + if (!this->send_message_impl(msg, CameraImageResponse::MESSAGE_TYPE)) { return; // Send failed, try again later } this->image_reader_->consume_data(to_send); @@ -1443,7 +1443,7 @@ bool APIConnection::try_send_log_message(int level, const char *tag, const char SubscribeLogsResponse msg; msg.level = static_cast(level); msg.set_message(reinterpret_cast(line), message_len); - return this->send_message_(msg, SubscribeLogsResponse::MESSAGE_TYPE); + return this->send_message_impl(msg, SubscribeLogsResponse::MESSAGE_TYPE); } void APIConnection::complete_authentication_() { @@ -1768,7 +1768,7 @@ bool APIConnection::try_to_clear_buffer(bool log_out_of_space) { } return false; } -bool APIConnection::send_message_(const ProtoMessage &msg, uint8_t message_type) { +bool APIConnection::send_message_impl(const ProtoMessage &msg, uint8_t message_type) { ProtoSize size; msg.calculate_size(size); std::vector &shared_buf = this->parent_->get_shared_buffer_ref(); diff --git a/esphome/components/api/api_connection.h b/esphome/components/api/api_connection.h index 156ccdb57c..40e4fd61c1 100644 --- a/esphome/components/api/api_connection.h +++ b/esphome/components/api/api_connection.h @@ -255,7 +255,7 @@ class APIConnection final : public APIServerConnection { void on_fatal_error() override; void on_no_setup_connection() override; - bool send_message_(const ProtoMessage &msg, uint8_t message_type) override; + bool send_message_impl(const ProtoMessage &msg, uint8_t message_type) override; void prepare_first_message_buffer(std::vector &shared_buf, size_t header_padding, size_t total_size) { shared_buf.clear(); diff --git a/esphome/components/api/api_pb2_service.h b/esphome/components/api/api_pb2_service.h index 80a61c1041..e2bc1609ed 100644 --- a/esphome/components/api/api_pb2_service.h +++ b/esphome/components/api/api_pb2_service.h @@ -23,7 +23,7 @@ class APIServerConnectionBase : public ProtoService { DumpBuffer dump_buf; this->log_send_message_(msg.message_name(), msg.dump_to(dump_buf)); #endif - return this->send_message_(msg, message_type); + return this->send_message_impl(msg, message_type); } virtual void on_hello_request(const HelloRequest &value){}; diff --git a/esphome/components/api/proto.h b/esphome/components/api/proto.h index 2614af294e..92978f765f 100644 --- a/esphome/components/api/proto.h +++ b/esphome/components/api/proto.h @@ -961,11 +961,12 @@ class ProtoService { virtual void read_message(uint32_t msg_size, uint32_t msg_type, const uint8_t *msg_data) = 0; /** * Send a protobuf message by calculating its size, allocating a buffer, encoding, and sending. + * This is the implementation method - callers should use send_message() which adds logging. * @param msg The protobuf message to send. * @param message_type The message type identifier. * @return True if the message was sent successfully, false otherwise. */ - virtual bool send_message_(const ProtoMessage &msg, uint8_t message_type) = 0; + virtual bool send_message_impl(const ProtoMessage &msg, uint8_t message_type) = 0; // Authentication helper methods inline bool check_connection_setup_() { diff --git a/script/api_protobuf/api_protobuf.py b/script/api_protobuf/api_protobuf.py index 8baf6acf11..4021a062ca 100755 --- a/script/api_protobuf/api_protobuf.py +++ b/script/api_protobuf/api_protobuf.py @@ -2848,7 +2848,7 @@ static const char *const TAG = "api.service"; hpp += " DumpBuffer dump_buf;\n" hpp += " this->log_send_message_(msg.message_name(), msg.dump_to(dump_buf));\n" hpp += "#endif\n" - hpp += " return this->send_message_(msg, message_type);\n" + hpp += " return this->send_message_impl(msg, message_type);\n" hpp += " }\n\n" # Add logging helper method implementations to cpp From 3859f48dddd5fd8f6c7e0f5f13a232d421eebc0f Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 5 Feb 2026 14:38:12 +0100 Subject: [PATCH 13/14] make clang-tidy happy --- esphome/components/api/api_connection.cpp | 6 +++--- esphome/components/api/api_connection.h | 2 +- esphome/components/api/api_pb2_service.h | 2 +- esphome/components/api/proto.h | 2 +- script/api_protobuf/api_protobuf.py | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/esphome/components/api/api_connection.cpp b/esphome/components/api/api_connection.cpp index 2aa5956f24..575f696142 100644 --- a/esphome/components/api/api_connection.cpp +++ b/esphome/components/api/api_connection.cpp @@ -1035,7 +1035,7 @@ void APIConnection::try_send_camera_image_() { msg.device_id = camera::Camera::instance()->get_device_id(); #endif - if (!this->send_message_impl(msg, CameraImageResponse::MESSAGE_TYPE)) { + if (!this->send_message_impl_(msg, CameraImageResponse::MESSAGE_TYPE)) { return; // Send failed, try again later } this->image_reader_->consume_data(to_send); @@ -1443,7 +1443,7 @@ bool APIConnection::try_send_log_message(int level, const char *tag, const char SubscribeLogsResponse msg; msg.level = static_cast(level); msg.set_message(reinterpret_cast(line), message_len); - return this->send_message_impl(msg, SubscribeLogsResponse::MESSAGE_TYPE); + return this->send_message_impl_(msg, SubscribeLogsResponse::MESSAGE_TYPE); } void APIConnection::complete_authentication_() { @@ -1768,7 +1768,7 @@ bool APIConnection::try_to_clear_buffer(bool log_out_of_space) { } return false; } -bool APIConnection::send_message_impl(const ProtoMessage &msg, uint8_t message_type) { +bool APIConnection::send_message_impl_(const ProtoMessage &msg, uint8_t message_type) { ProtoSize size; msg.calculate_size(size); std::vector &shared_buf = this->parent_->get_shared_buffer_ref(); diff --git a/esphome/components/api/api_connection.h b/esphome/components/api/api_connection.h index 40e4fd61c1..5e92d48180 100644 --- a/esphome/components/api/api_connection.h +++ b/esphome/components/api/api_connection.h @@ -255,7 +255,7 @@ class APIConnection final : public APIServerConnection { void on_fatal_error() override; void on_no_setup_connection() override; - bool send_message_impl(const ProtoMessage &msg, uint8_t message_type) override; + bool send_message_impl_(const ProtoMessage &msg, uint8_t message_type) override; void prepare_first_message_buffer(std::vector &shared_buf, size_t header_padding, size_t total_size) { shared_buf.clear(); diff --git a/esphome/components/api/api_pb2_service.h b/esphome/components/api/api_pb2_service.h index e2bc1609ed..fc837dbcae 100644 --- a/esphome/components/api/api_pb2_service.h +++ b/esphome/components/api/api_pb2_service.h @@ -23,7 +23,7 @@ class APIServerConnectionBase : public ProtoService { DumpBuffer dump_buf; this->log_send_message_(msg.message_name(), msg.dump_to(dump_buf)); #endif - return this->send_message_impl(msg, message_type); + return this->send_message_impl_(msg, message_type); } virtual void on_hello_request(const HelloRequest &value){}; diff --git a/esphome/components/api/proto.h b/esphome/components/api/proto.h index 92978f765f..d2f79c263f 100644 --- a/esphome/components/api/proto.h +++ b/esphome/components/api/proto.h @@ -966,7 +966,7 @@ class ProtoService { * @param message_type The message type identifier. * @return True if the message was sent successfully, false otherwise. */ - virtual bool send_message_impl(const ProtoMessage &msg, uint8_t message_type) = 0; + virtual bool send_message_impl_(const ProtoMessage &msg, uint8_t message_type) = 0; // Authentication helper methods inline bool check_connection_setup_() { diff --git a/script/api_protobuf/api_protobuf.py b/script/api_protobuf/api_protobuf.py index 4021a062ca..a4a6d6c175 100755 --- a/script/api_protobuf/api_protobuf.py +++ b/script/api_protobuf/api_protobuf.py @@ -2848,7 +2848,7 @@ static const char *const TAG = "api.service"; hpp += " DumpBuffer dump_buf;\n" hpp += " this->log_send_message_(msg.message_name(), msg.dump_to(dump_buf));\n" hpp += "#endif\n" - hpp += " return this->send_message_impl(msg, message_type);\n" + hpp += " return this->send_message_impl_(msg, message_type);\n" hpp += " }\n\n" # Add logging helper method implementations to cpp From da4fc73c067aa6e9e3e711ae5c6a35bc2593fb2e Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 5 Feb 2026 14:44:13 +0100 Subject: [PATCH 14/14] tweak --- esphome/components/api/api_connection.cpp | 6 +++--- esphome/components/api/api_connection.h | 2 +- esphome/components/api/api_pb2_service.h | 2 +- esphome/components/api/proto.h | 2 +- script/api_protobuf/api_protobuf.py | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/esphome/components/api/api_connection.cpp b/esphome/components/api/api_connection.cpp index 575f696142..2aa5956f24 100644 --- a/esphome/components/api/api_connection.cpp +++ b/esphome/components/api/api_connection.cpp @@ -1035,7 +1035,7 @@ void APIConnection::try_send_camera_image_() { msg.device_id = camera::Camera::instance()->get_device_id(); #endif - if (!this->send_message_impl_(msg, CameraImageResponse::MESSAGE_TYPE)) { + if (!this->send_message_impl(msg, CameraImageResponse::MESSAGE_TYPE)) { return; // Send failed, try again later } this->image_reader_->consume_data(to_send); @@ -1443,7 +1443,7 @@ bool APIConnection::try_send_log_message(int level, const char *tag, const char SubscribeLogsResponse msg; msg.level = static_cast(level); msg.set_message(reinterpret_cast(line), message_len); - return this->send_message_impl_(msg, SubscribeLogsResponse::MESSAGE_TYPE); + return this->send_message_impl(msg, SubscribeLogsResponse::MESSAGE_TYPE); } void APIConnection::complete_authentication_() { @@ -1768,7 +1768,7 @@ bool APIConnection::try_to_clear_buffer(bool log_out_of_space) { } return false; } -bool APIConnection::send_message_impl_(const ProtoMessage &msg, uint8_t message_type) { +bool APIConnection::send_message_impl(const ProtoMessage &msg, uint8_t message_type) { ProtoSize size; msg.calculate_size(size); std::vector &shared_buf = this->parent_->get_shared_buffer_ref(); diff --git a/esphome/components/api/api_connection.h b/esphome/components/api/api_connection.h index 5e92d48180..40e4fd61c1 100644 --- a/esphome/components/api/api_connection.h +++ b/esphome/components/api/api_connection.h @@ -255,7 +255,7 @@ class APIConnection final : public APIServerConnection { void on_fatal_error() override; void on_no_setup_connection() override; - bool send_message_impl_(const ProtoMessage &msg, uint8_t message_type) override; + bool send_message_impl(const ProtoMessage &msg, uint8_t message_type) override; void prepare_first_message_buffer(std::vector &shared_buf, size_t header_padding, size_t total_size) { shared_buf.clear(); diff --git a/esphome/components/api/api_pb2_service.h b/esphome/components/api/api_pb2_service.h index fc837dbcae..e2bc1609ed 100644 --- a/esphome/components/api/api_pb2_service.h +++ b/esphome/components/api/api_pb2_service.h @@ -23,7 +23,7 @@ class APIServerConnectionBase : public ProtoService { DumpBuffer dump_buf; this->log_send_message_(msg.message_name(), msg.dump_to(dump_buf)); #endif - return this->send_message_impl_(msg, message_type); + return this->send_message_impl(msg, message_type); } virtual void on_hello_request(const HelloRequest &value){}; diff --git a/esphome/components/api/proto.h b/esphome/components/api/proto.h index d2f79c263f..92978f765f 100644 --- a/esphome/components/api/proto.h +++ b/esphome/components/api/proto.h @@ -966,7 +966,7 @@ class ProtoService { * @param message_type The message type identifier. * @return True if the message was sent successfully, false otherwise. */ - virtual bool send_message_impl_(const ProtoMessage &msg, uint8_t message_type) = 0; + virtual bool send_message_impl(const ProtoMessage &msg, uint8_t message_type) = 0; // Authentication helper methods inline bool check_connection_setup_() { diff --git a/script/api_protobuf/api_protobuf.py b/script/api_protobuf/api_protobuf.py index a4a6d6c175..4021a062ca 100755 --- a/script/api_protobuf/api_protobuf.py +++ b/script/api_protobuf/api_protobuf.py @@ -2848,7 +2848,7 @@ static const char *const TAG = "api.service"; hpp += " DumpBuffer dump_buf;\n" hpp += " this->log_send_message_(msg.message_name(), msg.dump_to(dump_buf));\n" hpp += "#endif\n" - hpp += " return this->send_message_impl_(msg, message_type);\n" + hpp += " return this->send_message_impl(msg, message_type);\n" hpp += " }\n\n" # Add logging helper method implementations to cpp