diff --git a/tasmota/xdrv_23_zigbee_1z_libs.ino b/tasmota/xdrv_23_zigbee_1z_libs.ino index 1f9012152..8b64baa5b 100644 --- a/tasmota/xdrv_23_zigbee_1z_libs.ino +++ b/tasmota/xdrv_23_zigbee_1z_libs.ino @@ -64,6 +64,46 @@ 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 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 += ']'; + } + void addStrRaw(const char * sval) { + // remove trailing ']' + val.remove(val.length()-1); + if (val.length() > 1) { // if not empty, prefix with comma + val += ','; + } + val += sval; + val += ']'; + } + void addStr(const char * sval) { + addStrRaw(EscapeJSONString(sval).c_str()); + } + String &toString(void) { + return val; + } + +private : + String val; +}; + /*********************************************************************************************\ * * Class for single attribute @@ -79,7 +119,10 @@ enum class Za_type : uint8_t { Za_float, // float 32, uses fval // non-nummericals Za_raw, // bytes buffer, uses bval - Za_str // string, uses sval + Za_str, // string, uses sval + // sub_objects + Za_obj, // json sub-object + Za_arr, // array sub-object (string add-only) }; class Z_attribute { @@ -90,16 +133,18 @@ public: struct { uint16_t cluster; uint16_t attr_id; - } id; - char * key; + } id; + char * key; } key; // attribute value union { - uint32_t uval32; - int32_t ival32; - float fval; - SBuffer* bval; - char* sval; + uint32_t uval32; + int32_t ival32; + float fval; + SBuffer* bval; + char* sval; + class Z_attribute_list * objval; + class Z_json_array * arrval; } val; Za_type type; // uint8_t in size, type of attribute, see above bool key_is_str; // is the key a string? @@ -139,389 +184,67 @@ public: } // 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; - } - } + void freeVal(void); + // free any allocated memoruy for keys - void freeKey(void) { - if (key_is_str && key.key && !key_is_pmem) { delete[] key.key; } - key.key = nullptr; - } + void freeKey(void); // 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); - } - } + void setKeyName(const char * _key, bool pmem = false); // 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 setKeyName(const char * _key, const char * _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; - } + void setKeyId(uint16_t cluster, uint16_t 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 setNone(void); + void setUInt(uint32_t _val); + void setBool(bool _val); + void setInt(int32_t _val); + void setFloat(float _val); - 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; - } + void setBuf(const SBuffer &buf, size_t index, size_t len); // 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; - } + void setStr(const char * _val); inline void setStrRaw(const char * _val) { setStr(_val); val_str_raw = true; } + Z_attribute_list & newAttrList(void); + Z_json_array & newJsonArray(void); + inline bool isNum(void) const { return (type >= Za_type::Za_bool) && (type <= Za_type::Za_float); } inline bool isNone(void) const { return (type == Za_type::Za_none);} // 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; - } + float getFloat(void) const; + int32_t getInt(void) const; + uint32_t getUInt(void) const; + bool getBool(void) const; + const SBuffer * getRaw(void) const; // 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 ""; - } + const char * getStr(void) const; - 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(const Z_attribute & attr2, bool ignore_key_suffix = false) const; + bool equalsKey(uint16_t cluster, uint16_t attr_id, uint8_t suffix = 0) const; + bool equalsKey(const char * name, uint8_t suffix = 0) const; + bool equalsVal(const Z_attribute & attr2) const; + bool equals(const Z_attribute & attr2) const; - 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; - } + String toString(bool prefix_comma = false) const; // 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; - } + void copyVal(const Z_attribute & rhs); 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; - attr_type = rhs.attr_type; - attr_multiplier = rhs.attr_multiplier; - // 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; + void deepCopy(const Z_attribute & rhs); }; /*********************************************************************************************\ @@ -613,6 +336,406 @@ public: bool mergeList(const Z_attribute_list &list2); }; +/*********************************************************************************************\ + * + * Implementation for Z_attribute + * +\*********************************************************************************************/ + +// free any allocated memoruy for keys +void Z_attribute::freeKey(void) { + if (key_is_str && key.key && !key_is_pmem) { delete[] key.key; } + key.key = nullptr; +} + +// set key name +void Z_attribute::setKeyName(const char * _key, bool pmem) { + 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 Z_attribute::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 Z_attribute::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 Z_attribute::setNone(void) { + freeVal(); // free any previously allocated memory + val.uval32 = 0; + type = Za_type::Za_none; +} +void Z_attribute::setUInt(uint32_t _val) { + freeVal(); // free any previously allocated memory + val.uval32 = _val; + type = Za_type::Za_uint; +} +void Z_attribute::setBool(bool _val) { + freeVal(); // free any previously allocated memory + val.uval32 = _val; + type = Za_type::Za_bool; +} +void Z_attribute::setInt(int32_t _val) { + freeVal(); // free any previously allocated memory + val.ival32 = _val; + type = Za_type::Za_int; +} +void Z_attribute::setFloat(float _val) { + freeVal(); // free any previously allocated memory + val.fval = _val; + type = Za_type::Za_float; +} + +void Z_attribute::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 Z_attribute::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; +} + +Z_attribute_list & Z_attribute::newAttrList(void) { + freeVal(); + val.objval = new Z_attribute_list(); + type = Za_type::Za_obj; + return *val.objval; +} + +Z_json_array & Z_attribute::newJsonArray(void) { + freeVal(); + val.arrval = new Z_json_array(); + type = Za_type::Za_arr; + return *val.arrval; +} + +// get num values +float Z_attribute::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 Z_attribute::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 Z_attribute::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 Z_attribute::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 * Z_attribute::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 * Z_attribute::getStr(void) const { + if (Za_type::Za_str == type) { return val.sval; } + return ""; +} + +bool Z_attribute::equalsKey(const Z_attribute & attr2, bool ignore_key_suffix) 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 Z_attribute::equalsKey(uint16_t cluster, uint16_t attr_id, uint8_t suffix) 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 Z_attribute::equalsKey(const char * name, uint8_t suffix) 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 Z_attribute::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; } + } else if (type == Za_type::Za_obj) { + return false; // TODO for now we'll assume sub-objects are always different + } else if (type == Za_type::Za_arr) { + return false; // TODO for now we'll assume sub-objects are always different + } + return true; +} + +bool Z_attribute::equals(const Z_attribute & attr2) const { + return equalsKey(attr2) && equalsVal(attr2); +} + +String Z_attribute::toString(bool prefix_comma) 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; + case Za_type::Za_obj: + res += '{'; + if (val.objval) { + res += val.objval->toString(); + } + res += '}'; + break; + case Za_type::Za_arr: + if (val.arrval) { + res += val.arrval->toString(); + } else { + res += "[]"; + } + break; + } + + return res; +} + +// copy value from one attribute to another, without changing its type +void Z_attribute::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; +} + +// free any allocated memoruy for values +void Z_attribute::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; + case Za_type::Za_obj: + if (val.objval) { delete val.objval; val.objval = nullptr; } + break; + case Za_type::Za_arr: + if (val.arrval) { delete val.arrval; val.arrval = nullptr; } + break; + } +} + +void Z_attribute::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; + attr_type = rhs.attr_type; + attr_multiplier = rhs.attr_multiplier; + // copy value + copyVal(rhs); + // don't touch next pointer +} + +/*********************************************************************************************\ + * + * Implementation for Z_attribute_list + * +\*********************************************************************************************/ // 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(); diff --git a/tasmota/xdrv_23_zigbee_2_devices.ino b/tasmota/xdrv_23_zigbee_2_devices.ino index b84db3e2e..dbc459e38 100644 --- a/tasmota/xdrv_23_zigbee_2_devices.ino +++ b/tasmota/xdrv_23_zigbee_2_devices.ino @@ -992,12 +992,9 @@ String Z_Devices::dumpLightState(uint16_t shortaddr) const { // Dump the internal memory of Zigbee devices // Mode = 1: simple dump of devices addresses -// Mode = 2: simple dump of devices addresses and names -// Mode = 3: Mode 2 + also dump the endpoints, profiles and clusters +// Mode = 2: simple dump of devices addresses and names, endpoints, light String Z_Devices::dump(uint32_t dump_mode, uint16_t status_shortaddr) const { - DynamicJsonBuffer jsonBuffer; - JsonArray& json = jsonBuffer.createArray(); - JsonArray& devices = json; + Z_json_array json_arr; for (const auto & device : _devices) { uint16_t shortaddr = device.shortaddr; @@ -1006,44 +1003,41 @@ String Z_Devices::dump(uint32_t dump_mode, uint16_t status_shortaddr) const { // ignore non-current device, if device specified if ((BAD_SHORTADDR != status_shortaddr) && (status_shortaddr != shortaddr)) { continue; } - JsonObject& dev = devices.createNestedObject(); + Z_attribute_list attr_list; snprintf_P(hex, sizeof(hex), PSTR("0x%04X"), shortaddr); - dev[F(D_JSON_ZIGBEE_DEVICE)] = hex; + attr_list.addAttribute(F(D_JSON_ZIGBEE_DEVICE)).setStr(hex); if (device.friendlyName > 0) { - dev[F(D_JSON_ZIGBEE_NAME)] = (char*) device.friendlyName; + attr_list.addAttribute(F(D_JSON_ZIGBEE_NAME)).setStr(device.friendlyName); } if (2 <= dump_mode) { hex[0] = '0'; // prefix with '0x' hex[1] = 'x'; Uint64toHex(device.longaddr, &hex[2], 64); - dev[F("IEEEAddr")] = hex; + attr_list.addAttribute(F("IEEEAddr")).setStr(hex); if (device.modelId) { - dev[F(D_JSON_MODEL D_JSON_ID)] = device.modelId; + attr_list.addAttribute(F(D_JSON_MODEL D_JSON_ID)).setStr(device.modelId); } int8_t bulbtype = getHueBulbtype(shortaddr); if (bulbtype >= 0) { - dev[F(D_JSON_ZIGBEE_LIGHT)] = bulbtype; // sign extend, 0xFF changed as -1 + attr_list.addAttribute(F(D_JSON_ZIGBEE_LIGHT)).setInt(bulbtype); // sign extend, 0xFF changed as -1 } if (device.manufacturerId) { - dev[F("Manufacturer")] = device.manufacturerId; + attr_list.addAttribute(F("Manufacturer")).setStr(device.manufacturerId); } - JsonArray& dev_endpoints = dev.createNestedArray(F("Endpoints")); + Z_json_array arr_ep; for (uint32_t i = 0; i < endpoints_max; i++) { uint8_t endpoint = device.endpoints[i]; if (0x00 == endpoint) { break; } - - snprintf_P(hex, sizeof(hex), PSTR("0x%02X"), endpoint); - dev_endpoints.add(hex); + arr_ep.add(endpoint); } + attr_list.addAttribute(F("Endpoints")).setStrRaw(arr_ep.toString().c_str()); } + json_arr.addStrRaw(attr_list.toString(true).c_str()); } - String payload = ""; - payload.reserve(200); - json.printTo(payload); - return payload; + return json_arr.toString(); } // Restore a single device configuration based on json export