SensESP 3.3.0
Universal Signal K sensor toolkit ESP32
Loading...
Searching...
No Matches
config_handler.cpp
Go to the documentation of this file.
1#include "sensesp.h"
2
3#include "config_handler.h"
4
5#include <memory>
6
8
9namespace sensesp {
10
11bool get_item_data(JsonDocument& doc,
12 const std::shared_ptr<ConfigItemBase>& item) {
13 JsonObject obj = doc.to<JsonObject>();
14
15 String str;
16 serializeJson(doc, str);
17
18 obj["path"] = item->get_config_path();
19 obj["title"] = item->get_title();
20 obj["description"] = item->get_description();
21 obj["requires_restart"] = item->requires_restart();
22 obj["schema"] = serialized(item->get_config_schema());
23
24 item->refresh();
25
26 JsonObject config = obj["config"].to<JsonObject>();
27 bool result = item->to_json(config);
28
29 serializeJson(obj, str);
30
31 if (doc.overflowed()) {
32 ESP_LOGE("ConfigHandler", "JSON document overflowed");
33 return false;
34 }
35
36 return result;
37}
38
39esp_err_t handle_config_item_list(httpd_req_t* req) {
40 ESP_LOGI("ConfigHandler", "GET request to URL %s", req->uri);
41 String url = String(req->uri);
42 String query = "";
43 if (url.indexOf('?') != -1) {
44 query = url.substring(url.indexOf('?') + 1);
45 }
46
47 bool cards_only = false;
48
49 // If query is "cards", return only the cards
50 if (query == "cards") {
51 cards_only = true;
52 } else if (query != "") {
53 httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Invalid query");
54 return ESP_FAIL;
55 }
56
57 JsonDocument json_doc;
58 JsonArray arr = json_doc.to<JsonArray>();
59
60 auto config_items = ConfigItemBase::get_config_items();
61
62 for (auto it = config_items->begin(); it != config_items->end(); ++it) {
63 if (cards_only &&
64 ((*it)->get_config_schema() == "null" ||
65 (*it)->get_config_schema() == "{}" || (*it)->get_title() == "")) {
66 continue;
67 }
68 const String& path = (*it)->get_config_path();
69 if (path == "") {
70 continue;
71 }
72 auto obj = arr.add(path);
73 }
74
75 String response;
76 serializeJson(json_doc, response);
77 httpd_resp_set_type(req, "application/json");
78 httpd_resp_sendstr(req, response.c_str());
79 return ESP_OK;
80}
81
82void add_config_list_handler(std::shared_ptr<HTTPServer>& server) {
83 auto handler = std::make_shared<HTTPRequestHandler>(
84 1 << HTTP_GET, "/api/config", handle_config_item_list);
85 server->add_handler(handler);
86}
87
88void add_config_get_handler(std::shared_ptr<HTTPServer>& server) {
89 auto handler = std::make_shared<HTTPRequestHandler>(
90 1 << HTTP_GET, "/api/config/*", [](httpd_req_t* req) {
91 ESP_LOGD("ConfigHandler", "GET request to URL %s", req->uri);
92 String url_tail = String(req->uri).substring(11);
93 String path;
94 String query = "";
95 if (url_tail.indexOf('?') != -1) {
96 path = url_tail.substring(0, url_tail.indexOf('?'));
97 query = url_tail.substring(url_tail.indexOf('?') + 1);
98 } else {
99 path = url_tail;
100 }
101 char path_cstr[path.length() + 1];
102 urldecode2(path_cstr, path.c_str());
103 url_tail = String(path_cstr);
104
105 if (path.length() == 0) {
106 // return a list of all ConfigItemT objects
107 return handle_config_item_list(req);
108 }
109
110 // find the config item object with the matching config_path
111 auto config_item = ConfigItemBase::get_config_item(url_tail);
112 if (config_item == nullptr) {
113 httpd_resp_send_err(req, HTTPD_404_NOT_FOUND,
114 "No ConfigItem found with that path");
115 return ESP_FAIL;
116 }
117
118 JsonDocument doc;
119
120 get_item_data(doc, config_item);
121
122 String response;
123 serializeJson(doc, response);
124 httpd_resp_set_type(req, "application/json");
125 httpd_resp_sendstr(req, response.c_str());
126 return ESP_OK;
127 });
128 server->add_handler(handler);
129}
130
131void add_config_put_handler(std::shared_ptr<HTTPServer>& server) {
132 auto handler = std::make_shared<HTTPRequestHandler>(
133 1 << HTTP_PUT, "/api/config/*",
134 [](httpd_req_t* req) { // check that the content type is JSON
135 ESP_LOGI(__FILENAME__, "PUT request to URL %s", req->uri);
136 if (get_content_type(req) != "application/json") {
137 httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST,
138 "application/json content type expected");
139 return ESP_FAIL;
140 }
141
142 // get the URL tail after /api/config
143 String url_tail = String(req->uri).substring(11);
144 if (url_tail.length() == 0) {
145 httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST,
146 "No configuration path specified");
147 return ESP_FAIL;
148 }
149
150 // urldecode the URL tail
151 char url_tail_cstr[url_tail.length() + 1];
152 urldecode2(url_tail_cstr, url_tail.c_str());
153 url_tail = String(url_tail_cstr);
154
155 // find the ConfigItemT object with the matching config_path
156 auto config_item = ConfigItemBase::get_config_item(url_tail);
157 if (config_item == nullptr) {
158 httpd_resp_send_err(req, HTTPD_404_NOT_FOUND,
159 "No Configurable found with that path");
160 return ESP_FAIL;
161 }
162
163 // receive the payload
164 constexpr size_t kMaxConfigPayloadSize = 4096;
165 size_t payload_len = req->content_len;
166 if (payload_len > kMaxConfigPayloadSize) {
167 httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Payload too large");
168 return ESP_FAIL;
169 }
170 std::unique_ptr<char[]> payload(new char[payload_len + 1]);
171 int ret = httpd_req_recv(req, payload.get(), payload_len);
172 if (ret <= 0) {
173 httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR,
174 "Error receiving payload");
175 return ESP_FAIL;
176 }
177 payload[payload_len] = '\0';
178
179 ESP_LOGV("ConfigHandler", "Received payload: %s", payload.get());
180
181 // parse the content as JSON
182 JsonDocument doc;
183 DeserializationError error = deserializeJson(doc, payload.get());
184 if (error) {
185 ESP_LOGE("ConfigHandler", "Error parsing JSON payload: %s",
186 error.c_str());
187 httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST,
188 "Error parsing JSON payload");
189 return ESP_FAIL;
190 }
191
192 String response;
193 bool result = config_item->from_json(doc.as<JsonObject>());
194 if (!result) {
195 ESP_LOGE("ConfigHandler", "Error applying JSON payload");
196 httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST,
197 "Invalid JSON payload");
198 return ESP_FAIL;
199 }
200 config_item->save();
201 response = "{\"status\":\"ok\"}";
202
203 httpd_resp_set_type(req, "application/json");
204 httpd_resp_sendstr(req, response.c_str());
205 return ESP_OK;
206
207 });
208 server->add_handler(handler);
209}
210
211void add_config_handlers(std::shared_ptr<HTTPServer>& server) {
215}
216
217} // namespace sensesp
static std::shared_ptr< ConfigItemBase > get_config_item(const String key)
Get a single ConfigItemT by key.
static std::unique_ptr< std::vector< std::shared_ptr< ConfigItemBase > > > get_config_items()
Get all config items as a vector.
void add_config_handlers(std::shared_ptr< HTTPServer > &server)
Handle HTTP requests to /config.
void add_config_put_handler(std::shared_ptr< HTTPServer > &server)
void urldecode2(char *dst, const char *src)
void add_config_list_handler(std::shared_ptr< HTTPServer > &server)
bool get_item_data(JsonDocument &doc, const std::shared_ptr< ConfigItemBase > &item)
void add_config_get_handler(std::shared_ptr< HTTPServer > &server)
esp_err_t handle_config_item_list(httpd_req_t *req)
String get_content_type(httpd_req_t *req)