From b4bc001a2adcdf74fcb7b66d23e9d2a7dcd31e44 Mon Sep 17 00:00:00 2001
From: Xavier MULLER <33861984+localhost61@users.noreply.github.com>
Date: Wed, 2 Sep 2020 01:03:56 +0200
Subject: [PATCH 01/38] Update fr_FR.h
v8.4.0.3
2nd attempt, I made 2 typos, sorry :-/
---
tasmota/language/fr_FR.h | 134 ++++++++++++++++++---------------------
1 file changed, 61 insertions(+), 73 deletions(-)
diff --git a/tasmota/language/fr_FR.h b/tasmota/language/fr_FR.h
index 434dd07b8..28310cc52 100644
--- a/tasmota/language/fr_FR.h
+++ b/tasmota/language/fr_FR.h
@@ -1,18 +1,14 @@
/*
fr-FR.h - localization for French - France for Tasmota
-
Copyright (C) 2020 Olivier Francais
-
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 .
*/
@@ -28,7 +24,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.1.0.1
+ * Updated until v8.4.0.3
\*********************************************************************/
#define LANGUAGE_MODULE_NAME // Enable to display "Module Generic" (ie Spanish), Disable to display "Generic Module" (ie English)
@@ -84,7 +80,7 @@
#define D_DISABLED "Désactivé"
#define D_DISTANCE "Distance"
#define D_DNS_SERVER "Serveur DNS"
-#define D_DONE "Fait"
+#define D_DONE "Terminé"
#define D_DST_TIME "DST"
#define D_ECO2 "eCO₂"
#define D_EMULATION "Émulation"
@@ -99,8 +95,8 @@
#define D_FILE "Fichier"
#define D_FLOW_RATE "Débit"
#define D_FREE_MEMORY "Mémoire libre"
-#define D_PSR_MAX_MEMORY "PS-RAM Memory"
-#define D_PSR_FREE_MEMORY "PS-RAM free Memory"
+#define D_PSR_MAX_MEMORY "Mémoire PS-RAM"
+#define D_PSR_FREE_MEMORY "Mémoire PS-RAM libre"
#define D_FREQUENCY "Fréquence"
#define D_GAS "Gaz"
#define D_GATEWAY "Passerelle"
@@ -304,7 +300,7 @@
#define D_OTHER_PARAMETERS "Autres paramètres"
#define D_TEMPLATE "Modèle"
#define D_ACTIVATE "Activer"
-#define D_DEVICE_NAME "Device Name"
+#define D_DEVICE_NAME "Nom de l'appareil"
#define D_WEB_ADMIN_PASSWORD "Mot de passe Web Admin"
#define D_MQTT_ENABLE "MQTT activé"
#define D_MQTT_TLS_ENABLE "MQTT TLS"
@@ -351,8 +347,8 @@
#define D_UPLOAD_STARTED "Téléchargement lancé"
#define D_UPGRADE_STARTED "Mise à jour lancée"
#define D_UPLOAD_DONE "Téléchargement terminé"
-#define D_UPLOAD_TRANSFER "Upload transfer"
-#define D_TRANSFER_STARTED "Transfer started"
+#define D_UPLOAD_TRANSFER "Transfert ZigBee"
+#define D_TRANSFER_STARTED "Transfert lancé"
#define D_UPLOAD_ERR_1 "Aucun fichier sélectionné"
#define D_UPLOAD_ERR_2 "Espace insuffisant"
#define D_UPLOAD_ERR_3 "L'octet magique n'est pas 0xE9"
@@ -486,7 +482,7 @@
// xsns_07_sht1x.ino
#define D_SENSOR_DID_NOT_ACK_COMMAND "Le capteur n'a pas acquitté la commande"
-#define D_SHT1X_FOUND "SHT1X found"
+#define D_SHT1X_FOUND "SHT1X trouvé"
// xsns_18_pms5003.ino
#define D_STANDARD_CONCENTRATION "CF-1 PM" // Standard Particle CF-1 Particle Matter
@@ -533,24 +529,24 @@
#define D_TX20_WEST "O"
// xsns_53_sml.ino
-#define D_TPWRIN "Energy Total-In"
-#define D_TPWROUT "Energy Total-Out"
-#define D_TPWRCURR "Active Power-In/Out"
-#define D_TPWRCURR1 "Active Power-In p1"
-#define D_TPWRCURR2 "Active Power-In p2"
-#define D_TPWRCURR3 "Active Power-In p3"
-#define D_Strom_L1 "Current L1"
-#define D_Strom_L2 "Current L2"
-#define D_Strom_L3 "Current L3"
-#define D_Spannung_L1 "Voltage L1"
-#define D_Spannung_L2 "Voltage L2"
-#define D_Spannung_L3 "Voltage L3"
-#define D_METERNR "Meter_number"
+#define D_TPWRIN "Energie totale Entrée"
+#define D_TPWROUT "Energie totale Sortie"
+#define D_TPWRCURR "Puissance active E/S"
+#define D_TPWRCURR1 "Puissance active Ent Ph1"
+#define D_TPWRCURR2 "Puissance active Ent Ph2"
+#define D_TPWRCURR3 "Puissance active Ent Ph3"
+#define D_Strom_L1 "Courant Ph1"
+#define D_Strom_L2 "Courant Ph2"
+#define D_Strom_L3 "Courant Ph3"
+#define D_Spannung_L1 "Tension Ph1"
+#define D_Spannung_L2 "Tension Ph2"
+#define D_Spannung_L3 "Tension Ph3"
+#define D_METERNR "Numéro compteur"
#define D_METERSID "Service ID"
-#define D_GasIN "Compteur"
-#define D_H2oIN "Compteur"
-#define D_StL1L2L3 "Current L1+L2+L3"
-#define D_SpL1L2L3 "Voltage L1+L2+L3/3"
+#define D_GasIN "Compteur Gaz"
+#define D_H2oIN "Compteur Eau"
+#define D_StL1L2L3 "Courant Ph1+Ph2+Ph3"
+#define D_SpL1L2L3 "Tension (Ph1+Ph2+Ph3)/3"
// tasmota_template.h - keep them as short as possible to be able to fit them in GUI drop down box
#define D_SENSOR_NONE "Aucun"
@@ -689,17 +685,17 @@
#define D_SENSOR_DYP_RX "DYP Rx"
#define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx"
#define D_SENSOR_AS3935 "AS3935"
-#define D_SENSOR_WINDMETER_SPEED "WindMeter Spd"
+#define D_SENSOR_WINDMETER_SPEED "Anémomètre"
#define D_SENSOR_TELEINFO_RX "TInfo Rx"
-#define D_SENSOR_TELEINFO_ENABLE "TInfo EN"
-#define D_SENSOR_LMT01_PULSE "LMT01 Pulse"
-#define D_SENSOR_ADC_INPUT "ADC Input"
+#define D_SENSOR_TELEINFO_ENABLE "TInfo En"
+#define D_SENSOR_LMT01_PULSE "LMT01 Impulsion"
+#define D_SENSOR_ADC_INPUT "ADC Entrée"
#define D_SENSOR_ADC_TEMP "ADC Temp"
-#define D_SENSOR_ADC_LIGHT "ADC Light"
-#define D_SENSOR_ADC_BUTTON "ADC Button"
-#define D_SENSOR_ADC_RANGE "ADC Range"
+#define D_SENSOR_ADC_LIGHT "ADC Lumière"
+#define D_SENSOR_ADC_BUTTON "ADC Bouton"
+#define D_SENSOR_ADC_RANGE "ADC Distance"
#define D_SENSOR_ADC_CT_POWER "ADC CT Power"
-#define D_SENSOR_ADC_JOYSTICK "ADC Joystick"
+#define D_SENSOR_ADC_JOYSTICK "ADC Manette"
#define D_GPIO_WEBCAM_PWDN "CAM_PWDN"
#define D_GPIO_WEBCAM_RESET "CAM_RESET"
#define D_GPIO_WEBCAM_XCLK "CAM_XCLK"
@@ -719,7 +715,6 @@
#define D_SENSOR_TCP_RXD "TCP Rx"
#define D_SENSOR_IEM3000_TX "iEM3000 TX"
#define D_SENSOR_IEM3000_RX "iEM3000 RX"
-
// Units
#define D_UNIT_AMPERE "A"
#define D_UNIT_CELSIUS "C"
@@ -759,7 +754,6 @@
#define D_UNIT_WATT "W"
#define D_UNIT_WATTHOUR "Wh"
#define D_UNIT_WATT_METER_QUADRAT "W/m²"
-
//SDM220, SDM120, LE01MR
#define D_PHASE_ANGLE "Angle de phase"
#define D_IMPORT_ACTIVE "Énergie act conso"
@@ -770,7 +764,6 @@
#define D_UNIT_KWARH "kVArh"
#define D_UNIT_ANGLE "°"
#define D_TOTAL_ACTIVE "Total Active"
-
//SOLAXX1
#define D_PV1_VOLTAGE "Tension PV1"
#define D_PV1_CURRENT "Courant PV1"
@@ -794,47 +787,43 @@
#define D_SOLAX_ERROR_6 "Défaut Surchauffe"
#define D_SOLAX_ERROR_7 "Défaut Ventilateur"
#define D_SOLAX_ERROR_8 "Défaut Autre équipement"
-
//xdrv_10_scripter.ino
-#define D_CONFIGURE_SCRIPT "Edit script"
-#define D_SCRIPT "edit script"
-#define D_SDCARD_UPLOAD "file upload"
-#define D_SDCARD_DIR "sd card directory"
-#define D_UPL_DONE "Done"
-#define D_SCRIPT_CHARS_LEFT "chars left"
-#define D_SCRIPT_CHARS_NO_MORE "no more chars"
+#define D_CONFIGURE_SCRIPT "Éditer le script"
+#define D_SCRIPT "édition du script"
+#define D_SDCARD_UPLOAD "Envoi du fichier"
+#define D_SDCARD_DIR "Dossier carte SD"
+#define D_UPL_DONE "Terminé"
+#define D_SCRIPT_CHARS_LEFT "car. restant"
+#define D_SCRIPT_CHARS_NO_MORE "plus de car."
#define D_SCRIPT_DOWNLOAD "Download"
-#define D_SCRIPT_ENABLE "script enable"
-#define D_SCRIPT_UPLOAD "Upload"
-#define D_SCRIPT_UPLOAD_FILES "Upload files"
-
+#define D_SCRIPT_ENABLE "script actif"
+#define D_SCRIPT_UPLOAD "Envoi"
+#define D_SCRIPT_UPLOAD_FILES "Envoi de fichiers"
//xsns_67_as3935.ino
#define D_AS3935_GAIN "gain:"
-#define D_AS3935_ENERGY "energy:"
+#define D_AS3935_ENERGY "energie:"
#define D_AS3935_DISTANCE "distance:"
-#define D_AS3935_DISTURBER "disturber:"
+#define D_AS3935_DISTURBER "interférence:"
#define D_AS3935_VRMS "µVrms:"
-#define D_AS3935_APRX "aprx.:"
-#define D_AS3935_AWAY "away"
-#define D_AS3935_LIGHT "lightning"
-#define D_AS3935_OUT "lightning out of range"
-#define D_AS3935_NOT "distance not determined"
-#define D_AS3935_ABOVE "lightning overhead"
-#define D_AS3935_NOISE "noise detected"
-#define D_AS3935_DISTDET "disturber detected"
-#define D_AS3935_INTNOEV "Interrupt with no Event!"
-#define D_AS3935_NOMESS "listening..."
-#define D_AS3935_ON "On"
-#define D_AS3935_OFF "Off"
-#define D_AS3935_INDOORS "Indoors"
-#define D_AS3935_OUTDOORS "Outdoors"
-#define D_AS3935_CAL_FAIL "calibration failed"
-#define D_AS3935_CAL_OK "calibration set to:"
-
+#define D_AS3935_APRX "approx.:"
+#define D_AS3935_AWAY "de distance"
+#define D_AS3935_LIGHT "éclair"
+#define D_AS3935_OUT "éclair éloigné"
+#define D_AS3935_NOT "distance indéterminée"
+#define D_AS3935_ABOVE "éclair trop intense"
+#define D_AS3935_NOISE "bruit détecté"
+#define D_AS3935_DISTDET "interférence détectée"
+#define D_AS3935_INTNOEV "Interruption sans évenement!"
+#define D_AS3935_NOMESS "en écoute..."
+#define D_AS3935_ON "Actif"
+#define D_AS3935_OFF "Inactif"
+#define D_AS3935_INDOORS "Intérieur"
+#define D_AS3935_OUTDOORS "Extérieur"
+#define D_AS3935_CAL_FAIL "défaut de calibration"
+#define D_AS3935_CAL_OK "calibration établie à :"
//xsns_68_opentherm.ino
#define D_SENSOR_BOILER_OT_RX "OpenTherm RX"
#define D_SENSOR_BOILER_OT_TX "OpenTherm TX"
-
// xnrg_15_teleinfo Denky (Teleinfo)
#define D_CONTRACT "Type contrat"
#define D_POWER_LOAD "Charge actuelle"
@@ -843,5 +832,4 @@
#define D_OVERLOAD "ADPS"
#define D_MAX_POWER "Puissance max"
#define D_MAX_CURRENT "Courant max"
-
#endif // _LANGUAGE_FR_FR_H_
From 79c52ce1ef0e3c5e15a85fedbbcacc8c0dcd0691 Mon Sep 17 00:00:00 2001
From: Theo Arends <11044339+arendst@users.noreply.github.com>
Date: Wed, 2 Sep 2020 11:45:17 +0200
Subject: [PATCH 02/38] Refactor zigbee transfer
---
tasmota/xdrv_23_zigbee_9a_upload.ino | 17 +++++++----------
1 file changed, 7 insertions(+), 10 deletions(-)
diff --git a/tasmota/xdrv_23_zigbee_9a_upload.ino b/tasmota/xdrv_23_zigbee_9a_upload.ino
index 05b20fdab..fc7defb15 100644
--- a/tasmota/xdrv_23_zigbee_9a_upload.ino
+++ b/tasmota/xdrv_23_zigbee_9a_upload.ino
@@ -491,28 +491,25 @@ const char HTTP_SCRIPT_XFER_STATE[] PROGMEM =
"if(x.readyState==4&&x.status==200){"
"var s=x.responseText;"
"if(s!=7){" // ZBU_UPLOAD
- "location.href='/u3';"
+ "location.href='/u3';" // Load page HandleUploadDone()
"}"
"}"
"};"
- "x.open('GET','" WEB_HANDLE_ZIGBEE_XFER "?m=1',true);" // ?m related to Webserver->hasArg("m")
+ "x.open('GET','" WEB_HANDLE_ZIGBEE_XFER "?z=1',true);" // ?z related to Webserver->hasArg("z")
"x.send();"
- "if(pc==1){"
- "lt=setTimeout(z9,950);" // Poll every 0.95 second
- "}"
+ "lt=setTimeout(z9,950);" // Poll every 0.95 second
"}"
- "pc=1;"
- "wl(z9);";
+ "wl(z9);"; // Execute z9() on page load
void HandleZigbeeXfer(void) {
if (!HttpCheckPriviledgedAccess()) { return; }
- if (Webserver->hasArg("m")) { // Status refresh requested
+ if (Webserver->hasArg("z")) { // Status refresh requested
WSContentBegin(200, CT_PLAIN);
WSContentSend_P(PSTR("%d"), ZbUpload.state);
WSContentEnd();
if (ZBU_ERROR == ZbUpload.state) {
- Web.upload_error = 7; // Upload aborted (failed)
+ Web.upload_error = 7; // Upload aborted (xmodem transfer failed)
}
return;
}
@@ -522,7 +519,7 @@ void HandleZigbeeXfer(void) {
WSContentStart_P(S_INFORMATION);
WSContentSend_P(HTTP_SCRIPT_XFER_STATE);
WSContentSendStyle();
- WSContentSend_P(PSTR("
" D_UPLOAD_TRANSFER "...
"));
+ WSContentSend_P(PSTR("" D_UPLOAD_TRANSFER " ...
"));
WSContentSpaceButton(BUTTON_MAIN);
WSContentStop();
}
From a5bb8b640c2f0dd8e216ca6816d7ac9509f52168 Mon Sep 17 00:00:00 2001
From: bovirus <1262554+bovirus@users.noreply.github.com>
Date: Wed, 2 Sep 2020 21:53:39 +0200
Subject: [PATCH 03/38] Update Italian language
---
tasmota/language/it_IT.h | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/tasmota/language/it_IT.h b/tasmota/language/it_IT.h
index 34f5a1696..747ec1560 100644
--- a/tasmota/language/it_IT.h
+++ b/tasmota/language/it_IT.h
@@ -1,7 +1,7 @@
/*
it-IT.h - localization for Italian - Italy for Tasmota
- Copyright (C) 2020 Gennaro Tortone - some mods by Antonio Fragola - Updated by bovirus - rev. 19.08.2020
+ Copyright (C) 2020 Gennaro Tortone - some mods by Antonio Fragola - Updated by bovirus - rev. 02.09.2020
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
@@ -351,8 +351,8 @@
#define D_UPLOAD_STARTED "Caricamento..."
#define D_UPGRADE_STARTED "Aggiornamento..."
#define D_UPLOAD_DONE "Caricamento completato"
-#define D_UPLOAD_TRANSFER "Upload transfer"
-#define D_TRANSFER_STARTED "Transfer started"
+#define D_UPLOAD_TRANSFER "Trasferimento caricamento"
+#define D_TRANSFER_STARTED "Trasferimento avviato"
#define D_UPLOAD_ERR_1 "Nessun file selezionato"
#define D_UPLOAD_ERR_2 "Spazio insufficiente"
#define D_UPLOAD_ERR_3 "Magic byte non corrispondente a 0xE9"
From 994081430c3fa7bd4346b60b233259b24d771567 Mon Sep 17 00:00:00 2001
From: device111 <48546979+device111@users.noreply.github.com>
Date: Thu, 3 Sep 2020 09:37:10 +0200
Subject: [PATCH 04/38] AS3935 Refactor
- Fix Autodisturber
- Fix Event IRQ Flicker when calibrating and on Start up
- Add command `power` on/off
- Add command `noirqevent` (suppress IRQ with no event)
- Add Info Log when no Pin defined
- Add Info Log when SCO and TCO Calibration Fails
- optimize Autotune Caps
- new init procedure (Reset before Start)
- save 128 Bytes of Ram
- update language files
---
tasmota/language/bg_BG.h | 2 +
tasmota/language/cs_CZ.h | 2 +
tasmota/language/de_DE.h | 4 +-
tasmota/language/el_GR.h | 2 +
tasmota/language/en_GB.h | 2 +
tasmota/language/es_ES.h | 2 +
tasmota/language/fr_FR.h | 2 +
tasmota/language/he_HE.h | 2 +
tasmota/language/hu_HU.h | 2 +
tasmota/language/it_IT.h | 2 +
tasmota/language/ko_KO.h | 2 +
tasmota/language/nl_NL.h | 2 +
tasmota/language/pl_PL.h | 2 +
tasmota/language/pt_BR.h | 2 +
tasmota/language/pt_PT.h | 2 +
tasmota/language/ro_RO.h | 2 +
tasmota/language/ru_RU.h | 2 +
tasmota/language/sk_SK.h | 2 +
tasmota/language/sv_SE.h | 2 +
tasmota/language/tr_TR.h | 2 +
tasmota/language/uk_UA.h | 2 +
tasmota/language/zh_CN.h | 2 +
tasmota/language/zh_TW.h | 2 +
tasmota/my_user_config.h | 2 +-
tasmota/settings.h | 4 +-
tasmota/tasmota_template.h | 4 +-
tasmota/tasmota_template_ESP32.h | 4 +-
tasmota/xsns_67_as3935.ino | 674 ++++++++++++++++---------------
28 files changed, 413 insertions(+), 323 deletions(-)
diff --git a/tasmota/language/bg_BG.h b/tasmota/language/bg_BG.h
index ec637b220..e0543019c 100644
--- a/tasmota/language/bg_BG.h
+++ b/tasmota/language/bg_BG.h
@@ -823,6 +823,8 @@
#define D_AS3935_NOISE "открит шум"
#define D_AS3935_DISTDET "открито смущение"
#define D_AS3935_INTNOEV "Прекъсване без Събитие!"
+#define D_AS3935_FLICKER "IRQ flicker!"
+#define D_AS3935_POWEROFF "Power Off"
#define D_AS3935_NOMESS "слушане..."
#define D_AS3935_ON "Вкл."
#define D_AS3935_OFF "Изкл."
diff --git a/tasmota/language/cs_CZ.h b/tasmota/language/cs_CZ.h
index 1d591b610..1200ceab5 100644
--- a/tasmota/language/cs_CZ.h
+++ b/tasmota/language/cs_CZ.h
@@ -823,6 +823,8 @@
#define D_AS3935_NOISE "noise detected"
#define D_AS3935_DISTDET "disturber detected"
#define D_AS3935_INTNOEV "Interrupt with no Event!"
+#define D_AS3935_FLICKER "IRQ flicker!"
+#define D_AS3935_POWEROFF "Power Off"
#define D_AS3935_NOMESS "listening..."
#define D_AS3935_ON "On"
#define D_AS3935_OFF "Off"
diff --git a/tasmota/language/de_DE.h b/tasmota/language/de_DE.h
index 4798c625e..abef862c9 100644
--- a/tasmota/language/de_DE.h
+++ b/tasmota/language/de_DE.h
@@ -812,7 +812,7 @@
#define D_AS3935_GAIN "Rauschpegel:"
#define D_AS3935_ENERGY "Energie:"
#define D_AS3935_DISTANCE "Entfernung:"
-#define D_AS3935_DISTURBER "Störsingal:"
+#define D_AS3935_DISTURBER "Enstörer:"
#define D_AS3935_VRMS "µVrms:"
#define D_AS3935_APRX "ca.:"
#define D_AS3935_AWAY "entfernt"
@@ -823,6 +823,8 @@
#define D_AS3935_NOISE "Rauschen entdeckt"
#define D_AS3935_DISTDET "Störer entdeckt"
#define D_AS3935_INTNOEV "Interrupt ohne Grund!"
+#define D_AS3935_FLICKER "IRQ Pin flackerd!"
+#define D_AS3935_POWEROFF "Ausgeschaltet"
#define D_AS3935_NOMESS "lausche..."
#define D_AS3935_ON "On"
#define D_AS3935_OFF "Off"
diff --git a/tasmota/language/el_GR.h b/tasmota/language/el_GR.h
index 8e3653957..74a7b625f 100644
--- a/tasmota/language/el_GR.h
+++ b/tasmota/language/el_GR.h
@@ -823,6 +823,8 @@
#define D_AS3935_NOISE "noise detected"
#define D_AS3935_DISTDET "disturber detected"
#define D_AS3935_INTNOEV "Interrupt with no Event!"
+#define D_AS3935_FLICKER "IRQ flicker!"
+#define D_AS3935_POWEROFF "Power Off"
#define D_AS3935_NOMESS "listening..."
#define D_AS3935_ON "On"
#define D_AS3935_OFF "Off"
diff --git a/tasmota/language/en_GB.h b/tasmota/language/en_GB.h
index c077dcb81..18d655ce3 100644
--- a/tasmota/language/en_GB.h
+++ b/tasmota/language/en_GB.h
@@ -823,6 +823,8 @@
#define D_AS3935_NOISE "noise detected"
#define D_AS3935_DISTDET "disturber detected"
#define D_AS3935_INTNOEV "Interrupt with no Event!"
+#define D_AS3935_FLICKER "IRQ Pin flicker!"
+#define D_AS3935_POWEROFF "Powerd Off"
#define D_AS3935_NOMESS "listening..."
#define D_AS3935_ON "On"
#define D_AS3935_OFF "Off"
diff --git a/tasmota/language/es_ES.h b/tasmota/language/es_ES.h
index 10c0b1ac4..674b38eab 100644
--- a/tasmota/language/es_ES.h
+++ b/tasmota/language/es_ES.h
@@ -823,6 +823,8 @@
#define D_AS3935_NOISE "Ruido detectado"
#define D_AS3935_DISTDET "Perturbancia detectada"
#define D_AS3935_INTNOEV "Interrupción sin evento!"
+#define D_AS3935_FLICKER "IRQ flicker!"
+#define D_AS3935_POWEROFF "Power Off"
#define D_AS3935_NOMESS "Escuchando..."
#define D_AS3935_ON "Encendido"
#define D_AS3935_OFF "Apagado"
diff --git a/tasmota/language/fr_FR.h b/tasmota/language/fr_FR.h
index 28310cc52..be1fd4f93 100644
--- a/tasmota/language/fr_FR.h
+++ b/tasmota/language/fr_FR.h
@@ -814,6 +814,8 @@
#define D_AS3935_NOISE "bruit détecté"
#define D_AS3935_DISTDET "interférence détectée"
#define D_AS3935_INTNOEV "Interruption sans évenement!"
+#define D_AS3935_FLICKER "IRQ flicker!"
+#define D_AS3935_POWEROFF "Power Off"
#define D_AS3935_NOMESS "en écoute..."
#define D_AS3935_ON "Actif"
#define D_AS3935_OFF "Inactif"
diff --git a/tasmota/language/he_HE.h b/tasmota/language/he_HE.h
index dd0f2fbee..0de84e609 100644
--- a/tasmota/language/he_HE.h
+++ b/tasmota/language/he_HE.h
@@ -823,6 +823,8 @@
#define D_AS3935_NOISE "noise detected"
#define D_AS3935_DISTDET "disturber detected"
#define D_AS3935_INTNOEV "Interrupt with no Event!"
+#define D_AS3935_FLICKER "IRQ flicker!"
+#define D_AS3935_POWEROFF "Power Off"
#define D_AS3935_NOMESS "listening..."
#define D_AS3935_ON "On"
#define D_AS3935_OFF "Off"
diff --git a/tasmota/language/hu_HU.h b/tasmota/language/hu_HU.h
index 79d7c40aa..23a7be121 100644
--- a/tasmota/language/hu_HU.h
+++ b/tasmota/language/hu_HU.h
@@ -823,6 +823,8 @@
#define D_AS3935_NOISE "noise detected"
#define D_AS3935_DISTDET "disturber detected"
#define D_AS3935_INTNOEV "Interrupt with no Event!"
+#define D_AS3935_FLICKER "IRQ flicker!"
+#define D_AS3935_POWEROFF "Power Off"
#define D_AS3935_NOMESS "listening..."
#define D_AS3935_ON "On"
#define D_AS3935_OFF "Off"
diff --git a/tasmota/language/it_IT.h b/tasmota/language/it_IT.h
index 34f5a1696..60792b77f 100644
--- a/tasmota/language/it_IT.h
+++ b/tasmota/language/it_IT.h
@@ -823,6 +823,8 @@
#define D_AS3935_NOISE "rilevato rumore"
#define D_AS3935_DISTDET "rilevato disturbatore"
#define D_AS3935_INTNOEV "Interrupt senza evento!"
+#define D_AS3935_FLICKER "IRQ flicker!"
+#define D_AS3935_POWEROFF "Power Off"
#define D_AS3935_NOMESS "in ascolto..."
#define D_AS3935_ON "ON"
#define D_AS3935_OFF "OFF"
diff --git a/tasmota/language/ko_KO.h b/tasmota/language/ko_KO.h
index ef1f781b2..d08aea216 100644
--- a/tasmota/language/ko_KO.h
+++ b/tasmota/language/ko_KO.h
@@ -823,6 +823,8 @@
#define D_AS3935_NOISE "noise detected"
#define D_AS3935_DISTDET "disturber detected"
#define D_AS3935_INTNOEV "Interrupt with no Event!"
+#define D_AS3935_FLICKER "IRQ flicker!"
+#define D_AS3935_POWEROFF "Power Off"
#define D_AS3935_NOMESS "listening..."
#define D_AS3935_ON "On"
#define D_AS3935_OFF "Off"
diff --git a/tasmota/language/nl_NL.h b/tasmota/language/nl_NL.h
index a3a5821fd..6a40607c9 100644
--- a/tasmota/language/nl_NL.h
+++ b/tasmota/language/nl_NL.h
@@ -823,6 +823,8 @@
#define D_AS3935_NOISE "noise detected"
#define D_AS3935_DISTDET "disturber detected"
#define D_AS3935_INTNOEV "Interrupt with no Event!"
+#define D_AS3935_FLICKER "IRQ flicker!"
+#define D_AS3935_POWEROFF "Power Off"
#define D_AS3935_NOMESS "listening..."
#define D_AS3935_ON "On"
#define D_AS3935_OFF "Off"
diff --git a/tasmota/language/pl_PL.h b/tasmota/language/pl_PL.h
index 929774738..7e9d25e5c 100644
--- a/tasmota/language/pl_PL.h
+++ b/tasmota/language/pl_PL.h
@@ -823,6 +823,8 @@
#define D_AS3935_NOISE "noise detected"
#define D_AS3935_DISTDET "disturber detected"
#define D_AS3935_INTNOEV "Interrupt with no Event!"
+#define D_AS3935_FLICKER "IRQ flicker!"
+#define D_AS3935_POWEROFF "Power Off"
#define D_AS3935_NOMESS "listening..."
#define D_AS3935_ON "On"
#define D_AS3935_OFF "Off"
diff --git a/tasmota/language/pt_BR.h b/tasmota/language/pt_BR.h
index 990b308cb..88f1d0c86 100644
--- a/tasmota/language/pt_BR.h
+++ b/tasmota/language/pt_BR.h
@@ -823,6 +823,8 @@
#define D_AS3935_NOISE "noise detected"
#define D_AS3935_DISTDET "disturber detected"
#define D_AS3935_INTNOEV "Interrupt with no Event!"
+#define D_AS3935_FLICKER "IRQ flicker!"
+#define D_AS3935_POWEROFF "Power Off"
#define D_AS3935_NOMESS "listening..."
#define D_AS3935_ON "On"
#define D_AS3935_OFF "Off"
diff --git a/tasmota/language/pt_PT.h b/tasmota/language/pt_PT.h
index a976811c3..83bbbc778 100644
--- a/tasmota/language/pt_PT.h
+++ b/tasmota/language/pt_PT.h
@@ -823,6 +823,8 @@
#define D_AS3935_NOISE "noise detected"
#define D_AS3935_DISTDET "disturber detected"
#define D_AS3935_INTNOEV "Interrupt with no Event!"
+#define D_AS3935_FLICKER "IRQ flicker!"
+#define D_AS3935_POWEROFF "Power Off"
#define D_AS3935_NOMESS "listening..."
#define D_AS3935_ON "On"
#define D_AS3935_OFF "Off"
diff --git a/tasmota/language/ro_RO.h b/tasmota/language/ro_RO.h
index c4cba2ee3..00ab45bcf 100644
--- a/tasmota/language/ro_RO.h
+++ b/tasmota/language/ro_RO.h
@@ -823,6 +823,8 @@
#define D_AS3935_NOISE "noise detected"
#define D_AS3935_DISTDET "disturber detected"
#define D_AS3935_INTNOEV "Interrupt with no Event!"
+#define D_AS3935_FLICKER "IRQ flicker!"
+#define D_AS3935_POWEROFF "Power Off"
#define D_AS3935_NOMESS "listening..."
#define D_AS3935_ON "On"
#define D_AS3935_OFF "Off"
diff --git a/tasmota/language/ru_RU.h b/tasmota/language/ru_RU.h
index f9e576f70..13068198f 100644
--- a/tasmota/language/ru_RU.h
+++ b/tasmota/language/ru_RU.h
@@ -823,6 +823,8 @@
#define D_AS3935_NOISE "noise detected"
#define D_AS3935_DISTDET "disturber detected"
#define D_AS3935_INTNOEV "Interrupt with no Event!"
+#define D_AS3935_FLICKER "IRQ flicker!"
+#define D_AS3935_POWEROFF "Power Off"
#define D_AS3935_NOMESS "listening..."
#define D_AS3935_ON "On"
#define D_AS3935_OFF "Off"
diff --git a/tasmota/language/sk_SK.h b/tasmota/language/sk_SK.h
index 072caa53d..911581ee7 100644
--- a/tasmota/language/sk_SK.h
+++ b/tasmota/language/sk_SK.h
@@ -823,6 +823,8 @@
#define D_AS3935_NOISE "noise detected"
#define D_AS3935_DISTDET "disturber detected"
#define D_AS3935_INTNOEV "Interrupt with no Event!"
+#define D_AS3935_FLICKER "IRQ flicker!"
+#define D_AS3935_POWEROFF "Power Off"
#define D_AS3935_NOMESS "listening..."
#define D_AS3935_ON "On"
#define D_AS3935_OFF "Off"
diff --git a/tasmota/language/sv_SE.h b/tasmota/language/sv_SE.h
index 1ba0b0b7e..73ecd4bab 100644
--- a/tasmota/language/sv_SE.h
+++ b/tasmota/language/sv_SE.h
@@ -823,6 +823,8 @@
#define D_AS3935_NOISE "noise detected"
#define D_AS3935_DISTDET "disturber detected"
#define D_AS3935_INTNOEV "Interrupt with no Event!"
+#define D_AS3935_FLICKER "IRQ flicker!"
+#define D_AS3935_POWEROFF "Power Off"
#define D_AS3935_NOMESS "listening..."
#define D_AS3935_ON "On"
#define D_AS3935_OFF "Off"
diff --git a/tasmota/language/tr_TR.h b/tasmota/language/tr_TR.h
index 928ad30b6..e2b496d1e 100644
--- a/tasmota/language/tr_TR.h
+++ b/tasmota/language/tr_TR.h
@@ -823,6 +823,8 @@
#define D_AS3935_NOISE "noise detected"
#define D_AS3935_DISTDET "disturber detected"
#define D_AS3935_INTNOEV "Interrupt with no Event!"
+#define D_AS3935_FLICKER "IRQ flicker!"
+#define D_AS3935_POWEROFF "Power Off"
#define D_AS3935_NOMESS "listening..."
#define D_AS3935_ON "On"
#define D_AS3935_OFF "Off"
diff --git a/tasmota/language/uk_UA.h b/tasmota/language/uk_UA.h
index 8ce7ee877..388aecc9a 100644
--- a/tasmota/language/uk_UA.h
+++ b/tasmota/language/uk_UA.h
@@ -823,6 +823,8 @@
#define D_AS3935_NOISE "noise detected"
#define D_AS3935_DISTDET "disturber detected"
#define D_AS3935_INTNOEV "Interrupt with no Event!"
+#define D_AS3935_FLICKER "IRQ flicker!"
+#define D_AS3935_POWEROFF "Power Off"
#define D_AS3935_NOMESS "listening..."
#define D_AS3935_ON "On"
#define D_AS3935_OFF "Off"
diff --git a/tasmota/language/zh_CN.h b/tasmota/language/zh_CN.h
index b1b58b4c4..220f866e9 100644
--- a/tasmota/language/zh_CN.h
+++ b/tasmota/language/zh_CN.h
@@ -823,6 +823,8 @@
#define D_AS3935_NOISE "noise detected"
#define D_AS3935_DISTDET "disturber detected"
#define D_AS3935_INTNOEV "Interrupt with no Event!"
+#define D_AS3935_FLICKER "IRQ flicker!"
+#define D_AS3935_POWEROFF "Power Off"
#define D_AS3935_NOMESS "listening..."
#define D_AS3935_ON "On"
#define D_AS3935_OFF "Off"
diff --git a/tasmota/language/zh_TW.h b/tasmota/language/zh_TW.h
index fc9606a90..aca69ad6b 100644
--- a/tasmota/language/zh_TW.h
+++ b/tasmota/language/zh_TW.h
@@ -823,6 +823,8 @@
#define D_AS3935_NOISE "偵測到雜訊"
#define D_AS3935_DISTDET "偵測到干擾物"
#define D_AS3935_INTNOEV "沒有任何事件觸發中斷!"
+#define D_AS3935_FLICKER "IRQ flicker!"
+#define D_AS3935_POWEROFF "Power Off"
#define D_AS3935_NOMESS "聽取中..."
#define D_AS3935_ON "開啟"
#define D_AS3935_OFF "關閉"
diff --git a/tasmota/my_user_config.h b/tasmota/my_user_config.h
index 5f94c5fc0..067490f14 100644
--- a/tasmota/my_user_config.h
+++ b/tasmota/my_user_config.h
@@ -552,7 +552,7 @@
// #define WEMOS_MOTOR_V1_FREQ 1000 // Default frequency
// #define USE_HDC1080 // [I2cDriver45] Enable HDC1080 temperature/humidity sensor (I2C address 0x40) (+1k5 code)
// #define USE_IAQ // [I2cDriver46] Enable iAQ-core air quality sensor (I2C address 0x5a) (+0k6 code)
-// #define USE_AS3935 // [I2cDriver48] Enable AS3935 Franklin Lightning Sensor (I2C address 0x03) (+5k4 code)
+ #define USE_AS3935 // [I2cDriver48] Enable AS3935 Franklin Lightning Sensor (I2C address 0x03) (+5k4 code)
// #define USE_VEML6075 // [I2cDriver49] Enable VEML6075 UVA/UVB/UVINDEX Sensor (I2C address 0x10) (+2k1 code)
// #define USE_VEML7700 // [I2cDriver50] Enable VEML7700 Ambient Light sensor (I2C addresses 0x10) (+4k5 code)
// #define USE_MCP9808 // [I2cDriver51] Enable MCP9808 temperature sensor (I2C addresses 0x18 - 0x1F) (+0k9 code)
diff --git a/tasmota/settings.h b/tasmota/settings.h
index d7a526d41..60c837364 100644
--- a/tasmota/settings.h
+++ b/tasmota/settings.h
@@ -282,9 +282,9 @@ typedef union {
struct {
uint8_t nf_autotune : 1; // Autotune the NF Noise Level
uint8_t dist_autotune : 1; // Autotune Disturber on/off
- uint8_t nf_autotune_both : 1; // Autotune over both Areas: INDOORS/OUDOORS
+ uint8_t nf_autotune_both : 1; // Autotune over both Areas: INDOORS/OUDOORS
uint8_t mqtt_only_Light_Event : 1; // mqtt only if lightning Irq
- uint8_t spare4 : 1;
+ uint8_t suppress_irq_no_Event : 1; // suppress mqtt "IRQ with no Event". (Chip Bug)
uint8_t spare5 : 1;
uint8_t spare6 : 1;
uint8_t spare7 : 1;
diff --git a/tasmota/tasmota_template.h b/tasmota/tasmota_template.h
index f6d28e63d..32a914f8f 100644
--- a/tasmota/tasmota_template.h
+++ b/tasmota/tasmota_template.h
@@ -228,7 +228,7 @@ enum UserSelectablePins {
GPIO_CC1101_GDO2, // CC1101 pin for RX
GPIO_HRXL_RX, // Data from MaxBotix HRXL sonar range sensor
GPIO_ELECTRIQ_MOODL_TX, // ElectriQ iQ-wifiMOODL Serial TX
- GPIO_AS3935,
+ GPIO_AS3935, // Franklin Lightning Sensor
GPIO_PMS5003_TX, // Plantower PMS5003 Serial interface
GPIO_BOILER_OT_RX, // OpenTherm Boiler RX pin
GPIO_BOILER_OT_TX, // OpenTherm Boiler TX pin
@@ -710,7 +710,7 @@ const uint8_t kGpioNiceList[] PROGMEM = {
GPIO_DYP_RX,
#endif
#ifdef USE_AS3935
- GPIO_AS3935,
+ GPIO_AS3935, // AS3935 IRQ Pin
#endif
#ifdef USE_TELEINFO
GPIO_TELEINFO_RX,
diff --git a/tasmota/tasmota_template_ESP32.h b/tasmota/tasmota_template_ESP32.h
index 75e8e5a70..f0f59b488 100644
--- a/tasmota/tasmota_template_ESP32.h
+++ b/tasmota/tasmota_template_ESP32.h
@@ -111,7 +111,7 @@ enum UserSelectablePins {
GPIO_CC1101_GDO0, GPIO_CC1101_GDO2, // CC1101 Serial interface
GPIO_HRXL_RX, // Data from MaxBotix HRXL sonar range sensor
GPIO_ELECTRIQ_MOODL_TX, // ElectriQ iQ-wifiMOODL Serial TX
- GPIO_AS3935,
+ GPIO_AS3935, // Franklin Lightning Sensor
GPIO_ADC_INPUT, // Analog input
GPIO_ADC_TEMP, // Analog Thermistor
GPIO_ADC_LIGHT, // Analog Light sensor
@@ -557,7 +557,7 @@ const uint16_t kGpioNiceList[] PROGMEM = {
AGPIO(GPIO_DYP_RX),
#endif
#ifdef USE_AS3935
- AGPIO(GPIO_AS3935),
+ AGPIO(GPIO_AS3935), // AS3935 IRQ Pin
#endif
#ifdef USE_TELEINFO
AGPIO(GPIO_TELEINFO_RX),
diff --git a/tasmota/xsns_67_as3935.ino b/tasmota/xsns_67_as3935.ino
index 3723079b4..540b59ce0 100644
--- a/tasmota/xsns_67_as3935.ino
+++ b/tasmota/xsns_67_as3935.ino
@@ -31,15 +31,8 @@
#define D_NAME_AS3935 "AS3935"
#define AS3935_ADDR 0x03
-// Reg mask shift
-#define IRQ_TBL 0x03, 0x0F, 0
-#define ENERGY_RAW_1 0x04, 0xFF, 0
-#define ENERGY_RAW_2 0x05, 0xFF, 0
-#define ENERGY_RAW_3 0x06, 0x1F, 0
-#define LGHT_DIST 0x07, 0x3F, 0
-#define DISP_TRCO 0x08, 0x20, 5
-#define DISP_LCO 0x08, 0x80, 7
-#define TUNE_CAPS 0x08, 0x0F, 0
+// I2C Registers Reg mask shift
+#define PWR_REG 0x00, 0x01, 0
#define AFE_GB 0x00, 0x3E, 0
#define WDTH 0x01, 0x0F, 0
#define NF_LEVEL 0x01, 0x70, 4
@@ -47,12 +40,32 @@
#define MIN_NUM_LIGH 0x02, 0x30, 4
#define DISTURBER 0x03, 0x20, 5
#define LCO_FDIV 0x03, 0xC0, 6
+#define IRQ_TBL 0x03, 0x0F, 0
+#define ENERGY_RAW_1 0x04, 0xFF, 0
+#define ENERGY_RAW_2 0x05, 0xFF, 0
+#define ENERGY_RAW_3 0x06, 0x1F, 0
+#define LGHT_DIST 0x07, 0x3F, 0
+#define DISP_TRCO 0x08, 0x20, 5 // should 31.250 kHz with devide 16
+#define DISP_SRCO 0x08, 0x40, 6 // 1,1 MHz
+#define DISP_LCO 0x08, 0x80, 7 // 32.768 kHz
+#define TUNE_CAPS 0x08, 0x0F, 0
+#define CAL_TRCO_NOK 0x3A, 0x40, 6 // 1 = NOK
+#define CAL_TRCO_DONE 0x3A, 0x80, 7 // 0 = OK
+#define CAL_SRCO_NOK 0x3B, 0x40, 6
+#define CAL_SRCO_DONE 0x3B, 0x80, 7
+// I2C Commands
+#define RESET_DEFAULT 0x3C, 0x96
+#define CALIBATE_RCO 0x3D, 0x96
+
+// NF-Level
#define INDOORS 0x24
#define OUTDOORS 0x1C
-// Global
-const char HTTP_SNS_UNIT_KILOMETER[] PROGMEM = D_UNIT_KILOMETER;
+// Load Settings Mask
+#define SETREG00MASK 0x3E // For Power On
+#define SETREG03MASK 0xF0 // For LCO and Disturber
+
// Http
const char HTTP_SNS_AS3935_ENERGY[] PROGMEM = "{s}" D_NAME_AS3935 " " D_AS3935_ENERGY " {m}%d{e}";
const char HTTP_SNS_AS3935_DISTANZ[] PROGMEM = "{s}" D_NAME_AS3935 " " D_AS3935_DISTANCE " {m}%u " D_UNIT_KILOMETER "{e}";
@@ -66,15 +79,18 @@ const char HTTP_SNS_AS3935_DIST_ON[] PROGMEM = "{s}%s " D_AS3935_DISTURBER " {m}
const char HTTP_SNS_AS3935_DIST_OFF[] PROGMEM = "{s}%s " D_AS3935_DISTURBER " {m}" D_AS3935_OFF " {e}";
const char* const HTTP_SNS_AS3935_DISTURBER[] PROGMEM = {HTTP_SNS_AS3935_DIST_OFF, HTTP_SNS_AS3935_DIST_ON};
// http Messages
-const char HTTP_SNS_AS3935_EMPTY[] PROGMEM = "{s}%s: " D_AS3935_NOMESS "{e}";
-const char HTTP_SNS_AS3935_OUT[] PROGMEM = "{s}%s: " D_AS3935_OUT "{e}";
-const char HTTP_SNS_AS3935_NOT[] PROGMEM = "{s}%s: " D_AS3935_NOT "{e}";
-const char HTTP_SNS_AS3935_ABOVE[] PROGMEM = "{s}%s: " D_AS3935_ABOVE "{e}";
-const char HTTP_SNS_AS3935_NOISE[] PROGMEM = "{s}%s: " D_AS3935_NOISE "{e}";
-const char HTTP_SNS_AS3935_DISTURB[] PROGMEM = "{s}%s: " D_AS3935_DISTDET "{e}";
-const char HTTP_SNS_AS3935_INTNOEV[] PROGMEM = "{s}%s: " D_AS3935_INTNOEV "{e}";
-const char HTTP_SNS_AS3935_MSG[] PROGMEM = "{s}%s: " D_AS3935_LIGHT " " D_AS3935_APRX " %d " D_UNIT_KILOMETER " " D_AS3935_AWAY "{e}";
-const char* const HTTP_SNS_AS3935_TABLE_1[] PROGMEM = { HTTP_SNS_AS3935_EMPTY, HTTP_SNS_AS3935_MSG, HTTP_SNS_AS3935_OUT, HTTP_SNS_AS3935_NOT, HTTP_SNS_AS3935_ABOVE, HTTP_SNS_AS3935_NOISE, HTTP_SNS_AS3935_DISTURB, HTTP_SNS_AS3935_INTNOEV };
+const char HTTP_SNS_AS3935_EMPTY[] PROGMEM = "{s}%s " D_AS3935_NOMESS "{e}";
+const char HTTP_SNS_AS3935_OUT[] PROGMEM = "{s}%s " D_AS3935_OUT "{e}";
+const char HTTP_SNS_AS3935_NOT[] PROGMEM = "{s}%s " D_AS3935_NOT "{e}";
+const char HTTP_SNS_AS3935_ABOVE[] PROGMEM = "{s}%s " D_AS3935_ABOVE "{e}";
+const char HTTP_SNS_AS3935_NOISE[] PROGMEM = "{s}%s " D_AS3935_NOISE "{e}";
+const char HTTP_SNS_AS3935_DISTURB[] PROGMEM = "{s}%s " D_AS3935_DISTDET "{e}";
+const char HTTP_SNS_AS3935_INTNOEV[] PROGMEM = "{s}%s " D_AS3935_INTNOEV "{e}";
+const char HTTP_SNS_AS3935_FLICKER[] PROGMEM = "{s}%s " D_AS3935_FLICKER "{e}";
+const char HTTP_SNS_AS3935_POWEROFF[] PROGMEM = "{s}%s " D_AS3935_POWEROFF "{e}";
+
+const char HTTP_SNS_AS3935_MSG[] PROGMEM = "{s}%s " D_AS3935_LIGHT " " D_AS3935_APRX " %d " D_UNIT_KILOMETER " " D_AS3935_AWAY "{e}";
+const char* const HTTP_SNS_AS3935_TABLE_1[] PROGMEM = { HTTP_SNS_AS3935_EMPTY, HTTP_SNS_AS3935_MSG, HTTP_SNS_AS3935_OUT, HTTP_SNS_AS3935_NOT, HTTP_SNS_AS3935_ABOVE, HTTP_SNS_AS3935_NOISE, HTTP_SNS_AS3935_DISTURB, HTTP_SNS_AS3935_INTNOEV, HTTP_SNS_AS3935_FLICKER, HTTP_SNS_AS3935_POWEROFF };
// Json
const char JSON_SNS_AS3935_EVENTS[] PROGMEM = ",\"%s\":{\"" D_JSON_EVENT "\":%d,\"" D_JSON_DISTANCE "\":%d,\"" D_JSON_ENERGY "\":%u,\"" D_JSON_STAGE "\":%d}";
// Json Command
@@ -84,13 +100,17 @@ const char* const S_JSON_AS3935_COMMAND_CAL[] PROGMEM = {"" D_AS3935_CAL_FAIL ""
const char S_JSON_AS3935_COMMAND_STRING[] PROGMEM = "{\"" D_NAME_AS3935 "\":{\"%s\":%s}}";
const char S_JSON_AS3935_COMMAND_NVALUE[] PROGMEM = "{\"" D_NAME_AS3935 "\":{\"%s\":%d}}";
-const char S_JSON_AS3935_COMMAND_SETTINGS[] PROGMEM = "{\"" D_NAME_AS3935 "\":{\"Gain\":%s,\"NFfloor\":%d,\"uVrms\":%d,\"Tunecaps\":%d,\"MinNumLight\":%d,\"Rejektion\":%d,\"Wdthreshold\":%d,\"MinNFstage\":%d,\"NFAutoTime\":%d,\"DisturberAutoTime\":%d,\"Disturber\":%s,\"NFauto\":%s,\"Disturberauto\":%s,\"NFautomax\":%s,\"Mqttlightevent\":%s}}";
+const char S_JSON_AS3935_COMMAND_SETTINGS[] PROGMEM = "{\"AS3935_Settings\":{\"Gain\":%s,\"NFfloor\":%d,\"uVrms\":%d,\"Tunecaps\":%d,\"MinNumLight\":%d,\"Rejektion\":%d,\"Wdthreshold\":%d,\"MinNFstage\":%d,\"NFAutoTime\":%d,\"DisturberAutoTime\":%d,\"Disturber\":%s,\"NFauto\":%s,\"Disturberauto\":%s,\"NFautomax\":%s,\"Mqttlightevent\":%s,\"Mqttnoirqevent\":%s}}";
-const char kAS3935_Commands[] PROGMEM = "setnf|setminstage|setml|default|setgain|settunecaps|setrej|setwdth|disttime|nftime|disturber|autonf|autodisturber|autonfmax|mqttevent|settings|calibrate";
+const char kAS3935_Commands[] PROGMEM = "power|setnf|setminstage|setml|default|setgain|settunecaps|setrej|setwdth|disttime|nftime|disturber|autonf|autodisturber|autonfmax|lightevent|noirqevent|settings|calibrate";
+
+const uint8_t AS3935_VrmsIndoor[] PROGMEM = { 28, 45, 62, 78, 95, 112, 130, 146 };
+const uint16_t AS3935_VrmsOutdoor[] PROGMEM = { 390, 630, 860, 1100, 1140, 1570, 1800, 2000 };
enum AS3935_Commands { // commands for Console
+ CMND_AS3935_POWER, // Power on/off the device (1 Bit)
CMND_AS3935_SET_NF, // Noise Floor Level, value from 0-7 (3 Bit)
- CMND_AS3935_SET_MINNF, // Set Min Noise Floor Level when Autotune is active Value von 0-15
+ CMND_AS3935_SET_MINNF, // Set Min Noise Floor Level when Autotune is active Value from 0-15
CMND_AS3935_SET_MINLIGHT, // Minimum number of lightning 0=1/1=5/2=9/3=16 Lightnings
CMND_AS3935_SET_DEF, // set default for Sensor and Settings
CMND_AS3935_SET_GAIN, // Set Inddoor/Outdoor
@@ -104,24 +124,25 @@ enum AS3935_Commands { // commands for Console
CMND_AS3935_DIST_AUTOTUNE, // Autotune Disturber on/off
CMND_AS3935_NF_ATUNE_BOTH, // Autotune over both Areas: INDOORS/OUDOORS
CMND_AS3935_MQTT_LIGHT_EVT, // mqtt only if lightning Irq
+ CMND_AS3935_MQTT_NO_IRQ_EVT, // suppress mqtt "IRQ with no Event"
CMND_AS3935_SETTINGS, // Json output of all settings
CMND_AS3935_CALIBRATE // caps autocalibrate
- };
+};
-struct AS3935STRUCT
-{
- bool autodist_activ = false;
+struct {
+ bool active = false;
+ bool http_count_start = false;
+ bool poweroff = false;
volatile bool detected = false;
- volatile bool dispLCO = 0;
- uint8_t icount = 0;
+ volatile bool dispLCO = false;
+ volatile uint8_t icount = 0;
uint8_t irq = 0;
- uint8_t mqtt_irq = 0;
- uint8_t http_irq = 0;
- uint8_t http_count_start = 0;
+ uint8_t mqtt_event = 0;
+ uint8_t http_event = 0;
+ uint8_t http_time = 0;
+ uint8_t http_count = 0;
int16_t http_distance = 0;
int16_t distance = 0;
- uint16_t http_timer = 0;
- uint16_t http_count = 0;
uint16_t nftimer = 0;
uint16_t disttimer = 0;
uint32_t intensity = 0;
@@ -129,16 +150,16 @@ struct AS3935STRUCT
volatile uint32_t pulse = 0;
} as3935_sensor;
-uint8_t as3935_active = 0;
-
-void ICACHE_RAM_ATTR AS3935Isr() {
+void ICACHE_RAM_ATTR AS3935Isr(void) {
as3935_sensor.detected = true;
+ as3935_sensor.icount++;
}
+// we have to store 5 Bytes in the eeprom. Register 8 is mapped to Byte 4
uint8_t AS3935ReadRegister(uint8_t reg, uint8_t mask, uint8_t shift) {
uint8_t data = I2cRead8(AS3935_ADDR, reg);
if (reg == 0x08) Settings.as3935_sensor_cfg[4] = data;
- if (reg < 0x04) Settings.as3935_sensor_cfg[reg] = data;
+ if (reg <= 0x03) Settings.as3935_sensor_cfg[reg] = data;
return ((data & mask) >> shift);
}
@@ -150,12 +171,12 @@ void AS3935WriteRegister(uint8_t reg, uint8_t mask, uint8_t shift, uint8_t data)
data |= currentReg;
I2cWrite8(AS3935_ADDR, reg, data);
if (reg == 0x08) Settings.as3935_sensor_cfg[4] = I2cRead8(AS3935_ADDR, reg);
- if (reg < 0x04) Settings.as3935_sensor_cfg[reg] = I2cRead8(AS3935_ADDR, reg);
+ if (reg <= 0x03) Settings.as3935_sensor_cfg[reg] = I2cRead8(AS3935_ADDR, reg);
}
/********************************************************************************************/
// Autotune Caps
-void ICACHE_RAM_ATTR AS3935CountFreq() {
+void ICACHE_RAM_ATTR AS3935CountFreq(void) {
if (as3935_sensor.dispLCO)
as3935_sensor.pulse++;
}
@@ -163,52 +184,161 @@ void ICACHE_RAM_ATTR AS3935CountFreq() {
bool AS3935AutoTuneCaps(uint8_t irqpin) {
int32_t maxtune = 17500; // there max 3.5 % tol
uint8_t besttune;
+ uint8_t oldvalue = AS3935GetTuneCaps();
AS3935WriteRegister(LCO_FDIV, 0); // Fdiv 16
delay(2);
for (uint8_t tune = 0; tune < 16; tune++) {
AS3935WriteRegister(TUNE_CAPS, tune);
delay(2);
- AS3935WriteRegister(DISP_LCO,1);
+ AS3935WriteRegister(DISP_LCO, 1);
delay(1);
as3935_sensor.dispLCO = true;
as3935_sensor.pulse = 0;
attachInterrupt(digitalPinToInterrupt(irqpin), AS3935CountFreq, RISING);
- delay(200); // 100ms callback not work accurat for fequ. measure
+ delay(50);
as3935_sensor.dispLCO = false;
detachInterrupt(irqpin);
- AS3935WriteRegister(DISP_LCO,0);
- int32_t currentfreq = 500000 - ((as3935_sensor.pulse * 5) * 16);
+ AS3935WriteRegister(DISP_LCO, 0);
+ int32_t currentfreq = 500000 - ((as3935_sensor.pulse * 20) * 16);
if(currentfreq < 0) currentfreq = -currentfreq;
if(maxtune > currentfreq) {
maxtune = currentfreq;
besttune = tune;
}
}
- if (maxtune >= 17500) // max. 3.5%
+ if (maxtune >= 17500) { // max. 3.5%
+ AS3935SetTuneCaps(oldvalue);
return false;
+ }
AS3935SetTuneCaps(besttune);
return true;
}
/********************************************************************************************/
// functions
-void AS3935CalibrateRCO() {
- I2cWrite8(AS3935_ADDR, 0x3D, 0x96);
- AS3935WriteRegister(DISP_TRCO, 1);
+bool AS3935CalRCOResult(void) {
+ if(AS3935ReadRegister(CAL_SRCO_NOK) || AS3935ReadRegister(CAL_TRCO_NOK)) {
+ AddLog_P2(LOG_LEVEL_INFO, PSTR("I2C: AS3935 Fatal Failure of TRCO or SRCO calibration"));
+ return false;
+ }
+ return true;
+}
+
+bool AS3935CalibrateRCO(void) {
+ detachInterrupt(Pin(GPIO_AS3935)); // Prevent AS3935Isr from RCO Calibration
+ I2cWrite8(AS3935_ADDR, CALIBATE_RCO); // Cal TRCO & SRCO
+ AS3935WriteRegister(DISP_TRCO, 1); // need for Power up
delay(2);
AS3935WriteRegister(DISP_TRCO, 0);
+ if(!AS3935CalRCOResult())
+ return false;
+ attachInterrupt(digitalPinToInterrupt(Pin(GPIO_AS3935)), AS3935Isr, RISING);
+ return true;
+}
+
+void AS3935Reset(void) {
+ I2cWrite8(AS3935_ADDR, RESET_DEFAULT);
+ delay(2);
+}
+
+void AS3935PwrDown(void) {
+ AS3935WriteRegister(PWR_REG ,1);
+ detachInterrupt(Pin(GPIO_AS3935));
+ as3935_sensor.poweroff = true;
+ as3935_sensor.mqtt_event = 9;
+ as3935_sensor.http_event = 9;
+ as3935_sensor.intensity = 0;
+ as3935_sensor.distance = 0;
+}
+
+void AS3935PwrUp(void) {
+ AS3935WriteRegister(PWR_REG ,0);
+ AS3935CalibrateRCO();
+ as3935_sensor.poweroff = false;
+ as3935_sensor.mqtt_event = 0;
+ as3935_sensor.http_event = 0;
+}
+
+uint8_t AS3935GetPwrStat(void) {
+ if (AS3935ReadRegister(PWR_REG))
+ return 0;
+ return 1;
+}
+
+uint8_t AS3935GetIRQ(void) {
+ delay(2);
+ return AS3935ReadRegister(IRQ_TBL);
+}
+
+uint8_t AS3935TranslIrq(uint8_t irq, uint8_t distance) {
+ switch(irq) {
+ case 0: return 7; // Interrupt with no IRQ
+ case 1: return 5; // Noise level too high
+ case 4: return 6; // Disturber detected
+ case 8:
+ if (distance == -1) return 2; // Lightning out of Distance
+ else if (distance == 0) return 3; // Distance cannot be determined
+ else if (distance == 1) return 4; // Storm is Overhead
+ else return 1; // Lightning with Distance detected
+ }
+ return 0; // Fix GCC 10.1 warning
+}
+
+uint8_t AS3935GetDistance(void) {
+ return AS3935ReadRegister(LGHT_DIST);
+}
+
+int16_t AS3935CalcDistance(void) {
+ uint8_t dist = AS3935GetDistance();
+ switch (dist) {
+ case 0x3F: return -1; // Out of Range
+ case 0x00: return 0; // Distance cannot be determined
+ case 0x01: return 1; // Storm is Overhead
+ default:
+ if (40 < dist) return 40; // limited because higher is not accurate
+ return dist;
+ }
+}
+
+uint32_t AS3935GetIntensity(void) {
+ uint32_t energy_raw = (AS3935ReadRegister(ENERGY_RAW_3) << 8);
+ energy_raw |= AS3935ReadRegister(ENERGY_RAW_2);
+ energy_raw <<= 8;
+ energy_raw |= AS3935ReadRegister(ENERGY_RAW_1);
+ return energy_raw;
+}
+
+uint8_t AS3935GetTuneCaps(void) {
+ return AS3935ReadRegister(TUNE_CAPS);
+}
+
+void AS3935SetTuneCaps(uint8_t tune) {
+ AS3935WriteRegister(TUNE_CAPS, tune);
+ delay(2);
+ AS3935CalibrateRCO();
+}
+
+uint8_t AS3935GetDisturber(void) {
+ return AS3935ReadRegister(DISTURBER);
+}
+
+void AS3935SetDisturber(uint8_t stat) {
+ AS3935WriteRegister(DISTURBER, stat);
+}
+
+uint8_t AS3935GetMinLights(void) {
+ return AS3935ReadRegister(MIN_NUM_LIGH);
+}
+
+void AS3935SetMinLights(uint8_t stat) {
+ AS3935WriteRegister(MIN_NUM_LIGH, stat);
}
uint8_t AS3935TransMinLights(uint8_t min_lights) {
- if (5 > min_lights) {
- return 0;
- } else if (9 > min_lights) {
- return 1;
- } else if (16 > min_lights) {
- return 2;
- } else {
- return 3;
- }
+ if (5 > min_lights) return 0;
+ else if (9 > min_lights) return 1;
+ else if (16 > min_lights) return 2;
+ else return 3;
}
uint8_t AS3935TranslMinLightsInt(uint8_t min_lights) {
@@ -221,146 +351,7 @@ uint8_t AS3935TranslMinLightsInt(uint8_t min_lights) {
return 0; // Fix GCC 10.1 warning
}
-uint8_t AS3935TranslIrq(uint8_t irq, uint8_t distance) {
- switch(irq) {
- case 0: return 7; // Interrupt with no IRQ
- case 1: return 5; // Noise level too high
- case 4: return 6; // Disturber detected
- case 8:
- if (distance == -1) return 2; // Lightning out of Distance
- else if (distance == 0) return 3; // Distance cannot be determined
- else if (distance == 1) return 4; // Storm is Overhead
- else return 1; // Lightning with Distance detected
- }
- return 0; // Fix GCC 10.1 warning
-}
-
-void AS3935CalcVrmsLevel(uint16_t &vrms, uint8_t &stage)
-{
- uint8_t room = AS3935GetGain();
- uint8_t nflev = AS3935GetNoiseFloor();
- if (room == 0x24)
- {
- switch (nflev){
- case 0x00:
- vrms = 28;
- break;
- case 0x01:
- vrms = 45;
- break;
- case 0x02:
- vrms = 62;
- break;
- case 0x03:
- vrms = 78;
- break;
- case 0x04:
- vrms = 95;
- break;
- case 0x05:
- vrms = 112;
- break;
- case 0x06:
- vrms = 130;
- break;
- case 0x07:
- vrms = 146;
- break;
- }
- stage = nflev;
- }
- else
- {
- switch (nflev)
- {
- case 0x00:
- vrms = 390;
- break;
- case 0x01:
- vrms = 630;
- break;
- case 0x02:
- vrms = 860;
- break;
- case 0x03:
- vrms = 1100;
- break;
- case 0x04:
- vrms = 1140;
- break;
- case 0x05:
- vrms = 1570;
- break;
- case 0x06:
- vrms = 1800;
- break;
- case 0x07:
- vrms = 2000;
- break;
- }
- stage = nflev + 8;
- }
-}
-
-/********************************************************************************************/
-uint8_t AS3935GetIRQ() {
- delay(2);
- return AS3935ReadRegister(IRQ_TBL);
-}
-
-uint8_t AS3935GetDistance() {
- return AS3935ReadRegister(LGHT_DIST);
-}
-
-int16_t AS3935CalcDistance() {
- uint8_t dist = AS3935GetDistance();
- switch (dist) {
- case 0x3F: return -1; // Out of Range
- case 0x01: return 1; // Storm is Overhead
- case 0x00: return 0; // Distance cannot be determined
- default:
- if (40 < dist){
- return 40;// limited because higher is not accurate
- }
- return dist;
- }
-}
-
-uint32_t AS3935GetIntensity() {
- uint32_t nrgy_raw = (AS3935ReadRegister(ENERGY_RAW_3) << 8);
- nrgy_raw |= AS3935ReadRegister(ENERGY_RAW_2);
- nrgy_raw <<= 8;
- nrgy_raw |= AS3935ReadRegister(ENERGY_RAW_1);
- return nrgy_raw;
-}
-
-uint8_t AS3935GetTuneCaps() {
- return AS3935ReadRegister(TUNE_CAPS);
-}
-
-void AS3935SetTuneCaps(uint8_t tune) {
- AS3935WriteRegister(TUNE_CAPS, tune);
- delay(2);
- AS3935CalibrateRCO();
-}
-
-uint8_t AS3935GetDisturber() {
- return AS3935ReadRegister(DISTURBER);
-}
-
-void AS3935SetDisturber(uint8_t stat) {
- AS3935WriteRegister(DISTURBER, stat);
-}
-
-uint8_t AS3935GetMinLights() {
- return AS3935ReadRegister(MIN_NUM_LIGH);
-}
-
-void AS3935SetMinLights(uint8_t stat) {
- AS3935WriteRegister(MIN_NUM_LIGH, stat);
-}
-
-uint8_t AS3935GetNoiseFloor() {
+uint8_t AS3935GetNoiseFloor(void) {
return AS3935ReadRegister(NF_LEVEL);
}
@@ -368,7 +359,7 @@ void AS3935SetNoiseFloor(uint8_t noise) {
AS3935WriteRegister(NF_LEVEL , noise);
}
-uint8_t AS3935GetGain() {
+uint8_t AS3935GetGain(void) {
if (AS3935ReadRegister(AFE_GB) == OUTDOORS)
return OUTDOORS;
return INDOORS;
@@ -378,13 +369,25 @@ void AS3935SetGain(uint8_t room) {
AS3935WriteRegister(AFE_GB, room);
}
-uint8_t AS3935GetGainInt() {
+uint8_t AS3935GetGainInt(void) {
if (AS3935ReadRegister(AFE_GB) == OUTDOORS)
- return 1;
-return 0;
+ return 1;
+ return 0;
}
-uint8_t AS3935GetSpikeRejection() {
+void AS3935CalcVrmsLevel(uint16_t &vrms, uint8_t &stage) {
+ uint8_t room = AS3935GetGain();
+ uint8_t nflev = AS3935GetNoiseFloor();
+ if (room == INDOORS) {
+ vrms = pgm_read_byte(AS3935_VrmsIndoor + nflev);
+ stage = nflev;
+ } else {
+ vrms = pgm_read_word(AS3935_VrmsOutdoor + nflev);
+ stage = nflev + 8;
+ }
+}
+
+uint8_t AS3935GetSpikeRejection(void) {
return AS3935ReadRegister(SPIKE_REJECT);
}
@@ -392,7 +395,7 @@ void AS3935SetSpikeRejection(uint8_t rej) {
AS3935WriteRegister(SPIKE_REJECT, rej);
}
-uint8_t AS3935GetWdth() {
+uint8_t AS3935GetWdth(void) {
return AS3935ReadRegister(WDTH);
}
@@ -400,17 +403,15 @@ void AS3935SetWdth(uint8_t wdth) {
AS3935WriteRegister(WDTH, wdth);
}
-bool AS3935AutoTune(){
+bool AS3935AutoTune(void) {
detachInterrupt(Pin(GPIO_AS3935));
bool result = AS3935AutoTuneCaps(Pin(GPIO_AS3935));
- attachInterrupt(digitalPinToInterrupt(Pin(GPIO_AS3935)), AS3935Isr, RISING);
return result;
}
/********************************************************************************************/
// Noise Floor autofunctions
-bool AS3935LowerNoiseFloor() {
- uint8_t noise = AS3935GetNoiseFloor();
+bool AS3935LowerNoiseFloor(void) {
uint16_t vrms;
uint8_t stage;
AS3935CalcVrmsLevel(vrms, stage);
@@ -421,6 +422,7 @@ bool AS3935LowerNoiseFloor() {
return true;
}
}
+ uint8_t noise = AS3935GetNoiseFloor();
if (0 < noise && stage > Settings.as3935_parameter.nf_autotune_min) {
noise--;
AS3935SetNoiseFloor(noise);
@@ -429,7 +431,7 @@ bool AS3935LowerNoiseFloor() {
return false;
}
-bool AS3935RaiseNoiseFloor() {
+bool AS3935RaiseNoiseFloor(void) {
uint8_t noise = AS3935GetNoiseFloor();
uint8_t room = AS3935GetGain();
if (Settings.as3935_functions.nf_autotune_both) {
@@ -449,22 +451,25 @@ bool AS3935RaiseNoiseFloor() {
/********************************************************************************************/
// init functions
-bool AS3935SetDefault() {
- I2cWrite8(AS3935_ADDR, 0x3C, 0x96); // Set default
- delay(2);
+bool AS3935SetDefault(void) {
+ AS3935Reset();
+ AS3935SetDisturber(1); // Disturber on by default
+ AS3935SetNoiseFloor(7); // NF High on by default
Settings.as3935_sensor_cfg[0] = I2cRead8(AS3935_ADDR, 0x00);
Settings.as3935_sensor_cfg[1] = I2cRead8(AS3935_ADDR, 0x01);
Settings.as3935_sensor_cfg[2] = I2cRead8(AS3935_ADDR, 0x02);
Settings.as3935_sensor_cfg[3] = I2cRead8(AS3935_ADDR, 0x03);
Settings.as3935_sensor_cfg[4] = I2cRead8(AS3935_ADDR, 0x08);
+ // set all eeprom functions and values to default
+ Settings.as3935_functions.data = 0x00;
Settings.as3935_parameter.nf_autotune_min = 0x00;
Settings.as3935_parameter.nf_autotune_time = 4;
Settings.as3935_parameter.dist_autotune_time = 1;
return true;
}
-void AS3935InitSettings() {
- if(Settings.as3935_functions.nf_autotune){
+void AS3935InitSettings(void) {
+ if(Settings.as3935_functions.nf_autotune) {
if(Settings.as3935_parameter.nf_autotune_min) {
if (Settings.as3935_parameter.nf_autotune_min > 7) {
AS3935SetGain(OUTDOORS);
@@ -475,114 +480,137 @@ void AS3935InitSettings() {
}
}
}
- I2cWrite8(AS3935_ADDR, 0x00, Settings.as3935_sensor_cfg[0]);
+ I2cWrite8(AS3935_ADDR, 0x00, Settings.as3935_sensor_cfg[0] & SETREG00MASK);
I2cWrite8(AS3935_ADDR, 0x01, Settings.as3935_sensor_cfg[1]);
I2cWrite8(AS3935_ADDR, 0x02, Settings.as3935_sensor_cfg[2]);
- I2cWrite8(AS3935_ADDR, 0x03, Settings.as3935_sensor_cfg[3]);
+ I2cWrite8(AS3935_ADDR, 0x03, Settings.as3935_sensor_cfg[3] & SETREG03MASK);
I2cWrite8(AS3935_ADDR, 0x08, Settings.as3935_sensor_cfg[4]);
delay(2);
}
-void AS3935Setup(void) {
+bool AS3935Setup(void) {
if (Settings.as3935_sensor_cfg[0] == 0x00) {
AS3935SetDefault();
} else {
AS3935InitSettings();
}
- AS3935CalibrateRCO();
+ return AS3935CalibrateRCO();
}
-bool AS3935init() {
- uint8_t ret = I2cRead8(AS3935_ADDR, 0x00);
- if(INDOORS == ret || OUTDOORS == ret) // 0x24
+bool AS3935init(void) {
+ AS3935Reset();
+ uint8_t afe_gb = I2cRead8(AS3935_ADDR, 0x00) & SETREG00MASK;
+ if(INDOORS == afe_gb)
return true;
return false;
}
void AS3935Detect(void) {
- if (I2cActive(AS3935_ADDR)) return;
- if (AS3935init())
- {
+ if (!I2cSetDevice(AS3935_ADDR)) return;
+ if (AS3935init()) {
I2cSetActiveFound(AS3935_ADDR, D_NAME_AS3935);
- pinMode(Pin(GPIO_AS3935), INPUT);
- attachInterrupt(digitalPinToInterrupt(Pin(GPIO_AS3935)), AS3935Isr, RISING);
- AS3935Setup();
- as3935_active = 1;
+ if (PinUsed(GPIO_AS3935)) {
+ pinMode(Pin(GPIO_AS3935), INPUT);
+ if (!AS3935Setup()) return;
+ as3935_sensor.active = true;
+ } else {
+ AddLog_P2(LOG_LEVEL_INFO, PSTR("I2C: AS3935 GPIO Pin not defined!"));
+ }
}
}
-void AS3935EverySecond() {
- if (as3935_sensor.detected) {
- as3935_sensor.irq = AS3935GetIRQ(); // 1 =Noise, 4 = Disturber, 8 = storm
- switch (as3935_sensor.irq) {
- case 1:
- if (Settings.as3935_functions.nf_autotune) {
- if (AS3935RaiseNoiseFloor()) as3935_sensor.nftimer = 0;
+void AS3935EverySecond(void) {
+ if (!as3935_sensor.poweroff) { // Power Off
+ if (as3935_sensor.detected) {
+ as3935_sensor.detected = false;
+ as3935_sensor.irq = AS3935GetIRQ(); // 1 = Noise, 4 = Disturber, 8 = storm
+
+ if (10 > as3935_sensor.icount) {
+ switch (as3935_sensor.irq) {
+ case 1:
+ if (Settings.as3935_functions.nf_autotune) {
+ if (AS3935RaiseNoiseFloor())
+ as3935_sensor.nftimer = 0;
+ }
+ break;
+ case 4:
+ if (Settings.as3935_functions.dist_autotune) {
+ AS3935SetDisturber(1);
+ }
+ break;
+ case 8:
+ as3935_sensor.intensity = AS3935GetIntensity();
+ as3935_sensor.distance = AS3935CalcDistance();
+ as3935_sensor.http_intensity = as3935_sensor.intensity;
+ as3935_sensor.http_distance = as3935_sensor.distance;
+ break;
}
- break;
- case 4:
- if (Settings.as3935_functions.dist_autotune) {
- AS3935SetDisturber(1);
- as3935_sensor.autodist_activ = true;
- }
- break;
- case 8:
- as3935_sensor.intensity = AS3935GetIntensity();
- as3935_sensor.distance = AS3935CalcDistance();
- as3935_sensor.http_intensity = as3935_sensor.intensity;
- as3935_sensor.http_distance = as3935_sensor.distance;
- break;
- }
- // http show
- as3935_sensor.http_irq = AS3935TranslIrq(as3935_sensor.irq, as3935_sensor.distance);
- // mqtt publish
- as3935_sensor.mqtt_irq = as3935_sensor.http_irq;
- switch (as3935_sensor.mqtt_irq) {
+ // http show
+ as3935_sensor.http_event = AS3935TranslIrq(as3935_sensor.irq, as3935_sensor.distance);
+ } else {
+ as3935_sensor.http_event = 8; // flicker detected
+ }
+
+ // mqtt publish
+ as3935_sensor.mqtt_event = as3935_sensor.http_event;
+
+ switch (as3935_sensor.mqtt_event) {
case 5:
case 6:
- if (!Settings.as3935_functions.mqtt_only_Light_Event) {
+ if (!Settings.as3935_functions.mqtt_only_Light_Event) {
MqttPublishSensor();
- as3935_sensor.http_timer = 10;
- }
- break;
- default:
- as3935_sensor.http_timer = 60;
+ as3935_sensor.http_time = 10;
+ }
+ break;
+ case 7:
+ if (!Settings.as3935_functions.suppress_irq_no_Event) {
+ MqttPublishSensor();
+ as3935_sensor.http_time = 10;
+ }
+ break;
+ default:
+ as3935_sensor.http_time = 30;
MqttPublishSensor();
- }
- // clear mqtt events for Teleperiod
- as3935_sensor.intensity = 0;
- as3935_sensor.distance = 0;
- as3935_sensor.mqtt_irq = 0;
- // start http times
- as3935_sensor.http_count_start = 1;
- as3935_sensor.http_count = 0;
- as3935_sensor.icount++; // Int counter
- as3935_sensor.detected = false;
- }
+ }
- if (as3935_sensor.http_count_start) as3935_sensor.http_count++;
- // clear Http
- if (as3935_sensor.http_count > as3935_sensor.http_timer) {
- as3935_sensor.http_count_start = 0;
- as3935_sensor.http_intensity = 0;
- as3935_sensor.http_distance = 0;
- as3935_sensor.http_irq = 0;
- }
- // Noise Floor Autotune function
- if (Settings.as3935_functions.nf_autotune) {
- as3935_sensor.nftimer++;
- if (as3935_sensor.nftimer > Settings.as3935_parameter.nf_autotune_time * 60) {
- AS3935LowerNoiseFloor();
- as3935_sensor.nftimer = 0;
+ as3935_sensor.irq = 0;
+ // clear mqtt events for Teleperiod
+ as3935_sensor.intensity = 0;
+ as3935_sensor.distance = 0;
+ as3935_sensor.mqtt_event = 0;
+ // start http times
+ as3935_sensor.http_count_start = true;
+ as3935_sensor.http_count = 0;
}
- }
- // Disturber auto function
- if (Settings.as3935_functions.dist_autotune) {
- if (as3935_sensor.autodist_activ) as3935_sensor.disttimer++;
- if (as3935_sensor.disttimer >= Settings.as3935_parameter.dist_autotune_time * 60) {
- AS3935SetDisturber(0);
- as3935_sensor.disttimer = 0;
- as3935_sensor.autodist_activ = false;
+ as3935_sensor.icount = 0;
+
+ // count http times
+ if (as3935_sensor.http_count_start)
+ as3935_sensor.http_count++;
+ // clear Http Event
+ if (as3935_sensor.http_count > as3935_sensor.http_time) {
+ as3935_sensor.http_count_start = false;
+ as3935_sensor.http_intensity = 0;
+ as3935_sensor.http_distance = 0;
+ as3935_sensor.http_event = 0;
+ }
+ // Noise Floor Autotune function
+ if (Settings.as3935_functions.nf_autotune) {
+ as3935_sensor.nftimer++;
+ if (as3935_sensor.nftimer > Settings.as3935_parameter.nf_autotune_time * 60) {
+ AS3935LowerNoiseFloor();
+ as3935_sensor.nftimer = 0;
+ }
+ }
+ // Disturber auto function
+ if (Settings.as3935_functions.dist_autotune) {
+ if (AS3935GetDisturber()) {
+ as3935_sensor.disttimer++;
+ }
+ if (as3935_sensor.disttimer >= Settings.as3935_parameter.dist_autotune_time * 60) {
+ AS3935SetDisturber(0);
+ as3935_sensor.disttimer = 0;
+ }
}
}
}
@@ -593,6 +621,16 @@ bool AS3935Cmd(void) {
if (!strncasecmp_P(XdrvMailbox.topic, PSTR(D_NAME_AS3935), name_len)) {
uint32_t command_code = GetCommandCode(command, sizeof(command), XdrvMailbox.topic + name_len, kAS3935_Commands);
switch (command_code) {
+ case CMND_AS3935_POWER:
+ if (XdrvMailbox.data_len) {
+ if (!XdrvMailbox.payload) {
+ AS3935PwrDown();
+ } else {
+ AS3935PwrUp();
+ }
+ }
+ Response_P(S_JSON_AS3935_COMMAND_STRING, command, S_JSON_AS3935_COMMAND_ONOFF[AS3935GetPwrStat()]);
+ break;
case CMND_AS3935_SET_NF:
if (XdrvMailbox.data_len) {
if (15 >= XdrvMailbox.payload) {
@@ -712,6 +750,14 @@ bool AS3935Cmd(void) {
}
Response_P(S_JSON_AS3935_COMMAND_STRING, command, S_JSON_AS3935_COMMAND_ONOFF[Settings.as3935_functions.mqtt_only_Light_Event]);
break;
+ case CMND_AS3935_MQTT_NO_IRQ_EVT:
+ if (XdrvMailbox.data_len) {
+ if (2 > XdrvMailbox.payload) {
+ Settings.as3935_functions.suppress_irq_no_Event = XdrvMailbox.payload;
+ }
+ }
+ Response_P(S_JSON_AS3935_COMMAND_STRING, command, S_JSON_AS3935_COMMAND_ONOFF[Settings.as3935_functions.suppress_irq_no_Event]);
+ break;
case CMND_AS3935_SETTINGS: {
if (!XdrvMailbox.data_len) {
uint8_t gain = AS3935GetGainInt();
@@ -719,30 +765,31 @@ bool AS3935Cmd(void) {
uint8_t stage;
AS3935CalcVrmsLevel(vrms, stage);
uint8_t nf_floor = AS3935GetNoiseFloor();
- uint8_t min_nf = Settings.as3935_parameter.nf_autotune_min;
uint8_t tunecaps = AS3935GetTuneCaps();
uint8_t minnumlight = AS3935TranslMinLightsInt(AS3935GetMinLights());
uint8_t disturber = AS3935GetDisturber();
uint8_t reinj = AS3935GetSpikeRejection();
uint8_t wdth = AS3935GetWdth();
+ uint8_t min_nf = Settings.as3935_parameter.nf_autotune_min;
+ uint8_t nf_time = Settings.as3935_parameter.nf_autotune_time;
uint8_t nfauto = Settings.as3935_functions.nf_autotune;
- uint8_t distauto = Settings.as3935_functions.dist_autotune;
uint8_t nfautomax = Settings.as3935_functions.nf_autotune_both;
+ uint8_t distauto = Settings.as3935_functions.dist_autotune;
uint8_t jsonlight = Settings.as3935_functions.mqtt_only_Light_Event;
- uint8_t nf_time = Settings.as3935_parameter.nf_autotune_time;
- uint8_t dist_time =Settings.as3935_parameter.dist_autotune_time;
- Response_P(S_JSON_AS3935_COMMAND_SETTINGS, S_JSON_AS3935_COMMAND_GAIN[gain], nf_floor, vrms, tunecaps, minnumlight, reinj, wdth, min_nf, nf_time, dist_time, S_JSON_AS3935_COMMAND_ONOFF[disturber], S_JSON_AS3935_COMMAND_ONOFF[nfauto], S_JSON_AS3935_COMMAND_ONOFF[distauto], S_JSON_AS3935_COMMAND_ONOFF[nfautomax], S_JSON_AS3935_COMMAND_ONOFF[jsonlight]);
+ uint8_t jsonirq = Settings.as3935_functions.suppress_irq_no_Event;
+ uint8_t dist_time = Settings.as3935_parameter.dist_autotune_time;
+ Response_P(S_JSON_AS3935_COMMAND_SETTINGS, S_JSON_AS3935_COMMAND_GAIN[gain], nf_floor, vrms, tunecaps, minnumlight, reinj, wdth, min_nf, nf_time, dist_time, S_JSON_AS3935_COMMAND_ONOFF[disturber], S_JSON_AS3935_COMMAND_ONOFF[nfauto], S_JSON_AS3935_COMMAND_ONOFF[distauto], S_JSON_AS3935_COMMAND_ONOFF[nfautomax], S_JSON_AS3935_COMMAND_ONOFF[jsonlight], S_JSON_AS3935_COMMAND_ONOFF[jsonirq]);
}
}
- break;
- case CMND_AS3935_CALIBRATE: {
- bool calreslt;
- if (!XdrvMailbox.data_len) calreslt = AS3935AutoTune();
- Response_P(S_JSON_AS3935_COMMAND_NVALUE, S_JSON_AS3935_COMMAND_CAL[calreslt], AS3935GetTuneCaps());
- }
- break;
- default:
- return false;
+ break;
+ case CMND_AS3935_CALIBRATE: {
+ bool calreslt;
+ if (!XdrvMailbox.data_len) calreslt = AS3935AutoTune();
+ Response_P(S_JSON_AS3935_COMMAND_NVALUE, S_JSON_AS3935_COMMAND_CAL[calreslt], AS3935GetTuneCaps());
+ }
+ break;
+ default:
+ return false;
}
return true;
} else {
@@ -750,13 +797,12 @@ bool AS3935Cmd(void) {
}
}
-void AH3935Show(bool json)
-{
+void AH3935Show(bool json) {
if (json) {
uint16_t vrms;
uint8_t stage;
AS3935CalcVrmsLevel(vrms, stage);
- ResponseAppend_P(JSON_SNS_AS3935_EVENTS, D_SENSOR_AS3935, as3935_sensor.mqtt_irq, as3935_sensor.distance, as3935_sensor.intensity, stage);
+ ResponseAppend_P(JSON_SNS_AS3935_EVENTS, D_SENSOR_AS3935, as3935_sensor.mqtt_event, as3935_sensor.distance, as3935_sensor.intensity, stage);
#ifdef USE_WEBSERVER
} else {
uint8_t gain = AS3935GetGainInt();
@@ -765,12 +811,13 @@ void AH3935Show(bool json)
uint8_t stage;
AS3935CalcVrmsLevel(vrms, stage);
- WSContentSend_PD(HTTP_SNS_AS3935_TABLE_1[as3935_sensor.http_irq], D_NAME_AS3935, as3935_sensor.http_distance);
+ WSContentSend_PD(HTTP_SNS_AS3935_TABLE_1[as3935_sensor.http_event], D_NAME_AS3935, as3935_sensor.http_distance);
WSContentSend_PD(HTTP_SNS_AS3935_DISTANZ, as3935_sensor.http_distance);
WSContentSend_PD(HTTP_SNS_AS3935_ENERGY, as3935_sensor.http_intensity);
WSContentSend_PD(HTTP_SNS_AS3935_GAIN[gain], D_NAME_AS3935);
WSContentSend_PD(HTTP_SNS_AS3935_DISTURBER[disturber], D_NAME_AS3935);
WSContentSend_PD(HTTP_SNS_AS3935_VRMS, vrms, stage);
+
#endif // USE_WEBSERVER
}
}
@@ -779,16 +826,13 @@ void AH3935Show(bool json)
* Interface
\*********************************************************************************************/
-bool Xsns67(uint8_t function)
-{
+bool Xsns67(uint8_t function) {
if (!I2cEnabled(XI2C_48)) { return false; }
-
bool result = false;
-
if (FUNC_INIT == function) {
AS3935Detect();
}
- else if (as3935_active) {
+ else if (as3935_sensor.active) {
switch (function) {
case FUNC_EVERY_SECOND:
AS3935EverySecond();
From 2e067977b957296a19ef4623be6e244b0b45d766 Mon Sep 17 00:00:00 2001
From: device111 <48546979+device111@users.noreply.github.com>
Date: Thu, 3 Sep 2020 09:39:48 +0200
Subject: [PATCH 05/38] Update de_DE.h
---
tasmota/language/de_DE.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tasmota/language/de_DE.h b/tasmota/language/de_DE.h
index abef862c9..cf7271539 100644
--- a/tasmota/language/de_DE.h
+++ b/tasmota/language/de_DE.h
@@ -812,7 +812,7 @@
#define D_AS3935_GAIN "Rauschpegel:"
#define D_AS3935_ENERGY "Energie:"
#define D_AS3935_DISTANCE "Entfernung:"
-#define D_AS3935_DISTURBER "Enstörer:"
+#define D_AS3935_DISTURBER "Entstörer:"
#define D_AS3935_VRMS "µVrms:"
#define D_AS3935_APRX "ca.:"
#define D_AS3935_AWAY "entfernt"
From 4f4f74a545e90fa0503e9240665e94f1ae1af1b3 Mon Sep 17 00:00:00 2001
From: Jason2866 <24528715+Jason2866@users.noreply.github.com>
Date: Thu, 3 Sep 2020 10:48:36 +0200
Subject: [PATCH 06/38] AS3935 not enabled
---
tasmota/my_user_config.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tasmota/my_user_config.h b/tasmota/my_user_config.h
index 067490f14..5f94c5fc0 100644
--- a/tasmota/my_user_config.h
+++ b/tasmota/my_user_config.h
@@ -552,7 +552,7 @@
// #define WEMOS_MOTOR_V1_FREQ 1000 // Default frequency
// #define USE_HDC1080 // [I2cDriver45] Enable HDC1080 temperature/humidity sensor (I2C address 0x40) (+1k5 code)
// #define USE_IAQ // [I2cDriver46] Enable iAQ-core air quality sensor (I2C address 0x5a) (+0k6 code)
- #define USE_AS3935 // [I2cDriver48] Enable AS3935 Franklin Lightning Sensor (I2C address 0x03) (+5k4 code)
+// #define USE_AS3935 // [I2cDriver48] Enable AS3935 Franklin Lightning Sensor (I2C address 0x03) (+5k4 code)
// #define USE_VEML6075 // [I2cDriver49] Enable VEML6075 UVA/UVB/UVINDEX Sensor (I2C address 0x10) (+2k1 code)
// #define USE_VEML7700 // [I2cDriver50] Enable VEML7700 Ambient Light sensor (I2C addresses 0x10) (+4k5 code)
// #define USE_MCP9808 // [I2cDriver51] Enable MCP9808 temperature sensor (I2C addresses 0x18 - 0x1F) (+0k9 code)
From a7fb362e1c0df4d4688bd2ffd950cbcc99d1e37f Mon Sep 17 00:00:00 2001
From: gemu2015
Date: Thu, 3 Sep 2020 12:26:03 +0200
Subject: [PATCH 07/38] display touch interface unified
---
.../Arduino_ST7789.cpp | 5 +-
lib/Arduino_ST7789-gemu-1.0/Arduino_ST7789.h | 2 +-
lib/FT5206_Library/src/FT5206.cpp | 2 +-
lib/FT5206_Library/src/FT5206.h | 2 +
.../src/renderer.cpp | 1 +
.../src/renderer.h | 16 +-
tasmota/xdrv_10_scripter.ino | 182 +++++++++++++-----
tasmota/xdrv_13_display.ino | 182 +++++++++++++++++-
tasmota/xdsp_08_ILI9488.ino | 101 +++++-----
tasmota/xdsp_10_RA8876.ino | 172 +++--------------
tasmota/xdsp_12_ST7789.ino | 161 +++-------------
11 files changed, 438 insertions(+), 388 deletions(-)
diff --git a/lib/Arduino_ST7789-gemu-1.0/Arduino_ST7789.cpp b/lib/Arduino_ST7789-gemu-1.0/Arduino_ST7789.cpp
index 344c22dad..487af20de 100755
--- a/lib/Arduino_ST7789-gemu-1.0/Arduino_ST7789.cpp
+++ b/lib/Arduino_ST7789-gemu-1.0/Arduino_ST7789.cpp
@@ -546,7 +546,7 @@ void Arduino_ST7789::DisplayOnff(int8_t on) {
writecommand(ST7789_DISPON); //Display on
if (_bp>=0) {
#ifdef ST7789_DIMMER
- ledcWrite(ESP32_PWM_CHANNEL,255);
+ ledcWrite(ESP32_PWM_CHANNEL,dimmer);
#else
digitalWrite(_bp,HIGH);
#endif
@@ -564,7 +564,8 @@ void Arduino_ST7789::DisplayOnff(int8_t on) {
}
// dimmer 0-100
-void Arduino_ST7789::dim(uint8_t dimmer) {
+void Arduino_ST7789::dim(uint8_t dim) {
+ dimmer = dim;
if (dimmer>15) dimmer=15;
dimmer=((float)dimmer/15.0)*255.0;
#ifdef ESP32
diff --git a/lib/Arduino_ST7789-gemu-1.0/Arduino_ST7789.h b/lib/Arduino_ST7789-gemu-1.0/Arduino_ST7789.h
index f2571a104..2d97346e7 100755
--- a/lib/Arduino_ST7789-gemu-1.0/Arduino_ST7789.h
+++ b/lib/Arduino_ST7789-gemu-1.0/Arduino_ST7789.h
@@ -161,7 +161,7 @@ class Arduino_ST7789 : public Renderer {
boolean _hwSPI;
boolean _SPI9bit;
boolean _DCbit;
-
+ uint8_t dimmer;
int8_t _cs, _dc, _rst, _sid, _sclk, _bp;
#if defined(USE_FAST_IO)
diff --git a/lib/FT5206_Library/src/FT5206.cpp b/lib/FT5206_Library/src/FT5206.cpp
index 29d4c6778..f0b106174 100755
--- a/lib/FT5206_Library/src/FT5206.cpp
+++ b/lib/FT5206_Library/src/FT5206.cpp
@@ -41,7 +41,7 @@ int FT5206_Class::begin(TwoWire &port, uint8_t addr)
}
_readByte(FT5206_CHIPID_REG, 1, &val);
//Serial.printf("chip id %d\n",val );
- if ((val != FT6206_CHIPID) && (val != FT6236_CHIPID) && (val != FT6236U_CHIPID) && (val != FT5206U_CHIPID)) {
+ if ((val != FT6206_CHIPID) && (val != FT6236_CHIPID) && (val != FT6236U_CHIPID) && (val != FT5206U_CHIPID) && (val != FT5316_CHIPID) ) {
return false;
}
_init = true;
diff --git a/lib/FT5206_Library/src/FT5206.h b/lib/FT5206_Library/src/FT5206.h
index 1a9d4d0ad..d8b6a8e3a 100755
--- a/lib/FT5206_Library/src/FT5206.h
+++ b/lib/FT5206_Library/src/FT5206.h
@@ -49,6 +49,8 @@ github:https://github.com/lewisxhe/FT5206_Library
#define FT6236U_CHIPID 0x64
#define FT5206U_CHIPID 0x64
+#define FT5316_CHIPID 0x0a
+
#define DEVIDE_MODE 0x00
#define TD_STATUS 0x02
#define TOUCH1_XH 0x03
diff --git a/lib/esp-epaper-29-ws-20171230-gemu-1.1/src/renderer.cpp b/lib/esp-epaper-29-ws-20171230-gemu-1.1/src/renderer.cpp
index 10806220f..a8f0f9e57 100644
--- a/lib/esp-epaper-29-ws-20171230-gemu-1.1/src/renderer.cpp
+++ b/lib/esp-epaper-29-ws-20171230-gemu-1.1/src/renderer.cpp
@@ -519,4 +519,5 @@ void VButton::xdrawButton(bool inverted) {
wr_redir=0;
}
+
/* END OF FILE */
diff --git a/lib/esp-epaper-29-ws-20171230-gemu-1.1/src/renderer.h b/lib/esp-epaper-29-ws-20171230-gemu-1.1/src/renderer.h
index 09347ae29..27ff56efe 100644
--- a/lib/esp-epaper-29-ws-20171230-gemu-1.1/src/renderer.h
+++ b/lib/esp-epaper-29-ws-20171230-gemu-1.1/src/renderer.h
@@ -46,9 +46,23 @@ private:
uint8_t font;
};
+typedef union {
+ uint8_t data;
+ struct {
+ uint8_t spare0 : 1;
+ uint8_t spare1 : 1;
+ uint8_t spare2 : 1;
+ uint8_t spare3 : 1;
+ uint8_t disable : 1;
+ uint8_t on_off : 1;
+ uint8_t is_pushbutton : 1;
+ uint8_t is_virtual : 1;
+ };
+} TButton_State;
+
class VButton : public Adafruit_GFX_Button {
public:
- uint8_t vpower;
+ TButton_State vpower;
void xdrawButton(bool inverted);
};
diff --git a/tasmota/xdrv_10_scripter.ino b/tasmota/xdrv_10_scripter.ino
index 6353fa38a..cd5f986d2 100755
--- a/tasmota/xdrv_10_scripter.ino
+++ b/tasmota/xdrv_10_scripter.ino
@@ -300,12 +300,35 @@ struct T_INDEX {
};
struct M_FILT {
+#ifdef LARGE_ARRAYS
+ uint16_t numvals;
+ uint16_t index;
+#else
uint8_t numvals;
uint8_t index;
+#endif // LARGE_ARRAYS
float maccu;
float rbuff[1];
};
+
+#ifdef LARGE_ARRAYS
+#undef AND_FILT_MASK
+#undef OR_FILT_MASK
+#define AND_FILT_MASK 0x7fff
+#define OR_FILT_MASK 0x8000
+#undef MAX_ARRAY_SIZE
+#define MAX_ARRAY_SIZE 1000
+#else
+#undef AND_FILT_MASK
+#undef OR_FILT_MASK
+#define AND_FILT_MASK 0x7f
+#define OR_FILT_MASK 0x80
+#undef MAX_ARRAY_SIZE
+#define MAX_ARRAY_SIZE 127
+#endif
+
+
typedef union {
uint8_t data;
struct {
@@ -462,6 +485,8 @@ void RulesTeleperiod(void) {
#define SCRIPT_SKIP_SPACES while (*lp==' ' || *lp=='\t') lp++;
#define SCRIPT_SKIP_EOL while (*lp==SCRIPT_EOL) lp++;
+float *Get_MFAddr(uint8_t index,uint16_t *len,uint16_t *ipos);
+
// allocates all variables and presets them
int16_t Init_Scripter(void) {
char *script;
@@ -543,12 +568,16 @@ char *script;
if ((*lp=='m' || *lp=='M') && *(lp+1)==':') {
uint8_t flg=*lp;
lp+=2;
+ if (*lp=='p' && *(lp+1)==':') {
+ vtypes[vars].bits.is_permanent=1;
+ lp+=2;
+ }
if (flg=='M') mfilt[numflt].numvals=8;
else mfilt[numflt].numvals=5;
vtypes[vars].bits.is_filter=1;
mfilt[numflt].index=0;
if (flg=='M') {
- mfilt[numflt].numvals|=0x80;
+ mfilt[numflt].numvals|=OR_FILT_MASK;
}
vtypes[vars].index=numflt;
numflt++;
@@ -587,9 +616,13 @@ char *script;
while (*op==' ') op++;
if (isdigit(*op)) {
// lenght define follows
- uint8_t flen=atoi(op);
- mfilt[numflt-1].numvals&=0x80;
- mfilt[numflt-1].numvals|=flen&0x7f;
+ uint16_t flen=atoi(op);
+ if (flen>MAX_ARRAY_SIZE) {
+ // limit array size
+ flen=MAX_ARRAY_SIZE;
+ }
+ mfilt[numflt-1].numvals&=OR_FILT_MASK;
+ mfilt[numflt-1].numvals|=flen&AND_FILT_MASK;
}
}
@@ -635,11 +668,11 @@ char *script;
uint16_t fsize=0;
for (count=0; countnumvals=mfilt[count].numvals;
- mp+=sizeof(struct M_FILT)+((mfilt[count].numvals&0x7f)-1)*sizeof(float);
+ mp+=sizeof(struct M_FILT)+((mfilt[count].numvals&AND_FILT_MASK)-1)*sizeof(float);
}
glob_script_mem.numvars=vars;
@@ -760,12 +793,21 @@ char *script;
for (uint8_t count=0; countnumvals&0x7f;
+ *len=mflp->numvals&AND_FILT_MASK;
if (ipos) *ipos=mflp->index;
return mflp->rbuff;
}
- mp+=sizeof(struct M_FILT)+((mflp->numvals&0x7f)-1)*sizeof(float);
+ mp+=sizeof(struct M_FILT)+((mflp->numvals&AND_FILT_MASK)-1)*sizeof(float);
}
return 0;
}
-float Get_MFVal(uint8_t index,uint8_t bind) {
+float Get_MFVal(uint8_t index,int16_t bind) {
uint8_t *mp=(uint8_t*)glob_script_mem.mfilt;
for (uint8_t count=0; countnumvals&0x7f;
+ uint16_t maxind=mflp->numvals&AND_FILT_MASK;
if (!bind) {
return mflp->index;
}
+ if (bind<0) {
+ return maxind;
+ }
if (bind<1 || bind>maxind) bind=maxind;
return mflp->rbuff[bind-1];
}
- mp+=sizeof(struct M_FILT)+((mflp->numvals&0x7f)-1)*sizeof(float);
+ mp+=sizeof(struct M_FILT)+((mflp->numvals&AND_FILT_MASK)-1)*sizeof(float);
}
return 0;
}
-void Set_MFVal(uint8_t index,uint8_t bind,float val) {
+void Set_MFVal(uint8_t index,uint16_t bind,float val) {
uint8_t *mp=(uint8_t*)glob_script_mem.mfilt;
for (uint8_t count=0; countnumvals&0x7f;
+ uint16_t maxind=mflp->numvals&AND_FILT_MASK;
if (!bind) {
mflp->index=val;
} else {
@@ -1022,7 +1067,7 @@ void Set_MFVal(uint8_t index,uint8_t bind,float val) {
}
return;
}
- mp+=sizeof(struct M_FILT)+((mflp->numvals&0x7f)-1)*sizeof(float);
+ mp+=sizeof(struct M_FILT)+((mflp->numvals&AND_FILT_MASK)-1)*sizeof(float);
}
}
@@ -1032,15 +1077,15 @@ float Get_MFilter(uint8_t index) {
for (uint8_t count=0; countnumvals&0x80) {
+ if (mflp->numvals&OR_FILT_MASK) {
// moving average
- return mflp->maccu/(mflp->numvals&0x7f);
+ return mflp->maccu/(mflp->numvals&AND_FILT_MASK);
} else {
// median, sort array indices
return median_array(mflp->rbuff,mflp->numvals);
}
}
- mp+=sizeof(struct M_FILT)+((mflp->numvals&0x7f)-1)*sizeof(float);
+ mp+=sizeof(struct M_FILT)+((mflp->numvals&AND_FILT_MASK)-1)*sizeof(float);
}
return 0;
}
@@ -1050,13 +1095,13 @@ void Set_MFilter(uint8_t index, float invar) {
for (uint8_t count=0; countnumvals&0x80) {
+ if (mflp->numvals&OR_FILT_MASK) {
// moving average
mflp->maccu-=mflp->rbuff[mflp->index];
mflp->maccu+=invar;
mflp->rbuff[mflp->index]=invar;
mflp->index++;
- if (mflp->index>=(mflp->numvals&0x7f)) mflp->index=0;
+ if (mflp->index>=(mflp->numvals&AND_FILT_MASK)) mflp->index=0;
} else {
// median
mflp->rbuff[mflp->index]=invar;
@@ -1065,7 +1110,7 @@ void Set_MFilter(uint8_t index, float invar) {
}
break;
}
- mp+=sizeof(struct M_FILT)+((mflp->numvals&0x7f)-1)*sizeof(float);
+ mp+=sizeof(struct M_FILT)+((mflp->numvals&AND_FILT_MASK)-1)*sizeof(float);
}
}
@@ -2140,7 +2185,7 @@ chknext:
}
} else {
if (index>glob_script_mem.si_num) {
- fvar=glob_script_mem.si_num;
+ index=glob_script_mem.si_num;
}
strlcpy(str,glob_script_mem.last_index_string+(index*glob_script_mem.max_ssize),glob_script_mem.max_ssize);
}
@@ -2691,7 +2736,7 @@ chknext:
if (index<1 || index>MAXBUTTONS) index=1;
index--;
if (buttons[index]) {
- fvar=buttons[index]->vpower&0x80;
+ fvar=buttons[index]->vpower.on_off;
} else {
fvar=-1;
}
@@ -2807,7 +2852,7 @@ chknext:
#if defined(USE_TTGO_WATCH) && defined(USE_FT5206)
if (!strncmp(vname,"wtch(",5)) {
lp=GetNumericResult(lp+5,OPER_EQU,&fvar,0);
- fvar=FT5206_touched(fvar);
+ fvar=Touch_Status(fvar);
lp++;
len=0;
goto exit;
@@ -3430,7 +3475,7 @@ int16_t Run_Scripter(const char *type, int8_t tlen, char *js) {
int16_t Run_script_sub(const char *type, int8_t tlen, JsonObject *jo) {
uint8_t vtype=0,sindex,xflg,floop=0,globvindex,fromscriptcmd=0;
char *lp_next;
- int8_t globaindex,saindex;
+ int16_t globaindex,saindex;
struct T_INDEX ind;
uint8_t operand,lastop,numeric=1,if_state[IF_NEST],if_exe[IF_NEST],if_result[IF_NEST],and_or,ifstck=0;
if_state[ifstck]=0;
@@ -3762,7 +3807,7 @@ int16_t Run_script_sub(const char *type, int8_t tlen, JsonObject *jo) {
if ((vtype&STYPE)==0) {
// numeric result
if (glob_script_mem.type[ind.index].bits.is_filter) {
- uint8_t len=0;
+ uint16_t len=0;
float *fa=Get_MFAddr(index,&len,0);
//Serial.printf(">> 2 %d\n",(uint32_t)*fa);
if (fa && len) ws2812_set_array(fa,len,fvar);
@@ -4175,12 +4220,26 @@ void Scripter_save_pvars(void) {
for (uint8_t count=0; countPMEM_SIZE) {
- vtp[count].bits.is_permanent=0;
- return;
+ if (vtp[count].bits.is_filter) {
+ // save array
+ uint16_t len=0;
+ float *fa=Get_MFAddr(index,&len,0);
+ mlen+=sizeof(float)*len;
+ if (mlen>glob_script_mem.script_pram_size) {
+ vtp[count].bits.is_permanent=0;
+ return;
+ }
+ while (len--) {
+ *fp++=*fa++;
+ }
+ } else {
+ mlen+=sizeof(float);
+ if (mlen>glob_script_mem.script_pram_size) {
+ vtp[count].bits.is_permanent=0;
+ return;
+ }
+ *fp++=glob_script_mem.fvars[index];
}
- *fp++=glob_script_mem.fvars[index];
}
}
char *cp=(char*)fp;
@@ -4190,7 +4249,7 @@ void Scripter_save_pvars(void) {
char *sp=glob_script_mem.glob_snp+(index*glob_script_mem.max_ssize);
uint8_t slen=strlen(sp);
mlen+=slen+1;
- if (mlen>PMEM_SIZE) {
+ if (mlen>glob_script_mem.script_pram_size) {
vtp[count].bits.is_permanent=0;
return;
}
@@ -6013,6 +6072,9 @@ const char SCRIPT_MSG_GOPT4[] PROGMEM =
const char SCRIPT_MSG_GOPT5[] PROGMEM =
"new Date(0,1,1,%d,%d)";
+const char SCRIPT_MSG_GOPT6[] PROGMEM =
+"title:'%s',isStacked:false,vAxis:{viewWindow:{min:%d,max:%d}}%s";
+
const char SCRIPT_MSG_GTE1[] PROGMEM = "'%s'";
#define GLIBS_MAIN 1<<0
@@ -6022,11 +6084,11 @@ const char SCRIPT_MSG_GTE1[] PROGMEM = "'%s'";
#define MAX_GARRAY 4
-char *gc_get_arrays(char *lp, float **arrays, uint8_t *ranum, uint8_t *rentries, uint8_t *ipos) {
+char *gc_get_arrays(char *lp, float **arrays, uint8_t *ranum, uint16_t *rentries, uint16_t *ipos) {
struct T_INDEX ind;
uint8_t vtype;
-uint8 entries=0;
-uint8_t cipos=0;
+uint16 entries=0;
+uint16_t cipos=0;
uint8_t anum=0;
while (anum> 2 %d\n",(uint32_t)*fa);
+ //Serial.printf(">> 2 %d\n",len);
if (fa && len>=entries) {
if (!entries) {
entries = len;
@@ -6295,12 +6357,12 @@ void ScriptWebShow(char mc) {
} else {
if (mc=='w') {
- WSContentSend_PD(PSTR("%s"),tmp);
+ WSContentSend_PD(PSTR("%s"),lin);
} else {
if (optflg) {
- WSContentSend_PD(PSTR("%s
"),tmp);
+ WSContentSend_PD(PSTR("%s
"),lin);
} else {
- WSContentSend_PD(PSTR("{s}%s{e}"),tmp);
+ WSContentSend_PD(PSTR("{s}%s{e}"),lin);
}
}
}
@@ -6388,8 +6450,8 @@ exgc:
float *arrays[MAX_GARRAY];
uint8_t anum=0;
- uint8 entries=0;
- uint8 ipos=0;
+ uint16_t entries=0;
+ uint16_t ipos=0;
lp=gc_get_arrays(lp, &arrays[0], &anum, &entries, &ipos);
if (anum>nanum) {
@@ -6434,10 +6496,19 @@ exgc:
lp=GetStringResult(lp,OPER_EQU,label,0);
SCRIPT_SKIP_SPACES
- int8_t todflg=-1;
+ int16_t divflg=1;
+ int16_t todflg=-1;
if (!strncmp(label,"cnt",3)) {
todflg=atoi(&label[3]);
if (todflg>=entries) todflg=entries-1;
+ } else {
+ uint16 segments=1;
+ for (uint32_t cnt=0; cnt
-#include
Renderer *renderer;
@@ -757,7 +756,28 @@ void DisplayText(void)
#ifdef USE_TOUCH_BUTTONS
case 'b':
- { int16_t num,gxp,gyp,gxs,gys,outline,fill,textcolor,textsize;
+ { int16_t num,gxp,gyp,gxs,gys,outline,fill,textcolor,textsize; uint8_t dflg=1;
+ if (*cp=='e' || *cp=='d') {
+ // enable disable
+ uint8_t dis=0;
+ if (*cp=='d') dis=1;
+ cp++;
+ var=atoiv(cp,&num);
+ num=num%MAXBUTTONS;
+ cp+=var;
+ if (buttons[num]) {
+ buttons[num]->vpower.disable=dis;
+ if (!dis) {
+ if (buttons[num]->vpower.is_virtual) buttons[num]->xdrawButton(buttons[num]->vpower.on_off);
+ else buttons[num]->xdrawButton(bitRead(power,num));
+ }
+ }
+ break;
+ }
+ if (*cp=='-') {
+ cp++;
+ dflg=0;
+ }
var=atoiv(cp,&num);
cp+=var;
cp++;
@@ -797,16 +817,23 @@ void DisplayText(void)
if (renderer) {
buttons[num]= new VButton();
if (buttons[num]) {
- buttons[num]->vpower=bflags;
buttons[num]->initButtonUL(renderer,gxp,gyp,gxs,gys,renderer->GetColorFromIndex(outline),\
- renderer->GetColorFromIndex(fill),renderer->GetColorFromIndex(textcolor),bbuff,textsize);
+ renderer->GetColorFromIndex(fill),renderer->GetColorFromIndex(textcolor),bbuff,textsize);
if (!bflags) {
// power button
- buttons[num]->xdrawButton(bitRead(power,num));
+ if (dflg) buttons[num]->xdrawButton(bitRead(power,num));
+ buttons[num]->vpower.is_virtual=0;
} else {
// virtual button
- buttons[num]->vpower&=0x7f;
- buttons[num]->xdrawButton(buttons[num]->vpower&0x80);
+ buttons[num]->vpower.is_virtual=1;
+ if (bflags==2) {
+ // push
+ buttons[num]->vpower.is_pushbutton=1;
+ } else {
+ // toggle
+ buttons[num]->vpower.is_pushbutton=0;
+ }
+ if (dflg) buttons[num]->xdrawButton(buttons[num]->vpower.on_off);
}
}
}
@@ -2019,7 +2046,146 @@ void AddValue(uint8_t num,float fval) {
}
}
}
-#endif
+#endif // USE_GRAPH
+
+#ifdef USE_FT5206
+
+// touch panel controller
+#undef FT5206_address
+#define FT5206_address 0x38
+
+#include
+FT5206_Class *touchp;
+TP_Point pLoc;
+
+
+extern VButton *buttons[];
+bool FT5206_found;
+
+bool Touch_Init(TwoWire &i2c) {
+ FT5206_found = false;
+ touchp = new FT5206_Class();
+ if (touchp->begin(i2c, FT5206_address)) {
+ I2cSetActiveFound(FT5206_address, "FT5206");
+ FT5206_found = true;
+ }
+ return FT5206_found;
+}
+
+uint32_t Touch_Status(uint32_t sel) {
+ if (FT5206_found) {
+ switch (sel) {
+ case 0:
+ return touchp->touched();
+ case 1:
+ return pLoc.x;
+ case 2:
+ return pLoc.y;
+ }
+ return 0;
+ } else {
+ return 0;
+ }
+}
+
+#ifdef USE_TOUCH_BUTTONS
+void Touch_MQTT(uint8_t index, const char *cp) {
+ ResponseTime_P(PSTR(",\"FT5206\":{\"%s%d\":\"%d\"}}"), cp, index+1, buttons[index]->vpower.on_off);
+ MqttPublishTeleSensor();
+}
+
+void Touch_RDW_BUTT(uint32_t count, uint32_t pwr) {
+ buttons[count]->xdrawButton(pwr);
+ if (pwr) buttons[count]->vpower.on_off = 1;
+ else buttons[count]->vpower.on_off = 0;
+}
+
+// check digitizer hit
+void Touch_Check(void(*rotconvert)(int16_t *x, int16_t *y)) {
+uint16_t temp;
+uint8_t rbutt=0;
+uint8_t vbutt=0;
+
+
+ if (touchp->touched()) {
+ // did find a hit
+ pLoc = touchp->getPoint(0);
+
+ if (renderer) {
+
+ rotconvert(&pLoc.x, &pLoc.y);
+
+ //AddLog_P2(LOG_LEVEL_INFO, PSTR("touch %d - %d"), pLoc.x, pLoc.y);
+ // now must compare with defined buttons
+ for (uint8_t count=0; countvpower.disable) {
+ if (buttons[count]->contains(pLoc.x, pLoc.y)) {
+ // did hit
+ buttons[count]->press(true);
+ if (buttons[count]->justPressed()) {
+ if (!buttons[count]->vpower.is_virtual) {
+ uint8_t pwr=bitRead(power, rbutt);
+ if (!SendKey(KEY_BUTTON, rbutt+1, POWER_TOGGLE)) {
+ ExecuteCommandPower(rbutt+1, POWER_TOGGLE, SRC_BUTTON);
+ Touch_RDW_BUTT(count, !pwr);
+ }
+ } else {
+ // virtual button
+ const char *cp;
+ if (!buttons[count]->vpower.is_pushbutton) {
+ // toggle button
+ buttons[count]->vpower.on_off ^= 1;
+ cp="TBT";
+ } else {
+ // push button
+ buttons[count]->vpower.on_off = 1;
+ cp="PBT";
+ }
+ buttons[count]->xdrawButton(buttons[count]->vpower.on_off);
+ Touch_MQTT(count,cp);
+ }
+ }
+ }
+ if (!buttons[count]->vpower.is_virtual) {
+ rbutt++;
+ } else {
+ vbutt++;
+ }
+ }
+ }
+ }
+ } else {
+ // no hit
+ for (uint8_t count=0; countpress(false);
+ if (buttons[count]->justReleased()) {
+ if (buttons[count]->vpower.is_virtual) {
+ if (buttons[count]->vpower.is_pushbutton) {
+ // push button
+ buttons[count]->vpower.on_off = 0;
+ Touch_MQTT(count,"PBT");
+ buttons[count]->xdrawButton(buttons[count]->vpower.on_off);
+ }
+ }
+ }
+ if (!buttons[count]->vpower.is_virtual) {
+ // check if power button stage changed
+ uint8_t pwr = bitRead(power, rbutt);
+ uint8_t vpwr = buttons[count]->vpower.on_off;
+ if (pwr != vpwr) {
+ Touch_RDW_BUTT(count, pwr);
+ }
+ rbutt++;
+ }
+ }
+ }
+ pLoc.x = 0;
+ pLoc.y = 0;
+ }
+}
+#endif // USE_TOUCH_BUTTONS
+#endif // USE_FT5206
/*********************************************************************************************\
* Interface
diff --git a/tasmota/xdsp_08_ILI9488.ino b/tasmota/xdsp_08_ILI9488.ino
index e49e7bffe..b1e738fd4 100644
--- a/tasmota/xdsp_08_ILI9488.ino
+++ b/tasmota/xdsp_08_ILI9488.ino
@@ -27,18 +27,12 @@
#define COLORED 1
#define UNCOLORED 0
-// touch panel controller
-#define FT6236_address 0x38
-
// using font 8 is opional (num=3)
// very badly readable, but may be useful for graphs
#define USE_TINY_FONT
#include
-#include
-
-TouchLocation ili9488_pLoc;
uint8_t ili9488_ctouch_counter = 0;
// currently fixed
@@ -47,13 +41,7 @@ uint8_t ili9488_ctouch_counter = 0;
extern uint8_t *buffer;
extern uint8_t color_type;
ILI9488 *ili9488;
-
-#ifdef USE_TOUCH_BUTTONS
-extern VButton *buttons[];
-#endif
-
extern const uint16_t picture[];
-uint8_t FT6236_found;
/*********************************************************************************************/
@@ -126,30 +114,52 @@ void ILI9488_InitDriver()
#endif
color_type = COLOR_COLOR;
- // start digitizer with fixed adress
-
- if (I2cEnabled(XI2C_38) && I2cSetDevice(FT6236_address)) {
- FT6236begin(FT6236_address);
- FT6236_found=1;
- I2cSetActiveFound(FT6236_address, "FT6236");
- } else {
- FT6236_found=0;
- }
-
+ // start digitizer
+#ifdef USE_FT5206
+ Touch_Init(Wire);
+#endif
}
}
+#ifdef USE_FT5206
#ifdef USE_TOUCH_BUTTONS
-void ILI9488_MQTT(uint8_t count,const char *cp) {
- ResponseTime_P(PSTR(",\"RA8876\":{\"%s%d\":\"%d\"}}"), cp,count+1,(buttons[count]->vpower&0x80)>>7);
- MqttPublishTeleSensor();
+
+void ILI9488_RotConvert(int16_t *x, int16_t *y) {
+int16_t temp;
+ if (renderer) {
+ uint8_t rot=renderer->getRotation();
+ switch (rot) {
+ case 0:
+ temp=*y;
+ *y=renderer->height()-*x;
+ *x=temp;
+ break;
+ case 1:
+ break;
+ case 2:
+ break;
+ case 3:
+ temp=*y;
+ *y=*x;
+ *x=renderer->width()-temp;
+ break;
+ }
+ }
}
-void ILI9488_RDW_BUTT(uint32_t count,uint32_t pwr) {
- buttons[count]->xdrawButton(pwr);
- if (pwr) buttons[count]->vpower|=0x80;
- else buttons[count]->vpower&=0x7f;
+// check digitizer hit
+void ILI9488_CheckTouch(void) {
+ ili9488_ctouch_counter++;
+ if (2 == ili9488_ctouch_counter) {
+ // every 100 ms should be enough
+ ili9488_ctouch_counter = 0;
+ Touch_Check(ILI9488_RotConvert);
+ }
}
+#endif // USE_TOUCH_BUTTONS
+#endif // USE_FT5206
+
+/*
// check digitizer hit
void FT6236Check() {
uint16_t temp;
@@ -181,12 +191,11 @@ if (2 == ili9488_ctouch_counter) {
// now must compare with defined buttons
for (uint8_t count=0; countvpower&0x7f;
if (buttons[count]->contains(ili9488_pLoc.x,ili9488_pLoc.y)) {
// did hit
buttons[count]->press(true);
if (buttons[count]->justPressed()) {
- if (!bflags) {
+ if (!buttons[count]->vpower.is_virtual) {
uint8_t pwr=bitRead(power,rbutt);
if (!SendKey(KEY_BUTTON, rbutt+1, POWER_TOGGLE)) {
ExecuteCommandPower(rbutt+1, POWER_TOGGLE, SRC_BUTTON);
@@ -195,21 +204,21 @@ if (2 == ili9488_ctouch_counter) {
} else {
// virtual button
const char *cp;
- if (bflags==1) {
+ if (!buttons[count]->vpower.is_pushbutton) {
// toggle button
- buttons[count]->vpower^=0x80;
+ buttons[count]->vpower.on_off^=1;
cp="TBT";
} else {
// push button
- buttons[count]->vpower|=0x80;
+ buttons[count]->vpower.on_off=1;
cp="PBT";
}
- buttons[count]->xdrawButton(buttons[count]->vpower&0x80);
+ buttons[count]->xdrawButton(buttons[count]->vpower.on_off);
ILI9488_MQTT(count,cp);
}
}
}
- if (!bflags) {
+ if (!buttons[count]->vpower.is_virtual) {
rbutt++;
} else {
vbutt++;
@@ -221,23 +230,21 @@ if (2 == ili9488_ctouch_counter) {
// no hit
for (uint8_t count=0; countvpower&0x7f;
buttons[count]->press(false);
if (buttons[count]->justReleased()) {
- uint8_t bflags=buttons[count]->vpower&0x7f;
- if (bflags>0) {
- if (bflags>1) {
+ if (buttons[count]->vpower.is_virtual) {
+ if (buttons[count]->vpower.is_pushbutton) {
// push button
- buttons[count]->vpower&=0x7f;
+ buttons[count]->vpower.on_off=0;
ILI9488_MQTT(count,"PBT");
}
- buttons[count]->xdrawButton(buttons[count]->vpower&0x80);
+ buttons[count]->xdrawButton(buttons[count]->vpower.on_off);
}
}
- if (!bflags) {
+ if (!buttons[count]->vpower.is_virtual) {
// check if power button stage changed
uint8_t pwr=bitRead(power,rbutt);
- uint8_t vpwr=(buttons[count]->vpower&0x80)>>7;
+ uint8_t vpwr=buttons[count]->vpower.on_off;
if (pwr!=vpwr) {
ILI9488_RDW_BUTT(count,pwr);
}
@@ -251,6 +258,8 @@ if (2 == ili9488_ctouch_counter) {
}
}
#endif // USE_TOUCH_BUTTONS
+*/
+
/*********************************************************************************************/
/*********************************************************************************************\
* Interface
@@ -270,7 +279,9 @@ bool Xdsp08(uint8_t function)
break;
case FUNC_DISPLAY_EVERY_50_MSECOND:
#ifdef USE_TOUCH_BUTTONS
- if (FT6236_found) FT6236Check();
+ if (FT5206_found) {
+ ILI9488_CheckTouch();
+ }
#endif
break;
}
diff --git a/tasmota/xdsp_10_RA8876.ino b/tasmota/xdsp_10_RA8876.ino
index aa8e82f4d..47b909b4c 100644
--- a/tasmota/xdsp_10_RA8876.ino
+++ b/tasmota/xdsp_10_RA8876.ino
@@ -27,29 +27,17 @@
#define COLORED 1
#define UNCOLORED 0
-// touch panel controller
-#define FT5316_address 0x38
-
// using font 8 is opional (num=3)
// very badly readable, but may be useful for graphs
#define USE_TINY_FONT
#include
-#include
-TouchLocation ra8876_pLoc;
uint8_t ra8876_ctouch_counter = 0;
-
-#ifdef USE_TOUCH_BUTTONS
-extern VButton *buttons[];
-#endif
-
extern uint8_t *buffer;
extern uint8_t color_type;
RA8876 *ra8876;
-uint8_t FT5316_found;
-
/*********************************************************************************************/
void RA8876_InitDriver()
{
@@ -114,147 +102,41 @@ void RA8876_InitDriver()
#endif
color_type = COLOR_COLOR;
- if (I2cEnabled(XI2C_39) && I2cSetDevice(FT5316_address)) {
- FT6236begin(FT5316_address);
- FT5316_found=1;
- I2cSetActiveFound(FT5316_address, "FT5316");
- } else {
- FT5316_found=0;
- }
+#ifdef USE_FT5206
+ Touch_Init(Wire);
+#endif
}
}
-#ifdef USE_TOUCH_BUTTONS
-void RA8876_MQTT(uint8_t count,const char *cp) {
- ResponseTime_P(PSTR(",\"RA8876\":{\"%s%d\":\"%d\"}}"), cp,count+1,(buttons[count]->vpower&0x80)>>7);
- MqttPublishTeleSensor();
-}
-void RA8876_RDW_BUTT(uint32_t count,uint32_t pwr) {
- buttons[count]->xdrawButton(pwr);
- if (pwr) buttons[count]->vpower|=0x80;
- else buttons[count]->vpower&=0x7f;
+#ifdef USE_FT5206
+#ifdef USE_TOUCH_BUTTONS
+
+// no rotation support
+void RA8876_RotConvert(int16_t *x, int16_t *y) {
+int16_t temp;
+ if (renderer) {
+ *x=*x*renderer->width()/800;
+ *y=*y*renderer->height()/480;
+
+ *x = renderer->width() - *x;
+ *y = renderer->height() - *y;
+ }
}
// check digitizer hit
-void FT5316Check() {
-uint16_t temp;
-uint8_t rbutt=0,vbutt=0;
-ra8876_ctouch_counter++;
-if (2 == ra8876_ctouch_counter) {
- // every 100 ms should be enough
- ra8876_ctouch_counter=0;
- // panel has 800x480
- if (FT6236readTouchLocation(&ra8876_pLoc,1)) {
- ra8876_pLoc.x=ra8876_pLoc.x*RA8876_TFTWIDTH/800;
- ra8876_pLoc.y=ra8876_pLoc.y*RA8876_TFTHEIGHT/480;
- // did find a hit
-
- if (renderer) {
-
- // rotation not supported
- ra8876_pLoc.x=RA8876_TFTWIDTH-ra8876_pLoc.x;
- ra8876_pLoc.y=RA8876_TFTHEIGHT-ra8876_pLoc.y;
-
- /*
- uint8_t rot=renderer->getRotation();
- switch (rot) {
- case 0:
- //temp=pLoc.y;
- pLoc.x=renderer->width()-pLoc.x;
- pLoc.y=renderer->height()-pLoc.y;
- //pLoc.x=temp;
- break;
- case 1:
- break;
- case 2:
- break;
- case 3:
- temp=pLoc.y;
- pLoc.y=pLoc.x;
- pLoc.x=renderer->width()-temp;
- break;
- }
- */
- //AddLog_P2(LOG_LEVEL_INFO, PSTR(">> %d,%d"),ra8876_pLoc.x,ra8876_pLoc.y);
-
-
- //Serial.printf("loc x: %d , loc y: %d\n",pLoc.x,pLoc.y);
-
- // now must compare with defined buttons
- for (uint8_t count=0; countvpower&0x7f;
- if (buttons[count]->contains(ra8876_pLoc.x,ra8876_pLoc.y)) {
- // did hit
- buttons[count]->press(true);
- if (buttons[count]->justPressed()) {
- if (!bflags) {
- // real button
- uint8_t pwr=bitRead(power,rbutt);
- if (!SendKey(KEY_BUTTON, rbutt+1, POWER_TOGGLE)) {
- ExecuteCommandPower(rbutt+1, POWER_TOGGLE, SRC_BUTTON);
- RA8876_RDW_BUTT(count,!pwr);
- }
- } else {
- // virtual button
- const char *cp;
- if (bflags==1) {
- // toggle button
- buttons[count]->vpower^=0x80;
- cp="TBT";
- } else {
- // push button
- buttons[count]->vpower|=0x80;
- cp="PBT";
- }
- buttons[count]->xdrawButton(buttons[count]->vpower&0x80);
- RA8876_MQTT(count,cp);
- }
- }
- }
- if (!bflags) {
- rbutt++;
- } else {
- vbutt++;
- }
- }
- }
- }
- } else {
- // no hit
- for (uint8_t count=0; countvpower&0x7f;
- buttons[count]->press(false);
- if (buttons[count]->justReleased()) {
- if (bflags>0) {
- if (bflags>1) {
- // push button
- buttons[count]->vpower&=0x7f;
- RA8876_MQTT(count,"PBT");
- }
- buttons[count]->xdrawButton(buttons[count]->vpower&0x80);
- }
- }
- if (!bflags) {
- // check if power button stage changed
- uint8_t pwr=bitRead(power,rbutt);
- uint8_t vpwr=(buttons[count]->vpower&0x80)>>7;
- if (pwr!=vpwr) {
- RA8876_RDW_BUTT(count,pwr);
- }
- rbutt++;
- }
- }
- }
- ra8876_pLoc.x=0;
- ra8876_pLoc.y=0;
+void RA8876_CheckTouch(void) {
+ ra8876_ctouch_counter++;
+ if (2 == ra8876_ctouch_counter) {
+ // every 100 ms should be enough
+ ra8876_ctouch_counter = 0;
+ Touch_Check(RA8876_RotConvert);
}
}
-}
-#endif // USE_TOUCH_BUTTONS
+#endif // USE_TOUCH_BUTTONS
+#endif // USE_FT5206
+
/*
void testall() {
ra8876->clearScreen(0);
@@ -452,8 +334,8 @@ bool Xdsp10(uint8_t function)
result = true;
break;
case FUNC_DISPLAY_EVERY_50_MSECOND:
-#ifdef USE_TOUCH_BUTTONS
- if (FT5316_found) FT5316Check();
+#ifdef USE_FT5206
+ if (FT5206_found) RA8876_CheckTouch();
#endif
break;
}
diff --git a/tasmota/xdsp_12_ST7789.ino b/tasmota/xdsp_12_ST7789.ino
index 3b2ef8d82..d31c55fa4 100644
--- a/tasmota/xdsp_12_ST7789.ino
+++ b/tasmota/xdsp_12_ST7789.ino
@@ -17,6 +17,7 @@
along with this program. If not, see .
*/
+//#ifdef USE_SPI
#ifdef USE_SPI
#ifdef USE_DISPLAY
#ifdef USE_DISPLAY_ST7789
@@ -50,12 +51,6 @@ extern uint8_t color_type;
Arduino_ST7789 *st7789;
#ifdef USE_FT5206
-#ifdef USE_TOUCH_BUTTONS
-extern VButton *buttons[];
-#endif
-FT5206_Class *touchp;
-uint8_t FT5206_found;
-TP_Point st7789_pLoc;
uint8_t st7789_ctouch_counter = 0;
#endif // USE_FT5206
@@ -142,155 +137,51 @@ void ST7789_InitDriver()
#define SDA_2 23
#define SCL_2 32
Wire1.begin(SDA_2, SCL_2, 400000);
- touchp = new FT5206_Class();
- if (touchp->begin(Wire1, FT5206_address)) {
- FT5206_found=1;
- //I2cSetDevice(FT5206_address);
- I2cSetActiveFound(FT5206_address, "FT5206");
- } else {
- FT5206_found=0;
- }
+ Touch_Init(Wire1);
#endif // USE_FT5206
#endif // ESP32
}
}
-
#ifdef ESP32
#ifdef USE_FT5206
#ifdef USE_TOUCH_BUTTONS
-void ST7789_MQTT(uint8_t count,const char *cp) {
- ResponseTime_P(PSTR(",\"ST7789\":{\"%s%d\":\"%d\"}}"), cp,count+1,(buttons[count]->vpower&0x80)>>7);
- MqttPublishTeleSensor();
-}
-uint32_t FT5206_touched(uint32_t sel) {
- if (touchp) {
- switch (sel) {
+void ST7789_RotConvert(int16_t *x, int16_t *y) {
+int16_t temp;
+ if (renderer) {
+ uint8_t rot=renderer->getRotation();
+ switch (rot) {
case 0:
- return touchp->touched();
+ break;
case 1:
- return st7789_pLoc.x;
+ temp=*y;
+ *y=renderer->height()-*x;
+ *x=temp;
+ break;
case 2:
- return st7789_pLoc.y;
+ *x=renderer->width()-*x;
+ *y=renderer->height()-*y;
+ break;
+ case 3:
+ temp=*y;
+ *y=*x;
+ *x=renderer->width()-temp;
+ break;
}
- return 0;
- } else {
- return 0;
}
}
-void ST7789_RDW_BUTT(uint32_t count,uint32_t pwr) {
- buttons[count]->xdrawButton(pwr);
- if (pwr) buttons[count]->vpower|=0x80;
- else buttons[count]->vpower&=0x7f;
-}
// check digitizer hit
-void FT5206Check() {
-uint16_t temp;
-uint8_t rbutt=0,vbutt=0;
+void ST7789_CheckTouch() {
st7789_ctouch_counter++;
-if (2 == st7789_ctouch_counter) {
- // every 100 ms should be enough
- st7789_ctouch_counter=0;
-
- if (touchp->touched()) {
- // did find a hit
- st7789_pLoc = touchp->getPoint(0);
- if (renderer) {
- uint8_t rot=renderer->getRotation();
- switch (rot) {
- case 0:
- break;
- case 1:
- temp=st7789_pLoc.y;
- st7789_pLoc.y=renderer->height()-st7789_pLoc.x;
- st7789_pLoc.x=temp;
- break;
- case 2:
- st7789_pLoc.x=renderer->width()-st7789_pLoc.x;
- st7789_pLoc.y=renderer->height()-st7789_pLoc.y;
- break;
- case 3:
- temp=st7789_pLoc.y;
- st7789_pLoc.y=st7789_pLoc.x;
- st7789_pLoc.x=renderer->width()-temp;
- break;
- }
- //AddLog_P2(LOG_LEVEL_INFO, PSTR("touch %d - %d"), st7789_pLoc.x, st7789_pLoc.y);
- // now must compare with defined buttons
- for (uint8_t count=0; countvpower&0x7f;
- if (buttons[count]->contains(st7789_pLoc.x,st7789_pLoc.y)) {
- // did hit
- buttons[count]->press(true);
- if (buttons[count]->justPressed()) {
- if (!bflags) {
- uint8_t pwr=bitRead(power,rbutt);
- if (!SendKey(KEY_BUTTON, rbutt+1, POWER_TOGGLE)) {
- ExecuteCommandPower(rbutt+1, POWER_TOGGLE, SRC_BUTTON);
- ST7789_RDW_BUTT(count,!pwr);
- }
- } else {
- // virtual button
- const char *cp;
- if (bflags==1) {
- // toggle button
- buttons[count]->vpower^=0x80;
- cp="TBT";
- } else {
- // push button
- buttons[count]->vpower|=0x80;
- cp="PBT";
- }
- buttons[count]->xdrawButton(buttons[count]->vpower&0x80);
- ST7789_MQTT(count,cp);
- }
- }
- }
- if (!bflags) {
- rbutt++;
- } else {
- vbutt++;
- }
- }
- }
- }
- } else {
- // no hit
- for (uint8_t count=0; countvpower&0x7f;
- buttons[count]->press(false);
- if (buttons[count]->justReleased()) {
- uint8_t bflags=buttons[count]->vpower&0x7f;
- if (bflags>0) {
- if (bflags>1) {
- // push button
- buttons[count]->vpower&=0x7f;
- ST7789_MQTT(count,"PBT");
- }
- buttons[count]->xdrawButton(buttons[count]->vpower&0x80);
- }
- }
- if (!bflags) {
- // check if power button stage changed
- uint8_t pwr=bitRead(power,rbutt);
- uint8_t vpwr=(buttons[count]->vpower&0x80)>>7;
- if (pwr!=vpwr) {
- ST7789_RDW_BUTT(count,pwr);
- }
- rbutt++;
- }
- }
- }
- st7789_pLoc.x=0;
- st7789_pLoc.y=0;
+ if (2 == st7789_ctouch_counter) {
+ // every 100 ms should be enough
+ st7789_ctouch_counter = 0;
+ Touch_Check(ST7789_RotConvert);
}
}
-}
#endif // USE_TOUCH_BUTTONS
#endif // USE_FT5206
#endif // ESP32
@@ -317,7 +208,7 @@ bool Xdsp12(uint8_t function)
#ifdef USE_FT5206
#ifdef USE_TOUCH_BUTTONS
if (FT5206_found) {
- FT5206Check();
+ ST7789_CheckTouch();
}
#endif
#endif // USE_FT5206
From 1f4b2f7ae5247bf17eda46537394e95ecd8a90b2 Mon Sep 17 00:00:00 2001
From: gemu2015
Date: Thu, 3 Sep 2020 12:27:37 +0200
Subject: [PATCH 08/38] remove obsolete lib
---
lib/FT6236-gemu-1.0/FT6236.cpp | 159 ---------------------------------
lib/FT6236-gemu-1.0/FT6236.h | 28 ------
2 files changed, 187 deletions(-)
delete mode 100644 lib/FT6236-gemu-1.0/FT6236.cpp
delete mode 100644 lib/FT6236-gemu-1.0/FT6236.h
diff --git a/lib/FT6236-gemu-1.0/FT6236.cpp b/lib/FT6236-gemu-1.0/FT6236.cpp
deleted file mode 100644
index 6397190fb..000000000
--- a/lib/FT6236-gemu-1.0/FT6236.cpp
+++ /dev/null
@@ -1,159 +0,0 @@
-#include
-#include
-
-/*
- * This is a static library so we need to make sure we process stuff as quick as possible
- * as we do not want it to interfere with the RTOS by delaying routines unnecessarily.
- * So, no delay()'s etc and opto the code as much as possible.
- * ^^^ Need to be on TODO list to go through and make sure everything is as opto as
- * possible
- */
-
-uint8_t FT6236buf[FT6236_BUFFER_SIZE];
-uint8_t FT6236_i2c_addr = 0x38;
-uint8_t lenLibVersion = 0;
-uint8_t firmwareId = 0;
-
-struct tbuttonregister {
- uint16_t BUTTONID;
- uint16_t xmin;
- uint16_t xmax;
- uint16_t ymin;
- uint16_t ymax;
-} buttonregister[FT6236_MAX_BUTTONS]; // we're limiting to 16 buttons for now - can reduce or increase later as needed.
-
-uint8_t buttoncount = 0;
-
-void FT6236flushbuttonregister(void) {
- uint16_t bid;
- for (bid=0;bid 0) {
- uint16_t x = tl[0].x;
- uint16_t y = tl[0].y;
- for (bid=0;bid= buttonregister[bid].xmin) {
- if (x <= buttonregister[bid].xmax) {
- if (y >= buttonregister[bid].ymin) {
- if (y <= buttonregister[bid].ymax) {
- return buttonregister[bid].BUTTONID;
- }
- }
- }
- }
- }
- }
- return 0;
-}
-
-void FT6236begin(uint8_t i2c_addr) {
- FT6236_i2c_addr=i2c_addr;
- Wire.begin();
- FT6236writeTouchRegister(0,FT6236_MODE_NORMAL);
- lenLibVersion = FT6236readTouchAddr(0x0a1, FT6236buf, 2 );
- firmwareId = FT6236readTouchRegister( 0xa6 );
-}
-
-void FT6236writeTouchRegister(uint8_t reg, uint8_t val)
-{
- Wire.beginTransmission(FT6236_i2c_addr);
- Wire.write(reg); // register 0
- Wire.write(val); // value
- Wire.endTransmission();
-}
-
-uint8_t FT6236readTouchRegister(uint8_t reg)
-{
- Wire.beginTransmission(FT6236_i2c_addr);
- Wire.write(reg); // register 0
- uint8_t retVal = Wire.endTransmission();
- uint8_t returned = Wire.requestFrom(FT6236_i2c_addr,uint8_t(1)); // request 6 uint8_ts from slave device #2
- if (Wire.available())
- {
- retVal = Wire.read();
- }
- return retVal;
-}
-
-uint8_t FT6236readTouchAddr( uint8_t regAddr, uint8_t * pBuf, uint8_t len )
-{
- Wire.beginTransmission(FT6236_i2c_addr);
- Wire.write( regAddr ); // register 0
- uint8_t retVal = Wire.endTransmission();
- uint8_t returned = Wire.requestFrom(FT6236_i2c_addr, len); // request 1 bytes from slave device #2
- uint8_t i;
- for (i = 0; (i < len) && Wire.available(); i++) {
- pBuf[i] = Wire.read();
- }
- return i;
-}
-
-uint8_t FT6236readTouchLocation( TouchLocation * pLoc, uint8_t num )
-{
- uint8_t retVal = 0;
- uint8_t i;
- uint8_t k;
- do
- {
- if (!pLoc) break; // must have a buffer
- if (!num) break; // must be able to take at least one
- uint8_t status = FT6236readTouchRegister(2);
- static uint8_t tbuf[40];
- if ((status & 0x0f) == 0) break; // no points detected
- uint8_t hitPoints = status & 0x0f;
- FT6236readTouchAddr( 0x03, tbuf, hitPoints*6);
- for (k=0,i = 0; (i < hitPoints*6)&&(k < num); k++, i += 6) {
- pLoc[k].x = (tbuf[i+0] & 0x0f) << 8 | tbuf[i+1];
- pLoc[k].y = (tbuf[i+2] & 0x0f) << 8 | tbuf[i+3];
- }
- retVal = k;
- } while (0);
- return retVal;
-}
-
-uint32_t FT6236dist(const TouchLocation & loc)
-{
- uint32_t retVal = 0;
- uint32_t x = loc.x;
- uint32_t y = loc.y;
- retVal = x*x + y*y;
- return retVal;
-}
-
-
-/*
-uint32_t FT6236dist(const TouchLocation & loc1, const TouchLocation & loc2)
-{
- uint32_t retVal = 0;
- uint32_t x = loc1.x - loc2.x;
- uint32_t y = loc1.y - loc2.y;
- retVal = sqrt(x*x + y*y);
- return retVal;
-}
-*/
-
-bool FT6236sameLoc( const TouchLocation & loc, const TouchLocation & loc2 )
-{
- return FT6236dist(loc,loc2) < 50;
-}
diff --git a/lib/FT6236-gemu-1.0/FT6236.h b/lib/FT6236-gemu-1.0/FT6236.h
deleted file mode 100644
index 601d9c67e..000000000
--- a/lib/FT6236-gemu-1.0/FT6236.h
+++ /dev/null
@@ -1,28 +0,0 @@
-#ifndef FT6236
-#define FT6236
-
-#define FT6236_MODE_NORMAL 0x00
-#define FT6236_MODE_TEST 0x04
-#define FT6236_MODE_SYSTEM 0x01
-
-#define FT6236_BUFFER_SIZE 0x1E // 30 bytes buffer
-#define FT6236_MAX_BUTTONS 1 // 50 buttons should be enough for just about any page
-
-struct TouchLocation {
- uint16_t y; // we swop x and y in position because we're using the screen in portrait mode
- uint16_t x;
-};
-
-void FT6236flushbuttonregister(void);
-void FT6236registerbutton(uint16_t buttonid,uint16_t xmin,uint16_t ymin,uint16_t xmax, uint16_t ymax);
-uint16_t FT6236GetButtonMask(void);
-void FT6236begin(uint8_t i2c_addr);
-uint8_t FT6236readTouchRegister( uint8_t reg );
-uint8_t FT6236readTouchLocation( TouchLocation * pLoc, uint8_t num );
-uint8_t FT6236readTouchAddr( uint8_t regAddr, uint8_t * pBuf, uint8_t len );
-void FT6236writeTouchRegister( uint8_t reg, uint8_t val);
-uint32_t FT6236dist(const TouchLocation & loc);
-uint32_t FT6236dist(const TouchLocation & loc1, const TouchLocation & loc2);
-bool FT6236sameLoc( const TouchLocation & loc, const TouchLocation & loc2 );
-
-#endif
From b67c0d129ca0e2dc86e52a1950b45f6530e048c9 Mon Sep 17 00:00:00 2001
From: gemu2015
Date: Thu, 3 Sep 2020 12:38:14 +0200
Subject: [PATCH 09/38] Update xdsp_08_ILI9488.ino
---
tasmota/xdsp_08_ILI9488.ino | 100 ------------------------------------
1 file changed, 100 deletions(-)
diff --git a/tasmota/xdsp_08_ILI9488.ino b/tasmota/xdsp_08_ILI9488.ino
index b1e738fd4..2e8abc434 100644
--- a/tasmota/xdsp_08_ILI9488.ino
+++ b/tasmota/xdsp_08_ILI9488.ino
@@ -159,106 +159,6 @@ void ILI9488_CheckTouch(void) {
#endif // USE_TOUCH_BUTTONS
#endif // USE_FT5206
-/*
-// check digitizer hit
-void FT6236Check() {
-uint16_t temp;
-uint8_t rbutt=0,vbutt=0;
-ili9488_ctouch_counter++;
-if (2 == ili9488_ctouch_counter) {
- // every 100 ms should be enough
- ili9488_ctouch_counter=0;
- if (FT6236readTouchLocation(&ili9488_pLoc,1)) {
- // did find a hit
- if (renderer) {
- uint8_t rot=renderer->getRotation();
- switch (rot) {
- case 0:
- temp=ili9488_pLoc.y;
- ili9488_pLoc.y=renderer->height()-ili9488_pLoc.x;
- ili9488_pLoc.x=temp;
- break;
- case 1:
- break;
- case 2:
- break;
- case 3:
- temp=ili9488_pLoc.y;
- ili9488_pLoc.y=ili9488_pLoc.x;
- ili9488_pLoc.x=renderer->width()-temp;
- break;
- }
- // now must compare with defined buttons
- for (uint8_t count=0; countcontains(ili9488_pLoc.x,ili9488_pLoc.y)) {
- // did hit
- buttons[count]->press(true);
- if (buttons[count]->justPressed()) {
- if (!buttons[count]->vpower.is_virtual) {
- uint8_t pwr=bitRead(power,rbutt);
- if (!SendKey(KEY_BUTTON, rbutt+1, POWER_TOGGLE)) {
- ExecuteCommandPower(rbutt+1, POWER_TOGGLE, SRC_BUTTON);
- ILI9488_RDW_BUTT(count,!pwr);
- }
- } else {
- // virtual button
- const char *cp;
- if (!buttons[count]->vpower.is_pushbutton) {
- // toggle button
- buttons[count]->vpower.on_off^=1;
- cp="TBT";
- } else {
- // push button
- buttons[count]->vpower.on_off=1;
- cp="PBT";
- }
- buttons[count]->xdrawButton(buttons[count]->vpower.on_off);
- ILI9488_MQTT(count,cp);
- }
- }
- }
- if (!buttons[count]->vpower.is_virtual) {
- rbutt++;
- } else {
- vbutt++;
- }
- }
- }
- }
- } else {
- // no hit
- for (uint8_t count=0; countpress(false);
- if (buttons[count]->justReleased()) {
- if (buttons[count]->vpower.is_virtual) {
- if (buttons[count]->vpower.is_pushbutton) {
- // push button
- buttons[count]->vpower.on_off=0;
- ILI9488_MQTT(count,"PBT");
- }
- buttons[count]->xdrawButton(buttons[count]->vpower.on_off);
- }
- }
- if (!buttons[count]->vpower.is_virtual) {
- // check if power button stage changed
- uint8_t pwr=bitRead(power,rbutt);
- uint8_t vpwr=buttons[count]->vpower.on_off;
- if (pwr!=vpwr) {
- ILI9488_RDW_BUTT(count,pwr);
- }
- rbutt++;
- }
- }
- }
- ili9488_pLoc.x=0;
- ili9488_pLoc.y=0;
- }
-}
-}
-#endif // USE_TOUCH_BUTTONS
-*/
/*********************************************************************************************/
/*********************************************************************************************\
From d49997fa0163126ded6ded3bd570dc98c1c6429c Mon Sep 17 00:00:00 2001
From: Theo Arends <11044339+arendst@users.noreply.github.com>
Date: Thu, 3 Sep 2020 14:29:08 +0200
Subject: [PATCH 10/38] Fix ESP32 TasmotaClient detection
Fix ESP32 TasmotaClient detection (#9218)
---
tasmota/xdrv_01_webserver.ino | 3 ++-
tasmota/xdrv_31_tasmota_client.ino | 4 ++++
2 files changed, 6 insertions(+), 1 deletion(-)
diff --git a/tasmota/xdrv_01_webserver.ino b/tasmota/xdrv_01_webserver.ino
index 72876ecf2..dc50e68e1 100644
--- a/tasmota/xdrv_01_webserver.ino
+++ b/tasmota/xdrv_01_webserver.ino
@@ -2781,9 +2781,10 @@ void HandleUploadLoop(void)
} else
#endif // USE_RF_FLASH
#ifdef USE_TASMOTA_CLIENT
- if ((WEMOS == my_module_type) && (upload.buf[0] == ':')) { // Check if this is a ARDUINO CLIENT hex file
+ if (TasmotaClient_Available() && (upload.buf[0] == ':')) { // Check if this is a ARDUINO CLIENT hex file
Update.end(); // End esp8266 update session
Web.upload_file_type = UPL_TASMOTACLIENT;
+
Web.upload_error = TasmotaClient_UpdateInit(); // 0
if (Web.upload_error != 0) { return; }
} else
diff --git a/tasmota/xdrv_31_tasmota_client.ino b/tasmota/xdrv_31_tasmota_client.ino
index 86121475c..89402b408 100644
--- a/tasmota/xdrv_31_tasmota_client.ino
+++ b/tasmota/xdrv_31_tasmota_client.ino
@@ -458,6 +458,10 @@ void TasmotaClient_Init(void) {
}
}
+bool TasmotaClient_Available(void) {
+ return TClient.SerialEnabled;
+}
+
void TasmotaClient_Show(void) {
if ((TClient.type) && (TClientSettings.features.func_json_append)) {
char buffer[100];
From f8f0f4f616a698ffb9a300718f7ba272e8f28906 Mon Sep 17 00:00:00 2001
From: Stephan Hadinger
Date: Thu, 3 Sep 2020 15:11:14 +0200
Subject: [PATCH 11/38] Reduce memory usage of BackLog
---
tasmota/support_command.ino | 2 +-
tasmota/tasmota.ino | 1 +
2 files changed, 2 insertions(+), 1 deletion(-)
diff --git a/tasmota/support_command.ino b/tasmota/support_command.ino
index 379d15e0c..0cdba2969 100644
--- a/tasmota/support_command.ino
+++ b/tasmota/support_command.ino
@@ -323,7 +323,7 @@ void CmndBacklog(void)
backlog.add(blcommand);
}
#else
- backlog[backlog_index] = String(blcommand);
+ backlog[backlog_index] = blcommand;
backlog_index++;
if (backlog_index >= MAX_BACKLOG) backlog_index = 0;
#endif
diff --git a/tasmota/tasmota.ino b/tasmota/tasmota.ino
index d257b6ddb..d3b3afd32 100644
--- a/tasmota/tasmota.ino
+++ b/tasmota/tasmota.ino
@@ -339,6 +339,7 @@ void BacklogLoop(void) {
#else
backlog_mutex = true;
ExecuteCommand((char*)backlog[backlog_pointer].c_str(), SRC_BACKLOG);
+ backlog[backlog_pointer] = (const char*) nullptr; // force deallocation of the String internal memory
backlog_pointer++;
if (backlog_pointer >= MAX_BACKLOG) { backlog_pointer = 0; }
backlog_mutex = false;
From 423b4c0712fe399cfb305dcc0d702f0967a45df6 Mon Sep 17 00:00:00 2001
From: Theo Arends <11044339+arendst@users.noreply.github.com>
Date: Thu, 3 Sep 2020 20:31:31 +0200
Subject: [PATCH 12/38] Workaround compilation regression
Workaround compilation regression (#9223)
---
tasmota/tasmota.ino | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/tasmota/tasmota.ino b/tasmota/tasmota.ino
index d3b3afd32..b8d3293da 100644
--- a/tasmota/tasmota.ino
+++ b/tasmota/tasmota.ino
@@ -60,9 +60,9 @@
#ifdef USE_DISCOVERY
#include // MQTT, Webserver, Arduino OTA
#endif // USE_DISCOVERY
-#ifdef USE_I2C
+//#ifdef USE_I2C
#include // I2C support library
-#endif // USE_I2C
+//#endif // USE_I2C
#ifdef USE_SPI
#include // SPI support, TFT
#endif // USE_SPI
From 23a550cbff071867625d7517fc5ad2adfb761039 Mon Sep 17 00:00:00 2001
From: device111 <48546979+device111@users.noreply.github.com>
Date: Thu, 3 Sep 2020 20:36:02 +0200
Subject: [PATCH 13/38] AS3935 update DE-language
---
tasmota/language/de_DE.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tasmota/language/de_DE.h b/tasmota/language/de_DE.h
index cf7271539..92601dc68 100644
--- a/tasmota/language/de_DE.h
+++ b/tasmota/language/de_DE.h
@@ -809,7 +809,7 @@
#define D_SCRIPT_UPLOAD_FILES "Upload Dateien"
//xsns_67_as3935.ino
-#define D_AS3935_GAIN "Rauschpegel:"
+#define D_AS3935_GAIN "Umgebung:"
#define D_AS3935_ENERGY "Energie:"
#define D_AS3935_DISTANCE "Entfernung:"
#define D_AS3935_DISTURBER "Entstörer:"
From a9209fa170784e4a89059d58ca02813086465d61 Mon Sep 17 00:00:00 2001
From: device111 <48546979+device111@users.noreply.github.com>
Date: Thu, 3 Sep 2020 20:54:04 +0200
Subject: [PATCH 14/38] Update de_DE.h
---
tasmota/language/de_DE.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tasmota/language/de_DE.h b/tasmota/language/de_DE.h
index 92601dc68..272f67988 100644
--- a/tasmota/language/de_DE.h
+++ b/tasmota/language/de_DE.h
@@ -823,7 +823,7 @@
#define D_AS3935_NOISE "Rauschen entdeckt"
#define D_AS3935_DISTDET "Störer entdeckt"
#define D_AS3935_INTNOEV "Interrupt ohne Grund!"
-#define D_AS3935_FLICKER "IRQ Pin flackerd!"
+#define D_AS3935_FLICKER "IRQ Pin flackert!"
#define D_AS3935_POWEROFF "Ausgeschaltet"
#define D_AS3935_NOMESS "lausche..."
#define D_AS3935_ON "On"
From 1bae56eb66bbf0625ab722d283a626c0072f4574 Mon Sep 17 00:00:00 2001
From: Jason2866 <24528715+Jason2866@users.noreply.github.com>
Date: Thu, 3 Sep 2020 23:33:20 +0200
Subject: [PATCH 15/38] Revert "Revert "Use Tasmota Core 2.7.4.1 from
PlatformIO registry""
---
platformio.ini | 2 +-
platformio_override_sample.ini | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/platformio.ini b/platformio.ini
index cac96ba03..e0ee7de7d 100644
--- a/platformio.ini
+++ b/platformio.ini
@@ -125,6 +125,6 @@ build_flags = -DUSE_IR_REMOTE_FULL
[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.1/esp8266-2.7.4.1.zip
+platform_packages = framework-arduinoespressif8266 @ jason2866/framework-arduinoespressif8266
build_unflags = ${esp_defaults.build_unflags}
build_flags = ${esp82xx_defaults.build_flags}
diff --git a/platformio_override_sample.ini b/platformio_override_sample.ini
index facbc450d..1131e7244 100644
--- a/platformio_override_sample.ini
+++ b/platformio_override_sample.ini
@@ -89,7 +89,7 @@ extra_scripts = ${scripts_defaults.extra_scripts}
[tasmota_stage]
; *** Esp8266 core for Arduino version Tasmota stage
platform = espressif8266@2.6.2
-platform_packages = framework-arduinoespressif8266 @ https://github.com/tasmota/Arduino/releases/download/2.7.4.1/esp8266-2.7.4.1.zip
+platform_packages = framework-arduinoespressif8266 @ jason2866/framework-arduinoespressif8266
build_unflags = ${esp_defaults.build_unflags}
build_flags = ${esp82xx_defaults.build_flags}
From fe9339e5621ccddb5176cb433482547d8b540a92 Mon Sep 17 00:00:00 2001
From: Jason2866 <24528715+Jason2866@users.noreply.github.com>
Date: Thu, 3 Sep 2020 23:40:37 +0200
Subject: [PATCH 16/38] Update platformio.ini
---
platformio.ini | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/platformio.ini b/platformio.ini
index e0ee7de7d..86f72211a 100644
--- a/platformio.ini
+++ b/platformio.ini
@@ -125,6 +125,6 @@ build_flags = -DUSE_IR_REMOTE_FULL
[core]
; *** Esp8266 Tasmota modified Arduino core based on core 2.7.4
platform = espressif8266@2.6.2
-platform_packages = framework-arduinoespressif8266 @ jason2866/framework-arduinoespressif8266
+platform_packages = jason2866/framework-arduinoespressif8266
build_unflags = ${esp_defaults.build_unflags}
build_flags = ${esp82xx_defaults.build_flags}
From 8ee0b651553a7ea021450cf60eed8006284f890c Mon Sep 17 00:00:00 2001
From: Jason2866 <24528715+Jason2866@users.noreply.github.com>
Date: Thu, 3 Sep 2020 23:41:16 +0200
Subject: [PATCH 17/38] Update platformio_override_sample.ini
---
platformio_override_sample.ini | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/platformio_override_sample.ini b/platformio_override_sample.ini
index 1131e7244..be5d5f24f 100644
--- a/platformio_override_sample.ini
+++ b/platformio_override_sample.ini
@@ -89,7 +89,7 @@ extra_scripts = ${scripts_defaults.extra_scripts}
[tasmota_stage]
; *** Esp8266 core for Arduino version Tasmota stage
platform = espressif8266@2.6.2
-platform_packages = framework-arduinoespressif8266 @ jason2866/framework-arduinoespressif8266
+platform_packages = jason2866/framework-arduinoespressif8266
build_unflags = ${esp_defaults.build_unflags}
build_flags = ${esp82xx_defaults.build_flags}
From 146eb6654d421c82a14b6ec2e0e3357be199eaf5 Mon Sep 17 00:00:00 2001
From: Theo Arends <11044339+arendst@users.noreply.github.com>
Date: Fri, 4 Sep 2020 12:49:57 +0200
Subject: [PATCH 18/38] Refactor gui upload
---
tasmota/xdrv_01_webserver.ino | 19 +++++++++++++++----
1 file changed, 15 insertions(+), 4 deletions(-)
diff --git a/tasmota/xdrv_01_webserver.ino b/tasmota/xdrv_01_webserver.ino
index dc50e68e1..ed15c9e12 100644
--- a/tasmota/xdrv_01_webserver.ino
+++ b/tasmota/xdrv_01_webserver.ino
@@ -2712,6 +2712,7 @@ void HandleUploadLoop(void)
HTTPUpload& upload = Webserver->upload();
+ // ***** Step1: Start upload file
if (UPLOAD_FILE_START == upload.status) {
restart_flag = 60;
if (0 == upload.filename.c_str()[0]) {
@@ -2752,7 +2753,10 @@ void HandleUploadLoop(void)
}
}
Web.upload_progress_dot_count = 0;
- } else if (!Web.upload_error && (UPLOAD_FILE_WRITE == upload.status)) {
+ }
+
+ // ***** Step2: Write upload file
+ else if (!Web.upload_error && (UPLOAD_FILE_WRITE == upload.status)) {
if (0 == upload.totalSize) {
if (UPL_SETTINGS == Web.upload_file_type) {
Web.config_block_count = 0;
@@ -2803,6 +2807,7 @@ void HandleUploadLoop(void)
// upload.buf[2] = 3; // Force DOUT - ESP8285
}
}
+ AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_UPLOAD "File type %d"), Web.upload_file_type);
}
}
if (UPL_SETTINGS == Web.upload_file_type) {
@@ -2874,7 +2879,10 @@ void HandleUploadLoop(void)
if (!(Web.upload_progress_dot_count % 80)) { Serial.println(); }
}
}
- } else if(!Web.upload_error && (UPLOAD_FILE_END == upload.status)) {
+ }
+
+ // ***** Step3: Finish upload file
+ else if(!Web.upload_error && (UPLOAD_FILE_END == upload.status)) {
if (_serialoutput && (Web.upload_progress_dot_count % 80)) {
Serial.println();
}
@@ -2950,9 +2958,12 @@ void HandleUploadLoop(void)
}
}
if (!Web.upload_error) {
- AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_UPLOAD D_SUCCESSFUL " %u bytes. " D_RESTARTING), upload.totalSize);
+ AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_UPLOAD D_SUCCESSFUL " %u bytes"), upload.totalSize);
}
- } else if (UPLOAD_FILE_ABORTED == upload.status) {
+ }
+
+ // ***** Step4: Abort upload file
+ else if (UPLOAD_FILE_ABORTED == upload.status) {
restart_flag = 0;
MqttRetryCounter(0);
#ifdef USE_COUNTER
From 1295370bf106b0ac42f2af800936073b9f9313fb Mon Sep 17 00:00:00 2001
From: Stephan Hadinger
Date: Sat, 5 Sep 2020 14:44:31 +0200
Subject: [PATCH 19/38] Zigbee refactor of internal structures
---
tasmota/support_light_list.ino | 186 +++
tasmota/support_static_buffer.ino | 15 +
tasmota/xdrv_23_zigbee_1z_libs.ino | 752 ++++++++++++
tasmota/xdrv_23_zigbee_2_devices.ino | 424 +++----
tasmota/xdrv_23_zigbee_5_converters.ino | 1436 +++++++++++------------
tasmota/xdrv_23_zigbee_6_commands.ino | 162 ++-
tasmota/xdrv_23_zigbee_8_parsers.ino | 170 ++-
tasmota/xdrv_23_zigbee_A_impl.ino | 2 +-
8 files changed, 1923 insertions(+), 1224 deletions(-)
create mode 100644 tasmota/support_light_list.ino
create mode 100644 tasmota/xdrv_23_zigbee_1z_libs.ino
diff --git a/tasmota/support_light_list.ino b/tasmota/support_light_list.ino
new file mode 100644
index 000000000..28d2af595
--- /dev/null
+++ b/tasmota/support_light_list.ino
@@ -0,0 +1,186 @@
+/*
+ support_light_list.ino - Lightweight Linked List for simple objects - optimized for low code size and low memory
+
+ Copyright (C) 2020 Theo Arends and Stephan Hadinger
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+*/
+
+/*********************************************************************************************\
+ *
+ * private class for Linked List element
+ *
+\*********************************************************************************************/
+template
+class LList;
+
+template
+class LList_elt {
+public:
+
+ LList_elt() : _next(nullptr), _val() {}
+
+ inline T & val(void) { return _val; }
+ inline LList_elt * next(void) { return _next; }
+ inline void next(LList_elt * next) { _next = next; }
+
+ friend class LList;
+
+protected:
+ LList_elt * _next;
+ T _val;
+};
+
+/*********************************************************************************************\
+ *
+ * Lightweight Linked List - optimized for low code size
+ *
+\*********************************************************************************************/
+template
+class LList {
+public:
+ LList() : _head(nullptr) {}
+ ~LList() { reset(); }
+
+ // remove elements
+ void removeHead(void); // remove first element
+ void reset(void); // remove all elements
+ void remove(const T * val);
+
+ // read the list
+ inline bool isEmpty(void) const { return (_head == nullptr) ? true : false; }
+ size_t length(void) const;
+ inline T * head(void) { return _head ? &_head->_val : nullptr; }
+ inline const T * head(void) const { return _head ? &_head->_val : nullptr; }
+ const T * at(size_t index) const ;
+ // non-const variants
+ // not very academic cast but reduces code size
+ inline T * at(size_t index) { return (T*) ((const LList*)this)->at(index); }
+
+ // adding elements
+ T & addHead(void);
+ T & addHead(const T &val);
+ T & addToLast(void);
+
+ // iterator
+ // see https://stackoverflow.com/questions/8164567/how-to-make-my-custom-type-to-work-with-range-based-for-loops
+ class iterator {
+ public:
+ iterator(LList_elt *_cur): cur(_cur), next(nullptr) { if (cur) { next = cur->_next; } }
+ iterator operator++() { cur = next; if (cur) { next = cur->_next;} return *this; }
+ bool operator!=(const iterator & other) const { return cur != other.cur; }
+ T & operator*() const { return cur->_val; }
+ private:
+ LList_elt *cur;
+ LList_elt *next; // we need to keep next pointer in case the current attribute gets deleted
+ };
+ iterator begin() { return iterator(this->_head); } // start with 'head'
+ iterator end() { return iterator(nullptr); } // end with null pointer
+
+ // const iterator
+ class const_iterator {
+ public:
+ const_iterator(const LList_elt *_cur): cur(_cur), next(nullptr) { if (cur) { next = cur->_next; } }
+ const_iterator operator++() { cur = next; if (cur) { next = cur->_next;} return *this; }
+ bool operator!=(const_iterator & other) const { return cur != other.cur; }
+ const T & operator*() const { return cur->_val; }
+ private:
+ const LList_elt *cur;
+ const LList_elt *next; // we need to keep next pointer in case the current attribute gets deleted
+ };
+ const_iterator begin() const { return const_iterator(this->_head); } // start with 'head'
+ const_iterator end() const { return const_iterator(nullptr); } // end with null pointer
+
+protected:
+ LList_elt * _head;
+};
+
+template
+size_t LList::length(void) const {
+ size_t count = 0;
+ for (auto & elt : *this) {count++; }
+ return count;
+}
+
+
+template
+const T * LList::at(size_t index) const {
+ size_t count = 0;
+ for (const auto & elt : *this) {
+ if (index == count++) { return &elt; }
+ }
+ return nullptr;
+}
+
+template
+void LList::reset(void) {
+ while (_head) {
+ LList_elt * next = _head->next();
+ delete _head;
+ _head = next;
+ }
+}
+
+template
+void LList::removeHead(void) {
+ if (_head) {
+ LList_elt * next = _head->next();
+ delete _head;
+ _head = next;
+ }
+}
+
+template
+void LList::remove(const T * val) {
+ if (nullptr == val) { return; }
+ // find element in chain and find pointer before
+ LList_elt **curr_ptr = &_head;
+ while (*curr_ptr) {
+ LList_elt * curr_elt = *curr_ptr;
+ if ( &(curr_elt->_val) == val) {
+ *curr_ptr = curr_elt->_next; // update previous pointer to next element
+ delete curr_elt;
+ break; // stop iteration now
+ }
+ curr_ptr = &((*curr_ptr)->_next); // move to next element
+ }
+}
+
+template
+T & LList::addHead(void) {
+ LList_elt * elt = new LList_elt(); // create element
+ elt->next(_head); // insert at the head
+ _head = elt;
+ return elt->_val;
+}
+
+template
+T & LList::addHead(const T &val) {
+ LList_elt * elt = new LList_elt(); // create element
+ elt->next(_head); // insert at the head
+ elt->_val = val;
+ _head = elt;
+ return elt->_val;
+}
+
+template
+T & LList::addToLast(void) {
+ LList_elt **curr_ptr = &_head;
+ while (*curr_ptr) {
+ curr_ptr = &((*curr_ptr)->_next);
+ }
+ LList_elt * elt = new LList_elt(); // create element
+ *curr_ptr = elt;
+ return elt->_val;
+}
\ No newline at end of file
diff --git a/tasmota/support_static_buffer.ino b/tasmota/support_static_buffer.ino
index ea21b2805..81deb083b 100644
--- a/tasmota/support_static_buffer.ino
+++ b/tasmota/support_static_buffer.ino
@@ -237,3 +237,18 @@ public:
_buf = nullptr;
}
} PreAllocatedSBuffer;
+
+// nullptr accepted
+bool equalsSBuffer(const class SBuffer * buf1, const class SBuffer * buf2) {
+ if (buf1 == buf2) { return true; }
+ if (!buf1 && (buf2->len() == 0)) { return true; }
+ if (!buf2 && (buf1->len() == 0)) { return true; }
+ if (!buf1 || !buf2) { return false; } // at least one buf is not empty
+ // we know that both buf1 and buf2 are non-null
+ if (buf1->len() != buf2->len()) { return false; }
+ size_t len = buf1->len();
+ for (uint32_t i=0; iget8(i) != buf2->get8(i)) { return false; }
+ }
+ return true;
+}
\ No newline at end of file
diff --git a/tasmota/xdrv_23_zigbee_1z_libs.ino b/tasmota/xdrv_23_zigbee_1z_libs.ino
new file mode 100644
index 000000000..0590cff81
--- /dev/null
+++ b/tasmota/xdrv_23_zigbee_1z_libs.ino
@@ -0,0 +1,752 @@
+/*
+ xdrv_23_zigbee_1z_libs.ino - zigbee support for Tasmota, JSON replacement libs
+
+ Copyright (C) 2020 Theo Arends and Stephan Hadinger
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+*/
+
+#ifdef USE_ZIGBEE
+
+/*********************************************************************************************\
+ * Replacement libs for JSON to output a list of attributes
+\*********************************************************************************************/
+
+// simplified version of strcmp accepting both arguments to be in PMEM, and accepting nullptr arguments
+// inspired from https://code.woboq.org/userspace/glibc/string/strcmp.c.html
+int strcmp_PP(const char *p1, const char *p2) {
+ if (p1 == p2) { return 0; } // equality
+ if (!p1) { return -1; } // first string is null
+ if (!p2) { return 1; } // second string is null
+ const unsigned char *s1 = (const unsigned char *) p1;
+ const unsigned char *s2 = (const unsigned char *) p2;
+ unsigned char c1, c2;
+ do {
+ c1 = (unsigned char) pgm_read_byte(s1);
+ s1++;
+ c2 = (unsigned char) pgm_read_byte(s2);
+ s2++;
+ if (c1 == '\0')
+ return c1 - c2;
+ }
+ while (c1 == c2);
+ return c1 - c2;
+}
+
+/*********************************************************************************************\
+ *
+ * Variables for Rules from last Zigbee message received
+ *
+\*********************************************************************************************/
+
+typedef struct Z_LastMessageVars {
+ uint16_t device; // device short address
+ uint16_t groupaddr; // group address
+ uint16_t cluster; // cluster id
+ uint8_t endpoint; // source endpoint
+} Z_LastMessageVars;
+
+Z_LastMessageVars gZbLastMessage;
+
+uint16_t Z_GetLastDevice(void) { return gZbLastMessage.device; }
+uint16_t Z_GetLastGroup(void) { return gZbLastMessage.groupaddr; }
+uint16_t Z_GetLastCluster(void) { return gZbLastMessage.cluster; }
+uint8_t Z_GetLastEndpoint(void) { return gZbLastMessage.endpoint; }
+
+/*********************************************************************************************\
+ *
+ * Class for single attribute
+ *
+\*********************************************************************************************/
+
+enum class Za_type : uint8_t {
+ Za_none, // empty, translates into null in JSON
+ // numericals
+ Za_bool, // boolean, translates to true/false, uses uval32 to store
+ Za_uint, // unsigned 32 int, uses uval32
+ Za_int, // signed 32 int, uses ival32
+ Za_float, // float 32, uses fval
+ // non-nummericals
+ Za_raw, // bytes buffer, uses bval
+ Za_str // string, uses sval
+};
+
+class Z_attribute {
+public:
+
+ // attribute key, either cluster+attribute_id or plain name
+ union {
+ struct {
+ uint16_t cluster;
+ uint16_t attr_id;
+ } id;
+ char * key;
+ } key;
+ // attribute value
+ union {
+ uint32_t uval32;
+ int32_t ival32;
+ float fval;
+ SBuffer* bval;
+ char* sval;
+ } val;
+ Za_type type; // uint8_t in size, type of attribute, see above
+ bool key_is_str; // is the key a string?
+ bool key_is_pmem; // is the string in progmem, so we don't need to make a copy
+ bool val_str_raw; // if val is String, it is raw JSON and should not be escaped
+ uint8_t key_suffix; // append a suffix to key (default is 1, explicitly output if >1)
+
+ // Constructor with all defaults
+ Z_attribute():
+ key{ .id = { 0x0000, 0x0000 } },
+ val{ .uval32 = 0x0000 },
+ type(Za_type::Za_none),
+ key_is_str(false),
+ key_is_pmem(false),
+ val_str_raw(false),
+ key_suffix(1)
+ {};
+
+ Z_attribute(const Z_attribute & rhs) {
+ deepCopy(rhs);
+ }
+
+ Z_attribute & operator = (const Z_attribute & rhs) {
+ freeKey();
+ freeVal();
+ deepCopy(rhs);
+ }
+
+ // Destructor, free memory that was allocated
+ ~Z_attribute() {
+ freeKey();
+ freeVal();
+ }
+
+ // free any allocated memoruy for values
+ void freeVal(void) {
+ switch (type) {
+ case Za_type::Za_raw:
+ if (val.bval) { delete val.bval; val.bval = nullptr; }
+ break;
+ case Za_type::Za_str:
+ if (val.sval) { delete[] val.sval; val.sval = nullptr; }
+ break;
+ }
+ }
+ // free any allocated memoruy for keys
+ void freeKey(void) {
+ if (key_is_str && key.key && !key_is_pmem) { delete[] key.key; }
+ key.key = nullptr;
+ }
+
+ // set key name
+ void setKeyName(const char * _key, bool pmem = false) {
+ freeKey();
+ key_is_str = true;
+ key_is_pmem = pmem;
+ if (pmem) {
+ key.key = (char*) _key;
+ } else {
+ setKeyName(_key, nullptr);
+ }
+ }
+ // provide two entries and concat
+ void setKeyName(const char * _key, const char * _key2) {
+ freeKey();
+ key_is_str = true;
+ key_is_pmem = false;
+ if (_key) {
+ size_t key_len = strlen_P(_key);
+ if (_key2) {
+ key_len += strlen_P(_key2);
+ }
+ key.key = new char[key_len+1];
+ strcpy_P(key.key, _key);
+ if (_key2) {
+ strcat_P(key.key, _key2);
+ }
+ }
+ }
+
+ void setKeyId(uint16_t cluster, uint16_t attr_id) {
+ freeKey();
+ key_is_str = false;
+ key.id.cluster = cluster;
+ key.id.attr_id = attr_id;
+ }
+
+ // Setters
+ void setNone(void) {
+ freeVal(); // free any previously allocated memory
+ val.uval32 = 0;
+ type = Za_type::Za_none;
+ }
+ void setUInt(uint32_t _val) {
+ freeVal(); // free any previously allocated memory
+ val.uval32 = _val;
+ type = Za_type::Za_uint;
+ }
+ void setBool(bool _val) {
+ freeVal(); // free any previously allocated memory
+ val.uval32 = _val;
+ type = Za_type::Za_bool;
+ }
+ void setInt(int32_t _val) {
+ freeVal(); // free any previously allocated memory
+ val.ival32 = _val;
+ type = Za_type::Za_int;
+ }
+ void setFloat(float _val) {
+ freeVal(); // free any previously allocated memory
+ val.fval = _val;
+ type = Za_type::Za_float;
+ }
+
+ void setBuf(const SBuffer &buf, size_t index, size_t len) {
+ freeVal();
+ if (len) {
+ val.bval = new SBuffer(len);
+ val.bval->addBuffer(buf.buf(index), len);
+ }
+ type = Za_type::Za_raw;
+ }
+
+ // set the string value
+ // PMEM argument is allowed
+ // string will be copied, so it can be changed later
+ // nullptr is allowed and considered as empty string
+ // Note: memory is allocated only if string is non-empty
+ void setStr(const char * _val) {
+ freeVal(); // free any previously allocated memory
+ val_str_raw = false;
+ // val.sval is always nullptr after freeVal()
+ if (_val) {
+ size_t len = strlen_P(_val);
+ if (len) {
+ val.sval = new char[len+1];
+ strcpy_P(val.sval, _val);
+ }
+ }
+ type = Za_type::Za_str;
+ }
+ inline void setStrRaw(const char * _val) {
+ setStr(_val);
+ val_str_raw = true;
+ }
+
+ inline bool isNum(void) const { return (type >= Za_type::Za_bool) && (type <= Za_type::Za_float); }
+ // get num values
+ float getFloat(void) const {
+ switch (type) {
+ case Za_type::Za_bool:
+ case Za_type::Za_uint: return (float) val.uval32;
+ case Za_type::Za_int: return (float) val.ival32;
+ case Za_type::Za_float: return val.fval;
+ default: return 0.0f;
+ }
+ }
+
+ int32_t getInt(void) const {
+ switch (type) {
+ case Za_type::Za_bool:
+ case Za_type::Za_uint: return (int32_t) val.uval32;
+ case Za_type::Za_int: return val.ival32;
+ case Za_type::Za_float: return (int32_t) val.fval;
+ default: return 0;
+ }
+ }
+
+ uint32_t getUInt(void) const {
+ switch (type) {
+ case Za_type::Za_bool:
+ case Za_type::Za_uint: return val.uval32;
+ case Za_type::Za_int: return (uint32_t) val.ival32;
+ case Za_type::Za_float: return (uint32_t) val.fval;
+ default: return 0;
+ }
+ }
+
+ bool getBool(void) const {
+ switch (type) {
+ case Za_type::Za_bool:
+ case Za_type::Za_uint: return val.uval32 ? true : false;
+ case Za_type::Za_int: return val.ival32 ? true : false;
+ case Za_type::Za_float: return val.fval ? true : false;
+ default: return false;
+ }
+ }
+
+ const SBuffer * getRaw(void) const {
+ if (Za_type::Za_raw == type) { return val.bval; }
+ return nullptr;
+ }
+
+ // always return a point to a string, if not defined then empty string.
+ // Never returns nullptr
+ const char * getStr(void) const {
+ if (Za_type::Za_str == type) { return val.sval; }
+ return "";
+ }
+
+ bool equalsKey(const Z_attribute & attr2, bool ignore_key_suffix = false) const {
+ // check if keys are equal
+ if (key_is_str != attr2.key_is_str) { return false; }
+ if (key_is_str) {
+ if (strcmp_PP(key.key, attr2.key.key)) { return false; }
+ } else {
+ if ((key.id.cluster != attr2.key.id.cluster) ||
+ (key.id.attr_id != attr2.key.id.attr_id)) { return false; }
+ }
+ if (!ignore_key_suffix) {
+ if (key_suffix != attr2.key_suffix) { return false; }
+ }
+ return true;
+ }
+
+ bool equalsKey(uint16_t cluster, uint16_t attr_id, uint8_t suffix = 0) const {
+ if (!key_is_str) {
+ if ((key.id.cluster == cluster) && (key.id.attr_id == attr_id)) {
+ if (suffix) {
+ if (key_suffix == suffix) { return true; }
+ } else {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ bool equalsKey(const char * name, uint8_t suffix = 0) const {
+ if (key_is_str) {
+ if (0 == strcmp_PP(key.key, name)) {
+ if (suffix) {
+ if (key_suffix == suffix) { return true; }
+ } else {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ bool equalsVal(const Z_attribute & attr2) const {
+ if (type != attr2.type) { return false; }
+ if ((type >= Za_type::Za_bool) && (type <= Za_type::Za_float)) {
+ // numerical value
+ if (val.uval32 != attr2.val.uval32) { return false; }
+ } else if (type == Za_type::Za_raw) {
+ // compare 2 Static buffers
+ return equalsSBuffer(val.bval, attr2.val.bval);
+ } else if (type == Za_type::Za_str) {
+ // if (val_str_raw != attr2.val_str_raw) { return false; }
+ if (strcmp_PP(val.sval, attr2.val.sval)) { return false; }
+ }
+ return true;
+ }
+
+ bool equals(const Z_attribute & attr2) const {
+ return equalsKey(attr2) && equalsVal(attr2);
+ }
+
+ String toString(bool prefix_comma = false) const {
+ String res("");
+ if (prefix_comma) { res += ','; }
+ res += '"';
+ // compute the attribute name
+ if (key_is_str) {
+ if (key.key) { res += EscapeJSONString(key.key); }
+ else { res += F("null"); } // shouldn't happen
+ if (key_suffix > 1) {
+ res += key_suffix;
+ }
+ } else {
+ char attr_name[12];
+ snprintf_P(attr_name, sizeof(attr_name), PSTR("%04X/%04X"), key.id.cluster, key.id.attr_id);
+ res += attr_name;
+ if (key_suffix > 1) {
+ res += '+';
+ res += key_suffix;
+ }
+ }
+ res += F("\":");
+ // value part
+ switch (type) {
+ case Za_type::Za_none:
+ res += "null";
+ break;
+ case Za_type::Za_bool:
+ res += val.uval32 ? F("true") : F("false");
+ break;
+ case Za_type::Za_uint:
+ res += val.uval32;
+ break;
+ case Za_type::Za_int:
+ res += val.ival32;
+ break;
+ case Za_type::Za_float:
+ {
+ String fstr(val.fval, 2);
+ size_t last = fstr.length() - 1;
+ // remove trailing zeros
+ while (fstr[last] == '0') {
+ fstr.remove(last--);
+ }
+ // remove trailing dot
+ if (fstr[last] == '.') {
+ fstr.remove(last);
+ }
+ res += fstr;
+ }
+ break;
+ case Za_type::Za_raw:
+ res += '"';
+ if (val.bval) {
+ size_t blen = val.bval->len();
+ // print as HEX
+ char hex[2*blen+1];
+ ToHex_P(val.bval->getBuffer(), blen, hex, sizeof(hex));
+ res += hex;
+ }
+ res += '"';
+ break;
+ case Za_type::Za_str:
+ if (val_str_raw) {
+ if (val.sval) { res += val.sval; }
+ } else {
+ res += '"';
+ if (val.sval) {
+ res += EscapeJSONString(val.sval); // escape JSON chars
+ }
+ res += '"';
+ }
+ break;
+ }
+
+ return res;
+ }
+
+ // copy value from one attribute to another, without changing its type
+ void copyVal(const Z_attribute & rhs) {
+ freeVal();
+ // copy value
+ val.uval32 = 0x00000000;
+ type = rhs.type;
+ if (rhs.isNum()) {
+ val.uval32 = rhs.val.uval32;
+ } else if (rhs.type == Za_type::Za_raw) {
+ if (rhs.val.bval) {
+ val.bval = new SBuffer(rhs.val.bval->len());
+ val.bval->addBuffer(*(rhs.val.bval));
+ }
+ } else if (rhs.type == Za_type::Za_str) {
+ if (rhs.val.sval) {
+ size_t s_len = strlen_P(rhs.val.sval);
+ val.sval = new char[s_len+1];
+ strcpy_P(val.sval, rhs.val.sval);
+ }
+ }
+ val_str_raw = rhs.val_str_raw;
+ }
+
+protected:
+ void deepCopy(const Z_attribute & rhs) {
+ // copy key
+ if (!rhs.key_is_str) {
+ key.id.cluster = rhs.key.id.cluster;
+ key.id.attr_id = rhs.key.id.attr_id;
+ } else {
+ if (rhs.key_is_pmem) {
+ key.key = rhs.key.key; // PMEM, don't copy
+ } else {
+ key.key = nullptr;
+ if (rhs.key.key) {
+ size_t key_len = strlen_P(rhs.key.key);
+ if (key_len) {
+ key.key = new char[key_len+1];
+ strcpy_P(key.key, rhs.key.key);
+ }
+ }
+ }
+ }
+ key_is_str = rhs.key_is_str;
+ key_is_pmem = rhs.key_is_pmem;
+ key_suffix = rhs.key_suffix;
+ // copy value
+ copyVal(rhs);
+ // don't touch next pointer
+ }
+};
+
+/*********************************************************************************************\
+ *
+ * Class for attribute array of values
+ * This is a helper function to generate a clean list of unsigned ints
+ *
+\*********************************************************************************************/
+
+class Z_json_array {
+public:
+
+ Z_json_array(): val("[]") {} // start with empty array
+ void add(uint32_t uval32) {
+ // remove trailing ']'
+ val.remove(val.length()-1);
+ if (val.length() > 1) { // if not empty, prefix with comma
+ val += ',';
+ }
+ val += uval32;
+ val += ']';
+ }
+ String &toString(void) {
+ return val;
+ }
+
+private :
+ String val;
+};
+
+/*********************************************************************************************\
+ *
+ * Class for attribute ordered list
+ *
+\*********************************************************************************************/
+
+
+// Attribute list
+// Contains meta-information:
+// - source endpoint (is conflicting)
+// - LQI (if not conflicting)
+class Z_attribute_list : public LList {
+public:
+ uint8_t src_ep; // source endpoint, 0xFF if unknown
+ uint8_t lqi; // linkquality, 0xFF if unknown
+ uint16_t group_id; // group address OxFFFF if inknown
+
+ Z_attribute_list():
+ LList(), // call superclass constructor
+ src_ep(0xFF),
+ lqi(0xFF),
+ group_id(0xFFFF)
+ {};
+
+ // we don't define any destructor, the superclass destructor is automatically called
+
+ // reset object to its initial state
+ // free all allocated memory
+ void reset(void) {
+ LList::reset();
+ src_ep = 0xFF;
+ lqi = 0xFF;
+ group_id = 0xFFFF;
+ }
+
+ inline bool isValidSrcEp(void) const { return 0xFF != src_ep; }
+ inline bool isValidLQI(void) const { return 0xFF != lqi; }
+ inline bool isValidGroupId(void) const { return 0xFFFF != group_id; }
+
+ // the following addAttribute() compute the suffix and increments it
+ // Add attribute to the list, given cluster and attribute id
+ Z_attribute & addAttribute(uint16_t cluster, uint16_t attr_id, uint8_t suffix = 0);
+
+ // Add attribute to the list, given name
+ Z_attribute & addAttribute(const char * name, bool pmem = false, uint8_t suffix = 0);
+ Z_attribute & addAttribute(const char * name, const char * name2, uint8_t suffix = 0);
+ inline Z_attribute & addAttribute(const __FlashStringHelper * name, uint8_t suffix = 0) {
+ return addAttribute((const char*) name, true, suffix);
+ }
+
+ // Remove from list by reference, if null or not found, then do nothing
+ inline void removeAttribute(const Z_attribute * attr) { remove(attr); }
+
+ // dump the entire structure as JSON, starting from head (as parameter)
+ // does not start not end with a comma
+ // do we enclosed in brackets '{' '}'
+ String toString(bool enclose_brackets = false) const;
+
+ // find if attribute with same key already exists, return null if not found
+ const Z_attribute * findAttribute(uint16_t cluster, uint16_t attr_id, uint8_t suffix = 0) const;
+ const Z_attribute * findAttribute(const char * name, uint8_t suffix = 0) const;
+ const Z_attribute * findAttribute(const Z_attribute &attr) const; // suffis always count here
+ // non-const variants
+ inline Z_attribute * findAttribute(uint16_t cluster, uint16_t attr_id, uint8_t suffix = 0) {
+ return (Z_attribute*) ((const Z_attribute_list*)this)->findAttribute(cluster, attr_id, suffix);
+ }
+ inline Z_attribute * findAttribute(const char * name, uint8_t suffix = 0) {
+ return (Z_attribute*) (((const Z_attribute_list*)this)->findAttribute(name, suffix));
+ }
+ inline Z_attribute * findAttribute(const Z_attribute &attr) {
+ return (Z_attribute*) ((const Z_attribute_list*)this)->findAttribute(attr);
+ }
+
+ // count matching attributes, ignoring suffix
+ size_t countAttribute(uint16_t cluster, uint16_t attr_id) const ;
+ size_t countAttribute(const char * name) const ;
+
+ // if suffix == 0, we don't care and find the first match
+ Z_attribute & findOrCreateAttribute(uint16_t cluster, uint16_t attr_id, uint8_t suffix = 0);
+ Z_attribute & findOrCreateAttribute(const char * name, uint8_t suffix = 0);
+ // always care about suffix
+ Z_attribute & findOrCreateAttribute(const Z_attribute &attr);
+ // replace attribute with new value, suffix does care
+ Z_attribute & replaceOrCreate(const Z_attribute &attr);
+
+ // merge with secondary list, return true if ok, false if conflict
+ bool mergeList(const Z_attribute_list &list2);
+};
+
+// add a cluster/attr_id attribute at the end of the list
+Z_attribute & Z_attribute_list::addAttribute(uint16_t cluster, uint16_t attr_id, uint8_t suffix) {
+ Z_attribute & attr = addToLast();
+ attr.key.id.cluster = cluster;
+ attr.key.id.attr_id = attr_id;
+ attr.key_is_str = false;
+ if (!suffix) { attr.key_suffix = countAttribute(attr.key.id.cluster, attr.key.id.attr_id); }
+ else { attr.key_suffix = suffix; }
+ return attr;
+}
+
+// add a cluster/attr_id attribute at the end of the list
+Z_attribute & Z_attribute_list::addAttribute(const char * name, bool pmem, uint8_t suffix) {
+ Z_attribute & attr = addToLast();
+ attr.setKeyName(name, pmem);
+ if (!suffix) { attr.key_suffix = countAttribute(attr.key.key); }
+ else { attr.key_suffix = suffix; }
+ return attr;
+}
+
+Z_attribute & Z_attribute_list::addAttribute(const char * name, const char * name2, uint8_t suffix) {
+ Z_attribute & attr = addToLast();
+ attr.setKeyName(name, name2);
+ if (!suffix) { attr.key_suffix = countAttribute(attr.key.key); }
+ else { attr.key_suffix = suffix; }
+ return attr;
+}
+
+String Z_attribute_list::toString(bool enclose_brackets) const {
+ String res = "";
+ if (enclose_brackets) { res += '{'; }
+ bool prefix_comma = false;
+ for (const auto & attr : *this) {
+ res += attr.toString(prefix_comma);
+ prefix_comma = true;
+ }
+ // add source endpoint
+ if (0xFF != src_ep) {
+ if (prefix_comma) { res += ','; }
+ prefix_comma = true;
+ res += F("\"" D_CMND_ZIGBEE_ENDPOINT "\":");
+ res += src_ep;
+ }
+ // add group address
+ if (0xFFFF != group_id) {
+ if (prefix_comma) { res += ','; }
+ prefix_comma = true;
+ res += F("\"" D_CMND_ZIGBEE_GROUP "\":");
+ res += group_id;
+ }
+ // add lqi
+ if (0xFF != lqi) {
+ if (prefix_comma) { res += ','; }
+ prefix_comma = true;
+ res += F("\"" D_CMND_ZIGBEE_LINKQUALITY "\":");
+ res += lqi;
+ }
+ if (enclose_brackets) { res += '}'; }
+ // done
+ return res;
+}
+
+// suffis always count here
+const Z_attribute * Z_attribute_list::findAttribute(const Z_attribute &attr) const {
+ uint8_t suffix = attr.key_suffix;
+ if (attr.key_is_str) {
+ return findAttribute(attr.key.key, suffix);
+ } else {
+ return findAttribute(attr.key.id.cluster, attr.key.id.attr_id, suffix);
+ }
+}
+
+const Z_attribute * Z_attribute_list::findAttribute(uint16_t cluster, uint16_t attr_id, uint8_t suffix) const {
+ for (const auto & attr : *this) {
+ if (attr.equalsKey(cluster, attr_id, suffix)) { return &attr; }
+ }
+ return nullptr;
+}
+size_t Z_attribute_list::countAttribute(uint16_t cluster, uint16_t attr_id) const {
+ size_t count = 0;
+ for (const auto & attr : *this) {
+ if (attr.equalsKey(cluster, attr_id, 0)) { count++; }
+ }
+ return count;
+}
+
+// return the existing attribute or create a new one
+Z_attribute & Z_attribute_list::findOrCreateAttribute(uint16_t cluster, uint16_t attr_id, uint8_t suffix) {
+ Z_attribute * found = findAttribute(cluster, attr_id, suffix);
+ return found ? *found : addAttribute(cluster, attr_id, suffix);
+}
+
+const Z_attribute * Z_attribute_list::findAttribute(const char * name, uint8_t suffix) const {
+ for (const auto & attr : *this) {
+ if (attr.equalsKey(name, suffix)) { return &attr; }
+ }
+ return nullptr;
+}
+size_t Z_attribute_list::countAttribute(const char * name) const {
+ size_t count = 0;
+ for (const auto & attr : *this) {
+ if (attr.equalsKey(name, 0)) { count++; }
+ }
+ return count;
+}
+// return the existing attribute or create a new one
+Z_attribute & Z_attribute_list::findOrCreateAttribute(const char * name, uint8_t suffix) {
+ Z_attribute * found = findAttribute(name, suffix);
+ return found ? *found : addAttribute(name, suffix);
+}
+
+// same but passing a Z_attribute as key
+Z_attribute & Z_attribute_list::findOrCreateAttribute(const Z_attribute &attr) {
+ if (attr.key_is_str) {
+ return findOrCreateAttribute(attr.key.key, attr.key_suffix);
+ } else {
+ return findOrCreateAttribute(attr.key.id.cluster, attr.key.id.attr_id, attr.key_suffix);
+ }
+}
+// replace the entire content with new attribute or create
+Z_attribute & Z_attribute_list::replaceOrCreate(const Z_attribute &attr) {
+ Z_attribute &new_attr = findOrCreateAttribute(attr);
+ new_attr.copyVal(attr);
+ return new_attr;
+}
+
+
+bool Z_attribute_list::mergeList(const Z_attribute_list &attr_list) {
+ // Check source endpoint
+ if (0xFF == src_ep) {
+ src_ep = attr_list.src_ep;
+ } else if (0xFF != attr_list.src_ep) {
+ if (src_ep != attr_list.src_ep) { return false; }
+ }
+ if (0xFF != attr_list.lqi) {
+ lqi = attr_list.lqi;
+ }
+ for (auto & attr : attr_list) {
+ replaceOrCreate(attr);
+ }
+ return true;
+}
+
+#endif // USE_ZIGBEE
diff --git a/tasmota/xdrv_23_zigbee_2_devices.ino b/tasmota/xdrv_23_zigbee_2_devices.ino
index aa7ea9d2c..8ad36f861 100644
--- a/tasmota/xdrv_23_zigbee_2_devices.ino
+++ b/tasmota/xdrv_23_zigbee_2_devices.ino
@@ -19,8 +19,6 @@
#ifdef USE_ZIGBEE
-#include
-
#ifndef ZIGBEE_SAVE_DELAY_SECONDS
#define ZIGBEE_SAVE_DELAY_SECONDS 2 // wait for 2s before saving Zigbee info
#endif
@@ -29,25 +27,6 @@ const uint16_t kZigbeeSaveDelaySeconds = ZIGBEE_SAVE_DELAY_SECONDS; // wait f
/*********************************************************************************************\
* Structures for Rules variables related to the last received message
\*********************************************************************************************/
-
-typedef struct Z_LastMessageVars {
- uint16_t device; // device short address
- uint16_t groupaddr; // group address
- uint16_t cluster; // cluster id
- uint8_t endpoint; // source endpoint
-} Z_LastMessageVars;
-
-Z_LastMessageVars gZbLastMessage;
-
-uint16_t Z_GetLastDevice(void) { return gZbLastMessage.device; }
-uint16_t Z_GetLastGroup(void) { return gZbLastMessage.groupaddr; }
-uint16_t Z_GetLastCluster(void) { return gZbLastMessage.cluster; }
-uint8_t Z_GetLastEndpoint(void) { return gZbLastMessage.endpoint; }
-
-/*********************************************************************************************\
- * Structures for device configuration
-\*********************************************************************************************/
-
const size_t endpoints_max = 8; // we limit to 8 endpoints
class Z_Device {
@@ -58,9 +37,8 @@ public:
char * modelId;
char * friendlyName;
uint8_t endpoints[endpoints_max]; // static array to limit memory consumption, list of endpoints until 0x00 or end of array
- // json buffer used for attribute reporting
- DynamicJsonBuffer *json_buffer;
- JsonObject *json;
+ // Used for attribute reporting
+ Z_attribute_list attr_list;
// sequence number for Zigbee frames
uint16_t shortaddr; // unique key if not null, or unspecified if null
uint8_t seqNumber;
@@ -95,14 +73,13 @@ public:
int16_t mains_power; // Active power
// Constructor with all defaults
- Z_Device(uint16_t _shortaddr, uint64_t _longaddr = 0x00):
+ Z_Device(uint16_t _shortaddr = BAD_SHORTADDR, uint64_t _longaddr = 0x00):
longaddr(_longaddr),
manufacturerId(nullptr),
modelId(nullptr),
friendlyName(nullptr),
endpoints{ 0, 0, 0, 0, 0, 0, 0, 0 },
- json_buffer(nullptr),
- json(nullptr),
+ attr_list(),
shortaddr(_shortaddr),
seqNumber(0),
// Hue support
@@ -207,7 +184,7 @@ typedef struct Z_Deferred {
// - shortaddr and longaddr cannot be both null
class Z_Devices {
public:
- Z_Devices() {};
+ Z_Devices() : _deferred() {};
// Probe the existence of device keys
// Results:
@@ -218,12 +195,17 @@ public:
uint16_t isKnownIndex(uint32_t index) const;
uint16_t isKnownFriendlyName(const char * name) const;
+ Z_Device & findShortAddr(uint16_t shortaddr);
const Z_Device & findShortAddr(uint16_t shortaddr) const;
+ Z_Device & findLongAddr(uint64_t longaddr);
+ const Z_Device & findLongAddr(uint64_t longaddr) const;
Z_Device & getShortAddr(uint16_t shortaddr); // find Device from shortAddr, creates it if does not exist
- const Z_Device & getShortAddrConst(uint16_t shortaddr) const ; // 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);
+ }
- int32_t findLongAddr(uint64_t longaddr) const;
int32_t findFriendlyName(const char * name) const;
uint64_t getDeviceLongAddr(uint16_t shortaddr) const;
@@ -281,19 +263,22 @@ public:
void runTimer(void);
// Append or clear attributes Json structure
- void jsonClear(uint16_t shortaddr);
- void jsonAppend(uint16_t shortaddr, const JsonObject &values);
- const JsonObject *jsonGet(uint16_t shortaddr);
+ void jsonAppend(uint16_t shortaddr, const Z_attribute_list &attr_list);
void jsonPublishFlush(uint16_t shortaddr); // publish the json message and clear buffer
- bool jsonIsConflict(uint16_t shortaddr, const JsonObject &values);
- void jsonPublishNow(uint16_t shortaddr, JsonObject &values);
+ bool jsonIsConflict(uint16_t shortaddr, const Z_attribute_list &attr_list) const;
+ void jsonPublishNow(uint16_t shortaddr, Z_attribute_list &attr_list);
// Iterator
size_t devicesSize(void) const {
- return _devices.size();
+ return _devices.length();
}
- const Z_Device &devicesAt(size_t i) const {
- return *(_devices.at(i));
+ const Z_Device & devicesAt(size_t i) const {
+ const Z_Device * devp = _devices.at(i);
+ if (devp) {
+ return *devp;
+ } else {
+ return device_unk;
+ }
}
// Remove device from list
@@ -308,8 +293,8 @@ public:
uint16_t parseDeviceParam(const char * param, bool short_must_be_known = false) const;
private:
- std::vector _devices = {};
- std::vector _deferred = {}; // list of deferred calls
+ LList _devices; // list of devices
+ LList _deferred; // list of deferred calls
uint32_t _saveTimer = 0;
uint8_t _seqNumber = 0; // global seqNumber if device is unknown
@@ -317,10 +302,7 @@ private:
// Any find() function will not return Null, instead it will return this instance
const Z_Device device_unk = Z_Device(BAD_SHORTADDR);
- template < typename T>
- static bool findInVector(const std::vector & vecOfElements, const T & element);
-
- int32_t findShortAddrIdx(uint16_t shortaddr) const;
+ //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);
void freeDeviceEntry(Z_Device *device);
@@ -343,31 +325,16 @@ uint16_t localShortAddr = 0;
* Implementation
\*********************************************************************************************/
-// https://thispointer.com/c-how-to-find-an-element-in-vector-and-get-its-index/
-template < typename T>
-bool Z_Devices::findInVector(const std::vector & vecOfElements, const T & element) {
- // Find given element in vector
- auto it = std::find(vecOfElements.begin(), vecOfElements.end(), element);
-
- if (it != vecOfElements.end()) {
- return true;
- } else {
- return false;
- }
-}
-
//
// 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
- Z_Device * device_alloc = new Z_Device(shortaddr, longaddr);
+ Z_Device device(shortaddr, longaddr);
- device_alloc->json_buffer = new DynamicJsonBuffer(16);
- _devices.push_back(device_alloc);
dirty();
- return *(_devices.back());
+ return _devices.addHead(device);
}
void Z_Devices::freeDeviceEntry(Z_Device *device) {
@@ -383,20 +350,17 @@ void Z_Devices::freeDeviceEntry(Z_Device *device) {
// In:
// shortaddr (not BAD_SHORTADDR)
// Out:
-// index in _devices of entry, -1 if not found
-//
-int32_t Z_Devices::findShortAddrIdx(uint16_t shortaddr) const {
- if (BAD_SHORTADDR == shortaddr) { return -1; } // does not make sense to look for BAD_SHORTADDR shortaddr (broadcast)
- int32_t found = 0;
- for (auto &elem : _devices) {
- if (elem->shortaddr == shortaddr) { return found; }
- found++;
+// reference to device, or to device_unk if not found
+// (use foundDevice() to check if found)
+Z_Device & Z_Devices::findShortAddr(uint16_t shortaddr) {
+ for (auto & elem : _devices) {
+ if (elem.shortaddr == shortaddr) { return elem; }
}
- return -1;
+ return (Z_Device&) device_unk;
}
const Z_Device & Z_Devices::findShortAddr(uint16_t shortaddr) const {
- for (auto &elem : _devices) {
- if (elem->shortaddr == shortaddr) { return *elem; }
+ for (const auto & elem : _devices) {
+ if (elem.shortaddr == shortaddr) { return elem; }
}
return device_unk;
}
@@ -408,14 +372,19 @@ const Z_Device & Z_Devices::findShortAddr(uint16_t shortaddr) const {
// Out:
// index in _devices of entry, -1 if not found
//
-int32_t Z_Devices::findLongAddr(uint64_t longaddr) const {
- if (!longaddr) { return -1; }
- int32_t found = 0;
+Z_Device & Z_Devices::findLongAddr(uint64_t longaddr) {
+ if (!longaddr) { return (Z_Device&) device_unk; }
for (auto &elem : _devices) {
- if (elem->longaddr == longaddr) { return found; }
- found++;
+ if (elem.longaddr == longaddr) { return elem; }
}
- return -1;
+ return (Z_Device&) device_unk;
+}
+const Z_Device & Z_Devices::findLongAddr(uint64_t longaddr) const {
+ if (!longaddr) { return device_unk; }
+ for (const auto &elem : _devices) {
+ if (elem.longaddr == longaddr) { return elem; }
+ }
+ return device_unk;
}
//
// Scan all devices to find a corresponding friendlyNme
@@ -431,8 +400,8 @@ int32_t Z_Devices::findFriendlyName(const char * name) const {
int32_t found = 0;
if (name_len) {
for (auto &elem : _devices) {
- if (elem->friendlyName) {
- if (strcasecmp(elem->friendlyName, name) == 0) { return found; }
+ if (elem.friendlyName) {
+ if (strcasecmp(elem.friendlyName, name) == 0) { return found; }
}
found++;
}
@@ -441,9 +410,8 @@ int32_t Z_Devices::findFriendlyName(const char * name) const {
}
uint16_t Z_Devices::isKnownLongAddr(uint64_t longaddr) const {
- int32_t found = findLongAddr(longaddr);
- if (found >= 0) {
- const Z_Device & device = devicesAt(found);
+ const Z_Device & device = findLongAddr(longaddr);
+ if (foundDevice(device)) {
return device.shortaddr; // can be zero, if not yet registered
} else {
return BAD_SHORTADDR;
@@ -471,7 +439,7 @@ uint16_t Z_Devices::isKnownFriendlyName(const char * name) const {
}
uint64_t Z_Devices::getDeviceLongAddr(uint16_t shortaddr) const {
- return getShortAddrConst(shortaddr).longaddr; // if unknown, it reverts to the Unknown device and longaddr is 0x00
+ return findShortAddr(shortaddr).longaddr; // if unknown, it reverts to the Unknown device and longaddr is 0x00
}
//
@@ -479,38 +447,28 @@ uint64_t Z_Devices::getDeviceLongAddr(uint16_t shortaddr) const {
//
Z_Device & Z_Devices::getShortAddr(uint16_t shortaddr) {
if (BAD_SHORTADDR == shortaddr) { return (Z_Device&) device_unk; } // this is not legal
- int32_t found = findShortAddrIdx(shortaddr);
- if (found >= 0) {
- return *(_devices[found]);
+ Z_Device & device = findShortAddr(shortaddr);
+ if (foundDevice(device)) {
+ return device;
}
- //Serial.printf("Device entry created for shortaddr = 0x%02X, found = %d\n", shortaddr, found);
return createDeviceEntry(shortaddr, 0);
}
-// Same version but Const
-const Z_Device & Z_Devices::getShortAddrConst(uint16_t shortaddr) const {
- int32_t found = findShortAddrIdx(shortaddr);
- if (found >= 0) {
- return *(_devices[found]);
- }
- return device_unk;
-}
// 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; }
- int32_t found = findLongAddr(longaddr);
- if (found > 0) {
- return *(_devices[found]);
+ Z_Device & device = findLongAddr(longaddr);
+ if (foundDevice(device)) {
+ return device;
}
return createDeviceEntry(0, longaddr);
}
// Remove device from list, return true if it was known, false if it was not recorded
bool Z_Devices::removeDevice(uint16_t shortaddr) {
- int32_t found = findShortAddrIdx(shortaddr);
- if (found >= 0) {
- freeDeviceEntry(_devices.at(found));
- _devices.erase(_devices.begin() + found);
+ Z_Device & device = findShortAddr(shortaddr);
+ if (foundDevice(device)) {
+ _devices.remove(&device);
dirty();
return true;
}
@@ -523,27 +481,27 @@ bool Z_Devices::removeDevice(uint16_t shortaddr) {
// shortaddr
// longaddr (both can't be null at the same time)
void Z_Devices::updateDevice(uint16_t shortaddr, uint64_t longaddr) {
- int32_t s_found = findShortAddrIdx(shortaddr); // is there already a shortaddr entry
- int32_t l_found = findLongAddr(longaddr); // is there already a longaddr entry
+ Z_Device * s_found = &findShortAddr(shortaddr); // is there already a shortaddr entry
+ Z_Device * l_found = &findLongAddr(longaddr); // is there already a longaddr entry
- if ((s_found >= 0) && (l_found >= 0)) { // both shortaddr and longaddr are already registered
+ if (foundDevice(*s_found) && foundDevice(*l_found)) { // both shortaddr and longaddr are already registered
if (s_found == l_found) {
} else { // they don't match
// the device with longaddr got a new shortaddr
- _devices[l_found]->shortaddr = shortaddr; // update the shortaddr corresponding to the longaddr
+ l_found->shortaddr = shortaddr; // update the shortaddr corresponding to the longaddr
// erase the previous shortaddr
- freeDeviceEntry(_devices.at(s_found));
- _devices.erase(_devices.begin() + s_found);
+ freeDeviceEntry(s_found);
+ _devices.remove(s_found);
dirty();
}
- } else if (s_found >= 0) {
+ } else if (foundDevice(*s_found)) {
// shortaddr already exists but longaddr not
// add the longaddr to the entry
- _devices[s_found]->longaddr = longaddr;
+ s_found->longaddr = longaddr;
dirty();
- } else if (l_found >= 0) {
+ } else if (foundDevice(*l_found)) {
// longaddr entry exists, update shortaddr
- _devices[l_found]->shortaddr = shortaddr;
+ l_found->shortaddr = shortaddr;
dirty();
} else {
// neither short/lonf addr are found.
@@ -588,9 +546,8 @@ void Z_Devices::addEndpoint(uint16_t shortaddr, uint8_t endpoint) {
//
uint32_t Z_Devices::countEndpoints(uint16_t shortaddr) const {
uint32_t count_ep = 0;
- int32_t found = findShortAddrIdx(shortaddr);
- if (found < 0) return 0; // avoid creating an entry if the device was never seen
- const Z_Device &device = devicesAt(found);
+ const Z_Device & device =findShortAddr(shortaddr);
+ if (!foundDevice(device)) return 0;
for (uint32_t i = 0; i < endpoints_max; i++) {
if (0 != device.endpoints[i]) {
@@ -666,9 +623,8 @@ void Z_Devices::setBatteryPercent(uint16_t shortaddr, uint8_t bp) {
// get the next sequance number for the device, or use the global seq number if device is unknown
uint8_t Z_Devices::getNextSeqNumber(uint16_t shortaddr) {
- int32_t short_found = findShortAddrIdx(shortaddr);
- if (short_found >= 0) {
- Z_Device &device = getShortAddr(shortaddr);
+ Z_Device & device = findShortAddr(shortaddr);
+ if (foundDevice(device)) {
device.seqNumber += 1;
return device.seqNumber;
} else {
@@ -754,9 +710,9 @@ void Z_Devices::hideHueBulb(uint16_t shortaddr, bool hidden) {
}
// true if device is not knwon or not a bulb - it wouldn't make sense to publish a non-bulb
bool Z_Devices::isHueBulbHidden(uint16_t shortaddr) const {
- int32_t found = findShortAddrIdx(shortaddr);
- if (found >= 0) {
- uint8_t zb_profile = _devices[found]->zb_profile;
+ const Z_Device & device = findShortAddr(shortaddr);
+ if (foundDevice(device)) {
+ uint8_t zb_profile = device.zb_profile;
if (0x00 == (zb_profile & 0xF0)) {
// bulb type
return (zb_profile & 0x08) ? true : false;
@@ -769,13 +725,10 @@ bool Z_Devices::isHueBulbHidden(uint16_t shortaddr) const {
// Parse for a specific category, of all deferred for a device if category == 0xFF
void Z_Devices::resetTimersForDevice(uint16_t shortaddr, uint16_t groupaddr, uint8_t category) {
// iterate the list of deferred, and remove any linked to the shortaddr
- for (auto it = _deferred.begin(); it != _deferred.end(); it++) {
- // Notice that the iterator is decremented after it is passed
- // to erase() but before erase() is executed
- // see https://www.techiedelight.com/remove-elements-vector-inside-loop-cpp/
- if ((it->shortaddr == shortaddr) && (it->groupaddr == groupaddr)) {
- if ((0xFF == category) || (it->category == category)) {
- _deferred.erase(it--);
+ for (auto & defer : _deferred) {
+ if ((defer.shortaddr == shortaddr) && (defer.groupaddr == groupaddr)) {
+ if ((0xFF == category) || (defer.category == category)) {
+ _deferred.remove(&defer);
}
}
}
@@ -789,7 +742,8 @@ void Z_Devices::setTimer(uint16_t shortaddr, uint16_t groupaddr, uint32_t wait_m
}
// Now create the new timer
- Z_Deferred deferred = { wait_ms + millis(), // timer
+ Z_Deferred & deferred = _deferred.addHead();
+ deferred = { wait_ms + millis(), // timer
shortaddr,
groupaddr,
cluster,
@@ -797,20 +751,17 @@ void Z_Devices::setTimer(uint16_t shortaddr, uint16_t groupaddr, uint32_t wait_m
category,
value,
func };
- _deferred.push_back(deferred);
}
// Run timer at each tick
// WARNING: don't set a new timer within a running timer, this causes memory corruption
void Z_Devices::runTimer(void) {
// visit all timers
- for (auto it = _deferred.begin(); it != _deferred.end(); it++) {
- Z_Deferred &defer = *it;
-
+ for (auto & defer : _deferred) {
uint32_t timer = defer.timer;
if (TimeReached(timer)) {
(*defer.func)(defer.shortaddr, defer.groupaddr, defer.cluster, defer.endpoint, defer.value);
- _deferred.erase(it--); // remove from list
+ _deferred.remove(&defer);
}
}
@@ -821,173 +772,100 @@ void Z_Devices::runTimer(void) {
}
}
-// Clear the JSON buffer for coalesced and deferred attributes
-void Z_Devices::jsonClear(uint16_t shortaddr) {
- Z_Device & device = getShortAddr(shortaddr);
- device.json = nullptr;
- device.json_buffer->clear();
-}
-
-// Copy JSON from one object to another, this helps preserving the order of attributes
-void CopyJsonVariant(JsonObject &to, const String &key, const JsonVariant &val) {
- // first remove the potentially existing key in the target JSON, so new adds will be at the end of the list
- to.remove(key); // force remove to have metadata like LinkQuality at the end
-
- if (val.is()) {
- const char * sval = val.as(); // using char* forces a copy, and also captures 'null' values
- to.set(key, (char*) sval);
- } else if (val.is()) {
- JsonArray &nested_arr = to.createNestedArray(key);
- CopyJsonArray(nested_arr, val.as()); // deep copy
- } else if (val.is()) {
- JsonObject &nested_obj = to.createNestedObject(key);
- CopyJsonObject(nested_obj, val.as()); // deep copy
- } else {
- to.set(key, val); // general case for non array, object or string
- }
-}
-
-// Shallow copy of array, we skip any sub-array or sub-object. It may be added in the future
-void CopyJsonArray(JsonArray &to, const JsonArray &arr) {
- for (auto v : arr) {
- if (v.is()) {
- String sval = v.as(); // force a copy of the String value
- to.add(sval);
- } else if (v.is()) {
- } else if (v.is()) {
- } else {
- to.add(v);
- }
- }
-}
-
-// Deep copy of object
-void CopyJsonObject(JsonObject &to, const JsonObject &from) {
- for (auto kv : from) {
- String key_string = kv.key;
- JsonVariant &val = kv.value;
-
- CopyJsonVariant(to, key_string, val);
- }
-}
-
// does the new payload conflicts with the existing payload, i.e. values would be overwritten
// true - one attribute (except LinkQuality) woudl be lost, there is conflict
// false - new attributes can be safely added
-bool Z_Devices::jsonIsConflict(uint16_t shortaddr, const JsonObject &values) {
- Z_Device & device = getShortAddr(shortaddr);
- if (&values == nullptr) { return false; }
+bool Z_Devices::jsonIsConflict(uint16_t shortaddr, const Z_attribute_list &attr_list) const {
+ const Z_Device & device = findShortAddr(shortaddr);
- if (nullptr == device.json) {
+ if (!foundDevice(device)) { return false; }
+ if (attr_list.isEmpty()) {
return false; // if no previous value, no conflict
}
// compare groups
- // Special case for group addresses. Group attribute is only present if the target
- // address is a group address, so just comparing attributes will not work.
- // Eg: if the first packet has no group attribute, and the second does, conflict would not be detected
- // Here we explicitly compute the group address of both messages, and compare them. No group means group=0x0000
- // (we use the property of an missing attribute returning 0)
- // (note: we use .get() here which is case-sensitive. We know however that the attribute was set with the exact syntax D_CMND_ZIGBEE_GROUP, so we don't need a case-insensitive get())
- uint16_t group1 = device.json->get(D_CMND_ZIGBEE_GROUP);
- uint16_t group2 = values.get(D_CMND_ZIGBEE_GROUP);
- if (group1 != group2) {
- return true; // if group addresses differ, then conflict
+ if (device.attr_list.isValidGroupId() && attr_list.isValidGroupId()) {
+ if (device.attr_list.group_id != attr_list.group_id) { return true; } // groups are in conflict
}
- // parse all other parameters
- for (auto kv : values) {
- String key_string = kv.key;
+ // compare src_ep
+ if (device.attr_list.isValidSrcEp() && attr_list.isValidSrcEp()) {
+ if (device.attr_list.src_ep != attr_list.src_ep) { return true; }
+ }
+
+ // LQI does not count as conflicting
- if (0 == strcasecmp_P(kv.key, PSTR(D_CMND_ZIGBEE_GROUP))) {
- // ignore group, it was handled already
- } else if (0 == strcasecmp_P(kv.key, PSTR(D_CMND_ZIGBEE_ENDPOINT))) {
- // attribute "Endpoint" or "Group"
- if (device.json->containsKey(kv.key)) {
- if (kv.value.as() != device.json->get(kv.key)) {
- return true;
- }
- }
- } else if (strcasecmp_P(kv.key, PSTR(D_CMND_ZIGBEE_LINKQUALITY))) { // exception = ignore duplicates for LinkQuality
- if (device.json->containsKey(kv.key)) {
- return true; // conflict!
+ // parse all other parameters
+ for (const auto & attr : attr_list) {
+ const Z_attribute * curr_attr = device.attr_list.findAttribute(attr);
+ if (nullptr != curr_attr) {
+ if (!curr_attr->equalsVal(attr)) {
+ return true; // the value already exists and is different - conflict!
}
}
}
return false;
}
-void Z_Devices::jsonAppend(uint16_t shortaddr, const JsonObject &values) {
+void Z_Devices::jsonAppend(uint16_t shortaddr, const Z_attribute_list &attr_list) {
Z_Device & device = getShortAddr(shortaddr);
- if (&values == nullptr) { return; }
-
- if (nullptr == device.json) {
- device.json = &(device.json_buffer->createObject());
- }
- // Prepend Device, will be removed later if redundant
- char sa[8];
- snprintf_P(sa, sizeof(sa), PSTR("0x%04X"), shortaddr);
- device.json->set(F(D_JSON_ZIGBEE_DEVICE), sa);
- // Prepend Friendly Name if it has one
- const char * fname = zigbee_devices.getFriendlyName(shortaddr);
- if (fname) {
- device.json->set(F(D_JSON_ZIGBEE_NAME), (char*) fname); // (char*) forces ArduinoJson to make a copy of the cstring
- }
-
- // copy all values from 'values' to 'json'
- CopyJsonObject(*device.json, values);
-}
-
-const JsonObject *Z_Devices::jsonGet(uint16_t shortaddr) {
- return getShortAddr(shortaddr).json;
+ device.attr_list.mergeList(attr_list);
}
void Z_Devices::jsonPublishFlush(uint16_t shortaddr) {
Z_Device & device = getShortAddr(shortaddr);
if (!device.valid()) { return; } // safeguard
- JsonObject & json = *device.json;
- if (&json == nullptr) { return; } // abort if nothing in buffer
+ Z_attribute_list &attr_list = device.attr_list;
- const char * fname = zigbee_devices.getFriendlyName(shortaddr);
- bool use_fname = (Settings.flag4.zigbee_use_names) && (fname); // should we replace shortaddr with friendlyname?
+ if (!attr_list.isEmpty()) {
+ const char * fname = zigbee_devices.getFriendlyName(shortaddr);
+ bool use_fname = (Settings.flag4.zigbee_use_names) && (fname); // should we replace shortaddr with friendlyname?
- // save parameters is global variables to be used by Rules
- gZbLastMessage.device = shortaddr; // %zbdevice%
- gZbLastMessage.groupaddr = json[F(D_CMND_ZIGBEE_GROUP)]; // %zbgroup%
- gZbLastMessage.cluster = json[F(D_CMND_ZIGBEE_CLUSTER)]; // %zbcluster%
- gZbLastMessage.endpoint = json[F(D_CMND_ZIGBEE_ENDPOINT)]; // %zbendpoint%
+ // save parameters is global variables to be used by Rules
+ gZbLastMessage.device = shortaddr; // %zbdevice%
+ gZbLastMessage.groupaddr = attr_list.group_id; // %zbgroup%
+ gZbLastMessage.endpoint = attr_list.src_ep; // %zbendpoint%
- // dump json in string
- String msg = "";
- json.printTo(msg);
- zigbee_devices.jsonClear(shortaddr);
-
- if (use_fname) {
- if (Settings.flag4.remove_zbreceived) {
- Response_P(PSTR("{\"%s\":%s}"), fname, msg.c_str());
- } else {
- Response_P(PSTR("{\"" D_JSON_ZIGBEE_RECEIVED "\":{\"%s\":%s}}"), fname, msg.c_str());
+ mqtt_data[0] = 0; // clear string
+ // Do we prefix with `ZbReceived`?
+ if (!Settings.flag4.remove_zbreceived) {
+ Response_P(PSTR("{\"" D_JSON_ZIGBEE_RECEIVED "\":"));
}
- } else {
- if (Settings.flag4.remove_zbreceived) {
- Response_P(PSTR("{\"0x%04X\":%s}"), shortaddr, msg.c_str());
+ // What key do we use, shortaddr or name?
+ if (use_fname) {
+ Response_P(PSTR("%s{\"%s\":{"), mqtt_data, fname);
} else {
- Response_P(PSTR("{\"" D_JSON_ZIGBEE_RECEIVED "\":{\"0x%04X\":%s}}"), shortaddr, msg.c_str());
+ Response_P(PSTR("%s{\"0x%04X\":{"), mqtt_data, shortaddr);
}
+ // Add "Device":"0x...."
+ Response_P(PSTR("%s\"" D_JSON_ZIGBEE_DEVICE "\":\"0x%04X\","), mqtt_data, shortaddr);
+ // Add "Name":"xxx" if name is present
+ if (fname) {
+ Response_P(PSTR("%s\"" D_JSON_ZIGBEE_NAME "\":\"%s\","), mqtt_data, EscapeJSONString(fname).c_str());
+ }
+ // Add all other attributes
+ Response_P(PSTR("%s%s}}"), mqtt_data, attr_list.toString().c_str());
+
+ if (!Settings.flag4.remove_zbreceived) {
+ Response_P(PSTR("%s}"), mqtt_data);
+ }
+ // AddLog_P2(LOG_LEVEL_INFO, PSTR(">>> %s"), mqtt_data); // TODO
+ attr_list.reset(); // clear the attributes
+
+ if (Settings.flag4.zigbee_distinct_topics) {
+ char subtopic[16];
+ snprintf_P(subtopic, sizeof(subtopic), PSTR("%04X/" D_RSLT_SENSOR), shortaddr);
+ MqttPublishPrefixTopic_P(TELE, subtopic, Settings.flag.mqtt_sensor_retain);
+ } else {
+ MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR), Settings.flag.mqtt_sensor_retain);
+ }
+ XdrvRulesProcess(); // apply rules
}
- if (Settings.flag4.zigbee_distinct_topics) {
- char subtopic[16];
- snprintf_P(subtopic, sizeof(subtopic), PSTR("%04X/" D_RSLT_SENSOR), shortaddr);
- MqttPublishPrefixTopic_P(TELE, subtopic, Settings.flag.mqtt_sensor_retain);
- } else {
- MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR), Settings.flag.mqtt_sensor_retain);
- }
- XdrvRulesProcess(); // apply rules
}
-void Z_Devices::jsonPublishNow(uint16_t shortaddr, JsonObject & values) {
+void Z_Devices::jsonPublishNow(uint16_t shortaddr, Z_attribute_list &attr_list) {
jsonPublishFlush(shortaddr); // flush any previous buffer
- jsonAppend(shortaddr, values);
+ jsonAppend(shortaddr, attr_list);
jsonPublishFlush(shortaddr); // publish now
}
@@ -1044,9 +922,8 @@ String Z_Devices::dumpLightState(uint16_t shortaddr) const {
JsonObject& json = jsonBuffer.createObject();
char hex[8];
- int32_t found = findShortAddrIdx(shortaddr);
- if (found >= 0) {
- const Z_Device & device = devicesAt(found);
+ const Z_Device & device = findShortAddr(shortaddr);
+ if (foundDevice(device)) {
const char * fname = getFriendlyName(shortaddr);
bool use_fname = (Settings.flag4.zigbee_use_names) && (fname); // should we replace shortaddr with friendlyname?
@@ -1090,8 +967,7 @@ String Z_Devices::dump(uint32_t dump_mode, uint16_t status_shortaddr) const {
JsonArray& json = jsonBuffer.createArray();
JsonArray& devices = json;
- for (std::vector::const_iterator it = _devices.begin(); it != _devices.end(); ++it) {
- const Z_Device &device = **it;
+ for (const auto & device : _devices) {
uint16_t shortaddr = device.shortaddr;
char hex[22];
diff --git a/tasmota/xdrv_23_zigbee_5_converters.ino b/tasmota/xdrv_23_zigbee_5_converters.ino
index 5f7c0786b..5655244d7 100644
--- a/tasmota/xdrv_23_zigbee_5_converters.ino
+++ b/tasmota/xdrv_23_zigbee_5_converters.ino
@@ -90,18 +90,13 @@ bool Z_isDiscreteDataType(uint8_t t) {
}
}
-// return value:
-// 0 = keep initial value
-// 1 = remove initial value
-typedef int32_t (*Z_AttrConverter)(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const String &new_name, uint16_t cluster, uint16_t attr);
typedef struct Z_AttributeConverter {
uint8_t type;
uint8_t cluster_short;
uint16_t attribute;
uint16_t name_offset;
int8_t multiplier; // multiplier for numerical value, (if > 0 multiply by x, if <0 device by x)
- uint8_t cb; // callback func from Z_ConvOperators
- // Z_AttrConverter func;
+ // still room for a byte
} Z_AttributeConverter;
// Cluster numbers are store in 8 bits format to save space,
@@ -129,400 +124,391 @@ uint16_t CxToCluster(uint8_t cx) {
}
return 0xFFFF;
}
-
-enum Z_ConvOperators {
- Z_Nop, // copy value
- Z_AddPressureUnit, // add pressure unit attribute (non numerical)
- Z_ManufKeep, // copy and record Manufacturer attribute
- Z_ModelKeep, // copy and record ModelId attribute
- Z_AqaraSensor, // decode prioprietary Aqara Sensor message
- Z_AqaraSensor2, // decode prioprietary Aqara Sensor message V2
- Z_AqaraVibration, // decode Aqara vibration modes
- Z_AqaraCube, // decode Aqara cube
- Z_AqaraButton, // decode Aqara button
- Z_BatteryPercentage, // memorize Battery Percentage in RAM
-};
-
// list of post-processing directives
const Z_AttributeConverter Z_PostProcess[] PROGMEM = {
- { Zuint8, Cx0000, 0x0000, Z_(ZCLVersion), 1, Z_Nop },
- { Zuint8, Cx0000, 0x0001, Z_(AppVersion), 1, Z_Nop },
- { Zuint8, Cx0000, 0x0002, Z_(StackVersion), 1, Z_Nop },
- { Zuint8, Cx0000, 0x0003, Z_(HWVersion), 1, Z_Nop },
- { Zstring, Cx0000, 0x0004, Z_(Manufacturer), 1, Z_ManufKeep }, // record Manufacturer
- { Zstring, Cx0000, 0x0005, Z_(ModelId), 1, Z_ModelKeep }, // record Model
- { Zstring, Cx0000, 0x0006, Z_(DateCode), 1, Z_Nop },
- { Zenum8, Cx0000, 0x0007, Z_(PowerSource), 1, Z_Nop },
- { Zenum8, Cx0000, 0x0008, Z_(GenericDeviceClass), 1, Z_Nop },
- { Zenum8, Cx0000, 0x0009, Z_(GenericDeviceType), 1, Z_Nop },
- { Zoctstr, Cx0000, 0x000A, Z_(ProductCode), 1, Z_Nop },
- { Zstring, Cx0000, 0x000B, Z_(ProductURL), 1, Z_Nop },
- { Zstring, Cx0000, 0x4000, Z_(SWBuildID), 1, Z_Nop },
- // { Zunk, Cx0000, 0xFFFF, nullptr, 0, Z_Nop }, // Remove all other values
+ { Zuint8, Cx0000, 0x0000, Z_(ZCLVersion), 1 },
+ { Zuint8, Cx0000, 0x0001, Z_(AppVersion), 1 },
+ { Zuint8, Cx0000, 0x0002, Z_(StackVersion), 1 },
+ { Zuint8, Cx0000, 0x0003, Z_(HWVersion), 1 },
+ { Zstring, Cx0000, 0x0004, Z_(Manufacturer), 1 }, // record Manufacturer
+ { Zstring, Cx0000, 0x0005, Z_(ModelId), 1 }, // record Model
+ // { Zstring, Cx0000, 0x0004, Z_(Manufacturer), 1, Z_ManufKeep }, // record Manufacturer
+ // { Zstring, Cx0000, 0x0005, Z_(ModelId), 1, Z_ModelKeep }, // record Model
+ { Zstring, Cx0000, 0x0006, Z_(DateCode), 1 },
+ { Zenum8, Cx0000, 0x0007, Z_(PowerSource), 1 },
+ { Zenum8, Cx0000, 0x0008, Z_(GenericDeviceClass), 1 },
+ { Zenum8, Cx0000, 0x0009, Z_(GenericDeviceType), 1 },
+ { Zoctstr, Cx0000, 0x000A, Z_(ProductCode), 1 },
+ { Zstring, Cx0000, 0x000B, Z_(ProductURL), 1 },
+ { Zstring, Cx0000, 0x4000, Z_(SWBuildID), 1 },
+ // { Zunk, Cx0000, 0xFFFF, nullptr, 0 }, // Remove all other values
// Cmd 0x0A - Cluster 0x0000, attribute 0xFF01 - proprietary
- { Zmap8, Cx0000, 0xFF01, Z_(), 0, Z_AqaraSensor },
- { Zmap8, Cx0000, 0xFF02, Z_(), 0, Z_AqaraSensor2 },
+ { Zmap8, Cx0000, 0xFF01, Z_(), 0 },
+ { Zmap8, Cx0000, 0xFF02, Z_(), 0 },
+ // { Zmap8, Cx0000, 0xFF01, Z_(), 0, Z_AqaraSensor },
+ // { Zmap8, Cx0000, 0xFF02, Z_(), 0, Z_AqaraSensor2 },
// Power Configuration cluster
- { Zuint16, Cx0001, 0x0000, Z_(MainsVoltage), 1, Z_Nop },
- { Zuint8, Cx0001, 0x0001, Z_(MainsFrequency), 1, Z_Nop },
- { Zuint8, Cx0001, 0x0020, Z_(BatteryVoltage), -10,Z_Nop }, // divide by 10
- { Zuint8, Cx0001, 0x0021, Z_(BatteryPercentage), -2, Z_BatteryPercentage }, // divide by 2
+ { Zuint16, Cx0001, 0x0000, Z_(MainsVoltage), 1 },
+ { Zuint8, Cx0001, 0x0001, Z_(MainsFrequency), 1 },
+ { Zuint8, Cx0001, 0x0020, Z_(BatteryVoltage), -10 }, // divide by 10
+ { Zuint8, Cx0001, 0x0021, Z_(BatteryPercentage), -2 }, // divide by 2
+ // { Zuint8, Cx0001, 0x0021, Z_(BatteryPercentage), -2, Z_BatteryPercentage }, // divide by 2
// Device Temperature Configuration cluster
- { Zint16, Cx0002, 0x0000, Z_(CurrentTemperature), 1, Z_Nop },
- { Zint16, Cx0002, 0x0001, Z_(MinTempExperienced), 1, Z_Nop },
- { Zint16, Cx0002, 0x0002, Z_(MaxTempExperienced), 1, Z_Nop },
- { Zuint16, Cx0002, 0x0003, Z_(OverTempTotalDwell), 1, Z_Nop },
+ { Zint16, Cx0002, 0x0000, Z_(CurrentTemperature), 1 },
+ { Zint16, Cx0002, 0x0001, Z_(MinTempExperienced), 1 },
+ { Zint16, Cx0002, 0x0002, Z_(MaxTempExperienced), 1 },
+ { Zuint16, Cx0002, 0x0003, Z_(OverTempTotalDwell), 1 },
// Identify cluster
- { Zuint16, Cx0003, 0x0000, Z_(IdentifyTime), 1, Z_Nop },
+ { Zuint16, Cx0003, 0x0000, Z_(IdentifyTime), 1 },
// Groups cluster
- { Zmap8, Cx0004, 0x0000, Z_(GroupNameSupport), 1, Z_Nop },
+ { Zmap8, Cx0004, 0x0000, Z_(GroupNameSupport), 1 },
// Scenes cluster
- { Zuint8, Cx0005, 0x0000, Z_(SceneCount), 1, Z_Nop },
- { Zuint8, Cx0005, 0x0001, Z_(CurrentScene), 1, Z_Nop },
- { Zuint16, Cx0005, 0x0002, Z_(CurrentGroup), 1, Z_Nop },
- { Zbool, Cx0005, 0x0003, Z_(SceneValid), 1, Z_Nop },
- //{ Zmap8, Cx0005, 0x0004, (NameSupport), 1, Z_Nop },
+ { Zuint8, Cx0005, 0x0000, Z_(SceneCount), 1 },
+ { Zuint8, Cx0005, 0x0001, Z_(CurrentScene), 1 },
+ { Zuint16, Cx0005, 0x0002, Z_(CurrentGroup), 1 },
+ { Zbool, Cx0005, 0x0003, Z_(SceneValid), 1 },
+ //{ Zmap8, Cx0005, 0x0004, (NameSupport), 1 },
// On/off cluster
- { Zbool, Cx0006, 0x0000, Z_(Power), 1, Z_Nop },
- { Zenum8, Cx0006, 0x4003, Z_(StartUpOnOff), 1, Z_Nop },
- { Zbool, Cx0006, 0x8000, Z_(Power), 1, Z_Nop }, // See 7280
+ { Zbool, Cx0006, 0x0000, Z_(Power), 1 },
+ { Zenum8, Cx0006, 0x4003, Z_(StartUpOnOff), 1 },
+ { Zbool, Cx0006, 0x8000, Z_(Power), 1 }, // See 7280
// On/Off Switch Configuration cluster
- { Zenum8, Cx0007, 0x0000, Z_(SwitchType), 1, Z_Nop },
+ { Zenum8, Cx0007, 0x0000, Z_(SwitchType), 1 },
// Level Control cluster
- { Zuint8, Cx0008, 0x0000, Z_(Dimmer), 1, Z_Nop },
- { Zmap8, Cx0008, 0x000F, Z_(DimmerOptions), 1, Z_Nop },
- { Zuint16, Cx0008, 0x0001, Z_(DimmerRemainingTime), 1, Z_Nop },
- { Zuint16, Cx0008, 0x0010, Z_(OnOffTransitionTime), 1, Z_Nop },
- // { Zuint8, Cx0008, 0x0011, (OnLevel), 1, Z_Nop },
- // { Zuint16, Cx0008, 0x0012, (OnTransitionTime), 1, Z_Nop },
- // { Zuint16, Cx0008, 0x0013, (OffTransitionTime), 1, Z_Nop },
- // { Zuint16, Cx0008, 0x0014, (DefaultMoveRate), 1, Z_Nop },
+ { Zuint8, Cx0008, 0x0000, Z_(Dimmer), 1 },
+ { Zmap8, Cx0008, 0x000F, Z_(DimmerOptions), 1 },
+ { Zuint16, Cx0008, 0x0001, Z_(DimmerRemainingTime), 1 },
+ { Zuint16, Cx0008, 0x0010, Z_(OnOffTransitionTime), 1 },
+ // { Zuint8, Cx0008, 0x0011, (OnLevel), 1 },
+ // { Zuint16, Cx0008, 0x0012, (OnTransitionTime), 1 },
+ // { Zuint16, Cx0008, 0x0013, (OffTransitionTime), 1 },
+ // { Zuint16, Cx0008, 0x0014, (DefaultMoveRate), 1 },
// Alarms cluster
- { Zuint16, Cx0009, 0x0000, Z_(AlarmCount), 1, Z_Nop },
+ { Zuint16, Cx0009, 0x0000, Z_(AlarmCount), 1 },
// Time cluster
- { ZUTC, Cx000A, 0x0000, Z_(Time), 1, Z_Nop },
- { Zmap8, Cx000A, 0x0001, Z_(TimeStatus), 1, Z_Nop },
- { Zint32, Cx000A, 0x0002, Z_(TimeZone), 1, Z_Nop },
- { Zuint32, Cx000A, 0x0003, Z_(DstStart), 1, Z_Nop },
- { Zuint32, Cx000A, 0x0004, Z_(DstEnd), 1, Z_Nop },
- { Zint32, Cx000A, 0x0005, Z_(DstShift), 1, Z_Nop },
- { Zuint32, Cx000A, 0x0006, Z_(StandardTime), 1, Z_Nop },
- { Zuint32, Cx000A, 0x0007, Z_(LocalTime), 1, Z_Nop },
- { ZUTC, Cx000A, 0x0008, Z_(LastSetTime), 1, Z_Nop },
- { ZUTC, Cx000A, 0x0009, Z_(ValidUntilTime), 1, Z_Nop },
- { ZUTC, Cx000A, 0xFF00, Z_(TimeEpoch), 1, Z_Nop }, // Tasmota specific, epoch
+ { ZUTC, Cx000A, 0x0000, Z_(Time), 1 },
+ { Zmap8, Cx000A, 0x0001, Z_(TimeStatus), 1 },
+ { Zint32, Cx000A, 0x0002, Z_(TimeZone), 1 },
+ { Zuint32, Cx000A, 0x0003, Z_(DstStart), 1 },
+ { Zuint32, Cx000A, 0x0004, Z_(DstEnd), 1 },
+ { Zint32, Cx000A, 0x0005, Z_(DstShift), 1 },
+ { Zuint32, Cx000A, 0x0006, Z_(StandardTime), 1 },
+ { Zuint32, Cx000A, 0x0007, Z_(LocalTime), 1 },
+ { ZUTC, Cx000A, 0x0008, Z_(LastSetTime), 1 },
+ { ZUTC, Cx000A, 0x0009, Z_(ValidUntilTime), 1 },
+ { ZUTC, Cx000A, 0xFF00, Z_(TimeEpoch), 1 }, // Tasmota specific, epoch
// RSSI Location cluster
- { Zdata8, Cx000B, 0x0000, Z_(LocationType), 1, Z_Nop },
- { Zenum8, Cx000B, 0x0001, Z_(LocationMethod), 1, Z_Nop },
- { Zuint16, Cx000B, 0x0002, Z_(LocationAge), 1, Z_Nop },
- { Zuint8, Cx000B, 0x0003, Z_(QualityMeasure), 1, Z_Nop },
- { Zuint8, Cx000B, 0x0004, Z_(NumberOfDevices), 1, Z_Nop },
+ { Zdata8, Cx000B, 0x0000, Z_(LocationType), 1 },
+ { Zenum8, Cx000B, 0x0001, Z_(LocationMethod), 1 },
+ { Zuint16, Cx000B, 0x0002, Z_(LocationAge), 1 },
+ { Zuint8, Cx000B, 0x0003, Z_(QualityMeasure), 1 },
+ { Zuint8, Cx000B, 0x0004, Z_(NumberOfDevices), 1 },
// Analog Input cluster
- // { 0xFF, Cx000C, 0x0004, (AnalogInActiveText), 1, Z_Nop },
- { Zstring, Cx000C, 0x001C, Z_(AnalogInDescription), 1, Z_Nop },
- // { 0xFF, Cx000C, 0x002E, (AnalogInInactiveText), 1, Z_Nop },
- { Zsingle, Cx000C, 0x0041, Z_(AnalogInMaxValue), 1, Z_Nop },
- { Zsingle, Cx000C, 0x0045, Z_(AnalogInMinValue), 1, Z_Nop },
- { Zbool, Cx000C, 0x0051, Z_(AnalogInOutOfService), 1, Z_Nop },
- { Zsingle, Cx000C, 0x0055, Z_(AqaraRotate), 1, Z_Nop },
- // { 0xFF, Cx000C, 0x0057, (AnalogInPriorityArray),1, Z_Nop },
- { Zenum8, Cx000C, 0x0067, Z_(AnalogInReliability), 1, Z_Nop },
- // { 0xFF, Cx000C, 0x0068, (AnalogInRelinquishDefault),1, Z_Nop },
- { Zsingle, Cx000C, 0x006A, Z_(AnalogInResolution), 1, Z_Nop },
- { Zmap8, Cx000C, 0x006F, Z_(AnalogInStatusFlags), 1, Z_Nop },
- { Zenum16, Cx000C, 0x0075, Z_(AnalogInEngineeringUnits),1, Z_Nop },
- { Zuint32, Cx000C, 0x0100, Z_(AnalogInApplicationType),1, Z_Nop },
- { Zuint16, Cx000C, 0xFF05, Z_(Aqara_FF05), 1, Z_Nop },
+ // { 0xFF, Cx000C, 0x0004, (AnalogInActiveText), 1 },
+ { Zstring, Cx000C, 0x001C, Z_(AnalogInDescription), 1 },
+ // { 0xFF, Cx000C, 0x002E, (AnalogInInactiveText), 1 },
+ { Zsingle, Cx000C, 0x0041, Z_(AnalogInMaxValue), 1 },
+ { Zsingle, Cx000C, 0x0045, Z_(AnalogInMinValue), 1 },
+ { Zbool, Cx000C, 0x0051, Z_(AnalogInOutOfService), 1 },
+ { Zsingle, Cx000C, 0x0055, Z_(AqaraRotate), 1 },
+ // { 0xFF, Cx000C, 0x0057, (AnalogInPriorityArray),1 },
+ { Zenum8, Cx000C, 0x0067, Z_(AnalogInReliability), 1 },
+ // { 0xFF, Cx000C, 0x0068, (AnalogInRelinquishDefault),1 },
+ { Zsingle, Cx000C, 0x006A, Z_(AnalogInResolution), 1 },
+ { Zmap8, Cx000C, 0x006F, Z_(AnalogInStatusFlags), 1 },
+ { Zenum16, Cx000C, 0x0075, Z_(AnalogInEngineeringUnits),1 },
+ { Zuint32, Cx000C, 0x0100, Z_(AnalogInApplicationType),1 },
+ { Zuint16, Cx000C, 0xFF05, Z_(Aqara_FF05), 1 },
// Analog Output cluster
- { Zstring, Cx000D, 0x001C, Z_(AnalogOutDescription), 1, Z_Nop },
- { Zsingle, Cx000D, 0x0041, Z_(AnalogOutMaxValue), 1, Z_Nop },
- { Zsingle, Cx000D, 0x0045, Z_(AnalogOutMinValue), 1, Z_Nop },
- { Zbool, Cx000D, 0x0051, Z_(AnalogOutOutOfService),1, Z_Nop },
- { Zsingle, Cx000D, 0x0055, Z_(AnalogOutValue), 1, Z_Nop },
- // { Zunk, Cx000D, 0x0057, (AnalogOutPriorityArray),1, Z_Nop },
- { Zenum8, Cx000D, 0x0067, Z_(AnalogOutReliability), 1, Z_Nop },
- { Zsingle, Cx000D, 0x0068, Z_(AnalogOutRelinquishDefault),1, Z_Nop },
- { Zsingle, Cx000D, 0x006A, Z_(AnalogOutResolution), 1, Z_Nop },
- { Zmap8, Cx000D, 0x006F, Z_(AnalogOutStatusFlags), 1, Z_Nop },
- { Zenum16, Cx000D, 0x0075, Z_(AnalogOutEngineeringUnits),1, Z_Nop },
- { Zuint32, Cx000D, 0x0100, Z_(AnalogOutApplicationType),1, Z_Nop },
+ { Zstring, Cx000D, 0x001C, Z_(AnalogOutDescription), 1 },
+ { Zsingle, Cx000D, 0x0041, Z_(AnalogOutMaxValue), 1 },
+ { Zsingle, Cx000D, 0x0045, Z_(AnalogOutMinValue), 1 },
+ { Zbool, Cx000D, 0x0051, Z_(AnalogOutOutOfService),1 },
+ { Zsingle, Cx000D, 0x0055, Z_(AnalogOutValue), 1 },
+ // { Zunk, Cx000D, 0x0057, (AnalogOutPriorityArray),1 },
+ { Zenum8, Cx000D, 0x0067, Z_(AnalogOutReliability), 1 },
+ { Zsingle, Cx000D, 0x0068, Z_(AnalogOutRelinquishDefault),1 },
+ { Zsingle, Cx000D, 0x006A, Z_(AnalogOutResolution), 1 },
+ { Zmap8, Cx000D, 0x006F, Z_(AnalogOutStatusFlags), 1 },
+ { Zenum16, Cx000D, 0x0075, Z_(AnalogOutEngineeringUnits),1 },
+ { Zuint32, Cx000D, 0x0100, Z_(AnalogOutApplicationType),1 },
// Analog Value cluster
- { Zstring, Cx000E, 0x001C, Z_(AnalogDescription), 1, Z_Nop },
- { Zbool, Cx000E, 0x0051, Z_(AnalogOutOfService), 1, Z_Nop },
- { Zsingle, Cx000E, 0x0055, Z_(AnalogValue), 1, Z_Nop },
- { Zunk, Cx000E, 0x0057, Z_(AnalogPriorityArray), 1, Z_Nop },
- { Zenum8, Cx000E, 0x0067, Z_(AnalogReliability), 1, Z_Nop },
- { Zsingle, Cx000E, 0x0068, Z_(AnalogRelinquishDefault),1, Z_Nop },
- { Zmap8, Cx000E, 0x006F, Z_(AnalogStatusFlags), 1, Z_Nop },
- { Zenum16, Cx000E, 0x0075, Z_(AnalogEngineeringUnits),1, Z_Nop },
- { Zuint32, Cx000E, 0x0100, Z_(AnalogApplicationType),1, Z_Nop },
+ { Zstring, Cx000E, 0x001C, Z_(AnalogDescription), 1 },
+ { Zbool, Cx000E, 0x0051, Z_(AnalogOutOfService), 1 },
+ { Zsingle, Cx000E, 0x0055, Z_(AnalogValue), 1 },
+ { Zunk, Cx000E, 0x0057, Z_(AnalogPriorityArray), 1 },
+ { Zenum8, Cx000E, 0x0067, Z_(AnalogReliability), 1 },
+ { Zsingle, Cx000E, 0x0068, Z_(AnalogRelinquishDefault),1 },
+ { Zmap8, Cx000E, 0x006F, Z_(AnalogStatusFlags), 1 },
+ { Zenum16, Cx000E, 0x0075, Z_(AnalogEngineeringUnits),1 },
+ { Zuint32, Cx000E, 0x0100, Z_(AnalogApplicationType),1 },
// Binary Input cluster
- { Zstring, Cx000F, 0x0004, Z_(BinaryInActiveText), 1, Z_Nop },
- { Zstring, Cx000F, 0x001C, Z_(BinaryInDescription), 1, Z_Nop },
- { Zstring, Cx000F, 0x002E, Z_(BinaryInInactiveText),1, Z_Nop },
- { Zbool, Cx000F, 0x0051, Z_(BinaryInOutOfService),1, Z_Nop },
- { Zenum8, Cx000F, 0x0054, Z_(BinaryInPolarity), 1, Z_Nop },
- { Zstring, Cx000F, 0x0055, Z_(BinaryInValue), 1, Z_Nop },
- // { 0xFF, Cx000F, 0x0057, (BinaryInPriorityArray),1, Z_Nop },
- { Zenum8, Cx000F, 0x0067, Z_(BinaryInReliability), 1, Z_Nop },
- { Zmap8, Cx000F, 0x006F, Z_(BinaryInStatusFlags), 1, Z_Nop },
- { Zuint32, Cx000F, 0x0100, Z_(BinaryInApplicationType),1, Z_Nop },
+ { Zstring, Cx000F, 0x0004, Z_(BinaryInActiveText), 1 },
+ { Zstring, Cx000F, 0x001C, Z_(BinaryInDescription), 1 },
+ { Zstring, Cx000F, 0x002E, Z_(BinaryInInactiveText),1 },
+ { Zbool, Cx000F, 0x0051, Z_(BinaryInOutOfService),1 },
+ { Zenum8, Cx000F, 0x0054, Z_(BinaryInPolarity), 1 },
+ { Zstring, Cx000F, 0x0055, Z_(BinaryInValue), 1 },
+ // { 0xFF, Cx000F, 0x0057, (BinaryInPriorityArray),1 },
+ { Zenum8, Cx000F, 0x0067, Z_(BinaryInReliability), 1 },
+ { Zmap8, Cx000F, 0x006F, Z_(BinaryInStatusFlags), 1 },
+ { Zuint32, Cx000F, 0x0100, Z_(BinaryInApplicationType),1 },
// Binary Output cluster
- { Zstring, Cx0010, 0x0004, Z_(BinaryOutActiveText), 1, Z_Nop },
- { Zstring, Cx0010, 0x001C, Z_(BinaryOutDescription), 1, Z_Nop },
- { Zstring, Cx0010, 0x002E, Z_(BinaryOutInactiveText),1, Z_Nop },
- { Zuint32, Cx0010, 0x0042, Z_(BinaryOutMinimumOffTime),1, Z_Nop },
- { Zuint32, Cx0010, 0x0043, Z_(BinaryOutMinimumOnTime),1, Z_Nop },
- { Zbool, Cx0010, 0x0051, Z_(BinaryOutOutOfService),1, Z_Nop },
- { Zenum8, Cx0010, 0x0054, Z_(BinaryOutPolarity), 1, Z_Nop },
- { Zbool, Cx0010, 0x0055, Z_(BinaryOutValue), 1, Z_Nop },
- // { Zunk, Cx0010, 0x0057, (BinaryOutPriorityArray),1, Z_Nop },
- { Zenum8, Cx0010, 0x0067, Z_(BinaryOutReliability), 1, Z_Nop },
- { Zbool, Cx0010, 0x0068, Z_(BinaryOutRelinquishDefault),1, Z_Nop },
- { Zmap8, Cx0010, 0x006F, Z_(BinaryOutStatusFlags), 1, Z_Nop },
- { Zuint32, Cx0010, 0x0100, Z_(BinaryOutApplicationType),1, Z_Nop },
+ { Zstring, Cx0010, 0x0004, Z_(BinaryOutActiveText), 1 },
+ { Zstring, Cx0010, 0x001C, Z_(BinaryOutDescription), 1 },
+ { Zstring, Cx0010, 0x002E, Z_(BinaryOutInactiveText),1 },
+ { Zuint32, Cx0010, 0x0042, Z_(BinaryOutMinimumOffTime),1 },
+ { Zuint32, Cx0010, 0x0043, Z_(BinaryOutMinimumOnTime),1 },
+ { Zbool, Cx0010, 0x0051, Z_(BinaryOutOutOfService),1 },
+ { Zenum8, Cx0010, 0x0054, Z_(BinaryOutPolarity), 1 },
+ { Zbool, Cx0010, 0x0055, Z_(BinaryOutValue), 1 },
+ // { Zunk, Cx0010, 0x0057, (BinaryOutPriorityArray),1 },
+ { Zenum8, Cx0010, 0x0067, Z_(BinaryOutReliability), 1 },
+ { Zbool, Cx0010, 0x0068, Z_(BinaryOutRelinquishDefault),1 },
+ { Zmap8, Cx0010, 0x006F, Z_(BinaryOutStatusFlags), 1 },
+ { Zuint32, Cx0010, 0x0100, Z_(BinaryOutApplicationType),1 },
// Binary Value cluster
- { Zstring, Cx0011, 0x0004, Z_(BinaryActiveText), 1, Z_Nop },
- { Zstring, Cx0011, 0x001C, Z_(BinaryDescription), 1, Z_Nop },
- { Zstring, Cx0011, 0x002E, Z_(BinaryInactiveText), 1, Z_Nop },
- { Zuint32, Cx0011, 0x0042, Z_(BinaryMinimumOffTime), 1, Z_Nop },
- { Zuint32, Cx0011, 0x0043, Z_(BinaryMinimumOnTime), 1, Z_Nop },
- { Zbool, Cx0011, 0x0051, Z_(BinaryOutOfService), 1, Z_Nop },
- { Zbool, Cx0011, 0x0055, Z_(BinaryValue), 1, Z_Nop },
- // { Zunk, Cx0011, 0x0057, (BinaryPriorityArray), 1, Z_Nop },
- { Zenum8, Cx0011, 0x0067, Z_(BinaryReliability), 1, Z_Nop },
- { Zbool, Cx0011, 0x0068, Z_(BinaryRelinquishDefault),1, Z_Nop },
- { Zmap8, Cx0011, 0x006F, Z_(BinaryStatusFlags), 1, Z_Nop },
- { Zuint32, Cx0011, 0x0100, Z_(BinaryApplicationType),1, Z_Nop },
+ { Zstring, Cx0011, 0x0004, Z_(BinaryActiveText), 1 },
+ { Zstring, Cx0011, 0x001C, Z_(BinaryDescription), 1 },
+ { Zstring, Cx0011, 0x002E, Z_(BinaryInactiveText), 1 },
+ { Zuint32, Cx0011, 0x0042, Z_(BinaryMinimumOffTime), 1 },
+ { Zuint32, Cx0011, 0x0043, Z_(BinaryMinimumOnTime), 1 },
+ { Zbool, Cx0011, 0x0051, Z_(BinaryOutOfService), 1 },
+ { Zbool, Cx0011, 0x0055, Z_(BinaryValue), 1 },
+ // { Zunk, Cx0011, 0x0057, (BinaryPriorityArray), 1 },
+ { Zenum8, Cx0011, 0x0067, Z_(BinaryReliability), 1 },
+ { Zbool, Cx0011, 0x0068, Z_(BinaryRelinquishDefault),1 },
+ { Zmap8, Cx0011, 0x006F, Z_(BinaryStatusFlags), 1 },
+ { Zuint32, Cx0011, 0x0100, Z_(BinaryApplicationType),1 },
// Multistate Input cluster
- // { Zunk, Cx0012, 0x000E, (MultiInStateText), 1, Z_Nop },
- { Zstring, Cx0012, 0x001C, Z_(MultiInDescription), 1, Z_Nop },
- { Zuint16, Cx0012, 0x004A, Z_(MultiInNumberOfStates),1, Z_Nop },
- { Zbool, Cx0012, 0x0051, Z_(MultiInOutOfService), 1, Z_Nop },
- { Zuint16, Cx0012, 0x0055, Z_(MultiInValue), 0, Z_AqaraCube },
- { Zuint16, Cx0012, 0x0055, Z_(MultiInValue), 0, Z_AqaraButton },
- { Zenum8, Cx0012, 0x0067, Z_(MultiInReliability), 1, Z_Nop },
- { Zmap8, Cx0012, 0x006F, Z_(MultiInStatusFlags), 1, Z_Nop },
- { Zuint32, Cx0012, 0x0100, Z_(MultiInApplicationType),1, Z_Nop },
+ // { Zunk, Cx0012, 0x000E, (MultiInStateText), 1 },
+ { Zstring, Cx0012, 0x001C, Z_(MultiInDescription), 1 },
+ { Zuint16, Cx0012, 0x004A, Z_(MultiInNumberOfStates),1 },
+ { Zbool, Cx0012, 0x0051, Z_(MultiInOutOfService), 1 },
+ { Zuint16, Cx0012, 0x0055, Z_(MultiInValue), 1 },
+ // { Zuint16, Cx0012, 0x0055, Z_(MultiInValue), 0, Z_AqaraCube },
+ // { Zuint16, Cx0012, 0x0055, Z_(MultiInValue), 0, Z_AqaraButton },
+ { Zenum8, Cx0012, 0x0067, Z_(MultiInReliability), 1 },
+ { Zmap8, Cx0012, 0x006F, Z_(MultiInStatusFlags), 1 },
+ { Zuint32, Cx0012, 0x0100, Z_(MultiInApplicationType),1 },
// Multistate output
- // { Zunk, Cx0013, 0x000E, (MultiOutStateText), 1, Z_Nop },
- { Zstring, Cx0013, 0x001C, Z_(MultiOutDescription), 1, Z_Nop },
- { Zuint16, Cx0013, 0x004A, Z_(MultiOutNumberOfStates),1, Z_Nop },
- { Zbool, Cx0013, 0x0051, Z_(MultiOutOutOfService), 1, Z_Nop },
- { Zuint16, Cx0013, 0x0055, Z_(MultiOutValue), 1, Z_Nop },
- // { Zunk, Cx0013, 0x0057, (MultiOutPriorityArray),1, Z_Nop },
- { Zenum8, Cx0013, 0x0067, Z_(MultiOutReliability), 1, Z_Nop },
- { Zuint16, Cx0013, 0x0068, Z_(MultiOutRelinquishDefault),1, Z_Nop },
- { Zmap8, Cx0013, 0x006F, Z_(MultiOutStatusFlags), 1, Z_Nop },
- { Zuint32, Cx0013, 0x0100, Z_(MultiOutApplicationType),1, Z_Nop },
+ // { Zunk, Cx0013, 0x000E, (MultiOutStateText), 1 },
+ { Zstring, Cx0013, 0x001C, Z_(MultiOutDescription), 1 },
+ { Zuint16, Cx0013, 0x004A, Z_(MultiOutNumberOfStates),1 },
+ { Zbool, Cx0013, 0x0051, Z_(MultiOutOutOfService), 1 },
+ { Zuint16, Cx0013, 0x0055, Z_(MultiOutValue), 1 },
+ // { Zunk, Cx0013, 0x0057, (MultiOutPriorityArray),1 },
+ { Zenum8, Cx0013, 0x0067, Z_(MultiOutReliability), 1 },
+ { Zuint16, Cx0013, 0x0068, Z_(MultiOutRelinquishDefault),1 },
+ { Zmap8, Cx0013, 0x006F, Z_(MultiOutStatusFlags), 1 },
+ { Zuint32, Cx0013, 0x0100, Z_(MultiOutApplicationType),1 },
// Multistate Value cluster
- // { Zunk, Cx0014, 0x000E, (MultiStateText), 1, Z_Nop },
- { Zstring, Cx0014, 0x001C, Z_(MultiDescription), 1, Z_Nop },
- { Zuint16, Cx0014, 0x004A, Z_(MultiNumberOfStates), 1, Z_Nop },
- { Zbool, Cx0014, 0x0051, Z_(MultiOutOfService), 1, Z_Nop },
- { Zuint16, Cx0014, 0x0055, Z_(MultiValue), 1, Z_Nop },
- { Zenum8, Cx0014, 0x0067, Z_(MultiReliability), 1, Z_Nop },
- { Zuint16, Cx0014, 0x0068, Z_(MultiRelinquishDefault),1, Z_Nop },
- { Zmap8, Cx0014, 0x006F, Z_(MultiStatusFlags), 1, Z_Nop },
- { Zuint32, Cx0014, 0x0100, Z_(MultiApplicationType), 1, Z_Nop },
+ // { Zunk, Cx0014, 0x000E, (MultiStateText), 1 },
+ { Zstring, Cx0014, 0x001C, Z_(MultiDescription), 1 },
+ { Zuint16, Cx0014, 0x004A, Z_(MultiNumberOfStates), 1 },
+ { Zbool, Cx0014, 0x0051, Z_(MultiOutOfService), 1 },
+ { Zuint16, Cx0014, 0x0055, Z_(MultiValue), 1 },
+ { Zenum8, Cx0014, 0x0067, Z_(MultiReliability), 1 },
+ { Zuint16, Cx0014, 0x0068, Z_(MultiRelinquishDefault),1 },
+ { Zmap8, Cx0014, 0x006F, Z_(MultiStatusFlags), 1 },
+ { Zuint32, Cx0014, 0x0100, Z_(MultiApplicationType), 1 },
// Power Profile cluster
- { Zuint8, Cx001A, 0x0000, Z_(TotalProfileNum), 1, Z_Nop },
- { Zbool, Cx001A, 0x0001, Z_(MultipleScheduling), 1, Z_Nop },
- { Zmap8, Cx001A, 0x0002, Z_(EnergyFormatting), 1, Z_Nop },
- { Zbool, Cx001A, 0x0003, Z_(EnergyRemote), 1, Z_Nop },
- { Zmap8, Cx001A, 0x0004, Z_(ScheduleMode), 1, Z_Nop },
+ { Zuint8, Cx001A, 0x0000, Z_(TotalProfileNum), 1 },
+ { Zbool, Cx001A, 0x0001, Z_(MultipleScheduling), 1 },
+ { Zmap8, Cx001A, 0x0002, Z_(EnergyFormatting), 1 },
+ { Zbool, Cx001A, 0x0003, Z_(EnergyRemote), 1 },
+ { Zmap8, Cx001A, 0x0004, Z_(ScheduleMode), 1 },
// Poll Control cluster
- { Zuint32, Cx0020, 0x0000, Z_(CheckinInterval), 1, Z_Nop },
- { Zuint32, Cx0020, 0x0001, Z_(LongPollInterval), 1, Z_Nop },
- { Zuint16, Cx0020, 0x0002, Z_(ShortPollInterval), 1, Z_Nop },
- { Zuint16, Cx0020, 0x0003, Z_(FastPollTimeout), 1, Z_Nop },
- { Zuint32, Cx0020, 0x0004, Z_(CheckinIntervalMin), 1, Z_Nop },
- { Zuint32, Cx0020, 0x0005, Z_(LongPollIntervalMin), 1, Z_Nop },
- { Zuint16, Cx0020, 0x0006, Z_(FastPollTimeoutMax), 1, Z_Nop },
+ { Zuint32, Cx0020, 0x0000, Z_(CheckinInterval), 1 },
+ { Zuint32, Cx0020, 0x0001, Z_(LongPollInterval), 1 },
+ { Zuint16, Cx0020, 0x0002, Z_(ShortPollInterval), 1 },
+ { Zuint16, Cx0020, 0x0003, Z_(FastPollTimeout), 1 },
+ { Zuint32, Cx0020, 0x0004, Z_(CheckinIntervalMin), 1 },
+ { Zuint32, Cx0020, 0x0005, Z_(LongPollIntervalMin), 1 },
+ { Zuint16, Cx0020, 0x0006, Z_(FastPollTimeoutMax), 1 },
// Shade Configuration cluster
- { Zuint16, Cx0100, 0x0000, Z_(PhysicalClosedLimit), 1, Z_Nop },
- { Zuint8, Cx0100, 0x0001, Z_(MotorStepSize), 1, Z_Nop },
- { Zmap8, Cx0100, 0x0002, Z_(Status), 1, Z_Nop },
- { Zuint16, Cx0100, 0x0010, Z_(ClosedLimit), 1, Z_Nop },
- { Zenum8, Cx0100, 0x0011, Z_(Mode), 1, Z_Nop },
+ { Zuint16, Cx0100, 0x0000, Z_(PhysicalClosedLimit), 1 },
+ { Zuint8, Cx0100, 0x0001, Z_(MotorStepSize), 1 },
+ { Zmap8, Cx0100, 0x0002, Z_(Status), 1 },
+ { Zuint16, Cx0100, 0x0010, Z_(ClosedLimit), 1 },
+ { Zenum8, Cx0100, 0x0011, Z_(Mode), 1 },
// Door Lock cluster
- { Zenum8, Cx0101, 0x0000, Z_(LockState), 1, Z_Nop },
- { Zenum8, Cx0101, 0x0001, Z_(LockType), 1, Z_Nop },
- { Zbool, Cx0101, 0x0002, Z_(ActuatorEnabled), 1, Z_Nop },
- { Zenum8, Cx0101, 0x0003, Z_(DoorState), 1, Z_Nop },
- { Zuint32, Cx0101, 0x0004, Z_(DoorOpenEvents), 1, Z_Nop },
- { Zuint32, Cx0101, 0x0005, Z_(DoorClosedEvents), 1, Z_Nop },
- { Zuint16, Cx0101, 0x0006, Z_(OpenPeriod), 1, Z_Nop },
+ { Zenum8, Cx0101, 0x0000, Z_(LockState), 1 },
+ { Zenum8, Cx0101, 0x0001, Z_(LockType), 1 },
+ { Zbool, Cx0101, 0x0002, Z_(ActuatorEnabled), 1 },
+ { Zenum8, Cx0101, 0x0003, Z_(DoorState), 1 },
+ { Zuint32, Cx0101, 0x0004, Z_(DoorOpenEvents), 1 },
+ { Zuint32, Cx0101, 0x0005, Z_(DoorClosedEvents), 1 },
+ { Zuint16, Cx0101, 0x0006, Z_(OpenPeriod), 1 },
// Aqara Lumi Vibration Sensor
- { Zuint16, Cx0101, 0x0055, Z_(AqaraVibrationMode), 0, Z_AqaraVibration },
- { Zuint16, Cx0101, 0x0503, Z_(AqaraVibrationsOrAngle), 1, Z_Nop },
- { Zuint32, Cx0101, 0x0505, Z_(AqaraVibration505), 1, Z_Nop },
- { Zuint48, Cx0101, 0x0508, Z_(AqaraAccelerometer), 0, Z_AqaraVibration },
+ { Zuint16, Cx0101, 0x0055, Z_(AqaraVibrationMode), 1 },
+ { Zuint16, Cx0101, 0x0503, Z_(AqaraVibrationsOrAngle), 1 },
+ { Zuint32, Cx0101, 0x0505, Z_(AqaraVibration505), 1 },
+ { Zuint48, Cx0101, 0x0508, Z_(AqaraAccelerometer), 1 },
// Window Covering cluster
- { Zenum8, Cx0102, 0x0000, Z_(WindowCoveringType), 1, Z_Nop },
- { Zuint16, Cx0102, 0x0001, Z_(PhysicalClosedLimitLift),1, Z_Nop },
- { Zuint16, Cx0102, 0x0002, Z_(PhysicalClosedLimitTilt),1, Z_Nop },
- { Zuint16, Cx0102, 0x0003, Z_(CurrentPositionLift), 1, Z_Nop },
- { Zuint16, Cx0102, 0x0004, Z_(CurrentPositionTilt), 1, Z_Nop },
- { Zuint16, Cx0102, 0x0005, Z_(NumberofActuationsLift),1, Z_Nop },
- { Zuint16, Cx0102, 0x0006, Z_(NumberofActuationsTilt),1, Z_Nop },
- { Zmap8, Cx0102, 0x0007, Z_(ConfigStatus), 1, Z_Nop },
- { Zuint8, Cx0102, 0x0008, Z_(CurrentPositionLiftPercentage),1, Z_Nop },
- { Zuint8, Cx0102, 0x0009, Z_(CurrentPositionTiltPercentage),1, Z_Nop },
- { Zuint16, Cx0102, 0x0010, Z_(InstalledOpenLimitLift),1, Z_Nop },
- { Zuint16, Cx0102, 0x0011, Z_(InstalledClosedLimitLift),1, Z_Nop },
- { Zuint16, Cx0102, 0x0012, Z_(InstalledOpenLimitTilt),1, Z_Nop },
- { Zuint16, Cx0102, 0x0013, Z_(InstalledClosedLimitTilt),1, Z_Nop },
- { Zuint16, Cx0102, 0x0014, Z_(VelocityLift), 1, Z_Nop },
- { Zuint16, Cx0102, 0x0015, Z_(AccelerationTimeLift),1, Z_Nop },
- { Zuint16, Cx0102, 0x0016, Z_(DecelerationTimeLift), 1, Z_Nop },
- { Zmap8, Cx0102, 0x0017, Z_(Mode), 1, Z_Nop },
- { Zoctstr, Cx0102, 0x0018, Z_(IntermediateSetpointsLift),1, Z_Nop },
- { Zoctstr, Cx0102, 0x0019, Z_(IntermediateSetpointsTilt),1, Z_Nop },
+ { Zenum8, Cx0102, 0x0000, Z_(WindowCoveringType), 1 },
+ { Zuint16, Cx0102, 0x0001, Z_(PhysicalClosedLimitLift),1 },
+ { Zuint16, Cx0102, 0x0002, Z_(PhysicalClosedLimitTilt),1 },
+ { Zuint16, Cx0102, 0x0003, Z_(CurrentPositionLift), 1 },
+ { Zuint16, Cx0102, 0x0004, Z_(CurrentPositionTilt), 1 },
+ { Zuint16, Cx0102, 0x0005, Z_(NumberofActuationsLift),1 },
+ { Zuint16, Cx0102, 0x0006, Z_(NumberofActuationsTilt),1 },
+ { Zmap8, Cx0102, 0x0007, Z_(ConfigStatus), 1 },
+ { Zuint8, Cx0102, 0x0008, Z_(CurrentPositionLiftPercentage),1 },
+ { Zuint8, Cx0102, 0x0009, Z_(CurrentPositionTiltPercentage),1 },
+ { Zuint16, Cx0102, 0x0010, Z_(InstalledOpenLimitLift),1 },
+ { Zuint16, Cx0102, 0x0011, Z_(InstalledClosedLimitLift),1 },
+ { Zuint16, Cx0102, 0x0012, Z_(InstalledOpenLimitTilt),1 },
+ { Zuint16, Cx0102, 0x0013, Z_(InstalledClosedLimitTilt),1 },
+ { Zuint16, Cx0102, 0x0014, Z_(VelocityLift), 1 },
+ { Zuint16, Cx0102, 0x0015, Z_(AccelerationTimeLift),1 },
+ { Zuint16, Cx0102, 0x0016, Z_(DecelerationTimeLift), 1 },
+ { Zmap8, Cx0102, 0x0017, Z_(Mode), 1 },
+ { Zoctstr, Cx0102, 0x0018, Z_(IntermediateSetpointsLift),1 },
+ { Zoctstr, Cx0102, 0x0019, Z_(IntermediateSetpointsTilt),1 },
// Color Control cluster
- { Zuint8, Cx0300, 0x0000, Z_(Hue), 1, Z_Nop },
- { Zuint8, Cx0300, 0x0001, Z_(Sat), 1, Z_Nop },
- { Zuint16, Cx0300, 0x0002, Z_(RemainingTime), 1, Z_Nop },
- { Zuint16, Cx0300, 0x0003, Z_(X), 1, Z_Nop },
- { Zuint16, Cx0300, 0x0004, Z_(Y), 1, Z_Nop },
- { Zenum8, Cx0300, 0x0005, Z_(DriftCompensation), 1, Z_Nop },
- { Zstring, Cx0300, 0x0006, Z_(CompensationText), 1, Z_Nop },
- { Zuint16, Cx0300, 0x0007, Z_(CT), 1, Z_Nop },
- { Zenum8, Cx0300, 0x0008, Z_(ColorMode), 1, Z_Nop },
- { Zuint8, Cx0300, 0x0010, Z_(NumberOfPrimaries), 1, Z_Nop },
- { Zuint16, Cx0300, 0x0011, Z_(Primary1X), 1, Z_Nop },
- { Zuint16, Cx0300, 0x0012, Z_(Primary1Y), 1, Z_Nop },
- { Zuint8, Cx0300, 0x0013, Z_(Primary1Intensity), 1, Z_Nop },
- { Zuint16, Cx0300, 0x0015, Z_(Primary2X), 1, Z_Nop },
- { Zuint16, Cx0300, 0x0016, Z_(Primary2Y), 1, Z_Nop },
- { Zuint8, Cx0300, 0x0017, Z_(Primary2Intensity), 1, Z_Nop },
- { Zuint16, Cx0300, 0x0019, Z_(Primary3X), 1, Z_Nop },
- { Zuint16, Cx0300, 0x001A, Z_(Primary3Y), 1, Z_Nop },
- { Zuint8, Cx0300, 0x001B, Z_(Primary3Intensity), 1, Z_Nop },
- { Zuint16, Cx0300, 0x0030, Z_(WhitePointX), 1, Z_Nop },
- { Zuint16, Cx0300, 0x0031, Z_(WhitePointY), 1, Z_Nop },
- { Zuint16, Cx0300, 0x0032, Z_(ColorPointRX), 1, Z_Nop },
- { Zuint16, Cx0300, 0x0033, Z_(ColorPointRY), 1, Z_Nop },
- { Zuint8, Cx0300, 0x0034, Z_(ColorPointRIntensity), 1, Z_Nop },
- { Zuint16, Cx0300, 0x0036, Z_(ColorPointGX), 1, Z_Nop },
- { Zuint16, Cx0300, 0x0037, Z_(ColorPointGY), 1, Z_Nop },
- { Zuint8, Cx0300, 0x0038, Z_(ColorPointGIntensity), 1, Z_Nop },
- { Zuint16, Cx0300, 0x003A, Z_(ColorPointBX), 1, Z_Nop },
- { Zuint16, Cx0300, 0x003B, Z_(ColorPointBY), 1, Z_Nop },
- { Zuint8, Cx0300, 0x003C, Z_(ColorPointBIntensity), 1, Z_Nop },
+ { Zuint8, Cx0300, 0x0000, Z_(Hue), 1 },
+ { Zuint8, Cx0300, 0x0001, Z_(Sat), 1 },
+ { Zuint16, Cx0300, 0x0002, Z_(RemainingTime), 1 },
+ { Zuint16, Cx0300, 0x0003, Z_(X), 1 },
+ { Zuint16, Cx0300, 0x0004, Z_(Y), 1 },
+ { Zenum8, Cx0300, 0x0005, Z_(DriftCompensation), 1 },
+ { Zstring, Cx0300, 0x0006, Z_(CompensationText), 1 },
+ { Zuint16, Cx0300, 0x0007, Z_(CT), 1 },
+ { Zenum8, Cx0300, 0x0008, Z_(ColorMode), 1 },
+ { Zuint8, Cx0300, 0x0010, Z_(NumberOfPrimaries), 1 },
+ { Zuint16, Cx0300, 0x0011, Z_(Primary1X), 1 },
+ { Zuint16, Cx0300, 0x0012, Z_(Primary1Y), 1 },
+ { Zuint8, Cx0300, 0x0013, Z_(Primary1Intensity), 1 },
+ { Zuint16, Cx0300, 0x0015, Z_(Primary2X), 1 },
+ { Zuint16, Cx0300, 0x0016, Z_(Primary2Y), 1 },
+ { Zuint8, Cx0300, 0x0017, Z_(Primary2Intensity), 1 },
+ { Zuint16, Cx0300, 0x0019, Z_(Primary3X), 1 },
+ { Zuint16, Cx0300, 0x001A, Z_(Primary3Y), 1 },
+ { Zuint8, Cx0300, 0x001B, Z_(Primary3Intensity), 1 },
+ { Zuint16, Cx0300, 0x0030, Z_(WhitePointX), 1 },
+ { Zuint16, Cx0300, 0x0031, Z_(WhitePointY), 1 },
+ { Zuint16, Cx0300, 0x0032, Z_(ColorPointRX), 1 },
+ { Zuint16, Cx0300, 0x0033, Z_(ColorPointRY), 1 },
+ { Zuint8, Cx0300, 0x0034, Z_(ColorPointRIntensity), 1 },
+ { Zuint16, Cx0300, 0x0036, Z_(ColorPointGX), 1 },
+ { Zuint16, Cx0300, 0x0037, Z_(ColorPointGY), 1 },
+ { Zuint8, Cx0300, 0x0038, Z_(ColorPointGIntensity), 1 },
+ { Zuint16, Cx0300, 0x003A, Z_(ColorPointBX), 1 },
+ { Zuint16, Cx0300, 0x003B, Z_(ColorPointBY), 1 },
+ { Zuint8, Cx0300, 0x003C, Z_(ColorPointBIntensity), 1 },
// Illuminance Measurement cluster
- { Zuint16, Cx0400, 0x0000, Z_(Illuminance), 1, Z_Nop }, // Illuminance (in Lux)
- { Zuint16, Cx0400, 0x0001, Z_(IlluminanceMinMeasuredValue), 1, Z_Nop }, //
- { Zuint16, Cx0400, 0x0002, Z_(IlluminanceMaxMeasuredValue), 1, Z_Nop }, //
- { Zuint16, Cx0400, 0x0003, Z_(IlluminanceTolerance), 1, Z_Nop }, //
- { Zenum8, Cx0400, 0x0004, Z_(IlluminanceLightSensorType), 1, Z_Nop }, //
- { Zunk, Cx0400, 0xFFFF, Z_(), 0, Z_Nop }, // Remove all other values
+ { Zuint16, Cx0400, 0x0000, Z_(Illuminance), 1 }, // Illuminance (in Lux)
+ { Zuint16, Cx0400, 0x0001, Z_(IlluminanceMinMeasuredValue), 1 }, //
+ { Zuint16, Cx0400, 0x0002, Z_(IlluminanceMaxMeasuredValue), 1 }, //
+ { Zuint16, Cx0400, 0x0003, Z_(IlluminanceTolerance), 1 }, //
+ { Zenum8, Cx0400, 0x0004, Z_(IlluminanceLightSensorType), 1 }, //
+ { Zunk, Cx0400, 0xFFFF, Z_(), 0 }, // Remove all other values
// Illuminance Level Sensing cluster
- { Zenum8, Cx0401, 0x0000, Z_(IlluminanceLevelStatus), 1, Z_Nop }, // Illuminance (in Lux)
- { Zenum8, Cx0401, 0x0001, Z_(IlluminanceLightSensorType), 1, Z_Nop }, // LightSensorType
- { Zuint16, Cx0401, 0x0010, Z_(IlluminanceTargetLevel), 1, Z_Nop }, //
- { Zunk, Cx0401, 0xFFFF, Z_(), 0, Z_Nop }, // Remove all other values
+ { Zenum8, Cx0401, 0x0000, Z_(IlluminanceLevelStatus), 1 }, // Illuminance (in Lux)
+ { Zenum8, Cx0401, 0x0001, Z_(IlluminanceLightSensorType), 1 }, // LightSensorType
+ { Zuint16, Cx0401, 0x0010, Z_(IlluminanceTargetLevel), 1 }, //
+ { Zunk, Cx0401, 0xFFFF, Z_(), 0 }, // Remove all other values
// Temperature Measurement cluster
- { Zint16, Cx0402, 0x0000, Z_(Temperature), -100, Z_Nop }, // divide by 100
- { Zint16, Cx0402, 0x0001, Z_(TemperatureMinMeasuredValue), -100, Z_Nop }, //
- { Zint16, Cx0402, 0x0002, Z_(TemperatureMaxMeasuredValue), -100, Z_Nop }, //
- { Zuint16, Cx0402, 0x0003, Z_(TemperatureTolerance), -100, Z_Nop }, //
- { Zunk, Cx0402, 0xFFFF, Z_(), 0, Z_Nop }, // Remove all other values
+ { Zint16, Cx0402, 0x0000, Z_(Temperature), -100 }, // divide by 100
+ { Zint16, Cx0402, 0x0001, Z_(TemperatureMinMeasuredValue), -100 }, //
+ { Zint16, Cx0402, 0x0002, Z_(TemperatureMaxMeasuredValue), -100 }, //
+ { Zuint16, Cx0402, 0x0003, Z_(TemperatureTolerance), -100 }, //
+ { Zunk, Cx0402, 0xFFFF, Z_(), 0 }, // Remove all other values
// Pressure Measurement cluster
- { Zunk, Cx0403, 0x0000, Z_(PressureUnit), 0, Z_AddPressureUnit }, // Pressure Unit
- { Zint16, Cx0403, 0x0000, Z_(Pressure), 1, Z_Nop }, // Pressure
- { Zint16, Cx0403, 0x0001, Z_(PressureMinMeasuredValue), 1, Z_Nop }, //
- { Zint16, Cx0403, 0x0002, Z_(PressureMaxMeasuredValue), 1, Z_Nop }, //
- { Zuint16, Cx0403, 0x0003, Z_(PressureTolerance), 1, Z_Nop }, //
- { Zint16, Cx0403, 0x0010, Z_(PressureScaledValue), 1, Z_Nop }, //
- { Zint16, Cx0403, 0x0011, Z_(PressureMinScaledValue), 1, Z_Nop }, //
- { Zint16, Cx0403, 0x0012, Z_(PressureMaxScaledValue), 1, Z_Nop }, //
- { Zuint16, Cx0403, 0x0013, Z_(PressureScaledTolerance), 1, Z_Nop }, //
- { Zint8, Cx0403, 0x0014, Z_(PressureScale), 1, Z_Nop }, //
- { Zunk, Cx0403, 0xFFFF, Z_(), 0, Z_Nop }, // Remove all other Pressure values
+ { Zint16, Cx0403, 0x0000, Z_(Pressure), 1 }, // Pressure
+ { Zint16, Cx0403, 0x0001, Z_(PressureMinMeasuredValue), 1 }, //
+ { Zint16, Cx0403, 0x0002, Z_(PressureMaxMeasuredValue), 1 }, //
+ { Zuint16, Cx0403, 0x0003, Z_(PressureTolerance), 1 }, //
+ { Zint16, Cx0403, 0x0010, Z_(PressureScaledValue), 1 }, //
+ { Zint16, Cx0403, 0x0011, Z_(PressureMinScaledValue), 1 }, //
+ { Zint16, Cx0403, 0x0012, Z_(PressureMaxScaledValue), 1 }, //
+ { Zuint16, Cx0403, 0x0013, Z_(PressureScaledTolerance), 1 }, //
+ { Zint8, Cx0403, 0x0014, Z_(PressureScale), 1 }, //
+ { Zunk, Cx0403, 0xFFFF, Z_(), 0 }, // Remove all other Pressure values
// Flow Measurement cluster
- { Zuint16, Cx0404, 0x0000, Z_(FlowRate), -10, Z_Nop }, // Flow (in m3/h)
- { Zuint16, Cx0404, 0x0001, Z_(FlowMinMeasuredValue), 1, Z_Nop }, //
- { Zuint16, Cx0404, 0x0002, Z_(FlowMaxMeasuredValue), 1, Z_Nop }, //
- { Zuint16, Cx0404, 0x0003, Z_(FlowTolerance), 1, Z_Nop }, //
- { Zunk, Cx0404, 0xFFFF, Z_(), 0, Z_Nop }, // Remove all other values
+ { Zuint16, Cx0404, 0x0000, Z_(FlowRate), -10 }, // Flow (in m3/h)
+ { Zuint16, Cx0404, 0x0001, Z_(FlowMinMeasuredValue), 1 }, //
+ { Zuint16, Cx0404, 0x0002, Z_(FlowMaxMeasuredValue), 1 }, //
+ { Zuint16, Cx0404, 0x0003, Z_(FlowTolerance), 1 }, //
+ { Zunk, Cx0404, 0xFFFF, Z_(), 0 }, // Remove all other values
// Relative Humidity Measurement cluster
- { Zuint16, Cx0405, 0x0000, Z_(Humidity), -100, Z_Nop }, // Humidity
- { Zuint16, Cx0405, 0x0001, Z_(HumidityMinMeasuredValue), 1, Z_Nop }, //
- { Zuint16, Cx0405, 0x0002, Z_(HumidityMaxMeasuredValue), 1, Z_Nop }, //
- { Zuint16, Cx0405, 0x0003, Z_(HumidityTolerance), 1, Z_Nop }, //
- { Zunk, Cx0405, 0xFFFF, Z_(), 0, Z_Nop }, // Remove all other values
+ { Zuint16, Cx0405, 0x0000, Z_(Humidity), -100 }, // Humidity
+ { Zuint16, Cx0405, 0x0001, Z_(HumidityMinMeasuredValue), 1 }, //
+ { Zuint16, Cx0405, 0x0002, Z_(HumidityMaxMeasuredValue), 1 }, //
+ { Zuint16, Cx0405, 0x0003, Z_(HumidityTolerance), 1 }, //
+ { Zunk, Cx0405, 0xFFFF, Z_(), 0 }, // Remove all other values
// Occupancy Sensing cluster
- { Zmap8, Cx0406, 0x0000, Z_(Occupancy), 1, Z_Nop }, // Occupancy (map8)
- { Zenum8, Cx0406, 0x0001, Z_(OccupancySensorType), 1, Z_Nop }, // OccupancySensorType
- { Zunk, Cx0406, 0xFFFF, Z_(), 0, Z_Nop }, // Remove all other values
+ { Zmap8, Cx0406, 0x0000, Z_(Occupancy), 1 }, // Occupancy (map8)
+ { Zenum8, Cx0406, 0x0001, Z_(OccupancySensorType), 1 }, // OccupancySensorType
+ { Zunk, Cx0406, 0xFFFF, Z_(), 0 }, // Remove all other values
// IAS Cluster (Intruder Alarm System)
- { Zenum8, Cx0500, 0x0000, Z_(ZoneState), 1, Z_Nop }, // Occupancy (map8)
- { Zenum16, Cx0500, 0x0001, Z_(ZoneType), 1, Z_Nop }, // Occupancy (map8)
- { Zmap16, Cx0500, 0x0002, Z_(ZoneStatus), 1, Z_Nop }, // Occupancy (map8)
+ { Zenum8, Cx0500, 0x0000, Z_(ZoneState), 1 }, // Occupancy (map8)
+ { Zenum16, Cx0500, 0x0001, Z_(ZoneType), 1 }, // Occupancy (map8)
+ { Zmap16, Cx0500, 0x0002, Z_(ZoneStatus), 1 }, // Occupancy (map8)
// Metering (Smart Energy) cluster
- { Zuint48, Cx0702, 0x0000, Z_(CurrentSummDelivered), 1, Z_Nop },
+ { Zuint48, Cx0702, 0x0000, Z_(CurrentSummDelivered), 1 },
// Meter Identification cluster
- { Zstring, Cx0B01, 0x0000, Z_(CompanyName), 1, Z_Nop },
- { Zuint16, Cx0B01, 0x0001, Z_(MeterTypeID), 1, Z_Nop },
- { Zuint16, Cx0B01, 0x0004, Z_(DataQualityID), 1, Z_Nop },
- { Zstring, Cx0B01, 0x0005, Z_(CustomerName), 1, Z_Nop },
- { Zoctstr, Cx0B01, 0x0006, Z_(Model), 1, Z_Nop },
- { Zoctstr, Cx0B01, 0x0007, Z_(PartNumber), 1, Z_Nop },
- { Zoctstr, Cx0B01, 0x0008, Z_(ProductRevision), 1, Z_Nop },
- { Zoctstr, Cx0B01, 0x000A, Z_(SoftwareRevision), 1, Z_Nop },
- { Zstring, Cx0B01, 0x000B, Z_(UtilityName), 1, Z_Nop },
- { Zstring, Cx0B01, 0x000C, Z_(POD), 1, Z_Nop },
- { Zint24, Cx0B01, 0x000D, Z_(AvailablePower), 1, Z_Nop },
- { Zint24, Cx0B01, 0x000E, Z_(PowerThreshold), 1, Z_Nop },
+ { Zstring, Cx0B01, 0x0000, Z_(CompanyName), 1 },
+ { Zuint16, Cx0B01, 0x0001, Z_(MeterTypeID), 1 },
+ { Zuint16, Cx0B01, 0x0004, Z_(DataQualityID), 1 },
+ { Zstring, Cx0B01, 0x0005, Z_(CustomerName), 1 },
+ { Zoctstr, Cx0B01, 0x0006, Z_(Model), 1 },
+ { Zoctstr, Cx0B01, 0x0007, Z_(PartNumber), 1 },
+ { Zoctstr, Cx0B01, 0x0008, Z_(ProductRevision), 1 },
+ { Zoctstr, Cx0B01, 0x000A, Z_(SoftwareRevision), 1 },
+ { Zstring, Cx0B01, 0x000B, Z_(UtilityName), 1 },
+ { Zstring, Cx0B01, 0x000C, Z_(POD), 1 },
+ { Zint24, Cx0B01, 0x000D, Z_(AvailablePower), 1 },
+ { Zint24, Cx0B01, 0x000E, Z_(PowerThreshold), 1 },
// Electrical Measurement cluster
- { Zuint16, Cx0B04, 0x0505, Z_(RMSVoltage), 1, Z_Nop },
- { Zuint16, Cx0B04, 0x0508, Z_(RMSCurrent), 1, Z_Nop },
- { Zint16, Cx0B04, 0x050B, Z_(ActivePower), 1, Z_Nop },
+ { Zuint16, Cx0B04, 0x0505, Z_(RMSVoltage), 1 },
+ { Zuint16, Cx0B04, 0x0508, Z_(RMSCurrent), 1 },
+ { Zint16, Cx0B04, 0x050B, Z_(ActivePower), 1 },
// Diagnostics cluster
- { Zuint16, Cx0B05, 0x0000, Z_(NumberOfResets), 1, Z_Nop },
- { Zuint16, Cx0B05, 0x0001, Z_(PersistentMemoryWrites),1, Z_Nop },
- { Zuint8, Cx0B05, 0x011C, Z_(LastMessageLQI), 1, Z_Nop },
- { Zuint8, Cx0B05, 0x011D, Z_(LastMessageRSSI), 1, Z_Nop },
+ { Zuint16, Cx0B05, 0x0000, Z_(NumberOfResets), 1 },
+ { Zuint16, Cx0B05, 0x0001, Z_(PersistentMemoryWrites),1 },
+ { Zuint8, Cx0B05, 0x011C, Z_(LastMessageLQI), 1 },
+ { Zuint8, Cx0B05, 0x011D, Z_(LastMessageRSSI), 1 },
};
@@ -544,8 +530,7 @@ typedef union ZCLHeaderFrameControl_t {
// If not found:
// - returns nullptr
const __FlashStringHelper* zigbeeFindAttributeByName(const char *command,
- uint16_t *cluster, uint16_t *attribute, int8_t *multiplier,
- uint8_t *cb) {
+ uint16_t *cluster, uint16_t *attribute, int8_t *multiplier) {
for (uint32_t i = 0; i < ARRAY_SIZE(Z_PostProcess); i++) {
const Z_AttributeConverter *converter = &Z_PostProcess[i];
if (0 == pgm_read_word(&converter->name_offset)) { continue; } // avoid strcasecmp_P() from crashing
@@ -553,7 +538,6 @@ const __FlashStringHelper* zigbeeFindAttributeByName(const char *command,
if (cluster) { *cluster = CxToCluster(pgm_read_byte(&converter->cluster_short)); }
if (attribute) { *attribute = pgm_read_word(&converter->attribute); }
if (multiplier) { *multiplier = pgm_read_byte(&converter->multiplier); }
- if (cb) { *cb = pgm_read_byte(&converter->cb); }
return (const __FlashStringHelper*) (Z_strings + pgm_read_word(&converter->name_offset));
}
}
@@ -627,16 +611,24 @@ public:
return _frame_control.b.frame_type & 1;
}
- static void generateAttributeName(const JsonObject& json, uint16_t cluster, uint16_t attr, char *key, size_t key_len);
- void parseReportAttributes(JsonObject& json, uint8_t offset = 0);
- void parseReadAttributes(JsonObject& json, uint8_t offset = 0);
- void parseReadAttributesResponse(JsonObject& json, uint8_t offset = 0);
- void parseReadConfigAttributes(JsonObject& json, uint8_t offset = 0);
- void parseConfigAttributes(JsonObject& json, uint8_t offset = 0);
+ void parseReportAttributes(Z_attribute_list& attr_list);
+ void generateSyntheticAttributes(Z_attribute_list& attr_list);
+ void generateCallBacks(Z_attribute_list& attr_list);
+ void parseReadAttributes(Z_attribute_list& attr_list);
+ void parseReadAttributesResponse(Z_attribute_list& attr_list);
+ void parseReadConfigAttributes(Z_attribute_list& attr_list);
+ void parseConfigAttributes(Z_attribute_list& attr_list);
void parseResponse(void);
- void parseClusterSpecificCommand(JsonObject& json, uint8_t offset = 0);
- void postProcessAttributes(uint16_t shortaddr, JsonObject& json);
- void updateInternalAttributes(uint16_t shortaddr, JsonObject& json);
+ void parseResponseOld(void);
+ void parseClusterSpecificCommand(Z_attribute_list& attr_list);
+ void postProcessAttributes(uint16_t shortaddr, Z_attribute_list& attr_list);
+
+ // synthetic attributes converters
+ void syntheticAqaraSensor(Z_attribute_list &attr_list, class Z_attribute &attr);
+ void syntheticAqaraSensor2(Z_attribute_list &attr_list, class Z_attribute &attr);
+ void syntheticAqaraCubeOrButton(Z_attribute_list &attr_list, class Z_attribute &attr);
+ void syntheticAqaraVibration(Z_attribute_list &attr_list, class Z_attribute &attr);
+
inline void setGroupId(uint16_t groupid) {
_groupaddr = groupid;
@@ -782,14 +774,13 @@ int32_t encodeSingleAttribute(class SBuffer &buf, double val_d, const char *val_
// parse a single attribute
//
// Input:
-// json: json Object where to add the attribute
-// attrid_str: the key for the attribute
+// attr: attribute object to store to
// buf: the buffer to read from
// offset: location in the buffer to read from
// attrtype: type of attribute (byte) or -1 to read from the stream as first byte
// Output:
// return: the length in bytes of the attribute
-uint32_t parseSingleAttribute(JsonObject& json, char *attrid_str, class SBuffer &buf,
+uint32_t parseSingleAttribute(Z_attribute & attr, const SBuffer &buf,
uint32_t offset, int32_t attrtype = -1) {
uint32_t i = offset;
@@ -798,7 +789,7 @@ uint32_t parseSingleAttribute(JsonObject& json, char *attrid_str, class SBuffer
}
// fallback - enter a null value
- json[attrid_str] = (char*) nullptr;
+ attr.setNone(); // set to null by default
uint32_t len = Z_getDatatypeLen(attrtype); // pre-compute lenght, overloaded for variable length attributes
@@ -814,7 +805,7 @@ uint32_t parseSingleAttribute(JsonObject& json, char *attrid_str, class SBuffer
uint8_t uint8_val = buf.get8(i);
// i += 1;
if (0xFF != uint8_val) {
- json[attrid_str] = uint8_val;
+ attr.setUInt(uint8_val);
}
}
break;
@@ -824,7 +815,7 @@ uint32_t parseSingleAttribute(JsonObject& json, char *attrid_str, class SBuffer
uint16_t uint16_val = buf.get16(i);
// i += 2;
if (0xFFFF != uint16_val) {
- json[attrid_str] = uint16_val;
+ attr.setUInt(uint16_val);
}
}
break;
@@ -834,7 +825,7 @@ uint32_t parseSingleAttribute(JsonObject& json, char *attrid_str, class SBuffer
uint32_t uint32_val = buf.get32(i);
// i += 4;
if (0xFFFFFFFF != uint32_val) {
- json[attrid_str] = uint32_val;
+ attr.setUInt(uint32_val);
}
}
break;
@@ -856,7 +847,7 @@ uint32_t parseSingleAttribute(JsonObject& json, char *attrid_str, class SBuffer
for (uint32_t j=0; j= i + 3) {
uint16_t attrid = _payload.get16(i);
i += 2;
- char key[16];
- generateAttributeName(json, _cluster_id, attrid, key, sizeof(key));
-
// exception for Xiaomi lumi.weather - specific field to be treated as octet and not char
if ((0x0000 == _cluster_id) && (0xFF01 == attrid)) {
if (0x42 == _payload.get8(i)) {
_payload.set8(i, 0x41); // change type from 0x42 to 0x41
}
}
- i += parseSingleAttribute(json, key, _payload, i);
+
+ // TODO look for suffix
+ Z_attribute & attr = attr_list.addAttribute(_cluster_id, attrid);
+
+ i += parseSingleAttribute(attr, _payload, i);
}
// Issue Philips outdoor motion sensor SML002, see https://github.com/Koenkk/zigbee2mqtt/issues/897
@@ -1061,19 +1032,67 @@ void ZCLFrame::parseReportAttributes(JsonObject& json, uint8_t offset) {
}
}
+void ZCLFrame::generateSyntheticAttributes(Z_attribute_list& attr_list) {
+ // scan through attributes and apply specific converters
+ for (auto &attr : attr_list) {
+ if (attr.key_is_str) { continue; } // pass if key is a name
+ uint32_t ccccaaaa = (attr.key.id.cluster << 16) | attr.key.id.attr_id;
+
+ switch (ccccaaaa) { // 0xccccaaaa . c=cluster, a=attribute
+ case 0x0000FF01:
+ syntheticAqaraSensor(attr_list, attr);
+ break;
+ case 0x0000FF02:
+ syntheticAqaraSensor2(attr_list, attr);
+ break;
+ case 0x00120055:
+ syntheticAqaraCubeOrButton(attr_list, attr);
+ break;
+ case 0x01010055:
+ case 0x01010508:
+ syntheticAqaraVibration(attr_list, attr);
+ break;
+ }
+ }
+}
+
+// Set deferred callbacks for Occupancy
+// TODO make delay a parameter
+void ZCLFrame::generateCallBacks(Z_attribute_list& attr_list) {
+ static const uint32_t OCCUPANCY_TIMEOUT = 90 * 1000; // 90 s
+ // scan through attributes and apply specific converters
+ for (auto &attr : attr_list) {
+ if (attr.key_is_str) { continue; } // pass if key is a name
+ uint32_t ccccaaaa = (attr.key.id.cluster << 16) | attr.key.id.attr_id;
+
+ switch (ccccaaaa) { // 0xccccaaaa . c=cluster, a=attribute
+ case 0x04060000: // Occupancy
+ uint32_t occupancy = attr.getUInt();
+ if (occupancy) {
+ zigbee_devices.setTimer(_srcaddr, 0 /* groupaddr */, OCCUPANCY_TIMEOUT, _cluster_id, _srcendpoint, Z_CAT_VIRTUAL_OCCUPANCY, 0, &Z_OccupancyCallback);
+ } else {
+ zigbee_devices.resetTimersForDevice(_srcaddr, 0 /* groupaddr */, Z_CAT_VIRTUAL_OCCUPANCY);
+ }
+ break;
+ }
+ }
+}
+
// ZCL_READ_ATTRIBUTES
-// TODO
-void ZCLFrame::parseReadAttributes(JsonObject& json, uint8_t offset) {
- uint32_t i = offset;
+void ZCLFrame::parseReadAttributes(Z_attribute_list& attr_list) {
+ uint32_t i = 0;
uint32_t len = _payload.len();
- json[F(D_CMND_ZIGBEE_CLUSTER)] = _cluster_id;
+ uint16_t read_attr_ids[len/2];
- JsonArray &attr_list = json.createNestedArray(F("Read"));
- JsonObject &attr_names = json.createNestedObject(F("ReadNames"));
+ attr_list.addAttribute(F(D_CMND_ZIGBEE_CLUSTER)).setUInt(_cluster_id);
+
+ Z_json_array attr_numbers;
+ Z_attribute_list attr_names;
while (len >= 2 + i) {
uint16_t attrid = _payload.get16(i);
- attr_list.add(attrid);
+ attr_numbers.add(attrid);
+ read_attr_ids[i/2] = attrid;
// find the attribute name
for (uint32_t i = 0; i < ARRAY_SIZE(Z_PostProcess); i++) {
@@ -1082,43 +1101,49 @@ void ZCLFrame::parseReadAttributes(JsonObject& json, uint8_t offset) {
uint16_t conv_attribute = pgm_read_word(&converter->attribute);
if ((conv_cluster == _cluster_id) && (conv_attribute == attrid)) {
- attr_names[(const __FlashStringHelper*) (Z_strings + pgm_read_word(&converter->name_offset))] = true;
+ attr_names.addAttribute(Z_strings + pgm_read_word(&converter->name_offset), true).setBool(true);
break;
}
}
i += 2;
}
+ attr_list.addAttribute(F("Read")).setStrRaw(attr_numbers.toString().c_str());
+ attr_list.addAttribute(F("ReadNames")).setStrRaw(attr_names.toString(true).c_str());
+
+ // call auto-responder
+ Z_AutoResponder(_srcaddr, _cluster_id, _srcendpoint, read_attr_ids, len/2);
}
// ZCL_CONFIGURE_REPORTING_RESPONSE
-void ZCLFrame::parseConfigAttributes(JsonObject& json, uint8_t offset) {
- uint32_t i = offset;
+void ZCLFrame::parseConfigAttributes(Z_attribute_list& attr_list) {
+ uint32_t i = 0;
uint32_t len = _payload.len();
-
- JsonObject &config_rsp = json.createNestedObject(F("ConfigResponse"));
uint8_t status = _payload.get8(i);
- config_rsp[F("Status")] = status;
- config_rsp[F("StatusMsg")] = getZigbeeStatusMessage(status);
+
+ Z_attribute_list attr_config_response;
+ attr_config_response.addAttribute(F("Status")).setUInt(status);
+ attr_config_response.addAttribute(F("StatusMsg")).setStr(getZigbeeStatusMessage(status).c_str());
+
+ Z_attribute &attr_1 = attr_list.addAttribute(F("ConfigResponse"));
+ attr_1.setStrRaw(attr_config_response.toString(true).c_str());
}
// ZCL_READ_REPORTING_CONFIGURATION_RESPONSE
-void ZCLFrame::parseReadConfigAttributes(JsonObject& json, uint8_t offset) {
- uint32_t i = offset;
+void ZCLFrame::parseReadConfigAttributes(Z_attribute_list& attr_list) {
+ uint32_t i = 0;
uint32_t len = _payload.len();
- // json[F(D_CMND_ZIGBEE_CLUSTER)] = _cluster_id; // TODO is it necessary?
+ Z_attribute &attr_root = attr_list.addAttribute(F("ReadConfig"));
+ Z_attribute_list attr_1;
- JsonObject &attr_names = json.createNestedObject(F("ReadConfig"));
while (len >= i + 4) {
uint8_t status = _payload.get8(i);
uint8_t direction = _payload.get8(i+1);
uint16_t attrid = _payload.get16(i+2);
- char attr_hex[12];
- snprintf_P(attr_hex, sizeof(attr_hex), "%04X/%04X", _cluster_id, attrid);
- JsonObject &attr_details = attr_names.createNestedObject(attr_hex);
+ Z_attribute_list attr_2;
if (direction) {
- attr_details[F("DirectionReceived")] = true;
+ attr_2.addAttribute(F("DirectionReceived")).setBool(true);
}
// find the attribute name
@@ -1128,21 +1153,22 @@ void ZCLFrame::parseReadConfigAttributes(JsonObject& json, uint8_t offset) {
uint16_t conv_attribute = pgm_read_word(&converter->attribute);
if ((conv_cluster == _cluster_id) && (conv_attribute == attrid)) {
- attr_details[(const __FlashStringHelper*) (Z_strings + pgm_read_word(&converter->name_offset))] = true;
+ const char * attr_name = Z_strings + pgm_read_word(&converter->name_offset);
+ attr_2.addAttribute(attr_name, true).setBool(true);
break;
}
}
i += 4;
if (0 != status) {
- attr_details[F("Status")] = status;
- attr_details[F("StatusMsg")] = getZigbeeStatusMessage(status);
+ attr_2.addAttribute(F("Status")).setUInt(status);
+ attr_2.addAttribute(F("StatusMsg")).setStr(getZigbeeStatusMessage(status).c_str());
} else {
// no error, decode data
if (direction) {
// only Timeout period is present
uint16_t attr_timeout = _payload.get16(i);
i += 2;
- attr_details[F("TimeoutPeriod")] = (0xFFFF == attr_timeout) ? -1 : attr_timeout;
+ attr_2.addAttribute(F("TimeoutPeriod")).setUInt((0xFFFF == attr_timeout) ? -1 : attr_timeout);
} else {
// direction == 0, we have a data type
uint8_t attr_type = _payload.get8(i);
@@ -1150,22 +1176,23 @@ void ZCLFrame::parseReadConfigAttributes(JsonObject& json, uint8_t offset) {
uint16_t attr_min_interval = _payload.get16(i+1);
uint16_t attr_max_interval = _payload.get16(i+3);
i += 5;
- attr_details[F("MinInterval")] = (0xFFFF == attr_min_interval) ? -1 : attr_min_interval;
- attr_details[F("MaxInterval")] = (0xFFFF == attr_max_interval) ? -1 : attr_max_interval;
+ attr_2.addAttribute(F("MinInterval")).setUInt((0xFFFF == attr_min_interval) ? -1 : attr_min_interval);
+ attr_2.addAttribute(F("MaxInterval")).setUInt((0xFFFF == attr_max_interval) ? -1 : attr_max_interval);
if (!attr_discrete) {
// decode Reportable Change
- char attr_name[20];
- strcpy_P(attr_name, PSTR("ReportableChange"));
- i += parseSingleAttribute(attr_details, attr_name, _payload, i, attr_type);
+ Z_attribute &attr_change = attr_2.addAttribute(F("ReportableChange"));
+ i += parseSingleAttribute(attr_change, _payload, i, attr_type);
}
}
}
+ attr_1.addAttribute(_cluster_id, attrid).setStrRaw(attr_2.toString(true).c_str());
}
+ attr_root.setStrRaw(attr_1.toString(true).c_str());
}
// ZCL_READ_ATTRIBUTES_RESPONSE
-void ZCLFrame::parseReadAttributesResponse(JsonObject& json, uint8_t offset) {
- uint32_t i = offset;
+void ZCLFrame::parseReadAttributesResponse(Z_attribute_list& attr_list) {
+ uint32_t i = 0;
uint32_t len = _payload.len();
while (len >= i + 4) {
@@ -1174,10 +1201,8 @@ void ZCLFrame::parseReadAttributesResponse(JsonObject& json, uint8_t offset) {
uint8_t status = _payload.get8(i++);
if (0 == status) {
- char key[16];
- generateAttributeName(json, _cluster_id, attrid, key, sizeof(key));
-
- i += parseSingleAttribute(json, key, _payload, i);
+ Z_attribute & attr = attr_list.addAttribute(_cluster_id, attrid);
+ i += parseSingleAttribute(attr, _payload, i);
}
}
}
@@ -1188,185 +1213,224 @@ void ZCLFrame::parseResponse(void) {
uint8_t cmd = _payload.get8(0);
uint8_t status = _payload.get8(1);
- DynamicJsonBuffer jsonBuffer;
- JsonObject& json = jsonBuffer.createObject();
+ Z_attribute_list attr_list;
// "Device"
char s[12];
snprintf_P(s, sizeof(s), PSTR("0x%04X"), _srcaddr);
- json[F(D_JSON_ZIGBEE_DEVICE)] = s;
+ attr_list.addAttribute(F(D_JSON_ZIGBEE_DEVICE)).setStr(s);
// "Name"
const char * friendlyName = zigbee_devices.getFriendlyName(_srcaddr);
if (friendlyName) {
- json[F(D_JSON_ZIGBEE_NAME)] = (char*) friendlyName;
+ attr_list.addAttribute(F(D_JSON_ZIGBEE_NAME)).setStr(friendlyName);
}
// "Command"
snprintf_P(s, sizeof(s), PSTR("%04X!%02X"), _cluster_id, cmd);
- json[F(D_JSON_ZIGBEE_CMD)] = s;
+ attr_list.addAttribute(F(D_JSON_ZIGBEE_CMD)).setStr(s);
// "Status"
- json[F(D_JSON_ZIGBEE_STATUS)] = status;
+ attr_list.addAttribute(F(D_JSON_ZIGBEE_STATUS)).setUInt(status);
// "StatusMessage"
- json[F(D_JSON_ZIGBEE_STATUS_MSG)] = getZigbeeStatusMessage(status);
+ attr_list.addAttribute(F(D_JSON_ZIGBEE_STATUS_MSG)).setStr(getZigbeeStatusMessage(status).c_str());
// Add Endpoint
- json[F(D_CMND_ZIGBEE_ENDPOINT)] = _srcendpoint;
+ attr_list.addAttribute(F(D_CMND_ZIGBEE_ENDPOINT)).setUInt(_srcendpoint);
// Add Group if non-zero
- if (_groupaddr) {
- json[F(D_CMND_ZIGBEE_GROUP)] = _groupaddr;
+ if (_groupaddr) { // TODO what about group zero
+ attr_list.group_id = _groupaddr;
}
// Add linkquality
- json[F(D_CMND_ZIGBEE_LINKQUALITY)] = _linkquality;
+ attr_list.lqi = _linkquality;
- String msg("");
- msg.reserve(100);
- json.printTo(msg);
- Response_P(PSTR("{\"" D_JSON_ZIGBEE_RESPONSE "\":%s}"), msg.c_str());
+ Response_P(PSTR("{\"" D_JSON_ZIGBEE_RESPONSE "\":%s}"), attr_list.toString(true).c_str());
MqttPublishPrefixTopicRulesProcess_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEEZCL_RECEIVED));
}
-
// Parse non-normalized attributes
-void ZCLFrame::parseClusterSpecificCommand(JsonObject& json, uint8_t offset) {
- convertClusterSpecific(json, _cluster_id, _cmd_id, _frame_control.b.direction, _srcaddr, _srcendpoint, _payload);
+void ZCLFrame::parseClusterSpecificCommand(Z_attribute_list& attr_list) {
+ convertClusterSpecific(attr_list, _cluster_id, _cmd_id, _frame_control.b.direction, _srcaddr, _srcendpoint, _payload);
#ifndef USE_ZIGBEE_NO_READ_ATTRIBUTES // read attributes unless disabled
sendHueUpdate(_srcaddr, _groupaddr, _cluster_id, _cmd_id, _frame_control.b.direction);
#endif
}
// ======================================================================
-// Record Manuf
-int32_t Z_ManufKeepFunc(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const String &new_name, uint16_t cluster, uint16_t attr) {
- zigbee_devices.setManufId(shortaddr, value.as());
- return 1;
-}
-// Record ModelId
-int32_t Z_ModelKeepFunc(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const String &new_name, uint16_t cluster, uint16_t attr) {
- zigbee_devices.setModelId(shortaddr, value.as());
- return 1;
-}
-// Record BatteryPercentage
-int32_t Z_BatteryPercentageKeepFunc(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const String &new_name, uint16_t cluster, uint16_t attr) {
- zigbee_devices.setBatteryPercent(shortaddr, json[new_name]);
- return 1;
+// New version of synthetic attribute generation
+void ZCLFrame::syntheticAqaraSensor(Z_attribute_list &attr_list, class Z_attribute &attr) {
+ const SBuffer * buf = attr.getRaw();
+ if (buf) {
+ const SBuffer & buf2 = *buf;
+ uint32_t i = 0;
+ uint32_t len = buf2.len();
+
+ const char * modelId_c = zigbee_devices.getModelId(_srcaddr); // null if unknown
+ String modelId((char*) modelId_c);
+
+ while (len >= 2 + i) {
+ uint8_t attrid = buf2.get8(i++);
+
+ Z_attribute attr; // temporary attribute
+ i += parseSingleAttribute(attr, buf2, i);
+ int32_t ival32 = attr.getInt();
+ float fval = attr.getFloat();
+ bool translated = false; // were we able to translate to a known format?
+ if (0x01 == attrid) {
+ float batteryvoltage = fval / 100;
+ attr_list.addAttribute(0x0001, 0x0020).setFloat(batteryvoltage);
+ uint8_t batterypercentage = toPercentageCR2032(fval);
+ attr_list.addAttribute(0x0001, 0x0021).setUInt(batterypercentage * 2);
+ } else if ((nullptr != modelId) && (0 == getManufCode())) {
+ translated = true;
+ if (modelId.startsWith(F("lumi.sensor_ht")) ||
+ modelId.startsWith(F("lumi.weather"))) { // Temp sensor
+ // Filter according to prefix of model name
+ // onla Aqara Temp/Humidity has manuf_code of zero. If non-zero we skip the parameters
+ if (0x64 == attrid) {
+ attr_list.addAttribute(0x0402, 0x0000).setInt(ival32); // Temperature
+ } else if (0x65 == attrid) {
+ attr_list.addAttribute(0x0405, 0x0000).setFloat(fval); // Humidity * 100
+ } else if (0x66 == attrid) {
+ attr_list.addAttribute(0x0403, 0x0000).setUInt((ival32 + 50) / 100); // Pressure
+ }
+ } else if (modelId.startsWith(F("lumi.sensor_smoke"))) { // gas leak
+ if (0x64 == attrid) {
+ attr_list.addAttribute(F("SmokeDensity")).copyVal(attr);
+ }
+ } else if (modelId.startsWith(F("lumi.sensor_natgas"))) { // gas leak
+ if (0x64 == attrid) {
+ attr_list.addAttribute(F("GasDensity")).copyVal(attr);
+ }
+ } else {
+ translated = false; // we didn't find a match
+ }
+ // } else if (0x115F == zcl->getManufCode()) { // Aqara Motion Sensor, still unknown field
+ }
+ if (!translated) {
+ if (attrid >= 100) { // payload is always above 0x64 or 100
+ char attr_name[12];
+ snprintf_P(attr_name, sizeof(attr_name), PSTR("Xiaomi_%02X"), attrid);
+ attr_list.addAttribute(attr_name).copyVal(attr);
+ }
+ }
+ }
+ }
}
-// Add pressure unit
-int32_t Z_AddPressureUnitFunc(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const String &new_name, uint16_t cluster, uint16_t attr) {
- json[new_name] = F(D_UNIT_PRESSURE);
- return 0; // keep original key
+void ZCLFrame::syntheticAqaraSensor2(class Z_attribute_list &attr_list, class Z_attribute &attr) {
+ const SBuffer * buf = attr.getRaw();
+ if (buf) {
+ const SBuffer & buf2 = *buf;
+ uint32_t len = buf2.len();
+
+ // Look for battery value which is the first attribute of type 0x21
+ uint16_t struct_size = buf2.get16(0);
+ size_t struct_len = 2;
+ if (0xFFFF != struct_size) {
+ if (struct_size > 16) { struct_size = 16; }
+ for (uint32_t j = 0; (j < struct_size) && (struct_len < len); j++) {
+ uint8_t attr_type = buf2.get8(struct_len);
+ if (0x21 == attr_type) {
+ uint16_t val = buf2.get16(struct_len+1);
+ float batteryvoltage = (float)val / 100;
+ attr_list.addAttribute(0x0001, 0x0020).setFloat(batteryvoltage);
+ uint8_t batterypercentage = toPercentageCR2032(val);
+ attr_list.addAttribute(0x0001, 0x0021).setUInt(batterypercentage * 2);
+ break;
+ }
+ struct_len += Z_getDatatypeLen(attr_type) + 1;
+ }
+ }
+ }
+ attr_list.removeAttribute(&attr);
}
-// Publish a message for `"Occupancy":0` when the timer expired
-int32_t Z_OccupancyCallback(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster, uint8_t endpoint, uint32_t value) {
- DynamicJsonBuffer jsonBuffer;
- JsonObject& json = jsonBuffer.createObject();
- json[F(OCCUPANCY)] = 0;
- zigbee_devices.jsonPublishNow(shortaddr, json);
- return 0; // Fix GCC 10.1 warning
-}
-
-// Aqara Cube
-int32_t Z_AqaraCubeFunc(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const String &new_name, uint16_t cluster, uint16_t attr) {
- const char * modelId_c = zigbee_devices.findShortAddr(shortaddr).modelId; // null if unknown
+// Aqara Cube and Button
+void ZCLFrame::syntheticAqaraCubeOrButton(class Z_attribute_list &attr_list, class Z_attribute &attr) {
+ const char * modelId_c = zigbee_devices.findShortAddr(_srcaddr).modelId; // null if unknown
String modelId((char*) modelId_c);
if (modelId.startsWith(F("lumi.sensor_cube"))) { // only for Aqara cube
- int32_t val = value;
+ int32_t val = attr.getInt();
const __FlashStringHelper *aqara_cube = F("AqaraCube");
const __FlashStringHelper *aqara_cube_side = F("AqaraCubeSide");
const __FlashStringHelper *aqara_cube_from_side = F("AqaraCubeFromSide");
switch (val) {
case 0:
- json[aqara_cube] = F("shake");
+ attr_list.addAttribute(aqara_cube).setStr(PSTR("shake"));
break;
case 2:
- json[aqara_cube] = F("wakeup");
+ attr_list.addAttribute(aqara_cube).setStr(PSTR("wakeup"));
break;
case 3:
- json[aqara_cube] = F("fall");
+ attr_list.addAttribute(aqara_cube).setStr(PSTR("fall"));
break;
case 64 ... 127:
- json[aqara_cube] = F("flip90");
- json[aqara_cube_side] = val % 8;
- json[aqara_cube_from_side] = (val - 64) / 8;
+ attr_list.addAttribute(aqara_cube).setStr(PSTR("flip90"));
+ attr_list.addAttribute(aqara_cube_side).setInt(val % 8);
+ attr_list.addAttribute(aqara_cube_from_side).setInt((val - 64) / 8);
break;
case 128 ... 132:
- json[aqara_cube] = F("flip180");
- json[aqara_cube_side] = val - 128;
+ attr_list.addAttribute(aqara_cube).setStr(PSTR("flip180"));
+ attr_list.addAttribute(aqara_cube_side).setInt(val - 128);
break;
case 256 ... 261:
- json[aqara_cube] = F("slide");
- json[aqara_cube_side] = val - 256;
+ attr_list.addAttribute(aqara_cube).setStr(PSTR("slide"));
+ attr_list.addAttribute(aqara_cube_side).setInt(val - 256);
break;
case 512 ... 517:
- json[aqara_cube] = F("tap");
- json[aqara_cube_side] = val - 512;
+ attr_list.addAttribute(aqara_cube).setStr(PSTR("tap"));
+ attr_list.addAttribute(aqara_cube_side).setInt(val - 512);
break;
}
- return 1;
- }
-
- // Source: https://github.com/kirovilya/ioBroker.zigbee
- // +---+
- // | 2 |
- // +---+---+---+
- // | 4 | 0 | 1 |
- // +---+---+---+
- // |M5I|
- // +---+
- // | 3 |
- // +---+
- // Side 5 is with the MI logo, side 3 contains the battery door.
- // presentValue = 0 = shake
- // presentValue = 2 = wakeup
- // presentValue = 3 = fly/fall
- // presentValue = y + x * 8 + 64 = 90º Flip from side x on top to side y on top
- // presentValue = x + 128 = 180º flip to side x on top
- // presentValue = x + 256 = push/slide cube while side x is on top
- // presentValue = x + 512 = double tap while side x is on top
- return 0;
-}
-
-// Aqara Button
-int32_t Z_AqaraButtonFunc(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const String &new_name, uint16_t cluster, uint16_t attr) {
- const char * modelId_c = zigbee_devices.getModelId(shortaddr); // null if unknown
- String modelId((char*) modelId_c);
-
- if (modelId.startsWith(F("lumi.remote"))) { // only for Aqara button
- int32_t val = value;
+ attr_list.removeAttribute(&attr);
+ // Source: https://github.com/kirovilya/ioBroker.zigbee
+ // +---+
+ // | 2 |
+ // +---+---+---+
+ // | 4 | 0 | 1 |
+ // +---+---+---+
+ // |M5I|
+ // +---+
+ // | 3 |
+ // +---+
+ // Side 5 is with the MI logo, side 3 contains the battery door.
+ // presentValue = 0 = shake
+ // presentValue = 2 = wakeup
+ // presentValue = 3 = fly/fall
+ // presentValue = y + x * 8 + 64 = 90º Flip from side x on top to side y on top
+ // presentValue = x + 128 = 180º flip to side x on top
+ // presentValue = x + 256 = push/slide cube while side x is on top
+ // presentValue = x + 512 = double tap while side x is on top
+ } else if (modelId.startsWith(F("lumi.remote"))) { // only for Aqara button
+ int32_t val = attr.getInt();
const __FlashStringHelper *aqara_click = F("click");
const __FlashStringHelper *aqara_action = F("action");
switch (val) {
case 0:
- json[aqara_action] = F("hold");
+ attr_list.addAttribute(aqara_action).setStr(PSTR("hold"));
break;
case 1:
- json[aqara_click] = F("single");
+ attr_list.addAttribute(aqara_click).setStr(PSTR("single"));
break;
case 2:
- json[aqara_click] = F("double");
+ attr_list.addAttribute(aqara_click).setStr(PSTR("double"));
break;
case 255:
- json[aqara_action] = F("release");
+ attr_list.addAttribute(aqara_click).setStr(PSTR("release"));
break;
default:
- json[aqara_action] = val;
+ attr_list.addAttribute(aqara_click).setUInt(val);
break;
}
- return 1;
}
-
- return 0;
}
-// Aqara Vibration Sensor - special proprietary attributes
-int32_t Z_AqaraVibrationFunc(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const String &new_name, uint16_t cluster, uint16_t attr) {
- //json[new_name] = value;
- switch (attr) {
+// Aqara vibration device
+void ZCLFrame::syntheticAqaraVibration(class Z_attribute_list &attr_list, class Z_attribute &attr) {
+ switch (attr.key.id.attr_id) {
case 0x0055:
{
- int32_t ivalue = value;
+ int32_t ivalue = attr.getInt();
const __FlashStringHelper * svalue;
switch (ivalue) {
case 1: svalue = F("vibrate"); break;
@@ -1374,283 +1438,123 @@ int32_t Z_AqaraVibrationFunc(const class ZCLFrame *zcl, uint16_t shortaddr, Json
case 3: svalue = F("drop"); break;
default: svalue = F("unknown"); break;
}
- json[new_name] = svalue;
+ attr.setStr((const char*)svalue);
}
break;
- // case 0x0503:
- // break;
- // case 0x0505:
- // break;
+ case 0x0503:
+ break;
+ case 0x0505:
+ break;
case 0x0508:
{
// see https://github.com/Koenkk/zigbee2mqtt/issues/295 and http://faire-ca-soi-meme.fr/domotique/2018/09/03/test-xiaomi-aqara-vibration-sensor/
// report accelerometer measures
- String hex = value;
- SBuffer buf2 = SBuffer::SBufferFromHex(hex.c_str(), hex.length());
- int16_t x, y, z;
- z = buf2.get16(0);
- y = buf2.get16(2);
- x = buf2.get16(4);
- JsonArray& xyz = json.createNestedArray(new_name);
- xyz.add(x);
- xyz.add(y);
- xyz.add(z);
- // calculate angles
- float X = x;
- float Y = y;
- float Z = z;
- int32_t Angle_X = 0.5f + atanf(X/sqrtf(z*z+y*y)) * f_180pi;
- int32_t Angle_Y = 0.5f + atanf(Y/sqrtf(x*x+z*z)) * f_180pi;
- int32_t Angle_Z = 0.5f + atanf(Z/sqrtf(x*x+y*y)) * f_180pi;
- JsonArray& angles = json.createNestedArray(F("AqaraAngles"));
- angles.add(Angle_X);
- angles.add(Angle_Y);
- angles.add(Angle_Z);
+ const SBuffer * buf = attr.getRaw();
+ if (buf) {
+ const SBuffer & buf2 = *buf;
+ int16_t x, y, z;
+ z = buf2.get16(0);
+ y = buf2.get16(2);
+ x = buf2.get16(4);
+ char temp[32];
+ snprintf_P(temp, sizeof(temp), "[%i,%i,%i]", x, y, z);
+ attr.setStrRaw(temp);
+ // calculate angles
+ float X = x;
+ float Y = y;
+ float Z = z;
+ int32_t Angle_X = 0.5f + atanf(X/sqrtf(z*z+y*y)) * f_180pi;
+ int32_t Angle_Y = 0.5f + atanf(Y/sqrtf(x*x+z*z)) * f_180pi;
+ int32_t Angle_Z = 0.5f + atanf(Z/sqrtf(x*x+y*y)) * f_180pi;
+ snprintf_P(temp, sizeof(temp), "[%i,%i,%i]", Angle_X, Angle_Y, Angle_Z);
+ attr_list.addAttribute(F("AqaraAngles")).setStrRaw(temp);
+ }
}
break;
}
- return 1; // remove original key
}
-int32_t Z_AqaraSensorFunc(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const String &new_name, uint16_t cluster, uint16_t attr) {
- String hex = value;
- SBuffer buf2 = SBuffer::SBufferFromHex(hex.c_str(), hex.length());
- uint32_t i = 0;
- uint32_t len = buf2.len();
- char tmp[] = "tmp"; // for obscure reasons, it must be converted from const char* to char*, otherwise ArduinoJson gets confused
-
- const char * modelId_c = zigbee_devices.getModelId(shortaddr); // null if unknown
- String modelId((char*) modelId_c);
-
- while (len >= 2 + i) {
- uint8_t attrid = buf2.get8(i++);
-
- i += parseSingleAttribute(json, tmp, buf2, i);
- float val = json[tmp];
- json.remove(tmp);
- bool translated = false; // were we able to translate to a known format?
- if (0x01 == attrid) {
- float batteryvoltage = val / 1000.0f;
- json[F("BatteryVoltage")] = batteryvoltage;
- uint8_t batterypercentage = toPercentageCR2032(val);
- json[F("BatteryPercentage")] = batterypercentage;
- zigbee_devices.setBatteryPercent(shortaddr, batterypercentage);
- // deprecated
- json[F(D_JSON_VOLTAGE)] = batteryvoltage;
- json[F("Battery")] = toPercentageCR2032(val);
- } else if ((nullptr != modelId) && (0 == zcl->getManufCode())) {
- translated = true;
- if (modelId.startsWith(F("lumi.sensor_ht")) ||
- modelId.startsWith(F("lumi.weather"))) { // Temp sensor
- // Filter according to prefix of model name
- // onla Aqara Temp/Humidity has manuf_code of zero. If non-zero we skip the parameters
- if (0x64 == attrid) {
- json[F(D_JSON_TEMPERATURE)] = val / 100.0f;
- } else if (0x65 == attrid) {
- json[F(D_JSON_HUMIDITY)] = val / 100.0f;
- } else if (0x66 == attrid) {
- json[F(D_JSON_PRESSURE)] = val / 100.0f;
- json[F(D_JSON_PRESSURE_UNIT)] = F(D_UNIT_PRESSURE); // hPa
- }
- } else if (modelId.startsWith(F("lumi.sensor_smoke"))) { // gas leak
- if (0x64 == attrid) {
- json[F("SmokeDensity")] = val;
- }
- } else if (modelId.startsWith(F("lumi.sensor_natgas"))) { // gas leak
- if (0x64 == attrid) {
- json[F("GasDensity")] = val;
- }
- } else {
- translated = false; // we didn't find a match
- }
- // } else if (0x115F == zcl->getManufCode()) { // Aqara Motion Sensor, still unknown field
- }
- if (!translated) {
- if (attrid >= 100) { // payload is always above 0x64 or 100
- char attr_name[12];
- snprintf_P(attr_name, sizeof(attr_name), PSTR("Xiaomi_%02X"), attrid);
- json[attr_name] = val;
- }
- }
- }
- return 1; // remove original key
+/// Publish a message for `"Occupancy":0` when the timer expired
+int32_t Z_OccupancyCallback(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster, uint8_t endpoint, uint32_t value) {
+ Z_attribute_list attr_list;
+ attr_list.addAttribute(F(OCCUPANCY)).setUInt(0);
+ zigbee_devices.jsonPublishNow(shortaddr, attr_list);
+ return 0; // Fix GCC 10.1 warning
}
-int32_t Z_AqaraSensorFunc2(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const String &new_name, uint16_t cluster, uint16_t attr) {
- String hex = value;
- SBuffer buf2 = SBuffer::SBufferFromHex(hex.c_str(), hex.length());
- uint32_t i = 0;
- uint32_t len = buf2.len();
-
- // Look for battery value which is the first attribute of type 0x21
- uint16_t struct_size = buf2.get16(0);
- size_t struct_len = 2;
- if (0xFFFF != struct_size) {
- if (struct_size > 16) { struct_size = 16; }
- for (uint32_t j = 0; (j < struct_size) && (struct_len < len); j++) {
- uint8_t attr_type = buf2.get8(struct_len);
- if (0x21 == attr_type) {
- uint16_t val = buf2.get16(struct_len+1);
- float batteryvoltage = val / 1000.0f;
- json[F("BatteryVoltage")] = batteryvoltage;
- uint8_t batterypercentage = toPercentageCR2032(val);
- json[F("BatteryPercentage")] = batterypercentage;
- zigbee_devices.setBatteryPercent(shortaddr, batterypercentage);
- break;
- }
- struct_len += Z_getDatatypeLen(attr_type) + 1;
- }
- }
-
- return 0; // remove original key
-}
// ======================================================================
-
-// apply the transformation from the converter
-int32_t Z_ApplyConverter(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const String &new_name,
- uint16_t cluster, uint16_t attr, int8_t multiplier, uint8_t cb) {
- // apply multiplier if needed
- if (1 == multiplier) { // copy unchanged
- json[new_name] = value;
- } else if (0 != multiplier) {
- if (multiplier > 0) {
- json[new_name] = ((float)value) * multiplier;
- } else {
- json[new_name] = ((float)value) / (-multiplier);
- }
- }
-
- // apply callback if needed
- Z_AttrConverter func = nullptr;
- switch (cb) {
- case Z_Nop:
- return 1; // drop original key
- case Z_AddPressureUnit:
- func = &Z_AddPressureUnitFunc;
- break;
- case Z_ManufKeep:
- func = &Z_ManufKeepFunc;
- break;
- case Z_ModelKeep:
- func = &Z_ModelKeepFunc;
- break;
- case Z_AqaraSensor:
- func = &Z_AqaraSensorFunc;
- break;
- case Z_AqaraSensor2:
- func = &Z_AqaraSensorFunc2;
- break;
- case Z_AqaraVibration:
- func = &Z_AqaraVibrationFunc;
- break;
- case Z_AqaraCube:
- func = &Z_AqaraCubeFunc;
- break;
- case Z_AqaraButton:
- func = &Z_AqaraButtonFunc;
- break;
- case Z_BatteryPercentage:
- func = &Z_BatteryPercentageKeepFunc;
- break;
- };
-
- if (func) {
- return (*func)(zcl, shortaddr, json, name, value, new_name, cluster, attr);
- }
- return 1; // Fix GCC 10.1 warning
-}
-
-// Scan all the final attributes and update any internal representation like sensors
-void ZCLFrame::updateInternalAttributes(uint16_t shortaddr, JsonObject& json) {
- Z_Device & device = zigbee_devices.getShortAddr(shortaddr);
- for (auto kv : json) {
- String key_string = kv.key;
- const char * key = key_string.c_str();
- JsonVariant& value = kv.value;
-
- if (key_string.equalsIgnoreCase(F("Temperature"))) {
- device.temperature = value.as() * 10 + 0.5f;
- } else if (key_string.equalsIgnoreCase(F("Humidity"))) {
- device.humidity = value.as() + 0.5f;
- } else if (key_string.equalsIgnoreCase(F("Pressure"))) {
- device.pressure = value.as() + 0.5f;
- }
- }
-}
-
-void ZCLFrame::postProcessAttributes(uint16_t shortaddr, JsonObject& json) {
+void ZCLFrame::postProcessAttributes(uint16_t shortaddr, Z_attribute_list& attr_list) {
// source endpoint
uint8_t src_ep = _srcendpoint;
- // iterate on json elements
- for (auto kv : json) {
- String key_string = kv.key;
- const char * key = key_string.c_str();
- JsonVariant& value = kv.value;
- // Check that format looks like "CCCC/AAAA" or "CCCC/AAAA+d"
- char * delimiter = strchr(key, '/');
- char * delimiter2 = strchr(key, '+');
- if (delimiter) {
- uint16_t attribute;
- uint16_t suffix = 1;
- uint16_t cluster = strtoul(key, &delimiter, 16);
- if (!delimiter2) {
- attribute = strtoul(delimiter+1, nullptr, 16);
- } else {
- attribute = strtoul(delimiter+1, &delimiter2, 16);
- suffix = strtoul(delimiter2+1, nullptr, 10);
- }
-
+
+ for (auto &attr : attr_list) {
+ // attr is Z_attribute&
+ if (!attr.key_is_str) {
+ uint16_t cluster = attr.key.id.cluster;
+ uint16_t attribute = attr.key.id.attr_id;
+ uint32_t ccccaaaa = (attr.key.id.cluster << 16) | attr.key.id.attr_id;
Z_Device & device = zigbee_devices.getShortAddr(shortaddr);
- uint16_t uval16 = value; // call converter from JSonVariant to int only once
- int16_t ival16 = value; // call converter from JSonVariant to int only once
- // see if we need to update the Hue bulb status
- if ((cluster == 0x0006) && ((attribute == 0x0000) || (attribute == 0x8000))) {
- bool power = value;
- device.setPower(power);
- } else if ((cluster == 0x0008) && (attribute == 0x0000)) {
- device.dimmer = uval16;
- } else if ((cluster == 0x0300) && (attribute == 0x0000)) {
- device.hue = changeUIntScale(uval16, 0, 254, 0, 360); // change range from 0..254 to 0..360
- } else if ((cluster == 0x0300) && (attribute == 0x0001)) {
- device.sat = uval16;
- } else if ((cluster == 0x0300) && (attribute == 0x0003)) {
- device.x = uval16;
- } else if ((cluster == 0x0300) && (attribute == 0x0004)) {
- device.y = uval16;
- } else if ((cluster == 0x0300) && (attribute == 0x0007)) {
- device.ct = uval16;
- } else if ((cluster == 0x0300) && (attribute == 0x0008)) {
- device.colormode = uval16;
- } else if ((cluster == 0x0B04) && (attribute == 0x0505)) {
- device.mains_voltage = uval16;
- } else if ((cluster == 0x0B04) && (attribute == 0x050B)) {
- device.mains_power = ival16;
- }
- // Iterate on filter
+ // Look for an entry in the converter table
+ bool found = false;
+ int8_t conv_multiplier;
+ const char * conv_name;
for (uint32_t i = 0; i < ARRAY_SIZE(Z_PostProcess); i++) {
const Z_AttributeConverter *converter = &Z_PostProcess[i];
uint16_t conv_cluster = CxToCluster(pgm_read_byte(&converter->cluster_short));
uint16_t conv_attribute = pgm_read_word(&converter->attribute);
- int8_t conv_multiplier = pgm_read_byte(&converter->multiplier);
- uint8_t conv_cb = pgm_read_byte(&converter->cb); // callback id
if ((conv_cluster == cluster) &&
((conv_attribute == attribute) || (conv_attribute == 0xFFFF)) ) {
- String new_name_str = (const __FlashStringHelper*) (Z_strings + pgm_read_word(&converter->name_offset));
- if (suffix > 1) { new_name_str += suffix; } // append suffix number
- // apply the transformation
- int32_t drop = Z_ApplyConverter(this, shortaddr, json, key, value, new_name_str, conv_cluster, conv_attribute, conv_multiplier, conv_cb);
- if (drop) {
- json.remove(key);
- }
+ conv_multiplier = pgm_read_byte(&converter->multiplier);
+ conv_name = Z_strings + pgm_read_word(&converter->name_offset);
+ found = true;
+ break;
+ }
+ }
+ // apply multiplier if needed
+ float fval = attr.getFloat();
+ if (found) {
+ if (0 == conv_multiplier) { attr_list.removeAttribute(&attr); continue; } // remove attribute if multiplier is zero
+ if (1 != conv_multiplier) {
+ if (conv_multiplier > 0) { fval = fval * conv_multiplier; }
+ else { fval = fval / (-conv_multiplier); }
+ attr.setFloat(fval);
+ }
+ }
+
+ uint16_t uval16 = attr.getUInt(); // call converter to uint only once
+ int16_t ival16 = attr.getInt(); // call converter to int only once
+ // update any internal structure
+ switch (ccccaaaa) {
+ case 0x00000004: zigbee_devices.setManufId(shortaddr, attr.getStr()); break;
+ case 0x00000005: zigbee_devices.setModelId(shortaddr, attr.getStr()); break;
+ case 0x00010021: zigbee_devices.setBatteryPercent(shortaddr, uval16); break;
+ case 0x00060000:
+ case 0x00068000: device.setPower(attr.getBool()); break;
+ case 0x00080000: device.dimmer = uval16; break;
+ case 0x03000000: device.hue = changeUIntScale(uval16, 0, 254, 0, 360); break;
+ case 0x03000001: device.sat = uval16; break;
+ case 0x03000003: device.x = uval16; break;
+ case 0x03000004: device.y = uval16; break;
+ case 0x03000007: device.ct = uval16; break;
+ case 0x03000008: device.colormode = uval16; break;
+ case 0x04020000: device.temperature = fval * 10 + 0.5f; break;
+ case 0x04030000: device.pressure = fval + 0.5f; break;
+ case 0x04050000: device.humidity = fval + 0.5f; break;
+ case 0x0B040505: device.mains_voltage = uval16; break;
+ case 0x0B04050B: device.mains_power = ival16; break;
+ }
+
+ // Replace cluster/attribute with name
+ if (found) {
+ if (0x00 != pgm_read_byte(conv_name)) {// if name is not null, replace it
+ attr.setKeyName(conv_name, true); // PMEM string so no need to copy
}
}
}
}
-
- updateInternalAttributes(shortaddr, json);
}
#endif // USE_ZIGBEE
diff --git a/tasmota/xdrv_23_zigbee_6_commands.ino b/tasmota/xdrv_23_zigbee_6_commands.ino
index 1a3e5b103..c533eb8ed 100644
--- a/tasmota/xdrv_23_zigbee_6_commands.ino
+++ b/tasmota/xdrv_23_zigbee_6_commands.ino
@@ -328,13 +328,8 @@ void sendHueUpdate(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster, uin
// Parse a cluster specific command, and try to convert into human readable
-void convertClusterSpecific(JsonObject& json, uint16_t cluster, uint8_t cmd, bool direction, uint16_t shortaddr, uint8_t srcendpoint, const SBuffer &payload) {
- size_t hex_char_len = payload.len()*2+2;
- char *hex_char = (char*) malloc(hex_char_len);
- if (!hex_char) { return; }
- ToHex_P((unsigned char*)payload.getBuffer(), payload.len(), hex_char, hex_char_len);
-
- const __FlashStringHelper* command_name = nullptr;
+void convertClusterSpecific(class Z_attribute_list &attr_list, uint16_t cluster, uint8_t cmd, bool direction, uint16_t shortaddr, uint8_t srcendpoint, const SBuffer &payload) {
+ const char * command_name = nullptr;
uint8_t conv_direction;
Z_XYZ_Var xyz;
@@ -373,7 +368,7 @@ void convertClusterSpecific(JsonObject& json, uint16_t cluster, uint8_t cmd, boo
p += 2;
}
if (match) {
- command_name = (const __FlashStringHelper*) (Z_strings + pgm_read_word(&conv->tasmota_cmd_offset));
+ command_name = Z_strings + pgm_read_word(&conv->tasmota_cmd_offset);
parseXYZ(Z_strings + pgm_read_word(&conv->param_offset), payload, &xyz);
if (0xFF == conv_cmd) {
// shift all values
@@ -396,112 +391,105 @@ void convertClusterSpecific(JsonObject& json, uint16_t cluster, uint8_t cmd, boo
// Format: "0004<00": "00" = "<": "" for commands to devices
char attrid_str[12];
snprintf_P(attrid_str, sizeof(attrid_str), PSTR("%04X%c%02X"), cluster, direction ? '<' : '!', cmd);
- json[attrid_str] = hex_char;
- free(hex_char);
+ attr_list.addAttribute(attrid_str).setBuf(payload, 0, payload.len());
if (command_name) {
// Now try to transform into a human readable format
- String command_name2 = String(command_name);
// if (direction & 0x80) then specific transform
if (conv_direction & 0x80) {
- // TODO need to create a specific command
+ uint32_t cccc00mm = (cluster << 16) | cmd; // format = cccc00mm, cccc = cluster, mm = command
// IAS
- if ((cluster == 0x0500) && (cmd == 0x00)) {
- // "ZoneStatusChange"
- json[command_name] = xyz.x;
+ switch (cccc00mm) {
+ case 0x05000000: // "ZoneStatusChange"
+ attr_list.addAttribute(command_name, true).setUInt(xyz.x);
if (0 != xyz.y) {
- json[command_name2 + F("Ext")] = xyz.y;
+ attr_list.addAttribute(command_name, PSTR("Ext")).setUInt(xyz.y);
}
if ((0 != xyz.z) && (0xFF != xyz.z)) {
- json[command_name2 + F("Zone")] = xyz.z;
+ attr_list.addAttribute(command_name, PSTR("Zone")).setUInt(xyz.z);
}
- } else if ((cluster == 0x0004) && ((cmd == 0x00) || (cmd == 0x01) || (cmd == 0x03))) {
- // AddGroupResp or ViewGroupResp (group name ignored) or RemoveGroup
- json[command_name] = xyz.y;
- json[command_name2 + F("Status")] = xyz.x;
- json[command_name2 + F("StatusMsg")] = getZigbeeStatusMessage(xyz.x);
- } else if ((cluster == 0x0004) && (cmd == 0x02)) {
- // GetGroupResp
- json[command_name2 + F("Capacity")] = xyz.x;
- json[command_name2 + F("Count")] = xyz.y;
- JsonArray &arr = json.createNestedArray(command_name);
- for (uint32_t i = 0; i < xyz.y; i++) {
- arr.add(payload.get16(2 + 2*i));
+ break;
+ case 0x00040000:
+ case 0x00040001:
+ case 0x00040003: // AddGroupResp or ViewGroupResp (group name ignored) or RemoveGroup
+ attr_list.addAttribute(command_name, true).setUInt(xyz.y);
+ attr_list.addAttribute(command_name, PSTR("Status")).setUInt(xyz.x);
+ attr_list.addAttribute(command_name, PSTR("StatusMsg")).setStr(getZigbeeStatusMessage(xyz.x).c_str());
+ break;
+ case 0x00040002: // GetGroupResp
+ attr_list.addAttribute(command_name, PSTR("Capacity")).setUInt(xyz.x);
+ attr_list.addAttribute(command_name, PSTR("Count")).setUInt(xyz.y);
+ {
+
+ Z_json_array group_list;
+ for (uint32_t i = 0; i < xyz.y; i++) {
+ group_list.add(payload.get16(2 + 2*i));
+ }
+ attr_list.addAttribute(command_name, true).setStrRaw(group_list.toString().c_str());
}
- } else if ((cluster == 0x0005) && ((cmd == 0x00) || (cmd == 0x02) || (cmd == 0x03))) {
- // AddScene or RemoveScene or StoreScene
- json[command_name2 + F("Status")] = xyz.x;
- json[command_name2 + F("StatusMsg")] = getZigbeeStatusMessage(xyz.x);
- json[F("GroupId")] = xyz.y;
- json[F("SceneId")] = xyz.z;
- } else if ((cluster == 0x0005) && (cmd == 0x01)) {
- // ViewScene
- json[command_name2 + F("Status")] = xyz.x;
- json[command_name2 + F("StatusMsg")] = getZigbeeStatusMessage(xyz.x);
- json[F("GroupId")] = xyz.y;
- json[F("SceneId")] = xyz.z;
- String scene_payload = json[attrid_str];
- json[F("ScenePayload")] = scene_payload.substring(8); // remove first 8 characters
- } else if ((cluster == 0x0005) && (cmd == 0x03)) {
- // RemoveAllScenes
- json[command_name2 + F("Status")] = xyz.x;
- json[command_name2 + F("StatusMsg")] = getZigbeeStatusMessage(xyz.x);
- json[F("GroupId")] = xyz.y;
- } else if ((cluster == 0x0005) && (cmd == 0x06)) {
- // GetSceneMembership
- json[command_name2 + F("Status")] = xyz.x;
- json[command_name2 + F("StatusMsg")] = getZigbeeStatusMessage(xyz.x);
- json[F("Capacity")] = xyz.y;
- json[F("GroupId")] = xyz.z;
- String scene_payload = json[attrid_str];
- json[F("ScenePayload")] = scene_payload.substring(8); // remove first 8 characters
- } else if ((cluster == 0x0006) && (cmd == 0x40)) {
- // Power Off With Effect
- json[F("Power")] = 0; // always "Power":0
- json[F("PowerEffect")] = xyz.x;
- json[F("PowerEffectVariant")] = xyz.y;
- } else if ((cluster == 0x0006) && (cmd == 0x41)) {
- // Power On With Recall Global Scene
- json[F("Power")] = 1; // always "Power":1
- json[F("PowerRecallGlobalScene")] = true;
- } else if ((cluster == 0x0006) && (cmd == 0x42)) {
- // Power On With Timed Off Command
- json[F("Power")] = 1; // always "Power":1
- json[F("PowerOnlyWhenOn")] = xyz.x;
- json[F("PowerOnTime")] = xyz.y / 10.0f;
- json[F("PowerOffWait")] = xyz.z / 10.0f;
+ break;
+ case 0x00050000:
+ case 0x00050001: // ViewScene
+ case 0x00050002:
+ case 0x00050004: // AddScene or RemoveScene or StoreScene
+ attr_list.addAttribute(command_name, PSTR("Status")).setUInt(xyz.x);
+ attr_list.addAttribute(command_name, PSTR("StatusMsg")).setStr(getZigbeeStatusMessage(xyz.x).c_str());
+ attr_list.addAttribute(PSTR("GroupId"), true).setUInt(xyz.y);
+ attr_list.addAttribute(PSTR("SceneId"), true).setUInt(xyz.z);
+ if (0x00050001 == cccc00mm) { // ViewScene specific
+ attr_list.addAttribute(PSTR("ScenePayload"), true).setBuf(payload, 4, payload.len()-4); // remove first 4 bytes
+ }
+ break;
+ case 0x00050003: // RemoveAllScenes
+ attr_list.addAttribute(command_name, PSTR("Status")).setUInt(xyz.x);
+ attr_list.addAttribute(command_name, PSTR("StatusMsg")).setStr(getZigbeeStatusMessage(xyz.x).c_str());
+ attr_list.addAttribute(PSTR("GroupId"), true).setUInt(xyz.y);
+ break;
+ case 0x00050006: // GetSceneMembership
+ attr_list.addAttribute(command_name, PSTR("Status")).setUInt(xyz.x);
+ attr_list.addAttribute(command_name, PSTR("StatusMsg")).setStr(getZigbeeStatusMessage(xyz.x).c_str());
+ attr_list.addAttribute(PSTR("Capacity"), true).setUInt(xyz.y);
+ attr_list.addAttribute(PSTR("GroupId"), true).setUInt(xyz.z);
+ attr_list.addAttribute(PSTR("ScenePayload"), true).setBuf(payload, 4, payload.len()-4); // remove first 4 bytes
+ break;
+ case 0x00060040: // Power Off With Effect
+ attr_list.addAttribute(PSTR("Power"), true).setUInt(0);
+ attr_list.addAttribute(PSTR("PowerEffect"), true).setUInt(xyz.x);
+ attr_list.addAttribute(PSTR("PowerEffectVariant"), true).setUInt(xyz.y);
+ break;
+ case 0x00060041: // Power On With Recall Global Scene
+ attr_list.addAttribute(PSTR("Power"), true).setUInt(1);
+ attr_list.addAttribute(PSTR("PowerRecallGlobalScene"), true).setBool(true);
+ break;
+ case 0x00060042: // Power On With Timed Off Command
+ attr_list.addAttribute(PSTR("Power"), true).setUInt(1);
+ attr_list.addAttribute(PSTR("PowerOnlyWhenOn"), true).setUInt(xyz.x);
+ attr_list.addAttribute(PSTR("PowerOnTime"), true).setFloat(xyz.y / 10.0f);
+ attr_list.addAttribute(PSTR("PowerOffWait"), true).setFloat(xyz.z / 10.0f);
+ break;
}
} else { // general case
- bool extended_command = false; // do we send command with endpoint suffix
+ // do we send command with endpoint suffix
+ char command_suffix[4] = { 0x00 }; // empty string by default
// if SO101 and multiple endpoints, append endpoint number
if (Settings.flag4.zb_index_ep) {
if (zigbee_devices.countEndpoints(shortaddr) > 0) {
- command_name2 += srcendpoint;
- extended_command = true;
+ snprintf_P(command_suffix, sizeof(command_suffix), PSTR("%d"), srcendpoint);
}
}
if (0 == xyz.x_type) {
- json[command_name] = true; // no parameter
- if (extended_command) { json[command_name2] = true; }
+ attr_list.addAttribute(command_name, command_suffix).setBool(true);
} else if (0 == xyz.y_type) {
- json[command_name] = xyz.x; // 1 parameter
- if (extended_command) { json[command_name2] = xyz.x; }
+ attr_list.addAttribute(command_name, command_suffix).setUInt(xyz.x);
} else {
// multiple answers, create an array
- JsonArray &arr = json.createNestedArray(command_name);
+ Z_json_array arr;
arr.add(xyz.x);
arr.add(xyz.y);
if (xyz.z_type) {
arr.add(xyz.z);
}
- if (extended_command) {
- JsonArray &arr = json.createNestedArray(command_name2);
- arr.add(xyz.x);
- arr.add(xyz.y);
- if (xyz.z_type) {
- arr.add(xyz.z);
- }
- }
+ attr_list.addAttribute(command_name, command_suffix).setStrRaw(arr.toString().c_str());
}
}
}
diff --git a/tasmota/xdrv_23_zigbee_8_parsers.ino b/tasmota/xdrv_23_zigbee_8_parsers.ino
index 67eafb8b0..1f5d33a8f 100644
--- a/tasmota/xdrv_23_zigbee_8_parsers.ino
+++ b/tasmota/xdrv_23_zigbee_8_parsers.ino
@@ -1011,53 +1011,53 @@ int32_t EZ_ReceiveTCJoinHandler(int32_t res, const class SBuffer &buf) {
// Parse incoming ZCL message.
//
// This code is common to ZNP and EZSP
-void Z_IncomingMessage(ZCLFrame &zcl_received) {
+void Z_IncomingMessage(class ZCLFrame &zcl_received) {
uint16_t srcaddr = zcl_received.getSrcAddr();
uint16_t groupid = zcl_received.getGroupAddr();
uint16_t clusterid = zcl_received.getClusterId();
uint8_t linkquality = zcl_received.getLinkQuality();
uint8_t srcendpoint = zcl_received.getSrcEndpoint();
+ linkquality = linkquality != 0xFF ? linkquality : 0xFE; // avoid 0xFF (reserved for unknown)
bool defer_attributes = false; // do we defer attributes reporting to coalesce
// log the packet details
zcl_received.log();
- zigbee_devices.setLQI(srcaddr, linkquality != 0xFF ? linkquality : 0xFE); // EFR32 has a different scale for LQI
+ zigbee_devices.setLQI(srcaddr, linkquality); // EFR32 has a different scale for LQI
char shortaddr[8];
snprintf_P(shortaddr, sizeof(shortaddr), PSTR("0x%04X"), srcaddr);
- DynamicJsonBuffer jsonBuffer;
- JsonObject& json = jsonBuffer.createObject();
+ Z_attribute_list attr_list;
+ attr_list.lqi = linkquality;
+ attr_list.src_ep = srcendpoint;
+ if (groupid) { // TODO we miss the group_id == 0 here
+ attr_list.group_id = groupid;
+ }
if ( (!zcl_received.isClusterSpecificCommand()) && (ZCL_DEFAULT_RESPONSE == zcl_received.getCmdId())) {
- zcl_received.parseResponse(); // Zigbee general "Degault Response", publish ZbResponse message
+ zcl_received.parseResponse(); // Zigbee general "Default Response", publish ZbResponse message
} else {
- // Build the ZbReceive json
+ // Build the ZbReceive list
if ( (!zcl_received.isClusterSpecificCommand()) && (ZCL_REPORT_ATTRIBUTES == zcl_received.getCmdId())) {
- zcl_received.parseReportAttributes(json); // Zigbee report attributes from sensors
+ zcl_received.parseReportAttributes(attr_list); // Zigbee report attributes from sensors
if (clusterid) { defer_attributes = true; } // don't defer system Cluster=0 messages
} else if ( (!zcl_received.isClusterSpecificCommand()) && (ZCL_READ_ATTRIBUTES_RESPONSE == zcl_received.getCmdId())) {
- zcl_received.parseReadAttributesResponse(json);
+ zcl_received.parseReadAttributesResponse(attr_list);
if (clusterid) { defer_attributes = true; } // don't defer system Cluster=0 messages
} else if ( (!zcl_received.isClusterSpecificCommand()) && (ZCL_READ_ATTRIBUTES == zcl_received.getCmdId())) {
- zcl_received.parseReadAttributes(json);
+ zcl_received.parseReadAttributes(attr_list);
// never defer read_attributes, so the auto-responder can send response back on a per cluster basis
} else if ( (!zcl_received.isClusterSpecificCommand()) && (ZCL_READ_REPORTING_CONFIGURATION_RESPONSE == zcl_received.getCmdId())) {
- zcl_received.parseReadConfigAttributes(json);
+ zcl_received.parseReadConfigAttributes(attr_list);
} else if ( (!zcl_received.isClusterSpecificCommand()) && (ZCL_CONFIGURE_REPORTING_RESPONSE == zcl_received.getCmdId())) {
- zcl_received.parseConfigAttributes(json);
+ zcl_received.parseConfigAttributes(attr_list);
} else if (zcl_received.isClusterSpecificCommand()) {
- zcl_received.parseClusterSpecificCommand(json);
+ zcl_received.parseClusterSpecificCommand(attr_list);
}
- { // fence to force early de-allocation of msg
- String msg("");
- msg.reserve(100);
- json.printTo(msg);
- AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_ZIGBEE D_JSON_ZIGBEEZCL_RAW_RECEIVED ": {\"0x%04X\":%s}"), srcaddr, msg.c_str());
- }
+ AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_ZIGBEE D_JSON_ZIGBEEZCL_RAW_RECEIVED ": {\"0x%04X\":{%s}}"), srcaddr, attr_list.toString().c_str());
// discard the message if it was sent by us (broadcast or group loopback)
if (srcaddr == localShortAddr) {
@@ -1065,37 +1065,25 @@ void Z_IncomingMessage(ZCLFrame &zcl_received) {
return; // abort the rest of message management
}
- zcl_received.postProcessAttributes(srcaddr, json);
- // Add Endpoint
- json[F(D_CMND_ZIGBEE_ENDPOINT)] = srcendpoint;
- // Add Group if non-zero
- if (groupid) {
- json[F(D_CMND_ZIGBEE_GROUP)] = groupid;
- }
- // Add linkquality
- json[F(D_CMND_ZIGBEE_LINKQUALITY)] = linkquality;
+ zcl_received.generateSyntheticAttributes(attr_list);
+ zcl_received.generateCallBacks(attr_list); // set deferred callbacks, ex: Occupancy
+ zcl_received.postProcessAttributes(srcaddr, attr_list);
// 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
- // Post-provess for Aqara Presence Senson
- Z_AqaraOccupancy(srcaddr, clusterid, srcendpoint, json);
-
if (defer_attributes) {
// Prepare for publish
- if (zigbee_devices.jsonIsConflict(srcaddr, json)) {
+ if (zigbee_devices.jsonIsConflict(srcaddr, attr_list)) {
// there is conflicting values, force a publish of the previous message now and don't coalesce
zigbee_devices.jsonPublishFlush(srcaddr);
}
- zigbee_devices.jsonAppend(srcaddr, json);
+ zigbee_devices.jsonAppend(srcaddr, attr_list);
zigbee_devices.setTimer(srcaddr, 0 /* groupaddr */, USE_ZIGBEE_COALESCE_ATTR_TIMER, clusterid, srcendpoint, Z_CAT_READ_ATTR, 0, &Z_PublishAttributes);
} else {
// Publish immediately
- zigbee_devices.jsonPublishNow(srcaddr, json);
-
- // Add auto-responder here
- Z_AutoResponder(srcaddr, clusterid, srcendpoint, json[F("ReadNames")]);
+ zigbee_devices.jsonPublishNow(srcaddr, attr_list);
}
}
}
@@ -1281,30 +1269,8 @@ int32_t EZ_Recv_Default(int32_t res, const class SBuffer &buf) {
* Callbacks
\*********************************************************************************************/
-
-// Aqara Occupancy behavior: the Aqara device only sends Occupancy: true events every 60 seconds.
-// Here we add a timer so if we don't receive a Occupancy event for 90 seconds, we send Occupancy:false
-void Z_AqaraOccupancy(uint16_t shortaddr, uint16_t cluster, uint8_t endpoint, const JsonObject &json) {
- static const uint32_t OCCUPANCY_TIMEOUT = 90 * 1000; // 90 s
- // Read OCCUPANCY value if any
- const JsonVariant &val_endpoint = GetCaseInsensitive(json, PSTR(OCCUPANCY));
- if (nullptr != &val_endpoint) {
- uint32_t occupancy = strToUInt(val_endpoint);
-
- if (occupancy) {
- zigbee_devices.setTimer(shortaddr, 0 /* groupaddr */, OCCUPANCY_TIMEOUT, cluster, endpoint, Z_CAT_VIRTUAL_OCCUPANCY, 0, &Z_OccupancyCallback);
- } else {
- zigbee_devices.resetTimersForDevice(shortaddr, 0 /* groupaddr */, Z_CAT_VIRTUAL_OCCUPANCY);
- }
- }
-}
-
-
// Publish the received values once they have been coalesced
int32_t Z_PublishAttributes(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster, uint8_t endpoint, uint32_t value) {
- const JsonObject *json = zigbee_devices.jsonGet(shortaddr);
- if (json == nullptr) { return 0; } // don't crash if not found
-
zigbee_devices.jsonPublishFlush(shortaddr);
return 1;
}
@@ -1476,49 +1442,61 @@ int32_t Z_State_Ready(uint8_t value) {
//
// Mostly used for routers/end-devices
// json: holds the attributes in JSON format
-void Z_AutoResponder(uint16_t srcaddr, uint16_t cluster, uint8_t endpoint, const JsonObject &json) {
+void Z_AutoResponder(uint16_t srcaddr, uint16_t cluster, uint8_t endpoint, const uint16_t *attr_list, size_t attr_len) {
DynamicJsonBuffer jsonBuffer;
JsonObject& json_out = jsonBuffer.createObject();
- // responder
- switch (cluster) {
- case 0x0000:
- if (HasKeyCaseInsensitive(json, PSTR("ModelId"))) { json_out[F("ModelId")] = F(USE_ZIGBEE_MODELID); }
- if (HasKeyCaseInsensitive(json, PSTR("Manufacturer"))) { json_out[F("Manufacturer")] = F(USE_ZIGBEE_MANUFACTURER); }
- break;
+ for (uint32_t i=0; i 0xFEFF) ? uxy[i] : 0xFEFF;
- }
- if (HasKeyCaseInsensitive(json, PSTR("Hue"))) { json_out[F("Hue")] = changeUIntScale(hue, 0, 360, 0, 254); }
- if (HasKeyCaseInsensitive(json, PSTR("Sat"))) { json_out[F("Sat")] = changeUIntScale(sat, 0, 255, 0, 254); }
- if (HasKeyCaseInsensitive(json, PSTR("CT"))) { json_out[F("CT")] = LightGetColorTemp(); }
- if (HasKeyCaseInsensitive(json, PSTR("X"))) { json_out[F("X")] = uxy[0]; }
- if (HasKeyCaseInsensitive(json, PSTR("Y"))) { json_out[F("Y")] = uxy[1]; }
- }
- break;
+ case 0x00060000: json_out[F("Power")] = Light.power ? 1 : 0; break; // Power
+ case 0x00080000: json_out[F("Dimmer")] = LightGetDimmer(0); break; // Dimmer
+
+ case 0x03000000: // Hue
+ case 0x03000001: // Sat
+ case 0x03000003: // X
+ case 0x03000004: // Y
+ case 0x03000007: // CT
+ {
+ uint16_t hue;
+ uint8_t sat;
+ float XY[2];
+ LightGetHSB(&hue, &sat, nullptr);
+ LightGetXY(&XY[0], &XY[1]);
+ uint16_t uxy[2];
+ for (uint32_t i = 0; i < ARRAY_SIZE(XY); i++) {
+ uxy[i] = XY[i] * 65536.0f;
+ uxy[i] = (uxy[i] > 0xFEFF) ? uxy[i] : 0xFEFF;
+ }
+ if (0x0000 == attr) { json_out[F("Hue")] = changeUIntScale(hue, 0, 360, 0, 254); }
+ if (0x0001 == attr) { json_out[F("Sat")] = changeUIntScale(sat, 0, 255, 0, 254); }
+ if (0x0003 == attr) { json_out[F("X")] = uxy[0]; }
+ if (0x0004 == attr) { json_out[F("Y")] = uxy[1]; }
+ if (0x0007 == attr) { json_out[F("CT")] = LightGetColorTemp(); }
+ }
+ break;
#endif
- case 0x000A: // Time
- if (HasKeyCaseInsensitive(json, PSTR("Time"))) { json_out[F("Time")] = (Rtc.utc_time > (60 * 60 * 24 * 365 * 10)) ? Rtc.utc_time - 946684800 : Rtc.utc_time; }
- if (HasKeyCaseInsensitive(json, PSTR("TimeEpoch"))) { json_out[F("TimeEpoch")] = Rtc.utc_time; }
- if (HasKeyCaseInsensitive(json, PSTR("TimeStatus"))) { json_out[F("TimeStatus")] = (Rtc.utc_time > (60 * 60 * 24 * 365 * 10)) ? 0x02 : 0x00; } // if time is beyond 2010 then we are synchronized
- if (HasKeyCaseInsensitive(json, PSTR("TimeZone"))) { json_out[F("TimeZone")] = Settings.toffset[0] * 60; } // seconds
- break;
+ case 0x000A0000: // Time
+ json_out[F("Time")] = (Rtc.utc_time > (60 * 60 * 24 * 365 * 10)) ? Rtc.utc_time - 946684800 : Rtc.utc_time;
+ break;
+ case 0x000AFF00: // TimeEpoch - Tasmota specific
+ json_out[F("TimeEpoch")] = Rtc.utc_time;
+ break;
+ case 0x000A0001: // TimeStatus
+ json_out[F("TimeStatus")] = (Rtc.utc_time > (60 * 60 * 24 * 365 * 10)) ? 0x02 : 0x00; // if time is beyond 2010 then we are synchronized
+ break;
+ case 0x000A0002: // TimeZone
+ json_out[F("TimeZone")] = Settings.toffset[0] * 60;
+ break;
+ case 0x000A0007: // LocalTime // TODO take DST
+ json_out[F("LocalTime")] = Settings.toffset[0] * 60 + ((Rtc.utc_time > (60 * 60 * 24 * 365 * 10)) ? Rtc.utc_time - 946684800 : Rtc.utc_time);
+ break;
+ }
}
if (json_out.size() > 0) {
diff --git a/tasmota/xdrv_23_zigbee_A_impl.ino b/tasmota/xdrv_23_zigbee_A_impl.ino
index a6386e491..4dd7e99ad 100644
--- a/tasmota/xdrv_23_zigbee_A_impl.ino
+++ b/tasmota/xdrv_23_zigbee_A_impl.ino
@@ -795,7 +795,7 @@ void ZbBindUnbind(bool unbind) { // false = bind, true = unbind
if (nullptr != &val_cluster) {
cluster = strToUInt(val_cluster); // first convert as number
if (0 == cluster) {
- zigbeeFindAttributeByName(val_cluster.as(), &cluster, nullptr, nullptr, nullptr);
+ zigbeeFindAttributeByName(val_cluster.as(), &cluster, nullptr, nullptr);
}
}
From 4f33a68dae58760dd3a790afe600477353149210 Mon Sep 17 00:00:00 2001
From: Theo Arends <11044339+arendst@users.noreply.github.com>
Date: Sat, 5 Sep 2020 15:01:31 +0200
Subject: [PATCH 20/38] Update template info
---
MODULES.md | 2 +-
TEMPLATES.md | 452 ++++++++++++++++++++++++++++++++-------------------
2 files changed, 282 insertions(+), 172 deletions(-)
diff --git a/MODULES.md b/MODULES.md
index 6106f73e6..f4a28d3f7 100644
--- a/MODULES.md
+++ b/MODULES.md
@@ -80,4 +80,4 @@ Module | LCode | Description
74 Sonoff D1 | x | Sonoff D1 Wifi and RF Dimmer
75 Sonoff ZbBridge | x | Sonoff Zigbee bridge
-Over 1400 additional devices are supported using [templates](TEMPLATES.md).
+Over 1500 additional devices are supported using [templates](TEMPLATES.md).
diff --git a/TEMPLATES.md b/TEMPLATES.md
index ff354aab7..8df91d725 100644
--- a/TEMPLATES.md
+++ b/TEMPLATES.md
@@ -2,12 +2,12 @@
# Templates
-Find below the available templates as of July 2020. More template information can be found in the [Tasmota Device Templates Repository](http://blakadder.github.io/templates)
+Find below the available templates as of September 2020. More template information can be found in the [Tasmota Device Templates Repository](http://blakadder.github.io/templates)
## Aromatherapy Diffuser
```
Asakuki 500ml {"NAME":"Oil Diffuser","GPIO":[255,255,255,255,255,255,0,0,255,255,255,21,22],"FLAG":0,"BASE":18}
-Blitzwolf BW-FUN3 400ml {"NAME":"Generic","GPIO":[255,255,255,255,255,255,255,255,255,255,255,255,255],"FLAG":0,"BASE":54}
+BlitzWolf BW-FUN3 400ml {"NAME":"Generic","GPIO":[255,255,255,255,255,255,255,255,255,255,255,255,255],"FLAG":0,"BASE":54}
Brilex 400ml Oil Diffuser {"NAME":"BrilexDiffuser","GPIO":[255,255,255,255,255,255,0,0,255,255,255,255,255],"FLAG":0,"BASE":54}
Essential Oil 400ml {"NAME":"XD800W","GPIO":[255,255,255,255,255,255,0,0,255,255,255,255,255],"FLAG":0,"BASE":54}
GD-30W 300ml {"NAME":"GD-30W","GPIO":[255,255,255,255,255,255,0,0,255,255,255,255,255],"FLAG":0,"BASE":54}
@@ -38,32 +38,35 @@ Anoopsyche 9W 800lm {"NAME":"Anoop-CW-WW","GPIO":[0,0,0,0,0,37,0,0,0,38,0,0
Arlec Smart 1350lm PAR38 {"NAME":"Arlec GLD302HA","GPIO":[0,0,0,0,0,0,0,0,38,0,37,0,0],"FLAG":0,"BASE":18}
Arlec Smart 9.5W 806lm {"NAME":"Arlec GLD110HA","GPIO":[0,0,0,0,0,37,0,0,0,38,0,0,0],"FLAG":0,"BASE":48}
Arlec Smart 9.5W 806lm {"NAME":"Arlec CCT","GPIO":[0,0,0,0,0,37,0,0,0,38,0,0,0],"FLAG":0,"BASE":48}
+BlitzWolf A70 9W 900lm {"NAME":"BW-LT29","GPIO":[0,0,0,0,0,0,0,0,0,47,0,37,0],"FLAG":0,"BASE":18}
BrilliantSmart 20696 9W 900lm {"NAME":"Brilliant20696","GPIO":[0,0,0,0,0,0,0,0,38,0,37,0,0],"FLAG":0,"BASE":18}
BrilliantSmart 20697 9W 900lm {"NAME":"Brilliant20696","GPIO":[0,0,0,0,0,0,0,0,38,0,37,0,0],"FLAG":0,"BASE":18}
+BrilliantSmart 4.5W 400lm {"NAME":"BS-GU10-20887","GPIO":[0,0,0,0,0,37,0,0,38,0,0,0,0],"FLAG":0,"BASE":18}
Bulbrite Solana A19 Edison Filament {"NAME":"BulbBrite01","GPIO":[0,0,0,0,0,0,0,0,37,0,38,0,0],"FLAG":0,"BASE":18}
Bulbrite Solana G25 5.5W 600lm Filament {"NAME":"BulbBrite01","GPIO":[0,0,0,0,0,0,0,0,38,0,37,0,0],"FLAG":0,"BASE":18}
Bulbrite Solana ST18 16W 140lm Filament {"NAME":"BulbBrite02","GPIO":[0,0,0,0,0,0,0,0,38,0,37,0,0],"FLAG":0,"BASE":18}
Calex G125 7,5W 1055lm {"NAME":"Calex G125 E27","GPIO":[0,0,0,0,0,140,0,0,38,0,37,142,141],"FLAG":0,"BASE":18}
Calex G125 7.5W 1055lm Globe {"NAME":"Calex G125 E27","GPIO":[0,0,0,0,0,0,0,0,38,0,37,0,0],"FLAG":0,"BASE":18}
-Connect Smart GU5.3 5W {"NAME":"Connect CSH-GU53WW5W","GPIO":[0,0,0,0,37,38,0,0,0,0,0,0,0],"FLAG":0,"BASE":18}
-Connect SmartHome 10W 900lm {"NAME":"Connect CCT","GPIO":[0,0,0,0,0,37,0,0,0,38,0,0,0],"FLAG":0,"BASE":48}
+Connect SmartHome 10W {"NAME":"CSH-B22WW10W","GPIO":[0,0,0,0,0,37,0,0,0,38,0,0,0],"FLAG":0,"BASE":18}
+Connect SmartHome 10W {"NAME":"CSH-E27WW10W","GPIO":[0,0,0,0,0,37,0,0,0,38,0,0,0],"FLAG":0,"BASE":18}
+Connect SmartHome GU5.3 5W {"NAME":"Connect CSH-GU53WW5W","GPIO":[0,0,0,0,37,38,0,0,0,0,0,0,0],"FLAG":0,"BASE":18}
Deltaco SH-LE14W 470lm {"NAME":"SH-LE14W","GPIO":[0,0,0,0,0,0,0,0,38,0,37,0,0],"FLAG":0,"BASE":18}
Deltaco SH-LE27W 810lm {"NAME":"SH-LE27W","GPIO":[0,0,0,0,0,0,0,0,38,0,37,0,0],"FLAG":0,"BASE":18}
DORESshop A60 720lm Filament {"NAME":"DORESshop-A60","GPIO":[0,0,0,0,0,0,0,0,38,0,37,0,0],"FLAG":0,"BASE":18}
Energizer A19 10W Smart White {"NAME":"Energizer CCT ","GPIO":[0,0,0,0,0,37,0,0,38,0,0,0,0],"FLAG":0,"BASE":18}
Euri Lighting A19 10W 800lm {"NAME":"Euri Lighting ","GPIO":[0,0,0,0,37,38,0,0,0,0,0,0,0],"FLAG":0,"BASE":18}
-Geeni LUX 1050lm {"NAME":"Geeni-1050-WW","GPIO":[0,0,0,0,37,37,0,0,38,0,0,0,0],"FLAG":1,"BASE":18}
+Geeni LUX 1050lm {"NAME":"Geeni-1050-WW","GPIO":[0,0,0,0,0,37,0,0,38,0,0,0,0],"FLAG":1,"BASE":18}
Globe A19 10W 800lm {"NAME":"GlobeCCT","GPIO":[0,0,0,0,38,37,0,0,0,0,0,0,0],"FLAG":0,"BASE":18}
Globe G25 Edison 500lm {"NAME":"Globe 34920","GPIO":[0,0,0,0,0,0,0,0,38,0,37,0,0],"FLAG":0,"BASE":18}
Globe ST19 Edison 500lm {"NAME":"Globe 34919","GPIO":[0,0,0,0,0,0,0,0,38,0,37,0,0],"FLAG":0,"BASE":18}
Hama 806lm {"NAME":"Hama 00176550","GPIO":[0,0,0,0,0,37,0,0,0,38,0,0,0],"FLAG":0,"BASE":18}
Hykker SL-0392 650lm {"NAME":"Hykker 7W","GPIO":[0,0,0,0,0,37,0,0,38,0,0,0,0],"FLAG":0,"BASE":18}
Iotton 9W 700lm {"NAME":"Iotton Light","GPIO":[0,0,0,0,37,38,0,0,0,0,0,0,0],"FLAG":0,"BASE":18}
-Kogan 10W Cool & Warm White 1050lm {"NAME":"Kogan 10W CCT","GPIO":[0,0,0,0,0,37,0,0,0,47,0,0,0],"FLAG":0,"BASE":48}
+Kogan 10W Cool & Warm White 1050lm {"NAME":"Kogan 10W CCT","GPIO":[0,0,0,0,0,37,0,0,0,38,0,0,0],"FLAG":0,"BASE":48}
Kogan 4.5W 330lm 110C {"NAME":"Kogan White/Wa","GPIO":[0,0,0,0,0,37,0,0,38,0,0,0,0],"FLAG":0,"BASE":18}
Kogan 5W {"NAME":"Kogan Co/Wa","GPIO":[0,0,0,0,0,37,0,0,0,38,0,0,0],"FLAG":0,"BASE":18}
Laser 10W 1000lm {"NAME":"Laser 10W CCT","GPIO":[0,0,0,0,0,37,0,0,0,38,0,0,0],"FLAG":0,"BASE":48}
-Laser 10W 1000lm {"NAME":"Laster 10W CCT","GPIO":[0,0,0,0,0,37,0,0,0,38,0,0,0],"FLAG":0,"BASE":48}
+Laser 10W 1000lm {"NAME":"Laster 10W CCT","GPIO":[0,0,0,0,0,37,0,0,0,38,0,0,0],"FLAG":0,"BASE":18}
LE lampUX 380lm Candle {"NAME":"LE Bulb","GPIO":[0,0,0,0,0,0,0,0,38,0,37,0,0],"FLAG":0,"BASE":18}
LE LampUX 4.5W 410lm {"NAME":"LE LampUX","GPIO":[0,0,0,0,0,37,0,0,38,0,0,0,0],"FLAG":0,"BASE":48}
ledscom.de 4.5W 430lm {"NAME":"GX53","GPIO":[0,0,0,0,0,0,0,0,38,0,37,0,0],"FLAG":0,"BASE":0}
@@ -75,12 +78,12 @@ Luminea ZX-2831 {"NAME":"Luminea CCT","GPIO":[0,0,0,0,140,37,0,0,38,142
LVWIT A60 6.5W 806lm Filament {"NAME":"LVWIT-E27-WiFi-6.5","GPIO":[0,0,0,0,0,0,0,0,38,0,37,0,0],"FLAG":0,"BASE":18}
Merkury MI-BW905-999W 700lm {"NAME":"MI-BW905-999W","GPIO":[0,0,0,0,0,37,0,0,38,0,0,0,0],"FLAG":0,"BASE":18}
Mimoodz A21 10.5W 1050lm {"NAME":"Mimoodz","GPIO":[0,0,0,0,21,22,0,0,23,24,25,26,27],"FLAG":0,"BASE":18}
+Mirabella Genio 6W 500lm {"NAME":"GenioGU10","GPIO":[0,0,0,0,0,0,0,0,38,0,37,0,0],"FLAG":0,"BASE":18}
Mirabella Genio 9W 800lm {"NAME":"GenioBulbCCT","GPIO":[0,0,0,0,0,37,0,0,0,38,0,0,0],"FLAG":0,"BASE":18}
Mirabella Genio 9W 800lm {"NAME":"GenioBulbCCT","GPIO":[0,0,0,0,0,37,0,0,0,38,0,0,0],"FLAG":0,"BASE":18}
Mirabella Genio 9W 800lm {"NAME":"GenioBulbCCT","GPIO":[0,0,0,0,0,37,0,0,0,38,0,0,0],"FLAG":0,"BASE":18}
-Mirabella Genio I002745 SES 470lm {"NAME":"Mirabella Candle","GPIO":[0,0,0,0,0,0,0,0,38,0,37,0,0],"FLAG":0,"BASE":18}
-Mirabella Genio I002746 500lm {"NAME":"GenioGU10","GPIO":[0,0,0,0,0,0,0,0,38,0,37,0,0],"FLAG":0,"BASE":18}
-Mirabella Genio I002747 G95 470lm {"NAME":"Genio Filament","GPIO":[0,0,0,0,0,0,0,0,38,0,37,0,0],"FLAG":0,"BASE":18}
+Mirabella Genio G95 5W 470lm {"NAME":"Genio Filament","GPIO":[0,0,0,0,0,0,0,0,38,0,37,0,0],"FLAG":0,"BASE":18}
+Mirabella Genio SES 5.5W 470lm {"NAME":"Mirabella Candle","GPIO":[0,0,0,0,0,0,0,0,38,0,37,0,0],"FLAG":0,"BASE":18}
Nedis A60 5.5W 350lm Twisted Filament {"NAME":"WIFILT10GDA60","GPIO":[0,0,0,0,0,0,0,0,38,0,37,0,0],"FLAG":0,"BASE":18}
Nedis A60 800lm {"NAME":"WIFILW10WTE27","GPIO":[0,0,0,0,0,37,0,0,38,0,0,0,0],"FLAG":0,"BASE":18}
Nedis C10 350lm {"NAME":"WIFILW10WTE14","GPIO":[0,0,0,0,0,37,0,0,0,38,0,0,0],"FLAG":0,"BASE":18}
@@ -89,7 +92,9 @@ Nedis PAR16 330lm {"NAME":"Nedis WIFILW30","GPIO":[0,0,0,0,0,37,0,0,38,0,
Nedis PAR16 4.5W 330lm 110C {"NAME":"WIFILW30","GPIO":[0,0,0,0,0,37,0,0,38,0,0,0,0],"FLAG":0,"BASE":18}
Philips Zhirui Candle 250lm {"NAME":"Xiaomi Philips","GPIO":[0,0,0,0,0,0,0,0,38,0,0,37,0],"FLAG":0,"BASE":48}
Phillips Zhirui 450lm {"NAME":"Xiaomi Philips","GPIO":[0,0,0,0,0,0,0,0,38,0,0,37,0],"FLAG":0,"BASE":48}
+Polux ST64 5.5W 470lm {"NAME":"basic","GPIO":[0,0,0,0,0,0,0,0,38,0,37,0,0],"FLAG":0,"BASE":18}
SmartDGM L-WT9W1 9W 800lm {"NAME":"L-WT9W1","GPIO":[0,0,0,0,0,37,0,0,9,38,0,0,0],"FLAG":0,"BASE":18}
+Spectrum Smart 5W 410lm Candle {"NAME":"lightbulb","GPIO":[0,0,0,0,0,0,0,0,38,0,37,0,0],"FLAG":0,"BASE":18}
Swisstone SH 330 806lm {"NAME":"SwisstoneSH330","GPIO":[0,0,0,0,140,37,0,0,38,142,141,0,0],"FLAG":0,"BASE":18}
V-Tac PAR16 4.5W 300lm 110C {"NAME":"V-TAC VT-5174","GPIO":[0,0,0,0,0,0,0,0,38,0,37,0,0],"FLAG":0,"BASE":18}
Vestaiot BR30 800lm {"NAME":"Vesta BR30 CCT","GPIO":[0,0,0,0,0,37,0,0,0,38,0,0,0],"FLAG":0,"BASE":18}
@@ -97,6 +102,36 @@ Wipro Garnet NS9100 810lm {"NAME":"WiproSmartBulb","GPIO":[0,0,0,0,38,0,0,0,37,
Wyze WLPA19 A19 800lm {"NAME":"Wyze Bulb","GPIO":[0,0,0,0,0,0,0,0,0,37,38,0,0],"FLAG":0,"BASE":48}
```
+## Ceiling Light
+```
+BAZZ 14" RGBCCT {"NAME":"WF19129W","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":0,"BASE":18}
+BlitzWolf 24W {"NAME":"BW-LT20","GPIO":[0,0,0,0,0,37,0,0,0,38,0,0,0],"FLAG":15,"BASE":18}
+BrilliantSmart 12W 290mm Salisbury CCT {"NAME":"Brilliant Smart Salisbury","GPIO":[0,0,0,0,0,0,0,0,47,0,37,0,0],"FLAG":0,"BASE":18}
+BrilliantSmart 24W 410mm Salisbury CCT {"NAME":"Salisbury","GPIO":[0,0,0,0,0,0,0,0,47,0,37,0,0],"FLAG":0,"BASE":48}
+BrilliantSmart Cordia 24W CCT {"NAME":"Cordia","GPIO":[0,0,0,0,0,37,0,0,38,0,0,0,0],"FLAG":0,"BASE":18}
+Calex 429250 {"NAME":"Calex_LED","GPIO":[0,0,0,0,0,0,0,0,0,47,37,0,0],"FLAG":0,"BASE":18}
+Fcmila 48W RGBCCT {"NAME":"XDD-48W","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":0,"BASE":18}
+LE lampUX 15W RGBCCT {"NAME":"LE lampUX 15W","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":0,"BASE":18}
+Lohas ZN026CL10 RGBCCT {"NAME":"Lohas LED Lamp","GPIO":[0,0,0,0,38,37,0,0,40,39,41,0,0],"FLAG":0,"BASE":18}
+LSC 20W 1400lm White Ambiance {"NAME":"LSC RGBCW LED","GPIO":[0,0,0,0,0,0,0,0,181,0,180,0,0],"FLAG":0,"BASE":18}
+Luminea 24W CCT {"NAME":"Luminea NX6205-944","GPIO":[0,0,0,0,0,0,0,0,47,0,37,0,0],"FLAG":0,"BASE":48}
+LVL 300mm Round {"NAME":"LVL 300m Round 24W Ceiling LED","GPIO":[0,0,0,0,0,37,0,0,0,47,0,0,0],"FLAG":0,"BASE":48}
+SMRTLite LED Panel {"NAME":"SMRTLite","GPIO":[0,0,0,0,38,37,0,0,41,39,40,0,0],"FLAG":0,"BASE":18}
+Utorch 24W 2000lm CCT {"NAME":"Utorch PZE-911","GPIO":[0,0,0,0,0,0,0,0,38,0,37,0,0],"FLAG":0,"BASE":1}
+Utorch 24W 2000lm CCT {"NAME":"Utorch UT40","GPIO":[0,0,0,0,0,0,0,0,38,0,37,0,0],"FLAG":0,"BASE":1}
+Verve Design Angie 18W RGB Ring {"NAME":"ACL12HA Light","GPIO":[0,0,0,0,38,37,0,0,41,39,40,0,0],"FLAG":0,"BASE":18}
+Verve Design Charlie 22W CCT {"NAME":"Verve ACL01HA","GPIO":[0,0,0,0,0,37,0,0,0,47,0,0,0],"FLAG":0,"BASE":48}
+Verve Design Hana 24W CCT {"NAME":"Verve ACL03HA","GPIO":[0,0,0,0,0,0,0,0,38,0,37,0,0],"FLAG":0,"BASE":48}
+```
+
+## Contact Sensor
+```
+Digoo DG-ZXD21 Door Detector {"NAME":"Digoo ZXD21","GPIO":[0,107,0,108,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":54}
+Earykong TYMC-1 Door Window {"NAME":"TYMC-1","GPIO":[0,107,0,108,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":54}
+TY01 Door Window {"NAME":"TY01","GPIO":[0,107,0,108,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":54}
+Zemismart Door Window {"NAME":"Zemismart","GPIO":[255,107,255,108,255,255,0,0,255,255,255,255,255],"FLAG":0,"BASE":54}
+```
+
## Curtain Switch
```
Anccy Relax {"NAME":"Tuya Shutter","GPIO":[157,0,54,10,22,19,0,0,17,21,53,23,52],"FLAG":0,"BASE":18}
@@ -123,11 +158,10 @@ ESP-01(S) {"NAME":"ESP01","GPIO":[255,255,255,255,0,0,0,0,0,0,0,0
ESP-M3 {"NAME":"ESP-M3","GPIO":[255,255,255,255,255,0,0,0,0,255,255,0,255],"FLAG":0,"BASE":18}
Heltec WiFi Kit 8 {"NAME":"HTIT-W8266","GPIO":[255,255,255,255,6,5,0,0,255,255,255,255,162],"FLAG":15,"BASE":18}
LC Tech relay and PZEM-004T {"NAME":"HW-655 PZEM","GPIO":[0,63,0,62,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":18}
-Luani HVIO {"NAME":"Luani HVIO","GPIO":[0,255,255,255,21,22,0,0,9,10,255,52,0],"FLAG":1,"BASE":35}
+LC Technology PSF-B04 Ewelink 4 Channel Switch Module {"NAME":"LC-EWL-B04-MB","GPIO":[255,255,255,255,255,255,0,0,255,52,255,255,0],"FLAG":0,"BASE":18}
OLED Display Module 0.66" for Wemos D1 Mini {"NAME":"OLED 64x48","GPIO":[255,255,255,255,6,5,0,0,255,255,255,255,162],"FLAG":15,"BASE":18}
QuinLED 2 Channel {"NAME":"QuinLED 2 channel","GPIO":[37,0,38,0,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":18}
-Splatura USB Device Power Switch {"NAME":"Splatura USB","GPIO":[0,0,52,0,0,0,0,0,0,21,0,122,0],"FLAG":0,"BASE":18}
-SUPLA inCan by Espablo {"NAME":"Supla Espablo","GPIO":[0,255,4,255,17,21,0,0,255,22,255,0,52],"FLAG":1,"BASE":31}
+WifInfo - Teleinfo Server {"NAME":"WifInfo","GPIO":[7,255,255,210,6,5,255,255,255,255,255,255,255],"FLAG":15,"BASE":18}
Witty Cloud {"NAME":"Witty Cloud","GPIO":[255,255,56,255,17,255,0,0,38,39,255,37,255],"FLAG":1,"BASE":32}
Yison ESP-01/ESP-202 Development Board {"NAME":"Yison Dev Board","GPIO":[32,157,31,255,33,34,255,255,37,39,30,38,29],"FLAG":15,"BASE":18}
```
@@ -141,7 +175,6 @@ Arlec Smart 4W 380lm Candle {"NAME":"Arlec Bulb 4W","GPIO":[0,0,0,0,0,0,0,0,0,0
Arlec Smart 9W 950lm 4000K {"NAME":"Arlec-GLD124HA","GPIO":[0,0,0,0,0,37,0,0,0,0,0,0,0],"FLAG":0,"BASE":18}
Avatar ALS15L Candle {"NAME":"AVATAR","GPIO":[0,0,0,0,0,0,0,0,0,143,0,144,0],"FLAG":0,"BASE":27}
Cleverio 51396 800lm {"NAME":"Cleverio E27","GPIO":[0,0,0,0,0,0,0,0,0,0,46,0,0],"FLAG":0,"BASE":18}
-Connect Smart 10W {"NAME":"CSH-B22WW10W","GPIO":[0,0,0,0,0,37,0,0,0,38,0,0,0],"FLAG":0,"BASE":18}
Digma DiLight E27 W1 {"NAME":"DiLight E27 W1","GPIO":[0,0,0,0,37,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":18}
DORESshop B11 600lm Filament {"NAME":"Doresshop-cand","GPIO":[0,0,0,0,0,0,0,0,0,0,37,0,0],"FLAG":0,"BASE":18}
DORESshop ST64 720lm Filament {"NAME":"DORESshop-ST64","GPIO":[0,0,0,0,0,0,0,0,38,0,37,0,0],"FLAG":0,"BASE":18}
@@ -154,6 +187,7 @@ Geeni LUX Edison {"NAME":"Geeni-Edison","GPIO":[0,0,0,0,0,0,0,0,0,0,37,0
Globe A19 10W 800lm {"NAME":"Globe WW 800lm","GPIO":[0,0,0,0,0,37,0,0,0,0,0,0,0],"FLAG":0,"BASE":18}
Gosund A19 8W 2700K {"NAME":"Gosund Wifi Dimmable Bulb","GPIO":[0,0,0,0,37,0,0,0,0,52,0,0,0],"FLAG":0,"BASE":18}
Hama 7W 900lm Filament {"NAME":"Hama Filament","GPIO":[255,255,255,255,255,255,255,255,255,255,46,255,255],"FLAG":15,"BASE":18}
+Jetstream 9W 800lm {"NAME":"Jetstream MA19WH","GPIO":[0,0,0,0,37,0,0,0,0,52,0,0,0],"FLAG":0,"BASE":18}
KMC 70113 A19 7.5W {"NAME":"Generic","GPIO":[0,0,0,0,0,0,37,38,0,0,0,0,0],"FLAG":15,"BASE":18}
Kogan ST-20 Filament {"NAME":"Kogan Filament","GPIO":[0,0,0,0,0,0,0,0,0,0,37,0,0],"FLAG":15,"BASE":18}
Krisbow Bohlam 14W {"NAME":"Krisbow SmartL","GPIO":[0,0,0,0,0,37,0,0,0,38,0,0,0],"FLAG":0,"BASE":48}
@@ -165,23 +199,29 @@ LSC Filament A60 {"NAME":"LSC Filam E27","GPIO":[0,0,0,0,0,0,0,0,38,0,37
LSC Filament C35 {"NAME":"LSC Filam E14","GPIO":[0,255,0,255,0,0,0,0,38,0,37,0,0],"FLAG":15,"BASE":18}
LSC Filament G125 {"NAME":"LSC Filam Huge","GPIO":[0,0,0,0,0,0,0,0,38,0,37,0,0],"FLAG":0,"BASE":18}
LSC Filament ST64 5.5W 470lm {"NAME":"LSC Filam Big","GPIO":[0,0,0,0,0,0,0,0,38,0,37,0,0],"FLAG":0,"BASE":18}
+Luedd 7W 800lm Filament {"NAME":"Luedd E27","GPIO":[0,0,0,0,0,0,0,0,0,0,46,0,0],"FLAG":0,"BASE":18}
Lumary 6W 700lm Edison {"NAME":"Lumary TS3Y","GPIO":[0,0,0,0,0,0,0,0,0,37,0,0,0],"FLAG":0,"BASE":18}
Luminea ZX-2880 A60 800lm {"NAME":"LAV-110.w","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":0,"BASE":18}
Luminea ZX-2982 ST64 Filament {"NAME":"Luminea ZX2982","GPIO":[0,0,0,0,0,0,0,0,0,0,37,0,0],"FLAG":0,"BASE":18}
+MagicHome 7.5W Warm White {"NAME":"ZJ-8C2B-CCT-AV1.1","GPIO":[0,0,0,0,0,0,0,0,37,0,0,0,0],"FLAG":0,"BASE":18}
Merkury 9W 800lm {"NAME":"MI-BW320-999W","GPIO":[0,0,0,0,0,0,0,0,0,0,21,0,0],"FLAG":0,"BASE":18}
Merkury MI-BW902-999W 800lm {"NAME":"MI-BW902-999W","GPIO":[0,0,0,0,0,37,0,0,0,0,0,0,0],"FLAG":0,"BASE":18}
Merkury MI-BW942-999W 800lm {"NAME":"MI-BW942-999W","GPIO":[0,0,0,0,37,0,0,0,0,52,0,0,0],"FLAG":0,"BASE":18}
Merkury MIC-BW902-999W {"NAME":"MI-BW902-999W","GPIO":[0,0,0,0,0,0,0,0,0,0,37,0,0],"FLAG":0,"BASE":18}
Merkury Vintage Edison A19 {"NAME":"Merkury A19 Ed","GPIO":[0,0,0,0,0,0,0,0,0,0,37,0,0],"FLAG":0,"BASE":18}
Mirabella Genio 800lm {"NAME":"GenioBulbCW","GPIO":[0,0,0,0,38,37,0,0,0,40,39,0,0],"FLAG":0,"BASE":18}
-Mirabella Genio 9W 800lm {"NAME":"GenioB22","GPIO":[0,0,0,0,0,0,0,0,0,37,0,0,0],"FLAG":0,"BASE":18}
+Mirabella Genio 9W 800lm {"NAME":"GenioB22","GPIO":[0,0,0,0,0,0,0,0,0,0,37,0,0],"FLAG":0,"BASE":18}
+Mirabella Genio 9W 800lm {"NAME":"Mirabella Genio I002606","GPIO":[0,0,0,0,0,0,0,0,0,0,37,0,0],"FLAG":0,"BASE":18}
+Mirabella Genio 9W 800lm WW {"NAME":"Genio WW","GPIO":[0,0,0,0,0,0,0,0,0,37,0,0,0],"FLAG":0,"BASE":18}
Mirabella Genio A70 1400lm {"NAME":"GenioB_CW2744","GPIO":[0,0,0,0,0,37,0,0,0,0,0,0,0],"FLAG":0,"BASE":18}
+Nedis A60 5W 500lm {"NAME":"WIFILF11WTA60","GPIO":[0,0,0,0,0,0,0,0,0,0,37,0,0],"FLAG":0,"BASE":18}
Nedis A60 Filament {"NAME":"WIFILF10WTA60","GPIO":[0,0,0,0,0,0,0,0,0,0,37,0,0],"FLAG":0,"BASE":18}
Nedis A60 Warm White 9W 800lm {"NAME":"WIFILW11WTE27","GPIO":[0,0,0,0,0,37,0,0,0,0,0,0,0],"FLAG":0,"BASE":18}
Nedis G125 Filament {"NAME":"WIFILF10GDG125","GPIO":[0,0,0,0,0,0,0,0,0,0,37,0,0],"FLAG":0,"BASE":18}
Nedis PAR16 330lm {"NAME":"Nedis WIFILW31","GPIO":[0,0,0,0,0,37,0,0,0,0,0,0,0],"FLAG":0,"BASE":18}
Sealight Vintage Edison A19 {"NAME":"SealightEdison","GPIO":[0,0,0,0,0,37,0,0,0,0,0,0,0],"FLAG":0,"BASE":18}
TCP Smart 810lm Filament {"NAME":"TCP Filament","GPIO":[0,0,0,0,0,0,0,0,0,0,46,0,0],"FLAG":0,"BASE":18}
+TCP Smart 810lm Filament {"NAME":"TCP Filament","GPIO":[0,0,0,0,0,0,0,0,0,0,46,0,0],"FLAG":0,"BASE":18}
Xiaomi Philips MUE4088RT {"NAME":"Xiaomi Philips","GPIO":[0,0,0,0,0,0,0,0,0,0,0,37,0],"FLAG":0,"BASE":18}
```
@@ -196,7 +236,8 @@ BrilliantSmart Jupiter {"NAME":"BrSm Jupiter","GPIO":[0,107,0,108,0,0,0,0,0,0,
CE Smart Home {"NAME":"CE-WF500D","GPIO":[0,0,0,0,0,0,0,0,0,108,0,107,0],"FLAG":0,"BASE":54}
CE Smart Home CFW500D-3W 3 Way {"NAME":"CE-WF500D-3W","GPIO":[0,0,0,0,0,0,0,0,0,108,0,107,0],"FLAG":0,"BASE":54}
CNSKOU Touch {"NAME":"CNSKOU Dimmer Switch","GPIO":[0,0,0,0,0,0,0,0,0,0,54,0,0],"FLAG":0,"BASE":54}
-Eva Logik WF31 {"NAME":"WF31 Dimmer","GPIO":[255,107,255,108,255,255,0,0,255,255,255,255,255],"FLAG":0,"BASE":54}
+Eva Logik {"NAME":"WF31 Dimmer","GPIO":[255,107,255,108,255,255,0,0,255,255,255,255,255],"FLAG":0,"BASE":54}
+Eva Logik 3 Way {"NAME":"EL WF31T","GPIO":[255,107,255,108,255,255,0,0,255,255,255,255,255],"FLAG":0,"BASE":54}
EX-Store 2 Kanal RS232 V4 {"NAME":"EXS Dimmer","GPIO":[0,148,0,149,0,0,0,0,0,183,0,0,0],"FLAG":0,"BASE":72}
Feit Electric DIM/WIFI {"NAME":"Generic","GPIO":[255,107,255,108,255,255,0,0,255,0,255,0,255],"FLAG":0,"BASE":54}
Gosund SW2 {"NAME":"Gosund Dimmer","GPIO":[255,148,255,149,17,0,255,255,56,158,37,255,255],"FLAG":0,"BASE":18}
@@ -213,11 +254,18 @@ Tessan MJ-SD02 {"NAME":"MJ-SD02","GPIO":[19,18,0,59,158,58,0,0,57,37,5
TopGreener TGWF500D {"NAME":"TopGreener-Dimmer","GPIO":[0,0,0,0,0,0,0,0,0,108,0,107,0],"FLAG":0,"BASE":54}
TreatLife DS01 {"NAME":"DS02S Dimmer","GPIO":[0,107,0,108,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":54}
TreatLife DS02S {"NAME":"DS02S Dimmer","GPIO":[0,107,0,108,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":54}
+TreatLife Light and Fan {"NAME":"DS03 Fan/Light","GPIO":[0,107,0,108,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":54}
WF-DS01 {"NAME":"Dimmer WF-DS01","GPIO":[255,255,255,255,255,255,0,0,255,255,54,255,255],"FLAG":0,"BASE":54}
WiFi Dimmer Switch {"NAME":"PS-16-DZ","GPIO":[0,148,0,149,0,0,0,0,0,52,0,0,0],"FLAG":0,"BASE":58}
Zemismart KS-7011 {"NAME":"KS-7011 Dimmer","GPIO":[255,107,255,108,255,255,0,0,255,255,255,255,255],"FLAG":0,"BASE":54}
```
+## Energy Meter
+```
+Hiking Single Phase 65A Din Rail {"NAME":"hiking dds2382wifi","GPIO":[0,107,0,108,0,0,0,0,0,0,56,0,17],"FLAG":0,"BASE":1}
+ZMAi-90 Digital {"NAME":"ZMAi-90","GPIO":[0,148,0,149,0,0,0,0,21,90,0,0,0],"FLAG":0,"BASE":18}
+```
+
## Fan
```
Anko HEGSM40 {"NAME":"Anko HEGSM40","GPIO":[0,0,0,0,0,0,0,0,0,108,0,107,0],"FLAG":0,"BASE":54}
@@ -233,6 +281,11 @@ Technical Pro FXA16 {"NAME":"FXA16 Fan","GPIO":[0,107,0,108,0,0,0,0,0,0,0,0
Zemismart Bladeless {"NAME":"Bladeless Fan","GPIO":[255,107,255,108,255,255,0,0,255,255,255,255,255],"FLAG":0,"BASE":54}
```
+## Gas Sensor
+```
+Natural Gas (CH4) Alarm {"NAME":"PA-210WYS","GPIO":[255,107,255,108,255,255,0,0,255,255,255,255,255],"FLAG":0,"BASE":54}
+```
+
## IR Bridge
```
A1 Universal Remote Control {"NAME":"A1 IR Bridge","GPIO":[255,255,255,255,56,51,0,0,0,17,8,0,0],"FLAG":0,"BASE":62}
@@ -240,7 +293,7 @@ Alfawise KS1 {"NAME":"KS1","GPIO":[255,71,17,72,17,51,0,0,56,0,8,0,0
auvisio S06 {"NAME":"NX-4519-675","GPIO":[255,255,255,255,52,51,0,0,255,255,8,255,255],"FLAG":1,"BASE":18}
BlitzWolf BW-RC1 Smart IR Controller {"NAME":"BW-RC1","GPIO":[0,0,0,0,56,51,0,0,0,17,8,0,0],"FLAG":0,"BASE":62}
Connect SmartHome Universal Smart IR Remote {"NAME":"CSH IR Bridge","GPIO":[255,255,255,255,56,51,0,0,0,17,8,0,0],"FLAG":0,"BASE":62}
-Cusam CS-IRC-1 {"NAME":"YTF IR Bridge","GPIO":[255,255,255,255,56,51,0,0,0,17,8,0,0],"FLAG":0,"BASE":62}
+Cusam CS-IRC-1 {"NAME":"YTF IR Bridge","GPIO":[255,255,255,255,52,51,0,0,0,17,8,0,0],"FLAG":0,"BASE":62}
Eachen IR DC6 {"NAME":"Eachen IR","GPIO":[0,0,0,0,56,51,0,0,255,17,8,0,0],"FLAG":0,"BASE":18}
Geeklink GK01 {"NAME":"GL IR Blaster","GPIO":[255,255,255,255,56,51,0,0,0,17,8,0,0],"FLAG":0,"BASE":62}
Jinvoo AC/TV Box Controller {"NAME":"Jinvoo IR Bridge","GPIO":[255,255,255,255,56,51,0,0,0,17,8,0,0],"FLAG":0,"BASE":62}
@@ -283,10 +336,10 @@ Luminea ZX-2844 {"NAME":"Luminea ZX-284","GPIO":[40,0,0,0,0,39,0,0,38,1
Luminea ZX-2844-675 {"NAME":"ZX-2844-675","GPIO":[17,0,0,0,38,40,0,0,37,0,39,0,0],"FLAG":0,"BASE":18}
Lustreon {"NAME":"Lustreon WiFi ","GPIO":[17,0,55,0,37,40,0,0,38,41,39,0,0],"FLAG":0,"BASE":38}
Magic UFO RGBW {"NAME":"Magic UFO","GPIO":[17,0,157,0,0,37,0,0,39,40,38,56,0],"FLAG":0,"BASE":18}
-MagicHome RGB ZJ-WFMN-B V1.1 {"NAME":"MagicHome RGB","GPIO":[0,0,0,0,0,37,0,0,38,39,0,0,0],"FLAG":0,"BASE":34}
-MagicHome RGBW ESP-IR-B-v2.3 {"NAME":"ESP-IR-B-v2.3","GPIO":[0,0,51,0,0,38,0,0,37,39,0,40,0],"FLAG":0,"BASE":18}
-MagicHome RGBW RF {"NAME":"MagicHome RF","GPIO":[0,0,0,0,0,38,0,0,39,40,37,0,0],"FLAG":0,"BASE":18}
-MagicHome RGBW ZJ-WFMN-A V1.1 {"NAME":"MagicHome RGBW","GPIO":[0,0,0,0,51,38,0,0,37,39,0,40,0],"FLAG":0,"BASE":34}
+MagicHome RGB ZJ-WFMN-A V1.1 {"NAME":"MagicHome RGB","GPIO":[0,0,0,0,0,37,0,0,38,39,0,0,0],"FLAG":0,"BASE":34}
+MagicHome RGBW {"NAME":"ESP-IR-B-v2.3","GPIO":[0,0,51,0,0,38,0,0,37,39,0,40,0],"FLAG":0,"BASE":18}
+MagicHome RGBW RF {"NAME":"MagicHome RGBW RF","GPIO":[0,0,56,0,147,38,0,0,39,40,37,159,0],"FLAG":0,"BASE":18}
+MagicHome RGBW ZJ-WFMN-B V1.1 {"NAME":"MagicHome RGBW RF (ZJ-WFMN-B V1.1)","GPIO":[0,0,0,0,147,38,0,0,39,40,37,0,159],"FLAG":0,"BASE":34}
MagicHome RGBWW w/ RF {"NAME":"MagicHome RF","GPIO":[0,0,159,0,37,38,0,0,41,40,39,147,0],"FLAG":0,"BASE":38}
MagicHome RGBWW w/ RF {"NAME":"MagicHome RF","GPIO":[0,0,0,0,147,40,0,0,38,39,37,41,159],"FLAG":0,"BASE":18}
MagicHome Single Color 5-28V {"NAME":"MagicHome","GPIO":[0,0,0,0,0,0,0,0,37,0,0,0,0],"FLAG":0,"BASE":18}
@@ -308,8 +361,10 @@ Cocoon Smart {"NAME":"Cocoon Smart","GPIO":[17,0,0,0,37,0,0,0,38,0,3
Deltaco 3m RGBCCT {"NAME":"Deltaco Led Strip","GPIO":[0,0,0,0,37,17,0,0,38,0,39,0,0],"FLAG":0,"BASE":18}
electriQ 3m RGBCCT {"NAME":"ElectricQ wifiRGBWLEDSTR","GPIO":[0,0,0,0,37,41,0,0,38,40,39,0,0],"FLAG":0,"BASE":18}
Elfeland 10m RGB {"NAME":"Elfeland RGB","GPIO":[0,0,0,0,0,38,0,0,39,51,0,37,0],"FLAG":0,"BASE":18}
+Energizer Multi-Color 6.5ft {"NAME":"Energizer","GPIO":[0,0,0,0,0,38,0,0,39,0,0,37,0],"FLAG":0,"BASE":18}
Geeni Prisma Plus {"NAME":"Geeni Prisma Plus Strip","GPIO":[17,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":0,"BASE":18}
Gosund 2.8m RGB {"NAME":"Gosund LED Strip","GPIO":[17,0,0,0,0,0,0,0,37,39,38,0,0],"FLAG":3,"BASE":18}
+Gosund SL2 5m RGB {"NAME":"Gosund LED Str","GPIO":[0,0,0,0,17,38,0,0,37,39,0,0,0],"FLAG":3,"BASE":18}
HitLights L1012V-MC1 {"NAME":"HitLights RBG","GPIO":[17,0,0,0,37,40,0,0,38,0,39,0,0],"FLAG":0,"BASE":18}
HomeMate 10m RGB {"NAME":"Homemate Strip","GPIO":[0,0,0,0,0,37,0,0,39,17,38,0,0],"FLAG":0,"BASE":18}
Hykker 3m RGB {"NAME":"HYKKER Strip","GPIO":[0,0,0,0,0,37,0,0,39,17,38,0,0],"FLAG":0,"BASE":18}
@@ -321,77 +376,64 @@ LE LampUX 5m RGB {"NAME":"LampUX","GPIO":[17,0,0,0,0,38,0,0,39,0,0,37,0]
LE LampUX 5m RGB {"NAME":"LE LampUx","GPIO":[0,0,0,0,0,38,0,0,39,0,0,37,0],"FLAG":0,"BASE":34}
LE lampUX 5m RGBW {"NAME":"LampUX","GPIO":[0,0,17,0,0,38,0,0,39,0,40,37,0],"FLAG":0,"BASE":18}
Lohas ZN022 5m RGBW {"NAME":"LOHAS M5-022","GPIO":[0,0,0,0,38,37,0,0,17,39,0,0,0],"FLAG":0,"BASE":18}
-LSC RGBW {"NAME":"LSC RGBW Strip","GPIO":[51,0,0,0,37,0,0,0,38,40,39,0,0],"FLAG":0,"BASE":18}
+LSC RGBW {"NAME":"LSC RGBW Strip","GPIO":[51,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":0,"BASE":18}
Lumary RGBCCT {"NAME":"Lumary LED","GPIO":[17,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":0,"BASE":18}
Lumary RGBCCT {"NAME":"Lumary LED","GPIO":[17,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":0,"BASE":18}
Maxonar Lightstrip Pro XS-SLD001 {"NAME":"Maxonar LED","GPIO":[0,0,0,0,0,37,0,0,39,17,38,0,0],"FLAG":0,"BASE":18}
Merkury Innovations MI-EW003-999W {"NAME":"MI-EW003-999W ","GPIO":[17,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":0,"BASE":18}
-Mirabella Genio I002579 {"NAME":"MirabellaStrip","GPIO":[17,0,0,0,37,40,0,0,38,0,39,0,0],"FLAG":0,"BASE":18}
+Mirabella Genio RGB+CW {"NAME":"MirabellaStrip","GPIO":[17,0,0,0,37,40,0,0,38,0,39,0,0],"FLAG":0,"BASE":18}
Monster Smart IlluminEssence {"NAME":"MI-EW003-999W ","GPIO":[17,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":0,"BASE":18}
+Polux RGB+NW 2m {"NAME":"Polux Wi-Fi SM","GPIO":[17,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":0,"BASE":37}
Powertech 5m RGBW {"NAME":"Jaycar ST3992 LED Strip","GPIO":[122,0,0,0,37,40,0,0,38,0,39,0,0],"FLAG":0,"BASE":18}
+Reafoo RGBW 5m {"NAME":"REAFOO RGBW LS","GPIO":[17,0,0,0,37,40,0,0,38,0,39,0,0],"FLAG":0,"BASE":18}
Sonoff L1 {"NAME":"SonoffL1","GPIO":[0,148,0,149,0,0,0,0,0,56,0,0,0],"FLAG":0,"BASE":70}
Teckin SL02 {"NAME":"Teckin SL02","GPIO":[51,0,0,0,37,0,0,0,38,0,39,0,0],"FLAG":0,"BASE":52}
Teckin SL07 32.8ft RGB {"NAME":"WIFI RGB","GPIO":[51,0,0,0,37,0,0,0,38,0,39,0,0],"FLAG":0,"BASE":18}
TORCHSTAR CCT 30W Lighting Kit {"NAME":"Torchstar CCT ","GPIO":[0,0,0,0,52,38,0,0,0,17,0,37,53],"FLAG":0,"BASE":18}
Torchstar Safe Lighting Kit {"NAME":"Torchstar","GPIO":[0,0,0,0,52,0,0,0,0,0,0,37,53],"FLAG":1,"BASE":18}
-Woox 5m RGBW {"NAME":"GardenLedstrip1","GPIO":[0,0,0,0,0,38,0,0,39,9,37,0,0],"FLAG":0,"BASE":18}
+WOOX 5m RGBW {"NAME":"GardenLedstrip1","GPIO":[0,0,0,0,0,38,0,0,39,9,37,0,0],"FLAG":0,"BASE":18}
Zemismart 3m Extendable RGBW {"NAME":"Zemismart LED","GPIO":[0,0,0,0,38,37,0,0,0,39,40,0,0],"FLAG":0,"BASE":18}
```
## Light
```
+Arlec Smart 10W LED Bunker {"NAME":"DetaBulkhead","GPIO":[0,0,0,0,0,0,0,0,0,0,37,0,0],"FLAG":0,"BASE":18}
Arlec Smart 15W Security Floodlight {"NAME":"ArlecFlood","GPIO":[0,0,0,0,0,0,0,0,0,0,37,0,0],"FLAG":15,"BASE":18}
Arlec Smart 20W Movement Activated Security {"NAME":"Arlec MAL300HA","GPIO":[0,0,0,0,21,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":18}
Arlec Smart 40W 4000lm LED Batten {"NAME":"Arlec Batten","GPIO":[0,0,0,0,0,37,0,0,0,0,0,0,0],"FLAG":0,"BASE":18}
Arlec Smart Portable Floodlight 10.5W {"NAME":"Arlec GLD301HA","GPIO":[0,0,0,0,0,37,0,0,0,0,0,0,0],"FLAG":0,"BASE":18}
Arlec Smart Up & Down LED Wall {"NAME":"Arlec Up Down CCT","GPIO":[0,0,0,0,0,37,0,0,0,38,0,0,0],"FLAG":0,"BASE":18}
-BAZZ 14" RGBCCT Ceiling Fixture {"NAME":"WF19129W","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":0,"BASE":18}
BAZZ 4 in. RGB Recessed Fixture {"NAME":"SLMR4RGBWWFW","GPIO":[0,0,0,0,38,37,0,0,41,39,40,0,0],"FLAG":0,"BASE":18}
-BlitzWolf BW-LT20 {"NAME":"BW-LT20","GPIO":[0,0,0,0,0,37,0,0,0,38,0,0,0],"FLAG":15,"BASE":18}
Brelong USB Charging Night {"NAME":"Brelong Smart USB Charging Lamp","GPIO":[0,0,0,0,37,0,0,0,38,0,39,0,40],"FLAG":0,"BASE":18}
-Brilex Nightstand Lamp {"NAME":"Smart Table La","GPIO":[0,0,18,0,37,157,0,0,38,17,39,0,40],"FLAG":0,"BASE":18}
-BrilliantSmart 12W 290mm Salisbury CCT Ceiling {"NAME":"Brilliant Smart Salisbury","GPIO":[0,0,0,0,0,0,0,0,47,0,37,0,0],"FLAG":0,"BASE":18}
+Brilex Nightstand Lamp {"NAME":"Smart Table Lamp","GPIO":[0,0,18,0,37,157,0,0,38,17,39,0,40],"FLAG":0,"BASE":18}
BrilliantSmart 20695 Downlight CCT {"NAME":"SmartCCTDwnLgt","GPIO":[0,0,0,0,0,37,0,0,0,38,0,0,0],"FLAG":0,"BASE":48}
-BrilliantSmart 24W 410mm Salisbury CCT Ceiling {"NAME":"Salisbury","GPIO":[0,0,0,0,0,0,0,0,47,0,37,0,0],"FLAG":0,"BASE":48}
-BrilliantSmart Cordia 24W CCT Ceiling {"NAME":"Cordia","GPIO":[0,0,0,0,0,37,0,0,38,0,0,0,0],"FLAG":0,"BASE":18}
BrilliantSmart Prism LED RGBCCT Downlight {"NAME":"Prism","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":0,"BASE":18}
BrilliantSmart RGB Garden Kit {"NAME":"Brilliant Gard","GPIO":[0,0,0,0,37,0,0,0,38,0,39,0,0],"FLAG":0,"BASE":18}
-Calex 429250 Ceiling {"NAME":"Calex_LED","GPIO":[0,0,0,0,0,0,0,0,0,47,37,0,0],"FLAG":0,"BASE":18}
-Connect SmartHome CSH-240RGB10W {"NAME":"Connect1","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":0,"BASE":18}
+Connect SmartHome CSH-240RGB10W {"NAME":"CSH-240RGB10W","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":0,"BASE":18}
Connect SmartHome CSH-FSTN12 {"NAME":"CSH-FSTN12","GPIO":[0,0,0,0,37,0,0,0,38,0,39,0,0],"FLAG":0,"BASE":18}
Deta 18W 1900lm T8 Tube {"NAME":"DETA Smart LED","GPIO":[0,0,0,0,0,0,0,0,0,0,37,0,0],"FLAG":0,"BASE":18}
Deta DET902HA 10W 940lm RGB+CCT {"NAME":"Deta DownLight","GPIO":[0,0,0,0,38,37,0,0,41,39,40,0,0],"FLAG":0,"BASE":18}
electriQ MOODL Ambiance Lamp {"NAME":"ElectriQ MOODL","GPIO":[0,201,0,0,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":18}
-Fcmila 48W RGBCCT Ceiling Lamp {"NAME":"XDD-48W","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":0,"BASE":18}
Feit Electric 6in. RGBW Recessed Downlight {"NAME":"Feit LEDR6/RGB","GPIO":[0,0,0,0,37,40,0,0,38,50,39,0,0],"FLAG":0,"BASE":48}
Globe 5W 4" Recessed RGBCCT {"NAME":"GlobeRGBWW","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":0,"BASE":18}
+Hugoai Table Lamp {"NAME":"HG02","GPIO":[255,107,255,108,255,255,0,0,255,255,255,255,255],"FLAG":0,"BASE":54}
Hyperikon 14W 1000lm 6" Downlight {"NAME":"HyperikonDL6","GPIO":[0,0,0,0,38,37,0,0,41,39,40,0,0],"FLAG":0,"BASE":18}
iHomma 6W RGBCCT Downlight {"NAME":"iHomma RGBWW","GPIO":[0,0,0,0,41,40,0,0,37,38,39,0,0],"FLAG":0,"BASE":18}
iHomma Downlight {"NAME":"iHommaLEDDownl","GPIO":[0,0,0,0,0,40,0,0,37,38,39,0,0],"FLAG":0,"BASE":18}
Kogan 9W Downlight RGBCCT {"NAME":"Kogan_SMARTLED","GPIO":[0,0,0,0,38,37,0,0,41,39,40,0,0],"FLAG":0,"BASE":18}
-LE lampUX 15W RGBCCT Ceiling {"NAME":"LE lampUX 15W","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":0,"BASE":18}
-Lohas ZN026CL10 RGBCCT {"NAME":"Lohas LED Lamp","GPIO":[0,0,0,0,38,37,0,0,40,39,41,0,0],"FLAG":0,"BASE":18}
-LSC 20W 1400lm White Ambiance Ceiling {"NAME":"LSC RGBCW LED","GPIO":[0,0,0,0,0,0,0,0,181,0,180,0,0],"FLAG":0,"BASE":18}
Lumary 18W RGBCCT Recessed Panel {"NAME":"LumaryDLghtRGB","GPIO":[0,0,0,0,38,37,0,0,41,39,40,0,0],"FLAG":0,"BASE":18}
-LVL 300mm Round Ceiling {"NAME":"LVL 300m Round 24W Ceiling LED","GPIO":[0,0,0,0,0,37,0,0,0,47,0,0,0],"FLAG":0,"BASE":48}
Mi LED Desk Lamp MJTD01YL {"NAME":"Mi Desk Lamp","GPIO":[0,0,17,0,37,38,0,0,150,151,0,0,0],"FLAG":0,"BASE":66}
-MiraBella Genio 6 Pack 30mm Stainless Steel Deck Kit {"NAME":"Genio RGB Deck Lights","GPIO":[0,0,0,0,37,0,0,0,38,0,39,0,0],"FLAG":0,"BASE":18}
-Mirabella Genio CCT Deck Lights {"NAME":"Mirabella Deck CCT","GPIO":[0,0,0,0,0,37,0,0,0,38,0,0,0],"FLAG":0,"BASE":18}
-Mirabella Genio I002741 {"NAME":"GenioDLightRGB","GPIO":[0,0,0,0,38,37,0,0,41,39,40,0,0],"FLAG":0,"BASE":18}
-Mirabella Genio I002742 {"NAME":"GenioDLightCCT","GPIO":[0,0,0,0,0,0,0,0,38,0,37,0,0],"FLAG":0,"BASE":48}
-Mirabella Genio I002798 Warm White Filament Festoon {"NAME":"GenioFestoon","GPIO":[0,0,0,0,0,0,0,0,0,0,37,0,0],"FLAG":0,"BASE":18}
+Mirabella Genio 10 LED Filament Festoon {"NAME":"GenioFestoon","GPIO":[0,0,0,0,0,0,0,0,0,0,37,0,0],"FLAG":0,"BASE":18}
+Mirabella Genio 4 Black LED Garden Path {"NAME":"GenioGardenStr","GPIO":[0,0,0,0,37,0,0,0,38,0,39,0,0],"FLAG":0,"BASE":18}
+Mirabella Genio 9W CCT Downlight {"NAME":"GenioDLightCCT","GPIO":[0,0,0,0,0,0,0,0,47,0,37,0,0],"FLAG":0,"BASE":48}
+Mirabella Genio 9W RGBCCT Downlight {"NAME":"GenioDLightRGB","GPIO":[0,0,0,0,38,37,0,0,41,39,40,0,0],"FLAG":0,"BASE":18}
+Mirabella Genio CCT 6 LED 30mm Stainless Steel Deck {"NAME":"Mirabella Deck CCT","GPIO":[0,0,0,0,0,37,0,0,0,38,0,0,0],"FLAG":0,"BASE":18}
+MiraBella Genio Colour 6 LED 30mm Stainless Steel Deck {"NAME":"Genio RGB Deck Lights","GPIO":[0,0,0,0,37,0,0,0,38,0,39,0,0],"FLAG":0,"BASE":18}
Moes 7W RGBCCT Downlight {"NAME":"Moes Downlight","GPIO":[0,0,0,0,40,41,0,0,37,38,39,0,0],"FLAG":0,"BASE":18}
Novostella UT88835 20W Floodlight {"NAME":"Novo 20W Flood","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":0,"BASE":18}
-Reafoo A27 9W 810lm {"NAME":"ReaFooE27","GPIO":[0,0,0,0,41,40,0,0,37,0,39,38,0],"FLAG":0,"BASE":18}
-SMRTLite LED Panel {"NAME":"SMRTLite","GPIO":[0,0,0,0,38,37,0,0,41,39,40,0,0],"FLAG":0,"BASE":18}
Sonoff BN-SZ01 {"NAME":"Sonoff BN-SZ","GPIO":[0,0,0,0,0,0,0,0,37,56,0,0,0],"FLAG":0,"BASE":22}
Spotlight 9cm RGB+W 7W {"NAME":"Spotlight RGBW","GPIO":[0,0,0,0,0,0,0,0,0,143,0,144,0],"FLAG":0,"BASE":27}
Teckin FL41 {"NAME":"Teckin FL41","GPIO":[0,0,0,0,0,17,0,0,0,0,37,0,0],"FLAG":0,"BASE":18}
-Utorch PZE-911 {"NAME":"Utorch PZE-911","GPIO":[0,0,0,0,0,0,0,0,38,0,37,0,0],"FLAG":0,"BASE":1}
-Utorch UT40 {"NAME":"Utorch UT40","GPIO":[0,0,0,0,0,0,0,0,38,0,37,0,0],"FLAG":0,"BASE":1}
-Verve Design Angie 18W Ceiling Light With RGB Ring {"NAME":"ACL12HA Light","GPIO":[0,0,0,0,38,37,0,0,41,39,40,0,0],"FLAG":0,"BASE":18}
-Verve Design Charlie 22W CCT Ceiling {"NAME":"Verve ACL01HA","GPIO":[0,0,0,0,0,37,0,0,0,47,0,0,0],"FLAG":0,"BASE":48}
-Verve Design Hana 24W CCT Ceiling {"NAME":"Verve ACL03HA","GPIO":[0,0,0,0,0,0,0,0,38,0,37,0,0],"FLAG":0,"BASE":48}
Wipro Next 20W Smart LED Batten {"NAME":"WIPROBatten","GPIO":[0,0,0,0,0,37,0,0,0,47,0,0,0],"FLAG":1,"BASE":18}
Zemismart 4" 10W RGBCCT {"NAME":"ZemiDownLight4","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":0,"BASE":18}
Zemismart 4" 10W RGBW {"NAME":"ZemiDownLight","GPIO":[0,0,0,0,0,0,0,0,0,143,0,144,0],"FLAG":0,"BASE":27}
@@ -400,6 +442,8 @@ Zemismart 6" 14W RGBCCT {"NAME":"ZemiDownLight6","GPIO":[0,0,0,0,37,40,0,0,38,4
## Miscellaneous
```
+Alfawise Air Purifier {"NAME":"alfawise P2","GPIO":[255,107,255,108,255,255,0,0,255,255,255,255,255],"FLAG":0,"BASE":54}
+Dealdig Robvaccum 8 Vacuum {"NAME":"WhiteVacuum","GPIO":[0,107,0,108,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":18}
iLONDA Fish feeder {"NAME":"Feeder","GPIO":[0,0,0,0,17,56,0,0,42,0,21,0,0],"FLAG":0,"BASE":18}
Kogan 1500w Panel Heater {"NAME":"Kogan Panel Heater","GPIO":[0,0,0,0,0,0,0,0,0,108,0,107,0],"FLAG":0,"BASE":54}
Mosquito Killer Lamp {"NAME":"MosquitoKiller","GPIO":[17,0,0,0,0,0,0,0,37,56,0,0,0],"FLAG":0,"BASE":18}
@@ -407,6 +451,13 @@ Proscenic 807C Humidifier {"NAME":"Generic","GPIO":[255,255,255,255,255,255,0,0
Sonoff RM433 RF Remote Controller {"NAME":"REQUIRES RF DEVICE"}
```
+## Motion Sensor
+```
+DP-WP001 PIR {"NAME":"TUYA PIR","GPIO":[255,107,255,108,255,255,0,0,255,255,255,255,255],"FLAG":0,"BASE":54}
+Lenovo Rechargable PIR Motion {"NAME":"Lenovo PIR","GPIO":[255,107,255,108,255,255,0,0,255,255,255,255,255],"FLAG":0,"BASE":54}
+Mirabella Genio I002576 {"NAME":"GenioPir","GPIO":[17,107,0,108,0,0,0,0,0,56,0,0,0],"FLAG":0,"BASE":54}
+```
+
## Motor
```
Zemismart BCM300D-TY {"NAME":"Zemistart_Curt","GPIO":[0,0,0,0,0,0,0,0,0,108,0,107,0],"FLAG":0,"BASE":54}
@@ -415,14 +466,22 @@ Zemismart Roller Shade {"NAME":"M2805EIGB","GPIO":[255,107,255,108,255,255,0,0
Zemismart Updated RF Remote Roller Shade {"NAME":"Zemismart M515EGB","GPIO":[255,107,255,108,255,255,0,0,255,255,255,255,255],"FLAG":0,"BASE":54}
```
+## Multisensor
+```
+NEO Coolcam Siren with Temperature and Humidity {"NAME":"Neo Siren 3in1","GPIO":[255,107,255,108,255,255,0,0,255,255,255,255,255],"FLAG":0,"BASE":54}
+Sonoff SC {"NAME":"Sonoff SC","GPIO":[17,148,255,149,0,0,0,0,0,56,0,0,0],"FLAG":0,"BASE":21}
+```
+
## Outdoor Plug
```
Acenx SOP04-US Dual {"NAME":"SOP04-US Dual","GPIO":[255,255,255,255,56,57,0,0,21,17,22,255,255],"FLAG":0,"BASE":18}
Aicliv SOP03-US {"NAME":"AICLIV SOP03US","GPIO":[0,0,0,23,57,0,0,0,21,18,22,0,0],"FLAG":15,"BASE":18}
Albohes PC-1606 {"NAME":"Albohes PC1606","GPIO":[17,0,0,0,0,22,18,0,21,0,0,0,0],"FLAG":1,"BASE":39}
Albohes PS-1602 {"NAME":"Albohes PC1606","GPIO":[17,0,0,0,0,22,18,0,21,0,0,0,0],"FLAG":1,"BASE":39}
+Amzdest IP55 {"NAME":"C168 Outdoor","GPIO":[0,0,0,130,134,132,0,0,21,56,22,23,17],"FLAG":0,"BASE":18}
Aoycocr X13 {"NAME":"Aoycocr X13","GPIO":[0,0,56,0,0,0,0,0,22,17,0,21,0],"FLAG":0,"BASE":18}
Atomi AT1320 {"NAME":"AT1320","GPIO":[0,0,0,0,57,52,0,0,21,17,0,0,0],"FLAG":0,"BASE":18}
+BN-Link {"NAME":"BNC-60/U130T","GPIO":[255,0,255,0,255,56,255,255,21,17,255,0,0],"FLAG":15,"BASE":18}
Brennenstuhl WA 3000 XS02 {"NAME":"WA 3000 XS02","GPIO":[0,0,0,0,21,17,0,0,158,52,0,0,0],"FLAG":0,"BASE":61}
C137 IP55 {"NAME":"C137 Outdoor","GPIO":[0,17,0,56,134,132,0,0,21,131,22,0,0],"FLAG":15,"BASE":18}
C168 IP64 {"NAME":"C188","GPIO":[56,0,57,0,18,0,0,0,21,17,157,22,0],"FLAG":0,"BASE":18}
@@ -459,8 +518,8 @@ Teckin SS42 {"NAME":"Teckin SS42","GPIO":[0,0,0,0,56,57,0,0,21,17,2
Top-Max PS-1602 {"NAME":"PS-1602","GPIO":[17,255,255,255,0,22,18,0,21,56,0,0,0],"FLAG":0,"BASE":29}
Torchstar LITEdge 2-in-1 {"NAME":"LITEdge Plug","GPIO":[0,0,0,0,56,57,0,0,21,17,22,0,0],"FLAG":0,"BASE":18}
Ucomen PA-GEBA-01SWP {"NAME":"PA-GEBA-01SWP","GPIO":[0,0,0,0,52,57,0,0,21,17,0,0,0],"FLAG":0,"BASE":18}
-Woox R4051 {"NAME":"Woox R4051","GPIO":[17,0,0,0,0,0,0,0,21,56,0,0,0],"FLAG":0,"BASE":18}
-Woox R4052 {"NAME":"Woox R4052","GPIO":[17,0,0,0,0,0,0,0,21,56,0,0,0],"FLAG":0,"BASE":18}
+WOOX R4051 {"NAME":"WOOX R4051","GPIO":[17,0,0,0,0,0,0,0,21,56,0,0,0],"FLAG":0,"BASE":18}
+WOOX R4052 {"NAME":"WOOX R4052","GPIO":[17,0,0,0,0,0,0,0,21,56,0,0,0],"FLAG":0,"BASE":18}
```
## Plug
@@ -475,7 +534,7 @@ Aigoss 16A Mini {"NAME":"Aigoss Plug","GPIO":[255,255,0,255,52,21,0,0,5
Aisirer AWP07L {"NAME":"AISIRER AWP07L","GPIO":[56,0,57,0,0,133,0,0,131,17,132,21,0],"FLAG":0,"BASE":18}
Aisirer AWP07L v2 {"NAME":"AWP07L v2","GPIO":[0,17,57,0,134,132,0,0,131,56,21,0,0],"FLAG":0,"BASE":18}
Aisirer AWP07L v3 {"NAME":"AWP07L v3","GPIO":[0,17,52,0,134,132,0,0,130,53,21,0,0],"FLAG":0,"BASE":18}
-Aisirer AWP08L {"NAME":"AISIRER AWP08L","GPIO":[0,0,56,0,0,0,0,0,0,0,0,21,0],"FLAG":0,"BASE":18}
+Aisirer AWP08L {"NAME":"AISIRER AWP08L","GPIO":[0,0,56,0,0,0,0,0,0,17,0,21,0],"FLAG":0,"BASE":18}
Aisirer AWP08L v2 {"NAME":"AISIRER AWP08L","GPIO":[0,0,0,0,17,57,0,0,21,0,0,0,0],"FLAG":0,"BASE":18}
Aisirer JH-G018 {"NAME":"AISIRER JH-G01","GPIO":[0,0,0,0,56,0,0,0,21,17,0,0,0],"FLAG":0,"BASE":18}
Aisirer SWA11 {"NAME":"SWA11","GPIO":[0,0,0,0,52,21,0,0,0,17,0,0,0],"FLAG":0,"BASE":18}
@@ -484,6 +543,7 @@ Alexfirst TV-ASP801EU {"NAME":"Alexfirst","GPIO":[17,0,0,0,54,56,0,0,21,0,0,0
Alfawise PE1004T {"NAME":"PE1004T","GPIO":[0,0,0,0,56,57,0,0,21,17,0,0,0],"FLAG":0,"BASE":18}
Alfawise PF1006 {"NAME":"PF1006","GPIO":[0,0,17,0,0,0,0,0,0,56,21,0,0],"FLAG":0,"BASE":18}
Alfawise PME1606 {"NAME":"PME1606","GPIO":[0,0,0,17,133,132,0,0,131,52,21,0,0],"FLAG":0,"BASE":18}
+Amped Wireless {"NAME":"AWP48W","GPIO":[0,0,0,0,56,17,0,0,0,53,21,0,0],"FLAG":0,"BASE":18}
Amysen JSM-WF02 {"NAME":"Amysen JSMWF02","GPIO":[0,17,0,0,0,0,0,0,0,56,21,0,0],"FLAG":0,"BASE":18}
Amysen YX-WS01 {"NAME":"Amysen YX-WS01","GPIO":[0,17,0,0,0,0,0,0,0,56,21,0,0],"FLAG":0,"BASE":18}
ANBES ABS-CZ004 {"NAME":"ANBES ABS-CZ00","GPIO":[0,0,0,0,21,133,0,0,131,17,132,22,18],"FLAG":0,"BASE":18}
@@ -530,6 +590,7 @@ Awow EU3S {"NAME":"AWOW BSD33","GPIO":[0,0,56,0,0,134,0,0,131,17,
Awow X5P {"NAME":"Awow","GPIO":[0,0,56,0,0,0,0,0,0,17,0,21,0],"FLAG":0,"BASE":18}
AWP02L-N {"NAME":"AWP02L-N","GPIO":[0,0,56,0,0,0,0,0,0,17,0,21,0],"FLAG":0,"BASE":18}
AzpenHome Smart {"NAME":"Socket2Me","GPIO":[52,255,255,255,22,255,0,0,21,255,17,255,255],"FLAG":0,"BASE":18}
+Bagotte SK-EU-A01 {"NAME":"Bagotte SK-EU-A01","GPIO":[122,0,0,0,0,0,0,0,21,56,0,0,0],"FLAG":0,"BASE":18}
Bakibo TP22Y {"NAME":"Bakibo TP22Y","GPIO":[0,0,0,17,134,132,0,0,131,56,21,0,0],"FLAG":0,"BASE":52}
Bardi 16A {"NAME":"BARDI","GPIO":[56,0,0,0,0,134,0,0,21,17,132,57,131],"FLAG":0,"BASE":18}
Bauhn ASPU-1019 {"NAME":"Bauhn Smart Pl","GPIO":[0,0,0,0,21,22,0,0,0,56,17,0,0],"FLAG":0,"BASE":18}
@@ -543,13 +604,13 @@ BlitzWolf BW-SHP3 alt {"NAME":"BlitzWolf SHP3","GPIO":[18,56,0,131,134,132,0,
BlitzWolf BW-SHP4 {"NAME":"BlitzWolf SHP","GPIO":[57,255,56,255,0,134,0,0,131,17,132,21,0],"FLAG":0,"BASE":45}
BlitzWolf BW-SHP5 {"NAME":"SHP5","GPIO":[57,145,56,146,0,22,0,0,0,0,21,0,17],"FLAG":0,"BASE":18}
BlitzWolf BW-SHP6 10A {"NAME":"BW-SHP6 10A","GPIO":[158,255,56,255,0,134,0,0,131,17,132,21,0],"FLAG":0,"BASE":45}
-Blitzwolf BW-SHP6 15A {"NAME":"Blitzwolf SHP6","GPIO":[56,255,158,255,132,134,0,0,131,17,0,21,0],"FLAG":0,"BASE":45}
+BlitzWolf BW-SHP6 15A {"NAME":"BlitzWolf SHP6","GPIO":[56,255,158,255,132,134,0,0,131,17,0,21,0],"FLAG":0,"BASE":45}
BlitzWolf BW-SHP7 {"NAME":"SHP7","GPIO":[17,158,57,131,134,132,0,0,18,56,21,0,22],"FLAG":0,"BASE":45}
-BN-LINK BNC-60/U133TJ-2P {"NAME":"BNC-60/U133TJ","GPIO":[0,56,0,17,134,132,0,0,131,57,21,0,0],"FLAG":0,"BASE":18}
+BN-Link {"NAME":"BNC-60/U133TJ","GPIO":[0,56,0,17,134,132,0,0,131,57,21,0,0],"FLAG":0,"BASE":18}
BNETA IO-WIFI-PlugSA {"NAME":"BNETA WifiPlug","GPIO":[17,0,0,0,133,132,0,0,131,56,21,0,0],"FLAG":0,"BASE":18}
Brennenstuhl WA 3000 XS01 {"NAME":"WA 3000 XS01","GPIO":[0,0,0,0,21,17,0,0,158,52,0,0,0],"FLAG":0,"BASE":61}
Bright {"NAME":"Bright Wi-Fi Smart Plug","GPIO":[0,0,0,0,56,0,0,0,21,0,17,0,0],"FLAG":0,"BASE":18}
-Brilliant HK17654S05 {"NAME":"HK17654S05","GPIO":[17,255,255,255,133,132,255,255,131,56,21,255,255],"FLAG":0,"BASE":18}
+Brilliant {"NAME":"HK17654S05","GPIO":[17,255,255,255,133,132,255,255,131,56,21,255,255],"FLAG":0,"BASE":18}
Brilliant Lighting BL20925 {"NAME":"BL20925","GPIO":[0,0,0,17,133,132,0,0,131,158,21,0,0],"FLAG":0,"BASE":52}
Brilliant Smart Double Adaptor {"NAME":"Brilliant_2Plg","GPIO":[0,0,0,52,21,22,255,255,157,17,18,0,0],"FLAG":0,"BASE":18}
BrilliantSmart 20676 USB Charger {"NAME":"Brilliant","GPIO":[0,0,0,0,0,21,0,0,0,52,90,0,0],"FLAG":0,"BASE":18}
@@ -573,7 +634,7 @@ Coosa {"NAME":"COOSA","GPIO":[0,0,0,0,57,52,0,0,21,17,255,0,0
Coosa SP1 {"NAME":"COOSA SP1","GPIO":[57,255,56,255,0,134,0,0,131,17,132,21,0],"FLAG":0,"BASE":45}
CrazyLynX WiFi {"NAME":"CrazyLynX","GPIO":[0,0,0,0,57,56,0,0,21,17,0,0,0],"FLAG":1,"BASE":18}
CYYLTF BIFANS J23 {"NAME":"CYYLTD BIFANS J23","GPIO":[56,0,0,0,0,0,0,0,21,17,0,0,0],"FLAG":1,"BASE":18}
-D3D Smart Plug with USB & Power Monitor {"NAME":"D3D d_SUM","GPIO":[57,255,56,255,0,134,0,0,131,17,132,21,0],"FLAG":0,"BASE":45}
+D3D Smart Plug with USB & Power Monitor {"NAME":"D3D FLHS-ZN04","GPIO":[57,255,56,131,255,133,255,255,255,17,132,21,255],"FLAG":15,"BASE":18}
DE-1 16A {"NAME":"DE-1","GPIO":[0,17,0,0,133,0,0,0,0,52,21,0,0],"FLAG":1,"BASE":18}
DeLock 11826 {"NAME":"DeLock 11826","GPIO":[17,0,0,0,0,0,0,0,21,158,0,0,0],"FLAG":0,"BASE":1}
Deltaco SH-P01 {"NAME":"DELTACO SH-P01","GPIO":[0,0,0,0,0,56,0,0,21,17,0,0,0],"FLAG":0,"BASE":18}
@@ -599,24 +660,25 @@ EletecPro 2 {"NAME":"EletecPro-2","GPIO":[255,255,255,255,17,255,0,
Enjowi SP111-EU-W {"NAME":"Enjowi SP111-E","GPIO":[17,56,0,0,134,132,0,0,131,57,21,0,0],"FLAG":0,"BASE":55}
Epicka {"NAME":"Epicka","GPIO":[255,255,255,255,57,56,0,0,21,17,255,255,255],"FLAG":1,"BASE":18}
Esicoo JSM-WF02 {"NAME":"Esicoo Plug","GPIO":[0,17,0,0,0,0,0,0,0,56,21,0,0],"FLAG":0,"BASE":18}
-Esicoo YX-WS01 {"NAME":"Esicoo Plug","GPIO":[255,17,255,255,255,255,0,0,255,56,21,255,255],"FLAG":0,"BASE":18}
Estink C178 {"NAME":"Estink C178","GPIO":[0,0,0,0,52,57,0,0,21,17,0,0,0],"FLAG":0,"BASE":18}
Etekcity ESW01-USA {"NAME":"ESW01-USA","GPIO":[0,0,0,0,21,157,0,0,132,133,17,130,52],"FLAG":0,"BASE":55}
Etekcity ESW15-USA {"NAME":"ESW15-US","GPIO":[0,0,0,0,0,21,0,0,132,133,17,130,52],"FLAG":0,"BASE":18}
-Eva Logik NWF001 {"NAME":"EVA LOGIK Plug","GPIO":[255,17,255,255,255,255,0,0,255,52,21,255,255],"FLAG":0,"BASE":18}
+Eva Logik {"NAME":"EVA LOGIK Plug","GPIO":[255,17,255,255,255,255,0,0,255,52,21,255,255],"FLAG":0,"BASE":18}
EVO-Smart JH-G01U {"NAME":"EVO JH-G01U","GPIO":[0,0,0,0,21,17,0,0,56,0,0,0,0],"FLAG":0,"BASE":18}
Feit Electric PLUG/WIFI {"NAME":"Feit Wifi Plug","GPIO":[0,0,0,56,0,0,0,0,21,0,17,0,0],"FLAG":0,"BASE":18}
FK-PW901U {"NAME":"FK-PW901U","GPIO":[56,255,255,255,255,23,0,0,21,17,24,22,255],"FLAG":0,"BASE":18}
FLHS-ZN04 {"NAME":"FLHS-ZN04","GPIO":[57,0,56,0,0,134,0,0,131,17,132,21,0],"FLAG":0,"BASE":45}
Fontastic SH01 {"NAME":"Fontastic","GPIO":[255,0,255,0,56,0,0,0,21,17,0,0,0],"FLAG":1,"BASE":18}
Foval SM-PW701E {"NAME":"SM-PW701E","GPIO":[0,0,0,0,56,0,0,0,21,17,0,0,0],"FLAG":0,"BASE":18}
-FrankEver FLHS-ZN04 {"NAME":"Israel plug","GPIO":[57,0,56,131,0,134,0,0,0,17,132,21,0],"FLAG":0,"BASE":45}
+FrankEver 15A {"NAME":"Israel plug","GPIO":[57,0,56,131,0,134,0,0,0,17,132,21,0],"FLAG":0,"BASE":45}
+FrankEver 16A {"NAME":"FrankEver FK-PW801ER","GPIO":[0,0,0,0,52,0,0,0,21,17,0,0,0],"FLAG":0,"BASE":18}
+FrankEver Mini {"NAME":"FK-PW801US","GPIO":[0,0,0,0,21,22,0,0,23,24,25,26,27],"FLAG":0,"BASE":18}
GDTech W-US001 {"NAME":"GDTech W-US001","GPIO":[255,17,255,255,255,255,0,0,255,56,21,255,255],"FLAG":1,"BASE":18}
GDTech W-US003 {"NAME":"W-US003","GPIO":[0,17,255,255,255,0,0,0,0,56,21,0,0],"FLAG":0,"BASE":18}
Geekbes YM-WS-1 {"NAME":"Office Test Pl","GPIO":[255,255,255,255,255,255,255,255,158,17,12,21,255],"FLAG":15,"BASE":18}
Geeni Spot {"NAME":"Geeni Spot","GPIO":[0,0,0,0,52,57,0,0,21,17,0,0,0],"FLAG":0,"BASE":18}
Geeni Spot Glo {"NAME":"Geeni Glo","GPIO":[0,0,0,0,56,0,0,0,21,17,22,0,0],"FLAG":0,"BASE":18}
-Geeni Switch {"NAME":"Geeni Switch","GPIO":[0,0,0,0,52,57,0,0,21,17,0,0,0],"FLAG":0,"BASE":18}
+Geeni SWITCH {"NAME":"Geeni Switch","GPIO":[0,0,0,0,52,57,0,0,21,17,0,0,0],"FLAG":0,"BASE":18}
Geeni Switch Duo {"NAME":"Geeni Duo","GPIO":[0,0,0,0,18,22,0,0,17,52,21,0,53],"FLAG":0,"BASE":18}
Globe 50020 2 Outlet {"NAME":"Globe 50020","GPIO":[0,158,0,57,18,17,0,0,21,56,22,0,0],"FLAG":0,"BASE":18}
Globe Smart {"NAME":"GlobeSmartPlug","GPIO":[0,0,0,0,56,0,0,0,21,0,17,0,0],"FLAG":0,"BASE":18}
@@ -624,6 +686,7 @@ GoldenDot Mini {"NAME":"GoldenDot Mini","GPIO":[0,17,0,0,0,0,0,0,0,57,
GoldenDot with ADC {"NAME":"W-US003-Power","GPIO":[56,0,0,0,0,0,0,0,0,17,0,21,0],"FLAG":7,"BASE":18}
Gosund SP1 {"NAME":"Gosund SP1 v23","GPIO":[0,56,0,17,134,132,0,0,131,57,21,0,0],"FLAG":0,"BASE":55}
Gosund SP111 {"NAME":"Gosund SP111","GPIO":[56,0,57,0,0,134,0,0,131,17,132,21,0],"FLAG":0,"BASE":18}
+Gosund SP111 15A {"NAME":"SP111 v1.1","GPIO":[56,0,158,0,132,134,0,0,131,17,0,21,0],"FLAG":0,"BASE":45}
Gosund SP111 v1.1 {"NAME":"SP111 v1.1","GPIO":[56,0,158,0,132,134,0,0,131,17,0,21,0],"FLAG":0,"BASE":45}
Gosund SP111 v1.4 {"NAME":"Gosund SP111","GPIO":[57,255,56,255,0,134,0,0,131,17,132,21,0],"FLAG":0,"BASE":45}
Gosund SP112 {"NAME":"SHP5","GPIO":[57,145,56,146,255,22,0,0,255,255,21,255,17],"FLAG":0,"BASE":18}
@@ -670,6 +733,7 @@ Insmart WP5 {"NAME":"INSMART","GPIO":[0,0,46,0,0,0,0,0,0,9,0,21,0],
iSwitch {"NAME":"Smart Plug XSA","GPIO":[255,17,255,255,255,255,0,0,255,56,21,255,255],"FLAG":0,"BASE":18}
Jeeo TF-SH330 {"NAME":"Jeeo TF-SH330","GPIO":[56,0,0,0,0,0,0,0,0,17,0,21,0],"FLAG":1,"BASE":18}
Jeeo TF-SH331W {"NAME":"Jeeo SH331W","GPIO":[56,0,158,0,0,134,0,0,131,17,132,21,0],"FLAG":0,"BASE":18}
+Jetstream MSP150 {"NAME":"JetstreamMSP150","GPIO":[56,0,57,0,21,134,0,0,131,17,132,0,0],"FLAG":0,"BASE":45}
Jinli XS-SSA01 {"NAME":"JINLI","GPIO":[0,0,0,0,0,0,0,0,56,17,0,21,0],"FLAG":0,"BASE":18}
Jinvoo SM-PW701U {"NAME":"SM-PW702","GPIO":[0,0,0,0,57,56,0,0,21,17,0,0,0],"FLAG":0,"BASE":18}
Jinvoo SM-PW712UA 10A {"NAME":"SM-PW712UA","GPIO":[0,0,0,158,56,57,0,0,22,17,21,0,0],"FLAG":15,"BASE":18}
@@ -690,11 +754,13 @@ KMC 4 {"NAME":"KMC 4 Outlet","GPIO":[0,56,0,0,133,132,0,0,130
KMC 70011 {"NAME":"KMC 70011","GPIO":[17,0,0,0,133,132,0,0,130,56,21,0,0],"FLAG":0,"BASE":36}
Kogan Energy Meter {"NAME":"Kogan Smart Sw","GPIO":[17,0,0,0,133,132,0,0,131,56,21,0,0],"FLAG":0,"BASE":18}
Kogan Energy Meter & USB {"NAME":"KoganPOW","GPIO":[0,0,0,17,134,132,0,0,131,56,21,0,0],"FLAG":0,"BASE":52}
+Koogeek {"NAME":"Koogeek KLSP1","GPIO":[0,0,0,17,0,0,0,0,0,56,21,0,0],"FLAG":0,"BASE":18}
+Koogeek {"NAME":"Koogeek-KLUP1","GPIO":[0,0,0,17,134,132,0,0,131,56,21,0,0],"FLAG":0,"BASE":18}
Koogeek KLSP1 {"NAME":"Koogeek-KLSP1","GPIO":[0,56,0,17,134,132,0,0,131,158,21,0,0],"FLAG":0,"BASE":18}
Koogeek KLSP2 {"NAME":"KOOGEEK KLSP2","GPIO":[57,145,56,146,0,22,0,0,0,0,21,0,17],"FLAG":0,"BASE":45}
-Koogeek KLUP1 {"NAME":"Koogeek-KLUP1","GPIO":[0,0,0,17,134,132,0,0,131,56,21,0,0],"FLAG":0,"BASE":18}
Koogeek KLWP1 {"NAME":"Koogeek-KLWP1","GPIO":[157,0,56,0,0,134,0,0,131,17,132,21,0],"FLAG":0,"BASE":1}
Koogeek W-DEXI {"NAME":"W-DEXI","GPIO":[0,90,0,0,134,132,0,0,130,52,21,0,0],"FLAG":0,"BASE":18}
+Koogeek W-UKX {"NAME":"Koogeek W-UKX","GPIO":[0,17,255,255,255,0,0,0,0,56,21,0,0],"FLAG":0,"BASE":1}
KULED K63 {"NAME":"KULED K63","GPIO":[0,0,0,0,21,17,0,0,56,0,0,0,0],"FLAG":0,"BASE":18}
Laduo YX-DE01 {"NAME":"YX-DE01","GPIO":[255,17,255,255,255,255,0,0,255,56,21,255,255],"FLAG":0,"BASE":18}
Lenovo SE-341A {"NAME":"Lenovo SE-341A","GPIO":[0,0,0,0,17,21,0,0,158,0,56,0,0],"FLAG":0,"BASE":18}
@@ -710,10 +776,10 @@ Luminea NX-4491 {"NAME":"Luminea NX-449","GPIO":[56,0,158,0,0,0,0,0,0,1
Luminea NX-4541 {"NAME":"NX-4451","GPIO":[0,0,0,17,0,0,0,0,0,56,21,0,0],"FLAG":0,"BASE":55}
Luminea ZX-2820 {"NAME":"ZX2820-675","GPIO":[0,0,0,17,133,132,0,0,131,56,21,0,0],"FLAG":0,"BASE":65}
Luminea ZX-2858 {"NAME":"Luminea SF-200","GPIO":[0,0,0,17,0,0,0,0,0,56,21,0,0],"FLAG":0,"BASE":18}
+Lunvon {"NAME":"Lunvon Smart Plug","GPIO":[17,0,0,0,0,0,52,21,0,0,0,0,0],"FLAG":0,"BASE":18}
Martin Jerry V01 {"NAME":"MJ V01","GPIO":[0,0,0,0,56,0,0,0,21,17,0,0,0],"FLAG":0,"BASE":18}
Martin Jerry XS-SSA01 {"NAME":"MJ_XS-SSA01","GPIO":[0,17,0,0,0,0,0,0,0,56,21,0,0],"FLAG":0,"BASE":18}
Maxcio W-DE004 {"NAME":"Maxcio W-DE004","GPIO":[0,17,0,0,0,0,0,0,0,56,21,0,0],"FLAG":1,"BASE":18}
-Maxcio W-UK007 {"NAME":"Maxcio","GPIO":[0,17,0,0,0,0,0,0,0,56,21,0,0],"FLAG":0,"BASE":18}
Maxcio W-UK007S {"NAME":"Maxcio","GPIO":[56,0,255,0,0,134,0,0,131,17,132,21,0],"FLAG":0,"BASE":45}
Maxcio W-US002S {"NAME":"W-US002S","GPIO":[57,0,56,0,0,134,0,0,131,17,132,21,0],"FLAG":0,"BASE":45}
Maxcio W-US003 {"NAME":"W-US003","GPIO":[255,17,255,255,255,255,0,0,255,22,21,255,255],"FLAG":0,"BASE":18}
@@ -727,7 +793,7 @@ Merkury MI-WW105-199W {"NAME":"Merkury Switch","GPIO":[255,255,255,255,158,56
Minleaf W-DEXI {"NAME":"W-DEXI","GPIO":[0,17,0,0,134,132,0,0,131,52,21,0,0],"FLAG":0,"BASE":18}
Mirabella Genio 1002341 {"NAME":"Genio 1","GPIO":[0,0,56,0,0,0,0,0,21,17,0,0,0],"FLAG":0,"BASE":1}
Mirabella Genio USB {"NAME":"Mirabella Genio 1002826","GPIO":[0,0,0,17,0,0,0,0,0,56,21,0,0],"FLAG":0,"BASE":1}
-Mirabella Genio USB Port and Plug {"NAME":"Genio I002341","GPIO":[0,0,0,0,56,0,0,0,21,17,0,0,0],"FLAG":0,"BASE":1}
+Mirabella Genio USB Port {"NAME":"Genio I002341","GPIO":[0,0,0,0,56,0,0,0,21,17,0,0,0],"FLAG":0,"BASE":1}
Mistral {"NAME":"Mistral Smart ","GPIO":[56,0,0,0,0,0,0,0,0,17,0,21,0],"FLAG":0,"BASE":18}
Moes NX-SP203 {"NAME":"Moes NX-SP203","GPIO":[52,0,0,0,17,134,0,0,21,18,0,22,0],"FLAG":0,"BASE":18}
Moes W-DE004S {"NAME":"Moes DE004S ","GPIO":[57,0,0,0,0,134,0,0,131,17,132,21,0],"FLAG":0,"BASE":18}
@@ -744,10 +810,12 @@ Nightlight and AC Outlet {"NAME":"SWN03","GPIO":[17,0,0,0,0,0,255,255,37,0,0,21
Nishica SM-PW701I {"NAME":"SM-PW701I","GPIO":[255,255,255,255,255,255,255,255,21,52,17,255,255],"FLAG":15,"BASE":18}
NX-SM112 {"NAME":"NX-SM112v3","GPIO":[0,0,0,0,134,132,0,0,158,17,130,21,0],"FLAG":0,"BASE":45}
NX-SM200 {"NAME":"NX-SM200","GPIO":[56,0,0,0,0,134,0,0,21,17,132,57,131],"FLAG":0,"BASE":18}
+NX-SM223 {"NAME":"Smart Thurmm","GPIO":[0,17,0,0,0,0,0,0,0,56,21,0,0],"FLAG":0,"BASE":18}
Obi Stecker {"NAME":"OBI Socket","GPIO":[255,255,0,255,52,21,0,0,54,255,17,0,255],"FLAG":1,"BASE":51}
Obi Stecker 2 {"NAME":"OBI Socket 2","GPIO":[0,0,0,0,21,17,0,0,56,53,0,0,0],"FLAG":0,"BASE":61}
Oittm Smart {"NAME":"Oittm","GPIO":[0,0,0,0,21,56,0,0,17,0,0,0,0],"FLAG":0,"BASE":1}
Olliwon {"NAME":"Olliwon","GPIO":[0,0,56,0,0,0,0,0,0,17,0,21,0],"FLAG":0,"BASE":18}
+Onestyle SD-WL-02 {"NAME":"JH-G01B1","GPIO":[0,145,0,146,0,0,0,0,17,56,21,0,0],"FLAG":0,"BASE":41}
Orbecco W-US009 {"NAME":"Orbecco Plug","GPIO":[0,17,0,0,0,0,0,0,0,52,21,0,0],"FLAG":0,"BASE":18}
Orvibo B25 {"NAME":"Orvibo B25","GPIO":[0,0,0,0,53,21,0,0,52,0,17,0,0],"FLAG":0,"BASE":18}
Oukitel P1 {"NAME":"Oukitel P1Dual","GPIO":[52,255,0,255,0,0,0,0,22,17,0,21,0],"FLAG":0,"BASE":18}
@@ -761,6 +829,7 @@ Panamalar Nightlight {"NAME":"Panamalar EWN0","GPIO":[17,0,0,0,0,0,255,255,3
Panamalar NX-SM200 {"NAME":"NX-SM200","GPIO":[0,0,0,0,56,134,0,0,131,17,132,21,0],"FLAG":1,"BASE":18}
Positivo PPW1000 {"NAME":"PPW1000","GPIO":[0,0,56,0,0,134,0,0,131,17,132,21,0],"FLAG":0,"BASE":45}
Positivo Max {"NAME":"PPW1600","GPIO":[0,0,0,17,134,132,0,0,131,56,21,0,0],"FLAG":0,"BASE":55}
+PowerAdd BIE0091 {"NAME":"BIE0091","GPIO":[17,0,0,0,0,0,0,0,37,0,0,21,0],"FLAG":0,"BASE":18}
Powertech {"NAME":"Jaycar","GPIO":[56,0,0,0,0,0,0,0,0,9,0,21,0],"FLAG":0,"BASE":6}
Powertech MS6104 {"NAME":"Jaycar MS6104","GPIO":[0,0,0,17,134,132,0,0,131,56,21,0,0],"FLAG":0,"BASE":52}
Powrui 3-Outlet with 4 USB {"NAME":"POWRUI AHR-077","GPIO":[0,0,0,20,19,18,0,0,22,23,17,21,157],"FLAG":0,"BASE":18}
@@ -775,8 +844,9 @@ RenPho RF-SM004 {"NAME":"RenPho RFSM004","GPIO":[0,0,0,0,157,56,0,0,21,
Robaxo {"NAME":"Robaxo RSP-025","GPIO":[17,0,0,0,134,132,0,0,131,56,21,0,0],"FLAG":0,"BASE":18}
RSH-WS007-EU {"NAME":"RSH-WS007","GPIO":[0,0,56,0,0,134,0,0,131,17,132,21,0],"FLAG":1,"BASE":18}
S126 {"NAME":"tuya-plug","GPIO":[0,0,0,0,0,0,0,0,21,20,0,0,0],"FLAG":0,"BASE":8}
+SA-P202C 16A {"NAME":"Elivco 202C-G","GPIO":[0,0,0,17,134,132,0,0,130,56,21,0,0],"FLAG":0,"BASE":18}
Sansui {"NAME":"Sansui YSP-1","GPIO":[52,0,53,0,0,0,0,0,0,17,0,21,0],"FLAG":0,"BASE":18}
-Shelly Plug S {"NAME":"Shelly Plug S","GPIO":[57,255,56,255,0,134,0,0,131,17,132,21,0],"FLAG":2,"BASE":45}
+Shelly Plug S {"NAME":"Shelly Plug S","GPIO":[56,255,158,255,255,134,0,0,131,17,132,21,255],"FLAG":2,"BASE":45}
Silvergear Slimme Stekker {"NAME":"Silvergear SmartHomePlug","GPIO":[0,0,0,122,0,0,0,0,0,56,21,0,0],"FLAG":0,"BASE":18}
SimpleHome {"NAME":"SimpleHome","GPIO":[53,0,0,0,0,0,0,0,21,56,17,0,0],"FLAG":0,"BASE":1}
Slitinto NX-SM110 {"NAME":"Slitinto SM110","GPIO":[0,0,56,0,0,134,0,0,131,17,132,21,0],"FLAG":0,"BASE":45}
@@ -790,6 +860,8 @@ SM-PW701K {"NAME":"SM-PW701K","GPIO":[0,0,0,0,52,0,0,0,21,17,0,0,
Smaho {"NAME":"SMAHO WiFi P.","GPIO":[17,0,0,0,134,132,0,0,131,56,21,0,0],"FLAG":0,"BASE":18}
SmartDGM PP-W162 {"NAME":"SmartDGM Plug","GPIO":[0,0,0,17,134,132,0,0,131,52,21,0,0],"FLAG":0,"BASE":18}
SmartGrade AC 5008 {"NAME":"SmartGrade AC","GPIO":[17,0,0,0,133,132,0,0,131,56,21,0,0],"FLAG":0,"BASE":49}
+Smitch 16A {"NAME":"Smitch SP0602","GPIO":[57,255,56,255,0,134,0,0,131,17,132,21,0],"FLAG":0,"BASE":45}
+Smitch 6A {"NAME":"Smitch SP0601","GPIO":[57,255,56,255,0,134,0,0,131,17,132,21,0],"FLAG":0,"BASE":45}
Sonoff S20 {"NAME":"Sonoff S20","GPIO":[17,255,255,255,0,0,0,0,21,56,0,0,0],"FLAG":0,"BASE":8}
Sonoff S22 TH {"NAME":"Sonoff S22","GPIO":[17,255,0,255,255,0,0,0,21,56,255,0,0],"FLAG":0,"BASE":4}
Sonoff S26 {"NAME":"Sonoff S26","GPIO":[17,255,255,255,0,0,0,0,21,56,0,0,0],"FLAG":0,"BASE":8}
@@ -804,6 +876,7 @@ Steren SHOME-100 {"NAME":"SHOME-100 V1.0","GPIO":[0,0,0,0,0,56,0,0,21,17
STITCH by Monoprice 27937 {"NAME":"Stitch 27937","GPIO":[17,0,56,0,133,132,0,0,131,0,21,0,57],"FLAG":0,"BASE":18}
STITCH by Monoprice 35511 {"NAME":"Stitch 35511","GPIO":[56,0,57,0,0,133,0,0,0,17,132,21,131],"FLAG":0,"BASE":18}
SuperNight Dual {"NAME":"SuperNight Dua","GPIO":[255,17,255,21,132,133,0,0,22,130,58,255,255],"FLAG":1,"BASE":18}
+SWA1 {"NAME":"SWA1","GPIO":[0,0,0,0,52,21,0,0,0,17,0,0,0],"FLAG":0,"BASE":18}
SWA1 FR {"NAME":"SWA1","GPIO":[0,0,0,0,52,21,0,0,0,17,0,0,0],"FLAG":0,"BASE":18}
SWA1 UK {"NAME":"SWA1","GPIO":[0,0,0,0,52,21,0,0,0,17,0,0,0],"FLAG":0,"BASE":18}
SWA11 {"NAME":"SWA11","GPIO":[0,0,0,0,52,21,0,0,0,17,0,0,0],"FLAG":0,"BASE":18}
@@ -813,7 +886,7 @@ SwissTone SH 100 {"NAME":"SwissTone","GPIO":[0,0,0,0,56,57,0,0,21,17,0,0
Sygonix SY-4276902 {"NAME":"SYGONIX","GPIO":[0,0,0,17,133,132,0,0,131,52,21,0,0],"FLAG":0,"BASE":18}
TanTan WP2 {"NAME":"TanTan WP2","GPIO":[57,56,158,255,21,134,255,255,131,17,132,22,18],"FLAG":15,"BASE":18}
TanTan WP3 {"NAME":"TanTan WP3","GPIO":[0,0,56,0,17,0,0,0,57,0,21,0,0],"FLAG":0,"BASE":18}
-TCP Smart WISSINWUK {"NAME":"TCP_Plug","GPIO":[0,0,0,0,52,21,0,0,0,17,0,0,0],"FLAG":0,"BASE":45}
+TCP Smart 13A {"NAME":"TCP_Plug","GPIO":[0,0,0,0,52,21,0,0,0,17,0,0,0],"FLAG":0,"BASE":45}
Teckin SP10 {"NAME":"Teckin SP10","GPIO":[255,255,56,255,255,255,0,0,255,17,255,21,255],"FLAG":0,"BASE":18}
Teckin SP20 {"NAME":"TECKIN SP20","GPIO":[56,255,57,255,21,134,0,0,131,17,132,0,0],"FLAG":0,"BASE":45}
Teckin SP21 {"NAME":"Teckin SP21","GPIO":[0,0,56,0,0,0,0,0,0,17,0,21,0],"FLAG":0,"BASE":45}
@@ -827,7 +900,7 @@ TikLok TL650 {"NAME":"TikLok Mini","GPIO":[0,0,0,0,57,0,0,0,21,17,0,
Timethinker C338 {"NAME":"C338","GPIO":[17,0,255,0,0,0,0,0,21,52,255,0,0],"FLAG":0,"BASE":1}
Timethinker TK04 {"NAME":"TimethinkerEU","GPIO":[255,255,255,255,17,255,0,0,255,52,21,255,0],"FLAG":0,"BASE":18}
TimeThinker WS2 {"NAME":"TimeThinkerWS2","GPIO":[0,0,0,0,17,0,0,0,0,52,21,0,0],"FLAG":0,"BASE":18}
-TomaxUSA HKWL-SO07W {"NAME":"HKWL-SO07W","GPIO":[17,255,255,255,255,255,0,0,255,255,21,255,56],"FLAG":0,"BASE":18}
+TomaxUSA {"NAME":"HKWL-SO07W","GPIO":[17,255,255,255,255,255,0,0,255,255,21,255,56],"FLAG":0,"BASE":18}
Topersun WL-SC01 {"NAME":"Topersun ","GPIO":[0,0,0,0,52,0,0,0,21,0,17,0,0],"FLAG":0,"BASE":18}
TopGreener TGWF115APM {"NAME":"TGWF115APM","GPIO":[0,56,0,17,134,132,0,0,131,57,21,0,0],"FLAG":0,"BASE":18}
Torchstar LITEdge Smart {"NAME":"LITEdge Plug","GPIO":[0,17,0,0,0,0,0,0,0,52,21,0,0],"FLAG":0,"BASE":18}
@@ -842,7 +915,7 @@ Vaupan X6P {"NAME":"Vaupan 10a X6P","GPIO":[0,0,56,0,0,0,0,0,0,17,
Vettora Smart Plug {"NAME":"Vettora","GPIO":[0,0,0,17,0,0,0,0,0,56,21,0,0],"FLAG":0,"BASE":18}
Vingo {"NAME":"Karpal-01","GPIO":[0,0,0,0,0,56,0,0,21,17,0,0,0],"FLAG":0,"BASE":18}
Vivanco 39625 Smart Home Power Adapter {"NAME":"Vivianco","GPIO":[0,0,0,17,133,132,0,0,131,52,21,0,0],"FLAG":0,"BASE":18}
-Vivitar HA-1003 {"NAME":"Vivitar HA1003","GPIO":[158,0,56,0,0,0,0,0,0,17,0,21,0],"FLAG":0,"BASE":18}
+Vivitar {"NAME":"Vivitar HA1003","GPIO":[158,0,56,0,0,0,0,0,0,17,0,21,0],"FLAG":0,"BASE":18}
Vivitar HA-1006 {"NAME":"HA-1006","GPIO":[0,0,0,0,56,0,0,0,21,90,0,0,0],"FLAG":0,"BASE":18}
Vivitar HA-1006-AU {"NAME":"HA-1006-AU","GPIO":[0,0,0,0,56,0,0,0,21,90,0,0,0],"FLAG":0,"BASE":18}
WAGA life CHCZ02MB {"NAME":"WAGA CHCZ02MB","GPIO":[57,0,0,131,0,134,0,0,21,17,132,56,0],"FLAG":0,"BASE":68}
@@ -854,10 +927,11 @@ Wily Electronics {"NAME":"VC Plug","GPIO":[157,0,0,0,0,134,0,0,131,17,13
WiOn 50055 {"NAME":"WiOn","GPIO":[255,0,52,0,0,0,0,0,255,17,0,21,0],"FLAG":0,"BASE":17}
Wisdom ZY_ACU02 {"NAME":"ZY-ACU02","GPIO":[0,0,0,157,22,21,0,0,56,17,57,0,0],"FLAG":0,"BASE":18}
WL-SC01 {"NAME":"WL-SC01","GPIO":[0,0,0,0,56,0,0,0,21,0,17,0,0],"FLAG":0,"BASE":1}
-Woox R4026 {"NAME":"WOOX R4026","GPIO":[0,0,0,17,0,0,0,0,0,56,21,0,0],"FLAG":0,"BASE":18}
-Woox R4785 {"NAME":"WooxR4785","GPIO":[0,0,0,0,52,21,0,0,0,17,0,0,0],"FLAG":0,"BASE":18}
-WOOX R5024 {"NAME":"Woox5024","GPIO":[0,0,56,0,0,0,0,0,0,17,0,21,0],"FLAG":0,"BASE":18}
+WOOX R4026 {"NAME":"WOOX R4026","GPIO":[0,0,0,17,0,0,0,0,0,56,21,0,0],"FLAG":0,"BASE":18}
+WOOX R4785 {"NAME":"WOOXR4785","GPIO":[0,0,0,0,52,21,0,0,0,17,0,0,0],"FLAG":0,"BASE":18}
+WOOX R5024 {"NAME":"WOOX5024","GPIO":[0,0,56,0,0,0,0,0,0,17,0,21,0],"FLAG":0,"BASE":18}
WP211 {"NAME":"YUNTAB WP211","GPIO":[56,0,158,0,22,0,0,0,0,18,0,21,17],"FLAG":0,"BASE":18}
+WP5 {"NAME":"WP5","GPIO":[57,0,56,0,0,0,0,0,0,17,0,21,0],"FLAG":0,"BASE":18}
Wsiiroon {"NAME":"WSIIROON","GPIO":[0,0,0,0,17,56,0,0,21,0,0,0,0],"FLAG":0,"BASE":18}
Wsiiroon {"NAME":"Wsiiroon/Wsky","GPIO":[0,0,0,0,17,56,0,0,21,0,0,0,0],"FLAG":0,"BASE":18}
Wyze WLPP1 {"NAME":"WyzePlugWLPP1","GPIO":[0,0,0,0,0,56,0,0,21,0,17,0,0],"FLAG":0,"BASE":18}
@@ -869,7 +943,7 @@ XS-A14 {"NAME":"NETVIP XS-A14","GPIO":[37,0,38,0,0,17,0,0,39,2
XS-A17 {"NAME":"XS-A18","GPIO":[37,0,38,0,0,39,0,0,0,17,0,21,0],"FLAG":0,"BASE":45}
XS-A18 {"NAME":"XS-A18","GPIO":[37,0,38,0,0,39,0,0,0,17,0,21,0],"FLAG":0,"BASE":45}
XS-A23 {"NAME":"XS-A23","GPIO":[56,255,0,131,17,134,0,0,0,18,132,21,22],"FLAG":0,"BASE":45}
-XS-SSA01 {"NAME":"XS-SSA01","GPIO":[255,17,255,255,255,255,0,0,255,56,21,255,255],"FLAG":0,"BASE":18}
+XS-SSA01 {"NAME":"XS-SSA01","GPIO":[0,17,0,0,0,0,0,0,56,9,0,21,0],"FLAG":0,"BASE":18}
XS-SSA01 v2 {"NAME":"XS-SSA01","GPIO":[255,17,255,255,255,255,0,0,56,255,255,21,255],"FLAG":0,"BASE":18}
XS-SSA05 {"NAME":"XS-SSA05","GPIO":[30,255,255,131,255,133,0,0,21,17,132,31,255],"FLAG":1,"BASE":18}
XS-SSA06 {"NAME":"XS-SSA06","GPIO":[37,0,38,0,0,39,0,0,0,90,0,21,0],"FLAG":0,"BASE":18}
@@ -878,6 +952,7 @@ Yelomin JH-G01E {"NAME":"Yelomin","GPIO":[0,145,0,146,0,0,0,0,17,56,21,
YERON US101 {"NAME":"YERON_US101","GPIO":[255,255,255,17,133,132,0,0,131,56,21,255,255],"FLAG":0,"BASE":18}
YM-WS-1 Mini {"NAME":"YM-WS1","GPIO":[0,0,0,0,0,0,0,0,56,17,0,21,0],"FLAG":0,"BASE":18}
YM-WS-3 16A {"NAME":"YM-WS-3","GPIO":[0,17,0,0,0,0,0,0,0,52,21,0,158],"FLAG":1,"BASE":18}
+YT-E002 {"NAME":"YT-E002","GPIO":[158,0,0,0,18,0,0,0,22,17,0,21,0],"FLAG":0,"BASE":18}
YT-E003 {"NAME":"YT-E003-SP202","GPIO":[17,0,0,0,134,132,0,0,131,52,22,21,91],"FLAG":0,"BASE":64}
Yuanguo KS-501 {"NAME":"Yuanguo_KS-501","GPIO":[17,0,0,0,0,0,0,0,21,56,0,0,0],"FLAG":0,"BASE":18}
YX-DE01 {"NAME":"YX-DE01","GPIO":[0,17,0,0,0,0,0,0,0,56,21,0,0],"FLAG":0,"BASE":18}
@@ -895,8 +970,10 @@ A0F0 ZLD-44EU-W {"NAME":"AOFO-4AC-4USB","GPIO":[0,56,0,17,22,21,0,0,23,
Acenx 3AC+3USB {"NAME":"ACENX 3-Outlet","GPIO":[56,55,54,53,0,21,0,0,23,24,22,0,17],"FLAG":0,"BASE":18}
AHRise 4+4AC+4USB {"NAME":"AHRise-083","GPIO":[0,0,0,0,56,17,0,0,22,21,23,24,0],"FLAG":0,"BASE":18}
AHRise AHR-085 {"NAME":"AHRise AHR-085","GPIO":[0,0,0,0,17,56,0,0,22,23,21,0,0],"FLAG":0,"BASE":18}
+Aicliv 4AC 3USB {"NAME":"Aicliv WiFi","GPIO":[0,0,0,24,23,158,0,0,21,17,22,0,0],"FLAG":0,"BASE":18}
Annhome 3AC + 2USB {"NAME":"1200W WiFi SPS","GPIO":[32,0,0,0,57,52,0,0,21,17,22,23,33],"FLAG":0,"BASE":18}
AOFO 3AC+4USB {"NAME":"AOFO","GPIO":[0,56,0,17,22,21,0,0,0,23,24,0,0],"FLAG":1,"BASE":18}
+AOFO 4AC + 4USB {"NAME":"AOFO C379 4AC+4USB UK ","GPIO":[0,158,0,17,24,23,0,0,21,25,22,0,0],"FLAG":0,"BASE":18}
AOFO 4AC+4USB {"NAME":"AOFO4AC4USB","GPIO":[0,56,0,17,22,21,0,0,23,24,25,0,0],"FLAG":0,"BASE":18}
AOFO 4AC+4USB Tuya {"NAME":"AOFO-4AC-4USB","GPIO":[255,255,255,255,255,255,0,0,255,255,255,255,255],"FLAG":1,"BASE":54}
AOFO 4AC+4USB UK {"NAME":"AOFO4AC4USB-UK","GPIO":[0,56,0,17,23,24,0,0,22,21,33,0,0],"FLAG":0,"BASE":18}
@@ -909,18 +986,19 @@ BrilliantSmart 20691 Powerboard with USB Chargers {"NAME":"B_WiFi-4","GPIO":[56
CE Smart Home {"NAME":"CE Power Strip","GPIO":[52,0,0,0,22,21,0,0,24,23,25,26,17],"FLAG":0,"BASE":18}
CE Smart Home Garden Stake {"NAME":"CE Power Stake","GPIO":[0,0,0,0,56,57,0,0,21,17,0,0,0],"FLAG":0,"BASE":18}
CRST LTS-4G-W {"NAME":"CRST LTS-4G-W","GPIO":[0,0,0,0,24,0,0,0,22,23,21,0,0],"FLAG":0,"BASE":18}
-Deltaco SH-P03USB {"NAME":"Deltaco SH-P03","GPIO":[0,56,0,0,0,21,0,0,23,17,22,24,0],"FLAG":1,"BASE":18}
+Deltaco SH-P03USB {"NAME":"Deltaco SH-P03","GPIO":[56,0,0,0,0,23,0,0,21,17,22,24,0],"FLAG":0,"BASE":18}
Digoo DG-PS01 {"NAME":"Digoo DG-PS01","GPIO":[0,56,0,17,23,22,0,0,0,24,21,0,0],"FLAG":1,"BASE":18}
Geekbes 4AC+4USB {"NAME":"Geekbes 4xStri","GPIO":[0,56,0,17,22,21,0,0,23,24,25,0,0],"FLAG":1,"BASE":18}
Geeni Surge {"NAME":"Geeni GNCSW003","GPIO":[52,0,0,0,22,21,0,0,24,25,23,26,17],"FLAG":0,"BASE":18}
Geeni SURGE 6-Outlet Surge Protector {"NAME":"Geeni 6 Strip","GPIO":[56,0,0,0,22,21,0,0,24,25,23,26,0],"FLAG":0,"BASE":18}
Geeni Surge Mini {"NAME":"Geeni-GN-SW004","GPIO":[56,0,0,17,0,0,0,0,22,21,23,0,0],"FLAG":15,"BASE":18}
Gosund P1 {"NAME":"Gosund_P1","GPIO":[0,145,157,146,0,32,0,0,22,23,21,0,17],"FLAG":1,"BASE":18}
-Gousund WP9 {"NAME":"Gosund WP9","GPIO":[56,55,54,53,0,21,0,0,23,24,22,0,17],"FLAG":0,"BASE":18}
+Gosund WP9 {"NAME":"Gosund WP9","GPIO":[56,55,54,53,0,21,0,0,23,24,22,0,17],"FLAG":0,"BASE":18}
Heyvalue 3AC+3USB {"NAME":"HeyvalueHLT-330","GPIO":[52,0,53,0,24,29,0,0,30,20,31,0,0],"FLAG":0,"BASE":18}
-Heyvalue 4AC+4USB {"NAME":"Heyvalue HLT-331","GPIO":[56,0,57,0,25,22,0,0,24,9,23,21,0],"FLAG":0,"BASE":18}
+Heyvalue 4AC+4USB {"NAME":"Heyvalue HLT-331","GPIO":[57,0,158,56,32,17,0,0,30,31,29,0,25],"FLAG":0,"BASE":18}
HIPER IoT PS44 {"NAME":"HIPER IoT PS44","GPIO":[0,56,0,17,22,21,0,0,23,24,25,0,0],"FLAG":0,"BASE":18}
HLT-333 {"NAME":"BEYAWL","GPIO":[0,0,56,24,0,0,0,0,0,30,29,31,0],"FLAG":0,"BASE":18}
+Home Awesome 4AC 4USB {"NAME":"Home Awesome","GPIO":[52,0,53,0,25,22,0,0,24,17,23,21,0],"FLAG":0,"BASE":18}
Hyleton 330 {"NAME":"Hyleton-330","GPIO":[57,0,0,56,29,17,0,0,31,30,32,0,25],"FLAG":0,"BASE":18}
Hyleton 331 {"NAME":"HLT-331","GPIO":[52,255,255,57,29,17,0,0,31,30,32,255,58],"FLAG":0,"BASE":18}
Hyleton 333 {"NAME":"HLT-333","GPIO":[52,0,0,57,29,17,0,0,31,30,0,0,24],"FLAG":0,"BASE":18}
@@ -935,15 +1013,17 @@ Luminea 3AC+4USB 16A {"NAME":"Luminea-NX4473","GPIO":[0,56,0,17,22,21,0,0,0,
Maxcio ZLD-34EU-W {"NAME":"MAXCIO","GPIO":[0,56,0,17,22,21,0,0,0,23,24,0,0],"FLAG":1,"BASE":18}
Meross MSS425 {"NAME":"Meross MSS425","GPIO":[33,0,0,0,56,0,0,0,21,17,22,23,32],"FLAG":0,"BASE":18}
Mirabella Genio 4 Outlet Power Board with 2 USB {"NAME":"Genio i002340","GPIO":[56,0,0,0,21,22,0,0,23,17,24,25,0],"FLAG":0,"BASE":18}
-Mirabella Genio Powerboard I002578 {"NAME":"Genio Powerboa","GPIO":[21,52,0,0,23,22,0,0,25,17,26,24,0],"FLAG":0,"BASE":18}
+Mirabella Genio Powerboard {"NAME":"Genio Powerboa","GPIO":[21,52,0,0,23,22,0,0,25,17,26,24,0],"FLAG":0,"BASE":18}
Nedis P310 {"NAME":"Nedis WIFIP310","GPIO":[0,56,0,17,22,21,0,0,0,23,24,0,0],"FLAG":1,"BASE":18}
Nozdom 3AC+2USB {"NAME":"NOZDOM SWB3","GPIO":[157,0,53,0,0,23,0,0,21,17,22,24,0],"FLAG":0,"BASE":18}
Powrui AHR-079 {"NAME":"Powrui Power S","GPIO":[56,0,0,17,19,21,0,0,23,20,22,24,18],"FLAG":0,"BASE":18}
+Powrui AHR-081 {"NAME":"POWRUI AHR-081","GPIO":[0,0,0,0,56,17,0,0,22,23,21,0,0],"FLAG":0,"BASE":18}
Powrui AW-39 {"NAME":"Powrui AW-39","GPIO":[56,0,0,0,21,255,0,0,23,24,22,0,9],"FLAG":0,"BASE":18}
S2199EU {"NAME":"S2199EU","GPIO":[0,17,0,52,23,25,0,0,21,24,22,0,0],"FLAG":1,"BASE":18}
SA-P402A {"NAME":"SA-P402A","GPIO":[0,17,0,56,23,25,21,24,22,0,0,1,0],"FLAG":1,"BASE":18}
STITCH by Monoprice 34082 {"NAME":"Stitch 34082","GPIO":[255,255,255,255,21,20,0,0,23,22,24,255,255],"FLAG":0,"BASE":18}
SWB1 {"NAME":"SWB1","GPIO":[52,0,0,0,0,24,0,0,21,17,22,23,0],"FLAG":0,"BASE":18}
+SWB2 3AC + 2USB {"NAME":"SWB2","GPIO":[158,255,0,255,0,23,0,0,21,17,22,24,0],"FLAG":0,"BASE":18}
TCP Smart 4AC+USB {"NAME":"TCP WPS4WUK","GPIO":[255,56,0,17,23,24,0,0,22,21,25,0,0],"FLAG":15,"BASE":18}
Teckin SS30 {"NAME":"Teckin SS30","GPIO":[52,255,255,57,29,17,0,0,31,30,32,255,25],"FLAG":0,"BASE":18}
Tellur TLL331031 {"NAME":"Tellur","GPIO":[0,56,0,17,22,21,0,0,0,23,24,0,0],"FLAG":1,"BASE":18}
@@ -954,7 +1034,8 @@ Vivitar HA-1007 {"NAME":"Vivitar HA-1007 Power Strip","GPIO":[157,0,0,0
Vivitar HA-1007-AU {"NAME":"HA-1007-AU","GPIO":[56,17,0,58,29,57,0,0,31,30,32,0,25],"FLAG":0,"BASE":18}
wesmartify essentials 4AC+4USB {"NAME":"essential_4_po","GPIO":[56,0,0,0,24,25,0,0,22,21,23,0,17],"FLAG":0,"BASE":18}
Wipro Smart Extension {"NAME":"Generic","GPIO":[57,0,0,0,32,0,0,0,30,31,29,0,25],"FLAG":0,"BASE":18}
-Woox R4028 {"NAME":"Woox R4028","GPIO":[0,56,0,17,23,22,0,0,0,24,21,0,0],"FLAG":1,"BASE":18}
+WOOX R4028 {"NAME":"WOOX R4028","GPIO":[0,56,0,17,23,22,0,0,0,24,21,0,0],"FLAG":1,"BASE":18}
+WP40 {"NAME":"WP40","GPIO":[33,0,0,0,56,0,0,0,21,17,22,23,32],"FLAG":0,"BASE":18}
Xenon SM-S0301 {"NAME":"SM-SO301","GPIO":[52,255,255,57,29,17,0,0,31,30,32,255,25],"FLAG":0,"BASE":18}
Xenon SM-S0301-U {"NAME":"SM-SO301-U","GPIO":[52,255,255,57,29,17,0,0,31,30,32,255,25],"FLAG":0,"BASE":18}
Xenon SM-SO301 v2 {"NAME":"SM-SO301","GPIO":[56,0,0,0,30,17,0,0,32,31,33,0,21],"FLAG":0,"BASE":18}
@@ -1005,13 +1086,13 @@ Avatar ALS18L A60 800lm {"NAME":"Avatar E14 7W","GPIO":[0,0,0,0,38,37,0,0,41,39
B.K.Licht BKL1253 9W 806lm {"NAME":"BKL1253","GPIO":[0,0,0,0,38,37,0,0,41,39,40,0,0],"FLAG":0,"BASE":18}
Bakibo TB95 9W 1000lm {"NAME":"Bakibo A19 9W","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":0,"BASE":18}
BAZZ BR30 650lm {"NAME":"BAZZrgb","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":1,"BASE":18}
-BlitzWolf BW-LT27 w/ remote 850lm {"NAME":"BW-LT27","GPIO":[0,0,0,0,41,38,0,0,39,51,40,37,0],"FLAG":0,"BASE":18}
+BlitzWolf w/ remote 850lm {"NAME":"BW-LT27","GPIO":[0,0,0,0,41,38,0,0,39,51,40,37,0],"FLAG":0,"BASE":18}
BNETA 8.5W 800lm {"NAME":"BNETA IO-WIFI60-E27P","GPIO":[0,0,0,0,37,40,0,0,38,50,39,0,0],"FLAG":0,"BASE":18}
BNETA 8.5W 800lm {"NAME":"OM60/RGBW","GPIO":[0,0,0,0,140,37,0,0,38,142,141,0,0],"FLAG":0,"BASE":18}
Bomcosy 600lm {"NAME":"Generic","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":1,"BASE":18}
-Calex 429002 Reflector 350lm {"NAME":"Calex RGBW","GPIO":[0,0,0,0,0,0,0,0,181,0,180,0,0],"FLAG":0,"BASE":18}
Calex 429004 A60 806lm {"NAME":"Calex E27 RGB ","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":0,"BASE":18}
Calex 429008 B35 5W 470lm {"NAME":"Calex E14 RGBW","GPIO":[0,0,0,0,0,0,0,0,181,0,180,0,0],"FLAG":0,"BASE":18}
+Calex 5W 350lm Reflector {"NAME":"Calex RGBW","GPIO":[0,0,0,0,0,0,0,0,181,0,180,0,0],"FLAG":0,"BASE":18}
Cleverio 51395 806lm {"NAME":"CleverioE27RGB","GPIO":[0,0,0,0,140,37,0,0,38,142,141,0,0],"FLAG":0,"BASE":18}
CMARS 4W Reflector {"NAME":"RGBWW GU10","GPIO":[0,0,0,0,40,41,0,0,38,39,37,0,0],"FLAG":15,"BASE":18}
Dogain 320lm {"NAME":"DOGAIN","GPIO":[0,0,0,0,40,41,0,0,38,39,37,0,0],"FLAG":0,"BASE":18}
@@ -1021,7 +1102,7 @@ Euri Lighting 10W 800lm {"NAME":"Euri Lighting ","GPIO":[0,0,0,0,37,40,0,0,38,4
EXUP C37 5W {"NAME":"EXUP","GPIO":[0,0,0,0,40,41,0,0,38,39,37,0,0],"FLAG":15,"BASE":18}
Feit Electric A19 1600lm {"NAME":"OM100/RGBWW","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":0,"BASE":18}
Feit Electric A19 800lm {"NAME":" BPA800/RGBW/AG/2","GPIO":[0,0,0,0,140,37,0,0,38,142,141,0,0],"FLAG":0,"BASE":18}
-Feit Electric A19 800lm {"NAME":" BPA800/RGBW/AG/2P","GPIO":[0,0,0,0,37,38,0,0,141,142,140,0,0],"FLAG":0,"BASE":48}
+Feit Electric A19 800lm {"NAME":"BPA800/RGBW/AG/2P","GPIO":[0,0,0,0,37,47,0,0,141,142,140,0,0],"FLAG":0,"BASE":48}
Feit Electric A19 800lm {"NAME":"FE-OM60-15K-AG","GPIO":[0,0,0,0,141,140,0,0,37,142,38,0,0],"FLAG":0,"BASE":18}
Feit Electric A19 800lm {"NAME":"OM60/RGBW","GPIO":[0,0,0,0,140,37,0,0,38,142,141,0,0],"FLAG":0,"BASE":18}
Fitop 9W {"NAME":"E27RGBCCT9w","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":15,"BASE":18}
@@ -1029,6 +1110,7 @@ Fulighture 9W 810lm {"NAME":"Fulighture 9W","GPIO":[0,0,0,0,38,37,0,0,41,39
Geeni Prisma 10W 1050lm {"NAME":"Geeni Prisma 1050 RGB","GPIO":[0,0,0,0,141,140,0,37,38,142,0,0,0],"FLAG":0,"BASE":18}
Geeni Prisma Plus 800lm {"NAME":"Geeni Prisma P","GPIO":[0,0,0,0,140,37,0,0,38,142,141,0,0],"FLAG":0,"BASE":18}
Generic GU10 5W 460lm {"NAME":"RGBCCT GU10","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":0,"BASE":18}
+Girier 9W {"NAME":"GIRIER E27 9W ","GPIO":[0,0,0,0,41,38,0,0,39,0,40,37,0],"FLAG":0,"BASE":18}
Globe A19 10W 800lm {"NAME":"GlobeRGBWW","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":0,"BASE":18}
Helloify BR30 9W 600lm {"NAME":"Helloify","GPIO":[255,255,255,255,37,40,255,255,38,41,39,255,255],"FLAG":0,"BASE":18}
HIPER IoT A61 {"NAME":"HIPER IoT A61","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":0,"BASE":18}
@@ -1066,6 +1148,7 @@ LVWIT A60 8.5W 806lm {"NAME":"LVWIT A60 8.5W","GPIO":[0,0,0,0,37,40,0,0,38,4
LVWIT A70 12W 1521lm {"NAME":"LVWIT A70 12W","GPIO":[0,0,0,0,37,40,0,0,38,50,39,0,0],"FLAG":0,"BASE":18}
LVWIT BR30 8.5W 650lm {"NAME":"LVWIT BR30 8.5W","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":0,"BASE":18}
LVWIT G45 5W 470Lm {"NAME":"LVWIT E14 5W G45 RGBWCCT","GPIO":[0,0,0,0,0,0,0,0,181,0,180,0,0],"FLAG":0,"BASE":18}
+Lyhope 7W 650lm {"NAME":"Lyhope 014BB06","GPIO":[0,0,0,0,38,37,0,0,41,39,40,0,0],"FLAG":0,"BASE":18}
MagicHome 7W {"NAME":"MagicHome E27","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":0,"BASE":18}
Moes 9W 800lm {"NAME":"Moes 9w","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":0,"BASE":18}
Nishica JBT 9W 806lm {"NAME":"Nishica","GPIO":[0,0,0,0,38,37,0,0,41,39,40,0,0],"FLAG":0,"BASE":18}
@@ -1087,6 +1170,7 @@ Positivo 10W 806lm {"NAME":"Positivo Bulb","GPIO":[0,0,0,0,37,40,0,0,38,50
Powertech SL225X 800lm {"NAME":"Jaycar SL225X","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":0,"BASE":18}
Qualitel ALS08L 1100lm {"NAME":"Qualitel ALS08","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":0,"BASE":18}
Reafoo A26 9W {"NAME":"ReaFooE26","GPIO":[0,0,0,0,41,38,0,0,39,0,40,37,0],"FLAG":0,"BASE":18}
+Reafoo A27 9W 810lm {"NAME":"ReaFooE27","GPIO":[0,0,0,0,41,40,0,0,37,0,39,38,0],"FLAG":0,"BASE":18}
RYE 5W 450LM Candle {"NAME":"RYE Candlebra","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":0,"BASE":18}
Saudio A19 7W 700lm {"NAME":"X002BU0DOL","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":0,"BASE":18}
Sealight A19 9W 810lm {"NAME":"DGO/SEASTAR","GPIO":[0,0,0,0,38,37,0,0,41,39,40,0,0],"FLAG":0,"BASE":18}
@@ -1122,12 +1206,14 @@ Aoycocr Q9WM A21 10W 900lm {"NAME":"Aoycocr Q9WM","GPIO":[0,0,0,0,0,39,0,0,38,4
Avatar ALS08L A19 910lm {"NAME":"Avatar E27 7W","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":0,"BASE":20}
Avatar ALS09L A60 900lm {"NAME":"Avatar E14 9W","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":0,"BASE":20}
Avatar ALS11L PAR16 500lm {"NAME":"Avatar_GU10","GPIO":[0,0,0,0,0,0,0,0,0,143,0,144,0],"FLAG":0,"BASE":27}
+Avatar Controls A19 10W 900lm {"NAME":"Avatar E26 10W","GPIO":[0,0,0,0,0,39,0,0,38,0,37,40,0],"FLAG":0,"BASE":18}
+AWOW A60 9W 800lm {"NAME":"AWOW 9W RGBW","GPIO":[0,0,0,0,37,40,0,0,38,0,39,0,0],"FLAG":0,"BASE":18}
Axtee AI-003 A19 700lm {"NAME":"Axtee E26 7W","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":0,"BASE":20}
Bawoo EUWL122130 925lm {"NAME":"Bawoo","GPIO":[0,0,0,0,140,37,0,0,0,142,141,0,0],"FLAG":0,"BASE":18}
-BlitzWolf BW-LT21 900lm {"NAME":"BlitzWolf LT21","GPIO":[0,0,0,0,41,39,0,0,38,0,37,40,0],"FLAG":0,"BASE":18}
+BlitzWolf 900lm {"NAME":"BlitzWolf LT21","GPIO":[0,0,0,0,41,39,0,0,38,0,37,40,0],"FLAG":0,"BASE":18}
BNeta 4.5W 380lm {"NAME":"BNeta","GPIO":[0,0,0,0,141,140,0,0,37,142,0,0,0],"FLAG":0,"BASE":18}
BriHome 6,5W 500lm {"NAME":"BRI E27 6,5W","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":0,"BASE":20}
-Brilliant HK17653S72 350lm {"NAME":"HK17653S72","GPIO":[0,0,0,0,37,40,0,0,38,0,39,0,0],"FLAG":0,"BASE":18}
+Brilliant 350lm Candle {"NAME":"HK17653S72","GPIO":[0,0,0,0,37,40,0,0,38,0,39,0,0],"FLAG":0,"BASE":18}
BrilliantSmart 20698 9W 800lm {"NAME":"Brilliant20698","GPIO":[0,0,0,0,141,140,0,0,37,142,0,0,0],"FLAG":0,"BASE":18}
BrilliantSmart 20699 9W 800lm {"NAME":"Brilliant20699","GPIO":[0,0,0,0,141,140,0,0,37,142,0,0,0],"FLAG":0,"BASE":18}
BrilliantSmart 20741 9W 750lm {"NAME":"Brilliant RGB+","GPIO":[0,0,0,0,37,40,0,0,38,0,39,0,0],"FLAG":0,"BASE":18}
@@ -1140,8 +1226,12 @@ BTZ1 {"NAME":"WifiBulb","GPIO":[0,0,0,0,0,37,0,0,39,40,38,0,
Cleverio 4.5W 350lm {"NAME":"HK17653S72","GPIO":[0,0,0,0,37,40,0,0,38,0,39,0,0],"FLAG":0,"BASE":18}
Cleverio 51398 370lm {"NAME":"CleverioGU10","GPIO":[0,0,0,0,140,37,0,0,38,142,141,0,0],"FLAG":0,"BASE":18}
Cocoon DY180363-B 800lm {"NAME":"Cocoon RGBW","GPIO":[0,0,0,0,37,40,0,0,38,0,39,0,0],"FLAG":0,"BASE":18}
-Connect SmartHome 5W {"NAME":"Connect CSH-E1","GPIO":[0,0,0,0,40,41,0,0,38,39,37,0,0],"FLAG":0,"BASE":18}
+Connect SmartHome 10W {"NAME":"CSH-B22RGB10W","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":0,"BASE":18}
+Connect SmartHome 10W {"NAME":"CSH-E27RGB10W","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":0,"BASE":18}
+Connect SmartHome 5W {"NAME":"CSH-E14RGB5W","GPIO":[0,0,0,0,40,41,0,0,38,39,37,0,0],"FLAG":0,"BASE":18}
+Connex A70 10W 1050lm {"NAME":"Connex 10w RGBWW","GPIO":[0,0,0,38,140,37,0,0,0,142,141,0,0],"FLAG":0,"BASE":18}
Connex Connect A60 6W 470lm {"NAME":"Connex RGBW Bu","GPIO":[0,0,0,0,37,40,0,0,38,0,39,0,0],"FLAG":0,"BASE":18}
+DailyComb 7W 600lm {"NAME":"DailyComb RGBW Bulb","GPIO":[0,0,0,0,37,40,0,0,38,0,39,0,0],"FLAG":0,"BASE":18}
Elari A60 6W 470lm {"NAME":"OM60/RGBW","GPIO":[0,0,0,0,140,37,0,0,38,142,141,0,0],"FLAG":0,"BASE":18}
electriQ 600lm {"NAME":"ElectricQ B22","GPIO":[0,0,0,0,37,41,0,0,38,40,39,0,0],"FLAG":0,"BASE":18}
EleLight 350lm {"NAME":"EleLight 7wA19","GPIO":[0,0,0,0,140,37,0,0,0,142,141,0,0],"FLAG":0,"BASE":18}
@@ -1152,7 +1242,9 @@ Fcmila 7W {"NAME":"FCMILA E27 0.1","GPIO":[0,0,0,0,37,40,0,0,38,0
Fcmila Spotlight 460lm {"NAME":"Fcmila LED 6W","GPIO":[0,0,0,0,40,0,0,0,38,39,37,0,0],"FLAG":0,"BASE":18}
Feit Electric BR30 650lm {"NAME":"BR30/RGBW","GPIO":[0,0,0,0,140,37,0,0,38,142,141,0,0],"FLAG":0,"BASE":18}
Feit Electric BR30 700lm {"NAME":"Feit BR30/RGBW","GPIO":[0,0,0,38,141,140,0,0,0,142,37,0,0],"FLAG":0,"BASE":18}
+FFHL 12W 900lm {"NAME":"FFHL RGBW Bulb","GPIO":[0,0,0,0,0,37,0,0,39,40,38,0,0],"FLAG":0,"BASE":18}
Fulighture A60 810lm {"NAME":"Fulighture A60","GPIO":[0,0,0,0,38,37,0,0,0,39,40,0,0],"FLAG":0,"BASE":18}
+Garsent 10W {"NAME":"Garsent 10W RGBW-Bulb","GPIO":[0,0,0,0,0,39,0,0,38,0,37,40,0],"FLAG":1,"BASE":18}
Generic GU10 5W 450lm {"NAME":"RGBCW GU10","GPIO":[0,255,0,255,40,0,0,0,38,39,37,0,0],"FLAG":0,"BASE":3}
Gosund 8W 800lm {"NAME":"Gosund RGBW 8W","GPIO":[0,0,0,0,41,40,0,0,37,38,39,0,0],"FLAG":0,"BASE":18}
Gosund WB3 8W 800lm {"NAME":"Gosund WB3","GPIO":[0,0,0,0,40,0,0,0,37,38,39,0,0],"FLAG":0,"BASE":18}
@@ -1160,12 +1252,15 @@ Hama 10W 1050lm {"NAME":"Hama Bulb RGBW","GPIO":[0,0,0,0,140,37,0,0,0,1
Hama 10W 806lm {"NAME":"Hama Smart WiF","GPIO":[0,0,0,0,37,40,0,0,38,0,39,0,0],"FLAG":0,"BASE":18}
Hama 4.5W {"NAME":"hama E14 RGB","GPIO":[0,0,0,0,37,40,0,0,38,0,39,0,0],"FLAG":0,"BASE":18}
Hiiten A19 7W 650lm {"NAME":"Hiiten Bulb","GPIO":[37,0,0,0,140,38,0,0,0,142,141,0,0],"FLAG":0,"BASE":18}
+Hikenri 7W 640lm {"NAME":"HIKENRI E27 RGB","GPIO":[17,0,0,0,0,0,0,0,0,143,0,144,0],"FLAG":0,"BASE":27}
+Hombli 4.5W 380lm Candle {"NAME":"Hombli E14 RGB","GPIO":[0,0,0,0,141,140,0,0,0,142,37,0,0],"FLAG":0,"BASE":18}
Hykker SL-0492 810lm {"NAME":"Hykker RBGW 9W","GPIO":[0,0,0,0,0,40,0,0,37,0,39,38,0],"FLAG":0,"BASE":18}
+Jetstream 9W 800lm {"NAME":"Jetstream MA19CL","GPIO":[0,0,0,0,37,0,0,0,141,142,140,0,0],"FLAG":0,"BASE":18}
Kainsy 600lm {"NAME":"KAINSY","GPIO":[17,0,0,0,143,144,0,0,0,0,0,0,0],"FLAG":0,"BASE":27}
Kkmoon 9W 800lm {"NAME":"KKMOON V21","GPIO":[0,0,0,0,40,0,0,0,38,39,37,0,0],"FLAG":0,"BASE":18}
Koaanw 650lm {"NAME":"KOAANW Bulb","GPIO":[0,0,0,0,143,144,0,0,0,0,0,0,0],"FLAG":0,"BASE":27}
Kogan 10W Ambient 1050lm {"NAME":"Kogan RGB","GPIO":[0,0,0,0,140,37,0,0,0,0,141,0,0],"FLAG":0,"BASE":18}
-Kogan 4.5W 330lm 110� {"NAME":"Kogan_GU10","GPIO":[0,0,0,0,39,40,0,0,37,0,38,0,0],"FLAG":0,"BASE":18}
+Kogan 4.5W 330lm 110C {"NAME":"Kogan_GU10","GPIO":[0,0,0,0,39,40,0,0,37,0,38,0,0],"FLAG":0,"BASE":18}
Kogan Ambient Candle {"NAME":"Kogan_E14","GPIO":[0,0,0,0,37,40,0,0,38,0,39,0,0],"FLAG":0,"BASE":18}
Kuled 800lm {"NAME":"KULED 60W RGB","GPIO":[0,0,0,0,39,40,0,0,37,0,38,0,0],"FLAG":1,"BASE":18}
Laideyi 7W {"NAME":"7W-E14-RGBW-La","GPIO":[0,0,0,0,38,37,0,0,39,0,40,0,0],"FLAG":0,"BASE":18}
@@ -1187,6 +1282,7 @@ LTC 10W {"NAME":"LTC LXU403","GPIO":[0,0,0,0,40,0,0,0,38,39,37,
Lumiman LM530 7.5W 800lm {"NAME":"Lumiman LM530","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":1,"BASE":18}
Lumiman LM530 7.5W 800lm {"NAME":"Lumiman LM530","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":1,"BASE":18}
Lumiman LM530 7.5W 800lm {"NAME":"LM530","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":1,"BASE":18}
+Luminea 4.5W 350lm Candle {"NAME":"Luminea NX4462 RGB+W","GPIO":[0,0,0,0,39,40,0,0,37,0,38,0,0],"FLAG":0,"BASE":18}
Luminea ZX-2832 {"NAME":"Luminea RGBW","GPIO":[0,0,0,0,140,37,0,0,38,142,141,0,0],"FLAG":1,"BASE":18}
Luminea ZX-2986 1400lm {"NAME":"Luminea RGBW","GPIO":[0,0,0,0,37,41,0,0,0,38,39,40,0],"FLAG":0,"BASE":18}
LWE3 600lm {"NAME":"Linganzh LWE3 ","GPIO":[0,0,0,0,0,38,0,0,39,0,40,37,0],"FLAG":0,"BASE":18}
@@ -1196,13 +1292,14 @@ Maxcio YX-L01P-E27-2P 9W {"NAME":"Maxcio YXL01P","GPIO":[17,0,0,0,143,144,0,0,0
Melery 5W {"NAME":"MeleryMR16","GPIO":[0,0,0,0,0,0,0,0,0,143,0,144,0],"FLAG":0,"BASE":27}
Merkury 75W 1050lm {"NAME":"MIC-BW904-999W","GPIO":[38,0,0,0,141,140,0,0,37,142,0,0,0],"FLAG":0,"BASE":18}
Merkury A21 10W 1050lm {"NAME":"MI-BW210-999W","GPIO":[38,0,0,0,140,37,0,0,0,142,141,0,0],"FLAG":0,"BASE":69}
+Merkury A21 10W 1050lm {"NAME":"MI-BW210-999W","GPIO":[0,0,0,0,39,38,0,0,0,37,40,0,0],"FLAG":0,"BASE":18}
+Merkury BR30 8W 750lm {"NAME":"MI-BW906-999W","GPIO":[0,0,0,0,140,37,0,0,38,142,141,0,0],"FLAG":0,"BASE":18}
Merkury MI-BW904-999W 1050lm {"NAME":"MI-BW904-999W","GPIO":[0,0,0,0,140,37,0,0,0,142,141,0,0],"FLAG":0,"BASE":18}
Merkury MI-BW904-999W v2 1050lm {"NAME":"MI-BW210-999W","GPIO":[0,0,0,0,38,37,0,0,141,142,140,0,0],"FLAG":0,"BASE":48}
Merkury MI-BW904-999W v3 {"NAME":"MI-BW904-999W","GPIO":[0,0,0,0,37,38,0,0,141,142,140,0,0],"FLAG":0,"BASE":69}
-Merkury MI-BW906-999W BR30 750lm {"NAME":"MI-BW906-999W","GPIO":[0,0,0,0,38,37,0,0,141,142,140,0,0],"FLAG":0,"BASE":18}
Mimoodz A19 6.5W {"NAME":"Miimoodz RGBCW LED","GPIO":[0,0,0,0,180,0,0,0,0,0,181,0,0],"FLAG":0,"BASE":18}
Mirabella Genio 9W 800lm {"NAME":"GenioBulbRGB","GPIO":[0,0,0,0,37,40,0,0,38,0,39,0,0],"FLAG":0,"BASE":18}
-Mirabella Genio 9W 800lm {"NAME":"GenioBulbRGB","GPIO":[0,0,0,0,37,40,0,0,38,0,39,0,0],"FLAG":0,"BASE":18}
+Mirabella Genio 9W 800lm {"NAME":"MiraBellaGenio","GPIO":[0,0,0,0,0,0,0,0,181,0,180,0,0],"FLAG":0,"BASE":18}
Mirabella Genio 9W 800lm {"NAME":"MiraBellaGenio","GPIO":[0,0,0,0,0,0,0,0,181,0,180,0,0],"FLAG":0,"BASE":18}
Mixigoo 950lm {"NAME":"Mixigoo Bulb","GPIO":[0,0,0,0,37,40,0,0,38,0,39,0,0],"FLAG":0,"BASE":18}
MoKo GU10 {"NAME":"MoKo GU10","GPIO":[255,255,255,255,39,255,255,255,38,41,37,40,255],"FLAG":15,"BASE":18}
@@ -1229,8 +1326,9 @@ Solimo 12W {"NAME":"Solimo RGBCCT 12","GPIO":[0,0,0,0,37,41,0,0,38
Solimo 810lm {"NAME":"Solimo RGBWW 9","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":0,"BASE":18}
Swisstone SH 320 350lm {"NAME":"SH 320","GPIO":[0,0,0,0,37,40,0,0,38,0,39,0,0],"FLAG":0,"BASE":18}
Swisstone SH 340 806lm {"NAME":"SH 340","GPIO":[0,0,0,0,140,37,0,0,0,142,141,0,0],"FLAG":15,"BASE":18}
-Syska 720lm {"NAME":"SyskaSmartBulb","GPIO":[0,0,0,0,37,40,0,0,38,0,39,0,0],"FLAG":0,"BASE":18}
-TCP Smart 806lm {"NAME":"TCP Smart RGBW","GPIO":[0,0,0,0,140,37,0,0,38,142,141,0,0],"FLAG":0,"BASE":18}
+Syska 7W 480lm {"NAME":"Syska","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":0,"BASE":18}
+Syska 9W 720lm {"NAME":"SyskaSmartBulb","GPIO":[0,0,0,0,37,40,0,0,38,0,39,0,0],"FLAG":0,"BASE":18}
+TCP Smart 9W 806lm {"NAME":"TCP Smart RGBW","GPIO":[0,0,0,0,140,37,0,0,38,142,141,0,0],"FLAG":0,"BASE":18}
Teckin 7.5W 800lm {"NAME":"Teckin SB60","GPIO":[0,0,0,0,37,40,0,0,38,0,39,0,0],"FLAG":0,"BASE":18}
Teckin SB50 800lm {"NAME":"Teckin SB50","GPIO":[0,0,0,0,40,0,0,0,38,39,37,0,0],"FLAG":0,"BASE":18}
Teckin SB50 v2 800lm {"NAME":"Teckin SB50","GPIO":[0,0,0,0,37,0,0,0,38,40,39,0,0],"FLAG":0,"BASE":18}
@@ -1245,7 +1343,7 @@ Wixann C37 5W 450lm {"NAME":"WIXANNE12","GPIO":[0,0,0,0,37,40,0,0,38,41,39,
Woopower 460lm {"NAME":"Woopower E14","GPIO":[0,0,0,0,0,0,0,0,0,143,0,144,0],"FLAG":0,"BASE":27}
WOOX R4553 650lm {"NAME":"WOOX R4553","GPIO":[0,0,0,0,37,40,0,0,38,0,39,0,0],"FLAG":0,"BASE":18}
WOOX R5076 4W 350lm {"NAME":"WOOX R4553","GPIO":[0,0,0,0,37,40,0,0,38,0,39,0,0],"FLAG":0,"BASE":18}
-Woox R5077 {"NAME":"WOOX R5077","GPIO":[0,0,0,0,140,37,0,0,38,142,141,0,0],"FLAG":0,"BASE":18}
+WOOX R5077 {"NAME":"WOOX R5077","GPIO":[0,0,0,0,140,37,0,0,38,142,141,0,0],"FLAG":0,"BASE":18}
Zemismart 5W {"NAME":"Zemismart_GU10","GPIO":[0,0,0,0,0,0,0,0,0,143,0,144,0],"FLAG":0,"BASE":27}
Zemismart A19 10W {"NAME":"Zemism_E27_A19","GPIO":[0,0,0,0,0,0,0,0,0,143,0,144,0],"FLAG":0,"BASE":27}
Zilotek A19 800lm {"NAME":"Zilotek RGBW","GPIO":[0,0,0,0,140,37,0,0,38,142,141,0,0],"FLAG":0,"BASE":18}
@@ -1253,153 +1351,110 @@ Zilotek A19 800lm {"NAME":"Zilotek RGBW","GPIO":[0,0,0,0,140,37,0,0,38,14
## Relay
```
-1 Channel Inching/Self-Locking {"NAME":"1 Channel","GPIO":[17,0,0,0,0,0,0,0,21,56,0,0,0],"FLAG":0,"BASE":12}
Anmbest 2 Channel Inching Self-locking Switch Module {"NAME":"Generic","GPIO":[17,255,255,255,255,22,18,0,21,56,0,0,0],"FLAG":0,"BASE":1}
-ATMS1601 230VAC DIN Timer/Switch {"NAME":"ATMS1601","GPIO":[255,255,255,255,157,56,255,255,21,17,255,255,255],"FLAG":15,"BASE":18}
-BlitzWolf BW-SS1 {"NAME":"BW-SS1","GPIO":[255,255,255,255,157,21,0,0,255,17,255,255,0],"FLAG":0,"BASE":18}
-BlitzWolf BW-SS5 1 Gang {"NAME":"BlitzWolf SS5 1 Gang","GPIO":[0,0,0,0,0,0,0,0,9,21,0,0,0],"FLAG":0,"BASE":18}
-BlitzWolf BW-SS5 2 Gang {"NAME":"BlitzWolf SS5 2 Gang","GPIO":[0,0,0,0,160,0,0,0,43,42,21,22,0],"FLAG":0,"BASE":18}
-BlitzWolf SS4 {"NAME":"BlitzWolf SS4 Two Gang","GPIO":[0,0,0,0,56,21,0,0,22,17,0,0,0],"FLAG":0,"BASE":18}
-Canwing CW-001 {"NAME":"Canwing CW-001","GPIO":[17,255,0,255,0,0,0,0,21,56,0,0,0],"FLAG":0,"BASE":1}
-Century Aoke Smart Switch {"NAME":"CenturyAoke","GPIO":[0,255,0,255,21,0,0,0,17,56,255,0,0],"FLAG":0,"BASE":18}
-Deta 6000HA Smart Inline Switch {"NAME":"DETA-6000HA","GPIO":[0,17,0,0,0,0,0,0,0,56,21,0,0],"FLAG":0,"BASE":18}
-dewenwils Outdoor Timer Box {"NAME":"Dewenwils50054","GPIO":[0,0,54,0,0,0,0,0,0,17,0,21,0],"FLAG":0,"BASE":18}
DoHome HomeKit DIY Switch {"NAME":"DoHome DIY","GPIO":[255,255,0,255,255,157,0,0,21,0,0,0,0],"FLAG":0,"BASE":1}
Eachen ST-DC2 {"NAME":"Garage Control","GPIO":[11,0,0,0,23,22,18,0,21,52,12,24,0],"FLAG":1,"BASE":18}
Eachen ST-UDC1 {"NAME":"ST-UDC1","GPIO":[9,0,0,0,0,0,0,0,21,56,0,0,0],"FLAG":1,"BASE":18}
Electrodragon Board SPDT {"NAME":"ED Relay Board","GPIO":[255,255,255,255,255,255,0,0,21,22,255,255,52],"FLAG":1,"BASE":18}
Electrodragon ESP8266 {"NAME":"ElectroDragon","GPIO":[18,255,17,255,255,255,0,0,22,21,255,255,52],"FLAG":1,"BASE":15}
-eMylo 2 Channel {"NAME":"eMylo XL9252WI","GPIO":[0,255,0,0,56,22,0,0,21,0,12,0,0],"FLAG":0,"BASE":18}
-eMylo SS-8839-02 {"NAME":"SS-8839-02","GPIO":[0,255,0,255,56,0,0,0,21,0,17,0,0],"FLAG":0,"BASE":18}
-eMylo SS-8839-03 {"NAME":"SS-8839-03","GPIO":[0,255,0,255,52,0,0,0,21,0,17,0,0],"FLAG":0,"BASE":18}
ESP-01 Relay V4.0 {"NAME":"ESP01v4","GPIO":[29,52,0,0,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":18}
ESP-01S 5V Relay Module V1.0 {"NAME":"ESP-01S Relay","GPIO":[29,52,255,255,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":18}
ESP12F 220V 10A 7V-30V DC {"NAME":"Yunshan 10A","GPIO":[17,255,52,255,21,10,0,0,22,0,0,0,0],"FLAG":0,"BASE":18}
-EX Store 2 Kanal V5 {"NAME":"EXS Relay V5","GPIO":[255,255,255,255,255,255,0,0,21,22,31,52,32],"FLAG":0,"BASE":16}
-Garage Door Controller {"NAME":"Garage Opener","GPIO":[0,107,0,108,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":54}
-Geekcreit 2 Channel AC 85V-250V {"NAME":"Geekcreit 2ch","GPIO":[17,0,0,0,0,22,18,0,21,52,0,0,0],"FLAG":1,"BASE":18}
Geekcreit 5V DIY 4 Channel Jog Inching Self-Locking {"NAME":"Geekcreit-4ch","GPIO":[9,0,0,0,23,22,10,11,21,52,12,24,0],"FLAG":0,"BASE":18}
Geekcreit Module 220V 10A {"NAME":"DIY ESP8266 Re","GPIO":[0,0,157,0,21,17,0,0,0,0,0,0,0],"FLAG":0,"BASE":18}
-Gocomma Wi-Fi Smart Switch {"NAME":"GoCommaSmartSw","GPIO":[17,255,255,255,21,0,0,0,255,56,0,0,0],"FLAG":0,"BASE":18}
-Hoch Circuit Breaker 1P {"NAME":"HOCH ZJSB9","GPIO":[17,255,255,255,255,255,0,0,21,56,0,0,0],"FLAG":0,"BASE":18}
HW-622 ESP8266 {"NAME":"HW-622","GPIO":[0,0,157,0,21,17,0,0,0,0,0,0,0],"FLAG":0,"BASE":18}
-L-5A01 {"NAME":"L-5A01","GPIO":[17,255,0,255,0,0,0,0,21,56,0,0,0],"FLAG":0,"BASE":1}
+LC Technology 12V 4 Channel {"NAME":"LC Technology 4CH Relay","GPIO":[21,0,22,0,23,24,0,0,0,0,0,0,0],"FLAG":0,"BASE":18}
LC Technology 5V 2 Channel {"NAME":"LC-ESP01-2R-5V","GPIO":[0,148,0,149,0,0,0,0,21,22,0,0,0],"FLAG":0,"BASE":18}
LC Technology 5V 4 Channel {"NAME":"LC-Tech_4CH ","GPIO":[52,255,17,255,0,0,0,0,21,22,23,24,0],"FLAG":0,"BASE":18}
LC Technology AC/DC 1 Channel ESP-12F Dev Board {"NAME":"LC-ESP12-1R-MV","GPIO":[255,255,157,255,255,21,255,255,255,255,255,255,57],"FLAG":15,"BASE":18}
LC Technology ESP8266 5V {"NAME":"ESP8266-01S","GPIO":[21,148,0,149,0,0,0,0,0,0,0,0,0],"FLAG":1,"BASE":18}
LinkNode R4 {"NAME":"LinkNode R4","GPIO":[0,0,0,0,0,0,0,0,21,22,23,0,24],"FLAG":0,"BASE":18}
LinkNode R8 {"NAME":"LinkNode R8","GPIO":[0,0,0,0,25,26,0,28,23,24,22,27,21],"FLAG":0,"BASE":18}
-LoraTap 10A {"NAME":"LoraTap RR400W","GPIO":[0,0,0,0,157,0,0,0,21,17,0,0,0],"FLAG":0,"BASE":18}
-LoraTap RR500W {"NAME":"LoraTap RR500W","GPIO":[157,255,255,255,9,255,255,255,255,21,255,255,56],"FLAG":15,"BASE":18}
Mhcozy 5V {"NAME":"Portail","GPIO":[9,0,0,0,0,0,0,0,21,56,0,0,0],"FLAG":1,"BASE":18}
-Moes MS-104B-1 {"NAME":"Moes MS-104B","GPIO":[0,0,17,0,160,0,0,0,43,42,21,22,0],"FLAG":0,"BASE":18}
-Nova Digital Basic 1 MS101 {"NAME":"NovaDigBasic1","GPIO":[0,255,0,255,56,0,0,0,21,17,0,0,0],"FLAG":0,"BASE":18}
-OpenEnergyMonitor WiFi MQTT Thermostat {"NAME":"MQTT-RELAY","GPIO":[17,0,255,0,0,21,0,0,0,0,0,0,56],"FLAG":0,"BASE":18}
-Protium PS-1604 {"NAME":"Protium16A","GPIO":[17,255,255,255,255,0,0,0,21,56,255,0,0],"FLAG":0,"BASE":1}
-QS-WIFI-S03 Module Switch {"NAME":"QS-WIFI-S03","GPIO":[17,255,255,255,255,0,0,0,82,21,0,0,0],"FLAG":0,"BASE":1}
-QS-WIFI-S05 {"NAME":"QS-WIFI-S05","GPIO":[17,255,255,255,255,0,0,0,82,21,0,0,0],"FLAG":0,"BASE":1}
-Shelly 1 {"NAME":"Shelly 1","GPIO":[0,0,0,0,21,82,0,0,0,0,0,0,0],"FLAG":0,"BASE":46}
-Shelly 1PM {"NAME":"Shelly 1PM","GPIO":[56,0,0,0,82,134,0,0,0,0,0,21,0],"FLAG":2,"BASE":18}
-Shelly 2 {"NAME":"Shelly 2","GPIO":[0,135,0,136,21,22,0,0,9,0,10,137,0],"FLAG":0,"BASE":47}
-Shelly 2.5 {"NAME":"Shelly 2.5","GPIO":[56,0,17,0,21,83,0,0,6,82,5,22,156],"FLAG":2,"BASE":18}
-Shelly EM {"NAME":"Shelly EM","GPIO":[0,0,0,0,0,0,0,0,6,156,5,21,0],"FLAG":15,"BASE":18}
-Sinilink XY-WF36V DC6V-36V Switch Module {"NAME":"Sinilink XY-WF5V","GPIO":[0,0,0,0,21,255,0,0,17,52,0,0,255],"FLAG":0,"BASE":18}
-Sinilink XY-WFMS MOS Switch Module {"NAME":"Sinilink MOS","GPIO":[0,0,0,0,21,255,0,0,17,52,0,0,255],"FLAG":0,"BASE":18}
-Sinilink XY-WFUSB USB Switch {"NAME":"XY-WFUSB","GPIO":[255,255,0,255,17,21,0,0,0,0,56,0,157],"FLAG":0,"BASE":18}
-Smart Home SS-8839-01 {"NAME":"SS-8839-01","GPIO":[0,255,0,255,21,0,0,0,17,57,0,56,0],"FLAG":0,"BASE":18}
-Sonoff 4CH (R2) {"NAME":"Sonoff 4CH","GPIO":[17,255,255,255,23,22,18,19,21,56,20,24,0],"FLAG":0,"BASE":7}
-Sonoff 4CH Pro (R2) {"NAME":"Sonoff 4CH Pro","GPIO":[17,255,255,255,23,22,18,19,21,56,20,24,0],"FLAG":0,"BASE":23}
-Sonoff 4CHPROR3 {"NAME":"Sonoff 4CHPROR3","GPIO":[17,255,255,255,23,22,18,19,21,56,20,24,0],"FLAG":0,"BASE":23}
-Sonoff 4CHR3 {"NAME":"Sonoff 4CHR3","GPIO":[17,255,255,255,23,22,18,19,21,56,20,24,0],"FLAG":0,"BASE":7}
-Sonoff 5V Inching/Selflock Module RE5V1C {"NAME":"Sonoff RE5V1C","GPIO":[17,255,255,255,255,255,0,0,21,56,0,0,0],"FLAG":0,"BASE":18}
-Sonoff Basic {"NAME":"Sonoff Basic","GPIO":[17,255,255,255,255,0,0,0,21,56,255,0,0],"FLAG":0,"BASE":1}
-Sonoff Basic R3 {"NAME":"Basic R3","GPIO":[17,255,0,255,255,0,0,0,21,56,255,0,255],"FLAG":0,"BASE":1}
-Sonoff Dual {"NAME":"Sonoff Dual","GPIO":[0,148,0,149,255,0,0,0,0,56,255,0,0],"FLAG":0,"BASE":5}
-Sonoff Dual R2 {"NAME":"Sonoff Dual R2","GPIO":[255,255,0,255,0,22,255,17,21,56,0,0,0],"FLAG":0,"BASE":39}
-Sonoff Mini {"NAME":"Sonoff Mini","GPIO":[17,0,0,0,9,0,0,0,21,56,0,0,255],"FLAG":0,"BASE":1}
-Sonoff Pow {"NAME":"Sonoff Pow","GPIO":[17,0,0,0,0,130,0,0,21,132,133,52,0],"FLAG":0,"BASE":6}
-Sonoff Pow R2 {"NAME":"Sonoff Pow R2","GPIO":[17,145,0,146,0,0,0,0,21,56,0,0,0],"FLAG":0,"BASE":43}
-Sonoff RF {"NAME":"Sonoff RF","GPIO":[17,255,255,255,255,0,0,0,21,56,255,0,0],"FLAG":0,"BASE":2}
+Sinilink DC6V-36V Module {"NAME":"Sinilink XY-WF5V","GPIO":[0,0,0,0,21,255,0,0,17,52,0,0,255],"FLAG":0,"BASE":18}
+Sinilink MOS {"NAME":"Sinilink MOS","GPIO":[0,0,0,0,21,255,0,0,17,52,0,0,255],"FLAG":0,"BASE":18}
+Sonoff 1 Channel Inching/Self-Locking {"NAME":"1 Channel","GPIO":[17,0,0,0,0,0,0,0,21,56,0,0,0],"FLAG":0,"BASE":12}
+Sonoff RE5V1C 5V Inching/Selflock {"NAME":"Sonoff RE5V1C","GPIO":[17,255,255,255,255,255,0,0,21,56,0,0,0],"FLAG":0,"BASE":18}
Sonoff SV {"NAME":"Sonoff SV","GPIO":[17,255,0,255,255,255,0,0,21,56,255,0,0],"FLAG":1,"BASE":3}
-Sonoff TH10/TH16 {"NAME":"Sonoff TH","GPIO":[17,255,0,255,255,0,0,0,21,56,255,0,0],"FLAG":0,"BASE":4}
-SS-8839-02 {"NAME":"SS-8839-02","GPIO":[0,255,0,255,56,0,0,0,21,0,17,0,0],"FLAG":0,"BASE":18}
-SS311KWS RF Kinetic Switch and WiFi {"NAME":"SS311KWS","GPIO":[0,0,0,0,52,0,0,0,21,17,0,0,0],"FLAG":0,"BASE":18}
-SW-R03 {"NAME":"SW-R03","GPIO":[0,0,0,0,0,0,0,0,21,17,0,0,0],"FLAG":0,"BASE":18}
-WL-SW01_10 {"NAME":"WL-SW01_10","GPIO":[17,149,0,148,0,0,0,0,21,56,0,0,0],"FLAG":0,"BASE":1}
-Yuntong Smart {"NAME":"Yuntong Smart ","GPIO":[0,0,0,0,21,0,0,0,122,56,0,0,0],"FLAG":0,"BASE":18}
-Zemismart ERC309 Kinetic Switch {"NAME":"Kinetic Switch","GPIO":[255,255,255,255,255,255,0,0,255,108,255,107,255],"FLAG":0,"BASE":54}
-ZMAi-90 Digital Energy Meter {"NAME":"ZMAi-90","GPIO":[0,148,0,149,0,0,0,0,21,90,0,0,0],"FLAG":0,"BASE":18}
```
-## Sensor
+## Smoke Sensor
```
-Digoo DG-ZXD21 Door Detector {"NAME":"Digoo ZXD21","GPIO":[0,107,0,108,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":54}
-DP-WP001 PIR {"NAME":"TUYA PIR","GPIO":[255,107,255,108,255,255,0,0,255,255,255,255,255],"FLAG":0,"BASE":54}
-DS18B20 ESP01 Module Temperature {"NAME":"ESP01S ds18b20","GPIO":[255,255,4,255,255,255,0,0,255,255,255,255,255],"FLAG":15,"BASE":18}
-Earykong TYMC-1 Door Window {"NAME":"TYMC-1","GPIO":[0,107,0,108,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":54}
-ESP01S DHT11 v1.0 Module Temperature {"NAME":"ESP01S DHT11","GPIO":[0,0,1,0,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":4}
-IOT4SH01DS Temperature {"NAME":"IOT4SH01DS","GPIO":[255,255,255,255,255,255,0,0,255,4,255,255,255],"FLAG":15,"BASE":18}
-Lenovo Rechargable PIR Motion {"NAME":"Lenovo PIR","GPIO":[255,107,255,108,255,255,0,0,255,255,255,255,255],"FLAG":0,"BASE":54}
-Mirabella Genio I002576 Motion {"NAME":"GenioPir","GPIO":[17,107,0,108,0,0,0,0,0,56,0,0,0],"FLAG":0,"BASE":54}
-Natural Gas (CH4) Alarm {"NAME":"PA-210WYS","GPIO":[255,107,255,108,255,255,0,0,255,255,255,255,255],"FLAG":0,"BASE":54}
Nedis Smoke Detector {"NAME":"Nedis Smoke","GPIO":[0,107,0,108,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":54}
-Shelly i3 Action and Scenes Activation Device {"NAME":"Shelly i3","GPIO":[0,0,0,0,0,0,0,0,83,84,82,0,0],"FLAG":0,"BASE":18}
-Shelly Temperature Sensor Add-on {"NAME":"Shelly 1 Temp ","GPIO":[192,0,0,4,21,82,0,0,0,0,0,0,0],"FLAG":0,"BASE":46}
Smoke Alarm {"NAME":"YG400A","GPIO":[255,107,255,108,255,255,0,0,255,255,255,255,255],"FLAG":0,"BASE":54}
-Sonoff SC {"NAME":"Sonoff SC","GPIO":[17,148,255,149,0,0,0,0,0,56,0,0,0],"FLAG":0,"BASE":21}
-TY01 Door Window {"NAME":"TY01","GPIO":[0,107,0,108,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":54}
-Zemismart Door Window {"NAME":"Zemismart","GPIO":[255,107,255,108,255,255,0,0,255,255,255,255,255],"FLAG":0,"BASE":54}
```
## Switch
```
-3A Smart Home HGZB-043 {"NAME":"3A Smart Home ","GPIO":[52,0,55,18,22,19,0,0,17,21,54,23,53],"FLAG":0,"BASE":18}
-AGL 3 Gang {"NAME":"AGL WiFi 03","GPIO":[0,0,56,0,19,18,0,0,22,21,23,0,17],"FLAG":0,"BASE":18}
+3A Smart Home {"NAME":"3A Smart Home ","GPIO":[52,0,55,18,22,19,0,0,17,21,54,23,53],"FLAG":0,"BASE":18}
+AGL 2 Gang {"NAME":"AGL WiFi 02","GPIO":[0,0,157,0,0,18,0,0,22,21,0,0,17],"FLAG":0,"BASE":18}
+AGL 3 Gang {"NAME":"AGL WiFi 03","GPIO":[0,0,157,0,19,18,0,0,22,21,23,0,17],"FLAG":0,"BASE":18}
+AGL Modulo Relay 01 Canal {"NAME":"AGL-Basic","GPIO":[0,255,0,0,21,17,0,0,0,0,56,0,0],"FLAG":0,"BASE":18}
+Albohes 2 Channel {"NAME":"Albohes SH-08","GPIO":[0,148,18,149,57,56,0,0,21,157,17,0,22],"FLAG":15,"BASE":18}
Aoycocr SW1 {"NAME":"Aoycocr SW1","GPIO":[158,255,57,255,255,255,255,255,56,17,255,21,255],"FLAG":15,"BASE":18}
+ATMS1601 230VAC DIN Timer/Switch {"NAME":"ATMS1601","GPIO":[255,255,255,255,157,56,255,255,21,17,255,255,255],"FLAG":15,"BASE":18}
Avatto 2 Gang {"NAME":"Avatto Wifi - ","GPIO":[0,0,52,0,0,17,0,0,21,22,0,0,18],"FLAG":0,"BASE":18}
+Avatto 3-Gang {"NAME":"AVATTO 3 Gang","GPIO":[0,57,158,19,23,18,0,0,56,21,58,22,17],"FLAG":0,"BASE":18}
Avatto Fan Light {"NAME":"AVATTO Smart Wifi Fan Light","GPIO":[0,107,0,108,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":54}
Bardi Smart Wallswitch 1 {"NAME":"Bardi 1 Gang","GPIO":[57,56,157,0,0,17,0,0,0,0,0,21,0],"FLAG":0,"BASE":18}
Bardi Smart Wallswitch 2 {"NAME":"BARDI 2 Gang","GPIO":[56,0,157,18,22,0,0,0,52,21,57,0,17],"FLAG":0,"BASE":18}
Bardi Smart Wallswitch 3 {"NAME":"BARDI 3 Gang","GPIO":[56,57,157,19,23,18,0,0,52,21,58,22,17],"FLAG":0,"BASE":18}
BAZZ SWTCHWFW1 {"NAME":"BAZZ KS-602S","GPIO":[17,0,0,0,0,0,21,52,29,56,0,0,0],"FLAG":0,"BASE":18}
-Blitzwolf BW-SS3 1 Gang {"NAME":"BW-SS3-1G-EU","GPIO":[52,0,0,17,0,0,0,0,0,21,0,0,0],"FLAG":0,"BASE":18}
+BlitzWolf BW-SS1 {"NAME":"BW-SS1","GPIO":[255,255,255,255,157,21,0,0,255,17,255,255,0],"FLAG":0,"BASE":18}
+BlitzWolf BW-SS3 1 Gang {"NAME":"BW-SS3-1G-EU","GPIO":[52,0,0,17,0,0,0,0,0,21,0,0,0],"FLAG":0,"BASE":18}
BlitzWolf BW-SS3 2 Gang {"NAME":"BW-SS3-2G-EU","GPIO":[157,255,255,255,22,18,255,255,17,21,255,255,255],"FLAG":15,"BASE":18}
BlitzWolf BW-SS3 3 Gang {"NAME":"BlitzWolf SS3","GPIO":[158,0,0,10,22,11,0,0,9,21,0,23,0],"FLAG":0,"BASE":18}
+BlitzWolf BW-SS5 1 Gang {"NAME":"BlitzWolf SS5 1 Gang","GPIO":[0,0,0,0,0,0,0,0,9,21,0,0,0],"FLAG":0,"BASE":18}
+BlitzWolf BW-SS5 2 Gang {"NAME":"BlitzWolf SS5 2 Gang","GPIO":[0,0,17,0,160,0,0,0,10,9,21,22,0],"FLAG":0,"BASE":18}
+BlitzWolf SS4 {"NAME":"BlitzWolf SS4 Two Gang","GPIO":[0,0,0,0,56,21,0,0,22,17,0,0,0],"FLAG":0,"BASE":18}
+Canwing CW-001 {"NAME":"Canwing CW-001","GPIO":[17,255,0,255,0,0,0,0,21,56,0,0,0],"FLAG":0,"BASE":1}
CD303 3 Gang Touch {"NAME":"Touch Switch 3","GPIO":[54,57,255,19,23,18,255,255,17,21,255,22,52],"FLAG":15,"BASE":18}
-Connect Smart 2 Gang Wall {"NAME":"CSH-SWTCH2","GPIO":[0,0,52,0,0,18,0,0,22,21,0,0,17],"FLAG":0,"BASE":18}
+Century Aoke Smart Switch {"NAME":"CenturyAoke","GPIO":[0,255,0,255,21,0,0,0,17,56,255,0,0],"FLAG":0,"BASE":18}
+Connect SmartHome 2 Gang Wall {"NAME":"CSH-SWTCH2","GPIO":[0,0,52,0,0,18,0,0,22,21,0,0,17],"FLAG":0,"BASE":18}
Deta 1 Gang {"NAME":"Deta 1G Switch","GPIO":[0,0,0,0,157,0,0,0,0,21,0,0,90],"FLAG":0,"BASE":18}
Deta 2 Gang {"NAME":"DETA 2G Switch","GPIO":[0,0,0,0,157,0,0,0,91,21,22,0,90],"FLAG":0,"BASE":18}
Deta 3 Gang {"NAME":"DETA 3G Switch","GPIO":[157,0,0,92,91,21,0,0,23,0,22,0,90],"FLAG":0,"BASE":18}
Deta 4 Gang {"NAME":"Deta 4G Switch","GPIO":[157,0,0,19,18,21,0,0,23,20,22,24,17],"FLAG":0,"BASE":18}
+Deta 6000HA Smart Inline Switch {"NAME":"DETA-6000HA","GPIO":[0,17,0,0,0,0,0,0,0,56,21,0,0],"FLAG":0,"BASE":18}
+Deta Fan Speed Controller with Light {"NAME":"Deta Fan Speed and Light Controller","GPIO":[18,0,0,157,23,19,0,0,0,22,21,24,17],"FLAG":0,"BASE":18}
+dewenwils Outdoor Timer Box {"NAME":"Dewenwils50054","GPIO":[0,0,54,0,0,0,0,0,0,17,0,21,0],"FLAG":0,"BASE":18}
Digoo DG-S811 3 Gang {"NAME":"DIGOO Switch","GPIO":[0,0,0,0,19,18,0,0,22,21,23,0,17],"FLAG":0,"BASE":18}
DS-102 1 Gang {"NAME":"DS-102 1 Gang","GPIO":[57,0,0,17,0,0,0,0,0,21,56,0,0],"FLAG":1,"BASE":18}
DS-102 2 Gang {"NAME":"DS-102 2 Gang","GPIO":[158,57,0,17,22,18,0,0,0,21,56,255,0],"FLAG":0,"BASE":18}
DS-102 3 Gang {"NAME":"DS-102 3 Gang","GPIO":[158,58,0,18,22,19,0,0,17,21,57,23,56],"FLAG":0,"BASE":18}
Eachen CD303 3 Gang {"NAME":"ID Components","GPIO":[157,53,0,11,21,10,0,0,9,22,54,23,52],"FLAG":15,"BASE":18}
Eachen SWT-2Gang {"NAME":"ID Components","GPIO":[157,255,255,255,255,10,255,255,9,21,53,22,52],"FLAG":15,"BASE":18}
+eMylo 2 Channel {"NAME":"eMylo XL9252WI","GPIO":[0,255,0,0,56,22,0,0,21,0,12,0,0],"FLAG":0,"BASE":18}
+eMylo SS-8839-02 {"NAME":"SS-8839-02","GPIO":[0,255,0,255,56,0,0,0,21,0,17,0,0],"FLAG":0,"BASE":18}
+eMylo SS-8839-03 {"NAME":"SS-8839-03","GPIO":[0,255,0,255,52,0,0,0,21,0,17,0,0],"FLAG":0,"BASE":18}
Enjowi WF-SK301 {"NAME":"Tuya 3 Channel","GPIO":[0,0,0,0,23,18,0,0,17,21,19,22,157],"FLAG":0,"BASE":18}
Esmlfe 3 Gang {"NAME":"Esmlfe DS-122","GPIO":[57,0,0,17,0,0,0,0,0,21,52,0,0],"FLAG":0,"BASE":18}
Etekcity ESWL01 {"NAME":"EtekCityESWL01","GPIO":[0,255,0,255,52,53,0,0,0,21,122,0,0],"FLAG":1,"BASE":18}
Etekcity ESWL03 3-way {"NAME":"Etekcity 3Way","GPIO":[0,0,0,0,23,29,0,0,82,22,10,0,0],"FLAG":0,"BASE":18}
-Eva Logik WF30 3-Way {"NAME":"WF30 Switch","GPIO":[0,0,0,0,18,0,0,0,21,17,0,0,0],"FLAG":0,"BASE":18}
+Eva Logik 3-Way {"NAME":"WF30 Switch","GPIO":[0,0,0,0,18,0,0,0,21,17,0,0,0],"FLAG":0,"BASE":18}
+EX Store 2 Kanal V5 {"NAME":"EXS Relay V5","GPIO":[255,255,255,255,255,255,0,0,21,22,31,52,32],"FLAG":0,"BASE":16}
FrankEver 4 Gang {"NAME":"FrankEver Wifi Smart Switch","GPIO":[0,0,0,17,21,18,255,255,19,23,24,22,20],"FLAG":0,"BASE":18}
Freecube AWS01F {"NAME":"Freecube","GPIO":[0,0,0,17,21,0,0,0,0,0,22,0,0],"FLAG":0,"BASE":18}
+Garage Door Controller {"NAME":"Garage Opener","GPIO":[0,107,0,108,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":54}
+Geekcreit 2 Channel AC 85V-250V {"NAME":"Geekcreit 2ch","GPIO":[17,0,0,0,0,22,18,0,21,52,0,0,0],"FLAG":1,"BASE":18}
Geeni TAP 3-Way {"NAME":"Geeni 3-Way","GPIO":[157,0,0,0,0,0,0,0,17,21,0,0,0],"FLAG":0,"BASE":18}
Girier EK01 RF433Mhz 1 Gang {"NAME":"Girier EK01","GPIO":[157,0,0,0,21,0,0,0,0,0,0,0,17],"FLAG":0,"BASE":18}
Girier EK02 RF433Mhz 2 Gang {"NAME":"Girier EK02","GPIO":[157,0,0,0,0,17,0,0,18,21,22,0,0],"FLAG":0,"BASE":18}
Girier EK03 RF433Mhz 3 Gang {"NAME":"EK03","GPIO":[157,0,0,0,22,17,0,0,19,21,23,0,18],"FLAG":0,"BASE":18}
-Girier JRSWR-SEU01 1 Gang {"NAME":"W601","GPIO":[0,0,0,0,0,17,0,0,0,0,0,21,157],"FLAG":15,"BASE":18}
-Girier JRSWR-SEU01 2 Gang {"NAME":"W602","GPIO":[0,0,0,0,22,0,0,0,17,21,18,0,157],"FLAG":15,"BASE":18}
-Girier JRSWR-SEU01 3 Gang {"NAME":"W603","GPIO":[0,0,0,0,23,18,0,0,17,21,19,22,157],"FLAG":15,"BASE":18}
Girier JRSWR-US01 No Neutral 1 Gang {"NAME":"Tuya 1 Channel","GPIO":[0,0,0,0,0,17,0,0,0,0,0,21,157],"FLAG":0,"BASE":18}
Girier JRSWR-US01 No Neutral 3 Gang {"NAME":"Girier JRSWR-U","GPIO":[0,0,0,0,21,18,0,0,19,23,17,22,157],"FLAG":0,"BASE":18}
+Girier RF433 1 Gang No Neutral {"NAME":"Girier","GPIO":[0,157,0,0,0,17,0,0,21,0,0,0,0],"FLAG":0,"BASE":18}
+Girier RF433 2 Gang No Neutral {"NAME":"W602","GPIO":[0,0,0,0,22,0,0,0,17,21,18,0,157],"FLAG":0,"BASE":18}
+Girier RF433 3 Gang No Neutral {"NAME":"W603","GPIO":[0,0,0,0,23,18,0,0,17,21,19,22,157],"FLAG":15,"BASE":18}
+Gocomma Wi-Fi Smart Switch {"NAME":"GoCommaSmartSw","GPIO":[17,255,255,255,21,0,0,0,255,56,0,0,0],"FLAG":0,"BASE":18}
GoKlug Glass Touch 1 Gang {"NAME":"GoKlug 1x","GPIO":[56,57,0,0,0,9,0,0,0,0,0,21,0],"FLAG":0,"BASE":18}
-Gosund KS-602S {"NAME":"Gosund KS-602S","GPIO":[17,0,56,0,0,0,0,0,0,0,21,0,158],"FLAG":0,"BASE":18}
+Gosund KS-602S {"NAME":"Gosund KS-602S","GPIO":[17,0,0,0,0,0,0,0,21,157,0,0,0],"FLAG":0,"BASE":18}
Gosund SW1 {"NAME":"Gosund SW1","GPIO":[17,0,57,0,0,0,0,0,0,0,21,0,56],"FLAG":0,"BASE":18}
Gosund SW6 3-Way {"NAME":"Gosund SW6","GPIO":[17,0,56,0,9,0,0,0,0,0,22,21,158],"FLAG":0,"BASE":18}
Hama Flush-mounted {"NAME":"Hama WiFiTouch","GPIO":[157,0,0,0,0,18,0,0,17,22,0,21,0],"FLAG":0,"BASE":45}
HBN Wall-Mounted Timer {"NAME":"HBN Timer Switch","GPIO":[0,0,0,0,54,57,0,0,21,17,0,0,0],"FLAG":0,"BASE":18}
+Hoch Circuit Breaker 1P {"NAME":"HOCH ZJSB9","GPIO":[17,255,255,255,255,255,0,0,21,56,0,0,0],"FLAG":0,"BASE":18}
+HomeMate 4 Node In-wall Smart Switch {"NAME":"HomeMate Wifi 4N ","GPIO":[255,255,255,9,21,12,255,255,10,22,23,24,11],"FLAG":15,"BASE":18}
Innens 1 Gang 1 Way {"NAME":"Innens 1 Gang 1 Way","GPIO":[0,0,0,17,21,0,0,0,0,0,52,0,0],"FLAG":0,"BASE":18}
iSwitch Light & Fan {"NAME":"iSwitchOZ Light Fan","GPIO":[0,107,0,108,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":54}
Jinvoo SM-SW101-1 {"NAME":"SM-SW101-1","GPIO":[52,0,0,18,0,0,0,0,17,21,0,0,0],"FLAG":1,"BASE":18}
@@ -1417,6 +1472,7 @@ KTNN-KG-T100 2 Gang Switch {"NAME":"Sonoff T1 2CH","GPIO":[17,255,255,255,0,22,
Kuled K36 {"NAME":"KULED-B","GPIO":[9,255,255,255,255,255,21,52,29,56,255,255,255],"FLAG":0,"BASE":18}
Kuled KS602S {"NAME":"KULED","GPIO":[17,255,255,255,255,255,0,0,21,56,255,255,255],"FLAG":0,"BASE":18}
Kygne CD-301 {"NAME":"KYGNE Touch","GPIO":[0,0,0,0,52,53,0,0,21,17,0,0,0],"FLAG":0,"BASE":10}
+L-5A01 {"NAME":"L-5A01","GPIO":[17,255,0,255,0,0,0,0,21,56,0,0,0],"FLAG":0,"BASE":1}
Laghten SS02S {"NAME":"Laghten SS02S","GPIO":[0,0,0,0,52,57,0,0,21,17,0,0,0],"FLAG":0,"BASE":18}
LerLink X801A-L No Neutral {"NAME":"LerLink X801-L","GPIO":[0,0,0,0,17,0,0,0,21,56,0,0,0],"FLAG":15,"BASE":18}
Lerlink X802A 2 Gang {"NAME":"Lerlink X802A","GPIO":[0,0,0,18,17,0,0,0,21,23,22,0,0],"FLAG":15,"BASE":18}
@@ -1426,7 +1482,10 @@ Lonsonho 3 Gang {"NAME":"Lonsonho X803A","GPIO":[0,0,0,18,17,19,0,0,21,
Lonsonho SK3-01 {"NAME":"Tuya 1 Channel","GPIO":[0,0,0,0,0,17,0,0,0,0,0,21,52],"FLAG":0,"BASE":18}
Lonsonho SK3-02 {"NAME":"Tuya 2 Channel","GPIO":[0,0,0,0,22,0,0,0,17,21,18,0,52],"FLAG":0,"BASE":18}
Lonsonho SK3-03 {"NAME":"Tuya 3-ch v2","GPIO":[157,58,0,18,22,19,0,0,17,21,57,23,56],"FLAG":0,"BASE":18}
+LoraTap 10A {"NAME":"LoraTap RR400W","GPIO":[0,0,0,0,157,0,0,0,21,17,0,0,0],"FLAG":0,"BASE":18}
+LoraTap RR500W {"NAME":"LoraTap RR500W","GPIO":[157,255,255,255,9,255,255,255,255,21,255,255,56],"FLAG":15,"BASE":18}
LoraTap WH100W-US 20A {"NAME":"LoraTap Boiler","GPIO":[0,0,0,0,0,0,0,0,17,21,0,0,56],"FLAG":0,"BASE":18}
+Luani HVIO {"NAME":"Luani HVIO","GPIO":[0,255,255,255,21,22,0,0,9,10,255,52,0],"FLAG":1,"BASE":35}
Luminea LHC-101.on {"NAME":"LHC-101.on","GPIO":[157,0,0,17,21,0,0,0,0,0,52,0,0],"FLAG":0,"BASE":18}
Luminea LHC-102.on {"NAME":"LHC-102.on","GPIO":[157,0,53,0,0,18,0,0,17,21,0,22,52],"FLAG":0,"BASE":18}
LX-WIFI-00M 4 Gang {"NAME":"LX-WIFI-00M","GPIO":[17,25,255,255,23,22,18,19,21,0,20,24,0],"FLAG":0,"BASE":7}
@@ -1442,9 +1501,11 @@ Minitiger 1 Gang v2 {"NAME":"MiniTiger1Band","GPIO":[0,56,0,0,0,17,0,0,21,0
Minitiger 2 Gang {"NAME":"minitiger 2 Gang","GPIO":[17,255,255,255,0,22,18,0,21,56,0,0,0],"FLAG":0,"BASE":28}
Minitiger 2 Gang v2 {"NAME":"Minitiger2Band","GPIO":[0,0,0,17,18,0,0,0,0,21,22,0,0],"FLAG":0,"BASE":18}
Minitiger 3 Gang {"NAME":"Minitiger3gang","GPIO":[0,0,0,9,11,10,255,255,22,21,23,0,0],"FLAG":0,"BASE":18}
+Minitiger 4 Gang {"NAME":"Minitiger 4 Gang","GPIO":[17,0,0,0,23,22,18,19,21,158,20,24,0],"FLAG":0,"BASE":18}
Moes 2 Gang {"NAME":"Moes WS-EU2-W","GPIO":[157,0,53,0,0,18,0,0,17,21,0,22,52],"FLAG":15,"BASE":18}
Moes 3 Gang {"NAME":"Moes WS-EU3-W","GPIO":[157,0,54,18,22,19,0,0,17,21,53,23,52],"FLAG":15,"BASE":18}
Moes BS-US-W Boiler {"NAME":"BS-US-W","GPIO":[54,0,0,17,21,0,0,0,0,0,52,0,55],"FLAG":0,"BASE":18}
+Moes MS-104B-1 {"NAME":"Moes MS-104B","GPIO":[0,0,17,0,160,0,0,0,10,9,21,22,0],"FLAG":0,"BASE":18}
Moes RF433 2 Gang Switch {"NAME":"WS-EUB2-WR","GPIO":[0,107,0,108,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":54}
Moes SS01-1 3-Way {"NAME":"Moes 3-Way","GPIO":[255,255,255,255,21,57,0,0,30,10,9,255,255],"FLAG":0,"BASE":18}
Moes SS01S-1 {"NAME":"Moes Switch","GPIO":[255,255,255,255,56,0,0,0,21,17,255,255,255],"FLAG":0,"BASE":18}
@@ -1466,6 +1527,11 @@ NaamaSmart KS602 {"NAME":"KS-602","GPIO":[17,0,0,0,0,0,0,0,21,158,0,0,0]
Nedis Dual {"NAME":"SM-SW102U-2","GPIO":[158,0,0,18,22,0,0,0,17,21,0,0,0],"FLAG":1,"BASE":18}
Nexete DS-123 {"NAME":"DS-123","GPIO":[157,57,255,17,21,18,0,0,255,22,56,255,255],"FLAG":0,"BASE":18}
Nexete DS-123 Single {"NAME":"DS-123","GPIO":[157,0,255,18,0,17,0,0,255,21,56,255,255],"FLAG":0,"BASE":18}
+Nova Digital Basic 1 MS101 {"NAME":"NovaDigBasic1","GPIO":[0,255,0,255,56,0,0,0,21,17,0,0,0],"FLAG":0,"BASE":18}
+OpenEnergyMonitor WiFi MQTT Thermostat {"NAME":"MQTT-RELAY","GPIO":[17,0,255,0,0,21,0,0,0,0,0,0,56],"FLAG":0,"BASE":18}
+PS-1604 16A {"NAME":"PS-1604 16A","GPIO":[17,255,255,255,255,0,0,0,21,56,255,0,0],"FLAG":0,"BASE":1}
+QS-WIFI-S03 Module {"NAME":"QS-WIFI-S03","GPIO":[17,255,255,255,255,0,0,0,82,21,0,0,0],"FLAG":0,"BASE":1}
+QS-WIFI-S05 {"NAME":"QS-WIFI-S05","GPIO":[17,255,255,255,255,0,0,0,82,21,0,0,0],"FLAG":0,"BASE":1}
Qualitel 1 Gang {"NAME":"Qualitel 1 Gang","GPIO":[157,0,0,9,21,0,0,0,0,0,52,0,0],"FLAG":0,"BASE":18}
Qualitel 2-Gang {"NAME":"Qualitel 2 Gang","GPIO":[157,0,53,0,0,10,0,0,9,21,0,22,52],"FLAG":0,"BASE":18}
Qualitel 3 Gang {"NAME":"Qualitel 3 Gang","GPIO":[157,0,54,10,22,11,0,0,9,21,53,23,52],"FLAG":0,"BASE":18}
@@ -1480,11 +1546,31 @@ Semicom LM-HP/GEVD-W {"NAME":"WaterHeater","GPIO":[158,56,0,0,0,17,0,0,0,0,0
Sesoo WIFI-EU-SK3-01 {"NAME":"Sensoo SK3-01","GPIO":[255,255,57,255,255,17,0,0,255,255,255,21,52],"FLAG":0,"BASE":18}
Sesoo WIFI-EU-SK3-02 {"NAME":"Sesoo SK3-02","GPIO":[0,0,57,0,22,0,0,0,17,21,18,0,52],"FLAG":0,"BASE":18}
Sesoo WIFI-US-SK3-04 {"NAME":"Tuya 4 Channel","GPIO":[52,255,255,19,23,17,0,0,20,24,22,21,18],"FLAG":0,"BASE":18}
+Shelly 1 {"NAME":"Shelly 1","GPIO":[0,0,0,0,21,82,0,0,0,0,0,0,0],"FLAG":0,"BASE":46}
+Shelly 1PM {"NAME":"Shelly 1PM","GPIO":[56,0,0,0,82,134,0,0,0,0,0,21,0],"FLAG":2,"BASE":18}
+Shelly 2 {"NAME":"Shelly 2","GPIO":[0,135,0,136,21,22,0,0,9,0,10,137,0],"FLAG":0,"BASE":47}
+Shelly 2.5 {"NAME":"Shelly 2.5","GPIO":[56,0,17,0,21,83,0,0,6,82,5,22,156],"FLAG":2,"BASE":18}
+Shelly EM {"NAME":"Shelly EM","GPIO":[0,0,0,0,0,0,0,0,6,156,5,21,0],"FLAG":15,"BASE":18}
+Shelly i3 Action and Scenes Activation Device {"NAME":"Shelly i3","GPIO":[0,0,0,0,0,0,0,0,83,84,82,0,0],"FLAG":2,"BASE":18}
+Sinilink USB {"NAME":"XY-WFUSB","GPIO":[255,255,0,255,17,21,0,0,0,0,56,0,157],"FLAG":0,"BASE":18}
SK-A801-01-US 1 Gang {"NAME":"jsankou US Switch 1 Gang","GPIO":[157,0,0,0,0,0,0,0,17,29,0,0,0],"FLAG":0,"BASE":18}
SK-W803-01-US 3 Gang {"NAME":"jsankou US Switch 3 Gang","GPIO":[157,0,0,18,30,19,0,0,17,29,0,31,0],"FLAG":0,"BASE":18}
+Smart Home SS-8839-01 {"NAME":"SS-8839-01","GPIO":[0,255,0,255,21,0,0,0,17,57,0,56,0],"FLAG":0,"BASE":18}
Smartlife Opard CD302 {"NAME":"CD302","GPIO":[0,0,0,0,52,57,0,0,29,17,0,0,0],"FLAG":0,"BASE":18}
SmartPlex 3 Gang {"NAME":"Tuya 3 Channel","GPIO":[255,255,255,255,21,18,0,0,19,23,17,22,255],"FLAG":0,"BASE":18}
+Sonoff 4CH (R2) {"NAME":"Sonoff 4CH","GPIO":[17,255,255,255,23,22,18,19,21,56,20,24,0],"FLAG":0,"BASE":7}
+Sonoff 4CH Pro (R2) {"NAME":"Sonoff 4CH Pro","GPIO":[17,255,255,255,23,22,18,19,21,56,20,24,0],"FLAG":0,"BASE":23}
+Sonoff 4CHPROR3 {"NAME":"Sonoff 4CHPROR3","GPIO":[17,255,255,255,23,22,18,19,21,56,20,24,0],"FLAG":0,"BASE":23}
+Sonoff 4CHR3 {"NAME":"Sonoff 4CHR3","GPIO":[17,255,255,255,23,22,18,19,21,56,20,24,0],"FLAG":0,"BASE":7}
+Sonoff Basic {"NAME":"Sonoff Basic","GPIO":[17,255,255,255,255,0,0,0,21,56,255,0,0],"FLAG":0,"BASE":1}
+Sonoff Basic R3 {"NAME":"Basic R3","GPIO":[17,255,0,255,255,0,255,255,21,56,0,0,255],"FLAG":0,"BASE":1}
+Sonoff Dual {"NAME":"Sonoff Dual","GPIO":[0,148,0,149,255,0,0,0,0,56,255,0,0],"FLAG":0,"BASE":5}
+Sonoff Dual R2 {"NAME":"Sonoff Dual R2","GPIO":[255,255,0,255,0,22,255,17,21,56,0,0,0],"FLAG":0,"BASE":39}
Sonoff IW101 {"NAME":"Sonoff IW101","GPIO":[17,145,0,146,0,0,0,0,21,157,0,0,0],"FLAG":0,"BASE":41}
+Sonoff Mini {"NAME":"Sonoff Mini","GPIO":[17,0,0,0,9,0,0,0,21,56,0,0,255],"FLAG":0,"BASE":1}
+Sonoff Pow {"NAME":"Sonoff Pow","GPIO":[17,0,0,0,0,130,0,0,21,132,133,52,0],"FLAG":0,"BASE":6}
+Sonoff Pow R2 {"NAME":"Sonoff Pow R2","GPIO":[17,145,0,146,0,0,0,0,21,56,0,0,0],"FLAG":0,"BASE":43}
+Sonoff RF {"NAME":"Sonoff RF","GPIO":[17,255,255,255,255,0,0,0,21,56,255,0,0],"FLAG":0,"BASE":2}
Sonoff T1 EU 1 Gang {"NAME":"Sonoff T1 1CH","GPIO":[17,255,255,255,0,0,0,0,21,56,0,0,0],"FLAG":0,"BASE":28}
Sonoff T1 EU 2 Gang {"NAME":"Sonoff T1 2CH","GPIO":[17,255,255,255,0,22,18,0,21,56,0,0,0],"FLAG":0,"BASE":29}
Sonoff T1 UK 1 Gang {"NAME":"Sonoff T1 1CH","GPIO":[17,255,255,255,0,0,0,0,21,56,0,0,0],"FLAG":0,"BASE":28}
@@ -1493,16 +1579,23 @@ Sonoff T1 UK 3 Gang {"NAME":"Sonoff T1 3CH","GPIO":[17,255,255,255,23,22,18
Sonoff T1 US 1 Gang {"NAME":"Sonoff T1 1CH","GPIO":[17,255,255,255,0,0,0,0,21,56,0,0,0],"FLAG":0,"BASE":28}
Sonoff T1 US 2 Gang {"NAME":"Sonoff T1 2CH","GPIO":[17,255,255,255,0,22,18,0,21,56,0,0,0],"FLAG":0,"BASE":29}
Sonoff T1 US 3 Gang {"NAME":"Sonoff T1 3CH","GPIO":[17,255,255,255,23,22,18,19,21,56,0,0,0],"FLAG":0,"BASE":30}
+Sonoff TH10/TH16 {"NAME":"Sonoff TH","GPIO":[17,255,0,255,255,0,0,0,21,56,255,0,0],"FLAG":0,"BASE":4}
Sonoff Touch EU {"NAME":"Sonoff Touch","GPIO":[17,255,0,255,0,0,0,0,21,56,0,0,0],"FLAG":0,"BASE":10}
Sonoff Touch US {"NAME":"Sonoff Touch","GPIO":[17,255,0,255,0,0,0,0,21,56,0,0,0],"FLAG":0,"BASE":10}
Sonoff TX T3 EU 3 Gang {"NAME":"TX T3EU3C","GPIO":[17,255,0,255,23,22,18,19,21,158,0,0,0],"FLAG":0,"BASE":30}
Sonoff TX T3 US 3 Gang {"NAME":"TX T3US3C","GPIO":[17,255,0,255,23,22,18,19,21,158,0,0,0],"FLAG":0,"BASE":30}
SPC Hera {"NAME":"SPC HERA","GPIO":[157,0,0,17,21,0,0,0,0,0,52,0,0],"FLAG":0,"BASE":18}
+Splatura USB Device Power Switch {"NAME":"Splatura USB","GPIO":[0,0,52,0,0,0,0,0,0,21,0,122,0],"FLAG":0,"BASE":18}
SRL 3-4WW 4 Gang {"NAME":"SRL 4WW Switch","GPIO":[0,0,0,19,23,18,0,0,17,21,24,22,20],"FLAG":0,"BASE":18}
+SS-8839-02 {"NAME":"SS-8839-02","GPIO":[0,255,0,255,56,0,0,0,21,0,17,0,0],"FLAG":0,"BASE":18}
SS118-01K1 {"NAME":"SS118-01K1","GPIO":[255,255,255,17,21,255,0,0,255,255,56,255,255],"FLAG":0,"BASE":18}
+SS311KWS RF Kinetic Switch and WiFi {"NAME":"SS311KWS","GPIO":[0,0,0,0,52,0,0,0,21,17,0,0,0],"FLAG":0,"BASE":18}
SS86-AI 3-Gang {"NAME":"SS86-AI 3 Gang","GPIO":[157,0,58,18,22,19,0,0,17,21,57,23,56],"FLAG":0,"BASE":18}
SSMS118-01A1 Scene Light Smart {"NAME":"RGB Switch","GPIO":[30,0,32,10,39,38,0,0,31,9,37,21,0],"FLAG":0,"BASE":18}
STITCH by Monoprice 35557 {"NAME":"Tuya WF15S ","GPIO":[255,255,0,0,255,255,0,0,255,108,255,107,0],"FLAG":0,"BASE":54}
+SUPLA inCan by Espablo {"NAME":"Supla Espablo","GPIO":[0,255,4,255,17,21,0,0,255,22,255,0,52],"FLAG":1,"BASE":31}
+SW-R03 {"NAME":"SW-R03","GPIO":[0,0,0,0,0,0,0,0,21,17,0,0,0],"FLAG":0,"BASE":18}
+TCP Smart 1 Gang {"NAME":"TCP 1 Gang 1 Way","GPIO":[157,0,0,17,21,0,0,0,0,0,56,0,0],"FLAG":0,"BASE":18}
Teckin SR-41 Single Pole {"NAME":"Teckin SR-41","GPIO":[17,0,0,0,0,0,0,0,21,158,0,0,0],"FLAG":0,"BASE":18}
Teckin SR43 {"NAME":"Teckin SR43","GPIO":[0,0,52,0,0,17,0,0,21,22,0,0,18],"FLAG":0,"BASE":18}
Teekar 10 Way 1 Gang {"NAME":"Teekar 10way","GPIO":[9,10,11,20,13,14,0,0,15,16,0,0,0],"FLAG":0,"BASE":18}
@@ -1523,12 +1616,15 @@ Vaticas 1 {"NAME":"Vaticas","GPIO":[0,0,0,17,21,0,0,0,0,0,52,0,0]
vhome RF433 3 Gang {"NAME":"VH-TB-US-003","GPIO":[0,0,0,0,21,18,0,0,19,23,17,22,158],"FLAG":15,"BASE":18}
WiFi Smart Switch 2 Gang {"NAME":"Kingart N2","GPIO":[17,255,0,255,0,22,18,0,21,0,0,0,0],"FLAG":15,"BASE":18}
WiFi Smart Switch 3 Gang {"NAME":"KingArt-3CH","GPIO":[17,255,0,255,23,22,18,19,21,52,0,0,0],"FLAG":15,"BASE":18}
+WL-SW01_10 {"NAME":"WL-SW01_10","GPIO":[17,149,0,148,0,0,0,0,21,56,0,0,0],"FLAG":0,"BASE":1}
WS-US-03 {"NAME":"WS-US-03","GPIO":[17,255,255,255,23,22,18,19,21,56,0,0,0],"FLAG":0,"BASE":30}
Xenon SM-SW102U 2 Gang {"NAME":"SM-SW102U-2","GPIO":[52,0,0,18,22,0,0,0,17,21,0,0,0],"FLAG":1,"BASE":18}
Xenon SM-SW202 {"NAME":"SM-SW202","GPIO":[0,0,0,17,21,0,0,0,0,0,52,0,0],"FLAG":0,"BASE":18}
Yapmor 1-gang {"NAME":"YAPMOR 1CH","GPIO":[17,255,255,255,0,0,0,0,21,56,0,0,0],"FLAG":0,"BASE":28}
Youngzuth 2in1 {"NAME":"SW02 2W","GPIO":[52,0,0,9,21,0,0,0,10,22,0,0,0],"FLAG":0,"BASE":18}
Youngzuth 3in1 {"NAME":"SW02 3W","GPIO":[56,0,0,19,23,18,0,0,17,21,0,22,0],"FLAG":0,"BASE":18}
+Yuntong Smart {"NAME":"Yuntong Smart","GPIO":[0,0,0,0,21,0,0,0,122,56,0,158,0],"FLAG":0,"BASE":1}
+Zemismart ERC309 Kinetic {"NAME":"Kinetic Switch","GPIO":[255,255,255,255,255,255,0,0,255,108,255,107,255],"FLAG":0,"BASE":54}
Zemismart KS-811 1 Gang {"NAME":"KS-811 Single","GPIO":[17,0,0,0,0,0,0,0,21,56,0,0,0],"FLAG":0,"BASE":18}
Zemismart KS-811 2 Gang {"NAME":"KS-811 Dual","GPIO":[0,0,158,0,0,18,0,0,22,21,0,0,17],"FLAG":0,"BASE":18}
Zemismart KS-811 3 Gang {"NAME":"KS-811 Triple","GPIO":[0,0,56,0,19,18,0,0,22,21,23,0,17],"FLAG":0,"BASE":18}
@@ -1538,10 +1634,19 @@ Zemismart WF-BS01 {"NAME":"WF-BS01","GPIO":[53,0,0,17,21,0,0,0,0,0,52,0,0
Zemismart ZM-L01E {"NAME":"ZSmart ZM-L01E","GPIO":[255,255,255,255,255,255,0,0,255,21,255,255,17],"FLAG":0,"BASE":18}
Zemismart ZM-L02E {"NAME":"ZSmart ZM-L02E","GPIO":[255,255,255,255,255,17,0,0,18,21,22,255,255],"FLAG":0,"BASE":18}
Zemismart ZM-L03E {"NAME":"ZSmart ZM-L03E","GPIO":[52,53,0,0,23,17,0,0,19,21,22,0,18],"FLAG":0,"BASE":18}
+ZUCZUG 1 Gang {"NAME":"2ph105626a x1","GPIO":[0,52,0,0,0,17,0,0,21,0,0,0,0],"FLAG":0,"BASE":1}
ZUCZUG 2 Gang {"NAME":"2ph105626a x2","GPIO":[0,52,0,17,18,0,0,0,0,21,22,0,0],"FLAG":0,"BASE":1}
ZUCZUG 3 Gang {"NAME":"2ph105626a x3","GPIO":[0,52,0,17,19,18,0,0,22,21,23,0,0],"FLAG":0,"BASE":1}
```
+## Temperature Sensor
+```
+DS18B20 ESP01 DIY {"NAME":"ESP01S ds18b20","GPIO":[255,255,4,255,255,255,0,0,255,255,255,255,255],"FLAG":15,"BASE":18}
+ESP01S DHT11 v1.0 DIY {"NAME":"ESP01S DHT11","GPIO":[0,0,1,0,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":4}
+IOT4SH01DS {"NAME":"IOT4SH01DS","GPIO":[255,255,255,255,255,255,0,0,255,4,255,255,255],"FLAG":15,"BASE":18}
+Shelly Add-on {"NAME":"Shelly 1 Temp ","GPIO":[192,0,0,4,21,82,0,0,0,0,0,0,0],"FLAG":0,"BASE":46}
+```
+
## Valve
```
Garden Water Timer BQ05 {"NAME":"BQ05","GPIO":[17,0,0,0,0,0,0,0,21,157,0,0,0],"FLAG":1,"BASE":18}
@@ -1575,13 +1680,18 @@ PS-1607 {"NAME":"PS-1607","GPIO":[17,0,0,0,0,22,18,0,21,0,0,0,0
Smanergy KA10 {"NAME":"KA10","GPIO":[0,56,0,17,134,132,0,0,131,53,21,0,0],"FLAG":0,"BASE":64}
Sonoff IW100 {"NAME":"Sonoff IW100","GPIO":[17,145,0,146,0,0,0,0,21,157,0,0,0],"FLAG":0,"BASE":41}
Sonoff S55 {"NAME":"Sonoff S55","GPIO":[17,255,0,255,255,0,0,0,21,56,0,0,0],"FLAG":0,"BASE":1}
-T16E Dual USB 10A {"NAME":"t16E 10A","GPIO":[0,0,0,17,134,132,0,0,131,56,21,0,0],"FLAG":0,"BASE":18}
+T16E Dual USB 10A {"NAME":"T16E 10A","GPIO":[0,0,0,17,134,132,0,0,131,56,21,0,0],"FLAG":0,"BASE":18}
Teckin SR40 {"NAME":"RF-SR40-US","GPIO":[158,0,0,17,56,18,0,0,22,21,57,23,0],"FLAG":0,"BASE":18}
TopGreener TGWF15RM {"NAME":"TGWF15RM","GPIO":[0,56,0,17,134,132,0,0,131,57,21,0,0],"FLAG":0,"BASE":55}
Vigica VGSPK00815 {"NAME":"VIGICA outlet","GPIO":[17,255,255,255,255,22,18,255,21,255,255,255,255],"FLAG":1,"BASE":18}
```
+## Water Sensor
+```
+W06 {"NAME":"W06 Water Sensor","GPIO":[0,148,0,149,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":18}
+```
+
## Zigbee Bridge
```
-Sonoff ZBBridge {"NAME":"Sonoff ZbBridge","GPIO":[56,165,0,166,215,0,0,0,0,158,0,0,17],"FLAG":0,"BASE":75}
+Sonoff ZBBridge {"NAME":"Sonoff ZbBridge","GPIO":[56,165,0,166,215,0,0,0,6,158,5,0,17],"FLAG":0,"BASE":75}
```
From 2a18de5942b8301cbe8358bf4a7a879c84466361 Mon Sep 17 00:00:00 2001
From: Stephan Hadinger
Date: Sat, 5 Sep 2020 16:33:53 +0200
Subject: [PATCH 21/38] Zigbee better support for WSDCGQ01LM variants
---
tasmota/xdrv_23_zigbee_5_converters.ino | 1 +
1 file changed, 1 insertion(+)
diff --git a/tasmota/xdrv_23_zigbee_5_converters.ino b/tasmota/xdrv_23_zigbee_5_converters.ino
index 5655244d7..9c8d10cdf 100644
--- a/tasmota/xdrv_23_zigbee_5_converters.ino
+++ b/tasmota/xdrv_23_zigbee_5_converters.ino
@@ -1280,6 +1280,7 @@ void ZCLFrame::syntheticAqaraSensor(Z_attribute_list &attr_list, class Z_attribu
} else if ((nullptr != modelId) && (0 == getManufCode())) {
translated = true;
if (modelId.startsWith(F("lumi.sensor_ht")) ||
+ modelId.equals(F("lumi.sens")) ||
modelId.startsWith(F("lumi.weather"))) { // Temp sensor
// Filter according to prefix of model name
// onla Aqara Temp/Humidity has manuf_code of zero. If non-zero we skip the parameters
From 8c555dd22dfd4d24822e337758699e5a9edf9b9e Mon Sep 17 00:00:00 2001
From: Jason2866 <24528715+Jason2866@users.noreply.github.com>
Date: Sat, 5 Sep 2020 18:04:11 +0200
Subject: [PATCH 22/38] Delete core_esp8266_waveform.cpp
---
tasmota/core_esp8266_waveform.cpp | 440 ------------------------------
1 file changed, 440 deletions(-)
delete mode 100644 tasmota/core_esp8266_waveform.cpp
diff --git a/tasmota/core_esp8266_waveform.cpp b/tasmota/core_esp8266_waveform.cpp
deleted file mode 100644
index 371e9e554..000000000
--- a/tasmota/core_esp8266_waveform.cpp
+++ /dev/null
@@ -1,440 +0,0 @@
-/*
- esp8266_waveform - General purpose waveform generation and control,
- supporting outputs on all pins in parallel.
-
- Copyright (c) 2018 Earle F. Philhower, III. All rights reserved.
- Copyright (c) 2020 Dirk O. Kaar.
-
- The core idea is to have a programmable waveform generator with a unique
- high and low period (defined in microseconds or CPU clock cycles). TIMER1 is
- set to 1-shot mode and is always loaded with the time until the next edge
- of any live waveforms.
-
- Up to one waveform generator per pin supported.
-
- Each waveform generator is synchronized to the ESP clock cycle counter, not the
- timer. This allows for removing interrupt jitter and delay as the counter
- always increments once per 80MHz clock. Changes to a waveform are
- contiguous and only take effect on the next waveform transition,
- allowing for smooth transitions.
-
- This replaces older tone(), analogWrite(), and the Servo classes.
-
- Everywhere in the code where "ccy" or "ccys" is used, it means ESP.getCycleCount()
- clock cycle time, or an interval measured in clock cycles, but not TIMER1
- cycles (which may be 2 CPU clock cycles @ 160MHz).
-
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 2.1 of the License, or (at your option) any later version.
-
- This library 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
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with this library; if not, write to the Free Software
- Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-*/
-
-#ifdef ESP8266
-
-#include "core_esp8266_waveform.h"
-#include
-#include "ets_sys.h"
-#include
-
-// Timer is 80MHz fixed. 160MHz CPU frequency need scaling.
-constexpr bool ISCPUFREQ160MHZ = clockCyclesPerMicrosecond() == 160;
-// Maximum delay between IRQs, Timer1, <= 2^23 / 80MHz
-constexpr int32_t MAXIRQTICKSCCYS = microsecondsToClockCycles(10000);
-// Maximum servicing time for any single IRQ
-constexpr uint32_t ISRTIMEOUTCCYS = microsecondsToClockCycles(18);
-// The latency between in-ISR rearming of the timer and the earliest firing
-constexpr int32_t IRQLATENCYCCYS = microsecondsToClockCycles(2);
-// The SDK and hardware take some time to actually get to our NMI code
-constexpr int32_t DELTAIRQCCYS = ISCPUFREQ160MHZ ?
- microsecondsToClockCycles(2) >> 1 : microsecondsToClockCycles(2);
-
-// for INFINITE, the NMI proceeds on the waveform without expiry deadline.
-// for EXPIRES, the NMI expires the waveform automatically on the expiry ccy.
-// for UPDATEEXPIRY, the NMI recomputes the exact expiry ccy and transitions to EXPIRES.
-// for INIT, the NMI initializes nextPeriodCcy, and if expiryCcy != 0 includes UPDATEEXPIRY.
-enum class WaveformMode : uint8_t {INFINITE = 0, EXPIRES = 1, UPDATEEXPIRY = 2, INIT = 3};
-
-// Waveform generator can create tones, PWM, and servos
-typedef struct {
- uint32_t nextPeriodCcy; // ESP clock cycle when a period begins. If WaveformMode::INIT, temporarily holds positive phase offset ccy count
- uint32_t endDutyCcy; // ESP clock cycle when going from duty to off
- int32_t dutyCcys; // Set next off cycle at low->high to maintain phase
- int32_t adjDutyCcys; // Temporary correction for next period
- int32_t periodCcys; // Set next phase cycle at low->high to maintain phase
- uint32_t expiryCcy; // For time-limited waveform, the CPU clock cycle when this waveform must stop. If WaveformMode::UPDATE, temporarily holds relative ccy count
- WaveformMode mode;
- int8_t alignPhase; // < 0 no phase alignment, otherwise starts waveform in relative phase offset to given pin
- bool autoPwm; // perform PWM duty to idle cycle ratio correction under high load at the expense of precise timings
-} Waveform;
-
-namespace {
-
- static struct {
- Waveform pins[17]; // State of all possible pins
- uint32_t states = 0; // Is the pin high or low, updated in NMI so no access outside the NMI code
- uint32_t enabled = 0; // Is it actively running, updated in NMI so no access outside the NMI code
-
- // Enable lock-free by only allowing updates to waveform.states and waveform.enabled from IRQ service routine
- int32_t toSetBits = 0; // Message to the NMI handler to start/modify exactly one waveform
- int32_t toDisableBits = 0; // Message to the NMI handler to disable exactly one pin from waveform generation
-
- uint32_t(*timer1CB)() = nullptr;
-
- bool timer1Running = false;
-
- uint32_t nextEventCcy;
- } waveform;
-
-}
-
-// Interrupt on/off control
-static ICACHE_RAM_ATTR void timer1Interrupt();
-
-// Non-speed critical bits
-#pragma GCC optimize ("Os")
-
-static void initTimer() {
- timer1_disable();
- ETS_FRC_TIMER1_INTR_ATTACH(NULL, NULL);
- ETS_FRC_TIMER1_NMI_INTR_ATTACH(timer1Interrupt);
- timer1_enable(TIM_DIV1, TIM_EDGE, TIM_SINGLE);
- waveform.timer1Running = true;
- timer1_write(IRQLATENCYCCYS); // Cause an interrupt post-haste
-}
-
-static void ICACHE_RAM_ATTR deinitTimer() {
- ETS_FRC_TIMER1_NMI_INTR_ATTACH(NULL);
- timer1_disable();
- timer1_isr_init();
- waveform.timer1Running = false;
-}
-
-extern "C" {
-
-// Set a callback. Pass in NULL to stop it
-void setTimer1Callback(uint32_t (*fn)()) {
- waveform.timer1CB = fn;
- std::atomic_thread_fence(std::memory_order_acq_rel);
- if (!waveform.timer1Running && fn) {
- initTimer();
- } else if (waveform.timer1Running && !fn && !waveform.enabled) {
- deinitTimer();
- }
-}
-
-int startWaveform(uint8_t pin, uint32_t highUS, uint32_t lowUS,
- uint32_t runTimeUS, int8_t alignPhase, uint32_t phaseOffsetUS, bool autoPwm) {
- return startWaveformClockCycles(pin,
- microsecondsToClockCycles(highUS), microsecondsToClockCycles(lowUS),
- microsecondsToClockCycles(runTimeUS), alignPhase, microsecondsToClockCycles(phaseOffsetUS), autoPwm);
-}
-
-// Start up a waveform on a pin, or change the current one. Will change to the new
-// waveform smoothly on next low->high transition. For immediate change, stopWaveform()
-// first, then it will immediately begin.
-int startWaveformClockCycles(uint8_t pin, uint32_t highCcys, uint32_t lowCcys,
- uint32_t runTimeCcys, int8_t alignPhase, uint32_t phaseOffsetCcys, bool autoPwm) {
- uint32_t periodCcys = highCcys + lowCcys;
- if (periodCcys < MAXIRQTICKSCCYS) {
- if (!highCcys) {
- periodCcys = (MAXIRQTICKSCCYS / periodCcys) * periodCcys;
- }
- else if (!lowCcys) {
- highCcys = periodCcys = (MAXIRQTICKSCCYS / periodCcys) * periodCcys;
- }
- }
- // sanity checks, including mixed signed/unsigned arithmetic safety
- if ((pin > 16) || isFlashInterfacePin(pin) || (alignPhase > 16) ||
- static_cast(periodCcys) <= 0 ||
- static_cast(highCcys) < 0 || static_cast(lowCcys) < 0) {
- return false;
- }
- Waveform& wave = waveform.pins[pin];
- wave.dutyCcys = highCcys;
- wave.adjDutyCcys = 0;
- wave.periodCcys = periodCcys;
- wave.autoPwm = autoPwm;
-
- std::atomic_thread_fence(std::memory_order_acquire);
- const uint32_t pinBit = 1UL << pin;
- if (!(waveform.enabled & pinBit)) {
- // wave.nextPeriodCcy and wave.endDutyCcy are initialized by the ISR
- wave.nextPeriodCcy = phaseOffsetCcys;
- wave.expiryCcy = runTimeCcys; // in WaveformMode::INIT, temporarily hold relative cycle count
- wave.mode = WaveformMode::INIT;
- wave.alignPhase = (alignPhase < 0) ? -1 : alignPhase;
- if (!wave.dutyCcys) {
- // If initially at zero duty cycle, force GPIO off
- if (pin == 16) {
- GP16O = 0;
- }
- else {
- GPOC = pinBit;
- }
- }
- std::atomic_thread_fence(std::memory_order_release);
- waveform.toSetBits = 1UL << pin;
- std::atomic_thread_fence(std::memory_order_release);
- if (!waveform.timer1Running) {
- initTimer();
- }
- else if (T1V > IRQLATENCYCCYS) {
- // Must not interfere if Timer is due shortly
- timer1_write(IRQLATENCYCCYS);
- }
- }
- else {
- wave.mode = WaveformMode::INFINITE; // turn off possible expiry to make update atomic from NMI
- std::atomic_thread_fence(std::memory_order_release);
- wave.expiryCcy = runTimeCcys; // in WaveformMode::UPDATEEXPIRY, temporarily hold relative cycle count
- if (runTimeCcys) {
- wave.mode = WaveformMode::UPDATEEXPIRY;
- std::atomic_thread_fence(std::memory_order_release);
- waveform.toSetBits = 1UL << pin;
- }
- }
- std::atomic_thread_fence(std::memory_order_acq_rel);
- while (waveform.toSetBits) {
- delay(0); // Wait for waveform to update
- std::atomic_thread_fence(std::memory_order_acquire);
- }
- return true;
-}
-
-// Stops a waveform on a pin
-int ICACHE_RAM_ATTR stopWaveform(uint8_t pin) {
- // Can't possibly need to stop anything if there is no timer active
- if (!waveform.timer1Running) {
- return false;
- }
- // If user sends in a pin >16 but <32, this will always point to a 0 bit
- // If they send >=32, then the shift will result in 0 and it will also return false
- std::atomic_thread_fence(std::memory_order_acquire);
- const uint32_t pinBit = 1UL << pin;
- if (waveform.enabled & pinBit) {
- waveform.toDisableBits = 1UL << pin;
- std::atomic_thread_fence(std::memory_order_release);
- // Must not interfere if Timer is due shortly
- if (T1V > IRQLATENCYCCYS) {
- timer1_write(IRQLATENCYCCYS);
- }
- while (waveform.toDisableBits) {
- /* no-op */ // Can't delay() since stopWaveform may be called from an IRQ
- std::atomic_thread_fence(std::memory_order_acquire);
- }
- }
- if (!waveform.enabled && !waveform.timer1CB) {
- deinitTimer();
- }
- return true;
-}
-
-};
-
-// Speed critical bits
-#pragma GCC optimize ("O2")
-
-// For dynamic CPU clock frequency switch in loop the scaling logic would have to be adapted.
-// Using constexpr makes sure that the CPU clock frequency is compile-time fixed.
-static inline ICACHE_RAM_ATTR int32_t scaleCcys(const int32_t ccys, const bool isCPU2X) {
- if (ISCPUFREQ160MHZ) {
- return isCPU2X ? ccys : (ccys >> 1);
- }
- else {
- return isCPU2X ? (ccys << 1) : ccys;
- }
-}
-
-static ICACHE_RAM_ATTR void timer1Interrupt() {
- const uint32_t isrStartCcy = ESP.getCycleCount();
- int32_t clockDrift = isrStartCcy - waveform.nextEventCcy;
- const bool isCPU2X = CPU2X & 1;
- if ((waveform.toSetBits && !(waveform.enabled & waveform.toSetBits)) || waveform.toDisableBits) {
- // Handle enable/disable requests from main app.
- waveform.enabled = (waveform.enabled & ~waveform.toDisableBits) | waveform.toSetBits; // Set the requested waveforms on/off
- // Find the first GPIO being generated by checking GCC's find-first-set (returns 1 + the bit of the first 1 in an int32_t)
- waveform.toDisableBits = 0;
- }
-
- if (waveform.toSetBits) {
- const int toSetPin = __builtin_ffs(waveform.toSetBits) - 1;
- Waveform& wave = waveform.pins[toSetPin];
- switch (wave.mode) {
- case WaveformMode::INIT:
- waveform.states &= ~waveform.toSetBits; // Clear the state of any just started
- if (wave.alignPhase >= 0 && waveform.enabled & (1UL << wave.alignPhase)) {
- wave.nextPeriodCcy = waveform.pins[wave.alignPhase].nextPeriodCcy + wave.nextPeriodCcy;
- }
- else {
- wave.nextPeriodCcy = waveform.nextEventCcy;
- }
- if (!wave.expiryCcy) {
- wave.mode = WaveformMode::INFINITE;
- break;
- }
- // fall through
- case WaveformMode::UPDATEEXPIRY:
- // in WaveformMode::UPDATEEXPIRY, expiryCcy temporarily holds relative CPU cycle count
- wave.expiryCcy = wave.nextPeriodCcy + scaleCcys(wave.expiryCcy, isCPU2X);
- wave.mode = WaveformMode::EXPIRES;
- break;
- default:
- break;
- }
- waveform.toSetBits = 0;
- }
-
- // Exit the loop if the next event, if any, is sufficiently distant.
- const uint32_t isrTimeoutCcy = isrStartCcy + ISRTIMEOUTCCYS;
- uint32_t busyPins = waveform.enabled;
- waveform.nextEventCcy = isrStartCcy + MAXIRQTICKSCCYS;
-
- uint32_t now = ESP.getCycleCount();
- uint32_t isrNextEventCcy = now;
- while (busyPins) {
- if (static_cast(isrNextEventCcy - now) > IRQLATENCYCCYS) {
- waveform.nextEventCcy = isrNextEventCcy;
- break;
- }
- isrNextEventCcy = waveform.nextEventCcy;
- uint32_t loopPins = busyPins;
- while (loopPins) {
- const int pin = __builtin_ffsl(loopPins) - 1;
- const uint32_t pinBit = 1UL << pin;
- loopPins ^= pinBit;
-
- Waveform& wave = waveform.pins[pin];
-
- if (clockDrift) {
- wave.endDutyCcy += clockDrift;
- wave.nextPeriodCcy += clockDrift;
- wave.expiryCcy += clockDrift;
- }
-
- uint32_t waveNextEventCcy = (waveform.states & pinBit) ? wave.endDutyCcy : wave.nextPeriodCcy;
- if (WaveformMode::EXPIRES == wave.mode &&
- static_cast(waveNextEventCcy - wave.expiryCcy) >= 0 &&
- static_cast(now - wave.expiryCcy) >= 0) {
- // Disable any waveforms that are done
- waveform.enabled ^= pinBit;
- busyPins ^= pinBit;
- }
- else {
- const int32_t overshootCcys = now - waveNextEventCcy;
- if (overshootCcys >= 0) {
- const int32_t periodCcys = scaleCcys(wave.periodCcys, isCPU2X);
- if (waveform.states & pinBit) {
- // active configuration and forward are 100% duty
- if (wave.periodCcys == wave.dutyCcys) {
- wave.nextPeriodCcy += periodCcys;
- wave.endDutyCcy = wave.nextPeriodCcy;
- }
- else {
- if (wave.autoPwm) {
- wave.adjDutyCcys += overshootCcys;
- }
- waveform.states ^= pinBit;
- if (16 == pin) {
- GP16O = 0;
- }
- else {
- GPOC = pinBit;
- }
- }
- waveNextEventCcy = wave.nextPeriodCcy;
- }
- else {
- wave.nextPeriodCcy += periodCcys;
- if (!wave.dutyCcys) {
- wave.endDutyCcy = wave.nextPeriodCcy;
- }
- else {
- int32_t dutyCcys = scaleCcys(wave.dutyCcys, isCPU2X);
- if (dutyCcys <= wave.adjDutyCcys) {
- dutyCcys >>= 1;
- wave.adjDutyCcys -= dutyCcys;
- }
- else if (wave.adjDutyCcys) {
- dutyCcys -= wave.adjDutyCcys;
- wave.adjDutyCcys = 0;
- }
- wave.endDutyCcy = now + dutyCcys;
- if (static_cast(wave.endDutyCcy - wave.nextPeriodCcy) > 0) {
- wave.endDutyCcy = wave.nextPeriodCcy;
- }
- waveform.states |= pinBit;
- if (16 == pin) {
- GP16O = 1;
- }
- else {
- GPOS = pinBit;
- }
- }
- waveNextEventCcy = wave.endDutyCcy;
- }
-
- if (WaveformMode::EXPIRES == wave.mode && static_cast(waveNextEventCcy - wave.expiryCcy) > 0) {
- waveNextEventCcy = wave.expiryCcy;
- }
- }
-
- if (static_cast(waveNextEventCcy - isrTimeoutCcy) >= 0) {
- busyPins ^= pinBit;
- if (static_cast(waveform.nextEventCcy - waveNextEventCcy) > 0) {
- waveform.nextEventCcy = waveNextEventCcy;
- }
- }
- else if (static_cast(isrNextEventCcy - waveNextEventCcy) > 0) {
- isrNextEventCcy = waveNextEventCcy;
- }
- }
- now = ESP.getCycleCount();
- }
- clockDrift = 0;
- }
-
- int32_t callbackCcys = 0;
- if (waveform.timer1CB) {
- callbackCcys = scaleCcys(microsecondsToClockCycles(waveform.timer1CB()), isCPU2X);
- }
- now = ESP.getCycleCount();
- int32_t nextEventCcys = waveform.nextEventCcy - now;
- // Account for unknown duration of timer1CB().
- if (waveform.timer1CB && nextEventCcys > callbackCcys) {
- waveform.nextEventCcy = now + callbackCcys;
- nextEventCcys = callbackCcys;
- }
-
- // Timer is 80MHz fixed. 160MHz CPU frequency need scaling.
- int32_t deltaIrqCcys = DELTAIRQCCYS;
- int32_t irqLatencyCcys = IRQLATENCYCCYS;
- if (isCPU2X) {
- nextEventCcys >>= 1;
- deltaIrqCcys >>= 1;
- irqLatencyCcys >>= 1;
- }
-
- // Firing timer too soon, the NMI occurs before ISR has returned.
- if (nextEventCcys < irqLatencyCcys + deltaIrqCcys) {
- waveform.nextEventCcy = now + IRQLATENCYCCYS + DELTAIRQCCYS;
- nextEventCcys = irqLatencyCcys;
- }
- else {
- nextEventCcys -= deltaIrqCcys;
- }
-
- // Register access is fast and edge IRQ was configured before.
- T1L = nextEventCcys;
-}
-
-#endif // ESP8266
From f3123a276105f4dba1648d4909f7d9382485bbb4 Mon Sep 17 00:00:00 2001
From: Jason2866 <24528715+Jason2866@users.noreply.github.com>
Date: Sat, 5 Sep 2020 18:05:43 +0200
Subject: [PATCH 23/38] Delete core_esp8266_waveform.h
---
tasmota/core_esp8266_waveform.h | 93 ---------------------------------
1 file changed, 93 deletions(-)
delete mode 100644 tasmota/core_esp8266_waveform.h
diff --git a/tasmota/core_esp8266_waveform.h b/tasmota/core_esp8266_waveform.h
deleted file mode 100644
index ff5a0f56f..000000000
--- a/tasmota/core_esp8266_waveform.h
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- esp8266_waveform - General purpose waveform generation and control,
- supporting outputs on all pins in parallel.
-
- Copyright (c) 2018 Earle F. Philhower, III. All rights reserved.
- Copyright (c) 2020 Dirk O. Kaar.
-
- The core idea is to have a programmable waveform generator with a unique
- high and low period (defined in microseconds or CPU clock cycles). TIMER1 is
- set to 1-shot mode and is always loaded with the time until the next edge
- of any live waveforms.
-
- Up to one waveform generator per pin supported.
-
- Each waveform generator is synchronized to the ESP clock cycle counter, not the
- timer. This allows for removing interrupt jitter and delay as the counter
- always increments once per 80MHz clock. Changes to a waveform are
- contiguous and only take effect on the next waveform transition,
- allowing for smooth transitions.
-
- This replaces older tone(), analogWrite(), and the Servo classes.
-
- Everywhere in the code where "ccy" or "ccys" is used, it means ESP.getCycleCount()
- clock cycle count, or an interval measured in CPU clock cycles, but not TIMER1
- cycles (which may be 2 CPU clock cycles @ 160MHz).
-
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 2.1 of the License, or (at your option) any later version.
-
- This library 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
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with this library; if not, write to the Free Software
- Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-*/
-
-#ifdef ESP8266
-
-#include
-
-#ifndef __ESP8266_WAVEFORM_H
-#define __ESP8266_WAVEFORM_H
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-// Start or change a waveform of the specified high and low times on specific pin.
-// If runtimeUS > 0 then automatically stop it after that many usecs, relative to the next
-// full period.
-// If waveform is not yet started on pin, and on pin == alignPhase a waveform is running,
-// the new waveform is started at phaseOffsetUS phase offset, in microseconds, to that.
-// Setting autoPwm to true allows the wave generator to maintain PWM duty to idle cycle ratio
-// under load, for applications where frequency or duty cycle must not change, leave false.
-// Returns true or false on success or failure.
-int startWaveform(uint8_t pin, uint32_t timeHighUS, uint32_t timeLowUS,
- uint32_t runTimeUS = 0, int8_t alignPhase = -1, uint32_t phaseOffsetUS = 0, bool autoPwm = false);
-// Start or change a waveform of the specified high and low CPU clock cycles on specific pin.
-// If runtimeCycles > 0 then automatically stop it after that many CPU clock cycles, relative to the next
-// full period.
-// If waveform is not yet started on pin, and on pin == alignPhase a waveform is running,
-// the new waveform is started at phaseOffsetCcys phase offset, in CPU clock cycles, to that.
-// Setting autoPwm to true allows the wave generator to maintain PWM duty to idle cycle ratio
-// under load, for applications where frequency or duty cycle must not change, leave false.
-// Returns true or false on success or failure.
-int startWaveformClockCycles(uint8_t pin, uint32_t timeHighCcys, uint32_t timeLowCcys,
- uint32_t runTimeCcys = 0, int8_t alignPhase = -1, uint32_t phaseOffsetCcys = 0, bool autoPwm = false);
-// Stop a waveform, if any, on the specified pin.
-// Returns true or false on success or failure.
-int stopWaveform(uint8_t pin);
-
-// Add a callback function to be called on *EVERY* timer1 trigger. The
-// callback returns the number of microseconds until the next desired call.
-// However, since it is called every timer1 interrupt, it may be called
-// again before this period. It should therefore use the ESP Cycle Counter
-// to determine whether or not to perform an operation.
-// Pass in NULL to disable the callback and, if no other waveforms being
-// generated, stop the timer as well.
-// Make sure the CB function has the ICACHE_RAM_ATTR decorator.
-void setTimer1Callback(uint32_t (*fn)());
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
-
-#endif // ESP8266
From 3c2746892ae70ff8f32bd052a843bed21cadbe12 Mon Sep 17 00:00:00 2001
From: Jason2866 <24528715+Jason2866@users.noreply.github.com>
Date: Sat, 5 Sep 2020 18:05:53 +0200
Subject: [PATCH 24/38] Delete core_esp8266_wiring_digital.cpp
---
tasmota/core_esp8266_wiring_digital.cpp | 269 ------------------------
1 file changed, 269 deletions(-)
delete mode 100644 tasmota/core_esp8266_wiring_digital.cpp
diff --git a/tasmota/core_esp8266_wiring_digital.cpp b/tasmota/core_esp8266_wiring_digital.cpp
deleted file mode 100644
index 982e1c6bf..000000000
--- a/tasmota/core_esp8266_wiring_digital.cpp
+++ /dev/null
@@ -1,269 +0,0 @@
-/*
- digital.c - wiring digital implementation for esp8266
-
- Copyright (c) 2015 Hristo Gochkov. All rights reserved.
- This file is part of the esp8266 core for Arduino environment.
-
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 2.1 of the License, or (at your option) any later version.
-
- This library 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
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with this library; if not, write to the Free Software
- Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-*/
-
-#ifdef ESP8266
-
-#define ARDUINO_MAIN
-#include "wiring_private.h"
-#include "pins_arduino.h"
-#include "c_types.h"
-#include "eagle_soc.h"
-#include "ets_sys.h"
-#include "user_interface.h"
-#include "core_esp8266_waveform.h"
-#include "interrupts.h"
-
-extern "C" {
-
-volatile uint32_t* const esp8266_gpioToFn[16] PROGMEM = { &GPF0, &GPF1, &GPF2, &GPF3, &GPF4, &GPF5, &GPF6, &GPF7, &GPF8, &GPF9, &GPF10, &GPF11, &GPF12, &GPF13, &GPF14, &GPF15 };
-
-extern void __pinMode(uint8_t pin, uint8_t mode) {
- if(pin < 16){
- if(mode == SPECIAL){
- GPC(pin) = (GPC(pin) & (0xF << GPCI)); //SOURCE(GPIO) | DRIVER(NORMAL) | INT_TYPE(UNCHANGED) | WAKEUP_ENABLE(DISABLED)
- GPEC = (1 << pin); //Disable
- GPF(pin) = GPFFS(GPFFS_BUS(pin));//Set mode to BUS (RX0, TX0, TX1, SPI, HSPI or CLK depending in the pin)
- if(pin == 3) GPF(pin) |= (1 << GPFPU);//enable pullup on RX
- } else if(mode & FUNCTION_0){
- GPC(pin) = (GPC(pin) & (0xF << GPCI)); //SOURCE(GPIO) | DRIVER(NORMAL) | INT_TYPE(UNCHANGED) | WAKEUP_ENABLE(DISABLED)
- GPEC = (1 << pin); //Disable
- GPF(pin) = GPFFS((mode >> 4) & 0x07);
- if(pin == 13 && mode == FUNCTION_4) GPF(pin) |= (1 << GPFPU);//enable pullup on RX
- } else if(mode == OUTPUT || mode == OUTPUT_OPEN_DRAIN){
- GPF(pin) = GPFFS(GPFFS_GPIO(pin));//Set mode to GPIO
- GPC(pin) = (GPC(pin) & (0xF << GPCI)); //SOURCE(GPIO) | DRIVER(NORMAL) | INT_TYPE(UNCHANGED) | WAKEUP_ENABLE(DISABLED)
- if(mode == OUTPUT_OPEN_DRAIN) GPC(pin) |= (1 << GPCD);
- GPES = (1 << pin); //Enable
- } else if(mode == INPUT || mode == INPUT_PULLUP){
- GPF(pin) = GPFFS(GPFFS_GPIO(pin));//Set mode to GPIO
- GPEC = (1 << pin); //Disable
- GPC(pin) = (GPC(pin) & (0xF << GPCI)) | (1 << GPCD); //SOURCE(GPIO) | DRIVER(OPEN_DRAIN) | INT_TYPE(UNCHANGED) | WAKEUP_ENABLE(DISABLED)
- if(mode == INPUT_PULLUP) {
- GPF(pin) |= (1 << GPFPU); // Enable Pullup
- }
- } else if(mode == WAKEUP_PULLUP || mode == WAKEUP_PULLDOWN){
- GPF(pin) = GPFFS(GPFFS_GPIO(pin));//Set mode to GPIO
- GPEC = (1 << pin); //Disable
- if(mode == WAKEUP_PULLUP) {
- GPF(pin) |= (1 << GPFPU); // Enable Pullup
- GPC(pin) = (1 << GPCD) | (4 << GPCI) | (1 << GPCWE); //SOURCE(GPIO) | DRIVER(OPEN_DRAIN) | INT_TYPE(LOW) | WAKEUP_ENABLE(ENABLED)
- } else {
- GPF(pin) |= (1 << GPFPD); // Enable Pulldown
- GPC(pin) = (1 << GPCD) | (5 << GPCI) | (1 << GPCWE); //SOURCE(GPIO) | DRIVER(OPEN_DRAIN) | INT_TYPE(HIGH) | WAKEUP_ENABLE(ENABLED)
- }
- }
- } else if(pin == 16){
- GPF16 = GP16FFS(GPFFS_GPIO(pin));//Set mode to GPIO
- GPC16 = 0;
- if(mode == INPUT || mode == INPUT_PULLDOWN_16){
- if(mode == INPUT_PULLDOWN_16){
- GPF16 |= (1 << GP16FPD);//Enable Pulldown
- }
- GP16E &= ~1;
- } else if(mode == OUTPUT){
- GP16E |= 1;
- }
- }
-}
-
-extern void ICACHE_RAM_ATTR __digitalWrite(uint8_t pin, uint8_t val) {
- stopWaveform(pin);
- if(pin < 16){
- if(val) GPOS = (1 << pin);
- else GPOC = (1 << pin);
- } else if(pin == 16){
- if(val) GP16O |= 1;
- else GP16O &= ~1;
- }
-}
-
-extern int ICACHE_RAM_ATTR __digitalRead(uint8_t pin) {
- if(pin < 16){
- return GPIP(pin);
- } else if(pin == 16){
- return GP16I & 0x01;
- }
- return 0;
-}
-
-/*
- GPIO INTERRUPTS
-*/
-
-typedef void (*voidFuncPtr)(void);
-typedef void (*voidFuncPtrArg)(void*);
-
-typedef struct {
- uint8_t mode;
- voidFuncPtr fn;
- void * arg;
- bool functional;
-} interrupt_handler_t;
-
-//duplicate from functionalInterrupt.h keep in sync
-typedef struct InterruptInfo {
- uint8_t pin;
- uint8_t value;
- uint32_t micro;
-} InterruptInfo;
-
-typedef struct {
- InterruptInfo* interruptInfo;
- void* functionInfo;
-} ArgStructure;
-
-static interrupt_handler_t interrupt_handlers[16] = { {0, 0, 0, 0}, };
-static uint32_t interrupt_reg = 0;
-
-void ICACHE_RAM_ATTR interrupt_handler(void *arg, void *frame)
-{
- (void) arg;
- (void) frame;
- uint32_t status = GPIE;
- GPIEC = status;//clear them interrupts
- uint32_t levels = GPI;
- if(status == 0 || interrupt_reg == 0) return;
- ETS_GPIO_INTR_DISABLE();
- int i = 0;
- uint32_t changedbits = status & interrupt_reg;
- while(changedbits){
- while(!(changedbits & (1 << i))) i++;
- changedbits &= ~(1 << i);
- interrupt_handler_t *handler = &interrupt_handlers[i];
- if (handler->fn &&
- (handler->mode == CHANGE ||
- (handler->mode & 1) == !!(levels & (1 << i)))) {
- // to make ISR compatible to Arduino AVR model where interrupts are disabled
- // we disable them before we call the client ISR
- esp8266::InterruptLock irqLock; // stop other interrupts
- if (handler->functional)
- {
- ArgStructure* localArg = (ArgStructure*)handler->arg;
- if (localArg && localArg->interruptInfo)
- {
- localArg->interruptInfo->pin = i;
- localArg->interruptInfo->value = __digitalRead(i);
- localArg->interruptInfo->micro = micros();
- }
- }
- if (handler->arg)
- {
- ((voidFuncPtrArg)handler->fn)(handler->arg);
- }
- else
- {
- handler->fn();
- }
- }
- }
- ETS_GPIO_INTR_ENABLE();
-}
-
-extern void cleanupFunctional(void* arg);
-
-static void set_interrupt_handlers(uint8_t pin, voidFuncPtr userFunc, void* arg, uint8_t mode, bool functional)
-{
- interrupt_handler_t* handler = &interrupt_handlers[pin];
- handler->mode = mode;
- handler->fn = userFunc;
- if (handler->functional && handler->arg) // Clean when new attach without detach
- {
- cleanupFunctional(handler->arg);
- }
- handler->arg = arg;
- handler->functional = functional;
-}
-
-extern void __attachInterruptFunctionalArg(uint8_t pin, voidFuncPtrArg userFunc, void* arg, int mode, bool functional)
-{
- // #5780
- // https://github.com/esp8266/esp8266-wiki/wiki/Memory-Map
- if ((uint32_t)userFunc >= 0x40200000)
- {
- // ISR not in IRAM
- ::printf((PGM_P)F("ISR not in IRAM!\r\n"));
- abort();
- }
-
- if(pin < 16) {
- ETS_GPIO_INTR_DISABLE();
- set_interrupt_handlers(pin, (voidFuncPtr)userFunc, arg, mode, functional);
- interrupt_reg |= (1 << pin);
- GPC(pin) &= ~(0xF << GPCI);//INT mode disabled
- GPIEC = (1 << pin); //Clear Interrupt for this pin
- GPC(pin) |= ((mode & 0xF) << GPCI);//INT mode "mode"
- ETS_GPIO_INTR_ATTACH(interrupt_handler, &interrupt_reg);
- ETS_GPIO_INTR_ENABLE();
- }
-}
-
-extern void __attachInterruptArg(uint8_t pin, voidFuncPtrArg userFunc, void* arg, int mode)
-{
- __attachInterruptFunctionalArg(pin, userFunc, arg, mode, false);
-}
-
-extern void ICACHE_RAM_ATTR __detachInterrupt(uint8_t pin) {
- if (pin < 16)
- {
- ETS_GPIO_INTR_DISABLE();
- GPC(pin) &= ~(0xF << GPCI);//INT mode disabled
- GPIEC = (1 << pin); //Clear Interrupt for this pin
- interrupt_reg &= ~(1 << pin);
- set_interrupt_handlers(pin, nullptr, nullptr, 0, false);
- if (interrupt_reg)
- {
- ETS_GPIO_INTR_ENABLE();
- }
- }
-}
-
-extern void __attachInterrupt(uint8_t pin, voidFuncPtr userFunc, int mode)
-{
- __attachInterruptFunctionalArg(pin, (voidFuncPtrArg)userFunc, 0, mode, false);
-}
-
-extern void __resetPins() {
- for (int i = 0; i <= 16; ++i) {
- if (!isFlashInterfacePin(i))
- pinMode(i, INPUT);
- }
-}
-
-extern void initPins() {
- //Disable UART interrupts
- system_set_os_print(0);
- U0IE = 0;
- U1IE = 0;
-
- resetPins();
-}
-
-extern void resetPins() __attribute__ ((weak, alias("__resetPins")));
-extern void pinMode(uint8_t pin, uint8_t mode) __attribute__ ((weak, alias("__pinMode")));
-extern void digitalWrite(uint8_t pin, uint8_t val) __attribute__ ((weak, alias("__digitalWrite")));
-extern int digitalRead(uint8_t pin) __attribute__ ((weak, alias("__digitalRead"), nothrow));
-extern void attachInterrupt(uint8_t pin, voidFuncPtr handler, int mode) __attribute__ ((weak, alias("__attachInterrupt")));
-extern void attachInterruptArg(uint8_t pin, voidFuncPtrArg handler, void* arg, int mode) __attribute__((weak, alias("__attachInterruptArg")));
-extern void detachInterrupt(uint8_t pin) __attribute__ ((weak, alias("__detachInterrupt")));
-
-};
-
-#endif // ESP8266
From 5e03ddc8e8b0995cf33b20aa96f94ed66c556fe8 Mon Sep 17 00:00:00 2001
From: Jason2866 <24528715+Jason2866@users.noreply.github.com>
Date: Sat, 5 Sep 2020 18:06:07 +0200
Subject: [PATCH 25/38] Delete core_esp8266_wiring_pwm.cpp
---
tasmota/core_esp8266_wiring_pwm.cpp | 96 -----------------------------
1 file changed, 96 deletions(-)
delete mode 100644 tasmota/core_esp8266_wiring_pwm.cpp
diff --git a/tasmota/core_esp8266_wiring_pwm.cpp b/tasmota/core_esp8266_wiring_pwm.cpp
deleted file mode 100644
index d879a0001..000000000
--- a/tasmota/core_esp8266_wiring_pwm.cpp
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- pwm.c - analogWrite implementation for esp8266
-
- Use the shared TIMER1 utilities to generate PWM signals
-
- Original Copyright (c) 2015 Hristo Gochkov. All rights reserved.
- This file is part of the esp8266 core for Arduino environment.
-
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 2.1 of the License, or (at your option) any later version.
-
- This library 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
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with this library; if not, write to the Free Software
- Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-*/
-
-#ifdef ESP8266
-
-#include
-#include "core_esp8266_waveform.h"
-
-extern "C" {
-
-static uint32_t analogMap = 0;
-static int32_t analogScale = 255; // Match upstream default, breaking change from 2.x.x
-static uint16_t analogFreq = 1000;
-
-extern void __analogWriteRange(uint32_t range) {
- if ((range >= 15) && (range <= 65535)) {
- analogScale = range;
- }
-}
-
-extern void __analogWriteResolution(int res) {
- if ((res >= 4) && (res <= 16)) {
- analogScale = (1 << res) - 1;
- }
-}
-
-extern void __analogWriteFreq(uint32_t freq) {
- if (freq < 40) {
- analogFreq = 40;
- } else if (freq > 60000) {
- analogFreq = 60000;
- } else {
- analogFreq = freq;
- }
-}
-
-
-extern void __analogWrite(uint8_t pin, int val) {
- if (pin > 16) {
- return;
- }
-
- uint32_t analogPeriod = microsecondsToClockCycles(1000000UL) / analogFreq;
- if (val < 0) {
- val = 0;
- } else if (val > analogScale) {
- val = analogScale;
- }
-
- // Per the Arduino docs at https://www.arduino.cc/reference/en/language/functions/analog-io/analogwrite/
- // val: the duty cycle: between 0 (always off) and 255 (always on).
- // So if val = 0 we have digitalWrite(LOW), if we have val==range we have digitalWrite(HIGH)
-
- if (analogMap & 1UL << pin) {
- analogMap &= ~(1 << pin);
- }
- else {
- pinMode(pin, OUTPUT);
- }
- uint32_t high = (analogPeriod * val) / analogScale;
- uint32_t low = analogPeriod - high;
- // Find the first GPIO being generated by checking GCC's find-first-set (returns 1 + the bit of the first 1 in an int32_t)
- int phaseReference = __builtin_ffs(analogMap) - 1;
- if (startWaveformClockCycles(pin, high, low, 0, phaseReference, 0, true)) {
- analogMap |= (1 << pin);
- }
-}
-
-extern void analogWrite(uint8_t pin, int val) __attribute__((weak, alias("__analogWrite")));
-extern void analogWriteFreq(uint32_t freq) __attribute__((weak, alias("__analogWriteFreq")));
-extern void analogWriteRange(uint32_t range) __attribute__((weak, alias("__analogWriteRange")));
-extern void analogWriteResolution(int res) __attribute__((weak, alias("__analogWriteResolution")));
-
-};
-
-#endif // ESP8266
From be12a7e0d0921a14893294c09701736cf82a768a Mon Sep 17 00:00:00 2001
From: bovirus <1262554+bovirus@users.noreply.github.com>
Date: Sat, 5 Sep 2020 21:52:43 +0200
Subject: [PATCH 26/38] Update Italian language
---
tasmota/language/it_IT.h | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/tasmota/language/it_IT.h b/tasmota/language/it_IT.h
index bca4ea339..70360f15c 100644
--- a/tasmota/language/it_IT.h
+++ b/tasmota/language/it_IT.h
@@ -1,7 +1,7 @@
/*
it-IT.h - localization for Italian - Italy for Tasmota
- Copyright (C) 2020 Gennaro Tortone - some mods by Antonio Fragola - Updated by bovirus - rev. 02.09.2020
+ Copyright (C) 2020 Gennaro Tortone - some mods by Antonio Fragola - Updated by bovirus - rev. 05.09.2020
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
@@ -823,8 +823,8 @@
#define D_AS3935_NOISE "rilevato rumore"
#define D_AS3935_DISTDET "rilevato disturbatore"
#define D_AS3935_INTNOEV "Interrupt senza evento!"
-#define D_AS3935_FLICKER "IRQ flicker!"
-#define D_AS3935_POWEROFF "Power Off"
+#define D_AS3935_FLICKER "Flicker PIN IRQ!"
+#define D_AS3935_POWEROFF "Spegnimento"
#define D_AS3935_NOMESS "in ascolto..."
#define D_AS3935_ON "ON"
#define D_AS3935_OFF "OFF"
From abc7ba6cacb0f66a9b0c7d50c78557ef2cc9fab5 Mon Sep 17 00:00:00 2001
From: Jason2866 <24528715+Jason2866@users.noreply.github.com>
Date: Sat, 5 Sep 2020 22:42:15 +0200
Subject: [PATCH 27/38] Remove ESP32 move to main
---
platformio_override_sample.ini | 41 ----------------------------------
1 file changed, 41 deletions(-)
diff --git a/platformio_override_sample.ini b/platformio_override_sample.ini
index be5d5f24f..f8bf9ead1 100644
--- a/platformio_override_sample.ini
+++ b/platformio_override_sample.ini
@@ -167,44 +167,3 @@ build_type = debug
build_unflags = ${esp_defaults.build_unflags}
build_flags = ${esp82xx_defaults.build_flags}
-Wstack-usage=300
-
-
-; *** Experimental ESP32 Tasmota version ***
-; *** expect the unexpected. Many features not working!!! ***
-
-[common32]
-platform = espressif32@1.12.4
-platform_packages = tool-esptoolpy@1.20800.0
-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}
-build_unflags = ${esp_defaults.build_unflags}
- -Wpointer-arith
-monitor_speed = ${common.monitor_speed}
-upload_port = ${common.upload_port}
-upload_resetmethod = ${common.upload_resetmethod}
-upload_speed = 921600
-extra_scripts = ${common.extra_scripts}
-
-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
-
-lib_extra_dirs =
- libesp32
-
-lib_ignore =
- cc1101
From 7b83aeb5ff773b5e5e14065979e4f7509495c0ff Mon Sep 17 00:00:00 2001
From: Jason2866 <24528715+Jason2866@users.noreply.github.com>
Date: Sat, 5 Sep 2020 22:50:26 +0200
Subject: [PATCH 28/38] ESP32 settings
---
platformio.ini | 41 +++++++++++++++++++++++++++++++++++++++++
1 file changed, 41 insertions(+)
diff --git a/platformio.ini b/platformio.ini
index 86f72211a..ace3cc6db 100644
--- a/platformio.ini
+++ b/platformio.ini
@@ -14,6 +14,7 @@ build_dir = .pioenvs
workspace_dir = .pioenvs
build_cache_dir = .cache
extra_configs = platformio_tasmota_env.ini
+ platformio_tasmota_env32.ini
platformio_override.ini
; *** Build/upload environment
@@ -76,6 +77,46 @@ upload_resetmethod = nodemcu
upload_port = COM5
extra_scripts = ${scripts_defaults.extra_scripts}
+; *** BETA ESP32 Tasmota version ***
+; *** expect the unexpected. Some features not working!!! ***
+
+[common32]
+platform = espressif32@1.12.4
+platform_packages = tool-esptoolpy@1.20800.0
+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}
+build_unflags = ${esp_defaults.build_unflags}
+ -Wpointer-arith
+monitor_speed = ${common.monitor_speed}
+upload_port = ${common.upload_port}
+upload_resetmethod = ${common.upload_resetmethod}
+upload_speed = 921600
+extra_scripts = ${common.extra_scripts}
+
+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
+
+lib_extra_dirs =
+ libesp32
+
+lib_ignore =
+ cc1101
+
[scripts_defaults]
extra_scripts = pio/strip-floats.py
pio/name-firmware.py
From e966fbb2f1a51f9a3bd1d197a40b5ee526fbf303 Mon Sep 17 00:00:00 2001
From: Jason2866 <24528715+Jason2866@users.noreply.github.com>
Date: Sat, 5 Sep 2020 22:52:11 +0200
Subject: [PATCH 29/38] Remove esp32 env
---
platformio_override_sample.ini | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/platformio_override_sample.ini b/platformio_override_sample.ini
index f8bf9ead1..ddd74a60d 100644
--- a/platformio_override_sample.ini
+++ b/platformio_override_sample.ini
@@ -9,8 +9,7 @@
; http://docs.platformio.org/en/stable/projectconf.html
[platformio]
-extra_configs = platformio_tasmota_env32.ini
- platformio_tasmota_cenv.ini
+extra_configs = platformio_tasmota_cenv.ini
; *** Build/upload environment
default_envs =
From 640117844cb7d49e1ebd184f7214edab32637829 Mon Sep 17 00:00:00 2001
From: Jason2866 <24528715+Jason2866@users.noreply.github.com>
Date: Sat, 5 Sep 2020 23:00:09 +0200
Subject: [PATCH 30/38] Create platformio_tasmota32.ini
---
platformio_tasmota32.ini | 33 +++++++++++++++++++++++++++++++++
1 file changed, 33 insertions(+)
create mode 100644 platformio_tasmota32.ini
diff --git a/platformio_tasmota32.ini b/platformio_tasmota32.ini
new file mode 100644
index 000000000..0ddf8c692
--- /dev/null
+++ b/platformio_tasmota32.ini
@@ -0,0 +1,33 @@
+; *** BETA ESP32 Tasmota version ***
+; *** expect the unexpected. Some features not working!!! ***
+
+[common32]
+platform = espressif32@1.12.4
+platform_packages = tool-esptoolpy@1.20800.0
+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}
+build_unflags = ${esp_defaults.build_unflags}
+ -Wpointer-arith
+monitor_speed = ${common.monitor_speed}
+upload_port = ${common.upload_port}
+upload_resetmethod = ${common.upload_resetmethod}
+upload_speed = 921600
+extra_scripts = ${common.extra_scripts}
+
+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
From 8a5ab7febbc45af5b73336ffc52dc3efc1934b2e Mon Sep 17 00:00:00 2001
From: Jason2866 <24528715+Jason2866@users.noreply.github.com>
Date: Sat, 5 Sep 2020 23:03:19 +0200
Subject: [PATCH 31/38] Move ESP32 in extra platformio32 file
---
platformio.ini | 43 ++-----------------------------------------
1 file changed, 2 insertions(+), 41 deletions(-)
diff --git a/platformio.ini b/platformio.ini
index ace3cc6db..161c0686f 100644
--- a/platformio.ini
+++ b/platformio.ini
@@ -13,7 +13,8 @@ src_dir = tasmota
build_dir = .pioenvs
workspace_dir = .pioenvs
build_cache_dir = .cache
-extra_configs = platformio_tasmota_env.ini
+extra_configs = platformio_tasmota32.ini
+ platformio_tasmota_env.ini
platformio_tasmota_env32.ini
platformio_override.ini
@@ -76,46 +77,6 @@ upload_speed = 115200
upload_resetmethod = nodemcu
upload_port = COM5
extra_scripts = ${scripts_defaults.extra_scripts}
-
-; *** BETA ESP32 Tasmota version ***
-; *** expect the unexpected. Some features not working!!! ***
-
-[common32]
-platform = espressif32@1.12.4
-platform_packages = tool-esptoolpy@1.20800.0
-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}
-build_unflags = ${esp_defaults.build_unflags}
- -Wpointer-arith
-monitor_speed = ${common.monitor_speed}
-upload_port = ${common.upload_port}
-upload_resetmethod = ${common.upload_resetmethod}
-upload_speed = 921600
-extra_scripts = ${common.extra_scripts}
-
-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
-
-lib_extra_dirs =
- libesp32
-
-lib_ignore =
- cc1101
[scripts_defaults]
extra_scripts = pio/strip-floats.py
From e581e59e50c0fe5d601abe1b9cc0d12b91865c57 Mon Sep 17 00:00:00 2001
From: Jason2866 <24528715+Jason2866@users.noreply.github.com>
Date: Sat, 5 Sep 2020 23:15:17 +0200
Subject: [PATCH 32/38] Update platformio_tasmota32.ini
---
platformio_tasmota32.ini | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/platformio_tasmota32.ini b/platformio_tasmota32.ini
index 0ddf8c692..feca8c849 100644
--- a/platformio_tasmota32.ini
+++ b/platformio_tasmota32.ini
@@ -31,3 +31,9 @@ build_flags = ${esp_defaults.build_flags}
-D sint16_t=int16_t
-D memcpy_P=memcpy
-D memcmp_P=memcmp
+
+lib_extra_dirs =
+ libesp32
+
+lib_ignore =
+ cc1101
From e940c28be99a1d7e1b309cdacc8829e3d57c2442 Mon Sep 17 00:00:00 2001
From: Jason2866 <24528715+Jason2866@users.noreply.github.com>
Date: Sat, 5 Sep 2020 23:22:51 +0200
Subject: [PATCH 33/38] Remove spaces
---
platformio.ini | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/platformio.ini b/platformio.ini
index 161c0686f..ecf7d41ab 100644
--- a/platformio.ini
+++ b/platformio.ini
@@ -77,7 +77,7 @@ upload_speed = 115200
upload_resetmethod = nodemcu
upload_port = COM5
extra_scripts = ${scripts_defaults.extra_scripts}
-
+
[scripts_defaults]
extra_scripts = pio/strip-floats.py
pio/name-firmware.py
From 201bad3eb0744006886fa3407427008d6108da30 Mon Sep 17 00:00:00 2001
From: Theo Arends <11044339+arendst@users.noreply.github.com>
Date: Sun, 6 Sep 2020 18:41:39 +0200
Subject: [PATCH 34/38] Prep release 8.5
---
BUILDS.md | 10 +++++++++-
RELEASENOTES.md | 28 ++++++++++++++++++----------
tasmota/CHANGELOG.md | 5 +++--
tasmota/support_features.ino | 8 ++++++--
tools/decode-status.py | 16 +++++++++++-----
5 files changed, 47 insertions(+), 20 deletions(-)
diff --git a/BUILDS.md b/BUILDS.md
index 48c1ce19c..023c8ebec 100644
--- a/BUILDS.md
+++ b/BUILDS.md
@@ -10,6 +10,7 @@
| USE_MQTT_TLS_CA_CERT | - | - | - | - | - | - | - |
| USE_MQTT_AWS_IOT | - | - | - | - | - | - | - |
| USE_4K_RSA | - | - | - | - | - | - | - |
+| USE_TELEGRAM | - | - | - | - | - | - | - |
| USE_KNX | - | - | - | x | - | - | - |
| USE_WEBSERVER | x | x | x | x | x | x | x |
| USE_JAVASCRIPT_ES6 | - | - | - | - | - | - | - |
@@ -69,7 +70,9 @@
| USE_DDSU666 | - | - | - | - | x | - | - |
| USE_SOLAX_X1 | - | - | - | - | - | - | - |
| USE_LE01MR | - | - | - | - | - | - | - |
+| USE_BL0940 | - | x | x | x | x | - | - |
| USE_TELEINFO | - | - | - | - | - | - | - |
+| USE_IEM3000 | - | - | - | - | - | - | - |
| | | | | | | | |
| USE_ADC_VCC | x | x | - | - | - | x | - |
| USE_COUNTER | - | - | x | x | x | - | x |
@@ -126,6 +129,7 @@
| USE_VEML6075 | - | - | - | - | - | - | - |
| USE_VEML7700 | - | - | - | - | - | - | - |
| USE_MCP9808 | - | - | - | - | - | - | - |
+| USE_HP303B | - | - | - | - | - | - | - |
| | | | | | | | |
| Feature or Sensor | minimal | lite | tasmota | knx | sensors | ir | display | Remarks
| USE_SPI | - | - | - | - | - | - | x |
@@ -143,8 +147,9 @@
| USE_GPS | - | - | - | - | - | - | - |
| USE_HM10 | - | - | - | - | x | - | - |
| USE_HRXL | - | - | - | - | x | - | - |
-| USE_TASMOTA_SLAVE | - | - | - | - | - | - | - |
+| USE_TASMOTA_CLIENT | - | - | - | - | - | - | - |
| USE_OPENTHERM | - | - | - | - | - | - | - |
+| USE_TCP_BRIDGE | - | - | - | - | - | - | - | zbbridge
| | | | | | | | |
| USE_NRF24 | - | - | - | - | - | - | - |
| USE_MIBLE | - | - | - | - | - | - | - |
@@ -157,6 +162,7 @@
| USE_IR_REMOTE_FULL | - | - | - | - | - | x | - | Enable ALL protocols
| | | | | | | | |
| USE_SR04 | - | - | - | - | x | - | - |
+| USE_DYP | - | - | - | - | - | - | - |
| USE_TM1638 | - | - | - | - | x | - | - |
| USE_HX711 | - | - | - | - | x | - | - |
| USE_TX2x_WIND_SENSOR | - | - | - | - | - | - | - |
@@ -186,3 +192,5 @@
| USE_MI_ESP32 | - | - | - | - | - | - | - | - |
| USE_WEBCAM | - | - | - | - | - | - | - | x |
| USE_ETHERNET | - | - | - | - | - | - | - | - |
+| USE_I2S_AUDIO | - | - | - | - | - | - | - | - |
+| USE_TTGO_WATCH | - | - | - | - | - | - | - | - |
diff --git a/RELEASENOTES.md b/RELEASENOTES.md
index 76c63cbca..3703517fc 100644
--- a/RELEASENOTES.md
+++ b/RELEASENOTES.md
@@ -21,7 +21,7 @@ While fallback or downgrading is common practice it was never supported due to S
## Supported Core versions
-This release will be supported from ESP8266/Arduino library Core version **2.7.2.1** due to reported security and stability issues on previous Core version. This will also support gzipped binaries.
+This release will be supported from ESP8266/Arduino library Core version **2.7.4.1** due to reported security and stability issues on previous Core version. This will also support gzipped binaries.
Support of Core versions before 2.7.1 has been removed.
@@ -35,7 +35,7 @@ For initial configuration this release supports Webserver based **WifiManager**
## Provided Binary Downloads
-The following binary downloads have been compiled with ESP8266/Arduino library core version **2.7.2.1**.
+The following binary downloads have been compiled with ESP8266/Arduino library core version **2.7.4.1**.
- **tasmota.bin** = The Tasmota version with most drivers. **RECOMMENDED RELEASE BINARY**
- **tasmota-BG.bin** to **tasmota-TW.bin** = The Tasmota version in different languages.
@@ -55,19 +55,27 @@ The following binary downloads have been compiled with ESP8266/Arduino library c
### Version 8.4.0.3
-- Remove support for 1-step upgrade from versions before 6.6.0.11 to versions after 8.4.0.1
-- Change references from http://thehackbox.org to http://ota.tasmota.com
-- Change White blend mode moved to using ``SetOption 105`` instead of ``RGBWWTable``
+- Remove support for direct upgrade from versions before 6.6.0.11 to versions after 8.4.0.1
+- Change references from http://thehackbox.org/tasmota/ to http://ota.tasmota.com/tasmota/
+- Change triple-mode TLS via configuration in a single firmware (TLS AWS IoT, Letsencrypt and No-TLS)
+- Change White blend mode to using command ``SetOption 105`` instead of ``RGBWWTable``
- Fix ESP32 PWM range
- Fix display power control (#9114)
-- Add command ``SetOption108 0/1`` to enable Teleinfo telemetry into Tasmota Energy MQTT (0) or Teleinfo only (1) - Add Zigbee better support for IKEA Motion Sensor
+- Add command ``SetOption102 0/1`` to set Baud rate for Teleinfo communication (0 = 1200 or 1 = 9600)
+- Add command ``SetOption103 0/1`` to set TLS mode when TLS is selected
+- Add command ``SetOption104 1`` to disable all MQTT retained messages
+- Add command ``SetOption106 1`` to create a virtual White ColorTemp for RGBW lights
+- Add command ``SetOption107 0/1`` to select virtual White as (0) Warm or (1) Cold
+- Add command ``SetOption108 0/1`` to enable Teleinfo telemetry into Tasmota Energy MQTT (0) or Teleinfo only (1)
- Add command ``SetOption109 1`` to force gen1 Alexa mode, for Echo Dot 2nd gen devices only
- Add command ``Restart 2`` to halt system. Needs hardware reset or power cycle to restart (#9046)
-- Add ESP32 Analog input support for GPIO32 to GPIO39
+- Add command ``PowerDelta1`` to ``PowerDelta3`` to trigger on up to three phases (#9134)
- Add Zigbee options to ``ZbSend`` ``Config`` and ``ReadCondig``
-- Add Zigbee web gui widget for Temp/Humidity/Pressure sensors
+- Add Zigbee better support for IKEA Motion Sensor
+- Add Zigbee web gui widget for Battery and Temp/Humidity/Pressure sensors
- Add Zigbee web ui for power metering plugs
-- Add better config corruption recovery (#9046)
+- Add better configuration corruption recovery (#9046)
- Add virtual CT for 4 channels lights, emulating a 5th channel
- Add support for DYP ME007 ultrasonic distance sensor by Janusz Kostorz (#9113)
-- Add command ``PowerDelta1`` to ``PowerDelta3`` to trigger on up to three phases (#9134)
+- Add ESP32 Analog input support for GPIO32 to GPIO39
+- Add experimental support for ESP32 TTGO Watch and I2S Audio by Gerhard Mutz
diff --git a/tasmota/CHANGELOG.md b/tasmota/CHANGELOG.md
index 4f7b20031..e5967a9a1 100644
--- a/tasmota/CHANGELOG.md
+++ b/tasmota/CHANGELOG.md
@@ -2,15 +2,16 @@
### 8.4.0.3 20200823
-- Change references from http://thehackbox.org to http://ota.tasmota.com
+- Change references from http://thehackbox.org/tasmota/ to http://ota.tasmota.com/tasmota/
- Add command ``PowerDelta1`` to ``PowerDelta3`` to trigger on up to three phases (#9134)
- Add Zigbee web ui widget for Lights
- Add ``SetOption109 1`` to force gen1 Alexa mode, for Echo Dot 2nd gen devices only
- Add Zigbee web ui for power metering plugs
+- Add experimental support for ESP32 TTGO Watch and I2S Audio by Gerhard Mutz
### 8.4.0.2 20200813
-- Remove support for 1-step upgrade from versions before 6.6.0.11 to versions after 8.4.0.1
+- Remove support for direct upgrade from versions before 6.6.0.11 to versions after 8.4.0.1
- Change White blend mode moved to using ``SetOption 105`` instead of ``RGBWWTable``
- Fix display power control (#9114)
- Add command ``SetOption108 0/1`` to enable Teleinfo telemetry into Tasmota Energy MQTT (0) or Teleinfo only (1) - Add better config corruption recovery (#9046)
diff --git a/tasmota/support_features.ino b/tasmota/support_features.ino
index 8707650c0..d7f8328d3 100644
--- a/tasmota/support_features.ino
+++ b/tasmota/support_features.ino
@@ -598,7 +598,9 @@ void GetFeatures(void)
#ifdef USE_DYP
feature6 |= 0x00400000; // xsns_76_dyp.ino
#endif
-// feature6 |= 0x00800000;
+#ifdef USE_I2S_AUDIO
+ feature6 |= 0x00800000; // xdrv_42_i2s_audio.ino
+#endif
// feature6 |= 0x01000000;
// feature6 |= 0x02000000;
@@ -606,7 +608,9 @@ void GetFeatures(void)
// feature6 |= 0x08000000;
// feature6 |= 0x10000000;
-// feature6 |= 0x20000000;
+#if defined(ESP32) && defined(USE_TTGO_WATCH)
+ feature6 |= 0x20000000; // xdrv_83_esp32watch.ino
+#endif
#if defined(ESP32) && defined(USE_ETHERNET)
feature6 |= 0x40000000; // xdrv_82_ethernet.ino
#endif
diff --git a/tools/decode-status.py b/tools/decode-status.py
index 876c9eb91..26f7fe900 100755
--- a/tools/decode-status.py
+++ b/tools/decode-status.py
@@ -155,8 +155,14 @@ a_setoption = [[
"Enable zerocross dimmer on PWM DIMMER",
"Remove ZbReceived form JSON message",
"Add the source endpoint as suffix to attributes",
- "","","","",
- "","","","",
+ "Baud rate for Teleinfo communication (0 = 1200 or 1 = 9600)",
+ "TLS mode",
+ "Disable all MQTT retained messages",
+ "Enable White blend mode",
+ "Create a virtual White ColorTemp for RGBW lights",
+ "Select virtual White as (0) Warm or (1) Cold",
+ "Enable Teleinfo telemetry into Tasmota Energy MQTT (0) or Teleinfo only (1)",
+ "Force gen1 Alexa mode",
"","","",""
],[
"","","","",
@@ -220,9 +226,9 @@ a_features = [[
"USE_WINDMETER","USE_OPENTHERM","USE_THERMOSTAT","USE_VEML6075",
"USE_VEML7700","USE_MCP9808","USE_BL0940","USE_TELEGRAM",
"USE_HP303B","USE_TCP_BRIDGE","USE_TELEINFO","USE_LMT01",
- "USE_PROMETHEUS","USE_IEM3000","USE_DYP","",
+ "USE_PROMETHEUS","USE_IEM3000","USE_DYP","USE_I2S_AUDIO",
"","","","",
- "","","USE_ETHERNET","USE_WEBCAM"
+ "","USE_TTGO_WATCH","USE_ETHERNET","USE_WEBCAM"
],[
"","","","",
"","","","",
@@ -259,7 +265,7 @@ else:
obj = json.load(fp)
def StartDecode():
- print ("\n*** decode-status.py v20200817 by Theo Arends and Jacek Ziolkowski ***")
+ print ("\n*** decode-status.py v20200906 by Theo Arends and Jacek Ziolkowski ***")
# print("Decoding\n{}".format(obj))
From bac7c6210f04cf112f54c440226fe0f38987d8db Mon Sep 17 00:00:00 2001
From: Federico Leoni
Date: Sun, 6 Sep 2020 14:37:27 -0300
Subject: [PATCH 35/38] Bugfix and Prep for new Discovery
Correct wrong unsubscribe for HAss lwt, removed the limitation for `Scheme 0` because is not needed anymore.
---
tasmota/xdrv_12_home_assistant.ino | 8 +++-----
1 file changed, 3 insertions(+), 5 deletions(-)
diff --git a/tasmota/xdrv_12_home_assistant.ino b/tasmota/xdrv_12_home_assistant.ino
index 957c9bb29..ef9d25be9 100644
--- a/tasmota/xdrv_12_home_assistant.ino
+++ b/tasmota/xdrv_12_home_assistant.ino
@@ -849,7 +849,7 @@ void HAssDiscovery(void)
Settings.flag3.hass_tele_on_power = 1; // SetOption59 - Send tele/%topic%/STATE in addition to stat/%topic%/RESULT - send tele/STATE message as stat/RESULT
// the purpose of that is so that if HA is restarted, state in HA will be correct within one teleperiod otherwise state
// will not be correct until the device state is changed this is why in the patterns for switch and light, we tell HA to trigger on STATE, not RESULT.
- Settings.light_scheme = 0; // To just control color it needs to be Scheme 0
+ //Settings.light_scheme = 0; // To just control color it needs to be Scheme 0 (on hold due to new light configuration)
}
if (Settings.flag.hass_discovery || (1 == hass_mode))
@@ -940,7 +940,7 @@ void HassLwtSubscribe(bool hasslwt)
{
char htopic[TOPSZ];
snprintf_P(htopic, sizeof(htopic), PSTR(HOME_ASSISTANT_LWT_TOPIC));
- if (hasslwt) {
+ if (hasslwt && Settings.flag.hass_discovery) {
MqttSubscribe(htopic);
} else { MqttUnsubscribe(htopic); }
}
@@ -984,9 +984,7 @@ bool Xdrv12(uint8_t function)
hass_mode = 0; // Discovery only if Settings.flag.hass_discovery is set
hass_init_step = 2; // Delayed discovery
break;
- // if (!Settings.flag.hass_discovery) {
- // AddLog_P2(LOG_LEVEL_INFO, PSTR("MQT: homeassistant/49A3BC/Discovery = {\"dev\":{\"ids\":[\"49A3BC\"]},\"cmd_t\":\"cmnd/test1/\",\"Discovery\":0}"));
- // }
+
case FUNC_MQTT_SUBSCRIBE:
HassLwtSubscribe(hasslwt);
break;
From 8d49a4b037a974abea611a2cce03ba18a7f45a67 Mon Sep 17 00:00:00 2001
From: Stephan Hadinger
Date: Sun, 6 Sep 2020 20:51:20 +0200
Subject: [PATCH 36/38] Zigbee fixes
---
tasmota/xdrv_23_zigbee_1_headers.ino | 17 ++++-
tasmota/xdrv_23_zigbee_5_converters.ino | 62 +++++++++++++++++-
tasmota/xdrv_23_zigbee_6_commands.ino | 85 ++++--------------------
tasmota/xdrv_23_zigbee_8_parsers.ino | 15 ++++-
tasmota/xdrv_23_zigbee_9_serial.ino | 86 ++++++++++++-------------
tasmota/xdrv_23_zigbee_A_impl.ino | 50 ++++++++++++--
6 files changed, 187 insertions(+), 128 deletions(-)
diff --git a/tasmota/xdrv_23_zigbee_1_headers.ino b/tasmota/xdrv_23_zigbee_1_headers.ino
index 046450927..bb7555757 100644
--- a/tasmota/xdrv_23_zigbee_1_headers.ino
+++ b/tasmota/xdrv_23_zigbee_1_headers.ino
@@ -21,7 +21,22 @@
// contains some definitions for functions used before their declarations
-void ZigbeeZCLSend_Raw(uint16_t dtsAddr, uint16_t groupaddr, uint16_t clusterId, uint8_t endpoint, uint8_t cmdId, bool clusterSpecific, const uint8_t *msg, size_t len, bool needResponse, uint8_t transacId);
+class ZigbeeZCLSendMessage {
+public:
+ uint16_t shortaddr;
+ uint16_t groupaddr;
+ uint16_t clusterId;
+ uint8_t endpoint;
+ uint8_t cmdId;
+ uint16_t manuf;
+ bool clusterSpecific;
+ bool needResponse;
+ uint8_t transacId; // ZCL transaction number
+ const uint8_t *msg;
+ size_t len;
+};
+
+void ZigbeeZCLSend_Raw(const ZigbeeZCLSendMessage &zcl);
// get the result as a string (const char*) and nullptr if there is no field or the string is empty
const char * getCaseInsensitiveConstCharNull(const JsonObject &json, const char *needle) {
diff --git a/tasmota/xdrv_23_zigbee_5_converters.ino b/tasmota/xdrv_23_zigbee_5_converters.ino
index 9c8d10cdf..1155a6f2e 100644
--- a/tasmota/xdrv_23_zigbee_5_converters.ino
+++ b/tasmota/xdrv_23_zigbee_5_converters.ino
@@ -1028,7 +1028,19 @@ void ZCLFrame::parseReportAttributes(Z_attribute_list& attr_list) {
SBuffer buf(2);
buf.add8(_cmd_id);
buf.add8(0x00); // Status = OK
- ZigbeeZCLSend_Raw(_srcaddr, 0x0000, 0x0000 /*cluster*/, _srcendpoint, ZCL_DEFAULT_RESPONSE, false /* not cluster specific */, _manuf_code, buf.getBuffer(), buf.len(), false /* noresponse */, _transact_seq);
+
+ ZigbeeZCLSend_Raw(ZigbeeZCLSendMessage({
+ _srcaddr,
+ 0x0000,
+ _cluster_id,
+ _srcendpoint,
+ ZCL_DEFAULT_RESPONSE,
+ _manuf_code,
+ false /* not cluster specific */,
+ false /* noresponse */,
+ _transact_seq, /* zcl transaction id */
+ buf.getBuffer(), buf.len()
+ }));
}
}
@@ -1078,6 +1090,48 @@ void ZCLFrame::generateCallBacks(Z_attribute_list& attr_list) {
}
}
+
+// A command has been sent to a device this device, or to a group
+// Set timers to read back values.
+// If it's a device address, also set a timer for reachability test
+void sendHueUpdate(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster, uint8_t endpoint = 0) {
+ int32_t z_cat = -1;
+ uint32_t wait_ms = 0;
+
+ switch (cluster) {
+ case 0x0006:
+ z_cat = Z_CAT_READ_0006;
+ wait_ms = 200; // wait 0.2 s
+ break;
+ case 0x0008:
+ z_cat = Z_CAT_READ_0008;
+ wait_ms = 1050; // wait 1.0 s
+ break;
+ case 0x0102:
+ z_cat = Z_CAT_READ_0102;
+ wait_ms = 10000; // wait 10.0 s
+ break;
+ case 0x0300:
+ z_cat = Z_CAT_READ_0300;
+ wait_ms = 1050; // wait 1.0 s
+ break;
+ default:
+ break;
+ }
+ if (z_cat >= 0) {
+ if ((BAD_SHORTADDR != shortaddr) && (0 == endpoint)) {
+ endpoint = zigbee_devices.findFirstEndpoint(shortaddr);
+ }
+ if ((BAD_SHORTADDR == shortaddr) || (endpoint)) { // send if group address or endpoint is known
+ zigbee_devices.setTimer(shortaddr, groupaddr, wait_ms, cluster, endpoint, z_cat, 0 /* value */, &Z_ReadAttrCallback);
+ if (BAD_SHORTADDR != shortaddr) { // reachability test is not possible for group addresses, since we don't know the list of devices in the group
+ zigbee_devices.setTimer(shortaddr, groupaddr, wait_ms + Z_CAT_REACHABILITY_TIMEOUT, cluster, endpoint, Z_CAT_REACHABILITY, 0 /* value */, &Z_Unreachable);
+ }
+
+ }
+ }
+}
+
// ZCL_READ_ATTRIBUTES
void ZCLFrame::parseReadAttributes(Z_attribute_list& attr_list) {
uint32_t i = 0;
@@ -1248,7 +1302,11 @@ void ZCLFrame::parseResponse(void) {
void ZCLFrame::parseClusterSpecificCommand(Z_attribute_list& attr_list) {
convertClusterSpecific(attr_list, _cluster_id, _cmd_id, _frame_control.b.direction, _srcaddr, _srcendpoint, _payload);
#ifndef USE_ZIGBEE_NO_READ_ATTRIBUTES // read attributes unless disabled
- sendHueUpdate(_srcaddr, _groupaddr, _cluster_id, _cmd_id, _frame_control.b.direction);
+ if (!_frame_control.b.direction) { // only handle server->client (i.e. device->coordinator)
+ if (_wasbroadcast) { // only update for broadcast messages since we don't see unicast from device to device and we wouldn't know the target
+ sendHueUpdate(BAD_SHORTADDR, _groupaddr, _cluster_id);
+ }
+ }
#endif
}
diff --git a/tasmota/xdrv_23_zigbee_6_commands.ino b/tasmota/xdrv_23_zigbee_6_commands.ino
index c533eb8ed..a32f637c4 100644
--- a/tasmota/xdrv_23_zigbee_6_commands.ino
+++ b/tasmota/xdrv_23_zigbee_6_commands.ino
@@ -174,7 +174,19 @@ int32_t Z_ReadAttrCallback(uint16_t shortaddr, uint16_t groupaddr, uint16_t clus
if (groupaddr) {
shortaddr = BAD_SHORTADDR; // if group address, don't send to device
}
- ZigbeeZCLSend_Raw(shortaddr, groupaddr, cluster, endpoint, ZCL_READ_ATTRIBUTES, false, 0, attrs, attrs_len, true /* we do want a response */, zigbee_devices.getNextSeqNumber(shortaddr));
+ uint8_t seq = zigbee_devices.getNextSeqNumber(shortaddr);
+ ZigbeeZCLSend_Raw(ZigbeeZCLSendMessage({
+ shortaddr,
+ groupaddr,
+ cluster /*cluster*/,
+ endpoint,
+ ZCL_READ_ATTRIBUTES,
+ 0, /* manuf */
+ false /* not cluster specific */,
+ true /* response */,
+ seq, /* zcl transaction id */
+ attrs, attrs_len
+ }));
}
return 0; // Fix GCC 10.1 warning
}
@@ -188,31 +200,6 @@ int32_t Z_Unreachable(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster,
return 0; // Fix GCC 10.1 warning
}
-// set a timer to read back the value in the future
-void zigbeeSetCommandTimer(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster, uint8_t endpoint) {
- uint32_t wait_ms = 0;
-
- switch (cluster) {
- case 0x0006: // for On/Off
- case 0x0009: // for Alamrs
- wait_ms = 200; // wait 0.2 s
- break;
- case 0x0008: // for Dimmer
- case 0x0300: // for Color
- wait_ms = 1050; // wait 1.0 s
- break;
- case 0x0102: // for Shutters
- wait_ms = 10000; // wait 10.0 s
- break;
- }
- if (wait_ms) {
- zigbee_devices.setTimer(shortaddr, groupaddr, wait_ms, cluster, endpoint, Z_CAT_NONE, 0 /* value */, &Z_ReadAttrCallback);
- if (BAD_SHORTADDR != shortaddr) { // reachability test is not possible for group addresses, since we don't know the list of devices in the group
- zigbee_devices.setTimer(shortaddr, groupaddr, wait_ms + Z_CAT_REACHABILITY_TIMEOUT, cluster, endpoint, Z_CAT_REACHABILITY, 0 /* value */, &Z_Unreachable);
- }
- }
-}
-
// returns true if char is 'x', 'y' or 'z'
inline bool isXYZ(char c) {
return (c >= 'x') && (c <= 'z');
@@ -280,52 +267,6 @@ void parseXYZ(const char *model, const SBuffer &payload, struct Z_XYZ_Var *xyz)
}
}
-// works on big endiand hex only
-// Returns if found:
-// - cluster number
-// - command number or 0xFF if command is part of the variable part
-// - the payload in the form of a HEX string with x/y/z variables
-void sendHueUpdate(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster, uint8_t cmd, bool direction) {
- if (direction) { return; } // no need to update if server->client
-
- int32_t z_cat = -1;
- uint32_t wait_ms = 0;
-
- switch (cluster) {
- case 0x0006:
- z_cat = Z_CAT_READ_0006;
- wait_ms = 200; // wait 0.2 s
- break;
- case 0x0008:
- z_cat = Z_CAT_READ_0008;
- wait_ms = 1050; // wait 1.0 s
- break;
- case 0x0102:
- z_cat = Z_CAT_READ_0102;
- wait_ms = 10000; // wait 10.0 s
- break;
- case 0x0300:
- z_cat = Z_CAT_READ_0300;
- wait_ms = 1050; // wait 1.0 s
- break;
- default:
- break;
- }
- if (z_cat >= 0) {
- uint8_t endpoint = 0;
- if (BAD_SHORTADDR != shortaddr) {
- endpoint = zigbee_devices.findFirstEndpoint(shortaddr);
- }
- if ((BAD_SHORTADDR == shortaddr) || (endpoint)) { // send if group address or endpoint is known
- zigbee_devices.setTimer(shortaddr, groupaddr, wait_ms, cluster, endpoint, z_cat, 0 /* value */, &Z_ReadAttrCallback);
- if (BAD_SHORTADDR != shortaddr) { // reachability test is not possible for group addresses, since we don't know the list of devices in the group
- zigbee_devices.setTimer(shortaddr, groupaddr, wait_ms + Z_CAT_REACHABILITY_TIMEOUT, cluster, endpoint, Z_CAT_REACHABILITY, 0 /* value */, &Z_Unreachable);
- }
-
- }
- }
-}
-
// Parse a cluster specific command, and try to convert into human readable
void convertClusterSpecific(class Z_attribute_list &attr_list, uint16_t cluster, uint8_t cmd, bool direction, uint16_t shortaddr, uint8_t srcendpoint, const SBuffer &payload) {
diff --git a/tasmota/xdrv_23_zigbee_8_parsers.ino b/tasmota/xdrv_23_zigbee_8_parsers.ino
index 1f5d33a8f..d800eaf71 100644
--- a/tasmota/xdrv_23_zigbee_8_parsers.ino
+++ b/tasmota/xdrv_23_zigbee_8_parsers.ino
@@ -969,9 +969,18 @@ void Z_SendAFInfoRequest(uint16_t shortaddr) {
uint8_t InfoReq[] = { 0x04, 0x00, 0x05, 0x00 };
- ZigbeeZCLSend_Raw(shortaddr, 0x0000 /*group*/, 0x0000 /*cluster*/, endpoint, ZCL_READ_ATTRIBUTES,
- false /*clusterSpecific*/, 0x0000 /*manuf*/,
- InfoReq, sizeof(InfoReq), true /*needResponse*/, transacid);
+ ZigbeeZCLSend_Raw(ZigbeeZCLSendMessage({
+ shortaddr,
+ 0x0000, /* group */
+ 0x0000 /*cluster*/,
+ endpoint,
+ ZCL_READ_ATTRIBUTES,
+ 0x0000, /* manuf */
+ false /* not cluster specific */,
+ true /* response */,
+ transacid, /* zcl transaction id */
+ InfoReq, sizeof(InfoReq)
+ }));
}
diff --git a/tasmota/xdrv_23_zigbee_9_serial.ino b/tasmota/xdrv_23_zigbee_9_serial.ino
index 3fdddfedb..f5056c58c 100644
--- a/tasmota/xdrv_23_zigbee_9_serial.ino
+++ b/tasmota/xdrv_23_zigbee_9_serial.ino
@@ -765,96 +765,96 @@ void CmndZbEZSPSend(void)
// - transacId: 8-bits, transation id of message (should be incremented at each message), used both for Zigbee message number and ZCL message number
// Returns: None
//
-void ZigbeeZCLSend_Raw(uint16_t shortaddr, uint16_t groupaddr, uint16_t clusterId, uint8_t endpoint, uint8_t cmdId, bool clusterSpecific, uint16_t manuf, const uint8_t *msg, size_t len, bool needResponse, uint8_t transacId) {
+void ZigbeeZCLSend_Raw(const ZigbeeZCLSendMessage &zcl) {
#ifdef USE_ZIGBEE_ZNP
- SBuffer buf(32+len);
+ SBuffer buf(32+zcl.len);
buf.add8(Z_SREQ | Z_AF); // 24
buf.add8(AF_DATA_REQUEST_EXT); // 02
- if (BAD_SHORTADDR == shortaddr) { // if no shortaddr we assume group address
+ if (BAD_SHORTADDR == zcl.shortaddr) { // if no shortaddr we assume group address
buf.add8(Z_Addr_Group); // 01
- buf.add64(groupaddr); // group address, only 2 LSB, upper 6 MSB are discarded
+ buf.add64(zcl.groupaddr); // group address, only 2 LSB, upper 6 MSB are discarded
buf.add8(0xFF); // dest endpoint is not used for group addresses
} else {
buf.add8(Z_Addr_ShortAddress); // 02
- buf.add64(shortaddr); // dest address, only 2 LSB, upper 6 MSB are discarded
- buf.add8(endpoint); // dest endpoint
+ buf.add64(zcl.shortaddr); // dest address, only 2 LSB, upper 6 MSB are discarded
+ buf.add8(zcl.endpoint); // dest endpoint
}
buf.add16(0x0000); // dest Pan ID, 0x0000 = intra-pan
buf.add8(0x01); // source endpoint
- buf.add16(clusterId);
- buf.add8(transacId); // transacId
+ buf.add16(zcl.clusterId);
+ buf.add8(zcl.transacId); // transacId
buf.add8(0x30); // 30 options
buf.add8(0x1E); // 1E radius
- buf.add16(3 + len + (manuf ? 2 : 0));
- buf.add8((needResponse ? 0x00 : 0x10) | (clusterSpecific ? 0x01 : 0x00) | (manuf ? 0x04 : 0x00)); // Frame Control Field
- if (manuf) {
- buf.add16(manuf); // add Manuf Id if not null
+ buf.add16(3 + zcl.len + (zcl.manuf ? 2 : 0));
+ buf.add8((zcl.needResponse ? 0x00 : 0x10) | (zcl.clusterSpecific ? 0x01 : 0x00) | (zcl.manuf ? 0x04 : 0x00)); // Frame Control Field
+ if (zcl.manuf) {
+ buf.add16(zcl.manuf); // add Manuf Id if not null
}
- buf.add8(transacId); // Transaction Sequence Number
- buf.add8(cmdId);
- if (len > 0) {
- buf.addBuffer(msg, len); // add the payload
+ buf.add8(zcl.transacId); // Transaction Sequence Number
+ buf.add8(zcl.cmdId);
+ if (zcl.len > 0) {
+ buf.addBuffer(zcl.msg, zcl.len); // add the payload
}
ZigbeeZNPSend(buf.getBuffer(), buf.len());
#endif // USE_ZIGBEE_ZNP
#ifdef USE_ZIGBEE_EZSP
- SBuffer buf(32+len);
+ SBuffer buf(32+zcl.len);
- if (BAD_SHORTADDR != shortaddr) {
+ if (BAD_SHORTADDR != zcl.shortaddr) {
// send unicast message to an address
buf.add16(EZSP_sendUnicast); // 3400
buf.add8(EMBER_OUTGOING_DIRECT); // 00
- buf.add16(shortaddr); // dest addr
+ buf.add16(zcl.shortaddr); // dest addr
// ApsFrame
buf.add16(Z_PROF_HA); // Home Automation profile
- buf.add16(clusterId); // cluster
+ buf.add16(zcl.clusterId); // cluster
buf.add8(0x01); // srcEp
- buf.add8(endpoint); // dstEp
+ buf.add8(zcl.endpoint); // dstEp
buf.add16(EMBER_APS_OPTION_ENABLE_ROUTE_DISCOVERY | EMBER_APS_OPTION_RETRY); // APS frame
- buf.add16(groupaddr); // groupId
- buf.add8(transacId);
+ buf.add16(zcl.groupaddr); // groupId
+ buf.add8(zcl.transacId);
// end of ApsFrame
buf.add8(0x01); // tag TODO
- buf.add8(3 + len + (manuf ? 2 : 0));
- buf.add8((needResponse ? 0x00 : 0x10) | (clusterSpecific ? 0x01 : 0x00) | (manuf ? 0x04 : 0x00)); // Frame Control Field
- if (manuf) {
- buf.add16(manuf); // add Manuf Id if not null
+ buf.add8(3 + zcl.len + (zcl.manuf ? 2 : 0));
+ buf.add8((zcl.needResponse ? 0x00 : 0x10) | (zcl.clusterSpecific ? 0x01 : 0x00) | (zcl.manuf ? 0x04 : 0x00)); // Frame Control Field
+ if (zcl.manuf) {
+ buf.add16(zcl.manuf); // add Manuf Id if not null
}
- buf.add8(transacId); // Transaction Sequance Number
- buf.add8(cmdId);
- if (len > 0) {
- buf.addBuffer(msg, len); // add the payload
+ buf.add8(zcl.transacId); // Transaction Sequance Number
+ buf.add8(zcl.cmdId);
+ if (zcl.len > 0) {
+ buf.addBuffer(zcl.msg, zcl.len); // add the payload
}
} else {
// send broadcast group address, aka groupcast
buf.add16(EZSP_sendMulticast); // 3800
// ApsFrame
buf.add16(Z_PROF_HA); // Home Automation profile
- buf.add16(clusterId); // cluster
+ buf.add16(zcl.clusterId); // cluster
buf.add8(0x01); // srcEp
- buf.add8(endpoint); // broadcast endpoint for groupcast
+ buf.add8(zcl.endpoint); // broadcast endpoint for groupcast
buf.add16(EMBER_APS_OPTION_ENABLE_ROUTE_DISCOVERY | EMBER_APS_OPTION_RETRY); // APS frame
- buf.add16(groupaddr); // groupId
- buf.add8(transacId);
+ buf.add16(zcl.groupaddr); // groupId
+ buf.add8(zcl.transacId);
// end of ApsFrame
buf.add8(0); // hops, 0x00 = EMBER_MAX_HOPS
buf.add8(7); // nonMemberRadius, 7 = infinite
buf.add8(0x01); // tag TODO
- buf.add8(3 + len + (manuf ? 2 : 0));
- buf.add8((needResponse ? 0x00 : 0x10) | (clusterSpecific ? 0x01 : 0x00) | (manuf ? 0x04 : 0x00)); // Frame Control Field
- if (manuf) {
- buf.add16(manuf); // add Manuf Id if not null
+ buf.add8(3 + zcl.len + (zcl.manuf ? 2 : 0));
+ buf.add8((zcl.needResponse ? 0x00 : 0x10) | (zcl.clusterSpecific ? 0x01 : 0x00) | (zcl.manuf ? 0x04 : 0x00)); // Frame Control Field
+ if (zcl.manuf) {
+ buf.add16(zcl.manuf); // add Manuf Id if not null
}
- buf.add8(transacId); // Transaction Sequance Number
- buf.add8(cmdId);
- if (len > 0) {
- buf.addBuffer(msg, len); // add the payload
+ buf.add8(zcl.transacId); // Transaction Sequance Number
+ buf.add8(zcl.cmdId);
+ if (zcl.len > 0) {
+ buf.addBuffer(zcl.msg, zcl.len); // add the payload
}
}
diff --git a/tasmota/xdrv_23_zigbee_A_impl.ino b/tasmota/xdrv_23_zigbee_A_impl.ino
index 4dd7e99ad..fff151af6 100644
--- a/tasmota/xdrv_23_zigbee_A_impl.ino
+++ b/tasmota/xdrv_23_zigbee_A_impl.ino
@@ -139,7 +139,6 @@ void CmndZbReset(void) {
// High-level function
// Send a command specified as an HEX string for the workload.
// The target endpoint is computed if zero, i.e. sent to the first known endpoint of the device.
-// If cluster-specific, a timer may be set calling `zigbeeSetCommandTimer()`, for ex to coalesce attributes or Aqara presence sensor
//
// Inputs:
// - shortaddr: 16-bits short address, or 0x0000 if group address
@@ -176,11 +175,24 @@ void zigbeeZCLSendStr(uint16_t shortaddr, uint16_t groupaddr, uint8_t endpoint,
}
// everything is good, we can send the command
- ZigbeeZCLSend_Raw(shortaddr, groupaddr, cluster, endpoint, cmd, clusterSpecific, manuf, buf.getBuffer(), buf.len(), true, zigbee_devices.getNextSeqNumber(shortaddr));
+
+ uint8_t seq = zigbee_devices.getNextSeqNumber(shortaddr);
+ ZigbeeZCLSend_Raw(ZigbeeZCLSendMessage({
+ shortaddr,
+ groupaddr,
+ cluster /*cluster*/,
+ endpoint,
+ cmd,
+ manuf, /* manuf */
+ clusterSpecific /* not cluster specific */,
+ true /* response */,
+ seq, /* zcl transaction id */
+ buf.getBuffer(), buf.len()
+ }));
// now set the timer, if any, to read back the state later
if (clusterSpecific) {
#ifndef USE_ZIGBEE_NO_READ_ATTRIBUTES // read back attribute value unless it is disabled
- zigbeeSetCommandTimer(shortaddr, groupaddr, cluster, endpoint);
+ sendHueUpdate(shortaddr, groupaddr, cluster, endpoint);
#endif
}
}
@@ -202,7 +214,7 @@ void ZbApplyMultiplier(double &val_d, int8_t multiplier) {
// Parse "Report", "Write", "Response" or "Condig" attribute
// Operation is one of: ZCL_REPORT_ATTRIBUTES (0x0A), ZCL_WRITE_ATTRIBUTES (0x02) or ZCL_READ_ATTRIBUTES_RESPONSE (0x01)
-void ZbSendReportWrite(const JsonObject &val_pubwrite, uint16_t device, uint16_t groupaddr, uint16_t cluster, uint8_t endpoint, uint16_t manuf, uint32_t operation) {
+void ZbSendReportWrite(const JsonObject &val_pubwrite, uint16_t device, uint16_t groupaddr, uint16_t cluster, uint8_t endpoint, uint16_t manuf, uint8_t operation) {
SBuffer buf(200); // buffer to store the binary output of attibutes
if (nullptr == XdrvMailbox.command) {
@@ -376,7 +388,19 @@ void ZbSendReportWrite(const JsonObject &val_pubwrite, uint16_t device, uint16_t
}
// all good, send the packet
- ZigbeeZCLSend_Raw(device, groupaddr, cluster, endpoint, operation, false /* not cluster specific */, manuf, buf.getBuffer(), buf.len(), false /* noresponse */, zigbee_devices.getNextSeqNumber(device));
+ uint8_t seq = zigbee_devices.getNextSeqNumber(device);
+ ZigbeeZCLSend_Raw(ZigbeeZCLSendMessage({
+ device,
+ groupaddr,
+ cluster /*cluster*/,
+ endpoint,
+ operation,
+ manuf, /* manuf */
+ false /* not cluster specific */,
+ false /* no response */,
+ seq, /* zcl transaction id */
+ buf.getBuffer(), buf.len()
+ }));
ResponseCmndDone();
}
@@ -510,7 +534,7 @@ void ZbSendSend(const JsonVariant &val_cmd, uint16_t device, uint16_t groupaddr,
// Parse the "Send" attribute and send the command
-void ZbSendRead(const JsonVariant &val_attr, uint16_t device, uint16_t groupaddr, uint16_t cluster, uint8_t endpoint, uint16_t manuf, uint32_t operation) {
+void ZbSendRead(const JsonVariant &val_attr, uint16_t device, uint16_t groupaddr, uint16_t cluster, uint8_t endpoint, uint16_t manuf, uint8_t operation) {
// ZbSend {"Device":"0xF289","Cluster":0,"Endpoint":3,"Read":5}
// ZbSend {"Device":"0xF289","Cluster":"0x0000","Endpoint":"0x0003","Read":"0x0005"}
// ZbSend {"Device":"0xF289","Cluster":0,"Endpoint":3,"Read":[5,6,7,4]}
@@ -602,7 +626,19 @@ void ZbSendRead(const JsonVariant &val_attr, uint16_t device, uint16_t groupaddr
}
if (attrs_len > 0) {
- ZigbeeZCLSend_Raw(device, groupaddr, cluster, endpoint, operation, false, manuf, attrs, attrs_len, true /* we do want a response */, zigbee_devices.getNextSeqNumber(device));
+ uint8_t seq = zigbee_devices.getNextSeqNumber(device);
+ ZigbeeZCLSend_Raw(ZigbeeZCLSendMessage({
+ device,
+ groupaddr,
+ cluster /*cluster*/,
+ endpoint,
+ operation,
+ manuf, /* manuf */
+ false /* not cluster specific */,
+ true /* response */,
+ seq, /* zcl transaction id */
+ attrs, attrs_len
+ }));
ResponseCmndDone();
} else {
ResponseCmndChar_P(PSTR("Missing parameters"));
From 5176e3ab02ffab614e1fb1a52e55b06694ed1fe7 Mon Sep 17 00:00:00 2001
From: Theo Arends <11044339+arendst@users.noreply.github.com>
Date: Mon, 7 Sep 2020 14:54:31 +0200
Subject: [PATCH 37/38] Bump version to 8.5.0.1
---
RELEASENOTES.md | 27 +--------------------------
tasmota/CHANGELOG.md | 14 ++++++++++++++
tasmota/tasmota_version.h | 2 +-
3 files changed, 16 insertions(+), 27 deletions(-)
diff --git a/RELEASENOTES.md b/RELEASENOTES.md
index 3703517fc..8c95d47b5 100644
--- a/RELEASENOTES.md
+++ b/RELEASENOTES.md
@@ -53,29 +53,4 @@ The following binary downloads have been compiled with ESP8266/Arduino library c
## Changelog
-### Version 8.4.0.3
-
-- Remove support for direct upgrade from versions before 6.6.0.11 to versions after 8.4.0.1
-- Change references from http://thehackbox.org/tasmota/ to http://ota.tasmota.com/tasmota/
-- Change triple-mode TLS via configuration in a single firmware (TLS AWS IoT, Letsencrypt and No-TLS)
-- Change White blend mode to using command ``SetOption 105`` instead of ``RGBWWTable``
-- Fix ESP32 PWM range
-- Fix display power control (#9114)
-- Add command ``SetOption102 0/1`` to set Baud rate for Teleinfo communication (0 = 1200 or 1 = 9600)
-- Add command ``SetOption103 0/1`` to set TLS mode when TLS is selected
-- Add command ``SetOption104 1`` to disable all MQTT retained messages
-- Add command ``SetOption106 1`` to create a virtual White ColorTemp for RGBW lights
-- Add command ``SetOption107 0/1`` to select virtual White as (0) Warm or (1) Cold
-- Add command ``SetOption108 0/1`` to enable Teleinfo telemetry into Tasmota Energy MQTT (0) or Teleinfo only (1)
-- Add command ``SetOption109 1`` to force gen1 Alexa mode, for Echo Dot 2nd gen devices only
-- Add command ``Restart 2`` to halt system. Needs hardware reset or power cycle to restart (#9046)
-- Add command ``PowerDelta1`` to ``PowerDelta3`` to trigger on up to three phases (#9134)
-- Add Zigbee options to ``ZbSend`` ``Config`` and ``ReadCondig``
-- Add Zigbee better support for IKEA Motion Sensor
-- Add Zigbee web gui widget for Battery and Temp/Humidity/Pressure sensors
-- Add Zigbee web ui for power metering plugs
-- Add better configuration corruption recovery (#9046)
-- Add virtual CT for 4 channels lights, emulating a 5th channel
-- Add support for DYP ME007 ultrasonic distance sensor by Janusz Kostorz (#9113)
-- Add ESP32 Analog input support for GPIO32 to GPIO39
-- Add experimental support for ESP32 TTGO Watch and I2S Audio by Gerhard Mutz
+### Version 8.5.0.1
diff --git a/tasmota/CHANGELOG.md b/tasmota/CHANGELOG.md
index e5967a9a1..0f5b4ec47 100644
--- a/tasmota/CHANGELOG.md
+++ b/tasmota/CHANGELOG.md
@@ -1,5 +1,15 @@
+## Released
+
## Unreleased (development)
+### 8.5.0.1 20200907
+
+- New released
+
+### 8.5.0 20200907
+
+- Release Hannah
+
### 8.4.0.3 20200823
- Change references from http://thehackbox.org/tasmota/ to http://ota.tasmota.com/tasmota/
@@ -14,6 +24,10 @@
- Remove support for direct upgrade from versions before 6.6.0.11 to versions after 8.4.0.1
- Change White blend mode moved to using ``SetOption 105`` instead of ``RGBWWTable``
- Fix display power control (#9114)
+- Add command ``SetOption103 0/1`` to set TLS mode when TLS is selected
+- Add command ``SetOption104 1`` to disable all MQTT retained messages
+- Add command ``SetOption106 1`` to create a virtual White ColorTemp for RGBW lights
+- Add command ``SetOption107 0/1`` to select virtual White as (0) Warm or (1) Cold
- Add command ``SetOption108 0/1`` to enable Teleinfo telemetry into Tasmota Energy MQTT (0) or Teleinfo only (1) - Add better config corruption recovery (#9046)
- Add virtual CT for 4 channels lights, emulating a 5th channel
- Add support for DYP ME007 ultrasonic distance sensor by Janusz Kostorz (#9113)
diff --git a/tasmota/tasmota_version.h b/tasmota/tasmota_version.h
index ce7e073c2..3103534c2 100644
--- a/tasmota/tasmota_version.h
+++ b/tasmota/tasmota_version.h
@@ -20,7 +20,7 @@
#ifndef _TASMOTA_VERSION_H_
#define _TASMOTA_VERSION_H_
-const uint32_t VERSION = 0x08040003;
+const uint32_t VERSION = 0x08050001;
// Lowest compatible version
const uint32_t VERSION_COMPATIBLE = 0x07010006;
From 654f9de322e89ee6743244adff0a50905dfd0d9e Mon Sep 17 00:00:00 2001
From: Jason2866 <24528715+Jason2866@users.noreply.github.com>
Date: Mon, 7 Sep 2020 17:16:23 +0200
Subject: [PATCH 38/38] espressif32@2.0.0
---
platformio_tasmota32.ini | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/platformio_tasmota32.ini b/platformio_tasmota32.ini
index feca8c849..549f2e333 100644
--- a/platformio_tasmota32.ini
+++ b/platformio_tasmota32.ini
@@ -2,7 +2,7 @@
; *** expect the unexpected. Some features not working!!! ***
[common32]
-platform = espressif32@1.12.4
+platform = espressif32@2.0.0
platform_packages = tool-esptoolpy@1.20800.0
board = esp32dev
board_build.ldscript = esp32_out.ld