mirror of
https://github.com/arendst/Tasmota.git
synced 2025-07-23 10:46:31 +00:00
Merge pull request #8572 from s-hadinger/zigbee_report
Add Zigbee options to ``ZbSend`` to write and report attributes
This commit is contained in:
commit
abd60ea164
@ -11,6 +11,7 @@
|
|||||||
- Add Three Phase Export Active Energy to SDM630 driver
|
- Add Three Phase Export Active Energy to SDM630 driver
|
||||||
- Add wildcard pattern ``?`` for JSON matching in rules
|
- Add wildcard pattern ``?`` for JSON matching in rules
|
||||||
- Add support for unique MQTTClient (and inherited fallback topic) by full Mac address using ``mqttclient DVES_%12X`` (#8300)
|
- Add support for unique MQTTClient (and inherited fallback topic) by full Mac address using ``mqttclient DVES_%12X`` (#8300)
|
||||||
|
- Add Zigbee options to ``ZbSend`` to write and report attributes
|
||||||
|
|
||||||
### 8.3.1.1 20200518
|
### 8.3.1.1 20200518
|
||||||
|
|
||||||
|
@ -513,10 +513,15 @@
|
|||||||
#define D_CMND_ZIGBEE_FORGET "Forget"
|
#define D_CMND_ZIGBEE_FORGET "Forget"
|
||||||
#define D_CMND_ZIGBEE_SAVE "Save"
|
#define D_CMND_ZIGBEE_SAVE "Save"
|
||||||
#define D_CMND_ZIGBEE_LINKQUALITY "LinkQuality"
|
#define D_CMND_ZIGBEE_LINKQUALITY "LinkQuality"
|
||||||
|
#define D_CMND_ZIGBEE_CLUSTER "Cluster"
|
||||||
#define D_CMND_ZIGBEE_ENDPOINT "Endpoint"
|
#define D_CMND_ZIGBEE_ENDPOINT "Endpoint"
|
||||||
#define D_CMND_ZIGBEE_GROUP "Group"
|
#define D_CMND_ZIGBEE_GROUP "Group"
|
||||||
|
#define D_CMND_ZIGBEE_MANUF "Manuf"
|
||||||
|
#define D_CMND_ZIGBEE_DEVICE "Device"
|
||||||
#define D_CMND_ZIGBEE_READ "Read"
|
#define D_CMND_ZIGBEE_READ "Read"
|
||||||
#define D_CMND_ZIGBEE_SEND "Send"
|
#define D_CMND_ZIGBEE_SEND "Send"
|
||||||
|
#define D_CMND_ZIGBEE_WRITE "Write"
|
||||||
|
#define D_CMND_ZIGBEE_REPORT "Report"
|
||||||
#define D_JSON_ZIGBEE_ZCL_SENT "ZbZCLSent"
|
#define D_JSON_ZIGBEE_ZCL_SENT "ZbZCLSent"
|
||||||
#define D_JSON_ZIGBEE_RECEIVED "ZbReceived"
|
#define D_JSON_ZIGBEE_RECEIVED "ZbReceived"
|
||||||
#define D_CMND_ZIGBEE_BIND "Bind"
|
#define D_CMND_ZIGBEE_BIND "Bind"
|
||||||
|
@ -458,6 +458,21 @@ bool RulesRuleMatch(uint8_t rule_set, String &event, String &rule)
|
|||||||
rule_param = String(SunMinutes(1));
|
rule_param = String(SunMinutes(1));
|
||||||
}
|
}
|
||||||
#endif // USE_TIMERS and USE_SUNRISE
|
#endif // USE_TIMERS and USE_SUNRISE
|
||||||
|
// #ifdef USE_ZIGBEE
|
||||||
|
// if (rule_param.startsWith(F("%ZBDEVICE%"))) {
|
||||||
|
// snprintf_P(stemp, sizeof(stemp), PSTR("0x%04X"), Z_GetLastDevice());
|
||||||
|
// rule_param = String(stemp);
|
||||||
|
// }
|
||||||
|
// if (rule_param.startsWith(F("%ZBGROUP%"))) {
|
||||||
|
// rule_param = String(Z_GetLastGroup());
|
||||||
|
// }
|
||||||
|
// if (rule_param.startsWith(F("%ZBCLUSTER%"))) {
|
||||||
|
// rule_param = String(Z_GetLastCluster());
|
||||||
|
// }
|
||||||
|
// if (rule_param.startsWith(F("%ZBENDPOINT%"))) {
|
||||||
|
// rule_param = String(Z_GetLastEndpoint());
|
||||||
|
// }
|
||||||
|
// #endif
|
||||||
rule_param.toUpperCase();
|
rule_param.toUpperCase();
|
||||||
strlcpy(rule_svalue, rule_param.c_str(), sizeof(rule_svalue));
|
strlcpy(rule_svalue, rule_param.c_str(), sizeof(rule_svalue));
|
||||||
|
|
||||||
@ -701,6 +716,13 @@ bool RuleSetProcess(uint8_t rule_set, String &event_saved)
|
|||||||
RulesVarReplace(commands, F("%SUNRISE%"), String(SunMinutes(0)));
|
RulesVarReplace(commands, F("%SUNRISE%"), String(SunMinutes(0)));
|
||||||
RulesVarReplace(commands, F("%SUNSET%"), String(SunMinutes(1)));
|
RulesVarReplace(commands, F("%SUNSET%"), String(SunMinutes(1)));
|
||||||
#endif // USE_TIMERS and USE_SUNRISE
|
#endif // USE_TIMERS and USE_SUNRISE
|
||||||
|
#ifdef USE_ZIGBEE
|
||||||
|
snprintf_P(stemp, sizeof(stemp), PSTR("0x%04X"), Z_GetLastDevice());
|
||||||
|
RulesVarReplace(commands, F("%ZBDEVICE%"), String(stemp));
|
||||||
|
RulesVarReplace(commands, F("%ZBGROUP%"), String(Z_GetLastGroup()));
|
||||||
|
RulesVarReplace(commands, F("%ZBCLUSTER%"), String(Z_GetLastCluster()));
|
||||||
|
RulesVarReplace(commands, F("%ZBENDPOINT%"), String(Z_GetLastEndpoint()));
|
||||||
|
#endif
|
||||||
|
|
||||||
char command[commands.length() +1];
|
char command[commands.length() +1];
|
||||||
strlcpy(command, commands.c_str(), sizeof(command));
|
strlcpy(command, commands.c_str(), sizeof(command));
|
||||||
@ -1261,6 +1283,16 @@ bool findNextVariableValue(char * &pVarname, float &value)
|
|||||||
} else if (sVarName.equals(F("SUNSET"))) {
|
} else if (sVarName.equals(F("SUNSET"))) {
|
||||||
value = SunMinutes(1);
|
value = SunMinutes(1);
|
||||||
#endif
|
#endif
|
||||||
|
// #ifdef USE_ZIGBEE
|
||||||
|
// // } else if (sVarName.equals(F("ZBDEVICE"))) {
|
||||||
|
// // value = Z_GetLastDevice();
|
||||||
|
// } else if (sVarName.equals(F("ZBGROUP"))) {
|
||||||
|
// value = Z_GetLastGroup();
|
||||||
|
// } else if (sVarName.equals(F("ZBCLUSTER"))) {
|
||||||
|
// value = Z_GetLastCluster();
|
||||||
|
// } else if (sVarName.equals(F("ZBENDPOINT"))) {
|
||||||
|
// value = Z_GetLastEndpoint();
|
||||||
|
// #endif
|
||||||
} else {
|
} else {
|
||||||
succeed = false;
|
succeed = false;
|
||||||
}
|
}
|
||||||
|
@ -26,6 +26,24 @@
|
|||||||
#endif
|
#endif
|
||||||
const uint16_t kZigbeeSaveDelaySeconds = ZIGBEE_SAVE_DELAY_SECONDS; // wait for x seconds
|
const uint16_t kZigbeeSaveDelaySeconds = ZIGBEE_SAVE_DELAY_SECONDS; // wait for x seconds
|
||||||
|
|
||||||
|
/*********************************************************************************************\
|
||||||
|
* Structures for Rules variables related to the last received message
|
||||||
|
\*********************************************************************************************/
|
||||||
|
|
||||||
|
typedef struct Z_LastMessageVars {
|
||||||
|
uint16_t device; // device short address
|
||||||
|
uint16_t groupaddr; // group address
|
||||||
|
uint16_t cluster; // cluster id
|
||||||
|
uint8_t endpoint; // source endpoint
|
||||||
|
} Z_LastMessageVars;
|
||||||
|
|
||||||
|
Z_LastMessageVars gZbLastMessage;
|
||||||
|
|
||||||
|
uint16_t Z_GetLastDevice(void) { return gZbLastMessage.device; }
|
||||||
|
uint16_t Z_GetLastGroup(void) { return gZbLastMessage.groupaddr; }
|
||||||
|
uint16_t Z_GetLastCluster(void) { return gZbLastMessage.cluster; }
|
||||||
|
uint8_t Z_GetLastEndpoint(void) { return gZbLastMessage.endpoint; }
|
||||||
|
|
||||||
/*********************************************************************************************\
|
/*********************************************************************************************\
|
||||||
* Structures for device configuration
|
* Structures for device configuration
|
||||||
\*********************************************************************************************/
|
\*********************************************************************************************/
|
||||||
@ -256,7 +274,7 @@ int32_t Z_Devices::findEndpointInVector(const std::vector<T> & vecOfElements, u
|
|||||||
// entry with same shortaddr or longaddr exists.
|
// entry with same shortaddr or longaddr exists.
|
||||||
//
|
//
|
||||||
Z_Device & Z_Devices::createDeviceEntry(uint16_t shortaddr, uint64_t longaddr) {
|
Z_Device & Z_Devices::createDeviceEntry(uint16_t shortaddr, uint64_t longaddr) {
|
||||||
if (!shortaddr && !longaddr) { return *(Z_Device*) nullptr; } // it is not legal to create an enrty with both short/long addr null
|
if ((BAD_SHORTADDR == shortaddr) && !longaddr) { return *(Z_Device*) nullptr; } // it is not legal to create this entry
|
||||||
//Z_Device* device_alloc = (Z_Device*) malloc(sizeof(Z_Device));
|
//Z_Device* device_alloc = (Z_Device*) malloc(sizeof(Z_Device));
|
||||||
Z_Device* device_alloc = new Z_Device{
|
Z_Device* device_alloc = new Z_Device{
|
||||||
longaddr,
|
longaddr,
|
||||||
@ -340,7 +358,7 @@ int32_t Z_Devices::findFriendlyName(const char * name) const {
|
|||||||
if (name_len) {
|
if (name_len) {
|
||||||
for (auto &elem : _devices) {
|
for (auto &elem : _devices) {
|
||||||
if (elem->friendlyName) {
|
if (elem->friendlyName) {
|
||||||
if (strcmp(elem->friendlyName, name) == 0) { return found; }
|
if (strcasecmp(elem->friendlyName, name) == 0) { return found; }
|
||||||
}
|
}
|
||||||
found++;
|
found++;
|
||||||
}
|
}
|
||||||
@ -860,21 +878,21 @@ const JsonObject *Z_Devices::jsonGet(uint16_t shortaddr) {
|
|||||||
void Z_Devices::jsonPublishFlush(uint16_t shortaddr) {
|
void Z_Devices::jsonPublishFlush(uint16_t shortaddr) {
|
||||||
Z_Device & device = getShortAddr(shortaddr);
|
Z_Device & device = getShortAddr(shortaddr);
|
||||||
if (&device == nullptr) { return; } // don't crash if not found
|
if (&device == nullptr) { return; } // don't crash if not found
|
||||||
JsonObject * json = device.json;
|
JsonObject & json = *device.json;
|
||||||
if (json == nullptr) { return; } // abort if nothing in buffer
|
if (&json == nullptr) { return; } // abort if nothing in buffer
|
||||||
|
|
||||||
const char * fname = zigbee_devices.getFriendlyName(shortaddr);
|
const char * fname = zigbee_devices.getFriendlyName(shortaddr);
|
||||||
bool use_fname = (Settings.flag4.zigbee_use_names) && (fname); // should we replace shortaddr with friendlyname?
|
bool use_fname = (Settings.flag4.zigbee_use_names) && (fname); // should we replace shortaddr with friendlyname?
|
||||||
|
|
||||||
// Remove redundant "Name" or "Device"
|
// save parameters is global variables to be used by Rules
|
||||||
if (use_fname) {
|
gZbLastMessage.device = shortaddr; // %zbdevice%
|
||||||
json->remove(F(D_JSON_ZIGBEE_NAME));
|
gZbLastMessage.groupaddr = json[F(D_CMND_ZIGBEE_GROUP)]; // %zbgroup%
|
||||||
} else {
|
gZbLastMessage.cluster = json[F(D_CMND_ZIGBEE_CLUSTER)]; // %zbcluster%
|
||||||
json->remove(F(D_JSON_ZIGBEE_DEVICE));
|
gZbLastMessage.endpoint = json[F(D_CMND_ZIGBEE_ENDPOINT)]; // %zbendpoint%
|
||||||
}
|
|
||||||
|
|
||||||
|
// dump json in string
|
||||||
String msg = "";
|
String msg = "";
|
||||||
json->printTo(msg);
|
json.printTo(msg);
|
||||||
zigbee_devices.jsonClear(shortaddr);
|
zigbee_devices.jsonClear(shortaddr);
|
||||||
|
|
||||||
if (use_fname) {
|
if (use_fname) {
|
||||||
@ -889,7 +907,7 @@ void Z_Devices::jsonPublishFlush(uint16_t shortaddr) {
|
|||||||
} else {
|
} else {
|
||||||
MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR), Settings.flag.mqtt_sensor_retain);
|
MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR), Settings.flag.mqtt_sensor_retain);
|
||||||
}
|
}
|
||||||
XdrvRulesProcess();
|
XdrvRulesProcess(); // apply rules
|
||||||
}
|
}
|
||||||
|
|
||||||
void Z_Devices::jsonPublishNow(uint16_t shortaddr, JsonObject & values) {
|
void Z_Devices::jsonPublishNow(uint16_t shortaddr, JsonObject & values) {
|
||||||
@ -923,7 +941,7 @@ uint16_t Z_Devices::parseDeviceParam(const char * param, bool short_must_be_know
|
|||||||
if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload <= 99)) {
|
if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload <= 99)) {
|
||||||
shortaddr = zigbee_devices.isKnownIndex(XdrvMailbox.payload - 1);
|
shortaddr = zigbee_devices.isKnownIndex(XdrvMailbox.payload - 1);
|
||||||
}
|
}
|
||||||
} else if ((dataBuf[0] == '0') && (dataBuf[1] == 'x')) {
|
} else if ((dataBuf[0] == '0') && ((dataBuf[1] == 'x') || (dataBuf[1] == 'X'))) {
|
||||||
// starts with 0x
|
// starts with 0x
|
||||||
if (strlen(dataBuf) < 18) {
|
if (strlen(dataBuf) < 18) {
|
||||||
// expect a short address
|
// expect a short address
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -106,10 +106,11 @@ enum Zigbee_StateMachine_Instruction_Set {
|
|||||||
// Labels used in the State Machine -- internal only
|
// Labels used in the State Machine -- internal only
|
||||||
const uint8_t ZIGBEE_LABEL_INIT_COORD = 10; // Start ZNP as coordinator
|
const uint8_t ZIGBEE_LABEL_INIT_COORD = 10; // Start ZNP as coordinator
|
||||||
const uint8_t ZIGBEE_LABEL_START_COORD = 11; // Start ZNP as coordinator
|
const uint8_t ZIGBEE_LABEL_START_COORD = 11; // Start ZNP as coordinator
|
||||||
const uint8_t ZIGBEE_LABEL_INIT_ROUTER = 12; // Start ZNP as router
|
const uint8_t ZIGBEE_LABEL_INIT_ROUTER = 12; // Init ZNP as router
|
||||||
const uint8_t ZIGBEE_LABEL_START_ROUTER = 13; // Start ZNP as router
|
const uint8_t ZIGBEE_LABEL_START_ROUTER = 13; // Start ZNP as router
|
||||||
const uint8_t ZIGBEE_LABEL_INIT_DEVICE = 14; // Start ZNP as end-device
|
const uint8_t ZIGBEE_LABEL_INIT_DEVICE = 14; // Init ZNP as end-device
|
||||||
// const uint8_t ZIGBEE_LABEL_START_DEVICE = 15; // Start ZNP as end-device - same as ZIGBEE_LABEL_START_ROUTER
|
const uint8_t ZIGBEE_LABEL_START_DEVICE = 15; // Start ZNP as end-device
|
||||||
|
const uint8_t ZIGBEE_LABEL_START_ROUTER_DEVICE = 16; // Start common to router and device
|
||||||
const uint8_t ZIGBEE_LABEL_FACT_RESET_ROUTER_DEVICE_POST = 19; // common post configuration for router and device
|
const uint8_t ZIGBEE_LABEL_FACT_RESET_ROUTER_DEVICE_POST = 19; // common post configuration for router and device
|
||||||
const uint8_t ZIGBEE_LABEL_READY = 20; // goto label 20 for main loop
|
const uint8_t ZIGBEE_LABEL_READY = 20; // goto label 20 for main loop
|
||||||
const uint8_t ZIGBEE_LABEL_MAIN_LOOP = 21; // main loop
|
const uint8_t ZIGBEE_LABEL_MAIN_LOOP = 21; // main loop
|
||||||
@ -400,7 +401,7 @@ void Z_UpdateConfig(uint8_t zb_channel, uint16_t zb_pan_id, uint64_t zb_ext_pani
|
|||||||
const char kCheckingDeviceConfiguration[] PROGMEM = D_LOG_ZIGBEE "checking device configuration";
|
const char kCheckingDeviceConfiguration[] PROGMEM = D_LOG_ZIGBEE "checking device configuration";
|
||||||
const char kConfiguredCoord[] PROGMEM = "Configured, starting coordinator";
|
const char kConfiguredCoord[] PROGMEM = "Configured, starting coordinator";
|
||||||
const char kConfiguredRouter[] PROGMEM = "Configured, starting router";
|
const char kConfiguredRouter[] PROGMEM = "Configured, starting router";
|
||||||
const char kConfiguredDevice[] PROGMEM = "Configured, starting end-device";
|
const char kConfiguredDevice[] PROGMEM = "Configured, starting device";
|
||||||
const char kStarted[] PROGMEM = "Started";
|
const char kStarted[] PROGMEM = "Started";
|
||||||
const char kZigbeeStarted[] PROGMEM = D_LOG_ZIGBEE "Zigbee started";
|
const char kZigbeeStarted[] PROGMEM = D_LOG_ZIGBEE "Zigbee started";
|
||||||
const char kResetting[] PROGMEM = "Resetting configuration";
|
const char kResetting[] PROGMEM = "Resetting configuration";
|
||||||
@ -426,7 +427,7 @@ static const Zigbee_Instruction zb_prog[] PROGMEM = {
|
|||||||
ZI_WAIT_RECV_FUNC(2000, ZBR_VERSION, &Z_ReceiveCheckVersion) // Check if version is valid
|
ZI_WAIT_RECV_FUNC(2000, ZBR_VERSION, &Z_ReceiveCheckVersion) // Check if version is valid
|
||||||
|
|
||||||
// Dispatching whether coordinator, router or end-device
|
// Dispatching whether coordinator, router or end-device
|
||||||
ZI_CALL(&Z_SwitchDeviceType, 0) // goto ZIGBEE_LABEL_START_ROUTER, ZIGBEE_LABEL_START_DEVICE or continue if coordinator
|
ZI_CALL(&Z_SwitchDeviceType, 0) // goto ZIGBEE_LABEL_INIT_ROUTER, ZIGBEE_LABEL_INIT_DEVICE or continue if coordinator
|
||||||
|
|
||||||
// ======================================================================
|
// ======================================================================
|
||||||
// Start as Zigbee Coordinator
|
// Start as Zigbee Coordinator
|
||||||
@ -537,8 +538,9 @@ static const Zigbee_Instruction zb_prog[] PROGMEM = {
|
|||||||
ZI_SEND(ZBS_LOGTYPE) // check the logical type
|
ZI_SEND(ZBS_LOGTYPE) // check the logical type
|
||||||
ZI_WAIT_RECV(1000, ZBS_LOGTYPE_ROUTER) // it should be coordinator
|
ZI_WAIT_RECV(1000, ZBS_LOGTYPE_ROUTER) // it should be coordinator
|
||||||
|
|
||||||
ZI_LABEL(ZIGBEE_LABEL_START_ROUTER) // Init as a router
|
// ZI_LABEL(ZIGBEE_LABEL_START_ROUTER) // Init as a router
|
||||||
ZI_MQTT_STATE(ZIGBEE_STATUS_STARTING, kConfiguredRouter)
|
ZI_MQTT_STATE(ZIGBEE_STATUS_STARTING, kConfiguredRouter)
|
||||||
|
ZI_LABEL(ZIGBEE_LABEL_START_ROUTER_DEVICE)
|
||||||
ZI_ON_ERROR_GOTO(ZIGBEE_LABEL_ABORT)
|
ZI_ON_ERROR_GOTO(ZIGBEE_LABEL_ABORT)
|
||||||
ZI_SEND(ZBS_AF_REGISTER_ALL) // Z_AF register for endpoint 01, profile 0x0104 Home Automation
|
ZI_SEND(ZBS_AF_REGISTER_ALL) // Z_AF register for endpoint 01, profile 0x0104 Home Automation
|
||||||
ZI_WAIT_RECV(1000, ZBR_AF_REGISTER)
|
ZI_WAIT_RECV(1000, ZBR_AF_REGISTER)
|
||||||
@ -570,7 +572,7 @@ static const Zigbee_Instruction zb_prog[] PROGMEM = {
|
|||||||
ZI_SEND(ZBS_WNV_ZNPHC) // Write NV ZNP Has Configured
|
ZI_SEND(ZBS_WNV_ZNPHC) // Write NV ZNP Has Configured
|
||||||
ZI_WAIT_RECV(1000, ZBR_WNV_OK)
|
ZI_WAIT_RECV(1000, ZBR_WNV_OK)
|
||||||
|
|
||||||
ZI_GOTO(ZIGBEE_LABEL_START_ROUTER)
|
ZI_GOTO(ZIGBEE_LABEL_START_ROUTER_DEVICE)
|
||||||
|
|
||||||
// ======================================================================
|
// ======================================================================
|
||||||
// Start as Zigbee Device
|
// Start as Zigbee Device
|
||||||
@ -583,7 +585,8 @@ static const Zigbee_Instruction zb_prog[] PROGMEM = {
|
|||||||
ZI_SEND(ZBS_LOGTYPE) // check the logical type
|
ZI_SEND(ZBS_LOGTYPE) // check the logical type
|
||||||
ZI_WAIT_RECV(1000, ZBS_LOGTYPE_DEVICE) // it should be coordinator
|
ZI_WAIT_RECV(1000, ZBS_LOGTYPE_DEVICE) // it should be coordinator
|
||||||
|
|
||||||
ZI_GOTO(ZIGBEE_LABEL_START_ROUTER)
|
ZI_MQTT_STATE(ZIGBEE_STATUS_STARTING, kConfiguredDevice)
|
||||||
|
ZI_GOTO(ZIGBEE_LABEL_START_ROUTER_DEVICE)
|
||||||
|
|
||||||
ZI_LABEL(ZIGBEE_LABEL_FACT_RESET_DEVICE) // Factory reset for router
|
ZI_LABEL(ZIGBEE_LABEL_FACT_RESET_DEVICE) // Factory reset for router
|
||||||
ZI_MQTT_STATE(ZIGBEE_STATUS_RESET_CONF, kResetting)
|
ZI_MQTT_STATE(ZIGBEE_STATUS_RESET_CONF, kResetting)
|
||||||
|
@ -653,13 +653,16 @@ int32_t Z_ReceiveAfIncomingMessage(int32_t res, const class SBuffer &buf) {
|
|||||||
JsonObject& json = jsonBuffer.createObject();
|
JsonObject& json = jsonBuffer.createObject();
|
||||||
|
|
||||||
if ( (!zcl_received.isClusterSpecificCommand()) && (ZCL_DEFAULT_RESPONSE == zcl_received.getCmdId())) {
|
if ( (!zcl_received.isClusterSpecificCommand()) && (ZCL_DEFAULT_RESPONSE == zcl_received.getCmdId())) {
|
||||||
zcl_received.parseResponse();
|
zcl_received.parseResponse(); // Zigbee general "Degault Response", publish ZbResponse message
|
||||||
} else {
|
} else {
|
||||||
// Build the ZbReceive json
|
// Build the ZbReceive json
|
||||||
if ( (!zcl_received.isClusterSpecificCommand()) && (ZCL_REPORT_ATTRIBUTES == zcl_received.getCmdId())) {
|
if ( (!zcl_received.isClusterSpecificCommand()) && (ZCL_REPORT_ATTRIBUTES == zcl_received.getCmdId())) {
|
||||||
zcl_received.parseRawAttributes(json);
|
zcl_received.parseRawAttributes(json); // Zigbee report attributes from sensors
|
||||||
if (clusterid) { defer_attributes = true; } // don't defer system Cluster=0 messages
|
if (clusterid) { defer_attributes = true; } // don't defer system Cluster=0 messages
|
||||||
} else if ( (!zcl_received.isClusterSpecificCommand()) && (ZCL_READ_ATTRIBUTES_RESPONSE == zcl_received.getCmdId())) {
|
} else if ( (!zcl_received.isClusterSpecificCommand()) && (ZCL_READ_ATTRIBUTES_RESPONSE == zcl_received.getCmdId())) {
|
||||||
|
zcl_received.parseReadAttributesResponse(json);
|
||||||
|
if (clusterid) { defer_attributes = true; } // don't defer system Cluster=0 messages
|
||||||
|
} else if ( (!zcl_received.isClusterSpecificCommand()) && (ZCL_READ_ATTRIBUTES == zcl_received.getCmdId())) {
|
||||||
zcl_received.parseReadAttributes(json);
|
zcl_received.parseReadAttributes(json);
|
||||||
if (clusterid) { defer_attributes = true; } // don't defer system Cluster=0 messages
|
if (clusterid) { defer_attributes = true; } // don't defer system Cluster=0 messages
|
||||||
} else if (zcl_received.isClusterSpecificCommand()) {
|
} else if (zcl_received.isClusterSpecificCommand()) {
|
||||||
|
@ -32,7 +32,7 @@ TasmotaSerial *ZigbeeSerial = nullptr;
|
|||||||
const char kZbCommands[] PROGMEM = D_PRFX_ZB "|" // prefix
|
const char kZbCommands[] PROGMEM = D_PRFX_ZB "|" // prefix
|
||||||
D_CMND_ZIGBEEZNPSEND "|" D_CMND_ZIGBEE_PERMITJOIN "|"
|
D_CMND_ZIGBEEZNPSEND "|" D_CMND_ZIGBEE_PERMITJOIN "|"
|
||||||
D_CMND_ZIGBEE_STATUS "|" D_CMND_ZIGBEE_RESET "|" D_CMND_ZIGBEE_SEND "|"
|
D_CMND_ZIGBEE_STATUS "|" D_CMND_ZIGBEE_RESET "|" D_CMND_ZIGBEE_SEND "|"
|
||||||
D_CMND_ZIGBEE_PROBE "|" D_CMND_ZIGBEE_READ "|" D_CMND_ZIGBEEZNPRECEIVE "|"
|
D_CMND_ZIGBEE_PROBE "|" D_CMND_ZIGBEEZNPRECEIVE "|"
|
||||||
D_CMND_ZIGBEE_FORGET "|" D_CMND_ZIGBEE_SAVE "|" D_CMND_ZIGBEE_NAME "|"
|
D_CMND_ZIGBEE_FORGET "|" D_CMND_ZIGBEE_SAVE "|" D_CMND_ZIGBEE_NAME "|"
|
||||||
D_CMND_ZIGBEE_BIND "|" D_CMND_ZIGBEE_UNBIND "|" D_CMND_ZIGBEE_PING "|" D_CMND_ZIGBEE_MODELID "|"
|
D_CMND_ZIGBEE_BIND "|" D_CMND_ZIGBEE_UNBIND "|" D_CMND_ZIGBEE_PING "|" D_CMND_ZIGBEE_MODELID "|"
|
||||||
D_CMND_ZIGBEE_LIGHT "|" D_CMND_ZIGBEE_RESTORE "|" D_CMND_ZIGBEE_BIND_STATE "|"
|
D_CMND_ZIGBEE_LIGHT "|" D_CMND_ZIGBEE_RESTORE "|" D_CMND_ZIGBEE_BIND_STATE "|"
|
||||||
@ -42,7 +42,7 @@ const char kZbCommands[] PROGMEM = D_PRFX_ZB "|" // prefix
|
|||||||
void (* const ZigbeeCommand[])(void) PROGMEM = {
|
void (* const ZigbeeCommand[])(void) PROGMEM = {
|
||||||
&CmndZbZNPSend, &CmndZbPermitJoin,
|
&CmndZbZNPSend, &CmndZbPermitJoin,
|
||||||
&CmndZbStatus, &CmndZbReset, &CmndZbSend,
|
&CmndZbStatus, &CmndZbReset, &CmndZbSend,
|
||||||
&CmndZbProbe, &CmndZbRead, &CmndZbZNPReceive,
|
&CmndZbProbe, &CmndZbZNPReceive,
|
||||||
&CmndZbForget, &CmndZbSave, &CmndZbName,
|
&CmndZbForget, &CmndZbSave, &CmndZbName,
|
||||||
&CmndZbBind, &CmndZbUnbind, &CmndZbPing, &CmndZbModelId,
|
&CmndZbBind, &CmndZbUnbind, &CmndZbPing, &CmndZbModelId,
|
||||||
&CmndZbLight, &CmndZbRestore, &CmndZbBindState,
|
&CmndZbLight, &CmndZbRestore, &CmndZbBindState,
|
||||||
@ -393,61 +393,107 @@ void zigbeeZCLSendStr(uint16_t shortaddr, uint16_t groupaddr, uint8_t endpoint,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
// Parse "Report" or "Write" attribute
|
||||||
// Command `ZbSend`
|
void ZbSendReportWrite(const JsonVariant &val_pubwrite, uint16_t device, uint16_t groupaddr, uint16_t cluster, uint8_t endpoint, uint16_t manuf, bool write) {
|
||||||
//
|
SBuffer buf(200); // buffer to store the binary output of attibutes
|
||||||
void CmndZbSend(void) {
|
|
||||||
// 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;
|
|
||||||
const JsonObject &json = jsonBuf.parseObject((const char*) XdrvMailbox.data);
|
|
||||||
if (!json.success()) { ResponseCmndChar_P(PSTR(D_JSON_INVALID_JSON)); return; }
|
|
||||||
|
|
||||||
// params
|
const JsonObject &attrs = val_pubwrite.as<const JsonObject&>();
|
||||||
static char delim[] = ", "; // delimiters for parameters
|
// iterate on keys
|
||||||
uint16_t device = BAD_SHORTADDR; // 0x0000 is local, so considered invalid
|
for (JsonObject::const_iterator it=attrs.begin(); it!=attrs.end(); ++it) {
|
||||||
uint16_t groupaddr = 0x0000; // group address
|
const char *key = it->key;
|
||||||
uint8_t endpoint = 0x00; // 0x00 is invalid for the dst endpoint
|
const JsonVariant &value = it->value;
|
||||||
uint16_t manuf = 0x0000; // Manuf Id in ZCL frame
|
|
||||||
// Command elements
|
uint16_t attr_id = 0xFFFF;
|
||||||
uint16_t cluster = 0;
|
uint16_t cluster_id = 0xFFFF;
|
||||||
|
uint8_t type_id = Znodata;
|
||||||
|
|
||||||
|
// check if the name has the format "XXXX/YYYY" where XXXX is the cluster, YYYY the attribute id
|
||||||
|
// alternative "XXXX/YYYY%ZZ" where ZZ is the type (for unregistered attributes)
|
||||||
|
char * delimiter = strchr(key, '/');
|
||||||
|
char * delimiter2 = strchr(key, '%');
|
||||||
|
if (delimiter) {
|
||||||
|
cluster_id = strtoul(key, &delimiter, 16);
|
||||||
|
if (!delimiter2) {
|
||||||
|
attr_id = strtoul(delimiter+1, nullptr, 16);
|
||||||
|
} else {
|
||||||
|
attr_id = strtoul(delimiter+1, &delimiter2, 16);
|
||||||
|
type_id = strtoul(delimiter2+1, nullptr, 16);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// AddLog_P2(LOG_LEVEL_DEBUG, PSTR("cluster_id = 0x%04X, attr_id = 0x%04X"), cluster_id, attr_id);
|
||||||
|
|
||||||
|
// do we already know the type, i.e. attribute and cluster are also known
|
||||||
|
if (Znodata == type_id) {
|
||||||
|
// scan attributes to find by name, and retrieve type
|
||||||
|
for (uint32_t i = 0; i < ARRAY_SIZE(Z_PostProcess); i++) {
|
||||||
|
const Z_AttributeConverter *converter = &Z_PostProcess[i];
|
||||||
|
bool match = false;
|
||||||
|
uint16_t local_attr_id = pgm_read_word(&converter->attribute);
|
||||||
|
uint16_t local_cluster_id = CxToCluster(pgm_read_byte(&converter->cluster_short));
|
||||||
|
uint8_t local_type_id = pgm_read_byte(&converter->type);
|
||||||
|
// AddLog_P2(LOG_LEVEL_DEBUG, PSTR("Try cluster = 0x%04X, attr = 0x%04X, type_id = 0x%02X"), local_cluster_id, local_attr_id, local_type_id);
|
||||||
|
|
||||||
|
if (delimiter) {
|
||||||
|
if ((cluster_id == local_cluster_id) && (attr_id == local_attr_id)) {
|
||||||
|
type_id = local_type_id;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else if (converter->name) {
|
||||||
|
// AddLog_P2(LOG_LEVEL_DEBUG, PSTR("Comparing '%s' with '%s'"), attr_name, converter->name);
|
||||||
|
if (0 == strcasecmp_P(key, converter->name)) {
|
||||||
|
// match
|
||||||
|
cluster_id = local_cluster_id;
|
||||||
|
attr_id = local_attr_id;
|
||||||
|
type_id = local_type_id;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddLog_P2(LOG_LEVEL_DEBUG, PSTR("cluster_id = 0x%04X, attr_id = 0x%04X, type_id = 0x%02X"), cluster_id, attr_id, type_id);
|
||||||
|
if ((0xFFFF == attr_id) || (0xFFFF == cluster_id)) {
|
||||||
|
Response_P(PSTR("{\"%s\":\"%s'%s'\"}"), XdrvMailbox.command, PSTR("Unknown attribute "), key);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (Znodata == type_id) {
|
||||||
|
Response_P(PSTR("{\"%s\":\"%s'%s'\"}"), XdrvMailbox.command, PSTR("Unknown attribute type for attribute "), key);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (0xFFFF == cluster) {
|
||||||
|
cluster = cluster_id; // set the cluster for this packet
|
||||||
|
} else if (cluster != cluster_id) {
|
||||||
|
ResponseCmndChar_P(PSTR("No more than one cluster id per command"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// push the value in the buffer
|
||||||
|
int32_t res = encodeSingleAttribute(buf, value, attr_id, type_id);
|
||||||
|
if (res < 0) {
|
||||||
|
Response_P(PSTR("{\"%s\":\"%s'%s' 0x%02X\"}"), XdrvMailbox.command, PSTR("Unsupported attribute type "), key, type_id);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// did we have any attribute?
|
||||||
|
if (0 == buf.len()) {
|
||||||
|
ResponseCmndChar_P(PSTR("No attribute in list"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// all good, send the packet
|
||||||
|
ZigbeeZCLSend_Raw(device, groupaddr, cluster, endpoint, write ? ZCL_WRITE_ATTRIBUTES : ZCL_REPORT_ATTRIBUTES, false /* not cluster specific */, manuf, buf.getBuffer(), buf.len(), false /* noresponse */, zigbee_devices.getNextSeqNumber(device));
|
||||||
|
ResponseCmndDone();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse the "Send" attribute and send the command
|
||||||
|
void ZbSendSend(const JsonVariant &val_cmd, uint16_t device, uint16_t groupaddr, uint16_t cluster, uint8_t endpoint, uint16_t manuf) {
|
||||||
uint8_t cmd = 0;
|
uint8_t cmd = 0;
|
||||||
String cmd_str = ""; // the actual low-level command, either specified or computed
|
String cmd_str = ""; // the actual low-level command, either specified or computed
|
||||||
const char *cmd_s; // pointer to payload string
|
const char *cmd_s; // pointer to payload string
|
||||||
bool clusterSpecific = true;
|
bool clusterSpecific = true;
|
||||||
|
|
||||||
// parse JSON
|
static char delim[] = ", "; // delimiters for parameters
|
||||||
const JsonVariant &val_device = GetCaseInsensitive(json, PSTR("Device"));
|
|
||||||
if (nullptr != &val_device) {
|
|
||||||
device = zigbee_devices.parseDeviceParam(val_device.as<char*>());
|
|
||||||
if (BAD_SHORTADDR == device) { ResponseCmndChar_P(PSTR("Invalid parameter")); return; }
|
|
||||||
}
|
|
||||||
if (BAD_SHORTADDR == device) { // if not found, check if we have a group
|
|
||||||
const JsonVariant &val_group = GetCaseInsensitive(json, PSTR("Group"));
|
|
||||||
if (nullptr != &val_group) {
|
|
||||||
groupaddr = strToUInt(val_group);
|
|
||||||
} else { // no device nor group
|
|
||||||
ResponseCmndChar_P(PSTR("Unknown device"));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const JsonVariant &val_endpoint = GetCaseInsensitive(json, PSTR("Endpoint"));
|
|
||||||
if (nullptr != &val_endpoint) { endpoint = strToUInt(val_endpoint); }
|
|
||||||
const JsonVariant &val_manuf = GetCaseInsensitive(json, PSTR("Manuf"));
|
|
||||||
if (nullptr != &val_manuf) { manuf = strToUInt(val_manuf); }
|
|
||||||
const JsonVariant &val_cmd = GetCaseInsensitive(json, PSTR("Send"));
|
|
||||||
if (nullptr != &val_cmd) {
|
|
||||||
// probe the type of the argument
|
// probe the type of the argument
|
||||||
// If JSON object, it's high level commands
|
// If JSON object, it's high level commands
|
||||||
// If String, it's a low level command
|
// If String, it's a low level command
|
||||||
@ -550,8 +596,187 @@ void CmndZbSend(void) {
|
|||||||
device, groupaddr, endpoint, cluster, cmd, cmd_s);
|
device, groupaddr, endpoint, cluster, cmd, cmd_s);
|
||||||
zigbeeZCLSendStr(device, groupaddr, endpoint, clusterSpecific, manuf, cluster, cmd, cmd_s);
|
zigbeeZCLSendStr(device, groupaddr, endpoint, clusterSpecific, manuf, cluster, cmd, cmd_s);
|
||||||
ResponseCmndDone();
|
ResponseCmndDone();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Parse the "Send" attribute and send the command
|
||||||
|
void ZbSendRead(const JsonVariant &val_attr, uint16_t device, uint16_t groupaddr, uint16_t cluster, uint8_t endpoint, uint16_t manuf) {
|
||||||
|
// ZbSend {"Device":"0xF289","Cluster":0,"Endpoint":3,"Read":5}
|
||||||
|
// ZbSend {"Device":"0xF289","Cluster":"0x0000","Endpoint":"0x0003","Read":"0x0005"}
|
||||||
|
// ZbSend {"Device":"0xF289","Cluster":0,"Endpoint":3,"Read":[5,6,7,4]}
|
||||||
|
// ZbSend {"Device":"0xF289","Endpoint":3,"Read":{"ModelId":true}}
|
||||||
|
// ZbSend {"Device":"0xF289","Read":{"ModelId":true}}
|
||||||
|
|
||||||
|
// params
|
||||||
|
size_t attrs_len = 0;
|
||||||
|
uint8_t* attrs = nullptr; // empty string is valid
|
||||||
|
|
||||||
|
uint16_t val = strToUInt(val_attr);
|
||||||
|
if (val_attr.is<JsonArray>()) {
|
||||||
|
const JsonArray& attr_arr = val_attr.as<const JsonArray&>();
|
||||||
|
attrs_len = attr_arr.size() * 2;
|
||||||
|
attrs = new uint8_t[attrs_len];
|
||||||
|
|
||||||
|
uint32_t i = 0;
|
||||||
|
for (auto value : attr_arr) {
|
||||||
|
uint16_t val = strToUInt(value);
|
||||||
|
attrs[i++] = val & 0xFF;
|
||||||
|
attrs[i++] = val >> 8;
|
||||||
|
}
|
||||||
|
} else if (val_attr.is<JsonObject>()) {
|
||||||
|
const JsonObject& attr_obj = val_attr.as<const JsonObject&>();
|
||||||
|
attrs_len = attr_obj.size() * 2;
|
||||||
|
attrs = new uint8_t[attrs_len];
|
||||||
|
uint32_t actual_attr_len = 0;
|
||||||
|
|
||||||
|
// iterate on keys
|
||||||
|
for (JsonObject::const_iterator it=attr_obj.begin(); it!=attr_obj.end(); ++it) {
|
||||||
|
const char *key = it->key;
|
||||||
|
// const JsonVariant &value = it->value; // we don't need the value here, only keys are relevant
|
||||||
|
|
||||||
|
bool found = false;
|
||||||
|
// scan attributes to find by name, and retrieve type
|
||||||
|
for (uint32_t i = 0; i < ARRAY_SIZE(Z_PostProcess); i++) {
|
||||||
|
const Z_AttributeConverter *converter = &Z_PostProcess[i];
|
||||||
|
bool match = false;
|
||||||
|
uint16_t local_attr_id = pgm_read_word(&converter->attribute);
|
||||||
|
uint16_t local_cluster_id = CxToCluster(pgm_read_byte(&converter->cluster_short));
|
||||||
|
// uint8_t local_type_id = pgm_read_byte(&converter->type);
|
||||||
|
|
||||||
|
if ((converter->name) && (0 == strcasecmp_P(key, converter->name))) {
|
||||||
|
// match name
|
||||||
|
// check if there is a conflict with cluster
|
||||||
|
// TODO
|
||||||
|
attrs[actual_attr_len++] = local_attr_id & 0xFF;
|
||||||
|
attrs[actual_attr_len++] = local_attr_id >> 8;
|
||||||
|
found = true;
|
||||||
|
break; // found, exit loop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!found) {
|
||||||
|
AddLog_P2(LOG_LEVEL_INFO, PSTR("ZIG: Unknown attribute name (ignored): %s"), key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
attrs_len = actual_attr_len;
|
||||||
} else {
|
} else {
|
||||||
Response_P(PSTR("Missing zigbee 'Send'"));
|
attrs_len = 2;
|
||||||
|
attrs = new uint8_t[attrs_len];
|
||||||
|
attrs[0] = val & 0xFF; // little endian
|
||||||
|
attrs[1] = val >> 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (attrs_len > 0) {
|
||||||
|
ZigbeeZCLSend_Raw(device, groupaddr, cluster, endpoint, ZCL_READ_ATTRIBUTES, false, manuf, attrs, attrs_len, true /* we do want a response */, zigbee_devices.getNextSeqNumber(device));
|
||||||
|
ResponseCmndDone();
|
||||||
|
} else {
|
||||||
|
ResponseCmndChar_P(PSTR("Missing parameters"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (attrs) { delete[] attrs; }
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Command `ZbSend`
|
||||||
|
//
|
||||||
|
// Examples:
|
||||||
|
// ZbSend {"Device":"0x0000","Endpoint":1,"Write":{"0006/0000":0}}
|
||||||
|
// ZbSend {"Device":"0x0000","Endpoint":1,"Write":{"Power":0}}
|
||||||
|
// ZbSend {"Device":"0x0000","Endpoint":1,"Write":{"AqaraRotate":0}}
|
||||||
|
// ZbSend {"Device":"0x0000","Endpoint":1,"Write":{"AqaraRotate":12.5}}
|
||||||
|
// ZbSend {"Device":"0x0000","Endpoint":1,"Write":{"006/0000%39":12.5}}
|
||||||
|
// ZbSend {"Device":"0x0000","Endpoint":1,"Write":{"AnalogInApplicationType":1000000}}
|
||||||
|
// ZbSend {"Device":"0x0000","Endpoint":1,"Write":{"TimeZone":-1000000}}
|
||||||
|
// ZbSend {"Device":"0x0000","Endpoint":1,"Write":{"Manufacturer":"Tasmota","ModelId":"Tasmota Z2T Router"}}
|
||||||
|
void CmndZbSend(void) {
|
||||||
|
// 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 { "device":"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;
|
||||||
|
const JsonObject &json = jsonBuf.parseObject((const char*) XdrvMailbox.data);
|
||||||
|
if (!json.success()) { ResponseCmndChar_P(PSTR(D_JSON_INVALID_JSON)); return; }
|
||||||
|
|
||||||
|
// params
|
||||||
|
uint16_t device = BAD_SHORTADDR; // BAD_SHORTADDR is broadcast, so considered invalid
|
||||||
|
uint16_t groupaddr = 0x0000; // group address valid only if device == BAD_SHORTADDR
|
||||||
|
uint16_t cluster = 0xFFFF; // no default
|
||||||
|
uint8_t endpoint = 0x00; // 0x00 is invalid for the dst endpoint
|
||||||
|
uint16_t manuf = 0x0000; // Manuf Id in ZCL frame
|
||||||
|
|
||||||
|
|
||||||
|
// parse "Device" and "Group"
|
||||||
|
const JsonVariant &val_device = GetCaseInsensitive(json, PSTR(D_CMND_ZIGBEE_DEVICE));
|
||||||
|
if (nullptr != &val_device) {
|
||||||
|
device = zigbee_devices.parseDeviceParam(val_device.as<char*>());
|
||||||
|
if (BAD_SHORTADDR == device) { ResponseCmndChar_P(PSTR("Invalid parameter")); return; }
|
||||||
|
}
|
||||||
|
if (BAD_SHORTADDR == device) { // if not found, check if we have a group
|
||||||
|
const JsonVariant &val_group = GetCaseInsensitive(json, PSTR(D_CMND_ZIGBEE_GROUP));
|
||||||
|
if (nullptr != &val_group) {
|
||||||
|
groupaddr = strToUInt(val_group);
|
||||||
|
} else { // no device nor group
|
||||||
|
ResponseCmndChar_P(PSTR("Unknown device"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// read other parameters
|
||||||
|
const JsonVariant &val_cluster = GetCaseInsensitive(json, PSTR(D_CMND_ZIGBEE_CLUSTER));
|
||||||
|
if (nullptr != &val_cluster) { cluster = strToUInt(val_cluster); }
|
||||||
|
const JsonVariant &val_endpoint = GetCaseInsensitive(json, PSTR(D_CMND_ZIGBEE_ENDPOINT));
|
||||||
|
if (nullptr != &val_endpoint) { endpoint = strToUInt(val_endpoint); }
|
||||||
|
const JsonVariant &val_manuf = GetCaseInsensitive(json, PSTR(D_CMND_ZIGBEE_MANUF));
|
||||||
|
if (nullptr != &val_manuf) { manuf = strToUInt(val_manuf); }
|
||||||
|
|
||||||
|
// infer endpoint
|
||||||
|
if (BAD_SHORTADDR == device) {
|
||||||
|
endpoint = 0xFF; // endpoint not used for group addresses
|
||||||
|
} else if (0 == endpoint) {
|
||||||
|
endpoint = zigbee_devices.findFirstEndpoint(device);
|
||||||
|
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ZIG: guessing endpoint %d"), endpoint);
|
||||||
|
}
|
||||||
|
|
||||||
|
const JsonVariant &val_cmd = GetCaseInsensitive(json, PSTR(D_CMND_ZIGBEE_SEND));
|
||||||
|
const JsonVariant &val_read = GetCaseInsensitive(json, PSTR(D_CMND_ZIGBEE_READ));
|
||||||
|
const JsonVariant &val_write = GetCaseInsensitive(json, PSTR(D_CMND_ZIGBEE_WRITE));
|
||||||
|
const JsonVariant &val_publish = GetCaseInsensitive(json, PSTR(D_CMND_ZIGBEE_REPORT));
|
||||||
|
uint32_t multi_cmd = (nullptr != &val_cmd) + (nullptr != &val_read) + (nullptr != &val_write) + (nullptr != &val_publish);
|
||||||
|
if (multi_cmd > 1) {
|
||||||
|
ResponseCmndChar_P(PSTR("Can only have one of: 'Send', 'Read', 'Write' or 'Report'"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nullptr != &val_cmd) {
|
||||||
|
// "Send":{...commands...}
|
||||||
|
ZbSendSend(val_cmd, device, groupaddr, cluster, endpoint, manuf);
|
||||||
|
} else if (nullptr != &val_read) {
|
||||||
|
// "Read":{...attributes...}, "Read":attribute or "Read":[...attributes...]
|
||||||
|
ZbSendRead(val_read, device, groupaddr, cluster, endpoint, manuf);
|
||||||
|
} else if (nullptr != &val_write) {
|
||||||
|
if ((0 == endpoint) || (!val_write.is<JsonObject>())) {
|
||||||
|
ResponseCmndChar_P(PSTR("Missing parameters"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// "Write":{...attributes...}
|
||||||
|
ZbSendReportWrite(val_write, device, groupaddr, cluster, endpoint, manuf, true /* write */);
|
||||||
|
} else if (nullptr != &val_publish) {
|
||||||
|
if ((0 == endpoint) || (!val_publish.is<JsonObject>())) {
|
||||||
|
ResponseCmndChar_P(PSTR("Missing parameters"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// "Report":{...attributes...}
|
||||||
|
ZbSendReportWrite(val_publish, device, groupaddr, cluster, endpoint, manuf, false /* report */);
|
||||||
|
} else {
|
||||||
|
Response_P(PSTR("Missing zigbee 'Send', 'Write' or 'Report'"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -570,7 +795,6 @@ void ZbBindUnbind(bool unbind) { // false = bind, true = unbind
|
|||||||
if (!json.success()) { ResponseCmndChar_P(PSTR(D_JSON_INVALID_JSON)); return; }
|
if (!json.success()) { ResponseCmndChar_P(PSTR(D_JSON_INVALID_JSON)); return; }
|
||||||
|
|
||||||
// params
|
// params
|
||||||
// static char delim[] = ", "; // delimiters for parameters
|
|
||||||
uint16_t srcDevice = BAD_SHORTADDR; // BAD_SHORTADDR is broadcast, so considered invalid
|
uint16_t srcDevice = BAD_SHORTADDR; // BAD_SHORTADDR is broadcast, so considered invalid
|
||||||
uint16_t dstDevice = BAD_SHORTADDR; // BAD_SHORTADDR is broadcast, so considered invalid
|
uint16_t dstDevice = BAD_SHORTADDR; // BAD_SHORTADDR is broadcast, so considered invalid
|
||||||
uint64_t dstLongAddr = 0;
|
uint64_t dstLongAddr = 0;
|
||||||
@ -582,7 +806,7 @@ void ZbBindUnbind(bool unbind) { // false = bind, true = unbind
|
|||||||
|
|
||||||
// Information about source device: "Device", "Endpoint", "Cluster"
|
// Information about source device: "Device", "Endpoint", "Cluster"
|
||||||
// - the source endpoint must have a known IEEE address
|
// - the source endpoint must have a known IEEE address
|
||||||
const JsonVariant &val_device = GetCaseInsensitive(json, PSTR("Device"));
|
const JsonVariant &val_device = GetCaseInsensitive(json, PSTR(D_CMND_ZIGBEE_DEVICE));
|
||||||
if (nullptr != &val_device) {
|
if (nullptr != &val_device) {
|
||||||
srcDevice = zigbee_devices.parseDeviceParam(val_device.as<char*>());
|
srcDevice = zigbee_devices.parseDeviceParam(val_device.as<char*>());
|
||||||
}
|
}
|
||||||
@ -591,10 +815,10 @@ void ZbBindUnbind(bool unbind) { // false = bind, true = unbind
|
|||||||
uint64_t srcLongAddr = zigbee_devices.getDeviceLongAddr(srcDevice);
|
uint64_t srcLongAddr = zigbee_devices.getDeviceLongAddr(srcDevice);
|
||||||
if (0 == srcLongAddr) { ResponseCmndChar_P(PSTR("Unknown source IEEE address")); return; }
|
if (0 == srcLongAddr) { ResponseCmndChar_P(PSTR("Unknown source IEEE address")); return; }
|
||||||
// look for source endpoint
|
// look for source endpoint
|
||||||
const JsonVariant &val_endpoint = GetCaseInsensitive(json, PSTR("Endpoint"));
|
const JsonVariant &val_endpoint = GetCaseInsensitive(json, PSTR(D_CMND_ZIGBEE_ENDPOINT));
|
||||||
if (nullptr != &val_endpoint) { endpoint = strToUInt(val_endpoint); }
|
if (nullptr != &val_endpoint) { endpoint = strToUInt(val_endpoint); }
|
||||||
// look for source cluster
|
// look for source cluster
|
||||||
const JsonVariant &val_cluster = GetCaseInsensitive(json, PSTR("Cluster"));
|
const JsonVariant &val_cluster = GetCaseInsensitive(json, PSTR(D_CMND_ZIGBEE_CLUSTER));
|
||||||
if (nullptr != &val_cluster) { cluster = strToUInt(val_cluster); }
|
if (nullptr != &val_cluster) { cluster = strToUInt(val_cluster); }
|
||||||
|
|
||||||
// Either Device address
|
// Either Device address
|
||||||
@ -885,90 +1109,6 @@ void CmndZbRestore(void) {
|
|||||||
ResponseCmndDone();
|
ResponseCmndDone();
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
|
||||||
// Command `ZbRead`
|
|
||||||
// Send an attribute read command to a device, specifying cluster and list of attributes
|
|
||||||
//
|
|
||||||
void CmndZbRead(void) {
|
|
||||||
// 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((const char*) XdrvMailbox.data);
|
|
||||||
if (!json.success()) { ResponseCmndChar_P(PSTR(D_JSON_INVALID_JSON)); return; }
|
|
||||||
|
|
||||||
// params
|
|
||||||
uint16_t device = BAD_SHORTADDR; // BAD_SHORTADDR is broadcast, so considered invalid
|
|
||||||
uint16_t groupaddr = 0x0000; // if 0x0000 ignore group adress
|
|
||||||
uint16_t cluster = 0x0000; // default to general cluster
|
|
||||||
uint8_t endpoint = 0x00; // 0x00 is invalid for the dst endpoint
|
|
||||||
uint16_t manuf = 0x0000; // Manuf Id in ZCL frame
|
|
||||||
size_t attrs_len = 0;
|
|
||||||
uint8_t* attrs = nullptr; // empty string is valid
|
|
||||||
|
|
||||||
const JsonVariant &val_device = GetCaseInsensitive(json, PSTR("Device"));
|
|
||||||
if (nullptr != &val_device) {
|
|
||||||
device = zigbee_devices.parseDeviceParam(val_device.as<char*>());
|
|
||||||
if (BAD_SHORTADDR == device) { ResponseCmndChar_P(PSTR("Invalid parameter")); return; }
|
|
||||||
}
|
|
||||||
if (BAD_SHORTADDR == device) { // if not found, check if we have a group
|
|
||||||
const JsonVariant &val_group = GetCaseInsensitive(json, PSTR("Group"));
|
|
||||||
if (nullptr != &val_group) {
|
|
||||||
groupaddr = strToUInt(val_group);
|
|
||||||
} else { // no device nor group
|
|
||||||
ResponseCmndChar_P(PSTR("Unknown device"));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const JsonVariant &val_cluster = GetCaseInsensitive(json, PSTR("Cluster"));
|
|
||||||
if (nullptr != &val_cluster) { cluster = strToUInt(val_cluster); }
|
|
||||||
const JsonVariant &val_endpoint = GetCaseInsensitive(json, PSTR("Endpoint"));
|
|
||||||
if (nullptr != &val_endpoint) { endpoint = strToUInt(val_endpoint); }
|
|
||||||
const JsonVariant &val_manuf = GetCaseInsensitive(json, PSTR("Manuf"));
|
|
||||||
if (nullptr != &val_manuf) { manuf = strToUInt(val_manuf); }
|
|
||||||
|
|
||||||
const JsonVariant &val_attr = GetCaseInsensitive(json, PSTR("Read"));
|
|
||||||
if (nullptr != &val_attr) {
|
|
||||||
uint16_t val = strToUInt(val_attr);
|
|
||||||
if (val_attr.is<JsonArray>()) {
|
|
||||||
const JsonArray& attr_arr = val_attr.as<const JsonArray&>();
|
|
||||||
attrs_len = attr_arr.size() * 2;
|
|
||||||
attrs = new uint8_t[attrs_len];
|
|
||||||
|
|
||||||
uint32_t i = 0;
|
|
||||||
for (auto value : attr_arr) {
|
|
||||||
uint16_t val = strToUInt(value);
|
|
||||||
attrs[i++] = val & 0xFF;
|
|
||||||
attrs[i++] = val >> 8;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
attrs_len = 2;
|
|
||||||
attrs = new uint8_t[attrs_len];
|
|
||||||
attrs[0] = val & 0xFF; // little endian
|
|
||||||
attrs[1] = val >> 8;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((0 == endpoint) && (device)) { // try to compute the endpoint
|
|
||||||
endpoint = zigbee_devices.findFirstEndpoint(device);
|
|
||||||
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ZbRead: guessing endpoint 0x%02X"), endpoint);
|
|
||||||
}
|
|
||||||
if (BAD_SHORTADDR == device) {
|
|
||||||
endpoint = 0xFF; // endpoint not used for group addresses
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((0 != endpoint) && (attrs_len > 0)) {
|
|
||||||
ZigbeeZCLSend_Raw(device, groupaddr, cluster, endpoint, ZCL_READ_ATTRIBUTES, false, manuf, attrs, attrs_len, true /* we do want a response */, zigbee_devices.getNextSeqNumber(device));
|
|
||||||
ResponseCmndDone();
|
|
||||||
} else {
|
|
||||||
ResponseCmndChar_P(PSTR("Missing parameters"));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (attrs) { delete[] attrs; }
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// Command `ZbPermitJoin`
|
// Command `ZbPermitJoin`
|
||||||
// Allow or Deny pairing of new Zigbee devices
|
// Allow or Deny pairing of new Zigbee devices
|
||||||
|
Loading…
x
Reference in New Issue
Block a user