Skip to content

Callback Signature Changes for text_sensor, text, and select

The callback signatures for text_sensor, text, and select components have been updated to reduce heap allocations. The select component callback signature has changed significantly.

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

These PRs optimize state callbacks to reduce heap churn:

PR #12503: text_sensor Changes callback from void(std::string) to void(const std::string &).

PR #12504: text Changes callback from void(std::string) to void(const std::string &).

PR #12505: select Changes callback from void(std::string, size_t) to void(size_t) - string parameter removed entirely.

// Before - passed by value (copies string)
std::function<void(std::string)>
// After - passed by const reference (no copy)
std::function<void(const std::string &)>
// Before - received string and index
std::function<void(std::string, size_t)>
// After - receives index only
std::function<void(size_t)>

Most code requires no changes. Lambda callbacks continue working with either signature:

// Both still work:
sensor->add_on_state_callback([](const std::string &value) { ... });
sensor->add_on_state_callback([](std::string value) { ... }); // Still works (copy made)

Breaking case: Explicitly-typed std::function variables must be updated. This is extremely rare in practice.

All callbacks must be updated. The string parameter has been removed entirely.

Standard YAML configurations are not affected - on_value triggers in YAML still receive both x (string) and index.

text_sensor and text (if using explicit types)

Section titled “text_sensor and text (if using explicit types)”
// Before
std::function<void(std::string)> callback = [](std::string value) {
ESP_LOGD(TAG, "Value: %s", value.c_str());
};
text_sensor->add_on_state_callback(callback);
// After
std::function<void(const std::string &)> callback = [](const std::string &value) {
ESP_LOGD(TAG, "Value: %s", value.c_str());
};
text_sensor->add_on_state_callback(callback);
// Before - received string and index
this->select_->add_on_state_callback([](const std::string &value, size_t index) {
ESP_LOGD(TAG, "Selected: %s (index %zu)", value.c_str(), index);
});
// After - receives index only, get string from select if needed
this->select_->add_on_state_callback([this](size_t index) {
const char *value = this->select_->option_at(index); // Returns const char*, no allocation
if (value != nullptr) {
ESP_LOGD(TAG, "Selected: %s (index %zu)", value, index);
}
});
// Or if you only need the index (common case)
this->select_->add_on_state_callback([this](size_t index) {
this->handle_selection(index);
});

Getting the selected option string from index

Section titled “Getting the selected option string from index”

If your callback needs the option string, retrieve it from the select entity:

this->select_->add_on_state_callback([this](size_t index) {
// Option 1: Use option_at()
const char *value = this->select_->option_at(index);
if (value != nullptr) {
// use value
}
// Option 2: Use traits directly
const auto &options = this->select_->traits.get_options();
if (index < options.size()) {
const char *value = options[index];
// use value
}
});
// Lambda callbacks work unchanged for both versions
text_sensor->add_on_state_callback([](const std::string &value) {
ESP_LOGD(TAG, "Value: %s", value.c_str());
});
#if ESPHOME_VERSION_CODE >= VERSION_CODE(2026, 1, 0)
// New API - index only
this->select_->add_on_state_callback([this](size_t index) {
const char *value = this->select_->option_at(index);
if (value != nullptr) {
ESP_LOGD(TAG, "Selected: %s", value);
}
});
#else
// Old API - string and index
this->select_->add_on_state_callback([](const std::string &value, size_t index) {
ESP_LOGD(TAG, "Selected: %s", value.c_str());
});
#endif
  • ESPHome 2026.1.0 (January 2026): New callback signatures active
  • No deprecation period for select - signature changed directly
Terminal window
# Find select callbacks with old signature
grep -rn "add_on_state_callback.*std::string.*size_t" your_component/
# Find explicitly-typed function objects
grep -rn "std::function<void(std::string)>" your_component/
# Find all state callback registrations
grep -rn "add_on_state_callback" 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.