mirror of https://github.com/arendst/Tasmota.git
Merge pull request #6824 from s-hadinger/zigbee_1_0_RC1
Zigbee command support, considered as v1.0 for full Zigbee support
This commit is contained in:
commit
58959349fe
|
@ -4,6 +4,7 @@
|
|||
* Add support for Honeywell I2C HIH series Humidity and Temperetaure sensor (#6808)
|
||||
* Fix wrong Dimmer behavior introduced with #6799 when SetOption37 < 128
|
||||
* Change add DS18x20 support in Tasmota-IR
|
||||
* Add Zigbee command support, considered as v1.0 for full Zigbee support
|
||||
*
|
||||
* 7.0.0.1 20191027
|
||||
* Remove references to versions before 6.0
|
||||
|
|
|
@ -456,6 +456,7 @@
|
|||
#define D_JSON_TUYA_MCU_RECEIVED "TuyaReceived"
|
||||
|
||||
// Commands xdrv_23_zigbee.ino
|
||||
#define D_ZIGBEE_NOT_STARTED "Zigbee not started (yet)"
|
||||
#define D_CMND_ZIGBEE_PERMITJOIN "ZigbeePermitJoin"
|
||||
#define D_CMND_ZIGBEE_STATUS "ZigbeeStatus"
|
||||
#define D_CMND_ZIGBEE_RESET "ZigbeeReset"
|
||||
|
@ -468,12 +469,12 @@
|
|||
#define D_JSON_ZIGBEEZCL_RAW_RECEIVED "ZigbeeZCLRawReceived"
|
||||
#define D_JSON_ZIGBEE_DEVICE "Device"
|
||||
#define D_JSON_ZIGBEE_NAME "Name"
|
||||
#define D_CMND_ZIGBEE_ZCL_SEND "ZigbeeZCLSend"
|
||||
#define D_JSON_ZIGBEE_ZCL_SENT "ZigbeeZCLSent"
|
||||
#define D_CMND_ZIGBEE_PROBE "ZigbeeProbe"
|
||||
#define D_CMND_ZIGBEE_RECEIVED "ZigbeeReceived"
|
||||
#define D_CMND_ZIGBEE_LINKQUALITY "LinkQuality"
|
||||
#define D_CMND_ZIGBEE_READ "ZigbeeRead"
|
||||
#define D_CMND_ZIGBEE_SEND "ZigbeeSend"
|
||||
#define D_JSON_ZIGBEE_ZCL_SENT "ZigbeeZCLSent"
|
||||
|
||||
// Commands xdrv_25_A4988_Stepper.ino
|
||||
#ifdef USE_A4988_STEPPER
|
||||
|
|
|
@ -68,7 +68,7 @@ public:
|
|||
void updateLastSeen(uint16_t shortaddr);
|
||||
|
||||
// Dump json
|
||||
String dump(uint8_t dump_mode) const;
|
||||
String dump(uint32_t dump_mode, int32_t device_num = 0) const;
|
||||
|
||||
private:
|
||||
std::vector<Z_Device> _devices = {};
|
||||
|
@ -349,13 +349,21 @@ void Z_Devices::updateLastSeen(uint16_t shortaddr) {
|
|||
// Dump the internal memory of Zigbee devices
|
||||
// Mode = 1: simple dump of devices addresses and names
|
||||
// Mode = 2: Mode 1 + also dump the endpoints, profiles and clusters
|
||||
String Z_Devices::dump(uint8_t dump_mode) const {
|
||||
String Z_Devices::dump(uint32_t dump_mode, int32_t device_num) const {
|
||||
DynamicJsonBuffer jsonBuffer;
|
||||
JsonArray& json = jsonBuffer.createArray();
|
||||
JsonArray& devices = json;
|
||||
//JsonArray& devices = json.createNestedArray(F("ZigbeeDevices"));
|
||||
|
||||
for (std::vector<Z_Device>::const_iterator it = _devices.begin(); it != _devices.end(); ++it) {
|
||||
// if device_num == 0, then we show all devices.
|
||||
// When no payload, the default is -99. In this case change it to 0.
|
||||
if (device_num < 0) { device_num = 0; }
|
||||
|
||||
uint32_t device_current = 1;
|
||||
for (std::vector<Z_Device>::const_iterator it = _devices.begin(); it != _devices.end(); ++it, ++device_current) {
|
||||
// ignore non-current device, if specified device is non-zero
|
||||
if ((device_num > 0) && (device_num != device_current)) { continue; }
|
||||
|
||||
const Z_Device& device = *it;
|
||||
uint16_t shortaddr = device.shortaddr;
|
||||
char hex[20];
|
||||
|
@ -369,7 +377,7 @@ String Z_Devices::dump(uint8_t dump_mode) const {
|
|||
dev[F(D_JSON_ZIGBEE_NAME)] = device.friendlyName;
|
||||
}
|
||||
|
||||
if (1 == dump_mode) {
|
||||
if (2 <= dump_mode) {
|
||||
Uint64toHex(device.longaddr, hex, 64);
|
||||
dev[F("IEEEAddr")] = hex;
|
||||
if (device.modelId.length() > 0) {
|
||||
|
@ -381,7 +389,7 @@ String Z_Devices::dump(uint8_t dump_mode) const {
|
|||
}
|
||||
|
||||
// If dump_mode == 2, dump a lot more details
|
||||
if (2 == dump_mode) {
|
||||
if (3 <= dump_mode) {
|
||||
JsonObject& dev_endpoints = dev.createNestedObject(F("Endpoints"));
|
||||
for (std::vector<uint32_t>::const_iterator ite = device.endpoints.begin() ; ite != device.endpoints.end(); ++ite) {
|
||||
uint32_t ep_profile = *ite;
|
||||
|
|
|
@ -209,12 +209,19 @@ uint32_t parseSingleAttribute(JsonObject& json, char *attrid_str, class SBuffer
|
|||
}
|
||||
}
|
||||
break;
|
||||
// Note: uint40, uint48, uint56, uint64 are not used in ZCL, so they are not implemented (yet)
|
||||
case 0x24: // int40
|
||||
case 0x25: // int48
|
||||
case 0x26: // int56
|
||||
case 0x27: // int64
|
||||
i += attrtype - 0x1F; // 5 - 8;
|
||||
// Note: uint40, uint48, uint56, uint64 are stored as Hex
|
||||
case 0x24: // uint40
|
||||
case 0x25: // uint48
|
||||
case 0x26: // uint56
|
||||
case 0x27: // uint64
|
||||
{
|
||||
uint8_t len = attrtype - 0x1F; // 5 - 8
|
||||
// print as HEX
|
||||
char hex[2*len+1];
|
||||
ToHex_P(buf.buf(i), len, hex, sizeof(hex));
|
||||
json[attrid_str] = hex;
|
||||
i += len;
|
||||
}
|
||||
break;
|
||||
case 0x28: // uint8
|
||||
{
|
||||
|
@ -243,12 +250,19 @@ uint32_t parseSingleAttribute(JsonObject& json, char *attrid_str, class SBuffer
|
|||
}
|
||||
}
|
||||
break;
|
||||
// Note: int40, int48, int56, int64 are not used in ZCL, so they are not implemented (yet)
|
||||
// Note: int40, int48, int56, int64 are not stored as Hex
|
||||
case 0x2C: // int40
|
||||
case 0x2D: // int48
|
||||
case 0x2E: // int56
|
||||
case 0x2F: // int64
|
||||
i += attrtype - 0x27; // 5 - 8;
|
||||
{
|
||||
uint8_t len = attrtype - 0x27; // 5 - 8
|
||||
// print as HEX
|
||||
char hex[2*len+1];
|
||||
ToHex_P(buf.buf(i), len, hex, sizeof(hex));
|
||||
json[attrid_str] = hex;
|
||||
i += len;
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x41: // octet string, 1 byte len
|
||||
|
@ -452,10 +466,6 @@ typedef struct Z_AttributeConverter {
|
|||
|
||||
// list of post-processing directives
|
||||
const Z_AttributeConverter Z_PostProcess[] PROGMEM = {
|
||||
// { 0x0000, 0x0004, "Manufacturer", &Z_ManufKeep }, // record Manufacturer
|
||||
// { 0x0000, 0x0005, D_JSON_MODEL D_JSON_ID, &Z_ModelKeep }, // record Model
|
||||
// { 0x0405, 0x0000, D_JSON_HUMIDITY, &Z_FloatDiv100 }, // Humidity
|
||||
|
||||
{ 0x0000, 0x0000, "ZCLVersion", &Z_Copy },
|
||||
{ 0x0000, 0x0001, "AppVersion", &Z_Copy },
|
||||
{ 0x0000, 0x0002, "StackVersion", &Z_Copy },
|
||||
|
@ -466,51 +476,36 @@ const Z_AttributeConverter Z_PostProcess[] PROGMEM = {
|
|||
{ 0x0000, 0x0007, "PowerSource", &Z_Copy },
|
||||
{ 0x0000, 0x4000, "SWBuildID", &Z_Copy },
|
||||
{ 0x0000, 0xFFFF, nullptr, &Z_Remove }, // Remove all other values
|
||||
// Cmd 0x0A - Cluster 0x0000, attribute 0xFF01 - proprietary
|
||||
{ 0x0000, 0xFF01, nullptr, &Z_AqaraSensor }, // Occupancy (map8)
|
||||
|
||||
// Color Control cluster
|
||||
{ 0x0003, 0x0000, "CurrentHue", &Z_Copy },
|
||||
{ 0x0003, 0x0001, "CurrentSaturation", &Z_Copy },
|
||||
{ 0x0003, 0x0002, "RemainingTime", &Z_Copy },
|
||||
{ 0x0003, 0x0003, "CurrentX", &Z_Copy },
|
||||
{ 0x0003, 0x0004, "CurrentY", &Z_Copy },
|
||||
{ 0x0003, 0x0005, "DriftCompensation", &Z_Copy },
|
||||
{ 0x0003, 0x0006, "CompensationText", &Z_Copy },
|
||||
{ 0x0003, 0x0007, "ColorTemperatureMireds",&Z_Copy },
|
||||
{ 0x0003, 0x0008, "ColorMode", &Z_Copy },
|
||||
{ 0x0003, 0x0010, "NumberOfPrimaries", &Z_Copy },
|
||||
{ 0x0003, 0x0011, "Primary1X", &Z_Copy },
|
||||
{ 0x0003, 0x0012, "Primary1Y", &Z_Copy },
|
||||
{ 0x0003, 0x0013, "Primary1Intensity", &Z_Copy },
|
||||
{ 0x0003, 0x0015, "Primary2X", &Z_Copy },
|
||||
{ 0x0003, 0x0016, "Primary2Y", &Z_Copy },
|
||||
{ 0x0003, 0x0017, "Primary2Intensity", &Z_Copy },
|
||||
{ 0x0003, 0x0019, "Primary3X", &Z_Copy },
|
||||
{ 0x0003, 0x001A, "Primary3Y", &Z_Copy },
|
||||
{ 0x0003, 0x001B, "Primary3Intensity", &Z_Copy },
|
||||
{ 0x0003, 0x0030, "WhitePointX", &Z_Copy },
|
||||
{ 0x0003, 0x0031, "WhitePointY", &Z_Copy },
|
||||
{ 0x0003, 0x0032, "ColorPointRX", &Z_Copy },
|
||||
{ 0x0003, 0x0033, "ColorPointRY", &Z_Copy },
|
||||
{ 0x0003, 0x0034, "ColorPointRIntensity", &Z_Copy },
|
||||
{ 0x0003, 0x0036, "ColorPointGX", &Z_Copy },
|
||||
{ 0x0003, 0x0037, "ColorPointGY", &Z_Copy },
|
||||
{ 0x0003, 0x0038, "ColorPointGIntensity", &Z_Copy },
|
||||
{ 0x0003, 0x003A, "ColorPointBX", &Z_Copy },
|
||||
{ 0x0003, 0x003B, "ColorPointBY", &Z_Copy },
|
||||
{ 0x0003, 0x003C, "ColorPointBIntensity", &Z_Copy },
|
||||
// Power Configuration cluster
|
||||
{ 0x0001, 0x0000, "MainsVoltage", &Z_Copy },
|
||||
{ 0x0001, 0x0001, "MainsFrequency", &Z_Copy },
|
||||
{ 0x0001, 0x0020, "BatteryVoltage", &Z_Copy },
|
||||
{ 0x0001, 0x0021, "BatteryPercentageRemaining",&Z_Copy },
|
||||
|
||||
// Device Temperature Configuration cluster
|
||||
{ 0x0002, 0x0000, "CurrentTemperature", &Z_Copy },
|
||||
{ 0x0002, 0x0001, "MinTempExperienced", &Z_Copy },
|
||||
{ 0x0002, 0x0002, "MaxTempExperienced", &Z_Copy },
|
||||
{ 0x0002, 0x0003, "OverTempTotalDwell", &Z_Copy },
|
||||
|
||||
// On/off cluster
|
||||
{ 0x0006, 0x0000, "Power", &Z_Copy },
|
||||
|
||||
// On/Off Switch Configuration cluster
|
||||
{ 0x0007, 0x0000, "SwitchType", &Z_Copy },
|
||||
|
||||
// Level Control cluster
|
||||
{ 0x0008, 0x0000, "CurrentLevel", &Z_Copy },
|
||||
{ 0x0008, 0x0001, "RemainingTime", &Z_Copy },
|
||||
{ 0x0008, 0x0010, "OnOffTransitionTime", &Z_Copy },
|
||||
{ 0x0008, 0x0011, "OnLevel", &Z_Copy },
|
||||
{ 0x0008, 0x0012, "OnTransitionTime", &Z_Copy },
|
||||
{ 0x0008, 0x0013, "OffTransitionTime", &Z_Copy },
|
||||
{ 0x0008, 0x0014, "DefaultMoveRate", &Z_Copy },
|
||||
// { 0x0008, 0x0001, "RemainingTime", &Z_Copy },
|
||||
// { 0x0008, 0x0010, "OnOffTransitionTime", &Z_Copy },
|
||||
// { 0x0008, 0x0011, "OnLevel", &Z_Copy },
|
||||
// { 0x0008, 0x0012, "OnTransitionTime", &Z_Copy },
|
||||
// { 0x0008, 0x0013, "OffTransitionTime", &Z_Copy },
|
||||
// { 0x0008, 0x0014, "DefaultMoveRate", &Z_Copy },
|
||||
|
||||
// Alarms cluster
|
||||
{ 0x0009, 0x0000, "AlarmCount", &Z_Copy },
|
||||
// Time cluster
|
||||
|
@ -602,11 +597,12 @@ const Z_AttributeConverter Z_PostProcess[] PROGMEM = {
|
|||
{ 0x0014, 0x0068, "RelinquishDefault", &Z_Copy },
|
||||
{ 0x0014, 0x006F, "StatusFlags", &Z_Copy },
|
||||
{ 0x0014, 0x0100, "ApplicationType", &Z_Copy },
|
||||
// Diagnostics cluster
|
||||
{ 0x0B05, 0x0000, "NumberOfResets", &Z_Copy },
|
||||
{ 0x0B05, 0x0001, "PersistentMemoryWrites",&Z_Copy },
|
||||
{ 0x0B05, 0x011C, "LastMessageLQI", &Z_Copy },
|
||||
{ 0x0B05, 0x011D, "LastMessageRSSI", &Z_Copy },
|
||||
// Power Profile cluster
|
||||
{ 0x001A, 0x0000, "TotalProfileNum", &Z_Copy },
|
||||
{ 0x001A, 0x0001, "MultipleScheduling", &Z_Copy },
|
||||
{ 0x001A, 0x0002, "EnergyFormatting", &Z_Copy },
|
||||
{ 0x001A, 0x0003, "EnergyRemote", &Z_Copy },
|
||||
{ 0x001A, 0x0004, "ScheduleMode", &Z_Copy },
|
||||
// Poll Control cluster
|
||||
{ 0x0020, 0x0000, "CheckinInterval", &Z_Copy },
|
||||
{ 0x0020, 0x0001, "LongPollInterval", &Z_Copy },
|
||||
|
@ -651,24 +647,39 @@ const Z_AttributeConverter Z_PostProcess[] PROGMEM = {
|
|||
{ 0x0102, 0x0018, "IntermediateSetpointsLift",&Z_Copy },
|
||||
{ 0x0102, 0x0019, "IntermediateSetpointsTilt",&Z_Copy },
|
||||
|
||||
// Power Profile cluster
|
||||
{ 0x001A, 0x0000, "TotalProfileNum", &Z_Copy },
|
||||
{ 0x001A, 0x0001, "MultipleScheduling", &Z_Copy },
|
||||
{ 0x001A, 0x0002, "EnergyFormatting", &Z_Copy },
|
||||
{ 0x001A, 0x0003, "EnergyRemote", &Z_Copy },
|
||||
{ 0x001A, 0x0004, "ScheduleMode", &Z_Copy },
|
||||
// Meter Identification cluster
|
||||
{ 0x0B01, 0x0000, "CompanyName", &Z_Copy },
|
||||
{ 0x0B01, 0x0001, "MeterTypeID", &Z_Copy },
|
||||
{ 0x0B01, 0x0004, "DataQualityID", &Z_Copy },
|
||||
{ 0x0B01, 0x0005, "CustomerName", &Z_Copy },
|
||||
{ 0x0B01, 0x0006, "Model", &Z_Copy },
|
||||
{ 0x0B01, 0x0007, "PartNumber", &Z_Copy },
|
||||
{ 0x0B01, 0x000A, "SoftwareRevision", &Z_Copy },
|
||||
{ 0x0B01, 0x000C, "POD", &Z_Copy },
|
||||
{ 0x0B01, 0x000D, "AvailablePower", &Z_Copy },
|
||||
{ 0x0B01, 0x000E, "PowerThreshold", &Z_Copy },
|
||||
// Color Control cluster
|
||||
{ 0x0300, 0x0000, "CurrentHue", &Z_Copy },
|
||||
{ 0x0300, 0x0001, "CurrentSaturation", &Z_Copy },
|
||||
{ 0x0300, 0x0002, "RemainingTime", &Z_Copy },
|
||||
{ 0x0300, 0x0003, "CurrentX", &Z_Copy },
|
||||
{ 0x0300, 0x0004, "CurrentY", &Z_Copy },
|
||||
{ 0x0300, 0x0005, "DriftCompensation", &Z_Copy },
|
||||
{ 0x0300, 0x0006, "CompensationText", &Z_Copy },
|
||||
{ 0x0300, 0x0007, "ColorTemperatureMireds",&Z_Copy },
|
||||
{ 0x0300, 0x0008, "ColorMode", &Z_Copy },
|
||||
{ 0x0300, 0x0010, "NumberOfPrimaries", &Z_Copy },
|
||||
{ 0x0300, 0x0011, "Primary1X", &Z_Copy },
|
||||
{ 0x0300, 0x0012, "Primary1Y", &Z_Copy },
|
||||
{ 0x0300, 0x0013, "Primary1Intensity", &Z_Copy },
|
||||
{ 0x0300, 0x0015, "Primary2X", &Z_Copy },
|
||||
{ 0x0300, 0x0016, "Primary2Y", &Z_Copy },
|
||||
{ 0x0300, 0x0017, "Primary2Intensity", &Z_Copy },
|
||||
{ 0x0300, 0x0019, "Primary3X", &Z_Copy },
|
||||
{ 0x0300, 0x001A, "Primary3Y", &Z_Copy },
|
||||
{ 0x0300, 0x001B, "Primary3Intensity", &Z_Copy },
|
||||
{ 0x0300, 0x0030, "WhitePointX", &Z_Copy },
|
||||
{ 0x0300, 0x0031, "WhitePointY", &Z_Copy },
|
||||
{ 0x0300, 0x0032, "ColorPointRX", &Z_Copy },
|
||||
{ 0x0300, 0x0033, "ColorPointRY", &Z_Copy },
|
||||
{ 0x0300, 0x0034, "ColorPointRIntensity", &Z_Copy },
|
||||
{ 0x0300, 0x0036, "ColorPointGX", &Z_Copy },
|
||||
{ 0x0300, 0x0037, "ColorPointGY", &Z_Copy },
|
||||
{ 0x0300, 0x0038, "ColorPointGIntensity", &Z_Copy },
|
||||
{ 0x0300, 0x003A, "ColorPointBX", &Z_Copy },
|
||||
{ 0x0300, 0x003B, "ColorPointBY", &Z_Copy },
|
||||
{ 0x0300, 0x003C, "ColorPointBIntensity", &Z_Copy },
|
||||
|
||||
// Illuminance Measurement cluster
|
||||
{ 0x0400, 0x0000, D_JSON_ILLUMINANCE, &Z_Copy }, // Illuminance (in Lux)
|
||||
{ 0x0400, 0x0001, "MinMeasuredValue", &Z_Copy }, //
|
||||
{ 0x0400, 0x0002, "MaxMeasuredValue", &Z_Copy }, //
|
||||
|
@ -676,16 +687,19 @@ const Z_AttributeConverter Z_PostProcess[] PROGMEM = {
|
|||
{ 0x0400, 0x0004, "LightSensorType", &Z_Copy }, //
|
||||
{ 0x0400, 0xFFFF, nullptr, &Z_Remove }, // Remove all other values
|
||||
|
||||
// Illuminance Level Sensing cluster
|
||||
{ 0x0401, 0x0000, "LevelStatus", &Z_Copy }, // Illuminance (in Lux)
|
||||
{ 0x0401, 0x0001, "LightSensorType", &Z_Copy }, // LightSensorType
|
||||
{ 0x0401, 0xFFFF, nullptr, &Z_Remove }, // Remove all other values
|
||||
|
||||
// Temperature Measurement cluster
|
||||
{ 0x0402, 0x0000, D_JSON_TEMPERATURE, &Z_FloatDiv100 }, // Temperature
|
||||
{ 0x0402, 0x0001, "MinMeasuredValue", &Z_FloatDiv100 }, //
|
||||
{ 0x0402, 0x0002, "MaxMeasuredValue", &Z_FloatDiv100 }, //
|
||||
{ 0x0402, 0x0003, "Tolerance", &Z_FloatDiv100 }, //
|
||||
{ 0x0402, 0xFFFF, nullptr, &Z_Remove }, // Remove all other values
|
||||
|
||||
// Pressure Measurement cluster
|
||||
{ 0x0403, 0x0000, D_JSON_PRESSURE_UNIT, &Z_AddPressureUnit }, // Pressure Unit
|
||||
{ 0x0403, 0x0000, D_JSON_PRESSURE, &Z_Copy }, // Pressure
|
||||
{ 0x0403, 0x0001, "MinMeasuredValue", &Z_Copy }, //
|
||||
|
@ -698,24 +712,42 @@ const Z_AttributeConverter Z_PostProcess[] PROGMEM = {
|
|||
{ 0x0403, 0x0014, "Scale", &Z_Copy }, //
|
||||
{ 0x0403, 0xFFFF, nullptr, &Z_Remove }, // Remove all other Pressure values
|
||||
|
||||
// Flow Measurement cluster
|
||||
{ 0x0404, 0x0000, D_JSON_FLOWRATE, &Z_FloatDiv10 }, // Flow (in m3/h)
|
||||
{ 0x0404, 0x0001, "MinMeasuredValue", &Z_Copy }, //
|
||||
{ 0x0404, 0x0002, "MaxMeasuredValue", &Z_Copy }, //
|
||||
{ 0x0404, 0x0003, "Tolerance", &Z_Copy }, //
|
||||
{ 0x0404, 0xFFFF, nullptr, &Z_Remove }, // Remove all other values
|
||||
|
||||
// Relative Humidity Measurement cluster
|
||||
{ 0x0405, 0x0000, D_JSON_HUMIDITY, &Z_FloatDiv100 }, // Humidity
|
||||
{ 0x0405, 0x0001, "MinMeasuredValue", &Z_Copy }, //
|
||||
{ 0x0405, 0x0002, "MaxMeasuredValue", &Z_Copy }, //
|
||||
{ 0x0405, 0x0003, "Tolerance", &Z_Copy }, //
|
||||
{ 0x0405, 0xFFFF, nullptr, &Z_Remove }, // Remove all other values
|
||||
|
||||
// Occupancy Sensing cluster
|
||||
{ 0x0406, 0x0000, "Occupancy", &Z_Copy }, // Occupancy (map8)
|
||||
{ 0x0406, 0x0001, "OccupancySensorType", &Z_Copy }, // OccupancySensorType
|
||||
{ 0x0406, 0xFFFF, nullptr, &Z_Remove }, // Remove all other values
|
||||
|
||||
// Cmd 0x0A - Cluster 0x0000, attribute 0xFF01 - proprietary
|
||||
{ 0x0000, 0xFF01, nullptr, &Z_AqaraSensor }, // Occupancy (map8)
|
||||
// Meter Identification cluster
|
||||
{ 0x0B01, 0x0000, "CompanyName", &Z_Copy },
|
||||
{ 0x0B01, 0x0001, "MeterTypeID", &Z_Copy },
|
||||
{ 0x0B01, 0x0004, "DataQualityID", &Z_Copy },
|
||||
{ 0x0B01, 0x0005, "CustomerName", &Z_Copy },
|
||||
{ 0x0B01, 0x0006, "Model", &Z_Copy },
|
||||
{ 0x0B01, 0x0007, "PartNumber", &Z_Copy },
|
||||
{ 0x0B01, 0x000A, "SoftwareRevision", &Z_Copy },
|
||||
{ 0x0B01, 0x000C, "POD", &Z_Copy },
|
||||
{ 0x0B01, 0x000D, "AvailablePower", &Z_Copy },
|
||||
{ 0x0B01, 0x000E, "PowerThreshold", &Z_Copy },
|
||||
|
||||
// Diagnostics cluster
|
||||
{ 0x0B05, 0x0000, "NumberOfResets", &Z_Copy },
|
||||
{ 0x0B05, 0x0001, "PersistentMemoryWrites",&Z_Copy },
|
||||
{ 0x0B05, 0x011C, "LastMessageLQI", &Z_Copy },
|
||||
{ 0x0B05, 0x011D, "LastMessageRSSI", &Z_Copy },
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -31,11 +31,15 @@ const Z_CommandConverter Z_Commands[] = {
|
|||
{ "Dimmer", "0008!04/xx0A00" }, // Move to Level with On/Off, xx=0..254 (255 is invalid)
|
||||
{ "Dimmer+", "0008!06/001902" }, // Step up by 10%, 0.2 secs
|
||||
{ "Dimmer-", "0008!06/011902" }, // Step down by 10%, 0.2 secs
|
||||
{ "DimmerStop", "0008!03" }, // Stop any Dimmer animation
|
||||
{ "ResetAlarm", "0009!00/xxyyyy" }, // Reset alarm (alarm code + cluster identifier)
|
||||
{ "ResetAllAlarms","0009!01" }, // Reset all alarms
|
||||
{ "Hue", "0300!00/xx000A00" }, // Move to Hue, shortest time, 1s
|
||||
{ "Sat", "0300!03/xx0A00" }, // Move to Sat
|
||||
{ "HueSat", "0300!06/xxyy0A00" }, // Hue, Sat
|
||||
{ "Color", "0300!07/xxxxyyyy0A00" }, // x, y (uint16)
|
||||
{ "CT", "0300!0A/xxxx0A00" }, // Color Temperature Mireds (uint16)
|
||||
{ "Shutter", "0102!xx" },
|
||||
{ "ShutterOpen", "0102!00"},
|
||||
{ "ShutterClose", "0102!01"},
|
||||
{ "ShutterStop", "0102!02"},
|
||||
|
@ -43,6 +47,18 @@ const Z_CommandConverter Z_Commands[] = {
|
|||
{ "ShutterTilt", "0102!08xx"}, // Tilt percentage
|
||||
};
|
||||
|
||||
const __FlashStringHelper* zigbeeFindCommand(const char *command) {
|
||||
char parm_uc[16]; // used to convert JSON keys to uppercase
|
||||
for (uint32_t i = 0; i < sizeof(Z_Commands) / sizeof(Z_Commands[0]); i++) {
|
||||
const Z_CommandConverter *conv = &Z_Commands[i];
|
||||
if (0 == strcasecmp_P(command, conv->tasmota_cmd)) {
|
||||
return (const __FlashStringHelper*) conv->zcl_cmd;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
inline bool isXYZ(char c) {
|
||||
return (c >= 'x') && (c <= 'z');
|
||||
}
|
||||
|
@ -54,8 +70,7 @@ inline char hexDigit(uint32_t h) {
|
|||
}
|
||||
|
||||
// replace all xx/yy/zz substrings with unsigned ints, and the corresponding len (8, 16 or 32 bits)
|
||||
// zcl_cmd can be in PROGMEM
|
||||
String SendZCLCommand_P(const char *zcl_cmd_P, uint32_t x, uint32_t y, uint32_t z) {
|
||||
String zigbeeCmdAddParams(const char *zcl_cmd_P, uint32_t x, uint32_t y, uint32_t z) {
|
||||
size_t len = strlen_P(zcl_cmd_P);
|
||||
char zcl_cmd[len+1];
|
||||
strcpy_P(zcl_cmd, zcl_cmd_P); // copy into RAM
|
||||
|
@ -89,4 +104,26 @@ String SendZCLCommand_P(const char *zcl_cmd_P, uint32_t x, uint32_t y, uint32_t
|
|||
return String(zcl_cmd);
|
||||
}
|
||||
|
||||
const char kZ_Alias[] PROGMEM = "OFF|" D_OFF "|" D_FALSE "|" D_STOP "|" "OPEN" "|" // 0
|
||||
"ON|" D_ON "|" D_TRUE "|" D_START "|" "CLOSE" "|" // 1
|
||||
"TOGGLE|" D_TOGGLE "|" // 2
|
||||
"ALL" ; // 255
|
||||
|
||||
const uint8_t kZ_Numbers[] PROGMEM = { 0,0,0,0,0,
|
||||
1,1,1,1,1,
|
||||
2,2,
|
||||
255 };
|
||||
|
||||
uint32_t ZigbeeAliasOrNumber(const char *state_text) {
|
||||
char command[16];
|
||||
int state_number = GetCommandCode(command, sizeof(command), state_text, kZ_Alias);
|
||||
if (state_number >= 0) {
|
||||
// found an alias, get its value
|
||||
return pgm_read_byte(kZ_Numbers + state_number);
|
||||
} else {
|
||||
// no alias found, convert it as number
|
||||
return strtoul(state_text, nullptr, 0);
|
||||
}
|
||||
}
|
||||
|
||||
#endif // USE_ZIGBEE
|
||||
|
|
|
@ -325,7 +325,6 @@ int32_t Z_ReceiveSimpleDesc(int32_t res, const class SBuffer &buf) {
|
|||
XdrvRulesProcess();
|
||||
|
||||
uint8_t cluster = zigbee_devices.findClusterEndpointIn(nwkAddr, 0x0000);
|
||||
//Serial.printf(">>> Endpoint is 0x%02X for cluster 0x%04X\n", cluster, 0x0000);
|
||||
if (cluster) {
|
||||
Z_SendAFInfoRequest(nwkAddr, cluster, 0x0000, 0x01); // TODO, do we need tarnsacId counter?
|
||||
}
|
||||
|
|
|
@ -37,11 +37,11 @@ TasmotaSerial *ZigbeeSerial = nullptr;
|
|||
|
||||
|
||||
const char kZigbeeCommands[] PROGMEM = "|" D_CMND_ZIGBEEZNPSEND "|" D_CMND_ZIGBEE_PERMITJOIN
|
||||
"|" D_CMND_ZIGBEE_STATUS "|" D_CMND_ZIGBEE_RESET "|" D_CMND_ZIGBEE_ZCL_SEND
|
||||
"|" D_CMND_ZIGBEE_PROBE "|" D_CMND_ZIGBEE_READ;
|
||||
"|" D_CMND_ZIGBEE_STATUS "|" D_CMND_ZIGBEE_RESET "|" D_CMND_ZIGBEE_SEND
|
||||
"|" D_CMND_ZIGBEE_PROBE "|" D_CMND_ZIGBEE_READ ;
|
||||
|
||||
void (* const ZigbeeCommand[])(void) PROGMEM = { &CmndZigbeeZNPSend, &CmndZigbeePermitJoin,
|
||||
&CmndZigbeeStatus, &CmndZigbeeReset, &CmndZigbeeZCLSend,
|
||||
&CmndZigbeeStatus, &CmndZigbeeReset, &CmndZigbeeSend,
|
||||
&CmndZigbeeProbe, &CmndZigbeeRead };
|
||||
|
||||
int32_t ZigbeeProcessInput(class SBuffer &buf) {
|
||||
|
@ -246,7 +246,7 @@ void ZigbeeInit(void)
|
|||
* Commands
|
||||
\*********************************************************************************************/
|
||||
|
||||
uint32_t strToUInt(const JsonVariant &val) {
|
||||
uint32_t strToUInt(const JsonVariant val) {
|
||||
// if the string starts with 0x, it is considered Hex, otherwise it is an int
|
||||
if (val.is<unsigned int>()) {
|
||||
return val.as<unsigned int>();
|
||||
|
@ -258,7 +258,9 @@ uint32_t strToUInt(const JsonVariant &val) {
|
|||
return 0; // couldn't parse anything
|
||||
}
|
||||
|
||||
const unsigned char ZIGBEE_FACTORY_RESET[] PROGMEM = "2112000F0100"; // Z_SREQ | Z_SYS, SYS_OSAL_NV_DELETE, 0x0F00 id, 0x0001 len
|
||||
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 CmndZigbeeReset(void) {
|
||||
if (ZigbeeSerial) {
|
||||
|
@ -276,7 +278,7 @@ void CmndZigbeeReset(void) {
|
|||
|
||||
void CmndZigbeeStatus(void) {
|
||||
if (ZigbeeSerial) {
|
||||
String dump = zigbee_devices.dump(XdrvMailbox.payload);
|
||||
String dump = zigbee_devices.dump(XdrvMailbox.index, XdrvMailbox.payload);
|
||||
Response_P(PSTR("{\"%s%d\":%s}"), XdrvMailbox.command, XdrvMailbox.payload, dump.c_str());
|
||||
}
|
||||
}
|
||||
|
@ -385,37 +387,15 @@ uint32_t parseHex(const char **data, size_t max_len = 8) {
|
|||
return ret;
|
||||
}
|
||||
|
||||
void CmndZigbeeZCLSend(void) {
|
||||
char parm_uc[12]; // used to convert JSON keys to uppercase
|
||||
// ZigbeeZCLSend { "dst":"0x1234", "endpoint":"0x01", "cmd":"AABBCC" }
|
||||
char dataBufUc[XdrvMailbox.data_len];
|
||||
UpperCase(dataBufUc, XdrvMailbox.data);
|
||||
RemoveSpace(dataBufUc);
|
||||
if (strlen(dataBufUc) < 8) { ResponseCmndChar(D_JSON_INVALID_JSON); return; }
|
||||
void zigbeeZCLSendStr(uint16_t dstAddr, uint8_t endpoint, const char *data) {
|
||||
|
||||
DynamicJsonBuffer jsonBuf;
|
||||
JsonObject &json = jsonBuf.parseObject(dataBufUc);
|
||||
if (!json.success()) { ResponseCmndChar(D_JSON_INVALID_JSON); return; }
|
||||
|
||||
// params
|
||||
uint16_t dstAddr = 0xFFFF; // 0xFFFF is braodcast, so considered invalid
|
||||
uint16_t clusterId = 0x0000; // 0x0000 is a valid default value
|
||||
uint8_t endpoint = 0x00; // 0x00 is invalid for the dst endpoint
|
||||
uint16_t cluster = 0x0000; // 0x0000 is a valid default value
|
||||
uint8_t cmd = ZCL_READ_ATTRIBUTES; // default command is READ_ATTRIBUTES
|
||||
bool clusterSpecific = false;
|
||||
const char* data = ""; // empty string is valid
|
||||
|
||||
UpperCase_P(parm_uc, PSTR("device"));
|
||||
if (json.containsKey(parm_uc)) { dstAddr = strToUInt(json[parm_uc]); }
|
||||
UpperCase_P(parm_uc, PSTR("endpoint"));
|
||||
if (json.containsKey(parm_uc)) { endpoint = strToUInt(json[parm_uc]); }
|
||||
UpperCase_P(parm_uc, PSTR("cmd"));
|
||||
if (json.containsKey(parm_uc)) { data = json[parm_uc].as<const char*>(); }
|
||||
|
||||
// Parse 'cmd' in the form "AAAA_BB/CCCCCCCC" or "AAAA!BB/CCCCCCCC"
|
||||
// where AA is the cluster number, BBBB the command number, CCCC... the payload
|
||||
// First delimiter is '_' for a global command, or '!' for a cluster specific commanc
|
||||
clusterId = parseHex(&data, 4);
|
||||
cluster = parseHex(&data, 4);
|
||||
|
||||
// delimiter
|
||||
if (('_' == *data) || ('!' == *data)) {
|
||||
|
@ -442,24 +422,149 @@ void CmndZigbeeZCLSend(void) {
|
|||
|
||||
if (0 == endpoint) {
|
||||
// endpoint is not specified, let's try to find it from shortAddr
|
||||
endpoint = zigbee_devices.findClusterEndpointIn(dstAddr, clusterId);
|
||||
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("CmndZigbeeZCLSend: guessing endpoint 0x%02X"), endpoint);
|
||||
endpoint = zigbee_devices.findClusterEndpointIn(dstAddr, cluster);
|
||||
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ZigbeeSend: guessing endpoint 0x%02X"), endpoint);
|
||||
}
|
||||
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("CmndZigbeeZCLSend: dstAddr 0x%04X, cluster 0x%04X, endpoint 0x%02X, cmd 0x%02X, data %s"),
|
||||
dstAddr, clusterId, endpoint, cmd, data);
|
||||
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ZigbeeSend: dstAddr 0x%04X, cluster 0x%04X, endpoint 0x%02X, cmd 0x%02X, data %s"),
|
||||
dstAddr, cluster, endpoint, cmd, data);
|
||||
|
||||
if (0 == endpoint) {
|
||||
AddLog_P2(LOG_LEVEL_INFO, PSTR("CmndZigbeeZCLSend: unspecified endpoint"));
|
||||
AddLog_P2(LOG_LEVEL_INFO, PSTR("ZigbeeSend: unspecified endpoint"));
|
||||
return;
|
||||
}
|
||||
|
||||
// everything is good, we can send the command
|
||||
ZigbeeZCLSend(dstAddr, clusterId, endpoint, cmd, clusterSpecific, buf.getBuffer(), buf.len());
|
||||
ZigbeeZCLSend(dstAddr, cluster, endpoint, cmd, clusterSpecific, buf.getBuffer(), buf.len());
|
||||
ResponseCmndDone();
|
||||
}
|
||||
|
||||
// Get an JSON attribute, with case insensitive key search
|
||||
JsonVariant &getCaseInsensitive(const JsonObject &json, const char *needle) {
|
||||
// key can be in PROGMEM
|
||||
if ((nullptr == &json) || (nullptr == needle) || (0 == pgm_read_byte(needle))) {
|
||||
return *(JsonVariant*)nullptr;
|
||||
}
|
||||
|
||||
for (auto kv : json) {
|
||||
const char *key = kv.key;
|
||||
JsonVariant &value = kv.value;
|
||||
|
||||
if (0 == strcasecmp_P(key, needle)) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
// if not found
|
||||
return *(JsonVariant*)nullptr;
|
||||
}
|
||||
|
||||
void CmndZigbeeSend(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"} }
|
||||
if (zigbee.init_phase) { ResponseCmndChar(D_ZIGBEE_NOT_STARTED); return; }
|
||||
DynamicJsonBuffer jsonBuf;
|
||||
JsonObject &json = jsonBuf.parseObject(XdrvMailbox.data);
|
||||
if (!json.success()) { ResponseCmndChar(D_JSON_INVALID_JSON); return; }
|
||||
|
||||
// params
|
||||
static char delim[] = ", "; // delimiters for parameters
|
||||
uint16_t device = 0xFFFF; // 0xFFFF is broadcast, so considered valid
|
||||
uint8_t endpoint = 0x00; // 0x00 is invalid for the dst endpoint
|
||||
String cmd_str = ""; // the actual low-level command, either specified or computed
|
||||
|
||||
const JsonVariant &val_device = getCaseInsensitive(json, PSTR("device"));
|
||||
if (nullptr != &val_device) { device = strToUInt(val_device); }
|
||||
const JsonVariant &val_endpoint = getCaseInsensitive(json, PSTR("endpoint"));
|
||||
if (nullptr != &val_endpoint) { endpoint = strToUInt(val_endpoint); }
|
||||
const JsonVariant val_cmd = getCaseInsensitive(json, PSTR("Send"));
|
||||
if (nullptr != &val_cmd) {
|
||||
// probe the type of the argument
|
||||
// If JSON object, it's high level commands
|
||||
// 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&>();
|
||||
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
|
||||
String key = it->key;
|
||||
JsonVariant& value = it->value;
|
||||
uint32_t x = 0, y = 0, z = 0;
|
||||
|
||||
const __FlashStringHelper* tasmota_cmd = zigbeeFindCommand(key.c_str());
|
||||
if (tasmota_cmd) {
|
||||
cmd_str = tasmota_cmd;
|
||||
} else {
|
||||
Response_P(PSTR("Unrecognized zigbee command: %s"), key.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
// parse the JSON value, depending on its type fill in x,y,z
|
||||
if (value.is<bool>()) {
|
||||
x = value.as<bool>() ? 1 : 0;
|
||||
} else if (value.is<unsigned int>()) {
|
||||
x = value.as<unsigned int>();
|
||||
} else {
|
||||
// if non-bool or non-int, trying char*
|
||||
const char *s_const = value.as<const char*>();
|
||||
if (s_const != nullptr) {
|
||||
char s[strlen(s_const)+1];
|
||||
strcpy(s, s_const);
|
||||
if ((nullptr != s) && (0x00 != *s)) { // ignore any null or empty string, could represent 'null' json value
|
||||
char *sval = strtok(s, delim);
|
||||
if (sval) {
|
||||
x = ZigbeeAliasOrNumber(sval);
|
||||
sval = strtok(nullptr, delim);
|
||||
if (sval) {
|
||||
y = ZigbeeAliasOrNumber(sval);
|
||||
sval = strtok(nullptr, delim);
|
||||
if (sval) {
|
||||
z = ZigbeeAliasOrNumber(sval);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ZigbeeSend: command_template = %s"), cmd_str.c_str());
|
||||
cmd_str = zigbeeCmdAddParams(cmd_str.c_str(), x, y, z); // fill in parameters
|
||||
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ZigbeeSend: command_final = %s"), cmd_str.c_str());
|
||||
} else {
|
||||
// we have zero command, pass through until last error for missing command
|
||||
}
|
||||
} else if (val_cmd.is<char*>()) {
|
||||
// low-level command
|
||||
cmd_str = val_cmd.as<String>();
|
||||
} else {
|
||||
// we have an unsupported command type, just ignore it and fallback to missing command
|
||||
}
|
||||
|
||||
AddLog_P2(LOG_LEVEL_INFO, PSTR("ZigbeeCmd_actual: ZigbeeZCLSend {\"device\":\"0x%04X\",\"endpoint\":%d,\"send\":\"%s\"}"),
|
||||
device, endpoint, cmd_str.c_str());
|
||||
zigbeeZCLSendStr(device, endpoint, cmd_str.c_str());
|
||||
} else {
|
||||
Response_P(PSTR("Missing zigbee 'Send'"));
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Probe a specific device to get its endpoints and supported clusters
|
||||
void CmndZigbeeProbe(void) {
|
||||
if (zigbee.init_phase) { ResponseCmndChar(D_ZIGBEE_NOT_STARTED); return; }
|
||||
char dataBufUc[XdrvMailbox.data_len];
|
||||
UpperCase(dataBufUc, XdrvMailbox.data);
|
||||
RemoveSpace(dataBufUc);
|
||||
|
@ -476,34 +581,33 @@ void CmndZigbeeProbe(void) {
|
|||
|
||||
// Send an attribute read command to a device, specifying cluster and list of attributes
|
||||
void CmndZigbeeRead(void) {
|
||||
char parm_uc[12]; // used to convert JSON keys to uppercase
|
||||
// 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]}
|
||||
char dataBufUc[XdrvMailbox.data_len];
|
||||
UpperCase(dataBufUc, XdrvMailbox.data);
|
||||
RemoveSpace(dataBufUc);
|
||||
if (strlen(dataBufUc) < 8) { ResponseCmndChar(D_JSON_INVALID_JSON); return; }
|
||||
|
||||
if (zigbee.init_phase) { ResponseCmndChar(D_ZIGBEE_NOT_STARTED); return; }
|
||||
DynamicJsonBuffer jsonBuf;
|
||||
JsonObject &json = jsonBuf.parseObject(dataBufUc);
|
||||
JsonObject &json = jsonBuf.parseObject(XdrvMailbox.data);
|
||||
if (!json.success()) { ResponseCmndChar(D_JSON_INVALID_JSON); return; }
|
||||
|
||||
// params
|
||||
uint16_t dstAddr = 0x0000; // default to local address
|
||||
uint16_t cluster = 0x0000; // default to general clsuter
|
||||
uint16_t device = 0xFFFF; // 0xFFFF is braodcast, so considered valid
|
||||
uint16_t cluster = 0x0000; // default to general cluster
|
||||
uint8_t endpoint = 0x00; // 0x00 is invalid for the dst endpoint
|
||||
size_t attrs_len = 0;
|
||||
uint8_t* attrs = nullptr; // empty string is valid
|
||||
size_t attrs_len = 0;
|
||||
uint8_t* attrs = nullptr; // empty string is valid
|
||||
|
||||
dstAddr = strToUInt(json[UpperCase_P(parm_uc, PSTR("device"))]);
|
||||
endpoint = strToUInt(json[UpperCase_P(parm_uc, PSTR("endpoint"))]);
|
||||
cluster = strToUInt(json[UpperCase_P(parm_uc, PSTR("cluster"))]);
|
||||
UpperCase_P(parm_uc, PSTR("Attr"));
|
||||
if (json.containsKey(parm_uc)) {
|
||||
const JsonVariant& attr_data = json[parm_uc];
|
||||
if (attr_data.is<JsonArray>()) {
|
||||
JsonArray& attr_arr = attr_data;
|
||||
|
||||
const JsonVariant &val_device = getCaseInsensitive(json, PSTR("Device"));
|
||||
if (nullptr != &val_device) { device = strToUInt(val_device); }
|
||||
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_attr = getCaseInsensitive(json, PSTR("Read"));
|
||||
if (nullptr != &val_attr) {
|
||||
if (val_attr.is<JsonArray>()) {
|
||||
JsonArray& attr_arr = val_attr;
|
||||
attrs_len = attr_arr.size() * 2;
|
||||
attrs = new uint8_t[attrs_len];
|
||||
|
||||
|
@ -517,13 +621,13 @@ void CmndZigbeeRead(void) {
|
|||
} else {
|
||||
attrs_len = 2;
|
||||
attrs = new uint8_t[attrs_len];
|
||||
uint16_t val = strToUInt(attr_data);
|
||||
uint16_t val = strToUInt(val_attr);
|
||||
attrs[0] = val & 0xFF; // little endian
|
||||
attrs[1] = val >> 8;
|
||||
}
|
||||
}
|
||||
|
||||
ZigbeeZCLSend(dstAddr, cluster, endpoint, ZCL_READ_ATTRIBUTES, false, attrs, attrs_len, false /* we do want a response */);
|
||||
ZigbeeZCLSend(device, cluster, endpoint, ZCL_READ_ATTRIBUTES, false, attrs, attrs_len, false /* we do want a response */);
|
||||
|
||||
if (attrs) { delete[] attrs; }
|
||||
}
|
||||
|
@ -531,6 +635,7 @@ void CmndZigbeeRead(void) {
|
|||
// Allow or Deny pairing of new Zigbee devices
|
||||
void CmndZigbeePermitJoin(void)
|
||||
{
|
||||
if (zigbee.init_phase) { ResponseCmndChar(D_ZIGBEE_NOT_STARTED); return; }
|
||||
uint32_t payload = XdrvMailbox.payload;
|
||||
if (payload < 0) { payload = 0; }
|
||||
if ((99 != payload) && (payload > 1)) { payload = 1; }
|
||||
|
|
Loading…
Reference in New Issue