new module

This commit is contained in:
2025-12-09 11:48:31 +00:00
parent 4820d9111e
commit e6e2622a95
98 changed files with 5349 additions and 8607 deletions

View File

@@ -8,6 +8,7 @@ set(srcs
"src/loadbalancing_settings_api.c"
"src/evse_link_config_api.c"
"src/dashboard_api.c"
"src/scheduler_settings_api.c"
"src/static_file_api.c"
)
@@ -15,7 +16,7 @@ idf_component_register(
SRCS ${srcs}
INCLUDE_DIRS "include"
PRIV_INCLUDE_DIRS "src"
PRIV_REQUIRES nvs_flash esp_http_server esp_netif vfs spiffs json evse meter_manager protocols loadbalancer evse_link
PRIV_REQUIRES nvs_flash esp_http_server esp_netif vfs spiffs json evse meter_manager protocols loadbalancer evse_link scheduler
)
# SPIFFS image (opcional)

View File

@@ -0,0 +1,23 @@
// =========================
// scheduler_settings_api.h
// =========================
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include "esp_http_server.h"
/**
* @brief Registra os handlers de configuração do scheduler
*
* Endpoints:
* GET /api/v1/config/scheduler
* POST /api/v1/config/scheduler
*/
void register_scheduler_settings_handlers(httpd_handle_t server, void *ctx);
#ifdef __cplusplus
}
#endif

View File

@@ -7,6 +7,7 @@
#include "ocpp_api.h"
#include "auth_api.h"
#include "dashboard_api.h"
#include "scheduler_settings_api.h"
#include "static_file_api.h"
#include "esp_log.h"
@@ -49,8 +50,8 @@ esp_err_t rest_server_init(const char *base_path)
register_meters_settings_handlers(server, ctx); // Apenas chamando a função sem comparação
register_loadbalancing_settings_handlers(server, ctx); // Apenas chamando a função sem comparação
register_link_config_handlers(server, ctx);
register_meters_data_handlers(server, ctx);
register_scheduler_settings_handlers(server, ctx);
register_static_file_handlers(server, ctx); // Apenas chamando a função sem comparação

View File

