1
0
mirror of https://github.com/esphome/esphome.git synced 2026-02-12 18:51:55 +00:00

Compare commits

...

17 Commits

Author SHA1 Message Date
J. Nick Koston
aa870483f1 Reduce SerializationBuffer stack size from 768 to 512 bytes
768 bytes on the httpd task stack contributes to stack overflow when
combined with other stack-allocated buffers (query string parsing, etc.).
512 bytes still covers all typical JSON payloads (sensors ~200B, lights
~170B, climate ~500-700B). Only extreme edge cases with 40+ options
trigger heap fallback.
2026-02-11 18:58:28 -06:00
J. Nick Koston
840ad30880 Merge branch 'dev' into json_web_server_stack 2026-01-29 14:48:48 -10:00
J. Nick Koston
cfe121b38b private implementation details 2026-01-29 18:45:18 -06:00
J. Nick Koston
5fbd9d5b14 avoid misuse 2026-01-29 18:23:25 -06:00
J. Nick Koston
2b1783ce61 tweak 2026-01-29 18:19:29 -06:00
J. Nick Koston
904072ce79 make sure NRVO works 2026-01-29 18:17:34 -06:00
J. Nick Koston
0a4b98d74a fix double templates 2026-01-29 17:43:26 -06:00
J. Nick Koston
b8017de724 avoid regressing performance of mqtt 2026-01-29 16:55:20 -06:00
J. Nick Koston
ca96604582 change all the cases 2026-01-29 16:49:28 -06:00
J. Nick Koston
d18d378f06 missed a few 2026-01-29 16:27:28 -06:00
J. Nick Koston
83e3752544 missed a few 2026-01-29 16:26:50 -06:00
J. Nick Koston
0490b2d450 no dummy 2026-01-29 16:24:30 -06:00
J. Nick Koston
55ff740e4e no dummy 2026-01-29 16:23:41 -06:00
J. Nick Koston
aba8a83cba ard as well 2026-01-29 16:02:32 -06:00
J. Nick Koston
a23809d5db fixes 2026-01-29 15:41:29 -06:00
J. Nick Koston
32fc3ea6f5 config stack 2026-01-29 15:33:24 -06:00
J. Nick Koston
deb8ffd348 json webserver stack 2026-01-29 15:26:37 -06:00
9 changed files with 346 additions and 184 deletions

View File

@@ -264,9 +264,9 @@ template<typename... Ts> class APIRespondAction : public Action<Ts...> {
// Build and send JSON response
json::JsonBuilder builder;
this->json_builder_(x..., builder.root());
std::string json_str = builder.serialize();
auto json_buf = builder.serialize();
this->parent_->send_action_response(call_id, success, StringRef(error_message),
reinterpret_cast<const uint8_t *>(json_str.data()), json_str.size());
reinterpret_cast<const uint8_t *>(json_buf.data()), json_buf.size());
return;
}
#endif

View File

