3.9.21 20170224
* Add ajax to web root page and web console (#79)
* Add commands SwitchMode1..4 and enable user switches 2, 3 and 4 (#84,
#88)
* Fix MQTT upgrade when webserver is active
This commit is contained in:
arendst 2017-02-24 18:17:48 +01:00
parent 3f73e48bfa
commit 3fefb9b931
8 changed files with 193 additions and 124 deletions

View File

@ -1,7 +1,7 @@
## Sonoff-Tasmota ## Sonoff-Tasmota
Provide ESP8266 based Sonoff by [iTead Studio](https://www.itead.cc/) and ElectroDragon IoT Relay with Serial, Web and MQTT control allowing 'Over the Air' or OTA firmware updates using Arduino IDE. Provide ESP8266 based Sonoff by [iTead Studio](https://www.itead.cc/) and ElectroDragon IoT Relay with Serial, Web and MQTT control allowing 'Over the Air' or OTA firmware updates using Arduino IDE.
Current version is **3.9.20** - See ```sonoff/_releasenotes.ino``` for change information. Current version is **3.9.21** - See ```sonoff/_releasenotes.ino``` for change information.
- This version provides all (Sonoff) modules in one file and starts up with Sonoff Basic. - This version provides all (Sonoff) modules in one file and starts up with Sonoff Basic.
- Once uploaded select module using the configuration webpage or the commands ```Modules``` and ```Module```. - Once uploaded select module using the configuration webpage or the commands ```Modules``` and ```Module```.

Binary file not shown.

View File

@ -1,4 +1,9 @@
/* 3.9.20 20170221 /* 3.9.21 20170224
* Add ajax to web root page and web console (#79)
* Add commands SwitchMode1..4 and enable user switches 2, 3 and 4 (#84, #88)
* Fix MQTT upgrade when webserver is active
*
* 3.9.20 20170221
* Add minimal basic authentication to Web Admin mode (#87) * Add minimal basic authentication to Web Admin mode (#87)
* Fix Hue and add HSB support (#89) * Fix Hue and add HSB support (#89)
* *

View File

@ -114,7 +114,7 @@ struct SYSCFG {
uint8_t power; uint8_t power;
uint8_t ledstate; uint8_t ledstate;
uint8_t switchmode; uint8_t ex_switchmode; // Not used since 3.9.21
char domoticz_in_topic[33]; char domoticz_in_topic[33];
char domoticz_out_topic[33]; char domoticz_out_topic[33];
@ -185,5 +185,7 @@ struct SYSCFG {
uint8_t emulation; uint8_t emulation;
char web_password[33]; char web_password[33];
uint8_t switchmode[4];
} sysCfg; } sysCfg;

View File

@ -367,7 +367,7 @@ void CFG_DefaultSet2()
sysCfg.poweronstate = APP_POWERON_STATE; sysCfg.poweronstate = APP_POWERON_STATE;
sysCfg.pulsetime = APP_PULSETIME; sysCfg.pulsetime = APP_PULSETIME;
sysCfg.ledstate = APP_LEDSTATE; sysCfg.ledstate = APP_LEDSTATE;
sysCfg.switchmode = SWITCH_MODE; // sysCfg.switchmode = SWITCH_MODE;
sysCfg.blinktime = APP_BLINKTIME; sysCfg.blinktime = APP_BLINKTIME;
sysCfg.blinkcount = APP_BLINKCOUNT; sysCfg.blinkcount = APP_BLINKCOUNT;
sysCfg.sleep = APP_SLEEP; sysCfg.sleep = APP_SLEEP;
@ -376,6 +376,7 @@ void CFG_DefaultSet2()
strlcpy(sysCfg.domoticz_out_topic, DOMOTICZ_OUT_TOPIC, sizeof(sysCfg.domoticz_out_topic)); strlcpy(sysCfg.domoticz_out_topic, DOMOTICZ_OUT_TOPIC, sizeof(sysCfg.domoticz_out_topic));
sysCfg.domoticz_update_timer = DOMOTICZ_UPDATE_TIMER; sysCfg.domoticz_update_timer = DOMOTICZ_UPDATE_TIMER;
for (byte i = 0; i < 4; i++) { for (byte i = 0; i < 4; i++) {
sysCfg.switchmode[i] = SWITCH_MODE;
sysCfg.domoticz_relay_idx[i] = 0; sysCfg.domoticz_relay_idx[i] = 0;
sysCfg.domoticz_key_idx[i] = 0; sysCfg.domoticz_key_idx[i] = 0;
sysCfg.domoticz_switch_idx[i] = 0; sysCfg.domoticz_switch_idx[i] = 0;
@ -545,7 +546,7 @@ void CFG_Migrate_Part2()
sysCfg.hlw_kWhdoy = sysCfg2.hlw_kWhdoy; sysCfg.hlw_kWhdoy = sysCfg2.hlw_kWhdoy;
} }
if (sysCfg2.version >= 0x02001200) { // 2.0.18 if (sysCfg2.version >= 0x02001200) { // 2.0.18
sysCfg.switchmode = sysCfg2.switchmode; sysCfg.switchmode[0] = sysCfg2.switchmode;
} }
if (sysCfg2.version >= 0x02010000) { // 2.1.0 if (sysCfg2.version >= 0x02010000) { // 2.1.0
strlcpy(sysCfg.mqtt_fingerprint, sysCfg2.mqtt_fingerprint, sizeof(sysCfg.mqtt_fingerprint)); strlcpy(sysCfg.mqtt_fingerprint, sysCfg2.mqtt_fingerprint, sizeof(sysCfg.mqtt_fingerprint));
@ -629,6 +630,9 @@ void CFG_Delta()
if (sysCfg.version < 0x03091301) { if (sysCfg.version < 0x03091301) {
strlcpy(sysCfg.web_password, WEB_PASSWORD, sizeof(sysCfg.web_password)); strlcpy(sysCfg.web_password, WEB_PASSWORD, sizeof(sysCfg.web_password));
} }
if (sysCfg.version < 0x03091500) {
for (byte i = 0; i < 4; i++) sysCfg.switchmode[i] = sysCfg.ex_switchmode;
}
sysCfg.version = VERSION; sysCfg.version = VERSION;
} }

View File

@ -10,7 +10,7 @@
* ==================================================== * ====================================================
*/ */
#define VERSION 0x03091400 // 3.9.20 #define VERSION 0x03091500 // 3.9.21
//#define BE_MINIMAL // Compile a minimal version if upgrade memory gets tight (still 404k) //#define BE_MINIMAL // Compile a minimal version if upgrade memory gets tight (still 404k)
// To be used as step 1. Next step is compile and use desired version // To be used as step 1. Next step is compile and use desired version
@ -118,7 +118,7 @@ enum emul_t {EMUL_NONE, EMUL_WEMO, EMUL_HUE, EMUL_MAX};
#define STATES 10 // loops per second #define STATES 10 // loops per second
#define SYSLOG_TIMER 600 // Seconds to restore syslog_level #define SYSLOG_TIMER 600 // Seconds to restore syslog_level
#define SERIALLOG_TIMER 600 // Seconds to disable SerialLog #define SERIALLOG_TIMER 600 // Seconds to disable SerialLog
#define OTA_ATTEMPTS 5 // Number of times to try fetching the new firmware #define OTA_ATTEMPTS 10 // Number of times to try fetching the new firmware
#define INPUT_BUFFER_SIZE 100 // Max number of characters in serial buffer #define INPUT_BUFFER_SIZE 100 // Max number of characters in serial buffer
#define TOPSZ 60 // Max number of characters in topic string #define TOPSZ 60 // Max number of characters in topic string
@ -213,12 +213,14 @@ int state = 0; // State per second flag
int mqttflag = 2; // MQTT connection messages flag int mqttflag = 2; // MQTT connection messages flag
int otaflag = 0; // OTA state flag int otaflag = 0; // OTA state flag
int otaok = 0; // OTA result int otaok = 0; // OTA result
byte otaretry = OTA_ATTEMPTS; // OTA retry counter
int restartflag = 0; // Sonoff restart flag int restartflag = 0; // Sonoff restart flag
int wificheckflag = WIFI_RESTART; // Wifi state flag int wificheckflag = WIFI_RESTART; // Wifi state flag
int uptime = 0; // Current uptime in hours int uptime = 0; // Current uptime in hours
int tele_period = 0; // Tele period timer int tele_period = 0; // Tele period timer
String Log[MAX_LOG_LINES]; // Web log buffer String Log[MAX_LOG_LINES]; // Web log buffer
byte logidx = 0; // Index in Web log buffer byte logidx = 0; // Index in Web log buffer
byte logajaxflg = 0; // Reset web console log
byte Maxdevice = MAX_DEVICE; // Max number of devices supported byte Maxdevice = MAX_DEVICE; // Max number of devices supported
int status_update_timer = 0; // Refresh initial status int status_update_timer = 0; // Refresh initial status
uint16_t pulse_timer = 0; // Power off timer uint16_t pulse_timer = 0; // Power off timer
@ -891,11 +893,11 @@ void mqttDataCb(char* topic, byte* data, unsigned int data_len)
} }
snprintf_P(svalue, sizeof(svalue), PSTR("{\"FriendlyName%d\":\"%s\"}"), index, sysCfg.friendlyname[index -1]); snprintf_P(svalue, sizeof(svalue), PSTR("{\"FriendlyName%d\":\"%s\"}"), index, sysCfg.friendlyname[index -1]);
} }
else if (swt_flg && !strcmp(type,"SWITCHMODE")) { else if (swt_flg && !strcmp(type,"SWITCHMODE") && (index > 0) && (index <= 4)) {
if ((data_len > 0) && (payload >= 0) && (payload < MAX_SWITCH_OPTION)) { if ((data_len > 0) && (payload >= 0) && (payload < MAX_SWITCH_OPTION)) {
sysCfg.switchmode = payload; sysCfg.switchmode[index -1] = payload;
} }
snprintf_P(svalue, sizeof(svalue), PSTR("{\"SwitchMode\":%d}"), sysCfg.switchmode); snprintf_P(svalue, sizeof(svalue), PSTR("{\"SwitchMode%d\":%d}"), index, sysCfg.switchmode[index-1]);
} }
#ifdef USE_WEBSERVER #ifdef USE_WEBSERVER
else if (!strcmp(type,"WEBSERVER")) { else if (!strcmp(type,"WEBSERVER")) {
@ -1184,7 +1186,7 @@ void mqttDataCb(char* topic, byte* data, unsigned int data_len)
snprintf_P(svalue, sizeof(svalue), PSTR("{\"Commands3\":\"%s%s, PulseTime, BlinkTime, BlinkCount"), (Maxdevice == 1) ? "Power, Light" : "Power1, Power2, Light1 Light2", (sysCfg.module != MOTOR) ? ", PowerOnState" : ""); snprintf_P(svalue, sizeof(svalue), PSTR("{\"Commands3\":\"%s%s, PulseTime, BlinkTime, BlinkCount"), (Maxdevice == 1) ? "Power, Light" : "Power1, Power2, Light1 Light2", (sysCfg.module != MOTOR) ? ", PowerOnState" : "");
#ifdef USE_WEBSERVER #ifdef USE_WEBSERVER
snprintf_P(svalue, sizeof(svalue), PSTR("%s, Weblog, Webserver, Emulation"), svalue); snprintf_P(svalue, sizeof(svalue), PSTR("%s, Weblog, Webserver, WebPassword, Emulation"), svalue);
#endif #endif
if (swt_flg) snprintf_P(svalue, sizeof(svalue), PSTR("%s, SwitchMode"), svalue); if (swt_flg) snprintf_P(svalue, sizeof(svalue), PSTR("%s, SwitchMode"), svalue);
#ifdef USE_I2C #ifdef USE_I2C
@ -1223,10 +1225,10 @@ void send_button_power(byte key, byte device, byte state)
char stopic[TOPSZ], svalue[TOPSZ], stemp1[10]; char stopic[TOPSZ], svalue[TOPSZ], stemp1[10];
if (device > Maxdevice) device = 1; if (!key && (device > Maxdevice)) device = 1;
snprintf_P(stemp1, sizeof(stemp1), PSTR("%d"), device); snprintf_P(stemp1, sizeof(stemp1), PSTR("%d"), device);
snprintf_P(stopic, sizeof(stopic), PSTR("%s/%s/%s%s"), snprintf_P(stopic, sizeof(stopic), PSTR("%s/%s/%s%s"),
SUB_PREFIX, (key) ? sysCfg.switch_topic : sysCfg.button_topic, sysCfg.mqtt_subtopic, (Maxdevice > 1) ? stemp1 : ""); SUB_PREFIX, (key) ? sysCfg.switch_topic : sysCfg.button_topic, sysCfg.mqtt_subtopic, (key || (Maxdevice > 1)) ? stemp1 : "");
if (state == 3) { if (state == 3) {
svalue[0] = '\0'; svalue[0] = '\0';
@ -1676,11 +1678,12 @@ void stateloop()
lastbutton[i] = button; lastbutton[i] = button;
} }
for (byte i = 0; i < Maxdevice; i++) if (pin[GPIO_SWT1 +i] < 99) { // for (byte i = 0; i < Maxdevice; i++) if (pin[GPIO_SWT1 +i] < 99) {
for (byte i = 0; i < 4; i++) if (pin[GPIO_SWT1 +i] < 99) {
button = digitalRead(pin[GPIO_SWT1 +i]); button = digitalRead(pin[GPIO_SWT1 +i]);
if (button != lastwallswitch[i]) { if (button != lastwallswitch[i]) {
switchflag = 3; switchflag = 3;
switch (sysCfg.switchmode) { switch (sysCfg.switchmode[i]) {
case TOGGLE: case TOGGLE:
switchflag = 2; // Toggle switchflag = 2; // Toggle
break; break;
@ -1698,9 +1701,9 @@ void stateloop()
} }
if (switchflag < 3) { if (switchflag < 3) {
if (sysCfg.mqtt_enabled && mqttClient.connected() && strcmp(sysCfg.switch_topic,"0")) { if (sysCfg.mqtt_enabled && mqttClient.connected() && strcmp(sysCfg.switch_topic,"0")) {
send_button_power(1, i +1, switchflag); // Execute commend via MQTT send_button_power(1, i +1, switchflag); // Execute commend via MQTT
} else { } else {
do_cmnd_power(i +1, switchflag); // Execute command internally do_cmnd_power(i +1, switchflag); // Execute command internally (if i < Maxdevice)
} }
} }
lastwallswitch[i] = button; lastwallswitch[i] = button;
@ -1730,30 +1733,34 @@ void stateloop()
case (STATES/10)*2: case (STATES/10)*2:
if (otaflag) { if (otaflag) {
otaflag--; otaflag--;
if (otaflag <= 0) { if (otaflag == 2){
otaflag = 12; otaretry = OTA_ATTEMPTS;
ESPhttpUpdate.rebootOnUpdate(false); ESPhttpUpdate.rebootOnUpdate(false);
sl_blank(1); sl_blank(1);
// Try multiple times to get the update, in case we have a transient issue. }
// e.g. Someone issued "cmnd/sonoffs/update 1" and all the devices if (otaflag <= 0) {
// are hammering the OTAURL. #ifdef USE_WEBSERVER
for (byte i = 0; i < OTA_ATTEMPTS && !otaok; i++) { if (sysCfg.webserver) stopWebserver();
// Delay an increasing pseudo-random time for each interation. #endif // USE_WEBSERVER
// Starting at 0 (no delay) up to a maximum of OTA_ATTEMPTS-1 seconds. otaflag = 92;
delay((ESP.getChipId() % 1000) * i); otaok = 0;
otaretry--;
if (otaretry) {
// snprintf_P(log, sizeof(log), PSTR("OTA: Attempt %d"), OTA_ATTEMPTS - otaretry);
// addLog(LOG_LEVEL_INFO, log);
otaok = (ESPhttpUpdate.update(sysCfg.otaUrl) == HTTP_UPDATE_OK); otaok = (ESPhttpUpdate.update(sysCfg.otaUrl) == HTTP_UPDATE_OK);
if (!otaok) otaflag = 2;
} }
} }
if (otaflag == 10) { // Allow MQTT to reconnect if (otaflag == 90) { // Allow MQTT to reconnect
otaflag = 0; otaflag = 0;
if (otaok) { if (otaok) {
if ((sysCfg.module == SONOFF_TOUCH) || (sysCfg.module == SONOFF_4CH)) setFlashChipMode(1, 3); // DOUT - ESP8285 if ((sysCfg.module == SONOFF_TOUCH) || (sysCfg.module == SONOFF_4CH)) setFlashChipMode(1, 3); // DOUT - ESP8285
snprintf_P(svalue, sizeof(svalue), PSTR("Successful. Restarting")); snprintf_P(svalue, sizeof(svalue), PSTR("Successful. Restarting"));
restartflag = 2;
} else { } else {
sl_blank(0);
snprintf_P(svalue, sizeof(svalue), PSTR("Failed %s"), ESPhttpUpdate.getLastErrorString().c_str()); snprintf_P(svalue, sizeof(svalue), PSTR("Failed %s"), ESPhttpUpdate.getLastErrorString().c_str());
} }
restartflag = 2; // Restart anyway to keep memory clean webserver
mqtt_publish_topic_P(0, PSTR("UPGRADE"), svalue); mqtt_publish_topic_P(0, PSTR("UPGRADE"), svalue);
} }
} }

View File

@ -14,6 +14,9 @@ enum upins_t {
GPIO_WS2812, // WS2812 Led string GPIO_WS2812, // WS2812 Led string
GPIO_IRSEND, // IR remote GPIO_IRSEND, // IR remote
GPIO_SWT1, // User connected external switches GPIO_SWT1, // User connected external switches
GPIO_SWT2,
GPIO_SWT3,
GPIO_SWT4,
GPIO_SENSOR_END }; GPIO_SENSOR_END };
// Text in webpage Module Parameters and commands GPIOS and GPIO // Text in webpage Module Parameters and commands GPIOS and GPIO
@ -27,14 +30,14 @@ const char sensors[GPIO_SENSOR_END][9] PROGMEM = {
"I2C SDA", "I2C SDA",
"WS2812", "WS2812",
"IRremote", "IRremote",
"Switch" }; "Switch1",
"Switch2",
"Switch3",
"Switch4" };
// Programmer selectable GPIO functionality offset by user selectable GPIOs // Programmer selectable GPIO functionality offset by user selectable GPIOs
enum fpins_t { enum fpins_t {
GPIO_SWT2 = GPIO_SENSOR_END, GPIO_KEY1 = GPIO_SENSOR_END, // Button usually connected to GPIO0
GPIO_SWT3,
GPIO_SWT4,
GPIO_KEY1, // Button usually connected to GPIO0
GPIO_KEY2, GPIO_KEY2,
GPIO_KEY3, GPIO_KEY3,
GPIO_KEY4, GPIO_KEY4,
@ -107,14 +110,22 @@ typedef struct MYTMPLT {
const mytmplt modules[MAXMODULE] PROGMEM = { const mytmplt modules[MAXMODULE] PROGMEM = {
{ "Sonoff Basic", // Sonoff Basic { "Sonoff Basic", // Sonoff Basic
GPIO_KEY1, // GPIO00 Button GPIO_KEY1, // GPIO00 Button
0, 0, 0, // GPIO01 Serial RXD
0, // GPIO02
GPIO_USER, // GPIO03 Serial TXD and Optional sensor GPIO_USER, // GPIO03 Serial TXD and Optional sensor
GPIO_USER, // GPIO04 Optional sensor GPIO_USER, // GPIO04 Optional sensor
0, 0, 0, 0, 0, 0, 0, 0, // GPIO05
0, // GPIO06 (SD_CLK Flash)
0, // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT)
0, // GPIO08 (SD_DATA1 Flash QIO/DIO)
0, // GPIO09 (SD_DATA2 Flash QIO)
0, // GPIO10 (SD_DATA3 Flash QIO)
0, // GPIO11 (SD_CMD Flash)
GPIO_REL1, // GPIO12 Red Led and Relay (0 = Off, 1 = On) GPIO_REL1, // GPIO12 Red Led and Relay (0 = Off, 1 = On)
GPIO_LED1_INV, // GPIO13 Green Led (0 = On, 1 = Off) GPIO_LED1_INV, // GPIO13 Green Led (0 = On, 1 = Off)
GPIO_USER, // GPIO14 Optional sensor GPIO_USER, // GPIO14 Optional sensor
0, 0 0, // GPIO15
0 // GPIO16
}, },
{ "Sonoff RF", // Sonoff RF { "Sonoff RF", // Sonoff RF
GPIO_KEY1, // GPIO00 Button GPIO_KEY1, // GPIO00 Button

View File

@ -53,34 +53,58 @@ const char HTTP_HEAD[] PROGMEM =
"document.getElementById('s1').value=l.innerText||l.textContent;" "document.getElementById('s1').value=l.innerText||l.textContent;"
"document.getElementById('p1').focus();" "document.getElementById('p1').focus();"
"}" "}"
"var sn=0;" "var x=null;" // Allow for abortion
"var lt;" // Enable clearTimeout
"function la(p){"
"var a='';"
"if(la.arguments.length==1){"
"a='?o='+p;"
"clearTimeout(lt);"
"}"
"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){"
"document.getElementById('l1').innerHTML=x.responseText;"
"}"
"};"
"x.open('GET','ay'+a,true);"
"x.send();"
"lt=setTimeout(la,2000);"
"}"
"var sn=0;" // Scroll position
"var id=99;" // Get most of weblog initially
"function l(){" "function l(){"
"var e=document.getElementById('t1');" "var e=document.getElementById('t1');"
"if(e.scrollTop>=sn){" "if(e.scrollTop>=sn){" // User scrolled back so no updates
"var x=new XMLHttpRequest();" "if(x!=null){x.abort();}" // Abort if no response within 2 seconds (happens on restart 1)
"x=new XMLHttpRequest();"
"x.onreadystatechange=function(){" "x.onreadystatechange=function(){"
"if(x.readyState==4&&x.status==200){" "if(x.readyState==4&&x.status==200){"
"var s1=x.responseText;" "var z,d;"
"if(e.value.length==0){" "d=x.responseXML;"
"e.value=s1;" "id=d.getElementsByTagName('i')[0].childNodes[0].nodeValue;"
"}else{" "if(d.getElementsByTagName('j')[0].childNodes[0].nodeValue==0){e.value=\"\";}"
"var s2=e.value.slice(e.value.lastIndexOf(\"\\n\")+2);" "z=d.getElementsByTagName('l')[0].childNodes;"
"var p2=s1.search(s2);" "if(z.length>0){e.value+=z[0].nodeValue;}"
"if(p2>-1){"
"e.value=e.value.replace(s2,s1.slice(p2));"
"}else{"
"e.value=e.value+\"\\n\"+s1;"
"}"
"}"
"e.scrollTop=99999;" "e.scrollTop=99999;"
"sn=e.scrollTop;" "sn=e.scrollTop;"
"}" "}"
"};" "};"
"x.open('GET','ax',true);" "x.open('GET','ax?c2='+id,true);"
"x.send();" "x.send();"
"}" "}"
"setTimeout(l,2000);" "setTimeout(l,2000);"
"}" "}"
"function lc(){"
"var e=document.getElementById('c1');"
// "if(x!=null){x.abort();}" // Abort if no response within 2 seconds (happens on restart 1)
"var x=new XMLHttpRequest();"
"x.open('GET','ax?c1='+e.value+'&c2='+id,true);"
"x.send();"
"e.value=\"\";"
"return false;"
"}"
"</script>" "</script>"
"<style>" "<style>"
"div,fieldset,input,select{padding:5px;font-size:1em;}" "div,fieldset,input,select{padding:5px;font-size:1em;}"
@ -210,7 +234,7 @@ const char HTTP_FORM_UPG[] PROGMEM =
"<div id='f2' name='f2' style='display:none;text-align:center;'><b>Upload started ...</b></div>"; "<div id='f2' name='f2' style='display:none;text-align:center;'><b>Upload started ...</b></div>";
const char HTTP_FORM_CMND[] PROGMEM = const char HTTP_FORM_CMND[] PROGMEM =
"<br/><textarea readonly id='t1' name='t1' cols='99' wrap='off'></textarea><br/><br/>" "<br/><textarea readonly id='t1' name='t1' cols='99' wrap='off'></textarea><br/><br/>"
"<form method='post' action='cs'>" "<form method='get' onsubmit='return lc();'>"
"<input style='width:98%' id='c1' name='c1' length='99' placeholder='Enter command' autofocus><br/>" "<input style='width:98%' id='c1' name='c1' length='99' placeholder='Enter command' autofocus><br/>"
// "<br/><button type='submit'>Send command</button>" // "<br/><button type='submit'>Send command</button>"
"</form>"; "</form>";
@ -261,6 +285,7 @@ void startWebserver(int type, IPAddress ipweb)
webServer->on("/cm", handleCmnd); webServer->on("/cm", handleCmnd);
webServer->on("/cs", handleConsole); webServer->on("/cs", handleConsole);
webServer->on("/ax", handleAjax); webServer->on("/ax", handleAjax);
webServer->on("/ay", handleAjax2);
webServer->on("/in", handleInfo); webServer->on("/in", handleInfo);
webServer->on("/rb", handleRestart); webServer->on("/rb", handleRestart);
webServer->on("/fwlink", handleRoot); // Microsoft captive portal. Maybe not needed. Might be handled by notFound handler. webServer->on("/fwlink", handleRoot); // Microsoft captive portal. Maybe not needed. Might be handled by notFound handler.
@ -276,6 +301,7 @@ void startWebserver(int type, IPAddress ipweb)
#endif // USE_EMULATION #endif // USE_EMULATION
webServer->onNotFound(handleNotFound); webServer->onNotFound(handleNotFound);
} }
logajaxflg = 0;
webServer->begin(); // Web server start webServer->begin(); // Web server start
} }
if (_httpflag != type) { if (_httpflag != type) {
@ -356,73 +382,81 @@ void handleRoot()
if (_httpflag == HTTP_MANAGER) { if (_httpflag == HTTP_MANAGER) {
handleWifi0(); handleWifi0();
} else { } else {
String page = FPSTR(HTTP_HEAD); String page = FPSTR(HTTP_HEAD);
// page.replace("<meta", "<meta http-equiv=\"refresh\" content=\"4; URL=/\"><meta"); // Fails Edge (asks for reload)
// page.replace("</script>", "setTimeout(function(){window.location.reload(1);},4000);</script>"); // Repeats POST on All
page.replace("</script>", "setTimeout(function(){window.location.replace(\"/\");},4000);</script>"); // OK on All
page.replace("{v}", "Main menu"); page.replace("{v}", "Main menu");
page.replace("<body>", "<body onload='la()'>");
page += F("<div id='l1' name='l1'></div>");
if (Maxdevice) { if (Maxdevice) {
if (strlen(webServer->arg("o").c_str())) {
do_cmnd_power(atoi(webServer->arg("o").c_str()), 2);
}
page += F("<table style='width:100%'><tr>"); page += F("<table style='width:100%'><tr>");
for (byte idx = 1; idx <= Maxdevice; idx++) { for (byte idx = 1; idx <= Maxdevice; idx++) {
page += F("<td style='width:"); page += F("<td style='width:");
page += String(100 / Maxdevice); page += String(100 / Maxdevice);
page += F("%'><form action='/?o="); page += F("%'><button onclick='la(");
page += String(idx); page += String(idx);
page += F("' method='post'><div style='text-align:center;font-weight:bold;font-size:"); page += F(");'>Toggle");
page += String(70 - (Maxdevice * 8));
page += F("px'>");
page += (power & (0x01 << (idx -1))) ? "ON" : "OFF";
page += F("</div><br/><button>Toggle");
if (Maxdevice > 1) { if (Maxdevice > 1) {
page += F(" "); page += F(" ");
page += String(idx); page += String(idx);
} }
page += F("</button></form></td>"); page += F("</button></td>");
} }
page += F("</tr></table><br/>"); page += F("</tr></table>");
} }
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
if (tpage.length() > 0) {
page += F("<table style='width:100%'>");
page += tpage;
page += F("</table><br/>");
}
if (_httpflag == HTTP_ADMIN) { if (_httpflag == HTTP_ADMIN) {
page += FPSTR(HTTP_BTN_MENU1); page += FPSTR(HTTP_BTN_MENU1);
page += FPSTR(HTTP_BTN_RSTRT); page += FPSTR(HTTP_BTN_RSTRT);
} }
showPage(page); showPage(page);
}
}
void handleAjax2()
{
if (strlen(webServer->arg("o").c_str())) do_cmnd_power(atoi(webServer->arg("o").c_str()), 2);
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 #ifdef USE_DS18x20
if (pin[GPIO_DSB] < 99) {
tpage += ds18x20_webPresent();
ds18x20_search(); // Check for changes in sensors number ds18x20_search(); // Check for changes in sensors number
ds18x20_convert(); // Start Conversion, takes up to one second ds18x20_convert(); // Start Conversion, takes up to one second
#endif // USE_DS18x20
} }
#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("<table style='width:100%'>");
page += tpage;
page += F("</table>");
}
if (Maxdevice) {
page += F("<table style='width:100%'><tr>");
for (byte idx = 1; idx <= Maxdevice; idx++) {
page += F("<td style='width:{1%'><div style='text-align:center;font-weight:bold;font-size:{2px'>{3</div></td>");
page.replace("{1", String(100 / Maxdevice));
page.replace("{2", String(70 - (Maxdevice * 8)));
page.replace("{3", (power & (0x01 << (idx -1))) ? "ON" : "OFF");
}
page += F("</tr></table>");
}
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 httpUser()
@ -1119,15 +1153,9 @@ void handleCmnd()
void handleConsole() void handleConsole()
{ {
if (httpUser()) return; if (httpUser()) return;
char svalue[MESSZ];
addLog_P(LOG_LEVEL_DEBUG, PSTR("HTTP: Handle console")); addLog_P(LOG_LEVEL_DEBUG, PSTR("HTTP: Handle console"));
if (strlen(webServer->arg("c1").c_str())) {
snprintf_P(svalue, sizeof(svalue), webServer->arg("c1").c_str());
do_cmnd(svalue);
}
String page = FPSTR(HTTP_HEAD); String page = FPSTR(HTTP_HEAD);
page.replace("{v}", "Console"); page.replace("{v}", "Console");
page.replace("<body>", "<body onload='l()'>"); page.replace("<body>", "<body onload='l()'>");
@ -1139,29 +1167,44 @@ void handleConsole()
void handleAjax() void handleAjax()
{ {
if (httpUser()) return; if (httpUser()) return;
String message = ""; char svalue[MESSZ], log[LOGSZ];
uint16_t size = 0; byte counter = 99;
int maxSize = ESP.getFreeHeap() - 6000; 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);
do_cmnd(svalue);
}
if (strlen(webServer->arg("c2").c_str())) counter = atoi(webServer->arg("c2").c_str());
byte counter = logidx; String message = F("<r><i>");
do { message += String(logidx);
counter--; message += F("</i><j>");
if (counter == 255) counter = MAX_LOG_LINES -1; message += String(logajaxflg);
size += Log[counter].length(); if (!logajaxflg) {
} while ((counter != logidx) && (size < maxSize)); counter = 99;
do { logajaxflg = 1;
if (Log[counter].length()) { }
if (message.length()) message += F("\n"); message += F("</j><l>");
message += Log[counter]; if (counter != logidx) {
} if (counter == 99) counter = logidx;
counter++; do {
if (counter > MAX_LOG_LINES -1) counter = 0; if (Log[counter].length()) {
} while (counter != logidx); message += F("\n");
message += Log[counter];
}
counter++;
if (counter > MAX_LOG_LINES -1) counter = 0;
} while (counter != logidx);
}
message += F("</l></r>");
webServer->sendHeader("Cache-Control", "no-cache, no-store, must-revalidate"); webServer->sendHeader("Cache-Control", "no-cache, no-store, must-revalidate");
webServer->sendHeader("Pragma", "no-cache"); webServer->sendHeader("Pragma", "no-cache");
webServer->sendHeader("Expires", "-1"); webServer->sendHeader("Expires", "-1");
webServer->send(200, "text/plain", message); webServer->send(200, "text/xml", message);
} }
void handleInfo() void handleInfo()
@ -1274,9 +1317,6 @@ void handleRestart()
restartflag = 2; restartflag = 2;
} }
/********************************************************************************************/
/********************************************************************************************/ /********************************************************************************************/
void handleNotFound() void handleNotFound()