@@ -0,0 +1,224 @@
#include "scheduler_settings_api.h"
#include "scheduler.h"
#include "scheduler_types.h"
#include "esp_log.h"
#include "esp_http_server.h"
#include "cJSON.h"
#include <string.h>
#include <ctype.h>
#include <stdio.h> // sscanf, snprintf
static const char *TAG = "scheduler_api";
/* =========================
* Helpers HH:MM <-> minutos
* ========================= */
static bool parse_hhmm(const char *s, uint16_t *out_min)
{
if (!s || !out_min)
return false;
// formato esperado: "HH:MM"
int h = 0, m = 0;
if (sscanf(s, "%d:%d", &h, &m) != 2)
{
return false;
}
if (h < 0 || h > 23 || m < 0 || m > 59)
{
return false;
}
*out_min = (uint16_t)(h * 60 + m);
return true;
}
static void format_hhmm(uint16_t minutes, char *buf, size_t buf_sz)
{
if (!buf || buf_sz < 6)
return;
minutes %= (24 * 60);
int h = minutes / 60;
int m = minutes % 60;
snprintf(buf, buf_sz, "%02d:%02d", h, m);
}
/* =========================
* GET /api/v1/config/scheduler
* ========================= */
static esp_err_t scheduler_config_get_handler(httpd_req_t *req)
{
ESP_LOGI(TAG, "GET /api/v1/config/scheduler");
httpd_resp_set_type(req, "application/json");
sched_config_t cfg = scheduler_get_config();
bool allowed_now = scheduler_is_allowed_now();
cJSON *root = cJSON_CreateObject();
if (!root)
{
httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "cJSON alloc failed");
return ESP_FAIL;
}
cJSON_AddBoolToObject(root, "enabled", cfg.enabled);
cJSON_AddStringToObject(root, "mode", sched_mode_to_str(cfg.mode));
char buf[8];
format_hhmm(cfg.start_min, buf, sizeof(buf));
cJSON_AddStringToObject(root, "startTime", buf);
format_hhmm(cfg.end_min, buf, sizeof(buf));
cJSON_AddStringToObject(root, "endTime", buf);
cJSON_AddBoolToObject(root, "allowedNow", allowed_now);
char *json_str = cJSON_PrintUnformatted(root);
if (!json_str)
{
cJSON_Delete(root);
httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "cJSON print failed");
return ESP_FAIL;
}
httpd_resp_sendstr(req, json_str);
free(json_str);
cJSON_Delete(root);
return ESP_OK;
}
/* =========================
* POST /api/v1/config/scheduler
* ========================= */
static esp_err_t scheduler_config_post_handler(httpd_req_t *req)
{
ESP_LOGI(TAG, "POST /api/v1/config/scheduler");
// NOTA: para payloads pequenos 512 bytes chega; se quiseres robustez total,
// usa req->content_len e faz um loop com httpd_req_recv.
char buf[512];
int len = httpd_req_recv(req, buf, sizeof(buf) - 1);
if (len <= 0)
{
ESP_LOGE(TAG, "Empty body / recv error");
httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Empty body");
return ESP_FAIL;
}
buf[len] = '\0';
ESP_LOGI(TAG, "Body: %s", buf);
cJSON *json = cJSON_Parse(buf);
if (!json)
{
ESP_LOGE(TAG, "Invalid JSON");
httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Invalid JSON");
return ESP_FAIL;
}
// Começa a partir da config atual
sched_config_t cfg = scheduler_get_config();
// enabled
cJSON *j_enabled = cJSON_GetObjectItem(json, "enabled");
if (cJSON_IsBool(j_enabled))
{
cfg.enabled = cJSON_IsTrue(j_enabled);
ESP_LOGI(TAG, " enabled = %d", cfg.enabled);
}
// mode
cJSON *j_mode = cJSON_GetObjectItem(json, "mode");
if (cJSON_IsString(j_mode) && j_mode->valuestring)
{
sched_mode_t m;
if (!sched_mode_from_str(j_mode->valuestring, &m))
{
ESP_LOGW(TAG, "Invalid mode: %s", j_mode->valuestring);
cJSON_Delete(json);
httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST,
"Invalid mode (use: disabled|simple|weekly)");
return ESP_FAIL;
}
cfg.mode = m;
ESP_LOGI(TAG, " mode = %s", sched_mode_to_str(cfg.mode));
}
// startTime (string "HH:MM")
cJSON *j_start = cJSON_GetObjectItem(json, "startTime");
if (cJSON_IsString(j_start) && j_start->valuestring)
{
uint16_t minutes = 0;
if (!parse_hhmm(j_start->valuestring, &minutes))
{
ESP_LOGW(TAG, "Invalid startTime: %s", j_start->valuestring);
cJSON_Delete(json);
httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST,
"Invalid startTime (use HH:MM)");
return ESP_FAIL;
}
cfg.start_min = minutes;
ESP_LOGI(TAG, " start_min = %u", (unsigned)cfg.start_min);
}
// endTime (string "HH:MM")
cJSON *j_end = cJSON_GetObjectItem(json, "endTime");
if (cJSON_IsString(j_end) && j_end->valuestring)
{
uint16_t minutes = 0;
if (!parse_hhmm(j_end->valuestring, &minutes))
{
ESP_LOGW(TAG, "Invalid endTime: %s", j_end->valuestring);
cJSON_Delete(json);
httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST,
"Invalid endTime (use HH:MM)");
return ESP_FAIL;
}
cfg.end_min = minutes;
ESP_LOGI(TAG, " end_min = %u", (unsigned)cfg.end_min);
}
// (Opcional) validações extra:
// exemplo: impedir janela vazia quando ativo
/*
if (cfg.enabled && cfg.mode != SCHED_MODE_DISABLED &&
cfg.start_min == cfg.end_min) {
cJSON_Delete(json);
httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST,
"startTime and endTime cannot be equal");
return ESP_FAIL;
}
*/
// Aplica config no módulo scheduler (ele trata de NVS + eventos)
scheduler_set_config(&cfg);
cJSON_Delete(json);
httpd_resp_sendstr(req, "Scheduler config atualizada com sucesso");
return ESP_OK;
}
/* =========================
* Registo dos handlers
* ========================= */
void register_scheduler_settings_handlers(httpd_handle_t server, void *ctx)
{
httpd_uri_t get_uri = {
.uri = "/api/v1/config/scheduler",
.method = HTTP_GET,
.handler = scheduler_config_get_handler,
.user_ctx = ctx};
httpd_register_uri_handler(server, &get_uri);
httpd_uri_t post_uri = {
.uri = "/api/v1/config/scheduler",
.method = HTTP_POST,
.handler = scheduler_config_post_handler,
.user_ctx = ctx};
httpd_register_uri_handler(server, &post_uri);
ESP_LOGI(TAG, "Scheduler REST handlers registered");
}