Zigbee more consistent messages for ZbInfo and ZbLight

This commit is contained in:
Stephan Hadinger 2020-11-13 13:32:45 +01:00
parent bbdce8af29
commit 5f39d15a84
5 changed files with 135 additions and 118 deletions

View File

@ -565,6 +565,7 @@
#define D_CMND_ZIGBEE_RESPONSE "Response" #define D_CMND_ZIGBEE_RESPONSE "Response"
#define D_JSON_ZIGBEE_ZCL_SENT "ZbZCLSent" #define D_JSON_ZIGBEE_ZCL_SENT "ZbZCLSent"
#define D_JSON_ZIGBEE_RECEIVED "ZbReceived" #define D_JSON_ZIGBEE_RECEIVED "ZbReceived"
#define D_JSON_ZIGBEE_INFO "ZbInfo"
#define D_CMND_ZIGBEE_BIND "Bind" #define D_CMND_ZIGBEE_BIND "Bind"
#define D_JSON_ZIGBEE_BIND "ZbBind" #define D_JSON_ZIGBEE_BIND "ZbBind"
#define D_CMND_ZIGBEE_UNBIND "Unbind" #define D_CMND_ZIGBEE_UNBIND "Unbind"

View File

@ -73,7 +73,7 @@ public:
inline uint8_t getEndpoint(void) const { return _endpoint; } inline uint8_t getEndpoint(void) const { return _endpoint; }
void toAttributes(Z_attribute_list & attr_list, Z_Data_Type type) const; void toAttributes(Z_attribute_list & attr_list) const;
// update internal structures after an attribut update // update internal structures after an attribut update
// True if a configuration was changed // True if a configuration was changed
@ -215,7 +215,7 @@ public:
inline bool validColormode(void) const { return 0xFF != colormode; } inline bool validColormode(void) const { return 0xFF != colormode; }
inline bool validDimmer(void) const { return 0xFF != dimmer; } inline bool validDimmer(void) const { return 0xFF != dimmer; }
inline bool validSat(void) const { return 0xFF != sat; } inline bool validSat(void) const { return 0xFF != sat; }
inline bool validHue(void) const { return 0xFFFF != hue; } inline bool validHue(void) const { return 0xFF != hue; }
inline bool validCT(void) const { return 0xFFFF != ct; } inline bool validCT(void) const { return 0xFFFF != ct; }
inline bool validX(void) const { return 0xFFFF != x; } inline bool validX(void) const { return 0xFFFF != x; }
inline bool validY(void) const { return 0xFFFF != y; } inline bool validY(void) const { return 0xFFFF != y; }
@ -674,6 +674,18 @@ public:
void setLastSeenNow(void); void setLastSeenNow(void);
// multiple function to dump part of the Device state into JSON
void jsonAddDeviceNamme(Z_attribute_list & attr_list) const;
void jsonAddIEEE(Z_attribute_list & attr_list) const;
void jsonAddModelManuf(Z_attribute_list & attr_list) const;
void jsonAddEndpoints(Z_attribute_list & attr_list) const;
void jsonAddConfig(Z_attribute_list & attr_list) const;
void jsonAddDataAttributes(Z_attribute_list & attr_list) const;
void jsonAddDeviceAttributes(Z_attribute_list & attr_list) const;
void jsonDumpSingleDevice(Z_attribute_list & attr_list, uint32_t dump_mode, bool add_name) const;
void jsonPublishAttrList(const char * json_prefix, const Z_attribute_list &attr_list) const;
void jsonLightState(Z_attribute_list & attr_list) const;
// dump device attributes to ZbData // dump device attributes to ZbData
void toAttributes(Z_attribute_list & attr_list) const; void toAttributes(Z_attribute_list & attr_list) const;
@ -791,9 +803,7 @@ public:
uint8_t getNextSeqNumber(uint16_t shortaddr); uint8_t getNextSeqNumber(uint16_t shortaddr);
// Dump json // Dump json
static String dumpLightState(const Z_Device & device);
String dumpDevice(uint32_t dump_mode, const Z_Device & device) const; String dumpDevice(uint32_t dump_mode, const Z_Device & device) const;
static String dumpSingleDevice(uint32_t dump_mode, const class Z_Device & device, bool add_device_name = true, bool add_brackets = true);
int32_t deviceRestore(JsonParserObject json); int32_t deviceRestore(JsonParserObject json);
// Hue support // Hue support
@ -810,7 +820,6 @@ public:
// Append or clear attributes Json structure // Append or clear attributes Json structure
void jsonAppend(uint16_t shortaddr, const Z_attribute_list &attr_list); void jsonAppend(uint16_t shortaddr, const Z_attribute_list &attr_list);
static void jsonPublishFlushAttrList(const Z_Device & device, const String & attr_list_string);
void jsonPublishFlush(uint16_t shortaddr); // publish the json message and clear buffer void jsonPublishFlush(uint16_t shortaddr); // publish the json message and clear buffer
bool jsonIsConflict(uint16_t shortaddr, const Z_attribute_list &attr_list) const; bool jsonIsConflict(uint16_t shortaddr, const Z_attribute_list &attr_list) const;
void jsonPublishNow(uint16_t shortaddr, Z_attribute_list &attr_list); void jsonPublishNow(uint16_t shortaddr, Z_attribute_list &attr_list);

