diff --git a/esphome/config_validation.py b/esphome/config_validation.py index 69f8ea9ef8..368b4f9f4a 100644 --- a/esphome/config_validation.py +++ b/esphome/config_validation.py @@ -398,6 +398,11 @@ def string_strict(value): ) +# Max icon string length (63 chars + null = 64-byte PROGMEM buffer) +# Keep in sync with MAX_ICON_LENGTH in esphome/core/entity_base.h +ICON_MAX_LENGTH = 63 + + def icon(value): """Validate that a given config value is a valid icon.""" value = string_strict(value) @@ -407,9 +412,9 @@ def icon(value): raise Invalid( 'Icons must match the format "[icon pack]:[icon]", e.g. "mdi:home-assistant"' ) - if len(value) > 63: + if len(value) > ICON_MAX_LENGTH: raise Invalid( - f"Icon string is too long ({len(value)} chars, max 63). " + f"Icon string is too long ({len(value)} chars, max {ICON_MAX_LENGTH}). " "Icons are stored in PROGMEM with a 64-byte buffer limit." ) return value diff --git a/esphome/core/entity_base.h b/esphome/core/entity_base.h index 6b6fe83834..a03d615c02 100644 --- a/esphome/core/entity_base.h +++ b/esphome/core/entity_base.h @@ -136,13 +136,13 @@ class EntityBase { #ifdef USE_ESP8266 // On ESP8266, rodata is RAM. Icons are in PROGMEM and cannot be accessed // directly as const char*. Use get_icon_to() with a stack buffer instead. - template StringRef get_icon_ref() const { - static_assert(!sizeof(T), + template StringRef get_icon_ref() const { + static_assert(sizeof(T) == 0, "get_icon_ref() unavailable on ESP8266 (rodata is RAM). Use get_icon_to() with a stack buffer."); return StringRef(""); } - template std::string get_icon() const { - static_assert(!sizeof(T), + template std::string get_icon() const { + static_assert(sizeof(T) == 0, "get_icon() unavailable on ESP8266 (rodata is RAM). Use get_icon_to() with a stack buffer."); return ""; } diff --git a/esphome/core/entity_helpers.py b/esphome/core/entity_helpers.py index c699ac3dda..a8ca2f7432 100644 --- a/esphome/core/entity_helpers.py +++ b/esphome/core/entity_helpers.py @@ -188,14 +188,11 @@ def register_unit_of_measurement(value: str) -> int: return _register_string(value, _get_pool().units, _MAX_UNITS, "unit_of_measurement") -_MAX_ICON_LENGTH = 63 # Max icon string length (64-byte buffer with null terminator) - - def register_icon(value: str) -> int: """Register an icon string and return its 1-based index.""" - if value and len(value) > _MAX_ICON_LENGTH: + if value and len(value) > cv.ICON_MAX_LENGTH: raise ValueError( - f"Icon string too long ({len(value)} chars, max {_MAX_ICON_LENGTH}): '{value}'" + f"Icon string too long ({len(value)} chars, max {cv.ICON_MAX_LENGTH}): '{value}'" ) return _register_string(value, _get_pool().icons, _MAX_ICONS, "icon")