/*
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 =
""
"
"
""
"
{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='+encodeURI(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_SCRIPT_MODULE[] PROGMEM =
"var os;"
"function sk(s,g){"
"var o=os.replace(\"value='\"+s+\"'\",\"selected value='\"+s+\"'\");"
"document.getElementById('g'+g).innerHTML=o;"
"}"
"function sl(){"
"var o0=\"";
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 ");
#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 +20];
char stemp[20];
byte what = 0;
byte 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 : (!strcmp(webServer->arg("mu").c_str(),"0")) ? "" : webServer->arg("mu").c_str(), sizeof(sysCfg.mqtt_user));
strlcpy(sysCfg.mqtt_pwd, (!strlen(webServer->arg("mp").c_str())) ? MQTT_PASS : (!strcmp(webServer->arg("mp").c_str(),"0")) ? "" : 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 : (!strcmp(webServer->arg("p1").c_str(),"0")) ? "" : webServer->arg("p1").c_str(), sizeof(sysCfg.web_password));
sysCfg.flag.mqtt_enabled = webServer->hasArg("b1");
#ifdef USE_EMULATION
sysCfg.flag.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"),
getStateText(sysCfg.flag.mqtt_enabled), sysCfg.flag.emulation, sysCfg.friendlyname[0], sysCfg.friendlyname[1], sysCfg.friendlyname[2], sysCfg.friendlyname[3]);
addLog(LOG_LEVEL_INFO, log);
break;
case 6:
byte new_module = (!strlen(webServer->arg("mt").c_str())) ? MODULE : atoi(webServer->arg("mt").c_str());
byte new_modflg = (sysCfg.module != new_module);
sysCfg.module = new_module;
mytmplt cmodule;
memcpy_P(&cmodule, &modules[sysCfg.module], sizeof(cmodule));
String gpios = "";
for (byte i = 0; i < MAX_GPIO_PIN; i++) {
if (new_modflg) {
sysCfg.my_module.gp.io[i] = 0;
}
if (GPIO_USER == cmodule.gp.io[i]) {
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]);
}
}
setModuleFlashMode(0);
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(F("{v}"), F("Save parameters"));
page += F("
Parameters saved ");
page += result;
page += F("
");
page += FPSTR(HTTP_MSG_RSTRT);
if (HTTP_MANAGER == _httpflag) {
_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(F("{v}"), F("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(F("{v}"), F("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(F("{v}"), F("Firmware upgrade"));
page += FPSTR(HTTP_FORM_UPG);
page.replace(F("{o1}"), sysCfg.otaUrl);
page += FPSTR(HTTP_BTN_MAIN);
showPage(page);
_uploaderror = 0;
_uploadfiletype = 0;
}
void handleUpgradeStart()
{
if (httpUser()) {
return;
}
char svalue[100]; // 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(F("{v}"), F("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];
char log[LOGSZ];
WIFI_configCounter();
restartflag = 0;
mqttcounter = 0;
String page = FPSTR(HTTP_HEAD);
page.replace(F("{v}"), F("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);
} 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 (HTTP_USER == _httpflag) {
return;
}
if (_uploaderror) {
if (!_uploadfiletype) {
Update.end();
}
return;
}
HTTPUpload& upload = webServer->upload();
if (UPLOAD_FILE_START == upload.status) {
restartflag = 60;
if (0 == upload.filename.c_str()[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.flag.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;
}
}
_colcount = 0;
} else if (!_uploaderror && (UPLOAD_FILE_WRITE == upload.status)) {
if (0 == upload.totalSize)
{
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 ((SONOFF_TOUCH == sysCfg.module) || (SONOFF_4CH == sysCfg.module)) {
upload.buf[2] = 3; // DOUT - ESP8285
addLog_P(LOG_LEVEL_DEBUG, PSTR("FLSH: Set Flash 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_FILE_END == upload.status)) {
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_FILE_ABORTED == upload.status) {
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());
snprintf_P(svalue, sizeof(svalue), PSTR("%s"), 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.flag.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(F("{v}"), F("Console"));
page.replace(F(""), FPSTR(HTTP_SCRIPT_CONSOL));
page.replace(F(""), F(""));
page += FPSTR(HTTP_FORM_CMND);
page += FPSTR(HTTP_BTN_MAIN);
showPage(page);
}
void handleAjax()
{
if (httpUser()) {
return;
}
char log[LOGSZ];
char svalue[128]; // was MESSZ
byte cflg = 1;
byte counter = 99;
if (strlen(webServer->arg("c1").c_str())) {
snprintf_P(svalue, sizeof(svalue), PSTR("%s"), 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 (99 == counter) {
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(F("{v}"), F("Information"));
// page += F(" Information ");
page += F("");
page += F("");
page += F("Program version "); page += Version; page += F(" ");
page += F("Build Date & Time "); page += getBuildDateTime(); page += F(" ");
page += F("Core/SDK version "); page += ESP.getCoreVersion(); page += F("/"); page += String(ESP.getSdkVersion()); page += F(" ");
// page += F("Boot version "); page += String(ESP.getBootVersion()); page += F(" ");
page += F("Uptime "); page += String(uptime); page += F(" Hours ");
page += F("Flash write count "); page += String(sysCfg.saveFlag); page += F(" ");
page += F("Boot count "); page += String(sysCfg.bootcount); page += F(" ");
page += F("Reset reason "); page += getResetReason(); page += F(" ");
for (byte i = 0; i < Maxdevice; i++) {
page += F("Friendly name ");
page += i +1;
page += F(" "); page += sysCfg.friendlyname[i]; page += F(" ");
}
page += F(" ");
// page += F("SSId (RSSI) "); page += (sysCfg.sta_active)? sysCfg.sta_ssid2 : sysCfg.sta_ssid1; page += F(" ("); page += WIFI_getRSSIasQuality(WiFi.RSSI()); page += F("%) ");
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("%) ");
page += F("Hostname "); page += Hostname; page += F(" ");
if (static_cast(WiFi.localIP()) != 0) {
page += F("IP address "); page += WiFi.localIP().toString(); page += F(" ");
page += F("Gateway "); page += IPAddress(sysCfg.ip_address[1]).toString(); page += F(" ");
page += F("Subnet mask "); page += IPAddress(sysCfg.ip_address[2]).toString(); page += F(" ");
page += F("DNS server "); page += IPAddress(sysCfg.ip_address[3]).toString(); page += F(" ");
page += F("MAC address "); page += WiFi.macAddress(); page += F(" ");
}
if (static_cast(WiFi.softAPIP()) != 0) {
page += F("AP IP address "); page += WiFi.softAPIP().toString(); page += F(" ");
page += F("AP Gateway "); page += WiFi.softAPIP().toString(); page += F(" ");
page += F("AP MAC address "); page += WiFi.softAPmacAddress(); page += F(" ");
}
page += F(" ");
if (sysCfg.flag.mqtt_enabled) {
page += F("MQTT Host "); page += sysCfg.mqtt_host; page += F(" ");
page += F("MQTT Port "); page += String(sysCfg.mqtt_port); page += F(" ");
page += F("MQTT Client & Fallback Topic "); page += MQTTClient; page += F(" ");
page += F("MQTT User "); page += sysCfg.mqtt_user; page += F(" ");
// page += F("MQTT Password "); page += sysCfg.mqtt_pwd; page += F(" ");
page += F("MQTT Topic "); page += sysCfg.mqtt_topic; page += F(" ");
page += F("MQTT Group Topic "); page += sysCfg.mqtt_grptopic; page += F(" ");
} else {
page += F("MQTT Disabled ");
}
page += F("Emulation ");
#ifdef USE_EMULATION
if (EMUL_WEMO == sysCfg.flag.emulation) {
page += F("Belkin WeMo");
}
else if (EMUL_HUE == sysCfg.flag.emulation) {
page += F("Hue Bridge");
}
else {
page += F("None");
}
#else
page += F("Disabled");
#endif // USE_EMULATION
page += F(" ");
page += F("mDNS Discovery ");
#ifdef USE_DISCOVERY
page += F("Enabled");
page += F(" ");
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(" ");
page += F(" ");
page += F("ESP Chip id "); page += String(ESP.getChipId()); page += F(" ");
page += F("Flash Chip id "); page += String(ESP.getFlashChipId()); page += F(" ");
page += F("Flash size "); page += String(ESP.getFlashChipRealSize() / 1024); page += F("kB ");
page += F("Program flash size "); page += String(ESP.getFlashChipSize() / 1024); page += F("kB ");
page += F("Program size "); page += String(ESP.getSketchSize() / 1024); page += F("kB ");
page += F("Free program space "); page += String(ESP.getFreeSketchSpace() / 1024); page += F("kB ");
page += F("Free memory "); page += String(freeMem / 1024); page += F("kB ");
page += F("
");
// 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(F("{v}"), F("Info"));
page += FPSTR(HTTP_MSG_RSTRT);
if (HTTP_MANAGER == _httpflag) {
_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 ((EMUL_HUE == sysCfg.flag.emulation) && (path.startsWith("/api"))) {
handle_hue_api(&path);
} else
#endif // USE_EMULATION
{
String message = F("File Not Found\n\nURI: ");
message += webServer->uri();
message += F("\nMethod: ");
message += ( webServer->method() == HTTP_GET ) ? F("GET") : F("POST");
message += F("\nArguments: ");
message += webServer->args();
message += F("\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 ((HTTP_MANAGER == _httpflag) && !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