From 8a3e26e6e965383a5e916feceb030914e786db10 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 9 Jan 2026 08:07:57 -1000 Subject: [PATCH 1/6] [event] Return std::string_view from get_last_event_type() --- esphome/components/api/api_connection.cpp | 9 +++++---- esphome/components/api/api_connection.h | 4 ++-- esphome/components/event/event.h | 10 ++++++++-- esphome/components/prometheus/prometheus_handler.cpp | 5 +++-- esphome/components/web_server/web_server.cpp | 3 +-- tests/components/event/common.yaml | 11 +++++++++++ 6 files changed, 30 insertions(+), 12 deletions(-) diff --git a/esphome/components/api/api_connection.cpp b/esphome/components/api/api_connection.cpp index fb3548d117..321257e9e2 100644 --- a/esphome/components/api/api_connection.cpp +++ b/esphome/components/api/api_connection.cpp @@ -1414,14 +1414,15 @@ void APIConnection::on_water_heater_command_request(const WaterHeaterCommandRequ #endif #ifdef USE_EVENT -void APIConnection::send_event(event::Event *event, const char *event_type) { - this->send_message_smart_(event, MessageCreator(event_type), EventResponse::MESSAGE_TYPE, +void APIConnection::send_event(event::Event *event, std::string_view event_type) { + // MessageCreator stores const char* - data() is safe as event types are null-terminated from codegen + this->send_message_smart_(event, MessageCreator(event_type.data()), EventResponse::MESSAGE_TYPE, EventResponse::ESTIMATED_SIZE); } -uint16_t APIConnection::try_send_event_response(event::Event *event, const char *event_type, APIConnection *conn, +uint16_t APIConnection::try_send_event_response(event::Event *event, std::string_view event_type, APIConnection *conn, uint32_t remaining_size, bool is_single) { EventResponse resp; - resp.event_type = StringRef(event_type); + resp.event_type = StringRef(event_type.data(), event_type.size()); return fill_and_encode_entity_state(event, resp, EventResponse::MESSAGE_TYPE, conn, remaining_size, is_single); } diff --git a/esphome/components/api/api_connection.h b/esphome/components/api/api_connection.h index 15d79a25ec..03cb787002 100644 --- a/esphome/components/api/api_connection.h +++ b/esphome/components/api/api_connection.h @@ -173,7 +173,7 @@ class APIConnection final : public APIServerConnection { #endif #ifdef USE_EVENT - void send_event(event::Event *event, const char *event_type); + void send_event(event::Event *event, std::string_view event_type); #endif #ifdef USE_UPDATE @@ -469,7 +469,7 @@ class APIConnection final : public APIServerConnection { bool is_single); #endif #ifdef USE_EVENT - static uint16_t try_send_event_response(event::Event *event, const char *event_type, APIConnection *conn, + static uint16_t try_send_event_response(event::Event *event, std::string_view event_type, APIConnection *conn, uint32_t remaining_size, bool is_single); static uint16_t try_send_event_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single); #endif diff --git a/esphome/components/event/event.h b/esphome/components/event/event.h index 0d5850d339..a8f0b872a3 100644 --- a/esphome/components/event/event.h +++ b/esphome/components/event/event.h @@ -2,6 +2,7 @@ #include #include +#include #include #include "esphome/core/component.h" @@ -44,8 +45,13 @@ class Event : public EntityBase, public EntityBase_DeviceClass { /// Return the event types supported by this event. const FixedVector &get_event_types() const { return this->types_; } - /// Return the last triggered event type (pointer to string in types_), or nullptr if no event triggered yet. - const char *get_last_event_type() const { return this->last_event_type_; } + /// Return the last triggered event type, or empty string_view if no event triggered yet. + std::string_view get_last_event_type() const { + return this->last_event_type_ != nullptr ? std::string_view(this->last_event_type_) : std::string_view(); + } + + /// Check if an event has been triggered. + bool has_event() const { return this->last_event_type_ != nullptr; } void add_on_event_callback(std::function &&callback); diff --git a/esphome/components/prometheus/prometheus_handler.cpp b/esphome/components/prometheus/prometheus_handler.cpp index 88b357041a..81bbcb423c 100644 --- a/esphome/components/prometheus/prometheus_handler.cpp +++ b/esphome/components/prometheus/prometheus_handler.cpp @@ -599,7 +599,7 @@ void PrometheusHandler::event_row_(AsyncResponseStream *stream, event::Event *ob std::string &friendly_name) { if (obj->is_internal() && !this->include_internal_) return; - if (obj->get_last_event_type() != nullptr) { + 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()); @@ -618,7 +618,8 @@ void PrometheusHandler::event_row_(AsyncResponseStream *stream, event::Event *ob stream->print(ESPHOME_F("\",name=\"")); stream->print(relabel_name_(obj).c_str()); stream->print(ESPHOME_F("\",last_event_type=\"")); - stream->print(obj->get_last_event_type()); + // get_last_event_type() returns string_view; data() is safe as event types are null-terminated + stream->print(obj->get_last_event_type().data()); stream->print(ESPHOME_F("\"} ")); stream->print(ESPHOME_F("1.0")); stream->print(ESPHOME_F("\n")); diff --git a/esphome/components/web_server/web_server.cpp b/esphome/components/web_server/web_server.cpp index cab177c182..d0fa35dacd 100644 --- a/esphome/components/web_server/web_server.cpp +++ b/esphome/components/web_server/web_server.cpp @@ -1850,8 +1850,7 @@ void WebServer::handle_event_request(AsyncWebServerRequest *request, const UrlMa } static std::string get_event_type(event::Event *event) { - const char *last_type = event ? event->get_last_event_type() : nullptr; - return last_type ? last_type : ""; + return event ? std::string(event->get_last_event_type()) : ""; } std::string WebServer::event_state_json_generator(WebServer *web_server, void *source) { diff --git a/tests/components/event/common.yaml b/tests/components/event/common.yaml index 71cc19a6b0..26caabac5c 100644 --- a/tests/components/event/common.yaml +++ b/tests/components/event/common.yaml @@ -7,3 +7,14 @@ event: - template_event_type2 on_event: - logger.log: Event fired + - lambda: |- + // Test get_last_event_type() returns std::string_view + if (id(some_event).has_event()) { + auto event_type = id(some_event).get_last_event_type(); + // Compare with string literal using == + if (event_type == "template_event_type1") { + ESP_LOGD("test", "Event type is template_event_type1"); + } + // Log using %.*s format for string_view + ESP_LOGD("test", "Event type: %.*s", (int) event_type.size(), event_type.data()); + } From 2eb98c19f700bd83afc809a838961213004ef62d Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 9 Jan 2026 16:43:28 -1000 Subject: [PATCH 2/6] strinferf --- esphome/components/api/api_connection.cpp | 10 +++++----- esphome/components/api/api_connection.h | 4 ++-- esphome/components/event/event.h | 8 +++----- esphome/components/prometheus/prometheus_handler.cpp | 4 ++-- tests/components/event/common.yaml | 6 +++--- 5 files changed, 15 insertions(+), 17 deletions(-) diff --git a/esphome/components/api/api_connection.cpp b/esphome/components/api/api_connection.cpp index 321257e9e2..1c002f82da 100644 --- a/esphome/components/api/api_connection.cpp +++ b/esphome/components/api/api_connection.cpp @@ -1414,15 +1414,15 @@ void APIConnection::on_water_heater_command_request(const WaterHeaterCommandRequ #endif #ifdef USE_EVENT -void APIConnection::send_event(event::Event *event, std::string_view event_type) { - // MessageCreator stores const char* - data() is safe as event types are null-terminated from codegen - this->send_message_smart_(event, MessageCreator(event_type.data()), EventResponse::MESSAGE_TYPE, +void APIConnection::send_event(event::Event *event, StringRef event_type) { + // get_last_event_type() returns StringRef pointing to null-terminated string literals from codegen + this->send_message_smart_(event, MessageCreator(event_type.c_str()), EventResponse::MESSAGE_TYPE, EventResponse::ESTIMATED_SIZE); } -uint16_t APIConnection::try_send_event_response(event::Event *event, std::string_view event_type, APIConnection *conn, +uint16_t APIConnection::try_send_event_response(event::Event *event, StringRef event_type, APIConnection *conn, uint32_t remaining_size, bool is_single) { EventResponse resp; - resp.event_type = StringRef(event_type.data(), event_type.size()); + resp.event_type = event_type; return fill_and_encode_entity_state(event, resp, EventResponse::MESSAGE_TYPE, conn, remaining_size, is_single); } diff --git a/esphome/components/api/api_connection.h b/esphome/components/api/api_connection.h index 03cb787002..0289b3d2ff 100644 --- a/esphome/components/api/api_connection.h +++ b/esphome/components/api/api_connection.h @@ -173,7 +173,7 @@ class APIConnection final : public APIServerConnection { #endif #ifdef USE_EVENT - void send_event(event::Event *event, std::string_view event_type); + void send_event(event::Event *event, StringRef event_type); #endif #ifdef USE_UPDATE @@ -469,7 +469,7 @@ class APIConnection final : public APIServerConnection { bool is_single); #endif #ifdef USE_EVENT - static uint16_t try_send_event_response(event::Event *event, std::string_view event_type, APIConnection *conn, + static uint16_t try_send_event_response(event::Event *event, StringRef event_type, APIConnection *conn, uint32_t remaining_size, bool is_single); static uint16_t try_send_event_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single); #endif diff --git a/esphome/components/event/event.h b/esphome/components/event/event.h index a8f0b872a3..27700e32d8 100644 --- a/esphome/components/event/event.h +++ b/esphome/components/event/event.h @@ -2,12 +2,12 @@ #include #include -#include #include #include "esphome/core/component.h" #include "esphome/core/entity_base.h" #include "esphome/core/helpers.h" +#include "esphome/core/string_ref.h" namespace esphome { namespace event { @@ -45,10 +45,8 @@ class Event : public EntityBase, public EntityBase_DeviceClass { /// Return the event types supported by this event. const FixedVector &get_event_types() const { return this->types_; } - /// Return the last triggered event type, or empty string_view if no event triggered yet. - std::string_view get_last_event_type() const { - return this->last_event_type_ != nullptr ? std::string_view(this->last_event_type_) : std::string_view(); - } + /// Return the last triggered event type, or empty StringRef if no event triggered yet. + StringRef get_last_event_type() const { return StringRef::from_maybe_nullptr(this->last_event_type_); } /// Check if an event has been triggered. bool has_event() const { return this->last_event_type_ != nullptr; } diff --git a/esphome/components/prometheus/prometheus_handler.cpp b/esphome/components/prometheus/prometheus_handler.cpp index 81bbcb423c..c6f5751420 100644 --- a/esphome/components/prometheus/prometheus_handler.cpp +++ b/esphome/components/prometheus/prometheus_handler.cpp @@ -618,8 +618,8 @@ void PrometheusHandler::event_row_(AsyncResponseStream *stream, event::Event *ob stream->print(ESPHOME_F("\",name=\"")); stream->print(relabel_name_(obj).c_str()); stream->print(ESPHOME_F("\",last_event_type=\"")); - // get_last_event_type() returns string_view; data() is safe as event types are null-terminated - stream->print(obj->get_last_event_type().data()); + // get_last_event_type() returns StringRef pointing to null-terminated string literals from codegen + stream->print(obj->get_last_event_type().c_str()); stream->print(ESPHOME_F("\"} ")); stream->print(ESPHOME_F("1.0")); stream->print(ESPHOME_F("\n")); diff --git a/tests/components/event/common.yaml b/tests/components/event/common.yaml index 26caabac5c..555d049c70 100644 --- a/tests/components/event/common.yaml +++ b/tests/components/event/common.yaml @@ -8,13 +8,13 @@ event: on_event: - logger.log: Event fired - lambda: |- - // Test get_last_event_type() returns std::string_view + // Test get_last_event_type() returns StringRef if (id(some_event).has_event()) { auto event_type = id(some_event).get_last_event_type(); // Compare with string literal using == if (event_type == "template_event_type1") { ESP_LOGD("test", "Event type is template_event_type1"); } - // Log using %.*s format for string_view - ESP_LOGD("test", "Event type: %.*s", (int) event_type.size(), event_type.data()); + // Log using %.*s format for StringRef + ESP_LOGD("test", "Event type: %.*s", (int) event_type.size(), event_type.c_str()); } From a3a4c12f3e5612f4c40c63f364340c9fa7841cc2 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 9 Jan 2026 16:47:40 -1000 Subject: [PATCH 3/6] try --- esphome/components/prometheus/prometheus_handler.cpp | 5 +++-- esphome/components/web_server/web_server.cpp | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/esphome/components/prometheus/prometheus_handler.cpp b/esphome/components/prometheus/prometheus_handler.cpp index c6f5751420..9ab689ba06 100644 --- a/esphome/components/prometheus/prometheus_handler.cpp +++ b/esphome/components/prometheus/prometheus_handler.cpp @@ -618,8 +618,9 @@ void PrometheusHandler::event_row_(AsyncResponseStream *stream, event::Event *ob stream->print(ESPHOME_F("\",name=\"")); stream->print(relabel_name_(obj).c_str()); stream->print(ESPHOME_F("\",last_event_type=\"")); - // get_last_event_type() returns StringRef pointing to null-terminated string literals from codegen - stream->print(obj->get_last_event_type().c_str()); + // get_last_event_type() returns StringRef + auto event_type = obj->get_last_event_type(); + stream->print(event_type.c_str(), event_type.size()); stream->print(ESPHOME_F("\"} ")); stream->print(ESPHOME_F("1.0")); stream->print(ESPHOME_F("\n")); diff --git a/esphome/components/web_server/web_server.cpp b/esphome/components/web_server/web_server.cpp index 4c218479e0..e4baebe3cb 100644 --- a/esphome/components/web_server/web_server.cpp +++ b/esphome/components/web_server/web_server.cpp @@ -1966,7 +1966,7 @@ void WebServer::handle_event_request(AsyncWebServerRequest *request, const UrlMa } static std::string get_event_type(event::Event *event) { - return event ? std::string(event->get_last_event_type()) : ""; + return event ? std::string(event->get_last_event_type()) : std::string(); } std::string WebServer::event_state_json_generator(WebServer *web_server, void *source) { From 08bd49c038966dae859680759ae1f4b8ab9e5cca Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 9 Jan 2026 16:50:42 -1000 Subject: [PATCH 4/6] fix --- esphome/components/api/api_connection.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/api/api_connection.cpp b/esphome/components/api/api_connection.cpp index 1c002f82da..6852022b00 100644 --- a/esphome/components/api/api_connection.cpp +++ b/esphome/components/api/api_connection.cpp @@ -2056,7 +2056,7 @@ uint16_t APIConnection::MessageCreator::operator()(EntityBase *entity, APIConnec // Special case: EventResponse uses const char * pointer if (message_type == EventResponse::MESSAGE_TYPE) { auto *e = static_cast(entity); - return APIConnection::try_send_event_response(e, data_.const_char_ptr, conn, remaining_size, is_single); + return APIConnection::try_send_event_response(e, StringRef(data_.const_char_ptr), conn, remaining_size, is_single); } #endif From 54d3ea409825ee857a408f7d8cf2e840b964d6d1 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 9 Jan 2026 16:54:21 -1000 Subject: [PATCH 5/6] fix: use simple .c_str() for ESP8266 compatibility --- esphome/components/prometheus/prometheus_handler.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/esphome/components/prometheus/prometheus_handler.cpp b/esphome/components/prometheus/prometheus_handler.cpp index 9ab689ba06..e14dccc382 100644 --- a/esphome/components/prometheus/prometheus_handler.cpp +++ b/esphome/components/prometheus/prometheus_handler.cpp @@ -618,9 +618,8 @@ void PrometheusHandler::event_row_(AsyncResponseStream *stream, event::Event *ob stream->print(ESPHOME_F("\",name=\"")); stream->print(relabel_name_(obj).c_str()); stream->print(ESPHOME_F("\",last_event_type=\"")); - // get_last_event_type() returns StringRef - auto event_type = obj->get_last_event_type(); - stream->print(event_type.c_str(), event_type.size()); + // get_last_event_type() returns StringRef (null-terminated) + stream->print(obj->get_last_event_type().c_str()); stream->print(ESPHOME_F("\"} ")); stream->print(ESPHOME_F("1.0")); stream->print(ESPHOME_F("\n")); From 3178ae32dd13f490a9b8c7e08fb3f3d172627960 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 9 Jan 2026 21:56:14 -1000 Subject: [PATCH 6/6] missed some --- esphome/components/web_server/web_server.cpp | 8 +++----- esphome/components/web_server/web_server.h | 2 +- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/esphome/components/web_server/web_server.cpp b/esphome/components/web_server/web_server.cpp index e4baebe3cb..78d5f66c79 100644 --- a/esphome/components/web_server/web_server.cpp +++ b/esphome/components/web_server/web_server.cpp @@ -1957,7 +1957,7 @@ void WebServer::handle_event_request(AsyncWebServerRequest *request, const UrlMa // Note: request->method() is always HTTP_GET here (canHandle ensures this) if (entity_match.action_is_empty) { auto detail = get_request_detail(request); - std::string data = this->event_json_(obj, "", detail); + std::string data = this->event_json_(obj, StringRef(), detail); request->send(200, "application/json", data.c_str()); return; } @@ -1965,9 +1965,7 @@ void WebServer::handle_event_request(AsyncWebServerRequest *request, const UrlMa request->send(404); } -static std::string get_event_type(event::Event *event) { - return event ? std::string(event->get_last_event_type()) : std::string(); -} +static StringRef get_event_type(event::Event *event) { return event ? event->get_last_event_type() : StringRef(); } std::string WebServer::event_state_json_generator(WebServer *web_server, void *source) { auto *event = static_cast(source); @@ -1978,7 +1976,7 @@ std::string WebServer::event_all_json_generator(WebServer *web_server, void *sou auto *event = static_cast(source); return web_server->event_json_(event, get_event_type(event), DETAIL_ALL); } -std::string WebServer::event_json_(event::Event *obj, const std::string &event_type, JsonDetail start_config) { +std::string WebServer::event_json_(event::Event *obj, StringRef event_type, JsonDetail start_config) { json::JsonBuilder builder; JsonObject root = builder.root(); diff --git a/esphome/components/web_server/web_server.h b/esphome/components/web_server/web_server.h index c52cf981e0..6deed10c8e 100644 --- a/esphome/components/web_server/web_server.h +++ b/esphome/components/web_server/web_server.h @@ -643,7 +643,7 @@ class WebServer : public Controller, alarm_control_panel::AlarmControlPanelState value, JsonDetail start_config); #endif #ifdef USE_EVENT - std::string event_json_(event::Event *obj, const std::string &event_type, JsonDetail start_config); + std::string event_json_(event::Event *obj, StringRef event_type, JsonDetail start_config); #endif #ifdef USE_WATER_HEATER std::string water_heater_json_(water_heater::WaterHeater *obj, JsonDetail start_config);