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_JSON_ZIGBEE_ZCL_SENT "ZbZCLSent"
#define D_JSON_ZIGBEE_RECEIVED "ZbReceived"
#define D_JSON_ZIGBEE_INFO "ZbInfo"
#define D_CMND_ZIGBEE_BIND "Bind"
#define D_JSON_ZIGBEE_BIND "ZbBind"
#define D_CMND_ZIGBEE_UNBIND "Unbind"

View File

@ -73,7 +73,7 @@ public:
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
// True if a configuration was changed
@ -215,7 +215,7 @@ public:
inline bool validColormode(void) const { return 0xFF != colormode; }
inline bool validDimmer(void) const { return 0xFF != dimmer; }
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 validX(void) const { return 0xFFFF != x; }
inline bool validY(void) const { return 0xFFFF != y; }
@ -674,6 +674,18 @@ public:
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
void toAttributes(Z_attribute_list & attr_list) const;
@ -791,9 +803,7 @@ public:
uint8_t getNextSeqNumber(uint16_t shortaddr);
// Dump json
static String dumpLightState(const Z_Device & device);
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);
// Hue support
@ -810,7 +820,6 @@ public:
// Append or clear attributes Json structure
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
bool jsonIsConflict(uint16_t shortaddr, const Z_attribute_list &attr_list) const;
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
//
void Z_Devices::jsonPublishFlushAttrList(const Z_Device & device, const String & attr_list_string) {
const char * fname = zigbee_devices.getFriendlyName(device.shortaddr);
bool use_fname = (Settings.flag4.zigbee_use_names) && (fname); // should we replace shortaddr with friendlyname?
void Z_Device::jsonPublishAttrList(const char * json_prefix, const Z_attribute_list &attr_list) const {
bool use_fname = (Settings.flag4.zigbee_use_names) && (friendlyName); // should we replace shortaddr with friendlyname?
TasmotaGlobal.mqtt_data[0] = 0; // clear string
// Do we prefix with `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?
if (use_fname) {
Response_P(PSTR("%s{\"%s\":{"), TasmotaGlobal.mqtt_data, fname);
Response_P(PSTR("%s{\"%s\":{"), TasmotaGlobal.mqtt_data, friendlyName);
} 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...."
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
if (fname) {
ResponseAppend_P(PSTR("\"" D_JSON_ZIGBEE_NAME "\":\"%s\","), EscapeJSONString(fname).c_str());
if (friendlyName) {
ResponseAppend_P(PSTR("\"" D_JSON_ZIGBEE_NAME "\":\"%s\","), EscapeJSONString(friendlyName).c_str());
}
// 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) {
ResponseAppend_P(PSTR("}"));
}
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
char stemp[TOPSZ];
strlcpy(stemp, (!strlen(fname)) ? MQTT_TOPIC : fname, sizeof(stemp));
strlcpy(stemp, (!strlen(friendlyName)) ? MQTT_TOPIC : friendlyName, sizeof(stemp));
MakeValidMqtt(0, stemp);
//Create topic with Prefix3 and cleaned up friendly name
char frtopic[TOPSZ];
@ -537,7 +536,7 @@ void Z_Devices::jsonPublishFlushAttrList(const Z_Device & device, const String &
MqttPublish(frtopic, Settings.flag.mqtt_sensor_retain);
} else {
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);
}
} else {
@ -557,7 +556,7 @@ void Z_Devices::jsonPublishFlush(uint16_t shortaddr) {
gZbLastMessage.groupaddr = attr_list.group_id; // %zbgroup%
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
}
}
@ -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;
char hex[8];
/*********************************************************************************************\
*
* Methods below build a JSON representation of device data
* 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?
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);
if (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
attr_list.addAttribute(F("Reachable")).setBool(device.getReachable());
if (device.validPower()) { attr_list.addAttribute(F("Power")).setUInt(device.getPower()); }
const Z_Data_Light & light = device.data.find<Z_Data_Light>(0);
attr_list.addAttribute(F("Reachable")).setBool(getReachable());
if (validPower()) { attr_list.addAttribute(F("Power")).setUInt(getPower()); }
const Z_Data_Light & light = data.find<Z_Data_Light>(0);
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
if (light.validHue()) {
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 = 2: simple dump of devices addresses and names, endpoints, light
// Mode = 3: dump last known data attributes
// add_device_name : do we add shortaddr/name ?
String Z_Devices::dumpSingleDevice(uint32_t dump_mode, const class Z_Device & device, bool add_device_name, bool add_brackets) {
uint16_t shortaddr = device.shortaddr;
char hex[22];
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);
}
// String Z_Device::dumpSingleDevice(uint32_t dump_mode, bool add_device_name, bool add_brackets) const {
void Z_Device::jsonDumpSingleDevice(Z_attribute_list & attr_list, uint32_t dump_mode, bool add_name) const {
if (add_name) {
jsonAddDeviceNamme(attr_list);
}
if (dump_mode >= 2) {
hex[0] = '0'; // prefix with '0x'
hex[1] = 'x';
Uint64toHex(device.longaddr, &hex[2], 64);
attr_list.addAttribute(F("IEEEAddr")).setStr(hex);
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());
jsonAddIEEE(attr_list);
jsonAddModelManuf(attr_list);
jsonAddEndpoints(attr_list);
jsonAddConfig(attr_list);
}
if (dump_mode >= 3) {
// show internal data - mostly last known values
for (auto & data_elt : device.data) {
Z_Data_Type data_type = data_elt.getType();
data_elt.toAttributes(attr_list, data_type);
}
jsonAddDataAttributes(attr_list);
// add device wide attributes
device.toAttributes(attr_list);
jsonAddDeviceAttributes(attr_list);
}
return attr_list.toString(add_brackets);
}
// 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) {
// dump light mode for all 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 {
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();
@ -814,20 +832,6 @@ Z_Data_Light & Z_Devices::getLight(uint16_t shortaddr) {
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
\*********************************************************************************************/

View File

@ -1967,7 +1967,8 @@ bool Z_parseAttributeKey(class Z_attribute & attr) {
// Input:
// the Json object to add attributes to
// 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
for (uint32_t i = 0; i < ARRAY_SIZE(Z_PostProcess); i++) {
const Z_AttributeConverter *converter = &Z_PostProcess[i];

View File

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