/* Copyright (c) 2017 Theo Arends. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - 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. 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. */ #ifdef USE_WEBSERVER /*********************************************************************************************\ * Web server and WiFi Manager * * Enables configuration and reconfiguration of WiFi credentials using a Captive Portal * Source by AlexT (https://github.com/tzapu) \*********************************************************************************************/ #define STR_HELPER(x) #x #define STR(x) STR_HELPER(x) const char HTTP_HEAD[] PROGMEM = "" "" "" "" "{v}" "" "" "" "" "
" "

{ha} Module

{h}

"; const char HTTP_SCRIPT_CONSOL[] PROGMEM = "var sn=0;" // Scroll position "var id=99;" // Get most of weblog initially "function l(p){" // Console log and command service "var c,o,t;" "clearTimeout(lt);" "o='';" "t=document.getElementById('t1');" "if(p==1){" "c=document.getElementById('c1');" "o='&c1='+c.value;" "c.value='';" "t.scrollTop=sn;" "}" "if(t.scrollTop>=sn){" // User scrolled back so no updates "if(x!=null){x.abort();}" // Abort if no response within 2 seconds (happens on restart 1) "x=new XMLHttpRequest();" "x.onreadystatechange=function(){" "if(x.readyState==4&&x.status==200){" "var z,d;" "d=x.responseXML;" "id=d.getElementsByTagName('i')[0].childNodes[0].nodeValue;" "if(d.getElementsByTagName('j')[0].childNodes[0].nodeValue==0){t.value='';}" "z=d.getElementsByTagName('l')[0].childNodes;" "if(z.length>0){t.value+=z[0].nodeValue;}" "t.scrollTop=99999;" "sn=t.scrollTop;" "}" "};" "x.open('GET','ax?c2='+id+o,true);" "x.send();" "}" "lt=setTimeout(l,2345);" "return false;" "}" ""; const char HTTP_LNK_STYLE[] PROGMEM = ".q{float:right;width:64px;text-align:right;}" ".l{background:url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAALVBMVEX///8EBwfBwsLw8PAzNjaCg4NTVVUjJiZDRUUUFxdiZGSho6O" "Sk5Pg4eFydHTCjaf3AAAAZElEQVQ4je2NSw7AIAhEBamKn97/uMXEGBvozkWb9C2Zx4xzWykBhFAeYp9gkLyZE0zIMno9n4g19hmdY39scwqVkOXaxph0ZCXQcqxSpgQpONa59wkRDOL93eA" "XvimwlbPbwwVAegLS1HGfZAAAAABJRU5ErkJggg==') no-repeat left center;background-size:1em;}" ""; const char HTTP_MSG_RSTRT[] PROGMEM = "
Device will restart in a few seconds

"; const char HTTP_BTN_MENU1[] PROGMEM = "
" "
" "
" "
"; const char HTTP_BTN_RSTRT[] PROGMEM = "
"; const char HTTP_BTN_MENU2[] PROGMEM = "
" "
"; const char HTTP_BTN_MENU3[] PROGMEM = "
" #ifdef USE_DOMOTICZ "
" #endif // USE_DOMOTICZ ""; const char HTTP_BTN_MENU4[] PROGMEM = "
" "
" "
" "
" "
"; const char HTTP_BTN_MAIN[] PROGMEM = "

"; const char HTTP_BTN_CONF[] PROGMEM = "

"; const char HTTP_FORM_MODULE[] PROGMEM = "
 Module parameters 
" "" "
Module type ({mt})
" "
AP1 SSId (" STA_SSID1 ")

" "
AP1 Password

" "
AP2 SSId (" STA_SSID2 ")

" "
AP2 Password

" "
Hostname (" WIFI_HOSTNAME ")

"; const char HTTP_FORM_MQTT[] PROGMEM = "
 MQTT parameters " "" "
Host (" MQTT_HOST ")

" "
Port (" STR(MQTT_PORT) ")

" "
Client Id ({m0})

" "
User (" MQTT_USER ")

" "
Password

" "
Topic (" MQTT_TOPIC ")

"; const char HTTP_FORM_LOG1[] PROGMEM = "
 Logging parameters " ""; const char HTTP_FORM_LOG2[] PROGMEM = "
{b0}log level ({b1})

"; const char HTTP_FORM_LOG3[] PROGMEM = "
Syslog host (" SYS_LOG_HOST ")

" "
Syslog port (" STR(SYS_LOG_PORT) ")

" "
Telemetric period (" STR(TELE_PERIOD) ")

"; const char HTTP_FORM_OTHER[] PROGMEM = "
 Other parameters " "" "
Web Admin Password

" "
MQTT enable
"; const char HTTP_FORM_OTHER2[] PROGMEM = "
Friendly Name {1 ({2)

"; #ifdef USE_EMULATION const char HTTP_FORM_OTHER3[] PROGMEM = "
 Emulation " "
None" "
Belkin WeMo" "
Hue Bridge
"; #endif // USE_EMULATION const char HTTP_FORM_END[] PROGMEM = "
"; const char HTTP_FORM_RST[] PROGMEM = "
" "
 Restore configuration " "
" "

" "
" "
" "
" ""; const char HTTP_FORM_UPG[] PROGMEM = "
" "
 Upgrade by web server " "
" "
OTA Url

" "
" "


" "
 Upgrade by file upload " "
" "

" // "
" "
" "
" "
" ""; const char HTTP_FORM_CMND[] PROGMEM = "


" "
" "
" // "
" "
"; const char HTTP_COUNTER[] PROGMEM = "
"; const char HTTP_SNS_TEMP[] PROGMEM = "%s Temperature%s°%c"; const char HTTP_SNS_HUM[] PROGMEM = "%s Humidity%s%"; const char HTTP_SNS_PRESSURE[] PROGMEM = "%s Pressure%s hPa"; const char HTTP_END[] PROGMEM = "" "" ""; #define DNS_PORT 53 enum http_t {HTTP_OFF, HTTP_USER, HTTP_ADMIN, HTTP_MANAGER}; DNSServer *dnsServer; ESP8266WebServer *webServer; boolean _removeDuplicateAPs = true; int _minimumQuality = -1; uint8_t _httpflag = HTTP_OFF, _uploaderror = 0, _uploadfiletype, _colcount; void startWebserver(int type, IPAddress ipweb) { char log[LOGSZ]; if (!_httpflag) { if (!webServer) { webServer = new ESP8266WebServer(80); webServer->on("/", handleRoot); webServer->on("/cn", handleConfig); webServer->on("/md", handleModule); webServer->on("/w1", handleWifi1); webServer->on("/w0", handleWifi0); if (sysCfg.mqtt_enabled) { webServer->on("/mq", handleMqtt); #ifdef USE_DOMOTICZ webServer->on("/dm", handleDomoticz); #endif // USE_DOMOTICZ } webServer->on("/lg", handleLog); webServer->on("/co", handleOther); webServer->on("/dl", handleDownload); webServer->on("/sv", handleSave); webServer->on("/rs", handleRestore); webServer->on("/rt", handleReset); webServer->on("/up", handleUpgrade); webServer->on("/u1", handleUpgradeStart); // OTA webServer->on("/u2", HTTP_POST, handleUploadDone, handleUploadLoop); webServer->on("/cm", handleCmnd); webServer->on("/cs", handleConsole); webServer->on("/ax", handleAjax); webServer->on("/ay", handleAjax2); webServer->on("/in", handleInfo); webServer->on("/rb", handleRestart); webServer->on("/fwlink", handleRoot); // Microsoft captive portal. Maybe not needed. Might be handled by notFound handler. #ifdef USE_EMULATION if (sysCfg.emulation == EMUL_WEMO) { webServer->on("/upnp/control/basicevent1", HTTP_POST, handleUPnPevent); webServer->on("/eventservice.xml", handleUPnPservice); webServer->on("/setup.xml", handleUPnPsetupWemo); } if (sysCfg.emulation == EMUL_HUE) { webServer->on("/description.xml", handleUPnPsetupHue); } #endif // USE_EMULATION webServer->onNotFound(handleNotFound); } logajaxflg = 0; webServer->begin(); // Web server start } if (_httpflag != type) { snprintf_P(log, sizeof(log), PSTR("HTTP: Webserver active on %s%s with IP address %s"), Hostname, (mDNSbegun)?".local":"", ipweb.toString().c_str()); addLog(LOG_LEVEL_INFO, log); } if (type) _httpflag = type; } void stopWebserver() { if (_httpflag) { webServer->close(); _httpflag = HTTP_OFF; addLog_P(LOG_LEVEL_INFO, PSTR("HTTP: Webserver stopped")); } } void beginWifiManager() { // setup AP if ((WiFi.status() == WL_CONNECTED) && (static_cast(WiFi.localIP()) != 0)) { WiFi.mode(WIFI_AP_STA); addLog_P(LOG_LEVEL_DEBUG, PSTR("Wifimanager: Set AccessPoint and keep Station")); } else { WiFi.mode(WIFI_AP); addLog_P(LOG_LEVEL_DEBUG, PSTR("Wifimanager: Set AccessPoint")); } stopWebserver(); dnsServer = new DNSServer(); WiFi.softAP(Hostname); delay(500); // Without delay I've seen the IP address blank /* Setup the DNS server redirecting all the domains to the apIP */ dnsServer->setErrorReplyCode(DNSReplyCode::NoError); dnsServer->start(DNS_PORT, "*", WiFi.softAPIP()); startWebserver(HTTP_MANAGER, WiFi.softAPIP()); } void pollDnsWeb() { if (dnsServer) dnsServer->processNextRequest(); if (webServer) webServer->handleClient(); } void showPage(String &page) { if((_httpflag == HTTP_ADMIN) && (sysCfg.web_password[0] != 0) && !webServer->authenticate(WEB_USERNAME, sysCfg.web_password)) { return webServer->requestAuthentication(); } page.replace("{ha}", my_module.name); page.replace("{h}", sysCfg.friendlyname[0]); if (_httpflag == HTTP_MANAGER) { if (WIFI_configCounter()) { page.replace("", ""); page += FPSTR(HTTP_COUNTER); } } page += FPSTR(HTTP_END); webServer->sendHeader("Cache-Control", "no-cache, no-store, must-revalidate"); webServer->sendHeader("Pragma", "no-cache"); webServer->sendHeader("Expires", "-1"); webServer->send(200, "text/html", page); } void handleRoot() { addLog_P(LOG_LEVEL_DEBUG, PSTR("HTTP: Handle root")); if (captivePortal()) { // If captive portal redirect instead of displaying the page. return; } if (_httpflag == HTTP_MANAGER) { handleWifi0(); } else { char stemp[10], line[100]; String page = FPSTR(HTTP_HEAD); page.replace("{v}", "Main menu"); page.replace("", ""); page += F("
"); if (Maxdevice) { if (sysCfg.module == SONOFF_LED) { snprintf_P(line, sizeof(line), PSTR(""), sysCfg.led_dimmer[0]); page += line; } page += F(""); for (byte idx = 1; idx <= Maxdevice; idx++) { snprintf_P(stemp, sizeof(stemp), PSTR(" %d"), idx); snprintf_P(line, sizeof(line), PSTR(""), 100 / Maxdevice, idx, (Maxdevice > 1) ? stemp : ""); page += line; } page += F("
"); } if (_httpflag == HTTP_ADMIN) { page += FPSTR(HTTP_BTN_MENU1); page += FPSTR(HTTP_BTN_RSTRT); } showPage(page); } } void handleAjax2() { char svalue[16]; if (strlen(webServer->arg("o").c_str())) do_cmnd_power(atoi(webServer->arg("o").c_str()), 2); if (strlen(webServer->arg("d").c_str())) { snprintf_P(svalue, sizeof(svalue), PSTR("dimmer %s"), webServer->arg("d").c_str()); do_cmnd(svalue); } String tpage = ""; if (hlw_flg) tpage += hlw_webPresent(); #ifdef USE_DS18B20 if (pin[GPIO_DSB] < 99) tpage += dsb_webPresent(); #endif // USE_DS18B20 #ifdef USE_DS18x20 if (pin[GPIO_DSB] < 99) tpage += ds18x20_webPresent(); #endif // USE_DS18x20 #ifdef USE_DHT if (dht_type) tpage += dht_webPresent(); #endif // USE_DHT #ifdef USE_I2C if (i2c_flg) { tpage += htu_webPresent(); tpage += bmp_webPresent(); tpage += bh1750_webPresent(); } #endif // USE_I2C String page = ""; if (tpage.length() > 0) { page += F(""); page += tpage; page += F("
"); } char line[120]; if (Maxdevice) { page += F(""); for (byte idx = 1; idx <= Maxdevice; idx++) { snprintf_P(line, sizeof(line), PSTR(""), 100 / Maxdevice, 70 - (Maxdevice * 8), (power & (0x01 << (idx -1))) ? "ON" : "OFF"); page += line; } page += F("
%s
"); } /* * Will interrupt user action when selected if (sysCfg.module == SONOFF_LED) { snprintf_P(line, sizeof(line), PSTR(""), sysCfg.led_dimmer[0]); page += line; } */ webServer->sendHeader("Cache-Control", "no-cache, no-store, must-revalidate"); webServer->sendHeader("Pragma", "no-cache"); webServer->sendHeader("Expires", "-1"); webServer->send(200, "text/plain", page); } boolean httpUser() { boolean status = (_httpflag == HTTP_USER); if (status) handleRoot(); return status; } void handleConfig() { if (httpUser()) return; addLog_P(LOG_LEVEL_DEBUG, PSTR("HTTP: Handle config")); String page = FPSTR(HTTP_HEAD); page.replace("{v}", "Configuration"); page += FPSTR(HTTP_BTN_MENU2); if (sysCfg.mqtt_enabled) page += FPSTR(HTTP_BTN_MENU3); page += FPSTR(HTTP_BTN_MENU4); page += FPSTR(HTTP_BTN_MAIN); showPage(page); } void handleModule() { if (httpUser()) return; char stemp[20], line[128]; addLog_P(LOG_LEVEL_DEBUG, PSTR("HTTP: Handle Module config")); String page = FPSTR(HTTP_HEAD); page.replace("{v}", "Config module"); page += FPSTR(HTTP_FORM_MODULE); snprintf_P(stemp, sizeof(stemp), modules[MODULE].name); page.replace("{mt}", stemp); for (byte i = 0; i < MAXMODULE; i++) { snprintf_P(stemp, sizeof(stemp), modules[i].name); snprintf_P(line, sizeof(line), PSTR("%d %s"), (i == sysCfg.module) ? " selected" : "", i, i +1, stemp); page += line; } page += F("
"); mytmplt cmodule; memcpy_P(&cmodule, &modules[sysCfg.module], sizeof(cmodule)); for (byte i = 0; i < MAX_GPIO_PIN; i++) { if (cmodule.gp.io[i] == GPIO_USER) { snprintf_P(line, sizeof(line), PSTR("
GPIO%d
"); } } page += FPSTR(HTTP_FORM_END); page += FPSTR(HTTP_BTN_CONF); showPage(page); } void handleWifi1() { handleWifi(true); } void handleWifi0() { handleWifi(false); } void handleWifi(boolean scan) { if (httpUser()) return; char log[LOGSZ]; addLog_P(LOG_LEVEL_DEBUG, PSTR("HTTP: Handle Wifi config")); String page = FPSTR(HTTP_HEAD); page.replace("{v}", "Configure Wifi"); page.replace("", FPSTR(HTTP_LNK_STYLE)); if (scan) { #ifdef USE_EMULATION UDP_Disconnect(); #endif // USE_EMULATION int n = WiFi.scanNetworks(); addLog_P(LOG_LEVEL_DEBUG, PSTR("Wifi: Scan done")); if (n == 0) { addLog_P(LOG_LEVEL_DEBUG, PSTR("Wifi: No networks found")); page += F("No networks found. Refresh to scan again."); } else { //sort networks int indices[n]; for (int i = 0; i < n; i++) { indices[i] = i; } // RSSI SORT for (int i = 0; i < n; i++) { for (int j = i + 1; j < n; j++) { if (WiFi.RSSI(indices[j]) > WiFi.RSSI(indices[i])) { std::swap(indices[i], indices[j]); } } } // remove duplicates ( must be RSSI sorted ) if (_removeDuplicateAPs) { String cssid; for (int i = 0; i < n; i++) { if (indices[i] == -1) continue; cssid = WiFi.SSID(indices[i]); for (int j = i + 1; j < n; j++) { if (cssid == WiFi.SSID(indices[j])) { snprintf_P(log, sizeof(log), PSTR("Wifi: Duplicate AccessPoint %s"), WiFi.SSID(indices[j]).c_str()); addLog(LOG_LEVEL_DEBUG, log); indices[j] = -1; // set dup aps to index -1 } } } } //display networks in page for (int i = 0; i < n; i++) { if (indices[i] == -1) continue; // skip dups snprintf_P(log, sizeof(log), PSTR("Wifi: SSID %s, RSSI %d"), WiFi.SSID(indices[i]).c_str(), WiFi.RSSI(indices[i])); addLog(LOG_LEVEL_DEBUG, log); int quality = WIFI_getRSSIasQuality(WiFi.RSSI(indices[i])); if (_minimumQuality == -1 || _minimumQuality < quality) { String item = FPSTR(HTTP_LNK_ITEM); String rssiQ; rssiQ += quality; item.replace("{v}", WiFi.SSID(indices[i])); item.replace("{r}", rssiQ); if (WiFi.encryptionType(indices[i]) != ENC_TYPE_NONE) { item.replace("{i}", "l"); } else { item.replace("{i}", ""); } page += item; delay(0); } else { addLog_P(LOG_LEVEL_DEBUG, PSTR("Wifi: Skipping due to low quality")); } } page += "
"; } } else { page += FPSTR(HTTP_LNK_SCAN); } page += FPSTR(HTTP_FORM_WIFI); page.replace("{h1}", sysCfg.hostname); page.replace("{s1}", sysCfg.sta_ssid[0]); page.replace("{p1}", sysCfg.sta_pwd[0]); page.replace("{s2}", sysCfg.sta_ssid[1]); page.replace("{p2}", sysCfg.sta_pwd[1]); page += FPSTR(HTTP_FORM_END); if (_httpflag == HTTP_MANAGER) { page += FPSTR(HTTP_BTN_RSTRT); } else { page += FPSTR(HTTP_BTN_CONF); } showPage(page); } void handleMqtt() { if (httpUser()) return; addLog_P(LOG_LEVEL_DEBUG, PSTR("HTTP: Handle MQTT config")); String page = FPSTR(HTTP_HEAD); page.replace("{v}", "Configure MQTT"); page += FPSTR(HTTP_FORM_MQTT); char str[sizeof(sysCfg.mqtt_client)]; getClient(str, MQTT_CLIENT_ID, sizeof(sysCfg.mqtt_client)); page.replace("{m0}", str); page.replace("{m1}", sysCfg.mqtt_host); page.replace("{m2}", String(sysCfg.mqtt_port)); page.replace("{m3}", sysCfg.mqtt_client); page.replace("{m4}", sysCfg.mqtt_user); page.replace("{m5}", sysCfg.mqtt_pwd); page.replace("{m6}", sysCfg.mqtt_topic); page += FPSTR(HTTP_FORM_END); page += FPSTR(HTTP_BTN_CONF); showPage(page); } void handleLog() { if (httpUser()) return; addLog_P(LOG_LEVEL_DEBUG, PSTR("HTTP: Handle Log config")); String page = FPSTR(HTTP_HEAD); page.replace("{v}", "Config logging"); page += FPSTR(HTTP_FORM_LOG1); for (byte idx = 0; idx < 3; idx++) { page += FPSTR(HTTP_FORM_LOG2); switch (idx) { case 0: page.replace("{b0}", F("Serial ")); page.replace("{b1}", STR(SERIAL_LOG_LEVEL)); page.replace("{b2}", "ls"); for (byte i = LOG_LEVEL_NONE; i < LOG_LEVEL_ALL; i++) { page.replace("{a" + String(i), (i == sysCfg.seriallog_level) ? " selected " : " "); } break; case 1: page.replace("{b0}", F("Web ")); page.replace("{b1}", STR(WEB_LOG_LEVEL)); page.replace("{b2}", "lw"); for (byte i = LOG_LEVEL_NONE; i < LOG_LEVEL_ALL; i++) { page.replace("{a" + String(i), (i == sysCfg.weblog_level) ? " selected " : " "); } break; case 2: page.replace("{b0}", F("Sys")); page.replace("{b1}", STR(SYS_LOG_LEVEL)); page.replace("{b2}", "ll"); for (byte i = LOG_LEVEL_NONE; i < LOG_LEVEL_ALL; i++) { page.replace("{a" + String(i), (i == sysCfg.syslog_level) ? " selected " : " "); } break; } } page += FPSTR(HTTP_FORM_LOG3); page.replace("{l2}", sysCfg.syslog_host); page.replace("{l3}", String(sysCfg.syslog_port)); page.replace("{l4}", String(sysCfg.tele_period)); page += FPSTR(HTTP_FORM_END); page += FPSTR(HTTP_BTN_CONF); showPage(page); } void handleOther() { if (httpUser()) return; addLog_P(LOG_LEVEL_DEBUG, PSTR("HTTP: Handle other config")); char stemp[40]; String page = FPSTR(HTTP_HEAD); page.replace("{v}", "Configure Other"); page += FPSTR(HTTP_FORM_OTHER); page.replace("{p1}", sysCfg.web_password); page.replace("{r1}", (sysCfg.mqtt_enabled) ? " checked" : ""); page += FPSTR(HTTP_FORM_OTHER2); page.replace("{1", "1"); page.replace("{2", FRIENDLY_NAME); page.replace("{3", sysCfg.friendlyname[0]); #ifdef USE_EMULATION page += FPSTR(HTTP_FORM_OTHER3); page.replace("{r2}", (sysCfg.emulation == EMUL_NONE) ? " checked" : ""); page.replace("{r3}", (sysCfg.emulation == EMUL_WEMO) ? " checked" : ""); page.replace("{r4}", (sysCfg.emulation == EMUL_HUE) ? " checked" : ""); for (int i = 1; i < Maxdevice; i++) { page += FPSTR(HTTP_FORM_OTHER2); page.replace("{1", String(i +1)); snprintf_P(stemp, sizeof(stemp), PSTR(FRIENDLY_NAME"%d"), i +1); page.replace("{2", stemp); page.replace("{3", sysCfg.friendlyname[i]); } page += F("
"); #endif // USE_EMULATION page += FPSTR(HTTP_FORM_END); page += FPSTR(HTTP_BTN_CONF); showPage(page); } void handleDownload() { if (httpUser()) return; addLog_P(LOG_LEVEL_DEBUG, PSTR("HTTP: Handle download config")); uint8_t buffer[sizeof(sysCfg)]; WiFiClient myClient = webServer->client(); webServer->setContentLength(4096); char attachment[100]; snprintf_P(attachment, sizeof(attachment), PSTR("attachment; filename=Config_%s_%s.dmp"), sysCfg.friendlyname[0], Version); webServer->sendHeader("Content-Disposition", attachment); webServer->send(200, "application/octet-stream", ""); memcpy(buffer, &sysCfg, sizeof(sysCfg)); buffer[0] = CONFIG_FILE_SIGN; buffer[1] = (!CONFIG_FILE_XOR)?0:1; if (buffer[1]) for (uint16_t i = 2; i < sizeof(buffer); i++) buffer[i] ^= (CONFIG_FILE_XOR +i); myClient.write((const char*)buffer, sizeof(buffer)); } void handleSave() { if (httpUser()) return; char log[LOGSZ], stemp[20]; byte what = 0, restart; String result = ""; addLog_P(LOG_LEVEL_DEBUG, PSTR("HTTP: Parameter save")); if (strlen(webServer->arg("w").c_str())) what = atoi(webServer->arg("w").c_str()); switch (what) { case 1: strlcpy(sysCfg.hostname, (!strlen(webServer->arg("h").c_str())) ? WIFI_HOSTNAME : webServer->arg("h").c_str(), sizeof(sysCfg.hostname)); if (strstr(sysCfg.hostname,"%")) strlcpy(sysCfg.hostname, WIFI_HOSTNAME, sizeof(sysCfg.hostname)); strlcpy(sysCfg.sta_ssid[0], (!strlen(webServer->arg("s1").c_str())) ? STA_SSID1 : webServer->arg("s1").c_str(), sizeof(sysCfg.sta_ssid[0])); strlcpy(sysCfg.sta_pwd[0], (!strlen(webServer->arg("p1").c_str())) ? STA_PASS1 : webServer->arg("p1").c_str(), sizeof(sysCfg.sta_pwd[0])); strlcpy(sysCfg.sta_ssid[1], (!strlen(webServer->arg("s2").c_str())) ? STA_SSID2 : webServer->arg("s2").c_str(), sizeof(sysCfg.sta_ssid[1])); strlcpy(sysCfg.sta_pwd[1], (!strlen(webServer->arg("p2").c_str())) ? STA_PASS2 : webServer->arg("p2").c_str(), sizeof(sysCfg.sta_pwd[1])); snprintf_P(log, sizeof(log), PSTR("HTTP: Wifi Hostname %s, SSID1 %s, Password1 %s, SSID2 %s, Password2 %s"), sysCfg.hostname, sysCfg.sta_ssid[0], sysCfg.sta_pwd[0], sysCfg.sta_ssid[1], sysCfg.sta_pwd[1]); addLog(LOG_LEVEL_INFO, log); result += F("
Trying to connect device to network
If it fails reconnect to try again"); break; case 2: strlcpy(sysCfg.mqtt_host, (!strlen(webServer->arg("mh").c_str())) ? MQTT_HOST : webServer->arg("mh").c_str(), sizeof(sysCfg.mqtt_host)); sysCfg.mqtt_port = (!strlen(webServer->arg("ml").c_str())) ? MQTT_PORT : atoi(webServer->arg("ml").c_str()); strlcpy(sysCfg.mqtt_client, (!strlen(webServer->arg("mc").c_str())) ? MQTT_CLIENT_ID : webServer->arg("mc").c_str(), sizeof(sysCfg.mqtt_client)); strlcpy(sysCfg.mqtt_user, (!strlen(webServer->arg("mu").c_str())) ? MQTT_USER : webServer->arg("mu").c_str(), sizeof(sysCfg.mqtt_user)); strlcpy(sysCfg.mqtt_pwd, (!strlen(webServer->arg("mp").c_str())) ? MQTT_PASS : webServer->arg("mp").c_str(), sizeof(sysCfg.mqtt_pwd)); strlcpy(sysCfg.mqtt_topic, (!strlen(webServer->arg("mt").c_str())) ? MQTT_TOPIC : webServer->arg("mt").c_str(), sizeof(sysCfg.mqtt_topic)); snprintf_P(log, sizeof(log), PSTR("HTTP: MQTT Host %s, Port %d, Client %s, User %s, Password %s, Topic %s"), sysCfg.mqtt_host, sysCfg.mqtt_port, sysCfg.mqtt_client, sysCfg.mqtt_user, sysCfg.mqtt_pwd, sysCfg.mqtt_topic); addLog(LOG_LEVEL_INFO, log); break; case 3: sysCfg.seriallog_level = (!strlen(webServer->arg("ls").c_str())) ? SERIAL_LOG_LEVEL : atoi(webServer->arg("ls").c_str()); sysCfg.weblog_level = (!strlen(webServer->arg("lw").c_str())) ? WEB_LOG_LEVEL : atoi(webServer->arg("lw").c_str()); sysCfg.syslog_level = (!strlen(webServer->arg("ll").c_str())) ? SYS_LOG_LEVEL : atoi(webServer->arg("ll").c_str()); syslog_level = sysCfg.syslog_level; syslog_timer = 0; strlcpy(sysCfg.syslog_host, (!strlen(webServer->arg("lh").c_str())) ? SYS_LOG_HOST : webServer->arg("lh").c_str(), sizeof(sysCfg.syslog_host)); sysCfg.syslog_port = (!strlen(webServer->arg("lp").c_str())) ? SYS_LOG_PORT : atoi(webServer->arg("lp").c_str()); sysCfg.tele_period = (!strlen(webServer->arg("lt").c_str())) ? TELE_PERIOD : atoi(webServer->arg("lt").c_str()); snprintf_P(log, sizeof(log), PSTR("HTTP: Logging Seriallog %d, Weblog %d, Syslog %d, Host %s, Port %d, TelePeriod %d"), sysCfg.seriallog_level, sysCfg.weblog_level, sysCfg.syslog_level, sysCfg.syslog_host, sysCfg.syslog_port, sysCfg.tele_period); addLog(LOG_LEVEL_INFO, log); break; #ifdef USE_DOMOTICZ case 4: domoticz_saveSettings(); break; #endif // USE_DOMOTICZ case 5: strlcpy(sysCfg.web_password, (!strlen(webServer->arg("p1").c_str())) ? WEB_PASSWORD : webServer->arg("p1").c_str(), sizeof(sysCfg.web_password)); if (sysCfg.web_password[0] == '0') sysCfg.web_password[0] = '\0'; sysCfg.mqtt_enabled = webServer->hasArg("b1"); #ifdef USE_EMULATION sysCfg.emulation = (!strlen(webServer->arg("b2").c_str())) ? 0 : atoi(webServer->arg("b2").c_str()); #endif // USE_EMULATION strlcpy(sysCfg.friendlyname[0], (!strlen(webServer->arg("a1").c_str())) ? FRIENDLY_NAME : webServer->arg("a1").c_str(), sizeof(sysCfg.friendlyname[0])); strlcpy(sysCfg.friendlyname[1], (!strlen(webServer->arg("a2").c_str())) ? FRIENDLY_NAME"2" : webServer->arg("a2").c_str(), sizeof(sysCfg.friendlyname[1])); strlcpy(sysCfg.friendlyname[2], (!strlen(webServer->arg("a3").c_str())) ? FRIENDLY_NAME"3" : webServer->arg("a3").c_str(), sizeof(sysCfg.friendlyname[2])); strlcpy(sysCfg.friendlyname[3], (!strlen(webServer->arg("a4").c_str())) ? FRIENDLY_NAME"4" : webServer->arg("a4").c_str(), sizeof(sysCfg.friendlyname[3])); snprintf_P(log, sizeof(log), PSTR("HTTP: Other MQTT Enable %s, Emulation %d, Friendly Names %s, %s, %s and %s"), (sysCfg.mqtt_enabled) ? MQTT_STATUS_ON : MQTT_STATUS_OFF, sysCfg.emulation, sysCfg.friendlyname[0], sysCfg.friendlyname[1], sysCfg.friendlyname[2], sysCfg.friendlyname[3]); addLog(LOG_LEVEL_INFO, log); break; case 6: sysCfg.module = (!strlen(webServer->arg("mt").c_str())) ? MODULE : atoi(webServer->arg("mt").c_str()); mytmplt cmodule; memcpy_P(&cmodule, &modules[sysCfg.module], sizeof(cmodule)); String gpios = ""; for (byte i = 0; i < MAX_GPIO_PIN; i++) { if (cmodule.gp.io[i] == GPIO_USER) { snprintf_P(stemp, sizeof(stemp), PSTR("g%d"), i); sysCfg.my_module.gp.io[i] = (!strlen(webServer->arg(stemp).c_str())) ? 0 : atoi(webServer->arg(stemp).c_str()); gpios += F(", GPIO"); gpios += String(i); gpios += F(" "); gpios += String(sysCfg.my_module.gp.io[i]); } } snprintf_P(stemp, sizeof(stemp), modules[sysCfg.module].name); snprintf_P(log, sizeof(log), PSTR("HTTP: %s Module%s"), stemp, gpios.c_str()); addLog(LOG_LEVEL_INFO, log); break; } restart = (!strlen(webServer->arg("r").c_str())) ? 1 : atoi(webServer->arg("r").c_str()); if (restart) { String page = FPSTR(HTTP_HEAD); page.replace("{v}", "Save parameters"); page += F("
Parameters saved
"); page += result; page += F("
"); page += FPSTR(HTTP_MSG_RSTRT); if (_httpflag == HTTP_MANAGER) { _httpflag = HTTP_ADMIN; } else { page += FPSTR(HTTP_BTN_MAIN); } showPage(page); restartflag = 2; } else { handleConfig(); } } void handleReset() { if (httpUser()) return; char svalue[16]; // was MESSZ addLog_P(LOG_LEVEL_DEBUG, PSTR("HTTP: Reset parameters")); String page = FPSTR(HTTP_HEAD); page.replace("{v}", "Default parameters"); page += F("
Parameters reset to default
"); page += FPSTR(HTTP_MSG_RSTRT); page += FPSTR(HTTP_BTN_MAIN); showPage(page); snprintf_P(svalue, sizeof(svalue), PSTR("reset 1")); do_cmnd(svalue); } void handleRestore() { if (httpUser()) return; addLog_P(LOG_LEVEL_DEBUG, PSTR("HTTP: Handle restore")); String page = FPSTR(HTTP_HEAD); page.replace("{v}", "Restore Configuration"); page += FPSTR(HTTP_FORM_RST); page += FPSTR(HTTP_BTN_CONF); showPage(page); _uploaderror = 0; _uploadfiletype = 1; } void handleUpgrade() { if (httpUser()) return; addLog_P(LOG_LEVEL_DEBUG, PSTR("HTTP: Handle upgrade")); String page = FPSTR(HTTP_HEAD); page.replace("{v}", "Firmware upgrade"); page += FPSTR(HTTP_FORM_UPG); page.replace("{o1}", sysCfg.otaUrl); page += FPSTR(HTTP_BTN_MAIN); showPage(page); _uploaderror = 0; _uploadfiletype = 0; } void handleUpgradeStart() { if (httpUser()) return; char svalue[16]; // was MESSZ addLog_P(LOG_LEVEL_DEBUG, PSTR("HTTP: Firmware upgrade start")); WIFI_configCounter(); if (strlen(webServer->arg("o").c_str())) { snprintf_P(svalue, sizeof(svalue), PSTR("otaurl %s"), webServer->arg("o").c_str()); do_cmnd(svalue); } String page = FPSTR(HTTP_HEAD); page.replace("{v}", "Info"); page += F("
Upgrade started ...
"); page += FPSTR(HTTP_MSG_RSTRT); page += FPSTR(HTTP_BTN_MAIN); showPage(page); snprintf_P(svalue, sizeof(svalue), PSTR("upgrade 1")); do_cmnd(svalue); } void handleUploadDone() { if (httpUser()) return; addLog_P(LOG_LEVEL_DEBUG, PSTR("HTTP: File upload done")); char error[80], log[LOGSZ]; WIFI_configCounter(); restartflag = 0; mqttcounter = 0; String page = FPSTR(HTTP_HEAD); page.replace("{v}", "Info"); page += F("
Upload "); if (_uploaderror) { page += F("failed

"); switch (_uploaderror) { case 1: strcpy_P(error, PSTR("No file selected")); break; case 2: strcpy_P(error, PSTR("File size is larger than available free space")); break; case 3: strcpy_P(error, PSTR("File magic header does not start with 0xE9")); break; case 4: strcpy_P(error, PSTR("File flash size is larger than device flash size")); break; case 5: strcpy_P(error, PSTR("File upload buffer miscompare")); break; case 6: strcpy_P(error, PSTR("Upload failed. Enable logging option 3 for more information")); break; case 7: strcpy_P(error, PSTR("Upload aborted")); break; case 8: strcpy_P(error, PSTR("Invalid configuration file")); break; case 9: strcpy_P(error, PSTR("Configuration file too large")); break; default: snprintf_P(error, sizeof(error), PSTR("Upload error code %d"), _uploaderror); } page += error; if (!_uploadfiletype && Update.hasError()) { page += F("

Update error code (see Updater.cpp) "); page += String(Update.getError()); } snprintf_P(log, sizeof(log), PSTR("Upload: Error - %s"), error); addLog(LOG_LEVEL_DEBUG, log); sl_blank(0); } else { page += F("successful

Device will restart in a few seconds"); restartflag = 2; } page += F("

"); page += FPSTR(HTTP_BTN_MAIN); showPage(page); } void handleUploadLoop() { // Based on ESP8266HTTPUpdateServer.cpp uses ESP8266WebServer Parsing.cpp and Cores Updater.cpp (Update) char log[LOGSZ]; boolean _serialoutput = (LOG_LEVEL_DEBUG <= seriallog_level); if (_httpflag == HTTP_USER) return; if (_uploaderror) { if (!_uploadfiletype) Update.end(); return; } HTTPUpload& upload = webServer->upload(); if (upload.status == UPLOAD_FILE_START) { restartflag = 60; if (upload.filename.c_str()[0] == 0) { _uploaderror = 1; return; } snprintf_P(log, sizeof(log), PSTR("Upload: File %s ..."), upload.filename.c_str()); addLog(LOG_LEVEL_INFO, log); if (!_uploadfiletype) { mqttcounter = 60; #ifdef USE_EMULATION UDP_Disconnect(); #endif // USE_EMULATION if (sysCfg.mqtt_enabled) mqttClient.disconnect(); uint32_t maxSketchSpace = (ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000; if (!Update.begin(maxSketchSpace)) { //start with max available size if (_serialoutput) Update.printError(Serial); _uploaderror = 2; return; } } sl_blank(1); _colcount = 0; } else if (!_uploaderror && (upload.status == UPLOAD_FILE_WRITE)) { if (upload.totalSize == 0) { if (_uploadfiletype) { if (upload.buf[0] != CONFIG_FILE_SIGN) { _uploaderror = 8; return; } if (upload.currentSize > sizeof(sysCfg)) { _uploaderror = 9; return; } } else { if (upload.buf[0] != 0xE9) { _uploaderror = 3; return; } uint32_t bin_flash_size = ESP.magicFlashChipSize((upload.buf[3] & 0xf0) >> 4); if(bin_flash_size > ESP.getFlashChipRealSize()) { _uploaderror = 4; return; } if ((sysCfg.module == SONOFF_TOUCH) || (sysCfg.module == SONOFF_4CH)) { upload.buf[2] = 3; // DOUT - ESP8285 addLog_P(LOG_LEVEL_DEBUG, PSTR("FLSH: Updated Flash Chip Mode to 3")); } } } if (_uploadfiletype) { // config if (!_uploaderror) { if (upload.buf[1]) for (uint16_t i = 2; i < upload.currentSize; i++) upload.buf[i] ^= (CONFIG_FILE_XOR +i); CFG_DefaultSet2(); memcpy((char*)&sysCfg +16, upload.buf +16, upload.currentSize -16); } } else { // firmware if (!_uploaderror && (Update.write(upload.buf, upload.currentSize) != upload.currentSize)) { if (_serialoutput) Update.printError(Serial); _uploaderror = 5; return; } if (_serialoutput) { Serial.printf("."); _colcount++; if (!(_colcount % 80)) Serial.println(); } } } else if(!_uploaderror && (upload.status == UPLOAD_FILE_END)) { if (_serialoutput && (_colcount % 80)) Serial.println(); if (!_uploadfiletype) { if (!Update.end(true)) { // true to set the size to the current progress if (_serialoutput) Update.printError(Serial); _uploaderror = 6; return; } } if (!_uploaderror) { snprintf_P(log, sizeof(log), PSTR("Upload: Successful %u bytes. Restarting"), upload.totalSize); addLog(LOG_LEVEL_INFO, log); } } else if(upload.status == UPLOAD_FILE_ABORTED) { restartflag = 0; mqttcounter = 0; _uploaderror = 7; if (!_uploadfiletype) Update.end(); } delay(0); } void handleCmnd() { if (httpUser()) return; char svalue[128]; // was MESSZ addLog_P(LOG_LEVEL_DEBUG, PSTR("HTTP: Handle cmnd")); uint8_t valid = 1; if (sysCfg.web_password[0] != 0) { if (!(!strcmp(webServer->arg("user").c_str(),WEB_USERNAME) && !strcmp(webServer->arg("password").c_str(),sysCfg.web_password))) { valid = 0; } } String message = ""; if (valid) { byte curridx = logidx; if (strlen(webServer->arg("cmnd").c_str())) { snprintf_P(svalue, sizeof(svalue), webServer->arg("cmnd").c_str()); byte syslog_now = syslog_level; syslog_level = 0; // Disable UDP syslog to not trigger hardware WDT do_cmnd(svalue); syslog_level = syslog_now; } if (logidx != curridx) { byte counter = curridx; do { if (Log[counter].length()) { if (message.length()) message += F("\n"); if (sysCfg.mqtt_enabled) { // [14:49:36 MQTT: stat/wemos5/RESULT = {"POWER":"OFF"}] > [RESULT = {"POWER":"OFF"}] // message += Log[counter].substring(17 + strlen(PUB_PREFIX) + strlen(sysCfg.mqtt_topic)); message += Log[counter].substring(Log[counter].lastIndexOf("/",Log[counter].indexOf("="))+1); } else { // [14:49:36 RSLT: RESULT = {"POWER":"OFF"}] > [RESULT = {"POWER":"OFF"}] message += Log[counter].substring(Log[counter].indexOf(": ")+2); } } counter++; if (counter > MAX_LOG_LINES -1) counter = 0; } while (counter != logidx); } else { message = F("Enable weblog 2 if response expected\n"); } } else { message = F("Need user=&password=\n"); } webServer->sendHeader("Cache-Control", "no-cache, no-store, must-revalidate"); webServer->sendHeader("Pragma", "no-cache"); webServer->sendHeader("Expires", "-1"); webServer->send(200, "text/plain", message); } void handleConsole() { if (httpUser()) return; addLog_P(LOG_LEVEL_DEBUG, PSTR("HTTP: Handle console")); String page = FPSTR(HTTP_HEAD); page.replace("{v}", "Console"); page.replace("", FPSTR(HTTP_SCRIPT_CONSOL)); page.replace("", ""); page += FPSTR(HTTP_FORM_CMND); page += FPSTR(HTTP_BTN_MAIN); showPage(page); } void handleAjax() { if (httpUser()) return; char log[LOGSZ], svalue[128]; // was MESSZ byte cflg = 1, counter = 99; if (strlen(webServer->arg("c1").c_str())) { snprintf_P(svalue, sizeof(svalue), webServer->arg("c1").c_str()); snprintf_P(log, sizeof(log), PSTR("CMND: %s"), svalue); addLog(LOG_LEVEL_INFO, log); byte syslog_now = syslog_level; syslog_level = 0; // Disable UDP syslog to not trigger hardware WDT do_cmnd(svalue); syslog_level = syslog_now; } if (strlen(webServer->arg("c2").c_str())) counter = atoi(webServer->arg("c2").c_str()); String message = F(""); message += String(logidx); message += F(""); message += String(logajaxflg); if (!logajaxflg) { counter = 99; logajaxflg = 1; } message += F(""); if (counter != logidx) { if (counter == 99) { counter = logidx; cflg = 0; } do { if (Log[counter].length()) { if (cflg) message += F("\n"); else cflg = 1; message += Log[counter]; } counter++; if (counter > MAX_LOG_LINES -1) counter = 0; } while (counter != logidx); } message += F(""); webServer->sendHeader("Cache-Control", "no-cache, no-store, must-revalidate"); webServer->sendHeader("Pragma", "no-cache"); webServer->sendHeader("Expires", "-1"); webServer->send(200, "text/xml", message); } void handleInfo() { if (httpUser()) return; addLog_P(LOG_LEVEL_DEBUG, PSTR("HTTP: Handle info")); int freeMem = ESP.getFreeHeap(); String page = FPSTR(HTTP_HEAD); page.replace("{v}", "Information"); // page += F("
 Information "); page += F(""); page += F(""); page += F(""); page += F(""); page += F(""); // page += F(""); page += F(""); page += F(""); page += F(""); page += F(""); for (byte i = 0; i < Maxdevice; i++) { page += F(""); } page += F(""); // page += F(""); page += F(""); page += F(""); if (static_cast(WiFi.localIP()) != 0) { page += F(""); page += F(""); page += F(""); } if (static_cast(WiFi.softAPIP()) != 0) { page += F(""); page += F(""); page += F(""); } page += F(""); if (sysCfg.mqtt_enabled) { page += F(""); page += F(""); page += F(""); page += F(""); // page += F(""); page += F(""); page += F(""); } else { page += F(""); } page += F(""); page += F(""); page += F(""); page += F(""); page += F(""); page += F(""); page += F(""); page += F(""); page += F(""); page += F(""); page += F(""); page += F("
Program version"); page += Version; page += F("
Build Date & Time"); page += getBuildDateTime(); page += F("
Core/SDK version"); page += ESP.getCoreVersion(); page += F("/"); page += String(ESP.getSdkVersion()); page += F("
Boot version"); page += String(ESP.getBootVersion()); page += F("
Uptime"); page += String(uptime); page += F(" Hours
Flash write count"); page += String(sysCfg.saveFlag); page += F("
Boot count"); page += String(sysCfg.bootcount); page += F("
Reset reason"); page += getResetReason(); page += F("
Friendly name "); page += i +1; page += F(""); page += sysCfg.friendlyname[i]; page += F("
 
SSId (RSSI)"); page += (sysCfg.sta_active)? sysCfg.sta_ssid2 : sysCfg.sta_ssid1; page += F(" ("); page += WIFI_getRSSIasQuality(WiFi.RSSI()); page += F("%)
AP"); page += String(sysCfg.sta_active +1); page += F(" SSId (RSSI)"); page += sysCfg.sta_ssid[sysCfg.sta_active]; page += F(" ("); page += WIFI_getRSSIasQuality(WiFi.RSSI()); page += F("%)
Hostname"); page += Hostname; page += F("
IP address"); page += WiFi.localIP().toString(); page += F("
Gateway"); page += WiFi.gatewayIP().toString(); page += F("
MAC address"); page += WiFi.macAddress(); page += F("
AP IP address"); page += WiFi.softAPIP().toString(); page += F("
AP Gateway"); page += WiFi.softAPIP().toString(); page += F("
AP MAC address"); page += WiFi.softAPmacAddress(); page += F("
 
MQTT Host"); page += sysCfg.mqtt_host; page += F("
MQTT Port"); page += String(sysCfg.mqtt_port); page += F("
MQTT Client &
 Fallback Topic
"); page += MQTTClient; page += F("
MQTT User"); page += sysCfg.mqtt_user; page += F("
MQTT Password"); page += sysCfg.mqtt_pwd; page += F("
MQTT Topic"); page += sysCfg.mqtt_topic; page += F("
MQTT Group Topic"); page += sysCfg.mqtt_grptopic; page += F("
MQTTDisabled
Emulation"); #ifdef USE_EMULATION if (sysCfg.emulation == EMUL_WEMO) page += F("Belkin WeMo"); else if (sysCfg.emulation == EMUL_HUE) page += F("Hue Bridge"); else page += F("None"); #else page += F("Disabled"); #endif // USE_EMULATION page += F("
mDNS Discovery"); #ifdef USE_DISCOVERY page += F("Enabled"); page += F("
mDNS Advertise"); #ifdef WEBSERVER_ADVERTISE page += F("Webserver"); #else page += F("Disabled"); #endif // WEBSERVER_ADVERTISE #else page += F("Disabled"); #endif // USE_DISCOVERY page += F("
 
ESP Chip id"); page += String(ESP.getChipId()); page += F("
Flash Chip id"); page += String(ESP.getFlashChipId()); page += F("
Flash size"); page += String(ESP.getFlashChipRealSize() / 1024); page += F("kB
Program flash size"); page += String(ESP.getFlashChipSize() / 1024); page += F("kB
Program size"); page += String(ESP.getSketchSize() / 1024); page += F("kB
Free program space"); page += String(ESP.getFreeSketchSpace() / 1024); page += F("kB
Free memory"); page += String(freeMem / 1024); page += F("kB
"); // page += F("
"); page += FPSTR(HTTP_BTN_MAIN); showPage(page); } void handleRestart() { if (httpUser()) return; addLog_P(LOG_LEVEL_DEBUG, PSTR("HTTP: Restarting")); String page = FPSTR(HTTP_HEAD); page.replace("{v}", "Info"); page += FPSTR(HTTP_MSG_RSTRT); if (_httpflag == HTTP_MANAGER) { _httpflag = HTTP_ADMIN; } else { page += FPSTR(HTTP_BTN_MAIN); } showPage(page); restartflag = 2; } /********************************************************************************************/ void handleNotFound() { if (captivePortal()) { // If captive portal redirect instead of displaying the error page. return; } #ifdef USE_EMULATION String path = webServer->uri(); if ((sysCfg.emulation == EMUL_HUE) && (path.startsWith("/api"))) { handle_hue_api(&path); } else #endif // USE_EMULATION { String message = "File Not Found\n\n"; message += "URI: "; message += webServer->uri(); message += "\nMethod: "; message += ( webServer->method() == HTTP_GET ) ? "GET" : "POST"; message += "\nArguments: "; message += webServer->args(); message += "\n"; for ( uint8_t i = 0; i < webServer->args(); i++ ) { message += " " + webServer->argName ( i ) + ": " + webServer->arg ( i ) + "\n"; } webServer->sendHeader("Cache-Control", "no-cache, no-store, must-revalidate"); webServer->sendHeader("Pragma", "no-cache"); webServer->sendHeader("Expires", "-1"); webServer->send(404, "text/plain", message); } } /* Redirect to captive portal if we got a request for another domain. Return true in that case so the page handler do not try to handle the request again. */ boolean captivePortal() { if ((_httpflag == HTTP_MANAGER) && !isIp(webServer->hostHeader())) { addLog_P(LOG_LEVEL_DEBUG, PSTR("HTTP: Request redirected to captive portal")); webServer->sendHeader("Location", String("http://") + webServer->client().localIP().toString(), true); webServer->send(302, "text/plain", ""); // Empty content inhibits Content-length header so we have to close the socket ourselves. webServer->client().stop(); // Stop is needed because we sent no content length return true; } return false; } /** Is this an IP? */ boolean isIp(String str) { for (uint16_t i = 0; i < str.length(); i++) { int c = str.charAt(i); if (c != '.' && (c < '0' || c > '9')) { return false; } } return true; } #endif // USE_WEBSERVER