new release

This commit is contained in:
2026-05-15 12:20:47 +01:00
parent 286028b6a8
commit d0d431daf2
440 changed files with 2776 additions and 2462 deletions

View File

@@ -10,6 +10,7 @@ set(srcs
"src/dashboard_api.c"
"src/scheduler_settings_api.c"
"src/static_file_api.c"
"src/system_api.c"
)
idf_component_register(

View File

@@ -0,0 +1,16 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include "esp_http_server.h"
/**
* @brief Regista os handlers REST de informação do sistema.
*/
void register_system_handlers(httpd_handle_t server, void *ctx);
#ifdef __cplusplus
}
#endif

View File

@@ -57,6 +57,94 @@ static esp_err_t recv_body(httpd_req_t *req, char *buf, size_t buf_sz, int *out_
return ESP_OK;
}
static void trim_in_place(char *s) {
if (!s || s[0] == '\0') return;
char *start = s;
while (*start == ' ' || *start == '\t' || *start == '\r' || *start == '\n') {
start++;
}
if (start != s) {
memmove(s, start, strlen(start) + 1);
}
size_t len = strlen(s);
while (len > 0) {
char c = s[len - 1];
if (c == ' ' || c == '\t' || c == '\r' || c == '\n') {
s[len - 1] = '\0';
len--;
} else {
break;
}
}
}
static void strip_wrapping_quotes_in_place(char *s) {
size_t len;
if (!s) return;
trim_in_place(s);
len = strlen(s);
if (len >= 2 && s[0] == '"' && s[len - 1] == '"') {
memmove(s, s + 1, len - 2);
s[len - 2] = '\0';
trim_in_place(s);
}
}
static bool extract_username_from_body(const char *buf, int len, char *out, size_t out_sz) {
bool ok = false;
cJSON *json = cJSON_ParseWithLength(buf, len);
out[0] = '\0';
if (json) {
if (cJSON_IsString(json) && json->valuestring) {
strlcpy(out, json->valuestring, out_sz);
ok = true;
} else if (cJSON_IsObject(json)) {
cJSON *username_js = cJSON_GetObjectItem(json, "username");
if (cJSON_IsString(username_js) && username_js->valuestring) {
strlcpy(out, username_js->valuestring, out_sz);
ok = true;
}
}
cJSON_Delete(json);
}
if (!ok) {
strlcpy(out, buf, out_sz);
}
trim_in_place(out);
strip_wrapping_quotes_in_place(out);
return out[0] != '\0';
}
static bool usernames_equal_normalized(const char *a, const char *b) {
char na[128] = {0};
char nb[128] = {0};
strlcpy(na, a ? a : "", sizeof(na));
strlcpy(nb, b ? b : "", sizeof(nb));
trim_in_place(na);
trim_in_place(nb);
strip_wrapping_quotes_in_place(na);
strip_wrapping_quotes_in_place(nb);
return strcmp(na, nb) == 0;
}
static int find_user_index(const char *username) {
for (int i = 0; i < num_users; ++i) {
if (usernames_equal_normalized(users[i].username, username)) {
return i;
}
}
return -1;
}
// =================================
// Auth Mode (NEW API)
// =================================
@@ -125,37 +213,48 @@ static esp_err_t users_get_handler(httpd_req_t *req) {
static esp_err_t users_post_handler(httpd_req_t *req) {
char buf[128];
char username[128] = {0};
int len = 0;
if (recv_body(req, buf, sizeof(buf), &len) != ESP_OK || len <= 0) {
httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Body vazio");
return ESP_FAIL;
}
if (num_users < 10) {
strlcpy(users[num_users].username, buf, sizeof(users[num_users].username));
num_users++;
httpd_resp_sendstr(req, "Usuário adicionado com sucesso");
return ESP_OK;
} else {
if (!extract_username_from_body(buf, len, username, sizeof(username))) {
httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Campo 'username' inválido ou ausente");
return ESP_FAIL;
}
if (find_user_index(username) >= 0) {
httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Usuário já existe");
return ESP_FAIL;
}
if (num_users >= 10) {
httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Limite de usuários atingido");
return ESP_FAIL;
}
strlcpy(users[num_users].username, username, sizeof(users[num_users].username));
num_users++;
httpd_resp_sendstr(req, "Usuário adicionado com sucesso");
return ESP_OK;
}
static esp_err_t users_delete_handler(httpd_req_t *req) {
char query[128];
if (httpd_req_get_url_query_str(req, query, sizeof(query)) == ESP_OK) {
char username[128];
char username[128] = {0};
if (httpd_query_key_value(query, "username", username, sizeof(username)) == ESP_OK) {
for (int i = 0; i < num_users; i++) {
if (strcmp(users[i].username, username) == 0) {
for (int j = i; j < num_users - 1; j++) {
users[j] = users[j + 1];
}
num_users--;
httpd_resp_sendstr(req, "Usuário removido com sucesso");
return ESP_OK;
int idx = find_user_index(username);
if (idx >= 0) {
for (int j = idx; j < num_users - 1; j++) {
users[j] = users[j + 1];
}
num_users--;
httpd_resp_sendstr(req, "Usuário removido com sucesso");
return ESP_OK;
}
}
}

View File

@@ -17,19 +17,34 @@ static esp_err_t dashboard_get_handler(httpd_req_t *req) {
// Status do sistema
evse_state_t state = evse_get_state();
int runtime_current = evse_get_runtime_charging_current();
int requested_current = evse_get_charging_current();
int hardware_max = evse_get_max_charging_current();
int power = runtime_current * 230;
cJSON_AddStringToObject(dashboard, "status", evse_state_to_str(state));
// Aliases de topo para clientes novos (mantendo compatibilidade com chargers[])
cJSON_AddNumberToObject(dashboard, "currentA", runtime_current);
cJSON_AddNumberToObject(dashboard, "currentLimitNowA", runtime_current);
cJSON_AddNumberToObject(dashboard, "maxCurrentA", requested_current);
cJSON_AddNumberToObject(dashboard, "hardwareMaxA", hardware_max);
cJSON_AddNumberToObject(dashboard, "power", power);
cJSON_AddNumberToObject(dashboard, "powerW", power);
// Carregador - informação do carregador 1 (adapte conforme necessário)
cJSON *chargers = cJSON_CreateArray();
cJSON *charger1 = cJSON_CreateObject();
cJSON_AddNumberToObject(charger1, "id", 1);
cJSON_AddStringToObject(charger1, "status", evse_state_to_str(state));
cJSON_AddNumberToObject(charger1, "current", evse_get_runtime_charging_current());
cJSON_AddNumberToObject(charger1, "maxCurrent", evse_get_charging_current());
cJSON_AddNumberToObject(charger1, "current", runtime_current);
cJSON_AddNumberToObject(charger1, "currentA", runtime_current);
cJSON_AddNumberToObject(charger1, "maxCurrent", requested_current);
cJSON_AddNumberToObject(charger1, "maxCurrentA", requested_current);
// Calcular a potência com base na corrente (considerando 230V)
int power = (evse_get_runtime_charging_current()) * 230;
cJSON_AddNumberToObject(charger1, "power", power);
cJSON_AddNumberToObject(charger1, "powerW", power);
cJSON_AddItemToArray(chargers, charger1);
cJSON_AddItemToObject(dashboard, "chargers", chargers);

View File

@@ -4,6 +4,7 @@
#include "evse_settings_api.h"
#include "evse_api.h"
#include "evse_config.h"
#include "evse_limits.h"
#include "esp_log.h"
#include "cJSON.h"
@@ -11,12 +12,23 @@ static const char *TAG = "evse_settings_api";
static esp_err_t config_settings_get_handler(httpd_req_t *req) {
httpd_resp_set_type(req, "application/json");
cJSON *config = cJSON_CreateObject();
cJSON_AddNumberToObject(config, "maxCurrentLimit", evse_get_max_charging_current());
cJSON_AddNumberToObject(config, "currentLimit", evse_get_charging_current());
cJSON_AddNumberToObject(config, "powerLimit", 0);
cJSON_AddNumberToObject(config, "energyLimit", evse_get_consumption_limit());
cJSON_AddNumberToObject(config, "chargingTimeLimit", evse_get_charging_time_limit());
cJSON_AddNumberToObject(config, "temperatureLimit", evse_get_temp_threshold());
const char *json_str = cJSON_Print(config);
httpd_resp_sendstr(req, json_str);
free((void *)json_str);
cJSON *security = cJSON_CreateObject();
cJSON_AddBoolToObject(security, "earthFault", false);
cJSON_AddBoolToObject(security, "rcm", false);
cJSON_AddItemToObject(config, "security", security);
char *json_str = cJSON_PrintUnformatted(config);
httpd_resp_sendstr(req, json_str ? json_str : "{}");
if (json_str) free(json_str);
cJSON_Delete(config);
return ESP_OK;
}
@@ -36,9 +48,25 @@ static esp_err_t config_settings_post_handler(httpd_req_t *req) {
}
cJSON *current = cJSON_GetObjectItem(json, "currentLimit");
if (current) evse_set_charging_current(current->valueint);
if (cJSON_IsNumber(current)) {
int value = current->valueint;
int hw_max = evse_get_max_charging_current();
if (value < 0) value = 0;
if (value > hw_max) value = hw_max;
evse_set_charging_current(value);
}
cJSON *temp = cJSON_GetObjectItem(json, "temperatureLimit");
if (temp) evse_set_temp_threshold(temp->valueint);
if (cJSON_IsNumber(temp)) {
evse_set_temp_threshold(temp->valueint);
}
// Campos aceites por compatibilidade de contrato v1.
// Nesta sprint são ignorados caso o firmware ainda não os suporte internamente.
(void)cJSON_GetObjectItem(json, "powerLimit");
(void)cJSON_GetObjectItem(json, "energyLimit");
(void)cJSON_GetObjectItem(json, "chargingTimeLimit");
(void)cJSON_GetObjectItem(json, "security");
cJSON_Delete(json);
httpd_resp_sendstr(req, "Configurações atualizadas com sucesso");

View File

@@ -13,6 +13,23 @@
static const char *TAG = "network_api";
static bool mqtt_host_is_invalid(const char *host)
{
if (!host || host[0] == '\0')
return true;
if (strcmp(host, "localhost") == 0)
return true;
if (strcmp(host, "127.0.0.1") == 0)
return true;
if (strcmp(host, "0.0.0.0") == 0)
return true;
return false;
}
typedef struct
{
bool enabled;
@@ -225,10 +242,14 @@ static esp_err_t config_mqtt_post_handler(httpd_req_t *req)
if (cJSON_IsNumber(j_periodicity))
periodicity = j_periodicity->valueint;
// --- Regras: se vier NULL ou "" mantém o atual; se atual também estiver vazio, usa default
const char *host =
(host_in && host_in[0] != '\0') ? host_in : (current_host[0] != '\0') ? current_host
: "mqtt.plixin.com";
const char *candidate_host =
(host_in && host_in[0] != '\0') ? host_in :
(current_host[0] != '\0') ? current_host :
"mqtt.plixin.com";
const char *host = mqtt_host_is_invalid(candidate_host)
? "mqtt.plixin.com"
: candidate_host;
const char *topic =
(topic_in && topic_in[0] != '\0') ? topic_in : (current_topic[0] != '\0') ? current_topic
@@ -298,4 +319,4 @@ void register_network_handlers(httpd_handle_t server, void *ctx)
.handler = config_mqtt_post_handler,
.user_ctx = ctx};
httpd_register_uri_handler(server, &config_mqtt_post_uri);
}
}

View File

@@ -9,6 +9,7 @@
#include "dashboard_api.h"
#include "scheduler_settings_api.h"
#include "static_file_api.h"
#include "system_api.h"
#include "esp_log.h"
#include "freertos/FreeRTOS.h"
@@ -62,6 +63,7 @@ esp_err_t rest_server_init(const char *base_path)
register_link_config_handlers(server, ctx);
register_meters_data_handlers(server, ctx);
register_scheduler_settings_handlers(server, ctx);
register_system_handlers(server, ctx);
register_static_file_handlers(server, ctx);
ESP_LOGI(TAG, "All REST API endpoint groups registered successfully");

View File

@@ -1,3 +1,4 @@
// components/rest_api/src/scheduler_settings_api.c
#include "scheduler_settings_api.h"
#include "scheduler.h"
#include "scheduler_types.h"
@@ -6,14 +7,14 @@
#include "esp_http_server.h"
#include "cJSON.h"
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <stdio.h> // sscanf, snprintf
static const char *TAG = "scheduler_api";
/* =========================
* Helpers HH:MM <-> minutos
* - aceita 24:00 => 1440
* ========================= */
static bool parse_hhmm(const char *s, uint16_t *out_min)
@@ -21,16 +22,19 @@ 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 == 24 && m == 0)
{
*out_min = 1440;
return true;
}
if (h < 0 || h > 23 || m < 0 || m > 59)
{
return false;
}
*out_min = (uint16_t)(h * 60 + m);
return true;
}
@@ -39,10 +43,78 @@ 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);
if (minutes == 1440)
{
snprintf(buf, buf_sz, "24:00");
return;
}
minutes %= 1440;
snprintf(buf, buf_sz, "%02d:%02d", minutes / 60, minutes % 60);
}
static cJSON *window_to_json(const sched_window_t *w)
{
cJSON *o = cJSON_CreateObject();
if (!o)
return NULL;
char b[8];
format_hhmm(w->start_min, b, sizeof(b));
cJSON_AddStringToObject(o, "startTime", b);
format_hhmm(w->end_min, b, sizeof(b));
cJSON_AddStringToObject(o, "endTime", b);
cJSON_AddNumberToObject(o, "currentA", (int)w->current_a);
return o;
}
static bool parse_windows_array(cJSON *arr, sched_config_t *cfg)
{
if (!arr || !cfg || !cJSON_IsArray(arr))
return false;
int n = cJSON_GetArraySize(arr);
if (n <= 0)
return false;
if (n > SCHED_MAX_WINDOWS)
n = SCHED_MAX_WINDOWS;
cfg->window_count = 0;
for (int i = 0; i < n; i++)
{
cJSON *it = cJSON_GetArrayItem(arr, i);
if (!cJSON_IsObject(it))
return false;
cJSON *js = cJSON_GetObjectItem(it, "startTime");
cJSON *je = cJSON_GetObjectItem(it, "endTime");
cJSON *ja = cJSON_GetObjectItem(it, "currentA");
if (!cJSON_IsString(js) || !cJSON_IsString(je) || !cJSON_IsNumber(ja))
return false;
uint16_t sm = 0, em = 0;
if (!parse_hhmm(js->valuestring, &sm))
return false;
if (!parse_hhmm(je->valuestring, &em))
return false;
int a = ja->valueint;
if (a < 6 || a > 80)
return false;
cfg->windows[cfg->window_count].start_min = sm;
cfg->windows[cfg->window_count].end_min = em;
cfg->windows[cfg->window_count].current_a = (uint16_t)a;
cfg->window_count++;
}
return true;
}
/* =========================
@@ -51,11 +123,9 @@ static void format_hhmm(uint16_t minutes, char *buf, size_t buf_sz)
static esp_err_t scheduler_config_get_handler(httpd_req_t *req)
{
ESP_LOGD(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)
@@ -67,13 +137,17 @@ static esp_err_t scheduler_config_get_handler(httpd_req_t *req)
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 *arr = cJSON_CreateArray();
for (int i = 0; i < cfg.window_count && i < SCHED_MAX_WINDOWS; i++)
{
cJSON *w = window_to_json(&cfg.windows[i]);
if (w)
cJSON_AddItemToArray(arr, w);
}
cJSON_AddItemToObject(root, "windows", arr);
cJSON_AddBoolToObject(root, "allowedNow", allowed_now);
cJSON_AddBoolToObject(root, "allowedNow", scheduler_is_allowed_now());
cJSON_AddNumberToObject(root, "currentLimitNowA", (int)scheduler_get_current_limit_now_a());
char *json_str = cJSON_PrintUnformatted(root);
if (!json_str)
@@ -84,7 +158,6 @@ static esp_err_t scheduler_config_get_handler(httpd_req_t *req)
}
httpd_resp_sendstr(req, json_str);
free(json_str);
cJSON_Delete(root);
return ESP_OK;
@@ -97,113 +170,66 @@ static esp_err_t scheduler_config_post_handler(httpd_req_t *req)
{
ESP_LOGD(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_LOGD(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
// começa do estado 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_LOGD(TAG, " enabled = %d", cfg.enabled);
}
// mode
cJSON *j_mode = cJSON_GetObjectItem(json, "mode");
if (cJSON_IsString(j_mode) && j_mode->valuestring)
if (!cfg.enabled)
{
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_LOGD(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_LOGD(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_LOGD(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) {
cfg.mode = SCHED_MODE_DISABLED;
cfg.window_count = 0;
scheduler_set_config(&cfg);
cJSON_Delete(json);
httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST,
"startTime and endTime cannot be equal");
httpd_resp_sendstr(req, "OK");
return ESP_OK;
}
// enabled=true => exige windows[]
cJSON *j_windows = cJSON_GetObjectItem(json, "windows");
if (!j_windows || !cJSON_IsArray(j_windows))
{
cJSON_Delete(json);
httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Missing windows[]");
return ESP_FAIL;
}
cfg.mode = SCHED_MODE_DAILY_MULTI;
if (!parse_windows_array(j_windows, &cfg))
{
cJSON_Delete(json);
httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST,
"Invalid windows (use: [{startTime,endTime,currentA}], currentA 6-80)");
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");
httpd_resp_sendstr(req, "OK");
return ESP_OK;
}
/* =========================
* Registo dos handlers
* ========================= */
void register_scheduler_settings_handlers(httpd_handle_t server, void *ctx)
{
httpd_uri_t get_uri = {
@@ -221,4 +247,4 @@ void register_scheduler_settings_handlers(httpd_handle_t server, void *ctx)
httpd_register_uri_handler(server, &post_uri);
ESP_LOGD(TAG, "Scheduler REST handlers registered");
}
}