@@ -15,7 +15,7 @@ static const char *const TAG = "json";
static SpiRamAllocator global_json_allocator;
#endif
std::string build_json(const json_build_t &f) {
SerializationBuffer<> build_json(const json_build_t &f) {
// NOLINTBEGIN(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
JsonBuilder builder;
JsonObject root = builder.root();
@@ -61,14 +61,62 @@ JsonDocument parse_json(const uint8_t *data, size_t len) {
// NOLINTEND(clang-analyzer-cplusplus.NewDeleteLeaks)
}
std::string JsonBuilder::serialize() {
SerializationBuffer<> JsonBuilder::serialize() {
// ===========================================================================================
// CRITICAL: NRVO (Named Return Value Optimization) - DO NOT REFACTOR WITHOUT UNDERSTANDING
// ===========================================================================================
//
// This function is carefully structured to enable NRVO. The compiler constructs `result`
// directly in the caller's stack frame, eliminating the move constructor call entirely.
//
// WITHOUT NRVO: Each return would trigger SerializationBuffer's move constructor, which
// must memcpy up to 512 bytes of stack buffer content. This happens on EVERY JSON
// serialization (sensor updates, web server responses, MQTT publishes, etc.).
//
// WITH NRVO: Zero memcpy, zero move constructor overhead. The buffer lives directly
// where the caller needs it.
//
// Requirements for NRVO to work:
// 1. Single named variable (`result`) returned from ALL paths
// 2. All paths must return the SAME variable (not different variables)
// 3. No std::move() on the return statement
//
// If you must modify this function:
// - Keep a single `result` variable declared at the top
// - All code paths must return `result` (not a different variable)
// - Verify NRVO still works by checking the disassembly for move constructor calls
// - Test: objdump -d -C firmware.elf | grep "SerializationBuffer.*SerializationBuffer"
// Should show only destructor, NOT move constructor
//
// Why we avoid measureJson(): It instantiates DummyWriter templates adding ~1KB flash.
// Instead, try stack buffer first. 512 bytes covers 99.9% of JSON payloads (sensors ~200B,
// lights ~170B, climate ~700B). Only entities with 40+ options exceed this.
//
// ===========================================================================================
constexpr size_t buf_size = SerializationBuffer<>::BUFFER_SIZE;
SerializationBuffer<> result(buf_size - 1); // Max content size (reserve 1 for null)
if (doc_.overflowed()) {
ESP_LOGE(TAG, "JSON document overflow");
return "{}";
auto *buf = result.data_writable_();
buf[0] = '{';
buf[1] = '}';
buf[2] = '\0';
result.set_size_(2);
return result;
}
std::string output;
serializeJson(doc_, output);
return output;
size_t size = serializeJson(doc_, result.data_writable_(), buf_size);
if (size < buf_size) {
// Fits in stack buffer - update size to actual length
result.set_size_(size);
return result;
}
// Needs heap allocation - reallocate and serialize again with exact size
result.reallocate_heap_(size);
serializeJson(doc_, result.data_writable_(), size + 1);
return result;
}
} // namespace json

View File

@@ -1,5 +1,7 @@
#pragma once
#include <cstring>
#include <string>
#include <vector>
#include "esphome/core/defines.h"
@@ -14,6 +16,108 @@
namespace esphome {
namespace json {
/// Buffer for JSON serialization that uses stack allocation for small payloads.
/// Template parameter STACK_SIZE specifies the stack buffer size (default 512 bytes).
/// Supports move semantics for efficient return-by-value.
template<size_t STACK_SIZE = 512> class SerializationBuffer {
public:
static constexpr size_t BUFFER_SIZE = STACK_SIZE; ///< Stack buffer size for this instantiation
/// Construct with known size (typically from measureJson)
explicit SerializationBuffer(size_t size) : size_(size) {
if (size + 1 <= STACK_SIZE) {
buffer_ = stack_buffer_;
} else {
heap_buffer_ = new char[size + 1];
buffer_ = heap_buffer_;
}
buffer_[0] = '\0';
}
~SerializationBuffer() { delete[] heap_buffer_; }
// Move constructor - works with same template instantiation
SerializationBuffer(SerializationBuffer &&other) noexcept : heap_buffer_(other.heap_buffer_), size_(other.size_) {
if (other.buffer_ == other.stack_buffer_) {
// Stack buffer - must copy content
std::memcpy(stack_buffer_, other.stack_buffer_, size_ + 1);
buffer_ = stack_buffer_;
} else {
// Heap buffer - steal ownership
buffer_ = heap_buffer_;
other.heap_buffer_ = nullptr;
}
// Leave moved-from object in valid empty state
other.stack_buffer_[0] = '\0';
other.buffer_ = other.stack_buffer_;
other.size_ = 0;
}
// Move assignment
SerializationBuffer &operator=(SerializationBuffer &&other) noexcept {
if (this != &other) {
delete[] heap_buffer_;
heap_buffer_ = other.heap_buffer_;
size_ = other.size_;
if (other.buffer_ == other.stack_buffer_) {
std::memcpy(stack_buffer_, other.stack_buffer_, size_ + 1);
buffer_ = stack_buffer_;
} else {
buffer_ = heap_buffer_;
other.heap_buffer_ = nullptr;
}
// Leave moved-from object in valid empty state
other.stack_buffer_[0] = '\0';
other.buffer_ = other.stack_buffer_;
other.size_ = 0;
}
return *this;
}
// Delete copy operations
SerializationBuffer(const SerializationBuffer &) = delete;
SerializationBuffer &operator=(const SerializationBuffer &) = delete;
/// Get null-terminated C string
const char *c_str() const { return buffer_; }
/// Get data pointer
const char *data() const { return buffer_; }
/// Get string length (excluding null terminator)
size_t size() const { return size_; }
/// Implicit conversion to std::string for backward compatibility
/// WARNING: This allocates a new std::string on the heap. Prefer using
/// c_str() or data()/size() directly when possible to avoid allocation.
operator std::string() const { return std::string(buffer_, size_); } // NOLINT(google-explicit-constructor)
private:
friend class JsonBuilder; ///< Allows JsonBuilder::serialize() to call private methods
/// Get writable buffer (for serialization)
char *data_writable_() { return buffer_; }
/// Set actual size after serialization (must not exceed allocated size)
/// Also ensures null termination for c_str() safety
void set_size_(size_t size) {
size_ = size;
buffer_[size] = '\0';
}
/// Reallocate to heap buffer with new size (for when stack buffer is too small)
/// This invalidates any previous buffer content. Used by JsonBuilder::serialize().
void reallocate_heap_(size_t size) {
delete[] heap_buffer_;
heap_buffer_ = new char[size + 1];
buffer_ = heap_buffer_;
size_ = size;
buffer_[0] = '\0';
}
char stack_buffer_[STACK_SIZE];
char *heap_buffer_{nullptr};
char *buffer_;
size_t size_;
};
#ifdef USE_PSRAM
// Build an allocator for the JSON Library using the RAMAllocator class
// This is only compiled when PSRAM is enabled
@@ -46,7 +150,8 @@ using json_parse_t = std::function<bool(JsonObject)>;
using json_build_t = std::function<void(JsonObject)>;
/// Build a JSON string with the provided json build function.
std::string build_json(const json_build_t &f);
/// Returns SerializationBuffer for stack-first allocation; implicitly converts to std::string.
SerializationBuffer<> build_json(const json_build_t &f);
/// Parse a JSON string and run the provided json parse function if it's valid.
bool parse_json(const std::string &data, const json_parse_t &f);
@@ -69,7 +174,9 @@ class JsonBuilder {
return root_;
}
std::string serialize();
/// Serialize the JSON document to a SerializationBuffer (stack-first allocation)
/// Uses 512-byte stack buffer by default, falls back to heap for larger JSON
SerializationBuffer<> serialize();
private:
#ifdef USE_PSRAM

View File

@@ -564,8 +564,8 @@ bool MQTTClientComponent::publish(const char *topic, const char *payload, size_t
}
bool MQTTClientComponent::publish_json(const char *topic, const json::json_build_t &f, uint8_t qos, bool retain) {
std::string message = json::build_json(f);
return this->publish(topic, message.c_str(), message.length(), qos, retain);
auto message = json::build_json(f);
return this->publish(topic, message.c_str(), message.size(), qos, retain);
}
void MQTTClientComponent::enable() {

View File

@@ -209,7 +209,7 @@ void DeferredUpdateEventSource::deq_push_back_with_dedup_(void *source, message_
void DeferredUpdateEventSource::process_deferred_queue_() {
while (!deferred_queue_.empty()) {
DeferredEvent &de = deferred_queue_.front();
std::string message = de.message_generator_(web_server_, de.source_);
auto message = de.message_generator_(web_server_, de.source_);
if (this->send(message.c_str(), "state") != DISCARDED) {
// O(n) but memory efficiency is more important than speed here which is why std::vector was chosen
deferred_queue_.erase(deferred_queue_.begin());
@@ -266,7 +266,7 @@ void DeferredUpdateEventSource::deferrable_send_state(void *source, const char *
// deferred queue still not empty which means downstream event queue full, no point trying to send first
deq_push_back_with_dedup_(source, message_generator);
} else {
std::string message = message_generator(web_server_, source);
auto message = message_generator(web_server_, source);
if (this->send(message.c_str(), "state") == DISCARDED) {
deq_push_back_with_dedup_(source, message_generator);
} else {
@@ -320,7 +320,7 @@ void DeferredUpdateEventSourceList::on_client_connect_(DeferredUpdateEventSource
ws->defer([ws, source]() {
// Configure reconnect timeout and send config
// this should always go through since the AsyncEventSourceClient event queue is empty on connect
std::string message = ws->get_config_json();
auto message = ws->get_config_json();
source->try_send_nodefer(message.c_str(), "ping", millis(), 30000);
#ifdef USE_WEBSERVER_SORTING
@@ -329,10 +329,10 @@ void DeferredUpdateEventSourceList::on_client_connect_(DeferredUpdateEventSource
JsonObject root = builder.root();
root[ESPHOME_F("name")] = group.second.name;
root[ESPHOME_F("sorting_weight")] = group.second.weight;
message = builder.serialize();
auto group_msg = builder.serialize();
// up to 31 groups should be able to be queued initially without defer
source->try_send_nodefer(message.c_str(), "sorting_group");
source->try_send_nodefer(group_msg.c_str(), "sorting_group");
}
#endif
@@ -365,7 +365,7 @@ void WebServer::set_css_include(const char *css_include) { this->css_include_ =
void WebServer::set_js_include(const char *js_include) { this->js_include_ = js_include; }
#endif
std::string WebServer::get_config_json() {
json::SerializationBuffer<> WebServer::get_config_json() {
json::JsonBuilder builder;
JsonObject root = builder.root();
@@ -597,20 +597,20 @@ void WebServer::handle_sensor_request(AsyncWebServerRequest *request, const UrlM
// 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->sensor_json_(obj, obj->state, detail);
auto data = this->sensor_json_(obj, obj->state, detail);
request->send(200, "application/json", data.c_str());
return;
}
}
request->send(404);
}
std::string WebServer::sensor_state_json_generator(WebServer *web_server, void *source) {
json::SerializationBuffer<> WebServer::sensor_state_json_generator(WebServer *web_server, void *source) {
return web_server->sensor_json_((sensor::Sensor *) (source), ((sensor::Sensor *) (source))->state, DETAIL_STATE);
}
std::string WebServer::sensor_all_json_generator(WebServer *web_server, void *source) {
json::SerializationBuffer<> WebServer::sensor_all_json_generator(WebServer *web_server, void *source) {
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) {
json::SerializationBuffer<> WebServer::sensor_json_(sensor::Sensor *obj, float value, JsonDetail start_config) {
json::JsonBuilder builder;
JsonObject root = builder.root();
@@ -644,23 +644,23 @@ void WebServer::handle_text_sensor_request(AsyncWebServerRequest *request, const
// 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->text_sensor_json_(obj, obj->state, detail);
auto data = this->text_sensor_json_(obj, obj->state, detail);
request->send(200, "application/json", data.c_str());
return;
}
}
request->send(404);
}
std::string WebServer::text_sensor_state_json_generator(WebServer *web_server, void *source) {
json::SerializationBuffer<> WebServer::text_sensor_state_json_generator(WebServer *web_server, void *source) {
return web_server->text_sensor_json_((text_sensor::TextSensor *) (source),
((text_sensor::TextSensor *) (source))->state, DETAIL_STATE);
}
std::string WebServer::text_sensor_all_json_generator(WebServer *web_server, void *source) {
json::SerializationBuffer<> WebServer::text_sensor_all_json_generator(WebServer *web_server, void *source) {
return web_server->text_sensor_json_((text_sensor::TextSensor *) (source),
((text_sensor::TextSensor *) (source))->state, DETAIL_ALL);
}
std::string WebServer::text_sensor_json_(text_sensor::TextSensor *obj, const std::string &value,
JsonDetail start_config) {
json::SerializationBuffer<> WebServer::text_sensor_json_(text_sensor::TextSensor *obj, const std::string &value,
JsonDetail start_config) {
json::JsonBuilder builder;
JsonObject root = builder.root();
@@ -705,7 +705,7 @@ void WebServer::handle_switch_request(AsyncWebServerRequest *request, const UrlM
if (request->method() == HTTP_GET && entity_match.action_is_empty) {
auto detail = get_request_detail(request);
std::string data = this->switch_json_(obj, obj->state, detail);
auto data = this->switch_json_(obj, obj->state, detail);
request->send(200, "application/json", data.c_str());
return;
}
@@ -734,13 +734,13 @@ void WebServer::handle_switch_request(AsyncWebServerRequest *request, const UrlM
}
request->send(404);
}
std::string WebServer::switch_state_json_generator(WebServer *web_server, void *source) {
json::SerializationBuffer<> WebServer::switch_state_json_generator(WebServer *web_server, void *source) {
return web_server->switch_json_((switch_::Switch *) (source), ((switch_::Switch *) (source))->state, DETAIL_STATE);
}
std::string WebServer::switch_all_json_generator(WebServer *web_server, void *source) {
json::SerializationBuffer<> WebServer::switch_all_json_generator(WebServer *web_server, void *source) {
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) {
json::SerializationBuffer<> WebServer::switch_json_(switch_::Switch *obj, bool value, JsonDetail start_config) {
json::JsonBuilder builder;
JsonObject root = builder.root();
@@ -762,7 +762,7 @@ void WebServer::handle_button_request(AsyncWebServerRequest *request, const UrlM
continue;
if (request->method() == HTTP_GET && entity_match.action_is_empty) {
auto detail = get_request_detail(request);
std::string data = this->button_json_(obj, detail);
auto data = this->button_json_(obj, detail);
request->send(200, "application/json", data.c_str());
} else if (match.method_equals(ESPHOME_F("press"))) {
DEFER_ACTION(obj, obj->press());
@@ -775,10 +775,10 @@ void WebServer::handle_button_request(AsyncWebServerRequest *request, const UrlM
}
request->send(404);
}
std::string WebServer::button_all_json_generator(WebServer *web_server, void *source) {
json::SerializationBuffer<> WebServer::button_all_json_generator(WebServer *web_server, void *source) {
return web_server->button_json_((button::Button *) (source), DETAIL_ALL);
}
std::string WebServer::button_json_(button::Button *obj, JsonDetail start_config) {
json::SerializationBuffer<> WebServer::button_json_(button::Button *obj, JsonDetail start_config) {
json::JsonBuilder builder;
JsonObject root = builder.root();
@@ -805,22 +805,23 @@ void WebServer::handle_binary_sensor_request(AsyncWebServerRequest *request, con
// 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->binary_sensor_json_(obj, obj->state, detail);
auto data = this->binary_sensor_json_(obj, obj->state, detail);
request->send(200, "application/json", data.c_str());
return;
}
}
request->send(404);
}
std::string WebServer::binary_sensor_state_json_generator(WebServer *web_server, void *source) {
json::SerializationBuffer<> WebServer::binary_sensor_state_json_generator(WebServer *web_server, void *source) {
return web_server->binary_sensor_json_((binary_sensor::BinarySensor *) (source),
((binary_sensor::BinarySensor *) (source))->state, DETAIL_STATE);
}
std::string WebServer::binary_sensor_all_json_generator(WebServer *web_server, void *source) {
json::SerializationBuffer<> WebServer::binary_sensor_all_json_generator(WebServer *web_server, void *source) {
return web_server->binary_sensor_json_((binary_sensor::BinarySensor *) (source),
((binary_sensor::BinarySensor *) (source))->state, DETAIL_ALL);
}
std::string WebServer::binary_sensor_json_(binary_sensor::BinarySensor *obj, bool value, JsonDetail start_config) {
json::SerializationBuffer<> WebServer::binary_sensor_json_(binary_sensor::BinarySensor *obj, bool value,
JsonDetail start_config) {
json::JsonBuilder builder;
JsonObject root = builder.root();
@@ -847,7 +848,7 @@ void WebServer::handle_fan_request(AsyncWebServerRequest *request, const UrlMatc
if (request->method() == HTTP_GET && entity_match.action_is_empty) {
auto detail = get_request_detail(request);
std::string data = this->fan_json_(obj, detail);
auto data = this->fan_json_(obj, detail);
request->send(200, "application/json", data.c_str());
} else if (match.method_equals(ESPHOME_F("toggle"))) {
DEFER_ACTION(obj, obj->toggle().perform());
@@ -888,13 +889,13 @@ void WebServer::handle_fan_request(AsyncWebServerRequest *request, const UrlMatc
}
request->send(404);
}
std::string WebServer::fan_state_json_generator(WebServer *web_server, void *source) {
json::SerializationBuffer<> WebServer::fan_state_json_generator(WebServer *web_server, void *source) {
return web_server->fan_json_((fan::Fan *) (source), DETAIL_STATE);
}
std::string WebServer::fan_all_json_generator(WebServer *web_server, void *source) {
json::SerializationBuffer<> WebServer::fan_all_json_generator(WebServer *web_server, void *source) {
return web_server->fan_json_((fan::Fan *) (source), DETAIL_ALL);
}
std::string WebServer::fan_json_(fan::Fan *obj, JsonDetail start_config) {
json::SerializationBuffer<> WebServer::fan_json_(fan::Fan *obj, JsonDetail start_config) {
json::JsonBuilder builder;
JsonObject root = builder.root();
@@ -928,7 +929,7 @@ void WebServer::handle_light_request(AsyncWebServerRequest *request, const UrlMa
if (request->method() == HTTP_GET && entity_match.action_is_empty) {
auto detail = get_request_detail(request);
std::string data = this->light_json_(obj, detail);
auto data = this->light_json_(obj, detail);
request->send(200, "application/json", data.c_str());
} else if (match.method_equals(ESPHOME_F("toggle"))) {
DEFER_ACTION(obj, obj->toggle().perform());
@@ -967,13 +968,13 @@ void WebServer::handle_light_request(AsyncWebServerRequest *request, const UrlMa
}
request->send(404);
}
std::string WebServer::light_state_json_generator(WebServer *web_server, void *source) {
json::SerializationBuffer<> WebServer::light_state_json_generator(WebServer *web_server, void *source) {
return web_server->light_json_((light::LightState *) (source), DETAIL_STATE);
}
std::string WebServer::light_all_json_generator(WebServer *web_server, void *source) {
json::SerializationBuffer<> WebServer::light_all_json_generator(WebServer *web_server, void *source) {
return web_server->light_json_((light::LightState *) (source), DETAIL_ALL);
}
std::string WebServer::light_json_(light::LightState *obj, JsonDetail start_config) {
json::SerializationBuffer<> WebServer::light_json_(light::LightState *obj, JsonDetail start_config) {
json::JsonBuilder builder;
JsonObject root = builder.root();
@@ -1007,7 +1008,7 @@ void WebServer::handle_cover_request(AsyncWebServerRequest *request, const UrlMa
if (request->method() == HTTP_GET && entity_match.action_is_empty) {
auto detail = get_request_detail(request);
std::string data = this->cover_json_(obj, detail);
auto data = this->cover_json_(obj, detail);
request->send(200, "application/json", data.c_str());
return;
}
@@ -1055,13 +1056,13 @@ void WebServer::handle_cover_request(AsyncWebServerRequest *request, const UrlMa
}
request->send(404);
}
std::string WebServer::cover_state_json_generator(WebServer *web_server, void *source) {
json::SerializationBuffer<> WebServer::cover_state_json_generator(WebServer *web_server, void *source) {
return web_server->cover_json_((cover::Cover *) (source), DETAIL_STATE);
}
std::string WebServer::cover_all_json_generator(WebServer *web_server, void *source) {
json::SerializationBuffer<> WebServer::cover_all_json_generator(WebServer *web_server, void *source) {
return web_server->cover_json_((cover::Cover *) (source), DETAIL_ALL);
}
std::string WebServer::cover_json_(cover::Cover *obj, JsonDetail start_config) {
json::SerializationBuffer<> WebServer::cover_json_(cover::Cover *obj, JsonDetail start_config) {
json::JsonBuilder builder;
JsonObject root = builder.root();
@@ -1096,7 +1097,7 @@ void WebServer::handle_number_request(AsyncWebServerRequest *request, const UrlM
if (request->method() == HTTP_GET && entity_match.action_is_empty) {
auto detail = get_request_detail(request);
std::string data = this->number_json_(obj, obj->state, detail);
auto data = this->number_json_(obj, obj->state, detail);
request->send(200, "application/json", data.c_str());
return;
}
@@ -1115,13 +1116,13 @@ void WebServer::handle_number_request(AsyncWebServerRequest *request, const UrlM
request->send(404);
}
std::string WebServer::number_state_json_generator(WebServer *web_server, void *source) {
json::SerializationBuffer<> WebServer::number_state_json_generator(WebServer *web_server, void *source) {
return web_server->number_json_((number::Number *) (source), ((number::Number *) (source))->state, DETAIL_STATE);
}
std::string WebServer::number_all_json_generator(WebServer *web_server, void *source) {
json::SerializationBuffer<> WebServer::number_all_json_generator(WebServer *web_server, void *source) {
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) {
json::SerializationBuffer<> WebServer::number_json_(number::Number *obj, float value, JsonDetail start_config) {
json::JsonBuilder builder;
JsonObject root = builder.root();
@@ -1163,7 +1164,7 @@ void WebServer::handle_date_request(AsyncWebServerRequest *request, const UrlMat
continue;
if (request->method() == HTTP_GET && entity_match.action_is_empty) {
auto detail = get_request_detail(request);
std::string data = this->date_json_(obj, detail);
auto data = this->date_json_(obj, detail);
request->send(200, "application/json", data.c_str());
return;
}
@@ -1188,13 +1189,13 @@ void WebServer::handle_date_request(AsyncWebServerRequest *request, const UrlMat
request->send(404);
}
std::string WebServer::date_state_json_generator(WebServer *web_server, void *source) {
json::SerializationBuffer<> WebServer::date_state_json_generator(WebServer *web_server, void *source) {
return web_server->date_json_((datetime::DateEntity *) (source), DETAIL_STATE);
}
std::string WebServer::date_all_json_generator(WebServer *web_server, void *source) {
json::SerializationBuffer<> WebServer::date_all_json_generator(WebServer *web_server, void *source) {
return web_server->date_json_((datetime::DateEntity *) (source), DETAIL_ALL);
}
std::string WebServer::date_json_(datetime::DateEntity *obj, JsonDetail start_config) {
json::SerializationBuffer<> WebServer::date_json_(datetime::DateEntity *obj, JsonDetail start_config) {
json::JsonBuilder builder;
JsonObject root = builder.root();
@@ -1223,7 +1224,7 @@ void WebServer::handle_time_request(AsyncWebServerRequest *request, const UrlMat
continue;
if (request->method() == HTTP_GET && entity_match.action_is_empty) {
auto detail = get_request_detail(request);
std::string data = this->time_json_(obj, detail);
auto data = this->time_json_(obj, detail);
request->send(200, "application/json", data.c_str());
return;
}
@@ -1247,13 +1248,13 @@ void WebServer::handle_time_request(AsyncWebServerRequest *request, const UrlMat
}
request->send(404);
}
std::string WebServer::time_state_json_generator(WebServer *web_server, void *source) {
json::SerializationBuffer<> WebServer::time_state_json_generator(WebServer *web_server, void *source) {
return web_server->time_json_((datetime::TimeEntity *) (source), DETAIL_STATE);
}
std::string WebServer::time_all_json_generator(WebServer *web_server, void *source) {
json::SerializationBuffer<> WebServer::time_all_json_generator(WebServer *web_server, void *source) {
return web_server->time_json_((datetime::TimeEntity *) (source), DETAIL_ALL);
}
std::string WebServer::time_json_(datetime::TimeEntity *obj, JsonDetail start_config) {
json::SerializationBuffer<> WebServer::time_json_(datetime::TimeEntity *obj, JsonDetail start_config) {
json::JsonBuilder builder;
JsonObject root = builder.root();
@@ -1282,7 +1283,7 @@ void WebServer::handle_datetime_request(AsyncWebServerRequest *request, const Ur
continue;
if (request->method() == HTTP_GET && entity_match.action_is_empty) {
auto detail = get_request_detail(request);
std::string data = this->datetime_json_(obj, detail);
auto data = this->datetime_json_(obj, detail);
request->send(200, "application/json", data.c_str());
return;
}
@@ -1306,13 +1307,13 @@ void WebServer::handle_datetime_request(AsyncWebServerRequest *request, const Ur
}
request->send(404);
}
std::string WebServer::datetime_state_json_generator(WebServer *web_server, void *source) {
json::SerializationBuffer<> WebServer::datetime_state_json_generator(WebServer *web_server, void *source) {
return web_server->datetime_json_((datetime::DateTimeEntity *) (source), DETAIL_STATE);
}
std::string WebServer::datetime_all_json_generator(WebServer *web_server, void *source) {
json::SerializationBuffer<> WebServer::datetime_all_json_generator(WebServer *web_server, void *source) {
return web_server->datetime_json_((datetime::DateTimeEntity *) (source), DETAIL_ALL);
}
std::string WebServer::datetime_json_(datetime::DateTimeEntity *obj, JsonDetail start_config) {
json::SerializationBuffer<> WebServer::datetime_json_(datetime::DateTimeEntity *obj, JsonDetail start_config) {
json::JsonBuilder builder;
JsonObject root = builder.root();
@@ -1343,7 +1344,7 @@ void WebServer::handle_text_request(AsyncWebServerRequest *request, const UrlMat
if (request->method() == HTTP_GET && entity_match.action_is_empty) {
auto detail = get_request_detail(request);
std::string data = this->text_json_(obj, obj->state, detail);
auto data = this->text_json_(obj, obj->state, detail);
request->send(200, "application/json", data.c_str());
return;
}
@@ -1362,13 +1363,13 @@ void WebServer::handle_text_request(AsyncWebServerRequest *request, const UrlMat
request->send(404);
}
std::string WebServer::text_state_json_generator(WebServer *web_server, void *source) {
json::SerializationBuffer<> WebServer::text_state_json_generator(WebServer *web_server, void *source) {
return web_server->text_json_((text::Text *) (source), ((text::Text *) (source))->state, DETAIL_STATE);
}
std::string WebServer::text_all_json_generator(WebServer *web_server, void *source) {
json::SerializationBuffer<> WebServer::text_all_json_generator(WebServer *web_server, void *source) {
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) {
json::SerializationBuffer<> WebServer::text_json_(text::Text *obj, const std::string &value, JsonDetail start_config) {
json::JsonBuilder builder;
JsonObject root = builder.root();
@@ -1400,7 +1401,7 @@ void WebServer::handle_select_request(AsyncWebServerRequest *request, const UrlM
if (request->method() == HTTP_GET && entity_match.action_is_empty) {
auto detail = get_request_detail(request);
std::string data = this->select_json_(obj, obj->has_state() ? obj->current_option() : StringRef(), detail);
auto data = this->select_json_(obj, obj->has_state() ? obj->current_option() : StringRef(), detail);
request->send(200, "application/json", data.c_str());
return;
}
@@ -1419,15 +1420,15 @@ void WebServer::handle_select_request(AsyncWebServerRequest *request, const UrlM
}
request->send(404);
}
std::string WebServer::select_state_json_generator(WebServer *web_server, void *source) {
json::SerializationBuffer<> WebServer::select_state_json_generator(WebServer *web_server, void *source) {
auto *obj = (select::Select *) (source);
return web_server->select_json_(obj, obj->has_state() ? obj->current_option() : StringRef(), DETAIL_STATE);
}
std::string WebServer::select_all_json_generator(WebServer *web_server, void *source) {
json::SerializationBuffer<> WebServer::select_all_json_generator(WebServer *web_server, void *source) {
auto *obj = (select::Select *) (source);
return web_server->select_json_(obj, obj->has_state() ? obj->current_option() : StringRef(), DETAIL_ALL);
}
std::string WebServer::select_json_(select::Select *obj, StringRef value, JsonDetail start_config) {
json::SerializationBuffer<> WebServer::select_json_(select::Select *obj, StringRef value, JsonDetail start_config) {
json::JsonBuilder builder;
JsonObject root = builder.root();
@@ -1459,7 +1460,7 @@ void WebServer::handle_climate_request(AsyncWebServerRequest *request, const Url
if (request->method() == HTTP_GET && entity_match.action_is_empty) {
auto detail = get_request_detail(request);
std::string data = this->climate_json_(obj, detail);
auto data = this->climate_json_(obj, detail);
request->send(200, "application/json", data.c_str());
return;
}
@@ -1488,15 +1489,15 @@ void WebServer::handle_climate_request(AsyncWebServerRequest *request, const Url
}
request->send(404);
}
std::string WebServer::climate_state_json_generator(WebServer *web_server, void *source) {
json::SerializationBuffer<> WebServer::climate_state_json_generator(WebServer *web_server, void *source) {
// NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
return web_server->climate_json_((climate::Climate *) (source), DETAIL_STATE);
}
std::string WebServer::climate_all_json_generator(WebServer *web_server, void *source) {
json::SerializationBuffer<> WebServer::climate_all_json_generator(WebServer *web_server, void *source) {
// NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
return web_server->climate_json_((climate::Climate *) (source), DETAIL_ALL);
}
std::string WebServer::climate_json_(climate::Climate *obj, JsonDetail start_config) {
json::SerializationBuffer<> WebServer::climate_json_(climate::Climate *obj, JsonDetail start_config) {
// NOLINTBEGIN(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
json::JsonBuilder builder;
JsonObject root = builder.root();
@@ -1629,7 +1630,7 @@ void WebServer::handle_lock_request(AsyncWebServerRequest *request, const UrlMat
if (request->method() == HTTP_GET && entity_match.action_is_empty) {
auto detail = get_request_detail(request);
std::string data = this->lock_json_(obj, obj->state, detail);
auto data = this->lock_json_(obj, obj->state, detail);
request->send(200, "application/json", data.c_str());
return;
}
@@ -1658,13 +1659,13 @@ void WebServer::handle_lock_request(AsyncWebServerRequest *request, const UrlMat
}
request->send(404);
}
std::string WebServer::lock_state_json_generator(WebServer *web_server, void *source) {
json::SerializationBuffer<> WebServer::lock_state_json_generator(WebServer *web_server, void *source) {
return web_server->lock_json_((lock::Lock *) (source), ((lock::Lock *) (source))->state, DETAIL_STATE);
}
std::string WebServer::lock_all_json_generator(WebServer *web_server, void *source) {
json::SerializationBuffer<> WebServer::lock_all_json_generator(WebServer *web_server, void *source) {
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) {
json::SerializationBuffer<> WebServer::lock_json_(lock::Lock *obj, lock::LockState value, JsonDetail start_config) {
json::JsonBuilder builder;
JsonObject root = builder.root();
@@ -1692,7 +1693,7 @@ void WebServer::handle_valve_request(AsyncWebServerRequest *request, const UrlMa
if (request->method() == HTTP_GET && entity_match.action_is_empty) {
auto detail = get_request_detail(request);
std::string data = this->valve_json_(obj, detail);
auto data = this->valve_json_(obj, detail);
request->send(200, "application/json", data.c_str());
return;
}
@@ -1738,13 +1739,13 @@ void WebServer::handle_valve_request(AsyncWebServerRequest *request, const UrlMa
}
request->send(404);
}
std::string WebServer::valve_state_json_generator(WebServer *web_server, void *source) {
json::SerializationBuffer<> WebServer::valve_state_json_generator(WebServer *web_server, void *source) {
return web_server->valve_json_((valve::Valve *) (source), DETAIL_STATE);
}
std::string WebServer::valve_all_json_generator(WebServer *web_server, void *source) {
json::SerializationBuffer<> WebServer::valve_all_json_generator(WebServer *web_server, void *source) {
return web_server->valve_json_((valve::Valve *) (source), DETAIL_ALL);
}
std::string WebServer::valve_json_(valve::Valve *obj, JsonDetail start_config) {
json::SerializationBuffer<> WebServer::valve_json_(valve::Valve *obj, JsonDetail start_config) {
json::JsonBuilder builder;
JsonObject root = builder.root();
@@ -1777,7 +1778,7 @@ void WebServer::handle_alarm_control_panel_request(AsyncWebServerRequest *reques
if (request->method() == HTTP_GET && entity_match.action_is_empty) {
auto detail = get_request_detail(request);
std::string data = this->alarm_control_panel_json_(obj, obj->get_state(), detail);
auto data = this->alarm_control_panel_json_(obj, obj->get_state(), detail);
request->send(200, "application/json", data.c_str());
return;
}
@@ -1817,19 +1818,19 @@ void WebServer::handle_alarm_control_panel_request(AsyncWebServerRequest *reques
}
request->send(404);
}
std::string WebServer::alarm_control_panel_state_json_generator(WebServer *web_server, void *source) {
json::SerializationBuffer<> WebServer::alarm_control_panel_state_json_generator(WebServer *web_server, void *source) {
return web_server->alarm_control_panel_json_((alarm_control_panel::AlarmControlPanel *) (source),
((alarm_control_panel::AlarmControlPanel *) (source))->get_state(),
DETAIL_STATE);
}
std::string WebServer::alarm_control_panel_all_json_generator(WebServer *web_server, void *source) {
json::SerializationBuffer<> WebServer::alarm_control_panel_all_json_generator(WebServer *web_server, void *source) {
return web_server->alarm_control_panel_json_((alarm_control_panel::AlarmControlPanel *) (source),
((alarm_control_panel::AlarmControlPanel *) (source))->get_state(),
DETAIL_ALL);
}
std::string WebServer::alarm_control_panel_json_(alarm_control_panel::AlarmControlPanel *obj,
alarm_control_panel::AlarmControlPanelState value,
JsonDetail start_config) {
json::SerializationBuffer<> WebServer::alarm_control_panel_json_(alarm_control_panel::AlarmControlPanel *obj,
alarm_control_panel::AlarmControlPanelState value,
JsonDetail start_config) {
json::JsonBuilder builder;
JsonObject root = builder.root();
@@ -1858,7 +1859,7 @@ void WebServer::handle_water_heater_request(AsyncWebServerRequest *request, cons
if (request->method() == HTTP_GET && entity_match.action_is_empty) {
auto detail = get_request_detail(request);
std::string data = this->water_heater_json_(obj, detail);
auto data = this->water_heater_json_(obj, detail);
request->send(200, "application/json", data.c_str());
return;
}
@@ -1894,14 +1895,14 @@ void WebServer::handle_water_heater_request(AsyncWebServerRequest *request, cons
request->send(404);
}
std::string WebServer::water_heater_state_json_generator(WebServer *web_server, void *source) {
json::SerializationBuffer<> WebServer::water_heater_state_json_generator(WebServer *web_server, void *source) {
return web_server->water_heater_json_(static_cast<water_heater::WaterHeater *>(source), DETAIL_STATE);
}
std::string WebServer::water_heater_all_json_generator(WebServer *web_server, void *source) {
json::SerializationBuffer<> WebServer::water_heater_all_json_generator(WebServer *web_server, void *source) {
// NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
return web_server->water_heater_json_(static_cast<water_heater::WaterHeater *>(source), DETAIL_ALL);
}
std::string WebServer::water_heater_json_(water_heater::WaterHeater *obj, JsonDetail start_config) {
json::SerializationBuffer<> WebServer::water_heater_json_(water_heater::WaterHeater *obj, JsonDetail start_config) {
json::JsonBuilder builder;
JsonObject root = builder.root();
char buf[PSTR_LOCAL_SIZE];
@@ -1964,7 +1965,7 @@ void WebServer::handle_infrared_request(AsyncWebServerRequest *request, const Ur
if (request->method() == HTTP_GET && entity_match.action_is_empty) {
auto detail = get_request_detail(request);
std::string data = this->infrared_json_(obj, detail);
auto data = this->infrared_json_(obj, detail);
request->send(200, ESPHOME_F("application/json"), data.c_str());
return;
}
@@ -2035,12 +2036,12 @@ void WebServer::handle_infrared_request(AsyncWebServerRequest *request, const Ur
request->send(404);
}
std::string WebServer::infrared_all_json_generator(WebServer *web_server, void *source) {
json::SerializationBuffer<> WebServer::infrared_all_json_generator(WebServer *web_server, void *source) {
// NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
return web_server->infrared_json_(static_cast<infrared::Infrared *>(source), DETAIL_ALL);
}
std::string WebServer::infrared_json_(infrared::Infrared *obj, JsonDetail start_config) {
json::SerializationBuffer<> WebServer::infrared_json_(infrared::Infrared *obj, JsonDetail start_config) {
json::JsonBuilder builder;
JsonObject root = builder.root();
@@ -2075,7 +2076,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, StringRef(), detail);
auto data = this->event_json_(obj, StringRef(), detail);
request->send(200, "application/json", data.c_str());
return;
}
@@ -2085,16 +2086,16 @@ void WebServer::handle_event_request(AsyncWebServerRequest *request, const UrlMa
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) {
json::SerializationBuffer<> WebServer::event_state_json_generator(WebServer *web_server, void *source) {
auto *event = static_cast<event::Event *>(source);
return web_server->event_json_(event, get_event_type(event), DETAIL_STATE);
}
// NOLINTBEGIN(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
std::string WebServer::event_all_json_generator(WebServer *web_server, void *source) {
json::SerializationBuffer<> WebServer::event_all_json_generator(WebServer *web_server, void *source) {
auto *event = static_cast<event::Event *>(source);
return web_server->event_json_(event, get_event_type(event), DETAIL_ALL);
}
std::string WebServer::event_json_(event::Event *obj, StringRef event_type, JsonDetail start_config) {
json::SerializationBuffer<> WebServer::event_json_(event::Event *obj, StringRef event_type, JsonDetail start_config) {
json::JsonBuilder builder;
JsonObject root = builder.root();
@@ -2141,7 +2142,7 @@ void WebServer::handle_update_request(AsyncWebServerRequest *request, const UrlM
if (request->method() == HTTP_GET && entity_match.action_is_empty) {
auto detail = get_request_detail(request);
std::string data = this->update_json_(obj, detail);
auto data = this->update_json_(obj, detail);
request->send(200, "application/json", data.c_str());
return;
}
@@ -2157,15 +2158,15 @@ void WebServer::handle_update_request(AsyncWebServerRequest *request, const UrlM
}
request->send(404);
}
std::string WebServer::update_state_json_generator(WebServer *web_server, void *source) {
json::SerializationBuffer<> WebServer::update_state_json_generator(WebServer *web_server, void *source) {
// NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
return web_server->update_json_((update::UpdateEntity *) (source), DETAIL_STATE);
}
std::string WebServer::update_all_json_generator(WebServer *web_server, void *source) {
json::SerializationBuffer<> WebServer::update_all_json_generator(WebServer *web_server, void *source) {
// NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
return web_server->update_json_((update::UpdateEntity *) (source), DETAIL_STATE);
}
std::string WebServer::update_json_(update::UpdateEntity *obj, JsonDetail start_config) {
json::SerializationBuffer<> WebServer::update_json_(update::UpdateEntity *obj, JsonDetail start_config) {
// NOLINTBEGIN(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
json::JsonBuilder builder;
JsonObject root = builder.root();

View File

@@ -2,6 +2,7 @@
#include "list_entities.h"
#include "esphome/components/json/json_util.h"
#include "esphome/components/web_server_base/web_server_base.h"
#ifdef USE_WEBSERVER
#include "esphome/core/component.h"
@@ -104,7 +105,7 @@ enum JsonDetail { DETAIL_ALL, DETAIL_STATE };
can be forgotten.
*/
#if !defined(USE_ESP32) && defined(USE_ARDUINO)
using message_generator_t = std::string(WebServer *, void *);
using message_generator_t = json::SerializationBuffer<>(WebServer *, void *);
class DeferredUpdateEventSourceList;
class DeferredUpdateEventSource : public AsyncEventSource {
@@ -263,7 +264,7 @@ class WebServer : public Controller,
void handle_index_request(AsyncWebServerRequest *request);
/// Return the webserver configuration as JSON.
std::string get_config_json();
json::SerializationBuffer<> get_config_json();
#ifdef USE_WEBSERVER_CSS_INCLUDE
/// Handle included css request under '/0.css'.
@@ -285,8 +286,8 @@ class WebServer : public Controller,
/// Handle a sensor request under '/sensor/<id>'.
void handle_sensor_request(AsyncWebServerRequest *request, const UrlMatch &match);
static std::string sensor_state_json_generator(WebServer *web_server, void *source);
static std::string sensor_all_json_generator(WebServer *web_server, void *source);
static json::SerializationBuffer<> sensor_state_json_generator(WebServer *web_server, void *source);
static json::SerializationBuffer<> sensor_all_json_generator(WebServer *web_server, void *source);
#endif
#ifdef USE_SWITCH
@@ -295,8 +296,8 @@ class WebServer : public Controller,
/// Handle a switch request under '/switch/<id>/</turn_on/turn_off/toggle>'.
void handle_switch_request(AsyncWebServerRequest *request, const UrlMatch &match);
static std::string switch_state_json_generator(WebServer *web_server, void *source);
static std::string switch_all_json_generator(WebServer *web_server, void *source);
static json::SerializationBuffer<> switch_state_json_generator(WebServer *web_server, void *source);
static json::SerializationBuffer<> switch_all_json_generator(WebServer *web_server, void *source);
#endif
#ifdef USE_BUTTON
@@ -304,7 +305,7 @@ class WebServer : public Controller,
void handle_button_request(AsyncWebServerRequest *request, const UrlMatch &match);
// Buttons are stateless, so there is no button_state_json_generator
static std::string button_all_json_generator(WebServer *web_server, void *source);
static json::SerializationBuffer<> button_all_json_generator(WebServer *web_server, void *source);
#endif
#ifdef USE_BINARY_SENSOR
@@ -313,8 +314,8 @@ class WebServer : public Controller,
/// Handle a binary sensor request under '/binary_sensor/<id>'.
void handle_binary_sensor_request(AsyncWebServerRequest *request, const UrlMatch &match);
static std::string binary_sensor_state_json_generator(WebServer *web_server, void *source);
static std::string binary_sensor_all_json_generator(WebServer *web_server, void *source);
static json::SerializationBuffer<> binary_sensor_state_json_generator(WebServer *web_server, void *source);
static json::SerializationBuffer<> binary_sensor_all_json_generator(WebServer *web_server, void *source);
#endif
#ifdef USE_FAN
@@ -323,8 +324,8 @@ class WebServer : public Controller,
/// Handle a fan request under '/fan/<id>/</turn_on/turn_off/toggle>'.
void handle_fan_request(AsyncWebServerRequest *request, const UrlMatch &match);
static std::string fan_state_json_generator(WebServer *web_server, void *source);
static std::string fan_all_json_generator(WebServer *web_server, void *source);
static json::SerializationBuffer<> fan_state_json_generator(WebServer *web_server, void *source);
static json::SerializationBuffer<> fan_all_json_generator(WebServer *web_server, void *source);
#endif
#ifdef USE_LIGHT
@@ -333,8 +334,8 @@ class WebServer : public Controller,
/// Handle a light request under '/light/<id>/</turn_on/turn_off/toggle>'.
void handle_light_request(AsyncWebServerRequest *request, const UrlMatch &match);
static std::string light_state_json_generator(WebServer *web_server, void *source);
static std::string light_all_json_generator(WebServer *web_server, void *source);
static json::SerializationBuffer<> light_state_json_generator(WebServer *web_server, void *source);
static json::SerializationBuffer<> light_all_json_generator(WebServer *web_server, void *source);
#endif
#ifdef USE_TEXT_SENSOR
@@ -343,8 +344,8 @@ class WebServer : public Controller,
/// Handle a text sensor request under '/text_sensor/<id>'.
void handle_text_sensor_request(AsyncWebServerRequest *request, const UrlMatch &match);
static std::string text_sensor_state_json_generator(WebServer *web_server, void *source);
static std::string text_sensor_all_json_generator(WebServer *web_server, void *source);
static json::SerializationBuffer<> text_sensor_state_json_generator(WebServer *web_server, void *source);
static json::SerializationBuffer<> text_sensor_all_json_generator(WebServer *web_server, void *source);
#endif
#ifdef USE_COVER
@@ -353,8 +354,8 @@ class WebServer : public Controller,
/// Handle a cover request under '/cover/<id>/<open/close/stop/set>'.
void handle_cover_request(AsyncWebServerRequest *request, const UrlMatch &match);
static std::string cover_state_json_generator(WebServer *web_server, void *source);
static std::string cover_all_json_generator(WebServer *web_server, void *source);
static json::SerializationBuffer<> cover_state_json_generator(WebServer *web_server, void *source);
static json::SerializationBuffer<> cover_all_json_generator(WebServer *web_server, void *source);
#endif
#ifdef USE_NUMBER
@@ -362,8 +363,8 @@ class WebServer : public Controller,
/// Handle a number request under '/number/<id>'.
void handle_number_request(AsyncWebServerRequest *request, const UrlMatch &match);
static std::string number_state_json_generator(WebServer *web_server, void *source);
static std::string number_all_json_generator(WebServer *web_server, void *source);
static json::SerializationBuffer<> number_state_json_generator(WebServer *web_server, void *source);
static json::SerializationBuffer<> number_all_json_generator(WebServer *web_server, void *source);
#endif
#ifdef USE_DATETIME_DATE
@@ -371,8 +372,8 @@ class WebServer : public Controller,
/// Handle a date request under '/date/<id>'.
void handle_date_request(AsyncWebServerRequest *request, const UrlMatch &match);
static std::string date_state_json_generator(WebServer *web_server, void *source);
static std::string date_all_json_generator(WebServer *web_server, void *source);
static json::SerializationBuffer<> date_state_json_generator(WebServer *web_server, void *source);
static json::SerializationBuffer<> date_all_json_generator(WebServer *web_server, void *source);
#endif
#ifdef USE_DATETIME_TIME
@@ -380,8 +381,8 @@ class WebServer : public Controller,
/// Handle a time request under '/time/<id>'.
void handle_time_request(AsyncWebServerRequest *request, const UrlMatch &match);
static std::string time_state_json_generator(WebServer *web_server, void *source);
static std::string time_all_json_generator(WebServer *web_server, void *source);
static json::SerializationBuffer<> time_state_json_generator(WebServer *web_server, void *source);
static json::SerializationBuffer<> time_all_json_generator(WebServer *web_server, void *source);
#endif
#ifdef USE_DATETIME_DATETIME
@@ -389,8 +390,8 @@ class WebServer : public Controller,
/// Handle a datetime request under '/datetime/<id>'.
void handle_datetime_request(AsyncWebServerRequest *request, const UrlMatch &match);
static std::string datetime_state_json_generator(WebServer *web_server, void *source);
static std::string datetime_all_json_generator(WebServer *web_server, void *source);
static json::SerializationBuffer<> datetime_state_json_generator(WebServer *web_server, void *source);
static json::SerializationBuffer<> datetime_all_json_generator(WebServer *web_server, void *source);
#endif
#ifdef USE_TEXT
@@ -398,8 +399,8 @@ class WebServer : public Controller,
/// Handle a text input request under '/text/<id>'.
void handle_text_request(AsyncWebServerRequest *request, const UrlMatch &match);
static std::string text_state_json_generator(WebServer *web_server, void *source);
static std::string text_all_json_generator(WebServer *web_server, void *source);
static json::SerializationBuffer<> text_state_json_generator(WebServer *web_server, void *source);
static json::SerializationBuffer<> text_all_json_generator(WebServer *web_server, void *source);
#endif
#ifdef USE_SELECT
@@ -407,8 +408,8 @@ class WebServer : public Controller,
/// Handle a select request under '/select/<id>'.
void handle_select_request(AsyncWebServerRequest *request, const UrlMatch &match);
static std::string select_state_json_generator(WebServer *web_server, void *source);
static std::string select_all_json_generator(WebServer *web_server, void *source);
static json::SerializationBuffer<> select_state_json_generator(WebServer *web_server, void *source);
static json::SerializationBuffer<> select_all_json_generator(WebServer *web_server, void *source);
#endif
#ifdef USE_CLIMATE
@@ -416,8 +417,8 @@ class WebServer : public Controller,
/// Handle a climate request under '/climate/<id>'.
void handle_climate_request(AsyncWebServerRequest *request, const UrlMatch &match);
static std::string climate_state_json_generator(WebServer *web_server, void *source);
static std::string climate_all_json_generator(WebServer *web_server, void *source);
static json::SerializationBuffer<> climate_state_json_generator(WebServer *web_server, void *source);
static json::SerializationBuffer<> climate_all_json_generator(WebServer *web_server, void *source);
#endif
#ifdef USE_LOCK
@@ -426,8 +427,8 @@ class WebServer : public Controller,
/// Handle a lock request under '/lock/<id>/</lock/unlock/open>'.
void handle_lock_request(AsyncWebServerRequest *request, const UrlMatch &match);
static std::string lock_state_json_generator(WebServer *web_server, void *source);
static std::string lock_all_json_generator(WebServer *web_server, void *source);
static json::SerializationBuffer<> lock_state_json_generator(WebServer *web_server, void *source);
static json::SerializationBuffer<> lock_all_json_generator(WebServer *web_server, void *source);
#endif
#ifdef USE_VALVE
@@ -436,8 +437,8 @@ class WebServer : public Controller,
/// Handle a valve request under '/valve/<id>/<open/close/stop/set>'.
void handle_valve_request(AsyncWebServerRequest *request, const UrlMatch &match);
static std::string valve_state_json_generator(WebServer *web_server, void *source);
static std::string valve_all_json_generator(WebServer *web_server, void *source);
static json::SerializationBuffer<> valve_state_json_generator(WebServer *web_server, void *source);
static json::SerializationBuffer<> valve_all_json_generator(WebServer *web_server, void *source);
#endif
#ifdef USE_ALARM_CONTROL_PANEL
@@ -446,8 +447,8 @@ class WebServer : public Controller,
/// Handle a alarm_control_panel request under '/alarm_control_panel/<id>'.
void handle_alarm_control_panel_request(AsyncWebServerRequest *request, const UrlMatch &match);
static std::string alarm_control_panel_state_json_generator(WebServer *web_server, void *source);
static std::string alarm_control_panel_all_json_generator(WebServer *web_server, void *source);
static json::SerializationBuffer<> alarm_control_panel_state_json_generator(WebServer *web_server, void *source);
static json::SerializationBuffer<> alarm_control_panel_all_json_generator(WebServer *web_server, void *source);
#endif
#ifdef USE_WATER_HEATER
@@ -456,22 +457,22 @@ class WebServer : public Controller,
/// Handle a water_heater request under '/water_heater/<id>/<mode/set>'.
void handle_water_heater_request(AsyncWebServerRequest *request, const UrlMatch &match);
static std::string water_heater_state_json_generator(WebServer *web_server, void *source);
static std::string water_heater_all_json_generator(WebServer *web_server, void *source);
static json::SerializationBuffer<> water_heater_state_json_generator(WebServer *web_server, void *source);
static json::SerializationBuffer<> water_heater_all_json_generator(WebServer *web_server, void *source);
#endif
#ifdef USE_INFRARED
/// Handle an infrared request under '/infrared/<id>/transmit'.
void handle_infrared_request(AsyncWebServerRequest *request, const UrlMatch &match);
static std::string infrared_all_json_generator(WebServer *web_server, void *source);
static json::SerializationBuffer<> infrared_all_json_generator(WebServer *web_server, void *source);
#endif
#ifdef USE_EVENT
void on_event(event::Event *obj) override;
static std::string event_state_json_generator(WebServer *web_server, void *source);
static std::string event_all_json_generator(WebServer *web_server, void *source);
static json::SerializationBuffer<> event_state_json_generator(WebServer *web_server, void *source);
static json::SerializationBuffer<> event_all_json_generator(WebServer *web_server, void *source);
/// Handle a event request under '/event<id>'.
void handle_event_request(AsyncWebServerRequest *request, const UrlMatch &match);
@@ -483,8 +484,8 @@ class WebServer : public Controller,
/// Handle a update request under '/update/<id>'.
void handle_update_request(AsyncWebServerRequest *request, const UrlMatch &match);
static std::string update_state_json_generator(WebServer *web_server, void *source);
static std::string update_all_json_generator(WebServer *web_server, void *source);
static json::SerializationBuffer<> update_state_json_generator(WebServer *web_server, void *source);
static json::SerializationBuffer<> update_all_json_generator(WebServer *web_server, void *source);
#endif
/// Override the web handler's canHandle method.
@@ -609,71 +610,74 @@ class WebServer : public Controller,
private:
#ifdef USE_SENSOR
std::string sensor_json_(sensor::Sensor *obj, float value, JsonDetail start_config);
json::SerializationBuffer<> sensor_json_(sensor::Sensor *obj, float value, JsonDetail start_config);
#endif
#ifdef USE_SWITCH
std::string switch_json_(switch_::Switch *obj, bool value, JsonDetail start_config);
json::SerializationBuffer<> switch_json_(switch_::Switch *obj, bool value, JsonDetail start_config);
#endif
#ifdef USE_BUTTON
std::string button_json_(button::Button *obj, JsonDetail start_config);
json::SerializationBuffer<> button_json_(button::Button *obj, JsonDetail start_config);
#endif
#ifdef USE_BINARY_SENSOR
std::string binary_sensor_json_(binary_sensor::BinarySensor *obj, bool value, JsonDetail start_config);
json::SerializationBuffer<> binary_sensor_json_(binary_sensor::BinarySensor *obj, bool value,
JsonDetail start_config);
#endif
#ifdef USE_FAN
std::string fan_json_(fan::Fan *obj, JsonDetail start_config);
json::SerializationBuffer<> fan_json_(fan::Fan *obj, JsonDetail start_config);
#endif
#ifdef USE_LIGHT
std::string light_json_(light::LightState *obj, JsonDetail start_config);
json::SerializationBuffer<> light_json_(light::LightState *obj, JsonDetail start_config);
#endif
#ifdef USE_TEXT_SENSOR
std::string text_sensor_json_(text_sensor::TextSensor *obj, const std::string &value, JsonDetail start_config);
json::SerializationBuffer<> text_sensor_json_(text_sensor::TextSensor *obj, const std::string &value,
JsonDetail start_config);
#endif
#ifdef USE_COVER
std::string cover_json_(cover::Cover *obj, JsonDetail start_config);
json::SerializationBuffer<> cover_json_(cover::Cover *obj, JsonDetail start_config);
#endif
#ifdef USE_NUMBER
std::string number_json_(number::Number *obj, float value, JsonDetail start_config);
json::SerializationBuffer<> number_json_(number::Number *obj, float value, JsonDetail start_config);
#endif
#ifdef USE_DATETIME_DATE
std::string date_json_(datetime::DateEntity *obj, JsonDetail start_config);
json::SerializationBuffer<> date_json_(datetime::DateEntity *obj, JsonDetail start_config);
#endif
#ifdef USE_DATETIME_TIME
std::string time_json_(datetime::TimeEntity *obj, JsonDetail start_config);
json::SerializationBuffer<> time_json_(datetime::TimeEntity *obj, JsonDetail start_config);
#endif
#ifdef USE_DATETIME_DATETIME
std::string datetime_json_(datetime::DateTimeEntity *obj, JsonDetail start_config);
json::SerializationBuffer<> datetime_json_(datetime::DateTimeEntity *obj, JsonDetail start_config);
#endif
#ifdef USE_TEXT
std::string text_json_(text::Text *obj, const std::string &value, JsonDetail start_config);
json::SerializationBuffer<> text_json_(text::Text *obj, const std::string &value, JsonDetail start_config);
#endif
#ifdef USE_SELECT
std::string select_json_(select::Select *obj, StringRef value, JsonDetail start_config);
json::SerializationBuffer<> select_json_(select::Select *obj, StringRef value, JsonDetail start_config);
#endif
#ifdef USE_CLIMATE
std::string climate_json_(climate::Climate *obj, JsonDetail start_config);
json::SerializationBuffer<> climate_json_(climate::Climate *obj, JsonDetail start_config);
#endif
#ifdef USE_LOCK
std::string lock_json_(lock::Lock *obj, lock::LockState value, JsonDetail start_config);
json::SerializationBuffer<> lock_json_(lock::Lock *obj, lock::LockState value, JsonDetail start_config);
#endif
#ifdef USE_VALVE
std::string valve_json_(valve::Valve *obj, JsonDetail start_config);
json::SerializationBuffer<> valve_json_(valve::Valve *obj, JsonDetail start_config);
#endif
#ifdef USE_ALARM_CONTROL_PANEL
std::string alarm_control_panel_json_(alarm_control_panel::AlarmControlPanel *obj,
alarm_control_panel::AlarmControlPanelState value, JsonDetail start_config);
json::SerializationBuffer<> alarm_control_panel_json_(alarm_control_panel::AlarmControlPanel *obj,
alarm_control_panel::AlarmControlPanelState value,
JsonDetail start_config);
#endif
#ifdef USE_EVENT
std::string event_json_(event::Event *obj, StringRef event_type, JsonDetail start_config);
json::SerializationBuffer<> 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);
json::SerializationBuffer<> water_heater_json_(water_heater::WaterHeater *obj, JsonDetail start_config);
#endif
#ifdef USE_INFRARED
std::string infrared_json_(infrared::Infrared *obj, JsonDetail start_config);
json::SerializationBuffer<> infrared_json_(infrared::Infrared *obj, JsonDetail start_config);
#endif
#ifdef USE_UPDATE
std::string update_json_(update::UpdateEntity *obj, JsonDetail start_config);
json::SerializationBuffer<> update_json_(update::UpdateEntity *obj, JsonDetail start_config);
#endif
};

View File

@@ -507,7 +507,7 @@ AsyncEventSourceResponse::AsyncEventSourceResponse(const AsyncWebServerRequest *
// Configure reconnect timeout and send config
// this should always go through since the tcp send buffer is empty on connect
std::string message = ws->get_config_json();
auto message = ws->get_config_json();
this->try_send_nodefer(message.c_str(), "ping", millis(), 30000);
#ifdef USE_WEBSERVER_SORTING
@@ -561,7 +561,7 @@ void AsyncEventSourceResponse::deq_push_back_with_dedup_(void *source, message_g
void AsyncEventSourceResponse::process_deferred_queue_() {
while (!deferred_queue_.empty()) {
DeferredEvent &de = deferred_queue_.front();
std::string message = de.message_generator_(web_server_, de.source_);
auto message = de.message_generator_(web_server_, de.source_);
if (this->try_send_nodefer(message.c_str(), "state")) {
// O(n) but memory efficiency is more important than speed here which is why std::vector was chosen
deferred_queue_.erase(deferred_queue_.begin());
@@ -798,7 +798,7 @@ void AsyncEventSourceResponse::deferrable_send_state(void *source, const char *e
// trying to send first
deq_push_back_with_dedup_(source, message_generator);
} else {
std::string message = message_generator(web_server_, source);
auto message = message_generator(web_server_, source);
if (!this->try_send_nodefer(message.c_str(), "state")) {
deq_push_back_with_dedup_(source, message_generator);
}

View File

@@ -16,6 +16,7 @@
#include <vector>
#ifdef USE_WEBSERVER
#include "esphome/components/json/json_util.h"
#include "esphome/components/web_server/list_entities.h"
#endif
@@ -250,7 +251,7 @@ class AsyncWebHandler {
class AsyncEventSource;
class AsyncEventSourceResponse;
using message_generator_t = std::string(esphome::web_server::WebServer *, void *);
using message_generator_t = json::SerializationBuffer<>(esphome::web_server::WebServer *, void *);
/*
This class holds a pointer to the source component that wants to publish a state event, and a pointer to a function

View File

@@ -4,15 +4,16 @@ interval:
- interval: 60s
then:
- lambda: |-
// Test build_json
std::string json_str = esphome::json::build_json([](JsonObject root) {
// Test build_json - returns SerializationBuffer, use auto to avoid heap allocation
auto json_buf = esphome::json::build_json([](JsonObject root) {
root["sensor"] = "temperature";
root["value"] = 23.5;
root["unit"] = "°C";
});
ESP_LOGD("test", "Built JSON: %s", json_str.c_str());
ESP_LOGD("test", "Built JSON: %s", json_buf.c_str());
// Test parse_json
// Test parse_json - implicit conversion to std::string for backward compatibility
std::string json_str = json_buf;
bool parse_ok = esphome::json::parse_json(json_str, [](JsonObject root) {
if (root["sensor"].is<const char*>() && root["value"].is<float>()) {
const char* sensor = root["sensor"];
@@ -26,10 +27,10 @@ interval:
});
ESP_LOGD("test", "Parse result (JSON syntax only): %s", parse_ok ? "success" : "failed");
// Test JsonBuilder class
// Test JsonBuilder class - returns SerializationBuffer
esphome::json::JsonBuilder builder;
JsonObject obj = builder.root();
obj["test"] = "direct_builder";
obj["count"] = 42;
std::string result = builder.serialize();
auto result = builder.serialize();
ESP_LOGD("test", "JsonBuilder result: %s", result.c_str());