Merge branch 'development' into pre-release-9.1.0

This commit is contained in:
Theo Arends 2020-11-02 14:48:47 +01:00
commit 1838c582b3
51 changed files with 1390 additions and 903 deletions

View File

@ -134,6 +134,7 @@
| USE_MCP9808 | - | - | - | - | - | - | - |
| USE_HP303B | - | - | - | - | - | - | - |
| USE_EZOCO2 | - | - | - | - | - | - | - |
| USE_EZODO | - | - | - | - | - | - | - |
| USE_EZOEC | - | - | - | - | - | - | - |
| USE_EZOFLO | - | - | - | - | - | - | - |
| USE_EZOHUM | - | - | - | - | - | - | - |

View File

@ -15,11 +15,13 @@ All notable changes to this project will be documented in this file.
- Support for EZO O2 sensors by Christopher Tremblay (#9619)
- Support for EZO PRS sensors by Christopher Tremblay (#9659)
- Support for EZO FLO sensors by Christopher Tremblay (#9697)
- Support for EZO DO sensors by Christopher Tremblay (#9707)
- Zigbee reduce battery drain (#9642)
- Zigbee command ``ZbMap`` to describe Zigbee topology (#9651)
- Zigbee command ``ZbOccupancy`` to configure the time-out for PIR
- Command ``Gpios 255`` to show all possible GPIO configurations
- Command ``SwitchText`` to change JSON switch names by barbudor (#9691)
- HM10 Beacon support and refactoring by Christian Baars (#9702)
### Changed
- PlatformIO library structure redesigned for compilation speed by Jason2866
@ -29,6 +31,8 @@ All notable changes to this project will be documented in this file.
### Fixed
- Rule Break not working as expected when ONCE is enabled (#9245)
- Rule expressions using mems corrupts character pool (#9301)
- Button press rules regression introduced by #9589 (#9700)
- Rule handling of JSON ``null`` regression from v8.5.0.1 (#9685)
## [9.0.0.2] - 20201025
### Added
@ -45,9 +49,10 @@ All notable changes to this project will be documented in this file.
- Support for EZO CO2 sensors by Christopher Tremblay (#9619)
- On ZigbeeBridge support for glowing led when permit join is active (#9581)
- Support for PWM Dimmer multi-press and ledmask (#9584)
- Make button press rules override PWM Dimmer functions (#9589)
- Support for fixed output Hi or Lo GPIO selection
- ESP32 support for Wireless-Tag WT32-ETH01 (#9496)
- ESP32 MI32 Beacon support, RSSI at TELEPERIOD, refactoring (#9609)
- ESP32 MI32 Beacon support, RSSI at TELEPERIOD, refactoring by Christian Baars (#9609)
### Changed
- Command ``Gpio17`` replaces command ``Adc``

View File

@ -86,3 +86,4 @@ Index | Define | Driver | Device | Address(es) | Description
55 | USE_EZOO2 | xsns_78 | EZOO2 | 0x61 - 0x70 | O2 sensor
55 | USE_EZOPRS | xsns_78 | EZOPRS | 0x61 - 0x70 | Pressure sensor
55 | USE_EZOFLO | xsns_78 | EZOFLO | 0x61 - 0x70 | Flow meter sensor
55 | USE_EZODO | xsns_78 | EZODO | 0x61 - 0x70 | Disolved Oxygen sensor

View File

@ -81,7 +81,8 @@ The attached binaries can also be downloaded from http://ota.tasmota.com/tasmota
- TLS in binary tasmota-zbbridge (#9620)
- Zigbee reduce battery drain (#9642)
- ESP32 support for Wireless-Tag WT32-ETH01 (#9496)
- ESP32 MI32 Beacon support, RSSI at TELEPERIOD, refactoring (#9609)
- ESP32 MI32 Beacon support, RSSI at TELEPERIOD, refactoring by Christian Baars (#9609)
- HM10 Beacon support and refactoring by Christian Baars (#9702)
### Breaking Changed
- Redesigned ESP8266 GPIO internal representation in line with ESP32 changing ``Template`` layout too
@ -113,6 +114,7 @@ The attached binaries can also be downloaded from http://ota.tasmota.com/tasmota
- Thermostat sensor status corruption regression from v8.5.0.1 (#9449)
- Telegram message decoding error regression from v8.5.0.1
- Rule handling of Var or Mem using text regression from v8.5.0.1 (#9540)
- Rule handling of JSON ``null`` regression from v8.5.0.1 (#9685)
- Correct Energy period display shortly after midnight by gominoa (#9536)
- TuyaMcu energy display regression from v8.5.0.1 (#9547)
- Tuyamcu dimmers MQTT topic (#9606)

View File

@ -28,7 +28,7 @@ const char * k_current_json_buffer = "";
/*********************************************************************************************\
* Lightweight String to Float, because atof() or strtof() takes 10KB
*
*
* To remove code, exponents are not parsed
* (commented out below, just in case we need them after all)
\*********************************************************************************************/
@ -51,7 +51,7 @@ float json_strtof(const char* s) {
}
while ((unsigned int)(*p - '0') < 10u) {
value = value*10 + (*p++ - '0');
value = value*10 + (*p++ - '0');
}
if (*p == '.' ) {
@ -335,7 +335,7 @@ float JsonParserToken::getFloat(float val) const {
}
const char * JsonParserToken::getStr(const char * val) const {
if (t->type == JSMN_INVALID) { return val; }
if (t->type == JSMN_NULL) return "";
if (t->type == JSMN_NULL) return "null";
return (t->type >= JSMN_STRING) ? &k_current_json_buffer[t->start] : val;
}
@ -476,7 +476,7 @@ JsonParserToken JsonParserObject::findStartsWith(const char * needle) const {
if ((!this->isValid()) || (nullptr == needle) || (0 == pgm_read_byte(needle))) {
return JsonParserToken(&token_bad);
}
String needle_s((const __FlashStringHelper *)needle);
needle_s.toLowerCase();

View File

@ -1,146 +1,146 @@
; PlatformIO Project Configuration File
;
; Build options: build flags, source filter, extra scripting
; Upload options: custom port, speed and extra flags
; Library options: dependencies, extra library storages
;
; Please visit documentation for the other options and examples
; http://docs.platformio.org/en/stable/projectconf.html
; *** Tasmota build variant selection
[build_envs]
default_envs =
; *** Uncomment by deleting ";" in the line(s) below to select version(s)
; tasmota
; tasmota-ircustom
; tasmota-minimal
; tasmota-lite
; tasmota-knx
; tasmota-sensors
; tasmota-display
; tasmota-zbbridge
; tasmota-ir
; tasmota-BG
; tasmota-BR
; tasmota-CN
; tasmota-CZ
; tasmota-DE
; tasmota-ES
; tasmota-FR
; tasmota-GR
; tasmota-HE
; tasmota-HU
; tasmota-IT
; tasmota-KO
; tasmota-NL
; tasmota-PL
; tasmota-PT
; tasmota-RO
; tasmota-RU
; tasmota-SE
; tasmota-SK
; tasmota-TR
; tasmota-TW
; tasmota-UK
; tasmota-VN
;
; *** Selection for Tasmota ESP32 is done in platformio_tasmota32.ini
;
; *** alternatively can be done in: platformio_override.ini
; *** See example: platformio_override_sample.ini
; *********************************************************************
[platformio]
description = Provide ESP8266 / ESP32 based devices with Web, MQTT and OTA firmware
src_dir = tasmota
lib_dir = lib/default
build_cache_dir = .cache
extra_configs = platformio_tasmota32.ini
platformio_tasmota_env.ini
platformio_tasmota_env32.ini
platformio_override.ini
default_envs = ${build_envs.default_envs}
[common]
framework = arduino
board = esp01_1m
board_build.flash_mode = dout
board_build.ldscript = eagle.flash.1m.ld
platform = ${core.platform}
platform_packages = ${core.platform_packages}
build_unflags = ${core.build_unflags}
build_flags = ${core.build_flags}
board_build.f_cpu = 80000000L
board_build.f_flash = 40000000L
monitor_speed = 115200
upload_speed = 115200
; *** Upload Serial reset method for Wemos and NodeMCU
upload_resetmethod = nodemcu
upload_port = COM5
extra_scripts = ${scripts_defaults.extra_scripts}
lib_ldf_mode = chain+
shared_libdeps_dir = lib
lib_extra_dirs =
lib/lib_basic
lib/lib_i2c
lib/lib_display
lib/lib_ssl
lib/lib_audio
lib/lib_rf
lib/lib_div
[scripts_defaults]
extra_scripts = pio/strip-floats.py
pio/name-firmware.py
pio/gzip-firmware.py
pio/override_copy.py
[esp_defaults]
; *** remove undesired all warnings
build_unflags = -Wall
-Wdeprecated-declarations
build_flags = -Wno-deprecated-declarations
-D_IR_ENABLE_DEFAULT_=false
-DDECODE_HASH=true -DDECODE_NEC=true -DSEND_NEC=true
-DDECODE_RC5=true -DSEND_RC5=true -DDECODE_RC6=true -DSEND_RC6=true
; new mechanism to set the IRremoteESP8266 supported protocols: none except HASH, NEC, RC5, RC6
; *********************************************************************
; *** Use custom settings from file user_config_override.h
-DUSE_CONFIG_OVERRIDE
; *********************************************************************
[esp82xx_defaults]
build_flags = ${esp_defaults.build_flags}
-Wl,-Map,firmware.map
-D CORE_DEBUG_LEVEL=0
-D NDEBUG
-mtarget-align
-DFP_IN_IROM
-DBEARSSL_SSL_BASIC
; NONOSDK22x_190703 = 2.2.2-dev(38a443e)
-DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK22x_190703
; lwIP 2 - Higher Bandwidth no Features
-DPIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH_LOW_FLASH
; VTABLES in Flash
-DVTABLES_IN_FLASH
; remove the 4-bytes alignment for PSTR()
-DPSTR_ALIGN=1
; restrict to minimal mime-types
-DMIMETYPE_MINIMAL
[irremoteesp_full]
build_flags = -DUSE_IR_REMOTE_FULL
-U_IR_ENABLE_DEFAULT_
-DDECODE_PRONTO=false -DSEND_PRONTO=false
[core]
; *** Esp8266 Tasmota modified Arduino core based on core 2.7.4
platform = espressif8266@2.6.2
platform_packages = framework-arduinoespressif8266@https://github.com/tasmota/Arduino/releases/download/2.7.4.5/esp8266-2.7.4.5.zip
platformio/tool-esptool @ 1.413.0
build_unflags = ${esp_defaults.build_unflags}
build_flags = ${esp82xx_defaults.build_flags}
; PlatformIO Project Configuration File
;
; Build options: build flags, source filter, extra scripting
; Upload options: custom port, speed and extra flags
; Library options: dependencies, extra library storages
;
; Please visit documentation for the other options and examples
; http://docs.platformio.org/en/stable/projectconf.html
; *** Tasmota build variant selection
[build_envs]
default_envs =
; *** Uncomment by deleting ";" in the line(s) below to select version(s)
; tasmota
; tasmota-ircustom
; tasmota-minimal
; tasmota-lite
; tasmota-knx
; tasmota-sensors
; tasmota-display
; tasmota-zbbridge
; tasmota-ir
; tasmota-BG
; tasmota-BR
; tasmota-CN
; tasmota-CZ
; tasmota-DE
; tasmota-ES
; tasmota-FR
; tasmota-GR
; tasmota-HE
; tasmota-HU
; tasmota-IT
; tasmota-KO
; tasmota-NL
; tasmota-PL
; tasmota-PT
; tasmota-RO
; tasmota-RU
; tasmota-SE
; tasmota-SK
; tasmota-TR
; tasmota-TW
; tasmota-UK
; tasmota-VN
;
; *** Selection for Tasmota ESP32 is done in platformio_tasmota32.ini
;
; *** alternatively can be done in: platformio_override.ini
; *** See example: platformio_override_sample.ini
; *********************************************************************
[platformio]
description = Provide ESP8266 / ESP32 based devices with Web, MQTT and OTA firmware
src_dir = tasmota
lib_dir = lib/default
build_cache_dir = .cache
extra_configs = platformio_tasmota32.ini
platformio_tasmota_env.ini
platformio_tasmota_env32.ini
platformio_override.ini
default_envs = ${build_envs.default_envs}
[common]
framework = arduino
board = esp01_1m
board_build.flash_mode = dout
board_build.ldscript = eagle.flash.1m.ld
platform = ${core.platform}
platform_packages = ${core.platform_packages}
build_unflags = ${core.build_unflags}
build_flags = ${core.build_flags}
board_build.f_cpu = 80000000L
board_build.f_flash = 40000000L
monitor_speed = 115200
upload_speed = 115200
; *** Upload Serial reset method for Wemos and NodeMCU
upload_resetmethod = nodemcu
upload_port = COM5
extra_scripts = ${scripts_defaults.extra_scripts}
lib_ldf_mode = chain+
shared_libdeps_dir = lib
lib_extra_dirs =
lib/lib_basic
lib/lib_i2c
lib/lib_display
lib/lib_ssl
lib/lib_audio
lib/lib_rf
lib/lib_div
[scripts_defaults]
extra_scripts = pio/strip-floats.py
pio/name-firmware.py
pio/gzip-firmware.py
pio/override_copy.py
[esp_defaults]
; *** remove undesired all warnings
build_unflags = -Wall
-Wdeprecated-declarations
build_flags = -Wno-deprecated-declarations
-D_IR_ENABLE_DEFAULT_=false
-DDECODE_HASH=true -DDECODE_NEC=true -DSEND_NEC=true
-DDECODE_RC5=true -DSEND_RC5=true -DDECODE_RC6=true -DSEND_RC6=true
; new mechanism to set the IRremoteESP8266 supported protocols: none except HASH, NEC, RC5, RC6
; *********************************************************************
; *** Use custom settings from file user_config_override.h
-DUSE_CONFIG_OVERRIDE
; *********************************************************************
[esp82xx_defaults]
build_flags = ${esp_defaults.build_flags}
-Wl,-Map,firmware.map
-D CORE_DEBUG_LEVEL=0
-D NDEBUG
-mtarget-align
-DFP_IN_IROM
-DBEARSSL_SSL_BASIC
; NONOSDK22x_190703 = 2.2.2-dev(38a443e)
-DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK22x_190703
-DPIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH_LOW_FLASH
; VTABLES in Flash
-DVTABLES_IN_FLASH
; remove the 4-bytes alignment for PSTR()
-DPSTR_ALIGN=1
; restrict to minimal mime-types
-DMIMETYPE_MINIMAL
[irremoteesp_full]
build_flags = -DUSE_IR_REMOTE_FULL
-U_IR_ENABLE_DEFAULT_
-DDECODE_PRONTO=false -DSEND_PRONTO=false
[core]
; *** Esp8266 Tasmota modified Arduino core based on core 2.7.4
platform = espressif8266@2.6.2
platform_packages = framework-arduinoespressif8266@https://github.com/tasmota/Arduino/releases/download/2.7.4.5/esp8266-2.7.4.5.zip
platformio/tool-esptool @ 1.413.0
build_unflags = ${esp_defaults.build_unflags}
build_flags = ${esp82xx_defaults.build_flags}

View File

@ -1,200 +1,234 @@
; *** Example PlatformIO Project Configuration Override File ***
; *** Changes done here override settings in platformio.ini ***
;
; *****************************************************************
; *** to activate rename this file to platformio_override.ini ***
; *****************************************************************
;
; Please visit documentation for the options and examples
; http://docs.platformio.org/en/stable/projectconf.html
[platformio]
; For best Gitpod performance remove the ";" in the next line. Needed Platformio files are cached and installed at first run
;core_dir = .platformio
extra_configs = platformio_tasmota_cenv.ini
; *** Build/upload environment
default_envs =
; *** Uncomment the line(s) below to select version(s)
tasmota
; tasmota-debug
; tasmota-ircustom
; tasmota-minimal
; tasmota-lite
; tasmota-knx
; tasmota-sensors
; tasmota-display
; tasmota-zbbridge
; tasmota-ir
; tasmota32
; tasmota32-webcam
; tasmota32-minimal
; tasmota32-lite
; tasmota32-knx
; tasmota32-sensors
; tasmota32-display
; tasmota32-ir
; tasmota32-ircustom
[common]
platform_packages = ${core.platform_packages}
build_unflags = ${core.build_unflags}
build_flags = ${core.build_flags}
; *** Optional Debug messages
; -DDEBUG_TASMOTA_CORE
; -DDEBUG_TASMOTA_DRIVER
; -DDEBUG_TASMOTA_SENSOR
; set CPU frequency to 80MHz (default) or 160MHz
;board_build.f_cpu = 160000000L
; set Flash chip frequency to 40MHz (default), 20MHz, 26Mhz, 80Mhz
;board_build.f_flash = 20000000L
;board_build.f_flash = 26000000L
;board_build.f_flash = 80000000L
; *** Upload Serial reset method for Wemos and NodeMCU
upload_port = COM5
extra_scripts = ${scripts_defaults.extra_scripts}
; pio/obj-dump.py
; *** Upload file to OTA server using SCP
;upload_port = user@host:/path
;extra_scripts = ${scripts_defaults.extra_scripts}
; pio/strip-floats.py
; pio/sftp-uploader.py
; *** Upload file to OTA server in folder api/arduino using HTTP
;upload_port = domus1:80/api/upload-arduino.php
;extra_scripts = ${scripts_defaults.extra_scripts}
; pio/strip-floats.py
; pio/http-uploader.py
lib_ldf_mode = chain+
shared_libdeps_dir = lib
; *** Library disable / enable for variant Tasmota. Disable reduces compile time
; *** !!! Disabling needed libs will generate compile errors !!!
; *** The resulting firmware will NOT be different if you leave all libs enabled
; *** Disabling by putting a ";" in front of the lib name
; *** If you dont know what it is all about, do not change
lib_extra_dirs =
; *** Only disabled for Tasmota minimal and Tasmota light. For all other variants needed!
lib/lib_basic
; **** I2C devices. Most sensors. Disable only if you dont have ANY I2C device enabled
lib/lib_i2c
; *** Displays. Disable if you dont have any Display activated
lib/lib_display
; *** Bear SSL and base64. Disable if you dont have SSL or TLS activated
lib/lib_ssl
; *** Audio needs a lot of time to compile. Mostly not used functions. Recommended to disable
lib/lib_audio
; *** RF 433 stuff (not RF Bridge). Recommended to disable
lib/lib_rf
; *** Mostly not used functions. Recommended to disable
lib/lib_div
[core]
; Activate only (one set) if you want to override the standard core defined in platformio.ini !!!
;platform_packages = ${tasmota_stage.platform_packages}
;build_unflags = ${tasmota_stage.build_unflags}
;build_flags = ${tasmota_stage.build_flags}
;platform_packages = ${core_stage.platform_packages}
;build_unflags = ${core_stage.build_unflags}
;build_flags = ${core_stage.build_flags}
[tasmota_stage]
; *** Esp8266 core for Arduino version Tasmota stage (PR7231 and Backport PR7514)
platform_packages = framework-arduinoespressif8266@https://github.com/Jason2866/Arduino.git#2.7.4.4
build_unflags = ${esp_defaults.build_unflags}
build_flags = ${esp82xx_defaults.build_flags}
; *********** Alternative Options, enable only if you know exactly what you do ********
; NONOSDK221
; -DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK221
; NONOSDK22x_190313
; -DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK22x_190313
; NONOSDK22x_191024 = 2.2.1+111-dev(5ab15d1)
; -DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK22x_191024
; 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
; -DPIO_FRAMEWORK_ARDUINO_LWIP_HIGHER_BANDWIDTH
; lwIP 2 - Low Memory
; -DPIO_FRAMEWORK_ARDUINO_LWIP2_LOW_MEMORY
; lwIP 2 - Higher Bandwidth
; -DPIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH
; lwIP 2 - Higher Bandwidth Low Memory no Features
; -DPIO_FRAMEWORK_ARDUINO_LWIP2_LOW_MEMORY_LOW_FLASH
; VTABLES in Heap
; -DVTABLES_IN_DRAM
; VTABLES in IRAM
; -DVTABLES_IN_IRAM
; Exception code in firmware /needs much space!
; -fexceptions
; -lstdc++-exc
[core_stage]
; *** Esp8266 core version. Tasmota stage or Arduino stage version. Built with GCC 10.1 toolchain
platform_packages = framework-arduinoespressif8266 @ https://github.com/Jason2866/platform-espressif8266/releases/download/2.9.1/framework-arduinoespressif8266-3.20901.0.tar.gz
;framework-arduinoespressif8266 @ https://github.com/esp8266/Arduino.git
toolchain-xtensa @ ~2.100100.0
build_unflags = ${esp_defaults.build_unflags}
-Wswitch-unreachable
build_flags = ${esp82xx_defaults.build_flags}
-Wno-switch-unreachable
; *********** Alternative Options, enable only if you know exactly what you do ********
; NONOSDK221
; -DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK221
; NONOSDK22x_190313
; -DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK22x_190313
; NONOSDK22x_191024 = 2.2.1+111-dev(5ab15d1)
; -DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK22x_191024
; 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 2 - Low Memory
; -DPIO_FRAMEWORK_ARDUINO_LWIP2_LOW_MEMORY
; lwIP 2 - Higher Bandwidth
; -DPIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH
; lwIP 2 - Higher Bandwidth Low Memory no Features
; -DPIO_FRAMEWORK_ARDUINO_LWIP2_LOW_MEMORY_LOW_FLASH
; VTABLES in Heap
; -DVTABLES_IN_DRAM
; VTABLES in IRAM
; -DVTABLES_IN_IRAM
; Exception code in firmware /needs much space!
; -fexceptions
; -lstdc++-exc
[core32]
; Activate Stage Core32 by removing ";" in next lines, if you want to override the standard core32
;platform_packages = ${core32_stage.platform_packages}
;build_flags = ${core32_stage.build_flags}
[core32_stage]
platform_packages = tool-esptoolpy@1.20800.0
framework-arduinoespressif32 @ https://github.com/espressif/arduino-esp32.git#f7fb00632e04d74a7890a77fa7dbbb8ae572e866
build_flags = ${esp32_defaults.build_flags}
-D ESP32_STAGE=true
; *** Debug version used for PlatformIO Home Project Inspection
[env:tasmota-debug]
build_type = debug
build_unflags = ${esp_defaults.build_unflags}
build_flags = ${esp82xx_defaults.build_flags}
-Wstack-usage=300
; *** Example PlatformIO Project Configuration Override File ***
; *** Changes done here override settings in platformio.ini ***
;
; *****************************************************************
; *** to activate rename this file to platformio_override.ini ***
; *****************************************************************
;
; Please visit documentation for the options and examples
; http://docs.platformio.org/en/stable/projectconf.html
[platformio]
; For best Gitpod performance remove the ";" in the next line. Needed Platformio files are cached and installed at first run
;core_dir = .platformio
extra_configs = platformio_tasmota_cenv.ini
; *** Build/upload environment
default_envs =
; *** Uncomment the line(s) below to select version(s)
tasmota
; tasmota-debug
; tasmota-ircustom
; tasmota-minimal
; tasmota-lite
; tasmota-knx
; tasmota-sensors
; tasmota-display
; tasmota-zbbridge
; tasmota-ir
; tasmota32
; tasmota32-webcam
; tasmota32-minimal
; tasmota32-lite
; tasmota32-knx
; tasmota32-sensors
; tasmota32-display
; tasmota32-ir
; tasmota32-ircustom
[common]
platform_packages = ${core.platform_packages}
build_unflags = ${core.build_unflags}
build_flags = ${core.build_flags}
; *** Optional Debug messages
; -DDEBUG_TASMOTA_CORE
; -DDEBUG_TASMOTA_DRIVER
; -DDEBUG_TASMOTA_SENSOR
; set CPU frequency to 80MHz (default) or 160MHz
;board_build.f_cpu = 160000000L
; set Flash chip frequency to 40MHz (default), 20MHz, 26Mhz, 80Mhz
;board_build.f_flash = 20000000L
;board_build.f_flash = 26000000L
;board_build.f_flash = 80000000L
; *** Upload Serial reset method for Wemos and NodeMCU
upload_port = COM5
extra_scripts = ${scripts_defaults.extra_scripts}
; pio/obj-dump.py
; *** Upload file to OTA server using SCP
;upload_port = user@host:/path
;extra_scripts = ${scripts_defaults.extra_scripts}
; pio/strip-floats.py
; pio/sftp-uploader.py
; *** Upload file to OTA server in folder api/arduino using HTTP
;upload_port = domus1:80/api/upload-arduino.php
;extra_scripts = ${scripts_defaults.extra_scripts}
; pio/strip-floats.py
; pio/http-uploader.py
lib_extra_dirs = ${library.lib_extra_dirs}
[core]
; Activate only (one set) if you want to override the standard core defined in platformio.ini !!!
;platform_packages = ${tasmota_stage.platform_packages}
;build_unflags = ${tasmota_stage.build_unflags}
;build_flags = ${tasmota_stage.build_flags}
;platform_packages = ${core_stage.platform_packages}
;build_unflags = ${core_stage.build_unflags}
;build_flags = ${core_stage.build_flags}
[tasmota_stage]
; *** Esp8266 core for Arduino version Tasmota stage (PR7231 and Backport PR7514)
platform_packages = framework-arduinoespressif8266@https://github.com/Jason2866/Arduino.git#2.7.4.4
build_unflags = ${esp_defaults.build_unflags}
build_flags = ${esp82xx_defaults.build_flags}
; *********** Alternative Options, enable only if you know exactly what you do ********
; NONOSDK221
; -DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK221
; NONOSDK22x_190313
; -DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK22x_190313
; NONOSDK22x_191024 = 2.2.1+111-dev(5ab15d1)
; -DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK22x_191024
; 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
; -DPIO_FRAMEWORK_ARDUINO_LWIP_HIGHER_BANDWIDTH
; lwIP 2 - Low Memory
; -DPIO_FRAMEWORK_ARDUINO_LWIP2_LOW_MEMORY
; lwIP 2 - Higher Bandwidth
; -DPIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH
; lwIP 2 - Higher Bandwidth Low Memory no Features
; -DPIO_FRAMEWORK_ARDUINO_LWIP2_LOW_MEMORY_LOW_FLASH
; VTABLES in Heap
; -DVTABLES_IN_DRAM
; VTABLES in IRAM
; -DVTABLES_IN_IRAM
; Exception code in firmware /needs much space!
; -fexceptions
; -lstdc++-exc
[core_stage]
; *** Esp8266 core version. Tasmota stage or Arduino stage version. Built with GCC 10.1 toolchain
platform_packages = framework-arduinoespressif8266 @ https://github.com/Jason2866/platform-espressif8266/releases/download/2.9.1/framework-arduinoespressif8266-3.20901.0.tar.gz
;framework-arduinoespressif8266 @ https://github.com/esp8266/Arduino.git
toolchain-xtensa @ ~2.100100.0
build_unflags = ${esp_defaults.build_unflags}
-Wswitch-unreachable
build_flags = ${esp82xx_defaults.build_flags}
-Wno-switch-unreachable
; *********** Alternative Options, enable only if you know exactly what you do ********
; NONOSDK221
; -DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK221
; NONOSDK22x_190313
; -DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK22x_190313
; NONOSDK22x_191024 = 2.2.1+111-dev(5ab15d1)
; -DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK22x_191024
; 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 2 - Low Memory
; -DPIO_FRAMEWORK_ARDUINO_LWIP2_LOW_MEMORY
; lwIP 2 - Higher Bandwidth
; -DPIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH
; lwIP 2 - Higher Bandwidth Low Memory no Features
; -DPIO_FRAMEWORK_ARDUINO_LWIP2_LOW_MEMORY_LOW_FLASH
; VTABLES in Heap
; -DVTABLES_IN_DRAM
; VTABLES in IRAM
; -DVTABLES_IN_IRAM
; Exception code in firmware /needs much space!
; -fexceptions
; -lstdc++-exc
[common32]
platform = ${core32.platform}
platform_packages = ${core32.platform_packages}
build_unflags = ${core32.build_unflags}
build_flags = ${core32.build_flags}
board = esp32dev
board_build.ldscript = esp32_out.ld
board_build.partitions = esp32_partition_app1984k_spiffs64k.csv
board_build.flash_mode = ${common.board_build.flash_mode}
board_build.f_flash = ${common.board_build.f_flash}
board_build.f_cpu = ${common.board_build.f_cpu}
monitor_speed = ${common.monitor_speed}
upload_port = ${common.upload_port}
upload_resetmethod = ${common.upload_resetmethod}
upload_speed = 921600
extra_scripts = ${common.extra_scripts}
lib_extra_dirs = ${library.lib_extra_dirs}
; *** ESP32 lib. ALWAYS needed for ESP32 !!!
lib/libesp32
[core32]
; Activate Stage Core32 by removing ";" in next 4 lines, if you want to override the standard core32
;platform = ${core32_stage.platform}
;platform_packages = ${core32_stage.platform_packages}
;build_unflags = ${core32_stage.build_unflags}
;build_flags = ${core32_stage.build_flags}
[core32_stage]
platform = espressif32@2.0.0
platform_packages = tool-esptoolpy@1.20800.0
framework-arduinoespressif32 @ https://github.com/espressif/arduino-esp32.git#f7fb00632e04d74a7890a77fa7dbbb8ae572e866
build_unflags = ${esp32_defaults.build_unflags}
build_flags = ${esp32_defaults.build_flags}
-D ESP32_STAGE=true
[library]
lib_ldf_mode = chain+
shared_libdeps_dir = lib
; *** Library disable / enable for variant Tasmota(32). Disable reduces compile time
; *** !!! Disabling needed libs will generate compile errors !!!
; *** The resulting firmware will NOT be different if you leave all libs enabled
; *** Disabling by putting a ";" in front of the lib name
; *** If you dont know what it is all about, do not change
lib_extra_dirs =
; *** Only disabled for Tasmota minimal and Tasmota light. For all other variants needed!
lib/lib_basic
; **** I2C devices. Most sensors. Disable only if you dont have ANY I2C device enabled
lib/lib_i2c
; *** Displays. Disable if you dont have any Display activated
lib/lib_display
; *** Bear SSL and base64. Disable if you dont have SSL or TLS activated
lib/lib_ssl
; *** Audio needs a lot of time to compile. Mostly not used functions. Recommended to disable
lib/lib_audio
; *** RF 433 stuff (not RF Bridge). Recommended to disable
lib/lib_rf
; *** Mostly not used functions. Recommended to disable
lib/lib_div
; *** Debug version used for PlatformIO Home Project Inspection
[env:tasmota-debug]
build_type = debug
build_unflags = ${esp_defaults.build_unflags}
build_flags = ${esp82xx_defaults.build_flags}
; -Wstack-usage=300
[env:tasmota32-debug]
extends = env:tasmota32
build_type = debug
build_unflags = ${esp32_defaults.build_unflags}
build_flags = ${esp32_defaults.build_flags}
; -Wstack-usage=300

View File

@ -1,92 +1,92 @@
; *** BETA ESP32 Tasmota version ***
; *** expect the unexpected. Some features not working!!! ***
[platformio]
; *** Tasmota build variant selection
default_envs = ${build_envs.default_envs}
; *** Uncomment by deleting ";" in the line(s) below to select version(s)
; tasmota32
; tasmota32-webcam
; tasmota32-minimal
; tasmota32-lite
; tasmota32-knx
; tasmota32-sensors
; tasmota32-display
; tasmota32-ir
; tasmota32-ircustom
; tasmota32-BG
; tasmota32-BR
; tasmota32-CN
; tasmota32-CZ
; tasmota32-DE
; tasmota32-ES
; tasmota32-FR
; tasmota32-GR
; tasmota32-HE
; tasmota32-HU
; tasmota32-IT
; tasmota32-KO
; tasmota32-NL
; tasmota32-PL
; tasmota32-PT
; tasmota32-RO
; tasmota32-RU
; tasmota32-SE
; tasmota32-SK
; tasmota32-TR
; tasmota32-TW
; tasmota32-UK
; tasmota32-VN
[common32]
board = esp32dev
board_build.flash_mode = ${common.board_build.flash_mode}
board_build.ldscript = esp32_out.ld
board_build.partitions = esp32_partition_app1984k_spiffs64k.csv
platform = ${core32.platform}
platform_packages = ${core32.platform_packages}
build_unflags = ${esp_defaults.build_unflags}
-Wpointer-arith
build_flags = ${core32.build_flags}
board_build.f_cpu = ${common.board_build.f_cpu}
board_build.f_flash = ${common.board_build.f_flash}
monitor_speed = ${common.monitor_speed}
upload_port = ${common.upload_port}
upload_resetmethod = ${common.upload_resetmethod}
upload_speed = 921600
extra_scripts = ${common.extra_scripts}
lib_ldf_mode = chain+
shared_libdeps_dir = lib
lib_extra_dirs =
lib/libesp32
lib/lib_basic
lib/lib_i2c
lib/lib_display
lib/lib_ssl
lib/lib_audio
lib/lib_rf
lib/lib_div
[esp32_defaults]
build_flags = ${esp_defaults.build_flags}
-D CORE_DEBUG_LEVEL=0
-D BUFFER_LENGTH=128
-D MQTT_MAX_PACKET_SIZE=1200
-D uint32=uint32_t
-D uint16=uint16_t
-D uint8=uint8_t
-D sint8_t=int8_t
-D sint32_t=int32_t
-D sint16_t=int16_t
-D memcpy_P=memcpy
-D memcmp_P=memcmp
[core32]
platform = espressif32@2.0.0
platform_packages = tool-esptoolpy@1.20800.0
platformio/framework-arduinoespressif32 @ 3.10004.201016
build_flags = ${esp32_defaults.build_flags}
; *** BETA ESP32 Tasmota version ***
; *** expect the unexpected. Some features not working!!! ***
[platformio]
; *** Tasmota build variant selection
default_envs = ${build_envs.default_envs}
; *** Uncomment by deleting ";" in the line(s) below to select version(s)
; tasmota32
; tasmota32-webcam
; tasmota32-minimal
; tasmota32-lite
; tasmota32-knx
; tasmota32-sensors
; tasmota32-display
; tasmota32-ir
; tasmota32-ircustom
; tasmota32-BG
; tasmota32-BR
; tasmota32-CN
; tasmota32-CZ
; tasmota32-DE
; tasmota32-ES
; tasmota32-FR
; tasmota32-GR
; tasmota32-HE
; tasmota32-HU
; tasmota32-IT
; tasmota32-KO
; tasmota32-NL
; tasmota32-PL
; tasmota32-PT
; tasmota32-RO
; tasmota32-RU
; tasmota32-SE
; tasmota32-SK
; tasmota32-TR
; tasmota32-TW
; tasmota32-UK
; tasmota32-VN
[common32]
platform = ${core32.platform}
platform_packages = ${core32.platform_packages}
build_unflags = ${core32.build_unflags}
build_flags = ${core32.build_flags}
board = esp32dev
board_build.ldscript = esp32_out.ld
board_build.partitions = esp32_partition_app1984k_spiffs64k.csv
board_build.flash_mode = ${common.board_build.flash_mode}
board_build.f_flash = ${common.board_build.f_flash}
board_build.f_cpu = ${common.board_build.f_cpu}
monitor_speed = ${common.monitor_speed}
upload_port = ${common.upload_port}
upload_resetmethod = ${common.upload_resetmethod}
upload_speed = 921600
extra_scripts = ${common.extra_scripts}
lib_ldf_mode = chain+
shared_libdeps_dir = lib
lib_extra_dirs =
lib/libesp32
lib/lib_basic
lib/lib_i2c
lib/lib_display
lib/lib_ssl
lib/lib_audio
lib/lib_rf
lib/lib_div
[esp32_defaults]
build_unflags = ${esp_defaults.build_unflags}
-Wpointer-arith
build_flags = ${esp_defaults.build_flags}
-D CORE_DEBUG_LEVEL=0
-D BUFFER_LENGTH=128
-D MQTT_MAX_PACKET_SIZE=1200
-D uint32=uint32_t
-D uint16=uint16_t
-D uint8=uint8_t
-D sint8_t=int8_t
-D sint32_t=int32_t
-D sint16_t=int16_t
-D memcpy_P=memcpy
-D memcmp_P=memcmp
[core32]
platform = espressif32@2.0.0
platform_packages = tool-esptoolpy@1.20800.0
platformio/framework-arduinoespressif32 @ 3.10004.201016
build_unflags = ${esp32_defaults.build_unflags}
build_flags = ${esp32_defaults.build_flags}

View File

@ -53,6 +53,7 @@
#define D_JSON_DARKNESS "Darkness"
#define D_JSON_DATA "Data"
#define D_JSON_DEWPOINT "DewPoint"
#define D_JSON_DO "DisolvedOxygen"
#define D_JSON_DISTANCE "Distance"
#define D_JSON_DNSSERVER "DNSServer"
#define D_JSON_DONE "Done"
@ -785,6 +786,7 @@ const char HTTP_SNS_EC[] PROGMEM = "{s}%s " D_EC "{
const char HTTP_SNS_O2[] PROGMEM = "{s}%s " D_O2 "{m}%s " D_UNIT_PERCENT "{e}";
const char HTTP_SNS_LITERS[] PROGMEM = "{s}%s " D_VOLUME "{m}%s " D_UNIT_LITERS "{e}";
const char HTTP_SNS_LPM[] PROGMEM = "{s}%s " D_FLOW_RATE "{m}%s " D_UNIT_LITERS_PER_MIN "{e}";
const char HTTP_SNS_DO[] PROGMEM = "{s}%s " D_DO "{m}%s " D_UNIT_PARTS_PER_MILLION "{e}";
const char S_MAIN_MENU[] PROGMEM = D_MAIN_MENU;
const char S_CONFIGURATION[] PROGMEM = D_CONFIGURATION;

View File

@ -84,6 +84,7 @@
#define D_DISABLED "Забранен"
#define D_DISTANCE "Разстояние"
#define D_DNS_SERVER "DNS Сървър"
#define D_DO "Disolved Oxygen"
#define D_DONE "Изпълнено"
#define D_DST_TIME "Лятно време"
#define D_EC "EC"

View File

@ -84,6 +84,7 @@
#define D_DISABLED "Zablokováno"
#define D_DISTANCE "Distance"
#define D_DNS_SERVER "Server DNS"
#define D_DO "Disolved Oxygen"
#define D_DONE "Provedeno"
#define D_DST_TIME "DST"
#define D_EC "EC"

View File

@ -84,6 +84,7 @@
#define D_DISABLED "deaktiviert"
#define D_DISTANCE "Abstand"
#define D_DNS_SERVER "DNS-Server"
#define D_DO "Disolved Oxygen"
#define D_DONE "erledigt"
#define D_DST_TIME "DST"
#define D_EC "EC"

View File

@ -84,6 +84,7 @@
#define D_DISABLED "Ανενεργό"
#define D_DISTANCE "Απόσταση"
#define D_DNS_SERVER "Διακομιστής DNS"
#define D_DO "Disolved Oxygen"
#define D_DONE "Ολοκληρώθηκε"
#define D_DST_TIME "DST"
#define D_EC "EC"

View File

@ -84,6 +84,7 @@
#define D_DISABLED "Disabled"
#define D_DISTANCE "Distance"
#define D_DNS_SERVER "DNS Server"
#define D_DO "Disolved Oxygen"
#define D_DONE "Done"
#define D_DST_TIME "DST"
#define D_EC "EC"

View File

@ -84,6 +84,7 @@
#define D_DISABLED "Deshabilitado"
#define D_DISTANCE "Distancia"
#define D_DNS_SERVER "Servidor DNS"
#define D_DO "Disolved Oxygen"
#define D_DONE "Listo"
#define D_DST_TIME "DST"
#define D_EC "EC"

View File

@ -80,6 +80,7 @@
#define D_DISABLED "Désactivé"
#define D_DISTANCE "Distance"
#define D_DNS_SERVER "Serveur DNS"
#define D_DO "Disolved Oxygen"
#define D_DONE "Terminé"
#define D_DST_TIME "DST"
#define D_EC "EC"

View File

@ -84,6 +84,7 @@
#define D_DISABLED "מבוטל"
#define D_DISTANCE "מרחק"
#define D_DNS_SERVER "DNS שרת"
#define D_DO "Disolved Oxygen"
#define D_DONE "סיים"
#define D_DST_TIME "DST"
#define D_EC "EC"

View File

@ -84,6 +84,7 @@
#define D_DISABLED "Letiltva"
#define D_DISTANCE "Távolság"
#define D_DNS_SERVER "DNS szerver"
#define D_DO "Disolved Oxygen"
#define D_DONE "Kész"
#define D_DST_TIME "nyári idő"
#define D_EC "EC"

View File

@ -84,6 +84,7 @@
#define D_DISABLED "Disabilitato/a"
#define D_DISTANCE "Distanza"
#define D_DNS_SERVER "Server DNS"
#define D_DO "Disolved Oxygen"
#define D_DONE "Completato"
#define D_DST_TIME "DST"
#define D_EC "EC"

View File

@ -84,6 +84,7 @@
#define D_DISABLED "사용안함"
#define D_DISTANCE "거리"
#define D_DNS_SERVER "DNS 서버"
#define D_DO "Disolved Oxygen"
#define D_DONE "완료"
#define D_DST_TIME "DST"
#define D_EC "EC"

View File

@ -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 v8.0.0
* Updated until v9.1.0
\*********************************************************************/
//#define LANGUAGE_MODULE_NAME // Enable to display "Module Generic" (ie Spanish), Disable to display "Generic Module" (ie English)
@ -84,6 +84,7 @@
#define D_DISABLED "Uitgeschakeld"
#define D_DISTANCE "Afstand"
#define D_DNS_SERVER "DNS Server"
#define D_DO "Opgelost zuurstof"
#define D_DONE "Klaar"
#define D_DST_TIME "ZT"
#define D_EC "EC"

View File

@ -84,6 +84,7 @@
#define D_DISABLED "Wyłączony"
#define D_DISTANCE "Odległość"
#define D_DNS_SERVER "Serwer DNS"
#define D_DO "Disolved Oxygen"
#define D_DONE "Wykonane"
#define D_DST_TIME "Czas DST"
#define D_EC "EC"

View File

@ -84,6 +84,7 @@
#define D_DISABLED "Desabilitado"
#define D_DISTANCE "Distância"
#define D_DNS_SERVER "Servidor DNS"
#define D_DO "Disolved Oxygen"
#define D_DONE "Concluído"
#define D_DST_TIME "DST"
#define D_EC "EC"

View File

@ -84,6 +84,7 @@
#define D_DISABLED "Disabilitado"
#define D_DISTANCE "Distância"
#define D_DNS_SERVER "Servidor DNS"
#define D_DO "Disolved Oxygen"
#define D_DONE "Concluído"
#define D_DST_TIME "DST"
#define D_EC "EC"

View File

@ -84,6 +84,7 @@
#define D_DISABLED "Dezactivat"
#define D_DISTANCE "Distanță"
#define D_DNS_SERVER "Server DNS"
#define D_DO "Disolved Oxygen"
#define D_DONE "Terminat"
#define D_DST_TIME "DST"
#define D_EC "EC"

View File

@ -84,6 +84,7 @@
#define D_DISABLED "Блокирован"
#define D_DISTANCE "Distance"
#define D_DNS_SERVER "DNS Сервер"
#define D_DO "Disolved Oxygen"
#define D_DONE "Выполнено"
#define D_DST_TIME "DST"
#define D_EC "EC"

View File

@ -85,6 +85,7 @@
#define D_DISABLED "Zablokované"
#define D_DISTANCE "Vzdialenosť"
#define D_DNS_SERVER "Server DNS"
#define D_DO "Disolved Oxygen"
#define D_DONE "Hotovo"
#define D_DST_TIME "DST"
#define D_EC "EC"

View File

@ -84,6 +84,7 @@
#define D_DISABLED "Inaktiverad"
#define D_DISTANCE "Distans"
#define D_DNS_SERVER "DNS-server"
#define D_DO "Disolved Oxygen"
#define D_DONE "Gjort"
#define D_DST_TIME "DST"
#define D_EC "EC"

View File

@ -84,6 +84,7 @@
#define D_DISABLED "Etkin Değil"
#define D_DISTANCE "Mesage"
#define D_DNS_SERVER "DNS Sunucu"
#define D_DO "Disolved Oxygen"
#define D_DONE "Tamam"
#define D_DST_TIME "DST"
#define D_EC "EC"

View File

@ -84,6 +84,7 @@
#define D_DISABLED "Вимкнено"
#define D_DISTANCE "Відстань"
#define D_DNS_SERVER "Сервер DNS"
#define D_DO "Disolved Oxygen"
#define D_DONE "Виконано"
#define D_DST_TIME "Літній час"
#define D_EC "EC"

View File

@ -84,6 +84,7 @@
#define D_DISABLED "Vô hiệu hóa"
#define D_DISTANCE "Khoảng cách"
#define D_DNS_SERVER "Máy chủ DNS"
#define D_DO "Disolved Oxygen"
#define D_DONE "Hoàn thành"
#define D_DST_TIME "DST"
#define D_EC "EC"

View File

@ -84,6 +84,7 @@
#define D_DISABLED "禁用"
#define D_DISTANCE "距离"
#define D_DNS_SERVER "DNS服务器"
#define D_DO "Disolved Oxygen"
#define D_DONE "完成"
#define D_DST_TIME "DST"
#define D_EC "EC"

View File

@ -84,6 +84,7 @@
#define D_DISABLED "已停用"
#define D_DISTANCE "距離"
#define D_DNS_SERVER "DNS伺服器"
#define D_DO "Disolved Oxygen"
#define D_DONE "完成"
#define D_DST_TIME "DST"
#define D_EC "EC"

View File

@ -571,6 +571,7 @@
// #define USE_EZOO2 // [I2cDriver55] Enable support for EZO's O2 sensor (+0k3 code) - Shared EZO code required for any EZO device (+1k2 code)
// #define USE_EZOPRS // [I2cDriver55] Enable support for EZO's PRS sensor (+0k7 code) - Shared EZO code required for any EZO device (+1k2 code)
// #define USE_EZOFLO // [I2cDriver55] Enable support for EZO's FLO sensor (+0k4 code) - Shared EZO code required for any EZO device (+1k2 code)
// #define USE_EZODO // [I2cDriver55] Enable support for EZO's DO sensor (+0k3 code) - Shared EZO code required for any EZO device (+1k2 code)
// #define USE_DISPLAY // Add I2C Display Support (+2k code)
#define USE_DISPLAY_MODES1TO5 // Enable display mode 1 to 5 in addition to mode 0

View File

@ -649,8 +649,9 @@ void ResponseAppendFeatures(void)
#if defined(USE_I2C) && defined(USE_EZOFLO)
feature7 |= 0x00000080;
#endif
// feature7 |= 0x00000100;
#if defined(USE_I2C) && defined(USE_EZODO)
feature7 |= 0x00000100;
#endif
// feature7 |= 0x00000200;
// feature7 |= 0x00000400;
// feature7 |= 0x00000800;

View File

@ -490,7 +490,7 @@ bool SendKey(uint32_t key, uint32_t device, uint32_t state)
result = XdrvRulesProcess();
}
#ifdef USE_PWM_DIMMER
if (PWM_DIMMER == TasmotaGlobal.module_type && !result) {
if (PWM_DIMMER != TasmotaGlobal.module_type || !result) {
#endif // USE_PWM_DIMMER
int32_t payload_save = XdrvMailbox.payload;
XdrvMailbox.payload = device_save << 24 | key << 16 | state << 8 | device;

View File

@ -136,6 +136,7 @@
//#define USE_EZOO2 // [I2cDriver55] Enable support for EZO's O2 sensor (+0k3 code) - Shared EZO code required for any EZO device (+1k2 code)
//#define USE_EZOPRS // [I2cDriver55] Enable support for EZO's PRS sensor (+0k7 code) - Shared EZO code required for any EZO device (+1k2 code)
//#define USE_EZOFLO // [I2cDriver55] Enable support for EZO's FLO sensor (+0k4 code) - Shared EZO code required for any EZO device (+1k2 code)
// #define USE_EZODO // [I2cDriver55] Enable support for EZO's DO sensor (+0k3 code) - Shared EZO code required for any EZO device (+1k2 code)
#define USE_MHZ19 // Add support for MH-Z19 CO2 sensor (+2k code)
#define USE_SENSEAIR // Add support for SenseAir K30, K70 and S8 CO2 sensor (+2k3 code)

View File

@ -67,6 +67,8 @@
#define XDRV_10 10
//#define DEBUG_RULES
#include <unishox.h>
#define D_CMND_RULE "Rule"
@ -424,7 +426,9 @@ bool RulesRuleMatch(uint8_t rule_set, String &event, String &rule, bool stop_all
// rule_name = "INA219#CURRENT"
// rule_param = "0.100" or "%VAR1%"
//AddLog_P2(LOG_LEVEL_DEBUG, PSTR("RUL: expr %s, name %s, param %s"), rule_expr.c_str(), rule_name.c_str(), rule_param.c_str());
#ifdef DEBUG_RULES
// AddLog_P2(LOG_LEVEL_DEBUG, PSTR("RUL-RM1: expr %s, name %s, param %s"), rule_expr.c_str(), rule_name.c_str(), rule_param.c_str());
#endif
char rule_svalue[80] = { 0 };
float rule_value = 0;
@ -499,7 +503,7 @@ bool RulesRuleMatch(uint8_t rule_set, String &event, String &rule, bool stop_all
String buf = event; // copy the string into a new buffer that will be modified
//AddLog_P2(LOG_LEVEL_DEBUG, PSTR("RUL: RulesRuleMatch |%s|"), buf.c_str());
//AddLog_P2(LOG_LEVEL_DEBUG, PSTR("RUL-RM2: RulesRuleMatch |%s|"), buf.c_str());
JsonParser parser((char*)buf.c_str());
JsonParserObject obj = parser.getRootObject();
@ -513,7 +517,7 @@ bool RulesRuleMatch(uint8_t rule_set, String &event, String &rule, bool stop_all
while ((pos = rule_name.indexOf("#")) > 0) { // "SUBTYPE1#SUBTYPE2#CURRENT"
subtype = rule_name.substring(0, pos);
obj = obj[subtype.c_str()].getObject();
if (!obj) { return false; } // not found
if (!obj) { return false; } // not found
rule_name = rule_name.substring(pos +1);
if (i++ > 10) { return false; } // Abandon possible loop
@ -522,7 +526,7 @@ bool RulesRuleMatch(uint8_t rule_set, String &event, String &rule, bool stop_all
}
JsonParserToken val = obj[rule_name.c_str()];
if (!val) { return false; } // last level not found
if (!val) { return false; } // last level not found
const char* str_value;
if (rule_name_idx) {
if (val.isArray()) {
@ -531,11 +535,14 @@ bool RulesRuleMatch(uint8_t rule_set, String &event, String &rule, bool stop_all
str_value = val.getStr();
}
} else {
str_value = val.getStr(); // "CURRENT"
str_value = val.getStr(); // "CURRENT"
}
//AddLog_P2(LOG_LEVEL_DEBUG, PSTR("RUL: Name %s, Value |%s|, TrigCnt %d, TrigSt %d, Source %s, Json %s"),
// rule_name.c_str(), rule_svalue, Rules.trigger_count[rule_set], bitRead(Rules.triggers[rule_set], Rules.trigger_count[rule_set]), event.c_str(), (str_value) ? str_value : "none");
#ifdef DEBUG_RULES
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("RUL-RM3: Name %s, Value |%s|, TrigCnt %d, TrigSt %d, Source %s, Json |%s|"),
rule_name.c_str(), rule_svalue, Rules.trigger_count[rule_set], bitRead(Rules.triggers[rule_set],
Rules.trigger_count[rule_set]), event.c_str(), (str_value[0] != '\0') ? str_value : "none");
#endif
Rules.event_value = str_value; // Prepare %value%
@ -577,7 +584,7 @@ bool RulesRuleMatch(uint8_t rule_set, String &event, String &rule, bool stop_all
if (stop_all_rules) { match = false; }
//AddLog_P2(LOG_LEVEL_DEBUG, PSTR("RUL: Match 1 %d, Triggers %08X, TriggerCount %d"), match, Rules.triggers[rule_set], Rules.trigger_count[rule_set]);
//AddLog_P2(LOG_LEVEL_DEBUG, PSTR("RUL-RM4: Match 1 %d, Triggers %08X, TriggerCount %d"), match, Rules.triggers[rule_set], Rules.trigger_count[rule_set]);
if (bitRead(Settings.rule_once, rule_set)) {
if (match) { // Only allow match state changes
@ -591,7 +598,7 @@ bool RulesRuleMatch(uint8_t rule_set, String &event, String &rule, bool stop_all
}
}
//AddLog_P2(LOG_LEVEL_DEBUG, PSTR("RUL: Match 2 %d, Triggers %08X, TriggerCount %d"), match, Rules.triggers[rule_set], Rules.trigger_count[rule_set]);
//AddLog_P2(LOG_LEVEL_DEBUG, PSTR("RUL-RM5: Match 2 %d, Triggers %08X, TriggerCount %d"), match, Rules.triggers[rule_set], Rules.trigger_count[rule_set]);
return match;
}
@ -663,7 +670,7 @@ bool RuleSetProcess(uint8_t rule_set, String &event_saved)
delay(0); // Prohibit possible loop software watchdog
//AddLog_P2(LOG_LEVEL_DEBUG, PSTR("RUL: Event = %s, Rule = %s"), event_saved.c_str(), Settings.rules[rule_set]);
//AddLog_P2(LOG_LEVEL_DEBUG, PSTR("RUL-RP1: Event = %s, Rule = %s"), event_saved.c_str(), Settings.rules[rule_set]);
String rules = GetRule(rule_set);
@ -696,7 +703,9 @@ bool RuleSetProcess(uint8_t rule_set, String &event_saved)
Rules.event_value = "";
String event = event_saved;
//AddLog_P2(LOG_LEVEL_DEBUG, PSTR("RUL: Event |%s|, Rule |%s|, Command(s) |%s|"), event.c_str(), event_trigger.c_str(), commands.c_str());
#ifdef DEBUG_RULES
// AddLog_P2(LOG_LEVEL_DEBUG, PSTR("RUL-RP2: Event |%s|, Rule |%s|, Command(s) |%s|"), event.c_str(), event_trigger.c_str(), commands.c_str());
#endif
if (RulesRuleMatch(rule_set, event, event_trigger, stop_all_rules)) {
if (plen == plen2) { stop_all_rules = true; } // If BREAK was used on a triggered rule, Stop execution of this rule set

View File

@ -543,6 +543,9 @@ public:
inline bool getReachable(void) const { return reachable; }
inline bool getPower(uint8_t ep =0) const;
inline void setLQI(uint8_t _lqi) { lqi = _lqi; }
inline void setBatteryPercent(uint8_t bp) { batterypercent = bp; }
// Add an endpoint to a device
bool addEndpoint(uint8_t endpoint);
void clearEndpoints(void);
@ -552,6 +555,8 @@ public:
void setModelId(const char * str);
void setFriendlyName(const char * str);
void setLastSeenNow(void);
// dump device attributes to ZbData
void toAttributes(Z_attribute_list & attr_list) const;
@ -568,30 +573,8 @@ public:
}
}
// returns: dirty flag, did we change the value of the object
bool setLightChannels(int8_t channels) {
bool dirty = false;
if (channels >= 0) {
// retrieve of create light object
Z_Data_Light & light = data.get<Z_Data_Light>(0);
if (channels != light.getConfig()) {
light.setConfig(channels);
dirty = true;
}
Z_Data_OnOff & onoff = data.get<Z_Data_OnOff>(0);
} else {
// remove light / onoff object if any
for (auto & data_elt : data) {
if ((data_elt.getType() == Z_Data_Type::Z_Light) ||
(data_elt.getType() == Z_Data_Type::Z_OnOff)) {
// remove light object
data.remove(&data_elt);
dirty = true;
}
}
}
return dirty;
}
void setLightChannels(int8_t channels);
protected:
static void setStringAttribute(char*& attr, const char * str);
@ -655,9 +638,9 @@ public:
// - 0x0000 = not found
// - BAD_SHORTADDR = bad parameter
// - 0x<shortaddr> = the device's short address
uint16_t isKnownLongAddr(uint64_t longaddr) const;
uint16_t isKnownIndex(uint32_t index) const;
uint16_t isKnownFriendlyName(const char * name) const;
Z_Device & isKnownLongAddrDevice(uint64_t longaddr) const;
Z_Device & isKnownIndexDevice(uint32_t index) const;
Z_Device & isKnownFriendlyNameDevice(const char * name) const;
Z_Device & findShortAddr(uint16_t shortaddr);
const Z_Device & findShortAddr(uint16_t shortaddr) const;
@ -666,9 +649,7 @@ public:
Z_Device & getShortAddr(uint16_t shortaddr); // find Device from shortAddr, creates it if does not exist
Z_Device & getLongAddr(uint64_t longaddr); // find Device from shortAddr, creates it if does not exist
// check if a device was found or if it's the fallback device
inline bool foundDevice(const Z_Device & device) const {
return (&device != &device_unk);
}
inline bool foundDevice(const Z_Device & device) const { return device.valid(); }
int32_t findFriendlyName(const char * name) const;
uint64_t getDeviceLongAddr(uint16_t shortaddr) const;
@ -689,26 +670,15 @@ public:
return findShortAddr(shortaddr).manufacturerId;
}
void setReachable(uint16_t shortaddr, bool reachable);
void setLQI(uint16_t shortaddr, uint8_t lqi);
void setLastSeenNow(uint16_t shortaddr);
// uint8_t getLQI(uint16_t shortaddr) const;
void setBatteryPercent(uint16_t shortaddr, uint8_t bp);
uint8_t getBatteryPercent(uint16_t shortaddr) const;
// get next sequence number for (increment at each all)
uint8_t getNextSeqNumber(uint16_t shortaddr);
// Dump json
static void addLightState(Z_attribute_list & attr_list, const Z_Data_Light & light);
String dumpLightState(uint16_t shortaddr) const;
String dump(uint32_t dump_mode, uint16_t status_shortaddr = 0) const;
static String dumpLightState(const Z_Device & device);
String dumpDevice(uint32_t dump_mode, const Z_Device & device) const;
static String dumpSingleDevice(uint32_t dump_mode, const Z_Device & device);
int32_t deviceRestore(JsonParserObject json);
// General Zigbee device profile support
void setLightProfile(uint16_t shortaddr, uint8_t light_profile);
uint8_t getLightProfile(uint16_t shortaddr) const ;
// Hue support
int8_t getHueBulbtype(uint16_t shortaddr) const ;
void hideHueBulb(uint16_t shortaddr, bool hidden);
@ -731,14 +701,7 @@ public:
size_t devicesSize(void) const {
return _devices.length();
}
const Z_Device & devicesAt(size_t i) const {
const Z_Device * devp = _devices.at(i);
if (devp) {
return *devp;
} else {
return device_unk;
}
}
Z_Device & devicesAt(size_t i) const;
// Remove device from list
bool removeDevice(uint16_t shortaddr);
@ -746,10 +709,10 @@ public:
// Mark data as 'dirty' and requiring to save in Flash
void dirty(void);
void clean(void); // avoid writing to flash the last changes
void shrinkToFit(uint16_t shortaddr);
// Find device by name, can be short_addr, long_addr, number_in_array or name
uint16_t parseDeviceParam(const char * param, bool short_must_be_known = false) const;
Z_Device & parseDeviceFromName(const char * param, bool short_must_be_known = false);
private:
LList<Z_Device> _devices; // list of devices
@ -757,10 +720,6 @@ private:
uint32_t _saveTimer = 0;
uint8_t _seqNumber = 0; // global seqNumber if device is unknown
// Following device is used represent the unknown device, with all defaults
// Any find() function will not return Null, instead it will return this instance
const Z_Device device_unk = Z_Device(BAD_SHORTADDR);
//int32_t findShortAddrIdx(uint16_t shortaddr) const;
// Create a new entry in the devices list - must be called if it is sure it does not already exist
Z_Device & createDeviceEntry(uint16_t shortaddr, uint64_t longaddr = 0);
@ -772,6 +731,10 @@ private:
\*********************************************************************************************/
Z_Devices zigbee_devices = Z_Devices();
// Following device is used represent the unknown device, with all defaults
// Any find() function will not return Null, instead it will return this instance
Z_Device device_unk = Z_Device(BAD_SHORTADDR);
// Local coordinator information
uint64_t localIEEEAddr = 0;
uint16_t localShortAddr = 0;

View File

@ -23,12 +23,21 @@
* Implementation
\*********************************************************************************************/
Z_Device & Z_Devices::devicesAt(size_t i) const {
Z_Device * devp = (Z_Device*) _devices.at(i);
if (devp) {
return *devp;
} else {
return device_unk;
}
}
//
// Create a new Z_Device entry in _devices. Only to be called if you are sure that no
// entry with same shortaddr or longaddr exists.
//
Z_Device & Z_Devices::createDeviceEntry(uint16_t shortaddr, uint64_t longaddr) {
if ((BAD_SHORTADDR == shortaddr) && !longaddr) { return (Z_Device&) device_unk; } // it is not legal to create this entry
if ((BAD_SHORTADDR == shortaddr) && !longaddr) { return device_unk; } // it is not legal to create this entry
Z_Device & device = _devices.addToLast();
device.shortaddr = shortaddr;
device.longaddr = longaddr;
@ -56,7 +65,7 @@ Z_Device & Z_Devices::findShortAddr(uint16_t shortaddr) {
for (auto & elem : _devices) {
if (elem.shortaddr == shortaddr) { return elem; }
}
return (Z_Device&) device_unk;
return device_unk;
}
const Z_Device & Z_Devices::findShortAddr(uint16_t shortaddr) const {
for (const auto & elem : _devices) {
@ -73,11 +82,11 @@ const Z_Device & Z_Devices::findShortAddr(uint16_t shortaddr) const {
// index in _devices of entry, -1 if not found
//
Z_Device & Z_Devices::findLongAddr(uint64_t longaddr) {
if (!longaddr) { return (Z_Device&) device_unk; }
if (!longaddr) { return device_unk; }
for (auto &elem : _devices) {
if (elem.longaddr == longaddr) { return elem; }
}
return (Z_Device&) device_unk;
return device_unk;
}
const Z_Device & Z_Devices::findLongAddr(uint64_t longaddr) const {
if (!longaddr) { return device_unk; }
@ -109,32 +118,25 @@ int32_t Z_Devices::findFriendlyName(const char * name) const {
return -1;
}
uint16_t Z_Devices::isKnownLongAddr(uint64_t longaddr) const {
const Z_Device & device = findLongAddr(longaddr);
if (foundDevice(device)) {
return device.shortaddr; // can be zero, if not yet registered
} else {
return BAD_SHORTADDR;
}
Z_Device & Z_Devices::isKnownLongAddrDevice(uint64_t longaddr) const {
return (Z_Device &) findLongAddr(longaddr);
}
uint16_t Z_Devices::isKnownIndex(uint32_t index) const {
Z_Device & Z_Devices::isKnownIndexDevice(uint32_t index) const {
if (index < devicesSize()) {
const Z_Device & device = devicesAt(index);
return device.shortaddr;
return devicesAt(index);
} else {
return BAD_SHORTADDR;
return device_unk;
}
}
uint16_t Z_Devices::isKnownFriendlyName(const char * name) const {
if ((!name) || (0 == strlen(name))) { return BAD_SHORTADDR; } // Error
Z_Device & Z_Devices::isKnownFriendlyNameDevice(const char * name) const {
if ((!name) || (0 == strlen(name))) { return device_unk; } // Error
int32_t found = findFriendlyName(name);
if (found >= 0) {
const Z_Device & device = devicesAt(found);
return device.shortaddr; // can be zero, if not yet registered
return devicesAt(found);
} else {
return BAD_SHORTADDR;
return device_unk;
}
}
@ -146,7 +148,7 @@ uint64_t Z_Devices::getDeviceLongAddr(uint16_t shortaddr) const {
// We have a seen a shortaddr on the network, get the corresponding device object
//
Z_Device & Z_Devices::getShortAddr(uint16_t shortaddr) {
if (BAD_SHORTADDR == shortaddr) { return (Z_Device&) device_unk; } // this is not legal
if (BAD_SHORTADDR == shortaddr) { return device_unk; } // this is not legal
Z_Device & device = findShortAddr(shortaddr);
if (foundDevice(device)) {
return device;
@ -156,7 +158,7 @@ Z_Device & Z_Devices::getShortAddr(uint16_t shortaddr) {
// find the Device object by its longaddr (unique key if not null)
Z_Device & Z_Devices::getLongAddr(uint64_t longaddr) {
if (!longaddr) { return (Z_Device&) device_unk; }
if (!longaddr) { return device_unk; }
Z_Device & device = findLongAddr(longaddr);
if (foundDevice(device)) {
return device;
@ -211,7 +213,7 @@ Z_Device & Z_Devices::updateDevice(uint16_t shortaddr, uint64_t longaddr) {
if ((BAD_SHORTADDR != shortaddr) || longaddr) {
return createDeviceEntry(shortaddr, longaddr);
}
return (Z_Device&) device_unk;
return device_unk;
}
}
@ -308,29 +310,13 @@ void Z_Device::setFriendlyName(const char * str) {
setStringAttribute(friendlyName, str);
}
void Z_Devices::setReachable(uint16_t shortaddr, bool reachable) {
getShortAddr(shortaddr).setReachable(reachable);
}
void Z_Devices::setLQI(uint16_t shortaddr, uint8_t lqi) {
if (shortaddr == localShortAddr) { return; }
getShortAddr(shortaddr).lqi = lqi;
}
void Z_Devices::setLastSeenNow(uint16_t shortaddr) {
if (shortaddr == localShortAddr) { return; }
void Z_Device::setLastSeenNow(void) {
// Only update time if after 2020-01-01 0000.
// Fixes issue where zigbee device pings before WiFi/NTP has set utc_time
// to the correct time, and "last seen" calculations are based on the
// pre-corrected last_seen time and the since-corrected utc_time.
if (Rtc.utc_time < 1577836800) { return; }
getShortAddr(shortaddr).last_seen = Rtc.utc_time;
}
void Z_Devices::setBatteryPercent(uint16_t shortaddr, uint8_t bp) {
getShortAddr(shortaddr).batterypercent = bp;
last_seen = Rtc.utc_time;
}
// get the next sequance number for the device, or use the global seq number if device is unknown
@ -345,22 +331,32 @@ uint8_t Z_Devices::getNextSeqNumber(uint16_t shortaddr) {
}
}
// General Zigbee device profile support
void Z_Devices::setLightProfile(uint16_t shortaddr, uint8_t light_profile) {
Z_Device &device = getShortAddr(shortaddr);
if (device.setLightChannels(light_profile)) {
dirty();
// returns: dirty flag, did we change the value of the object
void Z_Device::setLightChannels(int8_t channels) {
if (channels >= 0) {
// retrieve of create light object
Z_Data_Light & light = data.get<Z_Data_Light>(0);
if (channels != light.getConfig()) {
light.setConfig(channels);
zigbee_devices.dirty();
}
Z_Data_OnOff & onoff = data.get<Z_Data_OnOff>(0);
} else {
// remove light / onoff object if any
for (auto & data_elt : data) {
if ((data_elt.getType() == Z_Data_Type::Z_Light) ||
(data_elt.getType() == Z_Data_Type::Z_OnOff)) {
// remove light object
data.remove(&data_elt);
zigbee_devices.dirty();
}
}
}
}
// Returns the device profile or 0xFF if the device or profile is unknown
uint8_t Z_Devices::getLightProfile(uint16_t shortaddr) const {
const Z_Device &device = findShortAddr(shortaddr);
return device.getLightChannels();
}
int8_t Z_Devices::getHueBulbtype(uint16_t shortaddr) const {
int8_t light_profile = getLightProfile(shortaddr);
const Z_Device &device = findShortAddr(shortaddr);
int8_t light_profile = device.getLightChannels();
if (0x00 == (light_profile & 0xF0)) {
return (light_profile & 0x07);
} else {
@ -575,57 +571,56 @@ void Z_Devices::clean(void) {
// - a long address starting with "0x", example: 0x7CB03EBB0A0292DD
// - a number 0..99, the index number in ZigbeeStatus
// - a friendly name, between quotes, example: "Room_Temp"
uint16_t Z_Devices::parseDeviceParam(const char * param, bool short_must_be_known) const {
if (nullptr == param) { return BAD_SHORTADDR; }
Z_Device & Z_Devices::parseDeviceFromName(const char * param, bool short_must_be_known) {
if (nullptr == param) { return device_unk; }
size_t param_len = strlen(param);
char dataBuf[param_len + 1];
strcpy(dataBuf, param);
RemoveSpace(dataBuf);
uint16_t shortaddr = BAD_SHORTADDR; // start with unknown
if (strlen(dataBuf) < 4) {
if ((dataBuf[0] >= '0') && (dataBuf[0] <= '9') && (strlen(dataBuf) < 4)) {
// simple number 0..99
if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload <= 99)) {
shortaddr = zigbee_devices.isKnownIndex(XdrvMailbox.payload - 1);
return isKnownIndexDevice(XdrvMailbox.payload - 1);
} else {
return device_unk;
}
} else if ((dataBuf[0] == '0') && ((dataBuf[1] == 'x') || (dataBuf[1] == 'X'))) {
// starts with 0x
if (strlen(dataBuf) < 18) {
// expect a short address
shortaddr = strtoull(dataBuf, nullptr, 0);
uint16_t shortaddr = strtoull(dataBuf, nullptr, 0);
if (short_must_be_known) {
shortaddr = zigbee_devices.findShortAddr(shortaddr).shortaddr; // if not found, it reverts to the unknown_device with address BAD_SHORTADDR
return (Z_Device&) findShortAddr(shortaddr); // if not found, it reverts to the unknown_device with address BAD_SHORTADDR
} else {
return getShortAddr(shortaddr); // create it if not registered
}
// else we don't check if it's already registered to force unregistered devices
} else {
// expect a long address
uint64_t longaddr = strtoull(dataBuf, nullptr, 0);
shortaddr = zigbee_devices.isKnownLongAddr(longaddr);
return isKnownLongAddrDevice(longaddr);
}
} else {
// expect a Friendly Name
shortaddr = zigbee_devices.isKnownFriendlyName(dataBuf);
return isKnownFriendlyNameDevice(dataBuf);
}
return shortaddr;
}
// Display the tracked status for a light
String Z_Devices::dumpLightState(uint16_t shortaddr) const {
String Z_Devices::dumpLightState(const Z_Device & device) {
Z_attribute_list attr_list;
char hex[8];
const Z_Device & device = findShortAddr(shortaddr);
const char * fname = getFriendlyName(shortaddr);
const char * fname = device.friendlyName;
bool use_fname = (Settings.flag4.zigbee_use_names) && (fname); // should we replace shortaddr with friendlyname?
snprintf_P(hex, sizeof(hex), PSTR("0x%04X"), shortaddr);
snprintf_P(hex, sizeof(hex), PSTR("0x%04X"), device.shortaddr);
attr_list.addAttribute(F(D_JSON_ZIGBEE_DEVICE)).setStr(hex);
if (fname) {
attr_list.addAttribute(F(D_JSON_ZIGBEE_NAME)).setStr(fname);
}
if (foundDevice(device)) {
if (device.valid()) {
// dump all known values
attr_list.addAttribute(F("Reachable")).setBool(device.getReachable());
if (device.validPower()) { attr_list.addAttribute(F("Power")).setUInt(device.getPower()); }
@ -654,60 +649,70 @@ String Z_Devices::dumpLightState(uint16_t shortaddr) const {
// Dump the internal memory of Zigbee devices
// Mode = 1: simple dump of devices addresses
// Mode = 2: simple dump of devices addresses and names, endpoints, light
String Z_Devices::dump(uint32_t dump_mode, uint16_t status_shortaddr) const {
String Z_Devices::dumpSingleDevice(uint32_t dump_mode, const class Z_Device & device) {
uint16_t shortaddr = device.shortaddr;
char hex[22];
Z_attribute_list attr_list;
snprintf_P(hex, sizeof(hex), PSTR("0x%04X"), shortaddr);
attr_list.addAttribute(F(D_JSON_ZIGBEE_DEVICE)).setStr(hex);
if (device.friendlyName > 0) {
attr_list.addAttribute(F(D_JSON_ZIGBEE_NAME)).setStr(device.friendlyName);
}
if (2 <= dump_mode) {
hex[0] = '0'; // prefix with '0x'
hex[1] = 'x';
Uint64toHex(device.longaddr, &hex[2], 64);
attr_list.addAttribute(F("IEEEAddr")).setStr(hex);
if (device.modelId) {
attr_list.addAttribute(F(D_JSON_MODEL D_JSON_ID)).setStr(device.modelId);
}
if (device.manufacturerId) {
attr_list.addAttribute(F("Manufacturer")).setStr(device.manufacturerId);
}
JsonGeneratorArray arr_ep;
for (uint32_t i = 0; i < endpoints_max; i++) {
uint8_t endpoint = device.endpoints[i];
if (0x00 == endpoint) { break; }
arr_ep.add(endpoint);
}
attr_list.addAttribute(F("Endpoints")).setStrRaw(arr_ep.toString().c_str());
JsonGeneratorArray arr_data;
for (auto & data_elt : device.data) {
char key[8];
if (data_elt.validConfig()) {
snprintf_P(key, sizeof(key), "?%02X.%1X", data_elt.getEndpoint(), data_elt.getConfig());
} else {
snprintf_P(key, sizeof(key), "?%02X", data_elt.getEndpoint());
}
key[0] = Z_Data::DataTypeToChar(data_elt.getType());
arr_data.addStr(key);
}
attr_list.addAttribute(F("Config")).setStrRaw(arr_data.toString().c_str());
}
return attr_list.toString(true);
}
// If &device == nullptr, then dump all
String Z_Devices::dumpDevice(uint32_t dump_mode, const Z_Device & device) const {
JsonGeneratorArray json_arr;
for (const auto & device : _devices) {
uint16_t shortaddr = device.shortaddr;
char hex[22];
// ignore non-current device, if device specified
if ((BAD_SHORTADDR != status_shortaddr) && (status_shortaddr != shortaddr)) { continue; }
Z_attribute_list attr_list;
snprintf_P(hex, sizeof(hex), PSTR("0x%04X"), shortaddr);
attr_list.addAttribute(F(D_JSON_ZIGBEE_DEVICE)).setStr(hex);
if (device.friendlyName > 0) {
attr_list.addAttribute(F(D_JSON_ZIGBEE_NAME)).setStr(device.friendlyName);
if (&device == nullptr) {
if (dump_mode < 2) {
// dump light mode for all devices
for (const auto & device2 : _devices) {
json_arr.addStrRaw(dumpSingleDevice(dump_mode, device2).c_str());
}
}
if (2 <= dump_mode) {
hex[0] = '0'; // prefix with '0x'
hex[1] = 'x';
Uint64toHex(device.longaddr, &hex[2], 64);
attr_list.addAttribute(F("IEEEAddr")).setStr(hex);
if (device.modelId) {
attr_list.addAttribute(F(D_JSON_MODEL D_JSON_ID)).setStr(device.modelId);
}
if (device.manufacturerId) {
attr_list.addAttribute(F("Manufacturer")).setStr(device.manufacturerId);
}
JsonGeneratorArray arr_ep;
for (uint32_t i = 0; i < endpoints_max; i++) {
uint8_t endpoint = device.endpoints[i];
if (0x00 == endpoint) { break; }
arr_ep.add(endpoint);
}
attr_list.addAttribute(F("Endpoints")).setStrRaw(arr_ep.toString().c_str());
JsonGeneratorArray arr_data;
for (auto & data_elt : device.data) {
char key[8];
if (data_elt.validConfig()) {
snprintf_P(key, sizeof(key), "?%02X.%1X", data_elt.getEndpoint(), data_elt.getConfig());
} else {
snprintf_P(key, sizeof(key), "?%02X", data_elt.getEndpoint());
}
key[0] = Z_Data::DataTypeToChar(data_elt.getType());
arr_data.addStr(key);
}
attr_list.addAttribute(F("Config")).setStrRaw(arr_data.toString().c_str());
}
json_arr.addStrRaw(attr_list.toString(true).c_str());
} else {
json_arr.addStrRaw(dumpSingleDevice(dump_mode, device).c_str());
}
return json_arr.toString();
}

View File

@ -257,7 +257,7 @@ void hydrateSingleDevice(const SBuffer & buf_d, uint32_t version) {
// Hue bulbtype - if present
if (1 == version) {
zigbee_devices.setLightProfile(shortaddr, buf_d.get8(d));
device.setLightChannels(buf_d.get8(d));
d++;
} else if (2 == version) {
// v2 parser

View File

@ -1853,7 +1853,7 @@ void Z_postProcessAttributes(uint16_t shortaddr, uint16_t src_ep, class Z_attrib
switch (ccccaaaa) {
case 0x00000004: device.setManufId(attr.getStr()); break;
case 0x00000005: device.setModelId(attr.getStr()); break;
case 0x00010021: zigbee_devices.setBatteryPercent(shortaddr, uval16 / 2); break;
case 0x00010021: device.setBatteryPercent(uval16 / 2); break;
case 0x00060000:
case 0x00068000: device.setPower(attr.getBool(), src_ep); break;
}

View File

@ -199,7 +199,7 @@ void Z_ReadAttrCallback(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster
// This callback is registered after a an attribute read command was made to a light, and fires if we don't get any response after 1000 ms
void Z_Unreachable(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster, uint8_t endpoint, uint32_t value) {
if (BAD_SHORTADDR != shortaddr) {
zigbee_devices.setReachable(shortaddr, false); // mark device as reachable
zigbee_devices.getShortAddr(shortaddr).setReachable(false); // mark device as reachable
}
}

View File

@ -1470,8 +1470,11 @@ void Z_IncomingMessage(class ZCLFrame &zcl_received) {
// log the packet details
zcl_received.log();
zigbee_devices.setLQI(srcaddr, linkquality != 0xFF ? linkquality : 0xFE); // EFR32 has a different scale for LQI
zigbee_devices.setLastSeenNow(srcaddr);
Z_Device & device = zigbee_devices.getShortAddr(srcaddr);
if (srcaddr != localShortAddr) {
device.setLQI(linkquality != 0xFF ? linkquality : 0xFE); // EFR32 has a different scale for LQI
device.setLastSeenNow();
}
char shortaddr[8];
snprintf_P(shortaddr, sizeof(shortaddr), PSTR("0x%04X"), srcaddr);
@ -1519,7 +1522,7 @@ void Z_IncomingMessage(class ZCLFrame &zcl_received) {
// since we just receveived data from the device, it is reachable
zigbee_devices.resetTimersForDevice(srcaddr, 0 /* groupaddr */, Z_CAT_REACHABILITY); // remove any reachability timer already there
zigbee_devices.setReachable(srcaddr, true); // mark device as reachable
device.setReachable(true); // mark device as reachable
if (defer_attributes) {
// Prepare for publish
@ -1615,8 +1618,11 @@ int32_t EZ_IncomingMessage(int32_t res, const class SBuffer &buf) {
if ((0x0000 == profileid) && (0x00 == srcendpoint)) {
// ZDO request
// Report LQI
zigbee_devices.setLQI(srcaddr, linkquality);
zigbee_devices.setLastSeenNow(srcaddr);
Z_Device & device = zigbee_devices.getShortAddr(srcaddr);
if (srcaddr != localShortAddr) {
device.setLQI(linkquality);
device.setLastSeenNow();
}
// Since ZDO messages start with a sequence number, we skip it
// but we add the source address in the last 2 bytes
SBuffer zdo_buf(buf.get8(20) - 1 + 2);

View File

@ -683,7 +683,7 @@ void CmndZbSend(void) {
// parse "Device" and "Group"
JsonParserToken val_device = root[PSTR(D_CMND_ZIGBEE_DEVICE)];
if (val_device) {
device = zigbee_devices.parseDeviceParam(val_device.getStr());
device = zigbee_devices.parseDeviceFromName(val_device.getStr(), true).shortaddr;
if (BAD_SHORTADDR == device) { ResponseCmndChar_P(PSTR("Invalid parameter")); return; }
}
if (BAD_SHORTADDR == device) { // if not found, check if we have a group
@ -823,19 +823,19 @@ void ZbBindUnbind(bool unbind) { // false = bind, true = unbind
uint8_t endpoint = 0x00; // 0x00 is invalid for the src endpoint
uint8_t toendpoint = 0x01; // default dest endpoint to 0x01
uint16_t toGroup = 0x0000; // group address
uint16_t cluster = 0; // 0xFFFF is invalid
uint16_t cluster = 0; // cluster 0 is default
uint32_t group = 0xFFFFFFFF; // 16 bits values, otherwise 0xFFFFFFFF is unspecified
// Information about source device: "Device", "Endpoint", "Cluster"
// - the source endpoint must have a known IEEE address
srcDevice = zigbee_devices.parseDeviceParam(root.getStr(PSTR(D_CMND_ZIGBEE_DEVICE), nullptr));
if (BAD_SHORTADDR == srcDevice) { ResponseCmndChar_P(PSTR("Unknown source device")); return; }
const Z_Device & src_device = zigbee_devices.parseDeviceFromName(root.getStr(PSTR(D_CMND_ZIGBEE_DEVICE), nullptr), true);
if (!src_device.valid()) { ResponseCmndChar_P(PSTR("Unknown source device")); return; }
// check if IEEE address is known
uint64_t srcLongAddr = zigbee_devices.getDeviceLongAddr(srcDevice);
uint64_t srcLongAddr = src_device.longaddr;
if (0 == srcLongAddr) { ResponseCmndChar_P(PSTR("Unknown source IEEE address")); return; }
// look for source endpoint
endpoint = root.getUInt(PSTR(D_CMND_ZIGBEE_ENDPOINT), endpoint);
if (0 == endpoint) { endpoint = zigbee_devices.findFirstEndpoint(srcDevice); }
if (0 == endpoint) { endpoint = zigbee_devices.findFirstEndpoint(src_device.shortaddr); }
// look for source cluster
JsonParserToken val_cluster = root[PSTR(D_CMND_ZIGBEE_CLUSTER)];
if (val_cluster) {
@ -858,26 +858,23 @@ void ZbBindUnbind(bool unbind) { // false = bind, true = unbind
// If no target is specified, we default to coordinator 0x0000
if ((!to_group) && (!dst_device)) {
dstDevice = 0x0000;
dstLongAddr = localIEEEAddr;
toendpoint = 1;
}
if ((dst_device) || (BAD_SHORTADDR != dstDevice)) {
if (BAD_SHORTADDR == dstDevice) {
dstDevice = zigbee_devices.parseDeviceParam(dst_device.getStr(nullptr));
if (BAD_SHORTADDR == dstDevice) { ResponseCmndChar_P(PSTR("Invalid parameter")); return; }
}
if (0x0000 == dstDevice) {
dstLongAddr = localIEEEAddr;
} else {
dstLongAddr = zigbee_devices.getDeviceLongAddr(dstDevice);
}
if (0 == dstLongAddr) { ResponseCmndChar_P(PSTR("Unknown dest IEEE address")); return; }
if (dst_device) {
const Z_Device & dstDevice = zigbee_devices.parseDeviceFromName(dst_device.getStr(nullptr), true);
if (!dstDevice.valid()) { ResponseCmndChar_P(PSTR("Unknown dest device")); return; }
dstLongAddr = dstDevice.longaddr;
}
if (!to_group) {
if (0 == dstLongAddr) { ResponseCmndChar_P(PSTR("Unknown dest IEEE address")); return; }
toendpoint = root.getUInt(PSTR("ToEndpoint"), toendpoint);
}
// make sure we don't have conflicting parameters
if (to_group && dstLongAddr) { ResponseCmndChar_P(PSTR("Cannot have both \"ToDevice\" and \"ToGroup\"")); return; }
if (!to_group && !dstLongAddr) { ResponseCmndChar_P(PSTR("Missing \"ToDevice\" or \"ToGroup\"")); return; }
if (to_group && dst_device) { ResponseCmndChar_P(PSTR("Cannot have both \"ToDevice\" and \"ToGroup\"")); return; }
#ifdef USE_ZIGBEE_ZNP
SBuffer buf(34);
@ -891,7 +888,7 @@ void ZbBindUnbind(bool unbind) { // false = bind, true = unbind
buf.add64(srcLongAddr);
buf.add8(endpoint);
buf.add16(cluster);
if (dstLongAddr) {
if (!to_group) {
buf.add8(Z_Addr_IEEEAddress); // DstAddrMode - 0x03 = ADDRESS_64_BIT
buf.add64(dstLongAddr);
buf.add8(toendpoint);
@ -910,7 +907,7 @@ void ZbBindUnbind(bool unbind) { // false = bind, true = unbind
buf.add64(srcLongAddr);
buf.add8(endpoint);
buf.add16(cluster);
if (dstLongAddr) {
if (!to_group) {
buf.add8(Z_Addr_IEEEAddress); // DstAddrMode - 0x03 = ADDRESS_64_BIT
buf.add64(dstLongAddr);
buf.add8(toendpoint);
@ -941,7 +938,7 @@ void CmndZbUnbind(void) {
void CmndZbBindState_or_Map(uint16_t zdo_cmd) {
if (zigbee.init_phase) { ResponseCmndChar_P(PSTR(D_ZIGBEE_NOT_STARTED)); return; }
uint16_t shortaddr = zigbee_devices.parseDeviceParam(XdrvMailbox.data);
uint16_t shortaddr = zigbee_devices.parseDeviceFromName(XdrvMailbox.data, true).shortaddr;
if (BAD_SHORTADDR == shortaddr) { ResponseCmndChar_P(PSTR("Unknown device")); return; }
uint8_t index = XdrvMailbox.index - 1; // change default 1 to 0
@ -1002,7 +999,7 @@ void CmndZbProbe(void) {
//
void CmndZbProbeOrPing(boolean probe) {
if (zigbee.init_phase) { ResponseCmndChar_P(PSTR(D_ZIGBEE_NOT_STARTED)); return; }
uint16_t shortaddr = zigbee_devices.parseDeviceParam(XdrvMailbox.data);
uint16_t shortaddr = zigbee_devices.parseDeviceFromName(XdrvMailbox.data, true).shortaddr;
if (BAD_SHORTADDR == shortaddr) { ResponseCmndChar_P(PSTR("Unknown device")); return; }
// everything is good, we can send the command
@ -1034,19 +1031,19 @@ void CmndZbName(void) {
// check if parameters contain a comma ','
char *p;
char *str = strtok_r(XdrvMailbox.data, ", ", &p);
char *str = strtok_r(XdrvMailbox.data, ",", &p);
// parse first part, <device_id>
uint16_t shortaddr = zigbee_devices.parseDeviceParam(XdrvMailbox.data, true); // in case of short_addr, it must be already registered
if (BAD_SHORTADDR == shortaddr) { ResponseCmndChar_P(PSTR("Unknown device")); return; }
Z_Device & device = zigbee_devices.parseDeviceFromName(XdrvMailbox.data, false); // it's the only case where we create a new device
if (!device.valid()) { ResponseCmndChar_P(PSTR("Unknown device")); return; }
if (p == nullptr) {
const char * friendlyName = zigbee_devices.getFriendlyName(shortaddr);
Response_P(PSTR("{\"0x%04X\":{\"" D_JSON_ZIGBEE_NAME "\":\"%s\"}}"), shortaddr, friendlyName ? friendlyName : "");
const char * friendlyName = device.friendlyName;
Response_P(PSTR("{\"0x%04X\":{\"" D_JSON_ZIGBEE_NAME "\":\"%s\"}}"), device.shortaddr, friendlyName ? friendlyName : "");
} else {
if (strlen(p) > 32) { p[32] = 0x00; } // truncate to 32 chars max
zigbee_devices.getShortAddr(shortaddr).setFriendlyName(p);
Response_P(PSTR("{\"0x%04X\":{\"" D_JSON_ZIGBEE_NAME "\":\"%s\"}}"), shortaddr, p);
device.setFriendlyName(p);
Response_P(PSTR("{\"0x%04X\":{\"" D_JSON_ZIGBEE_NAME "\":\"%s\"}}"), device.shortaddr, p);
}
}
@ -1066,18 +1063,18 @@ void CmndZbModelId(void) {
// check if parameters contain a comma ','
char *p;
char *str = strtok_r(XdrvMailbox.data, ", ", &p);
char *str = strtok_r(XdrvMailbox.data, ",", &p);
// parse first part, <device_id>
uint16_t shortaddr = zigbee_devices.parseDeviceParam(XdrvMailbox.data, true); // in case of short_addr, it must be already registered
if (BAD_SHORTADDR == shortaddr) { ResponseCmndChar_P(PSTR("Unknown device")); return; }
Z_Device & device = zigbee_devices.parseDeviceFromName(XdrvMailbox.data, true); // in case of short_addr, it must be already registered
if (!device.valid()) { ResponseCmndChar_P(PSTR("Unknown device")); return; }
if (p == nullptr) {
const char * modelId = zigbee_devices.getModelId(shortaddr);
Response_P(PSTR("{\"0x%04X\":{\"" D_JSON_ZIGBEE_MODELID "\":\"%s\"}}"), shortaddr, modelId ? modelId : "");
const char * modelId = device.modelId;
Response_P(PSTR("{\"0x%04X\":{\"" D_JSON_ZIGBEE_MODELID "\":\"%s\"}}"), device.shortaddr, modelId ? modelId : "");
} else {
zigbee_devices.getShortAddr(shortaddr).setModelId(p);
Response_P(PSTR("{\"0x%04X\":{\"" D_JSON_ZIGBEE_MODELID "\":\"%s\"}}"), shortaddr, p);
device.setModelId(p);
Response_P(PSTR("{\"0x%04X\":{\"" D_JSON_ZIGBEE_MODELID "\":\"%s\"}}"), device.shortaddr, p);
}
}
@ -1098,16 +1095,16 @@ void CmndZbLight(void) {
char *str = strtok_r(XdrvMailbox.data, ", ", &p);
// parse first part, <device_id>
uint16_t shortaddr = zigbee_devices.parseDeviceParam(XdrvMailbox.data, true); // in case of short_addr, it must be already registered
if (BAD_SHORTADDR == shortaddr) { ResponseCmndChar_P(PSTR("Unknown device")); return; }
Z_Device & device = zigbee_devices.parseDeviceFromName(XdrvMailbox.data, true); // in case of short_addr, it must be already registered
if (!device.valid()) { ResponseCmndChar_P(PSTR("Unknown device")); return; }
if (p) {
int8_t bulbtype = strtol(p, nullptr, 10);
if (bulbtype > 5) { bulbtype = 5; }
if (bulbtype < -1) { bulbtype = -1; }
zigbee_devices.setLightProfile(shortaddr, bulbtype);
device.setLightChannels(bulbtype);
}
String dump = zigbee_devices.dumpLightState(shortaddr);
String dump = zigbee_devices.dumpLightState(device);
Response_P(PSTR("{\"" D_PRFX_ZB D_CMND_ZIGBEE_LIGHT "\":%s}"), dump.c_str());
MqttPublishPrefixTopicRulesProcess_P(RESULT_OR_STAT, PSTR(D_PRFX_ZB D_CMND_ZIGBEE_LIGHT));
@ -1141,10 +1138,8 @@ void CmndZbOccupancy(void) {
char *str = strtok_r(XdrvMailbox.data, ", ", &p);
// parse first part, <device_id>
uint16_t shortaddr = zigbee_devices.parseDeviceParam(XdrvMailbox.data, true); // in case of short_addr, it must be already registered
if (BAD_SHORTADDR == shortaddr) { ResponseCmndChar_P(PSTR("Unknown device")); return; }
Z_Device & device = zigbee_devices.getShortAddr(shortaddr);
Z_Device & device = zigbee_devices.parseDeviceFromName(XdrvMailbox.data, true); // in case of short_addr, it must be already registered
if (!device.valid()) { ResponseCmndChar_P(PSTR("Unknown device")); return; }
int8_t occupancy_time = -1;
if (p) {
@ -1170,11 +1165,11 @@ void CmndZbOccupancy(void) {
//
void CmndZbForget(void) {
if (zigbee.init_phase) { ResponseCmndChar_P(PSTR(D_ZIGBEE_NOT_STARTED)); return; }
uint16_t shortaddr = zigbee_devices.parseDeviceParam(XdrvMailbox.data);
if (BAD_SHORTADDR == shortaddr) { ResponseCmndChar_P(PSTR("Unknown device")); return; }
Z_Device & device = zigbee_devices.parseDeviceFromName(XdrvMailbox.data, true); // in case of short_addr, it must be already registered
if (!device.valid()) { ResponseCmndChar_P(PSTR("Unknown device")); return; }
// everything is good, we can send the command
if (zigbee_devices.removeDevice(shortaddr)) {
if (zigbee_devices.removeDevice(device.shortaddr)) {
ResponseCmndDone();
} else {
ResponseCmndChar_P(PSTR("Unknown device"));
@ -1369,12 +1364,17 @@ void ZigbeeGlowPermitJoinLight(void) {
void CmndZbStatus(void) {
if (ZigbeeSerial) {
if (zigbee.init_phase) { ResponseCmndChar_P(PSTR(D_ZIGBEE_NOT_STARTED)); return; }
uint16_t shortaddr = zigbee_devices.parseDeviceParam(XdrvMailbox.data);
String dump;
Z_Device & device = zigbee_devices.parseDeviceFromName(XdrvMailbox.data, true);
if (XdrvMailbox.data_len > 0) {
if (BAD_SHORTADDR == shortaddr) { ResponseCmndChar_P(PSTR("Unknown device")); return; }
if (!device.valid()) { ResponseCmndChar_P(PSTR("Unknown device")); return; }
dump = zigbee_devices.dumpDevice(XdrvMailbox.index, device);
} else {
if (XdrvMailbox.index >= 2) { ResponseCmndChar_P(PSTR("Unknown device")); return; }
dump = zigbee_devices.dumpDevice(XdrvMailbox.index, *(Z_Device*)nullptr);
}
String dump = zigbee_devices.dump(XdrvMailbox.index, shortaddr);
Response_P(PSTR("{\"%s%d\":%s}"), XdrvMailbox.command, XdrvMailbox.index, dump.c_str());
}
}
@ -1502,9 +1502,8 @@ void CmndZbData(void) {
}
for (auto device_name : root) {
uint16_t shortaddr = zigbee_devices.parseDeviceParam(device_name.getStr());
if (BAD_SHORTADDR == shortaddr) { ResponseCmndChar_P(PSTR("Unknown device")); return; }
Z_Device & device = zigbee_devices.getShortAddr(shortaddr);
Z_Device & device = zigbee_devices.parseDeviceFromName(device_name.getStr(), true);
if (!device.valid()) { ResponseCmndChar_P(PSTR("Unknown device")); return; }
JsonParserObject inner_data = device_name.getValue().getObject();
if (inner_data) {
if (!parseDeviceInnerData(device, inner_data)) {
@ -1518,9 +1517,8 @@ void CmndZbData(void) {
// non-JSON, export current data
// ZbData 0x1234
// ZbData Device_Name
uint16_t shortaddr = zigbee_devices.parseDeviceParam(XdrvMailbox.data);
if (BAD_SHORTADDR == shortaddr) { ResponseCmndChar_P(PSTR("Unknown device")); return; }
const Z_Device & device = zigbee_devices.findShortAddr(shortaddr);
Z_Device & device = zigbee_devices.parseDeviceFromName(XdrvMailbox.data, true);
if (!device.valid()) { ResponseCmndChar_P(PSTR("Unknown device")); return; }
Z_attribute_list attr_data;
@ -1568,7 +1566,7 @@ void CmndZbData(void) {
}
char hex[8];
snprintf_P(hex, sizeof(hex), PSTR("0x%04X"), shortaddr);
snprintf_P(hex, sizeof(hex), PSTR("0x%04X"), device.shortaddr);
Response_P(PSTR("{\"%s\":{\"%s\":%s}}"), XdrvMailbox.command, hex, attr_data.toString(true).c_str());
}
}

View File

@ -107,9 +107,21 @@ struct METER_DESC {
#define COMBO3b 12
#define WGS_COMBO 13
#define EBZD_G 14
#define SML_NO_OP 15
// select this meter
#define METER EHZ161_1
// SML_NO_OP ignores hardcoded interface
#define METER SML_NO_OP
//#define METER EHZ161_1
#if METER==SML_NO_OP
#undef METERS_USED
#define METERS_USED 0
struct METER_DESC const meter_desc[1]={
[0]={3,'o',0,SML_BAUDRATE,"OBIS",-1,1,0}};
const uint8_t meter[]=
"1,1-0:1.8.0*255(@1," D_TPWRIN ",kWh," DJ_TPWRIN ",4|";
#endif
#if METER==EHZ161_0
@ -1662,6 +1674,7 @@ void SML_Show(boolean json) {
//char b_mqtt_data[MESSZ];
//b_mqtt_data[0]=0;
if (!meters_used) return;
int8_t lastmind=((*mp)&7)-1;
if (lastmind<0 || lastmind>=meters_used) lastmind=0;
@ -1794,7 +1807,7 @@ void SML_Show(boolean json) {
}
/*
#ifdef USE_DOMOTICZ
if (json && !TasmotaGlobal.tele_period) {
char str[16];
@ -1806,7 +1819,7 @@ void SML_Show(boolean json) {
DomoticzSensor(DZ_CURRENT, str); // Current
}
#endif // USE_DOMOTICZ
*/
}
struct SML_COUNTER {
@ -1826,39 +1839,32 @@ struct SML_COUNTER {
#endif
} sml_counters[MAX_COUNTERS];
void ICACHE_RAM_ATTR SML_CounterUpd(uint8_t index) {
uint8_t sml_counter_pinstate;
uint8_t level=digitalRead(meter_desc_p[sml_counters[index].sml_cnt_old_state].srcpin);
if (!level) {
// falling edge
uint32_t ltime=millis()-sml_counters[index].sml_counter_ltime;
sml_counters[index].sml_counter_ltime=millis();
if (ltime>sml_counters[index].sml_debounce) {
RtcSettings.pulse_counter[index]++;
sml_counters[index].sml_cnt_updated=1;
//InjektCounterValue(sml_counters[index].sml_cnt_old_state,RtcSettings.pulse_counter[index]);
}
} else {
// rising edge
sml_counters[index].sml_counter_ltime=millis();
uint8_t sml_cnt_index[MAX_COUNTERS] = { 0, 1, 2, 3 };
void ICACHE_RAM_ATTR SML_CounterIsr(void *arg) {
uint32_t index = *static_cast<uint8_t*>(arg);
uint32_t time = micros();
uint32_t debounce_time;
if (digitalRead(meter_desc_p[sml_counters[index].sml_cnt_old_state].srcpin) == bitRead(sml_counter_pinstate, index)) {
return;
}
debounce_time = time - sml_counters[index].sml_counter_ltime;
if (debounce_time <= sml_counters[index].sml_debounce * 1000) return;
if bitRead(sml_counter_pinstate, index) {
// falling edge
RtcSettings.pulse_counter[index]++;
sml_counters[index].sml_cnt_updated=1;
}
sml_counters[index].sml_counter_ltime = time;
sml_counter_pinstate ^= (1<<index);
}
void ICACHE_RAM_ATTR SML_CounterUpd1(void) {
SML_CounterUpd(0);
}
void ICACHE_RAM_ATTR SML_CounterUpd2(void) {
SML_CounterUpd(1);
}
void ICACHE_RAM_ATTR SML_CounterUpd3(void) {
SML_CounterUpd(2);
}
void ICACHE_RAM_ATTR SML_CounterUpd4(void) {
SML_CounterUpd(3);
}
#ifdef USE_SCRIPT
struct METER_DESC script_meter_desc[MAX_METERS];
@ -1916,14 +1922,6 @@ uint32_t SML_getscriptsize(char *lp) {
#endif
bool Gpio_used(uint8_t gpiopin) {
/*
for (uint16_t i=0;i<GPIO_SENSOR_END;i++) {
// if (pin_gpio[i]==gpiopin) {
if (Pin(i)==gpiopin) {
return true;
}
}
*/
if ((gpiopin < ARRAY_SIZE(TasmotaGlobal.gpio_pin)) && (TasmotaGlobal.gpio_pin[gpiopin] > 0)) {
return true;
}
@ -2131,7 +2129,6 @@ next_line:
init10:
typedef void (*function)();
function counter_callbacks[] = {SML_CounterUpd1,SML_CounterUpd2,SML_CounterUpd3,SML_CounterUpd4};
uint8_t cindex=0;
// preloud counters
for (byte i = 0; i < MAX_COUNTERS; i++) {
@ -2158,7 +2155,7 @@ init10:
// check for irq mode
if (meter_desc_p[meters].params<=0) {
// init irq mode
attachInterrupt(meter_desc_p[meters].srcpin, counter_callbacks[cindex], CHANGE);
attachInterruptArg(meter_desc_p[meters].srcpin, SML_CounterIsr,&sml_cnt_index[cindex], CHANGE);
sml_counters[cindex].sml_cnt_old_state=meters;
sml_counters[cindex].sml_debounce=-meter_desc_p[meters].params;
}

View File

@ -20,6 +20,8 @@
--------------------------------------------------------------------------------------------
Version yyyymmdd Action Description
--------------------------------------------------------------------------------------------
0.9.5.0 20201101 added - bugfixes, better advertisement parsing, beacon, HASS-fixes, CGD1 now passive readable
---
0.9.4.1 20200807 added - add ATC, some optimizations and a bit more error handling
---
0.9.4.0 20200807 added - multiple backports from the HM10-driver (NLIGHT,MJYD2S,YEERC,MHOC401,MHOC303),
@ -54,44 +56,7 @@ TasmotaSerial *HM10Serial;
#define HM10_MAX_TASK_NUMBER 12
uint8_t HM10_TASK_LIST[HM10_MAX_TASK_NUMBER+1][2]; // first value: kind of task - second value: delay in x * 100ms
#define HM10_MAX_RX_BUF 384
struct {
uint8_t current_task_delay; // number of 100ms-cycles
uint8_t last_command;
uint16_t perPage = 4;
uint16_t firmware;
uint32_t period; // set manually in addition to TELE-period, is set to TELE-period after start
uint32_t serialSpeed;
union {
uint32_t time;
uint8_t timebuf[4];
};
uint16_t autoScanInterval;
struct {
uint32_t awaiting:8;
uint32_t init:1;
uint32_t pending_task:1;
uint32_t connected:1;
uint32_t subscribed:1;
uint32_t autoScan:1;
uint32_t shallTriggerTele:1;
uint32_t triggeredTele:1;
} mode;
struct {
uint8_t sensor; // points to to the number 0...255
// TODO: more to come
} state;
struct {
uint32_t allwaysAggregate:1;
uint32_t showRSSI:1;
uint32_t ignoreBogusBattery:1;
uint32_t noSummary:1;
uint32_t minimalSummary:1;
uint32_t noRealTime:1;
} option;
char *rxBuffer;
} HM10;
#define HM10_MAX_RX_BUF 64
#pragma pack(1) // byte-aligned structures to read the sensor data
@ -152,8 +117,44 @@ struct ATCPacket_t{
uint8_t frameCnt;
};
struct cg_packet_t {
uint16_t frameID;
uint8_t MAC[6];
uint16_t mode;
union {
struct {
int16_t temp; // -9 - 59 °C
uint16_t hum;
};
uint8_t bat;
};
};
#pragma pack(0)
struct scan_entry_t {
uint8_t MAC[6];
uint16_t CID;
uint16_t UUID;
int32_t RSSI;
uint8_t TX;
union{
uint16_t SVC;
uint8_t svcData[32];
};
};
struct generic_beacon_t {
uint8_t MAC[6];
uint32_t time;
int32_t RSSI;
uint16_t CID; // company identifier
uint16_t UUID; // the first, if more than one exists
uint16_t SVC;
uint16_t TX;
bool active = false;
};
struct mi_sensor_t{
uint8_t type; //Flora = 1; MI-HT_V1=2; LYWSD02=3; LYWSD03=4; CGG1=5; CGD1=6
uint8_t lastCnt; //device generated counter of the packet
@ -216,8 +217,51 @@ struct mi_sensor_t{
};
};
struct {
uint8_t current_task_delay; // number of 100ms-cycles
uint8_t last_command;
uint16_t perPage = 4;
uint16_t firmware;
uint32_t period; // set manually in addition to TELE-period, is set to TELE-period after start
uint32_t serialSpeed;
union {
uint32_t time;
uint8_t timebuf[4];
};
uint16_t autoScanInterval;
struct {
uint32_t awaiting:8;
uint32_t init:1;
uint32_t pending_task:1;
uint32_t connected:1;
uint32_t subscribed:1;
uint32_t autoScan:1;
uint32_t shallTriggerTele:1;
uint32_t triggeredTele:1;
uint32_t activeBeacon:1;
uint32_t firstAutodiscoveryDone:1;
} mode;
struct {
uint8_t sensor; // points to to the number 0...255
uint8_t beaconScanCounter;
// TODO: more to come
} state;
struct {
uint32_t allwaysAggregate:1; // always show all known values of one sensor in brdigemode
uint32_t noSummary:1; // no sensor values at TELE-period
uint32_t directBridgeMode:1; // send every received BLE-packet as a MQTT-message in real-time
uint32_t holdBackFirstAutodiscovery:1; // allows to trigger it later
uint32_t showRSSI:1;
uint32_t ignoreBogusBattery:1;
uint32_t minimalSummary:1; // DEPRECATED!!
} option;
scan_entry_t rxAdvertisement;
char *rxBuffer;
} HM10;
std::vector<mi_sensor_t> MIBLEsensors;
std::array<generic_beacon_t,4> MIBLEbeacons; // we support a fixed number
std::vector<scan_entry_t> MINBLEscanResult;
/*********************************************************************************************\
* constants
@ -226,8 +270,9 @@ std::vector<mi_sensor_t> MIBLEsensors;
#define D_CMND_HM10 "HM10"
const char S_JSON_HM10_COMMAND_NVALUE[] PROGMEM = "{\"" D_CMND_HM10 "%s\":%d}";
const char S_JSON_HM10_COMMAND_SVALUE[] PROGMEM = "{\"" D_CMND_HM10 "%s%u\":\"%s\"}";
const char S_JSON_HM10_COMMAND[] PROGMEM = "{\"" D_CMND_HM10 "%s%s\"}";
const char kHM10_Commands[] PROGMEM = "Scan|AT|Period|Baud|Time|Auto|Page";
const char kHM10_Commands[] PROGMEM = "Scan|AT|Period|Baud|Time|Auto|Page|Beacon";
#define FLORA 1
#define MJ_HT_V1 2
@ -285,7 +330,8 @@ enum HM10_Commands { // commands useable in console or rules
CMND_HM10_BAUD, // serial speed of ESP8266 (<-> HM10), does not change baud rate of HM10
CMND_HM10_TIME, // set LYWSD02-Time from ESP8266-time
CMND_HM10_AUTO, // do discovery scans permanently to receive MiBeacons in seconds between read-cycles
CMND_HM10_PAGE // sensor entries per web page, which will be shown alternated
CMND_HM10_PAGE, // sensor entries per web page, which will be shown alternated
CMND_HM10_BEACON // add up to 4 beacons defined by their MAC addresses
};
enum HM10_awaitData: uint8_t {
@ -354,6 +400,50 @@ void HM10_TaskReplaceInSlot(uint8_t task, uint8_t slot){
HM10_TASK_LIST[slot][0] = task;
}
/**
* @brief Remove all colons from null terminated char array
*
* @param _string Typically representing a MAC-address like AA:BB:CC:DD:EE:FF
*/
void HM10stripColon(char* _string){
uint32_t _length = strlen(_string);
uint32_t _index = 0;
while (_index < _length) {
char c = _string[_index];
if(c==':'){
memmove(_string+_index,_string+_index+1,_length-_index);
}
_index++;
}
_string[_index] = 0;
}
/**
* @brief Convert string that repesents a hexadecimal number to a byte array
*
* @param _string input string in format: AABBCCDDEEFF or AA:BB:CC:DD:EE:FF, caseinsensitive
* @param _mac target byte array must match the correct size (i.e. AA:BB -> uint8_t bytes[2])
*/
void HM10HexStringToBytes(char* _string, uint8_t* _byteArray) {
HM10stripColon(_string);
UpperCase(_string,_string);
uint32_t index = 0;
uint32_t _end = strlen(_string);
memset(_byteArray,0,_end/2);
while (index < _end) {
char c = _string[index];
uint8_t value = 0;
if(c >= '0' && c <= '9')
value = (c - '0');
else if (c >= 'A' && c <= 'F')
value = (10 + (c - 'A'));
_byteArray[(index/2)] += value << (((index + 1) % 2) * 4);
index++;
}
}
void HM10_ReverseMAC(uint8_t _mac[]){
uint8_t _reversedMAC[6];
for (uint8_t i=0; i<6; i++){
@ -361,7 +451,23 @@ void HM10_ReverseMAC(uint8_t _mac[]){
}
memcpy(_mac,_reversedMAC, sizeof(_reversedMAC));
}
#ifdef USE_HOME_ASSISTANT
/**
* @brief For HASS only, changes last entry of JSON in mqtt_data to 'null'
*/
void HM10nullifyEndOfMQTT_DATA(){
char *p = TasmotaGlobal.mqtt_data + strlen(TasmotaGlobal.mqtt_data);
while(true){
*p--;
if(p[0]==':'){
p[1] = 0;
break;
}
}
ResponseAppend_P(PSTR("null"));
}
#endif // USE_HOME_ASSISTANT
/*********************************************************************************************\
* chained tasks
\*********************************************************************************************/
@ -377,9 +483,9 @@ void HM10_Reset(void) { HM10_Launchtask(TASK_HM10_DISCONN,0,1); // disco
}
void HM10_Discovery_Scan(void) {
HM10_Launchtask(TASK_HM10_DISCONN,0,1); // disconnect
HM10_Launchtask(TASK_HM10_DISC,1,1); // discovery
HM10_Launchtask(TASK_HM10_STATUS_EVENT,2,1); // status
// HM10_Launchtask(TASK_HM10_DISCONN,0,1); // disconnect
HM10_Launchtask(TASK_HM10_DISC,0,1); // discovery
// HM10_Launchtask(TASK_HM10_STATUS_EVENT,2,1); // status
}
void HM10_Read_LYWSD03(void) { //and MHO-C401
@ -441,7 +547,7 @@ void HM10_Read_MJ_HT_V1(void) {
* @param _type Type number of the sensor
* @return uint32_t Known or new slot in the sensors-vector
*/
uint32_t MIBLEgetSensorSlot(uint8_t (&_MAC)[6], uint16_t _type, uint32_t _rssi){
uint32_t MIBLEgetSensorSlot(uint8_t (&_MAC)[6], uint16_t _type, int _rssi){
DEBUG_SENSOR_LOG(PSTR("%s: will test ID-type: %x"),D_CMND_HM10, _type);
bool _success = false;
@ -461,6 +567,7 @@ uint32_t MIBLEgetSensorSlot(uint8_t (&_MAC)[6], uint16_t _type, uint32_t _rssi){
for(uint32_t i=0; i<MIBLEsensors.size(); i++){
if(memcmp(_MAC,MIBLEsensors[i].MAC,sizeof(_MAC))==0){
DEBUG_SENSOR_LOG(PSTR("%s: known sensor at slot: %u"),D_CMND_HM10, i);
MIBLEsensors[i].rssi=_rssi;
if(MIBLEsensors[i].showedUp < 4){ // if we got an intact packet, the sensor should show up several times
MIBLEsensors[i].showedUp++; // count up to the above number ... now we are pretty sure
}
@ -477,7 +584,7 @@ uint32_t MIBLEgetSensorSlot(uint8_t (&_MAC)[6], uint16_t _type, uint32_t _rssi){
_newSensor.feature.raw = 0;
_newSensor.temp =NAN;
_newSensor.bat=0x00;
_newSensor.rssi=_rssi * -1;
_newSensor.rssi=_rssi;
_newSensor.lux = 0x00ffffff;
switch (_type){
case FLORA:
@ -540,13 +647,15 @@ void HM10SerialInit(void) {
HM10.period = Settings.tele_period;
DEBUG_SENSOR_LOG(PSTR("%s_TASK_LIST initialized, now return to main loop"),D_CMND_HM10);
//test section for options - TODO: make a real interface for it
HM10.option.noRealTime = 1;
//test section for options
HM10.option.allwaysAggregate = 1;
HM10.option.showRSSI = 0;
HM10.option.ignoreBogusBattery = 1;
HM10.option.noSummary = 0;
HM10.option.minimalSummary = 0;
HM10.option.directBridgeMode = 0;
HM10.option.showRSSI = 1;
HM10.option.ignoreBogusBattery = 1; // from advertisements
HM10.option.holdBackFirstAutodiscovery = 1;
HM10.mode.autoScan = 1;
HM10.rxBuffer = new char[HM10_MAX_RX_BUF];
}
@ -568,24 +677,26 @@ void HM10parseMiBeacon(char * _buf, uint32_t _slot){
memcpy((void*)&_beacon,(void*)_buf, sizeof(_beacon));
}
HM10_ReverseMAC(_beacon.MAC);
if(memcmp(_beacon.MAC,MIBLEsensors[_slot].MAC,sizeof(_beacon.MAC))!=0){
if (MIBLEsensors[_slot].showedUp>3) return; // probably false alarm from a damaged packet
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: remove garbage sensor"),D_CMND_HM10);
DEBUG_SENSOR_LOG(PSTR("%s i: %x %x %x %x %x %x"),D_CMND_HM10, MIBLEsensors[_slot].MAC[5], MIBLEsensors[_slot].MAC[4],MIBLEsensors[_slot].MAC[3],MIBLEsensors[_slot].MAC[2],MIBLEsensors[_slot].MAC[1],MIBLEsensors[_slot].MAC[0]);
DEBUG_SENSOR_LOG(PSTR("%s n: %x %x %x %x %x %x"),D_CMND_HM10, _beacon.MAC[5], _beacon.MAC[4], _beacon.MAC[3],_beacon.MAC[2],_beacon.MAC[1],_beacon.MAC[0]);
MIBLEsensors.erase(MIBLEsensors.begin()+_slot);
return;
}
if (MIBLEsensors[_slot].showedUp<4) MIBLEsensors[_slot].showedUp++;
// if(memcmp(_beacon.MAC,MIBLEsensors[_slot].MAC,sizeof(_beacon.MAC))!=0){
// if (MIBLEsensors[_slot].showedUp>3) return; // probably false alarm from a damaged packet
// AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: remove garbage sensor"),D_CMND_HM10);
// DEBUG_SENSOR_LOG(PSTR("%s i: %x %x %x %x %x %x"),D_CMND_HM10, MIBLEsensors[_slot].MAC[5], MIBLEsensors[_slot].MAC[4],MIBLEsensors[_slot].MAC[3],MIBLEsensors[_slot].MAC[2],MIBLEsensors[_slot].MAC[1],MIBLEsensors[_slot].MAC[0]);
// DEBUG_SENSOR_LOG(PSTR("%s n: %x %x %x %x %x %x"),D_CMND_HM10, _beacon.MAC[5], _beacon.MAC[4], _beacon.MAC[3],_beacon.MAC[2],_beacon.MAC[1],_beacon.MAC[0]);
// MIBLEsensors.erase(MIBLEsensors.begin()+_slot);
// return;
// }
// if (MIBLEsensors[_slot].showedUp<4) MIBLEsensors[_slot].showedUp++;
DEBUG_SENSOR_LOG(PSTR("MiBeacon type:%02x: %02x %02x %02x %02x %02x %02x %02x %02x"),_beacon.type, (uint8_t)_buf[0],(uint8_t)_buf[1],(uint8_t)_buf[2],(uint8_t)_buf[3],(uint8_t)_buf[4],(uint8_t)_buf[5],(uint8_t)_buf[6],(uint8_t)_buf[7]);
DEBUG_SENSOR_LOG(PSTR(" type:%02x: %02x %02x %02x %02x %02x %02x %02x %02x"),_beacon.type, (uint8_t)_buf[8],(uint8_t)_buf[9],(uint8_t)_buf[10],(uint8_t)_buf[11],(uint8_t)_buf[12],(uint8_t)_buf[13],(uint8_t)_buf[14],(uint8_t)_buf[15]);
// MIBLEsensors[_slot].rssi = _rssi;
if(MIBLEsensors[_slot].type==4 || MIBLEsensors[_slot].type==6){
if(MIBLEsensors[_slot].type==LYWSD03MMC || MIBLEsensors[_slot].type==CGD1 || MIBLEsensors[_slot].type==MHOC401){
DEBUG_SENSOR_LOG(PSTR("LYWSD03 and CGD1 no support for MiBeacon, type %u"),MIBLEsensors[_slot].type);
return;
}
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s: %s mibeacon type: %x"),D_CMND_HM10, kHM10DeviceType[MIBLEsensors[_slot].type-1], _beacon.type);
DEBUG_SENSOR_LOG(PSTR("%s at slot %u"), kHM10DeviceType[MIBLEsensors[_slot].type-1],_slot);
switch(_beacon.type){
case 0x01:
@ -659,7 +770,7 @@ void HM10parseMiBeacon(char * _buf, uint32_t _slot){
}
if(MIBLEsensors[_slot].eventType.raw == 0) return;
MIBLEsensors[_slot].shallSendMQTT = 1;
HM10.mode.shallTriggerTele = 1;
if(HM10.option.directBridgeMode) HM10.mode.shallTriggerTele = 1;
}
void HM10parseATC(char * _buf, uint32_t _slot){
@ -669,75 +780,64 @@ void HM10parseATC(char * _buf, uint32_t _slot){
MIBLEsensors.at(_slot).hum = (float)_packet->hum;
MIBLEsensors.at(_slot).bat = _packet->batPer;
MIBLEsensors[_slot].shallSendMQTT = 1;
if(HM10.option.directBridgeMode) HM10.mode.shallTriggerTele = 1;
}
char* HM10ParseResponse(char *buf, uint16_t bufsize) {
if (!strncmp(buf,"HMSoft",6)) { //8
const char* _fw = "000";
memcpy((void *)_fw,(void *)(buf+8),3);
HM10.firmware = atoi(_fw);
DEBUG_SENSOR_LOG(PSTR("%s: Firmware: %d"),D_CMND_HM10, HM10.firmware);
return buf;
}
char * _pos = nullptr;
uint32_t _idx = 0;
char _subStr[] = "SA:";
while(_pos = (char*) memchr(buf+_idx, 'I', 60)){ //strstr() does miss too much
_idx=_pos-buf;
if(memcmp(&_pos+1,_subStr,3)){
break;
void HM10parseCGD1Packet(char * _buf, uint32_t _slot){ // no MiBeacon
cg_packet_t *_packet = (cg_packet_t*)_buf;
switch (_packet->mode){
case 0x0401:
float _tempFloat;
_tempFloat=(float)(_packet->temp)/10.0f;
if(_tempFloat<60){
MIBLEsensors.at(_slot).temp = _tempFloat;
MIBLEsensors[_slot].eventType.temp = 1;
DEBUG_SENSOR_LOG(PSTR("CGD1: temp updated"));
}
}
if(_pos) {
uint8_t _newMacArray[6] = {0};
memcpy((void *)_newMacArray,(void *)(_pos+4),6);
uint32_t _rssi = 255- (uint8_t)(_pos[11]);
HM10_ReverseMAC(_newMacArray);
DEBUG_SENSOR_LOG(PSTR("%s: MAC-array: %02x%02x%02x%02x%02x%02x"),D_CMND_HM10,_newMacArray[0],_newMacArray[1],_newMacArray[2],_newMacArray[3],_newMacArray[4],_newMacArray[5]);
uint16_t _type=0xffff;
_tempFloat=(float)(_packet->hum)/10.0f;
if(_tempFloat<100){
MIBLEsensors.at(_slot).hum = _tempFloat;
MIBLEsensors[_slot].eventType.hum = 1;
DEBUG_SENSOR_LOG(PSTR("CGD1: hum updated"));
}
DEBUG_SENSOR_LOG(PSTR("CGD1: U16: %x Temp U16: %x Hum"), _packet->temp, _packet->hum);
break;
case 0x0102:
if(_packet->bat<101){
MIBLEsensors.at(_slot).bat = _packet->bat;
MIBLEsensors[_slot].eventType.bat = 1;
DEBUG_SENSOR_LOG(PSTR("Mode a: bat updated"));
}
break;
default:
DEBUG_SENSOR_LOG(PSTR("HM10: unexpected CGD1-packet"));
}
if(MIBLEsensors[_slot].eventType.raw == 0) return;
MIBLEsensors[_slot].shallSendMQTT = 1;
if(HM10.option.directBridgeMode) HM10.mode.shallTriggerTele = 1;
}
for (_idx =10;_idx<32;_idx++){
if((uint8_t)_pos[_idx] == 0xfe){
if((uint8_t)_pos[_idx-2] == 0x16 && (uint8_t)_pos[_idx-1] == 0x95){
_pos = _pos+_idx+1;
_type = (uint8_t)_pos[3]*256 + (uint8_t)_pos[2];
DEBUG_SENSOR_LOG(PSTR("%s: type %04x _ %02x %02x"),D_CMND_HM10,_type, _pos[3],_pos[2]);
break;
}
}
else if ((uint8_t)_pos[_idx] == 0x1a){
if ((uint8_t)_pos[_idx+1] == 0x18){
if((uint8_t)_pos[_idx+4] == 0x95 && (uint8_t)_pos[_idx+3] == 0x16) continue; // LYWSD02 or MHO-C303
_type = 0xa1c; //ATC
_pos = _pos+_idx+2;
break;
}
}
}
uint16_t _slot = MIBLEgetSensorSlot(_newMacArray, _type, _rssi);
if(_slot!=0xff){
if (_type==0xa1c) HM10parseATC(_pos,_slot);
else HM10parseMiBeacon(_pos,_slot);
}
if(bufsize>64) return _pos+12;
else return nullptr;
void HM10ParseResponse(char *buf, uint16_t bufsize) {
if (!strncmp(buf,"HMSoft",6)) { //8
const char* _fw = "000";
memcpy((void *)_fw,(void *)(buf+8),3);
HM10.firmware = atoi(_fw);
DEBUG_SENSOR_LOG(PSTR("%s: Firmware: %d"),D_CMND_HM10, HM10.firmware);
}
else if (strstr(buf, "LOST")){
HM10.current_task_delay = 0;
HM10.mode.connected = false;
}
else if (strstr(buf, "CONNF")){
HM10.mode.connected = false;
HM10.current_task_delay = 0;
}
else if (strstr(buf, "CONN")){
HM10.current_task_delay = 0;
}
else {
DEBUG_SENSOR_LOG(PSTR("%s: empty response"),D_CMND_HM10);
return buf;
}
return _pos;
else if (strstr(buf, "LOST")){
HM10.current_task_delay = 0;
HM10.mode.connected = false;
}
else if (strstr(buf, "CONNF")){
HM10.mode.connected = false;
HM10.current_task_delay = 0;
}
else if (strstr(buf, "CONN")){
HM10.current_task_delay = 0;
}
else {
DEBUG_SENSOR_LOG(PSTR("%s: empty response"),D_CMND_HM10);
}
}
void HM10readHT_LY(char *_buf){
@ -768,7 +868,7 @@ void HM10readHT_LY(char *_buf){
MIBLEsensors[_slot].eventType.bat = 1;
}
MIBLEsensors[_slot].shallSendMQTT = 1;
HM10.mode.shallTriggerTele = 1;
if(HM10.option.directBridgeMode) HM10.mode.shallTriggerTele = 1;
}
}
@ -797,7 +897,7 @@ void HM10readHT_CGD1(char *_buf){
}
MIBLEsensors[_slot].eventType.tempHum = 1;
MIBLEsensors[_slot].shallSendMQTT = 1;
HM10.mode.shallTriggerTele = 1;
if(HM10.option.directBridgeMode) HM10.mode.shallTriggerTele = 1;
}
}
@ -827,7 +927,7 @@ void HM10readHT_MJ_HT_V1(char *_buf){
}
MIBLEsensors[_slot].eventType.tempHum = 1;
MIBLEsensors[_slot].shallSendMQTT = 1;
HM10.mode.shallTriggerTele = 1;
if(HM10.option.directBridgeMode) HM10.mode.shallTriggerTele = 1;
}
void HM10readTLMF(char *_buf){
@ -851,8 +951,7 @@ void HM10readTLMF(char *_buf){
MIBLEsensors[_slot].eventType.moist = 1;
MIBLEsensors[_slot].eventType.fert = 1;
MIBLEsensors[_slot].shallSendMQTT = 1;
HM10.mode.shallTriggerTele = 1;
if(HM10.option.directBridgeMode) HM10.mode.shallTriggerTele = 1;
HM10.mode.awaiting = none;
HM10.current_task_delay = 0;
}
@ -873,13 +972,90 @@ bool HM10readBat(char *_buf){
MIBLEsensors[_slot].showedUp=255; // this sensor is real
MIBLEsensors[_slot].eventType.bat = 1;
MIBLEsensors[_slot].shallSendMQTT = 1;
HM10.mode.shallTriggerTele = 1;
if(HM10.option.directBridgeMode) HM10.mode.shallTriggerTele = 1;
return true;
}
}
return false;
}
/*********************************************************************************************\
* beacon functions
\*********************************************************************************************/
/**
* @brief Handle a generic BLE advertisment in a running scan or to check a beacon
*
*
*/
void HM10HandleGenericBeacon(void){
if(HM10.state.beaconScanCounter==0){ //handle beacon
for(auto &_beacon : MIBLEbeacons){
if(memcmp(HM10.rxAdvertisement.MAC,_beacon.MAC,6)==0){
_beacon.time = 0;
_beacon.RSSI = HM10.rxAdvertisement.RSSI;
_beacon.SVC = HM10.rxAdvertisement.SVC;
_beacon.CID = HM10.rxAdvertisement.CID;
_beacon.UUID = HM10.rxAdvertisement.UUID;
_beacon.TX = HM10.rxAdvertisement.TX;
return;
}
}
return;
}
// else handle scan
if(MINBLEscanResult.size()>19) {
AddLog_P2(LOG_LEVEL_INFO,PSTR("HM10: Scan buffer full"));
HM10.state.beaconScanCounter = 1;
return;
}
for(auto _scanResult : MINBLEscanResult){
if(memcmp(HM10.rxAdvertisement.MAC,_scanResult.MAC,6)==0){
// AddLog_P2(LOG_LEVEL_INFO,PSTR("HM10: known device"));
return;
}
}
MINBLEscanResult.push_back(HM10.rxAdvertisement);
}
/**
* @brief Add a beacon defined by its MAC-address, if only zeros are given, the beacon will be deactivated
*
* @param index 1-4 beacons are currently supported
* @param data null terminated char array representing a MAC-address in hex
*/
void HM10addBeacon(uint8_t index, char* data){
auto &_new = MIBLEbeacons[index-1]; //TODO: check
HM10HexStringToBytes(data,_new.MAC);
char _MAC[18];
ToHex_P(MIBLEbeacons[index-1].MAC,6,_MAC,18,':');
char _empty[6] = {0};
_new.time = 0;
if(memcmp(_empty,_new.MAC,6) == 0){
_new.active = false;
AddLog_P2(LOG_LEVEL_INFO,PSTR("HM10: beacon%u deactivated"), index);
}
else{
_new.active = true;
HM10.mode.activeBeacon = 1;
AddLog_P2(LOG_LEVEL_INFO,PSTR("HM10: beacon added with MAC: %s"), _MAC);
}
}
/**
* @brief Present BLE scan in the console, after that deleting the scan data
*
*/
void HM10showScanResults(){
AddLog_P2(LOG_LEVEL_INFO,PSTR("HM10: found %u devices in scan:"), MINBLEscanResult.size());
for(auto _scanResult : MINBLEscanResult){
char _MAC[18];
ToHex_P(_scanResult.MAC,6,_MAC,18,':');
AddLog_P2(LOG_LEVEL_INFO,PSTR("MAC: %s _ CID: %04x _ SVC: %04x _ UUID: %04x _ TX: %02u _ RSSI: %d"), _MAC, _scanResult.CID, _scanResult.SVC, _scanResult.UUID, _scanResult.TX, _scanResult.RSSI);
}
MINBLEscanResult.clear();
}
/*********************************************************************************************\
* handle the return value from the HM10
\*********************************************************************************************/
@ -887,19 +1063,63 @@ bool HM10readBat(char *_buf){
bool HM10SerialHandleFeedback(){ // every 50 milliseconds
bool success = false;
uint32_t i = 0;
uint32_t _targetsize = 64; //set to some save value
bool _isPotentialAdv = false;
bool _isValidAdv = false;
char* _rx = HM10.rxBuffer; // we start always with the buffer
uint32_t _nextStep = 2; // we can expect length and AD type at the first two positions anyway
uint8_t length_type[64]; // length,AD-type,buffer - buffer should be too large
while(HM10Serial->available() && (_targetsize!=i)) {
*_rx= HM10Serial->read();
if(i==18){
if(memcmp(HM10.rxBuffer+4,"ISA:",4)==0){ //last 4 bytes of "OK+DISA:" should be safe enough
// AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s packet size: %u"),D_CMND_HM10,HM10.rxBuffer[16]);
_targetsize = HM10.rxBuffer[16] + 19; // this is the size byte according to HM-10 docs
if(_targetsize>64) _targetsize=64;
memcpy(HM10.rxAdvertisement.MAC,HM10.rxBuffer+8,6);
HM10_ReverseMAC(HM10.rxAdvertisement.MAC);
HM10.rxAdvertisement.RSSI = (256 - HM10.rxBuffer[15]) * -1;
length_type[0] = HM10.rxBuffer[17]; //length
length_type[1] = HM10.rxBuffer[18]; //AD-type
_isPotentialAdv = true;
}
}
while(HM10Serial->available()) {
if(i<HM10_MAX_RX_BUF){
HM10.rxBuffer[i] = HM10Serial->read();
if(_isPotentialAdv){ // we will change the pointer now according to the AD-type and data length
if(_nextStep>length_type[0]){
if(length_type[1]==0xff){ //we only want the CID from the custom data 0xff ...
memcpy((uint8_t*)&HM10.rxAdvertisement.CID,length_type+2,2); // ... and leave the rest unused in the buffer
}
_nextStep=0;
_rx = (char*)&length_type - 1;
}
else if(_nextStep == 2){
switch(length_type[1]){ //AD type
case 0x02: case 0x03:
_rx = (char*)&HM10.rxAdvertisement.UUID - 1;
break;
case 0x0a:
if(length_type[0] == 0xd) {
if(_targetsize-i == 1)_isValidAdv = true; // expected trailing bytes for a valid packet
}
_rx = (char*)&HM10.rxAdvertisement.TX - 1;
break;
case 0x16:
_rx = (char*)HM10.rxAdvertisement.svcData - 1;
break;
}
}
_nextStep++;
}
i++;
_rx++;
success = true;
}
if(i==0){
if(HM10.mode.shallTriggerTele){ // let us use the spare time for other things
HM10.mode.shallTriggerTele=0;
if(HM10.option.noRealTime){
if(HM10.option.directBridgeMode){
HM10.mode.triggeredTele=0;
return success;
}
@ -928,11 +1148,23 @@ bool HM10SerialHandleFeedback(){ // every 50 milliseconds
if (HM10.mode.connected) HM10readTLMF(HM10.rxBuffer);
break;
case discScan:
if(success) {
char *_src = HM10ParseResponse(HM10.rxBuffer,i);
if(_src){
HM10ParseResponse(_src,i-(_src-HM10.rxBuffer)); // try a second parse
}
if(_isValidAdv) {
if(HM10.state.beaconScanCounter!=0 || HM10.mode.activeBeacon){
HM10HandleGenericBeacon();
}
uint16_t _type = (uint8_t)HM10.rxAdvertisement.svcData[5]*256 + (uint8_t)HM10.rxAdvertisement.svcData[4];
// AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%04x %02x %04x %04x %04x"),HM10.rxAdvertisement.UUID,HM10.rxAdvertisement.TX,HM10.rxAdvertisement.CID,HM10.rxAdvertisement.SVC, _type);
if(HM10.rxAdvertisement.SVC==0x181a) _type = 0xa1c;
else if(HM10.rxAdvertisement.SVC==0xfdcd) _type = 0x0576;
uint16_t _slot = MIBLEgetSensorSlot(HM10.rxAdvertisement.MAC, _type, HM10.rxAdvertisement.RSSI);
if(_slot!=0xff){
if (_type==0xa1c) HM10parseATC((char*)HM10.rxAdvertisement.svcData+2,_slot);
else if (_type==0x0576) HM10parseCGD1Packet((char*)HM10.rxAdvertisement.svcData+2,_slot);
else HM10parseMiBeacon((char*)HM10.rxAdvertisement.svcData+2,_slot);
}
else{
// AddLogBuffer(LOG_LEVEL_INFO,(uint8_t*)HM10.rxAdvertisement.svcData,16);
}
}
break;
case tempHumMJ:
@ -949,6 +1181,9 @@ bool HM10SerialHandleFeedback(){ // every 50 milliseconds
break;
}
memset(HM10.rxBuffer,0,i); // wipe away the recent bytes
if(_isPotentialAdv){
memset((void*)&HM10.rxAdvertisement,0,sizeof(HM10.rxAdvertisement));
}
return success;
}
@ -1203,7 +1438,7 @@ void HM10StatusInfo() {
AddLog_P2(LOG_LEVEL_INFO, stemp);
RulesProcessEvent(stemp);
*/
Response_P(PSTR("{%s:{\"found\":%u}}"), D_CMND_HM10, MIBLEsensors.size());
Response_P(PSTR("{\"%s\":{\"found\":%u}}"), D_CMND_HM10, MIBLEsensors.size());
XdrvRulesProcess();
}
@ -1223,6 +1458,25 @@ void HM10EverySecond(bool restart){
return;
}
uint32_t _idx = 0;
uint32_t _activeBeacons = 0;
for (auto &_beacon : MIBLEbeacons){
_idx++;
if(_beacon.active == false) continue;
_activeBeacons++;
_beacon.time++;
Response_P(PSTR("{\"Beacon%u\":{\"Time\":%u}}"), _idx, _beacon.time);
XdrvRulesProcess();
}
if(_activeBeacons==0) HM10.mode.activeBeacon = 0;
if(HM10.state.beaconScanCounter!=0){
HM10.state.beaconScanCounter--;
if(HM10.state.beaconScanCounter==0){
HM10showScanResults();
}
}
if(HM10.firmware == 0) return;
if(HM10.mode.pending_task == 1) return;
if(MIBLEsensors.size()==0 && !HM10.mode.autoScan) return;
@ -1358,6 +1612,31 @@ bool HM10Cmd(void) {
HM10_Discovery_Scan();
Response_P(S_JSON_HM10_COMMAND, command, "");
break;
case CMND_HM10_BEACON:
if (XdrvMailbox.data_len == 0) {
switch(XdrvMailbox.index){
case 0:
HM10.state.beaconScanCounter = 8;
Response_P(S_JSON_HM10_COMMAND_SVALUE, command, XdrvMailbox.index,PSTR("scanning"));
break;
case 1: case 2: case 3: case 4:
char _MAC[18];
ToHex_P(MIBLEbeacons[XdrvMailbox.index-1].MAC,6,_MAC,18,':');
Response_P(S_JSON_HM10_COMMAND_SVALUE, command, XdrvMailbox.index,_MAC);
break;
}
}
else {
if(XdrvMailbox.data_len == 12 || XdrvMailbox.data_len == 17){ // MAC-string without or with colons
switch(XdrvMailbox.index){
case 1: case 2: case 3: case 4:
HM10addBeacon(XdrvMailbox.index,XdrvMailbox.data);
break;
}
}
Response_P(S_JSON_HM10_COMMAND_SVALUE, command, XdrvMailbox.index,XdrvMailbox.data);
}
break;
default:
// else for Unknown command
serviced = false;
@ -1388,7 +1667,7 @@ void HM10triggerTele(void){
* Presentation
\*********************************************************************************************/
const char HTTP_HM10[] PROGMEM = "{s}HM10 V%u{m}%u%s / %u{e}";
const char HTTP_HM10[] PROGMEM = "{s}HM10 FW%u V0950{m}%u%s / %u{e}";
const char HTTP_HM10_MAC[] PROGMEM = "{s}%s %s{m}%s{e}";
const char HTTP_BATTERY[] PROGMEM = "{s}%s" " Battery" "{m}%u%%{e}";
const char HTTP_RSSI[] PROGMEM = "{s}%s " D_RSSI "{m}%d dBm{e}";
@ -1398,6 +1677,20 @@ const char HTTP_HM10_HL[] PROGMEM = "{s}<hr>{m}<hr>{e}";
void HM10Show(bool json)
{
if (json) {
#ifdef USE_HOME_ASSISTANT
bool _noSummarySave = HM10.option.noSummary;
bool _minimalSummarySave = HM10.option.minimalSummary;
if(hass_mode==2){
if(HM10.option.holdBackFirstAutodiscovery){
if(!HM10.mode.firstAutodiscoveryDone){
HM10.mode.firstAutodiscoveryDone = 1;
return;
}
}
HM10.option.noSummary = false;
HM10.option.minimalSummary = false;
}
#endif //USE_HOME_ASSISTANT
if(!HM10.mode.triggeredTele){
if(HM10.option.noSummary) return; // no message at TELEPERIOD
}
@ -1416,7 +1709,11 @@ void HM10Show(bool json)
bool tempHumSended = false;
if(MIBLEsensors[i].feature.tempHum){
if(MIBLEsensors[i].eventType.tempHum || !HM10.mode.triggeredTele || HM10.option.allwaysAggregate){
if (!isnan(MIBLEsensors[i].hum) && !isnan(MIBLEsensors[i].temp)) {
if (!isnan(MIBLEsensors[i].hum) && !isnan(MIBLEsensors[i].temp)
#ifdef USE_HOME_ASSISTANT
||(hass_mode!=-1)
#endif //USE_HOME_ASSISTANT
) {
ResponseAppend_P(PSTR(","));
ResponseAppendTHD(MIBLEsensors[i].temp, MIBLEsensors[i].hum);
tempHumSended = true;
@ -1425,7 +1722,11 @@ void HM10Show(bool json)
}
if(MIBLEsensors[i].feature.temp && !tempHumSended){
if(MIBLEsensors[i].eventType.temp || !HM10.mode.triggeredTele || HM10.option.allwaysAggregate) {
if (!isnan(MIBLEsensors[i].temp)) {
if (!isnan(MIBLEsensors[i].temp)
#ifdef USE_HOME_ASSISTANT
||(hass_mode!=-1)
#endif //USE_HOME_ASSISTANT
) {
char temperature[FLOATSZ];
dtostrfd(MIBLEsensors[i].temp, Settings.flag2.temperature_resolution, temperature);
ResponseAppend_P(PSTR(",\"" D_JSON_TEMPERATURE "\":%s"), temperature);
@ -1434,7 +1735,11 @@ void HM10Show(bool json)
}
if(MIBLEsensors[i].feature.hum && !tempHumSended){
if(MIBLEsensors[i].eventType.hum || !HM10.mode.triggeredTele || HM10.option.allwaysAggregate) {
if (!isnan(MIBLEsensors[i].hum)) {
if (!isnan(MIBLEsensors[i].hum)
#ifdef USE_HOME_ASSISTANT
||(hass_mode!=-1)
#endif //USE_HOME_ASSISTANT
) {
char hum[FLOATSZ];
dtostrfd(MIBLEsensors[i].hum, Settings.flag2.humidity_resolution, hum);
ResponseAppend_P(PSTR(",\"" D_JSON_HUMIDITY "\":%s"), hum);
@ -1443,22 +1748,43 @@ void HM10Show(bool json)
}
if (MIBLEsensors[i].feature.lux){
if(MIBLEsensors[i].eventType.lux || !HM10.mode.triggeredTele || HM10.option.allwaysAggregate){
if (MIBLEsensors[i].lux!=0x0ffffff) { // this is the error code -> no lux
if (MIBLEsensors[i].lux!=0x0ffffff
#ifdef USE_HOME_ASSISTANT
||(hass_mode!=-1)
#endif //USE_HOME_ASSISTANT
) { // this is the error code -> no lux
ResponseAppend_P(PSTR(",\"" D_JSON_ILLUMINANCE "\":%u"), MIBLEsensors[i].lux);
#ifdef USE_HOME_ASSISTANT
if (MIBLEsensors[i].lux==0x0ffffff) HM10nullifyEndOfMQTT_DATA();
#endif //USE_HOME_ASSISTANT
}
}
}
if (MIBLEsensors[i].feature.moist){
if(MIBLEsensors[i].eventType.moist || !HM10.mode.triggeredTele || HM10.option.allwaysAggregate){
if (MIBLEsensors[i].moisture!=0xff) {
if (MIBLEsensors[i].moisture!=0xff
#ifdef USE_HOME_ASSISTANT
||(hass_mode!=-1)
#endif //USE_HOME_ASSISTANT
) {
ResponseAppend_P(PSTR(",\"" D_JSON_MOISTURE "\":%u"), MIBLEsensors[i].moisture);
#ifdef USE_HOME_ASSISTANT
if (MIBLEsensors[i].moisture==0xff) HM10nullifyEndOfMQTT_DATA();
#endif //USE_HOME_ASSISTANT
}
}
}
if (MIBLEsensors[i].feature.fert){
if(MIBLEsensors[i].eventType.fert || !HM10.mode.triggeredTele || HM10.option.allwaysAggregate){
if (MIBLEsensors[i].fertility!=0xffff) {
if (MIBLEsensors[i].fertility!=0xffff
#ifdef USE_HOME_ASSISTANT
||(hass_mode!=-1)
#endif //USE_HOME_ASSISTANT
) {
ResponseAppend_P(PSTR(",\"Fertility\":%u"), MIBLEsensors[i].fertility);
#ifdef USE_HOME_ASSISTANT
if (MIBLEsensors[i].fertility==0xffff) HM10nullifyEndOfMQTT_DATA();
#endif //USE_HOME_ASSISTANT
}
}
}
@ -1491,12 +1817,19 @@ void HM10Show(bool json)
}
if (MIBLEsensors[i].feature.bat){
if(MIBLEsensors[i].eventType.bat || !HM10.mode.triggeredTele || HM10.option.allwaysAggregate){
if (MIBLEsensors[i].bat != 0x00) { // this is the error code -> no battery
if (MIBLEsensors[i].bat != 0x00
#ifdef USE_HOME_ASSISTANT
||(hass_mode!=-1)
#endif //USE_HOME_ASSISTANT
) { // this is the error code -> no battery
ResponseAppend_P(PSTR(",\"Battery\":%u"), MIBLEsensors[i].bat);
#ifdef USE_HOME_ASSISTANT
if (MIBLEsensors[i].bat == 0x00) HM10nullifyEndOfMQTT_DATA();
#endif //USE_HOME_ASSISTANT
}
}
}
if (HM10.option.showRSSI && HM10.mode.triggeredTele) ResponseAppend_P(PSTR(",\"RSSI\":%d"), MIBLEsensors[i].rssi);
if (HM10.option.showRSSI) ResponseAppend_P(PSTR(",\"RSSI\":%d"), MIBLEsensors[i].rssi);
if(_positionCurlyBracket==strlen(TasmotaGlobal.mqtt_data)) ResponseAppend_P(PSTR(",")); // write some random char, to be overwritten in the next step
@ -1509,7 +1842,23 @@ void HM10Show(bool json)
}
}
HM10.mode.triggeredTele = 0;
// add beacons
uint32_t _idx = 0;
for (auto _beacon : MIBLEbeacons){
_idx++;
if(!_beacon.active) continue;
char _MAC[18];
ToHex_P(_beacon.MAC,6,_MAC,18,':');
ResponseAppend_P(PSTR(",\"Beacon%u\":{\"MAC\":\"%s\",\"CID\":\"0x%04x\",\"SVC\":\"0x%04x\","
"\"UUID\":\"0x%04x\",\"Time\":%u,\"RSSI\":%d,\"TX\":%u}"),
_idx,_MAC,_beacon.CID,_beacon.SVC,_beacon.UUID,_beacon.time,_beacon.RSSI,_beacon.TX);
}
#ifdef USE_HOME_ASSISTANT
if(hass_mode==2){
HM10.option.noSummary = _noSummarySave;
HM10.option.minimalSummary = _minimalSummarySave;
}
#endif //USE_HOME_ASSISTANT
#ifdef USE_WEBSERVER
} else {
static uint16_t _page = 0;
@ -1564,6 +1913,27 @@ void HM10Show(bool json)
}
if(MIBLEsensors.size()%HM10.perPage==0 && _page==MIBLEsensors.size()/HM10.perPage) _page=0;
if(_page>MIBLEsensors.size()/HM10.perPage) _page=0;
//always at the bottom of the page
uint32_t _idx=0;
if(HM10.mode.activeBeacon){
WSContentSend_PD(HTTP_HM10_HL);
char _sbeacon[] = "Beacon1";
for (auto &_beacon : MIBLEbeacons){
_idx++;
if(!_beacon.active) continue;
WSContentSend_PD(HTTP_HM10_HL);
_sbeacon[6] = _idx + 0x30;
char _MAC[18];
ToHex_P(_beacon.MAC,6,_MAC,18,':');
WSContentSend_PD(HTTP_HM10_MAC, _sbeacon, D_MAC_ADDRESS, _MAC);
WSContentSend_PD(HTTP_RSSI, _sbeacon, _beacon.RSSI);
if(_beacon.CID!=0) WSContentSend_PD(PSTR("{s}Beacon%u CID{m}0x%04X{e}"),_idx, _beacon.CID);
if(_beacon.SVC!=0) WSContentSend_PD(PSTR("{s}Beacon%u SVC{m}0x%04X{e}"),_idx, _beacon.SVC);
if(_beacon.UUID!=0) WSContentSend_PD(PSTR("{s}Beacon%u UUID{m}0x%04X{e}"),_idx, _beacon.UUID);
if(_beacon.TX!=0) WSContentSend_PD(PSTR("{s}Beacon%u TX{m}%u{e}"),_idx, _beacon.TX);
WSContentSend_PD(PSTR("{s}Beacon%u Time{m}%u seconds{e}"),_idx, _beacon.time);
}
}
#endif // USE_WEBSERVER
}
}

View File

@ -18,7 +18,7 @@
*/
#ifdef USE_I2C
#if defined(USE_EZOPH) || defined(USE_EZOORP) || defined(USE_EZORTD) || defined(USE_EZOHUM) || defined(USE_EZOEC) || defined(USE_EZOCO2) || defined(USE_EZOO2) || defined(USE_EZOPRS) || defined(USE_EZOFLO)
#if defined(USE_EZOPH) || defined(USE_EZOORP) || defined(USE_EZORTD) || defined(USE_EZOHUM) || defined(USE_EZOEC) || defined(USE_EZOCO2) || defined(USE_EZOO2) || defined(USE_EZOPRS) || defined(USE_EZOFLO) || defined(USE_EZODO)
#define USE_EZO
#endif
#if defined(USE_EZO)

60
tasmota/xsns_78_ezodo.ino Normal file
View File

@ -0,0 +1,60 @@
/*
xsns_78_ezodo.ino - EZO DO I2C DO sensor support for Tasmota
Copyright (C) 2020 Christopher Tremblay
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 <http://www.gnu.org/licenses/>.
*/
#ifdef USE_I2C
#ifdef USE_EZODO
#define EZO_DO_READ_LATENCY 600
struct EZODO : public EZOStruct {
EZODO(uint32_t addr) : EZOStruct(addr), DO(0) {}
virtual void ProcessMeasurement(void)
{
char data[D_EZO_MAX_BUF];
EZOStruct::ProcessMeasurement(data, sizeof(data), EZO_DO_READ_LATENCY);
DO = CharToFloat(data);
}
virtual void Show(bool json, const char *name)
{
char str[8];
dtostrfd(DO, 2, str);
if (json) {
ResponseAppend_P(PSTR(",\"%s\":{\"" D_JSON_DO "\":%d}" ), name, str);
}
#ifdef USE_WEBSERVER
else {
WSContentSend_PD(HTTP_SNS_DO, name, str);
#endif // USE_WEBSERVER
}
}
static const char id[] PROGMEM;
private:
float DO;
};
const char EZODO::id[] PROGMEM = "D.O.";
#endif // USE_EZODO
#endif // USE_I2C

View File

@ -51,7 +51,11 @@ enum {
// The order of the EZO devices must map with the enum declared above
const char *const EZOSupport[EZO_ADDR_n] PROGMEM = {
EZOStruct::id, // "DO"
#ifdef USE_EZODO
EZODO::id,
#else
EZOStruct::id,
#endif
#ifdef USE_EZOORP
EZOORP::id,
#else
@ -67,7 +71,7 @@ const char *const EZOSupport[EZO_ADDR_n] PROGMEM = {
#else
EZOStruct::id,
#endif
EZOStruct::id,
EZOStruct::id, // <unnamed>
#ifdef USE_EZORTD
EZORTD::id,
#else
@ -89,14 +93,14 @@ const char *const EZOSupport[EZO_ADDR_n] PROGMEM = {
#else
EZOStruct::id,
#endif
EZOStruct::id,
EZOStruct::id, // <unnamed>
#ifdef USE_EZOO2
EZOO2::id,
#else
EZOStruct::id,
#endif
EZOStruct::id,
EZOStruct::id,
EZOStruct::id, // <unnamed>
EZOStruct::id, // <unnamed>
#ifdef USE_EZOHUM
EZOHUM::id,
#else
@ -234,6 +238,9 @@ private:
// We use switch intead of virtual function to save RAM
switch (j + EZO_ADDR_0) {
#ifdef USE_EZODO
CREATE_EZO_CLASS(DO)
#endif
#ifdef USE_EZOORP
CREATE_EZO_CLASS(ORP)
#endif

View File

@ -235,7 +235,7 @@ a_features = [[
],[
"USE_EZOORP","USE_EZORTD","USE_EZOHUM","USE_EZOEC",
"USE_EZOCO2","USE_EZOO2","USE_EZOPRS","USE_EZOFLO",
"","","","",
"USE_EZODO","","","",
"","","","",
"","","","",
"","","","",
@ -268,7 +268,7 @@ else:
obj = json.load(fp)
def StartDecode():
print ("\n*** decode-status.py v20201101 by Theo Arends and Jacek Ziolkowski ***")
print ("\n*** decode-status.py v20201102 by Theo Arends and Jacek Ziolkowski ***")
# print("Decoding\n{}".format(obj))