diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index aea7cd816..a4a459a18 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -3,8 +3,9 @@ **Related issue (if applicable):** fixes # ## Checklist: - - [ ] The pull request is done against the dev branch - - [ ] Only relevant files were touched (Also beware if your editor has auto-formatting feature enabled) + - [ ] The pull request is done against the latest dev branch + - [ ] Only relevant files were touched (Also remember to update _changelog.ino_ file) - [ ] Only one feature/fix was added per PR. - [ ] The code change is tested and works. - [ ] The code change pass travis tests. **Your PR cannot be merged unless tests pass** + - [ ] I accept the [CLA](https://github.com/arendst/Sonoff-Tasmota/blob/development/CONTRIBUTING.md#contributor-license-agreement-cla). diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 7cfcdfadb..3ecb5a9bc 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -56,7 +56,57 @@ The process is straight-forward. 7. All pull requests should consider updates to the documentation. 8. Pull requests that address an outstanding issue, particularly an issue deemed to be severe, should be given priority. 9. If a PR is accepted, then it should undergo review and updated based on the feedback provided, then merged. -10. Pull requests that don't meet the above will be denied and closed. +10. By submitting a PR, it is needed to use the provided PR template and check all boxes, performing the required tasks and accepting the CLA. +11. Pull requests that don't meet the above will be denied and closed. + +-------------------------------------- + +## Contributor License Agreement (CLA) + +``` +By making a contribution to this project, I certify that: + +(a) The contribution was created in whole or in part by me and I + have the right to submit it under the GPL-3.0 license; or + +(b) The contribution is based upon previous work that, to the best + of my knowledge, is covered under an appropriate open source + license and I have the right under that license to submit that + work with modifications, whether created in whole or in part + by me, under the GPL-3.0 license; or + +(c) The contribution was provided directly to me by some other + person who certified (a), (b) or (c) and I have not modified + it. + +(d) I understand and agree that this project and the contribution + are public and that a record of the contribution (including all + personal information I submit with it) is maintained indefinitely + and may be redistributed consistent with this project or the open + source license(s) involved. +``` + +This Contributor License Agreement (CLA) was adopted on April 1st, 2019. + +The text of this license is available under the [Creative Commons Attribution-ShareAlike 3.0 Unported License](http://creativecommons.org/licenses/by-sa/3.0/). It is based on the Linux [Developer Certificate Of Origin](http://elinux.org/Developer_Certificate_Of_Origin), but is modified to explicitly use the GPL-3.0 license and not mention sign-off (due to GitHub.com keeps an historial, with your user name, of PRs' commits and all editions on PR's comments). + +To accept the CLA it is required to put a x between [ ] on `[ ] I accept the CLA` in the PR template when submitting it. The [ ] is an opt-in box, so you have to manually accept it. + +**Why a CLA ?** + +_"A Contributor Licence Agreement (CLA) is strongly recommended when accepting third party contributions to an open development project, such as an open source software project. In order to redistribute contributions, it is necessary to ensure that the project has the necessary rights to do so. A Contributor Licence Agreement is a lightweight agreement, signed by the copyright holder, that grants the necessary rights for the contribution to be redistributed as part of the project."_ [OSS Watch](http://oss-watch.ac.uk/resources/cla) + +A CLA is a legal document in which you state _you are entitled to contribute the code/documentation/translation to the project_ you’re contributing to and that _you are willing to have it used in distributions and derivative works_. This means that should there be any kind of legal issue in the future as to the origins and ownership of any particular piece of code, then that project has the necessary forms on file from the contributor(s) saying they were permitted to make this contribution. + +CLA is a safety because it also ensures that once you have provided a contribution, you cannot try to withdraw permission for its use at a later date. People can therefore use that software, confident that they will not be asked to stop using pieces of the code at a later date. + +A __license__ grants "outbound" rights to the user of project. + +A __CLA__ enables a contributor to grant "inbound" rights to a project. + + + + diff --git a/README.md b/README.md index 8a8402339..586520fc6 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ ## Sonoff-Tasmota -Alternative firmware for _ESP8266 based devices_ like [iTead](https://www.itead.cc/) _**Sonoff**_ with **web UI, rules and timers, OTA updates, custome device templates and sensors support**. Allows control over **MQTT**, **HTTP**, **Serial** and **KNX** for integrations with smart home systems. Written for Arduino IDE and PlatformIO. +Alternative firmware for _ESP8266 based devices_ like [iTead](https://www.itead.cc/) _**Sonoff**_ with **web UI, rules and timers, OTA updates, custom device templates and sensor support**. Allows control over **MQTT**, **HTTP**, **Serial** and **KNX** for integrations with smart home systems. Written for Arduino IDE and PlatformIO. [![GitHub version](https://img.shields.io/github/release/arendst/Sonoff-Tasmota.svg)](https://github.com/arendst/Sonoff-Tasmota/releases/latest) [![GitHub download](https://img.shields.io/github/downloads/arendst/Sonoff-Tasmota/total.svg)](https://github.com/arendst/Sonoff-Tasmota/releases/latest) @@ -15,7 +15,7 @@ If you like **Sonoff-Tasmota**, give it a star, or fork it and contribute! See [RELEASENOTES.md](https://github.com/arendst/Sonoff-Tasmota/blob/development/RELEASENOTES.md) for release information. -In addition to the [release webpage](https://github.com/arendst/Sonoff-Tasmota/releases/latest) the binaries can also be OTA downloaded from http://thehackbox.org/tasmota/release/ +In addition to the [release webpage](https://github.com/arendst/Sonoff-Tasmota/releases/latest), the binaries can also be OTA downloaded from http://thehackbox.org/tasmota/release/ ### Development [![Dev Version](https://img.shields.io/badge/development%20version-6.5.0.x-blue.svg)](https://github.com/arendst/Sonoff-Tasmota) @@ -29,12 +29,12 @@ The development codebase is checked hourly for changes and if new commits have b ### Disclaimer :warning: **DANGER OF ELECTROCUTION** :warning: -A Sonoff device is not a toy. It uses Mains AC so there is a danger of electrocution if not installed properly. If you don't know how to install it, please call an electrician. Remember: _**SAFETY FIRST**_. It is not worth to risk yourself, your family and your home if you don't know exactly what you are doing. Never try to flash a Sonoff device while it is connected to MAINS AC. +A Sonoff device is not a toy. It uses Mains AC so there is a danger of electrocution if not installed properly. If you don't know how to install it, please call an electrician. Remember: _**SAFETY FIRST**_. It is not worth risk to yourself, your family, and your home if you don't know exactly what you are doing. Never try to flash a Sonoff device while it is connected to MAINS AC. We don't take any responsibility nor liability for using this software nor for the installation or any tips, advice, videos, etc. given by any member of this site or any related site. ### Note -Please do not ask to add devices where you can't provide a basic working configuration (other than sonoff). Since there are thousands of them.. +Please do not ask to add devices where you can't provide a basic working configuration (other than sonoff). Since there are thousands of them. ### Quick Install Download one of the released binaries from https://github.com/arendst/Sonoff-Tasmota/releases and flash it to your hardware as documented in the wiki. diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 4485d3083..0df822456 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -71,14 +71,14 @@ Module | Description 49 Neo Coolcam | Neo Coolcam Wifi Smart Socket 50 ESP Switch | ESP Switch 4-gang Wifi Switch with Leds 51 OBI Socket | OBI Wifi Smart Socket -52 Teckin | Teckin SP20 Wifi Smart Switch with Energy Monitoring +52 Teckin | Teckin SP22 Wifi Smart Switch with Energy Monitoring 53 AplicWDP303075 | Aplic WDP 303075 CSL Wifi Smart Switch with Energy Monitoring 54 Tuya Dimmer | MIUO (and other Tuya based) Wifi Dimmer for Incandescent Lights and Led 55 Gosund SP1 v23 | Gosund SP1 v2.3 Wifi Smart Switch with Energy Monitoring 56 ARMTR Dimmer | ARMtronix Wifi dimmer for Incandescent Lights and Led 57 SK03 Outdoor | SK03 Outdoor Wifi Smart Switch with Energy Monitoring 58 PS-16-DZ | PS-16-DZ Wifi dimmer for Incandescent Lights and Led -59 Teckin US | Teckin US and ZooZee SA102 Wifi Smart Switch with Energy Monitoring +59 Teckin US | Teckin SP20 and ZooZee SA102 Wifi Smart Switch with Energy Monitoring 60 Manzoku strip | Manzoku Wifi Smart Power Strip with four Relays 61 OBI Socket 2 | OBI 2 Wifi Smart Socket 62 YTF IR Bridge | YTF Infra Red Wifi Bridge @@ -194,6 +194,7 @@ Core version **2.4.2** binaries can be found at http://thehackbox.org/tasmota/re | USE_RC_SWITCH | - | - | - | x | x | x | x | | USE_RF_SENSOR | - | - | - | - | - | x | - | AlectoV2 only | USE_SM16716 | - | x | x | x | x | x | x | +| USE_HRE | - | - | - | - | - | x | - | | USE_DISPLAY | - | - | - | - | - | - | x | | USE_DISPLAY_LCD | - | - | - | - | - | - | x | | USE_DISPLAY_SSD1306 | - | - | - | - | - | - | x | diff --git a/include/dummy.txt b/include/dummy.txt new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/include/dummy.txt @@ -0,0 +1 @@ + diff --git a/platformio.ini b/platformio.ini index 671a6f284..9ca59d3e8 100644 --- a/platformio.ini +++ b/platformio.ini @@ -67,9 +67,11 @@ build_flags = ${esp82xx_defaults.build_flags} [core_2_5_0] ; *** Esp8266 core for Arduino version 2.5.0 -platform = espressif8266@2.0.1 +platform = espressif8266@~2.0.4 build_flags = ${esp82xx_defaults.build_flags} -Wl,-Teagle.flash.1m.ld +; Code optimization see https://github.com/esp8266/Arduino/issues/5790#issuecomment-475672473 + -O2 ; lwIP 1.4 (Default) ; -DPIO_FRAMEWORK_ARDUINO_LWIP_HIGHER_BANDWIDTH ; lwIP 2 - Low Memory @@ -89,6 +91,8 @@ build_flags = ${esp82xx_defaults.build_flags} platform = https://github.com/platformio/platform-espressif8266.git#feature/stage build_flags = ${esp82xx_defaults.build_flags} -Wl,-Teagle.flash.1m.ld +; Code optimization see https://github.com/esp8266/Arduino/issues/5790#issuecomment-475672473 + -O2 ; nonos-sdk 22x -DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK22x ; nonos-sdk-pre-v3 @@ -343,7 +347,7 @@ board = ${common.board} board_build.flash_mode = ${common.board_build.flash_mode} board_build.f_cpu = ${common.board_build.f_cpu} build_unflags = ${common.build_unflags} -build_flags = ${common.build_flags} -DMY_LANGUAGE=es-AR +build_flags = ${common.build_flags} -DMY_LANGUAGE=es-ES monitor_speed = ${common.monitor_speed} upload_port = ${common.upload_port} upload_resetmethod = ${common.upload_resetmethod} diff --git a/sonoff/Parsing.cpp b/sonoff/Parsing.cpp index 525277c74..6c68a3af4 100644 --- a/sonoff/Parsing.cpp +++ b/sonoff/Parsing.cpp @@ -415,7 +415,7 @@ bool ESP8266WebServer::_parseForm(WiFiClient& client, String boundary, uint32_t DEBUG_OUTPUT.println(argFilename); #endif //use GET to set the filename if uploading using blob - if (argFilename == F("blob") && hasArg(FPSTR(filename))) + if (argFilename == F("blob") && hasArg(FPSTR(filename))) argFilename = arg(FPSTR(filename)); } #ifdef DEBUG_ESP_HTTP_SERVER @@ -510,7 +510,7 @@ readfile: uint8_t endBuf[boundary.length()]; client.readBytes(endBuf, boundary.length()); - if (strstr((const char*)endBuf, boundary.c_str()) != NULL){ + if (strstr((const char*)endBuf, boundary.c_str()) != nullptr){ if(_currentHandler && _currentHandler->canUpload(_currentUri)) _currentHandler->upload(*this, _currentUri, *_currentUpload); _currentUpload->totalSize += _currentUpload->currentSize; @@ -571,7 +571,7 @@ readfile: arg.value = postArgs[iarg].value; } _currentArgCount = iarg; - if (postArgs) + if (postArgs) delete[] postArgs; return true; } diff --git a/sonoff/_changelog.ino b/sonoff/_changelog.ino index 978aa861f..4698dc463 100644 --- a/sonoff/_changelog.ino +++ b/sonoff/_changelog.ino @@ -1,4 +1,37 @@ -/* 6.5.0.1 20190319 +/* 6.5.0.7 20190410 + * Add command LedMask to assign which relay has access to power LED (#5602, #5612) + * + * 6.5.0.6 20190409 + * Add WebColor parameters to Settings making them persistent and remove the need for using a rule + * Add alternative IRSend command syntax IRSend raw,,
,
,,,, (#5610) + * + * 6.5.0.5 20190406 + * Add compile time GUI hexadecimal only color options in my_user_config.h (#5586) + * Fix template activation and/or module selection regression from 6.5.0.4 (#5598) + * Add rule Http#Initialized + * Add command WebColor to change non-persistent GUI colors on the fly + Use a rule like: + rule3 on http#initialized do webcolor {"webcolor":["#eeeeee","#181818","#4f4f4f","#000000","#dddddd","#008000","#222222","#ff0000","#008000","#ffffff","#1fa3ec","#0e70a4","#d43535","#931f1f","#47c266","#5aaf6f","#ffffff","#999999"]} endon + or + rule3 on http#initialized do webcolor {"webcolor":["#eee","#181818","#4f4f4f","#000","#ddd","#009800","#222"]} endon + to make color changes persistent) + * + * 6.5.0.4 20190402 + * Fix Configure Timer Web GUI (#5568) + * Add validation check when loading settings from flash + * Fixed Display Bug in KNX webmenu for Physical Address + * + * 6.5.0.3 20190328 + * Add command Sensor20 1..255 to change Nova Fitness SDS01 working period in minutes (#5452) + * Change some defines to const + * Change IRsend and receive for 64-bit support (#5523) + * Change IRSend Panasonic protocol to 64-bit (#5523) + * + * 6.5.0.2 20190325 + * Change UDP initial message handling from string to char using static memory and add debug info (#5505) + * Add optional support for Badger HR-E Water Meter (#5539) + * + * 6.5.0.1 20190319 * Change Web GUI sensor data collection * * 6.5.0 20190319 @@ -17,7 +50,7 @@ * Change image name BE_MINIMAL to FIRMWARE_MINIMAL and USE_xyz to FIRMWARE_xyz (#5106) * Change GUI weblog from XML to plain text solving possible empty screens (#5154) * Fix most compiler warnings - * Fix Display exception 28 when JSON value is NULL received + * Fix Display exception 28 when JSON value is nullptr received * Fix epaper driver (#4785) * Fix HAss Sensor Discovery Software Watchdog restart (#4831, #4988) * Fix allowable MAX_RULE_VARS to 16 (#4933) diff --git a/sonoff/i18n.h b/sonoff/i18n.h index ded4ccd43..3585f710b 100644 --- a/sonoff/i18n.h +++ b/sonoff/i18n.h @@ -69,6 +69,7 @@ #define D_JSON_FLASHCHIPID "FlashChipId" #define D_JSON_FLASHMODE "FlashMode" #define D_JSON_FLASHSIZE "FlashSize" +#define D_JSON_FLOWRATE "FlowRate" #define D_JSON_FREEMEMORY "Free" #define D_JSON_FREQUENCY "Frequency" #define D_JSON_FROM "from" @@ -143,6 +144,7 @@ #define D_JSON_TIME "Time" #define D_JSON_TODAY "Today" #define D_JSON_TOTAL "Total" +#define D_JSON_TOTAL_USAGE "TotalUsage" #define D_JSON_TOTAL_REACTIVE "TotalReactivePower" #define D_JSON_TOTAL_START_TIME "TotalStartTime" #define D_JSON_TVOC "TVOC" @@ -264,6 +266,7 @@ #define D_CMND_ALTITUDE "Altitude" #define D_CMND_LEDPOWER "LedPower" #define D_CMND_LEDSTATE "LedState" +#define D_CMND_LEDMASK "LedMask" #define D_CMND_I2CSCAN "I2CScan" #define D_CMND_SERIALSEND "SerialSend" #define D_CMND_SERIALDELIMITER "SerialDelimiter" @@ -309,6 +312,7 @@ #define D_CMND_WEBLOG "WebLog" #define D_CMND_WEBREFRESH "WebRefresh" #define D_CMND_WEBSEND "WebSend" +#define D_CMND_WEBCOLOR "WebColor" #define D_CMND_EMULATION "Emulation" // Commands xdrv_03_energy.ino @@ -495,7 +499,6 @@ const char S_JSON_COMMAND_NVALUE_ACTIVE_NVALUE[] PROGMEM = "{\"%s\":\"%d (" D const char S_JSON_COMMAND_NVALUE[] PROGMEM = "{\"%s\":%d}"; const char S_JSON_COMMAND_LVALUE[] PROGMEM = "{\"%s\":%lu}"; const char S_JSON_COMMAND_SVALUE[] PROGMEM = "{\"%s\":\"%s\"}"; -const char S_JSON_COMMAND_HVALUE[] PROGMEM = "{\"%s\":\"#%X\"}"; const char S_JSON_COMMAND_ASTERIX[] PROGMEM = "{\"%s\":\"" D_ASTERIX "\"}"; const char S_JSON_COMMAND_XVALUE[] PROGMEM = "{\"%s\":%s}"; // %s must provide quotes on non-number @@ -506,14 +509,16 @@ const char S_JSON_COMMAND_INDEX_ASTERIX[] PROGMEM = "{\"%s%d\":\"" D_A const char S_JSON_COMMAND_INDEX_SVALUE_SVALUE[] PROGMEM = "{\"%s%d\":\"%s%s\"}"; const char S_JSON_COMMAND_INDEX_NVALUE_ACTIVE_NVALUE[] PROGMEM = "{\"%s%d\":\"%d (" D_JSON_ACTIVE " %d)\"}"; -const char S_JSON_SENSOR_INDEX_NVALUE[] PROGMEM = "{\"" D_CMND_SENSOR "%d\":%d}"; -const char S_JSON_SENSOR_INDEX_SVALUE[] PROGMEM = "{\"" D_CMND_SENSOR "%d\":\"%s\"}"; +const char S_JSON_SENSOR_INDEX_NVALUE[] PROGMEM = "{\"" D_CMND_SENSOR "%d\":%d}"; +const char S_JSON_SENSOR_INDEX_SVALUE[] PROGMEM = "{\"" D_CMND_SENSOR "%d\":\"%s\"}"; -const char S_JSON_DRIVER_INDEX_NVALUE[] PROGMEM = "{\"" D_CMND_DRIVER "%d\":%d}"; -const char S_JSON_DRIVER_INDEX_SVALUE[] PROGMEM = "{\"" D_CMND_DRIVER "%d\":\"%s\"}"; +const char S_JSON_DRIVER_INDEX_NVALUE[] PROGMEM = "{\"" D_CMND_DRIVER "%d\":%d}"; +const char S_JSON_DRIVER_INDEX_SVALUE[] PROGMEM = "{\"" D_CMND_DRIVER "%d\":\"%s\"}"; -const char JSON_SNS_TEMP[] PROGMEM = "%s,\"%s\":{\"" D_JSON_TEMPERATURE "\":%s}"; -const char JSON_SNS_TEMPHUM[] PROGMEM = "%s,\"%s\":{\"" D_JSON_TEMPERATURE "\":%s,\"" D_JSON_HUMIDITY "\":%s}"; +const char JSON_SNS_TEMP[] PROGMEM = ",\"%s\":{\"" D_JSON_TEMPERATURE "\":%s}"; +const char JSON_SNS_TEMPHUM[] PROGMEM = ",\"%s\":{\"" D_JSON_TEMPERATURE "\":%s,\"" D_JSON_HUMIDITY "\":%s}"; + +const char JSON_SNS_GNGPM[] PROGMEM = ",\"%s\":{\"" D_JSON_TOTAL_USAGE "\":%s,\"" D_JSON_FLOWRATE "\":%s}"; const char S_LOG_I2C_FOUND_AT[] PROGMEM = D_LOG_I2C "%s " D_FOUND_AT " 0x%x"; @@ -561,20 +566,16 @@ const char kOptionBlinkOff[] PROGMEM = "BLINKOFF|" D_BLINKOFF ; // xdrv_02_webserver.ino #ifdef USE_WEBSERVER -const char HTTP_SNS_TEMP[] PROGMEM = "{s}%s " D_TEMPERATURE "{m}%s°%c{e}"; // {s} = , {m} = , {e} = -const char HTTP_SNS_HUM[] PROGMEM = "{s}%s " D_HUMIDITY "{m}%s%%{e}"; // {s} = , {m} = , {e} = -const char HTTP_SNS_PRESSURE[] PROGMEM = "{s}%s " D_PRESSURE "{m}%s %s{e}"; // {s} = , {m} = , {e} = -const char HTTP_SNS_SEAPRESSURE[] PROGMEM = "{s}%s " D_PRESSUREATSEALEVEL "{m}%s %s{e}"; // {s} = , {m} = , {e} = -const char HTTP_SNS_ANALOG[] PROGMEM = "{s}%s " D_ANALOG_INPUT "%d{m}%d{e}"; // {s} = , {m} = , {e} = -const char HTTP_SNS_ILLUMINANCE[] PROGMEM = "{s}%s " D_ILLUMINANCE "{m}%d " D_UNIT_LUX "{e}"; // {s} = , {m} = , {e} = - -#if defined(USE_MHZ19) || defined(USE_SENSEAIR) || defined(USE_AZ7798) || defined(USE_SCD30) -const char HTTP_SNS_CO2[] PROGMEM = "{s}%s " D_CO2 "{m}%d " D_UNIT_PARTS_PER_MILLION "{e}"; // {s} = , {m} = , {e} = -#endif // USE_MHZ19 - -#if defined(USE_SCD30) -const char HTTP_SNS_CO2EAVG[] PROGMEM = "{s}%s " D_ECO2 "{m}%d " D_UNIT_PARTS_PER_MILLION "{e}"; // {s} = , {m} = , {e} = -#endif // USE_SCD30 +const char HTTP_SNS_TEMP[] PROGMEM = "{s}%s " D_TEMPERATURE "{m}%s°%c{e}"; // {s} = , {m} = , {e} = +const char HTTP_SNS_HUM[] PROGMEM = "{s}%s " D_HUMIDITY "{m}%s%%{e}"; // {s} = , {m} = , {e} = +const char HTTP_SNS_PRESSURE[] PROGMEM = "{s}%s " D_PRESSURE "{m}%s %s{e}"; // {s} = , {m} = , {e} = +const char HTTP_SNS_SEAPRESSURE[] PROGMEM = "{s}%s " D_PRESSUREATSEALEVEL "{m}%s %s{e}"; // {s} = , {m} = , {e} = +const char HTTP_SNS_ANALOG[] PROGMEM = "{s}%s " D_ANALOG_INPUT "%d{m}%d{e}"; // {s} = , {m} = , {e} = +const char HTTP_SNS_ILLUMINANCE[] PROGMEM = "{s}%s " D_ILLUMINANCE "{m}%d " D_UNIT_LUX "{e}"; // {s} = , {m} = , {e} = +const char HTTP_SNS_CO2[] PROGMEM = "{s}%s " D_CO2 "{m}%d " D_UNIT_PARTS_PER_MILLION "{e}"; // {s} = , {m} = , {e} = +const char HTTP_SNS_CO2EAVG[] PROGMEM = "{s}%s " D_ECO2 "{m}%d " D_UNIT_PARTS_PER_MILLION "{e}"; // {s} = , {m} = , {e} = +const char HTTP_SNS_GALLONS[] PROGMEM = "{s}%s " D_TOTAL_USAGE "{m}%s " D_UNIT_GALLONS " {e}"; // {s} = , {m} = , {e} = +const char HTTP_SNS_GPM[] PROGMEM = "{s}%s " D_FLOW_RATE "{m}%s " D_UNIT_GALLONS_PER_MIN" {e}"; // {s} = , {m} = , {e} = const char S_MAIN_MENU[] PROGMEM = D_MAIN_MENU; const char S_CONFIGURATION[] PROGMEM = D_CONFIGURATION; diff --git a/sonoff/language/bg-BG.h b/sonoff/language/bg-BG.h index 2214634f9..ccd767b2b 100644 --- a/sonoff/language/bg-BG.h +++ b/sonoff/language/bg-BG.h @@ -93,6 +93,7 @@ #define D_FALLBACK_TOPIC "Помощен топик" #define D_FALSE "Невярно" #define D_FILE "Файл" +#define D_FLOW_RATE "Flow rate" #define D_FREE_MEMORY "Свободна памет" #define D_FREQUENCY "Честота" #define D_GAS "Газ" @@ -156,6 +157,7 @@ #define D_TO "към" #define D_TOGGLE "Превключване" #define D_TOPIC "Топик" +#define D_TOTAL_USAGE "Total Usage" #define D_TRANSMIT "Предаване" #define D_TRUE "Вярно" #define D_TVOC "TVOC" @@ -305,6 +307,7 @@ #define D_BASE_TYPE "Базиран на" #define D_TEMPLATE_FLAGS "Флагове" #define D_ALLOW_ADC0 "ADC0 вход" +#define D_ALLOW_ADC0_TEMP "ADC0 температура" #define D_ALLOW_PULLUP "Потребителски избор на pull-up" #define D_SAVE_CONFIGURATION "Запазване на конфигурацията" @@ -492,6 +495,10 @@ #define D_TX20_SOUTH "Ю" #define D_TX20_WEST "З" +//xsns_43_hre.ino +#define D_LOG_HRE "HRE: " + + // sonoff_template.h - keep them as short as possible to be able to fit them in GUI drop down box #define D_SENSOR_NONE "Няма" #define D_SENSOR_USER "Потребит." @@ -576,12 +583,16 @@ #define D_SENSOR_TXD "Serial Tx" #define D_SENSOR_RXD "Serial Rx" #define D_SENSOR_ROTARY "Rotary" // Suffix "1A" +#define D_SENSOR_HRE_CLOCK "HRE Clock" +#define D_SENSOR_HRE_DATA "HRE Data" // Units #define D_UNIT_AMPERE "A" #define D_UNIT_CENTIMETER "cm" #define D_UNIT_HERTZ "Hz" #define D_UNIT_HOUR "h" +#define D_UNIT_GALLONS "gal" +#define D_UNIT_GALLONS_PER_MIN "g/m" #define D_UNIT_KILOGRAM "kg" #define D_UNIT_INCREMENTS "inc" #define D_UNIT_KILOMETER_PER_HOUR "km/h" diff --git a/sonoff/language/cs-CZ.h b/sonoff/language/cs-CZ.h index 41d4fc9de..bf74e95e8 100644 --- a/sonoff/language/cs-CZ.h +++ b/sonoff/language/cs-CZ.h @@ -93,6 +93,7 @@ #define D_FALLBACK_TOPIC "Záložní topic" #define D_FALSE "Nepravda" #define D_FILE "Soubor" +#define D_FLOW_RATE "Flow rate" #define D_FREE_MEMORY "Volná paměť" #define D_FREQUENCY "Kmitočet" #define D_GAS "Plyn" @@ -156,6 +157,7 @@ #define D_TO "do" #define D_TOGGLE "Přepni" #define D_TOPIC "Topic" +#define D_TOTAL_USAGE "Total Usage" #define D_TRANSMIT "Odešli" #define D_TRUE "Pravda" #define D_TVOC "TVOC" @@ -305,6 +307,7 @@ #define D_BASE_TYPE "Based on" #define D_TEMPLATE_FLAGS "Options" #define D_ALLOW_ADC0 "ADC0 input" +#define D_ALLOW_ADC0_TEMP "ADC0 temperature" #define D_ALLOW_PULLUP "User pull-up selection" #define D_SAVE_CONFIGURATION "Ulož nastavení" @@ -492,6 +495,9 @@ #define D_TX20_SOUTH "J" #define D_TX20_WEST "Z" +//xsns_43_hre.ino +#define D_LOG_HRE "HRE: " + // sonoff_template.h - keep them as short as possible to be able to fit them in GUI drop down box #define D_SENSOR_NONE "Není" #define D_SENSOR_USER "User" @@ -576,12 +582,16 @@ #define D_SENSOR_TXD "Serial Tx" #define D_SENSOR_RXD "Serial Rx" #define D_SENSOR_ROTARY "Rotary" // Suffix "1A" +#define D_SENSOR_HRE_CLOCK "HRE Clock" +#define D_SENSOR_HRE_DATA "HRE Data" // Units #define D_UNIT_AMPERE "A" #define D_UNIT_CENTIMETER "cm" #define D_UNIT_HERTZ "Hz" #define D_UNIT_HOUR "hod" +#define D_UNIT_GALLONS "gal" +#define D_UNIT_GALLONS_PER_MIN "g/m" #define D_UNIT_KILOGRAM "kg" #define D_UNIT_INCREMENTS "inc" #define D_UNIT_KILOMETER_PER_HOUR "km/h" // or "km/h" diff --git a/sonoff/language/de-DE.h b/sonoff/language/de-DE.h index 0808c91c4..6e1f8f4c1 100644 --- a/sonoff/language/de-DE.h +++ b/sonoff/language/de-DE.h @@ -93,6 +93,7 @@ #define D_FALLBACK_TOPIC "Fallback-Topic" #define D_FALSE "falsch" #define D_FILE "Datei" +#define D_FLOW_RATE "Flow rate" #define D_FREE_MEMORY "Freier Arbeitsspeicher" #define D_FREQUENCY "Frequenz" #define D_GAS "Gas" @@ -156,6 +157,7 @@ #define D_TO "zu" #define D_TOGGLE "An/Aus" #define D_TOPIC "topic" +#define D_TOTAL_USAGE "Total Usage" #define D_TRANSMIT "Übertragen" #define D_TRUE "wahr" #define D_TVOC "TVOC" @@ -305,6 +307,7 @@ #define D_BASE_TYPE "basiert auf" #define D_TEMPLATE_FLAGS "Options" #define D_ALLOW_ADC0 "ADC0 input" +#define D_ALLOW_ADC0_TEMP "ADC0 Temperatur" #define D_ALLOW_PULLUP "Nutzer pull-up Auswahl" #define D_SAVE_CONFIGURATION "Konfiguration speichern" @@ -492,6 +495,9 @@ #define D_TX20_SOUTH "S" #define D_TX20_WEST "W" +//xsns_43_hre.ino +#define D_LOG_HRE "HRE: " + // sonoff_template.h - keep them as short as possible to be able to fit them in GUI drop down box #define D_SENSOR_NONE "None" #define D_SENSOR_USER "User" @@ -576,12 +582,16 @@ #define D_SENSOR_TXD "Serial Tx" #define D_SENSOR_RXD "Serial Rx" #define D_SENSOR_ROTARY "Rotary" // Suffix "1A" +#define D_SENSOR_HRE_CLOCK "HRE Clock" +#define D_SENSOR_HRE_DATA "HRE Data" // Units #define D_UNIT_AMPERE "A" #define D_UNIT_CENTIMETER "cm" #define D_UNIT_HERTZ "Hz" #define D_UNIT_HOUR "h" +#define D_UNIT_GALLONS "gal" +#define D_UNIT_GALLONS_PER_MIN "g/m" #define D_UNIT_INCREMENTS "inc" #define D_UNIT_KILOGRAM "kg" #define D_UNIT_KILOMETER_PER_HOUR "km/h" diff --git a/sonoff/language/el-GR.h b/sonoff/language/el-GR.h index 0e483759d..53a0d779f 100644 --- a/sonoff/language/el-GR.h +++ b/sonoff/language/el-GR.h @@ -93,6 +93,7 @@ #define D_FALLBACK_TOPIC "Fallback Topic" #define D_FALSE "Ψευδές" #define D_FILE "Αρχείο" +#define D_FLOW_RATE "Flow rate" #define D_FREE_MEMORY "Ελεύθερη μνήμη" #define D_FREQUENCY "Συχνότητα" #define D_GAS "Αέριο" @@ -156,6 +157,7 @@ #define D_TO "έως" #define D_TOGGLE "Εναλλαγή" #define D_TOPIC "Topic" +#define D_TOTAL_USAGE "Total Usage" #define D_TRANSMIT "Μετάδοση" #define D_TRUE "Αληθές" #define D_TVOC "TVOC" @@ -305,6 +307,7 @@ #define D_BASE_TYPE "Based on" #define D_TEMPLATE_FLAGS "Options" #define D_ALLOW_ADC0 "ADC0 input" +#define D_ALLOW_ADC0_TEMP "ADC0 temperature" #define D_ALLOW_PULLUP "User pull-up selection" #define D_SAVE_CONFIGURATION "Αποθήκευση ρυθμίσεων" @@ -492,6 +495,9 @@ #define D_TX20_SOUTH "Ν" #define D_TX20_WEST "Δ" +//xsns_43_hre.ino +#define D_LOG_HRE "HRE: " + // sonoff_template.h - keep them as short as possible to be able to fit them in GUI drop down box #define D_SENSOR_NONE "Κανένα" #define D_SENSOR_USER "User" @@ -576,12 +582,16 @@ #define D_SENSOR_TXD "Serial Tx" #define D_SENSOR_RXD "Serial Rx" #define D_SENSOR_ROTARY "Rotary" // Suffix "1A" +#define D_SENSOR_HRE_CLOCK "HRE Clock" +#define D_SENSOR_HRE_DATA "HRE Data" // Units #define D_UNIT_AMPERE "A" #define D_UNIT_CENTIMETER "cm" #define D_UNIT_HERTZ "Hz" #define D_UNIT_HOUR "Hr" +#define D_UNIT_GALLONS "gal" +#define D_UNIT_GALLONS_PER_MIN "g/m" #define D_UNIT_INCREMENTS "inc" #define D_UNIT_KILOGRAM "kg" #define D_UNIT_KILOMETER_PER_HOUR "km/h" // or "km/h" diff --git a/sonoff/language/en-GB.h b/sonoff/language/en-GB.h index d5e792462..3b76639de 100644 --- a/sonoff/language/en-GB.h +++ b/sonoff/language/en-GB.h @@ -93,6 +93,7 @@ #define D_FALLBACK_TOPIC "Fallback Topic" #define D_FALSE "False" #define D_FILE "File" +#define D_FLOW_RATE "Flow rate" #define D_FREE_MEMORY "Free Memory" #define D_FREQUENCY "Frequency" #define D_GAS "Gas" @@ -156,6 +157,7 @@ #define D_TO "to" #define D_TOGGLE "Toggle" #define D_TOPIC "Topic" +#define D_TOTAL_USAGE "Total Usage" #define D_TRANSMIT "Transmit" #define D_TRUE "True" #define D_TVOC "TVOC" @@ -305,6 +307,7 @@ #define D_BASE_TYPE "Based on" #define D_TEMPLATE_FLAGS "Options" #define D_ALLOW_ADC0 "ADC0 input" +#define D_ALLOW_ADC0_TEMP "ADC0 temperature" #define D_ALLOW_PULLUP "User pull-up selection" #define D_SAVE_CONFIGURATION "Save configuration" @@ -492,6 +495,9 @@ #define D_TX20_SOUTH "S" #define D_TX20_WEST "W" +//xsns_43_hre.ino +#define D_LOG_HRE "HRE: " + // sonoff_template.h - keep them as short as possible to be able to fit them in GUI drop down box #define D_SENSOR_NONE "None" #define D_SENSOR_USER "User" @@ -576,12 +582,15 @@ #define D_SENSOR_TXD "Serial Tx" #define D_SENSOR_RXD "Serial Rx" #define D_SENSOR_ROTARY "Rotary" // Suffix "1A" - +#define D_SENSOR_HRE_CLOCK "HRE Clock" +#define D_SENSOR_HRE_DATA "HRE Data" // Units #define D_UNIT_AMPERE "A" #define D_UNIT_CENTIMETER "cm" #define D_UNIT_HERTZ "Hz" #define D_UNIT_HOUR "Hr" +#define D_UNIT_GALLONS "gal" +#define D_UNIT_GALLONS_PER_MIN "g/m" #define D_UNIT_INCREMENTS "inc" #define D_UNIT_KILOGRAM "kg" #define D_UNIT_KILOMETER_PER_HOUR "km/h" // or "km/h" diff --git a/sonoff/language/es-AR.h b/sonoff/language/es-ES.h similarity index 96% rename from sonoff/language/es-AR.h rename to sonoff/language/es-ES.h index baf9838bf..6b03d2482 100644 --- a/sonoff/language/es-AR.h +++ b/sonoff/language/es-ES.h @@ -1,5 +1,5 @@ /* - es-AR.h - localization for Spanish - Argentina for Sonoff-Tasmota + es-ES.h - localization for Spanish - Spain for Sonoff-Tasmota Copyright (C) 2019 Adrian Scillato @@ -17,8 +17,8 @@ along with this program. If not, see . */ -#ifndef _LANGUAGE_ES_AR_H_ -#define _LANGUAGE_ES_AR_H_ +#ifndef _LANGUAGE_ES_ES_H_ +#define _LANGUAGE_ES_ES_H_ /*************************** ATTENTION *******************************\ * @@ -28,12 +28,12 @@ * Use online command StateText to translate ON, OFF, HOLD and TOGGLE. * Use online command Prefix to translate cmnd, stat and tele. * - * Updated until v6.3.0.17 + * Updated until v6.5.0.7 \*********************************************************************/ #define LANGUAGE_MODULE_NAME // Enable to display "Module Generic" (ie Spanish), Disable to display "Generic Module" (ie English) -#define LANGUAGE_LCID 11274 +#define LANGUAGE_LCID 1034 // HTML (ISO 639-1) Language Code #define D_HTML_LANGUAGE "es" @@ -93,6 +93,7 @@ #define D_FALLBACK_TOPIC "FallbackTopic" #define D_FALSE "Falso" #define D_FILE "Archivo" +#define D_FLOW_RATE "Caudal" #define D_FREE_MEMORY "Memoria Libre" #define D_FREQUENCY "Frecuencia" #define D_GAS "Gas" @@ -156,6 +157,7 @@ #define D_TO "a" #define D_TOGGLE "Conmutar" #define D_TOPIC "Topic" +#define D_TOTAL_USAGE "Total Usado" #define D_TRANSMIT "Transmitir" #define D_TRUE "Verdadero" #define D_TVOC "TVOC" @@ -289,8 +291,8 @@ #define D_TELEMETRY_PERIOD "Período de Telemetría" #define D_OTHER_PARAMETERS "Otros parámetros" -#define D_TEMPLATE "Template" -#define D_ACTIVATE "Activate" +#define D_TEMPLATE "Plantilla" +#define D_ACTIVATE "Activar" #define D_WEB_ADMIN_PASSWORD "Clave Administrador Web" #define D_MQTT_ENABLE "Habilitar MQTT" #define D_FRIENDLY_NAME "Nombre Amigable" @@ -299,13 +301,14 @@ #define D_SINGLE_DEVICE "dispositivo simple" #define D_MULTI_DEVICE "dispositivo múltiple" -#define D_CONFIGURE_TEMPLATE "Configure Template" -#define D_TEMPLATE_PARAMETERS "Template parameters" -#define D_TEMPLATE_NAME "Name" -#define D_BASE_TYPE "Based on" -#define D_TEMPLATE_FLAGS "Options" -#define D_ALLOW_ADC0 "ADC0 input" -#define D_ALLOW_PULLUP "User pull-up selection" +#define D_CONFIGURE_TEMPLATE "Configurar Plantilla" +#define D_TEMPLATE_PARAMETERS "Parámetros de Plantilla" +#define D_TEMPLATE_NAME "Nombre" +#define D_BASE_TYPE "Basada en" +#define D_TEMPLATE_FLAGS "Opciones" +#define D_ALLOW_ADC0 "Entrada ADC0" +#define D_ALLOW_ADC0_TEMP "Temperatura por ADC0" +#define D_ALLOW_PULLUP "Habilitar pull-up" #define D_SAVE_CONFIGURATION "Grabar configuración" #define D_CONFIGURATION_SAVED "Configuración grabada" @@ -492,6 +495,9 @@ #define D_TX20_SOUTH "S" #define D_TX20_WEST "O" +//xsns_43_hre.ino +#define D_LOG_HRE "HRE: " + // sonoff_template.h - keep them as short as possible to be able to fit them in GUI drop down box #define D_SENSOR_NONE "Ninguno" #define D_SENSOR_USER "User" @@ -576,12 +582,16 @@ #define D_SENSOR_TXD "Serial Tx" #define D_SENSOR_RXD "Serial Rx" #define D_SENSOR_ROTARY "Rotary" // Suffix "1A" +#define D_SENSOR_HRE_CLOCK "HRE Clock" +#define D_SENSOR_HRE_DATA "HRE Data" // Units #define D_UNIT_AMPERE "A" #define D_UNIT_CENTIMETER "cm" #define D_UNIT_HERTZ "Hz" #define D_UNIT_HOUR "Hr" +#define D_UNIT_GALLONS "gal" +#define D_UNIT_GALLONS_PER_MIN "g/m" #define D_UNIT_INCREMENTS "inc" #define D_UNIT_KILOGRAM "kg" #define D_UNIT_KILOMETER_PER_HOUR "km/h" // or "km/h" @@ -644,4 +654,4 @@ #define D_UNIT_KWARH "kVArH" #define D_UNIT_ANGLE "Grados" -#endif // _LANGUAGE_ES_AR_H_ +#endif // _LANGUAGE_ES_ES_H_ diff --git a/sonoff/language/fr-FR.h b/sonoff/language/fr-FR.h index 0b7d29eb7..7f57fb230 100644 --- a/sonoff/language/fr-FR.h +++ b/sonoff/language/fr-FR.h @@ -93,6 +93,7 @@ #define D_FALLBACK_TOPIC "Topic de secours" #define D_FALSE "Faux" #define D_FILE "Fichier" +#define D_FLOW_RATE "Flow rate" #define D_FREE_MEMORY "Mémoire libre" #define D_FREQUENCY "Fréquence" #define D_GAS "Gaz" @@ -156,6 +157,7 @@ #define D_TO "à" #define D_TOGGLE "Inverser" #define D_TOPIC "Topic" // Keep MQTT keyword +#define D_TOTAL_USAGE "Total Usage" #define D_TRANSMIT "Transmettre" #define D_TRUE "Vrai" #define D_TVOC "TVOC" @@ -305,6 +307,7 @@ #define D_BASE_TYPE "Basé sur" #define D_TEMPLATE_FLAGS "Options" #define D_ALLOW_ADC0 "Entrée ADC0" +#define D_ALLOW_ADC0_TEMP "ADC0 temperature" #define D_ALLOW_PULLUP "Pull-up utilisateur" #define D_SAVE_CONFIGURATION "Enregistrer la configuration" @@ -492,6 +495,9 @@ #define D_TX20_SOUTH "S" #define D_TX20_WEST "O" +//xsns_43_hre.ino +#define D_LOG_HRE "HRE: " + // sonoff_template.h - keep them as short as possible to be able to fit them in GUI drop down box #define D_SENSOR_NONE "Aucun" #define D_SENSOR_USER "Utilisateur" @@ -576,12 +582,16 @@ #define D_SENSOR_TXD "Serial Tx" #define D_SENSOR_RXD "Serial Rx" #define D_SENSOR_ROTARY "Rotary" // Suffix "1A" +#define D_SENSOR_HRE_CLOCK "HRE Clock" +#define D_SENSOR_HRE_DATA "HRE Data" // Units #define D_UNIT_AMPERE "A" #define D_UNIT_CENTIMETER "cm" #define D_UNIT_HERTZ "Hz" #define D_UNIT_HOUR "h" +#define D_UNIT_GALLONS "gal" +#define D_UNIT_GALLONS_PER_MIN "g/m" #define D_UNIT_INCREMENTS "inc" #define D_UNIT_KILOGRAM "kg" #define D_UNIT_KILOMETER_PER_HOUR "km/h" diff --git a/sonoff/language/he-HE.h b/sonoff/language/he-HE.h index b26529cf2..9d6b76a12 100644 --- a/sonoff/language/he-HE.h +++ b/sonoff/language/he-HE.h @@ -93,6 +93,7 @@ #define D_FALLBACK_TOPIC "נושא לחזרה" #define D_FALSE "שגוי" #define D_FILE "קובץ" +#define D_FLOW_RATE "Flow rate" #define D_FREE_MEMORY "זכרון פנוי" #define D_FREQUENCY "תדר" #define D_GAS "גז" @@ -156,6 +157,7 @@ #define D_TO "ל" #define D_TOGGLE "מתג" #define D_TOPIC "נושא" +#define D_TOTAL_USAGE "Total Usage" #define D_TRANSMIT "עבר" #define D_TRUE "נכון" #define D_TVOC "TVOC" @@ -305,6 +307,7 @@ #define D_BASE_TYPE "מבוסס על" #define D_TEMPLATE_FLAGS "אפשריות" #define D_ALLOW_ADC0 "ADC0 כניסת" +#define D_ALLOW_ADC0_TEMP "ADC0 temperature" #define D_ALLOW_PULLUP "pull-up בחירת משתמש עבור" #define D_SAVE_CONFIGURATION "שמירת הגדרות" @@ -492,6 +495,9 @@ #define D_TX20_SOUTH "S" #define D_TX20_WEST "W" +//xsns_43_hre.ino +#define D_LOG_HRE "HRE: " + // sonoff_template.h - keep them as short as possible to be able to fit them in GUI drop down box #define D_SENSOR_NONE "None" #define D_SENSOR_USER "משתמש" @@ -576,12 +582,16 @@ #define D_SENSOR_TXD "Serial Tx" #define D_SENSOR_RXD "Serial Rx" #define D_SENSOR_ROTARY "Rotary" // Suffix "1A" +#define D_SENSOR_HRE_CLOCK "HRE Clock" +#define D_SENSOR_HRE_DATA "HRE Data" // Units #define D_UNIT_AMPERE "A" #define D_UNIT_CENTIMETER "cm" #define D_UNIT_HERTZ "Hz" #define D_UNIT_HOUR "Hr" +#define D_UNIT_GALLONS "gal" +#define D_UNIT_GALLONS_PER_MIN "g/m" #define D_UNIT_INCREMENTS "inc" #define D_UNIT_KILOGRAM "kg" #define D_UNIT_KILOMETER_PER_HOUR "km/h" // or "km/h" diff --git a/sonoff/language/hu-HU.h b/sonoff/language/hu-HU.h index e8b818029..61d9c01ad 100644 --- a/sonoff/language/hu-HU.h +++ b/sonoff/language/hu-HU.h @@ -93,6 +93,7 @@ #define D_FALLBACK_TOPIC "fallback topik" #define D_FALSE "Hamis" #define D_FILE "Fájl" +#define D_FLOW_RATE "Flow rate" #define D_FREE_MEMORY "Szabad memória" #define D_FREQUENCY "Frekvencia" #define D_GAS "Gáz" @@ -156,6 +157,7 @@ #define D_TO "-nak" #define D_TOGGLE "Megfordítás" #define D_TOPIC "Topic" +#define D_TOTAL_USAGE "Total Usage" #define D_TRANSMIT "Továbbít" #define D_TRUE "Igaz" #define D_TVOC "TVOC" @@ -305,6 +307,7 @@ #define D_BASE_TYPE "Based on" #define D_TEMPLATE_FLAGS "Options" #define D_ALLOW_ADC0 "ADC0 input" +#define D_ALLOW_ADC0_TEMP "ADC0 temperature" #define D_ALLOW_PULLUP "User pull-up selection" #define D_SAVE_CONFIGURATION "Beállítások mentése" @@ -492,6 +495,9 @@ #define D_TX20_SOUTH "D" #define D_TX20_WEST "NY" +//xsns_43_hre.ino +#define D_LOG_HRE "HRE: " + // sonoff_template.h - keep them as short as possible to be able to fit them in GUI drop down box #define D_SENSOR_NONE "Nincs" #define D_SENSOR_USER "User" @@ -576,12 +582,16 @@ #define D_SENSOR_TXD "Serial Tx" #define D_SENSOR_RXD "Serial Rx" #define D_SENSOR_ROTARY "Rotary" // Suffix "1A" +#define D_SENSOR_HRE_CLOCK "HRE Clock" +#define D_SENSOR_HRE_DATA "HRE Data" // Units #define D_UNIT_AMPERE "A" #define D_UNIT_CENTIMETER "cm" #define D_UNIT_HERTZ "Hz" #define D_UNIT_HOUR "h" +#define D_UNIT_GALLONS "gal" +#define D_UNIT_GALLONS_PER_MIN "g/m" #define D_UNIT_INCREMENTS "inc" #define D_UNIT_KILOGRAM "kg" #define D_UNIT_KILOMETER_PER_HOUR "km/h" // or "km/h" diff --git a/sonoff/language/it-IT.h b/sonoff/language/it-IT.h index db8e58475..e392e7df0 100644 --- a/sonoff/language/it-IT.h +++ b/sonoff/language/it-IT.h @@ -93,6 +93,7 @@ #define D_FALLBACK_TOPIC "Topic Riserva" #define D_FALSE "Falso" #define D_FILE "File" +#define D_FLOW_RATE "Flow rate" #define D_FREE_MEMORY "Memoria Libera" #define D_FREQUENCY "Frequenza" #define D_GAS "Gas" @@ -156,6 +157,7 @@ #define D_TO "a" #define D_TOGGLE "Toggle" #define D_TOPIC "Topic" +#define D_TOTAL_USAGE "Total Usage" #define D_TRANSMIT "Trasmesso" #define D_TRUE "Vero" #define D_TVOC "TVOC" @@ -305,6 +307,7 @@ #define D_BASE_TYPE "Based on" #define D_TEMPLATE_FLAGS "Options" #define D_ALLOW_ADC0 "ADC0 input" +#define D_ALLOW_ADC0_TEMP "ADC0 temperature" #define D_ALLOW_PULLUP "User pull-up selection" #define D_SAVE_CONFIGURATION "Salva configurazione" @@ -492,6 +495,9 @@ #define D_TX20_SOUTH "S" #define D_TX20_WEST "W" +//xsns_43_hre.ino +#define D_LOG_HRE "HRE: " + // sonoff_template.h - keep them as short as possible to be able to fit them in GUI drop down box #define D_SENSOR_NONE "Nessuno" #define D_SENSOR_USER "User" @@ -576,12 +582,16 @@ #define D_SENSOR_TXD "Serial Tx" #define D_SENSOR_RXD "Serial Rx" #define D_SENSOR_ROTARY "Rotary" // Suffix "1A" +#define D_SENSOR_HRE_CLOCK "HRE Clock" +#define D_SENSOR_HRE_DATA "HRE Data" // Units #define D_UNIT_AMPERE "A" #define D_UNIT_CENTIMETER "cm" #define D_UNIT_HERTZ "Hz" #define D_UNIT_HOUR "Hr" +#define D_UNIT_GALLONS "gal" +#define D_UNIT_GALLONS_PER_MIN "g/m" #define D_UNIT_INCREMENTS "inc" #define D_UNIT_KILOGRAM "kg" #define D_UNIT_KILOMETER_PER_HOUR "km/h" // or "km/h" diff --git a/sonoff/language/ko-KO.h b/sonoff/language/ko-KO.h index ddfaba8d3..ad8102559 100644 --- a/sonoff/language/ko-KO.h +++ b/sonoff/language/ko-KO.h @@ -93,6 +93,7 @@ #define D_FALLBACK_TOPIC "Fallback Topic" #define D_FALSE "거짓" #define D_FILE "파일" +#define D_FLOW_RATE "Flow rate" #define D_FREE_MEMORY "남은 메모리" #define D_FREQUENCY "빈도" #define D_GAS "가스" @@ -156,6 +157,7 @@ #define D_TO "to" #define D_TOGGLE "전환" #define D_TOPIC "Topic" +#define D_TOTAL_USAGE "Total Usage" #define D_TRANSMIT "전송" #define D_TRUE "참" #define D_TVOC "TVOC" @@ -305,6 +307,7 @@ #define D_BASE_TYPE "Based on" #define D_TEMPLATE_FLAGS "옵션" #define D_ALLOW_ADC0 "ADC0 입력" +#define D_ALLOW_ADC0_TEMP "ADC0 temperature" #define D_ALLOW_PULLUP "User pull-up selection" #define D_SAVE_CONFIGURATION "설정 저장" @@ -492,6 +495,9 @@ #define D_TX20_SOUTH "S" #define D_TX20_WEST "W" +//xsns_43_hre.ino +#define D_LOG_HRE "HRE: " + // sonoff_template.h - keep them as short as possible to be able to fit them in GUI drop down box #define D_SENSOR_NONE "없음" #define D_SENSOR_USER "User" @@ -576,12 +582,16 @@ #define D_SENSOR_TXD "Serial Tx" #define D_SENSOR_RXD "Serial Rx" #define D_SENSOR_ROTARY "Rotary" // Suffix "1A" +#define D_SENSOR_HRE_CLOCK "HRE Clock" +#define D_SENSOR_HRE_DATA "HRE Data" // Units #define D_UNIT_AMPERE "A" #define D_UNIT_CENTIMETER "cm" #define D_UNIT_HERTZ "Hz" #define D_UNIT_HOUR "시" +#define D_UNIT_GALLONS "gal" +#define D_UNIT_GALLONS_PER_MIN "g/m" #define D_UNIT_INCREMENTS "inc" #define D_UNIT_KILOGRAM "kg" #define D_UNIT_KILOMETER_PER_HOUR "km/h" // or "km/h" diff --git a/sonoff/language/nl-NL.h b/sonoff/language/nl-NL.h index 9e428165d..ac7872228 100644 --- a/sonoff/language/nl-NL.h +++ b/sonoff/language/nl-NL.h @@ -93,6 +93,7 @@ #define D_FALLBACK_TOPIC "Fallback Topic" #define D_FALSE "Onwaar" #define D_FILE "Bestand" +#define D_FLOW_RATE "Flow rate" #define D_FREE_MEMORY "Vrij geheugen" #define D_FREQUENCY "Frequentie" #define D_GAS "Gas" @@ -156,6 +157,7 @@ #define D_TO "naar" #define D_TOGGLE "Toggle" // Wissel, Tuimel #define D_TOPIC "Topic" // Onderwerp +#define D_TOTAL_USAGE "Total Usage" #define D_TRANSMIT "Verzend" #define D_TRUE "Waar" #define D_TVOC "TVOC" @@ -289,8 +291,8 @@ #define D_TELEMETRY_PERIOD "Telemetry periode" #define D_OTHER_PARAMETERS "Overige parameters" -#define D_TEMPLATE "Template" -#define D_ACTIVATE "Activate" +#define D_TEMPLATE "Sjabloon" +#define D_ACTIVATE "Activeer" #define D_WEB_ADMIN_PASSWORD "Web Admin Wachtwoord" #define D_MQTT_ENABLE "MQTT ingeschakeld" #define D_FRIENDLY_NAME "Beschrijvende naam" @@ -299,12 +301,13 @@ #define D_SINGLE_DEVICE "een apparaat" #define D_MULTI_DEVICE "meer apparaten" -#define D_CONFIGURE_TEMPLATE "Configure Template" -#define D_TEMPLATE_PARAMETERS "Template parameters" -#define D_TEMPLATE_NAME "Name" -#define D_BASE_TYPE "Based on" -#define D_TEMPLATE_FLAGS "Options" -#define D_ALLOW_ADC0 "ADC0 input" +#define D_CONFIGURE_TEMPLATE "Configureer Sjabloon" +#define D_TEMPLATE_PARAMETERS "Sjabloon parameters" +#define D_TEMPLATE_NAME "Naam" +#define D_BASE_TYPE "Op basis van" +#define D_TEMPLATE_FLAGS "Opties" +#define D_ALLOW_ADC0 "ADC0 ingang" +#define D_ALLOW_ADC0_TEMP "ADC0 temperatuur" #define D_ALLOW_PULLUP "User pull-up selection" #define D_SAVE_CONFIGURATION "Bewaar configuratie" @@ -492,6 +495,9 @@ #define D_TX20_SOUTH "S" #define D_TX20_WEST "W" +//xsns_43_hre.ino +#define D_LOG_HRE "HRE: " + // sonoff_template.h - keep them as short as possible to be able to fit them in GUI drop down box #define D_SENSOR_NONE "Geen" #define D_SENSOR_USER "Gebruiker" @@ -576,12 +582,16 @@ #define D_SENSOR_TXD "Serial Tx" #define D_SENSOR_RXD "Serial Rx" #define D_SENSOR_ROTARY "Rotary" // Suffix "1A" +#define D_SENSOR_HRE_CLOCK "HRE Clock" +#define D_SENSOR_HRE_DATA "HRE Data" // Units #define D_UNIT_AMPERE "A" #define D_UNIT_CENTIMETER "cm" #define D_UNIT_HERTZ "Hz" #define D_UNIT_HOUR "h" +#define D_UNIT_GALLONS "gal" +#define D_UNIT_GALLONS_PER_MIN "g/m" #define D_UNIT_INCREMENTS "inc" #define D_UNIT_KILOGRAM "kg" #define D_UNIT_KILOMETER_PER_HOUR "km/h" // or "km/h" diff --git a/sonoff/language/pl-PL.h b/sonoff/language/pl-PL.h index 0f13d248a..6b99d7233 100644 --- a/sonoff/language/pl-PL.h +++ b/sonoff/language/pl-PL.h @@ -93,6 +93,7 @@ #define D_FALLBACK_TOPIC "Zastępczy temat" #define D_FALSE "Fałsz" #define D_FILE "Plik" +#define D_FLOW_RATE "Flow rate" #define D_FREE_MEMORY "Wolna pamięć" #define D_FREQUENCY "Frequency" #define D_GAS "Gas" @@ -156,6 +157,7 @@ #define D_TO "do" #define D_TOGGLE "Przełącz" #define D_TOPIC "Temat" +#define D_TOTAL_USAGE "Total Usage" #define D_TRANSMIT "Wyślij" #define D_TRUE "Prawda" #define D_TVOC "TVOC" @@ -305,6 +307,7 @@ #define D_BASE_TYPE "Based on" #define D_TEMPLATE_FLAGS "Options" #define D_ALLOW_ADC0 "ADC0 input" +#define D_ALLOW_ADC0_TEMP "ADC0 temperature" #define D_ALLOW_PULLUP "User pull-up selection" #define D_SAVE_CONFIGURATION "Zapisz ustawienia" @@ -492,6 +495,9 @@ #define D_TX20_SOUTH "S" #define D_TX20_WEST "W" +//xsns_43_hre.ino +#define D_LOG_HRE "HRE: " + // sonoff_template.h - keep them as short as possible to be able to fit them in GUI drop down box #define D_SENSOR_NONE "Brak" #define D_SENSOR_USER "User" @@ -576,12 +582,16 @@ #define D_SENSOR_TXD "Serial Tx" #define D_SENSOR_RXD "Serial Rx" #define D_SENSOR_ROTARY "Rotary" // Suffix "1A" +#define D_SENSOR_HRE_CLOCK "HRE Clock" +#define D_SENSOR_HRE_DATA "HRE Data" // Units #define D_UNIT_AMPERE "A" #define D_UNIT_CENTIMETER "cm" #define D_UNIT_HERTZ "Hz" #define D_UNIT_HOUR "Godz" +#define D_UNIT_GALLONS "gal" +#define D_UNIT_GALLONS_PER_MIN "g/m" #define D_UNIT_INCREMENTS "inc" #define D_UNIT_KILOGRAM "kg" #define D_UNIT_KILOMETER_PER_HOUR "km/h" // or "km/h" diff --git a/sonoff/language/pt-BR.h b/sonoff/language/pt-BR.h index 63efc3f1e..40feecc1c 100644 --- a/sonoff/language/pt-BR.h +++ b/sonoff/language/pt-BR.h @@ -93,6 +93,7 @@ #define D_FALLBACK_TOPIC "Tópico para retornar" #define D_FALSE "Falso" #define D_FILE "Arquivo" +#define D_FLOW_RATE "Flow rate" #define D_FREE_MEMORY "Memória livre" #define D_FREQUENCY "Frequência" #define D_GAS "Gás" @@ -156,6 +157,7 @@ #define D_TO "Para" #define D_TOGGLE "Inverter" #define D_TOPIC "Tópico" +#define D_TOTAL_USAGE "Total Usage" #define D_TRANSMIT "Transmitir" #define D_TRUE "Verdadeiro" #define D_TVOC "TVOC" @@ -305,6 +307,7 @@ #define D_BASE_TYPE "Based on" #define D_TEMPLATE_FLAGS "Options" #define D_ALLOW_ADC0 "ADC0 input" +#define D_ALLOW_ADC0_TEMP "ADC0 temperature" #define D_ALLOW_PULLUP "User pull-up selection" #define D_SAVE_CONFIGURATION "Gravar configuração" @@ -492,6 +495,9 @@ #define D_TX20_SOUTH "S" #define D_TX20_WEST "W" +//xsns_43_hre.ino +#define D_LOG_HRE "HRE: " + // sonoff_template.h - keep them as short as possible to be able to fit them in GUI drop down box #define D_SENSOR_NONE "Nenhum" #define D_SENSOR_USER "User" @@ -576,12 +582,16 @@ #define D_SENSOR_TXD "Serial Tx" #define D_SENSOR_RXD "Serial Rx" #define D_SENSOR_ROTARY "Rotary" // Suffix "1A" +#define D_SENSOR_HRE_CLOCK "HRE Clock" +#define D_SENSOR_HRE_DATA "HRE Data" // Units #define D_UNIT_AMPERE "A" #define D_UNIT_CENTIMETER "cm" #define D_UNIT_HERTZ "Hz" #define D_UNIT_HOUR "H" +#define D_UNIT_GALLONS "gal" +#define D_UNIT_GALLONS_PER_MIN "g/m" #define D_UNIT_INCREMENTS "inc" #define D_UNIT_KILOGRAM "kg" #define D_UNIT_KILOMETER_PER_HOUR "km/h" // or "km/h" diff --git a/sonoff/language/pt-PT.h b/sonoff/language/pt-PT.h index 171f33d9b..3cc8343d5 100644 --- a/sonoff/language/pt-PT.h +++ b/sonoff/language/pt-PT.h @@ -93,6 +93,7 @@ #define D_FALLBACK_TOPIC "Tópico para retornar" #define D_FALSE "Falso" #define D_FILE "Ficheiro" +#define D_FLOW_RATE "Flow rate" #define D_FREE_MEMORY "Memoria Livre" #define D_FREQUENCY "Frequency" #define D_GAS "Gás" @@ -156,6 +157,7 @@ #define D_TO "para" #define D_TOGGLE "Pressionar" #define D_TOPIC "Tópico" +#define D_TOTAL_USAGE "Total Usage" #define D_TRANSMIT "Transmitir" #define D_TRUE "Verdadeiro" #define D_TVOC "TVOC" @@ -305,6 +307,7 @@ #define D_BASE_TYPE "Based on" #define D_TEMPLATE_FLAGS "Options" #define D_ALLOW_ADC0 "ADC0 input" +#define D_ALLOW_ADC0_TEMP "ADC0 temperature" #define D_ALLOW_PULLUP "User pull-up selection" #define D_SAVE_CONFIGURATION "Salvar configuração" @@ -492,6 +495,9 @@ #define D_TX20_SOUTH "S" #define D_TX20_WEST "W" +//xsns_43_hre.ino +#define D_LOG_HRE "HRE: " + // sonoff_template.h - keep them as short as possible to be able to fit them in GUI drop down box #define D_SENSOR_NONE "Nenhum" #define D_SENSOR_USER "User" @@ -576,12 +582,16 @@ #define D_SENSOR_TXD "Serial Tx" #define D_SENSOR_RXD "Serial Rx" #define D_SENSOR_ROTARY "Rotary" // Suffix "1A" +#define D_SENSOR_HRE_CLOCK "HRE Clock" +#define D_SENSOR_HRE_DATA "HRE Data" // Units #define D_UNIT_AMPERE "A" #define D_UNIT_CENTIMETER "cm" #define D_UNIT_HERTZ "Hz" #define D_UNIT_HOUR "Hr" +#define D_UNIT_GALLONS "gal" +#define D_UNIT_GALLONS_PER_MIN "g/m" #define D_UNIT_INCREMENTS "inc" #define D_UNIT_KILOGRAM "kg" #define D_UNIT_KILOMETER_PER_HOUR "km/h" // or "km/h" diff --git a/sonoff/language/ru-RU.h b/sonoff/language/ru-RU.h index e4112953c..d91bd427c 100644 --- a/sonoff/language/ru-RU.h +++ b/sonoff/language/ru-RU.h @@ -93,6 +93,7 @@ #define D_FALLBACK_TOPIC "Топик обратной связи" #define D_FALSE "Ложно" #define D_FILE "Файл" +#define D_FLOW_RATE "Flow rate" #define D_FREE_MEMORY "Свободная память" #define D_FREQUENCY "Frequency" #define D_GAS "Газ" @@ -156,6 +157,7 @@ #define D_TO "до" #define D_TOGGLE "Переключить" #define D_TOPIC "Топик" +#define D_TOTAL_USAGE "Total Usage" #define D_TRANSMIT "Передать" #define D_TRUE "Истина" #define D_TVOC "TVOC" @@ -305,6 +307,7 @@ #define D_BASE_TYPE "Based on" #define D_TEMPLATE_FLAGS "Options" #define D_ALLOW_ADC0 "ADC0 input" +#define D_ALLOW_ADC0_TEMP "ADC0 temperature" #define D_ALLOW_PULLUP "User pull-up selection" #define D_SAVE_CONFIGURATION "Сохранить конфигурацию" @@ -492,6 +495,9 @@ #define D_TX20_SOUTH "S" #define D_TX20_WEST "W" +//xsns_43_hre.ino +#define D_LOG_HRE "HRE: " + // sonoff_template.h - keep them as short as possible to be able to fit them in GUI drop down box #define D_SENSOR_NONE "-нет-" #define D_SENSOR_USER "User" @@ -576,12 +582,16 @@ #define D_SENSOR_TXD "Serial Tx" #define D_SENSOR_RXD "Serial Rx" #define D_SENSOR_ROTARY "Rotary" // Suffix "1A" +#define D_SENSOR_HRE_CLOCK "HRE Clock" +#define D_SENSOR_HRE_DATA "HRE Data" // Units #define D_UNIT_AMPERE "А" #define D_UNIT_CENTIMETER "cm" #define D_UNIT_HERTZ "Hz" #define D_UNIT_HOUR "Ч" +#define D_UNIT_GALLONS "gal" +#define D_UNIT_GALLONS_PER_MIN "g/m" #define D_UNIT_INCREMENTS "inc" #define D_UNIT_KILOGRAM "kg" #define D_UNIT_KILOMETER_PER_HOUR "km/h" // or "km/h" diff --git a/sonoff/language/sk-SK.h b/sonoff/language/sk-SK.h index cf99703c8..8aa6174dc 100644 --- a/sonoff/language/sk-SK.h +++ b/sonoff/language/sk-SK.h @@ -75,6 +75,7 @@ #define D_COUNTER "Počítadlo" #define D_CURRENT "Prúd" // As in Voltage and Current #define D_DATA "Dáta" +#define D_FLOW_RATE "Flow rate" #define D_DARKLIGHT "Tmavý" #define D_DEBUG "Debug" #define D_DISABLED "Zablokované" @@ -156,6 +157,7 @@ #define D_TO "do" #define D_TOGGLE "Prepni" #define D_TOPIC "Topic" +#define D_TOTAL_USAGE "Total Usage" #define D_TRANSMIT "Odošli" #define D_TRUE "Pravda" #define D_TVOC "TVOC" @@ -305,6 +307,7 @@ #define D_BASE_TYPE "Based on" #define D_TEMPLATE_FLAGS "Options" #define D_ALLOW_ADC0 "ADC0 input" +#define D_ALLOW_ADC0_TEMP "ADC0 temperature" #define D_ALLOW_PULLUP "User pull-up selection" #define D_SAVE_CONFIGURATION "Ulož nastavenia" @@ -492,6 +495,9 @@ #define D_TX20_SOUTH "J" #define D_TX20_WEST "Z" +//xsns_43_hre.ino +#define D_LOG_HRE "HRE: " + // sonoff_template.h - keep them as short as possible to be able to fit them in GUI drop down box #define D_SENSOR_NONE "Žiaden" #define D_SENSOR_USER "User" @@ -576,12 +582,16 @@ #define D_SENSOR_TXD "Serial Tx" #define D_SENSOR_RXD "Serial Rx" #define D_SENSOR_ROTARY "Rotary" // Suffix "1A" +#define D_SENSOR_HRE_CLOCK "HRE Clock" +#define D_SENSOR_HRE_DATA "HRE Data" // Units #define D_UNIT_AMPERE "A" #define D_UNIT_CENTIMETER "cm" #define D_UNIT_HERTZ "Hz" #define D_UNIT_HOUR "hod" +#define D_UNIT_GALLONS "gal" +#define D_UNIT_GALLONS_PER_MIN "g/m" #define D_UNIT_KILOGRAM "kg" #define D_UNIT_INCREMENTS "inc" #define D_UNIT_KILOMETER_PER_HOUR "km/h" // or "km/h" diff --git a/sonoff/language/sv-SE.h b/sonoff/language/sv-SE.h index 6a92c1bc1..e82717157 100644 --- a/sonoff/language/sv-SE.h +++ b/sonoff/language/sv-SE.h @@ -93,6 +93,7 @@ #define D_FALLBACK_TOPIC "Reservämne" #define D_FALSE "Falskt" #define D_FILE "Fil" +#define D_FLOW_RATE "Flow rate" #define D_FREE_MEMORY "Ledigt minne" #define D_FREQUENCY "Frekvens" #define D_GAS "Gas" @@ -156,6 +157,7 @@ #define D_TO "till" #define D_TOGGLE "Växla" #define D_TOPIC "Ämne" +#define D_TOTAL_USAGE "Total Usage" #define D_TRANSMIT "Sänd" #define D_TRUE "Sant" #define D_TVOC "TVOC" @@ -305,6 +307,7 @@ #define D_BASE_TYPE "Based on" #define D_TEMPLATE_FLAGS "Options" #define D_ALLOW_ADC0 "ADC0 input" +#define D_ALLOW_ADC0_TEMP "ADC0 temperature" #define D_ALLOW_PULLUP "User pull-up selection" #define D_SAVE_CONFIGURATION "Spara konfiguration" @@ -492,6 +495,9 @@ #define D_TX20_SOUTH "S" #define D_TX20_WEST "V" +//xsns_43_hre.ino +#define D_LOG_HRE "HRE: " + // sonoff_template.h - keep them as short as possible to be able to fit them in GUI drop down box #define D_SENSOR_NONE "Ingen" #define D_SENSOR_USER "User" @@ -576,12 +582,16 @@ #define D_SENSOR_TXD "Serial Tx" #define D_SENSOR_RXD "Serial Rx" #define D_SENSOR_ROTARY "Rotary" // Suffix "1A" +#define D_SENSOR_HRE_CLOCK "HRE Clock" +#define D_SENSOR_HRE_DATA "HRE Data" // Units #define D_UNIT_AMPERE "A" #define D_UNIT_CENTIMETER "cm" #define D_UNIT_HERTZ "Hz" #define D_UNIT_HOUR "Tim" +#define D_UNIT_GALLONS "gal" +#define D_UNIT_GALLONS_PER_MIN "g/m" #define D_UNIT_INCREMENTS "ink" #define D_UNIT_KILOGRAM "kg" #define D_UNIT_KILOMETER_PER_HOUR "km/h" // or "km/h" diff --git a/sonoff/language/tr-TR.h b/sonoff/language/tr-TR.h index b887c89c2..aec12b8fb 100755 --- a/sonoff/language/tr-TR.h +++ b/sonoff/language/tr-TR.h @@ -93,6 +93,7 @@ #define D_FALLBACK_TOPIC "Geri İletim Topiği" #define D_FALSE "False" #define D_FILE "Dosya" +#define D_FLOW_RATE "Flow rate" #define D_FREE_MEMORY "Boş Hafıza" #define D_FREQUENCY "Frekans" #define D_GAS "Gas" @@ -156,6 +157,7 @@ #define D_TO "den" #define D_TOGGLE "Geçiş Tuşu" #define D_TOPIC "Başlık" +#define D_TOTAL_USAGE "Total Usage" #define D_TRANSMIT "İletim" #define D_TRUE "True" #define D_TVOC "TVOC" @@ -305,6 +307,7 @@ #define D_BASE_TYPE "Based on" #define D_TEMPLATE_FLAGS "Options" #define D_ALLOW_ADC0 "ADC0 input" +#define D_ALLOW_ADC0_TEMP "ADC0 temperature" #define D_ALLOW_PULLUP "User pull-up selection" #define D_SAVE_CONFIGURATION "Ayarları Kaydet" @@ -492,6 +495,9 @@ #define D_TX20_SOUTH "S" #define D_TX20_WEST "W" +//xsns_43_hre.ino +#define D_LOG_HRE "HRE: " + // sonoff_template.h - keep them as short as possible to be able to fit them in GUI drop down box #define D_SENSOR_NONE "None" #define D_SENSOR_USER "User" @@ -576,11 +582,15 @@ #define D_SENSOR_TXD "Serial Tx" #define D_SENSOR_RXD "Serial Rx" #define D_SENSOR_ROTARY "Rotary" // Suffix "1A" +#define D_SENSOR_HRE_CLOCK "HRE Clock" +#define D_SENSOR_HRE_DATA "HRE Data" // Units #define D_UNIT_AMPERE "A" #define D_UNIT_CENTIMETER "cm" #define D_UNIT_HOUR "Hr" +#define D_UNIT_GALLONS "gal" +#define D_UNIT_GALLONS_PER_MIN "g/m" #define D_UNIT_INCREMENTS "inc" #define D_UNIT_KILOGRAM "kg" #define D_UNIT_KILOMETER_PER_HOUR "km/h" // or "km/h" diff --git a/sonoff/language/uk-UK.h b/sonoff/language/uk-UK.h index 0efdd1c94..6bf6397bf 100644 --- a/sonoff/language/uk-UK.h +++ b/sonoff/language/uk-UK.h @@ -93,6 +93,7 @@ #define D_FALLBACK_TOPIC "Топік зворотнього зв'язку" #define D_FALSE "Помилково" #define D_FILE "Файл" +#define D_FLOW_RATE "Flow rate" #define D_FREE_MEMORY "Вільна память" #define D_FREQUENCY "Частота" #define D_GAS "Газ" @@ -156,6 +157,7 @@ #define D_TO "до" #define D_TOGGLE "Перекл." #define D_TOPIC "Топік" +#define D_TOTAL_USAGE "Total Usage" #define D_TRANSMIT "Передати" #define D_TRUE "Істина" #define D_TVOC "TVOC" @@ -305,6 +307,7 @@ #define D_BASE_TYPE "Based on" #define D_TEMPLATE_FLAGS "Options" #define D_ALLOW_ADC0 "ADC0 input" +#define D_ALLOW_ADC0_TEMP "ADC0 temperature" #define D_ALLOW_PULLUP "User pull-up selection" #define D_SAVE_CONFIGURATION "Зберегти конфігурацію" @@ -492,6 +495,9 @@ #define D_TX20_SOUTH "S" #define D_TX20_WEST "W" +//xsns_43_hre.ino +#define D_LOG_HRE "HRE: " + // sonoff_template.h - keep them as short as possible to be able to fit them in GUI drop down box #define D_SENSOR_NONE "-відсутньо-" #define D_SENSOR_USER "User" @@ -576,12 +582,16 @@ #define D_SENSOR_TXD "Serial Tx" #define D_SENSOR_RXD "Serial Rx" #define D_SENSOR_ROTARY "Rotary" // Suffix "1A" +#define D_SENSOR_HRE_CLOCK "HRE Clock" +#define D_SENSOR_HRE_DATA "HRE Data" // Units #define D_UNIT_AMPERE "А" #define D_UNIT_CENTIMETER "cм" #define D_UNIT_HERTZ "Гц" #define D_UNIT_HOUR "Г" +#define D_UNIT_GALLONS "gal" +#define D_UNIT_GALLONS_PER_MIN "g/m" #define D_UNIT_INCREMENTS "inc" #define D_UNIT_KILOGRAM "kg" #define D_UNIT_KILOMETER_PER_HOUR "km/h" // or "km/h" diff --git a/sonoff/language/zh-CN.h b/sonoff/language/zh-CN.h index 88767053b..a48f6a474 100644 --- a/sonoff/language/zh-CN.h +++ b/sonoff/language/zh-CN.h @@ -93,6 +93,7 @@ #define D_FALLBACK_TOPIC "回退主题" #define D_FALSE "False" #define D_FILE "文件:" +#define D_FLOW_RATE "Flow rate" #define D_FREE_MEMORY "空闲内存" #define D_FREQUENCY "频率" #define D_GAS "气体" @@ -156,6 +157,7 @@ #define D_TO "to" #define D_TOGGLE "切换" #define D_TOPIC "主题" +#define D_TOTAL_USAGE "Total Usage" #define D_TRANSMIT "发送" #define D_TRUE "True" #define D_TVOC "TVOC" @@ -305,6 +307,7 @@ #define D_BASE_TYPE "基于" #define D_TEMPLATE_FLAGS "选项" #define D_ALLOW_ADC0 "ADC0 输入" +#define D_ALLOW_ADC0_TEMP "ADC0 temperature" #define D_ALLOW_PULLUP "用户上拉选择" #define D_SAVE_CONFIGURATION "保存设置" @@ -492,6 +495,9 @@ #define D_TX20_SOUTH "南" #define D_TX20_WEST "西" +//xsns_43_hre.ino +#define D_LOG_HRE "HRE: " + // sonoff_template.h - keep them as short as possible to be able to fit them in GUI drop down box #define D_SENSOR_NONE "无" #define D_SENSOR_USER "User" @@ -576,11 +582,15 @@ #define D_SENSOR_TXD "Serial Tx" #define D_SENSOR_RXD "Serial Rx" #define D_SENSOR_ROTARY "Rotary" // Suffix "1A" +#define D_SENSOR_HRE_CLOCK "HRE Clock" +#define D_SENSOR_HRE_DATA "HRE Data" // Units #define D_UNIT_AMPERE "安" #define D_UNIT_CENTIMETER "厘米" #define D_UNIT_HOUR "时" +#define D_UNIT_GALLONS "gal" +#define D_UNIT_GALLONS_PER_MIN "g/m" #define D_UNIT_INCREMENTS "inc" #define D_UNIT_KILOGRAM "千克" #define D_UNIT_KILOMETER_PER_HOUR "公里/时" // or "km/h" diff --git a/sonoff/language/zh-TW.h b/sonoff/language/zh-TW.h index 11f85eef2..3caff8aa4 100644 --- a/sonoff/language/zh-TW.h +++ b/sonoff/language/zh-TW.h @@ -93,6 +93,7 @@ #define D_FALLBACK_TOPIC "Fallback Topic" #define D_FALSE "False" #define D_FILE "文件:" +#define D_FLOW_RATE "Flow rate" #define D_FREE_MEMORY "可用記憶體" #define D_FREQUENCY "Frequency" #define D_GAS "氣體" @@ -156,6 +157,7 @@ #define D_TO "to" #define D_TOGGLE "切換" #define D_TOPIC "主題" +#define D_TOTAL_USAGE "Total Usage" #define D_TRANSMIT "發送" #define D_TRUE "True" #define D_TVOC "TVOC" @@ -305,6 +307,7 @@ #define D_BASE_TYPE "Based on" #define D_TEMPLATE_FLAGS "Options" #define D_ALLOW_ADC0 "ADC0 input" +#define D_ALLOW_ADC0_TEMP "ADC0 temperature" #define D_ALLOW_PULLUP "User pull-up selection" #define D_SAVE_CONFIGURATION "保存設置" @@ -492,6 +495,9 @@ #define D_TX20_SOUTH "S" #define D_TX20_WEST "W" +//xsns_43_hre.ino +#define D_LOG_HRE "HRE: " + // sonoff_template.h - keep them as short as possible to be able to fit them in GUI drop down box #define D_SENSOR_NONE "None" #define D_SENSOR_USER "User" @@ -576,12 +582,16 @@ #define D_SENSOR_TXD "Serial Tx" #define D_SENSOR_RXD "Serial Rx" #define D_SENSOR_ROTARY "Rotary" // Suffix "1A" +#define D_SENSOR_HRE_CLOCK "HRE Clock" +#define D_SENSOR_HRE_DATA "HRE Data" // Units #define D_UNIT_AMPERE "安" #define D_UNIT_CENTIMETER "cm" #define D_UNIT_HERTZ "Hz" #define D_UNIT_HOUR "時" +#define D_UNIT_GALLONS "gal" +#define D_UNIT_GALLONS_PER_MIN "g/m" #define D_UNIT_INCREMENTS "inc" #define D_UNIT_KILOGRAM "kg" #define D_UNIT_KILOMETER_PER_HOUR "km/h" // or "km/h" diff --git a/sonoff/my_user_config.h b/sonoff/my_user_config.h index b7e0846e4..c076574e5 100644 --- a/sonoff/my_user_config.h +++ b/sonoff/my_user_config.h @@ -135,6 +135,25 @@ #define WEB_PASSWORD "" // [WebPassword] Web server Admin mode Password for WEB_USERNAME (empty string = Disable) #define FRIENDLY_NAME "Sonoff" // [FriendlyName] Friendlyname up to 32 characters used by webpages and Alexa #define EMULATION EMUL_NONE // [Emulation] Select Belkin WeMo (single relay/light) or Hue Bridge emulation (multi relay/light) (EMUL_NONE, EMUL_WEMO or EMUL_HUE) +// HTML hex color codes. Only 3 and 6 digit hex string values are supported!! See https://www.w3schools.com/colors/colors_hex.asp +#define COLOR_TEXT "#000" // [WebColor1] Global text color - Black +#define COLOR_BACKGROUND "#fff" // [WebColor2] Global background color - White +#define COLOR_FORM "#f2f2f2" // [WebColor3] Form background color - Greyish +#define COLOR_INPUT_TEXT "#000" // [WebColor4] Input text color - Black +#define COLOR_INPUT "#fff" // [WebColor5] Input background color - White +#define COLOR_CONSOLE_TEXT "#000" // [WebColor6] Console text color - Black +#define COLOR_CONSOLE "#fff" // [WebColor7] Console background color - White +#define COLOR_TEXT_WARNING "#f00" // [WebColor8] Warning text color - Red +#define COLOR_TEXT_SUCCESS "#008000" // [WebColor9] Success text color - Green +#define COLOR_BUTTON_TEXT "#fff" // [WebColor10] Button text color - White +#define COLOR_BUTTON "#1fa3ec" // [WebColor11] Button color - Blueish +#define COLOR_BUTTON_HOVER "#0e70a4" // [WebColor12] Button color when hovered over - Darker blueish +#define COLOR_BUTTON_RESET "#d43535" // [WebColor13] Restart/Reset/Delete button color - Redish +#define COLOR_BUTTON_RESET_HOVER "#931f1f" // [WebColor14] Restart/Reset/Delete button color when hovered over - Darker redish +#define COLOR_BUTTON_SAVE "#47c266" // [WebColor15] Save button color - Greenish +#define COLOR_BUTTON_SAVE_HOVER "#5aaf6f" // [WebColor16] Save button color when hovered over - Darker greenish +#define COLOR_TIMER_TAB_TEXT "#fff" // [WebColor17] Config timer tab text color - White +#define COLOR_TIMER_TAB_BACKGROUND "#999" // [WebColor18] Config timer tab background color - Light grey // -- mDNS ---------------------------------------- #define MDNS_ENABLED 0 // [SetOption55] Use mDNS (0 = Disable, 1 = Enable) @@ -168,6 +187,7 @@ #define APP_TIMEZONE 1 // [Timezone] +1 hour (Amsterdam) (-13 .. 14 = hours from UTC, 99 = use TIME_DST/TIME_STD) #define APP_LEDSTATE LED_POWER // [LedState] Function of led // (LED_OFF, LED_POWER, LED_MQTTSUB, LED_POWER_MQTTSUB, LED_MQTTPUB, LED_POWER_MQTTPUB, LED_MQTT, LED_POWER_MQTT) +#define APP_LEDMASK 0xFFFF // [LedMask] Assign Relay to Power led (0xFFFF is default) #define APP_PULSETIME 0 // [PulseTime] Time in 0.1 Sec to turn off power for relay 1 (0 = disabled) #define APP_POWERON_STATE POWER_ALL_SAVED // [PowerOnState] Power On Relay state // (POWER_ALL_OFF, POWER_ALL_ON, POWER_ALL_SAVED_TOGGLE, POWER_ALL_SAVED, POWER_ALL_ALWAYS_ON, POWER_ALL_OFF_PULSETIME_ON) @@ -204,7 +224,7 @@ //#define MY_LANGUAGE de-DE // German in Germany //#define MY_LANGUAGE el-GR // Greek in Greece //#define MY_LANGUAGE en-GB // English in Great Britain. Enabled by Default -//#define MY_LANGUAGE es-AR // Spanish in Argentina +//#define MY_LANGUAGE es-ES // Spanish in Spain //#define MY_LANGUAGE fr-FR // French in France //#define MY_LANGUAGE he-HE // Hebrew in Israel //#define MY_LANGUAGE hu-HU // Hungarian in Hungary @@ -369,6 +389,7 @@ #define TUYA_DIMMER_ID 0 // Default dimmer Id #define USE_ARMTRONIX_DIMMERS // Add support for Armtronix Dimmers (+1k4 code) #define USE_PS_16_DZ // Add support for PS-16-DZ Dimmer +//#define ROTARY_V1 // Add support for MI Desk Lamp //#define USE_AZ7798 // Add support for AZ-Instrument 7798 CO2 datalogger (+1k6 code) //#define USE_PN532_HSU // Add support for PN532 using HSU (Serial) interface (+1k8 code, 140 bytes mem) // #define USE_PN532_CAUSE_EVENTS // Cause event execution for PN532_UID= and PN532_DATA=[if defined] (+ 30 bytes code) @@ -415,6 +436,8 @@ #define USE_SM16716 // Add support for SM16716 RGB LED controller (+0k7 code) +//#define USE_HRE // Add support for Badger HR-E Water Meter (+1k4 code) + /*********************************************************************************************\ * Debug features are only supported in development branch \*********************************************************************************************/ diff --git a/sonoff/settings.h b/sonoff/settings.h index 5a7827365..1bb48ab8b 100644 --- a/sonoff/settings.h +++ b/sonoff/settings.h @@ -20,7 +20,7 @@ #ifndef _SETTINGS_H_ #define _SETTINGS_H_ -#define PARAM8_SIZE 18 // Number of param bytes (SetOption) +const uint8_t PARAM8_SIZE = 18; // Number of param bytes (SetOption) typedef union { // Restricted by MISRA-C Rule 18.4 but so useful... uint32_t data; // Allow bit manipulation using SetOption @@ -185,12 +185,12 @@ struct SYSCFG { unsigned long bootcount; // 00C */ struct SYSCFG { - uint16_t cfg_holder; // 000 v6 header - uint16_t cfg_size; // 002 + uint16_t cfg_holder; // 000 v6 header + uint16_t cfg_size; // 002 unsigned long save_flag; // 004 unsigned long version; // 008 - uint16_t bootcount; // 00C - uint16_t cfg_crc; // 00E + uint16_t bootcount; // 00C + uint16_t cfg_crc; // 00E SysBitfield flag; // 010 int16_t save_data; // 014 int8_t timezone; // 016 @@ -330,8 +330,10 @@ struct SYSCFG { uint8_t rgbwwTable[5]; // 71A uint8_t user_template_base; // 71F mytmplt user_template; // 720 29 bytes + uint8_t novasds_period; // 73D + uint8_t web_color[18][3]; // 73E - uint8_t free_73D[87]; // 73D + uint8_t free_774[32]; // 774 uint32_t drivers[3]; // 794 uint32_t monitors; // 7A0 @@ -339,9 +341,7 @@ struct SYSCFG { uint32_t displays; // 7B0 uint32_t energy_kWhtotal_time; // 7B4 unsigned long weight_item; // 7B8 Weight of one item in gram * 10 - - uint8_t free_7BC[2]; // 7BC - + uint16_t ledmask; // 7BC uint16_t weight_max; // 7BE Total max weight in kilogram unsigned long weight_reference; // 7C0 Reference weight in gram unsigned long weight_calibration; // 7C4 @@ -396,7 +396,7 @@ struct XDRVMAILBOX { char *data; } XdrvMailbox; -#define MAX_RULES_FLAG 7 // Number of bits used in RulesBitfield (tricky I know...) +const uint8_t MAX_RULES_FLAG = 8; // Number of bits used in RulesBitfield (tricky I know...) typedef union { // Restricted by MISRA-C Rule 18.4 but so useful... uint16_t data; // Allow bit manipulation struct { @@ -407,7 +407,7 @@ typedef union { // Restricted by MISRA-C Rule 18.4 bu uint16_t mqtt_disconnected : 1; uint16_t wifi_connected : 1; uint16_t wifi_disconnected : 1; - uint16_t spare07 : 1; + uint16_t http_init : 1; uint16_t spare08 : 1; uint16_t spare09 : 1; uint16_t spare10 : 1; diff --git a/sonoff/settings.ino b/sonoff/settings.ino index dff0d849f..f84173504 100644 --- a/sonoff/settings.ino +++ b/sonoff/settings.ino @@ -18,36 +18,36 @@ */ #ifndef DOMOTICZ_UPDATE_TIMER -#define DOMOTICZ_UPDATE_TIMER 0 // [DomoticzUpdateTimer] Send relay status (0 = disable, 1 - 3600 seconds) (Optional) +#define DOMOTICZ_UPDATE_TIMER 0 // [DomoticzUpdateTimer] Send relay status (0 = disable, 1 - 3600 seconds) (Optional) #endif #ifndef EMULATION -#define EMULATION EMUL_NONE // [Emulation] Select Belkin WeMo (single relay/light) or Hue Bridge emulation (multi relay/light) (EMUL_NONE, EMUL_WEMO or EMUL_HUE) +#define EMULATION EMUL_NONE // [Emulation] Select Belkin WeMo (single relay/light) or Hue Bridge emulation (multi relay/light) (EMUL_NONE, EMUL_WEMO or EMUL_HUE) #endif -#ifndef MTX_ADDRESS1 // Add Display Support for up to eigth Matrices -#define MTX_ADDRESS1 0 +#ifndef MTX_ADDRESS1 // Add Display Support for up to eigth Matrices +#define MTX_ADDRESS1 0 #endif #ifndef MTX_ADDRESS2 -#define MTX_ADDRESS2 0 +#define MTX_ADDRESS2 0 #endif #ifndef MTX_ADDRESS3 -#define MTX_ADDRESS3 0 +#define MTX_ADDRESS3 0 #endif #ifndef MTX_ADDRESS4 -#define MTX_ADDRESS4 0 +#define MTX_ADDRESS4 0 #endif #ifndef MTX_ADDRESS5 -#define MTX_ADDRESS5 0 +#define MTX_ADDRESS5 0 #endif #ifndef MTX_ADDRESS6 -#define MTX_ADDRESS6 0 +#define MTX_ADDRESS6 0 #endif #ifndef MTX_ADDRESS7 -#define MTX_ADDRESS7 0 +#define MTX_ADDRESS7 0 #endif #ifndef MTX_ADDRESS8 -#define MTX_ADDRESS8 0 +#define MTX_ADDRESS8 0 #endif #ifndef HOME_ASSISTANT_DISCOVERY_ENABLE @@ -55,17 +55,91 @@ #endif #ifndef LATITUDE -#define LATITUDE 48.858360 // [Latitude] Your location to be used with sunrise and sunset +#define LATITUDE 48.858360 // [Latitude] Your location to be used with sunrise and sunset #endif #ifndef LONGITUDE -#define LONGITUDE 2.294442 // [Longitude] Your location to be used with sunrise and sunset +#define LONGITUDE 2.294442 // [Longitude] Your location to be used with sunrise and sunset #endif +#ifndef WORKING_PERIOD +#define WORKING_PERIOD 5 // Working period of the SDS Sensor, Takes a reading every X Minutes +#endif + +#ifndef COLOR_TEXT +#define COLOR_TEXT "#000" // Global text color - Black +#endif +#ifndef COLOR_BACKGROUND +#define COLOR_BACKGROUND "#fff" // Global background color - White +#endif +#ifndef COLOR_FORM +#define COLOR_FORM "#f2f2f2" // Form background color - Greyish +#endif +#ifndef COLOR_INPUT_TEXT +#define COLOR_INPUT_TEXT "#000" // Input text color - Black +#endif +#ifndef COLOR_INPUT +#define COLOR_INPUT "#fff" // Input background color - White +#endif +#ifndef COLOR_CONSOLE_TEXT +#define COLOR_CONSOLE_TEXT "#000" // Console text color - Black +#endif +#ifndef COLOR_CONSOLE +#define COLOR_CONSOLE "#fff" // Console background color - White +#endif +#ifndef COLOR_TEXT_WARNING +#define COLOR_TEXT_WARNING "#f00" // Warning text color - Red +#endif +#ifndef COLOR_TEXT_SUCCESS +#define COLOR_TEXT_SUCCESS "#008000" // Success text color - Green +#endif +#ifndef COLOR_BUTTON_TEXT +#define COLOR_BUTTON_TEXT "#fff" // Button text color - White +#endif +#ifndef COLOR_BUTTON +#define COLOR_BUTTON "#1fa3ec" // Button color - Blueish +#endif +#ifndef COLOR_BUTTON_HOVER +#define COLOR_BUTTON_HOVER "#0e70a4" // Button color when hovered over - Darker blueish +#endif +#ifndef COLOR_BUTTON_RESET +#define COLOR_BUTTON_RESET "#d43535" // Restart/Reset/Delete button color - Redish +#endif +#ifndef COLOR_BUTTON_RESET_HOVER +#define COLOR_BUTTON_RESET_HOVER "#931f1f" // Restart/Reset/Delete button color when hovered over - Darker redish +#endif +#ifndef COLOR_BUTTON_SAVE +#define COLOR_BUTTON_SAVE "#47c266" // Save button color - Greenish +#endif +#ifndef COLOR_BUTTON_SAVE_HOVER +#define COLOR_BUTTON_SAVE_HOVER "#5aaf6f" // Save button color when hovered over - Darker greenish +#endif +#ifndef COLOR_TIMER_TAB_TEXT +#define COLOR_TIMER_TAB_TEXT "#fff" // Config timer tab text color - White +#endif +#ifndef COLOR_TIMER_TAB_BACKGROUND +#define COLOR_TIMER_TAB_BACKGROUND "#999" // Config timer tab background color - Light grey +#endif + +enum WebColors { + COL_TEXT, COL_BACKGROUND, COL_FORM, + COL_INPUT_TEXT, COL_INPUT, COL_CONSOLE_TEXT, COL_CONSOLE, + COL_TEXT_WARNING, COL_TEXT_SUCCESS, + COL_BUTTON_TEXT, COL_BUTTON, COL_BUTTON_HOVER, COL_BUTTON_RESET, COL_BUTTON_RESET_HOVER, COL_BUTTON_SAVE, COL_BUTTON_SAVE_HOVER, + COL_TIMER_TAB_TEXT, COL_TIMER_TAB_BACKGROUND, + COL_LAST }; + +const char kWebColors[] PROGMEM = + COLOR_TEXT "|" COLOR_BACKGROUND "|" COLOR_FORM "|" + COLOR_INPUT_TEXT "|" COLOR_INPUT "|" COLOR_CONSOLE_TEXT "|" COLOR_CONSOLE "|" + COLOR_TEXT_WARNING "|" COLOR_TEXT_SUCCESS "|" + COLOR_BUTTON_TEXT "|" COLOR_BUTTON "|" COLOR_BUTTON_HOVER "|" COLOR_BUTTON_RESET "|" COLOR_BUTTON_RESET_HOVER "|" COLOR_BUTTON_SAVE "|" COLOR_BUTTON_SAVE_HOVER "|" + COLOR_TIMER_TAB_TEXT "|" COLOR_TIMER_TAB_BACKGROUND; + /*********************************************************************************************\ * RTC memory \*********************************************************************************************/ -#define RTC_MEM_VALID 0xA55A +const uint16_t RTC_MEM_VALID = 0xA55A; uint32_t rtc_settings_crc = 0; @@ -164,16 +238,18 @@ extern "C" { extern "C" uint32_t _SPIFFS_end; // From libraries/EEPROM/EEPROM.cpp EEPROMClass -#define SPIFFS_END ((uint32_t)&_SPIFFS_end - 0x40200000) / SPI_FLASH_SEC_SIZE +const uint32_t SPIFFS_END = ((uint32_t)&_SPIFFS_end - 0x40200000) / SPI_FLASH_SEC_SIZE; // Version 4.2 config = eeprom area -#define SETTINGS_LOCATION SPIFFS_END // No need for SPIFFS as it uses EEPROM area +const uint32_t SETTINGS_LOCATION = SPIFFS_END; // No need for SPIFFS as it uses EEPROM area // Version 5.2 allow for more flash space -#define CFG_ROTATES 8 // Number of flash sectors used (handles uploads) +const uint8_t CFG_ROTATES = 8; // Number of flash sectors used (handles uploads) /*********************************************************************************************\ - * EEPROM support based on EEPROM library and tuned for Tasmota + * Optional EEPROM support based on EEPROM library and tuned for Tasmota \*********************************************************************************************/ +//#define USE_EEPROM +#ifdef USE_EEPROM uint32_t eeprom_sector = SPIFFS_END; uint8_t* eeprom_data = 0; @@ -301,12 +377,12 @@ void EepromEnd(void) eeprom_size = 0; eeprom_dirty = false; } - +#endif // USE_EEPROM /********************************************************************************************/ uint16_t settings_crc = 0; uint32_t settings_location = SETTINGS_LOCATION; -uint8_t *settings_buffer = NULL; +uint8_t *settings_buffer = nullptr; /********************************************************************************************/ /* @@ -333,9 +409,9 @@ void SetFlashModeDout(void) void SettingsBufferFree(void) { - if (settings_buffer != NULL) { + if (settings_buffer != nullptr) { free(settings_buffer); - settings_buffer = NULL; + settings_buffer = nullptr; } } @@ -368,7 +444,9 @@ void SettingsSaveAll(void) Settings.power = 0; } XsnsCall(FUNC_SAVE_BEFORE_RESTART); +#ifdef USE_EEPROM EepromCommit(); +#endif SettingsSave(0); } @@ -411,6 +489,7 @@ void SettingsSave(uint8_t rotate) Settings.cfg_size = sizeof(SYSCFG); Settings.cfg_crc = GetSettingsCrc(); +#ifdef USE_EEPROM if (SPIFFS_END == settings_location) { uint8_t* flash_buffer; flash_buffer = new uint8_t[SPI_FLASH_SEC_SIZE]; @@ -428,6 +507,10 @@ void SettingsSave(uint8_t rotate) ESP.flashEraseSector(settings_location); ESP.flashWrite(settings_location * SPI_FLASH_SEC_SIZE, (uint32*)&Settings, sizeof(SYSCFG)); } +#else + ESP.flashEraseSector(settings_location); + ESP.flashWrite(settings_location * SPI_FLASH_SEC_SIZE, (uint32*)&Settings, sizeof(SYSCFG)); +#endif // USE_EEPROM if (!stop_flash_rotate && rotate) { for (uint8_t i = 1; i < CFG_ROTATES; i++) { @@ -456,13 +539,17 @@ void SettingsLoad(void) settings_location = 0; uint32_t flash_location = SETTINGS_LOCATION +1; + uint16_t cfg_holder = 0; for (uint8_t i = 0; i < CFG_ROTATES; i++) { flash_location--; ESP.flashRead(flash_location * SPI_FLASH_SEC_SIZE, (uint32*)&Settings, sizeof(SYSCFG)); bool valid = false; if (Settings.version > 0x06000000) { - valid = (Settings.cfg_crc == GetSettingsCrc()); + bool almost_valid = (Settings.cfg_crc == GetSettingsCrc()); + // Sometimes CRC on pages below FB, overwritten by OTA, is fine but Settings are still invalid. So check cfg_holder too + if (almost_valid && (0 == cfg_holder)) { cfg_holder = Settings.cfg_holder; } // At FB always active cfg_holder + valid = (cfg_holder == Settings.cfg_holder); } else { ESP.flashRead((flash_location -1) * SPI_FLASH_SEC_SIZE, (uint32*)&_SettingsH, sizeof(SYSCFGH)); valid = (Settings.cfg_holder == _SettingsH.cfg_holder); @@ -481,7 +568,7 @@ void SettingsLoad(void) } if (settings_location > 0) { ESP.flashRead(settings_location * SPI_FLASH_SEC_SIZE, (uint32*)&Settings, sizeof(SYSCFG)); - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_CONFIG D_LOADED_FROM_FLASH_AT " %X, " D_COUNT " %d"), settings_location, Settings.save_flag); + AddLog_P2(LOG_LEVEL_NONE, PSTR(D_LOG_CONFIG D_LOADED_FROM_FLASH_AT " %X, " D_COUNT " %lu"), settings_location, Settings.save_flag); } #ifndef FIRMWARE_MINIMAL @@ -608,6 +695,7 @@ void SettingsDefaultSet2(void) Settings.blinktime = APP_BLINKTIME; Settings.blinkcount = APP_BLINKCOUNT; Settings.ledstate = APP_LEDSTATE; + Settings.ledmask = APP_LEDMASK; Settings.pulse_timer[0] = APP_PULSETIME; // for (uint8_t i = 1; i < MAX_PULSETIMERS; i++) { Settings.pulse_timer[i] = 0; } @@ -815,6 +903,10 @@ void SettingsDefaultSet2(void) Settings.rgbwwTable[j] = 255; } + Settings.novasds_period = WORKING_PERIOD; + + SettingsDefaultWebColor(); + memset(&Settings.drivers, 0xFF, 32); // Enable all possible monitors, displays, drivers and sensors } @@ -885,6 +977,14 @@ void SettingsDefaultSet_5_13_1c(void) SettingsResetDst(); } +void SettingsDefaultWebColor(void) +{ + char scolor[10]; + for (uint8_t i = 0; i < COL_LAST; i++) { + WebHexCode(i, GetTextIndexed(scolor, sizeof(scolor), i, kWebColors)); + } +} + /********************************************************************************************/ void SettingsDelta(void) @@ -1055,6 +1155,15 @@ void SettingsDelta(void) if (Settings.version < 0x06040113) { Settings.param[P_RGB_REMAP] = RGB_REMAP_RGBW; } + if (Settings.version < 0x06050003) { + Settings.novasds_period = WORKING_PERIOD; + } + if (Settings.version < 0x06050006) { + SettingsDefaultWebColor(); + } + if (Settings.version < 0x06050007) { + Settings.ledmask = APP_LEDMASK; + } Settings.version = VERSION; SettingsSave(1); diff --git a/sonoff/sonoff.h b/sonoff/sonoff.h index 635a1017a..cd1659388 100644 --- a/sonoff/sonoff.h +++ b/sonoff/sonoff.h @@ -42,109 +42,109 @@ \*********************************************************************************************/ typedef unsigned long power_t; // Power (Relay) type -#define POWER_MASK 0xffffffffUL // Power (Relay) full mask +const uint32_t POWER_MASK = 0xffffffffUL; // Power (Relay) full mask + +/*********************************************************************************************\ + * Constants +\*********************************************************************************************/ + +// Changes to the following MAX_ defines will impact settings layout +const uint8_t MAX_SWITCHES = 8; // Max number of switches +const uint8_t MAX_RELAYS = 8; // Max number of relays +const uint8_t MAX_INTERLOCKS = 4; // Max number of interlock groups (MAX_RELAYS / 2) +const uint8_t MAX_LEDS = 4; // Max number of leds +const uint8_t MAX_KEYS = 4; // Max number of keys or buttons +const uint8_t MAX_PWMS = 5; // Max number of PWM channels +const uint8_t MAX_COUNTERS = 4; // Max number of counter sensors +const uint8_t MAX_TIMERS = 16; // Max number of Timers +const uint8_t MAX_PULSETIMERS = 8; // Max number of supported pulse timers +const uint8_t MAX_FRIENDLYNAMES = 4; // Max number of Friendly names +const uint8_t MAX_DOMOTICZ_IDX = 4; // Max number of Domoticz device, key and switch indices +const uint8_t MAX_DOMOTICZ_SNS_IDX = 12; // Max number of Domoticz sensors indices +const uint8_t MAX_KNX_GA = 10; // Max number of KNX Group Addresses to read that can be set +const uint8_t MAX_KNX_CB = 10; // Max number of KNX Group Addresses to write that can be set +const uint8_t MAX_XNRG_DRIVERS = 32; // Max number of allowed energy drivers +const uint8_t MAX_XDSP_DRIVERS = 32; // Max number of allowed display drivers +const uint8_t MAX_XDRV_DRIVERS = 96; // Max number of allowed driver drivers +const uint8_t MAX_XSNS_DRIVERS = 96; // Max number of allowed sensor drivers +const uint8_t MAX_RULE_MEMS = 5; // Max number of saved vars +const uint8_t MAX_RULE_SETS = 3; // Max number of rule sets of size 512 characters +const uint16_t MAX_RULE_SIZE = 512; // Max number of characters in rules + +const uint8_t MAX_FAN_SPEED = 4; // Max number of iFan02 fan speeds (0 .. 3) + +const char MQTT_TOKEN_PREFIX[] PROGMEM = "%prefix%"; // To be substituted by mqtt_prefix[x] +const char MQTT_TOKEN_TOPIC[] PROGMEM = "%topic%"; // To be substituted by mqtt_topic, mqtt_grptopic, mqtt_buttontopic, mqtt_switchtopic +const char WIFI_HOSTNAME[] = "%s-%04d"; // Expands to - + +const uint8_t CONFIG_FILE_SIGN = 0xA5; // Configuration file signature +const uint8_t CONFIG_FILE_XOR = 0x5A; // Configuration file xor (0 = No Xor) + +const uint32_t HLW_PREF_PULSE = 12530; // was 4975us = 201Hz = 1000W +const uint32_t HLW_UREF_PULSE = 1950; // was 1666us = 600Hz = 220V +const uint32_t HLW_IREF_PULSE = 3500; // was 1666us = 600Hz = 4.545A + +const uint8_t MQTT_RETRY_SECS = 10; // Minimum seconds to retry MQTT connection +const uint32_t GLOBAL_VALUES_VALID = 300; // Max number of seconds to keep last received values +const power_t APP_POWER = 0; // Default saved power state Off +const uint16_t WS2812_MAX_LEDS = 512; // Max number of LEDs + +const uint32_t PWM_RANGE = 1023; // 255..1023 needs to be devisible by 256 +//const uint16_t PWM_FREQ = 1000; // 100..1000 Hz led refresh +//const uint16_t PWM_FREQ = 910; // 100..1000 Hz led refresh (iTead value) +const uint16_t PWM_FREQ = 880; // 100..1000 Hz led refresh (BN-SZ01 value) +const uint16_t PWM_MAX = 4000; // [PWM_MAX] Maximum frequency - Default: 4000 +const uint16_t PWM_MIN = 100; // [PWM_MIN] Minimum frequency - Default: 100 + // For Dimmers use double of your mains AC frequecy (100 for 50Hz and 120 for 60Hz) + // For Controlling Servos use 50 and also set PWM_FREQ as 50 (DO NOT USE THESE VALUES FOR DIMMERS) +//#define PWM_LIGHTSCHEME0_IGNORE_SLEEP // Do not change sleep value for LightAnimate() scheme 0 + +const uint8_t DEFAULT_POWER_DELTA = 80; // Power change percentage +const uint16_t MAX_POWER_HOLD = 10; // Time in SECONDS to allow max agreed power +const uint16_t MAX_POWER_WINDOW = 30; // Time in SECONDS to disable allow max agreed power +const uint16_t SAFE_POWER_HOLD = 10; // Time in SECONDS to allow max unit safe power +const uint16_t SAFE_POWER_WINDOW = 30; // Time in MINUTES to disable allow max unit safe power +const uint8_t MAX_POWER_RETRY = 5; // Retry count allowing agreed power limit overflow + +const uint8_t STATES = 20; // Number of states per second using 50 mSec interval +const uint8_t IMMINENT_RESET_FACTOR = 10; // Factor to extent button hold time for imminent Reset to default 40 seconds using KEY_HOLD_TIME of 40 +const uint32_t BOOT_LOOP_TIME = 10; // Number of seconds to stop detecting boot loops +const uint16_t SYSLOG_TIMER = 600; // Seconds to restore syslog_level +const uint16_t SERIALLOG_TIMER = 600; // Seconds to disable SerialLog +const uint8_t OTA_ATTEMPTS = 5; // Number of times to try fetching the new firmware + +const uint16_t INPUT_BUFFER_SIZE = 520; // Max number of characters in (serial and http) command buffer +const uint16_t CMDSZ = 24; // Max number of characters in command +const uint16_t TOPSZ = 100; // Max number of characters in topic string +const uint16_t LOGSZ = 520; // Max number of characters in log +const uint16_t MIN_MESSZ = 893; // Min number of characters in MQTT message + +const uint8_t SENSOR_MAX_MISS = 5; // Max number of missed sensor reads before deciding it's offline + +#ifdef USE_MQTT_TLS + const uint16_t WEB_LOG_SIZE = 2000; // Max number of characters in weblog +#else + const uint16_t WEB_LOG_SIZE = 4000; // Max number of characters in weblog +#endif + +const uint8_t MAX_BACKLOG = 30; // Max number of commands in backlog +const uint32_t MIN_BACKLOG_DELAY = 2; // Minimal backlog delay in 0.1 seconds + +const uint32_t SOFT_BAUDRATE = 9600; // Default software serial baudrate +const uint32_t APP_BAUDRATE = 115200; // Default serial baudrate +const uint32_t SERIAL_POLLING = 100; // Serial receive polling in ms +const uint8_t MAX_STATUS = 11; // Max number of status lines + +const uint32_t DRIVER_BOOT_DELAY = 1; // Number of milliseconds to retard driver cycles during boot-up time to reduce overall CPU load whilst Wifi is connecting +const uint32_t LOOP_SLEEP_DELAY = 50; // Lowest number of milliseconds to go through the main loop using delay when needed /*********************************************************************************************\ * Defines \*********************************************************************************************/ -// Changes to the following MAX_ defines will impact settings layout -#define MAX_SWITCHES 8 // Max number of switches -#define MAX_RELAYS 8 // Max number of relays -#define MAX_INTERLOCKS 4 // Max number of interlock groups (MAX_RELAYS / 2) -#define MAX_LEDS 4 // Max number of leds -#define MAX_KEYS 4 // Max number of keys or buttons -#define MAX_PWMS 5 // Max number of PWM channels -#define MAX_COUNTERS 4 // Max number of counter sensors -#define MAX_TIMERS 16 // Max number of Timers -#define MAX_PULSETIMERS 8 // Max number of supported pulse timers -#define MAX_FRIENDLYNAMES 4 // Max number of Friendly names -#define MAX_DOMOTICZ_IDX 4 // Max number of Domoticz device, key and switch indices -#define MAX_DOMOTICZ_SNS_IDX 12 // Max number of Domoticz sensors indices -#define MAX_KNX_GA 10 // Max number of KNX Group Addresses to read that can be set -#define MAX_KNX_CB 10 // Max number of KNX Group Addresses to write that can be set -#define MAX_XNRG_DRIVERS 32 // Max number of allowed energy drivers -#define MAX_XDSP_DRIVERS 32 // Max number of allowed display drivers -#define MAX_XDRV_DRIVERS 96 // Max number of allowed driver drivers -#define MAX_XSNS_DRIVERS 96 // Max number of allowed sensor drivers -#define MAX_RULE_MEMS 5 // Max number of saved vars -#define MAX_RULE_SETS 3 // Max number of rule sets of size 512 characters -#define MAX_RULE_SIZE 512 // Max number of characters in rules - -// Changes to the following defines have no impact on settings layout #define MAX_RULE_TIMERS 8 // Max number of rule timers (4 bytes / timer) #define MAX_RULE_VARS 5 // Max number of rule variables (10 bytes / variable) -#define MAX_FAN_SPEED 4 // Max number of iFan02 fan speeds (0 .. 3) - -#define MQTT_TOKEN_PREFIX "%prefix%" // To be substituted by mqtt_prefix[x] -#define MQTT_TOKEN_TOPIC "%topic%" // To be substituted by mqtt_topic, mqtt_grptopic, mqtt_buttontopic, mqtt_switchtopic -#define MQTT_TOKEN_HOSTNAME "%hostname%" // To be substituted by mqtt_topic, mqtt_grptopic, mqtt_buttontopic, mqtt_switchtopic -#define MQTT_TOKEN_ID "%id%" // To be substituted by mqtt_topic, mqtt_grptopic, mqtt_buttontopic, mqtt_switchtopic - -#define WIFI_HOSTNAME "%s-%04d" // Expands to - - -#define CONFIG_FILE_SIGN 0xA5 // Configuration file signature -#define CONFIG_FILE_XOR 0x5A // Configuration file xor (0 = No Xor) - -#define HLW_PREF_PULSE 12530 // was 4975us = 201Hz = 1000W -#define HLW_UREF_PULSE 1950 // was 1666us = 600Hz = 220V -#define HLW_IREF_PULSE 3500 // was 1666us = 600Hz = 4.545A - -#define MQTT_RETRY_SECS 10 // Minimum seconds to retry MQTT connection -#define GLOBAL_VALUES_VALID 300 // Max number of seconds to keep last received values -#define APP_POWER 0 // Default saved power state Off -#define WS2812_MAX_LEDS 512 // Max number of LEDs - -#define PWM_RANGE 1023 // 255..1023 needs to be devisible by 256 -//#define PWM_FREQ 1000 // 100..1000 Hz led refresh -//#define PWM_FREQ 910 // 100..1000 Hz led refresh (iTead value) -#define PWM_FREQ 880 // 100..1000 Hz led refresh (BN-SZ01 value) -#define PWM_MAX 4000 // [PWM_MAX] Maximum frequency - Default: 4000 -#define PWM_MIN 100 // [PWM_MIN] Minimum frequency - Default: 100 - // For Dimmers use double of your mains AC frequecy (100 for 50Hz and 120 for 60Hz) - // For Controlling Servos use 50 and also set PWM_FREQ as 50 (DO NOT USE THESE VALUES FOR DIMMERS) -//#define PWM_LIGHTSCHEME0_IGNORE_SLEEP // Do not change sleep value for LightAnimate() scheme 0 - -#define DEFAULT_POWER_DELTA 80 // Power change percentage -#define MAX_POWER_HOLD 10 // Time in SECONDS to allow max agreed power -#define MAX_POWER_WINDOW 30 // Time in SECONDS to disable allow max agreed power -#define SAFE_POWER_HOLD 10 // Time in SECONDS to allow max unit safe power -#define SAFE_POWER_WINDOW 30 // Time in MINUTES to disable allow max unit safe power -#define MAX_POWER_RETRY 5 // Retry count allowing agreed power limit overflow - -#define STATES 20 // Number of states per second using 50 mSec interval -#define IMMINENT_RESET_FACTOR 10 // Factor to extent button hold time for imminent Reset to default 40 seconds using KEY_HOLD_TIME of 40 -#define BOOT_LOOP_TIME 10 // Number of seconds to stop detecting boot loops -#define SYSLOG_TIMER 600 // Seconds to restore syslog_level -#define SERIALLOG_TIMER 600 // Seconds to disable SerialLog -#define OTA_ATTEMPTS 5 // Number of times to try fetching the new firmware - -#define INPUT_BUFFER_SIZE 520 // Max number of characters in (serial and http) command buffer -#define CMDSZ 24 // Max number of characters in command -#define TOPSZ 100 // Max number of characters in topic string -#define LOGSZ 520 // Max number of characters in log -#define MIN_MESSZ 893 // Min number of characters in MQTT message - -#define SENSOR_MAX_MISS 5 // Max number of missed sensor reads before deciding it's offline - -#ifdef USE_MQTT_TLS - #define WEB_LOG_SIZE 2000 // Max number of characters in weblog -#else - #define WEB_LOG_SIZE 4000 // Max number of characters in weblog -#endif - -#define MAX_BACKLOG 30 // Max number of commands in backlog -#define MIN_BACKLOG_DELAY 2 // Minimal backlog delay in 0.1 seconds - -#define SOFT_BAUDRATE 9600 // Default software serial baudrate -#define APP_BAUDRATE 115200 // Default serial baudrate -#define SERIAL_POLLING 100 // Serial receive polling in ms -#define MAX_STATUS 11 // Max number of status lines - -#define DRIVER_BOOT_DELAY 1 // Number of milliseconds to retard driver cycles during boot-up time to reduce overall CPU load whilst Wifi is connecting -#define LOOP_SLEEP_DELAY 50 // Lowest number of milliseconds to go through the main loop using delay when needed - #define NO_EXTRA_4K_HEAP // Allocate 4k heap for WPS in ESP8166/Arduino core v2.4.2 (was always allocated in previous versions) /* diff --git a/sonoff/sonoff.ino b/sonoff/sonoff.ino index 74fbafee5..81e3efe28 100755 --- a/sonoff/sonoff.ino +++ b/sonoff/sonoff.ino @@ -76,7 +76,7 @@ enum TasmotaCommands { CMND_COUNTERDEBOUNCE, CMND_BUTTONDEBOUNCE, CMND_SWITCHDEBOUNCE, CMND_SLEEP, CMND_UPGRADE, CMND_UPLOAD, CMND_OTAURL, CMND_SERIALLOG, CMND_SYSLOG, CMND_LOGHOST, CMND_LOGPORT, CMND_IPADDRESS, CMND_NTPSERVER, CMND_AP, CMND_SSID, CMND_PASSWORD, CMND_HOSTNAME, CMND_WIFICONFIG, CMND_FRIENDLYNAME, CMND_SWITCHMODE, CMND_INTERLOCK, CMND_TEMPLATE, - CMND_TELEPERIOD, CMND_RESTART, CMND_RESET, CMND_TIMEZONE, CMND_TIMESTD, CMND_TIMEDST, CMND_ALTITUDE, CMND_LEDPOWER, CMND_LEDSTATE, + CMND_TELEPERIOD, CMND_RESTART, CMND_RESET, CMND_TIMEZONE, CMND_TIMESTD, CMND_TIMEDST, CMND_ALTITUDE, CMND_LEDPOWER, CMND_LEDSTATE, CMND_LEDMASK, CMND_I2CSCAN, CMND_SERIALSEND, CMND_BAUDRATE, CMND_SERIALDELIMITER, CMND_DRIVER }; const char kTasmotaCommands[] PROGMEM = D_CMND_BACKLOG "|" D_CMND_DELAY "|" D_CMND_POWER "|" D_CMND_FANSPEED "|" D_CMND_STATUS "|" D_CMND_STATE "|" D_CMND_POWERONSTATE "|" D_CMND_PULSETIME "|" @@ -86,7 +86,7 @@ const char kTasmotaCommands[] PROGMEM = D_CMND_COUNTERDEBOUNCE "|" D_CMND_BUTTONDEBOUNCE "|" D_CMND_SWITCHDEBOUNCE "|" D_CMND_SLEEP "|" D_CMND_UPGRADE "|" D_CMND_UPLOAD "|" D_CMND_OTAURL "|" D_CMND_SERIALLOG "|" D_CMND_SYSLOG "|" D_CMND_LOGHOST "|" D_CMND_LOGPORT "|" D_CMND_IPADDRESS "|" D_CMND_NTPSERVER "|" D_CMND_AP "|" D_CMND_SSID "|" D_CMND_PASSWORD "|" D_CMND_HOSTNAME "|" D_CMND_WIFICONFIG "|" D_CMND_FRIENDLYNAME "|" D_CMND_SWITCHMODE "|" D_CMND_INTERLOCK "|" D_CMND_TEMPLATE "|" - D_CMND_TELEPERIOD "|" D_CMND_RESTART "|" D_CMND_RESET "|" D_CMND_TIMEZONE "|" D_CMND_TIMESTD "|" D_CMND_TIMEDST "|" D_CMND_ALTITUDE "|" D_CMND_LEDPOWER "|" D_CMND_LEDSTATE "|" + D_CMND_TELEPERIOD "|" D_CMND_RESTART "|" D_CMND_RESET "|" D_CMND_TIMEZONE "|" D_CMND_TIMESTD "|" D_CMND_TIMEDST "|" D_CMND_ALTITUDE "|" D_CMND_LEDPOWER "|" D_CMND_LEDSTATE "|" D_CMND_LEDMASK "|" D_CMND_I2CSCAN "|" D_CMND_SERIALSEND "|" D_CMND_BAUDRATE "|" D_CMND_SERIALDELIMITER "|" D_CMND_DRIVER; const char kSleepMode[] PROGMEM = "Dynamic|Normal"; @@ -188,15 +188,15 @@ char* Format(char* output, const char* input, int size) char *token; uint8_t digits = 0; - if (strstr(input, "%")) { + if (strstr(input, "%") != nullptr) { strlcpy(output, input, size); token = strtok(output, "%"); if (strstr(input, "%") == input) { output[0] = '\0'; } else { - token = strtok(NULL, ""); + token = strtok(nullptr, ""); } - if (token != NULL) { + if (token != nullptr) { digits = atoi(token); if (digits) { char tmp[size]; @@ -221,10 +221,10 @@ char* Format(char* output, const char* input, int size) char* GetOtaUrl(char *otaurl, size_t otaurl_size) { - if (strstr(Settings.ota_url, "%04d") != NULL) { // OTA url contains placeholder for chip ID + if (strstr(Settings.ota_url, "%04d") != nullptr) { // OTA url contains placeholder for chip ID snprintf(otaurl, otaurl_size, Settings.ota_url, ESP.getChipId() & 0x1fff); } - else if (strstr(Settings.ota_url, "%d") != NULL) { // OTA url contains placeholder for chip ID + else if (strstr(Settings.ota_url, "%d") != nullptr) { // OTA url contains placeholder for chip ID snprintf_P(otaurl, otaurl_size, Settings.ota_url, ESP.getChipId()); } else { @@ -254,20 +254,21 @@ char* GetTopic_P(char *stopic, uint8_t prefix, char *topic, const char* subtopic fulltopic += F("_fb"); // cmnd/_fb } else { fulltopic = Settings.mqtt_fulltopic; - if ((0 == prefix) && (-1 == fulltopic.indexOf(F(MQTT_TOKEN_PREFIX)))) { - fulltopic += F("/" MQTT_TOKEN_PREFIX); // Need prefix for commands to handle mqtt topic loops + if ((0 == prefix) && (-1 == fulltopic.indexOf(FPSTR(MQTT_TOKEN_PREFIX)))) { + fulltopic += F("/"); + fulltopic += FPSTR(MQTT_TOKEN_PREFIX); // Need prefix for commands to handle mqtt topic loops } for (uint8_t i = 0; i < 3; i++) { if ('\0' == Settings.mqtt_prefix[i][0]) { snprintf_P(Settings.mqtt_prefix[i], sizeof(Settings.mqtt_prefix[i]), kPrefixes[i]); } } - fulltopic.replace(F(MQTT_TOKEN_PREFIX), Settings.mqtt_prefix[prefix]); - fulltopic.replace(F(MQTT_TOKEN_TOPIC), topic); - fulltopic.replace(F(MQTT_TOKEN_HOSTNAME), my_hostname); + fulltopic.replace(FPSTR(MQTT_TOKEN_PREFIX), Settings.mqtt_prefix[prefix]); + fulltopic.replace(FPSTR(MQTT_TOKEN_TOPIC), topic); + fulltopic.replace(F("%hostname%"), my_hostname); String token_id = WiFi.macAddress(); token_id.replace(":", ""); - fulltopic.replace(F(MQTT_TOKEN_ID), token_id); + fulltopic.replace(F("%id%"), token_id); } fulltopic.replace(F("#"), ""); fulltopic.replace(F("//"), "/"); @@ -278,7 +279,7 @@ char* GetTopic_P(char *stopic, uint8_t prefix, char *topic, const char* subtopic char* GetFallbackTopic_P(char *stopic, uint8_t prefix, const char* subtopic) { - return GetTopic_P(stopic, prefix +4, NULL, subtopic); + return GetTopic_P(stopic, prefix +4, nullptr, subtopic); } char* GetStateText(uint8_t state) @@ -452,7 +453,7 @@ void MqttDataHandler(char* topic, uint8_t* data, unsigned int data_len) char command [CMDSZ]; char stemp1[TOPSZ]; char *p; - char *type = NULL; + char *type = nullptr; uint8_t lines = 1; bool jsflg = false; bool grpflg = false; @@ -480,7 +481,7 @@ void MqttDataHandler(char* topic, uint8_t* data, unsigned int data_len) if (XdrvMqttData(topicBuf, sizeof(topicBuf), dataBuf, sizeof(dataBuf))) { return; } - grpflg = (strstr(topicBuf, Settings.mqtt_grptopic) != NULL); + grpflg = (strstr(topicBuf, Settings.mqtt_grptopic) != nullptr); GetFallbackTopic_P(stemp1, CMND, ""); // Full Fallback topic = cmnd/DVES_xxxxxxxx_fb/ fallback_topic_flag = (!strncmp(topicBuf, stemp1, strlen(stemp1))); @@ -488,7 +489,7 @@ void MqttDataHandler(char* topic, uint8_t* data, unsigned int data_len) type = strrchr(topicBuf, '/'); // Last part of received topic is always the command (type) index = 1; - if (type != NULL) { + if (type != nullptr) { type++; for (i = 0; i < strlen(type); i++) { type[i] = toupper(type[i]); @@ -505,14 +506,14 @@ void MqttDataHandler(char* topic, uint8_t* data, unsigned int data_len) AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_RESULT D_GROUP " %d, " D_INDEX " %d, " D_COMMAND " %s, " D_DATA " %s"), grpflg, index, type, dataBuf); - if (type != NULL) { - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_JSON_COMMAND "\":\"" D_JSON_ERROR "\"}")); + if (type != nullptr) { + Response_P(PSTR("{\"" D_JSON_COMMAND "\":\"" D_JSON_ERROR "\"}")); if (Settings.ledstate &0x02) { blinks++; } if (!strcmp(dataBuf,"?")) { data_len = 0; } int16_t payload = -99; // No payload uint16_t payload16 = 0; - long payload32 = strtol(dataBuf, &p, 10); + long payload32 = strtol(dataBuf, &p, 0); // decimal, octal (0) or hex (0x) if (p != dataBuf) { payload = (int16_t) payload32; // -32766 - 32767 payload16 = (uint16_t) payload32; // 0 - 65535 @@ -524,7 +525,7 @@ void MqttDataHandler(char* topic, uint8_t* data, unsigned int data_len) int temp_payload = GetStateNumber(dataBuf); if (temp_payload > -1) { payload = temp_payload; } -// AddLog_P2(LOG_LEVEL_DEBUG, PSTR("RSLT: Payload %d, Payload16 %d"), payload, payload16); +// AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_RESULT "Payload %d, Payload16 %d, payload32 %u"), payload, payload16, payload32); int command_code = GetCommandCode(command, sizeof(command), type, kTasmotaCommands); if (-1 == command_code) { @@ -538,7 +539,7 @@ void MqttDataHandler(char* topic, uint8_t* data, unsigned int data_len) XdrvMailbox.data = dataBuf; if (!XdrvCall(FUNC_COMMAND)) { if (!XsnsCall(FUNC_COMMAND)) { - type = NULL; // Unknown command + type = nullptr; // Unknown command } } } @@ -547,7 +548,7 @@ void MqttDataHandler(char* topic, uint8_t* data, unsigned int data_len) uint8_t bl_pointer = (!backlog_pointer) ? MAX_BACKLOG -1 : backlog_pointer; bl_pointer--; char *blcommand = strtok(dataBuf, ";"); - while ((blcommand != NULL) && (backlog_index != bl_pointer)) { + while ((blcommand != nullptr) && (backlog_index != bl_pointer)) { while(true) { blcommand = Trim(blcommand); if (!strncasecmp_P(blcommand, PSTR(D_CMND_BACKLOG), strlen(D_CMND_BACKLOG))) { @@ -561,14 +562,14 @@ void MqttDataHandler(char* topic, uint8_t* data, unsigned int data_len) backlog_index++; if (backlog_index >= MAX_BACKLOG) backlog_index = 0; } - blcommand = strtok(NULL, ";"); + blcommand = strtok(nullptr, ";"); } -// snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_SVALUE, command, D_JSON_APPENDED); +// Response_P(S_JSON_COMMAND_SVALUE, command, D_JSON_APPENDED); mqtt_data[0] = '\0'; } else { uint8_t blflag = (backlog_pointer == backlog_index); backlog_pointer = backlog_index; - snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_SVALUE, command, blflag ? D_JSON_EMPTY : D_JSON_ABORTED); + Response_P(S_JSON_COMMAND_SVALUE, command, blflag ? D_JSON_EMPTY : D_JSON_ABORTED); } } else if (CMND_DELAY == command_code) { @@ -578,7 +579,7 @@ void MqttDataHandler(char* topic, uint8_t* data, unsigned int data_len) uint16_t bl_delay = 0; long bl_delta = TimePassedSince(backlog_delay); if (bl_delta < 0) { bl_delay = (bl_delta *-1) / 100; } - snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_NVALUE, command, bl_delay); + Response_P(S_JSON_COMMAND_NVALUE, command, bl_delay); } else if ((CMND_POWER == command_code) && (index > 0) && (index <= devices_present)) { if ((payload < 0) || (payload > 4)) { payload = 9; } @@ -601,7 +602,7 @@ void MqttDataHandler(char* topic, uint8_t* data, unsigned int data_len) if ((payload >= 0) && (payload < MAX_FAN_SPEED) && (payload != GetFanspeed())) { SetFanspeed(payload); } - snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_NVALUE, command, GetFanspeed()); + Response_P(S_JSON_COMMAND_NVALUE, command, GetFanspeed()); } else if (CMND_STATUS == command_code) { if ((payload < 0) || (payload > MAX_STATUS)) payload = 99; @@ -627,7 +628,7 @@ void MqttDataHandler(char* topic, uint8_t* data, unsigned int data_len) sleep = payload; WiFiSetSleepMode(); } - snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_NVALUE_UNIT_NVALUE_UNIT, command, sleep, (Settings.flag.value_units) ? " " D_UNIT_MILLISECOND : "", Settings.sleep, (Settings.flag.value_units) ? " " D_UNIT_MILLISECOND : ""); + Response_P(S_JSON_COMMAND_NVALUE_UNIT_NVALUE_UNIT, command, sleep, (Settings.flag.value_units) ? " " D_UNIT_MILLISECOND : "", Settings.sleep, (Settings.flag.value_units) ? " " D_UNIT_MILLISECOND : ""); } else if ((CMND_UPGRADE == command_code) || (CMND_UPLOAD == command_code)) { // Check if the payload is numerically 1, and had no trailing chars. @@ -636,36 +637,36 @@ void MqttDataHandler(char* topic, uint8_t* data, unsigned int data_len) // We also need at least 3 chars to make a valid version number string. if (((1 == data_len) && (1 == payload)) || ((data_len >= 3) && NewerVersion(dataBuf))) { ota_state_flag = 3; - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"%s\":\"" D_JSON_VERSION " %s " D_JSON_FROM " %s\"}"), command, my_version, GetOtaUrl(stemp1, sizeof(stemp1))); + Response_P(PSTR("{\"%s\":\"" D_JSON_VERSION " %s " D_JSON_FROM " %s\"}"), command, my_version, GetOtaUrl(stemp1, sizeof(stemp1))); } else { - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"%s\":\"" D_JSON_ONE_OR_GT "\"}"), command, my_version); + Response_P(PSTR("{\"%s\":\"" D_JSON_ONE_OR_GT "\"}"), command, my_version); } } else if (CMND_OTAURL == command_code) { if ((data_len > 0) && (data_len < sizeof(Settings.ota_url))) { strlcpy(Settings.ota_url, (SC_DEFAULT == Shortcut(dataBuf)) ? OTA_URL : dataBuf, sizeof(Settings.ota_url)); } - snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_SVALUE, command, Settings.ota_url); + Response_P(S_JSON_COMMAND_SVALUE, command, Settings.ota_url); } else if (CMND_SERIALLOG == command_code) { if ((payload >= LOG_LEVEL_NONE) && (payload <= LOG_LEVEL_ALL)) { Settings.flag.mqtt_serial = 0; SetSeriallog(payload); } - snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_NVALUE_ACTIVE_NVALUE, command, Settings.seriallog_level, seriallog_level); + Response_P(S_JSON_COMMAND_NVALUE_ACTIVE_NVALUE, command, Settings.seriallog_level, seriallog_level); } else if (CMND_RESTART == command_code) { switch (payload) { case 1: restart_flag = 2; - snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_SVALUE, command, D_JSON_RESTARTING); + Response_P(S_JSON_COMMAND_SVALUE, command, D_JSON_RESTARTING); break; case 99: AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_APPLICATION D_RESTARTING)); EspRestart(); break; default: - snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_SVALUE, command, D_JSON_ONE_TO_RESTART); + Response_P(S_JSON_COMMAND_SVALUE, command, D_JSON_ONE_TO_RESTART); } } else if ((CMND_POWERONSTATE == command_code) && (my_module_type != MOTOR)) { @@ -684,28 +685,28 @@ void MqttDataHandler(char* topic, uint8_t* data, unsigned int data_len) } } } - snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_NVALUE, command, Settings.poweronstate); + Response_P(S_JSON_COMMAND_NVALUE, command, Settings.poweronstate); } else if ((CMND_PULSETIME == command_code) && (index > 0) && (index <= MAX_PULSETIMERS)) { if (data_len > 0) { Settings.pulse_timer[index -1] = payload16; // 0 - 65535 SetPulseTimer(index -1, payload16); } - snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_INDEX_NVALUE_ACTIVE_NVALUE, command, index, Settings.pulse_timer[index -1], GetPulseTimer(index -1)); + Response_P(S_JSON_COMMAND_INDEX_NVALUE_ACTIVE_NVALUE, command, index, Settings.pulse_timer[index -1], GetPulseTimer(index -1)); } else if (CMND_BLINKTIME == command_code) { if ((payload > 1) && (payload <= 3600)) { Settings.blinktime = payload; if (blink_timer > 0) { blink_timer = millis() + (100 * payload); } } - snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_NVALUE, command, Settings.blinktime); + Response_P(S_JSON_COMMAND_NVALUE, command, Settings.blinktime); } else if (CMND_BLINKCOUNT == command_code) { if (data_len > 0) { Settings.blinkcount = payload16; // 0 - 65535 if (blink_counter) blink_counter = Settings.blinkcount *2; } - snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_NVALUE, command, Settings.blinkcount); + Response_P(S_JSON_COMMAND_NVALUE, command, Settings.blinkcount); } else if (CMND_SAVEDATA == command_code) { if ((payload >= 0) && (payload <= 3600)) { @@ -716,7 +717,7 @@ void MqttDataHandler(char* topic, uint8_t* data, unsigned int data_len) if (Settings.save_data > 1) { snprintf_P(stemp1, sizeof(stemp1), PSTR(D_JSON_EVERY " %d " D_UNIT_SECOND), Settings.save_data); } - snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_SVALUE, command, (Settings.save_data > 1) ? stemp1 : GetStateText(Settings.save_data)); + Response_P(S_JSON_COMMAND_SVALUE, command, (Settings.save_data > 1) ? stemp1 : GetStateText(Settings.save_data)); } else if ((CMND_SENSOR == command_code) || (CMND_DRIVER == command_code)) { XdrvMailbox.index = index; @@ -814,92 +815,100 @@ void MqttDataHandler(char* topic, uint8_t* data, unsigned int data_len) } if (ptype < 99) { if (2 == ptype) snprintf_P(stemp1, sizeof(stemp1), PSTR("%d"), Settings.param[pindex]); - snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_INDEX_SVALUE, command, index, (2 == ptype) ? stemp1 : (1 == ptype) ? GetStateText(bitRead(Settings.flag3.data, pindex)) : GetStateText(bitRead(Settings.flag.data, pindex))); + Response_P(S_JSON_COMMAND_INDEX_SVALUE, command, index, (2 == ptype) ? stemp1 : (1 == ptype) ? GetStateText(bitRead(Settings.flag3.data, pindex)) : GetStateText(bitRead(Settings.flag.data, pindex))); } } else if (CMND_TEMPERATURE_RESOLUTION == command_code) { if ((payload >= 0) && (payload <= 3)) { Settings.flag2.temperature_resolution = payload; } - snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_NVALUE, command, Settings.flag2.temperature_resolution); + Response_P(S_JSON_COMMAND_NVALUE, command, Settings.flag2.temperature_resolution); } else if (CMND_HUMIDITY_RESOLUTION == command_code) { if ((payload >= 0) && (payload <= 3)) { Settings.flag2.humidity_resolution = payload; } - snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_NVALUE, command, Settings.flag2.humidity_resolution); + Response_P(S_JSON_COMMAND_NVALUE, command, Settings.flag2.humidity_resolution); } else if (CMND_PRESSURE_RESOLUTION == command_code) { if ((payload >= 0) && (payload <= 3)) { Settings.flag2.pressure_resolution = payload; } - snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_NVALUE, command, Settings.flag2.pressure_resolution); + Response_P(S_JSON_COMMAND_NVALUE, command, Settings.flag2.pressure_resolution); } else if (CMND_POWER_RESOLUTION == command_code) { if ((payload >= 0) && (payload <= 3)) { Settings.flag2.wattage_resolution = payload; } - snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_NVALUE, command, Settings.flag2.wattage_resolution); + Response_P(S_JSON_COMMAND_NVALUE, command, Settings.flag2.wattage_resolution); } else if (CMND_VOLTAGE_RESOLUTION == command_code) { if ((payload >= 0) && (payload <= 3)) { Settings.flag2.voltage_resolution = payload; } - snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_NVALUE, command, Settings.flag2.voltage_resolution); + Response_P(S_JSON_COMMAND_NVALUE, command, Settings.flag2.voltage_resolution); } else if (CMND_FREQUENCY_RESOLUTION == command_code) { if ((payload >= 0) && (payload <= 3)) { Settings.flag2.frequency_resolution = payload; } - snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_NVALUE, command, Settings.flag2.frequency_resolution); + Response_P(S_JSON_COMMAND_NVALUE, command, Settings.flag2.frequency_resolution); } else if (CMND_CURRENT_RESOLUTION == command_code) { if ((payload >= 0) && (payload <= 3)) { Settings.flag2.current_resolution = payload; } - snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_NVALUE, command, Settings.flag2.current_resolution); + Response_P(S_JSON_COMMAND_NVALUE, command, Settings.flag2.current_resolution); } else if (CMND_ENERGY_RESOLUTION == command_code) { if ((payload >= 0) && (payload <= 5)) { Settings.flag2.energy_resolution = payload; } - snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_NVALUE, command, Settings.flag2.energy_resolution); + Response_P(S_JSON_COMMAND_NVALUE, command, Settings.flag2.energy_resolution); } else if (CMND_WEIGHT_RESOLUTION == command_code) { if ((payload >= 0) && (payload <= 3)) { Settings.flag2.weight_resolution = payload; } - snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_NVALUE, command, Settings.flag2.weight_resolution); + Response_P(S_JSON_COMMAND_NVALUE, command, Settings.flag2.weight_resolution); } else if (CMND_MODULE == command_code) { if ((payload >= 0) && (payload <= MAXMODULE)) { - if (0 == payload) { payload = 256; } - payload--; - Settings.last_module = Settings.module; - Settings.module = payload; - SetModuleType(); - if (Settings.last_module != payload) { - for (uint8_t i = 0; i < sizeof(Settings.my_gp); i++) { - Settings.my_gp.io[i] = GPIO_NONE; - } + bool present = false; + if (0 == payload) { + payload = USER_MODULE; + present = true; + } else { + payload--; + present = ValidTemplateModule(payload); + } + if (present) { + Settings.last_module = Settings.module; + Settings.module = payload; + SetModuleType(); + if (Settings.last_module != payload) { + for (uint8_t i = 0; i < sizeof(Settings.my_gp); i++) { + Settings.my_gp.io[i] = GPIO_NONE; + } + } + restart_flag = 2; } - restart_flag = 2; } - snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_NVALUE_SVALUE, command, ModuleNr(), ModuleName().c_str()); + Response_P(S_JSON_COMMAND_NVALUE_SVALUE, command, ModuleNr(), ModuleName().c_str()); } else if (CMND_MODULES == command_code) { - for (uint8_t i = 0; i <= MAXMODULE; i++) { + uint8_t midx = USER_MODULE; + for (uint8_t i = 0; i <= sizeof(kModuleNiceList); i++) { + if (i > 0) { midx = pgm_read_byte(kModuleNiceList + i -1); } if (!jsflg) { - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_CMND_MODULES "%d\":["), lines); + Response_P(PSTR("{\"" D_CMND_MODULES "%d\":["), lines); } else { - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,"), mqtt_data); + ResponseAppend_P(PSTR(",")); } jsflg = true; - uint8_t j = i; - if (0 == i) { j = USER_MODULE; } else { j--; } - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s\"%d (%s)\""), mqtt_data, i, AnyModuleName(j).c_str()); - if ((strlen(mqtt_data) > (LOGSZ - TOPSZ)) || (i == MAXMODULE)) { - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s]}"), mqtt_data); + uint8_t j = i ? midx +1 : 0; + if ((ResponseAppend_P(PSTR("\"%d (%s)\""), j, AnyModuleName(midx).c_str()) > (LOGSZ - TOPSZ)) || (i == sizeof(kModuleNiceList))) { + ResponseAppend_P(PSTR("]}")); MqttPublishPrefixTopic_P(RESULT_OR_STAT, type); jsflg = false; lines++; @@ -926,19 +935,18 @@ void MqttDataHandler(char* topic, uint8_t* data, unsigned int data_len) restart_flag = 2; } } - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{")); + Response_P(PSTR("{")); for (uint8_t i = 0; i < sizeof(Settings.my_gp); i++) { if (ValidGPIO(i, cmodule.io[i])) { - if (jsflg) snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,"), mqtt_data); + if (jsflg) { ResponseAppend_P(PSTR(",")); } jsflg = true; - 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)); + ResponseAppend_P(PSTR("\"" D_CMND_GPIO "%d\":\"%d (%s)\""), i, Settings.my_gp.io[i], GetTextIndexed(stemp1, sizeof(stemp1), Settings.my_gp.io[i], kSensorNames)); } } if (jsflg) { - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s}"), mqtt_data); + ResponseAppend_P(PSTR("}")); } else { - snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_SVALUE, command, D_JSON_NOT_SUPPORTED); + Response_P(S_JSON_COMMAND_SVALUE, command, D_JSON_NOT_SUPPORTED); } } else if (CMND_GPIOS == command_code) { @@ -949,14 +957,13 @@ void MqttDataHandler(char* topic, uint8_t* data, unsigned int data_len) midx = pgm_read_byte(kGpioNiceList + i); if (!GetUsedInModule(midx, cmodule.io)) { if (!jsflg) { - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_CMND_GPIOS "%d\":["), lines); + Response_P(PSTR("{\"" D_CMND_GPIOS "%d\":["), lines); } else { - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,"), mqtt_data); + ResponseAppend_P(PSTR(",")); } jsflg = true; - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s\"%d (%s)\""), mqtt_data, midx, GetTextIndexed(stemp1, sizeof(stemp1), midx, kSensorNames)); - if ((strlen(mqtt_data) > (LOGSZ - TOPSZ)) || (i == sizeof(kGpioNiceList) -1)) { - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s]}"), mqtt_data); + if ((ResponseAppend_P(PSTR("\"%d (%s)\""), midx, GetTextIndexed(stemp1, sizeof(stemp1), midx, kSensorNames)) > (LOGSZ - TOPSZ)) || (i == sizeof(kGpioNiceList) -1)) { + ResponseAppend_P(PSTR("]}")); MqttPublishPrefixTopic_P(RESULT_OR_STAT, type); jsflg = false; lines++; @@ -969,10 +976,13 @@ void MqttDataHandler(char* topic, uint8_t* data, unsigned int data_len) // {"NAME":"Generic","GPIO":[17,254,29,254,7,254,254,254,138,254,139,254,254],"FLAG":1,"BASE":255} bool error = false; - if (!strstr(dataBuf, "{")) { // If no JSON it must be parameter + if (strstr(dataBuf, "{") == nullptr) { // If no JSON it must be parameter if ((payload > 0) && (payload <= MAXMODULE)) { - ModuleDefault(payload -1); // Copy template module - if (USER_MODULE == Settings.module) { restart_flag = 2; } + payload--; + if (ValidTemplateModule(payload)) { + ModuleDefault(payload); // Copy template module + if (USER_MODULE == Settings.module) { restart_flag = 2; } + } } else if (0 == payload) { // Copy current template to user template if (Settings.module != USER_MODULE) { @@ -995,11 +1005,11 @@ void MqttDataHandler(char* topic, uint8_t* data, unsigned int data_len) } } } - else if (data_len > 9) { // Workaround exception if empty JSON like {} - Needs checks + else { if (JsonTemplate(dataBuf)) { // Free 336 bytes StaticJsonBuffer stack space by moving code to function if (USER_MODULE == Settings.module) { restart_flag = 2; } } else { - snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_SVALUE, command, D_JSON_INVALID_JSON); + Response_P(S_JSON_COMMAND_SVALUE, command, D_JSON_INVALID_JSON); error = true; } } @@ -1010,16 +1020,16 @@ void MqttDataHandler(char* topic, uint8_t* data, unsigned int data_len) Settings.pwm_value[index -1] = payload; analogWrite(pin[GPIO_PWM1 + index -1], bitRead(pwm_inverted, index -1) ? Settings.pwm_range - payload : payload); } - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{")); + Response_P(PSTR("{")); MqttShowPWMState(); // Render the PWM status to MQTT - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s}"), mqtt_data); + ResponseAppend_P(PSTR("}")); } else if (CMND_PWMFREQUENCY == command_code) { if ((1 == payload) || ((payload >= PWM_MIN) && (payload <= PWM_MAX))) { Settings.pwm_frequency = (1 == payload) ? PWM_FREQ : payload; analogWriteFreq(Settings.pwm_frequency); // Default is 1000 (core_esp8266_wiring_pwm.c) } - snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_NVALUE, command, Settings.pwm_frequency); + Response_P(S_JSON_COMMAND_NVALUE, command, Settings.pwm_frequency); } else if (CMND_PWMRANGE == command_code) { if ((1 == payload) || ((payload > 254) && (payload < 1024))) { @@ -1031,7 +1041,7 @@ void MqttDataHandler(char* topic, uint8_t* data, unsigned int data_len) } analogWriteRange(Settings.pwm_range); // Default is 1023 (Arduino.h) } - snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_NVALUE, command, Settings.pwm_range); + Response_P(S_JSON_COMMAND_NVALUE, command, Settings.pwm_range); } else if ((CMND_COUNTER == command_code) && (index > 0) && (index <= MAX_COUNTERS)) { if ((data_len > 0) && (pin[GPIO_CNTR1 + index -1] < 99)) { @@ -1043,7 +1053,7 @@ void MqttDataHandler(char* topic, uint8_t* data, unsigned int data_len) Settings.pulse_counter[index -1] = payload32; } } - snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_INDEX_LVALUE, command, index, RtcSettings.pulse_counter[index -1]); + Response_P(S_JSON_COMMAND_INDEX_LVALUE, command, index, RtcSettings.pulse_counter[index -1]); } else if ((CMND_COUNTERTYPE == command_code) && (index > 0) && (index <= MAX_COUNTERS)) { if ((payload >= 0) && (payload <= 1) && (pin[GPIO_CNTR1 + index -1] < 99)) { @@ -1051,25 +1061,25 @@ void MqttDataHandler(char* topic, uint8_t* data, unsigned int data_len) RtcSettings.pulse_counter[index -1] = 0; Settings.pulse_counter[index -1] = 0; } - snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_INDEX_NVALUE, command, index, bitRead(Settings.pulse_counter_type, index -1)); + Response_P(S_JSON_COMMAND_INDEX_NVALUE, command, index, bitRead(Settings.pulse_counter_type, index -1)); } else if (CMND_COUNTERDEBOUNCE == command_code) { if ((data_len > 0) && (payload16 < 32001)) { Settings.pulse_counter_debounce = payload16; } - snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_NVALUE, command, Settings.pulse_counter_debounce); + Response_P(S_JSON_COMMAND_NVALUE, command, Settings.pulse_counter_debounce); } else if (CMND_BUTTONDEBOUNCE == command_code) { if ((payload > 39) && (payload < 1001)) { Settings.button_debounce = payload; } - snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_NVALUE, command, Settings.button_debounce); + Response_P(S_JSON_COMMAND_NVALUE, command, Settings.button_debounce); } else if (CMND_SWITCHDEBOUNCE == command_code) { if ((payload > 39) && (payload < 1001)) { Settings.switch_debounce = payload; } - snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_NVALUE, command, Settings.switch_debounce); + Response_P(S_JSON_COMMAND_NVALUE, command, Settings.switch_debounce); } else if (CMND_BAUDRATE == command_code) { if (payload32 > 1200) { @@ -1077,7 +1087,7 @@ void MqttDataHandler(char* topic, uint8_t* data, unsigned int data_len) baudrate = (1 == payload) ? APP_BAUDRATE : payload32 * 1200; SetSerialBaudrate(baudrate); } - snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_NVALUE, command, Settings.baudrate * 1200); + Response_P(S_JSON_COMMAND_NVALUE, command, Settings.baudrate * 1200); } else if ((CMND_SERIALSEND == command_code) && (index > 0) && (index <= 5)) { SetSeriallog(LOG_LEVEL_NONE); @@ -1099,7 +1109,7 @@ void MqttDataHandler(char* topic, uint8_t* data, unsigned int data_len) else if (5 == index) { SerialSendRaw(RemoveSpace(dataBuf)); // "AA004566" as hex values } - snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_SVALUE, command, D_JSON_DONE); + Response_P(S_JSON_COMMAND_SVALUE, command, D_JSON_DONE); } } else if (CMND_SERIALDELIMITER == command_code) { @@ -1112,7 +1122,7 @@ void MqttDataHandler(char* topic, uint8_t* data, unsigned int data_len) Settings.serial_delimiter = dataBuf[0]; } } - snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_NVALUE, command, Settings.serial_delimiter); + Response_P(S_JSON_COMMAND_NVALUE, command, Settings.serial_delimiter); } else if (CMND_SYSLOG == command_code) { if ((payload >= LOG_LEVEL_NONE) && (payload <= LOG_LEVEL_ALL)) { @@ -1120,19 +1130,19 @@ void MqttDataHandler(char* topic, uint8_t* data, unsigned int data_len) syslog_level = payload; syslog_timer = 0; } - snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_NVALUE_ACTIVE_NVALUE, command, Settings.syslog_level, syslog_level); + Response_P(S_JSON_COMMAND_NVALUE_ACTIVE_NVALUE, command, Settings.syslog_level, syslog_level); } else if (CMND_LOGHOST == command_code) { if ((data_len > 0) && (data_len < sizeof(Settings.syslog_host))) { strlcpy(Settings.syslog_host, (SC_DEFAULT == Shortcut(dataBuf)) ? SYS_LOG_HOST : dataBuf, sizeof(Settings.syslog_host)); } - snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_SVALUE, command, Settings.syslog_host); + Response_P(S_JSON_COMMAND_SVALUE, command, Settings.syslog_host); } else if (CMND_LOGPORT == command_code) { if (payload16 > 0) { Settings.syslog_port = (1 == payload16) ? SYS_LOG_PORT : payload16; } - snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_NVALUE, command, Settings.syslog_port); + Response_P(S_JSON_COMMAND_NVALUE, command, Settings.syslog_port); } else if ((CMND_IPADDRESS == command_code) && (index > 0) && (index <= 4)) { if (ParseIp(&address, dataBuf)) { @@ -1140,7 +1150,7 @@ void MqttDataHandler(char* topic, uint8_t* data, unsigned int data_len) // restart_flag = 2; } snprintf_P(stemp1, sizeof(stemp1), PSTR(" (%s)"), WiFi.localIP().toString().c_str()); - snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_INDEX_SVALUE_SVALUE, command, index, IPAddress(Settings.ip_address[index -1]).toString().c_str(), (1 == index) ? stemp1:""); + Response_P(S_JSON_COMMAND_INDEX_SVALUE_SVALUE, command, index, IPAddress(Settings.ip_address[index -1]).toString().c_str(), (1 == index) ? stemp1:""); } else if ((CMND_NTPSERVER == command_code) && (index > 0) && (index <= 3)) { if ((data_len > 0) && (data_len < sizeof(Settings.ntp_server[0]))) { @@ -1151,7 +1161,7 @@ void MqttDataHandler(char* topic, uint8_t* data, unsigned int data_len) // restart_flag = 2; // Issue #3890 ntp_force_sync = true; } - snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_INDEX_SVALUE, command, index, Settings.ntp_server[index -1]); + Response_P(S_JSON_COMMAND_INDEX_SVALUE, command, index, Settings.ntp_server[index -1]); } else if (CMND_AP == command_code) { if ((payload >= 0) && (payload <= 2)) { @@ -1165,7 +1175,7 @@ void MqttDataHandler(char* topic, uint8_t* data, unsigned int data_len) } restart_flag = 2; } - snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_NVALUE_SVALUE, command, Settings.sta_active +1, Settings.sta_ssid[Settings.sta_active]); + Response_P(S_JSON_COMMAND_NVALUE_SVALUE, command, Settings.sta_active +1, Settings.sta_ssid[Settings.sta_active]); } else if ((CMND_SSID == command_code) && (index > 0) && (index <= 2)) { if ((data_len > 0) && (data_len < sizeof(Settings.sta_ssid[0]))) { @@ -1173,41 +1183,41 @@ void MqttDataHandler(char* topic, uint8_t* data, unsigned int data_len) Settings.sta_active = index -1; restart_flag = 2; } - snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_INDEX_SVALUE, command, index, Settings.sta_ssid[index -1]); + Response_P(S_JSON_COMMAND_INDEX_SVALUE, command, index, Settings.sta_ssid[index -1]); } else if ((CMND_PASSWORD == command_code) && (index > 0) && (index <= 2)) { if ((data_len > 4 || SC_CLEAR == Shortcut(dataBuf) || SC_DEFAULT == Shortcut(dataBuf)) && (data_len < sizeof(Settings.sta_pwd[0]))) { strlcpy(Settings.sta_pwd[index -1], (SC_CLEAR == Shortcut(dataBuf)) ? "" : (SC_DEFAULT == Shortcut(dataBuf)) ? (1 == index) ? STA_PASS1 : STA_PASS2 : dataBuf, sizeof(Settings.sta_pwd[0])); Settings.sta_active = index -1; restart_flag = 2; - snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_INDEX_SVALUE, command, index, Settings.sta_pwd[index -1]); + Response_P(S_JSON_COMMAND_INDEX_SVALUE, command, index, Settings.sta_pwd[index -1]); } else { - snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_INDEX_ASTERIX, command, index); + Response_P(S_JSON_COMMAND_INDEX_ASTERIX, command, index); } } else if (CMND_HOSTNAME == command_code) { if (!grpflg && (data_len > 0) && (data_len < sizeof(Settings.hostname))) { strlcpy(Settings.hostname, (SC_DEFAULT == Shortcut(dataBuf)) ? WIFI_HOSTNAME : dataBuf, sizeof(Settings.hostname)); - if (strstr(Settings.hostname,"%")) { + if (strstr(Settings.hostname, "%") != nullptr) { strlcpy(Settings.hostname, WIFI_HOSTNAME, sizeof(Settings.hostname)); } restart_flag = 2; } - snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_SVALUE, command, Settings.hostname); + Response_P(S_JSON_COMMAND_SVALUE, command, Settings.hostname); } else if (CMND_WIFICONFIG == command_code) { if ((payload >= WIFI_RESTART) && (payload < MAX_WIFI_OPTION)) { Settings.sta_config = payload; wifi_state_flag = Settings.sta_config; snprintf_P(stemp1, sizeof(stemp1), kWifiConfig[Settings.sta_config]); - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_CMND_WIFICONFIG "\":\"%s " D_JSON_SELECTED "\"}"), stemp1); + Response_P(PSTR("{\"" D_CMND_WIFICONFIG "\":\"%s " D_JSON_SELECTED "\"}"), stemp1); if (WifiState() > WIFI_RESTART) { -// snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s after restart"), mqtt_data); +// ResponseAppend_P(PSTR(" after restart")); restart_flag = 2; } } else { snprintf_P(stemp1, sizeof(stemp1), kWifiConfig[Settings.sta_config]); - snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_NVALUE_SVALUE, command, Settings.sta_config, stemp1); + Response_P(S_JSON_COMMAND_NVALUE_SVALUE, command, Settings.sta_config, stemp1); } } else if ((CMND_FRIENDLYNAME == command_code) && (index > 0) && (index <= MAX_FRIENDLYNAMES)) { @@ -1219,13 +1229,13 @@ void MqttDataHandler(char* topic, uint8_t* data, unsigned int data_len) } strlcpy(Settings.friendlyname[index -1], (SC_DEFAULT == Shortcut(dataBuf)) ? stemp1 : dataBuf, sizeof(Settings.friendlyname[index -1])); } - snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_INDEX_SVALUE, command, index, Settings.friendlyname[index -1]); + Response_P(S_JSON_COMMAND_INDEX_SVALUE, command, index, Settings.friendlyname[index -1]); } else if ((CMND_SWITCHMODE == command_code) && (index > 0) && (index <= MAX_SWITCHES)) { if ((payload >= 0) && (payload < MAX_SWITCH_OPTION)) { Settings.switchmode[index -1] = payload; } - snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_INDEX_NVALUE, command, index, Settings.switchmode[index-1]); + Response_P(S_JSON_COMMAND_INDEX_NVALUE, command, index, Settings.switchmode[index-1]); } else if (CMND_INTERLOCK == command_code) { // Interlock 0 - Off, Interlock 1 - On, Interlock 1,2 3,4 5,6,7 uint8_t max_relays = devices_present; @@ -1233,15 +1243,15 @@ void MqttDataHandler(char* topic, uint8_t* data, unsigned int data_len) if (max_relays > sizeof(Settings.interlock[0]) * 8) { max_relays = sizeof(Settings.interlock[0]) * 8; } if (max_relays > 1) { // Only interlock with more than 1 relay if (data_len > 0) { - if (strstr(dataBuf, ",")) { // Interlock entry + if (strstr(dataBuf, ",") != nullptr) { // Interlock entry for (uint8_t i = 0; i < MAX_INTERLOCKS; i++) { Settings.interlock[i] = 0; } // Reset current interlocks char *group; char *q; uint8_t group_index = 0; power_t relay_mask = 0; - for (group = strtok_r(dataBuf, " ", &q); group && group_index < MAX_INTERLOCKS; group = strtok_r(NULL, " ", &q)) { + for (group = strtok_r(dataBuf, " ", &q); group && group_index < MAX_INTERLOCKS; group = strtok_r(nullptr, " ", &q)) { char *str; - for (str = strtok_r(group, ",", &p); str; str = strtok_r(NULL, ",", &p)) { + for (str = strtok_r(group, ",", &p); str; str = strtok_r(nullptr, ",", &p)) { int pbit = atoi(str); if ((pbit > 0) && (pbit <= max_relays)) { // Only valid relays pbit--; @@ -1267,18 +1277,18 @@ void MqttDataHandler(char* topic, uint8_t* data, unsigned int data_len) } } } - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_CMND_INTERLOCK "\":\"%s\",\"" D_JSON_GROUPS "\":\""), GetStateText(Settings.flag.interlock)); + Response_P(PSTR("{\"" D_CMND_INTERLOCK "\":\"%s\",\"" D_JSON_GROUPS "\":\""), GetStateText(Settings.flag.interlock)); uint8_t anygroup = 0; for (uint8_t i = 0; i < MAX_INTERLOCKS; i++) { if (Settings.interlock[i]) { anygroup++; - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s%s"), mqtt_data, (anygroup > 1) ? " " : ""); + ResponseAppend_P(PSTR("%s"), (anygroup > 1) ? " " : ""); uint8_t anybit = 0; power_t mask = 1; for (uint8_t j = 0; j < max_relays; j++) { if (Settings.interlock[i] & mask) { anybit++; - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s%s%d"), mqtt_data, (anybit > 1) ? "," : "", j +1); + ResponseAppend_P(PSTR("%s%d"), (anybit > 1) ? "," : "", j +1); } mask <<= 1; } @@ -1286,13 +1296,13 @@ void MqttDataHandler(char* topic, uint8_t* data, unsigned int data_len) } if (!anygroup) { for (uint8_t j = 1; j <= max_relays; j++) { - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s%s%d"), mqtt_data, (j > 1) ? "," : "", j); + ResponseAppend_P(PSTR("%s%d"), (j > 1) ? "," : "", j); } } - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s\"}"), mqtt_data); + ResponseAppend_P(PSTR("\"}")); } else { Settings.flag.interlock = 0; - snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_SVALUE, command, GetStateText(Settings.flag.interlock)); + Response_P(S_JSON_COMMAND_SVALUE, command, GetStateText(Settings.flag.interlock)); } } else if (CMND_TELEPERIOD == command_code) { @@ -1301,20 +1311,20 @@ void MqttDataHandler(char* topic, uint8_t* data, unsigned int data_len) if ((Settings.tele_period > 0) && (Settings.tele_period < 10)) Settings.tele_period = 10; // Do not allow periods < 10 seconds tele_period = Settings.tele_period; } - snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_NVALUE_UNIT, command, Settings.tele_period, (Settings.flag.value_units) ? " " D_UNIT_SECOND : ""); + Response_P(S_JSON_COMMAND_NVALUE_UNIT, command, Settings.tele_period, (Settings.flag.value_units) ? " " D_UNIT_SECOND : ""); } else if (CMND_RESET == command_code) { switch (payload) { case 1: restart_flag = 211; - snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_SVALUE, command , D_JSON_RESET_AND_RESTARTING); + Response_P(S_JSON_COMMAND_SVALUE, command , D_JSON_RESET_AND_RESTARTING); break; case 2 ... 6: restart_flag = 210 + payload; - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_CMND_RESET "\":\"" D_JSON_ERASE ", " D_JSON_RESET_AND_RESTARTING "\"}")); + Response_P(PSTR("{\"" D_CMND_RESET "\":\"" D_JSON_ERASE ", " D_JSON_RESET_AND_RESTARTING "\"}")); break; default: - snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_SVALUE, command, D_JSON_ONE_TO_RESET); + Response_P(S_JSON_COMMAND_SVALUE, command, D_JSON_ONE_TO_RESET); } } else if (CMND_TIMEZONE == command_code) { @@ -1324,9 +1334,9 @@ void MqttDataHandler(char* topic, uint8_t* data, unsigned int data_len) if (payload < 15) { p = strtok (dataBuf, ":"); if (p) { - p = strtok (NULL, ":"); + p = strtok (nullptr, ":"); if (p) { - Settings.timezone_minutes = strtol(p, NULL, 10); + Settings.timezone_minutes = strtol(p, nullptr, 10); if (Settings.timezone_minutes > 59) { Settings.timezone_minutes = 59; } } } @@ -1336,10 +1346,10 @@ void MqttDataHandler(char* topic, uint8_t* data, unsigned int data_len) ntp_force_sync = true; } if (99 == Settings.timezone) { - snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_NVALUE, command, Settings.timezone); + Response_P(S_JSON_COMMAND_NVALUE, command, Settings.timezone); } else { snprintf_P(stemp1, sizeof(stemp1), PSTR("%+03d:%02d"), Settings.timezone, Settings.timezone_minutes); - snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_SVALUE, command, stemp1); + Response_P(S_JSON_COMMAND_SVALUE, command, stemp1); } } else if ((CMND_TIMESTD == command_code) || (CMND_TIMEDST == command_code)) { @@ -1347,7 +1357,7 @@ void MqttDataHandler(char* topic, uint8_t* data, unsigned int data_len) uint8_t ts = 0; if (CMND_TIMEDST == command_code) { ts = 1; } if (data_len > 0) { - if (strstr(dataBuf, ",")) { // Process parameter entry + if (strstr(dataBuf, ",") != nullptr) { // Process parameter entry uint8_t tpos = 0; // Parameter index int value = 0; p = dataBuf; // Parameters like "1, 2,3 , 4 ,5, -120" or ",,,,,+240" @@ -1380,14 +1390,14 @@ void MqttDataHandler(char* topic, uint8_t* data, unsigned int data_len) ntp_force_sync = true; } } - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"%s\":{\"Hemisphere\":%d,\"Week\":%d,\"Month\":%d,\"Day\":%d,\"Hour\":%d,\"Offset\":%d}}"), + Response_P(PSTR("{\"%s\":{\"Hemisphere\":%d,\"Week\":%d,\"Month\":%d,\"Day\":%d,\"Hour\":%d,\"Offset\":%d}}"), command, Settings.tflag[ts].hemis, Settings.tflag[ts].week, Settings.tflag[ts].month, Settings.tflag[ts].dow, Settings.tflag[ts].hour, Settings.toffset[ts]); } else if (CMND_ALTITUDE == command_code) { if ((data_len > 0) && ((payload >= -30000) && (payload <= 30000))) { Settings.altitude = payload; } - snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_NVALUE, command, Settings.altitude); + Response_P(S_JSON_COMMAND_NVALUE, command, Settings.altitude); } else if (CMND_LEDPOWER == command_code) { if ((payload >= 0) && (payload <= 2)) { @@ -1404,7 +1414,7 @@ void MqttDataHandler(char* topic, uint8_t* data, unsigned int data_len) blinks = 0; SetLedPower(Settings.ledstate &8); } - snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_SVALUE, command, GetStateText(bitRead(Settings.ledstate, 3))); + Response_P(S_JSON_COMMAND_SVALUE, command, GetStateText(bitRead(Settings.ledstate, 3))); } else if (CMND_LEDSTATE == command_code) { if ((payload >= 0) && (payload < MAX_LED_OPTION)) { @@ -1414,22 +1424,29 @@ void MqttDataHandler(char* topic, uint8_t* data, unsigned int data_len) SetLedLink(0); } } - snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_NVALUE, command, Settings.ledstate); + Response_P(S_JSON_COMMAND_NVALUE, command, Settings.ledstate); + } + else if (CMND_LEDMASK == command_code) { + if (data_len > 0) { + Settings.ledmask = payload16; + } + snprintf_P(stemp1, sizeof(stemp1), PSTR("%d (0x%04X)"), Settings.ledmask, Settings.ledmask); + Response_P(S_JSON_COMMAND_SVALUE, command, stemp1); } #ifdef USE_I2C else if ((CMND_I2CSCAN == command_code) && i2c_flg) { I2cScan(mqtt_data, sizeof(mqtt_data)); } #endif // USE_I2C - else type = NULL; // Unknown command + else type = nullptr; // Unknown command } - if (type == NULL) { + if (type == nullptr) { blinks = 201; snprintf_P(topicBuf, sizeof(topicBuf), PSTR(D_JSON_COMMAND)); - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_JSON_COMMAND "\":\"" D_JSON_UNKNOWN "\"}")); + Response_P(PSTR("{\"" D_JSON_COMMAND "\":\"" D_JSON_UNKNOWN "\"}")); type = (char*)topicBuf; } - if (mqtt_data[0] != '\0') MqttPublishPrefixTopic_P(RESULT_OR_STAT, type); + if (mqtt_data[0] != '\0') { MqttPublishPrefixTopic_P(RESULT_OR_STAT, type); } fallback_topic_flag = false; } @@ -1473,7 +1490,7 @@ bool SendKey(uint8_t key, uint8_t device, uint8_t state) #endif // USE_DOMOTICZ result = !Settings.flag3.button_switch_force_local; } else { - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"%s%d\":{\"State\":%d}}"), (key) ? "Switch" : "Button", device, state); + Response_P(PSTR("{\"%s%d\":{\"State\":%d}}"), (key) ? "Switch" : "Button", device, state); result = XdrvRulesProcess(); } #ifdef USE_KNX @@ -1603,18 +1620,18 @@ void ExecuteCommand(char *cmnd, int source) ShowSource(source); token = strtok(cmnd, " "); - if (token != NULL) { + if (token != nullptr) { start = strrchr(token, '/'); // Skip possible cmnd/sonoff/ preamble if (start) { token = start +1; } } - uint16_t size = (token != NULL) ? strlen(token) : 0; + uint16_t size = (token != nullptr) ? strlen(token) : 0; char stopic[size +2]; // / + \0 - snprintf_P(stopic, sizeof(stopic), PSTR("/%s"), (token == NULL) ? "" : token); + snprintf_P(stopic, sizeof(stopic), PSTR("/%s"), (token == nullptr) ? "" : token); - token = strtok(NULL, ""); - size = (token != NULL) ? strlen(token) : 0; + token = strtok(nullptr, ""); + size = (token != nullptr) ? strlen(token) : 0; char svalue[size +1]; - strlcpy(svalue, (token == NULL) ? "" : token, sizeof(svalue)); // Fixed 5.8.0b + strlcpy(svalue, (token == nullptr) ? "" : token, sizeof(svalue)); // Fixed 5.8.0b MqttDataHandler(stopic, (uint8_t*)svalue, strlen(svalue)); } @@ -1641,19 +1658,19 @@ void PublishStatus(uint8_t payload) for (uint8_t i = 0; i < MAX_SWITCHES; i++) { snprintf_P(stemp2, sizeof(stemp2), PSTR("%s%s%d" ), stemp2, (i > 0 ? "," : ""), Settings.switchmode[i]); } - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_CMND_STATUS "\":{\"" D_CMND_MODULE "\":%d,\"" D_CMND_FRIENDLYNAME "\":[%s],\"" D_CMND_TOPIC "\":\"%s\",\"" D_CMND_BUTTONTOPIC "\":\"%s\",\"" D_CMND_POWER "\":%d,\"" D_CMND_POWERONSTATE "\":%d,\"" D_CMND_LEDSTATE "\":%d,\"" D_CMND_SAVEDATA "\":%d,\"" D_JSON_SAVESTATE "\":%d,\"" D_CMND_SWITCHTOPIC "\":\"%s\",\"" D_CMND_SWITCHMODE "\":[%s],\"" D_CMND_BUTTONRETAIN "\":%d,\"" D_CMND_SWITCHRETAIN "\":%d,\"" D_CMND_SENSORRETAIN "\":%d,\"" D_CMND_POWERRETAIN "\":%d}}"), - ModuleNr(), stemp, mqtt_topic, Settings.button_topic, power, Settings.poweronstate, Settings.ledstate, Settings.save_data, Settings.flag.save_state, Settings.switch_topic, stemp2, Settings.flag.mqtt_button_retain, Settings.flag.mqtt_switch_retain, Settings.flag.mqtt_sensor_retain, Settings.flag.mqtt_power_retain); + Response_P(PSTR("{\"" D_CMND_STATUS "\":{\"" D_CMND_MODULE "\":%d,\"" D_CMND_FRIENDLYNAME "\":[%s],\"" D_CMND_TOPIC "\":\"%s\",\"" D_CMND_BUTTONTOPIC "\":\"%s\",\"" D_CMND_POWER "\":%d,\"" D_CMND_POWERONSTATE "\":%d,\"" D_CMND_LEDSTATE "\":%d,\"" D_CMND_LEDMASK "\":\"%04X\",\"" D_CMND_SAVEDATA "\":%d,\"" D_JSON_SAVESTATE "\":%d,\"" D_CMND_SWITCHTOPIC "\":\"%s\",\"" D_CMND_SWITCHMODE "\":[%s],\"" D_CMND_BUTTONRETAIN "\":%d,\"" D_CMND_SWITCHRETAIN "\":%d,\"" D_CMND_SENSORRETAIN "\":%d,\"" D_CMND_POWERRETAIN "\":%d}}"), + ModuleNr(), stemp, mqtt_topic, Settings.button_topic, power, Settings.poweronstate, Settings.ledstate, Settings.ledmask, Settings.save_data, Settings.flag.save_state, Settings.switch_topic, stemp2, Settings.flag.mqtt_button_retain, Settings.flag.mqtt_switch_retain, Settings.flag.mqtt_sensor_retain, Settings.flag.mqtt_power_retain); MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS)); } if ((0 == payload) || (1 == payload)) { - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_CMND_STATUS D_STATUS1_PARAMETER "\":{\"" D_JSON_BAUDRATE "\":%d,\"" D_CMND_GROUPTOPIC "\":\"%s\",\"" D_CMND_OTAURL "\":\"%s\",\"" D_JSON_RESTARTREASON "\":\"%s\",\"" D_JSON_UPTIME "\":\"%s\",\"" D_JSON_STARTUPUTC "\":\"%s\",\"" D_CMND_SLEEP "\":%d,\"" D_JSON_CONFIG_HOLDER "\":%d,\"" D_JSON_BOOTCOUNT "\":%d,\"" D_JSON_SAVECOUNT "\":%d,\"" D_JSON_SAVEADDRESS "\":\"%X\"}}"), + Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS1_PARAMETER "\":{\"" D_JSON_BAUDRATE "\":%d,\"" D_CMND_GROUPTOPIC "\":\"%s\",\"" D_CMND_OTAURL "\":\"%s\",\"" D_JSON_RESTARTREASON "\":\"%s\",\"" D_JSON_UPTIME "\":\"%s\",\"" D_JSON_STARTUPUTC "\":\"%s\",\"" D_CMND_SLEEP "\":%d,\"" D_JSON_CONFIG_HOLDER "\":%d,\"" D_JSON_BOOTCOUNT "\":%d,\"" D_JSON_SAVECOUNT "\":%d,\"" D_JSON_SAVEADDRESS "\":\"%X\"}}"), baudrate, Settings.mqtt_grptopic, Settings.ota_url, GetResetReason().c_str(), GetUptime().c_str(), GetDateAndTime(DT_RESTART).c_str(), Settings.sleep, Settings.cfg_holder, Settings.bootcount, Settings.save_flag, GetSettingsAddress()); MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS "1")); } if ((0 == payload) || (2 == payload)) { - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_CMND_STATUS D_STATUS2_FIRMWARE "\":{\"" D_JSON_VERSION "\":\"%s%s\",\"" D_JSON_BUILDDATETIME "\":\"%s\",\"" D_JSON_BOOTVERSION "\":%d,\"" D_JSON_COREVERSION "\":\"" ARDUINO_ESP8266_RELEASE "\",\"" D_JSON_SDKVERSION "\":\"%s\"}}"), + Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS2_FIRMWARE "\":{\"" D_JSON_VERSION "\":\"%s%s\",\"" D_JSON_BUILDDATETIME "\":\"%s\",\"" D_JSON_BOOTVERSION "\":%d,\"" D_JSON_COREVERSION "\":\"" ARDUINO_ESP8266_RELEASE "\",\"" D_JSON_SDKVERSION "\":\"%s\"}}"), my_version, my_image, GetBuildDateAndTime().c_str(), ESP.getBootVersion(), ESP.getSdkVersion()); MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS "2")); } @@ -1663,26 +1680,26 @@ void PublishStatus(uint8_t payload) for (int8_t i = 0; i < PARAM8_SIZE; i++) { snprintf_P(stemp2, sizeof(stemp2), PSTR("%s%02X"), stemp2, Settings.param[i]); } - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_CMND_STATUS D_STATUS3_LOGGING "\":{\"" D_CMND_SERIALLOG "\":%d,\"" D_CMND_WEBLOG "\":%d,\"" D_CMND_SYSLOG "\":%d,\"" D_CMND_LOGHOST "\":\"%s\",\"" D_CMND_LOGPORT "\":%d,\"" D_CMND_SSID "\":[\"%s\",\"%s\"],\"" D_CMND_TELEPERIOD "\":%d,\"" D_JSON_RESOLUTION "\":\"%08X\",\"" D_CMND_SETOPTION "\":[\"%08X\",\"%s\",\"%08X\"]}}"), + Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS3_LOGGING "\":{\"" D_CMND_SERIALLOG "\":%d,\"" D_CMND_WEBLOG "\":%d,\"" D_CMND_SYSLOG "\":%d,\"" D_CMND_LOGHOST "\":\"%s\",\"" D_CMND_LOGPORT "\":%d,\"" D_CMND_SSID "\":[\"%s\",\"%s\"],\"" D_CMND_TELEPERIOD "\":%d,\"" D_JSON_RESOLUTION "\":\"%08X\",\"" D_CMND_SETOPTION "\":[\"%08X\",\"%s\",\"%08X\"]}}"), Settings.seriallog_level, Settings.weblog_level, Settings.syslog_level, Settings.syslog_host, Settings.syslog_port, Settings.sta_ssid[0], Settings.sta_ssid[1], Settings.tele_period, Settings.flag2.data, Settings.flag.data, stemp2, Settings.flag3.data); MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS "3")); } if ((0 == payload) || (4 == payload)) { - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_CMND_STATUS D_STATUS4_MEMORY "\":{\"" D_JSON_PROGRAMSIZE "\":%d,\"" D_JSON_FREEMEMORY "\":%d,\"" D_JSON_HEAPSIZE "\":%d,\"" D_JSON_PROGRAMFLASHSIZE "\":%d,\"" D_JSON_FLASHSIZE "\":%d,\"" D_JSON_FLASHCHIPID "\":\"%06X\",\"" D_JSON_FLASHMODE "\":%d,\"" D_JSON_FEATURES "\":[\"%08X\",\"%08X\",\"%08X\",\"%08X\",\"%08X\"]}}"), + Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS4_MEMORY "\":{\"" D_JSON_PROGRAMSIZE "\":%d,\"" D_JSON_FREEMEMORY "\":%d,\"" D_JSON_HEAPSIZE "\":%d,\"" D_JSON_PROGRAMFLASHSIZE "\":%d,\"" D_JSON_FLASHSIZE "\":%d,\"" D_JSON_FLASHCHIPID "\":\"%06X\",\"" D_JSON_FLASHMODE "\":%d,\"" D_JSON_FEATURES "\":[\"%08X\",\"%08X\",\"%08X\",\"%08X\",\"%08X\"]}}"), ESP.getSketchSize()/1024, ESP.getFreeSketchSpace()/1024, ESP.getFreeHeap()/1024, ESP.getFlashChipSize()/1024, ESP.getFlashChipRealSize()/1024, ESP.getFlashChipId(), ESP.getFlashChipMode(), LANGUAGE_LCID, feature_drv1, feature_drv2, feature_sns1, feature_sns2); MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS "4")); } if ((0 == payload) || (5 == payload)) { - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_CMND_STATUS D_STATUS5_NETWORK "\":{\"" D_CMND_HOSTNAME "\":\"%s\",\"" D_CMND_IPADDRESS "\":\"%s\",\"" D_JSON_GATEWAY "\":\"%s\",\"" D_JSON_SUBNETMASK "\":\"%s\",\"" D_JSON_DNSSERVER "\":\"%s\",\"" D_JSON_MAC "\":\"%s\",\"" D_CMND_WEBSERVER "\":%d,\"" D_CMND_WIFICONFIG "\":%d}}"), + Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS5_NETWORK "\":{\"" D_CMND_HOSTNAME "\":\"%s\",\"" D_CMND_IPADDRESS "\":\"%s\",\"" D_JSON_GATEWAY "\":\"%s\",\"" D_JSON_SUBNETMASK "\":\"%s\",\"" D_JSON_DNSSERVER "\":\"%s\",\"" D_JSON_MAC "\":\"%s\",\"" D_CMND_WEBSERVER "\":%d,\"" D_CMND_WIFICONFIG "\":%d}}"), my_hostname, WiFi.localIP().toString().c_str(), IPAddress(Settings.ip_address[1]).toString().c_str(), IPAddress(Settings.ip_address[2]).toString().c_str(), IPAddress(Settings.ip_address[3]).toString().c_str(), WiFi.macAddress().c_str(), Settings.webserver, Settings.sta_config); MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS "5")); } if (((0 == payload) || (6 == payload)) && Settings.flag.mqtt_enabled) { - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_CMND_STATUS D_STATUS6_MQTT "\":{\"" D_CMND_MQTTHOST "\":\"%s\",\"" D_CMND_MQTTPORT "\":%d,\"" D_CMND_MQTTCLIENT D_JSON_MASK "\":\"%s\",\"" D_CMND_MQTTCLIENT "\":\"%s\",\"" D_CMND_MQTTUSER "\":\"%s\",\"" D_JSON_MQTT_COUNT "\":%d,\"MAX_PACKET_SIZE\":%d,\"KEEPALIVE\":%d}}"), + Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS6_MQTT "\":{\"" D_CMND_MQTTHOST "\":\"%s\",\"" D_CMND_MQTTPORT "\":%d,\"" D_CMND_MQTTCLIENT D_JSON_MASK "\":\"%s\",\"" D_CMND_MQTTCLIENT "\":\"%s\",\"" D_CMND_MQTTUSER "\":\"%s\",\"" D_JSON_MQTT_COUNT "\":%d,\"MAX_PACKET_SIZE\":%d,\"KEEPALIVE\":%d}}"), Settings.mqtt_host, Settings.mqtt_port, Settings.mqtt_client, mqtt_client, Settings.mqtt_user, MqttConnectCount(), MQTT_MAX_PACKET_SIZE, MQTT_KEEPALIVE); MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS "6")); } @@ -1694,10 +1711,10 @@ void PublishStatus(uint8_t payload) snprintf_P(stemp, sizeof(stemp), PSTR("\"%s\"" ), GetTimeZone().c_str()); } #if defined(USE_TIMERS) && defined(USE_SUNRISE) - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_CMND_STATUS D_STATUS7_TIME "\":{\"" D_JSON_UTC_TIME "\":\"%s\",\"" D_JSON_LOCAL_TIME "\":\"%s\",\"" D_JSON_STARTDST "\":\"%s\",\"" D_JSON_ENDDST "\":\"%s\",\"" D_CMND_TIMEZONE "\":%s,\"" D_JSON_SUNRISE "\":\"%s\",\"" D_JSON_SUNSET "\":\"%s\"}}"), + Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS7_TIME "\":{\"" D_JSON_UTC_TIME "\":\"%s\",\"" D_JSON_LOCAL_TIME "\":\"%s\",\"" D_JSON_STARTDST "\":\"%s\",\"" D_JSON_ENDDST "\":\"%s\",\"" D_CMND_TIMEZONE "\":%s,\"" D_JSON_SUNRISE "\":\"%s\",\"" D_JSON_SUNSET "\":\"%s\"}}"), GetTime(0).c_str(), GetTime(1).c_str(), GetTime(2).c_str(), GetTime(3).c_str(), stemp, GetSun(0).c_str(), GetSun(1).c_str()); #else - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_CMND_STATUS D_STATUS7_TIME "\":{\"" D_JSON_UTC_TIME "\":\"%s\",\"" D_JSON_LOCAL_TIME "\":\"%s\",\"" D_JSON_STARTDST "\":\"%s\",\"" D_JSON_ENDDST "\":\"%s\",\"" D_CMND_TIMEZONE "\":%s}}"), + Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS7_TIME "\":{\"" D_JSON_UTC_TIME "\":\"%s\",\"" D_JSON_LOCAL_TIME "\":\"%s\",\"" D_JSON_STARTDST "\":\"%s\",\"" D_JSON_ENDDST "\":\"%s\",\"" D_CMND_TIMEZONE "\":%s}}"), GetTime(0).c_str(), GetTime(1).c_str(), GetTime(2).c_str(), GetTime(3).c_str(), stemp); #endif // USE_TIMERS and USE_SUNRISE MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS "7")); @@ -1705,16 +1722,16 @@ void PublishStatus(uint8_t payload) if (energy_flg) { if ((0 == payload) || (9 == payload)) { - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_CMND_STATUS D_STATUS9_MARGIN "\":{\"" D_CMND_POWERDELTA "\":%d,\"" D_CMND_POWERLOW "\":%d,\"" D_CMND_POWERHIGH "\":%d,\"" D_CMND_VOLTAGELOW "\":%d,\"" D_CMND_VOLTAGEHIGH "\":%d,\"" D_CMND_CURRENTLOW "\":%d,\"" D_CMND_CURRENTHIGH "\":%d}}"), + Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS9_MARGIN "\":{\"" D_CMND_POWERDELTA "\":%d,\"" D_CMND_POWERLOW "\":%d,\"" D_CMND_POWERHIGH "\":%d,\"" D_CMND_VOLTAGELOW "\":%d,\"" D_CMND_VOLTAGEHIGH "\":%d,\"" D_CMND_CURRENTLOW "\":%d,\"" D_CMND_CURRENTHIGH "\":%d}}"), Settings.energy_power_delta, Settings.energy_min_power, Settings.energy_max_power, Settings.energy_min_voltage, Settings.energy_max_voltage, Settings.energy_min_current, Settings.energy_max_current); MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS "9")); } } if ((0 == payload) || (8 == payload) || (10 == payload)) { - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_CMND_STATUS D_STATUS10_SENSOR "\":")); + Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS10_SENSOR "\":")); MqttShowSensor(); - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s}"), mqtt_data); + ResponseAppend_P(PSTR("}")); if (8 == payload) { MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS "8")); } else { @@ -1723,9 +1740,9 @@ void PublishStatus(uint8_t payload) } if ((0 == payload) || (11 == payload)) { - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_CMND_STATUS D_STATUS11_STATUS "\":")); + Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS11_STATUS "\":")); MqttShowState(); - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s}"), mqtt_data); + ResponseAppend_P(PSTR("}")); MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS "11")); } @@ -1733,50 +1750,50 @@ void PublishStatus(uint8_t payload) void MqttShowPWMState(void) { - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s\"" D_CMND_PWM "\":{"), mqtt_data); + ResponseAppend_P(PSTR("\"" D_CMND_PWM "\":{")); bool first = true; for (uint8_t i = 0; i < MAX_PWMS; i++) { if (pin[GPIO_PWM1 + i] < 99) { - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s%s\"" D_CMND_PWM "%d\":%d"), mqtt_data, first ? "" : ",", i+1, Settings.pwm_value[i]); + ResponseAppend_P(PSTR("%s\"" D_CMND_PWM "%d\":%d"), first ? "" : ",", i+1, Settings.pwm_value[i]); first = false; } } - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s}"), mqtt_data); + ResponseAppend_P(PSTR("}")); } void MqttShowState(void) { char stemp1[33]; - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s{\"" D_JSON_TIME "\":\"%s\",\"" D_JSON_UPTIME "\":\"%s\""), mqtt_data, GetDateAndTime(DT_LOCAL).c_str(), GetUptime().c_str()); + ResponseAppend_P(PSTR("{\"" D_JSON_TIME "\":\"%s\",\"" D_JSON_UPTIME "\":\"%s\""), GetDateAndTime(DT_LOCAL).c_str(), GetUptime().c_str()); #ifdef USE_ADC_VCC dtostrfd((double)ESP.getVcc()/1000, 3, stemp1); - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,\"" D_JSON_VCC "\":%s"), mqtt_data, stemp1); + ResponseAppend_P(PSTR(",\"" D_JSON_VCC "\":%s"), stemp1); #endif - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,\"SleepMode\":\"%s\",\"Sleep\":%u,\"LoadAvg\":%u"), - mqtt_data, GetTextIndexed(stemp1, sizeof(stemp1), Settings.flag3.sleep_normal, kSleepMode), sleep, loop_load_avg); + ResponseAppend_P(PSTR(",\"SleepMode\":\"%s\",\"Sleep\":%u,\"LoadAvg\":%u"), + GetTextIndexed(stemp1, sizeof(stemp1), Settings.flag3.sleep_normal, kSleepMode), sleep, loop_load_avg); for (uint8_t i = 0; i < devices_present; i++) { if (i == light_device -1) { LightState(1); } else { - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,\"%s\":\"%s\""), mqtt_data, GetPowerDevice(stemp1, i +1, sizeof(stemp1), Settings.flag.device_index_enable), GetStateText(bitRead(power, i))); + ResponseAppend_P(PSTR(",\"%s\":\"%s\""), GetPowerDevice(stemp1, i +1, sizeof(stemp1), Settings.flag.device_index_enable), GetStateText(bitRead(power, i))); if (SONOFF_IFAN02 == my_module_type) { - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,\"" D_CMND_FANSPEED "\":%d"), mqtt_data, GetFanspeed()); + ResponseAppend_P(PSTR(",\"" D_CMND_FANSPEED "\":%d"), GetFanspeed()); break; } } } if (pwm_present) { - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,"), mqtt_data); + ResponseAppend_P(PSTR(",")); MqttShowPWMState(); } - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,\"" D_JSON_WIFI "\":{\"" D_JSON_AP "\":%d,\"" D_JSON_SSID "\":\"%s\",\"" D_JSON_BSSID "\":\"%s\",\"" D_JSON_CHANNEL "\":%d,\"" D_JSON_RSSI "\":%d,\"" D_JSON_LINK_COUNT "\":%d,\"" D_JSON_DOWNTIME "\":\"%s\"}}"), - mqtt_data, Settings.sta_active +1, Settings.sta_ssid[Settings.sta_active], WiFi.BSSIDstr().c_str(), WiFi.channel(), WifiGetRssiAsQuality(WiFi.RSSI()), WifiLinkCount(), WifiDowntime().c_str()); + ResponseAppend_P(PSTR(",\"" D_JSON_WIFI "\":{\"" D_JSON_AP "\":%d,\"" D_JSON_SSID "\":\"%s\",\"" D_JSON_BSSID "\":\"%s\",\"" D_JSON_CHANNEL "\":%d,\"" D_JSON_RSSI "\":%d,\"" D_JSON_LINK_COUNT "\":%d,\"" D_JSON_DOWNTIME "\":\"%s\"}}"), + Settings.sta_active +1, Settings.sta_ssid[Settings.sta_active], WiFi.BSSIDstr().c_str(), WiFi.channel(), WifiGetRssiAsQuality(WiFi.RSSI()), WifiLinkCount(), WifiDowntime().c_str()); } void MqttPublishTeleState(void) @@ -1788,7 +1805,7 @@ void MqttPublishTeleState(void) bool MqttShowSensor(void) { - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s{\"" D_JSON_TIME "\":\"%s\""), mqtt_data, GetDateAndTime(DT_LOCAL).c_str()); + ResponseAppend_P(PSTR("{\"" D_JSON_TIME "\":\"%s\""), GetDateAndTime(DT_LOCAL).c_str()); int json_data_start = strlen(mqtt_data); for (uint8_t i = 0; i < MAX_SWITCHES; i++) { #ifdef USE_TM1638 @@ -1797,20 +1814,20 @@ bool MqttShowSensor(void) if (pin[GPIO_SWT1 +i] < 99) { #endif // USE_TM1638 bool swm = ((FOLLOW_INV == Settings.switchmode[i]) || (PUSHBUTTON_INV == Settings.switchmode[i]) || (PUSHBUTTONHOLD_INV == Settings.switchmode[i])); - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,\"" D_JSON_SWITCH "%d\":\"%s\""), mqtt_data, i +1, GetStateText(swm ^ SwitchLastState(i))); + ResponseAppend_P(PSTR(",\"" D_JSON_SWITCH "%d\":\"%s\""), i +1, GetStateText(swm ^ SwitchLastState(i))); } } XsnsCall(FUNC_JSON_APPEND); bool json_data_available = (strlen(mqtt_data) - json_data_start); - if (strstr_P(mqtt_data, PSTR(D_JSON_PRESSURE))) { - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,\"" D_JSON_PRESSURE_UNIT "\":\"%s\""), mqtt_data, PressureUnit().c_str()); + if (strstr_P(mqtt_data, PSTR(D_JSON_PRESSURE)) != nullptr) { + ResponseAppend_P(PSTR(",\"" D_JSON_PRESSURE_UNIT "\":\"%s\""), PressureUnit().c_str()); } - if (strstr_P(mqtt_data, PSTR(D_JSON_TEMPERATURE))) { - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,\"" D_JSON_TEMPERATURE_UNIT "\":\"%c\""), mqtt_data, TempUnit()); + if (strstr_P(mqtt_data, PSTR(D_JSON_TEMPERATURE)) != nullptr) { + ResponseAppend_P(PSTR(",\"" D_JSON_TEMPERATURE_UNIT "\":\"%c\""), TempUnit()); } - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s}"), mqtt_data); + ResponseAppend_P(PSTR("}")); - if (json_data_available) XdrvCall(FUNC_SHOW_SENSOR); + if (json_data_available) { XdrvCall(FUNC_SHOW_SENSOR); } return json_data_available; } @@ -1880,7 +1897,7 @@ void PerformEverySecond(void) if ((2 == RtcTime.minute) && latest_uptime_flag) { latest_uptime_flag = false; - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_JSON_TIME "\":\"%s\",\"" D_JSON_UPTIME "\":\"%s\"}"), GetDateAndTime(DT_LOCAL).c_str(), GetUptime().c_str()); + Response_P(PSTR("{\"" D_JSON_TIME "\":\"%s\",\"" D_JSON_UPTIME "\":\"%s\"}"), GetDateAndTime(DT_LOCAL).c_str(), GetUptime().c_str()); MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_UPTIME)); } if ((3 == RtcTime.minute) && !latest_uptime_flag) latest_uptime_flag = true; @@ -1981,7 +1998,7 @@ void Every250mSeconds(void) } } else if (Settings.ledstate &1) { - bool tstate = power; + bool tstate = power & Settings.ledmask; if ((SONOFF_TOUCH == my_module_type) || (SONOFF_T11 == my_module_type) || (SONOFF_T12 == my_module_type) || (SONOFF_T13 == my_module_type)) { tstate = (!power) ? 1 : 0; // As requested invert signal for Touch devices to find them in the dark } @@ -2020,8 +2037,8 @@ void Every250mSeconds(void) #ifndef FIRMWARE_MINIMAL if (RtcSettings.ota_loader) { char *bch = strrchr(mqtt_data, '/'); // Only consider filename after last backslash prevent change of urls having "-" in it - char *pch = strrchr((bch != NULL) ? bch : mqtt_data, '-'); // Change from filename-DE.bin into filename-minimal.bin - char *ech = strrchr((bch != NULL) ? bch : mqtt_data, '.'); // Change from filename.bin into filename-minimal.bin + char *pch = strrchr((bch != nullptr) ? bch : mqtt_data, '-'); // Change from filename-DE.bin into filename-minimal.bin + char *ech = strrchr((bch != nullptr) ? bch : mqtt_data, '.'); // Change from filename.bin into filename-minimal.bin if (!pch) { pch = ech; } if (pch) { mqtt_data[pch - mqtt_data] = '\0'; @@ -2054,9 +2071,9 @@ void Every250mSeconds(void) ota_state_flag = 0; if (ota_result) { // SetFlashModeDout(); // Force DOUT for both ESP8266 and ESP8285 - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR(D_JSON_SUCCESSFUL ". " D_JSON_RESTARTING)); + Response_P(PSTR(D_JSON_SUCCESSFUL ". " D_JSON_RESTARTING)); } else { - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR(D_JSON_FAILED " %s"), ESPhttpUpdate.getLastErrorString().c_str()); + Response_P(PSTR(D_JSON_FAILED " %s"), ESPhttpUpdate.getLastErrorString().c_str()); } restart_flag = 2; // Restart anyway to keep memory clean webserver MqttPublishPrefixTopic_P(STAT, PSTR(D_CMND_UPGRADE)); @@ -2298,13 +2315,13 @@ void SerialInput(void) if (Settings.flag.mqtt_serial && serial_in_byte_counter && (millis() > (serial_polling_window + SERIAL_POLLING))) { serial_in_buffer[serial_in_byte_counter] = 0; // Serial data completed if (!Settings.flag.mqtt_serial_raw) { - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_JSON_SERIALRECEIVED "\":\"%s\"}"), serial_in_buffer); + Response_P(PSTR("{\"" D_JSON_SERIALRECEIVED "\":\"%s\"}"), serial_in_buffer); } else { - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_JSON_SERIALRECEIVED "\":\"")); + Response_P(PSTR("{\"" D_JSON_SERIALRECEIVED "\":\"")); for (int i = 0; i < serial_in_byte_counter; i++) { - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s%02x"), mqtt_data, serial_in_buffer[i]); + ResponseAppend_P(PSTR("%02x"), serial_in_buffer[i]); } - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s\"}"), mqtt_data); + ResponseAppend_P(PSTR("\"}")); } MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_SERIALRECEIVED)); // XdrvRulesProcess(); @@ -2318,9 +2335,11 @@ void GpioInit(void) { uint8_t mpin; - if ((Settings.module >= MAXMODULE) && (Settings.module < USER_MODULE)) { - Settings.module = MODULE; - Settings.last_module = MODULE; + if (!ValidModule(Settings.module)) { + uint8_t module = MODULE; + if (!ValidModule(MODULE)) { module = SONOFF_BASIC; } + Settings.module = module; + Settings.last_module = module; } SetModuleType(); @@ -2501,7 +2520,9 @@ void GpioInit(void) ButtonInit(); SwitchInit(); +#ifdef ROTARY_V1 RotaryInit(); +#endif #ifdef USE_WS2812 if (!light_type && (pin[GPIO_WS2812] < 99)) { // RGB led @@ -2606,7 +2627,7 @@ void setup(void) Format(mqtt_client, Settings.mqtt_client, sizeof(mqtt_client)); Format(mqtt_topic, Settings.mqtt_topic, sizeof(mqtt_topic)); - if (strstr(Settings.hostname, "%")) { + if (strstr(Settings.hostname, "%") != nullptr) { strlcpy(Settings.hostname, WIFI_HOSTNAME, sizeof(Settings.hostname)); snprintf_P(my_hostname, sizeof(my_hostname)-1, Settings.hostname, mqtt_topic, ESP.getChipId() & 0x1FFF); } else { @@ -2694,7 +2715,9 @@ void loop(void) ButtonLoop(); SwitchLoop(); +#ifdef ROTARY_V1 RotaryLoop(); +#endif if (TimeReached(state_50msecond)) { SetNextTimeInterval(state_50msecond, 50); diff --git a/sonoff/sonoff_post.h b/sonoff/sonoff_post.h index 095c7e2ac..10a466981 100644 --- a/sonoff/sonoff_post.h +++ b/sonoff/sonoff_post.h @@ -140,6 +140,7 @@ void KNX_CB_Action(message_t const &msg, void *arg); #define USE_RF_SENSOR // Add support for RF sensor receiver (434MHz or 868MHz) (+0k8 code) // #define USE_THEO_V2 // Add support for decoding Theo V2 sensors as documented on https://sidweb.nl using 434MHz RF sensor receiver (+1k4 code) #define USE_ALECTO_V2 // Add support for decoding Alecto V2 sensors like ACH2010, WS3000 and DKW2012 using 868MHz RF sensor receiver (+1k7 code) +#define USE_HRE // Add support for Badger HR-E Water Meter (+1k4 code) #endif // FIRMWARE_SENSORS /*********************************************************************************************\ @@ -197,6 +198,7 @@ void KNX_CB_Action(message_t const &msg, void *arg); #undef USE_TX20_WIND_SENSOR // Disable support for La Crosse TX20 anemometer #undef USE_RC_SWITCH // Disable support for RF transceiver using library RcSwitch #undef USE_RF_SENSOR // Disable support for RF sensor receiver (434MHz or 868MHz) (+0k8 code) +#undef USE_HRE // Disable support for Badger HR-E Water Meter (+1k4 code) #undef DEBUG_THEO // Disable debug code #undef USE_DEBUG_DRIVER // Disable debug code #endif // FIRMWARE_CLASSIC @@ -326,6 +328,7 @@ void KNX_CB_Action(message_t const &msg, void *arg); #undef USE_TX20_WIND_SENSOR // Disable support for La Crosse TX20 anemometer #undef USE_RC_SWITCH // Disable support for RF transceiver using library RcSwitch #undef USE_RF_SENSOR // Disable support for RF sensor receiver (434MHz or 868MHz) (+0k8 code) +#undef USE_HRE // Disable support for Badger HR-E Water Meter (+1k4 code) #undef DEBUG_THEO // Disable debug code #undef USE_DEBUG_DRIVER // Disable debug code #endif // FIRMWARE_BASIC @@ -391,6 +394,7 @@ void KNX_CB_Action(message_t const &msg, void *arg); #undef USE_TX20_WIND_SENSOR // Disable support for La Crosse TX20 anemometer #undef USE_RC_SWITCH // Disable support for RF transceiver using library RcSwitch #undef USE_RF_SENSOR // Disable support for RF sensor receiver (434MHz or 868MHz) (+0k8 code) +#undef USE_HRE // Disable support for Badger HR-E Water Meter (+1k4 code) #undef DEBUG_THEO // Disable debug code #undef USE_DEBUG_DRIVER // Disable debug code #endif // FIRMWARE_MINIMAL diff --git a/sonoff/sonoff_template.h b/sonoff/sonoff_template.h index cf5a1af78..297177c6a 100644 --- a/sonoff/sonoff_template.h +++ b/sonoff/sonoff_template.h @@ -178,6 +178,8 @@ enum UserSelectablePins { GPIO_ROT1B, // Rotary switch1 B Pin GPIO_ROT2A, // Rotary switch2 A Pin GPIO_ROT2B, // Rotary switch2 B Pin + GPIO_HRE_CLOCK, // Clock/Power line for HR-E Water Meter + GPIO_HRE_DATA, // Data line for HR-E Water Meter GPIO_SENSOR_END }; // Programmer selectable GPIO functionality @@ -241,6 +243,7 @@ const char kSensorNames[] PROGMEM = D_SENSOR_CSE7766_TX "|" D_SENSOR_CSE7766_RX "|" D_SENSOR_ARIRFRCV "|" D_SENSOR_TXD "|" D_SENSOR_RXD "|" D_SENSOR_ROTARY "1a|" D_SENSOR_ROTARY "1b|" D_SENSOR_ROTARY "2a|" D_SENSOR_ROTARY "2b|" + D_SENSOR_HRE_CLOCK "|" D_SENSOR_HRE_DATA "|" ; /********************************************************************************************/ @@ -335,22 +338,22 @@ typedef struct MYCFGIO { uint8_t io[MAX_GPIO_PIN - MIN_FLASH_PINS]; } mycfgio; -#define GPIO_FLAG_USED 1 // Currently only one flag used +#define GPIO_FLAG_USED 2 // Currently two flags used -#define GPIO_FLAG_ADC0 1 // Allow ADC0 when define USE_ADC_VCC is disabled -#define GPIO_FLAG_SPARE01 2 // Allow input pull-up control using SetOption62 - Superseded by user template editing -#define GPIO_FLAG_SPARE02 4 -#define GPIO_FLAG_SPARE03 8 -#define GPIO_FLAG_SPARE04 16 -#define GPIO_FLAG_SPARE05 32 -#define GPIO_FLAG_SPARE06 64 -#define GPIO_FLAG_SPARE07 128 +#define GPIO_FLAG_ADC0 1 // Allow ADC0 when define USE_ADC_VCC is disabled +#define GPIO_FLAG_ADC0_TEMP 2 // Allow ADC0 as Temperature sensor when define USE_ADC_VCC is disabled +#define GPIO_FLAG_SPARE02 4 +#define GPIO_FLAG_SPARE03 8 +#define GPIO_FLAG_SPARE04 16 +#define GPIO_FLAG_SPARE05 32 +#define GPIO_FLAG_SPARE06 64 +#define GPIO_FLAG_SPARE07 128 typedef union { uint8_t data; struct { - uint8_t adc0 : 1; // Allow ADC0 when define USE_ADC_VCC is disabled - uint8_t spare01 : 1; + uint8_t adc0 : 1; // Allow ADC0 when define USE_ADC_VCC is disabled + uint8_t adc0_temp : 1; // Allow ADC0 as Temperature sensor when define USE_ADC_VCC is disabled uint8_t spare02 : 1; uint8_t spare03 : 1; uint8_t spare04 : 1; @@ -463,7 +466,9 @@ const uint8_t kGpioNiceList[] PROGMEM = { GPIO_DHT11, // DHT11 GPIO_DHT22, // DHT21, DHT22, AM2301, AM2302, AM2321 GPIO_SI7021, // iTead SI7021 +#if defined(USE_DS18B20) || defined(USE_DS18x20) || defined(USE_DS18x20_LEGACY) GPIO_DSB, // Single wire DS18B20 or DS18S20 +#endif #ifdef USE_WS2812 GPIO_WS2812, // WS2812 Led string #endif @@ -580,14 +585,22 @@ const uint8_t kGpioNiceList[] PROGMEM = { GPIO_SM16716_DAT, // SM16716 DATA GPIO_SM16716_SEL, // SM16716 SELECT #endif // USE_SM16716 +#ifdef ROTARY_V1 GPIO_ROT1A, // Rotary switch1 A Pin GPIO_ROT1B, // Rotary switch1 B Pin GPIO_ROT2A, // Rotary switch2 A Pin GPIO_ROT2B, // Rotary switch2 B Pin - GPIO_ARIRFRCV // AliLux RF Receive input +#endif +#ifdef USE_ARILUX_RF + GPIO_ARIRFRCV, // AliLux RF Receive input +#endif +#ifdef USE_HRE + GPIO_HRE_CLOCK, + GPIO_HRE_DATA +#endif }; -const uint8_t kModuleNiceList[MAXMODULE] PROGMEM = { +const uint8_t kModuleNiceList[] PROGMEM = { SONOFF_BASIC, // Sonoff Relay Devices SONOFF_RF, SONOFF_TH, @@ -639,9 +652,15 @@ const uint8_t kModuleNiceList[MAXMODULE] PROGMEM = { OBI2, MANZOKU_EU_4, ESP_SWITCH, // Switch Devices +#ifdef USE_TUYA_DIMMER TUYA_DIMMER, // Dimmer Devices +#endif +#ifdef USE_ARMTRONIX_DIMMERS ARMTRONIX_DIMMERS, +#endif +#ifdef USE_PS_16_DZ PS_16_DZ, +#endif H801, // Light Devices MAGICHOME, ARILUX_LC01, @@ -649,7 +668,9 @@ const uint8_t kModuleNiceList[MAXMODULE] PROGMEM = { ARILUX_LC11, ZENGGE_ZF_WF017, HUAFAN_SS, +#ifdef ROTARY_V1 MI_DESK_LAMP, +#endif KMC_70011, AILIGHT, // Light Bulbs PHILIPS, diff --git a/sonoff/sonoff_version.h b/sonoff/sonoff_version.h index 7a5dac4cc..30b6cc65e 100644 --- a/sonoff/sonoff_version.h +++ b/sonoff/sonoff_version.h @@ -20,11 +20,6 @@ #ifndef _SONOFF_VERSION_H_ #define _SONOFF_VERSION_H_ -#define VERSION 0x06050001 - -#define D_PROGRAMNAME "Sonoff-Tasmota" -#define D_AUTHOR "Theo Arends" -//#define D_WEBLINK "https://github.com/arendst/Sonoff-Tasmota" -#define D_WEBLINK "https://bit.ly/tasmota" +const uint32_t VERSION = 0x06050007; #endif // _SONOFF_VERSION_H_ diff --git a/sonoff/support.ino b/sonoff/support.ino index e6068fb77..336a36380 100644 --- a/sonoff/support.ino +++ b/sonoff/support.ino @@ -28,7 +28,7 @@ uint32_t syslog_host_hash = 0; // Syslog host name hash Ticker tickerOSWatch; -#define OSWATCH_RESET_TIME 120 +const uint32_t OSWATCH_RESET_TIME = 120; static unsigned long oswatch_last_loop_time; uint8_t oswatch_blocked_loop = 0; @@ -108,7 +108,7 @@ void* memchr(const void* ptr, int value, size_t num) return 0; } -// http://clc-wiki.net/wiki/C_standard_library:string.h:strspn +// http://clc-wiki.net/wiki/C_standard_library:string.h:strcspn // Get span until any character in string size_t strcspn(const char *str1, const char *str2) { @@ -123,6 +123,77 @@ size_t strcspn(const char *str1, const char *str2) } return ret; } + +// https://opensource.apple.com/source/Libc/Libc-583/stdlib/FreeBSD/strtoull.c +// Convert a string to an unsigned long long integer +#ifndef __LONG_LONG_MAX__ +#define __LONG_LONG_MAX__ 9223372036854775807LL +#endif +#ifndef ULLONG_MAX +#define ULLONG_MAX (__LONG_LONG_MAX__ * 2ULL + 1) +#endif + +unsigned long long strtoull(const char *__restrict nptr, char **__restrict endptr, int base) +{ + const char *s = nptr; + char c; + do { c = *s++; } while (isspace((unsigned char)c)); // Trim leading spaces + + int neg = 0; + if (c == '-') { // Set minus flag and/or skip sign + neg = 1; + c = *s++; + } else { + if (c == '+') { + c = *s++; + } + } + + if ((base == 0 || base == 16) && (c == '0') && (*s == 'x' || *s == 'X')) { // Set Hexadecimal + c = s[1]; + s += 2; + base = 16; + } + if (base == 0) { base = (c == '0') ? 8 : 10; } // Set Octal or Decimal + + unsigned long long acc = 0; + int any = 0; + if (base > 1 && base < 37) { + unsigned long long cutoff = ULLONG_MAX / base; + int cutlim = ULLONG_MAX % base; + for ( ; ; c = *s++) { + if (c >= '0' && c <= '9') + c -= '0'; + else if (c >= 'A' && c <= 'Z') + c -= 'A' - 10; + else if (c >= 'a' && c <= 'z') + c -= 'a' - 10; + else + break; + + if (c >= base) + break; + + if (any < 0 || acc > cutoff || (acc == cutoff && c > cutlim)) + any = -1; + else { + any = 1; + acc *= base; + acc += c; + } + } + if (any < 0) { + acc = ULLONG_MAX; // Range error + } + else if (any && neg) { + acc = -acc; + } + } + + if (endptr != nullptr) { *endptr = (char *)(any ? s - 1 : nptr); } + + return acc; +} #endif // ARDUINO_ESP8266_RELEASE_2_3_0 // Get span until single character in string @@ -139,15 +210,15 @@ size_t strchrspn(const char *str1, int character) char* subStr(char* dest, char* str, const char *delim, int index) { char *act; - char *sub = NULL; + char *sub = nullptr; char *ptr; int i; // Since strtok consumes the first arg, make a copy strncpy(dest, str, strlen(str)+1); - for (i = 1, act = dest; i <= index; i++, act = NULL) { + for (i = 1, act = dest; i <= index; i++, act = nullptr) { sub = strtok_r(act, delim, &ptr); - if (sub == NULL) break; + if (sub == nullptr) break; } sub = Trim(sub); return sub; @@ -200,6 +271,27 @@ int TextToInt(char *str) return strtol(str, &p, radix); } +char* ulltoa(unsigned long long value, char *str, int radix) +{ + char digits[64]; + char *dst = str; + int i = 0; + int n = 0; + +// if (radix < 2 || radix > 36) { radix = 10; } + + do { + n = value % radix; + digits[i++] = (n < 10) ? (char)n+'0' : (char)n-10+'A'; + value /= radix; + } while (value != 0); + + while (i > 0) { *dst++ = digits[--i]; } + + *dst = 0; + return str; +} + char* dtostrfd(double number, unsigned char prec, char *s) { if ((isnan(number)) || (isinf(number))) { // Fix for JSON output (https://stackoverflow.com/questions/1423081/json-left-out-infinity-and-nan-json-status-in-ecmascript) @@ -283,6 +375,19 @@ char* RemoveSpace(char* p) return p; } +char* LowerCase(char* dest, const char* source) +{ + char* write = dest; + const char* read = source; + char ch = '.'; + + while (ch != '\0') { + ch = *read++; + *write++ = tolower(ch); + } + return dest; +} + char* UpperCase(char* dest, const char* source) { char* write = dest; @@ -357,6 +462,14 @@ uint8_t Shortcut(const char* str) return result; } +bool ValidIpAddress(const char* str) +{ + const char* p = str; + + while (*p && ((*p == '.') || ((*p >= '0') && (*p <= '9')))) { p++; } + return (*p == '\0'); +} + bool ParseIp(uint32_t* addr, const char* str) { uint8_t *part = (uint8_t*)addr; @@ -364,9 +477,9 @@ bool ParseIp(uint32_t* addr, const char* str) *addr = 0; for (i = 0; i < 4; i++) { - part[i] = strtoul(str, NULL, 10); // Convert byte + part[i] = strtoul(str, nullptr, 10); // Convert byte str = strchr(str, '.'); - if (str == NULL || *str == '\0') { + if (str == nullptr || *str == '\0') { break; // No more separators, exit } str++; // Point to next character after separator @@ -409,7 +522,7 @@ bool NewerVersion(char* version_str) return false; // Bail if we can't duplicate. Assume bad. } // Loop through the version string, splitting on '.' seperators. - for (char *str = strtok_r(version_dup, ".", &str_ptr); str && i < sizeof(VERSION); str = strtok_r(NULL, ".", &str_ptr), i++) { + for (char *str = strtok_r(version_dup, ".", &str_ptr); str && i < sizeof(VERSION); str = strtok_r(nullptr, ".", &str_ptr), i++) { int field = atoi(str); // The fields in a version string can only range from 0-255. if ((field < 0) || (field > 255)) { @@ -697,6 +810,67 @@ void ShowSource(int source) } } +void WebHexCode(uint8_t i, const char* code) +{ + char scolor[10]; + + strlcpy(scolor, code, sizeof(scolor)); + char* p = scolor; + if ('#' == p[0]) { p++; } // Skip + + if (3 == strlen(p)) { // Convert 3 character to 6 character color code + p[6] = p[3]; // \0 + p[5] = p[2]; // 3 + p[4] = p[2]; // 3 + p[3] = p[1]; // 2 + p[2] = p[1]; // 2 + p[1] = p[0]; // 1 + } + + uint32_t color = strtol(p, nullptr, 16); +/* + if (3 == strlen(p)) { // Convert 3 character to 6 character color code + uint32_t w = ((color & 0xF00) << 8) | ((color & 0x0F0) << 4) | (color & 0x00F); // 00010203 + color = w | (w << 4); // 00112233 + } +*/ + + Settings.web_color[i][0] = (color >> 16) & 0xFF; // Red + Settings.web_color[i][1] = (color >> 8) & 0xFF; // Green + Settings.web_color[i][2] = color & 0xFF; // Blue +} + +uint32_t WebColor(uint8_t i) +{ + uint32_t tcolor = (Settings.web_color[i][0] << 16) | (Settings.web_color[i][1] << 8) | Settings.web_color[i][2]; + return tcolor; +} + +/*********************************************************************************************\ + * Response data handling +\*********************************************************************************************/ + +int Response_P(const char* format, ...) // Content send snprintf_P char data +{ + // This uses char strings. Be aware of sending %% if % is needed + va_list args; + va_start(args, format); + int len = vsnprintf_P(mqtt_data, sizeof(mqtt_data), format, args); + va_end(args); + return len; +} + +int ResponseAppend_P(const char* format, ...) // Content send snprintf_P char data +{ + // This uses char strings. Be aware of sending %% if % is needed + va_list args; + va_start(args, format); + int mlen = strlen(mqtt_data); + int len = vsnprintf_P(mqtt_data + mlen, sizeof(mqtt_data) - mlen, format, args); + va_end(args); + return len + mlen; +} + /*********************************************************************************************\ * GPIO Module and Template management \*********************************************************************************************/ @@ -708,6 +882,22 @@ uint8_t ModuleNr() return (USER_MODULE == Settings.module) ? 0 : Settings.module +1; } +bool ValidTemplateModule(uint8_t index) +{ + for (uint8_t i = 0; i < sizeof(kModuleNiceList); i++) { + if (index == pgm_read_byte(kModuleNiceList + i)) { + return true; + } + } + return false; +} + +bool ValidModule(uint8_t index) +{ + if (index == USER_MODULE) { return true; } + return ValidTemplateModule(index); +} + String AnyModuleName(uint8_t index) { if (USER_MODULE == index) { @@ -855,6 +1045,10 @@ bool GetUsedInModule(uint8_t val, uint8_t *arr) bool JsonTemplate(const char* dataBuf) { + // {"NAME":"Generic","GPIO":[17,254,29,254,7,254,254,254,138,254,139,254,254],"FLAG":1,"BASE":255} + + if (strlen(dataBuf) < 9) { return false; } // Workaround exception if empty JSON like {} - Needs checks + StaticJsonBuffer<350> jb; // 331 from https://arduinojson.org/v5/assistant/ JsonObject& obj = jb.parseObject(dataBuf); if (!obj.success()) { return false; } @@ -875,20 +1069,19 @@ bool JsonTemplate(const char* dataBuf) } if (obj[D_JSON_BASE].success()) { uint8_t base = obj[D_JSON_BASE]; - if ((0 == base) || (base >= MAXMODULE)) { base = 17; } else { base--; } - Settings.user_template_base = base; // Default WEMOS + if ((0 == base) || !ValidTemplateModule(base -1)) { base = 18; } + Settings.user_template_base = base -1; // Default WEMOS } return true; } void TemplateJson() { - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_JSON_NAME "\":\"%s\",\"" D_JSON_GPIO "\":["), Settings.user_template.name); + Response_P(PSTR("{\"" D_JSON_NAME "\":\"%s\",\"" D_JSON_GPIO "\":["), Settings.user_template.name); for (uint8_t i = 0; i < sizeof(Settings.user_template.gp); i++) { - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s%s%d"), mqtt_data, (i>0)?",":"", Settings.user_template.gp.io[i]); + ResponseAppend_P(PSTR("%s%d"), (i>0)?",":"", Settings.user_template.gp.io[i]); } - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s],\"" D_JSON_FLAG "\":%d,\"" D_JSON_BASE "\":%d}"), - mqtt_data, Settings.user_template.flag, Settings.user_template_base +1); + ResponseAppend_P(PSTR("],\"" D_JSON_FLAG "\":%d,\"" D_JSON_BASE "\":%d}"), Settings.user_template.flag, Settings.user_template_base +1); } /*********************************************************************************************\ @@ -959,7 +1152,7 @@ void SetNextTimeInterval(unsigned long& timer, const unsigned long step) \*********************************************************************************************/ #ifdef USE_I2C -#define I2C_RETRY_COUNTER 3 +const uint8_t I2C_RETRY_COUNTER = 3; uint32_t i2c_buffer = 0; @@ -1182,7 +1375,7 @@ void SetSeriallog(uint8_t loglevel) #ifdef USE_WEBSERVER void GetLog(uint8_t idx, char** entry_pp, size_t* len_p) { - char* entry_p = NULL; + char* entry_p = nullptr; size_t len = 0; if (idx) { @@ -1210,8 +1403,9 @@ void Syslog(void) // Destroys log_data char syslog_preamble[64]; // Hostname + Id - if (syslog_host_hash != GetHash(Settings.syslog_host, strlen(Settings.syslog_host))) { - syslog_host_hash = GetHash(Settings.syslog_host, strlen(Settings.syslog_host)); + uint32_t current_hash = GetHash(Settings.syslog_host, strlen(Settings.syslog_host)); + if (syslog_host_hash != current_hash) { + syslog_host_hash = current_hash; WiFi.hostByName(Settings.syslog_host, syslog_host_addr); // If sleep enabled this might result in exception so try to do it once using hash } if (PortUdp.beginPacket(syslog_host_addr, Settings.syslog_port)) { @@ -1221,6 +1415,7 @@ void Syslog(void) memcpy(log_data, syslog_preamble, strlen(syslog_preamble)); PortUdp.write(log_data); PortUdp.endPacket(); + delay(1); // Add time for UDP handling (#5512) } else { syslog_level = 0; syslog_timer = SYSLOG_TIMER; diff --git a/sonoff/support_features.ino b/sonoff/support_features.ino index f5229e81f..bde88738e 100644 --- a/sonoff/support_features.ino +++ b/sonoff/support_features.ino @@ -382,12 +382,14 @@ void GetFeatures(void) feature_sns2 |= 0x00100000; // xsns_40_pn532.ino #endif #ifdef USE_MAX44009 - feature_sns2 |= 0x00200000; + feature_sns2 |= 0x00200000; // xsns_41_max44009.ino #endif #ifdef USE_SCD30 - feature_sns2 |= 0x00400000; + feature_sns2 |= 0x00400000; // xsns_42_scd30.ino +#endif +#ifdef USE_HRE + feature_sns2 |= 0x00800000; // xsns_43_hre.ino #endif -// feature_sns2 |= 0x00800000; // feature_sns2 |= 0x01000000; // feature_sns2 |= 0x02000000; // feature_sns2 |= 0x04000000; diff --git a/sonoff/support_rotary.ino b/sonoff/support_rotary.ino index db88d8378..c04889a3f 100644 --- a/sonoff/support_rotary.ino +++ b/sonoff/support_rotary.ino @@ -17,8 +17,7 @@ along with this program. If not, see . */ -#define ROTARY_V1 -#ifdef ROTARY_V1 + /*********************************************************************************************\ * Rotary support \*********************************************************************************************/ @@ -31,6 +30,9 @@ uint8_t rotary_last_position = 128; uint8_t interrupts_in_use = 0; uint8_t rotary_changed = 0; +//#define ROTARY_V1 +#ifdef ROTARY_V1 + /********************************************************************************************/ void update_position(void) diff --git a/sonoff/support_rtc.ino b/sonoff/support_rtc.ino index 9a36951f0..fd9575b32 100644 --- a/sonoff/support_rtc.ino +++ b/sonoff/support_rtc.ino @@ -22,10 +22,11 @@ * Timezone by Jack Christensen (https://github.com/JChristensen/Timezone) \*********************************************************************************************/ -#define SECS_PER_MIN ((uint32_t)(60UL)) -#define SECS_PER_HOUR ((uint32_t)(3600UL)) -#define SECS_PER_DAY ((uint32_t)(SECS_PER_HOUR * 24UL)) -#define MINS_PER_HOUR ((uint32_t)(60UL)) +const uint32_t SECS_PER_MIN = 60UL; +const uint32_t SECS_PER_HOUR = 3600UL; +const uint32_t SECS_PER_DAY = SECS_PER_HOUR * 24UL; +const uint32_t MINS_PER_HOUR = 60UL; + #define LEAP_YEAR(Y) (((1970+Y)>0) && !((1970+Y)%4) && (((1970+Y)%100) || !((1970+Y)%400))) extern "C" { @@ -61,7 +62,7 @@ String GetBuildDateAndTime(void) // sscanf(mdate, "%s %d %d", bdt, &day, &year); // Not implemented in 2.3.0 and probably too much code uint8_t i = 0; - for (char *str = strtok_r(mdate, " ", &p); str && i < 3; str = strtok_r(NULL, " ", &p)) { + for (char *str = strtok_r(mdate, " ", &p); str && i < 3; str = strtok_r(nullptr, " ", &p)) { switch (i++) { case 0: // Month smonth = str; diff --git a/sonoff/support_switch.ino b/sonoff/support_switch.ino index 7e84149ba..3eb927388 100644 --- a/sonoff/support_switch.ino +++ b/sonoff/support_switch.ino @@ -25,7 +25,7 @@ * Inspired by (https://github.com/OLIMEX/olimex-iot-firmware-esp8266/blob/master/olimex/user/user_switch2.c) \*********************************************************************************************/ -#define SWITCH_PROBE_INTERVAL 10 // Time in milliseconds between switch input probe +const uint8_t SWITCH_PROBE_INTERVAL = 10; // Time in milliseconds between switch input probe #include diff --git a/sonoff/support_wifi.ino b/sonoff/support_wifi.ino index 2c7299991..445c14c0b 100644 --- a/sonoff/support_wifi.ino +++ b/sonoff/support_wifi.ino @@ -22,15 +22,15 @@ \*********************************************************************************************/ #ifndef WIFI_RSSI_THRESHOLD -#define WIFI_RSSI_THRESHOLD 10 // Difference in dB between current network and scanned network +#define WIFI_RSSI_THRESHOLD 10 // Difference in dB between current network and scanned network #endif #ifndef WIFI_RESCAN_MINUTES -#define WIFI_RESCAN_MINUTES 44 // Number of minutes between wifi network rescan +#define WIFI_RESCAN_MINUTES 44 // Number of minutes between wifi network rescan #endif -#define WIFI_CONFIG_SEC 180 // seconds before restart -#define WIFI_CHECK_SEC 20 // seconds -#define WIFI_RETRY_OFFSET_SEC 20 // seconds +const uint8_t WIFI_CONFIG_SEC = 180; // seconds before restart +const uint8_t WIFI_CHECK_SEC = 20; // seconds +const uint8_t WIFI_RETRY_OFFSET_SEC = 20; // seconds /* // This worked for 2_5_0_BETA2 but fails since then. Waiting for a solution from core team (#4952) diff --git a/sonoff/xdrv_01_webserver.ino b/sonoff/xdrv_01_webserver.ino index 48a13fe11..2ecd8c4c3 100644 --- a/sonoff/xdrv_01_webserver.ino +++ b/sonoff/xdrv_01_webserver.ino @@ -27,21 +27,21 @@ #define XDRV_01 1 -#define CHUNKED_BUFFER_SIZE 400 // Chunk buffer size - #ifndef WIFI_SOFT_AP_CHANNEL -#define WIFI_SOFT_AP_CHANNEL 1 // Soft Access Point Channel number between 1 and 11 as used by SmartConfig web GUI +#define WIFI_SOFT_AP_CHANNEL 1 // Soft Access Point Channel number between 1 and 11 as used by SmartConfig web GUI #endif -#define HTTP_REFRESH_TIME 2345 // milliseconds -#define HTTP_RESTART_RECONNECT_TIME 9000 // milliseconds -#define HTTP_OTA_RESTART_RECONNECT_TIME 20000 // milliseconds +const uint16_t CHUNKED_BUFFER_SIZE = 400; // Chunk buffer size (should be smaller than half mqtt_date size) + +const uint16_t HTTP_REFRESH_TIME = 2345; // milliseconds +#define HTTP_RESTART_RECONNECT_TIME 9000 // milliseconds +#define HTTP_OTA_RESTART_RECONNECT_TIME 20000 // milliseconds #include #include #ifdef USE_RF_FLASH -uint8_t *efm8bb1_update = NULL; +uint8_t *efm8bb1_update = nullptr; #endif // USE_RF_FLASH enum UploadTypes { UPL_TASMOTA, UPL_SETTINGS, UPL_EFM8BB1 }; @@ -239,21 +239,21 @@ const char HTTP_HEAD_STYLE1[] PROGMEM = "