Add cv.ByteLength() validator that checks UTF-8 byte length instead
of character count. This ensures multibyte characters don't cause
the encoded string to exceed the proto max_data_length limit.
Switch all length validators that feed into proto fields with
max_data_length annotations to use byte length:
- Entity names, friendly names, area names, device names
- Icons, device classes, units of measurement
- Board names, project name/version
- Fix HelloResponse.name and DeviceInfoResponse.name max_data_length
from 120 to 31 to match ESPHOME_DEVICE_NAME_MAX_LEN (hostname limit)
- Add max_data_length=120 + force to suggested_area field
- Add static_asserts for ESPHOME_DEVICE_NAME_MAX_LEN and
ESPHOME_FRIENDLY_NAME_MAX_LEN
- Fix comments to accurately describe guarantees
Add BOARD_MAX_LENGTH and PROJECT_MAX_LENGTH constants (127) and
enforce them in YAML validation for all platform board schemas
and project name/version.
Add max_data_length + force proto options to model, project_name,
and project_version fields in DeviceInfoResponse.
Add max_data_length + force proto options to string fields in
HelloResponse, AreaInfo, DeviceInfo, and DeviceInfoResponse where
we have strong compile-time or codegen guarantees on max length.
This enables encode_short_string_force() (direct byte writes,
no varint branching) for fields with single-byte tags, and
fixed-formula size calculations for all annotated fields.
Add static_asserts to cross-validate C++ constexpr constants
(MAC_ADDRESS_PRETTY_BUFFER_SIZE, BUILD_TIME_STR_SIZE,
ESPHOME_VERSION, ESPHOME_MANUFACTURER) against the proto
max_data_length annotations.
Move the notify_*_update() method definitions from controller_registry.cpp
into controller_registry.h as inline functions. These are tiny loops over
1-2 controllers that each have a single call site in the entity's
notify_frontend_() method.
When defined in a separate TU, the compiler cannot inline them at the call
site without LTO, resulting in an unnecessary function-call frame on every
state publish. CodSpeed profiling of the CallbackManager container change
(#15272) showed this extra frame accounting for ~12% regression in text
sensor publish benchmarks.
controller_registry.h is only ever included from .cpp files (never from
other headers), so including controller.h after the class definition is
safe and introduces no circular dependencies.
Add a max_data_length field option to api_options.proto for string/bytes
fields. When max_data_length < 128 and force = true, the code generator
uses encode_short_string_force() — a single call that writes the tag
byte, 1-byte length varint, and raw string data with no branching. Size
calculation simplifies from calc_length(1, size) to 2 + size.
Annotate entity fields across all 25 ListEntities*Response messages:
- name and object_id: max_data_length = 120, force = true (50 fields)
- icon: max_data_length = 63 (25 fields)
The 120 and 63 values match NAME_MAX_LENGTH and ICON_MAX_LENGTH in
esphome/core/config.py, validated at config time.
Scan all enum definitions in api.proto at codegen time and automatically
populate max_value for enum-typed fields. This eliminates the need for
manual (max_value) annotations on every enum field.
When max_value < 128, the generated calculate_size uses constant-size
arithmetic instead of a function call:
- `calc_uint32(1, static_cast<uint32_t>(field))` → `field ? 2 : 0`
For repeated enum fields with constant element size, the per-element
loop is replaced with a multiply: `size += count * bytes_per_element`.