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),