From c12166c1a17d75320b5e799f5d9628912d1ea0cf Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 27 Jun 2025 10:04:29 -0500 Subject: [PATCH 01/11] missed one --- esphome/components/web_server_idf/web_server_idf.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/esphome/components/web_server_idf/web_server_idf.cpp b/esphome/components/web_server_idf/web_server_idf.cpp index 90fdf720cd..30c6b04fb2 100644 --- a/esphome/components/web_server_idf/web_server_idf.cpp +++ b/esphome/components/web_server_idf/web_server_idf.cpp @@ -338,6 +338,7 @@ AsyncEventSourceResponse::AsyncEventSourceResponse(const AsyncWebServerRequest * std::string message = ws->get_config_json(); this->try_send_nodefer(message.c_str(), "ping", millis(), 30000); +#ifdef USE_WEBSERVER_SORTING for (auto &group : ws->sorting_groups_) { message = json::build_json([group](JsonObject root) { root["name"] = group.second.name; @@ -348,6 +349,7 @@ AsyncEventSourceResponse::AsyncEventSourceResponse(const AsyncWebServerRequest * // since the only thing in the send buffer at this point is the initial ping/config this->try_send_nodefer(message.c_str(), "sorting_group"); } +#endif this->entities_iterator_->begin(ws->include_internal_); From f4b3539d77be83722caf2a9bd578c95f0e4abb57 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 27 Jun 2025 10:05:30 -0500 Subject: [PATCH 02/11] clang-format --- esphome/components/web_server/web_server.cpp | 40 ++++++++++---------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/esphome/components/web_server/web_server.cpp b/esphome/components/web_server/web_server.cpp index 510cc3c2a4..56b5a95432 100644 --- a/esphome/components/web_server/web_server.cpp +++ b/esphome/components/web_server/web_server.cpp @@ -402,7 +402,7 @@ std::string WebServer::sensor_all_json_generator(WebServer *web_server, void *so return web_server->sensor_json((sensor::Sensor *) (source), ((sensor::Sensor *) (source))->state, DETAIL_ALL); } std::string WebServer::sensor_json(sensor::Sensor *obj, float value, JsonDetail start_config) { - return json::build_json([this, obj, value, start_config](JsonObject root) { + return json::build_json([obj, value, start_config](JsonObject root) { std::string state; if (std::isnan(value)) { state = "NA"; @@ -456,7 +456,7 @@ std::string WebServer::text_sensor_all_json_generator(WebServer *web_server, voi } std::string WebServer::text_sensor_json(text_sensor::TextSensor *obj, const std::string &value, JsonDetail start_config) { - return json::build_json([this, obj, value, start_config](JsonObject root) { + return json::build_json([obj, value, start_config](JsonObject root) { set_json_icon_state_value(root, obj, "text_sensor-" + obj->get_object_id(), value, value, start_config); if (start_config == DETAIL_ALL) { #ifdef USE_WEBSERVER_SORTING @@ -509,7 +509,7 @@ std::string WebServer::switch_all_json_generator(WebServer *web_server, void *so return web_server->switch_json((switch_::Switch *) (source), ((switch_::Switch *) (source))->state, DETAIL_ALL); } std::string WebServer::switch_json(switch_::Switch *obj, bool value, JsonDetail start_config) { - return json::build_json([this, obj, value, start_config](JsonObject root) { + return json::build_json([obj, value, start_config](JsonObject root) { set_json_icon_state_value(root, obj, "switch-" + obj->get_object_id(), value ? "ON" : "OFF", value, start_config); if (start_config == DETAIL_ALL) { root["assumed_state"] = obj->assumed_state(); @@ -552,7 +552,7 @@ std::string WebServer::button_all_json_generator(WebServer *web_server, void *so return web_server->button_json((button::Button *) (source), DETAIL_ALL); } std::string WebServer::button_json(button::Button *obj, JsonDetail start_config) { - return json::build_json([this, obj, start_config](JsonObject root) { + return json::build_json([obj, start_config](JsonObject root) { set_json_id(root, obj, "button-" + obj->get_object_id(), start_config); if (start_config == DETAIL_ALL) { #ifdef USE_WEBSERVER_SORTING @@ -595,7 +595,7 @@ std::string WebServer::binary_sensor_all_json_generator(WebServer *web_server, v ((binary_sensor::BinarySensor *) (source))->state, DETAIL_ALL); } std::string WebServer::binary_sensor_json(binary_sensor::BinarySensor *obj, bool value, JsonDetail start_config) { - return json::build_json([this, obj, value, start_config](JsonObject root) { + return json::build_json([obj, value, start_config](JsonObject root) { set_json_icon_state_value(root, obj, "binary_sensor-" + obj->get_object_id(), value ? "ON" : "OFF", value, start_config); if (start_config == DETAIL_ALL) { @@ -675,7 +675,7 @@ std::string WebServer::fan_all_json_generator(WebServer *web_server, void *sourc return web_server->fan_json((fan::Fan *) (source), DETAIL_ALL); } std::string WebServer::fan_json(fan::Fan *obj, JsonDetail start_config) { - return json::build_json([this, obj, start_config](JsonObject root) { + return json::build_json([obj, start_config](JsonObject root) { set_json_icon_state_value(root, obj, "fan-" + obj->get_object_id(), obj->state ? "ON" : "OFF", obj->state, start_config); const auto traits = obj->get_traits(); @@ -797,7 +797,7 @@ std::string WebServer::light_all_json_generator(WebServer *web_server, void *sou return web_server->light_json((light::LightState *) (source), DETAIL_ALL); } std::string WebServer::light_json(light::LightState *obj, JsonDetail start_config) { - return json::build_json([this, obj, start_config](JsonObject root) { + return json::build_json([obj, start_config](JsonObject root) { set_json_id(root, obj, "light-" + obj->get_object_id(), start_config); root["state"] = obj->remote_values.is_on() ? "ON" : "OFF"; @@ -885,7 +885,7 @@ std::string WebServer::cover_all_json_generator(WebServer *web_server, void *sou return web_server->cover_json((cover::Cover *) (source), DETAIL_STATE); } std::string WebServer::cover_json(cover::Cover *obj, JsonDetail start_config) { - return json::build_json([this, obj, start_config](JsonObject root) { + return json::build_json([obj, start_config](JsonObject root) { set_json_icon_state_value(root, obj, "cover-" + obj->get_object_id(), obj->is_fully_closed() ? "CLOSED" : "OPEN", obj->position, start_config); root["current_operation"] = cover::cover_operation_to_str(obj->current_operation); @@ -950,7 +950,7 @@ std::string WebServer::number_all_json_generator(WebServer *web_server, void *so return web_server->number_json((number::Number *) (source), ((number::Number *) (source))->state, DETAIL_ALL); } std::string WebServer::number_json(number::Number *obj, float value, JsonDetail start_config) { - return json::build_json([this, obj, value, start_config](JsonObject root) { + return json::build_json([obj, value, start_config](JsonObject root) { set_json_id(root, obj, "number-" + obj->get_object_id(), start_config); if (start_config == DETAIL_ALL) { root["min_value"] = @@ -1031,7 +1031,7 @@ std::string WebServer::date_all_json_generator(WebServer *web_server, void *sour return web_server->date_json((datetime::DateEntity *) (source), DETAIL_ALL); } std::string WebServer::date_json(datetime::DateEntity *obj, JsonDetail start_config) { - return json::build_json([this, obj, start_config](JsonObject root) { + return json::build_json([obj, start_config](JsonObject root) { set_json_id(root, obj, "date-" + obj->get_object_id(), start_config); std::string value = str_sprintf("%d-%02d-%02d", obj->year, obj->month, obj->day); root["value"] = value; @@ -1095,7 +1095,7 @@ std::string WebServer::time_all_json_generator(WebServer *web_server, void *sour return web_server->time_json((datetime::TimeEntity *) (source), DETAIL_ALL); } std::string WebServer::time_json(datetime::TimeEntity *obj, JsonDetail start_config) { - return json::build_json([this, obj, start_config](JsonObject root) { + return json::build_json([obj, start_config](JsonObject root) { set_json_id(root, obj, "time-" + obj->get_object_id(), start_config); std::string value = str_sprintf("%02d:%02d:%02d", obj->hour, obj->minute, obj->second); root["value"] = value; @@ -1159,7 +1159,7 @@ std::string WebServer::datetime_all_json_generator(WebServer *web_server, void * return web_server->datetime_json((datetime::DateTimeEntity *) (source), DETAIL_ALL); } std::string WebServer::datetime_json(datetime::DateTimeEntity *obj, JsonDetail start_config) { - return json::build_json([this, obj, start_config](JsonObject root) { + return json::build_json([obj, start_config](JsonObject root) { set_json_id(root, obj, "datetime-" + obj->get_object_id(), start_config); std::string value = str_sprintf("%d-%02d-%02d %02d:%02d:%02d", obj->year, obj->month, obj->day, obj->hour, obj->minute, obj->second); @@ -1220,7 +1220,7 @@ std::string WebServer::text_all_json_generator(WebServer *web_server, void *sour return web_server->text_json((text::Text *) (source), ((text::Text *) (source))->state, DETAIL_ALL); } std::string WebServer::text_json(text::Text *obj, const std::string &value, JsonDetail start_config) { - return json::build_json([this, obj, value, start_config](JsonObject root) { + return json::build_json([obj, value, start_config](JsonObject root) { set_json_id(root, obj, "text-" + obj->get_object_id(), start_config); root["min_length"] = obj->traits.get_min_length(); root["max_length"] = obj->traits.get_max_length(); @@ -1288,7 +1288,7 @@ std::string WebServer::select_all_json_generator(WebServer *web_server, void *so return web_server->select_json((select::Select *) (source), ((select::Select *) (source))->state, DETAIL_ALL); } std::string WebServer::select_json(select::Select *obj, const std::string &value, JsonDetail start_config) { - return json::build_json([this, obj, value, start_config](JsonObject root) { + return json::build_json([obj, value, start_config](JsonObject root) { set_json_icon_state_value(root, obj, "select-" + obj->get_object_id(), value, value, start_config); if (start_config == DETAIL_ALL) { JsonArray opt = root.createNestedArray("option"); @@ -1381,7 +1381,7 @@ std::string WebServer::climate_all_json_generator(WebServer *web_server, void *s return web_server->climate_json((climate::Climate *) (source), DETAIL_ALL); } std::string WebServer::climate_json(climate::Climate *obj, JsonDetail start_config) { - return json::build_json([this, obj, start_config](JsonObject root) { + return json::build_json([obj, start_config](JsonObject root) { set_json_id(root, obj, "climate-" + obj->get_object_id(), start_config); const auto traits = obj->get_traits(); int8_t target_accuracy = traits.get_target_temperature_accuracy_decimals(); @@ -1513,7 +1513,7 @@ std::string WebServer::lock_all_json_generator(WebServer *web_server, void *sour return web_server->lock_json((lock::Lock *) (source), ((lock::Lock *) (source))->state, DETAIL_ALL); } std::string WebServer::lock_json(lock::Lock *obj, lock::LockState value, JsonDetail start_config) { - return json::build_json([this, obj, value, start_config](JsonObject root) { + return json::build_json([obj, value, start_config](JsonObject root) { set_json_icon_state_value(root, obj, "lock-" + obj->get_object_id(), lock::lock_state_to_string(value), value, start_config); if (start_config == DETAIL_ALL) { @@ -1587,7 +1587,7 @@ std::string WebServer::valve_all_json_generator(WebServer *web_server, void *sou return web_server->valve_json((valve::Valve *) (source), DETAIL_ALL); } std::string WebServer::valve_json(valve::Valve *obj, JsonDetail start_config) { - return json::build_json([this, obj, start_config](JsonObject root) { + return json::build_json([obj, start_config](JsonObject root) { set_json_icon_state_value(root, obj, "valve-" + obj->get_object_id(), obj->is_fully_closed() ? "CLOSED" : "OPEN", obj->position, start_config); root["current_operation"] = valve::valve_operation_to_str(obj->current_operation); @@ -1664,7 +1664,7 @@ std::string WebServer::alarm_control_panel_all_json_generator(WebServer *web_ser std::string WebServer::alarm_control_panel_json(alarm_control_panel::AlarmControlPanel *obj, alarm_control_panel::AlarmControlPanelState value, JsonDetail start_config) { - return json::build_json([this, obj, value, start_config](JsonObject root) { + return json::build_json([obj, value, start_config](JsonObject root) { char buf[16]; set_json_icon_state_value(root, obj, "alarm-control-panel-" + obj->get_object_id(), PSTR_LOCAL(alarm_control_panel_state_to_string(value)), value, start_config); @@ -1709,7 +1709,7 @@ std::string WebServer::event_all_json_generator(WebServer *web_server, void *sou return web_server->event_json((event::Event *) (source), *(((event::Event *) (source))->last_event_type), DETAIL_ALL); } std::string WebServer::event_json(event::Event *obj, const std::string &event_type, JsonDetail start_config) { - return json::build_json([this, obj, event_type, start_config](JsonObject root) { + return json::build_json([obj, event_type, start_config](JsonObject root) { set_json_id(root, obj, "event-" + obj->get_object_id(), start_config); if (!event_type.empty()) { root["event_type"] = event_type; @@ -1768,7 +1768,7 @@ std::string WebServer::update_all_json_generator(WebServer *web_server, void *so return web_server->update_json((update::UpdateEntity *) (source), DETAIL_STATE); } std::string WebServer::update_json(update::UpdateEntity *obj, JsonDetail start_config) { - return json::build_json([this, obj, start_config](JsonObject root) { + return json::build_json([obj, start_config](JsonObject root) { set_json_id(root, obj, "update-" + obj->get_object_id(), start_config); root["value"] = obj->update_info.latest_version; switch (obj->state) { From 409346952f91f501074df9993cc06d55998ecfea Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 27 Jun 2025 10:15:04 -0500 Subject: [PATCH 03/11] clang-format --- esphome/components/web_server/web_server.cpp | 40 ++++++++++---------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/esphome/components/web_server/web_server.cpp b/esphome/components/web_server/web_server.cpp index 56b5a95432..510cc3c2a4 100644 --- a/esphome/components/web_server/web_server.cpp +++ b/esphome/components/web_server/web_server.cpp @@ -402,7 +402,7 @@ std::string WebServer::sensor_all_json_generator(WebServer *web_server, void *so return web_server->sensor_json((sensor::Sensor *) (source), ((sensor::Sensor *) (source))->state, DETAIL_ALL); } std::string WebServer::sensor_json(sensor::Sensor *obj, float value, JsonDetail start_config) { - return json::build_json([obj, value, start_config](JsonObject root) { + return json::build_json([this, obj, value, start_config](JsonObject root) { std::string state; if (std::isnan(value)) { state = "NA"; @@ -456,7 +456,7 @@ std::string WebServer::text_sensor_all_json_generator(WebServer *web_server, voi } std::string WebServer::text_sensor_json(text_sensor::TextSensor *obj, const std::string &value, JsonDetail start_config) { - return json::build_json([obj, value, start_config](JsonObject root) { + return json::build_json([this, obj, value, start_config](JsonObject root) { set_json_icon_state_value(root, obj, "text_sensor-" + obj->get_object_id(), value, value, start_config); if (start_config == DETAIL_ALL) { #ifdef USE_WEBSERVER_SORTING @@ -509,7 +509,7 @@ std::string WebServer::switch_all_json_generator(WebServer *web_server, void *so return web_server->switch_json((switch_::Switch *) (source), ((switch_::Switch *) (source))->state, DETAIL_ALL); } std::string WebServer::switch_json(switch_::Switch *obj, bool value, JsonDetail start_config) { - return json::build_json([obj, value, start_config](JsonObject root) { + return json::build_json([this, obj, value, start_config](JsonObject root) { set_json_icon_state_value(root, obj, "switch-" + obj->get_object_id(), value ? "ON" : "OFF", value, start_config); if (start_config == DETAIL_ALL) { root["assumed_state"] = obj->assumed_state(); @@ -552,7 +552,7 @@ std::string WebServer::button_all_json_generator(WebServer *web_server, void *so return web_server->button_json((button::Button *) (source), DETAIL_ALL); } std::string WebServer::button_json(button::Button *obj, JsonDetail start_config) { - return json::build_json([obj, start_config](JsonObject root) { + return json::build_json([this, obj, start_config](JsonObject root) { set_json_id(root, obj, "button-" + obj->get_object_id(), start_config); if (start_config == DETAIL_ALL) { #ifdef USE_WEBSERVER_SORTING @@ -595,7 +595,7 @@ std::string WebServer::binary_sensor_all_json_generator(WebServer *web_server, v ((binary_sensor::BinarySensor *) (source))->state, DETAIL_ALL); } std::string WebServer::binary_sensor_json(binary_sensor::BinarySensor *obj, bool value, JsonDetail start_config) { - return json::build_json([obj, value, start_config](JsonObject root) { + return json::build_json([this, obj, value, start_config](JsonObject root) { set_json_icon_state_value(root, obj, "binary_sensor-" + obj->get_object_id(), value ? "ON" : "OFF", value, start_config); if (start_config == DETAIL_ALL) { @@ -675,7 +675,7 @@ std::string WebServer::fan_all_json_generator(WebServer *web_server, void *sourc return web_server->fan_json((fan::Fan *) (source), DETAIL_ALL); } std::string WebServer::fan_json(fan::Fan *obj, JsonDetail start_config) { - return json::build_json([obj, start_config](JsonObject root) { + return json::build_json([this, obj, start_config](JsonObject root) { set_json_icon_state_value(root, obj, "fan-" + obj->get_object_id(), obj->state ? "ON" : "OFF", obj->state, start_config); const auto traits = obj->get_traits(); @@ -797,7 +797,7 @@ std::string WebServer::light_all_json_generator(WebServer *web_server, void *sou return web_server->light_json((light::LightState *) (source), DETAIL_ALL); } std::string WebServer::light_json(light::LightState *obj, JsonDetail start_config) { - return json::build_json([obj, start_config](JsonObject root) { + return json::build_json([this, obj, start_config](JsonObject root) { set_json_id(root, obj, "light-" + obj->get_object_id(), start_config); root["state"] = obj->remote_values.is_on() ? "ON" : "OFF"; @@ -885,7 +885,7 @@ std::string WebServer::cover_all_json_generator(WebServer *web_server, void *sou return web_server->cover_json((cover::Cover *) (source), DETAIL_STATE); } std::string WebServer::cover_json(cover::Cover *obj, JsonDetail start_config) { - return json::build_json([obj, start_config](JsonObject root) { + return json::build_json([this, obj, start_config](JsonObject root) { set_json_icon_state_value(root, obj, "cover-" + obj->get_object_id(), obj->is_fully_closed() ? "CLOSED" : "OPEN", obj->position, start_config); root["current_operation"] = cover::cover_operation_to_str(obj->current_operation); @@ -950,7 +950,7 @@ std::string WebServer::number_all_json_generator(WebServer *web_server, void *so return web_server->number_json((number::Number *) (source), ((number::Number *) (source))->state, DETAIL_ALL); } std::string WebServer::number_json(number::Number *obj, float value, JsonDetail start_config) { - return json::build_json([obj, value, start_config](JsonObject root) { + return json::build_json([this, obj, value, start_config](JsonObject root) { set_json_id(root, obj, "number-" + obj->get_object_id(), start_config); if (start_config == DETAIL_ALL) { root["min_value"] = @@ -1031,7 +1031,7 @@ std::string WebServer::date_all_json_generator(WebServer *web_server, void *sour return web_server->date_json((datetime::DateEntity *) (source), DETAIL_ALL); } std::string WebServer::date_json(datetime::DateEntity *obj, JsonDetail start_config) { - return json::build_json([obj, start_config](JsonObject root) { + return json::build_json([this, obj, start_config](JsonObject root) { set_json_id(root, obj, "date-" + obj->get_object_id(), start_config); std::string value = str_sprintf("%d-%02d-%02d", obj->year, obj->month, obj->day); root["value"] = value; @@ -1095,7 +1095,7 @@ std::string WebServer::time_all_json_generator(WebServer *web_server, void *sour return web_server->time_json((datetime::TimeEntity *) (source), DETAIL_ALL); } std::string WebServer::time_json(datetime::TimeEntity *obj, JsonDetail start_config) { - return json::build_json([obj, start_config](JsonObject root) { + return json::build_json([this, obj, start_config](JsonObject root) { set_json_id(root, obj, "time-" + obj->get_object_id(), start_config); std::string value = str_sprintf("%02d:%02d:%02d", obj->hour, obj->minute, obj->second); root["value"] = value; @@ -1159,7 +1159,7 @@ std::string WebServer::datetime_all_json_generator(WebServer *web_server, void * return web_server->datetime_json((datetime::DateTimeEntity *) (source), DETAIL_ALL); } std::string WebServer::datetime_json(datetime::DateTimeEntity *obj, JsonDetail start_config) { - return json::build_json([obj, start_config](JsonObject root) { + return json::build_json([this, obj, start_config](JsonObject root) { set_json_id(root, obj, "datetime-" + obj->get_object_id(), start_config); std::string value = str_sprintf("%d-%02d-%02d %02d:%02d:%02d", obj->year, obj->month, obj->day, obj->hour, obj->minute, obj->second); @@ -1220,7 +1220,7 @@ std::string WebServer::text_all_json_generator(WebServer *web_server, void *sour return web_server->text_json((text::Text *) (source), ((text::Text *) (source))->state, DETAIL_ALL); } std::string WebServer::text_json(text::Text *obj, const std::string &value, JsonDetail start_config) { - return json::build_json([obj, value, start_config](JsonObject root) { + return json::build_json([this, obj, value, start_config](JsonObject root) { set_json_id(root, obj, "text-" + obj->get_object_id(), start_config); root["min_length"] = obj->traits.get_min_length(); root["max_length"] = obj->traits.get_max_length(); @@ -1288,7 +1288,7 @@ std::string WebServer::select_all_json_generator(WebServer *web_server, void *so return web_server->select_json((select::Select *) (source), ((select::Select *) (source))->state, DETAIL_ALL); } std::string WebServer::select_json(select::Select *obj, const std::string &value, JsonDetail start_config) { - return json::build_json([obj, value, start_config](JsonObject root) { + return json::build_json([this, obj, value, start_config](JsonObject root) { set_json_icon_state_value(root, obj, "select-" + obj->get_object_id(), value, value, start_config); if (start_config == DETAIL_ALL) { JsonArray opt = root.createNestedArray("option"); @@ -1381,7 +1381,7 @@ std::string WebServer::climate_all_json_generator(WebServer *web_server, void *s return web_server->climate_json((climate::Climate *) (source), DETAIL_ALL); } std::string WebServer::climate_json(climate::Climate *obj, JsonDetail start_config) { - return json::build_json([obj, start_config](JsonObject root) { + return json::build_json([this, obj, start_config](JsonObject root) { set_json_id(root, obj, "climate-" + obj->get_object_id(), start_config); const auto traits = obj->get_traits(); int8_t target_accuracy = traits.get_target_temperature_accuracy_decimals(); @@ -1513,7 +1513,7 @@ std::string WebServer::lock_all_json_generator(WebServer *web_server, void *sour return web_server->lock_json((lock::Lock *) (source), ((lock::Lock *) (source))->state, DETAIL_ALL); } std::string WebServer::lock_json(lock::Lock *obj, lock::LockState value, JsonDetail start_config) { - return json::build_json([obj, value, start_config](JsonObject root) { + return json::build_json([this, obj, value, start_config](JsonObject root) { set_json_icon_state_value(root, obj, "lock-" + obj->get_object_id(), lock::lock_state_to_string(value), value, start_config); if (start_config == DETAIL_ALL) { @@ -1587,7 +1587,7 @@ std::string WebServer::valve_all_json_generator(WebServer *web_server, void *sou return web_server->valve_json((valve::Valve *) (source), DETAIL_ALL); } std::string WebServer::valve_json(valve::Valve *obj, JsonDetail start_config) { - return json::build_json([obj, start_config](JsonObject root) { + return json::build_json([this, obj, start_config](JsonObject root) { set_json_icon_state_value(root, obj, "valve-" + obj->get_object_id(), obj->is_fully_closed() ? "CLOSED" : "OPEN", obj->position, start_config); root["current_operation"] = valve::valve_operation_to_str(obj->current_operation); @@ -1664,7 +1664,7 @@ std::string WebServer::alarm_control_panel_all_json_generator(WebServer *web_ser std::string WebServer::alarm_control_panel_json(alarm_control_panel::AlarmControlPanel *obj, alarm_control_panel::AlarmControlPanelState value, JsonDetail start_config) { - return json::build_json([obj, value, start_config](JsonObject root) { + return json::build_json([this, obj, value, start_config](JsonObject root) { char buf[16]; set_json_icon_state_value(root, obj, "alarm-control-panel-" + obj->get_object_id(), PSTR_LOCAL(alarm_control_panel_state_to_string(value)), value, start_config); @@ -1709,7 +1709,7 @@ std::string WebServer::event_all_json_generator(WebServer *web_server, void *sou return web_server->event_json((event::Event *) (source), *(((event::Event *) (source))->last_event_type), DETAIL_ALL); } std::string WebServer::event_json(event::Event *obj, const std::string &event_type, JsonDetail start_config) { - return json::build_json([obj, event_type, start_config](JsonObject root) { + return json::build_json([this, obj, event_type, start_config](JsonObject root) { set_json_id(root, obj, "event-" + obj->get_object_id(), start_config); if (!event_type.empty()) { root["event_type"] = event_type; @@ -1768,7 +1768,7 @@ std::string WebServer::update_all_json_generator(WebServer *web_server, void *so return web_server->update_json((update::UpdateEntity *) (source), DETAIL_STATE); } std::string WebServer::update_json(update::UpdateEntity *obj, JsonDetail start_config) { - return json::build_json([obj, start_config](JsonObject root) { + return json::build_json([this, obj, start_config](JsonObject root) { set_json_id(root, obj, "update-" + obj->get_object_id(), start_config); root["value"] = obj->update_info.latest_version; switch (obj->state) { From 697ca1c7be41c56e09521e697e0fdea6b3f72fe2 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 27 Jun 2025 10:17:33 -0500 Subject: [PATCH 04/11] simplify --- esphome/components/web_server/web_server.cpp | 60 ++++---------------- esphome/components/web_server/web_server.h | 2 - 2 files changed, 11 insertions(+), 51 deletions(-) diff --git a/esphome/components/web_server/web_server.cpp b/esphome/components/web_server/web_server.cpp index 510cc3c2a4..053a3e693a 100644 --- a/esphome/components/web_server/web_server.cpp +++ b/esphome/components/web_server/web_server.cpp @@ -413,9 +413,7 @@ std::string WebServer::sensor_json(sensor::Sensor *obj, float value, JsonDetail } set_json_icon_state_value(root, obj, "sensor-" + obj->get_object_id(), state, value, start_config); if (start_config == DETAIL_ALL) { -#ifdef USE_WEBSERVER_SORTING this->add_sorting_info_(root, obj); -#endif if (!obj->get_unit_of_measurement().empty()) root["uom"] = obj->get_unit_of_measurement(); } @@ -459,9 +457,7 @@ std::string WebServer::text_sensor_json(text_sensor::TextSensor *obj, const std: return json::build_json([this, obj, value, start_config](JsonObject root) { set_json_icon_state_value(root, obj, "text_sensor-" + obj->get_object_id(), value, value, start_config); if (start_config == DETAIL_ALL) { -#ifdef USE_WEBSERVER_SORTING this->add_sorting_info_(root, obj); -#endif } }); } @@ -513,9 +509,7 @@ std::string WebServer::switch_json(switch_::Switch *obj, bool value, JsonDetail set_json_icon_state_value(root, obj, "switch-" + obj->get_object_id(), value ? "ON" : "OFF", value, start_config); if (start_config == DETAIL_ALL) { root["assumed_state"] = obj->assumed_state(); -#ifdef USE_WEBSERVER_SORTING this->add_sorting_info_(root, obj); -#endif } }); } @@ -555,9 +549,7 @@ std::string WebServer::button_json(button::Button *obj, JsonDetail start_config) return json::build_json([this, obj, start_config](JsonObject root) { set_json_id(root, obj, "button-" + obj->get_object_id(), start_config); if (start_config == DETAIL_ALL) { -#ifdef USE_WEBSERVER_SORTING this->add_sorting_info_(root, obj); -#endif } }); } @@ -599,9 +591,7 @@ std::string WebServer::binary_sensor_json(binary_sensor::BinarySensor *obj, bool set_json_icon_state_value(root, obj, "binary_sensor-" + obj->get_object_id(), value ? "ON" : "OFF", value, start_config); if (start_config == DETAIL_ALL) { -#ifdef USE_WEBSERVER_SORTING this->add_sorting_info_(root, obj); -#endif } }); } @@ -686,9 +676,7 @@ std::string WebServer::fan_json(fan::Fan *obj, JsonDetail start_config) { if (obj->get_traits().supports_oscillation()) root["oscillation"] = obj->oscillating; if (start_config == DETAIL_ALL) { -#ifdef USE_WEBSERVER_SORTING this->add_sorting_info_(root, obj); -#endif } }); } @@ -808,9 +796,7 @@ std::string WebServer::light_json(light::LightState *obj, JsonDetail start_confi for (auto const &option : obj->get_effects()) { opt.add(option->get_name()); } -#ifdef USE_WEBSERVER_SORTING this->add_sorting_info_(root, obj); -#endif } }); } @@ -895,9 +881,7 @@ std::string WebServer::cover_json(cover::Cover *obj, JsonDetail start_config) { if (obj->get_traits().get_supports_tilt()) root["tilt"] = obj->tilt; if (start_config == DETAIL_ALL) { -#ifdef USE_WEBSERVER_SORTING this->add_sorting_info_(root, obj); -#endif } }); } @@ -962,9 +946,7 @@ std::string WebServer::number_json(number::Number *obj, float value, JsonDetail root["mode"] = (int) obj->traits.get_mode(); if (!obj->traits.get_unit_of_measurement().empty()) root["uom"] = obj->traits.get_unit_of_measurement(); -#ifdef USE_WEBSERVER_SORTING this->add_sorting_info_(root, obj); -#endif } if (std::isnan(value)) { root["value"] = "\"NaN\""; @@ -1037,9 +1019,7 @@ std::string WebServer::date_json(datetime::DateEntity *obj, JsonDetail start_con root["value"] = value; root["state"] = value; if (start_config == DETAIL_ALL) { -#ifdef USE_WEBSERVER_SORTING this->add_sorting_info_(root, obj); -#endif } }); } @@ -1101,9 +1081,7 @@ std::string WebServer::time_json(datetime::TimeEntity *obj, JsonDetail start_con root["value"] = value; root["state"] = value; if (start_config == DETAIL_ALL) { -#ifdef USE_WEBSERVER_SORTING this->add_sorting_info_(root, obj); -#endif } }); } @@ -1166,9 +1144,7 @@ std::string WebServer::datetime_json(datetime::DateTimeEntity *obj, JsonDetail s root["value"] = value; root["state"] = value; if (start_config == DETAIL_ALL) { -#ifdef USE_WEBSERVER_SORTING this->add_sorting_info_(root, obj); -#endif } }); } @@ -1233,9 +1209,7 @@ std::string WebServer::text_json(text::Text *obj, const std::string &value, Json root["value"] = value; if (start_config == DETAIL_ALL) { root["mode"] = (int) obj->traits.get_mode(); -#ifdef USE_WEBSERVER_SORTING this->add_sorting_info_(root, obj); -#endif } }); } @@ -1295,9 +1269,7 @@ std::string WebServer::select_json(select::Select *obj, const std::string &value for (auto &option : obj->traits.get_options()) { opt.add(option); } -#ifdef USE_WEBSERVER_SORTING this->add_sorting_info_(root, obj); -#endif } }); } @@ -1418,9 +1390,7 @@ std::string WebServer::climate_json(climate::Climate *obj, JsonDetail start_conf for (auto const &custom_preset : traits.get_supported_custom_presets()) opt.add(custom_preset); } -#ifdef USE_WEBSERVER_SORTING this->add_sorting_info_(root, obj); -#endif } bool has_state = false; @@ -1517,9 +1487,7 @@ std::string WebServer::lock_json(lock::Lock *obj, lock::LockState value, JsonDet set_json_icon_state_value(root, obj, "lock-" + obj->get_object_id(), lock::lock_state_to_string(value), value, start_config); if (start_config == DETAIL_ALL) { -#ifdef USE_WEBSERVER_SORTING this->add_sorting_info_(root, obj); -#endif } }); } @@ -1595,9 +1563,7 @@ std::string WebServer::valve_json(valve::Valve *obj, JsonDetail start_config) { if (obj->get_traits().get_supports_position()) root["position"] = obj->position; if (start_config == DETAIL_ALL) { -#ifdef USE_WEBSERVER_SORTING this->add_sorting_info_(root, obj); -#endif } }); } @@ -1669,9 +1635,7 @@ std::string WebServer::alarm_control_panel_json(alarm_control_panel::AlarmContro set_json_icon_state_value(root, obj, "alarm-control-panel-" + obj->get_object_id(), PSTR_LOCAL(alarm_control_panel_state_to_string(value)), value, start_config); if (start_config == DETAIL_ALL) { -#ifdef USE_WEBSERVER_SORTING this->add_sorting_info_(root, obj); -#endif } }); } @@ -1720,9 +1684,7 @@ std::string WebServer::event_json(event::Event *obj, const std::string &event_ty event_types.add(event_type); } root["device_class"] = obj->get_device_class(); -#ifdef USE_WEBSERVER_SORTING this->add_sorting_info_(root, obj); -#endif } }); } @@ -1790,9 +1752,7 @@ std::string WebServer::update_json(update::UpdateEntity *obj, JsonDetail start_c root["title"] = obj->update_info.title; root["summary"] = obj->update_info.summary; root["release_url"] = obj->update_info.release_url; -#ifdef USE_WEBSERVER_SORTING this->add_sorting_info_(root, obj); -#endif } }); } @@ -2102,6 +2062,17 @@ void WebServer::handleRequest(AsyncWebServerRequest *request) { bool WebServer::isRequestHandlerTrivial() const { return false; } +void WebServer::add_sorting_info_(JsonObject &root, EntityBase *entity) { +#ifdef USE_WEBSERVER_SORTING + if (this->sorting_entitys_.find(entity) != this->sorting_entitys_.end()) { + root["sorting_weight"] = this->sorting_entitys_[entity].weight; + if (this->sorting_groups_.find(this->sorting_entitys_[entity].group_id) != this->sorting_groups_.end()) { + root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[entity].group_id].name; + } + } +#endif +} + #ifdef USE_WEBSERVER_SORTING void WebServer::add_entity_config(EntityBase *entity, float weight, uint64_t group) { this->sorting_entitys_[entity] = SortingComponents{weight, group}; @@ -2110,15 +2081,6 @@ void WebServer::add_entity_config(EntityBase *entity, float weight, uint64_t gro void WebServer::add_sorting_group(uint64_t group_id, const std::string &group_name, float weight) { this->sorting_groups_[group_id] = SortingGroup{group_name, weight}; } - -void WebServer::add_sorting_info_(JsonObject &root, EntityBase *entity) { - if (this->sorting_entitys_.find(entity) != this->sorting_entitys_.end()) { - root["sorting_weight"] = this->sorting_entitys_[entity].weight; - if (this->sorting_groups_.find(this->sorting_entitys_[entity].group_id) != this->sorting_groups_.end()) { - root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[entity].group_id].name; - } - } -} #endif void WebServer::schedule_(std::function &&f) { diff --git a/esphome/components/web_server/web_server.h b/esphome/components/web_server/web_server.h index 3b095e7661..3be99eebae 100644 --- a/esphome/components/web_server/web_server.h +++ b/esphome/components/web_server/web_server.h @@ -487,9 +487,7 @@ class WebServer : public Controller, public Component, public AsyncWebHandler { bool include_internal_{false}; protected: -#ifdef USE_WEBSERVER_SORTING void add_sorting_info_(JsonObject &root, EntityBase *entity); -#endif void schedule_(std::function &&f); web_server_base::WebServerBase *base_; #ifdef USE_ARDUINO From d0a402f20163b662271d9c83d7c5007217083988 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 27 Jun 2025 12:49:44 -0500 Subject: [PATCH 05/11] Extract lock-free queue and event pool to core helpers --- esphome/components/esp32_ble/ble.cpp | 1 - esphome/components/esp32_ble/ble.h | 8 ++-- esphome/components/esp32_ble/ble_event.h | 3 ++ .../ble_event_pool.h => core/event_pool.h} | 31 ++++++------- .../queue.h => core/lock_free_queue.h} | 46 +++++++++++++++---- 5 files changed, 59 insertions(+), 30 deletions(-) rename esphome/{components/esp32_ble/ble_event_pool.h => core/event_pool.h} (61%) rename esphome/{components/esp32_ble/queue.h => core/lock_free_queue.h} (58%) diff --git a/esphome/components/esp32_ble/ble.cpp b/esphome/components/esp32_ble/ble.cpp index b10d1fe10a..8b0cf4da98 100644 --- a/esphome/components/esp32_ble/ble.cpp +++ b/esphome/components/esp32_ble/ble.cpp @@ -1,7 +1,6 @@ #ifdef USE_ESP32 #include "ble.h" -#include "ble_event_pool.h" #include "esphome/core/application.h" #include "esphome/core/helpers.h" diff --git a/esphome/components/esp32_ble/ble.h b/esphome/components/esp32_ble/ble.h index 9fe996086e..ce452d65c4 100644 --- a/esphome/components/esp32_ble/ble.h +++ b/esphome/components/esp32_ble/ble.h @@ -12,8 +12,8 @@ #include "esphome/core/helpers.h" #include "ble_event.h" -#include "ble_event_pool.h" -#include "queue.h" +#include "esphome/core/lock_free_queue.h" +#include "esphome/core/event_pool.h" #ifdef USE_ESP32 @@ -148,8 +148,8 @@ class ESP32BLE : public Component { std::vector ble_status_event_handlers_; BLEComponentState state_{BLE_COMPONENT_STATE_OFF}; - LockFreeQueue ble_events_; - BLEEventPool ble_event_pool_; + esphome::LockFreeQueue ble_events_; + esphome::EventPool ble_event_pool_; BLEAdvertising *advertising_{}; esp_ble_io_cap_t io_cap_{ESP_IO_CAP_NONE}; uint32_t advertising_cycle_time_{}; diff --git a/esphome/components/esp32_ble/ble_event.h b/esphome/components/esp32_ble/ble_event.h index dd3ec3da42..bbb4984b9c 100644 --- a/esphome/components/esp32_ble/ble_event.h +++ b/esphome/components/esp32_ble/ble_event.h @@ -139,6 +139,9 @@ class BLEEvent { // Default constructor for pre-allocation in pool BLEEvent() : type_(GAP) {} + // Invoked on return to EventPool + void clear() { this->cleanup_heap_data(); } + // Clean up any heap-allocated data void cleanup_heap_data() { if (this->type_ == GAP) { diff --git a/esphome/components/esp32_ble/ble_event_pool.h b/esphome/core/event_pool.h similarity index 61% rename from esphome/components/esp32_ble/ble_event_pool.h rename to esphome/core/event_pool.h index ef123b1325..39537267ca 100644 --- a/esphome/components/esp32_ble/ble_event_pool.h +++ b/esphome/core/event_pool.h @@ -4,22 +4,20 @@ #include #include -#include "ble_event.h" -#include "queue.h" #include "esphome/core/helpers.h" +#include "esphome/core/lock_free_queue.h" namespace esphome { -namespace esp32_ble { -// BLE Event Pool - On-demand pool of BLEEvent objects to avoid heap fragmentation +// Event Pool - On-demand pool of objects to avoid heap fragmentation // Events are allocated on first use and reused thereafter, growing to peak usage -template class BLEEventPool { +template class EventPool { public: - BLEEventPool() : total_created_(0) {} + EventPool() : total_created_(0) {} - ~BLEEventPool() { + ~EventPool() { // Clean up any remaining events in the free list - BLEEvent *event; + T *event; while ((event = this->free_list_.pop()) != nullptr) { delete event; } @@ -27,9 +25,9 @@ template class BLEEventPool { // Allocate an event from the pool // Returns nullptr if pool is full - BLEEvent *allocate() { + T *allocate() { // Try to get from free list first - BLEEvent *event = this->free_list_.pop(); + T *event = this->free_list_.pop(); if (event != nullptr) return event; @@ -40,7 +38,7 @@ template class BLEEventPool { } // Use internal RAM for better performance - RAMAllocator allocator(RAMAllocator::ALLOC_INTERNAL); + RAMAllocator allocator(RAMAllocator::ALLOC_INTERNAL); event = allocator.allocate(1); if (event == nullptr) { @@ -49,24 +47,25 @@ template class BLEEventPool { } // Placement new to construct the object - new (event) BLEEvent(); + new (event) T(); this->total_created_++; return event; } // Return an event to the pool for reuse - void release(BLEEvent *event) { + void release(T *event) { if (event != nullptr) { + // Clean up the event's allocated memory + event->clear(); this->free_list_.push(event); } } private: - LockFreeQueue free_list_; // Free events ready for reuse - uint8_t total_created_; // Total events created (high water mark) + LockFreeQueue free_list_; // Free events ready for reuse + uint8_t total_created_; // Total events created (high water mark) }; -} // namespace esp32_ble } // namespace esphome #endif diff --git a/esphome/components/esp32_ble/queue.h b/esphome/core/lock_free_queue.h similarity index 58% rename from esphome/components/esp32_ble/queue.h rename to esphome/core/lock_free_queue.h index 75bf1eef25..ec26d268a0 100644 --- a/esphome/components/esp32_ble/queue.h +++ b/esphome/core/lock_free_queue.h @@ -4,23 +4,25 @@ #include #include +#include +#include /* - * BLE events come in from a separate Task (thread) in the ESP32 stack. Rather - * than using mutex-based locking, this lock-free queue allows the BLE - * task to enqueue events without blocking. The main loop() then processes - * these events at a safer time. + * Lock-free queue for single-producer single-consumer scenarios. + * This allows one thread to push items and another to pop them without + * blocking each other. * * This is a Single-Producer Single-Consumer (SPSC) lock-free ring buffer. - * The BLE task is the only producer, and the main loop() is the only consumer. + * Common use cases: + * - BLE events: BLE task produces, main loop consumes + * - MQTT messages: main task produces, MQTT thread consumes */ namespace esphome { -namespace esp32_ble { template class LockFreeQueue { public: - LockFreeQueue() : head_(0), tail_(0), dropped_count_(0) {} + LockFreeQueue() : head_(0), tail_(0), dropped_count_(0), task_to_notify_(nullptr) {} bool push(T *element) { if (element == nullptr) @@ -29,14 +31,37 @@ template class LockFreeQueue { uint8_t current_tail = tail_.load(std::memory_order_relaxed); uint8_t next_tail = (current_tail + 1) % SIZE; - if (next_tail == head_.load(std::memory_order_acquire)) { + // Read head before incrementing tail + uint8_t head_before = head_.load(std::memory_order_acquire); + + if (next_tail == head_before) { // Buffer full dropped_count_.fetch_add(1, std::memory_order_relaxed); return false; } + // Check if queue was empty before push + bool was_empty = (current_tail == head_before); + buffer_[current_tail] = element; tail_.store(next_tail, std::memory_order_release); + + // Notify optimization: only notify if we need to + if (task_to_notify_ != nullptr) { + if (was_empty) { + // Queue was empty - consumer might be going to sleep, must notify + xTaskNotifyGive(task_to_notify_); + } else { + // Queue wasn't empty - check if consumer has caught up to previous tail + uint8_t head_after = head_.load(std::memory_order_acquire); + if (head_after == current_tail) { + // Consumer just caught up to where tail was - might go to sleep, must notify + xTaskNotifyGive(task_to_notify_); + } + // Otherwise: consumer is still behind, no need to notify + } + } + return true; } @@ -69,6 +94,8 @@ template class LockFreeQueue { return next_tail == head_.load(std::memory_order_acquire); } + void set_task_to_notify(TaskHandle_t task) { task_to_notify_ = task; } + protected: T *buffer_[SIZE]; // Atomic: written by producer (push/increment), read+reset by consumer (get_and_reset) @@ -77,9 +104,10 @@ template class LockFreeQueue { std::atomic head_; // Atomic: written by producer (push), read by consumer (pop) to check if empty std::atomic tail_; + // Task handle for notification (optional) + TaskHandle_t task_to_notify_; }; -} // namespace esp32_ble } // namespace esphome #endif From 949689c318dae94aa8cc588d81753182d90b2c1b Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 27 Jun 2025 12:55:58 -0500 Subject: [PATCH 06/11] address bot review --- esphome/core/event_pool.h | 6 +++++- esphome/core/lock_free_queue.h | 3 +++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/esphome/core/event_pool.h b/esphome/core/event_pool.h index 39537267ca..6d61e9a80d 100644 --- a/esphome/core/event_pool.h +++ b/esphome/core/event_pool.h @@ -18,8 +18,12 @@ template class EventPool { ~EventPool() { // Clean up any remaining events in the free list T *event; + RAMAllocator allocator(RAMAllocator::ALLOC_INTERNAL); while ((event = this->free_list_.pop()) != nullptr) { - delete event; + // Call destructor + event->~T(); + // Deallocate using RAMAllocator + allocator.deallocate(event, 1); } } diff --git a/esphome/core/lock_free_queue.h b/esphome/core/lock_free_queue.h index ec26d268a0..ede7496737 100644 --- a/esphome/core/lock_free_queue.h +++ b/esphome/core/lock_free_queue.h @@ -94,6 +94,9 @@ template class LockFreeQueue { return next_tail == head_.load(std::memory_order_acquire); } + // Set the FreeRTOS task handle to notify when items are pushed to the queue + // This enables efficient wake-up of a consumer task that's waiting for data + // @param task The FreeRTOS task handle to notify, or nullptr to disable notifications void set_task_to_notify(TaskHandle_t task) { task_to_notify_ = task; } protected: From 3b6bd55d1e581422c0348daab7b97ce023b97b96 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 27 Jun 2025 13:16:06 -0500 Subject: [PATCH 07/11] address bot comments --- esphome/core/event_pool.h | 8 ++++++-- esphome/core/lock_free_queue.h | 15 +++++++++++++-- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/esphome/core/event_pool.h b/esphome/core/event_pool.h index 6d61e9a80d..198c0fe380 100644 --- a/esphome/core/event_pool.h +++ b/esphome/core/event_pool.h @@ -1,6 +1,6 @@ #pragma once -#ifdef USE_ESP32 +#if defined(USE_ESP32) || defined(USE_LIBRETINY) #include #include @@ -17,6 +17,10 @@ template class EventPool { ~EventPool() { // Clean up any remaining events in the free list + // IMPORTANT: This destructor assumes no concurrent access. The EventPool must not + // be destroyed while any thread might still call allocate() or release(). + // In practice, this is typically ensured by destroying the pool only during + // component shutdown when all producer/consumer threads have been stopped. T *event; RAMAllocator allocator(RAMAllocator::ALLOC_INTERNAL); while ((event = this->free_list_.pop()) != nullptr) { @@ -72,4 +76,4 @@ template class EventPool { } // namespace esphome -#endif +#endif // defined(USE_ESP32) || defined(USE_LIBRETINY) diff --git a/esphome/core/lock_free_queue.h b/esphome/core/lock_free_queue.h index ede7496737..1fc5d25048 100644 --- a/esphome/core/lock_free_queue.h +++ b/esphome/core/lock_free_queue.h @@ -1,11 +1,17 @@ #pragma once -#ifdef USE_ESP32 +#if defined(USE_ESP32) || defined(USE_LIBRETINY) #include #include + +#if defined(USE_ESP32) #include #include +#elif defined(USE_LIBRETINY) +#include +#include +#endif /* * Lock-free queue for single-producer single-consumer scenarios. @@ -13,6 +19,8 @@ * blocking each other. * * This is a Single-Producer Single-Consumer (SPSC) lock-free ring buffer. + * Available on platforms with FreeRTOS support (ESP32, LibreTiny). + * * Common use cases: * - BLE events: BLE task produces, main loop consumes * - MQTT messages: main task produces, MQTT thread consumes @@ -56,6 +64,9 @@ template class LockFreeQueue { uint8_t head_after = head_.load(std::memory_order_acquire); if (head_after == current_tail) { // Consumer just caught up to where tail was - might go to sleep, must notify + // Note: There's a benign race here - between reading head_after and calling + // xTaskNotifyGive(), the consumer could advance further. This would result + // in an unnecessary wake-up, but is harmless and extremely rare in practice. xTaskNotifyGive(task_to_notify_); } // Otherwise: consumer is still behind, no need to notify @@ -113,4 +124,4 @@ template class LockFreeQueue { } // namespace esphome -#endif +#endif // defined(USE_ESP32) || defined(USE_LIBRETINY) From 95ef131285ee86b3ad09cfafa027ae9f7caab49c Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 27 Jun 2025 13:18:39 -0500 Subject: [PATCH 08/11] address bot comments --- esphome/core/event_pool.h | 4 +++- esphome/core/lock_free_queue.h | 5 +++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/esphome/core/event_pool.h b/esphome/core/event_pool.h index 198c0fe380..7a206f823e 100644 --- a/esphome/core/event_pool.h +++ b/esphome/core/event_pool.h @@ -11,6 +11,8 @@ namespace esphome { // Event Pool - On-demand pool of objects to avoid heap fragmentation // Events are allocated on first use and reused thereafter, growing to peak usage +// @tparam T The type of objects managed by the pool (must have a clear() method) +// @tparam SIZE The maximum number of objects in the pool (1-255, limited by uint8_t) template class EventPool { public: EventPool() : total_created_(0) {} @@ -71,7 +73,7 @@ template class EventPool { private: LockFreeQueue free_list_; // Free events ready for reuse - uint8_t total_created_; // Total events created (high water mark) + uint8_t total_created_; // Total events created (high water mark, max 255) }; } // namespace esphome diff --git a/esphome/core/lock_free_queue.h b/esphome/core/lock_free_queue.h index 1fc5d25048..5460be0fae 100644 --- a/esphome/core/lock_free_queue.h +++ b/esphome/core/lock_free_queue.h @@ -24,6 +24,9 @@ * Common use cases: * - BLE events: BLE task produces, main loop consumes * - MQTT messages: main task produces, MQTT thread consumes + * + * @tparam T The type of elements stored in the queue (must be a pointer type) + * @tparam SIZE The maximum number of elements (1-255, limited by uint8_t indices) */ namespace esphome { @@ -115,6 +118,8 @@ template class LockFreeQueue { // Atomic: written by producer (push/increment), read+reset by consumer (get_and_reset) std::atomic dropped_count_; // 65535 max - more than enough for drop tracking // Atomic: written by consumer (pop), read by producer (push) to check if full + // Using uint8_t limits queue size to 255 elements but saves memory and ensures + // atomic operations are efficient on all platforms std::atomic head_; // Atomic: written by producer (push), read by consumer (pop) to check if empty std::atomic tail_; From d00a00d142eae924247d561e75f29ca9c67fa9ba Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 27 Jun 2025 13:58:22 -0500 Subject: [PATCH 09/11] Reduce libretiny logconfig messages align with https://developers.esphome.io/architecture/logging --- esphome/components/libretiny/lt_component.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/esphome/components/libretiny/lt_component.cpp b/esphome/components/libretiny/lt_component.cpp index ec4b60eaeb..ffccd0ad7a 100644 --- a/esphome/components/libretiny/lt_component.cpp +++ b/esphome/components/libretiny/lt_component.cpp @@ -10,9 +10,11 @@ namespace libretiny { static const char *const TAG = "lt.component"; void LTComponent::dump_config() { - ESP_LOGCONFIG(TAG, "LibreTiny:"); - ESP_LOGCONFIG(TAG, " Version: %s", LT_BANNER_STR + 10); - ESP_LOGCONFIG(TAG, " Loglevel: %u", LT_LOGLEVEL); + ESP_LOGCONFIG(TAG, + "LibreTiny:\n" + " Version: %s\n" + " Loglevel: %u", + LT_BANNER_STR + 10, LT_LOGLEVEL); #ifdef USE_TEXT_SENSOR if (this->version_ != nullptr) { From 9af88bd4825c225e1a4f497c4ef2c2699e153929 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 27 Jun 2025 14:07:27 -0500 Subject: [PATCH 10/11] DNM: Update libsodium needs https://github.com/esphome/noise-c/pull/4 --- esphome/components/api/__init__.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/esphome/components/api/__init__.py b/esphome/components/api/__init__.py index bd131ef8de..452ea98245 100644 --- a/esphome/components/api/__init__.py +++ b/esphome/components/api/__init__.py @@ -177,7 +177,11 @@ async def to_code(config): # and plaintext disabled. Only a factory reset can remove it. cg.add_define("USE_API_PLAINTEXT") cg.add_define("USE_API_NOISE") - cg.add_library("esphome/noise-c", "0.1.6") + cg.add_library( + None, + None, + "https://github.com/esphome/noise-c.git#libsodium_update", + ) else: cg.add_define("USE_API_PLAINTEXT") From 90736f367a0a0a857906c3a7fd21553e45f5f246 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 27 Jun 2025 16:36:32 -0500 Subject: [PATCH 11/11] release --- esphome/components/esp32_ble/ble_event.h | 15 ++++++--------- esphome/core/event_pool.h | 4 ++-- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/esphome/components/esp32_ble/ble_event.h b/esphome/components/esp32_ble/ble_event.h index bbb4984b9c..9268c710f3 100644 --- a/esphome/components/esp32_ble/ble_event.h +++ b/esphome/components/esp32_ble/ble_event.h @@ -134,16 +134,13 @@ class BLEEvent { } // Destructor to clean up heap allocations - ~BLEEvent() { this->cleanup_heap_data(); } + ~BLEEvent() { this->release(); } // Default constructor for pre-allocation in pool BLEEvent() : type_(GAP) {} - // Invoked on return to EventPool - void clear() { this->cleanup_heap_data(); } - - // Clean up any heap-allocated data - void cleanup_heap_data() { + // Invoked on return to EventPool - clean up any heap-allocated data + void release() { if (this->type_ == GAP) { return; } @@ -164,19 +161,19 @@ class BLEEvent { // Load new event data for reuse (replaces previous event data) void load_gap_event(esp_gap_ble_cb_event_t e, esp_ble_gap_cb_param_t *p) { - this->cleanup_heap_data(); + this->release(); this->type_ = GAP; this->init_gap_data_(e, p); } void load_gattc_event(esp_gattc_cb_event_t e, esp_gatt_if_t i, esp_ble_gattc_cb_param_t *p) { - this->cleanup_heap_data(); + this->release(); this->type_ = GATTC; this->init_gattc_data_(e, i, p); } void load_gatts_event(esp_gatts_cb_event_t e, esp_gatt_if_t i, esp_ble_gatts_cb_param_t *p) { - this->cleanup_heap_data(); + this->release(); this->type_ = GATTS; this->init_gatts_data_(e, i, p); } diff --git a/esphome/core/event_pool.h b/esphome/core/event_pool.h index 7a206f823e..69e03bafac 100644 --- a/esphome/core/event_pool.h +++ b/esphome/core/event_pool.h @@ -11,7 +11,7 @@ namespace esphome { // Event Pool - On-demand pool of objects to avoid heap fragmentation // Events are allocated on first use and reused thereafter, growing to peak usage -// @tparam T The type of objects managed by the pool (must have a clear() method) +// @tparam T The type of objects managed by the pool (must have a release() method) // @tparam SIZE The maximum number of objects in the pool (1-255, limited by uint8_t) template class EventPool { public: @@ -66,7 +66,7 @@ template class EventPool { void release(T *event) { if (event != nullptr) { // Clean up the event's allocated memory - event->clear(); + event->release(); this->free_list_.push(event); } }