diff --git a/src/app/clusters/basic/basic.cpp b/src/app/clusters/basic/basic.cpp index 88dcadf7de97a1..bbb9ff9cd1640e 100644 --- a/src/app/clusters/basic/basic.cpp +++ b/src/app/clusters/basic/basic.cpp @@ -33,10 +33,98 @@ using namespace chip; using namespace chip::app; using namespace chip::app::Clusters; using namespace chip::app::Clusters::Basic; +using namespace chip::app::Clusters::Basic::Attributes; using namespace chip::DeviceLayer; namespace { +class BasicAttrAccess : public AttributeAccessInterface +{ +public: + // Register for the Basic cluster on all endpoints. + BasicAttrAccess() : AttributeAccessInterface(Optional::Missing(), Basic::Id) {} + + CHIP_ERROR Read(const ConcreteReadAttributePath & aPath, AttributeValueEncoder & aEncoder) override; + CHIP_ERROR Write(const ConcreteDataAttributePath & aPath, AttributeValueDecoder & aDecoder) override; + +private: + CHIP_ERROR ReadLocation(AttributeValueEncoder & aEncoder); + CHIP_ERROR WriteLocation(AttributeValueDecoder & aDecoder); +}; + +BasicAttrAccess gAttrAccess; + +CHIP_ERROR BasicAttrAccess::Read(const ConcreteReadAttributePath & aPath, AttributeValueEncoder & aEncoder) +{ + if (aPath.mClusterId != Basic::Id) + { + // We shouldn't have been called at all. + return CHIP_ERROR_INVALID_ARGUMENT; + } + + switch (aPath.mAttributeId) + { + case Location::Id: + return ReadLocation(aEncoder); + default: + break; + } + + return CHIP_NO_ERROR; +} + +CHIP_ERROR BasicAttrAccess::ReadLocation(AttributeValueEncoder & aEncoder) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + + char location[DeviceLayer::ConfigurationManager::kMaxLocationLength + 1]; + size_t codeLen = 0; + + if (ConfigurationMgr().GetCountryCode(location, sizeof(location), codeLen) == CHIP_NO_ERROR) + { + if (codeLen == 0) + { + err = aEncoder.Encode(chip::CharSpan("XX", strlen("XX"))); + } + else + { + err = aEncoder.Encode(chip::CharSpan(location, strlen(location))); + } + } + else + { + err = aEncoder.Encode(chip::CharSpan("XX", strlen("XX"))); + } + + return err; +} + +CHIP_ERROR BasicAttrAccess::Write(const ConcreteDataAttributePath & aPath, AttributeValueDecoder & aDecoder) +{ + VerifyOrDie(aPath.mClusterId == Basic::Id); + + switch (aPath.mAttributeId) + { + case Location::Id: + return WriteLocation(aDecoder); + default: + break; + } + + return CHIP_NO_ERROR; +} + +CHIP_ERROR BasicAttrAccess::WriteLocation(AttributeValueDecoder & aDecoder) +{ + chip::CharSpan location; + + ReturnErrorOnFailure(aDecoder.Decode(location)); + VerifyOrReturnError(location.size() <= DeviceLayer::ConfigurationManager::kMaxLocationLength, + CHIP_ERROR_INVALID_MESSAGE_LENGTH); + + return DeviceLayer::ConfigurationMgr().StoreCountryCode(location.data(), location.size()); +} + class PlatformMgrDelegate : public DeviceLayer::PlatformManagerDelegate { // Gets called by the current Node after completing a boot or reboot process. @@ -232,5 +320,6 @@ void emberAfBasicClusterServerInitCallback(chip::EndpointId endpoint) void MatterBasicPluginServerInitCallback() { + registerAttributeAccessOverride(&gAttrAccess); PlatformMgr().SetDelegate(&gPlatformMgrDelegate); } diff --git a/src/app/tests/suites/TestBasicInformation.yaml b/src/app/tests/suites/TestBasicInformation.yaml index fb7503668fe81f..8c5c078c9552aa 100644 --- a/src/app/tests/suites/TestBasicInformation.yaml +++ b/src/app/tests/suites/TestBasicInformation.yaml @@ -22,9 +22,8 @@ tests: - label: "Wait for the commissioned device to be retrieved" cluster: "DelayCommands" command: "WaitForCommissionee" - #Disabled due to issue-12983 + - label: "Read location" - disabled: true command: "readAttribute" attribute: "location" response: @@ -35,9 +34,8 @@ tests: attribute: "location" arguments: value: "us" - #Disabled due to issue-12983 + - label: "Read back location" - disabled: true command: "readAttribute" attribute: "location" response: diff --git a/src/app/tests/suites/TestGroupMessaging.yaml b/src/app/tests/suites/TestGroupMessaging.yaml index fc9fcc2367af4c..ddf735ca5261d6 100644 --- a/src/app/tests/suites/TestGroupMessaging.yaml +++ b/src/app/tests/suites/TestGroupMessaging.yaml @@ -33,9 +33,8 @@ tests: groupId: "1234" arguments: value: "us" - #Disabled due to issue-12983 + - label: "Read back Attribute" - disabled: true command: "readAttribute" attribute: "location" response: @@ -47,13 +46,12 @@ tests: groupId: "1234" arguments: value: "" - #Disabled due to issue-12983 + - label: "Read back Attribute" - disabled: true command: "readAttribute" attribute: "location" response: - value: "" + value: "XX" - label: "Turn On the light to see attribute change" cluster: "On/Off" diff --git a/src/darwin/Framework/CHIPTests/CHIPClustersTests.m b/src/darwin/Framework/CHIPTests/CHIPClustersTests.m index 6f4dc7267ea2ff..90b3cc3b075233 100644 --- a/src/darwin/Framework/CHIPTests/CHIPClustersTests.m +++ b/src/darwin/Framework/CHIPTests/CHIPClustersTests.m @@ -34614,7 +34614,31 @@ - (void)testSendClusterTestBasicInformation_000000_WaitForCommissionee WaitForCommissionee(expectation, queue); [self waitForExpectationsWithTimeout:kTimeoutInSeconds handler:nil]; } -- (void)testSendClusterTestBasicInformation_000001_WriteAttribute +- (void)testSendClusterTestBasicInformation_000001_ReadAttribute +{ + XCTestExpectation * expectation = [self expectationWithDescription:@"Read location"]; + + CHIPDevice * device = GetConnectedDevice(); + dispatch_queue_t queue = dispatch_get_main_queue(); + CHIPTestBasic * cluster = [[CHIPTestBasic alloc] initWithDevice:device endpoint:0 queue:queue]; + XCTAssertNotNil(cluster); + + [cluster readAttributeLocationWithCompletionHandler:^(NSString * _Nullable value, NSError * _Nullable err) { + NSLog(@"Read location Error: %@", err); + + XCTAssertEqual([CHIPErrorTestUtils errorToZCLErrorCode:err], 0); + + { + id actualValue = value; + XCTAssertTrue([actualValue isEqualToString:@"XX"]); + } + + [expectation fulfill]; + }]; + + [self waitForExpectationsWithTimeout:kTimeoutInSeconds handler:nil]; +} +- (void)testSendClusterTestBasicInformation_000002_WriteAttribute { XCTestExpectation * expectation = [self expectationWithDescription:@"Write location"]; @@ -34636,7 +34660,31 @@ - (void)testSendClusterTestBasicInformation_000001_WriteAttribute [self waitForExpectationsWithTimeout:kTimeoutInSeconds handler:nil]; } -- (void)testSendClusterTestBasicInformation_000002_WriteAttribute +- (void)testSendClusterTestBasicInformation_000003_ReadAttribute +{ + XCTestExpectation * expectation = [self expectationWithDescription:@"Read back location"]; + + CHIPDevice * device = GetConnectedDevice(); + dispatch_queue_t queue = dispatch_get_main_queue(); + CHIPTestBasic * cluster = [[CHIPTestBasic alloc] initWithDevice:device endpoint:0 queue:queue]; + XCTAssertNotNil(cluster); + + [cluster readAttributeLocationWithCompletionHandler:^(NSString * _Nullable value, NSError * _Nullable err) { + NSLog(@"Read back location Error: %@", err); + + XCTAssertEqual([CHIPErrorTestUtils errorToZCLErrorCode:err], 0); + + { + id actualValue = value; + XCTAssertTrue([actualValue isEqualToString:@"us"]); + } + + [expectation fulfill]; + }]; + + [self waitForExpectationsWithTimeout:kTimeoutInSeconds handler:nil]; +} +- (void)testSendClusterTestBasicInformation_000004_WriteAttribute { XCTestExpectation * expectation = [self expectationWithDescription:@"Restore initial location value"]; @@ -34658,7 +34706,7 @@ - (void)testSendClusterTestBasicInformation_000002_WriteAttribute [self waitForExpectationsWithTimeout:kTimeoutInSeconds handler:nil]; } -- (void)testSendClusterTestBasicInformation_000003_ReadAttribute +- (void)testSendClusterTestBasicInformation_000005_ReadAttribute { XCTestExpectation * expectation = [self expectationWithDescription:@"Read AttributeList value"]; diff --git a/zzz_generated/chip-tool/zap-generated/test/Commands.h b/zzz_generated/chip-tool/zap-generated/test/Commands.h index 055a7811a29727..48b01fbe447aee 100644 --- a/zzz_generated/chip-tool/zap-generated/test/Commands.h +++ b/zzz_generated/chip-tool/zap-generated/test/Commands.h @@ -55216,16 +55216,24 @@ class TestBasicInformation : public TestCommand err = TestWaitForTheCommissionedDeviceToBeRetrieved_0(); break; case 1: - ChipLogProgress(chipTool, " ***** Test Step 1 : Write location\n"); - err = TestWriteLocation_1(); + ChipLogProgress(chipTool, " ***** Test Step 1 : Read location\n"); + err = TestReadLocation_1(); break; case 2: - ChipLogProgress(chipTool, " ***** Test Step 2 : Restore initial location value\n"); - err = TestRestoreInitialLocationValue_2(); + ChipLogProgress(chipTool, " ***** Test Step 2 : Write location\n"); + err = TestWriteLocation_2(); break; case 3: - ChipLogProgress(chipTool, " ***** Test Step 3 : Read AttributeList value\n"); - err = TestReadAttributeListValue_3(); + ChipLogProgress(chipTool, " ***** Test Step 3 : Read back location\n"); + err = TestReadBackLocation_3(); + break; + case 4: + ChipLogProgress(chipTool, " ***** Test Step 4 : Restore initial location value\n"); + err = TestRestoreInitialLocationValue_4(); + break; + case 5: + ChipLogProgress(chipTool, " ***** Test Step 5 : Read AttributeList value\n"); + err = TestReadAttributeListValue_5(); break; } @@ -55238,14 +55246,17 @@ class TestBasicInformation : public TestCommand private: std::atomic_uint16_t mTestIndex; - const uint16_t mTestCount = 4; + const uint16_t mTestCount = 6; static void OnFailureCallback_1(void * context, EmberAfStatus status) { (static_cast(context))->OnFailureResponse_1(status); } - static void OnSuccessCallback_1(void * context) { (static_cast(context))->OnSuccessResponse_1(); } + static void OnSuccessCallback_1(void * context, chip::CharSpan location) + { + (static_cast(context))->OnSuccessResponse_1(location); + } static void OnFailureCallback_2(void * context, EmberAfStatus status) { @@ -55259,9 +55270,26 @@ class TestBasicInformation : public TestCommand (static_cast(context))->OnFailureResponse_3(status); } - static void OnSuccessCallback_3(void * context, const chip::app::DataModel::DecodableList & attributeList) + static void OnSuccessCallback_3(void * context, chip::CharSpan location) + { + (static_cast(context))->OnSuccessResponse_3(location); + } + + static void OnFailureCallback_4(void * context, EmberAfStatus status) + { + (static_cast(context))->OnFailureResponse_4(status); + } + + static void OnSuccessCallback_4(void * context) { (static_cast(context))->OnSuccessResponse_4(); } + + static void OnFailureCallback_5(void * context, EmberAfStatus status) + { + (static_cast(context))->OnFailureResponse_5(status); + } + + static void OnSuccessCallback_5(void * context, const chip::app::DataModel::DecodableList & attributeList) { - (static_cast(context))->OnSuccessResponse_3(attributeList); + (static_cast(context))->OnSuccessResponse_5(attributeList); } // @@ -55274,32 +55302,34 @@ class TestBasicInformation : public TestCommand return WaitForCommissionee(); } - CHIP_ERROR TestWriteLocation_1() + CHIP_ERROR TestReadLocation_1() { const chip::EndpointId endpoint = mEndpointId.HasValue() ? mEndpointId.Value() : 0; chip::Controller::BasicClusterTest cluster; cluster.Associate(mDevices[kIdentityAlpha], endpoint); - chip::CharSpan locationArgument; - locationArgument = chip::Span("usgarbage: not in length on purpose", 2); - - ReturnErrorOnFailure(cluster.WriteAttribute( - locationArgument, this, OnSuccessCallback_1, OnFailureCallback_1)); + ReturnErrorOnFailure(cluster.ReadAttribute( + this, OnSuccessCallback_1, OnFailureCallback_1)); return CHIP_NO_ERROR; } void OnFailureResponse_1(EmberAfStatus status) { ThrowFailureResponse(); } - void OnSuccessResponse_1() { NextTest(); } + void OnSuccessResponse_1(chip::CharSpan location) + { + VerifyOrReturn(CheckValueAsString("location", location, chip::CharSpan("XX", 2))); - CHIP_ERROR TestRestoreInitialLocationValue_2() + NextTest(); + } + + CHIP_ERROR TestWriteLocation_2() { const chip::EndpointId endpoint = mEndpointId.HasValue() ? mEndpointId.Value() : 0; chip::Controller::BasicClusterTest cluster; cluster.Associate(mDevices[kIdentityAlpha], endpoint); 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_2, OnFailureCallback_2)); @@ -55310,20 +55340,58 @@ class TestBasicInformation : public TestCommand void OnSuccessResponse_2() { NextTest(); } - CHIP_ERROR TestReadAttributeListValue_3() + CHIP_ERROR TestReadBackLocation_3() { const chip::EndpointId endpoint = mEndpointId.HasValue() ? mEndpointId.Value() : 0; chip::Controller::BasicClusterTest cluster; cluster.Associate(mDevices[kIdentityAlpha], endpoint); - ReturnErrorOnFailure(cluster.ReadAttribute( + ReturnErrorOnFailure(cluster.ReadAttribute( this, OnSuccessCallback_3, OnFailureCallback_3)); return CHIP_NO_ERROR; } void OnFailureResponse_3(EmberAfStatus status) { ThrowFailureResponse(); } - void OnSuccessResponse_3(const chip::app::DataModel::DecodableList & attributeList) + void OnSuccessResponse_3(chip::CharSpan location) + { + VerifyOrReturn(CheckValueAsString("location", location, chip::CharSpan("us", 2))); + + NextTest(); + } + + CHIP_ERROR TestRestoreInitialLocationValue_4() + { + const chip::EndpointId endpoint = mEndpointId.HasValue() ? mEndpointId.Value() : 0; + chip::Controller::BasicClusterTest cluster; + cluster.Associate(mDevices[kIdentityAlpha], endpoint); + + chip::CharSpan locationArgument; + locationArgument = chip::Span("garbage: not in length on purpose", 0); + + ReturnErrorOnFailure(cluster.WriteAttribute( + locationArgument, this, OnSuccessCallback_4, OnFailureCallback_4)); + return CHIP_NO_ERROR; + } + + void OnFailureResponse_4(EmberAfStatus status) { ThrowFailureResponse(); } + + void OnSuccessResponse_4() { NextTest(); } + + CHIP_ERROR TestReadAttributeListValue_5() + { + const chip::EndpointId endpoint = mEndpointId.HasValue() ? mEndpointId.Value() : 0; + chip::Controller::BasicClusterTest cluster; + cluster.Associate(mDevices[kIdentityAlpha], endpoint); + + ReturnErrorOnFailure(cluster.ReadAttribute( + this, OnSuccessCallback_5, OnFailureCallback_5)); + return CHIP_NO_ERROR; + } + + void OnFailureResponse_5(EmberAfStatus status) { ThrowFailureResponse(); } + + void OnSuccessResponse_5(const chip::app::DataModel::DecodableList & attributeList) { auto iter = attributeList.begin(); VerifyOrReturn(CheckNextListItemDecodes("attributeList", iter, 0)); @@ -56838,8 +56906,16 @@ class TestGroupMessaging : public TestCommand err = TestGroupWriteAttribute_1(); break; case 2: - ChipLogProgress(chipTool, " ***** Test Step 2 : Restore initial location value\n"); - err = TestRestoreInitialLocationValue_2(); + ChipLogProgress(chipTool, " ***** Test Step 2 : Read back Attribute\n"); + err = TestReadBackAttribute_2(); + break; + case 3: + ChipLogProgress(chipTool, " ***** Test Step 3 : Restore initial location value\n"); + err = TestRestoreInitialLocationValue_3(); + break; + case 4: + ChipLogProgress(chipTool, " ***** Test Step 4 : Read back Attribute\n"); + err = TestReadBackAttribute_4(); break; } @@ -56852,7 +56928,7 @@ class TestGroupMessaging : public TestCommand private: std::atomic_uint16_t mTestIndex; - const uint16_t mTestCount = 3; + const uint16_t mTestCount = 5; static void OnDoneCallback_1(void * context) { (static_cast(context))->OnDoneResponse_1(); } @@ -56863,14 +56939,34 @@ class TestGroupMessaging : public TestCommand static void OnSuccessCallback_1(void * context) { (static_cast(context))->OnSuccessResponse_1(); } - static void OnDoneCallback_2(void * context) { (static_cast(context))->OnDoneResponse_2(); } - static void OnFailureCallback_2(void * context, EmberAfStatus status) { (static_cast(context))->OnFailureResponse_2(status); } - static void OnSuccessCallback_2(void * context) { (static_cast(context))->OnSuccessResponse_2(); } + static void OnSuccessCallback_2(void * context, chip::CharSpan location) + { + (static_cast(context))->OnSuccessResponse_2(location); + } + + static void OnDoneCallback_3(void * context) { (static_cast(context))->OnDoneResponse_3(); } + + static void OnFailureCallback_3(void * context, EmberAfStatus status) + { + (static_cast(context))->OnFailureResponse_3(status); + } + + static void OnSuccessCallback_3(void * context) { (static_cast(context))->OnSuccessResponse_3(); } + + static void OnFailureCallback_4(void * context, EmberAfStatus status) + { + (static_cast(context))->OnFailureResponse_4(status); + } + + static void OnSuccessCallback_4(void * context, chip::CharSpan location) + { + (static_cast(context))->OnSuccessResponse_4(location); + } // // Tests methods @@ -56902,7 +56998,27 @@ class TestGroupMessaging : public TestCommand void OnDoneResponse_1() { NextTest(); } - CHIP_ERROR TestRestoreInitialLocationValue_2() + CHIP_ERROR TestReadBackAttribute_2() + { + const chip::EndpointId endpoint = mEndpointId.HasValue() ? mEndpointId.Value() : 0; + chip::Controller::BasicClusterTest cluster; + cluster.Associate(mDevices[kIdentityAlpha], endpoint); + + ReturnErrorOnFailure(cluster.ReadAttribute( + this, OnSuccessCallback_2, OnFailureCallback_2)); + return CHIP_NO_ERROR; + } + + void OnFailureResponse_2(EmberAfStatus status) { ThrowFailureResponse(); } + + void OnSuccessResponse_2(chip::CharSpan location) + { + VerifyOrReturn(CheckValueAsString("location", location, chip::CharSpan("us", 2))); + + NextTest(); + } + + CHIP_ERROR TestRestoreInitialLocationValue_3() { const chip::GroupId groupId = 1234; chip::Controller::BasicClusterTest cluster; @@ -56912,15 +57028,35 @@ class TestGroupMessaging : public TestCommand locationArgument = chip::Span("garbage: not in length on purpose", 0); ReturnErrorOnFailure(cluster.WriteAttribute( - locationArgument, this, OnSuccessCallback_2, OnFailureCallback_2, OnDoneCallback_2)); + locationArgument, this, OnSuccessCallback_3, OnFailureCallback_3, OnDoneCallback_3)); return CHIP_NO_ERROR; } - void OnFailureResponse_2(EmberAfStatus status) { ThrowFailureResponse(); } + void OnFailureResponse_3(EmberAfStatus status) { ThrowFailureResponse(); } - void OnSuccessResponse_2() { NextTest(); } + void OnSuccessResponse_3() { NextTest(); } + + void OnDoneResponse_3() { NextTest(); } - void OnDoneResponse_2() { NextTest(); } + CHIP_ERROR TestReadBackAttribute_4() + { + const chip::EndpointId endpoint = mEndpointId.HasValue() ? mEndpointId.Value() : 0; + chip::Controller::BasicClusterTest cluster; + cluster.Associate(mDevices[kIdentityAlpha], endpoint); + + ReturnErrorOnFailure(cluster.ReadAttribute( + this, OnSuccessCallback_4, OnFailureCallback_4)); + return CHIP_NO_ERROR; + } + + void OnFailureResponse_4(EmberAfStatus status) { ThrowFailureResponse(); } + + void OnSuccessResponse_4(chip::CharSpan location) + { + VerifyOrReturn(CheckValueAsString("location", location, chip::CharSpan("XX", 2))); + + NextTest(); + } }; class Test_TC_DIAGSW_1_1 : public TestCommand