diff --git a/examples/chip-tool/templates/partials/test_cluster.zapt b/examples/chip-tool/templates/partials/test_cluster.zapt index ec9cd981380685..d5bc6bdab38f36 100644 --- a/examples/chip-tool/templates/partials/test_cluster.zapt +++ b/examples/chip-tool/templates/partials/test_cluster.zapt @@ -138,7 +138,11 @@ class {{filename}}: public TestCommand CHIP_ERROR {{>testCommand}}() { + {{#if isGroupCommand}} + const chip::GroupId groupId = {{groupId}}; + {{else}} const chip::EndpointId endpoint = mEndpointId.HasValue() ? mEndpointId.Value() : {{endpoint}}; + {{/if}} {{#if isCommand}} using RequestType = chip::app::Clusters::{{asUpperCamelCase cluster}}::Commands::{{asUpperCamelCase command}}::Type; @@ -159,7 +163,11 @@ class {{filename}}: public TestCommand {{#unless async}}return CHIP_NO_ERROR;{{/unless}} {{else}} chip::Controller::{{asUpperCamelCase cluster}}ClusterTest cluster; + {{#if isGroupCommand}} + cluster.AssociateWithGroup(mDevice, groupId); + {{else}} cluster.Associate(mDevice, endpoint); + {{/if}} {{#chip_tests_item_parameters}} {{zapTypeToEncodableClusterObjectType type ns=parent.cluster}} {{asLowerCamelCase name}}Argument; diff --git a/examples/chip-tool/templates/tests.js b/examples/chip-tool/templates/tests.js index 355423cf86aecb..3446706531ce6d 100644 --- a/examples/chip-tool/templates/tests.js +++ b/examples/chip-tool/templates/tests.js @@ -186,6 +186,7 @@ function getTests() 'TestIdentifyCluster', 'TestOperationalCredentialsCluster', 'TestModeSelectCluster', + 'TestGroupMessaging', ]; const SoftwareDiagnostics = [ diff --git a/src/app/tests/suites/TestGroupMessaging.yaml b/src/app/tests/suites/TestGroupMessaging.yaml new file mode 100644 index 00000000000000..cb913c391ece7f --- /dev/null +++ b/src/app/tests/suites/TestGroupMessaging.yaml @@ -0,0 +1,54 @@ +# Copyright (c) 2021 Project CHIP Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +name: Basic Group Messaging Tests + +config: + cluster: "Basic" + endpoint: 0 + +tests: + #TODO : Add Group membership command when implemented Issue #11077 + # - label: "Add device to Group" + # command: "TODO" + + - label: "Group Write Attribute" + command: "writeAttribute" + attribute: "location" + disabled: true + groupId: "1234" + arguments: + value: "us" + + - label: "Read back Attribute" + command: "readAttribute" + attribute: "location" + disabled: true + response: + value: "us" + + - label: "Restore initial location value" + command: "writeAttribute" + attribute: "location" + groupId: "1234" + disabled: true + arguments: + value: "" + + - label: "Read back Attribute" + command: "readAttribute" + attribute: "location" + disabled: true + response: + value: "" diff --git a/src/app/zap-templates/common/ClusterTestGeneration.js b/src/app/zap-templates/common/ClusterTestGeneration.js index 14bde361a46e8f..44bdc0429bed2e 100644 --- a/src/app/zap-templates/common/ClusterTestGeneration.js +++ b/src/app/zap-templates/common/ClusterTestGeneration.js @@ -33,6 +33,7 @@ const { asUpperCamelCase } = require(basePath + 'src/app/zap-templa const kClusterName = 'cluster'; const kEndpointName = 'endpoint'; +const kGroupId = 'groupId'; const kCommandName = 'command'; const kWaitCommandName = 'wait'; const kIndexName = 'index'; @@ -117,6 +118,10 @@ function setDefaultTypeForCommand(test) test.commandName = 'Write'; test.isAttribute = true; test.isWriteAttribute = true; + if ((kGroupId in test)) { + test.isGroupCommand = true; + test.groupId = parseInt(test[kGroupId], 10); + } break; case 'subscribeAttribute': diff --git a/src/controller/CHIPCluster.cpp b/src/controller/CHIPCluster.cpp index b6d2e03112c52f..f985b1596e8a4b 100644 --- a/src/controller/CHIPCluster.cpp +++ b/src/controller/CHIPCluster.cpp @@ -42,6 +42,38 @@ CHIP_ERROR ClusterBase::Associate(DeviceProxy * device, EndpointId endpoint) return err; } +CHIP_ERROR ClusterBase::AssociateWithGroup(DeviceProxy * device, GroupId groupId) +{ + // TODO Update this function to work in all possible conditions Issue #11850 + + CHIP_ERROR err = CHIP_NO_ERROR; + + mDevice = device; + if (mDevice->GetSecureSession().HasValue()) + { + // Local copy to preserve original SessionHandle for future Unicast communication. + SessionHandle session = mDevice->GetSecureSession().Value(); + session.SetGroupId(groupId); + mSessionHandle.SetValue(session); + + // Sanity check + if (!mSessionHandle.Value().IsGroupSession()) + { + err = CHIP_ERROR_INCORRECT_STATE; + } + } + else + { + // something fishy is going on + err = CHIP_ERROR_INCORRECT_STATE; + } + + // Set to 0 for now. + mEndpoint = 0; + + return err; +} + void ClusterBase::Dissociate() { mDevice = nullptr; diff --git a/src/controller/CHIPCluster.h b/src/controller/CHIPCluster.h index 16a0423669724b..43dd636466f655 100644 --- a/src/controller/CHIPCluster.h +++ b/src/controller/CHIPCluster.h @@ -51,6 +51,7 @@ class DLL_EXPORT ClusterBase virtual ~ClusterBase() {} CHIP_ERROR Associate(DeviceProxy * device, EndpointId endpoint); + CHIP_ERROR AssociateWithGroup(DeviceProxy * device, GroupId groupId); void Dissociate(); @@ -110,8 +111,9 @@ class DLL_EXPORT ClusterBase } }; - return chip::Controller::WriteAttribute(mDevice->GetSecureSession().Value(), mEndpoint, clusterId, attributeId, - requestData, onSuccessCb, onFailureCb); + return chip::Controller::WriteAttribute((mSessionHandle.HasValue()) ? mSessionHandle.Value() + : mDevice->GetSecureSession().Value(), + mEndpoint, clusterId, attributeId, requestData, onSuccessCb, onFailureCb); } template @@ -179,6 +181,7 @@ class DLL_EXPORT ClusterBase const ClusterId mClusterId; DeviceProxy * mDevice; EndpointId mEndpoint; + chip::Optional mSessionHandle; }; } // namespace Controller diff --git a/src/controller/WriteInteraction.h b/src/controller/WriteInteraction.h index 5db943846a7228..cd36b3e2f90444 100644 --- a/src/controller/WriteInteraction.h +++ b/src/controller/WriteInteraction.h @@ -97,8 +97,18 @@ CHIP_ERROR WriteAttribute(SessionHandle sessionHandle, chip::EndpointId endpoint VerifyOrReturnError(callback != nullptr, CHIP_ERROR_NO_MEMORY); ReturnErrorOnFailure(app::InteractionModelEngine::GetInstance()->NewWriteClient(handle, callback.get())); - ReturnErrorOnFailure( - handle.EncodeAttributeWritePayload(chip::app::AttributePathParams(endpointId, clusterId, attributeId), requestData)); + if (sessionHandle.IsGroupSession()) + { + // TODO : Issue #11604 + return CHIP_ERROR_NOT_IMPLEMENTED; + // ReturnErrorOnFailure( + // handle.EncodeAttributeWritePayload(chip::app::AttributePathParams(clusterId, attributeId), requestData)); + } + else + { + ReturnErrorOnFailure( + handle.EncodeAttributeWritePayload(chip::app::AttributePathParams(endpointId, clusterId, attributeId), requestData)); + } ReturnErrorOnFailure(handle.SendWriteRequest(sessionHandle)); callback.release(); diff --git a/src/darwin/Framework/CHIP/templates/tests.js b/src/darwin/Framework/CHIP/templates/tests.js index 05be1150adfbdc..68fca8dfce6dd1 100644 --- a/src/darwin/Framework/CHIP/templates/tests.js +++ b/src/darwin/Framework/CHIP/templates/tests.js @@ -168,6 +168,7 @@ function getTests() 'TestIdentifyCluster', 'TestOperationalCredentialsCluster', 'TestModeSelectCluster', + 'TestGroupMessaging', ]; const SoftwareDiagnostics = [ diff --git a/src/transport/SessionHandle.h b/src/transport/SessionHandle.h index 8de4c48595637c..2c85ca3ddd0e95 100644 --- a/src/transport/SessionHandle.h +++ b/src/transport/SessionHandle.h @@ -53,6 +53,7 @@ class SessionHandle bool HasFabricIndex() const { return (mFabric != kUndefinedFabricIndex); } FabricIndex GetFabricIndex() const { return mFabric; } void SetFabricIndex(FabricIndex fabricId) { mFabric = fabricId; } + void SetGroupId(GroupId groupId) { mGroupId.SetValue(groupId); } bool operator==(const SessionHandle & that) const { diff --git a/zzz_generated/chip-tool/zap-generated/test/Commands.h b/zzz_generated/chip-tool/zap-generated/test/Commands.h index 9797f58b4af36a..06e8e8be883d97 100644 --- a/zzz_generated/chip-tool/zap-generated/test/Commands.h +++ b/zzz_generated/chip-tool/zap-generated/test/Commands.h @@ -36096,6 +36096,54 @@ class TestModeSelectCluster : public TestCommand void OnSuccessResponse_7() { ThrowSuccessResponse(); } }; +class TestGroupMessaging : public TestCommand +{ +public: + TestGroupMessaging() : TestCommand("TestGroupMessaging"), mTestIndex(0) {} + + /////////// TestCommand Interface ///////// + void NextTest() override + { + CHIP_ERROR err = CHIP_NO_ERROR; + + if (0 == mTestIndex) + { + ChipLogProgress(chipTool, " **** Test Start: TestGroupMessaging\n"); + } + + if (mTestCount == mTestIndex) + { + ChipLogProgress(chipTool, " **** Test Complete: TestGroupMessaging\n"); + SetCommandExitStatus(CHIP_NO_ERROR); + return; + } + + Wait(); + + // Ensure we increment mTestIndex before we start running the relevant + // command. That way if we lose the timeslice after we send the message + // but before our function call returns, we won't end up with an + // incorrect mTestIndex value observed when we get the response. + switch (mTestIndex++) + { + } + + if (CHIP_NO_ERROR != err) + { + ChipLogError(chipTool, " ***** Test Failure: %s\n", chip::ErrorStr(err)); + SetCommandExitStatus(err); + } + } + +private: + std::atomic_uint16_t mTestIndex; + const uint16_t mTestCount = 0; + + // + // Tests methods + // +}; + class Test_TC_DIAGSW_1_1 : public TestCommand { public: @@ -36647,6 +36695,7 @@ void registerCommandsTests(Commands & commands) make_unique(), make_unique(), make_unique(), + make_unique(), make_unique(), make_unique(), };