Merge branch 'development' into release

This commit is contained in:
Theo Arends 2019-12-23 17:42:22 +01:00
commit bafd4cd90a
10 changed files with 189 additions and 57 deletions

View File

@ -67,7 +67,9 @@ See [wiki migration path](https://tasmota.github.io/docs/#/Upgrading?id=migratio
3. Migrate to **Sonoff-Tasmota 5.14** 3. Migrate to **Sonoff-Tasmota 5.14**
4. Migrate to **Sonoff-Tasmota 6.x** 4. Migrate to **Sonoff-Tasmota 6.x**
5. Migrate to **Tasmota 7.x** 5. Migrate to **Tasmota 7.x**
--- Major change in parameter storage layout --- --- Major change in parameter storage layout ---
6. Migrate to **Tasmota 8.1** 6. Migrate to **Tasmota 8.1**
7. Migrate to **Tasmota 8.x** 7. Migrate to **Tasmota 8.x**

View File

@ -11,7 +11,9 @@ See [migration path](https://tasmota.github.io/docs/#/Upgrading?id=migration-pat
3. Migrate to **Sonoff-Tasmota 5.14** 3. Migrate to **Sonoff-Tasmota 5.14**
4. Migrate to **Sonoff-Tasmota 6.x** 4. Migrate to **Sonoff-Tasmota 6.x**
5. Migrate to **Tasmota 7.x** 5. Migrate to **Tasmota 7.x**
--- Major change in parameter storage layout --- --- Major change in parameter storage layout ---
6. Migrate to **Tasmota 8.1** 6. Migrate to **Tasmota 8.1**
7. Migrate to **Tasmota 8.x** 7. Migrate to **Tasmota 8.x**
@ -56,4 +58,5 @@ The following binary downloads have been compiled with ESP8266/Arduino library c
- Add support for max 150 characters in most command parameter strings (#3686, #4754) - Add support for max 150 characters in most command parameter strings (#3686, #4754)
- Add support for GPS as NTP server by Christian Baars and Adrian Scillato - Add support for GPS as NTP server by Christian Baars and Adrian Scillato
- Add Zigbee coalesce sensor attributes into a single message - Add Zigbee coalesce sensor attributes into a single message
- Add Zigbee better support for Xiaomi Double Switch and Xiaomi Vibration sensor
- Add Deepsleep start delay based on Teleperiod if ``Teleperiod`` differs from 10 or 300 - Add Deepsleep start delay based on Teleperiod if ``Teleperiod`` differs from 10 or 300

View File

@ -9,6 +9,7 @@
### 8.0.0.2 20191223 ### 8.0.0.2 20191223
- Changed Settings variable namings - Changed Settings variable namings
- Add Zigbee better support for Xiaomi Double Switch and Xiaomi Vibration sensor
### 8.0.0.1 20191221 ### 8.0.0.1 20191221

View File

@ -468,6 +468,7 @@
#define D_CMND_ZIGBEE_STATUS "ZigbeeStatus" #define D_CMND_ZIGBEE_STATUS "ZigbeeStatus"
#define D_CMND_ZIGBEE_RESET "ZigbeeReset" #define D_CMND_ZIGBEE_RESET "ZigbeeReset"
#define D_JSON_ZIGBEE_CC2530 "CC2530" #define D_JSON_ZIGBEE_CC2530 "CC2530"
#define D_CMND_ZIGBEEZNPRECEIVE "ZigbeeZNPReceive" // only for debug
#define D_CMND_ZIGBEEZNPSEND "ZigbeeZNPSend" #define D_CMND_ZIGBEEZNPSEND "ZigbeeZNPSend"
#define D_JSON_ZIGBEE_STATE "ZigbeeState" #define D_JSON_ZIGBEE_STATE "ZigbeeState"
#define D_JSON_ZIGBEEZNPRECEIVED "ZigbeeZNPReceived" #define D_JSON_ZIGBEEZNPRECEIVED "ZigbeeZNPReceived"

View File

@ -460,7 +460,9 @@ void RtcSetTime(uint32_t epoch)
if (epoch < START_VALID_TIME) { // 2016-01-01 if (epoch < START_VALID_TIME) { // 2016-01-01
Rtc.user_time_entry = false; Rtc.user_time_entry = false;
ntp_force_sync = true; ntp_force_sync = true;
sntp_init();
} else { } else {
sntp_stop();
Rtc.user_time_entry = true; Rtc.user_time_entry = true;
Rtc.utc_time = epoch -1; // Will be corrected by RtcSecond Rtc.utc_time = epoch -1; // Will be corrected by RtcSecond
} }

View File

@ -91,6 +91,8 @@ public:
void jsonClear(uint16_t shortaddr); void jsonClear(uint16_t shortaddr);
void jsonAppend(uint16_t shortaddr, JsonObject &values); void jsonAppend(uint16_t shortaddr, JsonObject &values);
const JsonObject *jsonGet(uint16_t shortaddr); const JsonObject *jsonGet(uint16_t shortaddr);
const void jsonPublish(uint16_t shortaddr); // publish the json message and clear buffer
bool jsonIsConflict(uint16_t shortaddr, const JsonObject &values);
private: private:
std::vector<Z_Device> _devices = {}; std::vector<Z_Device> _devices = {};
@ -419,6 +421,67 @@ void Z_Devices::jsonClear(uint16_t shortaddr) {
device.json_buffer->clear(); device.json_buffer->clear();
} }
void CopyJsonVariant(JsonObject &to, const String &key, const JsonVariant &val) {
to.remove(key); // force remove to have metadata like LinkQuality at the end
if (val.is<char*>()) {
String sval = val.as<String>(); // force a copy of the String value
to.set(key, sval);
} else if (val.is<JsonArray>()) {
JsonArray &nested_arr = to.createNestedArray(key);
CopyJsonArray(nested_arr, val.as<JsonArray>());
} else if (val.is<JsonObject>()) {
JsonObject &nested_obj = to.createNestedObject(key);
CopyJsonObject(nested_obj, val.as<JsonObject>());
} else {
to.set(key, val);
}
}
void CopyJsonArray(JsonArray &to, const JsonArray &arr) {
for (auto v : arr) {
if (v.is<char*>()) {
String sval = v.as<String>(); // force a copy of the String value
to.add(sval);
} else if (v.is<JsonArray>()) {
} else if (v.is<JsonObject>()) {
} else {
to.add(v);
}
}
}
void CopyJsonObject(JsonObject &to, const JsonObject &from) {
for (auto kv : from) {
String key_string = kv.key;
JsonVariant &val = kv.value;
CopyJsonVariant(to, key_string, val);
}
}
// does the new payload conflicts with the existing payload, i.e. values would be overwritten
bool Z_Devices::jsonIsConflict(uint16_t shortaddr, const JsonObject &values) {
Z_Device & device = getShortAddr(shortaddr);
if (&device == nullptr) { return false; } // don't crash if not found
if (&values == nullptr) { return false; }
if (nullptr == device.json) {
return false; // if no previous value, no conflict
}
for (auto kv : values) {
String key_string = kv.key;
if (strcasecmp_P(kv.key, PSTR(D_CMND_ZIGBEE_LINKQUALITY))) { // exception = ignore duplicates for LinkQuality
if (device.json->containsKey(kv.key)) {
return true; // conflict!
}
}
}
return false;
}
void Z_Devices::jsonAppend(uint16_t shortaddr, JsonObject &values) { void Z_Devices::jsonAppend(uint16_t shortaddr, JsonObject &values) {
Z_Device & device = getShortAddr(shortaddr); Z_Device & device = getShortAddr(shortaddr);
if (&device == nullptr) { return; } // don't crash if not found if (&device == nullptr) { return; } // don't crash if not found
@ -428,24 +491,7 @@ void Z_Devices::jsonAppend(uint16_t shortaddr, JsonObject &values) {
device.json = &(device.json_buffer->createObject()); device.json = &(device.json_buffer->createObject());
} }
// copy all values from 'values' to 'json' // copy all values from 'values' to 'json'
for (auto kv : values) { CopyJsonObject(*device.json, values);
String key_string = kv.key;
const char * key = key_string.c_str();
JsonVariant &val = kv.value;
device.json->remove(key_string); // force remove to have metadata like LinkQuality at the end
if (val.is<char*>()) {
String sval = val.as<String>(); // force a copy of the String value
device.json->set(key_string, sval);
} else if (val.is<JsonArray>()) {
// todo
} else if (val.is<JsonObject>()) {
// todo
} else {
device.json->set(key_string, kv.value);
}
}
} }
const JsonObject *Z_Devices::jsonGet(uint16_t shortaddr) { const JsonObject *Z_Devices::jsonGet(uint16_t shortaddr) {
@ -454,6 +500,19 @@ const JsonObject *Z_Devices::jsonGet(uint16_t shortaddr) {
return device.json; return device.json;
} }
const void Z_Devices::jsonPublish(uint16_t shortaddr) {
const JsonObject *json = zigbee_devices.jsonGet(shortaddr);
if (json == nullptr) { return; } // don't crash if not found
String msg = "";
json->printTo(msg);
zigbee_devices.jsonClear(shortaddr);
Response_P(PSTR("{\"" D_CMND_ZIGBEE_RECEIVED "\":{\"0x%04X\":%s}}"), shortaddr, msg.c_str());
MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR));
XdrvRulesProcess();
}
// Dump the internal memory of Zigbee devices // Dump the internal memory of Zigbee devices
// Mode = 1: simple dump of devices addresses and names // Mode = 1: simple dump of devices addresses and names
// Mode = 2: Mode 1 + also dump the endpoints, profiles and clusters // Mode = 2: Mode 1 + also dump the endpoints, profiles and clusters

