mirror of https://github.com/arendst/Tasmota.git
Merge pull request #16148 from s-hadinger/zigbee_batt_autoprobe
Zigbee add Battery auto-probe (can be disabled with ``SetOption143 1``)
This commit is contained in:
commit
43a02cf369
|
@ -9,6 +9,7 @@ All notable changes to this project will be documented in this file.
|
||||||
- Support for Modbus bridge adding commands ``ModbusSend``, ``ModbusBaudrate`` and ``ModbusSerialConfig`` (#16013)
|
- Support for Modbus bridge adding commands ``ModbusSend``, ``ModbusBaudrate`` and ``ModbusSerialConfig`` (#16013)
|
||||||
- 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``)
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
- ESP32 LVGL library from v8.2.0 to v8.3.0
|
- ESP32 LVGL library from v8.2.0 to v8.3.0
|
||||||
|
|
|
@ -170,7 +170,7 @@ typedef union { // Restricted by MISRA-C Rule 18.4 bu
|
||||||
uint32_t mqtt_persistent : 1; // bit 26 (v11.1.0.1) - SetOption140 - (MQTT) MQTT clean session (0 = default) or persistent session (1)
|
uint32_t mqtt_persistent : 1; // bit 26 (v11.1.0.1) - SetOption140 - (MQTT) MQTT clean session (0 = default) or persistent session (1)
|
||||||
uint32_t gui_module_name : 1; // bit 27 (v11.1.0.3) - SetOption141 - (GUI) Disable display of GUI module name (1)
|
uint32_t gui_module_name : 1; // bit 27 (v11.1.0.3) - SetOption141 - (GUI) Disable display of GUI module name (1)
|
||||||
uint32_t wait_for_wifi_result : 1; // bit 28 (v11.1.0.4) - SetOption142 - (Wifi) Wait 1 second for wifi connection solving some FRITZ!Box modem issues (1)
|
uint32_t wait_for_wifi_result : 1; // bit 28 (v11.1.0.4) - SetOption142 - (Wifi) Wait 1 second for wifi connection solving some FRITZ!Box modem issues (1)
|
||||||
uint32_t spare29 : 1; // bit 29
|
uint32_t zigbee_no_batt_autoprobe : 1; // bit 29 (v12.0.2.4) - SetOption143 - (Zigbee) Disable Battery auto-probe and using auto-binding
|
||||||
uint32_t spare30 : 1; // bit 30
|
uint32_t spare30 : 1; // bit 30
|
||||||
uint32_t spare31 : 1; // bit 31
|
uint32_t spare31 : 1; // bit 31
|
||||||
};
|
};
|
||||||
|
|
|
@ -898,6 +898,8 @@
|
||||||
#define USE_ZIGBEE_DEBOUNCE_COMMANDS 200 // if commands are received from the same device/endpoint with same ZCL transaction number, discard packet in this time window (ms)
|
#define USE_ZIGBEE_DEBOUNCE_COMMANDS 200 // if commands are received from the same device/endpoint with same ZCL transaction number, discard packet in this time window (ms)
|
||||||
#define USE_ZIGBEE_MODELID "Tasmota Z2T" // reported "ModelId" (cluster 0000 / attribute 0005)
|
#define USE_ZIGBEE_MODELID "Tasmota Z2T" // reported "ModelId" (cluster 0000 / attribute 0005)
|
||||||
#define USE_ZIGBEE_MANUFACTURER "Tasmota" // reported "Manufacturer" (cluster 0000 / attribute 0004)
|
#define USE_ZIGBEE_MANUFACTURER "Tasmota" // reported "Manufacturer" (cluster 0000 / attribute 0004)
|
||||||
|
#define USE_ZIGBEE_BATT_REPROBE (24*3600) // Period in seconds during which we don't ask again for battery, default 1 day
|
||||||
|
#define USE_ZIGBEE_BATT_REPROBE_PAUSE (3600) // Min wait period when sending an autoprobe, default: wait at least 1 hour
|
||||||
#define USE_ZBBRIDGE_TLS // TLS support for zbbridge
|
#define USE_ZBBRIDGE_TLS // TLS support for zbbridge
|
||||||
#define USE_ZIGBEE_ZBBRIDGE_EEPROM 0x50 // I2C id for the ZBBridge EEPROM
|
#define USE_ZIGBEE_ZBBRIDGE_EEPROM 0x50 // I2C id for the ZBBridge EEPROM
|
||||||
// #define USE_ZIGBEE_FORCE_NO_CHILDREN // This feature forces `CONFIG_MAX_END_DEVICE_CHILDREN` to zero which means that the coordinator does not accept any direct child. End-devices must pair through a router.
|
// #define USE_ZIGBEE_FORCE_NO_CHILDREN // This feature forces `CONFIG_MAX_END_DEVICE_CHILDREN` to zero which means that the coordinator does not accept any direct child. End-devices must pair through a router.
|
||||||
|
|
|
@ -801,7 +801,7 @@ public:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
inline void setHasNoBattery(void) { batt_last_seen = 0xFFFFFFFF; }
|
inline void setHasNoBattery(void) { batt_last_seen = 0xFFFFFFFF; }
|
||||||
inline bool hasNoBattery(void) const { return 0xFFFFFFFF != batt_last_seen; }
|
inline bool hasNoBattery(void) const { return 0xFFFFFFFF == batt_last_seen; }
|
||||||
|
|
||||||
// Add an endpoint to a device
|
// Add an endpoint to a device
|
||||||
bool addEndpoint(uint8_t endpoint);
|
bool addEndpoint(uint8_t endpoint);
|
||||||
|
@ -959,6 +959,9 @@ public:
|
||||||
// device is reachable
|
// device is reachable
|
||||||
void deviceWasReached(uint16_t shortaddr);
|
void deviceWasReached(uint16_t shortaddr);
|
||||||
|
|
||||||
|
// device has no battery
|
||||||
|
void deviceHasNoBattery(uint16_t shortaddr);
|
||||||
|
|
||||||
// Timers
|
// Timers
|
||||||
void resetTimersForDevice(uint16_t shortaddr, uint16_t groupaddr, uint8_t category, uint16_t cluster = 0xFFFF, uint8_t endpoint = 0xFF);
|
void resetTimersForDevice(uint16_t shortaddr, uint16_t groupaddr, uint8_t category, uint16_t cluster = 0xFFFF, uint8_t endpoint = 0xFF);
|
||||||
void setTimer(uint16_t shortaddr, uint16_t groupaddr, uint32_t wait_ms, uint16_t cluster, uint8_t endpoint, uint8_t category, uint32_t value, Z_DeviceTimer func);
|
void setTimer(uint16_t shortaddr, uint16_t groupaddr, uint32_t wait_ms, uint16_t cluster, uint8_t endpoint, uint8_t category, uint32_t value, Z_DeviceTimer func);
|
||||||
|
|
|
@ -338,6 +338,13 @@ void Z_Devices::deviceWasReached(uint16_t shortaddr) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Z_Devices::deviceHasNoBattery(uint16_t shortaddr) {
|
||||||
|
Z_Device & device = findShortAddr(shortaddr);
|
||||||
|
if (device.valid()) {
|
||||||
|
device.setHasNoBattery(); // mark device as reachable
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// get the next sequance number for the device, or use the global seq number if device is unknown
|
// get the next sequance number for the device, or use the global seq number if device is unknown
|
||||||
uint8_t Z_Devices::getNextSeqNumber(uint16_t shortaddr) {
|
uint8_t Z_Devices::getNextSeqNumber(uint16_t shortaddr) {
|
||||||
Z_Device & device = findShortAddr(shortaddr);
|
Z_Device & device = findShortAddr(shortaddr);
|
||||||
|
|
|
@ -150,6 +150,7 @@ const Z_CommandConverter Z_Commands[] PROGMEM = {
|
||||||
#define ZLE(x) ((x) & 0xFF), ((x) >> 8) // Little Endian
|
#define ZLE(x) ((x) & 0xFF), ((x) >> 8) // Little Endian
|
||||||
|
|
||||||
// Below are the attributes we wand to read from each cluster
|
// Below are the attributes we wand to read from each cluster
|
||||||
|
const uint8_t CLUSTER_0001[] = { ZLE(0x0020), ZLE(0x0021) }; // BatteryVoltage, BatteryPercentage
|
||||||
const uint8_t CLUSTER_0006[] = { ZLE(0x0000) }; // Power
|
const uint8_t CLUSTER_0006[] = { ZLE(0x0000) }; // Power
|
||||||
const uint8_t CLUSTER_0008[] = { ZLE(0x0000) }; // CurrentLevel
|
const uint8_t CLUSTER_0008[] = { ZLE(0x0000) }; // CurrentLevel
|
||||||
const uint8_t CLUSTER_0009[] = { ZLE(0x0000) }; // AlarmCount
|
const uint8_t CLUSTER_0009[] = { ZLE(0x0000) }; // AlarmCount
|
||||||
|
@ -161,6 +162,10 @@ void Z_ReadAttrCallback(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster
|
||||||
const uint8_t* attrs = nullptr;
|
const uint8_t* attrs = nullptr;
|
||||||
|
|
||||||
switch (cluster) {
|
switch (cluster) {
|
||||||
|
case 0x0001:
|
||||||
|
attrs = CLUSTER_0001;
|
||||||
|
attrs_len = sizeof(CLUSTER_0001);
|
||||||
|
break;
|
||||||
case 0x0006: // for On/Off
|
case 0x0006: // for On/Off
|
||||||
attrs = CLUSTER_0006;
|
attrs = CLUSTER_0006;
|
||||||
attrs_len = sizeof(CLUSTER_0006);
|
attrs_len = sizeof(CLUSTER_0006);
|
||||||
|
|
|
@ -727,7 +727,8 @@ void Z_AutoBindDefer(uint16_t shortaddr, uint8_t endpoint, const SBuffer &buf,
|
||||||
for (uint32_t i=0; i<nitems(Z_bindings); i++) {
|
for (uint32_t i=0; i<nitems(Z_bindings); i++) {
|
||||||
if (bitRead(cluster_in_map, i)) {
|
if (bitRead(cluster_in_map, i)) {
|
||||||
uint16_t cluster = CxToCluster(pgm_read_byte(&Z_bindings[i]));
|
uint16_t cluster = CxToCluster(pgm_read_byte(&Z_bindings[i]));
|
||||||
if ((cluster == 0x0001) && (!Z_BatteryReportingDeviceSpecific(shortaddr))) { continue; }
|
// don't configure Battery reporting if `SetOption143 0` or if device is on the exception list
|
||||||
|
if ((cluster == 0x0001) && (!Settings->flag5.zigbee_no_batt_autoprobe) || !Z_BatteryReportingDeviceSpecific(shortaddr)) { continue; }
|
||||||
zigbee_devices.queueTimer(shortaddr, 0 /* groupaddr */, 2000, cluster, endpoint, Z_CAT_CONFIG_ATTR, 0 /* value */, &Z_AutoConfigReportingForCluster);
|
zigbee_devices.queueTimer(shortaddr, 0 /* groupaddr */, 2000, cluster, endpoint, Z_CAT_CONFIG_ATTR, 0 /* value */, &Z_AutoConfigReportingForCluster);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -951,11 +952,16 @@ int32_t Z_ReceiveEndDeviceAnnonce(int32_t res, const SBuffer &buf) {
|
||||||
// device is reachable
|
// device is reachable
|
||||||
zigbee_devices.deviceWasReached(nwkAddr);
|
zigbee_devices.deviceWasReached(nwkAddr);
|
||||||
|
|
||||||
|
bool power_source = (capabilities & 0x04);
|
||||||
|
if (power_source) {
|
||||||
|
zigbee_devices.deviceHasNoBattery(nwkAddr);
|
||||||
|
}
|
||||||
|
|
||||||
Response_P(PSTR("{\"" D_JSON_ZIGBEE_STATE "\":{"
|
Response_P(PSTR("{\"" D_JSON_ZIGBEE_STATE "\":{"
|
||||||
"\"Status\":%d,\"IEEEAddr\":\"0x%_X\",\"ShortAddr\":\"0x%04X\""
|
"\"Status\":%d,\"IEEEAddr\":\"0x%_X\",\"ShortAddr\":\"0x%04X\""
|
||||||
",\"PowerSource\":%s,\"ReceiveWhenIdle\":%s,\"Security\":%s}}"),
|
",\"PowerSource\":%s,\"ReceiveWhenIdle\":%s,\"Security\":%s}}"),
|
||||||
ZIGBEE_STATUS_DEVICE_ANNOUNCE, &ieeeAddr, nwkAddr,
|
ZIGBEE_STATUS_DEVICE_ANNOUNCE, &ieeeAddr, nwkAddr,
|
||||||
(capabilities & 0x04) ? PSTR("true") : PSTR("false"),
|
power_source ? PSTR("true") : PSTR("false"),
|
||||||
(capabilities & 0x08) ? PSTR("true") : PSTR("false"),
|
(capabilities & 0x08) ? PSTR("true") : PSTR("false"),
|
||||||
(capabilities & 0x40) ? PSTR("true") : PSTR("false")
|
(capabilities & 0x40) ? PSTR("true") : PSTR("false")
|
||||||
);
|
);
|
||||||
|
@ -1673,6 +1679,12 @@ void Z_IncomingMessage(class ZCLFrame &zcl_received) {
|
||||||
// Build the ZbReceive list
|
// Build the ZbReceive list
|
||||||
if ( (!zcl_received.isClusterSpecificCommand()) && (ZCL_REPORT_ATTRIBUTES == zcl_received.getCmdId() || ZCL_WRITE_ATTRIBUTES == zcl_received.getCmdId())) {
|
if ( (!zcl_received.isClusterSpecificCommand()) && (ZCL_REPORT_ATTRIBUTES == zcl_received.getCmdId() || ZCL_WRITE_ATTRIBUTES == zcl_received.getCmdId())) {
|
||||||
zcl_received.parseReportAttributes(attr_list); // Zigbee report attributes from sensors
|
zcl_received.parseReportAttributes(attr_list); // Zigbee report attributes from sensors
|
||||||
|
|
||||||
|
// since we receive a sensor value, and the device is still awake,
|
||||||
|
// try to read the battery value
|
||||||
|
if (clusterid != 0x0001) { // avoid sending Battery probe if we already received info from cluster 0x0001
|
||||||
|
Z_Query_Battery(srcaddr);
|
||||||
|
}
|
||||||
if (clusterid && (ZCL_REPORT_ATTRIBUTES == zcl_received.getCmdId())) { defer_attributes = true; } // don't defer system Cluster=0 messages or Write Attribute
|
if (clusterid && (ZCL_REPORT_ATTRIBUTES == zcl_received.getCmdId())) { defer_attributes = true; } // don't defer system Cluster=0 messages or Write Attribute
|
||||||
} else if ( (!zcl_received.isClusterSpecificCommand()) && (ZCL_READ_ATTRIBUTES_RESPONSE == zcl_received.getCmdId())) {
|
} else if ( (!zcl_received.isClusterSpecificCommand()) && (ZCL_READ_ATTRIBUTES_RESPONSE == zcl_received.getCmdId())) {
|
||||||
zcl_received.parseReadAttributesResponse(attr_list);
|
zcl_received.parseReadAttributesResponse(attr_list);
|
||||||
|
@ -2071,6 +2083,30 @@ void Z_Query_Bulb(uint16_t shortaddr, uint32_t &wait_ms) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Query the status of the battery (auto-probe)
|
||||||
|
//
|
||||||
|
void Z_Query_Battery(uint16_t shortaddr) {
|
||||||
|
if (Settings->flag5.zigbee_no_batt_autoprobe) { return; } // don't do auto-probe if `SetOption143 1`
|
||||||
|
if (0 == shortaddr) { return; }
|
||||||
|
Z_Device & device = zigbee_devices.findShortAddr(shortaddr);
|
||||||
|
if (device.valid()) {
|
||||||
|
uint32_t now = Rtc.utc_time;
|
||||||
|
if (now < START_VALID_TIME) { return; } // internal time is not valid
|
||||||
|
if (device.hasNoBattery()) { return; } // device is known to have no battery
|
||||||
|
if (device.batt_last_seen + USE_ZIGBEE_BATT_REPROBE > now) { return; } // battery status is fresh enough
|
||||||
|
if (device.batt_last_probed + USE_ZIGBEE_BATT_REPROBE_PAUSE > now) { return; } // battery has been probed soon enough
|
||||||
|
|
||||||
|
uint8_t endpoint = zigbee_devices.findFirstEndpoint(shortaddr);
|
||||||
|
if (endpoint) { // send only if we know the endpoint
|
||||||
|
device.batt_last_probed = now; // we are probing now
|
||||||
|
zigbee_devices.setTimer(shortaddr, 0 /* groupaddr */, 0 /* now */, 0x0001, endpoint, Z_CAT_READ_CLUSTER, 0 /* value */, &Z_ReadAttrCallback);
|
||||||
|
AddLog(LOG_LEVEL_INFO, PSTR("ZIG: Battery auto-probe "
|
||||||
|
"`ZbSend {\"Device\":\"0x%04X\",\"Read\":{\"BatteryPercentage\":true,\"BatteryVoltage\":true}}`"), shortaddr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// Send messages to query the state of each Hue emulated light
|
// Send messages to query the state of each Hue emulated light
|
||||||
//
|
//
|
||||||
|
|
|
@ -2024,7 +2024,7 @@ void ZigbeeShow(bool json)
|
||||||
name = sdevice;
|
name = sdevice;
|
||||||
}
|
}
|
||||||
|
|
||||||
char sbatt[64];
|
char sbatt[96];
|
||||||
char dhm[48];
|
char dhm[48];
|
||||||
snprintf_P(sbatt, sizeof(sbatt), PSTR(" "));
|
snprintf_P(sbatt, sizeof(sbatt), PSTR(" "));
|
||||||
if (device.validBatteryPercent()) {
|
if (device.validBatteryPercent()) {
|
||||||
|
|
Loading…
Reference in New Issue