diff --git a/README.md b/README.md index eab749266..c53f5c62e 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 **5.11.1g** - See [sonoff/_releasenotes.ino](https://github.com/arendst/Sonoff-Tasmota/blob/development/sonoff/_releasenotes.ino) for change information. +Current version is **5.11.1h** - See [sonoff/_releasenotes.ino](https://github.com/arendst/Sonoff-Tasmota/blob/development/sonoff/_releasenotes.ino) for change information. ### ATTENTION All versions diff --git a/sonoff/_releasenotes.ino b/sonoff/_releasenotes.ino index eee381749..1f7526392 100644 --- a/sonoff/_releasenotes.ino +++ b/sonoff/_releasenotes.ino @@ -1,4 +1,9 @@ -/* 5.11.1g +/* 5.11.1h + * Rewrite webserver argument processing gaining 5k code space (#1705) + * Redesign weblog storage (#1730) + * Fix command SetOption20 (#1741) + * + * 5.11.1g * Add support for PMS5003 and PMS7003 particle concentration sensor * Reinstate console weblog to 20 lines after some webpage rewrite * Add command SetOption20 to allow update of Dimmer/Color/Ct without turning power on (#1719) diff --git a/sonoff/language/de-DE.h b/sonoff/language/de-DE.h index 55710e89c..09877893c 100644 --- a/sonoff/language/de-DE.h +++ b/sonoff/language/de-DE.h @@ -381,7 +381,6 @@ #define D_PARTICALS_BEYOND "Particals" // sonoff_template.h -// Max string length is 8 characters including suffixes #define D_SENSOR_NONE "None" #define D_SENSOR_DHT11 "DHT11" #define D_SENSOR_AM2301 "AM2301" diff --git a/sonoff/language/en-GB.h b/sonoff/language/en-GB.h index e5ccdaed6..77db37009 100644 --- a/sonoff/language/en-GB.h +++ b/sonoff/language/en-GB.h @@ -381,7 +381,6 @@ #define D_PARTICALS_BEYOND "Particals" // sonoff_template.h -// Max string length is 8 characters including suffixes #define D_SENSOR_NONE "None" #define D_SENSOR_DHT11 "DHT11" #define D_SENSOR_AM2301 "AM2301" diff --git a/sonoff/language/es-AR.h b/sonoff/language/es-AR.h index 0ef008469..e54098edf 100644 --- a/sonoff/language/es-AR.h +++ b/sonoff/language/es-AR.h @@ -381,7 +381,6 @@ #define D_PARTICALS_BEYOND "Partículas" // sonoff_template.h -// Max string length is 8 characters including suffixes #define D_SENSOR_NONE "Ninguno" #define D_SENSOR_DHT11 "DHT11" #define D_SENSOR_AM2301 "AM2301" diff --git a/sonoff/language/fr-FR.h b/sonoff/language/fr-FR.h index 7e655c21c..78afc0677 100644 --- a/sonoff/language/fr-FR.h +++ b/sonoff/language/fr-FR.h @@ -381,7 +381,6 @@ #define D_PARTICALS_BEYOND "Particules" // sonoff_template.h -// Max string length is 8 characters including suffixes #define D_SENSOR_NONE "None" #define D_SENSOR_DHT11 "DHT11" #define D_SENSOR_AM2301 "AM2301" diff --git a/sonoff/language/it-IT.h b/sonoff/language/it-IT.h index 59ae8533d..6ab442ca1 100644 --- a/sonoff/language/it-IT.h +++ b/sonoff/language/it-IT.h @@ -381,7 +381,6 @@ #define D_PARTICALS_BEYOND "Particelle" // sonoff_template.h -// Max string length is 8 characters including suffixes #define D_SENSOR_NONE "None" #define D_SENSOR_DHT11 "DHT11" #define D_SENSOR_AM2301 "AM2301" diff --git a/sonoff/language/nl-NL.h b/sonoff/language/nl-NL.h index f33646579..6f9d39f72 100644 --- a/sonoff/language/nl-NL.h +++ b/sonoff/language/nl-NL.h @@ -381,7 +381,6 @@ #define D_PARTICALS_BEYOND "Stofdeeltjes" // sonoff_template.h -// Max string length is 8 characters including suffixes #define D_SENSOR_NONE "Geen" #define D_SENSOR_DHT11 "DHT11" #define D_SENSOR_AM2301 "AM2301" diff --git a/sonoff/language/pl-PL.h b/sonoff/language/pl-PL.h index c449da210..97c0bf51f 100644 --- a/sonoff/language/pl-PL.h +++ b/sonoff/language/pl-PL.h @@ -381,7 +381,6 @@ #define D_PARTICALS_BEYOND "Particals" // sonoff_template.h -// Max string length is 8 characters including suffixes #define D_SENSOR_NONE "Brak" #define D_SENSOR_DHT11 "DHT11" #define D_SENSOR_AM2301 "AM2301" diff --git a/sonoff/language/zh-CN.h b/sonoff/language/zh-CN.h index 929da46d5..76c271ee9 100644 --- a/sonoff/language/zh-CN.h +++ b/sonoff/language/zh-CN.h @@ -381,7 +381,6 @@ #define D_PARTICALS_BEYOND "颗粒物直径大于" // sonoff_template.h -// Max string length is 8 characters including suffixes #define D_SENSOR_NONE "None" #define D_SENSOR_DHT11 "DHT11" #define D_SENSOR_AM2301 "AM2301" diff --git a/sonoff/sonoff.h b/sonoff/sonoff.h index ff741090c..65462781a 100644 --- a/sonoff/sonoff.h +++ b/sonoff/sonoff.h @@ -87,15 +87,13 @@ typedef unsigned long power_t; // Power (Relay) type #define TOPSZ 100 // Max number of characters in topic string #define LOGSZ 400 // Max number of characters in log #define MIN_MESSZ 893 // Min number of characters in MQTT message + #ifdef USE_MQTT_TLS - #define MAX_LOG_LINES 10 // Max number of lines in weblog + #define WEB_LOG_SIZE 2000 // Max number of characters in weblog #else -// #ifdef ARDUINO_ESP8266_RELEASE_2_3_0 - #define MAX_LOG_LINES 20 // Max number of lines in weblog -// #else -// #define MAX_LOG_LINES 13 // Max number of lines in weblog (less due to more memory usage which prohibits full webpage load) -// #endif + #define WEB_LOG_SIZE 4000 // Max number of characters in weblog #endif + #define MAX_BACKLOG 16 // Max number of commands in backlog (chk backlog_index and backlog_pointer code) #define MIN_BACKLOG_DELAY 2 // Minimal backlog delay in 0.1 seconds diff --git a/sonoff/sonoff.ino b/sonoff/sonoff.ino index cf211650d..76ad700de 100644 --- a/sonoff/sonoff.ino +++ b/sonoff/sonoff.ino @@ -25,7 +25,7 @@ - Select IDE Tools - Flash Size: "1M (no SPIFFS)" ====================================================*/ -#define VERSION 0x050B0107 // 5.11.1g +#define VERSION 0x050B0108 // 5.11.1h // Location specific includes #include // Arduino_Esp8266 version information (ARDUINO_ESP8266_RELEASE and ARDUINO_ESP8266_RELEASE_2_3_0) @@ -190,10 +190,10 @@ boolean mdns_begun = false; char my_version[33]; // Composed version string char my_hostname[33]; // Composed Wifi hostname char mqtt_client[33]; // Composed MQTT Clientname -char serial_in_buffer[INPUT_BUFFER_SIZE + 2]; // Receive buffer +char serial_in_buffer[INPUT_BUFFER_SIZE + 2]; // Receive buffer char mqtt_data[MESSZ]; // MQTT publish buffer and web page ajax buffer char log_data[LOGSZ]; // Logging -String web_log[MAX_LOG_LINES]; // Web log buffer +char web_log[WEB_LOG_SIZE] = {'\0'}; // Web log buffer String backlog[MAX_BACKLOG]; // Command backlog /********************************************************************************************/ @@ -999,7 +999,7 @@ void MqttDataCallback(char* topic, byte* data, unsigned int data_len) // type = NULL; // } } - else if ((CMND_SETOPTION == command_code) && ((index >= 0) && (index <= 19)) || ((index > 31) && (index <= P_MAX_PARAM8 +31))) { + else if ((CMND_SETOPTION == command_code) && ((index >= 0) && (index <= 20)) || ((index > 31) && (index <= P_MAX_PARAM8 +31))) { if (index <= 31) { ptype = 0; // SetOption0 .. 31 } else { @@ -1152,8 +1152,8 @@ void MqttDataCallback(char* topic, byte* data, unsigned int data_len) snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,"), mqtt_data); } jsflg = 1; - snprintf_P(stemp1, sizeof(stemp1), kSensors[Settings.my_gp.io[i]]); - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s\"" D_CMND_GPIO "%d\":\"%d (%s)\""), mqtt_data, i, Settings.my_gp.io[i], stemp1); + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s\"" D_CMND_GPIO "%d\":\"%d (%s)\""), + mqtt_data, i, Settings.my_gp.io[i], GetTextIndexed(stemp1, sizeof(stemp1), Settings.my_gp.io[i], kSensorNames)); } } if (jsflg) { @@ -1170,8 +1170,7 @@ void MqttDataCallback(char* topic, byte* data, unsigned int data_len) snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,"), mqtt_data); } jsflg = 1; - snprintf_P(stemp1, sizeof(stemp1), kSensors[i]); - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s\"%d (%s)\""), mqtt_data, i, stemp1); + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s\"%d (%s)\""), mqtt_data, i, GetTextIndexed(stemp1, sizeof(stemp1), i, kSensorNames)); if ((strlen(mqtt_data) > (LOGSZ - TOPSZ)) || (i == GPIO_SENSOR_END -1)) { snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s]}"), mqtt_data); MqttPublishPrefixTopic_P(RESULT_OR_STAT, type); diff --git a/sonoff/sonoff_template.h b/sonoff/sonoff_template.h index 97ef8a0bb..ef8001225 100644 --- a/sonoff/sonoff_template.h +++ b/sonoff/sonoff_template.h @@ -90,76 +90,6 @@ enum UserSelectablePins { GPIO_PMS5003, // Plantower PMS5003 Serial interface GPIO_SENSOR_END }; -// Text in webpage Module Parameters and commands GPIOS and GPIO -const char kSensors[GPIO_SENSOR_END][9] PROGMEM = { - D_SENSOR_NONE, - D_SENSOR_DHT11, - D_SENSOR_AM2301, - D_SENSOR_SI7021, - D_SENSOR_DS18X20, - D_SENSOR_I2C_SCL, - D_SENSOR_I2C_SDA, - D_SENSOR_WS2812, - D_SENSOR_IRSEND, - D_SENSOR_SWITCH "1", - D_SENSOR_SWITCH "2", - D_SENSOR_SWITCH "3", - D_SENSOR_SWITCH "4", - D_SENSOR_BUTTON "1", - D_SENSOR_BUTTON "2", - D_SENSOR_BUTTON "3", - D_SENSOR_BUTTON "4", - D_SENSOR_RELAY "1", - D_SENSOR_RELAY "2", - D_SENSOR_RELAY "3", - D_SENSOR_RELAY "4", - D_SENSOR_RELAY "5", - D_SENSOR_RELAY "6", - D_SENSOR_RELAY "7", - D_SENSOR_RELAY "8", - D_SENSOR_RELAY "1i", - D_SENSOR_RELAY "2i", - D_SENSOR_RELAY "3i", - D_SENSOR_RELAY "4i", - D_SENSOR_RELAY "5i", - D_SENSOR_RELAY "6i", - D_SENSOR_RELAY "7i", - D_SENSOR_RELAY "8i", - D_SENSOR_PWM "1", - D_SENSOR_PWM "2", - D_SENSOR_PWM "3", - D_SENSOR_PWM "4", - D_SENSOR_PWM "5", - D_SENSOR_COUNTER "1", - D_SENSOR_COUNTER "2", - D_SENSOR_COUNTER "3", - D_SENSOR_COUNTER "4", - D_SENSOR_PWM "1i", - D_SENSOR_PWM "2i", - D_SENSOR_PWM "3i", - D_SENSOR_PWM "4i", - D_SENSOR_PWM "5i", - D_SENSOR_IRRECV, - D_SENSOR_LED "1", - D_SENSOR_LED "2", - D_SENSOR_LED "3", - D_SENSOR_LED "4", - D_SENSOR_LED "1i", - D_SENSOR_LED "2i", - D_SENSOR_LED "3i", - D_SENSOR_LED "4i", - D_SENSOR_MHZ_TX, - D_SENSOR_MHZ_RX, - D_SENSOR_PZEM_TX, - D_SENSOR_PZEM_RX, - D_SENSOR_SAIR_TX, - D_SENSOR_SAIR_RX, - D_SENSOR_SPI_CS, - D_SENSOR_SPI_DC, - D_SENSOR_BACKLIGHT, - D_SENSOR_PMS5003 -}; - // Programmer selectable GPIO functionality offset by user selectable GPIOs enum ProgramSelectablePins { GPIO_RXD = GPIO_SENSOR_END, // Serial interface @@ -177,6 +107,30 @@ enum ProgramSelectablePins { GPIO_USER, // User configurable GPIO_MAX }; +// Text in webpage Module Parameters and commands GPIOS and GPIO +const char kSensorNames[] PROGMEM = + D_SENSOR_NONE "|" + D_SENSOR_DHT11 "|" D_SENSOR_AM2301 "|" D_SENSOR_SI7021 "|" + D_SENSOR_DS18X20 "|" + D_SENSOR_I2C_SCL "|" D_SENSOR_I2C_SDA "|" + D_SENSOR_WS2812 "|" + D_SENSOR_IRSEND "|" + D_SENSOR_SWITCH "1|" D_SENSOR_SWITCH "2|" D_SENSOR_SWITCH "3|" D_SENSOR_SWITCH "4|" + D_SENSOR_BUTTON "1|" D_SENSOR_BUTTON "2|" D_SENSOR_BUTTON "3|" D_SENSOR_BUTTON "4|" + D_SENSOR_RELAY "1|" D_SENSOR_RELAY "2|" D_SENSOR_RELAY "3|" D_SENSOR_RELAY "4|" D_SENSOR_RELAY "5|" D_SENSOR_RELAY "6|" D_SENSOR_RELAY "7|" D_SENSOR_RELAY "8|" + D_SENSOR_RELAY "1i|" D_SENSOR_RELAY "2i|" D_SENSOR_RELAY "3i|" D_SENSOR_RELAY "4i|" D_SENSOR_RELAY "5i|" D_SENSOR_RELAY "6i|" D_SENSOR_RELAY "7i|" D_SENSOR_RELAY "8i|" + D_SENSOR_PWM "1|" D_SENSOR_PWM "2|" D_SENSOR_PWM "3|" D_SENSOR_PWM "4|" D_SENSOR_PWM "5|" + D_SENSOR_COUNTER "1|" D_SENSOR_COUNTER "2|" D_SENSOR_COUNTER "3|" D_SENSOR_COUNTER "4|" + D_SENSOR_PWM "1i|" D_SENSOR_PWM "2i|" D_SENSOR_PWM "3i|" D_SENSOR_PWM "4i|" D_SENSOR_PWM "5i|" + D_SENSOR_IRRECV "|" + D_SENSOR_LED "1|" D_SENSOR_LED "2|" D_SENSOR_LED "3|" D_SENSOR_LED "4|" + D_SENSOR_LED "1i|" D_SENSOR_LED "2i|" D_SENSOR_LED "3i|" D_SENSOR_LED "4i|" + D_SENSOR_MHZ_TX "|" D_SENSOR_MHZ_RX "|" + D_SENSOR_PZEM_TX "|" D_SENSOR_PZEM_RX "|" + D_SENSOR_SAIR_TX "|" D_SENSOR_SAIR_RX "|" + D_SENSOR_SPI_CS "|" D_SENSOR_SPI_DC "|" D_SENSOR_BACKLIGHT "|" + D_SENSOR_PMS5003; + /********************************************************************************************/ // Supported hardware modules diff --git a/sonoff/support.ino b/sonoff/support.ino index 48b218386..0b7a2996b 100644 --- a/sonoff/support.ino +++ b/sonoff/support.ino @@ -145,9 +145,53 @@ Decoding 14 results #endif // DEBUG_THEO /*********************************************************************************************\ - * General + * Miscellaneous \*********************************************************************************************/ +#ifdef ARDUINO_ESP8266_RELEASE_2_3_0 +// Functions not available in 2.3.0 + +// http://clc-wiki.net/wiki/C_standard_library:string.h:memchr +void* memchr(const void* ptr, int value, size_t num) +{ + unsigned char *p = (unsigned char*)ptr; + while (num--) { + if (*p != (unsigned char)value) { + p++; + } else { + return p; + } + } + return 0; +} + +// http://clc-wiki.net/wiki/C_standard_library:string.h:strspn +// Get span until any character in string +size_t strcspn(const char *str1, const char *str2) +{ + size_t ret = 0; + while (*str1) { + if (strchr(str2, *str1)) { // Slow + return ret; + } else { + str1++; + ret++; + } + } + return ret; +} +#endif // ARDUINO_ESP8266_RELEASE_2_3_0 + +// Get span until single character in string +size_t strchrspn(const char *str1, int character) +{ + size_t ret = 0; + char *start = (char*)str1; + char *end = strchr(str1, character); + if (end) ret = end - start; + return ret; +} + char* dtostrfd(double number, unsigned char prec, char *s) { return dtostrf(number, 1, prec, s); @@ -254,6 +298,122 @@ char* GetPowerDevice(char* dest, uint8_t idx, size_t size) return GetPowerDevice(dest, idx, size, 0); } +float ConvertTemp(float c) +{ + float result = c; + + if (!isnan(c) && Settings.flag.temperature_conversion) { + result = c * 1.8 + 32; // Fahrenheit + } + return result; +} + +char TempUnit() +{ + return (Settings.flag.temperature_conversion) ? 'F' : 'C'; +} + +double FastPrecisePow(double a, double b) +{ + // https://martin.ankerl.com/2012/01/25/optimized-approximative-pow-in-c-and-cpp/ + // calculate approximation with fraction of the exponent + int e = (int)b; + union { + double d; + int x[2]; + } u = { a }; + u.x[1] = (int)((b - e) * (u.x[1] - 1072632447) + 1072632447); + u.x[0] = 0; + // exponentiation by squaring with the exponent's integer part + // double r = u.d makes everything much slower, not sure why + double r = 1.0; + while (e) { + if (e & 1) { + r *= a; + } + a *= a; + e >>= 1; + } + return r * u.d; +} + +char* GetTextIndexed(char* destination, size_t destination_size, uint16_t index, const char* haystack) +{ + // Returns empty string if not found + // Returns text of found + char* write = destination; + const char* read = haystack; + + index++; + while (index--) { + size_t size = destination_size -1; + write = destination; + char ch = '.'; + while ((ch != '\0') && (ch != '|')) { + ch = pgm_read_byte(read++); + if (size && (ch != '|')) { + *write++ = ch; + size--; + } + } + if (0 == ch) { + if (index) { + write = destination; + } + break; + } + } + *write = '\0'; + return destination; +} + +int GetCommandCode(char* destination, size_t destination_size, const char* needle, const char* haystack) +{ + // Returns -1 of not found + // Returns index and command if found + int result = -1; + const char* read = haystack; + char* write = destination; + + while (true) { + result++; + size_t size = destination_size -1; + write = destination; + char ch = '.'; + while ((ch != '\0') && (ch != '|')) { + ch = pgm_read_byte(read++); + if (size && (ch != '|')) { + *write++ = ch; + size--; + } + } + *write = '\0'; + if (!strcasecmp(needle, destination)) { + break; + } + if (0 == ch) { + result = -1; + break; + } + } + return result; +} + +void SetSerialBaudrate(int baudrate) +{ + if (Serial.baudRate() != baudrate) { + if (seriallog_level) { + snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_APPLICATION D_SET_BAUDRATE_TO " %d"), baudrate); + AddLog(LOG_LEVEL_INFO); + } + delay(100); + Serial.flush(); + Serial.begin(baudrate); + delay(10); + Serial.println(); + } +} + /*********************************************************************************************\ * Wifi \*********************************************************************************************/ @@ -1115,127 +1275,6 @@ void RtcInit() TickerRtc.attach(1, RtcSecond); } -/*********************************************************************************************\ - * Miscellaneous -\*********************************************************************************************/ - -float ConvertTemp(float c) -{ - float result = c; - - if (!isnan(c) && Settings.flag.temperature_conversion) { - result = c * 1.8 + 32; // Fahrenheit - } - return result; -} - -char TempUnit() -{ - return (Settings.flag.temperature_conversion) ? 'F' : 'C'; -} - -double FastPrecisePow(double a, double b) -{ - // https://martin.ankerl.com/2012/01/25/optimized-approximative-pow-in-c-and-cpp/ - // calculate approximation with fraction of the exponent - int e = (int)b; - union { - double d; - int x[2]; - } u = { a }; - u.x[1] = (int)((b - e) * (u.x[1] - 1072632447) + 1072632447); - u.x[0] = 0; - // exponentiation by squaring with the exponent's integer part - // double r = u.d makes everything much slower, not sure why - double r = 1.0; - while (e) { - if (e & 1) { - r *= a; - } - a *= a; - e >>= 1; - } - return r * u.d; -} - -char* GetTextIndexed(char* destination, size_t destination_size, uint16_t index, const char* haystack) -{ - // Returns empty string if not found - // Returns text of found - char* write = destination; - const char* read = haystack; - - index++; - while (index--) { - size_t size = destination_size -1; - write = destination; - char ch = '.'; - while ((ch != '\0') && (ch != '|')) { - ch = pgm_read_byte(read++); - if (size && (ch != '|')) { - *write++ = ch; - size--; - } - } - if (0 == ch) { - if (index) { - write = destination; - } - break; - } - } - *write = '\0'; - return destination; -} - -int GetCommandCode(char* destination, size_t destination_size, const char* needle, const char* haystack) -{ - // Returns -1 of not found - // Returns index and command if found - int result = -1; - const char* read = haystack; - char* write = destination; - size_t maxcopy = (strlen(needle) > destination_size) ? destination_size : strlen(needle); - - while (true) { - result++; - size_t size = destination_size -1; - write = destination; - char ch = '.'; - while ((ch != '\0') && (ch != '|')) { - ch = pgm_read_byte(read++); - if (size && (ch != '|')) { - *write++ = ch; - size--; - } - } - *write = '\0'; - if (!strcasecmp(needle, destination)) { - break; - } - if (0 == ch) { - result = -1; - break; - } - } - return result; -} - -void SetSerialBaudrate(int baudrate) -{ - if (Serial.baudRate() != baudrate) { - if (seriallog_level) { - snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_APPLICATION D_SET_BAUDRATE_TO " %d"), baudrate); - AddLog(LOG_LEVEL_INFO); - } - delay(100); - Serial.flush(); - Serial.begin(baudrate); - delay(10); - Serial.println(); - } -} - #ifndef USE_ADC_VCC /*********************************************************************************************\ * ADC support @@ -1294,6 +1333,32 @@ boolean Xsns02(byte function) * \*********************************************************************************************/ +#ifdef USE_WEBSERVER +void GetLog(byte idx, char** entry_pp, size_t* len_p) +{ + char* entry_p = NULL; + size_t len = 0; + + if (idx) { + char* it = web_log; + do { + byte cur_idx = *it; + it++; + size_t tmp = strchrspn(it, '\1'); + tmp++; // Skip terminating '\1' + if (cur_idx == idx) { // Found the requested entry + len = tmp; + entry_p = it; + break; + } + it += tmp; + } while (it < web_log + WEB_LOG_SIZE && *it != '\0'); + } + *entry_pp = entry_p; + *len_p = len; +} +#endif // USE_WEBSERVER + void Syslog() { // Destroys log_data @@ -1320,20 +1385,28 @@ void Syslog() void AddLog(byte loglevel) { - char mxtime[9]; // 13:45:21 + char mxtime[10]; // "13:45:21 " - snprintf_P(mxtime, sizeof(mxtime), PSTR("%02d" D_HOUR_MINUTE_SEPARATOR "%02d" D_MINUTE_SECOND_SEPARATOR "%02d"), RtcTime.hour, RtcTime.minute, RtcTime.second); + snprintf_P(mxtime, sizeof(mxtime), PSTR("%02d" D_HOUR_MINUTE_SEPARATOR "%02d" D_MINUTE_SECOND_SEPARATOR "%02d "), RtcTime.hour, RtcTime.minute, RtcTime.second); if (loglevel <= seriallog_level) { - Serial.printf("%s %s\n", mxtime, log_data); + Serial.printf("%s%s\n", mxtime, log_data); } #ifdef USE_WEBSERVER if (Settings.webserver && (loglevel <= Settings.weblog_level)) { - web_log[web_log_index] = String(mxtime) + " " + String(log_data); - web_log_index++; - if (web_log_index > MAX_LOG_LINES -1) { - web_log_index = 0; + // Delimited, zero-terminated buffer of log lines. + // Each entry has this format: [index][log data]['\1'] + if (!web_log_index) web_log_index++; // Index 0 is not allowed as it is the end of char string + while (web_log_index == web_log[0] || // If log already holds the next index, remove it + strlen(web_log) + strlen(log_data) + 13 > WEB_LOG_SIZE) // 13 = web_log_index + mxtime + '\1' + '\0' + { + char* it = web_log; + it++; // Skip web_log_index + it += strchrspn(it, '\1'); // Skip log line + it++; // Skip delimiting "\1" + memmove(web_log, it, WEB_LOG_SIZE -(it-web_log)); // Move buffer forward to remove oldest log line } + snprintf_P(web_log, sizeof(web_log), PSTR("%s%c%s%s\1"), web_log, web_log_index++, mxtime, log_data); } #endif // USE_WEBSERVER if ((WL_CONNECTED == WiFi.status()) && (loglevel <= syslog_level)) { diff --git a/sonoff/user_config.h b/sonoff/user_config.h index 80e91730c..1cf1d788b 100644 --- a/sonoff/user_config.h +++ b/sonoff/user_config.h @@ -216,7 +216,7 @@ #define CO2_LOW 800 // Below this CO2 value show green light (needs PWM or WS2812 RG(B) led and enable with SetOption18 1) #define CO2_HIGH 1200 // Above this CO2 value show red light (needs PWM or WS2812 RG(B) led and enable with SetOption18 1) -#define USE_PMS5003 // Add support for PMS5003 particle concentration sensor (+1k3 code) +#define USE_PMS5003 // Add support for PMS5003 and PMS7003 particle concentration sensor (+1k3 code) #define USE_IR_REMOTE // Send IR remote commands using library IRremoteESP8266 and ArduinoJson (+4k code, 0k3 mem, 48 iram) // #define USE_IR_HVAC // Support for HVAC system using IR (+2k code) diff --git a/sonoff/webserver.ino b/sonoff/webserver.ino index 64596e3c4..1256879be 100644 --- a/sonoff/webserver.ino +++ b/sonoff/webserver.ino @@ -105,7 +105,7 @@ const char HTTP_HEAD_STYLE[] PROGMEM = #endif const char HTTP_SCRIPT_CONSOL[] PROGMEM = "var sn=0;" // Scroll position - "var id=99;" // Get most of weblog initially + "var id=0;" // Get most of weblog initially "function l(p){" // Console log and command service "var c,o,t;" "clearTimeout(lt);" @@ -314,6 +314,14 @@ uint8_t upload_error = 0; uint8_t upload_file_type; uint8_t upload_progress_dot_count; +// Helper function to avoid code duplication (saves 4k Flash) +static void WebGetArg(const char* arg, char* out, size_t max) +{ + String s = WebServer->arg(arg); + strncpy(out, s.c_str(), max); + out[max-1] = '\0'; // Ensure terminating NUL +} + void StartWebserver(int type, IPAddress ipweb) { if (!webserver_state) { @@ -474,6 +482,13 @@ void HandleRoot() if ((Settings.web_password[0] != 0) && !(WebServer->hasArg("USER1")) && !(WebServer->hasArg("PASS1"))) { HandleWifiLogin(); } else { +/* + char tmp1[100]; + WebGetArg("USER1", tmp1, sizeof(tmp1)); + char tmp2[100]; + WebGetArg("PASS1", tmp2, sizeof(tmp2)); + if (!(Settings.web_password[0] != 0) || (!(!strcmp(tmp1, WEB_USERNAME) && !strcmp(tmp2, Settings.web_password)))) { +*/ if (!(Settings.web_password[0] != 0) || ((WebServer->arg("USER1") == WEB_USERNAME ) && (WebServer->arg("PASS1") == Settings.web_password ))) { HandleWifiConfiguration(); } else { @@ -536,69 +551,28 @@ void HandleRoot() void HandleAjaxStatusRefresh() { char svalue[80]; + char tmp[100]; - if (strlen(WebServer->arg("o").c_str())) { - ExecuteCommandPower(atoi(WebServer->arg("o").c_str()), POWER_TOGGLE); + WebGetArg("o", tmp, sizeof(tmp)); + if (strlen(tmp)) { + ExecuteCommandPower(atoi(tmp), POWER_TOGGLE); } - if (strlen(WebServer->arg("d").c_str())) { - snprintf_P(svalue, sizeof(svalue), PSTR(D_CMND_DIMMER " %s"), WebServer->arg("d").c_str()); + WebGetArg("d", tmp, sizeof(tmp)); + if (strlen(tmp)) { + snprintf_P(svalue, sizeof(svalue), PSTR(D_CMND_DIMMER " %s"), tmp); ExecuteCommand(svalue); } - if (strlen(WebServer->arg("t").c_str())) { - snprintf_P(svalue, sizeof(svalue), PSTR(D_CMND_COLORTEMPERATURE " %s"), WebServer->arg("t").c_str()); + WebGetArg("t", tmp, sizeof(tmp)); + if (strlen(tmp)) { + snprintf_P(svalue, sizeof(svalue), PSTR(D_CMND_COLORTEMPERATURE " %s"), tmp); ExecuteCommand(svalue); } - if (strlen(WebServer->arg("k").c_str())) { - snprintf_P(svalue, sizeof(svalue), PSTR(D_CMND_RFKEY "%s"), WebServer->arg("k").c_str()); + WebGetArg("k", tmp, sizeof(tmp)); + if (strlen(tmp)) { + snprintf_P(svalue, sizeof(svalue), PSTR(D_CMND_RFKEY "%s"), tmp); ExecuteCommand(svalue); } -/* - String page = ""; - mqtt_data[0] = '\0'; - XsnsCall(FUNC_WEB_APPEND); - if (strlen(mqtt_data)) { - page += FPSTR(HTTP_TABLE100); - page += mqtt_data; - page.replace(F("."), F(D_DECIMAL_SEPARATOR)); - page += F(""); - } - if (devices_present) { - page += FPSTR(HTTP_TABLE100); - page += F(""); - uint8_t fsize = (devices_present < 5) ? 70 - (devices_present * 8) : 32; - for (byte idx = 1; idx <= devices_present; idx++) { - snprintf_P(svalue, sizeof(svalue), PSTR("%d"), bitRead(power, idx -1)); - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s"), // {c} = %'>
")); - XsnsCall(FUNC_WEB_APPEND); - if (D_DECIMAL_SEPARATOR[0] != '.') { - for (int i = 0; i < strlen(mqtt_data); i++) { - if ('.' == mqtt_data[i]) { - mqtt_data[i] = D_DECIMAL_SEPARATOR[0]; - } - } - } - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s"), mqtt_data); - if (devices_present) { - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s"), mqtt_data); - uint8_t fsize = (devices_present < 5) ? 70 - (devices_present * 8) : 32; - for (byte idx = 1; idx <= devices_present; idx++) { - snprintf_P(svalue, sizeof(svalue), PSTR("%d"), bitRead(power, idx -1)); - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s"), // {c} = %'>
arg("w").c_str())) { - what = atoi(WebServer->arg("w").c_str()); + char tmp[100]; + WebGetArg("w", tmp, sizeof(tmp)); + if (strlen(tmp)) { + what = atoi(tmp); } switch (what) { case 1: - strlcpy(Settings.hostname, (!strlen(WebServer->arg("h").c_str())) ? WIFI_HOSTNAME : WebServer->arg("h").c_str(), sizeof(Settings.hostname)); + WebGetArg("h", tmp, sizeof(tmp)); + strlcpy(Settings.hostname, (!strlen(tmp)) ? WIFI_HOSTNAME : tmp, sizeof(Settings.hostname)); if (strstr(Settings.hostname,"%")) { strlcpy(Settings.hostname, WIFI_HOSTNAME, sizeof(Settings.hostname)); } - strlcpy(Settings.sta_ssid[0], (!strlen(WebServer->arg("s1").c_str())) ? STA_SSID1 : WebServer->arg("s1").c_str(), sizeof(Settings.sta_ssid[0])); - strlcpy(Settings.sta_ssid[1], (!strlen(WebServer->arg("s2").c_str())) ? STA_SSID2 : WebServer->arg("s2").c_str(), sizeof(Settings.sta_ssid[1])); -// strlcpy(Settings.sta_ssid[0], (!strlen(WebServer->arg("s1").c_str())) ? "" : WebServer->arg("s1").c_str(), sizeof(Settings.sta_ssid[0])); -// strlcpy(Settings.sta_ssid[1], (!strlen(WebServer->arg("s2").c_str())) ? "" : WebServer->arg("s2").c_str(), sizeof(Settings.sta_ssid[1])); - strlcpy(Settings.sta_pwd[0], (!strlen(WebServer->arg("p1").c_str())) ? "" : (strchr(WebServer->arg("p1").c_str(),'*')) ? Settings.sta_pwd[0] : WebServer->arg("p1").c_str(), sizeof(Settings.sta_pwd[0])); - strlcpy(Settings.sta_pwd[1], (!strlen(WebServer->arg("p2").c_str())) ? "" : (strchr(WebServer->arg("p2").c_str(),'*')) ? Settings.sta_pwd[1] : WebServer->arg("p2").c_str(), sizeof(Settings.sta_pwd[1])); + WebGetArg("s1", tmp, sizeof(tmp)); + strlcpy(Settings.sta_ssid[0], (!strlen(tmp)) ? STA_SSID1 : tmp, sizeof(Settings.sta_ssid[0])); + WebGetArg("s2", tmp, sizeof(tmp)); + strlcpy(Settings.sta_ssid[1], (!strlen(tmp)) ? STA_SSID2 : tmp, sizeof(Settings.sta_ssid[1])); +// WebGetArg("s1", tmp, sizeof(tmp)); +// strlcpy(Settings.sta_ssid[0], (!strlen(tmp)) ? "" : tmp, sizeof(Settings.sta_ssid[0])); +// WebGetArg("s2", tmp, sizeof(tmp)); +// strlcpy(Settings.sta_ssid[1], (!strlen(tmp)) ? "" : tmp, sizeof(Settings.sta_ssid[1])); + WebGetArg("p1", tmp, sizeof(tmp)); + strlcpy(Settings.sta_pwd[0], (!strlen(tmp)) ? "" : (strchr(tmp,'*')) ? Settings.sta_pwd[0] : tmp, sizeof(Settings.sta_pwd[0])); + WebGetArg("p2", tmp, sizeof(tmp)); + strlcpy(Settings.sta_pwd[1], (!strlen(tmp)) ? "" : (strchr(tmp,'*')) ? Settings.sta_pwd[1] : tmp, sizeof(Settings.sta_pwd[1])); snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_WIFI D_CMND_HOSTNAME " %s, " D_CMND_SSID "1 %s, " D_CMND_PASSWORD "1 %s, " D_CMND_SSID "2 %s, " D_CMND_PASSWORD "2 %s"), Settings.hostname, Settings.sta_ssid[0], Settings.sta_pwd[0], Settings.sta_ssid[1], Settings.sta_pwd[1]); AddLog(LOG_LEVEL_INFO); result += F("
" D_TRYING_TO_CONNECT "
"); break; case 2: - strlcpy(stemp, (!strlen(WebServer->arg("mt").c_str())) ? MQTT_TOPIC : WebServer->arg("mt").c_str(), sizeof(stemp)); + WebGetArg("mt", tmp, sizeof(tmp)); + strlcpy(stemp, (!strlen(tmp)) ? MQTT_TOPIC : tmp, sizeof(stemp)); MakeValidMqtt(0, stemp); - strlcpy(stemp2, (!strlen(WebServer->arg("mf").c_str())) ? MQTT_FULLTOPIC : WebServer->arg("mf").c_str(), sizeof(stemp2)); + WebGetArg("mf", tmp, sizeof(tmp)); + strlcpy(stemp2, (!strlen(tmp)) ? MQTT_FULLTOPIC : tmp, sizeof(stemp2)); MakeValidMqtt(1,stemp2); if ((strcmp(stemp, Settings.mqtt_topic)) || (strcmp(stemp2, Settings.mqtt_fulltopic))) { snprintf_P(mqtt_data, sizeof(mqtt_data), (Settings.flag.mqtt_offline) ? S_OFFLINE : ""); @@ -1065,28 +1049,39 @@ void HandleSaveSettings() } strlcpy(Settings.mqtt_topic, stemp, sizeof(Settings.mqtt_topic)); strlcpy(Settings.mqtt_fulltopic, stemp2, sizeof(Settings.mqtt_fulltopic)); - strlcpy(Settings.mqtt_host, (!strlen(WebServer->arg("mh").c_str())) ? MQTT_HOST : (!strcmp(WebServer->arg("mh").c_str(),"0")) ? "" : WebServer->arg("mh").c_str(), sizeof(Settings.mqtt_host)); - Settings.mqtt_port = (!strlen(WebServer->arg("ml").c_str())) ? MQTT_PORT : atoi(WebServer->arg("ml").c_str()); - strlcpy(Settings.mqtt_client, (!strlen(WebServer->arg("mc").c_str())) ? MQTT_CLIENT_ID : WebServer->arg("mc").c_str(), sizeof(Settings.mqtt_client)); - strlcpy(Settings.mqtt_user, (!strlen(WebServer->arg("mu").c_str())) ? MQTT_USER : (!strcmp(WebServer->arg("mu").c_str(),"0")) ? "" : WebServer->arg("mu").c_str(), sizeof(Settings.mqtt_user)); - strlcpy(Settings.mqtt_pwd, (!strlen(WebServer->arg("mp").c_str())) ? MQTT_PASS : (!strcmp(WebServer->arg("mp").c_str(),"0")) ? "" : WebServer->arg("mp").c_str(), sizeof(Settings.mqtt_pwd)); + WebGetArg("mh", tmp, sizeof(tmp)); + strlcpy(Settings.mqtt_host, (!strlen(tmp)) ? MQTT_HOST : (!strcmp(tmp,"0")) ? "" : tmp, sizeof(Settings.mqtt_host)); + WebGetArg("ml", tmp, sizeof(tmp)); + Settings.mqtt_port = (!strlen(tmp)) ? MQTT_PORT : atoi(tmp); + WebGetArg("mc", tmp, sizeof(tmp)); + strlcpy(Settings.mqtt_client, (!strlen(tmp)) ? MQTT_CLIENT_ID : tmp, sizeof(Settings.mqtt_client)); + WebGetArg("mu", tmp, sizeof(tmp)); + strlcpy(Settings.mqtt_user, (!strlen(tmp)) ? MQTT_USER : (!strcmp(tmp,"0")) ? "" : tmp, sizeof(Settings.mqtt_user)); + WebGetArg("mp", tmp, sizeof(tmp)); + strlcpy(Settings.mqtt_pwd, (!strlen(tmp)) ? MQTT_PASS : (!strcmp(tmp,"0")) ? "" : tmp, sizeof(Settings.mqtt_pwd)); snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_MQTT D_CMND_MQTTHOST " %s, " D_CMND_MQTTPORT " %d, " D_CMND_MQTTCLIENT " %s, " D_CMND_MQTTUSER " %s, " D_CMND_MQTTPASSWORD " %s, " D_CMND_TOPIC " %s, " D_CMND_FULLTOPIC " %s"), Settings.mqtt_host, Settings.mqtt_port, Settings.mqtt_client, Settings.mqtt_user, Settings.mqtt_pwd, Settings.mqtt_topic, Settings.mqtt_fulltopic); AddLog(LOG_LEVEL_INFO); break; case 3: - Settings.seriallog_level = (!strlen(WebServer->arg("ls").c_str())) ? SERIAL_LOG_LEVEL : atoi(WebServer->arg("ls").c_str()); - Settings.weblog_level = (!strlen(WebServer->arg("lw").c_str())) ? WEB_LOG_LEVEL : atoi(WebServer->arg("lw").c_str()); - Settings.syslog_level = (!strlen(WebServer->arg("ll").c_str())) ? SYS_LOG_LEVEL : atoi(WebServer->arg("ll").c_str()); + WebGetArg("ls", tmp, sizeof(tmp)); + Settings.seriallog_level = (!strlen(tmp)) ? SERIAL_LOG_LEVEL : atoi(tmp); + WebGetArg("lw", tmp, sizeof(tmp)); + Settings.weblog_level = (!strlen(tmp)) ? WEB_LOG_LEVEL : atoi(tmp); + WebGetArg("ll", tmp, sizeof(tmp)); + Settings.syslog_level = (!strlen(tmp)) ? SYS_LOG_LEVEL : atoi(tmp); syslog_level = Settings.syslog_level; syslog_timer = 0; - strlcpy(Settings.syslog_host, (!strlen(WebServer->arg("lh").c_str())) ? SYS_LOG_HOST : WebServer->arg("lh").c_str(), sizeof(Settings.syslog_host)); - Settings.syslog_port = (!strlen(WebServer->arg("lp").c_str())) ? SYS_LOG_PORT : atoi(WebServer->arg("lp").c_str()); - Settings.tele_period = (!strlen(WebServer->arg("lt").c_str())) ? TELE_PERIOD : atoi(WebServer->arg("lt").c_str()); + WebGetArg("lh", tmp, sizeof(tmp)); + strlcpy(Settings.syslog_host, (!strlen(tmp)) ? SYS_LOG_HOST : tmp, sizeof(Settings.syslog_host)); + WebGetArg("lp", tmp, sizeof(tmp)); + Settings.syslog_port = (!strlen(tmp)) ? SYS_LOG_PORT : atoi(tmp); + WebGetArg("lt", tmp, sizeof(tmp)); + Settings.tele_period = (!strlen(tmp)) ? TELE_PERIOD : atoi(tmp); if ((Settings.tele_period > 0) && (Settings.tele_period < 10)) { Settings.tele_period = 10; // Do not allow periods < 10 seconds } -snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_LOG D_CMND_SERIALLOG " %d, " D_CMND_WEBLOG " %d, " D_CMND_SYSLOG " %d, " D_CMND_LOGHOST " %s, " D_CMND_LOGPORT " %d, " D_CMND_TELEPERIOD " %d"), + snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_LOG D_CMND_SERIALLOG " %d, " D_CMND_WEBLOG " %d, " D_CMND_SYSLOG " %d, " D_CMND_LOGHOST " %s, " D_CMND_LOGPORT " %d, " D_CMND_TELEPERIOD " %d"), Settings.seriallog_level, Settings.weblog_level, Settings.syslog_level, Settings.syslog_host, Settings.syslog_port, Settings.tele_period); AddLog(LOG_LEVEL_INFO); break; @@ -1096,21 +1091,28 @@ snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_LOG D_CMND_SERIALLOG " %d, " D break; #endif // USE_DOMOTICZ case 5: - strlcpy(Settings.web_password, (!strlen(WebServer->arg("p1").c_str())) ? "" : (strchr(WebServer->arg("p1").c_str(),'*')) ? Settings.web_password : WebServer->arg("p1").c_str(), sizeof(Settings.web_password)); + WebGetArg("p1", tmp, sizeof(tmp)); + strlcpy(Settings.web_password, (!strlen(tmp)) ? "" : (strchr(tmp,'*')) ? Settings.web_password : tmp, sizeof(Settings.web_password)); Settings.flag.mqtt_enabled = WebServer->hasArg("b1"); #ifdef USE_EMULATION - Settings.flag2.emulation = (!strlen(WebServer->arg("b2").c_str())) ? 0 : atoi(WebServer->arg("b2").c_str()); + WebGetArg("b2", tmp, sizeof(tmp)); + Settings.flag2.emulation = (!strlen(tmp)) ? 0 : atoi(tmp); #endif // USE_EMULATION - strlcpy(Settings.friendlyname[0], (!strlen(WebServer->arg("a1").c_str())) ? FRIENDLY_NAME : WebServer->arg("a1").c_str(), sizeof(Settings.friendlyname[0])); - strlcpy(Settings.friendlyname[1], (!strlen(WebServer->arg("a2").c_str())) ? FRIENDLY_NAME"2" : WebServer->arg("a2").c_str(), sizeof(Settings.friendlyname[1])); - strlcpy(Settings.friendlyname[2], (!strlen(WebServer->arg("a3").c_str())) ? FRIENDLY_NAME"3" : WebServer->arg("a3").c_str(), sizeof(Settings.friendlyname[2])); - strlcpy(Settings.friendlyname[3], (!strlen(WebServer->arg("a4").c_str())) ? FRIENDLY_NAME"4" : WebServer->arg("a4").c_str(), sizeof(Settings.friendlyname[3])); + WebGetArg("a1", tmp, sizeof(tmp)); + strlcpy(Settings.friendlyname[0], (!strlen(tmp)) ? FRIENDLY_NAME : tmp, sizeof(Settings.friendlyname[0])); + WebGetArg("a2", tmp, sizeof(tmp)); + strlcpy(Settings.friendlyname[1], (!strlen(tmp)) ? FRIENDLY_NAME"2" : tmp, sizeof(Settings.friendlyname[1])); + WebGetArg("a3", tmp, sizeof(tmp)); + strlcpy(Settings.friendlyname[2], (!strlen(tmp)) ? FRIENDLY_NAME"3" : tmp, sizeof(Settings.friendlyname[2])); + WebGetArg("a4", tmp, sizeof(tmp)); + strlcpy(Settings.friendlyname[3], (!strlen(tmp)) ? FRIENDLY_NAME"4" : tmp, sizeof(Settings.friendlyname[3])); snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_OTHER D_MQTT_ENABLE " %s, " D_CMND_EMULATION " %d, " D_CMND_FRIENDLYNAME " %s, %s, %s, %s"), GetStateText(Settings.flag.mqtt_enabled), Settings.flag2.emulation, Settings.friendlyname[0], Settings.friendlyname[1], Settings.friendlyname[2], Settings.friendlyname[3]); AddLog(LOG_LEVEL_INFO); break; case 6: - byte new_module = (!strlen(WebServer->arg("g99").c_str())) ? MODULE : atoi(WebServer->arg("g99").c_str()); + WebGetArg("g99", tmp, sizeof(tmp)); + byte new_module = (!strlen(tmp)) ? MODULE : atoi(tmp); Settings.last_module = Settings.module; Settings.module = new_module; mytmplt cmodule; @@ -1122,7 +1124,8 @@ snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_LOG D_CMND_SERIALLOG " %d, " D } else { if (GPIO_USER == cmodule.gp.io[i]) { snprintf_P(stemp, sizeof(stemp), PSTR("g%d"), i); - Settings.my_gp.io[i] = (!strlen(WebServer->arg(stemp).c_str())) ? 0 : atoi(WebServer->arg(stemp).c_str()); + WebGetArg(stemp, tmp, sizeof(tmp)); + Settings.my_gp.io[i] = (!strlen(tmp)) ? 0 : atoi(tmp); gpios += F(", " D_GPIO ); gpios += String(i); gpios += F(" "); gpios += String(Settings.my_gp.io[i]); } } @@ -1133,7 +1136,8 @@ snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_LOG D_CMND_SERIALLOG " %d, " D break; } - restart = (!strlen(WebServer->arg("r").c_str())) ? 1 : atoi(WebServer->arg("r").c_str()); + WebGetArg("r", tmp, sizeof(tmp)); + restart = (!strlen(tmp)) ? 1 : atoi(tmp); if (restart) { String page = FPSTR(HTTP_HEAD); page.replace(F("{v}"), FPSTR(S_SAVE_CONFIGURATION)); @@ -1228,8 +1232,10 @@ void HandleUpgradeFirmwareStart() AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP D_UPGRADE_STARTED)); WifiConfigCounter(); - if (strlen(WebServer->arg("o").c_str())) { - snprintf_P(svalue, sizeof(svalue), PSTR(D_CMND_OTAURL " %s"), WebServer->arg("o").c_str()); + char tmp[100]; + WebGetArg("o", tmp, sizeof(tmp)); + if (strlen(tmp)) { + snprintf_P(svalue, sizeof(svalue), PSTR(D_CMND_OTAURL " %s"), tmp); ExecuteCommand(svalue); } @@ -1420,7 +1426,11 @@ void HandleHttpCommand() uint8_t valid = 1; if (Settings.web_password[0] != 0) { - if (!(!strcmp(WebServer->arg("user").c_str(),WEB_USERNAME) && !strcmp(WebServer->arg("password").c_str(),Settings.web_password))) { + char tmp1[100]; + WebGetArg("user", tmp1, sizeof(tmp1)); + char tmp2[100]; + WebGetArg("password", tmp2, sizeof(tmp2)); + if (!(!strcmp(tmp1, WEB_USERNAME) && !strcmp(tmp2, Settings.web_password))) { valid = 0; } } @@ -1428,9 +1438,11 @@ void HandleHttpCommand() String message = F("{\"" D_RSLT_WARNING "\":\""); if (valid) { byte curridx = web_log_index; - if (strlen(WebServer->arg("cmnd").c_str())) { -// snprintf_P(svalue, sizeof(svalue), WebServer->arg("cmnd").c_str()); // Processes FullTopic %p - strlcpy(svalue, WebServer->arg("cmnd").c_str(), sizeof(svalue)); // Fixed 5.8.0b + char tmp[100]; + WebGetArg("cmnd", tmp, sizeof(tmp)); + if (strlen(tmp)) { +// snprintf_P(svalue, sizeof(svalue), tmp); // Processes FullTopic %p + strlcpy(svalue, tmp, sizeof(svalue)); // Fixed 5.8.0b // byte syslog_now = syslog_level; // syslog_level = 0; // Disable UDP syslog to not trigger hardware WDT - Seems to work fine since 5.7.1d (global logging) ExecuteCommand(svalue); @@ -1441,19 +1453,23 @@ void HandleHttpCommand() byte counter = curridx; message = F("{"); do { - if (web_log[counter].length()) { + char* tmp; + size_t len; + GetLog(counter, &tmp, &len); + if (len) { // [14:49:36 MQTT: stat/wemos5/RESULT = {"POWER":"OFF"}] > [{"POWER":"OFF"}] - if (web_log[counter].indexOf("{") > 0) { // Is it a JSON message (and not only [15:26:08 MQT: stat/wemos5/POWER = O]) + char* JSON = (char*)memchr(tmp, '{', len); + if (JSON) { // Is it a JSON message (and not only [15:26:08 MQT: stat/wemos5/POWER = O]) if (message.length() > 1) { message += F(","); } - message += web_log[counter].substring(web_log[counter].indexOf("{")+1,web_log[counter].length()-1); + size_t JSONlen = len - (JSON - tmp); + strlcpy(mqtt_data, JSON +1, JSONlen -2); + message += mqtt_data; } } counter++; - if (counter > MAX_LOG_LINES -1) { - counter = 0; - } + if (!counter) counter++; // Skip 0 as it is not allowed } while (counter != web_log_index); message += F("}"); } else { @@ -1491,11 +1507,13 @@ void HandleAjaxConsoleRefresh() } char svalue[INPUT_BUFFER_SIZE]; // big to serve Backlog byte cflg = 1; - byte counter = 99; + byte counter = 0; // Initial start, should never be 0 again - if (strlen(WebServer->arg("c1").c_str())) { -// snprintf_P(svalue, sizeof(svalue), WebServer->arg("c1").c_str()); // Processes FullTopic %p - strlcpy(svalue, WebServer->arg("c1").c_str(), sizeof(svalue)); // Fixed 5.8.0b + char tmp[100]; + WebGetArg("c1", tmp, sizeof(tmp)); + if (strlen(tmp)) { +// snprintf_P(svalue, sizeof(svalue), tmp); // Processes FullTopic %p + strlcpy(svalue, tmp, sizeof(svalue)); // Fixed 5.8.0b snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_COMMAND "%s"), svalue); AddLog(LOG_LEVEL_INFO); // byte syslog_now = syslog_level; @@ -1504,39 +1522,45 @@ void HandleAjaxConsoleRefresh() // syslog_level = syslog_now; } - if (strlen(WebServer->arg("c2").c_str())) { - counter = atoi(WebServer->arg("c2").c_str()); + WebGetArg("c2", tmp, sizeof(tmp)); + if (strlen(tmp)) { + counter = atoi(tmp); } - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%d%d"), web_log_index, reset_web_log_flag); + byte last_reset_web_log_flag = reset_web_log_flag; String message = F("}9"); // Cannot load mqtt_data here as <> will be encoded by replacements below if (!reset_web_log_flag) { - counter = 99; + counter = 0; reset_web_log_flag = 1; } if (counter != web_log_index) { - if (99 == counter) { + if (!counter) { counter = web_log_index; cflg = 0; } do { - if (web_log[counter].length()) { + char* tmp; + size_t len; + GetLog(counter, &tmp, &len); + if (len) { if (cflg) { message += F("\n"); } else { cflg = 1; } - message += web_log[counter]; + strlcpy(mqtt_data, tmp, len); + message += mqtt_data; } counter++; - if (counter > MAX_LOG_LINES -1) { - counter = 0; - } + if (!counter) counter++; // Skip 0 as it is not allowed } while (counter != web_log_index); - message.replace(F("<"), F("%3C")); // XML encoding to fix blank console log in concert with javascript decodeURIComponent - message.replace(F(">"), F("%3E")); + // XML encoding to fix blank console log in concert with javascript decodeURIComponent + message.replace(F("%"), F("%25")); // Needs to be done first as otherwise the % in %26 will also be converted message.replace(F("&"), F("%26")); + message.replace(F("<"), F("%3C")); + message.replace(F(">"), F("%3E")); } + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%d%d"), web_log_index, last_reset_web_log_flag); message.replace(F("}9"), mqtt_data); // Save to load here message += F(""); WebServer->send(200, FPSTR(HDR_CTYPE_XML), message); @@ -1714,8 +1738,8 @@ boolean CaptivePortal() AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP D_REDIRECTED)); WebServer->sendHeader(F("Location"), String("http://") + WebServer->client().localIP().toString(), true); - WebServer->send(302, FPSTR(HDR_CTYPE_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 + WebServer->send(302, FPSTR(HDR_CTYPE_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; diff --git a/sonoff/xdrv_01_light.ino b/sonoff/xdrv_01_light.ino index 9971f8e53..b6a4fcd39 100644 --- a/sonoff/xdrv_01_light.ino +++ b/sonoff/xdrv_01_light.ino @@ -893,21 +893,6 @@ void LightHsbToRgb() /********************************************************************************************/ -void LightReplaceHsb(String *response) -{ - if (light_subtype > LST_COLDWARM) { - LightRgbToHsb(); - response->replace("{h}", String((uint16_t)(65535.0f * light_hue))); - response->replace("{s}", String((uint8_t)(254.0f * light_saturation))); - response->replace("{b}", String((uint8_t)(254.0f * light_brightness))); - } else { - response->replace("{h}", "0"); - response->replace("{s}", "0"); -// response->replace("{b}", String((uint8_t)(2.54f * (float)Settings.light_dimmer))); - response->replace("{b}", String((uint8_t)(0.01f * (float)Settings.light_dimmer))); - } -} - void LightGetHsb(float *hue, float *sat, float *bri) { if (light_subtype > LST_COLDWARM) { diff --git a/sonoff/xdrv_03_energy.ino b/sonoff/xdrv_03_energy.ino index 3377c390d..f543de8c7 100644 --- a/sonoff/xdrv_03_energy.ino +++ b/sonoff/xdrv_03_energy.ino @@ -866,7 +866,7 @@ void EnergyInit() } #ifdef USE_WEBSERVER -const char HTTP_ENERGY_SNS[] PROGMEM = +const char HTTP_ENERGY_SNS[] PROGMEM = "%s" "{s}" D_VOLTAGE "{m}%s " D_UNIT_VOLT "{e}" "{s}" D_CURRENT "{m}%s " D_UNIT_AMPERE "{e}" "{s}" D_POWERUSAGE "{m}%s " D_UNIT_WATT "{e}" @@ -921,7 +921,7 @@ void EnergyShow(boolean json) #endif // USE_DOMOTICZ #ifdef USE_WEBSERVER } else { - snprintf_P(mqtt_data, sizeof(mqtt_data), HTTP_ENERGY_SNS, energy_voltage_chr, energy_current_chr, energy_power_chr, energy_power_factor_chr, energy_daily_chr, energy_yesterday_chr, energy_total_chr); + snprintf_P(mqtt_data, sizeof(mqtt_data), HTTP_ENERGY_SNS, mqtt_data, energy_voltage_chr, energy_current_chr, energy_power_chr, energy_power_factor_chr, energy_daily_chr, energy_yesterday_chr, energy_total_chr); #endif // USE_WEBSERVER } } diff --git a/sonoff/xdrv_05_domoticz.ino b/sonoff/xdrv_05_domoticz.ino index 0272d4947..2e947b0f9 100644 --- a/sonoff/xdrv_05_domoticz.ino +++ b/sonoff/xdrv_05_domoticz.ino @@ -368,22 +368,28 @@ void DomoticzSaveSettings() { char stemp[20]; char ssensor_indices[6 * MAX_DOMOTICZ_SNS_IDX]; + char tmp[100]; for (byte i = 0; i < MAX_DOMOTICZ_IDX; i++) { snprintf_P(stemp, sizeof(stemp), PSTR("r%d"), i +1); - Settings.domoticz_relay_idx[i] = (!strlen(WebServer->arg(stemp).c_str())) ? 0 : atoi(WebServer->arg(stemp).c_str()); + WebGetArg(stemp, tmp, sizeof(tmp)); + Settings.domoticz_relay_idx[i] = (!strlen(tmp)) ? 0 : atoi(tmp); snprintf_P(stemp, sizeof(stemp), PSTR("k%d"), i +1); - Settings.domoticz_key_idx[i] = (!strlen(WebServer->arg(stemp).c_str())) ? 0 : atoi(WebServer->arg(stemp).c_str()); + WebGetArg(stemp, tmp, sizeof(tmp)); + Settings.domoticz_key_idx[i] = (!strlen(tmp)) ? 0 : atoi(tmp); snprintf_P(stemp, sizeof(stemp), PSTR("s%d"), i +1); - Settings.domoticz_switch_idx[i] = (!strlen(WebServer->arg(stemp).c_str())) ? 0 : atoi(WebServer->arg(stemp).c_str()); + WebGetArg(stemp, tmp, sizeof(tmp)); + Settings.domoticz_switch_idx[i] = (!strlen(tmp)) ? 0 : atoi(tmp); } ssensor_indices[0] = '\0'; for (byte i = 0; i < DZ_MAX_SENSORS; i++) { snprintf_P(stemp, sizeof(stemp), PSTR("l%d"), i +1); - Settings.domoticz_sensor_idx[i] = (!strlen(WebServer->arg(stemp).c_str())) ? 0 : atoi(WebServer->arg(stemp).c_str()); + WebGetArg(stemp, tmp, sizeof(tmp)); + Settings.domoticz_sensor_idx[i] = (!strlen(tmp)) ? 0 : atoi(tmp); snprintf_P(ssensor_indices, sizeof(ssensor_indices), PSTR("%s%s%d"), ssensor_indices, (strlen(ssensor_indices)) ? "," : "", Settings.domoticz_sensor_idx[i]); } - Settings.domoticz_update_timer = (!strlen(WebServer->arg("ut").c_str())) ? DOMOTICZ_UPDATE_TIMER : atoi(WebServer->arg("ut").c_str()); + WebGetArg("ut", tmp, sizeof(tmp)); + Settings.domoticz_update_timer = (!strlen(tmp)) ? DOMOTICZ_UPDATE_TIMER : atoi(tmp); snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_DOMOTICZ D_CMND_IDX " %d,%d,%d,%d, " D_CMND_KEYIDX " %d,%d,%d,%d, " D_CMND_SWITCHIDX " %d,%d,%d,%d, " D_CMND_SENSORIDX " %s, " D_CMND_UPDATETIMER " %d"), Settings.domoticz_relay_idx[0], Settings.domoticz_relay_idx[1], Settings.domoticz_relay_idx[2], Settings.domoticz_relay_idx[3], diff --git a/sonoff/xdrv_07_home_assistant.ino b/sonoff/xdrv_07_home_assistant.ino index b4dec3043..f11c3023b 100644 --- a/sonoff/xdrv_07_home_assistant.ino +++ b/sonoff/xdrv_07_home_assistant.ino @@ -55,6 +55,23 @@ const char HASS_DISCOVER_LIGHT_SCHEME[] PROGMEM = "\"effect_value_template\":\"{{value_json." D_CMND_SCHEME "}}\"," "\"effect_list\":\"[0, 1, 2, 3, 4]\""; // Needs to be a Python string list providing Scheme parameter values (Unable to get this functional) */ +/* +#1690 - investigate +effect_list: +- 0 +- 1 +- 2 +- 3 +- 4 +- 5 +- 6 +- 7 +- 8 +- 9 +- 10 +- 11 +- 12 +*/ void HAssDiscovery() { char sidx[8]; diff --git a/sonoff/xplg_wemohue.ino b/sonoff/xplg_wemohue.ino index 07df239aa..5c96cff59 100644 --- a/sonoff/xplg_wemohue.ino +++ b/sonoff/xplg_wemohue.ino @@ -452,8 +452,8 @@ const char HUE_DESCRIPTION_XML[] PROGMEM = "" "\r\n" "\r\n"; -const char HueLightStatus_JSON[] PROGMEM = - "\"on\":{state}," +const char HUE_LIGHTS_STATUS_JSON[] PROGMEM = + "{\"on\":{state}," "\"bri\":{b}," "\"hue\":{h}," "\"sat\":{s}," @@ -462,19 +462,18 @@ const char HueLightStatus_JSON[] PROGMEM = "\"alert\":\"none\"," "\"effect\":\"none\"," "\"colormode\":\"hs\"," - "\"reachable\":true"; -const char HUE_LIGHTS_STATUS_JSON[] PROGMEM = - "\"type\":\"Extended color light\"," + "\"reachable\":true}"; +const char HUE_LIGHTS_STATUS_JSON2[] PROGMEM = + ",\"type\":\"Extended color light\"," "\"name\":\"{j1\"," "\"modelid\":\"LCT007\"," "\"uniqueid\":\"{j2\"," - "\"swversion\":\"5.50.1.19085\"" - "}"; + "\"swversion\":\"5.50.1.19085\"}"; const char HUE_GROUP0_STATUS_JSON[] PROGMEM = "{\"name\":\"Group 0\"," "\"lights\":[{l1]," "\"type\":\"LightGroup\"," - "\"action\":{"; + "\"action\":"; // "\"scene\":\"none\","; const char HueConfigResponse_JSON[] PROGMEM = "{\"name\":\"Philips hue\"," @@ -556,18 +555,27 @@ void HueConfig(String *path) WebServer->send(200, FPSTR(HDR_CTYPE_JSON), response); } -void HueLightStatus(byte device, String *response) +void HueLightStatus1(byte device, String *response) { - *response += FPSTR(HueLightStatus_JSON); - response->replace("{state}", (power & (1 << (device-1))) ? "true" : "false"); + float hue = 0; + float sat = 0; + float bri = 0; if (light_type) { - LightReplaceHsb(response); - } else { - response->replace("{h}", "0"); - response->replace("{s}", "0"); - response->replace("{b}", "0"); + LightGetHsb(&hue, &sat, &bri); } + *response += FPSTR(HUE_LIGHTS_STATUS_JSON); + response->replace("{state}", (power & (1 << (device-1))) ? "true" : "false"); + response->replace("{h}", String((uint16_t)(65535.0f * hue))); + response->replace("{s}", String((uint8_t)(254.0f * sat))); + response->replace("{b}", String((uint8_t)(254.0f * bri))); +} + +void HueLightStatus2(byte device, String *response) +{ + *response += FPSTR(HUE_LIGHTS_STATUS_JSON2); + response->replace("{j1", Settings.friendlyname[device-1]); + response->replace("{j2", GetHueDeviceId(device)); } void HueGlobalConfig(String *path) @@ -579,12 +587,9 @@ void HueGlobalConfig(String *path) response = F("{\"lights\":{\""); for (uint8_t i = 1; i <= maxhue; i++) { response += i; - response += F("\":{\"state\":{"); - HueLightStatus(i, &response); - response += "},"; - response += FPSTR(HUE_LIGHTS_STATUS_JSON); - response.replace("{j1", Settings.friendlyname[i-1]); - response.replace("{j2", GetHueDeviceId(i)); + response += F("\":{\"state\":"); + HueLightStatus1(i, &response); + HueLightStatus2(i, &response); if (i < maxhue) { response += ",\""; } @@ -627,12 +632,9 @@ void HueLights(String *path) response = "{\""; for (uint8_t i = 1; i <= maxhue; i++) { response += i; - response += F("\":{\"state\":{"); - HueLightStatus(i, &response); - response += "},"; - response += FPSTR(HUE_LIGHTS_STATUS_JSON); - response.replace("{j1", Settings.friendlyname[i-1]); - response.replace("{j2", GetHueDeviceId(i)); + response += F("\":{\"state\":"); + HueLightStatus1(i, &response); + HueLightStatus2(i, &response); if (i < maxhue) { response += ",\""; } @@ -674,7 +676,7 @@ void HueLights(String *path) } if (light_type) { - LightGetHsb(&hue,&sat,&bri); + LightGetHsb(&hue, &sat, &bri); } if (hue_json.containsKey("bri")) { @@ -749,12 +751,9 @@ void HueLights(String *path) if ((device < 1) || (device > maxhue)) { device = 1; } - response += F("{\"state\":{"); - HueLightStatus(device, &response); - response += "},"; - response += FPSTR(HUE_LIGHTS_STATUS_JSON); - response.replace("{j1", Settings.friendlyname[device-1]); - response.replace("{j2", GetHueDeviceId(device)); + response += F("{\"state\":"); + HueLightStatus1(device, &response); + HueLightStatus2(device, &response); WebServer->send(200, FPSTR(HDR_CTYPE_JSON), response); } else { @@ -777,8 +776,8 @@ void HueGroups(String *path) lights += ",\"" + String(i) + "\""; } response.replace("{l1", lights); - HueLightStatus(1, &response); - response += F("}}"); + HueLightStatus1(1, &response); + response += F("}"); } WebServer->send(200, FPSTR(HDR_CTYPE_JSON), response); diff --git a/sonoff/xsns_06_dht.ino b/sonoff/xsns_06_dht.ino index 620b57cd5..cdcd72f1d 100644 --- a/sonoff/xsns_06_dht.ino +++ b/sonoff/xsns_06_dht.ino @@ -203,7 +203,7 @@ void DhtInit() pinMode(Dht[i].pin, INPUT_PULLUP); Dht[i].lastreadtime = 0; Dht[i].lastresult = 0; - strcpy_P(Dht[i].stype, kSensors[Dht[i].type]); + GetTextIndexed(Dht[i].stype, sizeof(Dht[i].stype), Dht[i].type, kSensorNames); if (dht_sensors > 1) { snprintf_P(Dht[i].stype, sizeof(Dht[i].stype), PSTR("%s-%02d"), Dht[i].stype, Dht[i].pin); }
%s