feat: implement Web UI dashboard, Wi-Fi AP/STA dual mode, Captive Portal, mDNS, and RESTful APIs
This commit is contained in:
1 parent
61fc819a18
commit
caf5da201b
8 files changed
+739
-16
No files matched your search
@@ -0,0 +1,132 @@
|
||||
#include "web_server.h"
|
||||
#include "web_ui.h"
|
||||
#include "log/app_log.h"
|
||||
#include "wifi/wifi_manager.h"
|
||||
#include "version.h"
|
||||
#include "ble/ble_remote_client.h"
|
||||
#include "audio/audio_pipeline.h"
|
||||
#include "keymap/key_state_machine.h"
|
||||
#include <WebServer.h>
|
||||
#include <ArduinoJson.h>
|
||||
|
||||
static WebServer s_server(80);
|
||||
extern key_mapper_engine_t g_key_engine;
|
||||
|
||||
static void handle_root() {
|
||||
s_server.sendHeader("Connection", "close");
|
||||
s_server.send(200, "text/html; charset=utf-8", INDEX_HTML);
|
||||
}
|
||||
|
||||
static void handle_status() {
|
||||
JsonDocument doc;
|
||||
doc["firmware"] = FIRMWARE_NAME;
|
||||
doc["version"] = FIRMWARE_VERSION;
|
||||
doc["uptime_sec"] = millis() / 1000;
|
||||
doc["ble_state"] = (int)ble_remote_get_state();
|
||||
doc["frames_decoded"] = g_audio_pipeline.total_frames_decoded;
|
||||
doc["samples_pushed"] = g_audio_pipeline.total_samples_pushed;
|
||||
doc["free_heap"] = ESP.getFreeHeap();
|
||||
doc["free_psram"] = ESP.getFreePsram();
|
||||
doc["ap_ip"] = wifi_manager_get_ap_ip();
|
||||
doc["sta_ip"] = wifi_manager_get_sta_ip();
|
||||
doc["sta_connected"] = wifi_manager_is_sta_connected();
|
||||
|
||||
String out;
|
||||
serializeJson(doc, out);
|
||||
s_server.send(200, "application/json", out);
|
||||
}
|
||||
|
||||
static void handle_logs() {
|
||||
String json = app_log_get_json();
|
||||
s_server.send(200, "application/json", json);
|
||||
}
|
||||
|
||||
static void handle_logs_clear() {
|
||||
app_log_clear();
|
||||
s_server.send(200, "application/json", "{\"status\":\"cleared\"}");
|
||||
}
|
||||
|
||||
static void handle_wifi_scan() {
|
||||
String json = wifi_manager_scan_json();
|
||||
s_server.send(200, "application/json", json);
|
||||
}
|
||||
|
||||
static void handle_wifi_config() {
|
||||
if (!s_server.hasArg("plain")) {
|
||||
s_server.send(400, "application/json", "{\"error\":\"missing_body\"}");
|
||||
return;
|
||||
}
|
||||
JsonDocument doc;
|
||||
DeserializationError err = deserializeJson(doc, s_server.arg("plain"));
|
||||
if (err) {
|
||||
s_server.send(400, "application/json", "{\"error\":\"invalid_json\"}");
|
||||
return;
|
||||
}
|
||||
|
||||
String ssid = doc["ssid"] | "";
|
||||
String pass = doc["pass"] | "";
|
||||
|
||||
if (ssid.length() == 0) {
|
||||
s_server.send(400, "application/json", "{\"error\":\"empty_ssid\"}");
|
||||
return;
|
||||
}
|
||||
|
||||
wifi_manager_save_sta_config(ssid, pass);
|
||||
s_server.send(200, "application/json", "{\"status\":\"ok\"}");
|
||||
}
|
||||
|
||||
static void handle_keymap_reset() {
|
||||
key_engine_load_defaults(&g_key_engine);
|
||||
app_log("KEYMAP", "Reset keymap to factory defaults via Web API");
|
||||
s_server.send(200, "application/json", "{\"status\":\"reset_ok\"}");
|
||||
}
|
||||
|
||||
static void handle_ble_reconnect() {
|
||||
app_log("BLE", "Triggered manual reconnect scan via Web API");
|
||||
ble_remote_trigger_reconnect();
|
||||
s_server.send(200, "application/json", "{\"status\":\"reconnecting\"}");
|
||||
}
|
||||
|
||||
static void handle_system_restart() {
|
||||
app_log("SYSTEM", "Rebooting ESP32 via Web API...");
|
||||
s_server.send(200, "application/json", "{\"status\":\"rebooting\"}");
|
||||
delay(500);
|
||||
ESP.restart();
|
||||
}
|
||||
|
||||
static void handle_captive_portal() {
|
||||
String host = s_server.hostHeader();
|
||||
if (host != "192.168.4.1" && host != "remotemapper.local") {
|
||||
s_server.sendHeader("Location", "http://192.168.4.1/", true);
|
||||
s_server.send(302, "text/plain", "");
|
||||
return;
|
||||
}
|
||||
handle_root();
|
||||
}
|
||||
|
||||
void web_server_init(void) {
|
||||
// API Routes
|
||||
s_server.on("/", HTTP_GET, handle_root);
|
||||
s_server.on("/api/status", HTTP_GET, handle_status);
|
||||
s_server.on("/api/logs", HTTP_GET, handle_logs);
|
||||
s_server.on("/api/logs/clear", HTTP_POST, handle_logs_clear);
|
||||
s_server.on("/api/wifi/scan", HTTP_GET, handle_wifi_scan);
|
||||
s_server.on("/api/wifi/config", HTTP_POST, handle_wifi_config);
|
||||
s_server.on("/api/keymap/reset", HTTP_POST, handle_keymap_reset);
|
||||
s_server.on("/api/ble/reconnect", HTTP_POST, handle_ble_reconnect);
|
||||
s_server.on("/api/system/restart", HTTP_POST, handle_system_restart);
|
||||
|
||||
// Captive Portal probe redirects
|
||||
s_server.on("/generate_204", HTTP_GET, handle_captive_portal);
|
||||
s_server.on("/hotspot-detect.html", HTTP_GET, handle_captive_portal);
|
||||
s_server.on("/canonical.html", HTTP_GET, handle_captive_portal);
|
||||
s_server.on("/connecttest.txt", HTTP_GET, handle_captive_portal);
|
||||
s_server.onNotFound(handle_captive_portal);
|
||||
|
||||
s_server.begin();
|
||||
app_log("WEB", "HTTP Web Server started on port 80");
|
||||
}
|
||||
|
||||
void web_server_task(void) {
|
||||
s_server.handleClient();
|
||||
}
|
||||
Reference in new issue
Block a user