diff --git a/examples/chip-tool/commands/common/CommandInvoker.h b/examples/chip-tool/commands/common/CommandInvoker.h index 1214361198a179..29088db1c09a32 100644 --- a/examples/chip-tool/commands/common/CommandInvoker.h +++ b/examples/chip-tool/commands/common/CommandInvoker.h @@ -106,7 +106,8 @@ class CommandInvoker final : public ResponseReceiverAddRequestData(commandPath, aRequestData)); - Optional session = exchangeManager->GetSessionManager()->CreateGroupSession(groupId); + Optional session = exchangeManager->GetSessionManager()->CreateGroupSession(groupId, fabric); if (!session.HasValue()) { return CHIP_ERROR_NO_MEMORY; @@ -231,7 +232,11 @@ CHIP_ERROR InvokeGroupCommand(DeviceProxy * aDevice, void * aContext, // invoker will be deleted by the onDone call before the return of InvokeGroupCommand // invoker should not be used after the InvokeGroupCommand call - ReturnErrorOnFailure(invoker->InvokeGroupCommand(aDevice->GetExchangeManager(), groupId, aRequestData)); + // + // We assume the aDevice already has a Case session which is way we can use he established Secure Session + ReturnErrorOnFailure(invoker->InvokeGroupCommand(aDevice->GetExchangeManager(), + aDevice->GetSecureSession().Value()->AsSecureSession()->GetFabricIndex(), + groupId, aRequestData)); // invoker is already deleted and is not to be used invoker.release(); diff --git a/examples/lighting-app/esp32/main/CMakeLists.txt b/examples/lighting-app/esp32/main/CMakeLists.txt index 0b2deeab79926a..fa6bc71be325cf 100644 --- a/examples/lighting-app/esp32/main/CMakeLists.txt +++ b/examples/lighting-app/esp32/main/CMakeLists.txt @@ -57,6 +57,7 @@ idf_component_register(PRIV_INCLUDE_DIRS "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/user-label-server" "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/wifi-network-diagnostics-server" "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/ota-requestor" + "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/groups-server" PRIV_REQUIRES chip QRCode bt led_strip app_update) set_property(TARGET ${COMPONENT_LIB} PROPERTY CXX_STANDARD 17) diff --git a/examples/lighting-app/lighting-common/lighting-app.matter b/examples/lighting-app/lighting-common/lighting-app.matter index 3e08b82c466ec1..ff7ef39e3ccd87 100644 --- a/examples/lighting-app/lighting-common/lighting-app.matter +++ b/examples/lighting-app/lighting-common/lighting-app.matter @@ -542,6 +542,61 @@ server cluster GeneralDiagnostics = 51 { readonly global attribute int16u clusterRevision = 65533; } +server cluster Groups = 4 { + readonly attribute bitmap8 nameSupport = 0; + readonly global attribute int16u clusterRevision = 65533; + + request struct AddGroupRequest { + INT16U groupId = 0; + CHAR_STRING groupName = 1; + } + + request struct AddGroupIfIdentifyingRequest { + INT16U groupId = 0; + CHAR_STRING groupName = 1; + } + + request struct GetGroupMembershipRequest { + INT16U groupList[] = 0; + } + + request struct RemoveGroupRequest { + INT16U groupId = 0; + } + + request struct ViewGroupRequest { + INT16U groupId = 0; + } + + response struct AddGroupResponse { + ENUM8 status = 0; + INT16U groupId = 1; + } + + response struct GetGroupMembershipResponse { + INT8U capacity = 0; + INT16U groupList[] = 1; + } + + response struct RemoveGroupResponse { + ENUM8 status = 0; + INT16U groupId = 1; + } + + response struct ViewGroupResponse { + ENUM8 status = 0; + INT16U groupId = 1; + CHAR_STRING groupName = 2; + } + + command AddGroup(AddGroupRequest): AddGroupResponse = 0; + command AddGroupIfIdentifying(AddGroupIfIdentifyingRequest): DefaultSuccess = 5; + command GetGroupMembership(GetGroupMembershipRequest): GetGroupMembershipResponse = 2; + command RemoveAllGroups(): DefaultSuccess = 4; + command RemoveGroup(RemoveGroupRequest): RemoveGroupResponse = 3; + command ViewGroup(ViewGroupRequest): ViewGroupResponse = 1; +} + server cluster Identify = 3 { enum IdentifyEffectIdentifier : ENUM8 { kBlink = 0; @@ -1424,6 +1479,7 @@ endpoint 0 { server cluster FixedLabel; server cluster GeneralCommissioning; server cluster GeneralDiagnostics; + server cluster Groups; server cluster LocalizationConfiguration; server cluster NetworkCommissioning; binding cluster OtaSoftwareUpdateProvider; @@ -1440,6 +1496,7 @@ endpoint 0 { endpoint 1 { server cluster ColorControl; server cluster Descriptor; + server cluster Groups; server cluster Identify; server cluster LevelControl; server cluster OccupancySensing; diff --git a/examples/lighting-app/lighting-common/lighting-app.zap b/examples/lighting-app/lighting-common/lighting-app.zap index ca2047ae333efb..19f4059098f7ba 100644 --- a/examples/lighting-app/lighting-common/lighting-app.zap +++ b/examples/lighting-app/lighting-common/lighting-app.zap @@ -210,7 +210,7 @@ "mfgCode": null, "define": "GROUPS_CLUSTER", "side": "server", - "enabled": 0, + "enabled": 1, "commands": [ { "name": "AddGroupResponse", @@ -4653,7 +4653,7 @@ "mfgCode": null, "define": "GROUPS_CLUSTER", "side": "server", - "enabled": 0, + "enabled": 1, "commands": [ { "name": "AddGroupResponse", @@ -6734,6 +6734,26 @@ } ] }, + { + "name": "Groups", + "code": 4, + "mfgCode": null, + "define": "GROUPS_CLUSTER", + "side": "client", + "enabled": 0, + "commands": [], + "attributes": [] + }, + { + "name": "Groups", + "code": 4, + "mfgCode": null, + "define": "GROUPS_CLUSTER", + "side": "server", + "enabled": 0, + "commands": [], + "attributes": [] + }, { "name": "On/Off", "code": 6, diff --git a/examples/lighting-app/mbed/CMakeLists.txt b/examples/lighting-app/mbed/CMakeLists.txt index 38505dfc0c93b4..73df019875d8ac 100644 --- a/examples/lighting-app/mbed/CMakeLists.txt +++ b/examples/lighting-app/mbed/CMakeLists.txt @@ -101,6 +101,7 @@ target_sources(${APP_TARGET} PRIVATE ${CHIP_ROOT}/src/app/clusters/ota-requestor/ota-requestor-server.cpp ${CHIP_ROOT}/src/app/clusters/ota-requestor/BDXDownloader.cpp ${CHIP_ROOT}/src/app/clusters/ota-requestor/OTARequestor.cpp + ${CHIP_ROOT}/src/app/clusters/groups-server/groups-server.cpp ) target_link_libraries(${APP_TARGET} mbed-os-posix-socket mbed-os mbed-ble mbed-events mbed-netsocket mbed-storage mbed-storage-kv-global-api mbed-mbedtls mbed-emac chip) diff --git a/examples/lighting-app/telink/CMakeLists.txt b/examples/lighting-app/telink/CMakeLists.txt index b27eb35fff3680..7cefd3059f1bcd 100644 --- a/examples/lighting-app/telink/CMakeLists.txt +++ b/examples/lighting-app/telink/CMakeLists.txt @@ -100,4 +100,5 @@ target_sources(app PRIVATE ${CHIP_ROOT}/src/app/clusters/ota-requestor/ota-requestor-server.cpp ${CHIP_ROOT}/src/app/clusters/ota-requestor/BDXDownloader.cpp ${CHIP_ROOT}/src/app/clusters/ota-requestor/OTARequestor.cpp + ${CHIP_ROOT}/src/app/clusters/groups-server/groups-server.cpp ) diff --git a/src/app/CommandHandler.cpp b/src/app/CommandHandler.cpp index 4a2b77aeb983cd..b8e3b1970bd352 100644 --- a/src/app/CommandHandler.cpp +++ b/src/app/CommandHandler.cpp @@ -28,6 +28,7 @@ #include #include +#include #include #include @@ -127,7 +128,15 @@ CHIP_ERROR CommandHandler::ProcessInvokeRequest(System::PacketBufferHandle && pa VerifyOrReturnError(TLV::AnonymousTag() == invokeRequestsReader.GetTag(), CHIP_ERROR_INVALID_TLV_TAG); CommandDataIB::Parser commandData; ReturnErrorOnFailure(commandData.Init(invokeRequestsReader)); - ReturnErrorOnFailure(ProcessCommandDataIB(commandData)); + + if (mpExchangeCtx->IsGroupExchangeContext()) + { + ReturnErrorOnFailure(ProcessGroupCommandDataIB(commandData)); + } + else + { + ReturnErrorOnFailure(ProcessCommandDataIB(commandData)); + } } // if we have exhausted this container @@ -181,21 +190,24 @@ void CommandHandler::DecrementHoldOff() return; } - CHIP_ERROR err = CHIP_NO_ERROR; - if (!mpExchangeCtx->IsGroupExchangeContext()) + if (mpExchangeCtx->IsGroupExchangeContext()) { - err = SendCommandResponse(); + mpExchangeCtx->Close(); } - - if (err != CHIP_NO_ERROR) + else { - ChipLogError(DataManagement, "Failed to send command response: %" CHIP_ERROR_FORMAT, err.Format()); - // We marked the exchange as "WillSendMessage", need to shutdown the exchange manually to avoid leaking exchanges. - if (mpExchangeCtx != nullptr) + CHIP_ERROR err = SendCommandResponse(); + if (err != CHIP_NO_ERROR) { - mpExchangeCtx->Close(); + ChipLogError(DataManagement, "Failed to send command response: %" CHIP_ERROR_FORMAT, err.Format()); + // We marked the exchange as "WillSendMessage", need to shutdown the exchange manually to avoid leaking exchanges. + if (mpExchangeCtx != nullptr) + { + mpExchangeCtx->Close(); + } } } + Close(); } @@ -236,19 +248,7 @@ CHIP_ERROR CommandHandler::ProcessCommandDataIB(CommandDataIB::Parser & aCommand err = commandPath.GetCommandId(&concretePath.mCommandId); SuccessOrExit(err); - if (mpExchangeCtx != nullptr && mpExchangeCtx->IsGroupExchangeContext()) - { - // TODO retrieve Endpoint ID with GroupDataProvider using GroupId and FabricId - // Issue 11075 - - // Using endpoint 1 for test purposes - concretePath.mEndpointId = 1; - err = CHIP_NO_ERROR; - } - else - { - err = commandPath.GetEndpointId(&concretePath.mEndpointId); - } + err = commandPath.GetEndpointId(&concretePath.mEndpointId); SuccessOrExit(err); VerifyOrExit(mpCallback->CommandExists(concretePath), err = CHIP_ERROR_INVALID_PROFILE_ID); @@ -312,6 +312,103 @@ CHIP_ERROR CommandHandler::ProcessCommandDataIB(CommandDataIB::Parser & aCommand return CHIP_NO_ERROR; } +CHIP_ERROR CommandHandler::ProcessGroupCommandDataIB(CommandDataIB::Parser & aCommandElement) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + CommandPathIB::Parser commandPath; + TLV::TLVReader commandDataReader; + ClusterId clusterId; + CommandId commandId; + GroupId groupId; + FabricIndex fabric; + + Credentials::GroupDataProvider::GroupEndpoint mapping; + Credentials::GroupDataProvider * groupDataProvider = Credentials::GetGroupDataProvider(); + Credentials::GroupDataProvider::EndpointIterator * iterator; + + err = aCommandElement.GetPath(&commandPath); + SuccessOrExit(err); + + err = commandPath.GetClusterId(&clusterId); + SuccessOrExit(err); + + err = commandPath.GetCommandId(&commandId); + SuccessOrExit(err); + + groupId = mpExchangeCtx->GetSessionHandle()->AsGroupSession()->GetGroupId(); + fabric = GetAccessingFabricIndex(); + + ChipLogDetail(DataManagement, + "Received group command for Group=%" PRIu16 " Cluster=" ChipLogFormatMEI " Command=" ChipLogFormatMEI, groupId, + ChipLogValueMEI(clusterId), ChipLogValueMEI(commandId)); + + err = aCommandElement.GetData(&commandDataReader); + if (CHIP_END_OF_TLV == err) + { + ChipLogDetail(DataManagement, + "Received command without data for Group=%" PRIu16 " Cluster=" ChipLogFormatMEI " Command=" ChipLogFormatMEI, + groupId, ChipLogValueMEI(clusterId), ChipLogValueMEI(commandId)); + err = CHIP_NO_ERROR; + } + SuccessOrExit(err); + + iterator = groupDataProvider->IterateEndpoints(fabric); + VerifyOrExit(iterator != nullptr, err = CHIP_ERROR_NO_MEMORY); + + while (iterator->Next(mapping)) + { + if (groupId != mapping.group_id) + { + continue; + } + + ChipLogDetail(DataManagement, + "Processing group command for Endpoint=%" PRIu16 " Cluster=" ChipLogFormatMEI " Command=" ChipLogFormatMEI, + mapping.endpoint_id, ChipLogValueMEI(clusterId), ChipLogValueMEI(commandId)); + + const ConcreteCommandPath concretePath(mapping.endpoint_id, clusterId, commandId); + + if (!mpCallback->CommandExists(concretePath)) + { + ChipLogError(DataManagement, "No Cluster " ChipLogFormatMEI " on Endpoint 0x%" PRIx16, ChipLogValueMEI(clusterId), + mapping.endpoint_id); + + continue; + } + + { + Access::SubjectDescriptor subjectDescriptor = mpExchangeCtx->GetSessionHandle()->GetSubjectDescriptor(); + Access::RequestPath requestPath{ .cluster = concretePath.mClusterId, .endpoint = concretePath.mEndpointId }; + Access::Privilege requestPrivilege = Access::Privilege::kOperate; // TODO: get actual request privilege + err = Access::GetAccessControl().Check(subjectDescriptor, requestPath, requestPrivilege); + err = CHIP_NO_ERROR; // TODO: remove override + if (err != CHIP_NO_ERROR) + { + continue; + } + } + + if ((err = MatterPreCommandReceivedCallback(concretePath)) == CHIP_NO_ERROR) + { + TLV::TLVReader dataReader(commandDataReader); + mpCallback->DispatchCommand(*this, concretePath, dataReader); + MatterPostCommandReceivedCallback(concretePath); + } + else + { + ChipLogError(DataManagement, + "Error when calling MatterPreCommandReceivedCallback for Endpoint=%" PRIu16 " Cluster=" ChipLogFormatMEI + " Command=" ChipLogFormatMEI " : %" CHIP_ERROR_FORMAT, + mapping.endpoint_id, ChipLogValueMEI(clusterId), ChipLogValueMEI(commandId), err.Format()); + continue; + } + } + iterator->Release(); + +exit: + return CHIP_NO_ERROR; +} + CHIP_ERROR CommandHandler::AddStatusInternal(const ConcreteCommandPath & aCommandPath, const Protocols::InteractionModel::Status aStatus, const Optional & aClusterStatus) diff --git a/src/app/CommandHandler.h b/src/app/CommandHandler.h index c864104a045f38..5fdd10cca1a668 100644 --- a/src/app/CommandHandler.h +++ b/src/app/CommandHandler.h @@ -248,7 +248,17 @@ class CommandHandler */ void Close(); + /** + * ProcessCommandDataIB is only called when a unicast invoke command request is received + * It requires the endpointId in its command path to be able to dispatch the command + */ CHIP_ERROR ProcessCommandDataIB(CommandDataIB::Parser & aCommandElement); + + /** + * ProcessGroupCommandDataIB is only called when a group invoke command request is received + * It doesn't need the endpointId in it's command path since it uses the GroupId in message metadata to find it + */ + CHIP_ERROR ProcessGroupCommandDataIB(CommandDataIB::Parser & aCommandElement); CHIP_ERROR SendCommandResponse(); CHIP_ERROR AddStatusInternal(const ConcreteCommandPath & aCommandPath, const Protocols::InteractionModel::Status aStatus, const Optional & aClusterStatus); diff --git a/src/app/WriteHandler.cpp b/src/app/WriteHandler.cpp index 209246c68e9f62..0a91be508ccba9 100644 --- a/src/app/WriteHandler.cpp +++ b/src/app/WriteHandler.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include namespace chip { @@ -122,7 +123,8 @@ CHIP_ERROR WriteHandler::ProcessAttributeDataIBs(TLV::TLVReader & aAttributeData AttributePathIB::Parser attributePath; ClusterInfo clusterInfo; TLV::TLVReader reader = aAttributeDataIBsReader; - err = element.Init(reader); + + err = element.Init(reader); SuccessOrExit(err); err = element.GetPath(&attributePath); @@ -136,19 +138,9 @@ CHIP_ERROR WriteHandler::ProcessAttributeDataIBs(TLV::TLVReader & aAttributeData { err = CHIP_NO_ERROR; } - if (mpExchangeCtx->IsGroupExchangeContext()) - { - // TODO retrieve Endpoint ID with GroupDataProvider using GroupId and FabricId - // Issue 11075 - // Using endpoint 0 for test purposes - clusterInfo.mEndpointId = 0; - } - else - { - err = attributePath.GetEndpoint(&(clusterInfo.mEndpointId)); - SuccessOrExit(err); - } + err = attributePath.GetEndpoint(&(clusterInfo.mEndpointId)); + SuccessOrExit(err); err = attributePath.GetCluster(&(clusterInfo.mClusterId)); SuccessOrExit(err); @@ -188,6 +180,122 @@ CHIP_ERROR WriteHandler::ProcessAttributeDataIBs(TLV::TLVReader & aAttributeData return err; } +CHIP_ERROR WriteHandler::ProcessGroupAttributeDataIBs(TLV::TLVReader & aAttributeDataIBsReader) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + + ReturnErrorCodeIf(mpExchangeCtx == nullptr, CHIP_ERROR_INTERNAL); + const Access::SubjectDescriptor subjectDescriptor = mpExchangeCtx->GetSessionHandle()->AsGroupSession()->GetSubjectDescriptor(); + + while (CHIP_NO_ERROR == (err = aAttributeDataIBsReader.Next())) + { + chip::TLV::TLVReader dataReader; + AttributeDataIB::Parser element; + AttributePathIB::Parser attributePath; + ClusterInfo clusterInfo; + GroupId groupId; + FabricIndex fabric; + TLV::TLVReader reader = aAttributeDataIBsReader; + + Credentials::GroupDataProvider::GroupEndpoint mapping; + Credentials::GroupDataProvider * groupDataProvider = Credentials::GetGroupDataProvider(); + Credentials::GroupDataProvider::EndpointIterator * iterator; + + err = element.Init(reader); + SuccessOrExit(err); + + err = element.GetPath(&attributePath); + SuccessOrExit(err); + + // We are using the feature that the parser won't touch the value if the field does not exist, since all fields in the + // cluster info will be invalid / wildcard, it is safe to ignore CHIP_END_OF_TLV. + + err = attributePath.GetNode(&(clusterInfo.mNodeId)); + if (CHIP_END_OF_TLV == err) + { + err = CHIP_NO_ERROR; + } + + err = attributePath.GetCluster(&(clusterInfo.mClusterId)); + SuccessOrExit(err); + + err = attributePath.GetAttribute(&(clusterInfo.mAttributeId)); + SuccessOrExit(err); + + groupId = mpExchangeCtx->GetSessionHandle()->AsGroupSession()->GetGroupId(); + fabric = GetAccessingFabricIndex(); + + err = attributePath.GetListIndex(&(clusterInfo.mListIndex)); + if (CHIP_END_OF_TLV == err) + { + err = CHIP_NO_ERROR; + } + + err = element.GetData(&dataReader); + SuccessOrExit(err); + + ChipLogDetail(DataManagement, + "Received group attribute write for Group=%" PRIu16 " Cluster=" ChipLogFormatMEI + " attribute=" ChipLogFormatMEI, + groupId, ChipLogValueMEI(clusterInfo.mClusterId), ChipLogValueMEI(clusterInfo.mAttributeId)); + + iterator = groupDataProvider->IterateEndpoints(fabric); + VerifyOrExit(iterator != nullptr, err = CHIP_ERROR_NO_MEMORY); + + while (iterator->Next(mapping)) + { + if (groupId != mapping.group_id) + { + continue; + } + + clusterInfo.mEndpointId = mapping.endpoint_id; + + if (!clusterInfo.IsValidAttributePath() || clusterInfo.HasAttributeWildcard()) + { + ChipLogDetail(DataManagement, + "Invalid group attribute write for endpoint=%" PRIu16 " Cluster=" ChipLogFormatMEI + " attribute=" ChipLogFormatMEI, + mapping.endpoint_id, ChipLogValueMEI(clusterInfo.mClusterId), + ChipLogValueMEI(clusterInfo.mAttributeId)); + + continue; + } + + ChipLogDetail(DataManagement, + "Processing group attribute write for endpoint=%" PRIu16 " Cluster=" ChipLogFormatMEI + " attribute=" ChipLogFormatMEI, + mapping.endpoint_id, ChipLogValueMEI(clusterInfo.mClusterId), ChipLogValueMEI(clusterInfo.mAttributeId)); + + chip::TLV::TLVReader tmpDataReader(dataReader); + + const ConcreteAttributePath concretePath(clusterInfo.mEndpointId, clusterInfo.mClusterId, clusterInfo.mAttributeId); + + MatterPreAttributeWriteCallback(concretePath); + err = WriteSingleClusterData(subjectDescriptor, clusterInfo, tmpDataReader, this); + + if (err != CHIP_NO_ERROR) + { + ChipLogError(DataManagement, + "Error when calling WriteSingleClusterData for Endpoint=%" PRIu16 " Cluster=" ChipLogFormatMEI + " Attribute =" ChipLogFormatMEI " : %" CHIP_ERROR_FORMAT, + mapping.endpoint_id, ChipLogValueMEI(clusterInfo.mClusterId), + ChipLogValueMEI(clusterInfo.mAttributeId), err.Format()); + } + MatterPostAttributeWriteCallback(concretePath); + } + + iterator->Release(); + } + + if (CHIP_END_OF_TLV == err) + { + err = CHIP_NO_ERROR; + } +exit: + return err; +} + Status WriteHandler::ProcessWriteRequest(System::PacketBufferHandle && aPayload, bool aIsTimedWrite) { CHIP_ERROR err = CHIP_NO_ERROR; @@ -243,7 +351,16 @@ Status WriteHandler::ProcessWriteRequest(System::PacketBufferHandle && aPayload, } AttributeDataIBsParser.GetReader(&AttributeDataIBsReader); - err = ProcessAttributeDataIBs(AttributeDataIBsReader); + + if (mpExchangeCtx->IsGroupExchangeContext()) + { + err = ProcessGroupAttributeDataIBs(AttributeDataIBsReader); + } + else + { + err = ProcessAttributeDataIBs(AttributeDataIBsReader); + } + if (err == CHIP_NO_ERROR) { status = Status::Success; diff --git a/src/app/WriteHandler.h b/src/app/WriteHandler.h index b6c838641f8b87..1458a17de9106f 100644 --- a/src/app/WriteHandler.h +++ b/src/app/WriteHandler.h @@ -74,6 +74,7 @@ class WriteHandler virtual ~WriteHandler() = default; CHIP_ERROR ProcessAttributeDataIBs(TLV::TLVReader & aAttributeDataIBsReader); + CHIP_ERROR ProcessGroupAttributeDataIBs(TLV::TLVReader & aAttributeDataIBsReader); CHIP_ERROR AddStatus(const AttributePathParams & aAttributePathParams, const Protocols::InteractionModel::Status aStatus); diff --git a/src/app/server/Server.cpp b/src/app/server/Server.cpp index 5721659da0a118..28e49475510439 100644 --- a/src/app/server/Server.cpp +++ b/src/app/server/Server.cpp @@ -149,21 +149,15 @@ CHIP_ERROR Server::Init(AppDelegate * delegate, uint16_t secureServicePort, uint #endif ); + err = mListener.Init(&mTransports); + SuccessOrExit(err); + mGroupsProvider.SetListener(&mListener); + #if CONFIG_NETWORK_LAYER_BLE mBleLayer = DeviceLayer::ConnectivityMgr().GetBleLayer(); #endif SuccessOrExit(err); -// Enable Group Listening -// TODO : Fix this once GroupDataProvider is implemented #Issue 11075 -// for (iterate through all GroupDataProvider multicast Address) -// { -#ifdef CHIP_ENABLE_GROUP_MESSAGING_TESTS - err = mTransports.MulticastGroupJoinLeave(Transport::PeerAddress::Multicast(0, 1234), true); - SuccessOrExit(err); -#endif - //} - err = mSessions.Init(&DeviceLayer::SystemLayer(), &mTransports, &mMessageCounterManager); SuccessOrExit(err); @@ -238,6 +232,40 @@ CHIP_ERROR Server::Init(AppDelegate * delegate, uint16_t secureServicePort, uint err = mCASESessionManager.Init(); + // This code is necessary to restart listening to existing groups after a reboot + // Each manufacturer needs to validate that they can rejoin groups by placing this code at the appropriate location for them + // + // This is disabled for thread device because the same code is already present for thread devices in + // src/platform/OpenThread/GenericThreadStackManagerImpl_OpenThread_LwIP.cpp + // https://github.com/project-chip/connectedhomeip/issues/14254 +#if !CHIP_DEVICE_CONFIG_ENABLE_THREAD + { + ChipLogProgress(AppServer, "Adding Multicast groups"); + ConstFabricIterator fabricIterator = mFabrics.cbegin(); + while (!fabricIterator.IsAtEnd()) + { + const FabricInfo & fabric = *fabricIterator; + Credentials::GroupDataProvider::GroupInfo groupInfo; + + Credentials::GroupDataProvider::GroupInfoIterator * iterator = + mGroupsProvider.IterateGroupInfo(fabric.GetFabricIndex()); + while (iterator->Next(groupInfo)) + { + err = mTransports.MulticastGroupJoinLeave( + Transport::PeerAddress::Multicast(fabric.GetFabricIndex(), groupInfo.group_id), true); + if (err != CHIP_NO_ERROR) + { + ChipLogError(AppServer, "Error when trying to join Group %" PRIu16 " of fabric index %" PRIu8, + groupInfo.group_id, fabric.GetFabricIndex()); + break; + } + } + + fabricIterator++; + iterator->Release(); + } + } +#endif // !CHIP_DEVICE_CONFIG_ENABLE_THREAD exit: if (err != CHIP_NO_ERROR) { diff --git a/src/app/server/Server.h b/src/app/server/Server.h index a10e5bcf4695df..46258222705953 100644 --- a/src/app/server/Server.h +++ b/src/app/server/Server.h @@ -135,6 +135,37 @@ class Server CHIP_ERROR SyncDelete(FabricIndex fabricIndex, const char * key) override { return SyncDeleteKeyValue(key); }; }; + class GroupDataProviderListener final : public Credentials::GroupDataProvider::GroupListener + { + public: + GroupDataProviderListener() {} + + CHIP_ERROR Init(ServerTransportMgr * transports) + { + VerifyOrReturnError(transports != nullptr, CHIP_ERROR_INVALID_ARGUMENT); + + mTransports = transports; + return CHIP_NO_ERROR; + }; + + void OnGroupAdded(chip::FabricIndex fabric_index, const Credentials::GroupDataProvider::GroupInfo & new_group) override + { + if (mTransports->MulticastGroupJoinLeave(Transport::PeerAddress::Multicast(fabric_index, new_group.group_id), true) != + CHIP_NO_ERROR) + { + ChipLogError(AppServer, "Unable to listen to group"); + } + }; + + void OnGroupRemoved(chip::FabricIndex fabric_index, const Credentials::GroupDataProvider::GroupInfo & old_group) override + { + mTransports->MulticastGroupJoinLeave(Transport::PeerAddress::Multicast(fabric_index, old_group.group_id), false); + }; + + private: + ServerTransportMgr * mTransports; + }; + #if CONFIG_NETWORK_LAYER_BLE Ble::BleLayer * mBleLayer = nullptr; #endif @@ -168,6 +199,7 @@ class Server #endif Credentials::GroupDataProviderImpl mGroupsProvider; app::DefaultAttributePersistenceProvider mAttributePersister; + GroupDataProviderListener mListener; // TODO @ceille: Maybe use OperationalServicePort and CommissionableServicePort uint16_t mSecuredServicePort; diff --git a/src/app/tests/TestWriteInteraction.cpp b/src/app/tests/TestWriteInteraction.cpp index 0f87a42cddcb09..9e8419ced7bd3a 100644 --- a/src/app/tests/TestWriteInteraction.cpp +++ b/src/app/tests/TestWriteInteraction.cpp @@ -19,11 +19,13 @@ #include #include #include +#include #include #include #include #include #include +#include #include #include #include @@ -419,6 +421,12 @@ void TestWriteInteraction::TestWriteRoundtrip(nlTestSuite * apSuite, void * apCo namespace { +constexpr uint16_t kMaxGroupsPerFabric = 5; +constexpr uint16_t kMaxGroupKeysPerFabric = 8; + +static chip::TestPersistentStorageDelegate sDelegate; +static chip::Credentials::GroupDataProviderImpl sProvider(sDelegate, kMaxGroupsPerFabric, kMaxGroupKeysPerFabric); + /** * Test Suite. It lists all the test functions. */ @@ -436,12 +444,47 @@ const nlTest sTests[] = // clang-format on // clang-format off + +/** + * Set up the test suite. + */ +int Test_Setup(void * inContext) +{ + SetGroupDataProvider(&sProvider); + VerifyOrReturnError(CHIP_NO_ERROR == chip::Platform::MemoryInit(), FAILURE); + VerifyOrReturnError(CHIP_NO_ERROR == sProvider.Init(), FAILURE); + + + VerifyOrReturnError(TestContext::Initialize(inContext) == SUCCESS, FAILURE); + + return SUCCESS; +} + +/** + * Tear down the test suite. + */ +int Test_Teardown(void * inContext) +{ + chip::Platform::MemoryShutdown(); + chip::Credentials::GroupDataProvider * provider = chip::Credentials::GetGroupDataProvider(); + if (nullptr != provider) + { + provider->Finish(); + } + + + VerifyOrReturnError(TestContext::Finalize(inContext) == SUCCESS, FAILURE); + + + return SUCCESS; +} + nlTestSuite sSuite = { "TestWriteInteraction", &sTests[0], - TestContext::Initialize, - TestContext::Finalize + &Test_Setup, + &Test_Teardown }; // clang-format on diff --git a/src/app/tests/suites/TestGroupMessaging.yaml b/src/app/tests/suites/TestGroupMessaging.yaml index cb151176a75796..9bd05e97e41c03 100644 --- a/src/app/tests/suites/TestGroupMessaging.yaml +++ b/src/app/tests/suites/TestGroupMessaging.yaml @@ -29,14 +29,45 @@ tests: cluster: "DelayCommands" command: "WaitForCommissionee" - #TODO : Add Group membership command when implemented Issue #11077 - # - label: "Add device to Group" - # command: "TODO" + - label: "Add Group 1 - endpoint 1" + cluster: "Groups" + command: "AddGroup" + endpoint: 1 + arguments: + values: + - name: "groupId" + value: 0x1234 + - name: "groupName" + value: "Group #1" + response: + values: + - name: "status" + value: 0 + - name: "groupId" + value: 0x1234 + + - label: "Add Group 2 - endpoint 0" + cluster: "Groups" + command: "AddGroup" + endpoint: 0 + arguments: + values: + - name: "groupId" + value: 0x0001 + - name: "groupName" + value: "Group #2" + response: + values: + - name: "status" + value: 0 + - name: "groupId" + value: 0x0001 + # Test Pair 1 : Sends a Group Write Attribute - label: "Group Write Attribute" command: "writeAttribute" attribute: "location" - groupId: "1234" + groupId: "1" arguments: value: "us" @@ -51,7 +82,7 @@ tests: - label: "Restore initial location value" command: "writeAttribute" attribute: "location" - groupId: "1234" + groupId: "1" arguments: value: "" @@ -66,10 +97,11 @@ tests: - label: "Turn On the light to see attribute change" cluster: "On/Off" command: "On" - groupId: "1234" + groupId: "4660" - # Test Pair 3 : Validates previous group command with a unicast to read - - label: "Check on/off attribute value is true after on command" + # Test Pair 3 : Validates previous group command with a unicast to read + - label: + "Check on/off attribute value is true after on command for endpoint 1" cluster: "On/Off" command: "readAttribute" attribute: "OnOff" diff --git a/src/app/util/ember-compatibility-functions.cpp b/src/app/util/ember-compatibility-functions.cpp index 45494a42aed398..588b078e64b03f 100644 --- a/src/app/util/ember-compatibility-functions.cpp +++ b/src/app/util/ember-compatibility-functions.cpp @@ -158,6 +158,10 @@ void SetupEmberAfCommandSender(CommandSender * command, const ConcreteCommandPat { imCompatibilityEmberAfCluster.type = EMBER_INCOMING_MULTICAST; } + else + { + imCompatibilityEmberAfCluster.type = EMBER_INCOMING_UNICAST; + } imCompatibilityEmberAfCluster.commandId = commandPath.mCommandId; imCompatibilityEmberAfCluster.apsFrame = &imCompatibilityEmberApsFrame; @@ -181,6 +185,10 @@ void SetupEmberAfCommandHandler(CommandHandler * command, const ConcreteCommandP { imCompatibilityEmberAfCluster.type = EMBER_INCOMING_MULTICAST; } + else + { + imCompatibilityEmberAfCluster.type = EMBER_INCOMING_UNICAST; + } imCompatibilityEmberAfCluster.commandId = commandPath.mCommandId; imCompatibilityEmberAfCluster.apsFrame = &imCompatibilityEmberApsFrame; diff --git a/src/controller/CHIPCluster.cpp b/src/controller/CHIPCluster.cpp index 34a3f681261b39..ef94a7c2c823b2 100644 --- a/src/controller/CHIPCluster.cpp +++ b/src/controller/CHIPCluster.cpp @@ -52,7 +52,8 @@ CHIP_ERROR ClusterBase::AssociateWithGroup(DeviceProxy * device, GroupId groupId if (mDevice->GetSecureSession().HasValue()) { // Local copy to preserve original SessionHandle for future Unicast communication. - Optional session = mDevice->GetExchangeManager()->GetSessionManager()->CreateGroupSession(groupId); + Optional session = mDevice->GetExchangeManager()->GetSessionManager()->CreateGroupSession( + groupId, mDevice->GetSecureSession().Value()->AsSecureSession()->GetFabricIndex()); // Sanity check if (!session.HasValue() || !session.Value()->IsGroupSession()) { diff --git a/src/messaging/ExchangeContext.cpp b/src/messaging/ExchangeContext.cpp index 6757341ecc56c0..a2dc5cbfa75dc7 100644 --- a/src/messaging/ExchangeContext.cpp +++ b/src/messaging/ExchangeContext.cpp @@ -133,8 +133,10 @@ CHIP_ERROR ExchangeContext::SendMessage(Protocols::Id protocolId, uint8_t msgTyp // an error arising below. at the end, we have to close it. ExchangeHandle ref(*this); - // If session requires MRP and NoAutoRequestAck send flag is not specificed, request reliable transmission. - bool reliableTransmissionRequested = GetSessionHandle()->RequireMRP() && !sendFlags.Has(SendMessageFlags::kNoAutoRequestAck); + // If session requires MRP, NoAutoRequestAck send flag is not specified and is not a group exchange context, request reliable + // transmission. + bool reliableTransmissionRequested = + GetSessionHandle()->RequireMRP() && !sendFlags.Has(SendMessageFlags::kNoAutoRequestAck) && !IsGroupExchangeContext(); // If a response message is expected... if (sendFlags.Has(SendMessageFlags::kExpectResponse) && !IsGroupExchangeContext()) @@ -226,8 +228,7 @@ void ExchangeContext::Close() VerifyOrDie(mExchangeMgr != nullptr && GetReferenceCount() > 0); #if defined(CHIP_EXCHANGE_CONTEXT_DETAIL_LOGGING) - ChipLogDetail(ExchangeManager, "ec id: %d [" ChipLogFormatExchange "], %s", (this - mExchangeMgr->mContextPool.begin()), - ChipLogValueExchange(this), __func__); + ChipLogDetail(ExchangeManager, "ec - close[" ChipLogFormatExchange "], %s", ChipLogValueExchange(this), __func__); #endif DoClose(false); @@ -243,8 +244,7 @@ void ExchangeContext::Abort() VerifyOrDie(mExchangeMgr != nullptr && GetReferenceCount() > 0); #if defined(CHIP_EXCHANGE_CONTEXT_DETAIL_LOGGING) - ChipLogDetail(ExchangeManager, "ec id: %d [" ChipLogFormatExchange "], %s", (this - mExchangeMgr->mContextPool.begin()), - ChipLogValueExchange(this), __func__); + ChipLogDetail(ExchangeManager, "ec - abort[" ChipLogFormatExchange "], %s", ChipLogValueExchange(this), __func__); #endif DoClose(true); diff --git a/src/messaging/tests/MessagingContext.cpp b/src/messaging/tests/MessagingContext.cpp index 39d1c51070e533..05be52b6ef008a 100644 --- a/src/messaging/tests/MessagingContext.cpp +++ b/src/messaging/tests/MessagingContext.cpp @@ -82,7 +82,7 @@ CHIP_ERROR MessagingContext::CreateSessionAliceToBob() CHIP_ERROR MessagingContext::CreateSessionBobToFriends() { - mSessionBobToFriends.Grab(mSessionManager.CreateGroupSession(GetFriendsGroupId()).Value()); + mSessionBobToFriends.Grab(mSessionManager.CreateGroupSession(GetFriendsGroupId(), mSrcFabricIndex).Value()); return CHIP_NO_ERROR; } @@ -113,7 +113,7 @@ void MessagingContext::ExpireSessionAliceToBob() void MessagingContext::ExpireSessionBobToFriends() { - // TODO: expire the group session + mSessionManager.RemoveGroupSession(mSessionBobToFriends.Get()->AsGroupSession()); } Messaging::ExchangeContext * MessagingContext::NewUnauthenticatedExchangeToAlice(Messaging::ExchangeDelegate * delegate) diff --git a/src/messaging/tests/MessagingContext.h b/src/messaging/tests/MessagingContext.h index 768fde0abb26f5..cc99415e729029 100644 --- a/src/messaging/tests/MessagingContext.h +++ b/src/messaging/tests/MessagingContext.h @@ -128,8 +128,8 @@ class MessagingContext SessionHolder mSessionAliceToBob; SessionHolder mSessionBobToAlice; SessionHolder mSessionBobToFriends; - FabricIndex mSrcFabricIndex = 0; - FabricIndex mDestFabricIndex = 0; + FabricIndex mSrcFabricIndex = 1; + FabricIndex mDestFabricIndex = 1; }; template diff --git a/src/transport/SessionManager.cpp b/src/transport/SessionManager.cpp index 2aca1cdcaf8445..8397aefe1b8c58 100644 --- a/src/transport/SessionManager.cpp +++ b/src/transport/SessionManager.cpp @@ -617,7 +617,9 @@ void SessionManager::SecureGroupMessageDispatch(const PacketHeader & packetHeade if (mCB != nullptr) { - Optional session = CreateGroupSession(packetHeader.GetDestinationGroupId().Value()); + // TODO : remove hard coded fabric index once GroupDataProvider->Decrypt is implemented + Optional session = CreateGroupSession(packetHeader.GetDestinationGroupId().Value(), 1); + VerifyOrReturn(session.HasValue(), ChipLogError(Inet, "Error when creating group session handle.")); Transport::GroupSession * groupSession = session.Value()->AsGroupSession(); diff --git a/src/transport/SessionManager.h b/src/transport/SessionManager.h index b7005854f6e64d..7d30ba0f2364cf 100644 --- a/src/transport/SessionManager.h +++ b/src/transport/SessionManager.h @@ -210,8 +210,14 @@ class DLL_EXPORT SessionManager : public TransportMgrDelegate } // TODO: implements group sessions - Optional CreateGroupSession(GroupId group) { return mGroupSessions.AllocEntry(group, kUndefinedFabricIndex); } - Optional FindGroupSession(GroupId group) { return mGroupSessions.FindEntry(group, kUndefinedFabricIndex); } + Optional CreateGroupSession(GroupId group, chip::FabricIndex fabricIndex) + { + return mGroupSessions.AllocEntry(group, fabricIndex); + } + Optional FindGroupSession(GroupId group, chip::FabricIndex fabricIndex) + { + return mGroupSessions.FindEntry(group, fabricIndex); + } void RemoveGroupSession(Transport::GroupSession * session) { mGroupSessions.DeleteEntry(session); } // TODO: this is a temporary solution for legacy tests which use nodeId to send packets diff --git a/zzz_generated/chip-tool/zap-generated/test/Commands.h b/zzz_generated/chip-tool/zap-generated/test/Commands.h index dcc177858908c9..bf0a365499a23e 100644 --- a/zzz_generated/chip-tool/zap-generated/test/Commands.h +++ b/zzz_generated/chip-tool/zap-generated/test/Commands.h @@ -73068,28 +73068,37 @@ class TestGroupMessaging : public TestCommand err = TestWaitForTheCommissionedDeviceToBeRetrieved_0(); break; case 1: - ChipLogProgress(chipTool, " ***** Test Step 1 : Group Write Attribute\n"); - err = TestGroupWriteAttribute_1(); + ChipLogProgress(chipTool, " ***** Test Step 1 : Add Group 1 - endpoint 1\n"); + err = TestAddGroup1Endpoint1_1(); break; case 2: - ChipLogProgress(chipTool, " ***** Test Step 2 : Read back Attribute\n"); - err = TestReadBackAttribute_2(); + ChipLogProgress(chipTool, " ***** Test Step 2 : Add Group 2 - endpoint 0\n"); + err = TestAddGroup2Endpoint0_2(); break; case 3: - ChipLogProgress(chipTool, " ***** Test Step 3 : Restore initial location value\n"); - err = TestRestoreInitialLocationValue_3(); + ChipLogProgress(chipTool, " ***** Test Step 3 : Group Write Attribute\n"); + err = TestGroupWriteAttribute_3(); break; case 4: ChipLogProgress(chipTool, " ***** Test Step 4 : Read back Attribute\n"); err = TestReadBackAttribute_4(); break; case 5: - ChipLogProgress(chipTool, " ***** Test Step 5 : Turn On the light to see attribute change\n"); - err = TestTurnOnTheLightToSeeAttributeChange_5(); + ChipLogProgress(chipTool, " ***** Test Step 5 : Restore initial location value\n"); + err = TestRestoreInitialLocationValue_5(); break; case 6: - ChipLogProgress(chipTool, " ***** Test Step 6 : Check on/off attribute value is true after on command\n"); - err = TestCheckOnOffAttributeValueIsTrueAfterOnCommand_6(); + ChipLogProgress(chipTool, " ***** Test Step 6 : Read back Attribute\n"); + err = TestReadBackAttribute_6(); + break; + case 7: + ChipLogProgress(chipTool, " ***** Test Step 7 : Turn On the light to see attribute change\n"); + err = TestTurnOnTheLightToSeeAttributeChange_7(); + break; + case 8: + ChipLogProgress(chipTool, + " ***** Test Step 8 : Check on/off attribute value is true after on command for endpoint 1\n"); + err = TestCheckOnOffAttributeValueIsTrueAfterOnCommandForEndpoint1_8(); break; } @@ -73102,57 +73111,57 @@ class TestGroupMessaging : public TestCommand private: std::atomic_uint16_t mTestIndex; - const uint16_t mTestCount = 7; + const uint16_t mTestCount = 9; chip::Optional mCluster; chip::Optional mEndpoint; - static void OnDoneCallback_1(void * context) { (static_cast(context))->OnDoneResponse_1(); } + static void OnDoneCallback_3(void * context) { (static_cast(context))->OnDoneResponse_3(); } - static void OnFailureCallback_1(void * context, CHIP_ERROR error) + static void OnFailureCallback_3(void * context, CHIP_ERROR error) { - (static_cast(context))->OnFailureResponse_1(error); + (static_cast(context))->OnFailureResponse_3(error); } - static void OnSuccessCallback_1(void * context) { (static_cast(context))->OnSuccessResponse_1(); } + static void OnSuccessCallback_3(void * context) { (static_cast(context))->OnSuccessResponse_3(); } - static void OnFailureCallback_2(void * context, CHIP_ERROR error) + static void OnFailureCallback_4(void * context, CHIP_ERROR error) { - (static_cast(context))->OnFailureResponse_2(error); + (static_cast(context))->OnFailureResponse_4(error); } - static void OnSuccessCallback_2(void * context, chip::CharSpan location) + static void OnSuccessCallback_4(void * context, chip::CharSpan location) { - (static_cast(context))->OnSuccessResponse_2(location); + (static_cast(context))->OnSuccessResponse_4(location); } - static void OnDoneCallback_3(void * context) { (static_cast(context))->OnDoneResponse_3(); } + static void OnDoneCallback_5(void * context) { (static_cast(context))->OnDoneResponse_5(); } - static void OnFailureCallback_3(void * context, CHIP_ERROR error) + static void OnFailureCallback_5(void * context, CHIP_ERROR error) { - (static_cast(context))->OnFailureResponse_3(error); + (static_cast(context))->OnFailureResponse_5(error); } - static void OnSuccessCallback_3(void * context) { (static_cast(context))->OnSuccessResponse_3(); } + static void OnSuccessCallback_5(void * context) { (static_cast(context))->OnSuccessResponse_5(); } - static void OnFailureCallback_4(void * context, CHIP_ERROR error) + static void OnFailureCallback_6(void * context, CHIP_ERROR error) { - (static_cast(context))->OnFailureResponse_4(error); + (static_cast(context))->OnFailureResponse_6(error); } - static void OnSuccessCallback_4(void * context, chip::CharSpan location) + static void OnSuccessCallback_6(void * context, chip::CharSpan location) { - (static_cast(context))->OnSuccessResponse_4(location); + (static_cast(context))->OnSuccessResponse_6(location); } - static void OnFailureCallback_6(void * context, CHIP_ERROR error) + static void OnFailureCallback_8(void * context, CHIP_ERROR error) { - (static_cast(context))->OnFailureResponse_6(error); + (static_cast(context))->OnFailureResponse_8(error); } - static void OnSuccessCallback_6(void * context, bool onOff) + static void OnSuccessCallback_8(void * context, bool onOff) { - (static_cast(context))->OnSuccessResponse_6(onOff); + (static_cast(context))->OnSuccessResponse_8(onOff); } // @@ -73165,17 +73174,24 @@ class TestGroupMessaging : public TestCommand return WaitForCommissionee(); } - CHIP_ERROR TestGroupWriteAttribute_1() + CHIP_ERROR TestAddGroup1Endpoint1_1() { - const chip::GroupId groupId = 1234; - chip::Controller::BasicClusterTest cluster; - cluster.AssociateWithGroup(mDevices[kIdentityAlpha], groupId); + const chip::EndpointId endpoint = mEndpoint.HasValue() ? mEndpoint.Value() : 1; + using RequestType = chip::app::Clusters::Groups::Commands::AddGroup::Type; - chip::CharSpan locationArgument; - locationArgument = chip::Span("usgarbage: not in length on purpose", 2); + RequestType request; + request.groupId = 4660U; + request.groupName = chip::Span("Group #1garbage: not in length on purpose", 8); - ReturnErrorOnFailure(cluster.WriteAttribute( - locationArgument, this, OnSuccessCallback_1, OnFailureCallback_1, OnDoneCallback_1)); + auto success = [](void * context, const typename RequestType::ResponseType & data) { + (static_cast(context))->OnSuccessResponse_1(data.status, data.groupId); + }; + + auto failure = [](void * context, CHIP_ERROR error) { + (static_cast(context))->OnFailureResponse_1(error); + }; + + ReturnErrorOnFailure(chip::Controller::InvokeCommand(mDevices[kIdentityAlpha], this, success, failure, endpoint, request)); return CHIP_NO_ERROR; } @@ -73185,18 +73201,33 @@ class TestGroupMessaging : public TestCommand ThrowFailureResponse(); } - void OnSuccessResponse_1() { NextTest(); } + void OnSuccessResponse_1(uint8_t status, uint16_t groupId) + { + VerifyOrReturn(CheckValue("status", status, 0)); + + VerifyOrReturn(CheckValue("groupId", groupId, 4660U)); - void OnDoneResponse_1() { NextTest(); } + NextTest(); + } - CHIP_ERROR TestReadBackAttribute_2() + CHIP_ERROR TestAddGroup2Endpoint0_2() { const chip::EndpointId endpoint = mEndpoint.HasValue() ? mEndpoint.Value() : 0; - chip::Controller::BasicClusterTest cluster; - cluster.Associate(mDevices[kIdentityAlpha], endpoint); + using RequestType = chip::app::Clusters::Groups::Commands::AddGroup::Type; - ReturnErrorOnFailure(cluster.ReadAttribute( - this, OnSuccessCallback_2, OnFailureCallback_2)); + RequestType request; + request.groupId = 1U; + request.groupName = chip::Span("Group #2garbage: not in length on purpose", 8); + + auto success = [](void * context, const typename RequestType::ResponseType & data) { + (static_cast(context))->OnSuccessResponse_2(data.status, data.groupId); + }; + + auto failure = [](void * context, CHIP_ERROR error) { + (static_cast(context))->OnFailureResponse_2(error); + }; + + ReturnErrorOnFailure(chip::Controller::InvokeCommand(mDevices[kIdentityAlpha], this, success, failure, endpoint, request)); return CHIP_NO_ERROR; } @@ -73206,21 +73237,23 @@ class TestGroupMessaging : public TestCommand ThrowFailureResponse(); } - void OnSuccessResponse_2(chip::CharSpan location) + void OnSuccessResponse_2(uint8_t status, uint16_t groupId) { - VerifyOrReturn(CheckValueAsString("location", location, chip::CharSpan("us", 2))); + VerifyOrReturn(CheckValue("status", status, 0)); + + VerifyOrReturn(CheckValue("groupId", groupId, 1U)); NextTest(); } - CHIP_ERROR TestRestoreInitialLocationValue_3() + CHIP_ERROR TestGroupWriteAttribute_3() { - const chip::GroupId groupId = 1234; + const chip::GroupId groupId = 1; chip::Controller::BasicClusterTest cluster; cluster.AssociateWithGroup(mDevices[kIdentityAlpha], groupId); chip::CharSpan locationArgument; - locationArgument = chip::Span("garbage: not in length on purpose", 0); + locationArgument = chip::Span("usgarbage: not in length on purpose", 2); ReturnErrorOnFailure(cluster.WriteAttribute( locationArgument, this, OnSuccessCallback_3, OnFailureCallback_3, OnDoneCallback_3)); @@ -73255,62 +73288,110 @@ class TestGroupMessaging : public TestCommand } void OnSuccessResponse_4(chip::CharSpan location) + { + VerifyOrReturn(CheckValueAsString("location", location, chip::CharSpan("us", 2))); + + NextTest(); + } + + CHIP_ERROR TestRestoreInitialLocationValue_5() + { + const chip::GroupId groupId = 1; + chip::Controller::BasicClusterTest cluster; + cluster.AssociateWithGroup(mDevices[kIdentityAlpha], groupId); + + chip::CharSpan locationArgument; + locationArgument = chip::Span("garbage: not in length on purpose", 0); + + ReturnErrorOnFailure(cluster.WriteAttribute( + locationArgument, this, OnSuccessCallback_5, OnFailureCallback_5, OnDoneCallback_5)); + return CHIP_NO_ERROR; + } + + void OnFailureResponse_5(CHIP_ERROR error) + { + chip::app::StatusIB status(error); + ThrowFailureResponse(); + } + + void OnSuccessResponse_5() { NextTest(); } + + void OnDoneResponse_5() { NextTest(); } + + CHIP_ERROR TestReadBackAttribute_6() + { + const chip::EndpointId endpoint = mEndpoint.HasValue() ? mEndpoint.Value() : 0; + chip::Controller::BasicClusterTest cluster; + cluster.Associate(mDevices[kIdentityAlpha], endpoint); + + ReturnErrorOnFailure(cluster.ReadAttribute( + this, OnSuccessCallback_6, OnFailureCallback_6)); + return CHIP_NO_ERROR; + } + + void OnFailureResponse_6(CHIP_ERROR error) + { + chip::app::StatusIB status(error); + ThrowFailureResponse(); + } + + void OnSuccessResponse_6(chip::CharSpan location) { VerifyOrReturn(CheckValueAsString("location", location, chip::CharSpan("", 0))); NextTest(); } - CHIP_ERROR TestTurnOnTheLightToSeeAttributeChange_5() + CHIP_ERROR TestTurnOnTheLightToSeeAttributeChange_7() { - const chip::GroupId groupId = 1234; + const chip::GroupId groupId = 4660; using RequestType = chip::app::Clusters::OnOff::Commands::On::Type; RequestType request; auto success = [](void * context, const typename RequestType::ResponseType & data) { - (static_cast(context))->OnSuccessResponse_5(); + (static_cast(context))->OnSuccessResponse_7(); }; auto failure = [](void * context, CHIP_ERROR error) { - (static_cast(context))->OnFailureResponse_5(error); + (static_cast(context))->OnFailureResponse_7(error); }; - auto done = [](void * context) { (static_cast(context))->OnDoneResponse_5(); }; + auto done = [](void * context) { (static_cast(context))->OnDoneResponse_7(); }; ReturnErrorOnFailure( chip::Controller::InvokeGroupCommand(mDevices[kIdentityAlpha], this, success, failure, done, groupId, request)); return CHIP_NO_ERROR; } - void OnFailureResponse_5(CHIP_ERROR error) + void OnFailureResponse_7(CHIP_ERROR error) { chip::app::StatusIB status(error); ThrowFailureResponse(); } - void OnSuccessResponse_5() { NextTest(); } + void OnSuccessResponse_7() { NextTest(); } - void OnDoneResponse_5() { NextTest(); } + void OnDoneResponse_7() { NextTest(); } - CHIP_ERROR TestCheckOnOffAttributeValueIsTrueAfterOnCommand_6() + CHIP_ERROR TestCheckOnOffAttributeValueIsTrueAfterOnCommandForEndpoint1_8() { const chip::EndpointId endpoint = mEndpoint.HasValue() ? mEndpoint.Value() : 1; chip::Controller::OnOffClusterTest cluster; cluster.Associate(mDevices[kIdentityAlpha], endpoint); ReturnErrorOnFailure(cluster.ReadAttribute( - this, OnSuccessCallback_6, OnFailureCallback_6)); + this, OnSuccessCallback_8, OnFailureCallback_8)); return CHIP_NO_ERROR; } - void OnFailureResponse_6(CHIP_ERROR error) + void OnFailureResponse_8(CHIP_ERROR error) { chip::app::StatusIB status(error); ThrowFailureResponse(); } - void OnSuccessResponse_6(bool onOff) + void OnSuccessResponse_8(bool onOff) { VerifyOrReturn(CheckValue("onOff", onOff, 1)); diff --git a/zzz_generated/lighting-app/zap-generated/IMClusterCommandHandler.cpp b/zzz_generated/lighting-app/zap-generated/IMClusterCommandHandler.cpp index f3c22214531d85..0378e5df3ce3d7 100644 --- a/zzz_generated/lighting-app/zap-generated/IMClusterCommandHandler.cpp +++ b/zzz_generated/lighting-app/zap-generated/IMClusterCommandHandler.cpp @@ -449,6 +449,92 @@ void DispatchServerCommand(CommandHandler * apCommandObj, const ConcreteCommandP } // namespace GeneralCommissioning +namespace Groups { + +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::AddGroup::Id: { + Commands::AddGroup::DecodableType commandData; + TLVError = DataModel::Decode(aDataTlv, commandData); + if (TLVError == CHIP_NO_ERROR) + { + wasHandled = emberAfGroupsClusterAddGroupCallback(apCommandObj, aCommandPath, commandData); + } + break; + } + case Commands::AddGroupIfIdentifying::Id: { + Commands::AddGroupIfIdentifying::DecodableType commandData; + TLVError = DataModel::Decode(aDataTlv, commandData); + if (TLVError == CHIP_NO_ERROR) + { + wasHandled = emberAfGroupsClusterAddGroupIfIdentifyingCallback(apCommandObj, aCommandPath, commandData); + } + break; + } + case Commands::GetGroupMembership::Id: { + Commands::GetGroupMembership::DecodableType commandData; + TLVError = DataModel::Decode(aDataTlv, commandData); + if (TLVError == CHIP_NO_ERROR) + { + wasHandled = emberAfGroupsClusterGetGroupMembershipCallback(apCommandObj, aCommandPath, commandData); + } + break; + } + case Commands::RemoveAllGroups::Id: { + Commands::RemoveAllGroups::DecodableType commandData; + TLVError = DataModel::Decode(aDataTlv, commandData); + if (TLVError == CHIP_NO_ERROR) + { + wasHandled = emberAfGroupsClusterRemoveAllGroupsCallback(apCommandObj, aCommandPath, commandData); + } + break; + } + case Commands::RemoveGroup::Id: { + Commands::RemoveGroup::DecodableType commandData; + TLVError = DataModel::Decode(aDataTlv, commandData); + if (TLVError == CHIP_NO_ERROR) + { + wasHandled = emberAfGroupsClusterRemoveGroupCallback(apCommandObj, aCommandPath, commandData); + } + break; + } + case Commands::ViewGroup::Id: { + Commands::ViewGroup::DecodableType commandData; + TLVError = DataModel::Decode(aDataTlv, commandData); + if (TLVError == CHIP_NO_ERROR) + { + wasHandled = emberAfGroupsClusterViewGroupCallback(apCommandObj, aCommandPath, commandData); + } + break; + } + default: { + // Unrecognized command ID, error status will apply. + apCommandObj->AddStatus(aCommandPath, Protocols::InteractionModel::Status::UnsupportedCommand); + ChipLogError(Zcl, "Unknown command " ChipLogFormatMEI " for cluster " ChipLogFormatMEI, + ChipLogValueMEI(aCommandPath.mCommandId), ChipLogValueMEI(aCommandPath.mClusterId)); + 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 Groups + namespace Identify { void DispatchServerCommand(CommandHandler * apCommandObj, const ConcreteCommandPath & aCommandPath, TLV::TLVReader & aDataTlv) @@ -1280,6 +1366,9 @@ void DispatchSingleClusterCommand(const ConcreteCommandPath & aCommandPath, TLV: case Clusters::GeneralCommissioning::Id: Clusters::GeneralCommissioning::DispatchServerCommand(apCommandObj, aCommandPath, aReader); break; + case Clusters::Groups::Id: + Clusters::Groups::DispatchServerCommand(apCommandObj, aCommandPath, aReader); + break; case Clusters::Identify::Id: Clusters::Identify::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 f558a396382d5e..7efd0d8121b3a9 100644 --- a/zzz_generated/lighting-app/zap-generated/PluginApplicationCallbacks.h +++ b/zzz_generated/lighting-app/zap-generated/PluginApplicationCallbacks.h @@ -31,6 +31,7 @@ MatterFixedLabelPluginServerInitCallback(); \ MatterGeneralCommissioningPluginServerInitCallback(); \ MatterGeneralDiagnosticsPluginServerInitCallback(); \ + MatterGroupsPluginServerInitCallback(); \ MatterIdentifyPluginServerInitCallback(); \ MatterLevelControlPluginServerInitCallback(); \ MatterLocalizationConfigurationPluginServerInitCallback(); \ diff --git a/zzz_generated/lighting-app/zap-generated/callback-stub.cpp b/zzz_generated/lighting-app/zap-generated/callback-stub.cpp index fb97ee9c5583fc..eecb99666c2612 100644 --- a/zzz_generated/lighting-app/zap-generated/callback-stub.cpp +++ b/zzz_generated/lighting-app/zap-generated/callback-stub.cpp @@ -56,6 +56,9 @@ void emberAfClusterInitCallback(EndpointId endpoint, ClusterId clusterId) case ZCL_GENERAL_DIAGNOSTICS_CLUSTER_ID: emberAfGeneralDiagnosticsClusterInitCallback(endpoint); break; + case ZCL_GROUPS_CLUSTER_ID: + emberAfGroupsClusterInitCallback(endpoint); + break; case ZCL_IDENTIFY_CLUSTER_ID: emberAfIdentifyClusterInitCallback(endpoint); break; @@ -155,6 +158,11 @@ void __attribute__((weak)) emberAfGeneralDiagnosticsClusterInitCallback(Endpoint // To prevent warning (void) endpoint; } +void __attribute__((weak)) emberAfGroupsClusterInitCallback(EndpointId endpoint) +{ + // To prevent warning + (void) endpoint; +} void __attribute__((weak)) emberAfIdentifyClusterInitCallback(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 509d64ef7b38a3..f7ad9ce8071a79 100644 --- a/zzz_generated/lighting-app/zap-generated/endpoint_config.h +++ b/zzz_generated/lighting-app/zap-generated/endpoint_config.h @@ -612,12 +612,16 @@ #define ZAP_ATTRIBUTE_MASK(mask) ATTRIBUTE_MASK_##mask // This is an array of EmberAfAttributeMetadata structures. -#define GENERATED_ATTRIBUTE_COUNT 236 +#define GENERATED_ATTRIBUTE_COUNT 240 #define GENERATED_ATTRIBUTES \ { \ \ - /* Endpoint: 0, Cluster: Descriptor (server) */ \ - { 0x00000000, ZAP_TYPE(ARRAY), 0, ZAP_ATTRIBUTE_MASK(EXTERNAL_STORAGE), ZAP_EMPTY_DEFAULT() }, /* device list */ \ + /* Endpoint: 0, Cluster: Groups (server) */ \ + { 0x00000000, ZAP_TYPE(BITMAP8), 1, 0, ZAP_EMPTY_DEFAULT() }, /* name support */ \ + { 0x0000FFFD, ZAP_TYPE(INT16U), 2, 0, ZAP_SIMPLE_DEFAULT(3) }, /* ClusterRevision */ \ + \ + /* Endpoint: 0, Cluster: Descriptor (server) */ \ + { 0x00000000, ZAP_TYPE(ARRAY), 0, ZAP_ATTRIBUTE_MASK(EXTERNAL_STORAGE), ZAP_EMPTY_DEFAULT() }, /* device list */ \ { 0x00000001, ZAP_TYPE(ARRAY), 0, ZAP_ATTRIBUTE_MASK(EXTERNAL_STORAGE), ZAP_EMPTY_DEFAULT() }, /* server list */ \ { 0x00000002, ZAP_TYPE(ARRAY), 0, ZAP_ATTRIBUTE_MASK(EXTERNAL_STORAGE), ZAP_EMPTY_DEFAULT() }, /* client list */ \ { 0x00000003, ZAP_TYPE(ARRAY), 0, ZAP_ATTRIBUTE_MASK(EXTERNAL_STORAGE), ZAP_EMPTY_DEFAULT() }, /* parts list */ \ @@ -852,6 +856,10 @@ { 0x00000001, ZAP_TYPE(ENUM8), 1, 0, ZAP_SIMPLE_DEFAULT(0x0) }, /* identify type */ \ { 0x0000FFFD, ZAP_TYPE(INT16U), 2, 0, ZAP_SIMPLE_DEFAULT(2) }, /* ClusterRevision */ \ \ + /* Endpoint: 1, Cluster: Groups (server) */ \ + { 0x00000000, ZAP_TYPE(BITMAP8), 1, 0, ZAP_EMPTY_DEFAULT() }, /* name support */ \ + { 0x0000FFFD, ZAP_TYPE(INT16U), 2, 0, ZAP_SIMPLE_DEFAULT(3) }, /* ClusterRevision */ \ + \ /* Endpoint: 1, Cluster: On/Off (server) */ \ { 0x00000000, ZAP_TYPE(BOOLEAN), 1, ZAP_ATTRIBUTE_MASK(TOKENIZE), ZAP_SIMPLE_DEFAULT(0x00) }, /* OnOff */ \ { 0x00004000, ZAP_TYPE(BOOLEAN), 1, 0, ZAP_SIMPLE_DEFAULT(0x01) }, /* GlobalSceneControl */ \ @@ -942,6 +950,9 @@ // Cluster function static arrays #define GENERATED_FUNCTION_ARRAYS \ + const EmberAfGenericClusterFunction chipFuncArrayGroupsServer[] = { \ + (EmberAfGenericClusterFunction) emberAfGroupsClusterServerInitCallback, \ + }; \ const EmberAfGenericClusterFunction chipFuncArrayBasicServer[] = { \ (EmberAfGenericClusterFunction) emberAfBasicClusterServerInitCallback, \ }; \ @@ -971,113 +982,125 @@ }; #define ZAP_CLUSTER_MASK(mask) CLUSTER_MASK_##mask -#define GENERATED_CLUSTER_COUNT 27 +#define GENERATED_CLUSTER_COUNT 29 #define GENERATED_CLUSTERS \ { \ - { \ - 0x0000001D, ZAP_ATTRIBUTE_INDEX(0), 5, 0, ZAP_CLUSTER_MASK(SERVER), NULL \ - }, /* Endpoint: 0, Cluster: Descriptor (server) */ \ + { 0x00000004, \ + ZAP_ATTRIBUTE_INDEX(0), \ + 2, \ + 3, \ + ZAP_CLUSTER_MASK(SERVER) | ZAP_CLUSTER_MASK(INIT_FUNCTION), \ + chipFuncArrayGroupsServer }, /* Endpoint: 0, Cluster: Groups (server) */ \ + { \ + 0x0000001D, ZAP_ATTRIBUTE_INDEX(2), 5, 0, ZAP_CLUSTER_MASK(SERVER), NULL \ + }, /* Endpoint: 0, Cluster: Descriptor (server) */ \ { 0x00000028, \ - ZAP_ATTRIBUTE_INDEX(5), \ + ZAP_ATTRIBUTE_INDEX(7), \ 20, \ 687, \ ZAP_CLUSTER_MASK(SERVER) | ZAP_CLUSTER_MASK(INIT_FUNCTION), \ chipFuncArrayBasicServer }, /* Endpoint: 0, Cluster: Basic (server) */ \ { \ - 0x00000029, ZAP_ATTRIBUTE_INDEX(25), 1, 2, ZAP_CLUSTER_MASK(CLIENT), NULL \ + 0x00000029, ZAP_ATTRIBUTE_INDEX(27), 1, 2, ZAP_CLUSTER_MASK(CLIENT), NULL \ }, /* Endpoint: 0, Cluster: OTA Software Update Provider (client) */ \ { \ - 0x0000002A, ZAP_ATTRIBUTE_INDEX(26), 5, 5, ZAP_CLUSTER_MASK(SERVER), NULL \ + 0x0000002A, ZAP_ATTRIBUTE_INDEX(28), 5, 5, ZAP_CLUSTER_MASK(SERVER), NULL \ }, /* Endpoint: 0, Cluster: OTA Software Update Requestor (server) */ \ { 0x0000002B, \ - ZAP_ATTRIBUTE_INDEX(31), \ + ZAP_ATTRIBUTE_INDEX(33), \ 3, \ 38, \ ZAP_CLUSTER_MASK(SERVER) | ZAP_CLUSTER_MASK(INIT_FUNCTION) | ZAP_CLUSTER_MASK(PRE_ATTRIBUTE_CHANGED_FUNCTION), \ chipFuncArrayLocalizationConfigurationServer }, /* Endpoint: 0, Cluster: Localization Configuration (server) */ \ { 0x0000002C, \ - ZAP_ATTRIBUTE_INDEX(34), \ + ZAP_ATTRIBUTE_INDEX(36), \ 4, \ 4, \ ZAP_CLUSTER_MASK(SERVER) | ZAP_CLUSTER_MASK(INIT_FUNCTION) | ZAP_CLUSTER_MASK(PRE_ATTRIBUTE_CHANGED_FUNCTION), \ chipFuncArrayTimeFormatLocalizationServer }, /* Endpoint: 0, Cluster: Time Format Localization (server) */ \ { \ - 0x00000030, ZAP_ATTRIBUTE_INDEX(38), 6, 270, ZAP_CLUSTER_MASK(SERVER), NULL \ + 0x00000030, ZAP_ATTRIBUTE_INDEX(40), 6, 270, ZAP_CLUSTER_MASK(SERVER), NULL \ }, /* Endpoint: 0, Cluster: General Commissioning (server) */ \ { \ - 0x00000031, ZAP_ATTRIBUTE_INDEX(44), 10, 60, ZAP_CLUSTER_MASK(SERVER), NULL \ + 0x00000031, ZAP_ATTRIBUTE_INDEX(46), 10, 60, ZAP_CLUSTER_MASK(SERVER), NULL \ }, /* Endpoint: 0, Cluster: Network Commissioning (server) */ \ { \ - 0x00000032, ZAP_ATTRIBUTE_INDEX(54), 0, 0, ZAP_CLUSTER_MASK(SERVER), NULL \ + 0x00000032, ZAP_ATTRIBUTE_INDEX(56), 0, 0, ZAP_CLUSTER_MASK(SERVER), NULL \ }, /* Endpoint: 0, Cluster: Diagnostic Logs (server) */ \ { \ - 0x00000033, ZAP_ATTRIBUTE_INDEX(54), 9, 17, ZAP_CLUSTER_MASK(SERVER), NULL \ + 0x00000033, ZAP_ATTRIBUTE_INDEX(56), 9, 17, ZAP_CLUSTER_MASK(SERVER), NULL \ }, /* Endpoint: 0, Cluster: General Diagnostics (server) */ \ { \ - 0x00000034, ZAP_ATTRIBUTE_INDEX(63), 6, 30, ZAP_CLUSTER_MASK(SERVER), NULL \ + 0x00000034, ZAP_ATTRIBUTE_INDEX(65), 6, 30, ZAP_CLUSTER_MASK(SERVER), NULL \ }, /* Endpoint: 0, Cluster: Software Diagnostics (server) */ \ { \ - 0x00000035, ZAP_ATTRIBUTE_INDEX(69), 65, 247, ZAP_CLUSTER_MASK(SERVER), NULL \ + 0x00000035, ZAP_ATTRIBUTE_INDEX(71), 65, 247, ZAP_CLUSTER_MASK(SERVER), NULL \ }, /* Endpoint: 0, Cluster: Thread Network Diagnostics (server) */ \ { \ - 0x00000036, ZAP_ATTRIBUTE_INDEX(134), 15, 58, ZAP_CLUSTER_MASK(SERVER), NULL \ + 0x00000036, ZAP_ATTRIBUTE_INDEX(136), 15, 58, ZAP_CLUSTER_MASK(SERVER), NULL \ }, /* Endpoint: 0, Cluster: WiFi Network Diagnostics (server) */ \ { \ - 0x00000037, ZAP_ATTRIBUTE_INDEX(149), 11, 57, ZAP_CLUSTER_MASK(SERVER), NULL \ + 0x00000037, ZAP_ATTRIBUTE_INDEX(151), 11, 57, ZAP_CLUSTER_MASK(SERVER), NULL \ }, /* Endpoint: 0, Cluster: Ethernet Network Diagnostics (server) */ \ { \ - 0x0000003B, ZAP_ATTRIBUTE_INDEX(160), 0, 0, ZAP_CLUSTER_MASK(SERVER), NULL \ + 0x0000003B, ZAP_ATTRIBUTE_INDEX(162), 0, 0, ZAP_CLUSTER_MASK(SERVER), NULL \ }, /* Endpoint: 0, Cluster: Switch (server) */ \ { \ - 0x0000003C, ZAP_ATTRIBUTE_INDEX(160), 4, 2, ZAP_CLUSTER_MASK(SERVER), NULL \ + 0x0000003C, ZAP_ATTRIBUTE_INDEX(162), 4, 2, ZAP_CLUSTER_MASK(SERVER), NULL \ }, /* Endpoint: 0, Cluster: AdministratorCommissioning (server) */ \ { \ - 0x0000003E, ZAP_ATTRIBUTE_INDEX(164), 7, 4, ZAP_CLUSTER_MASK(SERVER), NULL \ + 0x0000003E, ZAP_ATTRIBUTE_INDEX(166), 7, 4, ZAP_CLUSTER_MASK(SERVER), NULL \ }, /* Endpoint: 0, Cluster: Operational Credentials (server) */ \ { \ - 0x00000040, ZAP_ATTRIBUTE_INDEX(171), 2, 2, ZAP_CLUSTER_MASK(SERVER), NULL \ + 0x00000040, ZAP_ATTRIBUTE_INDEX(173), 2, 2, ZAP_CLUSTER_MASK(SERVER), NULL \ }, /* Endpoint: 0, Cluster: Fixed Label (server) */ \ { \ - 0x00000041, ZAP_ATTRIBUTE_INDEX(173), 2, 2, ZAP_CLUSTER_MASK(SERVER), NULL \ + 0x00000041, ZAP_ATTRIBUTE_INDEX(175), 2, 2, ZAP_CLUSTER_MASK(SERVER), NULL \ }, /* Endpoint: 0, Cluster: User Label (server) */ \ { 0x00000003, \ - ZAP_ATTRIBUTE_INDEX(175), \ + ZAP_ATTRIBUTE_INDEX(177), \ 3, \ 5, \ ZAP_CLUSTER_MASK(SERVER) | ZAP_CLUSTER_MASK(INIT_FUNCTION) | ZAP_CLUSTER_MASK(ATTRIBUTE_CHANGED_FUNCTION), \ chipFuncArrayIdentifyServer }, /* Endpoint: 1, Cluster: Identify (server) */ \ + { 0x00000004, \ + ZAP_ATTRIBUTE_INDEX(180), \ + 2, \ + 3, \ + ZAP_CLUSTER_MASK(SERVER) | ZAP_CLUSTER_MASK(INIT_FUNCTION), \ + chipFuncArrayGroupsServer }, /* Endpoint: 1, Cluster: Groups (server) */ \ { 0x00000006, \ - ZAP_ATTRIBUTE_INDEX(178), \ + ZAP_ATTRIBUTE_INDEX(182), \ 7, \ 13, \ ZAP_CLUSTER_MASK(SERVER) | ZAP_CLUSTER_MASK(INIT_FUNCTION), \ chipFuncArrayOnOffServer }, /* Endpoint: 1, Cluster: On/Off (server) */ \ { 0x00000008, \ - ZAP_ATTRIBUTE_INDEX(185), \ + ZAP_ATTRIBUTE_INDEX(189), \ 16, \ 27, \ ZAP_CLUSTER_MASK(SERVER) | ZAP_CLUSTER_MASK(INIT_FUNCTION), \ chipFuncArrayLevelControlServer }, /* Endpoint: 1, Cluster: Level Control (server) */ \ { \ - 0x0000001D, ZAP_ATTRIBUTE_INDEX(201), 5, 0, ZAP_CLUSTER_MASK(SERVER), NULL \ + 0x0000001D, ZAP_ATTRIBUTE_INDEX(205), 5, 0, ZAP_CLUSTER_MASK(SERVER), NULL \ }, /* Endpoint: 1, Cluster: Descriptor (server) */ \ { 0x00000300, \ - ZAP_ATTRIBUTE_INDEX(206), \ + ZAP_ATTRIBUTE_INDEX(210), \ 22, \ 36, \ ZAP_CLUSTER_MASK(SERVER) | ZAP_CLUSTER_MASK(INIT_FUNCTION), \ chipFuncArrayColorControlServer }, /* Endpoint: 1, Cluster: Color Control (server) */ \ { 0x00000406, \ - ZAP_ATTRIBUTE_INDEX(228), \ + ZAP_ATTRIBUTE_INDEX(232), \ 4, \ 5, \ ZAP_CLUSTER_MASK(SERVER) | ZAP_CLUSTER_MASK(INIT_FUNCTION), \ chipFuncArrayOccupancySensingServer }, /* Endpoint: 1, Cluster: Occupancy Sensing (server) */ \ { \ - 0x00000006, ZAP_ATTRIBUTE_INDEX(232), 1, 2, ZAP_CLUSTER_MASK(CLIENT), NULL \ + 0x00000006, ZAP_ATTRIBUTE_INDEX(236), 1, 2, ZAP_CLUSTER_MASK(CLIENT), NULL \ }, /* Endpoint: 2, Cluster: On/Off (client) */ \ { \ - 0x00000007, ZAP_ATTRIBUTE_INDEX(233), 3, 4, ZAP_CLUSTER_MASK(SERVER), NULL \ + 0x00000007, ZAP_ATTRIBUTE_INDEX(237), 3, 4, ZAP_CLUSTER_MASK(SERVER), NULL \ }, /* Endpoint: 2, Cluster: On/off Switch Configuration (server) */ \ } @@ -1086,7 +1109,7 @@ // This is an array of EmberAfEndpointType structures. #define GENERATED_ENDPOINT_TYPES \ { \ - { ZAP_CLUSTER_INDEX(0), 19, 1485 }, { ZAP_CLUSTER_INDEX(19), 6, 86 }, { ZAP_CLUSTER_INDEX(25), 2, 6 }, \ + { ZAP_CLUSTER_INDEX(0), 20, 1488 }, { ZAP_CLUSTER_INDEX(20), 7, 89 }, { ZAP_CLUSTER_INDEX(27), 2, 6 }, \ } // Largest attribute size is needed for various buffers @@ -1096,7 +1119,7 @@ #define ATTRIBUTE_SINGLETONS_SIZE (687) // Total size of attribute storage -#define ATTRIBUTE_MAX_SIZE (1577) +#define ATTRIBUTE_MAX_SIZE (1583) // 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 526f5293117048..6c83cb6720bd75 100644 --- a/zzz_generated/lighting-app/zap-generated/gen_config.h +++ b/zzz_generated/lighting-app/zap-generated/gen_config.h @@ -38,6 +38,7 @@ #define EMBER_AF_FIXED_LABEL_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_GROUPS_CLUSTER_SERVER_ENDPOINT_COUNT (2) #define EMBER_AF_IDENTIFY_CLUSTER_SERVER_ENDPOINT_COUNT (1) #define EMBER_AF_LEVEL_CONTROL_CLUSTER_SERVER_ENDPOINT_COUNT (1) #define EMBER_AF_LOCALIZATION_CONFIGURATION_CLUSTER_SERVER_ENDPOINT_COUNT (1) @@ -107,6 +108,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 Groups cluster is included +#define ZCL_USING_GROUPS_CLUSTER_SERVER +#define EMBER_AF_PLUGIN_GROUPS_SERVER +#define EMBER_AF_PLUGIN_GROUPS + // 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