View File

@ -100,6 +100,7 @@ public:
return _frame_control.b.frame_type & 1; return _frame_control.b.frame_type & 1;
} }
static void generateAttributeName(const JsonObject& json, uint16_t cluster, uint16_t attr, char *key, size_t key_len);
void parseRawAttributes(JsonObject& json, uint8_t offset = 0); void parseRawAttributes(JsonObject& json, uint8_t offset = 0);
void parseReadAttributes(JsonObject& json, uint8_t offset = 0); void parseReadAttributes(JsonObject& json, uint8_t offset = 0);
void parseClusterSpecificCommand(JsonObject& json, uint8_t offset = 0); void parseClusterSpecificCommand(JsonObject& json, uint8_t offset = 0);
@ -290,17 +291,20 @@ uint32_t parseSingleAttribute(JsonObject& json, char *attrid_str, class SBuffer
bool parse_as_string = true; bool parse_as_string = true;
uint32_t len = (attrtype <= 0x42) ? buf.get8(i) : buf.get16(i); // len is 8 or 16 bits uint32_t len = (attrtype <= 0x42) ? buf.get8(i) : buf.get16(i); // len is 8 or 16 bits
i += (attrtype <= 0x42) ? 1 : 2; // increment pointer i += (attrtype <= 0x42) ? 1 : 2; // increment pointer
if (i + len > buf.len()) { // make sure we don't get past the buffer
len = buf.len() - i;
}
// check if we can safely use a string // check if we can safely use a string
if ((0x41 == attrtype) || (0x43 == attrtype)) { parse_as_string = false; } if ((0x41 == attrtype) || (0x43 == attrtype)) { parse_as_string = false; }
else { // else {
for (uint32_t j = 0; j < len; j++) { // for (uint32_t j = 0; j < len; j++) {
if (0x00 == buf.get8(i+j)) { // if (0x00 == buf.get8(i+j)) {
parse_as_string = false; // parse_as_string = false;
break; // break;
} // }
} // }
} // }
if (parse_as_string) { if (parse_as_string) {
char str[len+1]; char str[len+1];
@ -409,19 +413,28 @@ uint32_t parseSingleAttribute(JsonObject& json, char *attrid_str, class SBuffer
return i - offset; // how much have we increased the index return i - offset; // how much have we increased the index
} }
// Generate an attribute name based on cluster number, attribute, and suffix if duplicates
void ZCLFrame::generateAttributeName(const JsonObject& json, uint16_t cluster, uint16_t attr, char *key, size_t key_len) {
uint32_t suffix = 1;
snprintf_P(key, key_len, PSTR("%04X/%04X"), cluster, attr);
while (json.containsKey(key)) {
suffix++;
snprintf_P(key, key_len, PSTR("%04X/%04X+%d"), cluster, attr, suffix); // add "0008/0001+2" suffix if duplicate
}
}
// First pass, parse all attributes in their native format // First pass, parse all attributes in their native format
void ZCLFrame::parseRawAttributes(JsonObject& json, uint8_t offset) { void ZCLFrame::parseRawAttributes(JsonObject& json, uint8_t offset) {
uint32_t i = offset; uint32_t i = offset;
uint32_t len = _payload.len(); uint32_t len = _payload.len();
while (len - i >= 3) { while (len >= i + 3) {
uint16_t attrid = _payload.get16(i); uint16_t attrid = _payload.get16(i);
i += 2; i += 2;
char key[16]; char key[16];
snprintf_P(key, sizeof(key), PSTR("%04X/%04X"), generateAttributeName(json, _cluster_id, attrid, key, sizeof(key));
_cluster_id, attrid);
// exception for Xiaomi lumi.weather - specific field to be treated as octet and not char // exception for Xiaomi lumi.weather - specific field to be treated as octet and not char
if ((0x0000 == _cluster_id) && (0xFF01 == attrid)) { if ((0x0000 == _cluster_id) && (0xFF01 == attrid)) {
@ -445,8 +458,7 @@ void ZCLFrame::parseReadAttributes(JsonObject& json, uint8_t offset) {
if (0 == status) { if (0 == status) {
char key[16]; char key[16];
snprintf_P(key, sizeof(key), PSTR("%04X/%04X"), generateAttributeName(json, _cluster_id, attrid, key, sizeof(key));
_cluster_id, attrid);
i += parseSingleAttribute(json, key, _payload, i, len); i += parseSingleAttribute(json, key, _payload, i, len);
} }
@ -472,7 +484,7 @@ void ZCLFrame::parseClusterSpecificCommand(JsonObject& json, uint8_t offset) {
// return value: // return value:
// 0 = keep initial value // 0 = keep initial value
// 1 = remove initial value // 1 = remove initial value
typedef int32_t (*Z_AttrConverter)(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const __FlashStringHelper* new_name, uint16_t cluster, uint16_t attr); typedef int32_t (*Z_AttrConverter)(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const String &new_name, uint16_t cluster, uint16_t attr);
typedef struct Z_AttributeConverter { typedef struct Z_AttributeConverter {
uint16_t cluster; uint16_t cluster;
uint16_t attribute; uint16_t attribute;
@ -775,13 +787,13 @@ const Z_AttributeConverter Z_PostProcess[] PROGMEM = {
// ====================================================================== // ======================================================================
// Record Manuf // Record Manuf
int32_t Z_ManufKeep(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const __FlashStringHelper *new_name, uint16_t cluster, uint16_t attr) { int32_t Z_ManufKeep(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const String &new_name, uint16_t cluster, uint16_t attr) {
json[new_name] = value; json[new_name] = value;
zigbee_devices.setManufId(shortaddr, value.as<const char*>()); zigbee_devices.setManufId(shortaddr, value.as<const char*>());
return 1; return 1;
} }
// //
int32_t Z_ModelKeep(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const __FlashStringHelper *new_name, uint16_t cluster, uint16_t attr) { int32_t Z_ModelKeep(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const String &new_name, uint16_t cluster, uint16_t attr) {
json[new_name] = value; json[new_name] = value;
zigbee_devices.setModelId(shortaddr, value.as<const char*>()); zigbee_devices.setModelId(shortaddr, value.as<const char*>());
return 1; return 1;
@ -789,29 +801,29 @@ int32_t Z_ModelKeep(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& j
// ====================================================================== // ======================================================================
// Remove attribute // Remove attribute
int32_t Z_Remove(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const __FlashStringHelper *new_name, uint16_t cluster, uint16_t attr) { int32_t Z_Remove(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const String &new_name, uint16_t cluster, uint16_t attr) {
return 1; // remove original key return 1; // remove original key
} }
// Copy value as-is // Copy value as-is
int32_t Z_Copy(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const __FlashStringHelper *new_name, uint16_t cluster, uint16_t attr) { int32_t Z_Copy(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const String &new_name, uint16_t cluster, uint16_t attr) {
json[new_name] = value; json[new_name] = value;
return 1; // remove original key return 1; // remove original key
} }
// Add pressure unit // Add pressure unit
int32_t Z_AddPressureUnit(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const __FlashStringHelper *new_name, uint16_t cluster, uint16_t attr) { int32_t Z_AddPressureUnit(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const String &new_name, uint16_t cluster, uint16_t attr) {
json[new_name] = F(D_UNIT_PRESSURE); json[new_name] = F(D_UNIT_PRESSURE);
return 0; // keep original key return 0; // keep original key
} }
// Convert int to float and divide by 100 // Convert int to float and divide by 100
int32_t Z_FloatDiv100(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const __FlashStringHelper *new_name, uint16_t cluster, uint16_t attr) { int32_t Z_FloatDiv100(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const String &new_name, uint16_t cluster, uint16_t attr) {
json[new_name] = ((float)value) / 100.0f; json[new_name] = ((float)value) / 100.0f;
return 1; // remove original key return 1; // remove original key
} }
// Convert int to float and divide by 10 // Convert int to float and divide by 10
int32_t Z_FloatDiv10(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const __FlashStringHelper *new_name, uint16_t cluster, uint16_t attr) { int32_t Z_FloatDiv10(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const String &new_name, uint16_t cluster, uint16_t attr) {
json[new_name] = ((float)value) / 10.0f; json[new_name] = ((float)value) / 10.0f;
return 1; // remove original key return 1; // remove original key
} }
@ -825,7 +837,7 @@ int32_t Z_OccupancyCallback(uint16_t shortaddr, uint16_t cluster, uint16_t endpo
} }
// Aqara Vibration Sensor - special proprietary attributes // Aqara Vibration Sensor - special proprietary attributes
int32_t Z_AqaraVibration(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const __FlashStringHelper *new_name, uint16_t cluster, uint16_t attr) { int32_t Z_AqaraVibration(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const String &new_name, uint16_t cluster, uint16_t attr) {
//json[new_name] = value; //json[new_name] = value;
switch (attr) { switch (attr) {
case 0x0055: case 0x0055:
@ -879,7 +891,7 @@ int32_t Z_AqaraVibration(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObje
return 1; // remove original key return 1; // remove original key
} }
int32_t Z_AqaraSensor(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const __FlashStringHelper *new_name, uint16_t cluster, uint16_t attr) { int32_t Z_AqaraSensor(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const String &new_name, uint16_t cluster, uint16_t attr) {
String hex = value; String hex = value;
SBuffer buf2 = SBuffer::SBufferFromHex(hex.c_str(), hex.length()); SBuffer buf2 = SBuffer::SBufferFromHex(hex.c_str(), hex.length());
uint32_t i = 0; uint32_t i = 0;
@ -925,11 +937,19 @@ void ZCLFrame::postProcessAttributes(uint16_t shortaddr, JsonObject& json) {
String key_string = kv.key; String key_string = kv.key;
const char * key = key_string.c_str(); const char * key = key_string.c_str();
JsonVariant& value = kv.value; JsonVariant& value = kv.value;
// Check that format looks like "CCCC/AAAA" // Check that format looks like "CCCC/AAAA" or "CCCC/AAAA+d"
char * delimiter = strchr(key, '/'); char * delimiter = strchr(key, '/');
char * delimiter2 = strchr(key, '+');
if (delimiter) { if (delimiter) {
uint16_t attribute;
uint16_t suffix = 1;
uint16_t cluster = strtoul(key, &delimiter, 16); uint16_t cluster = strtoul(key, &delimiter, 16);
uint16_t attribute = strtoul(delimiter+1, nullptr, 16); if (!delimiter2) {
attribute = strtoul(delimiter+1, nullptr, 16);
} else {
attribute = strtoul(delimiter+1, &delimiter2, 16);
suffix = strtoul(delimiter2+1, nullptr, 10);
}
// Iterate on filter // Iterate on filter
for (uint32_t i = 0; i < sizeof(Z_PostProcess) / sizeof(Z_PostProcess[0]); i++) { for (uint32_t i = 0; i < sizeof(Z_PostProcess) / sizeof(Z_PostProcess[0]); i++) {
@ -939,7 +959,9 @@ void ZCLFrame::postProcessAttributes(uint16_t shortaddr, JsonObject& json) {
if ((conv_cluster == cluster) && if ((conv_cluster == cluster) &&
((conv_attribute == attribute) || (conv_attribute == 0xFFFF)) ) { ((conv_attribute == attribute) || (conv_attribute == 0xFFFF)) ) {
int32_t drop = (*converter->func)(this, shortaddr, json, key, value, (const __FlashStringHelper*) converter->name, conv_cluster, conv_attribute); String new_name_str = converter->name;
if (suffix > 1) { new_name_str += suffix; } // append suffix number
int32_t drop = (*converter->func)(this, shortaddr, json, key, value, new_name_str, conv_cluster, conv_attribute);
if (drop) { if (drop) {
json.remove(key); json.remove(key);
} }

View File

@ -32,6 +32,7 @@ const uint8_t ZIGBEE_STATUS_DEVICE_ANNOUNCE = 30; // Device announces its
const uint8_t ZIGBEE_STATUS_NODE_DESC = 31; // Node descriptor const uint8_t ZIGBEE_STATUS_NODE_DESC = 31; // Node descriptor
const uint8_t ZIGBEE_STATUS_ACTIVE_EP = 32; // Endpoints descriptor const uint8_t ZIGBEE_STATUS_ACTIVE_EP = 32; // Endpoints descriptor
const uint8_t ZIGBEE_STATUS_SIMPLE_DESC = 33; // Simple Descriptor (clusters) const uint8_t ZIGBEE_STATUS_SIMPLE_DESC = 33; // Simple Descriptor (clusters)
const uint8_t ZIGBEE_STATUS_DEVICE_INDICATION = 34; // Device announces its address
const uint8_t ZIGBEE_STATUS_CC_VERSION = 50; // Status: CC2530 ZNP Version const uint8_t ZIGBEE_STATUS_CC_VERSION = 50; // Status: CC2530 ZNP Version
const uint8_t ZIGBEE_STATUS_CC_INFO = 51; // Status: CC2530 Device Configuration const uint8_t ZIGBEE_STATUS_CC_INFO = 51; // Status: CC2530 Device Configuration
const uint8_t ZIGBEE_STATUS_UNSUPPORTED_VERSION = 98; // Unsupported ZNP version const uint8_t ZIGBEE_STATUS_UNSUPPORTED_VERSION = 98; // Unsupported ZNP version

View File

@ -357,6 +357,28 @@ int32_t Z_ReceiveEndDeviceAnnonce(int32_t res, const class SBuffer &buf) {
return -1; return -1;
} }
// 45CA
int32_t Z_ReceiveTCDevInd(int32_t res, const class SBuffer &buf) {
Z_ShortAddress srcAddr = buf.get16(2);
Z_IEEEAddress ieeeAddr = buf.get64(4);
Z_ShortAddress parentNw = buf.get16(12);
zigbee_devices.updateDevice(srcAddr, ieeeAddr);
char hex[20];
Uint64toHex(ieeeAddr, hex, 64);
Response_P(PSTR("{\"" D_JSON_ZIGBEE_STATE "\":{"
"\"Status\":%d,\"IEEEAddr\":\"%s\",\"ShortAddr\":\"0x%04X\""
",\"ParentNetwork\":\"0x%04X\"}}"),
ZIGBEE_STATUS_DEVICE_INDICATION, hex, srcAddr, parentNw
);
MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEEZCL_RECEIVED));
XdrvRulesProcess();
//Z_SendActiveEpReq(srcAddr);
return -1;
}
// Aqara Occupancy behavior: the Aqara device only sends Occupancy: true events every 60 seconds. // Aqara Occupancy behavior: the Aqara device only sends Occupancy: true events every 60 seconds.
// Here we add a timer so if we don't receive a Occupancy event for 90 seconds, we send Occupancy:false // Here we add a timer so if we don't receive a Occupancy event for 90 seconds, we send Occupancy:false
const uint32_t OCCUPANCY_TIMEOUT = 90 * 1000; // 90 s const uint32_t OCCUPANCY_TIMEOUT = 90 * 1000; // 90 s
@ -378,16 +400,11 @@ void Z_AqaraOccupancy(uint16_t shortaddr, uint16_t cluster, uint16_t endpoint, c
int32_t Z_PublishAttributes(uint16_t shortaddr, uint16_t cluster, uint16_t endpoint, uint32_t value) { int32_t Z_PublishAttributes(uint16_t shortaddr, uint16_t cluster, uint16_t endpoint, uint32_t value) {
const JsonObject *json = zigbee_devices.jsonGet(shortaddr); const JsonObject *json = zigbee_devices.jsonGet(shortaddr);
if (json == nullptr) { return 0; } // don't crash if not found if (json == nullptr) { return 0; } // don't crash if not found
// Post-provess for Aqara Presence Senson // Post-provess for Aqara Presence Senson
Z_AqaraOccupancy(shortaddr, cluster, endpoint, json); Z_AqaraOccupancy(shortaddr, cluster, endpoint, json);
String msg = ""; zigbee_devices.jsonPublish(shortaddr);
json->printTo(msg); return 1;
zigbee_devices.jsonClear(shortaddr);
Response_P(PSTR("{\"" D_CMND_ZIGBEE_RECEIVED "\":{\"0x%04X\":%s}}"), shortaddr, msg.c_str());
MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR));
XdrvRulesProcess();
} }
int32_t Z_ReceiveAfIncomingMessage(int32_t res, const class SBuffer &buf) { int32_t Z_ReceiveAfIncomingMessage(int32_t res, const class SBuffer &buf) {
@ -438,8 +455,13 @@ int32_t Z_ReceiveAfIncomingMessage(int32_t res, const class SBuffer &buf) {
if (defer_attributes) { if (defer_attributes) {
// Prepare for publish // Prepare for publish
zigbee_devices.jsonAppend(srcaddr, json); if (zigbee_devices.jsonIsConflict(srcaddr, json)) {
zigbee_devices.setTimer(srcaddr, USE_ZIGBEE_COALESCE_ATTR_TIMER, clusterid, srcendpoint, 0, &Z_PublishAttributes); // there is conflicting values, force a publish of the previous message now and don't coalesce
zigbee_devices.jsonPublish(srcaddr);
} else {
zigbee_devices.jsonAppend(srcaddr, json);
zigbee_devices.setTimer(srcaddr, USE_ZIGBEE_COALESCE_ATTR_TIMER, clusterid, srcendpoint, 0, &Z_PublishAttributes);
}
} else { } else {
// Publish immediately // Publish immediately
msg = ""; msg = "";
@ -459,6 +481,7 @@ typedef struct Z_Dispatcher {
// Filters for ZCL frames // Filters for ZCL frames
ZBM(AREQ_AF_INCOMING_MESSAGE, Z_AREQ | Z_AF, AF_INCOMING_MSG) // 4481 ZBM(AREQ_AF_INCOMING_MESSAGE, Z_AREQ | Z_AF, AF_INCOMING_MSG) // 4481
ZBM(AREQ_END_DEVICE_ANNCE_IND, Z_AREQ | Z_ZDO, ZDO_END_DEVICE_ANNCE_IND) // 45C1 ZBM(AREQ_END_DEVICE_ANNCE_IND, Z_AREQ | Z_ZDO, ZDO_END_DEVICE_ANNCE_IND) // 45C1
ZBM(AREQ_END_DEVICE_TC_DEV_IND, Z_AREQ | Z_ZDO, ZDO_TC_DEV_IND) // 45CA
ZBM(AREQ_PERMITJOIN_OPEN_XX, Z_AREQ | Z_ZDO, ZDO_PERMIT_JOIN_IND ) // 45CB ZBM(AREQ_PERMITJOIN_OPEN_XX, Z_AREQ | Z_ZDO, ZDO_PERMIT_JOIN_IND ) // 45CB
ZBM(AREQ_ZDO_ACTIVEEPRSP, Z_AREQ | Z_ZDO, ZDO_ACTIVE_EP_RSP) // 4585 ZBM(AREQ_ZDO_ACTIVEEPRSP, Z_AREQ | Z_ZDO, ZDO_ACTIVE_EP_RSP) // 4585
ZBM(AREQ_ZDO_SIMPLEDESCRSP, Z_AREQ | Z_ZDO, ZDO_SIMPLE_DESC_RSP) // 4584 ZBM(AREQ_ZDO_SIMPLEDESCRSP, Z_AREQ | Z_ZDO, ZDO_SIMPLE_DESC_RSP) // 4584
@ -466,6 +489,7 @@ ZBM(AREQ_ZDO_SIMPLEDESCRSP, Z_AREQ | Z_ZDO, ZDO_SIMPLE_DESC_RSP) // 4584
const Z_Dispatcher Z_DispatchTable[] PROGMEM = { const Z_Dispatcher Z_DispatchTable[] PROGMEM = {
{ AREQ_AF_INCOMING_MESSAGE, &Z_ReceiveAfIncomingMessage }, { AREQ_AF_INCOMING_MESSAGE, &Z_ReceiveAfIncomingMessage },
{ AREQ_END_DEVICE_ANNCE_IND, &Z_ReceiveEndDeviceAnnonce }, { AREQ_END_DEVICE_ANNCE_IND, &Z_ReceiveEndDeviceAnnonce },
{ AREQ_END_DEVICE_TC_DEV_IND, &Z_ReceiveTCDevInd },
{ AREQ_PERMITJOIN_OPEN_XX, &Z_ReceivePermitJoinStatus }, { AREQ_PERMITJOIN_OPEN_XX, &Z_ReceivePermitJoinStatus },
{ AREQ_ZDO_NODEDESCRSP, &Z_ReceiveNodeDesc }, { AREQ_ZDO_NODEDESCRSP, &Z_ReceiveNodeDesc },
{ AREQ_ZDO_ACTIVEEPRSP, &Z_ReceiveActiveEp }, { AREQ_ZDO_ACTIVEEPRSP, &Z_ReceiveActiveEp },

View File

@ -31,12 +31,14 @@ TasmotaSerial *ZigbeeSerial = nullptr;
const char kZigbeeCommands[] PROGMEM = "|" const char kZigbeeCommands[] PROGMEM = "|"
D_CMND_ZIGBEEZNPSEND "|" D_CMND_ZIGBEE_PERMITJOIN "|" D_CMND_ZIGBEEZNPSEND "|" D_CMND_ZIGBEE_PERMITJOIN "|"
D_CMND_ZIGBEE_STATUS "|" D_CMND_ZIGBEE_RESET "|" D_CMND_ZIGBEE_SEND "|" D_CMND_ZIGBEE_STATUS "|" D_CMND_ZIGBEE_RESET "|" D_CMND_ZIGBEE_SEND "|"
D_CMND_ZIGBEE_PROBE "|" D_CMND_ZIGBEE_READ ; D_CMND_ZIGBEE_PROBE "|" D_CMND_ZIGBEE_READ "|" D_CMND_ZIGBEEZNPRECEIVE
;
void (* const ZigbeeCommand[])(void) PROGMEM = { void (* const ZigbeeCommand[])(void) PROGMEM = {
&CmndZigbeeZNPSend, &CmndZigbeePermitJoin, &CmndZigbeeZNPSend, &CmndZigbeePermitJoin,
&CmndZigbeeStatus, &CmndZigbeeReset, &CmndZigbeeSend, &CmndZigbeeStatus, &CmndZigbeeReset, &CmndZigbeeSend,
&CmndZigbeeProbe, &CmndZigbeeRead }; &CmndZigbeeProbe, &CmndZigbeeRead, &CmndZigbeeZNPReceive
};
int32_t ZigbeeProcessInput(class SBuffer &buf) { int32_t ZigbeeProcessInput(class SBuffer &buf) {
if (!zigbee.state_machine) { return -1; } // if state machine is stopped, send 'ignore' message if (!zigbee.state_machine) { return -1; } // if state machine is stopped, send 'ignore' message
@ -268,7 +270,7 @@ void CmndZigbeeStatus(void) {
} }
} }
void CmndZigbeeZNPSend(void) void CmndZigbeeZNPSendOrReceive(bool send)
{ {
if (ZigbeeSerial && (XdrvMailbox.data_len > 0)) { if (ZigbeeSerial && (XdrvMailbox.data_len > 0)) {
uint8_t code; uint8_t code;
@ -286,11 +288,26 @@ void CmndZigbeeZNPSend(void)
size -= 2; size -= 2;
codes += 2; codes += 2;
} }
ZigbeeZNPSend(buf.getBuffer(), buf.len()); if (send) {
ZigbeeZNPSend(buf.getBuffer(), buf.len());
} else {
ZigbeeProcessInput(buf);
}
} }
ResponseCmndDone(); ResponseCmndDone();
} }
// For debug purposes only, simulates a message received
void CmndZigbeeZNPReceive(void)
{
CmndZigbeeZNPSendOrReceive(false);
}
void CmndZigbeeZNPSend(void)
{
CmndZigbeeZNPSendOrReceive(true);
}
void ZigbeeZNPSend(const uint8_t *msg, size_t len) { void ZigbeeZNPSend(const uint8_t *msg, size_t len) {
if ((len < 2) || (len > 252)) { if ((len < 2) || (len > 252)) {
// abort, message cannot be less than 2 bytes for CMD1 and CMD2 // abort, message cannot be less than 2 bytes for CMD1 and CMD2