From 783afaeaf2687a4d6c5c311814a020ae8e346ca7 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 15 May 2026 01:21:20 -0700 Subject: [PATCH] [store_yaml] Streaming: advance pos only on successful send MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Drop the pre-advance + rewind-on-failure dance in try_send_store_yaml_; match the camera streaming pattern (advance after send_message succeeds, retry the same chunk on WOULD_BLOCK). Also remove the unreachable `|| pos == 0` branch in the while condition — on_get_yaml_request short-circuits the zero-size path before this function is ever called. --- esphome/components/api/api_connection.cpp | 33 +++++++++++------------ 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/esphome/components/api/api_connection.cpp b/esphome/components/api/api_connection.cpp index 38d06be89b..5cae20b18e 100644 --- a/esphome/components/api/api_connection.cpp +++ b/esphome/components/api/api_connection.cpp @@ -1195,43 +1195,42 @@ void APIConnection::try_send_store_yaml_() { if (this->store_yaml_pos_ == std::numeric_limits::max()) return; auto *comp = store_yaml::global_store_yaml; - if (comp == nullptr) + if (comp == nullptr) { + this->store_yaml_pos_ = std::numeric_limits::max(); return; + } const size_t total = comp->get_size(); - while (this->store_yaml_pos_ < total || this->store_yaml_pos_ == 0) { + // Camera-style streaming: advance the position only after a successful send, + // so a WOULD_BLOCK simply retries the same chunk on the next loop iteration. + while (this->store_yaml_pos_ < total) { if (!this->helper_->can_write_without_blocking()) return; const size_t remaining = total - this->store_yaml_pos_; const size_t to_send = remaining < STORE_YAML_CHUNK_SIZE ? remaining : STORE_YAML_CHUNK_SIZE; - // Pulls a chunk out of PROGMEM into a stack buffer; on ESP8266 this routes + // Copy a chunk out of PROGMEM into a stack buffer; on ESP8266 this routes // through progmem_read_byte, on every other platform it's a plain byte copy. comp->read_chunk(this->store_yaml_pos_, store_yaml_chunk_buf, to_send); GetYamlResponse resp; resp.set_data(store_yaml_chunk_buf, to_send); - const bool first = this->store_yaml_pos_ == 0; - if (first) { + if (this->store_yaml_pos_ == 0) { resp.total_size = static_cast(total); resp.encoding = StringRef(store_yaml::ENCODING); } - this->store_yaml_pos_ += to_send; - const bool done = this->store_yaml_pos_ >= total; - resp.done = done; + resp.done = (this->store_yaml_pos_ + to_send) >= total; - if (!this->send_message(resp)) { - // Send failed; rewind so we retry this chunk next loop. - this->store_yaml_pos_ -= to_send; - return; - } - if (done) { - this->store_yaml_pos_ = std::numeric_limits::max(); - return; - } + if (!this->send_message(resp)) + return; // retry on next loop, pos unchanged + + this->store_yaml_pos_ += to_send; } + + // Reached end successfully — final response (with done=true) already sent above. + this->store_yaml_pos_ = std::numeric_limits::max(); } #endif