mirror of
https://github.com/arendst/Tasmota.git
synced 2025-07-27 12:46:34 +00:00
commit
022315d573
@ -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();
|
||||||
|
@ -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
|
||||||
|
Loading…
x
Reference in New Issue
Block a user