mirror of https://github.com/arendst/Tasmota.git
Merge pull request #6702 from s-hadinger/zigbee_read
Add ZigbeeRead command and many improvements (#6095)
This commit is contained in:
commit
7ff3e22ae7
|
@ -3,6 +3,7 @@
|
|||
* Add command SetOption65 0/1 to disable (1) fast power cycle detection fixing unwanted brownout trigger
|
||||
* Add absolute PowerDelta using command PowerDelta 101..32000 where 101 = 101-100 = 1W, 202 = 202-100 = 102W (#5901)
|
||||
* Add support for EX-Store WiFi Dimmer V4 (#5856)
|
||||
* Add ZigbeeRead command and many improvements (#6095)
|
||||
*
|
||||
* 6.6.0.19 20191018
|
||||
* Replace obsolete xsns_23_sdm120 with xnrg_08_sdm120 and consolidate define USE_SDM120
|
||||
|
|
|
@ -468,9 +468,14 @@
|
|||
#define D_JSON_ZIGBEEZNPSENT "ZigbeeZNPSent"
|
||||
#define D_JSON_ZIGBEEZCL_RECEIVED "ZigbeeZCLReceived"
|
||||
#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"
|
||||
|
||||
// Commands xdrv_25_A4988_Stepper.ino
|
||||
#ifdef USE_A4988_STEPPER
|
||||
|
|
|
@ -363,10 +363,10 @@ String Z_Devices::dump(uint8_t dump_mode) const {
|
|||
JsonObject& dev = devices.createNestedObject();
|
||||
|
||||
snprintf_P(hex, sizeof(hex), PSTR("0x%04X"), shortaddr);
|
||||
dev[F("ShortAddr")] = hex;
|
||||
dev[F(D_JSON_ZIGBEE_DEVICE)] = hex;
|
||||
|
||||
if (device.friendlyName.length() > 0) {
|
||||
dev[F("FriendlyName")] = device.friendlyName;
|
||||
dev[F(D_JSON_ZIGBEE_NAME)] = device.friendlyName;
|
||||
}
|
||||
|
||||
if (1 == dump_mode) {
|
||||
|
|
|
@ -59,7 +59,7 @@ public:
|
|||
Response_P(PSTR("{\"" D_JSON_ZIGBEEZCL_RECEIVED "\":{"
|
||||
"\"groupid\":%d," "\"clusterid\":%d," "\"srcaddr\":\"0x%04X\","
|
||||
"\"srcendpoint\":%d," "\"dstendpoint\":%d," "\"wasbroadcast\":%d,"
|
||||
"\"linkquality\":%d," "\"securityuse\":%d," "\"seqnumber\":%d,"
|
||||
"\"" D_CMND_ZIGBEE_LINKQUALITY "\":%d," "\"securityuse\":%d," "\"seqnumber\":%d,"
|
||||
"\"timestamp\":%d,"
|
||||
"\"fc\":\"0x%02X\",\"manuf\":\"0x%04X\",\"transact\":%d,"
|
||||
"\"cmdid\":\"0x%02X\",\"payload\":\"%s\""),
|
||||
|
@ -390,8 +390,8 @@ void ZCLFrame::parseRawAttributes(JsonObject& json, uint8_t offset) {
|
|||
i += 2;
|
||||
|
||||
char key[16];
|
||||
snprintf_P(key, sizeof(key), PSTR("%04X_%02X_%04X"),
|
||||
_cluster_id, _cmd_id, attrid);
|
||||
snprintf_P(key, sizeof(key), PSTR("%04X/%04X"),
|
||||
_cluster_id, attrid);
|
||||
|
||||
// exception for Xiaomi lumi.weather - specific field to be treated as octet and not char
|
||||
if ((0x0000 == _cluster_id) && (0xFF01 == attrid)) {
|
||||
|
@ -415,8 +415,8 @@ void ZCLFrame::parseReadAttributes(JsonObject& json, uint8_t offset) {
|
|||
|
||||
if (0 == status) {
|
||||
char key[16];
|
||||
snprintf_P(key, sizeof(key), PSTR("%04X_%02X_%04X"),
|
||||
_cluster_id, _cmd_id, attrid);
|
||||
snprintf_P(key, sizeof(key), PSTR("%04X/%04X"),
|
||||
_cluster_id, attrid);
|
||||
|
||||
i += parseSingleAttribute(json, key, _payload, i, len);
|
||||
}
|
||||
|
@ -442,106 +442,327 @@ void ZCLFrame::parseClusterSpecificCommand(JsonObject& json, uint8_t offset) {
|
|||
// return value:
|
||||
// 0 = keep initial value
|
||||
// 1 = remove initial value
|
||||
typedef int32_t (*Z_AttrConverter)(uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const char *new_name, void * param);
|
||||
typedef int32_t (*Z_AttrConverter)(uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const __FlashStringHelper* new_name);
|
||||
typedef struct Z_AttributeConverter {
|
||||
const char * filter;
|
||||
uint16_t cluster;
|
||||
uint16_t attribute;
|
||||
const char * name;
|
||||
Z_AttrConverter func;
|
||||
void * param;
|
||||
} Z_AttributeConverter;
|
||||
|
||||
const float Z_100 PROGMEM = 100.0f;
|
||||
const float Z_10 PROGMEM = 10.0f;
|
||||
|
||||
// list of post-processing directives
|
||||
const Z_AttributeConverter Z_PostProcess[] = {
|
||||
{ "0000_0?_0004", nullptr, &Z_ManufKeep, nullptr }, // record Manufacturer
|
||||
{ "0000_0?_0005", nullptr, &Z_ModelKeep, nullptr }, // record Model
|
||||
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
|
||||
|
||||
{ "0000_0?_0000", "ZCLVersion", &Z_Copy, nullptr },
|
||||
{ "0000_0?_0001", "AppVersion", &Z_Copy, nullptr },
|
||||
{ "0000_0?_0002", "StackVersion", &Z_Copy, nullptr },
|
||||
{ "0000_0?_0003", "HWVersion", &Z_Copy, nullptr },
|
||||
{ "0000_0?_0004", "Manufacturer", &Z_Copy, nullptr },
|
||||
{ "0000_0?_0005", D_JSON_MODEL D_JSON_ID, &Z_Copy, nullptr },
|
||||
{ "0000_0?_0006", "DateCode", &Z_Copy, nullptr },
|
||||
{ "0000_0?_0007", "PowerSource", &Z_Copy, nullptr },
|
||||
{ "0000_0?_4000", "SWBuildID", &Z_Copy, nullptr },
|
||||
{ "0000_0?_????", nullptr, &Z_Remove, nullptr }, // Remove all other values
|
||||
{ 0x0000, 0x0000, "ZCLVersion", &Z_Copy },
|
||||
{ 0x0000, 0x0001, "AppVersion", &Z_Copy },
|
||||
{ 0x0000, 0x0002, "StackVersion", &Z_Copy },
|
||||
{ 0x0000, 0x0003, "HWVersion", &Z_Copy },
|
||||
{ 0x0000, 0x0004, "Manufacturer", &Z_ManufKeep }, // record Manufacturer
|
||||
{ 0x0000, 0x0005, D_JSON_MODEL D_JSON_ID, &Z_ModelKeep }, // record Model
|
||||
{ 0x0000, 0x0006, "DateCode", &Z_Copy },
|
||||
{ 0x0000, 0x0007, "PowerSource", &Z_Copy },
|
||||
{ 0x0000, 0x4000, "SWBuildID", &Z_Copy },
|
||||
{ 0x0000, 0xFFFF, nullptr, &Z_Remove }, // Remove all other values
|
||||
|
||||
{ "0400_0A_0000", D_JSON_ILLUMINANCE, &Z_Copy, nullptr }, // Illuminance (in Lux)
|
||||
{ "0400_0A_0004", "LightSensorType", &Z_Copy, nullptr }, // LightSensorType
|
||||
{ "0400_0A_????", nullptr, &Z_Remove, nullptr }, // Remove all other values
|
||||
// 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 },
|
||||
|
||||
{ "0401_0A_0000", "LevelStatus", &Z_Copy, nullptr }, // Illuminance (in Lux)
|
||||
{ "0401_0A_0001", "LightSensorType", &Z_Copy, nullptr }, // LightSensorType
|
||||
{ "0401_0A_????", nullptr, &Z_Remove, nullptr }, // Remove all other values
|
||||
// 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 },
|
||||
// Alarms cluster
|
||||
{ 0x0009, 0x0000, "AlarmCount", &Z_Copy },
|
||||
// Time cluster
|
||||
{ 0x000A, 0x0000, "Time", &Z_Copy },
|
||||
{ 0x000A, 0x0001, "TimeStatus", &Z_Copy },
|
||||
{ 0x000A, 0x0002, "TimeZone", &Z_Copy },
|
||||
{ 0x000A, 0x0003, "DstStart", &Z_Copy },
|
||||
{ 0x000A, 0x0004, "DstStart", &Z_Copy },
|
||||
{ 0x000A, 0x0005, "DstShift", &Z_Copy },
|
||||
{ 0x000A, 0x0006, "StandardTime", &Z_Copy },
|
||||
{ 0x000A, 0x0007, "LocalTime", &Z_Copy },
|
||||
{ 0x000A, 0x0008, "LastSetTime", &Z_Copy },
|
||||
{ 0x000A, 0x0009, "ValidUntilTime", &Z_Copy },
|
||||
// RSSI Location cluster
|
||||
{ 0x000B, 0x0000, "LocationType", &Z_Copy },
|
||||
{ 0x000B, 0x0000, "LocationMethod", &Z_Copy },
|
||||
{ 0x000B, 0x0000, "LocationAge", &Z_Copy },
|
||||
{ 0x000B, 0x0000, "QualityMeasure", &Z_Copy },
|
||||
{ 0x000B, 0x0000, "NumberOfDevices", &Z_Copy },
|
||||
// Analog Input cluster
|
||||
{ 0x000C, 0x0004, "ActiveText", &Z_Copy },
|
||||
{ 0x000C, 0x001C, "Description", &Z_Copy },
|
||||
{ 0x000C, 0x002E, "InactiveText", &Z_Copy },
|
||||
{ 0x000C, 0x0041, "MaxPresentValue", &Z_Copy },
|
||||
{ 0x000C, 0x0045, "MinPresentValue", &Z_Copy },
|
||||
{ 0x000C, 0x0051, "OutOfService", &Z_Copy },
|
||||
{ 0x000C, 0x0055, "PresentValue", &Z_Copy },
|
||||
{ 0x000C, 0x0057, "PriorityArray", &Z_Copy },
|
||||
{ 0x000C, 0x0067, "Reliability", &Z_Copy },
|
||||
{ 0x000C, 0x0068, "RelinquishDefault", &Z_Copy },
|
||||
{ 0x000C, 0x006A, "Resolution", &Z_Copy },
|
||||
{ 0x000C, 0x006F, "StatusFlags", &Z_Copy },
|
||||
{ 0x000C, 0x0075, "EngineeringUnits", &Z_Copy },
|
||||
{ 0x000C, 0x0100, "ApplicationType", &Z_Copy },
|
||||
// Binary Output cluster
|
||||
{ 0x0010, 0x0004, "ActiveText", &Z_Copy },
|
||||
{ 0x0010, 0x001C, "Description", &Z_Copy },
|
||||
{ 0x0010, 0x002E, "InactiveText", &Z_Copy },
|
||||
{ 0x0010, 0x0042, "MinimumOffTime", &Z_Copy },
|
||||
{ 0x0010, 0x0043, "MinimumOnTime", &Z_Copy },
|
||||
{ 0x0010, 0x0051, "OutOfService", &Z_Copy },
|
||||
{ 0x0010, 0x0054, "Polarity", &Z_Copy },
|
||||
{ 0x0010, 0x0055, "PresentValue", &Z_Copy },
|
||||
{ 0x0010, 0x0057, "PriorityArray", &Z_Copy },
|
||||
{ 0x0010, 0x0067, "Reliability", &Z_Copy },
|
||||
{ 0x0010, 0x0068, "RelinquishDefault", &Z_Copy },
|
||||
{ 0x0010, 0x006F, "StatusFlags", &Z_Copy },
|
||||
{ 0x0010, 0x0100, "ApplicationType", &Z_Copy },
|
||||
// Binary Value cluster
|
||||
{ 0x0011, 0x0004, "ActiveText", &Z_Copy },
|
||||
{ 0x0011, 0x001C, "Description", &Z_Copy },
|
||||
{ 0x0011, 0x002E, "InactiveText", &Z_Copy },
|
||||
{ 0x0011, 0x0042, "MinimumOffTime", &Z_Copy },
|
||||
{ 0x0011, 0x0043, "MinimumOnTime", &Z_Copy },
|
||||
{ 0x0011, 0x0051, "OutOfService", &Z_Copy },
|
||||
{ 0x0011, 0x0055, "PresentValue", &Z_Copy },
|
||||
{ 0x0011, 0x0057, "PriorityArray", &Z_Copy },
|
||||
{ 0x0011, 0x0067, "Reliability", &Z_Copy },
|
||||
{ 0x0011, 0x0068, "RelinquishDefault", &Z_Copy },
|
||||
{ 0x0011, 0x006F, "StatusFlags", &Z_Copy },
|
||||
{ 0x0011, 0x0100, "ApplicationType", &Z_Copy },
|
||||
// Multistate Input cluster
|
||||
{ 0x0012, 0x000E, "StateText", &Z_Copy },
|
||||
{ 0x0012, 0x001C, "Description", &Z_Copy },
|
||||
{ 0x0012, 0x004A, "NumberOfStates", &Z_Copy },
|
||||
{ 0x0012, 0x0051, "OutOfService", &Z_Copy },
|
||||
{ 0x0012, 0x0055, "PresentValue", &Z_Copy },
|
||||
{ 0x0012, 0x0067, "Reliability", &Z_Copy },
|
||||
{ 0x0012, 0x006F, "StatusFlags", &Z_Copy },
|
||||
{ 0x0012, 0x0100, "ApplicationType", &Z_Copy },
|
||||
// Multistate output
|
||||
{ 0x0013, 0x000E, "StateText", &Z_Copy },
|
||||
{ 0x0013, 0x001C, "Description", &Z_Copy },
|
||||
{ 0x0013, 0x004A, "NumberOfStates", &Z_Copy },
|
||||
{ 0x0013, 0x0051, "OutOfService", &Z_Copy },
|
||||
{ 0x0013, 0x0055, "PresentValue", &Z_Copy },
|
||||
{ 0x0013, 0x0057, "PriorityArray", &Z_Copy },
|
||||
{ 0x0013, 0x0067, "Reliability", &Z_Copy },
|
||||
{ 0x0013, 0x0068, "RelinquishDefault", &Z_Copy },
|
||||
{ 0x0013, 0x006F, "StatusFlags", &Z_Copy },
|
||||
{ 0x0013, 0x0100, "ApplicationType", &Z_Copy },
|
||||
// Multistate Value cluster
|
||||
{ 0x0014, 0x000E, "StateText", &Z_Copy },
|
||||
{ 0x0014, 0x001C, "Description", &Z_Copy },
|
||||
{ 0x0014, 0x004A, "NumberOfStates", &Z_Copy },
|
||||
{ 0x0014, 0x0051, "OutOfService", &Z_Copy },
|
||||
{ 0x0014, 0x0055, "PresentValue", &Z_Copy },
|
||||
{ 0x0014, 0x0067, "Reliability", &Z_Copy },
|
||||
{ 0x0014, 0x0068, "RelinquishDefault", &Z_Copy },
|
||||
{ 0x0014, 0x006F, "StatusFlags", &Z_Copy },
|
||||
{ 0x0014, 0x0100, "ApplicationType", &Z_Copy },
|
||||
// Diagnostics cluster
|
||||
{ 0x0B05, 0x0000, "NumberOfResets", &Z_Copy },
|
||||
{ 0x0B05, 0x0001, "PersistentMemoryWrites",&Z_Copy },
|
||||
{ 0x0B05, 0x011C, "LastMessageLQI", &Z_Copy },
|
||||
{ 0x0B05, 0x011D, "LastMessageRSSI", &Z_Copy },
|
||||
// Poll Control cluster
|
||||
{ 0x0020, 0x0000, "CheckinInterval", &Z_Copy },
|
||||
{ 0x0020, 0x0001, "LongPollInterval", &Z_Copy },
|
||||
{ 0x0020, 0x0002, "ShortPollInterval", &Z_Copy },
|
||||
{ 0x0020, 0x0003, "FastPollTimeout", &Z_Copy },
|
||||
{ 0x0020, 0x0004, "CheckinIntervalMin", &Z_Copy },
|
||||
{ 0x0020, 0x0005, "LongPollIntervalMin", &Z_Copy },
|
||||
{ 0x0020, 0x0006, "FastPollTimeoutMax", &Z_Copy },
|
||||
// Shade Configuration cluster
|
||||
{ 0x0100, 0x0000, "PhysicalClosedLimit", &Z_Copy },
|
||||
{ 0x0100, 0x0001, "MotorStepSize", &Z_Copy },
|
||||
{ 0x0100, 0x0002, "Status", &Z_Copy },
|
||||
{ 0x0100, 0x0010, "ClosedLimit", &Z_Copy },
|
||||
{ 0x0100, 0x0011, "Mode", &Z_Copy },
|
||||
// Door Lock cluster
|
||||
{ 0x0101, 0x0000, "LockState", &Z_Copy },
|
||||
{ 0x0101, 0x0001, "LockType", &Z_Copy },
|
||||
{ 0x0101, 0x0002, "ActuatorEnabled", &Z_Copy },
|
||||
{ 0x0101, 0x0003, "DoorState", &Z_Copy },
|
||||
{ 0x0101, 0x0004, "DoorOpenEvents", &Z_Copy },
|
||||
{ 0x0101, 0x0005, "DoorClosedEvents", &Z_Copy },
|
||||
{ 0x0101, 0x0006, "OpenPeriod", &Z_Copy },
|
||||
// Window Covering cluster
|
||||
{ 0x0102, 0x0000, "WindowCoveringType", &Z_Copy },
|
||||
{ 0x0102, 0x0001, "PhysicalClosedLimitLift",&Z_Copy },
|
||||
{ 0x0102, 0x0002, "PhysicalClosedLimitTilt",&Z_Copy },
|
||||
{ 0x0102, 0x0003, "CurrentPositionLift", &Z_Copy },
|
||||
{ 0x0102, 0x0004, "CurrentPositionTilt", &Z_Copy },
|
||||
{ 0x0102, 0x0005, "NumberofActuationsLift",&Z_Copy },
|
||||
{ 0x0102, 0x0006, "NumberofActuationsTilt",&Z_Copy },
|
||||
{ 0x0102, 0x0007, "ConfigStatus", &Z_Copy },
|
||||
{ 0x0102, 0x0008, "CurrentPositionLiftPercentage",&Z_Copy },
|
||||
{ 0x0102, 0x0009, "CurrentPositionTiltPercentage",&Z_Copy },
|
||||
{ 0x0102, 0x0010, "InstalledOpenLimitLift",&Z_Copy },
|
||||
{ 0x0102, 0x0011, "InstalledClosedLimitLift",&Z_Copy },
|
||||
{ 0x0102, 0x0012, "InstalledOpenLimitTilt", &Z_Copy },
|
||||
{ 0x0102, 0x0013, "InstalledClosedLimitTilt", &Z_Copy },
|
||||
{ 0x0102, 0x0014, "VelocityLift",&Z_Copy },
|
||||
{ 0x0102, 0x0015, "AccelerationTimeLift",&Z_Copy },
|
||||
{ 0x0102, 0x0016, "DecelerationTimeLift", &Z_Copy },
|
||||
{ 0x0102, 0x0017, "Mode",&Z_Copy },
|
||||
{ 0x0102, 0x0018, "IntermediateSetpointsLift",&Z_Copy },
|
||||
{ 0x0102, 0x0019, "IntermediateSetpointsTilt",&Z_Copy },
|
||||
|
||||
{ "0402_0A_0000", D_JSON_TEMPERATURE, &Z_ConvFloatDivider, (void*) &Z_100 }, // Temperature
|
||||
{ "0402_0A_????", nullptr, &Z_Remove, nullptr }, // Remove all other values
|
||||
// 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 },
|
||||
|
||||
{ "0403_0A_0000", D_JSON_PRESSURE_UNIT, &Z_Const_Keep, (void*) D_UNIT_PRESSURE}, // Pressure Unit
|
||||
{ "0403_0A_0000", D_JSON_PRESSURE, &Z_Copy, nullptr }, // Pressure
|
||||
{ "0403_0A_????", nullptr, &Z_Remove, nullptr }, // Remove all other Pressure values
|
||||
{ 0x0400, 0x0000, D_JSON_ILLUMINANCE, &Z_Copy }, // Illuminance (in Lux)
|
||||
{ 0x0400, 0x0001, "MinMeasuredValue", &Z_Copy }, //
|
||||
{ 0x0400, 0x0002, "MaxMeasuredValue", &Z_Copy }, //
|
||||
{ 0x0400, 0x0003, "Tolerance", &Z_Copy }, //
|
||||
{ 0x0400, 0x0004, "LightSensorType", &Z_Copy }, //
|
||||
{ 0x0400, 0xFFFF, nullptr, &Z_Remove }, // Remove all other values
|
||||
|
||||
{ "0404_0A_0000", D_JSON_FLOWRATE, &Z_ConvFloatDivider, (void*) &Z_10 }, // Flow (in m3/h)
|
||||
{ "0404_0A_????", nullptr, &Z_Remove, nullptr }, // Remove all other values
|
||||
{ 0x0401, 0x0000, "LevelStatus", &Z_Copy }, // Illuminance (in Lux)
|
||||
{ 0x0401, 0x0001, "LightSensorType", &Z_Copy }, // LightSensorType
|
||||
{ 0x0401, 0xFFFF, nullptr, &Z_Remove }, // Remove all other values
|
||||
|
||||
{ "0405_0A_0000", D_JSON_HUMIDITY, &Z_ConvFloatDivider, (void*) &Z_100 }, // Humidity
|
||||
{ "0405_0A_????", nullptr, &Z_Remove, nullptr }, // Remove all other values
|
||||
{ 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
|
||||
|
||||
{ "0406_0A_0000", "Occupancy", &Z_Copy, nullptr }, // Occupancy (map8)
|
||||
{ "0406_0A_0001", "OccupancySensorType", &Z_Copy, nullptr }, // OccupancySensorType
|
||||
{ "0406_0A_????", nullptr, &Z_Remove, nullptr }, // Remove all other values
|
||||
{ 0x0403, 0x0000, D_JSON_PRESSURE_UNIT, &Z_AddPressureUnit }, // Pressure Unit
|
||||
{ 0x0403, 0x0000, D_JSON_PRESSURE, &Z_Copy }, // Pressure
|
||||
{ 0x0403, 0x0001, "MinMeasuredValue", &Z_Copy }, //
|
||||
{ 0x0403, 0x0002, "MaxMeasuredValue", &Z_Copy }, //
|
||||
{ 0x0403, 0x0003, "Tolerance", &Z_Copy }, //
|
||||
{ 0x0403, 0x0010, "ScaledValue", &Z_Copy }, //
|
||||
{ 0x0403, 0x0011, "MinScaledValue", &Z_Copy }, //
|
||||
{ 0x0403, 0x0012, "MaxScaledValue", &Z_Copy }, //
|
||||
{ 0x0403, 0x0013, "ScaledTolerance", &Z_Copy }, //
|
||||
{ 0x0403, 0x0014, "Scale", &Z_Copy }, //
|
||||
{ 0x0403, 0xFFFF, nullptr, &Z_Remove }, // Remove all other Pressure values
|
||||
|
||||
{ 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
|
||||
|
||||
{ 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
|
||||
|
||||
{ 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
|
||||
{ "0000_0A_FF01", nullptr, &Z_AqaraSensor, nullptr }, // Occupancy (map8)
|
||||
// // 0x0b04 Electrical Measurement
|
||||
// { "0B04_0100", "DCVoltage", &Z_Copy, nullptr }, //
|
||||
// { "0B04_0001", "OccupancySensorType", &Z_Copy, nullptr }, //
|
||||
// { "0B04_????", "", &Z_Remove, nullptr }, //
|
||||
};
|
||||
{ 0x0000, 0xFF01, nullptr, &Z_AqaraSensor }, // Occupancy (map8)
|
||||
|
||||
};
|
||||
|
||||
// ======================================================================
|
||||
// Record Manuf
|
||||
int32_t Z_ManufKeep(uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const char *new_name, void * param) {
|
||||
int32_t Z_ManufKeep(uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const __FlashStringHelper *new_name) {
|
||||
json[new_name] = value;
|
||||
zigbee_devices.setManufId(shortaddr, value.as<const char*>());
|
||||
return 0; // keep original key
|
||||
return 1;
|
||||
}
|
||||
//
|
||||
int32_t Z_ModelKeep(uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const char *new_name, void * param) {
|
||||
int32_t Z_ModelKeep(uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const __FlashStringHelper *new_name) {
|
||||
json[new_name] = value;
|
||||
zigbee_devices.setModelId(shortaddr, value.as<const char*>());
|
||||
return 0; // keep original key
|
||||
return 1;
|
||||
}
|
||||
|
||||
// ======================================================================
|
||||
// Remove attribute
|
||||
int32_t Z_Remove(uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const char *new_name, void * param) {
|
||||
int32_t Z_Remove(uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const __FlashStringHelper *new_name) {
|
||||
return 1; // remove original key
|
||||
}
|
||||
|
||||
// Copy value as-is
|
||||
int32_t Z_Copy(uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const char *new_name, void * param) {
|
||||
int32_t Z_Copy(uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const __FlashStringHelper *new_name) {
|
||||
json[new_name] = value;
|
||||
return 1; // remove original key
|
||||
}
|
||||
|
||||
// Copy value as-is
|
||||
int32_t Z_Const_Keep(uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const char *new_name, void * param) {
|
||||
json[new_name] = (char*)param;
|
||||
// Add pressure unit
|
||||
int32_t Z_AddPressureUnit(uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const __FlashStringHelper *new_name) {
|
||||
json[new_name] = F(D_UNIT_PRESSURE);
|
||||
return 0; // keep original key
|
||||
}
|
||||
|
||||
// Convert int to float with divider
|
||||
int32_t Z_ConvFloatDivider(uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const char *new_name, void * param) {
|
||||
float f_value = value;
|
||||
float *divider = (float*) param;
|
||||
json[new_name] = f_value / *divider;
|
||||
// Convert int to float and divide by 100
|
||||
int32_t Z_FloatDiv100(uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const __FlashStringHelper *new_name) {
|
||||
json[new_name] = ((float)value) / 100.0f;
|
||||
return 1; // remove original key
|
||||
}
|
||||
// Convert int to float and divide by 10
|
||||
int32_t Z_FloatDiv10(uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const __FlashStringHelper *new_name) {
|
||||
json[new_name] = ((float)value) / 10.0f;
|
||||
return 1; // remove original key
|
||||
}
|
||||
|
||||
int32_t Z_AqaraSensor(uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const char *new_name, void * param) {
|
||||
int32_t Z_AqaraSensor(uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const __FlashStringHelper *new_name) {
|
||||
String hex = value;
|
||||
SBuffer buf2 = SBuffer::SBufferFromHex(hex.c_str(), hex.length());
|
||||
uint32_t i = 0;
|
||||
|
@ -572,64 +793,41 @@ int32_t Z_AqaraSensor(uint16_t shortaddr, JsonObject& json, const char *name, Js
|
|||
}
|
||||
// ======================================================================
|
||||
|
||||
// #define ZCL_MODELID "A_0000_0005" // Cmd 0x0A - Cluster 0x0000, attribute 0x05
|
||||
// #define ZCL_TEMPERATURE "A_0402_0000" // Cmd 0x0A - Cluster 0x0402, attribute 0x00
|
||||
// #define ZCL_PRESSURE "A_0403_0000" // Cmd 0x0A - Cluster 0x0403, attribute 0x00
|
||||
// #define ZCL_PRESSURE_SCALED "A_0403_0010" // Cmd 0x0A - Cluster 0x0403, attribute 0x10
|
||||
// #define ZCL_PRESSURE_SCALE "A_0403_0014" // Cmd 0x0A - Cluster 0x0403, attribute 0x14
|
||||
// #define ZCL_HUMIDITY "A_0405_0000" // Cmd 0x0A - Cluster 0x0403, attribute 0x00
|
||||
// #define ZCL_LUMI_WEATHER "A_0000_FF01" // Cmd 0x0A - Cluster 0x0000, attribute 0xFF01 - proprietary
|
||||
|
||||
// Cluster Specific commands
|
||||
#define ZCL_OO_OFF "s_0006_00" // Cluster 0x0006, cmd 0x00 - On/Off - Off
|
||||
#define ZCL_OO_ON "s_0006_01" // Cluster 0x0006, cmd 0x01 - On/Off - On
|
||||
#define ZCL_COLORTEMP_MOVE "s_0300_0A" // Cluster 0x0300, cmd 0x0A, Move to Color Temp
|
||||
#define ZCL_LC_MOVE "s_0008_00" // Cluster 0x0008, cmd 0x00, Level Control Move to Level
|
||||
#define ZCL_LC_MOVE_1 "s_0008_01" // Cluster 0x0008, cmd 0x01, Level Control Move
|
||||
#define ZCL_LC_STEP "s_0008_02" // Cluster 0x0008, cmd 0x02, Level Control Step
|
||||
#define ZCL_LC_STOP "s_0008_03" // Cluster 0x0008, cmd 0x03, Level Control Stop
|
||||
#define ZCL_LC_MOVE_WOO "s_0008_04" // Cluster 0x0008, cmd 0x04, Level Control Move to Level, with On/Off
|
||||
#define ZCL_LC_MOVE_1_WOO "s_0008_05" // Cluster 0x0008, cmd 0x05, Level Control Move, with On/Off
|
||||
#define ZCL_LC_STEP_WOO "s_0008_06" // Cluster 0x0008, cmd 0x05, Level Control Step, with On/Off
|
||||
#define ZCL_LC_STOP_WOO "s_0008_07" // Cluster 0x0008, cmd 0x07, Level Control Stop
|
||||
// #define ZCL_OO_OFF "s_0006_00" // Cluster 0x0006, cmd 0x00 - On/Off - Off
|
||||
// #define ZCL_OO_ON "s_0006_01" // Cluster 0x0006, cmd 0x01 - On/Off - On
|
||||
// #define ZCL_COLORTEMP_MOVE "s_0300_0A" // Cluster 0x0300, cmd 0x0A, Move to Color Temp
|
||||
// #define ZCL_LC_MOVE "s_0008_00" // Cluster 0x0008, cmd 0x00, Level Control Move to Level
|
||||
// #define ZCL_LC_MOVE_1 "s_0008_01" // Cluster 0x0008, cmd 0x01, Level Control Move
|
||||
// #define ZCL_LC_STEP "s_0008_02" // Cluster 0x0008, cmd 0x02, Level Control Step
|
||||
// #define ZCL_LC_STOP "s_0008_03" // Cluster 0x0008, cmd 0x03, Level Control Stop
|
||||
// #define ZCL_LC_MOVE_WOO "s_0008_04" // Cluster 0x0008, cmd 0x04, Level Control Move to Level, with On/Off
|
||||
// #define ZCL_LC_MOVE_1_WOO "s_0008_05" // Cluster 0x0008, cmd 0x05, Level Control Move, with On/Off
|
||||
// #define ZCL_LC_STEP_WOO "s_0008_06" // Cluster 0x0008, cmd 0x05, Level Control Step, with On/Off
|
||||
// #define ZCL_LC_STOP_WOO "s_0008_07" // Cluster 0x0008, cmd 0x07, Level Control Stop
|
||||
|
||||
// inspired from https://github.com/torvalds/linux/blob/master/lib/glob.c
|
||||
bool mini_glob_match(char const *pat, char const *str) {
|
||||
for (;;) {
|
||||
unsigned char c = *str++;
|
||||
unsigned char d = *pat++;
|
||||
|
||||
switch (d) {
|
||||
case '?': /* Wildcard: anything but nul */
|
||||
if (c == '\0')
|
||||
return false;
|
||||
break;
|
||||
case '\\':
|
||||
d = *pat++;
|
||||
/*FALLTHROUGH*/
|
||||
default: /* Literal character */
|
||||
if (c == d) {
|
||||
if (d == '\0')
|
||||
return true;
|
||||
break;
|
||||
}
|
||||
return false; /* No point continuing */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ZCLFrame::postProcessAttributes(uint16_t shortaddr, JsonObject& json) {
|
||||
// iterate on json elements
|
||||
for (auto kv : json) {
|
||||
String key = kv.key;
|
||||
String key_string = kv.key;
|
||||
const char * key = key_string.c_str();
|
||||
JsonVariant& value = kv.value;
|
||||
// Check that format looks like "CCCC/AAAA"
|
||||
char * delimiter = strchr(key, '/');
|
||||
if (delimiter) {
|
||||
uint16_t cluster = strtoul(key, &delimiter, 16);
|
||||
uint16_t attribute = strtoul(delimiter+1, nullptr, 16);
|
||||
|
||||
// Iterate on filter
|
||||
for (uint32_t i = 0; i < sizeof(Z_PostProcess) / sizeof(Z_PostProcess[0]); i++) {
|
||||
const Z_AttributeConverter *converter = &Z_PostProcess[i];
|
||||
uint16_t conv_cluster = pgm_read_word(&converter->cluster);
|
||||
uint16_t conv_attribute = pgm_read_word(&converter->attribute);
|
||||
|
||||
if (mini_glob_match(converter->filter, key.c_str())) {
|
||||
int32_t drop = (*converter->func)(shortaddr, json, key.c_str(), value, converter->name, converter->param);
|
||||
if ((conv_cluster == cluster) &&
|
||||
((conv_attribute == attribute) || (conv_attribute == 0xFFFF)) ) {
|
||||
int32_t drop = (*converter->func)(shortaddr, json, key, value, (const __FlashStringHelper*) converter->name);
|
||||
if (drop) {
|
||||
json.remove(key);
|
||||
}
|
||||
|
@ -637,6 +835,7 @@ void ZCLFrame::postProcessAttributes(uint16_t shortaddr, JsonObject& json) {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//void ZCLFrame::postProcessAttributes2(JsonObject& json) {
|
||||
|
|
|
@ -0,0 +1,92 @@
|
|||
/*
|
||||
xdrv_23_zigbee_converters.ino - zigbee support for Sonoff-Tasmota
|
||||
|
||||
Copyright (C) 2019 Theo Arends and Stephan Hadinger
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifdef USE_ZIGBEE
|
||||
|
||||
//typedef int32_t (*Z_AttrConverter)(uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const char *new_name, void * param);
|
||||
typedef struct Z_CommandConverter {
|
||||
const char * tasmota_cmd;
|
||||
const char * zcl_cmd;
|
||||
} Z_CommandConverter;
|
||||
|
||||
// list of post-processing directives
|
||||
const Z_CommandConverter Z_Commands[] = {
|
||||
{ "Power", "0006!xx" }, // 0=Off, 1=On, 2=Toggle
|
||||
{ "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
|
||||
{ "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)
|
||||
{ "ShutterOpen", "0102!00"},
|
||||
{ "ShutterClose", "0102!01"},
|
||||
{ "ShutterStop", "0102!02"},
|
||||
{ "ShutterLift", "0102!05xx"}, // Lift percentage, 0%=open, 100%=closed
|
||||
{ "ShutterTilt", "0102!08xx"}, // Tilt percentage
|
||||
};
|
||||
|
||||
inline bool isXYZ(char c) {
|
||||
return (c >= 'x') && (c <= 'z');
|
||||
}
|
||||
|
||||
// take the lower 4 bits and turn it to an hex char
|
||||
inline char hexDigit(uint32_t h) {
|
||||
uint32_t nybble = h & 0x0F;
|
||||
return (nybble > 9) ? 'A' - 10 + nybble : '0' + nybble;
|
||||
}
|
||||
|
||||
// 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) {
|
||||
size_t len = strlen_P(zcl_cmd_P);
|
||||
char zcl_cmd[len+1];
|
||||
strcpy_P(zcl_cmd, zcl_cmd_P); // copy into RAM
|
||||
|
||||
char *p = zcl_cmd;
|
||||
while (*p) {
|
||||
if (isXYZ(*p) && (*p == *(p+1))) { // if char is [x-z] and followed by same char
|
||||
uint8_t val;
|
||||
switch (*p) {
|
||||
case 'x':
|
||||
val = x & 0xFF;
|
||||
x = x >> 8;
|
||||
break;
|
||||
case 'y':
|
||||
val = y & 0xFF;
|
||||
y = y >> 8;
|
||||
break;
|
||||
case 'z':
|
||||
val = z & 0xFF;
|
||||
z = z >> 8;
|
||||
break;
|
||||
}
|
||||
*p = hexDigit(val >> 4);
|
||||
*(p+1) = hexDigit(val);
|
||||
p++;
|
||||
}
|
||||
p++;
|
||||
}
|
||||
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SendZCLCommand_P: zcl_cmd = %s"), zcl_cmd);
|
||||
|
||||
return String(zcl_cmd);
|
||||
}
|
||||
|
||||
#endif // USE_ZIGBEE
|
|
@ -319,7 +319,7 @@ static const Zigbee_Instruction zb_prog[] PROGMEM = {
|
|||
ZI_ON_ERROR_GOTO(ZIGBEE_LABEL_ABORT)
|
||||
ZI_ON_TIMEOUT_GOTO(ZIGBEE_LABEL_ABORT)
|
||||
ZI_ON_RECV_UNEXPECTED(&Z_Recv_Default)
|
||||
ZI_WAIT(10000) // wait for 10 seconds for Tasmota to stabilize
|
||||
ZI_WAIT(10500) // wait for 10 seconds for Tasmota to stabilize
|
||||
ZI_ON_ERROR_GOTO(50)
|
||||
|
||||
//ZI_MQTT_STATUS(ZIGBEE_STATUS_BOOT, "Booting")
|
||||
|
|
|
@ -274,7 +274,7 @@ void Z_SendAFInfoRequest(uint16_t shortaddr, uint8_t endpoint, uint16_t clusteri
|
|||
|
||||
buf.add8(3 + 2*sizeof(uint16_t)); // Len = 0x07
|
||||
buf.add8(0x00); // Frame Control Field
|
||||
buf.add8(transacid); // Transaction Sequance Number
|
||||
buf.add8(transacid); // Transaction Sequence Number
|
||||
buf.add8(ZCL_READ_ATTRIBUTES); // 00 Command
|
||||
buf.add16(0x0004); // 0400 ManufacturerName
|
||||
buf.add16(0x0005); // 0500 ModelIdentifier
|
||||
|
@ -385,7 +385,9 @@ int32_t Z_ReceiveAfIncomingMessage(int32_t res, const class SBuffer &buf) {
|
|||
|
||||
DynamicJsonBuffer jsonBuffer;
|
||||
JsonObject& json_root = jsonBuffer.createObject();
|
||||
JsonObject& json = json_root.createNestedObject(shortaddr);
|
||||
JsonObject& json = json_root.createNestedObject(F(D_CMND_ZIGBEE_RECEIVED));
|
||||
json[F(D_JSON_ZIGBEE_DEVICE)] = shortaddr;
|
||||
// TODO add name field if it is known
|
||||
if ( (!zcl_received.isClusterSpecificCommand()) && (ZCL_REPORT_ATTRIBUTES == zcl_received.getCmdId())) {
|
||||
zcl_received.parseRawAttributes(json);
|
||||
} else if ( (!zcl_received.isClusterSpecificCommand()) && (ZCL_READ_ATTRIBUTES_RESPONSE == zcl_received.getCmdId())) {
|
||||
|
@ -399,11 +401,13 @@ int32_t Z_ReceiveAfIncomingMessage(int32_t res, const class SBuffer &buf) {
|
|||
AddLog_P2(LOG_LEVEL_INFO, PSTR("ZigbeeZCLRawReceived: %s"), msg.c_str());
|
||||
|
||||
zcl_received.postProcessAttributes(srcaddr, json);
|
||||
// Add linkquality
|
||||
json[F("_" D_CMND_ZIGBEE_LINKQUALITY)] = linkquality; // prefix with underscore for metadata
|
||||
|
||||
msg = "";
|
||||
json_root.printTo(msg);
|
||||
Response_P(PSTR("%s"), msg.c_str());
|
||||
MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEEZCL_RECEIVED));
|
||||
MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR));
|
||||
XdrvRulesProcess();
|
||||
return -1;
|
||||
}
|
||||
|
|
|
@ -38,11 +38,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_PROBE "|" D_CMND_ZIGBEE_READ;
|
||||
|
||||
void (* const ZigbeeCommand[])(void) PROGMEM = { &CmndZigbeeZNPSend, &CmndZigbeePermitJoin,
|
||||
&CmndZigbeeStatus, &CmndZigbeeReset, &CmndZigbeeZCLSend,
|
||||
&CmndZigbeeProbe };
|
||||
&CmndZigbeeProbe, &CmndZigbeeRead };
|
||||
|
||||
int32_t ZigbeeProcessInput(class SBuffer &buf) {
|
||||
if (!zigbee.state_machine) { return -1; } // if state machine is stopped, send 'ignore' message
|
||||
|
@ -338,7 +338,7 @@ void ZigbeeZNPSend(const uint8_t *msg, size_t len) {
|
|||
#endif
|
||||
}
|
||||
|
||||
void ZigbeeZCLSend(uint16_t dtsAddr, uint16_t clusterId, uint8_t endpoint, uint8_t cmdId, bool clusterSpecific, const uint8_t *msg, size_t len, uint8_t transacId = 1) {
|
||||
void ZigbeeZCLSend(uint16_t dtsAddr, uint16_t clusterId, uint8_t endpoint, uint8_t cmdId, bool clusterSpecific, const uint8_t *msg, size_t len, bool disableDefResp = true, uint8_t transacId = 1) {
|
||||
SBuffer buf(25+len);
|
||||
buf.add8(Z_SREQ | Z_AF); // 24
|
||||
buf.add8(AF_DATA_REQUEST); // 01
|
||||
|
@ -351,10 +351,12 @@ void ZigbeeZCLSend(uint16_t dtsAddr, uint16_t clusterId, uint8_t endpoint, uint8
|
|||
buf.add8(0x1E); // 1E radius
|
||||
|
||||
buf.add8(3 + len);
|
||||
buf.add8(0x10 | (clusterSpecific ? 0x01 : 0x00)); // Frame Control Field
|
||||
buf.add8((disableDefResp ? 0x10 : 0x00) | (clusterSpecific ? 0x01 : 0x00)); // Frame Control Field
|
||||
buf.add8(transacId); // Transaction Sequance Number
|
||||
buf.add8(cmdId);
|
||||
if (len > 0) {
|
||||
buf.addBuffer(msg, len); // add the payload
|
||||
}
|
||||
|
||||
ZigbeeZNPSend(buf.getBuffer(), buf.len());
|
||||
}
|
||||
|
@ -385,7 +387,7 @@ uint32_t parseHex(const char **data, size_t max_len = 8) {
|
|||
|
||||
void CmndZigbeeZCLSend(void) {
|
||||
char parm_uc[12]; // used to convert JSON keys to uppercase
|
||||
// ZigbeeZCLSend { "dst":"0x1234", "cluster":"0x0300", "endpoint":"0x01", "cmd":10, "data":"AABBCC" }
|
||||
// ZigbeeZCLSend { "dst":"0x1234", "endpoint":"0x01", "cmd":"AABBCC" }
|
||||
char dataBufUc[XdrvMailbox.data_len];
|
||||
UpperCase(dataBufUc, XdrvMailbox.data);
|
||||
RemoveSpace(dataBufUc);
|
||||
|
@ -472,6 +474,60 @@ void CmndZigbeeProbe(void) {
|
|||
ResponseCmndDone();
|
||||
}
|
||||
|
||||
// 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; }
|
||||
|
||||
DynamicJsonBuffer jsonBuf;
|
||||
JsonObject &json = jsonBuf.parseObject(dataBufUc);
|
||||
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
|
||||
uint8_t endpoint = 0x00; // 0x00 is invalid for the dst endpoint
|
||||
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;
|
||||
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];
|
||||
uint16_t val = strToUInt(attr_data);
|
||||
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 */);
|
||||
|
||||
if (attrs) { delete[] attrs; }
|
||||
}
|
||||
|
||||
// Allow or Deny pairing of new Zigbee devices
|
||||
void CmndZigbeePermitJoin(void)
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue