1
0
mirror of https://github.com/esphome/esphome.git synced 2025-09-17 18:52:19 +01:00

Merge branch 'webserver_remove_lambas' into integration

This commit is contained in:
J. Nick Koston
2025-09-15 18:45:14 -05:00
4 changed files with 430 additions and 318 deletions

View File

@@ -35,25 +35,10 @@ struct SpiRamAllocator : ArduinoJson::Allocator {
std::string build_json(const json_build_t &f) { std::string build_json(const json_build_t &f) {
// NOLINTBEGIN(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson // NOLINTBEGIN(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
#ifdef USE_PSRAM JsonBuilder builder;
auto doc_allocator = SpiRamAllocator(); JsonObject root = builder.root();
JsonDocument json_document(&doc_allocator);
#else
JsonDocument json_document;
#endif
if (json_document.overflowed()) {
ESP_LOGE(TAG, "Could not allocate memory for JSON document!");
return "{}";
}
JsonObject root = json_document.to<JsonObject>();
f(root); f(root);
if (json_document.overflowed()) { return builder.serialize();
ESP_LOGE(TAG, "Could not allocate memory for JSON document!");
return "{}";
}
std::string output;
serializeJson(json_document, output);
return output;
// NOLINTEND(clang-analyzer-cplusplus.NewDeleteLeaks) // NOLINTEND(clang-analyzer-cplusplus.NewDeleteLeaks)
} }
@@ -84,5 +69,53 @@ bool parse_json(const std::string &data, const json_parse_t &f) {
// NOLINTEND(clang-analyzer-cplusplus.NewDeleteLeaks) // NOLINTEND(clang-analyzer-cplusplus.NewDeleteLeaks)
} }
// JsonBuilder implementation
class JsonBuilder::Impl {
public:
Impl() {
#ifdef USE_PSRAM
allocator_ = std::make_unique<SpiRamAllocator>();
doc_ = std::make_unique<JsonDocument>(allocator_.get());
#else
doc_ = std::make_unique<JsonDocument>();
#endif
}
JsonObject root() {
if (!root_created_) {
root_ = doc_->to<JsonObject>();
root_created_ = true;
}
return root_;
}
bool overflowed() const { return doc_->overflowed(); }
void serialize_to(std::string &output) { serializeJson(*doc_, output); }
private:
#ifdef USE_PSRAM
std::unique_ptr<SpiRamAllocator> allocator_;
#endif
std::unique_ptr<JsonDocument> doc_;
JsonObject root_;
bool root_created_{false};
};
JsonBuilder::JsonBuilder() : impl_(std::make_unique<Impl>()) {}
JsonBuilder::~JsonBuilder() = default;
JsonObject JsonBuilder::root() { return impl_->root(); }
std::string JsonBuilder::serialize() {
if (impl_->overflowed()) {
ESP_LOGE(TAG, "JSON document overflow");
return "{}";
}
std::string output;
impl_->serialize_to(output);
return output;
}
} // namespace json } // namespace json
} // namespace esphome } // namespace esphome

View File

@@ -25,5 +25,20 @@ std::string build_json(const json_build_t &f);
/// Parse a JSON string and run the provided json parse function if it's valid. /// 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); bool parse_json(const std::string &data, const json_parse_t &f);
/// Builder class for creating JSON documents without lambdas
class JsonBuilder {
public:
JsonBuilder();
~JsonBuilder();
JsonObject root();
std::string serialize();
private:
// Use opaque pointer to hide implementation details
class Impl;
std::unique_ptr<Impl> impl_;
};
} // namespace json } // namespace json
} // namespace esphome } // namespace esphome

View File

