SensESP 3.0.1
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 size_t payload_len = req->content_len;
165 char* payload = new char[payload_len + 1];
166 int ret = httpd_req_recv(req, payload, payload_len);
167 if (ret <= 0) {
168 httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR,
169 "Error receiving payload");
170 return ESP_FAIL;
171 }
172 payload[payload_len] = '\0';
173
174 ESP_LOGV("ConfigHandler", "Received payload: %s", payload);
175
176 // parse the content as JSON
177 JsonDocument doc;
178 DeserializationError error = deserializeJson(doc, payload);
179 delete[] payload;
180 if (error) {
181 ESP_LOGE("ConfigHandler", "Error parsing JSON payload: %s",
182 error.c_str());
183 httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST,
184 "Error parsing JSON payload");
185 return ESP_FAIL;
186 }
187
188 String response;
189 bool result = config_item->from_json(doc.as<JsonObject>());
190 if (!result) {
191 ESP_LOGE("ConfigHandler", "Error applying JSON payload");
192 httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST,
193 "Invalid JSON payload");
194 return ESP_FAIL;
195 }
196 config_item->save();
197 response = "{\"status\":\"ok\"}";
198
199 httpd_resp_set_type(req, "application/json");
200 httpd_resp_sendstr(req, response.c_str());
201 return ESP_OK;
202
203 });
204 server->add_handler(handler);
205}
206
207void add_config_handlers(std::shared_ptr<HTTPServer>& server) {
211}
212
213} // 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)