From e174e579ed3a3534edc2158eb59ac9a0d7fc4be0 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 3 Apr 2026 13:33:51 -1000 Subject: [PATCH 01/11] [api] Add max_data_length proto option and optimize entity name/object_id Add max_data_length field option for string/bytes fields. When max_data_length < 128, the codegen emits constant-size length varint calculations and direct byte writes. Annotate all entity name and object_id fields with (max_data_length) = 120 and (force) = true across all 25 ListEntities*Response messages (50 fields). Generated code changes: - calculate_size: `calc_length(1, size)` -> `2 + size` (constant) - encode: `encode_string(N, ref)` -> `write_raw_byte(tag) + write_raw_byte(len) + encode_raw(data, len)` Eliminates 2 function calls per field per entity list response, removes zero-check branches, and removes varint size computation. --- esphome/components/api/api.proto | 100 ++++---- esphome/components/api/api_options.proto | 6 + esphome/components/api/api_pb2.cpp | 300 +++++++++++++++-------- script/api_protobuf/api_protobuf.py | 17 +- 4 files changed, 271 insertions(+), 152 deletions(-) diff --git a/esphome/components/api/api.proto b/esphome/components/api/api.proto index 1e03675999..9b62f8e758 100644 --- a/esphome/components/api/api.proto +++ b/esphome/components/api/api.proto @@ -315,9 +315,9 @@ message ListEntitiesBinarySensorResponse { option (source) = SOURCE_SERVER; option (ifdef) = "USE_BINARY_SENSOR"; - string object_id = 1; + string object_id = 1 [(max_data_length) = 120, (force) = true]; fixed32 key = 2 [(force) = true]; - string name = 3; + string name = 3 [(max_data_length) = 120, (force) = true]; reserved 4; // Deprecated: was string unique_id string device_class = 5; @@ -349,9 +349,9 @@ message ListEntitiesCoverResponse { option (source) = SOURCE_SERVER; option (ifdef) = "USE_COVER"; - string object_id = 1; + string object_id = 1 [(max_data_length) = 120, (force) = true]; fixed32 key = 2 [(force) = true]; - string name = 3; + string name = 3 [(max_data_length) = 120, (force) = true]; reserved 4; // Deprecated: was string unique_id bool assumed_state = 5; @@ -433,9 +433,9 @@ message ListEntitiesFanResponse { option (source) = SOURCE_SERVER; option (ifdef) = "USE_FAN"; - string object_id = 1; + string object_id = 1 [(max_data_length) = 120, (force) = true]; fixed32 key = 2 [(force) = true]; - string name = 3; + string name = 3 [(max_data_length) = 120, (force) = true]; reserved 4; // Deprecated: was string unique_id bool supports_oscillation = 5; @@ -521,9 +521,9 @@ message ListEntitiesLightResponse { option (source) = SOURCE_SERVER; option (ifdef) = "USE_LIGHT"; - string object_id = 1; + string object_id = 1 [(max_data_length) = 120, (force) = true]; fixed32 key = 2 [(force) = true]; - string name = 3; + string name = 3 [(max_data_length) = 120, (force) = true]; reserved 4; // Deprecated: was string unique_id repeated ColorMode supported_color_modes = 12 [(container_pointer_no_template) = "light::ColorModeMask"]; @@ -626,9 +626,9 @@ message ListEntitiesSensorResponse { option (source) = SOURCE_SERVER; option (ifdef) = "USE_SENSOR"; - string object_id = 1; + string object_id = 1 [(max_data_length) = 120, (force) = true]; fixed32 key = 2 [(force) = true]; - string name = 3; + string name = 3 [(max_data_length) = 120, (force) = true]; reserved 4; // Deprecated: was string unique_id string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON"]; @@ -666,9 +666,9 @@ message ListEntitiesSwitchResponse { option (source) = SOURCE_SERVER; option (ifdef) = "USE_SWITCH"; - string object_id = 1; + string object_id = 1 [(max_data_length) = 120, (force) = true]; fixed32 key = 2 [(force) = true]; - string name = 3; + string name = 3 [(max_data_length) = 120, (force) = true]; reserved 4; // Deprecated: was string unique_id string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON"]; @@ -708,9 +708,9 @@ message ListEntitiesTextSensorResponse { option (source) = SOURCE_SERVER; option (ifdef) = "USE_TEXT_SENSOR"; - string object_id = 1; + string object_id = 1 [(max_data_length) = 120, (force) = true]; fixed32 key = 2 [(force) = true]; - string name = 3; + string name = 3 [(max_data_length) = 120, (force) = true]; reserved 4; // Deprecated: was string unique_id string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON"]; @@ -971,9 +971,9 @@ message ListEntitiesCameraResponse { option (source) = SOURCE_SERVER; option (ifdef) = "USE_CAMERA"; - string object_id = 1; + string object_id = 1 [(max_data_length) = 120, (force) = true]; fixed32 key = 2 [(force) = true]; - string name = 3; + string name = 3 [(max_data_length) = 120, (force) = true]; reserved 4; // Deprecated: was string unique_id bool disabled_by_default = 5; string icon = 6 [(field_ifdef) = "USE_ENTITY_ICON"]; @@ -1056,9 +1056,9 @@ message ListEntitiesClimateResponse { option (source) = SOURCE_SERVER; option (ifdef) = "USE_CLIMATE"; - string object_id = 1; + string object_id = 1 [(max_data_length) = 120, (force) = true]; fixed32 key = 2 [(force) = true]; - string name = 3; + string name = 3 [(max_data_length) = 120, (force) = true]; reserved 4; // Deprecated: was string unique_id bool supports_current_temperature = 5; // Deprecated: use feature_flags @@ -1167,9 +1167,9 @@ message ListEntitiesWaterHeaterResponse { option (source) = SOURCE_SERVER; option (ifdef) = "USE_WATER_HEATER"; - string object_id = 1; + string object_id = 1 [(max_data_length) = 120, (force) = true]; fixed32 key = 2 [(force) = true]; - string name = 3; + string name = 3 [(max_data_length) = 120, (force) = true]; string icon = 4 [(field_ifdef) = "USE_ENTITY_ICON"]; bool disabled_by_default = 5; EntityCategory entity_category = 6; @@ -1243,9 +1243,9 @@ message ListEntitiesNumberResponse { option (source) = SOURCE_SERVER; option (ifdef) = "USE_NUMBER"; - string object_id = 1; + string object_id = 1 [(max_data_length) = 120, (force) = true]; fixed32 key = 2 [(force) = true]; - string name = 3; + string name = 3 [(max_data_length) = 120, (force) = true]; reserved 4; // Deprecated: was string unique_id string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON"]; @@ -1292,9 +1292,9 @@ message ListEntitiesSelectResponse { option (source) = SOURCE_SERVER; option (ifdef) = "USE_SELECT"; - string object_id = 1; + string object_id = 1 [(max_data_length) = 120, (force) = true]; fixed32 key = 2 [(force) = true]; - string name = 3; + string name = 3 [(max_data_length) = 120, (force) = true]; reserved 4; // Deprecated: was string unique_id string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON"]; @@ -1336,9 +1336,9 @@ message ListEntitiesSirenResponse { option (source) = SOURCE_SERVER; option (ifdef) = "USE_SIREN"; - string object_id = 1; + string object_id = 1 [(max_data_length) = 120, (force) = true]; fixed32 key = 2 [(force) = true]; - string name = 3; + string name = 3 [(max_data_length) = 120, (force) = true]; reserved 4; // Deprecated: was string unique_id string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON"]; @@ -1399,9 +1399,9 @@ message ListEntitiesLockResponse { option (source) = SOURCE_SERVER; option (ifdef) = "USE_LOCK"; - string object_id = 1; + string object_id = 1 [(max_data_length) = 120, (force) = true]; fixed32 key = 2 [(force) = true]; - string name = 3; + string name = 3 [(max_data_length) = 120, (force) = true]; reserved 4; // Deprecated: was string unique_id string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON"]; @@ -1448,9 +1448,9 @@ message ListEntitiesButtonResponse { option (source) = SOURCE_SERVER; option (ifdef) = "USE_BUTTON"; - string object_id = 1; + string object_id = 1 [(max_data_length) = 120, (force) = true]; fixed32 key = 2 [(force) = true]; - string name = 3; + string name = 3 [(max_data_length) = 120, (force) = true]; reserved 4; // Deprecated: was string unique_id string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON"]; @@ -1515,9 +1515,9 @@ message ListEntitiesMediaPlayerResponse { option (source) = SOURCE_SERVER; option (ifdef) = "USE_MEDIA_PLAYER"; - string object_id = 1; + string object_id = 1 [(max_data_length) = 120, (force) = true]; fixed32 key = 2 [(force) = true]; - string name = 3; + string name = 3 [(max_data_length) = 120, (force) = true]; reserved 4; // Deprecated: was string unique_id string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON"]; @@ -2103,9 +2103,9 @@ message ListEntitiesAlarmControlPanelResponse { option (source) = SOURCE_SERVER; option (ifdef) = "USE_ALARM_CONTROL_PANEL"; - string object_id = 1; + string object_id = 1 [(max_data_length) = 120, (force) = true]; fixed32 key = 2 [(force) = true]; - string name = 3; + string name = 3 [(max_data_length) = 120, (force) = true]; reserved 4; // Deprecated: was string unique_id string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON"]; bool disabled_by_default = 6; @@ -2150,9 +2150,9 @@ message ListEntitiesTextResponse { option (source) = SOURCE_SERVER; option (ifdef) = "USE_TEXT"; - string object_id = 1; + string object_id = 1 [(max_data_length) = 120, (force) = true]; fixed32 key = 2 [(force) = true]; - string name = 3; + string name = 3 [(max_data_length) = 120, (force) = true]; reserved 4; // Deprecated: was string unique_id string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON"]; bool disabled_by_default = 6; @@ -2198,9 +2198,9 @@ message ListEntitiesDateResponse { option (source) = SOURCE_SERVER; option (ifdef) = "USE_DATETIME_DATE"; - string object_id = 1; + string object_id = 1 [(max_data_length) = 120, (force) = true]; fixed32 key = 2 [(force) = true]; - string name = 3; + string name = 3 [(max_data_length) = 120, (force) = true]; reserved 4; // Deprecated: was string unique_id string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON"]; @@ -2245,9 +2245,9 @@ message ListEntitiesTimeResponse { option (source) = SOURCE_SERVER; option (ifdef) = "USE_DATETIME_TIME"; - string object_id = 1; + string object_id = 1 [(max_data_length) = 120, (force) = true]; fixed32 key = 2 [(force) = true]; - string name = 3; + string name = 3 [(max_data_length) = 120, (force) = true]; reserved 4; // Deprecated: was string unique_id string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON"]; @@ -2292,9 +2292,9 @@ message ListEntitiesEventResponse { option (source) = SOURCE_SERVER; option (ifdef) = "USE_EVENT"; - string object_id = 1; + string object_id = 1 [(max_data_length) = 120, (force) = true]; fixed32 key = 2 [(force) = true]; - string name = 3; + string name = 3 [(max_data_length) = 120, (force) = true]; reserved 4; // Deprecated: was string unique_id string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON"]; @@ -2323,9 +2323,9 @@ message ListEntitiesValveResponse { option (source) = SOURCE_SERVER; option (ifdef) = "USE_VALVE"; - string object_id = 1; + string object_id = 1 [(max_data_length) = 120, (force) = true]; fixed32 key = 2 [(force) = true]; - string name = 3; + string name = 3 [(max_data_length) = 120, (force) = true]; reserved 4; // Deprecated: was string unique_id string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON"]; @@ -2378,9 +2378,9 @@ message ListEntitiesDateTimeResponse { option (source) = SOURCE_SERVER; option (ifdef) = "USE_DATETIME_DATETIME"; - string object_id = 1; + string object_id = 1 [(max_data_length) = 120, (force) = true]; fixed32 key = 2 [(force) = true]; - string name = 3; + string name = 3 [(max_data_length) = 120, (force) = true]; reserved 4; // Deprecated: was string unique_id string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON"]; @@ -2421,9 +2421,9 @@ message ListEntitiesUpdateResponse { option (source) = SOURCE_SERVER; option (ifdef) = "USE_UPDATE"; - string object_id = 1; + string object_id = 1 [(max_data_length) = 120, (force) = true]; fixed32 key = 2 [(force) = true]; - string name = 3; + string name = 3 [(max_data_length) = 120, (force) = true]; reserved 4; // Deprecated: was string unique_id string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON"]; @@ -2504,9 +2504,9 @@ message ListEntitiesInfraredResponse { option (source) = SOURCE_SERVER; option (ifdef) = "USE_INFRARED"; - string object_id = 1; + string object_id = 1 [(max_data_length) = 120, (force) = true]; fixed32 key = 2 [(force) = true]; - string name = 3; + string name = 3 [(max_data_length) = 120, (force) = true]; string icon = 4 [(field_ifdef) = "USE_ENTITY_ICON"]; bool disabled_by_default = 5; EntityCategory entity_category = 6; diff --git a/esphome/components/api/api_options.proto b/esphome/components/api/api_options.proto index 0aa9e814cf..0f71268d70 100644 --- a/esphome/components/api/api_options.proto +++ b/esphome/components/api/api_options.proto @@ -102,4 +102,10 @@ extend google.protobuf.FieldOptions { // and direct byte writes instead of varint branching, since the encoded varint // is guaranteed to be 1 byte. optional uint32 max_value = 50017; + + // max_data_length: Maximum length of a string or bytes field. + // When max_data_length < 128, the code generator emits constant-size + // length varint calculations and direct byte writes, since the length + // varint is guaranteed to be 1 byte. + optional uint32 max_data_length = 50018; } diff --git a/esphome/components/api/api_pb2.cpp b/esphome/components/api/api_pb2.cpp index f25d269e8f..00d700116f 100644 --- a/esphome/components/api/api_pb2.cpp +++ b/esphome/components/api/api_pb2.cpp @@ -207,9 +207,13 @@ uint32_t DeviceInfoResponse::calculate_size() const { } #ifdef USE_BINARY_SENSOR void ListEntitiesBinarySensorResponse::encode(ProtoWriteBuffer &buffer) const { - buffer.encode_string(1, this->object_id); + buffer.write_raw_byte(10); + buffer.write_raw_byte(static_cast(this->object_id.size())); + buffer.encode_raw(this->object_id.c_str(), this->object_id.size()); buffer.write_tag_and_fixed32(21, this->key); - buffer.encode_string(3, this->name); + buffer.write_raw_byte(26); + buffer.write_raw_byte(static_cast(this->name.size())); + buffer.encode_raw(this->name.c_str(), this->name.size()); buffer.encode_string(5, this->device_class); buffer.encode_bool(6, this->is_status_binary_sensor); buffer.encode_bool(7, this->disabled_by_default); @@ -223,9 +227,9 @@ void ListEntitiesBinarySensorResponse::encode(ProtoWriteBuffer &buffer) const { } uint32_t ListEntitiesBinarySensorResponse::calculate_size() const { uint32_t size = 0; - size += ProtoSize::calc_length(1, this->object_id.size()); + size += 2 + this->object_id.size(); size += 5; - size += ProtoSize::calc_length(1, this->name.size()); + size += 2 + this->name.size(); size += ProtoSize::calc_length(1, this->device_class.size()); size += ProtoSize::calc_bool(1, this->is_status_binary_sensor); size += ProtoSize::calc_bool(1, this->disabled_by_default); @@ -259,9 +263,13 @@ uint32_t BinarySensorStateResponse::calculate_size() const { #endif #ifdef USE_COVER void ListEntitiesCoverResponse::encode(ProtoWriteBuffer &buffer) const { - buffer.encode_string(1, this->object_id); + buffer.write_raw_byte(10); + buffer.write_raw_byte(static_cast(this->object_id.size())); + buffer.encode_raw(this->object_id.c_str(), this->object_id.size()); buffer.write_tag_and_fixed32(21, this->key); - buffer.encode_string(3, this->name); + buffer.write_raw_byte(26); + buffer.write_raw_byte(static_cast(this->name.size())); + buffer.encode_raw(this->name.c_str(), this->name.size()); buffer.encode_bool(5, this->assumed_state); buffer.encode_bool(6, this->supports_position); buffer.encode_bool(7, this->supports_tilt); @@ -278,9 +286,9 @@ void ListEntitiesCoverResponse::encode(ProtoWriteBuffer &buffer) const { } uint32_t ListEntitiesCoverResponse::calculate_size() const { uint32_t size = 0; - size += ProtoSize::calc_length(1, this->object_id.size()); + size += 2 + this->object_id.size(); size += 5; - size += ProtoSize::calc_length(1, this->name.size()); + size += 2 + this->name.size(); size += ProtoSize::calc_bool(1, this->assumed_state); size += ProtoSize::calc_bool(1, this->supports_position); size += ProtoSize::calc_bool(1, this->supports_tilt); @@ -356,9 +364,13 @@ bool CoverCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) { #endif #ifdef USE_FAN void ListEntitiesFanResponse::encode(ProtoWriteBuffer &buffer) const { - buffer.encode_string(1, this->object_id); + buffer.write_raw_byte(10); + buffer.write_raw_byte(static_cast(this->object_id.size())); + buffer.encode_raw(this->object_id.c_str(), this->object_id.size()); buffer.write_tag_and_fixed32(21, this->key); - buffer.encode_string(3, this->name); + buffer.write_raw_byte(26); + buffer.write_raw_byte(static_cast(this->name.size())); + buffer.encode_raw(this->name.c_str(), this->name.size()); buffer.encode_bool(5, this->supports_oscillation); buffer.encode_bool(6, this->supports_speed); buffer.encode_bool(7, this->supports_direction); @@ -377,9 +389,9 @@ void ListEntitiesFanResponse::encode(ProtoWriteBuffer &buffer) const { } uint32_t ListEntitiesFanResponse::calculate_size() const { uint32_t size = 0; - size += ProtoSize::calc_length(1, this->object_id.size()); + size += 2 + this->object_id.size(); size += 5; - size += ProtoSize::calc_length(1, this->name.size()); + size += 2 + this->name.size(); size += ProtoSize::calc_bool(1, this->supports_oscillation); size += ProtoSize::calc_bool(1, this->supports_speed); size += ProtoSize::calc_bool(1, this->supports_direction); @@ -486,9 +498,13 @@ bool FanCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) { #endif #ifdef USE_LIGHT void ListEntitiesLightResponse::encode(ProtoWriteBuffer &buffer) const { - buffer.encode_string(1, this->object_id); + buffer.write_raw_byte(10); + buffer.write_raw_byte(static_cast(this->object_id.size())); + buffer.encode_raw(this->object_id.c_str(), this->object_id.size()); buffer.write_tag_and_fixed32(21, this->key); - buffer.encode_string(3, this->name); + buffer.write_raw_byte(26); + buffer.write_raw_byte(static_cast(this->name.size())); + buffer.encode_raw(this->name.c_str(), this->name.size()); for (const auto &it : *this->supported_color_modes) { buffer.encode_uint32(12, static_cast(it), true); } @@ -508,9 +524,9 @@ void ListEntitiesLightResponse::encode(ProtoWriteBuffer &buffer) const { } uint32_t ListEntitiesLightResponse::calculate_size() const { uint32_t size = 0; - size += ProtoSize::calc_length(1, this->object_id.size()); + size += 2 + this->object_id.size(); size += 5; - size += ProtoSize::calc_length(1, this->name.size()); + size += 2 + this->name.size(); if (!this->supported_color_modes->empty()) { for (const auto &it : *this->supported_color_modes) { size += ProtoSize::calc_uint32_force(1, static_cast(it)); @@ -682,9 +698,13 @@ bool LightCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) { #endif #ifdef USE_SENSOR void ListEntitiesSensorResponse::encode(ProtoWriteBuffer &buffer) const { - buffer.encode_string(1, this->object_id); + buffer.write_raw_byte(10); + buffer.write_raw_byte(static_cast(this->object_id.size())); + buffer.encode_raw(this->object_id.c_str(), this->object_id.size()); buffer.write_tag_and_fixed32(21, this->key); - buffer.encode_string(3, this->name); + buffer.write_raw_byte(26); + buffer.write_raw_byte(static_cast(this->name.size())); + buffer.encode_raw(this->name.c_str(), this->name.size()); #ifdef USE_ENTITY_ICON buffer.encode_string(5, this->icon); #endif @@ -701,9 +721,9 @@ void ListEntitiesSensorResponse::encode(ProtoWriteBuffer &buffer) const { } uint32_t ListEntitiesSensorResponse::calculate_size() const { uint32_t size = 0; - size += ProtoSize::calc_length(1, this->object_id.size()); + size += 2 + this->object_id.size(); size += 5; - size += ProtoSize::calc_length(1, this->name.size()); + size += 2 + this->name.size(); #ifdef USE_ENTITY_ICON size += ProtoSize::calc_length(1, this->icon.size()); #endif @@ -740,9 +760,13 @@ uint32_t SensorStateResponse::calculate_size() const { #endif #ifdef USE_SWITCH void ListEntitiesSwitchResponse::encode(ProtoWriteBuffer &buffer) const { - buffer.encode_string(1, this->object_id); + buffer.write_raw_byte(10); + buffer.write_raw_byte(static_cast(this->object_id.size())); + buffer.encode_raw(this->object_id.c_str(), this->object_id.size()); buffer.write_tag_and_fixed32(21, this->key); - buffer.encode_string(3, this->name); + buffer.write_raw_byte(26); + buffer.write_raw_byte(static_cast(this->name.size())); + buffer.encode_raw(this->name.c_str(), this->name.size()); #ifdef USE_ENTITY_ICON buffer.encode_string(5, this->icon); #endif @@ -756,9 +780,9 @@ void ListEntitiesSwitchResponse::encode(ProtoWriteBuffer &buffer) const { } uint32_t ListEntitiesSwitchResponse::calculate_size() const { uint32_t size = 0; - size += ProtoSize::calc_length(1, this->object_id.size()); + size += 2 + this->object_id.size(); size += 5; - size += ProtoSize::calc_length(1, this->name.size()); + size += 2 + this->name.size(); #ifdef USE_ENTITY_ICON size += ProtoSize::calc_length(1, this->icon.size()); #endif @@ -815,9 +839,13 @@ bool SwitchCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) { #endif #ifdef USE_TEXT_SENSOR void ListEntitiesTextSensorResponse::encode(ProtoWriteBuffer &buffer) const { - buffer.encode_string(1, this->object_id); + buffer.write_raw_byte(10); + buffer.write_raw_byte(static_cast(this->object_id.size())); + buffer.encode_raw(this->object_id.c_str(), this->object_id.size()); buffer.write_tag_and_fixed32(21, this->key); - buffer.encode_string(3, this->name); + buffer.write_raw_byte(26); + buffer.write_raw_byte(static_cast(this->name.size())); + buffer.encode_raw(this->name.c_str(), this->name.size()); #ifdef USE_ENTITY_ICON buffer.encode_string(5, this->icon); #endif @@ -830,9 +858,9 @@ void ListEntitiesTextSensorResponse::encode(ProtoWriteBuffer &buffer) const { } uint32_t ListEntitiesTextSensorResponse::calculate_size() const { uint32_t size = 0; - size += ProtoSize::calc_length(1, this->object_id.size()); + size += 2 + this->object_id.size(); size += 5; - size += ProtoSize::calc_length(1, this->name.size()); + size += 2 + this->name.size(); #ifdef USE_ENTITY_ICON size += ProtoSize::calc_length(1, this->icon.size()); #endif @@ -1268,9 +1296,13 @@ uint32_t ExecuteServiceResponse::calculate_size() const { #endif #ifdef USE_CAMERA void ListEntitiesCameraResponse::encode(ProtoWriteBuffer &buffer) const { - buffer.encode_string(1, this->object_id); + buffer.write_raw_byte(10); + buffer.write_raw_byte(static_cast(this->object_id.size())); + buffer.encode_raw(this->object_id.c_str(), this->object_id.size()); buffer.write_tag_and_fixed32(21, this->key); - buffer.encode_string(3, this->name); + buffer.write_raw_byte(26); + buffer.write_raw_byte(static_cast(this->name.size())); + buffer.encode_raw(this->name.c_str(), this->name.size()); buffer.encode_bool(5, this->disabled_by_default); #ifdef USE_ENTITY_ICON buffer.encode_string(6, this->icon); @@ -1282,9 +1314,9 @@ void ListEntitiesCameraResponse::encode(ProtoWriteBuffer &buffer) const { } uint32_t ListEntitiesCameraResponse::calculate_size() const { uint32_t size = 0; - size += ProtoSize::calc_length(1, this->object_id.size()); + size += 2 + this->object_id.size(); size += 5; - size += ProtoSize::calc_length(1, this->name.size()); + size += 2 + this->name.size(); size += ProtoSize::calc_bool(1, this->disabled_by_default); #ifdef USE_ENTITY_ICON size += ProtoSize::calc_length(1, this->icon.size()); @@ -1329,9 +1361,13 @@ bool CameraImageRequest::decode_varint(uint32_t field_id, proto_varint_value_t v #endif #ifdef USE_CLIMATE void ListEntitiesClimateResponse::encode(ProtoWriteBuffer &buffer) const { - buffer.encode_string(1, this->object_id); + buffer.write_raw_byte(10); + buffer.write_raw_byte(static_cast(this->object_id.size())); + buffer.encode_raw(this->object_id.c_str(), this->object_id.size()); buffer.write_tag_and_fixed32(21, this->key); - buffer.encode_string(3, this->name); + buffer.write_raw_byte(26); + buffer.write_raw_byte(static_cast(this->name.size())); + buffer.encode_raw(this->name.c_str(), this->name.size()); buffer.encode_bool(5, this->supports_current_temperature); buffer.encode_bool(6, this->supports_two_point_target_temperature); for (const auto &it : *this->supported_modes) { @@ -1373,9 +1409,9 @@ void ListEntitiesClimateResponse::encode(ProtoWriteBuffer &buffer) const { } uint32_t ListEntitiesClimateResponse::calculate_size() const { uint32_t size = 0; - size += ProtoSize::calc_length(1, this->object_id.size()); + size += 2 + this->object_id.size(); size += 5; - size += ProtoSize::calc_length(1, this->name.size()); + size += 2 + this->name.size(); size += ProtoSize::calc_bool(1, this->supports_current_temperature); size += ProtoSize::calc_bool(1, this->supports_two_point_target_temperature); if (!this->supported_modes->empty()) { @@ -1562,9 +1598,13 @@ bool ClimateCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) { #endif #ifdef USE_WATER_HEATER void ListEntitiesWaterHeaterResponse::encode(ProtoWriteBuffer &buffer) const { - buffer.encode_string(1, this->object_id); + buffer.write_raw_byte(10); + buffer.write_raw_byte(static_cast(this->object_id.size())); + buffer.encode_raw(this->object_id.c_str(), this->object_id.size()); buffer.write_tag_and_fixed32(21, this->key); - buffer.encode_string(3, this->name); + buffer.write_raw_byte(26); + buffer.write_raw_byte(static_cast(this->name.size())); + buffer.encode_raw(this->name.c_str(), this->name.size()); #ifdef USE_ENTITY_ICON buffer.encode_string(4, this->icon); #endif @@ -1583,9 +1623,9 @@ void ListEntitiesWaterHeaterResponse::encode(ProtoWriteBuffer &buffer) const { } uint32_t ListEntitiesWaterHeaterResponse::calculate_size() const { uint32_t size = 0; - size += ProtoSize::calc_length(1, this->object_id.size()); + size += 2 + this->object_id.size(); size += 5; - size += ProtoSize::calc_length(1, this->name.size()); + size += 2 + this->name.size(); #ifdef USE_ENTITY_ICON size += ProtoSize::calc_length(1, this->icon.size()); #endif @@ -1674,9 +1714,13 @@ bool WaterHeaterCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value #endif #ifdef USE_NUMBER void ListEntitiesNumberResponse::encode(ProtoWriteBuffer &buffer) const { - buffer.encode_string(1, this->object_id); + buffer.write_raw_byte(10); + buffer.write_raw_byte(static_cast(this->object_id.size())); + buffer.encode_raw(this->object_id.c_str(), this->object_id.size()); buffer.write_tag_and_fixed32(21, this->key); - buffer.encode_string(3, this->name); + buffer.write_raw_byte(26); + buffer.write_raw_byte(static_cast(this->name.size())); + buffer.encode_raw(this->name.c_str(), this->name.size()); #ifdef USE_ENTITY_ICON buffer.encode_string(5, this->icon); #endif @@ -1694,9 +1738,9 @@ void ListEntitiesNumberResponse::encode(ProtoWriteBuffer &buffer) const { } uint32_t ListEntitiesNumberResponse::calculate_size() const { uint32_t size = 0; - size += ProtoSize::calc_length(1, this->object_id.size()); + size += 2 + this->object_id.size(); size += 5; - size += ProtoSize::calc_length(1, this->name.size()); + size += 2 + this->name.size(); #ifdef USE_ENTITY_ICON size += ProtoSize::calc_length(1, this->icon.size()); #endif @@ -1759,9 +1803,13 @@ bool NumberCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) { #endif #ifdef USE_SELECT void ListEntitiesSelectResponse::encode(ProtoWriteBuffer &buffer) const { - buffer.encode_string(1, this->object_id); + buffer.write_raw_byte(10); + buffer.write_raw_byte(static_cast(this->object_id.size())); + buffer.encode_raw(this->object_id.c_str(), this->object_id.size()); buffer.write_tag_and_fixed32(21, this->key); - buffer.encode_string(3, this->name); + buffer.write_raw_byte(26); + buffer.write_raw_byte(static_cast(this->name.size())); + buffer.encode_raw(this->name.c_str(), this->name.size()); #ifdef USE_ENTITY_ICON buffer.encode_string(5, this->icon); #endif @@ -1776,9 +1824,9 @@ void ListEntitiesSelectResponse::encode(ProtoWriteBuffer &buffer) const { } uint32_t ListEntitiesSelectResponse::calculate_size() const { uint32_t size = 0; - size += ProtoSize::calc_length(1, this->object_id.size()); + size += 2 + this->object_id.size(); size += 5; - size += ProtoSize::calc_length(1, this->name.size()); + size += 2 + this->name.size(); #ifdef USE_ENTITY_ICON size += ProtoSize::calc_length(1, this->icon.size()); #endif @@ -1848,9 +1896,13 @@ bool SelectCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) { #endif #ifdef USE_SIREN void ListEntitiesSirenResponse::encode(ProtoWriteBuffer &buffer) const { - buffer.encode_string(1, this->object_id); + buffer.write_raw_byte(10); + buffer.write_raw_byte(static_cast(this->object_id.size())); + buffer.encode_raw(this->object_id.c_str(), this->object_id.size()); buffer.write_tag_and_fixed32(21, this->key); - buffer.encode_string(3, this->name); + buffer.write_raw_byte(26); + buffer.write_raw_byte(static_cast(this->name.size())); + buffer.encode_raw(this->name.c_str(), this->name.size()); #ifdef USE_ENTITY_ICON buffer.encode_string(5, this->icon); #endif @@ -1867,9 +1919,9 @@ void ListEntitiesSirenResponse::encode(ProtoWriteBuffer &buffer) const { } uint32_t ListEntitiesSirenResponse::calculate_size() const { uint32_t size = 0; - size += ProtoSize::calc_length(1, this->object_id.size()); + size += 2 + this->object_id.size(); size += 5; - size += ProtoSize::calc_length(1, this->name.size()); + size += 2 + this->name.size(); #ifdef USE_ENTITY_ICON size += ProtoSize::calc_length(1, this->icon.size()); #endif @@ -1960,9 +2012,13 @@ bool SirenCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) { #endif #ifdef USE_LOCK void ListEntitiesLockResponse::encode(ProtoWriteBuffer &buffer) const { - buffer.encode_string(1, this->object_id); + buffer.write_raw_byte(10); + buffer.write_raw_byte(static_cast(this->object_id.size())); + buffer.encode_raw(this->object_id.c_str(), this->object_id.size()); buffer.write_tag_and_fixed32(21, this->key); - buffer.encode_string(3, this->name); + buffer.write_raw_byte(26); + buffer.write_raw_byte(static_cast(this->name.size())); + buffer.encode_raw(this->name.c_str(), this->name.size()); #ifdef USE_ENTITY_ICON buffer.encode_string(5, this->icon); #endif @@ -1978,9 +2034,9 @@ void ListEntitiesLockResponse::encode(ProtoWriteBuffer &buffer) const { } uint32_t ListEntitiesLockResponse::calculate_size() const { uint32_t size = 0; - size += ProtoSize::calc_length(1, this->object_id.size()); + size += 2 + this->object_id.size(); size += 5; - size += ProtoSize::calc_length(1, this->name.size()); + size += 2 + this->name.size(); #ifdef USE_ENTITY_ICON size += ProtoSize::calc_length(1, this->icon.size()); #endif @@ -2053,9 +2109,13 @@ bool LockCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) { #endif #ifdef USE_BUTTON void ListEntitiesButtonResponse::encode(ProtoWriteBuffer &buffer) const { - buffer.encode_string(1, this->object_id); + buffer.write_raw_byte(10); + buffer.write_raw_byte(static_cast(this->object_id.size())); + buffer.encode_raw(this->object_id.c_str(), this->object_id.size()); buffer.write_tag_and_fixed32(21, this->key); - buffer.encode_string(3, this->name); + buffer.write_raw_byte(26); + buffer.write_raw_byte(static_cast(this->name.size())); + buffer.encode_raw(this->name.c_str(), this->name.size()); #ifdef USE_ENTITY_ICON buffer.encode_string(5, this->icon); #endif @@ -2068,9 +2128,9 @@ void ListEntitiesButtonResponse::encode(ProtoWriteBuffer &buffer) const { } uint32_t ListEntitiesButtonResponse::calculate_size() const { uint32_t size = 0; - size += ProtoSize::calc_length(1, this->object_id.size()); + size += 2 + this->object_id.size(); size += 5; - size += ProtoSize::calc_length(1, this->name.size()); + size += 2 + this->name.size(); #ifdef USE_ENTITY_ICON size += ProtoSize::calc_length(1, this->icon.size()); #endif @@ -2123,9 +2183,13 @@ uint32_t MediaPlayerSupportedFormat::calculate_size() const { return size; } void ListEntitiesMediaPlayerResponse::encode(ProtoWriteBuffer &buffer) const { - buffer.encode_string(1, this->object_id); + buffer.write_raw_byte(10); + buffer.write_raw_byte(static_cast(this->object_id.size())); + buffer.encode_raw(this->object_id.c_str(), this->object_id.size()); buffer.write_tag_and_fixed32(21, this->key); - buffer.encode_string(3, this->name); + buffer.write_raw_byte(26); + buffer.write_raw_byte(static_cast(this->name.size())); + buffer.encode_raw(this->name.c_str(), this->name.size()); #ifdef USE_ENTITY_ICON buffer.encode_string(5, this->icon); #endif @@ -2142,9 +2206,9 @@ void ListEntitiesMediaPlayerResponse::encode(ProtoWriteBuffer &buffer) const { } uint32_t ListEntitiesMediaPlayerResponse::calculate_size() const { uint32_t size = 0; - size += ProtoSize::calc_length(1, this->object_id.size()); + size += 2 + this->object_id.size(); size += 5; - size += ProtoSize::calc_length(1, this->name.size()); + size += 2 + this->name.size(); #ifdef USE_ENTITY_ICON size += ProtoSize::calc_length(1, this->icon.size()); #endif @@ -2945,9 +3009,13 @@ bool VoiceAssistantSetConfiguration::decode_length(uint32_t field_id, ProtoLengt #endif #ifdef USE_ALARM_CONTROL_PANEL void ListEntitiesAlarmControlPanelResponse::encode(ProtoWriteBuffer &buffer) const { - buffer.encode_string(1, this->object_id); + buffer.write_raw_byte(10); + buffer.write_raw_byte(static_cast(this->object_id.size())); + buffer.encode_raw(this->object_id.c_str(), this->object_id.size()); buffer.write_tag_and_fixed32(21, this->key); - buffer.encode_string(3, this->name); + buffer.write_raw_byte(26); + buffer.write_raw_byte(static_cast(this->name.size())); + buffer.encode_raw(this->name.c_str(), this->name.size()); #ifdef USE_ENTITY_ICON buffer.encode_string(5, this->icon); #endif @@ -2962,9 +3030,9 @@ void ListEntitiesAlarmControlPanelResponse::encode(ProtoWriteBuffer &buffer) con } uint32_t ListEntitiesAlarmControlPanelResponse::calculate_size() const { uint32_t size = 0; - size += ProtoSize::calc_length(1, this->object_id.size()); + size += 2 + this->object_id.size(); size += 5; - size += ProtoSize::calc_length(1, this->name.size()); + size += 2 + this->name.size(); #ifdef USE_ENTITY_ICON size += ProtoSize::calc_length(1, this->icon.size()); #endif @@ -3033,9 +3101,13 @@ bool AlarmControlPanelCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit #endif #ifdef USE_TEXT void ListEntitiesTextResponse::encode(ProtoWriteBuffer &buffer) const { - buffer.encode_string(1, this->object_id); + buffer.write_raw_byte(10); + buffer.write_raw_byte(static_cast(this->object_id.size())); + buffer.encode_raw(this->object_id.c_str(), this->object_id.size()); buffer.write_tag_and_fixed32(21, this->key); - buffer.encode_string(3, this->name); + buffer.write_raw_byte(26); + buffer.write_raw_byte(static_cast(this->name.size())); + buffer.encode_raw(this->name.c_str(), this->name.size()); #ifdef USE_ENTITY_ICON buffer.encode_string(5, this->icon); #endif @@ -3051,9 +3123,9 @@ void ListEntitiesTextResponse::encode(ProtoWriteBuffer &buffer) const { } uint32_t ListEntitiesTextResponse::calculate_size() const { uint32_t size = 0; - size += ProtoSize::calc_length(1, this->object_id.size()); + size += 2 + this->object_id.size(); size += 5; - size += ProtoSize::calc_length(1, this->name.size()); + size += 2 + this->name.size(); #ifdef USE_ENTITY_ICON size += ProtoSize::calc_length(1, this->icon.size()); #endif @@ -3122,9 +3194,13 @@ bool TextCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) { #endif #ifdef USE_DATETIME_DATE void ListEntitiesDateResponse::encode(ProtoWriteBuffer &buffer) const { - buffer.encode_string(1, this->object_id); + buffer.write_raw_byte(10); + buffer.write_raw_byte(static_cast(this->object_id.size())); + buffer.encode_raw(this->object_id.c_str(), this->object_id.size()); buffer.write_tag_and_fixed32(21, this->key); - buffer.encode_string(3, this->name); + buffer.write_raw_byte(26); + buffer.write_raw_byte(static_cast(this->name.size())); + buffer.encode_raw(this->name.c_str(), this->name.size()); #ifdef USE_ENTITY_ICON buffer.encode_string(5, this->icon); #endif @@ -3136,9 +3212,9 @@ void ListEntitiesDateResponse::encode(ProtoWriteBuffer &buffer) const { } uint32_t ListEntitiesDateResponse::calculate_size() const { uint32_t size = 0; - size += ProtoSize::calc_length(1, this->object_id.size()); + size += 2 + this->object_id.size(); size += 5; - size += ProtoSize::calc_length(1, this->name.size()); + size += 2 + this->name.size(); #ifdef USE_ENTITY_ICON size += ProtoSize::calc_length(1, this->icon.size()); #endif @@ -3205,9 +3281,13 @@ bool DateCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) { #endif #ifdef USE_DATETIME_TIME void ListEntitiesTimeResponse::encode(ProtoWriteBuffer &buffer) const { - buffer.encode_string(1, this->object_id); + buffer.write_raw_byte(10); + buffer.write_raw_byte(static_cast(this->object_id.size())); + buffer.encode_raw(this->object_id.c_str(), this->object_id.size()); buffer.write_tag_and_fixed32(21, this->key); - buffer.encode_string(3, this->name); + buffer.write_raw_byte(26); + buffer.write_raw_byte(static_cast(this->name.size())); + buffer.encode_raw(this->name.c_str(), this->name.size()); #ifdef USE_ENTITY_ICON buffer.encode_string(5, this->icon); #endif @@ -3219,9 +3299,9 @@ void ListEntitiesTimeResponse::encode(ProtoWriteBuffer &buffer) const { } uint32_t ListEntitiesTimeResponse::calculate_size() const { uint32_t size = 0; - size += ProtoSize::calc_length(1, this->object_id.size()); + size += 2 + this->object_id.size(); size += 5; - size += ProtoSize::calc_length(1, this->name.size()); + size += 2 + this->name.size(); #ifdef USE_ENTITY_ICON size += ProtoSize::calc_length(1, this->icon.size()); #endif @@ -3288,9 +3368,13 @@ bool TimeCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) { #endif #ifdef USE_EVENT void ListEntitiesEventResponse::encode(ProtoWriteBuffer &buffer) const { - buffer.encode_string(1, this->object_id); + buffer.write_raw_byte(10); + buffer.write_raw_byte(static_cast(this->object_id.size())); + buffer.encode_raw(this->object_id.c_str(), this->object_id.size()); buffer.write_tag_and_fixed32(21, this->key); - buffer.encode_string(3, this->name); + buffer.write_raw_byte(26); + buffer.write_raw_byte(static_cast(this->name.size())); + buffer.encode_raw(this->name.c_str(), this->name.size()); #ifdef USE_ENTITY_ICON buffer.encode_string(5, this->icon); #endif @@ -3306,9 +3390,9 @@ void ListEntitiesEventResponse::encode(ProtoWriteBuffer &buffer) const { } uint32_t ListEntitiesEventResponse::calculate_size() const { uint32_t size = 0; - size += ProtoSize::calc_length(1, this->object_id.size()); + size += 2 + this->object_id.size(); size += 5; - size += ProtoSize::calc_length(1, this->name.size()); + size += 2 + this->name.size(); #ifdef USE_ENTITY_ICON size += ProtoSize::calc_length(1, this->icon.size()); #endif @@ -3344,9 +3428,13 @@ uint32_t EventResponse::calculate_size() const { #endif #ifdef USE_VALVE void ListEntitiesValveResponse::encode(ProtoWriteBuffer &buffer) const { - buffer.encode_string(1, this->object_id); + buffer.write_raw_byte(10); + buffer.write_raw_byte(static_cast(this->object_id.size())); + buffer.encode_raw(this->object_id.c_str(), this->object_id.size()); buffer.write_tag_and_fixed32(21, this->key); - buffer.encode_string(3, this->name); + buffer.write_raw_byte(26); + buffer.write_raw_byte(static_cast(this->name.size())); + buffer.encode_raw(this->name.c_str(), this->name.size()); #ifdef USE_ENTITY_ICON buffer.encode_string(5, this->icon); #endif @@ -3362,9 +3450,9 @@ void ListEntitiesValveResponse::encode(ProtoWriteBuffer &buffer) const { } uint32_t ListEntitiesValveResponse::calculate_size() const { uint32_t size = 0; - size += ProtoSize::calc_length(1, this->object_id.size()); + size += 2 + this->object_id.size(); size += 5; - size += ProtoSize::calc_length(1, this->name.size()); + size += 2 + this->name.size(); #ifdef USE_ENTITY_ICON size += ProtoSize::calc_length(1, this->icon.size()); #endif @@ -3431,9 +3519,13 @@ bool ValveCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) { #endif #ifdef USE_DATETIME_DATETIME void ListEntitiesDateTimeResponse::encode(ProtoWriteBuffer &buffer) const { - buffer.encode_string(1, this->object_id); + buffer.write_raw_byte(10); + buffer.write_raw_byte(static_cast(this->object_id.size())); + buffer.encode_raw(this->object_id.c_str(), this->object_id.size()); buffer.write_tag_and_fixed32(21, this->key); - buffer.encode_string(3, this->name); + buffer.write_raw_byte(26); + buffer.write_raw_byte(static_cast(this->name.size())); + buffer.encode_raw(this->name.c_str(), this->name.size()); #ifdef USE_ENTITY_ICON buffer.encode_string(5, this->icon); #endif @@ -3445,9 +3537,9 @@ void ListEntitiesDateTimeResponse::encode(ProtoWriteBuffer &buffer) const { } uint32_t ListEntitiesDateTimeResponse::calculate_size() const { uint32_t size = 0; - size += ProtoSize::calc_length(1, this->object_id.size()); + size += 2 + this->object_id.size(); size += 5; - size += ProtoSize::calc_length(1, this->name.size()); + size += 2 + this->name.size(); #ifdef USE_ENTITY_ICON size += ProtoSize::calc_length(1, this->icon.size()); #endif @@ -3504,9 +3596,13 @@ bool DateTimeCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) { #endif #ifdef USE_UPDATE void ListEntitiesUpdateResponse::encode(ProtoWriteBuffer &buffer) const { - buffer.encode_string(1, this->object_id); + buffer.write_raw_byte(10); + buffer.write_raw_byte(static_cast(this->object_id.size())); + buffer.encode_raw(this->object_id.c_str(), this->object_id.size()); buffer.write_tag_and_fixed32(21, this->key); - buffer.encode_string(3, this->name); + buffer.write_raw_byte(26); + buffer.write_raw_byte(static_cast(this->name.size())); + buffer.encode_raw(this->name.c_str(), this->name.size()); #ifdef USE_ENTITY_ICON buffer.encode_string(5, this->icon); #endif @@ -3519,9 +3615,9 @@ void ListEntitiesUpdateResponse::encode(ProtoWriteBuffer &buffer) const { } uint32_t ListEntitiesUpdateResponse::calculate_size() const { uint32_t size = 0; - size += ProtoSize::calc_length(1, this->object_id.size()); + size += 2 + this->object_id.size(); size += 5; - size += ProtoSize::calc_length(1, this->name.size()); + size += 2 + this->name.size(); #ifdef USE_ENTITY_ICON size += ProtoSize::calc_length(1, this->icon.size()); #endif @@ -3645,9 +3741,13 @@ uint32_t ZWaveProxyRequest::calculate_size() const { #endif #ifdef USE_INFRARED void ListEntitiesInfraredResponse::encode(ProtoWriteBuffer &buffer) const { - buffer.encode_string(1, this->object_id); + buffer.write_raw_byte(10); + buffer.write_raw_byte(static_cast(this->object_id.size())); + buffer.encode_raw(this->object_id.c_str(), this->object_id.size()); buffer.write_tag_and_fixed32(21, this->key); - buffer.encode_string(3, this->name); + buffer.write_raw_byte(26); + buffer.write_raw_byte(static_cast(this->name.size())); + buffer.encode_raw(this->name.c_str(), this->name.size()); #ifdef USE_ENTITY_ICON buffer.encode_string(4, this->icon); #endif @@ -3661,9 +3761,9 @@ void ListEntitiesInfraredResponse::encode(ProtoWriteBuffer &buffer) const { } uint32_t ListEntitiesInfraredResponse::calculate_size() const { uint32_t size = 0; - size += ProtoSize::calc_length(1, this->object_id.size()); + size += 2 + this->object_id.size(); size += 5; - size += ProtoSize::calc_length(1, this->name.size()); + size += 2 + this->name.size(); #ifdef USE_ENTITY_ICON size += ProtoSize::calc_length(1, this->icon.size()); #endif diff --git a/script/api_protobuf/api_protobuf.py b/script/api_protobuf/api_protobuf.py index c17f16412c..a0335ff404 100755 --- a/script/api_protobuf/api_protobuf.py +++ b/script/api_protobuf/api_protobuf.py @@ -161,6 +161,11 @@ class TypeInfo(ABC): """Get the max_value option for this field, or None if not set.""" return get_field_opt(self._field, pb.max_value, None) + @property + def max_data_length(self) -> int | None: + """Get the max_data_length option for this field, or None if not set.""" + return get_field_opt(self._field, pb.max_data_length, None) + @property def wire_type(self) -> WireType: """Get the wire type for the field.""" @@ -1064,7 +1069,9 @@ class PointerToStringBufferType(PointerToBufferTypeBase): @property def encode_content(self) -> str: if result := self._encode_bytes_with_precomputed_tag( - f"this->{self.field_name}.c_str()", f"this->{self.field_name}.size()" + f"this->{self.field_name}.c_str()", + f"this->{self.field_name}.size()", + max_len=self.max_data_length, ): return result if self.force: @@ -1089,7 +1096,13 @@ class PointerToStringBufferType(PointerToBufferTypeBase): return f'dump_field(out, ESPHOME_PSTR("{self.name}"), this->{self.field_name});' def get_size_calculation(self, name: str, force: bool = False) -> str: - return f"size += ProtoSize::calc_length({self.calculate_field_id_size()}, this->{self.field_name}.size());" + size_field = f"this->{self.field_name}.size()" + max_len = self.max_data_length + if max_len is not None and max_len < 128: + return self._get_single_byte_varint_size( + size_field, force, extra_expr=size_field + ) + return self._get_simple_size_calculation(size_field, force, "length") def get_estimated_size(self) -> int: return self.calculate_field_id_size() + 8 # field ID + 8 bytes typical string From 5dbe2a774124e47468241eb48204347c93ae0420 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 3 Apr 2026 13:36:32 -1000 Subject: [PATCH 02/11] [api] Add comment tying max_data_length = 120 to NAME_MAX_LENGTH --- esphome/components/api/api.proto | 3 +++ 1 file changed, 3 insertions(+) diff --git a/esphome/components/api/api.proto b/esphome/components/api/api.proto index 9b62f8e758..afb66ebb13 100644 --- a/esphome/components/api/api.proto +++ b/esphome/components/api/api.proto @@ -308,6 +308,9 @@ enum EntityCategory { ENTITY_CATEGORY_DIAGNOSTIC = 2; } +// Entity name/object_id max_data_length = 120 matches NAME_MAX_LENGTH +// in esphome/config_validation.py (validated at config time). + // ==================== BINARY SENSOR ==================== message ListEntitiesBinarySensorResponse { option (id) = 12; From 0124087e733d025594614967f418cf28acccafca Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 3 Apr 2026 13:40:27 -1000 Subject: [PATCH 03/11] [api] Add max_data_length = 63 for icon fields --- esphome/components/api/api.proto | 55 +++++++++++++++--------------- esphome/components/api/api_pb2.cpp | 50 +++++++++++++-------------- 2 files changed, 53 insertions(+), 52 deletions(-) diff --git a/esphome/components/api/api.proto b/esphome/components/api/api.proto index afb66ebb13..278e977a73 100644 --- a/esphome/components/api/api.proto +++ b/esphome/components/api/api.proto @@ -308,8 +308,9 @@ enum EntityCategory { ENTITY_CATEGORY_DIAGNOSTIC = 2; } -// Entity name/object_id max_data_length = 120 matches NAME_MAX_LENGTH -// in esphome/config_validation.py (validated at config time). +// Entity name/object_id max_data_length = 120 matches NAME_MAX_LENGTH, +// icon max_data_length = 63 matches ICON_MAX_LENGTH. +// Both defined in esphome/core/config.py and validated at config time. // ==================== BINARY SENSOR ==================== message ListEntitiesBinarySensorResponse { @@ -326,7 +327,7 @@ message ListEntitiesBinarySensorResponse { string device_class = 5; bool is_status_binary_sensor = 6; bool disabled_by_default = 7; - string icon = 8 [(field_ifdef) = "USE_ENTITY_ICON"]; + string icon = 8 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63]; EntityCategory entity_category = 9; uint32 device_id = 10 [(field_ifdef) = "USE_DEVICES"]; } @@ -362,7 +363,7 @@ message ListEntitiesCoverResponse { bool supports_tilt = 7; string device_class = 8; bool disabled_by_default = 9; - string icon = 10 [(field_ifdef) = "USE_ENTITY_ICON"]; + string icon = 10 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63]; EntityCategory entity_category = 11; bool supports_stop = 12; uint32 device_id = 13 [(field_ifdef) = "USE_DEVICES"]; @@ -446,7 +447,7 @@ message ListEntitiesFanResponse { bool supports_direction = 7; int32 supported_speed_count = 8; bool disabled_by_default = 9; - string icon = 10 [(field_ifdef) = "USE_ENTITY_ICON"]; + string icon = 10 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63]; EntityCategory entity_category = 11; repeated string supported_preset_modes = 12 [(container_pointer_no_template) = "std::vector"]; uint32 device_id = 13 [(field_ifdef) = "USE_DEVICES"]; @@ -543,7 +544,7 @@ message ListEntitiesLightResponse { float max_mireds = 10; repeated string effects = 11 [(container_pointer_no_template) = "FixedVector"]; bool disabled_by_default = 13; - string icon = 14 [(field_ifdef) = "USE_ENTITY_ICON"]; + string icon = 14 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63]; EntityCategory entity_category = 15; uint32 device_id = 16 [(field_ifdef) = "USE_DEVICES"]; } @@ -634,7 +635,7 @@ message ListEntitiesSensorResponse { string name = 3 [(max_data_length) = 120, (force) = true]; reserved 4; // Deprecated: was string unique_id - string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON"]; + string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63]; string unit_of_measurement = 6; int32 accuracy_decimals = 7; bool force_update = 8; @@ -674,7 +675,7 @@ message ListEntitiesSwitchResponse { string name = 3 [(max_data_length) = 120, (force) = true]; reserved 4; // Deprecated: was string unique_id - string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON"]; + string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63]; bool assumed_state = 6; bool disabled_by_default = 7; EntityCategory entity_category = 8; @@ -716,7 +717,7 @@ message ListEntitiesTextSensorResponse { string name = 3 [(max_data_length) = 120, (force) = true]; reserved 4; // Deprecated: was string unique_id - string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON"]; + string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63]; bool disabled_by_default = 6; EntityCategory entity_category = 7; string device_class = 8; @@ -979,7 +980,7 @@ message ListEntitiesCameraResponse { string name = 3 [(max_data_length) = 120, (force) = true]; reserved 4; // Deprecated: was string unique_id bool disabled_by_default = 5; - string icon = 6 [(field_ifdef) = "USE_ENTITY_ICON"]; + string icon = 6 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63]; EntityCategory entity_category = 7; uint32 device_id = 8 [(field_ifdef) = "USE_DEVICES"]; } @@ -1081,7 +1082,7 @@ message ListEntitiesClimateResponse { repeated ClimatePreset supported_presets = 16 [(container_pointer_no_template) = "climate::ClimatePresetMask"]; repeated string supported_custom_presets = 17 [(container_pointer_no_template) = "std::vector"]; bool disabled_by_default = 18; - string icon = 19 [(field_ifdef) = "USE_ENTITY_ICON"]; + string icon = 19 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63]; EntityCategory entity_category = 20; float visual_current_temperature_step = 21; bool supports_current_humidity = 22; // Deprecated: use feature_flags @@ -1173,7 +1174,7 @@ message ListEntitiesWaterHeaterResponse { string object_id = 1 [(max_data_length) = 120, (force) = true]; fixed32 key = 2 [(force) = true]; string name = 3 [(max_data_length) = 120, (force) = true]; - string icon = 4 [(field_ifdef) = "USE_ENTITY_ICON"]; + string icon = 4 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63]; bool disabled_by_default = 5; EntityCategory entity_category = 6; uint32 device_id = 7 [(field_ifdef) = "USE_DEVICES"]; @@ -1251,7 +1252,7 @@ message ListEntitiesNumberResponse { string name = 3 [(max_data_length) = 120, (force) = true]; reserved 4; // Deprecated: was string unique_id - string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON"]; + string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63]; float min_value = 6; float max_value = 7; float step = 8; @@ -1300,7 +1301,7 @@ message ListEntitiesSelectResponse { string name = 3 [(max_data_length) = 120, (force) = true]; reserved 4; // Deprecated: was string unique_id - string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON"]; + string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63]; repeated string options = 6 [(container_pointer_no_template) = "FixedVector"]; bool disabled_by_default = 7; EntityCategory entity_category = 8; @@ -1344,7 +1345,7 @@ message ListEntitiesSirenResponse { string name = 3 [(max_data_length) = 120, (force) = true]; reserved 4; // Deprecated: was string unique_id - string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON"]; + string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63]; bool disabled_by_default = 6; repeated string tones = 7 [(container_pointer_no_template) = "FixedVector"]; bool supports_duration = 8; @@ -1407,7 +1408,7 @@ message ListEntitiesLockResponse { string name = 3 [(max_data_length) = 120, (force) = true]; reserved 4; // Deprecated: was string unique_id - string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON"]; + string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63]; bool disabled_by_default = 6; EntityCategory entity_category = 7; bool assumed_state = 8; @@ -1456,7 +1457,7 @@ message ListEntitiesButtonResponse { string name = 3 [(max_data_length) = 120, (force) = true]; reserved 4; // Deprecated: was string unique_id - string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON"]; + string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63]; bool disabled_by_default = 6; EntityCategory entity_category = 7; string device_class = 8; @@ -1523,7 +1524,7 @@ message ListEntitiesMediaPlayerResponse { string name = 3 [(max_data_length) = 120, (force) = true]; reserved 4; // Deprecated: was string unique_id - string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON"]; + string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63]; bool disabled_by_default = 6; EntityCategory entity_category = 7; @@ -2110,7 +2111,7 @@ message ListEntitiesAlarmControlPanelResponse { fixed32 key = 2 [(force) = true]; string name = 3 [(max_data_length) = 120, (force) = true]; reserved 4; // Deprecated: was string unique_id - string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON"]; + string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63]; bool disabled_by_default = 6; EntityCategory entity_category = 7; uint32 supported_features = 8; @@ -2157,7 +2158,7 @@ message ListEntitiesTextResponse { fixed32 key = 2 [(force) = true]; string name = 3 [(max_data_length) = 120, (force) = true]; reserved 4; // Deprecated: was string unique_id - string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON"]; + string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63]; bool disabled_by_default = 6; EntityCategory entity_category = 7; @@ -2206,7 +2207,7 @@ message ListEntitiesDateResponse { string name = 3 [(max_data_length) = 120, (force) = true]; reserved 4; // Deprecated: was string unique_id - string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON"]; + string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63]; bool disabled_by_default = 6; EntityCategory entity_category = 7; uint32 device_id = 8 [(field_ifdef) = "USE_DEVICES"]; @@ -2253,7 +2254,7 @@ message ListEntitiesTimeResponse { string name = 3 [(max_data_length) = 120, (force) = true]; reserved 4; // Deprecated: was string unique_id - string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON"]; + string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63]; bool disabled_by_default = 6; EntityCategory entity_category = 7; uint32 device_id = 8 [(field_ifdef) = "USE_DEVICES"]; @@ -2300,7 +2301,7 @@ message ListEntitiesEventResponse { string name = 3 [(max_data_length) = 120, (force) = true]; reserved 4; // Deprecated: was string unique_id - string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON"]; + string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63]; bool disabled_by_default = 6; EntityCategory entity_category = 7; string device_class = 8; @@ -2331,7 +2332,7 @@ message ListEntitiesValveResponse { string name = 3 [(max_data_length) = 120, (force) = true]; reserved 4; // Deprecated: was string unique_id - string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON"]; + string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63]; bool disabled_by_default = 6; EntityCategory entity_category = 7; string device_class = 8; @@ -2386,7 +2387,7 @@ message ListEntitiesDateTimeResponse { string name = 3 [(max_data_length) = 120, (force) = true]; reserved 4; // Deprecated: was string unique_id - string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON"]; + string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63]; bool disabled_by_default = 6; EntityCategory entity_category = 7; uint32 device_id = 8 [(field_ifdef) = "USE_DEVICES"]; @@ -2429,7 +2430,7 @@ message ListEntitiesUpdateResponse { string name = 3 [(max_data_length) = 120, (force) = true]; reserved 4; // Deprecated: was string unique_id - string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON"]; + string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63]; bool disabled_by_default = 6; EntityCategory entity_category = 7; string device_class = 8; @@ -2510,7 +2511,7 @@ message ListEntitiesInfraredResponse { string object_id = 1 [(max_data_length) = 120, (force) = true]; fixed32 key = 2 [(force) = true]; string name = 3 [(max_data_length) = 120, (force) = true]; - string icon = 4 [(field_ifdef) = "USE_ENTITY_ICON"]; + string icon = 4 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63]; bool disabled_by_default = 5; EntityCategory entity_category = 6; uint32 device_id = 7 [(field_ifdef) = "USE_DEVICES"]; diff --git a/esphome/components/api/api_pb2.cpp b/esphome/components/api/api_pb2.cpp index 00d700116f..6ef7ca4d3f 100644 --- a/esphome/components/api/api_pb2.cpp +++ b/esphome/components/api/api_pb2.cpp @@ -234,7 +234,7 @@ uint32_t ListEntitiesBinarySensorResponse::calculate_size() const { size += ProtoSize::calc_bool(1, this->is_status_binary_sensor); size += ProtoSize::calc_bool(1, this->disabled_by_default); #ifdef USE_ENTITY_ICON - size += ProtoSize::calc_length(1, this->icon.size()); + size += this->icon.size() ? 2 + this->icon.size() : 0; #endif size += ProtoSize::calc_uint32(1, static_cast(this->entity_category)); #ifdef USE_DEVICES @@ -295,7 +295,7 @@ uint32_t ListEntitiesCoverResponse::calculate_size() const { size += ProtoSize::calc_length(1, this->device_class.size()); size += ProtoSize::calc_bool(1, this->disabled_by_default); #ifdef USE_ENTITY_ICON - size += ProtoSize::calc_length(1, this->icon.size()); + size += this->icon.size() ? 2 + this->icon.size() : 0; #endif size += ProtoSize::calc_uint32(1, static_cast(this->entity_category)); size += ProtoSize::calc_bool(1, this->supports_stop); @@ -398,7 +398,7 @@ uint32_t ListEntitiesFanResponse::calculate_size() const { size += ProtoSize::calc_int32(1, this->supported_speed_count); size += ProtoSize::calc_bool(1, this->disabled_by_default); #ifdef USE_ENTITY_ICON - size += ProtoSize::calc_length(1, this->icon.size()); + size += this->icon.size() ? 2 + this->icon.size() : 0; #endif size += ProtoSize::calc_uint32(1, static_cast(this->entity_category)); if (!this->supported_preset_modes->empty()) { @@ -541,7 +541,7 @@ uint32_t ListEntitiesLightResponse::calculate_size() const { } size += ProtoSize::calc_bool(1, this->disabled_by_default); #ifdef USE_ENTITY_ICON - size += ProtoSize::calc_length(1, this->icon.size()); + size += this->icon.size() ? 2 + this->icon.size() : 0; #endif size += ProtoSize::calc_uint32(1, static_cast(this->entity_category)); #ifdef USE_DEVICES @@ -725,7 +725,7 @@ uint32_t ListEntitiesSensorResponse::calculate_size() const { size += 5; size += 2 + this->name.size(); #ifdef USE_ENTITY_ICON - size += ProtoSize::calc_length(1, this->icon.size()); + size += this->icon.size() ? 2 + this->icon.size() : 0; #endif size += ProtoSize::calc_length(1, this->unit_of_measurement.size()); size += ProtoSize::calc_int32(1, this->accuracy_decimals); @@ -784,7 +784,7 @@ uint32_t ListEntitiesSwitchResponse::calculate_size() const { size += 5; size += 2 + this->name.size(); #ifdef USE_ENTITY_ICON - size += ProtoSize::calc_length(1, this->icon.size()); + size += this->icon.size() ? 2 + this->icon.size() : 0; #endif size += ProtoSize::calc_bool(1, this->assumed_state); size += ProtoSize::calc_bool(1, this->disabled_by_default); @@ -862,7 +862,7 @@ uint32_t ListEntitiesTextSensorResponse::calculate_size() const { size += 5; size += 2 + this->name.size(); #ifdef USE_ENTITY_ICON - size += ProtoSize::calc_length(1, this->icon.size()); + size += this->icon.size() ? 2 + this->icon.size() : 0; #endif size += ProtoSize::calc_bool(1, this->disabled_by_default); size += ProtoSize::calc_uint32(1, static_cast(this->entity_category)); @@ -1319,7 +1319,7 @@ uint32_t ListEntitiesCameraResponse::calculate_size() const { size += 2 + this->name.size(); size += ProtoSize::calc_bool(1, this->disabled_by_default); #ifdef USE_ENTITY_ICON - size += ProtoSize::calc_length(1, this->icon.size()); + size += this->icon.size() ? 2 + this->icon.size() : 0; #endif size += ProtoSize::calc_uint32(1, static_cast(this->entity_category)); #ifdef USE_DEVICES @@ -1450,7 +1450,7 @@ uint32_t ListEntitiesClimateResponse::calculate_size() const { } size += ProtoSize::calc_bool(2, this->disabled_by_default); #ifdef USE_ENTITY_ICON - size += ProtoSize::calc_length(2, this->icon.size()); + size += this->icon.size() ? 3 + this->icon.size() : 0; #endif size += ProtoSize::calc_uint32(2, static_cast(this->entity_category)); size += ProtoSize::calc_float(2, this->visual_current_temperature_step); @@ -1627,7 +1627,7 @@ uint32_t ListEntitiesWaterHeaterResponse::calculate_size() const { size += 5; size += 2 + this->name.size(); #ifdef USE_ENTITY_ICON - size += ProtoSize::calc_length(1, this->icon.size()); + size += this->icon.size() ? 2 + this->icon.size() : 0; #endif size += ProtoSize::calc_bool(1, this->disabled_by_default); size += ProtoSize::calc_uint32(1, static_cast(this->entity_category)); @@ -1742,7 +1742,7 @@ uint32_t ListEntitiesNumberResponse::calculate_size() const { size += 5; size += 2 + this->name.size(); #ifdef USE_ENTITY_ICON - size += ProtoSize::calc_length(1, this->icon.size()); + size += this->icon.size() ? 2 + this->icon.size() : 0; #endif size += ProtoSize::calc_float(1, this->min_value); size += ProtoSize::calc_float(1, this->max_value); @@ -1828,7 +1828,7 @@ uint32_t ListEntitiesSelectResponse::calculate_size() const { size += 5; size += 2 + this->name.size(); #ifdef USE_ENTITY_ICON - size += ProtoSize::calc_length(1, this->icon.size()); + size += this->icon.size() ? 2 + this->icon.size() : 0; #endif if (!this->options->empty()) { for (const char *it : *this->options) { @@ -1923,7 +1923,7 @@ uint32_t ListEntitiesSirenResponse::calculate_size() const { size += 5; size += 2 + this->name.size(); #ifdef USE_ENTITY_ICON - size += ProtoSize::calc_length(1, this->icon.size()); + size += this->icon.size() ? 2 + this->icon.size() : 0; #endif size += ProtoSize::calc_bool(1, this->disabled_by_default); if (!this->tones->empty()) { @@ -2038,7 +2038,7 @@ uint32_t ListEntitiesLockResponse::calculate_size() const { size += 5; size += 2 + this->name.size(); #ifdef USE_ENTITY_ICON - size += ProtoSize::calc_length(1, this->icon.size()); + size += this->icon.size() ? 2 + this->icon.size() : 0; #endif size += ProtoSize::calc_bool(1, this->disabled_by_default); size += ProtoSize::calc_uint32(1, static_cast(this->entity_category)); @@ -2132,7 +2132,7 @@ uint32_t ListEntitiesButtonResponse::calculate_size() const { size += 5; size += 2 + this->name.size(); #ifdef USE_ENTITY_ICON - size += ProtoSize::calc_length(1, this->icon.size()); + size += this->icon.size() ? 2 + this->icon.size() : 0; #endif size += ProtoSize::calc_bool(1, this->disabled_by_default); size += ProtoSize::calc_uint32(1, static_cast(this->entity_category)); @@ -2210,7 +2210,7 @@ uint32_t ListEntitiesMediaPlayerResponse::calculate_size() const { size += 5; size += 2 + this->name.size(); #ifdef USE_ENTITY_ICON - size += ProtoSize::calc_length(1, this->icon.size()); + size += this->icon.size() ? 2 + this->icon.size() : 0; #endif size += ProtoSize::calc_bool(1, this->disabled_by_default); size += ProtoSize::calc_uint32(1, static_cast(this->entity_category)); @@ -3034,7 +3034,7 @@ uint32_t ListEntitiesAlarmControlPanelResponse::calculate_size() const { size += 5; size += 2 + this->name.size(); #ifdef USE_ENTITY_ICON - size += ProtoSize::calc_length(1, this->icon.size()); + size += this->icon.size() ? 2 + this->icon.size() : 0; #endif size += ProtoSize::calc_bool(1, this->disabled_by_default); size += ProtoSize::calc_uint32(1, static_cast(this->entity_category)); @@ -3127,7 +3127,7 @@ uint32_t ListEntitiesTextResponse::calculate_size() const { size += 5; size += 2 + this->name.size(); #ifdef USE_ENTITY_ICON - size += ProtoSize::calc_length(1, this->icon.size()); + size += this->icon.size() ? 2 + this->icon.size() : 0; #endif size += ProtoSize::calc_bool(1, this->disabled_by_default); size += ProtoSize::calc_uint32(1, static_cast(this->entity_category)); @@ -3216,7 +3216,7 @@ uint32_t ListEntitiesDateResponse::calculate_size() const { size += 5; size += 2 + this->name.size(); #ifdef USE_ENTITY_ICON - size += ProtoSize::calc_length(1, this->icon.size()); + size += this->icon.size() ? 2 + this->icon.size() : 0; #endif size += ProtoSize::calc_bool(1, this->disabled_by_default); size += ProtoSize::calc_uint32(1, static_cast(this->entity_category)); @@ -3303,7 +3303,7 @@ uint32_t ListEntitiesTimeResponse::calculate_size() const { size += 5; size += 2 + this->name.size(); #ifdef USE_ENTITY_ICON - size += ProtoSize::calc_length(1, this->icon.size()); + size += this->icon.size() ? 2 + this->icon.size() : 0; #endif size += ProtoSize::calc_bool(1, this->disabled_by_default); size += ProtoSize::calc_uint32(1, static_cast(this->entity_category)); @@ -3394,7 +3394,7 @@ uint32_t ListEntitiesEventResponse::calculate_size() const { size += 5; size += 2 + this->name.size(); #ifdef USE_ENTITY_ICON - size += ProtoSize::calc_length(1, this->icon.size()); + size += this->icon.size() ? 2 + this->icon.size() : 0; #endif size += ProtoSize::calc_bool(1, this->disabled_by_default); size += ProtoSize::calc_uint32(1, static_cast(this->entity_category)); @@ -3454,7 +3454,7 @@ uint32_t ListEntitiesValveResponse::calculate_size() const { size += 5; size += 2 + this->name.size(); #ifdef USE_ENTITY_ICON - size += ProtoSize::calc_length(1, this->icon.size()); + size += this->icon.size() ? 2 + this->icon.size() : 0; #endif size += ProtoSize::calc_bool(1, this->disabled_by_default); size += ProtoSize::calc_uint32(1, static_cast(this->entity_category)); @@ -3541,7 +3541,7 @@ uint32_t ListEntitiesDateTimeResponse::calculate_size() const { size += 5; size += 2 + this->name.size(); #ifdef USE_ENTITY_ICON - size += ProtoSize::calc_length(1, this->icon.size()); + size += this->icon.size() ? 2 + this->icon.size() : 0; #endif size += ProtoSize::calc_bool(1, this->disabled_by_default); size += ProtoSize::calc_uint32(1, static_cast(this->entity_category)); @@ -3619,7 +3619,7 @@ uint32_t ListEntitiesUpdateResponse::calculate_size() const { size += 5; size += 2 + this->name.size(); #ifdef USE_ENTITY_ICON - size += ProtoSize::calc_length(1, this->icon.size()); + size += this->icon.size() ? 2 + this->icon.size() : 0; #endif size += ProtoSize::calc_bool(1, this->disabled_by_default); size += ProtoSize::calc_uint32(1, static_cast(this->entity_category)); @@ -3765,7 +3765,7 @@ uint32_t ListEntitiesInfraredResponse::calculate_size() const { size += 5; size += 2 + this->name.size(); #ifdef USE_ENTITY_ICON - size += ProtoSize::calc_length(1, this->icon.size()); + size += this->icon.size() ? 2 + this->icon.size() : 0; #endif size += ProtoSize::calc_bool(1, this->disabled_by_default); size += ProtoSize::calc_uint32(1, static_cast(this->entity_category)); From 675400cc314c5f7574c8770503f3f60dd439d39b Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 3 Apr 2026 13:51:25 -1000 Subject: [PATCH 04/11] [api] Use .empty() for string zero check in single-byte varint size --- esphome/components/api/api_pb2.cpp | 50 ++++++++++++++--------------- script/api_protobuf/api_protobuf.py | 15 +++++++-- 2 files changed, 37 insertions(+), 28 deletions(-) diff --git a/esphome/components/api/api_pb2.cpp b/esphome/components/api/api_pb2.cpp index 6ef7ca4d3f..9ba24449d7 100644 --- a/esphome/components/api/api_pb2.cpp +++ b/esphome/components/api/api_pb2.cpp @@ -234,7 +234,7 @@ uint32_t ListEntitiesBinarySensorResponse::calculate_size() const { size += ProtoSize::calc_bool(1, this->is_status_binary_sensor); size += ProtoSize::calc_bool(1, this->disabled_by_default); #ifdef USE_ENTITY_ICON - size += this->icon.size() ? 2 + this->icon.size() : 0; + size += !this->icon.empty() ? 2 + this->icon.size() : 0; #endif size += ProtoSize::calc_uint32(1, static_cast(this->entity_category)); #ifdef USE_DEVICES @@ -295,7 +295,7 @@ uint32_t ListEntitiesCoverResponse::calculate_size() const { size += ProtoSize::calc_length(1, this->device_class.size()); size += ProtoSize::calc_bool(1, this->disabled_by_default); #ifdef USE_ENTITY_ICON - size += this->icon.size() ? 2 + this->icon.size() : 0; + size += !this->icon.empty() ? 2 + this->icon.size() : 0; #endif size += ProtoSize::calc_uint32(1, static_cast(this->entity_category)); size += ProtoSize::calc_bool(1, this->supports_stop); @@ -398,7 +398,7 @@ uint32_t ListEntitiesFanResponse::calculate_size() const { size += ProtoSize::calc_int32(1, this->supported_speed_count); size += ProtoSize::calc_bool(1, this->disabled_by_default); #ifdef USE_ENTITY_ICON - size += this->icon.size() ? 2 + this->icon.size() : 0; + size += !this->icon.empty() ? 2 + this->icon.size() : 0; #endif size += ProtoSize::calc_uint32(1, static_cast(this->entity_category)); if (!this->supported_preset_modes->empty()) { @@ -541,7 +541,7 @@ uint32_t ListEntitiesLightResponse::calculate_size() const { } size += ProtoSize::calc_bool(1, this->disabled_by_default); #ifdef USE_ENTITY_ICON - size += this->icon.size() ? 2 + this->icon.size() : 0; + size += !this->icon.empty() ? 2 + this->icon.size() : 0; #endif size += ProtoSize::calc_uint32(1, static_cast(this->entity_category)); #ifdef USE_DEVICES @@ -725,7 +725,7 @@ uint32_t ListEntitiesSensorResponse::calculate_size() const { size += 5; size += 2 + this->name.size(); #ifdef USE_ENTITY_ICON - size += this->icon.size() ? 2 + this->icon.size() : 0; + size += !this->icon.empty() ? 2 + this->icon.size() : 0; #endif size += ProtoSize::calc_length(1, this->unit_of_measurement.size()); size += ProtoSize::calc_int32(1, this->accuracy_decimals); @@ -784,7 +784,7 @@ uint32_t ListEntitiesSwitchResponse::calculate_size() const { size += 5; size += 2 + this->name.size(); #ifdef USE_ENTITY_ICON - size += this->icon.size() ? 2 + this->icon.size() : 0; + size += !this->icon.empty() ? 2 + this->icon.size() : 0; #endif size += ProtoSize::calc_bool(1, this->assumed_state); size += ProtoSize::calc_bool(1, this->disabled_by_default); @@ -862,7 +862,7 @@ uint32_t ListEntitiesTextSensorResponse::calculate_size() const { size += 5; size += 2 + this->name.size(); #ifdef USE_ENTITY_ICON - size += this->icon.size() ? 2 + this->icon.size() : 0; + size += !this->icon.empty() ? 2 + this->icon.size() : 0; #endif size += ProtoSize::calc_bool(1, this->disabled_by_default); size += ProtoSize::calc_uint32(1, static_cast(this->entity_category)); @@ -1319,7 +1319,7 @@ uint32_t ListEntitiesCameraResponse::calculate_size() const { size += 2 + this->name.size(); size += ProtoSize::calc_bool(1, this->disabled_by_default); #ifdef USE_ENTITY_ICON - size += this->icon.size() ? 2 + this->icon.size() : 0; + size += !this->icon.empty() ? 2 + this->icon.size() : 0; #endif size += ProtoSize::calc_uint32(1, static_cast(this->entity_category)); #ifdef USE_DEVICES @@ -1450,7 +1450,7 @@ uint32_t ListEntitiesClimateResponse::calculate_size() const { } size += ProtoSize::calc_bool(2, this->disabled_by_default); #ifdef USE_ENTITY_ICON - size += this->icon.size() ? 3 + this->icon.size() : 0; + size += !this->icon.empty() ? 3 + this->icon.size() : 0; #endif size += ProtoSize::calc_uint32(2, static_cast(this->entity_category)); size += ProtoSize::calc_float(2, this->visual_current_temperature_step); @@ -1627,7 +1627,7 @@ uint32_t ListEntitiesWaterHeaterResponse::calculate_size() const { size += 5; size += 2 + this->name.size(); #ifdef USE_ENTITY_ICON - size += this->icon.size() ? 2 + this->icon.size() : 0; + size += !this->icon.empty() ? 2 + this->icon.size() : 0; #endif size += ProtoSize::calc_bool(1, this->disabled_by_default); size += ProtoSize::calc_uint32(1, static_cast(this->entity_category)); @@ -1742,7 +1742,7 @@ uint32_t ListEntitiesNumberResponse::calculate_size() const { size += 5; size += 2 + this->name.size(); #ifdef USE_ENTITY_ICON - size += this->icon.size() ? 2 + this->icon.size() : 0; + size += !this->icon.empty() ? 2 + this->icon.size() : 0; #endif size += ProtoSize::calc_float(1, this->min_value); size += ProtoSize::calc_float(1, this->max_value); @@ -1828,7 +1828,7 @@ uint32_t ListEntitiesSelectResponse::calculate_size() const { size += 5; size += 2 + this->name.size(); #ifdef USE_ENTITY_ICON - size += this->icon.size() ? 2 + this->icon.size() : 0; + size += !this->icon.empty() ? 2 + this->icon.size() : 0; #endif if (!this->options->empty()) { for (const char *it : *this->options) { @@ -1923,7 +1923,7 @@ uint32_t ListEntitiesSirenResponse::calculate_size() const { size += 5; size += 2 + this->name.size(); #ifdef USE_ENTITY_ICON - size += this->icon.size() ? 2 + this->icon.size() : 0; + size += !this->icon.empty() ? 2 + this->icon.size() : 0; #endif size += ProtoSize::calc_bool(1, this->disabled_by_default); if (!this->tones->empty()) { @@ -2038,7 +2038,7 @@ uint32_t ListEntitiesLockResponse::calculate_size() const { size += 5; size += 2 + this->name.size(); #ifdef USE_ENTITY_ICON - size += this->icon.size() ? 2 + this->icon.size() : 0; + size += !this->icon.empty() ? 2 + this->icon.size() : 0; #endif size += ProtoSize::calc_bool(1, this->disabled_by_default); size += ProtoSize::calc_uint32(1, static_cast(this->entity_category)); @@ -2132,7 +2132,7 @@ uint32_t ListEntitiesButtonResponse::calculate_size() const { size += 5; size += 2 + this->name.size(); #ifdef USE_ENTITY_ICON - size += this->icon.size() ? 2 + this->icon.size() : 0; + size += !this->icon.empty() ? 2 + this->icon.size() : 0; #endif size += ProtoSize::calc_bool(1, this->disabled_by_default); size += ProtoSize::calc_uint32(1, static_cast(this->entity_category)); @@ -2210,7 +2210,7 @@ uint32_t ListEntitiesMediaPlayerResponse::calculate_size() const { size += 5; size += 2 + this->name.size(); #ifdef USE_ENTITY_ICON - size += this->icon.size() ? 2 + this->icon.size() : 0; + size += !this->icon.empty() ? 2 + this->icon.size() : 0; #endif size += ProtoSize::calc_bool(1, this->disabled_by_default); size += ProtoSize::calc_uint32(1, static_cast(this->entity_category)); @@ -3034,7 +3034,7 @@ uint32_t ListEntitiesAlarmControlPanelResponse::calculate_size() const { size += 5; size += 2 + this->name.size(); #ifdef USE_ENTITY_ICON - size += this->icon.size() ? 2 + this->icon.size() : 0; + size += !this->icon.empty() ? 2 + this->icon.size() : 0; #endif size += ProtoSize::calc_bool(1, this->disabled_by_default); size += ProtoSize::calc_uint32(1, static_cast(this->entity_category)); @@ -3127,7 +3127,7 @@ uint32_t ListEntitiesTextResponse::calculate_size() const { size += 5; size += 2 + this->name.size(); #ifdef USE_ENTITY_ICON - size += this->icon.size() ? 2 + this->icon.size() : 0; + size += !this->icon.empty() ? 2 + this->icon.size() : 0; #endif size += ProtoSize::calc_bool(1, this->disabled_by_default); size += ProtoSize::calc_uint32(1, static_cast(this->entity_category)); @@ -3216,7 +3216,7 @@ uint32_t ListEntitiesDateResponse::calculate_size() const { size += 5; size += 2 + this->name.size(); #ifdef USE_ENTITY_ICON - size += this->icon.size() ? 2 + this->icon.size() : 0; + size += !this->icon.empty() ? 2 + this->icon.size() : 0; #endif size += ProtoSize::calc_bool(1, this->disabled_by_default); size += ProtoSize::calc_uint32(1, static_cast(this->entity_category)); @@ -3303,7 +3303,7 @@ uint32_t ListEntitiesTimeResponse::calculate_size() const { size += 5; size += 2 + this->name.size(); #ifdef USE_ENTITY_ICON - size += this->icon.size() ? 2 + this->icon.size() : 0; + size += !this->icon.empty() ? 2 + this->icon.size() : 0; #endif size += ProtoSize::calc_bool(1, this->disabled_by_default); size += ProtoSize::calc_uint32(1, static_cast(this->entity_category)); @@ -3394,7 +3394,7 @@ uint32_t ListEntitiesEventResponse::calculate_size() const { size += 5; size += 2 + this->name.size(); #ifdef USE_ENTITY_ICON - size += this->icon.size() ? 2 + this->icon.size() : 0; + size += !this->icon.empty() ? 2 + this->icon.size() : 0; #endif size += ProtoSize::calc_bool(1, this->disabled_by_default); size += ProtoSize::calc_uint32(1, static_cast(this->entity_category)); @@ -3454,7 +3454,7 @@ uint32_t ListEntitiesValveResponse::calculate_size() const { size += 5; size += 2 + this->name.size(); #ifdef USE_ENTITY_ICON - size += this->icon.size() ? 2 + this->icon.size() : 0; + size += !this->icon.empty() ? 2 + this->icon.size() : 0; #endif size += ProtoSize::calc_bool(1, this->disabled_by_default); size += ProtoSize::calc_uint32(1, static_cast(this->entity_category)); @@ -3541,7 +3541,7 @@ uint32_t ListEntitiesDateTimeResponse::calculate_size() const { size += 5; size += 2 + this->name.size(); #ifdef USE_ENTITY_ICON - size += this->icon.size() ? 2 + this->icon.size() : 0; + size += !this->icon.empty() ? 2 + this->icon.size() : 0; #endif size += ProtoSize::calc_bool(1, this->disabled_by_default); size += ProtoSize::calc_uint32(1, static_cast(this->entity_category)); @@ -3619,7 +3619,7 @@ uint32_t ListEntitiesUpdateResponse::calculate_size() const { size += 5; size += 2 + this->name.size(); #ifdef USE_ENTITY_ICON - size += this->icon.size() ? 2 + this->icon.size() : 0; + size += !this->icon.empty() ? 2 + this->icon.size() : 0; #endif size += ProtoSize::calc_bool(1, this->disabled_by_default); size += ProtoSize::calc_uint32(1, static_cast(this->entity_category)); @@ -3765,7 +3765,7 @@ uint32_t ListEntitiesInfraredResponse::calculate_size() const { size += 5; size += 2 + this->name.size(); #ifdef USE_ENTITY_ICON - size += this->icon.size() ? 2 + this->icon.size() : 0; + size += !this->icon.empty() ? 2 + this->icon.size() : 0; #endif size += ProtoSize::calc_bool(1, this->disabled_by_default); size += ProtoSize::calc_uint32(1, static_cast(this->entity_category)); diff --git a/script/api_protobuf/api_protobuf.py b/script/api_protobuf/api_protobuf.py index a0335ff404..208412b62b 100755 --- a/script/api_protobuf/api_protobuf.py +++ b/script/api_protobuf/api_protobuf.py @@ -376,7 +376,11 @@ class TypeInfo(ABC): return f"size += ProtoSize::{method}({field_id_size}, {value});" def _get_single_byte_varint_size( - self, name: str, force: bool, extra_expr: str | None = None + self, + name: str, + force: bool, + extra_expr: str | None = None, + zero_check: str | None = None, ) -> str: """Size calculation when the varint is guaranteed to be 1 byte. @@ -387,12 +391,14 @@ class TypeInfo(ABC): name: Expression to check for zero (non-force only) force: Whether to skip the zero check extra_expr: Additional variable expression to add (e.g., data length) + zero_check: Override expression for the zero check (e.g., "!x.empty()") """ fixed = self.calculate_field_id_size() + 1 size_expr = f"{fixed} + {extra_expr}" if extra_expr else str(fixed) if force: return f"size += {size_expr};" - return f"size += {name} ? {size_expr} : 0;" + check = zero_check or name + return f"size += {check} ? {size_expr} : 0;" @abstractmethod def get_size_calculation(self, name: str, force: bool = False) -> str: @@ -1100,7 +1106,10 @@ class PointerToStringBufferType(PointerToBufferTypeBase): max_len = self.max_data_length if max_len is not None and max_len < 128: return self._get_single_byte_varint_size( - size_field, force, extra_expr=size_field + size_field, + force, + extra_expr=size_field, + zero_check=f"!this->{self.field_name}.empty()", ) return self._get_simple_size_calculation(size_field, force, "length") From 2d13def0eee1c3a1af6c2902583355e1bb04bc75 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 3 Apr 2026 14:00:01 -1000 Subject: [PATCH 05/11] [api] Add encode_raw_short_string for forced string fields with max_data_length Replace 3-call sequence (write_raw_byte + write_raw_byte + encode_raw) with single inlined encode_raw_short_string call for forced string fields with max_data_length < 128. The compiler can hoist the pos pointer across all three writes in one function boundary. --- esphome/components/api/api_pb2.cpp | 200 +++++++--------------------- esphome/components/api/proto.h | 10 ++ script/api_protobuf/api_protobuf.py | 8 +- 3 files changed, 67 insertions(+), 151 deletions(-) diff --git a/esphome/components/api/api_pb2.cpp b/esphome/components/api/api_pb2.cpp index 9ba24449d7..565d86b41b 100644 --- a/esphome/components/api/api_pb2.cpp +++ b/esphome/components/api/api_pb2.cpp @@ -207,13 +207,9 @@ uint32_t DeviceInfoResponse::calculate_size() const { } #ifdef USE_BINARY_SENSOR void ListEntitiesBinarySensorResponse::encode(ProtoWriteBuffer &buffer) const { - buffer.write_raw_byte(10); - buffer.write_raw_byte(static_cast(this->object_id.size())); - buffer.encode_raw(this->object_id.c_str(), this->object_id.size()); + buffer.encode_raw_short_string(10, this->object_id); buffer.write_tag_and_fixed32(21, this->key); - buffer.write_raw_byte(26); - buffer.write_raw_byte(static_cast(this->name.size())); - buffer.encode_raw(this->name.c_str(), this->name.size()); + buffer.encode_raw_short_string(26, this->name); buffer.encode_string(5, this->device_class); buffer.encode_bool(6, this->is_status_binary_sensor); buffer.encode_bool(7, this->disabled_by_default); @@ -263,13 +259,9 @@ uint32_t BinarySensorStateResponse::calculate_size() const { #endif #ifdef USE_COVER void ListEntitiesCoverResponse::encode(ProtoWriteBuffer &buffer) const { - buffer.write_raw_byte(10); - buffer.write_raw_byte(static_cast(this->object_id.size())); - buffer.encode_raw(this->object_id.c_str(), this->object_id.size()); + buffer.encode_raw_short_string(10, this->object_id); buffer.write_tag_and_fixed32(21, this->key); - buffer.write_raw_byte(26); - buffer.write_raw_byte(static_cast(this->name.size())); - buffer.encode_raw(this->name.c_str(), this->name.size()); + buffer.encode_raw_short_string(26, this->name); buffer.encode_bool(5, this->assumed_state); buffer.encode_bool(6, this->supports_position); buffer.encode_bool(7, this->supports_tilt); @@ -364,13 +356,9 @@ bool CoverCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) { #endif #ifdef USE_FAN void ListEntitiesFanResponse::encode(ProtoWriteBuffer &buffer) const { - buffer.write_raw_byte(10); - buffer.write_raw_byte(static_cast(this->object_id.size())); - buffer.encode_raw(this->object_id.c_str(), this->object_id.size()); + buffer.encode_raw_short_string(10, this->object_id); buffer.write_tag_and_fixed32(21, this->key); - buffer.write_raw_byte(26); - buffer.write_raw_byte(static_cast(this->name.size())); - buffer.encode_raw(this->name.c_str(), this->name.size()); + buffer.encode_raw_short_string(26, this->name); buffer.encode_bool(5, this->supports_oscillation); buffer.encode_bool(6, this->supports_speed); buffer.encode_bool(7, this->supports_direction); @@ -498,13 +486,9 @@ bool FanCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) { #endif #ifdef USE_LIGHT void ListEntitiesLightResponse::encode(ProtoWriteBuffer &buffer) const { - buffer.write_raw_byte(10); - buffer.write_raw_byte(static_cast(this->object_id.size())); - buffer.encode_raw(this->object_id.c_str(), this->object_id.size()); + buffer.encode_raw_short_string(10, this->object_id); buffer.write_tag_and_fixed32(21, this->key); - buffer.write_raw_byte(26); - buffer.write_raw_byte(static_cast(this->name.size())); - buffer.encode_raw(this->name.c_str(), this->name.size()); + buffer.encode_raw_short_string(26, this->name); for (const auto &it : *this->supported_color_modes) { buffer.encode_uint32(12, static_cast(it), true); } @@ -698,13 +682,9 @@ bool LightCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) { #endif #ifdef USE_SENSOR void ListEntitiesSensorResponse::encode(ProtoWriteBuffer &buffer) const { - buffer.write_raw_byte(10); - buffer.write_raw_byte(static_cast(this->object_id.size())); - buffer.encode_raw(this->object_id.c_str(), this->object_id.size()); + buffer.encode_raw_short_string(10, this->object_id); buffer.write_tag_and_fixed32(21, this->key); - buffer.write_raw_byte(26); - buffer.write_raw_byte(static_cast(this->name.size())); - buffer.encode_raw(this->name.c_str(), this->name.size()); + buffer.encode_raw_short_string(26, this->name); #ifdef USE_ENTITY_ICON buffer.encode_string(5, this->icon); #endif @@ -760,13 +740,9 @@ uint32_t SensorStateResponse::calculate_size() const { #endif #ifdef USE_SWITCH void ListEntitiesSwitchResponse::encode(ProtoWriteBuffer &buffer) const { - buffer.write_raw_byte(10); - buffer.write_raw_byte(static_cast(this->object_id.size())); - buffer.encode_raw(this->object_id.c_str(), this->object_id.size()); + buffer.encode_raw_short_string(10, this->object_id); buffer.write_tag_and_fixed32(21, this->key); - buffer.write_raw_byte(26); - buffer.write_raw_byte(static_cast(this->name.size())); - buffer.encode_raw(this->name.c_str(), this->name.size()); + buffer.encode_raw_short_string(26, this->name); #ifdef USE_ENTITY_ICON buffer.encode_string(5, this->icon); #endif @@ -839,13 +815,9 @@ bool SwitchCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) { #endif #ifdef USE_TEXT_SENSOR void ListEntitiesTextSensorResponse::encode(ProtoWriteBuffer &buffer) const { - buffer.write_raw_byte(10); - buffer.write_raw_byte(static_cast(this->object_id.size())); - buffer.encode_raw(this->object_id.c_str(), this->object_id.size()); + buffer.encode_raw_short_string(10, this->object_id); buffer.write_tag_and_fixed32(21, this->key); - buffer.write_raw_byte(26); - buffer.write_raw_byte(static_cast(this->name.size())); - buffer.encode_raw(this->name.c_str(), this->name.size()); + buffer.encode_raw_short_string(26, this->name); #ifdef USE_ENTITY_ICON buffer.encode_string(5, this->icon); #endif @@ -1296,13 +1268,9 @@ uint32_t ExecuteServiceResponse::calculate_size() const { #endif #ifdef USE_CAMERA void ListEntitiesCameraResponse::encode(ProtoWriteBuffer &buffer) const { - buffer.write_raw_byte(10); - buffer.write_raw_byte(static_cast(this->object_id.size())); - buffer.encode_raw(this->object_id.c_str(), this->object_id.size()); + buffer.encode_raw_short_string(10, this->object_id); buffer.write_tag_and_fixed32(21, this->key); - buffer.write_raw_byte(26); - buffer.write_raw_byte(static_cast(this->name.size())); - buffer.encode_raw(this->name.c_str(), this->name.size()); + buffer.encode_raw_short_string(26, this->name); buffer.encode_bool(5, this->disabled_by_default); #ifdef USE_ENTITY_ICON buffer.encode_string(6, this->icon); @@ -1361,13 +1329,9 @@ bool CameraImageRequest::decode_varint(uint32_t field_id, proto_varint_value_t v #endif #ifdef USE_CLIMATE void ListEntitiesClimateResponse::encode(ProtoWriteBuffer &buffer) const { - buffer.write_raw_byte(10); - buffer.write_raw_byte(static_cast(this->object_id.size())); - buffer.encode_raw(this->object_id.c_str(), this->object_id.size()); + buffer.encode_raw_short_string(10, this->object_id); buffer.write_tag_and_fixed32(21, this->key); - buffer.write_raw_byte(26); - buffer.write_raw_byte(static_cast(this->name.size())); - buffer.encode_raw(this->name.c_str(), this->name.size()); + buffer.encode_raw_short_string(26, this->name); buffer.encode_bool(5, this->supports_current_temperature); buffer.encode_bool(6, this->supports_two_point_target_temperature); for (const auto &it : *this->supported_modes) { @@ -1598,13 +1562,9 @@ bool ClimateCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) { #endif #ifdef USE_WATER_HEATER void ListEntitiesWaterHeaterResponse::encode(ProtoWriteBuffer &buffer) const { - buffer.write_raw_byte(10); - buffer.write_raw_byte(static_cast(this->object_id.size())); - buffer.encode_raw(this->object_id.c_str(), this->object_id.size()); + buffer.encode_raw_short_string(10, this->object_id); buffer.write_tag_and_fixed32(21, this->key); - buffer.write_raw_byte(26); - buffer.write_raw_byte(static_cast(this->name.size())); - buffer.encode_raw(this->name.c_str(), this->name.size()); + buffer.encode_raw_short_string(26, this->name); #ifdef USE_ENTITY_ICON buffer.encode_string(4, this->icon); #endif @@ -1714,13 +1674,9 @@ bool WaterHeaterCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value #endif #ifdef USE_NUMBER void ListEntitiesNumberResponse::encode(ProtoWriteBuffer &buffer) const { - buffer.write_raw_byte(10); - buffer.write_raw_byte(static_cast(this->object_id.size())); - buffer.encode_raw(this->object_id.c_str(), this->object_id.size()); + buffer.encode_raw_short_string(10, this->object_id); buffer.write_tag_and_fixed32(21, this->key); - buffer.write_raw_byte(26); - buffer.write_raw_byte(static_cast(this->name.size())); - buffer.encode_raw(this->name.c_str(), this->name.size()); + buffer.encode_raw_short_string(26, this->name); #ifdef USE_ENTITY_ICON buffer.encode_string(5, this->icon); #endif @@ -1803,13 +1759,9 @@ bool NumberCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) { #endif #ifdef USE_SELECT void ListEntitiesSelectResponse::encode(ProtoWriteBuffer &buffer) const { - buffer.write_raw_byte(10); - buffer.write_raw_byte(static_cast(this->object_id.size())); - buffer.encode_raw(this->object_id.c_str(), this->object_id.size()); + buffer.encode_raw_short_string(10, this->object_id); buffer.write_tag_and_fixed32(21, this->key); - buffer.write_raw_byte(26); - buffer.write_raw_byte(static_cast(this->name.size())); - buffer.encode_raw(this->name.c_str(), this->name.size()); + buffer.encode_raw_short_string(26, this->name); #ifdef USE_ENTITY_ICON buffer.encode_string(5, this->icon); #endif @@ -1896,13 +1848,9 @@ bool SelectCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) { #endif #ifdef USE_SIREN void ListEntitiesSirenResponse::encode(ProtoWriteBuffer &buffer) const { - buffer.write_raw_byte(10); - buffer.write_raw_byte(static_cast(this->object_id.size())); - buffer.encode_raw(this->object_id.c_str(), this->object_id.size()); + buffer.encode_raw_short_string(10, this->object_id); buffer.write_tag_and_fixed32(21, this->key); - buffer.write_raw_byte(26); - buffer.write_raw_byte(static_cast(this->name.size())); - buffer.encode_raw(this->name.c_str(), this->name.size()); + buffer.encode_raw_short_string(26, this->name); #ifdef USE_ENTITY_ICON buffer.encode_string(5, this->icon); #endif @@ -2012,13 +1960,9 @@ bool SirenCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) { #endif #ifdef USE_LOCK void ListEntitiesLockResponse::encode(ProtoWriteBuffer &buffer) const { - buffer.write_raw_byte(10); - buffer.write_raw_byte(static_cast(this->object_id.size())); - buffer.encode_raw(this->object_id.c_str(), this->object_id.size()); + buffer.encode_raw_short_string(10, this->object_id); buffer.write_tag_and_fixed32(21, this->key); - buffer.write_raw_byte(26); - buffer.write_raw_byte(static_cast(this->name.size())); - buffer.encode_raw(this->name.c_str(), this->name.size()); + buffer.encode_raw_short_string(26, this->name); #ifdef USE_ENTITY_ICON buffer.encode_string(5, this->icon); #endif @@ -2109,13 +2053,9 @@ bool LockCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) { #endif #ifdef USE_BUTTON void ListEntitiesButtonResponse::encode(ProtoWriteBuffer &buffer) const { - buffer.write_raw_byte(10); - buffer.write_raw_byte(static_cast(this->object_id.size())); - buffer.encode_raw(this->object_id.c_str(), this->object_id.size()); + buffer.encode_raw_short_string(10, this->object_id); buffer.write_tag_and_fixed32(21, this->key); - buffer.write_raw_byte(26); - buffer.write_raw_byte(static_cast(this->name.size())); - buffer.encode_raw(this->name.c_str(), this->name.size()); + buffer.encode_raw_short_string(26, this->name); #ifdef USE_ENTITY_ICON buffer.encode_string(5, this->icon); #endif @@ -2183,13 +2123,9 @@ uint32_t MediaPlayerSupportedFormat::calculate_size() const { return size; } void ListEntitiesMediaPlayerResponse::encode(ProtoWriteBuffer &buffer) const { - buffer.write_raw_byte(10); - buffer.write_raw_byte(static_cast(this->object_id.size())); - buffer.encode_raw(this->object_id.c_str(), this->object_id.size()); + buffer.encode_raw_short_string(10, this->object_id); buffer.write_tag_and_fixed32(21, this->key); - buffer.write_raw_byte(26); - buffer.write_raw_byte(static_cast(this->name.size())); - buffer.encode_raw(this->name.c_str(), this->name.size()); + buffer.encode_raw_short_string(26, this->name); #ifdef USE_ENTITY_ICON buffer.encode_string(5, this->icon); #endif @@ -3009,13 +2945,9 @@ bool VoiceAssistantSetConfiguration::decode_length(uint32_t field_id, ProtoLengt #endif #ifdef USE_ALARM_CONTROL_PANEL void ListEntitiesAlarmControlPanelResponse::encode(ProtoWriteBuffer &buffer) const { - buffer.write_raw_byte(10); - buffer.write_raw_byte(static_cast(this->object_id.size())); - buffer.encode_raw(this->object_id.c_str(), this->object_id.size()); + buffer.encode_raw_short_string(10, this->object_id); buffer.write_tag_and_fixed32(21, this->key); - buffer.write_raw_byte(26); - buffer.write_raw_byte(static_cast(this->name.size())); - buffer.encode_raw(this->name.c_str(), this->name.size()); + buffer.encode_raw_short_string(26, this->name); #ifdef USE_ENTITY_ICON buffer.encode_string(5, this->icon); #endif @@ -3101,13 +3033,9 @@ bool AlarmControlPanelCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit #endif #ifdef USE_TEXT void ListEntitiesTextResponse::encode(ProtoWriteBuffer &buffer) const { - buffer.write_raw_byte(10); - buffer.write_raw_byte(static_cast(this->object_id.size())); - buffer.encode_raw(this->object_id.c_str(), this->object_id.size()); + buffer.encode_raw_short_string(10, this->object_id); buffer.write_tag_and_fixed32(21, this->key); - buffer.write_raw_byte(26); - buffer.write_raw_byte(static_cast(this->name.size())); - buffer.encode_raw(this->name.c_str(), this->name.size()); + buffer.encode_raw_short_string(26, this->name); #ifdef USE_ENTITY_ICON buffer.encode_string(5, this->icon); #endif @@ -3194,13 +3122,9 @@ bool TextCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) { #endif #ifdef USE_DATETIME_DATE void ListEntitiesDateResponse::encode(ProtoWriteBuffer &buffer) const { - buffer.write_raw_byte(10); - buffer.write_raw_byte(static_cast(this->object_id.size())); - buffer.encode_raw(this->object_id.c_str(), this->object_id.size()); + buffer.encode_raw_short_string(10, this->object_id); buffer.write_tag_and_fixed32(21, this->key); - buffer.write_raw_byte(26); - buffer.write_raw_byte(static_cast(this->name.size())); - buffer.encode_raw(this->name.c_str(), this->name.size()); + buffer.encode_raw_short_string(26, this->name); #ifdef USE_ENTITY_ICON buffer.encode_string(5, this->icon); #endif @@ -3281,13 +3205,9 @@ bool DateCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) { #endif #ifdef USE_DATETIME_TIME void ListEntitiesTimeResponse::encode(ProtoWriteBuffer &buffer) const { - buffer.write_raw_byte(10); - buffer.write_raw_byte(static_cast(this->object_id.size())); - buffer.encode_raw(this->object_id.c_str(), this->object_id.size()); + buffer.encode_raw_short_string(10, this->object_id); buffer.write_tag_and_fixed32(21, this->key); - buffer.write_raw_byte(26); - buffer.write_raw_byte(static_cast(this->name.size())); - buffer.encode_raw(this->name.c_str(), this->name.size()); + buffer.encode_raw_short_string(26, this->name); #ifdef USE_ENTITY_ICON buffer.encode_string(5, this->icon); #endif @@ -3368,13 +3288,9 @@ bool TimeCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) { #endif #ifdef USE_EVENT void ListEntitiesEventResponse::encode(ProtoWriteBuffer &buffer) const { - buffer.write_raw_byte(10); - buffer.write_raw_byte(static_cast(this->object_id.size())); - buffer.encode_raw(this->object_id.c_str(), this->object_id.size()); + buffer.encode_raw_short_string(10, this->object_id); buffer.write_tag_and_fixed32(21, this->key); - buffer.write_raw_byte(26); - buffer.write_raw_byte(static_cast(this->name.size())); - buffer.encode_raw(this->name.c_str(), this->name.size()); + buffer.encode_raw_short_string(26, this->name); #ifdef USE_ENTITY_ICON buffer.encode_string(5, this->icon); #endif @@ -3428,13 +3344,9 @@ uint32_t EventResponse::calculate_size() const { #endif #ifdef USE_VALVE void ListEntitiesValveResponse::encode(ProtoWriteBuffer &buffer) const { - buffer.write_raw_byte(10); - buffer.write_raw_byte(static_cast(this->object_id.size())); - buffer.encode_raw(this->object_id.c_str(), this->object_id.size()); + buffer.encode_raw_short_string(10, this->object_id); buffer.write_tag_and_fixed32(21, this->key); - buffer.write_raw_byte(26); - buffer.write_raw_byte(static_cast(this->name.size())); - buffer.encode_raw(this->name.c_str(), this->name.size()); + buffer.encode_raw_short_string(26, this->name); #ifdef USE_ENTITY_ICON buffer.encode_string(5, this->icon); #endif @@ -3519,13 +3431,9 @@ bool ValveCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) { #endif #ifdef USE_DATETIME_DATETIME void ListEntitiesDateTimeResponse::encode(ProtoWriteBuffer &buffer) const { - buffer.write_raw_byte(10); - buffer.write_raw_byte(static_cast(this->object_id.size())); - buffer.encode_raw(this->object_id.c_str(), this->object_id.size()); + buffer.encode_raw_short_string(10, this->object_id); buffer.write_tag_and_fixed32(21, this->key); - buffer.write_raw_byte(26); - buffer.write_raw_byte(static_cast(this->name.size())); - buffer.encode_raw(this->name.c_str(), this->name.size()); + buffer.encode_raw_short_string(26, this->name); #ifdef USE_ENTITY_ICON buffer.encode_string(5, this->icon); #endif @@ -3596,13 +3504,9 @@ bool DateTimeCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) { #endif #ifdef USE_UPDATE void ListEntitiesUpdateResponse::encode(ProtoWriteBuffer &buffer) const { - buffer.write_raw_byte(10); - buffer.write_raw_byte(static_cast(this->object_id.size())); - buffer.encode_raw(this->object_id.c_str(), this->object_id.size()); + buffer.encode_raw_short_string(10, this->object_id); buffer.write_tag_and_fixed32(21, this->key); - buffer.write_raw_byte(26); - buffer.write_raw_byte(static_cast(this->name.size())); - buffer.encode_raw(this->name.c_str(), this->name.size()); + buffer.encode_raw_short_string(26, this->name); #ifdef USE_ENTITY_ICON buffer.encode_string(5, this->icon); #endif @@ -3741,13 +3645,9 @@ uint32_t ZWaveProxyRequest::calculate_size() const { #endif #ifdef USE_INFRARED void ListEntitiesInfraredResponse::encode(ProtoWriteBuffer &buffer) const { - buffer.write_raw_byte(10); - buffer.write_raw_byte(static_cast(this->object_id.size())); - buffer.encode_raw(this->object_id.c_str(), this->object_id.size()); + buffer.encode_raw_short_string(10, this->object_id); buffer.write_tag_and_fixed32(21, this->key); - buffer.write_raw_byte(26); - buffer.write_raw_byte(static_cast(this->name.size())); - buffer.encode_raw(this->name.c_str(), this->name.size()); + buffer.encode_raw_short_string(26, this->name); #ifdef USE_ENTITY_ICON buffer.encode_string(4, this->icon); #endif diff --git a/esphome/components/api/proto.h b/esphome/components/api/proto.h index b629018a91..86f0085d32 100644 --- a/esphome/components/api/proto.h +++ b/esphome/components/api/proto.h @@ -240,6 +240,16 @@ class ProtoWriteBuffer { std::memcpy(this->pos_, data, len); this->pos_ += len; } + /// Write tag + 1-byte length + raw string data. For strings with max_data_length < 128. + /// Tag must be a single-byte varint (< 128). Always encodes (no zero check). + inline void encode_raw_short_string(uint8_t tag, const StringRef &ref) ESPHOME_ALWAYS_INLINE { + this->debug_check_bounds_(2 + ref.size()); + uint8_t *__restrict__ pos = this->pos_; + *pos++ = tag; + *pos++ = static_cast(ref.size()); + std::memcpy(pos, ref.c_str(), ref.size()); + this->pos_ = pos + ref.size(); + } /// Write a precomputed tag byte + 32-bit value in one operation. /// Tag must be a single-byte varint (< 128). No zero check. inline void write_tag_and_fixed32(uint8_t tag, uint32_t value) ESPHOME_ALWAYS_INLINE { diff --git a/script/api_protobuf/api_protobuf.py b/script/api_protobuf/api_protobuf.py index 208412b62b..644ff85b96 100755 --- a/script/api_protobuf/api_protobuf.py +++ b/script/api_protobuf/api_protobuf.py @@ -1074,10 +1074,16 @@ class PointerToStringBufferType(PointerToBufferTypeBase): @property def encode_content(self) -> str: + max_len = self.max_data_length + if max_len is not None and max_len < 128 and self.force: + tag = self.calculate_tag() + if tag < 128: + return ( + f"buffer.encode_raw_short_string({tag}, this->{self.field_name});" + ) if result := self._encode_bytes_with_precomputed_tag( f"this->{self.field_name}.c_str()", f"this->{self.field_name}.size()", - max_len=self.max_data_length, ): return result if self.force: From fd68e9a8274d544f9a9742ddfc4d052796801bdf Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 3 Apr 2026 14:01:14 -1000 Subject: [PATCH 06/11] [api] Rename encode_raw_short_string to encode_short_string --- esphome/components/api/api_pb2.cpp | 100 ++++++++++++++-------------- esphome/components/api/proto.h | 2 +- script/api_protobuf/api_protobuf.py | 4 +- 3 files changed, 52 insertions(+), 54 deletions(-) diff --git a/esphome/components/api/api_pb2.cpp b/esphome/components/api/api_pb2.cpp index 565d86b41b..8dea47bb6b 100644 --- a/esphome/components/api/api_pb2.cpp +++ b/esphome/components/api/api_pb2.cpp @@ -207,9 +207,9 @@ uint32_t DeviceInfoResponse::calculate_size() const { } #ifdef USE_BINARY_SENSOR void ListEntitiesBinarySensorResponse::encode(ProtoWriteBuffer &buffer) const { - buffer.encode_raw_short_string(10, this->object_id); + buffer.encode_short_string(10, this->object_id); buffer.write_tag_and_fixed32(21, this->key); - buffer.encode_raw_short_string(26, this->name); + buffer.encode_short_string(26, this->name); buffer.encode_string(5, this->device_class); buffer.encode_bool(6, this->is_status_binary_sensor); buffer.encode_bool(7, this->disabled_by_default); @@ -259,9 +259,9 @@ uint32_t BinarySensorStateResponse::calculate_size() const { #endif #ifdef USE_COVER void ListEntitiesCoverResponse::encode(ProtoWriteBuffer &buffer) const { - buffer.encode_raw_short_string(10, this->object_id); + buffer.encode_short_string(10, this->object_id); buffer.write_tag_and_fixed32(21, this->key); - buffer.encode_raw_short_string(26, this->name); + buffer.encode_short_string(26, this->name); buffer.encode_bool(5, this->assumed_state); buffer.encode_bool(6, this->supports_position); buffer.encode_bool(7, this->supports_tilt); @@ -356,9 +356,9 @@ bool CoverCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) { #endif #ifdef USE_FAN void ListEntitiesFanResponse::encode(ProtoWriteBuffer &buffer) const { - buffer.encode_raw_short_string(10, this->object_id); + buffer.encode_short_string(10, this->object_id); buffer.write_tag_and_fixed32(21, this->key); - buffer.encode_raw_short_string(26, this->name); + buffer.encode_short_string(26, this->name); buffer.encode_bool(5, this->supports_oscillation); buffer.encode_bool(6, this->supports_speed); buffer.encode_bool(7, this->supports_direction); @@ -486,9 +486,9 @@ bool FanCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) { #endif #ifdef USE_LIGHT void ListEntitiesLightResponse::encode(ProtoWriteBuffer &buffer) const { - buffer.encode_raw_short_string(10, this->object_id); + buffer.encode_short_string(10, this->object_id); buffer.write_tag_and_fixed32(21, this->key); - buffer.encode_raw_short_string(26, this->name); + buffer.encode_short_string(26, this->name); for (const auto &it : *this->supported_color_modes) { buffer.encode_uint32(12, static_cast(it), true); } @@ -682,9 +682,9 @@ bool LightCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) { #endif #ifdef USE_SENSOR void ListEntitiesSensorResponse::encode(ProtoWriteBuffer &buffer) const { - buffer.encode_raw_short_string(10, this->object_id); + buffer.encode_short_string(10, this->object_id); buffer.write_tag_and_fixed32(21, this->key); - buffer.encode_raw_short_string(26, this->name); + buffer.encode_short_string(26, this->name); #ifdef USE_ENTITY_ICON buffer.encode_string(5, this->icon); #endif @@ -740,9 +740,9 @@ uint32_t SensorStateResponse::calculate_size() const { #endif #ifdef USE_SWITCH void ListEntitiesSwitchResponse::encode(ProtoWriteBuffer &buffer) const { - buffer.encode_raw_short_string(10, this->object_id); + buffer.encode_short_string(10, this->object_id); buffer.write_tag_and_fixed32(21, this->key); - buffer.encode_raw_short_string(26, this->name); + buffer.encode_short_string(26, this->name); #ifdef USE_ENTITY_ICON buffer.encode_string(5, this->icon); #endif @@ -815,9 +815,9 @@ bool SwitchCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) { #endif #ifdef USE_TEXT_SENSOR void ListEntitiesTextSensorResponse::encode(ProtoWriteBuffer &buffer) const { - buffer.encode_raw_short_string(10, this->object_id); + buffer.encode_short_string(10, this->object_id); buffer.write_tag_and_fixed32(21, this->key); - buffer.encode_raw_short_string(26, this->name); + buffer.encode_short_string(26, this->name); #ifdef USE_ENTITY_ICON buffer.encode_string(5, this->icon); #endif @@ -1268,9 +1268,9 @@ uint32_t ExecuteServiceResponse::calculate_size() const { #endif #ifdef USE_CAMERA void ListEntitiesCameraResponse::encode(ProtoWriteBuffer &buffer) const { - buffer.encode_raw_short_string(10, this->object_id); + buffer.encode_short_string(10, this->object_id); buffer.write_tag_and_fixed32(21, this->key); - buffer.encode_raw_short_string(26, this->name); + buffer.encode_short_string(26, this->name); buffer.encode_bool(5, this->disabled_by_default); #ifdef USE_ENTITY_ICON buffer.encode_string(6, this->icon); @@ -1329,9 +1329,9 @@ bool CameraImageRequest::decode_varint(uint32_t field_id, proto_varint_value_t v #endif #ifdef USE_CLIMATE void ListEntitiesClimateResponse::encode(ProtoWriteBuffer &buffer) const { - buffer.encode_raw_short_string(10, this->object_id); + buffer.encode_short_string(10, this->object_id); buffer.write_tag_and_fixed32(21, this->key); - buffer.encode_raw_short_string(26, this->name); + buffer.encode_short_string(26, this->name); buffer.encode_bool(5, this->supports_current_temperature); buffer.encode_bool(6, this->supports_two_point_target_temperature); for (const auto &it : *this->supported_modes) { @@ -1562,9 +1562,9 @@ bool ClimateCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) { #endif #ifdef USE_WATER_HEATER void ListEntitiesWaterHeaterResponse::encode(ProtoWriteBuffer &buffer) const { - buffer.encode_raw_short_string(10, this->object_id); + buffer.encode_short_string(10, this->object_id); buffer.write_tag_and_fixed32(21, this->key); - buffer.encode_raw_short_string(26, this->name); + buffer.encode_short_string(26, this->name); #ifdef USE_ENTITY_ICON buffer.encode_string(4, this->icon); #endif @@ -1674,9 +1674,9 @@ bool WaterHeaterCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value #endif #ifdef USE_NUMBER void ListEntitiesNumberResponse::encode(ProtoWriteBuffer &buffer) const { - buffer.encode_raw_short_string(10, this->object_id); + buffer.encode_short_string(10, this->object_id); buffer.write_tag_and_fixed32(21, this->key); - buffer.encode_raw_short_string(26, this->name); + buffer.encode_short_string(26, this->name); #ifdef USE_ENTITY_ICON buffer.encode_string(5, this->icon); #endif @@ -1759,9 +1759,9 @@ bool NumberCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) { #endif #ifdef USE_SELECT void ListEntitiesSelectResponse::encode(ProtoWriteBuffer &buffer) const { - buffer.encode_raw_short_string(10, this->object_id); + buffer.encode_short_string(10, this->object_id); buffer.write_tag_and_fixed32(21, this->key); - buffer.encode_raw_short_string(26, this->name); + buffer.encode_short_string(26, this->name); #ifdef USE_ENTITY_ICON buffer.encode_string(5, this->icon); #endif @@ -1848,9 +1848,9 @@ bool SelectCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) { #endif #ifdef USE_SIREN void ListEntitiesSirenResponse::encode(ProtoWriteBuffer &buffer) const { - buffer.encode_raw_short_string(10, this->object_id); + buffer.encode_short_string(10, this->object_id); buffer.write_tag_and_fixed32(21, this->key); - buffer.encode_raw_short_string(26, this->name); + buffer.encode_short_string(26, this->name); #ifdef USE_ENTITY_ICON buffer.encode_string(5, this->icon); #endif @@ -1960,9 +1960,9 @@ bool SirenCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) { #endif #ifdef USE_LOCK void ListEntitiesLockResponse::encode(ProtoWriteBuffer &buffer) const { - buffer.encode_raw_short_string(10, this->object_id); + buffer.encode_short_string(10, this->object_id); buffer.write_tag_and_fixed32(21, this->key); - buffer.encode_raw_short_string(26, this->name); + buffer.encode_short_string(26, this->name); #ifdef USE_ENTITY_ICON buffer.encode_string(5, this->icon); #endif @@ -2053,9 +2053,9 @@ bool LockCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) { #endif #ifdef USE_BUTTON void ListEntitiesButtonResponse::encode(ProtoWriteBuffer &buffer) const { - buffer.encode_raw_short_string(10, this->object_id); + buffer.encode_short_string(10, this->object_id); buffer.write_tag_and_fixed32(21, this->key); - buffer.encode_raw_short_string(26, this->name); + buffer.encode_short_string(26, this->name); #ifdef USE_ENTITY_ICON buffer.encode_string(5, this->icon); #endif @@ -2123,9 +2123,9 @@ uint32_t MediaPlayerSupportedFormat::calculate_size() const { return size; } void ListEntitiesMediaPlayerResponse::encode(ProtoWriteBuffer &buffer) const { - buffer.encode_raw_short_string(10, this->object_id); + buffer.encode_short_string(10, this->object_id); buffer.write_tag_and_fixed32(21, this->key); - buffer.encode_raw_short_string(26, this->name); + buffer.encode_short_string(26, this->name); #ifdef USE_ENTITY_ICON buffer.encode_string(5, this->icon); #endif @@ -2945,9 +2945,9 @@ bool VoiceAssistantSetConfiguration::decode_length(uint32_t field_id, ProtoLengt #endif #ifdef USE_ALARM_CONTROL_PANEL void ListEntitiesAlarmControlPanelResponse::encode(ProtoWriteBuffer &buffer) const { - buffer.encode_raw_short_string(10, this->object_id); + buffer.encode_short_string(10, this->object_id); buffer.write_tag_and_fixed32(21, this->key); - buffer.encode_raw_short_string(26, this->name); + buffer.encode_short_string(26, this->name); #ifdef USE_ENTITY_ICON buffer.encode_string(5, this->icon); #endif @@ -3033,9 +3033,9 @@ bool AlarmControlPanelCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit #endif #ifdef USE_TEXT void ListEntitiesTextResponse::encode(ProtoWriteBuffer &buffer) const { - buffer.encode_raw_short_string(10, this->object_id); + buffer.encode_short_string(10, this->object_id); buffer.write_tag_and_fixed32(21, this->key); - buffer.encode_raw_short_string(26, this->name); + buffer.encode_short_string(26, this->name); #ifdef USE_ENTITY_ICON buffer.encode_string(5, this->icon); #endif @@ -3122,9 +3122,9 @@ bool TextCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) { #endif #ifdef USE_DATETIME_DATE void ListEntitiesDateResponse::encode(ProtoWriteBuffer &buffer) const { - buffer.encode_raw_short_string(10, this->object_id); + buffer.encode_short_string(10, this->object_id); buffer.write_tag_and_fixed32(21, this->key); - buffer.encode_raw_short_string(26, this->name); + buffer.encode_short_string(26, this->name); #ifdef USE_ENTITY_ICON buffer.encode_string(5, this->icon); #endif @@ -3205,9 +3205,9 @@ bool DateCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) { #endif #ifdef USE_DATETIME_TIME void ListEntitiesTimeResponse::encode(ProtoWriteBuffer &buffer) const { - buffer.encode_raw_short_string(10, this->object_id); + buffer.encode_short_string(10, this->object_id); buffer.write_tag_and_fixed32(21, this->key); - buffer.encode_raw_short_string(26, this->name); + buffer.encode_short_string(26, this->name); #ifdef USE_ENTITY_ICON buffer.encode_string(5, this->icon); #endif @@ -3288,9 +3288,9 @@ bool TimeCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) { #endif #ifdef USE_EVENT void ListEntitiesEventResponse::encode(ProtoWriteBuffer &buffer) const { - buffer.encode_raw_short_string(10, this->object_id); + buffer.encode_short_string(10, this->object_id); buffer.write_tag_and_fixed32(21, this->key); - buffer.encode_raw_short_string(26, this->name); + buffer.encode_short_string(26, this->name); #ifdef USE_ENTITY_ICON buffer.encode_string(5, this->icon); #endif @@ -3344,9 +3344,9 @@ uint32_t EventResponse::calculate_size() const { #endif #ifdef USE_VALVE void ListEntitiesValveResponse::encode(ProtoWriteBuffer &buffer) const { - buffer.encode_raw_short_string(10, this->object_id); + buffer.encode_short_string(10, this->object_id); buffer.write_tag_and_fixed32(21, this->key); - buffer.encode_raw_short_string(26, this->name); + buffer.encode_short_string(26, this->name); #ifdef USE_ENTITY_ICON buffer.encode_string(5, this->icon); #endif @@ -3431,9 +3431,9 @@ bool ValveCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) { #endif #ifdef USE_DATETIME_DATETIME void ListEntitiesDateTimeResponse::encode(ProtoWriteBuffer &buffer) const { - buffer.encode_raw_short_string(10, this->object_id); + buffer.encode_short_string(10, this->object_id); buffer.write_tag_and_fixed32(21, this->key); - buffer.encode_raw_short_string(26, this->name); + buffer.encode_short_string(26, this->name); #ifdef USE_ENTITY_ICON buffer.encode_string(5, this->icon); #endif @@ -3504,9 +3504,9 @@ bool DateTimeCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) { #endif #ifdef USE_UPDATE void ListEntitiesUpdateResponse::encode(ProtoWriteBuffer &buffer) const { - buffer.encode_raw_short_string(10, this->object_id); + buffer.encode_short_string(10, this->object_id); buffer.write_tag_and_fixed32(21, this->key); - buffer.encode_raw_short_string(26, this->name); + buffer.encode_short_string(26, this->name); #ifdef USE_ENTITY_ICON buffer.encode_string(5, this->icon); #endif @@ -3645,9 +3645,9 @@ uint32_t ZWaveProxyRequest::calculate_size() const { #endif #ifdef USE_INFRARED void ListEntitiesInfraredResponse::encode(ProtoWriteBuffer &buffer) const { - buffer.encode_raw_short_string(10, this->object_id); + buffer.encode_short_string(10, this->object_id); buffer.write_tag_and_fixed32(21, this->key); - buffer.encode_raw_short_string(26, this->name); + buffer.encode_short_string(26, this->name); #ifdef USE_ENTITY_ICON buffer.encode_string(4, this->icon); #endif diff --git a/esphome/components/api/proto.h b/esphome/components/api/proto.h index 86f0085d32..6bcd801682 100644 --- a/esphome/components/api/proto.h +++ b/esphome/components/api/proto.h @@ -242,7 +242,7 @@ class ProtoWriteBuffer { } /// Write tag + 1-byte length + raw string data. For strings with max_data_length < 128. /// Tag must be a single-byte varint (< 128). Always encodes (no zero check). - inline void encode_raw_short_string(uint8_t tag, const StringRef &ref) ESPHOME_ALWAYS_INLINE { + inline void encode_short_string(uint8_t tag, const StringRef &ref) ESPHOME_ALWAYS_INLINE { this->debug_check_bounds_(2 + ref.size()); uint8_t *__restrict__ pos = this->pos_; *pos++ = tag; diff --git a/script/api_protobuf/api_protobuf.py b/script/api_protobuf/api_protobuf.py index 644ff85b96..589e5dc140 100755 --- a/script/api_protobuf/api_protobuf.py +++ b/script/api_protobuf/api_protobuf.py @@ -1078,9 +1078,7 @@ class PointerToStringBufferType(PointerToBufferTypeBase): if max_len is not None and max_len < 128 and self.force: tag = self.calculate_tag() if tag < 128: - return ( - f"buffer.encode_raw_short_string({tag}, this->{self.field_name});" - ) + return f"buffer.encode_short_string({tag}, this->{self.field_name});" if result := self._encode_bytes_with_precomputed_tag( f"this->{self.field_name}.c_str()", f"this->{self.field_name}.size()", From 4ce24389b761ba8fa2a398578be65ee27e6601fd Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 3 Apr 2026 14:06:00 -1000 Subject: [PATCH 07/11] [api] Add max_data_length = 47 for device_class fields --- esphome/components/api/api.proto | 27 ++++++++++++++------------- esphome/components/api/api_pb2.cpp | 20 ++++++++++---------- 2 files changed, 24 insertions(+), 23 deletions(-) diff --git a/esphome/components/api/api.proto b/esphome/components/api/api.proto index 278e977a73..56423ca231 100644 --- a/esphome/components/api/api.proto +++ b/esphome/components/api/api.proto @@ -308,9 +308,10 @@ enum EntityCategory { ENTITY_CATEGORY_DIAGNOSTIC = 2; } -// Entity name/object_id max_data_length = 120 matches NAME_MAX_LENGTH, -// icon max_data_length = 63 matches ICON_MAX_LENGTH. -// Both defined in esphome/core/config.py and validated at config time. +// Entity field max_data_length values match constants in esphome/core/config.py: +// name/object_id = 120 (NAME_MAX_LENGTH) +// icon = 63 (ICON_MAX_LENGTH) +// device_class = 47 (DEVICE_CLASS_MAX_LENGTH) // ==================== BINARY SENSOR ==================== message ListEntitiesBinarySensorResponse { @@ -324,7 +325,7 @@ message ListEntitiesBinarySensorResponse { string name = 3 [(max_data_length) = 120, (force) = true]; reserved 4; // Deprecated: was string unique_id - string device_class = 5; + string device_class = 5 [(max_data_length) = 47]; bool is_status_binary_sensor = 6; bool disabled_by_default = 7; string icon = 8 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63]; @@ -361,7 +362,7 @@ message ListEntitiesCoverResponse { bool assumed_state = 5; bool supports_position = 6; bool supports_tilt = 7; - string device_class = 8; + string device_class = 8 [(max_data_length) = 47]; bool disabled_by_default = 9; string icon = 10 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63]; EntityCategory entity_category = 11; @@ -639,7 +640,7 @@ message ListEntitiesSensorResponse { string unit_of_measurement = 6; int32 accuracy_decimals = 7; bool force_update = 8; - string device_class = 9; + string device_class = 9 [(max_data_length) = 47]; SensorStateClass state_class = 10; // Last reset type removed in 2021.9.0 // Deprecated in API version 1.5 @@ -679,7 +680,7 @@ message ListEntitiesSwitchResponse { bool assumed_state = 6; bool disabled_by_default = 7; EntityCategory entity_category = 8; - string device_class = 9; + string device_class = 9 [(max_data_length) = 47]; uint32 device_id = 10 [(field_ifdef) = "USE_DEVICES"]; } message SwitchStateResponse { @@ -720,7 +721,7 @@ message ListEntitiesTextSensorResponse { string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63]; bool disabled_by_default = 6; EntityCategory entity_category = 7; - string device_class = 8; + string device_class = 8 [(max_data_length) = 47]; uint32 device_id = 9 [(field_ifdef) = "USE_DEVICES"]; } message TextSensorStateResponse { @@ -1260,7 +1261,7 @@ message ListEntitiesNumberResponse { EntityCategory entity_category = 10; string unit_of_measurement = 11; NumberMode mode = 12; - string device_class = 13; + string device_class = 13 [(max_data_length) = 47]; uint32 device_id = 14 [(field_ifdef) = "USE_DEVICES"]; } message NumberStateResponse { @@ -1460,7 +1461,7 @@ message ListEntitiesButtonResponse { string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63]; bool disabled_by_default = 6; EntityCategory entity_category = 7; - string device_class = 8; + string device_class = 8 [(max_data_length) = 47]; uint32 device_id = 9 [(field_ifdef) = "USE_DEVICES"]; } message ButtonCommandRequest { @@ -2304,7 +2305,7 @@ message ListEntitiesEventResponse { string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63]; bool disabled_by_default = 6; EntityCategory entity_category = 7; - string device_class = 8; + string device_class = 8 [(max_data_length) = 47]; repeated string event_types = 9 [(container_pointer_no_template) = "FixedVector"]; uint32 device_id = 10 [(field_ifdef) = "USE_DEVICES"]; @@ -2335,7 +2336,7 @@ message ListEntitiesValveResponse { string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63]; bool disabled_by_default = 6; EntityCategory entity_category = 7; - string device_class = 8; + string device_class = 8 [(max_data_length) = 47]; bool assumed_state = 9; bool supports_position = 10; @@ -2433,7 +2434,7 @@ message ListEntitiesUpdateResponse { string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63]; bool disabled_by_default = 6; EntityCategory entity_category = 7; - string device_class = 8; + string device_class = 8 [(max_data_length) = 47]; uint32 device_id = 9 [(field_ifdef) = "USE_DEVICES"]; } message UpdateStateResponse { diff --git a/esphome/components/api/api_pb2.cpp b/esphome/components/api/api_pb2.cpp index 8dea47bb6b..d00a61b692 100644 --- a/esphome/components/api/api_pb2.cpp +++ b/esphome/components/api/api_pb2.cpp @@ -226,7 +226,7 @@ uint32_t ListEntitiesBinarySensorResponse::calculate_size() const { size += 2 + this->object_id.size(); size += 5; size += 2 + this->name.size(); - size += ProtoSize::calc_length(1, this->device_class.size()); + size += !this->device_class.empty() ? 2 + this->device_class.size() : 0; size += ProtoSize::calc_bool(1, this->is_status_binary_sensor); size += ProtoSize::calc_bool(1, this->disabled_by_default); #ifdef USE_ENTITY_ICON @@ -284,7 +284,7 @@ uint32_t ListEntitiesCoverResponse::calculate_size() const { size += ProtoSize::calc_bool(1, this->assumed_state); size += ProtoSize::calc_bool(1, this->supports_position); size += ProtoSize::calc_bool(1, this->supports_tilt); - size += ProtoSize::calc_length(1, this->device_class.size()); + size += !this->device_class.empty() ? 2 + this->device_class.size() : 0; size += ProtoSize::calc_bool(1, this->disabled_by_default); #ifdef USE_ENTITY_ICON size += !this->icon.empty() ? 2 + this->icon.size() : 0; @@ -710,7 +710,7 @@ uint32_t ListEntitiesSensorResponse::calculate_size() const { size += ProtoSize::calc_length(1, this->unit_of_measurement.size()); size += ProtoSize::calc_int32(1, this->accuracy_decimals); size += ProtoSize::calc_bool(1, this->force_update); - size += ProtoSize::calc_length(1, this->device_class.size()); + size += !this->device_class.empty() ? 2 + this->device_class.size() : 0; size += ProtoSize::calc_uint32(1, static_cast(this->state_class)); size += ProtoSize::calc_bool(1, this->disabled_by_default); size += ProtoSize::calc_uint32(1, static_cast(this->entity_category)); @@ -765,7 +765,7 @@ uint32_t ListEntitiesSwitchResponse::calculate_size() const { size += ProtoSize::calc_bool(1, this->assumed_state); size += ProtoSize::calc_bool(1, this->disabled_by_default); size += ProtoSize::calc_uint32(1, static_cast(this->entity_category)); - size += ProtoSize::calc_length(1, this->device_class.size()); + size += !this->device_class.empty() ? 2 + this->device_class.size() : 0; #ifdef USE_DEVICES size += ProtoSize::calc_uint32(1, this->device_id); #endif @@ -838,7 +838,7 @@ uint32_t ListEntitiesTextSensorResponse::calculate_size() const { #endif size += ProtoSize::calc_bool(1, this->disabled_by_default); size += ProtoSize::calc_uint32(1, static_cast(this->entity_category)); - size += ProtoSize::calc_length(1, this->device_class.size()); + size += !this->device_class.empty() ? 2 + this->device_class.size() : 0; #ifdef USE_DEVICES size += ProtoSize::calc_uint32(1, this->device_id); #endif @@ -1707,7 +1707,7 @@ uint32_t ListEntitiesNumberResponse::calculate_size() const { size += ProtoSize::calc_uint32(1, static_cast(this->entity_category)); size += ProtoSize::calc_length(1, this->unit_of_measurement.size()); size += ProtoSize::calc_uint32(1, static_cast(this->mode)); - size += ProtoSize::calc_length(1, this->device_class.size()); + size += !this->device_class.empty() ? 2 + this->device_class.size() : 0; #ifdef USE_DEVICES size += ProtoSize::calc_uint32(1, this->device_id); #endif @@ -2076,7 +2076,7 @@ uint32_t ListEntitiesButtonResponse::calculate_size() const { #endif size += ProtoSize::calc_bool(1, this->disabled_by_default); size += ProtoSize::calc_uint32(1, static_cast(this->entity_category)); - size += ProtoSize::calc_length(1, this->device_class.size()); + size += !this->device_class.empty() ? 2 + this->device_class.size() : 0; #ifdef USE_DEVICES size += ProtoSize::calc_uint32(1, this->device_id); #endif @@ -3314,7 +3314,7 @@ uint32_t ListEntitiesEventResponse::calculate_size() const { #endif size += ProtoSize::calc_bool(1, this->disabled_by_default); size += ProtoSize::calc_uint32(1, static_cast(this->entity_category)); - size += ProtoSize::calc_length(1, this->device_class.size()); + size += !this->device_class.empty() ? 2 + this->device_class.size() : 0; if (!this->event_types->empty()) { for (const char *it : *this->event_types) { size += ProtoSize::calc_length_force(1, strlen(it)); @@ -3370,7 +3370,7 @@ uint32_t ListEntitiesValveResponse::calculate_size() const { #endif size += ProtoSize::calc_bool(1, this->disabled_by_default); size += ProtoSize::calc_uint32(1, static_cast(this->entity_category)); - size += ProtoSize::calc_length(1, this->device_class.size()); + size += !this->device_class.empty() ? 2 + this->device_class.size() : 0; size += ProtoSize::calc_bool(1, this->assumed_state); size += ProtoSize::calc_bool(1, this->supports_position); size += ProtoSize::calc_bool(1, this->supports_stop); @@ -3527,7 +3527,7 @@ uint32_t ListEntitiesUpdateResponse::calculate_size() const { #endif size += ProtoSize::calc_bool(1, this->disabled_by_default); size += ProtoSize::calc_uint32(1, static_cast(this->entity_category)); - size += ProtoSize::calc_length(1, this->device_class.size()); + size += !this->device_class.empty() ? 2 + this->device_class.size() : 0; #ifdef USE_DEVICES size += ProtoSize::calc_uint32(1, this->device_id); #endif From f0622b76249991f3e4e22e6a0ceae5cf3c9331cc Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 3 Apr 2026 14:10:25 -1000 Subject: [PATCH 08/11] [api] Add max_data_length = 63 for unit_of_measurement fields Add UNIT_OF_MEASUREMENT_MAX_LENGTH = 63 constant in core/config.py and enforce it in sensor and number component validators. Annotate unit_of_measurement proto fields with (max_data_length) = 63 so the codegen emits constant-size length varint calculations. --- esphome/components/api/api.proto | 3 ++- esphome/components/api/api_pb2.cpp | 2 +- esphome/components/number/__init__.py | 7 ++++++- esphome/components/sensor/__init__.py | 7 ++++++- esphome/core/config.py | 3 +++ 5 files changed, 18 insertions(+), 4 deletions(-) diff --git a/esphome/components/api/api.proto b/esphome/components/api/api.proto index 56423ca231..f67595f3b5 100644 --- a/esphome/components/api/api.proto +++ b/esphome/components/api/api.proto @@ -312,6 +312,7 @@ enum EntityCategory { // name/object_id = 120 (NAME_MAX_LENGTH) // icon = 63 (ICON_MAX_LENGTH) // device_class = 47 (DEVICE_CLASS_MAX_LENGTH) +// unit_of_measurement = 63 (UNIT_OF_MEASUREMENT_MAX_LENGTH) // ==================== BINARY SENSOR ==================== message ListEntitiesBinarySensorResponse { @@ -637,7 +638,7 @@ message ListEntitiesSensorResponse { reserved 4; // Deprecated: was string unique_id string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63]; - string unit_of_measurement = 6; + string unit_of_measurement = 6 [(max_data_length) = 63]; int32 accuracy_decimals = 7; bool force_update = 8; string device_class = 9 [(max_data_length) = 47]; diff --git a/esphome/components/api/api_pb2.cpp b/esphome/components/api/api_pb2.cpp index d00a61b692..e4d35f62a6 100644 --- a/esphome/components/api/api_pb2.cpp +++ b/esphome/components/api/api_pb2.cpp @@ -707,7 +707,7 @@ uint32_t ListEntitiesSensorResponse::calculate_size() const { #ifdef USE_ENTITY_ICON size += !this->icon.empty() ? 2 + this->icon.size() : 0; #endif - size += ProtoSize::calc_length(1, this->unit_of_measurement.size()); + size += !this->unit_of_measurement.empty() ? 2 + this->unit_of_measurement.size() : 0; size += ProtoSize::calc_int32(1, this->accuracy_decimals); size += ProtoSize::calc_bool(1, this->force_update); size += !this->device_class.empty() ? 2 + this->device_class.size() : 0; diff --git a/esphome/components/number/__init__.py b/esphome/components/number/__init__.py index 90f9fe1835..26d2602ba4 100644 --- a/esphome/components/number/__init__.py +++ b/esphome/components/number/__init__.py @@ -79,6 +79,7 @@ from esphome.const import ( DEVICE_CLASS_WIND_SPEED, ) from esphome.core import CORE, CoroPriority, coroutine_with_priority +from esphome.core.config import UNIT_OF_MEASUREMENT_MAX_LENGTH from esphome.core.entity_helpers import ( entity_duplicate_validator, setup_device_class, @@ -186,7 +187,11 @@ NUMBER_OPERATION_OPTIONS = { } validate_device_class = cv.one_of(*DEVICE_CLASSES, lower=True, space="_") -validate_unit_of_measurement = cv.string_strict +validate_unit_of_measurement = cv.All( + cv.string_strict, + # Keep in sync with max_data_length in api.proto + cv.Length(max=UNIT_OF_MEASUREMENT_MAX_LENGTH), +) _NUMBER_SCHEMA = ( cv.ENTITY_BASE_SCHEMA.extend(web_server.WEBSERVER_SORTING_SCHEMA) diff --git a/esphome/components/sensor/__init__.py b/esphome/components/sensor/__init__.py index 626466eefa..275c4542fb 100644 --- a/esphome/components/sensor/__init__.py +++ b/esphome/components/sensor/__init__.py @@ -106,6 +106,7 @@ from esphome.const import ( ENTITY_CATEGORY_CONFIG, ) from esphome.core import CORE, CoroPriority, coroutine_with_priority +from esphome.core.config import UNIT_OF_MEASUREMENT_MAX_LENGTH from esphome.core.entity_helpers import ( entity_duplicate_validator, setup_device_class, @@ -290,7 +291,11 @@ ClampFilter = sensor_ns.class_("ClampFilter", Filter) RoundFilter = sensor_ns.class_("RoundFilter", Filter) RoundMultipleFilter = sensor_ns.class_("RoundMultipleFilter", Filter) -validate_unit_of_measurement = cv.string_strict +validate_unit_of_measurement = cv.All( + cv.string_strict, + # Keep in sync with max_data_length in api.proto + cv.Length(max=UNIT_OF_MEASUREMENT_MAX_LENGTH), +) validate_accuracy_decimals = cv.int_ validate_icon = cv.icon validate_device_class = cv.one_of(*DEVICE_CLASSES, lower=True, space="_") diff --git a/esphome/core/config.py b/esphome/core/config.py index c47693c783..675b9296e5 100644 --- a/esphome/core/config.py +++ b/esphome/core/config.py @@ -233,6 +233,9 @@ DEVICE_CLASS_MAX_LENGTH = 47 # Keep in sync with MAX_ICON_LENGTH in esphome/core/entity_base.h ICON_MAX_LENGTH = 63 +# Max unit of measurement string length +UNIT_OF_MEASUREMENT_MAX_LENGTH = 63 + AREA_SCHEMA = cv.Schema( { cv.GenerateID(CONF_ID): cv.declare_id(Area), From 5740d74d97acd85cfdbb0ac47d0ee933bd9c4e05 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 3 Apr 2026 14:11:51 -1000 Subject: [PATCH 09/11] [core] Enforce UNIT_OF_MEASUREMENT_MAX_LENGTH in register_unit_of_measurement --- esphome/core/entity_helpers.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/esphome/core/entity_helpers.py b/esphome/core/entity_helpers.py index 0589b92364..fc931c2baa 100644 --- a/esphome/core/entity_helpers.py +++ b/esphome/core/entity_helpers.py @@ -17,7 +17,11 @@ from esphome.const import ( CONF_UNIT_OF_MEASUREMENT, ) from esphome.core import CORE, ID, CoroPriority, coroutine_with_priority -from esphome.core.config import DEVICE_CLASS_MAX_LENGTH, ICON_MAX_LENGTH +from esphome.core.config import ( + DEVICE_CLASS_MAX_LENGTH, + ICON_MAX_LENGTH, + UNIT_OF_MEASUREMENT_MAX_LENGTH, +) from esphome.cpp_generator import MockObj, RawStatement, add, get_variable import esphome.final_validate as fv from esphome.helpers import cpp_string_escape, fnv1_hash_object_id, sanitize, snake_case @@ -200,6 +204,11 @@ def register_device_class(value: str) -> int: def register_unit_of_measurement(value: str) -> int: """Register a unit_of_measurement string and return its 1-based index.""" + if value and len(value) > UNIT_OF_MEASUREMENT_MAX_LENGTH: + raise ValueError( + f"Unit of measurement string too long ({len(value)} chars, " + f"max {UNIT_OF_MEASUREMENT_MAX_LENGTH}): '{value}'" + ) return _register_string(value, _get_pool().units, _MAX_UNITS, "unit_of_measurement") From dd08d7631e990b34569033909d7f891cbb2ae5bd Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 3 Apr 2026 14:13:03 -1000 Subject: [PATCH 10/11] [core] Add test for register_unit_of_measurement max length --- tests/unit_tests/core/test_entity_helpers.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/tests/unit_tests/core/test_entity_helpers.py b/tests/unit_tests/core/test_entity_helpers.py index d6cbb8c6be..e79ff850f9 100644 --- a/tests/unit_tests/core/test_entity_helpers.py +++ b/tests/unit_tests/core/test_entity_helpers.py @@ -28,6 +28,7 @@ from esphome.core.entity_helpers import ( get_base_entity_object_id, register_device_class, register_icon, + register_unit_of_measurement, setup_device_class, setup_entity, setup_unit_of_measurement, @@ -925,6 +926,22 @@ def test_register_device_class_max_length() -> None: assert register_device_class("") == 0 +def test_register_unit_of_measurement_max_length() -> None: + """Test register_unit_of_measurement rejects units exceeding 63 characters.""" + # 63 chars should succeed + max_uom = "a" * 63 + idx = register_unit_of_measurement(max_uom) + assert idx > 0 + + # 64 chars should fail + too_long = "a" * 64 + with pytest.raises(ValueError, match="Unit of measurement string too long"): + register_unit_of_measurement(too_long) + + # Empty string returns 0 + assert register_unit_of_measurement("") == 0 + + @pytest.mark.asyncio async def test_setup_entity_with_entity_category( setup_test_environment: list[str], From 80502e8c3959a9ad727a00feeb0f846ca9727cbd Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 3 Apr 2026 14:15:19 -1000 Subject: [PATCH 11/11] [api] Rename encode_short_string to write_short_string --- esphome/components/api/api_pb2.cpp | 100 ++++++++++++++-------------- esphome/components/api/proto.h | 2 +- script/api_protobuf/api_protobuf.py | 2 +- 3 files changed, 52 insertions(+), 52 deletions(-) diff --git a/esphome/components/api/api_pb2.cpp b/esphome/components/api/api_pb2.cpp index e4d35f62a6..8c51122a4c 100644 --- a/esphome/components/api/api_pb2.cpp +++ b/esphome/components/api/api_pb2.cpp @@ -207,9 +207,9 @@ uint32_t DeviceInfoResponse::calculate_size() const { } #ifdef USE_BINARY_SENSOR void ListEntitiesBinarySensorResponse::encode(ProtoWriteBuffer &buffer) const { - buffer.encode_short_string(10, this->object_id); + buffer.write_short_string(10, this->object_id); buffer.write_tag_and_fixed32(21, this->key); - buffer.encode_short_string(26, this->name); + buffer.write_short_string(26, this->name); buffer.encode_string(5, this->device_class); buffer.encode_bool(6, this->is_status_binary_sensor); buffer.encode_bool(7, this->disabled_by_default); @@ -259,9 +259,9 @@ uint32_t BinarySensorStateResponse::calculate_size() const { #endif #ifdef USE_COVER void ListEntitiesCoverResponse::encode(ProtoWriteBuffer &buffer) const { - buffer.encode_short_string(10, this->object_id); + buffer.write_short_string(10, this->object_id); buffer.write_tag_and_fixed32(21, this->key); - buffer.encode_short_string(26, this->name); + buffer.write_short_string(26, this->name); buffer.encode_bool(5, this->assumed_state); buffer.encode_bool(6, this->supports_position); buffer.encode_bool(7, this->supports_tilt); @@ -356,9 +356,9 @@ bool CoverCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) { #endif #ifdef USE_FAN void ListEntitiesFanResponse::encode(ProtoWriteBuffer &buffer) const { - buffer.encode_short_string(10, this->object_id); + buffer.write_short_string(10, this->object_id); buffer.write_tag_and_fixed32(21, this->key); - buffer.encode_short_string(26, this->name); + buffer.write_short_string(26, this->name); buffer.encode_bool(5, this->supports_oscillation); buffer.encode_bool(6, this->supports_speed); buffer.encode_bool(7, this->supports_direction); @@ -486,9 +486,9 @@ bool FanCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) { #endif #ifdef USE_LIGHT void ListEntitiesLightResponse::encode(ProtoWriteBuffer &buffer) const { - buffer.encode_short_string(10, this->object_id); + buffer.write_short_string(10, this->object_id); buffer.write_tag_and_fixed32(21, this->key); - buffer.encode_short_string(26, this->name); + buffer.write_short_string(26, this->name); for (const auto &it : *this->supported_color_modes) { buffer.encode_uint32(12, static_cast(it), true); } @@ -682,9 +682,9 @@ bool LightCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) { #endif #ifdef USE_SENSOR void ListEntitiesSensorResponse::encode(ProtoWriteBuffer &buffer) const { - buffer.encode_short_string(10, this->object_id); + buffer.write_short_string(10, this->object_id); buffer.write_tag_and_fixed32(21, this->key); - buffer.encode_short_string(26, this->name); + buffer.write_short_string(26, this->name); #ifdef USE_ENTITY_ICON buffer.encode_string(5, this->icon); #endif @@ -740,9 +740,9 @@ uint32_t SensorStateResponse::calculate_size() const { #endif #ifdef USE_SWITCH void ListEntitiesSwitchResponse::encode(ProtoWriteBuffer &buffer) const { - buffer.encode_short_string(10, this->object_id); + buffer.write_short_string(10, this->object_id); buffer.write_tag_and_fixed32(21, this->key); - buffer.encode_short_string(26, this->name); + buffer.write_short_string(26, this->name); #ifdef USE_ENTITY_ICON buffer.encode_string(5, this->icon); #endif @@ -815,9 +815,9 @@ bool SwitchCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) { #endif #ifdef USE_TEXT_SENSOR void ListEntitiesTextSensorResponse::encode(ProtoWriteBuffer &buffer) const { - buffer.encode_short_string(10, this->object_id); + buffer.write_short_string(10, this->object_id); buffer.write_tag_and_fixed32(21, this->key); - buffer.encode_short_string(26, this->name); + buffer.write_short_string(26, this->name); #ifdef USE_ENTITY_ICON buffer.encode_string(5, this->icon); #endif @@ -1268,9 +1268,9 @@ uint32_t ExecuteServiceResponse::calculate_size() const { #endif #ifdef USE_CAMERA void ListEntitiesCameraResponse::encode(ProtoWriteBuffer &buffer) const { - buffer.encode_short_string(10, this->object_id); + buffer.write_short_string(10, this->object_id); buffer.write_tag_and_fixed32(21, this->key); - buffer.encode_short_string(26, this->name); + buffer.write_short_string(26, this->name); buffer.encode_bool(5, this->disabled_by_default); #ifdef USE_ENTITY_ICON buffer.encode_string(6, this->icon); @@ -1329,9 +1329,9 @@ bool CameraImageRequest::decode_varint(uint32_t field_id, proto_varint_value_t v #endif #ifdef USE_CLIMATE void ListEntitiesClimateResponse::encode(ProtoWriteBuffer &buffer) const { - buffer.encode_short_string(10, this->object_id); + buffer.write_short_string(10, this->object_id); buffer.write_tag_and_fixed32(21, this->key); - buffer.encode_short_string(26, this->name); + buffer.write_short_string(26, this->name); buffer.encode_bool(5, this->supports_current_temperature); buffer.encode_bool(6, this->supports_two_point_target_temperature); for (const auto &it : *this->supported_modes) { @@ -1562,9 +1562,9 @@ bool ClimateCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) { #endif #ifdef USE_WATER_HEATER void ListEntitiesWaterHeaterResponse::encode(ProtoWriteBuffer &buffer) const { - buffer.encode_short_string(10, this->object_id); + buffer.write_short_string(10, this->object_id); buffer.write_tag_and_fixed32(21, this->key); - buffer.encode_short_string(26, this->name); + buffer.write_short_string(26, this->name); #ifdef USE_ENTITY_ICON buffer.encode_string(4, this->icon); #endif @@ -1674,9 +1674,9 @@ bool WaterHeaterCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value #endif #ifdef USE_NUMBER void ListEntitiesNumberResponse::encode(ProtoWriteBuffer &buffer) const { - buffer.encode_short_string(10, this->object_id); + buffer.write_short_string(10, this->object_id); buffer.write_tag_and_fixed32(21, this->key); - buffer.encode_short_string(26, this->name); + buffer.write_short_string(26, this->name); #ifdef USE_ENTITY_ICON buffer.encode_string(5, this->icon); #endif @@ -1759,9 +1759,9 @@ bool NumberCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) { #endif #ifdef USE_SELECT void ListEntitiesSelectResponse::encode(ProtoWriteBuffer &buffer) const { - buffer.encode_short_string(10, this->object_id); + buffer.write_short_string(10, this->object_id); buffer.write_tag_and_fixed32(21, this->key); - buffer.encode_short_string(26, this->name); + buffer.write_short_string(26, this->name); #ifdef USE_ENTITY_ICON buffer.encode_string(5, this->icon); #endif @@ -1848,9 +1848,9 @@ bool SelectCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) { #endif #ifdef USE_SIREN void ListEntitiesSirenResponse::encode(ProtoWriteBuffer &buffer) const { - buffer.encode_short_string(10, this->object_id); + buffer.write_short_string(10, this->object_id); buffer.write_tag_and_fixed32(21, this->key); - buffer.encode_short_string(26, this->name); + buffer.write_short_string(26, this->name); #ifdef USE_ENTITY_ICON buffer.encode_string(5, this->icon); #endif @@ -1960,9 +1960,9 @@ bool SirenCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) { #endif #ifdef USE_LOCK void ListEntitiesLockResponse::encode(ProtoWriteBuffer &buffer) const { - buffer.encode_short_string(10, this->object_id); + buffer.write_short_string(10, this->object_id); buffer.write_tag_and_fixed32(21, this->key); - buffer.encode_short_string(26, this->name); + buffer.write_short_string(26, this->name); #ifdef USE_ENTITY_ICON buffer.encode_string(5, this->icon); #endif @@ -2053,9 +2053,9 @@ bool LockCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) { #endif #ifdef USE_BUTTON void ListEntitiesButtonResponse::encode(ProtoWriteBuffer &buffer) const { - buffer.encode_short_string(10, this->object_id); + buffer.write_short_string(10, this->object_id); buffer.write_tag_and_fixed32(21, this->key); - buffer.encode_short_string(26, this->name); + buffer.write_short_string(26, this->name); #ifdef USE_ENTITY_ICON buffer.encode_string(5, this->icon); #endif @@ -2123,9 +2123,9 @@ uint32_t MediaPlayerSupportedFormat::calculate_size() const { return size; } void ListEntitiesMediaPlayerResponse::encode(ProtoWriteBuffer &buffer) const { - buffer.encode_short_string(10, this->object_id); + buffer.write_short_string(10, this->object_id); buffer.write_tag_and_fixed32(21, this->key); - buffer.encode_short_string(26, this->name); + buffer.write_short_string(26, this->name); #ifdef USE_ENTITY_ICON buffer.encode_string(5, this->icon); #endif @@ -2945,9 +2945,9 @@ bool VoiceAssistantSetConfiguration::decode_length(uint32_t field_id, ProtoLengt #endif #ifdef USE_ALARM_CONTROL_PANEL void ListEntitiesAlarmControlPanelResponse::encode(ProtoWriteBuffer &buffer) const { - buffer.encode_short_string(10, this->object_id); + buffer.write_short_string(10, this->object_id); buffer.write_tag_and_fixed32(21, this->key); - buffer.encode_short_string(26, this->name); + buffer.write_short_string(26, this->name); #ifdef USE_ENTITY_ICON buffer.encode_string(5, this->icon); #endif @@ -3033,9 +3033,9 @@ bool AlarmControlPanelCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit #endif #ifdef USE_TEXT void ListEntitiesTextResponse::encode(ProtoWriteBuffer &buffer) const { - buffer.encode_short_string(10, this->object_id); + buffer.write_short_string(10, this->object_id); buffer.write_tag_and_fixed32(21, this->key); - buffer.encode_short_string(26, this->name); + buffer.write_short_string(26, this->name); #ifdef USE_ENTITY_ICON buffer.encode_string(5, this->icon); #endif @@ -3122,9 +3122,9 @@ bool TextCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) { #endif #ifdef USE_DATETIME_DATE void ListEntitiesDateResponse::encode(ProtoWriteBuffer &buffer) const { - buffer.encode_short_string(10, this->object_id); + buffer.write_short_string(10, this->object_id); buffer.write_tag_and_fixed32(21, this->key); - buffer.encode_short_string(26, this->name); + buffer.write_short_string(26, this->name); #ifdef USE_ENTITY_ICON buffer.encode_string(5, this->icon); #endif @@ -3205,9 +3205,9 @@ bool DateCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) { #endif #ifdef USE_DATETIME_TIME void ListEntitiesTimeResponse::encode(ProtoWriteBuffer &buffer) const { - buffer.encode_short_string(10, this->object_id); + buffer.write_short_string(10, this->object_id); buffer.write_tag_and_fixed32(21, this->key); - buffer.encode_short_string(26, this->name); + buffer.write_short_string(26, this->name); #ifdef USE_ENTITY_ICON buffer.encode_string(5, this->icon); #endif @@ -3288,9 +3288,9 @@ bool TimeCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) { #endif #ifdef USE_EVENT void ListEntitiesEventResponse::encode(ProtoWriteBuffer &buffer) const { - buffer.encode_short_string(10, this->object_id); + buffer.write_short_string(10, this->object_id); buffer.write_tag_and_fixed32(21, this->key); - buffer.encode_short_string(26, this->name); + buffer.write_short_string(26, this->name); #ifdef USE_ENTITY_ICON buffer.encode_string(5, this->icon); #endif @@ -3344,9 +3344,9 @@ uint32_t EventResponse::calculate_size() const { #endif #ifdef USE_VALVE void ListEntitiesValveResponse::encode(ProtoWriteBuffer &buffer) const { - buffer.encode_short_string(10, this->object_id); + buffer.write_short_string(10, this->object_id); buffer.write_tag_and_fixed32(21, this->key); - buffer.encode_short_string(26, this->name); + buffer.write_short_string(26, this->name); #ifdef USE_ENTITY_ICON buffer.encode_string(5, this->icon); #endif @@ -3431,9 +3431,9 @@ bool ValveCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) { #endif #ifdef USE_DATETIME_DATETIME void ListEntitiesDateTimeResponse::encode(ProtoWriteBuffer &buffer) const { - buffer.encode_short_string(10, this->object_id); + buffer.write_short_string(10, this->object_id); buffer.write_tag_and_fixed32(21, this->key); - buffer.encode_short_string(26, this->name); + buffer.write_short_string(26, this->name); #ifdef USE_ENTITY_ICON buffer.encode_string(5, this->icon); #endif @@ -3504,9 +3504,9 @@ bool DateTimeCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) { #endif #ifdef USE_UPDATE void ListEntitiesUpdateResponse::encode(ProtoWriteBuffer &buffer) const { - buffer.encode_short_string(10, this->object_id); + buffer.write_short_string(10, this->object_id); buffer.write_tag_and_fixed32(21, this->key); - buffer.encode_short_string(26, this->name); + buffer.write_short_string(26, this->name); #ifdef USE_ENTITY_ICON buffer.encode_string(5, this->icon); #endif @@ -3645,9 +3645,9 @@ uint32_t ZWaveProxyRequest::calculate_size() const { #endif #ifdef USE_INFRARED void ListEntitiesInfraredResponse::encode(ProtoWriteBuffer &buffer) const { - buffer.encode_short_string(10, this->object_id); + buffer.write_short_string(10, this->object_id); buffer.write_tag_and_fixed32(21, this->key); - buffer.encode_short_string(26, this->name); + buffer.write_short_string(26, this->name); #ifdef USE_ENTITY_ICON buffer.encode_string(4, this->icon); #endif diff --git a/esphome/components/api/proto.h b/esphome/components/api/proto.h index 6bcd801682..33c8f80d1e 100644 --- a/esphome/components/api/proto.h +++ b/esphome/components/api/proto.h @@ -242,7 +242,7 @@ class ProtoWriteBuffer { } /// Write tag + 1-byte length + raw string data. For strings with max_data_length < 128. /// Tag must be a single-byte varint (< 128). Always encodes (no zero check). - inline void encode_short_string(uint8_t tag, const StringRef &ref) ESPHOME_ALWAYS_INLINE { + inline void write_short_string(uint8_t tag, const StringRef &ref) ESPHOME_ALWAYS_INLINE { this->debug_check_bounds_(2 + ref.size()); uint8_t *__restrict__ pos = this->pos_; *pos++ = tag; diff --git a/script/api_protobuf/api_protobuf.py b/script/api_protobuf/api_protobuf.py index 589e5dc140..6b8d63184c 100755 --- a/script/api_protobuf/api_protobuf.py +++ b/script/api_protobuf/api_protobuf.py @@ -1078,7 +1078,7 @@ class PointerToStringBufferType(PointerToBufferTypeBase): if max_len is not None and max_len < 128 and self.force: tag = self.calculate_tag() if tag < 128: - return f"buffer.encode_short_string({tag}, this->{self.field_name});" + return f"buffer.write_short_string({tag}, this->{self.field_name});" if result := self._encode_bytes_with_precomputed_tag( f"this->{self.field_name}.c_str()", f"this->{self.field_name}.size()",