Skip to content

Entity Getters Now Return StringRef

Several entity component getters now return StringRef instead of const char* or std::string. This improves safety by eliminating null pointer crashes and provides a modern C++ API.

This is a breaking change for external components in ESPHome 2026.1.0 and later.

These PRs modernize entity getters to return StringRef, a lightweight string reference class that signals static lifetime semantics:

PR #13092: Fan - get_preset_mode() Returns StringRef instead of const char*. Adds has_preset_mode() helper.

PR #13095: Select - current_option() Returns StringRef instead of const char*.

PR #13103: Climate - get_custom_fan_mode(), get_custom_preset() Returns StringRef instead of const char*. Adds has_custom_fan_mode() and has_custom_preset() helpers.

PR #13104: Event - get_last_event_type() Returns StringRef instead of const char*. Adds has_event() helper.

PR #13105: Light - LightEffect::get_name(), LightState::get_effect_name() Returns StringRef instead of const char* and std::string respectively.

ComponentMethodBeforeAfter
Fanget_preset_mode()const char*StringRef
Selectcurrent_option()const char*StringRef
Climateget_custom_fan_mode()const char*StringRef
Climateget_custom_preset()const char*StringRef
Eventget_last_event_type()const char*StringRef
LightLightEffect::get_name()const char*StringRef
LightLightState::get_effect_name()std::stringStringRef
  • Empty state: Returns empty StringRef instead of nullptr when not set
  • Safety: == comparisons are always safe (no null pointer crashes)
  • Implicit conversion: StringRef converts to std::string automatically
  • External components that call these getter methods
  • YAML lambdas that check for null or use strcmp()

Standard YAML configurations are not affected.

The old pattern of checking for nullptr must be replaced:

// Before - checking for nullptr
const char *preset = fan->get_preset_mode();
if (preset != nullptr) {
// use preset
}
// After - use empty() or has_*() helpers
StringRef preset = fan->get_preset_mode();
if (!preset.empty()) {
// use preset
}
// Or use the new helper methods where available
if (fan->has_preset_mode()) {
StringRef preset = fan->get_preset_mode();
}

The == operator now works directly and is safe even when empty:

// Before - strcmp required (crashes if nullptr!)
const char *preset = fan->get_preset_mode();
if (preset != nullptr && strcmp(preset, "auto") == 0) {
// matched
}
// After - direct comparison (always safe)
if (fan->get_preset_mode() == "auto") {
// matched
}

Use %.*s format specifier for proper StringRef logging:

// Before
ESP_LOGD(TAG, "Preset: %s", fan->get_preset_mode());
// After
StringRef preset = fan->get_preset_mode();
ESP_LOGD(TAG, "Preset: %.*s", (int) preset.size(), preset.c_str());
// Or use c_str() directly (StringRef is null-terminated)
ESP_LOGD(TAG, "Preset: %s", fan->get_preset_mode().c_str());

StringRef converts implicitly to std::string:

// Before
const char *preset = fan->get_preset_mode();
std::string stored = preset ? preset : "";
// After - implicit conversion
std::string stored = fan->get_preset_mode();

Use .c_str() when passing to functions expecting const char*:

// StringRef is null-terminated, so c_str() is safe
some_c_function(fan->get_preset_mode().c_str());
# Before - null check pattern
lambda: |-
auto preset = id(my_fan).get_preset_mode();
if (preset != nullptr && strcmp(preset, "auto") == 0) {
return true;
}
return false;
# After - direct comparison
lambda: |-
return id(my_fan).get_preset_mode() == "auto";

StringRef is a lightweight reference to a string owned elsewhere:

StringRef ref = fan->get_preset_mode();
ref.empty() // true if empty (replaces nullptr check)
ref.size() // string length
ref.c_str() // null-terminated C string pointer
ref.str() // convert to std::string
ref == "value" // compare with string literal
ref == other_ref // compare with another StringRef
#if ESPHOME_VERSION_CODE >= VERSION_CODE(2026, 1, 0)
// New API - StringRef
if (!fan->get_preset_mode().empty()) {
ESP_LOGD(TAG, "Preset: %s", fan->get_preset_mode().c_str());
}
#else
// Old API - const char*
const char *preset = fan->get_preset_mode();
if (preset != nullptr) {
ESP_LOGD(TAG, "Preset: %s", preset);
}
#endif
  • ESPHome 2026.1.0 (January 2026): StringRef returns are active
  • No deprecation period - this is a signature change
Terminal window
# Find null pointer checks on these methods
grep -rn "get_preset_mode().*nullptr" your_component/
grep -rn "current_option().*nullptr" your_component/
grep -rn "get_custom_fan_mode().*nullptr" your_component/
grep -rn "get_custom_preset().*nullptr" your_component/
grep -rn "get_last_event_type().*nullptr" your_component/
grep -rn "get_effect_name().*nullptr" your_component/
# Find strcmp usage
grep -rn "strcmp.*get_preset_mode" your_component/
grep -rn "strcmp.*current_option" your_component/

If you have questions about migrating your external component, please ask in:

Comments

Feel free to leave a comment here to discuss this post with others. You can ask questions, share your experience, or suggest improvements. If you have a question about a specific feature or issue, please consider using the ESPHome Discord. Stick to English and follow ESPHome's code of conduct. These comments exist on a discussion on GitHub, so you can also comment there directly if you prefer.