View File

@ -497,39 +497,38 @@ void Z_Devices::jsonAppend(uint16_t shortaddr, const Z_attribute_list &attr_list
// //
// internal function to publish device information with respect to all `SetOption`s // internal function to publish device information with respect to all `SetOption`s
// //
void Z_Devices::jsonPublishFlushAttrList(const Z_Device & device, const String & attr_list_string) { void Z_Device::jsonPublishAttrList(const char * json_prefix, const Z_attribute_list &attr_list) const {
const char * fname = zigbee_devices.getFriendlyName(device.shortaddr); bool use_fname = (Settings.flag4.zigbee_use_names) && (friendlyName); // should we replace shortaddr with friendlyname?
bool use_fname = (Settings.flag4.zigbee_use_names) && (fname); // should we replace shortaddr with friendlyname?
TasmotaGlobal.mqtt_data[0] = 0; // clear string TasmotaGlobal.mqtt_data[0] = 0; // clear string
// Do we prefix with `ZbReceived`? // Do we prefix with `ZbReceived`?
if (!Settings.flag4.remove_zbreceived) { if (!Settings.flag4.remove_zbreceived) {
Response_P(PSTR("{\"" D_JSON_ZIGBEE_RECEIVED "\":")); Response_P(PSTR("{\"%s\":"), json_prefix);
} }
// What key do we use, shortaddr or name? // What key do we use, shortaddr or name?
if (use_fname) { if (use_fname) {
Response_P(PSTR("%s{\"%s\":{"), TasmotaGlobal.mqtt_data, fname); Response_P(PSTR("%s{\"%s\":{"), TasmotaGlobal.mqtt_data, friendlyName);
} else { } else {
Response_P(PSTR("%s{\"0x%04X\":{"), TasmotaGlobal.mqtt_data, device.shortaddr); Response_P(PSTR("%s{\"0x%04X\":{"), TasmotaGlobal.mqtt_data, shortaddr);
} }
// Add "Device":"0x...." // Add "Device":"0x...."
ResponseAppend_P(PSTR("\"" D_JSON_ZIGBEE_DEVICE "\":\"0x%04X\","), device.shortaddr); ResponseAppend_P(PSTR("\"" D_JSON_ZIGBEE_DEVICE "\":\"0x%04X\","), shortaddr);
// Add "Name":"xxx" if name is present // Add "Name":"xxx" if name is present
if (fname) { if (friendlyName) {
ResponseAppend_P(PSTR("\"" D_JSON_ZIGBEE_NAME "\":\"%s\","), EscapeJSONString(fname).c_str()); ResponseAppend_P(PSTR("\"" D_JSON_ZIGBEE_NAME "\":\"%s\","), EscapeJSONString(friendlyName).c_str());
} }
// Add all other attributes // Add all other attributes
ResponseAppend_P(PSTR("%s}}"), attr_list_string.c_str()); ResponseAppend_P(PSTR("%s}}"), attr_list.toString(false).c_str());
if (!Settings.flag4.remove_zbreceived) { if (!Settings.flag4.remove_zbreceived) {
ResponseAppend_P(PSTR("}")); ResponseAppend_P(PSTR("}"));
} }
if (Settings.flag4.zigbee_distinct_topics) { if (Settings.flag4.zigbee_distinct_topics) {
if (Settings.flag4.zb_topic_fname && fname) { if (Settings.flag4.zb_topic_fname && friendlyName) {
//Clean special characters and check size of friendly name //Clean special characters and check size of friendly name
char stemp[TOPSZ]; char stemp[TOPSZ];
strlcpy(stemp, (!strlen(fname)) ? MQTT_TOPIC : fname, sizeof(stemp)); strlcpy(stemp, (!strlen(friendlyName)) ? MQTT_TOPIC : friendlyName, sizeof(stemp));
MakeValidMqtt(0, stemp); MakeValidMqtt(0, stemp);
//Create topic with Prefix3 and cleaned up friendly name //Create topic with Prefix3 and cleaned up friendly name
char frtopic[TOPSZ]; char frtopic[TOPSZ];
@ -537,7 +536,7 @@ void Z_Devices::jsonPublishFlushAttrList(const Z_Device & device, const String &
MqttPublish(frtopic, Settings.flag.mqtt_sensor_retain); MqttPublish(frtopic, Settings.flag.mqtt_sensor_retain);
} else { } else {
char subtopic[16]; char subtopic[16];
snprintf_P(subtopic, sizeof(subtopic), PSTR("%04X/" D_RSLT_SENSOR), device.shortaddr); snprintf_P(subtopic, sizeof(subtopic), PSTR("%04X/" D_RSLT_SENSOR), shortaddr);
MqttPublishPrefixTopic_P(TELE, subtopic, Settings.flag.mqtt_sensor_retain); MqttPublishPrefixTopic_P(TELE, subtopic, Settings.flag.mqtt_sensor_retain);
} }
} else { } else {
@ -557,7 +556,7 @@ void Z_Devices::jsonPublishFlush(uint16_t shortaddr) {
gZbLastMessage.groupaddr = attr_list.group_id; // %zbgroup% gZbLastMessage.groupaddr = attr_list.group_id; // %zbgroup%
gZbLastMessage.endpoint = attr_list.src_ep; // %zbendpoint% gZbLastMessage.endpoint = attr_list.src_ep; // %zbendpoint%
jsonPublishFlushAttrList(device, attr_list.toString()); device.jsonPublishAttrList(PSTR(D_JSON_ZIGBEE_RECEIVED), attr_list);
attr_list.reset(); // clear the attributes attr_list.reset(); // clear the attributes
} }
} }
@ -615,110 +614,125 @@ Z_Device & Z_Devices::parseDeviceFromName(const char * param, bool short_must_be
} }
} }
// Display the tracked status for a light /*********************************************************************************************\
String Z_Devices::dumpLightState(const Z_Device & device) { *
Z_attribute_list attr_list; * Methods below build a JSON representation of device data
char hex[8]; * Used by: ZbLight, ZbStatus, ZbInfo
*
\*********************************************************************************************/
const char * fname = device.friendlyName; // Add "Device":"0x1234","Name":"FrienflyName"
void Z_Device::jsonAddDeviceNamme(Z_attribute_list & attr_list) const {
char hex[8];
const char * fname = friendlyName;
bool use_fname = (Settings.flag4.zigbee_use_names) && (fname); // should we replace shortaddr with friendlyname? bool use_fname = (Settings.flag4.zigbee_use_names) && (fname); // should we replace shortaddr with friendlyname?
snprintf_P(hex, sizeof(hex), PSTR("0x%04X"), device.shortaddr); snprintf_P(hex, sizeof(hex), PSTR("0x%04X"), shortaddr);
attr_list.addAttribute(F(D_JSON_ZIGBEE_DEVICE)).setStr(hex); attr_list.addAttribute(F(D_JSON_ZIGBEE_DEVICE)).setStr(hex);
if (fname) { if (fname) {
attr_list.addAttribute(F(D_JSON_ZIGBEE_NAME)).setStr(fname); attr_list.addAttribute(F(D_JSON_ZIGBEE_NAME)).setStr(fname);
} }
}
// Add "IEEEAddr":"0x1234567812345678"
void Z_Device::jsonAddIEEE(Z_attribute_list & attr_list) const {
char hex[22];
hex[0] = '0'; // prefix with '0x'
hex[1] = 'x';
Uint64toHex(longaddr, &hex[2], 64);
attr_list.addAttribute(F("IEEEAddr")).setStr(hex);
}
// Add "ModelId":"","Manufacturer":""
void Z_Device::jsonAddModelManuf(Z_attribute_list & attr_list) const {
if (modelId) {
attr_list.addAttribute(F(D_JSON_MODEL D_JSON_ID)).setStr(modelId);
}
if (manufacturerId) {
attr_list.addAttribute(F("Manufacturer")).setStr(manufacturerId);
}
}
// Add "Endpoints":[...]
void Z_Device::jsonAddEndpoints(Z_attribute_list & attr_list) const {
JsonGeneratorArray arr_ep;
for (uint32_t i = 0; i < endpoints_max; i++) {
uint8_t endpoint = endpoints[i];
if (0x00 == endpoint) { break; }
arr_ep.add(endpoint);
}
attr_list.addAttribute(F("Endpoints")).setStrRaw(arr_ep.toString().c_str());
}
// Add "Config":["",""...]
void Z_Device::jsonAddConfig(Z_attribute_list & attr_list) const {
JsonGeneratorArray arr_data;
for (auto & data_elt : 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());
}
// Add All data attributes
void Z_Device::jsonAddDataAttributes(Z_attribute_list & attr_list) const {
// show internal data - mostly last known values
for (auto & data_elt : data) {
data_elt.toAttributes(attr_list);
}
}
// Add "BatteryPercentage", "LastSeen", "LastSeenEpoch", "LinkQuality"
void Z_Device::jsonAddDeviceAttributes(Z_attribute_list & attr_list) const {
attr_list.addAttribute(F("Reachable")).setBool(getReachable());
if (validBatteryPercent()) { attr_list.addAttribute(PSTR("BatteryPercentage")).setUInt(batterypercent); }
if (validLastSeen()) {
if (Rtc.utc_time >= last_seen) {
attr_list.addAttribute(PSTR("LastSeen")).setUInt(Rtc.utc_time - last_seen);
}
attr_list.addAttribute(PSTR("LastSeenEpoch")).setUInt(last_seen);
}
if (validLqi()) { attr_list.addAttribute(PSTR(D_CMND_ZIGBEE_LINKQUALITY)).setUInt(lqi); }
}
if (device.valid()) {
// Display the tracked status for a light
void Z_Device::jsonLightState(Z_attribute_list & attr_list) const {
if (valid()) {
// dump all known values // dump all known values
attr_list.addAttribute(F("Reachable")).setBool(device.getReachable()); attr_list.addAttribute(F("Reachable")).setBool(getReachable());
if (device.validPower()) { attr_list.addAttribute(F("Power")).setUInt(device.getPower()); } if (validPower()) { attr_list.addAttribute(F("Power")).setUInt(getPower()); }
const Z_Data_Light & light = device.data.find<Z_Data_Light>(0); const Z_Data_Light & light = data.find<Z_Data_Light>(0);
if (&light != nullptr) { if (&light != nullptr) {
light.toAttributes(attr_list, Z_Data_Light::type); light.toAttributes(attr_list);
// Exception, we need to convert Hue to 0..360 instead of 0..254 // Exception, we need to convert Hue to 0..360 instead of 0..254
if (light.validHue()) { if (light.validHue()) {
attr_list.findOrCreateAttribute(PSTR("Hue")).setUInt(light.getHue()); attr_list.findOrCreateAttribute(PSTR("Hue")).setUInt(light.getHue());
} }
} }
// Z_Data_Light::toAttributes(attr_list, device.data.find<Z_Data_Light>(0));
} }
Z_attribute_list attr_list_root;
Z_attribute * attr_root;
if (use_fname) {
attr_root = &attr_list_root.addAttribute(fname);
} else {
attr_root = &attr_list_root.addAttribute(hex);
}
attr_root->setStrRaw(attr_list.toString(true).c_str());
return attr_list_root.toString(true);
} }
// Dump the internal memory of Zigbee devices // Dump the internal memory of Zigbee devices - does not include "Device" and "Name"
// Mode = 1: simple dump of devices addresses // Mode = 1: simple dump of devices addresses
// Mode = 2: simple dump of devices addresses and names, endpoints, light // Mode = 2: simple dump of devices addresses and names, endpoints, light
// Mode = 3: dump last known data attributes // Mode = 3: dump last known data attributes
// add_device_name : do we add shortaddr/name ? // String Z_Device::dumpSingleDevice(uint32_t dump_mode, bool add_device_name, bool add_brackets) const {
String Z_Devices::dumpSingleDevice(uint32_t dump_mode, const class Z_Device & device, bool add_device_name, bool add_brackets) { void Z_Device::jsonDumpSingleDevice(Z_attribute_list & attr_list, uint32_t dump_mode, bool add_name) const {
uint16_t shortaddr = device.shortaddr; if (add_name) {
char hex[22]; jsonAddDeviceNamme(attr_list);
Z_attribute_list attr_list;
if (add_device_name) {
snprintf_P(hex, sizeof(hex), PSTR("0x%04X"), shortaddr);
attr_list.addAttribute(F(D_JSON_ZIGBEE_DEVICE)).setStr(hex);
if (device.friendlyName > 0) {
attr_list.addAttribute(F(D_JSON_ZIGBEE_NAME)).setStr(device.friendlyName);
}
} }
if (dump_mode >= 2) { if (dump_mode >= 2) {
hex[0] = '0'; // prefix with '0x' jsonAddIEEE(attr_list);
hex[1] = 'x'; jsonAddModelManuf(attr_list);
Uint64toHex(device.longaddr, &hex[2], 64); jsonAddEndpoints(attr_list);
attr_list.addAttribute(F("IEEEAddr")).setStr(hex); jsonAddConfig(attr_list);
if (device.modelId) {
attr_list.addAttribute(F(D_JSON_MODEL D_JSON_ID)).setStr(device.modelId);
}
if (device.manufacturerId) {
attr_list.addAttribute(F("Manufacturer")).setStr(device.manufacturerId);
}
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());
} }
if (dump_mode >= 3) { if (dump_mode >= 3) {
// show internal data - mostly last known values jsonAddDataAttributes(attr_list);
for (auto & data_elt : device.data) {
Z_Data_Type data_type = data_elt.getType();
data_elt.toAttributes(attr_list, data_type);
}
// add device wide attributes // add device wide attributes
device.toAttributes(attr_list); jsonAddDeviceAttributes(attr_list);
} }
return attr_list.toString(add_brackets);
} }
// If &device == nullptr, then dump all // If &device == nullptr, then dump all
@ -729,11 +743,15 @@ String Z_Devices::dumpDevice(uint32_t dump_mode, const Z_Device & device) const
if (dump_mode < 2) { if (dump_mode < 2) {
// dump light mode for all devices // dump light mode for all devices
for (const auto & device2 : _devices) { for (const auto & device2 : _devices) {
json_arr.addStrRaw(dumpSingleDevice(dump_mode, device2).c_str()); Z_attribute_list attr_list;
device2.jsonDumpSingleDevice(attr_list, dump_mode, true);
json_arr.addStrRaw(attr_list.toString(true).c_str());
} }
} }
} else { } else {
json_arr.addStrRaw(dumpSingleDevice(dump_mode, device).c_str()); Z_attribute_list attr_list;
device.jsonDumpSingleDevice(attr_list, dump_mode, true);
json_arr.addStrRaw(attr_list.toString(true).c_str());
} }
return json_arr.toString(); return json_arr.toString();
@ -814,20 +832,6 @@ Z_Data_Light & Z_Devices::getLight(uint16_t shortaddr) {
return getShortAddr(shortaddr).data.get<Z_Data_Light>(); return getShortAddr(shortaddr).data.get<Z_Data_Light>();
} }
/*********************************************************************************************\
* Export device specific attributes to ZbData
\*********************************************************************************************/
void Z_Device::toAttributes(Z_attribute_list & attr_list) const {
if (validBatteryPercent()) { attr_list.addAttribute(PSTR("BatteryPercentage")).setUInt(batterypercent); }
if (validLastSeen()) {
if (Rtc.utc_time >= last_seen) {
attr_list.addAttribute(PSTR("LastSeen")).setUInt(Rtc.utc_time - last_seen);
}
attr_list.addAttribute(PSTR("LastSeenEpoch")).setUInt(last_seen);
}
if (validLqi()) { attr_list.addAttribute(PSTR(D_CMND_ZIGBEE_LINKQUALITY)).setUInt(lqi); }
}
/*********************************************************************************************\ /*********************************************************************************************\
* Device specific data handlers * Device specific data handlers
\*********************************************************************************************/ \*********************************************************************************************/

