From 3056769982604ac7e189e4bbcbca2f53c7b6eaa2 Mon Sep 17 00:00:00 2001 From: jmartinez-silabs <67972863+jmartinez-silabs@users.noreply.github.com> Date: Thu, 11 Nov 2021 14:06:10 -0500 Subject: [PATCH] Enable Identify cluster in Lighting-app, Perform actions on EFR32 (#11672) * Add identify cluster to light app * regen rebase regen * instantiate identify object in lighting app task, Use status led for Identify standard and trigger effects restyle * Add missing file on mbed and telink CMakeList --- examples/lighting-app/efr32/src/AppTask.cpp | 69 ++++++++++++++++++- .../lighting-app/efr32/src/ZclCallbacks.cpp | 5 ++ .../lighting-common/lighting-app.zap | 17 ++++- examples/lighting-app/mbed/CMakeLists.txt | 1 + examples/lighting-app/telink/CMakeLists.txt | 1 + .../zap-generated/IMClusterCommandHandler.cpp | 60 ++++++++++++++++ .../PluginApplicationCallbacks.h | 1 + .../zap-generated/callback-stub.cpp | 8 +++ .../zap-generated/endpoint_config.h | 37 +++++++--- .../lighting-app/zap-generated/gen_config.h | 6 ++ 10 files changed, 192 insertions(+), 13 deletions(-) diff --git a/examples/lighting-app/efr32/src/AppTask.cpp b/examples/lighting-app/efr32/src/AppTask.cpp index 821ce73b39429a..eb47d0ce51c497 100644 --- a/examples/lighting-app/efr32/src/AppTask.cpp +++ b/examples/lighting-app/efr32/src/AppTask.cpp @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -75,11 +76,58 @@ bool sIsThreadProvisioned = false; bool sIsThreadEnabled = false; bool sHaveBLEConnections = false; +EmberAfIdentifyEffectIdentifier sIdentifyEffect = EMBER_ZCL_IDENTIFY_EFFECT_IDENTIFIER_STOP_EFFECT; + uint8_t sAppEventQueueBuffer[APP_EVENT_QUEUE_SIZE * sizeof(AppEvent)]; StaticQueue_t sAppEventQueueStruct; StackType_t appStack[APP_TASK_STACK_SIZE / sizeof(StackType_t)]; StaticTask_t appTaskStruct; + +inline void OnTriggerEffectCompleted(chip::System::Layer * systemLayer, void * appState) +{ + sIdentifyEffect = EMBER_ZCL_IDENTIFY_EFFECT_IDENTIFIER_STOP_EFFECT; +} + +void OnTriggerEffect(Identify * identify) +{ + sIdentifyEffect = identify->mCurrentEffectIdentifier; + + if (identify->mCurrentEffectIdentifier == EMBER_ZCL_IDENTIFY_EFFECT_IDENTIFIER_CHANNEL_CHANGE) + { + ChipLogProgress(Zcl, "IDENTIFY_EFFECT_IDENTIFIER_CHANNEL_CHANGE - Not supported, use effect varriant %d", + identify->mEffectVariant); + sIdentifyEffect = static_cast(identify->mEffectVariant); + } + + switch (sIdentifyEffect) + { + case EMBER_ZCL_IDENTIFY_EFFECT_IDENTIFIER_BLINK: + case EMBER_ZCL_IDENTIFY_EFFECT_IDENTIFIER_BREATHE: + case EMBER_ZCL_IDENTIFY_EFFECT_IDENTIFIER_OKAY: + (void) chip::DeviceLayer::SystemLayer().StartTimer(chip::System::Clock::Seconds16(5), OnTriggerEffectCompleted, identify); + break; + case EMBER_ZCL_IDENTIFY_EFFECT_IDENTIFIER_FINISH_EFFECT: + (void) chip::DeviceLayer::SystemLayer().CancelTimer(OnTriggerEffectCompleted, identify); + (void) chip::DeviceLayer::SystemLayer().StartTimer(chip::System::Clock::Seconds16(1), OnTriggerEffectCompleted, identify); + break; + case EMBER_ZCL_IDENTIFY_EFFECT_IDENTIFIER_STOP_EFFECT: + (void) chip::DeviceLayer::SystemLayer().CancelTimer(OnTriggerEffectCompleted, identify); + sIdentifyEffect = EMBER_ZCL_IDENTIFY_EFFECT_IDENTIFIER_STOP_EFFECT; + break; + default: + ChipLogProgress(Zcl, "No identifier effect"); + } +} + +Identify gIdentify = { + chip::EndpointId{ 1 }, + [](Identify *) { ChipLogProgress(Zcl, "onIdentifyStart"); }, + [](Identify *) { ChipLogProgress(Zcl, "onIdentifyStop"); }, + EMBER_ZCL_IDENTIFY_IDENTIFY_TYPE_VISIBLE_LED, + OnTriggerEffect, +}; + } // namespace using namespace chip::TLV; @@ -212,7 +260,26 @@ void AppTask::AppTaskMain(void * pvParameter) // Otherwise, blink the LED ON for a very short time. if (sAppTask.mFunction != kFunction_FactoryReset) { - if (sIsThreadProvisioned && sIsThreadEnabled) + if (gIdentify.mActive) + { + sStatusLED.Blink(250, 250); + } + if (sIdentifyEffect != EMBER_ZCL_IDENTIFY_EFFECT_IDENTIFIER_STOP_EFFECT) + { + if (sIdentifyEffect == EMBER_ZCL_IDENTIFY_EFFECT_IDENTIFIER_BLINK) + { + sStatusLED.Blink(50, 50); + } + if (sIdentifyEffect == EMBER_ZCL_IDENTIFY_EFFECT_IDENTIFIER_BREATHE) + { + sStatusLED.Blink(1000, 1000); + } + if (sIdentifyEffect == EMBER_ZCL_IDENTIFY_EFFECT_IDENTIFIER_OKAY) + { + sStatusLED.Blink(300, 700); + } + } + else if (sIsThreadProvisioned && sIsThreadEnabled) { sStatusLED.Blink(950, 50); } diff --git a/examples/lighting-app/efr32/src/ZclCallbacks.cpp b/examples/lighting-app/efr32/src/ZclCallbacks.cpp index 03c93dbe537267..419ea954d67bbc 100644 --- a/examples/lighting-app/efr32/src/ZclCallbacks.cpp +++ b/examples/lighting-app/efr32/src/ZclCallbacks.cpp @@ -67,6 +67,11 @@ void MatterPostAttributeChangeCallback(const chip::app::ConcreteAttributePath & // WIP Apply attribute change to Light } + else if (clusterId == Identify::Id) + { + ChipLogProgress(Zcl, "Identify attribute ID: " ChipLogFormatMEI " Type: %" PRIu8 " Value: %" PRIu16 ", length %" PRIu16, + ChipLogValueMEI(attributeId), type, *value, size); + } } /** @brief OnOff Cluster Init diff --git a/examples/lighting-app/lighting-common/lighting-app.zap b/examples/lighting-app/lighting-common/lighting-app.zap index 24887bca3f60eb..f4dcd0a195ac45 100644 --- a/examples/lighting-app/lighting-common/lighting-app.zap +++ b/examples/lighting-app/lighting-common/lighting-app.zap @@ -3675,7 +3675,7 @@ "mfgCode": null, "define": "IDENTIFY_CLUSTER", "side": "server", - "enabled": 0, + "enabled": 1, "commands": [ { "name": "IdentifyQueryResponse", @@ -3702,6 +3702,21 @@ "maxInterval": 65344, "reportableChange": 0 }, + { + "name": "identify type", + "code": 1, + "mfgCode": null, + "side": "server", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "0x0", + "reportable": 0, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, { "name": "ClusterRevision", "code": 65533, diff --git a/examples/lighting-app/mbed/CMakeLists.txt b/examples/lighting-app/mbed/CMakeLists.txt index c638320b71ea4c..56c75658fc7f9e 100644 --- a/examples/lighting-app/mbed/CMakeLists.txt +++ b/examples/lighting-app/mbed/CMakeLists.txt @@ -79,6 +79,7 @@ target_sources(${APP_TARGET} PRIVATE ${CHIP_ROOT}/src/app/clusters/basic/basic.cpp ${CHIP_ROOT}/src/app/clusters/bindings/bindings.cpp ${CHIP_ROOT}/src/app/clusters/descriptor/descriptor.cpp + ${CHIP_ROOT}/src/app/clusters/identify-server/identify-server.cpp ${CHIP_ROOT}/src/app/clusters/diagnostic-logs-server/diagnostic-logs-server.cpp ${CHIP_ROOT}/src/app/clusters/ethernet_network_diagnostics_server/ethernet_network_diagnostics_server.cpp ${CHIP_ROOT}/src/app/clusters/thread_network_diagnostics_server/thread_network_diagnostics_server.cpp diff --git a/examples/lighting-app/telink/CMakeLists.txt b/examples/lighting-app/telink/CMakeLists.txt index b69e3715e3b298..c0961f3083b99f 100644 --- a/examples/lighting-app/telink/CMakeLists.txt +++ b/examples/lighting-app/telink/CMakeLists.txt @@ -79,6 +79,7 @@ target_sources(app PRIVATE ${CHIP_ROOT}/src/app/clusters/basic/basic.cpp ${CHIP_ROOT}/src/app/clusters/bindings/bindings.cpp ${CHIP_ROOT}/src/app/clusters/descriptor/descriptor.cpp + ${CHIP_ROOT}/src/app/clusters/identify-server/identify-server.cpp ${CHIP_ROOT}/src/app/clusters/diagnostic-logs-server/diagnostic-logs-server.cpp ${CHIP_ROOT}/src/app/clusters/ethernet_network_diagnostics_server/ethernet_network_diagnostics_server.cpp ${CHIP_ROOT}/src/app/clusters/thread_network_diagnostics_server/thread_network_diagnostics_server.cpp diff --git a/zzz_generated/lighting-app/zap-generated/IMClusterCommandHandler.cpp b/zzz_generated/lighting-app/zap-generated/IMClusterCommandHandler.cpp index 3f9448fbc71805..6dec594acc5130 100644 --- a/zzz_generated/lighting-app/zap-generated/IMClusterCommandHandler.cpp +++ b/zzz_generated/lighting-app/zap-generated/IMClusterCommandHandler.cpp @@ -447,6 +447,63 @@ void DispatchServerCommand(CommandHandler * apCommandObj, const ConcreteCommandP } // namespace GeneralCommissioning +namespace Identify { + +void DispatchServerCommand(CommandHandler * apCommandObj, const ConcreteCommandPath & aCommandPath, TLV::TLVReader & aDataTlv) +{ + // We are using TLVUnpackError and TLVError here since both of them can be CHIP_END_OF_TLV + // When TLVError is CHIP_END_OF_TLV, it means we have iterated all of the items, which is not a real error. + // Any error value TLVUnpackError means we have received an illegal value. + // The following variables are used for all commands to save code size. + CHIP_ERROR TLVError = CHIP_NO_ERROR; + bool wasHandled = false; + { + switch (aCommandPath.mCommandId) + { + case Commands::Identify::Id: { + Commands::Identify::DecodableType commandData; + TLVError = DataModel::Decode(aDataTlv, commandData); + if (TLVError == CHIP_NO_ERROR) + { + wasHandled = emberAfIdentifyClusterIdentifyCallback(apCommandObj, aCommandPath, commandData); + } + break; + } + case Commands::IdentifyQuery::Id: { + Commands::IdentifyQuery::DecodableType commandData; + TLVError = DataModel::Decode(aDataTlv, commandData); + if (TLVError == CHIP_NO_ERROR) + { + wasHandled = emberAfIdentifyClusterIdentifyQueryCallback(apCommandObj, aCommandPath, commandData); + } + break; + } + case Commands::TriggerEffect::Id: { + Commands::TriggerEffect::DecodableType commandData; + TLVError = DataModel::Decode(aDataTlv, commandData); + if (TLVError == CHIP_NO_ERROR) + { + wasHandled = emberAfIdentifyClusterTriggerEffectCallback(apCommandObj, aCommandPath, commandData); + } + break; + } + default: { + // Unrecognized command ID, error status will apply. + ReportCommandUnsupported(apCommandObj, aCommandPath); + return; + } + } + } + + if (CHIP_NO_ERROR != TLVError || !wasHandled) + { + apCommandObj->AddStatus(aCommandPath, Protocols::InteractionModel::Status::InvalidCommand); + ChipLogProgress(Zcl, "Failed to dispatch command, TLVError=%" CHIP_ERROR_FORMAT, TLVError.Format()); + } +} + +} // namespace Identify + namespace LevelControl { void DispatchServerCommand(CommandHandler * apCommandObj, const ConcreteCommandPath & aCommandPath, TLV::TLVReader & aDataTlv) @@ -963,6 +1020,9 @@ void DispatchSingleClusterCommand(const ConcreteCommandPath & aCommandPath, TLV: case Clusters::GeneralCommissioning::Id: Clusters::GeneralCommissioning::DispatchServerCommand(apCommandObj, aCommandPath, aReader); break; + case Clusters::Identify::Id: + Clusters::Identify::DispatchServerCommand(apCommandObj, aCommandPath, aReader); + break; case Clusters::LevelControl::Id: Clusters::LevelControl::DispatchServerCommand(apCommandObj, aCommandPath, aReader); break; diff --git a/zzz_generated/lighting-app/zap-generated/PluginApplicationCallbacks.h b/zzz_generated/lighting-app/zap-generated/PluginApplicationCallbacks.h index b6b9aa6aa67ada..24fe9f82edd973 100644 --- a/zzz_generated/lighting-app/zap-generated/PluginApplicationCallbacks.h +++ b/zzz_generated/lighting-app/zap-generated/PluginApplicationCallbacks.h @@ -30,6 +30,7 @@ MatterEthernetNetworkDiagnosticsPluginServerInitCallback(); \ MatterGeneralCommissioningPluginServerInitCallback(); \ MatterGeneralDiagnosticsPluginServerInitCallback(); \ + MatterIdentifyPluginServerInitCallback(); \ MatterLevelControlPluginServerInitCallback(); \ MatterNetworkCommissioningPluginServerInitCallback(); \ MatterOccupancySensingPluginServerInitCallback(); \ diff --git a/zzz_generated/lighting-app/zap-generated/callback-stub.cpp b/zzz_generated/lighting-app/zap-generated/callback-stub.cpp index 2eb19ab720afe9..8169285b84dcdf 100644 --- a/zzz_generated/lighting-app/zap-generated/callback-stub.cpp +++ b/zzz_generated/lighting-app/zap-generated/callback-stub.cpp @@ -53,6 +53,9 @@ void emberAfClusterInitCallback(EndpointId endpoint, ClusterId clusterId) case ZCL_GENERAL_DIAGNOSTICS_CLUSTER_ID: emberAfGeneralDiagnosticsClusterInitCallback(endpoint); break; + case ZCL_IDENTIFY_CLUSTER_ID: + emberAfIdentifyClusterInitCallback(endpoint); + break; case ZCL_LEVEL_CONTROL_CLUSTER_ID: emberAfLevelControlClusterInitCallback(endpoint); break; @@ -126,6 +129,11 @@ void __attribute__((weak)) emberAfGeneralDiagnosticsClusterInitCallback(Endpoint // To prevent warning (void) endpoint; } +void __attribute__((weak)) emberAfIdentifyClusterInitCallback(EndpointId endpoint) +{ + // To prevent warning + (void) endpoint; +} void __attribute__((weak)) emberAfLevelControlClusterInitCallback(EndpointId endpoint) { // To prevent warning diff --git a/zzz_generated/lighting-app/zap-generated/endpoint_config.h b/zzz_generated/lighting-app/zap-generated/endpoint_config.h index 925ad863f46a85..83881ec7ff4fc1 100644 --- a/zzz_generated/lighting-app/zap-generated/endpoint_config.h +++ b/zzz_generated/lighting-app/zap-generated/endpoint_config.h @@ -725,7 +725,7 @@ #define ZAP_ATTRIBUTE_MASK(mask) ATTRIBUTE_MASK_##mask // This is an array of EmberAfAttributeMetadata structures. -#define GENERATED_ATTRIBUTE_COUNT 190 +#define GENERATED_ATTRIBUTE_COUNT 193 #define GENERATED_ATTRIBUTES \ { \ \ @@ -892,6 +892,11 @@ ZAP_EMPTY_DEFAULT() }, /* CurrentFabricIndex */ \ { 0xFFFD, ZAP_TYPE(INT16U), 2, 0, ZAP_SIMPLE_DEFAULT(0x0001) }, /* ClusterRevision */ \ \ + /* Endpoint: 1, Cluster: Identify (server) */ \ + { 0x0000, ZAP_TYPE(INT16U), 2, ZAP_ATTRIBUTE_MASK(WRITABLE), ZAP_SIMPLE_DEFAULT(0x0000) }, /* identify time */ \ + { 0x0001, ZAP_TYPE(ENUM8), 1, 0, ZAP_SIMPLE_DEFAULT(0x0) }, /* identify type */ \ + { 0xFFFD, ZAP_TYPE(INT16U), 2, 0, ZAP_SIMPLE_DEFAULT(2) }, /* ClusterRevision */ \ + \ /* Endpoint: 1, Cluster: On/Off (server) */ \ { 0x0000, ZAP_TYPE(BOOLEAN), 1, 0, ZAP_SIMPLE_DEFAULT(0x00) }, /* OnOff */ \ { 0x4000, ZAP_TYPE(BOOLEAN), 1, 0, ZAP_SIMPLE_DEFAULT(0x01) }, /* GlobalSceneControl */ \ @@ -974,6 +979,10 @@ const EmberAfGenericClusterFunction chipFuncArrayBasicServer[] = { \ (EmberAfGenericClusterFunction) emberAfBasicClusterServerInitCallback, \ }; \ + const EmberAfGenericClusterFunction chipFuncArrayIdentifyServer[] = { \ + (EmberAfGenericClusterFunction) emberAfIdentifyClusterServerInitCallback, \ + (EmberAfGenericClusterFunction) MatterIdentifyClusterServerAttributeChangedCallback, \ + }; \ const EmberAfGenericClusterFunction chipFuncArrayOnOffServer[] = { \ (EmberAfGenericClusterFunction) emberAfOnOffClusterServerInitCallback, \ }; \ @@ -988,7 +997,7 @@ }; #define ZAP_CLUSTER_MASK(mask) CLUSTER_MASK_##mask -#define GENERATED_CLUSTER_COUNT 19 +#define GENERATED_CLUSTER_COUNT 20 #define GENERATED_CLUSTERS \ { \ { 0x001D, ZAP_ATTRIBUTE_INDEX(0), 5, 0, ZAP_CLUSTER_MASK(SERVER), NULL }, /* Endpoint: 0, Cluster: Descriptor (server) */ \ @@ -1028,38 +1037,44 @@ { \ 0x003E, ZAP_ATTRIBUTE_INDEX(127), 6, 4, ZAP_CLUSTER_MASK(SERVER), NULL \ }, /* Endpoint: 0, Cluster: Operational Credentials (server) */ \ - { 0x0006, \ + { 0x0003, \ ZAP_ATTRIBUTE_INDEX(133), \ + 3, \ + 5, \ + ZAP_CLUSTER_MASK(SERVER) | ZAP_CLUSTER_MASK(INIT_FUNCTION) | ZAP_CLUSTER_MASK(ATTRIBUTE_CHANGED_FUNCTION), \ + chipFuncArrayIdentifyServer }, /* Endpoint: 1, Cluster: Identify (server) */ \ + { 0x0006, \ + ZAP_ATTRIBUTE_INDEX(136), \ 7, \ 13, \ ZAP_CLUSTER_MASK(SERVER) | ZAP_CLUSTER_MASK(INIT_FUNCTION), \ chipFuncArrayOnOffServer }, /* Endpoint: 1, Cluster: On/Off (server) */ \ { 0x0008, \ - ZAP_ATTRIBUTE_INDEX(140), \ + ZAP_ATTRIBUTE_INDEX(143), \ 15, \ 23, \ ZAP_CLUSTER_MASK(SERVER) | ZAP_CLUSTER_MASK(INIT_FUNCTION), \ chipFuncArrayLevelControlServer }, /* Endpoint: 1, Cluster: Level Control (server) */ \ { \ - 0x001D, ZAP_ATTRIBUTE_INDEX(155), 5, 0, ZAP_CLUSTER_MASK(SERVER), NULL \ + 0x001D, ZAP_ATTRIBUTE_INDEX(158), 5, 0, ZAP_CLUSTER_MASK(SERVER), NULL \ }, /* Endpoint: 1, Cluster: Descriptor (server) */ \ { 0x0300, \ - ZAP_ATTRIBUTE_INDEX(160), \ + ZAP_ATTRIBUTE_INDEX(163), \ 22, \ 36, \ ZAP_CLUSTER_MASK(SERVER) | ZAP_CLUSTER_MASK(INIT_FUNCTION), \ chipFuncArrayColorControlServer }, /* Endpoint: 1, Cluster: Color Control (server) */ \ { 0x0406, \ - ZAP_ATTRIBUTE_INDEX(182), \ + ZAP_ATTRIBUTE_INDEX(185), \ 4, \ 5, \ ZAP_CLUSTER_MASK(SERVER) | ZAP_CLUSTER_MASK(INIT_FUNCTION), \ chipFuncArrayOccupancySensingServer }, /* Endpoint: 1, Cluster: Occupancy Sensing (server) */ \ { \ - 0x0006, ZAP_ATTRIBUTE_INDEX(186), 1, 2, ZAP_CLUSTER_MASK(CLIENT), NULL \ + 0x0006, ZAP_ATTRIBUTE_INDEX(189), 1, 2, ZAP_CLUSTER_MASK(CLIENT), NULL \ }, /* Endpoint: 2, Cluster: On/Off (client) */ \ { \ - 0x0007, ZAP_ATTRIBUTE_INDEX(187), 3, 4, ZAP_CLUSTER_MASK(SERVER), NULL \ + 0x0007, ZAP_ATTRIBUTE_INDEX(190), 3, 4, ZAP_CLUSTER_MASK(SERVER), NULL \ }, /* Endpoint: 2, Cluster: On/off Switch Configuration (server) */ \ } @@ -1068,7 +1083,7 @@ // This is an array of EmberAfEndpointType structures. #define GENERATED_ENDPOINT_TYPES \ { \ - { ZAP_CLUSTER_INDEX(0), 12, 1849 }, { ZAP_CLUSTER_INDEX(12), 5, 77 }, { ZAP_CLUSTER_INDEX(17), 2, 6 }, \ + { ZAP_CLUSTER_INDEX(0), 12, 1849 }, { ZAP_CLUSTER_INDEX(12), 6, 82 }, { ZAP_CLUSTER_INDEX(18), 2, 6 }, \ } // Largest attribute size is needed for various buffers @@ -1078,7 +1093,7 @@ #define ATTRIBUTE_SINGLETONS_SIZE (653) // Total size of attribute storage -#define ATTRIBUTE_MAX_SIZE (1932) +#define ATTRIBUTE_MAX_SIZE (1937) // Number of fixed endpoints #define FIXED_ENDPOINT_COUNT (3) diff --git a/zzz_generated/lighting-app/zap-generated/gen_config.h b/zzz_generated/lighting-app/zap-generated/gen_config.h index e9bf89d7566f2d..e80cb1f3b93d65 100644 --- a/zzz_generated/lighting-app/zap-generated/gen_config.h +++ b/zzz_generated/lighting-app/zap-generated/gen_config.h @@ -37,6 +37,7 @@ #define EMBER_AF_ETHERNET_NETWORK_DIAGNOSTICS_CLUSTER_SERVER_ENDPOINT_COUNT (1) #define EMBER_AF_GENERAL_COMMISSIONING_CLUSTER_SERVER_ENDPOINT_COUNT (1) #define EMBER_AF_GENERAL_DIAGNOSTICS_CLUSTER_SERVER_ENDPOINT_COUNT (1) +#define EMBER_AF_IDENTIFY_CLUSTER_SERVER_ENDPOINT_COUNT (1) #define EMBER_AF_LEVEL_CONTROL_CLUSTER_SERVER_ENDPOINT_COUNT (1) #define EMBER_AF_NETWORK_COMMISSIONING_CLUSTER_SERVER_ENDPOINT_COUNT (1) #define EMBER_AF_OCCUPANCY_SENSING_CLUSTER_SERVER_ENDPOINT_COUNT (1) @@ -94,6 +95,11 @@ #define EMBER_AF_PLUGIN_GENERAL_DIAGNOSTICS_SERVER #define EMBER_AF_PLUGIN_GENERAL_DIAGNOSTICS +// Use this macro to check if the server side of the Identify cluster is included +#define ZCL_USING_IDENTIFY_CLUSTER_SERVER +#define EMBER_AF_PLUGIN_IDENTIFY_SERVER +#define EMBER_AF_PLUGIN_IDENTIFY + // Use this macro to check if the server side of the Level Control cluster is included #define ZCL_USING_LEVEL_CONTROL_CLUSTER_SERVER #define EMBER_AF_PLUGIN_LEVEL_CONTROL_SERVER