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 01/22] 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 02/22] 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 03/22] 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 04/22] 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 05/22] 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 06/22] 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 07/22] 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 08/22] 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 09/22] 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 10/22] 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 11/22] 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 12/22] 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 13/22] 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 14/22] 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 15/22] 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 16/22] 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 17/22] 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 18/22] 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 19/22] 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 20/22] 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 21/22] 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 22/22] 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))