Files
gateway/components/gateway_network/src/gateway_network.cpp
T

759 lines
23 KiB
C++

#include "gateway_network.hpp"
#include "gateway_controller.hpp"
#include "gateway_runtime.hpp"
#include "cJSON.h"
#include "driver/gpio.h"
#include "esp_event.h"
#include "esp_log.h"
#include "esp_netif.h"
#include "esp_netif_ip_addr.h"
#include "esp_wifi.h"
#include "lwip/inet.h"
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <string_view>
#include <utility>
#include <vector>
namespace gateway {
namespace {
constexpr const char* kTag = "gateway_network";
constexpr const char* kSetupApSsid = "LAMMIN_Gateway";
constexpr size_t kUdpBufferSize = 256;
class LockGuard {
public:
explicit LockGuard(SemaphoreHandle_t lock) : lock_(lock) {
if (lock_ != nullptr) {
xSemaphoreTake(lock_, portMAX_DELAY);
}
}
~LockGuard() {
if (lock_ != nullptr) {
xSemaphoreGive(lock_);
}
}
private:
SemaphoreHandle_t lock_;
};
bool HexValue(char ch, uint8_t& value) {
if (ch >= '0' && ch <= '9') {
value = static_cast<uint8_t>(ch - '0');
return true;
}
if (ch >= 'a' && ch <= 'f') {
value = static_cast<uint8_t>(10 + ch - 'a');
return true;
}
if (ch >= 'A' && ch <= 'F') {
value = static_cast<uint8_t>(10 + ch - 'A');
return true;
}
return false;
}
std::vector<uint8_t> DecodeHex(std::string_view hex) {
if ((hex.size() & 1U) != 0U) {
return {};
}
std::vector<uint8_t> bytes;
bytes.reserve(hex.size() / 2);
for (size_t i = 0; i < hex.size(); i += 2) {
uint8_t high = 0;
uint8_t low = 0;
if (!HexValue(hex[i], high) || !HexValue(hex[i + 1], low)) {
return {};
}
bytes.push_back(static_cast<uint8_t>((high << 4) | low));
}
return bytes;
}
std::string MacToHex(const uint8_t mac[6]) {
char out[13] = {0};
std::snprintf(out, sizeof(out), "%02X%02X%02X%02X%02X%02X", mac[0], mac[1], mac[2], mac[3],
mac[4], mac[5]);
return std::string(out);
}
std::string PrintJson(cJSON* node) {
if (node == nullptr) {
return {};
}
char* rendered = cJSON_PrintUnformatted(node);
if (rendered == nullptr) {
return {};
}
std::string out(rendered);
cJSON_free(rendered);
return out;
}
esp_err_t ReadRequestBody(httpd_req_t* req, std::string& body) {
body.clear();
if (req == nullptr) {
return ESP_ERR_INVALID_ARG;
}
body.resize(static_cast<size_t>(req->content_len));
int received = 0;
while (received < req->content_len) {
const int ret = httpd_req_recv(req, body.data() + received, req->content_len - received);
if (ret <= 0) {
return ESP_FAIL;
}
received += ret;
}
return ESP_OK;
}
esp_err_t RegisterUri(httpd_handle_t server, const char* uri, httpd_method_t method,
esp_err_t (*handler)(httpd_req_t*), void* user_ctx) {
httpd_uri_t route = {};
route.uri = uri;
route.method = method;
route.handler = handler;
route.user_ctx = user_ctx;
return httpd_register_uri_handler(server, &route);
}
} // namespace
GatewayNetworkService::GatewayNetworkService(GatewayController& controller,
GatewayRuntime& runtime,
GatewayNetworkServiceConfig config)
: controller_(controller), runtime_(runtime), config_(config),
udp_lock_(xSemaphoreCreateMutex()) {}
esp_err_t GatewayNetworkService::start() {
if (started_) {
return ESP_OK;
}
esp_err_t err = ensureNetworkStack();
if (err != ESP_OK) {
return err;
}
if (config_.wifi_enabled) {
err = startWifi();
if (err != ESP_OK) {
return err;
}
}
err = configureStatusLed();
if (err != ESP_OK) {
return err;
}
controller_.addNotificationSink(
[this](const std::vector<uint8_t>& frame) { handleGatewayNotification(frame); });
controller_.addWifiStateSink([this](uint8_t mode) { handleWifiControl(mode); });
if (config_.http_enabled) {
err = startHttpServer();
if (err != ESP_OK) {
return err;
}
}
if (config_.udp_enabled) {
err = startUdpTask();
if (err != ESP_OK) {
return err;
}
}
started_ = true;
ESP_LOGI(kTag, "network service started http=%d udp=%d", config_.http_enabled,
config_.udp_enabled);
return ESP_OK;
}
esp_err_t GatewayNetworkService::ensureNetworkStack() {
esp_err_t err = esp_netif_init();
if (err != ESP_OK && err != ESP_ERR_INVALID_STATE) {
ESP_LOGE(kTag, "failed to init esp_netif: %s", esp_err_to_name(err));
return err;
}
err = esp_event_loop_create_default();
if (err != ESP_OK && err != ESP_ERR_INVALID_STATE) {
ESP_LOGE(kTag, "failed to create default event loop: %s", esp_err_to_name(err));
return err;
}
return ESP_OK;
}
esp_err_t GatewayNetworkService::startWifi() {
if (wifi_started_) {
return ESP_OK;
}
setup_ap_started_ = false;
if (wifi_sta_netif_ == nullptr) {
wifi_sta_netif_ = esp_netif_create_default_wifi_sta();
if (wifi_sta_netif_ == nullptr) {
ESP_LOGE(kTag, "failed to create default Wi-Fi STA netif");
return ESP_ERR_NO_MEM;
}
}
wifi_init_config_t wifi_init_config = WIFI_INIT_CONFIG_DEFAULT();
esp_err_t err = esp_wifi_init(&wifi_init_config);
if (err != ESP_OK && err != ESP_ERR_INVALID_STATE) {
ESP_LOGE(kTag, "failed to init Wi-Fi: %s", esp_err_to_name(err));
return err;
}
err = esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID,
&GatewayNetworkService::HandleWifiEvent, this);
if (err != ESP_OK && err != ESP_ERR_INVALID_STATE) {
ESP_LOGE(kTag, "failed to register Wi-Fi event handler: %s", esp_err_to_name(err));
return err;
}
err = esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP,
&GatewayNetworkService::HandleWifiEvent, this);
if (err != ESP_OK && err != ESP_ERR_INVALID_STATE) {
ESP_LOGE(kTag, "failed to register IP event handler: %s", esp_err_to_name(err));
return err;
}
ESP_ERROR_CHECK_WITHOUT_ABORT(esp_wifi_set_storage(WIFI_STORAGE_RAM));
err = esp_wifi_set_mode(WIFI_MODE_STA);
if (err != ESP_OK) {
ESP_LOGE(kTag, "failed to set Wi-Fi mode STA: %s", esp_err_to_name(err));
return err;
}
const auto device_info = runtime_.deviceInfo();
if (device_info.wlan.has_value() && !device_info.wlan->ssid.empty()) {
wifi_config_t wifi_config = {};
std::strncpy(reinterpret_cast<char*>(wifi_config.sta.ssid), device_info.wlan->ssid.c_str(),
sizeof(wifi_config.sta.ssid) - 1);
std::strncpy(reinterpret_cast<char*>(wifi_config.sta.password),
device_info.wlan->password.c_str(), sizeof(wifi_config.sta.password) - 1);
err = esp_wifi_set_config(WIFI_IF_STA, &wifi_config);
if (err != ESP_OK) {
ESP_LOGE(kTag, "failed to set Wi-Fi credentials: %s", esp_err_to_name(err));
return err;
}
}
err = esp_wifi_start();
if (err != ESP_OK && err != ESP_ERR_WIFI_CONN) {
ESP_LOGE(kTag, "failed to start Wi-Fi: %s", esp_err_to_name(err));
return err;
}
uint8_t mac[6] = {0};
if (device_info.wlan.has_value() && !device_info.wlan->ssid.empty() &&
esp_wifi_get_mac(WIFI_IF_STA, mac) == ESP_OK) {
WirelessInfo wireless = device_info.wlan.value_or(WirelessInfo{});
wireless.mac = MacToHex(mac);
runtime_.setWirelessInfo(std::move(wireless));
}
wifi_started_ = true;
ESP_LOGI(kTag, "Wi-Fi STA started has_credentials=%d",
device_info.wlan.has_value() && !device_info.wlan->ssid.empty());
return ESP_OK;
}
esp_err_t GatewayNetworkService::startSetupAp() {
if (wifi_ap_netif_ == nullptr) {
wifi_ap_netif_ = esp_netif_create_default_wifi_ap();
if (wifi_ap_netif_ == nullptr) {
ESP_LOGE(kTag, "failed to create default Wi-Fi AP netif");
return ESP_ERR_NO_MEM;
}
esp_netif_ip_info_t ip_info = {};
IP4_ADDR(&ip_info.ip, 192, 168, 3, 1);
IP4_ADDR(&ip_info.gw, 192, 168, 3, 1);
IP4_ADDR(&ip_info.netmask, 255, 255, 255, 0);
ESP_ERROR_CHECK_WITHOUT_ABORT(esp_netif_dhcps_stop(wifi_ap_netif_));
ESP_ERROR_CHECK_WITHOUT_ABORT(esp_netif_set_ip_info(wifi_ap_netif_, &ip_info));
ESP_ERROR_CHECK_WITHOUT_ABORT(esp_netif_dhcps_start(wifi_ap_netif_));
}
wifi_init_config_t wifi_init_config = WIFI_INIT_CONFIG_DEFAULT();
esp_err_t err = esp_wifi_init(&wifi_init_config);
if (err != ESP_OK && err != ESP_ERR_INVALID_STATE) {
ESP_LOGE(kTag, "failed to init Wi-Fi for setup AP: %s", esp_err_to_name(err));
return err;
}
if (wifi_started_) {
ESP_ERROR_CHECK_WITHOUT_ABORT(esp_wifi_disconnect());
ESP_ERROR_CHECK_WITHOUT_ABORT(esp_wifi_stop());
}
wifi_config_t ap_config = {};
std::strncpy(reinterpret_cast<char*>(ap_config.ap.ssid), kSetupApSsid,
sizeof(ap_config.ap.ssid) - 1);
ap_config.ap.ssid_len = std::strlen(kSetupApSsid);
ap_config.ap.channel = 1;
ap_config.ap.authmode = WIFI_AUTH_OPEN;
ap_config.ap.max_connection = 4;
err = esp_wifi_set_mode(WIFI_MODE_AP);
if (err != ESP_OK) {
ESP_LOGE(kTag, "failed to set Wi-Fi AP mode: %s", esp_err_to_name(err));
return err;
}
err = esp_wifi_set_config(WIFI_IF_AP, &ap_config);
if (err != ESP_OK) {
ESP_LOGE(kTag, "failed to configure setup AP: %s", esp_err_to_name(err));
return err;
}
err = esp_wifi_start();
if (err != ESP_OK && err != ESP_ERR_WIFI_CONN) {
ESP_LOGE(kTag, "failed to start setup AP: %s", esp_err_to_name(err));
return err;
}
wifi_started_ = true;
setup_ap_started_ = true;
ESP_LOGI(kTag, "setup AP started ssid=%s ip=192.168.3.1", kSetupApSsid);
return ESP_OK;
}
esp_err_t GatewayNetworkService::configureStatusLed() {
if (config_.status_led_gpio < 0) {
return ESP_OK;
}
gpio_config_t io_config = {};
io_config.pin_bit_mask = 1ULL << static_cast<uint32_t>(config_.status_led_gpio);
io_config.mode = GPIO_MODE_OUTPUT;
io_config.pull_up_en = GPIO_PULLUP_DISABLE;
io_config.pull_down_en = GPIO_PULLDOWN_DISABLE;
io_config.intr_type = GPIO_INTR_DISABLE;
const esp_err_t err = gpio_config(&io_config);
if (err != ESP_OK) {
ESP_LOGE(kTag, "failed to configure status LED GPIO%d: %s", config_.status_led_gpio,
esp_err_to_name(err));
return err;
}
setStatusLed(false);
return ESP_OK;
}
esp_err_t GatewayNetworkService::startHttpServer() {
httpd_config_t config = HTTPD_DEFAULT_CONFIG();
config.server_port = config_.http_port;
config.lru_purge_enable = true;
esp_err_t err = httpd_start(&http_server_, &config);
if (err != ESP_OK) {
ESP_LOGE(kTag, "failed to start HTTP server: %s", esp_err_to_name(err));
return err;
}
struct Route {
const char* uri;
httpd_method_t method;
esp_err_t (*handler)(httpd_req_t*);
};
const Route routes[] = {
{"/info", HTTP_GET, &GatewayNetworkService::HandleInfoGet},
{"/dali/cmd", HTTP_GET, &GatewayNetworkService::HandleCommandGet},
{"/dali/cmd", HTTP_POST, &GatewayNetworkService::HandleCommandPost},
{"/led/1", HTTP_GET, &GatewayNetworkService::HandleLedOnGet},
{"/led/0", HTTP_GET, &GatewayNetworkService::HandleLedOffGet},
{"/jq.js", HTTP_GET, &GatewayNetworkService::HandleJqJsGet},
};
for (const auto& route : routes) {
err = RegisterUri(http_server_, route.uri, route.method, route.handler, this);
if (err != ESP_OK) {
ESP_LOGE(kTag, "failed to register %s handler: %s", route.uri, esp_err_to_name(err));
return err;
}
}
return ESP_OK;
}
esp_err_t GatewayNetworkService::startUdpTask() {
if (udp_task_handle_ != nullptr) {
return ESP_OK;
}
const BaseType_t created =
xTaskCreate(&GatewayNetworkService::UdpTaskEntry, "gateway_udp",
config_.udp_task_stack_size, this, config_.udp_task_priority,
&udp_task_handle_);
if (created != pdPASS) {
udp_task_handle_ = nullptr;
ESP_LOGE(kTag, "failed to create UDP task");
return ESP_ERR_NO_MEM;
}
return ESP_OK;
}
void GatewayNetworkService::UdpTaskEntry(void* arg) {
static_cast<GatewayNetworkService*>(arg)->udpTaskLoop();
}
void GatewayNetworkService::HandleWifiEvent(void* arg, esp_event_base_t event_base,
int32_t event_id, void* event_data) {
auto* service = static_cast<GatewayNetworkService*>(arg);
if (service != nullptr) {
service->handleWifiEvent(event_base, event_id, event_data);
}
}
void GatewayNetworkService::handleWifiEvent(esp_event_base_t event_base, int32_t event_id,
void* event_data) {
if (!config_.wifi_enabled) {
return;
}
if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) {
const auto info = runtime_.deviceInfo();
if (info.wlan.has_value() && !info.wlan->ssid.empty()) {
ESP_ERROR_CHECK_WITHOUT_ABORT(esp_wifi_connect());
}
return;
}
if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) {
auto info = runtime_.deviceInfo();
if (info.wlan.has_value()) {
const bool has_credentials = !info.wlan->ssid.empty();
info.wlan->ip.clear();
runtime_.setWirelessInfo(std::move(*info.wlan));
if (has_credentials) {
ESP_ERROR_CHECK_WITHOUT_ABORT(esp_wifi_connect());
}
}
return;
}
if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP && event_data != nullptr) {
auto* event = static_cast<ip_event_got_ip_t*>(event_data);
char ip[16] = {0};
esp_ip4addr_ntoa(&event->ip_info.ip, ip, sizeof(ip));
WirelessInfo wireless = runtime_.deviceInfo().wlan.value_or(WirelessInfo{});
wireless.ip = ip;
runtime_.setWirelessInfo(std::move(wireless));
ESP_LOGI(kTag, "Wi-Fi got IP %s", ip);
}
}
void GatewayNetworkService::udpTaskLoop() {
udp_socket_ = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
if (udp_socket_ < 0) {
ESP_LOGE(kTag, "failed to create UDP socket");
udp_task_handle_ = nullptr;
vTaskDelete(nullptr);
return;
}
sockaddr_in local_addr = {};
local_addr.sin_family = AF_INET;
local_addr.sin_port = htons(config_.udp_port);
local_addr.sin_addr.s_addr = htonl(INADDR_ANY);
if (bind(udp_socket_, reinterpret_cast<sockaddr*>(&local_addr), sizeof(local_addr)) != 0) {
ESP_LOGE(kTag, "failed to bind UDP socket on port %u", config_.udp_port);
close(udp_socket_);
udp_socket_ = -1;
udp_task_handle_ = nullptr;
vTaskDelete(nullptr);
return;
}
ESP_LOGI(kTag, "UDP router listening on port %u", config_.udp_port);
while (true) {
uint8_t buffer[kUdpBufferSize] = {0};
sockaddr_storage remote_addr = {};
socklen_t remote_addr_len = sizeof(remote_addr);
const int read_len = recvfrom(udp_socket_, buffer, sizeof(buffer), 0,
reinterpret_cast<sockaddr*>(&remote_addr),
&remote_addr_len);
if (read_len <= 0) {
continue;
}
{
LockGuard guard(udp_lock_);
udp_remote_addr_ = remote_addr;
udp_remote_addr_len_ = remote_addr_len;
has_udp_remote_ = true;
}
controller_.enqueueCommandFrame(std::vector<uint8_t>(buffer, buffer + read_len));
}
}
void GatewayNetworkService::handleGatewayNotification(const std::vector<uint8_t>& frame) {
if (!config_.udp_enabled || udp_socket_ < 0 || frame.empty()) {
return;
}
sockaddr_storage remote_addr = {};
socklen_t remote_addr_len = 0;
{
LockGuard guard(udp_lock_);
if (!has_udp_remote_) {
return;
}
remote_addr = udp_remote_addr_;
remote_addr_len = udp_remote_addr_len_;
}
sendto(udp_socket_, frame.data(), frame.size(), 0,
reinterpret_cast<const sockaddr*>(&remote_addr), remote_addr_len);
}
void GatewayNetworkService::handleWifiControl(uint8_t mode) {
if (mode == 0) {
config_.wifi_enabled = false;
if (wifi_started_) {
ESP_ERROR_CHECK_WITHOUT_ABORT(esp_wifi_disconnect());
ESP_ERROR_CHECK_WITHOUT_ABORT(esp_wifi_stop());
wifi_started_ = false;
setup_ap_started_ = false;
}
auto info = runtime_.deviceInfo();
if (info.wlan.has_value()) {
info.wlan->ip.clear();
runtime_.setWirelessInfo(std::move(*info.wlan));
}
return;
}
if (mode == 101) {
config_.wifi_enabled = true;
ESP_ERROR_CHECK_WITHOUT_ABORT(startSetupAp());
return;
}
if (mode == 1) {
config_.wifi_enabled = true;
if (setup_ap_started_) {
ESP_ERROR_CHECK_WITHOUT_ABORT(esp_wifi_stop());
wifi_started_ = false;
setup_ap_started_ = false;
}
ESP_ERROR_CHECK_WITHOUT_ABORT(startWifi());
}
}
std::string GatewayNetworkService::deviceInfoJson() const {
const auto info = runtime_.deviceInfo();
cJSON* root = cJSON_CreateObject();
if (root == nullptr) {
return {};
}
cJSON_AddStringToObject(root, "serialId", info.serial_id.c_str());
cJSON_AddStringToObject(root, "type", info.type.c_str());
cJSON_AddStringToObject(root, "project", info.project.c_str());
cJSON_AddStringToObject(root, "version", info.version.c_str());
cJSON* gateway_info = cJSON_CreateObject();
if (gateway_info != nullptr) {
cJSON_AddNumberToObject(gateway_info, "count",
static_cast<double>(info.dali_gateway_count));
cJSON_AddItemToObject(root, "daliGatewayInfo", gateway_info);
}
if (info.wlan.has_value()) {
cJSON* wlan = cJSON_CreateObject();
if (wlan != nullptr) {
cJSON_AddStringToObject(wlan, "mac", info.wlan->mac.c_str());
cJSON_AddStringToObject(wlan, "IP", info.wlan->ip.c_str());
cJSON_AddStringToObject(wlan, "ssid", info.wlan->ssid.c_str());
cJSON_AddStringToObject(wlan, "passwd", info.wlan->password.c_str());
cJSON_AddItemToObject(root, "wlanInfo", wlan);
}
}
const std::string rendered = PrintJson(root);
cJSON_Delete(root);
return rendered;
}
std::string GatewayNetworkService::deviceInfoDoubleEncodedJson() const {
const std::string inner = deviceInfoJson();
cJSON* outer = cJSON_CreateString(inner.c_str());
if (outer == nullptr) {
return {};
}
const std::string rendered = PrintJson(outer);
cJSON_Delete(outer);
return rendered;
}
std::string GatewayNetworkService::gatewaySnapshotJson() {
const auto snapshot = controller_.snapshot();
cJSON* root = cJSON_CreateObject();
if (root == nullptr) {
return {};
}
cJSON_AddNumberToObject(root, "count", static_cast<double>(snapshot.channels.size()));
cJSON* gw_info = cJSON_CreateObject();
if (gw_info != nullptr) {
cJSON_AddBoolToObject(gw_info, "setupMode", snapshot.setup_mode);
cJSON_AddBoolToObject(gw_info, "bleEnabled", snapshot.ble_enabled);
cJSON_AddBoolToObject(gw_info, "wifiEnabled", snapshot.wifi_enabled);
cJSON_AddBoolToObject(gw_info, "IPRouter", snapshot.ip_router_enabled);
cJSON_AddBoolToObject(gw_info, "iSceneEnabled", snapshot.internal_scene_supported);
cJSON_AddBoolToObject(gw_info, "iGroupEnabled", snapshot.internal_group_supported);
cJSON_AddItemToObject(root, "gwInfo", gw_info);
}
cJSON* channels = cJSON_CreateArray();
if (channels != nullptr) {
for (const auto& channel : snapshot.channels) {
cJSON* item = cJSON_CreateObject();
if (item == nullptr) {
continue;
}
cJSON_AddNumberToObject(item, "channel", channel.channel_index + 1);
cJSON_AddNumberToObject(item, "gw", channel.gateway_id);
cJSON_AddStringToObject(item, "name", channel.name.c_str());
cJSON_AddStringToObject(item, "phy", channel.phy.c_str());
cJSON_AddNumberToObject(item, "sceneMaskLow", channel.scene_mask_low);
cJSON_AddNumberToObject(item, "sceneMaskHigh", channel.scene_mask_high);
cJSON_AddNumberToObject(item, "groupMaskLow", channel.group_mask_low);
cJSON_AddNumberToObject(item, "groupMaskHigh", channel.group_mask_high);
cJSON_AddBoolToObject(item, "isAllocAddr", channel.allocating);
cJSON_AddNumberToObject(item, "lastAllocAddr", channel.last_alloc_addr);
cJSON_AddItemToArray(channels, item);
}
cJSON_AddItemToObject(root, "channels", channels);
}
const std::string rendered = PrintJson(root);
cJSON_Delete(root);
return rendered;
}
void GatewayNetworkService::setStatusLed(bool on) {
if (config_.status_led_gpio < 0) {
return;
}
const bool level = config_.status_led_active_high ? on : !on;
gpio_set_level(static_cast<gpio_num_t>(config_.status_led_gpio), level ? 1 : 0);
}
esp_err_t GatewayNetworkService::HandleInfoGet(httpd_req_t* req) {
auto* service = static_cast<GatewayNetworkService*>(req->user_ctx);
if (service == nullptr) {
return ESP_FAIL;
}
const std::string payload = service->deviceInfoDoubleEncodedJson();
httpd_resp_set_type(req, "application/json");
return httpd_resp_send(req, payload.data(), payload.size());
}
esp_err_t GatewayNetworkService::HandleCommandGet(httpd_req_t* req) {
auto* service = static_cast<GatewayNetworkService*>(req->user_ctx);
if (service == nullptr) {
return ESP_FAIL;
}
const std::string payload = service->gatewaySnapshotJson();
httpd_resp_set_type(req, "application/json");
return httpd_resp_send(req, payload.data(), payload.size());
}
esp_err_t GatewayNetworkService::HandleCommandPost(httpd_req_t* req) {
auto* service = static_cast<GatewayNetworkService*>(req->user_ctx);
if (service == nullptr) {
return ESP_FAIL;
}
std::string body;
if (ReadRequestBody(req, body) != ESP_OK) {
return httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Bad Request");
}
cJSON* root = cJSON_ParseWithLength(body.c_str(), body.size());
if (root == nullptr) {
return httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Bad Request: json decode error");
}
const cJSON* command = cJSON_GetObjectItemCaseSensitive(root, "command");
if (!cJSON_IsString(command) || command->valuestring == nullptr) {
cJSON_Delete(root);
return httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Bad Request: missing command");
}
const auto frame = DecodeHex(command->valuestring);
cJSON_Delete(root);
if (frame.empty()) {
return httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Bad Request: invalid command hex");
}
service->controller_.enqueueCommandFrame(frame);
httpd_resp_set_type(req, "text/plain");
return httpd_resp_sendstr(req, "ok");
}
esp_err_t GatewayNetworkService::HandleLedOnGet(httpd_req_t* req) {
auto* service = static_cast<GatewayNetworkService*>(req->user_ctx);
if (service == nullptr) {
return ESP_FAIL;
}
service->setStatusLed(true);
httpd_resp_set_type(req, "text/plain");
return httpd_resp_sendstr(req, "ok");
}
esp_err_t GatewayNetworkService::HandleLedOffGet(httpd_req_t* req) {
auto* service = static_cast<GatewayNetworkService*>(req->user_ctx);
if (service == nullptr) {
return ESP_FAIL;
}
service->setStatusLed(false);
httpd_resp_set_type(req, "text/plain");
return httpd_resp_sendstr(req, "ok");
}
esp_err_t GatewayNetworkService::HandleJqJsGet(httpd_req_t* req) {
FILE* file = std::fopen("/jq.js", "rb");
if (file == nullptr) {
return httpd_resp_send_err(req, HTTPD_404_NOT_FOUND, "Not Found/jq.js");
}
httpd_resp_set_type(req, "application/javascript");
char buffer[512] = {0};
while (true) {
const size_t read_len = std::fread(buffer, 1, sizeof(buffer), file);
if (read_len > 0 && httpd_resp_send_chunk(req, buffer, read_len) != ESP_OK) {
std::fclose(file);
return ESP_FAIL;
}
if (read_len < sizeof(buffer)) {
break;
}
}
std::fclose(file);
return httpd_resp_send_chunk(req, nullptr, 0);
}
} // namespace gateway