Merge pull request #9328 from s-hadinger/zigbee_61_dev

Simplified JSON
This commit is contained in:
s-hadinger 2020-09-16 20:43:32 +02:00 committed by GitHub
commit 022315d573
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 497 additions and 380 deletions

View File

@ -64,6 +64,46 @@ uint16_t Z_GetLastGroup(void) { return gZbLastMessage.groupaddr; }
uint16_t Z_GetLastCluster(void) { return gZbLastMessage.cluster; } uint16_t Z_GetLastCluster(void) { return gZbLastMessage.cluster; }
uint8_t Z_GetLastEndpoint(void) { return gZbLastMessage.endpoint; } 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 * Class for single attribute
@ -79,7 +119,10 @@ enum class Za_type : uint8_t {
Za_float, // float 32, uses fval Za_float, // float 32, uses fval
// non-nummericals // non-nummericals
Za_raw, // bytes buffer, uses bval 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 { class Z_attribute {
@ -90,16 +133,18 @@ public:
struct { struct {
uint16_t cluster; uint16_t cluster;
uint16_t attr_id; uint16_t attr_id;
} id; } id;
char * key; char * key;
} key; } key;
// attribute value // attribute value
union { union {
uint32_t uval32; uint32_t uval32;
int32_t ival32; int32_t ival32;
float fval; float fval;
SBuffer* bval; SBuffer* bval;
char* sval; char* sval;
class Z_attribute_list * objval;
class Z_json_array * arrval;
} val; } val;
Za_type type; // uint8_t in size, type of attribute, see above Za_type type; // uint8_t in size, type of attribute, see above
bool key_is_str; // is the key a string? bool key_is_str; // is the key a string?
@ -139,389 +184,67 @@ public:
} }
// free any allocated memoruy for values // free any allocated memoruy for values
void freeVal(void) { 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 // free any allocated memoruy for keys
void freeKey(void) { void freeKey(void);
if (key_is_str && key.key && !key_is_pmem) { delete[] key.key; }
key.key = nullptr;
}
// set key name // set key name
void setKeyName(const char * _key, bool pmem = false) { 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 // provide two entries and concat
void setKeyName(const char * _key, const char * _key2) { 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) { 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 // Setters
void setNone(void) { void setNone(void);
freeVal(); // free any previously allocated memory void setUInt(uint32_t _val);
val.uval32 = 0; void setBool(bool _val);
type = Za_type::Za_none; void setInt(int32_t _val);
} void setFloat(float _val);
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) { 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 // set the string value
// PMEM argument is allowed // PMEM argument is allowed
// string will be copied, so it can be changed later // string will be copied, so it can be changed later
// nullptr is allowed and considered as empty string // nullptr is allowed and considered as empty string
// Note: memory is allocated only if string is non-empty // Note: memory is allocated only if string is non-empty
void setStr(const char * _val) { 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) { inline void setStrRaw(const char * _val) {
setStr(_val); setStr(_val);
val_str_raw = true; 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 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);} inline bool isNone(void) const { return (type == Za_type::Za_none);}
// get num values // get num values
float getFloat(void) const { float getFloat(void) const;
switch (type) { int32_t getInt(void) const;
case Za_type::Za_bool: uint32_t getUInt(void) const;
case Za_type::Za_uint: return (float) val.uval32; bool getBool(void) const;
case Za_type::Za_int: return (float) val.ival32; const SBuffer * getRaw(void) const;
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. // always return a point to a string, if not defined then empty string.
// Never returns nullptr // Never returns nullptr
const char * getStr(void) const { 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 { bool equalsKey(const Z_attribute & attr2, bool ignore_key_suffix = false) const;
// check if keys are equal bool equalsKey(uint16_t cluster, uint16_t attr_id, uint8_t suffix = 0) const;
if (key_is_str != attr2.key_is_str) { return false; } bool equalsKey(const char * name, uint8_t suffix = 0) const;
if (key_is_str) { bool equalsVal(const Z_attribute & attr2) const;
if (strcmp_PP(key.key, attr2.key.key)) { return false; } bool equals(const Z_attribute & attr2) const;
} 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 { String toString(bool prefix_comma = false) 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 // copy value from one attribute to another, without changing its type
void copyVal(const Z_attribute & rhs) { 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: protected:
void deepCopy(const Z_attribute & rhs) { 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;
}; };
/*********************************************************************************************\ /*********************************************************************************************\
@ -613,6 +336,406 @@ public:
bool mergeList(const Z_attribute_list &list2); 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 // 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 & Z_attribute_list::addAttribute(uint16_t cluster, uint16_t attr_id, uint8_t suffix) {
Z_attribute & attr = addToLast(); Z_attribute & attr = addToLast();

View File

@ -992,12 +992,9 @@ String Z_Devices::dumpLightState(uint16_t shortaddr) const {
// Dump the internal memory of Zigbee devices // Dump the internal memory of Zigbee devices
// Mode = 1: simple dump of devices addresses // Mode = 1: simple dump of devices addresses
// Mode = 2: simple dump of devices addresses and names // Mode = 2: simple dump of devices addresses and names, endpoints, light
// Mode = 3: Mode 2 + also dump the endpoints, profiles and clusters
String Z_Devices::dump(uint32_t dump_mode, uint16_t status_shortaddr) const { String Z_Devices::dump(uint32_t dump_mode, uint16_t status_shortaddr) const {
DynamicJsonBuffer jsonBuffer; Z_json_array json_arr;
JsonArray& json = jsonBuffer.createArray();
JsonArray& devices = json;
for (const auto & device : _devices) { for (const auto & device : _devices) {
uint16_t shortaddr = device.shortaddr; 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 // ignore non-current device, if device specified
if ((BAD_SHORTADDR != status_shortaddr) && (status_shortaddr != shortaddr)) { continue; } 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); 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) { 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) { if (2 <= dump_mode) {
hex[0] = '0'; // prefix with '0x' hex[0] = '0'; // prefix with '0x'
hex[1] = 'x'; hex[1] = 'x';
Uint64toHex(device.longaddr, &hex[2], 64); Uint64toHex(device.longaddr, &hex[2], 64);
dev[F("IEEEAddr")] = hex; attr_list.addAttribute(F("IEEEAddr")).setStr(hex);
if (device.modelId) { 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); int8_t bulbtype = getHueBulbtype(shortaddr);
if (bulbtype >= 0) { 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) { 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++) { for (uint32_t i = 0; i < endpoints_max; i++) {
uint8_t endpoint = device.endpoints[i]; uint8_t endpoint = device.endpoints[i];
if (0x00 == endpoint) { break; } if (0x00 == endpoint) { break; }
arr_ep.add(endpoint);
snprintf_P(hex, sizeof(hex), PSTR("0x%02X"), endpoint);
dev_endpoints.add(hex);
} }
attr_list.addAttribute(F("Endpoints")).setStrRaw(arr_ep.toString().c_str());
} }
json_arr.addStrRaw(attr_list.toString(true).c_str());
} }
String payload = ""; return json_arr.toString();
payload.reserve(200);
json.printTo(payload);
return payload;
} }
// Restore a single device configuration based on json export // Restore a single device configuration based on json export