Code optimization, cleaning and more error codes

This commit is contained in:
Stephan Hadinger 2020-03-23 22:46:26 +01:00
parent 0a573bd0d6
commit ac4d4ac571
7 changed files with 406 additions and 345 deletions

View File

@ -112,7 +112,7 @@ public:
}
size_t addBuffer(const uint8_t *buf2, size_t len2) {
if (len() + len2 <= size()) {
if ((buf2) && (len() + len2 <= size())) {
for (uint32_t i = 0; i < len2; i++) {
_buf->buf[_buf->len++] = pgm_read_byte(&buf2[i]);
}
@ -121,7 +121,7 @@ public:
}
size_t addBuffer(const char *buf2, size_t len2) {
if (len() + len2 <= size()) {
if ((buf2) && (len() + len2 <= size())) {
for (uint32_t i = 0; i < len2; i++) {
_buf->buf[_buf->len++] = pgm_read_byte(&buf2[i]);
}

View File

@ -393,88 +393,36 @@ typedef struct Z_StatusLine {
const char * status_msg;
} Z_StatusLine;
ZF(SUCCESS)
ZF(FAILURE)
ZF(NOT_AUTHORIZED)
ZF(RESERVED_FIELD_NOT_ZERO)
ZF(MALFORMED_COMMAND)
ZF(UNSUP_CLUSTER_COMMAND)
ZF(UNSUP_GENERAL_COMMAND)
ZF(UNSUP_MANUF_CLUSTER_COMMAND)
ZF(UNSUP_MANUF_GENERAL_COMMAND)
ZF(INVALID_FIELD)
ZF(UNSUPPORTED_ATTRIBUTE)
ZF(INVALID_VALUE)
ZF(READ_ONLY)
ZF(INSUFFICIENT_SPACE)
ZF(DUPLICATE_EXISTS)
ZF(NOT_FOUND)
ZF(UNREPORTABLE_ATTRIBUTE)
ZF(INVALID_DATA_TYPE)
ZF(INVALID_SELECTOR)
ZF(WRITE_ONLY)
ZF(INCONSISTENT_STARTUP_STATE)
ZF(DEFINED_OUT_OF_BAND)
ZF(INCONSISTENT)
ZF(ACTION_DENIED)
ZF(TIMEOUT)
ZF(ABORT)
ZF(INVALID_IMAGE)
ZF(WAIT_FOR_DATA)
ZF(NO_IMAGE_AVAILABLE)
ZF(REQUIRE_MORE_IMAGE)
ZF(NOTIFICATION_PENDING)
ZF(HARDWARE_FAILURE)
ZF(SOFTWARE_FAILURE)
ZF(CALIBRATION_ERROR)
ZF(UNSUPPORTED_CLUSTER)
// Undocumented Zigbee ZCL code here: https://github.com/dresden-elektronik/deconz-rest-plugin/wiki/Zigbee-Error-Codes-in-the-Log
String getZigbeeStatusMessage(uint8_t status) {
static const char StatusMsg[] PROGMEM = "SUCCESS|FAILURE|NOT_AUTHORIZED|RESERVED_FIELD_NOT_ZERO|MALFORMED_COMMAND|UNSUP_CLUSTER_COMMAND|UNSUP_GENERAL_COMMAND"
"|UNSUP_MANUF_CLUSTER_COMMAND|UNSUP_MANUF_GENERAL_COMMAND|INVALID_FIELD|UNSUPPORTED_ATTRIBUTE|INVALID_VALE|READ_ONLY"
"|INSUFFICIENT_SPACE|DUPLICATE_EXISTS|NOT_FOUND|UNREPORTABLE_ATTRIBUTE|INVALID_DATA_TYPE|INVALID_SELECTOR|WRITE_ONLY"
"|INCONSISTENT_STARTUP_STATE|DEFINED_OUT_OF_BAND|INCONSISTENT|ACTION_DENIED|TIMEOUT|ABORT|INVALID_IMAGE|WAIT_FOR_DATA"
"|NO_IMAGE_AVAILABLE|REQUIRE_MORE_IMAGE|NOTIFICATION_PENDING|HARDWARE_FAILURE|SOFTWARE_FAILURE|CALIBRATION_ERROR|UNSUPPORTED_CLUSTER"
"|CHANNEL_ACCESS_FAILURE|NO_ACK|NO_APP_ACK|NO_ROUTE"
;
static const uint8_t StatusIdx[] PROGMEM = { 0x00, 0x01, 0x7E, 0x7F, 0x80, 0x81, 0x82,
0x83, 0x84, 0x85, 0x86, 0x87, 0x88,
0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
0x98, 0x99, 0x9A, 0xC0, 0xC1, 0xC2, 0xC3,
0xE1, 0xE9, 0xA7, 0xD0};
const Z_StatusLine Z_Status[] PROGMEM = {
0x00, Z(SUCCESS),
0x01, Z(FAILURE),
0x7E, Z(NOT_AUTHORIZED),
0x7F, Z(RESERVED_FIELD_NOT_ZERO),
0x80, Z(MALFORMED_COMMAND),
0x81, Z(UNSUP_CLUSTER_COMMAND),
0x82, Z(UNSUP_GENERAL_COMMAND),
0x83, Z(UNSUP_MANUF_CLUSTER_COMMAND),
0x84, Z(UNSUP_MANUF_GENERAL_COMMAND),
0x85, Z(INVALID_FIELD),
0x86, Z(UNSUPPORTED_ATTRIBUTE),
0x87, Z(INVALID_VALUE),
0x88, Z(READ_ONLY),
0x89, Z(INSUFFICIENT_SPACE),
0x8A, Z(DUPLICATE_EXISTS),
0x8B, Z(NOT_FOUND),
0x8C, Z(UNREPORTABLE_ATTRIBUTE),
0x8D, Z(INVALID_DATA_TYPE),
0x8E, Z(INVALID_SELECTOR),
0x8F, Z(WRITE_ONLY),
0x90, Z(INCONSISTENT_STARTUP_STATE),
0x91, Z(DEFINED_OUT_OF_BAND),
0x92, Z(INCONSISTENT),
0x93, Z(ACTION_DENIED),
0x94, Z(TIMEOUT),
0x95, Z(ABORT),
0x96, Z(INVALID_IMAGE),
0x97, Z(WAIT_FOR_DATA),
0x98, Z(NO_IMAGE_AVAILABLE),
0x99, Z(REQUIRE_MORE_IMAGE),
0x9A, Z(NOTIFICATION_PENDING),
0xC0, Z(HARDWARE_FAILURE),
0xC1, Z(SOFTWARE_FAILURE),
0xC2, Z(CALIBRATION_ERROR),
0xC3, Z(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;
char msg[32];
int32_t idx = -1;
for (uint32_t i = 0; i < sizeof(StatusIdx); i++) {
if (status == pgm_read_byte(&StatusIdx[i])) {
idx = i;
break;
}
}
return F("");
if (idx >= 0) {
GetTextIndexed(msg, sizeof(msg), idx, StatusMsg);
} else {
*msg = 0x00; // empty string
}
return String(msg);
}
#endif // USE_ZIGBEE

View File

@ -26,7 +26,9 @@
#endif
const uint16_t kZigbeeSaveDelaySeconds = ZIGBEE_SAVE_DELAY_SECONDS; // wait for x seconds
typedef int32_t (*Z_DeviceTimer)(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster, uint8_t endpoint, uint32_t value);
/*********************************************************************************************\
* Structures for device configuration
\*********************************************************************************************/
const size_t endpoints_max = 8; // we limit to 8 endpoints
@ -53,6 +55,12 @@ typedef struct Z_Device {
uint16_t x, y; // last color [x,y]
} Z_Device;
/*********************************************************************************************\
* Structures for deferred callbacks
\*********************************************************************************************/
typedef int32_t (*Z_DeviceTimer)(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster, uint8_t endpoint, uint32_t value);
// Category for Deferred actions, this allows to selectively remove active deferred or update them
typedef enum Z_Def_Category {
Z_CAT_NONE = 0, // no category, it will happen anyways
@ -76,6 +84,10 @@ typedef struct Z_Deferred {
Z_DeviceTimer func; // function to call when timer occurs
} Z_Deferred;
/*********************************************************************************************\
* Singleton for device configuration
\*********************************************************************************************/
// All devices are stored in a Vector
// Invariants:
// - shortaddr is unique if not null
@ -190,13 +202,22 @@ private:
// Create a new entry in the devices list - must be called if it is sure it does not already exist
Z_Device & createDeviceEntry(uint16_t shortaddr, uint64_t longaddr = 0);
void freeDeviceEntry(Z_Device *device);
void setStringAttribute(char*& attr, const char * str);
};
/*********************************************************************************************\
* Singleton variable
\*********************************************************************************************/
Z_Devices zigbee_devices = Z_Devices();
// Local coordinator information
uint64_t localIEEEAddr = 0;
/*********************************************************************************************\
* Implementation
\*********************************************************************************************/
// https://thispointer.com/c-how-to-find-an-element-in-vector-and-get-its-index/
template < typename T>
bool Z_Devices::findInVector(const std::vector<T> & vecOfElements, const T & element) {
@ -493,73 +514,55 @@ uint8_t Z_Devices::findFirstEndpoint(uint16_t shortaddr) const {
return device.endpoints[0]; // returns 0x00 if no endpoint
}
void Z_Devices::setManufId(uint16_t shortaddr, const char * str) {
Z_Device & device = getShortAddr(shortaddr);
if (&device == nullptr) { return; } // don't crash if not found
void Z_Devices::setStringAttribute(char*& attr, const char * str) {
size_t str_len = str ? strlen(str) : 0; // len, handle both null ptr and zero length string
if ((!device.manufacturerId) && (0 == str_len)) { return; } // if both empty, don't do anything
if (device.manufacturerId) {
if ((nullptr == attr) && (0 == str_len)) { return; } // if both empty, don't do anything
if (attr) {
// we already have a value
if (strcmp(device.manufacturerId, str) != 0) {
if (strcmp(attr, str) != 0) {
// new value
free(device.manufacturerId); // free previous value
device.manufacturerId = nullptr;
free(attr); // free previous value
attr = nullptr;
} else {
return; // same value, don't change anything
}
}
if (str_len) {
device.manufacturerId = (char*) malloc(str_len + 1);
strlcpy(device.manufacturerId, str, str_len + 1);
attr = (char*) malloc(str_len + 1);
strlcpy(attr, str, str_len + 1);
}
dirty();
}
//
// Sets the ManufId for a device.
// No action taken if the device does not exist.
// Inputs:
// - shortaddr: 16-bits short address of the device. No action taken if the device is unknown
// - str: string pointer, if nullptr it is considered as empty string
// Impact:
// - Any actual change in ManufId (i.e. setting a different value) triggers a `dirty()` and saving to Flash
//
void Z_Devices::setManufId(uint16_t shortaddr, const char * str) {
Z_Device & device = getShortAddr(shortaddr);
if (&device == nullptr) { return; } // don't crash if not found
setStringAttribute(device.manufacturerId, str);
}
void Z_Devices::setModelId(uint16_t shortaddr, const char * str) {
Z_Device & device = getShortAddr(shortaddr);
if (&device == nullptr) { return; } // don't crash if not found
size_t str_len = str ? strlen(str) : 0; // len, handle both null ptr and zero length string
if ((!device.modelId) && (0 == str_len)) { return; } // if both empty, don't do anything
if (device.modelId) {
// we already have a value
if (strcmp(device.modelId, str) != 0) {
// new value
free(device.modelId); // free previous value
device.modelId = nullptr;
} else {
return; // same value, don't change anything
}
}
if (str_len) {
device.modelId = (char*) malloc(str_len + 1);
strlcpy(device.modelId, str, str_len + 1);
}
dirty();
setStringAttribute(device.modelId, str);
}
void Z_Devices::setFriendlyName(uint16_t shortaddr, const char * str) {
Z_Device & device = getShortAddr(shortaddr);
if (&device == nullptr) { return; } // don't crash if not found
size_t str_len = str ? strlen(str) : 0; // len, handle both null ptr and zero length string
if ((!device.friendlyName) && (0 == str_len)) { return; } // if both empty, don't do anything
if (device.friendlyName) {
// we already have a value
if (strcmp(device.friendlyName, str) != 0) {
// new value
free(device.friendlyName); // free previous value
device.friendlyName = nullptr;
} else {
return; // same value, don't change anything
}
}
if (str_len) {
device.friendlyName = (char*) malloc(str_len + 1);
strlcpy(device.friendlyName, str, str_len + 1);
}
dirty();
setStringAttribute(device.friendlyName, str);
}
const char * Z_Devices::getFriendlyName(uint16_t shortaddr) const {

View File

@ -19,6 +19,10 @@
#ifdef USE_ZIGBEE
/*********************************************************************************************\
* ZCL Command Structures
\*********************************************************************************************/
typedef struct Z_CommandConverter {
const char * tasmota_cmd;
uint16_t cluster;
@ -130,6 +134,9 @@ const Z_CommandConverter Z_Commands[] PROGMEM = {
{ Z(GetSceneMembership),0x0005, 0x06, 0x82,Z(xxyyzzzz) }, // specific
};
/*********************************************************************************************\
* ZCL Read Light status based on cluster number
\*********************************************************************************************/
#define ZLE(x) ((x) & 0xFF), ((x) >> 8) // Little Endian
// Below are the attributes we wand to read from each cluster

View File

@ -20,7 +20,7 @@
#ifdef USE_ZIGBEE
// Status code used for ZigbeeStatus MQTT message
// Ex: {"ZigbeeStatus":{"Status": 3,"Message":"Configured, starting coordinator"}}
// Ex: {"ZbStatus":{"Status": 3,"Message":"Configured, starting coordinator"}}
const uint8_t ZIGBEE_STATUS_OK = 0; // Zigbee started and working
const uint8_t ZIGBEE_STATUS_BOOT = 1; // CC2530 booting
const uint8_t ZIGBEE_STATUS_RESET_CONF = 2; // Resetting CC2530 configuration
@ -33,7 +33,6 @@ 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_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
@ -50,9 +49,6 @@ typedef union Zigbee_Instruction {
} i;
const void *p; // pointer
} Zigbee_Instruction;
//
// Zigbee_Instruction z1 = { .i = {1,2,3}};
// Zigbee_Instruction z3 = { .p = nullptr };
typedef struct Zigbee_Instruction_Type {
uint8_t instr;
@ -488,7 +484,6 @@ void ZigbeeStateMachine_Run(void) {
if (zigbee.state_waiting) { // state machine is waiting for external event or timeout
// checking if timeout expired
if ((zigbee.next_timeout) && (now > zigbee.next_timeout)) { // if next_timeout == 0 then wait forever
//AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "timeout occured pc=%d"), zigbee.pc);
if (!zigbee.state_no_timeout) {
AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "timeout, goto label %d"), zigbee.on_timeout_goto);
ZigbeeGotoLabel(zigbee.on_timeout_goto);
@ -505,7 +500,6 @@ void ZigbeeStateMachine_Run(void) {
zigbee.recv_until = false;
zigbee.state_no_timeout = false; // reset the no_timeout for next instruction
// AddLog_P2(LOG_LEVEL_INFO, PSTR("ZigbeeStateMachine_Run PC = %d, Mem1 = %d"), zigbee.pc, ESP.getFreeHeap());
if (zigbee.pc > (sizeof(zb_prog)/sizeof(zb_prog[0]))) {
AddLog_P2(LOG_LEVEL_ERROR, PSTR(D_LOG_ZIGBEE "Invalid pc: %d, aborting"), zigbee.pc);
zigbee.pc = -1;
@ -516,7 +510,6 @@ void ZigbeeStateMachine_Run(void) {
}
// load current instruction details
//AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_ZIGBEE "Executing instruction pc=%d"), zigbee.pc);
const Zigbee_Instruction *cur_instr_line = &zb_prog[zigbee.pc];
cur_instr = pgm_read_byte(&cur_instr_line->i.i);
cur_d8 = pgm_read_byte(&cur_instr_line->i.d8);
@ -553,7 +546,6 @@ void ZigbeeStateMachine_Run(void) {
case ZGB_INSTR_WAIT_FOREVER:
zigbee.next_timeout = 0;
zigbee.state_waiting = true;
//zigbee.state_no_timeout = true; // do not generate a timeout error when waiting is done
break;
case ZGB_INSTR_STOP:
zigbee.state_machine = false;
@ -617,4 +609,82 @@ void ZigbeeStateMachine_Run(void) {
}
}
//
// Process a bytes buffer and call any callback that matches the received message
//
int32_t ZigbeeProcessInput(class SBuffer &buf) {
if (!zigbee.state_machine) { return -1; } // if state machine is stopped, send 'ignore' message
// apply the receive filter, acts as 'startsWith()'
bool recv_filter_match = true;
bool recv_prefix_match = false; // do the first 2 bytes match the response
if ((zigbee.recv_filter) && (zigbee.recv_filter_len > 0)) {
if (zigbee.recv_filter_len >= 2) {
recv_prefix_match = false;
if ( (pgm_read_byte(&zigbee.recv_filter[0]) == buf.get8(0)) &&
(pgm_read_byte(&zigbee.recv_filter[1]) == buf.get8(1)) ) {
recv_prefix_match = true;
}
}
for (uint32_t i = 0; i < zigbee.recv_filter_len; i++) {
if (pgm_read_byte(&zigbee.recv_filter[i]) != buf.get8(i)) {
recv_filter_match = false;
break;
}
}
}
// if there is a recv_callback, call it now
int32_t res = -1; // default to ok
// res = 0 - proceed to next state
// res > 0 - proceed to the specified state
// res = -1 - silently ignore the message
// res <= -2 - move to error state
// pre-compute the suggested value
if ((zigbee.recv_filter) && (zigbee.recv_filter_len > 0)) {
if (!recv_prefix_match) {
res = -1; // ignore
} else { // recv_prefix_match
if (recv_filter_match) {
res = 0; // ok
} else {
if (zigbee.recv_until) {
res = -1; // ignore until full match
} else {
res = -2; // error, because message is expected but wrong value
}
}
}
} else { // we don't have any filter, ignore message by default
res = -1;
}
if (recv_prefix_match) {
if (zigbee.recv_func) {
res = (*zigbee.recv_func)(res, buf);
}
}
if (-1 == res) {
// if frame was ignored up to now
if (zigbee.recv_unexpected) {
res = (*zigbee.recv_unexpected)(res, buf);
}
}
// change state accordingly
if (0 == res) {
// if ok, continue execution
zigbee.state_waiting = false;
} else if (res > 0) {
ZigbeeGotoLabel(res); // if >0 then go to specified label
} else if (-1 == res) {
// -1 means ignore message
// just do nothing
} else {
// any other negative value means error
ZigbeeGotoLabel(zigbee.on_error_goto);
}
}
#endif // USE_ZIGBEE

View File

@ -19,6 +19,13 @@
#ifdef USE_ZIGBEE
/*********************************************************************************************\
* Parsers for incoming ZNP messages
\*********************************************************************************************/
//
// Handle a "Receive Device Info" incoming message
//
int32_t Z_ReceiveDeviceInfo(int32_t res, class SBuffer &buf) {
// Ex= 6700.00.6263151D004B1200.0000.07.09.02.83869991
// IEEE Adr (8 bytes) = 0x00124B001D156362
@ -76,12 +83,12 @@ int32_t Z_CheckNVWrite(int32_t res, class SBuffer &buf) {
}
}
const char Z_RebootReason[] PROGMEM = "Power-up|External|Watchdog";
int32_t Z_Reboot(int32_t res, class SBuffer &buf) {
// print information about the reboot of device
// 4180.02.02.00.02.06.03
//
static const char Z_RebootReason[] PROGMEM = "Power-up|External|Watchdog";
uint8_t reason = buf.get8(2);
uint8_t transport_rev = buf.get8(3);
uint8_t product_id = buf.get8(4);
@ -140,6 +147,9 @@ int32_t Z_ReceiveCheckVersion(int32_t res, class SBuffer &buf) {
}
}
//
// Helper function, checks if the incoming buffer matches the 2-bytes prefix, i.e. message type in PMEM
//
bool Z_ReceiveMatchPrefix(const class SBuffer &buf, const uint8_t *match) {
if ( (pgm_read_byte(&match[0]) == buf.get8(0)) &&
(pgm_read_byte(&match[1]) == buf.get8(1)) ) {
@ -149,6 +159,9 @@ bool Z_ReceiveMatchPrefix(const class SBuffer &buf, const uint8_t *match) {
}
}
//
// Handle Permit Join response
//
int32_t Z_ReceivePermitJoinStatus(int32_t res, const class SBuffer &buf) {
// we received a PermitJoin status change
uint8_t duration = buf.get8(2);
@ -176,22 +189,6 @@ int32_t Z_ReceivePermitJoinStatus(int32_t res, const class SBuffer &buf) {
return -1;
}
// Send ZDO_IEEE_ADDR_REQ request to get IEEE long address
void Z_SendIEEEAddrReq(uint16_t shortaddr) {
uint8_t IEEEAddrReq[] = { Z_SREQ | Z_ZDO, ZDO_IEEE_ADDR_REQ,
Z_B0(shortaddr), Z_B1(shortaddr), 0x00, 0x00 };
ZigbeeZNPSend(IEEEAddrReq, sizeof(IEEEAddrReq));
}
// Send ACTIVE_EP_REQ to collect active endpoints for this address
void Z_SendActiveEpReq(uint16_t shortaddr) {
uint8_t ActiveEpReq[] = { Z_SREQ | Z_ZDO, ZDO_ACTIVE_EP_REQ,
Z_B0(shortaddr), Z_B1(shortaddr), Z_B0(shortaddr), Z_B1(shortaddr) };
ZigbeeZNPSend(ActiveEpReq, sizeof(ActiveEpReq));
}
const char* Z_DeviceType[] = { "Coordinator", "Router", "End Device", "Unknown" };
int32_t Z_ReceiveNodeDesc(int32_t res, const class SBuffer &buf) {
// Received ZDO_NODE_DESC_RSP
@ -226,6 +223,9 @@ int32_t Z_ReceiveNodeDesc(int32_t res, const class SBuffer &buf) {
return -1;
}
//
// Porcess Receive Active Endpoint
//
int32_t Z_ReceiveActiveEp(int32_t res, const class SBuffer &buf) {
// Received ZDO_ACTIVE_EP_RSP
Z_ShortAddress srcAddr = buf.get16(2);
@ -254,37 +254,14 @@ int32_t Z_ReceiveActiveEp(int32_t res, const class SBuffer &buf) {
return -1;
}
void Z_SendAFInfoRequest(uint16_t shortaddr) {
uint8_t endpoint = zigbee_devices.findFirstEndpoint(shortaddr);
if (0x00 == endpoint) { endpoint = 0x01; } // if we don't know the endpoint, try 0x01
uint8_t transacid = zigbee_devices.getNextSeqNumber(shortaddr);
SBuffer buf(100);
buf.add8(Z_SREQ | Z_AF); // 24
buf.add8(AF_DATA_REQUEST); // 01
buf.add16(shortaddr);
buf.add8(endpoint); // dest endpoint
buf.add8(0x01); // source endpoint
buf.add16(0x0000);
buf.add8(transacid);
buf.add8(0x30); // 30 options
buf.add8(0x1E); // 1E radius
buf.add8(3 + 2*sizeof(uint16_t)); // Len = 0x07
buf.add8(0x00); // Frame Control Field
buf.add8(transacid); // Transaction Sequence Number
buf.add8(ZCL_READ_ATTRIBUTES); // 00 Command
buf.add16(0x0004); // 0400 ManufacturerName
buf.add16(0x0005); // 0500 ModelIdentifier
ZigbeeZNPSend(buf.getBuffer(), buf.len());
}
//
// Handle IEEEAddr incoming message
//
int32_t Z_ReceiveIEEEAddr(int32_t res, const class SBuffer &buf) {
uint8_t status = buf.get8(2);
Z_IEEEAddress ieeeAddr = buf.get64(3);
Z_ShortAddress nwkAddr = buf.get16(11);
// uint8_t startIndex = buf.get8(13);
// uint8_t startIndex = buf.get8(13); // not used
// uint8_t numAssocDev = buf.get8(14);
if (0 == status) { // SUCCESS
@ -308,34 +285,6 @@ 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);
char status_message[32];
strncpy_P(status_message, (const char*) getZigbeeStatusMessage(status), sizeof(status_message));
status_message[sizeof(status_message)-1] = 0; // truncate if needed, strlcpy is safer but strlcpy_P does not exist
const char * 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"
",\"" D_JSON_ZIGBEE_STATUS_MSG "\":\"%s\""
"}}"), nwkAddr, friendlyName, status, status_message);
} else {
Response_P(PSTR("{\"" D_JSON_ZIGBEE_BIND "\":{\"" D_JSON_ZIGBEE_DEVICE "\":\"0x%04X\""
",\"" D_JSON_ZIGBEE_STATUS "\":%d"
",\"" D_JSON_ZIGBEE_STATUS_MSG "\":\"%s\""
"}}"), nwkAddr, status, status_message);
}
MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEEZCL_RECEIVED));
XdrvRulesProcess();
return -1;
}
//
// Report any AF_DATA_CONFIRM message
// Ex: {"ZbConfirm":{"Endpoint":1,"Status":0,"StatusMessage":"SUCCESS"}}
@ -343,17 +292,13 @@ int32_t Z_BindRsp(int32_t res, const class SBuffer &buf) {
int32_t Z_DataConfirm(int32_t res, const class SBuffer &buf) {
uint8_t status = buf.get8(2);
uint8_t endpoint = buf.get8(3);
//uint8_t transId = buf.get8(4);
char status_message[32];
//uint8_t transId = buf.get8(4); // unused
if (status) { // only report errors
strncpy_P(status_message, (const char*) getZigbeeStatusMessage(status), sizeof(status_message));
status_message[sizeof(status_message)-1] = 0; // truncate if needed, strlcpy is safer but strlcpy_P does not exist
Response_P(PSTR("{\"" D_JSON_ZIGBEE_CONFIRM "\":{\"" D_CMND_ZIGBEE_ENDPOINT "\":%d"
",\"" D_JSON_ZIGBEE_STATUS "\":%d"
",\"" D_JSON_ZIGBEE_STATUS_MSG "\":\"%s\""
"}}"), endpoint, status, status_message);
"}}"), endpoint, status, getZigbeeStatusMessage(status).c_str());
MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEEZCL_RECEIVED));
XdrvRulesProcess();
}
@ -361,6 +306,10 @@ int32_t Z_DataConfirm(int32_t res, const class SBuffer &buf) {
return -1;
}
//
// Handle Receive End Device Announce incoming message
// Send back Active Ep Req message
//
int32_t Z_ReceiveEndDeviceAnnonce(int32_t res, const class SBuffer &buf) {
Z_ShortAddress srcAddr = buf.get16(2);
Z_ShortAddress nwkAddr = buf.get16(4);
@ -386,7 +335,10 @@ int32_t Z_ReceiveEndDeviceAnnonce(int32_t res, const class SBuffer &buf) {
return -1;
}
//
// Handle Receive TC Dev Ind incoming message
// 45CA
//
int32_t Z_ReceiveTCDevInd(int32_t res, const class SBuffer &buf) {
Z_ShortAddress srcAddr = buf.get16(2);
Z_IEEEAddress ieeeAddr = buf.get64(4);
@ -404,15 +356,82 @@ int32_t Z_ReceiveTCDevInd(int32_t res, const class SBuffer &buf) {
MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEEZCL_RECEIVED));
XdrvRulesProcess();
//Z_SendActiveEpReq(srcAddr);
return -1;
}
//
// Handle Bind Rsp incoming message
//
int32_t Z_BindRsp(int32_t res, const class SBuffer &buf) {
Z_ShortAddress nwkAddr = buf.get16(2);
uint8_t status = buf.get8(4);
const char * 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"
",\"" D_JSON_ZIGBEE_STATUS_MSG "\":\"%s\""
"}}"), nwkAddr, friendlyName, status, getZigbeeStatusMessage(status).c_str());
} else {
Response_P(PSTR("{\"" D_JSON_ZIGBEE_BIND "\":{\"" D_JSON_ZIGBEE_DEVICE "\":\"0x%04X\""
",\"" D_JSON_ZIGBEE_STATUS "\":%d"
",\"" D_JSON_ZIGBEE_STATUS_MSG "\":\"%s\""
"}}"), nwkAddr, status, getZigbeeStatusMessage(status).c_str());
}
MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEEZCL_RECEIVED));
XdrvRulesProcess();
return -1;
}
/*********************************************************************************************\
* Send specific ZNP messages
\*********************************************************************************************/
//
// Send ZDO_IEEE_ADDR_REQ request to get IEEE long address
//
void Z_SendIEEEAddrReq(uint16_t shortaddr) {
uint8_t IEEEAddrReq[] = { Z_SREQ | Z_ZDO, ZDO_IEEE_ADDR_REQ, Z_B0(shortaddr), Z_B1(shortaddr), 0x00, 0x00 };
ZigbeeZNPSend(IEEEAddrReq, sizeof(IEEEAddrReq));
}
//
// Send ACTIVE_EP_REQ to collect active endpoints for this address
//
void Z_SendActiveEpReq(uint16_t shortaddr) {
uint8_t ActiveEpReq[] = { Z_SREQ | Z_ZDO, ZDO_ACTIVE_EP_REQ, Z_B0(shortaddr), Z_B1(shortaddr), Z_B0(shortaddr), Z_B1(shortaddr) };
ZigbeeZNPSend(ActiveEpReq, sizeof(ActiveEpReq));
}
//
// Send AF Info Request
//
void Z_SendAFInfoRequest(uint16_t shortaddr) {
uint8_t endpoint = zigbee_devices.findFirstEndpoint(shortaddr);
if (0x00 == endpoint) { endpoint = 0x01; } // if we don't know the endpoint, try 0x01
uint8_t transacid = zigbee_devices.getNextSeqNumber(shortaddr);
uint8_t AFInfoReq[] = { Z_SREQ | Z_AF, AF_DATA_REQUEST, Z_B0(shortaddr), Z_B1(shortaddr), endpoint,
0x01, 0x00, 0x00, transacid, 0x30, 0x1E, 3 + 2*sizeof(uint16_t),
0x00, transacid, ZCL_READ_ATTRIBUTES, 0x04, 0x00, 0x05, 0x00
};
ZigbeeZNPSend(AFInfoReq, sizeof(AFInfoReq));
}
/*********************************************************************************************\
* Callbacks
\*********************************************************************************************/
// 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
const uint32_t OCCUPANCY_TIMEOUT = 90 * 1000; // 90 s
void Z_AqaraOccupancy(uint16_t shortaddr, uint16_t cluster, uint8_t endpoint, const JsonObject *json) {
static const uint32_t OCCUPANCY_TIMEOUT = 90 * 1000; // 90 s
// Read OCCUPANCY value if any
const JsonVariant &val_endpoint = getCaseInsensitive(*json, PSTR(OCCUPANCY));
if (nullptr != &val_endpoint) {
@ -436,6 +455,10 @@ int32_t Z_PublishAttributes(uint16_t shortaddr, uint16_t groupaddr, uint16_t clu
return 1;
}
/*********************************************************************************************\
* Global dispatcher for incoming messages
\*********************************************************************************************/
int32_t Z_ReceiveAfIncomingMessage(int32_t res, const class SBuffer &buf) {
uint16_t groupid = buf.get16(2);
uint16_t clusterid = buf.get16(4);
@ -506,22 +529,24 @@ int32_t Z_ReceiveAfIncomingMessage(int32_t res, const class SBuffer &buf) {
return -1;
}
// Structure for the Dispatcher callbacks table
typedef struct Z_Dispatcher {
const uint8_t* match;
ZB_RecvMsgFunc func;
} Z_Dispatcher;
// Filters for ZCL frames
ZBM(AREQ_AF_DATA_CONFIRM, Z_AREQ | Z_AF, AF_DATA_CONFIRM) // 4480
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_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_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
// Ffilters based on ZNP frames
ZBM(AREQ_AF_DATA_CONFIRM, Z_AREQ | Z_AF, AF_DATA_CONFIRM) // 4480
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_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_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
// Dispatcher callbacks table
const Z_Dispatcher Z_DispatchTable[] PROGMEM = {
{ AREQ_AF_DATA_CONFIRM, &Z_DataConfirm },
{ AREQ_AF_INCOMING_MESSAGE, &Z_ReceiveAfIncomingMessage },
@ -534,6 +559,10 @@ const Z_Dispatcher Z_DispatchTable[] PROGMEM = {
{ AREQ_ZDO_BIND_RSP, &Z_BindRsp },
};
/*********************************************************************************************\
* Default resolver
\*********************************************************************************************/
int32_t Z_Recv_Default(int32_t res, const class SBuffer &buf) {
// Default message handler for new messages
if (zigbee.init_phase) {
@ -549,12 +578,22 @@ int32_t Z_Recv_Default(int32_t res, const class SBuffer &buf) {
}
}
/*********************************************************************************************\
* Functions called by State Machine
\*********************************************************************************************/
//
// Callback for loading Zigbee configuration from Flash, called by the state machine
//
int32_t Z_Load_Devices(uint8_t value) {
// try to hidrate from known devices
loadZigbeeDevices();
return 0; // continue
}
//
// Send messages to query the state of each Hue emulated light
//
int32_t Z_Query_Bulbs(uint8_t value) {
// Scan all devices and send deferred requests to know the state of bulbs
uint32_t wait_ms = 1000; // start with 1.0 s delay
@ -588,6 +627,9 @@ int32_t Z_Query_Bulbs(uint8_t value) {
return 0; // continue
}
//
// Zigbee initialization is complete, let the party begin
//
int32_t Z_State_Ready(uint8_t value) {
zigbee.init_phase = false; // initialization phase complete
return 0; // continue

View File

@ -47,85 +47,10 @@ void (* const ZigbeeCommand[])(void) PROGMEM = {
&CmndZbLight, CmndZbRestore,
};
int32_t ZigbeeProcessInput(class SBuffer &buf) {
if (!zigbee.state_machine) { return -1; } // if state machine is stopped, send 'ignore' message
// apply the receive filter, acts as 'startsWith()'
bool recv_filter_match = true;
bool recv_prefix_match = false; // do the first 2 bytes match the response
if ((zigbee.recv_filter) && (zigbee.recv_filter_len > 0)) {
if (zigbee.recv_filter_len >= 2) {
recv_prefix_match = false;
if ( (pgm_read_byte(&zigbee.recv_filter[0]) == buf.get8(0)) &&
(pgm_read_byte(&zigbee.recv_filter[1]) == buf.get8(1)) ) {
recv_prefix_match = true;
}
}
for (uint32_t i = 0; i < zigbee.recv_filter_len; i++) {
if (pgm_read_byte(&zigbee.recv_filter[i]) != buf.get8(i)) {
recv_filter_match = false;
break;
}
}
//AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_ZIGBEE "ZbProcessInput: recv_prefix_match = %d, recv_filter_match = %d"), recv_prefix_match, recv_filter_match);
}
// if there is a recv_callback, call it now
int32_t res = -1; // default to ok
// res = 0 - proceed to next state
// res > 0 - proceed to the specified state
// res = -1 - silently ignore the message
// res <= -2 - move to error state
// pre-compute the suggested value
if ((zigbee.recv_filter) && (zigbee.recv_filter_len > 0)) {
if (!recv_prefix_match) {
res = -1; // ignore
} else { // recv_prefix_match
if (recv_filter_match) {
res = 0; // ok
} else {
if (zigbee.recv_until) {
res = -1; // ignore until full match
} else {
res = -2; // error, because message is expected but wrong value
}
}
}
} else { // we don't have any filter, ignore message by default
res = -1;
}
if (recv_prefix_match) {
if (zigbee.recv_func) {
res = (*zigbee.recv_func)(res, buf);
}
}
if (-1 == res) {
// if frame was ignored up to now
if (zigbee.recv_unexpected) {
res = (*zigbee.recv_unexpected)(res, buf);
}
}
//AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_ZIGBEE "ZbProcessInput: res = %d"), res);
// change state accordingly
if (0 == res) {
// if ok, continue execution
zigbee.state_waiting = false;
} else if (res > 0) {
ZigbeeGotoLabel(res); // if >0 then go to specified label
} else if (-1 == res) {
// -1 means ignore message
// just do nothing
} else {
// any other negative value means error
ZigbeeGotoLabel(zigbee.on_error_goto);
}
}
void ZigbeeInput(void)
//
// Called at event loop, checks for incoming data from the CC2530
//
void ZigbeeInputLoop(void)
{
static uint32_t zigbee_polling_window = 0;
static uint8_t fcs = ZIGBEE_SOF;
@ -217,6 +142,7 @@ void ZigbeeInput(void)
/********************************************************************************************/
// Initialize internal structures
void ZigbeeInit(void)
{
// AddLog_P2(LOG_LEVEL_INFO, PSTR("ZigbeeInit Mem1 = %d"), ESP.getFreeHeap());
@ -260,10 +186,10 @@ uint32_t strToUInt(const JsonVariant &val) {
return 0; // couldn't parse anything
}
// Do a factory reset of the CC2530
const unsigned char ZIGBEE_FACTORY_RESET[] PROGMEM =
{ Z_SREQ | Z_SAPI, SAPI_WRITE_CONFIGURATION, CONF_STARTUP_OPTION, 0x01 /* len */, 0x01 /* STARTOPT_CLEAR_CONFIG */};
//"2605030101"; // Z_SREQ | Z_SAPI, SAPI_WRITE_CONFIGURATION, CONF_STARTUP_OPTION, 0x01 len, 0x01 STARTOPT_CLEAR_CONFIG
// Do a factory reset of the CC2530
void CmndZbReset(void) {
if (ZigbeeSerial) {
switch (XdrvMailbox.payload) {
@ -279,6 +205,10 @@ void CmndZbReset(void) {
}
}
//
// Same code for `ZbZNPSend` and `ZbZNPReceive`
// building the complete message (intro, length)
//
void CmndZbZNPSendOrReceive(bool send)
{
if (ZigbeeSerial && (XdrvMailbox.data_len > 0)) {
@ -298,8 +228,10 @@ void CmndZbZNPSendOrReceive(bool send)
codes += 2;
}
if (send) {
// Command was `ZbZNPSend`
ZigbeeZNPSend(buf.getBuffer(), buf.len());
} else {
// Command was `ZbZNPReceive`
ZigbeeProcessInput(buf);
}
}
@ -347,6 +279,21 @@ void ZigbeeZNPSend(const uint8_t *msg, size_t len) {
ToHex_P(msg, len, hex_char, sizeof(hex_char)));
}
//
// Internal function, send the low-level frame
// Input:
// - shortaddr: 16-bits short address, or 0x0000 if group address
// - groupaddr: 16-bits group address, or 0x0000 if unicast using shortaddr
// - clusterIf: 16-bits cluster number
// - endpoint: 8-bits target endpoint (source is always 0x01), unused for group addresses. Should not be 0x00 except when sending to group address.
// - cmdId: 8-bits ZCL command number
// - clusterSpecific: boolean, is the message general cluster or cluster specific, used to create the FC byte of ZCL
// - msg: pointer to byte array, payload of ZCL message (len is following), ignored if nullptr
// - len: length of the 'msg' payload
// - needResponse: boolean, true = we ask the target to respond, false = the target should not respond
// - transacId: 8-bits, transation id of message (should be incremented at each message), used both for Zigbee message number and ZCL message number
// Returns: None
//
void ZigbeeZCLSend_Raw(uint16_t shortaddr, uint16_t groupaddr, uint16_t clusterId, uint8_t endpoint, uint8_t cmdId, bool clusterSpecific, const uint8_t *msg, size_t len, bool needResponse, uint8_t transacId) {
SBuffer buf(32+len);
@ -379,7 +326,22 @@ void ZigbeeZCLSend_Raw(uint16_t shortaddr, uint16_t groupaddr, uint16_t clusterI
ZigbeeZNPSend(buf.getBuffer(), buf.len());
}
// Send a command specified as an HEX string for the workload
/********************************************************************************************/
//
// High-level function
// Send a command specified as an HEX string for the workload.
// The target endpoint is computed if zero, i.e. sent to the first known endpoint of the device.
// If cluster-specific, a timer may be set calling `zigbeeSetCommandTimer()`, for ex to coalesce attributes or Aqara presence sensor
//
// Inputs:
// - shortaddr: 16-bits short address, or 0x0000 if group address
// - groupaddr: 16-bits group address, or 0x0000 if unicast using shortaddr
// - endpoint: 8-bits target endpoint (source is always 0x01), if 0x00, it will be guessed from ZbStatus information (basically the first endpoint of the device)
// - clusterSpecific: boolean, is the message general cluster or cluster specific, used to create the FC byte of ZCL
// - clusterIf: 16-bits cluster number
// - param: pointer to HEX string for payload, should not be nullptr
// Returns: None
//
void zigbeeZCLSendStr(uint16_t shortaddr, uint16_t groupaddr, uint8_t endpoint, bool clusterSpecific,
uint16_t cluster, uint8_t cmd, const char *param) {
size_t size = param ? strlen(param) : 0;
@ -395,15 +357,15 @@ void zigbeeZCLSendStr(uint16_t shortaddr, uint16_t groupaddr, uint8_t endpoint,
if ((0 == endpoint) && (shortaddr)) {
// endpoint is not specified, let's try to find it from shortAddr, unless it's a group address
endpoint = zigbee_devices.findFirstEndpoint(shortaddr);
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ZbSend: guessing endpoint 0x%02X"), endpoint);
//AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ZbSend: guessing endpoint 0x%02X"), endpoint);
}
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ZbSend: shortaddr 0x%04X, groupaddr 0x%04X, cluster 0x%04X, endpoint 0x%02X, cmd 0x%02X, data %s"),
shortaddr, groupaddr, cluster, endpoint, cmd, param);
if ((0 == endpoint) && (shortaddr)) {
if ((0 == endpoint) && (shortaddr)) { // endpoint null is ok for group address
AddLog_P2(LOG_LEVEL_INFO, PSTR("ZbSend: unspecified endpoint"));
return;
} // endpoint null is ok for group address
}
// everything is good, we can send the command
ZigbeeZCLSend_Raw(shortaddr, groupaddr, cluster, endpoint, cmd, clusterSpecific, buf.getBuffer(), buf.len(), true, zigbee_devices.getNextSeqNumber(shortaddr));
@ -413,21 +375,24 @@ void zigbeeZCLSendStr(uint16_t shortaddr, uint16_t groupaddr, uint8_t endpoint,
}
}
//
// Command `ZbSend`
//
void CmndZbSend(void) {
// ZigbeeSend { "device":"0x1234", "endpoint":"0x03", "send":{"Power":1} }
// ZigbeeSend { "device":"0x1234", "endpoint":"0x03", "send":{"Power":"3"} }
// ZigbeeSend { "device":"0x1234", "endpoint":"0x03", "send":{"Power":"0xFF"} }
// ZigbeeSend { "device":"0x1234", "endpoint":"0x03", "send":{"Power":null} }
// ZigbeeSend { "device":"0x1234", "endpoint":"0x03", "send":{"Power":false} }
// ZigbeeSend { "device":"0x1234", "endpoint":"0x03", "send":{"Power":true} }
// ZigbeeSend { "device":"0x1234", "endpoint":"0x03", "send":{"Power":"true"} }
// ZigbeeSend { "device":"0x1234", "endpoint":"0x03", "send":{"ShutterClose":null} }
// ZigbeeSend { "devicse":"0x1234", "endpoint":"0x03", "send":{"Power":1} }
// ZigbeeSend { "device":"0x1234", "endpoint":"0x03", "send":{"Color":"1,2"} }
// ZigbeeSend { "device":"0x1234", "endpoint":"0x03", "send":{"Color":"0x1122,0xFFEE"} }
// ZbSend { "device":"0x1234", "endpoint":"0x03", "send":{"Power":1} }
// ZbSend { "device":"0x1234", "endpoint":"0x03", "send":{"Power":"3"} }
// ZbSend { "device":"0x1234", "endpoint":"0x03", "send":{"Power":"0xFF"} }
// ZbSend { "device":"0x1234", "endpoint":"0x03", "send":{"Power":null} }
// ZbSend { "device":"0x1234", "endpoint":"0x03", "send":{"Power":false} }
// ZbSend { "device":"0x1234", "endpoint":"0x03", "send":{"Power":true} }
// ZbSend { "device":"0x1234", "endpoint":"0x03", "send":{"Power":"true"} }
// ZbSend { "device":"0x1234", "endpoint":"0x03", "send":{"ShutterClose":null} }
// ZbSend { "devicse":"0x1234", "endpoint":"0x03", "send":{"Power":1} }
// ZbSend { "device":"0x1234", "endpoint":"0x03", "send":{"Color":"1,2"} }
// ZbSend { "device":"0x1234", "endpoint":"0x03", "send":{"Color":"0x1122,0xFFEE"} }
if (zigbee.init_phase) { ResponseCmndChar_P(PSTR(D_ZIGBEE_NOT_STARTED)); return; }
DynamicJsonBuffer jsonBuf;
JsonObject &json = jsonBuf.parseObject(XdrvMailbox.data);
const JsonObject &json = jsonBuf.parseObject((const char*) XdrvMailbox.data);
if (!json.success()) { ResponseCmndChar_P(PSTR(D_JSON_INVALID_JSON)); return; }
// params
@ -463,16 +428,16 @@ void CmndZbSend(void) {
// If String, it's a low level command
if (val_cmd.is<JsonObject>()) {
// we have a high-level command
JsonObject &cmd_obj = val_cmd.as<JsonObject&>();
const JsonObject &cmd_obj = val_cmd.as<const JsonObject&>();
int32_t cmd_size = cmd_obj.size();
if (cmd_size > 1) {
Response_P(PSTR("Only 1 command allowed (%d)"), cmd_size);
return;
} else if (1 == cmd_size) {
// We have exactly 1 command, parse it
JsonObject::iterator it = cmd_obj.begin(); // just get the first key/value
JsonObject::const_iterator it = cmd_obj.begin(); // just get the first key/value
String key = it->key;
JsonVariant& value = it->value;
const JsonVariant& value = it->value;
uint32_t x = 0, y = 0, z = 0;
uint16_t cmd_var;
@ -526,7 +491,7 @@ void CmndZbSend(void) {
} else {
// we have zero command, pass through until last error for missing command
}
} else if (val_cmd.is<char*>()) {
} else if (val_cmd.is<const char*>()) {
// low-level command
cmd_str = val_cmd.as<String>();
// Now parse the string to extract cluster, command, and payload
@ -564,16 +529,18 @@ void CmndZbSend(void) {
Response_P(PSTR("Missing zigbee 'Send'"));
return;
}
}
//
// Command `ZbBind`
//
void CmndZbBind(void) {
// ZbBind { "device":"0x1234", "endpoint":1, "cluster":6 }
// ZbBind {"Device":"<device>", "Endpoint":<endpoint>, "Cluster":<cluster>, "ToDevice":"<to_device>", "ToEndpoint":<to_endpoint>, "ToGroup":<to_group> }
// local endpoint is always 1, IEEE addresses are calculated
if (zigbee.init_phase) { ResponseCmndChar_P(PSTR(D_ZIGBEE_NOT_STARTED)); return; }
DynamicJsonBuffer jsonBuf;
JsonObject &json = jsonBuf.parseObject(XdrvMailbox.data);
const JsonObject &json = jsonBuf.parseObject((const char*) XdrvMailbox.data);
if (!json.success()) { ResponseCmndChar_P(PSTR(D_JSON_INVALID_JSON)); return; }
// params
@ -658,6 +625,9 @@ void CmndZbProbe(void) {
CmndZbProbeOrPing(true);
}
//
// Common code for `ZbProbe` and `ZbPing`
//
void CmndZbProbeOrPing(boolean probe) {
if (zigbee.init_phase) { ResponseCmndChar_P(PSTR(D_ZIGBEE_NOT_STARTED)); return; }
uint16_t shortaddr = zigbee_devices.parseDeviceParam(XdrvMailbox.data);
@ -677,12 +647,15 @@ void CmndZbPing(void) {
CmndZbProbeOrPing(false);
}
//
// Command `ZbName`
// Specify, read or erase a Friendly Name
//
void CmndZbName(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
// ZbName <device_id>,<friendlyname> - assign a friendly name
// ZbName <device_id> - display the current friendly name
// ZbName <device_id>, - remove friendly name
//
// Where <device_id> can be: short_addr, long_addr, device_index, friendly_name
@ -706,12 +679,15 @@ void CmndZbName(void) {
}
}
//
// Command `ZbName`
// 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
// ZbName <device_id>,<friendlyname> - assign a friendly name
// ZbName <device_id> - display the current friendly name
// ZbName <device_id>, - remove friendly name
//
// Where <device_id> can be: short_addr, long_addr, device_index, friendly_name
@ -735,6 +711,8 @@ void CmndZbModelId(void) {
}
}
//
// Command `ZbLight`
// Specify, read or erase a Light type for Hue/Alexa integration
void CmndZbLight(void) {
// Syntax is:
@ -768,7 +746,10 @@ void CmndZbLight(void) {
ResponseCmndDone();
}
//
// Command `ZbForget`
// 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_P(PSTR(D_ZIGBEE_NOT_STARTED)); return; }
uint16_t shortaddr = zigbee_devices.parseDeviceParam(XdrvMailbox.data);
@ -783,12 +764,13 @@ void CmndZbForget(void) {
}
}
//
// Command `ZbSave`
// Save Zigbee information to flash
//
void CmndZbSave(void) {
if (zigbee.init_phase) { ResponseCmndChar_P(PSTR(D_ZIGBEE_NOT_STARTED)); return; }
saveZigbeeDevices();
ResponseCmndDone();
}
@ -803,7 +785,7 @@ void CmndZbSave(void) {
void CmndZbRestore(void) {
if (zigbee.init_phase) { ResponseCmndChar_P(PSTR(D_ZIGBEE_NOT_STARTED)); return; }
DynamicJsonBuffer jsonBuf;
const JsonVariant json_parsed = jsonBuf.parse((const char*)XdrvMailbox.data); // const to force a copy of parameter
const JsonVariant json_parsed = jsonBuf.parse((const char*) XdrvMailbox.data); // const to force a copy of parameter
const JsonVariant * json = &json_parsed; // root of restore, to be changed if needed
bool success = false;
@ -846,14 +828,17 @@ void CmndZbRestore(void) {
ResponseCmndDone();
}
//
// Command `ZbRead`
// Send an attribute read command to a device, specifying cluster and list of attributes
//
void CmndZbRead(void) {
// ZigbeeRead {"Device":"0xF289","Cluster":0,"Endpoint":3,"Attr":5}
// ZigbeeRead {"Device":"0xF289","Cluster":"0x0000","Endpoint":"0x0003","Attr":"0x0005"}
// ZigbeeRead {"Device":"0xF289","Cluster":0,"Endpoint":3,"Attr":[5,6,7,4]}
// ZbRead {"Device":"0xF289","Cluster":0,"Endpoint":3,"Attr":5}
// ZbRead {"Device":"0xF289","Cluster":"0x0000","Endpoint":"0x0003","Attr":"0x0005"}
// ZbRead {"Device":"0xF289","Cluster":0,"Endpoint":3,"Attr":[5,6,7,4]}
if (zigbee.init_phase) { ResponseCmndChar_P(PSTR(D_ZIGBEE_NOT_STARTED)); return; }
DynamicJsonBuffer jsonBuf;
JsonObject &json = jsonBuf.parseObject(XdrvMailbox.data);
JsonObject &json = jsonBuf.parseObject((const char*) XdrvMailbox.data);
if (!json.success()) { ResponseCmndChar_P(PSTR(D_JSON_INVALID_JSON)); return; }
// params
@ -884,7 +869,7 @@ void CmndZbRead(void) {
if (nullptr != &val_attr) {
uint16_t val = strToUInt(val_attr);
if (val_attr.is<JsonArray>()) {
JsonArray& attr_arr = val_attr;
const JsonArray& attr_arr = val_attr.as<const JsonArray&>();
attrs_len = attr_arr.size() * 2;
attrs = new uint8_t[attrs_len];
@ -920,7 +905,10 @@ void CmndZbRead(void) {
if (attrs) { delete[] attrs; }
}
//
// Command `ZbPermitJoin`
// Allow or Deny pairing of new Zigbee devices
//
void CmndZbPermitJoin(void) {
if (zigbee.init_phase) { ResponseCmndChar_P(PSTR(D_ZIGBEE_NOT_STARTED)); return; }
uint32_t payload = XdrvMailbox.payload;
@ -946,6 +934,9 @@ void CmndZbPermitJoin(void) {
ResponseCmndDone();
}
//
// Command `ZbStatus`
//
void CmndZbStatus(void) {
if (ZigbeeSerial) {
if (zigbee.init_phase) { ResponseCmndChar_P(PSTR(D_ZIGBEE_NOT_STARTED)); return; }
@ -976,7 +967,7 @@ bool Xdrv23(uint8_t function)
}
break;
case FUNC_LOOP:
if (ZigbeeSerial) { ZigbeeInput(); }
if (ZigbeeSerial) { ZigbeeInputLoop(); }
if (zigbee.state_machine) {
ZigbeeStateMachine_Run();
}