From 7f42caa6c0f9c139b8a8f1b3acd61422834609ba Mon Sep 17 00:00:00 2001 From: Philip Barclay Date: Mon, 18 Nov 2019 19:10:45 +1300 Subject: [PATCH 01/84] Added YPR to web interface --- sonoff/xsns_32_mpu6050.ino | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/sonoff/xsns_32_mpu6050.ino b/sonoff/xsns_32_mpu6050.ino index 58915efc9..d212a45e2 100644 --- a/sonoff/xsns_32_mpu6050.ino +++ b/sonoff/xsns_32_mpu6050.ino @@ -56,6 +56,7 @@ int16_t MPU_6050_temperature = 0; VectorInt16 aaReal; // [x, y, z] gravity-free accel sensor measurements VectorFloat gravity; // [x, y, z] gravity vector float euler[3]; // [psi, theta, phi] Euler angle container + float yawPitchRoll[3]; // [yaw, pitch roll] Yaw-pitch-roll container } MPU6050_DMP; MPU6050_DMP MPU6050_dmp; @@ -78,6 +79,7 @@ void MPU_6050PerformReading(void) mpu6050.dmpGetAccel(&MPU6050_dmp.aa, MPU6050_dmp.fifoBuffer); mpu6050.dmpGetGravity(&MPU6050_dmp.gravity, &MPU6050_dmp.q); mpu6050.dmpGetLinearAccel(&MPU6050_dmp.aaReal, &MPU6050_dmp.aa, &MPU6050_dmp.gravity); + mpu6050.dmpGetYawPitchRoll(MPU6050_dmp.yawPitchRoll, &MPU6050_dmp.q, &MPU6050_dmp.gravity); MPU_6050_gx = MPU6050_dmp.euler[0] * 180/M_PI; MPU_6050_gy = MPU6050_dmp.euler[1] * 180/M_PI; MPU_6050_gz = MPU6050_dmp.euler[2] * 180/M_PI; @@ -161,7 +163,13 @@ const char HTTP_SNS_AXIS[] PROGMEM = "{s}" D_SENSOR_MPU6050 " " D_AZ_AXIS "{m}%s{e}" // {s} = , {m} = , {e} = "{s}" D_SENSOR_MPU6050 " " D_GX_AXIS "{m}%s{e}" // {s} = , {m} = , {e} = "{s}" D_SENSOR_MPU6050 " " D_GY_AXIS "{m}%s{e}" // {s} = , {m} = , {e} = - "{s}" D_SENSOR_MPU6050 " " D_GZ_AXIS "{m}%s{e}"; // {s} = , {m} = , {e} = + "{s}" D_SENSOR_MPU6050 " " D_GZ_AXIS "{m}%s{e}" // {s} = , {m} = , {e} = +#ifdef USE_MPU6050_DMP + "{s}" D_SENSOR_MPU6050 " " D_YAW "{m}%s{e}" // {s} = , {m} = , {e} = + "{s}" D_SENSOR_MPU6050 " " D_PITCH "{m}%s{e}" // {s} = , {m} = , {e} = + "{s}" D_SENSOR_MPU6050 " " D_ROLL "{m}%s{e}" // {s} = , {m} = , {e} = +#endif // USE_MPU_DMP + ; #endif // USE_WEBSERVER #define D_JSON_AXIS_AX "AccelXAxis" From dac6ce1e54b73eda46da6d22b8058550184a393d Mon Sep 17 00:00:00 2001 From: Philip Barclay Date: Mon, 18 Nov 2019 21:33:34 +1300 Subject: [PATCH 02/84] Added YPR results to webserver and JSON output if DMP enabled. --- tasmota/xsns_32_mpu6050.ino | 32 ++++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/tasmota/xsns_32_mpu6050.ino b/tasmota/xsns_32_mpu6050.ino index 7b26101a1..6f3cff5e8 100644 --- a/tasmota/xsns_32_mpu6050.ino +++ b/tasmota/xsns_32_mpu6050.ino @@ -69,7 +69,7 @@ MPU6050 mpu6050; void MPU_6050PerformReading(void) { #ifdef USE_MPU6050_DMP - mpu6050.resetFIFO(); // with a default dampling rate of 200Hz, we create a delay of approx. 5ms with a complete read cycle + mpu6050.resetFIFO(); // with a default sampling rate of 200Hz, we create a delay of approx. 5ms with a complete read cycle MPU6050_dmp.fifoCount = mpu6050.getFIFOCount(); while (MPU6050_dmp.fifoCount < MPU6050_dmp.packetSize) MPU6050_dmp.fifoCount = mpu6050.getFIFOCount(); mpu6050.getFIFOBytes(MPU6050_dmp.fifoBuffer, MPU6050_dmp.packetSize); @@ -147,6 +147,10 @@ void MPU_6050Detect(void) } } +#define D_YAW "Yaw" +#define D_PITCH "Pitch" +#define D_ROLL "Roll" + #ifdef USE_WEBSERVER const char HTTP_SNS_AXIS[] PROGMEM = "{s}" D_SENSOR_MPU6050 " " D_AX_AXIS "{m}%s{e}" // {s} = , {m} = , {e} = @@ -154,13 +158,13 @@ const char HTTP_SNS_AXIS[] PROGMEM = "{s}" D_SENSOR_MPU6050 " " D_AZ_AXIS "{m}%s{e}" // {s} = , {m} = , {e} = "{s}" D_SENSOR_MPU6050 " " D_GX_AXIS "{m}%s{e}" // {s} = , {m} = , {e} = "{s}" D_SENSOR_MPU6050 " " D_GY_AXIS "{m}%s{e}" // {s} = , {m} = , {e} = - "{s}" D_SENSOR_MPU6050 " " D_GZ_AXIS "{m}%s{e}" // {s} = , {m} = , {e} = + "{s}" D_SENSOR_MPU6050 " " D_GZ_AXIS "{m}%s{e}"; // {s} = , {m} = , {e} = #ifdef USE_MPU6050_DMP +const char HTTP_SNS_YPR[] PROGMEM = "{s}" D_SENSOR_MPU6050 " " D_YAW "{m}%s{e}" // {s} = , {m} = , {e} = - "{s}" D_SENSOR_MPU6050 " " D_PITCH "{m}%s{e}" // {s} = , {m} = , {e} = - "{s}" D_SENSOR_MPU6050 " " D_ROLL "{m}%s{e}" // {s} = , {m} = , {e} = -#endif // USE_MPU_DMP - ; + "{s}" D_SENSOR_MPU6050 " " D_PITCH "{m}%s{e}" // {s} = , {m} = , {e} = + "{s}" D_SENSOR_MPU6050 " " D_ROLL "{m}%s{e}"; // {s} = , {m} = , {e} = +#endif // USE_MPU6050_DMP #endif // USE_WEBSERVER #define D_JSON_AXIS_AX "AccelXAxis" @@ -169,6 +173,9 @@ const char HTTP_SNS_AXIS[] PROGMEM = #define D_JSON_AXIS_GX "GyroXAxis" #define D_JSON_AXIS_GY "GyroYAxis" #define D_JSON_AXIS_GZ "GyroZAxis" +#define D_JSON_YAW "Yaw" +#define D_JSON_PITCH "Pitch" +#define D_JSON_ROLL "Roll" void MPU_6050Show(bool json) { @@ -203,8 +210,20 @@ void MPU_6050Show(bool json) snprintf_P(json_axis_gy, sizeof(json_axis_gy), PSTR(",\"" D_JSON_AXIS_GY "\":%s"), axis_gy); char json_axis_gz[25]; snprintf_P(json_axis_gz, sizeof(json_axis_gz), PSTR(",\"" D_JSON_AXIS_GZ "\":%s"), axis_gz); +#ifdef USE_MPU6050_DMP + char json_ypr_y[25]; + snprintf_P(json_ypr_y, sizeof(json_ypr_y), PSTR(",\"" D_JSON_YAW "\":%s"), MPU6050_dmp.yawPitchRoll[0]); + char json_ypr_p[25]; + snprintf_P(json_ypr_p, sizeof(json_ypr_p), PSTR(",\"" D_JSON_PITCH "\":%s"), MPU6050_dmp.yawPitchRoll[1]); + char json_ypr_r[25]; + snprintf_P(json_ypr_r, sizeof(json_ypr_r), PSTR(",\"" D_JSON_ROLL "\":%s"), MPU6050_dmp.yawPitchRoll[2]); + ResponseAppend_P(PSTR(",\"%s\":{\"" D_JSON_TEMPERATURE "\":%s%s%s%s%s%s%s%s%s%s}"), + D_SENSOR_MPU6050, temperature, json_axis_ax, json_axis_ay, json_axis_az, json_axis_gx, json_axis_gy, json_axis_gz, + json_ypr_y, json_ypr_p, json_ypr_r); +#else ResponseAppend_P(PSTR(",\"%s\":{\"" D_JSON_TEMPERATURE "\":%s%s%s%s%s%s%s}"), D_SENSOR_MPU6050, temperature, json_axis_ax, json_axis_ay, json_axis_az, json_axis_gx, json_axis_gy, json_axis_gz); +#endif // USE_MPU6050_DMP #ifdef USE_DOMOTICZ DomoticzSensor(DZ_TEMP, temperature); #endif // USE_DOMOTICZ @@ -212,6 +231,7 @@ void MPU_6050Show(bool json) } else { WSContentSend_PD(HTTP_SNS_TEMP, D_SENSOR_MPU6050, temperature, TempUnit()); WSContentSend_PD(HTTP_SNS_AXIS, axis_ax, axis_ay, axis_az, axis_gx, axis_gy, axis_gz); + WSContentSend_PD(HTTP_SNS_YPR, MPU6050_dmp.yawPitchRoll[0], MPU6050_dmp.yawPitchRoll[1], MPU6050_dmp.yawPitchRoll[2]); #endif // USE_WEBSERVER } } From fe63e5cca2fcae187e7d6c3e3211db0414667821 Mon Sep 17 00:00:00 2001 From: Philip Barclay Date: Fri, 29 Nov 2019 21:07:39 +1300 Subject: [PATCH 03/84] Stringification and conversion from radians to degrees. --- tasmota/xsns_32_mpu6050.ino | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/tasmota/xsns_32_mpu6050.ino b/tasmota/xsns_32_mpu6050.ino index 6f3cff5e8..bcec100b0 100644 --- a/tasmota/xsns_32_mpu6050.ino +++ b/tasmota/xsns_32_mpu6050.ino @@ -196,6 +196,14 @@ void MPU_6050Show(bool json) dtostrfd(MPU_6050_gy, Settings.flag2.axis_resolution, axis_gy); char axis_gz[33]; dtostrfd(MPU_6050_gz, Settings.flag2.axis_resolution, axis_gz); +#ifdef USE_MPU6050_DMP + char axis_yaw[33]; + dtostrfd(MPU6050_dmp.yawPitchRoll[0] / PI * 180.0, Settings.flag2.axis_resolution, axis_yaw); + char axis_pitch[33]; + dtostrfd(MPU6050_dmp.yawPitchRoll[1] / PI * 180.0, Settings.flag2.axis_resolution, axis_pitch); + char axis_roll[33]; + dtostrfd(MPU6050_dmp.yawPitchRoll[2] / PI * 180.0, Settings.flag2.axis_resolution, axis_roll); +#endif // USE_MPU6050_DMP if (json) { char json_axis_ax[25]; @@ -212,11 +220,11 @@ void MPU_6050Show(bool json) snprintf_P(json_axis_gz, sizeof(json_axis_gz), PSTR(",\"" D_JSON_AXIS_GZ "\":%s"), axis_gz); #ifdef USE_MPU6050_DMP char json_ypr_y[25]; - snprintf_P(json_ypr_y, sizeof(json_ypr_y), PSTR(",\"" D_JSON_YAW "\":%s"), MPU6050_dmp.yawPitchRoll[0]); + snprintf_P(json_ypr_y, sizeof(json_ypr_y), PSTR(",\"" D_JSON_YAW "\":%s"), axis_yaw); char json_ypr_p[25]; - snprintf_P(json_ypr_p, sizeof(json_ypr_p), PSTR(",\"" D_JSON_PITCH "\":%s"), MPU6050_dmp.yawPitchRoll[1]); + snprintf_P(json_ypr_p, sizeof(json_ypr_p), PSTR(",\"" D_JSON_PITCH "\":%s"), axis_pitch); char json_ypr_r[25]; - snprintf_P(json_ypr_r, sizeof(json_ypr_r), PSTR(",\"" D_JSON_ROLL "\":%s"), MPU6050_dmp.yawPitchRoll[2]); + snprintf_P(json_ypr_r, sizeof(json_ypr_r), PSTR(",\"" D_JSON_ROLL "\":%s"), axis_roll); ResponseAppend_P(PSTR(",\"%s\":{\"" D_JSON_TEMPERATURE "\":%s%s%s%s%s%s%s%s%s%s}"), D_SENSOR_MPU6050, temperature, json_axis_ax, json_axis_ay, json_axis_az, json_axis_gx, json_axis_gy, json_axis_gz, json_ypr_y, json_ypr_p, json_ypr_r); @@ -231,7 +239,9 @@ void MPU_6050Show(bool json) } else { WSContentSend_PD(HTTP_SNS_TEMP, D_SENSOR_MPU6050, temperature, TempUnit()); WSContentSend_PD(HTTP_SNS_AXIS, axis_ax, axis_ay, axis_az, axis_gx, axis_gy, axis_gz); - WSContentSend_PD(HTTP_SNS_YPR, MPU6050_dmp.yawPitchRoll[0], MPU6050_dmp.yawPitchRoll[1], MPU6050_dmp.yawPitchRoll[2]); +#ifdef USE_MPU6050_DMP + WSContentSend_PD(HTTP_SNS_YPR, axis_yaw, axis_pitch, axis_roll); +#endif // USE_MPU6050_DMP #endif // USE_WEBSERVER } } From 0b59cc6ea123b59b5ca96d66f62b8a298cfb7628 Mon Sep 17 00:00:00 2001 From: Norbert Richter Date: Mon, 2 Dec 2019 15:15:04 +0100 Subject: [PATCH 04/84] decode-config moved to https://github.com/tasmota/decode-config --- tools/decode-config.md | 408 +---- tools/decode-config.py | 3322 ---------------------------------------- 2 files changed, 2 insertions(+), 3728 deletions(-) delete mode 100755 tools/decode-config.py diff --git a/tools/decode-config.md b/tools/decode-config.md index 4097638fa..10019a23f 100644 --- a/tools/decode-config.md +++ b/tools/decode-config.md @@ -1,407 +1,3 @@ -# decode-config.py -_decode-config.py_ is able to backup and restore Tasmota configuration. +A tool to backup and restore the configuration of [Tasmota](http://tasmota.com/)-devices. -In comparison with the Tasmota build-in "Backup/Restore Configuration" function _decode-config.py_ -* uses human readable and editable [JSON](http://www.json.org/)-format for backup/restore, -* can restore previously backup and changed [JSON](http://www.json.org/)-format files, -* is able to create Tasmota compatible command list with related config parameter - -Comparing backup files created by *decode-config.py* and *.dmp files created by Tasmota "Backup/Restore Configuration": - -|   | decode-config.py
*.json file | Tasmota
*.dmp file | -|-------------------------|:-------------------------------:|:-----------------------------------:| -| Encrypted | No | Yes | -| Readable | Yes | No | -| Simply editable | Yes | No | -| Simply batch processing | Yes | No | - -_decode-config.py_ is compatible with Tasmota version from v5.10.0 up to now. - -# Content -* [Prerequisite](decode-config.md#prerequisite) -* [File Types](decode-config.md#file-types) - * [.dmp File Format](decode-config.md#-dmp-format) - * [.json File Format](decode-config.md#-json-format) - * [.bin File Format](decode-config.md#-bin-format) - * [File extensions](decode-config.md#file-extensions) -* [Usage](decode-config.md#usage) - * [Basics](decode-config.md#basics) - * [Save backup file](decode-config.md#save-backup-file) - * [Restore backup file](decode-config.md#restore-backup-file) - * [Output to screen](decode-config.md#output-to-screen) - * [JSON output](decode-config.md#json-output) - * [Tasmota command output](decode-config.md#tasmota-command-output) - * [Filter data](decode-config.md#filter-data) - * [Configuration file](decode-config.md#configuration-file) - * [More program arguments](decode-config.md#more-program-arguments) - * [Examples](decode-config.md#examples) - * [Config file](decode-config.md#config-file) - * [Using Tasmota binary configuration files](decode-config.md#using-tasmota-binary-configuration-files) - * [Use batch processing](decode-config.md#use-batch-processing) - * [Notes](decode-config.md#notes) - -## Prerequisite -* This program is written in [Python](https://en.wikipedia.org/wiki/Python_(programming_language)) so you need to install a working python environment for your operating system. - -### Linux -``` -sudo apt-get install python python-pip libcurl4-openssl-dev libssl-dev -``` -``` -pip install pycurl configargparse -``` - -### Windows 10 - -Install [Python 2.7](https://www.python.org/download/releases/2.7/) then install dependencies. For PyCurl you need to [download pycurl‑7.43.0.3‑cp27‑cp27m‑win_amd64.whl](https://www.lfd.uci.edu/~gohlke/pythonlibs/#pycurl) for Windows 10 64bit. -``` -pip install pycurl-7.43.0.3-cp27-cp27m-win_amd64.whl -// run the command from the folder where you downloaded the file - -pip install configargparse -``` - -* [Tasmota](https://github.com/arendst/Tasmota) [Firmware](https://github.com/arendst/Tasmota/releases) with Web-Server enabled: - * To backup or restore configurations from or to a Tasmota device you need a firmare with enabled web-server in admin mode (command [WebServer 2](https://tasmota.github.io/docs/#/Commands#wifi)). This is the Tasmota default. - * If using your own compiled firmware be aware to enable the web-server (`#define USE_WEBSERVER` and `#define WEB_SERVER 2`). - -## File Types -_decode-config.py_ can handle the following backup file types: -### .dmp Format -Configuration data as used by Tasmota "Backup/Restore Configuration" web interface. -This format is binary and encrypted. -### .json Format -Configuration data in [JSON](http://www.json.org/)-format. -This format is decrypted, human readable and editable and can also be used for the `--restore-file` parameter. -This file will be created by _decode-config.py_ using the `--backup-file` with `--backup-type json` parameter, this is the default. -### .bin Format -Configuration data in binary format. -This format is binary decryptet, editable (e.g. using a hex editor) and can also be used for `--restore-file` command. -It will be created by _decode-config.py_ using `--backup-file` with `--backup-type bin`. -Note: -The .bin file contains the same information as the original .dmp file from Tasmota "Backup/Restore Configuration" but it is decrpted and 4 byte longer than an original (it is a prefix header at the beginning). .bin file data starting at address 4 contains the same as the **struct SYSCFG** from Tasmota [settings.h](https://github.com/arendst/Tasmota/blob/master/tasmota/settings.h) in decrypted format. - -#### File extensions -You don't need to append exensions for your file name as _decode-config.py_ uses auto extension as default. The extension will be choose based on file contents and `--backup-type` parameter. -If you do not want using auto extensions use the `--no-extension` parameter. - -## Usage -After download don't forget to set the executable flag under linux with `chmod +x decode-config.py` or call the program using `python decode-config.py...`. - -### Basics -At least pass a source where you want to read the configuration data from using `-f ` or `-d `: - -The source can be either -* a Tasmota device hostname or IP using the `-d ` parameter -* a Tasmota `*.dmp` configuration file using `-f ` parameter - -Example: - - decode-config.py -d tasmota-4281 - -will output a human readable configuration in [JSON](http://www.json.org/)-format: - - { - "altitude": 112, - "baudrate": 115200, - "blinkcount": 10, - "blinktime": 10, - ... - "ws_width": [ - 1, - 3, - 5 - ] - } - - -### Save backup file -To save the output as backup file use `--backup-file `, you can use placeholder for Version, Friendlyname and Hostname: - - decode-config.py -d tasmota-4281 --backup-file Config_@f_@v - -If you have setup a WebPassword within Tasmota, use - - decode-config.py -d tasmota-4281 -p --backup-file Config_@f_@v - -will create a file like `Config_Tasmota_6.4.0.json` (the part `Tasmota` and `6.4.0` will choosen related to your device configuration). Because the default backup file format is JSON, you can read and change it with any raw text editor. - -### Restore backup file -Reading back a saved (and possible changed) backup file use the `--restore-file ` parameter. This will read the (changed) configuration data from this file and send it back to the source device or filename. - -To restore the previously save backup file `Config_Tasmota_6.2.1.json` to device `tasmota-4281` use: - - decode-config.py -d tasmota-4281 --restore-file Config_Tasmota_6.2.1.json - -with password set by WebPassword: - - decode-config.py -d tasmota-4281 -p --restore-file Config_Tasmota_6.2.1.json - -### Output to screen -To force screen output use the `--output` parameter. - -Output to screen is default enabled when calling the program with a source parameter (-f or -d) but without any backup or restore parameter. - -#### JSON output -The default output format is [JSON](decode-config.md#-json-format). You can force JSON output using the `--output-format json` parameter. - -Example: - - decode-config.py -d tasmota-4281 -c my.conf -x Wifi --output-format json - - { - ... - "hostname": "%s-%04d", - "ip_address": [ - "0.0.0.0", - "192.168.12.1", - "255.255.255.0", - "192.168.12.1" - ], - "ntp_server": [ - "ntp.localnet.home", - "ntp2.localnet.home", - "192.168.12.1" - ], - "sta_active": 0, - "sta_config": 5, - "sta_pwd": [ - "myWlAnPaszxwo!z", - "myWlAnPaszxwo!z2" - ], - "sta_ssid": [ - "wlan.1", - "my-wlan" - ], - "web_password": "myPaszxwo!z", - "webserver": 2 - ... - } - -Note: JSON output always contains all configuration data like the backup file except you are using `--group` arg. - - -#### Tasmota command output -_decode-config.py_ is able to translate the configuration data to (most all) Tasmota commands. To output your configuration as Tasmota commands use `--output-format cmnd` or `--output-format command`. - -Example: - - decode-config.py -d tasmota-4281 -c my.conf -g Wifi --output-format cmnd - - # Wifi: - AP 0 - Hostname %s-%04d - IPAddress1 0.0.0.0 - IPAddress2 192.168.12.1 - IPAddress3 255.255.255.0 - IPAddress4 192.168.12.1 - NtpServer1 ntp.localnet.home - NtpServer2 ntp2.localnet.home - NtpServer3 192.168.12.1 - Password1 myWlAnPaszxwo!z - Password2 myWlAnPaszxwo!z2 - SSId1 wlan.1 - SSId2 wlan.1 - WebPassword myPaszxwo!z - WebServer 2 - WifiConfig 5 - -Note: A few very specific module commands like MPC230xx, KNX and some Display commands are not supported. These are still available by JSON output. - -### Filter data -The huge number of Tasmota configuration data can be overstrained and confusing, so the most of the configuration data are grouped into categories. - -With _decode-config.py_ the following categories are available: `Display`, `Domoticz`, `Internal`, `KNX`, `Led`, `Logging`, `MCP230xx`, `MQTT`, `Main`, `Management`, `Pow`, `Sensor`, `Serial`, `SetOption`, `RF`, `System`, `Timers`, `Wifi` - -These are similary to the categories on [https://tasmota.github.io/docs/#/Commands](Tasmota Command Wiki). - -To filter outputs to a subset of groups use the `-g` or `--group` arg concatenating the grooup you want, e. g. - - decode-config.py -d tasmota-4281 -c my.conf --output-format cmnd --group Main MQTT Management Wifi - - -### Configuration file -Each argument that start with `--` (eg. `--file`) can also be set in a config file (specified via -c). Config file syntax allows: key=value, flag=true, stuff=[a,b,c] (for details, see syntax at [https://pypi.org/project/ConfigArgParse](https://pypi.org/project/ConfigArgParse/)). - -If an argument is specified in more than one place, then commandline values override config file values which override defaults. This is usefull if you always use the same argument or a basic set of arguments. - -The http authentication credentials `--username` and `--password` is predestinated to store it in a file instead using it on your command line as argument: - -e.g. my.conf: - - [source] - username = admin - password = myPaszxwo!z - -To make a backup file from example above you can now pass the config file instead using the password on command line: - - decode-config.py -d tasmota-4281 -c my.conf --backup-file Config_@f_@v - - - -### More program arguments -For better reading each short written arg (minus sign `-`) has a corresponding long version (two minus signs `--`), eg. `--device` for `-d` or `--file` for `-f` (note: not even all `--` arg has a corresponding `-` one). - -A short list of possible program args is displayed using `-h` or `--help`. - -For advanced help use `-H` or `--full-help`: - - usage: decode-config.py [-f ] [-d ] [-P ] - [-u ] [-p ] [-i ] - [-o ] [-t json|bin|dmp] [-E] [-e] [-F] - [--json-indent ] [--json-compact] - [--json-hide-pw] [--json-show-pw] - [--cmnd-indent ] [--cmnd-groups] - [--cmnd-nogroups] [--cmnd-sort] [--cmnd-unsort] - [-c ] [-S] [-T json|cmnd|command] - [-g {Control,Devices,Display,Domoticz,Internal,Knx,Light,Management,Mqtt,Power,Rf,Rules,Sensor,Serial,Setoption,Shutter,System,Timer,Wifi} [{Control,Devices,Display,Domoticz,Internal,Knx,Light,Management,Mqtt,Power,Rf,Rules,Sensor,Serial,Setoption,Shutter,System,Timer,Wifi} ...]] - [--ignore-warnings] [-h] [-H] [-v] [-V] - - Backup/Restore Tasmota configuration data. Args that start with '--' - (eg. -f) can also be set in a config file (specified via -c). Config file - syntax allows: key=value, flag=true, stuff=[a,b,c] (for details, see syntax at - https://goo.gl/R74nmi). If an arg is specified in more than one place, then - commandline values override config file values which override defaults. - - Source: - Read/Write Tasmota configuration from/to - - -f, --file, --tasmota-file - file to retrieve/write Tasmota configuration from/to - (default: None)' - -d, --device, --host - hostname or IP address to retrieve/send Tasmota - configuration from/to (default: None) - -P, --port TCP/IP port number to use for the host connection - (default: 80) - -u, --username - host HTTP access username (default: admin) - -p, --password - host HTTP access password (default: None) - - Backup/Restore: - Backup & restore specification - - -i, --restore-file - file to restore configuration from (default: None). - Replacements: @v=firmware version from config, - @f=device friendly name from config, @h=device - hostname from config, @H=device hostname from device - (-d arg only) - -o, --backup-file - file to backup configuration to (default: None). - Replacements: @v=firmware version from config, - @f=device friendly name from config, @h=device - hostname from config, @H=device hostname from device - (-d arg only) - -t, --backup-type json|bin|dmp - backup filetype (default: 'json') - -E, --extension append filetype extension for -i and -o filename - (default) - -e, --no-extension do not append filetype extension, use -i and -o - filename as passed - -F, --force-restore force restore even configuration is identical - - JSON output: - JSON format specification - - --json-indent - pretty-printed JSON output using indent level - (default: 'None'). -1 disables indent. - --json-compact compact JSON output by eliminate whitespace - --json-hide-pw hide passwords - --json-show-pw, --json-unhide-pw - unhide passwords (default) - - Tasmota command output: - Tasmota command output format specification - - --cmnd-indent - Tasmota command grouping indent level (default: '2'). - 0 disables indent - --cmnd-groups group Tasmota commands (default) - --cmnd-nogroups leave Tasmota commands ungrouped - --cmnd-sort sort Tasmota commands (default) - --cmnd-unsort leave Tasmota commands unsorted - - Common: - Optional arguments - - -c, --config - program config file - can be used to set default - command args (default: None) - -S, --output display output regardsless of backup/restore usage - (default do not output on backup or restore usage) - -T, --output-format json|cmnd|command - display output format (default: 'json') - -g, --group {Control,Devices,Display,Domoticz,Internal,Knx,Light,Management,Mqtt,Power,Rf,Rules,Sensor,Serial,Setoption,Shutter,System,Timer,Wifi} - limit data processing to command groups (default no - filter) - --ignore-warnings do not exit on warnings. Not recommended, used by your - own responsibility! - - Info: - Extra information - - -h, --help show usage help message and exit - -H, --full-help show full help message and exit - -v, --verbose produce more output about what the program does - -V, --version show program's version number and exit - - Either argument -d or -f must be given. - -### Program parameter notes - -_decode-config.py_ - - -### Examples -The most of the examples are for linux command line. Under Windows call the program using `python decode-config.py ...`. - -#### Config file -Note: The example contains .ini style sections `[...]`. Sections are always treated as comment and serves as clarity only. -For further details of config file syntax see [https://pypi.org/project/ConfigArgParse](https://pypi.org/project/ConfigArgParse/). - -*my.conf* - - [Source] - username = admin - password = myPaszxwo!z - - [JSON] - json-indent 2 - -#### Using Tasmota binary configuration files - -1. Restore a Tasmota configuration file - - `decode-config.py -c my.conf -d tasmota --restore-file Config_Tasmota_6.2.1.dmp` - -2. Backup device using Tasmota configuration compatible format - - a) use file extension to choice the file format - - `decode-config.py -c my.conf -d tasmota --backup-file Config_@f_@v.dmp` - - b) use args to choice the file format - - `decode-config.py -c my.conf -d tasmota --backup-type dmp --backup-file Config_@f_@v` - -#### Use batch processing - - for device in tasmota1 tasmota2 tasmota3; do ./decode-config.py -c my.conf -d $device -o Config_@f_@v - -or under windows - - for device in (tasmota1 tasmota2 tasmota3) do python decode-config.py -c my.conf -d %device -o Config_@f_@v - -will produce JSON configuration files for host tasmota1, tasmota2 and tasmota3 using friendly name and Tasmota firmware version for backup filenames. - -## Notes -Some general notes: -* Filename replacement macros **@h** and **@H**: - * **@h** -The **@h** replacement macro uses the hostname configured with the Tasomta Wifi `Hostname ` command (defaults to `%s-%04d`). It will not use the network hostname of your device because this is not available when working with files only (e.g. `--file ` as source). -To prevent having a useless % in your filename, **@h** will not replaced by configuration data hostname if this contains '%' characters. - * **@H** -If you want to use the network hostname within your filename, use the **@H** replacement macro instead - but be aware this will only replaced if you are using a network device as source (`-d`, `--device`, `--host`); it will not work when using a file as source (`-f`, `--file`) +## decode-config has moved to [https://github.com/tasmota/decode-config](https://github.com/tasmota/decode-config) diff --git a/tools/decode-config.py b/tools/decode-config.py deleted file mode 100755 index f15c696ae..000000000 --- a/tools/decode-config.py +++ /dev/null @@ -1,3322 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -from __future__ import print_function -from past.builtins import long -VER = '2.4.0039' - -""" - decode-config.py - Backup/Restore Tasmota configuration data - - Copyright (C) 2019 Norbert Richter - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - - -Requirements: - - Python 2.x: - pip install json requests urllib2 configargparse - - -Instructions: - Execute command with option -d to retrieve config data from a host - or use -f to read a configuration file saved using Tasmota Web-UI - - For further information read 'decode-config.md' - - For help execute command with argument -h (or -H for advanced help) - - -Usage: decode-config.py [-f ] [-d ] [-P ] - [-u ] [-p ] [-i ] - [-o ] [-t json|bin|dmp] [-E] [-e] [-F] - [--json-indent ] [--json-compact] - [--json-hide-pw] [--json-show-pw] - [--cmnd-indent ] [--cmnd-groups] - [--cmnd-nogroups] [--cmnd-sort] [--cmnd-unsort] - [-c ] [-S] [-T json|cmnd|command] - [-g {Control,Devices,Display,Domoticz,Internal,Knx,Light,Management,Mqtt,Power,Rules,Sensor,Serial,Setoption,Shutter,Rf,System,Timer,Wifi} [{Control,Devices,Display,Domoticz,Internal,Knx,Light,Management,Mqtt,Power,Rules,Sensor,Serial,Setoption,Shutter,Rf,System,Timer,Wifi} ...]] - [--ignore-warnings] [-h] [-H] [-v] [-V] - - Backup/Restore Tasmota configuration data. Args that start with '--' - (eg. -f) can also be set in a config file (specified via -c). Config file - syntax allows: key=value, flag=true, stuff=[a,b,c] (for details, see syntax at - https://goo.gl/R74nmi). If an arg is specified in more than one place, then - commandline values override config file values which override defaults. - - Source: - Read/Write Tasmota configuration from/to - - -f, --file, --tasmota-file - file to retrieve/write Tasmota configuration from/to - (default: None)' - -d, --device, --host - hostname or IP address to retrieve/send Tasmota - configuration from/to (default: None) - -P, --port TCP/IP port number to use for the host connection - (default: 80) - -u, --username - host HTTP access username (default: admin) - -p, --password - host HTTP access password (default: None) - - Backup/Restore: - Backup & restore specification - - -i, --restore-file - file to restore configuration from (default: None). - Replacements: @v=firmware version from config, - @f=device friendly name from config, @h=device - hostname from config, @H=device hostname from device - (-d arg only) - -o, --backup-file - file to backup configuration to (default: None). - Replacements: @v=firmware version from config, - @f=device friendly name from config, @h=device - hostname from config, @H=device hostname from device - (-d arg only) - -t, --backup-type json|bin|dmp - backup filetype (default: 'json') - -E, --extension append filetype extension for -i and -o filename - (default) - -e, --no-extension do not append filetype extension, use -i and -o - filename as passed - -F, --force-restore force restore even configuration is identical - - JSON output: - JSON format specification - - --json-indent - pretty-printed JSON output using indent level - (default: 'None'). -1 disables indent. - --json-compact compact JSON output by eliminate whitespace - --json-hide-pw hide passwords - --json-show-pw, --json-unhide-pw - unhide passwords (default) - - Tasmota command output: - Tasmota command output format specification - - --cmnd-indent - Tasmota command grouping indent level (default: '2'). - 0 disables indent - --cmnd-groups group Tasmota commands (default) - --cmnd-nogroups leave Tasmota commands ungrouped - --cmnd-sort sort Tasmota commands (default) - --cmnd-unsort leave Tasmota commands unsorted - - Common: - Optional arguments - - -c, --config - program config file - can be used to set default - command args (default: None) - -S, --output display output regardsless of backup/restore usage - (default do not output on backup or restore usage) - -T, --output-format json|cmnd|command - display output format (default: 'json') - -g, --group {Control,Devices,Display,Domoticz,Internal,Knx,Light,Management,Mqtt,Power,Rules,Sensor,Serial,Setoption,Shutter,Rf,System,Timer,Wifi} - limit data processing to command groups (default no - filter) - --ignore-warnings do not exit on warnings. Not recommended, used by your - own responsibility! - - Info: - Extra information - - -h, --help show usage help message and exit - -H, --full-help show full help message and exit - -v, --verbose produce more output about what the program does - -V, --version show program's version number and exit - - Either argument -d or -f must be given. - - -Returns: - 0: successful - 1: restore skipped - 2: program argument error - 3: file not found - 4: data size mismatch - 5: data CRC error - 6: unsupported configuration version - 7: configuration file read error - 8: JSON file decoding error - 9: Restore file data error - 10: Device data download error - 11: Device data upload error - 20: python module missing - 21: Internal error - >21: python library exit code - 4xx, 5xx: HTTP errors - -""" - -class ExitCode: - OK = 0 - RESTORE_SKIPPED = 1 - ARGUMENT_ERROR = 2 - FILE_NOT_FOUND = 3 - DATA_SIZE_MISMATCH = 4 - DATA_CRC_ERROR = 5 - UNSUPPORTED_VERSION = 6 - FILE_READ_ERROR = 7 - JSON_READ_ERROR = 8 - RESTORE_DATA_ERROR = 9 - DOWNLOAD_CONFIG_ERROR = 10 - UPLOAD_CONFIG_ERROR = 11 - MODULE_NOT_FOUND = 20 - INTERNAL_ERROR = 21 - -# ====================================================================== -# imports -# ====================================================================== -import os.path -import io -import sys, platform -def ModuleImportError(module): - er = str(module) - print('{}, try "pip install {}"'.format(er,er.split(' ')[len(er.split(' '))-1]), file=sys.stderr) - sys.exit(ExitCode.MODULE_NOT_FOUND) -try: - from datetime import datetime - import time - import copy - import struct - import socket - import re - import math - import inspect - import json - import configargparse - import requests - if sys.version_info.major==2: - import urllib2 - else: - import urllib -except ImportError as e: - ModuleImportError(e) - -# ====================================================================== -# globals -# ====================================================================== -PROG='{} v{} by Norbert Richter '.format(os.path.basename(sys.argv[0]),VER) - -CONFIG_FILE_XOR = 0x5A -BINARYFILE_MAGIC = 0x63576223 -STR_ENCODING = 'utf8' -HIDDEN_PASSWORD = '********' -INTERNAL = 'Internal' - -DEFAULTS = { - 'source': - { - 'device': None, - 'port': 80, - 'username': 'admin', - 'password': None, - 'tasmotafile': None, - }, - 'backup': - { - 'restorefile': None, - 'backupfile': None, - 'backupfileformat': 'json', - 'extension': True, - 'forcerestore': False, - }, - 'jsonformat': - { - 'jsonindent': None, - 'jsoncompact': False, - 'jsonsort': True, - 'jsonhidepw': False, - }, - 'cmndformat': - { - 'cmndindent': 2, - 'cmndgroup': True, - 'cmndsort': True, - }, - 'common': - { - 'output': False, - 'outputformat': 'json', - 'configfile': None, - 'ignorewarning':False, - 'filter': None, - }, -} -args = {} -exitcode = 0 - - -# ====================================================================== -# Settings mapping -# ====================================================================== -""" -Settings dictionary describes the config file fields definition: - - = { : } - - : "string" - a python valid dictionary key (string) - - : ( , , [,] ) - a tuple containing the following items: - - : | - data type & format definition - : - defines the use of data at - format is defined in 'struct module format string' - see - https://docs.python.org/3.8/library/struct.html#format-strings - : - A dictionary describes a (sub)setting dictonary - and can recursively define another - - : | (, , ) - address definition - : - The address (starting from 0) within binary config data. - : - number of bits used (positive integer) - : - bit shift : - >= 0: shift the result right - < 0: shift the result left - - : | (, [,cmd]) - data definition - : None | | [] | [ ,...] - None: - Single value, not an array - : - [] - Defines a one-dimensional array of size - [ ,...] - Defines a one- or multi-dimensional array - : - value validation function - : (, ) - Tasmota command definition - : - command group string - : | (,...) - convert data into Tasmota command function - - : | (, ) - read/write converter - : None | - Will be used in Bin2Mapping to convert values read - from the binary data object into mapping dictionary - None - None indicates not read conversion - - to convert value from binary object to JSON. - : None | False | - Will be used in Mapping2Bin to convert values read - from mapping dictionary before write to binary - data object - None - None indicates not write conversion - False - False indicates the value is readonly and will - not be written into the binary object. - - to convert value from JSON back to binary object - - Common definitions - - : | | None - function to be called or string to evaluate: - : - A function name will be called with one or two parameter: - The value to be processed - (optional) the current array index (1,n) - - A string will be evaluate as is. The following - placeholder can be used to replace it by runtime values: - '$': - will be replaced by the mapping name value - '#': - will be replace by array index (if any) - '@': - can be used as reference to other mapping values - see definition below for examples - - : 'string' | "string" - characters enclosed in ' or " - - : integer - numbers in the range -2147483648 through 2147483647 - : unsigned integer - numbers in the range 0 through 4294967295 - -""" -# ---------------------------------------------------------------------- -# Settings helper -# ---------------------------------------------------------------------- -def passwordread(value): - return HIDDEN_PASSWORD if args.jsonhidepw else value -def passwordwrite(value): - return None if value == HIDDEN_PASSWORD else value -def bitsRead(x, n=0, c=1): - """ - Reads bit(s) of a number - - @param x: - the number from which to read - - @param n: - which bit position to read - - @param c: - how many bits to read (1 if omitted) - - @return: - the bit value(s) - """ - if isinstance(x,str): - x = int(x, 0) - if isinstance(x,str): - n = int(n, 0) - - if n >= 0: - x >>= n - else: - x <<= abs(n) - if c>0: - x &= (1<, , [,] - 'cfg_holder': ('0 and bitsRead($,0,11)>(12*60) else "",time=time.strftime("%H:%M",time.gmtime((bitsRead($,0,11) if bitsRead($,29,2)==0 else bitsRead($,0,11) if bitsRead($,0,11)<=(12*60) else bitsRead($,0,11)-(12*60))*60)),window=bitsRead($,11,4),repeat=bitsRead($,15),days="{:07b}".format(bitsRead($,16,7))[::-1],device=bitsRead($,23,4)+1,power=bitsRead($,27,2) )')), ('"0x{:08x}".format($)', False) ), - 'time': (' 0 and type_ is not None else '', - sstatus=status if status is not None and status > 0 else '', - scolon=': ' if type_ is not None or line is not None else '', - smgs=msg, - slineno=' (@{:04d})'.format(line) if line is not None else '') - , file=sys.stderr) - - -def exit(status=0, msg="end", type_=LogType.ERROR, src=None, doexit=True, line=None): - """ - Called when the program should be exit - - @param status: - the exit status program returns to callert - @param msg: - the msg logged before exit - @param type_: - msg type: 'INFO', 'WARNING' or 'ERROR' - @param doexit: - True to exit program, otherwise return - """ - - if src is not None: - msg = '{} ({})'.format(src, msg) - message(msg, type_=type_ if status!=ExitCode.OK else LogType.INFO, status=status, line=line) - exitcode = status - if doexit: - sys.exit(exitcode) - - -def debug(args): - """ - Get debug level - - @param args: - configargparse.parse_args() result - - @return: - debug level - """ - return 0 if args.debug is None else args.debug - - -def instance(type_): - """ - Creates Python2/3 compatible isinstance test type(s) - - @param args: - Python3 instance type - - @return: - Python2/3 compatible isinstance type(s) - """ - newtype = type_ - if sys.version_info.major==2: - if type_==str: - newtype = (str,unicode) - elif isinstance(type_, tuple) and str in type_: - newtype = newtype + (unicode,) - return newtype - - -def ShortHelp(doexit=True): - """ - Show short help (usage) only - ued by own -h handling - - @param doexit: - sys.exit with OK if True - """ - print(parser.description) - print - parser.print_usage() - print - print("For advanced help use '{prog} -H' or '{prog} --full-help'".format(prog=os.path.basename(sys.argv[0]))) - if doexit: - sys.exit(ExitCode.OK) - - -class CustomHelpFormatter(configargparse.HelpFormatter): - """ - Class for customizing the help output - """ - - def _format_action_invocation(self, action): - """ - Reformat multiple metavar output - -d , --device , --host - to single output - -d, --device, --host - """ - - orgstr = configargparse.HelpFormatter._format_action_invocation(self, action) - if orgstr and orgstr[0] != '-': # only optional arguments - return orgstr - res = getattr(action, '_formatted_action_invocation', None) - if res: - return res - - options = orgstr.split(', ') - if len(options) <= 1: - action._formatted_action_invocation = orgstr - return orgstr - - return_list = [] - for option in options: - meta = "" - arg = option.split(' ') - if len(arg) > 1: - meta = arg[1] - return_list.append(arg[0]) - if len(meta) > 0 and len(return_list) > 0: - return_list[len(return_list)-1] += " "+meta - action._formatted_action_invocation = ', '.join(return_list) - return action._formatted_action_invocation - - -# ====================================================================== -# Tasmota config data handling -# ====================================================================== -def GetTemplateSizes(): - """ - Get all possible template sizes as list - - @return: - template sizes as list [] - """ - sizes = [] - for cfg in Settings: - sizes.append(cfg[1]) - # return unique sizes only (remove duplicates) - return list(set(sizes)) - - -def GetTemplateSetting(decode_cfg): - """ - Search for version, size and settings to be used depending on given binary config data - - @param decode_cfg: - binary config data (decrypted) - - @return: - version, size, settings to use; None if version is invalid - """ - version = 0x0 - size = setting = None - version = GetField(decode_cfg, 'version', Setting_6_2_1['version'], raw=True) - # search setting definition top-down - for cfg in sorted(Settings, key=lambda s: s[0], reverse=True): - if version >= cfg[0]: - size = cfg[1] - setting = cfg[2] - break - - return version, size, setting - - -def GetGroupList(setting): - """ - Get all avilable group definition from setting - - @return: - configargparse.parse_args() result - """ - groups = set() - - for name in setting: - dev = setting[name] - format_, group = GetFieldDef(dev, fields="format_, group") - if group is not None and len(group) > 0: - groups.add(group.title()) - if isinstance(format_, dict): - subgroups = GetGroupList(format_) - if subgroups is not None and len(subgroups) > 0: - for group in subgroups: - groups.add(group.title()) - - groups=list(groups) - groups.sort() - return groups - - -class FileType: - FILE_NOT_FOUND = None - DMP = 'dmp' - JSON = 'json' - BIN = 'bin' - UNKNOWN = 'unknown' - INCOMPLETE_JSON = 'incomplete json' - INVALID_JSON = 'invalid json' - INVALID_BIN = 'invalid bin' - -def GetFileType(filename): - """ - Get the FileType class member of a given filename - - @param filename: - filename of the file to analyse - - @return: - FileType class member - """ - filetype = FileType.UNKNOWN - - # try filename - try: - isfile = os.path.isfile(filename) - try: - with open(filename, "r") as f: - try: - # try reading as json - inputjson = json.load(f) - if 'header' in inputjson: - filetype = FileType.JSON - else: - filetype = FileType.INCOMPLETE_JSON - except ValueError: - filetype = FileType.INVALID_JSON - # not a valid json, get filesize and compare it with all possible sizes - try: - size = os.path.getsize(filename) - except: - filetype = FileType.UNKNOWN - sizes = GetTemplateSizes() - - # size is one of a dmp file size - if size in sizes: - filetype = FileType.DMP - elif (size - ((len(hex(BINARYFILE_MAGIC))-2)/2)) in sizes: - # check if the binary file has the magic header - with open(filename, "rb") as inputfile: - inputbin = inputfile.read() - if struct.unpack_from('>24) & 0xff) - minor = ((version>>16) & 0xff) - release = ((version>> 8) & 0xff) - subrelease = (version & 0xff) - if major >= 6: - if subrelease > 0: - subreleasestr = str(subrelease) - else: - subreleasestr = '' - else: - if subrelease > 0: - subreleasestr = str(chr(subrelease+ord('a')-1)) - else: - subreleasestr = '' - return "{:d}.{:d}.{:d}{}{}".format( major, minor, release, '.' if (major >= 6 and subreleasestr != '') else '', subreleasestr) - - -def MakeFilename(filename, filetype, configmapping): - """ - Replace variables within a filename - - @param filename: - original filename possible containing replacements: - @v: - Tasmota version from config data - @f: - friendlyname from config data - @h: - hostname from config data - @H: - hostname from device (-d arg only) - @param filetype: - FileType.x object - creates extension if not None - @param configmapping: - binary config data (decrypted) - - @return: - New filename with replacements - """ - config_version = config_friendlyname = config_hostname = device_hostname = '' - - if 'version' in configmapping: - config_version = GetVersionStr( int(str(configmapping['version']), 0) ) - if 'friendlyname' in configmapping: - config_friendlyname = re.sub('[^0-9a-zA-Z]','_', configmapping['friendlyname'][0]) - if 'hostname' in configmapping: - if configmapping['hostname'].find('%') < 0: - config_hostname = re.sub('[^0-9a-zA-Z]','_', configmapping['hostname']) - if filename.find('@H') >= 0 and args.device is not None: - device_hostname = GetTasmotaHostname(args.device, args.port, username=args.username, password=args.password) - if device_hostname is None: - device_hostname = '' - - dirname = basename = ext = '' - - # split file parts - dirname = os.path.normpath(os.path.dirname(filename)) - basename = os.path.basename(filename) - name, ext = os.path.splitext(basename) - - # make a valid filename - try: - name = name.decode('unicode-escape').translate(dict((ord(char), None) for char in '\/*?:"<>|')) - except: - pass - name = str(name.replace(' ','_')) - - # append extension based on filetype if not given - if len(ext) and ext[0]=='.': - ext = ext[1:] - if filetype is not None and args.extension and (len(ext)<2 or all(c.isdigit() for c in ext)): - ext = filetype.lower() - - # join filename + extension - if len(ext): - name_ext = name+'.'+ext - else: - name_ext = name - - # join path and filename - try: - filename = os.path.join(dirname, name_ext) - except: - pass - - filename = filename.replace('@v', config_version) - filename = filename.replace('@f', config_friendlyname ) - filename = filename.replace('@h', config_hostname ) - filename = filename.replace('@H', device_hostname ) - - return filename - - -def MakeUrl(host, port=80, location=''): - """ - Create a Tasmota host url - - @param host: - hostname or IP of Tasmota host - @param port: - port number to use for http connection - @param location: - http url location - - @return: - Tasmota http url - """ - return "http://{shost}{sdelimiter}{sport}/{slocation}".format(\ - shost=host, - sdelimiter=':' if port != 80 else '', - sport=port if port != 80 else '', - slocation=location ) - - -def LoadTasmotaConfig(filename): - """ - Load config from Tasmota file - - @param filename: - filename to load - - @return: - binary config data (encrypted) or None on error - """ - - encode_cfg = None - - # read config from a file - if not os.path.isfile(filename): # check file exists - exit(ExitCode.FILE_NOT_FOUND, "File '{}' not found".format(filename),line=inspect.getlineno(inspect.currentframe())) - try: - with open(filename, "rb") as tasmotafile: - encode_cfg = tasmotafile.read() - except Exception as e: - exit(e.args[0], "'{}' {}".format(filename, e[1]),line=inspect.getlineno(inspect.currentframe())) - - return encode_cfg - - -def TasmotaGet(cmnd, host, port, username=DEFAULTS['source']['username'], password=None, contenttype = None): - """ - Tasmota http request - - @param host: - hostname or IP of Tasmota device - @param port: - http port of Tasmota device - @param username: - optional username for Tasmota web login - @param password - optional password for Tasmota web login - - @return: - binary config data (encrypted) or None on error - """ - - # read config direct from device via http - url = MakeUrl(host, port, cmnd) - auth = None - if username is not None and password is not None: - auth = (username, password) - res = requests.get(url, auth=auth) - - if not res.ok: - exit(res.status_code, "Error on http GET request for {} - {}".format(url,res.reason), line=inspect.getlineno(inspect.currentframe())) - - if contenttype is not None and res.headers['Content-Type']!=contenttype: - exit(ExitCode.DOWNLOAD_CONFIG_ERROR, "Device did not response properly, may be Tasmota webserver admin mode is disabled (WebServer 2)",line=inspect.getlineno(inspect.currentframe())) - - return res.status_code, res.content - - -def GetTasmotaHostname(host, port, username=DEFAULTS['source']['username'], password=None): - """ - Get Tasmota hostname from device - - @param host: - hostname or IP of Tasmota device - @param port: - http port of Tasmota device - @param username: - optional username for Tasmota web login - @param password - optional password for Tasmota web login - - @return: - Tasmota real hostname or None on error - """ - hostname = None - - loginstr = "" - if password is not None: - loginstr = "user={}&password={}&".format(urllib2.quote(username), urllib2.quote(password)) - # get hostname - responsecode, body = TasmotaGet("cm?{}cmnd=status%205".format(loginstr), host, port, username=username, password=password) - if body is not None: - jsonbody = json.loads(body) - if "StatusNET" in jsonbody and "Hostname" in jsonbody["StatusNET"]: - hostname = jsonbody["StatusNET"]["Hostname"] - if args.verbose: - message("Hostname for '{}' retrieved: '{}'".format(host, hostname), type_=LogType.INFO) - - return hostname - - -def PullTasmotaConfig(host, port, username=DEFAULTS['source']['username'], password=None): - """ - Pull config from Tasmota device - - @param host: - hostname or IP of Tasmota device - @param port: - http port of Tasmota device - @param username: - optional username for Tasmota web login - @param password - optional password for Tasmota web login - - @return: - binary config data (encrypted) or None on error - """ - responsecode, body = TasmotaGet('dl', host, port, username, password, contenttype='application/octet-stream') - - return body - - -def PushTasmotaConfig(encode_cfg, host, port, username=DEFAULTS['source']['username'], password=None): - """ - Upload binary data to a Tasmota host using http - - @param encode_cfg: - encrypted binary data or filename containing Tasmota encrypted binary config - @param host: - hostname or IP of Tasmota device - @param port: - http port of Tasmota device - @param username: - optional username for Tasmota web login - @param password - optional password for Tasmota web login - - @return - errorcode, errorstring - errorcode=0 if success, otherwise http response or exception code - """ - if isinstance(encode_cfg, (bytes,bytearray)): - encode_cfg = str(encode_cfg) - - # get restore config page first to set internal Tasmota vars - responsecode, body = TasmotaGet('rs?', host, port, username, password, contenttype='text/html') - if body is None: - return responsecode, "ERROR" - - # ~ # post data - url = MakeUrl(host, port, "u2") - auth = None - if username is not None and password is not None: - auth = (username, password) - files = {'u2':('{sprog}_v{sver}.dmp'.format(sprog=os.path.basename(sys.argv[0]), sver=VER), encode_cfg)} - res = requests.post(url, auth=auth, files=files) - - if not res.ok: - exit(res.status_code, "Error on http POST request for {} - {}".format(url,res.reason), line=inspect.getlineno(inspect.currentframe())) - - if res.headers['Content-Type']!='text/html': - exit(ExitCode.DOWNLOAD_CONFIG_ERROR, "Device did not response properly, may be Tasmota webserver admin mode is disabled (WebServer 2)",line=inspect.getlineno(inspect.currentframe())) - - body = res.content - - findUpload = body.find("Upload") - if findUpload < 0: - return ExitCode.UPLOAD_CONFIG_ERROR, "Device did not response properly with upload result page" - - body = body[findUpload:] - findSuccessful = body.find("Successful") - if findSuccessful < 0: - errmatch = re.search("(\S*)

(.*)
", body) - reason = "Unknown error" - if errmatch and len(errmatch.groups()) > 1: - reason = errmatch.group(2) - return ExitCode.UPLOAD_CONFIG_ERROR, reason - - return 0, 'OK' - - -def DecryptEncrypt(obj): - """ - Decrpt/Encrypt binary config data - - @param obj: - binary config data - - @return: - decrypted configuration (if obj contains encrypted data) - """ - if isinstance(obj, (bytes,bytearray)): - obj = str(obj) - dobj = obj[0:2] - for i in range(2, len(obj)): - dobj += chr( (ord(obj[i]) ^ (CONFIG_FILE_XOR +i)) & 0xff ) - return dobj - - -def GetSettingsCrc(dobj): - """ - Return binary config data calclulated crc - - @param dobj: - decrypted binary config data - - @return: - 2 byte unsigned integer crc value - - """ - if isinstance(dobj, (bytes,bytearray)): - dobj = str(dobj) - version, size, setting = GetTemplateSetting(dobj) - if version < 0x06060007 or version > 0x0606000A: - size = 3584 - crc = 0 - for i in range(0, size): - if not i in [14,15]: # Skip crc - byte = ord(dobj[i]) - crc += byte * (i+1) - - return crc & 0xffff - - -def GetSettingsCrc32(dobj): - """ - Return binary config data calclulated crc32 - - @param dobj: - decrypted binary config data - - @return: - 4 byte unsigned integer crc value - - """ - if isinstance(dobj, (bytes,bytearray)): - dobj = str(dobj) - crc = 0 - for i in range(0, len(dobj)-4): - crc ^= ord(dobj[i]) - for j in range(0, 8): - crc = (crc >> 1) ^ (-int(crc & 1) & 0xEDB88320); - - return ~crc & 0xffffffff - - -def GetFieldDef(fielddef, fields="format_, addrdef, baseaddr, bits, bitshift, datadef, arraydef, validate, cmd, group, tasmotacmnd, converter, readconverter, writeconverter"): - - """ - Get field definition items - - @param fielddef: - field format - see "Settings dictionary" above - @param fields: - comma separated string list of values to be returned - possible values see fields default - - @return: - set of values defined in - """ - format_ = addrdef = baseaddr = datadef = arraydef = validate = cmd = group = tasmotacmnd = converter = readconverter = writeconverter = None - bits = bitshift = 0 - - # calling with nothing is wrong - if fielddef is None: - print(' is None', file=sys.stderr) - raise SyntaxError(' error') - - # get top level items - if len(fielddef) == 3: - # converter not present - format_, addrdef, datadef = fielddef - elif len(fielddef) == 4: - # converter present - format_, addrdef, datadef, converter = fielddef - else: - print('wrong {} length ({}) in setting'.format(fielddef, len(fielddef)), file=sys.stderr) - raise SyntaxError(' error') - - # ignore calls with 'root' setting - if isinstance(format_, instance(dict)) and baseaddr is None and datadef is None: - return eval(fields) - - if not isinstance(format_, instance((str,dict))): - print('wrong {} type {} in {}'.format(format_, type(format_), fielddef), file=sys.stderr) - raise SyntaxError(' error') - - # extract addrdef items - baseaddr = addrdef - if isinstance(baseaddr, instance((list,tuple))): - if len(baseaddr) == 3: - # baseaddr bit definition - baseaddr, bits, bitshift = baseaddr - if not isinstance(bits, instance(int)): - print(' must be defined as integer in {}'.format(bits, fielddef), file=sys.stderr) - raise SyntaxError(' error') - if not isinstance(bitshift, instance(int)): - print(' must be defined as integer in {}'.format(bitshift, fielddef), file=sys.stderr) - raise SyntaxError(' error') - else: - print('wrong {} length ({}) in {}'.format(addrdef, len(addrdef), fielddef), file=sys.stderr) - raise SyntaxError(' error') - if not isinstance(baseaddr, instance(int)): - print(' must be defined as integer in {}'.format(baseaddr, fielddef), file=sys.stderr) - raise SyntaxError(' error') - - # extract datadef items - arraydef = datadef - if isinstance(datadef, instance((tuple))): - if len(datadef) == 2: - # datadef has a validator - arraydef, validate = datadef - elif len(datadef) == 3: - # datadef has a validator and cmd set - arraydef, validate, cmd = datadef - # cmd must be a tuple with 2 objects - if isinstance(cmd, instance((tuple))) and len(cmd) == 2: - group, tasmotacmnd = cmd - if group is not None and not isinstance(group, instance(str)): - print('wrong {} in {}'.format(group, fielddef), file=sys.stderr) - raise SyntaxError(' error') - if tasmotacmnd is isinstance(tasmotacmnd, instance(tuple)): - tasmotacmnds = tasmotacmnd - for tasmotacmnd in tasmotacmnds: - if tasmotacmnd is not None and not callable(tasmotacmnd) and not isinstance(tasmotacmnd, instance(str)): - print('wrong {} in {}'.format(tasmotacmnd, fielddef), file=sys.stderr) - raise SyntaxError(' error') - else: - if tasmotacmnd is not None and not callable(tasmotacmnd) and not isinstance(tasmotacmnd, instance(str)): - print('wrong {} in {}'.format(tasmotacmnd, fielddef), file=sys.stderr) - raise SyntaxError(' error') - else: - print('wrong {} length ({}) in {}'.format(cmd, len(cmd), fielddef), file=sys.stderr) - raise SyntaxError(' error') - else: - print('wrong {} length ({}) in {}'.format(datadef, len(datadef), fielddef), file=sys.stderr) - raise SyntaxError(' error') - - if validate is not None and (not isinstance(validate, instance(str)) and not callable(validate)): - print('wrong {} type {} in {}'.format(validate, type(validate), fielddef), file=sys.stderr) - raise SyntaxError(' error') - - # convert single int into one-dimensional list - if isinstance(arraydef, instance(int)): - arraydef = [arraydef] - - if arraydef is not None and not isinstance(arraydef, instance((list))): - print('wrong {} type {} in {}'.format(arraydef, type(arraydef), fielddef), file=sys.stderr) - raise SyntaxError(' error') - - # get read/write converter items - readconverter = converter - if isinstance(converter, instance((tuple))): - if len(converter) == 2: - # converter has read/write converter - readconverter, writeconverter = converter - if readconverter is not None and not isinstance(readconverter, instance(str)) and not callable(readconverter): - print('wrong {} type {} in {}'.format(readconverter, type(readconverter), fielddef), file=sys.stderr) - raise SyntaxError(' error') - if writeconverter is not None and (not isinstance(writeconverter, instance((bool,str))) and not callable(writeconverter)): - print('wrong {} type {} in {}'.format(writeconverter, type(writeconverter), fielddef), file=sys.stderr) - raise SyntaxError(' error') - else: - print('wrong {} length ({}) in {}'.format(converter, len(converter), fielddef), file=sys.stderr) - raise SyntaxError(' error') - - - return eval(fields) - - -def ReadWriteConverter(value, fielddef, read=True, raw=False): - """ - Convert field value based on field desc - - @param value: - original value - @param fielddef - field definition - see "Settings dictionary" above - @param read - use read conversion if True, otherwise use write conversion - @param raw - return raw values (True) or converted values (False) - - @return: - (un)converted value - """ - converter, readconverter, writeconverter = GetFieldDef(fielddef, fields='converter, readconverter, writeconverter') - - # call password functions even if raw value should be processed - if read and callable(readconverter) and readconverter == passwordread: - raw = False - if not read and callable(writeconverter) and writeconverter == passwordwrite: - raw = False - - if not raw and converter is not None: - conv = readconverter if read else writeconverter - try: - if isinstance(conv, instance(str)): # evaluate strings - return eval(conv.replace('$','value')) - elif callable(conv): # use as format function - return conv(value) - except Exception as e: - exit(e.args[0], e.args[1], type_=LogType.WARNING, line=inspect.getlineno(inspect.currentframe())) - - return value - - -def CmndConverter(valuemapping, value, idx, fielddef): - """ - Convert field value into Tasmota command if available - - @param valuemapping: - data mapping - @param value: - original value - @param fielddef - field definition - see "Settings dictionary" above - - @return: - converted value, list of values or None if unable to convert - """ - converter, readconverter, writeconverter, group, tasmotacmnd = GetFieldDef(fielddef, fields='converter, readconverter, writeconverter, group, tasmotacmnd') - - result = None - - if (callable(readconverter) and readconverter == passwordread) or (callable(writeconverter) and writeconverter == passwordwrite): - if value == HIDDEN_PASSWORD: - return None - else: - result = value - - if tasmotacmnd is not None and (callable(tasmotacmnd) or len(tasmotacmnd) > 0): - if idx is not None: - idx += 1 - if isinstance(tasmotacmnd, instance(str)): # evaluate strings - if idx is not None: - evalstr = tasmotacmnd.replace('$','value').replace('#','idx').replace('@','valuemapping') - else: - evalstr = tasmotacmnd.replace('$','value').replace('@','valuemapping') - result = eval(evalstr) - - elif callable(tasmotacmnd): # use as format function - if idx is not None: - result = tasmotacmnd(value, idx) - else: - result = tasmotacmnd(value) - - return result - - -def ValidateValue(value, fielddef): - """ - Validate a value if validator is defined in fielddef - - @param value: - original value - @param fielddef - field definition - see "Settings dictionary" above - - @return: - True if value is valid, False if invalid - """ - validate = GetFieldDef(fielddef, fields='validate') - - if value == 0: - # can not complete all validate condition - # some Tasmota values are not allowed to be 0 on input - # even though these values are set to 0 on Tasmota initial. - # so we can't validate 0 values - return True; - - valid = True - try: - if isinstance(validate, instance(str)): # evaluate strings - valid = eval(validate.replace('$','value')) - elif callable(validate): # use as format function - valid = validate(value) - except: - valid = False - - return valid - - -def GetFormatCount(format_): - """ - Get format prefix count - - @param format_: - format specifier - - @return: - prefix count or 1 if not specified - """ - - if isinstance(format_, instance(str)): - match = re.search("\s*(\d+)", format_) - if match: - return int(match.group(0)) - - return 1 - - -def GetFormatType(format_): - """ - Get format type and bitsize without prefix - - @param format_: - format specifier - - @return: - (format_, 0) or (format without prefix, bitsize) - """ - - formattype = format_ - bitsize = 0 - if isinstance(format_, instance(str)): - match = re.search("\s*(\D+)", format_) - if match: - formattype = match.group(0) - bitsize = struct.calcsize(formattype) * 8 - return formattype, bitsize - - -def GetFieldMinMax(fielddef): - """ - Get minimum, maximum of field based on field format definition - - @param fielddef: - field format - see "Settings dictionary" above - - @return: - min, max - """ - minmax = {'c': (0, 0xff), - '?': (0, 1), - 'b': (~0x7f, 0x7f), - 'B': (0, 0xff), - 'h': (~0x7fff, 0x7fff), - 'H': (0, 0xffff), - 'i': (~0x7fffffff, 0x7fffffff), - 'I': (0, 0xffffffff), - 'l': (~0x7fffffff, 0x7fffffff), - 'L': (0, 0xffffffff), - 'q': (~0x7fffffffffffffff, 0x7fffffffffffffff), - 'Q': (0, 0x7fffffffffffffff), - 'f': (sys.float_info.min, sys.float_info.max), - 'd': (sys.float_info.min, sys.float_info.max), - } - format_ = GetFieldDef(fielddef, fields='format_') - min_ = 0 - max_ = 0 - - if format_[-1:] in minmax: - min_, max_ = minmax[format_[-1:]] - max_ *= GetFormatCount(format_) - elif format_[-1:] in ['s','p']: - # s and p may have a prefix as length - max_ = GetFormatCount(format_) - - return min_,max_ - - -def GetFieldLength(fielddef): - """ - Get length of a field in bytes based on field format definition - - @param fielddef: - field format - see "Settings dictionary" above - - @return: - length of field in bytes - """ - - length=0 - format_, addrdef, arraydef = GetFieldDef(fielddef, fields='format_, addrdef, arraydef') - - # contains a integer list - if isinstance(arraydef, instance(list)) and len(arraydef) > 0: - # arraydef contains a list - # calc size recursive by sum of all elements - for i in range(0, arraydef[0]): - subfielddef = GetSubfieldDef(fielddef) - if len(arraydef) > 1: - length += GetFieldLength( (format_, addrdef, subfielddef) ) - # single array - else: - length += GetFieldLength( (format_, addrdef, None) ) - - elif isinstance(format_, instance(dict)): - # -> iterate through format - addr = None - setting = format_ - for name in setting: - baseaddr, bits, bitshift = GetFieldDef(setting[name], fields='baseaddr, bits, bitshift') - _len = GetFieldLength(setting[name]) - if addr != baseaddr: - addr = baseaddr - length += _len - - # a simple value - elif isinstance(format_, instance(str)): - length = struct.calcsize(format_) - - return length - - -def GetSubfieldDef(fielddef): - """ - Get subfield definition from a given field definition - - @param fielddef: - see Settings desc above - - @return: - subfield definition - """ - - format_, addrdef, datadef, arraydef, validate, cmd, converter = GetFieldDef(fielddef, fields='format_, addrdef, datadef, arraydef, validate, cmd, converter') - - # create new arraydef - if len(arraydef) > 1: - arraydef = arraydef[1:] - else: - arraydef = None - - # create new datadef - if isinstance(datadef, instance(tuple)): - if cmd is not None: - datadef = (arraydef, validate, cmd) - else: - datadef = (arraydef, validate) - else: - datadef = arraydef - - # set new field def - subfielddef = None - if converter is not None: - subfielddef = (format_, addrdef, datadef, converter) - else: - subfielddef = (format_, addrdef, datadef) - - return subfielddef - - -def IsFilterGroup(group): - """ - Check if group is valid on filter - - @param grooup: - group name to check - - @return: - True if group is in filter, otherwise False - """ - - if args.filter is not None: - if group is None: - return False - if group == '*': - return True - if group.title() != INTERNAL.title() and group.title() not in (groupname.title() for groupname in args.filter): - return False - return True - - -def GetFieldValue(fielddef, dobj, addr): - """ - Get single field value from definition - - @param fielddef: - see Settings desc - @param dobj: - decrypted binary config data - @param addr - addr within dobj - - @return: - value read from dobj - """ - - format_, bits, bitshift = GetFieldDef(fielddef, fields='format_, bits, bitshift') - - value_ = 0 - unpackedvalue = struct.unpack_from(format_, dobj, addr) - singletype, bitsize = GetFormatType(format_) - - if not format_[-1:].lower() in ['s','p']: - for val in unpackedvalue: - value_ <<= bitsize - value_ = value_ + val - value_ = bitsRead(value_, bitshift, bits) - else: - value_ = unpackedvalue[0] - s = str(value_).split('\0')[0] # use left string until \0 - value_ = unicode(s, errors='ignore') # remove character > 127 - - return value_ - - -def SetFieldValue(fielddef, dobj, addr, value): - """ - Set single field value from definition - - @param fielddef: - see Settings desc - @param dobj: - decrypted binary config data - @param addr - addr within dobj - @param value - new value - - @return: - new decrypted binary config data - """ - - format_, bits, bitshift = GetFieldDef(fielddef, fields='format_, bits, bitshift') - formatcnt = GetFormatCount(format_) - singletype, bitsize = GetFormatType(format_) - if debug(args) >= 2: - print("SetFieldValue(): fielddef {}, addr 0x{:04x} value {} formatcnt {} singletype {} bitsize {} ".format(fielddef,addr,value,formatcnt,singletype,bitsize), file=sys.stderr) - if not format_[-1:].lower() in ['s','p']: - addr += (bitsize / 8) * formatcnt - for _ in range(0, formatcnt): - addr -= (bitsize / 8) - maxunsigned = ((2**bitsize) - 1) - maxsigned = ((2**bitsize)>>1)-1 - val = value & maxunsigned - if isinstance(value,instance(int)) and value < 0 and val > maxsigned: - val = ((maxunsigned+1)-val) * (-1) - if debug(args) >= 3: - print("SetFieldValue(): Single type - fielddef {}, addr 0x{:04x} value {} singletype {} bitsize {}".format(fielddef,addr,val,singletype,bitsize), file=sys.stderr) - try: - struct.pack_into(singletype, dobj, addr, val) - except struct.error as e: - exit(ExitCode.RESTORE_DATA_ERROR, - "Single type {} [fielddef={}, addr=0x{:04x}, value={}] - skipped!".format(e,fielddef,addr,val), - type_=LogType.WARNING, - doexit=not args.ignorewarning, - line=inspect.getlineno(inspect.currentframe())) - value >>= bitsize - else: - if debug(args) >= 3: - print("SetFieldValue(): String type - fielddef {}, addr 0x{:04x} value {} format_ {}".format(fielddef,addr,value,format_), file=sys.stderr) - try: - struct.pack_into(format_, dobj, addr, value) - except struct.error as e: - exit(ExitCode.RESTORE_DATA_ERROR, - "String type {} [fielddef={}, addr=0x{:04x}, value={}} - skipped!".format(e,fielddef,addr,value), - type_=LogType.WARNING, - doexit=not args.ignorewarning, - line=inspect.getlineno(inspect.currentframe())) - - return dobj - - -def GetField(dobj, fieldname, fielddef, raw=False, addroffset=0): - """ - Get field value from definition - - @param dobj: - decrypted binary config data - @param fieldname: - name of the field - @param fielddef: - see Settings desc above - @param raw - return raw values (True) or converted values (False) - @param addroffset - use offset for baseaddr (used for recursive calls) - - @return: - field mapping - """ - - if isinstance(dobj, instance((bytes,bytearray))): - dobj = str(dobj) - - valuemapping = None - - # get field definition - format_, baseaddr, bits, bitshift, arraydef, group, tasmotacmnd = GetFieldDef(fielddef, fields='format_, baseaddr, bits, bitshift, arraydef, group, tasmotacmnd') - - # filter groups - if not IsFilterGroup(group): - return valuemapping - - # contains a integer list - if isinstance(arraydef, instance(list)) and len(arraydef) > 0: - valuemapping = [] - offset = 0 - for i in range(0, arraydef[0]): - subfielddef = GetSubfieldDef(fielddef) - length = GetFieldLength(subfielddef) - if length != 0: - value = GetField(dobj, fieldname, subfielddef, raw=raw, addroffset=addroffset+offset) - valuemapping.append(value) - offset += length - - # contains a dict - elif isinstance(format_, instance(dict)): - mapping_value = {} - # -> iterate through format - for name in format_: - value = None - value = GetField(dobj, name, format_[name], raw=raw, addroffset=addroffset) - if value is not None: - mapping_value[name] = value - # copy complete returned mapping - valuemapping = copy.deepcopy(mapping_value) - - # a simple value - elif isinstance(format_, instance((str, bool, int, float, long))): - if GetFieldLength(fielddef) != 0: - valuemapping = ReadWriteConverter(GetFieldValue(fielddef, dobj, baseaddr+addroffset), fielddef, read=True, raw=raw) - - else: - exit(ExitCode.INTERNAL_ERROR, "Wrong mapping format definition: '{}'".format(format_), type_=LogType.WARNING, doexit=not args.ignorewarning, line=inspect.getlineno(inspect.currentframe())) - - return valuemapping - - -def SetField(dobj, fieldname, fielddef, restore, addroffset=0, filename=""): - """ - Get field value from definition - - @param dobj: - decrypted binary config data - @param fieldname: - name of the field - @param fielddef: - see Settings desc above - @param restore - restore mapping with the new value(s) - @param addroffset - use offset for baseaddr (used for recursive calls) - @param filename - related filename (for messages only) - - @return: - new decrypted binary config data - """ - format_, baseaddr, bits, bitshift, arraydef, group, writeconverter = GetFieldDef(fielddef, fields='format_, baseaddr, bits, bitshift, arraydef, group, writeconverter') - # cast unicode - fieldname = str(fieldname) - - # filter groups - if not IsFilterGroup(group): - return dobj - - # do not write readonly values - if writeconverter is False: - if debug(args) >= 2: - print("SetField(): Readonly '{}' using '{}'/{}{} @{} skipped".format(fieldname, format_, arraydef, bits, hex(baseaddr+addroffset)), file=sys.stderr) - return dobj - - # contains a list - if isinstance(arraydef, instance(list)) and len(arraydef) > 0: - offset = 0 - if len(restore) > arraydef[0]: - exit(ExitCode.RESTORE_DATA_ERROR, "file '{sfile}', array '{sname}[{selem}]' exceeds max number of elements [{smax}]".format(sfile=filename, sname=fieldname, selem=len(restore), smax=arraydef[0]), type_=LogType.WARNING, doexit=not args.ignorewarning, line=inspect.getlineno(inspect.currentframe())) - for i in range(0, arraydef[0]): - subfielddef = GetSubfieldDef(fielddef) - length = GetFieldLength(subfielddef) - if length != 0: - if i >= len(restore): # restore data list may be shorter than definition - break - subrestore = restore[i] - dobj = SetField(dobj, fieldname, subfielddef, subrestore, addroffset=addroffset+offset, filename=filename) - offset += length - - # contains a dict - elif isinstance(format_, instance(dict)): - for name in format_: # -> iterate through format - if name in restore: - dobj = SetField(dobj, name, format_[name], restore[name], addroffset=addroffset, filename=filename) - - # a simple value - elif isinstance(format_, instance((str, bool, int, float, long))): - valid = True - err = "" - errformat = "" - - min_, max_ = GetFieldMinMax(fielddef) - value = _value = None - skip = False - - # simple char value - if format_[-1:] in ['c']: - try: - value = ReadWriteConverter(restore.encode(STR_ENCODING)[0], fielddef, read=False) - except Exception as e: - exit(e.args[0], e.args[1], type_=LogType.WARNING, line=inspect.getlineno(inspect.currentframe())) - valid = False - - # bool - elif format_[-1:] in ['?']: - try: - value = ReadWriteConverter(bool(restore), fielddef, read=False) - except Exception as e: - exit(e.args[0], e.args[1], type_=LogType.WARNING, line=inspect.getlineno(inspect.currentframe())) - valid = False - - # integer - elif format_[-1:] in ['b','B','h','H','i','I','l','L','q','Q','P']: - value = ReadWriteConverter(restore, fielddef, read=False) - if isinstance(value, instance(str)): - value = int(value, 0) - else: - value = int(value) - # bits - if bits != 0: - bitvalue = value - value = struct.unpack_from(format_, dobj, baseaddr+addroffset)[0] - # validate restore value - valid = ValidateValue(bitvalue, fielddef) - if not valid: - err = "valid bit range exceeding" - value = bitvalue - else: - mask = (1< mask: - min_ = 0 - max_ = mask - _value = bitvalue - valid = False - else: - if bitshift >= 0: - bitvalue <<= bitshift - mask <<= bitshift - else: - bitvalue >>= abs(bitshift) - mask >>= abs(bitshift) - v=value - value &= (0xffffffff ^ mask) - value |= bitvalue - - # full size values - else: - # validate restore function - valid = ValidateValue(value, fielddef) - if not valid: - err = "valid range exceeding" - _value = value - - # float - elif format_[-1:] in ['f','d']: - try: - value = ReadWriteConverter(float(restore), fielddef, read=False) - except: - valid = False - - # string - elif format_[-1:] in ['s','p']: - value = ReadWriteConverter(restore.encode(STR_ENCODING), fielddef, read=False) - err = "string length exceeding" - if value is not None: - max_ -= 1 - valid = min_ <= len(value) <= max_ - else: - skip = True - valid = True - - if value is None and not skip: - # None is an invalid value - valid = False - - if valid is None and not skip: - # validate against object type size - valid = min_ <= value <= max_ - if not valid: - err = "type range exceeding" - errformat = " [{smin},{smax}]" - - if _value is None: - # copy value before possible change below - _value = value - - if isinstance(_value, instance(str)): - _value = "'{}'".format(_value) - - if valid: - if not skip: - if debug(args) >= 2: - sbits = " {} bits shift {}".format(bits, bitshift) if bits else "" - strvalue = "{} [{}]".format(_value, hex(value)) if isinstance(_value, instance(int)) else _value - print("SetField(): Set '{}' using '{}'/{}{} @{} to {}".format(fieldname, format_, arraydef, sbits, hex(baseaddr+addroffset), strvalue), file=sys.stderr) - if fieldname != 'cfg_crc' and fieldname != '_': - prevvalue = GetFieldValue(fielddef, dobj, baseaddr+addroffset) - dobj = SetFieldValue(fielddef, dobj, baseaddr+addroffset, value) - curvalue = GetFieldValue(fielddef, dobj, baseaddr+addroffset) - if prevvalue != curvalue and args.verbose: - message("Value for '{}' changed from {} to {}".format(fieldname, prevvalue, curvalue), type_=LogType.INFO) - else: - if debug(args) >= 2: - print("SetField(): Special field '{}' using '{}'/{}{} @{} skipped".format(fieldname, format_, arraydef, bits, hex(baseaddr+addroffset)), file=sys.stderr) - else: - sformat = "file '{sfile}' - {{'{sname}': {svalue}}} ({serror})"+errformat - exit(ExitCode.RESTORE_DATA_ERROR, sformat.format(sfile=filename, sname=fieldname, serror=err, svalue=_value, smin=min_, smax=max_), type_=LogType.WARNING, doexit=not args.ignorewarning) - - return dobj - - -def SetCmnd(cmnds, fieldname, fielddef, valuemapping, mappedvalue, addroffset=0, idx=None): - """ - Get field value from definition - - @param cmnds: - Tasmota command mapping: { 'group': ['cmnd' <,'cmnd'...>] ... } - @param fieldname: - name of the field - @param fielddef: - see Settings desc above - @param valuemapping: - data mapping - @param mappedvalue - mappedvalue mapping with the new value(s) - @param addroffset - use offset for baseaddr (used for recursive calls) - @param idx - optional array index - - @return: - new Tasmota command mapping - """ - format_, baseaddr, bits, bitshift, arraydef, group, tasmotacmnd, writeconverter = GetFieldDef(fielddef, fields='format_, baseaddr, bits, bitshift, arraydef, group, tasmotacmnd, writeconverter') - - # cast unicode - fieldname = str(fieldname) - - # filter groups - if not IsFilterGroup(group): - return cmnds - - # contains a list - if isinstance(arraydef, instance(list)) and len(arraydef) > 0: - offset = 0 - if len(mappedvalue) > arraydef[0]: - exit(ExitCode.RESTORE_DATA_ERROR, "array '{sname}[{selem}]' exceeds max number of elements [{smax}]".format(sname=fieldname, selem=len(mappedvalue), smax=arraydef[0]), type_=LogType.WARNING, doexit=not args.ignorewarning, line=inspect.getlineno(inspect.currentframe())) - for i in range(0, arraydef[0]): - subfielddef = GetSubfieldDef(fielddef) - length = GetFieldLength(subfielddef) - if length != 0: - if i >= len(mappedvalue): # mappedvalue data list may be shorter than definition - break - subrestore = mappedvalue[i] - cmnds = SetCmnd(cmnds, fieldname, subfielddef, valuemapping, subrestore, addroffset=addroffset+offset, idx=i) - offset += length - - # contains a dict - elif isinstance(format_, instance(dict)): - for name in format_: # -> iterate through format - if name in mappedvalue: - cmnds = SetCmnd(cmnds, name, format_[name], valuemapping, mappedvalue[name], addroffset=addroffset, idx=idx) - - # a simple value - elif isinstance(format_, instance((str, bool, int, float, long))): - if group is not None: - group = group.title(); - if isinstance(tasmotacmnd, instance(tuple)): - tasmotacmnds = tasmotacmnd - for tasmotacmnd in tasmotacmnds: - cmnd = CmndConverter(valuemapping, mappedvalue, idx, fielddef) - if group is not None and cmnd is not None: - if group not in cmnds: - cmnds[group] = [] - if isinstance(cmnd, instance(list)): - for c in cmnd: - cmnds[group].append(c) - else: - cmnds[group].append(cmnd) - else: - cmnd = CmndConverter(valuemapping, mappedvalue, idx, fielddef) - if group is not None and cmnd is not None: - if group not in cmnds: - cmnds[group] = [] - if isinstance(cmnd, instance(list)): - for c in cmnd: - cmnds[group].append(c) - else: - cmnds[group].append(cmnd) - - return cmnds - - -def Bin2Mapping(decode_cfg): - """ - Decodes binary data stream into pyhton mappings dict - - @param decode_cfg: - binary config data (decrypted) - - @return: - valuemapping data as mapping dictionary - """ - if isinstance(decode_cfg, instance((bytes,bytearray))): - decode_cfg = str(decode_cfg) - - # get binary header and template to use - version, size, setting = GetTemplateSetting(decode_cfg) - - # if we did not found a mathching setting - if setting is None: - exit(ExitCode.UNSUPPORTED_VERSION, "Tasmota configuration version {} not supported".format(version),line=inspect.getlineno(inspect.currentframe())) - - if 'version' in setting: - cfg_version = GetField(decode_cfg, 'version', setting['version'], raw=True) - - # check size if exists - if 'cfg_size' in setting: - cfg_size = GetField(decode_cfg, 'cfg_size', setting['cfg_size'], raw=True) - # read size should be same as definied in setting - if cfg_size > size: - # may be processed - exit(ExitCode.DATA_SIZE_MISMATCH, "Number of bytes read does ot match - read {}, expected {} byte".format(cfg_size, size), type_=LogType.ERROR,line=inspect.getlineno(inspect.currentframe())) - elif cfg_size < size: - # less number of bytes can not be processed - exit(ExitCode.DATA_SIZE_MISMATCH, "Number of bytes read to small to process - read {}, expected {} byte".format(cfg_size, size), type_=LogType.ERROR,line=inspect.getlineno(inspect.currentframe())) - - # check crc if exists - if 'cfg_crc' in setting: - cfg_crc = GetField(decode_cfg, 'cfg_crc', setting['cfg_crc'], raw=True) - else: - cfg_crc = GetSettingsCrc(decode_cfg) - if 'cfg_crc32' in setting: - cfg_crc32 = GetField(decode_cfg, 'cfg_crc32', setting['cfg_crc32'], raw=True) - else: - cfg_crc32 = GetSettingsCrc32(decode_cfg) - if version < 0x0606000B: - if cfg_crc != GetSettingsCrc(decode_cfg): - exit(ExitCode.DATA_CRC_ERROR, 'Data CRC error, read 0x{:4x} should be 0x{:4x}'.format(cfg_crc, GetSettingsCrc(decode_cfg)), type_=LogType.WARNING, doexit=not args.ignorewarning,line=inspect.getlineno(inspect.currentframe())) - else: - if cfg_crc32 != GetSettingsCrc32(decode_cfg): - exit(ExitCode.DATA_CRC_ERROR, 'Data CRC32 error, read 0x{:8x} should be 0x{:8x}'.format(cfg_crc32, GetSettingsCrc32(decode_cfg)), type_=LogType.WARNING, doexit=not args.ignorewarning,line=inspect.getlineno(inspect.currentframe())) - - # get valuemapping - valuemapping = GetField(decode_cfg, None, (setting,0,(None, None, (INTERNAL, None)))) - - # add header info - timestamp = datetime.now() - valuemapping['header'] = { 'timestamp':timestamp.strftime("%Y-%m-%d %H:%M:%S"), - 'format': { - 'jsonindent': args.jsonindent, - 'jsoncompact': args.jsoncompact, - 'jsonsort': args.jsonsort, - 'jsonhidepw': args.jsonhidepw, - }, - 'template': { - 'version': hex(version), - 'crc': hex(cfg_crc), - }, - 'data': { - 'crc': hex(GetSettingsCrc(decode_cfg)), - 'size': len(decode_cfg), - }, - 'script': { - 'name': os.path.basename(__file__), - 'version': VER, - }, - 'os': (platform.machine(), platform.system(), platform.release(), platform.version(), platform.platform()), - 'python': platform.python_version(), - } - if 'cfg_crc' in setting: - valuemapping['header']['template'].update({'size': cfg_size}) - if 'cfg_crc32' in setting: - valuemapping['header']['template'].update({'crc32': hex(cfg_crc32)}) - valuemapping['header']['data'].update({'crc32': hex(GetSettingsCrc32(decode_cfg))}) - if 'version' in setting: - valuemapping['header']['data'].update({'version': hex(cfg_version)}) - - return valuemapping - - -def Mapping2Bin(decode_cfg, jsonconfig, filename=""): - """ - Encodes into binary data stream - - @param decode_cfg: - binary config data (decrypted) - @param jsonconfig: - restore data mapping - @param filename: - name of the restore file (for error output only) - - @return: - changed binary config data (decrypted) or None on error - """ - if isinstance(decode_cfg, instance(str)): - decode_cfg = bytearray(decode_cfg) - - - # get binary header data to use the correct version template from device - version, size, setting = GetTemplateSetting(decode_cfg) - - # make empty binarray array - _buffer = bytearray() - # add data - _buffer.extend(decode_cfg) - - if setting is not None: - # iterate through restore data mapping - for name in jsonconfig: - # key must exist in both dict - if name in setting: - SetField(_buffer, name, setting[name], jsonconfig[name], addroffset=0, filename=filename) - else: - if name != 'header': - exit(ExitCode.RESTORE_DATA_ERROR, "Restore file '{}' contains obsolete name '{}', skipped".format(filename, name), type_=LogType.WARNING, doexit=not args.ignorewarning) - - if 'cfg_crc' in setting: - crc = GetSettingsCrc(_buffer) - struct.pack_into(setting['cfg_crc'][0], _buffer, setting['cfg_crc'][1], crc) - if 'cfg_crc32' in setting: - crc32 = GetSettingsCrc32(_buffer) - struct.pack_into(setting['cfg_crc32'][0], _buffer, setting['cfg_crc32'][1], crc32) - return _buffer - - else: - exit(ExitCode.UNSUPPORTED_VERSION,"File '{}', Tasmota configuration version 0x{:x} not supported".format(filename, version), type_=LogType.WARNING, doexit=not args.ignorewarning) - - return None - - -def Mapping2Cmnd(decode_cfg, valuemapping, filename=""): - """ - Encodes mapping data into Tasmota command mapping - - @param decode_cfg: - binary config data (decrypted) - @param valuemapping: - data mapping - @param filename: - name of the restore file (for error output only) - - @return: - Tasmota command mapping {group: [cmnd <,cmnd <,...>>]} - """ - if isinstance(decode_cfg, instance(str)): - decode_cfg = bytearray(decode_cfg) - - # get binary header data to use the correct version template from device - version, size, setting = GetTemplateSetting(decode_cfg) - - cmnds = {} - - if setting is not None: - # iterate through restore data mapping - for name in valuemapping: - # key must exist in both dict - if name in setting: - cmnds = SetCmnd(cmnds, name, setting[name], valuemapping, valuemapping[name], addroffset=0) - else: - if name != 'header': - exit(ExitCode.RESTORE_DATA_ERROR, "Restore file '{}' contains obsolete name '{}', skipped".format(filename, name), type_=LogType.WARNING, doexit=not args.ignorewarning) - - return cmnds - - else: - exit(ExitCode.UNSUPPORTED_VERSION,"File '{}', Tasmota configuration version 0x{:x} not supported".format(filename, version), type_=LogType.WARNING, doexit=not args.ignorewarning) - - return None - - -def Backup(backupfile, backupfileformat, encode_cfg, decode_cfg, configmapping): - """ - Create backup file - - @param backupfile: - Raw backup filename from program args - @param backupfileformat: - Backup file format - @param encode_cfg: - binary config data (encrypted) - @param decode_cfg: - binary config data (decrypted) - @param configmapping: - config data mapppings - """ - - name, ext = os.path.splitext(backupfile) - if ext.lower() == '.'+FileType.BIN.lower(): - backupfileformat = FileType.BIN - elif ext.lower() == '.'+FileType.DMP.lower(): - backupfileformat = FileType.DMP - elif ext.lower() == '.'+FileType.JSON.lower(): - backupfileformat = FileType.JSON - - fileformat = "" - # Tasmota format - if backupfileformat.lower() == FileType.DMP.lower(): - fileformat = "Tasmota" - backup_filename = MakeFilename(backupfile, FileType.DMP, configmapping) - if args.verbose: - message("Writing backup file '{}' ({} format)".format(backup_filename, fileformat), type_=LogType.INFO) - try: - with open(backup_filename, "wb") as backupfp: - backupfp.write(encode_cfg) - except Exception as e: - exit(e.args[0], "'{}' {}".format(backup_filename, e[1]),line=inspect.getlineno(inspect.currentframe())) - - # binary format - elif backupfileformat.lower() == FileType.BIN.lower(): - fileformat = "binary" - backup_filename = MakeFilename(backupfile, FileType.BIN, configmapping) - if args.verbose: - message("Writing backup file '{}' ({} format)".format(backup_filename, fileformat), type_=LogType.INFO) - try: - with open(backup_filename, "wb") as backupfp: - backupfp.write(struct.pack('>]} - """ - def OutputTasmotaSubCmnds(cmnds): - if args.cmndsort: - for cmnd in sorted(cmnds, key = lambda cmnd:[int(c) if c.isdigit() else c for c in re.split('(\d+)', cmnd)]): - print("{}{}".format(" "*args.cmndindent, cmnd)) - else: - for cmnd in cmnds: - print("{}{}".format(" "*args.cmndindent, cmnd)) - - groups = GetGroupList(Settings[0][2]) - - if args.cmndgroup: - for group in groups: - if group.title() in (groupname.title() for groupname in tasmotacmnds): - cmnds = tasmotacmnds[group] - print - print("# {}:".format(group)) - OutputTasmotaSubCmnds(cmnds) - - else: - cmnds = [] - for group in groups: - if group.title() in (groupname.title() for groupname in tasmotacmnds): - cmnds.extend(tasmotacmnds[group]) - OutputTasmotaSubCmnds(cmnds) - -def ParseArgs(): - """ - Program argument parser - - @return: - configargparse.parse_args() result - """ - global parser - parser = configargparse.ArgumentParser(description='Backup/Restore Tasmota configuration data.', - epilog='Either argument -d or -f must be given.', - add_help=False, - formatter_class=lambda prog: CustomHelpFormatter(prog)) - - source = parser.add_argument_group('Source', 'Read/Write Tasmota configuration from/to') - source.add_argument('-f', '--file', '--tasmota-file', - metavar='', - dest='tasmotafile', - default=DEFAULTS['source']['tasmotafile'], - help="file to retrieve/write Tasmota configuration from/to (default: {})'".format(DEFAULTS['source']['tasmotafile'])) - source.add_argument('-d', '--device', '--host', - metavar='', - dest='device', - default=DEFAULTS['source']['device'], - help="hostname or IP address to retrieve/send Tasmota configuration from/to (default: {})".format(DEFAULTS['source']['device']) ) - source.add_argument('-P', '--port', - metavar='', - dest='port', - default=DEFAULTS['source']['port'], - help="TCP/IP port number to use for the host connection (default: {})".format(DEFAULTS['source']['port']) ) - source.add_argument('-u', '--username', - metavar='', - dest='username', - default=DEFAULTS['source']['username'], - help="host HTTP access username (default: {})".format(DEFAULTS['source']['username'])) - source.add_argument('-p', '--password', - metavar='', - dest='password', - default=DEFAULTS['source']['password'], - help="host HTTP access password (default: {})".format(DEFAULTS['source']['password'])) - - backup = parser.add_argument_group('Backup/Restore', 'Backup & restore specification') - backup.add_argument('-i', '--restore-file', - metavar='', - dest='restorefile', - default=DEFAULTS['backup']['backupfile'], - help="file to restore configuration from (default: {}). Replacements: @v=firmware version from config, @f=device friendly name from config, @h=device hostname from config, @H=device hostname from device (-d arg only)".format(DEFAULTS['backup']['restorefile'])) - backup.add_argument('-o', '--backup-file', - metavar='', - dest='backupfile', - default=DEFAULTS['backup']['backupfile'], - help="file to backup configuration to (default: {}). Replacements: @v=firmware version from config, @f=device friendly name from config, @h=device hostname from config, @H=device hostname from device (-d arg only)".format(DEFAULTS['backup']['backupfile'])) - backup_file_formats = ['json', 'bin', 'dmp'] - backup.add_argument('-t', '--backup-type', - metavar='|'.join(backup_file_formats), - dest='backupfileformat', - choices=backup_file_formats, - default=DEFAULTS['backup']['backupfileformat'], - help="backup filetype (default: '{}')".format(DEFAULTS['backup']['backupfileformat']) ) - backup.add_argument('-E', '--extension', - dest='extension', - action='store_true', - default=DEFAULTS['backup']['extension'], - help="append filetype extension for -i and -o filename{}".format(' (default)' if DEFAULTS['backup']['extension'] else '') ) - backup.add_argument('-e', '--no-extension', - dest='extension', - action='store_false', - default=DEFAULTS['backup']['extension'], - help="do not append filetype extension, use -i and -o filename as passed{}".format(' (default)' if not DEFAULTS['backup']['extension'] else '') ) - backup.add_argument('-F', '--force-restore', - dest='forcerestore', - action='store_true', - default=DEFAULTS['backup']['forcerestore'], - help="force restore even configuration is identical{}".format(' (default)' if DEFAULTS['backup']['forcerestore'] else '') ) - - jsonformat = parser.add_argument_group('JSON output', 'JSON format specification') - jsonformat.add_argument('--json-indent', - metavar='', - dest='jsonindent', - type=int, - default=DEFAULTS['jsonformat']['jsonindent'], - help="pretty-printed JSON output using indent level (default: '{}'). -1 disables indent.".format(DEFAULTS['jsonformat']['jsonindent']) ) - jsonformat.add_argument('--json-compact', - dest='jsoncompact', - action='store_true', - default=DEFAULTS['jsonformat']['jsoncompact'], - help="compact JSON output by eliminate whitespace{}".format(' (default)' if DEFAULTS['jsonformat']['jsoncompact'] else '') ) - - jsonformat.add_argument('--json-sort', - dest='jsonsort', - action='store_true', - default=DEFAULTS['jsonformat']['jsonsort'], - help=configargparse.SUPPRESS) #"sort json keywords{}".format(' (default)' if DEFAULTS['jsonformat']['jsonsort'] else '') ) - jsonformat.add_argument('--json-unsort', - dest='jsonsort', - action='store_false', - default=DEFAULTS['jsonformat']['jsonsort'], - help=configargparse.SUPPRESS) #"do not sort json keywords{}".format(' (default)' if not DEFAULTS['jsonformat']['jsonsort'] else '') ) - - jsonformat.add_argument('--json-hide-pw', - dest='jsonhidepw', - action='store_true', - default=DEFAULTS['jsonformat']['jsonhidepw'], - help="hide passwords{}".format(' (default)' if DEFAULTS['jsonformat']['jsonhidepw'] else '') ) - jsonformat.add_argument('--json-show-pw', '--json-unhide-pw', - dest='jsonhidepw', - action='store_false', - default=DEFAULTS['jsonformat']['jsonhidepw'], - help="unhide passwords{}".format(' (default)' if not DEFAULTS['jsonformat']['jsonhidepw'] else '') ) - - cmndformat = parser.add_argument_group('Tasmota command output', 'Tasmota command output format specification') - cmndformat.add_argument('--cmnd-indent', - metavar='', - dest='cmndindent', - type=int, - default=DEFAULTS['cmndformat']['cmndindent'], - help="Tasmota command grouping indent level (default: '{}'). 0 disables indent".format(DEFAULTS['cmndformat']['cmndindent']) ) - cmndformat.add_argument('--cmnd-groups', - dest='cmndgroup', - action='store_true', - default=DEFAULTS['cmndformat']['cmndgroup'], - help="group Tasmota commands{}".format(' (default)' if DEFAULTS['cmndformat']['cmndgroup'] else '') ) - cmndformat.add_argument('--cmnd-nogroups', - dest='cmndgroup', - action='store_false', - default=DEFAULTS['cmndformat']['cmndgroup'], - help="leave Tasmota commands ungrouped{}".format(' (default)' if not DEFAULTS['cmndformat']['cmndgroup'] else '') ) - cmndformat.add_argument('--cmnd-sort', - dest='cmndsort', - action='store_true', - default=DEFAULTS['cmndformat']['cmndsort'], - help="sort Tasmota commands{}".format(' (default)' if DEFAULTS['cmndformat']['cmndsort'] else '') ) - cmndformat.add_argument('--cmnd-unsort', - dest='cmndsort', - action='store_false', - default=DEFAULTS['cmndformat']['cmndsort'], - help="leave Tasmota commands unsorted{}".format(' (default)' if not DEFAULTS['cmndformat']['cmndsort'] else '') ) - - common = parser.add_argument_group('Common', 'Optional arguments') - common.add_argument('-c', '--config', - metavar='', - dest='configfile', - default=DEFAULTS['common']['configfile'], - is_config_file=True, - help="program config file - can be used to set default command args (default: {})".format(DEFAULTS['common']['configfile']) ) - - common.add_argument('-S', '--output', - dest='output', - action='store_true', - default=DEFAULTS['common']['output'], - help="display output regardsless of backup/restore usage{}".format(" (default)" if DEFAULTS['common']['output'] else " (default do not output on backup or restore usage)") ) - output_formats = ['json', 'cmnd','command'] - common.add_argument('-T', '--output-format', - metavar='|'.join(output_formats), - dest='outputformat', - choices=output_formats, - default=DEFAULTS['common']['outputformat'], - help="display output format (default: '{}')".format(DEFAULTS['common']['outputformat']) ) - groups = GetGroupList(Settings[0][2]) - if '*' in groups: - groups.remove('*') - common.add_argument('-g', '--group', - dest='filter', - choices=groups, - nargs='+', - type=lambda s : s.title(), - default=DEFAULTS['common']['filter'], - help="limit data processing to command groups (default {})".format("no filter" if DEFAULTS['common']['filter'] == None else DEFAULTS['common']['filter']) ) - common.add_argument('--ignore-warnings', - dest='ignorewarning', - action='store_true', - default=DEFAULTS['common']['ignorewarning'], - help="do not exit on warnings{}. Not recommended, used by your own responsibility!".format(' (default)' if DEFAULTS['common']['ignorewarning'] else '') ) - - - info = parser.add_argument_group('Info','Extra information') - info.add_argument('-D', '--debug', - dest='debug', - action='count', - help=configargparse.SUPPRESS) - info.add_argument('-h', '--help', - dest='shorthelp', - action='store_true', - help='show usage help message and exit') - info.add_argument("-H", "--full-help", - action="help", - help="show full help message and exit") - info.add_argument('-v', '--verbose', - dest='verbose', - action='store_true', - help='produce more output about what the program does') - info.add_argument('-V', '--version', - action='version', - version=PROG) - - args = parser.parse_args() - - if debug(args) >= 1: - print(parser.format_values(), file=sys.stderr) - print("Settings:", file=sys.stderr) - for k in args.__dict__: - print(" "+str(k), "= ",eval('args.{}'.format(k)), file=sys.stderr) - return args - - -if __name__ == "__main__": - args = ParseArgs() - if args.shorthelp: - ShortHelp() - - # check source args - if args.device is not None and args.tasmotafile is not None: - exit(ExitCode.ARGUMENT_ERROR, "Unable to select source, do not use -d and -f together",line=inspect.getlineno(inspect.currentframe())) - - # default no configuration available - encode_cfg = None - - # pull config from Tasmota device - if args.tasmotafile is not None: - if args.verbose: - message("Load data from file '{}'".format(args.tasmotafile), type_=LogType.INFO) - encode_cfg = LoadTasmotaConfig(args.tasmotafile) - - # load config from Tasmota file - if args.device is not None: - if args.verbose: - message("Load data from device '{}'".format(args.device), type_=LogType.INFO) - encode_cfg = PullTasmotaConfig(args.device, args.port, username=args.username, password=args.password) - - if encode_cfg is None: - # no config source given - ShortHelp(False) - print - print(parser.epilog) - sys.exit(ExitCode.OK) - - if len(encode_cfg) == 0: - exit(ExitCode.FILE_READ_ERROR, "Unable to read configuration data from {} '{}'".format('device' if args.device is not None else 'file', \ - args.device if args.device is not None else args.tasmotafile) \ - ,line=inspect.getlineno(inspect.currentframe()) ) - # decrypt Tasmota config - decode_cfg = DecryptEncrypt(encode_cfg) - - # decode into mappings dictionary - configmapping = Bin2Mapping(decode_cfg) - if args.verbose and 'version' in configmapping: - message("{} '{}' is using Tasmota {}".format('File' if args.tasmotafile is not None else 'Device', - args.tasmotafile if args.tasmotafile is not None else args.device, - GetVersionStr(configmapping['version'])), - type_=LogType.INFO) - - # backup to file - if args.backupfile is not None: - Backup(args.backupfile, args.backupfileformat, encode_cfg, decode_cfg, configmapping) - - # restore from file - if args.restorefile is not None: - Restore(args.restorefile, args.backupfileformat, encode_cfg, decode_cfg, configmapping) - - # json screen output - if (args.backupfile is None and args.restorefile is None) or args.output: - if args.outputformat == 'json': - print(json.dumps(configmapping, sort_keys=args.jsonsort, indent=None if args.jsonindent<0 else args.jsonindent, separators=(',', ':') if args.jsoncompact else (', ', ': ') )) - - if args.outputformat == 'cmnd' or args.outputformat == 'command': - tasmotacmnds = Mapping2Cmnd(decode_cfg, configmapping) - OutputTasmotaCmnds(tasmotacmnds) - - sys.exit(exitcode) From 4441f212d5164d510ee9594399b36a2e1b60dc95 Mon Sep 17 00:00:00 2001 From: Adrian Scillato <35405447+ascillato@users.noreply.github.com> Date: Mon, 2 Dec 2019 13:03:43 -0300 Subject: [PATCH 05/84] Add rule trigger tele-switch1#state --- tasmota/support_tasmota.ino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tasmota/support_tasmota.ino b/tasmota/support_tasmota.ino index e7099affd..84e31d4b0 100644 --- a/tasmota/support_tasmota.ino +++ b/tasmota/support_tasmota.ino @@ -609,7 +609,7 @@ 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])); - ResponseAppend_P(PSTR(",\"" D_JSON_SWITCH "%d\":\"%s\""), i +1, GetStateText(swm ^ SwitchLastState(i))); + ResponseAppend_P(PSTR(",\"" D_JSON_SWITCH "%d\":{\"STATE\":\"%s\"}"), i +1, GetStateText(swm ^ SwitchLastState(i))); } } XsnsCall(FUNC_JSON_APPEND); From 621204e57f736c1c672c81cd197229e88e955f05 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Mon, 2 Dec 2019 18:54:08 +0100 Subject: [PATCH 06/84] Fix WS2812 power control (#7090) --- tasmota/CHANGELOG.md | 1 + tasmota/xdrv_04_light.ino | 8 ++++++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/tasmota/CHANGELOG.md b/tasmota/CHANGELOG.md index c639e5392..1706dbbb3 100644 --- a/tasmota/CHANGELOG.md +++ b/tasmota/CHANGELOG.md @@ -4,6 +4,7 @@ - Fix lost functionality of GPIO9 and GPIO10 on some devices (#7080) - Fix Zigbee uses Hardware Serial if GPIO 1/3 or GPIO 13/15 and SerialLog 0 (#7071) +- Fix WS2812 power control (#7090) - Change light color schemes 2, 3 and 4 from color wheel to Hue driven ## Released diff --git a/tasmota/xdrv_04_light.ino b/tasmota/xdrv_04_light.ino index 0ef3f1ea9..7f1e66fc6 100644 --- a/tasmota/xdrv_04_light.ino +++ b/tasmota/xdrv_04_light.ino @@ -1571,6 +1571,7 @@ void LightAnimate(void) { uint8_t cur_col[LST_MAX]; uint16_t light_still_on = 0; + bool power_off = false; Light.strip_timer_counter++; if (!Light.power) { // All channels powered off @@ -1578,6 +1579,9 @@ void LightAnimate(void) if (!Light.fade_running) { sleep = Settings.sleep; } + if (Settings.light_scheme >= LS_MAX) { + power_off = true; + } } else { if (Settings.sleep > PWM_MAX_SLEEP) { sleep = PWM_MAX_SLEEP; // set a maxumum value of 50 milliseconds to ensure that animations are smooth @@ -1629,7 +1633,7 @@ void LightAnimate(void) } } - if (Settings.light_scheme < LS_MAX) { // exclude WS281X Neopixel + if ((Settings.light_scheme < LS_MAX) || power_off) { // exclude WS281X Neopixel schemes // Apply power modifiers to Light.new_color LightApplyPower(Light.new_color, Light.power); @@ -1714,7 +1718,7 @@ void LightAnimate(void) cur_col_10bits[i] = orig_col_10bits[Light.color_remap[i]]; } - if (!Settings.light_fade) { // no fade + if (!Settings.light_fade || power_off) { // no fade // record the current value for a future Fade memcpy(Light.fade_start_8, cur_col, sizeof(Light.fade_start_8)); memcpy(Light.fade_start_10, cur_col_10bits, sizeof(Light.fade_start_10)); From 461d18b3f9966888ce7c780f34786ff70d07331f Mon Sep 17 00:00:00 2001 From: Adrian Scillato <35405447+ascillato@users.noreply.github.com> Date: Mon, 2 Dec 2019 15:04:38 -0300 Subject: [PATCH 07/84] Add new tele- rule triggers --- tasmota/support_tasmota.ino | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tasmota/support_tasmota.ino b/tasmota/support_tasmota.ino index 84e31d4b0..2f54cec7d 100644 --- a/tasmota/support_tasmota.ino +++ b/tasmota/support_tasmota.ino @@ -565,7 +565,7 @@ void MqttShowState(void) if (i == LightDevice()) { LightState(1); } // call it only once } else { #endif - ResponseAppend_P(PSTR(",\"%s\":\"%s\""), GetPowerDevice(stemp1, i, sizeof(stemp1), Settings.flag.device_index_enable), // SetOption26 - Switch between POWER or POWER1 + ResponseAppend_P(PSTR(",\"%s\":{\"STATE\":\"%s\"}"), GetPowerDevice(stemp1, i, sizeof(stemp1), Settings.flag.device_index_enable), // SetOption26 - Switch between POWER or POWER1 GetStateText(bitRead(power, i-1))); #ifdef USE_SONOFF_IFAN if (IsModuleIfan()) { @@ -592,7 +592,7 @@ void MqttPublishTeleState(void) mqtt_data[0] = '\0'; MqttShowState(); MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_STATE), MQTT_TELE_RETAIN); -#ifdef USE_SCRIPT +#if defined(USE_RULES) || defined(USE_SCRIPT) RulesTeleperiod(); // Allow rule based HA messages #endif // USE_SCRIPT } From 0b3f84c36e01db3f95ebb26404a2155910cc1aed Mon Sep 17 00:00:00 2001 From: stefanbode Date: Mon, 2 Dec 2019 21:44:05 +0100 Subject: [PATCH 08/84] Added #7006 request for support of stepper motors --- tasmota/xdrv_27_shutter.ino | 84 ++++++++++++++++++++++--------------- 1 file changed, 50 insertions(+), 34 deletions(-) diff --git a/tasmota/xdrv_27_shutter.ino b/tasmota/xdrv_27_shutter.ino index 3c3039dda..b397ac732 100644 --- a/tasmota/xdrv_27_shutter.ino +++ b/tasmota/xdrv_27_shutter.ino @@ -76,10 +76,10 @@ struct SHUTTER { uint16_t open_time[MAX_SHUTTERS]; // duration to open the shutter uint16_t close_time[MAX_SHUTTERS]; // duration to close the shutter uint16_t close_velocity[MAX_SHUTTERS]; // in relation to open velocity. higher value = faster - uint16_t operations[MAX_SHUTTERS]; int8_t direction[MAX_SHUTTERS]; // 1 == UP , 0 == stop; -1 == down uint8_t mode = 0; // operation mode definition. see enum type above SHT_OFF_OPEN__OFF_CLOSE, SHT_OFF_ON__OPEN_CLOSE, SHT_PULSE_OPEN__PULSE_CLOSE uint8_t motordelay[MAX_SHUTTERS]; // initial motorstarttime in 0.05sec. + uint16_t pwm_frequency; } Shutter; void ShutterRtc50mS(void) @@ -237,45 +237,32 @@ void ShutterUpdatePosition(void) { char scommand[CMDSZ]; char stopic[TOPSZ]; + char stemp2[10]; for (uint32_t i = 0; i < shutters_present; i++) { if (Shutter.direction[i] != 0) { //char stemp1[20]; + + if (pin[GPIO_PWM1]+i < 99 && Shutter.pwm_frequency != 1000) { + Shutter.pwm_frequency += 100; + Shutter.pwm_frequency = (Shutter.pwm_frequency > 1000 ? 1000 : Shutter.pwm_frequency); + analogWriteFreq(Shutter.pwm_frequency); + analogWrite(pin[GPIO_PWM1]+i, 50); + } + Shutter.real_position[i] = Shutter.start_position[i] + ( (Shutter.time[i] - Shutter.motordelay[i]) * (Shutter.direction[i] > 0 ? 100 : -Shutter.close_velocity[i])); // avoid real position leaving the boundaries. - Shutter.real_position[i] = Shutter.real_position[i] < 0 ? 0 : (Shutter.real_position[i] > Shutter.open_max[i] ? Shutter.open_max[i] : Shutter.real_position[i]) ; + if (Shutter.real_position[i] < 0 || Shutter.real_position[i] > Shutter.open_max[i]) { + dtostrfd((float)Shutter.time[i] / 20, 1, stemp2); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: ShutterTEMP %d: Real Pos. %d, Stoppos: %ld, direction %d, rtcshutter: %s [s]"), i, Shutter.real_position[i], Settings.shutter_position[i], Shutter.direction[i], stemp2); - // Add additional runtime, if shutter did not reach the endstop for some time. - if (Shutter.target_position[i] == Shutter.real_position[i] && Shutter.target_position[i] == 0) { - // for every operation add 5x50ms = 250ms to stop position - //AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Adding additional runtime")); - Shutter.real_position[i] += 500 * Shutter.operations[i] ; - Shutter.operations[i] = 0; + Shutter.real_position[i] = Shutter.real_position[i] < 0 ? 0 : (Shutter.real_position[i] > Shutter.open_max[i] ? Shutter.open_max[i] : Shutter.real_position[i]) ; } + if (Shutter.real_position[i] * Shutter.direction[i] >= Shutter.target_position[i] * Shutter.direction[i] ) { // calculate relay number responsible for current movement. //AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Stop Condition detected: real: %d, Target: %d, direction: %d"),Shutter.real_position[i], Shutter.target_position[i],Shutter.direction[i]); uint8_t cur_relay = Settings.shutter_startrelay[i] + (Shutter.direction[i] == 1 ? 0 : 1) ; - char stemp2[10]; - - Settings.shutter_position[i] = ShutterRealToPercentPosition(Shutter.real_position[i], i); - //Settings.shutter_position[i] = Settings.shuttercoeff[2][i] * 5 > Shutter.real_position[i] ? (Shutter.real_position[i] * 10 / Settings.shuttercoeff[2][i] + 4)/10 : ((Shutter.real_position[i]-Settings.shuttercoeff[0,i]) *10 / Settings.shuttercoeff[1][i] +4) / 10; - - if (0 < Settings.shutter_position[i] && Settings.shutter_position[i] < 100) { - Shutter.operations[i]++; - } else { - Shutter.operations[i] = 0; - } - - dtostrfd((float)Shutter.time[i] / 20, 1, stemp2); - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Shutter %d: Real Pos. %d, Stoppos: %ld, relay: %d, direction %d, pulsetimer: %d, rtcshutter: %s [s], operationtime %d"), i, Shutter.real_position[i], Settings.shutter_position[i], cur_relay -1, Shutter.direction[i], Settings.pulse_timer[cur_relay -1], stemp2, Shutter.operations[i]); - Shutter.start_position[i] = Shutter.real_position[i]; - - // sending MQTT result to broker - snprintf_P(scommand, sizeof(scommand),PSTR(D_SHUTTER "%d"), i+1); - GetTopic_P(stopic, STAT, mqtt_topic, scommand); - Response_P("%d", Settings.shutter_invert[i] ? 100 - Settings.shutter_position[i]: Settings.shutter_position[i]); - MqttPublish(stopic, Settings.flag.mqtt_power_retain); // CMND_POWERRETAIN switch (Shutter.mode) { case SHT_PULSE_OPEN__PULSE_CLOSE: @@ -288,8 +275,21 @@ void ShutterUpdatePosition(void) break; case SHT_OFF_ON__OPEN_CLOSE: // This is a failsafe configuration. Relay1 ON/OFF Relay2 -1/1 direction + if (pin[GPIO_PWM1 ]+i < 99) { + Shutter.pwm_frequency = 0; + //slow down for acurate position + analogWriteFreq(500); + analogWrite(pin[GPIO_PWM1]+i, 50); + analogWriteFreq(0); + while (RtcSettings.pulse_counter[i] < (Shutter.target_position[i]-Shutter.start_position[i])*Shutter.direction[i]/2) { + delay(1); + } + analogWrite(pin[GPIO_PWM1]+i, 0); + Shutter.real_position[i] = RtcSettings.pulse_counter[i]*Shutter.direction[i]*2+Shutter.start_position[i]; + } if ((1 << (Settings.shutter_startrelay[i]-1)) & power) { ExecuteCommandPower(Settings.shutter_startrelay[i], 0, SRC_SHUTTER); + ExecuteCommandPower(Settings.shutter_startrelay[i]+1, 0, SRC_SHUTTER); } break; case SHT_OFF_OPEN__OFF_CLOSE: @@ -300,6 +300,18 @@ void ShutterUpdatePosition(void) } break; } + Settings.shutter_position[i] = ShutterRealToPercentPosition(Shutter.real_position[i], i); + + dtostrfd((float)Shutter.time[i] / 20, 1, stemp2); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Shutter %d: Real Pos. %d, Stoppos: %ld, relay: %d, direction %d, pulsetimer: %d, rtcshutter: %s [s]"), i, Shutter.real_position[i], Settings.shutter_position[i], cur_relay -1, Shutter.direction[i], Settings.pulse_timer[cur_relay -1], stemp2); + Shutter.start_position[i] = Shutter.real_position[i]; + + // sending MQTT result to broker + snprintf_P(scommand, sizeof(scommand),PSTR(D_SHUTTER "%d"), i+1); + GetTopic_P(stopic, STAT, mqtt_topic, scommand); + Response_P("%d", Settings.shutter_invert[i] ? 100 - Settings.shutter_position[i]: Settings.shutter_position[i]); + MqttPublish(stopic, Settings.flag.mqtt_power_retain); // CMND_POWERRETAIN + Shutter.direction[i] = 0; uint8_t position = Settings.shutter_invert[i] ? 100 - Settings.shutter_position[i]: Settings.shutter_position[i]; Response_P(PSTR("{")); @@ -326,6 +338,12 @@ void ShutterStartInit(uint8_t index, uint8_t direction, int32_t target_pos) Shutter.target_position[index] = target_pos; Shutter.start_position[index] = Shutter.real_position[index]; Shutter.time[index] = 0; + if (pin[GPIO_PWM1]+index < 99) { + Shutter.pwm_frequency = 0; + analogWriteFreq(Shutter.pwm_frequency); + analogWrite(pin[GPIO_PWM1]+index, 0); + RtcSettings.pulse_counter[index] = 0; + } //AddLog_P2(LOG_LEVEL_INFO, PSTR("SHT: Start shutter: %d from %d to %d in directin %d"), index, Shutter.start_position[index], Shutter.target_position[index], Shutter.direction[index]); } @@ -426,7 +444,6 @@ void ShutterSetPosition(uint8_t device, uint8_t position) void CmndShutterOpen(void) { XdrvMailbox.payload = 100; - XdrvMailbox.data_len = 3; last_source = SRC_WEBGUI; CmndShutterPosition(); } @@ -434,7 +451,7 @@ void CmndShutterOpen(void) void CmndShutterClose(void) { XdrvMailbox.payload = 0; - XdrvMailbox.data_len = 1; + XdrvMailbox.data_len = 0; last_source = SRC_WEBGUI; CmndShutterPosition(); } @@ -445,7 +462,7 @@ void CmndShutterStop(void) uint32_t index = XdrvMailbox.index -1; if (Shutter.direction[index] != 0) { - //AddLog_P2(LOG_LEVEL_INFO, PSTR("SHT: Stop moving shutter %d: direction: %d"), XdrvMailbox.index, Shutter.direction[index]); + AddLog_P2(LOG_LEVEL_INFO, PSTR("SHT: Stop moving shutter %d: direction: %d"), XdrvMailbox.index, Shutter.direction[index]); int32_t temp_realpos = Shutter.start_position[index] + ( (Shutter.time[index]+10) * (Shutter.direction[index] > 0 ? 100 : -Shutter.close_velocity[index])); XdrvMailbox.payload = ShutterRealToPercentPosition(temp_realpos, index); @@ -472,7 +489,7 @@ void CmndShutterPosition(void) if (!strcmp(XdrvMailbox.data,"STOP")) { CmndShutterStop(); } return; } - + int8_t target_pos_percent = XdrvMailbox.payload < 0 ? 0 : (XdrvMailbox.payload > 100 ? 100 : XdrvMailbox.payload); // webgui still send also on inverted shutter the native position. target_pos_percent = Settings.shutter_invert[index] && SRC_WEBGUI != last_source ? 100 - target_pos_percent : target_pos_percent; @@ -497,7 +514,6 @@ void CmndShutterPosition(void) } if (Shutter.direction[index] != new_shutterdirection ) { ShutterStartInit(index, new_shutterdirection, Shutter.target_position[index]); - Shutter.operations[index]++; if (Shutter.mode == SHT_OFF_ON__OPEN_CLOSE) { ExecuteCommandPower(Settings.shutter_startrelay[index], 0, SRC_SHUTTER); //AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Delay5 5s, xdrv %d"), XdrvMailbox.payload); @@ -556,7 +572,7 @@ void CmndShutterMotorDelay(void) ShutterInit(); } char time_chr[10]; - dtostrfd((float)(Settings.shutter_motordelay[XdrvMailbox.index -1]) / 20, 1, time_chr); + dtostrfd((float)(Settings.shutter_motordelay[XdrvMailbox.index -1]) / 20, 2, time_chr); ResponseCmndIdxChar(time_chr); } } From c1bd30ef8c435ce0e2b9247f0f501cbe0a2e7eb2 Mon Sep 17 00:00:00 2001 From: Adrian Scillato <35405447+ascillato@users.noreply.github.com> Date: Tue, 3 Dec 2019 22:25:03 -0300 Subject: [PATCH 09/84] Add new SDK version available only for STAGE core This new SDK is added as an option Also some comments about older SDK are fixed --- platformio_override_sample.ini | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/platformio_override_sample.ini b/platformio_override_sample.ini index 9827447dd..ad669b0a7 100644 --- a/platformio_override_sample.ini +++ b/platformio_override_sample.ini @@ -85,11 +85,11 @@ build_flags = ${esp82xx_defaults.build_flags} ; -DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK221 ; NONOSDK22x_190313 ; -DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK22x_190313 -; NONOSDK22x_190703 = 2.2.2-dev(38a443e) (Tasmota default) +; NONOSDK22x_190703 = 2.2.1+100-dev(38a443e) (Tasmota default) (Firmware 2K smaller than NONOSDK22x_191105) -DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK22x_190703 -; NONOSDK22x_191024 = 2.2.2-dev(5ab15d1) +; NONOSDK22x_191024 = 2.2.1+111-dev(5ab15d1) ; -DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK22x_191024 -; NONOSDK22x_191105 = 2.2.2-dev(bb83b9b) +; NONOSDK22x_191105 = 2.2.1+113-dev(bb83b9b) ; -DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK22x_191105 ; NONOSDK3V0 (known issues) ; -DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK3 @@ -129,11 +129,11 @@ build_flags = ${esp82xx_defaults.build_flags} ; -DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK221 ; NONOSDK22x_190313 ; -DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK22x_190313 -; NONOSDK22x_190703 = 2.2.2-dev(38a443e) (Tasmota default) +; NONOSDK22x_190703 = 2.2.1+100-dev(38a443e) (Tasmota default) (Firmware 2K smaller than NONOSDK22x_191105) -DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK22x_190703 -; NONOSDK22x_191024 = 2.2.2-dev(5ab15d1) +; NONOSDK22x_191024 = 2.2.1+111-dev(5ab15d1) ; -DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK22x_191024 -; NONOSDK22x_191105 = 2.2.2-dev(bb83b9b) +; NONOSDK22x_191105 = 2.2.1+113-dev(bb83b9b) ; -DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK22x_191105 ; NONOSDK3V0 (known issues) ; -DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK3 @@ -173,12 +173,14 @@ build_flags = ${esp82xx_defaults.build_flags} ; -DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK221 ; NONOSDK22x_190313 ; -DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK22x_190313 -; NONOSDK22x_190703 (Tasmota default) +; NONOSDK22x_190703 = 2.2.1+100-dev(38a443e) (Tasmota default) (Firmware 2K smaller than NONOSDK22x_191105) -DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK22x_190703 -; NONOSDK22x_191024 +; NONOSDK22x_191024 = 2.2.1+111-dev(5ab15d1) ; -DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK22x_191024 -; NONOSDK22x_191105 +; NONOSDK22x_191105 = 2.2.1+113-dev(bb83b9b) ; -DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK22x_191105 +; NONOSDK22x_191122 = 2.2.1+119-dev(a58da79) +; -DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK22x_191122 ; NONOSDK3V0 (known issues) ; -DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK3 ; lwIP 1.4 From f0e6e0098d8c1e5ce89bd9097c387b33c5f315f9 Mon Sep 17 00:00:00 2001 From: stefanbode Date: Wed, 4 Dec 2019 12:34:27 +0100 Subject: [PATCH 10/84] Added frequency change support shutterfrequency 1000 is default. can be increased or decreased. Added more comments to the source code for explanation optimize load in RPC call --- tasmota/xdrv_27_shutter.ino | 90 ++++++++++++++++++++++++------------- 1 file changed, 58 insertions(+), 32 deletions(-) diff --git a/tasmota/xdrv_27_shutter.ino b/tasmota/xdrv_27_shutter.ino index b397ac732..f47b53376 100644 --- a/tasmota/xdrv_27_shutter.ino +++ b/tasmota/xdrv_27_shutter.ino @@ -37,10 +37,13 @@ #define D_CMND_SHUTTER_INVERT "Invert" #define D_CMND_SHUTTER_CLIBRATION "Calibration" #define D_CMND_SHUTTER_MOTORDELAY "MotorDelay" +#define D_CMND_SHUTTER_FREQUENCY "Frequency" #define D_SHUTTER "SHUTTER" -const uint16_t MOTOR_STOP_TIME = 500; // in mS +const uint16_t MOTOR_STOP_TIME = 500; // in mS + + uint8_t calibrate_pos[6] = {0,30,50,70,90,100}; uint16_t messwerte[5] = {30,50,70,90,100}; @@ -51,12 +54,13 @@ const char kShutterCommands[] PROGMEM = D_PRFX_SHUTTER "|" D_CMND_SHUTTER_OPEN "|" D_CMND_SHUTTER_CLOSE "|" D_CMND_SHUTTER_STOP "|" D_CMND_SHUTTER_POSITION "|" D_CMND_SHUTTER_OPENTIME "|" D_CMND_SHUTTER_CLOSETIME "|" D_CMND_SHUTTER_RELAY "|" D_CMND_SHUTTER_SETHALFWAY "|" D_CMND_SHUTTER_SETCLOSE "|" D_CMND_SHUTTER_INVERT "|" D_CMND_SHUTTER_CLIBRATION "|" - D_CMND_SHUTTER_MOTORDELAY; + D_CMND_SHUTTER_MOTORDELAY "|" D_CMND_SHUTTER_FREQUENCY; void (* const ShutterCommand[])(void) PROGMEM = { &CmndShutterOpen, &CmndShutterClose, &CmndShutterStop, &CmndShutterPosition, &CmndShutterOpenTime, &CmndShutterCloseTime, &CmndShutterRelay, - &CmndShutterSetHalfway, &CmndShutterSetClose, &CmndShutterInvert, &CmndShutterCalibration , &CmndShutterMotorDelay}; + &CmndShutterSetHalfway, &CmndShutterSetClose, &CmndShutterInvert, &CmndShutterCalibration , &CmndShutterMotorDelay, + &CmndShutterFrequency}; const char JSON_SHUTTER_POS[] PROGMEM = "\"" D_PRFX_SHUTTER "%d\":{\"Position\":%d,\"direction\":%d}"; @@ -68,23 +72,24 @@ struct SHUTTER { power_t mask = 0; // bit mask with 11 at the position of relays that belong to at least ONE shutter power_t old_power = 0; // preserve old bitmask for power to extract the relay that changes. power_t switched_relay = 0; // bitmatrix that contain the relays that was lastly changed. - uint32_t time[MAX_SHUTTERS]; + uint32_t time[MAX_SHUTTERS]; // operating time of the shutter in 0.05sec int32_t open_max[MAX_SHUTTERS]; // max value on maximum open calculated int32_t target_position[MAX_SHUTTERS]; // position to go to - int32_t start_position[MAX_SHUTTERS]; + int32_t start_position[MAX_SHUTTERS]; // position before a movement is started. init at start int32_t real_position[MAX_SHUTTERS]; // value between 0 and Shutter.open_max - uint16_t open_time[MAX_SHUTTERS]; // duration to open the shutter - uint16_t close_time[MAX_SHUTTERS]; // duration to close the shutter + uint16_t open_time[MAX_SHUTTERS]; // duration to open the shutter. 112 = 11.2sec + uint16_t close_time[MAX_SHUTTERS]; // duration to close the shutter. 112 = 11.2sec uint16_t close_velocity[MAX_SHUTTERS]; // in relation to open velocity. higher value = faster int8_t direction[MAX_SHUTTERS]; // 1 == UP , 0 == stop; -1 == down uint8_t mode = 0; // operation mode definition. see enum type above SHT_OFF_OPEN__OFF_CLOSE, SHT_OFF_ON__OPEN_CLOSE, SHT_PULSE_OPEN__PULSE_CLOSE - uint8_t motordelay[MAX_SHUTTERS]; // initial motorstarttime in 0.05sec. - uint16_t pwm_frequency; + uint8_t motordelay[MAX_SHUTTERS]; // initial motorstarttime in 0.05sec. + uint16_t pwm_frequency; // frequency of PWN for stepper motors + uint16_t max_pwm_frequency = 1000; } Shutter; void ShutterRtc50mS(void) { - for (uint32_t i = 0; i < MAX_SHUTTERS; i++) { + for (uint32_t i = 0; i < shutters_present; i++) { Shutter.time[i]++; } } @@ -189,6 +194,11 @@ void ShutterInit(void) } } else { Shutter.mode = SHT_OFF_ON__OPEN_CLOSE; + if (pin[GPIO_PWM1 ]+i < 99) { + Shutter.pwm_frequency = 0; + analogWriteFreq(Shutter.pwm_frequency); + analogWrite(pin[GPIO_PWM1]+i, 50); + } } TickerShutter.attach_ms(50, ShutterRtc50mS ); @@ -243,21 +253,21 @@ void ShutterUpdatePosition(void) if (Shutter.direction[i] != 0) { //char stemp1[20]; - if (pin[GPIO_PWM1]+i < 99 && Shutter.pwm_frequency != 1000) { - Shutter.pwm_frequency += 100; - Shutter.pwm_frequency = (Shutter.pwm_frequency > 1000 ? 1000 : Shutter.pwm_frequency); + // frequency start at 0. Stepper will start moving with first change of the Speed + // Counter should be initiated to 0 to count movement. + // 0..1000 in step 100 = 10 steps with 0.05 sec = 0.5sec total ramp time from start to + // full speed. + if (pin[GPIO_PWM1]+i < 99 && Shutter.pwm_frequency != Shutter.max_pwm_frequency) { + Shutter.pwm_frequency += Shutter.max_pwm_frequency/20; + Shutter.pwm_frequency = (Shutter.pwm_frequency > Shutter.max_pwm_frequency ? Shutter.max_pwm_frequency : Shutter.pwm_frequency); analogWriteFreq(Shutter.pwm_frequency); analogWrite(pin[GPIO_PWM1]+i, 50); } Shutter.real_position[i] = Shutter.start_position[i] + ( (Shutter.time[i] - Shutter.motordelay[i]) * (Shutter.direction[i] > 0 ? 100 : -Shutter.close_velocity[i])); // avoid real position leaving the boundaries. - if (Shutter.real_position[i] < 0 || Shutter.real_position[i] > Shutter.open_max[i]) { - dtostrfd((float)Shutter.time[i] / 20, 1, stemp2); - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: ShutterTEMP %d: Real Pos. %d, Stoppos: %ld, direction %d, rtcshutter: %s [s]"), i, Shutter.real_position[i], Settings.shutter_position[i], Shutter.direction[i], stemp2); + Shutter.real_position[i] = Shutter.real_position[i] < 0 ? 0 : (Shutter.real_position[i] > Shutter.open_max[i] ? Shutter.open_max[i] : Shutter.real_position[i]) ; - Shutter.real_position[i] = Shutter.real_position[i] < 0 ? 0 : (Shutter.real_position[i] > Shutter.open_max[i] ? Shutter.open_max[i] : Shutter.real_position[i]) ; - } if (Shutter.real_position[i] * Shutter.direction[i] >= Shutter.target_position[i] * Shutter.direction[i] ) { // calculate relay number responsible for current movement. @@ -275,17 +285,26 @@ void ShutterUpdatePosition(void) break; case SHT_OFF_ON__OPEN_CLOSE: // This is a failsafe configuration. Relay1 ON/OFF Relay2 -1/1 direction - if (pin[GPIO_PWM1 ]+i < 99) { + // Only allow PWM microstepping if PWM and COUNTER are defined. + // see wiki to connect PWM and COUNTER + if (pin[GPIO_PWM1 ]+i < 99 && pin[GPIO_CNTR1 ]+i < 99 ) { + int16_t missing_steps = ((Shutter.target_position[i]-Shutter.start_position[i])*Shutter.direction[i]*Shutter.max_pwm_frequency/2000) - RtcSettings.pulse_counter[i]; Shutter.pwm_frequency = 0; //slow down for acurate position analogWriteFreq(500); analogWrite(pin[GPIO_PWM1]+i, 50); + //prepare for stop PWM + Shutter.motordelay[i] = -2 + Shutter.motordelay[i] + missing_steps/(Shutter.max_pwm_frequency/20); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Missing steps %d, adjust motordelay %d, counter %d, temp realpos %d"), missing_steps, Shutter.motordelay[i],RtcSettings.pulse_counter[i] ,Shutter.real_position[i]); + Settings.shutter_motordelay[i]=Shutter.motordelay[i]; analogWriteFreq(0); - while (RtcSettings.pulse_counter[i] < (Shutter.target_position[i]-Shutter.start_position[i])*Shutter.direction[i]/2) { + while (RtcSettings.pulse_counter[i] < (uint32_t)(Shutter.target_position[i]-Shutter.start_position[i])*Shutter.direction[i]*Shutter.max_pwm_frequency/2000) { delay(1); } analogWrite(pin[GPIO_PWM1]+i, 0); - Shutter.real_position[i] = RtcSettings.pulse_counter[i]*Shutter.direction[i]*2+Shutter.start_position[i]; + Shutter.real_position[i] = ((int32_t)RtcSettings.pulse_counter[i]*Shutter.direction[i]*2000 / Shutter.max_pwm_frequency)+Shutter.start_position[i]; + //AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT:Realpos %d, pulsecount %d, startpos %d, int32 %d"), Shutter.real_position[i],RtcSettings.pulse_counter[i], Shutter.start_position[i], ((int32_t)RtcSettings.pulse_counter[i]*Shutter.direction[i]*2000 / Shutter.max_pwm_frequency)); + } if ((1 << (Settings.shutter_startrelay[i]-1)) & power) { ExecuteCommandPower(Settings.shutter_startrelay[i], 0, SRC_SHUTTER); @@ -303,7 +322,7 @@ void ShutterUpdatePosition(void) Settings.shutter_position[i] = ShutterRealToPercentPosition(Shutter.real_position[i], i); dtostrfd((float)Shutter.time[i] / 20, 1, stemp2); - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Shutter %d: Real Pos. %d, Stoppos: %ld, relay: %d, direction %d, pulsetimer: %d, rtcshutter: %s [s]"), i, Shutter.real_position[i], Settings.shutter_position[i], cur_relay -1, Shutter.direction[i], Settings.pulse_timer[cur_relay -1], stemp2); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Shutter %d: Real Pos. %d, Stoppos: %ld, relay: %d, direction %d, pulsetimer: %d, motordelay %d, rtcshutter: %s [s]"), i, Shutter.real_position[i], Settings.shutter_position[i], cur_relay -1, Shutter.direction[i], Settings.pulse_timer[cur_relay -1], Shutter.motordelay[i],stemp2); Shutter.start_position[i] = Shutter.real_position[i]; // sending MQTT result to broker @@ -342,7 +361,10 @@ void ShutterStartInit(uint8_t index, uint8_t direction, int32_t target_pos) Shutter.pwm_frequency = 0; analogWriteFreq(Shutter.pwm_frequency); analogWrite(pin[GPIO_PWM1]+index, 0); - RtcSettings.pulse_counter[index] = 0; + // can be operated without counter, but then not that acurate. + if (pin[GPIO_CNTR1]+index < 99) { + RtcSettings.pulse_counter[index] = 0; + } } //AddLog_P2(LOG_LEVEL_INFO, PSTR("SHT: Start shutter: %d from %d to %d in directin %d"), index, Shutter.start_position[index], Shutter.target_position[index], Shutter.direction[index]); } @@ -360,10 +382,10 @@ void ShutterReportPosition(void) if (Shutter.direction[i] != 0) { char stemp1[20]; char stemp2[10]; - dtostrfd((float)Shutter.time[i] / 20, 1, stemp2); + dtostrfd((float)Shutter.time[i] / 20, 2, stemp2); shutter_moving = 1; //Settings.shutter_position[i] = Settings.shuttercoeff[2][i] * 5 > Shutter.real_position[i] ? Shutter.real_position[i] / Settings.shuttercoeff[2][i] : (Shutter.real_position[i]-Settings.shuttercoeff[0,i]) / Settings.shuttercoeff[1][i]; - AddLog_P2(LOG_LEVEL_INFO, PSTR("SHT: Shutter %d: Real Pos: %d, Target %d, source: %s, start-pos: %d %%, direction: %d, rtcshutter: %s [s]"), i,Shutter.real_position[i], Shutter.target_position[i], GetTextIndexed(stemp1, sizeof(stemp1), last_source, kCommandSource), Settings.shutter_position[i], Shutter.direction[i], stemp2 ); + AddLog_P2(LOG_LEVEL_INFO, PSTR("SHT: Shutter %d: Real Pos: %d, Target %d, source: %s, start-pos: %d %%, direction: %d, motordelay %d, rtcshutter: %s [s]"), i,Shutter.real_position[i], Shutter.target_position[i], GetTextIndexed(stemp1, sizeof(stemp1), last_source, kCommandSource), Settings.shutter_position[i], Shutter.direction[i], Shutter.motordelay[i], stemp2 ); } } if (rules_flag.shutter_moving > shutter_moving) { @@ -424,12 +446,6 @@ void ShutterRelayChanged(void) } } -/////////////////////////////////////////////////////////////////////////////////// -// Shutter specific functions -// TODO: move to shutter driver and make them accessible in a generic way - -// device: 1.. -// position: 0-100 void ShutterSetPosition(uint8_t device, uint8_t position) { char svalue[32]; // Command and number parameter @@ -463,7 +479,7 @@ void CmndShutterStop(void) if (Shutter.direction[index] != 0) { AddLog_P2(LOG_LEVEL_INFO, PSTR("SHT: Stop moving shutter %d: direction: %d"), XdrvMailbox.index, Shutter.direction[index]); - + // set stop position 10 steps ahead (0.5sec to allow normal stop) int32_t temp_realpos = Shutter.start_position[index] + ( (Shutter.time[index]+10) * (Shutter.direction[index] > 0 ? 100 : -Shutter.close_velocity[index])); XdrvMailbox.payload = ShutterRealToPercentPosition(temp_realpos, index); //XdrvMailbox.payload = Settings.shuttercoeff[2][index] * 5 > temp_realpos ? temp_realpos / Settings.shuttercoeff[2][index] : (temp_realpos-Settings.shuttercoeff[0,index]) / Settings.shuttercoeff[1][index]; @@ -609,6 +625,16 @@ void CmndShutterSetHalfway(void) } } +void CmndShutterFrequency(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 10000)) { + Shutter.max_pwm_frequency = XdrvMailbox.payload; + ResponseCmndNumber(XdrvMailbox.payload); // ???? + } else { + ResponseCmndNumber(Shutter.max_pwm_frequency); + } +} + void CmndShutterSetClose(void) { if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= shutters_present)) { From f251eea64b7f28e9d99da55e851312d30718d703 Mon Sep 17 00:00:00 2001 From: Adrian Scillato <39969427+ascillato2@users.noreply.github.com> Date: Wed, 4 Dec 2019 15:53:48 -0300 Subject: [PATCH 11/84] Updated Problem Report --- .github/ISSUE_TEMPLATE/Bug_report.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/Bug_report.md b/.github/ISSUE_TEMPLATE/Bug_report.md index 847958093..f9622222f 100644 --- a/.github/ISSUE_TEMPLATE/Bug_report.md +++ b/.github/ISSUE_TEMPLATE/Bug_report.md @@ -38,7 +38,7 @@ _Make sure your have performed every step and checked the applicable boxes befor - [ ] Self-compiled - [ ] IDE / Compiler used: _____ - [ ] Flashing tools used: _____ -- [ ] Provide the output of command: ``Backlog Template; Module; GPIO``: +- [ ] Provide the output of command: ``Backlog Template; Module; GPIO 255``: ``` Configuration output here: From 55b0312f8620b960b7a4dbcd6771299b47893790 Mon Sep 17 00:00:00 2001 From: Adrian Scillato <35405447+ascillato@users.noreply.github.com> Date: Wed, 4 Dec 2019 17:11:03 -0300 Subject: [PATCH 12/84] Added new var %topic% for rules --- tasmota/xdrv_10_rules.ino | 1 + 1 file changed, 1 insertion(+) diff --git a/tasmota/xdrv_10_rules.ino b/tasmota/xdrv_10_rules.ino index 4025ef5d4..441722662 100644 --- a/tasmota/xdrv_10_rules.ino +++ b/tasmota/xdrv_10_rules.ino @@ -452,6 +452,7 @@ bool RuleSetProcess(uint8_t rule_set, String &event_saved) RulesVarReplace(commands, F("%TIME%"), String(MinutesPastMidnight())); RulesVarReplace(commands, F("%UPTIME%"), String(MinutesUptime())); RulesVarReplace(commands, F("%TIMESTAMP%"), GetDateAndTime(DT_LOCAL)); + RulesVarReplace(commands, F("%TOPIC%"), Settings.mqtt_topic); #if defined(USE_TIMERS) && defined(USE_SUNRISE) RulesVarReplace(commands, F("%SUNRISE%"), String(SunMinutes(0))); RulesVarReplace(commands, F("%SUNSET%"), String(SunMinutes(1))); From 8ed7257c9b8952e2e0d542461096ad0ad72db2a5 Mon Sep 17 00:00:00 2001 From: Jason2866 Date: Thu, 5 Dec 2019 15:16:57 +0100 Subject: [PATCH 13/84] Update platformio_override_sample.ini --- platformio_override_sample.ini | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/platformio_override_sample.ini b/platformio_override_sample.ini index ad669b0a7..6cbd749c4 100644 --- a/platformio_override_sample.ini +++ b/platformio_override_sample.ini @@ -28,6 +28,7 @@ default_envs = [common] platform = ${core_active.platform} +platform_packages = ${core_active.platform_packages} build_flags = ${core_active.build_flags} ; *** Use settings from file user_config_override.h -DUSE_CONFIG_OVERRIDE @@ -72,6 +73,7 @@ build_flags = ${core_2_6_1.build_flags} ;platform = ${core_2_6_2.platform} ;build_flags = ${core_2_6_2.build_flags} ;platform = ${core_stage.platform} +;platform_packages = ${core_stage.platform_packages} ;build_flags = ${core_stage.build_flags} @@ -165,7 +167,8 @@ build_flags = ${esp82xx_defaults.build_flags} [core_stage] ; *** Esp8266 core for Arduino version latest beta -platform = https://github.com/platformio/platform-espressif8266.git#feature/stage +platform = espressif8266@2.3.1 +platform_packages = framework-arduinoespressif8266 @ https://github.com/esp8266/Arduino.git build_flags = ${esp82xx_defaults.build_flags} -Wl,-Teagle.flash.1m.ld -DBEARSSL_SSL_BASIC From 246663dd9dd5759e64a6ceec1be3ae4abdab1485 Mon Sep 17 00:00:00 2001 From: Jason2866 Date: Thu, 5 Dec 2019 15:18:24 +0100 Subject: [PATCH 14/84] Update platformio_tasmota_env.ini --- platformio_tasmota_env.ini | 1 + 1 file changed, 1 insertion(+) diff --git a/platformio_tasmota_env.ini b/platformio_tasmota_env.ini index 46e71d7af..38aaf2223 100644 --- a/platformio_tasmota_env.ini +++ b/platformio_tasmota_env.ini @@ -1,5 +1,6 @@ [env] platform = ${common.platform} +platform_packages = ${common.platform_packages} framework = ${common.framework} board = ${common.board} board_build.flash_mode = ${common.board_build.flash_mode} From 3b4b549e61fafa8e9b2ed912fe7f196a6c7fe219 Mon Sep 17 00:00:00 2001 From: gemu2015 Date: Thu, 5 Dec 2019 16:34:30 +0100 Subject: [PATCH 15/84] sml mbus decoder syntax update support for byte order --- tasmota/xsns_53_sml.ino | 32 +++++++++++++++++++++++++++----- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/tasmota/xsns_53_sml.ino b/tasmota/xsns_53_sml.ino index ddc3f8db0..7aa0fd22d 100755 --- a/tasmota/xsns_53_sml.ino +++ b/tasmota/xsns_53_sml.ino @@ -499,7 +499,10 @@ const uint8_t meter[]= #define USE_SML_MEDIAN_FILTER // max number of vars , may be adjusted +#ifndef MAX_VARS #define MAX_VARS 20 +#endif + // max number of meters , may be adjusted #define MAX_METERS 5 double meter_vars[MAX_VARS]; @@ -1427,14 +1430,25 @@ void SML_Decode(uint8_t index) { //ignore mp+=2; cp++; - } else if (!strncmp(mp,"uuuuuuuu",8)) { + } else if (!strncmp(mp,"UUuuUUuu",8)) { uint32_t val= (cp[0]<<24)|(cp[1]<<16)|(cp[2]<<8)|(cp[3]<<0); ebus_dval=val; mbus_dval=val; mp+=8; cp+=4; - } - else if (*mp=='u' && *(mp+1)=='u' && *(mp+2)=='u' && *(mp+3)=='u'){ + } else if (*mp=='U' && *(mp+1)=='U' && *(mp+2)=='u' && *(mp+3)=='u'){ + uint16_t val = cp[1]|(cp[0]<<8); + mbus_dval=val; + ebus_dval=val; + mp+=4; + cp+=2; + } else if (!strncmp(mp,"SSssSSss",8)) { + int32_t val= (cp[0]<<24)|(cp[1]<<16)|(cp[2]<<8)|(cp[3]<<0); + ebus_dval=val; + mbus_dval=val; + mp+=8; + cp+=4; + } else if (*mp=='u' && *(mp+1)=='u' && *(mp+2)=='U' && *(mp+3)=='U'){ uint16_t val = cp[0]|(cp[1]<<8); mbus_dval=val; ebus_dval=val; @@ -1442,17 +1456,25 @@ void SML_Decode(uint8_t index) { cp+=2; } else if (*mp=='u' && *(mp+1)=='u') { uint8_t val = *cp++; + mbus_dval=val; ebus_dval=val; mp+=2; - } - else if (*mp=='s' && *(mp+1)=='s' && *(mp+2)=='s' && *(mp+3)=='s') { + } else if (*mp=='s' && *(mp+1)=='s' && *(mp+2)=='S' && *(mp+3)=='S') { int16_t val = *cp|(*(cp+1)<<8); + mbus_dval=val; + ebus_dval=val; + mp+=4; + cp+=2; + } else if (*mp=='S' && *(mp+1)=='S' && *(mp+2)=='s' && *(mp+3)=='s') { + int16_t val = cp[1]|(cp[0]<<8); + mbus_dval=val; ebus_dval=val; mp+=4; cp+=2; } else if (*mp=='s' && *(mp+1)=='s') { int8_t val = *cp++; + mbus_dval=val; ebus_dval=val; mp+=2; } From 726883b16884fa03e893cdf0d3d6864c44b04efa Mon Sep 17 00:00:00 2001 From: Bohdan Kmit Date: Thu, 5 Dec 2019 20:54:30 +0200 Subject: [PATCH 16/84] Allow to build Minimal firmware with TLS support --- tasmota/tasmota_post.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tasmota/tasmota_post.h b/tasmota/tasmota_post.h index be3b503f0..cc47e3fc1 100644 --- a/tasmota/tasmota_post.h +++ b/tasmota/tasmota_post.h @@ -512,7 +512,7 @@ char* ToHex_P(const unsigned char * in, size_t insz, char * out, size_t outsz, c #undef USE_ARDUINO_OTA // Disable support for Arduino OTA #undef USE_DOMOTICZ // Disable Domoticz #undef USE_HOME_ASSISTANT // Disable Home Assistant -#undef USE_MQTT_TLS // Disable TLS support won't work as the MQTTHost is not set +//#undef USE_MQTT_TLS // Disable TLS support won't work as the MQTTHost is not set #undef USE_KNX // Disable KNX IP Protocol Support //#undef USE_WEBSERVER // Disable Webserver #undef USE_WEBSEND_RESPONSE // Disable command WebSend response message (+1k code) From b347c53846c84f17f246ab25cb373c87a0c747b6 Mon Sep 17 00:00:00 2001 From: Adrian Scillato <39969427+ascillato2@users.noreply.github.com> Date: Thu, 5 Dec 2019 18:06:17 -0300 Subject: [PATCH 17/84] Fix markdown of Problem Issue Report --- .github/ISSUE_TEMPLATE/Bug_report.md | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/Bug_report.md b/.github/ISSUE_TEMPLATE/Bug_report.md index f9622222f..4b4027bf8 100644 --- a/.github/ISSUE_TEMPLATE/Bug_report.md +++ b/.github/ISSUE_TEMPLATE/Bug_report.md @@ -38,31 +38,31 @@ _Make sure your have performed every step and checked the applicable boxes befor - [ ] Self-compiled - [ ] IDE / Compiler used: _____ - [ ] Flashing tools used: _____ -- [ ] Provide the output of command: ``Backlog Template; Module; GPIO 255``: - ``` +- [ ] Provide the output of command: `Backlog Template; Module; GPIO 255`: +``` Configuration output here: - ``` -- [ ] If using rules, provide the output of this command: ``Backlog Rule1; Rule2; Rule3``: - ``` +``` +- [ ] If using rules, provide the output of this command: `Backlog Rule1; Rule2; Rule3`: +``` Rules output here: - ``` -- [ ] Provide the output of this command: ``Status 0``: - ``` +``` +- [ ] Provide the output of this command: `Status 0`: +``` STATUS 0 output here: - ``` +``` - [ ] Provide the output of the Console log output when you experience your issue; if applicable: - _(Please use_ ``weblog 4`` _for more debug information)_ - ``` + _(Please use_ `weblog 4` _for more debug information)_ +``` Console output here: - ``` +``` ### TO REPRODUCE _Steps to reproduce the behavior:_ From 9d9f3b0ca7b4b6ee7db5e8f8afaa44f1d7ca88fd Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Fri, 6 Dec 2019 11:10:15 +0100 Subject: [PATCH 18/84] Change log buffer size from 520 to 700 Change log buffer size from 520 to 700 characters accomodating full rule text (#7110) --- tasmota/CHANGELOG.md | 7 ++++++- tasmota/tasmota.h | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/tasmota/CHANGELOG.md b/tasmota/CHANGELOG.md index 1706dbbb3..e92cfd27a 100644 --- a/tasmota/CHANGELOG.md +++ b/tasmota/CHANGELOG.md @@ -5,7 +5,12 @@ - Fix lost functionality of GPIO9 and GPIO10 on some devices (#7080) - Fix Zigbee uses Hardware Serial if GPIO 1/3 or GPIO 13/15 and SerialLog 0 (#7071) - Fix WS2812 power control (#7090) -- Change light color schemes 2, 3 and 4 from color wheel to Hue driven +- Change light color schemes 2, 3 and 4 from color wheel to Hue driven with user Saturation control +- Change log buffer size from 520 to 700 characters accomodating full rule text (#7110) +- Add rule var ``%topic%`` (#5522) +- Add rule triggers ``tele_power1#state`` and multiple ``tele-wifi1#xxx`` (#7093) +- Add experimental support for stepper motor shutter control +- Add optional USE_MQTT_TLS to tasmota-minimal.bin (#7115) ## Released diff --git a/tasmota/tasmota.h b/tasmota/tasmota.h index d9e55bd15..cefef3a06 100644 --- a/tasmota/tasmota.h +++ b/tasmota/tasmota.h @@ -119,7 +119,7 @@ const uint16_t INPUT_BUFFER_SIZE = 520; // Max number of characters in (seri const uint16_t FLOATSZ = 16; // Max number of characters in float result from dtostrfd (max 32) 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 LOGSZ = 700; // 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 From 8c5c95747c4344522c4dc343f311e1123026dcbd Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Fri, 6 Dec 2019 12:18:46 +0100 Subject: [PATCH 19/84] Bump version 7.1.2.1 --- README.md | 2 +- RELEASENOTES.md | 4 +++- tasmota/CHANGELOG.md | 19 +++++++++++++------ tasmota/tasmota_version.h | 2 +- 4 files changed, 18 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 7b101df63..acb6430d5 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ In addition to the [release webpage](https://github.com/arendst/Tasmota/releases ## Development -[![Dev Version](https://img.shields.io/badge/development%20version-v7.1.1.x-blue.svg)](https://github.com/arendst/Tasmota) +[![Dev Version](https://img.shields.io/badge/development%20version-v7.1.2.x-blue.svg)](https://github.com/arendst/Tasmota) [![Download Dev](https://img.shields.io/badge/download-development-yellow.svg)](http://thehackbox.org/tasmota/) [![Build Status](https://img.shields.io/travis/arendst/Tasmota.svg)](https://travis-ci.org/arendst/Tasmota) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 51d078352..0baecb42a 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -51,4 +51,6 @@ The following binary downloads have been compiled with ESP8266/Arduino library c - Fix lost functionality of GPIO9 and GPIO10 on some devices (#7080) - Fix Zigbee uses Hardware Serial if GPIO 1/3 or GPIO 13/15 and SerialLog 0 (#7071) -- Change light color schemes 2, 3 and 4 from color wheel to Hue driven +- Fix WS2812 power control (#7090) +- Change light color schemes 2, 3 and 4 from color wheel to Hue driven with user Saturation control +- Change log buffer size from 520 to 700 characters accomodating full rule text (#7110) diff --git a/tasmota/CHANGELOG.md b/tasmota/CHANGELOG.md index e92cfd27a..2fe8fac75 100644 --- a/tasmota/CHANGELOG.md +++ b/tasmota/CHANGELOG.md @@ -1,5 +1,18 @@ ## Unreleased (development) +### 7.1.2.1 20191206 + +- Add rule var ``%topic%`` (#5522) +- Add rule triggers ``tele_power1#state`` and multiple ``tele-wifi1#xxx`` (#7093) +- Add experimental support for stepper motor shutter control +- Add optional USE_MQTT_TLS to tasmota-minimal.bin (#7115) + +## Released + +### 7.1.2 20191206 + +- Maintenance Release + ### 7.1.1.1 20191201 - Fix lost functionality of GPIO9 and GPIO10 on some devices (#7080) @@ -7,12 +20,6 @@ - Fix WS2812 power control (#7090) - Change light color schemes 2, 3 and 4 from color wheel to Hue driven with user Saturation control - Change log buffer size from 520 to 700 characters accomodating full rule text (#7110) -- Add rule var ``%topic%`` (#5522) -- Add rule triggers ``tele_power1#state`` and multiple ``tele-wifi1#xxx`` (#7093) -- Add experimental support for stepper motor shutter control -- Add optional USE_MQTT_TLS to tasmota-minimal.bin (#7115) - -## Released ### 7.1.1 20191201 diff --git a/tasmota/tasmota_version.h b/tasmota/tasmota_version.h index 54e4c165f..7b2207950 100644 --- a/tasmota/tasmota_version.h +++ b/tasmota/tasmota_version.h @@ -20,6 +20,6 @@ #ifndef _TASMOTA_VERSION_H_ #define _TASMOTA_VERSION_H_ -const uint32_t VERSION = 0x07010101; +const uint32_t VERSION = 0x07010201; #endif // _TASMOTA_VERSION_H_ From 00fd7f2f7c62765a95fcde751cf297f1bd37d2d3 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Fri, 6 Dec 2019 15:02:05 +0100 Subject: [PATCH 20/84] Bump version to 7.1.2.2 Add command ``SerialConfig 0..23`` or ``SerialConfig 8N1`` to select Serial Config (#7108) --- tasmota/CHANGELOG.md | 4 +++ tasmota/i18n.h | 1 + tasmota/settings.h | 4 +-- tasmota/settings.ino | 21 +++++++++++++++ tasmota/support.ino | 51 +++++++++++++++++++++++++++++-------- tasmota/support_command.ino | 46 +++++++++++++++++++++++++++++++-- tasmota/support_rtc.ino | 4 ++- tasmota/tasmota.ino | 2 -- tasmota/tasmota_version.h | 2 +- tasmota/xnrg_02_cse7766.ino | 2 +- 10 files changed, 117 insertions(+), 20 deletions(-) diff --git a/tasmota/CHANGELOG.md b/tasmota/CHANGELOG.md index 2fe8fac75..4bb5447ad 100644 --- a/tasmota/CHANGELOG.md +++ b/tasmota/CHANGELOG.md @@ -1,5 +1,9 @@ ## Unreleased (development) +### 7.1.2.2 20191206 + +- Add command ``SerialConfig 0..23`` or ``SerialConfig 8N1`` to select Serial Config (#7108) + ### 7.1.2.1 20191206 - Add rule var ``%topic%`` (#5522) diff --git a/tasmota/i18n.h b/tasmota/i18n.h index 5de4b7a1d..dfa998474 100644 --- a/tasmota/i18n.h +++ b/tasmota/i18n.h @@ -285,6 +285,7 @@ #define D_CMND_SERIALSEND "SerialSend" #define D_CMND_SERIALDELIMITER "SerialDelimiter" #define D_CMND_BAUDRATE "Baudrate" +#define D_CMND_SERIALCONFIG "SerialConfig" #define D_CMND_TEMPLATE "Template" #define D_JSON_NAME "NAME" #define D_JSON_GPIO "GPIO" diff --git a/tasmota/settings.h b/tasmota/settings.h index 554711cad..f464a24cb 100644 --- a/tasmota/settings.h +++ b/tasmota/settings.h @@ -269,9 +269,7 @@ struct SYSCFG { uint8_t free_1d6[10]; // 1D6 SysBitfield4 flag4; // 1E0 - - uint8_t free_1e4; // 1E4 - + uint8_t serial_config; // 1E4 uint8_t wifi_output_power; // 1E5 uint8_t shutter_accuracy; // 1E6 uint8_t mqttlog_level; // 1E7 diff --git a/tasmota/settings.ino b/tasmota/settings.ino index 9ca979b45..f6113be46 100644 --- a/tasmota/settings.ino +++ b/tasmota/settings.ino @@ -156,6 +156,23 @@ const char kWebColors[] PROGMEM = 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 "|" COLOR_TITLE_TEXT; +enum TasmotaSerialConfig { + TS_SERIAL_5N1, TS_SERIAL_6N1, TS_SERIAL_7N1, TS_SERIAL_8N1, + TS_SERIAL_5N2, TS_SERIAL_6N2, TS_SERIAL_7N2, TS_SERIAL_8N2, + TS_SERIAL_5E1, TS_SERIAL_6E1, TS_SERIAL_7E1, TS_SERIAL_8E1, + TS_SERIAL_5E2, TS_SERIAL_6E2, TS_SERIAL_7E2, TS_SERIAL_8E2, + TS_SERIAL_5O1, TS_SERIAL_6O1, TS_SERIAL_7O1, TS_SERIAL_8O1, + TS_SERIAL_5O2, TS_SERIAL_6O2, TS_SERIAL_7O2, TS_SERIAL_8O2 }; + +const uint8_t kTasmotaSerialConfig[] PROGMEM = { + SERIAL_5N1, SERIAL_6N1, SERIAL_7N1, SERIAL_8N1, + SERIAL_5N2, SERIAL_6N2, SERIAL_7N2, SERIAL_8N2, + SERIAL_5E1, SERIAL_6E1, SERIAL_7E1, SERIAL_8E1, + SERIAL_5E2, SERIAL_6E2, SERIAL_7E2, SERIAL_8E2, + SERIAL_5O1, SERIAL_6O1, SERIAL_7O1, SERIAL_8O1, + SERIAL_5O2, SERIAL_6O2, SERIAL_7O2, SERIAL_8O2 +}; + /*********************************************************************************************\ * RTC memory \*********************************************************************************************/ @@ -670,6 +687,7 @@ void SettingsDefaultSet2(void) // for (uint32_t i = 1; i < MAX_PULSETIMERS; i++) { Settings.pulse_timer[i] = 0; } // Serial + Settings.serial_config = TS_SERIAL_8N1; Settings.baudrate = APP_BAUDRATE / 300; Settings.sbaudrate = SOFT_BAUDRATE / 300; Settings.serial_delimiter = 0xff; @@ -1155,6 +1173,9 @@ void SettingsDelta(void) if (Settings.version < 0x07000004) { Settings.wifi_output_power = 170; } + if (Settings.version < 0x07010202) { + Settings.serial_config = TS_SERIAL_8N1; + } Settings.version = VERSION; SettingsSave(1); diff --git a/tasmota/support.ino b/tasmota/support.ino index 8ce7f2840..f44d14eb7 100644 --- a/tasmota/support.ino +++ b/tasmota/support.ino @@ -737,19 +737,50 @@ int GetStateNumber(char *state_text) return state_number; } +String GetSerialConfig(void) +{ + // Settings.serial_config layout + // b000000xx - 5, 6, 7 or 8 data bits + // b00000x00 - 1 or 2 stop bits + // b000xx000 - None, Even or Odd parity + + const char kParity[] = "NEOI"; + + char config[4]; + config[0] = '5' + (Settings.serial_config & 0x3); + config[1] = kParity[(Settings.serial_config >> 3) & 0x3]; + config[2] = '1' + ((Settings.serial_config >> 2) & 0x1); + config[3] = '\0'; + return String(config); +} + +void SetSerialBegin(uint32_t baudrate) +{ + if (seriallog_level) { + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_APPLICATION "Set Serial to %s %d bit/s"), GetSerialConfig().c_str(), baudrate); + delay(100); + } + Serial.flush(); + Serial.begin(baudrate, (SerialConfig)pgm_read_byte(kTasmotaSerialConfig + Settings.serial_config)); + delay(10); + Serial.println(); +} + +void SetSerialConfig(uint32_t serial_config) +{ + if (serial_config == Settings.serial_config) { return; } + if (serial_config > TS_SERIAL_8O2) { return; } + + Settings.serial_config = serial_config; + SetSerialBegin(Serial.baudRate()); +} + void SetSerialBaudrate(int baudrate) { Settings.baudrate = baudrate / 300; - if (Serial.baudRate() != baudrate) { - if (seriallog_level) { - AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_APPLICATION D_SET_BAUDRATE_TO " %d"), baudrate); - } - delay(100); - Serial.flush(); - Serial.begin(baudrate, serial_config); - delay(10); - Serial.println(); - } + if (Serial.baudRate() == baudrate) { return; } + + SetSerialBegin(baudrate); } void ClaimSerial(void) diff --git a/tasmota/support_command.ino b/tasmota/support_command.ino index fe8640045..070409566 100644 --- a/tasmota/support_command.ino +++ b/tasmota/support_command.ino @@ -23,7 +23,7 @@ const char kTasmotaCommands[] PROGMEM = "|" // No prefix D_CMND_SETOPTION "|" D_CMND_TEMPERATURE_RESOLUTION "|" D_CMND_HUMIDITY_RESOLUTION "|" D_CMND_PRESSURE_RESOLUTION "|" D_CMND_POWER_RESOLUTION "|" D_CMND_VOLTAGE_RESOLUTION "|" D_CMND_FREQUENCY_RESOLUTION "|" D_CMND_CURRENT_RESOLUTION "|" D_CMND_ENERGY_RESOLUTION "|" D_CMND_WEIGHT_RESOLUTION "|" D_CMND_MODULE "|" D_CMND_MODULES "|" D_CMND_GPIO "|" D_CMND_GPIOS "|" D_CMND_TEMPLATE "|" D_CMND_PWM "|" D_CMND_PWMFREQUENCY "|" D_CMND_PWMRANGE "|" - D_CMND_BUTTONDEBOUNCE "|" D_CMND_SWITCHDEBOUNCE "|" D_CMND_SYSLOG "|" D_CMND_LOGHOST "|" D_CMND_LOGPORT "|" D_CMND_SERIALSEND "|" D_CMND_BAUDRATE "|" + D_CMND_BUTTONDEBOUNCE "|" D_CMND_SWITCHDEBOUNCE "|" D_CMND_SYSLOG "|" D_CMND_LOGHOST "|" D_CMND_LOGPORT "|" D_CMND_SERIALSEND "|" D_CMND_BAUDRATE "|" D_CMND_SERIALCONFIG "|" D_CMND_SERIALDELIMITER "|" 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_TELEPERIOD "|" D_CMND_RESET "|" D_CMND_TIME "|" D_CMND_TIMEZONE "|" D_CMND_TIMESTD "|" D_CMND_TIMEDST "|" D_CMND_ALTITUDE "|" D_CMND_LEDPOWER "|" D_CMND_LEDSTATE "|" D_CMND_LEDMASK "|" D_CMND_WIFIPOWER "|" D_CMND_TEMPOFFSET "|" @@ -38,7 +38,7 @@ void (* const TasmotaCommand[])(void) PROGMEM = { &CmndSetoption, &CmndTemperatureResolution, &CmndHumidityResolution, &CmndPressureResolution, &CmndPowerResolution, &CmndVoltageResolution, &CmndFrequencyResolution, &CmndCurrentResolution, &CmndEnergyResolution, &CmndWeightResolution, &CmndModule, &CmndModules, &CmndGpio, &CmndGpios, &CmndTemplate, &CmndPwm, &CmndPwmfrequency, &CmndPwmrange, - &CmndButtonDebounce, &CmndSwitchDebounce, &CmndSyslog, &CmndLoghost, &CmndLogport, &CmndSerialSend, &CmndBaudrate, + &CmndButtonDebounce, &CmndSwitchDebounce, &CmndSyslog, &CmndLoghost, &CmndLogport, &CmndSerialSend, &CmndBaudrate, &CmndSerialConfig, &CmndSerialDelimiter, &CmndIpAddress, &CmndNtpServer, &CmndAp, &CmndSsid, &CmndPassword, &CmndHostname, &CmndWifiConfig, &CmndFriendlyname, &CmndSwitchMode, &CmndInterlock, &CmndTeleperiod, &CmndReset, &CmndTime, &CmndTimezone, &CmndTimeStd, &CmndTimeDst, &CmndAltitude, &CmndLedPower, &CmndLedState, &CmndLedMask, &CmndWifiPower, &CmndTempOffset, @@ -1082,6 +1082,48 @@ void CmndBaudrate(void) ResponseCmndNumber(Settings.baudrate * 300); } +void CmndSerialConfig(void) +{ + // See TasmotaSerialConfig for possible options + // SerialConfig 0..23 where 3 equals 8N1 + // SerialConfig 8N1 + + if (XdrvMailbox.data_len > 0) { + if (XdrvMailbox.data_len < 3) { // Use 0..23 as serial config option + if ((XdrvMailbox.payload >= TS_SERIAL_5N1) && (XdrvMailbox.payload <= TS_SERIAL_8O2)) { + SetSerialConfig(XdrvMailbox.payload); + } + } + else if ((XdrvMailbox.payload >= 5) && (XdrvMailbox.payload <= 8)) { + uint8_t serial_config = XdrvMailbox.payload -5; // Data bits 5, 6, 7 or 8, No parity and 1 stop bit + + bool valid = true; + char parity = (XdrvMailbox.data[1] & 0xdf); + if ('E' == parity) { + serial_config += 0x08; // Even parity + } + else if ('O' == parity) { + serial_config += 0x10; // Odd parity + } + else if ('N' != parity) { + valid = false; + } + + if ('2' == XdrvMailbox.data[2]) { + serial_config += 0x04; // Stop bits 2 + } + else if ('1' != XdrvMailbox.data[2]) { + valid = false; + } + + if (valid) { + SetSerialConfig(serial_config); + } + } + } + ResponseCmndChar(GetSerialConfig().c_str()); +} + void CmndSerialSend(void) { if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= 5)) { diff --git a/tasmota/support_rtc.ino b/tasmota/support_rtc.ino index ffb7239a6..27ef66ec7 100644 --- a/tasmota/support_rtc.ino +++ b/tasmota/support_rtc.ino @@ -379,7 +379,9 @@ void RtcSecond(void) Rtc.ntp_sync_minute = 1; // If sync prepare for a new cycle } uint8_t offset = (uptime < 30) ? RtcTime.second : (((ESP.getChipId() & 0xF) * 3) + 3) ; // First try ASAP to sync. If fails try once every 60 seconds based on chip id - if ( (((offset == RtcTime.second) && ( (RtcTime.year < 2016) || (Rtc.ntp_sync_minute == uptime_minute))) || ntp_force_sync ) ) { + if ( (((offset == RtcTime.second) && ( (RtcTime.year < 2016) || // Never synced + (Rtc.ntp_sync_minute == uptime_minute))) || // Re-sync every hour + ntp_force_sync ) ) { // Forced sync Rtc.ntp_time = sntp_get_current_timestamp(); if (Rtc.ntp_time > START_VALID_TIME) { // Fix NTP bug in core 2.4.1/SDK 2.2.1 (returns Thu Jan 01 08:00:10 1970 after power on) ntp_force_sync = false; diff --git a/tasmota/tasmota.ino b/tasmota/tasmota.ino index e10dfdb63..5cf0a430f 100644 --- a/tasmota/tasmota.ino +++ b/tasmota/tasmota.ino @@ -74,8 +74,6 @@ const char kCodeImage[] PROGMEM = "tasmota|minimal|sensors|knx|basic|display|ir" * Global variables \*********************************************************************************************/ -SerialConfig serial_config = SERIAL_8N1; // Serial interface configuration 8 data bits, No parity, 1 stop bit - WiFiUDP PortUdp; // UDP Syslog and Alexa unsigned long feature_drv1; // Compiled driver feature map diff --git a/tasmota/tasmota_version.h b/tasmota/tasmota_version.h index 7b2207950..929924840 100644 --- a/tasmota/tasmota_version.h +++ b/tasmota/tasmota_version.h @@ -20,6 +20,6 @@ #ifndef _TASMOTA_VERSION_H_ #define _TASMOTA_VERSION_H_ -const uint32_t VERSION = 0x07010201; +const uint32_t VERSION = 0x07010202; #endif // _TASMOTA_VERSION_H_ diff --git a/tasmota/xnrg_02_cse7766.ino b/tasmota/xnrg_02_cse7766.ino index 339d4fee2..d52ee9ca3 100644 --- a/tasmota/xnrg_02_cse7766.ino +++ b/tasmota/xnrg_02_cse7766.ino @@ -211,7 +211,7 @@ void CseDrvInit(void) { if ((3 == pin[GPIO_CSE7766_RX]) && (1 == pin[GPIO_CSE7766_TX])) { // As it uses 8E1 currently only hardware serial is supported baudrate = 4800; - serial_config = SERIAL_8E1; + Settings.serial_config = TS_SERIAL_8E1; if (0 == Settings.param[P_CSE7766_INVALID_POWER]) { Settings.param[P_CSE7766_INVALID_POWER] = CSE_MAX_INVALID_POWER; // SetOption39 1..255 } From ee37c258e64c397225090c082686e2a471a7836e Mon Sep 17 00:00:00 2001 From: Laurent Dong Date: Fri, 6 Dec 2019 10:12:13 -0500 Subject: [PATCH 21/84] Embeded IF statement in backlog did not been processed The backlog_mutex blocked the execution of IF statement during call ExecuteCommand(), because all IF statement need to use backlog to perform commands. --- tasmota/tasmota.ino | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/tasmota/tasmota.ino b/tasmota/tasmota.ino index 5cf0a430f..306250549 100644 --- a/tasmota/tasmota.ino +++ b/tasmota/tasmota.ino @@ -349,15 +349,18 @@ void BacklogLoop(void) { if (TimeReached(backlog_delay)) { if (!BACKLOG_EMPTY && !backlog_mutex) { - backlog_mutex = true; #ifdef SUPPORT_IF_STATEMENT - ExecuteCommand((char*)backlog.shift().c_str(), SRC_BACKLOG); + backlog_mutex = true; + String cmd = backlog.shift(); + backlog_mutex = false; + ExecuteCommand((char*)cmd.c_str(), SRC_BACKLOG); #else + backlog_mutex = true; ExecuteCommand((char*)backlog[backlog_pointer].c_str(), SRC_BACKLOG); backlog_pointer++; if (backlog_pointer >= MAX_BACKLOG) { backlog_pointer = 0; } -#endif backlog_mutex = false; +#endif } } } From 5f290f8b64cb215d4bc2d4f2f69397e43e9c1d2b Mon Sep 17 00:00:00 2001 From: Adrian Scillato <35405447+ascillato@users.noreply.github.com> Date: Fri, 6 Dec 2019 20:10:04 -0300 Subject: [PATCH 22/84] Reverting changes in tele message due to backwards compatibility --- tasmota/support_tasmota.ino | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tasmota/support_tasmota.ino b/tasmota/support_tasmota.ino index 2f54cec7d..96cec9c5a 100644 --- a/tasmota/support_tasmota.ino +++ b/tasmota/support_tasmota.ino @@ -565,7 +565,7 @@ void MqttShowState(void) if (i == LightDevice()) { LightState(1); } // call it only once } else { #endif - ResponseAppend_P(PSTR(",\"%s\":{\"STATE\":\"%s\"}"), GetPowerDevice(stemp1, i, sizeof(stemp1), Settings.flag.device_index_enable), // SetOption26 - Switch between POWER or POWER1 + ResponseAppend_P(PSTR(",\"%s\":\"%s\""), GetPowerDevice(stemp1, i, sizeof(stemp1), Settings.flag.device_index_enable), // SetOption26 - Switch between POWER or POWER1 GetStateText(bitRead(power, i-1))); #ifdef USE_SONOFF_IFAN if (IsModuleIfan()) { @@ -609,7 +609,7 @@ 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])); - ResponseAppend_P(PSTR(",\"" D_JSON_SWITCH "%d\":{\"STATE\":\"%s\"}"), i +1, GetStateText(swm ^ SwitchLastState(i))); + ResponseAppend_P(PSTR(",\"" D_JSON_SWITCH "%d\":\"%s\""), i +1, GetStateText(swm ^ SwitchLastState(i))); } } XsnsCall(FUNC_JSON_APPEND); From 5a0febc64ef515fd3873b3f6b842ac4d669fda55 Mon Sep 17 00:00:00 2001 From: Hadinger Date: Sat, 7 Dec 2019 10:41:29 +0100 Subject: [PATCH 23/84] Add save call stack in RTC memory in case of crash --- tasmota/CHANGELOG.md | 1 + tasmota/i18n.h | 3 ++ tasmota/my_user_config.h | 3 ++ tasmota/support_command.ino | 15 +++++- tasmota/support_crash_recorder.ino | 80 ++++++++++++++++++++++++++++++ tasmota/support_wifi.ino | 1 + tasmota/tasmota.h | 2 +- 7 files changed, 103 insertions(+), 2 deletions(-) create mode 100644 tasmota/support_crash_recorder.ino diff --git a/tasmota/CHANGELOG.md b/tasmota/CHANGELOG.md index 4bb5447ad..10c3c6941 100644 --- a/tasmota/CHANGELOG.md +++ b/tasmota/CHANGELOG.md @@ -3,6 +3,7 @@ ### 7.1.2.2 20191206 - Add command ``SerialConfig 0..23`` or ``SerialConfig 8N1`` to select Serial Config (#7108) +- Add save call stack in RTC memory in case of crash, command ``Status 12`` to dump the stack ### 7.1.2.1 20191206 diff --git a/tasmota/i18n.h b/tasmota/i18n.h index dfa998474..30fcf173e 100644 --- a/tasmota/i18n.h +++ b/tasmota/i18n.h @@ -208,6 +208,7 @@ #define D_STATUS9_MARGIN "PTH" #define D_STATUS10_SENSOR "SNS" #define D_STATUS11_STATUS "STS" + #define D_STATUS12_STATUS "STK" #define D_CMND_STATE "State" #define D_CMND_POWER "Power" #define D_CMND_FANSPEED "FanSpeed" @@ -292,6 +293,8 @@ #define D_JSON_FLAG "FLAG" #define D_JSON_BASE "BASE" #define D_CMND_TEMPOFFSET "TempOffset" +#define D_CMND_CRASH "Crash" + #define D_JSON_ONE_TO_CRASH "1 to crash" // Commands xdrv_01_mqtt.ino #define D_CMND_MQTTLOG "MqttLog" diff --git a/tasmota/my_user_config.h b/tasmota/my_user_config.h index 7ef154d53..46fe15def 100644 --- a/tasmota/my_user_config.h +++ b/tasmota/my_user_config.h @@ -268,6 +268,9 @@ //#define MY_LANGUAGE zh-CN // Chinese (Simplified) in China //#define MY_LANGUAGE zh-TW // Chinese (Traditional) in Taiwan +// -- Crash generator --------------------------- +//#define USE_CRASH // add a `Crash` command to test the crash recorder (+48 bytes) + // -- Wifi Config tools --------------------------- #define WIFI_SOFT_AP_CHANNEL 1 // Soft Access Point Channel number between 1 and 13 as used by Wifi Manager web GUI diff --git a/tasmota/support_command.ino b/tasmota/support_command.ino index 070409566..020e57b9d 100644 --- a/tasmota/support_command.ino +++ b/tasmota/support_command.ino @@ -30,7 +30,10 @@ const char kTasmotaCommands[] PROGMEM = "|" // No prefix #ifdef USE_I2C D_CMND_I2CSCAN "|" D_CMND_I2CDRIVER "|" #endif - D_CMND_SENSOR "|" D_CMND_DRIVER; +#ifdef USE_CRASH + D_CMND_CRASH "|" +#endif + D_CMND_SENSOR "|" D_CMND_DRIVER ; void (* const TasmotaCommand[])(void) PROGMEM = { &CmndBacklog, &CmndDelay, &CmndPower, &CmndStatus, &CmndState, &CmndSleep, &CmndUpgrade, &CmndUpgrade, &CmndOtaUrl, @@ -44,6 +47,9 @@ void (* const TasmotaCommand[])(void) PROGMEM = { &CmndTimeDst, &CmndAltitude, &CmndLedPower, &CmndLedState, &CmndLedMask, &CmndWifiPower, &CmndTempOffset, #ifdef USE_I2C &CmndI2cScan, CmndI2cDriver, +#endif +#ifdef USE_CRASH + &CmndCrash, #endif &CmndSensor, &CmndDriver }; @@ -483,6 +489,13 @@ void CmndStatus(void) MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS "11")); } + if ((0 == payload) || (12 == payload)) { + Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS12_STATUS "\":")); + CrashDump(); + ResponseJsonEnd(); + MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS "12")); + } + #ifdef USE_SCRIPT_STATUS if (bitRead(Settings.rule_enabled, 0)) Run_Scripter(">U",2,mqtt_data); #endif diff --git a/tasmota/support_crash_recorder.ino b/tasmota/support_crash_recorder.ino new file mode 100644 index 000000000..be0cde11b --- /dev/null +++ b/tasmota/support_crash_recorder.ino @@ -0,0 +1,80 @@ +/* + support_crash_recorder.ino - record the call stack in RTC in cas of crash + + Copyright (C) 2019 Stephan Hadinger, Theo Arends, + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +const uint32_t dump_max_len = 64; // dump only 64 call addresses + +/** + * Save crash information in RTC memory + * This function is called automatically if ESP8266 suffers an exception + * It should be kept quick / consise to be able to execute before hardware wdt may kick in + */ +extern "C" void custom_crash_callback(struct rst_info * rst_info, uint32_t stack, uint32_t stack_end ) { + uint32_t addr_written = 0; // how many addresses have we already written in RTC + uint32_t value; // 4 bytes buffer to write to RTC + + for (uint32_t i = stack; i < stack_end; i += 4) { + value = *((uint32_t*) i); // load value from stack + if ((value >= 0x40000000) && (value < 0x40300000)) { // keep only addresses in code area + ESP.rtcUserMemoryWrite(addr_written, (uint32_t*)&value, sizeof(value)); + addr_written++; + if (addr_written >= dump_max_len) { break; } // we store only 64 addresses + } + } + // fill the rest of RTC with zeros + value = 0; + while (addr_written < dump_max_len) { + ESP.rtcUserMemoryWrite(addr_written++, (uint32_t*)&value, sizeof(value)); + } +} + +// Generate a crash to test the crash recorder +void CmndCrash(void) +{ + if (1 == XdrvMailbox.payload) { + volatile uint32_t dummy; + dummy = *((uint32_t*) 0x00000000); // invalid address + } else { + ResponseCmndChar(D_JSON_ONE_TO_CRASH); + } +} + +// Clear the RTC dump area when we do a normal reboot, this avoids garbage data to stay in RTC +void CrashDumpClear(void) { + uint32_t value = 0; + for (uint32_t i = 0; i < dump_max_len; i++) { + ESP.rtcUserMemoryWrite(i, (uint32_t*)&value, sizeof(value)); + } +} + +/*********************************************************************************************\ + * CmndCrashDump - dump the crash history - called by `Status 12` +\*********************************************************************************************/ +void CrashDump(void) +{ + ResponseAppend_P(PSTR("{\"call_chain\":[")); + for (uint32_t i = 0; i < dump_max_len; i++) { + uint32_t value; + ESP.rtcUserMemoryRead(i, (uint32_t*)&value, sizeof(value)); + if ((value >= 0x40000000) && (value < 0x40300000)) { + if (i > 0) { ResponseAppend_P(PSTR(",")); } + ResponseAppend_P(PSTR("\"%08x\""), value); + } + } + ResponseAppend_P(PSTR("]}")); +} diff --git a/tasmota/support_wifi.ino b/tasmota/support_wifi.ino index 49faa0663..06d922b26 100644 --- a/tasmota/support_wifi.ino +++ b/tasmota/support_wifi.ino @@ -617,6 +617,7 @@ void WifiShutdown(void) void EspRestart(void) { WifiShutdown(); + CrashDumpClear(); // Clear the stack dump in RTC // ESP.restart(); // This results in exception 3 on restarts on core 2.3.0 ESP.reset(); } diff --git a/tasmota/tasmota.h b/tasmota/tasmota.h index cefef3a06..c31bfad83 100644 --- a/tasmota/tasmota.h +++ b/tasmota/tasmota.h @@ -131,7 +131,7 @@ 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 uint32_t ZIGBEE_POLLING = 100; // Serial receive polling in ms -const uint8_t MAX_STATUS = 11; // Max number of status lines +const uint8_t MAX_STATUS = 12; // Max number of status lines const uint32_t START_VALID_TIME = 1451602800; // Time is synced and after 2016-01-01 From 7796714ba08347122994b8033f9fb26a6b165643 Mon Sep 17 00:00:00 2001 From: Hadinger Date: Sat, 7 Dec 2019 11:00:32 +0100 Subject: [PATCH 24/84] Change from `Crash` to `Restart -1` --- tasmota/i18n.h | 2 -- tasmota/my_user_config.h | 3 --- tasmota/support_command.ino | 9 +++------ tasmota/support_crash_recorder.ino | 8 ++------ 4 files changed, 5 insertions(+), 17 deletions(-) diff --git a/tasmota/i18n.h b/tasmota/i18n.h index 30fcf173e..f913c14d2 100644 --- a/tasmota/i18n.h +++ b/tasmota/i18n.h @@ -293,8 +293,6 @@ #define D_JSON_FLAG "FLAG" #define D_JSON_BASE "BASE" #define D_CMND_TEMPOFFSET "TempOffset" -#define D_CMND_CRASH "Crash" - #define D_JSON_ONE_TO_CRASH "1 to crash" // Commands xdrv_01_mqtt.ino #define D_CMND_MQTTLOG "MqttLog" diff --git a/tasmota/my_user_config.h b/tasmota/my_user_config.h index 46fe15def..7ef154d53 100644 --- a/tasmota/my_user_config.h +++ b/tasmota/my_user_config.h @@ -268,9 +268,6 @@ //#define MY_LANGUAGE zh-CN // Chinese (Simplified) in China //#define MY_LANGUAGE zh-TW // Chinese (Traditional) in Taiwan -// -- Crash generator --------------------------- -//#define USE_CRASH // add a `Crash` command to test the crash recorder (+48 bytes) - // -- Wifi Config tools --------------------------- #define WIFI_SOFT_AP_CHANNEL 1 // Soft Access Point Channel number between 1 and 13 as used by Wifi Manager web GUI diff --git a/tasmota/support_command.ino b/tasmota/support_command.ino index 020e57b9d..23ce1f22e 100644 --- a/tasmota/support_command.ino +++ b/tasmota/support_command.ino @@ -29,9 +29,6 @@ const char kTasmotaCommands[] PROGMEM = "|" // No prefix D_CMND_TIMEDST "|" D_CMND_ALTITUDE "|" D_CMND_LEDPOWER "|" D_CMND_LEDSTATE "|" D_CMND_LEDMASK "|" D_CMND_WIFIPOWER "|" D_CMND_TEMPOFFSET "|" #ifdef USE_I2C D_CMND_I2CSCAN "|" D_CMND_I2CDRIVER "|" -#endif -#ifdef USE_CRASH - D_CMND_CRASH "|" #endif D_CMND_SENSOR "|" D_CMND_DRIVER ; @@ -47,9 +44,6 @@ void (* const TasmotaCommand[])(void) PROGMEM = { &CmndTimeDst, &CmndAltitude, &CmndLedPower, &CmndLedState, &CmndLedMask, &CmndWifiPower, &CmndTempOffset, #ifdef USE_I2C &CmndI2cScan, CmndI2cDriver, -#endif -#ifdef USE_CRASH - &CmndCrash, #endif &CmndSensor, &CmndDriver }; @@ -577,6 +571,9 @@ void CmndRestart(void) restart_flag = 2; ResponseCmndChar(D_JSON_RESTARTING); break; + case -1: + CmndCrash(); // force a crash + break; case 99: AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_APPLICATION D_RESTARTING)); EspRestart(); diff --git a/tasmota/support_crash_recorder.ino b/tasmota/support_crash_recorder.ino index be0cde11b..abd948c39 100644 --- a/tasmota/support_crash_recorder.ino +++ b/tasmota/support_crash_recorder.ino @@ -46,12 +46,8 @@ extern "C" void custom_crash_callback(struct rst_info * rst_info, uint32_t stack // Generate a crash to test the crash recorder void CmndCrash(void) { - if (1 == XdrvMailbox.payload) { - volatile uint32_t dummy; - dummy = *((uint32_t*) 0x00000000); // invalid address - } else { - ResponseCmndChar(D_JSON_ONE_TO_CRASH); - } + volatile uint32_t dummy; + dummy = *((uint32_t*) 0x00000000); } // Clear the RTC dump area when we do a normal reboot, this avoids garbage data to stay in RTC From 7e1e85eba95355fd814f393be8739b3f6a4008d4 Mon Sep 17 00:00:00 2001 From: Hadinger Date: Sat, 7 Dec 2019 11:01:48 +0100 Subject: [PATCH 25/84] Add save call stack in RTC memory in case of crash --- tasmota/support_command.ino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tasmota/support_command.ino b/tasmota/support_command.ino index 23ce1f22e..2ba09629e 100644 --- a/tasmota/support_command.ino +++ b/tasmota/support_command.ino @@ -45,7 +45,7 @@ void (* const TasmotaCommand[])(void) PROGMEM = { #ifdef USE_I2C &CmndI2cScan, CmndI2cDriver, #endif - &CmndSensor, &CmndDriver }; + &CmndSensor, &CmndDriver}; const char kWifiConfig[] PROGMEM = D_WCFG_0_RESTART "||" D_WCFG_2_WIFIMANAGER "||" D_WCFG_4_RETRY "|" D_WCFG_5_WAIT "|" D_WCFG_6_SERIAL "|" D_WCFG_7_WIFIMANAGER_RESET_ONLY; From edc2dc5c7947fedf80e2f55c34d875ef2d5d294a Mon Sep 17 00:00:00 2001 From: Hadinger Date: Sat, 7 Dec 2019 11:02:37 +0100 Subject: [PATCH 26/84] Add save call stack in RTC memory in case of crash --- tasmota/support_command.ino | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tasmota/support_command.ino b/tasmota/support_command.ino index 2ba09629e..f6baeef4e 100644 --- a/tasmota/support_command.ino +++ b/tasmota/support_command.ino @@ -30,7 +30,7 @@ const char kTasmotaCommands[] PROGMEM = "|" // No prefix #ifdef USE_I2C D_CMND_I2CSCAN "|" D_CMND_I2CDRIVER "|" #endif - D_CMND_SENSOR "|" D_CMND_DRIVER ; + D_CMND_SENSOR "|" D_CMND_DRIVER; void (* const TasmotaCommand[])(void) PROGMEM = { &CmndBacklog, &CmndDelay, &CmndPower, &CmndStatus, &CmndState, &CmndSleep, &CmndUpgrade, &CmndUpgrade, &CmndOtaUrl, @@ -45,7 +45,7 @@ void (* const TasmotaCommand[])(void) PROGMEM = { #ifdef USE_I2C &CmndI2cScan, CmndI2cDriver, #endif - &CmndSensor, &CmndDriver}; + &CmndSensor, &CmndDriver }; const char kWifiConfig[] PROGMEM = D_WCFG_0_RESTART "||" D_WCFG_2_WIFIMANAGER "||" D_WCFG_4_RETRY "|" D_WCFG_5_WAIT "|" D_WCFG_6_SERIAL "|" D_WCFG_7_WIFIMANAGER_RESET_ONLY; From a8c0c4e312015ba96e3d07b3b8effa24638a972d Mon Sep 17 00:00:00 2001 From: Hadinger Date: Sat, 7 Dec 2019 15:01:39 +0100 Subject: [PATCH 27/84] Remove any whitespace in TlsKey base64 --- tasmota/xdrv_02_mqtt.ino | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/tasmota/xdrv_02_mqtt.ino b/tasmota/xdrv_02_mqtt.ino index 49f35d579..a6994ebfb 100644 --- a/tasmota/xdrv_02_mqtt.ino +++ b/tasmota/xdrv_02_mqtt.ino @@ -1071,6 +1071,19 @@ void CmndTlsKey(void) { } memcpy_P(spi_buffer, tls_spi_start, tls_spi_len); + // remove any white space from the base64 + char *cursor = XdrvMailbox.data; + uint32_t offset = 0; + while (1) { + *cursor = *(cursor + offset); + if ((' ' == *cursor) || ('\t' == *cursor) || ('\n' == *cursor)) { // if space found, remove this char until end of string + offset++; + } else { + if (0 == *cursor) { break; } + cursor++; + } + } + // allocate buffer for decoded base64 uint32_t bin_len = decode_base64_length((unsigned char*)XdrvMailbox.data); uint8_t *bin_buf = nullptr; From 2686ccb117e55462ae9d02c87732b48d5373bccb Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Sat, 7 Dec 2019 16:50:10 +0100 Subject: [PATCH 28/84] Revert "Merge pull request #7134 from s-hadinger/crashrecorder_rtc" This reverts commit 758b25507822ad3c862216feb4c1af4fd5d53e78, reversing changes made to f4993736a500ffada2a110236dab04a93220d7e0. --- tasmota/CHANGELOG.md | 1 - tasmota/i18n.h | 1 - tasmota/support_command.ino | 10 ---- tasmota/support_crash_recorder.ino | 76 ------------------------------ tasmota/support_wifi.ino | 1 - tasmota/tasmota.h | 2 +- 6 files changed, 1 insertion(+), 90 deletions(-) delete mode 100644 tasmota/support_crash_recorder.ino diff --git a/tasmota/CHANGELOG.md b/tasmota/CHANGELOG.md index 10c3c6941..4bb5447ad 100644 --- a/tasmota/CHANGELOG.md +++ b/tasmota/CHANGELOG.md @@ -3,7 +3,6 @@ ### 7.1.2.2 20191206 - Add command ``SerialConfig 0..23`` or ``SerialConfig 8N1`` to select Serial Config (#7108) -- Add save call stack in RTC memory in case of crash, command ``Status 12`` to dump the stack ### 7.1.2.1 20191206 diff --git a/tasmota/i18n.h b/tasmota/i18n.h index f913c14d2..dfa998474 100644 --- a/tasmota/i18n.h +++ b/tasmota/i18n.h @@ -208,7 +208,6 @@ #define D_STATUS9_MARGIN "PTH" #define D_STATUS10_SENSOR "SNS" #define D_STATUS11_STATUS "STS" - #define D_STATUS12_STATUS "STK" #define D_CMND_STATE "State" #define D_CMND_POWER "Power" #define D_CMND_FANSPEED "FanSpeed" diff --git a/tasmota/support_command.ino b/tasmota/support_command.ino index f6baeef4e..070409566 100644 --- a/tasmota/support_command.ino +++ b/tasmota/support_command.ino @@ -483,13 +483,6 @@ void CmndStatus(void) MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS "11")); } - if ((0 == payload) || (12 == payload)) { - Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS12_STATUS "\":")); - CrashDump(); - ResponseJsonEnd(); - MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS "12")); - } - #ifdef USE_SCRIPT_STATUS if (bitRead(Settings.rule_enabled, 0)) Run_Scripter(">U",2,mqtt_data); #endif @@ -571,9 +564,6 @@ void CmndRestart(void) restart_flag = 2; ResponseCmndChar(D_JSON_RESTARTING); break; - case -1: - CmndCrash(); // force a crash - break; case 99: AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_APPLICATION D_RESTARTING)); EspRestart(); diff --git a/tasmota/support_crash_recorder.ino b/tasmota/support_crash_recorder.ino deleted file mode 100644 index abd948c39..000000000 --- a/tasmota/support_crash_recorder.ino +++ /dev/null @@ -1,76 +0,0 @@ -/* - support_crash_recorder.ino - record the call stack in RTC in cas of crash - - Copyright (C) 2019 Stephan Hadinger, Theo Arends, - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -const uint32_t dump_max_len = 64; // dump only 64 call addresses - -/** - * Save crash information in RTC memory - * This function is called automatically if ESP8266 suffers an exception - * It should be kept quick / consise to be able to execute before hardware wdt may kick in - */ -extern "C" void custom_crash_callback(struct rst_info * rst_info, uint32_t stack, uint32_t stack_end ) { - uint32_t addr_written = 0; // how many addresses have we already written in RTC - uint32_t value; // 4 bytes buffer to write to RTC - - for (uint32_t i = stack; i < stack_end; i += 4) { - value = *((uint32_t*) i); // load value from stack - if ((value >= 0x40000000) && (value < 0x40300000)) { // keep only addresses in code area - ESP.rtcUserMemoryWrite(addr_written, (uint32_t*)&value, sizeof(value)); - addr_written++; - if (addr_written >= dump_max_len) { break; } // we store only 64 addresses - } - } - // fill the rest of RTC with zeros - value = 0; - while (addr_written < dump_max_len) { - ESP.rtcUserMemoryWrite(addr_written++, (uint32_t*)&value, sizeof(value)); - } -} - -// Generate a crash to test the crash recorder -void CmndCrash(void) -{ - volatile uint32_t dummy; - dummy = *((uint32_t*) 0x00000000); -} - -// Clear the RTC dump area when we do a normal reboot, this avoids garbage data to stay in RTC -void CrashDumpClear(void) { - uint32_t value = 0; - for (uint32_t i = 0; i < dump_max_len; i++) { - ESP.rtcUserMemoryWrite(i, (uint32_t*)&value, sizeof(value)); - } -} - -/*********************************************************************************************\ - * CmndCrashDump - dump the crash history - called by `Status 12` -\*********************************************************************************************/ -void CrashDump(void) -{ - ResponseAppend_P(PSTR("{\"call_chain\":[")); - for (uint32_t i = 0; i < dump_max_len; i++) { - uint32_t value; - ESP.rtcUserMemoryRead(i, (uint32_t*)&value, sizeof(value)); - if ((value >= 0x40000000) && (value < 0x40300000)) { - if (i > 0) { ResponseAppend_P(PSTR(",")); } - ResponseAppend_P(PSTR("\"%08x\""), value); - } - } - ResponseAppend_P(PSTR("]}")); -} diff --git a/tasmota/support_wifi.ino b/tasmota/support_wifi.ino index 06d922b26..49faa0663 100644 --- a/tasmota/support_wifi.ino +++ b/tasmota/support_wifi.ino @@ -617,7 +617,6 @@ void WifiShutdown(void) void EspRestart(void) { WifiShutdown(); - CrashDumpClear(); // Clear the stack dump in RTC // ESP.restart(); // This results in exception 3 on restarts on core 2.3.0 ESP.reset(); } diff --git a/tasmota/tasmota.h b/tasmota/tasmota.h index c31bfad83..cefef3a06 100644 --- a/tasmota/tasmota.h +++ b/tasmota/tasmota.h @@ -131,7 +131,7 @@ 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 uint32_t ZIGBEE_POLLING = 100; // Serial receive polling in ms -const uint8_t MAX_STATUS = 12; // Max number of status lines +const uint8_t MAX_STATUS = 11; // Max number of status lines const uint32_t START_VALID_TIME = 1451602800; // Time is synced and after 2016-01-01 From f3a5b033c8ca527da001dc34121a45f269f98f78 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Sat, 7 Dec 2019 18:32:39 +0100 Subject: [PATCH 29/84] Add save call stack in RTC memory in case of crash Add save call stack in RTC memory in case of crash, command ``Status 12`` to dump the stack (#7114, #5883) --- tasmota/CHANGELOG.md | 1 + tasmota/i18n.h | 1 + tasmota/support_command.ino | 10 ++++ tasmota/support_crash_recorder.ino | 78 ++++++++++++++++++++++++++++++ tasmota/support_wifi.ino | 1 + tasmota/tasmota.h | 2 +- 6 files changed, 92 insertions(+), 1 deletion(-) create mode 100644 tasmota/support_crash_recorder.ino diff --git a/tasmota/CHANGELOG.md b/tasmota/CHANGELOG.md index 4bb5447ad..10c3c6941 100644 --- a/tasmota/CHANGELOG.md +++ b/tasmota/CHANGELOG.md @@ -3,6 +3,7 @@ ### 7.1.2.2 20191206 - Add command ``SerialConfig 0..23`` or ``SerialConfig 8N1`` to select Serial Config (#7108) +- Add save call stack in RTC memory in case of crash, command ``Status 12`` to dump the stack ### 7.1.2.1 20191206 diff --git a/tasmota/i18n.h b/tasmota/i18n.h index dfa998474..f913c14d2 100644 --- a/tasmota/i18n.h +++ b/tasmota/i18n.h @@ -208,6 +208,7 @@ #define D_STATUS9_MARGIN "PTH" #define D_STATUS10_SENSOR "SNS" #define D_STATUS11_STATUS "STS" + #define D_STATUS12_STATUS "STK" #define D_CMND_STATE "State" #define D_CMND_POWER "Power" #define D_CMND_FANSPEED "FanSpeed" diff --git a/tasmota/support_command.ino b/tasmota/support_command.ino index 070409566..f6baeef4e 100644 --- a/tasmota/support_command.ino +++ b/tasmota/support_command.ino @@ -483,6 +483,13 @@ void CmndStatus(void) MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS "11")); } + if ((0 == payload) || (12 == payload)) { + Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS12_STATUS "\":")); + CrashDump(); + ResponseJsonEnd(); + MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS "12")); + } + #ifdef USE_SCRIPT_STATUS if (bitRead(Settings.rule_enabled, 0)) Run_Scripter(">U",2,mqtt_data); #endif @@ -564,6 +571,9 @@ void CmndRestart(void) restart_flag = 2; ResponseCmndChar(D_JSON_RESTARTING); break; + case -1: + CmndCrash(); // force a crash + break; case 99: AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_APPLICATION D_RESTARTING)); EspRestart(); diff --git a/tasmota/support_crash_recorder.ino b/tasmota/support_crash_recorder.ino new file mode 100644 index 000000000..2c217e2ba --- /dev/null +++ b/tasmota/support_crash_recorder.ino @@ -0,0 +1,78 @@ +/* + support_crash_recorder.ino - record the call stack in RTC in cas of crash + + Copyright (C) 2019 Stephan Hadinger, Theo Arends, + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +const uint32_t dump_max_len = 63; // dump only 64 call addresses + +/** + * Save crash information in RTC memory + * This function is called automatically if ESP8266 suffers an exception + * It should be kept quick / consise to be able to execute before hardware wdt may kick in + */ +extern "C" void custom_crash_callback(struct rst_info * rst_info, uint32_t stack, uint32_t stack_end ) { + uint32_t addr_written = 0; // how many addresses have we already written in RTC + uint32_t value; // 4 bytes buffer to write to RTC + + for (uint32_t i = stack; i < stack_end; i += 4) { + value = *((uint32_t*) i); // load value from stack + if ((value >= 0x40000000) && (value < 0x40300000)) { // keep only addresses in code area + ESP.rtcUserMemoryWrite(addr_written, (uint32_t*)&value, sizeof(value)); + addr_written++; + if (addr_written >= dump_max_len) { break; } // we store only 63 addresses + } + } + // fill the rest of RTC with the amount of addresses + value = addr_written; + while (addr_written < dump_max_len +1) { + ESP.rtcUserMemoryWrite(addr_written++, (uint32_t*)&value, sizeof(value)); + } +} + +// Generate a crash to test the crash recorder +void CmndCrash(void) +{ + volatile uint32_t dummy; + dummy = *((uint32_t*) 0x00000000); +} + +// Clear the RTC dump counter when we do a normal reboot, this avoids garbage data to stay in RTC +void CrashDumpClear(void) { + uint32_t value = 0; + ESP.rtcUserMemoryWrite(dump_max_len, (uint32_t*)&value, sizeof(value)); +} + +/*********************************************************************************************\ + * CmndCrashDump - dump the crash history - called by `Status 12` +\*********************************************************************************************/ +void CrashDump(void) +{ + ResponseAppend_P(PSTR("{\"call_chain\":[")); + + uint32_t value; + ESP.rtcUserMemoryRead(dump_max_len, (uint32_t*)&value, sizeof(value)); + uint32_t count = value & 0x3F; + + for (uint32_t i = 0; i < count; i++) { + ESP.rtcUserMemoryRead(i, (uint32_t*)&value, sizeof(value)); + if ((value >= 0x40000000) && (value < 0x40300000)) { + if (i > 0) { ResponseAppend_P(PSTR(",")); } + ResponseAppend_P(PSTR("\"%08x\""), value); + } + } + ResponseAppend_P(PSTR("]}")); +} diff --git a/tasmota/support_wifi.ino b/tasmota/support_wifi.ino index 49faa0663..06d922b26 100644 --- a/tasmota/support_wifi.ino +++ b/tasmota/support_wifi.ino @@ -617,6 +617,7 @@ void WifiShutdown(void) void EspRestart(void) { WifiShutdown(); + CrashDumpClear(); // Clear the stack dump in RTC // ESP.restart(); // This results in exception 3 on restarts on core 2.3.0 ESP.reset(); } diff --git a/tasmota/tasmota.h b/tasmota/tasmota.h index cefef3a06..c31bfad83 100644 --- a/tasmota/tasmota.h +++ b/tasmota/tasmota.h @@ -131,7 +131,7 @@ 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 uint32_t ZIGBEE_POLLING = 100; // Serial receive polling in ms -const uint8_t MAX_STATUS = 11; // Max number of status lines +const uint8_t MAX_STATUS = 12; // Max number of status lines const uint32_t START_VALID_TIME = 1451602800; // Time is synced and after 2016-01-01 From 368e9cc34028500725734e0cc050f28ef06fbe0d Mon Sep 17 00:00:00 2001 From: Federico Leoni Date: Sat, 7 Dec 2019 15:20:47 -0300 Subject: [PATCH 30/84] Update xdrv_12_home_assistant.ino --- tasmota/xdrv_12_home_assistant.ino | 1 + 1 file changed, 1 insertion(+) diff --git a/tasmota/xdrv_12_home_assistant.ino b/tasmota/xdrv_12_home_assistant.ino index 38c36ead1..22c0e370e 100644 --- a/tasmota/xdrv_12_home_assistant.ino +++ b/tasmota/xdrv_12_home_assistant.ino @@ -86,6 +86,7 @@ const char HASS_DISCOVER_SENSOR[] PROGMEM = "{\"name\":\"%s\"," // dualr2 1 BTN "\"stat_t\":\"%s\"," // cmnd/dualr2/POWER (implies "\"optimistic\":\"false\",") "\"avty_t\":\"%s\"," // tele/dualr2/LWT + "\"frc_upd\":true," // force update for better graph representation "\"pl_avail\":\"" D_ONLINE "\"," // Online "\"pl_not_avail\":\"" D_OFFLINE "\""; // Offline From dae622b2cb2aa0e2e0ecf3cfc770b3cd87f24c24 Mon Sep 17 00:00:00 2001 From: Federico Leoni Date: Sat, 7 Dec 2019 15:44:33 -0300 Subject: [PATCH 31/84] Update my_user_config.h --- tasmota/my_user_config.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tasmota/my_user_config.h b/tasmota/my_user_config.h index 7ef154d53..d780f3d35 100644 --- a/tasmota/my_user_config.h +++ b/tasmota/my_user_config.h @@ -284,7 +284,7 @@ #define DOMOTICZ_OUT_TOPIC "domoticz/out" // Domoticz Output Topic // -- MQTT - Home Assistant Discovery ------------- -#define USE_HOME_ASSISTANT // Enable Home Assistant Discovery Support (+7k code) +#define USE_HOME_ASSISTANT // Enable Home Assistant Discovery Support (+4.1k code, +6 bytes mem) #define HOME_ASSISTANT_DISCOVERY_PREFIX "homeassistant" // Home Assistant discovery prefix // -- MQTT - TLS - AWS IoT ------------------------ From fd079c57a3fd3861e6540f2e975b0e3a8796d333 Mon Sep 17 00:00:00 2001 From: Hadinger Date: Sat, 7 Dec 2019 23:25:56 +0100 Subject: [PATCH 32/84] Fix crashdump bad JSON --- tasmota/support_crash_recorder.ino | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/tasmota/support_crash_recorder.ino b/tasmota/support_crash_recorder.ino index 2c217e2ba..c89fa681b 100644 --- a/tasmota/support_crash_recorder.ino +++ b/tasmota/support_crash_recorder.ino @@ -69,10 +69,8 @@ void CrashDump(void) for (uint32_t i = 0; i < count; i++) { ESP.rtcUserMemoryRead(i, (uint32_t*)&value, sizeof(value)); - if ((value >= 0x40000000) && (value < 0x40300000)) { - if (i > 0) { ResponseAppend_P(PSTR(",")); } - ResponseAppend_P(PSTR("\"%08x\""), value); - } + if (i > 0) { ResponseAppend_P(PSTR(",")); } + ResponseAppend_P(PSTR("\"%08x\""), value); } ResponseAppend_P(PSTR("]}")); } From 29d5d68ca05c807d987cb744d0673f438219552e Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Sun, 8 Dec 2019 13:18:15 +0100 Subject: [PATCH 33/84] Redesign Exception reporting Redesign Exception reporting removing exception details from both MQTT info and Status 1. Now consolidated in Status 12 if available. --- tasmota/support.ino | 6 ---- tasmota/support_command.ino | 17 +++++---- tasmota/support_crash_recorder.ino | 55 ++++++++++++++++++------------ tasmota/xdrv_02_mqtt.ino | 2 +- 4 files changed, 46 insertions(+), 34 deletions(-) diff --git a/tasmota/support.ino b/tasmota/support.ino index f44d14eb7..14f21db01 100644 --- a/tasmota/support.ino +++ b/tasmota/support.ino @@ -107,12 +107,6 @@ String GetResetReason(void) } } -String GetResetReasonInfo(void) -{ - // "Fatal exception:0 flag:2 (EXCEPTION) epc1:0x704022a7 epc2:0x00000000 epc3:0x00000000 excvaddr:0x00000000 depc:0x00000000" - return (ResetReason() == REASON_EXCEPTION_RST) ? ESP.getResetInfo() : GetResetReason(); -} - /*********************************************************************************************\ * Miscellaneous \*********************************************************************************************/ diff --git a/tasmota/support_command.ino b/tasmota/support_command.ino index f6baeef4e..09495f55a 100644 --- a/tasmota/support_command.ino +++ b/tasmota/support_command.ino @@ -334,6 +334,9 @@ void CmndStatus(void) if ((!Settings.flag.mqtt_enabled) && (6 == payload)) { payload = 99; } // SetOption3 - Enable MQTT if (!energy_flg && (9 == payload)) { payload = 99; } + bool exception_flg = (ResetReason() == REASON_EXCEPTION_RST); + if (!exception_flg && (12 == payload)) { payload = 99; } + if ((0 == payload) || (99 == payload)) { uint32_t maxfn = (devices_present > MAX_FRIENDLYNAMES) ? MAX_FRIENDLYNAMES : (!devices_present) ? 1 : devices_present; #ifdef USE_SONOFF_IFAN @@ -369,7 +372,7 @@ void CmndStatus(void) 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, - GetResetReasonInfo().c_str(), GetUptime().c_str(), GetDateAndTime(DT_RESTART).c_str(), Settings.sleep, + 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")); } @@ -483,11 +486,13 @@ void CmndStatus(void) MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS "11")); } - if ((0 == payload) || (12 == payload)) { - Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS12_STATUS "\":")); - CrashDump(); - ResponseJsonEnd(); - MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS "12")); + if (exception_flg) { + if ((0 == payload) || (12 == payload)) { + Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS12_STATUS "\":")); + CrashDump(); + ResponseJsonEnd(); + MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS "12")); + } } #ifdef USE_SCRIPT_STATUS diff --git a/tasmota/support_crash_recorder.ino b/tasmota/support_crash_recorder.ino index c89fa681b..87009bbc5 100644 --- a/tasmota/support_crash_recorder.ino +++ b/tasmota/support_crash_recorder.ino @@ -1,5 +1,5 @@ /* - support_crash_recorder.ino - record the call stack in RTC in cas of crash + support_crash_recorder.ino - record the call stack in RTC in case of crash Copyright (C) 2019 Stephan Hadinger, Theo Arends, @@ -17,30 +17,30 @@ along with this program. If not, see . */ -const uint32_t dump_max_len = 63; // dump only 64 call addresses +const uint32_t crash_magic = 0x53415400; // Stack trace magic number (TASx) +const uint32_t crash_rtc_offset = 32; // Offset in RTC memory skipping OTA used block +const uint32_t crash_dump_max_len = 31; // Dump only 31 call addresses to satisfy max JSON length of about 600 characters /** * Save crash information in RTC memory * This function is called automatically if ESP8266 suffers an exception * It should be kept quick / consise to be able to execute before hardware wdt may kick in */ -extern "C" void custom_crash_callback(struct rst_info * rst_info, uint32_t stack, uint32_t stack_end ) { +extern "C" void custom_crash_callback(struct rst_info * rst_info, uint32_t stack, uint32_t stack_end ) +{ uint32_t addr_written = 0; // how many addresses have we already written in RTC uint32_t value; // 4 bytes buffer to write to RTC for (uint32_t i = stack; i < stack_end; i += 4) { value = *((uint32_t*) i); // load value from stack if ((value >= 0x40000000) && (value < 0x40300000)) { // keep only addresses in code area - ESP.rtcUserMemoryWrite(addr_written, (uint32_t*)&value, sizeof(value)); + ESP.rtcUserMemoryWrite(crash_rtc_offset + addr_written, (uint32_t*)&value, sizeof(value)); addr_written++; - if (addr_written >= dump_max_len) { break; } // we store only 63 addresses + if (addr_written >= crash_dump_max_len) { break; } // we store only 31 addresses } } - // fill the rest of RTC with the amount of addresses - value = addr_written; - while (addr_written < dump_max_len +1) { - ESP.rtcUserMemoryWrite(addr_written++, (uint32_t*)&value, sizeof(value)); - } + value = crash_magic + addr_written; + ESP.rtcUserMemoryWrite(crash_rtc_offset + crash_dump_max_len, (uint32_t*)&value, sizeof(value)); } // Generate a crash to test the crash recorder @@ -51,26 +51,39 @@ void CmndCrash(void) } // Clear the RTC dump counter when we do a normal reboot, this avoids garbage data to stay in RTC -void CrashDumpClear(void) { +void CrashDumpClear(void) +{ uint32_t value = 0; - ESP.rtcUserMemoryWrite(dump_max_len, (uint32_t*)&value, sizeof(value)); + ESP.rtcUserMemoryWrite(crash_rtc_offset + crash_dump_max_len, (uint32_t*)&value, sizeof(value)); } /*********************************************************************************************\ * CmndCrashDump - dump the crash history - called by `Status 12` \*********************************************************************************************/ + void CrashDump(void) { - ResponseAppend_P(PSTR("{\"call_chain\":[")); + ResponseAppend_P(PSTR("{\"Exception\":%d,\"Reason\":\"%s\",\"EPC\":[\"%08x\",\"%08x\",\"%08x\"],\"EXCVADDR\":\"%08x\",\"DEPC\":\"%08x\""), + resetInfo.exccause, // Exception Cause + GetResetReason().c_str(), // Reset Reason + resetInfo.epc1, // Exception Progam Counter + resetInfo.epc2, // Exception Progam Counter - High-Priority Interrupt 1 + resetInfo.epc3, // Exception Progam Counter - High-Priority Interrupt 2 + resetInfo.excvaddr, // Exception Virtual Address Register - Virtual address that caused last fetch, load, or store exception + resetInfo.depc); // Double Exception Program Counter uint32_t value; - ESP.rtcUserMemoryRead(dump_max_len, (uint32_t*)&value, sizeof(value)); - uint32_t count = value & 0x3F; - - for (uint32_t i = 0; i < count; i++) { - ESP.rtcUserMemoryRead(i, (uint32_t*)&value, sizeof(value)); - if (i > 0) { ResponseAppend_P(PSTR(",")); } - ResponseAppend_P(PSTR("\"%08x\""), value); + ESP.rtcUserMemoryRead(crash_rtc_offset + crash_dump_max_len, (uint32_t*)&value, sizeof(value)); + if (crash_magic == (value & 0xFFFFFF00)) { + ResponseAppend_P(PSTR(",\"CallChain\":[")); + uint32_t count = value & 0x3F; + for (uint32_t i = 0; i < count; i++) { + ESP.rtcUserMemoryRead(crash_rtc_offset +i, (uint32_t*)&value, sizeof(value)); + if (i > 0) { ResponseAppend_P(PSTR(",")); } + ResponseAppend_P(PSTR("\"%08x\""), value); + } + ResponseAppend_P(PSTR("]")); } - ResponseAppend_P(PSTR("]}")); + + ResponseJsonEnd(); } diff --git a/tasmota/xdrv_02_mqtt.ino b/tasmota/xdrv_02_mqtt.ino index a6994ebfb..d31d0a860 100644 --- a/tasmota/xdrv_02_mqtt.ino +++ b/tasmota/xdrv_02_mqtt.ino @@ -555,7 +555,7 @@ void MqttConnected(void) MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_INFO "2")); } #endif // USE_WEBSERVER - Response_P(PSTR("{\"" D_JSON_RESTARTREASON "\":\"%s\"}"), GetResetReasonInfo().c_str()); + Response_P(PSTR("{\"" D_JSON_RESTARTREASON "\":\"%s\"}"), GetResetReason().c_str()); MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_INFO "3")); MqttPublishAllPowerState(); if (Settings.tele_period) { From c06008935878a84676c8df9eb95d8aabb7acab27 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Sun, 8 Dec 2019 13:37:18 +0100 Subject: [PATCH 34/84] Bump version to 7.1.2.3 Redesign Exception reporting removing exception details from both MQTT info and Status 1. Now consolidated in Status 12 if available. --- tasmota/CHANGELOG.md | 4 ++++ tasmota/tasmota_version.h | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/tasmota/CHANGELOG.md b/tasmota/CHANGELOG.md index 10c3c6941..240bca728 100644 --- a/tasmota/CHANGELOG.md +++ b/tasmota/CHANGELOG.md @@ -1,5 +1,9 @@ ## Unreleased (development) +### 7.1.2.3 20191208 + +- Redesign Exception reporting removing exception details from both MQTT info and Status 1. Now consolidated in Status 12 if available. + ### 7.1.2.2 20191206 - Add command ``SerialConfig 0..23`` or ``SerialConfig 8N1`` to select Serial Config (#7108) diff --git a/tasmota/tasmota_version.h b/tasmota/tasmota_version.h index 929924840..9b94a31f5 100644 --- a/tasmota/tasmota_version.h +++ b/tasmota/tasmota_version.h @@ -20,6 +20,6 @@ #ifndef _TASMOTA_VERSION_H_ #define _TASMOTA_VERSION_H_ -const uint32_t VERSION = 0x07010202; +const uint32_t VERSION = 0x07010203; #endif // _TASMOTA_VERSION_H_ From 46566952a052b1a4a08f4f86547373160c0c7f20 Mon Sep 17 00:00:00 2001 From: Andreas Schultz Date: Sun, 8 Dec 2019 19:42:08 +0100 Subject: [PATCH 35/84] report Signal Strength in dBm over MQTT Also include dBm in debug log and in WebIf information display. --- tasmota/i18n.h | 1 + tasmota/support.ino | 2 +- tasmota/support_tasmota.ino | 5 +++-- tasmota/xdrv_01_webserver.ino | 6 +++--- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/tasmota/i18n.h b/tasmota/i18n.h index f913c14d2..9b0457e19 100644 --- a/tasmota/i18n.h +++ b/tasmota/i18n.h @@ -134,6 +134,7 @@ #define D_JSON_SELECTED "selected" #define D_JSON_SERIALRECEIVED "SerialReceived" #define D_JSON_SET "Set" +#define D_JSON_SIGNAL "Signal" #define D_JSON_SSID "SSId" #define D_JSON_STARTDST "StartDST" // Start Daylight Savings Time #define D_JSON_STARTED "Started" diff --git a/tasmota/support.ino b/tasmota/support.ino index 14f21db01..ef3b8d9ee 100644 --- a/tasmota/support.ino +++ b/tasmota/support.ino @@ -51,7 +51,7 @@ void OsWatchTicker(void) uint32_t last_run = abs(t - oswatch_last_loop_time); #ifdef DEBUG_THEO - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION D_OSWATCH " FreeRam %d, rssi %d, last_run %d"), ESP.getFreeHeap(), WifiGetRssiAsQuality(WiFi.RSSI()), last_run); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION D_OSWATCH " FreeRam %d, rssi %d %% (%d dBm), last_run %d"), ESP.getFreeHeap(), WifiGetRssiAsQuality(WiFi.RSSI()), WiFi.RSSI(), last_run); #endif // DEBUG_THEO if (last_run >= (OSWATCH_RESET_TIME * 1000)) { // AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_APPLICATION D_OSWATCH " " D_BLOCKED_LOOP ". " D_RESTARTING)); // Save iram space diff --git a/tasmota/support_tasmota.ino b/tasmota/support_tasmota.ino index 96cec9c5a..2ab8fa0d7 100644 --- a/tasmota/support_tasmota.ino +++ b/tasmota/support_tasmota.ino @@ -583,8 +583,9 @@ void MqttShowState(void) MqttShowPWMState(); } - 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()); + 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_SIGNAL "\":%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()), WiFi.RSSI(), WifiLinkCount(), WifiDowntime().c_str()); } void MqttPublishTeleState(void) diff --git a/tasmota/xdrv_01_webserver.ino b/tasmota/xdrv_01_webserver.ino index 48e2cd26a..15707d224 100644 --- a/tasmota/xdrv_01_webserver.ino +++ b/tasmota/xdrv_01_webserver.ino @@ -1638,11 +1638,11 @@ void HandleWifiConfiguration(void) int quality = WifiGetRssiAsQuality(WiFi.RSSI(indices[i])); int auth = WiFi.encryptionType(indices[i]); char encryption[20]; - WSContentSend_P(PSTR("
%s (%d) %s %d%%
"), + WSContentSend_P(PSTR("
%s (%d) %s %d%% (%d dBm)
"), HtmlEscape(WiFi.SSID(indices[i])).c_str(), WiFi.channel(indices[i]), GetTextIndexed(encryption, sizeof(encryption), auth +1, kEncryptionType), - quality + quality, WiFi.RSSI() ); delay(0); @@ -1970,7 +1970,7 @@ void HandleInformation(void) WSContentSend_P(PSTR("}1" D_FRIENDLY_NAME " %d}2%s"), i +1, Settings.friendlyname[i]); } WSContentSend_P(PSTR("}1}2 ")); // Empty line - WSContentSend_P(PSTR("}1" D_AP "%d " D_SSID " (" D_RSSI ")}2%s (%d%%)"), Settings.sta_active +1, Settings.sta_ssid[Settings.sta_active], WifiGetRssiAsQuality(WiFi.RSSI())); + WSContentSend_P(PSTR("}1" D_AP "%d " D_SSID " (" D_RSSI ")}2%s (%d%%, %d dBm)"), Settings.sta_active +1, Settings.sta_ssid[Settings.sta_active], WifiGetRssiAsQuality(WiFi.RSSI()), WiFi.RSSI()); WSContentSend_P(PSTR("}1" D_HOSTNAME "}2%s%s"), my_hostname, (Wifi.mdns_begun) ? ".local" : ""); if (static_cast(WiFi.localIP()) != 0) { WSContentSend_P(PSTR("}1" D_IP_ADDRESS "}2%s"), WiFi.localIP().toString().c_str()); From 6c3d02928617fa34291088df1d87ee2d75098e60 Mon Sep 17 00:00:00 2001 From: Shantur Rathore Date: Fri, 29 Nov 2019 17:28:25 +0000 Subject: [PATCH 36/84] Implement capability for setting CORS to a specific website instead of just enable for all websites --- tasmota/i18n.h | 1 + tasmota/language/bg-BG.h | 1 + tasmota/language/cs-CZ.h | 1 + tasmota/language/de-DE.h | 1 + tasmota/language/el-GR.h | 1 + tasmota/language/en-GB.h | 1 + tasmota/language/es-ES.h | 1 + tasmota/language/fr-FR.h | 1 + tasmota/language/he-HE.h | 1 + tasmota/language/hu-HU.h | 1 + tasmota/language/it-IT.h | 1 + tasmota/language/ko-KO.h | 1 + tasmota/language/nl-NL.h | 1 + tasmota/language/pl-PL.h | 1 + tasmota/language/pt-BR.h | 1 + tasmota/language/pt-PT.h | 1 + tasmota/language/ru-RU.h | 1 + tasmota/language/sk-SK.h | 1 + tasmota/language/sv-SE.h | 1 + tasmota/language/tr-TR.h | 1 + tasmota/language/uk-UK.h | 1 + tasmota/language/zh-CN.h | 1 + tasmota/language/zh-TW.h | 1 + tasmota/my_user_config.h | 6 +++--- tasmota/settings.h | 5 +++-- tasmota/settings.ino | 13 +++++++++++++ tasmota/tasmota_version.h | 2 +- tasmota/xdrv_01_webserver.ino | 25 ++++++++++++++++++------- 28 files changed, 61 insertions(+), 13 deletions(-) diff --git a/tasmota/i18n.h b/tasmota/i18n.h index 9b0457e19..f4fde64b3 100644 --- a/tasmota/i18n.h +++ b/tasmota/i18n.h @@ -335,6 +335,7 @@ #define D_CMND_WEBSENSOR "WebSensor" #define D_CMND_EMULATION "Emulation" #define D_CMND_SENDMAIL "Sendmail" +#define D_CMND_CORS "CORS" // Commands xdrv_03_energy.ino #define D_CMND_POWERLOW "PowerLow" diff --git a/tasmota/language/bg-BG.h b/tasmota/language/bg-BG.h index 4bc5feebd..d39aba5f6 100644 --- a/tasmota/language/bg-BG.h +++ b/tasmota/language/bg-BG.h @@ -71,6 +71,7 @@ #define D_COLDLIGHT "Хладна" #define D_COMMAND "Команда" #define D_CONNECTED "Свързан" +#define D_CORS_DOMAIN "CORS Domain" #define D_COUNT "Брой" #define D_COUNTER "Брояч" #define D_CURRENT "Ток" // As in Voltage and Current diff --git a/tasmota/language/cs-CZ.h b/tasmota/language/cs-CZ.h index 8800368a3..1d7c6f381 100644 --- a/tasmota/language/cs-CZ.h +++ b/tasmota/language/cs-CZ.h @@ -71,6 +71,7 @@ #define D_COLDLIGHT "Studené světlo" #define D_COMMAND "Příkaz" #define D_CONNECTED "...připojeno" +#define D_CORS_DOMAIN "CORS Domain" #define D_COUNT "Počítej" #define D_COUNTER "Počítadlo" #define D_CURRENT "Proud" // As in Voltage and Current diff --git a/tasmota/language/de-DE.h b/tasmota/language/de-DE.h index c50de823f..6569dd232 100644 --- a/tasmota/language/de-DE.h +++ b/tasmota/language/de-DE.h @@ -71,6 +71,7 @@ #define D_COLDLIGHT "kalt" #define D_COMMAND "Befehl" #define D_CONNECTED "verbunden" +#define D_CORS_DOMAIN "CORS Domain" #define D_COUNT "zählen" #define D_COUNTER "Zähler" #define D_CURRENT "Strom" // As in Voltage and Current diff --git a/tasmota/language/el-GR.h b/tasmota/language/el-GR.h index 4fe47ae6d..f8b9c9614 100644 --- a/tasmota/language/el-GR.h +++ b/tasmota/language/el-GR.h @@ -72,6 +72,7 @@ #define D_COMMAND "Εντολή" #define D_CONNECTED "Συνδεδεμένο" #define D_COUNT "Μέτρηση" +#define D_CORS_DOMAIN "CORS Domain" #define D_COUNTER "Μετρητής" #define D_CURRENT "Ένταση" // As in Voltage and Current #define D_DATA "Δεδομένα" diff --git a/tasmota/language/en-GB.h b/tasmota/language/en-GB.h index a98087a18..257a34c86 100644 --- a/tasmota/language/en-GB.h +++ b/tasmota/language/en-GB.h @@ -71,6 +71,7 @@ #define D_COLDLIGHT "Cold" #define D_COMMAND "Command" #define D_CONNECTED "Connected" +#define D_CORS_DOMAIN "CORS Domain" #define D_COUNT "Count" #define D_COUNTER "Counter" #define D_CURRENT "Current" // As in Voltage and Current diff --git a/tasmota/language/es-ES.h b/tasmota/language/es-ES.h index 014d822a9..7527bb77a 100644 --- a/tasmota/language/es-ES.h +++ b/tasmota/language/es-ES.h @@ -71,6 +71,7 @@ #define D_COLDLIGHT "Fría" #define D_COMMAND "Comando" #define D_CONNECTED "Conectado" +#define D_CORS_DOMAIN "CORS Domain" #define D_COUNT "Conteo" #define D_COUNTER "Contador" #define D_CURRENT "Corriente" // As in Voltage and Current diff --git a/tasmota/language/fr-FR.h b/tasmota/language/fr-FR.h index 4704c49e7..236df351b 100644 --- a/tasmota/language/fr-FR.h +++ b/tasmota/language/fr-FR.h @@ -71,6 +71,7 @@ #define D_COLDLIGHT "Froid" #define D_COMMAND "Commande" #define D_CONNECTED "Connecté" +#define D_CORS_DOMAIN "CORS Domain" #define D_COUNT "Compte" #define D_COUNTER "Compteur" #define D_CURRENT "Courant" // As in Voltage and Current diff --git a/tasmota/language/he-HE.h b/tasmota/language/he-HE.h index 69387ceca..ac4dadb8e 100644 --- a/tasmota/language/he-HE.h +++ b/tasmota/language/he-HE.h @@ -71,6 +71,7 @@ #define D_COLDLIGHT "אור קר" #define D_COMMAND "פקודה" #define D_CONNECTED "מחובר" +#define D_CORS_DOMAIN "CORS Domain" #define D_COUNT "סופר" #define D_COUNTER "מונה" #define D_CURRENT "נוכחי" // As in Voltage and Current diff --git a/tasmota/language/hu-HU.h b/tasmota/language/hu-HU.h index 58f85eb22..62806fa46 100644 --- a/tasmota/language/hu-HU.h +++ b/tasmota/language/hu-HU.h @@ -71,6 +71,7 @@ #define D_COLDLIGHT "Hideg fény" #define D_COMMAND "Parancs" #define D_CONNECTED "Csatlakoztatva" +#define D_CORS_DOMAIN "CORS Domain" #define D_COUNT "Szám" #define D_COUNTER "Számláló" #define D_CURRENT "Áramerősség" // As in Voltage and Current diff --git a/tasmota/language/it-IT.h b/tasmota/language/it-IT.h index 3029f37da..0585c12d8 100644 --- a/tasmota/language/it-IT.h +++ b/tasmota/language/it-IT.h @@ -71,6 +71,7 @@ #define D_COLDLIGHT "Fredda" #define D_COMMAND "Comando" #define D_CONNECTED "Connesso" +#define D_CORS_DOMAIN "CORS Domain" #define D_COUNT "Conteggio" #define D_COUNTER "Contatore" #define D_CURRENT "Corrente" // As in Voltage and Current diff --git a/tasmota/language/ko-KO.h b/tasmota/language/ko-KO.h index c6236a05f..bff508474 100644 --- a/tasmota/language/ko-KO.h +++ b/tasmota/language/ko-KO.h @@ -72,6 +72,7 @@ #define D_COMMAND "커맨드" #define D_CONNECTED "연결됨" #define D_COUNT "횟수" +#define D_CORS_DOMAIN "CORS Domain" #define D_COUNTER "Counter" #define D_CURRENT "전류" // As in Voltage and Current #define D_DATA "Data" diff --git a/tasmota/language/nl-NL.h b/tasmota/language/nl-NL.h index da20ff1d7..75d841ddc 100644 --- a/tasmota/language/nl-NL.h +++ b/tasmota/language/nl-NL.h @@ -72,6 +72,7 @@ #define D_COMMAND "Opdracht" #define D_CONNECTED "Verbonden" #define D_COUNT "Aantal" +#define D_CORS_DOMAIN "CORS Domain" #define D_COUNTER "Teller" #define D_CURRENT "Stroom" // As in Voltage and Current #define D_DATA "Data" diff --git a/tasmota/language/pl-PL.h b/tasmota/language/pl-PL.h index 7a631f95f..8f5af6813 100644 --- a/tasmota/language/pl-PL.h +++ b/tasmota/language/pl-PL.h @@ -71,6 +71,7 @@ #define D_COLDLIGHT "Zimny" #define D_COMMAND "Komenda" #define D_CONNECTED "Połączony" +#define D_CORS_DOMAIN "CORS Domain" #define D_COUNT "Licz" #define D_COUNTER "Licznik" #define D_CURRENT "Prąd" // As in Voltage and Current diff --git a/tasmota/language/pt-BR.h b/tasmota/language/pt-BR.h index 2eb81f294..0efa57789 100644 --- a/tasmota/language/pt-BR.h +++ b/tasmota/language/pt-BR.h @@ -71,6 +71,7 @@ #define D_COLDLIGHT "Luz fria" #define D_COMMAND "Comando" #define D_CONNECTED "Ligado" +#define D_CORS_DOMAIN "CORS Domain" #define D_COUNT "Contagem" #define D_COUNTER "Contador" #define D_CURRENT "Corrente" // As in Voltage and Current diff --git a/tasmota/language/pt-PT.h b/tasmota/language/pt-PT.h index 46bafdef6..a6299c0c8 100644 --- a/tasmota/language/pt-PT.h +++ b/tasmota/language/pt-PT.h @@ -71,6 +71,7 @@ #define D_COLDLIGHT "Luz Fria" #define D_COMMAND "Comando" #define D_CONNECTED "Ligado" +#define D_CORS_DOMAIN "CORS Domain" #define D_COUNT "Contagem" #define D_COUNTER "Contador" #define D_CURRENT "Corrente" // As in Voltage and Current diff --git a/tasmota/language/ru-RU.h b/tasmota/language/ru-RU.h index 5ff9bff50..3bd788ede 100644 --- a/tasmota/language/ru-RU.h +++ b/tasmota/language/ru-RU.h @@ -71,6 +71,7 @@ #define D_COLDLIGHT "Холодный" #define D_COMMAND "Команда" #define D_CONNECTED "Соединен" +#define D_CORS_DOMAIN "CORS Domain" #define D_COUNT "Подсчет" #define D_COUNTER "Счетчик" #define D_CURRENT "Ток" // As in Voltage and Current diff --git a/tasmota/language/sk-SK.h b/tasmota/language/sk-SK.h index 57ec74f8a..a64e49e01 100644 --- a/tasmota/language/sk-SK.h +++ b/tasmota/language/sk-SK.h @@ -71,6 +71,7 @@ #define D_COLDLIGHT "Studené svetlo" #define D_COMMAND "Príkaz" #define D_CONNECTED "...pripojené" +#define D_CORS_DOMAIN "CORS Domain" #define D_COUNT "Počítaj" #define D_COUNTER "Počítadlo" #define D_CURRENT "Prúd" // As in Voltage and Current diff --git a/tasmota/language/sv-SE.h b/tasmota/language/sv-SE.h index aee8fd25d..51ffc2ae3 100644 --- a/tasmota/language/sv-SE.h +++ b/tasmota/language/sv-SE.h @@ -71,6 +71,7 @@ #define D_COLDLIGHT "Kallt" #define D_COMMAND "Kommando" #define D_CONNECTED "Ansluten" +#define D_CORS_DOMAIN "CORS Domain" #define D_COUNT "Räkna" #define D_COUNTER "Räknare" #define D_CURRENT "Ström" // As in Voltage and Current diff --git a/tasmota/language/tr-TR.h b/tasmota/language/tr-TR.h index 78612dace..6bc48c40f 100644 --- a/tasmota/language/tr-TR.h +++ b/tasmota/language/tr-TR.h @@ -71,6 +71,7 @@ #define D_COLDLIGHT "Soğuk" #define D_COMMAND "Komut" #define D_CONNECTED "Bağlandı" +#define D_CORS_DOMAIN "CORS Domain" #define D_COUNT "Sayı" #define D_COUNTER "Sayaç" #define D_CURRENT "Current" // As in Voltage and Current diff --git a/tasmota/language/uk-UK.h b/tasmota/language/uk-UK.h index f16d66648..b4a314630 100644 --- a/tasmota/language/uk-UK.h +++ b/tasmota/language/uk-UK.h @@ -71,6 +71,7 @@ #define D_COLDLIGHT "Холодний" #define D_COMMAND "Команда" #define D_CONNECTED "Під'єднано" +#define D_CORS_DOMAIN "CORS Domain" #define D_COUNT "Розмір" #define D_COUNTER "Лічильник" #define D_CURRENT "Струм" // As in Voltage and Current diff --git a/tasmota/language/zh-CN.h b/tasmota/language/zh-CN.h index 2c8610400..21aa63be3 100644 --- a/tasmota/language/zh-CN.h +++ b/tasmota/language/zh-CN.h @@ -71,6 +71,7 @@ #define D_COLDLIGHT "冷" #define D_COMMAND "命令:" #define D_CONNECTED "已连接" +#define D_CORS_DOMAIN "CORS Domain" #define D_COUNT "数量:" #define D_COUNTER "计数器" #define D_CURRENT "电流" // As in Voltage and Current diff --git a/tasmota/language/zh-TW.h b/tasmota/language/zh-TW.h index e5ef8926a..1d78f181e 100644 --- a/tasmota/language/zh-TW.h +++ b/tasmota/language/zh-TW.h @@ -71,6 +71,7 @@ #define D_COLDLIGHT "冷" #define D_COMMAND "命令:" #define D_CONNECTED "已連接" +#define D_CORS_DOMAIN "CORS Domain" #define D_COUNT "數量:" #define D_COUNTER "Counter" #define D_CURRENT "電流" // As in Voltage and Current diff --git a/tasmota/my_user_config.h b/tasmota/my_user_config.h index d780f3d35..c643f0a6d 100644 --- a/tasmota/my_user_config.h +++ b/tasmota/my_user_config.h @@ -132,7 +132,7 @@ #define WEB_PASSWORD "" // [WebPassword] Web server Admin mode Password for WEB_USERNAME (empty string = Disable) #define FRIENDLY_NAME "Tasmota" // [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) - +#define CORS_DOMAIN "" // [CorsDomain] CORS Domain for preflight requests // -- HTTP GUI Colors ----------------------------- // HTML hex color codes. Only 3 and 6 digit hex string values are supported!! See https://www.w3schools.com/colors/colors_hex.asp // Light theme - pre v7 @@ -219,7 +219,7 @@ #define APP_BLINKTIME 10 // [BlinkTime] Time in 0.1 Sec to blink/toggle power for relay 1 #define APP_BLINKCOUNT 10 // [BlinkCount] Number of blinks (0 = 32000) #define APP_SLEEP 0 // [Sleep] Sleep time to lower energy consumption (0 = Off, 1 - 250 mSec), -#define PWM_MAX_SLEEP 10 // Sleep will be lowered to this value when light is on, to avoid flickering +#define PWM_MAX_SLEEP 10 // Sleep will be lowered to this value when light is on, to avoid flickering #define KEY_DEBOUNCE_TIME 50 // [ButtonDebounce] Number of mSeconds button press debounce time #define KEY_HOLD_TIME 40 // [SetOption32] Number of 0.1 seconds to hold Button or external Pushbutton before sending HOLD message @@ -291,7 +291,7 @@ // Using TLS starting with version v6.5.0.16 compilation will only work using Core 2.4.2 and 2.5.2. No longer supported: 2.3.0 //#define USE_MQTT_TLS // Use TLS for MQTT connection (+34.5k code, +7.0k mem and +4.8k additional during connection handshake) // #define USE_MQTT_TLS_CA_CERT // Force full CA validation instead of fingerprints, slower, but simpler to use. (+2.2k code, +1.9k mem during connection handshake) - // This includes the LetsEncrypt CA in tasmota_ca.ino for verifying server certificates + // This includes the LetsEncrypt CA in tasmota_ca.ino for verifying server certificates // #define USE_MQTT_TLS_FORCE_EC_CIPHER // Force Elliptic Curve cipher (higher security) required by some servers (automatically enabled with USE_MQTT_AWS_IOT) (+11.4k code, +0.4k mem) // #define USE_MQTT_AWS_IOT // Enable MQTT for AWS IoT - requires a private key (+11.9k code, +0.4k mem) // Note: you need to generate a private key + certificate per device and update 'tasmota/tasmota_aws_iot.cpp' diff --git a/tasmota/settings.h b/tasmota/settings.h index f464a24cb..6da25a36b 100644 --- a/tasmota/settings.h +++ b/tasmota/settings.h @@ -86,7 +86,7 @@ typedef union { // Restricted by MISRA-C Rule 18.4 bu uint32_t energy_weekend : 1; // bit 20 (v6.6.0.8) - CMND_TARIFF uint32_t dds2382_model : 1; // bit 21 (v6.6.0.14) - SetOption71 - Select different Modbus registers for Active Energy (#6531) uint32_t hardware_energy_total : 1; // bit 22 (v6.6.0.15) - SetOption72 - Enable hardware energy total counter as reference (#6561) - uint32_t cors_enabled : 1; // bit 23 (v7.0.0.1) - SetOption73 - Enable HTTP CORS + uint32_t ex_cors_enabled : 1; // bit 23 (v7.0.0.1) - SetOption73 - Enable HTTP CORS uint32_t ds18x20_internal_pullup : 1; // bit 24 (v7.0.0.1) - SetOption74 - Enable internal pullup for single DS18x20 sensor uint32_t grouptopic_mode : 1; // bit 25 (v7.0.0.1) - SetOption75 - GroupTopic replaces %topic% (0) or fixed topic cmnd/grouptopic (1) uint32_t bootcount_update : 1; // bit 26 (v7.0.0.4) - SetOption76 - Enable incrementing bootcount when deepsleep is enabled @@ -438,7 +438,8 @@ struct SYSCFG { uint8_t web_color2[2][3]; // EA0 - Needs to be on integer / 3 distance from web_color - uint8_t free_ea4[326]; // EA6 + char cors_domain[33]; // EC1 + uint8_t free_ea4[293]; // EA6 uint32_t i2c_drivers[3]; // FEC I2cDriver uint32_t cfg_timestamp; // FF8 diff --git a/tasmota/settings.ino b/tasmota/settings.ino index f6113be46..50f4fe4a6 100644 --- a/tasmota/settings.ino +++ b/tasmota/settings.ino @@ -140,6 +140,10 @@ #ifndef DEFAULT_LIGHT_COMPONENT #define DEFAULT_LIGHT_COMPONENT 255 #endif +#ifndef CORS_ENABLED_ALL +#define CORS_ENABLED_ALL "*" +#endif + enum WebColors { COL_TEXT, COL_BACKGROUND, COL_FORM, @@ -718,6 +722,7 @@ void SettingsDefaultSet2(void) Settings.weblog_level = WEB_LOG_LEVEL; strlcpy(Settings.web_password, WEB_PASSWORD, sizeof(Settings.web_password)); Settings.flag3.mdns_enabled = MDNS_ENABLED; + strlcpy(Settings.cors_domain, CORS_DOMAIN, sizeof(Settings.cors_domain)); // Button // Settings.flag.button_restrict = 0; @@ -1177,6 +1182,14 @@ void SettingsDelta(void) Settings.serial_config = TS_SERIAL_8N1; } + if (Settings.version < 0x07010204) { + if (Settings.flag3.ex_cors_enabled == 1) { + strlcpy(Settings.cors_domain, CORS_ENABLED_ALL, sizeof(Settings.cors_domain)); + } else { + Settings.cors_domain[0] = 0; + } + } + Settings.version = VERSION; SettingsSave(1); } diff --git a/tasmota/tasmota_version.h b/tasmota/tasmota_version.h index 9b94a31f5..a0cbe1e41 100644 --- a/tasmota/tasmota_version.h +++ b/tasmota/tasmota_version.h @@ -20,6 +20,6 @@ #ifndef _TASMOTA_VERSION_H_ #define _TASMOTA_VERSION_H_ -const uint32_t VERSION = 0x07010203; +const uint32_t VERSION = 0x07010204; #endif // _TASMOTA_VERSION_H_ diff --git a/tasmota/xdrv_01_webserver.ino b/tasmota/xdrv_01_webserver.ino index 15707d224..28620e21f 100644 --- a/tasmota/xdrv_01_webserver.ino +++ b/tasmota/xdrv_01_webserver.ino @@ -414,7 +414,8 @@ const char HTTP_FORM_WIFI[] PROGMEM = "

" D_AP1_PASSWORD "

" "

" D_AP2_SSID " (" STA_SSID2 ")

" "

" D_AP2_PASSWORD "

" - "

" D_HOSTNAME " (%s)

"; + "

" D_HOSTNAME " (%s)

" + "

" D_CORS_DOMAIN "

"; const char HTTP_FORM_LOG1[] PROGMEM = "
 " D_LOGGING_PARAMETERS " " @@ -673,8 +674,8 @@ bool HttpCheckPriviledgedAccess(bool autorequestauth = true) void HttpHeaderCors(void) { - if (Settings.flag3.cors_enabled) { // SetOption73 - Enable HTTP CORS - WebServer->sendHeader(F("Access-Control-Allow-Origin"), F("*")); + if (Settings.cors_domain[0] != 0) { + WebServer->sendHeader(F("Access-Control-Allow-Origin"), Settings.cors_domain); } } @@ -1654,7 +1655,7 @@ void HandleWifiConfiguration(void) } // As WIFI_HOSTNAME may contain %s-%04d it cannot be part of HTTP_FORM_WIFI where it will exception - WSContentSend_P(HTTP_FORM_WIFI, Settings.sta_ssid[0], Settings.sta_ssid[1], WIFI_HOSTNAME, WIFI_HOSTNAME, Settings.hostname); + WSContentSend_P(HTTP_FORM_WIFI, Settings.sta_ssid[0], Settings.sta_ssid[1], WIFI_HOSTNAME, WIFI_HOSTNAME, Settings.hostname, Settings.cors_domain); WSContentSend_P(HTTP_FORM_END); } @@ -1678,6 +1679,8 @@ void WifiSaveSettings(void) if (strstr(Settings.hostname, "%") != nullptr) { strlcpy(Settings.hostname, WIFI_HOSTNAME, sizeof(Settings.hostname)); } + WebGetArg("c", tmp, sizeof(tmp)); + strlcpy(Settings.cors_domain, (!strlen(tmp)) ? CORS_DOMAIN : tmp, sizeof(Settings.cors_domain)); WebGetArg("s1", tmp, sizeof(tmp)); strlcpy(Settings.sta_ssid[0], (!strlen(tmp)) ? STA_SSID1 : tmp, sizeof(Settings.sta_ssid[0])); WebGetArg("s2", tmp, sizeof(tmp)); @@ -1686,7 +1689,7 @@ void WifiSaveSettings(void) strlcpy(Settings.sta_pwd[0], (!strlen(tmp)) ? "" : (strlen(tmp) < 5) ? Settings.sta_pwd[0] : tmp, sizeof(Settings.sta_pwd[0])); WebGetArg("p2", tmp, sizeof(tmp)); strlcpy(Settings.sta_pwd[1], (!strlen(tmp)) ? "" : (strlen(tmp) < 5) ? Settings.sta_pwd[1] : tmp, sizeof(Settings.sta_pwd[1])); - AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_WIFI D_CMND_HOSTNAME " %s, " D_CMND_SSID "1 %s, " D_CMND_SSID "2 %s"), Settings.hostname, Settings.sta_ssid[0], Settings.sta_ssid[1]); + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_WIFI D_CMND_HOSTNAME " %s, " D_CMND_SSID "1 %s, " D_CMND_SSID "2 %s, " D_CMND_CORS " %s"), Settings.hostname, Settings.sta_ssid[0], Settings.sta_ssid[1], Settings.cors_domain); } /*-------------------------------------------------------------------------------------------*/ @@ -2698,7 +2701,7 @@ const char kWebCommands[] PROGMEM = "|" // No prefix #ifdef USE_SENDMAIL D_CMND_SENDMAIL "|" #endif - D_CMND_WEBSERVER "|" D_CMND_WEBPASSWORD "|" D_CMND_WEBLOG "|" D_CMND_WEBREFRESH "|" D_CMND_WEBSEND "|" D_CMND_WEBCOLOR "|" D_CMND_WEBSENSOR; + D_CMND_WEBSERVER "|" D_CMND_WEBPASSWORD "|" D_CMND_WEBLOG "|" D_CMND_WEBREFRESH "|" D_CMND_WEBSEND "|" D_CMND_WEBCOLOR "|" D_CMND_WEBSENSOR "|" D_CMND_CORS; void (* const WebCommand[])(void) PROGMEM = { #ifdef USE_EMULATION @@ -2707,7 +2710,7 @@ void (* const WebCommand[])(void) PROGMEM = { #ifdef USE_SENDMAIL &CmndSendmail, #endif - &CmndWebServer, &CmndWebPassword, &CmndWeblog, &CmndWebRefresh, &CmndWebSend, &CmndWebColor, &CmndWebSensor }; + &CmndWebServer, &CmndWebPassword, &CmndWeblog, &CmndWebRefresh, &CmndWebSend, &CmndWebColor, &CmndWebSensor, &CmndCors }; /*********************************************************************************************\ * Commands @@ -2828,6 +2831,14 @@ void CmndWebSensor(void) ResponseJsonEnd(); } +void CmndCors(void) +{ + if ((XdrvMailbox.data_len > 0) && (XdrvMailbox.data_len < sizeof(Settings.cors_domain))) { + strlcpy(Settings.cors_domain, (SC_CLEAR == Shortcut()) ? "" : (SC_DEFAULT == Shortcut()) ? WEB_PASSWORD : XdrvMailbox.data, sizeof(Settings.cors_domain)); + } + ResponseCmndChar(Settings.cors_domain); +} + /*********************************************************************************************\ * Interface \*********************************************************************************************/ From 531d36e012d69ff06dc8ebf89463c24e0cfdf820 Mon Sep 17 00:00:00 2001 From: Christian Tacke <8560110+ChristianTacke@users.noreply.github.com> Date: Sun, 8 Dec 2019 21:15:45 +0100 Subject: [PATCH 37/84] Timers: Use visibility instead of disabled The "+/-" chooser is only needed for sunrise/sunset. Otherwise it's disabled currently. This works. But it's a little confusing. Especially, if you first had "-" there (for sunset) and then switched to normal "time", then the "-" is still there, but you can't change it, because it's disabled. It looks better, if one uses .style.visibility to hide the element. It doesn't change the layout, just the element isn't shown. --- tasmota/xdrv_09_timers.ino | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tasmota/xdrv_09_timers.ino b/tasmota/xdrv_09_timers.ino index a71960695..b00b5ba24 100644 --- a/tasmota/xdrv_09_timers.ino +++ b/tasmota/xdrv_09_timers.ino @@ -554,10 +554,10 @@ const char HTTP_TIMER_SCRIPT2[] PROGMEM = "o=qs('#ho');" "e=o.childElementCount;" "if(b==1){" - "qs('#dr').disabled='';" + "qs('#dr').style.visibility='';" "if(e>12){for(i=12;i<=23;i++){o.removeChild(o.lastElementChild);}}" // Create offset hours select options "}else{" - "qs('#dr').disabled='disabled';" + "qs('#dr').style.visibility='hidden';" "if(e<23){for(i=12;i<=23;i++){ce(i,o);}}" // Create hours select options "}" "}"; From 7cc7c2cc98bdb103def842807b44f6605802afb4 Mon Sep 17 00:00:00 2001 From: stefanbode Date: Mon, 9 Dec 2019 10:14:13 +0100 Subject: [PATCH 38/84] Update xdrv_27_shutter.ino fix #7006 support for multiple shutters with steppers + add ShutterOpen/Close/Stop can get shutter index as parameter. e.g. shutteropen 3 to open shutter 3 --- tasmota/xdrv_27_shutter.ino | 33 ++++++++++++++++++++++----------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/tasmota/xdrv_27_shutter.ino b/tasmota/xdrv_27_shutter.ino index f47b53376..c97318c5f 100644 --- a/tasmota/xdrv_27_shutter.ino +++ b/tasmota/xdrv_27_shutter.ino @@ -194,10 +194,10 @@ void ShutterInit(void) } } else { Shutter.mode = SHT_OFF_ON__OPEN_CLOSE; - if (pin[GPIO_PWM1 ]+i < 99) { + if (pin[GPIO_PWM1+i] < 99) { Shutter.pwm_frequency = 0; analogWriteFreq(Shutter.pwm_frequency); - analogWrite(pin[GPIO_PWM1]+i, 50); + analogWrite(pin[GPIO_PWM1+i], 50); } } @@ -257,11 +257,11 @@ void ShutterUpdatePosition(void) // Counter should be initiated to 0 to count movement. // 0..1000 in step 100 = 10 steps with 0.05 sec = 0.5sec total ramp time from start to // full speed. - if (pin[GPIO_PWM1]+i < 99 && Shutter.pwm_frequency != Shutter.max_pwm_frequency) { + if (pin[GPIO_PWM1+i] < 99 && Shutter.pwm_frequency != Shutter.max_pwm_frequency) { Shutter.pwm_frequency += Shutter.max_pwm_frequency/20; Shutter.pwm_frequency = (Shutter.pwm_frequency > Shutter.max_pwm_frequency ? Shutter.max_pwm_frequency : Shutter.pwm_frequency); analogWriteFreq(Shutter.pwm_frequency); - analogWrite(pin[GPIO_PWM1]+i, 50); + analogWrite(pin[GPIO_PWM1+i], 50); } Shutter.real_position[i] = Shutter.start_position[i] + ( (Shutter.time[i] - Shutter.motordelay[i]) * (Shutter.direction[i] > 0 ? 100 : -Shutter.close_velocity[i])); @@ -287,21 +287,21 @@ void ShutterUpdatePosition(void) // This is a failsafe configuration. Relay1 ON/OFF Relay2 -1/1 direction // Only allow PWM microstepping if PWM and COUNTER are defined. // see wiki to connect PWM and COUNTER - if (pin[GPIO_PWM1 ]+i < 99 && pin[GPIO_CNTR1 ]+i < 99 ) { + if (pin[GPIO_PWM1+i] < 99 && pin[GPIO_CNTR1+i] < 99 ) { int16_t missing_steps = ((Shutter.target_position[i]-Shutter.start_position[i])*Shutter.direction[i]*Shutter.max_pwm_frequency/2000) - RtcSettings.pulse_counter[i]; Shutter.pwm_frequency = 0; //slow down for acurate position analogWriteFreq(500); - analogWrite(pin[GPIO_PWM1]+i, 50); + analogWrite(pin[GPIO_PWM1+i], 50); //prepare for stop PWM Shutter.motordelay[i] = -2 + Shutter.motordelay[i] + missing_steps/(Shutter.max_pwm_frequency/20); AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Missing steps %d, adjust motordelay %d, counter %d, temp realpos %d"), missing_steps, Shutter.motordelay[i],RtcSettings.pulse_counter[i] ,Shutter.real_position[i]); - Settings.shutter_motordelay[i]=Shutter.motordelay[i]; + Settings.shutter_motordelay[i]=(missing_steps > 0 ? Shutter.motordelay[i] : 0); analogWriteFreq(0); while (RtcSettings.pulse_counter[i] < (uint32_t)(Shutter.target_position[i]-Shutter.start_position[i])*Shutter.direction[i]*Shutter.max_pwm_frequency/2000) { delay(1); } - analogWrite(pin[GPIO_PWM1]+i, 0); + analogWrite(pin[GPIO_PWM1+i], 0); Shutter.real_position[i] = ((int32_t)RtcSettings.pulse_counter[i]*Shutter.direction[i]*2000 / Shutter.max_pwm_frequency)+Shutter.start_position[i]; //AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT:Realpos %d, pulsecount %d, startpos %d, int32 %d"), Shutter.real_position[i],RtcSettings.pulse_counter[i], Shutter.start_position[i], ((int32_t)RtcSettings.pulse_counter[i]*Shutter.direction[i]*2000 / Shutter.max_pwm_frequency)); @@ -357,12 +357,12 @@ void ShutterStartInit(uint8_t index, uint8_t direction, int32_t target_pos) Shutter.target_position[index] = target_pos; Shutter.start_position[index] = Shutter.real_position[index]; Shutter.time[index] = 0; - if (pin[GPIO_PWM1]+index < 99) { + if (pin[GPIO_PWM1+index] < 99) { Shutter.pwm_frequency = 0; analogWriteFreq(Shutter.pwm_frequency); - analogWrite(pin[GPIO_PWM1]+index, 0); + analogWrite(pin[GPIO_PWM1+index], 0); // can be operated without counter, but then not that acurate. - if (pin[GPIO_CNTR1]+index < 99) { + if (pin[GPIO_CNTR1+index] < 99) { RtcSettings.pulse_counter[index] = 0; } } @@ -459,6 +459,10 @@ void ShutterSetPosition(uint8_t device, uint8_t position) void CmndShutterOpen(void) { + //AddLog_P2(LOG_LEVEL_INFO, PSTR("SHT: Payload close: %d, index %d"), XdrvMailbox.payload, XdrvMailbox.index); + if ( XdrvMailbox.index == 1 && XdrvMailbox.payload != -99) { + XdrvMailbox.index = XdrvMailbox.payload; + } XdrvMailbox.payload = 100; last_source = SRC_WEBGUI; CmndShutterPosition(); @@ -466,6 +470,10 @@ void CmndShutterOpen(void) void CmndShutterClose(void) { + //AddLog_P2(LOG_LEVEL_INFO, PSTR("SHT: Payload open: %d, index %d"), XdrvMailbox.payload, XdrvMailbox.index); + if ( XdrvMailbox.index == 1 && XdrvMailbox.payload != -99) { + XdrvMailbox.index = XdrvMailbox.payload; + } XdrvMailbox.payload = 0; XdrvMailbox.data_len = 0; last_source = SRC_WEBGUI; @@ -475,6 +483,9 @@ void CmndShutterClose(void) void CmndShutterStop(void) { if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= shutters_present)) { + if ( XdrvMailbox.index == 1 && XdrvMailbox.payload != -99) { + XdrvMailbox.index = XdrvMailbox.payload; + } uint32_t index = XdrvMailbox.index -1; if (Shutter.direction[index] != 0) { From dfee90bd61c70a96c8c28583258e07b85a23cf4c Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Mon, 9 Dec 2019 15:15:16 +0100 Subject: [PATCH 39/84] Changelog update --- tasmota/CHANGELOG.md | 21 +++++++++++++++------ tasmota/my_user_config.h | 3 ++- tasmota/settings.h | 6 +++--- tasmota/settings.ino | 1 - 4 files changed, 20 insertions(+), 11 deletions(-) diff --git a/tasmota/CHANGELOG.md b/tasmota/CHANGELOG.md index 240bca728..826aad3b6 100644 --- a/tasmota/CHANGELOG.md +++ b/tasmota/CHANGELOG.md @@ -1,20 +1,29 @@ ## Unreleased (development) +### 7.1.2.4 20191209 + +- Change HTTP CORS from command ``SetOption73 0/1`` to ``Cors `` allowing user control of specific CORS domain by Shantur Rathore (#7066) +- Add Wifi Signal Strength in dBm in addition to RSSI Wifi Experience by Andreas Schultz (#7145) +- Add Yaw, Pitch and Roll support for MPU6050 by Philip Barclay (#7058) + ### 7.1.2.3 20191208 - Redesign Exception reporting removing exception details from both MQTT info and Status 1. Now consolidated in Status 12 if available. ### 7.1.2.2 20191206 -- Add command ``SerialConfig 0..23`` or ``SerialConfig 8N1`` to select Serial Config (#7108) -- Add save call stack in RTC memory in case of crash, command ``Status 12`` to dump the stack +- Remove rule trigger ``tele_power1#state`` due to compatibility +- Add command ``SerialConfig 0..23`` or ``SerialConfig 8N1`` to select Serial Config based in PR by Luis Teixeira (#7108) +- Add save call stack in RTC memory in case of crash, command ``Status 12`` to dump the stack by Stefan Hadinger +- Add Home Assistant force update by Frederico Leoni (#7140, #7074) ### 7.1.2.1 20191206 -- Add rule var ``%topic%`` (#5522) -- Add rule triggers ``tele_power1#state`` and multiple ``tele-wifi1#xxx`` (#7093) -- Add experimental support for stepper motor shutter control -- Add optional USE_MQTT_TLS to tasmota-minimal.bin (#7115) +- Add SML bus decoder syntax support for byte order by Gerhard Mutz (#7112) +- Add rule var ``%topic%`` by Adrian Scillato (#5522) +- Add rule triggers ``tele_power1#state`` and multiple ``tele-wifi1#xxx`` by Adrian Scillato (#7093) +- Add experimental support for stepper motor shutter control by Stefan Bode +- Add optional USE_MQTT_TLS to tasmota-minimal.bin by Bohdan Kmit (#7115) ## Released diff --git a/tasmota/my_user_config.h b/tasmota/my_user_config.h index c643f0a6d..5ff6bb6a2 100644 --- a/tasmota/my_user_config.h +++ b/tasmota/my_user_config.h @@ -132,7 +132,8 @@ #define WEB_PASSWORD "" // [WebPassword] Web server Admin mode Password for WEB_USERNAME (empty string = Disable) #define FRIENDLY_NAME "Tasmota" // [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) -#define CORS_DOMAIN "" // [CorsDomain] CORS Domain for preflight requests +#define CORS_DOMAIN "" // [Cors] CORS Domain for preflight requests + // -- HTTP GUI Colors ----------------------------- // HTML hex color codes. Only 3 and 6 digit hex string values are supported!! See https://www.w3schools.com/colors/colors_hex.asp // Light theme - pre v7 diff --git a/tasmota/settings.h b/tasmota/settings.h index 6da25a36b..1c5206242 100644 --- a/tasmota/settings.h +++ b/tasmota/settings.h @@ -86,7 +86,7 @@ typedef union { // Restricted by MISRA-C Rule 18.4 bu uint32_t energy_weekend : 1; // bit 20 (v6.6.0.8) - CMND_TARIFF uint32_t dds2382_model : 1; // bit 21 (v6.6.0.14) - SetOption71 - Select different Modbus registers for Active Energy (#6531) uint32_t hardware_energy_total : 1; // bit 22 (v6.6.0.15) - SetOption72 - Enable hardware energy total counter as reference (#6561) - uint32_t ex_cors_enabled : 1; // bit 23 (v7.0.0.1) - SetOption73 - Enable HTTP CORS + uint32_t ex_cors_enabled : 1; // bit 23 (v7.0.0.1) - SetOption73 - Enable HTTP CORS uint32_t ds18x20_internal_pullup : 1; // bit 24 (v7.0.0.1) - SetOption74 - Enable internal pullup for single DS18x20 sensor uint32_t grouptopic_mode : 1; // bit 25 (v7.0.0.1) - SetOption75 - GroupTopic replaces %topic% (0) or fixed topic cmnd/grouptopic (1) uint32_t bootcount_update : 1; // bit 26 (v7.0.0.4) - SetOption76 - Enable incrementing bootcount when deepsleep is enabled @@ -437,9 +437,9 @@ struct SYSCFG { uint8_t free_e9f[1]; // E9F uint8_t web_color2[2][3]; // EA0 - Needs to be on integer / 3 distance from web_color + char cors_domain[33]; // EA6 - char cors_domain[33]; // EC1 - uint8_t free_ea4[293]; // EA6 + uint8_t free_ec1[293]; // EC1 uint32_t i2c_drivers[3]; // FEC I2cDriver uint32_t cfg_timestamp; // FF8 diff --git a/tasmota/settings.ino b/tasmota/settings.ino index 50f4fe4a6..cc6066c5a 100644 --- a/tasmota/settings.ino +++ b/tasmota/settings.ino @@ -1181,7 +1181,6 @@ void SettingsDelta(void) if (Settings.version < 0x07010202) { Settings.serial_config = TS_SERIAL_8N1; } - if (Settings.version < 0x07010204) { if (Settings.flag3.ex_cors_enabled == 1) { strlcpy(Settings.cors_domain, CORS_ENABLED_ALL, sizeof(Settings.cors_domain)); From e9f8792649053c8d617cc4b63965aa1fc281b6bd Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Mon, 9 Dec 2019 15:29:22 +0100 Subject: [PATCH 40/84] Revert removal of exception details from MQTT info on restart Revert removal of exception details from MQTT info on restart --- tasmota/CHANGELOG.md | 1 + tasmota/xdrv_02_mqtt.ino | 8 +++++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/tasmota/CHANGELOG.md b/tasmota/CHANGELOG.md index 826aad3b6..00211f84b 100644 --- a/tasmota/CHANGELOG.md +++ b/tasmota/CHANGELOG.md @@ -3,6 +3,7 @@ ### 7.1.2.4 20191209 - Change HTTP CORS from command ``SetOption73 0/1`` to ``Cors `` allowing user control of specific CORS domain by Shantur Rathore (#7066) +- Revert removal of exception details from MQTT info on restart - Add Wifi Signal Strength in dBm in addition to RSSI Wifi Experience by Andreas Schultz (#7145) - Add Yaw, Pitch and Roll support for MPU6050 by Philip Barclay (#7058) diff --git a/tasmota/xdrv_02_mqtt.ino b/tasmota/xdrv_02_mqtt.ino index d31d0a860..b38607ad7 100644 --- a/tasmota/xdrv_02_mqtt.ino +++ b/tasmota/xdrv_02_mqtt.ino @@ -555,7 +555,13 @@ void MqttConnected(void) MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_INFO "2")); } #endif // USE_WEBSERVER - Response_P(PSTR("{\"" D_JSON_RESTARTREASON "\":\"%s\"}"), GetResetReason().c_str()); + Response_P(PSTR("{\"" D_JSON_RESTARTREASON "\":")); + if (ResetReason() == REASON_EXCEPTION_RST) { + CrashDump(); + } else { + ResponseAppend_P(PSTR("\"%s\""), GetResetReason().c_str()); + } + ResponseJsonEnd(); MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_INFO "3")); MqttPublishAllPowerState(); if (Settings.tele_period) { From d3673963a2e77968b0775b397cc268640d99f9d6 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Mon, 9 Dec 2019 16:16:21 +0100 Subject: [PATCH 41/84] Add Char Usage Ratio Add Char Usage Ratio for future Settings char redesign --- tasmota/settings.ino | 41 +++++++++++++++++++++++++++++++++++++ tasmota/support_command.ino | 7 +++++-- 2 files changed, 46 insertions(+), 2 deletions(-) diff --git a/tasmota/settings.ino b/tasmota/settings.ino index cc6066c5a..4768ca24b 100644 --- a/tasmota/settings.ino +++ b/tasmota/settings.ino @@ -630,6 +630,47 @@ void SettingsSdkErase(void) delay(1000); } +String SettingsCharUsage(void) +{ + uint32_t str_len = 0; + uint32_t str_size = 0; + + for (uint32_t i = 0; i < 2; i++) { + str_len += strlen(Settings.sta_ssid[i]); str_size += sizeof(Settings.sta_ssid[i]); + str_len += strlen(Settings.sta_pwd[i]); str_size += sizeof(Settings.sta_pwd[i]); + } + for (uint32_t i = 0; i < 3; i++) { + str_len += strlen(Settings.mqtt_prefix[i]); str_size += sizeof(Settings.mqtt_prefix[i]); + str_len += strlen(Settings.ntp_server[i]); str_size += sizeof(Settings.ntp_server[i]); + } + for (uint32_t i = 0; i < 4; i++) { + str_len += strlen(Settings.state_text[i]); str_size += sizeof(Settings.state_text[i]); + str_len += strlen(Settings.friendlyname[i]); str_size += sizeof(Settings.friendlyname[i]); + } + for (uint32_t i = 0; i < MAX_RULE_MEMS; i++) { + str_len += strlen(Settings.mems[i]); str_size += sizeof(Settings.mems[i]); + } + + str_len += strlen(Settings.ota_url); str_size += sizeof(Settings.ota_url); + str_len += strlen(Settings.hostname); str_size += sizeof(Settings.hostname); + str_len += strlen(Settings.syslog_host); str_size += sizeof(Settings.syslog_host); + str_len += strlen(Settings.mqtt_host); str_size += sizeof(Settings.mqtt_host); + str_len += strlen(Settings.mqtt_client); str_size += sizeof(Settings.mqtt_client); + str_len += strlen(Settings.mqtt_user); str_size += sizeof(Settings.mqtt_user); + str_len += strlen(Settings.mqtt_pwd); str_size += sizeof(Settings.mqtt_pwd); + str_len += strlen(Settings.mqtt_topic); str_size += sizeof(Settings.mqtt_topic); + str_len += strlen(Settings.button_topic); str_size += sizeof(Settings.button_topic); + str_len += strlen(Settings.switch_topic); str_size += sizeof(Settings.switch_topic); + str_len += strlen(Settings.mqtt_grptopic); str_size += sizeof(Settings.mqtt_grptopic); + str_len += strlen(Settings.web_password); str_size += sizeof(Settings.web_password); + str_len += strlen(Settings.mqtt_fulltopic); str_size += sizeof(Settings.mqtt_fulltopic); + str_len += strlen(Settings.cors_domain); str_size += sizeof(Settings.cors_domain); + + char data[30]; + snprintf_P(data, sizeof(data), PSTR(",\"CR\":\"%d/%d\""), str_len, str_size); // Char Usage Ratio + return String(data); +} + /********************************************************************************************/ void SettingsDefault(void) diff --git a/tasmota/support_command.ino b/tasmota/support_command.ino index 09495f55a..b1c8512a4 100644 --- a/tasmota/support_command.ino +++ b/tasmota/support_command.ino @@ -380,9 +380,12 @@ void CmndStatus(void) if ((0 == payload) || (2 == payload)) { 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\"," - "\"Hardware\":\"%s\"}}"), + "\"Hardware\":\"%s\"" + "%s}}"), my_version, my_image, GetBuildDateAndTime().c_str(), - ESP.getBootVersion(), ESP.getSdkVersion(), GetDeviceHardware().c_str()); + ESP.getBootVersion(), ESP.getSdkVersion(), + GetDeviceHardware().c_str(), + SettingsCharUsage().c_str()); MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS "2")); } From 53cfabdbca993ed59d8cb8ac1a3b7d45ff62d6c0 Mon Sep 17 00:00:00 2001 From: Hadinger Date: Mon, 9 Dec 2019 21:43:30 +0100 Subject: [PATCH 42/84] Add RemoveAllSpaces() --- tasmota/support.ino | 17 +++++++++++++++++ tasmota/xdrv_02_mqtt.ino | 12 +----------- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/tasmota/support.ino b/tasmota/support.ino index ef3b8d9ee..3ecc9b179 100644 --- a/tasmota/support.ino +++ b/tasmota/support.ino @@ -377,6 +377,23 @@ char* Trim(char* p) return p; } +char* RemoveAllSpaces(char* p) +{ + // remove any white space from the base64 + char *cursor = p; + uint32_t offset = 0; + while (1) { + *cursor = *(cursor + offset); + if ((' ' == *cursor) || ('\t' == *cursor) || ('\n' == *cursor)) { // if space found, remove this char until end of string + offset++; + } else { + if (0 == *cursor) { break; } + cursor++; + } + } + return p; +} + char* NoAlNumToUnderscore(char* dest, const char* source) { char* write = dest; diff --git a/tasmota/xdrv_02_mqtt.ino b/tasmota/xdrv_02_mqtt.ino index b38607ad7..6d68a346e 100644 --- a/tasmota/xdrv_02_mqtt.ino +++ b/tasmota/xdrv_02_mqtt.ino @@ -1078,17 +1078,7 @@ void CmndTlsKey(void) { memcpy_P(spi_buffer, tls_spi_start, tls_spi_len); // remove any white space from the base64 - char *cursor = XdrvMailbox.data; - uint32_t offset = 0; - while (1) { - *cursor = *(cursor + offset); - if ((' ' == *cursor) || ('\t' == *cursor) || ('\n' == *cursor)) { // if space found, remove this char until end of string - offset++; - } else { - if (0 == *cursor) { break; } - cursor++; - } - } + RemoveAllSpaces(XdrvMailbox.data); // allocate buffer for decoded base64 uint32_t bin_len = decode_base64_length((unsigned char*)XdrvMailbox.data); From b2ac6c8ed7e3ddb47f809db66609ab23b8fa65cc Mon Sep 17 00:00:00 2001 From: localhost61 <33861984+localhost61@users.noreply.github.com> Date: Tue, 10 Dec 2019 00:51:08 +0100 Subject: [PATCH 43/84] Allow 4 DHT sensors --- tasmota/xsns_06_dht.ino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tasmota/xsns_06_dht.ino b/tasmota/xsns_06_dht.ino index c4e877de9..6ef7055ac 100644 --- a/tasmota/xsns_06_dht.ino +++ b/tasmota/xsns_06_dht.ino @@ -28,7 +28,7 @@ #define XSNS_06 6 -#define DHT_MAX_SENSORS 3 +#define DHT_MAX_SENSORS 4 #define DHT_MAX_RETRY 8 uint32_t dht_max_cycles; From b9545f5194b66b325928945304a93076af1cde41 Mon Sep 17 00:00:00 2001 From: Thomas Herrmann Date: Tue, 10 Dec 2019 10:55:46 +0100 Subject: [PATCH 44/84] Report raw value from HX711 to overcome auto-tare functionality. Currently, when the device is switched off (intentionally or due to power cut), Tasmota only supports starting with the last known weight (Sensor34 7) or starting with 0 kg. In either case, this is sometimes not the desired functionality. For off-site logging of weights, it is more useful to report the raw, absolute value from the HX711 and to do calibration/tare in upper layers. Note: although it is called "Raw" value, the value is both averaged and scaled! It is just the offset compensation that is missing. --- tasmota/xsns_34_hx711.ino | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/tasmota/xsns_34_hx711.ino b/tasmota/xsns_34_hx711.ino index d9076f61d..469a5bb68 100644 --- a/tasmota/xsns_34_hx711.ino +++ b/tasmota/xsns_34_hx711.ino @@ -57,6 +57,7 @@ #define D_JSON_WEIGHT_MAX "WeightMax" #define D_JSON_WEIGHT_ITEM "WeightItem" #define D_JSON_WEIGHT_CHANGE "WeightChange" +#define D_JSON_WEIGHT_RAW "WeightRaw" enum HxCalibrationSteps { HX_CAL_END, HX_CAL_LIMBO, HX_CAL_FINISH, HX_CAL_FAIL, HX_CAL_DONE, HX_CAL_FIRST, HX_CAL_RESET, HX_CAL_START }; @@ -64,8 +65,10 @@ const char kHxCalibrationStates[] PROGMEM = D_HX_CAL_FAIL "|" D_HX_CAL_DONE "|" struct HX { long weight = 0; + long raw = 0; long last_weight = 0; long sum_weight = 0; + long sum_raw = 0; long offset = 0; long scale = 1; long weight_diff = 0; @@ -272,13 +275,17 @@ void HxInit(void) void HxEvery100mSecond(void) { - Hx.sum_weight += HxRead(); + long raw = HxRead(); + Hx.sum_raw += raw; + Hx.sum_weight += raw; Hx.sample_count++; if (HX_SAMPLES == Hx.sample_count) { long average = Hx.sum_weight / Hx.sample_count; // grams + long raw_average = Hx.sum_raw / Hx.sample_count; // grams long value = average - Hx.offset; // grams Hx.weight = value / Hx.scale; // grams + Hx.raw = raw_average / Hx.scale; if (Hx.weight < 0) { if (Settings.energy_frequency_calibration) { long difference = Settings.energy_frequency_calibration + Hx.weight; @@ -367,6 +374,7 @@ void HxEvery100mSecond(void) } Hx.sum_weight = 0; + Hx.sum_raw = 0; Hx.sample_count = 0; } } @@ -405,7 +413,7 @@ void HxShow(bool json) dtostrfd(weight, Settings.flag2.weight_resolution, weight_chr); if (json) { - ResponseAppend_P(PSTR(",\"HX711\":{\"" D_JSON_WEIGHT "\":%s%s}"), weight_chr, scount); + ResponseAppend_P(PSTR(",\"HX711\":{\"" D_JSON_WEIGHT "\":%s%s, \"" D_JSON_WEIGHT_RAW "\":%d}"), weight_chr, scount, Hx.raw); #ifdef USE_WEBSERVER } else { WSContentSend_PD(HTTP_HX711_WEIGHT, weight_chr); @@ -565,4 +573,4 @@ bool Xsns34(uint8_t function) return result; } -#endif // USE_HX711 \ No newline at end of file +#endif // USE_HX711 From 407935efc422940098f3a57621a5f86156725b73 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Tue, 10 Dec 2019 12:33:45 +0100 Subject: [PATCH 45/84] Change GUI Shutter button text Change GUI Shutter button text to Up and Down Arrows based on PR by Xavier Muller (#7166) --- RELEASENOTES.md | 20 ++++++++++++++------ tasmota/CHANGELOG.md | 3 ++- tasmota/xdrv_01_webserver.ino | 15 +++++++++++++++ 3 files changed, 31 insertions(+), 7 deletions(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 0baecb42a..468e8e5d5 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -47,10 +47,18 @@ The following binary downloads have been compiled with ESP8266/Arduino library c ## Changelog -### Version 7.1.2 Betty +### Version 7.1.2.4 -- Fix lost functionality of GPIO9 and GPIO10 on some devices (#7080) -- Fix Zigbee uses Hardware Serial if GPIO 1/3 or GPIO 13/15 and SerialLog 0 (#7071) -- Fix WS2812 power control (#7090) -- Change light color schemes 2, 3 and 4 from color wheel to Hue driven with user Saturation control -- Change log buffer size from 520 to 700 characters accomodating full rule text (#7110) +- Change Exception reporting removing exception details from ``Status 1`` and consolidated in ``Status 12`` if available +- Change HTTP CORS from command ``SetOption73 0/1`` to ``Cors `` allowing user control of specific CORS domain by Shantur Rathore (#7066) +- Change GUI Shutter button text to Up and Down Arrows based on PR by Xavier Muller (#7166) +- Add command ``SerialConfig 0..23`` or ``SerialConfig 8N1`` to select Serial Config based in PR by Luis Teixeira (#7108) +- Add rule var ``%topic%`` by Adrian Scillato (#5522) +- Add rule triggers ``tele-wifi1#xxx`` by Adrian Scillato (#7093) +- Add SML bus decoder syntax support for byte order by Gerhard Mutz (#7112) +- Add experimental support for stepper motor shutter control by Stefan Bode +- Add optional USE_MQTT_TLS to tasmota-minimal.bin by Bohdan Kmit (#7115) +- Add save call stack in RTC memory in case of crash, command ``Status 12`` to dump the stack by Stefan Hadinger +- Add Home Assistant force update by Frederico Leoni (#7140, #7074) +- Add Wifi Signal Strength in dBm in addition to RSSI Wifi Experience by Andreas Schultz (#7145) +- Add Yaw, Pitch and Roll support for MPU6050 by Philip Barclay (#7058) diff --git a/tasmota/CHANGELOG.md b/tasmota/CHANGELOG.md index 00211f84b..51148e814 100644 --- a/tasmota/CHANGELOG.md +++ b/tasmota/CHANGELOG.md @@ -3,13 +3,14 @@ ### 7.1.2.4 20191209 - Change HTTP CORS from command ``SetOption73 0/1`` to ``Cors `` allowing user control of specific CORS domain by Shantur Rathore (#7066) +- Change GUI Shutter button text to Up and Down Arrows based on PR by Xavier Muller (#7166) - Revert removal of exception details from MQTT info on restart - Add Wifi Signal Strength in dBm in addition to RSSI Wifi Experience by Andreas Schultz (#7145) - Add Yaw, Pitch and Roll support for MPU6050 by Philip Barclay (#7058) ### 7.1.2.3 20191208 -- Redesign Exception reporting removing exception details from both MQTT info and Status 1. Now consolidated in Status 12 if available. +- Change Exception reporting removing exception details from both MQTT info and ``Status 1``. Now consolidated in ``Status 12`` if available. ### 7.1.2.2 20191206 diff --git a/tasmota/xdrv_01_webserver.ino b/tasmota/xdrv_01_webserver.ino index 28620e21f..0a72da399 100644 --- a/tasmota/xdrv_01_webserver.ino +++ b/tasmota/xdrv_01_webserver.ino @@ -1107,6 +1107,21 @@ void HandleRoot(void) } else { #endif // USE_SONOFF_IFAN for (uint32_t idx = 1; idx <= devices_present; idx++) { +#ifdef USE_SHUTTER + if (Settings.flag3.shutter_mode) { // SetOption80 - Enable shutter support + bool shutter_used = false; + for (uint32_t i = 0; i < MAX_SHUTTERS; i++) { + if (Settings.shutter_startrelay[i] == (((idx -1) & 0xFFFFFFFE) +1)) { + shutter_used = true; + break; + } + } + if (shutter_used) { + WSContentSend_P(HTTP_DEVICE_CONTROL, 100 / devices_present, idx, (idx % 2) ? "▲" : "▼" , ""); + continue; + } + } +#endif // USE_SHUTTER snprintf_P(stemp, sizeof(stemp), PSTR(" %d"), idx); WSContentSend_P(HTTP_DEVICE_CONTROL, 100 / devices_present, idx, (devices_present < 5) ? D_BUTTON_TOGGLE : "", (devices_present > 1) ? stemp : ""); } From f878eba66d37434ddf56bc84d22fb2014dab2132 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Tue, 10 Dec 2019 12:40:18 +0100 Subject: [PATCH 46/84] Change amount of supported DHT sensors Change amount of supported DHT sensors from 3 to 4 by Xavier Muller (#7167) --- RELEASENOTES.md | 1 + tasmota/CHANGELOG.md | 1 + 2 files changed, 2 insertions(+) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 468e8e5d5..9003da11e 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -52,6 +52,7 @@ The following binary downloads have been compiled with ESP8266/Arduino library c - Change Exception reporting removing exception details from ``Status 1`` and consolidated in ``Status 12`` if available - Change HTTP CORS from command ``SetOption73 0/1`` to ``Cors `` allowing user control of specific CORS domain by Shantur Rathore (#7066) - Change GUI Shutter button text to Up and Down Arrows based on PR by Xavier Muller (#7166) +- Change amount of supported DHT sensors from 3 to 4 by Xavier Muller (#7167) - Add command ``SerialConfig 0..23`` or ``SerialConfig 8N1`` to select Serial Config based in PR by Luis Teixeira (#7108) - Add rule var ``%topic%`` by Adrian Scillato (#5522) - Add rule triggers ``tele-wifi1#xxx`` by Adrian Scillato (#7093) diff --git a/tasmota/CHANGELOG.md b/tasmota/CHANGELOG.md index 51148e814..ba26dd7e6 100644 --- a/tasmota/CHANGELOG.md +++ b/tasmota/CHANGELOG.md @@ -4,6 +4,7 @@ - Change HTTP CORS from command ``SetOption73 0/1`` to ``Cors `` allowing user control of specific CORS domain by Shantur Rathore (#7066) - Change GUI Shutter button text to Up and Down Arrows based on PR by Xavier Muller (#7166) +- Change amount of supported DHT sensors from 3 to 4 by Xavier Muller (#7167) - Revert removal of exception details from MQTT info on restart - Add Wifi Signal Strength in dBm in addition to RSSI Wifi Experience by Andreas Schultz (#7145) - Add Yaw, Pitch and Roll support for MPU6050 by Philip Barclay (#7058) From f03156849d18923f166ce618f99a5f885ce2bbf8 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Tue, 10 Dec 2019 12:58:34 +0100 Subject: [PATCH 47/84] Add reporting of raw weight to HX711 Add reporting of raw weight to JSON from HX711 to overcome auto-tare functionality by @tobox (#7171) --- RELEASENOTES.md | 1 + tasmota/CHANGELOG.md | 1 + 2 files changed, 2 insertions(+) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 9003da11e..cfb5f714d 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -63,3 +63,4 @@ The following binary downloads have been compiled with ESP8266/Arduino library c - Add Home Assistant force update by Frederico Leoni (#7140, #7074) - Add Wifi Signal Strength in dBm in addition to RSSI Wifi Experience by Andreas Schultz (#7145) - Add Yaw, Pitch and Roll support for MPU6050 by Philip Barclay (#7058) +- Add reporting of raw weight to JSON from HX711 to overcome auto-tare functionality by @tobox (#7171) diff --git a/tasmota/CHANGELOG.md b/tasmota/CHANGELOG.md index ba26dd7e6..9eab486f8 100644 --- a/tasmota/CHANGELOG.md +++ b/tasmota/CHANGELOG.md @@ -8,6 +8,7 @@ - Revert removal of exception details from MQTT info on restart - Add Wifi Signal Strength in dBm in addition to RSSI Wifi Experience by Andreas Schultz (#7145) - Add Yaw, Pitch and Roll support for MPU6050 by Philip Barclay (#7058) +- Add reporting of raw weight to JSON from HX711 to overcome auto-tare functionality by @tobox (#7171) ### 7.1.2.3 20191208 From 5f9350831dbb18f2dcaa7e66faa486ebe63d9cf7 Mon Sep 17 00:00:00 2001 From: PtrO Date: Tue, 10 Dec 2019 18:09:59 +0100 Subject: [PATCH 48/84] 7.1.2.4 20191209 xdrv_09_timers.ino fix 12hr early Sunset/Sunrise due negative zero time. --- tasmota/xdrv_09_timers.ino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tasmota/xdrv_09_timers.ino b/tasmota/xdrv_09_timers.ino index b00b5ba24..db1cde97e 100644 --- a/tasmota/xdrv_09_timers.ino +++ b/tasmota/xdrv_09_timers.ino @@ -583,7 +583,7 @@ const char HTTP_TIMER_SCRIPT3[] PROGMEM = "if(m==0){s|=l;}" // Get time #ifdef USE_SUNRISE "if((m==1)||(m==2)){" - "if(qs('#dr').selectedIndex>0){l+=720;}" // If negative offset, add 12h to given offset time + "if(qs('#dr').selectedIndex>0){if(l>0){l+=720;}}" // If negative offset and delta-time > 0, add 12h to given offset time "s|=l&0x7FF;" // Save offset instead of time "}" #endif From 1761dee63e90eb61a6213d57ff9a62c7566e1f3a Mon Sep 17 00:00:00 2001 From: Adrian Scillato <35405447+ascillato@users.noreply.github.com> Date: Tue, 10 Dec 2019 15:42:44 -0300 Subject: [PATCH 49/84] KNX: Added Key for using custom settings for KNX --- lib/esp-knx-ip-0.5.2/esp-knx-ip.h | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/lib/esp-knx-ip-0.5.2/esp-knx-ip.h b/lib/esp-knx-ip-0.5.2/esp-knx-ip.h index eb5ecf7b0..7c6377bec 100644 --- a/lib/esp-knx-ip-0.5.2/esp-knx-ip.h +++ b/lib/esp-knx-ip-0.5.2/esp-knx-ip.h @@ -32,8 +32,18 @@ #define DISABLE_RESTORE_BUTTON 1 // [Default 0] Set to 1 to disable the "restore defaults" button in the web ui. // These values normally don't need adjustment -#define MULTICAST_PORT 3671 // [Default 3671] +#ifndef MULTICAST_IP #define MULTICAST_IP IPAddress(224, 0, 23, 12) // [Default IPAddress(224, 0, 23, 12)] +#else +#warning USING CUSTOM MULTICAST_IP +#endif + +#ifndef MULTICAST_PORT +#define MULTICAST_PORT 3671 // [Default 3671] +#else +#warning USING CUSTOM MULTICAST_PORT +#endif + #define SEND_CHECKSUM 0 // Uncomment to enable printing out debug messages. From 96cc933094a6f1f9ef1acca3ecf3248226aaca89 Mon Sep 17 00:00:00 2001 From: Hadinger Date: Tue, 10 Dec 2019 22:00:38 +0100 Subject: [PATCH 50/84] Fix flashing H801 led at boot (#7165, #649) --- tasmota/CHANGELOG.md | 1 + tasmota/support_tasmota.ino | 12 ++++++++---- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/tasmota/CHANGELOG.md b/tasmota/CHANGELOG.md index 9eab486f8..51277e62b 100644 --- a/tasmota/CHANGELOG.md +++ b/tasmota/CHANGELOG.md @@ -9,6 +9,7 @@ - Add Wifi Signal Strength in dBm in addition to RSSI Wifi Experience by Andreas Schultz (#7145) - Add Yaw, Pitch and Roll support for MPU6050 by Philip Barclay (#7058) - Add reporting of raw weight to JSON from HX711 to overcome auto-tare functionality by @tobox (#7171) +- Fix flashing H801 led at boot (#7165, #649) ### 7.1.2.3 20191208 diff --git a/tasmota/support_tasmota.ino b/tasmota/support_tasmota.ino index 2ab8fa0d7..34ce9f5dc 100644 --- a/tasmota/support_tasmota.ino +++ b/tasmota/support_tasmota.ino @@ -1286,15 +1286,19 @@ void GpioInit(void) } #endif // USE_SONOFF_SC - if (!light_type) { - for (uint32_t i = 0; i < MAX_PWMS; i++) { // Basic PWM control only - if (pin[GPIO_PWM1 +i] < 99) { + for (uint32_t i = 0; i < MAX_PWMS; i++) { // Basic PWM control only + if (pin[GPIO_PWM1 +i] < 99) { + pinMode(pin[GPIO_PWM1 +i], OUTPUT); + if (light_type) { + // force PWM GPIOs to low or high mode, see #7165 + analogWrite(pin[GPIO_PWM1 +i], bitRead(pwm_inverted, i) ? Settings.pwm_range : 0); + } else { pwm_present = true; - pinMode(pin[GPIO_PWM1 +i], OUTPUT); analogWrite(pin[GPIO_PWM1 +i], bitRead(pwm_inverted, i) ? Settings.pwm_range - Settings.pwm_value[i] : Settings.pwm_value[i]); } } } + for (uint32_t i = 0; i < MAX_RELAYS; i++) { if (pin[GPIO_REL1 +i] < 99) { pinMode(pin[GPIO_REL1 +i], OUTPUT); From b1d26b9b3a5378092137ee25959ea3f6d82f1824 Mon Sep 17 00:00:00 2001 From: Adrian Scillato <35405447+ascillato@users.noreply.github.com> Date: Tue, 10 Dec 2019 22:36:23 -0300 Subject: [PATCH 51/84] RULES: Fix duplicated BACKLOG when using EVENT inside a BACKLOG --- tasmota/xdrv_10_rules.ino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tasmota/xdrv_10_rules.ino b/tasmota/xdrv_10_rules.ino index 441722662..ac771f0d7 100644 --- a/tasmota/xdrv_10_rules.ino +++ b/tasmota/xdrv_10_rules.ino @@ -438,7 +438,7 @@ bool RuleSetProcess(uint8_t rule_set, String &event_saved) String ucommand = commands; ucommand.toUpperCase(); // if (!ucommand.startsWith("BACKLOG")) { commands = "backlog " + commands; } // Always use Backlog to prevent power race exception - if (ucommand.indexOf("EVENT ") != -1) { commands = "backlog " + commands; } // Always use Backlog with event to prevent rule event loop exception + if ((ucommand.indexOf("EVENT ") != -1) && (ucommand.indexOf("BACKLOG ") == -1)) { commands = "backlog " + commands; } // Always use Backlog with event to prevent rule event loop exception RulesVarReplace(commands, F("%VALUE%"), Rules.event_value); for (uint32_t i = 0; i < MAX_RULE_VARS; i++) { From 28c8761be21fc02d9addaf18f9a81023d4402602 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Wed, 11 Dec 2019 10:10:02 +0100 Subject: [PATCH 52/84] Update change log and release notes --- RELEASENOTES.md | 3 +++ tasmota/CHANGELOG.md | 4 +++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index cfb5f714d..2ce866863 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -53,6 +53,9 @@ The following binary downloads have been compiled with ESP8266/Arduino library c - Change HTTP CORS from command ``SetOption73 0/1`` to ``Cors `` allowing user control of specific CORS domain by Shantur Rathore (#7066) - Change GUI Shutter button text to Up and Down Arrows based on PR by Xavier Muller (#7166) - Change amount of supported DHT sensors from 3 to 4 by Xavier Muller (#7167) +- Fix flashing H801 led at boot by Stefan Hadinger (#7165, #649) +- Fix duplicated ``Backlog`` when using Event inside a Backlog by Adrian Scillato (#7178, #7147) +- Fix Gui Timer when using a negative zero offset of -00:00 by Peter Ooms (#7174) - Add command ``SerialConfig 0..23`` or ``SerialConfig 8N1`` to select Serial Config based in PR by Luis Teixeira (#7108) - Add rule var ``%topic%`` by Adrian Scillato (#5522) - Add rule triggers ``tele-wifi1#xxx`` by Adrian Scillato (#7093) diff --git a/tasmota/CHANGELOG.md b/tasmota/CHANGELOG.md index 51277e62b..8f5954e66 100644 --- a/tasmota/CHANGELOG.md +++ b/tasmota/CHANGELOG.md @@ -9,7 +9,9 @@ - Add Wifi Signal Strength in dBm in addition to RSSI Wifi Experience by Andreas Schultz (#7145) - Add Yaw, Pitch and Roll support for MPU6050 by Philip Barclay (#7058) - Add reporting of raw weight to JSON from HX711 to overcome auto-tare functionality by @tobox (#7171) -- Fix flashing H801 led at boot (#7165, #649) +- Fix flashing H801 led at boot by Stefan Hadinger (#7165, #649) +- Fix duplicated ``Backlog`` when using Event inside a Backlog by Adrian Scillato (#7178, #7147) +- Fix Gui Timer when using a negative zero offset of -00:00 by Peter Ooms (#7174) ### 7.1.2.3 20191208 From 72aa161dddab0b9a293fe921b7f8e526e02ab73f Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Wed, 11 Dec 2019 10:49:57 +0100 Subject: [PATCH 53/84] Extent statistics --- tasmota/settings.ino | 41 ---------------- tasmota/support_command.ino | 2 +- tasmota/support_statistics.ino | 88 ++++++++++++++++++++++++++++++++++ 3 files changed, 89 insertions(+), 42 deletions(-) create mode 100644 tasmota/support_statistics.ino diff --git a/tasmota/settings.ino b/tasmota/settings.ino index 4768ca24b..cc6066c5a 100644 --- a/tasmota/settings.ino +++ b/tasmota/settings.ino @@ -630,47 +630,6 @@ void SettingsSdkErase(void) delay(1000); } -String SettingsCharUsage(void) -{ - uint32_t str_len = 0; - uint32_t str_size = 0; - - for (uint32_t i = 0; i < 2; i++) { - str_len += strlen(Settings.sta_ssid[i]); str_size += sizeof(Settings.sta_ssid[i]); - str_len += strlen(Settings.sta_pwd[i]); str_size += sizeof(Settings.sta_pwd[i]); - } - for (uint32_t i = 0; i < 3; i++) { - str_len += strlen(Settings.mqtt_prefix[i]); str_size += sizeof(Settings.mqtt_prefix[i]); - str_len += strlen(Settings.ntp_server[i]); str_size += sizeof(Settings.ntp_server[i]); - } - for (uint32_t i = 0; i < 4; i++) { - str_len += strlen(Settings.state_text[i]); str_size += sizeof(Settings.state_text[i]); - str_len += strlen(Settings.friendlyname[i]); str_size += sizeof(Settings.friendlyname[i]); - } - for (uint32_t i = 0; i < MAX_RULE_MEMS; i++) { - str_len += strlen(Settings.mems[i]); str_size += sizeof(Settings.mems[i]); - } - - str_len += strlen(Settings.ota_url); str_size += sizeof(Settings.ota_url); - str_len += strlen(Settings.hostname); str_size += sizeof(Settings.hostname); - str_len += strlen(Settings.syslog_host); str_size += sizeof(Settings.syslog_host); - str_len += strlen(Settings.mqtt_host); str_size += sizeof(Settings.mqtt_host); - str_len += strlen(Settings.mqtt_client); str_size += sizeof(Settings.mqtt_client); - str_len += strlen(Settings.mqtt_user); str_size += sizeof(Settings.mqtt_user); - str_len += strlen(Settings.mqtt_pwd); str_size += sizeof(Settings.mqtt_pwd); - str_len += strlen(Settings.mqtt_topic); str_size += sizeof(Settings.mqtt_topic); - str_len += strlen(Settings.button_topic); str_size += sizeof(Settings.button_topic); - str_len += strlen(Settings.switch_topic); str_size += sizeof(Settings.switch_topic); - str_len += strlen(Settings.mqtt_grptopic); str_size += sizeof(Settings.mqtt_grptopic); - str_len += strlen(Settings.web_password); str_size += sizeof(Settings.web_password); - str_len += strlen(Settings.mqtt_fulltopic); str_size += sizeof(Settings.mqtt_fulltopic); - str_len += strlen(Settings.cors_domain); str_size += sizeof(Settings.cors_domain); - - char data[30]; - snprintf_P(data, sizeof(data), PSTR(",\"CR\":\"%d/%d\""), str_len, str_size); // Char Usage Ratio - return String(data); -} - /********************************************************************************************/ void SettingsDefault(void) diff --git a/tasmota/support_command.ino b/tasmota/support_command.ino index b1c8512a4..ec8de9126 100644 --- a/tasmota/support_command.ino +++ b/tasmota/support_command.ino @@ -385,7 +385,7 @@ void CmndStatus(void) my_version, my_image, GetBuildDateAndTime().c_str(), ESP.getBootVersion(), ESP.getSdkVersion(), GetDeviceHardware().c_str(), - SettingsCharUsage().c_str()); + GetStatistics().c_str()); MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS "2")); } diff --git a/tasmota/support_statistics.ino b/tasmota/support_statistics.ino new file mode 100644 index 000000000..3239c5b93 --- /dev/null +++ b/tasmota/support_statistics.ino @@ -0,0 +1,88 @@ +/* + support_statistics.ino - gather statistics for Tasmota + + Copyright (C) 2019 Theo Arends + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#define USE_STATS_CODE + +#ifdef USE_STATS_CODE +/*********************************************************************************************\ + * Gather statistics +\*********************************************************************************************/ + +struct STATS { +// uint32_t str_size = 1151; // Total number of characters reserved as char array in Settings + uint32_t str_len = 0; // Total number of characters used within char array + uint32_t str_char = 0; // Total number of character '|' within all char arrays +} Stats; + +void StatisticsChar(const char* text) +{ + uint32_t len = strlen(text); + Stats.str_len += len; + for (uint32_t i = 0; i < len; i++) { + if ('|' == text[i]) { // Text string separator as currently used in GetTextIndexed() + Stats.str_char++; + } + } +} + +String GetStatistics(void) +{ + for (uint32_t i = 0; i < 2; i++) { + StatisticsChar(Settings.sta_ssid[i]); + StatisticsChar(Settings.sta_pwd[i]); + } + for (uint32_t i = 0; i < 3; i++) { + StatisticsChar(Settings.mqtt_prefix[i]); + StatisticsChar(Settings.ntp_server[i]); + } + for (uint32_t i = 0; i < 4; i++) { + StatisticsChar(Settings.state_text[i]); + StatisticsChar(Settings.friendlyname[i]); + } + for (uint32_t i = 0; i < MAX_RULE_MEMS; i++) { + StatisticsChar(Settings.mems[i]); + } + StatisticsChar(Settings.ota_url); + StatisticsChar(Settings.hostname); + StatisticsChar(Settings.syslog_host); + StatisticsChar(Settings.mqtt_host); + StatisticsChar(Settings.mqtt_client); + StatisticsChar(Settings.mqtt_user); + StatisticsChar(Settings.mqtt_pwd); + StatisticsChar(Settings.mqtt_topic); + StatisticsChar(Settings.button_topic); + StatisticsChar(Settings.switch_topic); + StatisticsChar(Settings.mqtt_grptopic); + StatisticsChar(Settings.web_password); + StatisticsChar(Settings.mqtt_fulltopic); + StatisticsChar(Settings.cors_domain); + + char data[40]; + snprintf_P(data, sizeof(data), PSTR(",\"CR\":\"%d/1151/%d\""), Stats.str_len, Stats.str_char); // Char Usage Ratio + return String(data); +} + +#else + +String GetStatistics(void) +{ + return String(""); +} + +#endif // USE_STATS_CODE \ No newline at end of file From eccc9f20f5544c4b7b12813dda62a013b8259981 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Wed, 11 Dec 2019 11:54:27 +0100 Subject: [PATCH 54/84] Add weight_change for #7170 Add weight_change for #7170 --- tasmota/settings.h | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tasmota/settings.h b/tasmota/settings.h index 1c5206242..524a6ac19 100644 --- a/tasmota/settings.h +++ b/tasmota/settings.h @@ -433,9 +433,7 @@ struct SYSCFG { uint16_t energy_power_delta; // E98 uint8_t shutter_motordelay[MAX_SHUTTERS]; // E9A int8_t temp_comp; // E9E - - uint8_t free_e9f[1]; // E9F - + uint8_t weight_change; // E9F uint8_t web_color2[2][3]; // EA0 - Needs to be on integer / 3 distance from web_color char cors_domain[33]; // EA6 From 27dd251fc341b506e3a11331d6c7ae4a3a5c19cd Mon Sep 17 00:00:00 2001 From: Bastian Urschel Date: Wed, 11 Dec 2019 15:53:19 +0100 Subject: [PATCH 55/84] IPv6 global unique address is now displayed at webinterface "information" above IPv4 address and also mentioned in the initial MQTT message if WebServer is active. --- tasmota/support_wifi.ino | 8 ++++++++ tasmota/xdrv_01_webserver.ino | 12 ++++++++++++ tasmota/xdrv_02_mqtt.ino | 5 +++++ 3 files changed, 25 insertions(+) diff --git a/tasmota/support_wifi.ino b/tasmota/support_wifi.ino index 06d922b26..3f9e7795b 100644 --- a/tasmota/support_wifi.ino +++ b/tasmota/support_wifi.ino @@ -352,6 +352,14 @@ bool WifiCheckIPv6(void) return ipv6_global; } +String WifiGetIPv6(void) +{ + for (auto a : addrList) { + if(!a.isLocal() && a.isV6()) return a.toString(); + } + return ""; +} + bool WifiCheckIPAddrStatus(void) // Return false for 169.254.x.x or fe80::/64 { bool ip_global=false; diff --git a/tasmota/xdrv_01_webserver.ino b/tasmota/xdrv_01_webserver.ino index 0a72da399..5dbec658a 100644 --- a/tasmota/xdrv_01_webserver.ino +++ b/tasmota/xdrv_01_webserver.ino @@ -598,7 +598,13 @@ void StartWebserver(int type, IPAddress ipweb) WebServer->begin(); // Web server start } if (Web.state != type) { +#if LWIP_IPV6 + String ipv6_addr = WifiGetIPv6(); + if(ipv6_addr!="") AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_HTTP D_WEBSERVER_ACTIVE_ON " %s%s " D_WITH_IP_ADDRESS " %s and IPv6 global address %s "), my_hostname, (Wifi.mdns_begun) ? ".local" : "", ipweb.toString().c_str(),ipv6_addr.c_str()); + else AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_HTTP D_WEBSERVER_ACTIVE_ON " %s%s " D_WITH_IP_ADDRESS " %s"), my_hostname, (Wifi.mdns_begun) ? ".local" : "", ipweb.toString().c_str()); +#else AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_HTTP D_WEBSERVER_ACTIVE_ON " %s%s " D_WITH_IP_ADDRESS " %s"), my_hostname, (Wifi.mdns_begun) ? ".local" : "", ipweb.toString().c_str()); +#endif // LWIP_IPV6 = 1 rules_flag.http_init = 1; } if (type) { Web.state = type; } @@ -1990,6 +1996,12 @@ void HandleInformation(void) WSContentSend_P(PSTR("}1}2 ")); // Empty line WSContentSend_P(PSTR("}1" D_AP "%d " D_SSID " (" D_RSSI ")}2%s (%d%%, %d dBm)"), Settings.sta_active +1, Settings.sta_ssid[Settings.sta_active], WifiGetRssiAsQuality(WiFi.RSSI()), WiFi.RSSI()); WSContentSend_P(PSTR("}1" D_HOSTNAME "}2%s%s"), my_hostname, (Wifi.mdns_begun) ? ".local" : ""); +#if LWIP_IPV6 + String ipv6_addr = WifiGetIPv6(); + if(ipv6_addr != ""){ + WSContentSend_P(PSTR("}1 IPv6 Address }2%s"), ipv6_addr.c_str()); + } +#endif if (static_cast(WiFi.localIP()) != 0) { WSContentSend_P(PSTR("}1" D_IP_ADDRESS "}2%s"), WiFi.localIP().toString().c_str()); WSContentSend_P(PSTR("}1" D_GATEWAY "}2%s"), IPAddress(Settings.ip_address[1]).toString().c_str()); diff --git a/tasmota/xdrv_02_mqtt.ino b/tasmota/xdrv_02_mqtt.ino index 6d68a346e..7a9b1bf43 100644 --- a/tasmota/xdrv_02_mqtt.ino +++ b/tasmota/xdrv_02_mqtt.ino @@ -550,8 +550,13 @@ void MqttConnected(void) MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_INFO "1")); #ifdef USE_WEBSERVER if (Settings.webserver) { +#if LWIP_IPV6 + Response_P(PSTR("{\"" D_JSON_WEBSERVER_MODE "\":\"%s\",\"" D_CMND_HOSTNAME "\":\"%s\",\"" D_CMND_IPADDRESS "\":\"%s\",\"IPv6Address\":\"%s\"}"), + (2 == Settings.webserver) ? D_ADMIN : D_USER, my_hostname, WiFi.localIP().toString().c_str(),WifiGetIPv6().c_str()); +#else Response_P(PSTR("{\"" D_JSON_WEBSERVER_MODE "\":\"%s\",\"" D_CMND_HOSTNAME "\":\"%s\",\"" D_CMND_IPADDRESS "\":\"%s\"}"), (2 == Settings.webserver) ? D_ADMIN : D_USER, my_hostname, WiFi.localIP().toString().c_str()); +#endif // LWIP_IPV6 = 1 MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_INFO "2")); } #endif // USE_WEBSERVER From bfe2dc68cba880961ac4c18cb6512d0903c08172 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Wed, 11 Dec 2019 19:30:52 +0100 Subject: [PATCH 56/84] Fix statistics --- tasmota/settings.h | 2 +- tasmota/support_statistics.ino | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/tasmota/settings.h b/tasmota/settings.h index 524a6ac19..c938f0b39 100644 --- a/tasmota/settings.h +++ b/tasmota/settings.h @@ -437,7 +437,7 @@ struct SYSCFG { uint8_t web_color2[2][3]; // EA0 - Needs to be on integer / 3 distance from web_color char cors_domain[33]; // EA6 - uint8_t free_ec1[293]; // EC1 + uint8_t free_ec7[293]; // EC7 uint32_t i2c_drivers[3]; // FEC I2cDriver uint32_t cfg_timestamp; // FF8 diff --git a/tasmota/support_statistics.ino b/tasmota/support_statistics.ino index 3239c5b93..efd94f307 100644 --- a/tasmota/support_statistics.ino +++ b/tasmota/support_statistics.ino @@ -43,6 +43,9 @@ void StatisticsChar(const char* text) String GetStatistics(void) { + Stats.str_len = 0; + Stats.str_char = 0; + for (uint32_t i = 0; i < 2; i++) { StatisticsChar(Settings.sta_ssid[i]); StatisticsChar(Settings.sta_pwd[i]); From 2ecb1b18474d1efd57daef614f6489043ce87163 Mon Sep 17 00:00:00 2001 From: Thomas Herrmann Date: Wed, 11 Dec 2019 22:03:13 +0100 Subject: [PATCH 57/84] Make WeightDelta for JSON message configurable. --- tasmota/xsns_34_hx711.ino | 38 +++++++++++++++++++++++++++++++++++--- 1 file changed, 35 insertions(+), 3 deletions(-) diff --git a/tasmota/xsns_34_hx711.ino b/tasmota/xsns_34_hx711.ino index 469a5bb68..ecb8da493 100644 --- a/tasmota/xsns_34_hx711.ino +++ b/tasmota/xsns_34_hx711.ino @@ -58,6 +58,7 @@ #define D_JSON_WEIGHT_ITEM "WeightItem" #define D_JSON_WEIGHT_CHANGE "WeightChange" #define D_JSON_WEIGHT_RAW "WeightRaw" +#define D_JSON_WEIGHT_DELTA "WeightDelta" enum HxCalibrationSteps { HX_CAL_END, HX_CAL_LIMBO, HX_CAL_FINISH, HX_CAL_FAIL, HX_CAL_DONE, HX_CAL_FIRST, HX_CAL_RESET, HX_CAL_START }; @@ -81,6 +82,7 @@ struct HX { uint8_t pin_dout; bool tare_flg = false; bool weight_changed = false; + uint16_t weight_delta = 4; } Hx; /*********************************************************************************************/ @@ -149,6 +151,24 @@ void HxCalibrationStateTextJson(uint8_t msg_id) if (msg_id < 3) { MqttPublishPrefixTopic_P(RESULT_OR_STAT, PSTR("Sensor34")); } } +void SetWeightDelta() +{ + // backwards compatible: restore old default value of 4 grams + if (Settings.weight_change == 0) { + Hx.weight_delta = 4; + return; + } + + // map upper values 101-255 to + if (Settings.weight_change > 100) { + Hx.weight_delta = (Settings.weight_change - 100) * 10 + 100; + return; + } + + // map 1..100 to 0..99 grams + Hx.weight_delta = Settings.weight_change - 1; +} + /*********************************************************************************************\ * Supported commands for Sensor34: * @@ -166,6 +186,7 @@ void HxCalibrationStateTextJson(uint8_t msg_id) * Sensor34 7 - Save current weight to be used as start weight on restart * Sensor34 8 0 - Disable JSON weight change message * Sensor34 8 1 - Enable JSON weight change message + * Sensor34 9 - Set minimum delta to trigger JSON message \*********************************************************************************************/ bool HxCommand(void) @@ -228,6 +249,13 @@ bool HxCommand(void) } show_parms = true; break; + case 9: // WeightDelta + if (strstr(XdrvMailbox.data, ",") != nullptr) { + Settings.weight_change = strtol(subStr(sub_string, XdrvMailbox.data, ",", 2), nullptr, 10); + SetWeightDelta(); + } + show_parms = true; + break; default: show_parms = true; } @@ -235,8 +263,10 @@ bool HxCommand(void) if (show_parms) { char item[33]; dtostrfd((float)Settings.weight_item / 10, 1, item); - Response_P(PSTR("{\"Sensor34\":{\"" D_JSON_WEIGHT_REF "\":%d,\"" D_JSON_WEIGHT_CAL "\":%d,\"" D_JSON_WEIGHT_MAX "\":%d,\"" D_JSON_WEIGHT_ITEM "\":%s,\"" D_JSON_WEIGHT_CHANGE "\":\"%s\"}}"), - Settings.weight_reference, Settings.weight_calibration, Settings.weight_max * 1000, item, GetStateText(Settings.SensorBits1.hx711_json_weight_change)); + Response_P(PSTR("{\"Sensor34\":{\"" D_JSON_WEIGHT_REF "\":%d,\"" D_JSON_WEIGHT_CAL "\":%d,\"" D_JSON_WEIGHT_MAX "\":%d,\"" + D_JSON_WEIGHT_ITEM "\":%s,\"" D_JSON_WEIGHT_CHANGE "\":%s,\"" D_JSON_WEIGHT_DELTA "\":\"%d\"}}"), + Settings.weight_reference, Settings.weight_calibration, Settings.weight_max * 1000, + item, GetStateText(Settings.SensorBits1.hx711_json_weight_change), Settings.weight_change); } return serviced; @@ -261,6 +291,8 @@ void HxInit(void) digitalWrite(Hx.pin_sck, LOW); + SetWeightDelta(); + if (HxIsReady(8 * HX_TIMEOUT)) { // Can take 600 milliseconds after power on if (!Settings.weight_max) { Settings.weight_max = HX_MAX_WEIGHT / 1000; } if (!Settings.weight_calibration) { Settings.weight_calibration = HX_SCALE; } @@ -358,7 +390,7 @@ void HxEvery100mSecond(void) Hx.weight += Hx.last_weight; // grams if (Settings.SensorBits1.hx711_json_weight_change) { - if (abs(Hx.weight - Hx.weight_diff) > 4) { // Use 4 gram threshold to decrease "ghost" weights + if (abs(Hx.weight - Hx.weight_diff) > Hx.weight_delta) { // Use weight_delta threshold to decrease "ghost" weights Hx.weight_diff = Hx.weight; Hx.weight_changed = true; } From abf1d2b27c7813f51b81500b27ddf0769bc0e828 Mon Sep 17 00:00:00 2001 From: localhost61 <33861984+localhost61@users.noreply.github.com> Date: Thu, 12 Dec 2019 08:39:44 +0100 Subject: [PATCH 58/84] Cosmetic typo in shutter log --- tasmota/xdrv_27_shutter.ino | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tasmota/xdrv_27_shutter.ino b/tasmota/xdrv_27_shutter.ino index c97318c5f..8bc13dfa4 100644 --- a/tasmota/xdrv_27_shutter.ino +++ b/tasmota/xdrv_27_shutter.ino @@ -230,8 +230,8 @@ void ShutterInit(void) dtostrfd((float)Shutter.open_time[i] / 10 , 1, shutter_open_chr); char shutter_close_chr[10]; dtostrfd((float)Shutter.close_time[i] / 10, 1, shutter_close_chr); - AddLog_P2(LOG_LEVEL_INFO, PSTR("SHT: Shutter %d (Relay:%d): Init. Pos: %d [%d %%], Open Vel.: 100 Close Vel.: %d , Max Way: %d, Opentime %s [s], Closetime %s [s], CoedffCalc: c0: %d, c1 %d, c2: %d, c3: %d, c4: %d, binmask %d, is inverted %d, shuttermode %d,motordelay %d"), - i, Settings.shutter_startrelay[i], Shutter.real_position[i], Settings.shutter_position[i], Shutter.close_velocity[i], Shutter.open_max[i], shutter_open_chr, shutter_close_chr, + AddLog_P2(LOG_LEVEL_INFO, PSTR("SHT: Shutter %d (Relay:%d): Init. Pos: %d [%d %%], Open Vel.: 100 Close Vel.: %d , Max Way: %d, Opentime %s [s], Closetime %s [s], CoeffCalc: c0: %d, c1 %d, c2: %d, c3: %d, c4: %d, binmask %d, is inverted %d, shuttermode %d,motordelay %d"), + i+1, Settings.shutter_startrelay[i], Shutter.real_position[i], Settings.shutter_position[i], Shutter.close_velocity[i], Shutter.open_max[i], shutter_open_chr, shutter_close_chr, Settings.shuttercoeff[0][i], Settings.shuttercoeff[1][i], Settings.shuttercoeff[2][i], Settings.shuttercoeff[3][i], Settings.shuttercoeff[4][i], Shutter.mask, Settings.shutter_invert[i], Shutter.mode, Shutter.motordelay[i]); @@ -322,7 +322,7 @@ void ShutterUpdatePosition(void) Settings.shutter_position[i] = ShutterRealToPercentPosition(Shutter.real_position[i], i); dtostrfd((float)Shutter.time[i] / 20, 1, stemp2); - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Shutter %d: Real Pos. %d, Stoppos: %ld, relay: %d, direction %d, pulsetimer: %d, motordelay %d, rtcshutter: %s [s]"), i, Shutter.real_position[i], Settings.shutter_position[i], cur_relay -1, Shutter.direction[i], Settings.pulse_timer[cur_relay -1], Shutter.motordelay[i],stemp2); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Shutter %d: Real Pos. %d, Stoppos: %ld, relay: %d, direction %d, pulsetimer: %d, motordelay %d, rtcshutter: %s [s]"), i+1, Shutter.real_position[i], Settings.shutter_position[i], cur_relay -1, Shutter.direction[i], Settings.pulse_timer[cur_relay -1], Shutter.motordelay[i],stemp2); Shutter.start_position[i] = Shutter.real_position[i]; // sending MQTT result to broker @@ -385,7 +385,7 @@ void ShutterReportPosition(void) dtostrfd((float)Shutter.time[i] / 20, 2, stemp2); shutter_moving = 1; //Settings.shutter_position[i] = Settings.shuttercoeff[2][i] * 5 > Shutter.real_position[i] ? Shutter.real_position[i] / Settings.shuttercoeff[2][i] : (Shutter.real_position[i]-Settings.shuttercoeff[0,i]) / Settings.shuttercoeff[1][i]; - AddLog_P2(LOG_LEVEL_INFO, PSTR("SHT: Shutter %d: Real Pos: %d, Target %d, source: %s, start-pos: %d %%, direction: %d, motordelay %d, rtcshutter: %s [s]"), i,Shutter.real_position[i], Shutter.target_position[i], GetTextIndexed(stemp1, sizeof(stemp1), last_source, kCommandSource), Settings.shutter_position[i], Shutter.direction[i], Shutter.motordelay[i], stemp2 ); + AddLog_P2(LOG_LEVEL_INFO, PSTR("SHT: Shutter %d: Real Pos: %d, Target %d, source: %s, start-pos: %d %%, direction: %d, motordelay %d, rtcshutter: %s [s]"), i+1,Shutter.real_position[i], Shutter.target_position[i], GetTextIndexed(stemp1, sizeof(stemp1), last_source, kCommandSource), Settings.shutter_position[i], Shutter.direction[i], Shutter.motordelay[i], stemp2 ); } } if (rules_flag.shutter_moving > shutter_moving) { @@ -427,7 +427,7 @@ void ShutterRelayChanged(void) } else { if (Shutter.direction[i] != 0 && (!powerstate_local || (powerstate_local && Shutter.mode == SHT_PULSE_OPEN__PULSE_CLOSE))) { Shutter.target_position[i] = Shutter.real_position[i]; - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Shutter %d: Switch OFF motor. Target: %ld, source: %s, powerstate_local %ld, Shutter.switched_relay %d, manual change %d"), i, Shutter.target_position[i], GetTextIndexed(stemp1, sizeof(stemp1), last_source, kCommandSource), powerstate_local,Shutter.switched_relay,manual_relays_changed); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Shutter %d: Switch OFF motor. Target: %ld, source: %s, powerstate_local %ld, Shutter.switched_relay %d, manual change %d"), i+1, Shutter.target_position[i], GetTextIndexed(stemp1, sizeof(stemp1), last_source, kCommandSource), powerstate_local,Shutter.switched_relay,manual_relays_changed); } else { last_source = SRC_SHUTTER; // avoid switch off in the next loop if (powerstate_local == 2) { // testing on CLOSE relay, if ON @@ -440,7 +440,7 @@ void ShutterRelayChanged(void) ShutterStartInit(i, 1, Shutter.open_max[i]); } } - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Shutter %d: Target: %ld, powerstatelocal %d"), i, Shutter.target_position[i], powerstate_local); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Shutter %d: Target: %ld, powerstatelocal %d"), i+1, Shutter.target_position[i], powerstate_local); } } } From f0a2563b87fac4056524b4c7d5f385198f771217 Mon Sep 17 00:00:00 2001 From: localhost61 <33861984+localhost61@users.noreply.github.com> Date: Thu, 12 Dec 2019 09:32:34 +0100 Subject: [PATCH 59/84] Update French language --- tasmota/language/fr-FR.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tasmota/language/fr-FR.h b/tasmota/language/fr-FR.h index 236df351b..e107409e3 100644 --- a/tasmota/language/fr-FR.h +++ b/tasmota/language/fr-FR.h @@ -28,7 +28,7 @@ * Use online command StateText to translate ON, OFF, HOLD and TOGGLE. * Use online command Prefix to translate cmnd, stat and tele. * - * Updated until v6.6.0.15 + * Updated until v7.1.2.4 \*********************************************************************/ #define LANGUAGE_MODULE_NAME // Enable to display "Module Generic" (ie Spanish), Disable to display "Generic Module" (ie English) @@ -71,7 +71,7 @@ #define D_COLDLIGHT "Froid" #define D_COMMAND "Commande" #define D_CONNECTED "Connecté" -#define D_CORS_DOMAIN "CORS Domain" +#define D_CORS_DOMAIN "Domaine CORS" #define D_COUNT "Compte" #define D_COUNTER "Compteur" #define D_CURRENT "Courant" // As in Voltage and Current @@ -281,7 +281,7 @@ #define D_MQTT_PARAMETERS "Paramètres MQTT" #define D_CLIENT "Client" -#define D_FULL_TOPIC "topic complet" +#define D_FULL_TOPIC "Topic complet" #define D_LOGGING_PARAMETERS "Paramètres du journal" #define D_SERIAL_LOG_LEVEL "Niveau de journalisation série" @@ -688,7 +688,7 @@ #define D_CHECKING "En test" #define D_WORKING "En marche" #define D_FAILURE "Défault" -#define D_SOLAX_ERROR_0 "Aucun Code d'erreur" +#define D_SOLAX_ERROR_0 "Aucun code d'erreur" #define D_SOLAX_ERROR_1 "Défaut Perte de réseau" #define D_SOLAX_ERROR_2 "Défaut Tension réseau" #define D_SOLAX_ERROR_3 "Défaut Fréquence réseau" From 424f8c86f4ba72d009405bf318cc16649ba36c0b Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Thu, 12 Dec 2019 14:40:03 +0100 Subject: [PATCH 60/84] Update xsns_34_hx711.ino --- tasmota/xsns_34_hx711.ino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tasmota/xsns_34_hx711.ino b/tasmota/xsns_34_hx711.ino index ecb8da493..d64712a86 100644 --- a/tasmota/xsns_34_hx711.ino +++ b/tasmota/xsns_34_hx711.ino @@ -264,7 +264,7 @@ bool HxCommand(void) char item[33]; dtostrfd((float)Settings.weight_item / 10, 1, item); Response_P(PSTR("{\"Sensor34\":{\"" D_JSON_WEIGHT_REF "\":%d,\"" D_JSON_WEIGHT_CAL "\":%d,\"" D_JSON_WEIGHT_MAX "\":%d,\"" - D_JSON_WEIGHT_ITEM "\":%s,\"" D_JSON_WEIGHT_CHANGE "\":%s,\"" D_JSON_WEIGHT_DELTA "\":\"%d\"}}"), + D_JSON_WEIGHT_ITEM "\":%s,\"" D_JSON_WEIGHT_CHANGE "\":%s,\"" D_JSON_WEIGHT_DELTA "\":%d}}"), Settings.weight_reference, Settings.weight_calibration, Settings.weight_max * 1000, item, GetStateText(Settings.SensorBits1.hx711_json_weight_change), Settings.weight_change); } From c32d2088b0d617a0ab92107b110d19725f4de768 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Thu, 12 Dec 2019 14:51:41 +0100 Subject: [PATCH 61/84] Single Settings Char Array Phase 1 Single Settings Char Array Phase 1 - Does nothing yet --- RELEASENOTES.md | 1 + tasmota/CHANGELOG.md | 1 + tasmota/settings.ino | 148 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 150 insertions(+) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 2ce866863..d269109e8 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -57,6 +57,7 @@ The following binary downloads have been compiled with ESP8266/Arduino library c - Fix duplicated ``Backlog`` when using Event inside a Backlog by Adrian Scillato (#7178, #7147) - Fix Gui Timer when using a negative zero offset of -00:00 by Peter Ooms (#7174) - Add command ``SerialConfig 0..23`` or ``SerialConfig 8N1`` to select Serial Config based in PR by Luis Teixeira (#7108) +- Add command ``Sensor34 9 `` to set minimum delta to trigger JSON message by @tobox (#7188) - Add rule var ``%topic%`` by Adrian Scillato (#5522) - Add rule triggers ``tele-wifi1#xxx`` by Adrian Scillato (#7093) - Add SML bus decoder syntax support for byte order by Gerhard Mutz (#7112) diff --git a/tasmota/CHANGELOG.md b/tasmota/CHANGELOG.md index 8f5954e66..4bbeccb26 100644 --- a/tasmota/CHANGELOG.md +++ b/tasmota/CHANGELOG.md @@ -9,6 +9,7 @@ - Add Wifi Signal Strength in dBm in addition to RSSI Wifi Experience by Andreas Schultz (#7145) - Add Yaw, Pitch and Roll support for MPU6050 by Philip Barclay (#7058) - Add reporting of raw weight to JSON from HX711 to overcome auto-tare functionality by @tobox (#7171) +- Add command ``Sensor34 9 `` to set minimum delta to trigger JSON message by @tobox (#7188) - Fix flashing H801 led at boot by Stefan Hadinger (#7165, #649) - Fix duplicated ``Backlog`` when using Event inside a Backlog by Adrian Scillato (#7178, #7147) - Fix Gui Timer when using a negative zero offset of -00:00 by Peter Ooms (#7174) diff --git a/tasmota/settings.ino b/tasmota/settings.ino index cc6066c5a..d616d4cfc 100644 --- a/tasmota/settings.ino +++ b/tasmota/settings.ino @@ -442,6 +442,152 @@ void UpdateQuickPowerCycle(bool update) } } +/*********************************************************************************************\ + * Config single char array support +\*********************************************************************************************/ + +enum CharsIndex { SET_OTAURL, + SET_MQTTPREFIX1, SET_MQTTPREFIX2, SET_MQTTPREFIX3, +// SET_STASSID1, SET_STASSID2, +// SET_STAPWD1, SET_STAPWD2, SET_WEBPWD, +// SET_HOSTNAME, SET_SYSLOG_HOST, +// SET_MQTT_HOST, SET_MQTT_CLIENT, +// SET_MQTT_USER, SET_MQTT_PWD, +// SET_MQTT_FULLTOPIC, SET_MQTT_TOPIC, +// SET_MQTT_BUTTON_TOPIC, SET_MQTT_SWITCH_TOPIC, SET_MQTT_GRP_TOPIC, +// SET_STATE_TXT1, SET_STATE_TXT2, SET_STATE_TXT3, SET_STATE_TXT4, +// SET_FRIENDLYNAME1, SET_FRIENDLYNAME2, SET_FRIENDLYNAME3, SET_FRIENDLYNAME4, + +// SET_FRIENDLYNAME5, SET_FRIENDLYNAME6, SET_FRIENDLYNAME7, SET_FRIENDLYNAME8, // Future extension + +// SET_NTPSERVER1, SET_NTPSERVER2, SET_NTPSERVER3, +// SET_MEM1, SET_MEM2, SET_MEM3, SET_MEM4, SET_MEM5, +// SET_CORS, + +// SET_BUTTON1, SET_BUTTON2, SET_BUTTON3, SET_BUTTON4, // Future extension +// SET_BUTTON5, SET_BUTTON6, SET_BUTTON7, SET_BUTTON8, // Future extension +// SET_BUTTON9, SET_BUTTON10, SET_BUTTON11, SET_BUTTON12, // Future extension +// SET_BUTTON13, SET_BUTTON14, SET_BUTTON15, SET_BUTTON16, // Future extension + + SET_MAX }; + +const uint32_t settings_loc_num = 1; // First phase only ota_url and mqtt_prefix +const uint32_t settings_max_size = 134; + +char settings_fullstr[settings_max_size] = { 0 }; + +struct LOCATIONS { + char* address; + uint32_t size = 0; +} Location[settings_loc_num]; + +void SettingsInitText(void) +{ + for (uint32_t i = 0; i < settings_loc_num; i++) { + if (0 == i) { + Location[i].address = Settings.ota_url; + Location[i].size = sizeof(Settings.ota_url) + (3 * sizeof(Settings.mqtt_prefix[0])); +// Location[i].address = Settings.char_chunk1; +// Location[i].size = sizeof(Settings.char_chunk1); // 134 + } + else if (1 == i) { + Location[i].address = Settings.sta_ssid[0]; + Location[i].size = (2 * sizeof(Settings.sta_ssid[0])) + (2 * sizeof(Settings.sta_pwd[0])) + sizeof(Settings.hostname) + sizeof(Settings.syslog_host); +// Location[i].address = Settings.char_chunk2; +// Location[i].size = sizeof(Settings.char_chunk2); // 262 + } + else if (2 == i) { + // Need to move Settings.mqtt_port first! + Location[i].address = Settings.mqtt_host; + Location[i].size = sizeof(Settings.mqtt_host) + 2 + sizeof(Settings.mqtt_client) + sizeof(Settings.mqtt_user) + sizeof(Settings.mqtt_pwd) + sizeof(Settings.mqtt_topic) + sizeof(Settings.button_topic) + sizeof(Settings.mqtt_grptopic); +// Location[i].address = Settings.char_chunk3; +// Location[i].size = sizeof(Settings.char_chunk3); // 233 + } + } + + SettingsCopyText(0); // Load +} + +void SettingsCopyText(uint32_t direction) +{ + char* fullstr = settings_fullstr; + uint32_t size = 0; + for (uint32_t i = 0; i < settings_loc_num; i++) { + size = Location[i].size; + if (1 == direction) { + memcpy(Location[i].address, fullstr, size); // Save to Settings + } else { + memcpy(fullstr, Location[i].address, size); // Load from Settings + } + fullstr += size; + } +} + +bool SettingsUpdateText(uint32_t index, char* replace) +{ + if (index >= SET_MAX) { + return false; // Setting not supported - internal error + } + +// SettingsCopyText(0); // Load + + uint32_t start_pos = 0; + uint32_t end_pos = 0; + char* position = settings_fullstr; + for (uint32_t size = 0; size < SET_MAX; size++) { + while (*position++ != '\0') { } + if (1 == index) { + start_pos = position - settings_fullstr; + } + else if (0 == index) { + end_pos = position - settings_fullstr -1; + } + index--; + } + uint32_t len_pos = position - settings_fullstr; + + uint32_t current_len = end_pos - start_pos; + uint32_t replace_len = strlen(replace); + int diff = replace_len - current_len; + +// AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TST: start %d, end %d, len %d, current %d, replace %d, diff %d"), +// start_pos, end_pos, len_pos, current_len, replace_len, diff); + + int too_long = (len_pos + diff) - sizeof(settings_fullstr); + if (too_long > 0) { +// AddLog_P2(LOG_LEVEL_INFO, PSTR("CFG: Text too long by %d char(s)"), too_long); + return false; // Replace text too long + } + + if (diff != 0) { + // Shift full text string up or down + memmove_P(settings_fullstr + start_pos + replace_len, settings_fullstr + end_pos, len_pos - end_pos); + } + // Replace text + memmove_P(settings_fullstr + start_pos, replace, replace_len); + // Fill for future use + memset(settings_fullstr + len_pos + diff, 0x00, settings_max_size - len_pos - diff); + +// SettingsCopyText(1); // Save - Hold of for now + + return true; +} + +char* SettingsGetText(uint32_t index) +{ + if (index >= SET_MAX) { + return nullptr; // Setting not supported - internal error + } + +// SettingsCopyText(0); // Load + + char* position = settings_fullstr; + for (;index > 0; index--) { + while (*position++ != '\0') { } + } + return position; +} + /*********************************************************************************************\ * Config Save - Save parameters to Flash ONLY if any parameter has changed \*********************************************************************************************/ @@ -561,6 +707,8 @@ void SettingsLoad(void) settings_crc32 = GetSettingsCrc32(); #endif // FIRMWARE_MINIMAL + SettingsInitText(); + RtcSettingsLoad(); } From e09735dbaed91f8600d3b64b03ad1961b0a60a47 Mon Sep 17 00:00:00 2001 From: znanev <20048364+znanev@users.noreply.github.com> Date: Thu, 12 Dec 2019 14:18:12 +0000 Subject: [PATCH 62/84] Update bg-BG.h Update Bulgarian translation up to v7.1.2.4 --- tasmota/language/bg-BG.h | 76 ++++++++++++++++++++-------------------- 1 file changed, 38 insertions(+), 38 deletions(-) diff --git a/tasmota/language/bg-BG.h b/tasmota/language/bg-BG.h index d39aba5f6..d91db2dee 100644 --- a/tasmota/language/bg-BG.h +++ b/tasmota/language/bg-BG.h @@ -28,7 +28,7 @@ * Use online command StateText to translate ON, OFF, HOLD and TOGGLE. * Use online command Prefix to translate cmnd, stat and tele. * - * Updated until v6.5.0.8 + * Updated until v7.1.2.4 \*********************************************************************/ //#define LANGUAGE_MODULE_NAME // Enable to display "Module Generic" (ie Spanish), Disable to display "Generic Module" (ie English) @@ -71,7 +71,7 @@ #define D_COLDLIGHT "Хладна" #define D_COMMAND "Команда" #define D_CONNECTED "Свързан" -#define D_CORS_DOMAIN "CORS Domain" +#define D_CORS_DOMAIN "CORS домейн" #define D_COUNT "Брой" #define D_COUNTER "Брояч" #define D_CURRENT "Ток" // As in Voltage and Current @@ -83,7 +83,7 @@ #define D_DNS_SERVER "DNS Сървър" #define D_DONE "Изпълнено" #define D_DST_TIME "Лятно време" -#define D_ECO2 "eCO₂" +#define D_ECO2 "eCO2" #define D_EMULATION "Емулация" #define D_ENABLED "Разрешен" #define D_ERASE "Изтриване" @@ -114,7 +114,7 @@ #define D_LWT "LWT" #define D_MODULE "Модул" #define D_MQTT "MQTT" -#define D_MULTI_PRESS "множествено натискане" +#define D_MULTI_PRESS "неколкократно натискане" #define D_NOISE "Шум" #define D_NONE "Няма" #define D_OFF "Изкл." @@ -285,10 +285,10 @@ #define D_LOGGING_PARAMETERS "Параметри на лога" #define D_SERIAL_LOG_LEVEL "Степен на серийния лог" -#define D_MQTT_LOG_LEVEL "Mqtt log level" +#define D_MQTT_LOG_LEVEL "Степен на MQTT лога" #define D_WEB_LOG_LEVEL "Степен на уеб лога" #define D_SYS_LOG_LEVEL "Степен на системния лог" -#define D_MORE_DEBUG "Още дебъгване" +#define D_MORE_DEBUG "Допълнителна debug информация" #define D_SYSLOG_HOST "Хост на системния лог" #define D_SYSLOG_PORT "Порт на системния лог" #define D_TELEMETRY_PERIOD "Период на телеметрия" @@ -382,7 +382,7 @@ #define D_HUE "Hue" #define D_HUE_BRIDGE_SETUP "Настройка на Hue bridge" -#define D_HUE_API_NOT_IMPLEMENTED "Hue API не е внедрено" +#define D_HUE_API_NOT_IMPLEMENTED "Hue API не е внедрен" #define D_HUE_API "Hue API" #define D_HUE_POST_ARGS "Hue POST аргументи" #define D_3_RESPONSE_PACKETS_SENT "Изпратени са 3 пакета за отговор" @@ -444,17 +444,17 @@ #define D_ENERGY_TOTAL "Използвана енергия общо" // xdrv_27_shutter.ino -#define D_OPEN "Open" -#define D_CLOSE "Close" -#define D_DOMOTICZ_SHUTTER "Shutter" +#define D_OPEN "Отворена" +#define D_CLOSE "Затворена" +#define D_DOMOTICZ_SHUTTER "Щора" // xdrv_28_pcf8574.ino -#define D_CONFIGURE_PCF8574 "Configure PCF8574" -#define D_PCF8574_PARAMETERS "PCF8574 parameters" -#define D_INVERT_PORTS "Invert Ports" -#define D_DEVICE "Device" -#define D_DEVICE_INPUT "Input" -#define D_DEVICE_OUTPUT "Output" +#define D_CONFIGURE_PCF8574 "Конфигуриране на PCF8574" +#define D_PCF8574_PARAMETERS "PCF8574 параметри" +#define D_INVERT_PORTS "Обърни портовете" +#define D_DEVICE "Устройство" +#define D_DEVICE_INPUT "Вход" +#define D_DEVICE_OUTPUT "Изход" // xsns_05_ds18b20.ino #define D_SENSOR_BUSY "Датчикът DS18x20 е зает" @@ -675,27 +675,27 @@ #define D_UNIT_ANGLE "°" //SOLAXX1 -#define D_PV1_VOLTAGE "PV1 Voltage" -#define D_PV1_CURRENT "PV1 Current" -#define D_PV1_POWER "PV1 Power" -#define D_PV2_VOLTAGE "PV2 Voltage" -#define D_PV2_CURRENT "PV2 Current" -#define D_PV2_POWER "PV2 Power" -#define D_SOLAR_POWER "Solar Power" -#define D_INVERTER_POWER "Inverter Power" -#define D_STATUS "Status" -#define D_WAITING "Waiting" -#define D_CHECKING "Checking" -#define D_WORKING "Working" -#define D_FAILURE "Failure" -#define D_SOLAX_ERROR_0 "No Error Code" -#define D_SOLAX_ERROR_1 "Grid Lost Fault" -#define D_SOLAX_ERROR_2 "Grid Voltage Fault" -#define D_SOLAX_ERROR_3 "Grid Frequency Fault" -#define D_SOLAX_ERROR_4 "Pv Voltage Fault" -#define D_SOLAX_ERROR_5 "Isolation Fault" -#define D_SOLAX_ERROR_6 "Over Temperature Fault" -#define D_SOLAX_ERROR_7 "Fan Fault" -#define D_SOLAX_ERROR_8 "Other Device Fault" +#define D_PV1_VOLTAGE "Напрежение на PV1" +#define D_PV1_CURRENT "Ток на PV1" +#define D_PV1_POWER "Мощност на PV1" +#define D_PV2_VOLTAGE "Напрежение на PV2" +#define D_PV2_CURRENT "Ток на PV2" +#define D_PV2_POWER "Мощност на PV2" +#define D_SOLAR_POWER "Слънчева мощност" +#define D_INVERTER_POWER "Мощност на инвертера" +#define D_STATUS "Състояние" +#define D_WAITING "Очакване" +#define D_CHECKING "Проверка" +#define D_WORKING "Работи" +#define D_FAILURE "Грешка" +#define D_SOLAX_ERROR_0 "Грешка - няма код" +#define D_SOLAX_ERROR_1 "Грешка - загуба на мрежата" +#define D_SOLAX_ERROR_2 "Грешка - мрежово напрежение" +#define D_SOLAX_ERROR_3 "Грешка - мрежова честота" +#define D_SOLAX_ERROR_4 "Грешка - напрежение на Pv" +#define D_SOLAX_ERROR_5 "Грешка - проблем с изолацията" +#define D_SOLAX_ERROR_6 "Грешка - прегряване" +#define D_SOLAX_ERROR_7 "Грешка - вентилатор" +#define D_SOLAX_ERROR_8 "Грешка - друго оборудване" #endif // _LANGUAGE_BG_BG_H_ From e53f637bb6b94b81ca2b3f335f1fabea6a6882c6 Mon Sep 17 00:00:00 2001 From: stefanbode Date: Thu, 12 Dec 2019 16:05:58 +0100 Subject: [PATCH 63/84] Mode definitions to i18 to enable other modules to use shutters --- tasmota/i18n.h | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/tasmota/i18n.h b/tasmota/i18n.h index f4fde64b3..fed6930d7 100644 --- a/tasmota/i18n.h +++ b/tasmota/i18n.h @@ -494,6 +494,24 @@ #define D_JSON_MOTOR_MIS "setMIS" #endif + // Commands xdrv_27_Shutter.ino + #ifdef USE_SHUTTER + #define D_PRFX_SHUTTER "Shutter" + #define D_CMND_SHUTTER_OPEN "Open" + #define D_CMND_SHUTTER_CLOSE "Close" + #define D_CMND_SHUTTER_STOP "Stop" + #define D_CMND_SHUTTER_POSITION "Position" + #define D_CMND_SHUTTER_OPENTIME "OpenDuration" + #define D_CMND_SHUTTER_CLOSETIME "CloseDuration" + #define D_CMND_SHUTTER_RELAY "Relay" + #define D_CMND_SHUTTER_SETHALFWAY "SetHalfway" + #define D_CMND_SHUTTER_SETCLOSE "SetClose" + #define D_CMND_SHUTTER_INVERT "Invert" + #define D_CMND_SHUTTER_CLIBRATION "Calibration" + #define D_CMND_SHUTTER_MOTORDELAY "MotorDelay" + #define D_CMND_SHUTTER_FREQUENCY "Frequency" + #endif + /********************************************************************************************/ // Log message prefix From c9703c5584db4f19b237c0b35003fe91ad31fc0c Mon Sep 17 00:00:00 2001 From: znanev <20048364+znanev@users.noreply.github.com> Date: Thu, 12 Dec 2019 15:11:21 +0000 Subject: [PATCH 64/84] Update bg-BG.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change eCO2 to eCO₂ --- tasmota/language/bg-BG.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tasmota/language/bg-BG.h b/tasmota/language/bg-BG.h index d91db2dee..1f18d8efb 100644 --- a/tasmota/language/bg-BG.h +++ b/tasmota/language/bg-BG.h @@ -83,7 +83,7 @@ #define D_DNS_SERVER "DNS Сървър" #define D_DONE "Изпълнено" #define D_DST_TIME "Лятно време" -#define D_ECO2 "eCO2" +#define D_ECO2 "eCO₂" #define D_EMULATION "Емулация" #define D_ENABLED "Разрешен" #define D_ERASE "Изтриване" From 29de8b9dce5607326d8d42a6f5b28128cd93388e Mon Sep 17 00:00:00 2001 From: stefanbode Date: Thu, 12 Dec 2019 16:14:06 +0100 Subject: [PATCH 65/84] Update several functions - finally, secure pressing DOWN/UP button when shutter already in end-position will not turn ON the relay - remove shutter commands to main to allow other modules to interact - fix some overrun and underrun issues to end-positions --- tasmota/xdrv_27_shutter.ino | 104 +++++++++++++++++++++--------------- 1 file changed, 60 insertions(+), 44 deletions(-) diff --git a/tasmota/xdrv_27_shutter.ino b/tasmota/xdrv_27_shutter.ino index 8bc13dfa4..b3c02c80f 100644 --- a/tasmota/xdrv_27_shutter.ino +++ b/tasmota/xdrv_27_shutter.ino @@ -24,27 +24,10 @@ #define XDRV_27 27 -#define D_PRFX_SHUTTER "Shutter" -#define D_CMND_SHUTTER_OPEN "Open" -#define D_CMND_SHUTTER_CLOSE "Close" -#define D_CMND_SHUTTER_STOP "Stop" -#define D_CMND_SHUTTER_POSITION "Position" -#define D_CMND_SHUTTER_OPENTIME "OpenDuration" -#define D_CMND_SHUTTER_CLOSETIME "CloseDuration" -#define D_CMND_SHUTTER_RELAY "Relay" -#define D_CMND_SHUTTER_SETHALFWAY "SetHalfway" -#define D_CMND_SHUTTER_SETCLOSE "SetClose" -#define D_CMND_SHUTTER_INVERT "Invert" -#define D_CMND_SHUTTER_CLIBRATION "Calibration" -#define D_CMND_SHUTTER_MOTORDELAY "MotorDelay" -#define D_CMND_SHUTTER_FREQUENCY "Frequency" - #define D_SHUTTER "SHUTTER" const uint16_t MOTOR_STOP_TIME = 500; // in mS - - uint8_t calibrate_pos[6] = {0,30,50,70,90,100}; uint16_t messwerte[5] = {30,50,70,90,100}; @@ -82,9 +65,10 @@ struct SHUTTER { uint16_t close_velocity[MAX_SHUTTERS]; // in relation to open velocity. higher value = faster int8_t direction[MAX_SHUTTERS]; // 1 == UP , 0 == stop; -1 == down uint8_t mode = 0; // operation mode definition. see enum type above SHT_OFF_OPEN__OFF_CLOSE, SHT_OFF_ON__OPEN_CLOSE, SHT_PULSE_OPEN__PULSE_CLOSE - uint8_t motordelay[MAX_SHUTTERS]; // initial motorstarttime in 0.05sec. + int16_t motordelay[MAX_SHUTTERS]; // initial motorstarttime in 0.05sec. uint16_t pwm_frequency; // frequency of PWN for stepper motors - uint16_t max_pwm_frequency = 1000; + uint16_t max_pwm_frequency = 1000; // maximum of PWM frequency that can be used. depend on the motor and drivers + uint8_t skip_relay_change; // avoid overrun at endstops } Shutter; void ShutterRtc50mS(void) @@ -99,7 +83,7 @@ int32_t ShutterPercentToRealPosition(uint8_t percent,uint8_t index) if (Settings.shutter_set50percent[index] != 50) { return percent <= 5 ? Settings.shuttercoeff[2][index] * percent : Settings.shuttercoeff[1][index] * percent + Settings.shuttercoeff[0][index]; } else { - uint32_t realpos; + int32_t realpos; // check against DIV 0 for (uint8_t j=0 ; j < 5 ; j++) { if (Settings.shuttercoeff[j][index] == 0) { @@ -133,7 +117,7 @@ uint8_t ShutterRealToPercentPosition(int32_t realpos, uint8_t index) if (Settings.shutter_set50percent[index] != 50) { return Settings.shuttercoeff[2][index] * 5 > realpos ? realpos / Settings.shuttercoeff[2][index] : (realpos-Settings.shuttercoeff[0][index]) / Settings.shuttercoeff[1][index]; } else { - uint16_t realpercent; + int16_t realpercent; for (uint8_t i=0 ; i < 5 ; i++) { if (realpos > Shutter.open_max[index] * calibrate_pos[i+1] / 100) { @@ -151,7 +135,7 @@ uint8_t ShutterRealToPercentPosition(int32_t realpos, uint8_t index) break; } } - return realpercent; + return (realpercent < 0 ? 0 : (realpercent > 100 ? 0 : realpercent)); } } @@ -230,7 +214,7 @@ void ShutterInit(void) dtostrfd((float)Shutter.open_time[i] / 10 , 1, shutter_open_chr); char shutter_close_chr[10]; dtostrfd((float)Shutter.close_time[i] / 10, 1, shutter_close_chr); - AddLog_P2(LOG_LEVEL_INFO, PSTR("SHT: Shutter %d (Relay:%d): Init. Pos: %d [%d %%], Open Vel.: 100 Close Vel.: %d , Max Way: %d, Opentime %s [s], Closetime %s [s], CoeffCalc: c0: %d, c1 %d, c2: %d, c3: %d, c4: %d, binmask %d, is inverted %d, shuttermode %d,motordelay %d"), + AddLog_P2(LOG_LEVEL_INFO, PSTR("SHT: Shutter %d (Relay:%d): Init. Pos: %d [%d %%], Open Vel.: 100 Close Vel.: %d , Max Way: %d, Opentime %s [s], Closetime %s [s], CoedffCalc: c0: %d, c1 %d, c2: %d, c3: %d, c4: %d, binmask %d, is inverted %d, shuttermode %d,motordelay %d"), i+1, Settings.shutter_startrelay[i], Shutter.real_position[i], Settings.shutter_position[i], Shutter.close_velocity[i], Shutter.open_max[i], shutter_open_chr, shutter_close_chr, Settings.shuttercoeff[0][i], Settings.shuttercoeff[1][i], Settings.shuttercoeff[2][i], Settings.shuttercoeff[3][i], Settings.shuttercoeff[4][i], Shutter.mask, Settings.shutter_invert[i], Shutter.mode, Shutter.motordelay[i]); @@ -257,7 +241,7 @@ void ShutterUpdatePosition(void) // Counter should be initiated to 0 to count movement. // 0..1000 in step 100 = 10 steps with 0.05 sec = 0.5sec total ramp time from start to // full speed. - if (pin[GPIO_PWM1+i] < 99 && Shutter.pwm_frequency != Shutter.max_pwm_frequency) { + if (Shutter.mode == SHT_OFF_ON__OPEN_CLOSE && pin[GPIO_PWM1+i] < 99 && Shutter.pwm_frequency != Shutter.max_pwm_frequency) { Shutter.pwm_frequency += Shutter.max_pwm_frequency/20; Shutter.pwm_frequency = (Shutter.pwm_frequency > Shutter.max_pwm_frequency ? Shutter.max_pwm_frequency : Shutter.pwm_frequency); analogWriteFreq(Shutter.pwm_frequency); @@ -266,7 +250,7 @@ void ShutterUpdatePosition(void) Shutter.real_position[i] = Shutter.start_position[i] + ( (Shutter.time[i] - Shutter.motordelay[i]) * (Shutter.direction[i] > 0 ? 100 : -Shutter.close_velocity[i])); // avoid real position leaving the boundaries. - Shutter.real_position[i] = Shutter.real_position[i] < 0 ? 0 : (Shutter.real_position[i] > Shutter.open_max[i] ? Shutter.open_max[i] : Shutter.real_position[i]) ; + // Shutter.real_position[i] = Shutter.real_position[i] < 0 ? 0 : (Shutter.real_position[i] > Shutter.open_max[i] ? Shutter.open_max[i] : Shutter.real_position[i]) ; if (Shutter.real_position[i] * Shutter.direction[i] >= Shutter.target_position[i] * Shutter.direction[i] ) { @@ -295,8 +279,9 @@ void ShutterUpdatePosition(void) analogWrite(pin[GPIO_PWM1+i], 50); //prepare for stop PWM Shutter.motordelay[i] = -2 + Shutter.motordelay[i] + missing_steps/(Shutter.max_pwm_frequency/20); + Shutter.motordelay[i] = Shutter.motordelay[i] > 0 ? Shutter.motordelay[i] : 0; AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Missing steps %d, adjust motordelay %d, counter %d, temp realpos %d"), missing_steps, Shutter.motordelay[i],RtcSettings.pulse_counter[i] ,Shutter.real_position[i]); - Settings.shutter_motordelay[i]=(missing_steps > 0 ? Shutter.motordelay[i] : 0); + Settings.shutter_motordelay[i]=Shutter.motordelay[i]; analogWriteFreq(0); while (RtcSettings.pulse_counter[i] < (uint32_t)(Shutter.target_position[i]-Shutter.start_position[i])*Shutter.direction[i]*Shutter.max_pwm_frequency/2000) { delay(1); @@ -351,19 +336,35 @@ bool ShutterState(uint8_t device) (Shutter.mask & (1 << (Settings.shutter_startrelay[device]-1))) ); } -void ShutterStartInit(uint8_t index, uint8_t direction, int32_t target_pos) +void ShutterStartInit(uint8_t index, int8_t direction, int32_t target_pos) { - Shutter.direction[index] = direction; - Shutter.target_position[index] = target_pos; - Shutter.start_position[index] = Shutter.real_position[index]; - Shutter.time[index] = 0; - if (pin[GPIO_PWM1+index] < 99) { - Shutter.pwm_frequency = 0; - analogWriteFreq(Shutter.pwm_frequency); - analogWrite(pin[GPIO_PWM1+index], 0); - // can be operated without counter, but then not that acurate. - if (pin[GPIO_CNTR1+index] < 99) { - RtcSettings.pulse_counter[index] = 0; + //AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: dir %d, delta1 %d, delta2 %d, grant %d"),direction, (Shutter.open_max[index] - Shutter.real_position[index]) / Shutter.close_velocity[index], Shutter.real_position[index] / Shutter.close_velocity[index], 2+Shutter.motordelay[index]); + if (direction == 1 && (Shutter.open_max[index] - Shutter.real_position[index]) / Shutter.close_velocity[index] <= 2+Shutter.motordelay[index]) { + // shutter alread 100% cannot move further + // store relay number variable + Shutter.skip_relay_change = 1 ; + //AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: 100%% endstop already reached: Relay %d"),Shutter.skip_relay_change); + + } else if (direction == -1 && Shutter.real_position[index] / Shutter.close_velocity[index] <= 2+Shutter.motordelay[index]) { + // shutter already at 0% connot move into minus + // store relay number variable + Shutter.skip_relay_change = 1; + //AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: 0%% endstop already reached: Relay %d"),Shutter.skip_relay_change); + + } else { + Shutter.direction[index] = direction; + Shutter.target_position[index] = target_pos; + Shutter.start_position[index] = Shutter.real_position[index]; + Shutter.time[index] = 0; + Shutter.skip_relay_change = 0; + if (pin[GPIO_PWM1+index] < 99) { + Shutter.pwm_frequency = 0; + analogWriteFreq(Shutter.pwm_frequency); + analogWrite(pin[GPIO_PWM1+index], 0); + // can be operated without counter, but then not that acurate. + if (pin[GPIO_CNTR1+index] < 99) { + RtcSettings.pulse_counter[index] = 0; + } } } //AddLog_P2(LOG_LEVEL_INFO, PSTR("SHT: Start shutter: %d from %d to %d in directin %d"), index, Shutter.start_position[index], Shutter.target_position[index], Shutter.direction[index]); @@ -378,7 +379,7 @@ void ShutterDelayForMotorStop(void) void ShutterReportPosition(void) { uint16_t shutter_moving = 0; - for (uint32_t i = 0; i < shutters_present; i++) { + for (uint8_t i = 0; i < shutters_present; i++) { if (Shutter.direction[i] != 0) { char stemp1[20]; char stemp2[10]; @@ -414,7 +415,7 @@ void ShutterRelayChanged(void) switch (powerstate_local) { case 1: ShutterDelayForMotorStop(); - ShutterStartInit(i, 1, Shutter.open_max[i]); + ShutterStartInit(i, 1, Shutter.open_max[i]); break; case 3: ShutterDelayForMotorStop(); @@ -652,7 +653,7 @@ void CmndShutterSetClose(void) Shutter.real_position[XdrvMailbox.index -1] = 0; ShutterStartInit(XdrvMailbox.index -1, 0, 0); Settings.shutter_position[XdrvMailbox.index -1] = 0; - ResponseCmndChar(D_CONFIGURATION_RESET); + ResponseCmndIdxChar(D_CONFIGURATION_RESET); } } @@ -717,7 +718,7 @@ bool Xdrv27(uint8_t function) result = DecodeCommand(kShutterCommands, ShutterCommand); break; case FUNC_JSON_APPEND: - for (uint32_t i = 0; i < shutters_present; i++) { + for (uint8_t i = 0; i < shutters_present; i++) { uint8_t position = Settings.shutter_invert[i] ? 100 - Settings.shutter_position[i]: Settings.shutter_position[i]; ResponseAppend_P(","); ResponseAppend_P(JSON_SHUTTER_POS, i+1, position, Shutter.direction[i]); @@ -731,10 +732,25 @@ bool Xdrv27(uint8_t function) case FUNC_SET_POWER: char stemp1[10]; // extract the number of the relay that was switched and save for later in Update Position. - Shutter.switched_relay = power ^ Shutter.old_power; - Shutter.old_power = power; + Shutter.switched_relay = XdrvMailbox.index ^ Shutter.old_power; AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Switched relay: %d by %s"), Shutter.switched_relay,GetTextIndexed(stemp1, sizeof(stemp1), last_source, kCommandSource)); ShutterRelayChanged(); + Shutter.old_power = XdrvMailbox.index; + break; + case FUNC_SET_DEVICE_POWER: + if (Shutter.skip_relay_change ) { + uint8_t i; + for (i = 0; i < devices_present; i++) { + if (Shutter.switched_relay &1) { + break; + } + Shutter.switched_relay >>= 1; + } + //AddLog_P2(LOG_LEVEL_ERROR, PSTR("SHT: skip relay change: %d"),i+1); + result = true; + Shutter.skip_relay_change = 0; + ExecuteCommandPower(i+1, 0, SRC_SHUTTER); + } break; } } From bfeaabfc7f5a3dc3ac26e87935f1cd5c25e560a9 Mon Sep 17 00:00:00 2001 From: stefanbode Date: Thu, 12 Dec 2019 17:03:25 +0100 Subject: [PATCH 66/84] typo --- tasmota/xdrv_27_shutter.ino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tasmota/xdrv_27_shutter.ino b/tasmota/xdrv_27_shutter.ino index b3c02c80f..2338639a1 100644 --- a/tasmota/xdrv_27_shutter.ino +++ b/tasmota/xdrv_27_shutter.ino @@ -214,7 +214,7 @@ void ShutterInit(void) dtostrfd((float)Shutter.open_time[i] / 10 , 1, shutter_open_chr); char shutter_close_chr[10]; dtostrfd((float)Shutter.close_time[i] / 10, 1, shutter_close_chr); - AddLog_P2(LOG_LEVEL_INFO, PSTR("SHT: Shutter %d (Relay:%d): Init. Pos: %d [%d %%], Open Vel.: 100 Close Vel.: %d , Max Way: %d, Opentime %s [s], Closetime %s [s], CoedffCalc: c0: %d, c1 %d, c2: %d, c3: %d, c4: %d, binmask %d, is inverted %d, shuttermode %d,motordelay %d"), + AddLog_P2(LOG_LEVEL_INFO, PSTR("SHT: Shutter %d (Relay:%d): Init. Pos: %d [%d %%], Open Vel.: 100 Close Vel.: %d , Max Way: %d, Opentime %s [s], Closetime %s [s], CoeffCalc: c0: %d, c1 %d, c2: %d, c3: %d, c4: %d, binmask %d, is inverted %d, shuttermode %d,motordelay %d"), i+1, Settings.shutter_startrelay[i], Shutter.real_position[i], Settings.shutter_position[i], Shutter.close_velocity[i], Shutter.open_max[i], shutter_open_chr, shutter_close_chr, Settings.shuttercoeff[0][i], Settings.shuttercoeff[1][i], Settings.shuttercoeff[2][i], Settings.shuttercoeff[3][i], Settings.shuttercoeff[4][i], Shutter.mask, Settings.shutter_invert[i], Shutter.mode, Shutter.motordelay[i]); From 7d8a4f5077ed51c513c51fbb02240b4fa59c0a18 Mon Sep 17 00:00:00 2001 From: Hadinger Date: Thu, 12 Dec 2019 22:22:34 +0100 Subject: [PATCH 67/84] Fix Zigbee crash #7187 --- tasmota/xdrv_23_zigbee_9_impl.ino | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tasmota/xdrv_23_zigbee_9_impl.ino b/tasmota/xdrv_23_zigbee_9_impl.ino index cb08eeaee..9a08c3585 100644 --- a/tasmota/xdrv_23_zigbee_9_impl.ino +++ b/tasmota/xdrv_23_zigbee_9_impl.ino @@ -235,13 +235,14 @@ void ZigbeeInit(void) * Commands \*********************************************************************************************/ -uint32_t strToUInt(const JsonVariant val) { +uint32_t strToUInt(const JsonVariant &val) { // if the string starts with 0x, it is considered Hex, otherwise it is an int if (val.is()) { return val.as(); } else { - if (val.is()) { - return strtoull(val.as(), nullptr, 0); + if (val.is()) { + String sval = val.as(); + return strtoull(sval.c_str(), nullptr, 0); } } return 0; // couldn't parse anything @@ -593,6 +594,7 @@ void CmndZigbeeRead(void) { const JsonVariant &val_attr = getCaseInsensitive(json, PSTR("Read")); if (nullptr != &val_attr) { + uint16_t val = strToUInt(val_attr); if (val_attr.is()) { JsonArray& attr_arr = val_attr; attrs_len = attr_arr.size() * 2; @@ -604,11 +606,9 @@ void CmndZigbeeRead(void) { attrs[i++] = val & 0xFF; attrs[i++] = val >> 8; } - } else { attrs_len = 2; attrs = new uint8_t[attrs_len]; - uint16_t val = strToUInt(val_attr); attrs[0] = val & 0xFF; // little endian attrs[1] = val >> 8; } From 0c717ddbac4d08da681da09f69addcb583e9bdf6 Mon Sep 17 00:00:00 2001 From: Hadinger Date: Thu, 12 Dec 2019 22:32:00 +0100 Subject: [PATCH 68/84] Added ResponseCmndDone() to ZigbeeRead --- tasmota/xdrv_23_zigbee_9_impl.ino | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tasmota/xdrv_23_zigbee_9_impl.ino b/tasmota/xdrv_23_zigbee_9_impl.ino index 9a08c3585..4e2ba5499 100644 --- a/tasmota/xdrv_23_zigbee_9_impl.ino +++ b/tasmota/xdrv_23_zigbee_9_impl.ino @@ -614,9 +614,10 @@ void CmndZigbeeRead(void) { } } - ZigbeeZCLSend(device, cluster, endpoint, ZCL_READ_ATTRIBUTES, false, attrs, attrs_len, false /* we do want a response */); + ZigbeeZCLSend(device, cluster, endpoint, ZCL_READ_ATTRIBUTES, false, attrs, attrs_len, false /* we do want a response */); - if (attrs) { delete[] attrs; } + if (attrs) { delete[] attrs; } + ResponseCmndDone(); } // Allow or Deny pairing of new Zigbee devices From 21fa1fa04e2bd8872be15b42889afc687badf754 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Fri, 13 Dec 2019 12:05:07 +0100 Subject: [PATCH 69/84] Bump version to 7.1.2.5 Change some Settings locations freeing up space for future single char allowing variable length text --- RELEASENOTES.md | 3 ++- tasmota/CHANGELOG.md | 4 +++ tasmota/settings.h | 49 +++++++++++++++++++++++++--------- tasmota/settings.ino | 10 +++++-- tasmota/support_statistics.ino | 9 +------ tasmota/tasmota_version.h | 2 +- 6 files changed, 52 insertions(+), 25 deletions(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index d269109e8..0f5e1e33b 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -47,12 +47,13 @@ The following binary downloads have been compiled with ESP8266/Arduino library c ## Changelog -### Version 7.1.2.4 +### Version 7.1.2.5 - Change Exception reporting removing exception details from ``Status 1`` and consolidated in ``Status 12`` if available - Change HTTP CORS from command ``SetOption73 0/1`` to ``Cors `` allowing user control of specific CORS domain by Shantur Rathore (#7066) - Change GUI Shutter button text to Up and Down Arrows based on PR by Xavier Muller (#7166) - Change amount of supported DHT sensors from 3 to 4 by Xavier Muller (#7167) +- Change some Settings locations freeing up space for future single char allowing variable length text - Fix flashing H801 led at boot by Stefan Hadinger (#7165, #649) - Fix duplicated ``Backlog`` when using Event inside a Backlog by Adrian Scillato (#7178, #7147) - Fix Gui Timer when using a negative zero offset of -00:00 by Peter Ooms (#7174) diff --git a/tasmota/CHANGELOG.md b/tasmota/CHANGELOG.md index 4bbeccb26..970d22ca7 100644 --- a/tasmota/CHANGELOG.md +++ b/tasmota/CHANGELOG.md @@ -1,5 +1,9 @@ ## Unreleased (development) +### 7.1.2.5 20191213 + +- Change some Settings locations freeing up space for future single char allowing variable length text + ### 7.1.2.4 20191209 - Change HTTP CORS from command ``SetOption73 0/1`` to ``Cors `` allowing user control of specific CORS domain by Shantur Rathore (#7066) diff --git a/tasmota/settings.h b/tasmota/settings.h index c938f0b39..d05777d17 100644 --- a/tasmota/settings.h +++ b/tasmota/settings.h @@ -248,26 +248,34 @@ struct SYSCFG { SysBitfield flag; // 010 int16_t save_data; // 014 int8_t timezone; // 016 + + // Start of single char array + char ota_url[101]; // 017 char mqtt_prefix[3][11]; // 07C + uint8_t ex_baudrate; // 09D - Free since 6.6.0.9 - uint8_t seriallog_level; // 09E - uint8_t sta_config; // 09F - uint8_t sta_active; // 0A0 + uint8_t ex_seriallog_level; // 09E + uint8_t ex_sta_config; // 09F + uint8_t ex_sta_active; // 0A0 + char sta_ssid[2][33]; // 0A1 - Keep together with sta_pwd as being copied as one chunck with reset 5 char sta_pwd[2][65]; // 0E3 - Keep together with sta_ssid as being copied as one chunck with reset 5 char hostname[33]; // 165 char syslog_host[33]; // 186 - uint8_t rule_stop; // 1A7 - uint16_t syslog_port; // 1A8 - uint8_t syslog_level; // 1AA - uint8_t webserver; // 1AB - uint8_t weblog_level; // 1AC - uint8_t mqtt_fingerprint[2][20]; // 1AD - uint8_t adc_param_type; // 1D5 + + uint8_t ex_rule_stop; // 1A7 + uint16_t ex_syslog_port; // 1A8 + uint8_t ex_syslog_level; // 1AA + uint8_t ex_webserver; // 1AB + uint8_t ex_weblog_level; // 1AC + uint8_t ex_mqtt_fingerprint[2][20]; // 1AD + uint8_t ex_adc_param_type; // 1D5 uint8_t free_1d6[10]; // 1D6 + // End of single char array of 446 chars max (phase 3) + SysBitfield4 flag4; // 1E0 uint8_t serial_config; // 1E4 uint8_t wifi_output_power; // 1E5 @@ -282,6 +290,9 @@ struct SYSCFG { char mqtt_topic[33]; // 26F - Keep together with above items as being copied as one chunck with reset 6 char button_topic[33]; // 290 char mqtt_grptopic[33]; // 2B1 + + // Optional end of single char array of 698 chars max (phase 5) + uint8_t display_model; // 2D2 uint8_t display_mode; // 2D3 uint8_t display_refresh; // 2D4 @@ -302,7 +313,9 @@ struct SYSCFG { int16_t toffset[2]; // 30E uint8_t display_font; // 312 char state_text[4][11]; // 313 + uint8_t ex_energy_power_delta; // 33F - Free since 6.6.0.20 + uint16_t domoticz_update_timer; // 340 uint16_t pwm_range; // 342 unsigned long domoticz_relay_idx[MAX_DOMOTICZ_IDX]; // 344 @@ -338,7 +351,7 @@ struct SYSCFG { char friendlyname[MAX_FRIENDLYNAMES][33]; // 3AC char switch_topic[33]; // 430 char serial_delimiter; // 451 - uint8_t ex_sbaudrate; // 452 - Free since 6.6.0.9 + uint8_t seriallog_level; // 452 uint8_t sleep; // 453 uint16_t domoticz_switch_idx[MAX_DOMOTICZ_IDX]; // 454 uint16_t domoticz_sensor_idx[MAX_DOMOTICZ_SNS_IDX]; // 45C @@ -398,7 +411,6 @@ struct SYSCFG { uint16_t baudrate; // 778 uint16_t sbaudrate; // 77A EnergyUsage energy_usage; // 77C -// uint32_t drivers[3]; // 794 - 6.5.0.12 replaced by below three entries uint32_t adc_param1; // 794 uint32_t adc_param2; // 798 int adc_param3; // 79C @@ -414,7 +426,9 @@ struct SYSCFG { unsigned long energy_frequency_calibration; // 7C8 also used by HX711 to save last weight uint16_t web_refresh; // 7CC char mems[MAX_RULE_MEMS][10]; // 7CE + char rules[MAX_RULE_SETS][MAX_RULE_SIZE]; // 800 uses 512 bytes in v5.12.0m, 3 x 512 bytes in v5.14.0b + TuyaFnidDpidMap tuya_fnid_map[MAX_TUYA_FUNCTIONS]; // E00 32 bytes uint16_t ina226_r_shunt[4]; // E20 uint16_t ina226_i_fs[4]; // E28 @@ -436,8 +450,17 @@ struct SYSCFG { uint8_t weight_change; // E9F uint8_t web_color2[2][3]; // EA0 - Needs to be on integer / 3 distance from web_color char cors_domain[33]; // EA6 + uint8_t sta_config; // EC7 + uint8_t sta_active; // EC8 + uint8_t rule_stop; // EC9 + uint16_t syslog_port; // ECA + uint8_t syslog_level; // ECC + uint8_t webserver; // ECD + uint8_t weblog_level; // ECE + uint8_t mqtt_fingerprint[2][20]; // ECF + uint8_t adc_param_type; // EF7 - uint8_t free_ec7[293]; // EC7 + uint8_t free_ef8[244]; // EF8 uint32_t i2c_drivers[3]; // FEC I2cDriver uint32_t cfg_timestamp; // FF8 diff --git a/tasmota/settings.ino b/tasmota/settings.ino index d616d4cfc..866e72f99 100644 --- a/tasmota/settings.ino +++ b/tasmota/settings.ino @@ -1230,8 +1230,8 @@ void SettingsDelta(void) } } if (Settings.version < 0x06060009) { - Settings.baudrate = Settings.ex_baudrate * 4; - Settings.sbaudrate = Settings.ex_sbaudrate * 4; + Settings.baudrate = APP_BAUDRATE / 300; + Settings.sbaudrate = SOFT_BAUDRATE / 300; } if (Settings.version < 0x0606000A) { uint8_t tuyaindex = 0; @@ -1336,6 +1336,12 @@ void SettingsDelta(void) Settings.cors_domain[0] = 0; } } + if (Settings.version < 0x07010205) { + Settings.seriallog_level = Settings.ex_seriallog_level; // 09E -> 452 + Settings.sta_config = Settings.ex_sta_config; // 09F -> EC7 + Settings.sta_active = Settings.ex_sta_active; // 0A0 -> EC8 + memcpy((char*)&Settings.rule_stop, (char*)&Settings.ex_rule_stop, 47); // 1A7 -> EC9 + } Settings.version = VERSION; SettingsSave(1); diff --git a/tasmota/support_statistics.ino b/tasmota/support_statistics.ino index efd94f307..a34c7250a 100644 --- a/tasmota/support_statistics.ino +++ b/tasmota/support_statistics.ino @@ -27,24 +27,17 @@ struct STATS { // uint32_t str_size = 1151; // Total number of characters reserved as char array in Settings uint32_t str_len = 0; // Total number of characters used within char array - uint32_t str_char = 0; // Total number of character '|' within all char arrays } Stats; void StatisticsChar(const char* text) { uint32_t len = strlen(text); Stats.str_len += len; - for (uint32_t i = 0; i < len; i++) { - if ('|' == text[i]) { // Text string separator as currently used in GetTextIndexed() - Stats.str_char++; - } - } } String GetStatistics(void) { Stats.str_len = 0; - Stats.str_char = 0; for (uint32_t i = 0; i < 2; i++) { StatisticsChar(Settings.sta_ssid[i]); @@ -77,7 +70,7 @@ String GetStatistics(void) StatisticsChar(Settings.cors_domain); char data[40]; - snprintf_P(data, sizeof(data), PSTR(",\"CR\":\"%d/1151/%d\""), Stats.str_len, Stats.str_char); // Char Usage Ratio + snprintf_P(data, sizeof(data), PSTR(",\"CR\":\"%d/1151\""), Stats.str_len); // Char Usage Ratio return String(data); } diff --git a/tasmota/tasmota_version.h b/tasmota/tasmota_version.h index a0cbe1e41..ccc8dcbaf 100644 --- a/tasmota/tasmota_version.h +++ b/tasmota/tasmota_version.h @@ -20,6 +20,6 @@ #ifndef _TASMOTA_VERSION_H_ #define _TASMOTA_VERSION_H_ -const uint32_t VERSION = 0x07010204; +const uint32_t VERSION = 0x07010205; #endif // _TASMOTA_VERSION_H_ From 25c9666f2dfc6e0e90e551603089ab04ba288f2b Mon Sep 17 00:00:00 2001 From: Hadinger Date: Sat, 14 Dec 2019 12:06:59 +0100 Subject: [PATCH 70/84] Add Zigbee support for Xiaomi Aqara Vibration Sensor and Presence Sensor --- tasmota/CHANGELOG.md | 1 + tasmota/support_float.ino | 1 + tasmota/xdrv_23_zigbee_5_converters.ino | 287 ++++++++---------------- tasmota/xdrv_23_zigbee_8_parsers.ino | 2 +- tasmota/xdrv_23_zigbee_9_impl.ino | 4 +- 5 files changed, 99 insertions(+), 196 deletions(-) diff --git a/tasmota/CHANGELOG.md b/tasmota/CHANGELOG.md index 970d22ca7..8ffde8562 100644 --- a/tasmota/CHANGELOG.md +++ b/tasmota/CHANGELOG.md @@ -3,6 +3,7 @@ ### 7.1.2.5 20191213 - Change some Settings locations freeing up space for future single char allowing variable length text +- Add Zigbee support for Xiaomi Aqara Vibration Sensor and Presence Sensor ### 7.1.2.4 20191209 diff --git a/tasmota/support_float.ino b/tasmota/support_float.ino index 21ec2f47d..d08fc13f9 100644 --- a/tasmota/support_float.ino +++ b/tasmota/support_float.ino @@ -172,6 +172,7 @@ double const f_sixthpi = f_pi / 6.0; // f_pi/6.0, used in double const f_tansixthpi = tan(f_sixthpi); // tan(f_pi/6), used in atan routines double const f_twelfthpi = f_pi / 12.0; // f_pi/12.0, used in atan routines double const f_tantwelfthpi = tan(f_twelfthpi); // tan(f_pi/12), used in atan routines +float const f_180pi = 180 / f_pi; // 180 / pi for angles in degrees // ******************************************************************* // *** diff --git a/tasmota/xdrv_23_zigbee_5_converters.ino b/tasmota/xdrv_23_zigbee_5_converters.ino index bf58a80b9..4defbf043 100644 --- a/tasmota/xdrv_23_zigbee_5_converters.ino +++ b/tasmota/xdrv_23_zigbee_5_converters.ino @@ -49,31 +49,26 @@ public: }; - void publishMQTTReceived(uint16_t groupid, uint16_t clusterid, Z_ShortAddress srcaddr, + void publishMQTTReceived(uint16_t groupid, uint16_t clusterid, uint16_t srcaddr, uint8_t srcendpoint, uint8_t dstendpoint, uint8_t wasbroadcast, uint8_t linkquality, uint8_t securityuse, uint8_t seqnumber, uint32_t timestamp) { #ifdef ZIGBEE_VERBOSE char hex_char[_payload.len()*2+2]; ToHex_P((unsigned char*)_payload.getBuffer(), _payload.len(), hex_char, sizeof(hex_char)); - Response_P(PSTR("{\"" D_JSON_ZIGBEEZCL_RECEIVED "\":{" + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("{\"" D_JSON_ZIGBEEZCL_RECEIVED "\":{" "\"groupid\":%d," "\"clusterid\":%d," "\"srcaddr\":\"0x%04X\"," "\"srcendpoint\":%d," "\"dstendpoint\":%d," "\"wasbroadcast\":%d," "\"" D_CMND_ZIGBEE_LINKQUALITY "\":%d," "\"securityuse\":%d," "\"seqnumber\":%d," "\"timestamp\":%d," "\"fc\":\"0x%02X\",\"manuf\":\"0x%04X\",\"transact\":%d," - "\"cmdid\":\"0x%02X\",\"payload\":\"%s\""), + "\"cmdid\":\"0x%02X\",\"payload\":\"%s\"}}"), groupid, clusterid, srcaddr, srcendpoint, dstendpoint, wasbroadcast, linkquality, securityuse, seqnumber, timestamp, _frame_control, _manuf_code, _transact_seq, _cmd_id, hex_char); - - ResponseJsonEnd(); // append '}' - ResponseJsonEnd(); // append '}' - MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEEZCL_RECEIVED)); - XdrvRulesProcess(); #endif } @@ -126,6 +121,10 @@ public: return _payload; } + uint16_t getManufCode(void) const { + return _manuf_code; + } + private: ZCLHeaderFrameControl_t _frame_control = { .d8 = 0 }; uint16_t _manuf_code = 0; // optional @@ -456,7 +455,7 @@ void ZCLFrame::parseClusterSpecificCommand(JsonObject& json, uint8_t offset) { // return value: // 0 = keep initial value // 1 = remove initial value -typedef int32_t (*Z_AttrConverter)(uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const __FlashStringHelper* new_name); +typedef int32_t (*Z_AttrConverter)(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const __FlashStringHelper* new_name, uint16_t cluster, uint16_t attr); typedef struct Z_AttributeConverter { uint16_t cluster; uint16_t attribute; @@ -625,6 +624,11 @@ const Z_AttributeConverter Z_PostProcess[] PROGMEM = { { 0x0101, 0x0004, "DoorOpenEvents", &Z_Copy }, { 0x0101, 0x0005, "DoorClosedEvents", &Z_Copy }, { 0x0101, 0x0006, "OpenPeriod", &Z_Copy }, + // Aqara Lumi Vibration Sensor + { 0x0101, 0x0055, "AqaraVibrationMode", &Z_AqaraVibration }, + { 0x0101, 0x0503, "AqaraVibrationsOrAngle", &Z_Copy }, + { 0x0101, 0x0505, "AqaraVibration505", &Z_Copy }, + { 0x0101, 0x0508, "AqaraAccelerometer", &Z_AqaraVibration }, // Window Covering cluster { 0x0102, 0x0000, "WindowCoveringType", &Z_Copy }, { 0x0102, 0x0001, "PhysicalClosedLimitLift",&Z_Copy }, @@ -753,13 +757,13 @@ const Z_AttributeConverter Z_PostProcess[] PROGMEM = { // ====================================================================== // Record Manuf -int32_t Z_ManufKeep(uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const __FlashStringHelper *new_name) { +int32_t Z_ManufKeep(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const __FlashStringHelper *new_name, uint16_t cluster, uint16_t attr) { json[new_name] = value; zigbee_devices.setManufId(shortaddr, value.as()); return 1; } // -int32_t Z_ModelKeep(uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const __FlashStringHelper *new_name) { +int32_t Z_ModelKeep(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const __FlashStringHelper *new_name, uint16_t cluster, uint16_t attr) { json[new_name] = value; zigbee_devices.setModelId(shortaddr, value.as()); return 1; @@ -767,34 +771,89 @@ int32_t Z_ModelKeep(uint16_t shortaddr, JsonObject& json, const char *name, Json // ====================================================================== // Remove attribute -int32_t Z_Remove(uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const __FlashStringHelper *new_name) { +int32_t Z_Remove(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const __FlashStringHelper *new_name, uint16_t cluster, uint16_t attr) { return 1; // remove original key } // Copy value as-is -int32_t Z_Copy(uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const __FlashStringHelper *new_name) { +int32_t Z_Copy(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const __FlashStringHelper *new_name, uint16_t cluster, uint16_t attr) { json[new_name] = value; return 1; // remove original key } // Add pressure unit -int32_t Z_AddPressureUnit(uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const __FlashStringHelper *new_name) { +int32_t Z_AddPressureUnit(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const __FlashStringHelper *new_name, uint16_t cluster, uint16_t attr) { json[new_name] = F(D_UNIT_PRESSURE); return 0; // keep original key } // Convert int to float and divide by 100 -int32_t Z_FloatDiv100(uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const __FlashStringHelper *new_name) { +int32_t Z_FloatDiv100(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const __FlashStringHelper *new_name, uint16_t cluster, uint16_t attr) { json[new_name] = ((float)value) / 100.0f; return 1; // remove original key } // Convert int to float and divide by 10 -int32_t Z_FloatDiv10(uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const __FlashStringHelper *new_name) { +int32_t Z_FloatDiv10(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const __FlashStringHelper *new_name, uint16_t cluster, uint16_t attr) { json[new_name] = ((float)value) / 10.0f; return 1; // remove original key } -int32_t Z_AqaraSensor(uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const __FlashStringHelper *new_name) { +// Aqara Vibration Sensor - special proprietary attributes +int32_t Z_AqaraVibration(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const __FlashStringHelper *new_name, uint16_t cluster, uint16_t attr) { + //json[new_name] = value; + switch (attr) { + case 0x0055: + { + int32_t ivalue = value; + const __FlashStringHelper * svalue; + switch (ivalue) { + case 1: svalue = F("vibrate"); break; + case 2: svalue = F("tilt"); break; + case 3: svalue = F("drop"); break; + default: svalue = F("unknown"); break; + } + json[new_name] = svalue; + } + break; + // case 0x0503: + // break; + // case 0x0505: + // break; + case 0x0508: + { + // see https://github.com/Koenkk/zigbee2mqtt/issues/295 and http://faire-ca-soi-meme.fr/domotique/2018/09/03/test-xiaomi-aqara-vibration-sensor/ + // report accelerometer measures + String hex = value; + SBuffer buf2 = SBuffer::SBufferFromHex(hex.c_str(), hex.length()); + int16_t x, y, z; + z = buf2.get16(0); + y = buf2.get16(2); + x = buf2.get16(4); + JsonArray& xyz = json.createNestedArray(new_name); + xyz.add(x); + xyz.add(y); + xyz.add(z); + // calculate angles + float X = x; + float Y = y; + float Z = z; + int32_t Angle_X = 0.5f + atanf(X/sqrtf(z*z+y*y)) * f_180pi; + int32_t Angle_Y = 0.5f + atanf(Y/sqrtf(x*x+z*z)) * f_180pi; + int32_t Angle_Z = 0.5f + atanf(Z/sqrtf(x*x+y*y)) * f_180pi; + // int32_t Angle_X = 0.5f + atanf(X/sqrtf(Z*Z+Y*Y)) * f_180pi; + // int32_t Angle_Y = 0.5f + atanf(Y/sqrtf(X*X+Z*Z)) * f_180pi; + // int32_t Angle_Z = 0.5f + atanf(Z/sqrtf(X*X+Y*Y)) * f_180pi; + JsonArray& angles = json.createNestedArray(F("AqaraAngles")); + angles.add(Angle_X); + angles.add(Angle_Y); + angles.add(Angle_Z); + } + break; + } + return 1; // remove original key +} + +int32_t Z_AqaraSensor(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const __FlashStringHelper *new_name, uint16_t cluster, uint16_t attr) { String hex = value; SBuffer buf2 = SBuffer::SBufferFromHex(hex.c_str(), hex.length()); uint32_t i = 0; @@ -809,36 +868,31 @@ int32_t Z_AqaraSensor(uint16_t shortaddr, JsonObject& json, const char *name, Js i += parseSingleAttribute(json, tmp, buf2, i, len); float val = json[tmp]; json.remove(tmp); - if (0x64 == attrid) { - json[F(D_JSON_TEMPERATURE)] = val / 100.0f; - } else if (0x65 == attrid) { - json[F(D_JSON_HUMIDITY)] = val / 100.0f; - } else if (0x66 == attrid) { - json[F(D_JSON_PRESSURE)] = val / 100.0f; - json[F(D_JSON_PRESSURE_UNIT)] = F(D_UNIT_PRESSURE); // hPa - } else if (0x01 == attrid) { + if (0x01 == attrid) { json[F(D_JSON_VOLTAGE)] = val / 1000.0f; json[F("Battery")] = toPercentageCR2032(val); + } else if (0 == zcl->getManufCode()) { + // onla Aqara Temp/Humidity has manuf_code of zero. If non-zero we skip the parameters + if (0x64 == attrid) { + json[F(D_JSON_TEMPERATURE)] = val / 100.0f; + } else if (0x65 == attrid) { + json[F(D_JSON_HUMIDITY)] = val / 100.0f; + } else if (0x66 == attrid) { + json[F(D_JSON_PRESSURE)] = val / 100.0f; + json[F(D_JSON_PRESSURE_UNIT)] = F(D_UNIT_PRESSURE); // hPa + } else if (0x01 == attrid) { + json[F(D_JSON_VOLTAGE)] = val / 1000.0f; + json[F("Battery")] = toPercentageCR2032(val); + } + } else if (0x115F == zcl->getManufCode()) { + // Aqara Motion Sensor, still unknown field + json[F("AqaraUnknown")] = val; } } return 1; // remove original key } // ====================================================================== -// Cluster Specific commands -// #define ZCL_OO_OFF "s_0006_00" // Cluster 0x0006, cmd 0x00 - On/Off - Off -// #define ZCL_OO_ON "s_0006_01" // Cluster 0x0006, cmd 0x01 - On/Off - On -// #define ZCL_COLORTEMP_MOVE "s_0300_0A" // Cluster 0x0300, cmd 0x0A, Move to Color Temp -// #define ZCL_LC_MOVE "s_0008_00" // Cluster 0x0008, cmd 0x00, Level Control Move to Level -// #define ZCL_LC_MOVE_1 "s_0008_01" // Cluster 0x0008, cmd 0x01, Level Control Move -// #define ZCL_LC_STEP "s_0008_02" // Cluster 0x0008, cmd 0x02, Level Control Step -// #define ZCL_LC_STOP "s_0008_03" // Cluster 0x0008, cmd 0x03, Level Control Stop -// #define ZCL_LC_MOVE_WOO "s_0008_04" // Cluster 0x0008, cmd 0x04, Level Control Move to Level, with On/Off -// #define ZCL_LC_MOVE_1_WOO "s_0008_05" // Cluster 0x0008, cmd 0x05, Level Control Move, with On/Off -// #define ZCL_LC_STEP_WOO "s_0008_06" // Cluster 0x0008, cmd 0x05, Level Control Step, with On/Off -// #define ZCL_LC_STOP_WOO "s_0008_07" // Cluster 0x0008, cmd 0x07, Level Control Stop - - void ZCLFrame::postProcessAttributes(uint16_t shortaddr, JsonObject& json) { // iterate on json elements for (auto kv : json) { @@ -859,7 +913,7 @@ void ZCLFrame::postProcessAttributes(uint16_t shortaddr, JsonObject& json) { if ((conv_cluster == cluster) && ((conv_attribute == attribute) || (conv_attribute == 0xFFFF)) ) { - int32_t drop = (*converter->func)(shortaddr, json, key, value, (const __FlashStringHelper*) converter->name); + int32_t drop = (*converter->func)(this, shortaddr, json, key, value, (const __FlashStringHelper*) converter->name, conv_cluster, conv_attribute); if (drop) { json.remove(key); } @@ -870,157 +924,4 @@ void ZCLFrame::postProcessAttributes(uint16_t shortaddr, JsonObject& json) { } } -//void ZCLFrame::postProcessAttributes2(JsonObject& json) { -// void postProcessAttributes2(JsonObject& json) { -// const __FlashStringHelper *key; -// -// // Osram Mini Switch -// key = F(ZCL_OO_OFF); -// if (json.containsKey(key)) { -// json.remove(key); -// json[F(D_CMND_POWER)] = F("Off"); -// } -// key = F(ZCL_OO_ON); -// if (json.containsKey(key)) { -// json.remove(key); -// json[F(D_CMND_POWER)] = F("On"); -// } -// key = F(ZCL_COLORTEMP_MOVE); -// if (json.containsKey(key)) { -// String hex = json[key]; -// SBuffer buf2 = SBuffer::SBufferFromHex(hex.c_str(), hex.length()); -// uint16_t color_temp = buf2.get16(0); -// uint16_t transition_time = buf2.get16(2); -// json.remove(key); -// json[F("ColorTemp")] = color_temp; -// json[F("TransitionTime")] = transition_time / 10.0f; -// } -// key = F(ZCL_LC_MOVE_WOO); -// if (json.containsKey(key)) { -// String hex = json[key]; -// SBuffer buf2 = SBuffer::SBufferFromHex(hex.c_str(), hex.length()); -// uint8_t level = buf2.get8(0); -// uint16_t transition_time = buf2.get16(1); -// json.remove(key); -// json[F("Dimmer")] = changeUIntScale(level, 0, 255, 0, 100); // change to percentage -// json[F("TransitionTime")] = transition_time / 10.0f; -// if (0 == level) { -// json[F(D_CMND_POWER)] = F("Off"); -// } else { -// json[F(D_CMND_POWER)] = F("On"); -// } -// } -// key = F(ZCL_LC_MOVE); -// if (json.containsKey(key)) { -// String hex = json[key]; -// SBuffer buf2 = SBuffer::SBufferFromHex(hex.c_str(), hex.length()); -// uint8_t level = buf2.get8(0); -// uint16_t transition_time = buf2.get16(1); -// json.remove(key); -// json[F("Dimmer")] = changeUIntScale(level, 0, 255, 0, 100); // change to percentage -// json[F("TransitionTime")] = transition_time / 10.0f; -// } -// key = F(ZCL_LC_MOVE_1); -// if (json.containsKey(key)) { -// String hex = json[key]; -// SBuffer buf2 = SBuffer::SBufferFromHex(hex.c_str(), hex.length()); -// uint8_t move_mode = buf2.get8(0); -// uint8_t move_rate = buf2.get8(1); -// json.remove(key); -// json[F("Move")] = move_mode ? F("Down") : F("Up"); -// json[F("Rate")] = move_rate; -// } -// key = F(ZCL_LC_MOVE_1_WOO); -// if (json.containsKey(key)) { -// String hex = json[key]; -// SBuffer buf2 = SBuffer::SBufferFromHex(hex.c_str(), hex.length()); -// uint8_t move_mode = buf2.get8(0); -// uint8_t move_rate = buf2.get8(1); -// json.remove(key); -// json[F("Move")] = move_mode ? F("Down") : F("Up"); -// json[F("Rate")] = move_rate; -// if (0 == move_mode) { -// json[F(D_CMND_POWER)] = F("On"); -// } -// } -// key = F(ZCL_LC_STEP); -// if (json.containsKey(key)) { -// String hex = json[key]; -// SBuffer buf2 = SBuffer::SBufferFromHex(hex.c_str(), hex.length()); -// uint8_t step_mode = buf2.get8(0); -// uint8_t step_size = buf2.get8(1); -// uint16_t transition_time = buf2.get16(2); -// json.remove(key); -// json[F("Step")] = step_mode ? F("Down") : F("Up"); -// json[F("StepSize")] = step_size; -// json[F("TransitionTime")] = transition_time / 10.0f; -// } -// key = F(ZCL_LC_STEP_WOO); -// if (json.containsKey(key)) { -// String hex = json[key]; -// SBuffer buf2 = SBuffer::SBufferFromHex(hex.c_str(), hex.length()); -// uint8_t step_mode = buf2.get8(0); -// uint8_t step_size = buf2.get8(1); -// uint16_t transition_time = buf2.get16(2); -// json.remove(key); -// json[F("Step")] = step_mode ? F("Down") : F("Up"); -// json[F("StepSize")] = step_size; -// json[F("TransitionTime")] = transition_time / 10.0f; -// if (0 == step_mode) { -// json[F(D_CMND_POWER)] = F("On"); -// } -// } -// key = F(ZCL_LC_STOP); -// if (json.containsKey(key)) { -// json.remove(key); -// json[F("Stop")] = 1; -// } -// key = F(ZCL_LC_STOP_WOO); -// if (json.containsKey(key)) { -// json.remove(key); -// json[F("Stop")] = 1; -// } -// -// // Lumi.weather proprietary field -// key = F(ZCL_LUMI_WEATHER); -// if (json.containsKey(key)) { -// String hex = json[key]; -// SBuffer buf2 = SBuffer::SBufferFromHex(hex.c_str(), hex.length()); -// DynamicJsonBuffer jsonBuffer; -// JsonObject& json_lumi = jsonBuffer.createObject(); -// uint32_t i = 0; -// uint32_t len = buf2.len(); -// char shortaddr[8]; -// -// while (len - i >= 2) { -// uint8_t attrid = buf2.get8(i++); -// -// snprintf_P(shortaddr, sizeof(shortaddr), PSTR("0x%02X"), attrid); -// -// //json[shortaddr] = parseSingleAttribute(json_lumi, buf2, i, len, nullptr, 0); -// } -// // parse output -// if (json_lumi.containsKey("0x64")) { // Temperature -// int32_t temperature = json_lumi["0x64"]; -// json[F(D_JSON_TEMPERATURE)] = temperature / 100.0f; -// } -// if (json_lumi.containsKey("0x65")) { // Humidity -// uint32_t humidity = json_lumi["0x65"]; -// json[F(D_JSON_HUMIDITY)] = humidity / 100.0f; -// } -// if (json_lumi.containsKey("0x66")) { // Pressure -// int32_t pressure = json_lumi["0x66"]; -// json[F(D_JSON_PRESSURE)] = pressure / 100.0f; -// json[F(D_JSON_PRESSURE_UNIT)] = F(D_UNIT_PRESSURE); // hPa -// } -// if (json_lumi.containsKey("0x01")) { // Battery Voltage -// uint32_t voltage = json_lumi["0x01"]; -// json[F(D_JSON_VOLTAGE)] = voltage / 1000.0f; -// json[F("Battery")] = toPercentageCR2032(voltage); -// } -// json.remove(key); -// } -// -// } - #endif // USE_ZIGBEE diff --git a/tasmota/xdrv_23_zigbee_8_parsers.ino b/tasmota/xdrv_23_zigbee_8_parsers.ino index a610bb882..acd7e5d26 100644 --- a/tasmota/xdrv_23_zigbee_8_parsers.ino +++ b/tasmota/xdrv_23_zigbee_8_parsers.ino @@ -398,7 +398,7 @@ int32_t Z_ReceiveAfIncomingMessage(int32_t res, const class SBuffer &buf) { String msg(""); msg.reserve(100); json_root.printTo(msg); - AddLog_P2(LOG_LEVEL_INFO, PSTR("ZigbeeZCLRawReceived: %s"), msg.c_str()); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ZigbeeZCLRawReceived: %s"), msg.c_str()); zcl_received.postProcessAttributes(srcaddr, json); // Add linkquality diff --git a/tasmota/xdrv_23_zigbee_9_impl.ino b/tasmota/xdrv_23_zigbee_9_impl.ino index 4e2ba5499..3bfd6c4e3 100644 --- a/tasmota/xdrv_23_zigbee_9_impl.ino +++ b/tasmota/xdrv_23_zigbee_9_impl.ino @@ -269,7 +269,7 @@ void CmndZigbeeReset(void) { void CmndZigbeeStatus(void) { if (ZigbeeSerial) { String dump = zigbee_devices.dump(XdrvMailbox.index, XdrvMailbox.payload); - Response_P(PSTR("{\"%s%d\":%s}"), XdrvMailbox.command, XdrvMailbox.payload, dump.c_str()); + Response_P(PSTR("{\"%s%d\":%s}"), XdrvMailbox.command, XdrvMailbox.index, dump.c_str()); } } @@ -540,7 +540,7 @@ void CmndZigbeeSend(void) { // we have an unsupported command type, just ignore it and fallback to missing command } - AddLog_P2(LOG_LEVEL_INFO, PSTR("ZigbeeCmd_actual: ZigbeeZCLSend {\"device\":\"0x%04X\",\"endpoint\":%d,\"send\":\"%s\"}"), + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ZigbeeCmd_actual: ZigbeeZCLSend {\"device\":\"0x%04X\",\"endpoint\":%d,\"send\":\"%s\"}"), device, endpoint, cmd_str.c_str()); zigbeeZCLSendStr(device, endpoint, cmd_str.c_str()); } else { From 40da54a0ecaf28fee697830d7d6732b3cc9d6ccb Mon Sep 17 00:00:00 2001 From: stefanbode Date: Sat, 14 Dec 2019 12:09:35 +0100 Subject: [PATCH 71/84] New functions shutter: ramp up/down, MQTT reporting - code optimization - shuttermotordelay now defines ramp up and ramp down duration - shutterposition reported through MQTT during movement (HASS support) - improved stop (no underrun anymore) --- tasmota/xdrv_27_shutter.ino | 107 ++++++++++++++++++------------------ 1 file changed, 54 insertions(+), 53 deletions(-) diff --git a/tasmota/xdrv_27_shutter.ino b/tasmota/xdrv_27_shutter.ino index 2338639a1..7194788b7 100644 --- a/tasmota/xdrv_27_shutter.ino +++ b/tasmota/xdrv_27_shutter.ino @@ -46,6 +46,7 @@ void (* const ShutterCommand[])(void) PROGMEM = { &CmndShutterFrequency}; const char JSON_SHUTTER_POS[] PROGMEM = "\"" D_PRFX_SHUTTER "%d\":{\"Position\":%d,\"direction\":%d}"; +const char MSG_SHUTTER_POS[] PROGMEM = "SHT: " D_PRFX_SHUTTER " %d: Real. %d, Stop: %d, dir %d, pulsetimer: %d, motordelay %d, rtc: %s [s], freq %d"; #include @@ -147,8 +148,6 @@ void ShutterInit(void) Shutter.old_power = power; bool relay_in_interlock = false; - AddLog_P2(LOG_LEVEL_INFO, PSTR("SHT: Accuracy digits: %d"), Settings.shutter_accuracy); - for (uint32_t i = 0; i < MAX_SHUTTERS; i++) { // upgrade to 0.1sec calculation base. if ( Settings.shutter_accuracy == 0) { @@ -223,6 +222,9 @@ void ShutterInit(void) // terminate loop at first INVALID shutter. break; } + if (shutters_present < 4) { + Shutter.max_pwm_frequency = Settings.shuttercoeff[4][4]; + } Settings.shutter_accuracy = 1; } } @@ -237,22 +239,25 @@ void ShutterUpdatePosition(void) if (Shutter.direction[i] != 0) { //char stemp1[20]; - // frequency start at 0. Stepper will start moving with first change of the Speed - // Counter should be initiated to 0 to count movement. - // 0..1000 in step 100 = 10 steps with 0.05 sec = 0.5sec total ramp time from start to - // full speed. - if (Shutter.mode == SHT_OFF_ON__OPEN_CLOSE && pin[GPIO_PWM1+i] < 99 && Shutter.pwm_frequency != Shutter.max_pwm_frequency) { - Shutter.pwm_frequency += Shutter.max_pwm_frequency/20; - Shutter.pwm_frequency = (Shutter.pwm_frequency > Shutter.max_pwm_frequency ? Shutter.max_pwm_frequency : Shutter.pwm_frequency); + + if (pin[GPIO_PWM1+i] < 99 && pin[GPIO_CNTR1+i] < 99 ) { + // Calculate position with counter. Much more accurate and no need for motordelay workaround + // aading some counters to stop early + Shutter.real_position[i] = Shutter.start_position[i] + ( ((int32_t)RtcSettings.pulse_counter[i]+Shutter.max_pwm_frequency/100) * Shutter.direction[i] * 2000 / Shutter.max_pwm_frequency ); + //AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: real %d, start %d, counter %d, max_freq %d, dir %d, freq %d"),Shutter.real_position[i], Shutter.start_position[i] ,RtcSettings.pulse_counter[i],Shutter.max_pwm_frequency , Shutter.direction[i] ,Shutter.max_pwm_frequency ); + } else { + Shutter.real_position[i] = Shutter.start_position[i] + ( (Shutter.time[i] - Shutter.motordelay[i]) * (Shutter.direction[i] > 0 ? 100 : -Shutter.close_velocity[i])); + } + + if (Shutter.mode == SHT_OFF_ON__OPEN_CLOSE && pin[GPIO_PWM1+i] < 99) { + uint16_t freq_change = Shutter.max_pwm_frequency/(Shutter.motordelay[i]+1); + // ramp up phase. calculate frequency + Shutter.pwm_frequency = tmin(freq_change * Shutter.time[i],Shutter.max_pwm_frequency); + // ramp down at the end of the movement time will not be exactly motordelay + Shutter.pwm_frequency = tmax(tmin(freq_change * (Shutter.target_position[i]-Shutter.real_position[i])*Shutter.direction[i]/30, Shutter.pwm_frequency),10); analogWriteFreq(Shutter.pwm_frequency); analogWrite(pin[GPIO_PWM1+i], 50); } - - Shutter.real_position[i] = Shutter.start_position[i] + ( (Shutter.time[i] - Shutter.motordelay[i]) * (Shutter.direction[i] > 0 ? 100 : -Shutter.close_velocity[i])); - // avoid real position leaving the boundaries. - // Shutter.real_position[i] = Shutter.real_position[i] < 0 ? 0 : (Shutter.real_position[i] > Shutter.open_max[i] ? Shutter.open_max[i] : Shutter.real_position[i]) ; - - if (Shutter.real_position[i] * Shutter.direction[i] >= Shutter.target_position[i] * Shutter.direction[i] ) { // calculate relay number responsible for current movement. //AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Stop Condition detected: real: %d, Target: %d, direction: %d"),Shutter.real_position[i], Shutter.target_position[i],Shutter.direction[i]); @@ -273,22 +278,16 @@ void ShutterUpdatePosition(void) // see wiki to connect PWM and COUNTER if (pin[GPIO_PWM1+i] < 99 && pin[GPIO_CNTR1+i] < 99 ) { int16_t missing_steps = ((Shutter.target_position[i]-Shutter.start_position[i])*Shutter.direction[i]*Shutter.max_pwm_frequency/2000) - RtcSettings.pulse_counter[i]; - Shutter.pwm_frequency = 0; - //slow down for acurate position - analogWriteFreq(500); - analogWrite(pin[GPIO_PWM1+i], 50); //prepare for stop PWM - Shutter.motordelay[i] = -2 + Shutter.motordelay[i] + missing_steps/(Shutter.max_pwm_frequency/20); - Shutter.motordelay[i] = Shutter.motordelay[i] > 0 ? Shutter.motordelay[i] : 0; - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Missing steps %d, adjust motordelay %d, counter %d, temp realpos %d"), missing_steps, Shutter.motordelay[i],RtcSettings.pulse_counter[i] ,Shutter.real_position[i]); - Settings.shutter_motordelay[i]=Shutter.motordelay[i]; - analogWriteFreq(0); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Remain steps %d, counter %d, freq %d"), missing_steps, RtcSettings.pulse_counter[i] ,Shutter.pwm_frequency); + Shutter.pwm_frequency = 0; + analogWriteFreq(Shutter.pwm_frequency); while (RtcSettings.pulse_counter[i] < (uint32_t)(Shutter.target_position[i]-Shutter.start_position[i])*Shutter.direction[i]*Shutter.max_pwm_frequency/2000) { delay(1); } analogWrite(pin[GPIO_PWM1+i], 0); Shutter.real_position[i] = ((int32_t)RtcSettings.pulse_counter[i]*Shutter.direction[i]*2000 / Shutter.max_pwm_frequency)+Shutter.start_position[i]; - //AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT:Realpos %d, pulsecount %d, startpos %d, int32 %d"), Shutter.real_position[i],RtcSettings.pulse_counter[i], Shutter.start_position[i], ((int32_t)RtcSettings.pulse_counter[i]*Shutter.direction[i]*2000 / Shutter.max_pwm_frequency)); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT:Real %d, pulsecount %d, start %d"), Shutter.real_position[i],RtcSettings.pulse_counter[i], Shutter.start_position[i]); } if ((1 << (Settings.shutter_startrelay[i]-1)) & power) { @@ -307,7 +306,7 @@ void ShutterUpdatePosition(void) Settings.shutter_position[i] = ShutterRealToPercentPosition(Shutter.real_position[i], i); dtostrfd((float)Shutter.time[i] / 20, 1, stemp2); - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Shutter %d: Real Pos. %d, Stoppos: %ld, relay: %d, direction %d, pulsetimer: %d, motordelay %d, rtcshutter: %s [s]"), i+1, Shutter.real_position[i], Settings.shutter_position[i], cur_relay -1, Shutter.direction[i], Settings.pulse_timer[cur_relay -1], Shutter.motordelay[i],stemp2); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(MSG_SHUTTER_POS), i+1, Shutter.real_position[i], Settings.shutter_position[i], Shutter.direction[i], Settings.pulse_timer[cur_relay -1], Shutter.motordelay[i],stemp2,Shutter.pwm_frequency); Shutter.start_position[i] = Shutter.real_position[i]; // sending MQTT result to broker @@ -339,24 +338,10 @@ bool ShutterState(uint8_t device) void ShutterStartInit(uint8_t index, int8_t direction, int32_t target_pos) { //AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: dir %d, delta1 %d, delta2 %d, grant %d"),direction, (Shutter.open_max[index] - Shutter.real_position[index]) / Shutter.close_velocity[index], Shutter.real_position[index] / Shutter.close_velocity[index], 2+Shutter.motordelay[index]); - if (direction == 1 && (Shutter.open_max[index] - Shutter.real_position[index]) / Shutter.close_velocity[index] <= 2+Shutter.motordelay[index]) { - // shutter alread 100% cannot move further - // store relay number variable + if ( ( direction == 1 && (Shutter.open_max[index] - Shutter.real_position[index]) / 100 <= 2 ) + || ( direction == -1 && Shutter.real_position[index] / Shutter.close_velocity[index] <= 2)) { Shutter.skip_relay_change = 1 ; - //AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: 100%% endstop already reached: Relay %d"),Shutter.skip_relay_change); - - } else if (direction == -1 && Shutter.real_position[index] / Shutter.close_velocity[index] <= 2+Shutter.motordelay[index]) { - // shutter already at 0% connot move into minus - // store relay number variable - Shutter.skip_relay_change = 1; - //AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: 0%% endstop already reached: Relay %d"),Shutter.skip_relay_change); - } else { - Shutter.direction[index] = direction; - Shutter.target_position[index] = target_pos; - Shutter.start_position[index] = Shutter.real_position[index]; - Shutter.time[index] = 0; - Shutter.skip_relay_change = 0; if (pin[GPIO_PWM1+index] < 99) { Shutter.pwm_frequency = 0; analogWriteFreq(Shutter.pwm_frequency); @@ -366,6 +351,12 @@ void ShutterStartInit(uint8_t index, int8_t direction, int32_t target_pos) RtcSettings.pulse_counter[index] = 0; } } + Shutter.target_position[index] = target_pos; + Shutter.start_position[index] = Shutter.real_position[index]; + Shutter.time[index] = 0; + Shutter.skip_relay_change = 0; + Shutter.direction[index] = direction; + //AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: real %d, start %d, counter %d, max_freq %d, dir %d, freq %d"),Shutter.real_position[index], Shutter.start_position[index] ,RtcSettings.pulse_counter[index],Shutter.max_pwm_frequency , Shutter.direction[index] ,Shutter.max_pwm_frequency ); } //AddLog_P2(LOG_LEVEL_INFO, PSTR("SHT: Start shutter: %d from %d to %d in directin %d"), index, Shutter.start_position[index], Shutter.target_position[index], Shutter.direction[index]); } @@ -380,13 +371,19 @@ void ShutterReportPosition(void) { uint16_t shutter_moving = 0; for (uint8_t i = 0; i < shutters_present; i++) { + //AddLog_P2(LOG_LEVEL_INFO, PSTR("SHT: Shutter %d: Real Pos: %d"), i+1,Shutter.real_position[i]); if (Shutter.direction[i] != 0) { char stemp1[20]; char stemp2[10]; + uint8_t position = ShutterRealToPercentPosition(Shutter.real_position[i], i); dtostrfd((float)Shutter.time[i] / 20, 2, stemp2); shutter_moving = 1; //Settings.shutter_position[i] = Settings.shuttercoeff[2][i] * 5 > Shutter.real_position[i] ? Shutter.real_position[i] / Settings.shuttercoeff[2][i] : (Shutter.real_position[i]-Settings.shuttercoeff[0,i]) / Settings.shuttercoeff[1][i]; - AddLog_P2(LOG_LEVEL_INFO, PSTR("SHT: Shutter %d: Real Pos: %d, Target %d, source: %s, start-pos: %d %%, direction: %d, motordelay %d, rtcshutter: %s [s]"), i+1,Shutter.real_position[i], Shutter.target_position[i], GetTextIndexed(stemp1, sizeof(stemp1), last_source, kCommandSource), Settings.shutter_position[i], Shutter.direction[i], Shutter.motordelay[i], stemp2 ); + AddLog_P2(LOG_LEVEL_INFO, PSTR(MSG_SHUTTER_POS), i+1, Shutter.real_position[i], Settings.shutter_position[i], Shutter.direction[i], Settings.pulse_timer[cur_relay -1], Shutter.motordelay[i],stemp2,Shutter.pwm_frequency); + Response_P(PSTR("{")); + ResponseAppend_P(JSON_SHUTTER_POS, i+1, position, Shutter.direction[i]); + ResponseJsonEnd(); + MqttPublishPrefixTopic_P(RESULT_OR_TELE, mqtt_data); } } if (rules_flag.shutter_moving > shutter_moving) { @@ -490,7 +487,7 @@ void CmndShutterStop(void) uint32_t index = XdrvMailbox.index -1; if (Shutter.direction[index] != 0) { - AddLog_P2(LOG_LEVEL_INFO, PSTR("SHT: Stop moving shutter %d: direction: %d"), XdrvMailbox.index, Shutter.direction[index]); + AddLog_P2(LOG_LEVEL_INFO, PSTR("SHT: Stop moving %d: dir: %d"), XdrvMailbox.index, Shutter.direction[index]); // set stop position 10 steps ahead (0.5sec to allow normal stop) int32_t temp_realpos = Shutter.start_position[index] + ( (Shutter.time[index]+10) * (Shutter.direction[index] > 0 ? 100 : -Shutter.close_velocity[index])); XdrvMailbox.payload = ShutterRealToPercentPosition(temp_realpos, index); @@ -508,7 +505,7 @@ void CmndShutterPosition(void) if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= shutters_present)) { uint32_t index = XdrvMailbox.index -1; //limit the payload - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Position in: payload %s (%d), payload %d, index %d, source %d"), XdrvMailbox.data , XdrvMailbox.data_len, XdrvMailbox.payload , XdrvMailbox.index, last_source ); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Pos. in: payload %s (%d), payload %d, idx %d, src %d"), XdrvMailbox.data , XdrvMailbox.data_len, XdrvMailbox.payload , XdrvMailbox.index, last_source ); if (XdrvMailbox.data_len > 1 && XdrvMailbox.payload <=0) { UpperCase(XdrvMailbox.data, XdrvMailbox.data); @@ -525,7 +522,7 @@ void CmndShutterPosition(void) //target_pos_percent = Settings.shutter_invert[index] ? 100 - target_pos_percent : target_pos_percent; Shutter.target_position[index] = ShutterPercentToRealPosition(target_pos_percent, index); //Shutter.target_position[index] = XdrvMailbox.payload < 5 ? Settings.shuttercoeff[2][index] * XdrvMailbox.payload : Settings.shuttercoeff[1][index] * XdrvMailbox.payload + Settings.shuttercoeff[0,index]; - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: lastsource %d:, realpos %d, target %d, payload %d"), last_source, Shutter.real_position[index] ,Shutter.target_position[index],target_pos_percent); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: lastsource %d:, real %d, target %d, payload %d"), last_source, Shutter.real_position[index] ,Shutter.target_position[index],target_pos_percent); } if ( (target_pos_percent >= 0) && (target_pos_percent <= 100) && abs(Shutter.target_position[index] - Shutter.real_position[index] ) / Shutter.close_velocity[index] > 2) { int8_t new_shutterdirection = Shutter.real_position[index] < Shutter.target_position[index] ? 1 : -1; @@ -541,18 +538,20 @@ void CmndShutterPosition(void) } } if (Shutter.direction[index] != new_shutterdirection ) { - ShutterStartInit(index, new_shutterdirection, Shutter.target_position[index]); + if (Shutter.mode == SHT_OFF_ON__OPEN_CLOSE) { ExecuteCommandPower(Settings.shutter_startrelay[index], 0, SRC_SHUTTER); //AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Delay5 5s, xdrv %d"), XdrvMailbox.payload); ShutterDelayForMotorStop(); + ShutterStartInit(index, new_shutterdirection, Shutter.target_position[index]); // Code for shutters with circuit safe configuration, switch the direction Relay ExecuteCommandPower(Settings.shutter_startrelay[index] +1, new_shutterdirection == 1 ? 0 : 1, SRC_SHUTTER); // power on ExecuteCommandPower(Settings.shutter_startrelay[index], 1, SRC_SHUTTER); } else { // now start the motor for the right direction, work for momentary and normal shutters. - AddLog_P2(LOG_LEVEL_INFO, PSTR("SHT: Start shutter in direction %d"), Shutter.direction[index]); + AddLog_P2(LOG_LEVEL_INFO, PSTR("SHT: Start in dir %d"), Shutter.direction[index]); + ShutterStartInit(index, new_shutterdirection, Shutter.target_position[index]); ExecuteCommandPower(Settings.shutter_startrelay[index] + (new_shutterdirection == 1 ? 0 : 1), 1, SRC_SHUTTER); //AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Delay6 5s, xdrv %d"), XdrvMailbox.payload); } @@ -615,7 +614,6 @@ void CmndShutterRelay(void) } else { Shutter.mask ^= 3 << (Settings.shutter_startrelay[XdrvMailbox.index -1] - 1); } - AddLog_P2(LOG_LEVEL_INFO, PSTR("SHT: Relay %d is %d"), XdrvMailbox.index, XdrvMailbox.payload); Settings.shutter_startrelay[XdrvMailbox.index -1] = XdrvMailbox.payload; ShutterInit(); // if payload is 0 to disable the relay there must be a reboot. Otherwhise does not work @@ -630,10 +628,8 @@ void CmndShutterSetHalfway(void) if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 100)) { Settings.shutter_set50percent[XdrvMailbox.index -1] = Settings.shutter_invert[XdrvMailbox.index -1] ? 100 - XdrvMailbox.payload : XdrvMailbox.payload; ShutterInit(); - ResponseCmndIdxNumber(XdrvMailbox.payload); // ???? - } else { - ResponseCmndIdxNumber(Settings.shutter_set50percent[XdrvMailbox.index -1]); } + ResponseCmndIdxNumber(Settings.shutter_invert[XdrvMailbox.index -1] ? 100 - Settings.shutter_set50percent[XdrvMailbox.index -1] : Settings.shutter_set50percent[XdrvMailbox.index -1]); } } @@ -641,6 +637,9 @@ void CmndShutterFrequency(void) { if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 10000)) { Shutter.max_pwm_frequency = XdrvMailbox.payload; + if (shutters_present < 4) { + Settings.shuttercoeff[4][4] = Shutter.max_pwm_frequency; + } ResponseCmndNumber(XdrvMailbox.payload); // ???? } else { ResponseCmndNumber(Shutter.max_pwm_frequency); @@ -669,7 +668,7 @@ void CmndShutterInvert(void) void CmndShutterCalibration(void) // ???? { - if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_SHUTTERS)) { + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= shutters_present)) { if (XdrvMailbox.data_len > 0) { uint32_t i = 0; char *str_ptr; @@ -712,8 +711,10 @@ bool Xdrv27(uint8_t function) ShutterUpdatePosition(); break; case FUNC_EVERY_SECOND: + //case FUNC_EVERY_250_MSECOND: ShutterReportPosition(); break; + case FUNC_COMMAND: result = DecodeCommand(kShutterCommands, ShutterCommand); break; From de749b5b63a5144116f6eecb92e8bce227d5cb70 Mon Sep 17 00:00:00 2001 From: stefanbode Date: Sat, 14 Dec 2019 12:44:05 +0100 Subject: [PATCH 72/84] Update xdrv_27_shutter.ino Sorry you were to fast for me. I uploaded a buggy version. Now fixed --- tasmota/xdrv_27_shutter.ino | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tasmota/xdrv_27_shutter.ino b/tasmota/xdrv_27_shutter.ino index 7194788b7..2d143f415 100644 --- a/tasmota/xdrv_27_shutter.ino +++ b/tasmota/xdrv_27_shutter.ino @@ -46,7 +46,7 @@ void (* const ShutterCommand[])(void) PROGMEM = { &CmndShutterFrequency}; const char JSON_SHUTTER_POS[] PROGMEM = "\"" D_PRFX_SHUTTER "%d\":{\"Position\":%d,\"direction\":%d}"; -const char MSG_SHUTTER_POS[] PROGMEM = "SHT: " D_PRFX_SHUTTER " %d: Real. %d, Stop: %d, dir %d, pulsetimer: %d, motordelay %d, rtc: %s [s], freq %d"; +const char MSG_SHUTTER_POS[] PROGMEM = "SHT: " D_PRFX_SHUTTER " %d: Real. %d, Start: %d, Stop: %d, dir %d, motordelay %d, rtc: %s [s], freq %d"; #include @@ -306,7 +306,7 @@ void ShutterUpdatePosition(void) Settings.shutter_position[i] = ShutterRealToPercentPosition(Shutter.real_position[i], i); dtostrfd((float)Shutter.time[i] / 20, 1, stemp2); - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(MSG_SHUTTER_POS), i+1, Shutter.real_position[i], Settings.shutter_position[i], Shutter.direction[i], Settings.pulse_timer[cur_relay -1], Shutter.motordelay[i],stemp2,Shutter.pwm_frequency); + AddLog_P2(LOG_LEVEL_INFO, MSG_SHUTTER_POS, i+1, Shutter.real_position[i], Shutter.start_position[i], Shutter.target_position[i], Shutter.direction[i], Shutter.motordelay[i],stemp2,Shutter.pwm_frequency); Shutter.start_position[i] = Shutter.real_position[i]; // sending MQTT result to broker @@ -379,7 +379,7 @@ void ShutterReportPosition(void) dtostrfd((float)Shutter.time[i] / 20, 2, stemp2); shutter_moving = 1; //Settings.shutter_position[i] = Settings.shuttercoeff[2][i] * 5 > Shutter.real_position[i] ? Shutter.real_position[i] / Settings.shuttercoeff[2][i] : (Shutter.real_position[i]-Settings.shuttercoeff[0,i]) / Settings.shuttercoeff[1][i]; - AddLog_P2(LOG_LEVEL_INFO, PSTR(MSG_SHUTTER_POS), i+1, Shutter.real_position[i], Settings.shutter_position[i], Shutter.direction[i], Settings.pulse_timer[cur_relay -1], Shutter.motordelay[i],stemp2,Shutter.pwm_frequency); + AddLog_P2(LOG_LEVEL_INFO, MSG_SHUTTER_POS, i+1, Shutter.real_position[i], Shutter.start_position[i], Shutter.target_position[i], Shutter.direction[i], Shutter.motordelay[i],stemp2,Shutter.pwm_frequency); Response_P(PSTR("{")); ResponseAppend_P(JSON_SHUTTER_POS, i+1, position, Shutter.direction[i]); ResponseJsonEnd(); From 3d59de5d1d8345cc63c9776791f20e7fe8accf86 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Sat, 14 Dec 2019 13:16:55 +0100 Subject: [PATCH 73/84] Bump version to 7.1.2.6 - Change some more Settings locations freeing up space for future single char allowing variable length text - Add Zigbee support for Xiaomi Aqara Vibration Sensor and Presence Sensor by Stefan Hadinger - Add Shutter functions ramp up/down and MQTT reporting by Stefan Bode --- RELEASENOTES.md | 4 +- tasmota/CHANGELOG.md | 7 ++- tasmota/settings.h | 32 ++++++---- tasmota/settings.ino | 121 +++++++++--------------------------- tasmota/support_tasmota.ino | 4 ++ tasmota/tasmota.h | 24 +++++++ tasmota/tasmota_version.h | 2 +- 7 files changed, 87 insertions(+), 107 deletions(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 0f5e1e33b..4341ec587 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -47,7 +47,7 @@ The following binary downloads have been compiled with ESP8266/Arduino library c ## Changelog -### Version 7.1.2.5 +### Version 7.1.2.6 - Change Exception reporting removing exception details from ``Status 1`` and consolidated in ``Status 12`` if available - Change HTTP CORS from command ``SetOption73 0/1`` to ``Cors `` allowing user control of specific CORS domain by Shantur Rathore (#7066) @@ -69,3 +69,5 @@ The following binary downloads have been compiled with ESP8266/Arduino library c - Add Wifi Signal Strength in dBm in addition to RSSI Wifi Experience by Andreas Schultz (#7145) - Add Yaw, Pitch and Roll support for MPU6050 by Philip Barclay (#7058) - Add reporting of raw weight to JSON from HX711 to overcome auto-tare functionality by @tobox (#7171) +- Add Zigbee support for Xiaomi Aqara Vibration Sensor and Presence Sensor by Stefan Hadinger +- Add Shutter functions ramp up/down and MQTT reporting by Stefan Bode diff --git a/tasmota/CHANGELOG.md b/tasmota/CHANGELOG.md index 8ffde8562..e45d2efce 100644 --- a/tasmota/CHANGELOG.md +++ b/tasmota/CHANGELOG.md @@ -1,9 +1,14 @@ ## Unreleased (development) +### 7.1.2.6 20191214 + +- Change some more Settings locations freeing up space for future single char allowing variable length text + ### 7.1.2.5 20191213 - Change some Settings locations freeing up space for future single char allowing variable length text -- Add Zigbee support for Xiaomi Aqara Vibration Sensor and Presence Sensor +- Add Zigbee support for Xiaomi Aqara Vibration Sensor and Presence Sensor by Stefan Hadinger +- Add Shutter functions ramp up/down and MQTT reporting by Stefan Bode ### 7.1.2.4 20191209 diff --git a/tasmota/settings.h b/tasmota/settings.h index d05777d17..af74422df 100644 --- a/tasmota/settings.h +++ b/tasmota/settings.h @@ -249,7 +249,7 @@ struct SYSCFG { int16_t save_data; // 014 int8_t timezone; // 016 - // Start of single char array + // Start of single char array Settings.text char ota_url[101]; // 017 char mqtt_prefix[3][11]; // 07C @@ -272,18 +272,21 @@ struct SYSCFG { uint8_t ex_mqtt_fingerprint[2][20]; // 1AD uint8_t ex_adc_param_type; // 1D5 - uint8_t free_1d6[10]; // 1D6 + uint8_t ex_free_1d6[10]; // 1D6 - // End of single char array of 446 chars max (phase 3) + // End of single char array of 456 chars max (phase 3) + + SysBitfield4 ex_flag4; // 1E0 + uint8_t ex_serial_config; // 1E4 + uint8_t ex_wifi_output_power; // 1E5 + uint8_t ex_shutter_accuracy; // 1E6 + uint8_t ex_mqttlog_level; // 1E7 + uint8_t ex_sps30_inuse_hours; // 1E8 - SysBitfield4 flag4; // 1E0 - uint8_t serial_config; // 1E4 - uint8_t wifi_output_power; // 1E5 - uint8_t shutter_accuracy; // 1E6 - uint8_t mqttlog_level; // 1E7 - uint8_t sps30_inuse_hours; // 1E8 char mqtt_host[33]; // 1E9 - Keep together with below as being copied as one chunck with reset 6 - uint16_t mqtt_port; // 20A - Keep together + + uint16_t ex_mqtt_port; // 20A - Keep together + char mqtt_client[33]; // 20C - Keep together char mqtt_user[33]; // 22D - Keep together char mqtt_pwd[33]; // 24E - Keep together @@ -459,8 +462,15 @@ struct SYSCFG { uint8_t weblog_level; // ECE uint8_t mqtt_fingerprint[2][20]; // ECF uint8_t adc_param_type; // EF7 + SysBitfield4 flag4; // EF8 + uint16_t mqtt_port; // EFC + uint8_t serial_config; // EFE + uint8_t wifi_output_power; // EFF + uint8_t shutter_accuracy; // F00 + uint8_t mqttlog_level; // F01 + uint8_t sps30_inuse_hours; // F02 - uint8_t free_ef8[244]; // EF8 + uint8_t free_f03[233]; // F03 uint32_t i2c_drivers[3]; // FEC I2cDriver uint32_t cfg_timestamp; // FF8 diff --git a/tasmota/settings.ino b/tasmota/settings.ino index 866e72f99..c1faa559e 100644 --- a/tasmota/settings.ino +++ b/tasmota/settings.ino @@ -443,132 +443,66 @@ void UpdateQuickPowerCycle(bool update) } /*********************************************************************************************\ - * Config single char array support + * Config Settings.text char array support \*********************************************************************************************/ -enum CharsIndex { SET_OTAURL, - SET_MQTTPREFIX1, SET_MQTTPREFIX2, SET_MQTTPREFIX3, -// SET_STASSID1, SET_STASSID2, -// SET_STAPWD1, SET_STAPWD2, SET_WEBPWD, -// SET_HOSTNAME, SET_SYSLOG_HOST, -// SET_MQTT_HOST, SET_MQTT_CLIENT, -// SET_MQTT_USER, SET_MQTT_PWD, -// SET_MQTT_FULLTOPIC, SET_MQTT_TOPIC, -// SET_MQTT_BUTTON_TOPIC, SET_MQTT_SWITCH_TOPIC, SET_MQTT_GRP_TOPIC, -// SET_STATE_TXT1, SET_STATE_TXT2, SET_STATE_TXT3, SET_STATE_TXT4, -// SET_FRIENDLYNAME1, SET_FRIENDLYNAME2, SET_FRIENDLYNAME3, SET_FRIENDLYNAME4, +const uint32_t settings_text_size = 457; // Settings.flag4 (1E0) - Settings.ota_url (017) -// SET_FRIENDLYNAME5, SET_FRIENDLYNAME6, SET_FRIENDLYNAME7, SET_FRIENDLYNAME8, // Future extension - -// SET_NTPSERVER1, SET_NTPSERVER2, SET_NTPSERVER3, -// SET_MEM1, SET_MEM2, SET_MEM3, SET_MEM4, SET_MEM5, -// SET_CORS, - -// SET_BUTTON1, SET_BUTTON2, SET_BUTTON3, SET_BUTTON4, // Future extension -// SET_BUTTON5, SET_BUTTON6, SET_BUTTON7, SET_BUTTON8, // Future extension -// SET_BUTTON9, SET_BUTTON10, SET_BUTTON11, SET_BUTTON12, // Future extension -// SET_BUTTON13, SET_BUTTON14, SET_BUTTON15, SET_BUTTON16, // Future extension - - SET_MAX }; - -const uint32_t settings_loc_num = 1; // First phase only ota_url and mqtt_prefix -const uint32_t settings_max_size = 134; - -char settings_fullstr[settings_max_size] = { 0 }; - -struct LOCATIONS { - char* address; - uint32_t size = 0; -} Location[settings_loc_num]; - -void SettingsInitText(void) +uint32_t GetSettingsTextLen(void) { - for (uint32_t i = 0; i < settings_loc_num; i++) { - if (0 == i) { - Location[i].address = Settings.ota_url; - Location[i].size = sizeof(Settings.ota_url) + (3 * sizeof(Settings.mqtt_prefix[0])); -// Location[i].address = Settings.char_chunk1; -// Location[i].size = sizeof(Settings.char_chunk1); // 134 - } - else if (1 == i) { - Location[i].address = Settings.sta_ssid[0]; - Location[i].size = (2 * sizeof(Settings.sta_ssid[0])) + (2 * sizeof(Settings.sta_pwd[0])) + sizeof(Settings.hostname) + sizeof(Settings.syslog_host); -// Location[i].address = Settings.char_chunk2; -// Location[i].size = sizeof(Settings.char_chunk2); // 262 - } - else if (2 == i) { - // Need to move Settings.mqtt_port first! - Location[i].address = Settings.mqtt_host; - Location[i].size = sizeof(Settings.mqtt_host) + 2 + sizeof(Settings.mqtt_client) + sizeof(Settings.mqtt_user) + sizeof(Settings.mqtt_pwd) + sizeof(Settings.mqtt_topic) + sizeof(Settings.button_topic) + sizeof(Settings.mqtt_grptopic); -// Location[i].address = Settings.char_chunk3; -// Location[i].size = sizeof(Settings.char_chunk3); // 233 - } + char* position = Settings.ota_url; + for (uint32_t size = 0; size < SET_MAX; size++) { + while (*position++ != '\0') { } } - - SettingsCopyText(0); // Load + return position - Settings.ota_url; } -void SettingsCopyText(uint32_t direction) -{ - char* fullstr = settings_fullstr; - uint32_t size = 0; - for (uint32_t i = 0; i < settings_loc_num; i++) { - size = Location[i].size; - if (1 == direction) { - memcpy(Location[i].address, fullstr, size); // Save to Settings - } else { - memcpy(fullstr, Location[i].address, size); // Load from Settings - } - fullstr += size; - } -} - -bool SettingsUpdateText(uint32_t index, char* replace) +bool SettingsUpdateText(uint32_t index, char* replace_me) { if (index >= SET_MAX) { return false; // Setting not supported - internal error } -// SettingsCopyText(0); // Load + // Make a copy first in case we use source from Settings.text + uint32_t replace_len = strlen(replace_me); + char replace[replace_len +1]; + memcpy(replace, replace_me, sizeof(replace)); uint32_t start_pos = 0; uint32_t end_pos = 0; - char* position = settings_fullstr; + char* position = Settings.ota_url; for (uint32_t size = 0; size < SET_MAX; size++) { while (*position++ != '\0') { } if (1 == index) { - start_pos = position - settings_fullstr; + start_pos = position - Settings.ota_url; } else if (0 == index) { - end_pos = position - settings_fullstr -1; + end_pos = position - Settings.ota_url -1; } index--; } - uint32_t len_pos = position - settings_fullstr; + uint32_t char_len = position - Settings.ota_url; uint32_t current_len = end_pos - start_pos; - uint32_t replace_len = strlen(replace); int diff = replace_len - current_len; // AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TST: start %d, end %d, len %d, current %d, replace %d, diff %d"), -// start_pos, end_pos, len_pos, current_len, replace_len, diff); +// start_pos, end_pos, char_len, current_len, replace_len, diff); - int too_long = (len_pos + diff) - sizeof(settings_fullstr); + int too_long = (char_len + diff) - settings_text_size; if (too_long > 0) { // AddLog_P2(LOG_LEVEL_INFO, PSTR("CFG: Text too long by %d char(s)"), too_long); return false; // Replace text too long } if (diff != 0) { - // Shift full text string up or down - memmove_P(settings_fullstr + start_pos + replace_len, settings_fullstr + end_pos, len_pos - end_pos); + // Shift Settings.text up or down + memmove_P(Settings.ota_url + start_pos + replace_len, Settings.ota_url + end_pos, char_len - end_pos); } // Replace text - memmove_P(settings_fullstr + start_pos, replace, replace_len); + memmove_P(Settings.ota_url + start_pos, replace, replace_len); // Fill for future use - memset(settings_fullstr + len_pos + diff, 0x00, settings_max_size - len_pos - diff); - -// SettingsCopyText(1); // Save - Hold of for now + memset(Settings.ota_url + char_len + diff, 0x00, settings_text_size - char_len - diff); return true; } @@ -579,9 +513,7 @@ char* SettingsGetText(uint32_t index) return nullptr; // Setting not supported - internal error } -// SettingsCopyText(0); // Load - - char* position = settings_fullstr; + char* position = Settings.ota_url; for (;index > 0; index--) { while (*position++ != '\0') { } } @@ -707,8 +639,6 @@ void SettingsLoad(void) settings_crc32 = GetSettingsCrc32(); #endif // FIRMWARE_MINIMAL - SettingsInitText(); - RtcSettingsLoad(); } @@ -1342,6 +1272,11 @@ void SettingsDelta(void) Settings.sta_active = Settings.ex_sta_active; // 0A0 -> EC8 memcpy((char*)&Settings.rule_stop, (char*)&Settings.ex_rule_stop, 47); // 1A7 -> EC9 } + if (Settings.version < 0x07010206) { + Settings.flag4 = Settings.ex_flag4; // 1E0 -> EF8 + Settings.mqtt_port = Settings.ex_mqtt_port; // 20A -> EFC + memcpy((char*)&Settings.serial_config, (char*)&Settings.ex_serial_config, 5); // 1E4 -> EFE + } Settings.version = VERSION; SettingsSave(1); diff --git a/tasmota/support_tasmota.ino b/tasmota/support_tasmota.ino index 34ce9f5dc..0f930e06e 100644 --- a/tasmota/support_tasmota.ino +++ b/tasmota/support_tasmota.ino @@ -905,12 +905,15 @@ void Every250mSeconds(void) if ((214 == restart_flag) || (215 == restart_flag) || (216 == restart_flag)) { char storage_wifi[sizeof(Settings.sta_ssid) + sizeof(Settings.sta_pwd)]; + char storage_mqtt[sizeof(Settings.mqtt_host) + sizeof(Settings.mqtt_port) + sizeof(Settings.mqtt_client) + sizeof(Settings.mqtt_user) + sizeof(Settings.mqtt_pwd) + sizeof(Settings.mqtt_topic)]; + uint16_t mqtt_port = Settings.mqtt_port; // Workaround 7.1.2.6 Settings shuffle + memcpy(storage_wifi, Settings.sta_ssid, sizeof(storage_wifi)); // Backup current SSIDs and Passwords if (216 == restart_flag) { memcpy(storage_mqtt, Settings.mqtt_host, sizeof(storage_mqtt)); // Backup mqtt host, port, client, username and password @@ -923,6 +926,7 @@ void Every250mSeconds(void) if (216 == restart_flag) { memcpy(Settings.mqtt_host, storage_mqtt, sizeof(storage_mqtt)); // Restore the mqtt host, port, client, username and password strlcpy(Settings.mqtt_client, MQTT_CLIENT_ID, sizeof(Settings.mqtt_client)); // Set client to default + Settings.mqtt_port = mqtt_port; // Workaround 7.1.2.6 Settings shuffle } restart_flag = 2; } diff --git a/tasmota/tasmota.h b/tasmota/tasmota.h index c31bfad83..1f676acd4 100644 --- a/tasmota/tasmota.h +++ b/tasmota/tasmota.h @@ -272,6 +272,30 @@ enum XsnsFunctions {FUNC_SETTINGS_OVERRIDE, FUNC_PIN_STATE, FUNC_MODULE_INIT, FU enum AddressConfigSteps { ADDR_IDLE, ADDR_RECEIVE, ADDR_SEND }; +enum SettingsTextIndex { SET_OTAURL, + SET_MQTTPREFIX1, SET_MQTTPREFIX2, SET_MQTTPREFIX3, + SET_STASSID1, SET_STASSID2, + SET_STAPWD1, SET_STAPWD2, + SET_HOSTNAME, SET_SYSLOG_HOST, + SET_WEBPWD, + SET_MQTT_HOST, SET_MQTT_CLIENT, + SET_MQTT_USER, SET_MQTT_PWD, + SET_MQTT_FULLTOPIC, SET_MQTT_TOPIC, + SET_MQTT_BUTTON_TOPIC, SET_MQTT_SWITCH_TOPIC, SET_MQTT_GRP_TOPIC, + SET_STATE_TXT1, SET_STATE_TXT2, SET_STATE_TXT3, SET_STATE_TXT4, + SET_NTPSERVER1, SET_NTPSERVER2, SET_NTPSERVER3, + SET_MEM1, SET_MEM2, SET_MEM3, SET_MEM4, SET_MEM5, + SET_CORS, + SET_FRIENDLYNAME1, SET_FRIENDLYNAME2, SET_FRIENDLYNAME3, SET_FRIENDLYNAME4, + +// SET_FRIENDLYNAME5, SET_FRIENDLYNAME6, SET_FRIENDLYNAME7, SET_FRIENDLYNAME8, // Future extension +// SET_BUTTON1, SET_BUTTON2, SET_BUTTON3, SET_BUTTON4, // Future extension +// SET_BUTTON5, SET_BUTTON6, SET_BUTTON7, SET_BUTTON8, // Future extension +// SET_BUTTON9, SET_BUTTON10, SET_BUTTON11, SET_BUTTON12, // Future extension +// SET_BUTTON13, SET_BUTTON14, SET_BUTTON15, SET_BUTTON16, // Future extension + + SET_MAX }; + enum CommandSource { SRC_IGNORE, SRC_MQTT, SRC_RESTART, SRC_BUTTON, SRC_SWITCH, SRC_BACKLOG, SRC_SERIAL, SRC_WEBGUI, SRC_WEBCOMMAND, SRC_WEBCONSOLE, SRC_PULSETIMER, SRC_TIMER, SRC_RULE, SRC_MAXPOWER, SRC_MAXENERGY, SRC_OVERTEMP, SRC_LIGHT, SRC_KNX, SRC_DISPLAY, SRC_WEMO, SRC_HUE, SRC_RETRY, SRC_REMOTE, SRC_SHUTTER, SRC_MAX }; diff --git a/tasmota/tasmota_version.h b/tasmota/tasmota_version.h index ccc8dcbaf..a083dad4b 100644 --- a/tasmota/tasmota_version.h +++ b/tasmota/tasmota_version.h @@ -20,6 +20,6 @@ #ifndef _TASMOTA_VERSION_H_ #define _TASMOTA_VERSION_H_ -const uint32_t VERSION = 0x07010205; +const uint32_t VERSION = 0x07010206; #endif // _TASMOTA_VERSION_H_ From 4aa66b8032c0a95253ab04b45417fcd8bedcf7ec Mon Sep 17 00:00:00 2001 From: stefanbode Date: Sun, 15 Dec 2019 14:21:41 +0100 Subject: [PATCH 74/84] Update xdrv_27_shutter.ino - bugfix inverted shutter - improved stepper soft start/stop now in all situations - --- tasmota/xdrv_27_shutter.ino | 73 ++++++++++++++++++++++++------------- 1 file changed, 48 insertions(+), 25 deletions(-) diff --git a/tasmota/xdrv_27_shutter.ino b/tasmota/xdrv_27_shutter.ino index 2d143f415..e13bba771 100644 --- a/tasmota/xdrv_27_shutter.ino +++ b/tasmota/xdrv_27_shutter.ino @@ -67,7 +67,7 @@ struct SHUTTER { int8_t direction[MAX_SHUTTERS]; // 1 == UP , 0 == stop; -1 == down uint8_t mode = 0; // operation mode definition. see enum type above SHT_OFF_OPEN__OFF_CLOSE, SHT_OFF_ON__OPEN_CLOSE, SHT_PULSE_OPEN__PULSE_CLOSE int16_t motordelay[MAX_SHUTTERS]; // initial motorstarttime in 0.05sec. - uint16_t pwm_frequency; // frequency of PWN for stepper motors + int16_t pwm_frequency; // frequency of PWN for stepper motors uint16_t max_pwm_frequency = 1000; // maximum of PWM frequency that can be used. depend on the motor and drivers uint8_t skip_relay_change; // avoid overrun at endstops } Shutter; @@ -223,7 +223,7 @@ void ShutterInit(void) break; } if (shutters_present < 4) { - Shutter.max_pwm_frequency = Settings.shuttercoeff[4][4]; + Shutter.max_pwm_frequency = Settings.shuttercoeff[4][4] > 0 ? Settings.shuttercoeff[4][4] : Shutter.max_pwm_frequency; } Settings.shutter_accuracy = 1; } @@ -237,13 +237,10 @@ void ShutterUpdatePosition(void) for (uint32_t i = 0; i < shutters_present; i++) { if (Shutter.direction[i] != 0) { - //char stemp1[20]; - - - if (pin[GPIO_PWM1+i] < 99 && pin[GPIO_CNTR1+i] < 99 ) { + if (Shutter.mode == SHT_OFF_ON__OPEN_CLOSE && pin[GPIO_PWM1+i] < 99 && pin[GPIO_CNTR1+i] < 99 ) { // Calculate position with counter. Much more accurate and no need for motordelay workaround - // aading some counters to stop early - Shutter.real_position[i] = Shutter.start_position[i] + ( ((int32_t)RtcSettings.pulse_counter[i]+Shutter.max_pwm_frequency/100) * Shutter.direction[i] * 2000 / Shutter.max_pwm_frequency ); + // adding some steps to stop early + Shutter.real_position[i] = Shutter.direction[i] * 20 + ShutterCounterBasedPosition(i);; //AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: real %d, start %d, counter %d, max_freq %d, dir %d, freq %d"),Shutter.real_position[i], Shutter.start_position[i] ,RtcSettings.pulse_counter[i],Shutter.max_pwm_frequency , Shutter.direction[i] ,Shutter.max_pwm_frequency ); } else { Shutter.real_position[i] = Shutter.start_position[i] + ( (Shutter.time[i] - Shutter.motordelay[i]) * (Shutter.direction[i] > 0 ? 100 : -Shutter.close_velocity[i])); @@ -286,7 +283,7 @@ void ShutterUpdatePosition(void) delay(1); } analogWrite(pin[GPIO_PWM1+i], 0); - Shutter.real_position[i] = ((int32_t)RtcSettings.pulse_counter[i]*Shutter.direction[i]*2000 / Shutter.max_pwm_frequency)+Shutter.start_position[i]; + Shutter.real_position[i] = ShutterCounterBasedPosition(i); AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT:Real %d, pulsecount %d, start %d"), Shutter.real_position[i],RtcSettings.pulse_counter[i], Shutter.start_position[i]); } @@ -361,10 +358,29 @@ void ShutterStartInit(uint8_t index, int8_t direction, int32_t target_pos) //AddLog_P2(LOG_LEVEL_INFO, PSTR("SHT: Start shutter: %d from %d to %d in directin %d"), index, Shutter.start_position[index], Shutter.target_position[index], Shutter.direction[index]); } -void ShutterDelayForMotorStop(void) +void ShutterWaitForMotorStop(uint8_t index) { - AddLog_P2(LOG_LEVEL_INFO, PSTR("SHT: Wait for Motorstop %d"), MOTOR_STOP_TIME); - delay(MOTOR_STOP_TIME); + AddLog_P2(LOG_LEVEL_INFO, PSTR("SHT: Wait for Motorstop..")); + if (Shutter.mode == SHT_OFF_ON__OPEN_CLOSE) { + if (pin[GPIO_PWM1+index] < 99 && pin[GPIO_CNTR1+index] < 99 ) { + //AddLog_P2(LOG_LEVEL_INFO, PSTR("SHT: Frequency change %d"), Shutter.pwm_frequency); + while (Shutter.pwm_frequency > 100) { + Shutter.pwm_frequency = tmax(Shutter.pwm_frequency-(Shutter.max_pwm_frequency/(Shutter.motordelay[index]+1)) , 0); + analogWriteFreq(Shutter.pwm_frequency); + analogWrite(pin[GPIO_PWM1+index], 50); + delay(50); + } + Shutter.pwm_frequency = 0; + analogWriteFreq(Shutter.pwm_frequency); + analogWrite(pin[GPIO_PWM1+index], 0); + Shutter.real_position[index] = ShutterCounterBasedPosition(index); + } else { + ExecuteCommandPower(Settings.shutter_startrelay[index], 0, SRC_SHUTTER); + delay(MOTOR_STOP_TIME); + } + } else { + delay(MOTOR_STOP_TIME); + } } void ShutterReportPosition(void) @@ -373,7 +389,6 @@ void ShutterReportPosition(void) for (uint8_t i = 0; i < shutters_present; i++) { //AddLog_P2(LOG_LEVEL_INFO, PSTR("SHT: Shutter %d: Real Pos: %d"), i+1,Shutter.real_position[i]); if (Shutter.direction[i] != 0) { - char stemp1[20]; char stemp2[10]; uint8_t position = ShutterRealToPercentPosition(Shutter.real_position[i], i); dtostrfd((float)Shutter.time[i] / 20, 2, stemp2); @@ -381,7 +396,7 @@ void ShutterReportPosition(void) //Settings.shutter_position[i] = Settings.shuttercoeff[2][i] * 5 > Shutter.real_position[i] ? Shutter.real_position[i] / Settings.shuttercoeff[2][i] : (Shutter.real_position[i]-Settings.shuttercoeff[0,i]) / Settings.shuttercoeff[1][i]; AddLog_P2(LOG_LEVEL_INFO, MSG_SHUTTER_POS, i+1, Shutter.real_position[i], Shutter.start_position[i], Shutter.target_position[i], Shutter.direction[i], Shutter.motordelay[i],stemp2,Shutter.pwm_frequency); Response_P(PSTR("{")); - ResponseAppend_P(JSON_SHUTTER_POS, i+1, position, Shutter.direction[i]); + ResponseAppend_P(JSON_SHUTTER_POS, i+1, Settings.shutter_invert[i] ? 100-position : position, Shutter.direction[i]); ResponseJsonEnd(); MqttPublishPrefixTopic_P(RESULT_OR_TELE, mqtt_data); } @@ -395,6 +410,11 @@ void ShutterReportPosition(void) //AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: rules_flag.shutter_moving: %d, moved %d"), rules_flag.shutter_moving, rules_flag.shutter_moved); } +int32_t ShutterCounterBasedPosition(uint8 i) +{ + return ((int32_t)RtcSettings.pulse_counter[i]*Shutter.direction[i]*2000 / Shutter.max_pwm_frequency)+Shutter.start_position[i]; +} + void ShutterRelayChanged(void) { @@ -407,19 +427,20 @@ void ShutterRelayChanged(void) power_t powerstate_local = (power >> (Settings.shutter_startrelay[i] -1)) & 3; //uint8 manual_relays_changed = ((Shutter.switched_relay >> (Settings.shutter_startrelay[i] -1)) & 3) && SRC_IGNORE != last_source && SRC_SHUTTER != last_source && SRC_PULSETIMER != last_source ; uint8 manual_relays_changed = ((Shutter.switched_relay >> (Settings.shutter_startrelay[i] -1)) & 3) && SRC_SHUTTER != last_source && SRC_PULSETIMER != last_source ; + //AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Shutter %d: source: %s, powerstate_local %ld, Shutter.switched_relay %d, manual change %d"), i+1, GetTextIndexed(stemp1, sizeof(stemp1), last_source, kCommandSource), powerstate_local,Shutter.switched_relay,manual_relays_changed); if (manual_relays_changed) { + //Shutter.skip_relay_change = true; if (Shutter.mode == SHT_OFF_ON__OPEN_CLOSE) { + ShutterWaitForMotorStop(i); switch (powerstate_local) { case 1: - ShutterDelayForMotorStop(); ShutterStartInit(i, 1, Shutter.open_max[i]); break; case 3: - ShutterDelayForMotorStop(); ShutterStartInit(i, -1, 0); break; default: - Shutter.direction[i] = 0; + //AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Shutter %d: Switch OFF motor."),i); Shutter.target_position[i] = Shutter.real_position[i]; } } else { @@ -430,11 +451,11 @@ void ShutterRelayChanged(void) last_source = SRC_SHUTTER; // avoid switch off in the next loop if (powerstate_local == 2) { // testing on CLOSE relay, if ON // close with relay two - ShutterDelayForMotorStop(); + ShutterWaitForMotorStop(i); ShutterStartInit(i, -1, 0); } else { // opens with relay one - ShutterDelayForMotorStop(); + ShutterWaitForMotorStop(i); ShutterStartInit(i, 1, Shutter.open_max[i]); } } @@ -533,16 +554,17 @@ void CmndShutterPosition(void) ExecuteCommandPower(Settings.shutter_startrelay[index] + (new_shutterdirection == 1 ? 0 : 1), 1, SRC_SHUTTER); delay(100); } else { - ExecuteCommandPower(Settings.shutter_startrelay[index] + (new_shutterdirection == 1 ? 1 : 0), 0, SRC_SHUTTER); - ShutterDelayForMotorStop(); + if (Shutter.mode == SHT_OFF_OPEN__OFF_CLOSE) { + ExecuteCommandPower(Settings.shutter_startrelay[index] + (new_shutterdirection == 1 ? 1 : 0), 0, SRC_SHUTTER); + ShutterWaitForMotorStop(index); + } } } if (Shutter.direction[index] != new_shutterdirection ) { - if (Shutter.mode == SHT_OFF_ON__OPEN_CLOSE) { - ExecuteCommandPower(Settings.shutter_startrelay[index], 0, SRC_SHUTTER); //AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Delay5 5s, xdrv %d"), XdrvMailbox.payload); - ShutterDelayForMotorStop(); + ShutterWaitForMotorStop(index); + ExecuteCommandPower(Settings.shutter_startrelay[index], 0, SRC_SHUTTER); ShutterStartInit(index, new_shutterdirection, Shutter.target_position[index]); // Code for shutters with circuit safe configuration, switch the direction Relay ExecuteCommandPower(Settings.shutter_startrelay[index] +1, new_shutterdirection == 1 ? 0 : 1, SRC_SHUTTER); @@ -635,7 +657,7 @@ void CmndShutterSetHalfway(void) void CmndShutterFrequency(void) { - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 10000)) { + if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload <= 20000)) { Shutter.max_pwm_frequency = XdrvMailbox.payload; if (shutters_present < 4) { Settings.shuttercoeff[4][4] = Shutter.max_pwm_frequency; @@ -750,6 +772,7 @@ bool Xdrv27(uint8_t function) //AddLog_P2(LOG_LEVEL_ERROR, PSTR("SHT: skip relay change: %d"),i+1); result = true; Shutter.skip_relay_change = 0; + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Skipping switch off relay %d"),i); ExecuteCommandPower(i+1, 0, SRC_SHUTTER); } break; From 6985fcecaf012e9dc4798df7fbf7c0f68b7d0445 Mon Sep 17 00:00:00 2001 From: Hadinger Date: Sun, 15 Dec 2019 16:02:41 +0100 Subject: [PATCH 75/84] Add Zigbee send automatic ZigbeeRead after sending a command --- tasmota/CHANGELOG.md | 1 + tasmota/xdrv_23_zigbee_0_constants.ino | 2 - tasmota/xdrv_23_zigbee_1_headers.ino | 26 +++++++++++ tasmota/xdrv_23_zigbee_3_devices.ino | 59 ++++++++++++++++++++++++- tasmota/xdrv_23_zigbee_5_converters.ino | 14 +++--- tasmota/xdrv_23_zigbee_6_commands.ino | 59 +++++++++++++++++++++++++ tasmota/xdrv_23_zigbee_8_parsers.ino | 2 - tasmota/xdrv_23_zigbee_9_impl.ino | 18 ++++---- 8 files changed, 160 insertions(+), 21 deletions(-) create mode 100644 tasmota/xdrv_23_zigbee_1_headers.ino diff --git a/tasmota/CHANGELOG.md b/tasmota/CHANGELOG.md index e45d2efce..828b5d515 100644 --- a/tasmota/CHANGELOG.md +++ b/tasmota/CHANGELOG.md @@ -3,6 +3,7 @@ ### 7.1.2.6 20191214 - Change some more Settings locations freeing up space for future single char allowing variable length text +- Add Zigbee send automatic ZigbeeRead after sending a command ### 7.1.2.5 20191213 diff --git a/tasmota/xdrv_23_zigbee_0_constants.ino b/tasmota/xdrv_23_zigbee_0_constants.ino index 996d877f8..0c74c9d8a 100644 --- a/tasmota/xdrv_23_zigbee_0_constants.ino +++ b/tasmota/xdrv_23_zigbee_0_constants.ino @@ -19,8 +19,6 @@ #ifdef USE_ZIGBEE -#define ZIGBEE_VERBOSE // output versbose MQTT Zigbee logs. Will remain active for now - typedef uint64_t Z_IEEEAddress; typedef uint16_t Z_ShortAddress; diff --git a/tasmota/xdrv_23_zigbee_1_headers.ino b/tasmota/xdrv_23_zigbee_1_headers.ino new file mode 100644 index 000000000..b017d8e2e --- /dev/null +++ b/tasmota/xdrv_23_zigbee_1_headers.ino @@ -0,0 +1,26 @@ +/* + xdrv_23_zigbee_1_headers.ino - zigbee support for Tasmota + + Copyright (C) 2019 Theo Arends and Stephan Hadinger + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifdef USE_ZIGBEE + +// contains some definitions for functions used before their declarations + +void ZigbeeZCLSend(uint16_t dtsAddr, uint16_t clusterId, uint8_t endpoint, uint8_t cmdId, bool clusterSpecific, const uint8_t *msg, size_t len, bool disableDefResp = true, uint8_t transacId = 1); + +#endif // USE_ZIGBEE diff --git a/tasmota/xdrv_23_zigbee_3_devices.ino b/tasmota/xdrv_23_zigbee_3_devices.ino index f76c882c7..de13990c2 100644 --- a/tasmota/xdrv_23_zigbee_3_devices.ino +++ b/tasmota/xdrv_23_zigbee_3_devices.ino @@ -22,6 +22,9 @@ #include #include + +typedef int32_t (*Z_DeviceTimer)(uint16_t shortaddr, uint16_t cluster, uint16_t endpoint, uint32_t value); + typedef struct Z_Device { uint16_t shortaddr; // unique key if not null, or unspecified if null uint64_t longaddr; // 0x00 means unspecified @@ -33,6 +36,12 @@ typedef struct Z_Device { std::vector endpoints; // encoded as high 16 bits is endpoint, low 16 bits is ProfileId std::vector clusters_in; // encoded as high 16 bits is endpoint, low 16 bits is cluster number std::vector clusters_out; // encoded as high 16 bits is endpoint, low 16 bits is cluster number + // below are per device timers, used for example to query the new state of the device + uint32_t timer; // millis() when to fire the timer, 0 if no timer + uint16_t cluster; // cluster to use for the timer + uint16_t endpoint; // endpoint to use for timer + uint32_t value; // any raw value to use for the timer + Z_DeviceTimer func; // function to call when timer occurs } Z_Device; // All devices are stored in a Vector @@ -70,6 +79,11 @@ public: // Dump json String dump(uint32_t dump_mode, int32_t device_num = 0) const; + // Timers + void resetTimer(uint32_t shortaddr); + void setTimer(uint32_t shortaddr, uint32_t wait_ms, uint16_t cluster, uint16_t endpoint, uint32_t value, Z_DeviceTimer func); + void runTimer(void); + private: std::vector _devices = {}; @@ -157,7 +171,9 @@ Z_Device & Z_Devices::createDeviceEntry(uint16_t shortaddr, uint64_t longaddr) { String(), // FriendlyName std::vector(), std::vector(), - std::vector() }; + std::vector(), + 0,0,0,0, + nullptr }; _devices.push_back(device); return _devices.back(); } @@ -346,6 +362,47 @@ void Z_Devices::updateLastSeen(uint16_t shortaddr) { _updateLastSeen(device); } +// Per device timers +// +// Reset the timer for a specific device +void Z_Devices::resetTimer(uint32_t shortaddr) { + Z_Device & device = getShortAddr(shortaddr); + if (&device == nullptr) { return; } // don't crash if not found + device.timer = 0; + device.func = nullptr; +} + +// Set timer for a specific device +void Z_Devices::setTimer(uint32_t shortaddr, uint32_t wait_ms, uint16_t cluster, uint16_t endpoint, uint32_t value, Z_DeviceTimer func) { + Z_Device & device = getShortAddr(shortaddr); + if (&device == nullptr) { return; } // don't crash if not found + + device.cluster = cluster; + device.endpoint = endpoint; + device.value = value; + device.func = func; + device.timer = wait_ms + millis(); +} + +// Run timer at each tick +void Z_Devices::runTimer(void) { + uint32_t now = millis(); + + for (std::vector::iterator it = _devices.begin(); it != _devices.end(); ++it) { + Z_Device &device = *it; + uint16_t shortaddr = device.shortaddr; + + uint32_t timer = device.timer; + if ((timer) && (timer <= now)) { + // trigger the timer + (*device.func)(device.shortaddr, device.cluster, device.endpoint, device.value); + + device.timer = 0; // cancel the timer + } + } +} + + // Dump the internal memory of Zigbee devices // Mode = 1: simple dump of devices addresses and names // Mode = 2: Mode 1 + also dump the endpoints, profiles and clusters diff --git a/tasmota/xdrv_23_zigbee_5_converters.ino b/tasmota/xdrv_23_zigbee_5_converters.ino index 4defbf043..a56ada927 100644 --- a/tasmota/xdrv_23_zigbee_5_converters.ino +++ b/tasmota/xdrv_23_zigbee_5_converters.ino @@ -53,7 +53,6 @@ public: uint8_t srcendpoint, uint8_t dstendpoint, uint8_t wasbroadcast, uint8_t linkquality, uint8_t securityuse, uint8_t seqnumber, uint32_t timestamp) { -#ifdef ZIGBEE_VERBOSE char hex_char[_payload.len()*2+2]; ToHex_P((unsigned char*)_payload.getBuffer(), _payload.len(), hex_char, sizeof(hex_char)); AddLog_P2(LOG_LEVEL_DEBUG, PSTR("{\"" D_JSON_ZIGBEEZCL_RECEIVED "\":{" @@ -69,7 +68,6 @@ public: timestamp, _frame_control, _manuf_code, _transact_seq, _cmd_id, hex_char); -#endif } static ZCLFrame parseRawFrame(const SBuffer &buf, uint8_t offset, uint8_t len, uint16_t clusterid, uint16_t groupid) { // parse a raw frame and build the ZCL frame object @@ -497,7 +495,7 @@ const Z_AttributeConverter Z_PostProcess[] PROGMEM = { { 0x0007, 0x0000, "SwitchType", &Z_Copy }, // Level Control cluster - { 0x0008, 0x0000, "CurrentLevel", &Z_Copy }, + { 0x0008, 0x0000, "Dimmer", &Z_Copy }, // { 0x0008, 0x0001, "RemainingTime", &Z_Copy }, // { 0x0008, 0x0010, "OnOffTransitionTime", &Z_Copy }, // { 0x0008, 0x0011, "OnLevel", &Z_Copy }, @@ -652,14 +650,14 @@ const Z_AttributeConverter Z_PostProcess[] PROGMEM = { { 0x0102, 0x0019, "IntermediateSetpointsTilt",&Z_Copy }, // Color Control cluster - { 0x0300, 0x0000, "CurrentHue", &Z_Copy }, - { 0x0300, 0x0001, "CurrentSaturation", &Z_Copy }, + { 0x0300, 0x0000, "Hue", &Z_Copy }, + { 0x0300, 0x0001, "Sat", &Z_Copy }, { 0x0300, 0x0002, "RemainingTime", &Z_Copy }, - { 0x0300, 0x0003, "CurrentX", &Z_Copy }, - { 0x0300, 0x0004, "CurrentY", &Z_Copy }, + { 0x0300, 0x0003, "X", &Z_Copy }, + { 0x0300, 0x0004, "Y", &Z_Copy }, { 0x0300, 0x0005, "DriftCompensation", &Z_Copy }, { 0x0300, 0x0006, "CompensationText", &Z_Copy }, - { 0x0300, 0x0007, "ColorTemperatureMireds",&Z_Copy }, + { 0x0300, 0x0007, "CT", &Z_Copy }, { 0x0300, 0x0008, "ColorMode", &Z_Copy }, { 0x0300, 0x0010, "NumberOfPrimaries", &Z_Copy }, { 0x0300, 0x0011, "Primary1X", &Z_Copy }, diff --git a/tasmota/xdrv_23_zigbee_6_commands.ino b/tasmota/xdrv_23_zigbee_6_commands.ino index c877bd3a3..70a8f0788 100644 --- a/tasmota/xdrv_23_zigbee_6_commands.ino +++ b/tasmota/xdrv_23_zigbee_6_commands.ino @@ -47,6 +47,64 @@ const Z_CommandConverter Z_Commands[] = { { "ShutterTilt", "0102!08xx"}, // Tilt percentage }; +#define ZLE(x) ((x) & 0xFF), ((x) >> 8) // Little Endian + +// Below are the attributes we wand to read from each cluster +const uint8_t CLUSTER_0006[] = { ZLE(0x0000) }; // Power +const uint8_t CLUSTER_0008[] = { ZLE(0x0000) }; // CurrentLevel +const uint8_t CLUSTER_0009[] = { ZLE(0x0000) }; // AlarmCount +const uint8_t CLUSTER_0300[] = { ZLE(0x0000), ZLE(0x0001), ZLE(0x0003), ZLE(0x0004), ZLE(0x0007) }; // Hue, Sat, X, Y, CT + +int32_t Z_ReadAttrCallback(uint16_t shortaddr, uint16_t cluster, uint16_t endpoint, uint32_t value) { + size_t attrs_len = 0; + const uint8_t* attrs = nullptr; + + switch (cluster) { + case 0x0006: // for On/Off + attrs = CLUSTER_0006; + attrs_len = sizeof(CLUSTER_0006); + break; + case 0x0008: // for Dimmer + attrs = CLUSTER_0008; + attrs_len = sizeof(CLUSTER_0008); + break; + case 0x0009: // for Alarms + attrs = CLUSTER_0009; + attrs_len = sizeof(CLUSTER_0009); + break; + case 0x0300: // for Lights + attrs = CLUSTER_0300; + attrs_len = sizeof(CLUSTER_0300); + break; + } + if (attrs) { + ZigbeeZCLSend(shortaddr, cluster, endpoint, ZCL_READ_ATTRIBUTES, false, attrs, attrs_len, false /* we do want a response */); + } +} + + +// set a timer to read back the value in the future +void zigbeeSetCommandTimer(uint16_t shortaddr, uint16_t cluster, uint16_t endpoint) { + uint32_t wait_ms = 0; + + switch (cluster) { + case 0x0006: // for On/Off + case 0x0009: // for Alamrs + wait_ms = 200; // wait 0.2 s + break; + case 0x0008: // for Dimmer + case 0x0300: // for Color + wait_ms = 1050; // wait 1.0 s + break; + case 0x0102: // for Shutters + wait_ms = 10000; // wait 10.0 s + break; + } + if (wait_ms) { + zigbee_devices.setTimer(shortaddr, wait_ms, cluster, endpoint, 0 /* value */, &Z_ReadAttrCallback); + } +} + const __FlashStringHelper* zigbeeFindCommand(const char *command) { char parm_uc[16]; // used to convert JSON keys to uppercase for (uint32_t i = 0; i < sizeof(Z_Commands) / sizeof(Z_Commands[0]); i++) { @@ -114,6 +172,7 @@ const uint8_t kZ_Numbers[] PROGMEM = { 0,0,0,0,0, 2,2, 255 }; +// Convert an alias like "On" to the corresponding number uint32_t ZigbeeAliasOrNumber(const char *state_text) { char command[16]; int state_number = GetCommandCode(command, sizeof(command), state_text, kZ_Alias); diff --git a/tasmota/xdrv_23_zigbee_8_parsers.ino b/tasmota/xdrv_23_zigbee_8_parsers.ino index acd7e5d26..f88bf15e2 100644 --- a/tasmota/xdrv_23_zigbee_8_parsers.ino +++ b/tasmota/xdrv_23_zigbee_8_parsers.ino @@ -372,12 +372,10 @@ int32_t Z_ReceiveAfIncomingMessage(int32_t res, const class SBuffer &buf) { zigbee_devices.updateLastSeen(srcaddr); ZCLFrame zcl_received = ZCLFrame::parseRawFrame(buf, 19, buf.get8(18), clusterid, groupid); -#ifdef ZIGBEE_VERBOSE zcl_received.publishMQTTReceived(groupid, clusterid, srcaddr, srcendpoint, dstendpoint, wasbroadcast, linkquality, securityuse, seqnumber, timestamp); -#endif char shortaddr[8]; snprintf_P(shortaddr, sizeof(shortaddr), PSTR("0x%04X"), srcaddr); diff --git a/tasmota/xdrv_23_zigbee_9_impl.ino b/tasmota/xdrv_23_zigbee_9_impl.ino index 3bfd6c4e3..461b3af32 100644 --- a/tasmota/xdrv_23_zigbee_9_impl.ino +++ b/tasmota/xdrv_23_zigbee_9_impl.ino @@ -191,14 +191,9 @@ void ZigbeeInput(void) SBuffer znp_buffer = zigbee_buffer->subBuffer(2, zigbee_frame_len - 3); // remove SOF, LEN and FCS -#ifdef ZIGBEE_VERBOSE ToHex_P((unsigned char*)znp_buffer.getBuffer(), znp_buffer.len(), hex_char, sizeof(hex_char)); AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_ZIGBEE D_JSON_ZIGBEEZNPRECEIVED " %s"), hex_char); - // Response_P(PSTR("{\"" D_JSON_ZIGBEEZNPRECEIVED "\":\"%s\"}"), hex_char); - // MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEEZNPRECEIVED)); - // XdrvRulesProcess(); -#endif // now process the message ZigbeeProcessInput(znp_buffer); @@ -320,15 +315,13 @@ void ZigbeeZNPSend(const uint8_t *msg, size_t len) { ZigbeeSerial->write(fcs); // finally send fcs checksum byte AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("ZNPSend FCS %02X"), fcs); } -#ifdef ZIGBEE_VERBOSE // Now send a MQTT message to report the sent message char hex_char[(len * 2) + 2]; AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_ZIGBEE D_JSON_ZIGBEEZNPSENT " %s"), ToHex_P(msg, len, hex_char, sizeof(hex_char))); -#endif } -void ZigbeeZCLSend(uint16_t dtsAddr, uint16_t clusterId, uint8_t endpoint, uint8_t cmdId, bool clusterSpecific, const uint8_t *msg, size_t len, bool disableDefResp = true, uint8_t transacId = 1) { +void ZigbeeZCLSend(uint16_t dtsAddr, uint16_t clusterId, uint8_t endpoint, uint8_t cmdId, bool clusterSpecific, const uint8_t *msg, size_t len, bool disableDefResp, uint8_t transacId) { SBuffer buf(25+len); buf.add8(Z_SREQ | Z_AF); // 24 buf.add8(AF_DATA_REQUEST); // 01 @@ -423,6 +416,10 @@ void zigbeeZCLSendStr(uint16_t dstAddr, uint8_t endpoint, const char *data) { // everything is good, we can send the command ZigbeeZCLSend(dstAddr, cluster, endpoint, cmd, clusterSpecific, buf.getBuffer(), buf.len()); + // now set the timer, if any, to read back the state later + if (clusterSpecific) { + zigbeeSetCommandTimer(dstAddr, cluster, endpoint); + } ResponseCmndDone(); } @@ -648,6 +645,11 @@ bool Xdrv23(uint8_t function) if (zigbee.active) { switch (function) { + case FUNC_EVERY_50_MSECOND: + if (!zigbee.init_phase) { + zigbee_devices.runTimer(); + } + break; case FUNC_LOOP: if (ZigbeeSerial) { ZigbeeInput(); } if (zigbee.state_machine) { From d802b2dd6828aa79ee62d91127670ad489da0697 Mon Sep 17 00:00:00 2001 From: Hadinger Date: Sun, 15 Dec 2019 19:39:27 +0100 Subject: [PATCH 76/84] Add Zigbee improving Occupancy:false detection for Aqara sensor --- tasmota/CHANGELOG.md | 1 + tasmota/xdrv_23_zigbee_5_converters.ino | 69 ++++++++++++++++++++----- tasmota/xdrv_23_zigbee_8_parsers.ino | 13 +++-- 3 files changed, 64 insertions(+), 19 deletions(-) diff --git a/tasmota/CHANGELOG.md b/tasmota/CHANGELOG.md index 828b5d515..cb90c5af5 100644 --- a/tasmota/CHANGELOG.md +++ b/tasmota/CHANGELOG.md @@ -4,6 +4,7 @@ - Change some more Settings locations freeing up space for future single char allowing variable length text - Add Zigbee send automatic ZigbeeRead after sending a command +- Add Zigbee improving Occupancy:false detection for Aqara sensor ### 7.1.2.5 20191213 diff --git a/tasmota/xdrv_23_zigbee_5_converters.ino b/tasmota/xdrv_23_zigbee_5_converters.ino index a56ada927..687ac2483 100644 --- a/tasmota/xdrv_23_zigbee_5_converters.ino +++ b/tasmota/xdrv_23_zigbee_5_converters.ino @@ -39,20 +39,23 @@ class ZCLFrame { public: ZCLFrame(uint8_t frame_control, uint16_t manuf_code, uint8_t transact_seq, uint8_t cmd_id, - const char *buf, size_t buf_len, uint16_t clusterid = 0, uint16_t groupid = 0): + const char *buf, size_t buf_len, uint16_t clusterid = 0, uint16_t groupid = 0, + uint16_t srcaddr = 0, uint8_t srcendpoint = 0, uint8_t dstendpoint = 0, uint8_t wasbroadcast = 0, + uint8_t linkquality = 0, uint8_t securityuse = 0, uint8_t seqnumber = 0, + uint32_t timestamp = 0): _cmd_id(cmd_id), _manuf_code(manuf_code), _transact_seq(transact_seq), _payload(buf_len ? buf_len : 250), // allocate the data frame from source or preallocate big enough - _cluster_id(clusterid), _group_id(groupid) + _cluster_id(clusterid), _group_id(groupid), + _srcaddr(srcaddr), _srcendpoint(srcendpoint), _dstendpoint(dstendpoint), _wasbroadcast(wasbroadcast), + _linkquality(linkquality), _securityuse(securityuse), _seqnumber(seqnumber), + _timestamp(timestamp) { _frame_control.d8 = frame_control; _payload.addBuffer(buf, buf_len); }; - void publishMQTTReceived(uint16_t groupid, uint16_t clusterid, uint16_t srcaddr, - uint8_t srcendpoint, uint8_t dstendpoint, uint8_t wasbroadcast, - uint8_t linkquality, uint8_t securityuse, uint8_t seqnumber, - uint32_t timestamp) { + void log(void) { char hex_char[_payload.len()*2+2]; ToHex_P((unsigned char*)_payload.getBuffer(), _payload.len(), hex_char, sizeof(hex_char)); AddLog_P2(LOG_LEVEL_DEBUG, PSTR("{\"" D_JSON_ZIGBEEZCL_RECEIVED "\":{" @@ -62,15 +65,18 @@ public: "\"timestamp\":%d," "\"fc\":\"0x%02X\",\"manuf\":\"0x%04X\",\"transact\":%d," "\"cmdid\":\"0x%02X\",\"payload\":\"%s\"}}"), - groupid, clusterid, srcaddr, - srcendpoint, dstendpoint, wasbroadcast, - linkquality, securityuse, seqnumber, - timestamp, + _group_id, _cluster_id, _srcaddr, + _srcendpoint, _dstendpoint, _wasbroadcast, + _linkquality, _securityuse, _seqnumber, + _timestamp, _frame_control, _manuf_code, _transact_seq, _cmd_id, hex_char); } - static ZCLFrame parseRawFrame(const SBuffer &buf, uint8_t offset, uint8_t len, uint16_t clusterid, uint16_t groupid) { // parse a raw frame and build the ZCL frame object + static ZCLFrame parseRawFrame(const SBuffer &buf, uint8_t offset, uint8_t len, uint16_t clusterid, uint16_t groupid, + uint16_t srcaddr = 0, uint8_t srcendpoint = 0, uint8_t dstendpoint = 0, uint8_t wasbroadcast = 0, + uint8_t linkquality = 0, uint8_t securityuse = 0, uint8_t seqnumber = 0, + uint32_t timestamp = 0) { // parse a raw frame and build the ZCL frame object uint32_t i = offset; ZCLHeaderFrameControl_t frame_control; uint16_t manuf_code = 0; @@ -115,6 +121,10 @@ public: return _cluster_id; } + inline uint16_t getSrcEndpoint(void) const { + return _srcendpoint; + } + const SBuffer &getPayload(void) const { return _payload; } @@ -131,6 +141,15 @@ private: uint16_t _cluster_id = 0; uint16_t _group_id = 0; SBuffer _payload; + // information from decoded ZCL frame + uint16_t _srcaddr; + uint8_t _srcendpoint; + uint8_t _dstendpoint; + uint8_t _wasbroadcast; + uint8_t _linkquality; + uint8_t _securityuse; + uint8_t _seqnumber; + uint32_t _timestamp; }; // Zigbee ZCL converters @@ -461,6 +480,8 @@ typedef struct Z_AttributeConverter { Z_AttrConverter func; } Z_AttributeConverter; +#define OCCUPANCY "Occupancy" // global define for Aqara + // list of post-processing directives const Z_AttributeConverter Z_PostProcess[] PROGMEM = { { 0x0000, 0x0000, "ZCLVersion", &Z_Copy }, @@ -729,7 +750,7 @@ const Z_AttributeConverter Z_PostProcess[] PROGMEM = { { 0x0405, 0xFFFF, nullptr, &Z_Remove }, // Remove all other values // Occupancy Sensing cluster - { 0x0406, 0x0000, "Occupancy", &Z_Copy }, // Occupancy (map8) + { 0x0406, 0x0000, OCCUPANCY, &Z_AqaraOccupancy }, // Occupancy (map8) { 0x0406, 0x0001, "OccupancySensorType", &Z_Copy }, // OccupancySensorType { 0x0406, 0xFFFF, nullptr, &Z_Remove }, // Remove all other values @@ -796,6 +817,30 @@ int32_t Z_FloatDiv10(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& return 1; // remove original key } + +// Aqara Occupancy behavior: the Aqara device only sends Occupancy: true events every 60 seconds. +// Here we add a timer so if we don't receive a Occupancy event for 90 seconds, we send Occupancy:false +const uint32_t OCCUPANCY_TIMEOUT = 90 * 1000; // 90 s + +int32_t Z_OccupancyCallback(uint16_t shortaddr, uint16_t cluster, uint16_t endpoint, uint32_t value) { + // send Occupancy:false message + Response_P(PSTR("{\"" D_CMND_ZIGBEE_RECEIVED "\":{\"0x%04X\":{\"" OCCUPANCY "\":0}}}"), shortaddr); + MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR)); + XdrvRulesProcess(); +} + +int32_t Z_AqaraOccupancy(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const __FlashStringHelper *new_name, uint16_t cluster, uint16_t attr) { + json[new_name] = value; + uint32_t occupancy = value; + + if (occupancy) { + zigbee_devices.setTimer(shortaddr, OCCUPANCY_TIMEOUT, cluster, zcl->getSrcEndpoint(), 0, &Z_OccupancyCallback); + } else { + zigbee_devices.resetTimer(shortaddr); + } + return 1; // remove original key +} + // Aqara Vibration Sensor - special proprietary attributes int32_t Z_AqaraVibration(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const __FlashStringHelper *new_name, uint16_t cluster, uint16_t attr) { //json[new_name] = value; diff --git a/tasmota/xdrv_23_zigbee_8_parsers.ino b/tasmota/xdrv_23_zigbee_8_parsers.ino index f88bf15e2..2f30080fa 100644 --- a/tasmota/xdrv_23_zigbee_8_parsers.ino +++ b/tasmota/xdrv_23_zigbee_8_parsers.ino @@ -370,13 +370,12 @@ int32_t Z_ReceiveAfIncomingMessage(int32_t res, const class SBuffer &buf) { uint8_t seqnumber = buf.get8(17); zigbee_devices.updateLastSeen(srcaddr); - ZCLFrame zcl_received = ZCLFrame::parseRawFrame(buf, 19, buf.get8(18), clusterid, groupid); - - zcl_received.publishMQTTReceived(groupid, clusterid, srcaddr, - srcendpoint, dstendpoint, wasbroadcast, - linkquality, securityuse, seqnumber, - timestamp); - + ZCLFrame zcl_received = ZCLFrame::parseRawFrame(buf, 19, buf.get8(18), clusterid, groupid, + srcaddr, + srcendpoint, dstendpoint, wasbroadcast, + linkquality, securityuse, seqnumber, + timestamp); + zcl_received.log(); char shortaddr[8]; snprintf_P(shortaddr, sizeof(shortaddr), PSTR("0x%04X"), srcaddr); From d4c2e92fd560c3bad87fb1e168c19707dbbd6b4b Mon Sep 17 00:00:00 2001 From: gemu2015 Date: Mon, 16 Dec 2019 06:52:25 +0100 Subject: [PATCH 77/84] scripter FUNC_SET_POWER into separate section >P --- tasmota/xdrv_10_scripter.ino | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tasmota/xdrv_10_scripter.ino b/tasmota/xdrv_10_scripter.ino index 0bab141d7..5badb88ea 100755 --- a/tasmota/xdrv_10_scripter.ino +++ b/tasmota/xdrv_10_scripter.ino @@ -1030,8 +1030,10 @@ char *isvar(char *lp, uint8_t *vtype,struct T_INDEX *tind,float *fp,char *sp,Jso if ((*jo).is(vn)) { if (!strncmp(str_value,"ON",2)) { if (fp) *fp=1; + goto nexit; } else if (!strncmp(str_value,"OFF",3)) { if (fp) *fp=0; + goto nexit; } else { *vtype=STR_RES; tind->bits.constant=1; @@ -1039,6 +1041,7 @@ char *isvar(char *lp, uint8_t *vtype,struct T_INDEX *tind,float *fp,char *sp,Jso if (sp) strlcpy(sp,str_value,SCRIPT_MAXSSIZE); return lp+len; } + } else { if (fp) { if (!strncmp(vn.c_str(),"Epoch",5)) { @@ -1047,6 +1050,7 @@ char *isvar(char *lp, uint8_t *vtype,struct T_INDEX *tind,float *fp,char *sp,Jso *fp=CharToFloat((char*)str_value); } } + nexit: *vtype=NUM_RES; tind->bits.constant=1; tind->bits.is_string=0; @@ -4843,6 +4847,12 @@ bool Xdrv10(uint8_t function) result = ScriptCommand(); break; case FUNC_SET_POWER: +#ifdef SCRIPT_POWER_SECTION + if (bitRead(Settings.rule_enabled, 0)) Run_Scripter(">P",2,0); +#else + if (bitRead(Settings.rule_enabled, 0)) Run_Scripter(">E",2,0); +#endif + break; case FUNC_RULES_PROCESS: if (bitRead(Settings.rule_enabled, 0)) Run_Scripter(">E",2,mqtt_data); break; From aae86a6718d6db43271581a5900c83ff84b58acf Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Mon, 16 Dec 2019 15:13:57 +0100 Subject: [PATCH 78/84] Add fallback functionality Add fallback functionality from next version 7.1.2.7 --- tasmota/CHANGELOG.md | 1 + tasmota/settings.ino | 284 +++++++++++++++++++++-------- tasmota/support.ino | 20 +- tasmota/support_command.ino | 89 ++++----- tasmota/support_rtc.ino | 6 +- tasmota/support_statistics.ino | 45 +---- tasmota/support_tasmota.ino | 87 +++++---- tasmota/support_wifi.ino | 22 ++- tasmota/tasmota.ino | 15 +- tasmota/xdrv_01_webserver.ino | 106 ++++++----- tasmota/xdrv_02_mqtt.ino | 192 ++++++++----------- tasmota/xdrv_10_rules.ino | 20 +- tasmota/xdrv_10_scripter.ino | 12 +- tasmota/xdrv_12_home_assistant.ino | 42 ++--- tasmota/xdrv_13_display.ino | 8 +- tasmota/xdrv_20_hue.ino | 7 +- tasmota/xdrv_21_wemo.ino | 2 +- tasmota/xdrv_99_debug.ino | 46 ----- 18 files changed, 517 insertions(+), 487 deletions(-) diff --git a/tasmota/CHANGELOG.md b/tasmota/CHANGELOG.md index cb90c5af5..a466a3212 100644 --- a/tasmota/CHANGELOG.md +++ b/tasmota/CHANGELOG.md @@ -5,6 +5,7 @@ - Change some more Settings locations freeing up space for future single char allowing variable length text - Add Zigbee send automatic ZigbeeRead after sending a command - Add Zigbee improving Occupancy:false detection for Aqara sensor +- Add fallback functionality from next version 7.1.2.7 ### 7.1.2.5 20191213 diff --git a/tasmota/settings.ino b/tasmota/settings.ino index c1faa559e..d1d3fc1e8 100644 --- a/tasmota/settings.ino +++ b/tasmota/settings.ino @@ -446,6 +446,9 @@ void UpdateQuickPowerCycle(bool update) * Config Settings.text char array support \*********************************************************************************************/ +char aws_mqtt_host[66]; +char aws_mqtt_user[1] { 0 }; + const uint32_t settings_text_size = 457; // Settings.flag4 (1E0) - Settings.ota_url (017) uint32_t GetSettingsTextLen(void) @@ -457,7 +460,7 @@ uint32_t GetSettingsTextLen(void) return position - Settings.ota_url; } -bool SettingsUpdateText(uint32_t index, char* replace_me) +bool SettingsUpdateText(uint32_t index, const char* replace_me) { if (index >= SET_MAX) { return false; // Setting not supported - internal error @@ -468,55 +471,165 @@ bool SettingsUpdateText(uint32_t index, char* replace_me) char replace[replace_len +1]; memcpy(replace, replace_me, sizeof(replace)); - uint32_t start_pos = 0; - uint32_t end_pos = 0; - char* position = Settings.ota_url; - for (uint32_t size = 0; size < SET_MAX; size++) { - while (*position++ != '\0') { } - if (1 == index) { - start_pos = position - Settings.ota_url; + if (Settings.version < 0x07010207) { + uint32_t idx = 0; + switch (index) { + case SET_OTAURL: strlcpy(Settings.ota_url, replace, sizeof(Settings.ota_url)); break; + case SET_MQTTPREFIX3: idx++; + case SET_MQTTPREFIX2: idx++; + case SET_MQTTPREFIX1: strlcpy(Settings.mqtt_prefix[idx], replace, sizeof(Settings.mqtt_prefix[idx])); break; + case SET_STASSID2: idx++; + case SET_STASSID1: strlcpy(Settings.sta_ssid[idx], replace, sizeof(Settings.sta_ssid[idx])); break; + case SET_STAPWD2: idx++; + case SET_STAPWD1: strlcpy(Settings.sta_pwd[idx], replace, sizeof(Settings.sta_pwd[idx])); break; + case SET_HOSTNAME: strlcpy(Settings.hostname, replace, sizeof(Settings.hostname)); break; + case SET_SYSLOG_HOST: strlcpy(Settings.syslog_host, replace, sizeof(Settings.syslog_host)); break; + case SET_WEBPWD: strlcpy(Settings.web_password, replace, sizeof(Settings.web_password)); break; +#if defined(USE_MQTT_TLS) && defined(USE_MQTT_AWS_IOT) + case SET_MQTT_HOST: + if (strlen(replace) <= sizeof(Settings.mqtt_host)) { + strlcpy(Settings.mqtt_host, replace, sizeof(Settings.mqtt_host)); + Settings.mqtt_user[0] = 0; + } else { + // need to split in mqtt_user first then mqtt_host + strlcpy(Settings.mqtt_user, replace, sizeof(Settings.mqtt_user)); + strlcpy(Settings.mqtt_host, &replace[sizeof(Settings.mqtt_user)-1], sizeof(Settings.mqtt_host)); + } + break; + case SET_MQTT_USER: break; +#else + case SET_MQTT_HOST: strlcpy(Settings.mqtt_host, replace, sizeof(Settings.mqtt_host)); break; + case SET_MQTT_USER: strlcpy(Settings.mqtt_user, replace, sizeof(Settings.mqtt_user)); break; +#endif + case SET_MQTT_CLIENT: strlcpy(Settings.mqtt_client, replace, sizeof(Settings.mqtt_client)); break; + case SET_MQTT_PWD: strlcpy(Settings.mqtt_pwd, replace, sizeof(Settings.mqtt_pwd)); break; + case SET_MQTT_FULLTOPIC: strlcpy(Settings.mqtt_fulltopic, replace, sizeof(Settings.mqtt_fulltopic)); break; + case SET_MQTT_TOPIC: strlcpy(Settings.mqtt_topic, replace, sizeof(Settings.mqtt_topic)); break; + case SET_MQTT_BUTTON_TOPIC: strlcpy(Settings.button_topic, replace, sizeof(Settings.button_topic)); break; + case SET_MQTT_SWITCH_TOPIC: strlcpy(Settings.switch_topic, replace, sizeof(Settings.switch_topic)); break; + case SET_MQTT_GRP_TOPIC: strlcpy(Settings.mqtt_grptopic, replace, sizeof(Settings.mqtt_grptopic)); break; + case SET_STATE_TXT4: idx++; + case SET_STATE_TXT3: idx++; + case SET_STATE_TXT2: idx++; + case SET_STATE_TXT1: strlcpy(Settings.state_text[idx], replace, sizeof(Settings.state_text[idx])); break; + case SET_NTPSERVER3: idx++; + case SET_NTPSERVER2: idx++; + case SET_NTPSERVER1: strlcpy(Settings.ntp_server[idx], replace, sizeof(Settings.ntp_server[idx])); break; + case SET_MEM5: idx++; + case SET_MEM4: idx++; + case SET_MEM3: idx++; + case SET_MEM2: idx++; + case SET_MEM1: strlcpy(Settings.mems[idx], replace, sizeof(Settings.mems[idx])); break; + case SET_CORS: strlcpy(Settings.cors_domain, replace, sizeof(Settings.cors_domain)); break; + case SET_FRIENDLYNAME4: idx++; + case SET_FRIENDLYNAME3: idx++; + case SET_FRIENDLYNAME2: idx++; + case SET_FRIENDLYNAME1: strlcpy(Settings.friendlyname[idx], replace, sizeof(Settings.friendlyname[idx])); break; } - else if (0 == index) { - end_pos = position - Settings.ota_url -1; + } else { + uint32_t start_pos = 0; + uint32_t end_pos = 0; + char* position = Settings.ota_url; + for (uint32_t size = 0; size < SET_MAX; size++) { + while (*position++ != '\0') { } + if (1 == index) { + start_pos = position - Settings.ota_url; + } + else if (0 == index) { + end_pos = position - Settings.ota_url -1; + } + index--; } - index--; + uint32_t char_len = position - Settings.ota_url; + + uint32_t current_len = end_pos - start_pos; + int diff = replace_len - current_len; + + // AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TST: start %d, end %d, len %d, current %d, replace %d, diff %d"), + // start_pos, end_pos, char_len, current_len, replace_len, diff); + + int too_long = (char_len + diff) - settings_text_size; + if (too_long > 0) { + // AddLog_P2(LOG_LEVEL_INFO, PSTR("CFG: Text too long by %d char(s)"), too_long); + return false; // Replace text too long + } + + if (diff != 0) { + // Shift Settings.text up or down + memmove_P(Settings.ota_url + start_pos + replace_len, Settings.ota_url + end_pos, char_len - end_pos); + } + // Replace text + memmove_P(Settings.ota_url + start_pos, replace, replace_len); + // Fill for future use + memset(Settings.ota_url + char_len + diff, 0x00, settings_text_size - char_len - diff); } - uint32_t char_len = position - Settings.ota_url; - - uint32_t current_len = end_pos - start_pos; - int diff = replace_len - current_len; - -// AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TST: start %d, end %d, len %d, current %d, replace %d, diff %d"), -// start_pos, end_pos, char_len, current_len, replace_len, diff); - - int too_long = (char_len + diff) - settings_text_size; - if (too_long > 0) { -// AddLog_P2(LOG_LEVEL_INFO, PSTR("CFG: Text too long by %d char(s)"), too_long); - return false; // Replace text too long - } - - if (diff != 0) { - // Shift Settings.text up or down - memmove_P(Settings.ota_url + start_pos + replace_len, Settings.ota_url + end_pos, char_len - end_pos); - } - // Replace text - memmove_P(Settings.ota_url + start_pos, replace, replace_len); - // Fill for future use - memset(Settings.ota_url + char_len + diff, 0x00, settings_text_size - char_len - diff); return true; } -char* SettingsGetText(uint32_t index) +char* SettingsText(uint32_t index) { if (index >= SET_MAX) { return nullptr; // Setting not supported - internal error } char* position = Settings.ota_url; - for (;index > 0; index--) { - while (*position++ != '\0') { } + + if (Settings.version < 0x07010207) { + uint32_t idx = 0; + switch (index) { + case SET_MQTTPREFIX3: idx++; + case SET_MQTTPREFIX2: idx++; + case SET_MQTTPREFIX1: position = Settings.mqtt_prefix[idx]; break; + case SET_STASSID2: idx++; + case SET_STASSID1: position = Settings.sta_ssid[idx]; break; + case SET_STAPWD2: idx++; + case SET_STAPWD1: position = Settings.sta_pwd[idx]; break; + case SET_HOSTNAME: position = Settings.hostname; break; + case SET_SYSLOG_HOST: position = Settings.syslog_host; break; + case SET_WEBPWD: position = Settings.web_password; break; +#if defined(USE_MQTT_TLS) && defined(USE_MQTT_AWS_IOT) + case SET_MQTT_HOST: + strlcpy(aws_mqtt_host, Settings.mqtt_user, strlen(Settings.mqtt_user)); + strlcpy(&aws_mqtt_host[strlen(Settings.mqtt_user)], Settings.mqtt_host, sizeof(Settings.mqtt_host)); + position = aws_mqtt_host; break; + case SET_MQTT_USER: position = aws_mqtt_user; break; +#else + case SET_MQTT_HOST: position = Settings.mqtt_host; break; + case SET_MQTT_USER: position = Settings.mqtt_user; break; +#endif + case SET_MQTT_CLIENT: position = Settings.mqtt_client; break; + case SET_MQTT_PWD: position = Settings.mqtt_pwd; break; + case SET_MQTT_FULLTOPIC: position = Settings.mqtt_fulltopic; break; + case SET_MQTT_TOPIC: position = Settings.mqtt_topic; break; + case SET_MQTT_BUTTON_TOPIC: position = Settings.button_topic; break; + case SET_MQTT_SWITCH_TOPIC: position = Settings.switch_topic; break; + case SET_MQTT_GRP_TOPIC: position = Settings.mqtt_grptopic; break; + case SET_STATE_TXT4: idx++; + case SET_STATE_TXT3: idx++; + case SET_STATE_TXT2: idx++; + case SET_STATE_TXT1: position = Settings.state_text[idx]; break; + case SET_NTPSERVER3: idx++; + case SET_NTPSERVER2: idx++; + case SET_NTPSERVER1: position = Settings.ntp_server[idx]; break; + case SET_MEM5: idx++; + case SET_MEM4: idx++; + case SET_MEM3: idx++; + case SET_MEM2: idx++; + case SET_MEM1: position = Settings.mems[idx]; break; + case SET_CORS: position = Settings.cors_domain; break; + case SET_FRIENDLYNAME4: idx++; + case SET_FRIENDLYNAME3: idx++; + case SET_FRIENDLYNAME2: idx++; + case SET_FRIENDLYNAME1: position = Settings.friendlyname[idx]; break; + } + + } else { + for (;index > 0; index--) { + while (*position++ != '\0') { } + } } + return position; } @@ -751,11 +864,11 @@ void SettingsDefaultSet2(void) Settings.module = MODULE; ModuleDefault(WEMOS); // for (uint32_t i = 0; i < sizeof(Settings.my_gp); i++) { Settings.my_gp.io[i] = GPIO_NONE; } - strlcpy(Settings.friendlyname[0], FRIENDLY_NAME, sizeof(Settings.friendlyname[0])); - strlcpy(Settings.friendlyname[1], FRIENDLY_NAME"2", sizeof(Settings.friendlyname[1])); - strlcpy(Settings.friendlyname[2], FRIENDLY_NAME"3", sizeof(Settings.friendlyname[2])); - strlcpy(Settings.friendlyname[3], FRIENDLY_NAME"4", sizeof(Settings.friendlyname[3])); - strlcpy(Settings.ota_url, OTA_URL, sizeof(Settings.ota_url)); + SettingsUpdateText(SET_FRIENDLYNAME1, FRIENDLY_NAME); + SettingsUpdateText(SET_FRIENDLYNAME2, FRIENDLY_NAME"2"); + SettingsUpdateText(SET_FRIENDLYNAME3, FRIENDLY_NAME"3"); + SettingsUpdateText(SET_FRIENDLYNAME4, FRIENDLY_NAME"4"); + SettingsUpdateText(SET_OTAURL, OTA_URL); // Power Settings.flag.save_state = SAVE_STATE; @@ -783,14 +896,14 @@ void SettingsDefaultSet2(void) ParseIp(&Settings.ip_address[3], WIFI_DNS); Settings.sta_config = WIFI_CONFIG_TOOL; // Settings.sta_active = 0; - strlcpy(Settings.sta_ssid[0], STA_SSID1, sizeof(Settings.sta_ssid[0])); - strlcpy(Settings.sta_pwd[0], STA_PASS1, sizeof(Settings.sta_pwd[0])); - strlcpy(Settings.sta_ssid[1], STA_SSID2, sizeof(Settings.sta_ssid[1])); - strlcpy(Settings.sta_pwd[1], STA_PASS2, sizeof(Settings.sta_pwd[1])); - strlcpy(Settings.hostname, WIFI_HOSTNAME, sizeof(Settings.hostname)); + SettingsUpdateText(SET_STASSID1, STA_SSID1); + SettingsUpdateText(SET_STASSID2, STA_SSID2); + SettingsUpdateText(SET_STAPWD1, STA_PASS1); + SettingsUpdateText(SET_STAPWD2, STA_PASS2); + SettingsUpdateText(SET_HOSTNAME, WIFI_HOSTNAME); // Syslog - strlcpy(Settings.syslog_host, SYS_LOG_HOST, sizeof(Settings.syslog_host)); + SettingsUpdateText(SET_SYSLOG_HOST, SYS_LOG_HOST); Settings.syslog_port = SYS_LOG_PORT; Settings.syslog_level = SYS_LOG_LEVEL; @@ -798,9 +911,9 @@ void SettingsDefaultSet2(void) Settings.flag2.emulation = EMULATION; Settings.webserver = WEB_SERVER; Settings.weblog_level = WEB_LOG_LEVEL; - strlcpy(Settings.web_password, WEB_PASSWORD, sizeof(Settings.web_password)); + SettingsUpdateText(SET_WEBPWD, WEB_PASSWORD); Settings.flag3.mdns_enabled = MDNS_ENABLED; - strlcpy(Settings.cors_domain, CORS_DOMAIN, sizeof(Settings.cors_domain)); + SettingsUpdateText(SET_CORS, CORS_DOMAIN); // Button // Settings.flag.button_restrict = 0; @@ -823,24 +936,24 @@ void SettingsDefaultSet2(void) // Settings.flag.mqtt_offline = 0; // Settings.flag.mqtt_serial = 0; // Settings.flag.device_index_enable = 0; - strlcpy(Settings.mqtt_host, MQTT_HOST, sizeof(Settings.mqtt_host)); + SettingsUpdateText(SET_MQTT_HOST, MQTT_HOST); Settings.mqtt_port = MQTT_PORT; - strlcpy(Settings.mqtt_client, MQTT_CLIENT_ID, sizeof(Settings.mqtt_client)); - strlcpy(Settings.mqtt_user, MQTT_USER, sizeof(Settings.mqtt_user)); - strlcpy(Settings.mqtt_pwd, MQTT_PASS, sizeof(Settings.mqtt_pwd)); - strlcpy(Settings.mqtt_topic, MQTT_TOPIC, sizeof(Settings.mqtt_topic)); - strlcpy(Settings.button_topic, MQTT_BUTTON_TOPIC, sizeof(Settings.button_topic)); - strlcpy(Settings.switch_topic, MQTT_SWITCH_TOPIC, sizeof(Settings.switch_topic)); - strlcpy(Settings.mqtt_grptopic, MQTT_GRPTOPIC, sizeof(Settings.mqtt_grptopic)); - strlcpy(Settings.mqtt_fulltopic, MQTT_FULLTOPIC, sizeof(Settings.mqtt_fulltopic)); + SettingsUpdateText(SET_MQTT_CLIENT, MQTT_CLIENT_ID); + SettingsUpdateText(SET_MQTT_USER, MQTT_USER); + SettingsUpdateText(SET_MQTT_PWD, MQTT_PASS); + SettingsUpdateText(SET_MQTT_TOPIC, MQTT_TOPIC); + SettingsUpdateText(SET_MQTT_BUTTON_TOPIC, MQTT_BUTTON_TOPIC); + SettingsUpdateText(SET_MQTT_SWITCH_TOPIC, MQTT_SWITCH_TOPIC); + SettingsUpdateText(SET_MQTT_GRP_TOPIC, MQTT_GRPTOPIC); + SettingsUpdateText(SET_MQTT_FULLTOPIC, MQTT_FULLTOPIC); Settings.mqtt_retry = MQTT_RETRY_SECS; - strlcpy(Settings.mqtt_prefix[0], SUB_PREFIX, sizeof(Settings.mqtt_prefix[0])); - strlcpy(Settings.mqtt_prefix[1], PUB_PREFIX, sizeof(Settings.mqtt_prefix[1])); - strlcpy(Settings.mqtt_prefix[2], PUB_PREFIX2, sizeof(Settings.mqtt_prefix[2])); - strlcpy(Settings.state_text[0], MQTT_STATUS_OFF, sizeof(Settings.state_text[0])); - strlcpy(Settings.state_text[1], MQTT_STATUS_ON, sizeof(Settings.state_text[1])); - strlcpy(Settings.state_text[2], MQTT_CMND_TOGGLE, sizeof(Settings.state_text[2])); - strlcpy(Settings.state_text[3], MQTT_CMND_HOLD, sizeof(Settings.state_text[3])); + SettingsUpdateText(SET_MQTTPREFIX1, SUB_PREFIX); + SettingsUpdateText(SET_MQTTPREFIX2, PUB_PREFIX); + SettingsUpdateText(SET_MQTTPREFIX3, PUB_PREFIX2); + SettingsUpdateText(SET_STATE_TXT1, MQTT_STATUS_OFF); + SettingsUpdateText(SET_STATE_TXT2, MQTT_STATUS_ON); + SettingsUpdateText(SET_STATE_TXT3, MQTT_CMND_TOGGLE); + SettingsUpdateText(SET_STATE_TXT4, MQTT_CMND_HOLD); char fingerprint[60]; strlcpy(fingerprint, MQTT_FINGERPRINT1, sizeof(fingerprint)); char *p = fingerprint; @@ -992,15 +1105,11 @@ void SettingsDefaultSet2(void) Settings.timezone = APP_TIMEZONE / 60; Settings.timezone_minutes = abs(APP_TIMEZONE % 60); } - strlcpy(Settings.ntp_server[0], NTP_SERVER1, sizeof(Settings.ntp_server[0])); - strlcpy(Settings.ntp_server[1], NTP_SERVER2, sizeof(Settings.ntp_server[1])); - strlcpy(Settings.ntp_server[2], NTP_SERVER3, sizeof(Settings.ntp_server[2])); - for (uint32_t j = 0; j < 3; j++) { - for (uint32_t i = 0; i < strlen(Settings.ntp_server[j]); i++) { - if (Settings.ntp_server[j][i] == ',') { - Settings.ntp_server[j][i] = '.'; - } - } + SettingsUpdateText(SET_NTPSERVER1, NTP_SERVER1); + SettingsUpdateText(SET_NTPSERVER2, NTP_SERVER2); + SettingsUpdateText(SET_NTPSERVER3, NTP_SERVER3); + for (uint32_t i = 0; i < 3; i++) { + SettingsUpdateText(SET_NTPSERVER1 +i, ReplaceCommaWithDot(SettingsText(SET_NTPSERVER1 +i))); } Settings.latitude = (int)((double)LATITUDE * 1000000); Settings.longitude = (int)((double)LONGITUDE * 1000000); @@ -1278,6 +1387,33 @@ void SettingsDelta(void) memcpy((char*)&Settings.serial_config, (char*)&Settings.ex_serial_config, 5); // 1E4 -> EFE } + if ((VERSION < 0x7010207) && (Settings.version > VERSION)) { + char temp[strlen(SettingsText(SET_OTAURL)) +1]; strncpy(temp, SettingsText(SET_OTAURL), sizeof(temp)); + char temp21[strlen(SettingsText(SET_MQTTPREFIX1)) +1]; strncpy(temp21, SettingsText(SET_MQTTPREFIX1), sizeof(temp21)); + char temp22[strlen(SettingsText(SET_MQTTPREFIX2)) +1]; strncpy(temp22, SettingsText(SET_MQTTPREFIX2), sizeof(temp22)); + char temp23[strlen(SettingsText(SET_MQTTPREFIX3)) +1]; strncpy(temp23, SettingsText(SET_MQTTPREFIX3), sizeof(temp23)); + char temp31[strlen(SettingsText(SET_STASSID1)) +1]; strncpy(temp31, SettingsText(SET_STASSID1), sizeof(temp31)); + char temp32[strlen(SettingsText(SET_STASSID2)) +1]; strncpy(temp32, SettingsText(SET_STASSID2), sizeof(temp32)); + char temp41[strlen(SettingsText(SET_STAPWD1)) +1]; strncpy(temp41, SettingsText(SET_STAPWD1), sizeof(temp41)); + char temp42[strlen(SettingsText(SET_STAPWD2)) +1]; strncpy(temp42, SettingsText(SET_STAPWD2), sizeof(temp42)); + char temp5[strlen(SettingsText(SET_HOSTNAME)) +1]; strncpy(temp5, SettingsText(SET_HOSTNAME), sizeof(temp5)); + char temp6[strlen(SettingsText(SET_SYSLOG_HOST)) +1]; strncpy(temp6, SettingsText(SET_SYSLOG_HOST), sizeof(temp5)); + + uint32_t version = Settings.version; + Settings.version = VERSION; + SettingsUpdateText(SET_OTAURL, temp); + SettingsUpdateText(SET_MQTTPREFIX1, temp21); + SettingsUpdateText(SET_MQTTPREFIX2, temp22); + SettingsUpdateText(SET_MQTTPREFIX3, temp23); + SettingsUpdateText(SET_STASSID1, temp31); + SettingsUpdateText(SET_STASSID2, temp32); + SettingsUpdateText(SET_STAPWD1, temp41); + SettingsUpdateText(SET_STAPWD2, temp42); + SettingsUpdateText(SET_HOSTNAME, temp5); + SettingsUpdateText(SET_SYSLOG_HOST, temp6); + Settings.version = version; + } + Settings.version = VERSION; SettingsSave(1); } diff --git a/tasmota/support.ino b/tasmota/support.ino index 3ecc9b179..87f05ad27 100644 --- a/tasmota/support.ino +++ b/tasmota/support.ino @@ -328,6 +328,22 @@ char* RemoveSpace(char* p) return p; } +char* ReplaceCommaWithDot(char* p) +{ + char* write = (char*)p; + char* read = (char*)p; + char ch = '.'; + + while (ch != '\0') { + ch = *read++; + if (ch == ',') { + ch = '.'; + } + *write++ = ch; + } + return p; +} + char* LowerCase(char* dest, const char* source) { char* write = dest; @@ -1571,10 +1587,10 @@ void Syslog(void) { // Destroys log_data - uint32_t current_hash = GetHash(Settings.syslog_host, strlen(Settings.syslog_host)); + uint32_t current_hash = GetHash(SettingsText(SET_SYSLOG_HOST), strlen(SettingsText(SET_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 + WiFi.hostByName(SettingsText(SET_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)) { char syslog_preamble[64]; // Hostname + Id diff --git a/tasmota/support_command.ino b/tasmota/support_command.ino index ec8de9126..b8fbd4a7a 100644 --- a/tasmota/support_command.ino +++ b/tasmota/support_command.ino @@ -147,7 +147,7 @@ void CommandHandler(char* topicBuf, char* dataBuf, uint32_t data_len) data_len--; } - bool grpflg = (strstr(topicBuf, Settings.mqtt_grptopic) != nullptr); + bool grpflg = (strstr(topicBuf, SettingsText(SET_MQTT_GRP_TOPIC)) != nullptr); char stemp1[TOPSZ]; GetFallbackTopic_P(stemp1, ""); // Full Fallback topic = cmnd/DVES_xxxxxxxx_fb/ @@ -325,11 +325,11 @@ void CmndStatus(void) uint32_t payload = ((XdrvMailbox.payload < 0) || (XdrvMailbox.payload > MAX_STATUS)) ? 99 : XdrvMailbox.payload; uint32_t option = STAT; - char stemp[MAX_FRIENDLYNAMES * (sizeof(Settings.friendlyname[0]) +MAX_FRIENDLYNAMES)]; + char stemp[200]; char stemp2[100]; // Workaround MQTT - TCP/IP stack queueing when SUB_PREFIX = PUB_PREFIX - if (!strcmp(Settings.mqtt_prefix[0],Settings.mqtt_prefix[1]) && (!payload)) { option++; } // TELE + if (!strcmp(SettingsText(SET_MQTTPREFIX1), SettingsText(SET_MQTTPREFIX2)) && (!payload)) { option++; } // TELE if ((!Settings.flag.mqtt_enabled) && (6 == payload)) { payload = 99; } // SetOption3 - Enable MQTT if (!energy_flg && (9 == payload)) { payload = 99; } @@ -344,7 +344,7 @@ void CmndStatus(void) #endif // USE_SONOFF_IFAN stemp[0] = '\0'; for (uint32_t i = 0; i < maxfn; i++) { - snprintf_P(stemp, sizeof(stemp), PSTR("%s%s\"%s\"" ), stemp, (i > 0 ? "," : ""), Settings.friendlyname[i]); + snprintf_P(stemp, sizeof(stemp), PSTR("%s%s\"%s\"" ), stemp, (i > 0 ? "," : ""), SettingsText(SET_FRIENDLYNAME1 +i)); } stemp2[0] = '\0'; for (uint32_t i = 0; i < MAX_SWITCHES; i++) { @@ -355,10 +355,10 @@ void CmndStatus(void) 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, + SettingsText(SET_MQTT_BUTTON_TOPIC), power, Settings.poweronstate, Settings.ledstate, Settings.ledmask, Settings.save_data, Settings.flag.save_state, // SetOption0 - Save power state and use after restart - Settings.switch_topic, + SettingsText(SET_MQTT_SWITCH_TOPIC), stemp2, Settings.flag.mqtt_button_retain, // CMND_BUTTONRETAIN Settings.flag.mqtt_switch_retain, // CMND_SWITCHRETAIN @@ -371,7 +371,7 @@ void CmndStatus(void) 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, + baudrate, SettingsText(SET_MQTT_GRP_TOPIC), SettingsText(SET_OTAURL), 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")); @@ -394,7 +394,7 @@ void CmndStatus(void) 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\",\"%08X\"]}}"), Settings.seriallog_level, Settings.weblog_level, Settings.mqttlog_level, Settings.syslog_level, - Settings.syslog_host, Settings.syslog_port, Settings.sta_ssid[0], Settings.sta_ssid[1], Settings.tele_period, + SettingsText(SET_SYSLOG_HOST), Settings.syslog_port, SettingsText(SET_STASSID1), SettingsText(SET_STASSID2), Settings.tele_period, Settings.flag2.data, Settings.flag.data, ToHex_P((unsigned char*)Settings.param, PARAM8_SIZE, stemp2, sizeof(stemp2)), Settings.flag3.data, Settings.flag4.data); MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS "3")); @@ -425,17 +425,10 @@ void CmndStatus(void) } if (((0 == payload) || (6 == payload)) && Settings.flag.mqtt_enabled) { // SetOption3 - Enable MQTT -#ifdef USE_MQTT_AWS_IOT - Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS6_MQTT "\":{\"" D_CMND_MQTTHOST "\":\"%s%s\",\"" D_CMND_MQTTPORT "\":%d,\"" D_CMND_MQTTCLIENT D_JSON_MASK "\":\"%s\",\"" - D_CMND_MQTTCLIENT "\":\"%s\",\"" D_JSON_MQTT_COUNT "\":%d,\"MAX_PACKET_SIZE\":%d,\"KEEPALIVE\":%d}}"), - Settings.mqtt_user, Settings.mqtt_host, Settings.mqtt_port, Settings.mqtt_client, - mqtt_client, MqttConnectCount(), MQTT_MAX_PACKET_SIZE, MQTT_KEEPALIVE); -#else 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); -#endif + SettingsText(SET_MQTT_HOST), Settings.mqtt_port, SettingsText(SET_MQTT_CLIENT), + mqtt_client, SettingsText(SET_MQTT_USER), MqttConnectCount(), MQTT_MAX_PACKET_SIZE, MQTT_KEEPALIVE); MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS "6")); } @@ -557,10 +550,10 @@ void CmndUpgrade(void) void CmndOtaUrl(void) { - if ((XdrvMailbox.data_len > 0) && (XdrvMailbox.data_len < sizeof(Settings.ota_url))) { - strlcpy(Settings.ota_url, (SC_DEFAULT == Shortcut()) ? OTA_URL : XdrvMailbox.data, sizeof(Settings.ota_url)); + if (XdrvMailbox.data_len > 0) { + SettingsUpdateText(SET_OTAURL, (SC_DEFAULT == Shortcut()) ? OTA_URL : XdrvMailbox.data); } - ResponseCmndChar(Settings.ota_url); + ResponseCmndChar(SettingsText(SET_OTAURL)); } void CmndSeriallog(void) @@ -1193,10 +1186,10 @@ void CmndSyslog(void) void CmndLoghost(void) { - if ((XdrvMailbox.data_len > 0) && (XdrvMailbox.data_len < sizeof(Settings.syslog_host))) { - strlcpy(Settings.syslog_host, (SC_DEFAULT == Shortcut()) ? SYS_LOG_HOST : XdrvMailbox.data, sizeof(Settings.syslog_host)); + if (XdrvMailbox.data_len > 0) { + SettingsUpdateText(SET_SYSLOG_HOST, (SC_DEFAULT == Shortcut()) ? SYS_LOG_HOST : XdrvMailbox.data); } - ResponseCmndChar(Settings.syslog_host); + ResponseCmndChar(SettingsText(SET_SYSLOG_HOST)); } void CmndLogport(void) @@ -1224,17 +1217,15 @@ void CmndIpAddress(void) void CmndNtpServer(void) { if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= 3)) { - if ((XdrvMailbox.data_len > 0) && (XdrvMailbox.data_len < sizeof(Settings.ntp_server[0]))) { - strlcpy(Settings.ntp_server[XdrvMailbox.index -1], - (SC_CLEAR == Shortcut()) ? "" : (SC_DEFAULT == Shortcut()) ? (1==XdrvMailbox.index)?NTP_SERVER1:(2==XdrvMailbox.index)?NTP_SERVER2:NTP_SERVER3 : XdrvMailbox.data, - sizeof(Settings.ntp_server[0])); - for (uint32_t i = 0; i < strlen(Settings.ntp_server[XdrvMailbox.index -1]); i++) { - if (Settings.ntp_server[XdrvMailbox.index -1][i] == ',') Settings.ntp_server[XdrvMailbox.index -1][i] = '.'; - } + uint32_t ntp_server = SET_NTPSERVER1 + XdrvMailbox.index -1; + if (XdrvMailbox.data_len > 0) { + SettingsUpdateText(ntp_server, + (SC_CLEAR == Shortcut()) ? "" : (SC_DEFAULT == Shortcut()) ? (1 == XdrvMailbox.index) ? NTP_SERVER1 : (2 == XdrvMailbox.index) ? NTP_SERVER2 : NTP_SERVER3 : XdrvMailbox.data); + SettingsUpdateText(ntp_server, ReplaceCommaWithDot(SettingsText(ntp_server))); // restart_flag = 2; // Issue #3890 ntp_force_sync = true; } - ResponseCmndIdxChar(Settings.ntp_server[XdrvMailbox.index -1]); + ResponseCmndIdxChar(SettingsText(ntp_server)); } } @@ -1251,33 +1242,31 @@ void CmndAp(void) } restart_flag = 2; } - Response_P(S_JSON_COMMAND_NVALUE_SVALUE, XdrvMailbox.command, Settings.sta_active +1, Settings.sta_ssid[Settings.sta_active]); + Response_P(S_JSON_COMMAND_NVALUE_SVALUE, XdrvMailbox.command, Settings.sta_active +1, SettingsText(SET_STASSID1 + Settings.sta_active)); } void CmndSsid(void) { if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= 2)) { - if ((XdrvMailbox.data_len > 0) && (XdrvMailbox.data_len < sizeof(Settings.sta_ssid[0]))) { - strlcpy(Settings.sta_ssid[XdrvMailbox.index -1], - (SC_CLEAR == Shortcut()) ? "" : (SC_DEFAULT == Shortcut()) ? (1 == XdrvMailbox.index) ? STA_SSID1 : STA_SSID2 : XdrvMailbox.data, - sizeof(Settings.sta_ssid[0])); + if (XdrvMailbox.data_len > 0) { + SettingsUpdateText(SET_STASSID1 + XdrvMailbox.index -1, + (SC_CLEAR == Shortcut()) ? "" : (SC_DEFAULT == Shortcut()) ? (1 == XdrvMailbox.index) ? STA_SSID1 : STA_SSID2 : XdrvMailbox.data); Settings.sta_active = XdrvMailbox.index -1; restart_flag = 2; } - ResponseCmndIdxChar(Settings.sta_ssid[XdrvMailbox.index -1]); + ResponseCmndIdxChar(SettingsText(SET_STASSID1 + XdrvMailbox.index -1)); } } void CmndPassword(void) { if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= 2)) { - if ((XdrvMailbox.data_len > 4 || SC_CLEAR == Shortcut() || SC_DEFAULT == Shortcut()) && (XdrvMailbox.data_len < sizeof(Settings.sta_pwd[0]))) { - strlcpy(Settings.sta_pwd[XdrvMailbox.index -1], - (SC_CLEAR == Shortcut()) ? "" : (SC_DEFAULT == Shortcut()) ? (1 == XdrvMailbox.index) ? STA_PASS1 : STA_PASS2 : XdrvMailbox.data, - sizeof(Settings.sta_pwd[0])); + if ((XdrvMailbox.data_len > 4) || (SC_CLEAR == Shortcut()) || (SC_DEFAULT == Shortcut())) { + SettingsUpdateText(SET_STAPWD1 + XdrvMailbox.index -1, + (SC_CLEAR == Shortcut()) ? "" : (SC_DEFAULT == Shortcut()) ? (1 == XdrvMailbox.index) ? STA_PASS1 : STA_PASS2 : XdrvMailbox.data); Settings.sta_active = XdrvMailbox.index -1; restart_flag = 2; - ResponseCmndIdxChar(Settings.sta_pwd[XdrvMailbox.index -1]); + ResponseCmndIdxChar(SettingsText(SET_STAPWD1 + XdrvMailbox.index -1)); } else { Response_P(S_JSON_COMMAND_INDEX_ASTERISK, XdrvMailbox.command, XdrvMailbox.index); } @@ -1286,14 +1275,14 @@ void CmndPassword(void) void CmndHostname(void) { - if (!XdrvMailbox.grpflg && (XdrvMailbox.data_len > 0) && (XdrvMailbox.data_len < sizeof(Settings.hostname))) { - strlcpy(Settings.hostname, (SC_DEFAULT == Shortcut()) ? WIFI_HOSTNAME : XdrvMailbox.data, sizeof(Settings.hostname)); - if (strstr(Settings.hostname, "%") != nullptr) { - strlcpy(Settings.hostname, WIFI_HOSTNAME, sizeof(Settings.hostname)); + if (!XdrvMailbox.grpflg && (XdrvMailbox.data_len > 0)) { + SettingsUpdateText(SET_HOSTNAME, (SC_DEFAULT == Shortcut()) ? WIFI_HOSTNAME : XdrvMailbox.data); + if (strstr(SettingsText(SET_HOSTNAME), "%") != nullptr) { + SettingsUpdateText(SET_HOSTNAME, WIFI_HOSTNAME); } restart_flag = 2; } - ResponseCmndChar(Settings.hostname); + ResponseCmndChar(SettingsText(SET_HOSTNAME)); } void CmndWifiConfig(void) @@ -1315,16 +1304,16 @@ void CmndWifiConfig(void) void CmndFriendlyname(void) { if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_FRIENDLYNAMES)) { - if ((XdrvMailbox.data_len > 0) && (XdrvMailbox.data_len < sizeof(Settings.friendlyname[0]))) { + if (XdrvMailbox.data_len > 0) { char stemp1[TOPSZ]; if (1 == XdrvMailbox.index) { snprintf_P(stemp1, sizeof(stemp1), PSTR(FRIENDLY_NAME)); } else { snprintf_P(stemp1, sizeof(stemp1), PSTR(FRIENDLY_NAME "%d"), XdrvMailbox.index); } - strlcpy(Settings.friendlyname[XdrvMailbox.index -1], (SC_DEFAULT == Shortcut()) ? stemp1 : XdrvMailbox.data, sizeof(Settings.friendlyname[XdrvMailbox.index -1])); + SettingsUpdateText(SET_FRIENDLYNAME1 + XdrvMailbox.index -1, (SC_DEFAULT == Shortcut()) ? stemp1 : XdrvMailbox.data); } - ResponseCmndIdxChar(Settings.friendlyname[XdrvMailbox.index -1]); + ResponseCmndIdxChar(SettingsText(SET_FRIENDLYNAME1 + XdrvMailbox.index -1)); } } diff --git a/tasmota/support_rtc.ino b/tasmota/support_rtc.ino index 27ef66ec7..ffaf171a3 100644 --- a/tasmota/support_rtc.ino +++ b/tasmota/support_rtc.ino @@ -469,9 +469,9 @@ void RtcSetTime(uint32_t epoch) void RtcInit(void) { - sntp_setservername(0, Settings.ntp_server[0]); - sntp_setservername(1, Settings.ntp_server[1]); - sntp_setservername(2, Settings.ntp_server[2]); + sntp_setservername(0, SettingsText(SET_NTPSERVER1)); + sntp_setservername(1, SettingsText(SET_NTPSERVER2)); + sntp_setservername(2, SettingsText(SET_NTPSERVER3)); sntp_stop(); sntp_set_timezone(0); // UTC time sntp_init(); diff --git a/tasmota/support_statistics.ino b/tasmota/support_statistics.ino index a34c7250a..1907b1edc 100644 --- a/tasmota/support_statistics.ino +++ b/tasmota/support_statistics.ino @@ -24,53 +24,10 @@ * Gather statistics \*********************************************************************************************/ -struct STATS { -// uint32_t str_size = 1151; // Total number of characters reserved as char array in Settings - uint32_t str_len = 0; // Total number of characters used within char array -} Stats; - -void StatisticsChar(const char* text) -{ - uint32_t len = strlen(text); - Stats.str_len += len; -} - String GetStatistics(void) { - Stats.str_len = 0; - - for (uint32_t i = 0; i < 2; i++) { - StatisticsChar(Settings.sta_ssid[i]); - StatisticsChar(Settings.sta_pwd[i]); - } - for (uint32_t i = 0; i < 3; i++) { - StatisticsChar(Settings.mqtt_prefix[i]); - StatisticsChar(Settings.ntp_server[i]); - } - for (uint32_t i = 0; i < 4; i++) { - StatisticsChar(Settings.state_text[i]); - StatisticsChar(Settings.friendlyname[i]); - } - for (uint32_t i = 0; i < MAX_RULE_MEMS; i++) { - StatisticsChar(Settings.mems[i]); - } - StatisticsChar(Settings.ota_url); - StatisticsChar(Settings.hostname); - StatisticsChar(Settings.syslog_host); - StatisticsChar(Settings.mqtt_host); - StatisticsChar(Settings.mqtt_client); - StatisticsChar(Settings.mqtt_user); - StatisticsChar(Settings.mqtt_pwd); - StatisticsChar(Settings.mqtt_topic); - StatisticsChar(Settings.button_topic); - StatisticsChar(Settings.switch_topic); - StatisticsChar(Settings.mqtt_grptopic); - StatisticsChar(Settings.web_password); - StatisticsChar(Settings.mqtt_fulltopic); - StatisticsChar(Settings.cors_domain); - char data[40]; - snprintf_P(data, sizeof(data), PSTR(",\"CR\":\"%d/1151\""), Stats.str_len); // Char Usage Ratio + snprintf_P(data, sizeof(data), PSTR(",\"CR\":\"%d/%d\""), GetSettingsTextLen(), settings_text_size); // Char Usage Ratio return String(data); } diff --git a/tasmota/support_tasmota.ino b/tasmota/support_tasmota.ino index 0f930e06e..ff2b6f40a 100644 --- a/tasmota/support_tasmota.ino +++ b/tasmota/support_tasmota.ino @@ -60,15 +60,16 @@ char* Format(char* output, const char* input, int size) char* GetOtaUrl(char *otaurl, size_t otaurl_size) { - if (strstr(Settings.ota_url, "%04d") != nullptr) { // OTA url contains placeholder for chip ID - snprintf(otaurl, otaurl_size, Settings.ota_url, ESP.getChipId() & 0x1fff); + if (strstr(SettingsText(SET_OTAURL), "%04d") != nullptr) { // OTA url contains placeholder for chip ID + snprintf(otaurl, otaurl_size, SettingsText(SET_OTAURL), ESP.getChipId() & 0x1fff); } - 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 if (strstr(SettingsText(SET_OTAURL), "%d") != nullptr) { // OTA url contains placeholder for chip ID + snprintf_P(otaurl, otaurl_size, SettingsText(SET_OTAURL), ESP.getChipId()); } else { - strlcpy(otaurl, Settings.ota_url, otaurl_size); + strlcpy(otaurl, SettingsText(SET_OTAURL), otaurl_size); } + return otaurl; } @@ -101,17 +102,19 @@ char* GetTopic_P(char *stopic, uint32_t prefix, char *topic, const char* subtopi fulltopic += topic; // cmnd/ } } else { - fulltopic = Settings.mqtt_fulltopic; + fulltopic = SettingsText(SET_MQTT_FULLTOPIC); 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 (uint32_t i = 0; i < 3; i++) { - if ('\0' == Settings.mqtt_prefix[i][0]) { - GetTextIndexed(Settings.mqtt_prefix[i], sizeof(Settings.mqtt_prefix[i]), i, kPrefixes); + if ('\0' == SettingsText(SET_MQTTPREFIX1 + i)) { + char temp[TOPSZ]; + SettingsUpdateText(SET_MQTTPREFIX1 + i, GetTextIndexed(temp, sizeof(temp), i, kPrefixes)); } } - fulltopic.replace(FPSTR(MQTT_TOKEN_PREFIX), Settings.mqtt_prefix[prefix]); + fulltopic.replace(FPSTR(MQTT_TOKEN_PREFIX), SettingsText(SET_MQTTPREFIX1 + prefix)); + fulltopic.replace(FPSTR(MQTT_TOKEN_TOPIC), topic); fulltopic.replace(F("%hostname%"), my_hostname); String token_id = WiFi.macAddress(); @@ -131,7 +134,7 @@ char* GetGroupTopic_P(char *stopic, const char* subtopic) { // SetOption75 0: %prefix%/nothing/%topic% = cmnd/nothing//# // SetOption75 1: cmnd/ - return GetTopic_P(stopic, (Settings.flag3.grouptopic_mode) ? CMND +8 : CMND, Settings.mqtt_grptopic, subtopic); // SetOption75 - GroupTopic replaces %topic% (0) or fixed topic cmnd/grouptopic (1) + return GetTopic_P(stopic, (Settings.flag3.grouptopic_mode) ? CMND +8 : CMND, SettingsText(SET_MQTT_GRP_TOPIC), subtopic); // SetOption75 - GroupTopic replaces %topic% (0) or fixed topic cmnd/grouptopic (1) } char* GetFallbackTopic_P(char *stopic, const char* subtopic) @@ -144,7 +147,7 @@ char* GetStateText(uint32_t state) if (state > 3) { state = 1; } - return Settings.state_text[state]; + return SettingsText(SET_STATE_TXT1 + state); } /********************************************************************************************/ @@ -358,10 +361,10 @@ bool SendKey(uint32_t key, uint32_t device, uint32_t state) char stopic[TOPSZ]; char scommand[CMDSZ]; - char key_topic[sizeof(Settings.button_topic)]; + char key_topic[TOPSZ]; bool result = false; - char *tmp = (key) ? Settings.switch_topic : Settings.button_topic; + char *tmp = (key) ? SettingsText(SET_MQTT_SWITCH_TOPIC) : SettingsText(SET_MQTT_BUTTON_TOPIC); Format(key_topic, tmp, sizeof(key_topic)); if (Settings.flag.mqtt_enabled && MqttIsConnected() && (strlen(key_topic) != 0) && strcmp(key_topic, "0")) { // SetOption3 - Enable MQTT if (!key && (device > devices_present)) { @@ -374,7 +377,7 @@ bool SendKey(uint32_t key, uint32_t device, uint32_t state) } else { if ((Settings.flag3.button_switch_force_local || // SetOption61 - Force local operation when button/switch topic is set !strcmp(mqtt_topic, key_topic) || - !strcmp(Settings.mqtt_grptopic, key_topic)) && + !strcmp(SettingsText(SET_MQTT_GRP_TOPIC), key_topic)) && (POWER_TOGGLE == state)) { state = ~(power >> (device -1)) &1; // POWER_OFF or POWER_ON } @@ -584,7 +587,7 @@ void MqttShowState(void) } 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_SIGNAL "\":%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(), + Settings.sta_active +1, SettingsText(SET_STASSID1 + Settings.sta_active), WiFi.BSSIDstr().c_str(), WiFi.channel(), WifiGetRssiAsQuality(WiFi.RSSI()), WiFi.RSSI(), WifiLinkCount(), WifiDowntime().c_str()); } @@ -813,7 +816,6 @@ void Every250mSeconds(void) if (ota_state_flag && BACKLOG_EMPTY) { ota_state_flag--; if (2 == ota_state_flag) { - ota_url = Settings.ota_url; RtcSettings.ota_loader = 0; // Try requested image first ota_retry_counter = OTA_ATTEMPTS; ESPhttpUpdate.rebootOnUpdate(false); @@ -839,7 +841,7 @@ void Every250mSeconds(void) if (!pch) { pch = ech; } if (pch) { mqtt_data[pch - mqtt_data] = '\0'; - char *ech = strrchr(Settings.ota_url, '.'); // Change from filename.bin into filename-minimal.bin + char *ech = strrchr(SettingsText(SET_OTAURL), '.'); // Change from filename.bin into filename-minimal.bin snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s-" D_JSON_MINIMAL "%s"), mqtt_data, ech); // Minimal filename must be filename-minimal } } @@ -903,30 +905,45 @@ void Every250mSeconds(void) } if (restart_flag && BACKLOG_EMPTY) { if ((214 == restart_flag) || (215 == restart_flag) || (216 == restart_flag)) { - char storage_wifi[sizeof(Settings.sta_ssid) + - sizeof(Settings.sta_pwd)]; + // Backup current SSIDs and Passwords + char storage_ssid1[strlen(SettingsText(SET_STASSID1)) +1]; + strncpy(storage_ssid1, SettingsText(SET_STASSID1), sizeof(storage_ssid1)); + char storage_ssid2[strlen(SettingsText(SET_STASSID2)) +1]; + strncpy(storage_ssid2, SettingsText(SET_STASSID2), sizeof(storage_ssid2)); + char storage_pass1[strlen(SettingsText(SET_STAPWD1)) +1]; + strncpy(storage_pass1, SettingsText(SET_STAPWD1), sizeof(storage_pass1)); + char storage_pass2[strlen(SettingsText(SET_STAPWD2)) +1]; + strncpy(storage_pass2, SettingsText(SET_STAPWD2), sizeof(storage_pass2)); - char storage_mqtt[sizeof(Settings.mqtt_host) + - sizeof(Settings.mqtt_port) + - sizeof(Settings.mqtt_client) + - sizeof(Settings.mqtt_user) + - sizeof(Settings.mqtt_pwd) + - sizeof(Settings.mqtt_topic)]; - uint16_t mqtt_port = Settings.mqtt_port; // Workaround 7.1.2.6 Settings shuffle + char storage_mqtthost[strlen(SettingsText(SET_MQTT_HOST)) +1]; + strncpy(storage_mqtthost, SettingsText(SET_MQTT_HOST), sizeof(storage_mqtthost)); + char storage_mqttuser[strlen(SettingsText(SET_MQTT_USER)) +1]; + strncpy(storage_mqttuser, SettingsText(SET_MQTT_USER), sizeof(storage_mqttuser)); + char storage_mqttpwd[strlen(SettingsText(SET_MQTT_PWD)) +1]; + strncpy(storage_mqttpwd, SettingsText(SET_MQTT_PWD), sizeof(storage_mqttpwd)); + char storage_mqtttopic[strlen(SettingsText(SET_MQTT_TOPIC)) +1]; + strncpy(storage_mqtttopic, SettingsText(SET_MQTT_TOPIC), sizeof(storage_mqtttopic)); + uint16_t mqtt_port = Settings.mqtt_port; - memcpy(storage_wifi, Settings.sta_ssid, sizeof(storage_wifi)); // Backup current SSIDs and Passwords - if (216 == restart_flag) { - memcpy(storage_mqtt, Settings.mqtt_host, sizeof(storage_mqtt)); // Backup mqtt host, port, client, username and password - } +// if (216 == restart_flag) { + // Backup mqtt host, port, client, username and password +// } if ((215 == restart_flag) || (216 == restart_flag)) { SettingsErase(0); // Erase all flash from program end to end of physical flash } SettingsDefault(); - memcpy(Settings.sta_ssid, storage_wifi, sizeof(storage_wifi)); // Restore current SSIDs and Passwords + // Restore current SSIDs and Passwords + SettingsUpdateText(SET_STASSID1, storage_ssid1); + SettingsUpdateText(SET_STASSID2, storage_ssid2); + SettingsUpdateText(SET_STAPWD1, storage_pass1); + SettingsUpdateText(SET_STAPWD2, storage_pass2); if (216 == restart_flag) { - memcpy(Settings.mqtt_host, storage_mqtt, sizeof(storage_mqtt)); // Restore the mqtt host, port, client, username and password - strlcpy(Settings.mqtt_client, MQTT_CLIENT_ID, sizeof(Settings.mqtt_client)); // Set client to default - Settings.mqtt_port = mqtt_port; // Workaround 7.1.2.6 Settings shuffle + // Restore the mqtt host, port, client, username and password + SettingsUpdateText(SET_MQTT_HOST, storage_mqtthost); + SettingsUpdateText(SET_MQTT_USER, storage_mqttuser); + SettingsUpdateText(SET_MQTT_PWD, storage_mqttpwd); + SettingsUpdateText(SET_MQTT_TOPIC, storage_mqtttopic); + Settings.mqtt_port = mqtt_port; } restart_flag = 2; } @@ -977,7 +994,7 @@ void ArduinoOTAInit(void) { ArduinoOTA.setPort(8266); ArduinoOTA.setHostname(my_hostname); - if (Settings.web_password[0] !=0) { ArduinoOTA.setPassword(Settings.web_password); } + if (strlen(SettingsText(SET_WEBPWD))) { ArduinoOTA.setPassword(SettingsText(SET_WEBPWD)); } ArduinoOTA.onStart([]() { diff --git a/tasmota/support_wifi.ino b/tasmota/support_wifi.ino index 3f9e7795b..86cc81dd8 100644 --- a/tasmota/support_wifi.ino +++ b/tasmota/support_wifi.ino @@ -191,18 +191,20 @@ void WifiBegin(uint8_t flag, uint8_t channel) case 2: // Toggle Settings.sta_active ^= 1; } // 3: Current AP - if ('\0' == Settings.sta_ssid[Settings.sta_active][0]) { Settings.sta_active ^= 1; } // Skip empty SSID + if ('\0' == SettingsText(SET_STASSID1 + Settings.sta_active)) { + Settings.sta_active ^= 1; // Skip empty SSID + } if (Settings.ip_address[0]) { WiFi.config(Settings.ip_address[0], Settings.ip_address[1], Settings.ip_address[2], Settings.ip_address[3]); // Set static IP } WiFi.hostname(my_hostname); if (channel) { - WiFi.begin(Settings.sta_ssid[Settings.sta_active], Settings.sta_pwd[Settings.sta_active], channel, Wifi.bssid); + WiFi.begin(SettingsText(SET_STASSID1 + Settings.sta_active), SettingsText(SET_STAPWD1 + Settings.sta_active), channel, Wifi.bssid); } else { - WiFi.begin(Settings.sta_ssid[Settings.sta_active], Settings.sta_pwd[Settings.sta_active]); + WiFi.begin(SettingsText(SET_STASSID1 + Settings.sta_active), SettingsText(SET_STAPWD1 + Settings.sta_active)); } AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_WIFI D_CONNECTING_TO_AP "%d %s " D_IN_MODE " 11%c " D_AS " %s..."), - Settings.sta_active +1, Settings.sta_ssid[Settings.sta_active], kWifiPhyMode[WiFi.getPhyMode() & 0x3], my_hostname); + Settings.sta_active +1, SettingsText(SET_STASSID1 + Settings.sta_active), kWifiPhyMode[WiFi.getPhyMode() & 0x3], my_hostname); #if LWIP_IPV6 for (bool configured = false; !configured;) { @@ -278,10 +280,10 @@ void WifiBeginAfterScan(void) bool known = false; uint32_t j; for (j = 0; j < 2; j++) { - if (ssid_scan == Settings.sta_ssid[j]) { // SSID match + if (ssid_scan == SettingsText(SET_STASSID1 + j)) { // SSID match known = true; if (rssi_scan > best_network_db) { // Best network - if (sec_scan == ENC_TYPE_NONE || Settings.sta_pwd[j]) { // Check for passphrase if not open wlan + if (sec_scan == ENC_TYPE_NONE || SettingsText(SET_STAPWD1 + j)) { // Check for passphrase if not open wlan best_network_db = (int8_t)rssi_scan; channel = chan_scan; ap = j; // AP1 or AP2 @@ -434,7 +436,7 @@ void WifiCheckIp(void) if (!Wifi.retry || ((Wifi.retry_init / 2) == Wifi.retry)) { AddLog_P(LOG_LEVEL_INFO, S_LOG_WIFI, PSTR(D_CONNECT_FAILED_AP_TIMEOUT)); } else { - if (('\0' == Settings.sta_ssid[0][0]) && ('\0' == Settings.sta_ssid[1][0])) { + if (('\0' == SettingsText(SET_STASSID1)) && ('\0' == SettingsText(SET_STASSID2))) { wifi_config_tool = WIFI_MANAGER; // Skip empty SSIDs and start Wifi config tool Wifi.retry = 0; } else { @@ -480,13 +482,13 @@ void WifiCheck(uint8_t param) if (Wifi.config_counter) { if (!Wifi.config_counter) { if (strlen(WiFi.SSID().c_str())) { - strlcpy(Settings.sta_ssid[0], WiFi.SSID().c_str(), sizeof(Settings.sta_ssid[0])); + SettingsUpdateText(SET_STASSID1, WiFi.SSID().c_str()); } if (strlen(WiFi.psk().c_str())) { - strlcpy(Settings.sta_pwd[0], WiFi.psk().c_str(), sizeof(Settings.sta_pwd[0])); + SettingsUpdateText(SET_STAPWD1, WiFi.psk().c_str()); } Settings.sta_active = 0; - AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_WIFI D_WCFG_2_WIFIMANAGER D_CMND_SSID "1 %s"), Settings.sta_ssid[0]); + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_WIFI D_WCFG_2_WIFIMANAGER D_CMND_SSID "1 %s"), SettingsText(SET_STASSID1)); } } if (!Wifi.config_counter) { diff --git a/tasmota/tasmota.ino b/tasmota/tasmota.ino index 306250549..3b9e64a25 100644 --- a/tasmota/tasmota.ino +++ b/tasmota/tasmota.ino @@ -110,7 +110,6 @@ uint32_t web_log_index = 1; // Index in Web log buffer (should n float global_temperature = 9999; // Provide a global temperature to be used by some sensors float global_humidity = 0; // Provide a global humidity to be used by some sensors float global_pressure = 0; // Provide a global pressure to be used by some sensors -char *ota_url; // OTA url string pointer uint16_t tele_period = 9999; // Tele period timer uint16_t mqtt_cmnd_publish = 0; // ignore flag for publish command uint16_t blink_counter = 0; // Number of blink cycles @@ -265,13 +264,13 @@ 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, "%") != nullptr) { - strlcpy(Settings.hostname, WIFI_HOSTNAME, sizeof(Settings.hostname)); - snprintf_P(my_hostname, sizeof(my_hostname)-1, Settings.hostname, mqtt_topic, ESP.getChipId() & 0x1FFF); + Format(mqtt_client, SettingsText(SET_MQTT_CLIENT), sizeof(mqtt_client)); + Format(mqtt_topic, SettingsText(SET_MQTT_TOPIC), sizeof(mqtt_topic)); + if (strstr(SettingsText(SET_HOSTNAME), "%") != nullptr) { + SettingsUpdateText(SET_HOSTNAME, WIFI_HOSTNAME); + snprintf_P(my_hostname, sizeof(my_hostname)-1, SettingsText(SET_HOSTNAME), mqtt_topic, ESP.getChipId() & 0x1FFF); } else { - snprintf_P(my_hostname, sizeof(my_hostname)-1, Settings.hostname); + snprintf_P(my_hostname, sizeof(my_hostname)-1, SettingsText(SET_HOSTNAME)); } GetEspHardwareType(); @@ -330,7 +329,7 @@ void setup(void) } blink_powersave = power; - AddLog_P2(LOG_LEVEL_INFO, PSTR(D_PROJECT " %s %s " D_VERSION " %s%s-" ARDUINO_ESP8266_RELEASE), PROJECT, Settings.friendlyname[0], my_version, my_image); + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_PROJECT " %s %s " D_VERSION " %s%s-" ARDUINO_ESP8266_RELEASE), PROJECT, SettingsText(SET_FRIENDLYNAME1), my_version, my_image); #ifdef FIRMWARE_MINIMAL AddLog_P2(LOG_LEVEL_INFO, PSTR(D_WARNING_MINIMAL_VERSION)); #endif // FIRMWARE_MINIMAL diff --git a/tasmota/xdrv_01_webserver.ino b/tasmota/xdrv_01_webserver.ino index 5dbec658a..9c8131ad3 100644 --- a/tasmota/xdrv_01_webserver.ino +++ b/tasmota/xdrv_01_webserver.ino @@ -658,8 +658,8 @@ void PollDnsWebserver(void) bool WebAuthenticate(void) { - if (Settings.web_password[0] != 0 && HTTP_MANAGER_RESET_ONLY != Web.state) { - return WebServer->authenticate(WEB_USERNAME, Settings.web_password); + if (strlen(SettingsText(SET_WEBPWD)) && (HTTP_MANAGER_RESET_ONLY != Web.state)) { + return WebServer->authenticate(WEB_USERNAME, SettingsText(SET_WEBPWD)); } else { return true; } @@ -680,8 +680,8 @@ bool HttpCheckPriviledgedAccess(bool autorequestauth = true) void HttpHeaderCors(void) { - if (Settings.cors_domain[0] != 0) { - WebServer->sendHeader(F("Access-Control-Allow-Origin"), Settings.cors_domain); + if (strlen(SettingsText(SET_CORS))) { + WebServer->sendHeader(F("Access-Control-Allow-Origin"), SettingsText(SET_CORS)); } } @@ -816,7 +816,7 @@ void WSContentSend_PD(const char* formatP, ...) // Content send snprintf_P ch void WSContentStart_P(const char* title, bool auth) { - if (auth && (Settings.web_password[0] != 0) && !WebServer->authenticate(WEB_USERNAME, Settings.web_password)) { + if (auth && strlen(SettingsText(SET_WEBPWD)) && !WebServer->authenticate(WEB_USERNAME, SettingsText(SET_WEBPWD))) { return WebServer->requestAuthentication(); } @@ -825,7 +825,7 @@ void WSContentStart_P(const char* title, bool auth) if (title != nullptr) { char ctitle[strlen_P(title) +1]; strcpy_P(ctitle, title); // Get title from flash to RAM - WSContentSend_P(HTTP_HEADER, Settings.friendlyname[0], ctitle); + WSContentSend_P(HTTP_HEADER, SettingsText(SET_FRIENDLYNAME1), ctitle); } } @@ -869,7 +869,7 @@ void WSContentSendStyle_P(const char* formatP, ...) WebColor(COL_TEXT_WARNING), #endif WebColor(COL_TITLE), - ModuleName().c_str(), Settings.friendlyname[0]); + ModuleName().c_str(), SettingsText(SET_FRIENDLYNAME1)); if (Settings.flag3.gui_hostname_ip) { // SetOption53 - Show hostanme and IP address in GUI main menu bool lip = (static_cast(WiFi.localIP()) != 0); bool sip = (static_cast(WiFi.softAPIP()) != 0); @@ -993,10 +993,10 @@ void HandleRoot(void) if (WifiIsInManagerMode()) { #ifndef FIRMWARE_MINIMAL - if ((Settings.web_password[0] != 0) && !(WebServer->hasArg("USER1")) && !(WebServer->hasArg("PASS1")) && HTTP_MANAGER_RESET_ONLY != Web.state) { + if (strlen(SettingsText(SET_WEBPWD)) && !(WebServer->hasArg("USER1")) && !(WebServer->hasArg("PASS1")) && HTTP_MANAGER_RESET_ONLY != Web.state) { HandleWifiLogin(); } else { - if (!(Settings.web_password[0] != 0) || (((WebServer->arg("USER1") == WEB_USERNAME ) && (WebServer->arg("PASS1") == Settings.web_password )) || HTTP_MANAGER_RESET_ONLY == Web.state)) { + if (!strlen(SettingsText(SET_WEBPWD)) || (((WebServer->arg("USER1") == WEB_USERNAME ) && (WebServer->arg("PASS1") == SettingsText(SET_WEBPWD) )) || HTTP_MANAGER_RESET_ONLY == Web.state)) { HandleWifiConfiguration(); } else { // wrong user and pass @@ -1676,7 +1676,7 @@ void HandleWifiConfiguration(void) } // As WIFI_HOSTNAME may contain %s-%04d it cannot be part of HTTP_FORM_WIFI where it will exception - WSContentSend_P(HTTP_FORM_WIFI, Settings.sta_ssid[0], Settings.sta_ssid[1], WIFI_HOSTNAME, WIFI_HOSTNAME, Settings.hostname, Settings.cors_domain); + WSContentSend_P(HTTP_FORM_WIFI, SettingsText(SET_STASSID1), SettingsText(SET_STASSID2), WIFI_HOSTNAME, WIFI_HOSTNAME, SettingsText(SET_HOSTNAME), SettingsText(SET_CORS)); WSContentSend_P(HTTP_FORM_END); } @@ -1693,24 +1693,25 @@ void HandleWifiConfiguration(void) void WifiSaveSettings(void) { - char tmp[sizeof(Settings.sta_pwd[0])]; // Max length is currently 65 + char tmp[100]; // Max length is currently 65 WebGetArg("h", tmp, sizeof(tmp)); - strlcpy(Settings.hostname, (!strlen(tmp)) ? WIFI_HOSTNAME : tmp, sizeof(Settings.hostname)); - if (strstr(Settings.hostname, "%") != nullptr) { - strlcpy(Settings.hostname, WIFI_HOSTNAME, sizeof(Settings.hostname)); + SettingsUpdateText(SET_HOSTNAME, (!strlen(tmp)) ? WIFI_HOSTNAME : tmp); + if (strstr(SettingsText(SET_HOSTNAME), "%") != nullptr) { + SettingsUpdateText(SET_HOSTNAME, WIFI_HOSTNAME); } WebGetArg("c", tmp, sizeof(tmp)); - strlcpy(Settings.cors_domain, (!strlen(tmp)) ? CORS_DOMAIN : tmp, sizeof(Settings.cors_domain)); + SettingsUpdateText(SET_CORS, (!strlen(tmp)) ? CORS_DOMAIN : tmp); WebGetArg("s1", tmp, sizeof(tmp)); - strlcpy(Settings.sta_ssid[0], (!strlen(tmp)) ? STA_SSID1 : tmp, sizeof(Settings.sta_ssid[0])); + SettingsUpdateText(SET_STASSID1, (!strlen(tmp)) ? STA_SSID1 : tmp); WebGetArg("s2", tmp, sizeof(tmp)); - strlcpy(Settings.sta_ssid[1], (!strlen(tmp)) ? STA_SSID2 : tmp, sizeof(Settings.sta_ssid[1])); + SettingsUpdateText(SET_STASSID2, (!strlen(tmp)) ? STA_SSID2 : tmp); WebGetArg("p1", tmp, sizeof(tmp)); - strlcpy(Settings.sta_pwd[0], (!strlen(tmp)) ? "" : (strlen(tmp) < 5) ? Settings.sta_pwd[0] : tmp, sizeof(Settings.sta_pwd[0])); + SettingsUpdateText(SET_STAPWD1, (!strlen(tmp)) ? "" : (strlen(tmp) < 5) ? SettingsText(SET_STAPWD1) : tmp); WebGetArg("p2", tmp, sizeof(tmp)); - strlcpy(Settings.sta_pwd[1], (!strlen(tmp)) ? "" : (strlen(tmp) < 5) ? Settings.sta_pwd[1] : tmp, sizeof(Settings.sta_pwd[1])); - AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_WIFI D_CMND_HOSTNAME " %s, " D_CMND_SSID "1 %s, " D_CMND_SSID "2 %s, " D_CMND_CORS " %s"), Settings.hostname, Settings.sta_ssid[0], Settings.sta_ssid[1], Settings.cors_domain); + SettingsUpdateText(SET_STAPWD2, (!strlen(tmp)) ? "" : (strlen(tmp) < 5) ? SettingsText(SET_STAPWD2) : tmp); + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_WIFI D_CMND_HOSTNAME " %s, " D_CMND_SSID "1 %s, " D_CMND_SSID "2 %s, " D_CMND_CORS " %s"), + SettingsText(SET_HOSTNAME), SettingsText(SET_STASSID1), SettingsText(SET_STASSID2), SettingsText(SET_CORS)); } /*-------------------------------------------------------------------------------------------*/ @@ -1747,7 +1748,7 @@ void HandleLoggingConfiguration(void) } WSContentSend_P(PSTR("

")); } - WSContentSend_P(HTTP_FORM_LOG2, Settings.syslog_host, Settings.syslog_port, Settings.tele_period); + WSContentSend_P(HTTP_FORM_LOG2, SettingsText(SET_SYSLOG_HOST), Settings.syslog_port, Settings.tele_period); WSContentSend_P(HTTP_FORM_END); WSContentSpaceButton(BUTTON_CONFIGURATION); WSContentStop(); @@ -1755,7 +1756,7 @@ void HandleLoggingConfiguration(void) void LoggingSaveSettings(void) { - char tmp[sizeof(Settings.syslog_host)]; // Max length is currently 33 + char tmp[100]; // Max length is currently 33 WebGetArg("l0", tmp, sizeof(tmp)); SetSeriallog((!strlen(tmp)) ? SERIAL_LOG_LEVEL : atoi(tmp)); @@ -1766,7 +1767,7 @@ void LoggingSaveSettings(void) WebGetArg("l3", tmp, sizeof(tmp)); SetSyslog((!strlen(tmp)) ? SYS_LOG_LEVEL : atoi(tmp)); WebGetArg("lh", tmp, sizeof(tmp)); - strlcpy(Settings.syslog_host, (!strlen(tmp)) ? SYS_LOG_HOST : tmp, sizeof(Settings.syslog_host)); + SettingsUpdateText(SET_SYSLOG_HOST, (!strlen(tmp)) ? SYS_LOG_HOST : tmp); WebGetArg("lp", tmp, sizeof(tmp)); Settings.syslog_port = (!strlen(tmp)) ? SYS_LOG_PORT : atoi(tmp); WebGetArg("lt", tmp, sizeof(tmp)); @@ -1775,7 +1776,7 @@ void LoggingSaveSettings(void) Settings.tele_period = 10; // Do not allow periods < 10 seconds } AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_LOG D_CMND_SERIALLOG " %d, " D_CMND_WEBLOG " %d, " D_CMND_MQTTLOG " %d, " D_CMND_SYSLOG " %d, " D_CMND_LOGHOST " %s, " D_CMND_LOGPORT " %d, " D_CMND_TELEPERIOD " %d"), - Settings.seriallog_level, Settings.weblog_level, Settings.mqttlog_level, Settings.syslog_level, Settings.syslog_host, Settings.syslog_port, Settings.tele_period); + Settings.seriallog_level, Settings.weblog_level, Settings.mqttlog_level, Settings.syslog_level, SettingsText(SET_SYSLOG_HOST), Settings.syslog_port, Settings.tele_period); } /*-------------------------------------------------------------------------------------------*/ @@ -1811,7 +1812,7 @@ void HandleOtherConfiguration(void) (i) ? stemp : "", i, (i) ? stemp : "", - Settings.friendlyname[i]); + SettingsText(SET_FRIENDLYNAME1 + i)); } #ifdef USE_EMULATION @@ -1843,10 +1844,10 @@ void OtherSaveSettings(void) { char tmp[128]; char webindex[5]; - char friendlyname[sizeof(Settings.friendlyname[0])]; + char friendlyname[TOPSZ]; WebGetArg("wp", tmp, sizeof(tmp)); - strlcpy(Settings.web_password, (!strlen(tmp)) ? "" : (strchr(tmp,'*')) ? Settings.web_password : tmp, sizeof(Settings.web_password)); + SettingsUpdateText(SET_WEBPWD, (!strlen(tmp)) ? "" : (strchr(tmp,'*')) ? SettingsText(SET_WEBPWD) : tmp); Settings.flag.mqtt_enabled = WebServer->hasArg("b1"); // SetOption3 - Enable MQTT #ifdef USE_EMULATION WebGetArg("b2", tmp, sizeof(tmp)); @@ -1857,8 +1858,8 @@ void OtherSaveSettings(void) snprintf_P(webindex, sizeof(webindex), PSTR("a%d"), i); WebGetArg(webindex, tmp, sizeof(tmp)); snprintf_P(friendlyname, sizeof(friendlyname), PSTR(FRIENDLY_NAME"%d"), i +1); - strlcpy(Settings.friendlyname[i], (!strlen(tmp)) ? (i) ? friendlyname : FRIENDLY_NAME : tmp, sizeof(Settings.friendlyname[i])); - snprintf_P(log_data, sizeof(log_data), PSTR("%s%s %s"), log_data, (i) ? "," : "", Settings.friendlyname[i]); + SettingsUpdateText(SET_FRIENDLYNAME1 +i, (!strlen(tmp)) ? (i) ? friendlyname : FRIENDLY_NAME : tmp); + snprintf_P(log_data, sizeof(log_data), PSTR("%s%s %s"), log_data, (i) ? "," : "", SettingsText(SET_FRIENDLYNAME1 +i)); } AddLog(LOG_LEVEL_INFO); WebGetArg("t1", tmp, sizeof(tmp)); @@ -1890,8 +1891,8 @@ void HandleBackupConfiguration(void) char attachment[100]; -// char friendlyname[sizeof(Settings.friendlyname[0])]; -// snprintf_P(attachment, sizeof(attachment), PSTR("attachment; filename=Config_%s_%s.dmp"), NoAlNumToUnderscore(friendlyname, Settings.friendlyname[0]), my_version); +// char friendlyname[TOPSZ]; +// snprintf_P(attachment, sizeof(attachment), PSTR("attachment; filename=Config_%s_%s.dmp"), NoAlNumToUnderscore(friendlyname, SettingsText(SET_FRIENDLYNAME1)), my_version); char hostname[sizeof(my_hostname)]; snprintf_P(attachment, sizeof(attachment), PSTR("attachment; filename=Config_%s_%s.dmp"), NoAlNumToUnderscore(hostname, my_hostname), my_version); @@ -1991,10 +1992,10 @@ void HandleInformation(void) if (IsModuleIfan()) { maxfn = 1; } #endif // USE_SONOFF_IFAN for (uint32_t i = 0; i < maxfn; i++) { - WSContentSend_P(PSTR("}1" D_FRIENDLY_NAME " %d}2%s"), i +1, Settings.friendlyname[i]); + WSContentSend_P(PSTR("}1" D_FRIENDLY_NAME " %d}2%s"), i +1, SettingsText(SET_FRIENDLYNAME1 +i)); } WSContentSend_P(PSTR("}1}2 ")); // Empty line - WSContentSend_P(PSTR("}1" D_AP "%d " D_SSID " (" D_RSSI ")}2%s (%d%%, %d dBm)"), Settings.sta_active +1, Settings.sta_ssid[Settings.sta_active], WifiGetRssiAsQuality(WiFi.RSSI()), WiFi.RSSI()); + WSContentSend_P(PSTR("}1" D_AP "%d " D_SSID " (" D_RSSI ")}2%s (%d%%, %d dBm)"), Settings.sta_active +1, SettingsText(SET_STASSID1 + Settings.sta_active), WifiGetRssiAsQuality(WiFi.RSSI()), WiFi.RSSI()); WSContentSend_P(PSTR("}1" D_HOSTNAME "}2%s%s"), my_hostname, (Wifi.mdns_begun) ? ".local" : ""); #if LWIP_IPV6 String ipv6_addr = WifiGetIPv6(); @@ -2016,17 +2017,12 @@ void HandleInformation(void) } WSContentSend_P(PSTR("}1}2 ")); // Empty line if (Settings.flag.mqtt_enabled) { // SetOption3 - Enable MQTT -#ifdef USE_MQTT_AWS_IOT - WSContentSend_P(PSTR("}1" D_MQTT_HOST "}2%s%s"), Settings.mqtt_user, Settings.mqtt_host); + WSContentSend_P(PSTR("}1" D_MQTT_HOST "}2%s"), SettingsText(SET_MQTT_HOST)); WSContentSend_P(PSTR("}1" D_MQTT_PORT "}2%d"), Settings.mqtt_port); -#else - WSContentSend_P(PSTR("}1" D_MQTT_HOST "}2%s"), Settings.mqtt_host); - WSContentSend_P(PSTR("}1" D_MQTT_PORT "}2%d"), Settings.mqtt_port); - WSContentSend_P(PSTR("}1" D_MQTT_USER "}2%s"), Settings.mqtt_user); -#endif + WSContentSend_P(PSTR("}1" D_MQTT_USER "}2%s"), SettingsText(SET_MQTT_USER)); WSContentSend_P(PSTR("}1" D_MQTT_CLIENT "}2%s"), mqtt_client); - WSContentSend_P(PSTR("}1" D_MQTT_TOPIC "}2%s"), Settings.mqtt_topic); -// WSContentSend_P(PSTR("}1" D_MQTT_GROUP_TOPIC "}2%s"), Settings.mqtt_grptopic); + WSContentSend_P(PSTR("}1" D_MQTT_TOPIC "}2%s"), SettingsText(SET_MQTT_TOPIC)); +// WSContentSend_P(PSTR("}1" D_MQTT_GROUP_TOPIC "}2%s"), SettingsText(SET_MQTT_GRP_TOPIC)); WSContentSend_P(PSTR("}1" D_MQTT_GROUP_TOPIC "}2%s"), GetGroupTopic_P(stopic, "")); WSContentSend_P(PSTR("}1" D_MQTT_FULL_TOPIC "}2%s"), GetTopic_P(stopic, CMND, mqtt_topic, "")); WSContentSend_P(PSTR("}1" D_MQTT " " D_FALLBACK_TOPIC "}2%s"), GetFallbackTopic_P(stopic, "")); @@ -2085,7 +2081,7 @@ void HandleUpgradeFirmware(void) WSContentStart_P(S_FIRMWARE_UPGRADE); WSContentSendStyle(); - WSContentSend_P(HTTP_FORM_UPG, Settings.ota_url); + WSContentSend_P(HTTP_FORM_UPG, SettingsText(SET_OTAURL)); WSContentSend_P(HTTP_FORM_RST_UPG, D_UPGRADE); WSContentSpaceButton(BUTTON_MAIN); WSContentStop(); @@ -2098,12 +2094,12 @@ void HandleUpgradeFirmwareStart(void) { if (!HttpCheckPriviledgedAccess()) { return; } - char command[sizeof(Settings.ota_url) + 10]; // OtaUrl + char command[128]; // OtaUrl AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP D_UPGRADE_STARTED)); WifiConfigCounter(); - char otaurl[sizeof(Settings.ota_url)]; + char otaurl[101]; WebGetArg("o", otaurl, sizeof(otaurl)); if (strlen(otaurl)) { snprintf_P(command, sizeof(command), PSTR(D_CMND_OTAURL " %s"), otaurl); @@ -2412,12 +2408,12 @@ void HandleHttpCommand(void) AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP D_COMMAND)); bool valid = true; - if (Settings.web_password[0] != 0) { - char tmp1[sizeof(Settings.web_password)]; + if (strlen(SettingsText(SET_WEBPWD))) { + char tmp1[33]; WebGetArg("user", tmp1, sizeof(tmp1)); - char tmp2[sizeof(Settings.web_password)]; + char tmp2[strlen(SettingsText(SET_WEBPWD)) +1]; WebGetArg("password", tmp2, sizeof(tmp2)); - if (!(!strcmp(tmp1, WEB_USERNAME) && !strcmp(tmp2, Settings.web_password))) { valid = false; } + if (!(!strcmp(tmp1, WEB_USERNAME) && !strcmp(tmp2, SettingsText(SET_WEBPWD)))) { valid = false; } } WSContentBegin(200, CT_JSON); @@ -2790,9 +2786,9 @@ void CmndWebServer(void) void CmndWebPassword(void) { - if ((XdrvMailbox.data_len > 0) && (XdrvMailbox.data_len < sizeof(Settings.web_password))) { - strlcpy(Settings.web_password, (SC_CLEAR == Shortcut()) ? "" : (SC_DEFAULT == Shortcut()) ? WEB_PASSWORD : XdrvMailbox.data, sizeof(Settings.web_password)); - ResponseCmndChar(Settings.web_password); + if (XdrvMailbox.data_len > 0) { + SettingsUpdateText(SET_WEBPWD, (SC_CLEAR == Shortcut()) ? "" : (SC_DEFAULT == Shortcut()) ? WEB_PASSWORD : XdrvMailbox.data); + ResponseCmndChar(SettingsText(SET_WEBPWD)); } else { Response_P(S_JSON_COMMAND_ASTERISK, XdrvMailbox.command); } @@ -2860,10 +2856,10 @@ void CmndWebSensor(void) void CmndCors(void) { - if ((XdrvMailbox.data_len > 0) && (XdrvMailbox.data_len < sizeof(Settings.cors_domain))) { - strlcpy(Settings.cors_domain, (SC_CLEAR == Shortcut()) ? "" : (SC_DEFAULT == Shortcut()) ? WEB_PASSWORD : XdrvMailbox.data, sizeof(Settings.cors_domain)); + if (XdrvMailbox.data_len > 0) { + SettingsUpdateText(SET_CORS, (SC_CLEAR == Shortcut()) ? "" : (SC_DEFAULT == Shortcut()) ? WEB_PASSWORD : XdrvMailbox.data); } - ResponseCmndChar(Settings.cors_domain); + ResponseCmndChar(SettingsText(SET_CORS)); } /*********************************************************************************************\ diff --git a/tasmota/xdrv_02_mqtt.ino b/tasmota/xdrv_02_mqtt.ino index 7a9b1bf43..47838e223 100644 --- a/tasmota/xdrv_02_mqtt.ino +++ b/tasmota/xdrv_02_mqtt.ino @@ -91,10 +91,6 @@ tls_dir_t tls_dir; // memory copy of tls_dir from flash #endif // USE_MQTT_AWS_IOT -// A typical AWS IoT endpoint is 50 characters long, it does not fit -// in MqttHost field (32 chars). We need to concatenate both MqttUser and MqttHost -char AWS_endpoint[65]; // aWS IOT endpoint, concatenation of user and host - // check whether the fingerprint is filled with a single value // Filled with 0x00 = accept any fingerprint and learn it for next time // Filled with 0xFF = accept any fingerpring forever @@ -106,21 +102,6 @@ bool is_fingerprint_mono_value(uint8_t finger[20], uint8_t value) { } return true; } - -#ifdef USE_MQTT_AWS_IOT -void setLongMqttHost(const char *mqtt_host) { - if (strlen(mqtt_host) <= sizeof(Settings.mqtt_host)) { - strlcpy(Settings.mqtt_host, mqtt_host, sizeof(Settings.mqtt_host)); - Settings.mqtt_user[0] = 0; - } else { - // need to split in mqtt_user first then mqtt_host - strlcpy(Settings.mqtt_user, mqtt_host, sizeof(Settings.mqtt_user)); - strlcpy(Settings.mqtt_host, &mqtt_host[sizeof(Settings.mqtt_user)-1], sizeof(Settings.mqtt_host)); - } - strlcpy(AWS_endpoint, mqtt_host, sizeof(AWS_endpoint)); -} -#endif // USE_MQTT_AWS_IOT - #endif // USE_MQTT_TLS void MakeValidMqtt(uint32_t option, char* str) @@ -165,10 +146,10 @@ void MqttDiscoverServer(void) } } #endif // MDNS_HOSTNAME - snprintf_P(Settings.mqtt_host, sizeof(Settings.mqtt_host), MDNS.IP(i).toString().c_str()); + SettingsUpdateText(SET_MQTT_HOST, MDNS.IP(i).toString().c_str()); Settings.mqtt_port = MDNS.port(i); - AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_MDNS D_MQTT_SERVICE_FOUND " %s, " D_IP_ADDRESS " %s, " D_PORT " %d"), MDNS.hostname(i).c_str(), Settings.mqtt_host, Settings.mqtt_port); + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_MDNS D_MQTT_SERVICE_FOUND " %s, " D_IP_ADDRESS " %s, " D_PORT " %d"), MDNS.hostname(i).c_str(), SettingsText(SET_MQTT_HOST), Settings.mqtt_port); } } #endif // MQTT_HOST_DISCOVERY @@ -202,8 +183,6 @@ void MqttInit(void) tlsClient = new BearSSL::WiFiClientSecure_light(1024,1024); #ifdef USE_MQTT_AWS_IOT - snprintf_P(AWS_endpoint, sizeof(AWS_endpoint), PSTR("%s%s"), Settings.mqtt_user, Settings.mqtt_host); - loadTlsDir(); // load key and certificate data from Flash tlsClient->setClientECCert(AWS_IoT_Client_Certificate, AWS_IoT_Private_Key, @@ -261,8 +240,8 @@ void MqttDataHandler(char* mqtt_topic, uint8_t* mqtt_data, unsigned int data_len if (data_len >= MQTT_MAX_PACKET_SIZE) { return; } // Do not execute multiple times if Prefix1 equals Prefix2 - if (!strcmp(Settings.mqtt_prefix[0], Settings.mqtt_prefix[1])) { - char *str = strstr(mqtt_topic, Settings.mqtt_prefix[0]); + if (!strcmp(SettingsText(SET_MQTTPREFIX1), SettingsText(SET_MQTTPREFIX2))) { + char *str = strstr(mqtt_topic, SettingsText(SET_MQTTPREFIX1)); if ((str == mqtt_topic) && mqtt_cmnd_publish) { if (mqtt_cmnd_publish > 3) { mqtt_cmnd_publish -= 3; @@ -330,8 +309,8 @@ void MqttPublishLogging(const char *mxtime) GetTopic_P(stopic, STAT, mqtt_topic, romram); char *me; - if (!strcmp(Settings.mqtt_prefix[0], Settings.mqtt_prefix[1])) { - me = strstr(stopic, Settings.mqtt_prefix[0]); + if (!strcmp(SettingsText(SET_MQTTPREFIX1), SettingsText(SET_MQTTPREFIX2))) { + me = strstr(stopic, SettingsText(SET_MQTTPREFIX1)); if (me == stopic) { mqtt_cmnd_publish += 3; } @@ -389,8 +368,8 @@ void MqttPublish(const char* topic, bool retained) retained = false; // AWS IoT does not support retained, it will disconnect if received #endif - if (!strcmp(Settings.mqtt_prefix[0],Settings.mqtt_prefix[1])) { - me = strstr(topic,Settings.mqtt_prefix[0]); + if (!strcmp(SettingsText(SET_MQTTPREFIX1), SettingsText(SET_MQTTPREFIX2))) { + me = strstr(topic, SettingsText(SET_MQTTPREFIX1)); if (me == topic) { mqtt_cmnd_publish += 3; } @@ -505,11 +484,7 @@ void MqttDisconnected(int state) MqttClient.disconnect(); -#if defined(USE_MQTT_TLS) && defined(USE_MQTT_AWS_IOT) - AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_MQTT D_CONNECT_FAILED_TO " %s:%d, rc %d. " D_RETRY_IN " %d " D_UNIT_SECOND), AWS_endpoint, Settings.mqtt_port, state, Mqtt.retry_counter); -#else - AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_MQTT D_CONNECT_FAILED_TO " %s:%d, rc %d. " D_RETRY_IN " %d " D_UNIT_SECOND), Settings.mqtt_host, Settings.mqtt_port, state, Mqtt.retry_counter); -#endif + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_MQTT D_CONNECT_FAILED_TO " %s:%d, rc %d. " D_RETRY_IN " %d " D_UNIT_SECOND), SettingsText(SET_MQTT_HOST), Settings.mqtt_port, state, Mqtt.retry_counter); rules_flag.mqtt_disconnected = 1; } @@ -533,7 +508,7 @@ void MqttConnected(void) GetTopic_P(stopic, CMND, mqtt_topic, PSTR("#")); MqttSubscribe(stopic); - if (strstr_P(Settings.mqtt_fulltopic, MQTT_TOKEN_TOPIC) != nullptr) { + if (strstr_P(SettingsText(SET_MQTT_FULLTOPIC), MQTT_TOKEN_TOPIC) != nullptr) { GetGroupTopic_P(stopic, PSTR("#")); // SetOption75 0: %prefix%/nothing/%topic% = cmnd/nothing//# or SetOption75 1: cmnd/ MqttSubscribe(stopic); GetFallbackTopic_P(stopic, PSTR("#")); @@ -594,7 +569,7 @@ void MqttReconnect(void) MqttDiscoverServer(); #endif // MQTT_HOST_DISCOVERY #endif // USE_DISCOVERY - if (!strlen(Settings.mqtt_host) || !Settings.mqtt_port) { + if (!strlen(SettingsText(SET_MQTT_HOST)) || !Settings.mqtt_port) { Mqtt.allowed = false; } #if defined(USE_MQTT_TLS) && defined(USE_MQTT_AWS_IOT) @@ -621,8 +596,12 @@ void MqttReconnect(void) char *mqtt_user = nullptr; char *mqtt_pwd = nullptr; - if (strlen(Settings.mqtt_user) > 0) mqtt_user = Settings.mqtt_user; - if (strlen(Settings.mqtt_pwd) > 0) mqtt_pwd = Settings.mqtt_pwd; + if (strlen(SettingsText(SET_MQTT_USER))) { + mqtt_user = SettingsText(SET_MQTT_USER); + } + if (strlen(SettingsText(SET_MQTT_PWD))) { + mqtt_pwd = SettingsText(SET_MQTT_PWD); + } GetTopic_P(stopic, TELE, mqtt_topic, S_LWT); Response_P(S_OFFLINE); @@ -645,10 +624,8 @@ void MqttReconnect(void) tlsClient->setClientECCert(AWS_IoT_Client_Certificate, AWS_IoT_Private_Key, 0xFFFF /* all usages, don't care */, 0); - MqttClient.setServer(AWS_endpoint, Settings.mqtt_port); -#else - MqttClient.setServer(Settings.mqtt_host, Settings.mqtt_port); #endif + MqttClient.setServer(SettingsText(SET_MQTT_HOST), Settings.mqtt_port); uint32_t mqtt_connect_time = millis(); #if defined(USE_MQTT_TLS) && !defined(USE_MQTT_TLS_CA_CERT) @@ -662,7 +639,7 @@ void MqttReconnect(void) tlsClient->setPubKeyFingerprint(Settings.mqtt_fingerprint[0], Settings.mqtt_fingerprint[1], allow_all_fingerprints); #endif #if defined(USE_MQTT_TLS) && defined(USE_MQTT_AWS_IOT) - AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_MQTT "AWS IoT endpoint: %s"), AWS_endpoint); + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_MQTT "AWS IoT endpoint: %s"), SettingsText(SET_MQTT_HOST)); //if (MqttClient.connect(mqtt_client, nullptr, nullptr, nullptr, 0, false, nullptr)) { if (MqttClient.connect(mqtt_client, nullptr, nullptr, stopic, 1, false, mqtt_data, MQTT_CLEAN_SESSION)) { #else @@ -722,7 +699,7 @@ void MqttCheck(void) if (!Mqtt.retry_counter) { #ifdef USE_DISCOVERY #ifdef MQTT_HOST_DISCOVERY - if (!strlen(Settings.mqtt_host) && !Wifi.mdns_begun) { return; } + if (!strlen(SettingsText(SET_MQTT_HOST)) && !Wifi.mdns_begun) { return; } #endif // MQTT_HOST_DISCOVERY #endif // USE_DISCOVERY MqttReconnect(); @@ -763,18 +740,18 @@ void CmndMqttFingerprint(void) #if !defined(USE_MQTT_TLS) || !defined(USE_MQTT_AWS_IOT) // user and password are disabled with AWS IoT void CmndMqttUser(void) { - if ((XdrvMailbox.data_len > 0) && (XdrvMailbox.data_len < sizeof(Settings.mqtt_user))) { - strlcpy(Settings.mqtt_user, (SC_CLEAR == Shortcut()) ? "" : (SC_DEFAULT == Shortcut()) ? MQTT_USER : XdrvMailbox.data, sizeof(Settings.mqtt_user)); + if (XdrvMailbox.data_len > 0) { + SettingsUpdateText(SET_MQTT_USER, (SC_CLEAR == Shortcut()) ? "" : (SC_DEFAULT == Shortcut()) ? MQTT_USER : XdrvMailbox.data); restart_flag = 2; } - ResponseCmndChar(Settings.mqtt_user); + ResponseCmndChar(SettingsText(SET_MQTT_USER)); } void CmndMqttPassword(void) { - if ((XdrvMailbox.data_len > 0) && (XdrvMailbox.data_len < sizeof(Settings.mqtt_pwd))) { - strlcpy(Settings.mqtt_pwd, (SC_CLEAR == Shortcut()) ? "" : (SC_DEFAULT == Shortcut()) ? MQTT_PASS : XdrvMailbox.data, sizeof(Settings.mqtt_pwd)); - ResponseCmndChar(Settings.mqtt_pwd); + if (XdrvMailbox.data_len > 0) { + SettingsUpdateText(SET_MQTT_PWD, (SC_CLEAR == Shortcut()) ? "" : (SC_DEFAULT == Shortcut()) ? MQTT_PASS : XdrvMailbox.data); + ResponseCmndChar(SettingsText(SET_MQTT_PWD)); restart_flag = 2; } else { Response_P(S_JSON_COMMAND_ASTERISK, XdrvMailbox.command); @@ -792,19 +769,11 @@ void CmndMqttlog(void) void CmndMqttHost(void) { -#if defined(USE_MQTT_TLS) && defined(USE_MQTT_AWS_IOT) - if ((XdrvMailbox.data_len > 0) && (XdrvMailbox.data_len <= sizeof(Settings.mqtt_host) + sizeof(Settings.mqtt_user) - 2)) { - setLongMqttHost((SC_CLEAR == Shortcut()) ? "" : (SC_DEFAULT == Shortcut()) ? MQTT_HOST : XdrvMailbox.data); + if (XdrvMailbox.data_len > 0) { + SettingsUpdateText(SET_MQTT_HOST, (SC_CLEAR == Shortcut()) ? "" : (SC_DEFAULT == Shortcut()) ? MQTT_HOST : XdrvMailbox.data); restart_flag = 2; } - ResponseCmndChar(AWS_endpoint); -#else - if ((XdrvMailbox.data_len > 0) && (XdrvMailbox.data_len < sizeof(Settings.mqtt_host))) { - strlcpy(Settings.mqtt_host, (SC_CLEAR == Shortcut()) ? "" : (SC_DEFAULT == Shortcut()) ? MQTT_HOST : XdrvMailbox.data, sizeof(Settings.mqtt_host)); - restart_flag = 2; - } - ResponseCmndChar(Settings.mqtt_host); -#endif + ResponseCmndChar(SettingsText(SET_MQTT_HOST)); } void CmndMqttPort(void) @@ -828,11 +797,11 @@ void CmndMqttRetry(void) void CmndStateText(void) { if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= 4)) { - if ((XdrvMailbox.data_len > 0) && (XdrvMailbox.data_len < sizeof(Settings.state_text[0]))) { + if (XdrvMailbox.data_len > 0) { for (uint32_t i = 0; i <= XdrvMailbox.data_len; i++) { if (XdrvMailbox.data[i] == ' ') XdrvMailbox.data[i] = '_'; } - strlcpy(Settings.state_text[XdrvMailbox.index -1], XdrvMailbox.data, sizeof(Settings.state_text[0])); + SettingsUpdateText(SET_STATE_TXT1 + XdrvMailbox.index -1, XdrvMailbox.data); } ResponseCmndIdxChar(GetStateText(XdrvMailbox.index -1)); } @@ -840,40 +809,41 @@ void CmndStateText(void) void CmndMqttClient(void) { - if (!XdrvMailbox.grpflg && (XdrvMailbox.data_len > 0) && (XdrvMailbox.data_len < sizeof(Settings.mqtt_client))) { - strlcpy(Settings.mqtt_client, (SC_DEFAULT == Shortcut()) ? MQTT_CLIENT_ID : XdrvMailbox.data, sizeof(Settings.mqtt_client)); + if (!XdrvMailbox.grpflg && (XdrvMailbox.data_len > 0)) { + SettingsUpdateText(SET_MQTT_CLIENT, (SC_DEFAULT == Shortcut()) ? MQTT_CLIENT_ID : XdrvMailbox.data); restart_flag = 2; } - ResponseCmndChar(Settings.mqtt_client); + ResponseCmndChar(SettingsText(SET_MQTT_CLIENT)); } void CmndFullTopic(void) { - if ((XdrvMailbox.data_len > 0) && (XdrvMailbox.data_len < sizeof(Settings.mqtt_fulltopic))) { + if (XdrvMailbox.data_len > 0) { MakeValidMqtt(1, XdrvMailbox.data); if (!strcmp(XdrvMailbox.data, mqtt_client)) { SetShortcutDefault(); } char stemp1[TOPSZ]; strlcpy(stemp1, (SC_DEFAULT == Shortcut()) ? MQTT_FULLTOPIC : XdrvMailbox.data, sizeof(stemp1)); - if (strcmp(stemp1, Settings.mqtt_fulltopic)) { + if (strcmp(stemp1, SettingsText(SET_MQTT_FULLTOPIC))) { Response_P((Settings.flag.mqtt_offline) ? S_OFFLINE : ""); // SetOption10 - Control MQTT LWT message format MqttPublishPrefixTopic_P(TELE, PSTR(D_LWT), true); // Offline or remove previous retained topic - strlcpy(Settings.mqtt_fulltopic, stemp1, sizeof(Settings.mqtt_fulltopic)); + SettingsUpdateText(SET_MQTT_FULLTOPIC, stemp1); restart_flag = 2; } } - ResponseCmndChar(Settings.mqtt_fulltopic); + ResponseCmndChar(SettingsText(SET_MQTT_FULLTOPIC)); } void CmndPrefix(void) { if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= 3)) { - if ((XdrvMailbox.data_len > 0) && (XdrvMailbox.data_len < sizeof(Settings.mqtt_prefix[0]))) { + + if (XdrvMailbox.data_len > 0) { MakeValidMqtt(0, XdrvMailbox.data); - strlcpy(Settings.mqtt_prefix[XdrvMailbox.index -1], (SC_DEFAULT == Shortcut()) ? (1==XdrvMailbox.index)?SUB_PREFIX:(2==XdrvMailbox.index)?PUB_PREFIX:PUB_PREFIX2 : XdrvMailbox.data, sizeof(Settings.mqtt_prefix[0])); -// if (Settings.mqtt_prefix[XdrvMailbox.index -1][strlen(Settings.mqtt_prefix[XdrvMailbox.index -1])] == '/') Settings.mqtt_prefix[XdrvMailbox.index -1][strlen(Settings.mqtt_prefix[XdrvMailbox.index -1])] = 0; + SettingsUpdateText(SET_MQTTPREFIX1 + XdrvMailbox.index -1, + (SC_DEFAULT == Shortcut()) ? (1==XdrvMailbox.index) ? SUB_PREFIX : (2==XdrvMailbox.index) ? PUB_PREFIX : PUB_PREFIX2 : XdrvMailbox.data); restart_flag = 2; } - ResponseCmndIdxChar(Settings.mqtt_prefix[XdrvMailbox.index -1]); + ResponseCmndIdxChar(SettingsText(SET_MQTTPREFIX1 + XdrvMailbox.index -1)); } } @@ -899,60 +869,60 @@ void CmndPublish(void) void CmndGroupTopic(void) { - if ((XdrvMailbox.data_len > 0) && (XdrvMailbox.data_len < sizeof(Settings.mqtt_grptopic))) { + if (XdrvMailbox.data_len > 0) { MakeValidMqtt(0, XdrvMailbox.data); if (!strcmp(XdrvMailbox.data, mqtt_client)) { SetShortcutDefault(); } - strlcpy(Settings.mqtt_grptopic, (SC_DEFAULT == Shortcut()) ? MQTT_GRPTOPIC : XdrvMailbox.data, sizeof(Settings.mqtt_grptopic)); + SettingsUpdateText(SET_MQTT_GRP_TOPIC, (SC_DEFAULT == Shortcut()) ? MQTT_GRPTOPIC : XdrvMailbox.data); restart_flag = 2; } - ResponseCmndChar(Settings.mqtt_grptopic); + ResponseCmndChar(SettingsText(SET_MQTT_GRP_TOPIC)); } void CmndTopic(void) { - if (!XdrvMailbox.grpflg && (XdrvMailbox.data_len > 0) && (XdrvMailbox.data_len < sizeof(Settings.mqtt_topic))) { + if (!XdrvMailbox.grpflg && (XdrvMailbox.data_len > 0)) { MakeValidMqtt(0, XdrvMailbox.data); if (!strcmp(XdrvMailbox.data, mqtt_client)) { SetShortcutDefault(); } char stemp1[TOPSZ]; strlcpy(stemp1, (SC_DEFAULT == Shortcut()) ? MQTT_TOPIC : XdrvMailbox.data, sizeof(stemp1)); - if (strcmp(stemp1, Settings.mqtt_topic)) { + if (strcmp(stemp1, SettingsText(SET_MQTT_TOPIC))) { Response_P((Settings.flag.mqtt_offline) ? S_OFFLINE : ""); // SetOption10 - Control MQTT LWT message format MqttPublishPrefixTopic_P(TELE, PSTR(D_LWT), true); // Offline or remove previous retained topic - strlcpy(Settings.mqtt_topic, stemp1, sizeof(Settings.mqtt_topic)); + SettingsUpdateText(SET_MQTT_TOPIC, stemp1); restart_flag = 2; } } - ResponseCmndChar(Settings.mqtt_topic); + ResponseCmndChar(SettingsText(SET_MQTT_TOPIC)); } void CmndButtonTopic(void) { - if (!XdrvMailbox.grpflg && (XdrvMailbox.data_len > 0) && (XdrvMailbox.data_len < sizeof(Settings.button_topic))) { + if (!XdrvMailbox.grpflg && (XdrvMailbox.data_len > 0)) { MakeValidMqtt(0, XdrvMailbox.data); if (!strcmp(XdrvMailbox.data, mqtt_client)) { SetShortcutDefault(); } switch (Shortcut()) { - case SC_CLEAR: strlcpy(Settings.button_topic, "", sizeof(Settings.button_topic)); break; - case SC_DEFAULT: strlcpy(Settings.button_topic, mqtt_topic, sizeof(Settings.button_topic)); break; - case SC_USER: strlcpy(Settings.button_topic, MQTT_BUTTON_TOPIC, sizeof(Settings.button_topic)); break; - default: strlcpy(Settings.button_topic, XdrvMailbox.data, sizeof(Settings.button_topic)); + case SC_CLEAR: SettingsUpdateText(SET_MQTT_BUTTON_TOPIC, ""); break; + case SC_DEFAULT: SettingsUpdateText(SET_MQTT_BUTTON_TOPIC, mqtt_topic); break; + case SC_USER: SettingsUpdateText(SET_MQTT_BUTTON_TOPIC, MQTT_BUTTON_TOPIC); break; + default: SettingsUpdateText(SET_MQTT_BUTTON_TOPIC, XdrvMailbox.data); } } - ResponseCmndChar(Settings.button_topic); + ResponseCmndChar(SettingsText(SET_MQTT_BUTTON_TOPIC)); } void CmndSwitchTopic(void) { - if (!XdrvMailbox.grpflg && (XdrvMailbox.data_len > 0) && (XdrvMailbox.data_len < sizeof(Settings.switch_topic))) { + if (!XdrvMailbox.grpflg && (XdrvMailbox.data_len > 0)) { MakeValidMqtt(0, XdrvMailbox.data); if (!strcmp(XdrvMailbox.data, mqtt_client)) { SetShortcutDefault(); } switch (Shortcut()) { - case SC_CLEAR: strlcpy(Settings.switch_topic, "", sizeof(Settings.switch_topic)); break; - case SC_DEFAULT: strlcpy(Settings.switch_topic, mqtt_topic, sizeof(Settings.switch_topic)); break; - case SC_USER: strlcpy(Settings.switch_topic, MQTT_SWITCH_TOPIC, sizeof(Settings.switch_topic)); break; - default: strlcpy(Settings.switch_topic, XdrvMailbox.data, sizeof(Settings.switch_topic)); + case SC_CLEAR: SettingsUpdateText(SET_MQTT_SWITCH_TOPIC, ""); break; + case SC_DEFAULT: SettingsUpdateText(SET_MQTT_SWITCH_TOPIC, mqtt_topic); break; + case SC_USER: SettingsUpdateText(SET_MQTT_SWITCH_TOPIC, MQTT_SWITCH_TOPIC); break; + default: SettingsUpdateText(SET_MQTT_SWITCH_TOPIC, XdrvMailbox.data); } } - ResponseCmndChar(Settings.switch_topic); + ResponseCmndChar(SettingsText(SET_MQTT_SWITCH_TOPIC)); } void CmndButtonRetain(void) @@ -1220,22 +1190,18 @@ void HandleMqttConfiguration(void) return; } - char str[sizeof(Settings.mqtt_client)]; + char str[33]; WSContentStart_P(S_CONFIGURE_MQTT); WSContentSendStyle(); WSContentSend_P(HTTP_FORM_MQTT1, -#if defined(USE_MQTT_TLS) && defined(USE_MQTT_AWS_IOT) - AWS_endpoint, -#else - Settings.mqtt_host, -#endif + SettingsText(SET_MQTT_HOST), Settings.mqtt_port, - Format(str, MQTT_CLIENT_ID, sizeof(str)), MQTT_CLIENT_ID, Settings.mqtt_client); + Format(str, MQTT_CLIENT_ID, sizeof(str)), MQTT_CLIENT_ID, SettingsText(SET_MQTT_CLIENT)); WSContentSend_P(HTTP_FORM_MQTT2, - (Settings.mqtt_user[0] == '\0') ? "0" : Settings.mqtt_user, - Format(str, MQTT_TOPIC, sizeof(str)), MQTT_TOPIC, Settings.mqtt_topic, - MQTT_FULLTOPIC, MQTT_FULLTOPIC, Settings.mqtt_fulltopic); + (!strlen(SettingsText(SET_MQTT_USER))) ? "0" : SettingsText(SET_MQTT_USER), + Format(str, MQTT_TOPIC, sizeof(str)), MQTT_TOPIC, SettingsText(SET_MQTT_TOPIC), + MQTT_FULLTOPIC, MQTT_FULLTOPIC, SettingsText(SET_MQTT_FULLTOPIC)); WSContentSend_P(HTTP_FORM_END); WSContentSpaceButton(BUTTON_CONFIGURATION); WSContentStop(); @@ -1253,32 +1219,28 @@ void MqttSaveSettings(void) WebGetArg("mf", tmp, sizeof(tmp)); strlcpy(stemp2, (!strlen(tmp)) ? MQTT_FULLTOPIC : tmp, sizeof(stemp2)); MakeValidMqtt(1, stemp2); - if ((strcmp(stemp, Settings.mqtt_topic)) || (strcmp(stemp2, Settings.mqtt_fulltopic))) { + if ((strcmp(stemp, SettingsText(SET_MQTT_TOPIC))) || (strcmp(stemp2, SettingsText(SET_MQTT_FULLTOPIC)))) { Response_P((Settings.flag.mqtt_offline) ? S_OFFLINE : ""); // SetOption10 - Control MQTT LWT message format MqttPublishPrefixTopic_P(TELE, S_LWT, true); // Offline or remove previous retained topic } - strlcpy(Settings.mqtt_topic, stemp, sizeof(Settings.mqtt_topic)); - strlcpy(Settings.mqtt_fulltopic, stemp2, sizeof(Settings.mqtt_fulltopic)); + SettingsUpdateText(SET_MQTT_TOPIC, stemp); + SettingsUpdateText(SET_MQTT_FULLTOPIC, stemp2); WebGetArg("mh", tmp, sizeof(tmp)); -#if defined(USE_MQTT_TLS) && defined(USE_MQTT_AWS_IOT) - setLongMqttHost((!strlen(tmp)) ? MQTT_HOST : (!strcmp(tmp,"0")) ? "" : tmp); -#else - strlcpy(Settings.mqtt_host, (!strlen(tmp)) ? MQTT_HOST : (!strcmp(tmp,"0")) ? "" : tmp, sizeof(Settings.mqtt_host)); -#endif + SettingsUpdateText(SET_MQTT_HOST, (!strlen(tmp)) ? MQTT_HOST : (!strcmp(tmp,"0")) ? "" : tmp); WebGetArg("ml", tmp, sizeof(tmp)); Settings.mqtt_port = (!strlen(tmp)) ? MQTT_PORT : atoi(tmp); WebGetArg("mc", tmp, sizeof(tmp)); - strlcpy(Settings.mqtt_client, (!strlen(tmp)) ? MQTT_CLIENT_ID : tmp, sizeof(Settings.mqtt_client)); + SettingsUpdateText(SET_MQTT_CLIENT, (!strlen(tmp)) ? MQTT_CLIENT_ID : tmp); #if defined(USE_MQTT_TLS) && defined(USE_MQTT_AWS_IOT) AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_MQTT D_CMND_MQTTHOST " %s, " D_CMND_MQTTPORT " %d, " D_CMND_MQTTCLIENT " %s, " D_CMND_TOPIC " %s, " D_CMND_FULLTOPIC " %s"), - AWS_endpoint, Settings.mqtt_port, Settings.mqtt_client, Settings.mqtt_topic, Settings.mqtt_fulltopic); + SettingsText(SET_MQTT_HOST), Settings.mqtt_port, SettingsText(SET_MQTT_CLIENT), SettingsText(SET_MQTT_TOPIC), SettingsText(SET_MQTT_FULLTOPIC)); #else // USE_MQTT_AWS_IOT WebGetArg("mu", tmp, sizeof(tmp)); - strlcpy(Settings.mqtt_user, (!strlen(tmp)) ? MQTT_USER : (!strcmp(tmp,"0")) ? "" : tmp, sizeof(Settings.mqtt_user)); + SettingsUpdateText(SET_MQTT_USER, (!strlen(tmp)) ? MQTT_USER : (!strcmp(tmp,"0")) ? "" : tmp); WebGetArg("mp", tmp, sizeof(tmp)); - strlcpy(Settings.mqtt_pwd, (!strlen(tmp)) ? "" : (!strcmp(tmp, D_ASTERISK_PWD)) ? Settings.mqtt_pwd : tmp, sizeof(Settings.mqtt_pwd)); + SettingsUpdateText(SET_MQTT_PWD, (!strlen(tmp)) ? "" : (!strcmp(tmp, D_ASTERISK_PWD)) ? SettingsText(SET_MQTT_PWD) : tmp); AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_MQTT D_CMND_MQTTHOST " %s, " D_CMND_MQTTPORT " %d, " D_CMND_MQTTCLIENT " %s, " D_CMND_MQTTUSER " %s, " D_CMND_TOPIC " %s, " D_CMND_FULLTOPIC " %s"), - Settings.mqtt_host, Settings.mqtt_port, Settings.mqtt_client, Settings.mqtt_user, Settings.mqtt_topic, Settings.mqtt_fulltopic); + SettingsText(SET_MQTT_HOST), Settings.mqtt_port, SettingsText(SET_MQTT_CLIENT), SettingsText(SET_MQTT_USER), SettingsText(SET_MQTT_TOPIC), SettingsText(SET_MQTT_FULLTOPIC)); #endif } #endif // USE_WEBSERVER diff --git a/tasmota/xdrv_10_rules.ino b/tasmota/xdrv_10_rules.ino index ac771f0d7..6e1636518 100644 --- a/tasmota/xdrv_10_rules.ino +++ b/tasmota/xdrv_10_rules.ino @@ -217,7 +217,7 @@ bool RulesRuleMatch(uint8_t rule_set, String &event, String &rule) for (uint32_t i = 0; i < MAX_RULE_MEMS; i++) { snprintf_P(stemp, sizeof(stemp), PSTR("%%MEM%d%%"), i +1); if (rule_param.startsWith(stemp)) { - rule_param = Settings.mems[i]; + rule_param = SettingsText(SET_MEM1 + i); break; } } @@ -447,12 +447,12 @@ bool RuleSetProcess(uint8_t rule_set, String &event_saved) } for (uint32_t i = 0; i < MAX_RULE_MEMS; i++) { snprintf_P(stemp, sizeof(stemp), PSTR("%%MEM%d%%"), i +1); - RulesVarReplace(commands, stemp, Settings.mems[i]); + RulesVarReplace(commands, stemp, SettingsText(SET_MEM1 +i)); } RulesVarReplace(commands, F("%TIME%"), String(MinutesPastMidnight())); RulesVarReplace(commands, F("%UPTIME%"), String(MinutesUptime())); RulesVarReplace(commands, F("%TIMESTAMP%"), GetDateAndTime(DT_LOCAL)); - RulesVarReplace(commands, F("%TOPIC%"), Settings.mqtt_topic); + RulesVarReplace(commands, F("%TOPIC%"), SettingsText(SET_MQTT_TOPIC)); #if defined(USE_TIMERS) && defined(USE_SUNRISE) RulesVarReplace(commands, F("%SUNRISE%"), String(SunMinutes(0))); RulesVarReplace(commands, F("%SUNSET%"), String(SunMinutes(1))); @@ -608,7 +608,7 @@ void RulesEvery50ms(void) for (uint32_t i = 0; i < MAX_RULE_MEMS; i++) { if (bitRead(Rules.mems_event, i)) { bitClear(Rules.mems_event, i); - snprintf_P(json_event, sizeof(json_event), PSTR("{\"Mem%d\":{\"State\":%s}}"), i+1, Settings.mems[i]); + snprintf_P(json_event, sizeof(json_event), PSTR("{\"Mem%d\":{\"State\":%s}}"), i+1, SettingsText(SET_MEM1 +i)); RulesProcessEvent(json_event); break; } @@ -1002,7 +1002,7 @@ bool findNextVariableValue(char * &pVarname, float &value) } else if (sVarName.startsWith(F("MEM"))) { int index = sVarName.substring(3).toInt(); if (index > 0 && index <= MAX_RULE_MEMS) { - value = CharToFloat(Settings.mems[index -1]); + value = CharToFloat(SettingsText(SET_MEM1 + index -1)); } } else if (sVarName.equals(F("TIME"))) { value = MinutesPastMidnight(); @@ -1810,23 +1810,23 @@ void CmndMemory(void) if (!XdrvMailbox.usridx) { mqtt_data[0] = '\0'; for (uint32_t i = 0; i < MAX_RULE_MEMS; i++) { - ResponseAppend_P(PSTR("%c\"Mem%d\":\"%s\""), (i) ? ',' : '{', i +1, Settings.mems[i]); + ResponseAppend_P(PSTR("%c\"Mem%d\":\"%s\""), (i) ? ',' : '{', i +1, SettingsText(SET_MEM1 +i)); } ResponseJsonEnd(); } else { if (XdrvMailbox.data_len > 0) { #ifdef USE_EXPRESSION if (XdrvMailbox.data[0] == '=') { // Spaces already been skipped in data - dtostrfd(evaluateExpression(XdrvMailbox.data + 1, XdrvMailbox.data_len - 1), Settings.flag2.calc_resolution, Settings.mems[XdrvMailbox.index -1]); + dtostrfd(evaluateExpression(XdrvMailbox.data + 1, XdrvMailbox.data_len - 1), Settings.flag2.calc_resolution, SettingsText(SET_MEM1 + XdrvMailbox.index -1)); } else { - strlcpy(Settings.mems[XdrvMailbox.index -1], ('"' == XdrvMailbox.data[0]) ? "" : XdrvMailbox.data, sizeof(Settings.mems[XdrvMailbox.index -1])); + SettingsUpdateText(SET_MEM1 + XdrvMailbox.index -1, ('"' == XdrvMailbox.data[0]) ? "" : XdrvMailbox.data); } #else - strlcpy(Settings.mems[XdrvMailbox.index -1], ('"' == XdrvMailbox.data[0]) ? "" : XdrvMailbox.data, sizeof(Settings.mems[XdrvMailbox.index -1])); + SettingsUpdateText(SET_MEM1 + XdrvMailbox.index -1, ('"' == XdrvMailbox.data[0]) ? "" : XdrvMailbox.data); #endif // USE_EXPRESSION bitSet(Rules.mems_event, XdrvMailbox.index -1); } - ResponseCmndIdxChar(Settings.mems[XdrvMailbox.index -1]); + ResponseCmndIdxChar(SettingsText(SET_MEM1 + XdrvMailbox.index -1)); } } } diff --git a/tasmota/xdrv_10_scripter.ino b/tasmota/xdrv_10_scripter.ino index 5badb88ea..68df13984 100755 --- a/tasmota/xdrv_10_scripter.ino +++ b/tasmota/xdrv_10_scripter.ino @@ -1041,7 +1041,7 @@ char *isvar(char *lp, uint8_t *vtype,struct T_INDEX *tind,float *fp,char *sp,Jso if (sp) strlcpy(sp,str_value,SCRIPT_MAXSSIZE); return lp+len; } - + } else { if (fp) { if (!strncmp(vn.c_str(),"Epoch",5)) { @@ -1370,7 +1370,7 @@ chknext: goto exit; } if (!strncmp(vname,"gtopic",6)) { - if (sp) strlcpy(sp,Settings.mqtt_grptopic,glob_script_mem.max_ssize); + if (sp) strlcpy(sp,SettingsText(SET_MQTT_GRP_TOPIC),glob_script_mem.max_ssize); goto strexit; } break; @@ -1527,15 +1527,15 @@ chknext: goto exit; } if (!strncmp(vname,"prefix1",7)) { - if (sp) strlcpy(sp,Settings.mqtt_prefix[0],glob_script_mem.max_ssize); + if (sp) strlcpy(sp,SettingsText(SET_MQTTPREFIX1),glob_script_mem.max_ssize); goto strexit; } if (!strncmp(vname,"prefix2",7)) { - if (sp) strlcpy(sp,Settings.mqtt_prefix[1],glob_script_mem.max_ssize); + if (sp) strlcpy(sp,SettingsText(SET_MQTTPREFIX2),glob_script_mem.max_ssize); goto strexit; } if (!strncmp(vname,"prefix3",7)) { - if (sp) strlcpy(sp,Settings.mqtt_prefix[2],glob_script_mem.max_ssize); + if (sp) strlcpy(sp,SettingsText(SET_MQTTPREFIX3),glob_script_mem.max_ssize); goto strexit; } if (!strncmp(vname,"pow(",4)) { @@ -1744,7 +1744,7 @@ chknext: goto strexit; } if (!strncmp(vname,"topic",5)) { - if (sp) strlcpy(sp,Settings.mqtt_topic,glob_script_mem.max_ssize); + if (sp) strlcpy(sp,SettingsText(SET_MQTT_TOPIC),glob_script_mem.max_ssize); goto strexit; } #ifdef USE_DISPLAY diff --git a/tasmota/xdrv_12_home_assistant.ino b/tasmota/xdrv_12_home_assistant.ino index 22c0e370e..58c266c61 100644 --- a/tasmota/xdrv_12_home_assistant.ino +++ b/tasmota/xdrv_12_home_assistant.ino @@ -46,7 +46,7 @@ const char HASS_DISCOVER_BUTTON_TOGGLE[] PROGMEM = const char HASS_DISCOVER_SWITCH_TOGGLE[] PROGMEM = ",\"value_template\":\"{%%if is_state(entity_id,\\\"on\\\")-%%}OFF{%%-else-%%}ON{%%-endif%%}\""; // A switch must maintain his state until the next update - + const char HASS_DISCOVER_BUTTON_SWITCH_ONOFF[] PROGMEM = ",\"value_template\":\"{{value_json.%s}}\"," // STATE "\"frc_upd\":true," // In ON/OFF case, enable force_update to make automations work @@ -86,7 +86,7 @@ const char HASS_DISCOVER_SENSOR[] PROGMEM = "{\"name\":\"%s\"," // dualr2 1 BTN "\"stat_t\":\"%s\"," // cmnd/dualr2/POWER (implies "\"optimistic\":\"false\",") "\"avty_t\":\"%s\"," // tele/dualr2/LWT - "\"frc_upd\":true," // force update for better graph representation + "\"frc_upd\":true," // force update for better graph representation "\"pl_avail\":\"" D_ONLINE "\"," // Online "\"pl_not_avail\":\"" D_OFFLINE "\""; // Offline @@ -239,9 +239,9 @@ void HAssAnnounceRelayLight(void) char *availability_topic = stemp3; if (i > MAX_FRIENDLYNAMES) { - snprintf_P(name, sizeof(name), PSTR("%s %d"), Settings.friendlyname[0], i); + snprintf_P(name, sizeof(name), PSTR("%s %d"), SettingsText(SET_FRIENDLYNAME1), i); } else { - snprintf_P(name, sizeof(name), Settings.friendlyname[i -1]); + snprintf_P(name, sizeof(name), SettingsText(SET_FRIENDLYNAME1 +i -1)); } GetPowerDevice(value_template, i, sizeof(value_template), Settings.flag.device_index_enable); // SetOption26 - Switch between POWER or POWER1 GetTopic_P(command_topic, CMND, mqtt_topic, value_template); @@ -253,7 +253,7 @@ void HAssAnnounceRelayLight(void) Shorten(&state_topic, prefix); Shorten(&availability_topic, prefix); - Response_P(HASS_DISCOVER_RELAY, name, command_topic, state_topic, value_template, Settings.state_text[0], Settings.state_text[1], availability_topic); + Response_P(HASS_DISCOVER_RELAY, name, command_topic, state_topic, value_template, SettingsText(SET_STATE_TXT1), SettingsText(SET_STATE_TXT2), availability_topic); TryResponseAppend_P(HASS_DISCOVER_DEVICE_INFO_SHORT, unique_id, ESP.getChipId(), WiFi.macAddress().c_str()); TryResponseAppend_P(HASS_DISCOVER_TOPIC_PREFIX, prefix); @@ -324,7 +324,7 @@ void HAssAnnounceButtonSwitch(uint8_t device, char* topic, uint8_t present, uint char *availability_topic = stemp2; char jsoname[8]; - snprintf_P(name, sizeof(name), PSTR("%s %s%d"), Settings.friendlyname[0], key?"Switch":"Button", device+1); + snprintf_P(name, sizeof(name), PSTR("%s %s%d"), SettingsText(SET_FRIENDLYNAME1), key?"Switch":"Button", device+1); snprintf_P(jsoname, sizeof(jsoname), PSTR("%s%d"), key?"SWITCH":"BUTTON", device+1); GetPowerDevice(value_template, device+1, sizeof(value_template), key + Settings.flag.device_index_enable); // Force index for Switch 1, Index on Button1 is controlled by SetOption26 - Switch between POWER or POWER1 @@ -339,11 +339,11 @@ void HAssAnnounceButtonSwitch(uint8_t device, char* topic, uint8_t present, uint TryResponseAppend_P(HASS_DISCOVER_DEVICE_INFO_SHORT, unique_id, ESP.getChipId(), WiFi.macAddress().c_str()); if (strlen(prefix) > 0 ) TryResponseAppend_P(HASS_DISCOVER_TOPIC_PREFIX, prefix); if (toggle) { - if (!key) { - TryResponseAppend_P(HASS_DISCOVER_BUTTON_TOGGLE, PSTR(D_RSLT_STATE), Settings.state_text[toggle?2:1]); + if (!key) { + TryResponseAppend_P(HASS_DISCOVER_BUTTON_TOGGLE, PSTR(D_RSLT_STATE), SettingsText(SET_STATE_TXT1 + toggle?2:1)); } else {TryResponseAppend_P(HASS_DISCOVER_SWITCH_TOGGLE);} } - else TryResponseAppend_P(HASS_DISCOVER_BUTTON_SWITCH_ONOFF, PSTR(D_RSLT_STATE), Settings.state_text[toggle?2:1], Settings.state_text[0]); + else TryResponseAppend_P(HASS_DISCOVER_BUTTON_SWITCH_ONOFF, PSTR(D_RSLT_STATE), SettingsText(SET_STATE_TXT1 + toggle?2:1), SettingsText(SET_STATE_TXT1)); TryResponseAppend_P(PSTR("}")); } @@ -352,10 +352,10 @@ void HAssAnnounceButtonSwitch(uint8_t device, char* topic, uint8_t present, uint void HAssAnnounceSwitches(void) { - char sw_topic[sizeof(Settings.switch_topic)]; + char sw_topic[TOPSZ]; // Send info about buttons - char *tmp = Settings.switch_topic; + char *tmp = SettingsText(SET_MQTT_SWITCH_TOPIC); Format(sw_topic, tmp, sizeof(sw_topic)); if (!strcmp_P(sw_topic, "0") || strlen(sw_topic) == 0 ) { for (uint32_t switch_index = 0; switch_index < MAX_SWITCHES; switch_index++) { @@ -369,7 +369,7 @@ void HAssAnnounceSwitches(void) // Check if MQTT message will be ON/OFF or TOGGLE if (Settings.switchmode[switch_index] == FOLLOW || Settings.switchmode[switch_index] == FOLLOW_INV || Settings.flag3.button_switch_force_local || // SetOption61 - Force local operation when button/switch topic is set - !strcmp(mqtt_topic, sw_topic) || !strcmp(Settings.mqtt_grptopic, sw_topic)) + !strcmp(mqtt_topic, sw_topic) || !strcmp(SettingsText(SET_MQTT_GRP_TOPIC), sw_topic)) { toggle = 0; // MQTT message will be ON/OFF } @@ -381,10 +381,10 @@ void HAssAnnounceSwitches(void) void HAssAnnounceButtons(void) { - char key_topic[sizeof(Settings.button_topic)]; + char key_topic[TOPSZ]; // Send info about buttons - char *tmp = Settings.button_topic; + char *tmp = SettingsText(SET_MQTT_BUTTON_TOPIC); Format(key_topic, tmp, sizeof(key_topic)); if (!strcmp_P(key_topic, "0") || strlen(key_topic) == 0 ) { for (uint32_t button_index = 0; button_index < MAX_KEYS; button_index++) { @@ -401,7 +401,7 @@ void HAssAnnounceButtons(void) // Check if MQTT message will be ON/OFF or TOGGLE if (Settings.flag3.button_switch_force_local || // SetOption61 - Force local operation when button/switch topic is set - !strcmp(mqtt_topic, key_topic) || !strcmp(Settings.mqtt_grptopic, key_topic)) + !strcmp(mqtt_topic, key_topic) || !strcmp(SettingsText(SET_MQTT_GRP_TOPIC), key_topic)) { toggle = 0; // MQTT message will be ON/OFF } @@ -445,7 +445,7 @@ void HAssAnnounceSensor(const char* sensorname, const char* subsensortype) GetTopic_P(state_topic, TELE, mqtt_topic, PSTR(D_RSLT_SENSOR)); } - snprintf_P(name, sizeof(name), PSTR("%s %s %s"), Settings.friendlyname[0], sensorname, subsensortype); + snprintf_P(name, sizeof(name), PSTR("%s %s %s"), SettingsText(SET_FRIENDLYNAME1), sensorname, subsensortype); GetTopic_P(availability_topic, TELE, mqtt_topic, S_LWT); FindPrefix(state_topic, availability_topic, prefix); Shorten(&state_topic, prefix); @@ -458,7 +458,7 @@ void HAssAnnounceSensor(const char* sensorname, const char* subsensortype) TryResponseAppend_P(HASS_DISCOVER_SENSOR_TEMP, TempUnit(), sensorname); } else if (!strcmp_P(subsensortype, PSTR(D_JSON_HUMIDITY))) { TryResponseAppend_P(HASS_DISCOVER_SENSOR_HUM, sensorname); - } else if (!strcmp_P(subsensortype, PSTR(D_JSON_PRESSURE)) + } else if (!strcmp_P(subsensortype, PSTR(D_JSON_PRESSURE)) || !strcmp_P(subsensortype, PSTR(D_JSON_PRESSUREATSEALEVEL))){ TryResponseAppend_P(HASS_DISCOVER_SENSOR_PRESS, PressureUnit().c_str(), sensorname, subsensortype); } else if (!strcmp_P(subsensortype, PSTR(D_JSON_TOTAL)) @@ -548,7 +548,7 @@ void HAssAnnounceStatusSensor(void) char *state_topic = stemp1; char *availability_topic = stemp2; - snprintf_P(name, sizeof(name), PSTR("%s status"), Settings.friendlyname[0]); + snprintf_P(name, sizeof(name), PSTR("%s status"), SettingsText(SET_FRIENDLYNAME1)); GetTopic_P(state_topic, TELE, mqtt_topic, PSTR(D_RSLT_HASS_STATE)); GetTopic_P(availability_topic, TELE, mqtt_topic, S_LWT); FindPrefix(state_topic, availability_topic, prefix); @@ -558,7 +558,7 @@ void HAssAnnounceStatusSensor(void) Response_P(HASS_DISCOVER_SENSOR, name, state_topic, availability_topic); TryResponseAppend_P(HASS_DISCOVER_SENSOR_HASS_STATUS, state_topic); TryResponseAppend_P(HASS_DISCOVER_DEVICE_INFO, unique_id, ESP.getChipId(), WiFi.macAddress().c_str(), - Settings.friendlyname[0], ModuleName().c_str(), my_version, my_image); + SettingsText(SET_FRIENDLYNAME1), ModuleName().c_str(), my_version, my_image); TryResponseAppend_P(HASS_DISCOVER_TOPIC_PREFIX, prefix); TryResponseAppend_P(PSTR("}")); } @@ -588,8 +588,8 @@ void HAssDiscovery(void) Settings.flag.decimal_text = 1; // SetOption17 - Switch between decimal or hexadecimal output - Respond with decimal color values Settings.flag3.hass_tele_on_power = 1; // SetOption59 - Send tele/%topic%/STATE in addition to stat/%topic%/RESULT - send tele/STATE message as stat/RESULT // Settings.light_scheme = 0; // To just control color it needs to be Scheme 0 - if (strcmp_P(Settings.mqtt_fulltopic, PSTR("%topic%/%prefix%/"))) { - strncpy_P(Settings.mqtt_fulltopic, PSTR("%topic%/%prefix%/"), sizeof(Settings.mqtt_fulltopic)); + if (strcmp_P(SettingsText(SET_MQTT_FULLTOPIC), PSTR("%topic%/%prefix%/"))) { + SettingsUpdateText(SET_MQTT_FULLTOPIC, "%topic%/%prefix%/"); restart_flag = 2; return; // As full topic has changed do restart first before sending discovery data } diff --git a/tasmota/xdrv_13_display.ino b/tasmota/xdrv_13_display.ino index 42bb9858b..b34b8ed27 100644 --- a/tasmota/xdrv_13_display.ino +++ b/tasmota/xdrv_13_display.ino @@ -1008,7 +1008,7 @@ void DisplayLogBufferInit(void) snprintf_P(buffer, sizeof(buffer), PSTR(D_CMND_HOSTNAME " %s"), my_hostname); DisplayLogBufferAdd(buffer); - snprintf_P(buffer, sizeof(buffer), PSTR(D_JSON_SSID " %s"), Settings.sta_ssid[Settings.sta_active]); + snprintf_P(buffer, sizeof(buffer), PSTR(D_JSON_SSID " %s"), SettingsText(SET_STASSID1 + Settings.sta_active)); DisplayLogBufferAdd(buffer); snprintf_P(buffer, sizeof(buffer), PSTR(D_JSON_MAC " %s"), WiFi.macAddress().c_str()); DisplayLogBufferAdd(buffer); @@ -1196,7 +1196,7 @@ void DisplayMqttSubscribe(void) char ntopic[TOPSZ]; ntopic[0] = '\0'; - strlcpy(stopic, Settings.mqtt_fulltopic, sizeof(stopic)); + strlcpy(stopic, SettingsText(SET_MQTT_FULLTOPIC), sizeof(stopic)); char *tp = strtok(stopic, "/"); while (tp != nullptr) { if (!strcmp_P(tp, MQTT_TOKEN_PREFIX)) { @@ -1205,7 +1205,7 @@ void DisplayMqttSubscribe(void) strncat_P(ntopic, PSTR("+/"), sizeof(ntopic) - strlen(ntopic) -1); // Add single-level wildcards tp = strtok(nullptr, "/"); } - strncat(ntopic, Settings.mqtt_prefix[2], sizeof(ntopic) - strlen(ntopic) -1); // Subscribe to tele messages + strncat(ntopic, SettingsText(SET_MQTTPREFIX3), sizeof(ntopic) - strlen(ntopic) -1); // Subscribe to tele messages strncat_P(ntopic, PSTR("/#"), sizeof(ntopic) - strlen(ntopic) -1); // Add multi-level wildcard MqttSubscribe(ntopic); disp_subscribed = true; @@ -1219,7 +1219,7 @@ bool DisplayMqttData(void) if (disp_subscribed) { char stopic[TOPSZ]; - snprintf_P(stopic, sizeof(stopic) , PSTR("%s/"), Settings.mqtt_prefix[2]); // tele/ + snprintf_P(stopic, sizeof(stopic) , PSTR("%s/"), SettingsText(SET_MQTTPREFIX3)); // tele/ char *tp = strstr(XdrvMailbox.topic, stopic); if (tp) { // tele/tasmota/SENSOR if (Settings.display_mode &0x04) { diff --git a/tasmota/xdrv_20_hue.ino b/tasmota/xdrv_20_hue.ino index 254fa4d99..973499a1f 100644 --- a/tasmota/xdrv_20_hue.ino +++ b/tasmota/xdrv_20_hue.ino @@ -365,17 +365,18 @@ void HueLightStatus1(uint8_t device, String *response) // Any device whose friendly name start with "$" is considered hidden bool HueActive(uint8_t device) { if (device > MAX_FRIENDLYNAMES) { device = MAX_FRIENDLYNAMES; } - return '$' != Settings.friendlyname[device-1][0]; +// return '$' != Settings.friendlyname[device-1][0]; + return '$' != *SettingsText(SET_FRIENDLYNAME1 +device -1]; } void HueLightStatus2(uint8_t device, String *response) { *response += FPSTR(HUE_LIGHTS_STATUS_JSON2); if (device <= MAX_FRIENDLYNAMES) { - response->replace("{j1", Settings.friendlyname[device-1]); + response->replace("{j1", SettingsText(SET_FRIENDLYNAME1 +device -1)); } else { char fname[33]; - strcpy(fname, Settings.friendlyname[MAX_FRIENDLYNAMES-1]); + strcpy(fname, SettingsText(SET_FRIENDLYNAME1 + MAX_FRIENDLYNAMES -1)); uint32_t fname_len = strlen(fname); if (fname_len > 30) { fname_len = 30; } fname[fname_len++] = '-'; diff --git a/tasmota/xdrv_21_wemo.ino b/tasmota/xdrv_21_wemo.ino index 4a50560e2..13e867ae6 100644 --- a/tasmota/xdrv_21_wemo.ino +++ b/tasmota/xdrv_21_wemo.ino @@ -241,7 +241,7 @@ void HandleUpnpSetupWemo(void) AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, PSTR(D_WEMO_SETUP)); String setup_xml = FPSTR(WEMO_SETUP_XML); - setup_xml.replace("{x1", Settings.friendlyname[0]); + setup_xml.replace("{x1", SettingsText(SET_FRIENDLYNAME1)); setup_xml.replace("{x2", WemoUuid()); setup_xml.replace("{x3", WemoSerialnumber()); WSSend(200, CT_XML, setup_xml); diff --git a/tasmota/xdrv_99_debug.ino b/tasmota/xdrv_99_debug.ino index b347035e1..22bc5e162 100644 --- a/tasmota/xdrv_99_debug.ino +++ b/tasmota/xdrv_99_debug.ino @@ -25,8 +25,6 @@ #endif // USE_DEBUG_DRIVER #endif // DEBUG_THEO -//#define USE_DEBUG_SETTING_NAMES - #ifdef USE_DEBUG_DRIVER /*********************************************************************************************\ * Virtual debugging support - Part1 @@ -64,9 +62,6 @@ const char kDebugCommands[] PROGMEM = "|" // No prefix D_CMND_CFGDUMP "|" D_CMND_CFGPEEK "|" D_CMND_CFGPOKE "|" -#ifdef USE_DEBUG_SETTING_NAMES - D_CMND_CFGSHOW "|" -#endif #ifdef USE_WEBSERVER D_CMND_CFGXOR "|" #endif @@ -82,9 +77,6 @@ const char kDebugCommands[] PROGMEM = "|" // No prefix void (* const DebugCommand[])(void) PROGMEM = { &CmndCfgDump, &CmndCfgPeek, &CmndCfgPoke, -#ifdef USE_DEBUG_SETTING_NAMES - &CmndCfgShow, -#endif #ifdef USE_WEBSERVER &CmndCfgXor, #endif @@ -391,36 +383,6 @@ void DebugCfgPoke(char* parms) AddLog_P2(LOG_LEVEL_INFO, PSTR("%03X: 0x%0LX (%lu) poked to 0x%0LX (%lu)"), address, data32, data32, ndata32, ndata32); } -#ifdef USE_DEBUG_SETTING_NAMES -void DebugCfgShow(uint8_t more) -{ - uint8_t *SetAddr; - SetAddr = (uint8_t *)&Settings; - - AddLog_P2(LOG_LEVEL_INFO, PSTR("%03X: Hostname (%d) [%s]"), (uint8_t *)&Settings.hostname - SetAddr, sizeof(Settings.hostname)-1, Settings.hostname); - AddLog_P2(LOG_LEVEL_INFO, PSTR("%03X: SSids (%d) [%s], [%s]"), (uint8_t *)&Settings.sta_ssid - SetAddr, sizeof(Settings.sta_ssid[0])-1, Settings.sta_ssid[0], Settings.sta_ssid[1]); - AddLog_P2(LOG_LEVEL_INFO, PSTR("%03X: Friendlynames (%d) [%s], [%s], [%s], [%s]"), (uint8_t *)&Settings.friendlyname - SetAddr, sizeof(Settings.friendlyname[0])-1, Settings.friendlyname[0], Settings.friendlyname[1], Settings.friendlyname[2], Settings.friendlyname[3]); - AddLog_P2(LOG_LEVEL_INFO, PSTR("%03X: OTA Url (%d) [%s]"), (uint8_t *)&Settings.ota_url - SetAddr, sizeof(Settings.ota_url)-1, Settings.ota_url); - AddLog_P2(LOG_LEVEL_INFO, PSTR("%03X: StateText (%d) [%s], [%s], [%s], [%s]"), (uint8_t *)&Settings.state_text - SetAddr, sizeof(Settings.state_text[0])-1, Settings.state_text[0], Settings.state_text[1], Settings.state_text[2], Settings.state_text[3]); - AddLog_P2(LOG_LEVEL_INFO, PSTR("%03X: Syslog Host (%d) [%s]"), (uint8_t *)&Settings.syslog_host - SetAddr, sizeof(Settings.syslog_host)-1, Settings.syslog_host); - AddLog_P2(LOG_LEVEL_INFO, PSTR("%03X: NTP Servers (%d) [%s], [%s], [%s]"), (uint8_t *)&Settings.ntp_server - SetAddr, sizeof(Settings.ntp_server[0])-1, Settings.ntp_server[0], Settings.ntp_server[1], Settings.ntp_server[2]); - AddLog_P2(LOG_LEVEL_INFO, PSTR("%03X: MQTT Host (%d) [%s]"), (uint8_t *)&Settings.mqtt_host - SetAddr, sizeof(Settings.mqtt_host)-1, Settings.mqtt_host); - AddLog_P2(LOG_LEVEL_INFO, PSTR("%03X: MQTT Client (%d) [%s]"), (uint8_t *)&Settings.mqtt_client - SetAddr, sizeof(Settings.mqtt_client)-1, Settings.mqtt_client); - AddLog_P2(LOG_LEVEL_INFO, PSTR("%03X: MQTT User (%d) [%s]"), (uint8_t *)&Settings.mqtt_user - SetAddr, sizeof(Settings.mqtt_user)-1, Settings.mqtt_user); - AddLog_P2(LOG_LEVEL_INFO, PSTR("%03X: MQTT FullTopic (%d) [%s]"), (uint8_t *)&Settings.mqtt_fulltopic - SetAddr, sizeof(Settings.mqtt_fulltopic)-1, Settings.mqtt_fulltopic); - AddLog_P2(LOG_LEVEL_INFO, PSTR("%03X: MQTT Topic (%d) [%s]"), (uint8_t *)&Settings.mqtt_topic - SetAddr, sizeof(Settings.mqtt_topic)-1, Settings.mqtt_topic); - AddLog_P2(LOG_LEVEL_INFO, PSTR("%03X: MQTT GroupTopic (%d) [%s]"), (uint8_t *)&Settings.mqtt_grptopic - SetAddr, sizeof(Settings.mqtt_grptopic)-1, Settings.mqtt_grptopic); - AddLog_P2(LOG_LEVEL_INFO, PSTR("%03X: MQTT ButtonTopic (%d) [%s]"), (uint8_t *)&Settings.button_topic - SetAddr, sizeof(Settings.button_topic)-1, Settings.button_topic); - AddLog_P2(LOG_LEVEL_INFO, PSTR("%03X: MQTT SwitchTopic (%d) [%s]"), (uint8_t *)&Settings.switch_topic - SetAddr, sizeof(Settings.switch_topic)-1, Settings.switch_topic); - AddLog_P2(LOG_LEVEL_INFO, PSTR("%03X: MQTT Prefixes (%d) [%s], [%s], [%s]"), (uint8_t *)&Settings.mqtt_prefix - SetAddr, sizeof(Settings.mqtt_prefix[0])-1, Settings.mqtt_prefix[0], Settings.mqtt_prefix[1], Settings.mqtt_prefix[2]); - if (17 == more) { - AddLog_P2(LOG_LEVEL_INFO, PSTR("%03X: AP Passwords (%d) [%s], [%s]"), (uint8_t *)&Settings.sta_pwd - SetAddr, sizeof(Settings.sta_pwd[0])-1, Settings.sta_pwd[0], Settings.sta_pwd[1]); - AddLog_P2(LOG_LEVEL_INFO, PSTR("%03X: MQTT Password (%d) [%s]"), (uint8_t *)&Settings.mqtt_pwd - SetAddr, sizeof(Settings.mqtt_pwd)-1, Settings.mqtt_pwd); - AddLog_P2(LOG_LEVEL_INFO, PSTR("%03X: Web Password (%d) [%s]"), (uint8_t *)&Settings.web_password - SetAddr, sizeof(Settings.web_password)-1, Settings.web_password); - } -} -#endif // USE_DEBUG_SETTING_NAMES - void SetFlashMode(uint8_t mode) { uint8_t *_buffer; @@ -474,14 +436,6 @@ void CmndCfgPoke(void) ResponseCmndDone(); } -#ifdef USE_DEBUG_SETTING_NAMES -void CmndCfgShow(void) -{ - DebugCfgShow(XdrvMailbox.payload); - ResponseCmndDone(); -} -#endif // USE_DEBUG_SETTING_NAMES - #ifdef USE_WEBSERVER void CmndCfgXor(void) { From e47c8710fe428b6077b72b32781ac1f5f3079f4a Mon Sep 17 00:00:00 2001 From: bjeram Date: Mon, 16 Dec 2019 16:55:52 +0100 Subject: [PATCH 79/84] setting pinMode for the pin used by RFRecv to INPUT --- tasmota/xdrv_17_rcswitch.ino | 1 + 1 file changed, 1 insertion(+) diff --git a/tasmota/xdrv_17_rcswitch.ino b/tasmota/xdrv_17_rcswitch.ino index dfe5dc66a..156535876 100644 --- a/tasmota/xdrv_17_rcswitch.ino +++ b/tasmota/xdrv_17_rcswitch.ino @@ -85,6 +85,7 @@ void RfInit(void) mySwitch.enableTransmit(pin[GPIO_RFSEND]); } if (pin[GPIO_RFRECV] < 99) { + pinMode( pin[GPIO_RFRECV], INPUT); mySwitch.enableReceive(pin[GPIO_RFRECV]); } } From c1bb875abd7fd89e44d6ab21bf9137401fad02d8 Mon Sep 17 00:00:00 2001 From: Adrian Scillato <39969427+ascillato2@users.noreply.github.com> Date: Mon, 16 Dec 2019 14:37:18 -0300 Subject: [PATCH 80/84] Fix typo --- tasmota/xdrv_20_hue.ino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tasmota/xdrv_20_hue.ino b/tasmota/xdrv_20_hue.ino index 973499a1f..1d13ee263 100644 --- a/tasmota/xdrv_20_hue.ino +++ b/tasmota/xdrv_20_hue.ino @@ -366,7 +366,7 @@ void HueLightStatus1(uint8_t device, String *response) bool HueActive(uint8_t device) { if (device > MAX_FRIENDLYNAMES) { device = MAX_FRIENDLYNAMES; } // return '$' != Settings.friendlyname[device-1][0]; - return '$' != *SettingsText(SET_FRIENDLYNAME1 +device -1]; + return '$' != *SettingsText(SET_FRIENDLYNAME1 +device -1); } void HueLightStatus2(uint8_t device, String *response) From 98579fba0f0662cb7a8277740bfaa24cc7a9f89f Mon Sep 17 00:00:00 2001 From: Adrian Scillato <35405447+ascillato@users.noreply.github.com> Date: Mon, 16 Dec 2019 21:23:25 -0300 Subject: [PATCH 81/84] KNX: Fix compilation issue when using KNX on Tasmota-IR firmware type --- tasmota/xdrv_11_knx.ino | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tasmota/xdrv_11_knx.ino b/tasmota/xdrv_11_knx.ino index c848bddfe..b52c37e4d 100644 --- a/tasmota/xdrv_11_knx.ino +++ b/tasmota/xdrv_11_knx.ino @@ -499,6 +499,7 @@ void KNX_INIT(void) if (GetUsedInModule(GPIO_DHT22, my_module.io)) { device_param[KNX_HUMIDITY-1].show = true; } if (GetUsedInModule(GPIO_SI7021, my_module.io)) { device_param[KNX_HUMIDITY-1].show = true; } +#if defined(USE_ENERGY_SENSOR) // Any device with a Power Monitoring if ( energy_flg != ENERGY_NONE ) { device_param[KNX_ENERGY_POWER-1].show = true; @@ -509,6 +510,7 @@ void KNX_INIT(void) device_param[KNX_ENERGY_CURRENT-1].show = true; device_param[KNX_ENERGY_POWERFACTOR-1].show = true; } +#endif #ifdef USE_RULES device_param[KNX_SLOT1-1].show = true; From efc7dc2388988c6233cbf97cb5b65f36ea30976b Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Tue, 17 Dec 2019 10:54:38 +0100 Subject: [PATCH 82/84] Add fallback functionality from next version Add fallback functionality from next version 8.x --- tasmota/CHANGELOG.md | 2 +- tasmota/settings.ino | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tasmota/CHANGELOG.md b/tasmota/CHANGELOG.md index a466a3212..ad2ad30d4 100644 --- a/tasmota/CHANGELOG.md +++ b/tasmota/CHANGELOG.md @@ -5,7 +5,7 @@ - Change some more Settings locations freeing up space for future single char allowing variable length text - Add Zigbee send automatic ZigbeeRead after sending a command - Add Zigbee improving Occupancy:false detection for Aqara sensor -- Add fallback functionality from next version 7.1.2.7 +- Add fallback functionality from next version 8.x ### 7.1.2.5 20191213 diff --git a/tasmota/settings.ino b/tasmota/settings.ino index d1d3fc1e8..d5c59246f 100644 --- a/tasmota/settings.ino +++ b/tasmota/settings.ino @@ -471,7 +471,7 @@ bool SettingsUpdateText(uint32_t index, const char* replace_me) char replace[replace_len +1]; memcpy(replace, replace_me, sizeof(replace)); - if (Settings.version < 0x07010207) { + if (Settings.version < 0x08000000) { uint32_t idx = 0; switch (index) { case SET_OTAURL: strlcpy(Settings.ota_url, replace, sizeof(Settings.ota_url)); break; @@ -575,7 +575,7 @@ char* SettingsText(uint32_t index) char* position = Settings.ota_url; - if (Settings.version < 0x07010207) { + if (Settings.version < 0x08000000) { uint32_t idx = 0; switch (index) { case SET_MQTTPREFIX3: idx++; @@ -1387,7 +1387,7 @@ void SettingsDelta(void) memcpy((char*)&Settings.serial_config, (char*)&Settings.ex_serial_config, 5); // 1E4 -> EFE } - if ((VERSION < 0x7010207) && (Settings.version > VERSION)) { + if ((VERSION < 0x08000000) && (Settings.version > VERSION)) { char temp[strlen(SettingsText(SET_OTAURL)) +1]; strncpy(temp, SettingsText(SET_OTAURL), sizeof(temp)); char temp21[strlen(SettingsText(SET_MQTTPREFIX1)) +1]; strncpy(temp21, SettingsText(SET_MQTTPREFIX1), sizeof(temp21)); char temp22[strlen(SettingsText(SET_MQTTPREFIX2)) +1]; strncpy(temp22, SettingsText(SET_MQTTPREFIX2), sizeof(temp22)); From 359770c23eedf86f5c14ed098dde5cc12c7bb089 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Tue, 17 Dec 2019 11:43:22 +0100 Subject: [PATCH 83/84] Update support_statistics.ino --- tasmota/support_statistics.ino | 38 +++++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/tasmota/support_statistics.ino b/tasmota/support_statistics.ino index 1907b1edc..78334e7eb 100644 --- a/tasmota/support_statistics.ino +++ b/tasmota/support_statistics.ino @@ -27,7 +27,43 @@ String GetStatistics(void) { char data[40]; - snprintf_P(data, sizeof(data), PSTR(",\"CR\":\"%d/%d\""), GetSettingsTextLen(), settings_text_size); // Char Usage Ratio + + if (Settings.version < 0x08000000) { + uint32_t str_len = 0; + for (uint32_t i = 0; i < 2; i++) { + str_len += strlen(Settings.sta_ssid[i]); + str_len += strlen(Settings.sta_pwd[i]); + } + for (uint32_t i = 0; i < 3; i++) { + str_len += strlen(Settings.mqtt_prefix[i]); + str_len += strlen(Settings.ntp_server[i]); + } + for (uint32_t i = 0; i < 4; i++) { + str_len += strlen(Settings.state_text[i]); + str_len += strlen(Settings.friendlyname[i]); + } + for (uint32_t i = 0; i < MAX_RULE_MEMS; i++) { + str_len += strlen(Settings.mems[i]); + } + + str_len += strlen(Settings.ota_url); + str_len += strlen(Settings.hostname); + str_len += strlen(Settings.syslog_host); + str_len += strlen(Settings.mqtt_host); + str_len += strlen(Settings.mqtt_client); + str_len += strlen(Settings.mqtt_user); + str_len += strlen(Settings.mqtt_pwd); + str_len += strlen(Settings.mqtt_topic); + str_len += strlen(Settings.button_topic); + str_len += strlen(Settings.switch_topic); + str_len += strlen(Settings.mqtt_grptopic); + str_len += strlen(Settings.web_password); + str_len += strlen(Settings.mqtt_fulltopic); + str_len += strlen(Settings.cors_domain); + snprintf_P(data, sizeof(data), PSTR(",\"CR\":\"%d/1151\""), str_len); // Char Usage Ratio + } else { + snprintf_P(data, sizeof(data), PSTR(",\"CR\":\"%d/%d\""), GetSettingsTextLen(), settings_text_size); // Char Usage Ratio + } return String(data); } From 78b1363add7637e853a98f5ce417451cd36a1211 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Tue, 17 Dec 2019 12:08:32 +0100 Subject: [PATCH 84/84] Update support_statistics.ino --- tasmota/support_statistics.ino | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tasmota/support_statistics.ino b/tasmota/support_statistics.ino index 78334e7eb..b8f65bd20 100644 --- a/tasmota/support_statistics.ino +++ b/tasmota/support_statistics.ino @@ -45,7 +45,6 @@ String GetStatistics(void) for (uint32_t i = 0; i < MAX_RULE_MEMS; i++) { str_len += strlen(Settings.mems[i]); } - str_len += strlen(Settings.ota_url); str_len += strlen(Settings.hostname); str_len += strlen(Settings.syslog_host); @@ -60,7 +59,7 @@ String GetStatistics(void) str_len += strlen(Settings.web_password); str_len += strlen(Settings.mqtt_fulltopic); str_len += strlen(Settings.cors_domain); - snprintf_P(data, sizeof(data), PSTR(",\"CR\":\"%d/1151\""), str_len); // Char Usage Ratio + snprintf_P(data, sizeof(data), PSTR(",\"CR\":\"%d/1151\""), 37 + str_len); // Char Usage Ratio } else { snprintf_P(data, sizeof(data), PSTR(",\"CR\":\"%d/%d\""), GetSettingsTextLen(), settings_text_size); // Char Usage Ratio }