Compare commits

...

16 Commits

Author SHA1 Message Date
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 768 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. 768 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 768 bytes).
/// Supports move semantics for efficient return-by-value.
template<size_t STACK_SIZE = 768> 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 768-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());