View File

@@ -0,0 +1,108 @@
#include "system_api.h"
#include "cJSON.h"
#include "esp_log.h"
#include "esp_netif.h"
#include "esp_system.h"
#include "esp_timer.h"
#include "auth.h"
#include "evse_api.h"
#include "evse_config.h"
#include "evse_limits.h"
#include "mqtt.h"
#include "network.h"
#include <string.h>
static const char *TAG = "system_api";
#ifndef FW_VERSION
#define FW_VERSION "1.0"
#endif
static esp_err_t system_info_get_handler(httpd_req_t *req)
{
httpd_resp_set_type(req, "application/json");
cJSON *root = cJSON_CreateObject();
if (!root)
{
httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Falha ao criar resposta JSON");
return ESP_FAIL;
}
const bool wifi_enabled = wifi_get_enabled();
const int64_t uptime_us = esp_timer_get_time();
cJSON_AddStringToObject(root, "apiVersion", "1.0");
cJSON_AddStringToObject(root, "device", "PLX EV Charger");
cJSON_AddStringToObject(root, "fwVersion", FW_VERSION);
cJSON_AddStringToObject(root, "serial", "");
cJSON_AddStringToObject(root, "mode", wifi_enabled ? "sta" : "ap");
cJSON_AddBoolToObject(root, "online", true);
cJSON_AddStringToObject(root, "localConfigMode", wifi_enabled ? "sta" : "ap_default");
cJSON_AddStringToObject(root, "authScheme", "basic");
cJSON_AddNumberToObject(root, "uptimeS", (double)(uptime_us / 1000000));
cJSON_AddNumberToObject(root, "freeHeap", (double)esp_get_free_heap_size());
cJSON_AddNumberToObject(root, "hardwareMaxA", evse_get_max_charging_current());
cJSON_AddNumberToObject(root, "currentLimit", evse_get_charging_current());
cJSON_AddBoolToObject(root, "wifiEnabled", wifi_enabled);
cJSON_AddStringToObject(root, "authMode", auth_mode_to_str(auth_get_mode()));
esp_netif_t *netif = esp_netif_get_handle_from_ifkey("WIFI_STA_DEF");
if (netif)
{
esp_netif_ip_info_t ip_info;
if (esp_netif_get_ip_info(netif, &ip_info) == ESP_OK)
{
char ip_str[16] = {0};
snprintf(ip_str, sizeof(ip_str), IPSTR, IP2STR(&ip_info.ip));
cJSON_AddStringToObject(root, "ip", ip_str);
}
}
cJSON *capabilities = cJSON_CreateObject();
if (capabilities)
{
cJSON_AddBoolToObject(capabilities, "wifiScan", false);
cJSON_AddBoolToObject(capabilities, "logs", false);
cJSON_AddBoolToObject(capabilities, "systemReboot", false);
cJSON_AddBoolToObject(capabilities, "systemFactoryReset", false);
cJSON_AddBoolToObject(capabilities, "systemApExit", false);
cJSON_AddBoolToObject(capabilities, "users", true);
cJSON_AddBoolToObject(capabilities, "tags", true);
cJSON_AddBoolToObject(capabilities, "security", true);
cJSON_AddBoolToObject(capabilities, "scheduler", true);
cJSON_AddBoolToObject(capabilities, "loadBalancing", true);
cJSON_AddBoolToObject(capabilities, "ocpp", true);
cJSON_AddBoolToObject(capabilities, "link", true);
cJSON_AddBoolToObject(capabilities, "metersConfig", true);
cJSON_AddItemToObject(root, "capabilities", capabilities);
}
char *json_str = cJSON_PrintUnformatted(root);
if (!json_str)
{
cJSON_Delete(root);
httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Falha ao serializar JSON");
return ESP_FAIL;
}
ESP_LOGD(TAG, "GET /api/v1/system/info -> %s", json_str);
httpd_resp_sendstr(req, json_str);
free(json_str);
cJSON_Delete(root);
return ESP_OK;
}
void register_system_handlers(httpd_handle_t server, void *ctx)
{
httpd_register_uri_handler(server, &(httpd_uri_t){
.uri = "/api/v1/system/info",
.method = HTTP_GET,
.handler = system_info_get_handler,
.user_ctx = ctx,
});
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -13,8 +13,8 @@
}
</style>
<title>ChargeFlow</title>
<script type="module" crossorigin src="/assets/index-0q0tbwk5.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-BIZ-rt0x.css">
<script type="module" crossorigin src="/assets/index-CBPnRb7F.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-5y1tcufv.css">
</head>
<body>
<div id="root"></div>