diff --git a/esphome/components/api/api_frame_helper.cpp b/esphome/components/api/api_frame_helper.cpp index 669be8052c..90c35c3ec0 100644 --- a/esphome/components/api/api_frame_helper.cpp +++ b/esphome/components/api/api_frame_helper.cpp @@ -91,12 +91,14 @@ APIError APIFrameHelper::write_raw_(const struct iovec *iov, int iovcnt) { // Try to send the remaining data in this buffer ssize_t sent = this->socket_->write(front_buffer.current_data(), front_buffer.remaining()); - if (this->is_would_block(sent)) { - // Socket would block, we'll try again later - } else if (sent == -1) { - ESP_LOGVV(TAG, "%s: Socket write failed with errno %d", this->info_.c_str(), errno); - this->state_ = State::FAILED; - return APIError::SOCKET_WRITE_FAILED; // Socket write failed + if (sent == -1) { + if (errno != EWOULDBLOCK && errno != EAGAIN) { + // Real socket error (not just would block) + ESP_LOGVV(TAG, "%s: Socket write failed with errno %d", this->info_.c_str(), errno); + this->state_ = State::FAILED; + return APIError::SOCKET_WRITE_FAILED; // Socket write failed + } + // Socket would block, we'll try again later and continue execution to append new data to the buffer } else if (sent > 0 && static_cast(sent) < front_buffer.remaining()) { // Partially sent, update offset front_buffer.offset += sent; @@ -129,19 +131,20 @@ APIError APIFrameHelper::write_raw_(const struct iovec *iov, int iovcnt) { // Try to send directly if no buffered data ssize_t sent = this->socket_->writev(iov, iovcnt); - if (this->is_would_block(sent)) { - // Socket would block, buffer the data - SendBuffer buffer; - buffer.data.reserve(total_write_len); + if (sent == -1) { + if (errno == EWOULDBLOCK || errno == EAGAIN) { + // Socket would block, buffer the data + SendBuffer buffer; + buffer.data.reserve(total_write_len); - for (int i = 0; i < iovcnt; i++) { - const uint8_t *data = reinterpret_cast(iov[i].iov_base); - buffer.data.insert(buffer.data.end(), data, data + iov[i].iov_len); + for (int i = 0; i < iovcnt; i++) { + const uint8_t *data = reinterpret_cast(iov[i].iov_base); + buffer.data.insert(buffer.data.end(), data, data + iov[i].iov_len); + } + + this->tx_buf_.push_back(std::move(buffer)); + return APIError::OK; // Success, data buffered } - - this->tx_buf_.push_back(std::move(buffer)); - return APIError::OK; // Success, data buffered - } else if (sent == -1) { // Socket error ESP_LOGVV(TAG, "%s: Socket write failed with errno %d", this->info_.c_str(), errno); this->state_ = State::FAILED; @@ -184,10 +187,11 @@ APIError APIFrameHelper::try_send_tx_buf_() { // Try to send the remaining data in this buffer ssize_t sent = this->socket_->write(front_buffer.current_data(), front_buffer.remaining()); - if (this->is_would_block(sent)) { - // Socket would block, try again later - break; - } else if (sent == -1) { + if (sent == -1) { + if (errno == EWOULDBLOCK || errno == EAGAIN) { + // Socket would block, try again later + break; + } // Socket error state_ = State::FAILED; ESP_LOGVV(TAG, "%s: Socket write failed with errno %d", this->info_.c_str(), errno); diff --git a/esphome/components/api/api_frame_helper.h b/esphome/components/api/api_frame_helper.h index c4e7072666..15d9d8664d 100644 --- a/esphome/components/api/api_frame_helper.h +++ b/esphome/components/api/api_frame_helper.h @@ -164,14 +164,6 @@ class APIFrameHelper { // Try to send data from the tx buffer APIError try_send_tx_buf_(); - - // Check if the socket would block based on return value - inline bool is_would_block(ssize_t ret) const { - if (ret == -1) { - return errno == EWOULDBLOCK || errno == EAGAIN; - } - return ret == 0; - } }; #ifdef USE_API_NOISE diff --git a/esphome/components/api/proto.h b/esphome/components/api/proto.h index b8ee6b7920..e110a58eda 100644 --- a/esphome/components/api/proto.h +++ b/esphome/components/api/proto.h @@ -20,16 +20,26 @@ class ProtoVarInt { explicit ProtoVarInt(uint64_t value) : value_(value) {} static optional parse(const uint8_t *buffer, uint32_t len, uint32_t *consumed) { - if (consumed != nullptr) - *consumed = 0; - - if (len == 0) + if (len == 0) { + if (consumed != nullptr) + *consumed = 0; return {}; + } - uint64_t result = 0; - uint8_t bitpos = 0; + // Most common case: single-byte varint (values 0-127) + if ((buffer[0] & 0x80) == 0) { + if (consumed != nullptr) + *consumed = 1; + return ProtoVarInt(buffer[0]); + } - for (uint32_t i = 0; i < len; i++) { + // General case for multi-byte varints + // Since we know buffer[0]'s high bit is set, initialize with its value + uint64_t result = buffer[0] & 0x7F; + uint8_t bitpos = 7; + + // Start from the second byte since we've already processed the first + for (uint32_t i = 1; i < len; i++) { uint8_t val = buffer[i]; result |= uint64_t(val & 0x7F) << uint64_t(bitpos); bitpos += 7; @@ -40,7 +50,9 @@ class ProtoVarInt { } } - return {}; + if (consumed != nullptr) + *consumed = 0; + return {}; // Incomplete or invalid varint } uint32_t as_uint32() const { return this->value_; } diff --git a/requirements.txt b/requirements.txt index 106a6ff901..c89ad4a6e4 100644 --- a/requirements.txt +++ b/requirements.txt @@ -19,7 +19,7 @@ puremagic==1.29 ruamel.yaml==0.18.10 # dashboard_import esphome-glyphsets==0.2.0 pillow==10.4.0 -cairosvg==2.8.0 +cairosvg==2.8.1 freetype-py==2.5.1 # esp-idf requires this, but doesn't bundle it by default