diff --git a/tasmota/CHANGELOG.md b/tasmota/CHANGELOG.md index ee767851a..579d19975 100644 --- a/tasmota/CHANGELOG.md +++ b/tasmota/CHANGELOG.md @@ -5,6 +5,7 @@ - Change default my_user_config.h driver and sensor support removing most sensors and adding most drivers - Add support for Jarolift rollers by Keeloq algorithm - Change IRremoteESP8266 library updated to v2.7.4 +- Add Zigbee features and improvements ### 8.1.0.9 20200220 diff --git a/tasmota/i18n.h b/tasmota/i18n.h index 903224c99..0f1988944 100644 --- a/tasmota/i18n.h +++ b/tasmota/i18n.h @@ -470,10 +470,10 @@ // Commands xdrv_23_zigbee.ino #define D_PRFX_ZB "Zb" -#define D_PRFX_ZIGBEE "Zigbee" #define D_ZIGBEE_NOT_STARTED "Zigbee not started (yet)" #define D_CMND_ZIGBEE_PERMITJOIN "PermitJoin" #define D_CMND_ZIGBEE_STATUS "Status" + #define D_JSON_ZIGBEE_Status "Status" #define D_CMND_ZIGBEE_RESET "Reset" #define D_JSON_ZIGBEE_CC2530 "CC2530" #define D_CMND_ZIGBEEZNPRECEIVE "ZNPReceive" // only for debug @@ -486,19 +486,27 @@ #define D_JSON_ZIGBEE_DEVICE "Device" #define D_JSON_ZIGBEE_NAME "Name" #define D_CMND_ZIGBEE_NAME "Name" +#define D_CMND_ZIGBEE_MODELID "ModelId" + #define D_JSON_ZIGBEE_MODELID "ModelId" #define D_CMND_ZIGBEE_PROBE "Probe" #define D_CMND_ZIGBEE_FORGET "Forget" #define D_CMND_ZIGBEE_SAVE "Save" #define D_CMND_ZIGBEE_LINKQUALITY "LinkQuality" #define D_CMND_ZIGBEE_ENDPOINT "Endpoint" + #define D_CMND_ZIGBEE_GROUP "Group" #define D_CMND_ZIGBEE_READ "Read" #define D_CMND_ZIGBEE_SEND "Send" #define D_JSON_ZIGBEE_ZCL_SENT "ZbZCLSent" #define D_JSON_ZIGBEE_RECEIVED "ZbReceived" -#define D_JSON_ZIGBEE_RECEIVED_LEGACY "ZigbeeReceived" #define D_CMND_ZIGBEE_BIND "Bind" + #define D_JSON_ZIGBEE_BIND "ZbBind" #define D_CMND_ZIGBEE_PING "Ping" #define D_JSON_ZIGBEE_PING "ZbPing" + #define D_JSON_ZIGBEE_IEEE "IEEEAddr" +#define D_JSON_ZIGBEE_RESPONSE "ZbResponse" + #define D_JSON_ZIGBEE_CMD "Command" + #define D_JSON_ZIGBEE_STATUS "Status" + #define D_JSON_ZIGBEE_STATUS_MSG "StatusMessage" // Commands xdrv_25_A4988_Stepper.ino #define D_CMND_MOTOR "MOTOR" diff --git a/tasmota/xdrv_23_zigbee_0_constants.ino b/tasmota/xdrv_23_zigbee_0_constants.ino index a37969f8b..cc88fadc2 100644 --- a/tasmota/xdrv_23_zigbee_0_constants.ino +++ b/tasmota/xdrv_23_zigbee_0_constants.ino @@ -169,43 +169,6 @@ enum Z_configuration { ZNP_HAS_CONFIGURED = 0xF00 }; -// enum Z_nvItemIds { -// SCENE_TABLE = 145, -// MIN_FREE_NWK_ADDR = 146, -// MAX_FREE_NWK_ADDR = 147, -// MIN_FREE_GRP_ID = 148, -// MAX_FREE_GRP_ID = 149, -// MIN_GRP_IDS = 150, -// MAX_GRP_IDS = 151, -// OTA_BLOCK_REQ_DELAY = 152, -// SAPI_ENDPOINT = 161, -// SAS_SHORT_ADDR = 177, -// SAS_EXT_PANID = 178, -// SAS_PANID = 179, -// SAS_CHANNEL_MASK = 180, -// SAS_PROTOCOL_VER = 181, -// SAS_STACK_PROFILE = 182, -// SAS_STARTUP_CTRL = 183, -// SAS_TC_ADDR = 193, -// SAS_TC_MASTER_KEY = 194, -// SAS_NWK_KEY = 195, -// SAS_USE_INSEC_JOIN = 196, -// SAS_PRECFG_LINK_KEY = 197, -// SAS_NWK_KEY_SEQ_NUM = 198, -// SAS_NWK_KEY_TYPE = 199, -// SAS_NWK_MGR_ADDR = 200, -// SAS_CURR_TC_MASTER_KEY = 209, -// SAS_CURR_NWK_KEY = 210, -// SAS_CURR_PRECFG_LINK_KEY = 211, -// TCLK_TABLE_START = 257, -// TCLK_TABLE_END = 511, -// APS_LINK_KEY_DATA_START = 513, -// APS_LINK_KEY_DATA_END = 767, -// DUPLICATE_BINDING_TABLE = 768, -// DUPLICATE_DEVICE_LIST = 769, -// DUPLICATE_DEVICE_LIST_KA_TIMEOUT = 770, -//}; - // enum Z_Status { Z_Success = 0x00, @@ -261,16 +224,14 @@ enum Z_Device_Ids { // 0x0403 IAS Warning Device }; -// enum class AddrMode : uint8_t { -// NotPresent = 0, -// Group = 1, -// ShortAddress = 2, -// IEEEAddress = 3, -// Broadcast = 0xFF -// }; -// -// -// + enum Z_AddrMode : uint8_t { + Z_Addr_NotPresent = 0, + Z_Addr_Group = 1, + Z_Addr_ShortAddress = 2, + Z_Addr_IEEEAddress = 3, + Z_Addr_Broadcast = 0xFF +}; + // Commands in the AF subsystem enum AfCommand : uint8_t { AF_REGISTER = 0x00, @@ -286,7 +247,7 @@ enum AfCommand : uint8_t { AF_INCOMING_MSG = 0x81, AF_INCOMING_MSG_EXT = 0x82 }; -// + // Commands in the ZDO subsystem enum : uint8_t { ZDO_NWK_ADDR_REQ = 0x00, @@ -371,7 +332,7 @@ enum ZdoStates { ZDO_DEV_ZB_COORD = 0x09, // Started as a a Zigbee Coordinator ZDO_DEV_NWK_ORPHAN = 0x0A, // Device has lost information about its parent. }; -// + // Commands in the UTIL subsystem enum Z_Util { Z_UTIL_GET_DEVICE_INFO = 0x00, @@ -427,4 +388,57 @@ enum ZCL_Global_Commands { const uint16_t Z_ProfileIds[] PROGMEM = { 0x0104, 0x0109, 0xA10E, 0xC05E }; const char Z_ProfileNames[] PROGMEM = "ZigBee Home Automation|ZigBee Smart Energy|ZigBee Green Power|ZigBee Light Link"; +typedef struct Z_StatusLine { + uint32_t status; // no need to use uint8_t since it uses 32 bits anyways + const char * status_msg; +} Z_StatusLine; + +const Z_StatusLine Z_Status[] PROGMEM = { + 0x00, "SUCCESS", + 0x01, "FAILURE", + 0x7E, "NOT_AUTHORIZED", + 0x7F, "RESERVED_FIELD_NOT_ZERO", + 0x80, "MALFORMED_COMMAND", + 0x81, "UNSUP_CLUSTER_COMMAND", + 0x82, "UNSUP_GENERAL_COMMAND", + 0x83, "UNSUP_MANUF_CLUSTER_COMMAND", + 0x84, "UNSUP_MANUF_GENERAL_COMMAND", + 0x85, "INVALID_FIELD", + 0x86, "UNSUPPORTED_ATTRIBUTE", + 0x87, "INVALID_VALUE", + 0x88, "READ_ONLY", + 0x89, "INSUFFICIENT_SPACE", + 0x8A, "DUPLICATE_EXISTS", + 0x8B, "NOT_FOUND", + 0x8C, "UNREPORTABLE_ATTRIBUTE", + 0x8D, "INVALID_DATA_TYPE", + 0x8E, "INVALID_SELECTOR", + 0x8F, "WRITE_ONLY", + 0x90, "INCONSISTENT_STARTUP_STATE", + 0x91, "DEFINED_OUT_OF_BAND", + 0x92, "INCONSISTENT", + 0x93, "ACTION_DENIED", + 0x94, "TIMEOUT", + 0x95, "ABORT", + 0x96, "INVALID_IMAGE", + 0x97, "WAIT_FOR_DATA", + 0x98, "NO_IMAGE_AVAILABLE", + 0x99, "REQUIRE_MORE_IMAGE", + 0x9A, "NOTIFICATION_PENDING", + 0xC0, "HARDWARE_FAILURE", + 0xC1, "SOFTWARE_FAILURE", + 0xC2, "CALIBRATION_ERROR", + 0xC3, "UNSUPPORTED_CLUSTER", +}; + +const __FlashStringHelper* getZigbeeStatusMessage(uint8_t status) { + for (uint32_t i = 0; i < sizeof(Z_Status) / sizeof(Z_Status[0]); i++) { + const Z_StatusLine *statl = &Z_Status[i]; + if (statl->status == status) { + return (const __FlashStringHelper*) statl->status_msg; + } + } + return nullptr; +} + #endif // USE_ZIGBEE diff --git a/tasmota/xdrv_23_zigbee_3_devices.ino b/tasmota/xdrv_23_zigbee_3_devices.ino index c7c1f2d11..d814a8c0c 100644 --- a/tasmota/xdrv_23_zigbee_3_devices.ino +++ b/tasmota/xdrv_23_zigbee_3_devices.ino @@ -19,10 +19,6 @@ #ifdef USE_ZIGBEE -#ifndef ZIGBEERECEIVED -#define ZIGBEERECEIVED 1 -#endif - #include #include @@ -97,7 +93,8 @@ public: void setManufId(uint16_t shortaddr, const char * str); void setModelId(uint16_t shortaddr, const char * str); void setFriendlyName(uint16_t shortaddr, const char * str); - const String * getFriendlyName(uint16_t) const; + const String * getFriendlyName(uint16_t shortaddr) const; + const String * getModelId(uint16_t shortaddr) const; // device just seen on the network, update the lastSeen field void updateLastSeen(uint16_t shortaddr); @@ -537,6 +534,17 @@ const String * Z_Devices::getFriendlyName(uint16_t shortaddr) const { return nullptr; } +const String * Z_Devices::getModelId(uint16_t shortaddr) const { + int32_t found = findShortAddr(shortaddr); + if (found >= 0) { + const Z_Device & device = devicesAt(found); + if (device.modelId.length() > 0) { + return &device.modelId; + } + } + return nullptr; +} + // device just seen on the network, update the lastSeen field void Z_Devices::updateLastSeen(uint16_t shortaddr) { Z_Device & device = getShortAddr(shortaddr); @@ -656,13 +664,31 @@ bool Z_Devices::jsonIsConflict(uint16_t shortaddr, const JsonObject &values) { return false; // if no previous value, no conflict } + // compare groups + uint16_t group1 = 0; + uint16_t group2 = 0; + if (device.json->containsKey(D_CMND_ZIGBEE_GROUP)) { + group1 = device.json->get(D_CMND_ZIGBEE_GROUP); + } + if (values.containsKey(D_CMND_ZIGBEE_GROUP)) { + group2 = values.get(D_CMND_ZIGBEE_GROUP); + } + if (group1 != group2) { + return true; // if group addresses differ, then conflict + } + + // parse all other parameters for (auto kv : values) { String key_string = kv.key; - if (0 == strcasecmp_P(kv.key, PSTR(D_CMND_ZIGBEE_ENDPOINT))) { - // attribute "Endpoint" - if (kv.value.as() != device.json->get(kv.key)) { - return true; + if (0 == strcasecmp_P(kv.key, PSTR(D_CMND_ZIGBEE_GROUP))) { + // ignore group, it was handled already + } else if (0 == strcasecmp_P(kv.key, PSTR(D_CMND_ZIGBEE_ENDPOINT))) { + // attribute "Endpoint" or "Group" + if (device.json->containsKey(kv.key)) { + if (kv.value.as() != device.json->get(kv.key)) { + return true; + } } } else if (strcasecmp_P(kv.key, PSTR(D_CMND_ZIGBEE_LINKQUALITY))) { // exception = ignore duplicates for LinkQuality if (device.json->containsKey(kv.key)) { @@ -734,25 +760,11 @@ void Z_Devices::jsonPublishFlush(uint16_t shortaddr) { Response_P(PSTR("{\"" D_JSON_ZIGBEE_RECEIVED "\":{\"%s\":%s}}"), fname->c_str(), msg.c_str()); MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR)); XdrvRulesProcess(); -#if ZIGBEERECEIVED - // DEPRECATED TODO - Response_P(PSTR("{\"" D_JSON_ZIGBEE_RECEIVED_LEGACY "\":{\"%s\":%s}}"), fname->c_str(), msg.c_str()); - MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR)); - XdrvRulesProcess(); -#endif } else { Response_P(PSTR("{\"" D_JSON_ZIGBEE_RECEIVED "\":{\"0x%04X\":%s}}"), shortaddr, msg.c_str()); MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR)); XdrvRulesProcess(); -#if ZIGBEERECEIVED - // DEPRECATED TODO - Response_P(PSTR("{\"" D_JSON_ZIGBEE_RECEIVED_LEGACY "\":{\"0x%04X\":%s}}"), shortaddr, msg.c_str()); - MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR)); - XdrvRulesProcess(); -#endif } - // MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR)); - // XdrvRulesProcess(); } void Z_Devices::jsonPublishNow(uint16_t shortaddr, JsonObject & values) { diff --git a/tasmota/xdrv_23_zigbee_5_converters.ino b/tasmota/xdrv_23_zigbee_5_converters.ino index 4debd73fd..6d71d5ccf 100644 --- a/tasmota/xdrv_23_zigbee_5_converters.ino +++ b/tasmota/xdrv_23_zigbee_5_converters.ino @@ -112,6 +112,7 @@ public: static void generateAttributeName(const JsonObject& json, uint16_t cluster, uint16_t attr, char *key, size_t key_len); void parseRawAttributes(JsonObject& json, uint8_t offset = 0); void parseReadAttributes(JsonObject& json, uint8_t offset = 0); + void parseResponse(void); void parseClusterSpecificCommand(JsonObject& json, uint8_t offset = 0); void postProcessAttributes(uint16_t shortaddr, JsonObject& json); @@ -484,6 +485,51 @@ void ZCLFrame::parseReadAttributes(JsonObject& json, uint8_t offset) { } } +// ZCL_DEFAULT_RESPONSE +void ZCLFrame::parseResponse(void) { + if (_payload.len() < 2) { return; } // wrong format + uint8_t cmd = _payload.get8(0); + uint8_t status = _payload.get8(1); + + DynamicJsonBuffer jsonBuffer; + JsonObject& json = jsonBuffer.createObject(); + + // "Device" + char s[12]; + snprintf_P(s, sizeof(s), PSTR("0x%04X"), _srcaddr); + json[F(D_JSON_ZIGBEE_DEVICE)] = s; + // "Name" + const String * friendlyName = zigbee_devices.getFriendlyName(_srcaddr); + if (friendlyName) { + json[F(D_JSON_ZIGBEE_NAME)] = *friendlyName; + } + // "Command" + snprintf_P(s, sizeof(s), PSTR("%04X!%02X"), _cluster_id, cmd); + json[F(D_JSON_ZIGBEE_CMD)] = s; + // "Status" + json[F(D_JSON_ZIGBEE_STATUS)] = status; + // "StatusMessage" + const __FlashStringHelper* statm = getZigbeeStatusMessage(status); + if (statm) { + json[F(D_JSON_ZIGBEE_STATUS_MSG)] = statm; + } + // Add Endpoint + json[F(D_CMND_ZIGBEE_ENDPOINT)] = _srcendpoint; + // Add Group if non-zero + if (_group_id) { + json[F(D_CMND_ZIGBEE_GROUP)] = _group_id; + } + // Add linkquality + json[F(D_CMND_ZIGBEE_LINKQUALITY)] = _linkquality; + + String msg(""); + msg.reserve(100); + json.printTo(msg); + Response_P(PSTR("{\"" D_JSON_ZIGBEE_RESPONSE "\":%s}"), msg.c_str()); + MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEEZCL_RECEIVED)); + XdrvRulesProcess(); +} + // Parse non-normalized attributes void ZCLFrame::parseClusterSpecificCommand(JsonObject& json, uint8_t offset) { @@ -519,7 +565,7 @@ const Z_AttributeConverter Z_PostProcess[] PROGMEM = { // Power Configuration cluster { 0x0001, 0x0000, "MainsVoltage", &Z_Copy }, { 0x0001, 0x0001, "MainsFrequency", &Z_Copy }, - { 0x0001, 0x0020, "BatteryVoltage", &Z_Copy }, + { 0x0001, 0x0020, "BatteryVoltage", &Z_FloatDiv10 }, { 0x0001, 0x0021, "BatteryPercentageRemaining",&Z_Copy }, // Device Temperature Configuration cluster @@ -564,78 +610,112 @@ const Z_AttributeConverter Z_PostProcess[] PROGMEM = { { 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, 0x0004, "AnalogInActiveText", &Z_Copy }, + { 0x000C, 0x001C, "AnalogInDescription", &Z_Copy }, + { 0x000C, 0x002E, "AnalogInInactiveText", &Z_Copy }, + { 0x000C, 0x0041, "AnalogInMaxValue", &Z_Copy }, + { 0x000C, 0x0045, "AnalogInMinValue", &Z_Copy }, + { 0x000C, 0x0051, "AnalogInOutOfService", &Z_Copy }, { 0x000C, 0x0055, "AqaraRotate", &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 }, + { 0x000C, 0x0057, "AnalogInPriorityArray",&Z_Copy }, + { 0x000C, 0x0067, "AnalogInReliability", &Z_Copy }, + { 0x000C, 0x0068, "AnalogInRelinquishDefault",&Z_Copy }, + { 0x000C, 0x006A, "AnalogInResolution", &Z_Copy }, + { 0x000C, 0x006F, "AnalogInStatusFlags", &Z_Copy }, + { 0x000C, 0x0075, "AnalogInEngineeringUnits",&Z_Copy }, + { 0x000C, 0x0100, "AnalogInApplicationType",&Z_Copy }, { 0x000C, 0xFF05, "Aqara_FF05", &Z_Copy }, + // Analog Output cluster + { 0x000D, 0x001C, "AnalogOutDescription", &Z_Copy }, + { 0x000D, 0x0041, "AnalogOutMaxValue", &Z_Copy }, + { 0x000D, 0x0045, "AnalogOutMinValue", &Z_Copy }, + { 0x000D, 0x0051, "AnalogOutOutOfService",&Z_Copy }, + { 0x000D, 0x0055, "AnalogOutValue", &Z_Copy }, + { 0x000D, 0x0057, "AnalogOutPriorityArray",&Z_Copy }, + { 0x000D, 0x0067, "AnalogOutReliability", &Z_Copy }, + { 0x000D, 0x0068, "AnalogOutRelinquishDefault",&Z_Copy }, + { 0x000D, 0x006A, "AnalogOutResolution", &Z_Copy }, + { 0x000D, 0x006F, "AnalogOutStatusFlags", &Z_Copy }, + { 0x000D, 0x0075, "AnalogOutEngineeringUnits",&Z_Copy }, + { 0x000D, 0x0100, "AnalogOutApplicationType",&Z_Copy }, + // Analog Value cluster + { 0x000E, 0x001C, "AnalogDescription", &Z_Copy }, + { 0x000E, 0x0051, "AnalogOutOfService", &Z_Copy }, + { 0x000E, 0x0055, "AnalogValue", &Z_Copy }, + { 0x000E, 0x0057, "AnalogPriorityArray", &Z_Copy }, + { 0x000E, 0x0067, "AnalogReliability", &Z_Copy }, + { 0x000E, 0x0068, "AnalogRelinquishDefault",&Z_Copy }, + { 0x000E, 0x006F, "AnalogStatusFlags", &Z_Copy }, + { 0x000E, 0x0075, "AnalogEngineeringUnits",&Z_Copy }, + { 0x000E, 0x0100, "AnalogApplicationType",&Z_Copy }, + // Binary Input cluster + { 0x000F, 0x0004, "BinaryInActiveText", &Z_Copy }, + { 0x000F, 0x001C, "BinaryInDescription", &Z_Copy }, + { 0x000F, 0x002E, "BinaryInInactiveText",&Z_Copy }, + { 0x000F, 0x0051, "BinaryInOutOfService",&Z_Copy }, + { 0x000F, 0x0054, "BinaryInPolarity", &Z_Copy }, + { 0x000F, 0x0055, "BinaryInValue", &Z_Copy }, + { 0x000F, 0x0057, "BinaryInPriorityArray",&Z_Copy }, + { 0x000F, 0x0067, "BinaryInReliability", &Z_Copy }, + { 0x000F, 0x006F, "BinaryInStatusFlags", &Z_Copy }, + { 0x000F, 0x0100, "BinaryInApplicationType",&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 }, + { 0x0010, 0x0004, "BinaryOutActiveText", &Z_Copy }, + { 0x0010, 0x001C, "BinaryOutDescription", &Z_Copy }, + { 0x0010, 0x002E, "BinaryOutInactiveText",&Z_Copy }, + { 0x0010, 0x0042, "BinaryOutMinimumOffTime",&Z_Copy }, + { 0x0010, 0x0043, "BinaryOutMinimumOnTime",&Z_Copy }, + { 0x0010, 0x0051, "BinaryOutOutOfService",&Z_Copy }, + { 0x0010, 0x0054, "BinaryOutPolarity", &Z_Copy }, + { 0x0010, 0x0055, "BinaryOutValue", &Z_Copy }, + { 0x0010, 0x0057, "BinaryOutPriorityArray",&Z_Copy }, + { 0x0010, 0x0067, "BinaryOutReliability", &Z_Copy }, + { 0x0010, 0x0068, "BinaryOutRelinquishDefault",&Z_Copy }, + { 0x0010, 0x006F, "BinaryOutStatusFlags", &Z_Copy }, + { 0x0010, 0x0100, "BinaryOutApplicationType",&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 }, + { 0x0011, 0x0004, "BinaryActiveText", &Z_Copy }, + { 0x0011, 0x001C, "BinaryDescription", &Z_Copy }, + { 0x0011, 0x002E, "BinaryInactiveText", &Z_Copy }, + { 0x0011, 0x0042, "BinaryMinimumOffTime", &Z_Copy }, + { 0x0011, 0x0043, "BinaryMinimumOnTime", &Z_Copy }, + { 0x0011, 0x0051, "BinaryOutOfService", &Z_Copy }, + { 0x0011, 0x0055, "BinaryValue", &Z_Copy }, + { 0x0011, 0x0057, "BinaryPriorityArray", &Z_Copy }, + { 0x0011, 0x0067, "BinaryReliability", &Z_Copy }, + { 0x0011, 0x0068, "BinaryRelinquishDefault",&Z_Copy }, + { 0x0011, 0x006F, "BinaryStatusFlags", &Z_Copy }, + { 0x0011, 0x0100, "BinaryApplicationType",&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_AqaraCube }, - { 0x0012, 0x0067, "Reliability", &Z_Copy }, - { 0x0012, 0x006F, "StatusFlags", &Z_Copy }, - { 0x0012, 0x0100, "ApplicationType", &Z_Copy }, + { 0x0012, 0x000E, "MultiInStateText", &Z_Copy }, + { 0x0012, 0x001C, "MultiInDescription", &Z_Copy }, + { 0x0012, 0x004A, "MultiInNumberOfStates",&Z_Copy }, + { 0x0012, 0x0051, "MultiInOutOfService", &Z_Copy }, + { 0x0012, 0x0055, "MultiInValue", &Z_AqaraCube }, + { 0x0012, 0x0067, "MultiInReliability", &Z_Copy }, + { 0x0012, 0x006F, "MultiInStatusFlags", &Z_Copy }, + { 0x0012, 0x0100, "MultiInApplicationType",&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 }, + { 0x0013, 0x000E, "MultiOutStateText", &Z_Copy }, + { 0x0013, 0x001C, "MultiOutDescription", &Z_Copy }, + { 0x0013, 0x004A, "MultiOutNumberOfStates",&Z_Copy }, + { 0x0013, 0x0051, "MultiOutOutOfService", &Z_Copy }, + { 0x0013, 0x0055, "MultiOutValue", &Z_Copy }, + { 0x0013, 0x0057, "MultiOutPriorityArray",&Z_Copy }, + { 0x0013, 0x0067, "MultiOutReliability", &Z_Copy }, + { 0x0013, 0x0068, "MultiOutRelinquishDefault",&Z_Copy }, + { 0x0013, 0x006F, "MultiOutStatusFlags", &Z_Copy }, + { 0x0013, 0x0100, "MultiOutApplicationType",&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 }, + { 0x0014, 0x000E, "MultiStateText", &Z_Copy }, + { 0x0014, 0x001C, "MultiDescription", &Z_Copy }, + { 0x0014, 0x004A, "MultiNumberOfStates", &Z_Copy }, + { 0x0014, 0x0051, "MultiOutOfService", &Z_Copy }, + { 0x0014, 0x0055, "MultiValue", &Z_Copy }, + { 0x0014, 0x0067, "MultiReliability", &Z_Copy }, + { 0x0014, 0x0068, "MultiRelinquishDefault",&Z_Copy }, + { 0x0014, 0x006F, "MultiStatusFlags", &Z_Copy }, + { 0x0014, 0x0100, "MultiApplicationType", &Z_Copy }, // Power Profile cluster { 0x001A, 0x0000, "TotalProfileNum", &Z_Copy }, { 0x001A, 0x0001, "MultipleScheduling", &Z_Copy }, @@ -682,12 +762,12 @@ const Z_AttributeConverter Z_PostProcess[] PROGMEM = { { 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, 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, 0x0016, "DecelerationTimeLift", &Z_Copy }, + { 0x0102, 0x0017, "Mode", &Z_Copy }, { 0x0102, 0x0018, "IntermediateSetpointsLift",&Z_Copy }, { 0x0102, 0x0019, "IntermediateSetpointsTilt",&Z_Copy }, @@ -725,49 +805,49 @@ const Z_AttributeConverter Z_PostProcess[] PROGMEM = { // Illuminance Measurement cluster { 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, 0x0001, "IlluminanceMinMeasuredValue", &Z_Copy }, // + { 0x0400, 0x0002, "IlluminanceMaxMeasuredValue", &Z_Copy }, // + { 0x0400, 0x0003, "IlluminanceTolerance", &Z_Copy }, // + { 0x0400, 0x0004, "IlluminanceLightSensorType", &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, 0x0000, "IlluminanceLevelStatus", &Z_Copy }, // Illuminance (in Lux) + { 0x0401, 0x0001, "IlluminanceLightSensorType", &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, 0x0001, "TemperatureMinMeasuredValue", &Z_FloatDiv100 }, // + { 0x0402, 0x0002, "TemperatureMaxMeasuredValue", &Z_FloatDiv100 }, // + { 0x0402, 0x0003, "TemperatureTolerance", &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 }, // - { 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, 0x0001, "PressureMinMeasuredValue", &Z_Copy }, // + { 0x0403, 0x0002, "PressureMaxMeasuredValue", &Z_Copy }, // + { 0x0403, 0x0003, "PressureTolerance", &Z_Copy }, // + { 0x0403, 0x0010, "PressureScaledValue", &Z_Copy }, // + { 0x0403, 0x0011, "PressureMinScaledValue", &Z_Copy }, // + { 0x0403, 0x0012, "PressureMaxScaledValue", &Z_Copy }, // + { 0x0403, 0x0013, "PressureScaledTolerance", &Z_Copy }, // + { 0x0403, 0x0014, "PressureScale", &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, 0x0001, "FlowMinMeasuredValue", &Z_Copy }, // + { 0x0404, 0x0002, "FlowMaxMeasuredValue", &Z_Copy }, // + { 0x0404, 0x0003, "FlowTolerance", &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, 0x0001, "HumidityMinMeasuredValue", &Z_Copy }, // + { 0x0405, 0x0002, "HumidityMaxMeasuredValue", &Z_Copy }, // + { 0x0405, 0x0003, "HumidityTolerance", &Z_Copy }, // { 0x0405, 0xFFFF, nullptr, &Z_Remove }, // Remove all other values // Occupancy Sensing cluster @@ -837,6 +917,11 @@ int32_t Z_FloatDiv10(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& json[new_name] = ((float)value) / 10.0f; return 1; // remove original key } +// Convert int to float and divide by 10 +int32_t Z_FloatDiv2(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const String &new_name, uint16_t cluster, uint16_t attr) { + json[new_name] = ((float)value) / 2.0f; + return 1; // remove original key +} // Publish a message for `"Occupancy":0` when the timer expired int32_t Z_OccupancyCallback(uint16_t shortaddr, uint16_t cluster, uint16_t endpoint, uint32_t value) { @@ -947,9 +1032,6 @@ int32_t Z_AqaraVibration(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObje int32_t Angle_X = 0.5f + atanf(X/sqrtf(z*z+y*y)) * f_180pi; int32_t Angle_Y = 0.5f + atanf(Y/sqrtf(x*x+z*z)) * f_180pi; int32_t Angle_Z = 0.5f + atanf(Z/sqrtf(x*x+y*y)) * f_180pi; - // int32_t Angle_X = 0.5f + atanf(X/sqrtf(Z*Z+Y*Y)) * f_180pi; - // int32_t Angle_Y = 0.5f + atanf(Y/sqrtf(X*X+Z*Z)) * f_180pi; - // int32_t Angle_Z = 0.5f + atanf(Z/sqrtf(X*X+Y*Y)) * f_180pi; JsonArray& angles = json.createNestedArray(F("AqaraAngles")); angles.add(Angle_X); angles.add(Angle_Y); @@ -968,6 +1050,7 @@ int32_t Z_AqaraSensor(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& char tmp[] = "tmp"; // for obscure reasons, it must be converted from const char* to char*, otherwise ArduinoJson gets confused JsonVariant sub_value; + const String * modelId = zigbee_devices.getModelId(shortaddr); // null if unknown while (len - i >= 2) { uint8_t attrid = buf2.get8(i++); @@ -975,25 +1058,43 @@ int32_t Z_AqaraSensor(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& i += parseSingleAttribute(json, tmp, buf2, i, len); float val = json[tmp]; json.remove(tmp); + bool translated = false; // were we able to translate to a known format? if (0x01 == attrid) { json[F(D_JSON_VOLTAGE)] = val / 1000.0f; json[F("Battery")] = toPercentageCR2032(val); - } else if (0 == zcl->getManufCode()) { - // onla Aqara Temp/Humidity has manuf_code of zero. If non-zero we skip the parameters - if (0x64 == attrid) { - json[F(D_JSON_TEMPERATURE)] = val / 100.0f; - } else if (0x65 == attrid) { - json[F(D_JSON_HUMIDITY)] = val / 100.0f; - } else if (0x66 == attrid) { - json[F(D_JSON_PRESSURE)] = val / 100.0f; - json[F(D_JSON_PRESSURE_UNIT)] = F(D_UNIT_PRESSURE); // hPa - } else if (0x01 == attrid) { - json[F(D_JSON_VOLTAGE)] = val / 1000.0f; - json[F("Battery")] = toPercentageCR2032(val); + } else if ((nullptr != modelId) && (0 == zcl->getManufCode())) { + translated = true; + if (modelId->startsWith(F("lumi.sensor_ht")) || + modelId->startsWith(F("lumi.weather"))) { // Temp sensor + // Filter according to prefix of model name + // onla Aqara Temp/Humidity has manuf_code of zero. If non-zero we skip the parameters + if (0x64 == attrid) { + json[F(D_JSON_TEMPERATURE)] = val / 100.0f; + } else if (0x65 == attrid) { + json[F(D_JSON_HUMIDITY)] = val / 100.0f; + } else if (0x66 == attrid) { + json[F(D_JSON_PRESSURE)] = val / 100.0f; + json[F(D_JSON_PRESSURE_UNIT)] = F(D_UNIT_PRESSURE); // hPa + } + } else if (modelId->startsWith(F("lumi.sensor_smoke"))) { // gas leak + if (0x64 == attrid) { + json[F("SmokeDensity")] = val; + } + } else if (modelId->startsWith(F("lumi.sensor_natgas"))) { // gas leak + if (0x64 == attrid) { + json[F("GasDensity")] = val; + } + } else { + translated = false; // we didn't find a match + } + // } else if (0x115F == zcl->getManufCode()) { // Aqara Motion Sensor, still unknown field + } + if (!translated) { + if (attrid >= 100) { // payload is always above 0x64 or 100 + char attr_name[12]; + snprintf_P(attr_name, sizeof(attr_name), PSTR("Xiaomi_%02X"), attrid); + json[attr_name] = val; } - } else if (0x115F == zcl->getManufCode()) { - // Aqara Motion Sensor, still unknown field - json[F("AqaraUnknown")] = val; } } return 1; // remove original key diff --git a/tasmota/xdrv_23_zigbee_6_commands.ino b/tasmota/xdrv_23_zigbee_6_commands.ino index 5c3e3fc1b..3d8d378b0 100644 --- a/tasmota/xdrv_23_zigbee_6_commands.ino +++ b/tasmota/xdrv_23_zigbee_6_commands.ino @@ -36,8 +36,14 @@ typedef struct Z_XYZ_Var { // Holds values for vairables X, Y and Z uint8_t z_type = 0; } Z_XYZ_Var; -// list of post-processing directives -const Z_CommandConverter Z_Commands[] = { +// Cluster specific commands +// Note: the table is both for sending commands, but also displaying received commands +// - tasmota_cmd: the human-readable name of the command as entered or displayed, use '|' to split into multiple commands when displayed +// - cluster: cluster number of the command +// - cmd: the command number, of 0xFF if it's actually a variable to be assigned from 'xx' +// - direction: the direction of the command (bit field). 0x01=from client to server (coord to device), 0x02= from server to client (response), 0x80=needs specific decoding +// - param: the paylod template, x/y/z are substituted with arguments, little endian. For command display, payload must match until x/y/z character or until the end of the paylod. '??' means ignore. +const Z_CommandConverter Z_Commands[] PROGMEM = { // Group adress commands { "AddGroup", 0x0004, 0x00, 0x01, "xxxx00" }, // Add group id, group name is not supported { "ViewGroup", 0x0004, 0x01, 0x01, "xxxx" }, // Ask for the group name @@ -65,7 +71,7 @@ const Z_CommandConverter Z_Commands[] = { { "ShutterTilt", 0x0102, 0x08, 0x01, "xx" }, // Tilt percentage { "Shutter", 0x0102, 0xFF, 0x01, "" }, // Blitzwolf PIR - { "Occupancy", 0xEF00, 0x01, 0x01, "xx"}, // Specific decoder for Blitzwolf PIR, empty name means special treatment + { "Occupancy", 0xEF00, 0x01, 0x82, ""}, // Specific decoder for Blitzwolf PIR, empty name means special treatment // Decoders only - normally not used to send, and names may be masked by previous definitions { "Dimmer", 0x0008, 0x00, 0x01, "xx" }, { "DimmerMove", 0x0008, 0x01, 0x01, "xx0A" }, @@ -85,15 +91,14 @@ const Z_CommandConverter Z_Commands[] = { { "ArrowHold", 0x0005, 0x08, 0x01, "xx" }, // xx == 0x01 = left, 0x00 = right { "ArrowRelease", 0x0005, 0x09, 0x01, "" }, // IAS - Intruder Alarm System + leak/fire detection - { "ZoneStatusChange",0x0500, 0x00, 0x02, "xxxxyyzz" }, // xxxx = zone status, yy = extended status, zz = zone id, Delay is ignored + { "ZoneStatusChange",0x0500, 0x00, 0x82, "xxxxyyzz" }, // xxxx = zone status, yy = extended status, zz = zone id, Delay is ignored // responses for Group cluster commands - { "AddGroupResp", 0x0004, 0x00, 0x02, "xxyyyy" }, // xx = status, yy = group id - { "ViewGroupResp", 0x0004, 0x01, 0x02, "xxyyyy" }, // xx = status, yy = group id, name ignored - { "GetGroupResp", 0x0004, 0x02, 0x02, "xxyyzzzz" }, // xx = capacity, yy = count, zzzz = first group id, following groups ignored - { "RemoveGroup", 0x0004, 0x03, 0x02, "xxyyyy" }, // xx = status, yy = group id + { "AddGroup", 0x0004, 0x00, 0x82, "xxyyyy" }, // xx = status, yy = group id + { "ViewGroup", 0x0004, 0x01, 0x82, "xxyyyy" }, // xx = status, yy = group id, name ignored + { "GetGroup", 0x0004, 0x02, 0x82, "xxyyzzzz" }, // xx = capacity, yy = count, zzzz = first group id, following groups ignored + { "RemoveGroup", 0x0004, 0x03, 0x82, "xxyyyy" }, // xx = status, yy = group id }; - #define ZLE(x) ((x) & 0xFF), ((x) >> 8) // Little Endian // Below are the attributes we wand to read from each cluster @@ -235,16 +240,20 @@ void convertClusterSpecific(JsonObject& json, uint16_t cluster, uint8_t cmd, boo ToHex_P((unsigned char*)payload.getBuffer(), payload.len(), hex_char, hex_char_len); const __FlashStringHelper* command_name = nullptr; + uint8_t conv_direction; Z_XYZ_Var xyz; //AddLog_P2(LOG_LEVEL_INFO, PSTR(">>> len = %d - %02X%02X%02X"), payload.len(), payload.get8(0), payload.get8(1), payload.get8(2)); for (uint32_t i = 0; i < sizeof(Z_Commands) / sizeof(Z_Commands[0]); i++) { const Z_CommandConverter *conv = &Z_Commands[i]; - if (conv->cluster == cluster) { + uint16_t conv_cluster = pgm_read_word(&conv->cluster); + if (conv_cluster == cluster) { // cluster match - if ((0xFF == conv->cmd) || (cmd == conv->cmd)) { + uint8_t conv_cmd = pgm_read_byte(&conv->cmd); + conv_direction = pgm_read_byte(&conv->direction); + if ((0xFF == conv_cmd) || (cmd == conv_cmd)) { // cmd match - if ((direction && (conv->direction & 0x02)) || (!direction && (conv->direction & 0x01))) { + if ((direction && (conv_direction & 0x02)) || (!direction && (conv_direction & 0x01))) { // check if we have a match for params too // Match if: // - payload exactly matches conv->param (conv->param may be longer) @@ -271,7 +280,7 @@ void convertClusterSpecific(JsonObject& json, uint16_t cluster, uint8_t cmd, boo if (match) { command_name = (const __FlashStringHelper*) conv->tasmota_cmd; parseXYZ(conv->param, payload, &xyz); - if (0xFF == conv->cmd) { + if (0xFF == conv_cmd) { // shift all values xyz.z = xyz.y; xyz.z_type = xyz.y_type; @@ -296,31 +305,63 @@ void convertClusterSpecific(JsonObject& json, uint16_t cluster, uint8_t cmd, boo free(hex_char); if (command_name) { - if (0 == xyz.x_type) { - json[command_name] = true; // no parameter - } else if (0 == xyz.y_type) { - json[command_name] = xyz.x; // 1 parameter + // Now try to transform into a human readable format + // if (direction & 0x80) then specific transform + if (conv_direction & 0x80) { + // TODO need to create a specific command + // IAS + String command_name2 = String(command_name); + if ((cluster == 0x0500) && (cmd == 0x00)) { + // "ZoneStatusChange" + json[command_name] = xyz.x; + json[command_name2 + "Ext"] = xyz.y; + json[command_name2 + "Zone"] = xyz.z; + } else if ((cluster == 0x0004) && ((cmd == 0x00) || (cmd == 0x01) || (cmd == 0x03))) { + // AddGroupResp or ViewGroupResp (group name ignored) or RemoveGroup + json[command_name] = xyz.y; + json[command_name2 + "Status"] = xyz.x; + json[command_name2 + "StatusMsg"] = getZigbeeStatusMessage(xyz.x); + } else if ((cluster == 0x0004) && (cmd == 0x02)) { + // GetGroupResp + json[command_name2 + "Capacity"] = xyz.x; + json[command_name2 + "Count"] = xyz.y; + JsonArray &arr = json.createNestedArray(command_name); + for (uint32_t i = 0; i < xyz.y; i++) { + arr.add(payload.get16(2 + 2*i)); + } + //arr.add(xyz.z); + } } else { - // multiple answers, create an array - JsonArray &arr = json.createNestedArray(command_name); - arr.add(xyz.x); - arr.add(xyz.y); - if (xyz.z_type) { - arr.add(xyz.z); + if (0 == xyz.x_type) { + json[command_name] = true; // no parameter + } else if (0 == xyz.y_type) { + json[command_name] = xyz.x; // 1 parameter + } else { + // multiple answers, create an array + JsonArray &arr = json.createNestedArray(command_name); + arr.add(xyz.x); + arr.add(xyz.y); + if (xyz.z_type) { + arr.add(xyz.z); + } } } } } // Find the command details by command name +// Only take commands outgoing, i.e. direction == 0 // If not found: // - returns nullptr const __FlashStringHelper* zigbeeFindCommand(const char *command, uint16_t *cluster, uint16_t *cmd) { 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)) { - *cluster = conv->cluster; - *cmd = conv->cmd; + uint8_t conv_direction = pgm_read_byte(&conv->direction); + uint8_t conv_cmd = pgm_read_byte(&conv->cmd); + uint16_t conv_cluster = pgm_read_word(&conv->cluster); + if ((conv_direction & 0x01) && (0 == strcasecmp_P(command, conv->tasmota_cmd))) { + *cluster = conv_cluster; + *cmd = conv_cmd; return (const __FlashStringHelper*) conv->param; } } diff --git a/tasmota/xdrv_23_zigbee_7_statemachine.ino b/tasmota/xdrv_23_zigbee_7_statemachine.ino index d0616a40b..015a07ab4 100644 --- a/tasmota/xdrv_23_zigbee_7_statemachine.ino +++ b/tasmota/xdrv_23_zigbee_7_statemachine.ino @@ -33,7 +33,7 @@ const uint8_t ZIGBEE_STATUS_NODE_DESC = 31; // Node descriptor const uint8_t ZIGBEE_STATUS_ACTIVE_EP = 32; // Endpoints descriptor const uint8_t ZIGBEE_STATUS_SIMPLE_DESC = 33; // Simple Descriptor (clusters) const uint8_t ZIGBEE_STATUS_DEVICE_INDICATION = 34; // Device announces its address -const uint8_t ZIGBEE_STATUS_DEVICE_IEEE = 35; // Request of device address +//const uint8_t ZIGBEE_STATUS_DEVICE_IEEE = 35; // Request of device address const uint8_t ZIGBEE_STATUS_CC_VERSION = 50; // Status: CC2530 ZNP Version const uint8_t ZIGBEE_STATUS_CC_INFO = 51; // Status: CC2530 Device Configuration const uint8_t ZIGBEE_STATUS_UNSUPPORTED_VERSION = 98; // Unsupported ZNP version @@ -49,9 +49,6 @@ typedef union Zigbee_Instruction { uint16_t d16; // 16 bits data } i; const void *p; // pointer - // const void *m; // for type checking only, message - // const ZB_Func f; - // const ZB_RecvMsgFunc fr; } Zigbee_Instruction; // // Zigbee_Instruction z1 = { .i = {1,2,3}}; diff --git a/tasmota/xdrv_23_zigbee_8_parsers.ino b/tasmota/xdrv_23_zigbee_8_parsers.ino index 9e336d0c1..859351438 100644 --- a/tasmota/xdrv_23_zigbee_8_parsers.ino +++ b/tasmota/xdrv_23_zigbee_8_parsers.ino @@ -39,7 +39,7 @@ int32_t Z_ReceiveDeviceInfo(int32_t res, class SBuffer &buf) { char hex[20]; Uint64toHex(long_adr, hex, 64); Response_P(PSTR("{\"" D_JSON_ZIGBEE_STATE "\":{" - "\"Status\":%d,\"IEEEAddr\":\"%s\",\"ShortAddr\":\"0x%04X\"" + "\"Status\":%d,\"IEEEAddr\":\"0x%s\",\"ShortAddr\":\"0x%04X\"" ",\"DeviceType\":%d,\"DeviceState\":%d" ",\"NumAssocDevices\":%d"), ZIGBEE_STATUS_CC_INFO, hex, short_adr, device_type, device_state, @@ -355,21 +355,24 @@ int32_t Z_ReceiveIEEEAddr(int32_t res, const class SBuffer &buf) { zigbee_devices.updateDevice(nwkAddr, ieeeAddr); char hex[20]; Uint64toHex(ieeeAddr, hex, 64); - Response_P(PSTR("{\"" D_JSON_ZIGBEE_STATE "\":{" - "\"Status\":%d,\"IEEEAddr\":\"%s\",\"ShortAddr\":\"0x%04X\"" - "}}"), - ZIGBEE_STATUS_DEVICE_IEEE, hex, nwkAddr - ); + // Response_P(PSTR("{\"" D_JSON_ZIGBEE_STATE "\":{" + // "\"Status\":%d,\"IEEEAddr\":\"%s\",\"ShortAddr\":\"0x%04X\"" + // "}}"), + // ZIGBEE_STATUS_DEVICE_IEEE, hex, nwkAddr + // ); - MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEEZCL_RECEIVED)); - XdrvRulesProcess(); + // MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEEZCL_RECEIVED)); + // XdrvRulesProcess(); // Ping response const String * friendlyName = zigbee_devices.getFriendlyName(nwkAddr); if (friendlyName) { Response_P(PSTR("{\"" D_JSON_ZIGBEE_PING "\":{\"" D_JSON_ZIGBEE_DEVICE "\":\"0x%04X\"" - ",\"" D_JSON_ZIGBEE_NAME "\":\"%s\"}}"), nwkAddr, friendlyName->c_str()); + ",\"" D_JSON_ZIGBEE_IEEE "\":\"0x%s\"" + ",\"" D_JSON_ZIGBEE_NAME "\":\"%s\"}}"), nwkAddr, hex, friendlyName->c_str()); } else { - Response_P(PSTR("{\"" D_JSON_ZIGBEE_PING "\":{\"" D_JSON_ZIGBEE_DEVICE "\":\"0x%04X\"}}"), nwkAddr); + Response_P(PSTR("{\"" D_JSON_ZIGBEE_PING "\":{\"" D_JSON_ZIGBEE_DEVICE "\":\"0x%04X\"" + ",\"" D_JSON_ZIGBEE_IEEE "\":\"0x%s\"" + "}}"), nwkAddr, hex); } MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEEZCL_RECEIVED)); @@ -378,6 +381,27 @@ int32_t Z_ReceiveIEEEAddr(int32_t res, const class SBuffer &buf) { return -1; } +int32_t Z_BindRsp(int32_t res, const class SBuffer &buf) { + Z_ShortAddress nwkAddr = buf.get16(2); + uint8_t status = buf.get8(4); + + const String * friendlyName = zigbee_devices.getFriendlyName(nwkAddr); + if (friendlyName) { + Response_P(PSTR("{\"" D_JSON_ZIGBEE_BIND "\":{\"" D_JSON_ZIGBEE_DEVICE "\":\"0x%04X\"" + ",\"" D_JSON_ZIGBEE_NAME "\":\"%s\"" + ",\"" D_JSON_ZIGBEE_Status "\":%d" + "}}"), nwkAddr, friendlyName->c_str(), status); + } else { + Response_P(PSTR("{\"" D_JSON_ZIGBEE_BIND "\":{\"" D_JSON_ZIGBEE_DEVICE "\":\"0x%04X\"" + ",\"" D_JSON_ZIGBEE_Status "\":%d" + "}}"), nwkAddr, status); + } + MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEEZCL_RECEIVED)); + XdrvRulesProcess(); + + return -1; +} + int32_t Z_ReceiveEndDeviceAnnonce(int32_t res, const class SBuffer &buf) { Z_ShortAddress srcAddr = buf.get16(2); Z_ShortAddress nwkAddr = buf.get16(4); @@ -480,36 +504,45 @@ int32_t Z_ReceiveAfIncomingMessage(int32_t res, const class SBuffer &buf) { DynamicJsonBuffer jsonBuffer; JsonObject& json = jsonBuffer.createObject(); - if ( (!zcl_received.isClusterSpecificCommand()) && (ZCL_REPORT_ATTRIBUTES == zcl_received.getCmdId())) { - zcl_received.parseRawAttributes(json); - if (clusterid) { defer_attributes = true; } // don't defer system Cluster=0 messages - } else if ( (!zcl_received.isClusterSpecificCommand()) && (ZCL_READ_ATTRIBUTES_RESPONSE == zcl_received.getCmdId())) { - zcl_received.parseReadAttributes(json); - } else if (zcl_received.isClusterSpecificCommand()) { - zcl_received.parseClusterSpecificCommand(json); - } - String msg(""); - msg.reserve(100); - json.printTo(msg); - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_ZIGBEE D_JSON_ZIGBEEZCL_RAW_RECEIVED ": {\"0x%04X\":%s}"), srcaddr, msg.c_str()); - - zcl_received.postProcessAttributes(srcaddr, json); - // Add Endpoint - json[F(D_CMND_ZIGBEE_ENDPOINT)] = srcendpoint; - // Add linkquality - json[F(D_CMND_ZIGBEE_LINKQUALITY)] = linkquality; - - if (defer_attributes) { - // Prepare for publish - if (zigbee_devices.jsonIsConflict(srcaddr, json)) { - // there is conflicting values, force a publish of the previous message now and don't coalesce - zigbee_devices.jsonPublishFlush(srcaddr); + if ( (!zcl_received.isClusterSpecificCommand()) && (ZCL_DEFAULT_RESPONSE == zcl_received.getCmdId())) { + zcl_received.parseResponse(); + } else { + // Build the ZbReceive json + if ( (!zcl_received.isClusterSpecificCommand()) && (ZCL_REPORT_ATTRIBUTES == zcl_received.getCmdId())) { + zcl_received.parseRawAttributes(json); + if (clusterid) { defer_attributes = true; } // don't defer system Cluster=0 messages + } else if ( (!zcl_received.isClusterSpecificCommand()) && (ZCL_READ_ATTRIBUTES_RESPONSE == zcl_received.getCmdId())) { + zcl_received.parseReadAttributes(json); + } else if (zcl_received.isClusterSpecificCommand()) { + zcl_received.parseClusterSpecificCommand(json); + } + String msg(""); + msg.reserve(100); + json.printTo(msg); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_ZIGBEE D_JSON_ZIGBEEZCL_RAW_RECEIVED ": {\"0x%04X\":%s}"), srcaddr, msg.c_str()); + + zcl_received.postProcessAttributes(srcaddr, json); + // Add Endpoint + json[F(D_CMND_ZIGBEE_ENDPOINT)] = srcendpoint; + // Add Group if non-zero + if (groupid) { + json[F(D_CMND_ZIGBEE_GROUP)] = groupid; + } + // Add linkquality + json[F(D_CMND_ZIGBEE_LINKQUALITY)] = linkquality; + + if (defer_attributes) { + // Prepare for publish + if (zigbee_devices.jsonIsConflict(srcaddr, json)) { + // there is conflicting values, force a publish of the previous message now and don't coalesce + zigbee_devices.jsonPublishFlush(srcaddr); + } + zigbee_devices.jsonAppend(srcaddr, json); + zigbee_devices.setTimer(srcaddr, USE_ZIGBEE_COALESCE_ATTR_TIMER, clusterid, srcendpoint, 0, &Z_PublishAttributes); + } else { + // Publish immediately + zigbee_devices.jsonPublishNow(srcaddr, json); } - zigbee_devices.jsonAppend(srcaddr, json); - zigbee_devices.setTimer(srcaddr, USE_ZIGBEE_COALESCE_ATTR_TIMER, clusterid, srcendpoint, 0, &Z_PublishAttributes); - } else { - // Publish immediately - zigbee_devices.jsonPublishNow(srcaddr, json); } return -1; } @@ -527,6 +560,7 @@ ZBM(AREQ_PERMITJOIN_OPEN_XX, Z_AREQ | Z_ZDO, ZDO_PERMIT_JOIN_IND ) // 45CB ZBM(AREQ_ZDO_ACTIVEEPRSP, Z_AREQ | Z_ZDO, ZDO_ACTIVE_EP_RSP) // 4585 ZBM(AREQ_ZDO_SIMPLEDESCRSP, Z_AREQ | Z_ZDO, ZDO_SIMPLE_DESC_RSP) // 4584 ZBM(AREQ_ZDO_IEEE_ADDR_RSP, Z_AREQ | Z_ZDO, ZDO_IEEE_ADDR_RSP) // 4581 +ZBM(AREQ_ZDO_BIND_RSP, Z_AREQ | Z_ZDO, ZDO_BIND_RSP) // 45A1 const Z_Dispatcher Z_DispatchTable[] PROGMEM = { { AREQ_AF_INCOMING_MESSAGE, &Z_ReceiveAfIncomingMessage }, @@ -537,6 +571,7 @@ const Z_Dispatcher Z_DispatchTable[] PROGMEM = { { AREQ_ZDO_ACTIVEEPRSP, &Z_ReceiveActiveEp }, { AREQ_ZDO_SIMPLEDESCRSP, &Z_ReceiveSimpleDesc }, { AREQ_ZDO_IEEE_ADDR_RSP, &Z_ReceiveIEEEAddr }, + { AREQ_ZDO_BIND_RSP, &Z_BindRsp }, }; int32_t Z_Recv_Default(int32_t res, const class SBuffer &buf) { diff --git a/tasmota/xdrv_23_zigbee_9_impl.ino b/tasmota/xdrv_23_zigbee_9_impl.ino index 08055ba9e..3ed971b0e 100644 --- a/tasmota/xdrv_23_zigbee_9_impl.ino +++ b/tasmota/xdrv_23_zigbee_9_impl.ino @@ -33,22 +33,16 @@ const char kZbCommands[] PROGMEM = D_PRFX_ZB "|" // prefix D_CMND_ZIGBEEZNPSEND "|" D_CMND_ZIGBEE_PERMITJOIN "|" 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_FORGET "|" D_CMND_ZIGBEE_SAVE "|" D_CMND_ZIGBEE_NAME "|" D_CMND_ZIGBEE_BIND "|" - D_CMND_ZIGBEE_PING ; - -const char kZigbeeCommands[] PROGMEM = D_PRFX_ZIGBEE "|" // legacy prefix -- deprecated - D_CMND_ZIGBEEZNPSEND "|" D_CMND_ZIGBEE_PERMITJOIN "|" - 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_FORGET "|" D_CMND_ZIGBEE_SAVE "|" D_CMND_ZIGBEE_NAME "|" D_CMND_ZIGBEE_BIND "|" - D_CMND_ZIGBEE_PING ; + D_CMND_ZIGBEE_FORGET "|" D_CMND_ZIGBEE_SAVE "|" D_CMND_ZIGBEE_NAME "|" + D_CMND_ZIGBEE_BIND "|" D_CMND_ZIGBEE_PING "|" D_CMND_ZIGBEE_MODELID + ; void (* const ZigbeeCommand[])(void) PROGMEM = { &CmndZbZNPSend, &CmndZbPermitJoin, &CmndZbStatus, &CmndZbReset, &CmndZbSend, &CmndZbProbe, &CmndZbRead, &CmndZbZNPReceive, - &CmndZbForget, &CmndZbSave, &CmndZbName, &CmndZbBind, - &CmndZbPing, + &CmndZbForget, &CmndZbSave, &CmndZbName, + &CmndZbBind, &CmndZbPing, &CmndZbModelId, }; int32_t ZigbeeProcessInput(class SBuffer &buf) { @@ -547,35 +541,75 @@ void CmndZbBind(void) { // 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 + uint16_t srcDevice = 0xFFFF; // 0xFFFF is broadcast, so considered invalid + uint16_t dstDevice = 0xFFFF; // 0xFFFF is broadcast, so considered invalid + uint64_t dstLongAddr = 0; + uint8_t endpoint = 0x00; // 0x00 is invalid for the src endpoint + uint8_t toendpoint = 0x00; // 0x00 is invalid for the dst endpoint + uint16_t toGroup = 0x0000; // group address uint16_t cluster = 0; // 0xFFFF is invalid uint32_t group = 0xFFFFFFFF; // 16 bits values, otherwise 0xFFFFFFFF is unspecified + // Information about source device: "Device", "Endpoint", "Cluster" + // - the source endpoint must have a known IEEE address const JsonVariant &val_device = getCaseInsensitive(json, PSTR("Device")); if (nullptr != &val_device) { - device = zigbee_devices.parseDeviceParam(val_device.as()); - if (0xFFFF == device) { ResponseCmndChar("Invalid parameter"); return; } + srcDevice = zigbee_devices.parseDeviceParam(val_device.as()); + if (0xFFFF == srcDevice) { ResponseCmndChar("Invalid parameter"); return; } } - if ((nullptr == &val_device) || (0x000 == device)) { ResponseCmndChar("Unknown device"); return; } - + if ((nullptr == &val_device) || (0x0000 == srcDevice)) { ResponseCmndChar("Unknown source device"); return; } + // check if IEEE address is known + uint64_t srcLongAddr = zigbee_devices.getDeviceLongAddr(srcDevice); + if (0 == srcLongAddr) { ResponseCmndChar("Unknown source IEEE address"); return; } + // look for source endpoint const JsonVariant &val_endpoint = getCaseInsensitive(json, PSTR("Endpoint")); if (nullptr != &val_endpoint) { endpoint = strToUInt(val_endpoint); } + // look for source cluster const JsonVariant &val_cluster = getCaseInsensitive(json, PSTR("Cluster")); if (nullptr != &val_cluster) { cluster = strToUInt(val_cluster); } - // TODO compute endpoint from cluster + // Either Device address + // In this case the following parameters are mandatory + // - "ToDevice" and the device must have a known IEEE address + // - "ToEndpoint" + const JsonVariant &dst_device = getCaseInsensitive(json, PSTR("ToDevice")); + if (nullptr != &dst_device) { + dstDevice = zigbee_devices.parseDeviceParam(dst_device.as()); + if (0xFFFF == dstDevice) { ResponseCmndChar("Invalid parameter"); return; } + if (0x0000 == dstDevice) { + dstLongAddr = localIEEEAddr; + } else { + dstLongAddr = zigbee_devices.getDeviceLongAddr(dstDevice); + } + if (0 == dstLongAddr) { ResponseCmndChar("Unknown dest IEEE address"); return; } + + const JsonVariant &val_toendpoint = getCaseInsensitive(json, PSTR("ToEndpoint")); + if (nullptr != &val_toendpoint) { toendpoint = strToUInt(val_endpoint); } else { toendpoint = endpoint; } + } + + // Or Group Address - we don't need a dstEndpoint in this case + const JsonVariant &to_group = getCaseInsensitive(json, PSTR("ToGroup")); + if (nullptr != &to_group) { toGroup = strToUInt(to_group); } + + // make sure we don't have conflicting parameters + if (toGroup && dstLongAddr) { ResponseCmndChar("Cannot have both \"ToDevice\" and \"ToGroup\""); return; } + if (!toGroup && !dstLongAddr) { ResponseCmndChar("Missing \"ToDevice\" or \"ToGroup\""); return; } SBuffer buf(sizeof(ZBS_BIND_REQ)); buf.add8(Z_SREQ | Z_ZDO); buf.add8(ZDO_BIND_REQ); - buf.add16(device); - buf.add64(zigbee_devices.getDeviceLongAddr(device)); + buf.add16(srcDevice); + buf.add64(srcLongAddr); buf.add8(endpoint); buf.add16(cluster); - buf.add8(0x03); // DstAddrMode - 0x03 = ADDRESS_64_BIT - buf.add64(localIEEEAddr); // coordinatore IEEE address - buf.add8(0x01); // local endpoint = 1 + if (dstLongAddr) { + buf.add8(Z_Addr_IEEEAddress); // DstAddrMode - 0x03 = ADDRESS_64_BIT + buf.add64(dstLongAddr); + buf.add8(toendpoint); + } else { + buf.add8(Z_Addr_Group); // DstAddrMode - 0x01 = GROUP_ADDRESS + buf.add16(toGroup); + } ZigbeeZNPSend(buf.getBuffer(), buf.len()); @@ -635,6 +669,35 @@ void CmndZbName(void) { } } +// Specify, read or erase a ModelId, only for debug purposes +void CmndZbModelId(void) { + // Syntax is: + // ZigbeeName , - assign a friendly name + // ZigbeeName - display the current friendly name + // ZigbeeName , - remove friendly name + // + // Where can be: short_addr, long_addr, device_index, friendly_name + + if (zigbee.init_phase) { ResponseCmndChar(D_ZIGBEE_NOT_STARTED); return; } + + // check if parameters contain a comma ',' + char *p; + char *str = strtok_r(XdrvMailbox.data, ", ", &p); + + // parse first part, + uint16_t shortaddr = zigbee_devices.parseDeviceParam(XdrvMailbox.data, true); // in case of short_addr, it must be already registered + if (0x0000 == shortaddr) { ResponseCmndChar("Unknown device"); return; } + if (0xFFFF == shortaddr) { ResponseCmndChar("Invalid parameter"); return; } + + if (p == nullptr) { + const String * modelId = zigbee_devices.getModelId(shortaddr); + Response_P(PSTR("{\"0x%04X\":{\"" D_JSON_ZIGBEE_MODELID "\":\"%s\"}}"), shortaddr, modelId ? modelId->c_str() : ""); + } else { + zigbee_devices.setModelId(shortaddr, p); + Response_P(PSTR("{\"0x%04X\":{\"" D_JSON_ZIGBEE_MODELID "\":\"%s\"}}"), shortaddr, p); + } +} + // Remove an old Zigbee device from the list of known devices, use ZigbeeStatus to know all registered devices void CmndZbForget(void) { if (zigbee.init_phase) { ResponseCmndChar(D_ZIGBEE_NOT_STARTED); return; } @@ -775,7 +838,6 @@ bool Xdrv23(uint8_t function) case FUNC_LOOP: if (ZigbeeSerial) { ZigbeeInput(); } if (zigbee.state_machine) { - //ZigbeeStateMachine(); ZigbeeStateMachine_Run(); } break; @@ -784,7 +846,6 @@ bool Xdrv23(uint8_t function) break; case FUNC_COMMAND: result = DecodeCommand(kZbCommands, ZigbeeCommand); - result = result || DecodeCommand(kZigbeeCommands, ZigbeeCommand); // deprecated break; } }