#include "wled.h" /* * Integrated HTTP web server page declarations */ //Is this an IP? bool isIp(String str) { for (size_t i = 0; i < str.length(); i++) { int c = str.charAt(i); if (c != '.' && (c < '0' || c > '9')) { return false; } } return true; } bool captivePortal(AsyncWebServerRequest *request) { if (ON_STA_FILTER(request)) return false; //only serve captive in AP mode String hostH; if (!request->hasHeader("Host")) return false; hostH = request->getHeader("Host")->value(); if (!isIp(hostH) && hostH.indexOf("wled.me") < 0 && hostH.indexOf(cmDNS) < 0) { DEBUG_PRINTLN("Captive portal"); AsyncWebServerResponse *response = request->beginResponse(302); response->addHeader("Location", "http://4.3.2.1"); request->send(response); return true; } return false; } void initServer() { //CORS compatiblity DefaultHeaders::Instance().addHeader("Access-Control-Allow-Origin", "*"); DefaultHeaders::Instance().addHeader("Access-Control-Allow-Methods", "*"); DefaultHeaders::Instance().addHeader("Access-Control-Allow-Headers", "*"); server.on("/liveview", HTTP_GET, [](AsyncWebServerRequest *request){ request->send_P(200, "text/html", PAGE_liveview); }); //settings page server.on("/settings", HTTP_GET, [](AsyncWebServerRequest *request){ serveSettings(request); }); server.on("/favicon.ico", HTTP_GET, [](AsyncWebServerRequest *request){ if(!handleFileRead(request, "/favicon.ico")) { request->send_P(200, "image/x-icon", favicon, 156); } }); server.on("/sliders", HTTP_GET, [](AsyncWebServerRequest *request){ serveIndex(request); }); server.on("/welcome", HTTP_GET, [](AsyncWebServerRequest *request){ serveSettings(request); }); server.on("/reset", HTTP_GET, [](AsyncWebServerRequest *request){ serveMessage(request, 200,"Rebooting now...",F("Please wait ~10 seconds..."),129); doReboot = true; }); server.on("/settings/wifi", HTTP_POST, [](AsyncWebServerRequest *request){ if (!(wifiLock && otaLock)) handleSettingsSet(request, 1); serveMessage(request, 200,F("WiFi settings saved."),F("Please connect to the new IP (if changed)"),129); forceReconnect = true; }); server.on("/settings/leds", HTTP_POST, [](AsyncWebServerRequest *request){ handleSettingsSet(request, 2); serveMessage(request, 200,F("LED settings saved."),"Redirecting...",1); }); server.on("/settings/ui", HTTP_POST, [](AsyncWebServerRequest *request){ handleSettingsSet(request, 3); serveMessage(request, 200,F("UI settings saved."),"Redirecting...",1); }); server.on("/settings/dmx", HTTP_POST, [](AsyncWebServerRequest *request){ handleSettingsSet(request, 7); serveMessage(request, 200,F("UI settings saved."),"Redirecting...",1); }); server.on("/settings/sync", HTTP_POST, [](AsyncWebServerRequest *request){ handleSettingsSet(request, 4); serveMessage(request, 200,F("Sync settings saved."),"Redirecting...",1); }); server.on("/settings/time", HTTP_POST, [](AsyncWebServerRequest *request){ handleSettingsSet(request, 5); serveMessage(request, 200,F("Time settings saved."),"Redirecting...",1); }); server.on("/settings/sec", HTTP_POST, [](AsyncWebServerRequest *request){ handleSettingsSet(request, 6); if (!doReboot) serveMessage(request, 200,F("Security settings saved."),F("Rebooting, please wait ~10 seconds..."),129); doReboot = true; }); server.on("/json", HTTP_GET, [](AsyncWebServerRequest *request){ serveJson(request); }); AsyncCallbackJsonWebHandler* handler = new AsyncCallbackJsonWebHandler("/json", [](AsyncWebServerRequest *request) { bool verboseResponse = false; { //scope JsonDocument so it releases its buffer DynamicJsonDocument jsonBuffer(8192); DeserializationError error = deserializeJson(jsonBuffer, (uint8_t*)(request->_tempObject)); JsonObject root = jsonBuffer.as(); if (error || root.isNull()) { request->send(400, "application/json", "{\"error\":10}"); return; } verboseResponse = deserializeState(root); } if (verboseResponse) { //if JSON contains "v" serveJson(request); return; } request->send(200, "application/json", "{\"success\":true}"); }); server.addHandler(handler); //*******DEPRECATED******* server.on("/version", HTTP_GET, [](AsyncWebServerRequest *request){ request->send(200, "text/plain", (String)VERSION); }); server.on("/uptime", HTTP_GET, [](AsyncWebServerRequest *request){ request->send(200, "text/plain", (String)millis()); }); server.on("/freeheap", HTTP_GET, [](AsyncWebServerRequest *request){ request->send(200, "text/plain", (String)ESP.getFreeHeap()); }); //*******END*******/ server.on("/u", HTTP_GET, [](AsyncWebServerRequest *request){ request->send_P(200, "text/html", PAGE_usermod); }); server.on("/url", HTTP_GET, [](AsyncWebServerRequest *request){ URL_response(request); }); server.on("/teapot", HTTP_GET, [](AsyncWebServerRequest *request){ serveMessage(request, 418, F("418. I'm a teapot."), F("(Tangible Embedded Advanced Project Of Twinkling)"), 254); }); //if OTA is allowed if (!otaLock){ #if !defined WLED_DISABLE_FILESYSTEM && defined WLED_ENABLE_FS_EDITOR #ifdef ARDUINO_ARCH_ESP32 server.addHandler(new SPIFFSEditor(SPIFFS));//http_username,http_password)); #else server.addHandler(new SPIFFSEditor());//http_username,http_password)); #endif #else server.on("/edit", HTTP_GET, [](AsyncWebServerRequest *request){ serveMessage(request, 501, "Not implemented", F("The SPIFFS editor is disabled in this build."), 254); }); #endif //init ota page #ifndef WLED_DISABLE_OTA server.on("/update", HTTP_GET, [](AsyncWebServerRequest *request){ request->send_P(200, "text/html", PAGE_update); }); server.on("/update", HTTP_POST, [](AsyncWebServerRequest *request){ if (Update.hasError()) { serveMessage(request, 500, F("Failed updating firmware!"), F("Please check your file and retry!"), 254); return; } serveMessage(request, 200, F("Successfully updated firmware!"), F("Please wait while the module reboots..."), 131); doReboot = true; },[](AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final){ if(!index){ DEBUG_PRINTLN("OTA Update Start"); #ifdef ESP8266 Update.runAsync(true); #endif Update.begin((ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000); } if(!Update.hasError()) Update.write(data, len); if(final){ if(Update.end(true)){ DEBUG_PRINTLN("Update Success"); } else { DEBUG_PRINTLN("Update Failed"); } } }); #else server.on("/update", HTTP_GET, [](AsyncWebServerRequest *request){ serveMessage(request, 501, "Not implemented", F("OTA updates are disabled in this build."), 254); }); #endif } else { server.on("/edit", HTTP_GET, [](AsyncWebServerRequest *request){ serveMessage(request, 500, "Access Denied", "Please unlock OTA in security settings!", 254); }); server.on("/update", HTTP_GET, [](AsyncWebServerRequest *request){ serveMessage(request, 500, "Access Denied", "Please unlock OTA in security settings!", 254); }); } #ifdef WLED_ENABLE_DMX server.on("/dmxmap", HTTP_GET, [](AsyncWebServerRequest *request){ request->send_P(200, "text/html", PAGE_dmxmap , dmxProcessor); }); #else server.on("/dmxmap", HTTP_GET, [](AsyncWebServerRequest *request){ serveMessage(request, 501, "Not implemented", F("DMX support is not enabled in this build."), 254); }); #endif server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){ if (captivePortal(request)) return; serveIndexOrWelcome(request); }); //called when the url is not defined here, ajax-in; get-settings server.onNotFound([](AsyncWebServerRequest *request){ DEBUG_PRINTLN("Not-Found HTTP call:"); DEBUG_PRINTLN("URI: " + request->url()); if (captivePortal(request)) return; //make API CORS compatible if (request->method() == HTTP_OPTIONS) { request->send(200); return; } if(handleSet(request, request->url())) return; #ifndef WLED_DISABLE_ALEXA if(espalexa.handleAlexaApiCall(request)) return; #endif #ifdef WLED_ENABLE_FS_SERVING if(handleFileRead(request, request->url())) return; #endif request->send(404, "text/plain", "Not Found"); }); } void serveIndexOrWelcome(AsyncWebServerRequest *request) { if (!showWelcomePage){ serveIndex(request); } else { serveSettings(request); } } void serveIndex(AsyncWebServerRequest* request) { #ifdef WLED_ENABLE_FS_SERVING if (handleFileRead(request, "/index.htm")) return; #endif AsyncWebServerResponse *response = request->beginResponse_P(200, "text/html", PAGE_index, PAGE_index_L); response->addHeader("Content-Encoding","gzip"); request->send(response); } String msgProcessor(const String& var) { if (var == "MSG") { String messageBody = messageHead; messageBody += ""; messageBody += messageSub; uint32_t optt = optionType; if (optt < 60) //redirect to settings after optionType seconds { messageBody += ""; } else if (optt < 120) //redirect back after optionType-60 seconds, unused { //messageBody += ""; } else if (optt < 180) //reload parent after optionType-120 seconds { messageBody += ""; } else if (optt == 253) { messageBody += F("

"); //button to settings } else if (optt == 254) { messageBody += F("

"); } return messageBody; } return String(); } void serveMessage(AsyncWebServerRequest* request, uint16_t code, String headl, String subl, byte optionT) { messageHead = headl; messageSub = subl; optionType = optionT; request->send_P(code, "text/html", PAGE_msg, msgProcessor); } String settingsProcessor(const String& var) { if (var == "CSS") { char buf[2048]; getSettingsJS(optionType, buf); return String(buf); } #ifdef WLED_ENABLE_DMX if (var == "DMXMENU") { return String(F("
")); } #endif if (var == "SCSS") return String(FPSTR(PAGE_settingsCss)); return String(); } String dmxProcessor(const String& var) { String mapJS; #ifdef WLED_ENABLE_DMX if (var == "DMXVARS") { mapJS += "\nCN=" + String(DMXChannels) + ";\n"; mapJS += "CS=" + String(DMXStart) + ";\n"; mapJS += "CG=" + String(DMXGap) + ";\n"; mapJS += "LC=" + String(ledCount) + ";\n"; mapJS += "var CH=["; for (int i=0;i<15;i++) { mapJS += String(DMXFixtureMap[i]) + ","; } mapJS += "0];"; } #endif return mapJS; } void serveSettings(AsyncWebServerRequest* request) { byte subPage = 0; const String& url = request->url(); if (url.indexOf("sett") >= 0) { if (url.indexOf("wifi") > 0) subPage = 1; else if (url.indexOf("leds") > 0) subPage = 2; else if (url.indexOf("ui") > 0) subPage = 3; else if (url.indexOf("sync") > 0) subPage = 4; else if (url.indexOf("time") > 0) subPage = 5; else if (url.indexOf("sec") > 0) subPage = 6; #ifdef WLED_ENABLE_DMX // include only if DMX is enabled else if (url.indexOf("dmx") > 0) subPage = 7; #endif } else subPage = 255; //welcome page if (subPage == 1 && wifiLock && otaLock) { serveMessage(request, 500, "Access Denied", "Please unlock OTA in security settings!", 254); return; } #ifdef WLED_DISABLE_MOBILE_UI //disable welcome page if not enough storage if (subPage == 255) {serveIndex(request); return;} #endif optionType = subPage; switch (subPage) { case 1: request->send_P(200, "text/html", PAGE_settings_wifi, settingsProcessor); break; case 2: request->send_P(200, "text/html", PAGE_settings_leds, settingsProcessor); break; case 3: request->send_P(200, "text/html", PAGE_settings_ui , settingsProcessor); break; case 4: request->send_P(200, "text/html", PAGE_settings_sync, settingsProcessor); break; case 5: request->send_P(200, "text/html", PAGE_settings_time, settingsProcessor); break; case 6: request->send_P(200, "text/html", PAGE_settings_sec , settingsProcessor); break; case 7: request->send_P(200, "text/html", PAGE_settings_dmx , settingsProcessor); break; case 255: request->send_P(200, "text/html", PAGE_welcome); break; default: request->send_P(200, "text/html", PAGE_settings , settingsProcessor); } }