diff --git a/README.md b/README.md index 712d4a5fc..9c28ed7cd 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ ## 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. -Current version is **4.0.7** - See [sonoff/_releasenotes.ino](https://github.com/arendst/Sonoff-Tasmota/blob/master/sonoff/_releasenotes.ino) for change information. +Current version is **4.0.8** - See [sonoff/_releasenotes.ino](https://github.com/arendst/Sonoff-Tasmota/blob/master/sonoff/_releasenotes.ino) for change information. - 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```. diff --git a/api/arduino/sonoff.ino.bin b/api/arduino/sonoff.ino.bin index 9709ac87c..03dc74681 100644 Binary files a/api/arduino/sonoff.ino.bin and b/api/arduino/sonoff.ino.bin differ diff --git a/sonoff/_releasenotes.ino b/sonoff/_releasenotes.ino index 93b3152f8..1420097ed 100644 --- a/sonoff/_releasenotes.ino +++ b/sonoff/_releasenotes.ino @@ -1,4 +1,10 @@ -/* 4.0.7 20170319 +/* 4.0.8 20170321 + * Fix entering non-numeric webpassword + * Force selection between TLS or Webserver due to memory restraint (#240) + * Allow entering empty string using "0" for selected commands (#242) + * Fix exception when posting commands to web console containing % (#250) + * + * 4.0.7 20170319 * Increased Sonoff Led PWM frequency from 432 to 1000 * Fix possible watch dog reboot after changing module type on web page * Fix reporting of GPIO usage from web page diff --git a/sonoff/settings.h b/sonoff/settings.h index f5a9f5b10..4575f98e5 100644 --- a/sonoff/settings.h +++ b/sonoff/settings.h @@ -145,11 +145,17 @@ struct SYSCFG { uint16_t hlw_mkwh; // MaxEnergy uint16_t hlw_mkwhs; // MaxEnergyStart + // 3.0.6 uint16_t ex_pulsetime; // Not used since 4.0.4 + + // 3.1.1 uint8_t poweronstate; + + // 3.1.6 uint16_t blinktime; uint16_t blinkcount; + // 3.2.4 uint16_t ws_pixels; uint8_t ws_red; uint8_t ws_green; @@ -162,18 +168,22 @@ struct SYSCFG { uint8_t ws_width; uint16_t ws_wakeup; + // 3.2.5 char friendlyname[4][33]; + + // 3.2.8 char switch_topic[33]; byte mqtt_switch_retain; uint8_t mqtt_enabled; + + // 3.2.12 uint8_t sleep; + // 3.9.3 uint16_t domoticz_switch_idx[4]; uint16_t domoticz_sensor_idx[12]; - uint8_t module; mytmplt my_module; - uint16_t led_pixels; uint8_t led_color[5]; uint8_t led_table; @@ -184,13 +194,20 @@ struct SYSCFG { uint8_t led_width; uint16_t led_wakeup; + // 3.9.7 uint8_t emulation; + // 3.9.20 char web_password[33]; + + // 3.9.21 uint8_t switchmode[4]; + // 4.0.4 char ntp_server[3][33]; uint16_t pulsetime[MAX_PULSETIMERS]; + + // 4.0.7 uint16_t pwmvalue[5]; } sysCfg; diff --git a/sonoff/sonoff.ino b/sonoff/sonoff.ino index 279f41f4d..d85cf8344 100644 --- a/sonoff/sonoff.ino +++ b/sonoff/sonoff.ino @@ -12,9 +12,9 @@ //#define ALLOW_MIGRATE_TO_V3 #ifdef ALLOW_MIGRATE_TO_V3 - #define VERSION 0x03091E00 // 3.9.30 + #define VERSION 0x03091F00 // 3.9.31 #else - #define VERSION 0x04000700 // 4.0.7 + #define VERSION 0x04000800 // 4.0.8 #endif // ALLOW_MIGRATE_TO_V3 enum log_t {LOG_LEVEL_NONE, LOG_LEVEL_ERROR, LOG_LEVEL_INFO, LOG_LEVEL_DEBUG, LOG_LEVEL_DEBUG_MORE, LOG_LEVEL_ALL}; @@ -115,7 +115,7 @@ enum emul_t {EMUL_NONE, EMUL_WEMO, EMUL_HUE, EMUL_MAX}; #define MAX_PULSETIMERS 4 // Max number of supported pulse timers #define WS2812_MAX_LEDS 256 // Max number of LEDs -#define PWM_RANGE 1023 // 127..1023 but as Color is addressed by 8 bits it should be 255 for my code +#define PWM_RANGE 1023 // 255..1023 needs to be devisible by 256 #define PWM_FREQ 1000 // 100..1000 Hz led refresh #define MAX_POWER_HOLD 10 // Time in SECONDS to allow max agreed power (Pow) @@ -132,11 +132,7 @@ enum emul_t {EMUL_NONE, EMUL_WEMO, EMUL_HUE, EMUL_MAX}; #define INPUT_BUFFER_SIZE 100 // Max number of characters in serial buffer #define TOPSZ 60 // Max number of characters in topic string #define LOGSZ 128 // Max number of characters in log string -#ifdef USE_MQTT_TLS - #define MAX_LOG_LINES 10 // Max number of lines in weblog -#else - #define MAX_LOG_LINES 20 // Max number of lines in weblog -#endif +#define MAX_LOG_LINES 20 // Max number of lines in weblog #define APP_BAUDRATE 115200 // Default serial baudrate #define MAX_STATUS 11 // Max number of status lines @@ -144,8 +140,8 @@ enum emul_t {EMUL_NONE, EMUL_WEMO, EMUL_HUE, EMUL_MAX}; enum butt_t {PRESSED, NOT_PRESSED}; #include "support.h" // Global support -#include // MQTT +#include // MQTT #define MESSZ 360 // Max number of characters in JSON message string (4 x DS18x20 sensors) #if (MQTT_MAX_PACKET_SIZE -TOPSZ -7) < MESSZ // If the max message size is too small, throw an error at compile time // See pubsubclient.c line 359 @@ -620,7 +616,7 @@ boolean mqtt_command(boolean grpflg, char *type, uint16_t index, char *dataBuf, #ifdef USE_MQTT_TLS else if (!strcmp(type,"MQTTFINGERPRINT")) { if ((data_len > 0) && (data_len < sizeof(sysCfg.mqtt_fingerprint))) { - strlcpy(sysCfg.mqtt_fingerprint, (payload == 1) ? MQTT_FINGERPRINT : dataBuf, sizeof(sysCfg.mqtt_fingerprint)); + strlcpy(sysCfg.mqtt_fingerprint, (!strcmp(dataBuf,"0")) ? "" : (payload == 1) ? MQTT_FINGERPRINT : dataBuf, sizeof(sysCfg.mqtt_fingerprint)); restartflag = 2; } snprintf_P(svalue, ssvalue, PSTR("{\"MqttFingerprint\":\"%s\"}"), sysCfg.mqtt_fingerprint); @@ -635,14 +631,14 @@ boolean mqtt_command(boolean grpflg, char *type, uint16_t index, char *dataBuf, } else if (!strcmp(type,"MQTTUSER")) { if ((data_len > 0) && (data_len < sizeof(sysCfg.mqtt_user))) { - strlcpy(sysCfg.mqtt_user, (payload == 1) ? MQTT_USER : dataBuf, sizeof(sysCfg.mqtt_user)); + strlcpy(sysCfg.mqtt_user, (!strcmp(dataBuf,"0")) ? "" : (payload == 1) ? MQTT_USER : dataBuf, sizeof(sysCfg.mqtt_user)); restartflag = 2; } snprintf_P(svalue, ssvalue, PSTR("[\"MqttUser\":\"%s\"}"), sysCfg.mqtt_user); } else if (!strcmp(type,"MQTTPASSWORD")) { if ((data_len > 0) && (data_len < sizeof(sysCfg.mqtt_pwd))) { - strlcpy(sysCfg.mqtt_pwd, (payload == 1) ? MQTT_PASS : dataBuf, sizeof(sysCfg.mqtt_pwd)); + strlcpy(sysCfg.mqtt_pwd, (!strcmp(dataBuf,"0")) ? "" : (payload == 1) ? MQTT_PASS : dataBuf, sizeof(sysCfg.mqtt_pwd)); restartflag = 2; } snprintf_P(svalue, ssvalue, PSTR("{\"MqttPassword\":\"%s\"}"), sysCfg.mqtt_pwd); @@ -672,7 +668,7 @@ boolean mqtt_command(boolean grpflg, char *type, uint16_t index, char *dataBuf, for(i = 0; i <= data_len; i++) if ((dataBuf[i] == '/') || (dataBuf[i] == '+') || (dataBuf[i] == '#') || (dataBuf[i] == ' ')) dataBuf[i] = '_'; if (!strcmp(dataBuf, MQTTClient)) payload = 1; - strlcpy(sysCfg.button_topic, (payload == 1) ? sysCfg.mqtt_topic : dataBuf, sizeof(sysCfg.button_topic)); + strlcpy(sysCfg.button_topic, (!strcmp(dataBuf,"0")) ? "" : (payload == 1) ? sysCfg.mqtt_topic : dataBuf, sizeof(sysCfg.button_topic)); } snprintf_P(svalue, ssvalue, PSTR("{\"ButtonTopic\":\"%s\"}"), sysCfg.button_topic); } @@ -681,7 +677,7 @@ boolean mqtt_command(boolean grpflg, char *type, uint16_t index, char *dataBuf, for(i = 0; i <= data_len; i++) if ((dataBuf[i] == '/') || (dataBuf[i] == '+') || (dataBuf[i] == '#') || (dataBuf[i] == ' ')) dataBuf[i] = '_'; if (!strcmp(dataBuf, MQTTClient)) payload = 1; - strlcpy(sysCfg.switch_topic, (payload == 1) ? sysCfg.mqtt_topic : dataBuf, sizeof(sysCfg.switch_topic)); + strlcpy(sysCfg.switch_topic, (!strcmp(dataBuf,"0")) ? "" : (payload == 1) ? sysCfg.mqtt_topic : dataBuf, sizeof(sysCfg.switch_topic)); } snprintf_P(svalue, ssvalue, PSTR("{\"SwitchTopic\":\"%s\"}"), sysCfg.switch_topic); } @@ -1038,7 +1034,7 @@ void mqttDataCb(char* topic, byte* data, unsigned int data_len) } else if (!strcmp(type,"NTPSERVER") && (index > 0) && (index <= 3)) { if ((data_len > 0) && (data_len < sizeof(sysCfg.ntp_server[0]))) { - strlcpy(sysCfg.ntp_server[index -1], (payload == 1) ? (index==1)?NTP_SERVER1:(index==2)?NTP_SERVER2:NTP_SERVER3 : dataBuf, sizeof(sysCfg.ntp_server[0])); + strlcpy(sysCfg.ntp_server[index -1], (!strcmp(dataBuf,"0")) ? "" : (payload == 1) ? (index==1)?NTP_SERVER1:(index==2)?NTP_SERVER2:NTP_SERVER3 : dataBuf, sizeof(sysCfg.ntp_server[0])); for (i = 0; i < strlen(sysCfg.ntp_server[index -1]); i++) if (sysCfg.ntp_server[index -1][i] == ',') sysCfg.ntp_server[index -1][i] = '.'; restartflag = 2; } @@ -1128,11 +1124,7 @@ void mqttDataCb(char* topic, byte* data, unsigned int data_len) } else if (!strcmp(type,"WEBPASSWORD")) { if ((data_len > 0) && (data_len < sizeof(sysCfg.web_password))) { - if (payload == 0) { - sysCfg.web_password[0] = 0; // No password - } else { - strlcpy(sysCfg.web_password, (payload == 1) ? WEB_PASSWORD : dataBuf, sizeof(sysCfg.web_password)); - } + strlcpy(sysCfg.web_password, (!strcmp(dataBuf,"0")) ? "" : (payload == 1) ? WEB_PASSWORD : dataBuf, sizeof(sysCfg.web_password)); } snprintf_P(svalue, sizeof(svalue), PSTR("{\"WebPassword\":\"%s\"}"), sysCfg.web_password); } @@ -1757,7 +1749,7 @@ void stateloop() } else { flag = (multipress == 1); } - if (flag && sysCfg.mqtt_enabled && mqttClient.connected() && strcmp(sysCfg.button_topic, "0")) { + if (flag && sysCfg.mqtt_enabled && mqttClient.connected() && (strlen(sysCfg.button_topic) != 0) && strcmp(sysCfg.button_topic, "0")) { send_button_power(0, multipress, 2); // Execute command via MQTT using ButtonTopic to sync external clients } else { if ((multipress == 1) || (multipress == 2)) { @@ -1780,7 +1772,7 @@ void stateloop() for (byte i = 1; i < Maxdevice; i++) if (pin[GPIO_KEY1 +i] < 99) { button = digitalRead(pin[GPIO_KEY1 +i]); if ((button == PRESSED) && (lastbutton[i] == NOT_PRESSED)) { - if (sysCfg.mqtt_enabled && mqttClient.connected() && strcmp(sysCfg.button_topic, "0")) { + if (sysCfg.mqtt_enabled && mqttClient.connected() && (strlen(sysCfg.button_topic) != 0) && strcmp(sysCfg.button_topic, "0")) { send_button_power(0, i +1, 2); // Execute commend via MQTT } else { do_cmnd_power(i +1, 2); // Execute command internally @@ -1789,7 +1781,6 @@ void stateloop() lastbutton[i] = button; } -// 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]); if (button != lastwallswitch[i]) { @@ -1811,7 +1802,7 @@ void stateloop() if ((button == NOT_PRESSED) && (lastwallswitch[i] == PRESSED)) switchflag = 2; // Toggle with releasing pushbutton from Gnd } if (switchflag < 3) { - if (sysCfg.mqtt_enabled && mqttClient.connected() && strcmp(sysCfg.switch_topic,"0")) { + if (sysCfg.mqtt_enabled && mqttClient.connected() && (strlen(sysCfg.switch_topic) != 0) && strcmp(sysCfg.switch_topic, "0")) { send_button_power(1, i +1, switchflag); // Execute commend via MQTT } else { do_cmnd_power(i +1, switchflag); // Execute command internally (if i < Maxdevice) diff --git a/sonoff/user_config.h b/sonoff/user_config.h index 79dbf6f80..0e32b81cb 100644 --- a/sonoff/user_config.h +++ b/sonoff/user_config.h @@ -157,6 +157,10 @@ * No user configurable items below \*********************************************************************************************/ +#if defined(USE_MQTT_TLS) && defined(USE_WEBSERVER) + #error "Select either USE_MQTT_TLS or USE_WEBSERVER as there is just not enough memory to play with" +#endif + #if (ARDUINO < 10610) #error "This software is supported with Arduino IDE starting from 1.6.10 and ESP8266 Release 2.3.0" #endif diff --git a/sonoff/webserver.ino b/sonoff/webserver.ino index 988f71447..07ca6db85 100644 --- a/sonoff/webserver.ino +++ b/sonoff/webserver.ino @@ -103,7 +103,8 @@ const char HTTP_SCRIPT_CONSOL[] PROGMEM = "t=document.getElementById('t1');" "if(p==1){" "c=document.getElementById('c1');" - "o='&c1='+c.value;" +// "o='&c1='+c.value;" + "o='&c1='+encodeURI(c.value);" "c.value='';" "t.scrollTop=sn;" "}" @@ -691,8 +692,8 @@ void handleMqtt() 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("{m4}", (sysCfg.mqtt_user[0] == '\0')?"0":sysCfg.mqtt_user); + page.replace("{m5}", (sysCfg.mqtt_pwd[0] == '\0')?"0":sysCfg.mqtt_pwd); page.replace("{m6}", sysCfg.mqtt_topic); page += FPSTR(HTTP_FORM_END); page += FPSTR(HTTP_BTN_CONF); @@ -829,8 +830,8 @@ void handleSave() 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_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); @@ -855,8 +856,7 @@ void handleSave() 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'; + 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.mqtt_enabled = webServer->hasArg("b1"); #ifdef USE_EMULATION sysCfg.emulation = (!strlen(webServer->arg("b2").c_str())) ? 0 : atoi(webServer->arg("b2").c_str()); @@ -1152,7 +1152,8 @@ void handleCmnd() 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), 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); @@ -1211,7 +1212,7 @@ void handleAjax() byte cflg = 1, counter = 99; if (strlen(webServer->arg("c1").c_str())) { - snprintf_P(svalue, sizeof(svalue), 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;