@@ -228,10 +228,11 @@ void DeferredUpdateEventSourceList::on_client_connect_(WebServer *ws, DeferredUp
#ifdef USE_WEBSERVER_SORTING #ifdef USE_WEBSERVER_SORTING
for (auto &group : ws->sorting_groups_) { for (auto &group : ws->sorting_groups_) {
message = json::build_json([group](JsonObject root) { json::JsonBuilder builder;
root["name"] = group.second.name; JsonObject root = builder.root();
root["sorting_weight"] = group.second.weight; root["name"] = group.second.name;
}); root["sorting_weight"] = group.second.weight;
message = builder.serialize();
// up to 31 groups should be able to be queued initially without defer // 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(message.c_str(), "sorting_group");
@@ -265,17 +266,20 @@ void WebServer::set_js_include(const char *js_include) { this->js_include_ = js_
#endif #endif
std::string WebServer::get_config_json() { std::string WebServer::get_config_json() {
return json::build_json([this](JsonObject root) { json::JsonBuilder builder;
root["title"] = App.get_friendly_name().empty() ? App.get_name() : App.get_friendly_name(); JsonObject root = builder.root();
root["comment"] = App.get_comment();
root["title"] = App.get_friendly_name().empty() ? App.get_name() : App.get_friendly_name();
root["comment"] = App.get_comment();
#if defined(USE_WEBSERVER_OTA_DISABLED) || !defined(USE_WEBSERVER_OTA) #if defined(USE_WEBSERVER_OTA_DISABLED) || !defined(USE_WEBSERVER_OTA)
root["ota"] = false; // Note: USE_WEBSERVER_OTA_DISABLED only affects web_server, not captive_portal root["ota"] = false; // Note: USE_WEBSERVER_OTA_DISABLED only affects web_server, not captive_portal
#else #else
root["ota"] = true; root["ota"] = true;
#endif #endif
root["log"] = this->expose_log_; root["log"] = this->expose_log_;
root["lang"] = "en"; root["lang"] = "en";
});
return builder.serialize();
} }
void WebServer::setup() { void WebServer::setup() {
@@ -435,22 +439,26 @@ std::string WebServer::sensor_all_json_generator(WebServer *web_server, void *so
return web_server->sensor_json((sensor::Sensor *) (source), ((sensor::Sensor *) (source))->state, DETAIL_ALL); 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) { std::string WebServer::sensor_json(sensor::Sensor *obj, float value, JsonDetail start_config) {
return json::build_json([this, obj, value, start_config](JsonObject root) { json::JsonBuilder builder;
std::string state; JsonObject root = builder.root();
if (std::isnan(value)) {
state = "NA"; // Build JSON directly inline
} else { std::string state;
state = value_accuracy_to_string(value, obj->get_accuracy_decimals()); if (std::isnan(value)) {
if (!obj->get_unit_of_measurement().empty()) state = "NA";
state += " " + obj->get_unit_of_measurement(); } else {
} state = value_accuracy_to_string(value, obj->get_accuracy_decimals());
set_json_icon_state_value(root, obj, "sensor-" + obj->get_object_id(), state, value, start_config); if (!obj->get_unit_of_measurement().empty())
if (start_config == DETAIL_ALL) { state += " " + obj->get_unit_of_measurement();
this->add_sorting_info_(root, obj); }
if (!obj->get_unit_of_measurement().empty()) set_json_icon_state_value(root, obj, "sensor-" + obj->get_object_id(), state, value, start_config);
root["uom"] = obj->get_unit_of_measurement(); if (start_config == DETAIL_ALL) {
} this->add_sorting_info_(root, obj);
}); if (!obj->get_unit_of_measurement().empty())
root["uom"] = obj->get_unit_of_measurement();
}
return builder.serialize();
} }
#endif #endif
@@ -483,12 +491,15 @@ std::string WebServer::text_sensor_all_json_generator(WebServer *web_server, voi
} }
std::string WebServer::text_sensor_json(text_sensor::TextSensor *obj, const std::string &value, std::string WebServer::text_sensor_json(text_sensor::TextSensor *obj, const std::string &value,
JsonDetail start_config) { JsonDetail start_config) {
return json::build_json([this, obj, value, start_config](JsonObject root) { json::JsonBuilder builder;
set_json_icon_state_value(root, obj, "text_sensor-" + obj->get_object_id(), value, value, start_config); JsonObject root = builder.root();
if (start_config == DETAIL_ALL) {
this->add_sorting_info_(root, obj); set_json_icon_state_value(root, obj, "text_sensor-" + obj->get_object_id(), value, value, start_config);
} if (start_config == DETAIL_ALL) {
}); this->add_sorting_info_(root, obj);
}
return builder.serialize();
} }
#endif #endif
@@ -553,13 +564,16 @@ std::string WebServer::switch_all_json_generator(WebServer *web_server, void *so
return web_server->switch_json((switch_::Switch *) (source), ((switch_::Switch *) (source))->state, DETAIL_ALL); 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) { std::string WebServer::switch_json(switch_::Switch *obj, bool value, JsonDetail start_config) {
return json::build_json([this, obj, value, start_config](JsonObject root) { json::JsonBuilder builder;
set_json_icon_state_value(root, obj, "switch-" + obj->get_object_id(), value ? "ON" : "OFF", value, start_config); JsonObject root = builder.root();
if (start_config == DETAIL_ALL) {
root["assumed_state"] = obj->assumed_state(); set_json_icon_state_value(root, obj, "switch-" + obj->get_object_id(), value ? "ON" : "OFF", value, start_config);
this->add_sorting_info_(root, obj); if (start_config == DETAIL_ALL) {
} root["assumed_state"] = obj->assumed_state();
}); this->add_sorting_info_(root, obj);
}
return builder.serialize();
} }
#endif #endif
@@ -590,12 +604,15 @@ std::string WebServer::button_all_json_generator(WebServer *web_server, void *so
return web_server->button_json((button::Button *) (source), DETAIL_ALL); return web_server->button_json((button::Button *) (source), DETAIL_ALL);
} }
std::string WebServer::button_json(button::Button *obj, JsonDetail start_config) { std::string WebServer::button_json(button::Button *obj, JsonDetail start_config) {
return json::build_json([this, obj, start_config](JsonObject root) { json::JsonBuilder builder;
set_json_id(root, obj, "button-" + obj->get_object_id(), start_config); JsonObject root = builder.root();
if (start_config == DETAIL_ALL) {
this->add_sorting_info_(root, obj); set_json_id(root, obj, "button-" + obj->get_object_id(), start_config);
} if (start_config == DETAIL_ALL) {
}); this->add_sorting_info_(root, obj);
}
return builder.serialize();
} }
#endif #endif
@@ -627,13 +644,16 @@ std::string WebServer::binary_sensor_all_json_generator(WebServer *web_server, v
((binary_sensor::BinarySensor *) (source))->state, DETAIL_ALL); ((binary_sensor::BinarySensor *) (source))->state, DETAIL_ALL);
} }
std::string WebServer::binary_sensor_json(binary_sensor::BinarySensor *obj, bool value, JsonDetail start_config) { std::string WebServer::binary_sensor_json(binary_sensor::BinarySensor *obj, bool value, JsonDetail start_config) {
return json::build_json([this, obj, value, start_config](JsonObject root) { json::JsonBuilder builder;
set_json_icon_state_value(root, obj, "binary_sensor-" + obj->get_object_id(), value ? "ON" : "OFF", value, JsonObject root = builder.root();
start_config);
if (start_config == DETAIL_ALL) { set_json_icon_state_value(root, obj, "binary_sensor-" + obj->get_object_id(), value ? "ON" : "OFF", value,
this->add_sorting_info_(root, obj); start_config);
} if (start_config == DETAIL_ALL) {
}); this->add_sorting_info_(root, obj);
}
return builder.serialize();
} }
#endif #endif
@@ -694,20 +714,23 @@ std::string WebServer::fan_all_json_generator(WebServer *web_server, void *sourc
return web_server->fan_json((fan::Fan *) (source), DETAIL_ALL); return web_server->fan_json((fan::Fan *) (source), DETAIL_ALL);
} }
std::string WebServer::fan_json(fan::Fan *obj, JsonDetail start_config) { std::string WebServer::fan_json(fan::Fan *obj, JsonDetail start_config) {
return json::build_json([this, obj, start_config](JsonObject root) { json::JsonBuilder builder;
set_json_icon_state_value(root, obj, "fan-" + obj->get_object_id(), obj->state ? "ON" : "OFF", obj->state, JsonObject root = builder.root();
start_config);
const auto traits = obj->get_traits(); set_json_icon_state_value(root, obj, "fan-" + obj->get_object_id(), obj->state ? "ON" : "OFF", obj->state,
if (traits.supports_speed()) { start_config);
root["speed_level"] = obj->speed; const auto traits = obj->get_traits();
root["speed_count"] = traits.supported_speed_count(); if (traits.supports_speed()) {
} root["speed_level"] = obj->speed;
if (obj->get_traits().supports_oscillation()) root["speed_count"] = traits.supported_speed_count();
root["oscillation"] = obj->oscillating; }
if (start_config == DETAIL_ALL) { if (obj->get_traits().supports_oscillation())
this->add_sorting_info_(root, obj); root["oscillation"] = obj->oscillating;
} if (start_config == DETAIL_ALL) {
}); this->add_sorting_info_(root, obj);
}
return builder.serialize();
} }
#endif #endif
@@ -767,20 +790,23 @@ std::string WebServer::light_all_json_generator(WebServer *web_server, void *sou
return web_server->light_json((light::LightState *) (source), DETAIL_ALL); return web_server->light_json((light::LightState *) (source), DETAIL_ALL);
} }
std::string WebServer::light_json(light::LightState *obj, JsonDetail start_config) { std::string WebServer::light_json(light::LightState *obj, JsonDetail start_config) {
return json::build_json([this, obj, start_config](JsonObject root) { json::JsonBuilder builder;
set_json_id(root, obj, "light-" + obj->get_object_id(), start_config); JsonObject root = builder.root();
root["state"] = obj->remote_values.is_on() ? "ON" : "OFF";
light::LightJSONSchema::dump_json(*obj, root); set_json_id(root, obj, "light-" + obj->get_object_id(), start_config);
if (start_config == DETAIL_ALL) { root["state"] = obj->remote_values.is_on() ? "ON" : "OFF";
JsonArray opt = root["effects"].to<JsonArray>();
opt.add("None"); light::LightJSONSchema::dump_json(*obj, root);
for (auto const &option : obj->get_effects()) { if (start_config == DETAIL_ALL) {
opt.add(option->get_name()); JsonArray opt = root["effects"].to<JsonArray>();
} opt.add("None");
this->add_sorting_info_(root, obj); for (auto const &option : obj->get_effects()) {
opt.add(option->get_name());
} }
}); this->add_sorting_info_(root, obj);
}
return builder.serialize();
} }
#endif #endif
@@ -839,19 +865,22 @@ std::string WebServer::cover_all_json_generator(WebServer *web_server, void *sou
return web_server->cover_json((cover::Cover *) (source), DETAIL_ALL); return web_server->cover_json((cover::Cover *) (source), DETAIL_ALL);
} }
std::string WebServer::cover_json(cover::Cover *obj, JsonDetail start_config) { std::string WebServer::cover_json(cover::Cover *obj, JsonDetail start_config) {
return json::build_json([this, obj, start_config](JsonObject root) { json::JsonBuilder builder;
set_json_icon_state_value(root, obj, "cover-" + obj->get_object_id(), obj->is_fully_closed() ? "CLOSED" : "OPEN", JsonObject root = builder.root();
obj->position, start_config);
root["current_operation"] = cover::cover_operation_to_str(obj->current_operation);
if (obj->get_traits().get_supports_position()) set_json_icon_state_value(root, obj, "cover-" + obj->get_object_id(), obj->is_fully_closed() ? "CLOSED" : "OPEN",
root["position"] = obj->position; obj->position, start_config);
if (obj->get_traits().get_supports_tilt()) root["current_operation"] = cover::cover_operation_to_str(obj->current_operation);
root["tilt"] = obj->tilt;
if (start_config == DETAIL_ALL) { if (obj->get_traits().get_supports_position())
this->add_sorting_info_(root, obj); root["position"] = obj->position;
} if (obj->get_traits().get_supports_tilt())
}); root["tilt"] = obj->tilt;
if (start_config == DETAIL_ALL) {
this->add_sorting_info_(root, obj);
}
return builder.serialize();
} }
#endif #endif
@@ -894,31 +923,33 @@ std::string WebServer::number_all_json_generator(WebServer *web_server, void *so
return web_server->number_json((number::Number *) (source), ((number::Number *) (source))->state, DETAIL_ALL); 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) { std::string WebServer::number_json(number::Number *obj, float value, JsonDetail start_config) {
return json::build_json([this, obj, value, start_config](JsonObject root) { json::JsonBuilder builder;
set_json_id(root, obj, "number-" + obj->get_object_id(), start_config); JsonObject root = builder.root();
if (start_config == DETAIL_ALL) {
root["min_value"] = set_json_id(root, obj, "number-" + obj->get_object_id(), start_config);
value_accuracy_to_string(obj->traits.get_min_value(), step_to_accuracy_decimals(obj->traits.get_step())); if (start_config == DETAIL_ALL) {
root["max_value"] = root["min_value"] =
value_accuracy_to_string(obj->traits.get_max_value(), step_to_accuracy_decimals(obj->traits.get_step())); value_accuracy_to_string(obj->traits.get_min_value(), step_to_accuracy_decimals(obj->traits.get_step()));
root["step"] = root["max_value"] =
value_accuracy_to_string(obj->traits.get_step(), step_to_accuracy_decimals(obj->traits.get_step())); value_accuracy_to_string(obj->traits.get_max_value(), step_to_accuracy_decimals(obj->traits.get_step()));
root["mode"] = (int) obj->traits.get_mode(); root["step"] = value_accuracy_to_string(obj->traits.get_step(), step_to_accuracy_decimals(obj->traits.get_step()));
if (!obj->traits.get_unit_of_measurement().empty()) root["mode"] = (int) obj->traits.get_mode();
root["uom"] = obj->traits.get_unit_of_measurement(); if (!obj->traits.get_unit_of_measurement().empty())
this->add_sorting_info_(root, obj); root["uom"] = obj->traits.get_unit_of_measurement();
} this->add_sorting_info_(root, obj);
if (std::isnan(value)) { }
root["value"] = "\"NaN\""; if (std::isnan(value)) {
root["state"] = "NA"; root["value"] = "\"NaN\"";
} else { root["state"] = "NA";
root["value"] = value_accuracy_to_string(value, step_to_accuracy_decimals(obj->traits.get_step())); } else {
std::string state = value_accuracy_to_string(value, step_to_accuracy_decimals(obj->traits.get_step())); root["value"] = value_accuracy_to_string(value, step_to_accuracy_decimals(obj->traits.get_step()));
if (!obj->traits.get_unit_of_measurement().empty()) std::string state = value_accuracy_to_string(value, step_to_accuracy_decimals(obj->traits.get_step()));
state += " " + obj->traits.get_unit_of_measurement(); if (!obj->traits.get_unit_of_measurement().empty())
root["state"] = state; state += " " + obj->traits.get_unit_of_measurement();
} root["state"] = state;
}); }
return builder.serialize();
} }
#endif #endif
@@ -966,15 +997,18 @@ std::string WebServer::date_all_json_generator(WebServer *web_server, void *sour
return web_server->date_json((datetime::DateEntity *) (source), DETAIL_ALL); return web_server->date_json((datetime::DateEntity *) (source), DETAIL_ALL);
} }
std::string WebServer::date_json(datetime::DateEntity *obj, JsonDetail start_config) { std::string WebServer::date_json(datetime::DateEntity *obj, JsonDetail start_config) {
return json::build_json([this, obj, start_config](JsonObject root) { json::JsonBuilder builder;
set_json_id(root, obj, "date-" + obj->get_object_id(), start_config); JsonObject root = builder.root();
std::string value = str_sprintf("%d-%02d-%02d", obj->year, obj->month, obj->day);
root["value"] = value; set_json_id(root, obj, "date-" + obj->get_object_id(), start_config);
root["state"] = value; std::string value = str_sprintf("%d-%02d-%02d", obj->year, obj->month, obj->day);
if (start_config == DETAIL_ALL) { root["value"] = value;
this->add_sorting_info_(root, obj); root["state"] = value;
} if (start_config == DETAIL_ALL) {
}); this->add_sorting_info_(root, obj);
}
return builder.serialize();
} }
#endif // USE_DATETIME_DATE #endif // USE_DATETIME_DATE
@@ -1021,15 +1055,18 @@ std::string WebServer::time_all_json_generator(WebServer *web_server, void *sour
return web_server->time_json((datetime::TimeEntity *) (source), DETAIL_ALL); return web_server->time_json((datetime::TimeEntity *) (source), DETAIL_ALL);
} }
std::string WebServer::time_json(datetime::TimeEntity *obj, JsonDetail start_config) { std::string WebServer::time_json(datetime::TimeEntity *obj, JsonDetail start_config) {
return json::build_json([this, obj, start_config](JsonObject root) { json::JsonBuilder builder;
set_json_id(root, obj, "time-" + obj->get_object_id(), start_config); JsonObject root = builder.root();
std::string value = str_sprintf("%02d:%02d:%02d", obj->hour, obj->minute, obj->second);
root["value"] = value; set_json_id(root, obj, "time-" + obj->get_object_id(), start_config);
root["state"] = value; std::string value = str_sprintf("%02d:%02d:%02d", obj->hour, obj->minute, obj->second);
if (start_config == DETAIL_ALL) { root["value"] = value;
this->add_sorting_info_(root, obj); root["state"] = value;
} if (start_config == DETAIL_ALL) {
}); this->add_sorting_info_(root, obj);
}
return builder.serialize();
} }
#endif // USE_DATETIME_TIME #endif // USE_DATETIME_TIME
@@ -1076,16 +1113,19 @@ std::string WebServer::datetime_all_json_generator(WebServer *web_server, void *
return web_server->datetime_json((datetime::DateTimeEntity *) (source), DETAIL_ALL); return web_server->datetime_json((datetime::DateTimeEntity *) (source), DETAIL_ALL);
} }
std::string WebServer::datetime_json(datetime::DateTimeEntity *obj, JsonDetail start_config) { std::string WebServer::datetime_json(datetime::DateTimeEntity *obj, JsonDetail start_config) {
return json::build_json([this, obj, start_config](JsonObject root) { json::JsonBuilder builder;
set_json_id(root, obj, "datetime-" + obj->get_object_id(), start_config); JsonObject root = builder.root();
std::string value = str_sprintf("%d-%02d-%02d %02d:%02d:%02d", obj->year, obj->month, obj->day, obj->hour,
obj->minute, obj->second); set_json_id(root, obj, "datetime-" + obj->get_object_id(), start_config);
root["value"] = value; std::string value =
root["state"] = value; str_sprintf("%d-%02d-%02d %02d:%02d:%02d", obj->year, obj->month, obj->day, obj->hour, obj->minute, obj->second);
if (start_config == DETAIL_ALL) { root["value"] = value;
this->add_sorting_info_(root, obj); root["state"] = value;
} if (start_config == DETAIL_ALL) {
}); this->add_sorting_info_(root, obj);
}
return builder.serialize();
} }
#endif // USE_DATETIME_DATETIME #endif // USE_DATETIME_DATETIME
@@ -1128,22 +1168,25 @@ std::string WebServer::text_all_json_generator(WebServer *web_server, void *sour
return web_server->text_json((text::Text *) (source), ((text::Text *) (source))->state, DETAIL_ALL); 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) { std::string WebServer::text_json(text::Text *obj, const std::string &value, JsonDetail start_config) {
return json::build_json([this, obj, value, start_config](JsonObject root) { json::JsonBuilder builder;
set_json_id(root, obj, "text-" + obj->get_object_id(), start_config); JsonObject root = builder.root();
root["min_length"] = obj->traits.get_min_length();
root["max_length"] = obj->traits.get_max_length(); set_json_id(root, obj, "text-" + obj->get_object_id(), start_config);
root["pattern"] = obj->traits.get_pattern(); root["min_length"] = obj->traits.get_min_length();
if (obj->traits.get_mode() == text::TextMode::TEXT_MODE_PASSWORD) { root["max_length"] = obj->traits.get_max_length();
root["state"] = "********"; root["pattern"] = obj->traits.get_pattern();
} else { if (obj->traits.get_mode() == text::TextMode::TEXT_MODE_PASSWORD) {
root["state"] = value; root["state"] = "********";
} } else {
root["value"] = value; root["state"] = value;
if (start_config == DETAIL_ALL) { }
root["mode"] = (int) obj->traits.get_mode(); root["value"] = value;
this->add_sorting_info_(root, obj); if (start_config == DETAIL_ALL) {
} root["mode"] = (int) obj->traits.get_mode();
}); this->add_sorting_info_(root, obj);
}
return builder.serialize();
} }
#endif #endif
@@ -1186,16 +1229,19 @@ std::string WebServer::select_all_json_generator(WebServer *web_server, void *so
return web_server->select_json((select::Select *) (source), ((select::Select *) (source))->state, DETAIL_ALL); return web_server->select_json((select::Select *) (source), ((select::Select *) (source))->state, DETAIL_ALL);
} }
std::string WebServer::select_json(select::Select *obj, const std::string &value, JsonDetail start_config) { std::string WebServer::select_json(select::Select *obj, const std::string &value, JsonDetail start_config) {
return json::build_json([this, obj, value, start_config](JsonObject root) { json::JsonBuilder builder;
set_json_icon_state_value(root, obj, "select-" + obj->get_object_id(), value, value, start_config); JsonObject root = builder.root();
if (start_config == DETAIL_ALL) {
JsonArray opt = root["option"].to<JsonArray>(); set_json_icon_state_value(root, obj, "select-" + obj->get_object_id(), value, value, start_config);
for (auto &option : obj->traits.get_options()) { if (start_config == DETAIL_ALL) {
opt.add(option); JsonArray opt = root["option"].to<JsonArray>();
} for (auto &option : obj->traits.get_options()) {
this->add_sorting_info_(root, obj); opt.add(option);
} }
}); this->add_sorting_info_(root, obj);
}
return builder.serialize();
} }
#endif #endif
@@ -1251,91 +1297,93 @@ std::string WebServer::climate_all_json_generator(WebServer *web_server, void *s
} }
std::string WebServer::climate_json(climate::Climate *obj, JsonDetail start_config) { std::string WebServer::climate_json(climate::Climate *obj, JsonDetail start_config) {
// NOLINTBEGIN(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson // NOLINTBEGIN(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
return json::build_json([this, obj, start_config](JsonObject root) { json::JsonBuilder builder;
set_json_id(root, obj, "climate-" + obj->get_object_id(), start_config); JsonObject root = builder.root();
const auto traits = obj->get_traits(); set_json_id(root, obj, "climate-" + obj->get_object_id(), start_config);
int8_t target_accuracy = traits.get_target_temperature_accuracy_decimals(); const auto traits = obj->get_traits();
int8_t current_accuracy = traits.get_current_temperature_accuracy_decimals(); int8_t target_accuracy = traits.get_target_temperature_accuracy_decimals();
char buf[16]; int8_t current_accuracy = traits.get_current_temperature_accuracy_decimals();
char buf[16];
if (start_config == DETAIL_ALL) { if (start_config == DETAIL_ALL) {
JsonArray opt = root["modes"].to<JsonArray>(); JsonArray opt = root["modes"].to<JsonArray>();
for (climate::ClimateMode m : traits.get_supported_modes()) for (climate::ClimateMode m : traits.get_supported_modes())
opt.add(PSTR_LOCAL(climate::climate_mode_to_string(m))); opt.add(PSTR_LOCAL(climate::climate_mode_to_string(m)));
if (!traits.get_supported_custom_fan_modes().empty()) { if (!traits.get_supported_custom_fan_modes().empty()) {
JsonArray opt = root["fan_modes"].to<JsonArray>(); JsonArray opt = root["fan_modes"].to<JsonArray>();
for (climate::ClimateFanMode m : traits.get_supported_fan_modes()) for (climate::ClimateFanMode m : traits.get_supported_fan_modes())
opt.add(PSTR_LOCAL(climate::climate_fan_mode_to_string(m))); opt.add(PSTR_LOCAL(climate::climate_fan_mode_to_string(m)));
}
if (!traits.get_supported_custom_fan_modes().empty()) {
JsonArray opt = root["custom_fan_modes"].to<JsonArray>();
for (auto const &custom_fan_mode : traits.get_supported_custom_fan_modes())
opt.add(custom_fan_mode);
}
if (traits.get_supports_swing_modes()) {
JsonArray opt = root["swing_modes"].to<JsonArray>();
for (auto swing_mode : traits.get_supported_swing_modes())
opt.add(PSTR_LOCAL(climate::climate_swing_mode_to_string(swing_mode)));
}
if (traits.get_supports_presets() && obj->preset.has_value()) {
JsonArray opt = root["presets"].to<JsonArray>();
for (climate::ClimatePreset m : traits.get_supported_presets())
opt.add(PSTR_LOCAL(climate::climate_preset_to_string(m)));
}
if (!traits.get_supported_custom_presets().empty() && obj->custom_preset.has_value()) {
JsonArray opt = root["custom_presets"].to<JsonArray>();
for (auto const &custom_preset : traits.get_supported_custom_presets())
opt.add(custom_preset);
}
this->add_sorting_info_(root, obj);
} }
bool has_state = false; if (!traits.get_supported_custom_fan_modes().empty()) {
root["mode"] = PSTR_LOCAL(climate_mode_to_string(obj->mode)); JsonArray opt = root["custom_fan_modes"].to<JsonArray>();
root["max_temp"] = value_accuracy_to_string(traits.get_visual_max_temperature(), target_accuracy); for (auto const &custom_fan_mode : traits.get_supported_custom_fan_modes())
root["min_temp"] = value_accuracy_to_string(traits.get_visual_min_temperature(), target_accuracy); opt.add(custom_fan_mode);
root["step"] = traits.get_visual_target_temperature_step();
if (traits.get_supports_action()) {
root["action"] = PSTR_LOCAL(climate_action_to_string(obj->action));
root["state"] = root["action"];
has_state = true;
}
if (traits.get_supports_fan_modes() && obj->fan_mode.has_value()) {
root["fan_mode"] = PSTR_LOCAL(climate_fan_mode_to_string(obj->fan_mode.value()));
}
if (!traits.get_supported_custom_fan_modes().empty() && obj->custom_fan_mode.has_value()) {
root["custom_fan_mode"] = obj->custom_fan_mode.value().c_str();
}
if (traits.get_supports_presets() && obj->preset.has_value()) {
root["preset"] = PSTR_LOCAL(climate_preset_to_string(obj->preset.value()));
}
if (!traits.get_supported_custom_presets().empty() && obj->custom_preset.has_value()) {
root["custom_preset"] = obj->custom_preset.value().c_str();
} }
if (traits.get_supports_swing_modes()) { if (traits.get_supports_swing_modes()) {
root["swing_mode"] = PSTR_LOCAL(climate_swing_mode_to_string(obj->swing_mode)); JsonArray opt = root["swing_modes"].to<JsonArray>();
for (auto swing_mode : traits.get_supported_swing_modes())
opt.add(PSTR_LOCAL(climate::climate_swing_mode_to_string(swing_mode)));
} }
if (traits.get_supports_current_temperature()) { if (traits.get_supports_presets() && obj->preset.has_value()) {
if (!std::isnan(obj->current_temperature)) { JsonArray opt = root["presets"].to<JsonArray>();
root["current_temperature"] = value_accuracy_to_string(obj->current_temperature, current_accuracy); for (climate::ClimatePreset m : traits.get_supported_presets())
} else { opt.add(PSTR_LOCAL(climate::climate_preset_to_string(m)));
root["current_temperature"] = "NA";
}
} }
if (traits.get_supports_two_point_target_temperature()) { if (!traits.get_supported_custom_presets().empty() && obj->custom_preset.has_value()) {
root["target_temperature_low"] = value_accuracy_to_string(obj->target_temperature_low, target_accuracy); JsonArray opt = root["custom_presets"].to<JsonArray>();
root["target_temperature_high"] = value_accuracy_to_string(obj->target_temperature_high, target_accuracy); for (auto const &custom_preset : traits.get_supported_custom_presets())
if (!has_state) { opt.add(custom_preset);
root["state"] = value_accuracy_to_string((obj->target_temperature_high + obj->target_temperature_low) / 2.0f, }
target_accuracy); this->add_sorting_info_(root, obj);
} }
bool has_state = false;
root["mode"] = PSTR_LOCAL(climate_mode_to_string(obj->mode));
root["max_temp"] = value_accuracy_to_string(traits.get_visual_max_temperature(), target_accuracy);
root["min_temp"] = value_accuracy_to_string(traits.get_visual_min_temperature(), target_accuracy);
root["step"] = traits.get_visual_target_temperature_step();
if (traits.get_supports_action()) {
root["action"] = PSTR_LOCAL(climate_action_to_string(obj->action));
root["state"] = root["action"];
has_state = true;
}
if (traits.get_supports_fan_modes() && obj->fan_mode.has_value()) {
root["fan_mode"] = PSTR_LOCAL(climate_fan_mode_to_string(obj->fan_mode.value()));
}
if (!traits.get_supported_custom_fan_modes().empty() && obj->custom_fan_mode.has_value()) {
root["custom_fan_mode"] = obj->custom_fan_mode.value().c_str();
}
if (traits.get_supports_presets() && obj->preset.has_value()) {
root["preset"] = PSTR_LOCAL(climate_preset_to_string(obj->preset.value()));
}
if (!traits.get_supported_custom_presets().empty() && obj->custom_preset.has_value()) {
root["custom_preset"] = obj->custom_preset.value().c_str();
}
if (traits.get_supports_swing_modes()) {
root["swing_mode"] = PSTR_LOCAL(climate_swing_mode_to_string(obj->swing_mode));
}
if (traits.get_supports_current_temperature()) {
if (!std::isnan(obj->current_temperature)) {
root["current_temperature"] = value_accuracy_to_string(obj->current_temperature, current_accuracy);
} else { } else {
root["target_temperature"] = value_accuracy_to_string(obj->target_temperature, target_accuracy); root["current_temperature"] = "NA";
if (!has_state)
root["state"] = root["target_temperature"];
} }
}); }
if (traits.get_supports_two_point_target_temperature()) {
root["target_temperature_low"] = value_accuracy_to_string(obj->target_temperature_low, target_accuracy);
root["target_temperature_high"] = value_accuracy_to_string(obj->target_temperature_high, target_accuracy);
if (!has_state) {
root["state"] = value_accuracy_to_string((obj->target_temperature_high + obj->target_temperature_low) / 2.0f,
target_accuracy);
}
} else {
root["target_temperature"] = value_accuracy_to_string(obj->target_temperature, target_accuracy);
if (!has_state)
root["state"] = root["target_temperature"];
}
return builder.serialize();
// NOLINTEND(clang-analyzer-cplusplus.NewDeleteLeaks) // NOLINTEND(clang-analyzer-cplusplus.NewDeleteLeaks)
} }
#endif #endif
@@ -1401,13 +1449,16 @@ std::string WebServer::lock_all_json_generator(WebServer *web_server, void *sour
return web_server->lock_json((lock::Lock *) (source), ((lock::Lock *) (source))->state, DETAIL_ALL); 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) { std::string WebServer::lock_json(lock::Lock *obj, lock::LockState value, JsonDetail start_config) {
return json::build_json([this, obj, value, start_config](JsonObject root) { json::JsonBuilder builder;
set_json_icon_state_value(root, obj, "lock-" + obj->get_object_id(), lock::lock_state_to_string(value), value, JsonObject root = builder.root();
start_config);
if (start_config == DETAIL_ALL) { set_json_icon_state_value(root, obj, "lock-" + obj->get_object_id(), lock::lock_state_to_string(value), value,
this->add_sorting_info_(root, obj); start_config);
} if (start_config == DETAIL_ALL) {
}); this->add_sorting_info_(root, obj);
}
return builder.serialize();
} }
#endif #endif
@@ -1464,17 +1515,20 @@ std::string WebServer::valve_all_json_generator(WebServer *web_server, void *sou
return web_server->valve_json((valve::Valve *) (source), DETAIL_ALL); return web_server->valve_json((valve::Valve *) (source), DETAIL_ALL);
} }
std::string WebServer::valve_json(valve::Valve *obj, JsonDetail start_config) { std::string WebServer::valve_json(valve::Valve *obj, JsonDetail start_config) {
return json::build_json([this, obj, start_config](JsonObject root) { json::JsonBuilder builder;
set_json_icon_state_value(root, obj, "valve-" + obj->get_object_id(), obj->is_fully_closed() ? "CLOSED" : "OPEN", JsonObject root = builder.root();
obj->position, start_config);
root["current_operation"] = valve::valve_operation_to_str(obj->current_operation);
if (obj->get_traits().get_supports_position()) set_json_icon_state_value(root, obj, "valve-" + obj->get_object_id(), obj->is_fully_closed() ? "CLOSED" : "OPEN",
root["position"] = obj->position; obj->position, start_config);
if (start_config == DETAIL_ALL) { root["current_operation"] = valve::valve_operation_to_str(obj->current_operation);
this->add_sorting_info_(root, obj);
} if (obj->get_traits().get_supports_position())
}); root["position"] = obj->position;
if (start_config == DETAIL_ALL) {
this->add_sorting_info_(root, obj);
}
return builder.serialize();
} }
#endif #endif
@@ -1533,14 +1587,17 @@ std::string WebServer::alarm_control_panel_all_json_generator(WebServer *web_ser
std::string WebServer::alarm_control_panel_json(alarm_control_panel::AlarmControlPanel *obj, std::string WebServer::alarm_control_panel_json(alarm_control_panel::AlarmControlPanel *obj,
alarm_control_panel::AlarmControlPanelState value, alarm_control_panel::AlarmControlPanelState value,
JsonDetail start_config) { JsonDetail start_config) {
return json::build_json([this, obj, value, start_config](JsonObject root) { json::JsonBuilder builder;
char buf[16]; JsonObject root = builder.root();
set_json_icon_state_value(root, obj, "alarm-control-panel-" + obj->get_object_id(),
PSTR_LOCAL(alarm_control_panel_state_to_string(value)), value, start_config); char buf[16];
if (start_config == DETAIL_ALL) { set_json_icon_state_value(root, obj, "alarm-control-panel-" + obj->get_object_id(),
this->add_sorting_info_(root, obj); PSTR_LOCAL(alarm_control_panel_state_to_string(value)), value, start_config);
} if (start_config == DETAIL_ALL) {
}); this->add_sorting_info_(root, obj);
}
return builder.serialize();
} }
#endif #endif
@@ -1577,20 +1634,23 @@ std::string WebServer::event_all_json_generator(WebServer *web_server, void *sou
return web_server->event_json(event, get_event_type(event), DETAIL_ALL); return web_server->event_json(event, get_event_type(event), DETAIL_ALL);
} }
std::string WebServer::event_json(event::Event *obj, const std::string &event_type, JsonDetail start_config) { std::string WebServer::event_json(event::Event *obj, const std::string &event_type, JsonDetail start_config) {
return json::build_json([this, obj, event_type, start_config](JsonObject root) { json::JsonBuilder builder;
set_json_id(root, obj, "event-" + obj->get_object_id(), start_config); JsonObject root = builder.root();
if (!event_type.empty()) {
root["event_type"] = event_type; set_json_id(root, obj, "event-" + obj->get_object_id(), start_config);
if (!event_type.empty()) {
root["event_type"] = event_type;
}
if (start_config == DETAIL_ALL) {
JsonArray event_types = root["event_types"].to<JsonArray>();
for (auto const &event_type : obj->get_event_types()) {
event_types.add(event_type);
} }
if (start_config == DETAIL_ALL) { root["device_class"] = obj->get_device_class();
JsonArray event_types = root["event_types"].to<JsonArray>(); this->add_sorting_info_(root, obj);
for (auto const &event_type : obj->get_event_types()) { }
event_types.add(event_type);
} return builder.serialize();
root["device_class"] = obj->get_device_class();
this->add_sorting_info_(root, obj);
}
});
} }
#endif #endif
@@ -1644,18 +1704,21 @@ std::string WebServer::update_all_json_generator(WebServer *web_server, void *so
} }
std::string WebServer::update_json(update::UpdateEntity *obj, JsonDetail start_config) { std::string WebServer::update_json(update::UpdateEntity *obj, JsonDetail start_config) {
// NOLINTBEGIN(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson // NOLINTBEGIN(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
return json::build_json([this, obj, start_config](JsonObject root) { json::JsonBuilder builder;
set_json_id(root, obj, "update-" + obj->get_object_id(), start_config); JsonObject root = builder.root();
root["value"] = obj->update_info.latest_version;
root["state"] = update_state_to_string(obj->state); set_json_id(root, obj, "update-" + obj->get_object_id(), start_config);
if (start_config == DETAIL_ALL) { root["value"] = obj->update_info.latest_version;
root["current_version"] = obj->update_info.current_version; root["state"] = update_state_to_string(obj->state);
root["title"] = obj->update_info.title; if (start_config == DETAIL_ALL) {
root["summary"] = obj->update_info.summary; root["current_version"] = obj->update_info.current_version;
root["release_url"] = obj->update_info.release_url; root["title"] = obj->update_info.title;
this->add_sorting_info_(root, obj); root["summary"] = obj->update_info.summary;
} root["release_url"] = obj->update_info.release_url;
}); this->add_sorting_info_(root, obj);
}
return builder.serialize();
// NOLINTEND(clang-analyzer-cplusplus.NewDeleteLeaks) // NOLINTEND(clang-analyzer-cplusplus.NewDeleteLeaks)
} }
#endif #endif

View File

@@ -392,10 +392,11 @@ AsyncEventSourceResponse::AsyncEventSourceResponse(const AsyncWebServerRequest *
#ifdef USE_WEBSERVER_SORTING #ifdef USE_WEBSERVER_SORTING
for (auto &group : ws->sorting_groups_) { for (auto &group : ws->sorting_groups_) {
// NOLINTBEGIN(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson // NOLINTBEGIN(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
message = json::build_json([group](JsonObject root) { json::JsonBuilder builder;
root["name"] = group.second.name; JsonObject root = builder.root();
root["sorting_weight"] = group.second.weight; root["name"] = group.second.name;
}); root["sorting_weight"] = group.second.weight;
message = builder.serialize();
// NOLINTEND(clang-analyzer-cplusplus.NewDeleteLeaks) // NOLINTEND(clang-analyzer-cplusplus.NewDeleteLeaks)
// a (very) large number of these should be able to be queued initially without defer // a (very) large number of these should be able to be queued initially without defer