View File

@ -1967,7 +1967,8 @@ bool Z_parseAttributeKey(class Z_attribute & attr) {
// Input: // Input:
// the Json object to add attributes to // the Json object to add attributes to
// the type of object (necessary since the type system is unaware of the actual sub-type) // the type of object (necessary since the type system is unaware of the actual sub-type)
void Z_Data::toAttributes(Z_attribute_list & attr_list, Z_Data_Type type) const { void Z_Data::toAttributes(Z_attribute_list & attr_list) const {
Z_Data_Type type = getType();
// iterate through attributes to see which ones need to be exported // iterate through attributes to see which ones need to be exported
for (uint32_t i = 0; i < ARRAY_SIZE(Z_PostProcess); i++) { for (uint32_t i = 0; i < ARRAY_SIZE(Z_PostProcess); i++) {
const Z_AttributeConverter *converter = &Z_PostProcess[i]; const Z_AttributeConverter *converter = &Z_PostProcess[i];

View File

@ -1111,10 +1111,11 @@ void CmndZbLight(void) {
if (bulbtype < -1) { bulbtype = -1; } if (bulbtype < -1) { bulbtype = -1; }
device.setLightChannels(bulbtype); device.setLightChannels(bulbtype);
} }
String dump = zigbee_devices.dumpLightState(device); Z_attribute_list attr_list;
Response_P(PSTR("{\"" D_PRFX_ZB D_CMND_ZIGBEE_LIGHT "\":%s}"), dump.c_str()); device.jsonLightState(attr_list);
device.jsonPublishAttrList(PSTR(D_PRFX_ZB D_CMND_ZIGBEE_LIGHT), attr_list); // publish as ZbReceived
MqttPublishPrefixTopicRulesProcess_P(RESULT_OR_STAT, PSTR(D_PRFX_ZB D_CMND_ZIGBEE_LIGHT));
ResponseCmndDone(); ResponseCmndDone();
} }
// //
@ -1194,8 +1195,9 @@ void CmndZbInfo(void) {
// everything is good, we can send the command // everything is good, we can send the command
String device_info = Z_Devices::dumpSingleDevice(3, device, false, false); Z_attribute_list attr_list;
Z_Devices::jsonPublishFlushAttrList(device, device_info); device.jsonDumpSingleDevice(attr_list, 3, false); // don't add Device/Name
device.jsonPublishAttrList(PSTR(D_JSON_ZIGBEE_INFO), attr_list); // publish as ZbReceived
ResponseCmndDone(); ResponseCmndDone();
} }