|
|
|
#include <esp_log.h>
|
|
|
|
#include <esp_err.h>
|
|
|
|
|
|
|
|
#include <fileserver/token_subs.h>
|
|
|
|
#include <application.h>
|
|
|
|
#include <httpd_utils/captive.h>
|
|
|
|
|
|
|
|
#include "websrv.h"
|
|
|
|
#include "esp_http_server.h"
|
|
|
|
#include "utils.h"
|
|
|
|
|
|
|
|
#include "www_files_enum.h"
|
|
|
|
#include "data_report.h"
|
|
|
|
|
|
|
|
static const char *TAG = "websrv";
|
|
|
|
|
|
|
|
static httpd_handle_t s_hServer = NULL;
|
|
|
|
|
|
|
|
// Embedded files (must also be listed in CMakeLists.txt as COMPONENT_EMBED_TXTFILES)
|
|
|
|
efile(index_file, "index_html");
|
|
|
|
|
|
|
|
static struct tpl_kv_list build_index_replacements_kv(void) {
|
|
|
|
//char name[TPL_KV_KEY_LEN];
|
|
|
|
struct tpl_kv_list kv = tpl_kv_init();
|
|
|
|
tpl_kv_add(&kv, "version", APP_VERSION);
|
|
|
|
return kv;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Main page */
|
|
|
|
static esp_err_t handler_index(httpd_req_t *req) {
|
|
|
|
struct tpl_kv_list kv = build_index_replacements_kv();
|
|
|
|
|
|
|
|
esp_err_t suc = httpd_send_template_file(req, FILE_INDEX_HTML, tpl_kv_replacer, &kv, 0);
|
|
|
|
tpl_kv_free(&kv);
|
|
|
|
return suc;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Update XHR for new index page data */
|
|
|
|
static esp_err_t handler_update(httpd_req_t *req) {
|
|
|
|
struct tpl_kv_list kv = build_index_replacements_kv();
|
|
|
|
|
|
|
|
esp_err_t suc = tpl_kv_send_as_ascii_map(req, &kv);
|
|
|
|
tpl_kv_free(&kv);
|
|
|
|
return suc;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Set a param */
|
|
|
|
static esp_err_t handler_set(httpd_req_t *req) {
|
|
|
|
char buf[64];
|
|
|
|
int n = httpd_req_recv(req, buf, 63);
|
|
|
|
if (n < 0) {
|
|
|
|
ESP_LOGW(TAG, "rx er");
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
buf[n] = 0;
|
|
|
|
|
|
|
|
char keybuf[20];
|
|
|
|
char valbuf[20];
|
|
|
|
if (ESP_OK != httpd_query_key_value(buf, "key", keybuf, 20)) goto err;
|
|
|
|
if (ESP_OK != httpd_query_key_value(buf, "value", valbuf, 20)) goto err;
|
|
|
|
|
|
|
|
// TODO
|
|
|
|
ESP_LOGW(TAG, "bad key %s", keybuf);
|
|
|
|
goto err;
|
|
|
|
|
|
|
|
return httpd_resp_send(req, NULL, 0);
|
|
|
|
|
|
|
|
err:
|
|
|
|
return httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Request emulator reboot */
|
|
|
|
static esp_err_t handler_reboot(httpd_req_t *req) {
|
|
|
|
httpd_resp_send(req, "<!DOCTYPE html><html><head>"
|
|
|
|
"<meta http-equiv=\"refresh\" content=\"10; url=/\">"
|
|
|
|
"</head>"
|
|
|
|
"<body>"
|
|
|
|
"Reboot requested. Reloading in 10 seconds.<br>"
|
|
|
|
"<a href=\"/\">Try now.</a>", -1);
|
|
|
|
|
|
|
|
ESP_LOGI(TAG, "Restarting ESP...");
|
|
|
|
esp_restart();
|
|
|
|
}
|
|
|
|
|
|
|
|
static esp_err_t handler_sample(httpd_req_t *req) {
|
|
|
|
char *msg = malloc(256);
|
|
|
|
if (!msg) return ESP_ERR_NO_MEM;
|
|
|
|
|
|
|
|
// TODO use cJSON
|
|
|
|
|
|
|
|
char *wp = msg;
|
|
|
|
wp += sprintf(wp, "{");
|
|
|
|
|
|
|
|
bool need_comma = false;
|
|
|
|
if (g_data_report.iaq_ready) {
|
|
|
|
if (timestamp_age(g_data_report.iaq_timestamp) < DATA_MAX_AGE) {
|
|
|
|
wp += sprintf(wp, "\"iaq\":%.2f,\"iaq_s\":%.2f,\"iaq_co2\":%.2f,\"iaq_voc\":%.2f",
|
|
|
|
g_data_report.iaq,
|
|
|
|
g_data_report.iaq_static,
|
|
|
|
g_data_report.iaq_co2_ppm_equiv,
|
|
|
|
g_data_report.iaq_voc_ppm_equiv);
|
|
|
|
need_comma = true;
|
|
|
|
} else {
|
|
|
|
g_data_report.iaq_ready = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (g_data_report.thpg_ready) {
|
|
|
|
if (timestamp_age(g_data_report.thpg_timestamp) < DATA_MAX_AGE) {
|
|
|
|
wp += sprintf(wp, "%s\"temp\":%.2f,\"temp_r\":%.2f,\"hum\":%.2f,\"hum_r\":%.2f,\"pres\":%.1f,\"gas_r\":%.1f",
|
|
|
|
need_comma ? "," : "",
|
|
|
|
g_data_report.temperature,
|
|
|
|
g_data_report.temperature_raw,
|
|
|
|
g_data_report.humidity,
|
|
|
|
g_data_report.humidity_raw,
|
|
|
|
g_data_report.pressure,
|
|
|
|
g_data_report.gas_raw);
|
|
|
|
need_comma = true;
|
|
|
|
} else {
|
|
|
|
g_data_report.thpg_ready = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (g_data_report.co2_ready) {
|
|
|
|
if (timestamp_age(g_data_report.co2_timestamp) < DATA_MAX_AGE) {
|
|
|
|
wp += sprintf(wp, "%s\"co2\":%.2f",
|
|
|
|
need_comma ? "," : "",
|
|
|
|
g_data_report.co2_ppm);
|
|
|
|
} else {
|
|
|
|
g_data_report.co2_ready = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
sprintf(wp, "}");
|
|
|
|
|
|
|
|
esp_err_t rv = httpd_resp_send(req, msg, -1);
|
|
|
|
free(msg);
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* An HTTP GET handler */
|
|
|
|
static esp_err_t handler_staticfiles(httpd_req_t *r) {
|
|
|
|
const struct embedded_file_info *file;
|
|
|
|
const char *fname = r->user_ctx;
|
|
|
|
enum file_access_level access = FILE_ACCESS_PROTECTED;
|
|
|
|
|
|
|
|
// wildcard files must be public
|
|
|
|
if (fname == NULL) {
|
|
|
|
fname = r->uri + 1; // URI always starts with slash, but we dont want a slash in the file name
|
|
|
|
access = FILE_ACCESS_PUBLIC;
|
|
|
|
}
|
|
|
|
|
|
|
|
#if USE_CAPTIVE_PORTAL
|
|
|
|
// First check if this is a phone taken here by the captive portal
|
|
|
|
esp_err_t rv = httpd_captive_redirect(r);
|
|
|
|
if (rv != ESP_ERR_NOT_FOUND) return rv;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (ESP_OK != www_get_static_file(fname, access, &file)) {
|
|
|
|
ESP_LOGW(TAG, "File not found: %s", fname);
|
|
|
|
return httpd_resp_send_404(r);
|
|
|
|
} else {
|
|
|
|
if (streq(file->mime, "text/html")) {
|
|
|
|
// using the template func to allow includes
|
|
|
|
return httpd_send_template_file_struct(r, file, /*replacer*/NULL, /*ctx*/NULL, /*opts*/0);
|
|
|
|
} else {
|
|
|
|
return httpd_send_static_file_struct(r, file, TPL_ESCAPE_NONE, 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static const httpd_uri_t routes[] = {
|
|
|
|
{
|
|
|
|
.uri = "/",
|
|
|
|
.method = HTTP_GET,
|
|
|
|
.handler = handler_index,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.uri = "/data",
|
|
|
|
.method = HTTP_GET,
|
|
|
|
.handler = handler_update,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.uri = "/set",
|
|
|
|
.method = HTTP_POST,
|
|
|
|
.handler = handler_set,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.uri = "/reboot",
|
|
|
|
.method = HTTP_GET,
|
|
|
|
.handler = handler_reboot,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.uri = "/sample",
|
|
|
|
.method = HTTP_GET,
|
|
|
|
.handler = handler_sample,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.uri = "*", // any file except protected (e.g. not HTML, PEM etc)
|
|
|
|
.method = HTTP_GET,
|
|
|
|
.handler = handler_staticfiles,
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
esp_err_t websrv_init(void) {
|
|
|
|
httpd_config_t config = HTTPD_DEFAULT_CONFIG();
|
|
|
|
config.max_open_sockets = 3;
|
|
|
|
|
|
|
|
config.uri_match_fn = httpd_uri_match_wildcard;
|
|
|
|
config.lru_purge_enable = true;
|
|
|
|
|
|
|
|
ESP_LOGI(TAG, "Starting HTTP server on port: '%d'", config.server_port);
|
|
|
|
|
|
|
|
esp_err_t suc = httpd_start(&s_hServer, &config);
|
|
|
|
if (suc == ESP_OK) {
|
|
|
|
ESP_LOGI(TAG, "HTTP server started");
|
|
|
|
|
|
|
|
for (int i = 0; i < sizeof(routes) / sizeof(httpd_uri_t); i++) {
|
|
|
|
httpd_register_uri_handler(s_hServer, &routes[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
return ESP_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
ESP_LOGE(TAG, "Error starting server!");
|
|
|
|
return suc;
|
|
|
|
}
|