// === Início de: components/auth/include/auth.h === #ifndef AUTH_H #define AUTH_H #include #include #ifdef __cplusplus extern "C" { #endif /// Tamanho máximo de uma tag RFID (incluindo '\0') #define AUTH_TAG_MAX_LEN 20 /// Estrutura de evento emitida após leitura de uma tag typedef struct { char tag[AUTH_TAG_MAX_LEN]; ///< Tag lida bool authorized; ///< true se a tag for reconhecida como válida } auth_event_t; /** * @brief Inicializa o sistema de autenticação. * * - Carrega a configuração (enabled) da NVS * - Inicia o leitor Wiegand * - Emite evento AUTH_EVENT_INIT com estado atual */ void auth_init(void); /** * @brief Ativa ou desativa o uso de autenticação via RFID. * * Esta configuração é persistida em NVS. Se desativado, o sistema * considerará todas as autorizações como aceitas. * * @param value true para ativar, false para desativar */ void auth_set_enabled(bool value); /** * @brief Verifica se o sistema de autenticação está habilitado. */ bool auth_is_enabled(void); /** * @brief Adiciona uma nova tag RFID à lista de autorizadas. * * @param tag String da tag (máx AUTH_TAG_MAX_LEN-1) * @return true se a tag foi adicionada, false se já existia ou inválida */ bool auth_add_tag(const char *tag); /** * @brief Remove uma tag previamente cadastrada. * * @param tag String da tag * @return true se foi removida, false se não encontrada */ bool auth_remove_tag(const char *tag); /** * @brief Verifica se uma tag já está registrada como válida. */ bool auth_tag_exists(const char *tag); /** * @brief Lista todas as tags válidas atualmente registradas (via logs). */ void auth_list_tags(void); /** * @brief Processa uma tag RFID lida (chamada normalmente pelo leitor). * * - Verifica validade * - Emite evento AUTH_EVENT_TAG_PROCESSED * - Inicia timer de expiração se autorizada */ void auth_process_tag(const char *tag); #ifdef __cplusplus } #endif #endif // AUTH_H // === Fim de: components/auth/include/auth.h === // === Início de: components/auth/include/auth_events.h === #pragma once #include "esp_event.h" #define AUTH_EVENT_TAG_MAX_LEN 32 ESP_EVENT_DECLARE_BASE(AUTH_EVENTS); typedef enum { AUTH_EVENT_TAG_PROCESSED, AUTH_EVENT_ENABLED_CHANGED, AUTH_EVENT_INIT, } auth_event_id_t; typedef struct { char tag[AUTH_EVENT_TAG_MAX_LEN]; bool authorized; } auth_tag_event_data_t; typedef struct { bool enabled; } auth_enabled_event_data_t; // === Fim de: components/auth/include/auth_events.h === // === Início de: components/auth/include/wiegand.h === /* * Copyright (c) 2021 Ruslan V. Uss * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of itscontributors * may be used to endorse or promote products derived from this software without * specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** * @file wiegand.h * @defgroup wiegand wiegand * @{ * * ESP-IDF Wiegand protocol receiver * * Copyright (c) 2021 Ruslan V. Uss * * BSD Licensed as described in the file LICENSE */ #ifndef __WIEGAND_H__ #define __WIEGAND_H__ #include #include #include #ifdef __cplusplus extern "C" { #endif typedef struct wiegand_reader wiegand_reader_t; typedef void (*wiegand_callback_t)(wiegand_reader_t *reader); /** * Bit and byte order of data */ typedef enum { WIEGAND_MSB_FIRST = 0, WIEGAND_LSB_FIRST } wiegand_order_t; /** * Wiegand reader descriptor */ struct wiegand_reader { gpio_num_t gpio_d0, gpio_d1; wiegand_callback_t callback; wiegand_order_t bit_order; wiegand_order_t byte_order; uint8_t *buf; size_t size; size_t bits; esp_timer_handle_t timer; bool start_parity; bool enabled; }; /** * @brief Create and initialize reader instance. * * @param reader Reader descriptor * @param gpio_d0 GPIO pin for D0 * @param gpio_d1 GPIO pin for D0 * @param internal_pullups Enable internal pull-up resistors for D0 and D1 GPIO * @param buf_size Reader buffer size in bytes, must be large enough to * contain entire Wiegand key * @param callback Callback function for processing received codes * @param bit_order Bit order of data * @param byte_order Byte order of data * @return `ESP_OK` on success */ esp_err_t wiegand_reader_init(wiegand_reader_t *reader, gpio_num_t gpio_d0, gpio_num_t gpio_d1, bool internal_pullups, size_t buf_size, wiegand_callback_t callback, wiegand_order_t bit_order, wiegand_order_t byte_order); /** * @brief Disable reader * * While reader is disabled, it will not receive new data * * @param reader Reader descriptor * @return `ESP_OK` on success */ esp_err_t wiegand_reader_disable(wiegand_reader_t *reader); /** * @brief Enable reader * * @param reader Reader descriptor * @return `ESP_OK` on success */ esp_err_t wiegand_reader_enable(wiegand_reader_t *reader); /** * @brief Delete reader instance. * * @param reader Reader descriptor * @return `ESP_OK` on success */ esp_err_t wiegand_reader_done(wiegand_reader_t *reader); #ifdef __cplusplus } #endif /**@}*/ #endif /* __WIEGAND_H__ */ // === Fim de: components/auth/include/wiegand.h === // === Início de: components/auth/include/wiegand_reader.h === #ifndef WIEGAND_READER_H #define WIEGAND_READER_H #ifdef __cplusplus extern "C" { #endif void initWiegand(void); #ifdef __cplusplus } #endif #endif // WIEGAND_READER_H // === Fim de: components/auth/include/wiegand_reader.h === // === Início de: components/rest_api/src/ocpp_api.c === // ========================= // ocpp_api.c // ========================= #include "ocpp_api.h" #include "esp_log.h" #include "cJSON.h" static const char *TAG = "ocpp_api"; static struct { char url[256]; char chargeBoxId[128]; char certificate[256]; char privateKey[256]; } ocpp_config = {"", "", "", ""}; static esp_err_t ocpp_get_status_handler(httpd_req_t *req) { httpd_resp_set_type(req, "application/json"); cJSON *status = cJSON_CreateObject(); cJSON_AddStringToObject(status, "status", "connected"); char *str = cJSON_Print(status); httpd_resp_sendstr(req, str); free(str); cJSON_Delete(status); return ESP_OK; } static esp_err_t ocpp_get_config_handler(httpd_req_t *req) { httpd_resp_set_type(req, "application/json"); cJSON *json = cJSON_CreateObject(); cJSON_AddStringToObject(json, "url", ocpp_config.url); cJSON_AddStringToObject(json, "chargeBoxId", ocpp_config.chargeBoxId); cJSON_AddStringToObject(json, "certificate", ocpp_config.certificate); cJSON_AddStringToObject(json, "privateKey", ocpp_config.privateKey); char *str = cJSON_Print(json); httpd_resp_sendstr(req, str); free(str); cJSON_Delete(json); return ESP_OK; } static esp_err_t ocpp_post_config_handler(httpd_req_t *req) { char buf[512]; int len = httpd_req_recv(req, buf, sizeof(buf) - 1); if (len <= 0) { httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Empty body"); return ESP_FAIL; } buf[len] = '\0'; cJSON *json = cJSON_Parse(buf); if (!json) { httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Invalid JSON"); return ESP_FAIL; } cJSON *url = cJSON_GetObjectItem(json, "url"); if (url) strlcpy(ocpp_config.url, url->valuestring, sizeof(ocpp_config.url)); cJSON *id = cJSON_GetObjectItem(json, "chargeBoxId"); if (id) strlcpy(ocpp_config.chargeBoxId, id->valuestring, sizeof(ocpp_config.chargeBoxId)); cJSON *cert = cJSON_GetObjectItem(json, "certificate"); if (cert) strlcpy(ocpp_config.certificate, cert->valuestring, sizeof(ocpp_config.certificate)); cJSON *key = cJSON_GetObjectItem(json, "privateKey"); if (key) strlcpy(ocpp_config.privateKey, key->valuestring, sizeof(ocpp_config.privateKey)); cJSON_Delete(json); httpd_resp_sendstr(req, "OCPP config atualizada com sucesso"); return ESP_OK; } void register_ocpp_handlers(httpd_handle_t server, void *ctx) { httpd_uri_t status_uri = { .uri = "/api/v1/ocpp", .method = HTTP_GET, .handler = ocpp_get_status_handler, .user_ctx = ctx }; httpd_register_uri_handler(server, &status_uri); httpd_uri_t get_uri = { .uri = "/api/v1/config/ocpp", .method = HTTP_GET, .handler = ocpp_get_config_handler, .user_ctx = ctx }; httpd_register_uri_handler(server, &get_uri); httpd_uri_t post_uri = { .uri = "/api/v1/config/ocpp", .method = HTTP_POST, .handler = ocpp_post_config_handler, .user_ctx = ctx }; httpd_register_uri_handler(server, &post_uri); } // === Fim de: components/rest_api/src/ocpp_api.c === // === Início de: components/rest_api/src/static_file_api.c === #include "static_file_api.h" #include "esp_log.h" #include #include #include "esp_vfs.h" static const char *TAG = "static_file_api"; #define FILE_PATH_MAX (ESP_VFS_PATH_MAX + 128) #define SCRATCH_BUFSIZE (10240) typedef struct rest_server_context { char base_path[ESP_VFS_PATH_MAX + 1]; char scratch[SCRATCH_BUFSIZE]; } rest_server_context_t; #define CHECK_FILE_EXTENSION(filename, ext) \ (strcasecmp(&filename[strlen(filename) - strlen(ext)], ext) == 0) static esp_err_t set_content_type_from_file(httpd_req_t *req, const char *filepath) { const char *type = "text/plain"; if (CHECK_FILE_EXTENSION(filepath, ".html")) type = "text/html"; else if (CHECK_FILE_EXTENSION(filepath, ".js")) type = "application/javascript"; else if (CHECK_FILE_EXTENSION(filepath, ".css")) type = "text/css"; else if (CHECK_FILE_EXTENSION(filepath, ".png")) type = "image/png"; else if (CHECK_FILE_EXTENSION(filepath, ".ico")) type = "image/x-icon"; else if (CHECK_FILE_EXTENSION(filepath, ".svg")) type = "image/svg+xml"; return httpd_resp_set_type(req, type); } static esp_err_t static_get_handler(httpd_req_t *req) { char filepath[FILE_PATH_MAX]; rest_server_context_t *ctx = (rest_server_context_t *) req->user_ctx; strlcpy(filepath, ctx->base_path, sizeof(filepath)); if (req->uri[strlen(req->uri) - 1] == '/') { strlcat(filepath, "/index.html", sizeof(filepath)); } else { strlcat(filepath, req->uri, sizeof(filepath)); } int fd = open(filepath, O_RDONLY, 0); if (fd == -1) { // fallback para /index.html (SPA) ESP_LOGW(TAG, "Arquivo não encontrado: %s. Tentando index.html", filepath); strlcpy(filepath, ctx->base_path, sizeof(filepath)); strlcat(filepath, "/index.html", sizeof(filepath)); fd = open(filepath, O_RDONLY, 0); if (fd == -1) { httpd_resp_send_err(req, HTTPD_404_NOT_FOUND, "Arquivo não encontrado"); return ESP_FAIL; } } set_content_type_from_file(req, filepath); char *chunk = ctx->scratch; ssize_t read_bytes; do { read_bytes = read(fd, chunk, SCRATCH_BUFSIZE); if (read_bytes == -1) { ESP_LOGE(TAG, "Erro lendo arquivo: %s", filepath); close(fd); httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Erro ao ler arquivo"); return ESP_FAIL; } else if (read_bytes > 0) { if (httpd_resp_send_chunk(req, chunk, read_bytes) != ESP_OK) { close(fd); httpd_resp_sendstr_chunk(req, NULL); httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Erro ao enviar arquivo"); return ESP_FAIL; } } } while (read_bytes > 0); close(fd); httpd_resp_send_chunk(req, NULL, 0); return ESP_OK; } void register_static_file_handlers(httpd_handle_t server, void *ctx) { httpd_uri_t uri = { .uri = "/*", .method = HTTP_GET, .handler = static_get_handler, .user_ctx = ctx }; httpd_register_uri_handler(server, &uri); } // === Fim de: components/rest_api/src/static_file_api.c === // === Início de: components/rest_api/src/meters_settings_api.c === #include "meters_settings_api.h" #include "meter_manager.h" // Atualizado para usar o novo manager #include "esp_log.h" #include "cJSON.h" static const char *TAG = "meters_settings_api"; // Função para recuperar as configurações dos contadores static esp_err_t meters_config_get_handler(httpd_req_t *req) { ESP_LOGI(TAG, "Received GET request for /api/v1/config/meters"); httpd_resp_set_type(req, "application/json"); cJSON *config = cJSON_CreateObject(); // Recuperando as configurações dos contadores meter_type_t gridmeterType = meter_manager_grid_get_model(); meter_type_t evsemeterType = meter_manager_evse_get_model(); ESP_LOGI(TAG, "Grid meter type: %s", meter_type_to_str(gridmeterType)); ESP_LOGI(TAG, "EVSE meter type: %s", meter_type_to_str(evsemeterType)); // Adicionando os tipos de contadores ao objeto JSON cJSON_AddStringToObject(config, "gridmeter", meter_type_to_str(gridmeterType)); cJSON_AddStringToObject(config, "evsemeter", meter_type_to_str(evsemeterType)); // Convertendo o objeto JSON para uma string const char *json_str = cJSON_Print(config); ESP_LOGI(TAG, "Returning meters config: %s", json_str); httpd_resp_sendstr(req, json_str); // Liberação da memória free((void *)json_str); cJSON_Delete(config); return ESP_OK; } // Função para atualizar as configurações dos contadores static esp_err_t meters_config_post_handler(httpd_req_t *req) { ESP_LOGI(TAG, "Received POST request for /api/v1/config/meters"); char buf[512]; int len = httpd_req_recv(req, buf, sizeof(buf) - 1); if (len <= 0) { ESP_LOGE(TAG, "Received empty body in POST request"); httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Empty body"); return ESP_FAIL; } buf[len] = '\0'; // Garantir que a string está terminada ESP_LOGI(TAG, "Received POST data: %s", buf); cJSON *json = cJSON_Parse(buf); if (!json) { ESP_LOGE(TAG, "Failed to parse JSON data"); // Resposta detalhada de erro httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Invalid JSON format"); return ESP_FAIL; } // Atualizando os contadores cJSON *gridmeter = cJSON_GetObjectItem(json, "gridmeter"); if (gridmeter) { meter_type_t gridType = string_to_meter_type(gridmeter->valuestring); // Usando a função string_to_meter_type ESP_LOGI(TAG, "Updating grid meter type to: %s", gridmeter->valuestring); meter_manager_grid_set_model(gridType); } cJSON *evsemeter = cJSON_GetObjectItem(json, "evsemeter"); if (evsemeter) { meter_type_t evseType = string_to_meter_type(evsemeter->valuestring); // Usando a função string_to_meter_type ESP_LOGI(TAG, "Updating EVSE meter type to: %s", evsemeter->valuestring); meter_manager_evse_set_model(evseType); } cJSON_Delete(json); httpd_resp_sendstr(req, "Meters updated successfully"); ESP_LOGI(TAG, "Meters configuration updated successfully"); return ESP_OK; } // Registrando os manipuladores de URI para os contadores void register_meters_settings_handlers(httpd_handle_t server, void *ctx) { ESP_LOGI(TAG, "Registering URI handlers for meters settings"); // URI para o método GET httpd_uri_t meters_get_uri = { .uri = "/api/v1/config/meters", .method = HTTP_GET, .handler = meters_config_get_handler, .user_ctx = ctx }; ESP_LOGI(TAG, "Registering GET handler for /api/v1/config/meters"); httpd_register_uri_handler(server, &meters_get_uri); // URI para o método POST httpd_uri_t meters_post_uri = { .uri = "/api/v1/config/meters", .method = HTTP_POST, .handler = meters_config_post_handler, .user_ctx = ctx }; ESP_LOGI(TAG, "Registering POST handler for /api/v1/config/meters"); httpd_register_uri_handler(server, &meters_post_uri); } // === Fim de: components/rest_api/src/meters_settings_api.c === // === Início de: components/rest_api/src/rest_main.c === #include "rest_main.h" #include "evse_settings_api.h" #include "meters_settings_api.h" #include "loadbalancing_settings_api.h" #include "network_api.h" #include "ocpp_api.h" #include "auth_api.h" #include "dashboard_api.h" #include "static_file_api.h" #include "esp_log.h" static const char *TAG = "rest_main"; esp_err_t rest_server_init(const char *base_path) { ESP_LOGI(TAG, "Initializing REST API with base path: %s", base_path); rest_server_context_t *ctx = calloc(1, sizeof(rest_server_context_t)); if (!ctx) { ESP_LOGE(TAG, "Failed to allocate memory for REST context"); return ESP_ERR_NO_MEM; } strlcpy(ctx->base_path, base_path, sizeof(ctx->base_path)); httpd_config_t config = HTTPD_DEFAULT_CONFIG(); config.uri_match_fn = httpd_uri_match_wildcard; config.max_uri_handlers = 32; httpd_handle_t server = NULL; esp_err_t err = httpd_start(&server, &config); if (err != ESP_OK) { ESP_LOGE(TAG, "Failed to start HTTP server: %s", esp_err_to_name(err)); free(ctx); return err; } ESP_LOGI(TAG, "HTTP server started successfully"); // Register endpoint groups register_evse_settings_handlers(server, ctx); // Apenas chamando a função sem comparação register_network_handlers(server, ctx); // Apenas chamando a função sem comparação register_ocpp_handlers(server, ctx); // Apenas chamando a função sem comparação register_auth_handlers(server, ctx); // Apenas chamando a função sem comparação register_dashboard_handlers(server, ctx); // Apenas chamando a função sem comparação 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_static_file_handlers(server, ctx); // Apenas chamando a função sem comparação ESP_LOGI(TAG, "All REST API endpoint groups registered successfully"); return ESP_OK; } // === Fim de: components/rest_api/src/rest_main.c === // === Início de: components/rest_api/src/network_api.c === // ========================= // network_api.c // ========================= #include "network_api.h" #include "esp_log.h" #include "cJSON.h" #include "wifi.h" #include "mqtt.h" static const char *TAG = "network_api"; typedef struct { bool enabled; char ssid[33]; char password[65]; } wifi_task_data_t; static void wifi_apply_config_task(void *param) { wifi_task_data_t *data = (wifi_task_data_t *)param; ESP_LOGI("wifi_task", "Applying Wi-Fi config in background task"); wifi_set_config(data->enabled, data->ssid, data->password); free(data); vTaskDelete(NULL); } static esp_err_t wifi_get_handler(httpd_req_t *req) { ESP_LOGI(TAG, "Handling GET /api/v1/config/wifi"); httpd_resp_set_type(req, "application/json"); // Obter dados da NVS via wifi.c bool enabled = wifi_get_enabled(); char ssid[33] = {0}; char password[65] = {0}; wifi_get_ssid(ssid); wifi_get_password(password); // Criar JSON cJSON *json = cJSON_CreateObject(); cJSON_AddBoolToObject(json, "enabled", enabled); cJSON_AddStringToObject(json, "ssid", ssid); cJSON_AddStringToObject(json, "password", password); // Enviar resposta char *response = cJSON_Print(json); httpd_resp_sendstr(req, response); // Limpeza free(response); cJSON_Delete(json); return ESP_OK; } static esp_err_t wifi_post_handler(httpd_req_t *req) { ESP_LOGI(TAG, "Handling POST /api/v1/config/wifi"); char buf[512]; int len = httpd_req_recv(req, buf, sizeof(buf) - 1); if (len <= 0) return ESP_FAIL; buf[len] = '\0'; cJSON *json = cJSON_Parse(buf); if (!json) return ESP_FAIL; // Valores padrão bool enabled = false; const char *ssid = NULL; const char *password = NULL; cJSON *j_enabled = cJSON_GetObjectItem(json, "enabled"); if (cJSON_IsBool(j_enabled)) enabled = j_enabled->valueint; cJSON *j_ssid = cJSON_GetObjectItem(json, "ssid"); if (cJSON_IsString(j_ssid)) ssid = j_ssid->valuestring; cJSON *j_password = cJSON_GetObjectItem(json, "password"); if (cJSON_IsString(j_password)) password = j_password->valuestring; // Enviar resposta antes de alterar Wi-Fi httpd_resp_sendstr(req, "Wi-Fi config atualizada com sucesso"); // Alocar struct para passar para a task wifi_task_data_t *task_data = malloc(sizeof(wifi_task_data_t)); if (!task_data) { cJSON_Delete(json); ESP_LOGE(TAG, "Memory allocation failed for Wi-Fi task"); return ESP_ERR_NO_MEM; } task_data->enabled = enabled; strncpy(task_data->ssid, ssid ? ssid : "", sizeof(task_data->ssid)); strncpy(task_data->password, password ? password : "", sizeof(task_data->password)); // Criar task normal com função C xTaskCreate( wifi_apply_config_task, "wifi_config_task", 4096, task_data, 3, NULL ); cJSON_Delete(json); return ESP_OK; } static esp_err_t config_mqtt_get_handler(httpd_req_t *req) { ESP_LOGI(TAG, "Handling GET /api/v1/config/mqtt"); httpd_resp_set_type(req, "application/json"); bool enabled = mqtt_get_enabled(); char server[64] = {0}; char base_topic[32] = {0}; char username[32] = {0}; char password[64] = {0}; uint16_t periodicity = mqtt_get_periodicity(); mqtt_get_server(server); mqtt_get_base_topic(base_topic); mqtt_get_user(username); mqtt_get_password(password); ESP_LOGI(TAG, "MQTT Config:"); ESP_LOGI(TAG, " Enabled: %s", enabled ? "true" : "false"); ESP_LOGI(TAG, " Server: %s", server); ESP_LOGI(TAG, " Topic: %s", base_topic); ESP_LOGI(TAG, " Username: %s", username); ESP_LOGI(TAG, " Password: %s", password); ESP_LOGI(TAG, " Periodicity: %d", periodicity); cJSON *config = cJSON_CreateObject(); cJSON_AddBoolToObject(config, "enabled", enabled); cJSON_AddStringToObject(config, "host", server); cJSON_AddNumberToObject(config, "port", 1883); cJSON_AddStringToObject(config, "username", username); cJSON_AddStringToObject(config, "password", password); cJSON_AddStringToObject(config, "topic", base_topic); cJSON_AddNumberToObject(config, "periodicity", periodicity); const char *config_str = cJSON_Print(config); httpd_resp_sendstr(req, config_str); free((void *)config_str); cJSON_Delete(config); return ESP_OK; } static esp_err_t config_mqtt_post_handler(httpd_req_t *req) { ESP_LOGI(TAG, "Handling POST /api/v1/config/mqtt"); char buf[512]; int len = httpd_req_recv(req, buf, sizeof(buf) - 1); if (len <= 0) { ESP_LOGE(TAG, "Failed to read request body"); httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Invalid request body"); return ESP_FAIL; } buf[len] = '\0'; ESP_LOGI(TAG, "Received JSON: %s", buf); cJSON *json = cJSON_Parse(buf); if (!json) { ESP_LOGE(TAG, "Invalid JSON format"); httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Invalid JSON"); return ESP_FAIL; } bool enabled = false; const char *host = NULL, *topic = NULL, *username = NULL, *password = NULL; int periodicity = 30; if (cJSON_IsBool(cJSON_GetObjectItem(json, "enabled"))) enabled = cJSON_GetObjectItem(json, "enabled")->valueint; cJSON *j_host = cJSON_GetObjectItem(json, "host"); if (cJSON_IsString(j_host)) host = j_host->valuestring; cJSON *j_topic = cJSON_GetObjectItem(json, "topic"); if (cJSON_IsString(j_topic)) topic = j_topic->valuestring; cJSON *j_user = cJSON_GetObjectItem(json, "username"); if (cJSON_IsString(j_user)) username = j_user->valuestring; cJSON *j_pass = cJSON_GetObjectItem(json, "password"); if (cJSON_IsString(j_pass)) password = j_pass->valuestring; cJSON *j_periodicity = cJSON_GetObjectItem(json, "periodicity"); if (cJSON_IsNumber(j_periodicity)) periodicity = j_periodicity->valueint; ESP_LOGI(TAG, "Applying MQTT config:"); ESP_LOGI(TAG, " Enabled: %s", enabled ? "true" : "false"); ESP_LOGI(TAG, " Host: %s", host); ESP_LOGI(TAG, " Topic: %s", topic); ESP_LOGI(TAG, " Username: %s", username); ESP_LOGI(TAG, " Password: %s", password); ESP_LOGI(TAG, " Periodicity: %d", periodicity); esp_err_t err = mqtt_set_config(enabled, host, topic, username, password, periodicity); if (err != ESP_OK) { ESP_LOGE(TAG, "Failed to apply MQTT config (code %d)", err); httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to apply config"); cJSON_Delete(json); return ESP_FAIL; } httpd_resp_sendstr(req, "Configuração MQTT atualizada com sucesso"); cJSON_Delete(json); return ESP_OK; } void register_network_handlers(httpd_handle_t server, void *ctx) { httpd_uri_t wifi_get = { .uri = "/api/v1/config/wifi", .method = HTTP_GET, .handler = wifi_get_handler, .user_ctx = ctx }; httpd_register_uri_handler(server, &wifi_get); httpd_uri_t wifi_post = { .uri = "/api/v1/config/wifi", .method = HTTP_POST, .handler = wifi_post_handler, .user_ctx = ctx }; httpd_register_uri_handler(server, &wifi_post); // URI handler for getting MQTT config httpd_uri_t config_mqtt_get_uri = { .uri = "/api/v1/config/mqtt", .method = HTTP_GET, .handler = config_mqtt_get_handler, .user_ctx = ctx }; httpd_register_uri_handler(server, &config_mqtt_get_uri); // URI handler for posting MQTT config httpd_uri_t config_mqtt_post_uri = { .uri = "/api/v1/config/mqtt", .method = HTTP_POST, .handler = config_mqtt_post_handler, .user_ctx = ctx }; httpd_register_uri_handler(server, &config_mqtt_post_uri); } // === Fim de: components/rest_api/src/network_api.c ===