mirror of https://github.com/arendst/Tasmota.git
Code optimization, cleaning and more error codes
This commit is contained in:
parent
0a573bd0d6
commit
ac4d4ac571
|
@ -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]);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,12 +529,13 @@ 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
|
||||
// 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
|
||||
|
@ -522,6 +546,7 @@ 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
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue