From abdbe628dbd1c7f7ebd31699fd81dcbbf93f5f45 Mon Sep 17 00:00:00 2001 From: arendst Date: Sat, 27 Jan 2018 17:52:48 +0100 Subject: [PATCH 1/5] v5.11.1g - Add PMS5003 support and updates 5.11.1g * Add support for PMS5003 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) * Update language files nl-NL (#1723) and es-AR (#1722) --- README.md | 2 +- sonoff/_releasenotes.ino | 8 +- sonoff/language/de-DE.h | 4 +- sonoff/language/en-GB.h | 4 +- sonoff/language/es-AR.h | 4 +- sonoff/language/nl-NL.h | 8 +- sonoff/settings.h | 11 +-- sonoff/settings.ino | 8 +- sonoff/sonoff.h | 8 +- sonoff/sonoff.ino | 3 +- sonoff/user_config.h | 2 + sonoff/webserver.ino | 75 +++++++++++++--- sonoff/xdrv_01_light.ino | 4 +- sonoff/xsns_18_pms5003.ino | 172 +++++++++++++++++++++++++++++++++++++ 14 files changed, 270 insertions(+), 43 deletions(-) create mode 100644 sonoff/xsns_18_pms5003.ino diff --git a/README.md b/README.md index 710fe1359..eab749266 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.1f** - See [sonoff/_releasenotes.ino](https://github.com/arendst/Sonoff-Tasmota/blob/development/sonoff/_releasenotes.ino) for change information. +Current version is **5.11.1g** - 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 5fabdbf57..00d2ad660 100644 --- a/sonoff/_releasenotes.ino +++ b/sonoff/_releasenotes.ino @@ -1,4 +1,10 @@ -/* 5.11.1f +/* 5.11.1g + * Add support for PMS5003 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) + * Update language files nl-NL (#1723) and es-AR (#1722) + * + * 5.11.1f * Revert chunked webserver pages as it fails on many browsers due to chunks being too small (#1706) * Reduce initial console weblog from 20 to 13 lines due to memory constraints * diff --git a/sonoff/language/de-DE.h b/sonoff/language/de-DE.h index db6ee314f..3e749f1b4 100644 --- a/sonoff/language/de-DE.h +++ b/sonoff/language/de-DE.h @@ -376,8 +376,8 @@ #define D_SHT1X_FOUND "SHT1X gefunden" // xsns_18_pms5003.ino -#define D_STANDARD_CONCENTRATION "Standard Concentration" -#define D_ENVIRONMENTAL_CONCENTRATION "Environmental Concentration" +#define D_STANDARD_CONCENTRATION "Std Concentration" +#define D_ENVIRONMENTAL_CONCENTRATION "Env Concentration" #define D_PARTICALS_BEYOND "Particals beyond" // sonoff_template.h diff --git a/sonoff/language/en-GB.h b/sonoff/language/en-GB.h index 018923612..0204dd882 100644 --- a/sonoff/language/en-GB.h +++ b/sonoff/language/en-GB.h @@ -376,8 +376,8 @@ #define D_SHT1X_FOUND "SHT1X found" // xsns_18_pms5003.ino -#define D_STANDARD_CONCENTRATION "Standard Concentration" -#define D_ENVIRONMENTAL_CONCENTRATION "Environmental Concentration" +#define D_STANDARD_CONCENTRATION "Std Concentration" +#define D_ENVIRONMENTAL_CONCENTRATION "Env Concentration" #define D_PARTICALS_BEYOND "Particals beyond" // sonoff_template.h diff --git a/sonoff/language/es-AR.h b/sonoff/language/es-AR.h index 1d70b4230..a9f1964ff 100644 --- a/sonoff/language/es-AR.h +++ b/sonoff/language/es-AR.h @@ -54,7 +54,7 @@ #define D_BLINK "Blink" #define D_BLINKOFF "BlinkOff" #define D_BOOT_COUNT "Conteo Reinicios" -#define D_BRIGHTLIGHT "Brillo" +#define D_BRIGHTLIGHT "Brillante" #define D_BUTTON "Botón" #define D_BY "por" // Written by me #define D_BYTES "Bytes" @@ -79,7 +79,7 @@ #define D_ERASE "Borrar" #define D_ERROR "Error" #define D_FAHRENHEIT "Fahrenheit" -#define D_FAILED "Fallo" +#define D_FAILED "Falló" #define D_FALLBACK "Fallback" #define D_FALLBACK_TOPIC "FallbackTopic" #define D_FALSE "Falso" diff --git a/sonoff/language/nl-NL.h b/sonoff/language/nl-NL.h index 155e05de6..14c32755b 100644 --- a/sonoff/language/nl-NL.h +++ b/sonoff/language/nl-NL.h @@ -47,7 +47,7 @@ // Common #define D_ADMIN "Admin" -#define D_AIR_QUALITY "Lucht kwalitiet" +#define D_AIR_QUALITY "Lucht kwaliteit" #define D_AP "AP" // Access Point #define D_AS "als" #define D_AUTO "AUTO" @@ -376,9 +376,9 @@ #define D_SHT1X_FOUND "SHT1X gevonden" // xsns_18_pms5003.ino -#define D_STANDARD_CONCENTRATION "Standard Concentration" -#define D_ENVIRONMENTAL_CONCENTRATION "Environmental Concentration" -#define D_PARTICALS_BEYOND "Particals beyond" +#define D_STANDARD_CONCENTRATION "Std concentratie" +#define D_ENVIRONMENTAL_CONCENTRATION "Omg concentratie" +#define D_PARTICALS_BEYOND "Deeltjes groter dan" // sonoff_template.h // Max string length is 8 characters including suffixes diff --git a/sonoff/settings.h b/sonoff/settings.h index 381e7ee9f..4921b6a51 100644 --- a/sonoff/settings.h +++ b/sonoff/settings.h @@ -45,7 +45,7 @@ typedef union { // Restricted by MISRA-C Rule 18.4 bu uint32_t decimal_text : 1; // bit 17 (v5.8.1) uint32_t light_signal : 1; // bit 18 (v5.10.0c) uint32_t hass_discovery : 1; // bit 19 (v5.11.1a) - uint32_t voltage_resolution : 1; // Replaced by below + uint32_t not_power_linked : 1; // bit 20 (v5.11.1f) uint32_t spare21 : 1; uint32_t spare22 : 1; uint32_t spare23 : 1; @@ -57,15 +57,6 @@ typedef union { // Restricted by MISRA-C Rule 18.4 bu uint32_t spare29 : 1; uint32_t spare30 : 1; uint32_t spare31 : 1; - /* - uint32_t wattage_resolution : 1; - uint32_t voltage_resolution : 1; - uint32_t emulation : 2; - uint32_t energy_resolution : 3; - uint32_t pressure_resolution : 2; - uint32_t humidity_resolution : 2; - uint32_t temperature_resolution : 2; -*/ }; } SysBitfield; diff --git a/sonoff/settings.ino b/sonoff/settings.ino index 0e8937a43..1c090ef00 100644 --- a/sonoff/settings.ino +++ b/sonoff/settings.ino @@ -430,7 +430,7 @@ void SettingsDefaultSet2() Settings.flag.mqtt_power_retain = MQTT_POWER_RETAIN; Settings.flag.mqtt_button_retain = MQTT_BUTTON_RETAIN; Settings.flag.mqtt_switch_retain = MQTT_SWITCH_RETAIN; - Settings.flag.hass_discovery = HOME_ASSISTANT_DISCOVERY_ENABLE; + Settings.flag.hass_discovery = HOME_ASSISTANT_DISCOVERY_ENABLE; Settings.flag2.emulation = EMULATION; @@ -845,13 +845,17 @@ void SettingsDelta() if (Settings.version < 0x05090102) { Settings.flag2.data = Settings.flag.data; Settings.flag2.data &= 0xFFE80000; - Settings.flag2.voltage_resolution = Settings.flag.voltage_resolution; + Settings.flag2.voltage_resolution = Settings.flag.not_power_linked; Settings.flag2.current_resolution = 3; Settings.ina219_mode = 0; } if (Settings.version < 0x050A0009) { SettingsDefaultSet_5_10_1(); } + if (Settings.version < 0x050B0107) { + Settings.flag.not_power_linked = 0; + } + Settings.version = VERSION; SettingsSave(1); diff --git a/sonoff/sonoff.h b/sonoff/sonoff.h index 54813c7eb..ff741090c 100644 --- a/sonoff/sonoff.h +++ b/sonoff/sonoff.h @@ -90,11 +90,11 @@ typedef unsigned long power_t; // Power (Relay) type #ifdef USE_MQTT_TLS #define MAX_LOG_LINES 10 // Max number of lines in weblog #else - #ifdef ARDUINO_ESP8266_RELEASE_2_3_0 +// #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) - #endif +// #else +// #define MAX_LOG_LINES 13 // Max number of lines in weblog (less due to more memory usage which prohibits full webpage load) +// #endif #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 047d416a2..cf211650d 100644 --- a/sonoff/sonoff.ino +++ b/sonoff/sonoff.ino @@ -25,7 +25,7 @@ - Select IDE Tools - Flash Size: "1M (no SPIFFS)" ====================================================*/ -#define VERSION 0x050B0106 // 5.11.1f +#define VERSION 0x050B0107 // 5.11.1g // Location specific includes #include // Arduino_Esp8266 version information (ARDUINO_ESP8266_RELEASE and ARDUINO_ESP8266_RELEASE_2_3_0) @@ -1027,6 +1027,7 @@ void MqttDataCallback(char* topic, byte* data, unsigned int data_len) case 16: // ws_clock_reverse case 17: // decimal_text case 18: // light_signal + case 20: // not_power_linked bitWrite(Settings.flag.data, index, payload); } if (12 == index) { // stop_flash_rotate diff --git a/sonoff/user_config.h b/sonoff/user_config.h index 225e11725..80e91730c 100644 --- a/sonoff/user_config.h +++ b/sonoff/user_config.h @@ -216,6 +216,8 @@ #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_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) #define USE_IR_RECEIVE // Support for IR receiver (+5k5 code, 264 iram) diff --git a/sonoff/webserver.ino b/sonoff/webserver.ino index 0010f1020..64596e3c4 100644 --- a/sonoff/webserver.ino +++ b/sonoff/webserver.ino @@ -60,7 +60,7 @@ const char HTTP_HEAD[] PROGMEM = "x=new XMLHttpRequest();" "x.onreadystatechange=function(){" "if(x.readyState==4&&x.status==200){" - "var s=x.responseText.replace(/{s}/g,\"\").replace(/{m}/g,\"\").replace(/{e}/g,\"\").replace(/{t}/g,\"%'>
\").replace(/{s}/g,\"\").replace(/{m}/g,\"\").replace(/{e}/g,\"\").replace(/{c}/g,\"%'>
arg("k").c_str()); ExecuteCommand(svalue); } - +/* String page = ""; mqtt_data[0] = '\0'; XsnsCall(FUNC_WEB_APPEND); @@ -568,13 +568,58 @@ void HandleAjaxStatusRefresh() 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
"), // {t} = %'>
%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} = %'>
"), mqtt_data); + if (devices_present) { + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s{t}
"), 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} = %'>
%s " D_GPIO "%d %s
"), - (WEMOS==Settings.module)?stemp:"", i, (0==i)? D_SENSOR_BUTTON "1":(1==i)? D_SERIAL_OUT :(3==i)? D_SERIAL_IN :(12==i)? D_SENSOR_RELAY "1":(13==i)? D_SENSOR_LED "1i":(14==i)? D_SENSOR :"", i, i); - part2 += mqtt_data; snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("sk(%d,%d);"), my_module.gp.io[i], i); // g0 - g16 page += mqtt_data; } } page += F("}"); - page += part2; + + page += FPSTR(HTTP_HEAD_STYLE); page.replace(F(""), F("")); + page += FPSTR(HTTP_FORM_MODULE); + snprintf_P(stemp, sizeof(stemp), kModules[MODULE].name); + page.replace(F("{mt"), stemp); + page += F("
%s
%s
"); + for (byte i = 0; i < MAX_GPIO_PIN; i++) { + if (GPIO_USER == cmodule.gp.io[i]) { + snprintf_P(stemp, 3, PINS_WEMOS +i*2); + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR(""), + (WEMOS==Settings.module)?stemp:"", i, (0==i)? D_SENSOR_BUTTON "1":(1==i)? D_SERIAL_OUT :(3==i)? D_SERIAL_IN :(12==i)? D_SENSOR_RELAY "1":(13==i)? D_SENSOR_LED "1i":(14==i)? D_SENSOR :"", i, i); + page += mqtt_data; + } + } page += F("
%s " D_GPIO "%d %s
"); page += FPSTR(HTTP_FORM_END); page += FPSTR(HTTP_BTN_CONF); diff --git a/sonoff/xdrv_01_light.ino b/sonoff/xdrv_01_light.ino index 96f9c2f1d..9971f8e53 100644 --- a/sonoff/xdrv_01_light.ino +++ b/sonoff/xdrv_01_light.ino @@ -568,7 +568,9 @@ void LightState(uint8_t append) void LightPreparePower() { if (Settings.light_dimmer && !(light_power)) { - ExecuteCommandPower(light_device, POWER_ON_NO_STATE); + if (!Settings.flag.not_power_linked) { + ExecuteCommandPower(light_device, POWER_ON_NO_STATE); + } } else if (!Settings.light_dimmer && light_power) { ExecuteCommandPower(light_device, POWER_OFF_NO_STATE); diff --git a/sonoff/xsns_18_pms5003.ino b/sonoff/xsns_18_pms5003.ino new file mode 100644 index 000000000..3d73729f0 --- /dev/null +++ b/sonoff/xsns_18_pms5003.ino @@ -0,0 +1,172 @@ +/* + xsns_18_pms5003.ino - PMS5003 particle concentration sensor support for Sonoff-Tasmota + + Copyright (C) 2018 Theo Arends + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifdef USE_PMS5003 +/*********************************************************************************************\ + * PlanTower PMS5003 particle concentration sensor +\*********************************************************************************************/ + +#include + +TasmotaSerial *PmsSerial; + +uint8_t pms_type = 1; +uint8_t pms_valid = 0; + +struct pms5003data { + uint16_t framelen; + uint16_t pm10_standard, pm25_standard, pm100_standard; + uint16_t pm10_env, pm25_env, pm100_env; + uint16_t particles_03um, particles_05um, particles_10um, particles_25um, particles_50um, particles_100um; + uint16_t unused; + uint16_t checksum; +} pms_data; + +boolean PmsReadData() +{ + if (! PmsSerial->available()) { + return false; + } + while ((PmsSerial->peek() != 0x42) && PmsSerial->available()) { + PmsSerial->read(); + } + if (PmsSerial->available() < 32) { + return false; + } + + uint8_t buffer[32]; + uint16_t sum = 0; + PmsSerial->readBytes(buffer, 32); + PmsSerial->flush(); // Make room for another burst + + // get checksum ready + for (uint8_t i = 0; i < 30; i++) { + sum += buffer[i]; + } + // The data comes in endian'd, this solves it so it works on all platforms + uint16_t buffer_u16[15]; + for (uint8_t i = 0; i < 15; i++) { + buffer_u16[i] = buffer[2 + i*2 + 1]; + buffer_u16[i] += (buffer[2 + i*2] << 8); + } + if (sum != buffer_u16[14]) { + AddLog_P(LOG_LEVEL_DEBUG, PSTR("PMS: " D_CHECKSUM_FAILURE)); + return false; + } + + memcpy((void *)&pms_data, (void *)buffer_u16, 30); + pms_valid = 10; + + return true; +} + +/*********************************************************************************************/ + +void PmsSecond() // Every second +{ + if (PmsReadData()) { + pms_valid = 10; + } else { + if (pms_valid) { + pms_valid--; + } + } +} + +/*********************************************************************************************/ + +void PmsInit() +{ + pms_type = 0; + + if (pin[GPIO_PMS5003] < 99) { + PmsSerial = new TasmotaSerial(pin[GPIO_PMS5003], -1); + if (PmsSerial->begin()) { + pms_type = 1; + } + } +} + +#ifdef USE_WEBSERVER +const char HTTP_PMS5003_SNS[] PROGMEM = "%s" + "{s}" D_STANDARD_CONCENTRATION " 1" D_UNIT_MICROMETER "{m}%d " D_UNIT_MICROGRAM_PER_CUBIC_METER "{e}" + "{s}" D_STANDARD_CONCENTRATION " 2" D_DECIMAL_SEPARATOR "5" D_UNIT_MICROMETER "{m}%d " D_UNIT_MICROGRAM_PER_CUBIC_METER "{e}" + "{s}" D_STANDARD_CONCENTRATION " 10" D_UNIT_MICROMETER "{m}%d " D_UNIT_MICROGRAM_PER_CUBIC_METER "{e}" + "{s}" D_ENVIRONMENTAL_CONCENTRATION " 1" D_UNIT_MICROMETER "{m}%d " D_UNIT_MICROGRAM_PER_CUBIC_METER "{e}" + "{s}" D_ENVIRONMENTAL_CONCENTRATION " 2" D_DECIMAL_SEPARATOR "5" D_UNIT_MICROMETER "{m}%d " D_UNIT_MICROGRAM_PER_CUBIC_METER "{e}" + "{s}" D_ENVIRONMENTAL_CONCENTRATION " 10" D_UNIT_MICROMETER "{m}%d " D_UNIT_MICROGRAM_PER_CUBIC_METER "{e}" + "{s}" D_PARTICALS_BEYOND " 0" D_DECIMAL_SEPARATOR "3" D_UNIT_MICROMETER "{m}%d " D_UNIT_PARTS_PER_DECILITER "{e}" + "{s}" D_PARTICALS_BEYOND " 0" D_DECIMAL_SEPARATOR "5" D_UNIT_MICROMETER "{m}%d " D_UNIT_PARTS_PER_DECILITER "{e}" + "{s}" D_PARTICALS_BEYOND " 1" D_UNIT_MICROMETER "{m}%d " D_UNIT_PARTS_PER_DECILITER "{e}" + "{s}" D_PARTICALS_BEYOND " 2" D_DECIMAL_SEPARATOR "5" D_UNIT_MICROMETER "{m}%d " D_UNIT_PARTS_PER_DECILITER "{e}" + "{s}" D_PARTICALS_BEYOND " 5" D_UNIT_MICROMETER "{m}%d " D_UNIT_PARTS_PER_DECILITER "{e}" + "{s}" D_PARTICALS_BEYOND " 10" D_UNIT_MICROMETER "{m}%d " D_UNIT_PARTS_PER_DECILITER "{e}"; // {s} = , {m} = , {e} = +#endif // USE_WEBSERVER + +void PmsShow(boolean json) +{ + if (pms_valid) { + if (json) { + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,\"PMS5003\":{\"SC1\":%d,\"SC2.5\":%d,\"SC10\":%d,\"EC1\":%d,\"EC2.5\":%d,\"EC10\":%d,\"PB0.3\":%d,\"PB0.5\":%d,\"PB1\":%d,\"PB2.5\":%d,\"PB5\":%d,\"PB10\":%d}"), mqtt_data, + pms_data.pm10_standard, pms_data.pm25_standard, pms_data.pm100_standard, + pms_data.pm10_env, pms_data.pm25_env, pms_data.pm100_env, + pms_data.particles_03um, pms_data.particles_05um, pms_data.particles_10um, pms_data.particles_25um, pms_data.particles_50um, pms_data.particles_100um); +#ifdef USE_WEBSERVER + } else { + snprintf_P(mqtt_data, sizeof(mqtt_data), HTTP_PMS5003_SNS, mqtt_data, + pms_data.pm10_standard, pms_data.pm25_standard, pms_data.pm100_standard, + pms_data.pm10_env, pms_data.pm25_env, pms_data.pm100_env, + pms_data.particles_03um, pms_data.particles_05um, pms_data.particles_10um, pms_data.particles_25um, pms_data.particles_50um, pms_data.particles_100um); +#endif // USE_WEBSERVER + } + } +} + +/*********************************************************************************************\ + * Interface +\*********************************************************************************************/ + +#define XSNS_18 + +boolean Xsns18(byte function) +{ + boolean result = false; + + if (pms_type) { + switch (function) { + case FUNC_INIT: + PmsInit(); + break; + case FUNC_EVERY_SECOND: + PmsSecond(); + break; + case FUNC_JSON_APPEND: + PmsShow(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_APPEND: + PmsShow(0); + break; +#endif // USE_WEBSERVER + } + } + return result; +} + +#endif // USE_PMS5003 From b60ccddfe6574ad7b06ad32d28ac0bb1e6153c58 Mon Sep 17 00:00:00 2001 From: arendst Date: Sun, 28 Jan 2018 12:42:42 +0100 Subject: [PATCH 2/5] Update PMS5003 naming --- sonoff/_releasenotes.ino | 2 +- sonoff/language/de-DE.h | 6 +++--- sonoff/language/en-GB.h | 6 +++--- sonoff/language/es-AR.h | 6 +++--- sonoff/language/fr-FR.h | 6 +++--- sonoff/language/it-IT.h | 6 +++--- sonoff/language/nl-NL.h | 6 +++--- sonoff/language/pl-PL.h | 6 +++--- sonoff/language/zh-CN.h | 4 ++-- sonoff/xsns_18_pms5003.ino | 33 +++++++++++++++++---------------- 10 files changed, 41 insertions(+), 40 deletions(-) diff --git a/sonoff/_releasenotes.ino b/sonoff/_releasenotes.ino index 00d2ad660..eee381749 100644 --- a/sonoff/_releasenotes.ino +++ b/sonoff/_releasenotes.ino @@ -1,5 +1,5 @@ /* 5.11.1g - * Add support for PMS5003 particle concentration sensor + * 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) * Update language files nl-NL (#1723) and es-AR (#1722) diff --git a/sonoff/language/de-DE.h b/sonoff/language/de-DE.h index 3e749f1b4..55710e89c 100644 --- a/sonoff/language/de-DE.h +++ b/sonoff/language/de-DE.h @@ -376,9 +376,9 @@ #define D_SHT1X_FOUND "SHT1X gefunden" // xsns_18_pms5003.ino -#define D_STANDARD_CONCENTRATION "Std Concentration" -#define D_ENVIRONMENTAL_CONCENTRATION "Env Concentration" -#define D_PARTICALS_BEYOND "Particals beyond" +#define D_STANDARD_CONCENTRATION "CF-1 PM" // Standard Particle CF-1 Particle Matter +#define D_ENVIRONMENTAL_CONCENTRATION "PM" // Environmetal Particle Matter +#define D_PARTICALS_BEYOND "Particals" // sonoff_template.h // Max string length is 8 characters including suffixes diff --git a/sonoff/language/en-GB.h b/sonoff/language/en-GB.h index 0204dd882..e5ccdaed6 100644 --- a/sonoff/language/en-GB.h +++ b/sonoff/language/en-GB.h @@ -376,9 +376,9 @@ #define D_SHT1X_FOUND "SHT1X found" // xsns_18_pms5003.ino -#define D_STANDARD_CONCENTRATION "Std Concentration" -#define D_ENVIRONMENTAL_CONCENTRATION "Env Concentration" -#define D_PARTICALS_BEYOND "Particals beyond" +#define D_STANDARD_CONCENTRATION "CF-1 PM" // Standard Particle CF-1 Particle Matter +#define D_ENVIRONMENTAL_CONCENTRATION "PM" // Environmetal Particle Matter +#define D_PARTICALS_BEYOND "Particals" // sonoff_template.h // Max string length is 8 characters including suffixes diff --git a/sonoff/language/es-AR.h b/sonoff/language/es-AR.h index a9f1964ff..0ef008469 100644 --- a/sonoff/language/es-AR.h +++ b/sonoff/language/es-AR.h @@ -376,9 +376,9 @@ #define D_SHT1X_FOUND "SHT1X encontrado" // xsns_18_pms5003.ino -#define D_STANDARD_CONCENTRATION "Concentración Standard" -#define D_ENVIRONMENTAL_CONCENTRATION "Concentración en Medio Ambiente" -#define D_PARTICALS_BEYOND "Partículas sobre" +#define D_STANDARD_CONCENTRATION "CF-1 PM" // Standard Particle CF-1 Particle Matter +#define D_ENVIRONMENTAL_CONCENTRATION "PM" // Environmetal Particle Matter +#define D_PARTICALS_BEYOND "Partículas" // sonoff_template.h // Max string length is 8 characters including suffixes diff --git a/sonoff/language/fr-FR.h b/sonoff/language/fr-FR.h index 8d130f8f3..7e655c21c 100644 --- a/sonoff/language/fr-FR.h +++ b/sonoff/language/fr-FR.h @@ -376,9 +376,9 @@ #define D_SHT1X_FOUND "SHT1X found" // xsns_18_pms5003.ino -#define D_STANDARD_CONCENTRATION "Concentration standard" -#define D_ENVIRONMENTAL_CONCENTRATION "Concentration environmentale" -#define D_PARTICALS_BEYOND "Particules au-delà" +#define D_STANDARD_CONCENTRATION "CF-1 PM" // Standard Particle CF-1 Particle Matter +#define D_ENVIRONMENTAL_CONCENTRATION "PM" // Environmetal Particle Matter +#define D_PARTICALS_BEYOND "Particules" // sonoff_template.h // Max string length is 8 characters including suffixes diff --git a/sonoff/language/it-IT.h b/sonoff/language/it-IT.h index e97a61895..59ae8533d 100644 --- a/sonoff/language/it-IT.h +++ b/sonoff/language/it-IT.h @@ -376,9 +376,9 @@ #define D_SHT1X_FOUND "SHT1X trovato" // xsns_18_pms5003.ino -#define D_STANDARD_CONCENTRATION "Concentrazione Standard" -#define D_ENVIRONMENTAL_CONCENTRATION "Concentrazione Ambientale" -#define D_PARTICALS_BEYOND "Particelle oltre" +#define D_STANDARD_CONCENTRATION "CF-1 PM" // Standard Particle CF-1 Particle Matter +#define D_ENVIRONMENTAL_CONCENTRATION "PM" // Environmetal Particle Matter +#define D_PARTICALS_BEYOND "Particelle" // sonoff_template.h // Max string length is 8 characters including suffixes diff --git a/sonoff/language/nl-NL.h b/sonoff/language/nl-NL.h index 14c32755b..f33646579 100644 --- a/sonoff/language/nl-NL.h +++ b/sonoff/language/nl-NL.h @@ -376,9 +376,9 @@ #define D_SHT1X_FOUND "SHT1X gevonden" // xsns_18_pms5003.ino -#define D_STANDARD_CONCENTRATION "Std concentratie" -#define D_ENVIRONMENTAL_CONCENTRATION "Omg concentratie" -#define D_PARTICALS_BEYOND "Deeltjes groter dan" +#define D_STANDARD_CONCENTRATION "CF-1 PM" // Standard Particle CF-1 Particle Matter +#define D_ENVIRONMENTAL_CONCENTRATION "PM" // Environmetal Particle Matter +#define D_PARTICALS_BEYOND "Stofdeeltjes" // sonoff_template.h // Max string length is 8 characters including suffixes diff --git a/sonoff/language/pl-PL.h b/sonoff/language/pl-PL.h index 6c9679e2e..c449da210 100644 --- a/sonoff/language/pl-PL.h +++ b/sonoff/language/pl-PL.h @@ -376,9 +376,9 @@ #define D_SHT1X_FOUND "SHT1X znaleziony" // xsns_18_pms5003.ino -#define D_STANDARD_CONCENTRATION "Standard Concentration" -#define D_ENVIRONMENTAL_CONCENTRATION "Environmental Concentration" -#define D_PARTICALS_BEYOND "Particals beyond" +#define D_STANDARD_CONCENTRATION "CF-1 PM" // Standard Particle CF-1 Particle Matter +#define D_ENVIRONMENTAL_CONCENTRATION "PM" // Environmetal Particle Matter +#define D_PARTICALS_BEYOND "Particals" // sonoff_template.h // Max string length is 8 characters including suffixes diff --git a/sonoff/language/zh-CN.h b/sonoff/language/zh-CN.h index 510f96f51..929da46d5 100644 --- a/sonoff/language/zh-CN.h +++ b/sonoff/language/zh-CN.h @@ -376,8 +376,8 @@ #define D_SHT1X_FOUND "发现 SHT1X 传感器" // xsns_18_pms5003.ino -#define D_STANDARD_CONCENTRATION "标准颗粒物浓度" -#define D_ENVIRONMENTAL_CONCENTRATION "大气环境下浓度" +#define D_STANDARD_CONCENTRATION "CF-1 PM" // Standard Particle CF-1 Particle Matter +#define D_ENVIRONMENTAL_CONCENTRATION "PM" // Environmetal Particle Matter #define D_PARTICALS_BEYOND "颗粒物直径大于" // sonoff_template.h diff --git a/sonoff/xsns_18_pms5003.ino b/sonoff/xsns_18_pms5003.ino index 3d73729f0..ab000eab2 100644 --- a/sonoff/xsns_18_pms5003.ino +++ b/sonoff/xsns_18_pms5003.ino @@ -1,5 +1,5 @@ /* - xsns_18_pms5003.ino - PMS5003 particle concentration sensor support for Sonoff-Tasmota + xsns_18_pms5003.ino - PMS5003-7003 particle concentration sensor support for Sonoff-Tasmota Copyright (C) 2018 Theo Arends @@ -19,7 +19,8 @@ #ifdef USE_PMS5003 /*********************************************************************************************\ - * PlanTower PMS5003 particle concentration sensor + * PlanTower PMS5003 and PMS7003 particle concentration sensor + * For background information see http://aqicn.org/sensor/pms5003-7003/ \*********************************************************************************************/ #include @@ -105,32 +106,32 @@ void PmsInit() #ifdef USE_WEBSERVER const char HTTP_PMS5003_SNS[] PROGMEM = "%s" - "{s}" D_STANDARD_CONCENTRATION " 1" D_UNIT_MICROMETER "{m}%d " D_UNIT_MICROGRAM_PER_CUBIC_METER "{e}" - "{s}" D_STANDARD_CONCENTRATION " 2" D_DECIMAL_SEPARATOR "5" D_UNIT_MICROMETER "{m}%d " D_UNIT_MICROGRAM_PER_CUBIC_METER "{e}" - "{s}" D_STANDARD_CONCENTRATION " 10" D_UNIT_MICROMETER "{m}%d " D_UNIT_MICROGRAM_PER_CUBIC_METER "{e}" - "{s}" D_ENVIRONMENTAL_CONCENTRATION " 1" D_UNIT_MICROMETER "{m}%d " D_UNIT_MICROGRAM_PER_CUBIC_METER "{e}" - "{s}" D_ENVIRONMENTAL_CONCENTRATION " 2" D_DECIMAL_SEPARATOR "5" D_UNIT_MICROMETER "{m}%d " D_UNIT_MICROGRAM_PER_CUBIC_METER "{e}" - "{s}" D_ENVIRONMENTAL_CONCENTRATION " 10" D_UNIT_MICROMETER "{m}%d " D_UNIT_MICROGRAM_PER_CUBIC_METER "{e}" - "{s}" D_PARTICALS_BEYOND " 0" D_DECIMAL_SEPARATOR "3" D_UNIT_MICROMETER "{m}%d " D_UNIT_PARTS_PER_DECILITER "{e}" - "{s}" D_PARTICALS_BEYOND " 0" D_DECIMAL_SEPARATOR "5" D_UNIT_MICROMETER "{m}%d " D_UNIT_PARTS_PER_DECILITER "{e}" - "{s}" D_PARTICALS_BEYOND " 1" D_UNIT_MICROMETER "{m}%d " D_UNIT_PARTS_PER_DECILITER "{e}" - "{s}" D_PARTICALS_BEYOND " 2" D_DECIMAL_SEPARATOR "5" D_UNIT_MICROMETER "{m}%d " D_UNIT_PARTS_PER_DECILITER "{e}" - "{s}" D_PARTICALS_BEYOND " 5" D_UNIT_MICROMETER "{m}%d " D_UNIT_PARTS_PER_DECILITER "{e}" - "{s}" D_PARTICALS_BEYOND " 10" D_UNIT_MICROMETER "{m}%d " D_UNIT_PARTS_PER_DECILITER "{e}"; // {s} = , {m} = , {e} = +// "{s}PMS5003 " D_STANDARD_CONCENTRATION " 1 " D_UNIT_MICROMETER "{m}%d " D_UNIT_MICROGRAM_PER_CUBIC_METER "{e}" +// "{s}PMS5003 " D_STANDARD_CONCENTRATION " 2.5 " D_UNIT_MICROMETER "{m}%d " D_UNIT_MICROGRAM_PER_CUBIC_METER "{e}" +// "{s}PMS5003 " D_STANDARD_CONCENTRATION " 10 " D_UNIT_MICROMETER "{m}%d " D_UNIT_MICROGRAM_PER_CUBIC_METER "{e}" + "{s}PMS5003 " D_ENVIRONMENTAL_CONCENTRATION " 1 " D_UNIT_MICROMETER "{m}%d " D_UNIT_MICROGRAM_PER_CUBIC_METER "{e}" + "{s}PMS5003 " D_ENVIRONMENTAL_CONCENTRATION " 2.5 " D_UNIT_MICROMETER "{m}%d " D_UNIT_MICROGRAM_PER_CUBIC_METER "{e}" + "{s}PMS5003 " D_ENVIRONMENTAL_CONCENTRATION " 10 " D_UNIT_MICROMETER "{m}%d " D_UNIT_MICROGRAM_PER_CUBIC_METER "{e}" + "{s}PMS5003 " D_PARTICALS_BEYOND " 0.3 " D_UNIT_MICROMETER "{m}%d " D_UNIT_PARTS_PER_DECILITER "{e}" + "{s}PMS5003 " D_PARTICALS_BEYOND " 0.5 " D_UNIT_MICROMETER "{m}%d " D_UNIT_PARTS_PER_DECILITER "{e}" + "{s}PMS5003 " D_PARTICALS_BEYOND " 1 " D_UNIT_MICROMETER "{m}%d " D_UNIT_PARTS_PER_DECILITER "{e}" + "{s}PMS5003 " D_PARTICALS_BEYOND " 2.5 " D_UNIT_MICROMETER "{m}%d " D_UNIT_PARTS_PER_DECILITER "{e}" + "{s}PMS5003 " D_PARTICALS_BEYOND " 5 " D_UNIT_MICROMETER "{m}%d " D_UNIT_PARTS_PER_DECILITER "{e}" + "{s}PMS5003 " D_PARTICALS_BEYOND " 10 " D_UNIT_MICROMETER "{m}%d " D_UNIT_PARTS_PER_DECILITER "{e}"; // {s} = , {m} = , {e} = #endif // USE_WEBSERVER void PmsShow(boolean json) { if (pms_valid) { if (json) { - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,\"PMS5003\":{\"SC1\":%d,\"SC2.5\":%d,\"SC10\":%d,\"EC1\":%d,\"EC2.5\":%d,\"EC10\":%d,\"PB0.3\":%d,\"PB0.5\":%d,\"PB1\":%d,\"PB2.5\":%d,\"PB5\":%d,\"PB10\":%d}"), mqtt_data, + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,\"PMS5003\":{\"CF1\":%d,\"CF2.5\":%d,\"CF10\":%d,\"PM1\":%d,\"PM2.5\":%d,\"PM10\":%d,\"PB0.3\":%d,\"PB0.5\":%d,\"PB1\":%d,\"PB2.5\":%d,\"PB5\":%d,\"PB10\":%d}"), mqtt_data, pms_data.pm10_standard, pms_data.pm25_standard, pms_data.pm100_standard, pms_data.pm10_env, pms_data.pm25_env, pms_data.pm100_env, pms_data.particles_03um, pms_data.particles_05um, pms_data.particles_10um, pms_data.particles_25um, pms_data.particles_50um, pms_data.particles_100um); #ifdef USE_WEBSERVER } else { snprintf_P(mqtt_data, sizeof(mqtt_data), HTTP_PMS5003_SNS, mqtt_data, - pms_data.pm10_standard, pms_data.pm25_standard, pms_data.pm100_standard, +// pms_data.pm10_standard, pms_data.pm25_standard, pms_data.pm100_standard, pms_data.pm10_env, pms_data.pm25_env, pms_data.pm100_env, pms_data.particles_03um, pms_data.particles_05um, pms_data.particles_10um, pms_data.particles_25um, pms_data.particles_50um, pms_data.particles_100um); #endif // USE_WEBSERVER From b8f323a0245005de21a9a128be110715992c1f67 Mon Sep 17 00:00:00 2001 From: arendst Date: Tue, 30 Jan 2018 14:14:55 +0100 Subject: [PATCH 3/5] v5.11.1h - Weblog redesign saving RAM 5.11.1h * Rewrite webserver argument processing gaining 5k code space (#1705) * Redesign weblog storage (#1730) * Fix command SetOption20 (#1741) --- README.md | 2 +- sonoff/_releasenotes.ino | 7 +- sonoff/sonoff.h | 10 +- sonoff/sonoff.ino | 8 +- sonoff/support.ino | 350 +++++++++++++++++++----------- sonoff/user_config.h | 2 +- sonoff/webserver.ino | 261 ++++++++++++---------- sonoff/xdrv_01_light.ino | 15 -- sonoff/xdrv_05_domoticz.ino | 16 +- sonoff/xdrv_07_home_assistant.ino | 17 ++ sonoff/xplg_wemohue.ino | 69 +++--- 11 files changed, 442 insertions(+), 315 deletions(-) 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/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..a479fb83f 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 { diff --git a/sonoff/support.ino b/sonoff/support.ino index 48b218386..c5e89b711 100644 --- a/sonoff/support.ino +++ b/sonoff/support.ino @@ -145,9 +145,72 @@ 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 +size_t strcspn(const char *str1, const char *str2) +{ + size_t ret = 0; + while (*str1) { + if (strchr(str2, *str1)) { + return ret; + } else { + str1++; + ret++; + } + } + return ret; +} + +/* + * strcspn.c -- + * + * Source code for the "strcspn" library routine. + * + * Copyright 1988 Regents of the University of California + * Permission to use, copy, modify, and distribute this + * software and its documentation for any purpose and without + * fee is hereby granted, provided that the above copyright + * notice appear in all copies. The University of California + * makes no representations about the suitability of this + * software for any purpose. It is provided "as is" without + * express or implied warranty. + */ +/* +size_t strcspn(const char* str1, const char* str2) +{ + char c; + const char* p; + const char* s; + + for (s = str1, c = *s; c != 0; s++, c = *s) { + for (p = str2; *p != 0; p++) { + if (c == *p) return s -str1; + } + } + return s -str1; +} +*/ +#endif // ARDUINO_ESP8266_RELEASE_2_3_0 + char* dtostrfd(double number, unsigned char prec, char *s) { return dtostrf(number, 1, prec, s); @@ -254,6 +317,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 +1294,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 +1352,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 = strcspn(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 +1404,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 += strcspn(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..e632d657a 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 +1050,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 +1092,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 +1125,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 +1137,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 +1233,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 +1427,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 +1439,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 +1454,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 +1508,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 +1523,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 +1739,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_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..485e653e9 100644 --- a/sonoff/xplg_wemohue.ino +++ b/sonoff/xplg_wemohue.ino @@ -452,7 +452,8 @@ const char HUE_DESCRIPTION_XML[] PROGMEM = "" "\r\n" "\r\n"; -const char HueLightStatus_JSON[] PROGMEM = +const char HUE_LIGHTS_STATUS_JSON[] PROGMEM = + "{\"state\":{" "\"on\":{state}," "\"bri\":{b}," "\"hue\":{h}," @@ -462,14 +463,13 @@ 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]," @@ -556,18 +556,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 +588,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("\":"); + HueLightStatus1(i, &response); + HueLightStatus2(i, &response); if (i < maxhue) { response += ",\""; } @@ -627,12 +633,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("\":"); + HueLightStatus1(i, &response); + HueLightStatus2(i, &response); if (i < maxhue) { response += ",\""; } @@ -674,7 +677,7 @@ void HueLights(String *path) } if (light_type) { - LightGetHsb(&hue,&sat,&bri); + LightGetHsb(&hue, &sat, &bri); } if (hue_json.containsKey("bri")) { @@ -749,12 +752,8 @@ 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)); + 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); From 2fc348cec64dd1e52bdfc3b582a8d681044d6067 Mon Sep 17 00:00:00 2001 From: arendst Date: Tue, 30 Jan 2018 14:50:38 +0100 Subject: [PATCH 4/5] Adjust Wemo/Hue --- sonoff/xplg_wemohue.ino | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/sonoff/xplg_wemohue.ino b/sonoff/xplg_wemohue.ino index 485e653e9..5c96cff59 100644 --- a/sonoff/xplg_wemohue.ino +++ b/sonoff/xplg_wemohue.ino @@ -453,8 +453,7 @@ const char HUE_DESCRIPTION_XML[] PROGMEM = "\r\n" "\r\n"; const char HUE_LIGHTS_STATUS_JSON[] PROGMEM = - "{\"state\":{" - "\"on\":{state}," + "{\"on\":{state}," "\"bri\":{b}," "\"hue\":{h}," "\"sat\":{s}," @@ -474,7 +473,7 @@ 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\"," @@ -588,7 +587,7 @@ void HueGlobalConfig(String *path) response = F("{\"lights\":{\""); for (uint8_t i = 1; i <= maxhue; i++) { response += i; - response += F("\":"); + response += F("\":{\"state\":"); HueLightStatus1(i, &response); HueLightStatus2(i, &response); if (i < maxhue) { @@ -633,7 +632,7 @@ void HueLights(String *path) response = "{\""; for (uint8_t i = 1; i <= maxhue; i++) { response += i; - response += F("\":"); + response += F("\":{\"state\":"); HueLightStatus1(i, &response); HueLightStatus2(i, &response); if (i < maxhue) { @@ -752,6 +751,7 @@ void HueLights(String *path) if ((device < 1) || (device > maxhue)) { device = 1; } + response += F("{\"state\":"); HueLightStatus1(device, &response); HueLightStatus2(device, &response); WebServer->send(200, FPSTR(HDR_CTYPE_JSON), response); From acfcccaf6b3672c222dc35640385129c44e5bfb0 Mon Sep 17 00:00:00 2001 From: arendst Date: Tue, 30 Jan 2018 23:01:16 +0100 Subject: [PATCH 5/5] Fix Pow energy webpage --- sonoff/xdrv_03_energy.ino | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 } }
%s