diff --git a/esphome/components/api/api_pb2.cpp b/esphome/components/api/api_pb2.cpp index 92efb6f0d8..21056e4899 100644 --- a/esphome/components/api/api_pb2.cpp +++ b/esphome/components/api/api_pb2.cpp @@ -356,7 +356,7 @@ void ListEntitiesFanResponse::calculate_size(uint32_t &total_size) const { ProtoSize::add_enum_field(total_size, 1, static_cast(this->entity_category)); if (!this->supported_preset_modes.empty()) { for (const auto &it : this->supported_preset_modes) { - ProtoSize::add_string_field(total_size, 1, it.length()); + ProtoSize::add_string_field_repeated(total_size, 1, it); } } #ifdef USE_DEVICES @@ -480,7 +480,7 @@ void ListEntitiesLightResponse::calculate_size(uint32_t &total_size) const { ProtoSize::add_float_field(total_size, 1, this->max_mireds); if (!this->effects.empty()) { for (const auto &it : this->effects) { - ProtoSize::add_string_field(total_size, 1, it.length()); + ProtoSize::add_string_field_repeated(total_size, 1, it); } } ProtoSize::add_bool_field(total_size, 1, this->disabled_by_default); @@ -1130,7 +1130,7 @@ void ListEntitiesClimateResponse::calculate_size(uint32_t &total_size) const { } if (!this->supported_custom_fan_modes.empty()) { for (const auto &it : this->supported_custom_fan_modes) { - ProtoSize::add_string_field(total_size, 1, it.length()); + ProtoSize::add_string_field_repeated(total_size, 1, it); } } if (!this->supported_presets.empty()) { @@ -1140,7 +1140,7 @@ void ListEntitiesClimateResponse::calculate_size(uint32_t &total_size) const { } if (!this->supported_custom_presets.empty()) { for (const auto &it : this->supported_custom_presets) { - ProtoSize::add_string_field(total_size, 2, it.length()); + ProtoSize::add_string_field_repeated(total_size, 2, it); } } ProtoSize::add_bool_field(total_size, 2, this->disabled_by_default); @@ -1392,7 +1392,7 @@ void ListEntitiesSelectResponse::calculate_size(uint32_t &total_size) const { #endif if (!this->options.empty()) { for (const auto &it : this->options) { - ProtoSize::add_string_field(total_size, 1, it.length()); + ProtoSize::add_string_field_repeated(total_size, 1, it); } } ProtoSize::add_bool_field(total_size, 1, this->disabled_by_default); @@ -1479,7 +1479,7 @@ void ListEntitiesSirenResponse::calculate_size(uint32_t &total_size) const { ProtoSize::add_bool_field(total_size, 1, this->disabled_by_default); if (!this->tones.empty()) { for (const auto &it : this->tones) { - ProtoSize::add_string_field(total_size, 1, it.length()); + ProtoSize::add_string_field_repeated(total_size, 1, it); } } ProtoSize::add_bool_field(total_size, 1, this->supports_duration); @@ -2347,7 +2347,7 @@ void VoiceAssistantWakeWord::calculate_size(uint32_t &total_size) const { ProtoSize::add_string_field(total_size, 1, this->wake_word_ref_.size()); if (!this->trained_languages.empty()) { for (const auto &it : this->trained_languages) { - ProtoSize::add_string_field(total_size, 1, it.length()); + ProtoSize::add_string_field_repeated(total_size, 1, it); } } } @@ -2364,7 +2364,7 @@ void VoiceAssistantConfigurationResponse::calculate_size(uint32_t &total_size) c ProtoSize::add_repeated_message(total_size, 1, this->available_wake_words); if (!this->active_wake_words.empty()) { for (const auto &it : this->active_wake_words) { - ProtoSize::add_string_field(total_size, 1, it.length()); + ProtoSize::add_string_field_repeated(total_size, 1, it); } } ProtoSize::add_uint32_field(total_size, 1, this->max_active_wake_words); @@ -2735,7 +2735,7 @@ void ListEntitiesEventResponse::calculate_size(uint32_t &total_size) const { ProtoSize::add_string_field(total_size, 1, this->device_class_ref_.size()); if (!this->event_types.empty()) { for (const auto &it : this->event_types) { - ProtoSize::add_string_field(total_size, 1, it.length()); + ProtoSize::add_string_field_repeated(total_size, 1, it); } } #ifdef USE_DEVICES diff --git a/esphome/components/api/proto.h b/esphome/components/api/proto.h index 9b95454328..58242495d9 100644 --- a/esphome/components/api/proto.h +++ b/esphome/components/api/proto.h @@ -699,6 +699,15 @@ class ProtoSize { total_size += field_id_size + varint(static_cast(len)) + static_cast(len); } + /** + * @brief Calculates and adds the size of a string/bytes field to the total message size (repeated field version) + */ + static inline void add_string_field_repeated(uint32_t &total_size, uint32_t field_id_size, const std::string &str) { + // Always calculate size for repeated fields (no empty check) + const uint32_t str_size = static_cast(str.size()); + total_size += field_id_size + varint(str_size) + str_size; + } + /** * @brief Calculates and adds the size of a bytes field to the total message size */ diff --git a/script/api_protobuf/api_protobuf.py b/script/api_protobuf/api_protobuf.py index 148e04b7e2..d6f6159050 100755 --- a/script/api_protobuf/api_protobuf.py +++ b/script/api_protobuf/api_protobuf.py @@ -598,10 +598,11 @@ class StringType(TypeInfo): return self._get_simple_size_calculation(name, force, "add_string_field") # Check if this is being called from a repeated field context - # In that case, 'name' will be 'it' and we need to use .length() + # In that case, 'name' will be 'it' and we need to use the repeated version if name == "it": + # For repeated fields, we need to use add_string_field_repeated which includes field ID field_id_size = self.calculate_field_id_size() - return f"ProtoSize::add_string_field(total_size, {field_id_size}, it.length());" + return f"ProtoSize::add_string_field_repeated(total_size, {field_id_size}, it);" # For messages that need encoding, use the StringRef size field_id_size = self.calculate_field_id_size()