mirror of https://github.com/arendst/Tasmota.git
Zigbee flash storage refactor
This commit is contained in:
parent
b4dc2600c5
commit
afb5839b6c
|
@ -194,14 +194,10 @@ public:
|
|||
return 0;
|
||||
}
|
||||
|
||||
// if no NULL is found, returns length until the end of the buffer
|
||||
inline size_t strlen(const size_t offset) const {
|
||||
return strnlen((const char*) &_buf->buf[offset], len() - offset);
|
||||
}
|
||||
|
||||
size_t strlen_s(const size_t offset) const {
|
||||
size_t slen = this->strlen(offset);
|
||||
if (slen == len() - offset) {
|
||||
size_t strlen(const size_t offset) const {
|
||||
if (offset >= len()) { return 0; }
|
||||
size_t slen = strnlen((const char*) &_buf->buf[offset], len() - offset);
|
||||
if (slen == (len() - offset)) {
|
||||
return 0; // we didn't find a NULL char
|
||||
} else {
|
||||
return slen;
|
||||
|
|
|
@ -64,46 +64,6 @@ 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
|
||||
|
@ -143,8 +103,8 @@ public:
|
|||
float fval;
|
||||
SBuffer* bval;
|
||||
char* sval;
|
||||
class Z_attribute_list * objval;
|
||||
class Z_json_array * arrval;
|
||||
class Z_attribute_list * objval;
|
||||
class JsonGeneratorArray * arrval;
|
||||
} val;
|
||||
Za_type type; // uint8_t in size, type of attribute, see above
|
||||
bool key_is_str; // is the key a string?
|
||||
|
@ -217,7 +177,7 @@ public:
|
|||
}
|
||||
|
||||
Z_attribute_list & newAttrList(void);
|
||||
Z_json_array & newJsonArray(void);
|
||||
JsonGeneratorArray & 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);}
|
||||
|
@ -446,9 +406,9 @@ Z_attribute_list & Z_attribute::newAttrList(void) {
|
|||
return *val.objval;
|
||||
}
|
||||
|
||||
Z_json_array & Z_attribute::newJsonArray(void) {
|
||||
JsonGeneratorArray & Z_attribute::newJsonArray(void) {
|
||||
freeVal();
|
||||
val.arrval = new Z_json_array();
|
||||
val.arrval = new JsonGeneratorArray();
|
||||
type = Za_type::Za_arr;
|
||||
return *val.arrval;
|
||||
}
|
||||
|
|
|
@ -36,31 +36,110 @@ enum class Z_Data_Type : uint8_t {
|
|||
Z_Device = 0xFF // special value when parsing Device level attributes
|
||||
};
|
||||
|
||||
const uint8_t Z_Data_Type_char[] PROGMEM = {
|
||||
'?', // 0x00 Z_Data_Type::Z_Unknown
|
||||
'L', // 0x01 Z_Data_Type::Z_Light
|
||||
'P', // 0x02 Z_Data_Type::Z_Plug
|
||||
'I', // 0x03 Z_Data_Type::Z_PIR
|
||||
'A', // 0x04 Z_Data_Type::Z_Alarm
|
||||
'T', // 0x05 Z_Data_Type::Z_Thermo
|
||||
'O', // 0x05 Z_Data_Type::Z_OnOff
|
||||
'\0', // 0x06
|
||||
'\0', // 0x07
|
||||
'\0', // 0x08
|
||||
'\0', // 0x09
|
||||
'\0', // 0x0A
|
||||
'\0', // 0x0B
|
||||
'\0', // 0x0C
|
||||
'\0', // 0x0D
|
||||
'\0', // 0x0E
|
||||
'E', // 0x05 Z_Data_Type::Z_Ext
|
||||
// '_' maps to 0xFF Z_Data_Type::Z_Device
|
||||
};
|
||||
|
||||
class Z_Data_Set;
|
||||
/*********************************************************************************************\
|
||||
* Device specific data, sensors...
|
||||
\*********************************************************************************************/
|
||||
class Z_Data {
|
||||
public:
|
||||
Z_Data(Z_Data_Type type = Z_Data_Type::Z_Unknown, uint8_t endpoint = 0) : _type(type), _endpoint(endpoint), _config(-1), _power(0) {}
|
||||
Z_Data(Z_Data_Type type = Z_Data_Type::Z_Unknown, uint8_t endpoint = 0) : _type(type), _endpoint(endpoint), _config(0xF), _power(0) {}
|
||||
inline Z_Data_Type getType(void) const { return _type; }
|
||||
inline int8_t getConfig(void) const { return _config; }
|
||||
inline bool validConfig(void) const { return _config != 0xF; }
|
||||
inline void setConfig(int8_t config) { _config = config; }
|
||||
uint8_t getConfigByte(void) const { return ( ((uint8_t)_type) << 4) | ((_config & 0xF) & 0x0F); }
|
||||
|
||||
inline uint8_t getEndpoint(void) const { return _endpoint; }
|
||||
|
||||
void toAttributes(Z_attribute_list & attr_list, Z_Data_Type type) const;
|
||||
|
||||
static const Z_Data_Type type = Z_Data_Type::Z_Unknown;
|
||||
static bool ConfigToZData(const char * config_str, Z_Data_Type * type, uint8_t * ep, uint8_t * config);
|
||||
|
||||
static Z_Data_Type CharToDataType(char c);
|
||||
static char DataTypeToChar(Z_Data_Type t);
|
||||
|
||||
friend class Z_Data_Set;
|
||||
protected:
|
||||
Z_Data_Type _type; // encoded on 4 bits, type of the device
|
||||
uint8_t _endpoint; // source endpoint, or 0x00 if any endpoint
|
||||
int8_t _config; // encoded on 4 bits, customize behavior
|
||||
uint8_t _config : 4; // encoded on 4 bits, customize behavior
|
||||
uint8_t _power; // power state if the type supports it
|
||||
};
|
||||
|
||||
Z_Data_Type Z_Data::CharToDataType(char c) {
|
||||
if (c) {
|
||||
if (c == '_') {
|
||||
return Z_Data_Type::Z_Device;
|
||||
} else {
|
||||
for (uint32_t i=0; i<ARRAY_SIZE(Z_Data_Type_char); i++) {
|
||||
if (pgm_read_byte(&Z_Data_Type_char[i]) == c) {
|
||||
return (Z_Data_Type) i;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return Z_Data_Type::Z_Unknown;
|
||||
}
|
||||
|
||||
char Z_Data::DataTypeToChar(Z_Data_Type t) {
|
||||
if (t == Z_Data_Type::Z_Device) {
|
||||
return '_';
|
||||
} else {
|
||||
uint8_t tt = (uint8_t) t;
|
||||
if (tt < ARRAY_SIZE(Z_Data_Type_char)) {
|
||||
return pgm_read_byte(&Z_Data_Type_char[tt]);
|
||||
}
|
||||
}
|
||||
return '\0';
|
||||
}
|
||||
|
||||
// Parse configuration
|
||||
// Either '_'
|
||||
// or 'T01' or 'L01.2'
|
||||
// result: true if parsing ok
|
||||
bool Z_Data::ConfigToZData(const char * config_str, Z_Data_Type * type, uint8_t * ep, uint8_t * config) {
|
||||
if (config_str == nullptr) { return false; }
|
||||
|
||||
Z_Data_Type data_type = Z_Data::CharToDataType(config_str[0]);
|
||||
if (data_type == Z_Data_Type::Z_Unknown) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (type != nullptr) {
|
||||
*type = data_type;
|
||||
}
|
||||
if (ep != nullptr) {
|
||||
*ep = strtoul(&config_str[1], nullptr, 16); // hex base 16
|
||||
}
|
||||
if ((config != nullptr) && (config_str[3] == '.')) {
|
||||
// we have a config attribute
|
||||
*config = strtoul(&config_str[4], nullptr, 16) & 0x0F; // hex base 16
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/*********************************************************************************************\
|
||||
* Device specific: On/Off, power up to 8 relays
|
||||
\*********************************************************************************************/
|
||||
|
@ -157,8 +236,6 @@ public:
|
|||
inline void setCT(uint16_t _ct) { ct = _ct; }
|
||||
inline void setX(uint16_t _x) { x = _x; }
|
||||
inline void setY(uint16_t _y) { y = _y; }
|
||||
|
||||
void toAttributes(Z_attribute_list & attr_list, Z_Data_Type type) const;
|
||||
|
||||
static const Z_Data_Type type = Z_Data_Type::Z_Light;
|
||||
// 12 bytes
|
||||
|
@ -330,14 +407,6 @@ const Z_Data & Z_Data_Set::find(Z_Data_Type type, uint8_t ep) const {
|
|||
return *(Z_Data*)nullptr;
|
||||
}
|
||||
|
||||
// Low-level
|
||||
// Add light attributes, used by dumpLightState and by SbData
|
||||
//
|
||||
void Z_Data_Light::toAttributes(Z_attribute_list & attr_list, Z_Data_Type type) const {
|
||||
attr_list.addAttribute(PSTR(D_JSON_ZIGBEE_LIGHT)).setInt(getConfig()); // special case, since type is 0x00 we can assume getConfig() is good
|
||||
Z_Data::toAttributes(attr_list, type);
|
||||
}
|
||||
|
||||
void Z_Data_OnOff::toAttributes(Z_attribute_list & attr_list, Z_Data_Type type) const {
|
||||
if (validPower()) { attr_list.addAttribute(PSTR("Power")).setUInt(getPower() ? 1 : 0); }
|
||||
}
|
||||
|
@ -370,7 +439,7 @@ public:
|
|||
// Light information for Hue integration integration, last known values
|
||||
|
||||
// New version of device data handling
|
||||
Z_Data_Set data; // Linkedlist of device data per endpoint
|
||||
Z_Data_Set data; // Linkedlist of device data per endpoint
|
||||
// other status
|
||||
uint8_t lqi; // lqi from last message, 0xFF means unknown
|
||||
uint8_t batterypercent; // battery percentage (0..100), 0xFF means unknwon
|
||||
|
@ -390,7 +459,7 @@ public:
|
|||
seqNumber(0),
|
||||
hidden(false),
|
||||
reachable(false),
|
||||
// Hue support
|
||||
data(),
|
||||
lqi(0xFF),
|
||||
batterypercent(0xFF),
|
||||
last_seen(0)
|
||||
|
@ -533,7 +602,7 @@ public:
|
|||
|
||||
// Add new device, provide ShortAddr and optional longAddr
|
||||
// If it is already registered, update information, otherwise create the entry
|
||||
void updateDevice(uint16_t shortaddr, uint64_t longaddr = 0);
|
||||
Z_Device & updateDevice(uint16_t shortaddr, uint64_t longaddr = 0);
|
||||
|
||||
// Add an endpoint to a device
|
||||
void addEndpoint(uint16_t shortaddr, uint8_t endpoint);
|
||||
|
|
|
@ -29,10 +29,12 @@
|
|||
//
|
||||
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(shortaddr, longaddr);
|
||||
Z_Device & device = _devices.addToLast();
|
||||
device.shortaddr = shortaddr;
|
||||
device.longaddr = longaddr;
|
||||
|
||||
dirty();
|
||||
return _devices.addHead(device);
|
||||
return device;
|
||||
}
|
||||
|
||||
void Z_Devices::freeDeviceEntry(Z_Device *device) {
|
||||
|
@ -178,7 +180,7 @@ bool Z_Devices::removeDevice(uint16_t shortaddr) {
|
|||
// In:
|
||||
// shortaddr
|
||||
// longaddr (both can't be null at the same time)
|
||||
void Z_Devices::updateDevice(uint16_t shortaddr, uint64_t longaddr) {
|
||||
Z_Device & Z_Devices::updateDevice(uint16_t shortaddr, uint64_t longaddr) {
|
||||
Z_Device * s_found = &findShortAddr(shortaddr); // is there already a shortaddr entry
|
||||
Z_Device * l_found = &findLongAddr(longaddr); // is there already a longaddr entry
|
||||
|
||||
|
@ -191,21 +193,25 @@ void Z_Devices::updateDevice(uint16_t shortaddr, uint64_t longaddr) {
|
|||
freeDeviceEntry(s_found);
|
||||
_devices.remove(s_found);
|
||||
dirty();
|
||||
return *l_found;
|
||||
}
|
||||
} else if (foundDevice(*s_found)) {
|
||||
// shortaddr already exists but longaddr not
|
||||
// add the longaddr to the entry
|
||||
s_found->longaddr = longaddr;
|
||||
dirty();
|
||||
return *s_found;
|
||||
} else if (foundDevice(*l_found)) {
|
||||
// longaddr entry exists, update shortaddr
|
||||
l_found->shortaddr = shortaddr;
|
||||
dirty();
|
||||
return *l_found;
|
||||
} else {
|
||||
// neither short/lonf addr are found.
|
||||
if ((BAD_SHORTADDR != shortaddr) || longaddr) {
|
||||
createDeviceEntry(shortaddr, longaddr);
|
||||
return createDeviceEntry(shortaddr, longaddr);
|
||||
}
|
||||
return (Z_Device&) device_unk;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -224,7 +230,7 @@ void Z_Devices::clearEndpoints(uint16_t shortaddr) {
|
|||
// Add an endpoint to a shortaddr
|
||||
//
|
||||
void Z_Devices::addEndpoint(uint16_t shortaddr, uint8_t endpoint) {
|
||||
if (0x00 == endpoint) { return; }
|
||||
if ((0x00 == endpoint) || (endpoint > 240)) { return; }
|
||||
Z_Device &device = getShortAddr(shortaddr);
|
||||
|
||||
for (uint32_t i = 0; i < endpoints_max; i++) {
|
||||
|
@ -278,6 +284,7 @@ void Z_Devices::setStringAttribute(char*& attr, const char * str) {
|
|||
}
|
||||
}
|
||||
if (str_len) {
|
||||
if (str_len > 31) { str_len = 31; }
|
||||
attr = (char*) malloc(str_len + 1);
|
||||
strlcpy(attr, str, str_len + 1);
|
||||
}
|
||||
|
@ -652,7 +659,7 @@ String Z_Devices::dumpLightState(uint16_t shortaddr) const {
|
|||
// Mode = 1: simple dump of devices addresses
|
||||
// Mode = 2: simple dump of devices addresses and names, endpoints, light
|
||||
String Z_Devices::dump(uint32_t dump_mode, uint16_t status_shortaddr) const {
|
||||
Z_json_array json_arr;
|
||||
JsonGeneratorArray json_arr;
|
||||
|
||||
for (const auto & device : _devices) {
|
||||
uint16_t shortaddr = device.shortaddr;
|
||||
|
@ -678,20 +685,30 @@ String Z_Devices::dump(uint32_t dump_mode, uint16_t status_shortaddr) const {
|
|||
if (device.modelId) {
|
||||
attr_list.addAttribute(F(D_JSON_MODEL D_JSON_ID)).setStr(device.modelId);
|
||||
}
|
||||
int8_t bulbtype = getHueBulbtype(shortaddr);
|
||||
if (bulbtype >= 0) {
|
||||
attr_list.addAttribute(F(D_JSON_ZIGBEE_LIGHT)).setInt(bulbtype); // sign extend, 0xFF changed as -1
|
||||
}
|
||||
if (device.manufacturerId) {
|
||||
attr_list.addAttribute(F("Manufacturer")).setStr(device.manufacturerId);
|
||||
}
|
||||
Z_json_array arr_ep;
|
||||
|
||||
JsonGeneratorArray arr_ep;
|
||||
for (uint32_t i = 0; i < endpoints_max; i++) {
|
||||
uint8_t endpoint = device.endpoints[i];
|
||||
if (0x00 == endpoint) { break; }
|
||||
arr_ep.add(endpoint);
|
||||
}
|
||||
attr_list.addAttribute(F("Endpoints")).setStrRaw(arr_ep.toString().c_str());
|
||||
|
||||
JsonGeneratorArray arr_data;
|
||||
for (auto & data_elt : device.data) {
|
||||
char key[8];
|
||||
if (data_elt.validConfig()) {
|
||||
snprintf_P(key, sizeof(key), "?%02X.%1X", data_elt.getEndpoint(), data_elt.getConfig());
|
||||
} else {
|
||||
snprintf_P(key, sizeof(key), "?%02X", data_elt.getEndpoint());
|
||||
}
|
||||
key[0] = Z_Data::DataTypeToChar(data_elt.getType());
|
||||
arr_data.addStr(key);
|
||||
}
|
||||
attr_list.addAttribute(F("Config")).setStrRaw(arr_data.toString().c_str());
|
||||
}
|
||||
json_arr.addStrRaw(attr_list.toString(true).c_str());
|
||||
}
|
||||
|
@ -710,18 +727,17 @@ String Z_Devices::dump(uint32_t dump_mode, uint16_t status_shortaddr) const {
|
|||
int32_t Z_Devices::deviceRestore(JsonParserObject json) {
|
||||
|
||||
// params
|
||||
uint16_t device = 0x0000; // 0x0000 is coordinator so considered invalid
|
||||
uint16_t shortaddr = 0x0000; // 0x0000 is coordinator so considered invalid
|
||||
uint64_t ieeeaddr = 0x0000000000000000LL; // 0 means unknown
|
||||
const char * modelid = nullptr;
|
||||
const char * manufid = nullptr;
|
||||
const char * friendlyname = nullptr;
|
||||
int8_t bulbtype = -1;
|
||||
size_t endpoints_len = 0;
|
||||
|
||||
// read mandatory "Device"
|
||||
JsonParserToken val_device = json[PSTR("Device")];
|
||||
if (val_device) {
|
||||
device = (uint32_t) val_device.getUInt(device);
|
||||
shortaddr = (uint32_t) val_device.getUInt(shortaddr);
|
||||
} else {
|
||||
return -1; // missing "Device" attribute
|
||||
}
|
||||
|
@ -730,23 +746,41 @@ int32_t Z_Devices::deviceRestore(JsonParserObject json) {
|
|||
friendlyname = json.getStr(PSTR("Name"), nullptr); // read "Name"
|
||||
modelid = json.getStr(PSTR("ModelId"), nullptr);
|
||||
manufid = json.getStr(PSTR("Manufacturer"), nullptr);
|
||||
JsonParserToken tok_bulbtype = json[PSTR(D_JSON_ZIGBEE_LIGHT)];
|
||||
|
||||
// update internal device information
|
||||
updateDevice(device, ieeeaddr);
|
||||
if (modelid) { setModelId(device, modelid); }
|
||||
if (manufid) { setManufId(device, manufid); }
|
||||
if (friendlyname) { setFriendlyName(device, friendlyname); }
|
||||
if (tok_bulbtype) { setLightProfile(device, tok_bulbtype.getInt()); }
|
||||
updateDevice(shortaddr, ieeeaddr);
|
||||
if (modelid) { setModelId(shortaddr, modelid); }
|
||||
if (manufid) { setManufId(shortaddr, manufid); }
|
||||
if (friendlyname) { setFriendlyName(shortaddr, friendlyname); }
|
||||
|
||||
// read "Endpoints"
|
||||
JsonParserToken val_endpoints = json[PSTR("Endpoints")];
|
||||
if (val_endpoints.isArray()) {
|
||||
JsonParserArray arr_ep = JsonParserArray(val_endpoints);
|
||||
clearEndpoints(device); // clear even if array is empty
|
||||
clearEndpoints(shortaddr); // clear even if array is empty
|
||||
for (auto ep_elt : arr_ep) {
|
||||
uint8_t ep = ep_elt.getUInt();
|
||||
if (ep) { addEndpoint(device, ep); }
|
||||
if (ep) { addEndpoint(shortaddr, ep); }
|
||||
}
|
||||
}
|
||||
|
||||
// read "Config"
|
||||
JsonParserToken val_config = json[PSTR("Config")];
|
||||
Z_Device & device = getShortAddr(shortaddr);
|
||||
if (val_config.isArray()) {
|
||||
JsonParserArray arr_config = JsonParserArray(val_config);
|
||||
device.data.reset(); // remove existing configuration
|
||||
for (auto config_elt : arr_config) {
|
||||
const char * conf_str = config_elt.getStr();
|
||||
Z_Data_Type data_type;
|
||||
uint8_t ep, config;
|
||||
|
||||
if (Z_Data::ConfigToZData(conf_str, &data_type, &ep, &config)) {
|
||||
Z_Data & data = device.data.getByType(data_type, ep);
|
||||
if (&data != nullptr) {
|
||||
data.setConfig(config);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -26,6 +26,14 @@
|
|||
// uint16 - start address in Flash (offset)
|
||||
// uint16 - length in bytes (makes sure parsing stops)
|
||||
//
|
||||
// First byte:
|
||||
// 0x00 - Empty or V3 format
|
||||
// 0x01-0xFE - Legacy format
|
||||
// 0xFF - invalid
|
||||
//
|
||||
//
|
||||
// V1 Legacy
|
||||
// =========
|
||||
// File structure:
|
||||
// uint8 - number of devices, 0=none, 0xFF=invalid entry (probably Flash was erased)
|
||||
//
|
||||
|
@ -47,6 +55,29 @@
|
|||
// reserved for extensions
|
||||
// -- V2 --
|
||||
// int8_t - zigbee profile of the device
|
||||
//
|
||||
// =======================
|
||||
// v3 with version number
|
||||
// File structure:
|
||||
//
|
||||
// uint8 - number of devices, 0=none, 0xFF=invalid entry (probably Flash was erased)
|
||||
//
|
||||
// [Array of devices]
|
||||
// [Offset = 2]
|
||||
// uint8 - length of device record
|
||||
// uint16 - short address
|
||||
// uint64 - long IEEE address
|
||||
//
|
||||
// str - ModelID (null terminated C string, 32 chars max)
|
||||
// str - Manuf (null terminated C string, 32 chars max)
|
||||
// str - FriendlyName (null terminated C string, 32 chars max)
|
||||
//
|
||||
// [Array of endpoints]
|
||||
// uint8 - endpoint number, 0xFF marks the end of endpoints
|
||||
// uint8[] - list of configuration bytes, 0xFF marks the end
|
||||
// i.e. 0xFF-0xFF marks the end of the array of endpoints
|
||||
//
|
||||
|
||||
|
||||
// Memory footprint
|
||||
#ifdef ESP8266
|
||||
|
@ -63,71 +94,81 @@ const static size_t z_block_offset = 0x0000; // No offset needed
|
|||
const static size_t z_block_len = 0x1000; // 4kb
|
||||
#endif
|
||||
|
||||
class z_flashdata_t {
|
||||
// Each entry consumes 8 bytes
|
||||
class Z_Flashentry {
|
||||
public:
|
||||
uint32_t name; // simple 4 letters name. Currently 'zig1', 'zig2'. 0xFFFFFFFF if not entry
|
||||
uint16_t len; // len of object in bytes, 0xFFFF if no entry
|
||||
uint16_t start; // address of start, 0xFFFF if empty, must be aligned on 128 bytes boundaries
|
||||
};
|
||||
|
||||
class Z_Flashdirectory {
|
||||
public:
|
||||
// 8 bytes header
|
||||
uint32_t magic; // magic value 'Tsmt' to check that the block is initialized
|
||||
uint32_t clock; // clock vector to discard entries that are made before this one. This should be incremented by 1 for each new entry (future anti-weavering)
|
||||
// entries, 14*8 = 112 bytes
|
||||
Z_Flashentry entries[14];
|
||||
uint32_t name; // simple 4 letters name. Currently 'skey', 'crt ', 'crt1', 'crt2'
|
||||
uint16_t len; // len of object
|
||||
uint16_t reserved; // align on 4 bytes boundary
|
||||
// link to next entry, none for now, but may be used for anti-weavering
|
||||
uint16_t next_dir; // 0xFFFF if none
|
||||
uint16_t reserved1; // must be 0xFFFF
|
||||
uint32_t reserved2; // must be 0xFFFFFFFF
|
||||
};
|
||||
|
||||
const static uint32_t ZIGB_NAME = 0x3167697A; // 'zig1' little endian
|
||||
const static size_t Z_MAX_FLASH = z_block_len - sizeof(z_flashdata_t); // 2040
|
||||
|
||||
const static uint32_t ZIGB_NAME1 = 0x3167697A; // 'zig1' little endian
|
||||
const static uint32_t ZIGB_NAME2 = 0x3267697A; // 'zig2' little endian, v2
|
||||
const static size_t Z_MAX_FLASH = z_block_len - sizeof(Z_Flashentry); // 2040
|
||||
|
||||
class SBuffer hibernateDevice(const struct Z_Device &device) {
|
||||
bool hibernateDeviceConfiguration(SBuffer & buf, const class Z_Data_Set & data, uint8_t endpoint) {
|
||||
bool found = false;
|
||||
for (auto & elt : data) {
|
||||
if (endpoint == elt.getEndpoint()) {
|
||||
buf.add8(elt.getConfigByte());
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
return found;
|
||||
}
|
||||
|
||||
class SBuffer hibernateDevicev2(const struct Z_Device &device) {
|
||||
SBuffer buf(128);
|
||||
|
||||
buf.add8(0x00); // overall length, will be updated later
|
||||
buf.add16(device.shortaddr);
|
||||
buf.add64(device.longaddr);
|
||||
|
||||
uint32_t endpoints_count = 0;
|
||||
for (endpoints_count = 0; endpoints_count < endpoints_max; endpoints_count++) {
|
||||
if (0x00 == device.endpoints[endpoints_count]) { break; }
|
||||
char *names[3] = { device.modelId, device.manufacturerId, device.friendlyName };
|
||||
|
||||
for (uint32_t i=0; i<ARRAY_SIZE(names); i++) {
|
||||
char *p = names[i];
|
||||
if (p) {
|
||||
size_t len = strlen(p);
|
||||
if (len > 32) { len = 32; } // max 32 chars
|
||||
buf.addBuffer(p, len);
|
||||
}
|
||||
buf.add8(0x00); // end of string marker
|
||||
}
|
||||
|
||||
buf.add8(endpoints_count);
|
||||
// iterate on endpoints
|
||||
for (uint32_t i = 0; i < endpoints_max; i++) {
|
||||
// check if we need to write fake endpoint 0x00
|
||||
buf.add8(0x00);
|
||||
if (hibernateDeviceConfiguration(buf, device.data, 0)) {
|
||||
buf.add8(0xFF); // end of configuration
|
||||
} else {
|
||||
buf.setLen(buf.len()-1); // remove 1 byte header
|
||||
}
|
||||
// scan endpoints
|
||||
for (uint32_t i=0; i<endpoints_max; i++) {
|
||||
uint8_t endpoint = device.endpoints[i];
|
||||
if (0x00 == endpoint) { break; } // stop
|
||||
|
||||
if (0x00 == endpoint) { break; }
|
||||
buf.add8(endpoint);
|
||||
buf.add16(0x0000); // profile_id, not used anymore
|
||||
|
||||
// removed clusters_in
|
||||
buf.add8(0xFF); // end of endpoint marker
|
||||
|
||||
// no more storage of clusters_out
|
||||
buf.add8(0xFF); // end of endpoint marker
|
||||
hibernateDeviceConfiguration(buf, device.data, endpoint);
|
||||
buf.add8(0xFF); // end of configuration
|
||||
}
|
||||
|
||||
// ModelID
|
||||
if (device.modelId) {
|
||||
size_t model_len = strlen(device.modelId);
|
||||
if (model_len > 32) { model_len = 32; } // max 32 chars
|
||||
buf.addBuffer(device.modelId, model_len);
|
||||
}
|
||||
buf.add8(0x00); // end of string marker
|
||||
|
||||
// ManufID
|
||||
if (device.manufacturerId) {
|
||||
size_t manuf_len = strlen(device.manufacturerId);
|
||||
if (manuf_len > 32) { manuf_len = 32; } // max 32 chars
|
||||
buf.addBuffer(device.manufacturerId, manuf_len);
|
||||
}
|
||||
buf.add8(0x00); // end of string marker
|
||||
|
||||
// FriendlyName
|
||||
if (device.friendlyName) {
|
||||
size_t frname_len = strlen(device.friendlyName);
|
||||
if (frname_len > 32) {frname_len = 32; } // max 32 chars
|
||||
buf.addBuffer(device.friendlyName, frname_len);
|
||||
}
|
||||
buf.add8(0x00); // end of string marker
|
||||
|
||||
// Zigbee Profile
|
||||
buf.add8(device.getLightChannels());
|
||||
buf.add8(0xFF); // end of endpoints
|
||||
|
||||
// update overall length
|
||||
buf.set8(0, buf.len());
|
||||
|
@ -144,7 +185,7 @@ class SBuffer hibernateDevices(void) {
|
|||
|
||||
for (uint32_t i = 0; i < devices_size; i++) {
|
||||
const Z_Device & device = zigbee_devices.devicesAt(i);
|
||||
const SBuffer buf_device = hibernateDevice(device);
|
||||
const SBuffer buf_device = hibernateDevicev2(device);
|
||||
buf.addBuffer(buf_device);
|
||||
}
|
||||
|
||||
|
@ -164,22 +205,24 @@ class SBuffer hibernateDevices(void) {
|
|||
return buf;
|
||||
}
|
||||
|
||||
void hydrateDevices(const SBuffer &buf) {
|
||||
uint32_t buf_len = buf.len();
|
||||
if (buf_len <= 10) { return; }
|
||||
// parse a single string from the saved data
|
||||
// if something wrong happens, returns nullptr to ignore the string
|
||||
// Index d is incremented to just after the string
|
||||
const char * hydrateSingleString(const SBuffer & buf, uint32_t *d) {
|
||||
size_t s_len = buf.strlen(*d);
|
||||
const char * ptr = s_len ? buf.charptr(*d) : "";
|
||||
*d += s_len + 1;
|
||||
return ptr;
|
||||
}
|
||||
|
||||
uint32_t k = 0;
|
||||
uint32_t num_devices = buf.get8(k++);
|
||||
for (uint32_t i = 0; (i < num_devices) && (k < buf_len); i++) {
|
||||
uint32_t dev_record_len = buf.get8(k);
|
||||
|
||||
SBuffer buf_d = buf.subBuffer(k, dev_record_len);
|
||||
|
||||
uint32_t d = 1; // index in device buffer
|
||||
uint16_t shortaddr = buf_d.get16(d); d += 2;
|
||||
uint64_t longaddr = buf_d.get64(d); d += 8;
|
||||
zigbee_devices.updateDevice(shortaddr, longaddr); // update device's addresses
|
||||
void hydrateSingleDevice(const SBuffer & buf_d, uint32_t version) {
|
||||
uint32_t d = 1; // index in device buffer
|
||||
uint16_t shortaddr = buf_d.get16(d); d += 2;
|
||||
uint64_t longaddr = buf_d.get64(d); d += 8;
|
||||
size_t buf_len = buf_d.len();
|
||||
Z_Device & device = zigbee_devices.updateDevice(shortaddr, longaddr); // update device's addresses
|
||||
|
||||
if (1 == version) {
|
||||
uint32_t endpoints = buf_d.get8(d++);
|
||||
for (uint32_t j = 0; j < endpoints; j++) {
|
||||
uint8_t ep = buf_d.get8(d++);
|
||||
|
@ -187,51 +230,77 @@ void hydrateDevices(const SBuffer &buf) {
|
|||
zigbee_devices.addEndpoint(shortaddr, ep);
|
||||
|
||||
// in clusters
|
||||
while (d < dev_record_len) { // safe guard against overflow
|
||||
while (d < buf_len) { // safe guard against overflow
|
||||
uint8_t ep_cluster = buf_d.get8(d++);
|
||||
if (0xFF == ep_cluster) { break; } // end of block
|
||||
// ignore
|
||||
}
|
||||
// out clusters
|
||||
while (d < dev_record_len) { // safe guard against overflow
|
||||
while (d < buf_len) { // safe guard against overflow
|
||||
uint8_t ep_cluster = buf_d.get8(d++);
|
||||
if (0xFF == ep_cluster) { break; } // end of block
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// parse 3 strings
|
||||
char empty[] = "";
|
||||
// ModelId
|
||||
zigbee_devices.setModelId(shortaddr, hydrateSingleString(buf_d, &d));
|
||||
|
||||
// ManufID
|
||||
uint32_t s_len = buf_d.strlen_s(d);
|
||||
char *ptr = s_len ? buf_d.charptr(d) : empty;
|
||||
zigbee_devices.setModelId(shortaddr, ptr);
|
||||
d += s_len + 1;
|
||||
// ManufID
|
||||
zigbee_devices.setManufId(shortaddr, hydrateSingleString(buf_d, &d));
|
||||
|
||||
// ManufID
|
||||
s_len = buf_d.strlen_s(d);
|
||||
ptr = s_len ? buf_d.charptr(d) : empty;
|
||||
zigbee_devices.setManufId(shortaddr, ptr);
|
||||
d += s_len + 1;
|
||||
// FriendlyName
|
||||
zigbee_devices.setFriendlyName(shortaddr, hydrateSingleString(buf_d, &d));
|
||||
|
||||
// FriendlyName
|
||||
s_len = buf_d.strlen_s(d);
|
||||
ptr = s_len ? buf_d.charptr(d) : empty;
|
||||
zigbee_devices.setFriendlyName(shortaddr, ptr);
|
||||
d += s_len + 1;
|
||||
if (d >= buf_len) { return; }
|
||||
|
||||
// Hue bulbtype - if present
|
||||
if (d < dev_record_len) {
|
||||
zigbee_devices.setLightProfile(shortaddr, buf_d.get8(d));
|
||||
d++;
|
||||
// Hue bulbtype - if present
|
||||
if (1 == version) {
|
||||
zigbee_devices.setLightProfile(shortaddr, buf_d.get8(d));
|
||||
d++;
|
||||
} else if (2 == version) {
|
||||
// v2 parser
|
||||
while (d < buf_len) {
|
||||
uint8_t ep = buf_d.get8(d++);
|
||||
if (0xFF == ep) { break; } // ep 0xFF marks the end of the endpoints
|
||||
if (ep > 240) { ep = 0xFF; } // ep == 0xFF means ignore
|
||||
if ((ep > 0) && (ep != 0xFF)) { zigbee_devices.addEndpoint(shortaddr, ep); } // don't add endpoint if it is 0x00
|
||||
while (d < buf_len) {
|
||||
uint8_t config_type = buf_d.get8(d++);
|
||||
if (0xFF == config_type) { break; } // 0xFF marks the end of congiguration
|
||||
uint8_t config = config_type & 0x0F;
|
||||
Z_Data_Type type = (Z_Data_Type) (config_type >> 4);
|
||||
// set the configuration
|
||||
if (ep != 0xFF) {
|
||||
Z_Data & z_data = device.data.getByType(type, ep);
|
||||
if (&z_data != nullptr) {
|
||||
z_data.setConfig(config);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void hydrateDevices(const SBuffer &buf, uint32_t version) {
|
||||
uint32_t buf_len = buf.len();
|
||||
if (buf_len <= 10) { return; }
|
||||
|
||||
uint32_t k = 0; // byte index in global buffer
|
||||
uint32_t num_devices = buf.get8(k++);
|
||||
for (uint32_t i = 0; (i < num_devices) && (k < buf_len); i++) {
|
||||
uint32_t dev_record_len = buf.get8(k);
|
||||
|
||||
SBuffer buf_d = buf.subBuffer(k, dev_record_len);
|
||||
hydrateSingleDevice(buf_d, version);
|
||||
|
||||
// next iteration
|
||||
k += dev_record_len;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void loadZigbeeDevices(void) {
|
||||
#ifdef ESP32
|
||||
// first copy SPI buffer into ram
|
||||
|
@ -243,19 +312,24 @@ void loadZigbeeDevices(void) {
|
|||
ZigbeeRead(&spi_buffer, z_spi_len);
|
||||
z_dev_start = spi_buffer;
|
||||
#endif // ESP32
|
||||
z_flashdata_t flashdata;
|
||||
memcpy_P(&flashdata, z_dev_start, sizeof(z_flashdata_t));
|
||||
Z_Flashentry flashdata;
|
||||
memcpy_P(&flashdata, z_dev_start, sizeof(Z_Flashentry));
|
||||
// AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_ZIGBEE "Memory %d"), ESP_getFreeHeap());
|
||||
AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_ZIGBEE "Zigbee signature in Flash: %08X - %d"), flashdata.name, flashdata.len);
|
||||
|
||||
// Check the signature
|
||||
if ((flashdata.name == ZIGB_NAME) && (flashdata.len > 0)) {
|
||||
if ( ((flashdata.name == ZIGB_NAME1) || (flashdata.name == ZIGB_NAME2))
|
||||
&& (flashdata.len > 0)) {
|
||||
uint16_t buf_len = flashdata.len;
|
||||
uint32_t version = (flashdata.name == ZIGB_NAME2) ? 2 : 1;
|
||||
// parse what seems to be a valid entry
|
||||
SBuffer buf(buf_len);
|
||||
buf.addBuffer(z_dev_start + sizeof(z_flashdata_t), buf_len);
|
||||
AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "Zigbee devices data in Flash (%d bytes)"), buf_len);
|
||||
hydrateDevices(buf);
|
||||
buf.addBuffer(z_dev_start + sizeof(Z_Flashentry), buf_len);
|
||||
AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "Zigbee devices data in Flash v%d (%d bytes)"), version, buf_len);
|
||||
// Serial.printf(">> Buffer=");
|
||||
// for (uint32_t i=0; i<buf.len(); i++) Serial.printf("%02X ", buf.get8(i));
|
||||
// Serial.printf("\n");
|
||||
hydrateDevices(buf, version);
|
||||
zigbee_devices.clean(); // don't write back to Flash what we just loaded
|
||||
} else {
|
||||
AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "No zigbee devices data in Flash"));
|
||||
|
@ -287,12 +361,12 @@ void saveZigbeeDevices(void) {
|
|||
ZigbeeRead(&spi_buffer, z_spi_len);
|
||||
#endif // ESP8266 - ESP32
|
||||
|
||||
z_flashdata_t *flashdata = (z_flashdata_t*)(spi_buffer + z_block_offset);
|
||||
flashdata->name = ZIGB_NAME;
|
||||
Z_Flashentry *flashdata = (Z_Flashentry*)(spi_buffer + z_block_offset);
|
||||
flashdata->name = ZIGB_NAME2; // v2
|
||||
flashdata->len = buf_len;
|
||||
flashdata->reserved = 0;
|
||||
flashdata->start = 0;
|
||||
|
||||
memcpy(spi_buffer + z_block_offset + sizeof(z_flashdata_t), buf.getBuffer(), buf_len);
|
||||
memcpy(spi_buffer + z_block_offset + sizeof(Z_Flashentry), buf.getBuffer(), buf_len);
|
||||
|
||||
// buffer is now ready, write it back
|
||||
#ifdef ESP8266
|
||||
|
|
|
@ -1332,7 +1332,7 @@ void ZCLFrame::parseReadAttributes(Z_attribute_list& attr_list) {
|
|||
|
||||
attr_list.addAttribute(F(D_CMND_ZIGBEE_CLUSTER)).setUInt(_cluster_id);
|
||||
|
||||
Z_json_array attr_numbers;
|
||||
JsonGeneratorArray attr_numbers;
|
||||
Z_attribute_list attr_names;
|
||||
while (len >= 2 + i) {
|
||||
uint16_t attrid = _payload.get16(i);
|
||||
|
@ -1803,6 +1803,8 @@ void ZCLFrame::postProcessAttributes(uint16_t shortaddr, Z_attribute_list& attr_
|
|||
// First we find or instantiate the correct Z_Data_XXX accorfing to the endpoint
|
||||
// Then store the attribute at the attribute addres (via offset) and according to size 8/16/32 bits
|
||||
|
||||
// add the endpoint if it was not already known
|
||||
zigbee_devices.addEndpoint(shortaddr, src_ep);
|
||||
// we don't apply the multiplier, but instead store in Z_Data_XXX object
|
||||
Z_Data & data = device.data.getByType(map_type, src_ep);
|
||||
uint8_t *attr_address = ((uint8_t*)&data) + sizeof(Z_Data) + map_offset;
|
||||
|
|
|
@ -370,7 +370,7 @@ void convertClusterSpecific(class Z_attribute_list &attr_list, uint16_t cluster,
|
|||
attr_list.addAttribute(command_name, PSTR("Count")).setUInt(xyz.y);
|
||||
{
|
||||
|
||||
Z_json_array group_list;
|
||||
JsonGeneratorArray group_list;
|
||||
for (uint32_t i = 0; i < xyz.y; i++) {
|
||||
group_list.add(payload.get16(2 + 2*i));
|
||||
}
|
||||
|
@ -441,7 +441,7 @@ void convertClusterSpecific(class Z_attribute_list &attr_list, uint16_t cluster,
|
|||
attr_list.addAttribute(command_name, command_suffix).setUInt(xyz.x);
|
||||
} else {
|
||||
// multiple answers, create an array
|
||||
Z_json_array arr;
|
||||
JsonGeneratorArray arr;
|
||||
arr.add(xyz.x);
|
||||
arr.add(xyz.y);
|
||||
if (xyz.z_type) {
|
||||
|
|
|
@ -1314,17 +1314,12 @@ bool parseDeviceInnerData(class Z_Device & device, JsonParserObject root) {
|
|||
// Parse key in format "L02":....
|
||||
const char * data_type_str = data_elt.getStr();
|
||||
Z_Data_Type data_type;
|
||||
uint8_t endpoint;
|
||||
uint8_t config = 0xFF; // unspecified
|
||||
|
||||
// parse key in the form "L01.5"
|
||||
if (!Z_Data::ConfigToZData(data_type_str, &data_type, &endpoint, &config)) { data_type = Z_Data_Type::Z_Unknown; }
|
||||
|
||||
switch (data_type_str[0]) {
|
||||
case 'P': data_type = Z_Data_Type::Z_Plug; break;
|
||||
case 'L': data_type = Z_Data_Type::Z_Light; break;
|
||||
case 'O': data_type = Z_Data_Type::Z_OnOff; break;
|
||||
case 'T': data_type = Z_Data_Type::Z_Thermo; break;
|
||||
case 'A': data_type = Z_Data_Type::Z_Alarm; break;
|
||||
case '_': data_type = Z_Data_Type::Z_Device; break;
|
||||
default: data_type = Z_Data_Type::Z_Unknown; break;
|
||||
}
|
||||
// The format should be a valid Code Lette followed by '-'
|
||||
if (data_type == Z_Data_Type::Z_Unknown) {
|
||||
Response_P(PSTR("{\"%s\":\"%s \"%s\"\"}"), XdrvMailbox.command, PSTR("Invalid Parameters"), data_type_str);
|
||||
return false;
|
||||
|
@ -1333,79 +1328,81 @@ bool parseDeviceInnerData(class Z_Device & device, JsonParserObject root) {
|
|||
JsonParserObject data_values = data_elt.getValue().getObject();
|
||||
if (!data_values) { return false; }
|
||||
|
||||
// Decode the endpoint number
|
||||
uint8_t endpoint = strtoul(&data_type_str[1], nullptr, 16); // hex base 16
|
||||
JsonParserToken val;
|
||||
if (data_type == Z_Data_Type::Z_Device) {
|
||||
if (val = data_values[PSTR(D_CMND_ZIGBEE_LINKQUALITY)]) { device.lqi = val.getUInt(); }
|
||||
if (val = data_values[PSTR("BatteryPercentage")]) { device.batterypercent = val.getUInt(); }
|
||||
if (val = data_values[PSTR("LastSeen")]) { device.last_seen = val.getUInt(); }
|
||||
} else {
|
||||
// Import generic attributes first
|
||||
Z_Data & data = device.data.getByType(data_type, endpoint);
|
||||
|
||||
// Import generic attributes first
|
||||
Z_Data & data = device.data.getByType(data_type, endpoint);
|
||||
// scan through attributes
|
||||
if (&data != nullptr) {
|
||||
if (config != 0xFF) {
|
||||
data.setConfig(config);
|
||||
}
|
||||
|
||||
// scan through attributes
|
||||
for (auto attr : data_values) {
|
||||
JsonParserToken attr_value = attr.getValue();
|
||||
uint8_t conv_zigbee_type;
|
||||
Z_Data_Type conv_data_type;
|
||||
uint8_t conv_map_offset;
|
||||
if (zigbeeFindAttributeByName(attr.getStr(), nullptr, nullptr, nullptr, &conv_zigbee_type, &conv_data_type, &conv_map_offset) != nullptr) {
|
||||
// found an attribute matching the name, does is fit the type?
|
||||
if (conv_data_type == data_type) {
|
||||
// we got a match. Bear in mind that a zero value is not a valid 'data_type'
|
||||
for (auto attr : data_values) {
|
||||
JsonParserToken attr_value = attr.getValue();
|
||||
uint8_t conv_zigbee_type;
|
||||
Z_Data_Type conv_data_type;
|
||||
uint8_t conv_map_offset;
|
||||
if (zigbeeFindAttributeByName(attr.getStr(), nullptr, nullptr, nullptr, &conv_zigbee_type, &conv_data_type, &conv_map_offset) != nullptr) {
|
||||
// found an attribute matching the name, does is fit the type?
|
||||
if (conv_data_type == data_type) {
|
||||
// we got a match. Bear in mind that a zero value is not a valid 'data_type'
|
||||
|
||||
uint8_t *attr_address = ((uint8_t*)&data) + sizeof(Z_Data) + conv_map_offset;
|
||||
uint32_t uval32 = attr_value.getUInt(); // call converter to uint only once
|
||||
int32_t ival32 = attr_value.getInt(); // call converter to int only once
|
||||
switch (conv_zigbee_type) {
|
||||
case Zenum8:
|
||||
case Zuint8: *(uint8_t*)attr_address = uval32; break;
|
||||
case Zenum16:
|
||||
case Zuint16: *(uint16_t*)attr_address = uval32; break;
|
||||
case Zuint32: *(uint32_t*)attr_address = uval32; break;
|
||||
case Zint8: *(int8_t*)attr_address = ival32; break;
|
||||
case Zint16: *(int16_t*)attr_address = ival32; break;
|
||||
case Zint32: *(int32_t*)attr_address = ival32; break;
|
||||
uint8_t *attr_address = ((uint8_t*)&data) + sizeof(Z_Data) + conv_map_offset;
|
||||
uint32_t uval32 = attr_value.getUInt(); // call converter to uint only once
|
||||
int32_t ival32 = attr_value.getInt(); // call converter to int only once
|
||||
switch (conv_zigbee_type) {
|
||||
case Zenum8:
|
||||
case Zuint8: *(uint8_t*)attr_address = uval32; break;
|
||||
case Zenum16:
|
||||
case Zuint16: *(uint16_t*)attr_address = uval32; break;
|
||||
case Zuint32: *(uint32_t*)attr_address = uval32; break;
|
||||
case Zint8: *(int8_t*)attr_address = ival32; break;
|
||||
case Zint16: *(int16_t*)attr_address = ival32; break;
|
||||
case Zint32: *(int32_t*)attr_address = ival32; break;
|
||||
}
|
||||
} else if (conv_data_type != Z_Data_Type::Z_Unknown) {
|
||||
AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_ZIGBEE "attribute %s is wrong type %d (expected %d)"), attr.getStr(), (uint8_t)data_type, (uint8_t)conv_data_type);
|
||||
}
|
||||
}
|
||||
} else if (conv_data_type != Z_Data_Type::Z_Unknown) {
|
||||
AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_ZIGBEE "attribute %s is wrong type %d (expected %d)"), attr.getStr(), (uint8_t)data_type, (uint8_t)conv_data_type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Import specific attributes that are not handled with the generic method
|
||||
switch (data_type) {
|
||||
// case Z_Data_Type::Z_Plug:
|
||||
// {
|
||||
// Z_Data_Plug & plug = (Z_Data_Plug&) data;
|
||||
// }
|
||||
// break;
|
||||
// case Z_Data_Type::Z_Light:
|
||||
// {
|
||||
// Z_Data_Light & light = (Z_Data_Light&) data;
|
||||
// }
|
||||
// break;
|
||||
case Z_Data_Type::Z_OnOff:
|
||||
{
|
||||
Z_Data_OnOff & onoff = (Z_Data_OnOff&) data;
|
||||
// Import specific attributes that are not handled with the generic method
|
||||
switch (data_type) {
|
||||
// case Z_Data_Type::Z_Plug:
|
||||
// {
|
||||
// Z_Data_Plug & plug = (Z_Data_Plug&) data;
|
||||
// }
|
||||
// break;
|
||||
// case Z_Data_Type::Z_Light:
|
||||
// {
|
||||
// Z_Data_Light & light = (Z_Data_Light&) data;
|
||||
// }
|
||||
// break;
|
||||
case Z_Data_Type::Z_OnOff:
|
||||
{
|
||||
Z_Data_OnOff & onoff = (Z_Data_OnOff&) data;
|
||||
|
||||
if (val = data_values[PSTR("Power")]) { onoff.setPower(val.getUInt() ? true : false); }
|
||||
if (val = data_values[PSTR("Power")]) { onoff.setPower(val.getUInt() ? true : false); }
|
||||
}
|
||||
break;
|
||||
// case Z_Data_Type::Z_Thermo:
|
||||
// {
|
||||
// Z_Data_Thermo & thermo = (Z_Data_Thermo&) data;
|
||||
// }
|
||||
// break;
|
||||
// case Z_Data_Type::Z_Alarm:
|
||||
// {
|
||||
// Z_Data_Alarm & alarm = (Z_Data_Alarm&) data;
|
||||
// }
|
||||
// break;
|
||||
}
|
||||
break;
|
||||
// case Z_Data_Type::Z_Thermo:
|
||||
// {
|
||||
// Z_Data_Thermo & thermo = (Z_Data_Thermo&) data;
|
||||
// }
|
||||
// break;
|
||||
// case Z_Data_Type::Z_Alarm:
|
||||
// {
|
||||
// Z_Data_Alarm & alarm = (Z_Data_Alarm&) data;
|
||||
// }
|
||||
// break;
|
||||
case Z_Data_Type::Z_Device:
|
||||
{
|
||||
if (val = data_values[PSTR(D_CMND_ZIGBEE_LINKQUALITY)]) { device.lqi = val.getUInt(); }
|
||||
if (val = data_values[PSTR("BatteryPercentage")]) { device.batterypercent = val.getUInt(); }
|
||||
if (val = data_values[PSTR("LastSeen")]) { device.last_seen = val.getUInt(); }
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
@ -1454,56 +1451,39 @@ void CmndZbData(void) {
|
|||
{ // scope to force object deallocation
|
||||
Z_attribute_list device_attr;
|
||||
device.toAttributes(device_attr);
|
||||
attr_data.addAttribute(F("_00")).setStrRaw(device_attr.toString(true).c_str());
|
||||
attr_data.addAttribute(F("_")).setStrRaw(device_attr.toString(true).c_str());
|
||||
}
|
||||
|
||||
// Iterate on data objects
|
||||
for (auto & data_elt : device.data) {
|
||||
Z_attribute_list inner_attr;
|
||||
char key[4];
|
||||
snprintf_P(key, sizeof(key), "?%02X", data_elt.getEndpoint());
|
||||
// The key is in the form "L01", where 'L' is the type and '01' the endpoint in hex format
|
||||
// 'P' = Power
|
||||
// 'L' = Light
|
||||
// 'O' = OnOff
|
||||
// 'T' = Thermo & sensors
|
||||
// 'A' = Alarm
|
||||
// '?' = Device wide
|
||||
//
|
||||
char key[8];
|
||||
if (data_elt.validConfig()) {
|
||||
snprintf_P(key, sizeof(key), "?%02X.%1X", data_elt.getEndpoint(), data_elt.getConfig());
|
||||
} else {
|
||||
snprintf_P(key, sizeof(key), "?%02X", data_elt.getEndpoint());
|
||||
}
|
||||
|
||||
Z_Data_Type data_type = data_elt.getType();
|
||||
key[0] = Z_Data::DataTypeToChar(data_type);
|
||||
switch (data_type) {
|
||||
case Z_Data_Type::Z_Plug:
|
||||
{
|
||||
key[0] = 'P';
|
||||
((Z_Data_Plug&)data_elt).toAttributes(inner_attr, data_type);
|
||||
}
|
||||
((Z_Data_Plug&)data_elt).toAttributes(inner_attr, data_type);
|
||||
break;
|
||||
case Z_Data_Type::Z_Light:
|
||||
{
|
||||
key[0] = 'L';
|
||||
((Z_Data_Light&)data_elt).toAttributes(inner_attr, data_type);
|
||||
}
|
||||
((Z_Data_Light&)data_elt).toAttributes(inner_attr, data_type);
|
||||
break;
|
||||
case Z_Data_Type::Z_OnOff:
|
||||
{
|
||||
key[0] = 'O';
|
||||
((Z_Data_OnOff&)data_elt).toAttributes(inner_attr, data_type);
|
||||
}
|
||||
((Z_Data_OnOff&)data_elt).toAttributes(inner_attr, data_type);
|
||||
break;
|
||||
case Z_Data_Type::Z_Thermo:
|
||||
{
|
||||
key[0] = 'T';
|
||||
((Z_Data_Thermo&)data_elt).toAttributes(inner_attr, data_type);
|
||||
}
|
||||
((Z_Data_Thermo&)data_elt).toAttributes(inner_attr, data_type);
|
||||
break;
|
||||
case Z_Data_Type::Z_Alarm:
|
||||
{
|
||||
key[0] = 'A';
|
||||
((Z_Data_Alarm&)data_elt).toAttributes(inner_attr, data_type);
|
||||
}
|
||||
((Z_Data_Alarm&)data_elt).toAttributes(inner_attr, data_type);
|
||||
break;
|
||||
}
|
||||
if (key[0] != '?') {
|
||||
if ((key[0] != '\0') && (key[0] != '?')) {
|
||||
attr_data.addAttribute(key).setStrRaw(inner_attr.toString(true).c_str());
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue