From 1295370bf106b0ac42f2af800936073b9f9313fb Mon Sep 17 00:00:00 2001 From: Stephan Hadinger Date: Sat, 5 Sep 2020 14:44:31 +0200 Subject: [PATCH 01/55] 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 02/55] 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 03/55] 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 04/55] 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 05/55] 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 06/55] 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 07/55] 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 5c7e73f29b088e08874c458485136e5685369ee0 Mon Sep 17 00:00:00 2001 From: stefanbode Date: Sat, 5 Sep 2020 20:39:24 +0200 Subject: [PATCH 08/55] New major Version --- tasmota/xdrv_27_shutter.ino | 585 ++++++++++++++++++++++-------------- 1 file changed, 355 insertions(+), 230 deletions(-) diff --git a/tasmota/xdrv_27_shutter.ino b/tasmota/xdrv_27_shutter.ino index e5a5353f6..3a9b43d7e 100644 --- a/tasmota/xdrv_27_shutter.ino +++ b/tasmota/xdrv_27_shutter.ino @@ -23,29 +23,37 @@ \*********************************************************************************************/ #define XDRV_27 27 +#ifndef SHUTTER_STEPPER + #define SHUTTER_STEPPER +#endif #define D_SHUTTER "SHUTTER" const uint16_t MOTOR_STOP_TIME = 500; // in mS const uint8_t steps_per_second = 20; // FUNC_EVERY_50_MSECOND +const uint16_t pwm_max = 500; +const uint16_t pwm_min = 90; uint8_t calibrate_pos[6] = {0,30,50,70,90,100}; uint16_t messwerte[5] = {30,50,70,90,100}; uint16_t last_execute_step; +int32_t stop_position_delta = 20; -enum ShutterModes { SHT_OFF_OPEN__OFF_CLOSE, SHT_OFF_ON__OPEN_CLOSE, SHT_PULSE_OPEN__PULSE_CLOSE, SHT_OFF_ON__OPEN_CLOSE_STEPPER,}; +const uint8_t MAX_MODES = 7; +enum ShutterPositionMode {SHT_UNDEF, SHT_TIME, SHT_TIME_UP_DOWN, SHT_TIME_GARAGE, SHT_COUNTER, SHT_PWM_VALUE, SHT_PWM_TIME,}; +enum ShutterSwitchMode {SHT_SWITCH, SHT_PULSE,}; enum ShutterButtonStates { SHT_NOT_PRESSED, SHT_PRESSED_MULTI, SHT_PRESSED_HOLD, SHT_PRESSED_IMMEDIATE, SHT_PRESSED_EXT_HOLD, SHT_PRESSED_MULTI_SIMULTANEOUS, SHT_PRESSED_HOLD_SIMULTANEOUS, SHT_PRESSED_EXT_HOLD_SIMULTANEOUS,}; const char kShutterCommands[] PROGMEM = D_PRFX_SHUTTER "|" D_CMND_SHUTTER_OPEN "|" D_CMND_SHUTTER_CLOSE "|" D_CMND_SHUTTER_TOGGLE "|" D_CMND_SHUTTER_TOGGLEDIR "|" D_CMND_SHUTTER_STOP "|" D_CMND_SHUTTER_POSITION "|" - D_CMND_SHUTTER_OPENTIME "|" D_CMND_SHUTTER_CLOSETIME "|" D_CMND_SHUTTER_RELAY "|" + D_CMND_SHUTTER_OPENTIME "|" D_CMND_SHUTTER_CLOSETIME "|" D_CMND_SHUTTER_RELAY "|" D_CMND_SHUTTER_MODE "|" D_CMND_SHUTTER_SETHALFWAY "|" D_CMND_SHUTTER_SETCLOSE "|" D_CMND_SHUTTER_SETOPEN "|" D_CMND_SHUTTER_INVERT "|" D_CMND_SHUTTER_CLIBRATION "|" D_CMND_SHUTTER_MOTORDELAY "|" D_CMND_SHUTTER_FREQUENCY "|" D_CMND_SHUTTER_BUTTON "|" D_CMND_SHUTTER_LOCK "|" D_CMND_SHUTTER_ENABLEENDSTOPTIME "|" D_CMND_SHUTTER_INVERTWEBBUTTONS "|" D_CMND_SHUTTER_STOPOPEN "|" D_CMND_SHUTTER_STOPCLOSE "|" D_CMND_SHUTTER_STOPTOGGLE "|" D_CMND_SHUTTER_STOPTOGGLEDIR "|" D_CMND_SHUTTER_STOPPOSITION; void (* const ShutterCommand[])(void) PROGMEM = { &CmndShutterOpen, &CmndShutterClose, &CmndShutterToggle, &CmndShutterToggleDir, &CmndShutterStop, &CmndShutterPosition, - &CmndShutterOpenTime, &CmndShutterCloseTime, &CmndShutterRelay, + &CmndShutterOpenTime, &CmndShutterCloseTime, &CmndShutterRelay, &CmndShutterMode, &CmndShutterSetHalfway, &CmndShutterSetClose, &CmndShutterSetOpen, &CmndShutterInvert, &CmndShutterCalibration , &CmndShutterMotorDelay, &CmndShutterFrequency, &CmndShutterButton, &CmndShutterLock, &CmndShutterEnableEndStopTime, &CmndShutterInvertWebButtons, &CmndShutterStopOpen, &CmndShutterStopClose, &CmndShutterStopToggle, &CmndShutterStopToggleDir, &CmndShutterStopPosition}; @@ -71,40 +79,65 @@ struct SHUTTER { uint16_t close_velocity[MAX_SHUTTERS]; // in relation to open velocity. higher value = faster int8_t direction[MAX_SHUTTERS]; // 1 == UP , 0 == stop; -1 == down int8_t lastdirection[MAX_SHUTTERS]; // last direction (1 == UP , -1 == down) - uint8_t mode = 0; // operation mode definition. see enum type above SHT_OFF_OPEN__OFF_CLOSE, SHT_OFF_ON__OPEN_CLOSE, SHT_PULSE_OPEN__PULSE_CLOSE + uint8_t PositionMode = 0; // how to calculate actual position: SHT_TIME, SHT_COUNTER, SHT_PWM_VALUE, SHT_PWM_TIME + uint8_t SwitchMode = 0; // how to switch relays: SHT_SWITCH, SHT_PULSE int16_t motordelay[MAX_SHUTTERS]; // initial motorstarttime in 0.05sec. - int16_t pwm_frequency[MAX_SHUTTERS]; // frequency of PWN for stepper motors - uint16_t max_pwm_frequency = 1000; // maximum of PWM frequency for openig the shutter. depend on the motor and drivers - uint16_t max_close_pwm_frequency[MAX_SHUTTERS];// maximum of PWM frequency for closeing the shutter. depend on the motor and drivers + int16_t pwm_velocity[MAX_SHUTTERS]; // frequency of PWN for stepper motors or PWM duty cycle change for PWM servo + uint16_t pwm_value[MAX_SHUTTERS]; // dutyload of PWM 0..1023 on ESP8266 + uint16_t pwm_min[MAX_SHUTTERS]; // dutyload of PWM 0..1023 on ESP8266 + uint16_t pwm_max[MAX_SHUTTERS]; // dutyload of PWM 0..1023 on ESP8266 + uint16_t max_pwm_velocity = 1000; // maximum of PWM frequency for openig the shutter. depend on the motor and drivers + uint16_t max_close_pwm_velocity[MAX_SHUTTERS];// maximum of PWM frequency for closeing the shutter. depend on the motor and drivers uint8_t skip_relay_change; // avoid overrun at endstops int32_t accelerator[MAX_SHUTTERS]; // speed of ramp-up, ramp down of shutter uint8_t start_reported = 0; } Shutter; +#define SHT_DIV_ROUND(__A, __B) (((__A) + (__B)/2) / (__B)) + void ShutterLogPos(uint32_t i) { char stemp2[10]; dtostrfd((float)Shutter.time[i] / steps_per_second, 2, stemp2); - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Shutter%d Real %d, Start %d, Stop %d, Dir %d, Delay %d, Rtc %s [s], Freq %d"), - i+1, Shutter.real_position[i], Shutter.start_position[i], Shutter.target_position[i], Shutter.direction[i], Shutter.motordelay[i], stemp2, Shutter.pwm_frequency[i]); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Shutter%d Real %d, Start %d, Stop %d, Dir %d, Delay %d, Rtc %s [s], Freq %d, PWM %d"), + i+1, Shutter.real_position[i], Shutter.start_position[i], Shutter.target_position[i], Shutter.direction[i], Shutter.motordelay[i], stemp2, Shutter.pwm_velocity[i], Shutter.pwm_value[i]); +} + +void ExecuteCommandPowerShutter(uint32_t device, uint32_t state, uint32_t source) +{ + if (device <= devices_present) ExecuteCommandPower(device,state,source); +} + +void ShutterUpdateVelocity(uint8_t i) +{ + Shutter.pwm_velocity[i] += Shutter.accelerator[i]; + Shutter.pwm_velocity[i] = tmax(1,tmin(Shutter.direction[i]==1 ? Shutter.max_pwm_velocity : Shutter.max_close_pwm_velocity[i],Shutter.pwm_velocity[i])); } void ShutterRtc50mS(void) { for (uint8_t i = 0; i < shutters_present; i++) { Shutter.time[i]++; - if (Shutter.accelerator[i]) { - //AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: accelerator i=%d -> %d"),i, Shutter.accelerator[i]); - Shutter.pwm_frequency[i] += Shutter.accelerator[i]; - Shutter.pwm_frequency[i] = tmax(0,tmin(Shutter.direction[i]==1 ? Shutter.max_pwm_frequency : Shutter.max_close_pwm_frequency[i],Shutter.pwm_frequency[i])); - analogWriteFreq(Shutter.pwm_frequency[i]); - analogWrite(Pin(GPIO_PWM1, i), 50); + switch (Shutter.PositionMode) { + case SHT_PWM_VALUE: + if (Shutter.accelerator[i]) ShutterUpdateVelocity(i); + Shutter.real_position[i] += Shutter.direction[i] > 0 ? Shutter.pwm_velocity[i] : -Shutter.pwm_velocity[i]; + Shutter.pwm_value[i] = SHT_DIV_ROUND((Shutter.pwm_max[i]-Shutter.pwm_min[i]) * Shutter.real_position[i] , Shutter.open_max[i])+Shutter.pwm_min[i]; + analogWrite(Pin(GPIO_PWM1, i), Shutter.pwm_value[i]); + break; + + case SHT_COUNTER: + if (Shutter.accelerator[i]) { + //AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: accelerator i=%d -> %d"),i, Shutter.accelerator[i]); + ShutterUpdateVelocity(i); + analogWriteFreq(Shutter.pwm_velocity[i]); + analogWrite(Pin(GPIO_PWM1, i), 50); + } + break; } } } -#define SHT_DIV_ROUND(__A, __B) (((__A) + (__B)/2) / (__B)) - int32_t ShutterPercentToRealPosition(uint32_t percent, uint32_t index) { if (Settings.shutter_set50percent[index] != 50) { @@ -176,7 +209,7 @@ void ShutterInit(void) // if shutter 4 is unused if (Settings.shutter_startrelay[MAX_SHUTTERS -1] == 0) { - Shutter.max_pwm_frequency = Settings.shuttercoeff[4][3] > 0 ? Settings.shuttercoeff[4][3] : Shutter.max_pwm_frequency; + Shutter.max_pwm_velocity = Settings.shuttercoeff[4][3] > 0 ? Settings.shuttercoeff[4][3] : Shutter.max_pwm_velocity; } for (uint32_t i = 0; i < MAX_SHUTTERS; i++) { // set startrelay to 1 on first init, but only to shutter 1. 90% usecase @@ -194,22 +227,35 @@ void ShutterInit(void) relay_in_interlock = true; } } - if (relay_in_interlock) { - if (Settings.pulse_timer[i] > 0) { - Shutter.mode = SHT_PULSE_OPEN__PULSE_CLOSE; - } else { - Shutter.mode = SHT_OFF_OPEN__OFF_CLOSE; + switch (Settings.pulse_timer[i]) { + case 0: + Shutter.SwitchMode = SHT_SWITCH; + break; + default: + Shutter.SwitchMode = SHT_PULSE; + break; + } + + if (Settings.shutter_mode == SHT_UNDEF) { + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: mode undef.. calculate...")); + switch (Settings.pulse_timer[i+1]) { + case 0: + Shutter.PositionMode = SHT_TIME_GARAGE; + break; + default: + if (relay_in_interlock) { + Shutter.PositionMode = SHT_TIME; + } else { + Shutter.PositionMode = SHT_TIME_UP_DOWN; + if (PinUsed(GPIO_PWM1, i) && PinUsed(GPIO_CNTR1, i)) { + Shutter.PositionMode = SHT_COUNTER; + } + } + + break; } } else { - Shutter.mode = SHT_OFF_ON__OPEN_CLOSE; - if (PinUsed(GPIO_PWM1, i) && PinUsed(GPIO_CNTR1, i)) { - Shutter.mode = SHT_OFF_ON__OPEN_CLOSE_STEPPER; - Shutter.pwm_frequency[i] = 0; - Shutter.accelerator[i] = 0; - analogWriteFreq(Shutter.pwm_frequency[i]); - analogWrite(Pin(GPIO_PWM1, i), 0); -// ExecuteCommandPower(Settings.shutter_startrelay[i]+2, 0, SRC_SHUTTER); - } + Shutter.PositionMode = Settings.shutter_mode; } TickerShutter.attach_ms(50, ShutterRtc50mS ); @@ -220,11 +266,13 @@ void ShutterInit(void) Shutter.open_time[i] = (Settings.shutter_opentime[i] > 0) ? Settings.shutter_opentime[i] : 100; Shutter.close_time[i] = (Settings.shutter_closetime[i] > 0) ? Settings.shutter_closetime[i] : 100; + Shutter.pwm_min[i] = pwm_min; + Shutter.pwm_max[i] = pwm_max; + // Update Calculation 20 because time interval is 0.05 sec Shutter.open_max[i] = 200 * Shutter.open_time[i]; Shutter.close_velocity[i] = Shutter.open_max[i] / Shutter.close_time[i] / 2 ; - Shutter.max_close_pwm_frequency[i] = Shutter.max_pwm_frequency*Shutter.open_time[i] / Shutter.close_time[i]; - AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Shutter %d Closefreq: %d"),i, Shutter.max_close_pwm_frequency[i]); + // calculate a ramp slope at the first 5 percent to compensate that shutters move with down part later than the upper part if (Settings.shutter_set50percent[i] != 50) { @@ -240,9 +288,17 @@ void ShutterInit(void) Shutter.motordelay[i] = Settings.shutter_motordelay[i]; Shutter.lastdirection[i] = (50 < Settings.shutter_position[i]) ? 1 : -1; - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT%d: Init. Pos: %d,inverted %d, locked %d, end stop time enabled %d, webButtons inverted %d, shuttermode %d"), - i+1, Shutter.real_position[i], - (Settings.shutter_options[i]&1) ? 1 : 0, (Settings.shutter_options[i]&2) ? 1 : 0, (Settings.shutter_options[i]&4) ? 1 : 0, (Settings.shutter_options[i]&8) ? 1 : 0, Shutter.mode); + switch (Shutter.PositionMode) { + case SHT_COUNTER: + case SHT_PWM_VALUE: + Shutter.max_close_pwm_velocity[i] = Shutter.max_pwm_velocity*Shutter.open_time[i] / Shutter.close_time[i]; + break; + } + + //AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Shutter %d Closevel: %d"),i, Shutter.max_close_pwm_velocity[i]); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT%d: Init. Pos: %d,inverted %d, locked %d, end stop time enabled %d, webButtons inverted %d"), + i+1, Shutter.real_position[i], + (Settings.shutter_options[i]&1) ? 1 : 0, (Settings.shutter_options[i]&2) ? 1 : 0, (Settings.shutter_options[i]&4) ? 1 : 0, (Settings.shutter_options[i]&8) ? 1 : 0); } else { // terminate loop at first INVALID shutter. @@ -250,6 +306,7 @@ void ShutterInit(void) } ShutterLimitRealAndTargetPositions(i); Settings.shutter_accuracy = 1; + Settings.shutter_mode = Shutter.PositionMode; } } @@ -290,101 +347,130 @@ void ShutterLimitRealAndTargetPositions(uint32_t i) { if (Shutter.target_position[i]>Shutter.open_max[i]) Shutter.target_position[i] = Shutter.open_max[i]; } +void ShutterCalculateAccelerator(uint8_t i) +{ + switch (Shutter.PositionMode) { + case SHT_COUNTER: + case SHT_PWM_VALUE: + int32_t max_frequency = Shutter.direction[i] == 1 ? Shutter.max_pwm_velocity : Shutter.max_close_pwm_velocity[i]; + int32_t max_freq_change_per_sec = Shutter.max_pwm_velocity*steps_per_second / (Shutter.motordelay[i]>0 ? Shutter.motordelay[i] : 1); + int32_t min_runtime_ms = Shutter.pwm_velocity[i]*1000 / max_freq_change_per_sec; + int32_t velocity = Shutter.direction[i] == 1 ? 100 : Shutter.close_velocity[i]; + int32_t minstopway = min_runtime_ms * velocity / 100 * Shutter.pwm_velocity[i] / max_frequency * Shutter.direction[i] ; + + int32_t next_possible_stop = Shutter.real_position[i] + minstopway ; + stop_position_delta =200 * Shutter.pwm_velocity[i]/max_frequency + Shutter.direction[i] * (next_possible_stop - Shutter.target_position[i]); + + //Shutter.accelerator[i] = tmin(tmax(max_freq_change_per_sec*(100-(Shutter.direction[i]*(Shutter.target_position[i]-next_possible_stop) ))/2000 , max_freq_change_per_sec*9/200), max_freq_change_per_sec*11/200); + //int32_t act_freq_change = max_freq_change_per_sec/20; + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: time: %d, velocity %d, minstopway %d,cur_freq %d, max_frequency %d, act_freq_change %d, min_runtime_ms %d, act.pos %d, next_stop %d, target: %d"),Shutter.time[i],velocity,minstopway, + Shutter.pwm_velocity[i],max_frequency, Shutter.accelerator[i],min_runtime_ms,Shutter.real_position[i], next_possible_stop,Shutter.target_position[i]); + + if (Shutter.accelerator[i] < 0 || next_possible_stop * Shutter.direction[i] > (Shutter.target_position[i]- (100 * Shutter.direction[i])) * Shutter.direction[i] ) { + + Shutter.accelerator[i] = - tmin(tmax(max_freq_change_per_sec*(100-(Shutter.direction[i]*(Shutter.target_position[i]-next_possible_stop) ))/2000 , max_freq_change_per_sec*9/200), max_freq_change_per_sec*11/200); + //AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Ramp down: acc: %d"), Shutter.accelerator[i]); + } else if ( Shutter.accelerator[i] > 0 && Shutter.pwm_velocity[i] == max_frequency) { + Shutter.accelerator[i] = 0; + } + break; + } +} + +void ShutterDecellerateForStop(uint8_t i) +{ + switch (Shutter.PositionMode) { + case SHT_PWM_VALUE: + case SHT_COUNTER: + int16_t missing_steps; + Shutter.accelerator[i] = -(Shutter.direction[i] == 1 ? Shutter.max_pwm_velocity : Shutter.max_close_pwm_velocity[i])/(Shutter.motordelay[i]+1); + while (Shutter.pwm_velocity[i] > -Shutter.accelerator[i]) { + //AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: velocity: %ld, delta: %d"), Shutter.pwm_velocity[i], Shutter.accelerator[i] ); + //Shutter.pwm_velocity[i] = tmax(Shutter.pwm_velocity[i]-Shutter.accelerator[i] , 0); + // Control will be done in RTC Ticker. + delay(50); + } + if (Shutter.PositionMode == SHT_COUNTER){ + missing_steps = ((Shutter.target_position[i]-Shutter.start_position[i])*Shutter.direction[i]*Shutter.max_pwm_velocity/2000) - RtcSettings.pulse_counter[i]; + //prepare for stop PWM + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Remain steps %d, counter %d, freq %d"), missing_steps, RtcSettings.pulse_counter[i] ,Shutter.pwm_velocity[i]); + Shutter.accelerator[i] = 0; + Shutter.pwm_velocity[i] = Shutter.pwm_velocity[i] > 250 ? 250 : Shutter.pwm_velocity[i]; + analogWriteFreq(Shutter.pwm_velocity[i]); + analogWrite(Pin(GPIO_PWM1, i), 50); + Shutter.pwm_velocity[i] = 0; + analogWriteFreq(Shutter.pwm_velocity[i]); + while (RtcSettings.pulse_counter[i] < (uint32_t)(Shutter.target_position[i]-Shutter.start_position[i])*Shutter.direction[i]*Shutter.max_pwm_velocity/2000) { + delay(1); + } + analogWrite(Pin(GPIO_PWM1, i), 0); // removed with 8.3 because of reset caused by watchog + Shutter.real_position[i] = ShutterCalculatePosition(i); + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Real %d, pulsecount %d, start %d"), Shutter.real_position[i],RtcSettings.pulse_counter[i], Shutter.start_position[i]); + + } + Shutter.direction[i] = 0; + break; + } +} + +void ShutterPowerOff(uint8_t i) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Stop Shutter %d .."), i); + ShutterDecellerateForStop(i); + if (Shutter.direction[i] !=0) { + Shutter.direction[i] = 0; + delay(MOTOR_STOP_TIME); + } + switch (Shutter.SwitchMode) { + case SHT_SWITCH: + if ((1 << (Settings.shutter_startrelay[i]-1)) & power) { + ExecuteCommandPowerShutter(Settings.shutter_startrelay[i], 0, SRC_SHUTTER); + } + if ((1 << (Settings.shutter_startrelay[i])) & power) { + ExecuteCommandPowerShutter(Settings.shutter_startrelay[i]+1, 0, SRC_SHUTTER); + } + break; + case SHT_PULSE: + uint8_t cur_relay = Settings.shutter_startrelay[i] + (Shutter.direction[i] == 1 ? 0 : (uint8_t)(Shutter.PositionMode == SHT_TIME)) ; + // we have a momentary switch here. Needs additional pulse on same relay after the end + if ((SRC_PULSETIMER == last_source || SRC_SHUTTER == last_source || SRC_WEBGUI == last_source)) { + ExecuteCommandPowerShutter(cur_relay, 1, SRC_SHUTTER); + // switch off direction relay to make it power less + if ((1 << (Settings.shutter_startrelay[i])) & power) { + ExecuteCommandPowerShutter(Settings.shutter_startrelay[i]+1, 0, SRC_SHUTTER); + } + } else { + last_source = SRC_SHUTTER; + } + break; + } +} + void ShutterUpdatePosition(void) { char scommand[CMDSZ]; char stopic[TOPSZ]; + stop_position_delta = 20; + for (uint32_t i = 0; i < shutters_present; i++) { if (Shutter.direction[i] != 0) { - int32_t stop_position_delta = 20; + // Calculate position with counter. Much more accurate and no need for motordelay workaround // adding some steps to stop early - Shutter.real_position[i] = ShutterCounterBasedPosition(i); + Shutter.real_position[i] = ShutterCalculatePosition(i); if (!Shutter.start_reported) { ShutterReportPosition(true, i); XdrvRulesProcess(); Shutter.start_reported = 1; } + ShutterCalculateAccelerator(i); - if (Shutter.mode == SHT_OFF_ON__OPEN_CLOSE_STEPPER) { - int32_t max_frequency = Shutter.direction[i] == 1 ? Shutter.max_pwm_frequency : Shutter.max_close_pwm_frequency[i]; - int32_t max_freq_change_per_sec = Shutter.max_pwm_frequency*steps_per_second / (Shutter.motordelay[i]>0 ? Shutter.motordelay[i] : 1); - int32_t min_runtime_ms = Shutter.pwm_frequency[i]*1000 / max_freq_change_per_sec; - int32_t velocity = Shutter.direction[i] == 1 ? 100 : Shutter.close_velocity[i]; - int32_t minstopway = min_runtime_ms * velocity / 100 * Shutter.pwm_frequency[i] / max_frequency * Shutter.direction[i] ; - - int32_t next_possible_stop = Shutter.real_position[i] + minstopway ; - stop_position_delta =200 * Shutter.pwm_frequency[i]/max_frequency + Shutter.direction[i] * (next_possible_stop - Shutter.target_position[i]); - - //Shutter.accelerator[i] = tmin(tmax(max_freq_change_per_sec*(100-(Shutter.direction[i]*(Shutter.target_position[i]-next_possible_stop) ))/2000 , max_freq_change_per_sec*9/200), max_freq_change_per_sec*11/200); - //int32_t act_freq_change = max_freq_change_per_sec/20; - AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: time: %d, velocity %d, minstopway %d,cur_freq %d, max_frequency %d, act_freq_change %d, min_runtime_ms %d, act.pos %d, next_stop %d, target: %d"),Shutter.time[i],velocity,minstopway, - Shutter.pwm_frequency[i],max_frequency, Shutter.accelerator[i],min_runtime_ms,Shutter.real_position[i], next_possible_stop,Shutter.target_position[i]); - - if (Shutter.accelerator[i] < 0 || next_possible_stop * Shutter.direction[i] > (Shutter.target_position[i]- (100 * Shutter.direction[i])) * Shutter.direction[i] ) { - - Shutter.accelerator[i] = - tmin(tmax(max_freq_change_per_sec*(100-(Shutter.direction[i]*(Shutter.target_position[i]-next_possible_stop) ))/2000 , max_freq_change_per_sec*9/200), max_freq_change_per_sec*11/200); - //AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Ramp down: acc: %d"), Shutter.accelerator[i]); - } else if ( Shutter.accelerator[i] > 0 && Shutter.pwm_frequency[i] == max_frequency) { - Shutter.accelerator[i] = 0; - } - } else { - Shutter.real_position[i] = Shutter.start_position[i] + ( (Shutter.time[i] - Shutter.motordelay[i]) * (Shutter.direction[i] > 0 ? 100 : -Shutter.close_velocity[i])); - } - if ( Shutter.real_position[i] * Shutter.direction[i] + stop_position_delta >= Shutter.target_position[i] * Shutter.direction[i] ) { - // calculate relay number responsible for current movement. - //AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Stop Condition detected: real: %d, Target: %d, direction: %d"),Shutter.real_position[i], Shutter.target_position[i],Shutter.direction[i]); - uint8_t cur_relay = Settings.shutter_startrelay[i] + (Shutter.direction[i] == 1 ? 0 : 1) ; - int16_t missing_steps; - - switch (Shutter.mode) { - case SHT_PULSE_OPEN__PULSE_CLOSE: - // we have a momentary switch here. Needs additional pulse on same relay after the end - if (SRC_PULSETIMER == last_source || SRC_SHUTTER == last_source || SRC_WEBGUI == last_source) { - ExecuteCommandPower(cur_relay, 1, SRC_SHUTTER); - } else { - last_source = SRC_SHUTTER; - } - break; - case SHT_OFF_ON__OPEN_CLOSE_STEPPER: - missing_steps = ((Shutter.target_position[i]-Shutter.start_position[i])*Shutter.direction[i]*Shutter.max_pwm_frequency/2000) - RtcSettings.pulse_counter[i]; - //prepare for stop PWM - AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Remain steps %d, counter %d, freq %d"), missing_steps, RtcSettings.pulse_counter[i] ,Shutter.pwm_frequency[i]); - Shutter.accelerator[i] = 0; - Shutter.pwm_frequency[i] = Shutter.pwm_frequency[i] > 250 ? 250 : Shutter.pwm_frequency[i]; - analogWriteFreq(Shutter.pwm_frequency[i]); - analogWrite(Pin(GPIO_PWM1, i), 50); - Shutter.pwm_frequency[i] = 0; - analogWriteFreq(Shutter.pwm_frequency[i]); - while (RtcSettings.pulse_counter[i] < (uint32_t)(Shutter.target_position[i]-Shutter.start_position[i])*Shutter.direction[i]*Shutter.max_pwm_frequency/2000) { - delay(1); - } - analogWrite(Pin(GPIO_PWM1, i), 0); // removed with 8.3 because of reset caused by watchog -// ExecuteCommandPower(Settings.shutter_startrelay[i]+2, 0, SRC_SHUTTER); - Shutter.real_position[i] = ShutterCounterBasedPosition(i); - AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Real %d, pulsecount %d, start %d"), Shutter.real_position[i],RtcSettings.pulse_counter[i], Shutter.start_position[i]); - - if ((1 << (Settings.shutter_startrelay[i]-1)) & power) { - ExecuteCommandPower(Settings.shutter_startrelay[i], 0, SRC_SHUTTER); - ExecuteCommandPower(Settings.shutter_startrelay[i]+1, 0, SRC_SHUTTER); - } - break; - case SHT_OFF_ON__OPEN_CLOSE: - if ((1 << (Settings.shutter_startrelay[i]-1)) & power) { - ExecuteCommandPower(Settings.shutter_startrelay[i], 0, SRC_SHUTTER); - ExecuteCommandPower(Settings.shutter_startrelay[i]+1, 0, SRC_SHUTTER); - } - break; - case SHT_OFF_OPEN__OFF_CLOSE: - // avoid switching OFF a relay already OFF - if ((1 << (cur_relay-1)) & power) { - // Relay is on and need to be switched off. - ExecuteCommandPower(cur_relay, 0, SRC_SHUTTER); - } - break; + if ( Shutter.real_position[i] * Shutter.direction[i] + stop_position_delta >= Shutter.target_position[i] * Shutter.direction[i] ) { + if (Shutter.direction[i] != 0) { + Shutter.lastdirection[i] = Shutter.direction[i]; } + ShutterPowerOff(i); ShutterLimitRealAndTargetPositions(i); Settings.shutter_position[i] = ShutterRealToPercentPosition(Shutter.real_position[i], i); @@ -397,11 +483,6 @@ void ShutterUpdatePosition(void) GetTopic_P(stopic, STAT, mqtt_topic, scommand); Response_P("%d", (Settings.shutter_options[i] & 1) ? 100 - Settings.shutter_position[i]: Settings.shutter_position[i]); MqttPublish(stopic, Settings.flag.mqtt_power_retain); // CMND_POWERRETAIN - - if (Shutter.direction[i] != 0) { - Shutter.lastdirection[i] = Shutter.direction[i]; - } - Shutter.direction[i] = 0; ShutterReportPosition(true, i); rules_flag.shutter_moved = 1; XdrvRulesProcess(); @@ -425,14 +506,20 @@ void ShutterStartInit(uint32_t i, int32_t direction, int32_t target_pos) || ( (-1 == direction) && (Shutter.real_position[i] / Shutter.close_velocity[i] <= 2)) ) { Shutter.skip_relay_change = 1; } else { - if (Shutter.mode == SHT_OFF_ON__OPEN_CLOSE_STEPPER) { - Shutter.pwm_frequency[i] = 0; - analogWriteFreq(Shutter.pwm_frequency[i]); - analogWrite(Pin(GPIO_PWM1, i), 0); - RtcSettings.pulse_counter[i] = 0; - Shutter.accelerator[i] = Shutter.max_pwm_frequency / (Shutter.motordelay[i]>0 ? Shutter.motordelay[i] : 1); - AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Ramp up: %d"), Shutter.accelerator[i]); + Shutter.pwm_velocity[i] = 0; + switch (Shutter.PositionMode) { +#ifdef SHUTTER_STEPPER + case SHT_COUNTER: + analogWriteFreq(Shutter.pwm_velocity[i]); + analogWrite(Pin(GPIO_PWM1, i), 0); + RtcSettings.pulse_counter[i] = 0; + break; +#endif + case SHT_PWM_VALUE: + Shutter.max_pwm_velocity = 100; + break; } + Shutter.accelerator[i] = Shutter.max_pwm_velocity / (Shutter.motordelay[i]>0 ? Shutter.motordelay[i] : 1); Shutter.target_position[i] = target_pos; Shutter.start_position[i] = Shutter.real_position[i]; Shutter.time[i] = 0; @@ -441,40 +528,31 @@ void ShutterStartInit(uint32_t i, int32_t direction, int32_t target_pos) rules_flag.shutter_moving = 1; rules_flag.shutter_moved = 0; Shutter.start_reported = 0; - //AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: real %d, start %d, counter %d, max_freq %d, dir %d, freq %d"),Shutter.real_position[i], Shutter.start_position[i] ,RtcSettings.pulse_counter[i],Shutter.max_pwm_frequency , Shutter.direction[i] ,Shutter.max_pwm_frequency ); + //AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: real %d, start %d, counter %d, max_freq %d, dir %d, freq %d"),Shutter.real_position[i], Shutter.start_position[i] ,RtcSettings.pulse_counter[i],Shutter.max_pwm_velocity , Shutter.direction[i] ,Shutter.max_pwm_velocity ); } - //AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Start shutter: %d from %d to %d in directin %d"), i, Shutter.start_position[i], Shutter.target_position[i], Shutter.direction[i]); + //AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Start shutter: %d from %d to %d in direction %d"), i, Shutter.start_position[i], Shutter.target_position[i], Shutter.direction[i]); } -void ShutterWaitForMotorStop(uint32_t i) + +int32_t ShutterCalculatePosition(uint32_t i) { - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Wait for Motorstop..")); - if ((SHT_OFF_ON__OPEN_CLOSE == Shutter.mode) || (SHT_OFF_ON__OPEN_CLOSE_STEPPER == Shutter.mode)) { - if (SHT_OFF_ON__OPEN_CLOSE_STEPPER == Shutter.mode) { - //AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Frequency change %d"), Shutter.pwm_frequency); - while (Shutter.pwm_frequency[i] > 0) { - //AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Frequency: %ld, delta: %d"), Shutter.pwm_frequency[i], (int32_t)((Shutter.direction[i] == 1 ? Shutter.max_pwm_frequency : Shutter.max_close_pwm_frequency[i])/(Shutter.motordelay[i]+1)) ); - Shutter.pwm_frequency[i] = tmax(Shutter.pwm_frequency[i]-((Shutter.direction[i] == 1 ? Shutter.max_pwm_frequency : Shutter.max_close_pwm_frequency[i])/(Shutter.motordelay[i]+1)) , 0); - //AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Frequency: %ld"), Shutter.pwm_frequency[i]); - analogWriteFreq(Shutter.pwm_frequency[i]); - analogWrite(Pin(GPIO_PWM1, i), 50); - delay(50); - } - analogWrite(Pin(GPIO_PWM1, i), 0); -// ExecuteCommandPower(Settings.shutter_startrelay[i]+2, 0, SRC_SHUTTER); - Shutter.real_position[i] = ShutterCounterBasedPosition(i); - } else { - ExecuteCommandPower(Settings.shutter_startrelay[i], 0, SRC_SHUTTER); - delay(MOTOR_STOP_TIME); + switch (Shutter.PositionMode) { + case SHT_COUNTER: + return ((int32_t)RtcSettings.pulse_counter[i]*Shutter.direction[i]*2000 / Shutter.max_pwm_velocity)+Shutter.start_position[i]; + break; + case SHT_TIME: + case SHT_TIME_UP_DOWN: + case SHT_TIME_GARAGE: + return Shutter.start_position[i] + ( (Shutter.time[i] - Shutter.motordelay[i]) * (Shutter.direction[i] > 0 ? 100 : -Shutter.close_velocity[i])); + break; + case SHT_PWM_TIME: + break; + case SHT_PWM_VALUE: + return Shutter.real_position[i]; + break; + default: + break; } - } else { - delay(MOTOR_STOP_TIME); - } -} - -int32_t ShutterCounterBasedPosition(uint32_t i) -{ - return ((int32_t)RtcSettings.pulse_counter[i]*Shutter.direction[i]*2000 / Shutter.max_pwm_frequency)+Shutter.start_position[i]; } void ShutterRelayChanged(void) @@ -487,45 +565,72 @@ void ShutterRelayChanged(void) for (uint32_t i = 0; i < shutters_present; i++) { power_t powerstate_local = (power >> (Settings.shutter_startrelay[i] -1)) & 3; + // SRC_IGNORE added because INTERLOCK function bite causes this as last source for changing the relay. //uint8 manual_relays_changed = ((Shutter.switched_relay >> (Settings.shutter_startrelay[i] -1)) & 3) && SRC_IGNORE != last_source && SRC_SHUTTER != last_source && SRC_PULSETIMER != last_source ; uint8 manual_relays_changed = ((Shutter.switched_relay >> (Settings.shutter_startrelay[i] -1)) & 3) && SRC_SHUTTER != last_source && SRC_PULSETIMER != last_source ; - //AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Shutter %d: source: %s, powerstate_local %ld, Shutter.switched_relay %d, manual change %d"), i+1, GetTextIndexed(stemp1, sizeof(stemp1), last_source, kCommandSource), powerstate_local,Shutter.switched_relay,manual_relays_changed); + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Shutter %d: source: %s, powerstate_local %ld, Shutter.switched_relay %d, manual change %d"), i+1, GetTextIndexed(stemp1, sizeof(stemp1), last_source, kCommandSource), powerstate_local,Shutter.switched_relay,manual_relays_changed); if (manual_relays_changed) { //Shutter.skip_relay_change = true; ShutterLimitRealAndTargetPositions(i); - if (Shutter.mode == SHT_OFF_ON__OPEN_CLOSE || Shutter.mode == SHT_OFF_ON__OPEN_CLOSE_STEPPER) { - ShutterWaitForMotorStop(i); - switch (powerstate_local) { - case 1: - ShutterStartInit(i, 1, Shutter.open_max[i]); - break; - case 3: - ShutterStartInit(i, -1, 0); - break; - default: - //AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Shutter %d: Switch OFF motor."),i); - Shutter.target_position[i] = Shutter.real_position[i]; - } - } else { - if (Shutter.direction[i] != 0 && (!powerstate_local || (powerstate_local && Shutter.mode == SHT_PULSE_OPEN__PULSE_CLOSE))) { - Shutter.target_position[i] = Shutter.real_position[i]; - AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Shutter %d: Switch OFF motor. Target: %ld, source: %s, powerstate_local %ld, Shutter.switched_relay %d, manual change %d"), i+1, Shutter.target_position[i], GetTextIndexed(stemp1, sizeof(stemp1), last_source, kCommandSource), powerstate_local,Shutter.switched_relay,manual_relays_changed); - } else { - last_source = SRC_SHUTTER; // avoid switch off in the next loop - if (powerstate_local == 2) { // testing on CLOSE relay, if ON - // close with relay two - ShutterWaitForMotorStop(i); - ShutterStartInit(i, -1, 0); - } else { - // opens with relay one - ShutterWaitForMotorStop(i); - ShutterStartInit(i, 1, Shutter.open_max[i]); - } - } + switch (Shutter.SwitchMode ) { + case SHT_PULSE: + if (Shutter.direction[i] != 0 && powerstate_local) { + Shutter.target_position[i] = Shutter.real_position[i]; + powerstate_local = 0; + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Shutter %d: Switch OFF motor. Target: %ld, source: %s, powerstate_local %ld, Shutter.switched_relay %d, manual change %d"), i+1, Shutter.target_position[i], GetTextIndexed(stemp1, sizeof(stemp1), last_source, kCommandSource), powerstate_local,Shutter.switched_relay,manual_relays_changed); + } + break; + default: + last_source = SRC_SHUTTER; // avoid switch off in the next loop + if (Shutter.direction[i] != 0 )ShutterPowerOff(i); + } + switch (Shutter.PositionMode) { + // enum ShutterPositionMode {SHT_TIME, SHT_TIME_UP_DOWN, SHT_TIME_GARAGE, SHT_COUNTER, SHT_PWM_VALUE, SHT_PWM_TIME,}; + case SHT_TIME_UP_DOWN: + case SHT_COUNTER: + case SHT_PWM_VALUE: + case SHT_PWM_TIME: + ShutterPowerOff(i); + switch (powerstate_local) { + case 1: + ShutterStartInit(i, 1, Shutter.open_max[i]); + break; + case 3: + ShutterStartInit(i, -1, 0); + break; + default: + //AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Shutter %d: Switch OFF motor."),i); + Shutter.target_position[i] = Shutter.real_position[i]; + } + break; + case SHT_TIME: + switch (powerstate_local) { + case 1: + ShutterStartInit(i, 1, Shutter.open_max[i]); + break; + case 2: + ShutterStartInit(i, -1, 0); + break; + default: + //AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Shutter %d: Switch OFF motor."),i); + Shutter.target_position[i] = Shutter.real_position[i]; + } + break; + case SHT_TIME_GARAGE: + switch (powerstate_local) { + case 1: + ShutterStartInit(i, Shutter.lastdirection[i]*-1 , Shutter.lastdirection[i] == 1 ? 0 : Shutter.open_max[i]); + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Shutter %d Garage. NewTarget %d"), i, Shutter.target_position[i]); + break; + default: + Shutter.target_position[i] = Shutter.real_position[i]; + } + + + } // switch (Shutter.PositionMode) AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Shutter %d: Target: %ld, powerstatelocal %d"), i+1, Shutter.target_position[i], powerstate_local); - } - } - } + } // if (manual_relays_changed) + } // for (uint32_t i = 0; i < shutters_present; i++) } bool ShutterButtonIsSimultaneousHold(uint32_t button_index, uint32_t shutter_index) { @@ -723,12 +828,12 @@ void ShutterSetPosition(uint32_t device, uint32_t position) { char svalue[32]; // Command and number parameter snprintf_P(svalue, sizeof(svalue), PSTR(D_PRFX_SHUTTER D_CMND_SHUTTER_POSITION "%d %d"), device, position); - ExecuteCommand(svalue, SRC_IGNORE); + ExecuteCommand(svalue, SRC_SHUTTER); } void ShutterToggle(bool dir) { - //AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Payload toggle: %d, i %d"), XdrvMailbox.payload, XdrvMailbox.index); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Payload toggle: %d, i %d, dir %d"), XdrvMailbox.payload, XdrvMailbox.index, dir); if ((1 == XdrvMailbox.index) && (XdrvMailbox.payload != -99)) { XdrvMailbox.index = XdrvMailbox.payload; } @@ -843,6 +948,8 @@ void CmndShutterStop(void) AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Stop moving %d: dir: %d"), XdrvMailbox.index, Shutter.direction[i]); // set stop position 10 steps ahead (0.5sec to allow normal stop) + + //ToDo: Replace with function int32_t temp_realpos = Shutter.start_position[i] + ( (Shutter.time[i]+10) * (Shutter.direction[i] > 0 ? 100 : -Shutter.close_velocity[i])); XdrvMailbox.payload = ShutterRealToPercentPosition(temp_realpos, i); //XdrvMailbox.payload = Settings.shuttercoeff[2][i] * 5 > temp_realpos ? temp_realpos / Settings.shuttercoeff[2][i] : (temp_realpos-Settings.shuttercoeff[0,i]) / Settings.shuttercoeff[1][i]; @@ -900,7 +1007,7 @@ void CmndShutterPosition(void) if (XdrvMailbox.payload != -99) { //target_pos_percent = (Settings.shutter_options[index] & 1) ? 100 - target_pos_percent : target_pos_percent; Shutter.target_position[index] = ShutterPercentToRealPosition(target_pos_percent, index); - //Shutter.accelerator[index] = Shutter.max_pwm_frequency / ((Shutter.motordelay[index] > 0) ? Shutter.motordelay[index] : 1); + //Shutter.accelerator[index] = Shutter.max_pwm_velocity / ((Shutter.motordelay[index] > 0) ? Shutter.motordelay[index] : 1); //Shutter.target_position[index] = XdrvMailbox.payload < 5 ? Settings.shuttercoeff[2][index] * XdrvMailbox.payload : Settings.shuttercoeff[1][index] * XdrvMailbox.payload + Settings.shuttercoeff[0,index]; AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: lastsource %d:, real %d, target %d, payload %d"), last_source, Shutter.real_position[index] ,Shutter.target_position[index],target_pos_percent); } @@ -911,44 +1018,50 @@ void CmndShutterPosition(void) } int8_t new_shutterdirection = Shutter.real_position[index] < Shutter.target_position[index] ? 1 : -1; if (Shutter.direction[index] == -new_shutterdirection) { - // direction need to be changed. on momentary switches first stop the Shutter - if (SHT_PULSE_OPEN__PULSE_CLOSE == Shutter.mode) { - // code for momentary shutters only small switch on to stop Shutter - ExecuteCommandPower(Settings.shutter_startrelay[index] + ((new_shutterdirection == 1) ? 0 : 1), 1, SRC_SHUTTER); - delay(100); - } else { - if (SHT_OFF_OPEN__OFF_CLOSE == Shutter.mode) { - ExecuteCommandPower(Settings.shutter_startrelay[index] + ((new_shutterdirection == 1) ? 1 : 0), 0, SRC_SHUTTER); - ShutterWaitForMotorStop(index); - } - } + ShutterPowerOff(index); } if (Shutter.direction[index] != new_shutterdirection) { - if ((SHT_OFF_ON__OPEN_CLOSE == Shutter.mode) || (SHT_OFF_ON__OPEN_CLOSE_STEPPER == Shutter.mode)) { - //AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Delay5 5s, xdrv %d"), XdrvMailbox.payload); - ShutterWaitForMotorStop(index); - ExecuteCommandPower(Settings.shutter_startrelay[index], 0, SRC_SHUTTER); - ShutterStartInit(index, new_shutterdirection, Shutter.target_position[index]); - if (Shutter.skip_relay_change == 0) { - // Code for shutters with circuit safe configuration, switch the direction Relay - ExecuteCommandPower(Settings.shutter_startrelay[index] +1, new_shutterdirection == 1 ? 0 : 1, SRC_SHUTTER); - // power on - ExecuteCommandPower(Settings.shutter_startrelay[index], 1, SRC_SHUTTER); - if (SHT_OFF_ON__OPEN_CLOSE_STEPPER == Shutter.mode) { - ExecuteCommandPower(Settings.shutter_startrelay[index]+2, 1, SRC_SHUTTER); + ShutterStartInit(index, new_shutterdirection, Shutter.target_position[index]); + switch (Shutter.PositionMode) { + case SHT_COUNTER: + case SHT_PWM_TIME: + case SHT_PWM_VALUE: + case SHT_TIME_UP_DOWN: + if (!Shutter.skip_relay_change) { + // Code for shutters with circuit safe configuration, switch the direction Relay + ExecuteCommandPowerShutter(Settings.shutter_startrelay[index] +1, new_shutterdirection == 1 ? 0 : 1, SRC_SHUTTER); + // power on + ExecuteCommandPowerShutter(Settings.shutter_startrelay[index], 1, SRC_SHUTTER); } - } - } else { - // now start the motor for the right direction, work for momentary and normal shutters. - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Start in dir %d"), Shutter.direction[index]); - ShutterStartInit(index, new_shutterdirection, Shutter.target_position[index]); - if (Shutter.skip_relay_change == 0) { - ExecuteCommandPower(Settings.shutter_startrelay[index] + (new_shutterdirection == 1 ? 0 : 1), 1, SRC_SHUTTER); - } - //AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Delay6 5s, xdrv %d"), XdrvMailbox.payload); - } + if (Shutter.PositionMode != SHT_TIME_UP_DOWN) ExecuteCommandPowerShutter(Settings.shutter_startrelay[index]+2, 1, SRC_SHUTTER); + break; + case SHT_TIME: + if (!Shutter.skip_relay_change) { + if ( (power >> (Settings.shutter_startrelay[index] -1)) & 3 > 0 ) { + ExecuteCommandPowerShutter(Settings.shutter_startrelay[index] + (new_shutterdirection == 1 ? 1 : 0), Shutter.SwitchMode == SHT_SWITCH ? 0 : 1, SRC_SHUTTER); + } + ExecuteCommandPowerShutter(Settings.shutter_startrelay[index] + (new_shutterdirection == 1 ? 0 : 1), 1, SRC_SHUTTER); + } + break; + case SHT_TIME_GARAGE: + if (!Shutter.skip_relay_change) { + if (new_shutterdirection == Shutter.lastdirection[index]) { + AddLog_P2(LOG_LEVEL_INFO, PSTR("SHT: Garage not move in this direction: %d"), Shutter.SwitchMode == SHT_PULSE); + for (uint8_t k=0 ; k <= (uint8_t)(Shutter.SwitchMode == SHT_PULSE) ; k++) { + ExecuteCommandPowerShutter(Settings.shutter_startrelay[index], 1, SRC_SHUTTER); + delay(500); + ExecuteCommandPowerShutter(Settings.shutter_startrelay[index], 0, SRC_SHUTTER); + delay(500); + } + // reset shutter time to avoid 2 seconds above count as runtime + Shutter.time[index] = 0; + } // if (new_shutterdirection == Shutter.lastdirection[index]) + ExecuteCommandPowerShutter(Settings.shutter_startrelay[index], 1, SRC_SHUTTER); + } // if (!Shutter.skip_relay_change) + break; + } // switch (Shutter.PositionMode) Shutter.switched_relay = 0; - } + } // if (Shutter.direction[index] != new_shutterdirection) } else { target_pos_percent = ShutterRealToPercentPosition(Shutter.real_position[index], index); ShutterReportPosition(true, index); @@ -1015,6 +1128,18 @@ void CmndShutterMotorDelay(void) } } +void CmndShutterMode(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= MAX_MODES)) { + Shutter.PositionMode = XdrvMailbox.payload; + Settings.shutter_mode = XdrvMailbox.payload; + ShutterInit(); + ResponseCmndNumber(XdrvMailbox.payload); // ???? + } else { + ResponseCmndNumber(Shutter.PositionMode); + } +} + void CmndShutterRelay(void) { if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_SHUTTERS)) { @@ -1184,14 +1309,14 @@ void CmndShutterSetHalfway(void) void CmndShutterFrequency(void) { if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload <= 20000)) { - Shutter.max_pwm_frequency = XdrvMailbox.payload; + Shutter.max_pwm_velocity = XdrvMailbox.payload; if (shutters_present < 4) { - Settings.shuttercoeff[4][3] = Shutter.max_pwm_frequency; + Settings.shuttercoeff[4][3] = Shutter.max_pwm_velocity; } ShutterInit(); ResponseCmndNumber(XdrvMailbox.payload); // ???? } else { - ResponseCmndNumber(Shutter.max_pwm_frequency); + ResponseCmndNumber(Shutter.max_pwm_velocity); } } @@ -1353,7 +1478,7 @@ bool Xdrv27(uint8_t function) result = true; Shutter.skip_relay_change = 0; AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Skipping switch off relay %d"),i); - ExecuteCommandPower(i+1, 0, SRC_SHUTTER); + ExecuteCommandPowerShutter(i+1, 0, SRC_SHUTTER); } break; case FUNC_BUTTON_PRESSED: From f4190a96092a7bb8751a40b168da499247c94a37 Mon Sep 17 00:00:00 2001 From: stefanbode Date: Sat, 5 Sep 2020 20:41:08 +0200 Subject: [PATCH 09/55] Update i18n.h New command --- tasmota/i18n.h | 1 + 1 file changed, 1 insertion(+) diff --git a/tasmota/i18n.h b/tasmota/i18n.h index ba7e925f8..25b1cb8b7 100644 --- a/tasmota/i18n.h +++ b/tasmota/i18n.h @@ -585,6 +585,7 @@ #define D_CMND_SHUTTER_TOGGLEDIR "ToggleDir" #define D_CMND_SHUTTER_UP "Up" #define D_CMND_SHUTTER_DOWN "Down" +#define D_CMND_SHUTTER_MODE "Mode" #define D_CMND_SHUTTER_STOPOPEN "StopOpen" #define D_CMND_SHUTTER_STOPCLOSE "StopClose" #define D_CMND_SHUTTER_STOPTOGGLE "StopToggle" From 68c260d520a3940fbdb6f5c438c4a1872c3cb98b Mon Sep 17 00:00:00 2001 From: stefanbode Date: Sat, 5 Sep 2020 20:43:39 +0200 Subject: [PATCH 10/55] Adding 1 byte new shutter mode to set on demand --- tasmota/settings.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tasmota/settings.h b/tasmota/settings.h index 60c837364..3ca80ecd6 100644 --- a/tasmota/settings.h +++ b/tasmota/settings.h @@ -613,8 +613,9 @@ struct { uint8_t free_f43[1]; // F43 uint16_t energy_power_delta[3]; // F44 + uint8_t shutter_mode; // F45 - uint8_t free_f4e[106]; // F4A - Decrement if adding new Setting variables just above and below + uint8_t free_f4e[105]; // F4A - Decrement if adding new Setting variables just above and below // Only 32 bit boundary variables below SysBitfield5 flag5; // FB4 From b7565811db1236c73d7c2cd6df0532c008563578 Mon Sep 17 00:00:00 2001 From: stefanbode Date: Sat, 5 Sep 2020 20:45:42 +0200 Subject: [PATCH 11/55] Adding new shuttermode --- tasmota/support_command.ino | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tasmota/support_command.ino b/tasmota/support_command.ino index 0cdba2969..db83627e3 100644 --- a/tasmota/support_command.ino +++ b/tasmota/support_command.ino @@ -583,11 +583,12 @@ void CmndStatus(void) if (i > 0) { ResponseAppend_P(PSTR(",")); } ResponseAppend_P(PSTR("{\"" D_STATUS13_SHUTTER "%d\":{\"Relay1\":%d,\"Relay2\":%d,\"Open\":%d,\"Close\":%d," "\"50perc\":%d,\"Delay\":%d,\"Opt\":\"%s\"," - "\"Calib\":\"%d:%d:%d:%d:%d\"}"), + "\"Calib\":\"%d:%d:%d:%d:%d\"," + "\"Mode\":\"%d\"}"), i, Settings.shutter_startrelay[i], Settings.shutter_startrelay[i] +1, Settings.shutter_opentime[i], Settings.shutter_closetime[i], Settings.shutter_set50percent[i], Settings.shutter_motordelay[i], GetBinary(&Settings.shutter_options[i], 4).c_str(), - Settings.shuttercoeff[0][i], Settings.shuttercoeff[1][i], Settings.shuttercoeff[2][i], Settings.shuttercoeff[3][i], Settings.shuttercoeff[4][i]); - } + Settings.shuttercoeff[0][i], Settings.shuttercoeff[1][i], Settings.shuttercoeff[2][i], Settings.shuttercoeff[3][i], Settings.shuttercoeff[4][i], + Settings.shutter_mode); } ResponseJsonEnd(); MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS "13")); } From b2b0dfcbfdbf31ef2fe9e3dc28d6c501ce671f10 Mon Sep 17 00:00:00 2001 From: stefanbode Date: Sat, 5 Sep 2020 21:07:59 +0200 Subject: [PATCH 12/55] Bugfix --- tasmota/xdrv_27_shutter.ino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tasmota/xdrv_27_shutter.ino b/tasmota/xdrv_27_shutter.ino index 3a9b43d7e..d0fd0ae29 100644 --- a/tasmota/xdrv_27_shutter.ino +++ b/tasmota/xdrv_27_shutter.ino @@ -121,7 +121,7 @@ void ShutterRtc50mS(void) switch (Shutter.PositionMode) { case SHT_PWM_VALUE: if (Shutter.accelerator[i]) ShutterUpdateVelocity(i); - Shutter.real_position[i] += Shutter.direction[i] > 0 ? Shutter.pwm_velocity[i] : -Shutter.pwm_velocity[i]; + Shutter.real_position[i] += Shutter.direction[i] > 0 ? Shutter.pwm_velocity[i] : (Shutter.direction[i] < 0 ? -Shutter.pwm_velocity[i] : 0); Shutter.pwm_value[i] = SHT_DIV_ROUND((Shutter.pwm_max[i]-Shutter.pwm_min[i]) * Shutter.real_position[i] , Shutter.open_max[i])+Shutter.pwm_min[i]; analogWrite(Pin(GPIO_PWM1, i), Shutter.pwm_value[i]); break; 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 13/55] 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 14/55] 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 15/55] 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 16/55] 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 17/55] 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 18/55] 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 19/55] 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 20/55] 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 c3f04288fa85fddbd52ebfd6e94565201658797b Mon Sep 17 00:00:00 2001 From: stefanbode Date: Sat, 5 Sep 2020 23:35:05 +0200 Subject: [PATCH 21/55] Update xdrv_27_shutter.ino --- tasmota/xdrv_27_shutter.ino | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/tasmota/xdrv_27_shutter.ino b/tasmota/xdrv_27_shutter.ino index d0fd0ae29..c75a2fca2 100644 --- a/tasmota/xdrv_27_shutter.ino +++ b/tasmota/xdrv_27_shutter.ino @@ -121,7 +121,7 @@ void ShutterRtc50mS(void) switch (Shutter.PositionMode) { case SHT_PWM_VALUE: if (Shutter.accelerator[i]) ShutterUpdateVelocity(i); - Shutter.real_position[i] += Shutter.direction[i] > 0 ? Shutter.pwm_velocity[i] : (Shutter.direction[i] < 0 ? -Shutter.pwm_velocity[i] : 0); + Shutter.real_position[i] += Shutter.direction[i] > 0 ? Shutter.pwm_velocity[i] : (Shutter.direction[i] < 0 ? -Shutter.pwm_velocity[i] : 0); Shutter.pwm_value[i] = SHT_DIV_ROUND((Shutter.pwm_max[i]-Shutter.pwm_min[i]) * Shutter.real_position[i] , Shutter.open_max[i])+Shutter.pwm_min[i]; analogWrite(Pin(GPIO_PWM1, i), Shutter.pwm_value[i]); break; @@ -292,6 +292,7 @@ void ShutterInit(void) case SHT_COUNTER: case SHT_PWM_VALUE: Shutter.max_close_pwm_velocity[i] = Shutter.max_pwm_velocity*Shutter.open_time[i] / Shutter.close_time[i]; + stop_position_delta = 0; break; } @@ -357,22 +358,31 @@ void ShutterCalculateAccelerator(uint8_t i) int32_t min_runtime_ms = Shutter.pwm_velocity[i]*1000 / max_freq_change_per_sec; int32_t velocity = Shutter.direction[i] == 1 ? 100 : Shutter.close_velocity[i]; int32_t minstopway = min_runtime_ms * velocity / 100 * Shutter.pwm_velocity[i] / max_frequency * Shutter.direction[i] ; - + int32_t toBeAcc = 0; int32_t next_possible_stop = Shutter.real_position[i] + minstopway ; - stop_position_delta =200 * Shutter.pwm_velocity[i]/max_frequency + Shutter.direction[i] * (next_possible_stop - Shutter.target_position[i]); + stop_position_delta = Shutter.direction[i] * (next_possible_stop - Shutter.target_position[i]); + if (Shutter.PositionMode == SHT_COUNTER) { + // ToDo need to check the influence of frequency and speed on the secure area to stop right in time. + // seems currently only work with motordelay but not ok without motordelay. + stop_position_delta =+ 200 * Shutter.pwm_velocity[i]/max_frequency; + } else { + stop_position_delta =+ Shutter.pwm_velocity[i]; + } //Shutter.accelerator[i] = tmin(tmax(max_freq_change_per_sec*(100-(Shutter.direction[i]*(Shutter.target_position[i]-next_possible_stop) ))/2000 , max_freq_change_per_sec*9/200), max_freq_change_per_sec*11/200); //int32_t act_freq_change = max_freq_change_per_sec/20; - AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: time: %d, velocity %d, minstopway %d,cur_freq %d, max_frequency %d, act_freq_change %d, min_runtime_ms %d, act.pos %d, next_stop %d, target: %d"),Shutter.time[i],velocity,minstopway, - Shutter.pwm_velocity[i],max_frequency, Shutter.accelerator[i],min_runtime_ms,Shutter.real_position[i], next_possible_stop,Shutter.target_position[i]); - if (Shutter.accelerator[i] < 0 || next_possible_stop * Shutter.direction[i] > (Shutter.target_position[i]- (100 * Shutter.direction[i])) * Shutter.direction[i] ) { - - Shutter.accelerator[i] = - tmin(tmax(max_freq_change_per_sec*(100-(Shutter.direction[i]*(Shutter.target_position[i]-next_possible_stop) ))/2000 , max_freq_change_per_sec*9/200), max_freq_change_per_sec*11/200); + if (Shutter.accelerator[i] < 0 || next_possible_stop * Shutter.direction[i] > (Shutter.target_position[i]- (2*stop_position_delta * Shutter.direction[i])) * Shutter.direction[i] ) { + toBeAcc = 100+(Shutter.direction[i]*velocity*(next_possible_stop-Shutter.target_position[i])/Shutter.pwm_velocity[i]); + Shutter.accelerator[i] = - tmin(tmax((toBeAcc > 100 ? max_freq_change_per_sec*toBeAcc/2000+1 : max_freq_change_per_sec*toBeAcc/2000) , (max_freq_change_per_sec*9/200)-1), (max_freq_change_per_sec*11/200)+1); //AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Ramp down: acc: %d"), Shutter.accelerator[i]); } else if ( Shutter.accelerator[i] > 0 && Shutter.pwm_velocity[i] == max_frequency) { Shutter.accelerator[i] = 0; } + + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: time: %d, toBeAcc %d,velocity %d, minstopway %d,cur_vel %d, max_vel %d, act_vel_change %d, min_runtime_ms %d, act.pos %d, next_stop %d, target: %d, stop_position_delta %d, max_vel_change_per_sec %d"),Shutter.time[i],toBeAcc,velocity,minstopway, + Shutter.pwm_velocity[i],max_frequency, Shutter.accelerator[i],min_runtime_ms,Shutter.real_position[i], next_possible_stop,Shutter.target_position[i],stop_position_delta,max_freq_change_per_sec); + break; } } @@ -384,8 +394,8 @@ void ShutterDecellerateForStop(uint8_t i) case SHT_COUNTER: int16_t missing_steps; Shutter.accelerator[i] = -(Shutter.direction[i] == 1 ? Shutter.max_pwm_velocity : Shutter.max_close_pwm_velocity[i])/(Shutter.motordelay[i]+1); - while (Shutter.pwm_velocity[i] > -Shutter.accelerator[i]) { - //AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: velocity: %ld, delta: %d"), Shutter.pwm_velocity[i], Shutter.accelerator[i] ); + while (Shutter.pwm_velocity[i] > -Shutter.accelerator[i] && Shutter.accelerator[i] != 0) { + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: velocity: %ld, delta: %d"), Shutter.pwm_velocity[i], Shutter.accelerator[i] ); //Shutter.pwm_velocity[i] = tmax(Shutter.pwm_velocity[i]-Shutter.accelerator[i] , 0); // Control will be done in RTC Ticker. delay(50); From 1c657e86e7de6ab3aac6c14d49e77eb5908f22c4 Mon Sep 17 00:00:00 2001 From: stefanbode Date: Sun, 6 Sep 2020 12:14:29 +0200 Subject: [PATCH 22/55] Optimizes ramp --- tasmota/xdrv_27_shutter.ino | 35 +++++++++++++++++------------------ 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/tasmota/xdrv_27_shutter.ino b/tasmota/xdrv_27_shutter.ino index c75a2fca2..9b1b33027 100644 --- a/tasmota/xdrv_27_shutter.ino +++ b/tasmota/xdrv_27_shutter.ino @@ -292,7 +292,6 @@ void ShutterInit(void) case SHT_COUNTER: case SHT_PWM_VALUE: Shutter.max_close_pwm_velocity[i] = Shutter.max_pwm_velocity*Shutter.open_time[i] / Shutter.close_time[i]; - stop_position_delta = 0; break; } @@ -353,35 +352,35 @@ void ShutterCalculateAccelerator(uint8_t i) switch (Shutter.PositionMode) { case SHT_COUNTER: case SHT_PWM_VALUE: - int32_t max_frequency = Shutter.direction[i] == 1 ? Shutter.max_pwm_velocity : Shutter.max_close_pwm_velocity[i]; - int32_t max_freq_change_per_sec = Shutter.max_pwm_velocity*steps_per_second / (Shutter.motordelay[i]>0 ? Shutter.motordelay[i] : 1); - int32_t min_runtime_ms = Shutter.pwm_velocity[i]*1000 / max_freq_change_per_sec; - int32_t velocity = Shutter.direction[i] == 1 ? 100 : Shutter.close_velocity[i]; - int32_t minstopway = min_runtime_ms * velocity / 100 * Shutter.pwm_velocity[i] / max_frequency * Shutter.direction[i] ; + int32_t max_velocity = Shutter.direction[i] == 1 ? Shutter.max_pwm_velocity : Shutter.max_close_pwm_velocity[i]; + int32_t max_velocity_change_per_step = Shutter.max_pwm_velocity / (Shutter.motordelay[i]>0 ? Shutter.motordelay[i] : 1); + int32_t min_runtime_ms = Shutter.pwm_velocity[i] * 1000 / steps_per_second / max_velocity_change_per_step; + //int32_t velocity = Shutter.direction[i] == 1 ? 100 : Shutter.close_velocity[i]; + int32_t minstopway = (min_runtime_ms * (Shutter.pwm_velocity[i]+max_velocity_change_per_step)/100 - Shutter.pwm_velocity[i]) * Shutter.direction[i] ; int32_t toBeAcc = 0; int32_t next_possible_stop = Shutter.real_position[i] + minstopway ; stop_position_delta = Shutter.direction[i] * (next_possible_stop - Shutter.target_position[i]); if (Shutter.PositionMode == SHT_COUNTER) { // ToDo need to check the influence of frequency and speed on the secure area to stop right in time. // seems currently only work with motordelay but not ok without motordelay. - stop_position_delta =+ 200 * Shutter.pwm_velocity[i]/max_frequency; + stop_position_delta =+ 200 * Shutter.pwm_velocity[i]/max_velocity; } else { - stop_position_delta =+ Shutter.pwm_velocity[i]; + stop_position_delta =+ Shutter.pwm_velocity[i]-max_velocity_change_per_step; } - //Shutter.accelerator[i] = tmin(tmax(max_freq_change_per_sec*(100-(Shutter.direction[i]*(Shutter.target_position[i]-next_possible_stop) ))/2000 , max_freq_change_per_sec*9/200), max_freq_change_per_sec*11/200); - //int32_t act_freq_change = max_freq_change_per_sec/20; + //Shutter.accelerator[i] = tmin(tmax(max_velocity_change_per_step*(100-(Shutter.direction[i]*(Shutter.target_position[i]-next_possible_stop) ))/2000 , max_velocity_change_per_step*9/200), max_velocity_change_per_step*11/200); + //int32_t act_freq_change = max_velocity_change_per_step/20; - if (Shutter.accelerator[i] < 0 || next_possible_stop * Shutter.direction[i] > (Shutter.target_position[i]- (2*stop_position_delta * Shutter.direction[i])) * Shutter.direction[i] ) { - toBeAcc = 100+(Shutter.direction[i]*velocity*(next_possible_stop-Shutter.target_position[i])/Shutter.pwm_velocity[i]); - Shutter.accelerator[i] = - tmin(tmax((toBeAcc > 100 ? max_freq_change_per_sec*toBeAcc/2000+1 : max_freq_change_per_sec*toBeAcc/2000) , (max_freq_change_per_sec*9/200)-1), (max_freq_change_per_sec*11/200)+1); + if (Shutter.accelerator[i] < 0 || next_possible_stop * Shutter.direction[i] > (Shutter.target_position[i]- (stop_position_delta * Shutter.direction[i])) * Shutter.direction[i] ) { + toBeAcc = 100+(Shutter.direction[i]*max_velocity*(next_possible_stop-Shutter.target_position[i])/Shutter.pwm_velocity[i]); + Shutter.accelerator[i] = - tmin(tmax((toBeAcc > 100 ? max_velocity_change_per_step*toBeAcc/100 : max_velocity_change_per_step*toBeAcc/100) , (max_velocity_change_per_step*9/10)-1), (max_velocity_change_per_step*11/10)+1); //AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Ramp down: acc: %d"), Shutter.accelerator[i]); - } else if ( Shutter.accelerator[i] > 0 && Shutter.pwm_velocity[i] == max_frequency) { + } else if ( Shutter.accelerator[i] > 0 && Shutter.pwm_velocity[i] == max_velocity) { Shutter.accelerator[i] = 0; } - AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: time: %d, toBeAcc %d,velocity %d, minstopway %d,cur_vel %d, max_vel %d, act_vel_change %d, min_runtime_ms %d, act.pos %d, next_stop %d, target: %d, stop_position_delta %d, max_vel_change_per_sec %d"),Shutter.time[i],toBeAcc,velocity,minstopway, - Shutter.pwm_velocity[i],max_frequency, Shutter.accelerator[i],min_runtime_ms,Shutter.real_position[i], next_possible_stop,Shutter.target_position[i],stop_position_delta,max_freq_change_per_sec); + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: time: %d, toBeAcc %d, minstopway %d,cur_vel %d, max_vel %d, act_vel_change %d, min_runtime_ms %d, act.pos %d, next_stop %d, target: %d, stop_position_delta %d, max_vel_change_per_step %d"),Shutter.time[i],toBeAcc,minstopway, + Shutter.pwm_velocity[i],max_velocity, Shutter.accelerator[i],min_runtime_ms,Shutter.real_position[i], next_possible_stop,Shutter.target_position[i],stop_position_delta,max_velocity_change_per_step); break; } @@ -393,7 +392,7 @@ void ShutterDecellerateForStop(uint8_t i) case SHT_PWM_VALUE: case SHT_COUNTER: int16_t missing_steps; - Shutter.accelerator[i] = -(Shutter.direction[i] == 1 ? Shutter.max_pwm_velocity : Shutter.max_close_pwm_velocity[i])/(Shutter.motordelay[i]+1); + Shutter.accelerator[i] = -(Shutter.max_pwm_velocity / (Shutter.motordelay[i]>0 ? Shutter.motordelay[i] : 1)); while (Shutter.pwm_velocity[i] > -Shutter.accelerator[i] && Shutter.accelerator[i] != 0) { AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: velocity: %ld, delta: %d"), Shutter.pwm_velocity[i], Shutter.accelerator[i] ); //Shutter.pwm_velocity[i] = tmax(Shutter.pwm_velocity[i]-Shutter.accelerator[i] , 0); @@ -526,7 +525,7 @@ void ShutterStartInit(uint32_t i, int32_t direction, int32_t target_pos) break; #endif case SHT_PWM_VALUE: - Shutter.max_pwm_velocity = 100; + Shutter.max_pwm_velocity = 100; break; } Shutter.accelerator[i] = Shutter.max_pwm_velocity / (Shutter.motordelay[i]>0 ? Shutter.motordelay[i] : 1); 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 23/55] Prep release 8.5 --- BUILDS.md | 10 +++++++++- RELEASENOTES.md | 28 ++++++++++++++++++---------- tasmota/CHANGELOG.md | 5 +++-- tasmota/support_features.ino | 8 ++++++-- tools/decode-status.py | 16 +++++++++++----- 5 files changed, 47 insertions(+), 20 deletions(-) diff --git a/BUILDS.md b/BUILDS.md index 48c1ce19c..023c8ebec 100644 --- a/BUILDS.md +++ b/BUILDS.md @@ -10,6 +10,7 @@ | USE_MQTT_TLS_CA_CERT | - | - | - | - | - | - | - | | USE_MQTT_AWS_IOT | - | - | - | - | - | - | - | | USE_4K_RSA | - | - | - | - | - | - | - | +| USE_TELEGRAM | - | - | - | - | - | - | - | | USE_KNX | - | - | - | x | - | - | - | | USE_WEBSERVER | x | x | x | x | x | x | x | | USE_JAVASCRIPT_ES6 | - | - | - | - | - | - | - | @@ -69,7 +70,9 @@ | USE_DDSU666 | - | - | - | - | x | - | - | | USE_SOLAX_X1 | - | - | - | - | - | - | - | | USE_LE01MR | - | - | - | - | - | - | - | +| USE_BL0940 | - | x | x | x | x | - | - | | USE_TELEINFO | - | - | - | - | - | - | - | +| USE_IEM3000 | - | - | - | - | - | - | - | | | | | | | | | | | USE_ADC_VCC | x | x | - | - | - | x | - | | USE_COUNTER | - | - | x | x | x | - | x | @@ -126,6 +129,7 @@ | USE_VEML6075 | - | - | - | - | - | - | - | | USE_VEML7700 | - | - | - | - | - | - | - | | USE_MCP9808 | - | - | - | - | - | - | - | +| USE_HP303B | - | - | - | - | - | - | - | | | | | | | | | | | Feature or Sensor | minimal | lite | tasmota | knx | sensors | ir | display | Remarks | USE_SPI | - | - | - | - | - | - | x | @@ -143,8 +147,9 @@ | USE_GPS | - | - | - | - | - | - | - | | USE_HM10 | - | - | - | - | x | - | - | | USE_HRXL | - | - | - | - | x | - | - | -| USE_TASMOTA_SLAVE | - | - | - | - | - | - | - | +| USE_TASMOTA_CLIENT | - | - | - | - | - | - | - | | USE_OPENTHERM | - | - | - | - | - | - | - | +| USE_TCP_BRIDGE | - | - | - | - | - | - | - | zbbridge | | | | | | | | | | USE_NRF24 | - | - | - | - | - | - | - | | USE_MIBLE | - | - | - | - | - | - | - | @@ -157,6 +162,7 @@ | USE_IR_REMOTE_FULL | - | - | - | - | - | x | - | Enable ALL protocols | | | | | | | | | | USE_SR04 | - | - | - | - | x | - | - | +| USE_DYP | - | - | - | - | - | - | - | | USE_TM1638 | - | - | - | - | x | - | - | | USE_HX711 | - | - | - | - | x | - | - | | USE_TX2x_WIND_SENSOR | - | - | - | - | - | - | - | @@ -186,3 +192,5 @@ | USE_MI_ESP32 | - | - | - | - | - | - | - | - | | USE_WEBCAM | - | - | - | - | - | - | - | x | | USE_ETHERNET | - | - | - | - | - | - | - | - | +| USE_I2S_AUDIO | - | - | - | - | - | - | - | - | +| USE_TTGO_WATCH | - | - | - | - | - | - | - | - | diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 76c63cbca..3703517fc 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -21,7 +21,7 @@ While fallback or downgrading is common practice it was never supported due to S ## Supported Core versions -This release will be supported from ESP8266/Arduino library Core version **2.7.2.1** due to reported security and stability issues on previous Core version. This will also support gzipped binaries. +This release will be supported from ESP8266/Arduino library Core version **2.7.4.1** due to reported security and stability issues on previous Core version. This will also support gzipped binaries. Support of Core versions before 2.7.1 has been removed. @@ -35,7 +35,7 @@ For initial configuration this release supports Webserver based **WifiManager** ## Provided Binary Downloads -The following binary downloads have been compiled with ESP8266/Arduino library core version **2.7.2.1**. +The following binary downloads have been compiled with ESP8266/Arduino library core version **2.7.4.1**. - **tasmota.bin** = The Tasmota version with most drivers. **RECOMMENDED RELEASE BINARY** - **tasmota-BG.bin** to **tasmota-TW.bin** = The Tasmota version in different languages. @@ -55,19 +55,27 @@ The following binary downloads have been compiled with ESP8266/Arduino library c ### Version 8.4.0.3 -- Remove support for 1-step upgrade from versions before 6.6.0.11 to versions after 8.4.0.1 -- Change references from http://thehackbox.org to http://ota.tasmota.com -- Change White blend mode moved to using ``SetOption 105`` instead of ``RGBWWTable`` +- Remove support for direct upgrade from versions before 6.6.0.11 to versions after 8.4.0.1 +- Change references from http://thehackbox.org/tasmota/ to http://ota.tasmota.com/tasmota/ +- Change triple-mode TLS via configuration in a single firmware (TLS AWS IoT, Letsencrypt and No-TLS) +- Change White blend mode to using command ``SetOption 105`` instead of ``RGBWWTable`` - Fix ESP32 PWM range - Fix display power control (#9114) -- Add command ``SetOption108 0/1`` to enable Teleinfo telemetry into Tasmota Energy MQTT (0) or Teleinfo only (1) - Add Zigbee better support for IKEA Motion Sensor +- Add command ``SetOption102 0/1`` to set Baud rate for Teleinfo communication (0 = 1200 or 1 = 9600) +- Add command ``SetOption103 0/1`` to set TLS mode when TLS is selected +- Add command ``SetOption104 1`` to disable all MQTT retained messages +- Add command ``SetOption106 1`` to create a virtual White ColorTemp for RGBW lights +- Add command ``SetOption107 0/1`` to select virtual White as (0) Warm or (1) Cold +- Add command ``SetOption108 0/1`` to enable Teleinfo telemetry into Tasmota Energy MQTT (0) or Teleinfo only (1) - Add command ``SetOption109 1`` to force gen1 Alexa mode, for Echo Dot 2nd gen devices only - Add command ``Restart 2`` to halt system. Needs hardware reset or power cycle to restart (#9046) -- Add ESP32 Analog input support for GPIO32 to GPIO39 +- Add command ``PowerDelta1`` to ``PowerDelta3`` to trigger on up to three phases (#9134) - Add Zigbee options to ``ZbSend`` ``Config`` and ``ReadCondig`` -- Add Zigbee web gui widget for Temp/Humidity/Pressure sensors +- Add Zigbee better support for IKEA Motion Sensor +- Add Zigbee web gui widget for Battery and Temp/Humidity/Pressure sensors - Add Zigbee web ui for power metering plugs -- Add better config corruption recovery (#9046) +- Add better configuration corruption recovery (#9046) - Add virtual CT for 4 channels lights, emulating a 5th channel - Add support for DYP ME007 ultrasonic distance sensor by Janusz Kostorz (#9113) -- Add command ``PowerDelta1`` to ``PowerDelta3`` to trigger on up to three phases (#9134) +- Add ESP32 Analog input support for GPIO32 to GPIO39 +- Add experimental support for ESP32 TTGO Watch and I2S Audio by Gerhard Mutz diff --git a/tasmota/CHANGELOG.md b/tasmota/CHANGELOG.md index 4f7b20031..e5967a9a1 100644 --- a/tasmota/CHANGELOG.md +++ b/tasmota/CHANGELOG.md @@ -2,15 +2,16 @@ ### 8.4.0.3 20200823 -- Change references from http://thehackbox.org to http://ota.tasmota.com +- Change references from http://thehackbox.org/tasmota/ to http://ota.tasmota.com/tasmota/ - Add command ``PowerDelta1`` to ``PowerDelta3`` to trigger on up to three phases (#9134) - Add Zigbee web ui widget for Lights - Add ``SetOption109 1`` to force gen1 Alexa mode, for Echo Dot 2nd gen devices only - Add Zigbee web ui for power metering plugs +- Add experimental support for ESP32 TTGO Watch and I2S Audio by Gerhard Mutz ### 8.4.0.2 20200813 -- Remove support for 1-step upgrade from versions before 6.6.0.11 to versions after 8.4.0.1 +- Remove support for direct upgrade from versions before 6.6.0.11 to versions after 8.4.0.1 - Change White blend mode moved to using ``SetOption 105`` instead of ``RGBWWTable`` - Fix display power control (#9114) - Add command ``SetOption108 0/1`` to enable Teleinfo telemetry into Tasmota Energy MQTT (0) or Teleinfo only (1) - Add better config corruption recovery (#9046) diff --git a/tasmota/support_features.ino b/tasmota/support_features.ino index 8707650c0..d7f8328d3 100644 --- a/tasmota/support_features.ino +++ b/tasmota/support_features.ino @@ -598,7 +598,9 @@ void GetFeatures(void) #ifdef USE_DYP feature6 |= 0x00400000; // xsns_76_dyp.ino #endif -// feature6 |= 0x00800000; +#ifdef USE_I2S_AUDIO + feature6 |= 0x00800000; // xdrv_42_i2s_audio.ino +#endif // feature6 |= 0x01000000; // feature6 |= 0x02000000; @@ -606,7 +608,9 @@ void GetFeatures(void) // feature6 |= 0x08000000; // feature6 |= 0x10000000; -// feature6 |= 0x20000000; +#if defined(ESP32) && defined(USE_TTGO_WATCH) + feature6 |= 0x20000000; // xdrv_83_esp32watch.ino +#endif #if defined(ESP32) && defined(USE_ETHERNET) feature6 |= 0x40000000; // xdrv_82_ethernet.ino #endif diff --git a/tools/decode-status.py b/tools/decode-status.py index 876c9eb91..26f7fe900 100755 --- a/tools/decode-status.py +++ b/tools/decode-status.py @@ -155,8 +155,14 @@ a_setoption = [[ "Enable zerocross dimmer on PWM DIMMER", "Remove ZbReceived form JSON message", "Add the source endpoint as suffix to attributes", - "","","","", - "","","","", + "Baud rate for Teleinfo communication (0 = 1200 or 1 = 9600)", + "TLS mode", + "Disable all MQTT retained messages", + "Enable White blend mode", + "Create a virtual White ColorTemp for RGBW lights", + "Select virtual White as (0) Warm or (1) Cold", + "Enable Teleinfo telemetry into Tasmota Energy MQTT (0) or Teleinfo only (1)", + "Force gen1 Alexa mode", "","","","" ],[ "","","","", @@ -220,9 +226,9 @@ a_features = [[ "USE_WINDMETER","USE_OPENTHERM","USE_THERMOSTAT","USE_VEML6075", "USE_VEML7700","USE_MCP9808","USE_BL0940","USE_TELEGRAM", "USE_HP303B","USE_TCP_BRIDGE","USE_TELEINFO","USE_LMT01", - "USE_PROMETHEUS","USE_IEM3000","USE_DYP","", + "USE_PROMETHEUS","USE_IEM3000","USE_DYP","USE_I2S_AUDIO", "","","","", - "","","USE_ETHERNET","USE_WEBCAM" + "","USE_TTGO_WATCH","USE_ETHERNET","USE_WEBCAM" ],[ "","","","", "","","","", @@ -259,7 +265,7 @@ else: obj = json.load(fp) def StartDecode(): - print ("\n*** decode-status.py v20200817 by Theo Arends and Jacek Ziolkowski ***") + print ("\n*** decode-status.py v20200906 by Theo Arends and Jacek Ziolkowski ***") # print("Decoding\n{}".format(obj)) From bac7c6210f04cf112f54c440226fe0f38987d8db Mon Sep 17 00:00:00 2001 From: Federico Leoni Date: Sun, 6 Sep 2020 14:37:27 -0300 Subject: [PATCH 24/55] Bugfix and Prep for new Discovery Correct wrong unsubscribe for HAss lwt, removed the limitation for `Scheme 0` because is not needed anymore. --- tasmota/xdrv_12_home_assistant.ino | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/tasmota/xdrv_12_home_assistant.ino b/tasmota/xdrv_12_home_assistant.ino index 957c9bb29..ef9d25be9 100644 --- a/tasmota/xdrv_12_home_assistant.ino +++ b/tasmota/xdrv_12_home_assistant.ino @@ -849,7 +849,7 @@ void HAssDiscovery(void) Settings.flag3.hass_tele_on_power = 1; // SetOption59 - Send tele/%topic%/STATE in addition to stat/%topic%/RESULT - send tele/STATE message as stat/RESULT // the purpose of that is so that if HA is restarted, state in HA will be correct within one teleperiod otherwise state // will not be correct until the device state is changed this is why in the patterns for switch and light, we tell HA to trigger on STATE, not RESULT. - Settings.light_scheme = 0; // To just control color it needs to be Scheme 0 + //Settings.light_scheme = 0; // To just control color it needs to be Scheme 0 (on hold due to new light configuration) } if (Settings.flag.hass_discovery || (1 == hass_mode)) @@ -940,7 +940,7 @@ void HassLwtSubscribe(bool hasslwt) { char htopic[TOPSZ]; snprintf_P(htopic, sizeof(htopic), PSTR(HOME_ASSISTANT_LWT_TOPIC)); - if (hasslwt) { + if (hasslwt && Settings.flag.hass_discovery) { MqttSubscribe(htopic); } else { MqttUnsubscribe(htopic); } } @@ -984,9 +984,7 @@ bool Xdrv12(uint8_t function) hass_mode = 0; // Discovery only if Settings.flag.hass_discovery is set hass_init_step = 2; // Delayed discovery break; - // if (!Settings.flag.hass_discovery) { - // AddLog_P2(LOG_LEVEL_INFO, PSTR("MQT: homeassistant/49A3BC/Discovery = {\"dev\":{\"ids\":[\"49A3BC\"]},\"cmd_t\":\"cmnd/test1/\",\"Discovery\":0}")); - // } + case FUNC_MQTT_SUBSCRIBE: HassLwtSubscribe(hasslwt); break; From 8d49a4b037a974abea611a2cce03ba18a7f45a67 Mon Sep 17 00:00:00 2001 From: Stephan Hadinger Date: Sun, 6 Sep 2020 20:51:20 +0200 Subject: [PATCH 25/55] Zigbee fixes --- tasmota/xdrv_23_zigbee_1_headers.ino | 17 ++++- tasmota/xdrv_23_zigbee_5_converters.ino | 62 +++++++++++++++++- tasmota/xdrv_23_zigbee_6_commands.ino | 85 ++++-------------------- tasmota/xdrv_23_zigbee_8_parsers.ino | 15 ++++- tasmota/xdrv_23_zigbee_9_serial.ino | 86 ++++++++++++------------- tasmota/xdrv_23_zigbee_A_impl.ino | 50 ++++++++++++-- 6 files changed, 187 insertions(+), 128 deletions(-) diff --git a/tasmota/xdrv_23_zigbee_1_headers.ino b/tasmota/xdrv_23_zigbee_1_headers.ino index 046450927..bb7555757 100644 --- a/tasmota/xdrv_23_zigbee_1_headers.ino +++ b/tasmota/xdrv_23_zigbee_1_headers.ino @@ -21,7 +21,22 @@ // contains some definitions for functions used before their declarations -void ZigbeeZCLSend_Raw(uint16_t dtsAddr, uint16_t groupaddr, uint16_t clusterId, uint8_t endpoint, uint8_t cmdId, bool clusterSpecific, const uint8_t *msg, size_t len, bool needResponse, uint8_t transacId); +class ZigbeeZCLSendMessage { +public: + uint16_t shortaddr; + uint16_t groupaddr; + uint16_t clusterId; + uint8_t endpoint; + uint8_t cmdId; + uint16_t manuf; + bool clusterSpecific; + bool needResponse; + uint8_t transacId; // ZCL transaction number + const uint8_t *msg; + size_t len; +}; + +void ZigbeeZCLSend_Raw(const ZigbeeZCLSendMessage &zcl); // get the result as a string (const char*) and nullptr if there is no field or the string is empty const char * getCaseInsensitiveConstCharNull(const JsonObject &json, const char *needle) { diff --git a/tasmota/xdrv_23_zigbee_5_converters.ino b/tasmota/xdrv_23_zigbee_5_converters.ino index 9c8d10cdf..1155a6f2e 100644 --- a/tasmota/xdrv_23_zigbee_5_converters.ino +++ b/tasmota/xdrv_23_zigbee_5_converters.ino @@ -1028,7 +1028,19 @@ void ZCLFrame::parseReportAttributes(Z_attribute_list& attr_list) { SBuffer buf(2); buf.add8(_cmd_id); buf.add8(0x00); // Status = OK - ZigbeeZCLSend_Raw(_srcaddr, 0x0000, 0x0000 /*cluster*/, _srcendpoint, ZCL_DEFAULT_RESPONSE, false /* not cluster specific */, _manuf_code, buf.getBuffer(), buf.len(), false /* noresponse */, _transact_seq); + + ZigbeeZCLSend_Raw(ZigbeeZCLSendMessage({ + _srcaddr, + 0x0000, + _cluster_id, + _srcendpoint, + ZCL_DEFAULT_RESPONSE, + _manuf_code, + false /* not cluster specific */, + false /* noresponse */, + _transact_seq, /* zcl transaction id */ + buf.getBuffer(), buf.len() + })); } } @@ -1078,6 +1090,48 @@ void ZCLFrame::generateCallBacks(Z_attribute_list& attr_list) { } } + +// A command has been sent to a device this device, or to a group +// Set timers to read back values. +// If it's a device address, also set a timer for reachability test +void sendHueUpdate(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster, uint8_t endpoint = 0) { + int32_t z_cat = -1; + uint32_t wait_ms = 0; + + switch (cluster) { + case 0x0006: + z_cat = Z_CAT_READ_0006; + wait_ms = 200; // wait 0.2 s + break; + case 0x0008: + z_cat = Z_CAT_READ_0008; + wait_ms = 1050; // wait 1.0 s + break; + case 0x0102: + z_cat = Z_CAT_READ_0102; + wait_ms = 10000; // wait 10.0 s + break; + case 0x0300: + z_cat = Z_CAT_READ_0300; + wait_ms = 1050; // wait 1.0 s + break; + default: + break; + } + if (z_cat >= 0) { + if ((BAD_SHORTADDR != shortaddr) && (0 == endpoint)) { + endpoint = zigbee_devices.findFirstEndpoint(shortaddr); + } + if ((BAD_SHORTADDR == shortaddr) || (endpoint)) { // send if group address or endpoint is known + zigbee_devices.setTimer(shortaddr, groupaddr, wait_ms, cluster, endpoint, z_cat, 0 /* value */, &Z_ReadAttrCallback); + if (BAD_SHORTADDR != shortaddr) { // reachability test is not possible for group addresses, since we don't know the list of devices in the group + zigbee_devices.setTimer(shortaddr, groupaddr, wait_ms + Z_CAT_REACHABILITY_TIMEOUT, cluster, endpoint, Z_CAT_REACHABILITY, 0 /* value */, &Z_Unreachable); + } + + } + } +} + // ZCL_READ_ATTRIBUTES void ZCLFrame::parseReadAttributes(Z_attribute_list& attr_list) { uint32_t i = 0; @@ -1248,7 +1302,11 @@ void ZCLFrame::parseResponse(void) { void ZCLFrame::parseClusterSpecificCommand(Z_attribute_list& attr_list) { convertClusterSpecific(attr_list, _cluster_id, _cmd_id, _frame_control.b.direction, _srcaddr, _srcendpoint, _payload); #ifndef USE_ZIGBEE_NO_READ_ATTRIBUTES // read attributes unless disabled - sendHueUpdate(_srcaddr, _groupaddr, _cluster_id, _cmd_id, _frame_control.b.direction); + if (!_frame_control.b.direction) { // only handle server->client (i.e. device->coordinator) + if (_wasbroadcast) { // only update for broadcast messages since we don't see unicast from device to device and we wouldn't know the target + sendHueUpdate(BAD_SHORTADDR, _groupaddr, _cluster_id); + } + } #endif } diff --git a/tasmota/xdrv_23_zigbee_6_commands.ino b/tasmota/xdrv_23_zigbee_6_commands.ino index c533eb8ed..a32f637c4 100644 --- a/tasmota/xdrv_23_zigbee_6_commands.ino +++ b/tasmota/xdrv_23_zigbee_6_commands.ino @@ -174,7 +174,19 @@ int32_t Z_ReadAttrCallback(uint16_t shortaddr, uint16_t groupaddr, uint16_t clus if (groupaddr) { shortaddr = BAD_SHORTADDR; // if group address, don't send to device } - ZigbeeZCLSend_Raw(shortaddr, groupaddr, cluster, endpoint, ZCL_READ_ATTRIBUTES, false, 0, attrs, attrs_len, true /* we do want a response */, zigbee_devices.getNextSeqNumber(shortaddr)); + uint8_t seq = zigbee_devices.getNextSeqNumber(shortaddr); + ZigbeeZCLSend_Raw(ZigbeeZCLSendMessage({ + shortaddr, + groupaddr, + cluster /*cluster*/, + endpoint, + ZCL_READ_ATTRIBUTES, + 0, /* manuf */ + false /* not cluster specific */, + true /* response */, + seq, /* zcl transaction id */ + attrs, attrs_len + })); } return 0; // Fix GCC 10.1 warning } @@ -188,31 +200,6 @@ int32_t Z_Unreachable(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster, return 0; // Fix GCC 10.1 warning } -// set a timer to read back the value in the future -void zigbeeSetCommandTimer(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster, uint8_t endpoint) { - uint32_t wait_ms = 0; - - switch (cluster) { - case 0x0006: // for On/Off - case 0x0009: // for Alamrs - wait_ms = 200; // wait 0.2 s - break; - case 0x0008: // for Dimmer - case 0x0300: // for Color - wait_ms = 1050; // wait 1.0 s - break; - case 0x0102: // for Shutters - wait_ms = 10000; // wait 10.0 s - break; - } - if (wait_ms) { - zigbee_devices.setTimer(shortaddr, groupaddr, wait_ms, cluster, endpoint, Z_CAT_NONE, 0 /* value */, &Z_ReadAttrCallback); - if (BAD_SHORTADDR != shortaddr) { // reachability test is not possible for group addresses, since we don't know the list of devices in the group - zigbee_devices.setTimer(shortaddr, groupaddr, wait_ms + Z_CAT_REACHABILITY_TIMEOUT, cluster, endpoint, Z_CAT_REACHABILITY, 0 /* value */, &Z_Unreachable); - } - } -} - // returns true if char is 'x', 'y' or 'z' inline bool isXYZ(char c) { return (c >= 'x') && (c <= 'z'); @@ -280,52 +267,6 @@ void parseXYZ(const char *model, const SBuffer &payload, struct Z_XYZ_Var *xyz) } } -// works on big endiand hex only -// Returns if found: -// - cluster number -// - command number or 0xFF if command is part of the variable part -// - the payload in the form of a HEX string with x/y/z variables -void sendHueUpdate(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster, uint8_t cmd, bool direction) { - if (direction) { return; } // no need to update if server->client - - int32_t z_cat = -1; - uint32_t wait_ms = 0; - - switch (cluster) { - case 0x0006: - z_cat = Z_CAT_READ_0006; - wait_ms = 200; // wait 0.2 s - break; - case 0x0008: - z_cat = Z_CAT_READ_0008; - wait_ms = 1050; // wait 1.0 s - break; - case 0x0102: - z_cat = Z_CAT_READ_0102; - wait_ms = 10000; // wait 10.0 s - break; - case 0x0300: - z_cat = Z_CAT_READ_0300; - wait_ms = 1050; // wait 1.0 s - break; - default: - break; - } - if (z_cat >= 0) { - uint8_t endpoint = 0; - if (BAD_SHORTADDR != shortaddr) { - endpoint = zigbee_devices.findFirstEndpoint(shortaddr); - } - if ((BAD_SHORTADDR == shortaddr) || (endpoint)) { // send if group address or endpoint is known - zigbee_devices.setTimer(shortaddr, groupaddr, wait_ms, cluster, endpoint, z_cat, 0 /* value */, &Z_ReadAttrCallback); - if (BAD_SHORTADDR != shortaddr) { // reachability test is not possible for group addresses, since we don't know the list of devices in the group - zigbee_devices.setTimer(shortaddr, groupaddr, wait_ms + Z_CAT_REACHABILITY_TIMEOUT, cluster, endpoint, Z_CAT_REACHABILITY, 0 /* value */, &Z_Unreachable); - } - - } - } -} - // Parse a cluster specific command, and try to convert into human readable void convertClusterSpecific(class Z_attribute_list &attr_list, uint16_t cluster, uint8_t cmd, bool direction, uint16_t shortaddr, uint8_t srcendpoint, const SBuffer &payload) { diff --git a/tasmota/xdrv_23_zigbee_8_parsers.ino b/tasmota/xdrv_23_zigbee_8_parsers.ino index 1f5d33a8f..d800eaf71 100644 --- a/tasmota/xdrv_23_zigbee_8_parsers.ino +++ b/tasmota/xdrv_23_zigbee_8_parsers.ino @@ -969,9 +969,18 @@ void Z_SendAFInfoRequest(uint16_t shortaddr) { uint8_t InfoReq[] = { 0x04, 0x00, 0x05, 0x00 }; - ZigbeeZCLSend_Raw(shortaddr, 0x0000 /*group*/, 0x0000 /*cluster*/, endpoint, ZCL_READ_ATTRIBUTES, - false /*clusterSpecific*/, 0x0000 /*manuf*/, - InfoReq, sizeof(InfoReq), true /*needResponse*/, transacid); + ZigbeeZCLSend_Raw(ZigbeeZCLSendMessage({ + shortaddr, + 0x0000, /* group */ + 0x0000 /*cluster*/, + endpoint, + ZCL_READ_ATTRIBUTES, + 0x0000, /* manuf */ + false /* not cluster specific */, + true /* response */, + transacid, /* zcl transaction id */ + InfoReq, sizeof(InfoReq) + })); } diff --git a/tasmota/xdrv_23_zigbee_9_serial.ino b/tasmota/xdrv_23_zigbee_9_serial.ino index 3fdddfedb..f5056c58c 100644 --- a/tasmota/xdrv_23_zigbee_9_serial.ino +++ b/tasmota/xdrv_23_zigbee_9_serial.ino @@ -765,96 +765,96 @@ void CmndZbEZSPSend(void) // - transacId: 8-bits, transation id of message (should be incremented at each message), used both for Zigbee message number and ZCL message number // Returns: None // -void ZigbeeZCLSend_Raw(uint16_t shortaddr, uint16_t groupaddr, uint16_t clusterId, uint8_t endpoint, uint8_t cmdId, bool clusterSpecific, uint16_t manuf, const uint8_t *msg, size_t len, bool needResponse, uint8_t transacId) { +void ZigbeeZCLSend_Raw(const ZigbeeZCLSendMessage &zcl) { #ifdef USE_ZIGBEE_ZNP - SBuffer buf(32+len); + SBuffer buf(32+zcl.len); buf.add8(Z_SREQ | Z_AF); // 24 buf.add8(AF_DATA_REQUEST_EXT); // 02 - if (BAD_SHORTADDR == shortaddr) { // if no shortaddr we assume group address + if (BAD_SHORTADDR == zcl.shortaddr) { // if no shortaddr we assume group address buf.add8(Z_Addr_Group); // 01 - buf.add64(groupaddr); // group address, only 2 LSB, upper 6 MSB are discarded + buf.add64(zcl.groupaddr); // group address, only 2 LSB, upper 6 MSB are discarded buf.add8(0xFF); // dest endpoint is not used for group addresses } else { buf.add8(Z_Addr_ShortAddress); // 02 - buf.add64(shortaddr); // dest address, only 2 LSB, upper 6 MSB are discarded - buf.add8(endpoint); // dest endpoint + buf.add64(zcl.shortaddr); // dest address, only 2 LSB, upper 6 MSB are discarded + buf.add8(zcl.endpoint); // dest endpoint } buf.add16(0x0000); // dest Pan ID, 0x0000 = intra-pan buf.add8(0x01); // source endpoint - buf.add16(clusterId); - buf.add8(transacId); // transacId + buf.add16(zcl.clusterId); + buf.add8(zcl.transacId); // transacId buf.add8(0x30); // 30 options buf.add8(0x1E); // 1E radius - buf.add16(3 + len + (manuf ? 2 : 0)); - buf.add8((needResponse ? 0x00 : 0x10) | (clusterSpecific ? 0x01 : 0x00) | (manuf ? 0x04 : 0x00)); // Frame Control Field - if (manuf) { - buf.add16(manuf); // add Manuf Id if not null + buf.add16(3 + zcl.len + (zcl.manuf ? 2 : 0)); + buf.add8((zcl.needResponse ? 0x00 : 0x10) | (zcl.clusterSpecific ? 0x01 : 0x00) | (zcl.manuf ? 0x04 : 0x00)); // Frame Control Field + if (zcl.manuf) { + buf.add16(zcl.manuf); // add Manuf Id if not null } - buf.add8(transacId); // Transaction Sequence Number - buf.add8(cmdId); - if (len > 0) { - buf.addBuffer(msg, len); // add the payload + buf.add8(zcl.transacId); // Transaction Sequence Number + buf.add8(zcl.cmdId); + if (zcl.len > 0) { + buf.addBuffer(zcl.msg, zcl.len); // add the payload } ZigbeeZNPSend(buf.getBuffer(), buf.len()); #endif // USE_ZIGBEE_ZNP #ifdef USE_ZIGBEE_EZSP - SBuffer buf(32+len); + SBuffer buf(32+zcl.len); - if (BAD_SHORTADDR != shortaddr) { + if (BAD_SHORTADDR != zcl.shortaddr) { // send unicast message to an address buf.add16(EZSP_sendUnicast); // 3400 buf.add8(EMBER_OUTGOING_DIRECT); // 00 - buf.add16(shortaddr); // dest addr + buf.add16(zcl.shortaddr); // dest addr // ApsFrame buf.add16(Z_PROF_HA); // Home Automation profile - buf.add16(clusterId); // cluster + buf.add16(zcl.clusterId); // cluster buf.add8(0x01); // srcEp - buf.add8(endpoint); // dstEp + buf.add8(zcl.endpoint); // dstEp buf.add16(EMBER_APS_OPTION_ENABLE_ROUTE_DISCOVERY | EMBER_APS_OPTION_RETRY); // APS frame - buf.add16(groupaddr); // groupId - buf.add8(transacId); + buf.add16(zcl.groupaddr); // groupId + buf.add8(zcl.transacId); // end of ApsFrame buf.add8(0x01); // tag TODO - buf.add8(3 + len + (manuf ? 2 : 0)); - buf.add8((needResponse ? 0x00 : 0x10) | (clusterSpecific ? 0x01 : 0x00) | (manuf ? 0x04 : 0x00)); // Frame Control Field - if (manuf) { - buf.add16(manuf); // add Manuf Id if not null + buf.add8(3 + zcl.len + (zcl.manuf ? 2 : 0)); + buf.add8((zcl.needResponse ? 0x00 : 0x10) | (zcl.clusterSpecific ? 0x01 : 0x00) | (zcl.manuf ? 0x04 : 0x00)); // Frame Control Field + if (zcl.manuf) { + buf.add16(zcl.manuf); // add Manuf Id if not null } - buf.add8(transacId); // Transaction Sequance Number - buf.add8(cmdId); - if (len > 0) { - buf.addBuffer(msg, len); // add the payload + buf.add8(zcl.transacId); // Transaction Sequance Number + buf.add8(zcl.cmdId); + if (zcl.len > 0) { + buf.addBuffer(zcl.msg, zcl.len); // add the payload } } else { // send broadcast group address, aka groupcast buf.add16(EZSP_sendMulticast); // 3800 // ApsFrame buf.add16(Z_PROF_HA); // Home Automation profile - buf.add16(clusterId); // cluster + buf.add16(zcl.clusterId); // cluster buf.add8(0x01); // srcEp - buf.add8(endpoint); // broadcast endpoint for groupcast + buf.add8(zcl.endpoint); // broadcast endpoint for groupcast buf.add16(EMBER_APS_OPTION_ENABLE_ROUTE_DISCOVERY | EMBER_APS_OPTION_RETRY); // APS frame - buf.add16(groupaddr); // groupId - buf.add8(transacId); + buf.add16(zcl.groupaddr); // groupId + buf.add8(zcl.transacId); // end of ApsFrame buf.add8(0); // hops, 0x00 = EMBER_MAX_HOPS buf.add8(7); // nonMemberRadius, 7 = infinite buf.add8(0x01); // tag TODO - buf.add8(3 + len + (manuf ? 2 : 0)); - buf.add8((needResponse ? 0x00 : 0x10) | (clusterSpecific ? 0x01 : 0x00) | (manuf ? 0x04 : 0x00)); // Frame Control Field - if (manuf) { - buf.add16(manuf); // add Manuf Id if not null + buf.add8(3 + zcl.len + (zcl.manuf ? 2 : 0)); + buf.add8((zcl.needResponse ? 0x00 : 0x10) | (zcl.clusterSpecific ? 0x01 : 0x00) | (zcl.manuf ? 0x04 : 0x00)); // Frame Control Field + if (zcl.manuf) { + buf.add16(zcl.manuf); // add Manuf Id if not null } - buf.add8(transacId); // Transaction Sequance Number - buf.add8(cmdId); - if (len > 0) { - buf.addBuffer(msg, len); // add the payload + buf.add8(zcl.transacId); // Transaction Sequance Number + buf.add8(zcl.cmdId); + if (zcl.len > 0) { + buf.addBuffer(zcl.msg, zcl.len); // add the payload } } diff --git a/tasmota/xdrv_23_zigbee_A_impl.ino b/tasmota/xdrv_23_zigbee_A_impl.ino index 4dd7e99ad..fff151af6 100644 --- a/tasmota/xdrv_23_zigbee_A_impl.ino +++ b/tasmota/xdrv_23_zigbee_A_impl.ino @@ -139,7 +139,6 @@ void CmndZbReset(void) { // High-level function // Send a command specified as an HEX string for the workload. // The target endpoint is computed if zero, i.e. sent to the first known endpoint of the device. -// If cluster-specific, a timer may be set calling `zigbeeSetCommandTimer()`, for ex to coalesce attributes or Aqara presence sensor // // Inputs: // - shortaddr: 16-bits short address, or 0x0000 if group address @@ -176,11 +175,24 @@ void zigbeeZCLSendStr(uint16_t shortaddr, uint16_t groupaddr, uint8_t endpoint, } // everything is good, we can send the command - ZigbeeZCLSend_Raw(shortaddr, groupaddr, cluster, endpoint, cmd, clusterSpecific, manuf, buf.getBuffer(), buf.len(), true, zigbee_devices.getNextSeqNumber(shortaddr)); + + uint8_t seq = zigbee_devices.getNextSeqNumber(shortaddr); + ZigbeeZCLSend_Raw(ZigbeeZCLSendMessage({ + shortaddr, + groupaddr, + cluster /*cluster*/, + endpoint, + cmd, + manuf, /* manuf */ + clusterSpecific /* not cluster specific */, + true /* response */, + seq, /* zcl transaction id */ + buf.getBuffer(), buf.len() + })); // now set the timer, if any, to read back the state later if (clusterSpecific) { #ifndef USE_ZIGBEE_NO_READ_ATTRIBUTES // read back attribute value unless it is disabled - zigbeeSetCommandTimer(shortaddr, groupaddr, cluster, endpoint); + sendHueUpdate(shortaddr, groupaddr, cluster, endpoint); #endif } } @@ -202,7 +214,7 @@ void ZbApplyMultiplier(double &val_d, int8_t multiplier) { // Parse "Report", "Write", "Response" or "Condig" attribute // Operation is one of: ZCL_REPORT_ATTRIBUTES (0x0A), ZCL_WRITE_ATTRIBUTES (0x02) or ZCL_READ_ATTRIBUTES_RESPONSE (0x01) -void ZbSendReportWrite(const JsonObject &val_pubwrite, uint16_t device, uint16_t groupaddr, uint16_t cluster, uint8_t endpoint, uint16_t manuf, uint32_t operation) { +void ZbSendReportWrite(const JsonObject &val_pubwrite, uint16_t device, uint16_t groupaddr, uint16_t cluster, uint8_t endpoint, uint16_t manuf, uint8_t operation) { SBuffer buf(200); // buffer to store the binary output of attibutes if (nullptr == XdrvMailbox.command) { @@ -376,7 +388,19 @@ void ZbSendReportWrite(const JsonObject &val_pubwrite, uint16_t device, uint16_t } // all good, send the packet - ZigbeeZCLSend_Raw(device, groupaddr, cluster, endpoint, operation, false /* not cluster specific */, manuf, buf.getBuffer(), buf.len(), false /* noresponse */, zigbee_devices.getNextSeqNumber(device)); + uint8_t seq = zigbee_devices.getNextSeqNumber(device); + ZigbeeZCLSend_Raw(ZigbeeZCLSendMessage({ + device, + groupaddr, + cluster /*cluster*/, + endpoint, + operation, + manuf, /* manuf */ + false /* not cluster specific */, + false /* no response */, + seq, /* zcl transaction id */ + buf.getBuffer(), buf.len() + })); ResponseCmndDone(); } @@ -510,7 +534,7 @@ void ZbSendSend(const JsonVariant &val_cmd, uint16_t device, uint16_t groupaddr, // Parse the "Send" attribute and send the command -void ZbSendRead(const JsonVariant &val_attr, uint16_t device, uint16_t groupaddr, uint16_t cluster, uint8_t endpoint, uint16_t manuf, uint32_t operation) { +void ZbSendRead(const JsonVariant &val_attr, uint16_t device, uint16_t groupaddr, uint16_t cluster, uint8_t endpoint, uint16_t manuf, uint8_t operation) { // ZbSend {"Device":"0xF289","Cluster":0,"Endpoint":3,"Read":5} // ZbSend {"Device":"0xF289","Cluster":"0x0000","Endpoint":"0x0003","Read":"0x0005"} // ZbSend {"Device":"0xF289","Cluster":0,"Endpoint":3,"Read":[5,6,7,4]} @@ -602,7 +626,19 @@ void ZbSendRead(const JsonVariant &val_attr, uint16_t device, uint16_t groupaddr } if (attrs_len > 0) { - ZigbeeZCLSend_Raw(device, groupaddr, cluster, endpoint, operation, false, manuf, attrs, attrs_len, true /* we do want a response */, zigbee_devices.getNextSeqNumber(device)); + uint8_t seq = zigbee_devices.getNextSeqNumber(device); + ZigbeeZCLSend_Raw(ZigbeeZCLSendMessage({ + device, + groupaddr, + cluster /*cluster*/, + endpoint, + operation, + manuf, /* manuf */ + false /* not cluster specific */, + true /* response */, + seq, /* zcl transaction id */ + attrs, attrs_len + })); ResponseCmndDone(); } else { ResponseCmndChar_P(PSTR("Missing parameters")); From 5176e3ab02ffab614e1fb1a52e55b06694ed1fe7 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Mon, 7 Sep 2020 14:54:31 +0200 Subject: [PATCH 26/55] Bump version to 8.5.0.1 --- RELEASENOTES.md | 27 +-------------------------- tasmota/CHANGELOG.md | 14 ++++++++++++++ tasmota/tasmota_version.h | 2 +- 3 files changed, 16 insertions(+), 27 deletions(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 3703517fc..8c95d47b5 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -53,29 +53,4 @@ The following binary downloads have been compiled with ESP8266/Arduino library c ## Changelog -### Version 8.4.0.3 - -- Remove support for direct upgrade from versions before 6.6.0.11 to versions after 8.4.0.1 -- Change references from http://thehackbox.org/tasmota/ to http://ota.tasmota.com/tasmota/ -- Change triple-mode TLS via configuration in a single firmware (TLS AWS IoT, Letsencrypt and No-TLS) -- Change White blend mode to using command ``SetOption 105`` instead of ``RGBWWTable`` -- Fix ESP32 PWM range -- Fix display power control (#9114) -- Add command ``SetOption102 0/1`` to set Baud rate for Teleinfo communication (0 = 1200 or 1 = 9600) -- Add command ``SetOption103 0/1`` to set TLS mode when TLS is selected -- Add command ``SetOption104 1`` to disable all MQTT retained messages -- Add command ``SetOption106 1`` to create a virtual White ColorTemp for RGBW lights -- Add command ``SetOption107 0/1`` to select virtual White as (0) Warm or (1) Cold -- Add command ``SetOption108 0/1`` to enable Teleinfo telemetry into Tasmota Energy MQTT (0) or Teleinfo only (1) -- Add command ``SetOption109 1`` to force gen1 Alexa mode, for Echo Dot 2nd gen devices only -- Add command ``Restart 2`` to halt system. Needs hardware reset or power cycle to restart (#9046) -- Add command ``PowerDelta1`` to ``PowerDelta3`` to trigger on up to three phases (#9134) -- Add Zigbee options to ``ZbSend`` ``Config`` and ``ReadCondig`` -- Add Zigbee better support for IKEA Motion Sensor -- Add Zigbee web gui widget for Battery and Temp/Humidity/Pressure sensors -- Add Zigbee web ui for power metering plugs -- Add better configuration corruption recovery (#9046) -- Add virtual CT for 4 channels lights, emulating a 5th channel -- Add support for DYP ME007 ultrasonic distance sensor by Janusz Kostorz (#9113) -- Add ESP32 Analog input support for GPIO32 to GPIO39 -- Add experimental support for ESP32 TTGO Watch and I2S Audio by Gerhard Mutz +### Version 8.5.0.1 diff --git a/tasmota/CHANGELOG.md b/tasmota/CHANGELOG.md index e5967a9a1..0f5b4ec47 100644 --- a/tasmota/CHANGELOG.md +++ b/tasmota/CHANGELOG.md @@ -1,5 +1,15 @@ +## Released + ## Unreleased (development) +### 8.5.0.1 20200907 + +- New released + +### 8.5.0 20200907 + +- Release Hannah + ### 8.4.0.3 20200823 - Change references from http://thehackbox.org/tasmota/ to http://ota.tasmota.com/tasmota/ @@ -14,6 +24,10 @@ - Remove support for direct upgrade from versions before 6.6.0.11 to versions after 8.4.0.1 - Change White blend mode moved to using ``SetOption 105`` instead of ``RGBWWTable`` - Fix display power control (#9114) +- Add command ``SetOption103 0/1`` to set TLS mode when TLS is selected +- Add command ``SetOption104 1`` to disable all MQTT retained messages +- Add command ``SetOption106 1`` to create a virtual White ColorTemp for RGBW lights +- Add command ``SetOption107 0/1`` to select virtual White as (0) Warm or (1) Cold - Add command ``SetOption108 0/1`` to enable Teleinfo telemetry into Tasmota Energy MQTT (0) or Teleinfo only (1) - Add better config corruption recovery (#9046) - Add virtual CT for 4 channels lights, emulating a 5th channel - Add support for DYP ME007 ultrasonic distance sensor by Janusz Kostorz (#9113) diff --git a/tasmota/tasmota_version.h b/tasmota/tasmota_version.h index ce7e073c2..3103534c2 100644 --- a/tasmota/tasmota_version.h +++ b/tasmota/tasmota_version.h @@ -20,7 +20,7 @@ #ifndef _TASMOTA_VERSION_H_ #define _TASMOTA_VERSION_H_ -const uint32_t VERSION = 0x08040003; +const uint32_t VERSION = 0x08050001; // Lowest compatible version const uint32_t VERSION_COMPATIBLE = 0x07010006; From 654f9de322e89ee6743244adff0a50905dfd0d9e Mon Sep 17 00:00:00 2001 From: Jason2866 <24528715+Jason2866@users.noreply.github.com> Date: Mon, 7 Sep 2020 17:16:23 +0200 Subject: [PATCH 27/55] espressif32@2.0.0 --- platformio_tasmota32.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platformio_tasmota32.ini b/platformio_tasmota32.ini index feca8c849..549f2e333 100644 --- a/platformio_tasmota32.ini +++ b/platformio_tasmota32.ini @@ -2,7 +2,7 @@ ; *** expect the unexpected. Some features not working!!! *** [common32] -platform = espressif32@1.12.4 +platform = espressif32@2.0.0 platform_packages = tool-esptoolpy@1.20800.0 board = esp32dev board_build.ldscript = esp32_out.ld From 72afb156017447c9a566eed3ce8b24a94f595c6e Mon Sep 17 00:00:00 2001 From: stefanbode Date: Tue, 8 Sep 2020 19:34:10 +0200 Subject: [PATCH 28/55] Fixes on Stepper + Servo - fix restart bug on servo - refactor smooth-ramp for stepper and servo. Now in RTC and much more stable - increased internal resolution to better work with ramps - testing, testing, testing.... --- tasmota/xdrv_27_shutter.ino | 208 ++++++++++++++++++++---------------- 1 file changed, 113 insertions(+), 95 deletions(-) diff --git a/tasmota/xdrv_27_shutter.ino b/tasmota/xdrv_27_shutter.ino index 9b1b33027..439800ac2 100644 --- a/tasmota/xdrv_27_shutter.ino +++ b/tasmota/xdrv_27_shutter.ino @@ -30,14 +30,22 @@ #define D_SHUTTER "SHUTTER" const uint16_t MOTOR_STOP_TIME = 500; // in mS -const uint8_t steps_per_second = 20; // FUNC_EVERY_50_MSECOND +const uint16_t RESOLUTION = 1000; +const uint8_t STEPS_PER_SECOND = 20; // FUNC_EVERY_50_MSECOND const uint16_t pwm_max = 500; const uint16_t pwm_min = 90; uint8_t calibrate_pos[6] = {0,30,50,70,90,100}; uint16_t messwerte[5] = {30,50,70,90,100}; uint16_t last_execute_step; -int32_t stop_position_delta = 20; + +int32_t max_velocity = 0; +int32_t max_velocity_change_per_step = 0; +int32_t min_runtime_ms = 0; +int32_t minstopway = 0; +int32_t next_possible_stop = 0; +int32_t toBeAcc = 0; + const uint8_t MAX_MODES = 7; enum ShutterPositionMode {SHT_UNDEF, SHT_TIME, SHT_TIME_UP_DOWN, SHT_TIME_GARAGE, SHT_COUNTER, SHT_PWM_VALUE, SHT_PWM_TIME,}; @@ -98,8 +106,8 @@ struct SHUTTER { void ShutterLogPos(uint32_t i) { char stemp2[10]; - dtostrfd((float)Shutter.time[i] / steps_per_second, 2, stemp2); - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Shutter%d Real %d, Start %d, Stop %d, Dir %d, Delay %d, Rtc %s [s], Freq %d, PWM %d"), + dtostrfd((float)Shutter.time[i] / STEPS_PER_SECOND, 2, stemp2); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Shutter %d Real %d, Start %d, Stop %d, Dir %d, Delay %d, Rtc %s [s], Freq %d, PWM %d"), i+1, Shutter.real_position[i], Shutter.start_position[i], Shutter.target_position[i], Shutter.direction[i], Shutter.motordelay[i], stemp2, Shutter.pwm_velocity[i], Shutter.pwm_value[i]); } @@ -111,30 +119,35 @@ void ExecuteCommandPowerShutter(uint32_t device, uint32_t state, uint32_t source void ShutterUpdateVelocity(uint8_t i) { Shutter.pwm_velocity[i] += Shutter.accelerator[i]; - Shutter.pwm_velocity[i] = tmax(1,tmin(Shutter.direction[i]==1 ? Shutter.max_pwm_velocity : Shutter.max_close_pwm_velocity[i],Shutter.pwm_velocity[i])); + Shutter.pwm_velocity[i] = tmax(0,tmin(Shutter.direction[i]==1 ? Shutter.max_pwm_velocity : Shutter.max_close_pwm_velocity[i],Shutter.pwm_velocity[i])); } void ShutterRtc50mS(void) { for (uint8_t i = 0; i < shutters_present; i++) { - Shutter.time[i]++; - switch (Shutter.PositionMode) { - case SHT_PWM_VALUE: - if (Shutter.accelerator[i]) ShutterUpdateVelocity(i); - Shutter.real_position[i] += Shutter.direction[i] > 0 ? Shutter.pwm_velocity[i] : (Shutter.direction[i] < 0 ? -Shutter.pwm_velocity[i] : 0); - Shutter.pwm_value[i] = SHT_DIV_ROUND((Shutter.pwm_max[i]-Shutter.pwm_min[i]) * Shutter.real_position[i] , Shutter.open_max[i])+Shutter.pwm_min[i]; - analogWrite(Pin(GPIO_PWM1, i), Shutter.pwm_value[i]); - break; - - case SHT_COUNTER: - if (Shutter.accelerator[i]) { - //AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: accelerator i=%d -> %d"),i, Shutter.accelerator[i]); + if (Shutter.direction[i]) { + // update position data before increasing counter + Shutter.real_position[i] = ShutterCalculatePosition(i); + Shutter.time[i]++; + ShutterCalculateAccelerator(i); + switch (Shutter.PositionMode) { + case SHT_PWM_VALUE: ShutterUpdateVelocity(i); - analogWriteFreq(Shutter.pwm_velocity[i]); - analogWrite(Pin(GPIO_PWM1, i), 50); - } - break; - } + Shutter.real_position[i] += Shutter.direction[i] > 0 ? Shutter.pwm_velocity[i] : (Shutter.direction[i] < 0 ? -Shutter.pwm_velocity[i] : 0); + Shutter.pwm_value[i] = SHT_DIV_ROUND((Shutter.pwm_max[i]-Shutter.pwm_min[i]) * Shutter.real_position[i] , Shutter.open_max[i])+Shutter.pwm_min[i]; + analogWrite(Pin(GPIO_PWM1, i), Shutter.pwm_value[i]); + break; + + case SHT_COUNTER: + if (Shutter.accelerator[i]) { + //AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: accelerator i=%d -> %d"),i, Shutter.accelerator[i]); + ShutterUpdateVelocity(i); + analogWriteFreq(Shutter.pwm_velocity[i]); + analogWrite(Pin(GPIO_PWM1, i), 50); + } + break; + } + } // if (Shutter.direction[i]) } } @@ -266,14 +279,14 @@ void ShutterInit(void) Shutter.open_time[i] = (Settings.shutter_opentime[i] > 0) ? Settings.shutter_opentime[i] : 100; Shutter.close_time[i] = (Settings.shutter_closetime[i] > 0) ? Settings.shutter_closetime[i] : 100; + //temporary hard coded. Shutter.pwm_min[i] = pwm_min; Shutter.pwm_max[i] = pwm_max; - // Update Calculation 20 because time interval is 0.05 sec - Shutter.open_max[i] = 200 * Shutter.open_time[i]; + // Update Calculation 20 because time interval is 0.05 sec ans time is in 0.1sec + Shutter.open_max[i] = STEPS_PER_SECOND * RESOLUTION * Shutter.open_time[i] / 10; Shutter.close_velocity[i] = Shutter.open_max[i] / Shutter.close_time[i] / 2 ; - // calculate a ramp slope at the first 5 percent to compensate that shutters move with down part later than the upper part if (Settings.shutter_set50percent[i] != 50) { Settings.shuttercoeff[1][i] = Shutter.open_max[i] * (100 - Settings.shutter_set50percent[i] ) / 5000; @@ -283,19 +296,19 @@ void ShutterInit(void) Shutter.mask |= 3 << (Settings.shutter_startrelay[i] -1); Shutter.real_position[i] = ShutterPercentToRealPosition(Settings.shutter_position[i], i); - //Shutter.real_position[i] = Settings.shutter_position[i] <= 5 ? Settings.shuttercoeff[2][i] * Settings.shutter_position[i] : Settings.shuttercoeff[1][i] * Settings.shutter_position[i] + Settings.shuttercoeff[0,i]; + Shutter.start_position[i] = Shutter.target_position[i] = Shutter.real_position[i]; Shutter.motordelay[i] = Settings.shutter_motordelay[i]; Shutter.lastdirection[i] = (50 < Settings.shutter_position[i]) ? 1 : -1; switch (Shutter.PositionMode) { - case SHT_COUNTER: case SHT_PWM_VALUE: - Shutter.max_close_pwm_velocity[i] = Shutter.max_pwm_velocity*Shutter.open_time[i] / Shutter.close_time[i]; + Shutter.max_pwm_velocity = RESOLUTION; break; } + Shutter.max_close_pwm_velocity[i] = Shutter.max_pwm_velocity*Shutter.open_time[i] / Shutter.close_time[i]; - //AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Shutter %d Closevel: %d"),i, Shutter.max_close_pwm_velocity[i]); + //AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Shutter %d Openvel %d, Closevel: %d"),i, Shutter.max_pwm_velocity, Shutter.max_close_pwm_velocity[i]); AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT%d: Init. Pos: %d,inverted %d, locked %d, end stop time enabled %d, webButtons inverted %d"), i+1, Shutter.real_position[i], (Settings.shutter_options[i]&1) ? 1 : 0, (Settings.shutter_options[i]&2) ? 1 : 0, (Settings.shutter_options[i]&4) ? 1 : 0, (Settings.shutter_options[i]&8) ? 1 : 0); @@ -349,40 +362,35 @@ void ShutterLimitRealAndTargetPositions(uint32_t i) { void ShutterCalculateAccelerator(uint8_t i) { - switch (Shutter.PositionMode) { - case SHT_COUNTER: - case SHT_PWM_VALUE: - int32_t max_velocity = Shutter.direction[i] == 1 ? Shutter.max_pwm_velocity : Shutter.max_close_pwm_velocity[i]; - int32_t max_velocity_change_per_step = Shutter.max_pwm_velocity / (Shutter.motordelay[i]>0 ? Shutter.motordelay[i] : 1); - int32_t min_runtime_ms = Shutter.pwm_velocity[i] * 1000 / steps_per_second / max_velocity_change_per_step; - //int32_t velocity = Shutter.direction[i] == 1 ? 100 : Shutter.close_velocity[i]; - int32_t minstopway = (min_runtime_ms * (Shutter.pwm_velocity[i]+max_velocity_change_per_step)/100 - Shutter.pwm_velocity[i]) * Shutter.direction[i] ; - int32_t toBeAcc = 0; - int32_t next_possible_stop = Shutter.real_position[i] + minstopway ; - stop_position_delta = Shutter.direction[i] * (next_possible_stop - Shutter.target_position[i]); - if (Shutter.PositionMode == SHT_COUNTER) { - // ToDo need to check the influence of frequency and speed on the secure area to stop right in time. - // seems currently only work with motordelay but not ok without motordelay. - stop_position_delta =+ 200 * Shutter.pwm_velocity[i]/max_velocity; - } else { - stop_position_delta =+ Shutter.pwm_velocity[i]-max_velocity_change_per_step; - } + if (Shutter.direction[i] != 0) { + switch (Shutter.PositionMode) { + case SHT_COUNTER: + case SHT_PWM_VALUE: + // calculate max velocity allowed in this direction + max_velocity = Shutter.direction[i] == 1 ? Shutter.max_pwm_velocity : Shutter.max_close_pwm_velocity[i]; + // calculate max change of velocyty based on the defined motordelay in steps + max_velocity_change_per_step = max_velocity / (Shutter.motordelay[i]>0 ? Shutter.motordelay[i] : 1); + // minimumtime required from current velocity to stop + min_runtime_ms = Shutter.pwm_velocity[i] * 1000 / STEPS_PER_SECOND / max_velocity_change_per_step; + // decellartion way from current velocity + minstopway = (min_runtime_ms * (Shutter.pwm_velocity[i]+max_velocity_change_per_step)/100 - Shutter.pwm_velocity[i])*RESOLUTION/Shutter.max_pwm_velocity * Shutter.direction[i] ; + next_possible_stop = Shutter.real_position[i] + minstopway ; + toBeAcc = 0; + // ensure that accelerator kicks in IN TIME and that STOP procedure kicks in at least ONE step before reach end position. + //Shutter.accelerator[i] = tmin(tmax(max_velocity_change_per_step*(100-(Shutter.direction[i]*(Shutter.target_position[i]-next_possible_stop) ))/2000 , max_velocity_change_per_step*9/200), max_velocity_change_per_step*11/200); + //int32_t act_freq_change = max_velocity_change_per_step/20; - //Shutter.accelerator[i] = tmin(tmax(max_velocity_change_per_step*(100-(Shutter.direction[i]*(Shutter.target_position[i]-next_possible_stop) ))/2000 , max_velocity_change_per_step*9/200), max_velocity_change_per_step*11/200); - //int32_t act_freq_change = max_velocity_change_per_step/20; - - if (Shutter.accelerator[i] < 0 || next_possible_stop * Shutter.direction[i] > (Shutter.target_position[i]- (stop_position_delta * Shutter.direction[i])) * Shutter.direction[i] ) { - toBeAcc = 100+(Shutter.direction[i]*max_velocity*(next_possible_stop-Shutter.target_position[i])/Shutter.pwm_velocity[i]); - Shutter.accelerator[i] = - tmin(tmax((toBeAcc > 100 ? max_velocity_change_per_step*toBeAcc/100 : max_velocity_change_per_step*toBeAcc/100) , (max_velocity_change_per_step*9/10)-1), (max_velocity_change_per_step*11/10)+1); - //AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Ramp down: acc: %d"), Shutter.accelerator[i]); - } else if ( Shutter.accelerator[i] > 0 && Shutter.pwm_velocity[i] == max_velocity) { - Shutter.accelerator[i] = 0; - } - - AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: time: %d, toBeAcc %d, minstopway %d,cur_vel %d, max_vel %d, act_vel_change %d, min_runtime_ms %d, act.pos %d, next_stop %d, target: %d, stop_position_delta %d, max_vel_change_per_step %d"),Shutter.time[i],toBeAcc,minstopway, - Shutter.pwm_velocity[i],max_velocity, Shutter.accelerator[i],min_runtime_ms,Shutter.real_position[i], next_possible_stop,Shutter.target_position[i],stop_position_delta,max_velocity_change_per_step); - - break; + // ensure that the accelerotor kicks in at least one step BEFORE it is to late and a hard stop required. + if (Shutter.accelerator[i] < 0 || (next_possible_stop * Shutter.direction[i]) +RESOLUTION*Shutter.pwm_velocity[i]/Shutter.max_pwm_velocity>= Shutter.target_position[i] * Shutter.direction[i] ) { + // 10 times the deviation is the value of this simple p-regulator + toBeAcc = 100+(Shutter.direction[i]*(next_possible_stop-Shutter.target_position[i])*max_velocity/Shutter.pwm_velocity[i]*10/RESOLUTION); + Shutter.accelerator[i] = - tmin(tmax( max_velocity_change_per_step*toBeAcc/100 , (max_velocity_change_per_step*9/10)), (max_velocity_change_per_step*11/10)); + //AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Ramp down: acc: %d"), Shutter.accelerator[i]); + } else if ( Shutter.accelerator[i] > 0 && Shutter.pwm_velocity[i] == max_velocity) { + Shutter.accelerator[i] = 0; + } + break; + } } } @@ -392,15 +400,15 @@ void ShutterDecellerateForStop(uint8_t i) case SHT_PWM_VALUE: case SHT_COUNTER: int16_t missing_steps; - Shutter.accelerator[i] = -(Shutter.max_pwm_velocity / (Shutter.motordelay[i]>0 ? Shutter.motordelay[i] : 1)); - while (Shutter.pwm_velocity[i] > -Shutter.accelerator[i] && Shutter.accelerator[i] != 0) { + Shutter.accelerator[i] = -(Shutter.max_pwm_velocity / (Shutter.motordelay[i]>0 ? Shutter.motordelay[i] : 1) *11/10); + while (Shutter.pwm_velocity[i] > -2*Shutter.accelerator[i] ) { AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: velocity: %ld, delta: %d"), Shutter.pwm_velocity[i], Shutter.accelerator[i] ); //Shutter.pwm_velocity[i] = tmax(Shutter.pwm_velocity[i]-Shutter.accelerator[i] , 0); // Control will be done in RTC Ticker. delay(50); } if (Shutter.PositionMode == SHT_COUNTER){ - missing_steps = ((Shutter.target_position[i]-Shutter.start_position[i])*Shutter.direction[i]*Shutter.max_pwm_velocity/2000) - RtcSettings.pulse_counter[i]; + missing_steps = ((Shutter.target_position[i]-Shutter.start_position[i])*Shutter.direction[i]*Shutter.max_pwm_velocity/RESOLUTION/STEPS_PER_SECOND) - RtcSettings.pulse_counter[i]; //prepare for stop PWM AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Remain steps %d, counter %d, freq %d"), missing_steps, RtcSettings.pulse_counter[i] ,Shutter.pwm_velocity[i]); Shutter.accelerator[i] = 0; @@ -409,7 +417,7 @@ void ShutterDecellerateForStop(uint8_t i) analogWrite(Pin(GPIO_PWM1, i), 50); Shutter.pwm_velocity[i] = 0; analogWriteFreq(Shutter.pwm_velocity[i]); - while (RtcSettings.pulse_counter[i] < (uint32_t)(Shutter.target_position[i]-Shutter.start_position[i])*Shutter.direction[i]*Shutter.max_pwm_velocity/2000) { + while (RtcSettings.pulse_counter[i] < (uint32_t)(Shutter.target_position[i]-Shutter.start_position[i])*Shutter.direction[i]*Shutter.max_pwm_velocity/RESOLUTION/STEPS_PER_SECOND) { delay(1); } analogWrite(Pin(GPIO_PWM1, i), 0); // removed with 8.3 because of reset caused by watchog @@ -418,6 +426,7 @@ void ShutterDecellerateForStop(uint8_t i) } Shutter.direction[i] = 0; + Shutter.pwm_velocity[i] = 0; break; } } @@ -452,6 +461,14 @@ void ShutterPowerOff(uint8_t i) { } break; } + // Store current PWM value to ensure proper position after reboot. + switch (Shutter.PositionMode) { + case SHT_PWM_VALUE: + char scmnd[20]; + snprintf_P(scmnd, sizeof(scmnd), PSTR(D_CMND_PWM " %d" ),Shutter.pwm_value[i]); + ExecuteCommand(scmnd, SRC_BUTTON); + break; + } } void ShutterUpdatePosition(void) @@ -460,33 +477,33 @@ void ShutterUpdatePosition(void) char scommand[CMDSZ]; char stopic[TOPSZ]; - stop_position_delta = 20; - for (uint32_t i = 0; i < shutters_present; i++) { if (Shutter.direction[i] != 0) { // Calculate position with counter. Much more accurate and no need for motordelay workaround // adding some steps to stop early - Shutter.real_position[i] = ShutterCalculatePosition(i); + //Shutter.real_position[i] = ShutterCalculatePosition(i); if (!Shutter.start_reported) { ShutterReportPosition(true, i); XdrvRulesProcess(); Shutter.start_reported = 1; } - ShutterCalculateAccelerator(i); + //ShutterCalculateAccelerator(i); + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: time: %d, toBeAcc %d, minstopway %d,cur_vel %d, max_vel %d, act_vel_change %d, min_runtime_ms %d, act.pos %d, next_stop %d, target: %d, max_vel_change_per_step %d"),Shutter.time[i],toBeAcc,minstopway, + Shutter.pwm_velocity[i],max_velocity, Shutter.accelerator[i],min_runtime_ms,Shutter.real_position[i], next_possible_stop,Shutter.target_position[i],max_velocity_change_per_step); - if ( Shutter.real_position[i] * Shutter.direction[i] + stop_position_delta >= Shutter.target_position[i] * Shutter.direction[i] ) { + + if ( Shutter.real_position[i] * Shutter.direction[i] >= Shutter.target_position[i] * Shutter.direction[i] || Shutter.pwm_velocity[i]0 ? Shutter.motordelay[i] : 1); Shutter.target_position[i] = target_pos; @@ -545,22 +559,26 @@ void ShutterStartInit(uint32_t i, int32_t direction, int32_t target_pos) int32_t ShutterCalculatePosition(uint32_t i) { - switch (Shutter.PositionMode) { - case SHT_COUNTER: - return ((int32_t)RtcSettings.pulse_counter[i]*Shutter.direction[i]*2000 / Shutter.max_pwm_velocity)+Shutter.start_position[i]; + if (Shutter.direction[i] != 0) { + switch (Shutter.PositionMode) { + case SHT_COUNTER: + return ((int32_t)RtcSettings.pulse_counter[i]*Shutter.direction[i]*STEPS_PER_SECOND*RESOLUTION / Shutter.max_pwm_velocity)+Shutter.start_position[i]; + break; + case SHT_TIME: + case SHT_TIME_UP_DOWN: + case SHT_TIME_GARAGE: + return Shutter.start_position[i] + ( (Shutter.time[i] - Shutter.motordelay[i]) * (Shutter.direction[i] > 0 ? RESOLUTION : -Shutter.close_velocity[i])); + break; + case SHT_PWM_TIME: + break; + case SHT_PWM_VALUE: + return Shutter.real_position[i]; break; - case SHT_TIME: - case SHT_TIME_UP_DOWN: - case SHT_TIME_GARAGE: - return Shutter.start_position[i] + ( (Shutter.time[i] - Shutter.motordelay[i]) * (Shutter.direction[i] > 0 ? 100 : -Shutter.close_velocity[i])); - break; - case SHT_PWM_TIME: - break; - case SHT_PWM_VALUE: + default: + break; + } + } else { return Shutter.real_position[i]; - break; - default: - break; } } @@ -591,7 +609,7 @@ void ShutterRelayChanged(void) break; default: last_source = SRC_SHUTTER; // avoid switch off in the next loop - if (Shutter.direction[i] != 0 )ShutterPowerOff(i); + if (Shutter.direction[i] != 0 ) ShutterPowerOff(i); } switch (Shutter.PositionMode) { // enum ShutterPositionMode {SHT_TIME, SHT_TIME_UP_DOWN, SHT_TIME_GARAGE, SHT_COUNTER, SHT_PWM_VALUE, SHT_PWM_TIME,}; @@ -1022,8 +1040,8 @@ void CmndShutterPosition(void) } if ( (target_pos_percent >= 0) && (target_pos_percent <= 100) && abs(Shutter.target_position[index] - Shutter.real_position[index] ) / Shutter.close_velocity[index] > 2) { if (Settings.shutter_options[index] & 4) { - if (0 == target_pos_percent) Shutter.target_position[index] -= 1 * 2000; - if (100 == target_pos_percent) Shutter.target_position[index] += 1 * 2000; + if (0 == target_pos_percent) Shutter.target_position[index] -= 1 * RESOLUTION * STEPS_PER_SECOND; + if (100 == target_pos_percent) Shutter.target_position[index] += 1 * RESOLUTION * STEPS_PER_SECOND; } int8_t new_shutterdirection = Shutter.real_position[index] < Shutter.target_position[index] ? 1 : -1; if (Shutter.direction[index] == -new_shutterdirection) { @@ -1128,11 +1146,11 @@ void CmndShutterMotorDelay(void) { if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= shutters_present)) { if (XdrvMailbox.data_len > 0) { - Settings.shutter_motordelay[XdrvMailbox.index -1] = (uint16_t)(steps_per_second * CharToFloat(XdrvMailbox.data)); + Settings.shutter_motordelay[XdrvMailbox.index -1] = (uint16_t)(STEPS_PER_SECOND * CharToFloat(XdrvMailbox.data)); ShutterInit(); } char time_chr[10]; - dtostrfd((float)(Settings.shutter_motordelay[XdrvMailbox.index -1]) / steps_per_second, 2, time_chr); + dtostrfd((float)(Settings.shutter_motordelay[XdrvMailbox.index -1]) / STEPS_PER_SECOND, 2, time_chr); ResponseCmndIdxChar(time_chr); } } From 5eb24348084d7ad4291214a7b1e1a3da04a51ada Mon Sep 17 00:00:00 2001 From: Stephan Hadinger Date: Tue, 8 Sep 2020 21:10:24 +0200 Subject: [PATCH 29/55] Fix crash in `ZbRestore` --- tasmota/xdrv_23_zigbee_1_headers.ino | 2 +- tasmota/xdrv_23_zigbee_7_statemachine.ino | 2 +- tasmota/xdrv_23_zigbee_9_serial.ino | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/tasmota/xdrv_23_zigbee_1_headers.ino b/tasmota/xdrv_23_zigbee_1_headers.ino index bb7555757..604c53835 100644 --- a/tasmota/xdrv_23_zigbee_1_headers.ino +++ b/tasmota/xdrv_23_zigbee_1_headers.ino @@ -57,7 +57,7 @@ JsonVariant &startsWithCaseInsensitive(const JsonObject &json, const char *needl return *(JsonVariant*)nullptr; } - String needle_s(needle); + String needle_s((const __FlashStringHelper *)needle); needle_s.toLowerCase(); for (auto kv : json) { diff --git a/tasmota/xdrv_23_zigbee_7_statemachine.ino b/tasmota/xdrv_23_zigbee_7_statemachine.ino index 5a6ab4e29..ee9bfd19a 100644 --- a/tasmota/xdrv_23_zigbee_7_statemachine.ino +++ b/tasmota/xdrv_23_zigbee_7_statemachine.ino @@ -681,7 +681,7 @@ ZBM(ZBS_SET_CONCENTRATOR, EZSP_setConcentrator, 0x00 /*high*/, 0x00 /*false*/, 0 ZBM(ZBR_SET_CONCENTRATOR, EZSP_setConcentrator, 0x00 /*high*/, 0x00 /*ok*/) // 100000 // setInitialSecurityState -#define EZ_SECURITY_MODE EMBER_TRUST_CENTER_GLOBAL_LINK_KEY | EMBER_PRECONFIGURED_NETWORK_KEY_MODE | EMBER_HAVE_NETWORK_KEY | EMBER_HAVE_PRECONFIGURED_KEY | EMBER_NO_FRAME_COUNTER_RESET +#define EZ_SECURITY_MODE EMBER_TRUST_CENTER_GLOBAL_LINK_KEY | EMBER_PRECONFIGURED_NETWORK_KEY_MODE | EMBER_HAVE_NETWORK_KEY | EMBER_HAVE_PRECONFIGURED_KEY ZBR(ZBS_SET_SECURITY, EZSP_setInitialSecurityState, 0x00 /*high*/, Z_B0(EZ_SECURITY_MODE), Z_B1(EZ_SECURITY_MODE), // preConfiguredKey diff --git a/tasmota/xdrv_23_zigbee_9_serial.ino b/tasmota/xdrv_23_zigbee_9_serial.ino index f5056c58c..fd69ee16a 100644 --- a/tasmota/xdrv_23_zigbee_9_serial.ino +++ b/tasmota/xdrv_23_zigbee_9_serial.ino @@ -598,6 +598,7 @@ int32_t ZigbeeProcessInputEZSP(class SBuffer &buf) { case EZSP_messageSentHandler: // 3F00 case EZSP_setConfigurationValue: // 5300 case EZSP_setPolicy: // 5500 + case 0x0059: // 5900 - supposedly removed by still happening case EZSP_setMulticastTableEntry: // 6400 case EZSP_setInitialSecurityState: // 6800 case EZSP_getCurrentSecurityState: // 6900 From bef03c69f6d89f3573581838a45ee758bf63133f Mon Sep 17 00:00:00 2001 From: Stephan Hadinger Date: Tue, 8 Sep 2020 21:22:52 +0200 Subject: [PATCH 30/55] Add ``#define USE_MQTT_AWS_IOT_LIGHT`` for password based AWS IoT authentication --- tasmota/CHANGELOG.md | 2 +- tasmota/StackThunk_light.cpp | 4 +++- tasmota/WiFiClientSecureLightBearSSL.cpp | 13 +++++-------- tasmota/my_user_config.h | 5 +++-- tasmota/xdrv_02_mqtt.ino | 6 +++--- 5 files changed, 15 insertions(+), 15 deletions(-) diff --git a/tasmota/CHANGELOG.md b/tasmota/CHANGELOG.md index 0f5b4ec47..d9c9b1bfc 100644 --- a/tasmota/CHANGELOG.md +++ b/tasmota/CHANGELOG.md @@ -4,7 +4,7 @@ ### 8.5.0.1 20200907 -- New released +- Add ``#define USE_MQTT_AWS_IOT_LIGHT`` for password based AWS IoT authentication ### 8.5.0 20200907 diff --git a/tasmota/StackThunk_light.cpp b/tasmota/StackThunk_light.cpp index c9f9bc78e..3c0d91bcf 100644 --- a/tasmota/StackThunk_light.cpp +++ b/tasmota/StackThunk_light.cpp @@ -40,8 +40,10 @@ uint32_t *stack_thunk_light_save = NULL; /* Saved A1 while in BearSSL */ uint32_t stack_thunk_light_refcnt = 0; //#define _stackSize (5600/4) -#ifdef USE_MQTT_TLS_FORCE_EC_CIPHER +#if defined(USE_MQTT_AWS_IOT) #define _stackSize (5300/4) // using a light version of bearssl we can save 300 bytes +#elif defined(USE_MQTT_TLS_FORCE_EC_CIPHER) + #define _stackSize (4800/4) // no private key, we can reduce a little, max observed 4300 #else #define _stackSize (3600/4) // using a light version of bearssl we can save 2k #endif diff --git a/tasmota/WiFiClientSecureLightBearSSL.cpp b/tasmota/WiFiClientSecureLightBearSSL.cpp index 209555c3f..d85373688 100755 --- a/tasmota/WiFiClientSecureLightBearSSL.cpp +++ b/tasmota/WiFiClientSecureLightBearSSL.cpp @@ -872,7 +872,11 @@ extern "C" { #ifdef USE_MQTT_TLS_FORCE_EC_CIPHER // we support only P256 EC curve for AWS IoT, no EC curve for Letsencrypt unless forced - br_ssl_engine_set_ec(&cc->eng, &br_ec_p256_m15); + br_ssl_engine_set_ec(&cc->eng, &br_ec_p256_m15); // TODO +#endif +#ifdef USE_MQTT_AWS_IOT_LIGHT + static const char * alpn_mqtt = "mqtt"; + br_ssl_engine_set_protocol_names(&cc->eng, &alpn_mqtt, 1); #endif } } @@ -880,13 +884,6 @@ extern "C" { // Called by connect() to do the actual SSL setup and handshake. // Returns if the SSL handshake succeeded. bool WiFiClientSecure_light::_connectSSL(const char* hostName) { -// #ifdef USE_MQTT_AWS_IOT -// if ((!_chain_P) || (!_sk_ec_P)) { -// setLastError(ERR_MISSING_EC_KEY); -// return false; -// } -// #endif - // Validation context, either full CA validation or checking only fingerprints #ifdef USE_MQTT_TLS_CA_CERT br_x509_minimal_context *x509_minimal; diff --git a/tasmota/my_user_config.h b/tasmota/my_user_config.h index 5f94c5fc0..961a82e10 100644 --- a/tasmota/my_user_config.h +++ b/tasmota/my_user_config.h @@ -389,7 +389,8 @@ // #define USE_MQTT_TLS_CA_CERT // Force full CA validation instead of fingerprints, slower, but simpler to use. (+2.2k code, +1.9k mem during connection handshake) // This includes the LetsEncrypt CA in tasmota_ca.ino for verifying server certificates // #define USE_MQTT_TLS_FORCE_EC_CIPHER // Force Elliptic Curve cipher (higher security) required by some servers (automatically enabled with USE_MQTT_AWS_IOT) (+11.4k code, +0.4k mem) -// #define USE_MQTT_AWS_IOT // Enable MQTT for AWS IoT - requires a private key (+11.9k code, +0.4k mem) +// #define USE_MQTT_AWS_IOT_LIGHT // Enable MQTT for AWS IoT in light mode, with user/password instead of private certificate +// #define USE_MQTT_AWS_IOT // [Deprecated] Enable MQTT for AWS IoT - requires a private key (+11.9k code, +0.4k mem) // Note: you need to generate a private key + certificate per device and update 'tasmota/tasmota_aws_iot.cpp' // Full documentation here: https://github.com/arendst/Tasmota/wiki/AWS-IoT // #define USE_4K_RSA // Support 4096 bits certificates, instead of 2048 @@ -820,7 +821,7 @@ #include "user_config_override.h" // Configuration overrides for my_user_config.h #endif -#if defined(USE_DISCOVERY) && defined(USE_MQTT_AWS_IOT) +#if defined(USE_DISCOVERY) && (defined(USE_MQTT_AWS_IOT) || defined(USE_MQTT_AWS_IOT_LIGHT)) #error "Select either USE_DISCOVERY or USE_MQTT_AWS_IOT, mDNS takes too much code space and is not needed for AWS IoT" #endif diff --git a/tasmota/xdrv_02_mqtt.ino b/tasmota/xdrv_02_mqtt.ino index 767e65b8e..bcd71005f 100644 --- a/tasmota/xdrv_02_mqtt.ino +++ b/tasmota/xdrv_02_mqtt.ino @@ -154,7 +154,7 @@ void MqttInit(void) String host = String(SettingsText(SET_MQTT_HOST)); if (host.indexOf(".iot.") && host.endsWith(".amazonaws.com")) { // look for ".iot." and ".amazonaws.com" in the domain name Settings.flag4.mqtt_no_retain = true; - Mqtt.tls_private_key = true; + // Mqtt.tls_private_key = true; } if (Settings.flag4.mqtt_tls) { @@ -353,7 +353,7 @@ void MqttPublishPrefixTopic_P(uint32_t prefix, const char* subtopic, bool retain GetTopic_P(stopic, prefix, mqtt_topic, romram); MqttPublish(stopic, retained); -#ifdef USE_MQTT_AWS_IOT +#if defined(USE_MQTT_AWS_IOT) || defined(USE_MQTT_AWS_IOT_LIGHT) if ((prefix > 0) && (Settings.flag4.awsiot_shadow) && (Mqtt.connected)) { // placeholder for SetOptionXX // compute the target topic char *topic = SettingsText(SET_MQTT_TOPIC); @@ -1350,7 +1350,7 @@ void MqttSaveSettings(void) #endif WebGetArg("mc", tmp, sizeof(tmp)); SettingsUpdateText(SET_MQTT_CLIENT, (!strlen(tmp)) ? MQTT_CLIENT_ID : tmp); -#if defined(USE_MQTT_TLS) && defined(USE_MQTT_AWS_IOT) +#if defined(USE_MQTT_TLS) && (defined(USE_MQTT_AWS_IOT) || defined(USE_MQTT_AWS_IOT_LIGHT)) AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_MQTT D_CMND_MQTTHOST " %s, " D_CMND_MQTTPORT " %d, " D_CMND_MQTTCLIENT " %s, " D_CMND_TOPIC " %s, " D_CMND_FULLTOPIC " %s"), SettingsText(SET_MQTT_HOST), Settings.mqtt_port, SettingsText(SET_MQTT_CLIENT), SettingsText(SET_MQTT_TOPIC), SettingsText(SET_MQTT_FULLTOPIC)); #else // USE_MQTT_AWS_IOT From 60aeeb445f4213c61da51b7af2840e0720330878 Mon Sep 17 00:00:00 2001 From: stefanbode Date: Wed, 9 Sep 2020 08:58:00 +0200 Subject: [PATCH 31/55] Editorial changes - harmonized variable naming - add more code comments to help others to understand - SWITCH/PULSE now defined for each shutter. --- tasmota/xdrv_27_shutter.ino | 250 ++++++++++++++++++------------------ 1 file changed, 128 insertions(+), 122 deletions(-) diff --git a/tasmota/xdrv_27_shutter.ino b/tasmota/xdrv_27_shutter.ino index 439800ac2..2fd86464f 100644 --- a/tasmota/xdrv_27_shutter.ino +++ b/tasmota/xdrv_27_shutter.ino @@ -30,26 +30,25 @@ #define D_SHUTTER "SHUTTER" const uint16_t MOTOR_STOP_TIME = 500; // in mS -const uint16_t RESOLUTION = 1000; +const uint16_t RESOLUTION = 1000; // incresed to 1000 in 8.5 to ramp servos const uint8_t STEPS_PER_SECOND = 20; // FUNC_EVERY_50_MSECOND const uint16_t pwm_max = 500; const uint16_t pwm_min = 90; uint8_t calibrate_pos[6] = {0,30,50,70,90,100}; uint16_t messwerte[5] = {30,50,70,90,100}; -uint16_t last_execute_step; -int32_t max_velocity = 0; -int32_t max_velocity_change_per_step = 0; +int32_t velocity_max = 0; +int32_t velocity_change_per_step_max = 0; int32_t min_runtime_ms = 0; -int32_t minstopway = 0; -int32_t next_possible_stop = 0; +int32_t current_stop_way = 0; +int32_t next_possible_stop_position = 0; int32_t toBeAcc = 0; const uint8_t MAX_MODES = 7; -enum ShutterPositionMode {SHT_UNDEF, SHT_TIME, SHT_TIME_UP_DOWN, SHT_TIME_GARAGE, SHT_COUNTER, SHT_PWM_VALUE, SHT_PWM_TIME,}; -enum ShutterSwitchMode {SHT_SWITCH, SHT_PULSE,}; +enum Shutterposition_mode {SHT_UNDEF, SHT_TIME, SHT_TIME_UP_DOWN, SHT_TIME_GARAGE, SHT_COUNTER, SHT_PWM_VALUE, SHT_PWM_TIME,}; +enum Shutterswitch_mode {SHT_SWITCH, SHT_PULSE,}; enum ShutterButtonStates { SHT_NOT_PRESSED, SHT_PRESSED_MULTI, SHT_PRESSED_HOLD, SHT_PRESSED_IMMEDIATE, SHT_PRESSED_EXT_HOLD, SHT_PRESSED_MULTI_SIMULTANEOUS, SHT_PRESSED_HOLD_SIMULTANEOUS, SHT_PRESSED_EXT_HOLD_SIMULTANEOUS,}; const char kShutterCommands[] PROGMEM = D_PRFX_SHUTTER "|" @@ -74,31 +73,31 @@ void (* const ShutterCommand[])(void) PROGMEM = { Ticker TickerShutter; struct SHUTTER { - power_t mask = 0; // bit mask with 11 at the position of relays that belong to at least ONE shutter - power_t old_power = 0; // preserve old bitmask for power to extract the relay that changes. - power_t switched_relay = 0; // bitmatrix that contain the relays that was lastly changed. - uint32_t time[MAX_SHUTTERS]; // operating time of the shutter in 0.05sec - int32_t open_max[MAX_SHUTTERS]; // max value on maximum open calculated - int32_t target_position[MAX_SHUTTERS]; // position to go to - int32_t start_position[MAX_SHUTTERS]; // position before a movement is started. init at start - int32_t real_position[MAX_SHUTTERS]; // value between 0 and Shutter.open_max - uint16_t open_time[MAX_SHUTTERS]; // duration to open the shutter. 112 = 11.2sec - uint16_t close_time[MAX_SHUTTERS]; // duration to close the shutter. 112 = 11.2sec - uint16_t close_velocity[MAX_SHUTTERS]; // in relation to open velocity. higher value = faster - int8_t direction[MAX_SHUTTERS]; // 1 == UP , 0 == stop; -1 == down - int8_t lastdirection[MAX_SHUTTERS]; // last direction (1 == UP , -1 == down) - uint8_t PositionMode = 0; // how to calculate actual position: SHT_TIME, SHT_COUNTER, SHT_PWM_VALUE, SHT_PWM_TIME - uint8_t SwitchMode = 0; // how to switch relays: SHT_SWITCH, SHT_PULSE - int16_t motordelay[MAX_SHUTTERS]; // initial motorstarttime in 0.05sec. - int16_t pwm_velocity[MAX_SHUTTERS]; // frequency of PWN for stepper motors or PWM duty cycle change for PWM servo - uint16_t pwm_value[MAX_SHUTTERS]; // dutyload of PWM 0..1023 on ESP8266 - uint16_t pwm_min[MAX_SHUTTERS]; // dutyload of PWM 0..1023 on ESP8266 - uint16_t pwm_max[MAX_SHUTTERS]; // dutyload of PWM 0..1023 on ESP8266 - uint16_t max_pwm_velocity = 1000; // maximum of PWM frequency for openig the shutter. depend on the motor and drivers - uint16_t max_close_pwm_velocity[MAX_SHUTTERS];// maximum of PWM frequency for closeing the shutter. depend on the motor and drivers + power_t RelayShutterMask = 0; // bit mask with 11 at the position of relays that belong to at least ONE shutter + power_t RelayOldMask = 0; // bitmatrix that contain the last known state of all relays. Required to detemine the manual changed relay. + power_t RelayCurrentMask = 0; // bitmatrix that contain the current state of all relays + uint32_t time[MAX_SHUTTERS]; // operating time of the shutter in 0.05sec + int32_t open_max[MAX_SHUTTERS]; // max value on maximum open calculated + int32_t target_position[MAX_SHUTTERS]; // position to go to + int32_t start_position[MAX_SHUTTERS]; // position before a movement is started. init at start + int32_t real_position[MAX_SHUTTERS]; // value between 0 and Shutter.open_max + uint16_t open_time[MAX_SHUTTERS]; // duration to open the shutter. 112 = 11.2sec + uint16_t close_time[MAX_SHUTTERS]; // duration to close the shutter. 112 = 11.2sec + uint16_t close_velocity[MAX_SHUTTERS]; // in relation to open velocity. higher value = faster + int8_t direction[MAX_SHUTTERS]; // 1 == UP , 0 == stop; -1 == down + int8_t lastdirection[MAX_SHUTTERS]; // last direction (1 == UP , -1 == down) + uint8_t position_mode=0; // how to calculate actual position: SHT_TIME, SHT_COUNTER, SHT_PWM_VALUE, SHT_PWM_TIME + uint8_t switch_mode[MAX_SHUTTERS]; // how to switch relays: SHT_SWITCH, SHT_PULSE + int16_t motordelay[MAX_SHUTTERS]; // initial motorstarttime in 0.05sec. Also uses for ramp at steppers and servos + int16_t pwm_velocity[MAX_SHUTTERS]; // frequency of PWN for stepper motors or PWM duty cycle change for PWM servo + uint16_t pwm_value[MAX_SHUTTERS]; // dutyload of PWM 0..1023 on ESP8266 + uint16_t pwm_min[MAX_SHUTTERS]; // dutyload of PWM 0..1023 on ESP8266 + uint16_t pwm_max[MAX_SHUTTERS]; // dutyload of PWM 0..1023 on ESP8266 + uint16_t open_velocity_max = 1000; // maximum of PWM change during opening. Defines velocity on opening. Steppers and Servos only + uint16_t close_velocity_max[MAX_SHUTTERS]; // maximum of PWM change during closeing. Defines velocity on opening. Steppers and Servos only uint8_t skip_relay_change; // avoid overrun at endstops - int32_t accelerator[MAX_SHUTTERS]; // speed of ramp-up, ramp down of shutter - uint8_t start_reported = 0; + int32_t accelerator[MAX_SHUTTERS]; // speed of ramp-up, ramp down of shutters with velocity control. Steppers and Servos only + uint8_t start_reported = 0; // indicates of the shutter start was reported through MQTT JSON } Shutter; #define SHT_DIV_ROUND(__A, __B) (((__A) + (__B)/2) / (__B)) @@ -113,24 +112,28 @@ void ShutterLogPos(uint32_t i) void ExecuteCommandPowerShutter(uint32_t device, uint32_t state, uint32_t source) { + // first implementation for virtual relays. Avoid switching relay numbers that do not exist. if (device <= devices_present) ExecuteCommandPower(device,state,source); } void ShutterUpdateVelocity(uint8_t i) { + // No Logging allowed. Part of RTC Timer + // will be calles through RTC every 50ms. Shutter.pwm_velocity[i] += Shutter.accelerator[i]; - Shutter.pwm_velocity[i] = tmax(0,tmin(Shutter.direction[i]==1 ? Shutter.max_pwm_velocity : Shutter.max_close_pwm_velocity[i],Shutter.pwm_velocity[i])); + Shutter.pwm_velocity[i] = tmax(0,tmin(Shutter.direction[i]==1 ? Shutter.open_velocity_max : Shutter.close_velocity_max[i],Shutter.pwm_velocity[i])); } void ShutterRtc50mS(void) { + // No Logging allowed. RTC Timer for (uint8_t i = 0; i < shutters_present; i++) { if (Shutter.direction[i]) { // update position data before increasing counter Shutter.real_position[i] = ShutterCalculatePosition(i); Shutter.time[i]++; ShutterCalculateAccelerator(i); - switch (Shutter.PositionMode) { + switch (Shutter.position_mode) { case SHT_PWM_VALUE: ShutterUpdateVelocity(i); Shutter.real_position[i] += Shutter.direction[i] > 0 ? Shutter.pwm_velocity[i] : (Shutter.direction[i] < 0 ? -Shutter.pwm_velocity[i] : 0); @@ -215,14 +218,14 @@ uint8_t ShutterRealToPercentPosition(int32_t realpos, uint32_t index) void ShutterInit(void) { shutters_present = 0; - Shutter.mask = 0; + Shutter.RelayShutterMask = 0; //Initialize to get relay that changed - Shutter.old_power = power; - bool relay_in_interlock = false; + Shutter.RelayOldMask = power; + // if shutter 4 is unused if (Settings.shutter_startrelay[MAX_SHUTTERS -1] == 0) { - Shutter.max_pwm_velocity = Settings.shuttercoeff[4][3] > 0 ? Settings.shuttercoeff[4][3] : Shutter.max_pwm_velocity; + Shutter.open_velocity_max = Settings.shuttercoeff[4][3] > 0 ? Settings.shuttercoeff[4][3] : Shutter.open_velocity_max; } for (uint32_t i = 0; i < MAX_SHUTTERS; i++) { // set startrelay to 1 on first init, but only to shutter 1. 90% usecase @@ -230,48 +233,54 @@ void ShutterInit(void) if (Settings.shutter_startrelay[i] && (Settings.shutter_startrelay[i] < 9)) { shutters_present++; - // Determine shutter types - Shutter.mask |= 3 << (Settings.shutter_startrelay[i] -1) ; + // Add the two relays to the mask to knaw they belong to shutters + Shutter.RelayShutterMask |= 3 << (Settings.shutter_startrelay[i] -1) ; - for (uint32_t j = 0; j < MAX_INTERLOCKS * Settings.flag.interlock; j++) { // CMND_INTERLOCK - Enable/disable interlock - //AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Interlock state i=%d %d, flag %d, , shuttermask %d, maskedIL %d"),i, Settings.interlock[i], Settings.flag.interlock,Shutter.mask, Settings.interlock[i]&Shutter.mask); - if (Settings.interlock[j] && (Settings.interlock[j] & Shutter.mask)) { - //AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Relay in Interlock group")); - relay_in_interlock = true; - } - } + // All shutters must have same mode. Switch OR Pulse. N switch (Settings.pulse_timer[i]) { case 0: - Shutter.SwitchMode = SHT_SWITCH; + Shutter.switch_mode[i] = SHT_SWITCH; break; default: - Shutter.SwitchMode = SHT_PULSE; + Shutter.switch_mode[i] = SHT_PULSE; break; } if (Settings.shutter_mode == SHT_UNDEF) { + bool relay_in_interlock = false; AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: mode undef.. calculate...")); + + for (uint32_t j = 0; j < MAX_INTERLOCKS * Settings.flag.interlock; j++) { // CMND_INTERLOCK - Enable/disable interlock + //AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Interlock state i=%d %d, flag %d, , shuttermask %d, maskedIL %d"),i, Settings.interlock[i], Settings.flag.interlock,Shutter.RelayShutterMask, Settings.interlock[i]&Shutter.RelayShutterMask); + if (Settings.interlock[j] && (Settings.interlock[j] & Shutter.RelayShutterMask)) { + //AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Relay in Interlock group")); + relay_in_interlock = true; + } + } + switch (Settings.pulse_timer[i+1]) { case 0: - Shutter.PositionMode = SHT_TIME_GARAGE; + Shutter.position_mode = SHT_TIME_GARAGE; break; default: if (relay_in_interlock) { - Shutter.PositionMode = SHT_TIME; + Shutter.position_mode = SHT_TIME; } else { - Shutter.PositionMode = SHT_TIME_UP_DOWN; + Shutter.position_mode = SHT_TIME_UP_DOWN; if (PinUsed(GPIO_PWM1, i) && PinUsed(GPIO_CNTR1, i)) { - Shutter.PositionMode = SHT_COUNTER; + Shutter.position_mode = SHT_COUNTER; } } break; } } else { - Shutter.PositionMode = Settings.shutter_mode; + Shutter.position_mode = Settings.shutter_mode; } + // main function for stepper and servos to control velocity and acceleration. TickerShutter.attach_ms(50, ShutterRtc50mS ); + // default the 50 percent should not have any impact without changing it. set to 60 Settings.shutter_set50percent[i] = (Settings.shutter_set50percent[i] > 0) ? Settings.shutter_set50percent[i] : 50; @@ -293,7 +302,7 @@ void ShutterInit(void) Settings.shuttercoeff[0][i] = Shutter.open_max[i] - (Settings.shuttercoeff[1][i] * 100); Settings.shuttercoeff[2][i] = (Settings.shuttercoeff[0][i] + 5 * Settings.shuttercoeff[1][i]) / 5; } - Shutter.mask |= 3 << (Settings.shutter_startrelay[i] -1); + Shutter.RelayShutterMask |= 3 << (Settings.shutter_startrelay[i] -1); Shutter.real_position[i] = ShutterPercentToRealPosition(Settings.shutter_position[i], i); @@ -301,14 +310,14 @@ void ShutterInit(void) Shutter.motordelay[i] = Settings.shutter_motordelay[i]; Shutter.lastdirection[i] = (50 < Settings.shutter_position[i]) ? 1 : -1; - switch (Shutter.PositionMode) { + switch (Shutter.position_mode) { case SHT_PWM_VALUE: - Shutter.max_pwm_velocity = RESOLUTION; + Shutter.open_velocity_max = RESOLUTION; break; } - Shutter.max_close_pwm_velocity[i] = Shutter.max_pwm_velocity*Shutter.open_time[i] / Shutter.close_time[i]; + Shutter.close_velocity_max[i] = Shutter.open_velocity_max*Shutter.open_time[i] / Shutter.close_time[i]; - //AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Shutter %d Openvel %d, Closevel: %d"),i, Shutter.max_pwm_velocity, Shutter.max_close_pwm_velocity[i]); + //AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Shutter %d Openvel %d, Closevel: %d"),i, Shutter.open_velocity_max, Shutter.close_velocity_max[i]); AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT%d: Init. Pos: %d,inverted %d, locked %d, end stop time enabled %d, webButtons inverted %d"), i+1, Shutter.real_position[i], (Settings.shutter_options[i]&1) ? 1 : 0, (Settings.shutter_options[i]&2) ? 1 : 0, (Settings.shutter_options[i]&4) ? 1 : 0, (Settings.shutter_options[i]&8) ? 1 : 0); @@ -319,7 +328,7 @@ void ShutterInit(void) } ShutterLimitRealAndTargetPositions(i); Settings.shutter_accuracy = 1; - Settings.shutter_mode = Shutter.PositionMode; + Settings.shutter_mode = Shutter.position_mode; } } @@ -362,31 +371,27 @@ void ShutterLimitRealAndTargetPositions(uint32_t i) { void ShutterCalculateAccelerator(uint8_t i) { + // No Logging allowed. Part of RTC Timer if (Shutter.direction[i] != 0) { - switch (Shutter.PositionMode) { + switch (Shutter.position_mode) { case SHT_COUNTER: case SHT_PWM_VALUE: // calculate max velocity allowed in this direction - max_velocity = Shutter.direction[i] == 1 ? Shutter.max_pwm_velocity : Shutter.max_close_pwm_velocity[i]; + velocity_max = Shutter.direction[i] == 1 ? Shutter.open_velocity_max : Shutter.close_velocity_max[i]; // calculate max change of velocyty based on the defined motordelay in steps - max_velocity_change_per_step = max_velocity / (Shutter.motordelay[i]>0 ? Shutter.motordelay[i] : 1); + velocity_change_per_step_max = velocity_max / (Shutter.motordelay[i]>0 ? Shutter.motordelay[i] : 1); // minimumtime required from current velocity to stop - min_runtime_ms = Shutter.pwm_velocity[i] * 1000 / STEPS_PER_SECOND / max_velocity_change_per_step; + min_runtime_ms = Shutter.pwm_velocity[i] * 1000 / STEPS_PER_SECOND / velocity_change_per_step_max; // decellartion way from current velocity - minstopway = (min_runtime_ms * (Shutter.pwm_velocity[i]+max_velocity_change_per_step)/100 - Shutter.pwm_velocity[i])*RESOLUTION/Shutter.max_pwm_velocity * Shutter.direction[i] ; - next_possible_stop = Shutter.real_position[i] + minstopway ; + current_stop_way = (min_runtime_ms * (Shutter.pwm_velocity[i]+velocity_change_per_step_max)/100 - Shutter.pwm_velocity[i])*RESOLUTION/Shutter.open_velocity_max * Shutter.direction[i] ; + next_possible_stop_position = Shutter.real_position[i] + current_stop_way ; toBeAcc = 0; - // ensure that accelerator kicks in IN TIME and that STOP procedure kicks in at least ONE step before reach end position. - //Shutter.accelerator[i] = tmin(tmax(max_velocity_change_per_step*(100-(Shutter.direction[i]*(Shutter.target_position[i]-next_possible_stop) ))/2000 , max_velocity_change_per_step*9/200), max_velocity_change_per_step*11/200); - //int32_t act_freq_change = max_velocity_change_per_step/20; - // ensure that the accelerotor kicks in at least one step BEFORE it is to late and a hard stop required. - if (Shutter.accelerator[i] < 0 || (next_possible_stop * Shutter.direction[i]) +RESOLUTION*Shutter.pwm_velocity[i]/Shutter.max_pwm_velocity>= Shutter.target_position[i] * Shutter.direction[i] ) { - // 10 times the deviation is the value of this simple p-regulator - toBeAcc = 100+(Shutter.direction[i]*(next_possible_stop-Shutter.target_position[i])*max_velocity/Shutter.pwm_velocity[i]*10/RESOLUTION); - Shutter.accelerator[i] = - tmin(tmax( max_velocity_change_per_step*toBeAcc/100 , (max_velocity_change_per_step*9/10)), (max_velocity_change_per_step*11/10)); - //AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Ramp down: acc: %d"), Shutter.accelerator[i]); - } else if ( Shutter.accelerator[i] > 0 && Shutter.pwm_velocity[i] == max_velocity) { + if (Shutter.accelerator[i] < 0 || (next_possible_stop_position * Shutter.direction[i]) +RESOLUTION*Shutter.pwm_velocity[i]/Shutter.open_velocity_max>= Shutter.target_position[i] * Shutter.direction[i] ) { + // 10 times the deviation is the p-value of this simple p-regulator + toBeAcc = 100+(Shutter.direction[i]*(next_possible_stop_position-Shutter.target_position[i])*velocity_max/Shutter.pwm_velocity[i]*10/RESOLUTION); + Shutter.accelerator[i] = - tmin(tmax( velocity_change_per_step_max*toBeAcc/100 , (velocity_change_per_step_max*9/10)), (velocity_change_per_step_max*11/10)); + } else if ( Shutter.accelerator[i] > 0 && Shutter.pwm_velocity[i] == velocity_max) { Shutter.accelerator[i] = 0; } break; @@ -396,19 +401,19 @@ void ShutterCalculateAccelerator(uint8_t i) void ShutterDecellerateForStop(uint8_t i) { - switch (Shutter.PositionMode) { + switch (Shutter.position_mode) { case SHT_PWM_VALUE: case SHT_COUNTER: int16_t missing_steps; - Shutter.accelerator[i] = -(Shutter.max_pwm_velocity / (Shutter.motordelay[i]>0 ? Shutter.motordelay[i] : 1) *11/10); + Shutter.accelerator[i] = -(Shutter.open_velocity_max / (Shutter.motordelay[i]>0 ? Shutter.motordelay[i] : 1) *11/10); while (Shutter.pwm_velocity[i] > -2*Shutter.accelerator[i] ) { AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: velocity: %ld, delta: %d"), Shutter.pwm_velocity[i], Shutter.accelerator[i] ); //Shutter.pwm_velocity[i] = tmax(Shutter.pwm_velocity[i]-Shutter.accelerator[i] , 0); // Control will be done in RTC Ticker. delay(50); } - if (Shutter.PositionMode == SHT_COUNTER){ - missing_steps = ((Shutter.target_position[i]-Shutter.start_position[i])*Shutter.direction[i]*Shutter.max_pwm_velocity/RESOLUTION/STEPS_PER_SECOND) - RtcSettings.pulse_counter[i]; + if (Shutter.position_mode == SHT_COUNTER){ + missing_steps = ((Shutter.target_position[i]-Shutter.start_position[i])*Shutter.direction[i]*Shutter.open_velocity_max/RESOLUTION/STEPS_PER_SECOND) - RtcSettings.pulse_counter[i]; //prepare for stop PWM AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Remain steps %d, counter %d, freq %d"), missing_steps, RtcSettings.pulse_counter[i] ,Shutter.pwm_velocity[i]); Shutter.accelerator[i] = 0; @@ -417,7 +422,7 @@ void ShutterDecellerateForStop(uint8_t i) analogWrite(Pin(GPIO_PWM1, i), 50); Shutter.pwm_velocity[i] = 0; analogWriteFreq(Shutter.pwm_velocity[i]); - while (RtcSettings.pulse_counter[i] < (uint32_t)(Shutter.target_position[i]-Shutter.start_position[i])*Shutter.direction[i]*Shutter.max_pwm_velocity/RESOLUTION/STEPS_PER_SECOND) { + while (RtcSettings.pulse_counter[i] < (uint32_t)(Shutter.target_position[i]-Shutter.start_position[i])*Shutter.direction[i]*Shutter.open_velocity_max/RESOLUTION/STEPS_PER_SECOND) { delay(1); } analogWrite(Pin(GPIO_PWM1, i), 0); // removed with 8.3 because of reset caused by watchog @@ -438,7 +443,7 @@ void ShutterPowerOff(uint8_t i) { Shutter.direction[i] = 0; delay(MOTOR_STOP_TIME); } - switch (Shutter.SwitchMode) { + switch (Shutter.switch_mode[i]) { case SHT_SWITCH: if ((1 << (Settings.shutter_startrelay[i]-1)) & power) { ExecuteCommandPowerShutter(Settings.shutter_startrelay[i], 0, SRC_SHUTTER); @@ -448,7 +453,7 @@ void ShutterPowerOff(uint8_t i) { } break; case SHT_PULSE: - uint8_t cur_relay = Settings.shutter_startrelay[i] + (Shutter.direction[i] == 1 ? 0 : (uint8_t)(Shutter.PositionMode == SHT_TIME)) ; + uint8_t cur_relay = Settings.shutter_startrelay[i] + (Shutter.direction[i] == 1 ? 0 : (uint8_t)(Shutter.position_mode == SHT_TIME)) ; // we have a momentary switch here. Needs additional pulse on same relay after the end if ((SRC_PULSETIMER == last_source || SRC_SHUTTER == last_source || SRC_WEBGUI == last_source)) { ExecuteCommandPowerShutter(cur_relay, 1, SRC_SHUTTER); @@ -462,7 +467,7 @@ void ShutterPowerOff(uint8_t i) { break; } // Store current PWM value to ensure proper position after reboot. - switch (Shutter.PositionMode) { + switch (Shutter.position_mode) { case SHT_PWM_VALUE: char scmnd[20]; snprintf_P(scmnd, sizeof(scmnd), PSTR(D_CMND_PWM " %d" ),Shutter.pwm_value[i]); @@ -489,11 +494,11 @@ void ShutterUpdatePosition(void) Shutter.start_reported = 1; } //ShutterCalculateAccelerator(i); - AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: time: %d, toBeAcc %d, minstopway %d,cur_vel %d, max_vel %d, act_vel_change %d, min_runtime_ms %d, act.pos %d, next_stop %d, target: %d, max_vel_change_per_step %d"),Shutter.time[i],toBeAcc,minstopway, - Shutter.pwm_velocity[i],max_velocity, Shutter.accelerator[i],min_runtime_ms,Shutter.real_position[i], next_possible_stop,Shutter.target_position[i],max_velocity_change_per_step); + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: time: %d, toBeAcc %d, current_stop_way %d,vel_vur %d, vel_max %d, act_vel_change %d, min_runtime_ms %d, act.pos %d, next_stop %d, target: %d, velocity_change_per_step_max %d"),Shutter.time[i],toBeAcc,current_stop_way, + Shutter.pwm_velocity[i],velocity_max, Shutter.accelerator[i],min_runtime_ms,Shutter.real_position[i], next_possible_stop_position,Shutter.target_position[i],velocity_change_per_step_max); - if ( Shutter.real_position[i] * Shutter.direction[i] >= Shutter.target_position[i] * Shutter.direction[i] || Shutter.pwm_velocity[i]= Shutter.target_position[i] * Shutter.direction[i] || Shutter.pwm_velocity[i]0 ? Shutter.motordelay[i] : 1); + Shutter.accelerator[i] = Shutter.open_velocity_max / (Shutter.motordelay[i]>0 ? Shutter.motordelay[i] : 1); Shutter.target_position[i] = target_pos; Shutter.start_position[i] = Shutter.real_position[i]; Shutter.time[i] = 0; @@ -551,7 +556,7 @@ void ShutterStartInit(uint32_t i, int32_t direction, int32_t target_pos) rules_flag.shutter_moving = 1; rules_flag.shutter_moved = 0; Shutter.start_reported = 0; - //AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: real %d, start %d, counter %d, max_freq %d, dir %d, freq %d"),Shutter.real_position[i], Shutter.start_position[i] ,RtcSettings.pulse_counter[i],Shutter.max_pwm_velocity , Shutter.direction[i] ,Shutter.max_pwm_velocity ); + //AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: real %d, start %d, counter %d,freq_max %d, dir %d, freq %d"),Shutter.real_position[i], Shutter.start_position[i] ,RtcSettings.pulse_counter[i],Shutter.open_velocity_max , Shutter.direction[i] ,Shutter.open_velocity_max ); } //AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Start shutter: %d from %d to %d in direction %d"), i, Shutter.start_position[i], Shutter.target_position[i], Shutter.direction[i]); } @@ -559,10 +564,11 @@ void ShutterStartInit(uint32_t i, int32_t direction, int32_t target_pos) int32_t ShutterCalculatePosition(uint32_t i) { + // No Logging allowed. Part of RTC Timer if (Shutter.direction[i] != 0) { - switch (Shutter.PositionMode) { + switch (Shutter.position_mode) { case SHT_COUNTER: - return ((int32_t)RtcSettings.pulse_counter[i]*Shutter.direction[i]*STEPS_PER_SECOND*RESOLUTION / Shutter.max_pwm_velocity)+Shutter.start_position[i]; + return ((int32_t)RtcSettings.pulse_counter[i]*Shutter.direction[i]*STEPS_PER_SECOND*RESOLUTION / Shutter.open_velocity_max)+Shutter.start_position[i]; break; case SHT_TIME: case SHT_TIME_UP_DOWN: @@ -585,7 +591,7 @@ int32_t ShutterCalculatePosition(uint32_t i) void ShutterRelayChanged(void) { - // Shutter.switched_relay = binary relay that was recently changed and cause an Action + // Shutter.RelayCurrentMask = binary relay that was recently changed and cause an Action // powerstate_local = binary powermatrix and relays from shutter: 0..3 // relays_changed = bool if one of the relays that belong to the shutter changed not by shutter or pulsetimer char stemp1[10]; @@ -593,26 +599,26 @@ void ShutterRelayChanged(void) for (uint32_t i = 0; i < shutters_present; i++) { power_t powerstate_local = (power >> (Settings.shutter_startrelay[i] -1)) & 3; // SRC_IGNORE added because INTERLOCK function bite causes this as last source for changing the relay. - //uint8 manual_relays_changed = ((Shutter.switched_relay >> (Settings.shutter_startrelay[i] -1)) & 3) && SRC_IGNORE != last_source && SRC_SHUTTER != last_source && SRC_PULSETIMER != last_source ; - uint8 manual_relays_changed = ((Shutter.switched_relay >> (Settings.shutter_startrelay[i] -1)) & 3) && SRC_SHUTTER != last_source && SRC_PULSETIMER != last_source ; - AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Shutter %d: source: %s, powerstate_local %ld, Shutter.switched_relay %d, manual change %d"), i+1, GetTextIndexed(stemp1, sizeof(stemp1), last_source, kCommandSource), powerstate_local,Shutter.switched_relay,manual_relays_changed); + //uint8 manual_relays_changed = ((Shutter.RelayCurrentMask >> (Settings.shutter_startrelay[i] -1)) & 3) && SRC_IGNORE != last_source && SRC_SHUTTER != last_source && SRC_PULSETIMER != last_source ; + uint8 manual_relays_changed = ((Shutter.RelayCurrentMask >> (Settings.shutter_startrelay[i] -1)) & 3) && SRC_SHUTTER != last_source && SRC_PULSETIMER != last_source ; + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Shutter %d: source: %s, powerstate_local %ld, Shutter.RelayCurrentMask %d, manual change %d"), i+1, GetTextIndexed(stemp1, sizeof(stemp1), last_source, kCommandSource), powerstate_local,Shutter.RelayCurrentMask,manual_relays_changed); if (manual_relays_changed) { //Shutter.skip_relay_change = true; ShutterLimitRealAndTargetPositions(i); - switch (Shutter.SwitchMode ) { + switch (Shutter.switch_mode[i] ) { case SHT_PULSE: if (Shutter.direction[i] != 0 && powerstate_local) { Shutter.target_position[i] = Shutter.real_position[i]; powerstate_local = 0; - AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Shutter %d: Switch OFF motor. Target: %ld, source: %s, powerstate_local %ld, Shutter.switched_relay %d, manual change %d"), i+1, Shutter.target_position[i], GetTextIndexed(stemp1, sizeof(stemp1), last_source, kCommandSource), powerstate_local,Shutter.switched_relay,manual_relays_changed); + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Shutter %d: Switch OFF motor. Target: %ld, source: %s, powerstate_local %ld, Shutter.RelayCurrentMask %d, manual change %d"), i+1, Shutter.target_position[i], GetTextIndexed(stemp1, sizeof(stemp1), last_source, kCommandSource), powerstate_local,Shutter.RelayCurrentMask,manual_relays_changed); } break; default: last_source = SRC_SHUTTER; // avoid switch off in the next loop if (Shutter.direction[i] != 0 ) ShutterPowerOff(i); } - switch (Shutter.PositionMode) { - // enum ShutterPositionMode {SHT_TIME, SHT_TIME_UP_DOWN, SHT_TIME_GARAGE, SHT_COUNTER, SHT_PWM_VALUE, SHT_PWM_TIME,}; + switch (Shutter.position_mode) { + // enum Shutterposition_mode {SHT_TIME, SHT_TIME_UP_DOWN, SHT_TIME_GARAGE, SHT_COUNTER, SHT_PWM_VALUE, SHT_PWM_TIME,}; case SHT_TIME_UP_DOWN: case SHT_COUNTER: case SHT_PWM_VALUE: @@ -654,7 +660,7 @@ void ShutterRelayChanged(void) } - } // switch (Shutter.PositionMode) + } // switch (Shutter.position_mode) AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Shutter %d: Target: %ld, powerstatelocal %d"), i+1, Shutter.target_position[i], powerstate_local); } // if (manual_relays_changed) } // for (uint32_t i = 0; i < shutters_present; i++) @@ -1034,7 +1040,7 @@ void CmndShutterPosition(void) if (XdrvMailbox.payload != -99) { //target_pos_percent = (Settings.shutter_options[index] & 1) ? 100 - target_pos_percent : target_pos_percent; Shutter.target_position[index] = ShutterPercentToRealPosition(target_pos_percent, index); - //Shutter.accelerator[index] = Shutter.max_pwm_velocity / ((Shutter.motordelay[index] > 0) ? Shutter.motordelay[index] : 1); + //Shutter.accelerator[index] = Shutter.open_velocity_max / ((Shutter.motordelay[index] > 0) ? Shutter.motordelay[index] : 1); //Shutter.target_position[index] = XdrvMailbox.payload < 5 ? Settings.shuttercoeff[2][index] * XdrvMailbox.payload : Settings.shuttercoeff[1][index] * XdrvMailbox.payload + Settings.shuttercoeff[0,index]; AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: lastsource %d:, real %d, target %d, payload %d"), last_source, Shutter.real_position[index] ,Shutter.target_position[index],target_pos_percent); } @@ -1049,7 +1055,7 @@ void CmndShutterPosition(void) } if (Shutter.direction[index] != new_shutterdirection) { ShutterStartInit(index, new_shutterdirection, Shutter.target_position[index]); - switch (Shutter.PositionMode) { + switch (Shutter.position_mode) { case SHT_COUNTER: case SHT_PWM_TIME: case SHT_PWM_VALUE: @@ -1060,12 +1066,12 @@ void CmndShutterPosition(void) // power on ExecuteCommandPowerShutter(Settings.shutter_startrelay[index], 1, SRC_SHUTTER); } - if (Shutter.PositionMode != SHT_TIME_UP_DOWN) ExecuteCommandPowerShutter(Settings.shutter_startrelay[index]+2, 1, SRC_SHUTTER); + if (Shutter.position_mode != SHT_TIME_UP_DOWN) ExecuteCommandPowerShutter(Settings.shutter_startrelay[index]+2, 1, SRC_SHUTTER); break; case SHT_TIME: if (!Shutter.skip_relay_change) { if ( (power >> (Settings.shutter_startrelay[index] -1)) & 3 > 0 ) { - ExecuteCommandPowerShutter(Settings.shutter_startrelay[index] + (new_shutterdirection == 1 ? 1 : 0), Shutter.SwitchMode == SHT_SWITCH ? 0 : 1, SRC_SHUTTER); + ExecuteCommandPowerShutter(Settings.shutter_startrelay[index] + (new_shutterdirection == 1 ? 1 : 0), Shutter.switch_mode[index] == SHT_SWITCH ? 0 : 1, SRC_SHUTTER); } ExecuteCommandPowerShutter(Settings.shutter_startrelay[index] + (new_shutterdirection == 1 ? 0 : 1), 1, SRC_SHUTTER); } @@ -1073,8 +1079,8 @@ void CmndShutterPosition(void) case SHT_TIME_GARAGE: if (!Shutter.skip_relay_change) { if (new_shutterdirection == Shutter.lastdirection[index]) { - AddLog_P2(LOG_LEVEL_INFO, PSTR("SHT: Garage not move in this direction: %d"), Shutter.SwitchMode == SHT_PULSE); - for (uint8_t k=0 ; k <= (uint8_t)(Shutter.SwitchMode == SHT_PULSE) ; k++) { + AddLog_P2(LOG_LEVEL_INFO, PSTR("SHT: Garage not move in this direction: %d"), Shutter.switch_mode[index] == SHT_PULSE); + for (uint8_t k=0 ; k <= (uint8_t)(Shutter.switch_mode[index] == SHT_PULSE) ; k++) { ExecuteCommandPowerShutter(Settings.shutter_startrelay[index], 1, SRC_SHUTTER); delay(500); ExecuteCommandPowerShutter(Settings.shutter_startrelay[index], 0, SRC_SHUTTER); @@ -1086,8 +1092,8 @@ void CmndShutterPosition(void) ExecuteCommandPowerShutter(Settings.shutter_startrelay[index], 1, SRC_SHUTTER); } // if (!Shutter.skip_relay_change) break; - } // switch (Shutter.PositionMode) - Shutter.switched_relay = 0; + } // switch (Shutter.position_mode) + Shutter.RelayCurrentMask = 0; } // if (Shutter.direction[index] != new_shutterdirection) } else { target_pos_percent = ShutterRealToPercentPosition(Shutter.real_position[index], index); @@ -1158,12 +1164,12 @@ void CmndShutterMotorDelay(void) void CmndShutterMode(void) { if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= MAX_MODES)) { - Shutter.PositionMode = XdrvMailbox.payload; + Shutter.position_mode = XdrvMailbox.payload; Settings.shutter_mode = XdrvMailbox.payload; ShutterInit(); ResponseCmndNumber(XdrvMailbox.payload); // ???? } else { - ResponseCmndNumber(Shutter.PositionMode); + ResponseCmndNumber(Shutter.position_mode); } } @@ -1173,9 +1179,9 @@ void CmndShutterRelay(void) if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 64)) { Settings.shutter_startrelay[XdrvMailbox.index -1] = XdrvMailbox.payload; if (XdrvMailbox.payload > 0) { - Shutter.mask |= 3 << (XdrvMailbox.payload - 1); + Shutter.RelayShutterMask |= 3 << (XdrvMailbox.payload - 1); } else { - Shutter.mask ^= 3 << (Settings.shutter_startrelay[XdrvMailbox.index -1] - 1); + Shutter.RelayShutterMask ^= 3 << (Settings.shutter_startrelay[XdrvMailbox.index -1] - 1); } Settings.shutter_startrelay[XdrvMailbox.index -1] = XdrvMailbox.payload; ShutterInit(); @@ -1336,14 +1342,14 @@ void CmndShutterSetHalfway(void) void CmndShutterFrequency(void) { if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload <= 20000)) { - Shutter.max_pwm_velocity = XdrvMailbox.payload; + Shutter.open_velocity_max = XdrvMailbox.payload; if (shutters_present < 4) { - Settings.shuttercoeff[4][3] = Shutter.max_pwm_velocity; + Settings.shuttercoeff[4][3] = Shutter.open_velocity_max; } ShutterInit(); ResponseCmndNumber(XdrvMailbox.payload); // ???? } else { - ResponseCmndNumber(Shutter.max_pwm_velocity); + ResponseCmndNumber(Shutter.open_velocity_max); } } @@ -1487,19 +1493,19 @@ bool Xdrv27(uint8_t function) case FUNC_SET_POWER: char stemp1[10]; // extract the number of the relay that was switched and save for later in Update Position. - Shutter.switched_relay = XdrvMailbox.index ^ Shutter.old_power; - AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Switched relay: %d by %s"), Shutter.switched_relay,GetTextIndexed(stemp1, sizeof(stemp1), last_source, kCommandSource)); + Shutter.RelayCurrentMask = XdrvMailbox.index ^ Shutter.RelayOldMask; + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Switched relay: %d by %s"), Shutter.RelayCurrentMask,GetTextIndexed(stemp1, sizeof(stemp1), last_source, kCommandSource)); ShutterRelayChanged(); - Shutter.old_power = XdrvMailbox.index; + Shutter.RelayOldMask = XdrvMailbox.index; break; case FUNC_SET_DEVICE_POWER: if (Shutter.skip_relay_change ) { uint8_t i; for (i = 0; i < devices_present; i++) { - if (Shutter.switched_relay &1) { + if (Shutter.RelayCurrentMask &1) { break; } - Shutter.switched_relay >>= 1; + Shutter.RelayCurrentMask >>= 1; } //AddLog_P2(LOG_LEVEL_ERROR, PSTR("SHT: skip relay change: %d"),i+1); result = true; From 021ec06553c35e54f4c1d955bbfa3958d8b996e3 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Wed, 9 Sep 2020 09:40:28 +0200 Subject: [PATCH 32/55] Update settings.h --- tasmota/settings.h | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/tasmota/settings.h b/tasmota/settings.h index 3ca80ecd6..048b61754 100644 --- a/tasmota/settings.h +++ b/tasmota/settings.h @@ -609,13 +609,10 @@ struct { uint8_t ledpwm_off; // F40 uint8_t tcp_baudrate; // F41 uint8_t fallback_module; // F42 - - uint8_t free_f43[1]; // F43 - + uint8_t shutter_mode; // F43 uint16_t energy_power_delta[3]; // F44 - uint8_t shutter_mode; // F45 - uint8_t free_f4e[105]; // F4A - Decrement if adding new Setting variables just above and below + uint8_t free_f4a[106]; // F4A - Decrement if adding new Setting variables just above and below // Only 32 bit boundary variables below SysBitfield5 flag5; // FB4 From 9f26068b7d01d4a48f327097abeb5a2365deffcc Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Wed, 9 Sep 2020 10:49:04 +0200 Subject: [PATCH 33/55] Fix HLW8012 related total energy counters Fix HLW8012 related total energy counters (#9263, #9266) --- tasmota/xnrg_01_hlw8012.ino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tasmota/xnrg_01_hlw8012.ino b/tasmota/xnrg_01_hlw8012.ino index 40d3e4185..dfc536672 100644 --- a/tasmota/xnrg_01_hlw8012.ino +++ b/tasmota/xnrg_01_hlw8012.ino @@ -208,7 +208,7 @@ void HlwEverySecond(void) hlw_len = 10000 * 1000 / Hlw.energy_period_counter; // Add *1000 to fix rounding on loads at 3.6kW (#9160) Hlw.energy_period_counter = 0; if (hlw_len) { - Energy.kWhtoday_delta += ((Hlw.power_ratio * Settings.energy_power_calibration) * 1000 / hlw_len) / 36; + Energy.kWhtoday_delta += (((Hlw.power_ratio * Settings.energy_power_calibration) / 36) * 1000) / hlw_len; EnergyUpdateToday(); } } From 0eb078a5cb072d53c46aacce0aa2793cdba89801 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Wed, 9 Sep 2020 10:56:18 +0200 Subject: [PATCH 34/55] Final fix --- tasmota/xnrg_01_hlw8012.ino | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tasmota/xnrg_01_hlw8012.ino b/tasmota/xnrg_01_hlw8012.ino index dfc536672..4e73f0926 100644 --- a/tasmota/xnrg_01_hlw8012.ino +++ b/tasmota/xnrg_01_hlw8012.ino @@ -205,10 +205,10 @@ void HlwEverySecond(void) unsigned long hlw_len; if (Hlw.energy_period_counter) { - hlw_len = 10000 * 1000 / Hlw.energy_period_counter; // Add *1000 to fix rounding on loads at 3.6kW (#9160) + hlw_len = 10000 * 100 / Hlw.energy_period_counter; // Add *100 to fix rounding on loads at 3.6kW (#9160) Hlw.energy_period_counter = 0; if (hlw_len) { - Energy.kWhtoday_delta += (((Hlw.power_ratio * Settings.energy_power_calibration) / 36) * 1000) / hlw_len; + Energy.kWhtoday_delta += (((Hlw.power_ratio * Settings.energy_power_calibration) / 36) * 100) / hlw_len; EnergyUpdateToday(); } } From 05c153c7b131d7b604dc0a0c2b5553970c112046 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Wed, 9 Sep 2020 11:22:34 +0200 Subject: [PATCH 35/55] Update change log --- RELEASENOTES.md | 5 +++++ tasmota/CHANGELOG.md | 3 +++ 2 files changed, 8 insertions(+) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 8c95d47b5..db8706750 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -54,3 +54,8 @@ The following binary downloads have been compiled with ESP8266/Arduino library c ## Changelog ### Version 8.5.0.1 + +- Fix energy total counters (#9263, #9266) +- Fix crash in ``ZbRestore`` +- Add new shutter modes (#9244) +- Add ``#define USE_MQTT_AWS_IOT_LIGHT`` for password based AWS IoT authentication diff --git a/tasmota/CHANGELOG.md b/tasmota/CHANGELOG.md index d9c9b1bfc..faaa2ae17 100644 --- a/tasmota/CHANGELOG.md +++ b/tasmota/CHANGELOG.md @@ -4,6 +4,9 @@ ### 8.5.0.1 20200907 +- Fix energy total counters (#9263, #9266) +- Fix crash in ``ZbRestore`` +- Add new shutter modes (#9244) - Add ``#define USE_MQTT_AWS_IOT_LIGHT`` for password based AWS IoT authentication ### 8.5.0 20200907 From 0e5c1c31ef69602302a8c90b80131a3dc9883c91 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Wed, 9 Sep 2020 12:44:30 +0200 Subject: [PATCH 36/55] Update RELEASENOTES.md --- RELEASENOTES.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index db8706750..796435d03 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -47,6 +47,8 @@ The following binary downloads have been compiled with ESP8266/Arduino library c - **tasmota-zbbridge.bin** = The dedicated Sonoff Zigbee Bridge version. - **tasmota-minimal.bin** = The Minimal version allows intermediate OTA uploads to support larger versions and does NOT change any persistent parameter. This version **should NOT be used for initial installation**. +Binaries for ESP8266 based devices can be downloaded from http://ota.tasmota.com/tasmota/release. Binaries for ESP32 based devices can be downloaded from http://ota.tasmota.com/tasmota32/release. The base links can be used for OTA upgrades like ``OtaUrl http://ota.tasmota.com/tasmota/release/tasmota.bin`` + [List](MODULES.md) of embedded modules. [Complete list](BUILDS.md) of available feature and sensors. From 43c9705349a0682bb6b80846ab4f4dd676d1fb55 Mon Sep 17 00:00:00 2001 From: stefanbode Date: Wed, 9 Sep 2020 14:04:57 +0200 Subject: [PATCH 37/55] continues optimization --- tasmota/xdrv_27_shutter.ino | 568 +++++++++++++++++------------------- 1 file changed, 270 insertions(+), 298 deletions(-) diff --git a/tasmota/xdrv_27_shutter.ino b/tasmota/xdrv_27_shutter.ino index 2fd86464f..6b0ef98e7 100644 --- a/tasmota/xdrv_27_shutter.ino +++ b/tasmota/xdrv_27_shutter.ino @@ -1,5 +1,5 @@ /* - xdrv_27_shutter.ino - Shutter/Blind support for Tasmota + xdrv_27_Shutter[i].ino - Shutter/Blind support for Tasmota Copyright (C) 2020 Stefan Bode @@ -31,7 +31,7 @@ const uint16_t MOTOR_STOP_TIME = 500; // in mS const uint16_t RESOLUTION = 1000; // incresed to 1000 in 8.5 to ramp servos -const uint8_t STEPS_PER_SECOND = 20; // FUNC_EVERY_50_MSECOND +const uint8_t STEPS_PER_SECOND = 20; // FUNC_EVERY_50_MSECOND const uint16_t pwm_max = 500; const uint16_t pwm_min = 90; @@ -73,41 +73,44 @@ void (* const ShutterCommand[])(void) PROGMEM = { Ticker TickerShutter; struct SHUTTER { - power_t RelayShutterMask = 0; // bit mask with 11 at the position of relays that belong to at least ONE shutter - power_t RelayOldMask = 0; // bitmatrix that contain the last known state of all relays. Required to detemine the manual changed relay. - power_t RelayCurrentMask = 0; // bitmatrix that contain the current state of all relays - uint32_t time[MAX_SHUTTERS]; // operating time of the shutter in 0.05sec - int32_t open_max[MAX_SHUTTERS]; // max value on maximum open calculated - int32_t target_position[MAX_SHUTTERS]; // position to go to - int32_t start_position[MAX_SHUTTERS]; // position before a movement is started. init at start - int32_t real_position[MAX_SHUTTERS]; // value between 0 and Shutter.open_max - uint16_t open_time[MAX_SHUTTERS]; // duration to open the shutter. 112 = 11.2sec - uint16_t close_time[MAX_SHUTTERS]; // duration to close the shutter. 112 = 11.2sec - uint16_t close_velocity[MAX_SHUTTERS]; // in relation to open velocity. higher value = faster - int8_t direction[MAX_SHUTTERS]; // 1 == UP , 0 == stop; -1 == down - int8_t lastdirection[MAX_SHUTTERS]; // last direction (1 == UP , -1 == down) - uint8_t position_mode=0; // how to calculate actual position: SHT_TIME, SHT_COUNTER, SHT_PWM_VALUE, SHT_PWM_TIME - uint8_t switch_mode[MAX_SHUTTERS]; // how to switch relays: SHT_SWITCH, SHT_PULSE - int16_t motordelay[MAX_SHUTTERS]; // initial motorstarttime in 0.05sec. Also uses for ramp at steppers and servos - int16_t pwm_velocity[MAX_SHUTTERS]; // frequency of PWN for stepper motors or PWM duty cycle change for PWM servo - uint16_t pwm_value[MAX_SHUTTERS]; // dutyload of PWM 0..1023 on ESP8266 - uint16_t pwm_min[MAX_SHUTTERS]; // dutyload of PWM 0..1023 on ESP8266 - uint16_t pwm_max[MAX_SHUTTERS]; // dutyload of PWM 0..1023 on ESP8266 + uint32_t time; // operating time of the shutter in 0.05sec + int32_t open_max; // max value on maximum open calculated + int32_t target_position; // position to go to + int32_t start_position; // position before a movement is started. init at start + int32_t real_position; // value between 0 and Shutter[i].open_max + uint16_t open_time; // duration to open the Shutter[i]. 112 = 11.2sec + uint16_t close_time; // duration to close the Shutter[i]. 112 = 11.2sec + uint16_t close_velocity; // in relation to open velocity. higher value = faster + int8_t direction; // 1 == UP , 0 == stop; -1 == down + int8_t lastdirection; // last direction (1 == UP , -1 == down) + uint8_t switch_mode; // how to switch relays: SHT_SWITCH, SHT_PULSE + int16_t motordelay; // initial motorstarttime in 0.05sec. Also uses for ramp at steppers and servos + int16_t pwm_velocity; // frequency of PWN for stepper motors or PWM duty cycle change for PWM servo + uint16_t pwm_value; // dutyload of PWM 0..1023 on ESP8266 + uint16_t pwm_min; // dutyload of PWM 0..1023 on ESP8266 + uint16_t pwm_max; // dutyload of PWM 0..1023 on ESP8266 + uint16_t close_velocity_max; // maximum of PWM change during closeing. Defines velocity on opening. Steppers and Servos only + int32_t accelerator; // speed of ramp-up, ramp down of shutters with velocity control. Steppers and Servos only +} Shutter[MAX_SHUTTERS]; + +struct SHUTTERGLOBAL { + power_t RelayShutterMask = 0; // bit mask with 11 at the position of relays that belong to at least ONE shutter + power_t RelayOldMask = 0; // bitmatrix that contain the last known state of all relays. Required to detemine the manual changed relay. + power_t RelayCurrentMask = 0; // bitmatrix that contain the current state of all relays + uint8_t position_mode = 0; // how to calculate actual position: SHT_TIME, SHT_COUNTER, SHT_PWM_VALUE, SHT_PWM_TIME + uint8_t skip_relay_change; // avoid overrun at endstops + uint8_t start_reported = 0; // indicates of the shutter start was reported through MQTT JSON uint16_t open_velocity_max = 1000; // maximum of PWM change during opening. Defines velocity on opening. Steppers and Servos only - uint16_t close_velocity_max[MAX_SHUTTERS]; // maximum of PWM change during closeing. Defines velocity on opening. Steppers and Servos only - uint8_t skip_relay_change; // avoid overrun at endstops - int32_t accelerator[MAX_SHUTTERS]; // speed of ramp-up, ramp down of shutters with velocity control. Steppers and Servos only - uint8_t start_reported = 0; // indicates of the shutter start was reported through MQTT JSON -} Shutter; +} ShutterGlobal; #define SHT_DIV_ROUND(__A, __B) (((__A) + (__B)/2) / (__B)) void ShutterLogPos(uint32_t i) { char stemp2[10]; - dtostrfd((float)Shutter.time[i] / STEPS_PER_SECOND, 2, stemp2); + dtostrfd((float)Shutter[i].time / STEPS_PER_SECOND, 2, stemp2); AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Shutter %d Real %d, Start %d, Stop %d, Dir %d, Delay %d, Rtc %s [s], Freq %d, PWM %d"), - i+1, Shutter.real_position[i], Shutter.start_position[i], Shutter.target_position[i], Shutter.direction[i], Shutter.motordelay[i], stemp2, Shutter.pwm_velocity[i], Shutter.pwm_value[i]); + i+1, Shutter[i].real_position, Shutter[i].start_position, Shutter[i].target_position, Shutter[i].direction, Shutter[i].motordelay, stemp2, Shutter[i].pwm_velocity, Shutter[i].pwm_value); } void ExecuteCommandPowerShutter(uint32_t device, uint32_t state, uint32_t source) @@ -120,37 +123,37 @@ void ShutterUpdateVelocity(uint8_t i) { // No Logging allowed. Part of RTC Timer // will be calles through RTC every 50ms. - Shutter.pwm_velocity[i] += Shutter.accelerator[i]; - Shutter.pwm_velocity[i] = tmax(0,tmin(Shutter.direction[i]==1 ? Shutter.open_velocity_max : Shutter.close_velocity_max[i],Shutter.pwm_velocity[i])); + Shutter[i].pwm_velocity += Shutter[i].accelerator; + Shutter[i].pwm_velocity = tmax(0,tmin(Shutter[i].direction==1 ? ShutterGlobal.open_velocity_max : Shutter[i].close_velocity_max,Shutter[i].pwm_velocity)); } void ShutterRtc50mS(void) { // No Logging allowed. RTC Timer for (uint8_t i = 0; i < shutters_present; i++) { - if (Shutter.direction[i]) { + if (Shutter[i].direction) { // update position data before increasing counter - Shutter.real_position[i] = ShutterCalculatePosition(i); - Shutter.time[i]++; + Shutter[i].real_position = ShutterCalculatePosition(i); + Shutter[i].time++; ShutterCalculateAccelerator(i); - switch (Shutter.position_mode) { + switch (ShutterGlobal.position_mode) { case SHT_PWM_VALUE: ShutterUpdateVelocity(i); - Shutter.real_position[i] += Shutter.direction[i] > 0 ? Shutter.pwm_velocity[i] : (Shutter.direction[i] < 0 ? -Shutter.pwm_velocity[i] : 0); - Shutter.pwm_value[i] = SHT_DIV_ROUND((Shutter.pwm_max[i]-Shutter.pwm_min[i]) * Shutter.real_position[i] , Shutter.open_max[i])+Shutter.pwm_min[i]; - analogWrite(Pin(GPIO_PWM1, i), Shutter.pwm_value[i]); + Shutter[i].real_position += Shutter[i].direction > 0 ? Shutter[i].pwm_velocity : (Shutter[i].direction < 0 ? -Shutter[i].pwm_velocity : 0); + Shutter[i].pwm_value = SHT_DIV_ROUND((Shutter[i].pwm_max-Shutter[i].pwm_min) * Shutter[i].real_position , Shutter[i].open_max)+Shutter[i].pwm_min; + analogWrite(Pin(GPIO_PWM1, i), Shutter[i].pwm_value); break; case SHT_COUNTER: - if (Shutter.accelerator[i]) { - //AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: accelerator i=%d -> %d"),i, Shutter.accelerator[i]); + if (Shutter[i].accelerator) { + //AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: accelerator i=%d -> %d"),i, Shutter[i].accelerator); ShutterUpdateVelocity(i); - analogWriteFreq(Shutter.pwm_velocity[i]); + analogWriteFreq(Shutter[i].pwm_velocity); analogWrite(Pin(GPIO_PWM1, i), 50); } break; } - } // if (Shutter.direction[i]) + } // if (Shutter[i].direction) } } @@ -169,17 +172,17 @@ int32_t ShutterPercentToRealPosition(uint32_t percent, uint32_t index) } } } - for (uint32_t i = 0; i < 5; i++) { - if ((percent * 10) >= Settings.shuttercoeff[i][index]) { - realpos = SHT_DIV_ROUND(Shutter.open_max[index] * calibrate_pos[i+1], 100); + for (uint32_t k = 0; k < 5; k++) { + if ((percent * 10) >= Settings.shuttercoeff[k][index]) { + realpos = SHT_DIV_ROUND(Shutter[index].open_max * calibrate_pos[k+1], 100); //AddLog_P2(LOG_LEVEL_DEBUG, PSTR("Realposition TEMP1: %d, %% %d, coeff %d"), realpos, percent, Settings.shuttercoeff[i][index]); } else { - if (0 == i) { - realpos = SHT_DIV_ROUND(SHT_DIV_ROUND(percent * Shutter.open_max[index] * calibrate_pos[i+1], Settings.shuttercoeff[i][index]), 10); + if (0 == k) { + realpos = SHT_DIV_ROUND(SHT_DIV_ROUND(percent * Shutter[index].open_max * calibrate_pos[k+1], Settings.shuttercoeff[k][index]), 10); } else { //uint16_t addon = ( percent*10 - Settings.shuttercoeff[i-1][index] ) * Shutter_Open_Max[index] * (calibrate_pos[i+1] - calibrate_pos[i]) / (Settings.shuttercoeff[i][index] -Settings.shuttercoeff[i-1][index]) / 100; //AddLog_P2(LOG_LEVEL_DEBUG, PSTR("Realposition TEMP2: %d, %% %d, coeff %d"), addon, (calibrate_pos[i+1] - calibrate_pos[i]), (Settings.shuttercoeff[i][index] -Settings.shuttercoeff[i-1][index])); - realpos += SHT_DIV_ROUND(SHT_DIV_ROUND((percent*10 - Settings.shuttercoeff[i-1][index] ) * Shutter.open_max[index] * (calibrate_pos[i+1] - calibrate_pos[i]), Settings.shuttercoeff[i][index] - Settings.shuttercoeff[i-1][index]), 100); + realpos += SHT_DIV_ROUND(SHT_DIV_ROUND((percent*10 - Settings.shuttercoeff[k-1][index] ) * Shutter[index].open_max * (calibrate_pos[k+1] - calibrate_pos[k]), Settings.shuttercoeff[k][index] - Settings.shuttercoeff[k-1][index]), 100); } break; } @@ -195,18 +198,18 @@ uint8_t ShutterRealToPercentPosition(int32_t realpos, uint32_t index) } else { uint16_t realpercent; - for (uint32_t i = 0; i < 5; i++) { - if (realpos >= Shutter.open_max[index] * calibrate_pos[i+1] / 100) { - realpercent = SHT_DIV_ROUND(Settings.shuttercoeff[i][index], 10); + for (uint32_t j = 0; j < 5; j++) { + if (realpos >= Shutter[index].open_max * calibrate_pos[j+1] / 100) { + realpercent = SHT_DIV_ROUND(Settings.shuttercoeff[j][index], 10); //AddLog_P2(LOG_LEVEL_DEBUG, PSTR("Realpercent TEMP1: %d, %% %d, coeff %d"), realpercent, realpos, Shutter_Open_Max[index] * calibrate_pos[i+1] / 100); } else { - if (0 == i) { - realpercent = SHT_DIV_ROUND(SHT_DIV_ROUND((realpos - SHT_DIV_ROUND(Shutter.open_max[index] * calibrate_pos[i], 100)) * 10 * Settings.shuttercoeff[i][index], calibrate_pos[i+1]), Shutter.open_max[index]); + if (0 == j) { + realpercent = SHT_DIV_ROUND(SHT_DIV_ROUND((realpos - SHT_DIV_ROUND(Shutter[index].open_max * calibrate_pos[j], 100)) * 10 * Settings.shuttercoeff[j][index], calibrate_pos[j+1]), Shutter[index].open_max); } else { //uint16_t addon = ( realpos - (Shutter_Open_Max[index] * calibrate_pos[i] / 100) ) * 10 * (Settings.shuttercoeff[i][index] - Settings.shuttercoeff[i-1][index]) / (calibrate_pos[i+1] - calibrate_pos[i])/ Shutter_Open_Max[index]; //uint16_t addon = ( percent*10 - Settings.shuttercoeff[i-1][index] ) * Shutter_Open_Max[index] * (calibrate_pos[i+1] - calibrate_pos[i]) / (Settings.shuttercoeff[i][index] -Settings.shuttercoeff[i-1][index]) / 100; //AddLog_P2(LOG_LEVEL_DEBUG, PSTR("Realpercent TEMP2: %d, delta %d, %% %d, coeff %d"), addon,( realpos - (Shutter_Open_Max[index] * calibrate_pos[i] / 100) ) , (calibrate_pos[i+1] - calibrate_pos[i])* Shutter_Open_Max[index]/100, (Settings.shuttercoeff[i][index] -Settings.shuttercoeff[i-1][index])); - realpercent += SHT_DIV_ROUND(SHT_DIV_ROUND((realpos - SHT_DIV_ROUND(Shutter.open_max[index] * calibrate_pos[i], 100)) * 10 * (Settings.shuttercoeff[i][index] - Settings.shuttercoeff[i-1][index]), (calibrate_pos[i+1] - calibrate_pos[i])), Shutter.open_max[index]) ; + realpercent += SHT_DIV_ROUND(SHT_DIV_ROUND((realpos - SHT_DIV_ROUND(Shutter[index].open_max * calibrate_pos[j], 100)) * 10 * (Settings.shuttercoeff[j][index] - Settings.shuttercoeff[j-1][index]), (calibrate_pos[j+1] - calibrate_pos[j])), Shutter[index].open_max) ; } break; } @@ -218,14 +221,14 @@ uint8_t ShutterRealToPercentPosition(int32_t realpos, uint32_t index) void ShutterInit(void) { shutters_present = 0; - Shutter.RelayShutterMask = 0; + ShutterGlobal.RelayShutterMask = 0; //Initialize to get relay that changed - Shutter.RelayOldMask = power; + ShutterGlobal.RelayOldMask = power; // if shutter 4 is unused if (Settings.shutter_startrelay[MAX_SHUTTERS -1] == 0) { - Shutter.open_velocity_max = Settings.shuttercoeff[4][3] > 0 ? Settings.shuttercoeff[4][3] : Shutter.open_velocity_max; + ShutterGlobal.open_velocity_max = Settings.shuttercoeff[4][3] > 0 ? Settings.shuttercoeff[4][3] : ShutterGlobal.open_velocity_max; } for (uint32_t i = 0; i < MAX_SHUTTERS; i++) { // set startrelay to 1 on first init, but only to shutter 1. 90% usecase @@ -234,15 +237,15 @@ void ShutterInit(void) shutters_present++; // Add the two relays to the mask to knaw they belong to shutters - Shutter.RelayShutterMask |= 3 << (Settings.shutter_startrelay[i] -1) ; + ShutterGlobal.RelayShutterMask |= 3 << (Settings.shutter_startrelay[i] -1) ; // All shutters must have same mode. Switch OR Pulse. N switch (Settings.pulse_timer[i]) { case 0: - Shutter.switch_mode[i] = SHT_SWITCH; + Shutter[i].switch_mode = SHT_SWITCH; break; default: - Shutter.switch_mode[i] = SHT_PULSE; + Shutter[i].switch_mode = SHT_PULSE; break; } @@ -251,31 +254,24 @@ void ShutterInit(void) AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: mode undef.. calculate...")); for (uint32_t j = 0; j < MAX_INTERLOCKS * Settings.flag.interlock; j++) { // CMND_INTERLOCK - Enable/disable interlock - //AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Interlock state i=%d %d, flag %d, , shuttermask %d, maskedIL %d"),i, Settings.interlock[i], Settings.flag.interlock,Shutter.RelayShutterMask, Settings.interlock[i]&Shutter.RelayShutterMask); - if (Settings.interlock[j] && (Settings.interlock[j] & Shutter.RelayShutterMask)) { + //AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Interlock state i=%d %d, flag %d, , shuttermask %d, maskedIL %d"),i, Settings.interlock[i], Settings.flag.interlock,ShutterGlobal.RelayShutterMask, Settings.interlock[i]&ShutterGlobal.RelayShutterMask); + if (Settings.interlock[j] && (Settings.interlock[j] & ShutterGlobal.RelayShutterMask)) { //AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Relay in Interlock group")); relay_in_interlock = true; } } - switch (Settings.pulse_timer[i+1]) { - case 0: - Shutter.position_mode = SHT_TIME_GARAGE; - break; - default: - if (relay_in_interlock) { - Shutter.position_mode = SHT_TIME; - } else { - Shutter.position_mode = SHT_TIME_UP_DOWN; - if (PinUsed(GPIO_PWM1, i) && PinUsed(GPIO_CNTR1, i)) { - Shutter.position_mode = SHT_COUNTER; - } - } - - break; + if (relay_in_interlock) { + ShutterGlobal.position_mode = SHT_TIME; + } else { + ShutterGlobal.position_mode = SHT_TIME_UP_DOWN; + if (PinUsed(GPIO_PWM1, i) && PinUsed(GPIO_CNTR1, i)) { + ShutterGlobal.position_mode = SHT_COUNTER; + } } + } else { - Shutter.position_mode = Settings.shutter_mode; + ShutterGlobal.position_mode = Settings.shutter_mode; } // main function for stepper and servos to control velocity and acceleration. @@ -285,50 +281,50 @@ void ShutterInit(void) Settings.shutter_set50percent[i] = (Settings.shutter_set50percent[i] > 0) ? Settings.shutter_set50percent[i] : 50; // use 10 sec. as default to allow everybody to play without deep initialize - Shutter.open_time[i] = (Settings.shutter_opentime[i] > 0) ? Settings.shutter_opentime[i] : 100; - Shutter.close_time[i] = (Settings.shutter_closetime[i] > 0) ? Settings.shutter_closetime[i] : 100; + Shutter[i].open_time = Settings.shutter_opentime[i] = (Settings.shutter_opentime[i] > 0) ? Settings.shutter_opentime[i] : 100; + Shutter[i].close_time = Settings.shutter_closetime[i] = (Settings.shutter_closetime[i] > 0) ? Settings.shutter_closetime[i] : 100; //temporary hard coded. - Shutter.pwm_min[i] = pwm_min; - Shutter.pwm_max[i] = pwm_max; + Shutter[i].pwm_min = pwm_min; + Shutter[i].pwm_max = pwm_max; // Update Calculation 20 because time interval is 0.05 sec ans time is in 0.1sec - Shutter.open_max[i] = STEPS_PER_SECOND * RESOLUTION * Shutter.open_time[i] / 10; - Shutter.close_velocity[i] = Shutter.open_max[i] / Shutter.close_time[i] / 2 ; + Shutter[i].open_max = STEPS_PER_SECOND * RESOLUTION * Shutter[i].open_time / 10; + Shutter[i].close_velocity = Shutter[i].open_max / Shutter[i].close_time / 2 ; // calculate a ramp slope at the first 5 percent to compensate that shutters move with down part later than the upper part if (Settings.shutter_set50percent[i] != 50) { - Settings.shuttercoeff[1][i] = Shutter.open_max[i] * (100 - Settings.shutter_set50percent[i] ) / 5000; - Settings.shuttercoeff[0][i] = Shutter.open_max[i] - (Settings.shuttercoeff[1][i] * 100); + Settings.shuttercoeff[1][i] = Shutter[i].open_max * (100 - Settings.shutter_set50percent[i] ) / 5000; + Settings.shuttercoeff[0][i] = Shutter[i].open_max - (Settings.shuttercoeff[1][i] * 100); Settings.shuttercoeff[2][i] = (Settings.shuttercoeff[0][i] + 5 * Settings.shuttercoeff[1][i]) / 5; } - Shutter.RelayShutterMask |= 3 << (Settings.shutter_startrelay[i] -1); + ShutterGlobal.RelayShutterMask |= 3 << (Settings.shutter_startrelay[i] -1); - Shutter.real_position[i] = ShutterPercentToRealPosition(Settings.shutter_position[i], i); + Shutter[i].real_position = ShutterPercentToRealPosition(Settings.shutter_position[i], i); - Shutter.start_position[i] = Shutter.target_position[i] = Shutter.real_position[i]; - Shutter.motordelay[i] = Settings.shutter_motordelay[i]; - Shutter.lastdirection[i] = (50 < Settings.shutter_position[i]) ? 1 : -1; + Shutter[i].start_position = Shutter[i].target_position = Shutter[i].real_position; + Shutter[i].motordelay = Settings.shutter_motordelay[i]; + Shutter[i].lastdirection = (50 < Settings.shutter_position[i]) ? 1 : -1; - switch (Shutter.position_mode) { + switch (ShutterGlobal.position_mode) { case SHT_PWM_VALUE: - Shutter.open_velocity_max = RESOLUTION; + ShutterGlobal.open_velocity_max = RESOLUTION; break; } - Shutter.close_velocity_max[i] = Shutter.open_velocity_max*Shutter.open_time[i] / Shutter.close_time[i]; + Shutter[i].close_velocity_max = ShutterGlobal.open_velocity_max*Shutter[i].open_time / Shutter[i].close_time; - //AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Shutter %d Openvel %d, Closevel: %d"),i, Shutter.open_velocity_max, Shutter.close_velocity_max[i]); + //AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Shutter %d Openvel %d, Closevel: %d"),i, ShutterGlobal.open_velocity_max, Shutter[i].close_velocity_max); AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT%d: Init. Pos: %d,inverted %d, locked %d, end stop time enabled %d, webButtons inverted %d"), - i+1, Shutter.real_position[i], + i+1, Shutter[i].real_position, (Settings.shutter_options[i]&1) ? 1 : 0, (Settings.shutter_options[i]&2) ? 1 : 0, (Settings.shutter_options[i]&4) ? 1 : 0, (Settings.shutter_options[i]&8) ? 1 : 0); } else { - // terminate loop at first INVALID shutter. + // terminate loop at first INVALID Shutter[i]. break; } ShutterLimitRealAndTargetPositions(i); Settings.shutter_accuracy = 1; - Settings.shutter_mode = Shutter.position_mode; + } } @@ -343,15 +339,15 @@ void ShutterReportPosition(bool always, uint32_t index) n = index+1; } for (i; i < n; i++) { - //AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Shutter %d: Real Pos: %d"), i+1,Shutter.real_position[i]); - uint32_t position = ShutterRealToPercentPosition(Shutter.real_position[i], i); - if (Shutter.direction[i] != 0) { + //AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Shutter %d: Real Pos: %d"), i+1,Shutter[i].real_position); + uint32_t position = ShutterRealToPercentPosition(Shutter[i].real_position, i); + if (Shutter[i].direction != 0) { rules_flag.shutter_moving = 1; ShutterLogPos(i); } if (i && index == MAX_SHUTTERS) { ResponseAppend_P(PSTR(",")); } - uint32_t target = ShutterRealToPercentPosition(Shutter.target_position[i], i); - ResponseAppend_P(JSON_SHUTTER_POS, i+1, (Settings.shutter_options[i] & 1) ? 100-position : position, Shutter.direction[i],(Settings.shutter_options[i] & 1) ? 100-target : target ); + uint32_t target = ShutterRealToPercentPosition(Shutter[i].target_position, i); + ResponseAppend_P(JSON_SHUTTER_POS, i+1, (Settings.shutter_options[i] & 1) ? 100-position : position, Shutter[i].direction,(Settings.shutter_options[i] & 1) ? 100-target : target ); } ResponseJsonEnd(); if (always || (rules_flag.shutter_moving)) { @@ -363,36 +359,36 @@ void ShutterReportPosition(bool always, uint32_t index) } void ShutterLimitRealAndTargetPositions(uint32_t i) { - if (Shutter.real_position[i]<0) Shutter.real_position[i] = 0; - if (Shutter.real_position[i]>Shutter.open_max[i]) Shutter.real_position[i] = Shutter.open_max[i]; - if (Shutter.target_position[i]<0) Shutter.target_position[i] = 0; - if (Shutter.target_position[i]>Shutter.open_max[i]) Shutter.target_position[i] = Shutter.open_max[i]; + if (Shutter[i].real_position<0) Shutter[i].real_position = 0; + if (Shutter[i].real_position>Shutter[i].open_max) Shutter[i].real_position = Shutter[i].open_max; + if (Shutter[i].target_position<0) Shutter[i].target_position = 0; + if (Shutter[i].target_position>Shutter[i].open_max) Shutter[i].target_position = Shutter[i].open_max; } void ShutterCalculateAccelerator(uint8_t i) { // No Logging allowed. Part of RTC Timer - if (Shutter.direction[i] != 0) { - switch (Shutter.position_mode) { + if (Shutter[i].direction != 0) { + switch (ShutterGlobal.position_mode) { case SHT_COUNTER: case SHT_PWM_VALUE: // calculate max velocity allowed in this direction - velocity_max = Shutter.direction[i] == 1 ? Shutter.open_velocity_max : Shutter.close_velocity_max[i]; + velocity_max = Shutter[i].direction == 1 ? ShutterGlobal.open_velocity_max : Shutter[i].close_velocity_max; // calculate max change of velocyty based on the defined motordelay in steps - velocity_change_per_step_max = velocity_max / (Shutter.motordelay[i]>0 ? Shutter.motordelay[i] : 1); + velocity_change_per_step_max = velocity_max / (Shutter[i].motordelay>0 ? Shutter[i].motordelay : 1); // minimumtime required from current velocity to stop - min_runtime_ms = Shutter.pwm_velocity[i] * 1000 / STEPS_PER_SECOND / velocity_change_per_step_max; + min_runtime_ms = Shutter[i].pwm_velocity * 1000 / STEPS_PER_SECOND / velocity_change_per_step_max; // decellartion way from current velocity - current_stop_way = (min_runtime_ms * (Shutter.pwm_velocity[i]+velocity_change_per_step_max)/100 - Shutter.pwm_velocity[i])*RESOLUTION/Shutter.open_velocity_max * Shutter.direction[i] ; - next_possible_stop_position = Shutter.real_position[i] + current_stop_way ; + current_stop_way = (min_runtime_ms * (Shutter[i].pwm_velocity+velocity_change_per_step_max)/100 - Shutter[i].pwm_velocity)*RESOLUTION/ShutterGlobal.open_velocity_max * Shutter[i].direction ; + next_possible_stop_position = Shutter[i].real_position + current_stop_way ; toBeAcc = 0; // ensure that the accelerotor kicks in at least one step BEFORE it is to late and a hard stop required. - if (Shutter.accelerator[i] < 0 || (next_possible_stop_position * Shutter.direction[i]) +RESOLUTION*Shutter.pwm_velocity[i]/Shutter.open_velocity_max>= Shutter.target_position[i] * Shutter.direction[i] ) { + if (Shutter[i].accelerator < 0 || (next_possible_stop_position * Shutter[i].direction) +RESOLUTION*Shutter[i].pwm_velocity/ShutterGlobal.open_velocity_max>= Shutter[i].target_position * Shutter[i].direction ) { // 10 times the deviation is the p-value of this simple p-regulator - toBeAcc = 100+(Shutter.direction[i]*(next_possible_stop_position-Shutter.target_position[i])*velocity_max/Shutter.pwm_velocity[i]*10/RESOLUTION); - Shutter.accelerator[i] = - tmin(tmax( velocity_change_per_step_max*toBeAcc/100 , (velocity_change_per_step_max*9/10)), (velocity_change_per_step_max*11/10)); - } else if ( Shutter.accelerator[i] > 0 && Shutter.pwm_velocity[i] == velocity_max) { - Shutter.accelerator[i] = 0; + toBeAcc = 100+(Shutter[i].direction*(next_possible_stop_position-Shutter[i].target_position)*velocity_max/Shutter[i].pwm_velocity*10/RESOLUTION); + Shutter[i].accelerator = - tmin(tmax( velocity_change_per_step_max*toBeAcc/100 , (velocity_change_per_step_max*9/10)), (velocity_change_per_step_max*11/10)); + } else if ( Shutter[i].accelerator > 0 && Shutter[i].pwm_velocity == velocity_max) { + Shutter[i].accelerator = 0; } break; } @@ -401,37 +397,37 @@ void ShutterCalculateAccelerator(uint8_t i) void ShutterDecellerateForStop(uint8_t i) { - switch (Shutter.position_mode) { + switch (ShutterGlobal.position_mode) { case SHT_PWM_VALUE: case SHT_COUNTER: int16_t missing_steps; - Shutter.accelerator[i] = -(Shutter.open_velocity_max / (Shutter.motordelay[i]>0 ? Shutter.motordelay[i] : 1) *11/10); - while (Shutter.pwm_velocity[i] > -2*Shutter.accelerator[i] ) { - AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: velocity: %ld, delta: %d"), Shutter.pwm_velocity[i], Shutter.accelerator[i] ); - //Shutter.pwm_velocity[i] = tmax(Shutter.pwm_velocity[i]-Shutter.accelerator[i] , 0); + Shutter[i].accelerator = -(ShutterGlobal.open_velocity_max / (Shutter[i].motordelay>0 ? Shutter[i].motordelay : 1) *11/10); + while (Shutter[i].pwm_velocity > -2*Shutter[i].accelerator ) { + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: velocity: %ld, delta: %d"), Shutter[i].pwm_velocity, Shutter[i].accelerator ); + //Shutter[i].pwm_velocity = tmax(Shutter[i].pwm_velocity-Shutter[i].accelerator , 0); // Control will be done in RTC Ticker. delay(50); } - if (Shutter.position_mode == SHT_COUNTER){ - missing_steps = ((Shutter.target_position[i]-Shutter.start_position[i])*Shutter.direction[i]*Shutter.open_velocity_max/RESOLUTION/STEPS_PER_SECOND) - RtcSettings.pulse_counter[i]; + if (ShutterGlobal.position_mode == SHT_COUNTER){ + missing_steps = ((Shutter[i].target_position-Shutter[i].start_position)*Shutter[i].direction*ShutterGlobal.open_velocity_max/RESOLUTION/STEPS_PER_SECOND) - RtcSettings.pulse_counter[i]; //prepare for stop PWM - AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Remain steps %d, counter %d, freq %d"), missing_steps, RtcSettings.pulse_counter[i] ,Shutter.pwm_velocity[i]); - Shutter.accelerator[i] = 0; - Shutter.pwm_velocity[i] = Shutter.pwm_velocity[i] > 250 ? 250 : Shutter.pwm_velocity[i]; - analogWriteFreq(Shutter.pwm_velocity[i]); + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Remain steps %d, counter %d, freq %d"), missing_steps, RtcSettings.pulse_counter[i] ,Shutter[i].pwm_velocity); + Shutter[i].accelerator = 0; + Shutter[i].pwm_velocity = Shutter[i].pwm_velocity > 250 ? 250 : Shutter[i].pwm_velocity; + analogWriteFreq(Shutter[i].pwm_velocity); analogWrite(Pin(GPIO_PWM1, i), 50); - Shutter.pwm_velocity[i] = 0; - analogWriteFreq(Shutter.pwm_velocity[i]); - while (RtcSettings.pulse_counter[i] < (uint32_t)(Shutter.target_position[i]-Shutter.start_position[i])*Shutter.direction[i]*Shutter.open_velocity_max/RESOLUTION/STEPS_PER_SECOND) { + Shutter[i].pwm_velocity = 0; + analogWriteFreq(Shutter[i].pwm_velocity); + while (RtcSettings.pulse_counter[i] < (uint32_t)(Shutter[i].target_position-Shutter[i].start_position)*Shutter[i].direction*ShutterGlobal.open_velocity_max/RESOLUTION/STEPS_PER_SECOND) { delay(1); } analogWrite(Pin(GPIO_PWM1, i), 0); // removed with 8.3 because of reset caused by watchog - Shutter.real_position[i] = ShutterCalculatePosition(i); - AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Real %d, pulsecount %d, start %d"), Shutter.real_position[i],RtcSettings.pulse_counter[i], Shutter.start_position[i]); + Shutter[i].real_position = ShutterCalculatePosition(i); + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Real %d, pulsecount %d, start %d"), Shutter[i].real_position,RtcSettings.pulse_counter[i], Shutter[i].start_position); } - Shutter.direction[i] = 0; - Shutter.pwm_velocity[i] = 0; + Shutter[i].direction = 0; + Shutter[i].pwm_velocity = 0; break; } } @@ -439,11 +435,11 @@ void ShutterDecellerateForStop(uint8_t i) void ShutterPowerOff(uint8_t i) { AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Stop Shutter %d .."), i); ShutterDecellerateForStop(i); - if (Shutter.direction[i] !=0) { - Shutter.direction[i] = 0; + if (Shutter[i].direction !=0) { + Shutter[i].direction = 0; delay(MOTOR_STOP_TIME); } - switch (Shutter.switch_mode[i]) { + switch (Shutter[i].switch_mode) { case SHT_SWITCH: if ((1 << (Settings.shutter_startrelay[i]-1)) & power) { ExecuteCommandPowerShutter(Settings.shutter_startrelay[i], 0, SRC_SHUTTER); @@ -453,7 +449,7 @@ void ShutterPowerOff(uint8_t i) { } break; case SHT_PULSE: - uint8_t cur_relay = Settings.shutter_startrelay[i] + (Shutter.direction[i] == 1 ? 0 : (uint8_t)(Shutter.position_mode == SHT_TIME)) ; + uint8_t cur_relay = Settings.shutter_startrelay[i] + (Shutter[i].direction == 1 ? 0 : (uint8_t)(ShutterGlobal.position_mode == SHT_TIME)) ; // we have a momentary switch here. Needs additional pulse on same relay after the end if ((SRC_PULSETIMER == last_source || SRC_SHUTTER == last_source || SRC_WEBGUI == last_source)) { ExecuteCommandPowerShutter(cur_relay, 1, SRC_SHUTTER); @@ -467,10 +463,10 @@ void ShutterPowerOff(uint8_t i) { break; } // Store current PWM value to ensure proper position after reboot. - switch (Shutter.position_mode) { + switch (ShutterGlobal.position_mode) { case SHT_PWM_VALUE: char scmnd[20]; - snprintf_P(scmnd, sizeof(scmnd), PSTR(D_CMND_PWM " %d" ),Shutter.pwm_value[i]); + snprintf_P(scmnd, sizeof(scmnd), PSTR(D_CMND_PWM " %d" ),Shutter[i].pwm_value); ExecuteCommand(scmnd, SRC_BUTTON); break; } @@ -483,29 +479,29 @@ void ShutterUpdatePosition(void) char stopic[TOPSZ]; for (uint32_t i = 0; i < shutters_present; i++) { - if (Shutter.direction[i] != 0) { + if (Shutter[i].direction != 0) { // Calculate position with counter. Much more accurate and no need for motordelay workaround // adding some steps to stop early - //Shutter.real_position[i] = ShutterCalculatePosition(i); - if (!Shutter.start_reported) { + //Shutter[i].real_position = ShutterCalculatePosition(i); + if (!ShutterGlobal.start_reported) { ShutterReportPosition(true, i); XdrvRulesProcess(); - Shutter.start_reported = 1; + ShutterGlobal.start_reported = 1; } //ShutterCalculateAccelerator(i); - AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: time: %d, toBeAcc %d, current_stop_way %d,vel_vur %d, vel_max %d, act_vel_change %d, min_runtime_ms %d, act.pos %d, next_stop %d, target: %d, velocity_change_per_step_max %d"),Shutter.time[i],toBeAcc,current_stop_way, - Shutter.pwm_velocity[i],velocity_max, Shutter.accelerator[i],min_runtime_ms,Shutter.real_position[i], next_possible_stop_position,Shutter.target_position[i],velocity_change_per_step_max); + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: time: %d, toBeAcc %d, current_stop_way %d,vel_vur %d, vel_max %d, act_vel_change %d, min_runtime_ms %d, act.pos %d, next_stop %d, target: %d, velocity_change_per_step_max %d"),Shutter[i].time,toBeAcc,current_stop_way, + Shutter[i].pwm_velocity,velocity_max, Shutter[i].accelerator,min_runtime_ms,Shutter[i].real_position, next_possible_stop_position,Shutter[i].target_position,velocity_change_per_step_max); - if ( Shutter.real_position[i] * Shutter.direction[i] >= Shutter.target_position[i] * Shutter.direction[i] || Shutter.pwm_velocity[i]= Shutter[i].target_position * Shutter[i].direction || Shutter[i].pwm_velocity0 ? Shutter.motordelay[i] : 1); - Shutter.target_position[i] = target_pos; - Shutter.start_position[i] = Shutter.real_position[i]; - Shutter.time[i] = 0; - Shutter.skip_relay_change = 0; - Shutter.direction[i] = direction; + Shutter[i].accelerator = ShutterGlobal.open_velocity_max / (Shutter[i].motordelay>0 ? Shutter[i].motordelay : 1); + Shutter[i].target_position = target_pos; + Shutter[i].start_position = Shutter[i].real_position; + Shutter[i].time = 0; + ShutterGlobal.skip_relay_change = 0; + Shutter[i].direction = direction; rules_flag.shutter_moving = 1; rules_flag.shutter_moved = 0; - Shutter.start_reported = 0; - //AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: real %d, start %d, counter %d,freq_max %d, dir %d, freq %d"),Shutter.real_position[i], Shutter.start_position[i] ,RtcSettings.pulse_counter[i],Shutter.open_velocity_max , Shutter.direction[i] ,Shutter.open_velocity_max ); + ShutterGlobal.start_reported = 0; + //AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: real %d, start %d, counter %d,freq_max %d, dir %d, freq %d"),Shutter[i].real_position, Shutter[i].start_position ,RtcSettings.pulse_counter[i],ShutterGlobal.open_velocity_max , Shutter[i].direction ,ShutterGlobal.open_velocity_max ); } - //AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Start shutter: %d from %d to %d in direction %d"), i, Shutter.start_position[i], Shutter.target_position[i], Shutter.direction[i]); + //AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Start shutter: %d from %d to %d in direction %d"), i, Shutter[i].start_position, Shutter[i].target_position, Shutter[i].direction); } - int32_t ShutterCalculatePosition(uint32_t i) { // No Logging allowed. Part of RTC Timer - if (Shutter.direction[i] != 0) { - switch (Shutter.position_mode) { + if (Shutter[i].direction != 0) { + switch (ShutterGlobal.position_mode) { case SHT_COUNTER: - return ((int32_t)RtcSettings.pulse_counter[i]*Shutter.direction[i]*STEPS_PER_SECOND*RESOLUTION / Shutter.open_velocity_max)+Shutter.start_position[i]; + return ((int32_t)RtcSettings.pulse_counter[i]*Shutter[i].direction*STEPS_PER_SECOND*RESOLUTION / ShutterGlobal.open_velocity_max)+Shutter[i].start_position; break; case SHT_TIME: case SHT_TIME_UP_DOWN: case SHT_TIME_GARAGE: - return Shutter.start_position[i] + ( (Shutter.time[i] - Shutter.motordelay[i]) * (Shutter.direction[i] > 0 ? RESOLUTION : -Shutter.close_velocity[i])); + return Shutter[i].start_position + ( (Shutter[i].time - Shutter[i].motordelay) * (Shutter[i].direction > 0 ? RESOLUTION : -Shutter[i].close_velocity)); break; case SHT_PWM_TIME: break; case SHT_PWM_VALUE: - return Shutter.real_position[i]; + return Shutter[i].real_position; break; default: break; } } else { - return Shutter.real_position[i]; + return Shutter[i].real_position; } } void ShutterRelayChanged(void) { - // Shutter.RelayCurrentMask = binary relay that was recently changed and cause an Action + // ShutterGlobal.RelayCurrentMask = binary relay that was recently changed and cause an Action // powerstate_local = binary powermatrix and relays from shutter: 0..3 // relays_changed = bool if one of the relays that belong to the shutter changed not by shutter or pulsetimer char stemp1[10]; @@ -599,25 +594,25 @@ void ShutterRelayChanged(void) for (uint32_t i = 0; i < shutters_present; i++) { power_t powerstate_local = (power >> (Settings.shutter_startrelay[i] -1)) & 3; // SRC_IGNORE added because INTERLOCK function bite causes this as last source for changing the relay. - //uint8 manual_relays_changed = ((Shutter.RelayCurrentMask >> (Settings.shutter_startrelay[i] -1)) & 3) && SRC_IGNORE != last_source && SRC_SHUTTER != last_source && SRC_PULSETIMER != last_source ; - uint8 manual_relays_changed = ((Shutter.RelayCurrentMask >> (Settings.shutter_startrelay[i] -1)) & 3) && SRC_SHUTTER != last_source && SRC_PULSETIMER != last_source ; - AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Shutter %d: source: %s, powerstate_local %ld, Shutter.RelayCurrentMask %d, manual change %d"), i+1, GetTextIndexed(stemp1, sizeof(stemp1), last_source, kCommandSource), powerstate_local,Shutter.RelayCurrentMask,manual_relays_changed); + //uint8 manual_relays_changed = ((ShutterGlobal.RelayCurrentMask >> (Settings.shutter_startrelay[i] -1)) & 3) && SRC_IGNORE != last_source && SRC_SHUTTER != last_source && SRC_PULSETIMER != last_source ; + uint8 manual_relays_changed = ((ShutterGlobal.RelayCurrentMask >> (Settings.shutter_startrelay[i] -1)) & 3) && SRC_SHUTTER != last_source && SRC_PULSETIMER != last_source ; + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Shutter %d: source: %s, powerstate_local %ld, ShutterGlobal.RelayCurrentMask %d, manual change %d"), i+1, GetTextIndexed(stemp1, sizeof(stemp1), last_source, kCommandSource), powerstate_local,ShutterGlobal.RelayCurrentMask,manual_relays_changed); if (manual_relays_changed) { - //Shutter.skip_relay_change = true; + //ShutterGlobal.skip_relay_change = true; ShutterLimitRealAndTargetPositions(i); - switch (Shutter.switch_mode[i] ) { + switch (Shutter[i].switch_mode ) { case SHT_PULSE: - if (Shutter.direction[i] != 0 && powerstate_local) { - Shutter.target_position[i] = Shutter.real_position[i]; + if (Shutter[i].direction != 0 && powerstate_local) { + Shutter[i].target_position = Shutter[i].real_position; powerstate_local = 0; - AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Shutter %d: Switch OFF motor. Target: %ld, source: %s, powerstate_local %ld, Shutter.RelayCurrentMask %d, manual change %d"), i+1, Shutter.target_position[i], GetTextIndexed(stemp1, sizeof(stemp1), last_source, kCommandSource), powerstate_local,Shutter.RelayCurrentMask,manual_relays_changed); + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Shutter %d: Switch OFF motor. Target: %ld, source: %s, powerstate_local %ld, ShutterGlobal.RelayCurrentMask %d, manual change %d"), i+1, Shutter[i].target_position, GetTextIndexed(stemp1, sizeof(stemp1), last_source, kCommandSource), powerstate_local,ShutterGlobal.RelayCurrentMask,manual_relays_changed); } break; default: last_source = SRC_SHUTTER; // avoid switch off in the next loop - if (Shutter.direction[i] != 0 ) ShutterPowerOff(i); + if (Shutter[i].direction != 0 ) ShutterPowerOff(i); } - switch (Shutter.position_mode) { + switch (ShutterGlobal.position_mode) { // enum Shutterposition_mode {SHT_TIME, SHT_TIME_UP_DOWN, SHT_TIME_GARAGE, SHT_COUNTER, SHT_PWM_VALUE, SHT_PWM_TIME,}; case SHT_TIME_UP_DOWN: case SHT_COUNTER: @@ -626,42 +621,42 @@ void ShutterRelayChanged(void) ShutterPowerOff(i); switch (powerstate_local) { case 1: - ShutterStartInit(i, 1, Shutter.open_max[i]); + ShutterStartInit(i, 1, Shutter[i].open_max); break; case 3: ShutterStartInit(i, -1, 0); break; default: //AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Shutter %d: Switch OFF motor."),i); - Shutter.target_position[i] = Shutter.real_position[i]; + Shutter[i].target_position = Shutter[i].real_position; } break; case SHT_TIME: switch (powerstate_local) { case 1: - ShutterStartInit(i, 1, Shutter.open_max[i]); + ShutterStartInit(i, 1, Shutter[i].open_max); break; case 2: ShutterStartInit(i, -1, 0); break; default: //AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Shutter %d: Switch OFF motor."),i); - Shutter.target_position[i] = Shutter.real_position[i]; + Shutter[i].target_position = Shutter[i].real_position; } break; case SHT_TIME_GARAGE: switch (powerstate_local) { case 1: - ShutterStartInit(i, Shutter.lastdirection[i]*-1 , Shutter.lastdirection[i] == 1 ? 0 : Shutter.open_max[i]); - AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Shutter %d Garage. NewTarget %d"), i, Shutter.target_position[i]); + ShutterStartInit(i, Shutter[i].lastdirection*-1 , Shutter[i].lastdirection == 1 ? 0 : Shutter[i].open_max); + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Shutter %d Garage. NewTarget %d"), i, Shutter[i].target_position); break; default: - Shutter.target_position[i] = Shutter.real_position[i]; + Shutter[i].target_position = Shutter[i].real_position; } - } // switch (Shutter.position_mode) - AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Shutter %d: Target: %ld, powerstatelocal %d"), i+1, Shutter.target_position[i], powerstate_local); + } // switch (ShutterGlobal.position_mode) + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Shutter %d: Target: %ld, powerstatelocal %d"), i+1, Shutter[i].target_position, powerstate_local); } // if (manual_relays_changed) } // for (uint32_t i = 0; i < shutters_present; i++) } @@ -690,7 +685,7 @@ void ShutterButtonHandler(void) buttonState = SHT_PRESSED_MULTI; press_index = 1; } else { - if ((Shutter.direction[shutter_index]) && (Button.press_counter[button_index]==0)) { + if ((Shutter[shutter_index].direction) && (Button.press_counter[button_index]==0)) { buttonState = SHT_PRESSED_IMMEDIATE; press_index = 1; Button.press_counter[button_index] = 99; // Remember to discard further action for press & hold within button timings @@ -820,7 +815,7 @@ void ShutterButtonHandler(void) } else { uint8_t position = (Settings.shutter_button[button_index]>>(6*pos_press_index + 2)) & 0x03f; if (position) { - if (Shutter.direction[shutter_index]) { + if (Shutter[shutter_index].direction) { XdrvMailbox.payload = XdrvMailbox.index; CmndShutterStop(); } else { @@ -873,10 +868,10 @@ void ShutterToggle(bool dir) if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= shutters_present)) { uint32_t index = XdrvMailbox.index-1; if (dir) { - XdrvMailbox.payload = (Shutter.lastdirection[index] > 0) ? 0 : 100; + XdrvMailbox.payload = (Shutter[index].lastdirection > 0) ? 0 : 100; } else { - XdrvMailbox.payload = (50 < ShutterRealToPercentPosition(Shutter.real_position[index], index)) ? 0 : 100; + XdrvMailbox.payload = (50 < ShutterRealToPercentPosition(Shutter[index].real_position, index)) ? 0 : 100; } XdrvMailbox.data_len = 0; last_source = SRC_WEBGUI; @@ -903,7 +898,7 @@ void CmndShutterStopOpen(void) { if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= shutters_present)) { uint32_t index = XdrvMailbox.index-1; - if (Shutter.direction[index]) { + if (Shutter[index].direction) { CmndShutterStop(); } else { CmndShutterOpen(); @@ -927,7 +922,7 @@ void CmndShutterStopClose(void) { if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= shutters_present)) { uint32_t index = XdrvMailbox.index-1; - if (Shutter.direction[index]) { + if (Shutter[index].direction) { CmndShutterStop(); } else { CmndShutterClose(); @@ -949,7 +944,7 @@ void CmndShutterStopToggle(void) { if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= shutters_present)) { uint32_t index = XdrvMailbox.index-1; - if (Shutter.direction[index]) { + if (Shutter[index].direction) { CmndShutterStop(); } else { CmndShutterToggle(); @@ -961,7 +956,7 @@ void CmndShutterStopToggleDir(void) { if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= shutters_present)) { uint32_t index = XdrvMailbox.index-1; - if (Shutter.direction[index]) { + if (Shutter[index].direction) { CmndShutterStop(); } else { CmndShutterToggleDir(); @@ -977,13 +972,13 @@ void CmndShutterStop(void) XdrvMailbox.index = XdrvMailbox.payload; } uint32_t i = XdrvMailbox.index -1; - if (Shutter.direction[i] != 0) { + if (Shutter[i].direction != 0) { - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Stop moving %d: dir: %d"), XdrvMailbox.index, Shutter.direction[i]); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Stop moving %d: dir: %d"), XdrvMailbox.index, Shutter[i].direction); // set stop position 10 steps ahead (0.5sec to allow normal stop) //ToDo: Replace with function - int32_t temp_realpos = Shutter.start_position[i] + ( (Shutter.time[i]+10) * (Shutter.direction[i] > 0 ? 100 : -Shutter.close_velocity[i])); + int32_t temp_realpos = Shutter[i].start_position + ( (Shutter[i].time+10) * (Shutter[i].direction > 0 ? 100 : -Shutter[i].close_velocity)); XdrvMailbox.payload = ShutterRealToPercentPosition(temp_realpos, i); //XdrvMailbox.payload = Settings.shuttercoeff[2][i] * 5 > temp_realpos ? temp_realpos / Settings.shuttercoeff[2][i] : (temp_realpos-Settings.shuttercoeff[0,i]) / Settings.shuttercoeff[1][i]; last_source = SRC_WEBGUI; @@ -1011,11 +1006,11 @@ void CmndShutterPosition(void) // special handling fo UP,DOWN,TOGGLE,STOP command comming with payload -99 if ((XdrvMailbox.data_len > 1) && (XdrvMailbox.payload <= 0)) { //UpperCase(XdrvMailbox.data, XdrvMailbox.data); - if (!strcasecmp(XdrvMailbox.data,D_CMND_SHUTTER_UP) || !strcasecmp(XdrvMailbox.data,D_CMND_SHUTTER_OPEN) || ((Shutter.direction[index]==0) && !strcasecmp(XdrvMailbox.data,D_CMND_SHUTTER_STOPOPEN))) { + if (!strcasecmp(XdrvMailbox.data,D_CMND_SHUTTER_UP) || !strcasecmp(XdrvMailbox.data,D_CMND_SHUTTER_OPEN) || ((Shutter[index].direction==0) && !strcasecmp(XdrvMailbox.data,D_CMND_SHUTTER_STOPOPEN))) { CmndShutterOpen(); return; } - if (!strcasecmp(XdrvMailbox.data,D_CMND_SHUTTER_DOWN) || !strcasecmp(XdrvMailbox.data,D_CMND_SHUTTER_CLOSE) || ((Shutter.direction[index]==0) && !strcasecmp(XdrvMailbox.data,D_CMND_SHUTTER_STOPCLOSE))) { + if (!strcasecmp(XdrvMailbox.data,D_CMND_SHUTTER_DOWN) || !strcasecmp(XdrvMailbox.data,D_CMND_SHUTTER_CLOSE) || ((Shutter[index].direction==0) && !strcasecmp(XdrvMailbox.data,D_CMND_SHUTTER_STOPCLOSE))) { CmndShutterClose(); return; } @@ -1027,76 +1022,76 @@ void CmndShutterPosition(void) CmndShutterToggleDir(); return; } - if (!strcasecmp(XdrvMailbox.data,D_CMND_SHUTTER_STOP) || ((Shutter.direction[index]) && (!strcasecmp(XdrvMailbox.data,D_CMND_SHUTTER_STOPOPEN) || !strcasecmp(XdrvMailbox.data,D_CMND_SHUTTER_STOPCLOSE)))) { + if (!strcasecmp(XdrvMailbox.data,D_CMND_SHUTTER_STOP) || ((Shutter[index].direction) && (!strcasecmp(XdrvMailbox.data,D_CMND_SHUTTER_STOPOPEN) || !strcasecmp(XdrvMailbox.data,D_CMND_SHUTTER_STOPCLOSE)))) { XdrvMailbox.payload = -99; CmndShutterStop(); return; } } - int8_t target_pos_percent = (XdrvMailbox.payload < 0) ? (XdrvMailbox.payload == -99 ? ShutterRealToPercentPosition(Shutter.real_position[index], index) : 0) : ((XdrvMailbox.payload > 100) ? 100 : XdrvMailbox.payload); + int8_t target_pos_percent = (XdrvMailbox.payload < 0) ? (XdrvMailbox.payload == -99 ? ShutterRealToPercentPosition(Shutter[index].real_position, index) : 0) : ((XdrvMailbox.payload > 100) ? 100 : XdrvMailbox.payload); // webgui still send also on inverted shutter the native position. target_pos_percent = ((Settings.shutter_options[index] & 1) && (SRC_WEBGUI != last_source)) ? 100 - target_pos_percent : target_pos_percent; if (XdrvMailbox.payload != -99) { //target_pos_percent = (Settings.shutter_options[index] & 1) ? 100 - target_pos_percent : target_pos_percent; - Shutter.target_position[index] = ShutterPercentToRealPosition(target_pos_percent, index); - //Shutter.accelerator[index] = Shutter.open_velocity_max / ((Shutter.motordelay[index] > 0) ? Shutter.motordelay[index] : 1); - //Shutter.target_position[index] = XdrvMailbox.payload < 5 ? Settings.shuttercoeff[2][index] * XdrvMailbox.payload : Settings.shuttercoeff[1][index] * XdrvMailbox.payload + Settings.shuttercoeff[0,index]; - AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: lastsource %d:, real %d, target %d, payload %d"), last_source, Shutter.real_position[index] ,Shutter.target_position[index],target_pos_percent); + Shutter[index].target_position = ShutterPercentToRealPosition(target_pos_percent, index); + //Shutter[i].accelerator[index] = ShutterGlobal.open_velocity_max / ((Shutter[i].motordelay[index] > 0) ? Shutter[i].motordelay[index] : 1); + //Shutter[i].target_position[index] = XdrvMailbox.payload < 5 ? Settings.shuttercoeff[2][index] * XdrvMailbox.payload : Settings.shuttercoeff[1][index] * XdrvMailbox.payload + Settings.shuttercoeff[0,index]; + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: lastsource %d:, real %d, target %d, payload %d"), last_source, Shutter[index].real_position ,Shutter[index].target_position,target_pos_percent); } - if ( (target_pos_percent >= 0) && (target_pos_percent <= 100) && abs(Shutter.target_position[index] - Shutter.real_position[index] ) / Shutter.close_velocity[index] > 2) { + if ( (target_pos_percent >= 0) && (target_pos_percent <= 100) && abs(Shutter[index].target_position - Shutter[index].real_position ) / Shutter[index].close_velocity > 2) { if (Settings.shutter_options[index] & 4) { - if (0 == target_pos_percent) Shutter.target_position[index] -= 1 * RESOLUTION * STEPS_PER_SECOND; - if (100 == target_pos_percent) Shutter.target_position[index] += 1 * RESOLUTION * STEPS_PER_SECOND; + if (0 == target_pos_percent) Shutter[index].target_position -= 1 * RESOLUTION * STEPS_PER_SECOND; + if (100 == target_pos_percent) Shutter[index].target_position += 1 * RESOLUTION * STEPS_PER_SECOND; } - int8_t new_shutterdirection = Shutter.real_position[index] < Shutter.target_position[index] ? 1 : -1; - if (Shutter.direction[index] == -new_shutterdirection) { + int8_t new_shutterdirection = Shutter[index].real_position < Shutter[index].target_position ? 1 : -1; + if (Shutter[index].direction == -new_shutterdirection) { ShutterPowerOff(index); } - if (Shutter.direction[index] != new_shutterdirection) { - ShutterStartInit(index, new_shutterdirection, Shutter.target_position[index]); - switch (Shutter.position_mode) { + if (Shutter[index].direction != new_shutterdirection) { + ShutterStartInit(index, new_shutterdirection, Shutter[index].target_position); + switch (ShutterGlobal.position_mode) { case SHT_COUNTER: case SHT_PWM_TIME: case SHT_PWM_VALUE: case SHT_TIME_UP_DOWN: - if (!Shutter.skip_relay_change) { + if (!ShutterGlobal.skip_relay_change) { // Code for shutters with circuit safe configuration, switch the direction Relay ExecuteCommandPowerShutter(Settings.shutter_startrelay[index] +1, new_shutterdirection == 1 ? 0 : 1, SRC_SHUTTER); // power on ExecuteCommandPowerShutter(Settings.shutter_startrelay[index], 1, SRC_SHUTTER); } - if (Shutter.position_mode != SHT_TIME_UP_DOWN) ExecuteCommandPowerShutter(Settings.shutter_startrelay[index]+2, 1, SRC_SHUTTER); + if (ShutterGlobal.position_mode != SHT_TIME_UP_DOWN) ExecuteCommandPowerShutter(Settings.shutter_startrelay[index]+2, 1, SRC_SHUTTER); break; case SHT_TIME: - if (!Shutter.skip_relay_change) { + if (!ShutterGlobal.skip_relay_change) { if ( (power >> (Settings.shutter_startrelay[index] -1)) & 3 > 0 ) { - ExecuteCommandPowerShutter(Settings.shutter_startrelay[index] + (new_shutterdirection == 1 ? 1 : 0), Shutter.switch_mode[index] == SHT_SWITCH ? 0 : 1, SRC_SHUTTER); + ExecuteCommandPowerShutter(Settings.shutter_startrelay[index] + (new_shutterdirection == 1 ? 1 : 0), Shutter[index].switch_mode == SHT_SWITCH ? 0 : 1, SRC_SHUTTER); } ExecuteCommandPowerShutter(Settings.shutter_startrelay[index] + (new_shutterdirection == 1 ? 0 : 1), 1, SRC_SHUTTER); } break; case SHT_TIME_GARAGE: - if (!Shutter.skip_relay_change) { - if (new_shutterdirection == Shutter.lastdirection[index]) { - AddLog_P2(LOG_LEVEL_INFO, PSTR("SHT: Garage not move in this direction: %d"), Shutter.switch_mode[index] == SHT_PULSE); - for (uint8_t k=0 ; k <= (uint8_t)(Shutter.switch_mode[index] == SHT_PULSE) ; k++) { + if (!ShutterGlobal.skip_relay_change) { + if (new_shutterdirection == Shutter[index].lastdirection) { + AddLog_P2(LOG_LEVEL_INFO, PSTR("SHT: Garage not move in this direction: %d"), Shutter[index].switch_mode == SHT_PULSE); + for (uint8_t k=0 ; k <= (uint8_t)(Shutter[index].switch_mode == SHT_PULSE) ; k++) { ExecuteCommandPowerShutter(Settings.shutter_startrelay[index], 1, SRC_SHUTTER); delay(500); ExecuteCommandPowerShutter(Settings.shutter_startrelay[index], 0, SRC_SHUTTER); delay(500); } // reset shutter time to avoid 2 seconds above count as runtime - Shutter.time[index] = 0; - } // if (new_shutterdirection == Shutter.lastdirection[index]) + Shutter[index].time = 0; + } // if (new_shutterdirection == Shutter[i].lastdirection[index]) ExecuteCommandPowerShutter(Settings.shutter_startrelay[index], 1, SRC_SHUTTER); - } // if (!Shutter.skip_relay_change) + } // if (!ShutterGlobal.skip_relay_change) break; - } // switch (Shutter.position_mode) - Shutter.RelayCurrentMask = 0; - } // if (Shutter.direction[index] != new_shutterdirection) + } // switch (ShutterGlobal.position_mode) + ShutterGlobal.RelayCurrentMask = 0; + } // if (Shutter[i].direction[index] != new_shutterdirection) } else { - target_pos_percent = ShutterRealToPercentPosition(Shutter.real_position[index], index); + target_pos_percent = ShutterRealToPercentPosition(Shutter[index].real_position, index); ShutterReportPosition(true, index); } XdrvMailbox.index = index +1; // Fix random index for ShutterClose @@ -1114,7 +1109,7 @@ void CmndShutterStopPosition(void) { if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= shutters_present)) { uint32_t index = XdrvMailbox.index-1; - if (Shutter.direction[index]) { + if (Shutter[index].direction) { XdrvMailbox.payload = -99; CmndShutterStop(); } else { @@ -1164,13 +1159,11 @@ void CmndShutterMotorDelay(void) void CmndShutterMode(void) { if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= MAX_MODES)) { - Shutter.position_mode = XdrvMailbox.payload; + ShutterGlobal.position_mode = XdrvMailbox.payload; Settings.shutter_mode = XdrvMailbox.payload; ShutterInit(); - ResponseCmndNumber(XdrvMailbox.payload); // ???? - } else { - ResponseCmndNumber(Shutter.position_mode); } + ResponseCmndNumber(ShutterGlobal.position_mode); } void CmndShutterRelay(void) @@ -1179,9 +1172,9 @@ void CmndShutterRelay(void) if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 64)) { Settings.shutter_startrelay[XdrvMailbox.index -1] = XdrvMailbox.payload; if (XdrvMailbox.payload > 0) { - Shutter.RelayShutterMask |= 3 << (XdrvMailbox.payload - 1); + ShutterGlobal.RelayShutterMask |= 3 << (XdrvMailbox.payload - 1); } else { - Shutter.RelayShutterMask ^= 3 << (Settings.shutter_startrelay[XdrvMailbox.index -1] - 1); + ShutterGlobal.RelayShutterMask ^= 3 << (Settings.shutter_startrelay[XdrvMailbox.index -1] - 1); } Settings.shutter_startrelay[XdrvMailbox.index -1] = XdrvMailbox.payload; ShutterInit(); @@ -1342,21 +1335,19 @@ void CmndShutterSetHalfway(void) void CmndShutterFrequency(void) { if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload <= 20000)) { - Shutter.open_velocity_max = XdrvMailbox.payload; + ShutterGlobal.open_velocity_max = XdrvMailbox.payload; if (shutters_present < 4) { - Settings.shuttercoeff[4][3] = Shutter.open_velocity_max; + Settings.shuttercoeff[4][3] = ShutterGlobal.open_velocity_max; } ShutterInit(); - ResponseCmndNumber(XdrvMailbox.payload); // ???? - } else { - ResponseCmndNumber(Shutter.open_velocity_max); } + ResponseCmndNumber(ShutterGlobal.open_velocity_max); } void CmndShutterSetClose(void) { if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= shutters_present)) { - Shutter.real_position[XdrvMailbox.index -1] = 0; + Shutter[XdrvMailbox.index -1].real_position = 0; ShutterStartInit(XdrvMailbox.index -1, 0, 0); Settings.shutter_position[XdrvMailbox.index -1] = 0; ResponseCmndIdxChar(D_CONFIGURATION_RESET); @@ -1366,25 +1357,13 @@ void CmndShutterSetClose(void) void CmndShutterSetOpen(void) { if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= shutters_present)) { - Shutter.real_position[XdrvMailbox.index -1] = Shutter.open_max[XdrvMailbox.index -1]; - ShutterStartInit(XdrvMailbox.index -1, 0, Shutter.open_max[XdrvMailbox.index -1]); + Shutter[XdrvMailbox.index -1].real_position = Shutter[XdrvMailbox.index -1].open_max; + ShutterStartInit(XdrvMailbox.index -1, 0, Shutter[XdrvMailbox.index -1].open_max); Settings.shutter_position[XdrvMailbox.index -1] = 100; ResponseCmndIdxChar(D_CONFIGURATION_RESET); } } -void CmndShutterInvert(void) -{ - if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= shutters_present)) { - if (XdrvMailbox.payload == 0) { - Settings.shutter_options[XdrvMailbox.index -1] &= ~(1); - } else if (XdrvMailbox.payload == 1) { - Settings.shutter_options[XdrvMailbox.index -1] |= (1); - } - ResponseCmndIdxNumber((Settings.shutter_options[XdrvMailbox.index -1] & 1) ? 1 : 0); - } -} - void CmndShutterCalibration(void) { if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= shutters_present)) { @@ -1418,38 +1397,31 @@ void CmndShutterCalibration(void) } } -void CmndShutterLock(void) { +void ShutterOptionsSetHelper(uint16_t option){ if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= shutters_present)) { if (XdrvMailbox.payload == 0) { - Settings.shutter_options[XdrvMailbox.index -1] &= ~(2); + Settings.shutter_options[XdrvMailbox.index -1] &= ~(option); } else if (XdrvMailbox.payload == 1) { - Settings.shutter_options[XdrvMailbox.index -1] |= (2); + Settings.shutter_options[XdrvMailbox.index -1] |= (option); } - ResponseCmndIdxNumber((Settings.shutter_options[XdrvMailbox.index -1] & 2) ? 1 : 0); + ResponseCmndIdxNumber((Settings.shutter_options[XdrvMailbox.index -1] & option) ? 1 : 0); } } +void CmndShutterInvert(void) { + ShutterOptionsSetHelper(1); +} + +void CmndShutterLock(void) { + ShutterOptionsSetHelper(2); +} + void CmndShutterEnableEndStopTime(void) { - if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= shutters_present)) { - if (XdrvMailbox.payload == 0) { - Settings.shutter_options[XdrvMailbox.index -1] &= ~(4); - } else if (XdrvMailbox.payload == 1) { - Settings.shutter_options[XdrvMailbox.index -1] |= (4); - } - ResponseCmndIdxNumber((Settings.shutter_options[XdrvMailbox.index -1] & 4) ? 1 : 0); - } + ShutterOptionsSetHelper(4); } -void CmndShutterInvertWebButtons(void) -{ - if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= shutters_present)) { - if (XdrvMailbox.payload == 0) { - Settings.shutter_options[XdrvMailbox.index -1] &= ~(8); - } else if (XdrvMailbox.payload == 1) { - Settings.shutter_options[XdrvMailbox.index -1] |= (8); - } - ResponseCmndIdxNumber((Settings.shutter_options[XdrvMailbox.index -1] & 8) ? 1 : 0); - } +void CmndShutterInvertWebButtons(void) { + ShutterOptionsSetHelper(8); } /*********************************************************************************************\ @@ -1479,10 +1451,10 @@ bool Xdrv27(uint8_t function) case FUNC_JSON_APPEND: for (uint8_t i = 0; i < shutters_present; i++) { uint8_t position = (Settings.shutter_options[i] & 1) ? 100 - Settings.shutter_position[i] : Settings.shutter_position[i]; - uint8_t target = (Settings.shutter_options[i] & 1) ? 100 - ShutterRealToPercentPosition(Shutter.target_position[i], i) : ShutterRealToPercentPosition(Shutter.target_position[i], i); + uint8_t target = (Settings.shutter_options[i] & 1) ? 100 - ShutterRealToPercentPosition(Shutter[i].target_position, i) : ShutterRealToPercentPosition(Shutter[i].target_position, i); ResponseAppend_P(","); - ResponseAppend_P(JSON_SHUTTER_POS, i+1, position, Shutter.direction[i],target); + ResponseAppend_P(JSON_SHUTTER_POS, i+1, position, Shutter[i].direction,target); #ifdef USE_DOMOTICZ if ((0 == tele_period) && (0 == i)) { DomoticzSensor(DZ_SHUTTER, position); @@ -1493,23 +1465,23 @@ bool Xdrv27(uint8_t function) case FUNC_SET_POWER: char stemp1[10]; // extract the number of the relay that was switched and save for later in Update Position. - Shutter.RelayCurrentMask = XdrvMailbox.index ^ Shutter.RelayOldMask; - AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Switched relay: %d by %s"), Shutter.RelayCurrentMask,GetTextIndexed(stemp1, sizeof(stemp1), last_source, kCommandSource)); + ShutterGlobal.RelayCurrentMask = XdrvMailbox.index ^ ShutterGlobal.RelayOldMask; + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Switched relay: %d by %s"), ShutterGlobal.RelayCurrentMask,GetTextIndexed(stemp1, sizeof(stemp1), last_source, kCommandSource)); ShutterRelayChanged(); - Shutter.RelayOldMask = XdrvMailbox.index; + ShutterGlobal.RelayOldMask = XdrvMailbox.index; break; case FUNC_SET_DEVICE_POWER: - if (Shutter.skip_relay_change ) { + if (ShutterGlobal.skip_relay_change ) { uint8_t i; for (i = 0; i < devices_present; i++) { - if (Shutter.RelayCurrentMask &1) { + if (ShutterGlobal.RelayCurrentMask &1) { break; } - Shutter.RelayCurrentMask >>= 1; + ShutterGlobal.RelayCurrentMask >>= 1; } //AddLog_P2(LOG_LEVEL_ERROR, PSTR("SHT: skip relay change: %d"),i+1); result = true; - Shutter.skip_relay_change = 0; + ShutterGlobal.skip_relay_change = 0; AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Skipping switch off relay %d"),i); ExecuteCommandPowerShutter(i+1, 0, SRC_SHUTTER); } From 2db18c3e6d96df2c62346aa786422869b837880d Mon Sep 17 00:00:00 2001 From: stefanbode Date: Wed, 9 Sep 2020 15:24:21 +0200 Subject: [PATCH 38/55] Introduced PWM range for servo shutters --- tasmota/xdrv_27_shutter.ino | 44 +++++++++++++++++++++++++++++++------ 1 file changed, 37 insertions(+), 7 deletions(-) diff --git a/tasmota/xdrv_27_shutter.ino b/tasmota/xdrv_27_shutter.ino index 6b0ef98e7..15e765ff4 100644 --- a/tasmota/xdrv_27_shutter.ino +++ b/tasmota/xdrv_27_shutter.ino @@ -53,14 +53,14 @@ enum ShutterButtonStates { SHT_NOT_PRESSED, SHT_PRESSED_MULTI, SHT_PRESSED_HOLD, const char kShutterCommands[] PROGMEM = D_PRFX_SHUTTER "|" D_CMND_SHUTTER_OPEN "|" D_CMND_SHUTTER_CLOSE "|" D_CMND_SHUTTER_TOGGLE "|" D_CMND_SHUTTER_TOGGLEDIR "|" D_CMND_SHUTTER_STOP "|" D_CMND_SHUTTER_POSITION "|" - D_CMND_SHUTTER_OPENTIME "|" D_CMND_SHUTTER_CLOSETIME "|" D_CMND_SHUTTER_RELAY "|" D_CMND_SHUTTER_MODE "|" + D_CMND_SHUTTER_OPENTIME "|" D_CMND_SHUTTER_CLOSETIME "|" D_CMND_SHUTTER_RELAY "|" D_CMND_SHUTTER_MODE "|" D_CMND_SHUTTER_PWMRANGE "|" D_CMND_SHUTTER_SETHALFWAY "|" D_CMND_SHUTTER_SETCLOSE "|" D_CMND_SHUTTER_SETOPEN "|" D_CMND_SHUTTER_INVERT "|" D_CMND_SHUTTER_CLIBRATION "|" D_CMND_SHUTTER_MOTORDELAY "|" D_CMND_SHUTTER_FREQUENCY "|" D_CMND_SHUTTER_BUTTON "|" D_CMND_SHUTTER_LOCK "|" D_CMND_SHUTTER_ENABLEENDSTOPTIME "|" D_CMND_SHUTTER_INVERTWEBBUTTONS "|" D_CMND_SHUTTER_STOPOPEN "|" D_CMND_SHUTTER_STOPCLOSE "|" D_CMND_SHUTTER_STOPTOGGLE "|" D_CMND_SHUTTER_STOPTOGGLEDIR "|" D_CMND_SHUTTER_STOPPOSITION; void (* const ShutterCommand[])(void) PROGMEM = { &CmndShutterOpen, &CmndShutterClose, &CmndShutterToggle, &CmndShutterToggleDir, &CmndShutterStop, &CmndShutterPosition, - &CmndShutterOpenTime, &CmndShutterCloseTime, &CmndShutterRelay, &CmndShutterMode, + &CmndShutterOpenTime, &CmndShutterCloseTime, &CmndShutterRelay, &CmndShutterMode, &CmndShutterPwmRange, &CmndShutterSetHalfway, &CmndShutterSetClose, &CmndShutterSetOpen, &CmndShutterInvert, &CmndShutterCalibration , &CmndShutterMotorDelay, &CmndShutterFrequency, &CmndShutterButton, &CmndShutterLock, &CmndShutterEnableEndStopTime, &CmndShutterInvertWebButtons, &CmndShutterStopOpen, &CmndShutterStopClose, &CmndShutterStopToggle, &CmndShutterStopToggleDir, &CmndShutterStopPosition}; @@ -87,8 +87,6 @@ struct SHUTTER { int16_t motordelay; // initial motorstarttime in 0.05sec. Also uses for ramp at steppers and servos int16_t pwm_velocity; // frequency of PWN for stepper motors or PWM duty cycle change for PWM servo uint16_t pwm_value; // dutyload of PWM 0..1023 on ESP8266 - uint16_t pwm_min; // dutyload of PWM 0..1023 on ESP8266 - uint16_t pwm_max; // dutyload of PWM 0..1023 on ESP8266 uint16_t close_velocity_max; // maximum of PWM change during closeing. Defines velocity on opening. Steppers and Servos only int32_t accelerator; // speed of ramp-up, ramp down of shutters with velocity control. Steppers and Servos only } Shutter[MAX_SHUTTERS]; @@ -140,7 +138,7 @@ void ShutterRtc50mS(void) case SHT_PWM_VALUE: ShutterUpdateVelocity(i); Shutter[i].real_position += Shutter[i].direction > 0 ? Shutter[i].pwm_velocity : (Shutter[i].direction < 0 ? -Shutter[i].pwm_velocity : 0); - Shutter[i].pwm_value = SHT_DIV_ROUND((Shutter[i].pwm_max-Shutter[i].pwm_min) * Shutter[i].real_position , Shutter[i].open_max)+Shutter[i].pwm_min; + Shutter[i].pwm_value = SHT_DIV_ROUND((Settings.shutter_pwmrange[1][i]-Settings.shutter_pwmrange[0][i]) * Shutter[i].real_position , Shutter[i].open_max)+Settings.shutter_pwmrange[0][i]; analogWrite(Pin(GPIO_PWM1, i), Shutter[i].pwm_value); break; @@ -285,8 +283,8 @@ void ShutterInit(void) Shutter[i].close_time = Settings.shutter_closetime[i] = (Settings.shutter_closetime[i] > 0) ? Settings.shutter_closetime[i] : 100; //temporary hard coded. - Shutter[i].pwm_min = pwm_min; - Shutter[i].pwm_max = pwm_max; + Settings.shutter_pwmrange[0][i] = pwm_min; + Settings.shutter_pwmrange[1][i] = pwm_max; // Update Calculation 20 because time interval is 0.05 sec ans time is in 0.1sec Shutter[i].open_max = STEPS_PER_SECOND * RESOLUTION * Shutter[i].open_time / 10; @@ -309,6 +307,9 @@ void ShutterInit(void) switch (ShutterGlobal.position_mode) { case SHT_PWM_VALUE: ShutterGlobal.open_velocity_max = RESOLUTION; + // Initiate pwm range with defaults if not already set. + Settings.shutter_pwmrange[0][i] = Settings.shutter_pwmrange[0][i] > 0 ? Settings.shutter_pwmrange[0][i] : pwm_min; + Settings.shutter_pwmrange[1][i] = Settings.shutter_pwmrange[1][i] > 0 ? Settings.shutter_pwmrange[0][i] : pwm_max; break; } Shutter[i].close_velocity_max = ShutterGlobal.open_velocity_max*Shutter[i].open_time / Shutter[i].close_time; @@ -1364,6 +1365,35 @@ void CmndShutterSetOpen(void) } } +void CmndShutterPwmRange(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= shutters_present)) { + if (XdrvMailbox.data_len > 0) { + uint32_t i = 0; + char *str_ptr; + + char data_copy[strlen(XdrvMailbox.data) +1]; + strncpy(data_copy, XdrvMailbox.data, sizeof(data_copy)); // Duplicate data as strtok_r will modify it. + // Loop through the data string, splitting on ' ' seperators. + for (char *str = strtok_r(data_copy, " ", &str_ptr); str && i < 5; str = strtok_r(nullptr, " ", &str_ptr), i++) { + int field = atoi(str); + // The fields in a data string can only range from 1-30000. + // and following value must be higher than previous one + if ((field <= 0) || (field > 1023)) { + break; + } + Settings.shutter_pwmrange[i][XdrvMailbox.index -1] = field; + } + ShutterInit(); + ResponseCmndIdxChar(XdrvMailbox.data); + } else { + char setting_chr[30] = "0"; + snprintf_P(setting_chr, sizeof(setting_chr), PSTR("Shutter %d: min:%d max:%d"), XdrvMailbox.index, Settings.shutter_pwmrange[0][XdrvMailbox.index -1], Settings.shutter_pwmrange[1][XdrvMailbox.index -1]); + ResponseCmndIdxChar(setting_chr); + } + } +} + void CmndShutterCalibration(void) { if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= shutters_present)) { From 61b2d8e53ed23c1e9b2b2fb1133ca808272f6143 Mon Sep 17 00:00:00 2001 From: stefanbode Date: Wed, 9 Sep 2020 15:24:55 +0200 Subject: [PATCH 39/55] Update i18n.h --- tasmota/i18n.h | 1 + 1 file changed, 1 insertion(+) diff --git a/tasmota/i18n.h b/tasmota/i18n.h index 25b1cb8b7..24b26b032 100644 --- a/tasmota/i18n.h +++ b/tasmota/i18n.h @@ -607,6 +607,7 @@ #define D_CMND_SHUTTER_LOCK "Lock" #define D_CMND_SHUTTER_ENABLEENDSTOPTIME "EnableEndStopTime" #define D_CMND_SHUTTER_INVERTWEBBUTTONS "InvertWebButtons" +#define D_CMND_SHUTTER_PWMRANGE "PWMRange" // Commands xdrv_32_hotplug.ino #define D_CMND_HOTPLUG "HotPlug" From fb59126f9897cfdb27dac7d3b5caa9cb98474703 Mon Sep 17 00:00:00 2001 From: stefanbode Date: Wed, 9 Sep 2020 15:27:43 +0200 Subject: [PATCH 40/55] Update settings.h --- tasmota/settings.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tasmota/settings.h b/tasmota/settings.h index 048b61754..2ceadd523 100644 --- a/tasmota/settings.h +++ b/tasmota/settings.h @@ -611,8 +611,9 @@ struct { uint8_t fallback_module; // F42 uint8_t shutter_mode; // F43 uint16_t energy_power_delta[3]; // F44 + uint16_t shutter_pwmrange[2][MAX_SHUTTERS]; // F4A - uint8_t free_f4a[106]; // F4A - Decrement if adding new Setting variables just above and below + uint8_t free_f5a[90]; // F5A - Decrement if adding new Setting variables just above and below // Only 32 bit boundary variables below SysBitfield5 flag5; // FB4 From 9b8138f2a055624ee6f3a42d8f8e4cd8da734083 Mon Sep 17 00:00:00 2001 From: stefanbode Date: Wed, 9 Sep 2020 16:23:50 +0200 Subject: [PATCH 41/55] PWMrange introduced --- tasmota/xdrv_27_shutter.ino | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/tasmota/xdrv_27_shutter.ino b/tasmota/xdrv_27_shutter.ino index 15e765ff4..b7b609265 100644 --- a/tasmota/xdrv_27_shutter.ino +++ b/tasmota/xdrv_27_shutter.ino @@ -282,10 +282,6 @@ void ShutterInit(void) Shutter[i].open_time = Settings.shutter_opentime[i] = (Settings.shutter_opentime[i] > 0) ? Settings.shutter_opentime[i] : 100; Shutter[i].close_time = Settings.shutter_closetime[i] = (Settings.shutter_closetime[i] > 0) ? Settings.shutter_closetime[i] : 100; - //temporary hard coded. - Settings.shutter_pwmrange[0][i] = pwm_min; - Settings.shutter_pwmrange[1][i] = pwm_max; - // Update Calculation 20 because time interval is 0.05 sec ans time is in 0.1sec Shutter[i].open_max = STEPS_PER_SECOND * RESOLUTION * Shutter[i].open_time / 10; Shutter[i].close_velocity = Shutter[i].open_max / Shutter[i].close_time / 2 ; @@ -1369,14 +1365,14 @@ void CmndShutterPwmRange(void) { if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= shutters_present)) { if (XdrvMailbox.data_len > 0) { - uint32_t i = 0; + uint8_t i = 0; char *str_ptr; char data_copy[strlen(XdrvMailbox.data) +1]; strncpy(data_copy, XdrvMailbox.data, sizeof(data_copy)); // Duplicate data as strtok_r will modify it. // Loop through the data string, splitting on ' ' seperators. - for (char *str = strtok_r(data_copy, " ", &str_ptr); str && i < 5; str = strtok_r(nullptr, " ", &str_ptr), i++) { - int field = atoi(str); + for (char *str = strtok_r(data_copy, " ", &str_ptr); str && i < 2; str = strtok_r(nullptr, " ", &str_ptr), i++) { + uint16_t field = atoi(str); // The fields in a data string can only range from 1-30000. // and following value must be higher than previous one if ((field <= 0) || (field > 1023)) { @@ -1384,6 +1380,7 @@ void CmndShutterPwmRange(void) } Settings.shutter_pwmrange[i][XdrvMailbox.index -1] = field; } + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT%d: Init1. pwmmin %d, pwmmax %d"), XdrvMailbox.index , Settings.shutter_pwmrange[0][XdrvMailbox.index -1], Settings.shutter_pwmrange[1][XdrvMailbox.index -1]); ShutterInit(); ResponseCmndIdxChar(XdrvMailbox.data); } else { @@ -1398,7 +1395,7 @@ void CmndShutterCalibration(void) { if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= shutters_present)) { if (XdrvMailbox.data_len > 0) { - uint32_t i = 0; + uint8_t i = 0; char *str_ptr; char data_copy[strlen(XdrvMailbox.data) +1]; From 15ef20b6859fffb3baf326c144750dfa6c1eab39 Mon Sep 17 00:00:00 2001 From: stefanbode Date: Wed, 9 Sep 2020 16:34:03 +0200 Subject: [PATCH 42/55] small bug --- tasmota/xdrv_27_shutter.ino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tasmota/xdrv_27_shutter.ino b/tasmota/xdrv_27_shutter.ino index b7b609265..19e89f113 100644 --- a/tasmota/xdrv_27_shutter.ino +++ b/tasmota/xdrv_27_shutter.ino @@ -305,7 +305,7 @@ void ShutterInit(void) ShutterGlobal.open_velocity_max = RESOLUTION; // Initiate pwm range with defaults if not already set. Settings.shutter_pwmrange[0][i] = Settings.shutter_pwmrange[0][i] > 0 ? Settings.shutter_pwmrange[0][i] : pwm_min; - Settings.shutter_pwmrange[1][i] = Settings.shutter_pwmrange[1][i] > 0 ? Settings.shutter_pwmrange[0][i] : pwm_max; + Settings.shutter_pwmrange[1][i] = Settings.shutter_pwmrange[1][i] > 0 ? Settings.shutter_pwmrange[1][i] : pwm_max; break; } Shutter[i].close_velocity_max = ShutterGlobal.open_velocity_max*Shutter[i].open_time / Shutter[i].close_time; From fd3f77fb171c5d0acce493870634e416e98ee860 Mon Sep 17 00:00:00 2001 From: Jason2866 <24528715+Jason2866@users.noreply.github.com> Date: Thu, 10 Sep 2020 15:06:06 +0200 Subject: [PATCH 43/55] Fix compile error on MAC See https://github.com/platformio/platformio-core/issues/3659 Thx @ivankravets --- platformio.ini | 1 - 1 file changed, 1 deletion(-) diff --git a/platformio.ini b/platformio.ini index ecf7d41ab..f70cfa812 100644 --- a/platformio.ini +++ b/platformio.ini @@ -11,7 +11,6 @@ description = Provide ESP8266 based devices with Web, MQTT and OTA firmware src_dir = tasmota build_dir = .pioenvs -workspace_dir = .pioenvs build_cache_dir = .cache extra_configs = platformio_tasmota32.ini platformio_tasmota_env.ini From ed83e770a4a2dd9452dc739c5fb02959fea3263a Mon Sep 17 00:00:00 2001 From: Jason2866 <24528715+Jason2866@users.noreply.github.com> Date: Thu, 10 Sep 2020 15:10:34 +0200 Subject: [PATCH 44/55] is not needed anymore too since we do not support core 2.3.0 anymore --- platformio.ini | 1 - 1 file changed, 1 deletion(-) diff --git a/platformio.ini b/platformio.ini index f70cfa812..b07184478 100644 --- a/platformio.ini +++ b/platformio.ini @@ -10,7 +10,6 @@ [platformio] description = Provide ESP8266 based devices with Web, MQTT and OTA firmware src_dir = tasmota -build_dir = .pioenvs build_cache_dir = .cache extra_configs = platformio_tasmota32.ini platformio_tasmota_env.ini From da4caa6ec166b51586ad6d7362cb696c8d83f5c3 Mon Sep 17 00:00:00 2001 From: Jason2866 <24528715+Jason2866@users.noreply.github.com> Date: Thu, 10 Sep 2020 16:41:31 +0200 Subject: [PATCH 45/55] path changed --- pio/name-firmware.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pio/name-firmware.py b/pio/name-firmware.py index 1490ecc5c..1c79056de 100644 --- a/pio/name-firmware.py +++ b/pio/name-firmware.py @@ -5,7 +5,7 @@ import shutil OUTPUT_DIR = "build_output{}".format(os.path.sep) def bin_map_copy(source, target, env): - variant = str(target[0]).split(os.path.sep)[1] + variant = str(target[0]).split(os.path.sep)[2] # check if output directories exist and create if necessary if not os.path.isdir(OUTPUT_DIR): From 3d256b83c2214b52252e4c8dc01effde96dd92ac Mon Sep 17 00:00:00 2001 From: Jason2866 <24528715+Jason2866@users.noreply.github.com> Date: Thu, 10 Sep 2020 16:42:37 +0200 Subject: [PATCH 46/55] path changed --- pio/gzip-firmware.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pio/gzip-firmware.py b/pio/gzip-firmware.py index 43af1f933..248bb5a02 100644 --- a/pio/gzip-firmware.py +++ b/pio/gzip-firmware.py @@ -6,7 +6,7 @@ import gzip OUTPUT_DIR = "build_output{}".format(os.path.sep) def bin_gzip(source, target, env): - variant = str(target[0]).split(os.path.sep)[1] + variant = str(target[0]).split(os.path.sep)[2] # create string with location and file names based on variant bin_file = "{}firmware{}{}.bin".format(OUTPUT_DIR, os.path.sep, variant) From b239a1d77e10f64f9ffa5f0267bd12c19794ef04 Mon Sep 17 00:00:00 2001 From: Jason2866 <24528715+Jason2866@users.noreply.github.com> Date: Fri, 11 Sep 2020 10:11:18 +0200 Subject: [PATCH 47/55] Update platformio.ini --- platformio.ini | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/platformio.ini b/platformio.ini index b07184478..a0c4d82b5 100644 --- a/platformio.ini +++ b/platformio.ini @@ -7,16 +7,9 @@ ; Please visit documentation for the other options and examples ; http://docs.platformio.org/en/stable/projectconf.html -[platformio] -description = Provide ESP8266 based devices with Web, MQTT and OTA firmware -src_dir = tasmota -build_cache_dir = .cache -extra_configs = platformio_tasmota32.ini - platformio_tasmota_env.ini - platformio_tasmota_env32.ini - platformio_override.ini -; *** Build/upload environment +; *** Tasmota build variant selection +[build_envs] default_envs = ; *** Uncomment by deleting ";" in the line(s) below to select version(s) ; tasmota @@ -51,10 +44,21 @@ default_envs = ; tasmota-TW ; tasmota-UK ; +; *** Selection for Tasmota ESP32 is done in platformio_tasmota32.ini +; ; *** alternatively can be done in: platformio_override.ini ; *** See example: platformio_override_sample.ini ; ********************************************************************* +[platformio] +description = Provide ESP8266 / ESP32 based devices with Web, MQTT and OTA firmware +src_dir = tasmota +build_cache_dir = .cache +extra_configs = platformio_tasmota32.ini + platformio_tasmota_env.ini + platformio_tasmota_env32.ini + platformio_override.ini +default_envs = ${build_envs.default_envs} [common] framework = arduino From ef3855e92f24cc3099bca4127f104f784a3b993d Mon Sep 17 00:00:00 2001 From: Jason2866 <24528715+Jason2866@users.noreply.github.com> Date: Fri, 11 Sep 2020 10:13:12 +0200 Subject: [PATCH 48/55] Make selecting ESP32 versions possible without using override file --- platformio_tasmota32.ini | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/platformio_tasmota32.ini b/platformio_tasmota32.ini index 549f2e333..739ec2338 100644 --- a/platformio_tasmota32.ini +++ b/platformio_tasmota32.ini @@ -1,6 +1,44 @@ ; *** BETA ESP32 Tasmota version *** ; *** expect the unexpected. Some features not working!!! *** +[platformio] + +; *** Tasmota build variant selection +default_envs = ${build_envs.default_envs} +; *** Uncomment by deleting ";" in the line(s) below to select version(s) +; tasmota32 +; tasmota32-webcam +; tasmota32-minimal +; tasmota32-lite +; tasmota32-knx +; tasmota32-sensors +; tasmota32-display +; tasmota32-ir +; tasmota32-ircustom +; tasmota32-BG +; tasmota32-BR +; tasmota32-CN +; tasmota32-CZ +; tasmota32-DE +; tasmota32-ES +; tasmota32-FR +; tasmota32-GR +; tasmota32-HE +; tasmota32-HU +; tasmota32-IT +; tasmota32-KO +; tasmota32-NL +; tasmota32-PL +; tasmota32-PT +; tasmota32-RO +; tasmota32-RU +; tasmota32-SE +; tasmota32-SK +; tasmota32-TR +; tasmota32-TW +; tasmota32-UK + + [common32] platform = espressif32@2.0.0 platform_packages = tool-esptoolpy@1.20800.0 From 9afa8a5d4f564d9b5295809114d12c35f9b5d6e5 Mon Sep 17 00:00:00 2001 From: gemu2015 Date: Fri, 11 Sep 2020 15:44:16 +0200 Subject: [PATCH 49/55] scripter update reformatting, bug fixes, new options for google charts formulas in text substitutions %(formula)% --- tasmota/xdrv_10_scripter.ino | 4120 +++++++++++++++++----------------- 1 file changed, 2068 insertions(+), 2052 deletions(-) diff --git a/tasmota/xdrv_10_scripter.ino b/tasmota/xdrv_10_scripter.ino index cd5f986d2..ea8223233 100755 --- a/tasmota/xdrv_10_scripter.ino +++ b/tasmota/xdrv_10_scripter.ino @@ -136,19 +136,19 @@ Ticker Script_ticker4; void Script_ticker1_end(void) { Script_ticker1.detach(); - Run_Scripter(">ti1", 4,0); + Run_Scripter(">ti1", 4, 0); } void Script_ticker2_end(void) { Script_ticker2.detach(); - Run_Scripter(">ti2", 4,0); + Run_Scripter(">ti2", 4, 0); } void Script_ticker3_end(void) { Script_ticker3.detach(); - Run_Scripter(">ti3", 4,0); + Run_Scripter(">ti3", 4, 0); } void Script_ticker4_end(void) { Script_ticker4.detach(); - Run_Scripter(">ti4", 4,0); + Run_Scripter(">ti4", 4, 0); } #endif @@ -169,7 +169,7 @@ FS *fsp; #ifdef LITTLEFS_SCRIPT_SIZE -void SaveFile(const char *name,const uint8_t *buf,uint32_t len) { +void SaveFile(const char *name, const uint8_t *buf, uint32_t len) { File file = fsp->open(name, "w"); if (!file) return; file.write(buf, len); @@ -179,7 +179,7 @@ void SaveFile(const char *name,const uint8_t *buf,uint32_t len) { uint8_t fs_mounted=0; -void LoadFile(const char *name,uint8_t *buf,uint32_t len) { +void LoadFile(const char *name, uint8_t *buf, uint32_t len) { if (!fs_mounted) { #ifdef ESP32 if (!SPIFFS.begin(FORMAT_SPIFFS_IF_FAILED)) { @@ -214,7 +214,7 @@ FS *fsp; #else SDClass *fsp; #endif -#endif +#endif //USE_SCRIPT_FATFS #ifndef ESP32 // esp8266 @@ -404,7 +404,7 @@ struct SCRIPT_MEM { FILE_FLAGS file_flags[SFS_MAX]; uint8_t script_sd_found; char flink[2][14]; -#endif +#endif //USE_SCRIPT_FATFS #ifdef USE_SCRIPT_GLOBVARS UDP_FLAGS udp_flags; #endif @@ -423,8 +423,8 @@ char * IPAddressToString(const IPAddress& ip_address) { sprintf_P(ipbuffer, PSTR("%u.%u.%u.%u"), ip_address[0], ip_address[1], ip_address[2], ip_address[3]); return ipbuffer; } -#endif -#endif +#endif //USE_DEVICE_GROUPS +#endif //USE_SCRIPT_GLOBVARS int16_t last_findex; int16_t last_sindex; @@ -436,10 +436,10 @@ uint32_t script_lastmillis; #ifdef USE_BUTTON_EVENT int8_t script_button[MAX_KEYS]; -#endif +#endif //USE_BUTTON_EVENT -char *GetNumericResult(char *lp,uint8_t lastop,float *fp,JsonObject *jo); -char *GetStringResult(char *lp,uint8_t lastop,char *cp,JsonObject *jo); +char *GetNumericArgument(char *lp,uint8_t lastop,float *fp,JsonObject *jo); +char *GetStringArgument(char *lp,uint8_t lastop,char *cp,JsonObject *jo); char *ForceStringVar(char *lp,char *dstr); void send_download(void); uint8_t reject(char *name); @@ -447,75 +447,80 @@ uint8_t reject(char *name); void ScriptEverySecond(void) { if (bitRead(Settings.rule_enabled, 0)) { - struct T_INDEX *vtp=glob_script_mem.type; - float delta=(millis()-script_lastmillis)/1000.0; - script_lastmillis=millis(); + struct T_INDEX *vtp = glob_script_mem.type; + float delta = (millis() - script_lastmillis) / 1000.0; + script_lastmillis = millis(); for (uint8_t count=0; count0) { // decrement - *fp-=delta; - if (*fp<0) *fp=0; + *fp -= delta; + if (*fp<0) *fp = 0; } } if (vtp[count].bits.is_autoinc) { // increments timers - float *fp=&glob_script_mem.fvars[vtp[count].index]; + float *fp = &glob_script_mem.fvars[vtp[count].index]; if (*fp>=0) { - *fp+=delta; + *fp += delta; } } } - Run_Scripter(">S",2,0); + Run_Scripter(">S", 2, 0); } } void RulesTeleperiod(void) { - if (bitRead(Settings.rule_enabled, 0) && mqtt_data[0]) Run_Scripter(">T",2, mqtt_data); + if (bitRead(Settings.rule_enabled, 0) && mqtt_data[0]) Run_Scripter(">T", 2, mqtt_data); } // EEPROM MACROS // i2c eeprom -#define EEP_WRITE(A,B,C) eeprom_writeBytes(A,B,(uint8_t*)C); -#define EEP_READ(A,B,C) eeprom_readBytes(A,B,(uint8_t*)C); +#define EEP_WRITE(A,B,C) eeprom_writeBytes(A, B, (uint8_t*)C); +#define EEP_READ(A,B,C) eeprom_readBytes(A, B, (uint8_t*)C); #define SCRIPT_SKIP_SPACES while (*lp==' ' || *lp=='\t') lp++; #define SCRIPT_SKIP_EOL while (*lp==SCRIPT_EOL) lp++; -float *Get_MFAddr(uint8_t index,uint16_t *len,uint16_t *ipos); +float *Get_MFAddr(uint8_t index, uint16_t *len, uint16_t *ipos); // allocates all variables and presets them int16_t Init_Scripter(void) { char *script; - script=glob_script_mem.script_ram; + script = glob_script_mem.script_ram; // scan lines for >DEF - uint16_t lines=0,nvars=0,svars=0,vars=0; - char *lp=script; + uint16_t lines = 0; + uint16_t nvars = 0; + uint16_t svars = 0; + uint16_t vars = 0; + char *lp = script; char vnames[MAXVARS*10]; - char *vnames_p=vnames; + char *vnames_p = vnames; char *vnp[MAXVARS]; - char **vnp_p=vnp; + char **vnp_p = vnp; char strings[MAXSVARS*SCRIPT_MAXSSIZE]; struct M_FILT mfilt[MAXFILT]; - char *strings_p=strings; + char *strings_p = strings; char *snp[MAXSVARS]; - char **snp_p=snp; - uint8_t numperm=0,numflt=0,count; + char **snp_p = snp; + uint8_t numperm = 0; + uint8_t numflt = 0; + uint8_t count; - glob_script_mem.max_ssize=SCRIPT_SVARSIZE; - glob_script_mem.scriptptr=0; + glob_script_mem.max_ssize = SCRIPT_SVARSIZE; + glob_script_mem.scriptptr = 0; if (!*script) return -999; float fvalues[MAXVARS]; struct T_INDEX vtypes[MAXVARS]; - char init=0; + char init = 0; while (1) { // check line // skip leading spaces @@ -527,84 +532,84 @@ char *script; if (init) { // init section if (*lp=='>' || !*lp) { - init=0; + init = 0; break; } - char *op=strchr(lp,'='); + char *op = strchr(lp, '='); if (op) { - vtypes[vars].bits.data=0; + vtypes[vars].bits.data = 0; // found variable definition if (*lp=='p' && *(lp+1)==':') { - lp+=2; + lp += 2; if (numpermMAXFILT) { return -6; } } else { - vtypes[vars].bits.is_filter=0; + vtypes[vars].bits.is_filter = 0; } - *vnp_p++=vnames_p; + *vnp_p ++= vnames_p; while (lpMAXNVARS) { return -1; @@ -616,27 +621,27 @@ char *script; while (*op==' ') op++; if (isdigit(*op)) { // lenght define follows - uint16_t flen=atoi(op); + uint16_t flen = atoi(op); if (flen>MAX_ARRAY_SIZE) { // limit array size - flen=MAX_ARRAY_SIZE; + flen = MAX_ARRAY_SIZE; } - mfilt[numflt-1].numvals&=OR_FILT_MASK; - mfilt[numflt-1].numvals|=flen&AND_FILT_MASK; + mfilt[numflt-1].numvals &= OR_FILT_MASK; + mfilt[numflt-1].numvals |= flen&AND_FILT_MASK; } } } else { // string vars op++; - *snp_p++=strings_p; + *snp_p ++= strings_p; while (*op!='\"') { if (*op==SCRIPT_EOL) break; - *strings_p++=*op++; + *strings_p++ = *op++; } - *strings_p++=0; - vtypes[vars].bits.is_string=1; - vtypes[vars].index=svars; + *strings_p++ = 0; + vtypes[vars].bits.is_string = 1; + vtypes[vars].index = svars; svars++; if (svars>MAXSVARS) { return -2; @@ -648,15 +653,15 @@ char *script; } } } else { - if (!strncmp(lp,">D",2)) { - lp+=2; + if (!strncmp(lp, ">D", 2)) { + lp += 2; SCRIPT_SKIP_SPACES if (isdigit(*lp)) { - uint8_t ssize=atoi(lp)+1; + uint8_t ssize = atoi(lp)+1; if (ssize<10 || ssize>SCRIPT_MAXSSIZE) ssize=SCRIPT_MAXSSIZE; - glob_script_mem.max_ssize=ssize; + glob_script_mem.max_ssize = ssize; } - init=1; + init = 1; } } // next line @@ -666,78 +671,78 @@ char *script; lp++; } - uint16_t fsize=0; + uint16_t fsize = 0; for (count=0; countnumvals=mfilt[count].numvals; - mp+=sizeof(struct M_FILT)+((mfilt[count].numvals&AND_FILT_MASK)-1)*sizeof(float); + uint8_t *mp = (uint8_t*)glob_script_mem.mfilt; + for (count = 0; countnumvals = mfilt[count].numvals; + mp += sizeof(struct M_FILT) + ((mfilt[count].numvals & AND_FILT_MASK) - 1) * sizeof(float); } - glob_script_mem.numvars=vars; - glob_script_mem.script_dprec=SCRIPT_FLOAT_PRECISION; - glob_script_mem.script_loglevel=LOG_LEVEL_INFO; + glob_script_mem.numvars = vars; + glob_script_mem.script_dprec = SCRIPT_FLOAT_PRECISION; + glob_script_mem.script_loglevel = LOG_LEVEL_INFO; #if SCRIPT_DEBUG>2 - struct T_INDEX *dvtp=glob_script_mem.type; - for (uint8_t count=0; count=0 // user sd card - fsp=&SD; + fsp = &SD; if (SD.begin(USE_SCRIPT_FATFS)) { #else // use flash file @@ -836,35 +841,35 @@ char *script; if (FFat.begin(true)) { #else if (fsp->begin()) { -#endif +#endif // ESP32 #endif // USE_SCRIPT_FATFS>=0 - glob_script_mem.script_sd_found=1; + glob_script_mem.script_sd_found = 1; } else { - glob_script_mem.script_sd_found=0; + glob_script_mem.script_sd_found = 0; } } - for (uint8_t cnt=0;cnt0 ClaimSerial(); SetSerialBaudrate(9600); -#endif +#endif //SCRIPT_DEBUG // store start of actual program here - glob_script_mem.scriptptr=lp-1; - glob_script_mem.scriptptr_bu=glob_script_mem.scriptptr; + glob_script_mem.scriptptr = lp - 1; + glob_script_mem.scriptptr_bu = glob_script_mem.scriptptr; #ifdef USE_SCRIPT_GLOBVARS if (glob_script_mem.udp_flags.udp_used) { Script_Init_UDP(); - glob_script=Run_Scripter(">G",-2,0); + glob_script = Run_Scripter(">G", -2, 0); } -#endif +#endif //USE_SCRIPT_GLOBVARS return 0; @@ -899,42 +904,42 @@ void Script_PollUdp(void) { if (glob_script_mem.udp_flags.udp_connected ) { while (Script_PortUdp.parsePacket()) { char packet_buffer[SCRIPT_UDP_BUFFER_SIZE]; - int32_t len = Script_PortUdp.read(packet_buffer, SCRIPT_UDP_BUFFER_SIZE -1); + int32_t len = Script_PortUdp.read(packet_buffer, SCRIPT_UDP_BUFFER_SIZE - 1); packet_buffer[len] = 0; script_udp_remote_ip = Script_PortUdp.remoteIP(); AddLog_P2(LOG_LEVEL_DEBUG, PSTR("UDP: Packet %s - %d - %s"), packet_buffer, len, script_udp_remote_ip.toString().c_str()); char *lp=packet_buffer; - if (!strncmp(lp,"=>",2)) { - lp+=2; - char *cp=strchr(lp,'='); + if (!strncmp(lp,"=>", 2)) { + lp += 2; + char *cp=strchr(lp, '='); if (cp) { char vnam[32]; - for (uint32_t count=0; countG",2,0); + Run_Scripter(">G", 2, 0); } } } @@ -950,167 +955,169 @@ void script_udp_sendvar(char *vname,float *fp,char *sp) { if (!glob_script_mem.udp_flags.udp_used) return; if (!glob_script_mem.udp_flags.udp_connected) return; - char sbuf[SCRIPT_MAXSSIZE+4]; - strcpy(sbuf,"=>"); - strcat(sbuf,vname); - strcat(sbuf,"="); + char sbuf[SCRIPT_MAXSSIZE + 4]; + strcpy(sbuf, "=>"); + strcat(sbuf, vname); + strcat(sbuf, "="); if (fp) { char flstr[16]; - dtostrfd(*fp,8,flstr); - strcat(sbuf,flstr); - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("num var updated - %s"),sbuf); + dtostrfd(*fp, 8, flstr); + strcat(sbuf, flstr); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("num var updated - %s"), sbuf); } else { - strcat(sbuf,sp); - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("string var updated - %s"),sbuf); + strcat(sbuf, sp); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("string var updated - %s"), sbuf); } - Script_PortUdp.beginPacket(IPAddress(239,255,255,250), SCRIPT_UDP_PORT); + Script_PortUdp.beginPacket(IPAddress(239, 255, 255, 250), SCRIPT_UDP_PORT); // Udp.print(String("RET UC: ") + String(recv_Packet)); - Script_PortUdp.write((const uint8_t*)sbuf,strlen(sbuf)); + Script_PortUdp.write((const uint8_t*)sbuf, strlen(sbuf)); Script_PortUdp.endPacket(); } -#endif +#endif //USE_SCRIPT_GLOBVARS #ifdef USE_LIGHT #ifdef USE_WS2812 void ws2812_set_array(float *array ,uint32_t len, uint32_t offset) { Ws2812ForceSuspend(); - for (uint32_t cnt=0;cntSettings.light_pixels) break; - uint32_t col=array[cnt]; - Ws2812SetColor(index+1,col>>16,col>>8,col,0); + uint32_t col = array[cnt]; + Ws2812SetColor(index + 1, col>>16, col>>8, col, 0); } Ws2812ForceUpdate(); } -#endif -#endif +#endif //USE_WS2812 +#endif //USE_LIGHT -float median_array(float *array,uint16_t len) { +float median_array(float *array, uint16_t len) { uint8_t ind[len]; - uint8_t mind=0,index=0,flg; - float min=FLT_MAX; + uint8_t mind = 0; + uint8_t index = 0; + uint8_t flg; + float min = FLT_MAX; - for (uint8_t hcnt=0; hcntnumvals&AND_FILT_MASK; - if (ipos) *ipos=mflp->index; + *len = mflp->numvals & AND_FILT_MASK; + if (ipos) *ipos = mflp->index; return mflp->rbuff; } - mp+=sizeof(struct M_FILT)+((mflp->numvals&AND_FILT_MASK)-1)*sizeof(float); + mp += sizeof(struct M_FILT) + ((mflp->numvals & AND_FILT_MASK) - 1) * sizeof(float); } return 0; } -float Get_MFVal(uint8_t index,int16_t bind) { - uint8_t *mp=(uint8_t*)glob_script_mem.mfilt; - for (uint8_t count=0; countnumvals&AND_FILT_MASK; + uint16_t maxind = mflp->numvals & AND_FILT_MASK; if (!bind) { return mflp->index; } if (bind<0) { return maxind; } - if (bind<1 || bind>maxind) bind=maxind; - return mflp->rbuff[bind-1]; + if (bind<1 || bind>maxind) bind = maxind; + return mflp->rbuff[bind - 1]; } - mp+=sizeof(struct M_FILT)+((mflp->numvals&AND_FILT_MASK)-1)*sizeof(float); + mp += sizeof(struct M_FILT) + ((mflp->numvals & AND_FILT_MASK) - 1) * sizeof(float); } return 0; } -void Set_MFVal(uint8_t index,uint16_t bind,float val) { - uint8_t *mp=(uint8_t*)glob_script_mem.mfilt; - for (uint8_t count=0; countnumvals&AND_FILT_MASK; + uint16_t maxind = mflp->numvals & AND_FILT_MASK; if (!bind) { - mflp->index=val; + mflp->index = val; } else { - if (bind<1 || bind>maxind) bind=maxind; - mflp->rbuff[bind-1]=val; + if (bind<1 || bind>maxind) bind = maxind; + mflp->rbuff[bind-1] = val; } return; } - mp+=sizeof(struct M_FILT)+((mflp->numvals&AND_FILT_MASK)-1)*sizeof(float); + mp += sizeof(struct M_FILT) + ((mflp->numvals & AND_FILT_MASK) - 1) * sizeof(float); } } float Get_MFilter(uint8_t index) { - uint8_t *mp=(uint8_t*)glob_script_mem.mfilt; - for (uint8_t count=0; countnumvals&OR_FILT_MASK) { + if (mflp->numvals & OR_FILT_MASK) { // moving average - return mflp->maccu/(mflp->numvals&AND_FILT_MASK); + return mflp->maccu / (mflp->numvals & AND_FILT_MASK); } else { // median, sort array indices - return median_array(mflp->rbuff,mflp->numvals); + return median_array(mflp->rbuff, mflp->numvals); } } - mp+=sizeof(struct M_FILT)+((mflp->numvals&AND_FILT_MASK)-1)*sizeof(float); + mp += sizeof(struct M_FILT) + ((mflp->numvals & AND_FILT_MASK) - 1) * sizeof(float); } return 0; } void Set_MFilter(uint8_t index, float invar) { - uint8_t *mp=(uint8_t*)glob_script_mem.mfilt; - for (uint8_t count=0; countnumvals&OR_FILT_MASK) { + if (mflp->numvals & OR_FILT_MASK) { // moving average - mflp->maccu-=mflp->rbuff[mflp->index]; - mflp->maccu+=invar; - mflp->rbuff[mflp->index]=invar; + mflp->maccu -= mflp->rbuff[mflp->index]; + mflp->maccu += invar; + mflp->rbuff[mflp->index] = invar; mflp->index++; - if (mflp->index>=(mflp->numvals&AND_FILT_MASK)) mflp->index=0; + if (mflp->index>=(mflp->numvals&AND_FILT_MASK)) mflp->index = 0; } else { // median - mflp->rbuff[mflp->index]=invar; + mflp->rbuff[mflp->index] = invar; mflp->index++; - if (mflp->index>=mflp->numvals) mflp->index=0; + if (mflp->index>=mflp->numvals) mflp->index = 0; } break; } - mp+=sizeof(struct M_FILT)+((mflp->numvals&AND_FILT_MASK)-1)*sizeof(float); + mp += sizeof(struct M_FILT) + ((mflp->numvals & AND_FILT_MASK) - 1) * sizeof(float); } } @@ -1124,17 +1131,16 @@ int8_t index; float DoMedian5(uint8_t index, float in) { - if (index>=MEDIAN_FILTER_NUM) index=0; + if (index>=MEDIAN_FILTER_NUM) index = 0; - struct MEDIAN_FILTER* mf=&script_mf[index]; - mf->buffer[mf->index]=in; + struct MEDIAN_FILTER* mf = &script_mf[index]; + mf->buffer[mf->index] = in; mf->index++; - if (mf->index>=MEDIAN_SIZE) mf->index=0; - return median_array(mf->buffer,MEDIAN_SIZE); + if (mf->index>=MEDIAN_SIZE) mf->index = 0; + return median_array(mf->buffer, MEDIAN_SIZE); } #ifdef USE_LIGHT -//#ifdef USE_WS2812 uint32_t HSVToRGB(uint16_t hue, uint8_t saturation, uint8_t value) { float r = 0, g = 0, b = 0; struct HSV { @@ -1143,9 +1149,9 @@ struct HSV { float V; } hsv; -hsv.H=hue; -hsv.S=(float)saturation/100.0; -hsv.V=(float)value/100.0; +hsv.H = hue; +hsv.S = (float)saturation / 100.0; +hsv.V = (float)value / 100.0; if (hsv.S == 0) { r = hsv.V; @@ -1208,16 +1214,16 @@ if (hsv.S == 0) { } - uint8_t ir,ig,ib; - ir=r*255; - ig=g*255; - ib=b*255; + uint8_t ir, ig, ib; + ir = r * 255; + ig = g * 255; + ib = b * 255; - uint32_t rgb=(ir<<16)|(ig<<8)|ib; + uint32_t rgb = (ir<<16) | (ig<<8) | ib; return rgb; } -#endif -//#endif +#endif //USE_LIGHT + #ifdef USE_ANGLE_FUNC uint32_t pulse_time_hl; @@ -1249,8 +1255,8 @@ uint32_t MeasurePulseTime(int32_t in) { if (in >= 0) { // define pin; pt_pin = in; - pinMode(pt_pin&0x3f,INPUT_PULLUP); - attachInterrupt(pt_pin&0x3f, MP_Timer, CHANGE); + pinMode(pt_pin & 0x3f, INPUT_PULLUP); + attachInterrupt(pt_pin & 0x3f, MP_Timer, CHANGE); pulse_ltime_lh = millis(); pulse_ltime_hl = millis(); return 0; @@ -1269,26 +1275,26 @@ uint32_t MeasurePulseTime(int32_t in) { #ifdef USE_SCRIPT_GLOBVARS uint32_t match_vars(char *dvnam, float **fp, char **sp, uint32_t *ind) { - uint16_t olen=strlen(dvnam); - struct T_INDEX *vtp=glob_script_mem.type; - for (uint32_t count=0; count ff=nothing found, fe=constant number,fd = constant string else bit 7 => 80 = string, 0 = number // no flash strings here for performance reasons!!! -char *isvar(char *lp, uint8_t *vtype,struct T_INDEX *tind,float *fp,char *sp,JsonObject *jo) { - uint16_t count,len=0; - uint8_t nres=0; +char *isvar(char *lp, uint8_t *vtype, struct T_INDEX *tind, float *fp, char *sp, JsonObject *jo) { + uint16_t count,len = 0; + uint8_t nres = 0; char vname[32]; - float fvar=0; - tind->index=0; - tind->bits.data=0; + float fvar = 0; + tind->index = 0; + tind->bits.data = 0; if (isdigit(*lp) || (*lp=='-' && isdigit(*(lp+1))) || *lp=='.') { // isnumber if (fp) { if (*lp=='0' && *(lp+1)=='x') { - lp+=2; - *fp=strtol(lp,0,16); + lp += 2; + *fp = strtol(lp, 0, 16); } else { - *fp=CharToFloat(lp); + *fp = CharToFloat(lp); } } if (*lp=='-') lp++; @@ -1324,109 +1330,109 @@ char *isvar(char *lp, uint8_t *vtype,struct T_INDEX *tind,float *fp,char *sp,Jso if (*lp==0 || *lp==SCRIPT_EOL) break; lp++; } - tind->bits.constant=1; - tind->bits.is_string=0; - *vtype=NUM_RES; + tind->bits.constant = 1; + tind->bits.is_string = 0; + *vtype = NUM_RES; return lp; } if (*lp=='"') { lp++; while (*lp!='"') { if (*lp==0 || *lp==SCRIPT_EOL) break; - uint8_t iob=*lp; + uint8_t iob = *lp; if (iob=='\\') { lp++; if (*lp=='t') { - iob='\t'; + iob = '\t'; } else if (*lp=='n') { - iob='\n'; + iob = '\n'; } else if (*lp=='r') { - iob='\r'; + iob = '\r'; } else if (*lp=='\\') { - iob='\\'; + iob = '\\'; } else { lp--; } - if (sp) *sp++=iob; + if (sp) *sp++ = iob; } else { - if (sp) *sp++=iob; + if (sp) *sp++ = iob; } lp++; } - if (sp) *sp=0; - *vtype=STR_RES; - tind->bits.constant=1; - tind->bits.is_string=1; - return lp+1; + if (sp) *sp = 0; + *vtype = STR_RES; + tind->bits.constant = 1; + tind->bits.is_string = 1; + return lp + 1; } if (*lp=='-') { // inverted var - nres=1; + nres = 1; lp++; } - const char *term="\n\r ])=+-/*%>index=VAR_NV; - glob_script_mem.var_not_found=1; + *vtype = VAR_NV; + tind->index = VAR_NV; + glob_script_mem.var_not_found = 1; return lp; } - struct T_INDEX *vtp=glob_script_mem.type; + struct T_INDEX *vtp = glob_script_mem.type; char dvnam[32]; - strcpy (dvnam,vname); - uint8_t olen=len; - last_findex=-1; - last_sindex=-1; - char *ja=strchr(dvnam,'['); + strcpy (dvnam, vname); + uint8_t olen = len; + last_findex = -1; + last_sindex = -1; + char *ja = strchr(dvnam, '['); if (ja) { - *ja=0; + *ja = 0; ja++; - olen=strlen(dvnam); + olen = strlen(dvnam); } - for (count=0; countindex=count; // overwrite with global var index + if (!strncmp(cp, dvnam, olen)) { + uint8_t index = vtp[count].index; + *tind = vtp[count]; + tind->index = count; // overwrite with global var index if (vtp[count].bits.is_string==0) { - *vtype=NTYPE|index; + *vtype = NTYPE | index; if (vtp[count].bits.is_filter) { if (ja) { - lp+=olen+1; - lp=GetNumericResult(lp,OPER_EQU,&fvar,0); - last_findex=fvar; - fvar=Get_MFVal(index,fvar); - len=1; + lp += olen + 1; + lp = GetNumericArgument(lp, OPER_EQU, &fvar, 0); + last_findex = fvar; + fvar = Get_MFVal(index, fvar); + len = 1; } else { - fvar=Get_MFilter(index); + fvar = Get_MFilter(index); } } else { - fvar=glob_script_mem.fvars[index]; + fvar = glob_script_mem.fvars[index]; } - if (nres) fvar=-fvar; - if (fp) *fp=fvar; + if (nres) fvar = -fvar; + if (fp) *fp = fvar; } else { - *vtype=STYPE|index; - if (sp) strlcpy(sp,glob_script_mem.glob_snp+(index*glob_script_mem.max_ssize),SCRIPT_MAXSSIZE); + *vtype = STYPE|index; + if (sp) strlcpy(sp, glob_script_mem.glob_snp + (index * glob_script_mem.max_ssize), SCRIPT_MAXSSIZE); } - return lp+len; + return lp + len; } } } @@ -1434,50 +1440,50 @@ char *isvar(char *lp, uint8_t *vtype,struct T_INDEX *tind,float *fp,char *sp,Jso if (jo) { // look for json input char jvname[32]; - strcpy(jvname,vname); + strcpy(jvname, vname); const char* str_value; uint8_t aindex; String vn; - char *ja=strchr(jvname,'['); + char *ja=strchr(jvname, '['); if (ja) { // json array - *ja=0; + *ja = 0; ja++; // fetch array index float fvar; - GetNumericResult(ja,OPER_EQU,&fvar,0); - aindex=fvar; - if (aindex<1 || aindex>6) aindex=1; + GetNumericArgument(ja, OPER_EQU, &fvar, 0); + aindex = fvar; + if (aindex<1 || aindex>6) aindex = 1; aindex--; } if (jo->success()) { - char *subtype=strchr(jvname,'#'); + char *subtype = strchr(jvname, '#'); char *subtype2; if (subtype) { - *subtype=0; + *subtype = 0; subtype++; - subtype2=strchr(subtype,'#'); + subtype2 = strchr(subtype, '#'); if (subtype2) { - *subtype2=0; + *subtype2 = 0; *subtype2++; } } - vn=jvname; + vn = jvname; str_value = (*jo)[vn]; if ((*jo)[vn].success()) { if (subtype) { - JsonObject &jobj1=(*jo)[vn]; + JsonObject &jobj1 = (*jo)[vn]; if (jobj1.success()) { - vn=subtype; - jo=&jobj1; + vn = subtype; + jo = &jobj1; str_value = (*jo)[vn]; if ((*jo)[vn].success()) { // 2. stage if (subtype2) { - JsonObject &jobj2=(*jo)[vn]; + JsonObject &jobj2 = (*jo)[vn]; if ((*jo)[vn].success()) { - vn=subtype2; - jo=&jobj2; + vn = subtype2; + jo = &jobj2; str_value = (*jo)[vn]; if ((*jo)[vn].success()) { goto skip; @@ -1502,33 +1508,33 @@ char *isvar(char *lp, uint8_t *vtype,struct T_INDEX *tind,float *fp,char *sp,Jso } if (str_value && *str_value) { if ((*jo).is(vn)) { - if (!strncmp(str_value,"ON",2)) { - if (fp) *fp=1; + if (!strncmp(str_value, "ON", 2)) { + if (fp) *fp = 1; goto nexit; - } else if (!strncmp(str_value,"OFF",3)) { - if (fp) *fp=0; + } else if (!strncmp(str_value, "OFF", 3)) { + if (fp) *fp = 0; goto nexit; } else { - *vtype=STR_RES; - tind->bits.constant=1; - tind->bits.is_string=1; - if (sp) strlcpy(sp,str_value,SCRIPT_MAXSSIZE); - return lp+len; + *vtype = STR_RES; + tind->bits.constant = 1; + tind->bits.is_string = 1; + if (sp) strlcpy(sp, str_value, SCRIPT_MAXSSIZE); + return lp + len; } } else { if (fp) { - if (!strncmp(vn.c_str(),"Epoch",5)) { - *fp=atoi(str_value)-(uint32_t)EPOCH_OFFSET; + if (!strncmp(vn.c_str(), "Epoch", 5)) { + *fp = atoi(str_value) - (uint32_t)EPOCH_OFFSET; } else { - *fp=CharToFloat((char*)str_value); + *fp = CharToFloat((char*)str_value); } } nexit: - *vtype=NUM_RES; - tind->bits.constant=1; - tind->bits.is_string=0; - return lp+len; + *vtype = NUM_RES; + tind->bits.constant = 1; + tind->bits.is_string = 0; + return lp + len; } } } @@ -1539,29 +1545,28 @@ chknext: switch (vname[0]) { case 'a': #ifdef USE_ANGLE_FUNC - if (!strncmp(vname,"acos(",5)) { - lp+=5; - lp=GetNumericResult(lp,OPER_EQU,&fvar,0); - fvar=acosf(fvar); + if (!strncmp(vname, "acos(", 5)) { + lp=GetNumericArgument(lp + 5, OPER_EQU, &fvar, 0); + fvar = acosf(fvar); lp++; - len=0; + len = 0; goto exit; } #endif - if (!strncmp(vname,"asc(",4)) { + if (!strncmp(vname, "asc(", 4)) { char str[SCRIPT_MAXSSIZE]; - lp=GetStringResult(lp+4,OPER_EQU,str,0); - fvar=str[0]; + lp = GetStringArgument(lp + 4, OPER_EQU, str, 0); + fvar = str[0]; lp++; - len=0; + len = 0; goto exit; } - if (!strncmp(vname,"adc(",4)) { - lp=GetNumericResult(lp+4,OPER_EQU,&fvar,0); + if (!strncmp(vname, "adc(", 4)) { + lp = GetNumericArgument(lp + 4, OPER_EQU, &fvar, 0); while (*lp==' ') lp++; - float fvar1=1; + float fvar1 = 1; if (*lp!=')') { - lp=GetNumericResult(lp,OPER_EQU,&fvar1,0); + lp = GetNumericArgument(lp, OPER_EQU, &fvar1, 0); if (fvar1<32 || fvar1>39) fvar1 = 32; } lp++; @@ -1569,146 +1574,144 @@ chknext: #ifdef ESP32 // ESP32 #ifdef USE_ADC - fvar=AdcRead(fvar1, fvar); + fvar = AdcRead(fvar1, fvar); #else - fvar=999.999; + fvar = 999.999; #endif // USE_ADC #else // ESP8266 #ifndef USE_ADC_VCC - fvar=AdcRead(fvar); + fvar = AdcRead(fvar); #else - fvar=(float)ESP.getVcc()/1000.0; + fvar = (float)ESP.getVcc() / 1000.0; #endif // USE_ADC_VCC #endif // ESP32 - len=0; + len = 0; goto exit; } break; case 'b': - if (!strncmp(vname,"boot",4)) { + if (!strncmp(vname, "boot", 4)) { if (rules_flag.system_boot) { - rules_flag.system_boot=0; - fvar=1; + rules_flag.system_boot = 0; + fvar = 1; } goto exit; } #ifdef USE_BUTTON_EVENT - if (!strncmp(vname,"bt[",3)) { + if (!strncmp(vname, "bt[", 3)) { // tasmota button state - GetNumericResult(vname+3,OPER_EQU,&fvar,0); - uint32_t index=fvar; - if (index<1 || index>MAX_KEYS) index=1; - fvar=script_button[index-1]; - script_button[index-1]|=0x80; + GetNumericArgument(vname+3, OPER_EQU, &fvar, 0); + uint32_t index = fvar; + if (index<1 || index>MAX_KEYS) index = 1; + fvar=script_button[index - 1]; + script_button[index - 1] |= 0x80; len++; goto exit; } -#endif +#endif //USE_BUTTON_EVENT break; case 'c': - if (!strncmp(vname,"chg[",4)) { + if (!strncmp(vname, "chg[", 4)) { // var changed struct T_INDEX ind; uint8_t vtype; - isvar(vname+4,&vtype,&ind,0,0,0); + isvar(vname + 4, &vtype, &ind, 0, 0, 0); if (!ind.bits.constant) { - uint8_t index=glob_script_mem.type[ind.index].index; - if (glob_script_mem.fvars[index]!=glob_script_mem.s_fvars[index]) { + uint8_t index = glob_script_mem.type[ind.index].index; + if (glob_script_mem.fvars[index] != glob_script_mem.s_fvars[index]) { // var has changed - glob_script_mem.s_fvars[index]=glob_script_mem.fvars[index]; - fvar=1; + glob_script_mem.s_fvars[index] = glob_script_mem.fvars[index]; + fvar = 1; len++; goto exit; } else { - fvar=0; + fvar = 0; len++; goto exit; } } } #ifdef ESP32 - if (!strncmp(vname,"core",4)) { - fvar=xPortGetCoreID(); + if (!strncmp(vname, "core", 4)) { + fvar = xPortGetCoreID(); goto exit; } #ifdef USE_SCRIPT_TASK - if (!strncmp(vname,"ct(",3)) { - lp+=3; - lp=GetNumericResult(lp,OPER_EQU,&fvar,0); + if (!strncmp(vname, "ct(", 3)) { + lp = GetNumericArgument(lp + 3, OPER_EQU, &fvar, 0); while (*lp==' ') lp++; float fvar1; - lp=GetNumericResult(lp,OPER_EQU,&fvar1,0); + lp = GetNumericArgument(lp, OPER_EQU, &fvar1, 0); while (*lp==' ') lp++; float fvar2; - lp=GetNumericResult(lp,OPER_EQU,&fvar2,0); + lp = GetNumericArgument(lp, OPER_EQU, &fvar2, 0); lp++; - fvar=scripter_create_task(fvar,fvar1,fvar2); - len=0; + fvar = scripter_create_task(fvar, fvar1, fvar2); + len = 0; goto exit; } -#endif -#endif +#endif //USE_SCRIPT_TASK +#endif //ESP32 break; case 'd': - if (!strncmp(vname,"day",3)) { - fvar=RtcTime.day_of_month; + if (!strncmp(vname, "day", 3)) { + fvar = RtcTime.day_of_month; goto exit; } break; case 'e': - if (!strncmp(vname,"epoch",5)) { - fvar=UtcTime()-(uint32_t)EPOCH_OFFSET; + if (!strncmp(vname, "epoch", 5)) { + fvar = UtcTime() - (uint32_t)EPOCH_OFFSET; goto exit; } - if (!strncmp(vname,"eres",4)) { - fvar=event_handeled; - tind->index=SCRIPT_EVENT_HANDLED; + if (!strncmp(vname, "eres", 4)) { + fvar = event_handeled; + tind->index = SCRIPT_EVENT_HANDLED; goto exit_settable; } #ifdef USE_ENERGY_SENSOR - if (!strncmp(vname,"enrg[",5)) { - lp+=5; - lp=GetNumericResult(lp,OPER_EQU,&fvar,0); + if (!strncmp(vname, "enrg[", 5)) { + lp=GetNumericArgument(lp + 5, OPER_EQU, &fvar, 0); while (*lp==' ') lp++; switch ((uint32_t)fvar) { case 0: - fvar=Energy.total; + fvar = Energy.total; break; case 1: - fvar=Energy.voltage[0]; + fvar = Energy.voltage[0]; break; case 2: - fvar=Energy.voltage[1]; + fvar = Energy.voltage[1]; break; case 3: - fvar=Energy.voltage[2]; + fvar = Energy.voltage[2]; break; case 4: - fvar=Energy.current[0]; + fvar = Energy.current[0]; break; case 5: - fvar=Energy.current[1]; + fvar = Energy.current[1]; break; case 6: - fvar=Energy.current[2]; + fvar = Energy.current[2]; break; case 7: - fvar=Energy.active_power[0]; + fvar = Energy.active_power[0]; break; case 8: - fvar=Energy.active_power[1]; + fvar = Energy.active_power[1]; break; case 9: - fvar=Energy.active_power[2]; + fvar = Energy.active_power[2]; break; default: - fvar=99999; + fvar = 99999; break; } - len=0; + len = 0; lp++; goto exit; } @@ -1717,166 +1720,161 @@ chknext: case 'f': //#define DEBUG_FS #ifdef USE_SCRIPT_FATFS - if (!strncmp(vname,"fo(",3)) { - lp+=3; + if (!strncmp(vname, "fo(", 3)) { char str[SCRIPT_MAXSSIZE]; - lp=GetStringResult(lp,OPER_EQU,str,0); + lp = GetStringArgument(lp + 3, OPER_EQU, str, 0); while (*lp==' ') lp++; - uint8_t mode=0; + uint8_t mode = 0; if ((*lp=='r') || (*lp=='w') || (*lp=='a')) { switch (*lp) { case 'r': - mode=0; + mode = 0; break; case 'w': - mode=1; + mode = 1; break; case 'a': - mode=2; + mode = 2; break; } lp++; } else { - lp=GetNumericResult(lp,OPER_EQU,&fvar,0); - mode=fvar; + lp = GetNumericArgument(lp, OPER_EQU, &fvar, 0); + mode = fvar; } - fvar=-1; - for (uint8_t cnt=0;cntopen(str,FILE_READ); + glob_script_mem.files[cnt] = fsp->open(str, FILE_READ); if (glob_script_mem.files[cnt].isDirectory()) { glob_script_mem.files[cnt].rewindDirectory(); - glob_script_mem.file_flags[cnt].is_dir=1; + glob_script_mem.file_flags[cnt].is_dir = 1; } else { - glob_script_mem.file_flags[cnt].is_dir=0; + glob_script_mem.file_flags[cnt].is_dir = 0; } } else { if (mode==1) { - glob_script_mem.files[cnt]=fsp->open(str,FILE_WRITE); + glob_script_mem.files[cnt] = fsp->open(str,FILE_WRITE); #ifdef DEBUG_FS - AddLog_P2(LOG_LEVEL_INFO,PSTR("open file for write %d"),cnt); + AddLog_P2(LOG_LEVEL_INFO, PSTR("open file for write %d"), cnt); #endif } else { - glob_script_mem.files[cnt]=fsp->open(str,FILE_APPEND); + glob_script_mem.files[cnt] = fsp->open(str,FILE_APPEND); #ifdef DEBUG_FS - AddLog_P2(LOG_LEVEL_INFO,PSTR("open file for append %d"),cnt); + AddLog_P2(LOG_LEVEL_INFO, PSTR("open file for append %d"), cnt); #endif } } if (glob_script_mem.files[cnt]) { - fvar=cnt; - glob_script_mem.file_flags[cnt].is_open=1; + fvar = cnt; + glob_script_mem.file_flags[cnt].is_open = 1; } else { - AddLog_P(LOG_LEVEL_INFO,PSTR("file open failed")); + AddLog_P(LOG_LEVEL_INFO, PSTR("file open failed")); } break; } } lp++; - len=0; + len = 0; goto exit; } - if (!strncmp(vname,"fc(",3)) { - lp+=3; - lp=GetNumericResult(lp,OPER_EQU,&fvar,0); + if (!strncmp(vname, "fc(", 3)) { + lp = GetNumericArgument(lp + 3, OPER_EQU, &fvar, 0); if (fvar>=0) { - uint8_t ind=fvar; - if (ind>=SFS_MAX) ind=SFS_MAX-1; + uint8_t ind = fvar; + if (ind>=SFS_MAX) ind = SFS_MAX - 1; #ifdef DEBUG_FS - AddLog_P2(LOG_LEVEL_INFO,PSTR("closing file %d"),ind); + AddLog_P2(LOG_LEVEL_INFO, PSTR("closing file %d"), ind); #endif glob_script_mem.files[ind].close(); - glob_script_mem.file_flags[ind].is_open=0; + glob_script_mem.file_flags[ind].is_open = 0; } - fvar=0; + fvar = 0; lp++; - len=0; + len = 0; goto exit; } - if (!strncmp(vname,"ff(",3)) { - lp+=3; - lp=GetNumericResult(lp,OPER_EQU,&fvar,0); - uint8_t ind=fvar; - if (ind>=SFS_MAX) ind=SFS_MAX-1; + if (!strncmp(vname, "ff(", 3)) { + lp = GetNumericArgument(lp + 3, OPER_EQU, &fvar, 0); + uint8_t ind = fvar; + if (ind>=SFS_MAX) ind = SFS_MAX - 1; glob_script_mem.files[ind].flush(); - fvar=0; + fvar = 0; lp++; - len=0; + len = 0; goto exit; } - if (!strncmp(vname,"fw(",3)) { - lp+=3; + if (!strncmp(vname, "fw(", 3)) { char str[SCRIPT_MAXSSIZE]; - lp=ForceStringVar(lp,str); + lp = ForceStringVar(lp + 3, str); while (*lp==' ') lp++; - lp=GetNumericResult(lp,OPER_EQU,&fvar,0); - uint8_t ind=fvar; - if (ind>=SFS_MAX) ind=SFS_MAX-1; + lp = GetNumericArgument(lp, OPER_EQU, &fvar, 0); + uint8_t ind = fvar; + if (ind>=SFS_MAX) ind = SFS_MAX - 1; if (glob_script_mem.file_flags[ind].is_open) { - fvar=glob_script_mem.files[ind].print(str); + fvar = glob_script_mem.files[ind].print(str); } else { - fvar=0; + fvar = 0; } lp++; - len=0; + len = 0; goto exit; } - if (!strncmp(vname,"fr(",3)) { - lp+=3; + if (!strncmp(vname, "fr(", 3)) { struct T_INDEX ind; uint8_t vtype; - uint8_t sindex=0; - lp=isvar(lp,&vtype,&ind,0,0,0); + uint8_t sindex = 0; + lp = isvar(lp + 3, &vtype, &ind, 0, 0, 0); if (vtype!=VAR_NV) { // found variable as result if ((vtype&STYPE)==0) { // error - fvar=0; + fvar = 0; goto exit; } else { // string result - sindex=glob_script_mem.type[ind.index].index; + sindex = glob_script_mem.type[ind.index].index; } } else { // error - fvar=0; + fvar = 0; goto exit; } while (*lp==' ') lp++; - lp=GetNumericResult(lp,OPER_EQU,&fvar,0); - uint8_t find=fvar; - if (find>=SFS_MAX) find=SFS_MAX-1; - uint8_t index=0; - char str[glob_script_mem.max_ssize+1]; - char *cp=str; + lp = GetNumericArgument(lp, OPER_EQU, &fvar, 0); + uint8_t find = fvar; + if (find>=SFS_MAX) find = SFS_MAX - 1; + uint8_t index = 0; + char str[glob_script_mem.max_ssize + 1]; + char *cp = str; if (glob_script_mem.file_flags[find].is_open) { if (glob_script_mem.file_flags[find].is_dir) { while (true) { - File entry=glob_script_mem.files[find].openNextFile(); + File entry = glob_script_mem.files[find].openNextFile(); if (entry) { if (!reject((char*)entry.name())) { - char *ep=(char*)entry.name(); + char *ep = (char*)entry.name(); if (*ep=='/') ep++; char *lcp = strrchr(ep,'/'); if (lcp) { - ep=lcp+1; + ep = lcp + 1; } - strcpy(str,ep); + strcpy(str, ep); entry.close(); break; } } else { - *cp=0; + *cp = 0; break; } entry.close(); } - index=strlen(str); + index = strlen(str); } else { while (glob_script_mem.files[find].available()) { uint8_t buf[1]; @@ -1884,244 +1882,234 @@ chknext: if (buf[0]=='\t' || buf[0]==',' || buf[0]=='\n' || buf[0]=='\r') { break; } else { - *cp++=buf[0]; + *cp++ = buf[0]; index++; - if (index>=glob_script_mem.max_ssize-1) break; + if (index>=glob_script_mem.max_ssize - 1) break; } } - *cp=0; + *cp = 0; } } else { - strcpy(str,"file error"); + strcpy(str, "file error"); } lp++; - strlcpy(glob_script_mem.glob_snp+(sindex*glob_script_mem.max_ssize),str,glob_script_mem.max_ssize); - fvar=index; - len=0; + strlcpy(glob_script_mem.glob_snp + (sindex * glob_script_mem.max_ssize), str, glob_script_mem.max_ssize); + fvar = index; + len = 0; goto exit; } - if (!strncmp(vname,"fd(",3)) { - lp+=3; - char str[glob_script_mem.max_ssize+1]; - lp=GetStringResult(lp,OPER_EQU,str,0); + if (!strncmp(vname, "fd(", 3)) { + char str[glob_script_mem.max_ssize + 1]; + lp = GetStringArgument(lp + 3, OPER_EQU, str, 0); fsp->remove(str); lp++; - len=0; + len = 0; goto exit; } #if defined(ESP32) && defined(USE_WEBCAM) - if (!strncmp(vname,"fwp(",4)) { - lp+=4; - lp=GetNumericResult(lp,OPER_EQU,&fvar,0); + if (!strncmp(vname, "fwp(", 4)) { + lp = GetNumericArgument(lp + 4, OPER_EQU, &fvar, 0); while (*lp==' ') lp++; float fvar1; - lp=GetNumericResult(lp,OPER_EQU,&fvar1,0); - uint8_t ind=fvar1; - if (ind>=SFS_MAX) ind=SFS_MAX-1; + lp = GetNumericArgument(lp, OPER_EQU, &fvar1, 0); + uint8_t ind = fvar1; + if (ind>=SFS_MAX) ind = SFS_MAX - 1; if (glob_script_mem.file_flags[ind].is_open) { uint8_t *buff; - float maxps=WcGetPicstore(-1,0); - if (fvar<1 || fvar>maxps) fvar=1; - uint32_t len=WcGetPicstore(fvar-1, &buff); + float maxps = WcGetPicstore(-1, 0); + if (fvar<1 || fvar>maxps) fvar = 1; + uint32_t len = WcGetPicstore(fvar - 1, &buff); if (len) { //glob_script_mem.files[ind].seek(0,SeekEnd); - fvar=glob_script_mem.files[ind].write(buff,len); + fvar = glob_script_mem.files[ind].write(buff, len); } else { - fvar=0; + fvar = 0; } //AddLog_P2(LOG_LEVEL_INFO, PSTR("picture save: %d"), len); } else { - fvar=0; + fvar = 0; } lp++; - len=0; + len = 0; goto exit; } -#endif +#endif //ESP32 && USE_WEBCAM #ifdef USE_SCRIPT_FATFS_EXT - if (!strncmp(vname,"fe(",3)) { - lp+=3; - char str[glob_script_mem.max_ssize+1]; - lp=GetStringResult(lp,OPER_EQU,str,0); + if (!strncmp(vname, "fe(", 3)) { + char str[glob_script_mem.max_ssize + 1]; + lp = GetStringArgument(lp + 3, OPER_EQU, str, 0); // execute script - File ef=fsp->open(str,FILE_READ); + File ef = fsp->open(str, FILE_READ); if (ef) { - uint16_t fsiz=ef.size(); + uint16_t fsiz = ef.size(); if (fsiz<2048) { - char *script=(char*)calloc(fsiz+16,1); + char *script = (char*)calloc(fsiz + 16, 1); if (script) { ef.read((uint8_t*)script,fsiz); execute_script(script); free(script); - fvar=1; + fvar = 1; } } ef.close(); } lp++; - len=0; + len = 0; goto exit; } - if (!strncmp(vname,"fmd(",4)) { - lp+=4; - char str[glob_script_mem.max_ssize+1]; - lp=GetStringResult(lp,OPER_EQU,str,0); - fvar=fsp->mkdir(str); + if (!strncmp(vname, "fmd(", 4)) { + char str[glob_script_mem.max_ssize + 1]; + lp = GetStringArgument(lp + 4, OPER_EQU, str, 0); + fvar = fsp->mkdir(str); lp++; - len=0; + len = 0; goto exit; } - if (!strncmp(vname,"frd(",4)) { - lp+=4; - char str[glob_script_mem.max_ssize+1]; - lp=GetStringResult(lp,OPER_EQU,str,0); - fvar=fsp->rmdir(str); + if (!strncmp(vname, "frd(", 4)) { + char str[glob_script_mem.max_ssize + 1]; + lp = GetStringArgument(lp + 4, OPER_EQU, str, 0); + fvar = fsp->rmdir(str); lp++; - len=0; + len = 0; goto exit; } - if (!strncmp(vname,"fx(",3)) { - lp+=3; - char str[glob_script_mem.max_ssize+1]; - lp=GetStringResult(lp,OPER_EQU,str,0); - if (fsp->exists(str)) fvar=1; - else fvar=0; + if (!strncmp(vname, "fx(", 3)) { + char str[glob_script_mem.max_ssize + 1]; + lp = GetStringArgument(lp + 3, OPER_EQU, str, 0); + if (fsp->exists(str)) fvar = 1; + else fvar = 0; lp++; - len=0; + len = 0; goto exit; } #endif // USE_SCRIPT_FATFS_EXT - if (!strncmp(vname,"fl1(",4) || !strncmp(vname,"fl2(",4) ) { - uint8_t lknum=*(lp+2)&3; - lp+=4; - char str[glob_script_mem.max_ssize+1]; - lp=GetStringResult(lp,OPER_EQU,str,0); - if (lknum<1 || lknum>2) lknum=1; - strlcpy(glob_script_mem.flink[lknum-1],str,14); + if (!strncmp(vname, "fl1(", 4) || !strncmp(vname, "fl2(", 4) ) { + uint8_t lknum = *(lp+2)&3; + char str[glob_script_mem.max_ssize + 1]; + lp = GetStringArgument(lp + 4, OPER_EQU, str, 0); + if (lknum<1 || lknum>2) lknum = 1; + strlcpy(glob_script_mem.flink[lknum - 1], str, 14); lp++; - fvar=0; - len=0; + fvar = 0; + len = 0; goto exit; } - if (!strncmp(vname,"fsm",3)) { + if (!strncmp(vname, "fsm", 3)) { fvar=glob_script_mem.script_sd_found; //card_init(); goto exit; } #endif //USE_SCRIPT_FATFS - if (!strncmp(vname,"freq",4)) { + if (!strncmp(vname, "freq", 4)) { #ifdef ESP32 - fvar=getCpuFrequencyMhz(); + fvar = getCpuFrequencyMhz(); #else - fvar=ESP.getCpuFreqMHz(); + fvar = ESP.getCpuFreqMHz(); #endif goto exit; } break; case 'g': - if (!strncmp(vname,"gtmp",4)) { - fvar=global_temperature_celsius; + if (!strncmp(vname, "gtmp", 4)) { + fvar = global_temperature_celsius; goto exit; } - if (!strncmp(vname,"ghum",4)) { - fvar=global_humidity; + if (!strncmp(vname, "ghum", 4)) { + fvar = global_humidity; goto exit; } - if (!strncmp(vname,"gprs",4)) { - fvar=global_pressure_hpa; + if (!strncmp(vname, "gprs", 4)) { + fvar = global_pressure_hpa; goto exit; } - if (!strncmp(vname,"gtopic",6)) { - if (sp) strlcpy(sp,SettingsText(SET_MQTT_GRP_TOPIC),glob_script_mem.max_ssize); + if (!strncmp(vname, "gtopic", 6)) { + if (sp) strlcpy(sp, SettingsText(SET_MQTT_GRP_TOPIC), glob_script_mem.max_ssize); goto strexit; } #ifdef SCRIPT_GET_HTTPS_JP - if (!strncmp(vname,"gjp(",4)) { + if (!strncmp(vname, "gjp(", 4)) { char host[SCRIPT_MAXSSIZE]; - lp=GetStringResult(lp+4,OPER_EQU,host,0); + lp = GetStringArgument(lp + 4, OPER_EQU, host, 0); SCRIPT_SKIP_SPACES char path[SCRIPT_MAXSSIZE]; - lp=GetStringResult(lp,OPER_EQU,path,0); - fvar=call2https(host,path); + lp = GetStringArgument(lp, OPER_EQU, path, 0); + fvar = call2https(host, path); lp++; - len=0; + len = 0; goto exit; } -#endif +#endif //SCRIPT_GET_HTTPS_JP break; case 'h': - if (!strncmp(vname,"hours",5)) { - fvar=RtcTime.hour; + if (!strncmp(vname, "hours", 5)) { + fvar = RtcTime.hour; goto exit; } - if (!strncmp(vname,"heap",4)) { - fvar=ESP_getFreeHeap(); + if (!strncmp(vname, "heap", 4)) { + fvar = ESP_getFreeHeap(); goto exit; } - if (!strncmp(vname,"hn(",3)) { - lp=GetNumericResult(lp+3,OPER_EQU,&fvar,0); - if (fvar<0 || fvar>255) fvar=0; + if (!strncmp(vname, "hn(", 3)) { + lp = GetNumericArgument(lp + 3, OPER_EQU, &fvar, 0); + if (fvar<0 || fvar>255) fvar = 0; lp++; - len=0; + len = 0; if (sp) { - sprintf(sp,"%02x",(uint8_t)fvar); + sprintf(sp, "%02x", (uint8_t)fvar); } goto strexit; } - if (!strncmp(vname,"hx(",3)) { - lp=GetNumericResult(lp+3,OPER_EQU,&fvar,0); + if (!strncmp(vname, "hx(", 3)) { + lp = GetNumericArgument(lp + 3, OPER_EQU, &fvar, 0); lp++; - len=0; + len = 0; if (sp) { - sprintf(sp,"%08x",(uint32_t)fvar); + sprintf(sp, "%08x", (uint32_t)fvar); } goto strexit; } - if (!strncmp(vname,"hd(",3)) { + if (!strncmp(vname, "hd(", 3)) { char str[SCRIPT_MAXSSIZE]; - lp=GetStringResult(lp+3,OPER_EQU,str,0); - fvar=strtol(str,NULL,16); + lp = GetStringArgument(lp + 3, OPER_EQU, str, 0); + fvar = strtol(str, NULL, 16); lp++; - len=0; + len = 0; goto exit; } #ifdef USE_LIGHT -//#ifdef USE_WS2812 - if (!strncmp(vname,"hsvrgb(",7)) { - lp=GetNumericResult(lp+7,OPER_EQU,&fvar,0); - if (fvar<0 || fvar>360) fvar=0; + if (!strncmp(vname, "hsvrgb(", 7)) { + lp = GetNumericArgument(lp + 7, OPER_EQU, &fvar, 0); + if (fvar<0 || fvar>360) fvar = 0; SCRIPT_SKIP_SPACES // arg2 float fvar2; - lp=GetNumericResult(lp,OPER_EQU,&fvar2,0); - if (fvar2<0 || fvar2>100) fvar2=0; + lp = GetNumericArgument(lp, OPER_EQU, &fvar2, 0); + if (fvar2<0 || fvar2>100) fvar2 = 0; SCRIPT_SKIP_SPACES // arg3 float fvar3; - lp=GetNumericResult(lp,OPER_EQU,&fvar3,0); - if (fvar3<0 || fvar3>100) fvar3=0; - - fvar=HSVToRGB(fvar,fvar2,fvar3); + lp = GetNumericArgument(lp, OPER_EQU, &fvar3, 0); + if (fvar3<0 || fvar3>100) fvar3 = 0; + fvar = HSVToRGB(fvar, fvar2, fvar3); lp++; - len=0; + len = 0; goto exit; } -//#endif -#endif +#endif //USE_LIGHT break; #define MAX_SARRAY_NUM 32 case 'i': - if (!strncmp(vname,"int(",4)) { - lp=GetNumericResult(lp+4,OPER_EQU,&fvar,0); - fvar=floor(fvar); + if (!strncmp(vname, "int(", 4)) { + lp = GetNumericArgument(lp + 4, OPER_EQU, &fvar, 0); + fvar = floor(fvar); lp++; - len=0; + len = 0; goto exit; } - if (!strncmp(vname,"is(",3)) { - lp=GetNumericResult(lp+3,OPER_EQU,&fvar,0); + if (!strncmp(vname, "is(", 3)) { + lp = GetNumericArgument(lp + 3, OPER_EQU, &fvar, 0); SCRIPT_SKIP_SPACES if (*lp!='"') { break; @@ -2131,15 +2119,15 @@ chknext: if (glob_script_mem.si_num>0 && glob_script_mem.last_index_string) { free(glob_script_mem.last_index_string); } - char *sstart=lp; - uint8_t slen=0; - for (uint32_t cnt=0; cnt<256; cnt++) { + char *sstart = lp; + uint8_t slen = 0; + for (uint32_t cnt = 0; cnt<256; cnt++) { if (*lp=='\n' || *lp=='"' || *lp==0) { lp++; if (cnt>0 && !slen) { slen++; } - glob_script_mem.siro_num=slen; + glob_script_mem.siro_num = slen; break; } if (*lp=='|') { @@ -2151,31 +2139,31 @@ chknext: glob_script_mem.si_num = fvar; if (glob_script_mem.si_num>0) { if (glob_script_mem.si_num>MAX_SARRAY_NUM) { - glob_script_mem.si_num=MAX_SARRAY_NUM; + glob_script_mem.si_num = MAX_SARRAY_NUM; } - glob_script_mem.last_index_string=(char*)calloc(glob_script_mem.max_ssize*glob_script_mem.si_num,1); - for (uint32_t cnt=0; cntglob_script_mem.si_num) { - index=glob_script_mem.si_num; + index = glob_script_mem.si_num; } - strlcpy(str,glob_script_mem.last_index_string+(index*glob_script_mem.max_ssize),glob_script_mem.max_ssize); + strlcpy(str,glob_script_mem.last_index_string + (index * glob_script_mem.max_ssize), glob_script_mem.max_ssize); } } lp++; - if (sp) strlcpy(sp,str,glob_script_mem.max_ssize); - len=0; + if (sp) strlcpy(sp, str, glob_script_mem.max_ssize); + len = 0; goto strexit; } break; case 'l': - if (!strncmp(vname,"lip",3)) { - if (sp) strlcpy(sp,(const char*)WiFi.localIP().toString().c_str(),glob_script_mem.max_ssize); + if (!strncmp(vname, "lip", 3)) { + if (sp) strlcpy(sp, (const char*)WiFi.localIP().toString().c_str(), glob_script_mem.max_ssize); goto strexit; } #ifdef USE_SCRIPT_GLOBVARS - if (!strncmp(vname,"luip",4)) { - if (sp) strlcpy(sp,IPAddressToString(last_udp_ip),glob_script_mem.max_ssize); + if (!strncmp(vname, "luip", 4)) { + if (sp) strlcpy(sp, IPAddressToString(last_udp_ip), glob_script_mem.max_ssize); goto strexit; } -#endif - if (!strncmp(vname,"loglvl",6)) { - fvar=glob_script_mem.script_loglevel; - tind->index=SCRIPT_LOGLEVEL; +#endif //USE_SCRIPT_GLOBVARS + if (!strncmp(vname, "loglvl", 6)) { + fvar = glob_script_mem.script_loglevel; + tind->index = SCRIPT_LOGLEVEL; exit_settable: - if (fp) *fp=fvar; - *vtype=NTYPE; - tind->bits.settable=1; - tind->bits.is_string=0; - return lp+len; + if (fp) *fp = fvar; + *vtype = NTYPE; + tind->bits.settable = 1; + tind->bits.is_string = 0; + return lp + len; } break; case 'm': - if (!strncmp(vname,"med(",4)) { + if (!strncmp(vname, "med(", 4)) { float fvar1; - lp=GetNumericResult(lp+4,OPER_EQU,&fvar1,0); + lp = GetNumericArgument(lp + 4, OPER_EQU, &fvar1, 0); SCRIPT_SKIP_SPACES // arg2 float fvar2; - lp=GetNumericResult(lp,OPER_EQU,&fvar2,0); - fvar=DoMedian5(fvar1,fvar2); + lp = GetNumericArgument(lp, OPER_EQU, &fvar2, 0); + fvar = DoMedian5(fvar1, fvar2); lp++; - len=0; + len = 0; goto exit; } #ifdef USE_ANGLE_FUNC - if (!strncmp(vname,"mpt(",4)) { - lp=GetNumericResult(lp+4,OPER_EQU,&fvar,0); - fvar=MeasurePulseTime(fvar); + if (!strncmp(vname, "mpt(", 4)) { + lp = GetNumericArgument(lp + 4, OPER_EQU, &fvar, 0); + fvar = MeasurePulseTime(fvar); lp++; - len=0; + len = 0; goto exit; } -#endif - if (!strncmp(vname,"micros",6)) { - fvar=micros(); +#endif //USE_ANGLE_FUNC + if (!strncmp(vname, "micros", 6)) { + fvar = micros(); goto exit; } - if (!strncmp(vname,"millis",6)) { - fvar=millis(); + if (!strncmp(vname, "millis", 6)) { + fvar = millis(); goto exit; } - if (!strncmp(vname,"mins",4)) { - fvar=RtcTime.minute; + if (!strncmp(vname, "mins", 4)) { + fvar = RtcTime.minute; goto exit; } - if (!strncmp(vname,"month",5)) { - fvar=RtcTime.month; + if (!strncmp(vname, "month", 5)) { + fvar = RtcTime.month; goto exit; } - if (!strncmp(vname,"mqttc",5)) { + if (!strncmp(vname, "mqttc", 5)) { if (rules_flag.mqtt_connected) { - rules_flag.mqtt_connected=0; - fvar=1; + rules_flag.mqtt_connected = 0; + fvar = 1; } goto exit; } - if (!strncmp(vname,"mqttd",5)) { + if (!strncmp(vname, "mqttd", 5)) { if (rules_flag.mqtt_disconnected) { - rules_flag.mqtt_disconnected=0; - fvar=1; + rules_flag.mqtt_disconnected = 0; + fvar = 1; } goto exit; } - if (!strncmp(vname,"mqtts",5)) { - fvar=!global_state.mqtt_down; + if (!strncmp(vname, "mqtts", 5)) { + fvar = !global_state.mqtt_down; goto exit; } - if (!strncmp(vname,"mp(",3)) { - lp+=3; + if (!strncmp(vname, "mp(", 3)) { float fvar1; - lp=GetNumericResult(lp,OPER_EQU,&fvar1,0); + lp = GetNumericArgument(lp + 3, OPER_EQU, &fvar1, 0); SCRIPT_SKIP_SPACES while (*lp!=')') { - char *opp=lp; + char *opp = lp; lp++; float fvar2; - lp=GetNumericResult(lp,OPER_EQU,&fvar2,0); + lp = GetNumericArgument(lp, OPER_EQU, &fvar2, 0); SCRIPT_SKIP_SPACES - fvar=fvar1; + fvar = fvar1; if ((*opp=='<' && fvar1' && fvar1>fvar2) || - (*opp=='=' && fvar1==fvar2)) - { - if (*lp!='<' && *lp!='>' && *lp!='=' && *lp!=')' && *lp!=SCRIPT_EOL) { - float fvar3; - lp=GetNumericResult(lp,OPER_EQU,&fvar3,0); - SCRIPT_SKIP_SPACES - fvar=fvar3; - } else { - fvar=fvar2; - } - break; + (*opp=='=' && fvar1==fvar2)) { + if (*lp!='<' && *lp!='>' && *lp!='=' && *lp!=')' && *lp!=SCRIPT_EOL) { + float fvar3; + lp = GetNumericArgument(lp, OPER_EQU, &fvar3, 0); + SCRIPT_SKIP_SPACES + fvar=fvar3; + } else { + fvar = fvar2; + } + break; } while (*lp!='<' && *lp!='>' && *lp!='=' && *lp!=')' && *lp!=SCRIPT_EOL) lp++; } - len=0; + len = 0; goto exit; } #ifdef USE_MORITZ - if (!strncmp(vname,"mo(",3)) { + if (!strncmp(vname, "mo(", 3)) { float fvar1; - lp=GetNumericResult(lp+3,OPER_EQU,&fvar1,0); + lp = GetNumericArgument(lp + 3, OPER_EQU, &fvar1, 0); SCRIPT_SKIP_SPACES float fvar2; - lp=GetNumericResult(lp,OPER_EQU,&fvar2,0); + lp = GetNumericArgument(lp, OPER_EQU, &fvar2, 0); SCRIPT_SKIP_SPACES char rbuff[64]; - fvar=mo_getvars(fvar1,fvar2,rbuff); + fvar = mo_getvars(fvar1, fvar2, rbuff); lp++; - if (sp) strlcpy(sp,rbuff,glob_script_mem.max_ssize); - len=0; + if (sp) strlcpy(sp, rbuff, glob_script_mem.max_ssize); + len = 0; goto strexit; } -#endif +#endif //USE_MORITZ break; case 'p': - if (!strncmp(vname,"pin[",4)) { + if (!strncmp(vname, "pin[", 4)) { // raw pin level - GetNumericResult(vname+4,OPER_EQU,&fvar,0); - fvar=digitalRead((uint8_t)fvar); + GetNumericArgument(vname + 4, OPER_EQU, &fvar, 0); + fvar = digitalRead((uint8_t)fvar); // skip ] bracket len++; goto exit; } - if (!strncmp(vname,"pn[",3)) { - GetNumericResult(vname+3,OPER_EQU,&fvar,0); - fvar=Pin(fvar); + if (!strncmp(vname, "pn[", 3)) { + GetNumericArgument(vname + 3, OPER_EQU, &fvar, 0); + fvar = Pin(fvar); // skip ] bracket len++; goto exit; } #if defined(ESP32) && (defined(USE_I2S_AUDIO) || defined(USE_TTGO_WATCH)) - if (!strncmp(vname,"pl(",3)) { + if (!strncmp(vname, "pl(", 3)) { char path[SCRIPT_MAXSSIZE]; - lp=GetStringResult(lp+3,OPER_EQU,path,0); + lp = GetStringArgument(lp + 3, OPER_EQU, path, 0); Play_mp3(path); len++; - len=0; + len = 0; goto exit; } #endif // USE_I2S_AUDIO - if (!strncmp(vname,"pd[",3)) { - GetNumericResult(vname+3,OPER_EQU,&fvar,0); - uint8_t gpiopin=fvar; + if (!strncmp(vname, "pd[", 3)) { + GetNumericArgument(vname + 3, OPER_EQU, &fvar, 0); + uint8_t gpiopin = fvar; /* for (uint8_t i=0;iMAX_COUNTERS) index=1; - fvar=RtcSettings.pulse_counter[index-1]; - len+=1; + if (!strncmp(vname, "pc[", 3)) { + GetNumericArgument(vname + 3, OPER_EQU, &fvar, 0); + uint8_t index = fvar; + if (index<1 || index>MAX_COUNTERS) index = 1; + fvar = RtcSettings.pulse_counter[index - 1]; + len += 1; goto exit; } break; case 'r': - if (!strncmp(vname,"ram",3)) { - fvar=glob_script_mem.script_mem_size+(glob_script_mem.script_size)+(PMEM_SIZE); + if (!strncmp(vname, "ram", 3)) { + fvar = glob_script_mem.script_mem_size + (glob_script_mem.script_size) + (PMEM_SIZE); goto exit; } - if (!strncmp(vname,"rnd(",4)) { + if (!strncmp(vname, "rnd(", 4)) { // tasmota switch state - GetNumericResult(vname+4,OPER_EQU,&fvar,0); + GetNumericArgument(vname + 4, OPER_EQU, &fvar, 0); if (fvar<0) { randomSeed(-fvar); - fvar=0; + fvar = 0; } else { - fvar=random(fvar); + fvar = random(fvar); } // skip ] bracket len++; @@ -2444,81 +2429,78 @@ chknext: } break; case 's': - if (!strncmp(vname,"secs",4)) { - fvar=RtcTime.second; + if (!strncmp(vname, "secs", 4)) { + fvar = RtcTime.second; goto exit; } - if (!strncmp(vname,"sw[",3)) { + if (!strncmp(vname, "sw[", 3)) { // tasmota switch state - GetNumericResult(vname+3,OPER_EQU,&fvar,0); - fvar=SwitchLastState((uint32_t)fvar); + GetNumericArgument(vname + 3, OPER_EQU, &fvar, 0); + fvar = SwitchLastState((uint32_t)fvar); // skip ] bracket len++; goto exit; } - if (!strncmp(vname,"stack",5)) { - fvar=GetStack(); + if (!strncmp(vname, "stack", 5)) { + fvar = GetStack(); goto exit; } - if (!strncmp(vname,"slen",4)) { - fvar=strlen(glob_script_mem.script_ram); + if (!strncmp(vname, "slen", 4)) { + fvar = strlen(glob_script_mem.script_ram); goto exit; } - if (!strncmp(vname,"sl(",3)) { - lp+=3; + if (!strncmp(vname, "sl(", 3)) { char str[SCRIPT_MAXSSIZE]; - lp=GetStringResult(lp,OPER_EQU,str,0); + lp = GetStringArgument(lp + 3, OPER_EQU, str, 0); lp++; - len=0; - fvar=strlen(str); + len = 0; + fvar = strlen(str); goto exit; } - if (!strncmp(vname,"sb(",3)) { - lp+=3; + if (!strncmp(vname, "sb(", 3)) { char str[SCRIPT_MAXSSIZE]; - lp=GetStringResult(lp,OPER_EQU,str,0); + lp = GetStringArgument(lp + 3, OPER_EQU, str, 0); SCRIPT_SKIP_SPACES float fvar1; - lp=GetNumericResult(lp,OPER_EQU,&fvar1,0); + lp = GetNumericArgument(lp, OPER_EQU, &fvar1, 0); SCRIPT_SKIP_SPACES float fvar2; - lp=GetNumericResult(lp,OPER_EQU,&fvar2,0); + lp = GetNumericArgument(lp, OPER_EQU, &fvar2, 0); lp++; - len=0; + len = 0; if (fvar1<0) { - fvar1=strlen(str)+fvar1; + fvar1 = strlen(str) + fvar1; } - memcpy(sp,&str[(uint8_t)fvar1],(uint8_t)fvar2); + memcpy(sp, &str[(uint8_t)fvar1], (uint8_t)fvar2); sp[(uint8_t)fvar2] = '\0'; goto strexit; } - if (!strncmp(vname,"st(",3)) { - lp+=3; + if (!strncmp(vname, "st(", 3)) { char str[SCRIPT_MAXSSIZE]; - lp=GetStringResult(lp,OPER_EQU,str,0); + lp = GetStringArgument(lp + 3, OPER_EQU, str, 0); while (*lp==' ') lp++; char token[2]; - token[0]=*lp++; - token[1]=0; + token[0] = *lp++; + token[1] = 0; while (*lp==' ') lp++; - lp=GetNumericResult(lp,OPER_EQU,&fvar,0); + lp = GetNumericArgument(lp, OPER_EQU, &fvar, 0); // skip ) bracket lp++; - len=0; + len = 0; if (sp) { // get stringtoken - char *st=strtok(str,token); + char *st = strtok(str, token); if (!st) { - *sp=0; + *sp = 0; } else { - for (uint8_t cnt=1; cnt<=fvar; cnt++) { + for (uint8_t cnt = 1; cnt<=fvar; cnt++) { if (cnt==fvar) { - strcpy(sp,st); + strcpy(sp, st); break; } - st=strtok(NULL,token); + st = strtok(NULL, token); if (!st) { - *sp=0; + *sp = 0; break; } } @@ -2526,249 +2508,237 @@ chknext: } goto strexit; } - if (!strncmp(vname,"s(",2)) { - lp+=2; - lp=GetNumericResult(lp,OPER_EQU,&fvar,0); - char str[glob_script_mem.max_ssize+1]; - dtostrfd(fvar,glob_script_mem.script_dprec,str); - if (sp) strlcpy(sp,str,glob_script_mem.max_ssize); + if (!strncmp(vname, "s(", 2)) { + lp = GetNumericArgument(lp + 2, OPER_EQU, &fvar, 0); + char str[glob_script_mem.max_ssize + 1]; + dtostrfd(fvar, glob_script_mem.script_dprec, str); + if (sp) strlcpy(sp, str, glob_script_mem.max_ssize); lp++; - len=0; + len = 0; goto strexit; } #if defined(ESP32) && (defined(USE_I2S_AUDIO) || defined(USE_TTGO_WATCH)) - if (!strncmp(vname,"say(",4)) { + if (!strncmp(vname, "say(", 4)) { char text[SCRIPT_MAXSSIZE]; - lp=GetStringResult(lp+4,OPER_EQU,text,0); + lp = GetStringArgument(lp + 4, OPER_EQU, text, 0); Say(text); len++; - len=0; + len = 0; goto exit; } #endif // USE_I2S_AUDIO #ifdef ESP32 - if (!strncmp(vname,"sf(",3)) { - lp+=2; - lp=GetNumericResult(lp,OPER_EQU,&fvar,0); - if (fvar<80) fvar=80; - if (fvar>240) fvar=240; + if (!strncmp(vname, "sf(", 3)) { + lp = GetNumericArgument(lp + 3, OPER_EQU, &fvar, 0); + if (fvar<80) fvar = 80; + if (fvar>240) fvar = 240; setCpuFrequencyMhz(fvar); - fvar=getCpuFrequencyMhz(); + fvar = getCpuFrequencyMhz(); lp++; - len=0; + len = 0; goto exit; } -#endif +#endif //ESP32 #ifdef USE_TTGO_WATCH - if (!strncmp(vname,"slp(",4)) { - lp=GetNumericResult(lp+4,OPER_EQU,&fvar,0); + if (!strncmp(vname, "slp(", 4)) { + lp = GetNumericArgument(lp + 4, OPER_EQU, &fvar, 0); SCRIPT_SKIP_SPACES TTGO_Sleep(fvar); lp++; - len=0; + len = 0; goto exit; } -#endif +#endif //USE_TTGO_WATCH #if defined(USE_TIMERS) && defined(USE_SUNRISE) - if (!strncmp(vname,"sunrise",7)) { - fvar=SunMinutes(0); + if (!strncmp(vname, "sunrise", 7)) { + fvar = SunMinutes(0); goto exit; } - if (!strncmp(vname,"sunset",6)) { - fvar=SunMinutes(1); + if (!strncmp(vname, "sunset", 6)) { + fvar = SunMinutes(1); goto exit; } -#endif +#endif //USE_TIMERS #ifdef USE_SHUTTER - if (!strncmp(vname,"sht[",4)) { - GetNumericResult(vname+4,OPER_EQU,&fvar,0); - uint8_t index=fvar; + if (!strncmp(vname, "sht[", 4)) { + GetNumericArgument(vname + 4, OPER_EQU, &fvar, 0); + uint8_t index = fvar; if (index<=shutters_present) { - fvar=Settings.shutter_position[index-1]; + fvar = Settings.shutter_position[index - 1]; } else { - fvar=-1; + fvar = -1; } - len+=1; + len += 1; goto exit; } -#endif +#endif //USE_SHUTTER #ifdef USE_ANGLE_FUNC - if (!strncmp(vname,"sin(",4)) { - lp+=4; - lp=GetNumericResult(lp,OPER_EQU,&fvar,0); - fvar=sinf(fvar); + if (!strncmp(vname, "sin(", 4)) { + lp = GetNumericArgument(lp + 4, OPER_EQU, &fvar, 0); + fvar = sinf(fvar); lp++; - len=0; + len = 0; goto exit; } - if (!strncmp(vname,"sqrt(",5)) { - lp+=5; - lp=GetNumericResult(lp,OPER_EQU,&fvar,0); - fvar=sqrtf(fvar); + if (!strncmp(vname, "sqrt(", 5)) { + lp = GetNumericArgument(lp + 5, OPER_EQU, &fvar, 0); + fvar = sqrtf(fvar); lp++; - len=0; + len = 0; goto exit; } -#endif +#endif //USE_ANGLE_FUNC #if defined(USE_SML_M) && defined (USE_SML_SCRIPT_CMD) - if (!strncmp(vname,"sml[",4)) { - lp+=4; - lp=GetNumericResult(lp,OPER_EQU,&fvar,0); + if (!strncmp(vname, "sml[", 4)) { + lp = GetNumericArgument(lp + 4, OPER_EQU, &fvar, 0); SCRIPT_SKIP_SPACES - fvar=SML_GetVal(fvar); + fvar = SML_GetVal(fvar); lp++; - len=0; + len = 0; goto exit; } - if (!strncmp(vname,"sml(",4)) { - lp+=4; + if (!strncmp(vname, "sml(", 4)) { float fvar1; - lp=GetNumericResult(lp,OPER_EQU,&fvar1,0); + lp = GetNumericArgument(lp + 4, OPER_EQU, &fvar1, 0); SCRIPT_SKIP_SPACES float fvar2; - lp=GetNumericResult(lp,OPER_EQU,&fvar2,0); + lp = GetNumericArgument(lp, OPER_EQU, &fvar2, 0); SCRIPT_SKIP_SPACES if (fvar2==0) { float fvar3; - lp=GetNumericResult(lp,OPER_EQU,&fvar3,0); - fvar=SML_SetBaud(fvar1,fvar3); + lp = GetNumericArgument(lp, OPER_EQU, &fvar3, 0); + fvar = SML_SetBaud(fvar1, fvar3); } else if (fvar2==1) { char str[SCRIPT_MAXSSIZE]; - lp=GetStringResult(lp,OPER_EQU,str,0); - fvar=SML_Write(fvar1,str); + lp = GetStringArgument(lp, OPER_EQU, str, 0); + fvar = SML_Write(fvar1, str); } else if (fvar2==2) { char str[SCRIPT_MAXSSIZE]; - str[0]=0; - fvar=SML_Read(fvar1,str,SCRIPT_MAXSSIZE); - if (sp) strlcpy(sp,str,glob_script_mem.max_ssize); + str[0] = 0; + fvar = SML_Read(fvar1, str, SCRIPT_MAXSSIZE); + if (sp) strlcpy(sp, str, glob_script_mem.max_ssize); lp++; - len=0; + len = 0; goto strexit; } else { #ifdef ED300L - fvar=SML_Status(fvar1); + fvar = SML_Status(fvar1); #else - fvar=0; -#endif + fvar = 0; +#endif //ED300L } lp++; - len=0; + len = 0; goto exit; } -#endif +#endif //USE_SML_M break; case 't': - if (!strncmp(vname,"time",4)) { - fvar=MinutesPastMidnight(); + if (!strncmp(vname, "time", 4)) { + fvar = MinutesPastMidnight(); goto exit; } - if (!strncmp(vname,"tper",4)) { - fvar=Settings.tele_period; - tind->index=SCRIPT_TELEPERIOD; + if (!strncmp(vname, "tper", 4)) { + fvar = Settings.tele_period; + tind->index = SCRIPT_TELEPERIOD; goto exit_settable; } - if (!strncmp(vname,"tinit",5)) { - if (rules_flag.time_init) { - rules_flag.time_init=0; - fvar=1; - } + if (!strncmp(vname, "tinit", 5)) { + fvar = rules_flag.time_init; goto exit; } - if (!strncmp(vname,"tset",4)) { - if (rules_flag.time_set) { - rules_flag.time_set=0; - fvar=1; - } + if (!strncmp(vname, "tset", 4)) { + fvar = rules_flag.time_set; goto exit; } - if (!strncmp(vname,"tstamp",6)) { - if (sp) strlcpy(sp,GetDateAndTime(DT_LOCAL).c_str(),glob_script_mem.max_ssize); + if (!strncmp(vname, "tstamp", 6)) { + if (sp) strlcpy(sp, GetDateAndTime(DT_LOCAL).c_str(), glob_script_mem.max_ssize); goto strexit; } - if (!strncmp(vname,"topic",5)) { - if (sp) strlcpy(sp,SettingsText(SET_MQTT_TOPIC),glob_script_mem.max_ssize); + if (!strncmp(vname, "topic", 5)) { + if (sp) strlcpy(sp, SettingsText(SET_MQTT_TOPIC), glob_script_mem.max_ssize); goto strexit; } #ifdef USE_SCRIPT_TIMER - if (!strncmp(vname,"ts1(",4)) { - lp=GetNumericResult(lp+4,OPER_EQU,&fvar,0); - if (fvar<10) fvar=10; + if (!strncmp(vname, "ts1(", 4)) { + lp = GetNumericArgument(lp + 4, OPER_EQU, &fvar, 0); + if (fvar<10) fvar = 10; Script_ticker1.attach_ms(fvar, Script_ticker1_end); lp++; - len=0; + len = 0; goto exit; } - if (!strncmp(vname,"ts2(",4)) { - lp=GetNumericResult(lp+4,OPER_EQU,&fvar,0); - if (fvar<10) fvar=10; + if (!strncmp(vname, "ts2(", 4)) { + lp = GetNumericArgument(lp + 4, OPER_EQU, &fvar, 0); + if (fvar<10) fvar = 10; Script_ticker2.attach_ms(fvar, Script_ticker2_end); lp++; - len=0; + len = 0; goto exit; } - if (!strncmp(vname,"ts3(",4)) { - lp=GetNumericResult(lp+4,OPER_EQU,&fvar,0); - if (fvar<10) fvar=10; + if (!strncmp(vname, "ts3(", 4)) { + lp = GetNumericArgument(lp + 4, OPER_EQU, &fvar, 0); + if (fvar<10) fvar = 10; Script_ticker3.attach_ms(fvar, Script_ticker3_end); lp++; - len=0; + len = 0; goto exit; } - if (!strncmp(vname,"ts4(",4)) { - lp=GetNumericResult(lp+4,OPER_EQU,&fvar,0); - if (fvar<10) fvar=10; + if (!strncmp(vname, "ts4(", 4)) { + lp = GetNumericArgument(lp + 4, OPER_EQU, &fvar, 0); + if (fvar<10) fvar = 10; Script_ticker4.attach_ms(fvar, Script_ticker4_end); lp++; - len=0; + len = 0; goto exit; } #endif // USE_SCRIPT_TIMER #ifdef USE_DISPLAY #ifdef USE_TOUCH_BUTTONS - if (!strncmp(vname,"tbut[",5)) { - GetNumericResult(vname+5,OPER_EQU,&fvar,0); - uint8_t index=fvar; - if (index<1 || index>MAXBUTTONS) index=1; + if (!strncmp(vname, "tbut[", 5)) { + GetNumericArgument(vname + 5, OPER_EQU, &fvar, 0); + uint8_t index = fvar; + if (index<1 || index>MAXBUTTONS) index = 1; index--; if (buttons[index]) { - fvar=buttons[index]->vpower.on_off; + fvar = buttons[index]->vpower.on_off; } else { - fvar=-1; + fvar = -1; } - len+=1; + len += 1; goto exit; } -#endif -#endif +#endif //USE_TOUCH_BUTTONS +#endif //USE_DISPLAY break; case 'u': - if (!strncmp(vname,"uptime",6)) { - fvar=MinutesUptime(); + if (!strncmp(vname, "uptime", 6)) { + fvar = MinutesUptime(); goto exit; } - if (!strncmp(vname,"upsecs",6)) { - fvar=uptime; + if (!strncmp(vname, "upsecs", 6)) { + fvar = uptime; goto exit; } - if (!strncmp(vname,"upd[",4)) { + if (!strncmp(vname, "upd[", 4)) { // var was updated struct T_INDEX ind; uint8_t vtype; - isvar(vname+4,&vtype,&ind,0,0,0); + isvar(vname + 4, &vtype, &ind, 0, 0, 0); if (!ind.bits.constant) { if (!ind.bits.changed) { - fvar=0; + fvar = 0; len++; goto exit; } else { - glob_script_mem.type[ind.index].bits.changed=0; - fvar=1; + glob_script_mem.type[ind.index].bits.changed = 0; + fvar = 1; len++; goto exit; } @@ -2779,112 +2749,111 @@ chknext: case 'w': #if defined(ESP32) && defined(USE_WEBCAM) - if (!strncmp(vname,"wc(",3)) { - lp+=3; + if (!strncmp(vname, "wc(", 3)) { float fvar1; - lp=GetNumericResult(lp,OPER_EQU,&fvar1,0); + lp = GetNumericArgument(lp + 3, OPER_EQU, &fvar1, 0); SCRIPT_SKIP_SPACES switch ((uint32)fvar1) { case 0: { float fvar2; - lp=GetNumericResult(lp,OPER_EQU,&fvar2,0); - fvar=WcSetup(fvar2); + lp = GetNumericArgument(lp, OPER_EQU, &fvar2, 0); + fvar = WcSetup(fvar2); } break; case 1: { float fvar2; - lp=GetNumericResult(lp,OPER_EQU,&fvar2,0); - fvar=WcGetFrame(fvar2); + lp = GetNumericArgument(lp, OPER_EQU, &fvar2, 0); + fvar = WcGetFrame(fvar2); } break; case 2: { float fvar2,fvar3; - lp=GetNumericResult(lp,OPER_EQU,&fvar2,0); + lp = GetNumericArgument(lp, OPER_EQU, &fvar2, 0); SCRIPT_SKIP_SPACES - lp=GetNumericResult(lp,OPER_EQU,&fvar3,0); - fvar=WcSetOptions(fvar2,fvar3); + lp = GetNumericArgument(lp, OPER_EQU, &fvar3, 0); + fvar = WcSetOptions(fvar2, fvar3); } break; case 3: - fvar=WcGetWidth(); + fvar = WcGetWidth(); break; case 4: - fvar=WcGetHeight(); + fvar = WcGetHeight(); break; case 5: { float fvar2; - lp=GetNumericResult(lp,OPER_EQU,&fvar2,0); - fvar=WcSetStreamserver(fvar2); + lp = GetNumericArgument(lp, OPER_EQU, &fvar2, 0); + fvar = WcSetStreamserver(fvar2); } break; case 6: { float fvar2; - lp=GetNumericResult(lp,OPER_EQU,&fvar2,0); - fvar=WcSetMotionDetect(fvar2); + lp = GetNumericArgument(lp, OPER_EQU, &fvar2, 0); + fvar = WcSetMotionDetect(fvar2); } break; #ifdef USE_FACE_DETECT case 7: { float fvar2; - lp=GetNumericResult(lp,OPER_EQU,&fvar2,0); - fvar=WcSetFaceDetect(fvar2); + lp = GetNumericArgument(lp, OPER_EQU, &fvar2, 0); + fvar = WcSetFaceDetect(fvar2); } break; -#endif +#endif //USE_FACE_DETECT default: - fvar=0; + fvar = 0; } lp++; - len=0; + len = 0; goto exit; } #endif //ESP32, USE_WEBCAM #if defined(USE_TTGO_WATCH) && defined(USE_BMA423) - if (!strncmp(vname,"wdclk",5)) { - fvar=TTGO_doubleclick(); + if (!strncmp(vname, "wdclk", 5)) { + fvar = TTGO_doubleclick(); goto exit; } - if (!strncmp(vname,"wbut",4)) { - fvar=TTGO_button(); + if (!strncmp(vname, "wbut", 4)) { + fvar = TTGO_button(); goto exit; } #endif // USE_TTGO_WATCH #if defined(USE_TTGO_WATCH) && defined(USE_FT5206) - if (!strncmp(vname,"wtch(",5)) { - lp=GetNumericResult(lp+5,OPER_EQU,&fvar,0); - fvar=Touch_Status(fvar); + if (!strncmp(vname, "wtch(", 5)) { + lp = GetNumericArgument(lp + 5, OPER_EQU, &fvar, 0); + fvar = Touch_Status(fvar); lp++; - len=0; + len = 0; goto exit; } #endif // USE_FT5206 - if (!strncmp(vname,"wday",4)) { - fvar=RtcTime.day_of_week; + if (!strncmp(vname, "wday", 4)) { + fvar = RtcTime.day_of_week; goto exit; } - if (!strncmp(vname,"wific",5)) { + if (!strncmp(vname, "wific", 5)) { if (rules_flag.wifi_connected) { - rules_flag.wifi_connected=0; - fvar=1; + rules_flag.wifi_connected = 0; + fvar = 1; } goto exit; } - if (!strncmp(vname,"wifid",5)) { + if (!strncmp(vname, "wifid", 5)) { if (rules_flag.wifi_disconnected) { - rules_flag.wifi_disconnected=0; - fvar=1; + rules_flag.wifi_disconnected = 0; + fvar = 1; } goto exit; } - if (!strncmp(vname,"wifis",5)) { - fvar=!global_state.wifi_down; + if (!strncmp(vname, "wifis", 5)) { + fvar = !global_state.wifi_down; goto exit; } break; case 'y': - if (!strncmp(vname,"year",4)) { - fvar=RtcTime.year; + if (!strncmp(vname, "year", 4)) { + fvar = RtcTime.year; goto exit; } break; @@ -2894,23 +2863,23 @@ chknext: // nothing valid found notfound: if (fp) *fp=0; - *vtype=VAR_NV; - tind->index=VAR_NV; - glob_script_mem.var_not_found=1; + *vtype = VAR_NV; + tind->index = VAR_NV; + glob_script_mem.var_not_found = 1; return lp; // return constant numbers exit: - if (fp) *fp=fvar; - *vtype=NUM_RES; - tind->bits.constant=1; - tind->bits.is_string=0; - return lp+len; + if (fp) *fp = fvar; + *vtype = NUM_RES; + tind->bits.constant = 1; + tind->bits.is_string = 0; + return lp + len; // return constant strings strexit: - *vtype=STYPE; - tind->bits.constant=1; - tind->bits.is_string=1; - return lp+len; + *vtype = STYPE; + tind->bits.constant = 1; + tind->bits.is_string = 1; + return lp + len; } @@ -2918,113 +2887,113 @@ strexit: char *getop(char *lp, uint8_t *operand) { switch (*lp) { case '=': - if (*(lp+1)=='=') { - *operand=OPER_EQUEQU; - return lp+2; + if (*(lp + 1)=='=') { + *operand = OPER_EQUEQU; + return lp + 2; } else { - *operand=OPER_EQU; - return lp+1; + *operand = OPER_EQU; + return lp + 1; } break; case '+': - if (*(lp+1)=='=') { - *operand=OPER_PLSEQU; - return lp+2; + if (*(lp + 1)=='=') { + *operand = OPER_PLSEQU; + return lp + 2; } else { - *operand=OPER_PLS; - return lp+1; + *operand = OPER_PLS; + return lp + 1; } break; case '-': - if (*(lp+1)=='=') { - *operand=OPER_MINEQU; - return lp+2; + if (*(lp + 1)=='=') { + *operand = OPER_MINEQU; + return lp + 2; } else { - *operand=OPER_MIN; - return lp+1; + *operand = OPER_MIN; + return lp + 1; } break; case '*': - if (*(lp+1)=='=') { - *operand=OPER_MULEQU; - return lp+2; + if (*(lp + 1)=='=') { + *operand = OPER_MULEQU; + return lp + 2; } else { - *operand=OPER_MUL; - return lp+1; + *operand = OPER_MUL; + return lp + 1; } break; case '/': - if (*(lp+1)=='=') { - *operand=OPER_DIVEQU; - return lp+2; + if (*(lp + 1)=='=') { + *operand = OPER_DIVEQU; + return lp + 2; } else { - *operand=OPER_DIV; - return lp+1; + *operand = OPER_DIV; + return lp + 1; } break; case '!': - if (*(lp+1)=='=') { - *operand=OPER_NOTEQU; - return lp+2; + if (*(lp + 1)=='=') { + *operand = OPER_NOTEQU; + return lp + 2; } break; case '>': - if (*(lp+1)=='=') { - *operand=OPER_GRTEQU; - return lp+2; + if (*(lp + 1)=='=') { + *operand = OPER_GRTEQU; + return lp + 2; } else { - *operand=OPER_GRT; - return lp+1; + *operand = OPER_GRT; + return lp + 1; } break; case '<': - if (*(lp+1)=='=') { - *operand=OPER_LOWEQU; - return lp+2; + if (*(lp + 1)=='=') { + *operand = OPER_LOWEQU; + return lp + 2; } else { - *operand=OPER_LOW; - return lp+1; + *operand = OPER_LOW; + return lp + 1; } break; case '%': - if (*(lp+1)=='=') { - *operand=OPER_PERCEQU; - return lp+2; + if (*(lp + 1)=='=') { + *operand = OPER_PERCEQU; + return lp + 2; } else { - *operand=OPER_PERC; - return lp+1; + *operand = OPER_PERC; + return lp + 1; } break; case '^': - if (*(lp+1)=='=') { - *operand=OPER_XOREQU; - return lp+2; + if (*(lp + 1)=='=') { + *operand = OPER_XOREQU; + return lp + 2; } else { - *operand=OPER_XOR; - return lp+1; + *operand = OPER_XOR; + return lp + 1; } break; case '&': - if (*(lp+1)=='=') { - *operand=OPER_ANDEQU; - return lp+2; + if (*(lp + 1)=='=') { + *operand = OPER_ANDEQU; + return lp + 2; } else { - *operand=OPER_AND; - return lp+1; + *operand = OPER_AND; + return lp + 1; } break; case '|': - if (*(lp+1)=='=') { - *operand=OPER_OREQU; - return lp+2; + if (*(lp + 1)=='=') { + *operand = OPER_OREQU; + return lp + 2; } else { - *operand=OPER_OR; - return lp+1; + *operand = OPER_OR; + return lp + 1; } break; } - *operand=0; + *operand = 0; return lp; } @@ -3043,31 +3012,31 @@ uint16_t GetStack(void) { register uint8_t *sp asm("a1"); return (sp - pxTaskGetStackStart(NULL)); } -#endif +#endif //ESP8266 -char *GetStringResult(char *lp,uint8_t lastop,char *cp,JsonObject *jo) { - uint8_t operand=0; +char *GetStringArgument(char *lp, uint8_t lastop, char *cp, JsonObject *jo) { + uint8_t operand = 0; uint8_t vtype; char *slp; struct T_INDEX ind; char str[SCRIPT_MAXSSIZE],str1[SCRIPT_MAXSSIZE]; while (1) { - lp=isvar(lp,&vtype,&ind,0,str1,jo); - if (vtype!=STR_RES && !(vtype&STYPE)) { + lp=isvar(lp, &vtype, &ind, 0, str1, jo); + if (vtype!=STR_RES && !(vtype & STYPE)) { // numeric type - glob_script_mem.glob_error=1; + glob_script_mem.glob_error = 1; return lp; } switch (lastop) { case OPER_EQU: - strlcpy(str,str1,sizeof(str)); + strlcpy(str, str1, sizeof(str)); break; case OPER_PLS: - strncat(str,str1,sizeof(str)-strlen(str1)); + strncat(str, str1, sizeof(str) - strlen(str1)); break; } - slp=lp; - lp=getop(lp,&operand); + slp = lp; + lp = getop(lp, &operand); switch (operand) { case OPER_EQUEQU: case OPER_NOTEQU: @@ -3075,24 +3044,24 @@ char *GetStringResult(char *lp,uint8_t lastop,char *cp,JsonObject *jo) { case OPER_LOWEQU: case OPER_GRT: case OPER_GRTEQU: - lp=slp; - strcpy(cp,str); + lp = slp; + strcpy(cp, str); return lp; break; default: break; } - lastop=operand; + lastop = operand; if (!operand) { - strcpy(cp,str); + strcpy(cp, str); return lp; } } return lp; } -char *GetNumericResult(char *lp,uint8_t lastop,float *fp,JsonObject *jo) { -uint8_t operand=0; +char *GetNumericArgument(char *lp, uint8_t lastop, float *fp, JsonObject *jo) { +uint8_t operand = 0; float fvar1,fvar; char *slp; uint8_t vtype; @@ -3101,50 +3070,50 @@ struct T_INDEX ind; // get 1. value if (*lp=='(') { lp++; - lp=GetNumericResult(lp,OPER_EQU,&fvar1,jo); + lp = GetNumericArgument(lp, OPER_EQU, &fvar1, jo); lp++; //if (*lp==')') lp++; } else { - lp=isvar(lp,&vtype,&ind,&fvar1,0,jo); - if (vtype!=NUM_RES && vtype&STYPE) { + lp = isvar(lp, &vtype, &ind, &fvar1, 0, jo); + if ((vtype!=NUM_RES) && (vtype&STYPE)) { // string type - glob_script_mem.glob_error=1; + glob_script_mem.glob_error = 1; } } switch (lastop) { case OPER_EQU: - fvar=fvar1; + fvar = fvar1; break; case OPER_PLS: - fvar+=fvar1; + fvar += fvar1; break; case OPER_MIN: - fvar-=fvar1; + fvar -= fvar1; break; case OPER_MUL: - fvar*=fvar1; + fvar *= fvar1; break; case OPER_DIV: - fvar/=fvar1; + fvar /= fvar1; break; case OPER_PERC: - fvar=fmodf(fvar,fvar1); + fvar = fmodf(fvar, fvar1); break; case OPER_XOR: - fvar=(uint32_t)fvar^(uint32_t)fvar1; + fvar = (uint32_t)fvar ^ (uint32_t)fvar1; break; case OPER_AND: - fvar=(uint32_t)fvar&(uint32_t)fvar1; + fvar = (uint32_t)fvar & (uint32_t)fvar1; break; case OPER_OR: - fvar=(uint32_t)fvar|(uint32_t)fvar1; + fvar = (uint32_t)fvar | (uint32_t)fvar1; break; default: break; } - slp=lp; - lp=getop(lp,&operand); + slp = lp; + lp = getop(lp, &operand); switch (operand) { case OPER_EQUEQU: case OPER_NOTEQU: @@ -3152,102 +3121,115 @@ struct T_INDEX ind; case OPER_LOWEQU: case OPER_GRT: case OPER_GRTEQU: - lp=slp; - *fp=fvar; + lp = slp; + *fp = fvar; return lp; break; default: break; } - lastop=operand; + lastop = operand; if (!operand) { - *fp=fvar; + *fp = fvar; return lp; } } } -char *ForceStringVar(char *lp,char *dstr) { +char *ForceStringVar(char *lp, char *dstr) { float fvar; - char *slp=lp; - glob_script_mem.glob_error=0; - lp=GetStringResult(lp,OPER_EQU,dstr,0); + char *slp = lp; + glob_script_mem.glob_error = 0; + lp = GetStringArgument(lp, OPER_EQU, dstr, 0); if (glob_script_mem.glob_error) { // mismatch - lp=GetNumericResult(slp,OPER_EQU,&fvar,0); - dtostrfd(fvar,6,dstr); - glob_script_mem.glob_error=0; + lp = GetNumericArgument(slp, OPER_EQU, &fvar, 0); + dtostrfd(fvar, 6, dstr); + glob_script_mem.glob_error = 0; } return lp; } // replace vars in cmd %var% -void Replace_Cmd_Vars(char *srcbuf,uint32_t srcsize, char *dstbuf,uint32_t dstsize) { +void Replace_Cmd_Vars(char *srcbuf, uint32_t srcsize, char *dstbuf, uint32_t dstsize) { char *cp; uint16_t count; uint8_t vtype; - uint8_t dprec=glob_script_mem.script_dprec; + uint8_t dprec = glob_script_mem.script_dprec; float fvar; - cp=srcbuf; + cp = srcbuf; struct T_INDEX ind; char string[SCRIPT_MAXSSIZE]; - dstsize-=2; - for (count=0;count=sizeof(str)) len=len>=sizeof(str); - strlcpy(str,cp,len); + if (len>=sizeof(str)) len = sizeof(str); + strlcpy(str, cp, len); toSLog(str); } void toLogEOL(const char *s1,const char *str) { if (!str) return; - uint8_t index=0; - char *cp=log_data; - strcpy(cp,s1); - cp+=strlen(s1); + uint8_t index = 0; + char *cp = log_data; + strcpy(cp, s1); + cp += strlen(s1); while (*str) { if (*str==SCRIPT_EOL) break; - *cp++=*str++; + *cp++ = *str++; } - *cp=0; + *cp = 0; AddLog(LOG_LEVEL_INFO); } @@ -3297,70 +3279,70 @@ void toSLog(const char *str) { #endif } -char *Evaluate_expression(char *lp,uint8_t and_or, uint8_t *result,JsonObject *jo) { +char *Evaluate_expression(char *lp, uint8_t and_or, uint8_t *result,JsonObject *jo) { float fvar,*dfvar,fvar1; uint8_t numeric; struct T_INDEX ind; - uint8_t vtype=0,lastop; - uint8_t res=0; - char *llp=lp; + uint8_t vtype = 0,lastop; + uint8_t res = 0; + char *llp = lp; char *slp; SCRIPT_SKIP_SPACES if (*lp=='(') { - uint8_t res=0; - uint8_t xand_or=0; + uint8_t res = 0; + uint8_t xand_or = 0; lp++; loop: SCRIPT_SKIP_SPACES - lp=Evaluate_expression(lp,xand_or,&res,jo); + lp = Evaluate_expression(lp, xand_or, &res, jo); if (*lp==')') { lp++; goto exit0; } // check for next and or SCRIPT_SKIP_SPACES - if (!strncmp(lp,"or",2)) { - lp+=2; - xand_or=1; + if (!strncmp(lp, "or", 2)) { + lp += 2; + xand_or = 1; goto loop; - } else if (!strncmp(lp,"and",3)) { - lp+=3; - xand_or=2; + } else if (!strncmp(lp, "and", 3)) { + lp += 3; + xand_or = 2; goto loop; } exit0: if (!and_or) { - *result=res; + *result = res; } else if (and_or==1) { *result|=res; } else { - *result&=res; + *result &= res; } goto exit10; } - llp=lp; + llp = lp; // compare - dfvar=&fvar; - glob_script_mem.glob_error=0; - slp=lp; - numeric=1; - lp=GetNumericResult(lp,OPER_EQU,dfvar,0); + dfvar = &fvar; + glob_script_mem.glob_error = 0; + slp = lp; + numeric = 1; + lp = GetNumericArgument(lp, OPER_EQU, dfvar, 0); if (glob_script_mem.glob_error==1) { // was string, not number char cmpstr[SCRIPT_MAXSSIZE]; - lp=slp; - numeric=0; + lp = slp; + numeric = 0; // get the string - lp=isvar(lp,&vtype,&ind,0,cmpstr,0); - lp=getop(lp,&lastop); + lp = isvar(lp, &vtype, &ind, 0, cmpstr, 0); + lp = getop(lp, &lastop); // compare string char str[SCRIPT_MAXSSIZE]; - lp=GetStringResult(lp,OPER_EQU,str,jo); + lp = GetStringArgument(lp, OPER_EQU, str, jo); if (lastop==OPER_EQUEQU || lastop==OPER_NOTEQU) { - res=strcmp(cmpstr,str); + res = strcmp(cmpstr, str); if (lastop==OPER_EQUEQU) res=!res; goto exit; } @@ -3368,26 +3350,26 @@ exit0: } else { // numeric // evaluate operand - lp=getop(lp,&lastop); - lp=GetNumericResult(lp,OPER_EQU,&fvar1,jo); + lp = getop(lp, &lastop); + lp = GetNumericArgument(lp, OPER_EQU, &fvar1, jo); switch (lastop) { case OPER_EQUEQU: - res=(*dfvar==fvar1); + res = (*dfvar==fvar1); break; case OPER_NOTEQU: - res=(*dfvar!=fvar1); + res = (*dfvar!=fvar1); break; case OPER_LOW: - res=(*dfvarfvar1); + res = (*dfvar>fvar1); break; case OPER_GRTEQU: - res=(*dfvar>=fvar1); + res = (*dfvar>=fvar1); break; default: // error @@ -3396,11 +3378,11 @@ exit0: exit: if (!and_or) { - *result=res; + *result = res; } else if (and_or==1) { - *result|=res; + *result |= res; } else { - *result&=res; + *result &= res; } } @@ -3408,8 +3390,8 @@ exit: exit10: #if IFTHEN_DEBUG>0 char tbuff[128]; - sprintf(tbuff,"p1=%d,p2=%d,cmpres=%d,and_or=%d line: ",(int32_t)*dfvar,(int32_t)fvar1,*result,and_or); - toLogEOL(tbuff,llp); + sprintf(tbuff,"p1=%d,p2=%d,cmpres=%d,and_or=%d line: ", (int32_t)*dfvar, (int32_t)fvar1, *result, and_or); + toLogEOL(tbuff, llp); #endif return lp; } @@ -3420,28 +3402,28 @@ TimerHandle_t beep_th; void StopBeep( TimerHandle_t xTimer ); void StopBeep( TimerHandle_t xTimer ) { - ledcWriteTone(7,0); + ledcWriteTone(7, 0); xTimerStop(xTimer, 0); } void esp32_beep(int32_t freq ,uint32_t len) { if (freq<0) { - ledcSetup(7,500,10); - ledcAttachPin(-freq,7); - ledcWriteTone(7,0); + ledcSetup(7, 500, 10); + ledcAttachPin(-freq, 7); + ledcWriteTone(7, 0); if (!beep_th) { - beep_th = xTimerCreate("beep",100,pdFALSE,( void * ) 0,StopBeep); + beep_th = xTimerCreate("beep", 100, pdFALSE, ( void * ) 0, StopBeep); } } else { if (!beep_th) return; if (!freq) { - ledcWriteTone(7,0); + ledcWriteTone(7, 0); xTimerStop(beep_th, 10); return; } if (len < 10) return; if (xTimerIsTimerActive(beep_th)) return; - ledcWriteTone(7,freq); + ledcWriteTone(7, freq); uint32_t ticks = pdMS_TO_TICKS(len); xTimerChangePeriod( beep_th, ticks, 10); } @@ -3460,13 +3442,13 @@ int16_t Run_Scripter(const char *type, int8_t tlen, char *js) { if (tasm_cmd_activ && tlen>0) return 0; - JsonObject *jo=0; + JsonObject *jo = 0; DynamicJsonBuffer jsonBuffer; // on heap - JsonObject &jobj=jsonBuffer.parseObject(js); + JsonObject &jobj = jsonBuffer.parseObject(js); if (js) { - jo=&jobj; + jo = &jobj; } else { - jo=0; + jo = 0; } return Run_script_sub(type, tlen, jo); @@ -3477,23 +3459,23 @@ int16_t Run_script_sub(const char *type, int8_t tlen, JsonObject *jo) { char *lp_next; int16_t globaindex,saindex; struct T_INDEX ind; - uint8_t operand,lastop,numeric=1,if_state[IF_NEST],if_exe[IF_NEST],if_result[IF_NEST],and_or,ifstck=0; - if_state[ifstck]=0; - if_result[ifstck]=0; - if_exe[ifstck]=1; + uint8_t operand,lastop,numeric = 1,if_state[IF_NEST],if_exe[IF_NEST],if_result[IF_NEST],and_or,ifstck = 0; + if_state[ifstck] = 0; + if_result[ifstck] = 0; + if_exe[ifstck] = 1; char cmpstr[SCRIPT_MAXSSIZE]; - uint8_t check=0; + uint8_t check = 0; if (tlen<0) { - tlen=abs(tlen); - check=1; + tlen = abs(tlen); + check = 1; } float *dfvar,*cv_count,cv_max,cv_inc; char *cv_ptr; - float fvar=0,fvar1,sysvar,swvar; - uint8_t section=0,sysv_type=0,swflg=0; + float fvar = 0,fvar1,sysvar,swvar; + uint8_t section = 0,sysv_type = 0,swflg = 0; - char *lp=glob_script_mem.scriptptr; + char *lp = glob_script_mem.scriptptr; while (1) { // check line @@ -3514,71 +3496,71 @@ int16_t Run_script_sub(const char *type, int8_t tlen, JsonObject *jo) { if (*lp=='#') { return 0; } - glob_script_mem.var_not_found=0; + glob_script_mem.var_not_found = 0; //#if SCRIPT_DEBUG>0 #ifdef IFTHEN_DEBUG char tbuff[128]; - sprintf(tbuff,"stack=%d,exe=%d,state=%d,cmpres=%d line: ",ifstck,if_exe[ifstck],if_state[ifstck],if_result[ifstck]); - toLogEOL(tbuff,lp); -#endif + sprintf(tbuff, "stack=%d,exe=%d,state=%d,cmpres=%d line: ", ifstck, if_exe[ifstck], if_state[ifstck], if_result[ifstck]); + toLogEOL(tbuff, lp); +#endif //IFTHEN_DEBUG //if (if_state[s_ifstck]==3 && if_result[s_ifstck]) goto next_line; //if (if_state[s_ifstck]==2 && !if_result[s_ifstck]) goto next_line; - if (!strncmp(lp,"if",2)) { - lp+=2; + if (!strncmp(lp, "if", 2)) { + lp += 2; if (ifstck=2) { - lp+=5; + if_state[ifstck] = 1; + if_result[ifstck] = 0; + if (ifstck==1) if_exe[ifstck] = 1; + else if_exe[ifstck] = if_exe[ifstck - 1]; + and_or = 0; + } else if (!strncmp(lp, "then", 4) && if_state[ifstck]==1) { + lp += 4; + if_state[ifstck] = 2; + if (if_exe[ifstck - 1]) if_exe[ifstck] = if_result[ifstck]; + } else if (!strncmp(lp, "else", 4) && if_state[ifstck]==2) { + lp += 4; + if_state[ifstck] = 3; + if (if_exe[ifstck - 1]) if_exe[ifstck] = !if_result[ifstck]; + } else if (!strncmp(lp, "endif", 5) && if_state[ifstck]>=2) { + lp += 5; if (ifstck>0) { - if_state[ifstck]=0; + if_state[ifstck] = 0; ifstck--; } goto next_line; - } else if (!strncmp(lp,"or",2) && if_state[ifstck]==1) { - lp+=2; - and_or=1; - } else if (!strncmp(lp,"and",3) && if_state[ifstck]==1) { - lp+=3; - and_or=2; + } else if (!strncmp(lp, "or", 2) && if_state[ifstck]==1) { + lp += 2; + and_or = 1; + } else if (!strncmp(lp, "and", 3) && if_state[ifstck]==1) { + lp += 3; + and_or = 2; } if (*lp=='{' && if_state[ifstck]==1) { - lp+=1; // then - if_state[ifstck]=2; - if (if_exe[ifstck-1]) if_exe[ifstck]=if_result[ifstck]; + lp += 1; // then + if_state[ifstck] = 2; + if (if_exe[ifstck - 1]) if_exe[ifstck]=if_result[ifstck]; } else if (*lp=='{' && if_state[ifstck]==3) { - lp+=1; // after else + lp += 1; // after else //if_state[ifstck]=3; } else if (*lp=='}' && if_state[ifstck]>=2) { lp++; // must check for else - char *slp=lp; - uint8_t iselse=0; - for (uint8_t count=0; count<8;count++) { + char *slp = lp; + uint8_t iselse = 0; + for (uint8_t count = 0; count<8;count++) { if (*lp=='}') { // must be endif break; } - if (!strncmp(lp,"else",4)) { + if (!strncmp(lp, "else", 4)) { // is before else, no endif - if_state[ifstck]=3; + if_state[ifstck] = 3; if (if_exe[ifstck-1]) if_exe[ifstck]=!if_result[ifstck]; - lp+=4; - iselse=1; + lp += 4; + iselse = 1; SCRIPT_SKIP_SPACES if (*lp=='{') lp++; break; @@ -3586,111 +3568,111 @@ int16_t Run_script_sub(const char *type, int8_t tlen, JsonObject *jo) { lp++; } if (!iselse) { - lp=slp; + lp = slp; // endif if (ifstck>0) { - if_state[ifstck]=0; + if_state[ifstck] = 0; ifstck--; } goto next_line; } } - if (!strncmp(lp,"for",3)) { + if (!strncmp(lp, "for", 3)) { // start for next loop, fetch 3 params // simple implementation, zero loop count not supported - lp+=3; + lp += 3; SCRIPT_SKIP_SPACES - lp_next=0; - lp=isvar(lp,&vtype,&ind,0,0,0); + lp_next = 0; + lp = isvar(lp, &vtype, &ind, 0, 0, 0); if ((vtype!=VAR_NV) && (vtype&STYPE)==0) { // numeric var - uint8_t index=glob_script_mem.type[ind.index].index; - cv_count=&glob_script_mem.fvars[index]; + uint8_t index = glob_script_mem.type[ind.index].index; + cv_count = &glob_script_mem.fvars[index]; SCRIPT_SKIP_SPACES - lp=GetNumericResult(lp,OPER_EQU,cv_count,0); + lp = GetNumericArgument(lp, OPER_EQU, cv_count, 0); SCRIPT_SKIP_SPACES - lp=GetNumericResult(lp,OPER_EQU,&cv_max,0); + lp = GetNumericArgument(lp, OPER_EQU, &cv_max, 0); SCRIPT_SKIP_SPACES - lp=GetNumericResult(lp,OPER_EQU,&cv_inc,0); + lp = GetNumericArgument(lp, OPER_EQU, &cv_inc, 0); //SCRIPT_SKIP_EOL - cv_ptr=lp; + cv_ptr = lp; if (*cv_count<=cv_max && cv_inc>0) { // inc loop - floop=1; + floop = 1; } else { // dec loop - floop=2; + floop = 2; if (cv_inc>0) { - floop=1; + floop = 1; } } } else { // error - toLogEOL("for error",lp); + toLogEOL("for error", lp); } - } else if (!strncmp(lp,"next",4)) { - lp_next=lp; + } else if (!strncmp(lp, "next", 4)) { + lp_next = lp; if (floop>0) { // for next loop - *cv_count+=cv_inc; + *cv_count += cv_inc; if (floop==1) { if (*cv_count<=cv_max) { - lp=cv_ptr; + lp = cv_ptr; } else { - lp+=4; - floop=0; + lp += 4; + floop = 0; } } else { if (*cv_count>=cv_max) { - lp=cv_ptr; + lp = cv_ptr; } else { - lp+=4; - floop=0; + lp += 4; + floop = 0; } } } } - if (!strncmp(lp,"switch",6)) { - lp+=6; + if (!strncmp(lp, "switch", 6)) { + lp += 6; SCRIPT_SKIP_SPACES - char *slp=lp; - lp=GetNumericResult(lp,OPER_EQU,&swvar,0); + char *slp = lp; + lp = GetNumericArgument(lp, OPER_EQU, &swvar, 0); if (glob_script_mem.glob_error==1) { // was string, not number - lp=slp; + lp = slp; // get the string - lp=isvar(lp,&vtype,&ind,0,cmpstr,0); - swflg=0x81; + lp = isvar(lp, &vtype, &ind, 0, cmpstr, 0); + swflg = 0x81; } else { - swflg=1; + swflg = 1; } - } else if (!strncmp(lp,"case",4) && swflg>0) { - lp+=4; + } else if (!strncmp(lp, "case", 4) && swflg>0) { + lp += 4; SCRIPT_SKIP_SPACES float cvar; - if (!(swflg&0x80)) { - lp=GetNumericResult(lp,OPER_EQU,&cvar,0); + if (!(swflg & 0x80)) { + lp = GetNumericArgument(lp, OPER_EQU, &cvar, 0); if (swvar!=cvar) { - swflg=2; + swflg = 2; } else { - swflg=1; + swflg = 1; } } else { char str[SCRIPT_MAXSSIZE]; - lp=GetStringResult(lp,OPER_EQU,str,0); - if (!strcmp(cmpstr,str)) { - swflg=0x81; + lp = GetStringArgument(lp, OPER_EQU, str, 0); + if (!strcmp(cmpstr, str)) { + swflg = 0x81; } else { - swflg=0x82; + swflg = 0x82; } } - } else if (!strncmp(lp,"ends",4) && swflg>0) { - lp+=4; - swflg=0; + } else if (!strncmp(lp, "ends", 4) && swflg>0) { + lp += 4; + swflg = 0; } - if ((swflg&3)==2) goto next_line; + if ((swflg & 3)==2) goto next_line; SCRIPT_SKIP_SPACES //SCRIPT_SKIP_EOL @@ -3702,34 +3684,34 @@ int16_t Run_script_sub(const char *type, int8_t tlen, JsonObject *jo) { if (!if_exe[ifstck] && if_state[ifstck]!=1) goto next_line; #ifdef IFTHEN_DEBUG - sprintf(tbuff,"stack=%d,exe=%d,state=%d,cmpres=%d execute line: ",ifstck,if_exe[ifstck],if_state[ifstck],if_result[ifstck]); - toLogEOL(tbuff,lp); -#endif + sprintf(tbuff, "stack=%d,exe=%d,state=%d,cmpres=%d execute line: ", ifstck, if_exe[ifstck], if_state[ifstck], if_result[ifstck]); + toLogEOL(tbuff, lp); +#endif //IFTHEN_DEBUG - if (!strncmp(lp,"break",5)) { - lp+=5; + if (!strncmp(lp, "break", 5)) { + lp += 5; if (floop) { // should break loop if (lp_next) { - lp=lp_next; + lp = lp_next; } - floop=0; + floop = 0; } else { - section=0; + section = 0; } goto next_line; - } else if (!strncmp(lp,"dp",2) && isdigit(*(lp+2))) { - lp+=2; + } else if (!strncmp(lp, "dp", 2) && isdigit(*(lp + 2))) { + lp += 2; // number precision - glob_script_mem.script_dprec=atoi(lp); + glob_script_mem.script_dprec = atoi(lp); goto next_line; } #ifdef USE_DISPLAY - else if (!strncmp(lp,"dt",2)) { + else if (!strncmp(lp, "dt", 2)) { char dstbuf[256]; - lp+=2; + lp += 2; SCRIPT_SKIP_SPACES - Replace_Cmd_Vars(lp,1,dstbuf,sizeof(dstbuf)); + Replace_Cmd_Vars(lp, 1, dstbuf, sizeof(dstbuf)); char *savptr = XdrvMailbox.data; XdrvMailbox.data = dstbuf; XdrvMailbox.data_len = 0; @@ -3737,165 +3719,160 @@ int16_t Run_script_sub(const char *type, int8_t tlen, JsonObject *jo) { XdrvMailbox.data = savptr; goto next_line; } -#endif - else if (!strncmp(lp,"delay(",6)) { - lp+=5; +#endif //USE_DISPLAY + else if (!strncmp(lp, "delay(", 6)) { // delay - lp=GetNumericResult(lp,OPER_EQU,&fvar,0); + lp = GetNumericArgument(lp + 5, OPER_EQU, &fvar, 0); delay(fvar); goto next_line; - } else if (!strncmp(lp,"spinm(",6)) { - lp+=6; + } else if (!strncmp(lp, "spinm(", 6)) { // set pin mode - lp=GetNumericResult(lp,OPER_EQU,&fvar,0); - int8_t pinnr=fvar; + lp = GetNumericArgument(lp + 6, OPER_EQU, &fvar, 0); + int8_t pinnr = fvar; SCRIPT_SKIP_SPACES - uint8_t mode=0; + uint8_t mode = 0; if ((*lp=='I') || (*lp=='O') || (*lp=='P')) { switch (*lp) { case 'I': - mode=0; + mode = 0; break; case 'O': - mode=1; + mode = 1; break; case 'P': - mode=2; + mode = 2; break; } lp++; } else { - lp=GetNumericResult(lp,OPER_EQU,&fvar,0); - mode=fvar; + lp = GetNumericArgument(lp, OPER_EQU, &fvar, 0); + mode = fvar; } uint8_t pm=0; - if (mode==0) pm=INPUT; - if (mode==1) pm=OUTPUT; - if (mode==2) pm=INPUT_PULLUP; - pinMode(pinnr,pm); + if (mode==0) pm = INPUT; + if (mode==1) pm = OUTPUT; + if (mode==2) pm = INPUT_PULLUP; + pinMode(pinnr, pm); goto next_line; - } else if (!strncmp(lp,"spin(",5)) { - lp+=5; + } else if (!strncmp(lp, "spin(", 5)) { // set pin - lp=GetNumericResult(lp,OPER_EQU,&fvar,0); - int8_t pinnr=fvar; + lp = GetNumericArgument(lp + 5, OPER_EQU, &fvar, 0); + int8_t pinnr = fvar; SCRIPT_SKIP_SPACES - lp=GetNumericResult(lp,OPER_EQU,&fvar,0); - int8_t mode=fvar; - digitalWrite(pinnr,mode&1); + lp = GetNumericArgument(lp, OPER_EQU, &fvar, 0); + int8_t mode = fvar; + digitalWrite(pinnr, mode & 1); goto next_line; - } else if (!strncmp(lp,"svars(",5)) { - lp+=5; + } else if (!strncmp(lp, "svars(", 5)) { + lp += 5; // save vars Scripter_save_pvars(); goto next_line; } #ifdef USE_LIGHT #ifdef USE_WS2812 - else if (!strncmp(lp,"ws2812(",7)) { - lp+=7; - lp=isvar(lp,&vtype,&ind,0,0,0); + else if (!strncmp(lp, "ws2812(", 7)) { + lp = isvar(lp + 7, &vtype, &ind, 0, 0, 0); if (vtype!=VAR_NV) { SCRIPT_SKIP_SPACES if (*lp!=')') { - lp=GetNumericResult(lp,OPER_EQU,&fvar,0); + lp = GetNumericArgument(lp, OPER_EQU, &fvar, 0); } else { - fvar=0; + fvar = 0; } // found variable as result - uint8_t index=glob_script_mem.type[ind.index].index; + uint8_t index = glob_script_mem.type[ind.index].index; if ((vtype&STYPE)==0) { // numeric result if (glob_script_mem.type[ind.index].bits.is_filter) { - uint16_t len=0; - float *fa=Get_MFAddr(index,&len,0); + uint16_t len = 0; + float *fa = Get_MFAddr(index, &len, 0); //Serial.printf(">> 2 %d\n",(uint32_t)*fa); - if (fa && len) ws2812_set_array(fa,len,fvar); + if (fa && len) ws2812_set_array(fa, len, fvar); } } } goto next_line; } -#endif -#endif +#endif //USE_WS2812 +#endif //USE_LIGHT #ifdef ESP32 - else if (!strncmp(lp,"beep(",5)) { - lp+=5; - lp=GetNumericResult(lp,OPER_EQU,&fvar,0); + else if (!strncmp(lp, "beep(", 5)) { + lp = GetNumericArgument(lp + 5, OPER_EQU, &fvar, 0); SCRIPT_SKIP_SPACES float fvar1; - lp=GetNumericResult(lp,OPER_EQU,&fvar1,0); - esp32_beep(fvar,fvar1); + lp = GetNumericArgument(lp, OPER_EQU, &fvar1, 0); + esp32_beep(fvar, fvar1); lp++; goto next_line; } -#endif +#endif //ESP32 else if (!strncmp(lp,"=>",2) || !strncmp(lp,"->",2) || !strncmp(lp,"+>",2) || !strncmp(lp,"print",5)) { // execute cmd - uint8_t sflag=0,pflg=0,svmqtt,swll; + uint8_t sflag = 0,pflg = 0,svmqtt,swll; if (*lp=='p') { - pflg=1; - lp+=5; + pflg = 1; + lp += 5; } else { - if (*lp=='-') sflag=1; - if (*lp=='+') sflag=2; - lp+=2; + if (*lp=='-') sflag = 1; + if (*lp=='+') sflag = 2; + lp += 2; } - char *slp=lp; + char *slp = lp; SCRIPT_SKIP_SPACES #define SCRIPT_CMDMEM 512 - char *cmdmem=(char*)malloc(SCRIPT_CMDMEM); + char *cmdmem = (char*)malloc(SCRIPT_CMDMEM); if (cmdmem) { - char *cmd=cmdmem; + char *cmd = cmdmem; uint16_t count; - for (count=0; count",1,jo); - glob_script_mem.scriptptr=svd_sp; + char *svd_sp = glob_script_mem.scriptptr; + strcat(str, "\n#"); + glob_script_mem.scriptptr = str; + Run_script_sub(">", 1, jo); + glob_script_mem.scriptptr = svd_sp; } // check for variable result if (if_state[ifstck]==1) { // evaluate exxpression - lp=Evaluate_expression(lp,and_or,&if_result[ifstck],jo); + lp = Evaluate_expression(lp, and_or, &if_result[ifstck], jo); SCRIPT_SKIP_SPACES if (*lp=='{' && if_state[ifstck]==1) { - lp+=1; // then - if_state[ifstck]=2; - if (if_exe[ifstck-1]) if_exe[ifstck]=if_result[ifstck]; + lp += 1; // then + if_state[ifstck] = 2; + if (if_exe[ifstck - 1]) if_exe[ifstck] = if_result[ifstck]; } goto next_line; } else { - char *vnp=lp; - lp=isvar(lp,&vtype,&ind,&sysvar,0,0); + char *vnp = lp; + lp = isvar(lp, &vtype, &ind, &sysvar, 0, 0); if (vtype!=VAR_NV) { #ifdef USE_SCRIPT_GLOBVARS char varname[16]; - uint32_t vnl=(uint32_t)lp-(uint32)vnp; - strncpy(varname,vnp,vnl); - varname[vnl]=0; -#endif + uint32_t vnl = (uint32_t)lp - (uint32)vnp; + strncpy(varname, vnp, vnl); + varname[vnl] = 0; +#endif //USE_SCRIPT_GLOBVARS // found variable as result - globvindex=ind.index; // save destination var index here - globaindex=last_findex; - uint8_t index=glob_script_mem.type[ind.index].index; + globvindex = ind.index; // save destination var index here + globaindex = last_findex; + uint8_t index = glob_script_mem.type[ind.index].index; if ((vtype&STYPE)==0) { // numeric result if (ind.bits.settable || ind.bits.is_filter) { - dfvar=&sysvar; + dfvar = &sysvar; if (ind.bits.settable) { - sysv_type=ind.index; + sysv_type = ind.index; } else { - sysv_type=0; + sysv_type = 0; } } else { - dfvar=&glob_script_mem.fvars[index]; - sysv_type=0; + dfvar = &glob_script_mem.fvars[index]; + sysv_type = 0; } - numeric=1; - lp=getop(lp,&lastop); - char *slp=lp; - glob_script_mem.glob_error=0; - lp=GetNumericResult(lp,OPER_EQU,&fvar,jo); + numeric = 1; + lp = getop(lp, &lastop); + char *slp = lp; + glob_script_mem.glob_error = 0; + lp = GetNumericArgument(lp, OPER_EQU, &fvar, jo); if (glob_script_mem.glob_error==1) { // mismatch was string, not number // get the string and convert to number - lp=isvar(slp,&vtype,&ind,0,cmpstr,jo); - fvar=CharToFloat(cmpstr); + lp = isvar(slp, &vtype, &ind, 0, cmpstr, jo); + fvar = CharToFloat(cmpstr); } switch (lastop) { case OPER_EQU: @@ -3984,105 +3961,105 @@ int16_t Run_script_sub(const char *type, int8_t tlen, JsonObject *jo) { if (!jo) toLogEOL("var not found: ",lp); goto next_line; } - *dfvar=fvar; + *dfvar = fvar; break; case OPER_PLSEQU: - *dfvar+=fvar; + *dfvar += fvar; break; case OPER_MINEQU: - *dfvar-=fvar; + *dfvar -= fvar; break; case OPER_MULEQU: - *dfvar*=fvar; + *dfvar *= fvar; break; case OPER_DIVEQU: - *dfvar/=fvar; + *dfvar /= fvar; break; case OPER_PERCEQU: - *dfvar=fmodf(*dfvar,fvar); + *dfvar = fmodf(*dfvar, fvar); break; case OPER_ANDEQU: - *dfvar=(uint32_t)*dfvar&(uint32_t)fvar; + *dfvar = (uint32_t)*dfvar & (uint32_t)fvar; break; case OPER_OREQU: - *dfvar=(uint32_t)*dfvar|(uint32_t)fvar; + *dfvar = (uint32_t)*dfvar | (uint32_t)fvar; break; case OPER_XOREQU: - *dfvar=(uint32_t)*dfvar^(uint32_t)fvar; + *dfvar = (uint32_t)*dfvar ^ (uint32_t)fvar; break; default: // error break; } // var was changed - glob_script_mem.type[globvindex].bits.changed=1; + glob_script_mem.type[globvindex].bits.changed = 1; #ifdef USE_SCRIPT_GLOBVARS if (glob_script_mem.type[globvindex].bits.global) { - script_udp_sendvar(varname,dfvar,0); + script_udp_sendvar(varname, dfvar, 0); } -#endif +#endif //USE_SCRIPT_GLOBVARS if (glob_script_mem.type[globvindex].bits.is_filter) { if (globaindex>=0) { - Set_MFVal(glob_script_mem.type[globvindex].index,globaindex,*dfvar); + Set_MFVal(glob_script_mem.type[globvindex].index, globaindex, *dfvar); } else { - Set_MFilter(glob_script_mem.type[globvindex].index,*dfvar); + Set_MFilter(glob_script_mem.type[globvindex].index, *dfvar); } } if (sysv_type) { switch (sysv_type) { case SCRIPT_LOGLEVEL: - glob_script_mem.script_loglevel=*dfvar; + glob_script_mem.script_loglevel = *dfvar; break; case SCRIPT_TELEPERIOD: - if (*dfvar<10) *dfvar=10; - if (*dfvar>300) *dfvar=300; - Settings.tele_period=*dfvar; + if (*dfvar<10) *dfvar = 10; + if (*dfvar>300) *dfvar = 300; + Settings.tele_period = *dfvar; break; case SCRIPT_EVENT_HANDLED: - event_handeled=*dfvar; + event_handeled = *dfvar; break; } - sysv_type=0; + sysv_type = 0; } } else { // string result - numeric=0; - sindex=index; - saindex=last_sindex; + numeric = 0; + sindex = index; + saindex = last_sindex; // string result char str[SCRIPT_MAXSSIZE]; - lp=getop(lp,&lastop); - char *slp=lp; - glob_script_mem.glob_error=0; - lp=GetStringResult(lp,OPER_EQU,str,jo); + lp = getop(lp, &lastop); + char *slp = lp; + glob_script_mem.glob_error = 0; + lp = GetStringArgument(lp, OPER_EQU, str, jo); if (!jo && glob_script_mem.glob_error) { // mismatch - lp=GetNumericResult(slp,OPER_EQU,&fvar,0); - dtostrfd(fvar,6,str); - glob_script_mem.glob_error=0; + lp = GetNumericArgument(slp, OPER_EQU, &fvar, 0); + dtostrfd(fvar, 6, str); + glob_script_mem.glob_error = 0; } if (!glob_script_mem.var_not_found) { // var was changed - glob_script_mem.type[globvindex].bits.changed=1; + glob_script_mem.type[globvindex].bits.changed = 1; #ifdef USE_SCRIPT_GLOBVARS if (glob_script_mem.type[globvindex].bits.global) { - script_udp_sendvar(varname,0,str); + script_udp_sendvar(varname, 0, str); } -#endif +#endif //USE_SCRIPT_GLOBVARS if (saindex>=0) { if (lastop==OPER_EQU) { - strlcpy(glob_script_mem.last_index_string+(saindex*glob_script_mem.max_ssize),str,glob_script_mem.max_ssize); + strlcpy(glob_script_mem.last_index_string + (saindex * glob_script_mem.max_ssize), str, glob_script_mem.max_ssize); } else if (lastop==OPER_PLSEQU) { - strncat(glob_script_mem.last_index_string+(saindex*glob_script_mem.max_ssize),str,glob_script_mem.max_ssize); + strncat(glob_script_mem.last_index_string + (saindex * glob_script_mem.max_ssize), str, glob_script_mem.max_ssize); } - last_sindex=-1; + last_sindex = -1; } else { if (lastop==OPER_EQU) { - strlcpy(glob_script_mem.glob_snp+(sindex*glob_script_mem.max_ssize),str,glob_script_mem.max_ssize); + strlcpy(glob_script_mem.glob_snp + (sindex * glob_script_mem.max_ssize), str, glob_script_mem.max_ssize); } else if (lastop==OPER_PLSEQU) { - strncat(glob_script_mem.glob_snp+(sindex*glob_script_mem.max_ssize),str,glob_script_mem.max_ssize); + strncat(glob_script_mem.glob_snp + (sindex * glob_script_mem.max_ssize), str, glob_script_mem.max_ssize); } } } @@ -4091,7 +4068,7 @@ int16_t Run_script_sub(const char *type, int8_t tlen, JsonObject *jo) { } SCRIPT_SKIP_SPACES if (*lp=='{' && if_state[ifstck]==3) { - lp+=1; // else + lp += 1; // else //if_state[ifstck]=3; } goto next_line; @@ -4102,67 +4079,67 @@ int16_t Run_script_sub(const char *type, int8_t tlen, JsonObject *jo) { if (*lp=='>' && tlen==1) { // called from cmdline lp++; - section=1; - fromscriptcmd=1; + section = 1; + fromscriptcmd = 1; goto startline; } - if (!strncmp(lp,type,tlen)) { + if (!strncmp(lp, type, tlen)) { // found section - section=1; - glob_script_mem.section_ptr=lp; + section = 1; + glob_script_mem.section_ptr = lp; if (check) { return 99; } // check for subroutine - char *ctype=(char*)type; + char *ctype = (char*)type; if (*ctype=='#') { // check for parameter - ctype+=tlen; + ctype += tlen; if (*ctype=='(' && *(lp+tlen)=='(') { float fparam; - numeric=1; - glob_script_mem.glob_error=0; - GetNumericResult((char*)ctype,OPER_EQU,&fparam,0); + numeric = 1; + glob_script_mem.glob_error = 0; + GetNumericArgument((char*)ctype, OPER_EQU, &fparam, 0); if (glob_script_mem.glob_error==1) { // was string, not number - numeric=0; + numeric = 0; // get the string - GetStringResult((char*)ctype+1,OPER_EQU,cmpstr,0); + GetStringArgument((char*)ctype + 1, OPER_EQU, cmpstr, 0); } - lp+=tlen; + lp += tlen; if (*lp=='(') { // fetch destination lp++; - lp=isvar(lp,&vtype,&ind,0,0,0); + lp = isvar(lp, &vtype, &ind, 0, 0, 0); if (vtype!=VAR_NV) { // found variable as result - uint8_t index=glob_script_mem.type[ind.index].index; + uint8_t index = glob_script_mem.type[ind.index].index; if ((vtype&STYPE)==0) { // numeric result - dfvar=&glob_script_mem.fvars[index]; + dfvar = &glob_script_mem.fvars[index]; if (numeric) { - *dfvar=fparam; + *dfvar = fparam; } else { // mismatch - *dfvar=CharToFloat(cmpstr); + *dfvar = CharToFloat(cmpstr); } } else { // string result - sindex=index; + sindex = index; if (!numeric) { - strlcpy(glob_script_mem.glob_snp+(sindex*glob_script_mem.max_ssize),cmpstr,glob_script_mem.max_ssize); + strlcpy(glob_script_mem.glob_snp + (sindex * glob_script_mem.max_ssize), cmpstr, glob_script_mem.max_ssize); } else { // mismatch - dtostrfd(fparam,6,glob_script_mem.glob_snp+(sindex*glob_script_mem.max_ssize)); + dtostrfd(fparam, 6, glob_script_mem.glob_snp + (sindex * glob_script_mem.max_ssize)); } } } } } else { - lp+=tlen; + lp += tlen; if (*ctype=='(' || (*lp!=SCRIPT_EOL && *lp!='?')) { // revert - section=0; + section = 0; } } } @@ -4201,11 +4178,11 @@ void ScripterEvery100ms(void) { if (strlen(mqtt_data)) { mqtt_data[0] = '{'; snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s}"), mqtt_data); - Run_Scripter(">T",2, mqtt_data); + Run_Scripter(">T", 2, mqtt_data); } } if (Settings.rule_enabled) { - if (fast_script==99) Run_Scripter(">F",2,0); + if (fast_script==99) Run_Scripter(">F", 2, 0); } } @@ -4213,48 +4190,48 @@ void ScripterEvery100ms(void) { // can hold 11 floats or floats + strings // should report overflow later void Scripter_save_pvars(void) { - int16_t mlen=0; - float *fp=(float*)glob_script_mem.script_pram; + int16_t mlen = 0; + float *fp = (float*)glob_script_mem.script_pram; mlen+=sizeof(float); - struct T_INDEX *vtp=glob_script_mem.type; - for (uint8_t count=0; countglob_script_mem.script_pram_size) { - vtp[count].bits.is_permanent=0; + vtp[count].bits.is_permanent = 0; return; } while (len--) { - *fp++=*fa++; + *fp++ = *fa++; } } else { - mlen+=sizeof(float); + mlen += sizeof(float); if (mlen>glob_script_mem.script_pram_size) { - vtp[count].bits.is_permanent=0; + vtp[count].bits.is_permanent = 0; return; } - *fp++=glob_script_mem.fvars[index]; + *fp++ = glob_script_mem.fvars[index]; } } } - char *cp=(char*)fp; - for (uint8_t count=0; countglob_script_mem.script_pram_size) { - vtp[count].bits.is_permanent=0; + vtp[count].bits.is_permanent = 0; return; } - strcpy(cp,sp); - cp+=slen+1; + strcpy(cp, sp); + cp += slen + 1; } } } @@ -4351,7 +4328,7 @@ const char HTTP_FORM_SCRIPT1b[] PROGMEM = "});" -#endif +#endif //SCRIPT_STRIP_COMMENTS ""; @@ -4415,38 +4392,38 @@ void script_upload_start(void) { HTTPUpload& upload = Webserver->upload(); if (upload.status == UPLOAD_FILE_START) { //AddLog_P(LOG_LEVEL_INFO, PSTR("HTP: upload start")); - script_ex_ptr=(uint8_t*)glob_script_mem.script_ram; + script_ex_ptr = (uint8_t*)glob_script_mem.script_ram; //AddLog_P2(LOG_LEVEL_INFO, PSTR("HTP: upload file %s, %d"),upload.filename.c_str(),upload.totalSize); - if (strcmp(upload.filename.c_str(),"execute_script")) { - Web.upload_error=1; + if (strcmp(upload.filename.c_str(), "execute_script")) { + Web.upload_error = 1; WSSend(500, CT_PLAIN, F("500: wrong filename")); return; } if (upload.totalSize>=glob_script_mem.script_size) { - Web.upload_error=1; + Web.upload_error = 1; WSSend(500, CT_PLAIN, F("500: file to large")); return; } - uplsize=0; + uplsize = 0; sc_state = bitRead(Settings.rule_enabled, 0); - bitWrite(Settings.rule_enabled,0,0); + bitWrite(Settings.rule_enabled, 0, 0); } else if(upload.status == UPLOAD_FILE_WRITE) { //AddLog_P(LOG_LEVEL_INFO, PSTR("HTP: upload write")); - uint32_t csiz=upload.currentSize; - uint32_t tsiz=glob_script_mem.script_size-1; + uint32_t csiz = upload.currentSize; + uint32_t tsiz = glob_script_mem.script_size - 1; if (uplsizeopen(path, FILE_READ); + File dir = fsp->open(path, FILE_READ); if (dir) { dir.rewindDirectory(); if (strlen(path)>1) { - snprintf_P(npath,sizeof(npath),PSTR("http://%s/upl?download=%s"),WiFi.localIP().toString().c_str(),path); - for (uint8_t cnt=strlen(npath)-1;cnt>0;cnt--) { + snprintf_P(npath, sizeof(npath), PSTR("http://%s/upl?download=%s"), WiFi.localIP().toString().c_str(),path); + for (uint8_t cnt = strlen(npath) - 1; cnt>0; cnt--) { if (npath[cnt]=='/') { - if (npath[cnt-1]=='=') npath[cnt+1]=0; - else npath[cnt]=0; + if (npath[cnt - 1]=='=') npath[cnt + 1] = 0; + else npath[cnt] = 0; break; } } - WSContentSend_P(HTTP_FORM_SDC_DIRd,npath,path,".."); + WSContentSend_P(HTTP_FORM_SDC_DIRd, npath,path, ".."); } char *ep; while (true) { - File entry=dir.openNextFile(); + File entry = dir.openNextFile(); if (!entry) { break; } // esp32 returns path here, shorten to filename - ep=(char*)entry.name(); + ep = (char*)entry.name(); if (*ep=='/') ep++; char *lcp = strrchr(ep,'/'); if (lcp) { - ep=lcp+1; + ep = lcp + 1; } //AddLog_P2(LOG_LEVEL_INFO, PSTR("entry: %s"),ep); - time_t tm=entry.getLastWrite(); + time_t tm = entry.getLastWrite(); char tstr[24]; strftime(tstr, 22, "%d-%m-%Y - %H:%M:%S ", localtime(&tm)); - char *pp=path; - if (!*(pp+1)) pp++; - char *cp=name; + char *pp = path; + if (!*(pp + 1)) pp++; + char *cp = name; // osx formatted disks contain a lot of stuff we dont want if (reject((char*)ep)) goto fclose; - for (uint8_t cnt=0;cnt1) { - strcat(path,"/"); + strcat(path, "/"); } - strcat(path,ep); - ListDir(path,depth+4); - path[plen]=0; + strcat(path, ep); + ListDir(path, depth + 4); + path[plen] = 0; } else { - snprintf_P(npath,sizeof(npath),HTTP_FORM_SDC_HREF,WiFi.localIP().toString().c_str(),pp,ep); - WSContentSend_P(HTTP_FORM_SDC_DIRb,npath,ep,name,tstr,entry.size()); + snprintf_P(npath, sizeof(npath), HTTP_FORM_SDC_HREF, WiFi.localIP().toString().c_str(), pp,ep); + WSContentSend_P(HTTP_FORM_SDC_DIRb, npath, ep, name, tstr, entry.size()); } fclose: entry.close(); @@ -4576,18 +4553,18 @@ void ListDir(char *path, uint8_t depth) { char path[48]; void Script_FileUploadConfiguration(void) { - uint8_t depth=0; + uint8_t depth = 0; - strcpy(path,"/"); + strcpy(path, "/"); if (!HttpCheckPriviledgedAccess()) { return; } if (Webserver->hasArg("download")) { String stmp = Webserver->arg("download"); - char *cp=(char*)stmp.c_str(); + char *cp = (char*)stmp.c_str(); if (DownloadFile(cp)) { // is directory - strcpy(path,cp); + strcpy(path, cp); } } @@ -4598,7 +4575,7 @@ void Script_FileUploadConfiguration(void) { #ifdef SDCARD_DIR WSContentSend_P(HTTP_FORM_SDC_DIRa); if (glob_script_mem.script_sd_found) { - ListDir(path,depth); + ListDir(path, depth); } WSContentSend_P(HTTP_FORM_SDC_DIRc); #endif @@ -4629,15 +4606,15 @@ void script_upload(void) { char npath[48]; #if defined(ESP32) && defined(USE_SCRIPT_FATFS) && USE_SCRIPT_FATFS==-1 //sprintf(npath,"/%s",upload.filename.c_str()); - sprintf(npath,"%s/%s",path,upload.filename.c_str()); + sprintf(npath, "%s/%s", path, upload.filename.c_str()); #else - sprintf(npath,"%s/%s",path,upload.filename.c_str()); + sprintf(npath, "%s/%s", path, upload.filename.c_str()); #endif fsp->remove(npath); - upload_file=fsp->open(npath,FILE_WRITE); - if (!upload_file) Web.upload_error=1; + upload_file = fsp->open(npath, FILE_WRITE); + if (!upload_file) Web.upload_error = 1; } else if(upload.status == UPLOAD_FILE_WRITE) { - if (upload_file) upload_file.write(upload.buf,upload.currentSize); + if (upload_file) upload_file.write(upload.buf, upload.currentSize); } else if(upload.status == UPLOAD_FILE_END) { if (upload_file) upload_file.close(); if (Web.upload_error) { @@ -4658,7 +4635,7 @@ uint8_t DownloadFile(char *file) { return 0; } - download_file=fsp->open(file,FILE_READ); + download_file = fsp->open(file, FILE_READ); if (!download_file) { AddLog_P(LOG_LEVEL_INFO,PSTR("could not open file")); return 0; @@ -4669,20 +4646,20 @@ uint8_t DownloadFile(char *file) { return 1; } - uint32_t flen=download_file.size(); + uint32_t flen = download_file.size(); download_Client = Webserver->client(); Webserver->setContentLength(flen); char attachment[100]; char *cp; - for (uint8_t cnt=strlen(file); cnt>=0; cnt--) { + for (uint8_t cnt = strlen(file); cnt>=0; cnt--) { if (file[cnt]=='/') { - cp=&file[cnt+1]; + cp = &file[cnt + 1]; break; } } - snprintf_P(attachment, sizeof(attachment), PSTR("attachment; filename=%s"),cp); + snprintf_P(attachment, sizeof(attachment), PSTR("attachment; filename=%s"), cp); Webserver->sendHeader(F("Content-Disposition"), attachment); WSSend(200, CT_STREAM, ""); @@ -4690,15 +4667,15 @@ uint8_t DownloadFile(char *file) { uint16_t bread; // transfer is about 150kb/s - uint8_t cnt=0; + uint8_t cnt = 0; while (download_file.available()) { - bread=download_file.read(buff,sizeof(buff)); - uint16_t bw=download_Client.write((const char*)buff,bread); + bread = download_file.read(buff, sizeof(buff)); + uint16_t bw = download_Client.write((const char*)buff, bread); if (!bw) break; cnt++; if (cnt>7) { - cnt=0; - if (glob_script_mem.script_loglevel&0x80) { + cnt = 0; + if (glob_script_mem.script_loglevel & 0x80) { // this indeed multitasks, but is slower 50 kB/s loop(); } @@ -4747,8 +4724,8 @@ void HandleScriptConfiguration(void) { #ifdef xSCRIPT_STRIP_COMMENTS - uint16_t ssize=glob_script_mem.script_size; - if (bitRead(Settings.rule_enabled, 1)) ssize*=2; + uint16_t ssize = glob_script_mem.script_size; + if (bitRead(Settings.rule_enabled, 1)) ssize *= 2; WSContentSend_P(HTTP_FORM_SCRIPT1,1,1,bitRead(Settings.rule_enabled,0) ? " checked" : "",ssize); #else WSContentSend_P(HTTP_FORM_SCRIPT1,1,1,bitRead(Settings.rule_enabled,0) ? " checked" : "",glob_script_mem.script_size); @@ -4763,10 +4740,10 @@ void HandleScriptConfiguration(void) { #ifdef USE_SCRIPT_FATFS if (glob_script_mem.script_sd_found) { WSContentSend_P(HTTP_FORM_SCRIPT1d); - if (glob_script_mem.flink[0][0]) WSContentSend_P(HTTP_FORM_SCRIPT1c,1,glob_script_mem.flink[0]); - if (glob_script_mem.flink[1][0]) WSContentSend_P(HTTP_FORM_SCRIPT1c,2,glob_script_mem.flink[1]); + if (glob_script_mem.flink[0][0]) WSContentSend_P(HTTP_FORM_SCRIPT1c, 1, glob_script_mem.flink[0]); + if (glob_script_mem.flink[1][0]) WSContentSend_P(HTTP_FORM_SCRIPT1c, 2, glob_script_mem.flink[1]); } -#endif +#endif //USE_SCRIPT_FATFS WSContentSend_P(HTTP_SCRIPT_FORM_END); WSContentSpaceButton(BUTTON_CONFIGURATION); @@ -4777,22 +4754,22 @@ void SaveScript(void) { #ifdef EEP_SCRIPT_SIZE if (glob_script_mem.flags&1) { - EEP_WRITE(0,EEP_SCRIPT_SIZE,glob_script_mem.script_ram); + EEP_WRITE(0, EEP_SCRIPT_SIZE, glob_script_mem.script_ram); } #endif // EEP_SCRIPT_SIZE #ifdef USE_SCRIPT_FATFS - if (glob_script_mem.flags&1) { + if (glob_script_mem.flags & 1) { fsp->remove(FAT_SCRIPT_NAME); - File file=fsp->open(FAT_SCRIPT_NAME,FILE_WRITE); - file.write((const uint8_t*)glob_script_mem.script_ram,FAT_SCRIPT_SIZE); + File file = fsp->open(FAT_SCRIPT_NAME, FILE_WRITE); + file.write((const uint8_t*)glob_script_mem.script_ram, FAT_SCRIPT_SIZE); file.close(); } #endif // USE_SCRIPT_FATFS #ifdef LITTLEFS_SCRIPT_SIZE if (glob_script_mem.flags&1) { - SaveFile("/script.txt",(uint8_t*)glob_script_mem.script_ram,LITTLEFS_SCRIPT_SIZE); + SaveFile("/script.txt", (uint8_t*)glob_script_mem.script_ram, LITTLEFS_SCRIPT_SIZE); } #endif // LITTLEFS_SCRIPT_SIZE } @@ -4800,9 +4777,9 @@ void SaveScript(void) { void ScriptSaveSettings(void) { if (Webserver->hasArg("c1")) { - bitWrite(Settings.rule_enabled,0,1); + bitWrite(Settings.rule_enabled, 0, 1); } else { - bitWrite(Settings.rule_enabled,0,0); + bitWrite(Settings.rule_enabled, 0, 0); } @@ -4810,42 +4787,42 @@ void ScriptSaveSettings(void) { if (*str.c_str()) { - str.replace("\r\n","\n"); - str.replace("\r","\n"); + str.replace("\r\n", "\n"); + str.replace("\r", "\n"); #ifdef xSCRIPT_STRIP_COMMENTS if (bitRead(Settings.rule_enabled, 1)) { - char *sp=(char*)str.c_str(); - char *sp1=sp; - char *dp=sp; - uint8_t flg=0; + char *sp = (char*)str.c_str(); + char *sp1 = sp; + char *dp = sp; + uint8_t flg = 0; while (*sp) { while (*sp==' ') sp++; - sp1=sp; - sp=strchr(sp,'\n'); + sp1 = sp; + sp = strchr(sp,'\n'); if (!sp) { - flg=1; + flg = 1; } else { - *sp=0; + *sp = 0; } if (*sp1!=';') { - uint8_t slen=strlen(sp1); + uint8_t slen = strlen(sp1); if (slen) { - strcpy(dp,sp1); - dp+=slen; - *dp++='\n'; + strcpy(dp, sp1); + dp += slen; + *dp++ = '\n'; } } if (flg) { - *dp=0; + *dp = 0; break; } sp++; } } -#endif +#endif //xSCRIPT_STRIP_COMMENTS - strlcpy(glob_script_mem.script_ram,str.c_str(), glob_script_mem.script_size); + strlcpy(glob_script_mem.script_ram, str.c_str(), glob_script_mem.script_size); if (glob_script_mem.script_ram[0]!='>' && glob_script_mem.script_ram[1]!='D') { AddLog_P2(LOG_LEVEL_INFO, PSTR("script error: must start with >D")); @@ -4863,8 +4840,8 @@ void SaveScriptEnd(void) { if (glob_script_mem.script_mem) { Scripter_save_pvars(); free(glob_script_mem.script_mem); - glob_script_mem.script_mem=0; - glob_script_mem.script_mem_size=0; + glob_script_mem.script_mem = 0; + glob_script_mem.script_mem_size = 0; } #ifdef USE_SCRIPT_COMPRESSION @@ -4879,20 +4856,20 @@ void SaveScriptEnd(void) { #endif // USE_SCRIPT_COMPRESSION if (bitRead(Settings.rule_enabled, 0)) { - int16_t res=Init_Scripter(); + int16_t res = Init_Scripter(); if (res) { AddLog_P2(LOG_LEVEL_INFO, PSTR("script init error: %d"), res); return; } - Run_Scripter(">B\n",3,0); - Run_Scripter(">BS",3,0); + Run_Scripter(">B\n", 3, 0); + Run_Scripter(">BS", 3, 0); - fast_script=Run_Scripter(">F",-2,0); + fast_script = Run_Scripter(">F", -2, 0); } } -#endif +#endif // USE_WEBSERVER #if defined(USE_SCRIPT_HUE) && defined(USE_WEBSERVER) && defined(USE_EMULATION) && defined(USE_EMULATION_HUE) && defined(USE_LIGHT) @@ -5026,22 +5003,22 @@ break; void Script_HueStatus(String *response, uint16_t hue_devs) { if (hue_script[hue_devs].type=='p') { - *response+=FPSTR(SCRIPT_HUE_LIGHTS_STATUS_JSON2); - response->replace("{j1",hue_script[hue_devs].name); + *response += FPSTR(SCRIPT_HUE_LIGHTS_STATUS_JSON2); + response->replace("{j1", hue_script[hue_devs].name); response->replace("{j2", GetHueDeviceId(hue_devs)); - uint8_t pwr=glob_script_mem.fvars[hue_script[hue_devs].index[0]-1]; + uint8_t pwr = glob_script_mem.fvars[hue_script[hue_devs].index[0] - 1]; response->replace("{state}", (pwr ? "true" : "false")); return; } - *response+=FPSTR(SCRIPT_HUE_LIGHTS_STATUS_JSON1); - uint8_t pwr=glob_script_mem.fvars[hue_script[hue_devs].index[0]-1]; + *response += FPSTR(SCRIPT_HUE_LIGHTS_STATUS_JSON1); + uint8_t pwr = glob_script_mem.fvars[hue_script[hue_devs].index[0] - 1]; response->replace("{state}", (pwr ? "true" : "false")); String light_status = ""; if (hue_script[hue_devs].index[1]>0) { // bri light_status += "\"bri\":"; - uint32_t bri=glob_script_mem.fvars[hue_script[hue_devs].index[1]-1]; + uint32_t bri = glob_script_mem.fvars[hue_script[hue_devs].index[1] - 1]; if (bri > 254) bri = 254; if (bri < 1) bri = 1; light_status += String(bri); @@ -5049,7 +5026,7 @@ void Script_HueStatus(String *response, uint16_t hue_devs) { } if (hue_script[hue_devs].index[2]>0) { // hue - uint32_t hue=glob_script_mem.fvars[hue_script[hue_devs].index[2]-1]; + uint32_t hue = glob_script_mem.fvars[hue_script[hue_devs].index[2] - 1]; //hue = changeUIntScale(hue, 0, 359, 0, 65535); light_status += "\"hue\":"; light_status += String(hue); @@ -5057,7 +5034,7 @@ void Script_HueStatus(String *response, uint16_t hue_devs) { } if (hue_script[hue_devs].index[3]>0) { // sat - uint32_t sat=glob_script_mem.fvars[hue_script[hue_devs].index[3]-1] ; + uint32_t sat = glob_script_mem.fvars[hue_script[hue_devs].index[3] - 1] ; if (sat > 254) sat = 254; if (sat < 1) sat = 1; light_status += "\"sat\":"; @@ -5066,7 +5043,7 @@ void Script_HueStatus(String *response, uint16_t hue_devs) { } if (hue_script[hue_devs].index[4]>0) { // ct - uint32_t ct=glob_script_mem.fvars[hue_script[hue_devs].index[4]-1]; + uint32_t ct = glob_script_mem.fvars[hue_script[hue_devs].index[4] - 1]; light_status += "\"ct\":"; light_status += String(ct); light_status += ","; @@ -5101,7 +5078,7 @@ void Script_HueStatus(String *response, uint16_t hue_devs) { } response->replace("{light_status}", light_status); - response->replace("{j1",hue_script[hue_devs].name); + response->replace("{j1", hue_script[hue_devs].name); response->replace("{j2", GetHueDeviceId(hue_devs)); } @@ -5109,14 +5086,14 @@ void Script_HueStatus(String *response, uint16_t hue_devs) { void Script_Check_Hue(String *response) { if (!bitRead(Settings.rule_enabled, 0)) return; - uint8_t hue_script_found=Run_Scripter(">H",-2,0); + uint8_t hue_script_found = Run_Scripter(">H", -2, 0); if (hue_script_found!=99) return; char tmp[256]; - uint8_t hue_devs=0; - uint8_t vindex=0; + uint8_t hue_devs = 0; + uint8_t vindex = 0; char *cp; - char *lp=glob_script_mem.section_ptr+2; + char *lp = glob_script_mem.section_ptr + 2; while (lp) { SCRIPT_SKIP_SPACES while (*lp==SCRIPT_EOL) { @@ -5127,69 +5104,69 @@ void Script_Check_Hue(String *response) { } if (*lp!=';') { // check this line - Replace_Cmd_Vars(lp,1,tmp,sizeof(tmp)); + Replace_Cmd_Vars(lp, 1, tmp, sizeof(tmp)); // check for hue defintions // NAME, TYPE , vars - cp=tmp; - cp=strchr(cp,','); + cp = tmp; + cp = strchr(cp,','); if (!cp) break; - *cp=0; + *cp = 0; // copy name - strlcpy(hue_script[hue_devs].name,tmp,HUE_DEV_NSIZE); + strlcpy(hue_script[hue_devs].name, tmp, HUE_DEV_NSIZE); cp++; while (*cp==' ') cp++; // get type - hue_script[hue_devs].type=*cp; + hue_script[hue_devs].type = *cp; - for (vindex=0;vindex0) *response+=",\""; - else *response+="\""; + if (hue_devs>0) *response += ",\""; + else *response += "\""; } - *response+=String(EncodeLightId(hue_devs+devices_present+1))+"\":"; - Script_HueStatus(response,hue_devs); + *response += String(EncodeLightId(hue_devs + devices_present + 1))+"\":"; + Script_HueStatus(response, hue_devs); //AddLog_P2(LOG_LEVEL_INFO, PSTR("Hue: %s - %d "),response->c_str(), hue_devs); } @@ -5252,13 +5229,13 @@ void Script_Handle_Hue(String *path) { bool resp = false; uint8_t device = DecodeLightId(atoi(path->c_str())); - uint8_t index = device-devices_present-1; + uint8_t index = device - devices_present - 1; if (Webserver->args()) { response = "["; StaticJsonBuffer<400> jsonBuffer; - JsonObject &hue_json = jsonBuffer.parseObject(Webserver->arg((Webserver->args())-1)); + JsonObject &hue_json = jsonBuffer.parseObject(Webserver->arg((Webserver->args()) - 1)); if (hue_json.containsKey("on")) { response += FPSTR(sHUE_LIGHT_RESPONSE_JSON); @@ -5267,26 +5244,26 @@ void Script_Handle_Hue(String *path) { bool on = hue_json["on"]; if (on==false) { - glob_script_mem.fvars[hue_script[index].index[0]-1]=0; + glob_script_mem.fvars[hue_script[index].index[0] - 1] = 0; response.replace("{re", "false"); } else { - glob_script_mem.fvars[hue_script[index].index[0]-1]=1; + glob_script_mem.fvars[hue_script[index].index[0] - 1] = 1; response.replace("{re", "true"); } - glob_script_mem.type[hue_script[index].vindex[0]].bits.changed=1; + glob_script_mem.type[hue_script[index].vindex[0]].bits.changed = 1; resp = true; } if (hue_json.containsKey("bri")) { // Brightness is a scale from 1 (the minimum the light is capable of) to 254 (the maximum). Note: a brightness of 1 is not off. tmp = hue_json["bri"]; - bri=tmp; + bri = tmp; if (254 <= bri) { bri = 255; } if (resp) { response += ","; } response += FPSTR(sHUE_LIGHT_RESPONSE_JSON); response.replace("{id", String(EncodeLightId(device))); response.replace("{cm", "bri"); response.replace("{re", String(tmp)); - glob_script_mem.fvars[hue_script[index].index[1]-1]=bri; - glob_script_mem.type[hue_script[index].vindex[1]].bits.changed=1; + glob_script_mem.fvars[hue_script[index].index[1] - 1] = bri; + glob_script_mem.type[hue_script[index].vindex[1]].bits.changed = 1; resp = true; } if (hue_json.containsKey("xy")) { // Saturation of the light. 254 is the most saturated (colored) and 0 is the least saturated (white). @@ -5303,10 +5280,10 @@ void Script_Handle_Hue(String *path) { response.replace("{id", String(device)); response.replace("{cm", "xy"); response.replace("{re", "[" + x_str + "," + y_str + "]"); - glob_script_mem.fvars[hue_script[index].index[2]-1]=hue; - glob_script_mem.type[hue_script[index].vindex[2]].bits.changed=1; - glob_script_mem.fvars[hue_script[index].index[3]-1]=sat; - glob_script_mem.type[hue_script[index].vindex[3]].bits.changed=1; + glob_script_mem.fvars[hue_script[index].index[2]-1] = hue; + glob_script_mem.type[hue_script[index].vindex[2]].bits.changed = 1; + glob_script_mem.fvars[hue_script[index].index[3]-1] = sat; + glob_script_mem.type[hue_script[index].vindex[3]].bits.changed = 1; resp = true; } @@ -5314,27 +5291,27 @@ void Script_Handle_Hue(String *path) { tmp = hue_json["hue"]; //hue = changeUIntScale(tmp, 0, 65535, 0, 359); //tmp = changeUIntScale(hue, 0, 359, 0, 65535); - hue=tmp; + hue = tmp; if (resp) { response += ","; } response += FPSTR(sHUE_LIGHT_RESPONSE_JSON); response.replace("{id", String(EncodeLightId(device))); response.replace("{cm", "hue"); response.replace("{re", String(tmp)); - glob_script_mem.fvars[hue_script[index].index[2]-1]=hue; - glob_script_mem.type[hue_script[index].vindex[2]].bits.changed=1; + glob_script_mem.fvars[hue_script[index].index[2] - 1] = hue; + glob_script_mem.type[hue_script[index].vindex[2]].bits.changed = 1; resp = true; } if (hue_json.containsKey("sat")) { // Saturation of the light. 254 is the most saturated (colored) and 0 is the least saturated (white). tmp = hue_json["sat"]; - sat=tmp; + sat = tmp; if (254 <= sat) { sat = 255; } if (resp) { response += ","; } response += FPSTR(sHUE_LIGHT_RESPONSE_JSON); response.replace("{id", String(EncodeLightId(device))); response.replace("{cm", "sat"); response.replace("{re", String(tmp)); - glob_script_mem.fvars[hue_script[index].index[3]-1]=sat; - glob_script_mem.type[hue_script[index].vindex[3]].bits.changed=1; + glob_script_mem.fvars[hue_script[index].index[3] - 1] = sat; + glob_script_mem.type[hue_script[index].vindex[3]].bits.changed = 1; resp = true; } if (hue_json.containsKey("ct")) { // Color temperature 153 (Cold) to 500 (Warm) @@ -5344,8 +5321,8 @@ void Script_Handle_Hue(String *path) { response.replace("{id", String(EncodeLightId(device))); response.replace("{cm", "ct"); response.replace("{re", String(ct)); - glob_script_mem.fvars[hue_script[index].index[4]-1]=ct; - glob_script_mem.type[hue_script[index].vindex[4]].bits.changed=1; + glob_script_mem.fvars[hue_script[index].index[4] - 1] = ct; + glob_script_mem.type[hue_script[index].vindex[4]].bits.changed = 1; resp = true; } response += "]"; @@ -5356,7 +5333,7 @@ void Script_Handle_Hue(String *path) { AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_HTTP D_HUE " Result (%s)"), response.c_str()); WSSend(code, CT_JSON, response); if (resp) { - Run_Scripter(">E",2,0); + Run_Scripter(">E", 2, 0); } } #endif // hue interface @@ -5369,30 +5346,30 @@ bool Script_SubCmd(void) { if (tasm_cmd_activ) return false; char command[CMDSZ]; - strlcpy(command,XdrvMailbox.topic,CMDSZ); - uint32_t pl=XdrvMailbox.payload; + strlcpy(command, XdrvMailbox.topic, CMDSZ); + uint32_t pl = XdrvMailbox.payload; char pld[64]; - strlcpy(pld,XdrvMailbox.data,sizeof(pld)); + strlcpy(pld, XdrvMailbox.data, sizeof(pld)); char cmdbuff[128]; - char *cp=cmdbuff; - *cp++='#'; - strcpy(cp,XdrvMailbox.topic); - uint8_t tlen=strlen(XdrvMailbox.topic); - cp+=tlen; + char *cp = cmdbuff; + *cp++ = '#'; + strcpy(cp, XdrvMailbox.topic); + uint8_t tlen = strlen(XdrvMailbox.topic); + cp += tlen; if (XdrvMailbox.index > 0) { - *cp++=XdrvMailbox.index|0x30; + *cp++ = XdrvMailbox.index | 0x30; tlen++; } if ((XdrvMailbox.payload>0) || (XdrvMailbox.data_len>0)) { - *cp++='('; - strncpy(cp,XdrvMailbox.data,XdrvMailbox.data_len); - cp+=XdrvMailbox.data_len; - *cp++=')'; - *cp=0; + *cp++ = '('; + strncpy(cp, XdrvMailbox.data,XdrvMailbox.data_len); + cp += XdrvMailbox.data_len; + *cp++ = ')'; + *cp = 0; } //toLog(cmdbuff); - uint32_t res=Run_Scripter(cmdbuff,tlen+1,0); + uint32_t res = Run_Scripter(cmdbuff, tlen + 1, 0); //AddLog_P2(LOG_LEVEL_INFO,">>%d",res); if (res) return false; else { @@ -5404,14 +5381,14 @@ bool Script_SubCmd(void) { } return true; } -#endif +#endif //USE_SCRIPT_SUB_COMMAND void execute_script(char *script) { - char *svd_sp=glob_script_mem.scriptptr; - strcat(script,"\n#"); - glob_script_mem.scriptptr=script; - Run_Scripter(">",1,0); - glob_script_mem.scriptptr=svd_sp; + char *svd_sp = glob_script_mem.scriptptr; + strcat(script, "\n#"); + glob_script_mem.scriptptr = script; + Run_Scripter(">", 1, 0); + glob_script_mem.scriptptr = svd_sp; } #define D_CMND_SCRIPT "Script" #define D_CMND_SUBSCRIBE "Subscribe" @@ -5441,44 +5418,44 @@ bool ScriptCommand(void) { break; #ifdef xSCRIPT_STRIP_COMMENTS case 2: - bitWrite(Settings.rule_enabled, 1,0); + bitWrite(Settings.rule_enabled, 1, 0); break; case 3: - bitWrite(Settings.rule_enabled, 1,1); + bitWrite(Settings.rule_enabled, 1, 1); break; -#endif +#endif //xSCRIPT_STRIP_COMMENTS } } else { if ('>' == XdrvMailbox.data[0]) { // execute script - snprintf_P (mqtt_data, sizeof(mqtt_data), PSTR("{\"%s\":\"%s\"}"),command,XdrvMailbox.data); + snprintf_P (mqtt_data, sizeof(mqtt_data), PSTR("{\"%s\":\"%s\"}"), command,XdrvMailbox.data); if (bitRead(Settings.rule_enabled, 0)) { - for (uint8_t count=0; count, [, ] String result = ScriptSubscribe(XdrvMailbox.data, XdrvMailbox.data_len); @@ -5486,7 +5463,7 @@ bool ScriptCommand(void) { } else if (CMND_UNSUBSCRIBE == command_code) { //MQTT Un-subscribe command. UnSubscribe String result = ScriptUnsubscribe(XdrvMailbox.data, XdrvMailbox.data_len); Response_P(S_JSON_COMMAND_SVALUE, command, result.c_str()); -#endif //SUPPORT_MQTT_EVENT +#endif //SUPPORT_MQTT_EVENT } return serviced; } @@ -5508,7 +5485,7 @@ void dateTime(uint16_t* date, uint16_t* time) { *time = xFAT_TIME(RtcTime.hour,RtcTime.minute,RtcTime.second); } -#endif +#endif //USE_SCRIPT_FATFS @@ -5519,7 +5496,7 @@ void dateTime(uint16_t* date, uint16_t* time) { #endif #ifndef MQTT_EVENT_JSIZE #define MQTT_EVENT_JSIZE 400 -#endif +#endif //SUPPORT_MQTT_EVENT /********************************************************************************************/ /* @@ -5565,21 +5542,21 @@ bool ScriptMqttData(void) if ((dot = key1.indexOf('.')) > 0) { key2 = key1.substring(dot+1); key1 = key1.substring(0, dot); - lkey=key2; + lkey = key2; if (!jsonData[key1][key2].success()) break; //Failed to get the key/value, ignore this message. value = (const char *)jsonData[key1][key2]; } else { if (!jsonData[key1].success()) break; value = (const char *)jsonData[key1]; - lkey=key1; + lkey = key1; } } value.trim(); char sbuffer[128]; - if (!strncmp(lkey.c_str(),"Epoch",5)) { - uint32_t ep=atoi(value.c_str())-(uint32_t)EPOCH_OFFSET; - snprintf_P(sbuffer, sizeof(sbuffer), PSTR(">%s=%d\n"), event_item.Event.c_str(),ep); + if (!strncmp(lkey.c_str(), "Epoch", 5)) { + uint32_t ep = atoi(value.c_str()) - (uint32_t)EPOCH_OFFSET; + snprintf_P(sbuffer, sizeof(sbuffer), PSTR(">%s=%d\n"), event_item.Event.c_str(), ep); } else { snprintf_P(sbuffer, sizeof(sbuffer), PSTR(">%s=\"%s\"\n"), event_item.Event.c_str(), value.c_str()); } @@ -5611,7 +5588,7 @@ String ScriptSubscribe(const char *data, int data_len) MQTT_Subscription subscription_item; String events; if (data_len > 0) { - char parameters[data_len+1]; + char parameters[data_len + 1]; memcpy(parameters, data, data_len); parameters[data_len] = '\0'; String event_name, topic, key; @@ -5632,7 +5609,7 @@ String ScriptSubscribe(const char *data, int data_len) //event_name.toUpperCase(); if (event_name.length() > 0 && topic.length() > 0) { //Search all subscriptions - for (uint32_t index=0; index < subscriptions.size(); index++) { + for (uint32_t index = 0; index < subscriptions.size(); index++) { if (subscriptions.get(index).Event.equals(event_name)) { //If find exists one, remove it. String stopic = subscriptions.get(index).Topic + "/#"; @@ -5665,7 +5642,7 @@ String ScriptSubscribe(const char *data, int data_len) } } else { //If did not specify the event name, list all subscribed event - for (uint32_t index=0; index < subscriptions.size(); index++) { + for (uint32_t index = 0; index < subscriptions.size(); index++) { subscription_item = subscriptions.get(index); events.concat(subscription_item.Event + "," + subscription_item.Topic + (subscription_item.Key.length()>0 ? "," : "") @@ -5795,15 +5772,15 @@ void ScriptGetSDCard(void) { if (!HttpCheckPriviledgedAccess()) { return; } String stmp = Webserver->uri(); - char *cp=strstr_P(stmp.c_str(),PSTR("/sdc/")); + char *cp = strstr_P(stmp.c_str(), PSTR("/sdc/")); // if (cp) Serial.printf(">>>%s\n",cp); if (cp) { #ifdef ESP32 - cp+=4; + cp += 4; #else - cp+=5; + cp += 5; #endif - if (strstr_P(cp,PSTR("scrdmp.bmp"))) { + if (strstr_P(cp, PSTR("scrdmp.bmp"))) { SendFile(cp); return; } else { @@ -5821,34 +5798,34 @@ extern uint8_t *buffer; void SendFile(char *fname) { char buff[512]; const char *mime; - uint8_t sflg=0; - char *jpg=strstr(fname,".jpg"); + uint8_t sflg = 0; + char *jpg = strstr(fname,".jpg"); if (jpg) { - mime="image/jpeg"; + mime = "image/jpeg"; } #ifdef USE_DISPLAY_DUMP - char *sbmp=strstr_P(fname,PSTR("scrdmp.bmp")); + char *sbmp = strstr_P(fname, PSTR("scrdmp.bmp")); if (sbmp) { - mime="image/bmp"; - sflg=1; + mime = "image/bmp"; + sflg = 1; } #endif // USE_DISPLAY_DUMP - char *bmp=strstr(fname,".bmp"); + char *bmp = strstr(fname, ".bmp"); if (bmp) { - mime="image/bmp"; + mime = "image/bmp"; } - char *html=strstr(fname,".html"); + char *html = strstr(fname, ".html"); if (html) { - mime="text/html"; + mime = "text/html"; } - char *txt=strstr(fname,".txt"); + char *txt = strstr(fname, ".txt"); if (txt) { - mime="text/plain"; + mime = "text/plain"; } - WSContentSend_P(HTTP_SCRIPT_MIMES,fname,mime); + WSContentSend_P(HTTP_SCRIPT_MIMES, fname, mime); if (sflg) { #ifdef USE_DISPLAY_DUMP @@ -5856,8 +5833,8 @@ char buff[512]; #define fileHeaderSize 14 #define infoHeaderSize 40 if (buffer) { - uint8_t *bp=buffer; - uint8_t *lbuf=(uint8_t*)calloc(Settings.display_width+2,3); + uint8_t *bp = buffer; + uint8_t *lbuf = (uint8_t*)calloc(Settings.display_width + 2, 3); uint8_t *lbp; uint8_t fileHeader[fileHeaderSize]; createBitmapFileHeader(Settings.display_height , Settings.display_width , fileHeader); @@ -5865,37 +5842,37 @@ char buff[512]; uint8_t infoHeader[infoHeaderSize]; createBitmapInfoHeader(Settings.display_height, Settings.display_width, infoHeader ); Webserver->client().write((uint8_t *)infoHeader, infoHeaderSize); - for (uint32_t lins=0; lins>1; + bits = bits>>1; } bp++; } - Webserver->client().write((const char*)lbuf, Settings.display_width*3); + Webserver->client().write((const char*)lbuf, Settings.display_width * 3); } if (lbuf) free(lbuf); Webserver->client().stop(); } #endif // USE_DISPLAY_DUMP } else { - File file=fsp->open(fname,FILE_READ); + File file = fsp->open(fname,FILE_READ); uint32_t siz = file.size(); - uint32_t len=sizeof(buff); + uint32_t len = sizeof(buff); while (siz > 0) { - if (len>siz) len=siz; - file.read((uint8_t *)buff,len ); + if (len>siz) len = siz; + file.read((uint8_t *)buff, len); Webserver->client().write((const char*)buff, len); siz -= len; } @@ -5930,7 +5907,7 @@ void ScriptFullWebpage(void) { } WSContentBegin(200, CT_HTML); - const char *title="Full Screen"; + const char *title = "Full Screen"; WSContentSend_P(HTTP_SCRIPT_FULLPAGE1, SettingsText(SET_DEVICENAME), title, fullpage_refresh); WSContentSend_P(HTTP_SCRIPT_FULLPAGE2, fullpage_refresh); //WSContentSend_P(PSTR("
")); @@ -5954,34 +5931,34 @@ void Script_Check_HTML_Setvars(void) { if (Webserver->hasArg("sv")) { String stmp = Webserver->arg("sv"); - Serial.printf("fwp has arg dv %s\n",stmp.c_str()); + Serial.printf("fwp has arg dv %s\n", stmp.c_str()); char cmdbuf[64]; - memset(cmdbuf,0,sizeof(cmdbuf)); - char *cp=cmdbuf; - *cp++='>'; - strncpy(cp,stmp.c_str(),sizeof(cmdbuf)-1); - char *cp1=strchr(cp,'_'); + memset(cmdbuf, 0, sizeof(cmdbuf)); + char *cp = cmdbuf; + *cp++ = '>'; + strncpy(cp, stmp.c_str(), sizeof(cmdbuf) - 1); + char *cp1 = strchr(cp, '_'); if (!cp1) return; - *cp1=0; + *cp1 = 0; char vname[32]; - strncpy(vname,cp,sizeof(vname)); - *cp1='='; + strncpy(vname, cp, sizeof(vname)); + *cp1 = '='; cp1++; struct T_INDEX ind; uint8_t vtype; - isvar(vname,&vtype,&ind,0,0,0); + isvar(vname, &vtype, &ind, 0, 0, 0); if (vtype!=NUM_RES && vtype&STYPE) { // string type must insert quotes - uint8_t tlen=strlen(cp1); - memmove(cp1+1,cp1,tlen); - *cp1='\"'; - *(cp1+tlen+1)='\"'; + uint8_t tlen = strlen(cp1); + memmove(cp1 + 1, cp1, tlen); + *cp1 = '\"'; + *(cp1 + tlen +1 ) = '\"'; } //toLog(cmdbuf); execute_script(cmdbuf); - Run_Scripter(">E",2,0); + Run_Scripter(">E", 2, 0); } } @@ -6048,7 +6025,9 @@ const char SCRIPT_MSG_GTABLEd[] PROGMEM = const char SCRIPT_MSG_GTABLEb[] PROGMEM = "]);" - "var options={%s" CHART_EXTRA_OPTIONS "};" + "var options={%s" CHART_EXTRA_OPTIONS "};"; + +const char SCRIPT_MSG_GTABLEbx[] PROGMEM = "var chart=new google.visualization.%s(document.getElementById('chart%1d'));" "chart.draw(data,options);}" "google.charts.setOnLoadCallback(drawChart);"; @@ -6087,60 +6066,60 @@ const char SCRIPT_MSG_GTE1[] PROGMEM = "'%s'"; char *gc_get_arrays(char *lp, float **arrays, uint8_t *ranum, uint16_t *rentries, uint16_t *ipos) { struct T_INDEX ind; uint8_t vtype; -uint16 entries=0; -uint16_t cipos=0; +uint16 entries = 0; +uint16_t cipos = 0; - uint8_t anum=0; + uint8_t anum = 0; while (anum> 2 %d\n",len); if (fa && len>=entries) { if (!entries) { entries = len; } // add array to list - arrays[anum]=fa; + arrays[anum] = fa; anum++; } } else { // single numeric - arrays[anum]=&glob_script_mem.fvars[index]; + arrays[anum] = &glob_script_mem.fvars[index]; anum++; - entries=1; + entries = 1; } } else { - lp=lp1; + lp = lp1; break; } } } //Serial.printf(">> %d - %d - %d\n",anum,entries,(uint32_t)*arrays[0]); - *ranum=anum; - *rentries=entries; - *ipos=cipos; + *ranum = anum; + *rentries = entries; + *ipos = cipos; return lp; } char *gc_send_labels(char *lp,uint32_t anum) { WSContentSend_PD("["); - for (uint32_t cnt=0; cntw",-2,0); + web_script = Run_Scripter(">w", -2, 0); } else { - web_script=Run_Scripter(">W",-2,0); + web_script = Run_Scripter(">W", -2, 0); } if (web_script==99) { char tmp[256]; - uint8_t optflg=0; - uint8_t chartindex=1; - uint8_t google_libs=0; - char *lp=glob_script_mem.section_ptr+2; + uint8_t optflg = 0; + uint8_t chartindex = 1; + uint8_t google_libs = 0; + char *lp = glob_script_mem.section_ptr + 2; if (mc=='w') { while (*lp) { if (*lp=='\n') break; @@ -6194,175 +6173,175 @@ void ScriptWebShow(char mc) { } if (*lp!=';') { // send this line to web - Replace_Cmd_Vars(lp,1,tmp,sizeof(tmp)); - char *lin=tmp; + Replace_Cmd_Vars(lp, 1, tmp, sizeof(tmp)); + char *lin = tmp; if ((!mc && (*lin!='$')) || (mc=='w' && (*lin!='$'))) { // normal web section if (*lin=='@') { lin++; - optflg=1; + optflg = 1; } else { - optflg=0; + optflg = 0; } // check for input elements - if (!strncmp(lin,"sl(",3)) { + if (!strncmp(lin, "sl(", 3)) { // insert slider sl(min max var left mid right) - char *lp=lin; + char *lp = lin; float min; - lp=GetNumericResult(lp+3,OPER_EQU,&min,0); + lp = GetNumericArgument(lp + 3, OPER_EQU, &min, 0); SCRIPT_SKIP_SPACES // arg2 float max; - lp=GetNumericResult(lp,OPER_EQU,&max,0); + lp = GetNumericArgument(lp, OPER_EQU, &max, 0); SCRIPT_SKIP_SPACES float val; - char *slp=lp; - lp=GetNumericResult(lp,OPER_EQU,&val,0); + char *slp = lp; + lp = GetNumericArgument(lp, OPER_EQU, &val, 0); SCRIPT_SKIP_SPACES char vname[16]; - ScriptGetVarname(vname,slp,sizeof(vname)); + ScriptGetVarname(vname, slp, sizeof(vname)); char left[SCRIPT_MAXSSIZE]; - lp=GetStringResult(lp,OPER_EQU,left,0); + lp = GetStringArgument(lp, OPER_EQU, left, 0); SCRIPT_SKIP_SPACES char mid[SCRIPT_MAXSSIZE]; - lp=GetStringResult(lp,OPER_EQU,mid,0); + lp = GetStringArgument(lp, OPER_EQU, mid, 0); SCRIPT_SKIP_SPACES char right[SCRIPT_MAXSSIZE]; - lp=GetStringResult(lp,OPER_EQU,right,0); + lp = GetStringArgument(lp, OPER_EQU, right, 0); SCRIPT_SKIP_SPACES - WSContentSend_PD(SCRIPT_MSG_SLIDER,left,mid,right,(uint32_t)min,(uint32_t)max,(uint32_t)val,vname); + WSContentSend_PD(SCRIPT_MSG_SLIDER, left,mid, right, (uint32_t)min, (uint32_t)max, (uint32_t)val, vname); - } else if (!strncmp(lin,"ck(",3)) { - char *lp=lin+3; - char *slp=lp; + } else if (!strncmp(lin, "ck(", 3)) { + char *lp = lin + 3; + char *slp = lp; float val; - lp=GetNumericResult(lp,OPER_EQU,&val,0); + lp = GetNumericArgument(lp, OPER_EQU, &val, 0); SCRIPT_SKIP_SPACES char vname[16]; - ScriptGetVarname(vname,slp,sizeof(vname)); + ScriptGetVarname(vname, slp, sizeof(vname)); char label[SCRIPT_MAXSSIZE]; - lp=GetStringResult(lp,OPER_EQU,label,0); + lp = GetStringArgument(lp, OPER_EQU, label, 0); const char *cp; uint8_t uval; if (val>0) { - cp="checked='checked'"; - uval=0; + cp = "checked='checked'"; + uval = 0; } else { - cp=""; - uval=1; + cp = ""; + uval = 1; } - WSContentSend_PD(SCRIPT_MSG_CHKBOX,label,(char*)cp,uval,vname); + WSContentSend_PD(SCRIPT_MSG_CHKBOX, label, (char*)cp, uval, vname); - } else if (!strncmp(lin,"bu(",3)) { - char *lp=lin+3; - uint8_t bcnt=0; - char *found=lin; + } else if (!strncmp(lin, "bu(", 3)) { + char *lp = lin + 3; + uint8_t bcnt = 0; + char *found = lin; while (bcnt<4) { - found=strstr(found,"bu("); + found = strstr(found, "bu("); if (!found) break; - found+=3; + found += 3; bcnt++; } - uint8_t proz=100/bcnt; - if (!optflg && bcnt>1) proz-=2; + uint8_t proz = 100 / bcnt; + if (!optflg && bcnt>1) proz -= 2; if (optflg) WSContentSend_PD(SCRIPT_MSG_BUT_START_TBL); else WSContentSend_PD(SCRIPT_MSG_BUT_START); - for (uint32_t cnt=0;cnt0) { - cp=ontxt; - uval=0; + cp = ontxt; + uval = 0; } else { - cp=offtxt; - uval=1; + cp = offtxt; + uval = 1; } if (bcnt>1 && cnt==bcnt-1) { - if (!optflg) proz+=2; + if (!optflg) proz += 2; } if (!optflg) { - WSContentSend_PD(SCRIPT_MSG_BUTTONa,proz,uval,vname,cp); + WSContentSend_PD(SCRIPT_MSG_BUTTONa, proz, uval, vname, cp); } else { - WSContentSend_PD(SCRIPT_MSG_BUTTONa_TBL,proz,uval,vname,cp); + WSContentSend_PD(SCRIPT_MSG_BUTTONa_TBL, proz, uval, vname, cp); } if (bcnt>1 && cnt%s"),lin); + WSContentSend_PD(PSTR("
%s
"), lin); } else { - WSContentSend_PD(PSTR("{s}%s{e}"),lin); + WSContentSend_PD(PSTR("{s}%s{e}"), lin); } } } @@ -6375,58 +6354,67 @@ void ScriptWebShow(char mc) { lin++; exgc: char *lp; - if (!strncmp(lin,"gc(",3)) { + if (!strncmp(lin, "gc(", 3)) { // get google table - lp=lin+3; + lp = lin + 3; SCRIPT_SKIP_SPACES const char *type; const char *func; char options[312]; - uint8_t nanum=MAX_GARRAY; - uint8_t y2f=0; + uint8_t nanum = MAX_GARRAY; + uint8_t y2f = 0; + uint8_t tonly = 0; char ctype; - ctype=*lp; + ctype = *lp; lp++; - if (!(google_libs&GLIBS_MAIN)) { - google_libs|=GLIBS_MAIN; + if (!(google_libs & GLIBS_MAIN)) { + google_libs |= GLIBS_MAIN; WSContentSend_PD(SCRIPT_MSG_GTABLE); } switch (ctype) { case 'l': - type=PSTR("LineChart"); + type = PSTR("LineChart"); break; case 'b': - type=PSTR("BarChart"); + type = PSTR("BarChart"); break; case 'p': - type=PSTR("PieChart"); + type = PSTR("PieChart"); break; case 'g': - type=PSTR("Gauge"); - if (!(google_libs&GLIBS_GAUGE)) { - google_libs|=GLIBS_GAUGE; + type = PSTR("Gauge"); + if (!(google_libs & GLIBS_GAUGE)) { + google_libs |= GLIBS_GAUGE; WSContentSend_PD(SCRIPT_MSG_GAUGE); } break; case 't': - type=PSTR("Table"); - if (!(google_libs&GLIBS_TABLE)) { - google_libs|=GLIBS_TABLE; + type = PSTR("Table"); + if (!(google_libs & GLIBS_TABLE)) { + google_libs |= GLIBS_TABLE; WSContentSend_PD(SCRIPT_MSG_TABLE); } break; case 'T': - type=PSTR("Timeline"); - if (!(google_libs&GLIBS_TIMELINE)) { - google_libs|=GLIBS_TIMELINE; + type = PSTR("Timeline"); + if (!(google_libs & GLIBS_TIMELINE)) { + google_libs |= GLIBS_TIMELINE; WSContentSend_PD(SCRIPT_MSG_TIMELINE); } break; case 'h': - type=PSTR("Histogram"); + type = PSTR("Histogram"); break; case 'c': - type=PSTR("ColumnChart"); + type = PSTR("ColumnChart"); + break; + case 'C': + type = PSTR("ComboChart"); + break; + case 'e': + WSContentSend_PD(SCRIPT_MSG_GTABLEbx, type, chartindex); + chartindex++; + goto nextwebline; break; default: // error @@ -6435,24 +6423,28 @@ exgc: } if (ctype=='l' && *lp=='f') { lp++; - func=PSTR(",curveType:'function'"); + func = PSTR(",curveType:'function'"); } else { - func=""; + func = ""; } if (*lp=='2') { lp++; - nanum=2; - y2f=1; + nanum = 2; + y2f = 1; + } + if (*lp=='t') { + lp++; + tonly = 1; } SCRIPT_SKIP_SPACES //Serial.printf("type %d\n",ctype); float *arrays[MAX_GARRAY]; - uint8_t anum=0; - uint16_t entries=0; - uint16_t ipos=0; - lp=gc_get_arrays(lp, &arrays[0], &anum, &entries, &ipos); + uint8_t anum = 0; + uint16_t entries = 0; + uint16_t ipos = 0; + lp = gc_get_arrays(lp, &arrays[0], &anum, &entries, &ipos); if (anum>nanum) { goto nextwebline; @@ -6461,98 +6453,120 @@ exgc: //Serial.printf("arrays %d\n",anum); //Serial.printf("entries %d\n",entries); if (ctype=='T') { - if (anum && !(entries&1)) { + if (anum && !(entries & 1)) { WSContentSend_PD(SCRIPT_MSG_GTABLEa); WSContentSend_PD(SCRIPT_MSG_GTABLEd); char label[SCRIPT_MAXSSIZE]; - lp=GetStringResult(lp,OPER_EQU,label,0); + lp = GetStringArgument(lp, OPER_EQU, label, 0); SCRIPT_SKIP_SPACES - for (uint32_t ind=0; ind=entries) todflg=entries-1; + int16_t divflg = 1; + int16_t todflg = -1; + if (!strncmp(label, "cnt", 3)) { + char *cp = &label[3]; + //todflg=atoi(&label[3]); + todflg = strtol(cp, &cp, 10); + if (todflg>=entries) todflg = entries - 1; + if (*cp=='/') { + cp++; + divflg = strtol(cp, &cp, 10); + } } else { - uint16 segments=1; - for (uint32_t cnt=0; cnt=entries) aind=entries-1; - for (uint32_t cnt=0; cnt=entries) aind = entries - 1; + for (uint32_t cnt = 0; cnt < entries; cnt++) { WSContentSend_PD("['"); char lbl[16]; if (todflg>=0) { - sprintf(lbl,"%d",todflg); + sprintf(lbl, "%d", todflg / divflg); todflg++; - if (todflg>=entries) { - todflg=0; + if (todflg >= entries) { + todflg = 0; } } else { - GetTextIndexed(lbl, sizeof(lbl), aind/divflg, label); + if (todflg==-1) { + GetTextIndexed(lbl, sizeof(lbl), aind / divflg, label); + } else { + // day,hours,mins + GetTextIndexed(lbl, sizeof(lbl), aind / divflg, label + 4); + sprintf(lbl, "%s-%02d", lbl, aind % divflg); + } } WSContentSend_PD(lbl); WSContentSend_PD("',"); - for (uint32_t ind=0; ind=entries) { - aind=0; + aind = 0; } } - + // table complete + if (tonly) { + WSContentSend_PD("]);"); + goto nextwebline; + } // get header char header[SCRIPT_MAXSSIZE]; - lp=GetStringResult(lp,OPER_EQU,header,0); + lp = GetStringArgument(lp, OPER_EQU, header, 0); SCRIPT_SKIP_SPACES switch (ctype) { case 't': - snprintf_P(options,sizeof(options),SCRIPT_MSG_GOPT2); + snprintf_P(options, sizeof(options), SCRIPT_MSG_GOPT2); break; default: - snprintf_P(options,sizeof(options),SCRIPT_MSG_GOPT1,header); + snprintf_P(options, sizeof(options), SCRIPT_MSG_GOPT1, header); break; } // check for 2 axis option @@ -6560,49 +6574,52 @@ exgc: // 2 y axes variant SCRIPT_SKIP_SPACES float max1; - lp=GetNumericResult(lp,OPER_EQU,&max1,0); + lp = GetNumericArgument(lp, OPER_EQU, &max1, 0); SCRIPT_SKIP_SPACES float max2; - lp=GetNumericResult(lp,OPER_EQU,&max2,0); + lp = GetNumericArgument(lp, OPER_EQU, &max2, 0); SCRIPT_SKIP_SPACES - snprintf_P(options,sizeof(options),SCRIPT_MSG_GOPT3,header,(uint32_t)max1,(uint32_t)max2,func); + snprintf_P(options, sizeof(options), SCRIPT_MSG_GOPT3, header, (uint32_t)max1, (uint32_t)max2, func); } else { SCRIPT_SKIP_SPACES - if (*lp!=')') { - float max1; - lp=GetNumericResult(lp,OPER_EQU,&max1,0); - SCRIPT_SKIP_SPACES - float max2; - lp=GetNumericResult(lp,OPER_EQU,&max2,0); - SCRIPT_SKIP_SPACES - snprintf_P(options,sizeof(options),SCRIPT_MSG_GOPT6,header,(uint32_t)max1,(uint32_t)max2,func); + if (ctype!='g') { + if (*lp!=')') { + float max1; + lp = GetNumericArgument(lp, OPER_EQU, &max1, 0); + SCRIPT_SKIP_SPACES + float max2; + lp = GetNumericArgument(lp, OPER_EQU, &max2, 0); + SCRIPT_SKIP_SPACES + snprintf_P(options, sizeof(options), SCRIPT_MSG_GOPT6, header, (uint32_t)max1, (uint32_t)max2, func); + } } } if (ctype=='g') { float yellowFrom; - lp=GetNumericResult(lp,OPER_EQU,&yellowFrom,0); + lp = GetNumericArgument(lp, OPER_EQU, &yellowFrom, 0); SCRIPT_SKIP_SPACES float redFrom; - lp=GetNumericResult(lp,OPER_EQU,&redFrom,0); + lp = GetNumericArgument(lp, OPER_EQU, &redFrom, 0); SCRIPT_SKIP_SPACES float maxValue; - lp=GetNumericResult(lp,OPER_EQU,&maxValue,0); + lp = GetNumericArgument(lp, OPER_EQU, &maxValue, 0); SCRIPT_SKIP_SPACES - float redTo=maxValue; - float yellowTo=redFrom; - snprintf_P(options,sizeof(options),SCRIPT_MSG_GAUGEOPT,(uint32_t)maxValue,(uint32_t)redFrom,(uint32_t)redTo, - (uint32_t)yellowFrom,(uint32_t)yellowTo); + float redTo = maxValue; + float yellowTo = redFrom; + snprintf_P(options, sizeof(options), SCRIPT_MSG_GAUGEOPT, (uint32_t)maxValue, (uint32_t)redFrom, (uint32_t)redTo, + (uint32_t)yellowFrom, (uint32_t)yellowTo); } } - WSContentSend_PD(SCRIPT_MSG_GTABLEb,options,type,chartindex); + WSContentSend_PD(SCRIPT_MSG_GTABLEb, options); + WSContentSend_PD(SCRIPT_MSG_GTABLEbx, type, chartindex); chartindex++; } else { - WSContentSend_PD(PSTR("%s"),lin); + WSContentSend_PD(PSTR("%s"), lin); } #else lin++; - WSContentSend_PD(PSTR("%s"),lin); + WSContentSend_PD(PSTR("%s"), lin); } else { // WSContentSend_PD(PSTR("%s"),lin); #endif //USE_GOOGLE_CHARTS @@ -6626,10 +6643,10 @@ nextwebline: #ifdef USE_SENDMAIL void script_send_email_body(void(*func)(char *)) { -uint8_t msect=Run_Scripter(">m",-2,0); +uint8_t msect = Run_Scripter(">m", -2, 0); if (msect==99) { char tmp[256]; - char *lp=glob_script_mem.section_ptr+2; + char *lp = glob_script_mem.section_ptr + 2; while (lp) { while (*lp==SCRIPT_EOL) { lp++; @@ -6639,7 +6656,7 @@ uint8_t msect=Run_Scripter(">m",-2,0); } if (*lp!=';') { // send this line to smtp - Replace_Cmd_Vars(lp,1,tmp,sizeof(tmp)); + Replace_Cmd_Vars(lp, 1, tmp, sizeof(tmp)); //client->println(tmp); func(tmp); } @@ -6656,14 +6673,14 @@ uint8_t msect=Run_Scripter(">m",-2,0); func((char*)"*"); } } -#endif +#endif //USE_SENDMAIL #ifdef USE_SCRIPT_JSON_EXPORT void ScriptJsonAppend(void) { - uint8_t web_script=Run_Scripter(">J",-2,0); + uint8_t web_script = Run_Scripter(">J", -2, 0); if (web_script==99) { char tmp[256]; - char *lp=glob_script_mem.section_ptr+2; + char *lp = glob_script_mem.section_ptr + 2; while (lp) { while (*lp==SCRIPT_EOL) { lp++; @@ -6673,8 +6690,8 @@ void ScriptJsonAppend(void) { } if (*lp!=';') { // send this line to mqtt - Replace_Cmd_Vars(lp,1,tmp,sizeof(tmp)); - ResponseAppend_P(PSTR("%s"),tmp); + Replace_Cmd_Vars(lp, 1, tmp, sizeof(tmp)); + ResponseAppend_P(PSTR("%s"), tmp); } if (*lp==SCRIPT_EOL) { lp++; @@ -6690,7 +6707,7 @@ void ScriptJsonAppend(void) { bool RulesProcessEvent(char *json_event) { - if (bitRead(Settings.rule_enabled, 0)) Run_Scripter(">E",2,json_event); + if (bitRead(Settings.rule_enabled, 0)) Run_Scripter(">E", 2, json_event); return true; } @@ -6703,7 +6720,7 @@ bool RulesProcessEvent(char *json_event) { #ifndef STASK_PRIO #define STASK_PRIO 1 -#endif +#endif //ESP32 #if 1 @@ -6724,7 +6741,7 @@ void script_task1(void *arg) { //if (time<=esp32_tasks[0].task_timer) {vTaskDelay( pdMS_TO_TICKS( time ) ); } delay(esp32_tasks[0].task_timer); if (bitRead(Settings.rule_enabled, 0)) { - Run_Scripter(">t1",3,0); + Run_Scripter(">t1", 3, 0); } } } @@ -6740,7 +6757,7 @@ void script_task2(void *arg) { //if (time<=esp32_tasks[1].task_timer) {vTaskDelay( pdMS_TO_TICKS( time ) ); } delay(esp32_tasks[1].task_timer); if (bitRead(Settings.rule_enabled, 0)) { - Run_Scripter(">t2",3,0); + Run_Scripter(">t2", 3, 0); } } } @@ -6769,14 +6786,14 @@ TaskHandle_t task_t2; void script_task1(void *arg) { while (1) { delay(task_timer1); - Run_Scripter(">t1",3,0); + Run_Scripter(">t1", 3, 0); } } void script_task2(void *arg) { while (1) { delay(task_timer2); - Run_Scripter(">t2",3,0); + Run_Scripter(">t2", 3, 0); } } @@ -6805,12 +6822,12 @@ uint32_t scripter_create_task(uint32_t num, uint32_t time, uint32_t core) { #include "WiFiClientSecureLightBearSSL.h" #else #include -#endif +#endif //ESP8266 // get tesla powerwall info page json string uint32_t call2https(const char *host, const char *path) { if (global_state.wifi_down) return 1; - uint32_t status=0; + uint32_t status = 0; #ifdef ESP32 WiFiClientSecure *httpsClient; httpsClient = new WiFiClientSecure; @@ -6821,8 +6838,7 @@ uint32_t call2https(const char *host, const char *path) { httpsClient->setTimeout(1500); - int retry = 0; - String result; + uint32_t retry = 0; while ((!httpsClient->connect(host, 443)) && (retry < 5)) { delay(100); retry++; @@ -6842,6 +6858,7 @@ uint32_t call2https(const char *host, const char *path) { break; } } + String result; while (httpsClient->available()) { String line = httpsClient->readStringUntil('\n'); if (line!="") { @@ -6850,20 +6867,19 @@ uint32_t call2https(const char *host, const char *path) { } httpsClient->stop(); delete httpsClient; - Run_Scripter(">jp",3,(char*)result.c_str()); + Run_Scripter(">jp", 3, (char*)result.c_str()); return 0; } - #endif // SCRIPT_GET_HTTPS_JP -void cpy2lf(char *dst,uint32_t dstlen, char *src) { +void cpy2lf(char *dst, uint32_t dstlen, char *src) { for (uint32_t cnt=0; cnt0) glob_script_mem.script_ram[len_decompressed]=0; + if (len_decompressed>0) glob_script_mem.script_ram[len_decompressed] = 0; // indicates scripter use compression bitWrite(Settings.rule_once, 6, 1); //AddLog_P2(LOG_LEVEL_INFO, PSTR("decompressed script len %d"),len_decompressed); @@ -6908,29 +6924,29 @@ bool Xdrv10(uint8_t function) #endif // USE_SCRIPT_COMPRESSION #ifdef USE_BUTTON_EVENT - for (uint32_t cnt=0;cnt=0 AddLog_P(LOG_LEVEL_INFO,PSTR("FATFS mount OK!")); //fsp->dateTimeCallback(dateTime); - glob_script_mem.script_sd_found=1; + glob_script_mem.script_sd_found = 1; char *script; - script=(char*)calloc(FAT_SCRIPT_SIZE+4,1); + script = (char*)calloc(FAT_SCRIPT_SIZE + 4, 1); if (!script) break; - glob_script_mem.script_ram=script; - glob_script_mem.script_size=FAT_SCRIPT_SIZE; + glob_script_mem.script_ram = script; + glob_script_mem.script_size = FAT_SCRIPT_SIZE; if (fsp->exists(FAT_SCRIPT_NAME)) { - File file=fsp->open(FAT_SCRIPT_NAME,FILE_READ); - file.read((uint8_t*)script,FAT_SCRIPT_SIZE); + File file = fsp->open(FAT_SCRIPT_NAME, FILE_READ); + file.read((uint8_t*)script, FAT_SCRIPT_SIZE); file.close(); } - script[FAT_SCRIPT_SIZE-1]=0; + script[FAT_SCRIPT_SIZE - 1] = 0; // use rules storage for permanent vars - glob_script_mem.script_pram=(uint8_t*)Settings.rules[0]; - glob_script_mem.script_pram_size=MAX_SCRIPT_SIZE; + glob_script_mem.script_pram = (uint8_t*)Settings.rules[0]; + glob_script_mem.script_pram_size = MAX_SCRIPT_SIZE; - glob_script_mem.flags=1; + glob_script_mem.flags = 1; } else { AddLog_P(LOG_LEVEL_INFO,PSTR("FATFS mount failed!")); - glob_script_mem.script_sd_found=0; + glob_script_mem.script_sd_found = 0; } #endif // USE_SCRIPT_FATFS @@ -6997,46 +7013,46 @@ bool Xdrv10(uint8_t function) #else // lfs on esp8266 fsp = &LittleFS; -#endif +#endif //ESP32 char *script; - script=(char*)calloc(LITTLEFS_SCRIPT_SIZE+4,1); + script = (char*)calloc(LITTLEFS_SCRIPT_SIZE + 4, 1); if (!script) break; - LoadFile("/script.txt",(uint8_t*)script,LITTLEFS_SCRIPT_SIZE); + LoadFile("/script.txt", (uint8_t*)script, LITTLEFS_SCRIPT_SIZE); - glob_script_mem.script_ram=script; - glob_script_mem.script_size=LITTLEFS_SCRIPT_SIZE; - script[LITTLEFS_SCRIPT_SIZE-1]=0; + glob_script_mem.script_ram = script; + glob_script_mem.script_size = LITTLEFS_SCRIPT_SIZE; + script[LITTLEFS_SCRIPT_SIZE-1] = 0; // use rules storage for permanent vars - glob_script_mem.script_pram=(uint8_t*)Settings.rules[0]; - glob_script_mem.script_pram_size=MAX_SCRIPT_SIZE; - glob_script_mem.flags=1; + glob_script_mem.script_pram = (uint8_t*)Settings.rules[0]; + glob_script_mem.script_pram_size = MAX_SCRIPT_SIZE; + glob_script_mem.flags = 1; #endif // LITTLEFS_SCRIPT_SIZE // a valid script MUST start with >D if (glob_script_mem.script_ram[0]!='>' && glob_script_mem.script_ram[1]!='D') { // clr all - memset(glob_script_mem.script_ram,0,glob_script_mem.script_size); + memset(glob_script_mem.script_ram, 0 ,glob_script_mem.script_size); strcpy_P(glob_script_mem.script_ram, PSTR(">D\nscript error must start with >D")); bitWrite(Settings.rule_enabled, 0, 0); } // assure permanent memory is 4 byte aligned - { uint32_t ptr=(uint32_t)glob_script_mem.script_pram; - ptr&=0xfffffffc; - ptr+=4; - glob_script_mem.script_pram=(uint8_t*)ptr; - glob_script_mem.script_pram_size-=4; + { uint32_t ptr = (uint32_t)glob_script_mem.script_pram; + ptr &= 0xfffffffc; + ptr += 4; + glob_script_mem.script_pram = (uint8_t*)ptr; + glob_script_mem.script_pram_size -= 4; } if (bitRead(Settings.rule_enabled, 0)) Init_Scripter(); break; case FUNC_INIT: if (bitRead(Settings.rule_enabled, 0)) { - Run_Scripter(">B\n",3,0); - fast_script=Run_Scripter(">F",-2,0); + Run_Scripter(">B\n", 3, 0); + fast_script = Run_Scripter(">F", -2, 0); #if defined(USE_SCRIPT_HUE) && defined(USE_WEBSERVER) && defined(USE_EMULATION) && defined(USE_EMULATION_HUE) && defined(USE_LIGHT) Script_Check_Hue(0); -#endif +#endif //USE_SCRIPT_HUE } break; case FUNC_EVERY_100_MSECOND: @@ -7050,18 +7066,18 @@ bool Xdrv10(uint8_t function) break; case FUNC_SET_POWER: #ifdef SCRIPT_POWER_SECTION - if (bitRead(Settings.rule_enabled, 0)) Run_Scripter(">P",2,0); + if (bitRead(Settings.rule_enabled, 0)) Run_Scripter(">P", 2, 0); #else if (bitRead(Settings.rule_enabled, 0)) { - Run_Scripter(">E",2,0); - result=event_handeled; + Run_Scripter(">E", 2, 0); + result = event_handeled; } -#endif +#endif //SCRIPT_POWER_SECTION break; case FUNC_RULES_PROCESS: if (bitRead(Settings.rule_enabled, 0)) { - Run_Scripter(">E",2,mqtt_data); - result=event_handeled; + Run_Scripter(">E", 2, mqtt_data); + result = event_handeled; } break; #ifdef USE_WEBSERVER @@ -7073,11 +7089,11 @@ bool Xdrv10(uint8_t function) if (bitRead(Settings.rule_enabled, 0)) { ScriptWebShow('$'); #ifdef SCRIPT_FULL_WEBPAGE - uint8_t web_script=Run_Scripter(">w",-2,0); + uint8_t web_script = Run_Scripter(">w", -2, 0); if (web_script==99) { char bname[48]; - cpy2lf(bname,sizeof(bname),glob_script_mem.section_ptr+3); - WSContentSend_PD(HTTP_WEB_FULL_DISPLAY,bname); + cpy2lf(bname, sizeof(bname), glob_script_mem.section_ptr + 3); + WSContentSend_PD(HTTP_WEB_FULL_DISPLAY, bname); Webserver->on("/sfd", ScriptFullWebpage); #ifdef USE_SCRIPT_FATFS Webserver->onNotFound(ScriptGetSDCard); @@ -7090,24 +7106,24 @@ bool Xdrv10(uint8_t function) case FUNC_WEB_ADD_HANDLER: Webserver->on("/" WEB_HANDLE_SCRIPT, HandleScriptConfiguration); Webserver->on("/ta",HTTP_POST, HandleScriptTextareaConfiguration); - Webserver->on("/exs", HTTP_POST,[]() { Webserver->sendHeader("Location","/exs");Webserver->send(303);},script_upload_start); - Webserver->on("/exs", HTTP_GET,ScriptExecuteUploadSuccess); + Webserver->on("/exs", HTTP_POST,[]() { Webserver->sendHeader("Location","/exs");Webserver->send(303);}, script_upload_start); + Webserver->on("/exs", HTTP_GET, ScriptExecuteUploadSuccess); #ifdef USE_SCRIPT_FATFS - Webserver->on("/u3", HTTP_POST,[]() { Webserver->sendHeader("Location","/u3");Webserver->send(303);},script_upload); - Webserver->on("/u3", HTTP_GET,ScriptFileUploadSuccess); - Webserver->on("/upl", HTTP_GET,Script_FileUploadConfiguration); -#endif + Webserver->on("/u3", HTTP_POST,[]() { Webserver->sendHeader("Location","/u3");Webserver->send(303);}, script_upload); + Webserver->on("/u3", HTTP_GET, ScriptFileUploadSuccess); + Webserver->on("/upl", HTTP_GET, Script_FileUploadConfiguration); +#endif //USE_SCRIPT_FATFS break; #endif // USE_WEBSERVER case FUNC_SAVE_BEFORE_RESTART: if (bitRead(Settings.rule_enabled, 0)) { - Run_Scripter(">R",2,0); + Run_Scripter(">R", 2, 0); Scripter_save_pvars(); } #ifdef USE_SCRIPT_GLOBVARS Script_Stop_UDP(); -#endif +#endif //USE_SCRIPT_GLOBVARS break; #ifdef SUPPORT_MQTT_EVENT case FUNC_MQTT_DATA: @@ -7136,18 +7152,18 @@ bool Xdrv10(uint8_t function) case FUNC_BUTTON_PRESSED: if (bitRead(Settings.rule_enabled, 0)) { if ((script_button[XdrvMailbox.index]&1)!=(XdrvMailbox.payload&1)) { - script_button[XdrvMailbox.index]=XdrvMailbox.payload; - Run_Scripter(">b",2,0); + script_button[XdrvMailbox.index] = XdrvMailbox.payload; + Run_Scripter(">b", 2, 0); } } break; -#endif +#endif //USE_BUTTON_EVENT #ifdef USE_SCRIPT_GLOBVARS case FUNC_LOOP: Script_PollUdp(); break; -#endif +#endif //USE_SCRIPT_GLOBVARS } return result; From 01ab99f8a1108f06144349f25a98f8babc013ab3 Mon Sep 17 00:00:00 2001 From: gemu2015 Date: Fri, 11 Sep 2020 15:45:21 +0200 Subject: [PATCH 50/55] display fix touch init, some formatting --- tasmota/xdrv_13_display.ino | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/tasmota/xdrv_13_display.ino b/tasmota/xdrv_13_display.ino index 3a87826f3..8a0c5d395 100644 --- a/tasmota/xdrv_13_display.ino +++ b/tasmota/xdrv_13_display.ino @@ -506,7 +506,7 @@ void DisplayText(void) } } break; -#endif +#endif // USE_SCRIPT_FATFS case 'h': // hor line to var = atoiv(cp, &temp); @@ -696,7 +696,7 @@ void DisplayText(void) Restore_graph(temp,bbuff); break; } -#endif +#endif // USE_SCRIPT_FATFS { int16_t num,gxp,gyp,gxs,gys,dec,icol; float ymin,ymax; var=atoiv(cp,&num); @@ -744,7 +744,7 @@ void DisplayText(void) AddValue(num,temp); } break; -#endif +#endif // USE_GRAPH #ifdef USE_AWATCH case 'w': @@ -752,7 +752,7 @@ void DisplayText(void) cp += var; DrawAClock(temp); break; -#endif +#endif // USE_AWATCH #ifdef USE_TOUCH_BUTTONS case 'b': @@ -834,12 +834,13 @@ void DisplayText(void) buttons[num]->vpower.is_pushbutton=0; } if (dflg) buttons[num]->xdrawButton(buttons[num]->vpower.on_off); + buttons[num]->vpower.disable=!dflg; } } } } break; -#endif +#endif // USE_TOUCH_BUTTONS default: // unknown escape Response_P(PSTR("Unknown Escape")); @@ -1530,8 +1531,8 @@ void CmndDisplayRows(void) bool jpg2rgb888(const uint8_t *src, size_t src_len, uint8_t * out, jpg_scale_t scale); char get_jpeg_size(unsigned char* data, unsigned int data_size, unsigned short *width, unsigned short *height); void rgb888_to_565(uint8_t *in, uint16_t *out, uint32_t len); -#endif -#endif +#endif // JPEG_PICTS +#endif // ESP32 #if defined(USE_SCRIPT_FATFS) && defined(USE_SCRIPT) extern FS *fsp; @@ -1626,7 +1627,7 @@ void Draw_RGB_Bitmap(char *file,uint16_t xp, uint16_t yp) { #endif // ESP32 } } -#endif +#endif // USE_SCRIPT_FATFS #ifdef USE_AWATCH #define MINUTE_REDUCT 4 @@ -1663,7 +1664,7 @@ void DrawAClock(uint16_t rad) { temp=((float)RtcTime.minute*(pi/30.0)-(pi/2.0)); renderer->writeLine(disp_xpos, disp_ypos,disp_xpos+(frad-MINUTE_REDUCT)*cosf(temp),disp_ypos+(frad-MINUTE_REDUCT)*sinf(temp), fg_color); } -#endif +#endif // USE_AWATCH #ifdef USE_GRAPH @@ -1938,7 +1939,7 @@ void Restore_graph(uint8_t num, char *path) { fp.close(); RedrawGraph(num,1); } -#endif +#endif // USE_SCRIPT_FATFS void RedrawGraph(uint8_t num, uint8_t flags) { uint16_t index=num%NUM_GRAPHS; @@ -2050,16 +2051,13 @@ void AddValue(uint8_t num,float fval) { #ifdef USE_FT5206 +#include // touch panel controller #undef FT5206_address #define FT5206_address 0x38 -#include FT5206_Class *touchp; TP_Point pLoc; - - -extern VButton *buttons[]; bool FT5206_found; bool Touch_Init(TwoWire &i2c) { @@ -2088,6 +2086,7 @@ uint32_t Touch_Status(uint32_t sel) { } } + #ifdef USE_TOUCH_BUTTONS void Touch_MQTT(uint8_t index, const char *cp) { ResponseTime_P(PSTR(",\"FT5206\":{\"%s%d\":\"%d\"}}"), cp, index+1, buttons[index]->vpower.on_off); @@ -2184,6 +2183,7 @@ uint8_t vbutt=0; pLoc.y = 0; } } + #endif // USE_TOUCH_BUTTONS #endif // USE_FT5206 From 8468e093fc74b4c6f304a3af43f95d3547f749bb Mon Sep 17 00:00:00 2001 From: gemu2015 Date: Fri, 11 Sep 2020 15:45:59 +0200 Subject: [PATCH 51/55] i2s audio update save heap memory on esp32 with psram --- tasmota/xdrv_42_i2s_audio.ino | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/tasmota/xdrv_42_i2s_audio.ino b/tasmota/xdrv_42_i2s_audio.ino index 77492992b..5a31d832b 100644 --- a/tasmota/xdrv_42_i2s_audio.ino +++ b/tasmota/xdrv_42_i2s_audio.ino @@ -52,7 +52,7 @@ AudioFileSourceFS *file; AudioOutputI2S *out; AudioFileSourceID3 *id3; AudioGeneratorMP3 *decoder = NULL; - +void *mp3ram = NULL; #ifdef USE_WEBRADIO AudioFileSourceICYStream *ifile = NULL; @@ -210,6 +210,12 @@ void I2S_Init(void) { is2_volume=10; out->SetGain(((float)is2_volume/100.0)*4.0); out->stop(); + mp3ram = nullptr; + +#ifdef ESP32 + if (psramFound()) { + mp3ram = heap_caps_malloc(preallocateCodecSize, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); + } #ifdef USE_WEBRADIO if (psramFound()) { @@ -223,6 +229,7 @@ void I2S_Init(void) { //Serial.printf_P(PSTR("FATAL ERROR: Unable to preallocate %d bytes for app\n"), preallocateBufferSize+preallocateCodecSize); } #endif // USE_WEBRADIO +#endif // ESP32 } #ifdef ESP32 @@ -285,7 +292,7 @@ void Webradio(const char *url) { retryms = millis() + 2000; } - xTaskCreatePinnedToCore(mp3_task2, "MP3", 8192, NULL, 3, &mp3_task_h, 1); + xTaskCreatePinnedToCore(mp3_task2, "MP3-2", 8192, NULL, 3, &mp3_task_h, 1); } void mp3_task2(void *arg){ @@ -366,7 +373,12 @@ void Play_mp3(const char *path) { file = new AudioFileSourceFS(*fsp,path); id3 = new AudioFileSourceID3(file); - mp3 = new AudioGeneratorMP3(); + + if (mp3ram) { + mp3 = new AudioGeneratorMP3(mp3ram, preallocateCodecSize); + } else { + mp3 = new AudioGeneratorMP3(); + } mp3->begin(id3, out); if (I2S_Task) { From ecc27aa3835cfe8b0fb78027762a743771b92212 Mon Sep 17 00:00:00 2001 From: Stephan Hadinger Date: Sat, 12 Sep 2020 10:57:54 +0200 Subject: [PATCH 52/55] Add Zigbee auto-config when pairing --- tasmota/CHANGELOG.md | 1 + tasmota/settings.h | 2 +- tasmota/xdrv_23_zigbee_2_devices.ino | 57 +++- tasmota/xdrv_23_zigbee_5_converters.ino | 66 +++-- tasmota/xdrv_23_zigbee_6_commands.ino | 10 +- tasmota/xdrv_23_zigbee_8_parsers.ino | 337 +++++++++++++++++++++++- 6 files changed, 430 insertions(+), 43 deletions(-) diff --git a/tasmota/CHANGELOG.md b/tasmota/CHANGELOG.md index faaa2ae17..d07791a2e 100644 --- a/tasmota/CHANGELOG.md +++ b/tasmota/CHANGELOG.md @@ -8,6 +8,7 @@ - Fix crash in ``ZbRestore`` - Add new shutter modes (#9244) - Add ``#define USE_MQTT_AWS_IOT_LIGHT`` for password based AWS IoT authentication +- Add Zigbee auto-config when pairing ### 8.5.0 20200907 diff --git a/tasmota/settings.h b/tasmota/settings.h index 2ceadd523..a9c8680c1 100644 --- a/tasmota/settings.h +++ b/tasmota/settings.h @@ -129,7 +129,7 @@ typedef union { // Restricted by MISRA-C Rule 18.4 bu uint32_t virtual_ct_cw : 1; // bit 25 (v8.4.0.1) - SetOption107 - Virtual CT Channel - signals whether the hardware white is cold CW (true) or warm WW (false) uint32_t teleinfo_rawdata : 1; // bit 26 (v8.4.0.2) - SetOption108 - enable Teleinfo + Tasmota Energy device (0) or Teleinfo raw data only (1) uint32_t alexa_gen_1 : 1; // bit 27 (v8.4.0.3) - SetOption109 - Alexa gen1 mode - if you only have Echo Dot 2nd gen devices - uint32_t spare28 : 1; // bit 28 + uint32_t zb_disable_autobind : 1; // bit 28 (v8.5.0.1) - SetOption110 - disable Zigbee auto-config when pairing new devices uint32_t spare29 : 1; // bit 29 uint32_t spare30 : 1; // bit 30 uint32_t spare31 : 1; // bit 31 diff --git a/tasmota/xdrv_23_zigbee_2_devices.ino b/tasmota/xdrv_23_zigbee_2_devices.ino index 8ad36f861..1e2c29704 100644 --- a/tasmota/xdrv_23_zigbee_2_devices.ino +++ b/tasmota/xdrv_23_zigbee_2_devices.ino @@ -36,6 +36,10 @@ public: char * manufacturerId; char * modelId; char * friendlyName; + // _defer_last_time : what was the last time an outgoing message is scheduled + // this is designed for flow control and avoid messages to be lost or unanswered + uint32_t defer_last_message_sent; + uint8_t endpoints[endpoints_max]; // static array to limit memory consumption, list of endpoints until 0x00 or end of array // Used for attribute reporting Z_attribute_list attr_list; @@ -78,6 +82,7 @@ public: manufacturerId(nullptr), modelId(nullptr), friendlyName(nullptr), + defer_last_message_sent(0), endpoints{ 0, 0, 0, 0, 0, 0, 0, 0 }, attr_list(), shortaddr(_shortaddr), @@ -145,21 +150,28 @@ public: * Structures for deferred callbacks \*********************************************************************************************/ -typedef int32_t (*Z_DeviceTimer)(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster, uint8_t endpoint, uint32_t value); +typedef void (*Z_DeviceTimer)(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster, uint8_t endpoint, uint32_t value); // Category for Deferred actions, this allows to selectively remove active deferred or update them typedef enum Z_Def_Category { - Z_CAT_NONE = 0, // no category, it will happen anyways + Z_CAT_ALWAYS = 0, // no category, it will happen whatever new timers + // Below will clear any event in the same category for the same address (shortaddr / groupaddr) + Z_CLEAR_DEVICE = 0x01, Z_CAT_READ_ATTR, // Attribute reporting, either READ_ATTRIBUTE or REPORT_ATTRIBUTE, we coalesce all attributes reported if we can Z_CAT_VIRTUAL_OCCUPANCY, // Creation of a virtual attribute, typically after a time-out. Ex: Aqara presence sensor Z_CAT_REACHABILITY, // timer set to measure reachability of device, i.e. if we don't get an answer after 1s, it is marked as unreachable (for Alexa) - Z_CAT_READ_0006, // Read 0x0006 cluster - Z_CAT_READ_0008, // Read 0x0008 cluster - Z_CAT_READ_0102, // Read 0x0300 cluster - Z_CAT_READ_0300, // Read 0x0300 cluster + // Below will clear based on device + cluster pair. + Z_CLEAR_DEVICE_CLUSTER, + Z_CAT_READ_CLUSTER, + // Below will clear based on device + cluster + endpoint + Z_CLEAR_DEVICE_CLUSTER_ENDPOINT, + Z_CAT_EP_DESC, // read endpoint descriptor to gather clusters + Z_CAT_BIND, // send auto-binding to coordinator + Z_CAT_CONFIG_ATTR, // send a config attribute reporting request + Z_CAT_READ_ATTRIBUTE, // read a single attribute } Z_Def_Category; -const uint32_t Z_CAT_REACHABILITY_TIMEOUT = 1000; // 1000 ms or 1s +const uint32_t Z_CAT_REACHABILITY_TIMEOUT = 2000; // 1000 ms or 1s typedef struct Z_Deferred { // below are per device timers, used for example to query the new state of the device @@ -258,8 +270,9 @@ public: bool isHueBulbHidden(uint16_t shortaddr) const ; // Timers - void resetTimersForDevice(uint16_t shortaddr, uint16_t groupaddr, uint8_t category); + void resetTimersForDevice(uint16_t shortaddr, uint16_t groupaddr, uint8_t category, uint16_t cluster = 0xFFFF, uint8_t endpoint = 0xFF); void setTimer(uint16_t shortaddr, uint16_t groupaddr, uint32_t wait_ms, uint16_t cluster, uint8_t endpoint, uint8_t category, uint32_t value, Z_DeviceTimer func); + void queueTimer(uint16_t shortaddr, uint16_t groupaddr, uint32_t wait_ms, uint16_t cluster, uint8_t endpoint, uint8_t category, uint32_t value, Z_DeviceTimer func); void runTimer(void); // Append or clear attributes Json structure @@ -723,12 +736,17 @@ bool Z_Devices::isHueBulbHidden(uint16_t shortaddr) const { // Deferred actions // 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) { +// Only with specific cluster number or for all clusters if cluster == 0xFFFF +void Z_Devices::resetTimersForDevice(uint16_t shortaddr, uint16_t groupaddr, uint8_t category, uint16_t cluster, uint8_t endpoint) { // iterate the list of deferred, and remove any linked to the shortaddr for (auto & defer : _deferred) { if ((defer.shortaddr == shortaddr) && (defer.groupaddr == groupaddr)) { if ((0xFF == category) || (defer.category == category)) { - _deferred.remove(&defer); + if ((0xFFFF == cluster) || (defer.cluster == cluster)) { + if ((0xFF == endpoint) || (defer.endpoint == endpoint)) { + _deferred.remove(&defer); + } + } } } } @@ -737,8 +755,8 @@ void Z_Devices::resetTimersForDevice(uint16_t shortaddr, uint16_t groupaddr, uin // Set timer for a specific device void Z_Devices::setTimer(uint16_t shortaddr, uint16_t groupaddr, uint32_t wait_ms, uint16_t cluster, uint8_t endpoint, uint8_t category, uint32_t value, Z_DeviceTimer func) { // First we remove any existing timer for same device in same category, except for category=0x00 (they need to happen anyway) - if (category) { // if category == 0, we leave all previous - resetTimersForDevice(shortaddr, groupaddr, category); // remove any cluster + if (category >= Z_CLEAR_DEVICE) { // if category == 0, we leave all previous timers + resetTimersForDevice(shortaddr, groupaddr, category, category >= Z_CLEAR_DEVICE_CLUSTER ? cluster : 0xFFFF, category >= Z_CLEAR_DEVICE_CLUSTER_ENDPOINT ? endpoint : 0xFF); // remove any cluster } // Now create the new timer @@ -753,6 +771,21 @@ void Z_Devices::setTimer(uint16_t shortaddr, uint16_t groupaddr, uint32_t wait_m func }; } +// Set timer after the already queued events +// I.e. the wait_ms is not counted from now, but from the last event queued, which is 'now' or in the future +void Z_Devices::queueTimer(uint16_t shortaddr, uint16_t groupaddr, uint32_t wait_ms, uint16_t cluster, uint8_t endpoint, uint8_t category, uint32_t value, Z_DeviceTimer func) { + Z_Device & device = getShortAddr(shortaddr); + uint32_t now_millis = millis(); + if (TimeReached(device.defer_last_message_sent)) { + device.defer_last_message_sent = now_millis; + } + // defer_last_message_sent equals now or a value in the future + device.defer_last_message_sent += wait_ms; + + // for queueing we don't clear the backlog, so we force category to Z_CAT_ALWAYS + setTimer(shortaddr, groupaddr, (device.defer_last_message_sent - now_millis), cluster, endpoint, Z_CAT_ALWAYS, value, func); +} + // 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) { diff --git a/tasmota/xdrv_23_zigbee_5_converters.ino b/tasmota/xdrv_23_zigbee_5_converters.ino index 1155a6f2e..e70e6702d 100644 --- a/tasmota/xdrv_23_zigbee_5_converters.ino +++ b/tasmota/xdrv_23_zigbee_5_converters.ino @@ -124,6 +124,16 @@ uint16_t CxToCluster(uint8_t cx) { } return 0xFFFF; } + +uint8_t ClusterToCx(uint16_t cluster) { + for (uint8_t i=0; icluster_short)); + uint16_t conv_attr_id = pgm_read_word(&converter->attribute); + + if ((conv_cluster == cluster) && (conv_attr_id == attr_id)) { + if (multiplier) { *multiplier = pgm_read_byte(&converter->multiplier); } + if (attr_type) { *attr_type = pgm_read_byte(&converter->type); } + return (const __FlashStringHelper*) (Z_strings + pgm_read_word(&converter->name_offset)); + } + } + return nullptr; +} + class ZCLFrame { public: @@ -1095,35 +1124,30 @@ void ZCLFrame::generateCallBacks(Z_attribute_list& attr_list) { // Set timers to read back values. // If it's a device address, also set a timer for reachability test void sendHueUpdate(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster, uint8_t endpoint = 0) { - int32_t z_cat = -1; - uint32_t wait_ms = 0; + uint32_t wait_ms = 0xFFFF; switch (cluster) { case 0x0006: - z_cat = Z_CAT_READ_0006; wait_ms = 200; // wait 0.2 s break; case 0x0008: - z_cat = Z_CAT_READ_0008; wait_ms = 1050; // wait 1.0 s break; case 0x0102: - z_cat = Z_CAT_READ_0102; wait_ms = 10000; // wait 10.0 s break; case 0x0300: - z_cat = Z_CAT_READ_0300; wait_ms = 1050; // wait 1.0 s break; default: break; } - if (z_cat >= 0) { + if (0xFFFF != wait_ms) { if ((BAD_SHORTADDR != shortaddr) && (0 == endpoint)) { endpoint = zigbee_devices.findFirstEndpoint(shortaddr); } if ((BAD_SHORTADDR == shortaddr) || (endpoint)) { // send if group address or endpoint is known - zigbee_devices.setTimer(shortaddr, groupaddr, wait_ms, cluster, endpoint, z_cat, 0 /* value */, &Z_ReadAttrCallback); + zigbee_devices.queueTimer(shortaddr, groupaddr, wait_ms, cluster, endpoint, Z_CAT_READ_CLUSTER, 0 /* value */, &Z_ReadAttrCallback); if (BAD_SHORTADDR != shortaddr) { // reachability test is not possible for group addresses, since we don't know the list of devices in the group zigbee_devices.setTimer(shortaddr, groupaddr, wait_ms + Z_CAT_REACHABILITY_TIMEOUT, cluster, endpoint, Z_CAT_REACHABILITY, 0 /* value */, &Z_Unreachable); } @@ -1170,16 +1194,27 @@ void ZCLFrame::parseReadAttributes(Z_attribute_list& attr_list) { // ZCL_CONFIGURE_REPORTING_RESPONSE void ZCLFrame::parseConfigAttributes(Z_attribute_list& attr_list) { - uint32_t i = 0; uint32_t len = _payload.len(); - uint8_t status = _payload.get8(i); - 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_list attr_config_list; + for (uint32_t i=0; len >= i+4; i+=4) { + uint8_t status = _payload.get8(i); + uint16_t attr_id = _payload.get8(i+2); + + 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()); + + const __FlashStringHelper* attr_name = zigbeeFindAttributeById(_cluster_id, attr_id, nullptr, nullptr); + if (attr_name) { + attr_config_list.addAttribute(attr_name).setStrRaw(attr_config_response.toString(true).c_str()); + } else { + attr_config_list.addAttribute(_cluster_id, attr_id).setStrRaw(attr_config_response.toString(true).c_str()); + } + } Z_attribute &attr_1 = attr_list.addAttribute(F("ConfigResponse")); - attr_1.setStrRaw(attr_config_response.toString(true).c_str()); + attr_1.setStrRaw(attr_config_list.toString(true).c_str()); } // ZCL_READ_REPORTING_CONFIGURATION_RESPONSE @@ -1534,11 +1569,10 @@ void ZCLFrame::syntheticAqaraVibration(class Z_attribute_list &attr_list, class } /// 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) { +void 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 } // ====================================================================== diff --git a/tasmota/xdrv_23_zigbee_6_commands.ino b/tasmota/xdrv_23_zigbee_6_commands.ino index a32f637c4..947e2ffe0 100644 --- a/tasmota/xdrv_23_zigbee_6_commands.ino +++ b/tasmota/xdrv_23_zigbee_6_commands.ino @@ -148,7 +148,7 @@ const uint8_t CLUSTER_0009[] = { ZLE(0x0000) }; // AlarmCount const uint8_t CLUSTER_0300[] = { ZLE(0x0000), ZLE(0x0001), ZLE(0x0003), ZLE(0x0004), ZLE(0x0007), ZLE(0x0008) }; // Hue, Sat, X, Y, CT, ColorMode // This callback is registered after a cluster specific command and sends a read command for the same cluster -int32_t Z_ReadAttrCallback(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster, uint8_t endpoint, uint32_t value) { +void Z_ReadAttrCallback(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster, uint8_t endpoint, uint32_t value) { size_t attrs_len = 0; const uint8_t* attrs = nullptr; @@ -188,16 +188,14 @@ int32_t Z_ReadAttrCallback(uint16_t shortaddr, uint16_t groupaddr, uint16_t clus attrs, attrs_len })); } - return 0; // Fix GCC 10.1 warning } // This callback is registered after a an attribute read command was made to a light, and fires if we don't get any response after 1000 ms -int32_t Z_Unreachable(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster, uint8_t endpoint, uint32_t value) { +void Z_Unreachable(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster, uint8_t endpoint, uint32_t value) { if (BAD_SHORTADDR != shortaddr) { zigbee_devices.setReachable(shortaddr, false); // mark device as reachable } - return 0; // Fix GCC 10.1 warning } // returns true if char is 'x', 'y' or 'z' @@ -349,6 +347,10 @@ void convertClusterSpecific(class Z_attribute_list &attr_list, uint16_t cluster, if ((0 != xyz.z) && (0xFF != xyz.z)) { attr_list.addAttribute(command_name, PSTR("Zone")).setUInt(xyz.z); } + // for now convert alamrs 1 and 2 to Occupancy + // TODO we may only do this conversion to ZoneType == 0x000D 'Motion Sensor' + // Occupancy is 0406/0000 of type Zmap8 + attr_list.addAttribute(0x0406, 0x0000).setUInt((xyz.x) & 0x01 ? 1 : 0); break; case 0x00040000: case 0x00040001: diff --git a/tasmota/xdrv_23_zigbee_8_parsers.ino b/tasmota/xdrv_23_zigbee_8_parsers.ino index d800eaf71..788c72e88 100644 --- a/tasmota/xdrv_23_zigbee_8_parsers.ino +++ b/tasmota/xdrv_23_zigbee_8_parsers.ino @@ -533,7 +533,11 @@ int32_t Z_ReceiveActiveEp(int32_t res, const class SBuffer &buf) { #endif for (uint32_t i = 0; i < activeEpCount; i++) { - zigbee_devices.addEndpoint(nwkAddr, activeEpList[i]); + uint8_t ep = activeEpList[i]; + zigbee_devices.addEndpoint(nwkAddr, ep); + if ((i < 4) && (ep < 0x10)) { + zigbee_devices.queueTimer(nwkAddr, 0 /* groupaddr */, 1500, ep /* fake cluster as ep */, ep, Z_CAT_EP_DESC, 0 /* value */, &Z_SendSimpleDescReq); + } } Response_P(PSTR("{\"" D_JSON_ZIGBEE_STATE "\":{" @@ -546,7 +550,132 @@ int32_t Z_ReceiveActiveEp(int32_t res, const class SBuffer &buf) { ResponseAppend_P(PSTR("]}}")); MqttPublishPrefixTopicRulesProcess_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEEZCL_RECEIVED)); - Z_SendAFInfoRequest(nwkAddr); // probe for ModelId and ManufId + Z_SendDeviceInfoRequest(nwkAddr); // probe for ModelId and ManufId + + return -1; +} + +// list of clusters that need bindings +const uint8_t Z_bindings[] PROGMEM = { + Cx0001, Cx0006, Cx0008, Cx0300, + Cx0400, Cx0402, Cx0403, Cx0405, Cx0406, + Cx0500, +}; + +int32_t Z_ClusterToCxBinding(uint16_t cluster) { + uint8_t cx = ClusterToCx(cluster); + for (uint32_t i=0; i= 0) { + bitSet(cluster_map, found_cx); + bitSet(cluster_in_map, found_cx); + } + } + // scan out clusters + for (uint32_t i=0; i= 0) { + bitSet(cluster_map, found_cx); + } + } + + // if IAS device, request the device type + if (bitRead(cluster_map, Z_ClusterToCxBinding(0x0500))) { + // send a read command to cluster 0x0500, attribute 0x0001 (ZoneType) - to read the type of sensor + zigbee_devices.queueTimer(shortaddr, 0 /* groupaddr */, 2000, 0x0500, endpoint, Z_CAT_READ_ATTRIBUTE, 0x0001, &Z_SendSingleAttributeRead); + } + + // enqueue bind requests + for (uint32_t i=0; i 0) { ResponseAppend_P(PSTR(",")); } + ResponseAppend_P(PSTR("\"0x%04X\""), buf.get16(numInIndex + i*2)); + } + ResponseAppend_P(PSTR("],\"OutClusters\":[")); + for (uint32_t i = 0; i < numOutCluster; i++) { + if (i > 0) { ResponseAppend_P(PSTR(",")); } + ResponseAppend_P(PSTR("\"0x%04X\""), buf.get16(numOutIndex + i*2)); + } + ResponseAppend_P(PSTR("]}}")); + MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEEZCL_RECEIVED)); + XdrvRulesProcess(); + } return -1; } @@ -831,7 +960,7 @@ int32_t Z_MgmtBindRsp(int32_t res, const class SBuffer &buf) { //uint64_t srcaddr = buf.get16(idx); // unused uint8_t srcep = buf.get8(idx + 8); - uint8_t cluster = buf.get16(idx + 9); + uint16_t cluster = buf.get16(idx + 9); uint8_t addrmode = buf.get8(idx + 11); uint16_t group = 0x0000; uint64_t dstaddr = 0; @@ -960,9 +1089,31 @@ void Z_SendActiveEpReq(uint16_t shortaddr) { } // -// Send AF Info Request +// Probe the clusters_out on the first endpoint // -void Z_SendAFInfoRequest(uint16_t shortaddr) { +// Send ZDO_SIMPLE_DESC_REQ to get full list of supported Clusters for a specific endpoint +void Z_SendSimpleDescReq(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster, uint8_t endpoint, uint32_t value) { +#ifdef USE_ZIGBEE_ZNP + uint8_t SimpleDescReq[] = { Z_SREQ | Z_ZDO, ZDO_SIMPLE_DESC_REQ, // 2504 + Z_B0(shortaddr), Z_B1(shortaddr), Z_B0(shortaddr), Z_B1(shortaddr), + endpoint }; + ZigbeeZNPSend(SimpleDescReq, sizeof(SimpleDescReq)); +#endif +#ifdef USE_ZIGBEE_EZSP + uint8_t SimpleDescReq[] = { Z_B0(shortaddr), Z_B1(shortaddr), endpoint }; + EZ_SendZDO(shortaddr, ZDO_SIMPLE_DESC_REQ, SimpleDescReq, sizeof(SimpleDescReq)); +#endif +} + + +// +// Send AF Info Request +// Queue requests for the device +// 1. Request for 'ModelId' and 'Manufacturer': 0000/0005, 0000/0006 +// 2. Auto-bind to coordinator: +// Iterate among +// +void Z_SendDeviceInfoRequest(uint16_t shortaddr) { uint8_t endpoint = zigbee_devices.findFirstEndpoint(shortaddr); if (0x00 == endpoint) { endpoint = 0x01; } // if we don't know the endpoint, try 0x01 uint8_t transacid = zigbee_devices.getNextSeqNumber(shortaddr); @@ -983,6 +1134,170 @@ void Z_SendAFInfoRequest(uint16_t shortaddr) { })); } +// +// Send sing attribute read request in Timer +// +void Z_SendSingleAttributeRead(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster, uint8_t endpoint, uint32_t value) { + uint8_t transacid = zigbee_devices.getNextSeqNumber(shortaddr); + uint8_t InfoReq[2] = { Z_B0(value), Z_B1(value) }; // list of single attribute + + ZigbeeZCLSend_Raw(ZigbeeZCLSendMessage({ + shortaddr, + 0x0000, /* group */ + cluster /*cluster*/, + endpoint, + ZCL_READ_ATTRIBUTES, + 0x0000, /* manuf */ + false /* not cluster specific */, + true /* response */, + transacid, /* zcl transaction id */ + InfoReq, sizeof(InfoReq) + })); +} + +// +// Auto-bind some clusters to the coordinator's endpoint 0x01 +// +void Z_AutoBind(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster, uint8_t endpoint, uint32_t value) { + uint64_t srcLongAddr = zigbee_devices.getDeviceLongAddr(shortaddr); + + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "auto-bind `ZbBind {\"Device\":\"0x%04X\",\"Endpoint\":%d,\"Cluster\":\"0x%04X\"}`"), + shortaddr, endpoint, cluster); +#ifdef USE_ZIGBEE_ZNP + SBuffer buf(34); + buf.add8(Z_SREQ | Z_ZDO); + buf.add8(ZDO_BIND_REQ); + buf.add16(shortaddr); + buf.add64(srcLongAddr); + buf.add8(endpoint); + buf.add16(cluster); + buf.add8(Z_Addr_IEEEAddress); // DstAddrMode - 0x03 = ADDRESS_64_BIT + buf.add64(localIEEEAddr); + buf.add8(0x01); // toEndpoint + + ZigbeeZNPSend(buf.getBuffer(), buf.len()); +#endif // USE_ZIGBEE_ZNP + +#ifdef USE_ZIGBEE_EZSP + SBuffer buf(24); + + // ZDO message payload (see Zigbee spec 2.4.3.2.2) + buf.add64(srcLongAddr); + buf.add8(endpoint); + buf.add16(cluster); + buf.add8(Z_Addr_IEEEAddress); // DstAddrMode - 0x03 = ADDRESS_64_BIT + buf.add64(localIEEEAddr); + buf.add8(0x01); // toEndpoint + + EZ_SendZDO(shortaddr, ZDO_BIND_REQ, buf.buf(), buf.len()); +#endif // USE_ZIGBEE_EZSP +} + +// +// Auto-bind some clusters to the coordinator's endpoint 0x01 +// + +// the structure below indicates which attributes need to be configured for attribute reporting +typedef struct Z_autoAttributeReporting_t { + uint16_t cluster; + uint16_t attr_id; + uint16_t min_interval; // minimum interval in seconds (consecutive reports won't happen before this value) + uint16_t max_interval; // maximum interval in seconds (attribut will always be reported after this interval) + float report_change; // for non discrete attributes, the value change that triggers a report +} Z_autoAttributeReporting_t; + +// Note the attribute must be registered in the converter list, used to retrieve the type of the attribute +const Z_autoAttributeReporting_t Z_autoAttributeReporting[] PROGMEM = { + { 0x0001, 0x0020, 15*60, 15*60, 0.1 }, // BatteryVoltage + { 0x0001, 0x0021, 15*60, 15*60, 1 }, // BatteryPercentage + { 0x0006, 0x0000, 1, 60*60, 0 }, // Power + { 0x0008, 0x0000, 1, 60*60, 5 }, // Dimmer + { 0x0300, 0x0000, 1, 60*60, 5 }, // Hue + { 0x0300, 0x0001, 1, 60*60, 5 }, // Sat + { 0x0300, 0x0003, 1, 60*60, 100 }, // X + { 0x0300, 0x0004, 1, 60*60, 100 }, // Y + { 0x0300, 0x0007, 1, 60*60, 5 }, // CT + { 0x0300, 0x0008, 1, 60*60, 0 }, // ColorMode + { 0x0400, 0x0000, 10, 60*60, 5 }, // Illuminance (5 lux) + { 0x0402, 0x0000, 30, 60*60, 0.2 }, // Temperature (0.2 °C) + { 0x0403, 0x0000, 30, 60*60, 1 }, // Pressure (1 hPa) + { 0x0405, 0x0000, 30, 60*60, 1.0 }, // Humidity (1 %) + { 0x0406, 0x0000, 10, 60*60, 0 }, // Occupancy + { 0x0500, 0x0002, 1, 60*60, 0 }, // ZoneStatus +}; + +// +// Called by Device Auto-config +// Configures default values for the most common Attribute Rerporting configurations +// +// Note: must be of type `Z_DeviceTimer` +void Z_AutoConfigReportingForCluster(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster, uint8_t endpoint, uint32_t value) { + // Buffer, max 12 bytes per attribute + SBuffer buf(12*6); + + + Response_P(PSTR("ZbSend {\"Device\":\"0x%04X\",\"Config\":{"), shortaddr); + + boolean comma = false; + for (uint32_t i=0; i 0) { + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "auto-bind `%s`"), mqtt_data); + ZigbeeZCLSend_Raw(ZigbeeZCLSendMessage({ + shortaddr, + 0x0000, /* group */ + cluster /*cluster*/, + endpoint, + ZCL_CONFIGURE_REPORTING, + 0x0000, /* manuf */ + false /* not cluster specific */, + false /* no response */, + zigbee_devices.getNextSeqNumber(shortaddr), /* zcl transaction id */ + buf.buf(), buf.len() + })); + } +} // // Handle trustCenterJoinHandler @@ -1189,6 +1504,8 @@ int32_t EZ_IncomingMessage(int32_t res, const class SBuffer &buf) { return Z_ReceiveActiveEp(res, zdo_buf); case ZDO_IEEE_addr_rsp: return Z_ReceiveIEEEAddr(res, zdo_buf); + case ZDO_Simple_Desc_rsp: + return Z_ReceiveSimpleDesc(res, zdo_buf); case ZDO_Bind_rsp: return Z_BindRsp(res, zdo_buf); case ZDO_Unbind_rsp: @@ -1279,9 +1596,8 @@ int32_t EZ_Recv_Default(int32_t res, const class SBuffer &buf) { \*********************************************************************************************/ // 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) { +void Z_PublishAttributes(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster, uint8_t endpoint, uint32_t value) { zigbee_devices.jsonPublishFlush(shortaddr); - return 1; } /*********************************************************************************************\ @@ -1363,6 +1679,7 @@ const Z_Dispatcher Z_DispatchTable[] PROGMEM = { { { Z_AREQ | Z_ZDO, ZDO_PERMIT_JOIN_IND }, &ZNP_ReceivePermitJoinStatus }, // 45CB { { Z_AREQ | Z_ZDO, ZDO_NODE_DESC_RSP }, &ZNP_ReceiveNodeDesc }, // 4582 { { Z_AREQ | Z_ZDO, ZDO_ACTIVE_EP_RSP }, &Z_ReceiveActiveEp }, // 4585 + { { Z_AREQ | Z_ZDO, ZDO_SIMPLE_DESC_RSP}, &Z_ReceiveSimpleDesc}, // 4584 { { Z_AREQ | Z_ZDO, ZDO_IEEE_ADDR_RSP }, &Z_ReceiveIEEEAddr }, // 4581 { { Z_AREQ | Z_ZDO, ZDO_BIND_RSP }, &Z_BindRsp }, // 45A1 { { Z_AREQ | Z_ZDO, ZDO_UNBIND_RSP }, &Z_UnbindRsp }, // 45A2 @@ -1413,11 +1730,11 @@ void Z_Query_Bulb(uint16_t shortaddr, uint32_t &wait_ms) { uint8_t endpoint = zigbee_devices.findFirstEndpoint(shortaddr); if (endpoint) { // send only if we know the endpoint - zigbee_devices.setTimer(shortaddr, 0 /* groupaddr */, wait_ms, 0x0006, endpoint, Z_CAT_NONE, 0 /* value */, &Z_ReadAttrCallback); + zigbee_devices.setTimer(shortaddr, 0 /* groupaddr */, wait_ms, 0x0006, endpoint, Z_CAT_READ_CLUSTER, 0 /* value */, &Z_ReadAttrCallback); wait_ms += inter_message_ms; - zigbee_devices.setTimer(shortaddr, 0 /* groupaddr */, wait_ms, 0x0008, endpoint, Z_CAT_NONE, 0 /* value */, &Z_ReadAttrCallback); + zigbee_devices.setTimer(shortaddr, 0 /* groupaddr */, wait_ms, 0x0008, endpoint, Z_CAT_READ_CLUSTER, 0 /* value */, &Z_ReadAttrCallback); wait_ms += inter_message_ms; - zigbee_devices.setTimer(shortaddr, 0 /* groupaddr */, wait_ms, 0x0300, endpoint, Z_CAT_NONE, 0 /* value */, &Z_ReadAttrCallback); + zigbee_devices.setTimer(shortaddr, 0 /* groupaddr */, wait_ms, 0x0300, endpoint, Z_CAT_READ_CLUSTER, 0 /* value */, &Z_ReadAttrCallback); wait_ms += inter_message_ms; zigbee_devices.setTimer(shortaddr, 0, wait_ms + Z_CAT_REACHABILITY_TIMEOUT, 0, endpoint, Z_CAT_REACHABILITY, 0 /* value */, &Z_Unreachable); wait_ms += 1000; // wait 1 second between devices From 828490a1b20495dda963dde01c78a6a8a9225638 Mon Sep 17 00:00:00 2001 From: Jason2866 <24528715+Jason2866@users.noreply.github.com> Date: Sat, 12 Sep 2020 11:15:46 +0200 Subject: [PATCH 53/55] override not needed --- .github/workflows/CI_github_ESP32.yml | 90 +++++++++----------------- .github/workflows/Tasmota_build.yml | 93 +++++++++------------------ 2 files changed, 61 insertions(+), 122 deletions(-) diff --git a/.github/workflows/CI_github_ESP32.yml b/.github/workflows/CI_github_ESP32.yml index cc9af6f19..64fbd39b2 100644 --- a/.github/workflows/CI_github_ESP32.yml +++ b/.github/workflows/CI_github_ESP32.yml @@ -17,8 +17,7 @@ jobs: platformio upgrade --dev platformio update - name: Run PlatformIO - run: | - mv -f platformio_override_sample.ini platformio_override.ini + run: | platformio run -e tasmota32 tasmota32-webcam: @@ -34,8 +33,7 @@ jobs: platformio upgrade --dev platformio update - name: Run PlatformIO - run: | - mv -f platformio_override_sample.ini platformio_override.ini + run: | platformio run -e tasmota32-webcam tasmota32-minimal: @@ -51,8 +49,7 @@ jobs: platformio upgrade --dev platformio update - name: Run PlatformIO - run: | - mv -f platformio_override_sample.ini platformio_override.ini + run: | platformio run -e tasmota32-minimal tasmota32-lite: runs-on: ubuntu-latest @@ -67,8 +64,7 @@ jobs: platformio upgrade --dev platformio update - name: Run PlatformIO - run: | - mv -f platformio_override_sample.ini platformio_override.ini + run: | platformio run -e tasmota32-lite tasmota32-knx: runs-on: ubuntu-latest @@ -83,8 +79,7 @@ jobs: platformio upgrade --dev platformio update - name: Run PlatformIO - run: | - mv -f platformio_override_sample.ini platformio_override.ini + run: | platformio run -e tasmota32-knx tasmota32-sensors: @@ -100,8 +95,7 @@ jobs: platformio upgrade --dev platformio update - name: Run PlatformIO - run: | - mv -f platformio_override_sample.ini platformio_override.ini + run: | platformio run -e tasmota32-sensors tasmota32-display: runs-on: ubuntu-latest @@ -116,8 +110,7 @@ jobs: platformio upgrade --dev platformio update - name: Run PlatformIO - run: | - mv -f platformio_override_sample.ini platformio_override.ini + run: | platformio run -e tasmota32-display tasmota32-ir: runs-on: ubuntu-latest @@ -132,8 +125,7 @@ jobs: platformio upgrade --dev platformio update - name: Run PlatformIO - run: | - mv -f platformio_override_sample.ini platformio_override.ini + run: | platformio run -e tasmota32-ir tasmota32-BG: runs-on: ubuntu-latest @@ -148,8 +140,7 @@ jobs: platformio upgrade --dev platformio update - name: Run PlatformIO - run: | - mv -f platformio_override_sample.ini platformio_override.ini + run: | platformio run -e tasmota32-BG tasmota32-BR: @@ -165,8 +156,7 @@ jobs: platformio upgrade --dev platformio update - name: Run PlatformIO - run: | - mv -f platformio_override_sample.ini platformio_override.ini + run: | platformio run -e tasmota32-BR tasmota32-CN: runs-on: ubuntu-latest @@ -181,8 +171,7 @@ jobs: platformio upgrade --dev platformio update - name: Run PlatformIO - run: | - mv -f platformio_override_sample.ini platformio_override.ini + run: | platformio run -e tasmota32-CN tasmota32-CZ: runs-on: ubuntu-latest @@ -197,8 +186,7 @@ jobs: platformio upgrade --dev platformio update - name: Run PlatformIO - run: | - mv -f platformio_override_sample.ini platformio_override.ini + run: | platformio run -e tasmota32-CZ tasmota32-DE: runs-on: ubuntu-latest @@ -213,8 +201,7 @@ jobs: platformio upgrade --dev platformio update - name: Run PlatformIO - run: | - mv -f platformio_override_sample.ini platformio_override.ini + run: | platformio run -e tasmota32-DE tasmota32-ES: @@ -230,8 +217,7 @@ jobs: platformio upgrade --dev platformio update - name: Run PlatformIO - run: | - mv -f platformio_override_sample.ini platformio_override.ini + run: | platformio run -e tasmota32-ES tasmota32-FR: runs-on: ubuntu-latest @@ -246,8 +232,7 @@ jobs: platformio upgrade --dev platformio update - name: Run PlatformIO - run: | - mv -f platformio_override_sample.ini platformio_override.ini + run: | platformio run -e tasmota32-FR tasmota32-GR: runs-on: ubuntu-latest @@ -262,8 +247,7 @@ jobs: platformio upgrade --dev platformio update - name: Run PlatformIO - run: | - mv -f platformio_override_sample.ini platformio_override.ini + run: | platformio run -e tasmota32-GR tasmota32-HE: runs-on: ubuntu-latest @@ -278,8 +262,7 @@ jobs: platformio upgrade --dev platformio update - name: Run PlatformIO - run: | - mv -f platformio_override_sample.ini platformio_override.ini + run: | platformio run -e tasmota32-HE tasmota32-HU: @@ -295,8 +278,7 @@ jobs: platformio upgrade --dev platformio update - name: Run PlatformIO - run: | - mv -f platformio_override_sample.ini platformio_override.ini + run: | platformio run -e tasmota32-HU tasmota32-IT: runs-on: ubuntu-latest @@ -311,8 +293,7 @@ jobs: platformio upgrade --dev platformio update - name: Run PlatformIO - run: | - mv -f platformio_override_sample.ini platformio_override.ini + run: | platformio run -e tasmota32-IT tasmota32-KO: runs-on: ubuntu-latest @@ -327,8 +308,7 @@ jobs: platformio upgrade --dev platformio update - name: Run PlatformIO - run: | - mv -f platformio_override_sample.ini platformio_override.ini + run: | platformio run -e tasmota32-KO tasmota32-NL: runs-on: ubuntu-latest @@ -343,8 +323,7 @@ jobs: platformio upgrade --dev platformio update - name: Run PlatformIO - run: | - mv -f platformio_override_sample.ini platformio_override.ini + run: | platformio run -e tasmota32-NL tasmota32-PL: @@ -360,8 +339,7 @@ jobs: platformio upgrade --dev platformio update - name: Run PlatformIO - run: | - mv -f platformio_override_sample.ini platformio_override.ini + run: | platformio run -e tasmota32-PL tasmota32-PT: runs-on: ubuntu-latest @@ -376,8 +354,7 @@ jobs: platformio upgrade --dev platformio update - name: Run PlatformIO - run: | - mv -f platformio_override_sample.ini platformio_override.ini + run: | platformio run -e tasmota32-PT tasmota32-RO: runs-on: ubuntu-latest @@ -392,8 +369,7 @@ jobs: platformio upgrade --dev platformio update - name: Run PlatformIO - run: | - mv -f platformio_override_sample.ini platformio_override.ini + run: | platformio run -e tasmota32-RO tasmota32-RU: runs-on: ubuntu-latest @@ -408,8 +384,7 @@ jobs: platformio upgrade --dev platformio update - name: Run PlatformIO - run: | - mv -f platformio_override_sample.ini platformio_override.ini + run: | platformio run -e tasmota32-RU tasmota32-SE: @@ -425,8 +400,7 @@ jobs: platformio upgrade --dev platformio update - name: Run PlatformIO - run: | - mv -f platformio_override_sample.ini platformio_override.ini + run: | platformio run -e tasmota32-SE tasmota32-SK: runs-on: ubuntu-latest @@ -441,8 +415,7 @@ jobs: platformio upgrade --dev platformio update - name: Run PlatformIO - run: | - mv -f platformio_override_sample.ini platformio_override.ini + run: | platformio run -e tasmota32-SK tasmota32-TR: runs-on: ubuntu-latest @@ -457,8 +430,7 @@ jobs: platformio upgrade --dev platformio update - name: Run PlatformIO - run: | - mv -f platformio_override_sample.ini platformio_override.ini + run: | platformio run -e tasmota32-TR tasmota32-TW: runs-on: ubuntu-latest @@ -473,8 +445,7 @@ jobs: platformio upgrade --dev platformio update - name: Run PlatformIO - run: | - mv -f platformio_override_sample.ini platformio_override.ini + run: | platformio run -e tasmota32-TW tasmota32-UK: @@ -490,6 +461,5 @@ jobs: platformio upgrade --dev platformio update - name: Run PlatformIO - run: | - mv -f platformio_override_sample.ini platformio_override.ini + run: | platformio run -e tasmota32-UK diff --git a/.github/workflows/Tasmota_build.yml b/.github/workflows/Tasmota_build.yml index 5d5665715..4a890b89d 100644 --- a/.github/workflows/Tasmota_build.yml +++ b/.github/workflows/Tasmota_build.yml @@ -750,8 +750,7 @@ jobs: platformio upgrade --dev platformio update - name: Run PlatformIO - run: | - mv -f platformio_override_sample.ini platformio_override.ini + run: | platformio run -e tasmota32 - uses: actions/upload-artifact@v2 with: @@ -774,8 +773,7 @@ jobs: platformio upgrade --dev platformio update - name: Run PlatformIO - run: | - mv -f platformio_override_sample.ini platformio_override.ini + run: | platformio run -e tasmota32-minimal - uses: actions/upload-artifact@v2 with: @@ -798,8 +796,7 @@ jobs: platformio upgrade --dev platformio update - name: Run PlatformIO - run: | - mv -f platformio_override_sample.ini platformio_override.ini + run: | platformio run -e tasmota32-lite - uses: actions/upload-artifact@v2 with: @@ -822,8 +819,7 @@ jobs: platformio upgrade --dev platformio update - name: Run PlatformIO - run: | - mv -f platformio_override_sample.ini platformio_override.ini + run: | platformio run -e tasmota32-webcam - uses: actions/upload-artifact@v2 with: @@ -846,8 +842,7 @@ jobs: platformio upgrade --dev platformio update - name: Run PlatformIO - run: | - mv -f platformio_override_sample.ini platformio_override.ini + run: | platformio run -e tasmota32-knx - uses: actions/upload-artifact@v2 with: @@ -870,8 +865,7 @@ jobs: platformio upgrade --dev platformio update - name: Run PlatformIO - run: | - mv -f platformio_override_sample.ini platformio_override.ini + run: | platformio run -e tasmota32-sensors - uses: actions/upload-artifact@v2 with: @@ -894,8 +888,7 @@ jobs: platformio upgrade --dev platformio update - name: Run PlatformIO - run: | - mv -f platformio_override_sample.ini platformio_override.ini + run: | platformio run -e tasmota32-display - uses: actions/upload-artifact@v2 with: @@ -918,8 +911,7 @@ jobs: platformio upgrade --dev platformio update - name: Run PlatformIO - run: | - mv -f platformio_override_sample.ini platformio_override.ini + run: | platformio run -e tasmota32-ir - uses: actions/upload-artifact@v2 with: @@ -942,8 +934,7 @@ jobs: platformio upgrade --dev platformio update - name: Run PlatformIO - run: | - mv -f platformio_override_sample.ini platformio_override.ini + run: | platformio run -e tasmota32-ircustom - uses: actions/upload-artifact@v2 with: @@ -966,8 +957,7 @@ jobs: platformio upgrade --dev platformio update - name: Run PlatformIO - run: | - mv -f platformio_override_sample.ini platformio_override.ini + run: | platformio run -e tasmota32-BG - uses: actions/upload-artifact@v2 with: @@ -990,8 +980,7 @@ jobs: platformio upgrade --dev platformio update - name: Run PlatformIO - run: | - mv -f platformio_override_sample.ini platformio_override.ini + run: | platformio run -e tasmota32-BR - uses: actions/upload-artifact@v2 with: @@ -1014,8 +1003,7 @@ jobs: platformio upgrade --dev platformio update - name: Run PlatformIO - run: | - mv -f platformio_override_sample.ini platformio_override.ini + run: | platformio run -e tasmota32-CN - uses: actions/upload-artifact@v2 with: @@ -1038,8 +1026,7 @@ jobs: platformio upgrade --dev platformio update - name: Run PlatformIO - run: | - mv -f platformio_override_sample.ini platformio_override.ini + run: | platformio run -e tasmota32-CZ - uses: actions/upload-artifact@v2 with: @@ -1062,8 +1049,7 @@ jobs: platformio upgrade --dev platformio update - name: Run PlatformIO - run: | - mv -f platformio_override_sample.ini platformio_override.ini + run: | platformio run -e tasmota32-DE - uses: actions/upload-artifact@v2 with: @@ -1086,8 +1072,7 @@ jobs: platformio upgrade --dev platformio update - name: Run PlatformIO - run: | - mv -f platformio_override_sample.ini platformio_override.ini + run: | platformio run -e tasmota32-ES - uses: actions/upload-artifact@v2 with: @@ -1110,8 +1095,7 @@ jobs: platformio upgrade --dev platformio update - name: Run PlatformIO - run: | - mv -f platformio_override_sample.ini platformio_override.ini + run: | platformio run -e tasmota32-FR - uses: actions/upload-artifact@v2 with: @@ -1134,8 +1118,7 @@ jobs: platformio upgrade --dev platformio update - name: Run PlatformIO - run: | - mv -f platformio_override_sample.ini platformio_override.ini + run: | platformio run -e tasmota32-GR - uses: actions/upload-artifact@v2 with: @@ -1158,8 +1141,7 @@ jobs: platformio upgrade --dev platformio update - name: Run PlatformIO - run: | - mv -f platformio_override_sample.ini platformio_override.ini + run: | platformio run -e tasmota32-HE - uses: actions/upload-artifact@v2 with: @@ -1182,8 +1164,7 @@ jobs: platformio upgrade --dev platformio update - name: Run PlatformIO - run: | - mv -f platformio_override_sample.ini platformio_override.ini + run: | platformio run -e tasmota32-HU - uses: actions/upload-artifact@v2 with: @@ -1206,8 +1187,7 @@ jobs: platformio upgrade --dev platformio update - name: Run PlatformIO - run: | - mv -f platformio_override_sample.ini platformio_override.ini + run: | platformio run -e tasmota32-IT - uses: actions/upload-artifact@v2 with: @@ -1230,8 +1210,7 @@ jobs: platformio upgrade --dev platformio update - name: Run PlatformIO - run: | - mv -f platformio_override_sample.ini platformio_override.ini + run: | platformio run -e tasmota32-KO - uses: actions/upload-artifact@v2 with: @@ -1254,8 +1233,7 @@ jobs: platformio upgrade --dev platformio update - name: Run PlatformIO - run: | - mv -f platformio_override_sample.ini platformio_override.ini + run: | platformio run -e tasmota32-NL - uses: actions/upload-artifact@v2 with: @@ -1278,8 +1256,7 @@ jobs: platformio upgrade --dev platformio update - name: Run PlatformIO - run: | - mv -f platformio_override_sample.ini platformio_override.ini + run: | platformio run -e tasmota32-PL - uses: actions/upload-artifact@v2 with: @@ -1302,8 +1279,7 @@ jobs: platformio upgrade --dev platformio update - name: Run PlatformIO - run: | - mv -f platformio_override_sample.ini platformio_override.ini + run: | platformio run -e tasmota32-PT - uses: actions/upload-artifact@v2 with: @@ -1326,8 +1302,7 @@ jobs: platformio upgrade --dev platformio update - name: Run PlatformIO - run: | - mv -f platformio_override_sample.ini platformio_override.ini + run: | platformio run -e tasmota32-RO - uses: actions/upload-artifact@v2 with: @@ -1350,8 +1325,7 @@ jobs: platformio upgrade --dev platformio update - name: Run PlatformIO - run: | - mv -f platformio_override_sample.ini platformio_override.ini + run: | platformio run -e tasmota32-RU - uses: actions/upload-artifact@v2 with: @@ -1374,8 +1348,7 @@ jobs: platformio upgrade --dev platformio update - name: Run PlatformIO - run: | - mv -f platformio_override_sample.ini platformio_override.ini + run: | platformio run -e tasmota32-SE - uses: actions/upload-artifact@v2 with: @@ -1398,8 +1371,7 @@ jobs: platformio upgrade --dev platformio update - name: Run PlatformIO - run: | - mv -f platformio_override_sample.ini platformio_override.ini + run: | platformio run -e tasmota32-SK - uses: actions/upload-artifact@v2 with: @@ -1422,8 +1394,7 @@ jobs: platformio upgrade --dev platformio update - name: Run PlatformIO - run: | - mv -f platformio_override_sample.ini platformio_override.ini + run: | platformio run -e tasmota32-TR - uses: actions/upload-artifact@v2 with: @@ -1446,8 +1417,7 @@ jobs: platformio upgrade --dev platformio update - name: Run PlatformIO - run: | - mv -f platformio_override_sample.ini platformio_override.ini + run: | platformio run -e tasmota32-TW - uses: actions/upload-artifact@v2 with: @@ -1470,8 +1440,7 @@ jobs: platformio upgrade --dev platformio update - name: Run PlatformIO - run: | - mv -f platformio_override_sample.ini platformio_override.ini + run: | platformio run -e tasmota32-UK - uses: actions/upload-artifact@v2 with: From 414cf1f9ac38976f15944108a6fc6ba7a73ab234 Mon Sep 17 00:00:00 2001 From: nicandris Date: Sat, 12 Sep 2020 12:59:51 +0200 Subject: [PATCH 54/55] added support for Aqara button WXKG12LM --- tasmota/xdrv_23_zigbee_5_converters.ino | 27 ++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/tasmota/xdrv_23_zigbee_5_converters.ino b/tasmota/xdrv_23_zigbee_5_converters.ino index e70e6702d..5e3140afd 100644 --- a/tasmota/xdrv_23_zigbee_5_converters.ino +++ b/tasmota/xdrv_23_zigbee_5_converters.ino @@ -1494,7 +1494,7 @@ void ZCLFrame::syntheticAqaraCubeOrButton(class Z_attribute_list &attr_list, cla // 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 + } else if (modelId.startsWith(F("lumi.remote"))) { // only for Aqara button WXKG11LM int32_t val = attr.getInt(); const __FlashStringHelper *aqara_click = F("click"); const __FlashStringHelper *aqara_action = F("action"); @@ -1516,6 +1516,31 @@ void ZCLFrame::syntheticAqaraCubeOrButton(class Z_attribute_list &attr_list, cla attr_list.addAttribute(aqara_click).setUInt(val); break; } + } else if (modelId.startsWith(F("lumi.sensor_switch"))) { // only for Aqara button WXKG12LM + int32_t val = attr.getInt(); + const __FlashStringHelper *aqara_click = F("click"); + const __FlashStringHelper *aqara_action = F("action"); + + switch (val) { + case 1: + attr_list.addAttribute(aqara_click).setStr(PSTR("single")); + break; + case 2: + attr_list.addAttribute(aqara_click).setStr(PSTR("double")); + break; + case 16: + attr_list.addAttribute(aqara_action).setStr(PSTR("hold")); + break; + case 17: + attr_list.addAttribute(aqara_action).setStr(PSTR("release")); + break; + case 18: + attr_list.addAttribute(aqara_action).setStr(PSTR("shake")); + break; + default: + attr_list.addAttribute(aqara_click).setUInt(val); + break; + } } } From a951a38d393ba2fb70602a4f6f269dfdb2384c4f Mon Sep 17 00:00:00 2001 From: nicandris Date: Sat, 12 Sep 2020 13:10:35 +0200 Subject: [PATCH 55/55] merged together the 2 aqara buttons --- tasmota/xdrv_23_zigbee_5_converters.ino | 24 ++++-------------------- 1 file changed, 4 insertions(+), 20 deletions(-) diff --git a/tasmota/xdrv_23_zigbee_5_converters.ino b/tasmota/xdrv_23_zigbee_5_converters.ino index 5e3140afd..542aa3a41 100644 --- a/tasmota/xdrv_23_zigbee_5_converters.ino +++ b/tasmota/xdrv_23_zigbee_5_converters.ino @@ -1494,7 +1494,7 @@ void ZCLFrame::syntheticAqaraCubeOrButton(class Z_attribute_list &attr_list, cla // 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 WXKG11LM + } else if (modelId.startsWith(F("lumi.remote")) || modelId.startsWith(F("lumi.sensor_switch"))) { // only for Aqara buttons WXKG11LM & WXKG12LM int32_t val = attr.getInt(); const __FlashStringHelper *aqara_click = F("click"); const __FlashStringHelper *aqara_action = F("action"); @@ -1503,25 +1503,6 @@ void ZCLFrame::syntheticAqaraCubeOrButton(class Z_attribute_list &attr_list, cla case 0: attr_list.addAttribute(aqara_action).setStr(PSTR("hold")); break; - case 1: - attr_list.addAttribute(aqara_click).setStr(PSTR("single")); - break; - case 2: - attr_list.addAttribute(aqara_click).setStr(PSTR("double")); - break; - case 255: - attr_list.addAttribute(aqara_click).setStr(PSTR("release")); - break; - default: - attr_list.addAttribute(aqara_click).setUInt(val); - break; - } - } else if (modelId.startsWith(F("lumi.sensor_switch"))) { // only for Aqara button WXKG12LM - int32_t val = attr.getInt(); - const __FlashStringHelper *aqara_click = F("click"); - const __FlashStringHelper *aqara_action = F("action"); - - switch (val) { case 1: attr_list.addAttribute(aqara_click).setStr(PSTR("single")); break; @@ -1537,6 +1518,9 @@ void ZCLFrame::syntheticAqaraCubeOrButton(class Z_attribute_list &attr_list, cla case 18: attr_list.addAttribute(aqara_action).setStr(PSTR("shake")); break; + case 255: + attr_list.addAttribute(aqara_action).setStr(PSTR("release")); + break; default: attr_list.addAttribute(aqara_click).setUInt(val); break;