diff --git a/esphome/core/entity_base.cpp b/esphome/core/entity_base.cpp index 990f84075d9..bf0f8f255fd 100644 --- a/esphome/core/entity_base.cpp +++ b/esphome/core/entity_base.cpp @@ -119,9 +119,7 @@ const char *EntityBase::get_icon_to([[maybe_unused]] std::spanget_name_to(buf); - this->object_id_hash_ = fnv1_hash_object_id(name.c_str(), name.size()); + this->object_id_hash_ = fnv1_hash_object_id_P(this->name_.progmem_ptr(), this->name_.size()); } size_t EntityBase::write_object_id_to(char *buf, size_t buf_size) const { diff --git a/esphome/core/helpers.h b/esphome/core/helpers.h index b88a9d70e93..e06553e5b53 100644 --- a/esphome/core/helpers.h +++ b/esphome/core/helpers.h @@ -1140,15 +1140,26 @@ template inline char *str_sanitize_to(char (&buffer)[N], const char *s /// This computes object_id hashes directly from names without creating an intermediate buffer. /// IMPORTANT: Must match Python fnv1_hash_object_id() in esphome/helpers.py. /// If you modify this function, update the Python version and tests in both places. -inline uint32_t fnv1_hash_object_id(const char *str, size_t len) { +template inline uint32_t fnv1_hash_object_id_with(const char *str, size_t len, ReadByte read) { uint32_t hash = FNV1_OFFSET_BASIS; for (size_t i = 0; i < len; i++) { hash *= FNV1_PRIME; // Apply snake_case (space->underscore, uppercase->lowercase) then sanitize - hash ^= static_cast(to_sanitized_char(to_snake_case_char(str[i]))); + hash ^= static_cast(to_sanitized_char(to_snake_case_char(read(str + i)))); } return hash; } +inline uint32_t fnv1_hash_object_id(const char *str, size_t len) { + return fnv1_hash_object_id_with(str, len, [](const char *p) { return *p; }); +} +/// fnv1_hash_object_id() for a string that may be in flash (PROGMEM). +inline uint32_t fnv1_hash_object_id_P(const char *str, size_t len) { +#ifdef USE_ESP8266 + return fnv1_hash_object_id_with(str, len, [](const char *p) { return static_cast(pgm_read_byte(p)); }); +#else + return fnv1_hash_object_id(str, len); +#endif +} // str_snprintf, str_sprintf moved to alloc_helpers.h - remove this comment before 2026.11.0 diff --git a/tests/components/core/test_helpers.cpp b/tests/components/core/test_helpers.cpp index 9fee0c723b5..8ac6e72e820 100644 --- a/tests/components/core/test_helpers.cpp +++ b/tests/components/core/test_helpers.cpp @@ -440,4 +440,12 @@ TEST(RAMAllocatorMakeUnique, ArrayFormAllocatesElements) { EXPECT_EQ(buf[255], 0xA5); } +TEST(Fnv1HashObjectId, ProgmemVariantMatches) { + // Values match Python fnv1_hash_object_id(); the _P variant must hash identically + static const char NAME[] = "Living Room Temp"; + const size_t len = sizeof(NAME) - 1; + EXPECT_EQ(fnv1_hash_object_id_P(NAME, len), fnv1_hash_object_id(NAME, len)); + EXPECT_EQ(fnv1_hash_object_id_P("", 0), fnv1_hash_object_id("", 0)); +} + } // namespace esphome::core::testing