123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240 |
- /*
- * SPDX-FileCopyrightText: 2022 Helmut Pozimski <helmut@pozimski.eu>
- *
- * SPDX-License-Identifier: GPL-2.0-only
- */
-
- #include <string.h>
- #include <esp_http_server.h>
- #include <esp_event.h>
- #include <esp_log.h>
- #include <esp_err.h>
- #include <cJSON.h>
-
- #include "storage.h"
- #define TAG "api"
- #define BUF_LEN 1000
- static TaskHandle_t* alarm_task_handle = NULL;
- static char* extract_wakeup(const char* uri) {
- return strrchr(uri, '/') +1;
- }
- static uint8_t contains_weekday(char* param) {
- uint8_t result = 0;
- for (int i = 1; i<=NUM_WEEK_DAYS; i++) {
- if (strcmp(WEEK_DAYS[i], param) == 0) {
- result = 1;
- break;
- }
- }
- return result;
- }
- static uint8_t day_is_valid(char* day) {
- return strlen(day) == 8 || contains_weekday(day);
- }
- static esp_err_t wakeup_get_handler(httpd_req_t *req) {
- httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*");
- char* wakeup_str = extract_wakeup(req->uri);
-
- if (!day_is_valid(wakeup_str)) {
- httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Invalid day requested");
- return ESP_OK;
- }
-
- int16_t wakeup_minutes;
- esp_err_t ret = read_wakeup_time_str(wakeup_str, &wakeup_minutes);
-
- if (ret == ESP_OK) {
- httpd_resp_set_type(req, "application/json");
- cJSON *root = cJSON_CreateObject();
- if (wakeup_minutes >= 0) {
- cJSON_AddNumberToObject(root, "hour", wakeup_minutes / 60);
- cJSON_AddNumberToObject(root, "minute", wakeup_minutes % 60);
- }
- const char *response = cJSON_Print(root);
- httpd_resp_sendstr(req, response);
- } else if (ret == ESP_ERR_NVS_NOT_FOUND) {
- httpd_resp_send_err(req, HTTPD_404_NOT_FOUND, "Requested entry not found");
- } else {
- httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Request could not be processed");
- }
-
- return ESP_OK;
- }
- static const httpd_uri_t wakeup_get = {
- .uri = "/v1/wakeup/*",
- .method = HTTP_GET,
- .handler = wakeup_get_handler
- };
- static uint8_t validate_time(int hour, int minute) {
- return (hour >= 0 && hour < 24) && (minute >= 0 && minute < 60);
- }
- static void notify_alarm_task() {
- if (alarm_task_handle != NULL) {
- xTaskNotify(*alarm_task_handle, 0, eNoAction);
- }
- }
- static esp_err_t wakeup_put_handler(httpd_req_t *req) {
- httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*");
- char* wakeup_str = extract_wakeup(req->uri);
- char buf[BUF_LEN];
- if (!day_is_valid(wakeup_str)) {
- httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Invalid day requested");
- return ESP_OK;
- }
-
- if (req->content_len >= BUF_LEN) {
- httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Maximum content length exceeded");
- return ESP_OK;
- }
-
- int bytes_received = httpd_req_recv(req, buf, BUF_LEN);
-
- if (bytes_received == 0) {
- httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "No content received");
- return ESP_OK;
- } else if (bytes_received < 0) {
- httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Request could not be processed");
- return ESP_OK;
- }
- buf[req->content_len] = '\0';
-
- cJSON *root = cJSON_Parse(buf);
- if (root == NULL) {
- httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Could not parse object");
- return ESP_OK;
- }
-
- int16_t minute_of_day;
-
- if (cJSON_GetArraySize(root) == 0) {
- minute_of_day = -1;
- } else {
- int16_t hour = cJSON_GetObjectItem(root, "hour")->valueint;
- int16_t minute = cJSON_GetObjectItem(root, "minute")->valueint;
- if (validate_time(hour, minute)) {
- minute_of_day = hour * 60 + minute;
- } else {
- httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Time values outside allowed range");
- cJSON_Delete(root);
- return ESP_OK;
- }
- }
- esp_err_t ret = write_wakeup_time_str(wakeup_str, minute_of_day);
- if (ret == ESP_OK) {
- httpd_resp_sendstr(req, "");
- notify_alarm_task();
- } else {
- httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Error while writing data");
- }
- cJSON_Delete(root);
- return ESP_OK;
- }
- static const httpd_uri_t wakeup_put = {
- .uri = "/v1/wakeup/*",
- .method = HTTP_PUT,
- .handler = wakeup_put_handler
- };
- static esp_err_t wakeup_delete_handler(httpd_req_t *req) {
- httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*");
- char* wakeup_str = extract_wakeup(req->uri);
- if (!day_is_valid(wakeup_str)) {
- httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Invalid day requested");
- return ESP_OK;
- }
-
- esp_err_t ret = delete_wakeup_time_str(wakeup_str);
- if (ret == ESP_OK) {
- httpd_resp_sendstr(req, "");
- return ret;
- } else if (ret == ESP_ERR_NVS_NOT_FOUND) {
- httpd_resp_send_err(req, HTTPD_404_NOT_FOUND, "Entry does not exist");
- }
-
- httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Error while deleting data");
- return ESP_OK;
- }
- static const httpd_uri_t wakeup_delete = {
- .uri = "/v1/wakeup/*",
- .method = HTTP_DELETE,
- .handler = wakeup_delete_handler
- };
- static esp_err_t wakeup_options_handler(httpd_req_t *req) {
- httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*");
- httpd_resp_set_hdr(req, "Access-Control-Allow-Methods", "GET, PUT, DELETE, OPTIONS");
- httpd_resp_set_hdr(req, "Access-Control-Max-Age", "86400");
- httpd_resp_set_hdr(req, "Access-Control-Allow-Headers", "Content-Type");
- httpd_resp_sendstr(req, "");
- return ESP_OK;
- }
- static const httpd_uri_t wakeup_options = {
- .uri = "/v1/wakeup/*",
- .method = HTTP_OPTIONS,
- .handler = wakeup_options_handler
- };
- httpd_handle_t start_webserver(TaskHandle_t* task_handle) {
- httpd_handle_t server;
- httpd_config_t config = HTTPD_DEFAULT_CONFIG();
- config.lru_purge_enable = true;
- config.uri_match_fn = httpd_uri_match_wildcard;
- if (task_handle != NULL) {
- alarm_task_handle = task_handle;
- }
-
- ESP_LOGI(TAG, "Starting server on port: '%d'", config.server_port);
- if (httpd_start(&server, &config) == ESP_OK) {
- ESP_LOGI(TAG, "Registering URI handlers");
- httpd_register_uri_handler(server, &wakeup_get);
- httpd_register_uri_handler(server, &wakeup_put);
- httpd_register_uri_handler(server, &wakeup_delete);
- httpd_register_uri_handler(server, &wakeup_options);
- return server;
- }
- ESP_LOGI(TAG, "Error starting server!");
- return NULL;
- }
- esp_err_t stop_webserver(httpd_handle_t server) {
- return httpd_stop(server);
- }
- void disconnect_handler(void* arg, esp_event_base_t event_base,
- int32_t event_id, void* event_data) {
- httpd_handle_t* server = (httpd_handle_t*) arg;
- if (*server) {
- ESP_LOGI(TAG, "Stopping webserver");
- if (stop_webserver(*server) == ESP_OK) {
- *server = NULL;
- } else {
- ESP_LOGE(TAG, "Failed to stop http server");
- }
- }
- }
- void connect_handler(void* arg, esp_event_base_t event_base,
- int32_t event_id, void* event_data) {
- httpd_handle_t* server = (httpd_handle_t*) arg;
- if (*server == NULL) {
- ESP_LOGI(TAG, "Starting webserver");
- *server = start_webserver(NULL);
- }
- }
|