mirror of https://github.com/arendst/Tasmota.git
Merge pull request #7832 from s-hadinger/zigbee_32
Add Zigbee features and improvements
This commit is contained in:
commit
46fe19bcd7
|
@ -5,6 +5,7 @@
|
|||
- Change default my_user_config.h driver and sensor support removing most sensors and adding most drivers
|
||||
- Add support for Jarolift rollers by Keeloq algorithm
|
||||
- Change IRremoteESP8266 library updated to v2.7.4
|
||||
- Add Zigbee features and improvements
|
||||
|
||||
### 8.1.0.9 20200220
|
||||
|
||||
|
|
|
@ -470,10 +470,10 @@
|
|||
|
||||
// Commands xdrv_23_zigbee.ino
|
||||
#define D_PRFX_ZB "Zb"
|
||||
#define D_PRFX_ZIGBEE "Zigbee"
|
||||
#define D_ZIGBEE_NOT_STARTED "Zigbee not started (yet)"
|
||||
#define D_CMND_ZIGBEE_PERMITJOIN "PermitJoin"
|
||||
#define D_CMND_ZIGBEE_STATUS "Status"
|
||||
#define D_JSON_ZIGBEE_Status "Status"
|
||||
#define D_CMND_ZIGBEE_RESET "Reset"
|
||||
#define D_JSON_ZIGBEE_CC2530 "CC2530"
|
||||
#define D_CMND_ZIGBEEZNPRECEIVE "ZNPReceive" // only for debug
|
||||
|
@ -486,19 +486,27 @@
|
|||
#define D_JSON_ZIGBEE_DEVICE "Device"
|
||||
#define D_JSON_ZIGBEE_NAME "Name"
|
||||
#define D_CMND_ZIGBEE_NAME "Name"
|
||||
#define D_CMND_ZIGBEE_MODELID "ModelId"
|
||||
#define D_JSON_ZIGBEE_MODELID "ModelId"
|
||||
#define D_CMND_ZIGBEE_PROBE "Probe"
|
||||
#define D_CMND_ZIGBEE_FORGET "Forget"
|
||||
#define D_CMND_ZIGBEE_SAVE "Save"
|
||||
#define D_CMND_ZIGBEE_LINKQUALITY "LinkQuality"
|
||||
#define D_CMND_ZIGBEE_ENDPOINT "Endpoint"
|
||||
#define D_CMND_ZIGBEE_GROUP "Group"
|
||||
#define D_CMND_ZIGBEE_READ "Read"
|
||||
#define D_CMND_ZIGBEE_SEND "Send"
|
||||
#define D_JSON_ZIGBEE_ZCL_SENT "ZbZCLSent"
|
||||
#define D_JSON_ZIGBEE_RECEIVED "ZbReceived"
|
||||
#define D_JSON_ZIGBEE_RECEIVED_LEGACY "ZigbeeReceived"
|
||||
#define D_CMND_ZIGBEE_BIND "Bind"
|
||||
#define D_JSON_ZIGBEE_BIND "ZbBind"
|
||||
#define D_CMND_ZIGBEE_PING "Ping"
|
||||
#define D_JSON_ZIGBEE_PING "ZbPing"
|
||||
#define D_JSON_ZIGBEE_IEEE "IEEEAddr"
|
||||
#define D_JSON_ZIGBEE_RESPONSE "ZbResponse"
|
||||
#define D_JSON_ZIGBEE_CMD "Command"
|
||||
#define D_JSON_ZIGBEE_STATUS "Status"
|
||||
#define D_JSON_ZIGBEE_STATUS_MSG "StatusMessage"
|
||||
|
||||
// Commands xdrv_25_A4988_Stepper.ino
|
||||
#define D_CMND_MOTOR "MOTOR"
|
||||
|
|
|
@ -169,43 +169,6 @@ enum Z_configuration {
|
|||
ZNP_HAS_CONFIGURED = 0xF00
|
||||
};
|
||||
|
||||
// enum Z_nvItemIds {
|
||||
// SCENE_TABLE = 145,
|
||||
// MIN_FREE_NWK_ADDR = 146,
|
||||
// MAX_FREE_NWK_ADDR = 147,
|
||||
// MIN_FREE_GRP_ID = 148,
|
||||
// MAX_FREE_GRP_ID = 149,
|
||||
// MIN_GRP_IDS = 150,
|
||||
// MAX_GRP_IDS = 151,
|
||||
// OTA_BLOCK_REQ_DELAY = 152,
|
||||
// SAPI_ENDPOINT = 161,
|
||||
// SAS_SHORT_ADDR = 177,
|
||||
// SAS_EXT_PANID = 178,
|
||||
// SAS_PANID = 179,
|
||||
// SAS_CHANNEL_MASK = 180,
|
||||
// SAS_PROTOCOL_VER = 181,
|
||||
// SAS_STACK_PROFILE = 182,
|
||||
// SAS_STARTUP_CTRL = 183,
|
||||
// SAS_TC_ADDR = 193,
|
||||
// SAS_TC_MASTER_KEY = 194,
|
||||
// SAS_NWK_KEY = 195,
|
||||
// SAS_USE_INSEC_JOIN = 196,
|
||||
// SAS_PRECFG_LINK_KEY = 197,
|
||||
// SAS_NWK_KEY_SEQ_NUM = 198,
|
||||
// SAS_NWK_KEY_TYPE = 199,
|
||||
// SAS_NWK_MGR_ADDR = 200,
|
||||
// SAS_CURR_TC_MASTER_KEY = 209,
|
||||
// SAS_CURR_NWK_KEY = 210,
|
||||
// SAS_CURR_PRECFG_LINK_KEY = 211,
|
||||
// TCLK_TABLE_START = 257,
|
||||
// TCLK_TABLE_END = 511,
|
||||
// APS_LINK_KEY_DATA_START = 513,
|
||||
// APS_LINK_KEY_DATA_END = 767,
|
||||
// DUPLICATE_BINDING_TABLE = 768,
|
||||
// DUPLICATE_DEVICE_LIST = 769,
|
||||
// DUPLICATE_DEVICE_LIST_KA_TIMEOUT = 770,
|
||||
//};
|
||||
|
||||
//
|
||||
enum Z_Status {
|
||||
Z_Success = 0x00,
|
||||
|
@ -261,16 +224,14 @@ enum Z_Device_Ids {
|
|||
// 0x0403 IAS Warning Device
|
||||
};
|
||||
|
||||
// enum class AddrMode : uint8_t {
|
||||
// NotPresent = 0,
|
||||
// Group = 1,
|
||||
// ShortAddress = 2,
|
||||
// IEEEAddress = 3,
|
||||
// Broadcast = 0xFF
|
||||
// };
|
||||
//
|
||||
//
|
||||
//
|
||||
enum Z_AddrMode : uint8_t {
|
||||
Z_Addr_NotPresent = 0,
|
||||
Z_Addr_Group = 1,
|
||||
Z_Addr_ShortAddress = 2,
|
||||
Z_Addr_IEEEAddress = 3,
|
||||
Z_Addr_Broadcast = 0xFF
|
||||
};
|
||||
|
||||
// Commands in the AF subsystem
|
||||
enum AfCommand : uint8_t {
|
||||
AF_REGISTER = 0x00,
|
||||
|
@ -286,7 +247,7 @@ enum AfCommand : uint8_t {
|
|||
AF_INCOMING_MSG = 0x81,
|
||||
AF_INCOMING_MSG_EXT = 0x82
|
||||
};
|
||||
//
|
||||
|
||||
// Commands in the ZDO subsystem
|
||||
enum : uint8_t {
|
||||
ZDO_NWK_ADDR_REQ = 0x00,
|
||||
|
@ -371,7 +332,7 @@ enum ZdoStates {
|
|||
ZDO_DEV_ZB_COORD = 0x09, // Started as a a Zigbee Coordinator
|
||||
ZDO_DEV_NWK_ORPHAN = 0x0A, // Device has lost information about its parent.
|
||||
};
|
||||
//
|
||||
|
||||
// Commands in the UTIL subsystem
|
||||
enum Z_Util {
|
||||
Z_UTIL_GET_DEVICE_INFO = 0x00,
|
||||
|
@ -427,4 +388,57 @@ enum ZCL_Global_Commands {
|
|||
const uint16_t Z_ProfileIds[] PROGMEM = { 0x0104, 0x0109, 0xA10E, 0xC05E };
|
||||
const char Z_ProfileNames[] PROGMEM = "ZigBee Home Automation|ZigBee Smart Energy|ZigBee Green Power|ZigBee Light Link";
|
||||
|
||||
typedef struct Z_StatusLine {
|
||||
uint32_t status; // no need to use uint8_t since it uses 32 bits anyways
|
||||
const char * status_msg;
|
||||
} Z_StatusLine;
|
||||
|
||||
const Z_StatusLine Z_Status[] PROGMEM = {
|
||||
0x00, "SUCCESS",
|
||||
0x01, "FAILURE",
|
||||
0x7E, "NOT_AUTHORIZED",
|
||||
0x7F, "RESERVED_FIELD_NOT_ZERO",
|
||||
0x80, "MALFORMED_COMMAND",
|
||||
0x81, "UNSUP_CLUSTER_COMMAND",
|
||||
0x82, "UNSUP_GENERAL_COMMAND",
|
||||
0x83, "UNSUP_MANUF_CLUSTER_COMMAND",
|
||||
0x84, "UNSUP_MANUF_GENERAL_COMMAND",
|
||||
0x85, "INVALID_FIELD",
|
||||
0x86, "UNSUPPORTED_ATTRIBUTE",
|
||||
0x87, "INVALID_VALUE",
|
||||
0x88, "READ_ONLY",
|
||||
0x89, "INSUFFICIENT_SPACE",
|
||||
0x8A, "DUPLICATE_EXISTS",
|
||||
0x8B, "NOT_FOUND",
|
||||
0x8C, "UNREPORTABLE_ATTRIBUTE",
|
||||
0x8D, "INVALID_DATA_TYPE",
|
||||
0x8E, "INVALID_SELECTOR",
|
||||
0x8F, "WRITE_ONLY",
|
||||
0x90, "INCONSISTENT_STARTUP_STATE",
|
||||
0x91, "DEFINED_OUT_OF_BAND",
|
||||
0x92, "INCONSISTENT",
|
||||
0x93, "ACTION_DENIED",
|
||||
0x94, "TIMEOUT",
|
||||
0x95, "ABORT",
|
||||
0x96, "INVALID_IMAGE",
|
||||
0x97, "WAIT_FOR_DATA",
|
||||
0x98, "NO_IMAGE_AVAILABLE",
|
||||
0x99, "REQUIRE_MORE_IMAGE",
|
||||
0x9A, "NOTIFICATION_PENDING",
|
||||
0xC0, "HARDWARE_FAILURE",
|
||||
0xC1, "SOFTWARE_FAILURE",
|
||||
0xC2, "CALIBRATION_ERROR",
|
||||
0xC3, "UNSUPPORTED_CLUSTER",
|
||||
};
|
||||
|
||||
const __FlashStringHelper* getZigbeeStatusMessage(uint8_t status) {
|
||||
for (uint32_t i = 0; i < sizeof(Z_Status) / sizeof(Z_Status[0]); i++) {
|
||||
const Z_StatusLine *statl = &Z_Status[i];
|
||||
if (statl->status == status) {
|
||||
return (const __FlashStringHelper*) statl->status_msg;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
#endif // USE_ZIGBEE
|
||||
|
|
|
@ -19,10 +19,6 @@
|
|||
|
||||
#ifdef USE_ZIGBEE
|
||||
|
||||
#ifndef ZIGBEERECEIVED
|
||||
#define ZIGBEERECEIVED 1
|
||||
#endif
|
||||
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
|
@ -97,7 +93,8 @@ public:
|
|||
void setManufId(uint16_t shortaddr, const char * str);
|
||||
void setModelId(uint16_t shortaddr, const char * str);
|
||||
void setFriendlyName(uint16_t shortaddr, const char * str);
|
||||
const String * getFriendlyName(uint16_t) const;
|
||||
const String * getFriendlyName(uint16_t shortaddr) const;
|
||||
const String * getModelId(uint16_t shortaddr) const;
|
||||
|
||||
// device just seen on the network, update the lastSeen field
|
||||
void updateLastSeen(uint16_t shortaddr);
|
||||
|
@ -537,6 +534,17 @@ const String * Z_Devices::getFriendlyName(uint16_t shortaddr) const {
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
const String * Z_Devices::getModelId(uint16_t shortaddr) const {
|
||||
int32_t found = findShortAddr(shortaddr);
|
||||
if (found >= 0) {
|
||||
const Z_Device & device = devicesAt(found);
|
||||
if (device.modelId.length() > 0) {
|
||||
return &device.modelId;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// device just seen on the network, update the lastSeen field
|
||||
void Z_Devices::updateLastSeen(uint16_t shortaddr) {
|
||||
Z_Device & device = getShortAddr(shortaddr);
|
||||
|
@ -656,13 +664,31 @@ bool Z_Devices::jsonIsConflict(uint16_t shortaddr, const JsonObject &values) {
|
|||
return false; // if no previous value, no conflict
|
||||
}
|
||||
|
||||
// compare groups
|
||||
uint16_t group1 = 0;
|
||||
uint16_t group2 = 0;
|
||||
if (device.json->containsKey(D_CMND_ZIGBEE_GROUP)) {
|
||||
group1 = device.json->get<unsigned int>(D_CMND_ZIGBEE_GROUP);
|
||||
}
|
||||
if (values.containsKey(D_CMND_ZIGBEE_GROUP)) {
|
||||
group2 = values.get<unsigned int>(D_CMND_ZIGBEE_GROUP);
|
||||
}
|
||||
if (group1 != group2) {
|
||||
return true; // if group addresses differ, then conflict
|
||||
}
|
||||
|
||||
// parse all other parameters
|
||||
for (auto kv : values) {
|
||||
String key_string = kv.key;
|
||||
|
||||
if (0 == strcasecmp_P(kv.key, PSTR(D_CMND_ZIGBEE_ENDPOINT))) {
|
||||
// attribute "Endpoint"
|
||||
if (kv.value.as<unsigned int>() != device.json->get<unsigned int>(kv.key)) {
|
||||
return true;
|
||||
if (0 == strcasecmp_P(kv.key, PSTR(D_CMND_ZIGBEE_GROUP))) {
|
||||
// ignore group, it was handled already
|
||||
} else if (0 == strcasecmp_P(kv.key, PSTR(D_CMND_ZIGBEE_ENDPOINT))) {
|
||||
// attribute "Endpoint" or "Group"
|
||||
if (device.json->containsKey(kv.key)) {
|
||||
if (kv.value.as<unsigned int>() != device.json->get<unsigned int>(kv.key)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else if (strcasecmp_P(kv.key, PSTR(D_CMND_ZIGBEE_LINKQUALITY))) { // exception = ignore duplicates for LinkQuality
|
||||
if (device.json->containsKey(kv.key)) {
|
||||
|
@ -734,25 +760,11 @@ void Z_Devices::jsonPublishFlush(uint16_t shortaddr) {
|
|||
Response_P(PSTR("{\"" D_JSON_ZIGBEE_RECEIVED "\":{\"%s\":%s}}"), fname->c_str(), msg.c_str());
|
||||
MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR));
|
||||
XdrvRulesProcess();
|
||||
#if ZIGBEERECEIVED
|
||||
// DEPRECATED TODO
|
||||
Response_P(PSTR("{\"" D_JSON_ZIGBEE_RECEIVED_LEGACY "\":{\"%s\":%s}}"), fname->c_str(), msg.c_str());
|
||||
MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR));
|
||||
XdrvRulesProcess();
|
||||
#endif
|
||||
} else {
|
||||
Response_P(PSTR("{\"" D_JSON_ZIGBEE_RECEIVED "\":{\"0x%04X\":%s}}"), shortaddr, msg.c_str());
|
||||
MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR));
|
||||
XdrvRulesProcess();
|
||||
#if ZIGBEERECEIVED
|
||||
// DEPRECATED TODO
|
||||
Response_P(PSTR("{\"" D_JSON_ZIGBEE_RECEIVED_LEGACY "\":{\"0x%04X\":%s}}"), shortaddr, msg.c_str());
|
||||
MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR));
|
||||
XdrvRulesProcess();
|
||||
#endif
|
||||
}
|
||||
// MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR));
|
||||
// XdrvRulesProcess();
|
||||
}
|
||||
|
||||
void Z_Devices::jsonPublishNow(uint16_t shortaddr, JsonObject & values) {
|
||||
|
|
|
@ -112,6 +112,7 @@ public:
|
|||
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 parseReadAttributes(JsonObject& json, uint8_t offset = 0);
|
||||
void parseResponse(void);
|
||||
void parseClusterSpecificCommand(JsonObject& json, uint8_t offset = 0);
|
||||
void postProcessAttributes(uint16_t shortaddr, JsonObject& json);
|
||||
|
||||
|
@ -484,6 +485,51 @@ void ZCLFrame::parseReadAttributes(JsonObject& json, uint8_t offset) {
|
|||
}
|
||||
}
|
||||
|
||||
// ZCL_DEFAULT_RESPONSE
|
||||
void ZCLFrame::parseResponse(void) {
|
||||
if (_payload.len() < 2) { return; } // wrong format
|
||||
uint8_t cmd = _payload.get8(0);
|
||||
uint8_t status = _payload.get8(1);
|
||||
|
||||
DynamicJsonBuffer jsonBuffer;
|
||||
JsonObject& json = jsonBuffer.createObject();
|
||||
|
||||
// "Device"
|
||||
char s[12];
|
||||
snprintf_P(s, sizeof(s), PSTR("0x%04X"), _srcaddr);
|
||||
json[F(D_JSON_ZIGBEE_DEVICE)] = s;
|
||||
// "Name"
|
||||
const String * friendlyName = zigbee_devices.getFriendlyName(_srcaddr);
|
||||
if (friendlyName) {
|
||||
json[F(D_JSON_ZIGBEE_NAME)] = *friendlyName;
|
||||
}
|
||||
// "Command"
|
||||
snprintf_P(s, sizeof(s), PSTR("%04X!%02X"), _cluster_id, cmd);
|
||||
json[F(D_JSON_ZIGBEE_CMD)] = s;
|
||||
// "Status"
|
||||
json[F(D_JSON_ZIGBEE_STATUS)] = status;
|
||||
// "StatusMessage"
|
||||
const __FlashStringHelper* statm = getZigbeeStatusMessage(status);
|
||||
if (statm) {
|
||||
json[F(D_JSON_ZIGBEE_STATUS_MSG)] = statm;
|
||||
}
|
||||
// Add Endpoint
|
||||
json[F(D_CMND_ZIGBEE_ENDPOINT)] = _srcendpoint;
|
||||
// Add Group if non-zero
|
||||
if (_group_id) {
|
||||
json[F(D_CMND_ZIGBEE_GROUP)] = _group_id;
|
||||
}
|
||||
// Add linkquality
|
||||
json[F(D_CMND_ZIGBEE_LINKQUALITY)] = _linkquality;
|
||||
|
||||
String msg("");
|
||||
msg.reserve(100);
|
||||
json.printTo(msg);
|
||||
Response_P(PSTR("{\"" D_JSON_ZIGBEE_RESPONSE "\":%s}"), msg.c_str());
|
||||
MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEEZCL_RECEIVED));
|
||||
XdrvRulesProcess();
|
||||
}
|
||||
|
||||
|
||||
// Parse non-normalized attributes
|
||||
void ZCLFrame::parseClusterSpecificCommand(JsonObject& json, uint8_t offset) {
|
||||
|
@ -519,7 +565,7 @@ const Z_AttributeConverter Z_PostProcess[] PROGMEM = {
|
|||
// Power Configuration cluster
|
||||
{ 0x0001, 0x0000, "MainsVoltage", &Z_Copy },
|
||||
{ 0x0001, 0x0001, "MainsFrequency", &Z_Copy },
|
||||
{ 0x0001, 0x0020, "BatteryVoltage", &Z_Copy },
|
||||
{ 0x0001, 0x0020, "BatteryVoltage", &Z_FloatDiv10 },
|
||||
{ 0x0001, 0x0021, "BatteryPercentageRemaining",&Z_Copy },
|
||||
|
||||
// Device Temperature Configuration cluster
|
||||
|
@ -564,78 +610,112 @@ const Z_AttributeConverter Z_PostProcess[] PROGMEM = {
|
|||
{ 0x000B, 0x0000, "QualityMeasure", &Z_Copy },
|
||||
{ 0x000B, 0x0000, "NumberOfDevices", &Z_Copy },
|
||||
// Analog Input cluster
|
||||
{ 0x000C, 0x0004, "ActiveText", &Z_Copy },
|
||||
{ 0x000C, 0x001C, "Description", &Z_Copy },
|
||||
{ 0x000C, 0x002E, "InactiveText", &Z_Copy },
|
||||
{ 0x000C, 0x0041, "MaxPresentValue", &Z_Copy },
|
||||
{ 0x000C, 0x0045, "MinPresentValue", &Z_Copy },
|
||||
{ 0x000C, 0x0051, "OutOfService", &Z_Copy },
|
||||
{ 0x000C, 0x0004, "AnalogInActiveText", &Z_Copy },
|
||||
{ 0x000C, 0x001C, "AnalogInDescription", &Z_Copy },
|
||||
{ 0x000C, 0x002E, "AnalogInInactiveText", &Z_Copy },
|
||||
{ 0x000C, 0x0041, "AnalogInMaxValue", &Z_Copy },
|
||||
{ 0x000C, 0x0045, "AnalogInMinValue", &Z_Copy },
|
||||
{ 0x000C, 0x0051, "AnalogInOutOfService", &Z_Copy },
|
||||
{ 0x000C, 0x0055, "AqaraRotate", &Z_Copy },
|
||||
{ 0x000C, 0x0057, "PriorityArray", &Z_Copy },
|
||||
{ 0x000C, 0x0067, "Reliability", &Z_Copy },
|
||||
{ 0x000C, 0x0068, "RelinquishDefault", &Z_Copy },
|
||||
{ 0x000C, 0x006A, "Resolution", &Z_Copy },
|
||||
{ 0x000C, 0x006F, "StatusFlags", &Z_Copy },
|
||||
{ 0x000C, 0x0075, "EngineeringUnits", &Z_Copy },
|
||||
{ 0x000C, 0x0100, "ApplicationType", &Z_Copy },
|
||||
{ 0x000C, 0x0057, "AnalogInPriorityArray",&Z_Copy },
|
||||
{ 0x000C, 0x0067, "AnalogInReliability", &Z_Copy },
|
||||
{ 0x000C, 0x0068, "AnalogInRelinquishDefault",&Z_Copy },
|
||||
{ 0x000C, 0x006A, "AnalogInResolution", &Z_Copy },
|
||||
{ 0x000C, 0x006F, "AnalogInStatusFlags", &Z_Copy },
|
||||
{ 0x000C, 0x0075, "AnalogInEngineeringUnits",&Z_Copy },
|
||||
{ 0x000C, 0x0100, "AnalogInApplicationType",&Z_Copy },
|
||||
{ 0x000C, 0xFF05, "Aqara_FF05", &Z_Copy },
|
||||
// Analog Output cluster
|
||||
{ 0x000D, 0x001C, "AnalogOutDescription", &Z_Copy },
|
||||
{ 0x000D, 0x0041, "AnalogOutMaxValue", &Z_Copy },
|
||||
{ 0x000D, 0x0045, "AnalogOutMinValue", &Z_Copy },
|
||||
{ 0x000D, 0x0051, "AnalogOutOutOfService",&Z_Copy },
|
||||
{ 0x000D, 0x0055, "AnalogOutValue", &Z_Copy },
|
||||
{ 0x000D, 0x0057, "AnalogOutPriorityArray",&Z_Copy },
|
||||
{ 0x000D, 0x0067, "AnalogOutReliability", &Z_Copy },
|
||||
{ 0x000D, 0x0068, "AnalogOutRelinquishDefault",&Z_Copy },
|
||||
{ 0x000D, 0x006A, "AnalogOutResolution", &Z_Copy },
|
||||
{ 0x000D, 0x006F, "AnalogOutStatusFlags", &Z_Copy },
|
||||
{ 0x000D, 0x0075, "AnalogOutEngineeringUnits",&Z_Copy },
|
||||
{ 0x000D, 0x0100, "AnalogOutApplicationType",&Z_Copy },
|
||||
// Analog Value cluster
|
||||
{ 0x000E, 0x001C, "AnalogDescription", &Z_Copy },
|
||||
{ 0x000E, 0x0051, "AnalogOutOfService", &Z_Copy },
|
||||
{ 0x000E, 0x0055, "AnalogValue", &Z_Copy },
|
||||
{ 0x000E, 0x0057, "AnalogPriorityArray", &Z_Copy },
|
||||
{ 0x000E, 0x0067, "AnalogReliability", &Z_Copy },
|
||||
{ 0x000E, 0x0068, "AnalogRelinquishDefault",&Z_Copy },
|
||||
{ 0x000E, 0x006F, "AnalogStatusFlags", &Z_Copy },
|
||||
{ 0x000E, 0x0075, "AnalogEngineeringUnits",&Z_Copy },
|
||||
{ 0x000E, 0x0100, "AnalogApplicationType",&Z_Copy },
|
||||
// Binary Input cluster
|
||||
{ 0x000F, 0x0004, "BinaryInActiveText", &Z_Copy },
|
||||
{ 0x000F, 0x001C, "BinaryInDescription", &Z_Copy },
|
||||
{ 0x000F, 0x002E, "BinaryInInactiveText",&Z_Copy },
|
||||
{ 0x000F, 0x0051, "BinaryInOutOfService",&Z_Copy },
|
||||
{ 0x000F, 0x0054, "BinaryInPolarity", &Z_Copy },
|
||||
{ 0x000F, 0x0055, "BinaryInValue", &Z_Copy },
|
||||
{ 0x000F, 0x0057, "BinaryInPriorityArray",&Z_Copy },
|
||||
{ 0x000F, 0x0067, "BinaryInReliability", &Z_Copy },
|
||||
{ 0x000F, 0x006F, "BinaryInStatusFlags", &Z_Copy },
|
||||
{ 0x000F, 0x0100, "BinaryInApplicationType",&Z_Copy },
|
||||
// Binary Output cluster
|
||||
{ 0x0010, 0x0004, "ActiveText", &Z_Copy },
|
||||
{ 0x0010, 0x001C, "Description", &Z_Copy },
|
||||
{ 0x0010, 0x002E, "InactiveText", &Z_Copy },
|
||||
{ 0x0010, 0x0042, "MinimumOffTime", &Z_Copy },
|
||||
{ 0x0010, 0x0043, "MinimumOnTime", &Z_Copy },
|
||||
{ 0x0010, 0x0051, "OutOfService", &Z_Copy },
|
||||
{ 0x0010, 0x0054, "Polarity", &Z_Copy },
|
||||
{ 0x0010, 0x0055, "PresentValue", &Z_Copy },
|
||||
{ 0x0010, 0x0057, "PriorityArray", &Z_Copy },
|
||||
{ 0x0010, 0x0067, "Reliability", &Z_Copy },
|
||||
{ 0x0010, 0x0068, "RelinquishDefault", &Z_Copy },
|
||||
{ 0x0010, 0x006F, "StatusFlags", &Z_Copy },
|
||||
{ 0x0010, 0x0100, "ApplicationType", &Z_Copy },
|
||||
{ 0x0010, 0x0004, "BinaryOutActiveText", &Z_Copy },
|
||||
{ 0x0010, 0x001C, "BinaryOutDescription", &Z_Copy },
|
||||
{ 0x0010, 0x002E, "BinaryOutInactiveText",&Z_Copy },
|
||||
{ 0x0010, 0x0042, "BinaryOutMinimumOffTime",&Z_Copy },
|
||||
{ 0x0010, 0x0043, "BinaryOutMinimumOnTime",&Z_Copy },
|
||||
{ 0x0010, 0x0051, "BinaryOutOutOfService",&Z_Copy },
|
||||
{ 0x0010, 0x0054, "BinaryOutPolarity", &Z_Copy },
|
||||
{ 0x0010, 0x0055, "BinaryOutValue", &Z_Copy },
|
||||
{ 0x0010, 0x0057, "BinaryOutPriorityArray",&Z_Copy },
|
||||
{ 0x0010, 0x0067, "BinaryOutReliability", &Z_Copy },
|
||||
{ 0x0010, 0x0068, "BinaryOutRelinquishDefault",&Z_Copy },
|
||||
{ 0x0010, 0x006F, "BinaryOutStatusFlags", &Z_Copy },
|
||||
{ 0x0010, 0x0100, "BinaryOutApplicationType",&Z_Copy },
|
||||
// Binary Value cluster
|
||||
{ 0x0011, 0x0004, "ActiveText", &Z_Copy },
|
||||
{ 0x0011, 0x001C, "Description", &Z_Copy },
|
||||
{ 0x0011, 0x002E, "InactiveText", &Z_Copy },
|
||||
{ 0x0011, 0x0042, "MinimumOffTime", &Z_Copy },
|
||||
{ 0x0011, 0x0043, "MinimumOnTime", &Z_Copy },
|
||||
{ 0x0011, 0x0051, "OutOfService", &Z_Copy },
|
||||
{ 0x0011, 0x0055, "PresentValue", &Z_Copy },
|
||||
{ 0x0011, 0x0057, "PriorityArray", &Z_Copy },
|
||||
{ 0x0011, 0x0067, "Reliability", &Z_Copy },
|
||||
{ 0x0011, 0x0068, "RelinquishDefault", &Z_Copy },
|
||||
{ 0x0011, 0x006F, "StatusFlags", &Z_Copy },
|
||||
{ 0x0011, 0x0100, "ApplicationType", &Z_Copy },
|
||||
{ 0x0011, 0x0004, "BinaryActiveText", &Z_Copy },
|
||||
{ 0x0011, 0x001C, "BinaryDescription", &Z_Copy },
|
||||
{ 0x0011, 0x002E, "BinaryInactiveText", &Z_Copy },
|
||||
{ 0x0011, 0x0042, "BinaryMinimumOffTime", &Z_Copy },
|
||||
{ 0x0011, 0x0043, "BinaryMinimumOnTime", &Z_Copy },
|
||||
{ 0x0011, 0x0051, "BinaryOutOfService", &Z_Copy },
|
||||
{ 0x0011, 0x0055, "BinaryValue", &Z_Copy },
|
||||
{ 0x0011, 0x0057, "BinaryPriorityArray", &Z_Copy },
|
||||
{ 0x0011, 0x0067, "BinaryReliability", &Z_Copy },
|
||||
{ 0x0011, 0x0068, "BinaryRelinquishDefault",&Z_Copy },
|
||||
{ 0x0011, 0x006F, "BinaryStatusFlags", &Z_Copy },
|
||||
{ 0x0011, 0x0100, "BinaryApplicationType",&Z_Copy },
|
||||
// Multistate Input cluster
|
||||
{ 0x0012, 0x000E, "StateText", &Z_Copy },
|
||||
{ 0x0012, 0x001C, "Description", &Z_Copy },
|
||||
{ 0x0012, 0x004A, "NumberOfStates", &Z_Copy },
|
||||
{ 0x0012, 0x0051, "OutOfService", &Z_Copy },
|
||||
{ 0x0012, 0x0055, "PresentValue", &Z_AqaraCube },
|
||||
{ 0x0012, 0x0067, "Reliability", &Z_Copy },
|
||||
{ 0x0012, 0x006F, "StatusFlags", &Z_Copy },
|
||||
{ 0x0012, 0x0100, "ApplicationType", &Z_Copy },
|
||||
{ 0x0012, 0x000E, "MultiInStateText", &Z_Copy },
|
||||
{ 0x0012, 0x001C, "MultiInDescription", &Z_Copy },
|
||||
{ 0x0012, 0x004A, "MultiInNumberOfStates",&Z_Copy },
|
||||
{ 0x0012, 0x0051, "MultiInOutOfService", &Z_Copy },
|
||||
{ 0x0012, 0x0055, "MultiInValue", &Z_AqaraCube },
|
||||
{ 0x0012, 0x0067, "MultiInReliability", &Z_Copy },
|
||||
{ 0x0012, 0x006F, "MultiInStatusFlags", &Z_Copy },
|
||||
{ 0x0012, 0x0100, "MultiInApplicationType",&Z_Copy },
|
||||
// Multistate output
|
||||
{ 0x0013, 0x000E, "StateText", &Z_Copy },
|
||||
{ 0x0013, 0x001C, "Description", &Z_Copy },
|
||||
{ 0x0013, 0x004A, "NumberOfStates", &Z_Copy },
|
||||
{ 0x0013, 0x0051, "OutOfService", &Z_Copy },
|
||||
{ 0x0013, 0x0055, "PresentValue", &Z_Copy },
|
||||
{ 0x0013, 0x0057, "PriorityArray", &Z_Copy },
|
||||
{ 0x0013, 0x0067, "Reliability", &Z_Copy },
|
||||
{ 0x0013, 0x0068, "RelinquishDefault", &Z_Copy },
|
||||
{ 0x0013, 0x006F, "StatusFlags", &Z_Copy },
|
||||
{ 0x0013, 0x0100, "ApplicationType", &Z_Copy },
|
||||
{ 0x0013, 0x000E, "MultiOutStateText", &Z_Copy },
|
||||
{ 0x0013, 0x001C, "MultiOutDescription", &Z_Copy },
|
||||
{ 0x0013, 0x004A, "MultiOutNumberOfStates",&Z_Copy },
|
||||
{ 0x0013, 0x0051, "MultiOutOutOfService", &Z_Copy },
|
||||
{ 0x0013, 0x0055, "MultiOutValue", &Z_Copy },
|
||||
{ 0x0013, 0x0057, "MultiOutPriorityArray",&Z_Copy },
|
||||
{ 0x0013, 0x0067, "MultiOutReliability", &Z_Copy },
|
||||
{ 0x0013, 0x0068, "MultiOutRelinquishDefault",&Z_Copy },
|
||||
{ 0x0013, 0x006F, "MultiOutStatusFlags", &Z_Copy },
|
||||
{ 0x0013, 0x0100, "MultiOutApplicationType",&Z_Copy },
|
||||
// Multistate Value cluster
|
||||
{ 0x0014, 0x000E, "StateText", &Z_Copy },
|
||||
{ 0x0014, 0x001C, "Description", &Z_Copy },
|
||||
{ 0x0014, 0x004A, "NumberOfStates", &Z_Copy },
|
||||
{ 0x0014, 0x0051, "OutOfService", &Z_Copy },
|
||||
{ 0x0014, 0x0055, "PresentValue", &Z_Copy },
|
||||
{ 0x0014, 0x0067, "Reliability", &Z_Copy },
|
||||
{ 0x0014, 0x0068, "RelinquishDefault", &Z_Copy },
|
||||
{ 0x0014, 0x006F, "StatusFlags", &Z_Copy },
|
||||
{ 0x0014, 0x0100, "ApplicationType", &Z_Copy },
|
||||
{ 0x0014, 0x000E, "MultiStateText", &Z_Copy },
|
||||
{ 0x0014, 0x001C, "MultiDescription", &Z_Copy },
|
||||
{ 0x0014, 0x004A, "MultiNumberOfStates", &Z_Copy },
|
||||
{ 0x0014, 0x0051, "MultiOutOfService", &Z_Copy },
|
||||
{ 0x0014, 0x0055, "MultiValue", &Z_Copy },
|
||||
{ 0x0014, 0x0067, "MultiReliability", &Z_Copy },
|
||||
{ 0x0014, 0x0068, "MultiRelinquishDefault",&Z_Copy },
|
||||
{ 0x0014, 0x006F, "MultiStatusFlags", &Z_Copy },
|
||||
{ 0x0014, 0x0100, "MultiApplicationType", &Z_Copy },
|
||||
// Power Profile cluster
|
||||
{ 0x001A, 0x0000, "TotalProfileNum", &Z_Copy },
|
||||
{ 0x001A, 0x0001, "MultipleScheduling", &Z_Copy },
|
||||
|
@ -682,12 +762,12 @@ const Z_AttributeConverter Z_PostProcess[] PROGMEM = {
|
|||
{ 0x0102, 0x0009, "CurrentPositionTiltPercentage",&Z_Copy },
|
||||
{ 0x0102, 0x0010, "InstalledOpenLimitLift",&Z_Copy },
|
||||
{ 0x0102, 0x0011, "InstalledClosedLimitLift",&Z_Copy },
|
||||
{ 0x0102, 0x0012, "InstalledOpenLimitTilt", &Z_Copy },
|
||||
{ 0x0102, 0x0013, "InstalledClosedLimitTilt", &Z_Copy },
|
||||
{ 0x0102, 0x0014, "VelocityLift",&Z_Copy },
|
||||
{ 0x0102, 0x0012, "InstalledOpenLimitTilt",&Z_Copy },
|
||||
{ 0x0102, 0x0013, "InstalledClosedLimitTilt",&Z_Copy },
|
||||
{ 0x0102, 0x0014, "VelocityLift", &Z_Copy },
|
||||
{ 0x0102, 0x0015, "AccelerationTimeLift",&Z_Copy },
|
||||
{ 0x0102, 0x0016, "DecelerationTimeLift", &Z_Copy },
|
||||
{ 0x0102, 0x0017, "Mode",&Z_Copy },
|
||||
{ 0x0102, 0x0016, "DecelerationTimeLift", &Z_Copy },
|
||||
{ 0x0102, 0x0017, "Mode", &Z_Copy },
|
||||
{ 0x0102, 0x0018, "IntermediateSetpointsLift",&Z_Copy },
|
||||
{ 0x0102, 0x0019, "IntermediateSetpointsTilt",&Z_Copy },
|
||||
|
||||
|
@ -725,49 +805,49 @@ const Z_AttributeConverter Z_PostProcess[] PROGMEM = {
|
|||
|
||||
// Illuminance Measurement cluster
|
||||
{ 0x0400, 0x0000, D_JSON_ILLUMINANCE, &Z_Copy }, // Illuminance (in Lux)
|
||||
{ 0x0400, 0x0001, "MinMeasuredValue", &Z_Copy }, //
|
||||
{ 0x0400, 0x0002, "MaxMeasuredValue", &Z_Copy }, //
|
||||
{ 0x0400, 0x0003, "Tolerance", &Z_Copy }, //
|
||||
{ 0x0400, 0x0004, "LightSensorType", &Z_Copy }, //
|
||||
{ 0x0400, 0x0001, "IlluminanceMinMeasuredValue", &Z_Copy }, //
|
||||
{ 0x0400, 0x0002, "IlluminanceMaxMeasuredValue", &Z_Copy }, //
|
||||
{ 0x0400, 0x0003, "IlluminanceTolerance", &Z_Copy }, //
|
||||
{ 0x0400, 0x0004, "IlluminanceLightSensorType", &Z_Copy }, //
|
||||
{ 0x0400, 0xFFFF, nullptr, &Z_Remove }, // Remove all other values
|
||||
|
||||
// Illuminance Level Sensing cluster
|
||||
{ 0x0401, 0x0000, "LevelStatus", &Z_Copy }, // Illuminance (in Lux)
|
||||
{ 0x0401, 0x0001, "LightSensorType", &Z_Copy }, // LightSensorType
|
||||
{ 0x0401, 0x0000, "IlluminanceLevelStatus", &Z_Copy }, // Illuminance (in Lux)
|
||||
{ 0x0401, 0x0001, "IlluminanceLightSensorType", &Z_Copy }, // LightSensorType
|
||||
{ 0x0401, 0xFFFF, nullptr, &Z_Remove }, // Remove all other values
|
||||
|
||||
// Temperature Measurement cluster
|
||||
{ 0x0402, 0x0000, D_JSON_TEMPERATURE, &Z_FloatDiv100 }, // Temperature
|
||||
{ 0x0402, 0x0001, "MinMeasuredValue", &Z_FloatDiv100 }, //
|
||||
{ 0x0402, 0x0002, "MaxMeasuredValue", &Z_FloatDiv100 }, //
|
||||
{ 0x0402, 0x0003, "Tolerance", &Z_FloatDiv100 }, //
|
||||
{ 0x0402, 0x0001, "TemperatureMinMeasuredValue", &Z_FloatDiv100 }, //
|
||||
{ 0x0402, 0x0002, "TemperatureMaxMeasuredValue", &Z_FloatDiv100 }, //
|
||||
{ 0x0402, 0x0003, "TemperatureTolerance", &Z_FloatDiv100 }, //
|
||||
{ 0x0402, 0xFFFF, nullptr, &Z_Remove }, // Remove all other values
|
||||
|
||||
// Pressure Measurement cluster
|
||||
{ 0x0403, 0x0000, D_JSON_PRESSURE_UNIT, &Z_AddPressureUnit }, // Pressure Unit
|
||||
{ 0x0403, 0x0000, D_JSON_PRESSURE, &Z_Copy }, // Pressure
|
||||
{ 0x0403, 0x0001, "MinMeasuredValue", &Z_Copy }, //
|
||||
{ 0x0403, 0x0002, "MaxMeasuredValue", &Z_Copy }, //
|
||||
{ 0x0403, 0x0003, "Tolerance", &Z_Copy }, //
|
||||
{ 0x0403, 0x0010, "ScaledValue", &Z_Copy }, //
|
||||
{ 0x0403, 0x0011, "MinScaledValue", &Z_Copy }, //
|
||||
{ 0x0403, 0x0012, "MaxScaledValue", &Z_Copy }, //
|
||||
{ 0x0403, 0x0013, "ScaledTolerance", &Z_Copy }, //
|
||||
{ 0x0403, 0x0014, "Scale", &Z_Copy }, //
|
||||
{ 0x0403, 0x0001, "PressureMinMeasuredValue", &Z_Copy }, //
|
||||
{ 0x0403, 0x0002, "PressureMaxMeasuredValue", &Z_Copy }, //
|
||||
{ 0x0403, 0x0003, "PressureTolerance", &Z_Copy }, //
|
||||
{ 0x0403, 0x0010, "PressureScaledValue", &Z_Copy }, //
|
||||
{ 0x0403, 0x0011, "PressureMinScaledValue", &Z_Copy }, //
|
||||
{ 0x0403, 0x0012, "PressureMaxScaledValue", &Z_Copy }, //
|
||||
{ 0x0403, 0x0013, "PressureScaledTolerance", &Z_Copy }, //
|
||||
{ 0x0403, 0x0014, "PressureScale", &Z_Copy }, //
|
||||
{ 0x0403, 0xFFFF, nullptr, &Z_Remove }, // Remove all other Pressure values
|
||||
|
||||
// Flow Measurement cluster
|
||||
{ 0x0404, 0x0000, D_JSON_FLOWRATE, &Z_FloatDiv10 }, // Flow (in m3/h)
|
||||
{ 0x0404, 0x0001, "MinMeasuredValue", &Z_Copy }, //
|
||||
{ 0x0404, 0x0002, "MaxMeasuredValue", &Z_Copy }, //
|
||||
{ 0x0404, 0x0003, "Tolerance", &Z_Copy }, //
|
||||
{ 0x0404, 0x0001, "FlowMinMeasuredValue", &Z_Copy }, //
|
||||
{ 0x0404, 0x0002, "FlowMaxMeasuredValue", &Z_Copy }, //
|
||||
{ 0x0404, 0x0003, "FlowTolerance", &Z_Copy }, //
|
||||
{ 0x0404, 0xFFFF, nullptr, &Z_Remove }, // Remove all other values
|
||||
|
||||
// Relative Humidity Measurement cluster
|
||||
{ 0x0405, 0x0000, D_JSON_HUMIDITY, &Z_FloatDiv100 }, // Humidity
|
||||
{ 0x0405, 0x0001, "MinMeasuredValue", &Z_Copy }, //
|
||||
{ 0x0405, 0x0002, "MaxMeasuredValue", &Z_Copy }, //
|
||||
{ 0x0405, 0x0003, "Tolerance", &Z_Copy }, //
|
||||
{ 0x0405, 0x0001, "HumidityMinMeasuredValue", &Z_Copy }, //
|
||||
{ 0x0405, 0x0002, "HumidityMaxMeasuredValue", &Z_Copy }, //
|
||||
{ 0x0405, 0x0003, "HumidityTolerance", &Z_Copy }, //
|
||||
{ 0x0405, 0xFFFF, nullptr, &Z_Remove }, // Remove all other values
|
||||
|
||||
// Occupancy Sensing cluster
|
||||
|
@ -837,6 +917,11 @@ int32_t Z_FloatDiv10(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject&
|
|||
json[new_name] = ((float)value) / 10.0f;
|
||||
return 1; // remove original key
|
||||
}
|
||||
// Convert int to float and divide by 10
|
||||
int32_t Z_FloatDiv2(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) / 2.0f;
|
||||
return 1; // remove original key
|
||||
}
|
||||
|
||||
// Publish a message for `"Occupancy":0` when the timer expired
|
||||
int32_t Z_OccupancyCallback(uint16_t shortaddr, uint16_t cluster, uint16_t endpoint, uint32_t value) {
|
||||
|
@ -947,9 +1032,6 @@ int32_t Z_AqaraVibration(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObje
|
|||
int32_t Angle_X = 0.5f + atanf(X/sqrtf(z*z+y*y)) * f_180pi;
|
||||
int32_t Angle_Y = 0.5f + atanf(Y/sqrtf(x*x+z*z)) * f_180pi;
|
||||
int32_t Angle_Z = 0.5f + atanf(Z/sqrtf(x*x+y*y)) * f_180pi;
|
||||
// int32_t Angle_X = 0.5f + atanf(X/sqrtf(Z*Z+Y*Y)) * f_180pi;
|
||||
// int32_t Angle_Y = 0.5f + atanf(Y/sqrtf(X*X+Z*Z)) * f_180pi;
|
||||
// int32_t Angle_Z = 0.5f + atanf(Z/sqrtf(X*X+Y*Y)) * f_180pi;
|
||||
JsonArray& angles = json.createNestedArray(F("AqaraAngles"));
|
||||
angles.add(Angle_X);
|
||||
angles.add(Angle_Y);
|
||||
|
@ -968,6 +1050,7 @@ int32_t Z_AqaraSensor(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject&
|
|||
char tmp[] = "tmp"; // for obscure reasons, it must be converted from const char* to char*, otherwise ArduinoJson gets confused
|
||||
|
||||
JsonVariant sub_value;
|
||||
const String * modelId = zigbee_devices.getModelId(shortaddr); // null if unknown
|
||||
|
||||
while (len - i >= 2) {
|
||||
uint8_t attrid = buf2.get8(i++);
|
||||
|
@ -975,25 +1058,43 @@ int32_t Z_AqaraSensor(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject&
|
|||
i += parseSingleAttribute(json, tmp, buf2, i, len);
|
||||
float val = json[tmp];
|
||||
json.remove(tmp);
|
||||
bool translated = false; // were we able to translate to a known format?
|
||||
if (0x01 == attrid) {
|
||||
json[F(D_JSON_VOLTAGE)] = val / 1000.0f;
|
||||
json[F("Battery")] = toPercentageCR2032(val);
|
||||
} else if (0 == zcl->getManufCode()) {
|
||||
// onla Aqara Temp/Humidity has manuf_code of zero. If non-zero we skip the parameters
|
||||
if (0x64 == attrid) {
|
||||
json[F(D_JSON_TEMPERATURE)] = val / 100.0f;
|
||||
} else if (0x65 == attrid) {
|
||||
json[F(D_JSON_HUMIDITY)] = val / 100.0f;
|
||||
} else if (0x66 == attrid) {
|
||||
json[F(D_JSON_PRESSURE)] = val / 100.0f;
|
||||
json[F(D_JSON_PRESSURE_UNIT)] = F(D_UNIT_PRESSURE); // hPa
|
||||
} else if (0x01 == attrid) {
|
||||
json[F(D_JSON_VOLTAGE)] = val / 1000.0f;
|
||||
json[F("Battery")] = toPercentageCR2032(val);
|
||||
} else if ((nullptr != modelId) && (0 == zcl->getManufCode())) {
|
||||
translated = true;
|
||||
if (modelId->startsWith(F("lumi.sensor_ht")) ||
|
||||
modelId->startsWith(F("lumi.weather"))) { // Temp sensor
|
||||
// Filter according to prefix of model name
|
||||
// onla Aqara Temp/Humidity has manuf_code of zero. If non-zero we skip the parameters
|
||||
if (0x64 == attrid) {
|
||||
json[F(D_JSON_TEMPERATURE)] = val / 100.0f;
|
||||
} else if (0x65 == attrid) {
|
||||
json[F(D_JSON_HUMIDITY)] = val / 100.0f;
|
||||
} else if (0x66 == attrid) {
|
||||
json[F(D_JSON_PRESSURE)] = val / 100.0f;
|
||||
json[F(D_JSON_PRESSURE_UNIT)] = F(D_UNIT_PRESSURE); // hPa
|
||||
}
|
||||
} else if (modelId->startsWith(F("lumi.sensor_smoke"))) { // gas leak
|
||||
if (0x64 == attrid) {
|
||||
json[F("SmokeDensity")] = val;
|
||||
}
|
||||
} else if (modelId->startsWith(F("lumi.sensor_natgas"))) { // gas leak
|
||||
if (0x64 == attrid) {
|
||||
json[F("GasDensity")] = val;
|
||||
}
|
||||
} else {
|
||||
translated = false; // we didn't find a match
|
||||
}
|
||||
// } else if (0x115F == zcl->getManufCode()) { // Aqara Motion Sensor, still unknown field
|
||||
}
|
||||
if (!translated) {
|
||||
if (attrid >= 100) { // payload is always above 0x64 or 100
|
||||
char attr_name[12];
|
||||
snprintf_P(attr_name, sizeof(attr_name), PSTR("Xiaomi_%02X"), attrid);
|
||||
json[attr_name] = val;
|
||||
}
|
||||
} else if (0x115F == zcl->getManufCode()) {
|
||||
// Aqara Motion Sensor, still unknown field
|
||||
json[F("AqaraUnknown")] = val;
|
||||
}
|
||||
}
|
||||
return 1; // remove original key
|
||||
|
|
|
@ -36,8 +36,14 @@ typedef struct Z_XYZ_Var { // Holds values for vairables X, Y and Z
|
|||
uint8_t z_type = 0;
|
||||
} Z_XYZ_Var;
|
||||
|
||||
// list of post-processing directives
|
||||
const Z_CommandConverter Z_Commands[] = {
|
||||
// Cluster specific commands
|
||||
// Note: the table is both for sending commands, but also displaying received commands
|
||||
// - tasmota_cmd: the human-readable name of the command as entered or displayed, use '|' to split into multiple commands when displayed
|
||||
// - cluster: cluster number of the command
|
||||
// - cmd: the command number, of 0xFF if it's actually a variable to be assigned from 'xx'
|
||||
// - direction: the direction of the command (bit field). 0x01=from client to server (coord to device), 0x02= from server to client (response), 0x80=needs specific decoding
|
||||
// - param: the paylod template, x/y/z are substituted with arguments, little endian. For command display, payload must match until x/y/z character or until the end of the paylod. '??' means ignore.
|
||||
const Z_CommandConverter Z_Commands[] PROGMEM = {
|
||||
// Group adress commands
|
||||
{ "AddGroup", 0x0004, 0x00, 0x01, "xxxx00" }, // Add group id, group name is not supported
|
||||
{ "ViewGroup", 0x0004, 0x01, 0x01, "xxxx" }, // Ask for the group name
|
||||
|
@ -65,7 +71,7 @@ const Z_CommandConverter Z_Commands[] = {
|
|||
{ "ShutterTilt", 0x0102, 0x08, 0x01, "xx" }, // Tilt percentage
|
||||
{ "Shutter", 0x0102, 0xFF, 0x01, "" },
|
||||
// Blitzwolf PIR
|
||||
{ "Occupancy", 0xEF00, 0x01, 0x01, "xx"}, // Specific decoder for Blitzwolf PIR, empty name means special treatment
|
||||
{ "Occupancy", 0xEF00, 0x01, 0x82, ""}, // Specific decoder for Blitzwolf PIR, empty name means special treatment
|
||||
// Decoders only - normally not used to send, and names may be masked by previous definitions
|
||||
{ "Dimmer", 0x0008, 0x00, 0x01, "xx" },
|
||||
{ "DimmerMove", 0x0008, 0x01, 0x01, "xx0A" },
|
||||
|
@ -85,15 +91,14 @@ const Z_CommandConverter Z_Commands[] = {
|
|||
{ "ArrowHold", 0x0005, 0x08, 0x01, "xx" }, // xx == 0x01 = left, 0x00 = right
|
||||
{ "ArrowRelease", 0x0005, 0x09, 0x01, "" },
|
||||
// IAS - Intruder Alarm System + leak/fire detection
|
||||
{ "ZoneStatusChange",0x0500, 0x00, 0x02, "xxxxyyzz" }, // xxxx = zone status, yy = extended status, zz = zone id, Delay is ignored
|
||||
{ "ZoneStatusChange",0x0500, 0x00, 0x82, "xxxxyyzz" }, // xxxx = zone status, yy = extended status, zz = zone id, Delay is ignored
|
||||
// responses for Group cluster commands
|
||||
{ "AddGroupResp", 0x0004, 0x00, 0x02, "xxyyyy" }, // xx = status, yy = group id
|
||||
{ "ViewGroupResp", 0x0004, 0x01, 0x02, "xxyyyy" }, // xx = status, yy = group id, name ignored
|
||||
{ "GetGroupResp", 0x0004, 0x02, 0x02, "xxyyzzzz" }, // xx = capacity, yy = count, zzzz = first group id, following groups ignored
|
||||
{ "RemoveGroup", 0x0004, 0x03, 0x02, "xxyyyy" }, // xx = status, yy = group id
|
||||
{ "AddGroup", 0x0004, 0x00, 0x82, "xxyyyy" }, // xx = status, yy = group id
|
||||
{ "ViewGroup", 0x0004, 0x01, 0x82, "xxyyyy" }, // xx = status, yy = group id, name ignored
|
||||
{ "GetGroup", 0x0004, 0x02, 0x82, "xxyyzzzz" }, // xx = capacity, yy = count, zzzz = first group id, following groups ignored
|
||||
{ "RemoveGroup", 0x0004, 0x03, 0x82, "xxyyyy" }, // xx = status, yy = group id
|
||||
};
|
||||
|
||||
|
||||
#define ZLE(x) ((x) & 0xFF), ((x) >> 8) // Little Endian
|
||||
|
||||
// Below are the attributes we wand to read from each cluster
|
||||
|
@ -235,16 +240,20 @@ void convertClusterSpecific(JsonObject& json, uint16_t cluster, uint8_t cmd, boo
|
|||
ToHex_P((unsigned char*)payload.getBuffer(), payload.len(), hex_char, hex_char_len);
|
||||
|
||||
const __FlashStringHelper* command_name = nullptr;
|
||||
uint8_t conv_direction;
|
||||
Z_XYZ_Var xyz;
|
||||
|
||||
//AddLog_P2(LOG_LEVEL_INFO, PSTR(">>> len = %d - %02X%02X%02X"), payload.len(), payload.get8(0), payload.get8(1), payload.get8(2));
|
||||
for (uint32_t i = 0; i < sizeof(Z_Commands) / sizeof(Z_Commands[0]); i++) {
|
||||
const Z_CommandConverter *conv = &Z_Commands[i];
|
||||
if (conv->cluster == cluster) {
|
||||
uint16_t conv_cluster = pgm_read_word(&conv->cluster);
|
||||
if (conv_cluster == cluster) {
|
||||
// cluster match
|
||||
if ((0xFF == conv->cmd) || (cmd == conv->cmd)) {
|
||||
uint8_t conv_cmd = pgm_read_byte(&conv->cmd);
|
||||
conv_direction = pgm_read_byte(&conv->direction);
|
||||
if ((0xFF == conv_cmd) || (cmd == conv_cmd)) {
|
||||
// cmd match
|
||||
if ((direction && (conv->direction & 0x02)) || (!direction && (conv->direction & 0x01))) {
|
||||
if ((direction && (conv_direction & 0x02)) || (!direction && (conv_direction & 0x01))) {
|
||||
// check if we have a match for params too
|
||||
// Match if:
|
||||
// - payload exactly matches conv->param (conv->param may be longer)
|
||||
|
@ -271,7 +280,7 @@ void convertClusterSpecific(JsonObject& json, uint16_t cluster, uint8_t cmd, boo
|
|||
if (match) {
|
||||
command_name = (const __FlashStringHelper*) conv->tasmota_cmd;
|
||||
parseXYZ(conv->param, payload, &xyz);
|
||||
if (0xFF == conv->cmd) {
|
||||
if (0xFF == conv_cmd) {
|
||||
// shift all values
|
||||
xyz.z = xyz.y;
|
||||
xyz.z_type = xyz.y_type;
|
||||
|
@ -296,31 +305,63 @@ void convertClusterSpecific(JsonObject& json, uint16_t cluster, uint8_t cmd, boo
|
|||
free(hex_char);
|
||||
|
||||
if (command_name) {
|
||||
if (0 == xyz.x_type) {
|
||||
json[command_name] = true; // no parameter
|
||||
} else if (0 == xyz.y_type) {
|
||||
json[command_name] = xyz.x; // 1 parameter
|
||||
// Now try to transform into a human readable format
|
||||
// if (direction & 0x80) then specific transform
|
||||
if (conv_direction & 0x80) {
|
||||
// TODO need to create a specific command
|
||||
// IAS
|
||||
String command_name2 = String(command_name);
|
||||
if ((cluster == 0x0500) && (cmd == 0x00)) {
|
||||
// "ZoneStatusChange"
|
||||
json[command_name] = xyz.x;
|
||||
json[command_name2 + "Ext"] = xyz.y;
|
||||
json[command_name2 + "Zone"] = xyz.z;
|
||||
} else if ((cluster == 0x0004) && ((cmd == 0x00) || (cmd == 0x01) || (cmd == 0x03))) {
|
||||
// AddGroupResp or ViewGroupResp (group name ignored) or RemoveGroup
|
||||
json[command_name] = xyz.y;
|
||||
json[command_name2 + "Status"] = xyz.x;
|
||||
json[command_name2 + "StatusMsg"] = getZigbeeStatusMessage(xyz.x);
|
||||
} else if ((cluster == 0x0004) && (cmd == 0x02)) {
|
||||
// GetGroupResp
|
||||
json[command_name2 + "Capacity"] = xyz.x;
|
||||
json[command_name2 + "Count"] = xyz.y;
|
||||
JsonArray &arr = json.createNestedArray(command_name);
|
||||
for (uint32_t i = 0; i < xyz.y; i++) {
|
||||
arr.add(payload.get16(2 + 2*i));
|
||||
}
|
||||
//arr.add(xyz.z);
|
||||
}
|
||||
} else {
|
||||
// multiple answers, create an array
|
||||
JsonArray &arr = json.createNestedArray(command_name);
|
||||
arr.add(xyz.x);
|
||||
arr.add(xyz.y);
|
||||
if (xyz.z_type) {
|
||||
arr.add(xyz.z);
|
||||
if (0 == xyz.x_type) {
|
||||
json[command_name] = true; // no parameter
|
||||
} else if (0 == xyz.y_type) {
|
||||
json[command_name] = xyz.x; // 1 parameter
|
||||
} else {
|
||||
// multiple answers, create an array
|
||||
JsonArray &arr = json.createNestedArray(command_name);
|
||||
arr.add(xyz.x);
|
||||
arr.add(xyz.y);
|
||||
if (xyz.z_type) {
|
||||
arr.add(xyz.z);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Find the command details by command name
|
||||
// Only take commands outgoing, i.e. direction == 0
|
||||
// If not found:
|
||||
// - returns nullptr
|
||||
const __FlashStringHelper* zigbeeFindCommand(const char *command, uint16_t *cluster, uint16_t *cmd) {
|
||||
for (uint32_t i = 0; i < sizeof(Z_Commands) / sizeof(Z_Commands[0]); i++) {
|
||||
const Z_CommandConverter *conv = &Z_Commands[i];
|
||||
if (0 == strcasecmp_P(command, conv->tasmota_cmd)) {
|
||||
*cluster = conv->cluster;
|
||||
*cmd = conv->cmd;
|
||||
uint8_t conv_direction = pgm_read_byte(&conv->direction);
|
||||
uint8_t conv_cmd = pgm_read_byte(&conv->cmd);
|
||||
uint16_t conv_cluster = pgm_read_word(&conv->cluster);
|
||||
if ((conv_direction & 0x01) && (0 == strcasecmp_P(command, conv->tasmota_cmd))) {
|
||||
*cluster = conv_cluster;
|
||||
*cmd = conv_cmd;
|
||||
return (const __FlashStringHelper*) conv->param;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,7 +33,7 @@ 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_SIMPLE_DESC = 33; // Simple Descriptor (clusters)
|
||||
const uint8_t ZIGBEE_STATUS_DEVICE_INDICATION = 34; // Device announces its address
|
||||
const uint8_t ZIGBEE_STATUS_DEVICE_IEEE = 35; // Request of device address
|
||||
//const uint8_t ZIGBEE_STATUS_DEVICE_IEEE = 35; // Request of device address
|
||||
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_UNSUPPORTED_VERSION = 98; // Unsupported ZNP version
|
||||
|
@ -49,9 +49,6 @@ typedef union Zigbee_Instruction {
|
|||
uint16_t d16; // 16 bits data
|
||||
} i;
|
||||
const void *p; // pointer
|
||||
// const void *m; // for type checking only, message
|
||||
// const ZB_Func f;
|
||||
// const ZB_RecvMsgFunc fr;
|
||||
} Zigbee_Instruction;
|
||||
//
|
||||
// Zigbee_Instruction z1 = { .i = {1,2,3}};
|
||||
|
|
|
@ -39,7 +39,7 @@ int32_t Z_ReceiveDeviceInfo(int32_t res, class SBuffer &buf) {
|
|||
char hex[20];
|
||||
Uint64toHex(long_adr, hex, 64);
|
||||
Response_P(PSTR("{\"" D_JSON_ZIGBEE_STATE "\":{"
|
||||
"\"Status\":%d,\"IEEEAddr\":\"%s\",\"ShortAddr\":\"0x%04X\""
|
||||
"\"Status\":%d,\"IEEEAddr\":\"0x%s\",\"ShortAddr\":\"0x%04X\""
|
||||
",\"DeviceType\":%d,\"DeviceState\":%d"
|
||||
",\"NumAssocDevices\":%d"),
|
||||
ZIGBEE_STATUS_CC_INFO, hex, short_adr, device_type, device_state,
|
||||
|
@ -355,21 +355,24 @@ int32_t Z_ReceiveIEEEAddr(int32_t res, const class SBuffer &buf) {
|
|||
zigbee_devices.updateDevice(nwkAddr, ieeeAddr);
|
||||
char hex[20];
|
||||
Uint64toHex(ieeeAddr, hex, 64);
|
||||
Response_P(PSTR("{\"" D_JSON_ZIGBEE_STATE "\":{"
|
||||
"\"Status\":%d,\"IEEEAddr\":\"%s\",\"ShortAddr\":\"0x%04X\""
|
||||
"}}"),
|
||||
ZIGBEE_STATUS_DEVICE_IEEE, hex, nwkAddr
|
||||
);
|
||||
// Response_P(PSTR("{\"" D_JSON_ZIGBEE_STATE "\":{"
|
||||
// "\"Status\":%d,\"IEEEAddr\":\"%s\",\"ShortAddr\":\"0x%04X\""
|
||||
// "}}"),
|
||||
// ZIGBEE_STATUS_DEVICE_IEEE, hex, nwkAddr
|
||||
// );
|
||||
|
||||
MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEEZCL_RECEIVED));
|
||||
XdrvRulesProcess();
|
||||
// MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEEZCL_RECEIVED));
|
||||
// XdrvRulesProcess();
|
||||
// Ping response
|
||||
const String * friendlyName = zigbee_devices.getFriendlyName(nwkAddr);
|
||||
if (friendlyName) {
|
||||
Response_P(PSTR("{\"" D_JSON_ZIGBEE_PING "\":{\"" D_JSON_ZIGBEE_DEVICE "\":\"0x%04X\""
|
||||
",\"" D_JSON_ZIGBEE_NAME "\":\"%s\"}}"), nwkAddr, friendlyName->c_str());
|
||||
",\"" D_JSON_ZIGBEE_IEEE "\":\"0x%s\""
|
||||
",\"" D_JSON_ZIGBEE_NAME "\":\"%s\"}}"), nwkAddr, hex, friendlyName->c_str());
|
||||
} else {
|
||||
Response_P(PSTR("{\"" D_JSON_ZIGBEE_PING "\":{\"" D_JSON_ZIGBEE_DEVICE "\":\"0x%04X\"}}"), nwkAddr);
|
||||
Response_P(PSTR("{\"" D_JSON_ZIGBEE_PING "\":{\"" D_JSON_ZIGBEE_DEVICE "\":\"0x%04X\""
|
||||
",\"" D_JSON_ZIGBEE_IEEE "\":\"0x%s\""
|
||||
"}}"), nwkAddr, hex);
|
||||
}
|
||||
|
||||
MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEEZCL_RECEIVED));
|
||||
|
@ -378,6 +381,27 @@ int32_t Z_ReceiveIEEEAddr(int32_t res, const class SBuffer &buf) {
|
|||
return -1;
|
||||
}
|
||||
|
||||
int32_t Z_BindRsp(int32_t res, const class SBuffer &buf) {
|
||||
Z_ShortAddress nwkAddr = buf.get16(2);
|
||||
uint8_t status = buf.get8(4);
|
||||
|
||||
const String * friendlyName = zigbee_devices.getFriendlyName(nwkAddr);
|
||||
if (friendlyName) {
|
||||
Response_P(PSTR("{\"" D_JSON_ZIGBEE_BIND "\":{\"" D_JSON_ZIGBEE_DEVICE "\":\"0x%04X\""
|
||||
",\"" D_JSON_ZIGBEE_NAME "\":\"%s\""
|
||||
",\"" D_JSON_ZIGBEE_Status "\":%d"
|
||||
"}}"), nwkAddr, friendlyName->c_str(), status);
|
||||
} else {
|
||||
Response_P(PSTR("{\"" D_JSON_ZIGBEE_BIND "\":{\"" D_JSON_ZIGBEE_DEVICE "\":\"0x%04X\""
|
||||
",\"" D_JSON_ZIGBEE_Status "\":%d"
|
||||
"}}"), nwkAddr, status);
|
||||
}
|
||||
MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEEZCL_RECEIVED));
|
||||
XdrvRulesProcess();
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int32_t Z_ReceiveEndDeviceAnnonce(int32_t res, const class SBuffer &buf) {
|
||||
Z_ShortAddress srcAddr = buf.get16(2);
|
||||
Z_ShortAddress nwkAddr = buf.get16(4);
|
||||
|
@ -480,36 +504,45 @@ int32_t Z_ReceiveAfIncomingMessage(int32_t res, const class SBuffer &buf) {
|
|||
DynamicJsonBuffer jsonBuffer;
|
||||
JsonObject& json = jsonBuffer.createObject();
|
||||
|
||||
if ( (!zcl_received.isClusterSpecificCommand()) && (ZCL_REPORT_ATTRIBUTES == zcl_received.getCmdId())) {
|
||||
zcl_received.parseRawAttributes(json);
|
||||
if (clusterid) { defer_attributes = true; } // don't defer system Cluster=0 messages
|
||||
} else if ( (!zcl_received.isClusterSpecificCommand()) && (ZCL_READ_ATTRIBUTES_RESPONSE == zcl_received.getCmdId())) {
|
||||
zcl_received.parseReadAttributes(json);
|
||||
} else if (zcl_received.isClusterSpecificCommand()) {
|
||||
zcl_received.parseClusterSpecificCommand(json);
|
||||
}
|
||||
String msg("");
|
||||
msg.reserve(100);
|
||||
json.printTo(msg);
|
||||
AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_ZIGBEE D_JSON_ZIGBEEZCL_RAW_RECEIVED ": {\"0x%04X\":%s}"), srcaddr, msg.c_str());
|
||||
|
||||
zcl_received.postProcessAttributes(srcaddr, json);
|
||||
// Add Endpoint
|
||||
json[F(D_CMND_ZIGBEE_ENDPOINT)] = srcendpoint;
|
||||
// Add linkquality
|
||||
json[F(D_CMND_ZIGBEE_LINKQUALITY)] = linkquality;
|
||||
|
||||
if (defer_attributes) {
|
||||
// Prepare for publish
|
||||
if (zigbee_devices.jsonIsConflict(srcaddr, json)) {
|
||||
// there is conflicting values, force a publish of the previous message now and don't coalesce
|
||||
zigbee_devices.jsonPublishFlush(srcaddr);
|
||||
if ( (!zcl_received.isClusterSpecificCommand()) && (ZCL_DEFAULT_RESPONSE == zcl_received.getCmdId())) {
|
||||
zcl_received.parseResponse();
|
||||
} else {
|
||||
// Build the ZbReceive json
|
||||
if ( (!zcl_received.isClusterSpecificCommand()) && (ZCL_REPORT_ATTRIBUTES == zcl_received.getCmdId())) {
|
||||
zcl_received.parseRawAttributes(json);
|
||||
if (clusterid) { defer_attributes = true; } // don't defer system Cluster=0 messages
|
||||
} else if ( (!zcl_received.isClusterSpecificCommand()) && (ZCL_READ_ATTRIBUTES_RESPONSE == zcl_received.getCmdId())) {
|
||||
zcl_received.parseReadAttributes(json);
|
||||
} else if (zcl_received.isClusterSpecificCommand()) {
|
||||
zcl_received.parseClusterSpecificCommand(json);
|
||||
}
|
||||
String msg("");
|
||||
msg.reserve(100);
|
||||
json.printTo(msg);
|
||||
AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_ZIGBEE D_JSON_ZIGBEEZCL_RAW_RECEIVED ": {\"0x%04X\":%s}"), srcaddr, msg.c_str());
|
||||
|
||||
zcl_received.postProcessAttributes(srcaddr, json);
|
||||
// Add Endpoint
|
||||
json[F(D_CMND_ZIGBEE_ENDPOINT)] = srcendpoint;
|
||||
// Add Group if non-zero
|
||||
if (groupid) {
|
||||
json[F(D_CMND_ZIGBEE_GROUP)] = groupid;
|
||||
}
|
||||
// Add linkquality
|
||||
json[F(D_CMND_ZIGBEE_LINKQUALITY)] = linkquality;
|
||||
|
||||
if (defer_attributes) {
|
||||
// Prepare for publish
|
||||
if (zigbee_devices.jsonIsConflict(srcaddr, json)) {
|
||||
// there is conflicting values, force a publish of the previous message now and don't coalesce
|
||||
zigbee_devices.jsonPublishFlush(srcaddr);
|
||||
}
|
||||
zigbee_devices.jsonAppend(srcaddr, json);
|
||||
zigbee_devices.setTimer(srcaddr, USE_ZIGBEE_COALESCE_ATTR_TIMER, clusterid, srcendpoint, 0, &Z_PublishAttributes);
|
||||
} else {
|
||||
// Publish immediately
|
||||
zigbee_devices.jsonPublishNow(srcaddr, json);
|
||||
}
|
||||
zigbee_devices.jsonAppend(srcaddr, json);
|
||||
zigbee_devices.setTimer(srcaddr, USE_ZIGBEE_COALESCE_ATTR_TIMER, clusterid, srcendpoint, 0, &Z_PublishAttributes);
|
||||
} else {
|
||||
// Publish immediately
|
||||
zigbee_devices.jsonPublishNow(srcaddr, json);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
@ -527,6 +560,7 @@ 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_SIMPLEDESCRSP, Z_AREQ | Z_ZDO, ZDO_SIMPLE_DESC_RSP) // 4584
|
||||
ZBM(AREQ_ZDO_IEEE_ADDR_RSP, Z_AREQ | Z_ZDO, ZDO_IEEE_ADDR_RSP) // 4581
|
||||
ZBM(AREQ_ZDO_BIND_RSP, Z_AREQ | Z_ZDO, ZDO_BIND_RSP) // 45A1
|
||||
|
||||
const Z_Dispatcher Z_DispatchTable[] PROGMEM = {
|
||||
{ AREQ_AF_INCOMING_MESSAGE, &Z_ReceiveAfIncomingMessage },
|
||||
|
@ -537,6 +571,7 @@ const Z_Dispatcher Z_DispatchTable[] PROGMEM = {
|
|||
{ AREQ_ZDO_ACTIVEEPRSP, &Z_ReceiveActiveEp },
|
||||
{ AREQ_ZDO_SIMPLEDESCRSP, &Z_ReceiveSimpleDesc },
|
||||
{ AREQ_ZDO_IEEE_ADDR_RSP, &Z_ReceiveIEEEAddr },
|
||||
{ AREQ_ZDO_BIND_RSP, &Z_BindRsp },
|
||||
};
|
||||
|
||||
int32_t Z_Recv_Default(int32_t res, const class SBuffer &buf) {
|
||||
|
|
|
@ -33,22 +33,16 @@ const char kZbCommands[] PROGMEM = D_PRFX_ZB "|" // prefix
|
|||
D_CMND_ZIGBEEZNPSEND "|" D_CMND_ZIGBEE_PERMITJOIN "|"
|
||||
D_CMND_ZIGBEE_STATUS "|" D_CMND_ZIGBEE_RESET "|" D_CMND_ZIGBEE_SEND "|"
|
||||
D_CMND_ZIGBEE_PROBE "|" D_CMND_ZIGBEE_READ "|" D_CMND_ZIGBEEZNPRECEIVE "|"
|
||||
D_CMND_ZIGBEE_FORGET "|" D_CMND_ZIGBEE_SAVE "|" D_CMND_ZIGBEE_NAME "|" D_CMND_ZIGBEE_BIND "|"
|
||||
D_CMND_ZIGBEE_PING ;
|
||||
|
||||
const char kZigbeeCommands[] PROGMEM = D_PRFX_ZIGBEE "|" // legacy prefix -- deprecated
|
||||
D_CMND_ZIGBEEZNPSEND "|" D_CMND_ZIGBEE_PERMITJOIN "|"
|
||||
D_CMND_ZIGBEE_STATUS "|" D_CMND_ZIGBEE_RESET "|" D_CMND_ZIGBEE_SEND "|"
|
||||
D_CMND_ZIGBEE_PROBE "|" D_CMND_ZIGBEE_READ "|" D_CMND_ZIGBEEZNPRECEIVE "|"
|
||||
D_CMND_ZIGBEE_FORGET "|" D_CMND_ZIGBEE_SAVE "|" D_CMND_ZIGBEE_NAME "|" D_CMND_ZIGBEE_BIND "|"
|
||||
D_CMND_ZIGBEE_PING ;
|
||||
D_CMND_ZIGBEE_FORGET "|" D_CMND_ZIGBEE_SAVE "|" D_CMND_ZIGBEE_NAME "|"
|
||||
D_CMND_ZIGBEE_BIND "|" D_CMND_ZIGBEE_PING "|" D_CMND_ZIGBEE_MODELID
|
||||
;
|
||||
|
||||
void (* const ZigbeeCommand[])(void) PROGMEM = {
|
||||
&CmndZbZNPSend, &CmndZbPermitJoin,
|
||||
&CmndZbStatus, &CmndZbReset, &CmndZbSend,
|
||||
&CmndZbProbe, &CmndZbRead, &CmndZbZNPReceive,
|
||||
&CmndZbForget, &CmndZbSave, &CmndZbName, &CmndZbBind,
|
||||
&CmndZbPing,
|
||||
&CmndZbForget, &CmndZbSave, &CmndZbName,
|
||||
&CmndZbBind, &CmndZbPing, &CmndZbModelId,
|
||||
};
|
||||
|
||||
int32_t ZigbeeProcessInput(class SBuffer &buf) {
|
||||
|
@ -547,35 +541,75 @@ void CmndZbBind(void) {
|
|||
|
||||
// params
|
||||
// static char delim[] = ", "; // delimiters for parameters
|
||||
uint16_t device = 0xFFFF; // 0xFFFF is broadcast, so considered valid
|
||||
uint8_t endpoint = 0x00; // 0x00 is invalid for the dst endpoint
|
||||
uint16_t srcDevice = 0xFFFF; // 0xFFFF is broadcast, so considered invalid
|
||||
uint16_t dstDevice = 0xFFFF; // 0xFFFF is broadcast, so considered invalid
|
||||
uint64_t dstLongAddr = 0;
|
||||
uint8_t endpoint = 0x00; // 0x00 is invalid for the src endpoint
|
||||
uint8_t toendpoint = 0x00; // 0x00 is invalid for the dst endpoint
|
||||
uint16_t toGroup = 0x0000; // group address
|
||||
uint16_t cluster = 0; // 0xFFFF is invalid
|
||||
uint32_t group = 0xFFFFFFFF; // 16 bits values, otherwise 0xFFFFFFFF is unspecified
|
||||
|
||||
// Information about source device: "Device", "Endpoint", "Cluster"
|
||||
// - the source endpoint must have a known IEEE address
|
||||
const JsonVariant &val_device = getCaseInsensitive(json, PSTR("Device"));
|
||||
if (nullptr != &val_device) {
|
||||
device = zigbee_devices.parseDeviceParam(val_device.as<char*>());
|
||||
if (0xFFFF == device) { ResponseCmndChar("Invalid parameter"); return; }
|
||||
srcDevice = zigbee_devices.parseDeviceParam(val_device.as<char*>());
|
||||
if (0xFFFF == srcDevice) { ResponseCmndChar("Invalid parameter"); return; }
|
||||
}
|
||||
if ((nullptr == &val_device) || (0x000 == device)) { ResponseCmndChar("Unknown device"); return; }
|
||||
|
||||
if ((nullptr == &val_device) || (0x0000 == srcDevice)) { ResponseCmndChar("Unknown source device"); return; }
|
||||
// check if IEEE address is known
|
||||
uint64_t srcLongAddr = zigbee_devices.getDeviceLongAddr(srcDevice);
|
||||
if (0 == srcLongAddr) { ResponseCmndChar("Unknown source IEEE address"); return; }
|
||||
// look for source endpoint
|
||||
const JsonVariant &val_endpoint = getCaseInsensitive(json, PSTR("Endpoint"));
|
||||
if (nullptr != &val_endpoint) { endpoint = strToUInt(val_endpoint); }
|
||||
// look for source cluster
|
||||
const JsonVariant &val_cluster = getCaseInsensitive(json, PSTR("Cluster"));
|
||||
if (nullptr != &val_cluster) { cluster = strToUInt(val_cluster); }
|
||||
|
||||
// TODO compute endpoint from cluster
|
||||
// Either Device address
|
||||
// In this case the following parameters are mandatory
|
||||
// - "ToDevice" and the device must have a known IEEE address
|
||||
// - "ToEndpoint"
|
||||
const JsonVariant &dst_device = getCaseInsensitive(json, PSTR("ToDevice"));
|
||||
if (nullptr != &dst_device) {
|
||||
dstDevice = zigbee_devices.parseDeviceParam(dst_device.as<char*>());
|
||||
if (0xFFFF == dstDevice) { ResponseCmndChar("Invalid parameter"); return; }
|
||||
if (0x0000 == dstDevice) {
|
||||
dstLongAddr = localIEEEAddr;
|
||||
} else {
|
||||
dstLongAddr = zigbee_devices.getDeviceLongAddr(dstDevice);
|
||||
}
|
||||
if (0 == dstLongAddr) { ResponseCmndChar("Unknown dest IEEE address"); return; }
|
||||
|
||||
const JsonVariant &val_toendpoint = getCaseInsensitive(json, PSTR("ToEndpoint"));
|
||||
if (nullptr != &val_toendpoint) { toendpoint = strToUInt(val_endpoint); } else { toendpoint = endpoint; }
|
||||
}
|
||||
|
||||
// Or Group Address - we don't need a dstEndpoint in this case
|
||||
const JsonVariant &to_group = getCaseInsensitive(json, PSTR("ToGroup"));
|
||||
if (nullptr != &to_group) { toGroup = strToUInt(to_group); }
|
||||
|
||||
// make sure we don't have conflicting parameters
|
||||
if (toGroup && dstLongAddr) { ResponseCmndChar("Cannot have both \"ToDevice\" and \"ToGroup\""); return; }
|
||||
if (!toGroup && !dstLongAddr) { ResponseCmndChar("Missing \"ToDevice\" or \"ToGroup\""); return; }
|
||||
|
||||
SBuffer buf(sizeof(ZBS_BIND_REQ));
|
||||
buf.add8(Z_SREQ | Z_ZDO);
|
||||
buf.add8(ZDO_BIND_REQ);
|
||||
buf.add16(device);
|
||||
buf.add64(zigbee_devices.getDeviceLongAddr(device));
|
||||
buf.add16(srcDevice);
|
||||
buf.add64(srcLongAddr);
|
||||
buf.add8(endpoint);
|
||||
buf.add16(cluster);
|
||||
buf.add8(0x03); // DstAddrMode - 0x03 = ADDRESS_64_BIT
|
||||
buf.add64(localIEEEAddr); // coordinatore IEEE address
|
||||
buf.add8(0x01); // local endpoint = 1
|
||||
if (dstLongAddr) {
|
||||
buf.add8(Z_Addr_IEEEAddress); // DstAddrMode - 0x03 = ADDRESS_64_BIT
|
||||
buf.add64(dstLongAddr);
|
||||
buf.add8(toendpoint);
|
||||
} else {
|
||||
buf.add8(Z_Addr_Group); // DstAddrMode - 0x01 = GROUP_ADDRESS
|
||||
buf.add16(toGroup);
|
||||
}
|
||||
|
||||
ZigbeeZNPSend(buf.getBuffer(), buf.len());
|
||||
|
||||
|
@ -635,6 +669,35 @@ void CmndZbName(void) {
|
|||
}
|
||||
}
|
||||
|
||||
// Specify, read or erase a ModelId, only for debug purposes
|
||||
void CmndZbModelId(void) {
|
||||
// Syntax is:
|
||||
// ZigbeeName <device_id>,<friendlyname> - assign a friendly name
|
||||
// ZigbeeName <device_id> - display the current friendly name
|
||||
// ZigbeeName <device_id>, - remove friendly name
|
||||
//
|
||||
// Where <device_id> can be: short_addr, long_addr, device_index, friendly_name
|
||||
|
||||
if (zigbee.init_phase) { ResponseCmndChar(D_ZIGBEE_NOT_STARTED); return; }
|
||||
|
||||
// check if parameters contain a comma ','
|
||||
char *p;
|
||||
char *str = strtok_r(XdrvMailbox.data, ", ", &p);
|
||||
|
||||
// parse first part, <device_id>
|
||||
uint16_t shortaddr = zigbee_devices.parseDeviceParam(XdrvMailbox.data, true); // in case of short_addr, it must be already registered
|
||||
if (0x0000 == shortaddr) { ResponseCmndChar("Unknown device"); return; }
|
||||
if (0xFFFF == shortaddr) { ResponseCmndChar("Invalid parameter"); return; }
|
||||
|
||||
if (p == nullptr) {
|
||||
const String * modelId = zigbee_devices.getModelId(shortaddr);
|
||||
Response_P(PSTR("{\"0x%04X\":{\"" D_JSON_ZIGBEE_MODELID "\":\"%s\"}}"), shortaddr, modelId ? modelId->c_str() : "");
|
||||
} else {
|
||||
zigbee_devices.setModelId(shortaddr, p);
|
||||
Response_P(PSTR("{\"0x%04X\":{\"" D_JSON_ZIGBEE_MODELID "\":\"%s\"}}"), shortaddr, p);
|
||||
}
|
||||
}
|
||||
|
||||
// Remove an old Zigbee device from the list of known devices, use ZigbeeStatus to know all registered devices
|
||||
void CmndZbForget(void) {
|
||||
if (zigbee.init_phase) { ResponseCmndChar(D_ZIGBEE_NOT_STARTED); return; }
|
||||
|
@ -775,7 +838,6 @@ bool Xdrv23(uint8_t function)
|
|||
case FUNC_LOOP:
|
||||
if (ZigbeeSerial) { ZigbeeInput(); }
|
||||
if (zigbee.state_machine) {
|
||||
//ZigbeeStateMachine();
|
||||
ZigbeeStateMachine_Run();
|
||||
}
|
||||
break;
|
||||
|
@ -784,7 +846,6 @@ bool Xdrv23(uint8_t function)
|
|||
break;
|
||||
case FUNC_COMMAND:
|
||||
result = DecodeCommand(kZbCommands, ZigbeeCommand);
|
||||
result = result || DecodeCommand(kZigbeeCommands, ZigbeeCommand); // deprecated
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue