ESPHome 2026.2.3
Loading...
Searching...
No Matches
entity_base.cpp
Go to the documentation of this file.
5
6namespace esphome {
7
8static const char *const TAG = "entity_base";
9
10// Entity Name
11const StringRef &EntityBase::get_name() const { return this->name_; }
12void EntityBase::set_name(const char *name) { this->set_name(name, 0); }
13void EntityBase::set_name(const char *name, uint32_t object_id_hash) {
14 this->name_ = StringRef(name);
15 if (this->name_.empty()) {
16#ifdef USE_DEVICES
17 if (this->device_ != nullptr) {
18 this->name_ = StringRef(this->device_->get_name());
19 } else
20#endif
21 {
22 // Bug-for-bug compatibility with OLD behavior:
23 // - With MAC suffix: OLD code used App.get_friendly_name() directly (no fallback)
24 // - Without MAC suffix: OLD code used pre-computed object_id with fallback to device name
25 const std::string &friendly = App.get_friendly_name();
27 // MAC suffix enabled - use friendly_name directly (even if empty) for compatibility
28 this->name_ = StringRef(friendly);
29 } else {
30 // No MAC suffix - fallback to device name if friendly_name is empty
31 this->name_ = StringRef(!friendly.empty() ? friendly : App.get_name());
32 }
33 }
34 this->flags_.has_own_name = false;
35 // Dynamic name - must calculate hash at runtime
36 this->calc_object_id_();
37 } else {
38 this->flags_.has_own_name = true;
39 // Static name - use pre-computed hash if provided
40 if (object_id_hash != 0) {
41 this->object_id_hash_ = object_id_hash;
42 } else {
43 this->calc_object_id_();
44 }
45 }
46}
47
48// Entity Icon
49std::string EntityBase::get_icon() const {
50#ifdef USE_ENTITY_ICON
51 if (this->icon_c_str_ == nullptr) {
52 return "";
53 }
54 return this->icon_c_str_;
55#else
56 return "";
57#endif
58}
59void EntityBase::set_icon(const char *icon) {
60#ifdef USE_ENTITY_ICON
61 this->icon_c_str_ = icon;
62#else
63 // No-op when USE_ENTITY_ICON is not defined
64#endif
65}
66
67// Entity Object ID - computed on-demand from name
68std::string EntityBase::get_object_id() const {
69 char buf[OBJECT_ID_MAX_LEN];
70 size_t len = this->write_object_id_to(buf, sizeof(buf));
71 return std::string(buf, len);
72}
73
74// Calculate Object ID Hash directly from name using snake_case + sanitize
76 this->object_id_hash_ = fnv1_hash_object_id(this->name_.c_str(), this->name_.size());
77}
78
79size_t EntityBase::write_object_id_to(char *buf, size_t buf_size) const {
80 size_t len = std::min(this->name_.size(), buf_size - 1);
81 for (size_t i = 0; i < len; i++) {
82 buf[i] = to_sanitized_char(to_snake_case_char(this->name_[i]));
83 }
84 buf[len] = '\0';
85 return len;
86}
87
88StringRef EntityBase::get_object_id_to(std::span<char, OBJECT_ID_MAX_LEN> buf) const {
89 size_t len = this->write_object_id_to(buf.data(), buf.size());
90 return StringRef(buf.data(), len);
91}
92
94
95// Migrate preference data from old_key to new_key if they differ.
96// This helper is exposed so callers with custom key computation (like TextPrefs)
97// can use it for manual migration. See: https://github.com/esphome/backlog/issues/85
98//
99// FUTURE IMPLEMENTATION:
100// This will require raw load/save methods on ESPPreferenceObject that take uint8_t* and size.
101// void EntityBase::migrate_entity_preference_(size_t size, uint32_t old_key, uint32_t new_key) {
102// if (old_key == new_key)
103// return;
104// auto old_pref = global_preferences->make_preference(size, old_key);
105// auto new_pref = global_preferences->make_preference(size, new_key);
106// SmallBufferWithHeapFallback<64> buffer(size);
107// if (old_pref.load(buffer.data(), size)) {
108// new_pref.save(buffer.data(), size);
109// }
110// }
111
113 // This helper centralizes preference creation to enable fixing hash collisions.
114 // See: https://github.com/esphome/backlog/issues/85
115 //
116 // COLLISION PROBLEM: get_preference_hash() uses fnv1_hash on sanitized object_id.
117 // Multiple entity names can sanitize to the same object_id:
118 // - "Living Room" and "living_room" both become "living_room"
119 // - UTF-8 names like "温度" and "湿度" both become "__" (underscores)
120 // This causes entities to overwrite each other's stored preferences.
121 //
122 // FUTURE MIGRATION: When implementing get_preference_hash_v2() that hashes
123 // the original entity name (not sanitized object_id):
124 //
125 // uint32_t old_key = this->get_preference_hash() ^ version;
126 // uint32_t new_key = this->get_preference_hash_v2() ^ version;
127 // this->migrate_entity_preference_(size, old_key, new_key);
128 // return global_preferences->make_preference(size, new_key);
129 //
130#pragma GCC diagnostic push
131#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
132 uint32_t key = this->get_preference_hash() ^ version;
133#pragma GCC diagnostic pop
135}
136
137std::string EntityBase_DeviceClass::get_device_class() {
138 if (this->device_class_ == nullptr) {
139 return "";
140 }
141 return this->device_class_;
142}
143
144void EntityBase_DeviceClass::set_device_class(const char *device_class) { this->device_class_ = device_class; }
145
146std::string EntityBase_UnitOfMeasurement::get_unit_of_measurement() {
147 if (this->unit_of_measurement_ == nullptr)
148 return "";
149 return this->unit_of_measurement_;
150}
151void EntityBase_UnitOfMeasurement::set_unit_of_measurement(const char *unit_of_measurement) {
152 this->unit_of_measurement_ = unit_of_measurement;
153}
154
155void log_entity_icon(const char *tag, const char *prefix, const EntityBase &obj) {
156 if (!obj.get_icon_ref().empty()) {
157 ESP_LOGCONFIG(tag, "%s Icon: '%s'", prefix, obj.get_icon_ref().c_str());
158 }
159}
160
161void log_entity_device_class(const char *tag, const char *prefix, const EntityBase_DeviceClass &obj) {
162 if (!obj.get_device_class_ref().empty()) {
163 ESP_LOGCONFIG(tag, "%s Device Class: '%s'", prefix, obj.get_device_class_ref().c_str());
164 }
165}
166
167void log_entity_unit_of_measurement(const char *tag, const char *prefix, const EntityBase_UnitOfMeasurement &obj) {
168 if (!obj.get_unit_of_measurement_ref().empty()) {
169 ESP_LOGCONFIG(tag, "%s Unit of Measurement: '%s'", prefix, obj.get_unit_of_measurement_ref().c_str());
170 }
171}
172
173} // namespace esphome
const std::string & get_friendly_name() const
Get the friendly name of this Application set by pre_setup().
bool is_name_add_mac_suffix_enabled() const
const std::string & get_name() const
Get the name of this Application set by pre_setup().
const char * get_name()
Definition device.h:10
virtual ESPPreferenceObject make_preference(size_t length, uint32_t type, bool in_flash)=0
StringRef get_device_class_ref() const
Get the device class as StringRef.
ESPDEPRECATED("Use get_device_class_ref() instead for better performance (avoids string copy). Will be removed in " "ESPHome 2026.5.0", "2025.11.0") std void set_device_class(const char *device_class)
Get the device class, using the manual override if set.
const char * device_class_
Device class override.
const char * unit_of_measurement_
Unit of measurement override.
ESPDEPRECATED("Use get_unit_of_measurement_ref() instead for better performance (avoids string copy). Will be " "removed in ESPHome 2026.5.0", "2025.11.0") std void set_unit_of_measurement(const char *unit_of_measurement)
Get the unit of measurement, using the manual override if set.
StringRef get_unit_of_measurement_ref() const
Get the unit of measurement as StringRef.
struct esphome::EntityBase::EntityFlags flags_
ESPPreferenceObject make_entity_preference_(size_t size, uint32_t version)
Non-template helper for make_entity_preference() to avoid code bloat.
ESPDEPRECATED("object_id mangles names and all object_id methods are planned for removal " "(see https://github.com/esphome/backlog/issues/76). " "Now is the time to stop using object_id. If still needed, use get_object_id_to() " "which will remain available longer. get_object_id() will be removed in 2026.7.0", "2025.12.0") std uint32_t get_object_id_hash()
const StringRef & get_name() const
StringRef get_icon_ref() const
Definition entity_base.h:98
size_t write_object_id_to(char *buf, size_t buf_size) const
Write object_id directly to buffer, returns length written (excluding null) Useful for building compo...
void set_name(const char *name)
ESPDEPRECATED("Use get_icon_ref() instead for better performance (avoids string copy). Will be removed in ESPHome 2026.5.0", "2025.11.0") std void set_icon(const char *icon)
const char * icon_c_str_
StringRef get_object_id_to(std::span< char, OBJECT_ID_MAX_LEN > buf) const
Get object_id with zero heap allocation For static case: returns StringRef to internal storage (buffe...
StringRef is a reference to a string owned by something else.
Definition string_ref.h:26
constexpr const char * c_str() const
Definition string_ref.h:73
constexpr bool empty() const
Definition string_ref.h:76
constexpr size_type size() const
Definition string_ref.h:74
const char *const TAG
Definition spi.cpp:7
Providing packet encoding functions for exchanging data with a remote host.
Definition a01nyub.cpp:7
void log_entity_icon(const char *tag, const char *prefix, const EntityBase &obj)
void log_entity_device_class(const char *tag, const char *prefix, const EntityBase_DeviceClass &obj)
constexpr char to_sanitized_char(char c)
Sanitize a single char: keep alphanumerics, dashes, underscores; replace others with underscore.
Definition helpers.h:652
std::string size_t len
Definition helpers.h:692
void log_entity_unit_of_measurement(const char *tag, const char *prefix, const EntityBase_UnitOfMeasurement &obj)
size_t size
Definition helpers.h:729
ESPPreferences * global_preferences
uint32_t fnv1_hash_object_id(const char *str, size_t len)
Calculate FNV-1 hash of a string while applying snake_case + sanitize transformations.
Definition helpers.h:680
constexpr char to_snake_case_char(char c)
Convert a single char to snake_case: lowercase and space to underscore.
Definition helpers.h:646
Application App
Global storage of Application pointer - only one Application can exist.