Compare commits

...
Author SHA1 Message Date
J. Nick Koston c4c19a00e0 Move the entity domain and MQTT log conversions to the stacked PR 2026-10-12 17:27:40 -10:00
J. Nick Koston 522395de11 Read entity names flash-safely in web_server, MQTT and prometheus 2026-10-12 17:19:03 -10:00
J. Nick Koston 39d63a3166 Keep the entity domain log and dump helpers on the flash-safe name accessors 2026-10-12 17:10:33 -10:00
J. Nick Koston 36cfe5c785 Move the component conversions and the get_name() deprecation to a stacked PR 2026-10-12 17:02:29 -10:00
J. Nick Koston d8dd8cf36c Keep the flash object id hash in entity_base and read it with progmem_read_byte 2026-10-12 16:57:20 -10:00
J. Nick Koston 12c35c1545 Compute the object id hash when an external caller passes 0 2026-10-12 16:53:49 -10:00
J. Nick Koston 0e8a3dfb6f Fold dev's ProgmemStringRef struct into the class and share the progmem short string body 2026-10-12 16:53:49 -10:00
J. Nick Koston 00f8e89f28 Merge remote-tracking branch 'origin/dev' into esp8266-entity-names-flash 2026-10-12 16:49:42 -10:00
J. Nick Koston 5326d1dfe9 Merge remote-tracking branch 'origin/dev' into esp8266-entity-names-flash
# Conflicts:
#	esphome/components/web_server/web_server.cpp
2026-10-11 16:06:33 -10:00
J. Nick Koston ba9705e19b Construct the get_name() copy with placement new 2026-10-11 15:39:22 -10:00
J. Nick Koston 543a818307 Merge remote-tracking branch 'origin/dev' into esp8266-entity-names-flash
# Conflicts:
#	esphome/components/uart/button/uart_button.cpp
#	esphome/components/uart/switch/uart_switch.cpp
2026-10-11 15:36:07 -10:00
J. Nick Koston b31bedd464 Merge remote-tracking branch 'origin/dev' into HEAD
# Conflicts:
#	esphome/components/mqtt/mqtt_button.cpp
#	esphome/components/mqtt/mqtt_lock.cpp
#	esphome/components/mqtt/mqtt_switch.cpp
#	esphome/components/mqtt/mqtt_update.cpp
2026-10-09 18:28:52 -10:00
J. Nick Koston c8d331d44b Merge remote-tracking branch 'origin/dev' into esp8266-entity-names-flash
# Conflicts:
#	esphome/components/current_based/current_based_cover.cpp
2026-10-09 09:37:51 -10:00
J. Nick Koston 62f79e007a Merge remote-tracking branch 'origin/dev' into esp8266-entity-names-flash 2026-10-08 22:14:05 -10:00
J. Nick Koston b989221c71 Print prometheus names from flash, keep the inline short string encode off ESP8266 2026-10-08 22:07:33 -10:00
J. Nick Koston 096921cddb Keep get_name() working on ESP8266 with a RAM copy made on first use 2026-10-08 22:00:40 -10:00
J. Nick Koston e3d33df379 Share the outlined PROGMEM append for the progmem string dump 2026-10-08 21:44:09 -10:00
J. Nick Koston c263852df9 Dump progmem strings straight into the dump buffer 2026-10-08 21:34:34 -10:00
J. Nick Koston 7c58d26d13 [api] Add encode_progmem_string for progmem string fields
Generated code passes the ProgmemStringRef instead of spelling out the cast.
2026-10-08 20:17:55 -10:00
J. Nick Koston 88f7fc7454 Allow progmem on any string field and split the ProgmemStringRef tests
Long or unforced progmem strings encode through encode_progmem_bytes, which
API action strings need. The ProgmemStringRef tests move to their own file.
2026-10-08 20:04:18 -10:00
J. Nick Koston 140488723d Keep get_name() returning a reference off ESP8266
ProgmemStringRef now holds a StringRef, so get_name() and friendly_name_() can
keep returning const StringRef & on other platforms. External components that
return get_name() from a const StringRef & getter would otherwise return a
reference to a temporary. friendly_name_() is deprecated there, not removed.
2026-10-08 19:55:29 -10:00
J. Nick Koston b626711d50 Build the list entities benchmark with the ProgmemStringRef name field 2026-10-08 19:36:01 -10:00
pre-commit-ci-lite[bot] a6cb560e90 apply automatic formatting fixes 2026-10-09 04:36:31 +00:00
J. Nick Koston a721574706 Hash the RAM fallback name directly and share the short string encoder
Only unnamed entities hash at runtime, and their fallback name is in RAM, so
configure_entity_ hashes it before wrapping and named entities keep the codegen
hash; fnv1_hash_object_id_P is no longer needed. On ESP8266 the short string
encoder uses the outlined flash safe copy for RAM strings too.
2026-10-08 18:32:39 -10:00
J. Nick Koston 568934cc91 Drop the remaining entity name copies in mqtt, pid and prometheus
mqtt puts the name into the discovery JSON straight from flash, the pid
autotuner keeps the entity's log name instead of a std::string, and
prometheus prints names and ids without building a std::string per scrape.
2026-10-08 18:22:29 -10:00
J. Nick Koston 13057b7ae3 Type the API progmem name field as ProgmemStringRef
Progmem string fields must take the short encode path, the outlined encode
helper takes a ProgmemStringRef, and the hasher test checks the Python value.
2026-10-08 18:22:29 -10:00
J. Nick Koston b4289aee59 Add a flash safe fnv1_hash_object_id_P()
calc_object_id_() hashes the name in place instead of copying it into a
stack buffer first.
2026-10-08 18:11:36 -10:00
J. Nick Koston 93aecdd435 Deprecate Sprinkler::valve_name() and guard ProgmemStringRef::write_to
valve_name() returned a flash pointer on ESP8266; it now follows get_name():
static_assert there, deprecated elsewhere, with valve_log_name() for logs.
2026-10-08 17:58:37 -10:00
J. Nick Koston afac871938 [api] Encode entity names straight from flash
Extend the progmem proto option to string fields and mark the entity info
name field with it, so ListEntities copies the name once instead of through
a stack buffer.
2026-10-08 17:58:37 -10:00
J. Nick Koston 4c6e6b5447 Move flash copy and compare into ProgmemStringRef
ProgmemStringRef owns write_to() and equals() with the platform split, so the
EntityBase accessors are inline forwarders. Adds ESPHOME_ENTITY_NAME_MAX_LEN
mirroring NAME_MAX_LENGTH and tidies the codegen and tests.
2026-10-08 17:44:53 -10:00
J. Nick Koston b5c06efb2f Store entity names as ProgmemStringRef and deprecate its c_str()
ProgmemStringRef has no byte-reading operations, so new comparisons or copies
of name_ fail to compile. c_str() stays for external log-only users and is
deprecated until 2027.5.0; in-tree logs now use get_log_name().
2026-10-08 17:37:28 -10:00
J. Nick Koston 5cbdf26993 Tidy comments and always wrap entity names in ESPHOME_PSTR 2026-10-08 17:21:11 -10:00
J. Nick Koston 6f5108eb70 Apply review cleanups and deprecate get_name() off ESP8266
Inline get_name_to() off ESP8266, add write_name_to() and name_equals() so
web_server avoids copies, always emit ESPHOME_PSTR, and follow the get_icon()
pattern: get_name() is a static_assert stub on ESP8266 and ESPDEPRECATED
elsewhere until 2027.5.0.
2026-10-08 17:18:00 -10:00
J. Nick Koston 4ef86fe534 [core] Keep entity names in flash on ESP8266
On ESP8266 codegen emits entity names with PSTR and EntityBase::get_name()
is removed there. get_name_to() copies the name into a caller buffer and
get_log_name() returns it for %s log arguments; both exist on every platform.
2026-10-08 16:57:25 -10:00
29 changed files with 599 additions and 282 deletions
+26 -26
View File
@@ -552,7 +552,7 @@ message ListEntitiesBinarySensorResponse {
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 name = 3 [(max_data_length) = 120, (force) = true, (progmem) = true];
reserved 4; // Deprecated: was string unique_id
string device_class = 5 [(max_data_length) = 47];
@@ -586,7 +586,7 @@ message ListEntitiesCoverResponse {
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 name = 3 [(max_data_length) = 120, (force) = true, (progmem) = true];
reserved 4; // Deprecated: was string unique_id
bool assumed_state = 5;
@@ -670,7 +670,7 @@ message ListEntitiesFanResponse {
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 name = 3 [(max_data_length) = 120, (force) = true, (progmem) = true];
reserved 4; // Deprecated: was string unique_id
bool supports_oscillation = 5;
@@ -758,7 +758,7 @@ message ListEntitiesLightResponse {
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 name = 3 [(max_data_length) = 120, (force) = true, (progmem) = true];
reserved 4; // Deprecated: was string unique_id
repeated ColorMode supported_color_modes = 12 [(container_pointer_no_template) = "light::ColorModeMask"];
@@ -863,7 +863,7 @@ message ListEntitiesSensorResponse {
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 name = 3 [(max_data_length) = 120, (force) = true, (progmem) = true];
reserved 4; // Deprecated: was string unique_id
string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63];
@@ -904,7 +904,7 @@ message ListEntitiesSwitchResponse {
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 name = 3 [(max_data_length) = 120, (force) = true, (progmem) = true];
reserved 4; // Deprecated: was string unique_id
string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63];
@@ -949,7 +949,7 @@ message ListEntitiesTextSensorResponse {
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 name = 3 [(max_data_length) = 120, (force) = true, (progmem) = true];
reserved 4; // Deprecated: was string unique_id
string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63];
@@ -1220,7 +1220,7 @@ message ListEntitiesCameraResponse {
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 name = 3 [(max_data_length) = 120, (force) = true, (progmem) = true];
reserved 4; // Deprecated: was string unique_id
bool disabled_by_default = 5;
string icon = 6 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63];
@@ -1312,7 +1312,7 @@ message ListEntitiesClimateResponse {
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 name = 3 [(max_data_length) = 120, (force) = true, (progmem) = true];
reserved 4; // Deprecated: was string unique_id
bool supports_current_temperature = 5; // Deprecated: use feature_flags
@@ -1427,7 +1427,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 name = 3 [(max_data_length) = 120, (force) = true, (progmem) = true];
string icon = 4 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63];
bool disabled_by_default = 5;
EntityCategory entity_category = 6;
@@ -1507,7 +1507,7 @@ message ListEntitiesNumberResponse {
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 name = 3 [(max_data_length) = 120, (force) = true, (progmem) = true];
reserved 4; // Deprecated: was string unique_id
string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63];
@@ -1556,7 +1556,7 @@ message ListEntitiesSelectResponse {
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 name = 3 [(max_data_length) = 120, (force) = true, (progmem) = true];
reserved 4; // Deprecated: was string unique_id
string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63];
@@ -1600,7 +1600,7 @@ message ListEntitiesSirenResponse {
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 name = 3 [(max_data_length) = 120, (force) = true, (progmem) = true];
reserved 4; // Deprecated: was string unique_id
string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63];
@@ -1665,7 +1665,7 @@ message ListEntitiesLockResponse {
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 name = 3 [(max_data_length) = 120, (force) = true, (progmem) = true];
reserved 4; // Deprecated: was string unique_id
string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63];
@@ -1714,7 +1714,7 @@ message ListEntitiesButtonResponse {
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 name = 3 [(max_data_length) = 120, (force) = true, (progmem) = true];
reserved 4; // Deprecated: was string unique_id
string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63];
@@ -1781,7 +1781,7 @@ message ListEntitiesMediaPlayerResponse {
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 name = 3 [(max_data_length) = 120, (force) = true, (progmem) = true];
reserved 4; // Deprecated: was string unique_id
string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63];
@@ -2373,7 +2373,7 @@ message ListEntitiesAlarmControlPanelResponse {
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 name = 3 [(max_data_length) = 120, (force) = true, (progmem) = true];
reserved 4; // Deprecated: was string unique_id
string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63];
bool disabled_by_default = 6;
@@ -2420,7 +2420,7 @@ message ListEntitiesTextResponse {
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 name = 3 [(max_data_length) = 120, (force) = true, (progmem) = true];
reserved 4; // Deprecated: was string unique_id
string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63];
bool disabled_by_default = 6;
@@ -2468,7 +2468,7 @@ message ListEntitiesDateResponse {
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 name = 3 [(max_data_length) = 120, (force) = true, (progmem) = true];
reserved 4; // Deprecated: was string unique_id
string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63];
@@ -2515,7 +2515,7 @@ message ListEntitiesTimeResponse {
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 name = 3 [(max_data_length) = 120, (force) = true, (progmem) = true];
reserved 4; // Deprecated: was string unique_id
string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63];
@@ -2562,7 +2562,7 @@ message ListEntitiesEventResponse {
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 name = 3 [(max_data_length) = 120, (force) = true, (progmem) = true];
reserved 4; // Deprecated: was string unique_id
string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63];
@@ -2593,7 +2593,7 @@ message ListEntitiesValveResponse {
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 name = 3 [(max_data_length) = 120, (force) = true, (progmem) = true];
reserved 4; // Deprecated: was string unique_id
string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63];
@@ -2648,7 +2648,7 @@ message ListEntitiesDateTimeResponse {
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 name = 3 [(max_data_length) = 120, (force) = true, (progmem) = true];
reserved 4; // Deprecated: was string unique_id
string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63];
@@ -2691,7 +2691,7 @@ message ListEntitiesUpdateResponse {
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 name = 3 [(max_data_length) = 120, (force) = true, (progmem) = true];
reserved 4; // Deprecated: was string unique_id
string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63];
@@ -2791,7 +2791,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 name = 3 [(max_data_length) = 120, (force) = true, (progmem) = true];
string icon = 4 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63];
bool disabled_by_default = 5;
EntityCategory entity_category = 6;
@@ -2860,7 +2860,7 @@ message ListEntitiesRadioFrequencyResponse {
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 name = 3 [(max_data_length) = 120, (force) = true, (progmem) = true];
string icon = 4 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63];
bool disabled_by_default = 5;
EntityCategory entity_category = 6;
+1 -1
View File
@@ -474,7 +474,7 @@ uint16_t APIConnection::fill_and_encode_entity_info(EntityBase *entity, InfoResp
msg.key = entity->get_object_id_hash();
if (entity->has_own_name()) {
msg.name = entity->get_name();
msg.name = entity->get_name_progmem();
}
// Set common EntityBase properties
+1 -1
View File
@@ -123,7 +123,7 @@ extend google.protobuf.FieldOptions {
// from an absent one (e.g. a UTC ParsedTimezone, which is all zeros).
optional bool track_presence = 50020 [default=false];
// progmem: The pointer_to_buffer bytes field points into flash (PROGMEM).
// progmem: The string field, or pointer_to_buffer bytes field, points into flash (PROGMEM).
// The generated encode and dump copy it with progmem_memcpy, as ESP8266
// cannot read flash with ordinary loads.
optional bool progmem = 50021 [default=false];
+26 -26
View File
@@ -401,7 +401,7 @@ uint8_t *ListEntitiesBinarySensorResponse::encode_msg(const void *self,
const auto &msg = *static_cast<const ListEntitiesBinarySensorResponse *>(self);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 10, msg.object_id);
pos = ProtoEncode::write_tag_and_fixed32(pos PROTO_ENCODE_DEBUG_ARG, 21, msg.key);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
pos = ProtoEncode::encode_short_progmem_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
pos = ProtoEncode::encode_string(pos PROTO_ENCODE_DEBUG_ARG, 5, msg.device_class);
pos = ProtoEncode::encode_bool(pos PROTO_ENCODE_DEBUG_ARG, 6, msg.is_status_binary_sensor);
pos = ProtoEncode::encode_bool(pos PROTO_ENCODE_DEBUG_ARG, 7, msg.disabled_by_default);
@@ -459,7 +459,7 @@ uint8_t *ListEntitiesCoverResponse::encode_msg(const void *self, uint8_t *__rest
const auto &msg = *static_cast<const ListEntitiesCoverResponse *>(self);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 10, msg.object_id);
pos = ProtoEncode::write_tag_and_fixed32(pos PROTO_ENCODE_DEBUG_ARG, 21, msg.key);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
pos = ProtoEncode::encode_short_progmem_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
pos = ProtoEncode::encode_bool(pos PROTO_ENCODE_DEBUG_ARG, 5, msg.assumed_state);
pos = ProtoEncode::encode_bool(pos PROTO_ENCODE_DEBUG_ARG, 6, msg.supports_position);
pos = ProtoEncode::encode_bool(pos PROTO_ENCODE_DEBUG_ARG, 7, msg.supports_tilt);
@@ -558,7 +558,7 @@ uint8_t *ListEntitiesFanResponse::encode_msg(const void *self, uint8_t *__restri
const auto &msg = *static_cast<const ListEntitiesFanResponse *>(self);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 10, msg.object_id);
pos = ProtoEncode::write_tag_and_fixed32(pos PROTO_ENCODE_DEBUG_ARG, 21, msg.key);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
pos = ProtoEncode::encode_short_progmem_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
pos = ProtoEncode::encode_bool(pos PROTO_ENCODE_DEBUG_ARG, 5, msg.supports_oscillation);
pos = ProtoEncode::encode_bool(pos PROTO_ENCODE_DEBUG_ARG, 6, msg.supports_speed);
pos = ProtoEncode::encode_bool(pos PROTO_ENCODE_DEBUG_ARG, 7, msg.supports_direction);
@@ -678,7 +678,7 @@ uint8_t *ListEntitiesLightResponse::encode_msg(const void *self, uint8_t *__rest
const auto &msg = *static_cast<const ListEntitiesLightResponse *>(self);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 10, msg.object_id);
pos = ProtoEncode::write_tag_and_fixed32(pos PROTO_ENCODE_DEBUG_ARG, 21, msg.key);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
pos = ProtoEncode::encode_short_progmem_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
for (const auto &it : *msg.supported_color_modes) {
pos = ProtoEncode::encode_uint32_force(pos PROTO_ENCODE_DEBUG_ARG, 12, static_cast<uint32_t>(it));
}
@@ -884,7 +884,7 @@ uint8_t *ListEntitiesSensorResponse::encode_msg(const void *self, uint8_t *__res
const auto &msg = *static_cast<const ListEntitiesSensorResponse *>(self);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 10, msg.object_id);
pos = ProtoEncode::write_tag_and_fixed32(pos PROTO_ENCODE_DEBUG_ARG, 21, msg.key);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
pos = ProtoEncode::encode_short_progmem_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
#ifdef USE_ENTITY_ICON
pos = ProtoEncode::encode_string(pos PROTO_ENCODE_DEBUG_ARG, 5, msg.icon);
#endif
@@ -954,7 +954,7 @@ uint8_t *ListEntitiesSwitchResponse::encode_msg(const void *self, uint8_t *__res
const auto &msg = *static_cast<const ListEntitiesSwitchResponse *>(self);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 10, msg.object_id);
pos = ProtoEncode::write_tag_and_fixed32(pos PROTO_ENCODE_DEBUG_ARG, 21, msg.key);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
pos = ProtoEncode::encode_short_progmem_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
#ifdef USE_ENTITY_ICON
pos = ProtoEncode::encode_string(pos PROTO_ENCODE_DEBUG_ARG, 5, msg.icon);
#endif
@@ -1030,7 +1030,7 @@ uint8_t *ListEntitiesTextSensorResponse::encode_msg(const void *self,
const auto &msg = *static_cast<const ListEntitiesTextSensorResponse *>(self);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 10, msg.object_id);
pos = ProtoEncode::write_tag_and_fixed32(pos PROTO_ENCODE_DEBUG_ARG, 21, msg.key);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
pos = ProtoEncode::encode_short_progmem_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
#ifdef USE_ENTITY_ICON
pos = ProtoEncode::encode_string(pos PROTO_ENCODE_DEBUG_ARG, 5, msg.icon);
#endif
@@ -1478,7 +1478,7 @@ uint8_t *ListEntitiesCameraResponse::encode_msg(const void *self, uint8_t *__res
const auto &msg = *static_cast<const ListEntitiesCameraResponse *>(self);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 10, msg.object_id);
pos = ProtoEncode::write_tag_and_fixed32(pos PROTO_ENCODE_DEBUG_ARG, 21, msg.key);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
pos = ProtoEncode::encode_short_progmem_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
pos = ProtoEncode::encode_bool(pos PROTO_ENCODE_DEBUG_ARG, 5, msg.disabled_by_default);
#ifdef USE_ENTITY_ICON
pos = ProtoEncode::encode_string(pos PROTO_ENCODE_DEBUG_ARG, 6, msg.icon);
@@ -1544,7 +1544,7 @@ uint8_t *ListEntitiesClimateResponse::encode_msg(const void *self, uint8_t *__re
const auto &msg = *static_cast<const ListEntitiesClimateResponse *>(self);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 10, msg.object_id);
pos = ProtoEncode::write_tag_and_fixed32(pos PROTO_ENCODE_DEBUG_ARG, 21, msg.key);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
pos = ProtoEncode::encode_short_progmem_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
pos = ProtoEncode::encode_bool(pos PROTO_ENCODE_DEBUG_ARG, 5, msg.supports_current_temperature);
pos = ProtoEncode::encode_bool(pos PROTO_ENCODE_DEBUG_ARG, 6, msg.supports_two_point_target_temperature);
for (const auto &it : *msg.supported_modes) {
@@ -1781,7 +1781,7 @@ uint8_t *ListEntitiesWaterHeaterResponse::encode_msg(const void *self,
const auto &msg = *static_cast<const ListEntitiesWaterHeaterResponse *>(self);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 10, msg.object_id);
pos = ProtoEncode::write_tag_and_fixed32(pos PROTO_ENCODE_DEBUG_ARG, 21, msg.key);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
pos = ProtoEncode::encode_short_progmem_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
#ifdef USE_ENTITY_ICON
pos = ProtoEncode::encode_string(pos PROTO_ENCODE_DEBUG_ARG, 4, msg.icon);
#endif
@@ -1908,7 +1908,7 @@ uint8_t *ListEntitiesNumberResponse::encode_msg(const void *self, uint8_t *__res
const auto &msg = *static_cast<const ListEntitiesNumberResponse *>(self);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 10, msg.object_id);
pos = ProtoEncode::write_tag_and_fixed32(pos PROTO_ENCODE_DEBUG_ARG, 21, msg.key);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
pos = ProtoEncode::encode_short_progmem_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
#ifdef USE_ENTITY_ICON
pos = ProtoEncode::encode_string(pos PROTO_ENCODE_DEBUG_ARG, 5, msg.icon);
#endif
@@ -1999,7 +1999,7 @@ uint8_t *ListEntitiesSelectResponse::encode_msg(const void *self, uint8_t *__res
const auto &msg = *static_cast<const ListEntitiesSelectResponse *>(self);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 10, msg.object_id);
pos = ProtoEncode::write_tag_and_fixed32(pos PROTO_ENCODE_DEBUG_ARG, 21, msg.key);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
pos = ProtoEncode::encode_short_progmem_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
#ifdef USE_ENTITY_ICON
pos = ProtoEncode::encode_string(pos PROTO_ENCODE_DEBUG_ARG, 5, msg.icon);
#endif
@@ -2078,7 +2078,7 @@ uint8_t *ListEntitiesSirenResponse::encode_msg(const void *self, uint8_t *__rest
const auto &msg = *static_cast<const ListEntitiesSirenResponse *>(self);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 10, msg.object_id);
pos = ProtoEncode::write_tag_and_fixed32(pos PROTO_ENCODE_DEBUG_ARG, 21, msg.key);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
pos = ProtoEncode::encode_short_progmem_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
#ifdef USE_ENTITY_ICON
pos = ProtoEncode::encode_string(pos PROTO_ENCODE_DEBUG_ARG, 5, msg.icon);
#endif
@@ -2180,7 +2180,7 @@ uint8_t *ListEntitiesLockResponse::encode_msg(const void *self, uint8_t *__restr
const auto &msg = *static_cast<const ListEntitiesLockResponse *>(self);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 10, msg.object_id);
pos = ProtoEncode::write_tag_and_fixed32(pos PROTO_ENCODE_DEBUG_ARG, 21, msg.key);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
pos = ProtoEncode::encode_short_progmem_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
#ifdef USE_ENTITY_ICON
pos = ProtoEncode::encode_string(pos PROTO_ENCODE_DEBUG_ARG, 5, msg.icon);
#endif
@@ -2263,7 +2263,7 @@ uint8_t *ListEntitiesButtonResponse::encode_msg(const void *self, uint8_t *__res
const auto &msg = *static_cast<const ListEntitiesButtonResponse *>(self);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 10, msg.object_id);
pos = ProtoEncode::write_tag_and_fixed32(pos PROTO_ENCODE_DEBUG_ARG, 21, msg.key);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
pos = ProtoEncode::encode_short_progmem_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
#ifdef USE_ENTITY_ICON
pos = ProtoEncode::encode_string(pos PROTO_ENCODE_DEBUG_ARG, 5, msg.icon);
#endif
@@ -2332,7 +2332,7 @@ uint8_t *ListEntitiesMediaPlayerResponse::encode_msg(const void *self,
const auto &msg = *static_cast<const ListEntitiesMediaPlayerResponse *>(self);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 10, msg.object_id);
pos = ProtoEncode::write_tag_and_fixed32(pos PROTO_ENCODE_DEBUG_ARG, 21, msg.key);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
pos = ProtoEncode::encode_short_progmem_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
#ifdef USE_ENTITY_ICON
pos = ProtoEncode::encode_string(pos PROTO_ENCODE_DEBUG_ARG, 5, msg.icon);
#endif
@@ -3178,7 +3178,7 @@ uint8_t *ListEntitiesAlarmControlPanelResponse::encode_msg(const void *self,
const auto &msg = *static_cast<const ListEntitiesAlarmControlPanelResponse *>(self);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 10, msg.object_id);
pos = ProtoEncode::write_tag_and_fixed32(pos PROTO_ENCODE_DEBUG_ARG, 21, msg.key);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
pos = ProtoEncode::encode_short_progmem_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
#ifdef USE_ENTITY_ICON
pos = ProtoEncode::encode_string(pos PROTO_ENCODE_DEBUG_ARG, 5, msg.icon);
#endif
@@ -3258,7 +3258,7 @@ uint8_t *ListEntitiesTextResponse::encode_msg(const void *self, uint8_t *__restr
const auto &msg = *static_cast<const ListEntitiesTextResponse *>(self);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 10, msg.object_id);
pos = ProtoEncode::write_tag_and_fixed32(pos PROTO_ENCODE_DEBUG_ARG, 21, msg.key);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
pos = ProtoEncode::encode_short_progmem_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
#ifdef USE_ENTITY_ICON
pos = ProtoEncode::encode_string(pos PROTO_ENCODE_DEBUG_ARG, 5, msg.icon);
#endif
@@ -3337,7 +3337,7 @@ uint8_t *ListEntitiesDateResponse::encode_msg(const void *self, uint8_t *__restr
const auto &msg = *static_cast<const ListEntitiesDateResponse *>(self);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 10, msg.object_id);
pos = ProtoEncode::write_tag_and_fixed32(pos PROTO_ENCODE_DEBUG_ARG, 21, msg.key);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
pos = ProtoEncode::encode_short_progmem_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
#ifdef USE_ENTITY_ICON
pos = ProtoEncode::encode_string(pos PROTO_ENCODE_DEBUG_ARG, 5, msg.icon);
#endif
@@ -3418,7 +3418,7 @@ uint8_t *ListEntitiesTimeResponse::encode_msg(const void *self, uint8_t *__restr
const auto &msg = *static_cast<const ListEntitiesTimeResponse *>(self);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 10, msg.object_id);
pos = ProtoEncode::write_tag_and_fixed32(pos PROTO_ENCODE_DEBUG_ARG, 21, msg.key);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
pos = ProtoEncode::encode_short_progmem_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
#ifdef USE_ENTITY_ICON
pos = ProtoEncode::encode_string(pos PROTO_ENCODE_DEBUG_ARG, 5, msg.icon);
#endif
@@ -3499,7 +3499,7 @@ uint8_t *ListEntitiesEventResponse::encode_msg(const void *self, uint8_t *__rest
const auto &msg = *static_cast<const ListEntitiesEventResponse *>(self);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 10, msg.object_id);
pos = ProtoEncode::write_tag_and_fixed32(pos PROTO_ENCODE_DEBUG_ARG, 21, msg.key);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
pos = ProtoEncode::encode_short_progmem_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
#ifdef USE_ENTITY_ICON
pos = ProtoEncode::encode_string(pos PROTO_ENCODE_DEBUG_ARG, 5, msg.icon);
#endif
@@ -3561,7 +3561,7 @@ uint8_t *ListEntitiesValveResponse::encode_msg(const void *self, uint8_t *__rest
const auto &msg = *static_cast<const ListEntitiesValveResponse *>(self);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 10, msg.object_id);
pos = ProtoEncode::write_tag_and_fixed32(pos PROTO_ENCODE_DEBUG_ARG, 21, msg.key);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
pos = ProtoEncode::encode_short_progmem_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
#ifdef USE_ENTITY_ICON
pos = ProtoEncode::encode_string(pos PROTO_ENCODE_DEBUG_ARG, 5, msg.icon);
#endif
@@ -3649,7 +3649,7 @@ uint8_t *ListEntitiesDateTimeResponse::encode_msg(const void *self,
const auto &msg = *static_cast<const ListEntitiesDateTimeResponse *>(self);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 10, msg.object_id);
pos = ProtoEncode::write_tag_and_fixed32(pos PROTO_ENCODE_DEBUG_ARG, 21, msg.key);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
pos = ProtoEncode::encode_short_progmem_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
#ifdef USE_ENTITY_ICON
pos = ProtoEncode::encode_string(pos PROTO_ENCODE_DEBUG_ARG, 5, msg.icon);
#endif
@@ -3722,7 +3722,7 @@ uint8_t *ListEntitiesUpdateResponse::encode_msg(const void *self, uint8_t *__res
const auto &msg = *static_cast<const ListEntitiesUpdateResponse *>(self);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 10, msg.object_id);
pos = ProtoEncode::write_tag_and_fixed32(pos PROTO_ENCODE_DEBUG_ARG, 21, msg.key);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
pos = ProtoEncode::encode_short_progmem_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
#ifdef USE_ENTITY_ICON
pos = ProtoEncode::encode_string(pos PROTO_ENCODE_DEBUG_ARG, 5, msg.icon);
#endif
@@ -3878,7 +3878,7 @@ uint8_t *ListEntitiesInfraredResponse::encode_msg(const void *self,
const auto &msg = *static_cast<const ListEntitiesInfraredResponse *>(self);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 10, msg.object_id);
pos = ProtoEncode::write_tag_and_fixed32(pos PROTO_ENCODE_DEBUG_ARG, 21, msg.key);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
pos = ProtoEncode::encode_short_progmem_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
#ifdef USE_ENTITY_ICON
pos = ProtoEncode::encode_string(pos PROTO_ENCODE_DEBUG_ARG, 4, msg.icon);
#endif
@@ -3996,7 +3996,7 @@ uint8_t *ListEntitiesRadioFrequencyResponse::encode_msg(const void *self,
const auto &msg = *static_cast<const ListEntitiesRadioFrequencyResponse *>(self);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 10, msg.object_id);
pos = ProtoEncode::write_tag_and_fixed32(pos PROTO_ENCODE_DEBUG_ARG, 21, msg.key);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
pos = ProtoEncode::encode_short_progmem_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
#ifdef USE_ENTITY_ICON
pos = ProtoEncode::encode_string(pos PROTO_ENCODE_DEBUG_ARG, 4, msg.icon);
#endif
+1 -1
View File
@@ -399,7 +399,7 @@ class InfoResponseProtoMessage : public ProtoMessage {
public:
StringRef object_id{};
uint32_t key{0};
StringRef name{};
ProgmemStringRef name{};
bool disabled_by_default{false};
#ifdef USE_ENTITY_ICON
StringRef icon{nullptr, 0}; // null until set, encode only
+36 -28
View File
@@ -12,8 +12,9 @@ namespace esphome::api {
#ifdef USE_ESP8266
// Out-of-line to avoid inlining strlen_P/memcpy_P at every call site
void DumpBuffer::append_p_esp8266(const char *str) {
size_t len = strlen_P(str);
void DumpBuffer::append_p_esp8266(const char *str) { this->append_p_esp8266(str, strlen_P(str)); }
void DumpBuffer::append_p_esp8266(const char *str, size_t len) {
size_t space = CAPACITY - 1 - pos_;
if (len > space)
len = space;
@@ -134,6 +135,13 @@ static void dump_progmem_bytes_field(DumpBuffer &out, const char *field_name, co
progmem_memcpy(data_buf, data, len);
dump_bytes_field(out, field_name, data_buf, len, indent);
}
// Helper for string fields in flash
static void dump_progmem_string_field(DumpBuffer &out, const char *field_name, const ProgmemStringRef &value,
int indent = 2) {
append_field_prefix(out, field_name, indent);
out.append("'").append_p(value.progmem_ptr(), value.size()).append("'\n");
}
#pragma GCC diagnostic pop
template<> const char *proto_enum_to_string<enums::DisconnectReason>(enums::DisconnectReason value) {
@@ -1182,7 +1190,7 @@ const char *ListEntitiesBinarySensorResponse::dump_to(DumpBuffer &out) const {
MessageDumpHelper helper(out, ESPHOME_PSTR("ListEntitiesBinarySensorResponse"));
dump_field(out, ESPHOME_PSTR("object_id"), this->object_id);
dump_field(out, ESPHOME_PSTR("key"), this->key);
dump_field(out, ESPHOME_PSTR("name"), this->name);
dump_progmem_string_field(out, ESPHOME_PSTR("name"), this->name);
dump_field(out, ESPHOME_PSTR("device_class"), this->device_class);
dump_field(out, ESPHOME_PSTR("is_status_binary_sensor"), this->is_status_binary_sensor);
dump_field(out, ESPHOME_PSTR("disabled_by_default"), this->disabled_by_default);
@@ -1211,7 +1219,7 @@ const char *ListEntitiesCoverResponse::dump_to(DumpBuffer &out) const {
MessageDumpHelper helper(out, ESPHOME_PSTR("ListEntitiesCoverResponse"));
dump_field(out, ESPHOME_PSTR("object_id"), this->object_id);
dump_field(out, ESPHOME_PSTR("key"), this->key);
dump_field(out, ESPHOME_PSTR("name"), this->name);
dump_progmem_string_field(out, ESPHOME_PSTR("name"), this->name);
dump_field(out, ESPHOME_PSTR("assumed_state"), this->assumed_state);
dump_field(out, ESPHOME_PSTR("supports_position"), this->supports_position);
dump_field(out, ESPHOME_PSTR("supports_tilt"), this->supports_tilt);
@@ -1257,7 +1265,7 @@ const char *ListEntitiesFanResponse::dump_to(DumpBuffer &out) const {
MessageDumpHelper helper(out, ESPHOME_PSTR("ListEntitiesFanResponse"));
dump_field(out, ESPHOME_PSTR("object_id"), this->object_id);
dump_field(out, ESPHOME_PSTR("key"), this->key);
dump_field(out, ESPHOME_PSTR("name"), this->name);
dump_progmem_string_field(out, ESPHOME_PSTR("name"), this->name);
dump_field(out, ESPHOME_PSTR("supports_oscillation"), this->supports_oscillation);
dump_field(out, ESPHOME_PSTR("supports_speed"), this->supports_speed);
dump_field(out, ESPHOME_PSTR("supports_direction"), this->supports_direction);
@@ -1312,7 +1320,7 @@ const char *ListEntitiesLightResponse::dump_to(DumpBuffer &out) const {
MessageDumpHelper helper(out, ESPHOME_PSTR("ListEntitiesLightResponse"));
dump_field(out, ESPHOME_PSTR("object_id"), this->object_id);
dump_field(out, ESPHOME_PSTR("key"), this->key);
dump_field(out, ESPHOME_PSTR("name"), this->name);
dump_progmem_string_field(out, ESPHOME_PSTR("name"), this->name);
for (const auto &it : *this->supported_color_modes) {
dump_field(out, ESPHOME_PSTR("supported_color_modes"), static_cast<enums::ColorMode>(it), 4);
}
@@ -1391,7 +1399,7 @@ const char *ListEntitiesSensorResponse::dump_to(DumpBuffer &out) const {
MessageDumpHelper helper(out, ESPHOME_PSTR("ListEntitiesSensorResponse"));
dump_field(out, ESPHOME_PSTR("object_id"), this->object_id);
dump_field(out, ESPHOME_PSTR("key"), this->key);
dump_field(out, ESPHOME_PSTR("name"), this->name);
dump_progmem_string_field(out, ESPHOME_PSTR("name"), this->name);
#ifdef USE_ENTITY_ICON
dump_field(out, ESPHOME_PSTR("icon"), this->icon);
#endif
@@ -1423,7 +1431,7 @@ const char *ListEntitiesSwitchResponse::dump_to(DumpBuffer &out) const {
MessageDumpHelper helper(out, ESPHOME_PSTR("ListEntitiesSwitchResponse"));
dump_field(out, ESPHOME_PSTR("object_id"), this->object_id);
dump_field(out, ESPHOME_PSTR("key"), this->key);
dump_field(out, ESPHOME_PSTR("name"), this->name);
dump_progmem_string_field(out, ESPHOME_PSTR("name"), this->name);
#ifdef USE_ENTITY_ICON
dump_field(out, ESPHOME_PSTR("icon"), this->icon);
#endif
@@ -1461,7 +1469,7 @@ const char *ListEntitiesTextSensorResponse::dump_to(DumpBuffer &out) const {
MessageDumpHelper helper(out, ESPHOME_PSTR("ListEntitiesTextSensorResponse"));
dump_field(out, ESPHOME_PSTR("object_id"), this->object_id);
dump_field(out, ESPHOME_PSTR("key"), this->key);
dump_field(out, ESPHOME_PSTR("name"), this->name);
dump_progmem_string_field(out, ESPHOME_PSTR("name"), this->name);
#ifdef USE_ENTITY_ICON
dump_field(out, ESPHOME_PSTR("icon"), this->icon);
#endif
@@ -1692,7 +1700,7 @@ const char *ListEntitiesCameraResponse::dump_to(DumpBuffer &out) const {
MessageDumpHelper helper(out, ESPHOME_PSTR("ListEntitiesCameraResponse"));
dump_field(out, ESPHOME_PSTR("object_id"), this->object_id);
dump_field(out, ESPHOME_PSTR("key"), this->key);
dump_field(out, ESPHOME_PSTR("name"), this->name);
dump_progmem_string_field(out, ESPHOME_PSTR("name"), this->name);
dump_field(out, ESPHOME_PSTR("disabled_by_default"), this->disabled_by_default);
#ifdef USE_ENTITY_ICON
dump_field(out, ESPHOME_PSTR("icon"), this->icon);
@@ -1725,7 +1733,7 @@ const char *ListEntitiesClimateResponse::dump_to(DumpBuffer &out) const {
MessageDumpHelper helper(out, ESPHOME_PSTR("ListEntitiesClimateResponse"));
dump_field(out, ESPHOME_PSTR("object_id"), this->object_id);
dump_field(out, ESPHOME_PSTR("key"), this->key);
dump_field(out, ESPHOME_PSTR("name"), this->name);
dump_progmem_string_field(out, ESPHOME_PSTR("name"), this->name);
dump_field(out, ESPHOME_PSTR("supports_current_temperature"), this->supports_current_temperature);
dump_field(out, ESPHOME_PSTR("supports_two_point_target_temperature"), this->supports_two_point_target_temperature);
for (const auto &it : *this->supported_modes) {
@@ -1823,7 +1831,7 @@ const char *ListEntitiesWaterHeaterResponse::dump_to(DumpBuffer &out) const {
MessageDumpHelper helper(out, ESPHOME_PSTR("ListEntitiesWaterHeaterResponse"));
dump_field(out, ESPHOME_PSTR("object_id"), this->object_id);
dump_field(out, ESPHOME_PSTR("key"), this->key);
dump_field(out, ESPHOME_PSTR("name"), this->name);
dump_progmem_string_field(out, ESPHOME_PSTR("name"), this->name);
#ifdef USE_ENTITY_ICON
dump_field(out, ESPHOME_PSTR("icon"), this->icon);
#endif
@@ -1877,7 +1885,7 @@ const char *ListEntitiesNumberResponse::dump_to(DumpBuffer &out) const {
MessageDumpHelper helper(out, ESPHOME_PSTR("ListEntitiesNumberResponse"));
dump_field(out, ESPHOME_PSTR("object_id"), this->object_id);
dump_field(out, ESPHOME_PSTR("key"), this->key);
dump_field(out, ESPHOME_PSTR("name"), this->name);
dump_progmem_string_field(out, ESPHOME_PSTR("name"), this->name);
#ifdef USE_ENTITY_ICON
dump_field(out, ESPHOME_PSTR("icon"), this->icon);
#endif
@@ -1919,7 +1927,7 @@ const char *ListEntitiesSelectResponse::dump_to(DumpBuffer &out) const {
MessageDumpHelper helper(out, ESPHOME_PSTR("ListEntitiesSelectResponse"));
dump_field(out, ESPHOME_PSTR("object_id"), this->object_id);
dump_field(out, ESPHOME_PSTR("key"), this->key);
dump_field(out, ESPHOME_PSTR("name"), this->name);
dump_progmem_string_field(out, ESPHOME_PSTR("name"), this->name);
#ifdef USE_ENTITY_ICON
dump_field(out, ESPHOME_PSTR("icon"), this->icon);
#endif
@@ -1958,7 +1966,7 @@ const char *ListEntitiesSirenResponse::dump_to(DumpBuffer &out) const {
MessageDumpHelper helper(out, ESPHOME_PSTR("ListEntitiesSirenResponse"));
dump_field(out, ESPHOME_PSTR("object_id"), this->object_id);
dump_field(out, ESPHOME_PSTR("key"), this->key);
dump_field(out, ESPHOME_PSTR("name"), this->name);
dump_progmem_string_field(out, ESPHOME_PSTR("name"), this->name);
#ifdef USE_ENTITY_ICON
dump_field(out, ESPHOME_PSTR("icon"), this->icon);
#endif
@@ -2005,7 +2013,7 @@ const char *ListEntitiesLockResponse::dump_to(DumpBuffer &out) const {
MessageDumpHelper helper(out, ESPHOME_PSTR("ListEntitiesLockResponse"));
dump_field(out, ESPHOME_PSTR("object_id"), this->object_id);
dump_field(out, ESPHOME_PSTR("key"), this->key);
dump_field(out, ESPHOME_PSTR("name"), this->name);
dump_progmem_string_field(out, ESPHOME_PSTR("name"), this->name);
#ifdef USE_ENTITY_ICON
dump_field(out, ESPHOME_PSTR("icon"), this->icon);
#endif
@@ -2046,7 +2054,7 @@ const char *ListEntitiesButtonResponse::dump_to(DumpBuffer &out) const {
MessageDumpHelper helper(out, ESPHOME_PSTR("ListEntitiesButtonResponse"));
dump_field(out, ESPHOME_PSTR("object_id"), this->object_id);
dump_field(out, ESPHOME_PSTR("key"), this->key);
dump_field(out, ESPHOME_PSTR("name"), this->name);
dump_progmem_string_field(out, ESPHOME_PSTR("name"), this->name);
#ifdef USE_ENTITY_ICON
dump_field(out, ESPHOME_PSTR("icon"), this->icon);
#endif
@@ -2081,7 +2089,7 @@ const char *ListEntitiesMediaPlayerResponse::dump_to(DumpBuffer &out) const {
MessageDumpHelper helper(out, ESPHOME_PSTR("ListEntitiesMediaPlayerResponse"));
dump_field(out, ESPHOME_PSTR("object_id"), this->object_id);
dump_field(out, ESPHOME_PSTR("key"), this->key);
dump_field(out, ESPHOME_PSTR("name"), this->name);
dump_progmem_string_field(out, ESPHOME_PSTR("name"), this->name);
#ifdef USE_ENTITY_ICON
dump_field(out, ESPHOME_PSTR("icon"), this->icon);
#endif
@@ -2471,7 +2479,7 @@ const char *ListEntitiesAlarmControlPanelResponse::dump_to(DumpBuffer &out) cons
MessageDumpHelper helper(out, ESPHOME_PSTR("ListEntitiesAlarmControlPanelResponse"));
dump_field(out, ESPHOME_PSTR("object_id"), this->object_id);
dump_field(out, ESPHOME_PSTR("key"), this->key);
dump_field(out, ESPHOME_PSTR("name"), this->name);
dump_progmem_string_field(out, ESPHOME_PSTR("name"), this->name);
#ifdef USE_ENTITY_ICON
dump_field(out, ESPHOME_PSTR("icon"), this->icon);
#endif
@@ -2510,7 +2518,7 @@ const char *ListEntitiesTextResponse::dump_to(DumpBuffer &out) const {
MessageDumpHelper helper(out, ESPHOME_PSTR("ListEntitiesTextResponse"));
dump_field(out, ESPHOME_PSTR("object_id"), this->object_id);
dump_field(out, ESPHOME_PSTR("key"), this->key);
dump_field(out, ESPHOME_PSTR("name"), this->name);
dump_progmem_string_field(out, ESPHOME_PSTR("name"), this->name);
#ifdef USE_ENTITY_ICON
dump_field(out, ESPHOME_PSTR("icon"), this->icon);
#endif
@@ -2550,7 +2558,7 @@ const char *ListEntitiesDateResponse::dump_to(DumpBuffer &out) const {
MessageDumpHelper helper(out, ESPHOME_PSTR("ListEntitiesDateResponse"));
dump_field(out, ESPHOME_PSTR("object_id"), this->object_id);
dump_field(out, ESPHOME_PSTR("key"), this->key);
dump_field(out, ESPHOME_PSTR("name"), this->name);
dump_progmem_string_field(out, ESPHOME_PSTR("name"), this->name);
#ifdef USE_ENTITY_ICON
dump_field(out, ESPHOME_PSTR("icon"), this->icon);
#endif
@@ -2590,7 +2598,7 @@ const char *ListEntitiesTimeResponse::dump_to(DumpBuffer &out) const {
MessageDumpHelper helper(out, ESPHOME_PSTR("ListEntitiesTimeResponse"));
dump_field(out, ESPHOME_PSTR("object_id"), this->object_id);
dump_field(out, ESPHOME_PSTR("key"), this->key);
dump_field(out, ESPHOME_PSTR("name"), this->name);
dump_progmem_string_field(out, ESPHOME_PSTR("name"), this->name);
#ifdef USE_ENTITY_ICON
dump_field(out, ESPHOME_PSTR("icon"), this->icon);
#endif
@@ -2630,7 +2638,7 @@ const char *ListEntitiesEventResponse::dump_to(DumpBuffer &out) const {
MessageDumpHelper helper(out, ESPHOME_PSTR("ListEntitiesEventResponse"));
dump_field(out, ESPHOME_PSTR("object_id"), this->object_id);
dump_field(out, ESPHOME_PSTR("key"), this->key);
dump_field(out, ESPHOME_PSTR("name"), this->name);
dump_progmem_string_field(out, ESPHOME_PSTR("name"), this->name);
#ifdef USE_ENTITY_ICON
dump_field(out, ESPHOME_PSTR("icon"), this->icon);
#endif
@@ -2660,7 +2668,7 @@ const char *ListEntitiesValveResponse::dump_to(DumpBuffer &out) const {
MessageDumpHelper helper(out, ESPHOME_PSTR("ListEntitiesValveResponse"));
dump_field(out, ESPHOME_PSTR("object_id"), this->object_id);
dump_field(out, ESPHOME_PSTR("key"), this->key);
dump_field(out, ESPHOME_PSTR("name"), this->name);
dump_progmem_string_field(out, ESPHOME_PSTR("name"), this->name);
#ifdef USE_ENTITY_ICON
dump_field(out, ESPHOME_PSTR("icon"), this->icon);
#endif
@@ -2702,7 +2710,7 @@ const char *ListEntitiesDateTimeResponse::dump_to(DumpBuffer &out) const {
MessageDumpHelper helper(out, ESPHOME_PSTR("ListEntitiesDateTimeResponse"));
dump_field(out, ESPHOME_PSTR("object_id"), this->object_id);
dump_field(out, ESPHOME_PSTR("key"), this->key);
dump_field(out, ESPHOME_PSTR("name"), this->name);
dump_progmem_string_field(out, ESPHOME_PSTR("name"), this->name);
#ifdef USE_ENTITY_ICON
dump_field(out, ESPHOME_PSTR("icon"), this->icon);
#endif
@@ -2738,7 +2746,7 @@ const char *ListEntitiesUpdateResponse::dump_to(DumpBuffer &out) const {
MessageDumpHelper helper(out, ESPHOME_PSTR("ListEntitiesUpdateResponse"));
dump_field(out, ESPHOME_PSTR("object_id"), this->object_id);
dump_field(out, ESPHOME_PSTR("key"), this->key);
dump_field(out, ESPHOME_PSTR("name"), this->name);
dump_progmem_string_field(out, ESPHOME_PSTR("name"), this->name);
#ifdef USE_ENTITY_ICON
dump_field(out, ESPHOME_PSTR("icon"), this->icon);
#endif
@@ -2801,7 +2809,7 @@ const char *ListEntitiesInfraredResponse::dump_to(DumpBuffer &out) const {
MessageDumpHelper helper(out, ESPHOME_PSTR("ListEntitiesInfraredResponse"));
dump_field(out, ESPHOME_PSTR("object_id"), this->object_id);
dump_field(out, ESPHOME_PSTR("key"), this->key);
dump_field(out, ESPHOME_PSTR("name"), this->name);
dump_progmem_string_field(out, ESPHOME_PSTR("name"), this->name);
#ifdef USE_ENTITY_ICON
dump_field(out, ESPHOME_PSTR("icon"), this->icon);
#endif
@@ -2859,7 +2867,7 @@ const char *ListEntitiesRadioFrequencyResponse::dump_to(DumpBuffer &out) const {
MessageDumpHelper helper(out, ESPHOME_PSTR("ListEntitiesRadioFrequencyResponse"));
dump_field(out, ESPHOME_PSTR("object_id"), this->object_id);
dump_field(out, ESPHOME_PSTR("key"), this->key);
dump_field(out, ESPHOME_PSTR("name"), this->name);
dump_progmem_string_field(out, ESPHOME_PSTR("name"), this->name);
#ifdef USE_ENTITY_ICON
dump_field(out, ESPHOME_PSTR("icon"), this->icon);
#endif
+39
View File
@@ -409,6 +409,10 @@ class ProtoEncode {
/// Tag must be a single-byte varint (< 128). Always encodes (no zero check).
[[nodiscard]] static inline uint8_t *encode_short_string_force(uint8_t *__restrict__ pos PROTO_ENCODE_DEBUG_PARAM,
uint8_t tag, const StringRef &ref) {
#ifdef USE_ESP8266
// memcpy_P reads RAM too, and sharing the progmem copy is smaller than an inline memcpy
return encode_short_progmem_string_force(pos PROTO_ENCODE_DEBUG_ARG, tag, ProgmemStringRef(ref));
#else
#ifdef ESPHOME_DEBUG_API
assert(ref.size() < 128 && "encode_short_string_force: string exceeds max_data_length < 128");
#endif
@@ -417,6 +421,19 @@ class ProtoEncode {
pos[1] = static_cast<uint8_t>(ref.size());
std::memcpy(pos + 2, ref.c_str(), ref.size());
return pos + 2 + ref.size();
#endif
}
/// encode_short_string_force for a string that may be in flash (PROGMEM).
[[nodiscard]] static inline uint8_t *encode_short_progmem_string_force(
uint8_t *__restrict__ pos PROTO_ENCODE_DEBUG_PARAM, uint8_t tag, const ProgmemStringRef &ref) {
#ifdef ESPHOME_DEBUG_API
assert(ref.size() < 128 && "encode_short_progmem_string_force: string exceeds max_data_length < 128");
#endif
PROTO_ENCODE_CHECK_BOUNDS(pos, 2 + ref.size());
pos[0] = tag;
pos[1] = static_cast<uint8_t>(ref.size());
progmem_memcpy(pos + 2, ref.progmem_ptr(), ref.size());
return pos + 2 + ref.size();
}
/// Write a precomputed tag byte + 32-bit value. Outlined on embedded: one copy beats inline stores per field.
[[nodiscard]] static PROTO_OUTLINE_FOR_SIZE uint8_t *write_tag_and_fixed32(
@@ -481,6 +498,17 @@ class ProtoEncode {
progmem_memcpy(pos, data, len);
return pos + len;
}
/// encode_progmem_bytes for a string field that may be in flash.
[[nodiscard]] static inline uint8_t *encode_progmem_string(uint8_t *__restrict__ pos PROTO_ENCODE_DEBUG_PARAM,
uint32_t field_id, const ProgmemStringRef &ref) {
return encode_progmem_bytes(pos PROTO_ENCODE_DEBUG_ARG, field_id,
reinterpret_cast<const uint8_t *>(ref.progmem_ptr()), ref.size());
}
[[nodiscard]] static inline uint8_t *encode_progmem_string_force(uint8_t *__restrict__ pos PROTO_ENCODE_DEBUG_PARAM,
uint32_t field_id, const ProgmemStringRef &ref) {
return encode_progmem_bytes_force(pos PROTO_ENCODE_DEBUG_ARG, field_id,
reinterpret_cast<const uint8_t *>(ref.progmem_ptr()), ref.size());
}
[[nodiscard]] static inline uint8_t *encode_uint32_force(uint8_t *__restrict__ pos PROTO_ENCODE_DEBUG_PARAM,
uint32_t field_id, uint32_t value) {
pos = encode_field_raw(pos PROTO_ENCODE_DEBUG_ARG, field_id, 0);
@@ -680,9 +708,20 @@ class DumpBuffer {
return *this;
}
/// Append len bytes of a PROGMEM string
DumpBuffer &append_p(const char *str, size_t len) {
#ifdef USE_ESP8266
append_p_esp8266(str, len);
#else
append_impl_(str, len);
#endif
return *this;
}
#ifdef USE_ESP8266
/// Out-of-line ESP8266 PROGMEM append to avoid inlining strlen_P/memcpy_P at every call site
void append_p_esp8266(const char *str);
void append_p_esp8266(const char *str, size_t len);
#endif
const char *c_str() const { return buf_; }
+8 -2
View File
@@ -207,7 +207,12 @@ bool MQTTComponent::send_discovery_() {
}
// Fields from EntityBase
root[MQTT_NAME] = this->get_entity()->has_own_name() ? this->friendly_name_() : StringRef();
if (this->get_entity()->has_own_name()) {
// ArduinoJson copies flash strings into the document itself
root[MQTT_NAME] = reinterpret_cast<ProgmemStr>(this->get_entity()->get_name_progmem().progmem_ptr());
} else {
root[MQTT_NAME] = StringRef();
}
if (this->is_disabled_by_default_())
root[MQTT_ENABLED_BY_DEFAULT] = false;
@@ -253,9 +258,10 @@ bool MQTTComponent::send_discovery_() {
char object_id_buf[OBJECT_ID_MAX_LEN];
StringRef object_id = this->get_default_object_id_to_(object_id_buf);
if (discovery_info.unique_id_generator == MQTT_MAC_ADDRESS_UNIQUE_ID_GENERATOR) {
char name_buf[ENTITY_NAME_BUF_SIZE];
char friendly_name_hash[9];
buf_append_printf(friendly_name_hash, sizeof(friendly_name_hash), 0, "%08" PRIx32,
fnv1_hash(this->friendly_name_().c_str()));
fnv1_hash(this->get_entity()->get_name_to(name_buf).c_str()));
// Format: mac-component_type-hash (e.g. "aabbccddeeff-sensor-12345678")
// MAC (12) + "-" (1) + domain (max 20) + "-" (1) + hash (8) + null (1) = 43
char unique_id[MAC_ADDRESS_BUFFER_SIZE + ESPHOME_DOMAIN_MAX_LEN + 11];
@@ -109,18 +109,23 @@ void PrometheusHandler::handleRequest(AsyncWebServerRequest *req) {
req->send(stream);
}
std::string PrometheusHandler::relabel_id_(EntityBase *obj) {
void PrometheusHandler::print_relabel_id_(AsyncResponseStream *stream, EntityBase *obj) {
auto item = relabel_map_id_.find(obj);
if (item != relabel_map_id_.end()) {
return item->second;
stream->print(item->second.c_str());
return;
}
char object_id_buf[OBJECT_ID_MAX_LEN];
return obj->get_object_id_to(object_id_buf).str();
stream->print(obj->get_object_id_to(object_id_buf).c_str());
}
std::string PrometheusHandler::relabel_name_(EntityBase *obj) {
void PrometheusHandler::print_relabel_name_(AsyncResponseStream *stream, EntityBase *obj) {
auto item = relabel_map_name_.find(obj);
return item == relabel_map_name_.end() ? obj->get_name() : item->second;
if (item != relabel_map_name_.end()) {
stream->print(item->second.c_str());
return;
}
stream->print(reinterpret_cast<ProgmemStr>(obj->get_name_progmem().progmem_ptr()));
}
void PrometheusHandler::add_area_label_(AsyncResponseStream *stream, std::string &area) {
@@ -148,12 +153,12 @@ void PrometheusHandler::print_metric_labels_(AsyncResponseStream *stream, Progme
std::string &area, std::string &node, std::string &friendly_name) {
stream->print(metric_name);
stream->print(ESPHOME_F("{id=\""));
stream->print(relabel_id_(obj).c_str());
this->print_relabel_id_(stream, obj);
add_area_label_(stream, area);
add_node_label_(stream, node);
add_friendly_name_label_(stream, friendly_name);
stream->print(ESPHOME_F("\",name=\""));
stream->print(relabel_name_(obj).c_str());
this->print_relabel_name_(stream, obj);
}
// Type-specific implementation
@@ -169,21 +174,21 @@ void PrometheusHandler::sensor_row_(AsyncResponseStream *stream, sensor::Sensor
if (!std::isnan(obj->state)) {
// We have a valid value, output this value
stream->print(ESPHOME_F("esphome_sensor_failed{id=\""));
stream->print(relabel_id_(obj).c_str());
this->print_relabel_id_(stream, obj);
add_area_label_(stream, area);
add_node_label_(stream, node);
add_friendly_name_label_(stream, friendly_name);
stream->print(ESPHOME_F("\",name=\""));
stream->print(relabel_name_(obj).c_str());
this->print_relabel_name_(stream, obj);
stream->print(ESPHOME_F("\"} 0\n"));
// Data itself
stream->print(ESPHOME_F("esphome_sensor_value{id=\""));
stream->print(relabel_id_(obj).c_str());
this->print_relabel_id_(stream, obj);
add_area_label_(stream, area);
add_node_label_(stream, node);
add_friendly_name_label_(stream, friendly_name);
stream->print(ESPHOME_F("\",name=\""));
stream->print(relabel_name_(obj).c_str());
this->print_relabel_name_(stream, obj);
stream->print(ESPHOME_F("\",unit=\""));
stream->print(obj->get_unit_of_measurement_ref().c_str());
stream->print(ESPHOME_F("\"} "));
@@ -194,12 +199,12 @@ void PrometheusHandler::sensor_row_(AsyncResponseStream *stream, sensor::Sensor
} else {
// Invalid state
stream->print(ESPHOME_F("esphome_sensor_failed{id=\""));
stream->print(relabel_id_(obj).c_str());
this->print_relabel_id_(stream, obj);
add_area_label_(stream, area);
add_node_label_(stream, node);
add_friendly_name_label_(stream, friendly_name);
stream->print(ESPHOME_F("\",name=\""));
stream->print(relabel_name_(obj).c_str());
this->print_relabel_name_(stream, obj);
stream->print(ESPHOME_F("\"} 1\n"));
}
}
@@ -218,33 +223,33 @@ void PrometheusHandler::binary_sensor_row_(AsyncResponseStream *stream, binary_s
if (obj->has_state()) {
// We have a valid value, output this value
stream->print(ESPHOME_F("esphome_binary_sensor_failed{id=\""));
stream->print(relabel_id_(obj).c_str());
this->print_relabel_id_(stream, obj);
add_area_label_(stream, area);
add_node_label_(stream, node);
add_friendly_name_label_(stream, friendly_name);
stream->print(ESPHOME_F("\",name=\""));
stream->print(relabel_name_(obj).c_str());
this->print_relabel_name_(stream, obj);
stream->print(ESPHOME_F("\"} 0\n"));
// Data itself
stream->print(ESPHOME_F("esphome_binary_sensor_value{id=\""));
stream->print(relabel_id_(obj).c_str());
this->print_relabel_id_(stream, obj);
add_area_label_(stream, area);
add_node_label_(stream, node);
add_friendly_name_label_(stream, friendly_name);
stream->print(ESPHOME_F("\",name=\""));
stream->print(relabel_name_(obj).c_str());
this->print_relabel_name_(stream, obj);
stream->print(ESPHOME_F("\"} "));
stream->print(obj->state);
stream->print(ESPHOME_F("\n"));
} else {
// Invalid state
stream->print(ESPHOME_F("esphome_binary_sensor_failed{id=\""));
stream->print(relabel_id_(obj).c_str());
this->print_relabel_id_(stream, obj);
add_area_label_(stream, area);
add_node_label_(stream, node);
add_friendly_name_label_(stream, friendly_name);
stream->print(ESPHOME_F("\",name=\""));
stream->print(relabel_name_(obj).c_str());
this->print_relabel_name_(stream, obj);
stream->print(ESPHOME_F("\"} 1\n"));
}
}
@@ -262,33 +267,33 @@ void PrometheusHandler::fan_row_(AsyncResponseStream *stream, fan::Fan *obj, std
if (obj->is_internal() && !this->include_internal_)
return;
stream->print(ESPHOME_F("esphome_fan_failed{id=\""));
stream->print(relabel_id_(obj).c_str());
this->print_relabel_id_(stream, obj);
add_area_label_(stream, area);
add_node_label_(stream, node);
add_friendly_name_label_(stream, friendly_name);
stream->print(ESPHOME_F("\",name=\""));
stream->print(relabel_name_(obj).c_str());
this->print_relabel_name_(stream, obj);
stream->print(ESPHOME_F("\"} 0\n"));
// Data itself
stream->print(ESPHOME_F("esphome_fan_value{id=\""));
stream->print(relabel_id_(obj).c_str());
this->print_relabel_id_(stream, obj);
add_area_label_(stream, area);
add_node_label_(stream, node);
add_friendly_name_label_(stream, friendly_name);
stream->print(ESPHOME_F("\",name=\""));
stream->print(relabel_name_(obj).c_str());
this->print_relabel_name_(stream, obj);
stream->print(ESPHOME_F("\"} "));
stream->print(obj->state);
stream->print(ESPHOME_F("\n"));
// Speed if available
if (obj->get_traits().supports_speed()) {
stream->print(ESPHOME_F("esphome_fan_speed{id=\""));
stream->print(relabel_id_(obj).c_str());
this->print_relabel_id_(stream, obj);
add_area_label_(stream, area);
add_node_label_(stream, node);
add_friendly_name_label_(stream, friendly_name);
stream->print(ESPHOME_F("\",name=\""));
stream->print(relabel_name_(obj).c_str());
this->print_relabel_name_(stream, obj);
stream->print(ESPHOME_F("\"} "));
stream->print(obj->speed);
stream->print(ESPHOME_F("\n"));
@@ -296,12 +301,12 @@ void PrometheusHandler::fan_row_(AsyncResponseStream *stream, fan::Fan *obj, std
// Oscillation if available
if (obj->get_traits().supports_oscillation()) {
stream->print(ESPHOME_F("esphome_fan_oscillation{id=\""));
stream->print(relabel_id_(obj).c_str());
this->print_relabel_id_(stream, obj);
add_area_label_(stream, area);
add_node_label_(stream, node);
add_friendly_name_label_(stream, friendly_name);
stream->print(ESPHOME_F("\",name=\""));
stream->print(relabel_name_(obj).c_str());
this->print_relabel_name_(stream, obj);
stream->print(ESPHOME_F("\"} "));
stream->print(obj->oscillating);
stream->print(ESPHOME_F("\n"));
@@ -385,32 +390,32 @@ void PrometheusHandler::cover_row_(AsyncResponseStream *stream, cover::Cover *ob
if (!std::isnan(obj->position)) {
// We have a valid value, output this value
stream->print(ESPHOME_F("esphome_cover_failed{id=\""));
stream->print(relabel_id_(obj).c_str());
this->print_relabel_id_(stream, obj);
add_area_label_(stream, area);
add_node_label_(stream, node);
add_friendly_name_label_(stream, friendly_name);
stream->print(ESPHOME_F("\",name=\""));
stream->print(relabel_name_(obj).c_str());
this->print_relabel_name_(stream, obj);
stream->print(ESPHOME_F("\"} 0\n"));
// Data itself
stream->print(ESPHOME_F("esphome_cover_value{id=\""));
stream->print(relabel_id_(obj).c_str());
this->print_relabel_id_(stream, obj);
add_area_label_(stream, area);
add_node_label_(stream, node);
add_friendly_name_label_(stream, friendly_name);
stream->print(ESPHOME_F("\",name=\""));
stream->print(relabel_name_(obj).c_str());
this->print_relabel_name_(stream, obj);
stream->print(ESPHOME_F("\"} "));
stream->print(obj->position);
stream->print(ESPHOME_F("\n"));
if (obj->get_traits().get_supports_tilt()) {
stream->print(ESPHOME_F("esphome_cover_tilt{id=\""));
stream->print(relabel_id_(obj).c_str());
this->print_relabel_id_(stream, obj);
add_area_label_(stream, area);
add_node_label_(stream, node);
add_friendly_name_label_(stream, friendly_name);
stream->print(ESPHOME_F("\",name=\""));
stream->print(relabel_name_(obj).c_str());
this->print_relabel_name_(stream, obj);
stream->print(ESPHOME_F("\"} "));
stream->print(obj->tilt);
stream->print(ESPHOME_F("\n"));
@@ -418,12 +423,12 @@ void PrometheusHandler::cover_row_(AsyncResponseStream *stream, cover::Cover *ob
} else {
// Invalid state
stream->print(ESPHOME_F("esphome_cover_failed{id=\""));
stream->print(relabel_id_(obj).c_str());
this->print_relabel_id_(stream, obj);
add_area_label_(stream, area);
add_node_label_(stream, node);
add_friendly_name_label_(stream, friendly_name);
stream->print(ESPHOME_F("\",name=\""));
stream->print(relabel_name_(obj).c_str());
this->print_relabel_name_(stream, obj);
stream->print(ESPHOME_F("\"} 1\n"));
}
}
@@ -439,21 +444,21 @@ void PrometheusHandler::switch_row_(AsyncResponseStream *stream, switch_::Switch
if (obj->is_internal() && !this->include_internal_)
return;
stream->print(ESPHOME_F("esphome_switch_failed{id=\""));
stream->print(relabel_id_(obj).c_str());
this->print_relabel_id_(stream, obj);
add_area_label_(stream, area);
add_node_label_(stream, node);
add_friendly_name_label_(stream, friendly_name);
stream->print(ESPHOME_F("\",name=\""));
stream->print(relabel_name_(obj).c_str());
this->print_relabel_name_(stream, obj);
stream->print(ESPHOME_F("\"} 0\n"));
// Data itself
stream->print(ESPHOME_F("esphome_switch_value{id=\""));
stream->print(relabel_id_(obj).c_str());
this->print_relabel_id_(stream, obj);
add_area_label_(stream, area);
add_node_label_(stream, node);
add_friendly_name_label_(stream, friendly_name);
stream->print(ESPHOME_F("\",name=\""));
stream->print(relabel_name_(obj).c_str());
this->print_relabel_name_(stream, obj);
stream->print(ESPHOME_F("\"} "));
stream->print(obj->state);
stream->print(ESPHOME_F("\n"));
@@ -470,21 +475,21 @@ void PrometheusHandler::lock_row_(AsyncResponseStream *stream, lock::Lock *obj,
if (obj->is_internal() && !this->include_internal_)
return;
stream->print(ESPHOME_F("esphome_lock_failed{id=\""));
stream->print(relabel_id_(obj).c_str());
this->print_relabel_id_(stream, obj);
add_area_label_(stream, area);
add_node_label_(stream, node);
add_friendly_name_label_(stream, friendly_name);
stream->print(ESPHOME_F("\",name=\""));
stream->print(relabel_name_(obj).c_str());
this->print_relabel_name_(stream, obj);
stream->print(ESPHOME_F("\"} 0\n"));
// Data itself
stream->print(ESPHOME_F("esphome_lock_value{id=\""));
stream->print(relabel_id_(obj).c_str());
this->print_relabel_id_(stream, obj);
add_area_label_(stream, area);
add_node_label_(stream, node);
add_friendly_name_label_(stream, friendly_name);
stream->print(ESPHOME_F("\",name=\""));
stream->print(relabel_name_(obj).c_str());
this->print_relabel_name_(stream, obj);
stream->print(ESPHOME_F("\"} "));
stream->print(obj->state);
stream->print(ESPHOME_F("\n"));
@@ -504,21 +509,21 @@ void PrometheusHandler::text_sensor_row_(AsyncResponseStream *stream, text_senso
if (obj->has_state()) {
// We have a valid value, output this value
stream->print(ESPHOME_F("esphome_text_sensor_failed{id=\""));
stream->print(relabel_id_(obj).c_str());
this->print_relabel_id_(stream, obj);
add_area_label_(stream, area);
add_node_label_(stream, node);
add_friendly_name_label_(stream, friendly_name);
stream->print(ESPHOME_F("\",name=\""));
stream->print(relabel_name_(obj).c_str());
this->print_relabel_name_(stream, obj);
stream->print(ESPHOME_F("\"} 0\n"));
// Data itself
stream->print(ESPHOME_F("esphome_text_sensor_value{id=\""));
stream->print(relabel_id_(obj).c_str());
this->print_relabel_id_(stream, obj);
add_area_label_(stream, area);
add_node_label_(stream, node);
add_friendly_name_label_(stream, friendly_name);
stream->print(ESPHOME_F("\",name=\""));
stream->print(relabel_name_(obj).c_str());
this->print_relabel_name_(stream, obj);
stream->print(ESPHOME_F("\",value=\""));
stream->print(obj->state.c_str());
stream->print(ESPHOME_F("\"} "));
@@ -527,12 +532,12 @@ void PrometheusHandler::text_sensor_row_(AsyncResponseStream *stream, text_senso
} else {
// Invalid state
stream->print(ESPHOME_F("esphome_text_sensor_failed{id=\""));
stream->print(relabel_id_(obj).c_str());
this->print_relabel_id_(stream, obj);
add_area_label_(stream, area);
add_node_label_(stream, node);
add_friendly_name_label_(stream, friendly_name);
stream->print(ESPHOME_F("\",name=\""));
stream->print(relabel_name_(obj).c_str());
this->print_relabel_name_(stream, obj);
stream->print(ESPHOME_F("\"} 1\n"));
}
}
@@ -551,21 +556,21 @@ void PrometheusHandler::text_row_(AsyncResponseStream *stream, text::Text *obj,
if (obj->has_state()) {
// We have a valid value, output this value
stream->print(ESPHOME_F("esphome_text_failed{id=\""));
stream->print(relabel_id_(obj).c_str());
this->print_relabel_id_(stream, obj);
add_area_label_(stream, area);
add_node_label_(stream, node);
add_friendly_name_label_(stream, friendly_name);
stream->print(ESPHOME_F("\",name=\""));
stream->print(relabel_name_(obj).c_str());
this->print_relabel_name_(stream, obj);
stream->print(ESPHOME_F("\"} 0\n"));
// Data itself
stream->print(ESPHOME_F("esphome_text_value{id=\""));
stream->print(relabel_id_(obj).c_str());
this->print_relabel_id_(stream, obj);
add_area_label_(stream, area);
add_node_label_(stream, node);
add_friendly_name_label_(stream, friendly_name);
stream->print(ESPHOME_F("\",name=\""));
stream->print(relabel_name_(obj).c_str());
this->print_relabel_name_(stream, obj);
stream->print(ESPHOME_F("\",value=\""));
stream->print(obj->state.c_str());
stream->print(ESPHOME_F("\"} "));
@@ -574,12 +579,12 @@ void PrometheusHandler::text_row_(AsyncResponseStream *stream, text::Text *obj,
} else {
// Invalid state
stream->print(ESPHOME_F("esphome_text_failed{id=\""));
stream->print(relabel_id_(obj).c_str());
this->print_relabel_id_(stream, obj);
add_area_label_(stream, area);
add_node_label_(stream, node);
add_friendly_name_label_(stream, friendly_name);
stream->print(ESPHOME_F("\",name=\""));
stream->print(relabel_name_(obj).c_str());
this->print_relabel_name_(stream, obj);
stream->print(ESPHOME_F("\"} 1\n"));
}
}
@@ -598,21 +603,21 @@ void PrometheusHandler::event_row_(AsyncResponseStream *stream, event::Event *ob
if (obj->has_event()) {
// We have a valid event type, output this value
stream->print(ESPHOME_F("esphome_event_failed{id=\""));
stream->print(relabel_id_(obj).c_str());
this->print_relabel_id_(stream, obj);
add_area_label_(stream, area);
add_node_label_(stream, node);
add_friendly_name_label_(stream, friendly_name);
stream->print(ESPHOME_F("\",name=\""));
stream->print(relabel_name_(obj).c_str());
this->print_relabel_name_(stream, obj);
stream->print(ESPHOME_F("\"} 0\n"));
// Data itself
stream->print(ESPHOME_F("esphome_event_value{id=\""));
stream->print(relabel_id_(obj).c_str());
this->print_relabel_id_(stream, obj);
add_area_label_(stream, area);
add_node_label_(stream, node);
add_friendly_name_label_(stream, friendly_name);
stream->print(ESPHOME_F("\",name=\""));
stream->print(relabel_name_(obj).c_str());
this->print_relabel_name_(stream, obj);
stream->print(ESPHOME_F("\",last_event_type=\""));
// get_last_event_type() returns StringRef (null-terminated)
stream->print(obj->get_last_event_type().c_str());
@@ -622,12 +627,12 @@ void PrometheusHandler::event_row_(AsyncResponseStream *stream, event::Event *ob
} else {
// No event triggered yet
stream->print(ESPHOME_F("esphome_event_failed{id=\""));
stream->print(relabel_id_(obj).c_str());
this->print_relabel_id_(stream, obj);
add_area_label_(stream, area);
add_node_label_(stream, node);
add_friendly_name_label_(stream, friendly_name);
stream->print(ESPHOME_F("\",name=\""));
stream->print(relabel_name_(obj).c_str());
this->print_relabel_name_(stream, obj);
stream->print(ESPHOME_F("\"} 1\n"));
}
}
@@ -646,33 +651,33 @@ void PrometheusHandler::number_row_(AsyncResponseStream *stream, number::Number
if (!std::isnan(obj->state)) {
// We have a valid value, output this value
stream->print(ESPHOME_F("esphome_number_failed{id=\""));
stream->print(relabel_id_(obj).c_str());
this->print_relabel_id_(stream, obj);
add_area_label_(stream, area);
add_node_label_(stream, node);
add_friendly_name_label_(stream, friendly_name);
stream->print(ESPHOME_F("\",name=\""));
stream->print(relabel_name_(obj).c_str());
this->print_relabel_name_(stream, obj);
stream->print(ESPHOME_F("\"} 0\n"));
// Data itself
stream->print(ESPHOME_F("esphome_number_value{id=\""));
stream->print(relabel_id_(obj).c_str());
this->print_relabel_id_(stream, obj);
add_area_label_(stream, area);
add_node_label_(stream, node);
add_friendly_name_label_(stream, friendly_name);
stream->print(ESPHOME_F("\",name=\""));
stream->print(relabel_name_(obj).c_str());
this->print_relabel_name_(stream, obj);
stream->print(ESPHOME_F("\"} "));
stream->print(obj->state);
stream->print(ESPHOME_F("\n"));
} else {
// Invalid state
stream->print(ESPHOME_F("esphome_number_failed{id=\""));
stream->print(relabel_id_(obj).c_str());
this->print_relabel_id_(stream, obj);
add_area_label_(stream, area);
add_node_label_(stream, node);
add_friendly_name_label_(stream, friendly_name);
stream->print(ESPHOME_F("\",name=\""));
stream->print(relabel_name_(obj).c_str());
this->print_relabel_name_(stream, obj);
stream->print(ESPHOME_F("\"} 1\n"));
}
}
@@ -690,21 +695,21 @@ void PrometheusHandler::select_row_(AsyncResponseStream *stream, select::Select
if (obj->has_state()) {
// We have a valid value, output this value
stream->print(ESPHOME_F("esphome_select_failed{id=\""));
stream->print(relabel_id_(obj).c_str());
this->print_relabel_id_(stream, obj);
add_area_label_(stream, area);
add_node_label_(stream, node);
add_friendly_name_label_(stream, friendly_name);
stream->print(ESPHOME_F("\",name=\""));
stream->print(relabel_name_(obj).c_str());
this->print_relabel_name_(stream, obj);
stream->print(ESPHOME_F("\"} 0\n"));
// Data itself
stream->print(ESPHOME_F("esphome_select_value{id=\""));
stream->print(relabel_id_(obj).c_str());
this->print_relabel_id_(stream, obj);
add_area_label_(stream, area);
add_node_label_(stream, node);
add_friendly_name_label_(stream, friendly_name);
stream->print(ESPHOME_F("\",name=\""));
stream->print(relabel_name_(obj).c_str());
this->print_relabel_name_(stream, obj);
stream->print(ESPHOME_F("\",value=\""));
// c_str() is safe as option values are null-terminated strings from codegen
stream->print(obj->current_option().c_str());
@@ -714,12 +719,12 @@ void PrometheusHandler::select_row_(AsyncResponseStream *stream, select::Select
} else {
// Invalid state
stream->print(ESPHOME_F("esphome_select_failed{id=\""));
stream->print(relabel_id_(obj).c_str());
this->print_relabel_id_(stream, obj);
add_area_label_(stream, area);
add_node_label_(stream, node);
add_friendly_name_label_(stream, friendly_name);
stream->print(ESPHOME_F("\",name=\""));
stream->print(relabel_name_(obj).c_str());
this->print_relabel_name_(stream, obj);
stream->print(ESPHOME_F("\"} 1\n"));
}
}
@@ -737,43 +742,43 @@ void PrometheusHandler::media_player_row_(AsyncResponseStream *stream, media_pla
if (obj->is_internal() && !this->include_internal_)
return;
stream->print(ESPHOME_F("esphome_media_player_failed{id=\""));
stream->print(relabel_id_(obj).c_str());
this->print_relabel_id_(stream, obj);
add_area_label_(stream, area);
add_node_label_(stream, node);
add_friendly_name_label_(stream, friendly_name);
stream->print(ESPHOME_F("\",name=\""));
stream->print(relabel_name_(obj).c_str());
this->print_relabel_name_(stream, obj);
stream->print(ESPHOME_F("\"} 0\n"));
// Data itself
stream->print(ESPHOME_F("esphome_media_player_state_value{id=\""));
stream->print(relabel_id_(obj).c_str());
this->print_relabel_id_(stream, obj);
add_area_label_(stream, area);
add_node_label_(stream, node);
add_friendly_name_label_(stream, friendly_name);
stream->print(ESPHOME_F("\",name=\""));
stream->print(relabel_name_(obj).c_str());
this->print_relabel_name_(stream, obj);
stream->print(ESPHOME_F("\",value=\""));
stream->print(media_player::media_player_state_to_string(obj->state));
stream->print(ESPHOME_F("\"} "));
stream->print(ESPHOME_F("1.0"));
stream->print(ESPHOME_F("\n"));
stream->print(ESPHOME_F("esphome_media_player_volume{id=\""));
stream->print(relabel_id_(obj).c_str());
this->print_relabel_id_(stream, obj);
add_area_label_(stream, area);
add_node_label_(stream, node);
add_friendly_name_label_(stream, friendly_name);
stream->print(ESPHOME_F("\",name=\""));
stream->print(relabel_name_(obj).c_str());
this->print_relabel_name_(stream, obj);
stream->print(ESPHOME_F("\"} "));
stream->print(obj->volume);
stream->print(ESPHOME_F("\n"));
stream->print(ESPHOME_F("esphome_media_player_is_muted{id=\""));
stream->print(relabel_id_(obj).c_str());
this->print_relabel_id_(stream, obj);
add_area_label_(stream, area);
add_node_label_(stream, node);
add_friendly_name_label_(stream, friendly_name);
stream->print(ESPHOME_F("\",name=\""));
stream->print(relabel_name_(obj).c_str());
this->print_relabel_name_(stream, obj);
stream->print(ESPHOME_F("\"} "));
if (obj->is_muted()) {
stream->print(ESPHOME_F("1.0"));
@@ -818,21 +823,21 @@ void PrometheusHandler::update_entity_row_(AsyncResponseStream *stream, update::
if (obj->has_state()) {
// We have a valid value, output this value
stream->print(ESPHOME_F("esphome_update_entity_failed{id=\""));
stream->print(relabel_id_(obj).c_str());
this->print_relabel_id_(stream, obj);
add_area_label_(stream, area);
add_node_label_(stream, node);
add_friendly_name_label_(stream, friendly_name);
stream->print(ESPHOME_F("\",name=\""));
stream->print(relabel_name_(obj).c_str());
this->print_relabel_name_(stream, obj);
stream->print(ESPHOME_F("\"} 0\n"));
// First update state
stream->print(ESPHOME_F("esphome_update_entity_state{id=\""));
stream->print(relabel_id_(obj).c_str());
this->print_relabel_id_(stream, obj);
add_area_label_(stream, area);
add_node_label_(stream, node);
add_friendly_name_label_(stream, friendly_name);
stream->print(ESPHOME_F("\",name=\""));
stream->print(relabel_name_(obj).c_str());
this->print_relabel_name_(stream, obj);
stream->print(ESPHOME_F("\",value=\""));
handle_update_state_(stream, obj->state);
stream->print(ESPHOME_F("\"} "));
@@ -840,12 +845,12 @@ void PrometheusHandler::update_entity_row_(AsyncResponseStream *stream, update::
stream->print(ESPHOME_F("\n"));
// Next update info
stream->print(ESPHOME_F("esphome_update_entity_info{id=\""));
stream->print(relabel_id_(obj).c_str());
this->print_relabel_id_(stream, obj);
add_area_label_(stream, area);
add_node_label_(stream, node);
add_friendly_name_label_(stream, friendly_name);
stream->print(ESPHOME_F("\",name=\""));
stream->print(relabel_name_(obj).c_str());
this->print_relabel_name_(stream, obj);
stream->print(ESPHOME_F("\",current_version=\""));
stream->print(obj->update_info.current_version.c_str());
stream->print(ESPHOME_F("\",latest_version=\""));
@@ -858,12 +863,12 @@ void PrometheusHandler::update_entity_row_(AsyncResponseStream *stream, update::
} else {
// Invalid state
stream->print(ESPHOME_F("esphome_update_entity_failed{id=\""));
stream->print(relabel_id_(obj).c_str());
this->print_relabel_id_(stream, obj);
add_area_label_(stream, area);
add_node_label_(stream, node);
add_friendly_name_label_(stream, friendly_name);
stream->print(ESPHOME_F("\",name=\""));
stream->print(relabel_name_(obj).c_str());
this->print_relabel_name_(stream, obj);
stream->print(ESPHOME_F("\"} 1\n"));
}
}
@@ -881,21 +886,21 @@ void PrometheusHandler::valve_row_(AsyncResponseStream *stream, valve::Valve *ob
if (obj->is_internal() && !this->include_internal_)
return;
stream->print(ESPHOME_F("esphome_valve_failed{id=\""));
stream->print(relabel_id_(obj).c_str());
this->print_relabel_id_(stream, obj);
add_area_label_(stream, area);
add_node_label_(stream, node);
add_friendly_name_label_(stream, friendly_name);
stream->print(ESPHOME_F("\",name=\""));
stream->print(relabel_name_(obj).c_str());
this->print_relabel_name_(stream, obj);
stream->print(ESPHOME_F("\"} 0\n"));
// Data itself
stream->print(ESPHOME_F("esphome_valve_operation{id=\""));
stream->print(relabel_id_(obj).c_str());
this->print_relabel_id_(stream, obj);
add_area_label_(stream, area);
add_node_label_(stream, node);
add_friendly_name_label_(stream, friendly_name);
stream->print(ESPHOME_F("\",name=\""));
stream->print(relabel_name_(obj).c_str());
this->print_relabel_name_(stream, obj);
stream->print(ESPHOME_F("\",operation=\""));
stream->print(reinterpret_cast<ProgmemStr>(valve::valve_operation_to_str(obj->current_operation)));
stream->print(ESPHOME_F("\"} "));
@@ -904,12 +909,12 @@ void PrometheusHandler::valve_row_(AsyncResponseStream *stream, valve::Valve *ob
// Now see if position is supported
if (obj->get_traits().get_supports_position()) {
stream->print(ESPHOME_F("esphome_valve_position{id=\""));
stream->print(relabel_id_(obj).c_str());
this->print_relabel_id_(stream, obj);
add_area_label_(stream, area);
add_node_label_(stream, node);
add_friendly_name_label_(stream, friendly_name);
stream->print(ESPHOME_F("\",name=\""));
stream->print(relabel_name_(obj).c_str());
this->print_relabel_name_(stream, obj);
stream->print(ESPHOME_F("\"} "));
stream->print(obj->position);
stream->print(ESPHOME_F("\n"));
@@ -928,12 +933,12 @@ void PrometheusHandler::climate_setting_row_(AsyncResponseStream *stream, climat
std::string &node, std::string &friendly_name, std::string &setting,
const LogString *setting_value) {
stream->print(ESPHOME_F("esphome_climate_setting{id=\""));
stream->print(relabel_id_(obj).c_str());
this->print_relabel_id_(stream, obj);
add_area_label_(stream, area);
add_node_label_(stream, node);
add_friendly_name_label_(stream, friendly_name);
stream->print(ESPHOME_F("\",name=\""));
stream->print(relabel_name_(obj).c_str());
this->print_relabel_name_(stream, obj);
stream->print(ESPHOME_F("\",category=\""));
stream->print(setting.c_str());
stream->print(ESPHOME_F("\",setting_value=\""));
@@ -951,12 +956,12 @@ void PrometheusHandler::climate_value_row_(AsyncResponseStream *stream, climate:
std::string &node, std::string &friendly_name, std::string &category,
const char *climate_value) {
stream->print(ESPHOME_F("esphome_climate_value{id=\""));
stream->print(relabel_id_(obj).c_str());
this->print_relabel_id_(stream, obj);
add_area_label_(stream, area);
add_node_label_(stream, node);
add_friendly_name_label_(stream, friendly_name);
stream->print(ESPHOME_F("\",name=\""));
stream->print(relabel_name_(obj).c_str());
this->print_relabel_name_(stream, obj);
stream->print(ESPHOME_F("\",category=\""));
stream->print(category.c_str());
stream->print(ESPHOME_F("\"} "));
@@ -968,12 +973,12 @@ void PrometheusHandler::climate_failed_row_(AsyncResponseStream *stream, climate
std::string &node, std::string &friendly_name, std::string &category,
bool is_failed_value) {
stream->print(ESPHOME_F("esphome_climate_failed{id=\""));
stream->print(relabel_id_(obj).c_str());
this->print_relabel_id_(stream, obj);
add_area_label_(stream, area);
add_node_label_(stream, node);
add_friendly_name_label_(stream, friendly_name);
stream->print(ESPHOME_F("\",name=\""));
stream->print(relabel_name_(obj).c_str());
this->print_relabel_name_(stream, obj);
stream->print(ESPHOME_F("\",category=\""));
stream->print(category.c_str());
stream->print(ESPHOME_F("\"} "));
@@ -63,8 +63,8 @@ class PrometheusHandler final : public AsyncWebHandler, public Component {
}
protected:
std::string relabel_id_(EntityBase *obj);
std::string relabel_name_(EntityBase *obj);
void print_relabel_id_(AsyncResponseStream *stream, EntityBase *obj);
void print_relabel_name_(AsyncResponseStream *stream, EntityBase *obj);
void add_area_label_(AsyncResponseStream *stream, std::string &area);
void add_node_label_(AsyncResponseStream *stream, std::string &node);
void add_friendly_name_label_(AsyncResponseStream *stream, std::string &friendly_name);
+11 -6
View File
@@ -218,7 +218,7 @@ EntityMatchResult UrlMatch::match_entity(EntityBase *entity) const {
#endif
// Match by entity name
if (this->id == entity->get_name()) {
if (entity->name_equals(this->id)) {
result.matched = true;
}
@@ -651,9 +651,7 @@ void WebServer::handle_js_request(AsyncWebServerRequest *request) {
// Build unique id as: {domain}/{device_name}/{entity_name} or {domain}/{entity_name}
// Uses names (not object_id) to avoid UTF-8 collision issues
static void set_json_id(JsonObject root, EntityBase *obj, const char *prefix, JsonDetail start_config) {
const StringRef &name = obj->get_name();
size_t prefix_len = strlen(prefix);
size_t name_len = name.size();
#ifdef USE_DEVICES
Device *device = obj->get_device();
@@ -683,13 +681,20 @@ static void set_json_id(JsonObject root, EntityBase *obj, const char *prefix, Js
*p++ = '/';
}
#endif
memcpy(p, name.c_str(), name_len);
p[name_len] = '\0';
#ifdef USE_ESP8266
const char *name = p;
#endif
obj->write_name_to(p, id_buf + ID_BUF_SIZE - p);
root[ESPHOME_F("id")] = static_cast<const char *>(id_buf);
if (start_config == DETAIL_ALL) {
root[ESPHOME_F("domain")] = linked(prefix, prefix_len);
root[ESPHOME_F("name")] = linked(name);
#ifdef USE_ESP8266
root[ESPHOME_F("name")] = name; // in id_buf on the stack, so copied
#else
const StringRef &ram_name = obj->get_name_progmem().ram_ref();
root[ESPHOME_F("name")] = linked(ram_name.c_str(), ram_name.size());
#endif
#ifdef USE_DEVICES
if (device_name) {
root[ESPHOME_F("device")] = linked(device_name, device_len);
@@ -42,8 +42,10 @@ void write_row(AsyncResponseStream *stream, EntityBase *obj, const std::string &
// Add data attributes for hierarchical URL support
stream->print("\" data-domain=\"");
stream->print(klass.c_str());
char name_buf[ENTITY_NAME_BUF_SIZE];
const char *name = obj->get_name_to(name_buf).c_str();
stream->print("\" data-name=\"");
write_html_escaped(stream, obj->get_name().c_str());
write_html_escaped(stream, name);
#ifdef USE_DEVICES
Device *device = obj->get_device();
if (device != nullptr) {
@@ -59,7 +61,7 @@ void write_row(AsyncResponseStream *stream, EntityBase *obj, const std::string &
stream->print("] ");
}
#endif
write_html_escaped(stream, obj->get_name().c_str());
write_html_escaped(stream, name);
stream->print("</td><td></td><td>");
stream->print(action.c_str());
if (action_func) {
+1
View File
@@ -2471,6 +2471,7 @@ def _validate_no_slash(value):
# This ensures web server URL IDs fit in a 280-byte buffer:
# domain(20) + "/" + device(120) + "/" + name(120) + null = 263 bytes
# Note: Must be < 255 because web_server UrlMatch uses uint8_t for length fields
# Keep in sync with ESPHOME_ENTITY_NAME_MAX_LEN in esphome/core/entity_base.h
NAME_MAX_LENGTH = 120
+62 -20
View File
@@ -1,20 +1,64 @@
#include "esphome/core/entity_base.h"
#include "esphome/core/application.h"
#include "esphome/core/hal.h"
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
#include "esphome/core/progmem.h"
#include "esphome/core/string_ref.h"
#include <new>
namespace esphome {
ESPHOME_LOG_TAG(TAG, "entity_base");
#ifdef USE_ESP8266
namespace {
// fnv1_hash_object_id() for a name in flash
uint32_t fnv1_hash_object_id_progmem(const char *str, size_t len) {
uint32_t hash = FNV1_OFFSET_BASIS;
for (size_t i = 0; i < len; i++) {
hash *= FNV1_PRIME;
char c = static_cast<char>(progmem_read_byte(reinterpret_cast<const uint8_t *>(str + i)));
hash ^= static_cast<uint8_t>(to_sanitized_char(to_snake_case_char(c)));
}
return hash;
}
// RAM copies made by get_name(), only for entities whose name is asked for
struct NameCopy {
NameCopy *next;
const char *key;
StringRef ref;
};
NameCopy *name_copies = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
const StringRef EMPTY_NAME;
} // namespace
const StringRef &EntityBase::get_name() const {
const char *key = this->name_.progmem_ptr();
for (NameCopy *copy = name_copies; copy != nullptr; copy = copy->next) {
if (copy->key == key)
return copy->ref;
}
size_t len = this->name_.size();
void *mem = malloc(sizeof(NameCopy) + len + 1); // NOLINT(cppcoreguidelines-no-malloc)
if (mem == nullptr)
return EMPTY_NAME;
char *buf = static_cast<char *>(mem) + sizeof(NameCopy);
this->name_.write_to(buf, len + 1);
name_copies = new (mem) NameCopy{name_copies, key, StringRef(buf, len)};
return name_copies->ref;
}
#endif
void EntityBase::configure_entity_(const char *name, uint32_t object_id_hash, uint32_t entity_fields) {
this->name_ = StringRef(name);
this->name_ = ProgmemStringRef(name, ESPHOME_strlen_P(name));
if (this->name_.empty()) {
StringRef fallback;
#ifdef USE_DEVICES
if (this->device_ != nullptr) {
this->name_ = StringRef(this->device_->get_name());
fallback = StringRef(this->device_->get_name());
} else
#endif
{
@@ -24,23 +68,27 @@ void EntityBase::configure_entity_(const char *name, uint32_t object_id_hash, ui
const auto &friendly = App.get_friendly_name();
if (App.is_name_add_mac_suffix_enabled()) {
// MAC suffix enabled - use friendly_name directly (even if empty) for compatibility
this->name_ = friendly;
fallback = friendly;
} else {
// No MAC suffix - fallback to device name if friendly_name is empty
this->name_ = !friendly.empty() ? friendly : App.get_name();
fallback = !friendly.empty() ? friendly : App.get_name();
}
}
this->name_ = ProgmemStringRef(fallback);
this->flags_.has_own_name = false;
// Dynamic name - must calculate hash at runtime
this->calc_object_id_();
// Dynamic name in RAM - hash it at runtime
this->object_id_hash_ = fnv1_hash_object_id(fallback.c_str(), fallback.size());
} else {
this->flags_.has_own_name = true;
// Static name - use pre-computed hash if provided
if (object_id_hash != 0) {
this->object_id_hash_ = object_id_hash;
} else {
this->calc_object_id_();
// Static name - codegen precomputes the hash; external register_* callers may pass 0
if (object_id_hash == 0) {
#ifdef USE_ESP8266
object_id_hash = fnv1_hash_object_id_progmem(this->name_.progmem_ptr(), this->name_.size());
#else
object_id_hash = fnv1_hash_object_id(this->name_.progmem_ptr(), this->name_.size());
#endif
}
this->object_id_hash_ = object_id_hash;
}
// Unpack entity string table indices and flags from entity_fields.
#ifdef USE_ENTITY_DEVICE_CLASS
@@ -61,7 +109,7 @@ void EntityBase::set_internal(bool internal) {
// Remove the after-setup path in 2027.3.0 and ignore the call instead.
if (App.is_setup_complete()) {
ESP_LOGE(TAG, "'%s': set_internal() after setup is undefined behavior, stops working in 2027.3.0",
this->get_name().c_str());
LOG_STR_ARG(this->get_log_name()));
}
this->flags_.internal = internal;
}
@@ -117,17 +165,11 @@ const char *EntityBase::get_icon_to([[maybe_unused]] std::span<char, MAX_ICON_LE
#endif
}
// Calculate Object ID Hash directly from name using snake_case + sanitize
void EntityBase::calc_object_id_() {
this->object_id_hash_ = fnv1_hash_object_id(this->name_.c_str(), this->name_.size());
}
size_t EntityBase::write_object_id_to(char *buf, size_t buf_size) const {
size_t len = std::min(this->name_.size(), buf_size - 1);
size_t len = this->write_name_to(buf, buf_size);
for (size_t i = 0; i < len; i++) {
buf[i] = to_sanitized_char(to_snake_case_char(this->name_[i]));
buf[i] = to_sanitized_char(to_snake_case_char(buf[i]));
}
buf[len] = '\0';
return len;
}
+28 -5
View File
@@ -31,6 +31,11 @@ static constexpr size_t ESPHOME_DEVICE_NAME_MAX_LEN = 31;
// esphome/core/config.py
static constexpr size_t ESPHOME_FRIENDLY_NAME_MAX_LEN = 120;
// Maximum entity name length - keep in sync with NAME_MAX_LENGTH in esphome/config_validation.py
static constexpr size_t ESPHOME_ENTITY_NAME_MAX_LEN = 120;
static constexpr size_t ENTITY_NAME_BUF_SIZE = ESPHOME_ENTITY_NAME_MAX_LEN + 1;
// Maximum domain length (longest: "alarm_control_panel" = 19)
static constexpr size_t ESPHOME_DOMAIN_MAX_LEN = 20;
@@ -67,8 +72,28 @@ static constexpr uint8_t ENTITY_FIELD_ENTITY_CATEGORY_SHIFT = 26;
// The generic Entity base class that provides an interface common to all Entities.
class EntityBase {
public:
// Get the name of this Entity
const StringRef &get_name() const { return this->name_; }
#ifdef USE_ESP8266
/// The name is in flash here; the first call makes a RAM copy that is kept for the life of the device.
const StringRef &get_name() const;
/// Get the name of this Entity, copied out of flash into buffer.
StringRef get_name_to(std::span<char, ENTITY_NAME_BUF_SIZE> buffer) const {
return StringRef(buffer.data(), this->write_name_to(buffer.data(), buffer.size()));
}
#else
const StringRef &get_name() const { return this->name_.ram_ref(); }
const StringRef &get_name_to(std::span<char, ENTITY_NAME_BUF_SIZE> /*buffer*/) const { return this->name_.ram_ref(); }
#endif
bool name_equals(const StringRef &other) const { return this->name_.equals(other); }
/// The name as stored, for consumers that read flash themselves (e.g. API progmem fields).
const ProgmemStringRef &get_name_progmem() const { return this->name_; }
/// Copy the name into buf (null terminated), returns its length.
size_t write_name_to(char *buf, size_t buf_size) const { return this->name_.write_to(buf, buf_size); }
/// Get the name for a "%s" log argument (LOG_STR_ARG).
const LogString *get_log_name() const { return reinterpret_cast<const LogString *>(this->name_.progmem_ptr()); }
// Get whether this Entity has its own name or it should use the device friendly_name.
bool has_own_name() const { return this->flags_.has_own_name; }
@@ -210,9 +235,7 @@ class EntityBase {
/// When the preference hash algorithm changes, migration logic goes here.
ESPPreferenceObject make_entity_preference_(size_t size, uint32_t version);
void calc_object_id_();
StringRef name_;
ProgmemStringRef name_;
uint32_t object_id_hash_{};
#ifdef USE_DEVICES
Device *device_{};
+10 -2
View File
@@ -23,7 +23,13 @@ from esphome.core.config import (
ICON_MAX_LENGTH,
UNIT_OF_MEASUREMENT_MAX_LENGTH,
)
from esphome.cpp_generator import MockObj, RawStatement, add, get_variable
from esphome.cpp_generator import (
MockObj,
RawExpression,
RawStatement,
add,
get_variable,
)
from esphome.cpp_types import App
import esphome.final_validate as fv
from esphome.helpers import (
@@ -306,7 +312,9 @@ def finalize_entity_strings(var: MockObj, config: ConfigType) -> None:
``App.register_<method>(var, name, hash, packed)``. Otherwise falls back to a
standalone ``var->configure_entity_(name, hash, packed)``.
"""
entity_name = config[_KEY_ENTITY_NAME]
entity_name = RawExpression(
f"ESPHOME_PSTR({cpp_string_escape(config[_KEY_ENTITY_NAME])})"
)
object_id_hash = config[_KEY_OBJECT_ID_HASH]
dc_idx = config.get(_KEY_DC_IDX, 0)
uom_idx = config.get(_KEY_UOM_IDX, 0)
+61 -18
View File
@@ -1,5 +1,6 @@
#pragma once
#include <algorithm>
#include <cstdint>
#include <cstring>
#include <iterator>
@@ -189,24 +190,6 @@ inline bool operator!=(const StringRef &lhs, const __FlashStringHelper *rhs) { r
inline bool operator!=(const __FlashStringHelper *lhs, const StringRef &rhs) { return !(rhs == lhs); }
/// Codegen use only: a flash string literal with its byte length, compared without a runtime strlen.
struct ProgmemStringRef {
PGM_P str;
size_t len;
};
inline bool operator==(const std::string &lhs, ProgmemStringRef rhs) {
return lhs.size() == rhs.len && (rhs.len == 0 || memcmp_P(lhs.data(), rhs.str, rhs.len) == 0);
}
inline bool operator!=(const std::string &lhs, ProgmemStringRef rhs) { return !(lhs == rhs); }
inline bool operator==(const StringRef &lhs, ProgmemStringRef rhs) {
// A StringRef may be {nullptr, 0}, and memcmp_P with a null pointer is undefined even for 0 bytes
return lhs.size() == rhs.len && (rhs.len == 0 || memcmp_P(lhs.c_str(), rhs.str, rhs.len) == 0);
}
inline bool operator!=(const StringRef &lhs, ProgmemStringRef rhs) { return !(lhs == rhs); }
#endif // USE_ESP8266
inline bool operator<(const StringRef &lhs, const StringRef &rhs) {
@@ -241,6 +224,66 @@ inline std::string operator+(const std::string &lhs, const StringRef &rhs) {
str.append(rhs.c_str(), rhs.size());
return str;
}
/// A string that may live in flash (on ESP8266): pointer and length, with no byte-reading operations.
/// progmem_ptr() is safe as a "%s" log argument; read the contents with write_to() or equals().
class ProgmemStringRef {
public:
constexpr ProgmemStringRef() = default;
constexpr ProgmemStringRef(const char *s, size_t len) : ref_(s, len) {}
constexpr explicit ProgmemStringRef(const StringRef &s) : ref_(s) {}
constexpr const char *progmem_ptr() const { return this->ref_.c_str(); }
constexpr size_t size() const { return this->ref_.size(); }
constexpr bool empty() const { return this->ref_.empty(); }
/// Copy into buf (null terminated, truncated to fit), returns the length copied.
size_t write_to(char *buf, size_t buf_size) const {
if (buf_size == 0)
return 0;
size_t len = std::min(this->ref_.size(), buf_size - 1);
#ifdef USE_ESP8266
memcpy_P(buf, this->ref_.c_str(), len);
#else
std::memcpy(buf, this->ref_.c_str(), len);
#endif
buf[len] = '\0';
return len;
}
bool equals(const StringRef &other) const {
#ifdef USE_ESP8266
// A StringRef may be {nullptr, 0}, and memcmp_P with a null pointer is undefined even for 0 bytes
return other.size() == this->ref_.size() &&
(other.empty() || memcmp_P(other.c_str(), this->ref_.c_str(), other.size()) == 0);
#else
return other == this->ref_;
#endif
}
#ifndef USE_ESP8266
/// The string is in RAM here, so it can be viewed directly.
constexpr const StringRef &ram_ref() const { return this->ref_; }
#endif
// Remove before 2027.5.0
[[deprecated(
"May point to flash; use progmem_ptr() or the owner's accessors. Removed in 2027.5.0")]] constexpr const char *
c_str() const {
return this->ref_.c_str();
}
private:
StringRef ref_;
};
#ifdef USE_ESP8266
inline bool operator==(const StringRef &lhs, const ProgmemStringRef &rhs) { return rhs.equals(lhs); }
inline bool operator!=(const StringRef &lhs, const ProgmemStringRef &rhs) { return !rhs.equals(lhs); }
inline bool operator==(const std::string &lhs, const ProgmemStringRef &rhs) { return rhs.equals(StringRef(lhs)); }
inline bool operator!=(const std::string &lhs, const ProgmemStringRef &rhs) { return !rhs.equals(StringRef(lhs)); }
#endif
// String conversion functions for ADL compatibility (allows stoi(x) where x is StringRef)
// Must be in esphome namespace for ADL to find them. Uses strtol/strtod directly to avoid heap allocation.
namespace internal {
+43 -18
View File
@@ -536,16 +536,19 @@ def create_field_type_info(
f"track_presence on field '{field.name}' has no effect; it requires "
"a non-repeated message field in a message that is decoded"
)
if get_field_opt(field, pb.progmem, False) and (
field.label == FieldDescriptorProto.LABEL_REPEATED
or field.type != 12
or not get_field_opt(field, pb.pointer_to_buffer, False)
or get_field_opt(field, pb.fixed_array_size) is not None
):
raise ValueError(
f"progmem on field '{field.name}' requires a non-repeated bytes field "
"with pointer_to_buffer"
if get_field_opt(field, pb.progmem, False):
is_pointer_bytes = (
field.type == FieldDescriptorProto.TYPE_BYTES
and get_field_opt(field, pb.pointer_to_buffer, False)
and get_field_opt(field, pb.fixed_array_size) is None
)
if field.label == FieldDescriptorProto.LABEL_REPEATED or not (
field.type == FieldDescriptorProto.TYPE_STRING or is_pointer_bytes
):
raise ValueError(
f"progmem on field '{field.name}' requires a non-repeated string field, "
"or a bytes field with pointer_to_buffer"
)
if field.label == FieldDescriptorProto.LABEL_REPEATED:
# Check if this is a packed_buffer field (zero-copy packed repeated)
if get_field_opt(field, pb.packed_buffer, False):
@@ -1155,6 +1158,11 @@ class PointerToBufferTypeBase(TypeInfo):
"""Get the wire type for this field."""
return WireType.LENGTH_DELIMITED # Uses wire type 2
@property
def progmem(self) -> bool:
"""Whether the data is in flash, so encode and dump copy it with progmem_memcpy."""
return get_field_opt(self._field, pb.progmem, False)
def get_estimated_size(self) -> int:
# field ID + length varint + typical data (assume small for pointer fields)
return self.calculate_field_id_size() + 2 + 16
@@ -1168,11 +1176,6 @@ class PointerToBytesBufferType(PointerToBufferTypeBase):
reference_type = "const uint8_t*"
const_reference_type = "const uint8_t*"
@property
def progmem(self) -> bool:
"""Whether the data is in flash, so encode and dump copy it with progmem_memcpy."""
return get_field_opt(self._field, pb.progmem, False)
@property
def public_content(self) -> list[str]:
# Use uint16_t for length - max packet size is well below 65535
@@ -1252,6 +1255,8 @@ class PointerToStringBufferType(PointerToBufferTypeBase):
@property
def public_content(self) -> list[str]:
if self.progmem:
return [f"ProgmemStringRef {self.field_name}{{}};"]
if self._starts_null:
return [
f"StringRef {self.field_name}{{nullptr, 0}}; // null until set, encode only"
@@ -1267,9 +1272,19 @@ class PointerToStringBufferType(PointerToBufferTypeBase):
)
tag = self.calculate_tag()
if tag < 128:
return _encode_call(
"encode_short_string_force", str(tag), f"this->{self.field_name}"
encode_fn = (
"encode_short_progmem_string_force"
if self.progmem
else "encode_short_string_force"
)
return _encode_call(encode_fn, str(tag), f"this->{self.field_name}")
if self.progmem:
return _encode_call(
"encode_progmem_string",
str(self.number),
f"this->{self.field_name}",
force=self.force,
)
if result := self._encode_bytes_with_precomputed_tag(
f"this->{self.field_name}.c_str()",
f"this->{self.field_name}.size()",
@@ -1297,6 +1312,8 @@ class PointerToStringBufferType(PointerToBufferTypeBase):
@property
def dump_content(self) -> str:
if self.progmem:
return f'dump_progmem_string_field(out, ESPHOME_PSTR("{self.name}"), this->{self.field_name});'
return f'dump_field(out, ESPHOME_PSTR("{self.name}"), this->{self.field_name});'
def get_size_calculation(self, name: str, force: bool = False) -> str:
@@ -3246,8 +3263,9 @@ namespace esphome::api {
#ifdef USE_ESP8266
// Out-of-line to avoid inlining strlen_P/memcpy_P at every call site
void DumpBuffer::append_p_esp8266(const char *str) {
size_t len = strlen_P(str);
void DumpBuffer::append_p_esp8266(const char *str) { this->append_p_esp8266(str, strlen_P(str)); }
void DumpBuffer::append_p_esp8266(const char *str, size_t len) {
size_t space = CAPACITY - 1 - pos_;
if (len > space)
len = space;
@@ -3368,6 +3386,13 @@ static void dump_progmem_bytes_field(DumpBuffer &out, const char *field_name, co
progmem_memcpy(data_buf, data, len);
dump_bytes_field(out, field_name, data_buf, len, indent);
}
// Helper for string fields in flash
static void dump_progmem_string_field(DumpBuffer &out, const char *field_name, const ProgmemStringRef &value,
int indent = 2) {
append_field_prefix(out, field_name, indent);
out.append("'").append_p(value.progmem_ptr(), value.size()).append("'\\n");
}
#pragma GCC diagnostic pop
"""
@@ -16,7 +16,7 @@ static ListEntitiesSensorResponse make_sensor_response() {
ListEntitiesSensorResponse msg;
msg.object_id = StringRef::from_lit("living_room_temperature");
msg.key = 0x12345678;
msg.name = StringRef::from_lit("Living Room Temperature");
msg.name = ProgmemStringRef(StringRef::from_lit("Living Room Temperature"));
#ifdef USE_ENTITY_ICON
msg.icon = StringRef::from_lit("mdi:thermometer");
#endif
@@ -87,7 +87,7 @@ static ListEntitiesBinarySensorResponse make_binary_sensor_response() {
ListEntitiesBinarySensorResponse msg;
msg.object_id = StringRef::from_lit("front_door_contact");
msg.key = 0xAABBCCDD;
msg.name = StringRef::from_lit("Front Door Contact");
msg.name = ProgmemStringRef(StringRef::from_lit("Front Door Contact"));
#ifdef USE_ENTITY_ICON
msg.icon = StringRef::from_lit("mdi:door");
#endif
@@ -176,7 +176,7 @@ static ListEntitiesLightResponse make_light_response() {
ListEntitiesLightResponse msg;
msg.object_id = StringRef::from_lit("kitchen_ceiling_light");
msg.key = 0x55667788;
msg.name = StringRef::from_lit("Kitchen Ceiling Light");
msg.name = ProgmemStringRef(StringRef::from_lit("Kitchen Ceiling Light"));
#ifdef USE_ENTITY_ICON
msg.icon = StringRef::from_lit("mdi:ceiling-light");
#endif
+2 -1
View File
@@ -739,7 +739,8 @@ def blob_in(main_cpp: str) -> bytes:
def entity_hash(main_cpp: str, variable: str) -> int:
"""The key the generated code passes to configure_entity_, which ListEntities then sends."""
match = re.search(
rf'App\.register_switch\({variable}, "[^"]*", (\d+)UL, \d+\)', main_cpp
rf'App\.register_switch\({variable}, ESPHOME_PSTR\("[^"]*"\), (\d+)UL, \d+\)',
main_cpp,
)
assert match is not None, variable
return int(match.group(1))
@@ -31,7 +31,7 @@ def test_binary_sensor_sets_mandatory_fields(generate_main):
)
# Then
assert 'App.register_binary_sensor(bs_1, "test bs1",' in main_cpp
assert 'App.register_binary_sensor(bs_1, ESPHOME_PSTR("test bs1"),' in main_cpp
assert "bs_1->set_pin(" in main_cpp
+1 -1
View File
@@ -29,7 +29,7 @@ def test_button_sets_mandatory_fields(generate_main):
main_cpp = generate_main("tests/component_tests/button/test_button.yaml")
# Then
assert 'App.register_button(wol_1, "wol_test_1",' in main_cpp
assert 'App.register_button(wol_1, ESPHOME_PSTR("wol_test_1"),' in main_cpp
assert "wol_2->set_macaddr(18, 52, 86, 120, 144, 171);" in main_cpp
+3 -6
View File
@@ -14,15 +14,12 @@ def extract_packed_value(main_cpp: str, var_name: str) -> int:
combined form ``App.register_<entity>(var, name, hash, packed)``.
"""
escaped_var = re.escape(var_name)
name_re = r'ESPHOME_PSTR\("(?:\\.|[^"\\])*"\)'
legacy_pattern = (
rf"{escaped_var}->configure_entity_\("
r'"(?:\\.|[^"\\])*"'
r",\s*\w+,\s*(\d+)\)"
rf"{escaped_var}->configure_entity_\(" + name_re + r",\s*\w+,\s*(\d+)\)"
)
combined_pattern = (
rf"App\.register_\w+\(\s*{escaped_var}\s*,\s*"
r'"(?:\\.|[^"\\])*"'
r",\s*\w+,\s*(\d+)\)"
rf"App\.register_\w+\(\s*{escaped_var}\s*,\s*" + name_re + r",\s*\w+,\s*(\d+)\)"
)
match = re.search(combined_pattern, main_cpp) or re.search(legacy_pattern, main_cpp)
assert match, f"configure call not found for {var_name}"
+1 -1
View File
@@ -28,7 +28,7 @@ def test_text_sets_mandatory_fields(generate_main):
main_cpp = generate_main("tests/component_tests/text/test_text.yaml")
# Then
assert 'App.register_text(it_1, "test 1 text",' in main_cpp
assert 'App.register_text(it_1, ESPHOME_PSTR("test 1 text"),' in main_cpp
def test_text_config_value_internal_set(generate_main):
@@ -31,9 +31,9 @@ def test_text_sensor_sets_mandatory_fields(generate_main):
main_cpp = generate_main("tests/component_tests/text_sensor/test_text_sensor.yaml")
# Then
assert 'App.register_text_sensor(ts_1, "Template Text Sensor 1",' in main_cpp
assert 'App.register_text_sensor(ts_2, "Template Text Sensor 2",' in main_cpp
assert 'App.register_text_sensor(ts_3, "Template Text Sensor 3",' in main_cpp
for i in (1, 2, 3):
name = f'ESPHOME_PSTR("Template Text Sensor {i}")'
assert f"App.register_text_sensor(ts_{i}, {name}," in main_cpp
def test_text_sensor_config_value_internal_set(generate_main):
@@ -0,0 +1,33 @@
#include <gtest/gtest.h>
#include "esphome/core/entity_base.h"
#include <type_traits>
#include <utility>
namespace esphome::core::testing {
// External components return get_name() from `const StringRef &` getters, so it must stay a reference
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
static_assert(std::is_same_v<decltype(std::declval<const EntityBase &>().get_name()), const StringRef &>);
#pragma GCC diagnostic pop
class TestEntity : public EntityBase {
public:
void configure(const char *name, uint32_t object_id_hash) { this->configure_entity_(name, object_id_hash, 0); }
};
TEST(EntityBaseName, ZeroHashIsComputedFromTheName) {
TestEntity entity;
entity.configure("Living Room Temp", 0);
EXPECT_EQ(entity.get_object_id_hash(), fnv1_hash_object_id("Living Room Temp", 16));
}
TEST(EntityBaseName, PrecomputedHashIsKept) {
TestEntity entity;
entity.configure("Living Room Temp", 0x1234);
EXPECT_EQ(entity.get_object_id_hash(), 0x1234u);
}
} // namespace esphome::core::testing
@@ -0,0 +1,49 @@
#include <gtest/gtest.h>
#include "esphome/core/string_ref.h"
#include <type_traits>
#include <utility>
namespace esphome::core::testing {
// ram_ref() must return a reference so callers can hand it out as `const StringRef &`
static_assert(std::is_same_v<decltype(std::declval<const ProgmemStringRef &>().ram_ref()), const StringRef &>);
TEST(ProgmemStringRef, WriteToCopiesAndTerminates) {
ProgmemStringRef ref("Kitchen", 7);
char buf[16];
EXPECT_EQ(ref.write_to(buf, sizeof(buf)), 7u);
EXPECT_STREQ(buf, "Kitchen");
}
TEST(ProgmemStringRef, WriteToTruncatesToFit) {
ProgmemStringRef ref("Kitchen", 7);
char buf[4];
EXPECT_EQ(ref.write_to(buf, sizeof(buf)), 3u);
EXPECT_STREQ(buf, "Kit");
}
TEST(ProgmemStringRef, WriteToZeroSizeWritesNothing) {
ProgmemStringRef ref("Kitchen", 7);
char buf[1] = {'x'};
EXPECT_EQ(ref.write_to(buf, 0), 0u);
EXPECT_EQ(buf[0], 'x');
}
TEST(ProgmemStringRef, EqualsComparesLengthAndContent) {
ProgmemStringRef ref("Kitchen", 7);
EXPECT_TRUE(ref.equals(StringRef("Kitchen")));
EXPECT_FALSE(ref.equals(StringRef("Kitche")));
EXPECT_FALSE(ref.equals(StringRef("Kitchens")));
EXPECT_FALSE(ref.equals(StringRef("Bedroom")));
}
TEST(ProgmemStringRef, DefaultIsEmpty) {
ProgmemStringRef ref;
EXPECT_TRUE(ref.empty());
EXPECT_EQ(ref.size(), 0u);
EXPECT_TRUE(ref.equals(StringRef("")));
}
} // namespace esphome::core::testing
@@ -408,8 +408,39 @@ def test_progmem_rejected_in_a_decoded_message() -> None:
def test_progmem_rejected_on_non_bytes_field() -> None:
"""(progmem) only applies to bytes fields."""
field = _field(9)
"""(progmem) only applies to string and bytes fields."""
field = _field(13)
field.options.Extensions[pb.progmem] = True
with pytest.raises(ValueError, match="progmem on field 'value'"):
create_field_type_info(field, needs_decode=False)
@pytest.mark.parametrize(
("force", "max_len", "encode_fn"),
[
(True, 120, "encode_short_progmem_string_force("),
(False, None, "encode_progmem_string("),
(True, None, "encode_progmem_string_force("),
],
)
def test_progmem_string_field_copies_from_flash(
force: bool, max_len: int | None, encode_fn: str
) -> None:
"""A (progmem) string field is a ProgmemStringRef that encodes and dumps through the progmem helpers."""
field = _field(9, force=force)
field.options.Extensions[pb.progmem] = True
if max_len is not None:
field.options.Extensions[pb.max_data_length] = max_len
ti = create_field_type_info(field, needs_decode=False)
assert "ProgmemStringRef value{};" in ti.public_content
assert encode_fn in ti.encode_content
assert "encode_string" not in ti.encode_content
assert "dump_progmem_string_field(" in ti.dump_content
def test_progmem_rejected_on_repeated_string() -> None:
"""(progmem) on a repeated string field fails instead of silently using memcpy."""
field = _field(9, repeated=True)
field.options.Extensions[pb.progmem] = True
with pytest.raises(ValueError, match="progmem on field 'value'"):
create_field_type_info(field, needs_decode=False)
+4 -5
View File
@@ -38,11 +38,8 @@ from esphome.helpers import fnv1_hash, sanitize, snake_case
from .common import load_config_from_fixture
# Pre-compiled regex pattern for extracting names from configure_entity_/set_name calls
# Matches: .configure_entity_("name", ...) or .set_name("name", ...)
ENTITY_NAME_PATTERN = re.compile(
r'\.(?:configure_entity_|set_name)\(["\']([^"\']*)["\']'
)
# Extracts the name from .configure_entity_(ESPHOME_PSTR("name"), ...)
ENTITY_NAME_PATTERN = re.compile(r'\.configure_entity_\(ESPHOME_PSTR\("([^"]*)"')
FIXTURES_DIR = Path(__file__).parent.parent / "fixtures" / "core" / "entity_helpers"
@@ -1076,6 +1073,8 @@ async def test_finalize_no_flags(setup_test_environment: list[str]) -> None:
packed = _extract_packed_value(added_expressions)
assert packed == 0
assert "//" not in added_expressions[0]
# Names go through ESPHOME_PSTR so they stay in flash on ESP8266
assert 'ESPHOME_PSTR("Test")' in added_expressions[0]
@pytest.mark.asyncio