mirror of https://github.com/arendst/Tasmota.git
Merge pull request #16167 from s-hadinger/zigbee_battpercent
Zigbee include "BatteryPercentage" in all messages
This commit is contained in:
commit
f39679512b
|
@ -10,6 +10,7 @@ All notable changes to this project will be documented in this file.
|
||||||
- Support for multiple ``IRsend`` GPIOs
|
- Support for multiple ``IRsend`` GPIOs
|
||||||
- Zigbee added recording of when the battery was last reported
|
- Zigbee added recording of when the battery was last reported
|
||||||
- Zigbee add Battery auto-probe (can be disabled with ``SetOption143 1``)
|
- Zigbee add Battery auto-probe (can be disabled with ``SetOption143 1``)
|
||||||
|
- Zigbee include "BatteryPercentage" in all messages
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
- ESP32 LVGL library from v8.2.0 to v8.3.0
|
- ESP32 LVGL library from v8.2.0 to v8.3.0
|
||||||
|
|
|
@ -628,6 +628,7 @@
|
||||||
#define D_CMND_ZIGBEE_SAVE "Save"
|
#define D_CMND_ZIGBEE_SAVE "Save"
|
||||||
#define D_CMND_ZIGBEE_LINKQUALITY "LinkQuality"
|
#define D_CMND_ZIGBEE_LINKQUALITY "LinkQuality"
|
||||||
#define D_CMND_ZIGBEE_CLUSTER "Cluster"
|
#define D_CMND_ZIGBEE_CLUSTER "Cluster"
|
||||||
|
#define D_CMND_ZIGBEE_BATTPERCENT "BatteryPercentage"
|
||||||
#define D_CMND_ZIGBEE_ENDPOINT "Endpoint"
|
#define D_CMND_ZIGBEE_ENDPOINT "Endpoint"
|
||||||
#define D_CMND_ZIGBEE_GROUP "Group"
|
#define D_CMND_ZIGBEE_GROUP "Group"
|
||||||
#define D_CMND_ZIGBEE_MANUF "Manuf"
|
#define D_CMND_ZIGBEE_MANUF "Manuf"
|
||||||
|
|
|
@ -231,6 +231,7 @@ public:
|
||||||
uint8_t src_ep; // source endpoint, 0xFF if unknown
|
uint8_t src_ep; // source endpoint, 0xFF if unknown
|
||||||
uint8_t lqi; // linkquality, 0xFF if unknown
|
uint8_t lqi; // linkquality, 0xFF if unknown
|
||||||
uint16_t group_id; // group address OxFFFF if inknown
|
uint16_t group_id; // group address OxFFFF if inknown
|
||||||
|
uint8_t batt_percent; // battery percentage
|
||||||
|
|
||||||
Z_attribute_list():
|
Z_attribute_list():
|
||||||
LList<Z_attribute>(), // call superclass constructor
|
LList<Z_attribute>(), // call superclass constructor
|
||||||
|
@ -248,11 +249,15 @@ public:
|
||||||
src_ep = 0xFF;
|
src_ep = 0xFF;
|
||||||
lqi = 0xFF;
|
lqi = 0xFF;
|
||||||
group_id = 0xFFFF;
|
group_id = 0xFFFF;
|
||||||
|
batt_percent = 0xFF;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool isValidSrcEp(void) const { return 0xFF != src_ep; }
|
inline bool validSrcEp(void) const { return 0xFF != src_ep; }
|
||||||
inline bool isValidLQI(void) const { return 0xFF != lqi; }
|
inline bool validLQI(void) const { return 0xFF != lqi; }
|
||||||
inline bool isValidGroupId(void) const { return 0xFFFF != group_id; }
|
inline bool validGroupId(void) const { return 0xFFFF != group_id; }
|
||||||
|
inline bool validBattPercent(void) const { return 0xFF != batt_percent; }
|
||||||
|
|
||||||
|
inline void setBattPercent(uint8_t batt) { batt_percent = batt; }
|
||||||
|
|
||||||
// the following addAttribute() compute the suffix and increments it
|
// the following addAttribute() compute the suffix and increments it
|
||||||
// Add attribute to the list, given cluster and attribute id
|
// Add attribute to the list, given cluster and attribute id
|
||||||
|
@ -273,7 +278,7 @@ public:
|
||||||
// dump the entire structure as JSON, starting from head (as parameter)
|
// dump the entire structure as JSON, starting from head (as parameter)
|
||||||
// does not start not end with a comma
|
// does not start not end with a comma
|
||||||
// do we enclosed in brackets '{' '}'
|
// do we enclosed in brackets '{' '}'
|
||||||
String toString(bool enclose_brackets = false) const;
|
String toString(bool enclose_brackets = false, bool include_battery = false) const;
|
||||||
|
|
||||||
// find if attribute with same key already exists, return null if not found
|
// find if attribute with same key already exists, return null if not found
|
||||||
const Z_attribute * findAttribute(uint16_t cluster, uint16_t attr_id, uint8_t suffix = 0) const;
|
const Z_attribute * findAttribute(uint16_t cluster, uint16_t attr_id, uint8_t suffix = 0) const;
|
||||||
|
@ -765,7 +770,7 @@ Z_attribute & Z_attribute_list::addAttribute(const char * name, const char * nam
|
||||||
return attr;
|
return attr;
|
||||||
}
|
}
|
||||||
|
|
||||||
String Z_attribute_list::toString(bool enclose_brackets) const {
|
String Z_attribute_list::toString(bool enclose_brackets, bool include_battery) const {
|
||||||
String res = "";
|
String res = "";
|
||||||
if (enclose_brackets) { res += '{'; }
|
if (enclose_brackets) { res += '{'; }
|
||||||
bool prefix_comma = false;
|
bool prefix_comma = false;
|
||||||
|
@ -773,22 +778,29 @@ String Z_attribute_list::toString(bool enclose_brackets) const {
|
||||||
res += attr.toString(prefix_comma);
|
res += attr.toString(prefix_comma);
|
||||||
prefix_comma = true;
|
prefix_comma = true;
|
||||||
}
|
}
|
||||||
|
// add battery percentage if available, and if BatteryPercentage is not already present
|
||||||
|
if (include_battery && validBattPercent() && countAttribute(PSTR(D_CMND_ZIGBEE_BATTPERCENT)) == 0) {
|
||||||
|
if (prefix_comma) { res += ','; }
|
||||||
|
prefix_comma = true;
|
||||||
|
res += F("\"" D_CMND_ZIGBEE_BATTPERCENT "\":");
|
||||||
|
res += batt_percent;
|
||||||
|
}
|
||||||
// add source endpoint
|
// add source endpoint
|
||||||
if (0xFF != src_ep) {
|
if (validSrcEp()) {
|
||||||
if (prefix_comma) { res += ','; }
|
if (prefix_comma) { res += ','; }
|
||||||
prefix_comma = true;
|
prefix_comma = true;
|
||||||
res += F("\"" D_CMND_ZIGBEE_ENDPOINT "\":");
|
res += F("\"" D_CMND_ZIGBEE_ENDPOINT "\":");
|
||||||
res += src_ep;
|
res += src_ep;
|
||||||
}
|
}
|
||||||
// add group address
|
// add group address
|
||||||
if (0xFFFF != group_id) {
|
if (validGroupId()) {
|
||||||
if (prefix_comma) { res += ','; }
|
if (prefix_comma) { res += ','; }
|
||||||
prefix_comma = true;
|
prefix_comma = true;
|
||||||
res += F("\"" D_CMND_ZIGBEE_GROUP "\":");
|
res += F("\"" D_CMND_ZIGBEE_GROUP "\":");
|
||||||
res += group_id;
|
res += group_id;
|
||||||
}
|
}
|
||||||
// add lqi
|
// add lqi
|
||||||
if (0xFF != lqi) {
|
if (validLQI()) {
|
||||||
if (prefix_comma) { res += ','; }
|
if (prefix_comma) { res += ','; }
|
||||||
prefix_comma = true;
|
prefix_comma = true;
|
||||||
res += F("\"" D_CMND_ZIGBEE_LINKQUALITY "\":");
|
res += F("\"" D_CMND_ZIGBEE_LINKQUALITY "\":");
|
||||||
|
|
|
@ -737,7 +737,7 @@ public:
|
||||||
uint32_t batt_last_seen; // Time when we last received battery status (epoch), 0 means unknown, 0xFFFFFFFF means that the device has no battery
|
uint32_t batt_last_seen; // Time when we last received battery status (epoch), 0 means unknown, 0xFFFFFFFF means that the device has no battery
|
||||||
uint32_t batt_last_probed; // Time when the device was last probed for batteyr values
|
uint32_t batt_last_probed; // Time when the device was last probed for batteyr values
|
||||||
uint8_t lqi; // lqi from last message, 0xFF means unknown
|
uint8_t lqi; // lqi from last message, 0xFF means unknown
|
||||||
uint8_t batterypercent; // battery percentage (0..100), 0xFF means unknwon
|
uint8_t batt_percent; // battery percentage (0..100), 0xFF means unknwon
|
||||||
uint16_t reserved_for_alignment;
|
uint16_t reserved_for_alignment;
|
||||||
// Debounce informmation when receiving commands
|
// Debounce informmation when receiving commands
|
||||||
// If we receive the same ZCL transaction number from the same device and the same endpoint within 300ms
|
// If we receive the same ZCL transaction number from the same device and the same endpoint within 300ms
|
||||||
|
@ -765,7 +765,7 @@ public:
|
||||||
batt_last_seen(0),
|
batt_last_seen(0),
|
||||||
batt_last_probed(0),
|
batt_last_probed(0),
|
||||||
lqi(0xFF),
|
lqi(0xFF),
|
||||||
batterypercent(0xFF),
|
batt_percent(0xFF),
|
||||||
reserved_for_alignment(0xFFFF),
|
reserved_for_alignment(0xFFFF),
|
||||||
debounce_endpoint(0),
|
debounce_endpoint(0),
|
||||||
debounce_transact(0)
|
debounce_transact(0)
|
||||||
|
@ -781,7 +781,7 @@ public:
|
||||||
inline bool validPower(uint8_t ep =0) const;
|
inline bool validPower(uint8_t ep =0) const;
|
||||||
|
|
||||||
inline bool validLqi(void) const { return 0xFF != lqi; }
|
inline bool validLqi(void) const { return 0xFF != lqi; }
|
||||||
inline bool validBatteryPercent(void) const { return 0xFF != batterypercent; }
|
inline bool validBatteryPercent(void) const { return 0xFF != batt_percent; }
|
||||||
inline bool validLastSeen(void) const { return 0x0 != last_seen; }
|
inline bool validLastSeen(void) const { return 0x0 != last_seen; }
|
||||||
inline bool validBattLastSeen(void) const { return (0x0 != batt_last_seen) && (0xFFFFFFFF != batt_last_seen); }
|
inline bool validBattLastSeen(void) const { return (0x0 != batt_last_seen) && (0xFFFFFFFF != batt_last_seen); }
|
||||||
|
|
||||||
|
@ -794,8 +794,13 @@ public:
|
||||||
inline void setRouter(bool router) { is_router = router; }
|
inline void setRouter(bool router) { is_router = router; }
|
||||||
|
|
||||||
inline void setLQI(uint8_t _lqi) { lqi = _lqi; }
|
inline void setLQI(uint8_t _lqi) { lqi = _lqi; }
|
||||||
inline void setBatteryPercent(uint8_t bp) {
|
// set battery percentage to new value - and mark timestamp only if time is valid
|
||||||
batterypercent = bp;
|
// trigger an immediate `ZbSave` since it's important information to keep
|
||||||
|
void setBatteryPercent(uint8_t bp) {
|
||||||
|
if (batt_percent != bp) {
|
||||||
|
batt_percent = bp;
|
||||||
|
Z_Set_Save_Data_Timer_Once(0);
|
||||||
|
}
|
||||||
if (Rtc.utc_time >= START_VALID_TIME) {
|
if (Rtc.utc_time >= START_VALID_TIME) {
|
||||||
batt_last_seen = Rtc.utc_time;
|
batt_last_seen = Rtc.utc_time;
|
||||||
}
|
}
|
||||||
|
|
|
@ -491,12 +491,12 @@ bool Z_Devices::jsonIsConflict(uint16_t shortaddr, const Z_attribute_list &attr_
|
||||||
}
|
}
|
||||||
|
|
||||||
// compare groups
|
// compare groups
|
||||||
if (device.attr_list.isValidGroupId() && attr_list.isValidGroupId()) {
|
if (device.attr_list.validGroupId() && attr_list.validGroupId()) {
|
||||||
if (device.attr_list.group_id != attr_list.group_id) { return true; } // groups are in conflict
|
if (device.attr_list.group_id != attr_list.group_id) { return true; } // groups are in conflict
|
||||||
}
|
}
|
||||||
|
|
||||||
// compare src_ep
|
// compare src_ep
|
||||||
if (device.attr_list.isValidSrcEp() && attr_list.isValidSrcEp()) {
|
if (device.attr_list.validSrcEp() && attr_list.validSrcEp()) {
|
||||||
if (device.attr_list.src_ep != attr_list.src_ep) { return true; }
|
if (device.attr_list.src_ep != attr_list.src_ep) { return true; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -547,7 +547,7 @@ void Z_Device::jsonPublishAttrList(const char * json_prefix, const Z_attribute_l
|
||||||
ResponseAppend_P(PSTR("\"" D_JSON_ZIGBEE_NAME "\":\"%s\","), EscapeJSONString(friendlyName).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.toString(false).c_str());
|
ResponseAppend_P(PSTR("%s}"), attr_list.toString(false, true).c_str()); // (false, true) - include battery
|
||||||
|
|
||||||
if (!Settings->flag5.zb_omit_json_addr) {
|
if (!Settings->flag5.zb_omit_json_addr) {
|
||||||
ResponseAppend_P(PSTR("}"));
|
ResponseAppend_P(PSTR("}"));
|
||||||
|
@ -577,7 +577,7 @@ void Z_Device::jsonPublishAttrList(const char * json_prefix, const Z_attribute_l
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (Settings->flag5.zb_topic_endpoint) {
|
if (Settings->flag5.zb_topic_endpoint) {
|
||||||
if (attr_list.isValidSrcEp()) {
|
if (attr_list.validSrcEp()) {
|
||||||
snprintf_P(subtopic, sizeof(subtopic), PSTR("%s_%d"), subtopic, attr_list.src_ep);
|
snprintf_P(subtopic, sizeof(subtopic), PSTR("%s_%d"), subtopic, attr_list.src_ep);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -604,6 +604,11 @@ 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%
|
||||||
|
|
||||||
|
// add battery percentage from last known value
|
||||||
|
if (device.validBatteryPercent()) {
|
||||||
|
attr_list.setBattPercent(device.batt_percent);
|
||||||
|
}
|
||||||
|
|
||||||
device.jsonPublishAttrList(PSTR(D_JSON_ZIGBEE_RECEIVED), attr_list);
|
device.jsonPublishAttrList(PSTR(D_JSON_ZIGBEE_RECEIVED), attr_list);
|
||||||
attr_list.reset(); // clear the attributes
|
attr_list.reset(); // clear the attributes
|
||||||
}
|
}
|
||||||
|
@ -731,7 +736,7 @@ void Z_Device::jsonAddDataAttributes(Z_attribute_list & attr_list) const {
|
||||||
// Add "BatteryPercentage", "LastSeen", "LastSeenEpoch", "LinkQuality"
|
// Add "BatteryPercentage", "LastSeen", "LastSeenEpoch", "LinkQuality"
|
||||||
void Z_Device::jsonAddDeviceAttributes(Z_attribute_list & attr_list) const {
|
void Z_Device::jsonAddDeviceAttributes(Z_attribute_list & attr_list) const {
|
||||||
attr_list.addAttributePMEM(PSTR("Reachable")).setBool(getReachable());
|
attr_list.addAttributePMEM(PSTR("Reachable")).setBool(getReachable());
|
||||||
if (validBatteryPercent()) { attr_list.addAttributePMEM(PSTR("BatteryPercentage")).setUInt(batterypercent); }
|
if (validBatteryPercent()) { attr_list.addAttributePMEM(PSTR("BatteryPercentage")).setUInt(batt_percent); }
|
||||||
if (validBattLastSeen()) {
|
if (validBattLastSeen()) {
|
||||||
attr_list.addAttributePMEM(PSTR("BatteryLastSeenEpoch")).setUInt(batt_last_seen);
|
attr_list.addAttributePMEM(PSTR("BatteryLastSeenEpoch")).setUInt(batt_last_seen);
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,7 +46,7 @@ int32_t hydrateDeviceWideData(class Z_Device & device, const SBuffer & buf, size
|
||||||
}
|
}
|
||||||
device.last_seen = buf.get32(start+1);
|
device.last_seen = buf.get32(start+1);
|
||||||
device.lqi = buf.get8(start + 5);
|
device.lqi = buf.get8(start + 5);
|
||||||
device.batterypercent = buf.get8(start + 6);
|
device.batt_percent = buf.get8(start + 6);
|
||||||
if (segment_len >= 10) {
|
if (segment_len >= 10) {
|
||||||
device.batt_last_seen = buf.get32(start+7);
|
device.batt_last_seen = buf.get32(start+7);
|
||||||
}
|
}
|
||||||
|
@ -124,7 +124,7 @@ SBuffer hibernateDeviceData(const struct Z_Device & device) {
|
||||||
buf.add8(10); // 10 bytes
|
buf.add8(10); // 10 bytes
|
||||||
buf.add32(device.last_seen);
|
buf.add32(device.last_seen);
|
||||||
buf.add8(device.lqi);
|
buf.add8(device.lqi);
|
||||||
buf.add8(device.batterypercent);
|
buf.add8(device.batt_percent);
|
||||||
// now storing batt_last_seen
|
// now storing batt_last_seen
|
||||||
buf.add32(device.batt_last_seen);
|
buf.add32(device.batt_last_seen);
|
||||||
|
|
||||||
|
@ -280,6 +280,19 @@ void Z_SaveDataTimer(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster, u
|
||||||
Z_Set_Save_Data_Timer(0); // set a new timer
|
Z_Set_Save_Data_Timer(0); // set a new timer
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Callback for saving all data once, used after receiving important events
|
||||||
|
//
|
||||||
|
int32_t Z_Set_Save_Data_Timer_Once(uint8_t value) {
|
||||||
|
zigbee_devices.setTimer(0x0000, 0, 0 /* now */, 0, 0, Z_CAT_ALWAYS, 0 /* value */, &Z_SaveDataTimerOnce);
|
||||||
|
return 0; // continue
|
||||||
|
}
|
||||||
|
|
||||||
|
void Z_SaveDataTimerOnce(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster, uint8_t endpoint, uint32_t value) {
|
||||||
|
hibernateAllData();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#ifdef USE_ZIGBEE_EEPROM
|
#ifdef USE_ZIGBEE_EEPROM
|
||||||
void ZFS_Erase(void) {
|
void ZFS_Erase(void) {
|
||||||
if (zigbee.eeprom_present) {
|
if (zigbee.eeprom_present) {
|
||||||
|
|
|
@ -1703,7 +1703,7 @@ void Z_IncomingMessage(class ZCLFrame &zcl_received) {
|
||||||
Z_Query_Battery(srcaddr); // do battery auto-probing when receiving commands
|
Z_Query_Battery(srcaddr); // do battery auto-probing when receiving commands
|
||||||
}
|
}
|
||||||
|
|
||||||
AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_ZIGBEE D_JSON_ZIGBEEZCL_RAW_RECEIVED ": {\"0x%04X\":{%s}}"), srcaddr, attr_list.toString().c_str());
|
AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_ZIGBEE D_JSON_ZIGBEEZCL_RAW_RECEIVED ": {\"0x%04X\":{%s}}"), srcaddr, attr_list.toString(false, false).c_str()); // don't include battery
|
||||||
|
|
||||||
// discard the message if it was sent by us (broadcast or group loopback)
|
// discard the message if it was sent by us (broadcast or group loopback)
|
||||||
if (srcaddr == localShortAddr) {
|
if (srcaddr == localShortAddr) {
|
||||||
|
|
|
@ -2039,8 +2039,8 @@ void ZigbeeShow(bool json)
|
||||||
}
|
}
|
||||||
snprintf_P(sbatt, sizeof(sbatt),
|
snprintf_P(sbatt, sizeof(sbatt),
|
||||||
msg[ZB_WEB_BATTERY],
|
msg[ZB_WEB_BATTERY],
|
||||||
device.batterypercent, dhm,
|
device.batt_percent, dhm,
|
||||||
changeUIntScale(device.batterypercent, 0, 100, 0, 14),
|
changeUIntScale(device.batt_percent, 0, 100, 0, 14),
|
||||||
(color & 0xFF0000) >> 16, (color & 0x00FF00) >> 8, (color & 0x0000FF)
|
(color & 0xFF0000) >> 16, (color & 0x00FF00) >> 8, (color & 0x0000FF)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -66,7 +66,7 @@ extern "C" {
|
||||||
return d->lqi == 255 ? -1 : d->lqi;
|
return d->lqi == 255 ? -1 : d->lqi;
|
||||||
}
|
}
|
||||||
int32_t zd_battery(const class Z_Device* d) {
|
int32_t zd_battery(const class Z_Device* d) {
|
||||||
return d->batterypercent == 255 ? -1 : d->batterypercent;
|
return d->batt_percent == 255 ? -1 : d->batt_percent;
|
||||||
}
|
}
|
||||||
int32_t zd_battery_lastseen(const class Z_Device* d) {
|
int32_t zd_battery_lastseen(const class Z_Device* d) {
|
||||||
return 0; // TODO not yet known
|
return 0; // TODO not yet known
|
||||||
|
|
Loading…
Reference in New Issue