From 7ad2fc9b2db389f10f963a1b1d546df43375edac Mon Sep 17 00:00:00 2001 From: Junior Martinez <67972863+jmartinez-silabs@users.noreply.github.com> Date: Thu, 22 Aug 2024 13:00:41 -0400 Subject: [PATCH 01/11] use CHIP_DEVICE_CONFIG_FAILSAFE_EXPIRY_LENGTH_SEC define to arme the failsafe (#35137) --- src/app/server/CommissioningWindowManager.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/app/server/CommissioningWindowManager.cpp b/src/app/server/CommissioningWindowManager.cpp index 47bc3e8ff97f07..01bff4bf07db46 100644 --- a/src/app/server/CommissioningWindowManager.cpp +++ b/src/app/server/CommissioningWindowManager.cpp @@ -221,7 +221,8 @@ void CommissioningWindowManager::OnSessionEstablished(const SessionHandle & sess } else { - err = failSafeContext.ArmFailSafe(kUndefinedFabricIndex, System::Clock::Seconds16(60)); + err = failSafeContext.ArmFailSafe(kUndefinedFabricIndex, + System::Clock::Seconds16(CHIP_DEVICE_CONFIG_FAILSAFE_EXPIRY_LENGTH_SEC)); if (err != CHIP_NO_ERROR) { ChipLogError(AppServer, "Error arming failsafe on PASE session establishment completion"); From ae5d75c69f6cec4fb62b991e57b895b6f4812291 Mon Sep 17 00:00:00 2001 From: Boris Zbarsky Date: Thu, 22 Aug 2024 13:13:04 -0400 Subject: [PATCH 02/11] Remove unnecessary invokeCommand overrides from MTRDevice_Concrete. (#35151) The one part that is not shared with the XPC implementation is _invokeCommandWithEndpointID:.... Everything else is just generic argument massaging and forwarding that can keep living in the base MTRDevice. --- .../Framework/CHIP/MTRDevice_Concrete.mm | 126 ------------------ 1 file changed, 126 deletions(-) diff --git a/src/darwin/Framework/CHIP/MTRDevice_Concrete.mm b/src/darwin/Framework/CHIP/MTRDevice_Concrete.mm index 120d29aff122d4..12614ea4091b0c 100644 --- a/src/darwin/Framework/CHIP/MTRDevice_Concrete.mm +++ b/src/darwin/Framework/CHIP/MTRDevice_Concrete.mm @@ -2835,59 +2835,6 @@ - (void)writeAttributeWithEndpointID:(NSNumber *)endpointID [_asyncWorkQueue enqueueWorkItem:workItem descriptionWithFormat:@"write %@ 0x%llx 0x%llx", endpointID, clusterID.unsignedLongLongValue, attributeID.unsignedLongLongValue]; } -- (void)invokeCommandWithEndpointID:(NSNumber *)endpointID - clusterID:(NSNumber *)clusterID - commandID:(NSNumber *)commandID - commandFields:(NSDictionary * _Nullable)commandFields - expectedValues:(NSArray *> * _Nullable)expectedValues - expectedValueInterval:(NSNumber * _Nullable)expectedValueInterval - queue:(dispatch_queue_t)queue - completion:(MTRDeviceResponseHandler)completion -{ - if (commandFields == nil) { - commandFields = @{ - MTRTypeKey : MTRStructureValueType, - MTRValueKey : @[], - }; - } - - [self invokeCommandWithEndpointID:endpointID - clusterID:clusterID - commandID:commandID - commandFields:commandFields - expectedValues:expectedValues - expectedValueInterval:expectedValueInterval - timedInvokeTimeout:nil - queue:queue - completion:completion]; -} - -- (void)invokeCommandWithEndpointID:(NSNumber *)endpointID - clusterID:(NSNumber *)clusterID - commandID:(NSNumber *)commandID - commandFields:(id)commandFields - expectedValues:(NSArray *> * _Nullable)expectedValues - expectedValueInterval:(NSNumber * _Nullable)expectedValueInterval - timedInvokeTimeout:(NSNumber * _Nullable)timeout - queue:(dispatch_queue_t)queue - completion:(MTRDeviceResponseHandler)completion -{ - // We don't have a way to communicate a non-default invoke timeout - // here for now. - // TODO: https://github.com/project-chip/connectedhomeip/issues/24563 - - [self _invokeCommandWithEndpointID:endpointID - clusterID:clusterID - commandID:commandID - commandFields:commandFields - expectedValues:expectedValues - expectedValueInterval:expectedValueInterval - timedInvokeTimeout:timeout - serverSideProcessingTimeout:nil - queue:queue - completion:completion]; -} - - (void)_invokeCommandWithEndpointID:(NSNumber *)endpointID clusterID:(NSNumber *)clusterID commandID:(NSNumber *)commandID @@ -2985,58 +2932,6 @@ - (void)_invokeCommandWithEndpointID:(NSNumber *)endpointID [_asyncWorkQueue enqueueWorkItem:workItem descriptionWithFormat:@"invoke %@ 0x%llx 0x%llx", endpointID, clusterID.unsignedLongLongValue, commandID.unsignedLongLongValue]; } -- (void)_invokeKnownCommandWithEndpointID:(NSNumber *)endpointID - clusterID:(NSNumber *)clusterID - commandID:(NSNumber *)commandID - commandPayload:(id)commandPayload - expectedValues:(NSArray *> * _Nullable)expectedValues - expectedValueInterval:(NSNumber * _Nullable)expectedValueInterval - timedInvokeTimeout:(NSNumber * _Nullable)timeout - serverSideProcessingTimeout:(NSNumber * _Nullable)serverSideProcessingTimeout - responseClass:(Class _Nullable)responseClass - queue:(dispatch_queue_t)queue - completion:(void (^)(id _Nullable response, NSError * _Nullable error))completion -{ - if (![commandPayload respondsToSelector:@selector(_encodeAsDataValue:)]) { - dispatch_async(queue, ^{ - completion(nil, [MTRError errorForCHIPErrorCode:CHIP_ERROR_INVALID_ARGUMENT]); - }); - return; - } - - NSError * encodingError; - auto * commandFields = [commandPayload _encodeAsDataValue:&encodingError]; - if (commandFields == nil) { - dispatch_async(queue, ^{ - completion(nil, encodingError); - }); - return; - } - - auto responseHandler = ^(NSArray *> * _Nullable values, NSError * _Nullable error) { - id _Nullable response = nil; - if (error == nil) { - if (values.count != 1) { - error = [NSError errorWithDomain:MTRErrorDomain code:MTRErrorCodeSchemaMismatch userInfo:nil]; - } else if (responseClass != nil) { - response = [[responseClass alloc] initWithResponseValue:values[0] error:&error]; - } - } - completion(response, error); - }; - - [self _invokeCommandWithEndpointID:endpointID - clusterID:clusterID - commandID:commandID - commandFields:commandFields - expectedValues:expectedValues - expectedValueInterval:expectedValueInterval - timedInvokeTimeout:timeout - serverSideProcessingTimeout:serverSideProcessingTimeout - queue:queue - completion:responseHandler]; -} - - (void)openCommissioningWindowWithSetupPasscode:(NSNumber *)setupPasscode discriminator:(NSNumber *)discriminator duration:(NSNumber *)duration @@ -4072,27 +3967,6 @@ + (MTRDevice *)deviceWithNodeID:(uint64_t)nodeID deviceController:(MTRDeviceCont return [self deviceWithNodeID:@(nodeID) controller:deviceController]; } -- (void)invokeCommandWithEndpointID:(NSNumber *)endpointID - clusterID:(NSNumber *)clusterID - commandID:(NSNumber *)commandID - commandFields:(id)commandFields - expectedValues:(NSArray *> * _Nullable)expectedValues - expectedValueInterval:(NSNumber * _Nullable)expectedValueInterval - timedInvokeTimeout:(NSNumber * _Nullable)timeout - clientQueue:(dispatch_queue_t)queue - completion:(MTRDeviceResponseHandler)completion -{ - [self invokeCommandWithEndpointID:endpointID - clusterID:clusterID - commandID:commandID - commandFields:commandFields - expectedValues:expectedValues - expectedValueInterval:expectedValueInterval - timedInvokeTimeout:timeout - queue:queue - completion:completion]; -} - @end #pragma mark - SubscriptionCallback From 26956012d4d6d6a14796a2894ffe4af0a3c88ff9 Mon Sep 17 00:00:00 2001 From: Boris Zbarsky Date: Thu, 22 Aug 2024 13:16:50 -0400 Subject: [PATCH 03/11] Remove readAttributeWithEndpointID implementation from MTRDevice. (#35150) This is implemented (differently) by the different subclasses. Once this implementation is removed, the following become unreachable and can be removed: * _attributeValueDictionaryForAttributePath * _subscriptionAbleToReport * _readThroughSkipped At that point _subscriptionsAllowed becomes unreachable and can be removed. --- src/darwin/Framework/CHIP/MTRDevice.mm | 229 +------------------------ 1 file changed, 7 insertions(+), 222 deletions(-) diff --git a/src/darwin/Framework/CHIP/MTRDevice.mm b/src/darwin/Framework/CHIP/MTRDevice.mm index d4027f7725ddeb..3f2acce5f5587e 100644 --- a/src/darwin/Framework/CHIP/MTRDevice.mm +++ b/src/darwin/Framework/CHIP/MTRDevice.mm @@ -649,21 +649,7 @@ - (void)_setDSTOffsets:(NSArray return outputArray; } -#pragma mark Subscription and delegate handling - -// subscription intervals are in seconds -#define MTR_DEVICE_SUBSCRIPTION_MAX_INTERVAL_MIN (10 * 60) // 10 minutes (for now) -#define MTR_DEVICE_SUBSCRIPTION_MAX_INTERVAL_MAX (60 * 60) // 60 minutes - -- (BOOL)_subscriptionsAllowed -{ - os_unfair_lock_assert_owner(&self->_lock); - - // TODO: XPC: This function and all its callsites should go away from this class. - - // We should not allow a subscription for device controllers over XPC. - return ![_deviceController isKindOfClass:MTRDeviceControllerOverXPC.class]; -} +#pragma mark Delegate handling - (void)setDelegate:(id)delegate queue:(dispatch_queue_t)queue { @@ -757,41 +743,6 @@ - (void)nodeMayBeAdvertisingOperational MTR_LOG("%@ saw new operational advertisement", self); } -// Return YES if we are in a state where, apart from communication issues with -// the device, we will be able to get reports via our subscription. -- (BOOL)_subscriptionAbleToReport -{ - std::lock_guard lock(_lock); - if (![self _delegateExists]) { - // No delegate definitely means no subscription. - return NO; - } - - // For unit testing only, matching logic in setDelegate -#ifdef DEBUG - __block BOOL useTestDelegateOverride = NO; - __block BOOL testDelegateShouldSetUpSubscriptionForDevice = NO; - [self _callFirstDelegateSynchronouslyWithBlock:^(id testDelegate) { - if ([testDelegate respondsToSelector:@selector(unitTestShouldSetUpSubscriptionForDevice:)]) { - useTestDelegateOverride = YES; - testDelegateShouldSetUpSubscriptionForDevice = [testDelegate unitTestShouldSetUpSubscriptionForDevice:self]; - } - }]; - if (useTestDelegateOverride && !testDelegateShouldSetUpSubscriptionForDevice) { - return NO; - } - -#endif - - // Subscriptions are not able to report if they are not allowed. - return [self _subscriptionsAllowed]; -} - -// Notification that read-through was skipped for an attribute read. -- (void)_readThroughSkipped -{ -} - - (BOOL)_delegateExists { os_unfair_lock_assert_owner(&self->_lock); @@ -1880,147 +1831,12 @@ static BOOL AttributeHasChangesOmittedQuality(MTRAttributePath * attributePath) attributeID:(NSNumber *)attributeID params:(MTRReadParams * _Nullable)params { - MTRAttributePath * attributePath = [MTRAttributePath attributePathWithEndpointID:endpointID - clusterID:clusterID - attributeID:attributeID]; - - BOOL attributeIsSpecified = MTRAttributeIsSpecified(clusterID.unsignedIntValue, attributeID.unsignedIntValue); - BOOL hasChangesOmittedQuality; - if (attributeIsSpecified) { - hasChangesOmittedQuality = AttributeHasChangesOmittedQuality(attributePath); - } else { - if (params == nil) { - hasChangesOmittedQuality = NO; - } else { - hasChangesOmittedQuality = !params.assumeUnknownAttributesReportable; - } - } - - // Return current known / expected value right away - NSDictionary * attributeValueToReturn = [self _attributeValueDictionaryForAttributePath:attributePath]; - - // Send read request to device if any of the following are true: - // 1. Subscription not in a state we can expect reports - // 2. The attribute has the Changes Omitted quality, so we won't get reports for it. - // 3. The attribute is not in the spec, and the read params asks to assume - // an unknown attribute has the Changes Omitted quality. - if (![self _subscriptionAbleToReport] || hasChangesOmittedQuality) { - // Read requests container will be a mutable array of items, each being an array containing: - // [attribute request path, params] - // Batching handler should only coalesce when params are equal. - - // For this single read API there's only 1 array item. Use NSNull to stand in for nil params for easy comparison. - MTRAttributeRequestPath * readRequestPath = [MTRAttributeRequestPath requestPathWithEndpointID:endpointID - clusterID:clusterID - attributeID:attributeID]; - NSArray * readRequestData = @[ readRequestPath, params ?: [NSNull null] ]; - - // But first, check if a duplicate read request is already queued and return - if ([_asyncWorkQueue hasDuplicateForTypeID:MTRDeviceWorkItemDuplicateReadTypeID workItemData:readRequestData]) { - return attributeValueToReturn; - } - - NSMutableArray * readRequests = [NSMutableArray arrayWithObject:readRequestData]; - - // Create work item, set ready handler to perform task, then enqueue the work - MTRAsyncWorkItem * workItem = [[MTRAsyncWorkItem alloc] initWithQueue:self.queue]; - uint64_t workItemID = workItem.uniqueID; // capture only the ID, not the work item - NSNumber * nodeID = [self nodeID]; - - [workItem setBatchingID:MTRDeviceWorkItemBatchingReadID data:readRequests handler:^(id opaqueDataCurrent, id opaqueDataNext) { - mtr_hide(self); // don't capture self accidentally - NSMutableArray * readRequestsCurrent = opaqueDataCurrent; - NSMutableArray * readRequestsNext = opaqueDataNext; - - MTRBatchingOutcome outcome = MTRNotBatched; - while (readRequestsNext.count) { - // Can only read up to 9 paths at a time, per spec - if (readRequestsCurrent.count >= 9) { - MTR_LOG("Batching read attribute work item [%llu]: cannot add more work, item is full [0x%016llX:%@:0x%llx:0x%llx]", workItemID, nodeID.unsignedLongLongValue, endpointID, clusterID.unsignedLongLongValue, attributeID.unsignedLongLongValue); - return outcome; - } - - // if params don't match then they cannot be merged - if (![readRequestsNext[0][MTRDeviceReadRequestFieldParamsIndex] - isEqual:readRequestsCurrent[0][MTRDeviceReadRequestFieldParamsIndex]]) { - MTR_LOG("Batching read attribute work item [%llu]: cannot add more work, parameter mismatch [0x%016llX:%@:0x%llx:0x%llx]", workItemID, nodeID.unsignedLongLongValue, endpointID, clusterID.unsignedLongLongValue, attributeID.unsignedLongLongValue); - return outcome; - } - - // merge the next item's first request into the current item's list - auto readItem = readRequestsNext.firstObject; - [readRequestsNext removeObjectAtIndex:0]; - [readRequestsCurrent addObject:readItem]; - MTR_LOG("Batching read attribute work item [%llu]: added %@ (now %lu requests total) [0x%016llX:%@:0x%llx:0x%llx]", - workItemID, readItem, static_cast(readRequestsCurrent.count), nodeID.unsignedLongLongValue, endpointID, clusterID.unsignedLongLongValue, attributeID.unsignedLongLongValue); - outcome = MTRBatchedPartially; - } - NSCAssert(readRequestsNext.count == 0, @"should have batched everything or returned early"); - return MTRBatchedFully; - }]; - [workItem setDuplicateTypeID:MTRDeviceWorkItemDuplicateReadTypeID handler:^(id opaqueItemData, BOOL * isDuplicate, BOOL * stop) { - mtr_hide(self); // don't capture self accidentally - for (NSArray * readItem in readRequests) { - if ([readItem isEqual:opaqueItemData]) { - MTR_LOG("Read attribute work item [%llu] report duplicate %@ [0x%016llX:%@:0x%llx:0x%llx]", workItemID, readItem, nodeID.unsignedLongLongValue, endpointID, clusterID.unsignedLongLongValue, attributeID.unsignedLongLongValue); - *isDuplicate = YES; - *stop = YES; - return; - } - } - *stop = NO; - }]; - [workItem setReadyHandler:^(MTRDevice * self, NSInteger retryCount, MTRAsyncWorkCompletionBlock completion) { - // Sanity check - if (readRequests.count == 0) { - MTR_LOG_ERROR("Read attribute work item [%llu] contained no read requests", workItemID); - completion(MTRAsyncWorkComplete); - return; - } - - // Build the attribute paths from the read requests - NSMutableArray * attributePaths = [NSMutableArray array]; - for (NSArray * readItem in readRequests) { - NSAssert(readItem.count == 2, @"invalid read attribute item"); - [attributePaths addObject:readItem[MTRDeviceReadRequestFieldPathIndex]]; - } - // If param is the NSNull stand-in, then just use nil - id readParamObject = readRequests[0][MTRDeviceReadRequestFieldParamsIndex]; - MTRReadParams * readParams = (![readParamObject isEqual:[NSNull null]]) ? readParamObject : nil; - - MTRBaseDevice * baseDevice = [self newBaseDevice]; - [baseDevice - readAttributePaths:attributePaths - eventPaths:nil - params:readParams - includeDataVersion:YES - queue:self.queue - completion:^(NSArray *> * _Nullable values, NSError * _Nullable error) { - if (values) { - // Since the format is the same data-value dictionary, this looks like an - // attribute report - MTR_LOG("Read attribute work item [%llu] result: %@ [0x%016llX:%@:0x%llX:0x%llX]", workItemID, values, nodeID.unsignedLongLongValue, endpointID, clusterID.unsignedLongLongValue, attributeID.unsignedLongLongValue); - [self _handleAttributeReport:values fromSubscription:NO]; - } - - // TODO: better retry logic - if (error && (retryCount < 2)) { - MTR_LOG_ERROR("Read attribute work item [%llu] failed (will retry): %@ [0x%016llX:%@:0x%llx:0x%llx]", workItemID, error, nodeID.unsignedLongLongValue, endpointID, clusterID.unsignedLongLongValue, attributeID.unsignedLongLongValue); - completion(MTRAsyncWorkNeedsRetry); - } else { - if (error) { - MTR_LOG("Read attribute work item [%llu] failed (giving up): %@ [0x%016llX:%@:0x%llx:0x%llx]", workItemID, error, nodeID.unsignedLongLongValue, endpointID, clusterID.unsignedLongLongValue, attributeID.unsignedLongLongValue); - } - completion(MTRAsyncWorkComplete); - } - }]; - }]; - [_asyncWorkQueue enqueueWorkItem:workItem descriptionWithFormat:@"read %@ 0x%llx 0x%llx", endpointID, clusterID.unsignedLongLongValue, attributeID.unsignedLongLongValue]; - } else { - [self _readThroughSkipped]; - } - - return attributeValueToReturn; +#define ErrorStr "MTRDevice readAttributeWithEndpointID:clusterID:attributeID:params: must be handled by subclasses" + MTR_LOG_ERROR(ErrorStr); +#ifdef DEBUG + NSAssert(NO, @ErrorStr); +#endif // DEBUG + return nil; } - (void)writeAttributeWithEndpointID:(NSNumber *)endpointID @@ -2456,37 +2272,6 @@ - (void)_performScheduledExpirationCheck [self _checkExpiredExpectedValues]; } -// Get attribute value dictionary for an attribute path from the right cache -- (NSDictionary *)_attributeValueDictionaryForAttributePath:(MTRAttributePath *)attributePath -{ - std::lock_guard lock(_lock); - - // First check expected value cache - NSArray * expectedValue = _expectedValueCache[attributePath]; - if (expectedValue) { - NSDate * now = [NSDate date]; - if ([now compare:expectedValue[MTRDeviceExpectedValueFieldExpirationTimeIndex]] == NSOrderedDescending) { - // expired - purge and fall through - _expectedValueCache[attributePath] = nil; - } else { - // not yet expired - return result - return expectedValue[MTRDeviceExpectedValueFieldValueIndex]; - } - } - - // Then check read cache - NSDictionary * cachedAttributeValue = [self _cachedAttributeValueForPath:attributePath]; - if (cachedAttributeValue) { - return cachedAttributeValue; - } else { - // TODO: when not found in cache, generated default values should be used - MTR_LOG("%@ _attributeValueDictionaryForAttributePath: could not find cached attribute values for attribute %@", self, - attributePath); - } - - return nil; -} - - (BOOL)_attributeDataValue:(NSDictionary *)one isEqualToDataValue:(NSDictionary *)theOther { // Sanity check for nil cases From c6710f6268493f600b6d8ed5e7007abaa1c6f39e Mon Sep 17 00:00:00 2001 From: Arkadiusz Bokowy Date: Thu, 22 Aug 2024 21:12:55 +0200 Subject: [PATCH 04/11] Fix paths for fabric apps compiled with build_examples.py (#35123) * Fix paths for fabric apps compiled with build_examples.py * Require RPC to be enabled when building fabric-admin and fabric-bridge --- examples/fabric-admin/scripts/run_fabric_sync.sh | 8 ++++---- examples/fabric-admin/scripts/stop_fabric_sync.sh | 2 +- scripts/build/build/targets.py | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/examples/fabric-admin/scripts/run_fabric_sync.sh b/examples/fabric-admin/scripts/run_fabric_sync.sh index 8c820d111c37c1..957fe5f3cf16e9 100755 --- a/examples/fabric-admin/scripts/run_fabric_sync.sh +++ b/examples/fabric-admin/scripts/run_fabric_sync.sh @@ -7,14 +7,14 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" DEFAULT_ADMIN_CHOICES=( "./fabric-admin" "out/debug/standalone/fabric-admin" - "out/linux-x64-fabric-admin/fabric-admin" - "out/darwin-arm64-fabric-admin/fabric-admin" + "out/linux-x64-fabric-admin-rpc/fabric-admin" + "out/darwin-arm64-fabric-admin-rpc/fabric-admin" ) DEFAULT_BRIDGE_CHOICES=( "./fabric-bridge-app" "out/debug/standalone/fabric-bridge-app" - "out/linux-x64-fabric-bridge-app/fabric-bridge-app" - "out/darwin-arm64-fabric-bridge-app/fabric-bridge-app" + "out/linux-x64-fabric-bridge-rpc/fabric-bridge-app" + "out/darwin-arm64-fabric-bridge-rpc/fabric-bridge-app" ) FABRIC_ADMIN_LOG="/tmp/fabric_admin.log" FABRIC_BRIDGE_APP_LOG="/tmp/fabric_bridge_app.log" diff --git a/examples/fabric-admin/scripts/stop_fabric_sync.sh b/examples/fabric-admin/scripts/stop_fabric_sync.sh index e947c8992fac27..faa5c79ce211b6 100755 --- a/examples/fabric-admin/scripts/stop_fabric_sync.sh +++ b/examples/fabric-admin/scripts/stop_fabric_sync.sh @@ -20,5 +20,5 @@ if [ ! -z "$fabric_bridge_app_pid" ]; then fi # Remove /tmp/chip_* files and directories -sudo rm -rf /tmp/chip_* +rm -rf /tmp/chip_* echo "Removed /tmp/chip_* files and directories" diff --git a/scripts/build/build/targets.py b/scripts/build/build/targets.py index 8ab6e2ca4e0d58..37ede960ac6f9f 100755 --- a/scripts/build/build/targets.py +++ b/scripts/build/build/targets.py @@ -128,8 +128,8 @@ def BuildHostTarget(): TargetPart('tv-app', app=HostApp.TV_APP), TargetPart('tv-casting-app', app=HostApp.TV_CASTING), TargetPart('bridge', app=HostApp.BRIDGE), - TargetPart('fabric-admin', app=HostApp.FABRIC_ADMIN), - TargetPart('fabric-bridge', app=HostApp.FABRIC_BRIDGE), + TargetPart('fabric-admin', app=HostApp.FABRIC_ADMIN).OnlyIfRe("-rpc"), + TargetPart('fabric-bridge', app=HostApp.FABRIC_BRIDGE).OnlyIfRe("-rpc"), TargetPart('tests', app=HostApp.TESTS), TargetPart('chip-cert', app=HostApp.CERT_TOOL), TargetPart('address-resolve-tool', app=HostApp.ADDRESS_RESOLVE), From c1403a4f68456eb238ae5d09be85b0699c2df75f Mon Sep 17 00:00:00 2001 From: Arkadiusz Bokowy Date: Thu, 22 Aug 2024 21:14:27 +0200 Subject: [PATCH 05/11] [Fabric-Sync] Allow to specify custom port in fabric-bridge (#35146) --- .../fabric-bridge-common/include/CHIPProjectAppConfig.h | 3 +++ examples/fabric-bridge-app/linux/args.gni | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/examples/fabric-bridge-app/fabric-bridge-common/include/CHIPProjectAppConfig.h b/examples/fabric-bridge-app/fabric-bridge-common/include/CHIPProjectAppConfig.h index 326ddf28572517..534079821043a8 100644 --- a/examples/fabric-bridge-app/fabric-bridge-common/include/CHIPProjectAppConfig.h +++ b/examples/fabric-bridge-app/fabric-bridge-common/include/CHIPProjectAppConfig.h @@ -32,3 +32,6 @@ // include the CHIPProjectConfig from config/standalone #include + +// Allows app options (ports) to be configured on launch of app +#define CHIP_DEVICE_ENABLE_PORT_PARAMS 1 diff --git a/examples/fabric-bridge-app/linux/args.gni b/examples/fabric-bridge-app/linux/args.gni index acbabffb359358..592855ea98ffa3 100644 --- a/examples/fabric-bridge-app/linux/args.gni +++ b/examples/fabric-bridge-app/linux/args.gni @@ -21,7 +21,7 @@ chip_project_config_include = "" chip_system_project_config_include = "" chip_project_config_include_dirs = - [ "${chip_root}/examples/bridge-app/bridge-common/include" ] + [ "${chip_root}/examples/fabric-bridge-app/fabric-bridge-common/include" ] chip_project_config_include_dirs += [ "${chip_root}/config/standalone" ] matter_enable_tracing_support = true From 339274a3ce9f12cb42549460802a1fb2f4dfed93 Mon Sep 17 00:00:00 2001 From: Jeff Tung <100387939+jtung-apple@users.noreply.github.com> Date: Thu, 22 Aug 2024 14:39:10 -0700 Subject: [PATCH 06/11] [Darwin] XPC invoke should implement bottom-most method (#35159) * [Darwin] XPC invoke should implement bottom-most method * Restyled fix --- .../Framework/CHIP/MTRDeviceController_XPC.mm | 2 +- src/darwin/Framework/CHIP/MTRDevice_XPC.mm | 21 +++++++++++-------- .../CHIP/XPC Protocol/MTRXPCServerProtocol.h | 3 ++- 3 files changed, 15 insertions(+), 11 deletions(-) diff --git a/src/darwin/Framework/CHIP/MTRDeviceController_XPC.mm b/src/darwin/Framework/CHIP/MTRDeviceController_XPC.mm index 7111aa4eb7f2bd..67049b280d81f3 100644 --- a/src/darwin/Framework/CHIP/MTRDeviceController_XPC.mm +++ b/src/darwin/Framework/CHIP/MTRDeviceController_XPC.mm @@ -52,7 +52,7 @@ - (NSXPCInterface *)_interfaceForServerProtocol ]]; [interface setClasses:allowedClasses - forSelector:@selector(deviceController:nodeID:invokeCommandWithEndpointID:clusterID:commandID:commandFields:expectedValues:expectedValueInterval:timedInvokeTimeout:completion:) + forSelector:@selector(deviceController:nodeID:invokeCommandWithEndpointID:clusterID:commandID:commandFields:expectedValues:expectedValueInterval:timedInvokeTimeout:serverSideProcessingTimeout:completion:) argumentIndex:0 ofReply:YES]; return interface; diff --git a/src/darwin/Framework/CHIP/MTRDevice_XPC.mm b/src/darwin/Framework/CHIP/MTRDevice_XPC.mm index 03f318d83517d7..bc3b0a6b26bc4b 100644 --- a/src/darwin/Framework/CHIP/MTRDevice_XPC.mm +++ b/src/darwin/Framework/CHIP/MTRDevice_XPC.mm @@ -194,18 +194,20 @@ - (oneway void)deviceConfigurationChanged:(NSNumber *)nodeID : expectedValueInterval timedWriteTimeout : timeout) -- (void)invokeCommandWithEndpointID:(NSNumber *)endpointID - clusterID:(NSNumber *)clusterID - commandID:(NSNumber *)commandID - commandFields:(id)commandFields - expectedValues:(NSArray *> * _Nullable)expectedValues - expectedValueInterval:(NSNumber * _Nullable)expectedValueInterval - timedInvokeTimeout:(NSNumber * _Nullable)timeout - queue:(dispatch_queue_t)queue - completion:(MTRDeviceResponseHandler)completion +- (void)_invokeCommandWithEndpointID:(NSNumber *)endpointID + clusterID:(NSNumber *)clusterID + commandID:(NSNumber *)commandID + commandFields:(id)commandFields + expectedValues:(NSArray *> * _Nullable)expectedValues + expectedValueInterval:(NSNumber * _Nullable)expectedValueInterval + timedInvokeTimeout:(NSNumber * _Nullable)timeout + serverSideProcessingTimeout:(NSNumber * _Nullable)serverSideProcessingTimeout + queue:(dispatch_queue_t)queue + completion:(MTRDeviceResponseHandler)completion { NSXPCConnection * xpcConnection = [(MTRDeviceController_XPC *) [self deviceController] xpcConnection]; + // TODO: use asynchronous XPC and register a block with controller to call for this transaction [[xpcConnection synchronousRemoteObjectProxyWithErrorHandler:^(NSError * _Nonnull error) { MTR_LOG_ERROR("Error: %@", error); }] deviceController:[[self deviceController] uniqueIdentifier] @@ -217,6 +219,7 @@ - (void)invokeCommandWithEndpointID:(NSNumber *)endpointID expectedValues:expectedValues expectedValueInterval:expectedValueInterval timedInvokeTimeout:timeout + serverSideProcessingTimeout:serverSideProcessingTimeout completion:completion]; } diff --git a/src/darwin/Framework/CHIP/XPC Protocol/MTRXPCServerProtocol.h b/src/darwin/Framework/CHIP/XPC Protocol/MTRXPCServerProtocol.h index 1eebb0396570ac..81d6ab930cb6aa 100644 --- a/src/darwin/Framework/CHIP/XPC Protocol/MTRXPCServerProtocol.h +++ b/src/darwin/Framework/CHIP/XPC Protocol/MTRXPCServerProtocol.h @@ -29,7 +29,8 @@ MTR_NEWLY_AVAILABLE - (oneway void)deviceController:(NSUUID *)controller nodeID:(NSNumber *)nodeID readAttributeWithEndpointID:(NSNumber *)endpointID clusterID:(NSNumber *)clusterID attributeID:(NSNumber *)attributeID params:(MTRReadParams * _Nullable)params withReply:(void (^)(NSDictionary * _Nullable))reply; - (oneway void)deviceController:(NSUUID *)controller nodeID:(NSNumber *)nodeID writeAttributeWithEndpointID:(NSNumber *)endpointID clusterID:(NSNumber *)clusterID attributeID:(NSNumber *)attributeID value:(id _Nullable)value expectedValueInterval:(NSNumber * _Nullable)expectedValueInterval timedWriteTimeout:(NSNumber * _Nullable)timeout; -- (oneway void)deviceController:(NSUUID *)controller nodeID:(NSNumber *)nodeID invokeCommandWithEndpointID:(NSNumber *)endpointID clusterID:(NSNumber *)clusterID commandID:(NSNumber *)commandID commandFields:(id)commandFields expectedValues:(NSArray *> * _Nullable)expectedValues expectedValueInterval:(NSNumber * _Nullable)expectedValueInterval timedInvokeTimeout:(NSNumber * _Nullable)timeout completion:(MTRDeviceResponseHandler)completion; +- (oneway void)deviceController:(NSUUID *)controller nodeID:(NSNumber *)nodeID invokeCommandWithEndpointID:(NSNumber *)endpointID clusterID:(NSNumber *)clusterID commandID:(NSNumber *)commandID commandFields:(id)commandFields expectedValues:(NSArray *> * _Nullable)expectedValues expectedValueInterval:(NSNumber * _Nullable)expectedValueInterval timedInvokeTimeout:(NSNumber * _Nullable)timeout serverSideProcessingTimeout:(NSNumber * _Nullable)serverSideProcessingTimeout + completion:(MTRDeviceResponseHandler)completion; // Not Supported via XPC //- (oneway void)deviceController:(NSUUID *)controller nodeID:(NSNumber *)nodeID openCommissioningWindowWithSetupPasscode:(NSNumber *)setupPasscode discriminator:(NSNumber *)discriminator duration:(NSNumber *)duration completion:(MTRDeviceOpenCommissioningWindowHandler)completion; From 47b2783152208b40148d66a96f946dadc831986b Mon Sep 17 00:00:00 2001 From: William Date: Thu, 22 Aug 2024 23:46:47 +0100 Subject: [PATCH 07/11] Update PositionTag and AreaDesc field names (#35094) * Updated the PositionTag and AreaDesc field names in the service area XML. * Zap generated after XML update. * Updated the PositionTag and AreaDesc field names in the SDK server code and test scripts. * Restyled by clang-format * Fixed issues caused by merge. * reverted changes in generated code. * encoding --------- Co-authored-by: Restyled.io --- .../rvc-app/linux/RvcAppCommandDelegate.cpp | 4 +- examples/rvc-app/rvc-common/rvc-app.matter | 4 +- .../service-area-cluster-objects.h | 50 ++++---- .../service-area-server.cpp | 8 +- .../data-model/chip/service-area-cluster.xml | 6 +- .../data_model/controller-clusters.matter | 4 +- .../chip/devicecontroller/ChipStructs.java | 44 +++---- .../structs/ServiceAreaClusterAreaStruct.kt | 14 +-- .../ServiceAreaClusterLandmarkInfoStruct.kt | 20 ++-- .../structs/ServiceAreaClusterAreaStruct.kt | 14 +-- .../ServiceAreaClusterLandmarkInfoStruct.kt | 23 ++-- .../CHIPAttributeTLVValueDecoder.cpp | 111 +++++++++--------- .../python/chip/clusters/Objects.py | 8 +- .../MTRAttributeTLVValueDecoder.mm | 38 +++--- .../CHIP/zap-generated/MTRStructsObjc.h | 4 +- .../CHIP/zap-generated/MTRStructsObjc.mm | 12 +- src/python_testing/TC_SEAR_1_2.py | 30 ++--- .../zap-generated/cluster-objects.cpp | 12 +- .../zap-generated/cluster-objects.h | 10 +- .../cluster/ComplexArgumentParser.cpp | 22 ++-- .../cluster/logging/DataModelLogger.cpp | 8 +- 21 files changed, 225 insertions(+), 221 deletions(-) diff --git a/examples/rvc-app/linux/RvcAppCommandDelegate.cpp b/examples/rvc-app/linux/RvcAppCommandDelegate.cpp index 791df4d482cfe8..187997f9f8bf00 100644 --- a/examples/rvc-app/linux/RvcAppCommandDelegate.cpp +++ b/examples/rvc-app/linux/RvcAppCommandDelegate.cpp @@ -216,9 +216,9 @@ void RvcAppCommandHandler::OnAddServiceAreaArea(Json::Value jsonValue) if (jsonValue.isMember("LandmarkTag")) { DataModel::Nullable relativePositionTag = DataModel::NullNullable; - if (jsonValue.isMember("PositionTag")) + if (jsonValue.isMember("RelativePositionTag")) { - relativePositionTag = Globals::RelativePositionTag(jsonValue["PositionTag"].asUInt()); + relativePositionTag = Globals::RelativePositionTag(jsonValue["RelativePositionTag"].asUInt()); } area.SetLandmarkInfo(Globals::LandmarkTag(jsonValue["LandmarkTag"].asUInt()), relativePositionTag); diff --git a/examples/rvc-app/rvc-common/rvc-app.matter b/examples/rvc-app/rvc-common/rvc-app.matter index 4467c170acc6eb..29460e1f43c7a1 100644 --- a/examples/rvc-app/rvc-common/rvc-app.matter +++ b/examples/rvc-app/rvc-common/rvc-app.matter @@ -1458,7 +1458,7 @@ provisional cluster ServiceArea = 336 { struct LandmarkInfoStruct { LandmarkTag landmarkTag = 0; - nullable RelativePositionTag positionTag = 1; + nullable RelativePositionTag relativePositionTag = 1; } struct AreaInfoStruct { @@ -1469,7 +1469,7 @@ provisional cluster ServiceArea = 336 { struct AreaStruct { int32u areaID = 0; nullable int32u mapID = 1; - AreaInfoStruct areaDesc = 2; + AreaInfoStruct areaInfo = 2; } struct MapStruct { diff --git a/src/app/clusters/service-area-server/service-area-cluster-objects.h b/src/app/clusters/service-area-server/service-area-cluster-objects.h index 69a392a874f49d..470cd09c02b67d 100644 --- a/src/app/clusters/service-area-server/service-area-cluster-objects.h +++ b/src/app/clusters/service-area-server/service-area-cluster-objects.h @@ -68,8 +68,8 @@ struct AreaStructureWrapper : public chip::app::Clusters::ServiceArea::Structs:: { areaID = aOther.areaID; mapID = aOther.mapID; - SetLocationInfo(aOther.areaDesc.locationInfo); - SetLandmarkInfo(aOther.areaDesc.landmarkInfo); + SetLocationInfo(aOther.areaInfo.locationInfo); + SetLandmarkInfo(aOther.areaInfo.landmarkInfo); return *this; } @@ -95,7 +95,7 @@ struct AreaStructureWrapper : public chip::app::Clusters::ServiceArea::Structs:: AreaStructureWrapper & SetLocationInfoNull() { - areaDesc.locationInfo.SetNull(); + areaInfo.locationInfo.SetNull(); return *this; } @@ -108,15 +108,15 @@ struct AreaStructureWrapper : public chip::app::Clusters::ServiceArea::Structs:: AreaStructureWrapper & SetLocationInfo(const CharSpan & locationName, const DataModel::Nullable & floorNumber, const DataModel::Nullable & areaType) { - areaDesc.locationInfo.SetNonNull(); + areaInfo.locationInfo.SetNonNull(); // Copy the name. If the name is larger than kAreaNameMaxSize, truncate it to fit. auto sizeToCopy = std::min(kAreaNameMaxSize, locationName.size()); memcpy(mAreaNameBuffer, locationName.data(), sizeToCopy); - areaDesc.locationInfo.Value().locationName = CharSpan(mAreaNameBuffer, sizeToCopy); + areaInfo.locationInfo.Value().locationName = CharSpan(mAreaNameBuffer, sizeToCopy); - areaDesc.locationInfo.Value().floorNumber = floorNumber; - areaDesc.locationInfo.Value().areaType = areaType; + areaInfo.locationInfo.Value().floorNumber = floorNumber; + areaInfo.locationInfo.Value().areaType = areaType; return *this; } @@ -138,7 +138,7 @@ struct AreaStructureWrapper : public chip::app::Clusters::ServiceArea::Structs:: AreaStructureWrapper & SetLandmarkInfoNull() { - areaDesc.landmarkInfo.SetNull(); + areaInfo.landmarkInfo.SetNull(); return *this; } @@ -150,9 +150,9 @@ struct AreaStructureWrapper : public chip::app::Clusters::ServiceArea::Structs:: AreaStructureWrapper & SetLandmarkInfo(const Globals::LandmarkTag & landmarkTag, const DataModel::Nullable & relativePositionTag) { - areaDesc.landmarkInfo.SetNonNull(); - areaDesc.landmarkInfo.Value().landmarkTag = landmarkTag; - areaDesc.landmarkInfo.Value().positionTag = relativePositionTag; + areaInfo.landmarkInfo.SetNonNull(); + areaInfo.landmarkInfo.Value().landmarkTag = landmarkTag; + areaInfo.landmarkInfo.Value().relativePositionTag = relativePositionTag; return *this; } @@ -167,7 +167,7 @@ struct AreaStructureWrapper : public chip::app::Clusters::ServiceArea::Structs:: return SetLandmarkInfoNull(); } - return SetLandmarkInfo(landmarkInfo.Value().landmarkTag, landmarkInfo.Value().positionTag); + return SetLandmarkInfo(landmarkInfo.Value().landmarkTag, landmarkInfo.Value().relativePositionTag); } /** @@ -178,9 +178,9 @@ struct AreaStructureWrapper : public chip::app::Clusters::ServiceArea::Structs:: */ bool IsNameEqual(const CharSpan & aAreaName) const { - if (!areaDesc.locationInfo.IsNull()) + if (!areaInfo.locationInfo.IsNull()) { - return areaDesc.locationInfo.Value().locationName.data_equal(aAreaName); + return areaInfo.locationInfo.Value().locationName.data_equal(aAreaName); } return false; @@ -215,43 +215,43 @@ struct AreaStructureWrapper : public chip::app::Clusters::ServiceArea::Structs:: return false; } - if (areaDesc.locationInfo.IsNull() != aOther.areaDesc.locationInfo.IsNull()) + if (areaInfo.locationInfo.IsNull() != aOther.areaInfo.locationInfo.IsNull()) { return false; } - if (!areaDesc.locationInfo.IsNull()) + if (!areaInfo.locationInfo.IsNull()) { - if (!IsNameEqual(aOther.areaDesc.locationInfo.Value().locationName)) + if (!IsNameEqual(aOther.areaInfo.locationInfo.Value().locationName)) { return false; } - if (areaDesc.locationInfo.Value().floorNumber != aOther.areaDesc.locationInfo.Value().floorNumber) + if (areaInfo.locationInfo.Value().floorNumber != aOther.areaInfo.locationInfo.Value().floorNumber) { return false; } - if (areaDesc.locationInfo.Value().areaType != aOther.areaDesc.locationInfo.Value().areaType) + if (areaInfo.locationInfo.Value().areaType != aOther.areaInfo.locationInfo.Value().areaType) { return false; } } - if (areaDesc.landmarkInfo.IsNull() != aOther.areaDesc.landmarkInfo.IsNull()) + if (areaInfo.landmarkInfo.IsNull() != aOther.areaInfo.landmarkInfo.IsNull()) { return false; } - if (!areaDesc.landmarkInfo.IsNull()) + if (!areaInfo.landmarkInfo.IsNull()) { - if (areaDesc.landmarkInfo.Value().landmarkTag != aOther.areaDesc.landmarkInfo.Value().landmarkTag) + if (areaInfo.landmarkInfo.Value().landmarkTag != aOther.areaInfo.landmarkInfo.Value().landmarkTag) { return false; } - if (areaDesc.landmarkInfo.Value().positionTag != aOther.areaDesc.landmarkInfo.Value().positionTag) + if (areaInfo.landmarkInfo.Value().relativePositionTag != aOther.areaInfo.landmarkInfo.Value().relativePositionTag) { return false; } @@ -265,12 +265,12 @@ struct AreaStructureWrapper : public chip::app::Clusters::ServiceArea::Structs:: */ CharSpan GetName() { - if (areaDesc.locationInfo.IsNull()) + if (areaInfo.locationInfo.IsNull()) { return { mAreaNameBuffer, 0 }; } - return areaDesc.locationInfo.Value().locationName; + return areaInfo.locationInfo.Value().locationName; } private: diff --git a/src/app/clusters/service-area-server/service-area-server.cpp b/src/app/clusters/service-area-server/service-area-server.cpp index baf56cd4fc84be..2f2efceddd903d 100644 --- a/src/app/clusters/service-area-server/service-area-server.cpp +++ b/src/app/clusters/service-area-server/service-area-server.cpp @@ -442,7 +442,7 @@ bool Instance::IsValidSupportedArea(const AreaStructureWrapper & aArea) { // If the LocationInfo field is null, the LandmarkInfo field SHALL NOT be null. // If the LandmarkInfo field is null, the LocationInfo field SHALL NOT be null. - if (aArea.areaDesc.locationInfo.IsNull() && aArea.areaDesc.landmarkInfo.IsNull()) + if (aArea.areaInfo.locationInfo.IsNull() && aArea.areaInfo.landmarkInfo.IsNull()) { ChipLogDetail(Zcl, "IsValidAsSupportedArea %" PRIu32 " - must have locationInfo and/or LandmarkInfo", aArea.areaID); return false; @@ -450,10 +450,10 @@ bool Instance::IsValidSupportedArea(const AreaStructureWrapper & aArea) // If LocationInfo is not null, and its LocationName field is an empty string, at least one of the following SHALL NOT // be null: LocationInfo's FloorNumber field, LocationInfo's AreaType field, the LandmarkInfo - if (!aArea.areaDesc.locationInfo.IsNull()) + if (!aArea.areaInfo.locationInfo.IsNull()) { - if (aArea.areaDesc.locationInfo.Value().locationName.empty() && aArea.areaDesc.locationInfo.Value().floorNumber.IsNull() && - aArea.areaDesc.locationInfo.Value().areaType.IsNull() && aArea.areaDesc.landmarkInfo.IsNull()) + if (aArea.areaInfo.locationInfo.Value().locationName.empty() && aArea.areaInfo.locationInfo.Value().floorNumber.IsNull() && + aArea.areaInfo.locationInfo.Value().areaType.IsNull() && aArea.areaInfo.landmarkInfo.IsNull()) { ChipLogDetail( Zcl, "IsValidAsSupportedArea %" PRIu32 " - AreaName is empty string, FloorNumber, AreaType, LandmarkInfo are null", diff --git a/src/app/zap-templates/zcl/data-model/chip/service-area-cluster.xml b/src/app/zap-templates/zcl/data-model/chip/service-area-cluster.xml index e2f0199209d4eb..e4457c3745e2a7 100644 --- a/src/app/zap-templates/zcl/data-model/chip/service-area-cluster.xml +++ b/src/app/zap-templates/zcl/data-model/chip/service-area-cluster.xml @@ -20,8 +20,8 @@ limitations under the License. Data types - - + + @@ -40,7 +40,7 @@ limitations under the License. - + diff --git a/src/controller/data_model/controller-clusters.matter b/src/controller/data_model/controller-clusters.matter index 52f43c048aed14..ede6f1117ed37f 100644 --- a/src/controller/data_model/controller-clusters.matter +++ b/src/controller/data_model/controller-clusters.matter @@ -6483,7 +6483,7 @@ provisional cluster ServiceArea = 336 { struct LandmarkInfoStruct { LandmarkTag landmarkTag = 0; - nullable RelativePositionTag positionTag = 1; + nullable RelativePositionTag relativePositionTag = 1; } struct AreaInfoStruct { @@ -6494,7 +6494,7 @@ provisional cluster ServiceArea = 336 { struct AreaStruct { int32u areaID = 0; nullable int32u mapID = 1; - AreaInfoStruct areaDesc = 2; + AreaInfoStruct areaInfo = 2; } struct MapStruct { diff --git a/src/controller/java/generated/java/chip/devicecontroller/ChipStructs.java b/src/controller/java/generated/java/chip/devicecontroller/ChipStructs.java index 70e9ba6b1c6ef9..7ee8a88f80f64b 100644 --- a/src/controller/java/generated/java/chip/devicecontroller/ChipStructs.java +++ b/src/controller/java/generated/java/chip/devicecontroller/ChipStructs.java @@ -9294,22 +9294,22 @@ public String toString() { } public static class ServiceAreaClusterLandmarkInfoStruct { public Integer landmarkTag; - public @Nullable Integer positionTag; + public @Nullable Integer relativePositionTag; private static final long LANDMARK_TAG_ID = 0L; - private static final long POSITION_TAG_ID = 1L; + private static final long RELATIVE_POSITION_TAG_ID = 1L; public ServiceAreaClusterLandmarkInfoStruct( Integer landmarkTag, - @Nullable Integer positionTag + @Nullable Integer relativePositionTag ) { this.landmarkTag = landmarkTag; - this.positionTag = positionTag; + this.relativePositionTag = relativePositionTag; } public StructType encodeTlv() { ArrayList values = new ArrayList<>(); values.add(new StructElement(LANDMARK_TAG_ID, new UIntType(landmarkTag))); - values.add(new StructElement(POSITION_TAG_ID, positionTag != null ? new UIntType(positionTag) : new NullType())); + values.add(new StructElement(RELATIVE_POSITION_TAG_ID, relativePositionTag != null ? new UIntType(relativePositionTag) : new NullType())); return new StructType(values); } @@ -9319,23 +9319,23 @@ public static ServiceAreaClusterLandmarkInfoStruct decodeTlv(BaseTLVType tlvValu return null; } Integer landmarkTag = null; - @Nullable Integer positionTag = null; + @Nullable Integer relativePositionTag = null; for (StructElement element: ((StructType)tlvValue).value()) { if (element.contextTagNum() == LANDMARK_TAG_ID) { if (element.value(BaseTLVType.class).type() == TLVType.UInt) { UIntType castingValue = element.value(UIntType.class); landmarkTag = castingValue.value(Integer.class); } - } else if (element.contextTagNum() == POSITION_TAG_ID) { + } else if (element.contextTagNum() == RELATIVE_POSITION_TAG_ID) { if (element.value(BaseTLVType.class).type() == TLVType.UInt) { UIntType castingValue = element.value(UIntType.class); - positionTag = castingValue.value(Integer.class); + relativePositionTag = castingValue.value(Integer.class); } } } return new ServiceAreaClusterLandmarkInfoStruct( landmarkTag, - positionTag + relativePositionTag ); } @@ -9346,8 +9346,8 @@ public String toString() { output.append("\tlandmarkTag: "); output.append(landmarkTag); output.append("\n"); - output.append("\tpositionTag: "); - output.append(positionTag); + output.append("\trelativePositionTag: "); + output.append(relativePositionTag); output.append("\n"); output.append("}\n"); return output.toString(); @@ -9417,26 +9417,26 @@ public String toString() { public static class ServiceAreaClusterAreaStruct { public Long areaID; public @Nullable Long mapID; - public ChipStructs.ServiceAreaClusterAreaInfoStruct areaDesc; + public ChipStructs.ServiceAreaClusterAreaInfoStruct areaInfo; private static final long AREA_ID_ID = 0L; private static final long MAP_ID_ID = 1L; - private static final long AREA_DESC_ID = 2L; + private static final long AREA_INFO_ID = 2L; public ServiceAreaClusterAreaStruct( Long areaID, @Nullable Long mapID, - ChipStructs.ServiceAreaClusterAreaInfoStruct areaDesc + ChipStructs.ServiceAreaClusterAreaInfoStruct areaInfo ) { this.areaID = areaID; this.mapID = mapID; - this.areaDesc = areaDesc; + this.areaInfo = areaInfo; } public StructType encodeTlv() { ArrayList values = new ArrayList<>(); values.add(new StructElement(AREA_ID_ID, new UIntType(areaID))); values.add(new StructElement(MAP_ID_ID, mapID != null ? new UIntType(mapID) : new NullType())); - values.add(new StructElement(AREA_DESC_ID, areaDesc.encodeTlv())); + values.add(new StructElement(AREA_INFO_ID, areaInfo.encodeTlv())); return new StructType(values); } @@ -9447,7 +9447,7 @@ public static ServiceAreaClusterAreaStruct decodeTlv(BaseTLVType tlvValue) { } Long areaID = null; @Nullable Long mapID = null; - ChipStructs.ServiceAreaClusterAreaInfoStruct areaDesc = null; + ChipStructs.ServiceAreaClusterAreaInfoStruct areaInfo = null; for (StructElement element: ((StructType)tlvValue).value()) { if (element.contextTagNum() == AREA_ID_ID) { if (element.value(BaseTLVType.class).type() == TLVType.UInt) { @@ -9459,17 +9459,17 @@ public static ServiceAreaClusterAreaStruct decodeTlv(BaseTLVType tlvValue) { UIntType castingValue = element.value(UIntType.class); mapID = castingValue.value(Long.class); } - } else if (element.contextTagNum() == AREA_DESC_ID) { + } else if (element.contextTagNum() == AREA_INFO_ID) { if (element.value(BaseTLVType.class).type() == TLVType.Struct) { StructType castingValue = element.value(StructType.class); - areaDesc = ChipStructs.ServiceAreaClusterAreaInfoStruct.decodeTlv(castingValue); + areaInfo = ChipStructs.ServiceAreaClusterAreaInfoStruct.decodeTlv(castingValue); } } } return new ServiceAreaClusterAreaStruct( areaID, mapID, - areaDesc + areaInfo ); } @@ -9483,8 +9483,8 @@ public String toString() { output.append("\tmapID: "); output.append(mapID); output.append("\n"); - output.append("\tareaDesc: "); - output.append(areaDesc); + output.append("\tareaInfo: "); + output.append(areaInfo); output.append("\n"); output.append("}\n"); return output.toString(); diff --git a/src/controller/java/generated/java/chip/devicecontroller/cluster/structs/ServiceAreaClusterAreaStruct.kt b/src/controller/java/generated/java/chip/devicecontroller/cluster/structs/ServiceAreaClusterAreaStruct.kt index fc6405a96c2f7c..a5df845ac9c123 100644 --- a/src/controller/java/generated/java/chip/devicecontroller/cluster/structs/ServiceAreaClusterAreaStruct.kt +++ b/src/controller/java/generated/java/chip/devicecontroller/cluster/structs/ServiceAreaClusterAreaStruct.kt @@ -25,13 +25,13 @@ import matter.tlv.TlvWriter class ServiceAreaClusterAreaStruct( val areaID: ULong, val mapID: ULong?, - val areaDesc: ServiceAreaClusterAreaInfoStruct, + val areaInfo: ServiceAreaClusterAreaInfoStruct, ) { override fun toString(): String = buildString { append("ServiceAreaClusterAreaStruct {\n") append("\tareaID : $areaID\n") append("\tmapID : $mapID\n") - append("\tareaDesc : $areaDesc\n") + append("\tareaInfo : $areaInfo\n") append("}\n") } @@ -44,7 +44,7 @@ class ServiceAreaClusterAreaStruct( } else { putNull(ContextSpecificTag(TAG_MAP_ID)) } - areaDesc.toTlv(ContextSpecificTag(TAG_AREA_DESC), this) + areaInfo.toTlv(ContextSpecificTag(TAG_AREA_INFO), this) endStructure() } } @@ -52,7 +52,7 @@ class ServiceAreaClusterAreaStruct( companion object { private const val TAG_AREA_ID = 0 private const val TAG_MAP_ID = 1 - private const val TAG_AREA_DESC = 2 + private const val TAG_AREA_INFO = 2 fun fromTlv(tlvTag: Tag, tlvReader: TlvReader): ServiceAreaClusterAreaStruct { tlvReader.enterStructure(tlvTag) @@ -64,12 +64,12 @@ class ServiceAreaClusterAreaStruct( tlvReader.getNull(ContextSpecificTag(TAG_MAP_ID)) null } - val areaDesc = - ServiceAreaClusterAreaInfoStruct.fromTlv(ContextSpecificTag(TAG_AREA_DESC), tlvReader) + val areaInfo = + ServiceAreaClusterAreaInfoStruct.fromTlv(ContextSpecificTag(TAG_AREA_INFO), tlvReader) tlvReader.exitContainer() - return ServiceAreaClusterAreaStruct(areaID, mapID, areaDesc) + return ServiceAreaClusterAreaStruct(areaID, mapID, areaInfo) } } } diff --git a/src/controller/java/generated/java/chip/devicecontroller/cluster/structs/ServiceAreaClusterLandmarkInfoStruct.kt b/src/controller/java/generated/java/chip/devicecontroller/cluster/structs/ServiceAreaClusterLandmarkInfoStruct.kt index 04970798a28433..adf51e8e7c3f39 100644 --- a/src/controller/java/generated/java/chip/devicecontroller/cluster/structs/ServiceAreaClusterLandmarkInfoStruct.kt +++ b/src/controller/java/generated/java/chip/devicecontroller/cluster/structs/ServiceAreaClusterLandmarkInfoStruct.kt @@ -22,11 +22,11 @@ import matter.tlv.Tag import matter.tlv.TlvReader import matter.tlv.TlvWriter -class ServiceAreaClusterLandmarkInfoStruct(val landmarkTag: UInt, val positionTag: UInt?) { +class ServiceAreaClusterLandmarkInfoStruct(val landmarkTag: UInt, val relativePositionTag: UInt?) { override fun toString(): String = buildString { append("ServiceAreaClusterLandmarkInfoStruct {\n") append("\tlandmarkTag : $landmarkTag\n") - append("\tpositionTag : $positionTag\n") + append("\trelativePositionTag : $relativePositionTag\n") append("}\n") } @@ -34,10 +34,10 @@ class ServiceAreaClusterLandmarkInfoStruct(val landmarkTag: UInt, val positionTa tlvWriter.apply { startStructure(tlvTag) put(ContextSpecificTag(TAG_LANDMARK_TAG), landmarkTag) - if (positionTag != null) { - put(ContextSpecificTag(TAG_POSITION_TAG), positionTag) + if (relativePositionTag != null) { + put(ContextSpecificTag(TAG_RELATIVE_POSITION_TAG), relativePositionTag) } else { - putNull(ContextSpecificTag(TAG_POSITION_TAG)) + putNull(ContextSpecificTag(TAG_RELATIVE_POSITION_TAG)) } endStructure() } @@ -45,22 +45,22 @@ class ServiceAreaClusterLandmarkInfoStruct(val landmarkTag: UInt, val positionTa companion object { private const val TAG_LANDMARK_TAG = 0 - private const val TAG_POSITION_TAG = 1 + private const val TAG_RELATIVE_POSITION_TAG = 1 fun fromTlv(tlvTag: Tag, tlvReader: TlvReader): ServiceAreaClusterLandmarkInfoStruct { tlvReader.enterStructure(tlvTag) val landmarkTag = tlvReader.getUInt(ContextSpecificTag(TAG_LANDMARK_TAG)) - val positionTag = + val relativePositionTag = if (!tlvReader.isNull()) { - tlvReader.getUInt(ContextSpecificTag(TAG_POSITION_TAG)) + tlvReader.getUInt(ContextSpecificTag(TAG_RELATIVE_POSITION_TAG)) } else { - tlvReader.getNull(ContextSpecificTag(TAG_POSITION_TAG)) + tlvReader.getNull(ContextSpecificTag(TAG_RELATIVE_POSITION_TAG)) null } tlvReader.exitContainer() - return ServiceAreaClusterLandmarkInfoStruct(landmarkTag, positionTag) + return ServiceAreaClusterLandmarkInfoStruct(landmarkTag, relativePositionTag) } } } diff --git a/src/controller/java/generated/java/matter/controller/cluster/structs/ServiceAreaClusterAreaStruct.kt b/src/controller/java/generated/java/matter/controller/cluster/structs/ServiceAreaClusterAreaStruct.kt index d310fe5c315bf8..952623c2cf0215 100644 --- a/src/controller/java/generated/java/matter/controller/cluster/structs/ServiceAreaClusterAreaStruct.kt +++ b/src/controller/java/generated/java/matter/controller/cluster/structs/ServiceAreaClusterAreaStruct.kt @@ -25,13 +25,13 @@ import matter.tlv.TlvWriter class ServiceAreaClusterAreaStruct( val areaID: UInt, val mapID: UInt?, - val areaDesc: ServiceAreaClusterAreaInfoStruct, + val areaInfo: ServiceAreaClusterAreaInfoStruct, ) { override fun toString(): String = buildString { append("ServiceAreaClusterAreaStruct {\n") append("\tareaID : $areaID\n") append("\tmapID : $mapID\n") - append("\tareaDesc : $areaDesc\n") + append("\tareaInfo : $areaInfo\n") append("}\n") } @@ -44,7 +44,7 @@ class ServiceAreaClusterAreaStruct( } else { putNull(ContextSpecificTag(TAG_MAP_ID)) } - areaDesc.toTlv(ContextSpecificTag(TAG_AREA_DESC), this) + areaInfo.toTlv(ContextSpecificTag(TAG_AREA_INFO), this) endStructure() } } @@ -52,7 +52,7 @@ class ServiceAreaClusterAreaStruct( companion object { private const val TAG_AREA_ID = 0 private const val TAG_MAP_ID = 1 - private const val TAG_AREA_DESC = 2 + private const val TAG_AREA_INFO = 2 fun fromTlv(tlvTag: Tag, tlvReader: TlvReader): ServiceAreaClusterAreaStruct { tlvReader.enterStructure(tlvTag) @@ -64,12 +64,12 @@ class ServiceAreaClusterAreaStruct( tlvReader.getNull(ContextSpecificTag(TAG_MAP_ID)) null } - val areaDesc = - ServiceAreaClusterAreaInfoStruct.fromTlv(ContextSpecificTag(TAG_AREA_DESC), tlvReader) + val areaInfo = + ServiceAreaClusterAreaInfoStruct.fromTlv(ContextSpecificTag(TAG_AREA_INFO), tlvReader) tlvReader.exitContainer() - return ServiceAreaClusterAreaStruct(areaID, mapID, areaDesc) + return ServiceAreaClusterAreaStruct(areaID, mapID, areaInfo) } } } diff --git a/src/controller/java/generated/java/matter/controller/cluster/structs/ServiceAreaClusterLandmarkInfoStruct.kt b/src/controller/java/generated/java/matter/controller/cluster/structs/ServiceAreaClusterLandmarkInfoStruct.kt index 119667c339d88c..84d369e9a82498 100644 --- a/src/controller/java/generated/java/matter/controller/cluster/structs/ServiceAreaClusterLandmarkInfoStruct.kt +++ b/src/controller/java/generated/java/matter/controller/cluster/structs/ServiceAreaClusterLandmarkInfoStruct.kt @@ -22,11 +22,14 @@ import matter.tlv.Tag import matter.tlv.TlvReader import matter.tlv.TlvWriter -class ServiceAreaClusterLandmarkInfoStruct(val landmarkTag: UByte, val positionTag: UByte?) { +class ServiceAreaClusterLandmarkInfoStruct( + val landmarkTag: UByte, + val relativePositionTag: UByte?, +) { override fun toString(): String = buildString { append("ServiceAreaClusterLandmarkInfoStruct {\n") append("\tlandmarkTag : $landmarkTag\n") - append("\tpositionTag : $positionTag\n") + append("\trelativePositionTag : $relativePositionTag\n") append("}\n") } @@ -34,10 +37,10 @@ class ServiceAreaClusterLandmarkInfoStruct(val landmarkTag: UByte, val positionT tlvWriter.apply { startStructure(tlvTag) put(ContextSpecificTag(TAG_LANDMARK_TAG), landmarkTag) - if (positionTag != null) { - put(ContextSpecificTag(TAG_POSITION_TAG), positionTag) + if (relativePositionTag != null) { + put(ContextSpecificTag(TAG_RELATIVE_POSITION_TAG), relativePositionTag) } else { - putNull(ContextSpecificTag(TAG_POSITION_TAG)) + putNull(ContextSpecificTag(TAG_RELATIVE_POSITION_TAG)) } endStructure() } @@ -45,22 +48,22 @@ class ServiceAreaClusterLandmarkInfoStruct(val landmarkTag: UByte, val positionT companion object { private const val TAG_LANDMARK_TAG = 0 - private const val TAG_POSITION_TAG = 1 + private const val TAG_RELATIVE_POSITION_TAG = 1 fun fromTlv(tlvTag: Tag, tlvReader: TlvReader): ServiceAreaClusterLandmarkInfoStruct { tlvReader.enterStructure(tlvTag) val landmarkTag = tlvReader.getUByte(ContextSpecificTag(TAG_LANDMARK_TAG)) - val positionTag = + val relativePositionTag = if (!tlvReader.isNull()) { - tlvReader.getUByte(ContextSpecificTag(TAG_POSITION_TAG)) + tlvReader.getUByte(ContextSpecificTag(TAG_RELATIVE_POSITION_TAG)) } else { - tlvReader.getNull(ContextSpecificTag(TAG_POSITION_TAG)) + tlvReader.getNull(ContextSpecificTag(TAG_RELATIVE_POSITION_TAG)) null } tlvReader.exitContainer() - return ServiceAreaClusterLandmarkInfoStruct(landmarkTag, positionTag) + return ServiceAreaClusterLandmarkInfoStruct(landmarkTag, relativePositionTag) } } } diff --git a/src/controller/java/zap-generated/CHIPAttributeTLVValueDecoder.cpp b/src/controller/java/zap-generated/CHIPAttributeTLVValueDecoder.cpp index db775e5d52e5c6..cacd15683ab39b 100644 --- a/src/controller/java/zap-generated/CHIPAttributeTLVValueDecoder.cpp +++ b/src/controller/java/zap-generated/CHIPAttributeTLVValueDecoder.cpp @@ -28647,48 +28647,48 @@ jobject DecodeAttributeValue(const app::ConcreteAttributePath & aPath, TLV::TLVR newElement_0_mapIDCtorSignature.c_str(), jninewElement_0_mapID, newElement_0_mapID); } - jobject newElement_0_areaDesc; - jobject newElement_0_areaDesc_locationInfo; - if (entry_0.areaDesc.locationInfo.IsNull()) + jobject newElement_0_areaInfo; + jobject newElement_0_areaInfo_locationInfo; + if (entry_0.areaInfo.locationInfo.IsNull()) { - newElement_0_areaDesc_locationInfo = nullptr; + newElement_0_areaInfo_locationInfo = nullptr; } else { - jobject newElement_0_areaDesc_locationInfo_locationName; + jobject newElement_0_areaInfo_locationInfo_locationName; LogErrorOnFailure(chip::JniReferences::GetInstance().CharToStringUTF( - entry_0.areaDesc.locationInfo.Value().locationName, newElement_0_areaDesc_locationInfo_locationName)); - jobject newElement_0_areaDesc_locationInfo_floorNumber; - if (entry_0.areaDesc.locationInfo.Value().floorNumber.IsNull()) + entry_0.areaInfo.locationInfo.Value().locationName, newElement_0_areaInfo_locationInfo_locationName)); + jobject newElement_0_areaInfo_locationInfo_floorNumber; + if (entry_0.areaInfo.locationInfo.Value().floorNumber.IsNull()) { - newElement_0_areaDesc_locationInfo_floorNumber = nullptr; + newElement_0_areaInfo_locationInfo_floorNumber = nullptr; } else { - std::string newElement_0_areaDesc_locationInfo_floorNumberClassName = "java/lang/Integer"; - std::string newElement_0_areaDesc_locationInfo_floorNumberCtorSignature = "(I)V"; - jint jninewElement_0_areaDesc_locationInfo_floorNumber = - static_cast(entry_0.areaDesc.locationInfo.Value().floorNumber.Value()); + std::string newElement_0_areaInfo_locationInfo_floorNumberClassName = "java/lang/Integer"; + std::string newElement_0_areaInfo_locationInfo_floorNumberCtorSignature = "(I)V"; + jint jninewElement_0_areaInfo_locationInfo_floorNumber = + static_cast(entry_0.areaInfo.locationInfo.Value().floorNumber.Value()); chip::JniReferences::GetInstance().CreateBoxedObject( - newElement_0_areaDesc_locationInfo_floorNumberClassName.c_str(), - newElement_0_areaDesc_locationInfo_floorNumberCtorSignature.c_str(), - jninewElement_0_areaDesc_locationInfo_floorNumber, newElement_0_areaDesc_locationInfo_floorNumber); + newElement_0_areaInfo_locationInfo_floorNumberClassName.c_str(), + newElement_0_areaInfo_locationInfo_floorNumberCtorSignature.c_str(), + jninewElement_0_areaInfo_locationInfo_floorNumber, newElement_0_areaInfo_locationInfo_floorNumber); } - jobject newElement_0_areaDesc_locationInfo_areaType; - if (entry_0.areaDesc.locationInfo.Value().areaType.IsNull()) + jobject newElement_0_areaInfo_locationInfo_areaType; + if (entry_0.areaInfo.locationInfo.Value().areaType.IsNull()) { - newElement_0_areaDesc_locationInfo_areaType = nullptr; + newElement_0_areaInfo_locationInfo_areaType = nullptr; } else { - std::string newElement_0_areaDesc_locationInfo_areaTypeClassName = "java/lang/Integer"; - std::string newElement_0_areaDesc_locationInfo_areaTypeCtorSignature = "(I)V"; - jint jninewElement_0_areaDesc_locationInfo_areaType = - static_cast(entry_0.areaDesc.locationInfo.Value().areaType.Value()); + std::string newElement_0_areaInfo_locationInfo_areaTypeClassName = "java/lang/Integer"; + std::string newElement_0_areaInfo_locationInfo_areaTypeCtorSignature = "(I)V"; + jint jninewElement_0_areaInfo_locationInfo_areaType = + static_cast(entry_0.areaInfo.locationInfo.Value().areaType.Value()); chip::JniReferences::GetInstance().CreateBoxedObject( - newElement_0_areaDesc_locationInfo_areaTypeClassName.c_str(), - newElement_0_areaDesc_locationInfo_areaTypeCtorSignature.c_str(), - jninewElement_0_areaDesc_locationInfo_areaType, newElement_0_areaDesc_locationInfo_areaType); + newElement_0_areaInfo_locationInfo_areaTypeClassName.c_str(), + newElement_0_areaInfo_locationInfo_areaTypeCtorSignature.c_str(), + jninewElement_0_areaInfo_locationInfo_areaType, newElement_0_areaInfo_locationInfo_areaType); } jclass locationDescriptorStructStructClass_4; @@ -28711,42 +28711,43 @@ jobject DecodeAttributeValue(const app::ConcreteAttributePath & aPath, TLV::TLVR return nullptr; } - newElement_0_areaDesc_locationInfo = + newElement_0_areaInfo_locationInfo = env->NewObject(locationDescriptorStructStructClass_4, locationDescriptorStructStructCtor_4, - newElement_0_areaDesc_locationInfo_locationName, - newElement_0_areaDesc_locationInfo_floorNumber, newElement_0_areaDesc_locationInfo_areaType); + newElement_0_areaInfo_locationInfo_locationName, + newElement_0_areaInfo_locationInfo_floorNumber, newElement_0_areaInfo_locationInfo_areaType); } - jobject newElement_0_areaDesc_landmarkInfo; - if (entry_0.areaDesc.landmarkInfo.IsNull()) + jobject newElement_0_areaInfo_landmarkInfo; + if (entry_0.areaInfo.landmarkInfo.IsNull()) { - newElement_0_areaDesc_landmarkInfo = nullptr; + newElement_0_areaInfo_landmarkInfo = nullptr; } else { - jobject newElement_0_areaDesc_landmarkInfo_landmarkTag; - std::string newElement_0_areaDesc_landmarkInfo_landmarkTagClassName = "java/lang/Integer"; - std::string newElement_0_areaDesc_landmarkInfo_landmarkTagCtorSignature = "(I)V"; - jint jninewElement_0_areaDesc_landmarkInfo_landmarkTag = - static_cast(entry_0.areaDesc.landmarkInfo.Value().landmarkTag); + jobject newElement_0_areaInfo_landmarkInfo_landmarkTag; + std::string newElement_0_areaInfo_landmarkInfo_landmarkTagClassName = "java/lang/Integer"; + std::string newElement_0_areaInfo_landmarkInfo_landmarkTagCtorSignature = "(I)V"; + jint jninewElement_0_areaInfo_landmarkInfo_landmarkTag = + static_cast(entry_0.areaInfo.landmarkInfo.Value().landmarkTag); chip::JniReferences::GetInstance().CreateBoxedObject( - newElement_0_areaDesc_landmarkInfo_landmarkTagClassName.c_str(), - newElement_0_areaDesc_landmarkInfo_landmarkTagCtorSignature.c_str(), - jninewElement_0_areaDesc_landmarkInfo_landmarkTag, newElement_0_areaDesc_landmarkInfo_landmarkTag); - jobject newElement_0_areaDesc_landmarkInfo_positionTag; - if (entry_0.areaDesc.landmarkInfo.Value().positionTag.IsNull()) + newElement_0_areaInfo_landmarkInfo_landmarkTagClassName.c_str(), + newElement_0_areaInfo_landmarkInfo_landmarkTagCtorSignature.c_str(), + jninewElement_0_areaInfo_landmarkInfo_landmarkTag, newElement_0_areaInfo_landmarkInfo_landmarkTag); + jobject newElement_0_areaInfo_landmarkInfo_relativePositionTag; + if (entry_0.areaInfo.landmarkInfo.Value().relativePositionTag.IsNull()) { - newElement_0_areaDesc_landmarkInfo_positionTag = nullptr; + newElement_0_areaInfo_landmarkInfo_relativePositionTag = nullptr; } else { - std::string newElement_0_areaDesc_landmarkInfo_positionTagClassName = "java/lang/Integer"; - std::string newElement_0_areaDesc_landmarkInfo_positionTagCtorSignature = "(I)V"; - jint jninewElement_0_areaDesc_landmarkInfo_positionTag = - static_cast(entry_0.areaDesc.landmarkInfo.Value().positionTag.Value()); + std::string newElement_0_areaInfo_landmarkInfo_relativePositionTagClassName = "java/lang/Integer"; + std::string newElement_0_areaInfo_landmarkInfo_relativePositionTagCtorSignature = "(I)V"; + jint jninewElement_0_areaInfo_landmarkInfo_relativePositionTag = + static_cast(entry_0.areaInfo.landmarkInfo.Value().relativePositionTag.Value()); chip::JniReferences::GetInstance().CreateBoxedObject( - newElement_0_areaDesc_landmarkInfo_positionTagClassName.c_str(), - newElement_0_areaDesc_landmarkInfo_positionTagCtorSignature.c_str(), - jninewElement_0_areaDesc_landmarkInfo_positionTag, newElement_0_areaDesc_landmarkInfo_positionTag); + newElement_0_areaInfo_landmarkInfo_relativePositionTagClassName.c_str(), + newElement_0_areaInfo_landmarkInfo_relativePositionTagCtorSignature.c_str(), + jninewElement_0_areaInfo_landmarkInfo_relativePositionTag, + newElement_0_areaInfo_landmarkInfo_relativePositionTag); } jclass landmarkInfoStructStructClass_4; @@ -28769,9 +28770,9 @@ jobject DecodeAttributeValue(const app::ConcreteAttributePath & aPath, TLV::TLVR return nullptr; } - newElement_0_areaDesc_landmarkInfo = env->NewObject( + newElement_0_areaInfo_landmarkInfo = env->NewObject( landmarkInfoStructStructClass_4, landmarkInfoStructStructCtor_4, - newElement_0_areaDesc_landmarkInfo_landmarkTag, newElement_0_areaDesc_landmarkInfo_positionTag); + newElement_0_areaInfo_landmarkInfo_landmarkTag, newElement_0_areaInfo_landmarkInfo_relativePositionTag); } jclass areaInfoStructStructClass_2; @@ -28795,8 +28796,8 @@ jobject DecodeAttributeValue(const app::ConcreteAttributePath & aPath, TLV::TLVR return nullptr; } - newElement_0_areaDesc = env->NewObject(areaInfoStructStructClass_2, areaInfoStructStructCtor_2, - newElement_0_areaDesc_locationInfo, newElement_0_areaDesc_landmarkInfo); + newElement_0_areaInfo = env->NewObject(areaInfoStructStructClass_2, areaInfoStructStructCtor_2, + newElement_0_areaInfo_locationInfo, newElement_0_areaInfo_landmarkInfo); jclass areaStructStructClass_1; err = chip::JniReferences::GetInstance().GetLocalClassRef( @@ -28819,7 +28820,7 @@ jobject DecodeAttributeValue(const app::ConcreteAttributePath & aPath, TLV::TLVR } newElement_0 = env->NewObject(areaStructStructClass_1, areaStructStructCtor_1, newElement_0_areaID, - newElement_0_mapID, newElement_0_areaDesc); + newElement_0_mapID, newElement_0_areaInfo); chip::JniReferences::GetInstance().AddToList(value, newElement_0); } return value; diff --git a/src/controller/python/chip/clusters/Objects.py b/src/controller/python/chip/clusters/Objects.py index 624f42c8e80088..391765f3838e7b 100644 --- a/src/controller/python/chip/clusters/Objects.py +++ b/src/controller/python/chip/clusters/Objects.py @@ -31392,11 +31392,11 @@ def descriptor(cls) -> ClusterObjectDescriptor: return ClusterObjectDescriptor( Fields=[ ClusterObjectFieldDescriptor(Label="landmarkTag", Tag=0, Type=Globals.Enums.LandmarkTag), - ClusterObjectFieldDescriptor(Label="positionTag", Tag=1, Type=typing.Union[Nullable, Globals.Enums.RelativePositionTag]), + ClusterObjectFieldDescriptor(Label="relativePositionTag", Tag=1, Type=typing.Union[Nullable, Globals.Enums.RelativePositionTag]), ]) landmarkTag: 'Globals.Enums.LandmarkTag' = 0 - positionTag: 'typing.Union[Nullable, Globals.Enums.RelativePositionTag]' = NullValue + relativePositionTag: 'typing.Union[Nullable, Globals.Enums.RelativePositionTag]' = NullValue @dataclass class AreaInfoStruct(ClusterObject): @@ -31419,12 +31419,12 @@ def descriptor(cls) -> ClusterObjectDescriptor: Fields=[ ClusterObjectFieldDescriptor(Label="areaID", Tag=0, Type=uint), ClusterObjectFieldDescriptor(Label="mapID", Tag=1, Type=typing.Union[Nullable, uint]), - ClusterObjectFieldDescriptor(Label="areaDesc", Tag=2, Type=ServiceArea.Structs.AreaInfoStruct), + ClusterObjectFieldDescriptor(Label="areaInfo", Tag=2, Type=ServiceArea.Structs.AreaInfoStruct), ]) areaID: 'uint' = 0 mapID: 'typing.Union[Nullable, uint]' = NullValue - areaDesc: 'ServiceArea.Structs.AreaInfoStruct' = field(default_factory=lambda: ServiceArea.Structs.AreaInfoStruct()) + areaInfo: 'ServiceArea.Structs.AreaInfoStruct' = field(default_factory=lambda: ServiceArea.Structs.AreaInfoStruct()) @dataclass class MapStruct(ClusterObject): diff --git a/src/darwin/Framework/CHIP/zap-generated/MTRAttributeTLVValueDecoder.mm b/src/darwin/Framework/CHIP/zap-generated/MTRAttributeTLVValueDecoder.mm index 472a630d93df68..4b0e0bb96389b3 100644 --- a/src/darwin/Framework/CHIP/zap-generated/MTRAttributeTLVValueDecoder.mm +++ b/src/darwin/Framework/CHIP/zap-generated/MTRAttributeTLVValueDecoder.mm @@ -11197,37 +11197,37 @@ static id _Nullable DecodeAttributeValueForServiceAreaCluster(AttributeId aAttri } else { newElement_0.mapID = [NSNumber numberWithUnsignedInt:entry_0.mapID.Value()]; } - newElement_0.areaDesc = [MTRServiceAreaClusterAreaInfoStruct new]; - if (entry_0.areaDesc.locationInfo.IsNull()) { - newElement_0.areaDesc.locationInfo = nil; + newElement_0.areaInfo = [MTRServiceAreaClusterAreaInfoStruct new]; + if (entry_0.areaInfo.locationInfo.IsNull()) { + newElement_0.areaInfo.locationInfo = nil; } else { - newElement_0.areaDesc.locationInfo = [MTRDataTypeLocationDescriptorStruct new]; - newElement_0.areaDesc.locationInfo.locationName = AsString(entry_0.areaDesc.locationInfo.Value().locationName); - if (newElement_0.areaDesc.locationInfo.locationName == nil) { + newElement_0.areaInfo.locationInfo = [MTRDataTypeLocationDescriptorStruct new]; + newElement_0.areaInfo.locationInfo.locationName = AsString(entry_0.areaInfo.locationInfo.Value().locationName); + if (newElement_0.areaInfo.locationInfo.locationName == nil) { CHIP_ERROR err = CHIP_ERROR_INVALID_ARGUMENT; *aError = err; return nil; } - if (entry_0.areaDesc.locationInfo.Value().floorNumber.IsNull()) { - newElement_0.areaDesc.locationInfo.floorNumber = nil; + if (entry_0.areaInfo.locationInfo.Value().floorNumber.IsNull()) { + newElement_0.areaInfo.locationInfo.floorNumber = nil; } else { - newElement_0.areaDesc.locationInfo.floorNumber = [NSNumber numberWithShort:entry_0.areaDesc.locationInfo.Value().floorNumber.Value()]; + newElement_0.areaInfo.locationInfo.floorNumber = [NSNumber numberWithShort:entry_0.areaInfo.locationInfo.Value().floorNumber.Value()]; } - if (entry_0.areaDesc.locationInfo.Value().areaType.IsNull()) { - newElement_0.areaDesc.locationInfo.areaType = nil; + if (entry_0.areaInfo.locationInfo.Value().areaType.IsNull()) { + newElement_0.areaInfo.locationInfo.areaType = nil; } else { - newElement_0.areaDesc.locationInfo.areaType = [NSNumber numberWithUnsignedChar:chip::to_underlying(entry_0.areaDesc.locationInfo.Value().areaType.Value())]; + newElement_0.areaInfo.locationInfo.areaType = [NSNumber numberWithUnsignedChar:chip::to_underlying(entry_0.areaInfo.locationInfo.Value().areaType.Value())]; } } - if (entry_0.areaDesc.landmarkInfo.IsNull()) { - newElement_0.areaDesc.landmarkInfo = nil; + if (entry_0.areaInfo.landmarkInfo.IsNull()) { + newElement_0.areaInfo.landmarkInfo = nil; } else { - newElement_0.areaDesc.landmarkInfo = [MTRServiceAreaClusterLandmarkInfoStruct new]; - newElement_0.areaDesc.landmarkInfo.landmarkTag = [NSNumber numberWithUnsignedChar:chip::to_underlying(entry_0.areaDesc.landmarkInfo.Value().landmarkTag)]; - if (entry_0.areaDesc.landmarkInfo.Value().positionTag.IsNull()) { - newElement_0.areaDesc.landmarkInfo.positionTag = nil; + newElement_0.areaInfo.landmarkInfo = [MTRServiceAreaClusterLandmarkInfoStruct new]; + newElement_0.areaInfo.landmarkInfo.landmarkTag = [NSNumber numberWithUnsignedChar:chip::to_underlying(entry_0.areaInfo.landmarkInfo.Value().landmarkTag)]; + if (entry_0.areaInfo.landmarkInfo.Value().relativePositionTag.IsNull()) { + newElement_0.areaInfo.landmarkInfo.relativePositionTag = nil; } else { - newElement_0.areaDesc.landmarkInfo.positionTag = [NSNumber numberWithUnsignedChar:chip::to_underlying(entry_0.areaDesc.landmarkInfo.Value().positionTag.Value())]; + newElement_0.areaInfo.landmarkInfo.relativePositionTag = [NSNumber numberWithUnsignedChar:chip::to_underlying(entry_0.areaInfo.landmarkInfo.Value().relativePositionTag.Value())]; } } [array_0 addObject:newElement_0]; diff --git a/src/darwin/Framework/CHIP/zap-generated/MTRStructsObjc.h b/src/darwin/Framework/CHIP/zap-generated/MTRStructsObjc.h index 35df5cc3e32495..fac8e1bdf7afe8 100644 --- a/src/darwin/Framework/CHIP/zap-generated/MTRStructsObjc.h +++ b/src/darwin/Framework/CHIP/zap-generated/MTRStructsObjc.h @@ -1615,7 +1615,7 @@ MTR_AVAILABLE(ios(16.1), macos(13.0), watchos(9.1), tvos(16.1)) MTR_PROVISIONALLY_AVAILABLE @interface MTRServiceAreaClusterLandmarkInfoStruct : NSObject @property (nonatomic, copy) NSNumber * _Nonnull landmarkTag MTR_PROVISIONALLY_AVAILABLE; -@property (nonatomic, copy) NSNumber * _Nullable positionTag MTR_PROVISIONALLY_AVAILABLE; +@property (nonatomic, copy) NSNumber * _Nullable relativePositionTag MTR_PROVISIONALLY_AVAILABLE; @end MTR_PROVISIONALLY_AVAILABLE @@ -1628,7 +1628,7 @@ MTR_PROVISIONALLY_AVAILABLE @interface MTRServiceAreaClusterAreaStruct : NSObject @property (nonatomic, copy) NSNumber * _Nonnull areaID MTR_PROVISIONALLY_AVAILABLE; @property (nonatomic, copy) NSNumber * _Nullable mapID MTR_PROVISIONALLY_AVAILABLE; -@property (nonatomic, copy) MTRServiceAreaClusterAreaInfoStruct * _Nonnull areaDesc MTR_PROVISIONALLY_AVAILABLE; +@property (nonatomic, copy) MTRServiceAreaClusterAreaInfoStruct * _Nonnull areaInfo MTR_PROVISIONALLY_AVAILABLE; @end MTR_PROVISIONALLY_AVAILABLE diff --git a/src/darwin/Framework/CHIP/zap-generated/MTRStructsObjc.mm b/src/darwin/Framework/CHIP/zap-generated/MTRStructsObjc.mm index 6c3fc19e9b68bf..f24ca05abfb4eb 100644 --- a/src/darwin/Framework/CHIP/zap-generated/MTRStructsObjc.mm +++ b/src/darwin/Framework/CHIP/zap-generated/MTRStructsObjc.mm @@ -6739,7 +6739,7 @@ - (instancetype)init _landmarkTag = @(0); - _positionTag = nil; + _relativePositionTag = nil; } return self; } @@ -6749,14 +6749,14 @@ - (id)copyWithZone:(NSZone * _Nullable)zone auto other = [[MTRServiceAreaClusterLandmarkInfoStruct alloc] init]; other.landmarkTag = self.landmarkTag; - other.positionTag = self.positionTag; + other.relativePositionTag = self.relativePositionTag; return other; } - (NSString *)description { - NSString * descriptionString = [NSString stringWithFormat:@"<%@: landmarkTag:%@; positionTag:%@; >", NSStringFromClass([self class]), _landmarkTag, _positionTag]; + NSString * descriptionString = [NSString stringWithFormat:@"<%@: landmarkTag:%@; relativePositionTag:%@; >", NSStringFromClass([self class]), _landmarkTag, _relativePositionTag]; return descriptionString; } @@ -6801,7 +6801,7 @@ - (instancetype)init _mapID = nil; - _areaDesc = [MTRServiceAreaClusterAreaInfoStruct new]; + _areaInfo = [MTRServiceAreaClusterAreaInfoStruct new]; } return self; } @@ -6812,14 +6812,14 @@ - (id)copyWithZone:(NSZone * _Nullable)zone other.areaID = self.areaID; other.mapID = self.mapID; - other.areaDesc = self.areaDesc; + other.areaInfo = self.areaInfo; return other; } - (NSString *)description { - NSString * descriptionString = [NSString stringWithFormat:@"<%@: areaID:%@; mapID:%@; areaDesc:%@; >", NSStringFromClass([self class]), _areaID, _mapID, _areaDesc]; + NSString * descriptionString = [NSString stringWithFormat:@"<%@: areaID:%@; mapID:%@; areaInfo:%@; >", NSStringFromClass([self class]), _areaID, _mapID, _areaInfo]; return descriptionString; } diff --git a/src/python_testing/TC_SEAR_1_2.py b/src/python_testing/TC_SEAR_1_2.py index 083533277a5e6d..c21e831464557e 100644 --- a/src/python_testing/TC_SEAR_1_2.py +++ b/src/python_testing/TC_SEAR_1_2.py @@ -80,7 +80,7 @@ async def read_and_validate_supported_areas(self, step): asserts.assert_less_equal(len(supported_areas), 255, "SupportedAreas should have max 255 entries") areaid_list = [] - areadesc_s = set() + areainfo_s = set() for a in supported_areas: asserts.assert_true(a.areaID not in areaid_list, "SupportedAreas must have unique AreaID values!") @@ -91,27 +91,27 @@ async def read_and_validate_supported_areas(self, step): f"SupportedAreas entry with AreaID({a.areaID}) should not have null MapID") asserts.assert_true(a.mapID in self.mapid_list, f"SupportedAreas entry with AreaID({a.areaID}) has unknown MapID({a.mapID})") - k = f"mapID:{a.mapID} areaDesc:{a.areaDesc}" - asserts.assert_true(k not in areadesc_s, - f"SupportedAreas must have unique MapID({a.mapID}) + AreaDesc({a.areaDesc}) values!") - areadesc_s.add(k) + k = f"mapID:{a.mapID} areaInfo:{a.areaInfo}" + asserts.assert_true(k not in areainfo_s, + f"SupportedAreas must have unique MapID({a.mapID}) + AreaInfo({a.areaInfo}) values!") + areainfo_s.add(k) else: # empty SupportedMaps asserts.assert_is(a.mapID, NullValue, f"SupportedAreas entry with AreaID({a.areaID}) should have null MapID") - k = f"areaDesc:{a.areaDesc}" - asserts.assert_true(k not in areadesc_s, f"SupportedAreas must have unique AreaDesc({a.areaDesc}) values!") - areadesc_s.add(k) + k = f"areaInfo:{a.areaInfo}" + asserts.assert_true(k not in areainfo_s, f"SupportedAreas must have unique AreaInfo({a.areaInfo}) values!") + areainfo_s.add(k) - if a.areaDesc.locationInfo is NullValue and a.areaDesc.landmarkInfo is NullValue: + if a.areaInfo.locationInfo is NullValue and a.areaInfo.landmarkInfo is NullValue: asserts.assert_true( f"SupportedAreas entry with AreaID({a.areaID}) should not have null LocationInfo and null LandmarkInfo") - if a.areaDesc.landmarkInfo is not NullValue: - asserts.assert_true(a.areaDesc.landmarkInfo.landmarkTag <= self.MAX_LANDMARK_ID, - f"SupportedAreas entry with AreaID({a.areaID}) has invalid LandmarkTag({a.areaDesc.landmarkInfo.landmarkTag})") - asserts.assert_true(a.areaDesc.landmarkInfo.positionTag is NullValue or a - .areaDesc.landmarkInfo.positionTag in range(0, self.MAX_RELPOS_ID), - f"SupportedAreas entry with AreaID({a.areaID}) has invalid PositionTag({a.areaDesc.landmarkInfo.positionTag})") + if a.areaInfo.landmarkInfo is not NullValue: + asserts.assert_true(a.areaInfo.landmarkInfo.landmarkTag <= self.MAX_LANDMARK_ID, + f"SupportedAreas entry with AreaID({a.areaID}) has invalid LandmarkTag({a.areaInfo.landmarkInfo.landmarkTag})") + asserts.assert_true(a.areaInfo.landmarkInfo.relativePositionTag is NullValue or a + .areaInfo.landmarkInfo.relativePositionTag in range(0, self.MAX_RELPOS_ID), + f"SupportedAreas entry with AreaID({a.areaID}) has invalid RelativePositionTag({a.areaInfo.landmarkInfo.relativePositionTag})") # save so other methods can use this if needed self.areaid_list = areaid_list diff --git a/zzz_generated/app-common/app-common/zap-generated/cluster-objects.cpp b/zzz_generated/app-common/app-common/zap-generated/cluster-objects.cpp index 20fdd126514076..c2a25ed9477c2e 100644 --- a/zzz_generated/app-common/app-common/zap-generated/cluster-objects.cpp +++ b/zzz_generated/app-common/app-common/zap-generated/cluster-objects.cpp @@ -20457,7 +20457,7 @@ CHIP_ERROR Type::Encode(TLV::TLVWriter & aWriter, TLV::Tag aTag) const { DataModel::WrappedStructEncoder encoder{ aWriter, aTag }; encoder.Encode(to_underlying(Fields::kLandmarkTag), landmarkTag); - encoder.Encode(to_underlying(Fields::kPositionTag), positionTag); + encoder.Encode(to_underlying(Fields::kRelativePositionTag), relativePositionTag); return encoder.Finalize(); } @@ -20479,9 +20479,9 @@ CHIP_ERROR DecodableType::Decode(TLV::TLVReader & reader) { err = DataModel::Decode(reader, landmarkTag); } - else if (__context_tag == to_underlying(Fields::kPositionTag)) + else if (__context_tag == to_underlying(Fields::kRelativePositionTag)) { - err = DataModel::Decode(reader, positionTag); + err = DataModel::Decode(reader, relativePositionTag); } else { @@ -20540,7 +20540,7 @@ CHIP_ERROR Type::Encode(TLV::TLVWriter & aWriter, TLV::Tag aTag) const DataModel::WrappedStructEncoder encoder{ aWriter, aTag }; encoder.Encode(to_underlying(Fields::kAreaID), areaID); encoder.Encode(to_underlying(Fields::kMapID), mapID); - encoder.Encode(to_underlying(Fields::kAreaDesc), areaDesc); + encoder.Encode(to_underlying(Fields::kAreaInfo), areaInfo); return encoder.Finalize(); } @@ -20566,9 +20566,9 @@ CHIP_ERROR DecodableType::Decode(TLV::TLVReader & reader) { err = DataModel::Decode(reader, mapID); } - else if (__context_tag == to_underlying(Fields::kAreaDesc)) + else if (__context_tag == to_underlying(Fields::kAreaInfo)) { - err = DataModel::Decode(reader, areaDesc); + err = DataModel::Decode(reader, areaInfo); } else { diff --git a/zzz_generated/app-common/app-common/zap-generated/cluster-objects.h b/zzz_generated/app-common/app-common/zap-generated/cluster-objects.h index c2c1697ce271f3..fc62ada8bd3fcb 100644 --- a/zzz_generated/app-common/app-common/zap-generated/cluster-objects.h +++ b/zzz_generated/app-common/app-common/zap-generated/cluster-objects.h @@ -28428,15 +28428,15 @@ namespace Structs { namespace LandmarkInfoStruct { enum class Fields : uint8_t { - kLandmarkTag = 0, - kPositionTag = 1, + kLandmarkTag = 0, + kRelativePositionTag = 1, }; struct Type { public: Globals::LandmarkTag landmarkTag = static_cast(0); - DataModel::Nullable positionTag; + DataModel::Nullable relativePositionTag; CHIP_ERROR Decode(TLV::TLVReader & reader); @@ -28476,7 +28476,7 @@ enum class Fields : uint8_t { kAreaID = 0, kMapID = 1, - kAreaDesc = 2, + kAreaInfo = 2, }; struct Type @@ -28484,7 +28484,7 @@ struct Type public: uint32_t areaID = static_cast(0); DataModel::Nullable mapID; - Structs::AreaInfoStruct::Type areaDesc; + Structs::AreaInfoStruct::Type areaInfo; CHIP_ERROR Decode(TLV::TLVReader & reader); diff --git a/zzz_generated/chip-tool/zap-generated/cluster/ComplexArgumentParser.cpp b/zzz_generated/chip-tool/zap-generated/cluster/ComplexArgumentParser.cpp index 202a6efcb06a01..9a1062c23b40a0 100644 --- a/zzz_generated/chip-tool/zap-generated/cluster/ComplexArgumentParser.cpp +++ b/zzz_generated/chip-tool/zap-generated/cluster/ComplexArgumentParser.cpp @@ -4052,17 +4052,17 @@ CHIP_ERROR ComplexArgumentParser::Setup(const char * label, ReturnErrorOnFailure( ComplexArgumentParser::EnsureMemberExist("LandmarkInfoStruct.landmarkTag", "landmarkTag", value.isMember("landmarkTag"))); - ReturnErrorOnFailure( - ComplexArgumentParser::EnsureMemberExist("LandmarkInfoStruct.positionTag", "positionTag", value.isMember("positionTag"))); + ReturnErrorOnFailure(ComplexArgumentParser::EnsureMemberExist("LandmarkInfoStruct.relativePositionTag", "relativePositionTag", + value.isMember("relativePositionTag"))); char labelWithMember[kMaxLabelLength]; snprintf(labelWithMember, sizeof(labelWithMember), "%s.%s", label, "landmarkTag"); ReturnErrorOnFailure(ComplexArgumentParser::Setup(labelWithMember, request.landmarkTag, value["landmarkTag"])); valueCopy.removeMember("landmarkTag"); - snprintf(labelWithMember, sizeof(labelWithMember), "%s.%s", label, "positionTag"); - ReturnErrorOnFailure(ComplexArgumentParser::Setup(labelWithMember, request.positionTag, value["positionTag"])); - valueCopy.removeMember("positionTag"); + snprintf(labelWithMember, sizeof(labelWithMember), "%s.%s", label, "relativePositionTag"); + ReturnErrorOnFailure(ComplexArgumentParser::Setup(labelWithMember, request.relativePositionTag, value["relativePositionTag"])); + valueCopy.removeMember("relativePositionTag"); return ComplexArgumentParser::EnsureNoMembersRemaining(label, valueCopy); } @@ -4070,7 +4070,7 @@ CHIP_ERROR ComplexArgumentParser::Setup(const char * label, void ComplexArgumentParser::Finalize(chip::app::Clusters::ServiceArea::Structs::LandmarkInfoStruct::Type & request) { ComplexArgumentParser::Finalize(request.landmarkTag); - ComplexArgumentParser::Finalize(request.positionTag); + ComplexArgumentParser::Finalize(request.relativePositionTag); } CHIP_ERROR ComplexArgumentParser::Setup(const char * label, @@ -4115,7 +4115,7 @@ CHIP_ERROR ComplexArgumentParser::Setup(const char * label, chip::app::Clusters: ReturnErrorOnFailure(ComplexArgumentParser::EnsureMemberExist("AreaStruct.areaID", "areaID", value.isMember("areaID"))); ReturnErrorOnFailure(ComplexArgumentParser::EnsureMemberExist("AreaStruct.mapID", "mapID", value.isMember("mapID"))); - ReturnErrorOnFailure(ComplexArgumentParser::EnsureMemberExist("AreaStruct.areaDesc", "areaDesc", value.isMember("areaDesc"))); + ReturnErrorOnFailure(ComplexArgumentParser::EnsureMemberExist("AreaStruct.areaInfo", "areaInfo", value.isMember("areaInfo"))); char labelWithMember[kMaxLabelLength]; snprintf(labelWithMember, sizeof(labelWithMember), "%s.%s", label, "areaID"); @@ -4126,9 +4126,9 @@ CHIP_ERROR ComplexArgumentParser::Setup(const char * label, chip::app::Clusters: ReturnErrorOnFailure(ComplexArgumentParser::Setup(labelWithMember, request.mapID, value["mapID"])); valueCopy.removeMember("mapID"); - snprintf(labelWithMember, sizeof(labelWithMember), "%s.%s", label, "areaDesc"); - ReturnErrorOnFailure(ComplexArgumentParser::Setup(labelWithMember, request.areaDesc, value["areaDesc"])); - valueCopy.removeMember("areaDesc"); + snprintf(labelWithMember, sizeof(labelWithMember), "%s.%s", label, "areaInfo"); + ReturnErrorOnFailure(ComplexArgumentParser::Setup(labelWithMember, request.areaInfo, value["areaInfo"])); + valueCopy.removeMember("areaInfo"); return ComplexArgumentParser::EnsureNoMembersRemaining(label, valueCopy); } @@ -4137,7 +4137,7 @@ void ComplexArgumentParser::Finalize(chip::app::Clusters::ServiceArea::Structs:: { ComplexArgumentParser::Finalize(request.areaID); ComplexArgumentParser::Finalize(request.mapID); - ComplexArgumentParser::Finalize(request.areaDesc); + ComplexArgumentParser::Finalize(request.areaInfo); } CHIP_ERROR ComplexArgumentParser::Setup(const char * label, chip::app::Clusters::ServiceArea::Structs::MapStruct::Type & request, diff --git a/zzz_generated/chip-tool/zap-generated/cluster/logging/DataModelLogger.cpp b/zzz_generated/chip-tool/zap-generated/cluster/logging/DataModelLogger.cpp index 3d40f3897e852b..16248a7ca4e157 100644 --- a/zzz_generated/chip-tool/zap-generated/cluster/logging/DataModelLogger.cpp +++ b/zzz_generated/chip-tool/zap-generated/cluster/logging/DataModelLogger.cpp @@ -3586,10 +3586,10 @@ CHIP_ERROR DataModelLogger::LogValue(const char * label, size_t indent, } } { - CHIP_ERROR err = LogValue("PositionTag", indent + 1, value.positionTag); + CHIP_ERROR err = LogValue("RelativePositionTag", indent + 1, value.relativePositionTag); if (err != CHIP_NO_ERROR) { - DataModelLogger::LogString(indent + 1, "Struct truncated due to invalid value for 'PositionTag'"); + DataModelLogger::LogString(indent + 1, "Struct truncated due to invalid value for 'RelativePositionTag'"); return err; } } @@ -3644,10 +3644,10 @@ CHIP_ERROR DataModelLogger::LogValue(const char * label, size_t indent, } } { - CHIP_ERROR err = LogValue("AreaDesc", indent + 1, value.areaDesc); + CHIP_ERROR err = LogValue("AreaInfo", indent + 1, value.areaInfo); if (err != CHIP_NO_ERROR) { - DataModelLogger::LogString(indent + 1, "Struct truncated due to invalid value for 'AreaDesc'"); + DataModelLogger::LogString(indent + 1, "Struct truncated due to invalid value for 'AreaInfo'"); return err; } } From bbd21f496615539fdea1f4fb9c11fef32cb2f74a Mon Sep 17 00:00:00 2001 From: mthiesc Date: Fri, 23 Aug 2024 00:59:53 +0200 Subject: [PATCH 08/11] AccountLogin Login/Logout command support (#34162) * AccountLogin Login/Logout command support [Problem] * ContentAppPlatform should send AccountLogin::Login command after successfull commissioning if user was shown setupPIN prompt. * Handling Login/Logout commands is currently not implemented in the Android. [Solution] * Call AccountLoginManager::HandleLogin if commissioning succeeded successfully with setupPIN prompt flow. * Implement HandleLogin (and HandleLogout) for Android AccountLoginManager using the ContentAppCommandDelegate. [Testing] WIP * HandleLogin in ContentAppPlatform::ManageClientAccess * Cache rotating ID in OnUserDirectedCommissioningRequest * Restyled by google-java-format * Restyled by clang-format * Update content app * Restyled by whitespace * Restyled by clang-format * Update response * Update method name * Restyled by google-java-format * Update src/app/app-platform/ContentApp.h Co-authored-by: chrisdecenzo <61757564+chrisdecenzo@users.noreply.github.com> * Update content app platform validation * Update validation logic * Update logic * Update responses * Restyled by clang-format * Simplify logic * Restyled by whitespace * clean up * Update code * Update content app with dynamic pin code * Restyled by google-java-format * Remove getRotatingIdSpan class methods * Restyled by clang-format --------- Co-authored-by: Restyled.io Co-authored-by: Lazar Kovacic Co-authored-by: chrisdecenzo <61757564+chrisdecenzo@users.noreply.github.com> --- .../contentapp/CommandResponseHolder.java | 10 +++ .../receiver/MatterCommandReceiver.java | 17 ++++ .../account-login/AccountLoginManager.cpp | 87 ++++++++++++++++--- examples/tv-app/android/java/AppImpl.cpp | 2 +- .../java/ContentAppCommandDelegate.cpp | 66 +++++++++----- .../android/java/ContentAppCommandDelegate.h | 2 + examples/tv-app/android/java/TVApp-JNI.cpp | 35 +++++--- examples/tv-app/tv-common/src/AppTv.cpp | 37 +++++--- src/app/app-platform/ContentApp.cpp | 13 ++- src/app/app-platform/ContentApp.h | 3 +- src/app/app-platform/ContentAppPlatform.cpp | 26 +++++- src/app/app-platform/ContentAppPlatform.h | 6 +- .../CommissionerDiscoveryController.cpp | 11 ++- .../CommissionerDiscoveryController.h | 7 +- 14 files changed, 254 insertions(+), 68 deletions(-) diff --git a/examples/tv-app/android/App/content-app/src/main/java/com/example/contentapp/CommandResponseHolder.java b/examples/tv-app/android/App/content-app/src/main/java/com/example/contentapp/CommandResponseHolder.java index 61ee303b408587..fa49afc312d383 100644 --- a/examples/tv-app/android/App/content-app/src/main/java/com/example/contentapp/CommandResponseHolder.java +++ b/examples/tv-app/android/App/content-app/src/main/java/com/example/contentapp/CommandResponseHolder.java @@ -31,6 +31,16 @@ private CommandResponseHolder() { Clusters.AccountLogin.Id, Clusters.AccountLogin.Commands.GetSetupPIN.ID, "{\"0\":\"20202021\"}"); + setResponseValue( + Clusters.AccountLogin.Id, + Clusters.AccountLogin.Commands.Login.ID, + // 0 is for success, you can return 1 for failure + "{\"Status\":0}"); + setResponseValue( + Clusters.AccountLogin.Id, + Clusters.AccountLogin.Commands.Logout.ID, + // 0 is for success, you can return 1 for failure + "{\"Status\":0}"); }; public static CommandResponseHolder getInstance() { diff --git a/examples/tv-app/android/App/content-app/src/main/java/com/example/contentapp/receiver/MatterCommandReceiver.java b/examples/tv-app/android/App/content-app/src/main/java/com/example/contentapp/receiver/MatterCommandReceiver.java index 6a4eb77cb4be3b..7134691392e6aa 100644 --- a/examples/tv-app/android/App/content-app/src/main/java/com/example/contentapp/receiver/MatterCommandReceiver.java +++ b/examples/tv-app/android/App/content-app/src/main/java/com/example/contentapp/receiver/MatterCommandReceiver.java @@ -4,11 +4,13 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; +import android.provider.Settings; import android.util.Log; import com.example.contentapp.AttributeHolder; import com.example.contentapp.CommandResponseHolder; import com.example.contentapp.MainActivity; import com.example.contentapp.matter.MatterAgentClient; +import com.matter.tv.app.api.Clusters; import com.matter.tv.app.api.MatterIntentConstants; public class MatterCommandReceiver extends BroadcastReceiver { @@ -44,6 +46,21 @@ public void onReceive(Context context, Intent intent) { .append(command) .toString(); Log.d(TAG, message); + + int pinCode = + Settings.Secure.getInt(context.getContentResolver(), "matter_pin_code", 20202021); + Log.d(TAG, "Retrieved pin code:" + pinCode); + + CommandResponseHolder.getInstance() + .setResponseValue( + Clusters.AccountLogin.Id, + Clusters.AccountLogin.Commands.GetSetupPIN.ID, + "{\"" + + Clusters.AccountLogin.Commands.GetSetupPINResponse.Fields.SetupPIN + + "\":\"" + + pinCode + + "\"}"); + String response = CommandResponseHolder.getInstance().getCommandResponse(clusterId, commandId); diff --git a/examples/tv-app/android/include/account-login/AccountLoginManager.cpp b/examples/tv-app/android/include/account-login/AccountLoginManager.cpp index 12e6ba44dfb220..2aa0b313b0eda1 100644 --- a/examples/tv-app/android/include/account-login/AccountLoginManager.cpp +++ b/examples/tv-app/android/include/account-login/AccountLoginManager.cpp @@ -24,38 +24,105 @@ #include using namespace std; +using namespace chip::app::Clusters; using namespace chip::app::Clusters::AccountLogin; using Status = chip::Protocols::InteractionModel::Status; +namespace { + +const auto loginTempAccountIdentifierFieldId = + to_string(chip::to_underlying(AccountLogin::Commands::Login::Fields::kTempAccountIdentifier)); +const auto loginSetupPINFieldId = to_string(chip::to_underlying(AccountLogin::Commands::Login::Fields::kSetupPIN)); +const auto loginNodeFieldId = to_string(chip::to_underlying(AccountLogin::Commands::Login::Fields::kNode)); +const auto logoutNodeFieldId = to_string(chip::to_underlying(AccountLogin::Commands::Logout::Fields::kNode)); + +string charSpanToString(const CharSpan & charSpan) +{ + return { charSpan.data(), charSpan.size() }; +} + +std::string serializeLoginCommand(AccountLogin::Commands::Login::Type cmd) +{ + return R"({")" + loginTempAccountIdentifierFieldId + R"(":")" + charSpanToString(cmd.tempAccountIdentifier) + R"(",)" + R"(")" + + loginSetupPINFieldId + R"(":")" + charSpanToString(cmd.setupPIN) + R"(",)" + R"(")" + loginNodeFieldId + R"(":")" + + to_string(cmd.node.Value()) + R"("})"; +} + +std::string serializeLogoutCommand(AccountLogin::Commands::Logout::Type cmd) +{ + return R"({")" + logoutNodeFieldId + R"(":")" + to_string(cmd.node.Value()) + R"("})"; +} + +} // namespace + AccountLoginManager::AccountLoginManager(ContentAppCommandDelegate * commandDelegate, const char * setupPin) : mCommandDelegate(commandDelegate) { CopyString(mSetupPin, sizeof(mSetupPin), setupPin); } -bool AccountLoginManager::HandleLogin(const CharSpan & tempAccountIdentifier, const CharSpan & setupPin, +bool AccountLoginManager::HandleLogin(const CharSpan & tempAccountIdentifier, const CharSpan & setupPIN, const chip::Optional & nodeId) { ChipLogProgress(DeviceLayer, "AccountLoginManager::HandleLogin called for endpoint %d", mEndpointId); - string tempAccountIdentifierString(tempAccountIdentifier.data(), tempAccountIdentifier.size()); - string setupPinString(setupPin.data(), setupPin.size()); - if (strcmp(mSetupPin, setupPinString.c_str()) == 0) + if (mCommandDelegate == nullptr) { - ChipLogProgress(Zcl, "AccountLoginManager::HandleLogin success"); - return true; + ChipLogError(Zcl, "CommandDelegate not found"); + return false; } - else + + if (tempAccountIdentifier.empty() || setupPIN.empty() || !nodeId.HasValue()) { - ChipLogProgress(Zcl, "AccountLoginManager::HandleLogin failed expected pin %s", mSetupPin); + ChipLogError(Zcl, "Invalid parameters"); return false; } + + Json::Value response; + bool commandHandled = true; + AccountLogin::Commands::Login::Type cmd = { tempAccountIdentifier, setupPIN, nodeId }; + + auto status = mCommandDelegate->InvokeCommand(mEndpointId, AccountLogin::Id, AccountLogin::Commands::Login::Id, + serializeLoginCommand(cmd), commandHandled, response); + if (status == Status::Success) + { + // Format status response to verify that response is non-failure. + status = mCommandDelegate->FormatStatusResponse(response); + } + ChipLogProgress(Zcl, "AccountLoginManager::HandleLogin command returned with status: %d", chip::to_underlying(status)); + return status == chip::Protocols::InteractionModel::Status::Success; } bool AccountLoginManager::HandleLogout(const chip::Optional & nodeId) { - // TODO: Insert your code here to send logout request - return true; + ChipLogProgress(DeviceLayer, "AccountLoginManager::HandleLogout called for endpoint %d", mEndpointId); + + if (mCommandDelegate == nullptr) + { + ChipLogError(Zcl, "CommandDelegate not found"); + return false; + } + + if (!nodeId.HasValue()) + { + ChipLogError(Zcl, "Invalid parameters"); + return false; + } + + Json::Value response; + bool commandHandled = true; + AccountLogin::Commands::Logout::Type cmd = { nodeId }; + + auto status = mCommandDelegate->InvokeCommand(mEndpointId, AccountLogin::Id, AccountLogin::Commands::Logout::Id, + serializeLogoutCommand(cmd), commandHandled, response); + + if (status == Status::Success) + { + // Format status response to verify that response is non-failure. + status = mCommandDelegate->FormatStatusResponse(response); + } + ChipLogProgress(Zcl, "AccountLoginManager::HandleLogout command returned with status: %d", chip::to_underlying(status)); + return status == chip::Protocols::InteractionModel::Status::Success; } void AccountLoginManager::HandleGetSetupPin(CommandResponseHelper & helper, diff --git a/examples/tv-app/android/java/AppImpl.cpp b/examples/tv-app/android/java/AppImpl.cpp index d7da8ab6661a46..d33949a5d382d9 100644 --- a/examples/tv-app/android/java/AppImpl.cpp +++ b/examples/tv-app/android/java/AppImpl.cpp @@ -417,7 +417,7 @@ void refreshConnectedClientsAcl(uint16_t vendorId, uint16_t productId, ContentAp for (const auto & allowedVendor : app->GetApplicationBasicDelegate()->GetAllowedVendorList()) { - std::set tempNodeIds = ContentAppPlatform::GetInstance().GetNodeIdsForAllowVendorId(allowedVendor); + std::set tempNodeIds = ContentAppPlatform::GetInstance().GetNodeIdsForAllowedVendorId(allowedVendor); nodeIds.insert(tempNodeIds.begin(), tempNodeIds.end()); } diff --git a/examples/tv-app/android/java/ContentAppCommandDelegate.cpp b/examples/tv-app/android/java/ContentAppCommandDelegate.cpp index 02b4a7e806fefb..caf2d665b8522e 100644 --- a/examples/tv-app/android/java/ContentAppCommandDelegate.cpp +++ b/examples/tv-app/android/java/ContentAppCommandDelegate.cpp @@ -39,15 +39,9 @@ namespace chip { namespace AppPlatform { -using CommandHandlerInterface = chip::app::CommandHandlerInterface; -using LaunchResponseType = chip::app::Clusters::ContentLauncher::Commands::LauncherResponse::Type; -using PlaybackResponseType = chip::app::Clusters::MediaPlayback::Commands::PlaybackResponse::Type; -using NavigateTargetResponseType = chip::app::Clusters::TargetNavigator::Commands::NavigateTargetResponse::Type; -using GetSetupPINResponseType = chip::app::Clusters::AccountLogin::Commands::GetSetupPINResponse::Type; -using Status = chip::Protocols::InteractionModel::Status; - -const std::string FAILURE_KEY = "PlatformError"; -const std::string FAILURE_STATUS_KEY = "Status"; +const std::string FAILURE_KEY = "PlatformError"; +const std::string FAILURE_STATUS_KEY = "Status"; +const std::string RESPONSE_STATUS_KEY = "Status"; bool isValidJson(const char * response) { @@ -166,6 +160,7 @@ Status ContentAppCommandDelegate::InvokeCommand(EndpointId epId, ClusterId clust ChipLogError(Zcl, "Java exception in ContentAppCommandDelegate::sendCommand"); env->ExceptionDescribe(); env->ExceptionClear(); + return chip::Protocols::InteractionModel::Status::Failure; } else { @@ -198,7 +193,8 @@ Status ContentAppCommandDelegate::InvokeCommand(EndpointId epId, ClusterId clust } env->DeleteLocalRef(resp); - // handle errors from platform-app + // Parse response here in case there is failure response. + // Return non-success error code to indicate to caller it should not parse response. if (!value[FAILURE_KEY].empty()) { value = value[FAILURE_KEY]; @@ -209,7 +205,9 @@ Status ContentAppCommandDelegate::InvokeCommand(EndpointId epId, ClusterId clust return chip::Protocols::InteractionModel::Status::Failure; } - return chip::Protocols::InteractionModel::Status::UnsupportedEndpoint; + // Return success to indicate command has been sent, response returned and parsed successfully. + // Caller has to manually parse value input/output parameter to get response status/object. + return chip::Protocols::InteractionModel::Status::Success; } else { @@ -282,20 +280,31 @@ void ContentAppCommandDelegate::FormatResponseData(CommandHandlerInterface::Hand } case app::Clusters::AccountLogin::Id: { - if (app::Clusters::AccountLogin::Commands::GetSetupPIN::Id != handlerContext.mRequestPath.mCommandId) + switch (handlerContext.mRequestPath.mCommandId) { - // No response for other commands in this cluster + case app::Clusters::AccountLogin::Commands::GetSetupPIN::Id: { + Status status; + GetSetupPINResponseType getSetupPINresponse = FormatGetSetupPINResponse(value, status); + if (status != chip::Protocols::InteractionModel::Status::Success) + { + handlerContext.mCommandHandler.AddStatus(handlerContext.mRequestPath, status); + } + else + { + handlerContext.mCommandHandler.AddResponse(handlerContext.mRequestPath, getSetupPINresponse); + } break; } - Status status; - GetSetupPINResponseType getSetupPINresponse = FormatGetSetupPINResponse(value, status); - if (status != chip::Protocols::InteractionModel::Status::Success) - { - handlerContext.mCommandHandler.AddStatus(handlerContext.mRequestPath, status); + case app::Clusters::AccountLogin::Commands::Login::Id: { + handlerContext.mCommandHandler.AddStatus(handlerContext.mRequestPath, FormatStatusResponse(value)); + break; } - else - { - handlerContext.mCommandHandler.AddResponse(handlerContext.mRequestPath, getSetupPINresponse); + case app::Clusters::AccountLogin::Commands::Logout::Id: { + handlerContext.mCommandHandler.AddStatus(handlerContext.mRequestPath, FormatStatusResponse(value)); + break; + } + default: + break; } break; } @@ -388,10 +397,23 @@ GetSetupPINResponseType ContentAppCommandDelegate::FormatGetSetupPINResponse(Jso } else { - getSetupPINresponse.setupPIN = ""; + status = chip::Protocols::InteractionModel::Status::Failure; } return getSetupPINresponse; } +Status ContentAppCommandDelegate::FormatStatusResponse(Json::Value value) +{ + // check if JSON has "Status" key + if (!value[RESPONSE_STATUS_KEY].empty() && !value[RESPONSE_STATUS_KEY].isUInt()) + { + return static_cast(value.asUInt()); + } + else + { + return chip::Protocols::InteractionModel::Status::Failure; + } +} + } // namespace AppPlatform } // namespace chip diff --git a/examples/tv-app/android/java/ContentAppCommandDelegate.h b/examples/tv-app/android/java/ContentAppCommandDelegate.h index f033b1afa79189..49d3e6fda87610 100644 --- a/examples/tv-app/android/java/ContentAppCommandDelegate.h +++ b/examples/tv-app/android/java/ContentAppCommandDelegate.h @@ -29,6 +29,7 @@ #include #include #include +#include #include @@ -75,6 +76,7 @@ class ContentAppCommandDelegate : public CommandHandlerInterface LaunchResponseType FormatContentLauncherResponse(Json::Value value, Status & status); NavigateTargetResponseType FormatNavigateTargetResponse(Json::Value value, Status & status); PlaybackResponseType FormatMediaPlaybackResponse(Json::Value value, Status & status); + Status FormatStatusResponse(Json::Value value); private: void InitializeJNIObjects(jobject manager) diff --git a/examples/tv-app/android/java/TVApp-JNI.cpp b/examples/tv-app/android/java/TVApp-JNI.cpp index 0fe1bdef939ca8..228cab60e03748 100644 --- a/examples/tv-app/android/java/TVApp-JNI.cpp +++ b/examples/tv-app/android/java/TVApp-JNI.cpp @@ -258,15 +258,15 @@ SampleTvAppInstallationService gSampleTvAppInstallationService; class MyPostCommissioningListener : public PostCommissioningListener { - void CommissioningCompleted(uint16_t vendorId, uint16_t productId, NodeId nodeId, Messaging::ExchangeManager & exchangeMgr, - const SessionHandle & sessionHandle) override + void CommissioningCompleted(uint16_t vendorId, uint16_t productId, NodeId nodeId, CharSpan rotatingId, uint32_t passcode, + Messaging::ExchangeManager & exchangeMgr, const SessionHandle & sessionHandle) override { // read current binding list chip::Controller::ClusterBase cluster(exchangeMgr, sessionHandle, kTargetBindingClusterEndpointId); ContentAppPlatform::GetInstance().StoreNodeIdForContentApp(vendorId, productId, nodeId); - cacheContext(vendorId, productId, nodeId, exchangeMgr, sessionHandle); + cacheContext(vendorId, productId, nodeId, rotatingId, passcode, exchangeMgr, sessionHandle); CHIP_ERROR err = cluster.ReadAttribute(this, OnReadSuccessResponse, OnReadFailureResponse); @@ -350,17 +350,23 @@ class MyPostCommissioningListener : public PostCommissioningListener Optional opt = mSecureSession.Get(); SessionHandle & sessionHandle = opt.Value(); + auto rotatingIdSpan = CharSpan{ mRotatingId.data(), mRotatingId.size() }; ContentAppPlatform::GetInstance().ManageClientAccess(*mExchangeMgr, sessionHandle, mVendorId, mProductId, localNodeId, - bindings, OnSuccessResponse, OnFailureResponse); + rotatingIdSpan, mPasscode, bindings, OnSuccessResponse, + OnFailureResponse); clearContext(); } - void cacheContext(uint16_t vendorId, uint16_t productId, NodeId nodeId, Messaging::ExchangeManager & exchangeMgr, - const SessionHandle & sessionHandle) + void cacheContext(uint16_t vendorId, uint16_t productId, NodeId nodeId, CharSpan rotatingId, uint32_t passcode, + Messaging::ExchangeManager & exchangeMgr, const SessionHandle & sessionHandle) { - mVendorId = vendorId; - mProductId = productId; - mNodeId = nodeId; + mVendorId = vendorId; + mProductId = productId; + mNodeId = nodeId; + mRotatingId = std::string{ + rotatingId.data(), rotatingId.size() + }; // Allocates and copies to string instead of storing span to make sure lifetime is valid. + mPasscode = passcode; mExchangeMgr = &exchangeMgr; mSecureSession.ShiftToSession(sessionHandle); } @@ -370,12 +376,17 @@ class MyPostCommissioningListener : public PostCommissioningListener mVendorId = 0; mProductId = 0; mNodeId = 0; + mRotatingId = {}; + mPasscode = 0; mExchangeMgr = nullptr; mSecureSession.SessionReleased(); } - uint16_t mVendorId = 0; - uint16_t mProductId = 0; - NodeId mNodeId = 0; + + uint16_t mVendorId = 0; + uint16_t mProductId = 0; + NodeId mNodeId = 0; + std::string mRotatingId; + uint32_t mPasscode = 0; Messaging::ExchangeManager * mExchangeMgr = nullptr; SessionHolder mSecureSession; }; diff --git a/examples/tv-app/tv-common/src/AppTv.cpp b/examples/tv-app/tv-common/src/AppTv.cpp index 8d81d9cf6c2c5c..794c879b4c4da1 100644 --- a/examples/tv-app/tv-common/src/AppTv.cpp +++ b/examples/tv-app/tv-common/src/AppTv.cpp @@ -158,15 +158,15 @@ MyAppInstallationService gMyAppInstallationService; class MyPostCommissioningListener : public PostCommissioningListener { - void CommissioningCompleted(uint16_t vendorId, uint16_t productId, NodeId nodeId, Messaging::ExchangeManager & exchangeMgr, - const SessionHandle & sessionHandle) override + void CommissioningCompleted(uint16_t vendorId, uint16_t productId, NodeId nodeId, CharSpan rotatingId, uint32_t passcode, + Messaging::ExchangeManager & exchangeMgr, const SessionHandle & sessionHandle) override { // read current binding list chip::Controller::ClusterBase cluster(exchangeMgr, sessionHandle, kTargetBindingClusterEndpointId); ContentAppPlatform::GetInstance().StoreNodeIdForContentApp(vendorId, productId, nodeId); - cacheContext(vendorId, productId, nodeId, exchangeMgr, sessionHandle); + cacheContext(vendorId, productId, nodeId, rotatingId, passcode, exchangeMgr, sessionHandle); CHIP_ERROR err = cluster.ReadAttribute(this, OnReadSuccessResponse, OnReadFailureResponse); @@ -250,17 +250,23 @@ class MyPostCommissioningListener : public PostCommissioningListener Optional opt = mSecureSession.Get(); SessionHandle & sessionHandle = opt.Value(); + auto rotatingIdSpan = CharSpan{ mRotatingId.data(), mRotatingId.size() }; ContentAppPlatform::GetInstance().ManageClientAccess(*mExchangeMgr, sessionHandle, mVendorId, mProductId, localNodeId, - bindings, OnSuccessResponse, OnFailureResponse); + rotatingIdSpan, mPasscode, bindings, OnSuccessResponse, + OnFailureResponse); clearContext(); } - void cacheContext(uint16_t vendorId, uint16_t productId, NodeId nodeId, Messaging::ExchangeManager & exchangeMgr, - const SessionHandle & sessionHandle) + void cacheContext(uint16_t vendorId, uint16_t productId, NodeId nodeId, CharSpan rotatingId, uint32_t passcode, + Messaging::ExchangeManager & exchangeMgr, const SessionHandle & sessionHandle) { - mVendorId = vendorId; - mProductId = productId; - mNodeId = nodeId; + mVendorId = vendorId; + mProductId = productId; + mNodeId = nodeId; + mRotatingId = std::string{ + rotatingId.data(), rotatingId.size() + }; // Allocates and copies to string instead of storing span to make sure lifetime is valid. + mPasscode = passcode; mExchangeMgr = &exchangeMgr; mSecureSession.ShiftToSession(sessionHandle); } @@ -270,12 +276,17 @@ class MyPostCommissioningListener : public PostCommissioningListener mVendorId = 0; mProductId = 0; mNodeId = 0; + mRotatingId = {}; + mPasscode = 0; mExchangeMgr = nullptr; mSecureSession.SessionReleased(); } - uint16_t mVendorId = 0; - uint16_t mProductId = 0; - NodeId mNodeId = 0; + + uint16_t mVendorId = 0; + uint16_t mProductId = 0; + NodeId mNodeId = 0; + std::string mRotatingId; + uint32_t mPasscode = 0; Messaging::ExchangeManager * mExchangeMgr = nullptr; SessionHolder mSecureSession; }; @@ -684,7 +695,7 @@ void ContentAppFactoryImpl::InstallContentApp(uint16_t vendorId, uint16_t produc // update the list of node ids with content apps allowed vendor list for (const auto & allowedVendor : app->GetApplicationBasicDelegate()->GetAllowedVendorList()) { - std::set tempNodeIds = ContentAppPlatform::GetInstance().GetNodeIdsForAllowVendorId(allowedVendor); + std::set tempNodeIds = ContentAppPlatform::GetInstance().GetNodeIdsForAllowedVendorId(allowedVendor); nodeIds.insert(tempNodeIds.begin(), tempNodeIds.end()); } diff --git a/src/app/app-platform/ContentApp.cpp b/src/app/app-platform/ContentApp.cpp index a70965c5f2cd5e..4d9268e586dc2c 100644 --- a/src/app/app-platform/ContentApp.cpp +++ b/src/app/app-platform/ContentApp.cpp @@ -64,8 +64,17 @@ Status ContentApp::HandleWriteAttribute(ClusterId clusterId, AttributeId attribu return Status::Failure; } -void ContentApp::AddClientNode(NodeId subjectNodeId) +bool ContentApp::AddClientNode(NodeId subjectNodeId) { + for (int i = 0; i < kMaxClientNodes; ++i) + { + if (mClientNodes[i] == subjectNodeId) + { + // avoid storing duplicate nodes + return false; + } + } + mClientNodes[mNextClientNodeIndex++] = subjectNodeId; if (mClientNodeCount < kMaxClientNodes) { @@ -76,6 +85,8 @@ void ContentApp::AddClientNode(NodeId subjectNodeId) // if we exceed the max number, then overwrite the oldest entry mNextClientNodeIndex = 0; } + + return true; } void ContentApp::SendAppObserverCommand(chip::Controller::DeviceCommissioner * commissioner, NodeId clientNodeId, char * data, diff --git a/src/app/app-platform/ContentApp.h b/src/app/app-platform/ContentApp.h index 79c59e45dfd5bf..ee272c314d01f0 100644 --- a/src/app/app-platform/ContentApp.h +++ b/src/app/app-platform/ContentApp.h @@ -135,7 +135,8 @@ class DLL_EXPORT ContentApp uint16_t maxReadLength); Protocols::InteractionModel::Status HandleWriteAttribute(ClusterId clusterId, AttributeId attributeId, uint8_t * buffer); - void AddClientNode(NodeId clientNodeId); + // returns true only if new node is added. If node was added previously, then false is returned. + bool AddClientNode(NodeId clientNodeId); uint8_t GetClientNodeCount() const { return mClientNodeCount; } NodeId GetClientNode(uint8_t index) const { return mClientNodes[index]; } diff --git a/src/app/app-platform/ContentAppPlatform.cpp b/src/app/app-platform/ContentAppPlatform.cpp index 214af261e26e54..aa615aaae8be54 100644 --- a/src/app/app-platform/ContentAppPlatform.cpp +++ b/src/app/app-platform/ContentAppPlatform.cpp @@ -420,7 +420,7 @@ std::set ContentAppPlatform::GetNodeIdsForContentApp(uint16_t vendorId, return {}; } -std::set ContentAppPlatform::GetNodeIdsForAllowVendorId(uint16_t vendorId) +std::set ContentAppPlatform::GetNodeIdsForAllowedVendorId(uint16_t vendorId) { std::set result; std::string vendorPrefix = std::to_string(vendorId) + ":"; @@ -660,6 +660,7 @@ CHIP_ERROR ContentAppPlatform::GetACLEntryIndex(size_t * foundIndex, FabricIndex // and create bindings on the given client so that it knows what it has access to. CHIP_ERROR ContentAppPlatform::ManageClientAccess(Messaging::ExchangeManager & exchangeMgr, SessionHandle & sessionHandle, uint16_t targetVendorId, uint16_t targetProductId, NodeId localNodeId, + CharSpan rotatingId, uint32_t passcode, std::vector bindings, Controller::WriteResponseSuccessCallback successCb, Controller::WriteResponseFailureCallback failureCb) @@ -799,13 +800,32 @@ CHIP_ERROR ContentAppPlatform::ManageClientAccess(Messaging::ExchangeManager & e .cluster = NullOptional, .fabricIndex = kUndefinedFabricIndex, }); + + accessAllowed = true; } - accessAllowed = true; } if (accessAllowed) { // notify content app about this nodeId - app->AddClientNode(subjectNodeId); + bool isNodeAdded = app->AddClientNode(subjectNodeId); + + if (isNodeAdded && rotatingId.size() != 0) + { + // handle login + auto setupPIN = std::to_string(passcode); + auto accountLoginDelegate = app->GetAccountLoginDelegate(); + if (accountLoginDelegate != nullptr) + { + bool condition = accountLoginDelegate->HandleLogin(rotatingId, { setupPIN.data(), setupPIN.size() }, + MakeOptional(subjectNodeId)); + ChipLogProgress(Controller, "AccountLogin::Login command sent and returned: %s", + condition ? "success" : "failure"); + } + else + { + ChipLogError(Controller, "AccountLoginDelegate not found for app"); + } + } } } } diff --git a/src/app/app-platform/ContentAppPlatform.h b/src/app/app-platform/ContentAppPlatform.h index 45615d09ed8ddf..b00f9f1bf7a1ab 100644 --- a/src/app/app-platform/ContentAppPlatform.h +++ b/src/app/app-platform/ContentAppPlatform.h @@ -165,7 +165,7 @@ class DLL_EXPORT ContentAppPlatform std::set GetNodeIdsForContentApp(uint16_t vendorId, uint16_t productId); // returns set of connected nodes for a given allowed vendor id - std::set GetNodeIdsForAllowVendorId(uint16_t vendorId); + std::set GetNodeIdsForAllowedVendorId(uint16_t vendorId); // store node id for content app after commissioning // node id can be used later on to update ACL @@ -188,6 +188,8 @@ class DLL_EXPORT ContentAppPlatform * @param[in] targetVendorId Vendor ID for the target device. * @param[in] targetProductId Product ID for the target device. * @param[in] localNodeId The NodeId for the local device. + * @param[in] rotatingId The rotating account ID to handle account login. + * @param[in] passcode The passcode to handle account login. * @param[in] bindings Any additional bindings to include. This may include current bindings. * @param[in] successCb The function to be called on success of adding the binding. * @param[in] failureCb The function to be called on failure of adding the binding. @@ -195,7 +197,7 @@ class DLL_EXPORT ContentAppPlatform * @return CHIP_ERROR CHIP_NO_ERROR on success, or corresponding error */ CHIP_ERROR ManageClientAccess(Messaging::ExchangeManager & exchangeMgr, SessionHandle & sessionHandle, uint16_t targetVendorId, - uint16_t targetProductId, NodeId localNodeId, + uint16_t targetProductId, NodeId localNodeId, chip::CharSpan rotatingId, uint32_t passcode, std::vector bindings, Controller::WriteResponseSuccessCallback successCb, Controller::WriteResponseFailureCallback failureCb); diff --git a/src/controller/CommissionerDiscoveryController.cpp b/src/controller/CommissionerDiscoveryController.cpp index 09a22e03e0bcd2..baf8e14bfd4e37 100644 --- a/src/controller/CommissionerDiscoveryController.cpp +++ b/src/controller/CommissionerDiscoveryController.cpp @@ -166,6 +166,11 @@ void CommissionerDiscoveryController::OnUserDirectedCommissioningRequest(UDCClie ChipLogError(AppServer, "On UDC: could not convert rotating id to hex"); rotatingIdString[0] = '\0'; } + else + { + // Store rotating ID string. Don't include null terminator character. + mRotatingId = std::string{ rotatingIdString, state.GetRotatingIdLength() * 2 }; + } ChipLogDetail(Controller, "------PROMPT USER: %s is requesting permission to cast to this TV, approve? [" ChipLogFormatMEI @@ -455,6 +460,7 @@ void CommissionerDiscoveryController::InternalHandleContentAppPasscodeResponse() cd, Transport::PeerAddress::UDP(client->GetPeerAddress().GetIPAddress(), client->GetCdPort())); return; } + client->SetCachedCommissionerPasscode(passcode); client->SetUDCClientProcessingState(UDCClientProcessingState::kWaitingForCommissionerPasscodeReady); @@ -630,10 +636,13 @@ void CommissionerDiscoveryController::CommissioningSucceeded(uint16_t vendorId, mVendorId = vendorId; mProductId = productId; mNodeId = nodeId; + if (mPostCommissioningListener != nullptr) { ChipLogDetail(Controller, "CommissionerDiscoveryController calling listener"); - mPostCommissioningListener->CommissioningCompleted(vendorId, productId, nodeId, exchangeMgr, sessionHandle); + auto rotatingIdSpan = CharSpan{ mRotatingId.data(), mRotatingId.size() }; + mPostCommissioningListener->CommissioningCompleted(vendorId, productId, nodeId, rotatingIdSpan, mPasscode, exchangeMgr, + sessionHandle); } else { diff --git a/src/controller/CommissionerDiscoveryController.h b/src/controller/CommissionerDiscoveryController.h index 5f7572b29def17..94f172c1d32750 100644 --- a/src/controller/CommissionerDiscoveryController.h +++ b/src/controller/CommissionerDiscoveryController.h @@ -232,12 +232,14 @@ class DLL_EXPORT PostCommissioningListener * @param[in] vendorId The vendorid from the DAC of the new node. * @param[in] productId The productid from the DAC of the new node. * @param[in] nodeId The node id for the newly commissioned node. + * @param[in] rotatingId The rotating ID to handle account login. + * @param[in] passcode The passcode to handle account login. * @param[in] exchangeMgr The exchange manager to be used to get an exchange context. * @param[in] sessionHandle A reference to an established session. * */ - virtual void CommissioningCompleted(uint16_t vendorId, uint16_t productId, NodeId nodeId, - chip::Messaging::ExchangeManager & exchangeMgr, + virtual void CommissioningCompleted(uint16_t vendorId, uint16_t productId, NodeId nodeId, chip::CharSpan rotatingId, + uint32_t passcode, chip::Messaging::ExchangeManager & exchangeMgr, const chip::SessionHandle & sessionHandle) = 0; virtual ~PostCommissioningListener() = default; @@ -451,6 +453,7 @@ class CommissionerDiscoveryController : public chip::Protocols::UserDirectedComm uint16_t mProductId = 0; NodeId mNodeId = 0; uint32_t mPasscode = 0; + std::string mRotatingId; UserDirectedCommissioningServer * mUdcServer = nullptr; UserPrompter * mUserPrompter = nullptr; From 05ba80342a0b9949e286a19e02a6f658f0d97add Mon Sep 17 00:00:00 2001 From: Yufeng Wang Date: Thu, 22 Aug 2024 16:17:11 -0700 Subject: [PATCH 09/11] [Fabric-Sync] Symplify the build instructions with build_examples.py (#35162) --- examples/fabric-admin/README.md | 3 ++- examples/fabric-bridge-app/linux/README.md | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/examples/fabric-admin/README.md b/examples/fabric-admin/README.md index 7178ab2c2f8c94..c6b4ba7eb3577f 100644 --- a/examples/fabric-admin/README.md +++ b/examples/fabric-admin/README.md @@ -14,7 +14,8 @@ fabrics. For Linux host example: ``` -./scripts/examples/gn_build_example.sh examples/fabric-admin out/debug/standalone 'import("//with_pw_rpc.gni")' +source scripts/activate.sh +./scripts/build/build_examples.py --target linux-x64-fabric-admin-rpc build ``` For Raspberry Pi 4 example: diff --git a/examples/fabric-bridge-app/linux/README.md b/examples/fabric-bridge-app/linux/README.md index 6d830cdd987c33..96e8a2924a65bc 100644 --- a/examples/fabric-bridge-app/linux/README.md +++ b/examples/fabric-bridge-app/linux/README.md @@ -91,7 +91,8 @@ defined: ### For Linux host example: ``` - ./scripts/examples/gn_build_example.sh examples/fabric-bridge-app/linux out/debug/standalone chip_config_network_layer_ble=false 'import("//with_pw_rpc.gni")' + source scripts/activate.sh + ./scripts/build/build_examples.py --target linux-x64-fabric-bridge-rpc build ``` ### For Raspberry Pi 4 example: From daa2a57af16652ce17d7536db7665bf1e2ad40c2 Mon Sep 17 00:00:00 2001 From: Tennessee Carmel-Veilleux Date: Thu, 22 Aug 2024 19:48:46 -0400 Subject: [PATCH 10/11] Fix init of all TC-OCC-* tests (#35001) * Fix init of all TC-OCC-* tests - During TE2 if was found that all TC-OCC-* tests require to pass a `--int-arg endpoint:N` argument which is not the right way to specify endpoint. This is fixed here. - Some pre-steps were done in Step 1 which should have been done after Step 1 so that errors are not marked as Commissioning Failure when it's test failure. Fixes https://github.com/project-chip/matter-test-scripts/issues/340 * More fixes to OCC tests - 3.2 now properly using feature map - Fixed typos - Added queue flushing examples * Restyle * Fix line endings * Restyled by autopep8 * Fix CI --------- Co-authored-by: Restyled.io --- src/python_testing/TC_OCC_2_1.py | 566 ++++++++++--------- src/python_testing/TC_OCC_2_2.py | 264 ++++----- src/python_testing/TC_OCC_2_3.py | 255 ++++----- src/python_testing/TC_OCC_3_1.py | 263 +++++---- src/python_testing/TC_OCC_3_2.py | 411 +++++++------- src/python_testing/matter_testing_support.py | 26 + 6 files changed, 907 insertions(+), 878 deletions(-) diff --git a/src/python_testing/TC_OCC_2_1.py b/src/python_testing/TC_OCC_2_1.py index cf39a7eff5a111..ddbb34cbf0252a 100644 --- a/src/python_testing/TC_OCC_2_1.py +++ b/src/python_testing/TC_OCC_2_1.py @@ -1,278 +1,288 @@ -# -# Copyright (c) 2024 Project CHIP Authors -# All rights reserved. -# -# 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. -# -# There are CI issues to be followed up for the test cases below that implements manually controlling sensor device for -# the occupancy state ON/OFF change. -# [TC-OCC-3.1] test procedure step 4 -# [TC-OCC-3.2] test precedure step 3a, 3c - -# See https://github.com/project-chip/connectedhomeip/blob/master/docs/testing/python.md#defining-the-ci-test-arguments -# for details about the block below. -# -# === BEGIN CI TEST ARGUMENTS === -# test-runner-runs: run1 -# test-runner-run/run1/app: ${ALL_CLUSTERS_APP} -# test-runner-run/run1/factoryreset: True -# test-runner-run/run1/quiet: True -# test-runner-run/run1/app-args: --discriminator 1234 --KVS kvs1 --trace-to json:${TRACE_APP}.json -# test-runner-run/run1/script-args: --storage-path admin_storage.json --commissioning-method on-network --discriminator 1234 --passcode 20202021 --PICS src/app/tests/suites/certification/ci-pics-values --trace-to json:${TRACE_TEST_JSON}.json --trace-to perfetto:${TRACE_TEST_PERFETTO}.perfetto -# === END CI TEST ARGUMENTS === - -import logging - -import chip.clusters as Clusters -from matter_testing_support import MatterBaseTest, TestStep, async_test_body, default_matter_test_main -from mobly import asserts - - -class TC_OCC_2_1(MatterBaseTest): - async def read_occ_attribute_expect_success(self, endpoint, attribute): - cluster = Clusters.Objects.OccupancySensing - return await self.read_single_attribute_check_success(endpoint=endpoint, cluster=cluster, attribute=attribute) - - def desc_TC_OCC_2_1(self) -> str: - return "[TC-OCC-2.1] Attributes with DUT as Server" - - def steps_TC_OCC_2_1(self) -> list[TestStep]: - steps = [ - TestStep(1, "Commissioning, already done", is_commissioning=True), - TestStep(2, "Read Occupancy attribute."), - TestStep(3, "Read OccupancySensorType attribute."), - TestStep(4, "Read OccupancySensorTypeBitmap attribute."), - TestStep(5, "Read HoldTimeLimits attribute, if supported"), - TestStep(6, "Read HoldTime attribute, if supported"), - TestStep(7, "Read PIROccupiedToUnoccupiedDelay attribute, if supported"), - TestStep(8, "Read PIRUnoccupiedToOccupiedDelay attribute, if supported"), - TestStep(9, "Read PIRUnoccupiedToOccupiedThreshold attribute, if supported"), - TestStep(10, "Read UltrasonicOccupiedToUnoccupiedDelay attribute, if supported"), - TestStep(11, "Read UltrasonicUnoccupiedToOccupiedDelay attribute, if supported"), - TestStep(12, "Read UltrasonicUnoccupiedToOccupiedThreshold attribute, if supported"), - TestStep(13, "Read PhysicalContactOccupiedToUnoccupiedDelay attribute, if supported"), - TestStep(14, "Read PhysicalContactUnoccupiedToOccupiedDelay attribute, if supported"), - TestStep(15, "Read PhysicalContactUnoccupiedToOccupiedThreshold attribute, if supported") - ] - return steps - - def pics_TC_OCC_2_1(self) -> list[str]: - pics = [ - "OCC.S", - ] - return pics - - @async_test_body - async def test_TC_OCC_2_1(self): - - endpoint = self.user_params.get("endpoint", 1) - - self.step(1) - attributes = Clusters.OccupancySensing.Attributes - attribute_list = await self.read_occ_attribute_expect_success(endpoint=endpoint, attribute=attributes.AttributeList) - - self.step(2) - asserts.assert_in(attributes.Occupancy.attribute_id, attribute_list, "Occupancy attribute is mandatory") - occupancy_dut = await self.read_occ_attribute_expect_success(endpoint=endpoint, attribute=attributes.Occupancy) - asserts.assert_less_equal(occupancy_dut, 0b00000001, "Occupancy attribute is not in valid range") - - self.step(3) - asserts.assert_in(attributes.OccupancySensorType.attribute_id, attribute_list, - "OccupancySensorType attribute is a mandatory attribute.") - - occupancy_sensor_type_dut = await self.read_occ_attribute_expect_success(endpoint=endpoint, attribute=attributes.OccupancySensorType) - asserts.assert_less(occupancy_sensor_type_dut, Clusters.Objects.OccupancySensing.Enums.OccupancySensorTypeEnum.kUnknownEnumValue, - "OccupancySensorType is not in valid range") - asserts.assert_in(occupancy_sensor_type_dut, {Clusters.Objects.OccupancySensing.Enums.OccupancySensorTypeEnum.kPir, - Clusters.Objects.OccupancySensing.Enums.OccupancySensorTypeEnum.kUltrasonic, - Clusters.Objects.OccupancySensing.Enums.OccupancySensorTypeEnum.kPIRAndUltrasonic, - Clusters.Objects.OccupancySensing.Enums.OccupancySensorTypeEnum.kPhysicalContact}, "OccupancySensorType is not in valid range") - self.step(4) - asserts.assert_in(attributes.OccupancySensorTypeBitmap.attribute_id, attribute_list, - "OccupancySensorTypeBitmap attribute is a mandatory attribute.") - - occupancy_sensor_type_bitmap_dut = await self.read_occ_attribute_expect_success(endpoint=endpoint, attribute=attributes.OccupancySensorTypeBitmap) - asserts.assert_less_equal(occupancy_sensor_type_bitmap_dut, 0b00000111, - "OccupancySensorTypeBitmap attribute is not in valid range") - - self.step(5) - if attributes.HoldTimeLimits.attribute_id in attribute_list: - asserts.assert_in(attributes.HoldTime.attribute_id, attribute_list, "HoldTime attribute conformance failed.") - hold_time_limits_dut = await self.read_occ_attribute_expect_success(endpoint=endpoint, attribute=attributes.HoldTimeLimits) - asserts.assert_less_equal(hold_time_limits_dut.holdTimeMin, hold_time_limits_dut.holdTimeMax, - "HoldTimeMin is not in valid range") - asserts.assert_greater_equal(hold_time_limits_dut.holdTimeMin, 0, "HoldTimeMin is not in valid range") - asserts.assert_less_equal(hold_time_limits_dut.holdTimeMax, 0xFFFE, "HoldTimeMin is not in valid range") - asserts.assert_greater_equal(hold_time_limits_dut.holdTimeMax, - hold_time_limits_dut.holdTimeMin, "HoldTimeMin is not in valid range") - asserts.assert_less_equal(hold_time_limits_dut.holdTimeDefault, - hold_time_limits_dut.holdTimeMax, "HoldTimeMin is not in valid range") - asserts.assert_greater_equal(hold_time_limits_dut.holdTimeDefault, - hold_time_limits_dut.holdTimeMin, "HoldTimeMin is not in valid range") - else: - logging.info("HoldTimeLimits not supported. Test step skipped") - self.mark_current_step_skipped() - - self.step(6) - if attributes.HoldTime.attribute_id in attribute_list: - hold_time_dut = await self.read_occ_attribute_expect_success(endpoint=endpoint, attribute=attributes.HoldTime) - hold_time_limits_dut = await self.read_occ_attribute_expect_success(endpoint=endpoint, attribute=attributes.HoldTimeLimits) - - asserts.assert_less_equal(hold_time_dut, hold_time_limits_dut.holdTimeMax, "HoldTime attribute is out of range") - asserts.assert_greater_equal(hold_time_dut, hold_time_limits_dut.holdTimeMin, "HoldTime attribute is out of range") - else: - logging.info("HoldTime not supported. The rest of legacy attribute test can be skipped") - self.skip_all_remaining_steps(7) - return - - self.step(7) - if attributes.PIROccupiedToUnoccupiedDelay.attribute_id in attribute_list: - has_pir_bitmap = (occupancy_sensor_type_bitmap_dut & - Clusters.OccupancySensing.Bitmaps.OccupancySensorTypeBitmap.kPir) != 0 - has_ultrasonic_bitmap = (occupancy_sensor_type_bitmap_dut & - Clusters.OccupancySensing.Bitmaps.OccupancySensorTypeBitmap.kUltrasonic) != 0 - has_phy_bitmap = (occupancy_sensor_type_bitmap_dut & - Clusters.OccupancySensing.Bitmaps.OccupancySensorTypeBitmap.kPhysicalContact) != 0 - if has_pir_bitmap or (not has_ultrasonic_bitmap and not has_phy_bitmap): - pir_otou_delay_dut = await self.read_occ_attribute_expect_success(endpoint=endpoint, attribute=attributes.PIROccupiedToUnoccupiedDelay) - asserts.assert_less_equal(pir_otou_delay_dut, 0xFFFE, "PIROccupiedToUnoccupiedDelay is not in valid range") - asserts.assert_greater_equal(pir_otou_delay_dut, 0, "PIROccupiedToUnoccupiedDelay is not in valid range") - else: - logging.info("PIROccupiedToUnoccupiedDelay conformance failed") - asserts.fail( - f"PIROccupiedToUnoccupiedDelay conformance is incorrect: {has_pir_bitmap}, {has_ultrasonic_bitmap}, {has_phy_bitmap}") - else: - logging.info("PIROccupiedToUnoccupiedDelay not supported. Test step skipped") - self.mark_current_step_skipped() - - self.step(8) - if attributes.PIRUnoccupiedToOccupiedDelay.attribute_id in attribute_list: - has_delay = attributes.PIRUnoccupiedToOccupiedDelay.attribute_id in attribute_list - has_threshold = attributes.PIRUnoccupiedToOccupiedThreshold.attribute_id in attribute_list - asserts.assert_equal(has_delay, has_threshold, "PIRUnoccupiedToOccupiedDelay conformance failure") - pir_utoo_delay_dut = await self.read_occ_attribute_expect_success(endpoint=endpoint, attribute=attributes.PIRUnoccupiedToOccupiedDelay) - asserts.assert_less_equal(pir_utoo_delay_dut, 0xFFFE, "PIRUnoccupiedToOccupiedDelay is not in valid range") - asserts.assert_greater_equal(pir_utoo_delay_dut, 0, "PIRUnoccupiedToOccupiedDelay is not in valid range") - else: - logging.info("PIRUnoccupiedToOccupiedDelay not supported. Test step skipped") - self.mark_current_step_skipped() - - self.step(9) - if attributes.PIRUnoccupiedToOccupiedThreshold.attribute_id in attribute_list: - has_delay = attributes.PIRUnoccupiedToOccupiedDelay.attribute_id in attribute_list - has_threshold = attributes.PIRUnoccupiedToOccupiedThreshold.attribute_id in attribute_list - asserts.assert_equal(has_delay, has_threshold, "PIRUnoccupiedToOccupiedThreshold conformance failure") - pir_utoo_threshold_dut = await self.read_occ_attribute_expect_success(endpoint=endpoint, attribute=attributes.PIRUnoccupiedToOccupiedThreshold) - asserts.assert_less_equal(pir_utoo_threshold_dut, 0xFE, "PIRUnoccupiedToOccupiedThreshold is not in valid range") - asserts.assert_greater_equal(pir_utoo_threshold_dut, 0, "PIRUnoccupiedToOccupiedThreshold is not in valid range") - else: - logging.info("PIRUnoccupiedToOccupiedThreshold not supported. Test step skipped") - self.mark_current_step_skipped() - - self.step(10) - if attributes.UltrasonicOccupiedToUnoccupiedDelay.attribute_id in attribute_list: - has_ultrasonic_bitmap = (occupancy_sensor_type_bitmap_dut & - Clusters.OccupancySensing.Enums.OccupancySensorTypeEnum.kUltrasonic) != 0 - has_ultrasonic_delay = attributes.UltrasonicOccupiedToUnoccupiedDelay.attribute_id in attribute_list - asserts.assert_equal(has_ultrasonic_bitmap, has_ultrasonic_delay, "Bad conformance on Ultrasonic bitmap") - - ultrasonic_otou_delay_dut = await self.read_occ_attribute_expect_success(endpoint=endpoint, attribute=attributes.UltrasonicOccupiedToUnoccupiedDelay) - asserts.assert_less_equal(ultrasonic_otou_delay_dut, 0xFFFE, - "UltrasonicOccupiedToUnoccupiedDelay is not in valid range") - asserts.assert_greater_equal(ultrasonic_otou_delay_dut, 0, "UltrasonicOccupiedToUnoccupiedDelay is not in valid range") - - else: - logging.info("UltrasonicOccupiedToUnoccupiedDelay not supported. Test step skipped") - self.mark_current_step_skipped() - - self.step(11) - if attributes.UltrasonicUnoccupiedToOccupiedDelay.attribute_id in attribute_list: - has_delay = attributes.UltrasonicUnoccupiedToOccupiedDelay.attribute_id in attribute_list - has_threshold = attributes.UltrasonicUnoccupiedToOccupiedThreshold.attribute_id in attribute_list - asserts.assert_equal(has_delay, has_threshold, "UltrasonicUnoccupiedToOccupiedDelay conformance failure") - - ultrasonic_utoo_delay_dut = await self.read_occ_attribute_expect_success(endpoint=endpoint, attribute=attributes.UltrasonicUnoccupiedToOccupiedDelay) - asserts.assert_less_equal(ultrasonic_utoo_delay_dut, 0xFFFE, - "UltrasonicUnoccupiedToOccupiedDelay is not in valid range") - asserts.assert_greater_equal(ultrasonic_utoo_delay_dut, 0, "UltrasonicUnoccupiedToOccupiedDelay is not in valid range") - else: - logging.info("UltrasonicUnoccupiedToOccupiedDelay not supported. Test step skipped") - self.mark_current_step_skipped() - - self.step(12) - if attributes.UltrasonicUnoccupiedToOccupiedThreshold.attribute_id in attribute_list: - has_delay = attributes.UltrasonicUnoccupiedToOccupiedDelay.attribute_id in attribute_list - has_threshold = attributes.UltrasonicUnoccupiedToOccupiedThreshold.attribute_id in attribute_list - asserts.assert_equal(has_delay, has_threshold, "UltrasonicUnoccupiedToOccupiedThreshold conformance failure") - - ultrasonic_utoo_threshold_dut = await self.read_occ_attribute_expect_success(endpoint=endpoint, attribute=attributes.UltrasonicUnoccupiedToOccupiedThreshold) - asserts.assert_less_equal(ultrasonic_utoo_threshold_dut, 0xFE, - "UltrasonicUnoccupiedToOccupiedThreshold is not in valid range") - asserts.assert_greater_equal(ultrasonic_utoo_threshold_dut, 0, - "UltrasonicUnoccupiedToOccupiedThreshold is not in valid range") - - else: - logging.info("UltrasonicUnoccupiedToOccupiedThreshold not supported. Test step skipped") - self.mark_current_step_skipped() - - self.step(13) - if attributes.PhysicalContactOccupiedToUnoccupiedDelay.attribute_id in attribute_list: - has_phycon_bitmap = (occupancy_sensor_type_bitmap_dut & - Clusters.OccupancySensing.Enums.OccupancySensorTypeEnum.kPhysicalContact) != 0 - has_phycon_delay = attributes.PhysicalContactOccupiedToUnoccupiedDelay.attribute_id in attribute_list - asserts.assert_equal(has_phycon_bitmap, has_phycon_delay, "Bad conformance on PhysicalContact bitmap") - phycontact_otou_delay_dut = await self.read_occ_attribute_expect_success(endpoint=endpoint, attribute=attributes.PhysicalContactOccupiedToUnoccupiedDelay) - asserts.assert_less_equal(phycontact_otou_delay_dut, 0xFFFE, - "PhysicalContactOccupiedToUnoccupiedDelay is not in valid range") - asserts.assert_greater_equal(phycontact_otou_delay_dut, 0, - "PhysicalContactOccupiedToUnoccupiedDelay is not in valid range") - - else: - logging.info("PhysicalContactOccupiedToUnoccupiedDelay not supported. Test step skipped") - self.mark_current_step_skipped() - - self.step(14) - if attributes.PhysicalContactUnoccupiedToOccupiedDelay.attribute_id in attribute_list: - has_delay = attributes.PhysicalContactUnoccupiedToOccupiedDelay.attribute_id in attribute_list - has_threshold = attributes.PhysicalContactUnoccupiedToOccupiedThreshold.attribute_id in attribute_list - asserts.assert_equal(has_delay, has_threshold, "PhysicalContactUnoccupiedToOccupiedDelay conformance failure") - - phycontact_utoo_delay_dut = await self.read_occ_attribute_expect_success(endpoint=endpoint, attribute=attributes.PhysicalContactUnoccupiedToOccupiedDelay) - asserts.assert_less_equal(phycontact_utoo_delay_dut, 0xFFFE, - "PhysicalContactUnoccupiedToOccupiedDelay is not in valid range") - asserts.assert_greater_equal(phycontact_utoo_delay_dut, 0, - "PhysicalContactUnoccupiedToOccupiedDelay is not in valid range") - - else: - logging.info("PhysicalContactUnoccupiedToOccupiedDelay not supported. Test step skipped") - self.mark_current_step_skipped() - - self.step(15) - if attributes.PhysicalContactUnoccupiedToOccupiedThreshold.attribute_id in attribute_list: - has_delay = attributes.PhysicalContactUnoccupiedToOccupiedDelay.attribute_id in attribute_list - has_threshold = attributes.PhysicalContactUnoccupiedToOccupiedThreshold.attribute_id in attribute_list - asserts.assert_equal(has_delay, has_threshold, "PhysicalContactUnoccupiedToOccupiedThreshold conformance failure") - - phycontact_utoo_threshold_dut = await self.read_occ_attribute_expect_success(endpoint=endpoint, attribute=attributes.PhysicalContactUnoccupiedToOccupiedThreshold) - asserts.assert_less_equal(phycontact_utoo_threshold_dut, 0xFE, - "PhysicalContactUnoccupiedToOccupiedThreshold is not in valid range") - asserts.assert_greater_equal(phycontact_utoo_threshold_dut, 0, - "PhysicalContactUnoccupiedToOccupiedThreshold is not in valid range") - - else: - logging.info("PhysicalContactUnoccupiedToOccupiedThreshold not supported. Test step skipped") - self.mark_current_step_skipped() - - -if __name__ == "__main__": - default_matter_test_main() +# +# Copyright (c) 2024 Project CHIP Authors +# All rights reserved. +# +# 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. +# +# There are CI issues to be followed up for the test cases below that implements manually controlling sensor device for +# the occupancy state ON/OFF change. +# [TC-OCC-3.1] test procedure step 4 +# [TC-OCC-3.2] test precedure step 3a, 3c + +# See https://github.com/project-chip/connectedhomeip/blob/master/docs/testing/python.md#defining-the-ci-test-arguments +# for details about the block below. +# +# === BEGIN CI TEST ARGUMENTS === +# test-runner-runs: run1 +# test-runner-run/run1/app: ${ALL_CLUSTERS_APP} +# test-runner-run/run1/factoryreset: True +# test-runner-run/run1/quiet: True +# test-runner-run/run1/app-args: --discriminator 1234 --KVS kvs1 --trace-to json:${TRACE_APP}.json +# test-runner-run/run1/script-args: --storage-path admin_storage.json --commissioning-method on-network --discriminator 1234 --passcode 20202021 --PICS src/app/tests/suites/certification/ci-pics-values --trace-to json:${TRACE_TEST_JSON}.json --trace-to perfetto:${TRACE_TEST_PERFETTO}.perfetto --endpoint 1 +# === END CI TEST ARGUMENTS === + +import logging + +import chip.clusters as Clusters +from matter_testing_support import MatterBaseTest, TestStep, async_test_body, default_matter_test_main +from mobly import asserts + + +class TC_OCC_2_1(MatterBaseTest): + async def read_occ_attribute_expect_success(self, endpoint, attribute): + cluster = Clusters.Objects.OccupancySensing + return await self.read_single_attribute_check_success(endpoint=endpoint, cluster=cluster, attribute=attribute) + + def desc_TC_OCC_2_1(self) -> str: + return "[TC-OCC-2.1] Attributes with DUT as Server" + + def steps_TC_OCC_2_1(self) -> list[TestStep]: + steps = [ + TestStep(1, "Commissioning, already done", is_commissioning=True), + TestStep(2, "Read Occupancy attribute."), + TestStep(3, "Read OccupancySensorType attribute."), + TestStep(4, "Read OccupancySensorTypeBitmap attribute."), + TestStep(5, "Read HoldTimeLimits attribute, if supported"), + TestStep(6, "Read HoldTime attribute, if supported"), + TestStep(7, "Read PIROccupiedToUnoccupiedDelay attribute, if supported"), + TestStep(8, "Read PIRUnoccupiedToOccupiedDelay attribute, if supported"), + TestStep(9, "Read PIRUnoccupiedToOccupiedThreshold attribute, if supported"), + TestStep(10, "Read UltrasonicOccupiedToUnoccupiedDelay attribute, if supported"), + TestStep(11, "Read UltrasonicUnoccupiedToOccupiedDelay attribute, if supported"), + TestStep(12, "Read UltrasonicUnoccupiedToOccupiedThreshold attribute, if supported"), + TestStep(13, "Read PhysicalContactOccupiedToUnoccupiedDelay attribute, if supported"), + TestStep(14, "Read PhysicalContactUnoccupiedToOccupiedDelay attribute, if supported"), + TestStep(15, "Read PhysicalContactUnoccupiedToOccupiedThreshold attribute, if supported") + ] + return steps + + def pics_TC_OCC_2_1(self) -> list[str]: + pics = [ + "OCC.S", + ] + return pics + + @async_test_body + async def test_TC_OCC_2_1(self): + endpoint = self.matter_test_config.endpoint + cluster = Clusters.Objects.OccupancySensing + attributes = cluster.Attributes + + self.step(1) # Already done, immediately go to step 2 + + self.step(2) + + feature_map = await self.read_occ_attribute_expect_success(endpoint=endpoint, attribute=attributes.FeatureMap) + has_feature_pir = (feature_map & cluster.Bitmaps.Feature.kPassiveInfrared) != 0 + has_feature_ultrasonic = (feature_map & cluster.Bitmaps.Feature.kUltrasonic) != 0 + has_feature_contact = (feature_map & cluster.Bitmaps.Feature.kPhysicalContact) != 0 + + logging.info( + f"Feature map: 0x{feature_map:x}. PIR: {has_feature_pir}, US:{has_feature_ultrasonic}, PHY:{has_feature_contact}") + + attribute_list = await self.read_occ_attribute_expect_success(endpoint=endpoint, attribute=attributes.AttributeList) + + asserts.assert_in(attributes.Occupancy.attribute_id, attribute_list, "Occupancy attribute is mandatory") + occupancy_dut = await self.read_occ_attribute_expect_success(endpoint=endpoint, attribute=attributes.Occupancy) + asserts.assert_less_equal(occupancy_dut, 0b00000001, "Occupancy attribute is not in valid range") + + self.step(3) + asserts.assert_in(attributes.OccupancySensorType.attribute_id, attribute_list, + "OccupancySensorType attribute is a mandatory attribute.") + + occupancy_sensor_type_dut = await self.read_occ_attribute_expect_success(endpoint=endpoint, attribute=attributes.OccupancySensorType) + asserts.assert_less(occupancy_sensor_type_dut, Clusters.Objects.OccupancySensing.Enums.OccupancySensorTypeEnum.kUnknownEnumValue, + "OccupancySensorType is not in valid range") + asserts.assert_in(occupancy_sensor_type_dut, {Clusters.Objects.OccupancySensing.Enums.OccupancySensorTypeEnum.kPir, + Clusters.Objects.OccupancySensing.Enums.OccupancySensorTypeEnum.kUltrasonic, + Clusters.Objects.OccupancySensing.Enums.OccupancySensorTypeEnum.kPIRAndUltrasonic, + Clusters.Objects.OccupancySensing.Enums.OccupancySensorTypeEnum.kPhysicalContact}, "OccupancySensorType is not in valid range") + self.step(4) + asserts.assert_in(attributes.OccupancySensorTypeBitmap.attribute_id, attribute_list, + "OccupancySensorTypeBitmap attribute is a mandatory attribute.") + + occupancy_sensor_type_bitmap_dut = await self.read_occ_attribute_expect_success(endpoint=endpoint, attribute=attributes.OccupancySensorTypeBitmap) + asserts.assert_less_equal(occupancy_sensor_type_bitmap_dut, 0b00000111, + "OccupancySensorTypeBitmap attribute is not in valid range") + + self.step(5) + if attributes.HoldTimeLimits.attribute_id in attribute_list: + asserts.assert_in(attributes.HoldTime.attribute_id, attribute_list, "HoldTime attribute conformance failed.") + hold_time_limits_dut = await self.read_occ_attribute_expect_success(endpoint=endpoint, attribute=attributes.HoldTimeLimits) + asserts.assert_less_equal(hold_time_limits_dut.holdTimeMin, hold_time_limits_dut.holdTimeMax, + "HoldTimeMin is not in valid range") + asserts.assert_greater_equal(hold_time_limits_dut.holdTimeMin, 0, "HoldTimeMin is not in valid range") + asserts.assert_less_equal(hold_time_limits_dut.holdTimeMax, 0xFFFE, "HoldTimeMin is not in valid range") + asserts.assert_greater_equal(hold_time_limits_dut.holdTimeMax, + hold_time_limits_dut.holdTimeMin, "HoldTimeMin is not in valid range") + asserts.assert_less_equal(hold_time_limits_dut.holdTimeDefault, + hold_time_limits_dut.holdTimeMax, "HoldTimeMin is not in valid range") + asserts.assert_greater_equal(hold_time_limits_dut.holdTimeDefault, + hold_time_limits_dut.holdTimeMin, "HoldTimeMin is not in valid range") + else: + logging.info("HoldTimeLimits not supported. Test step skipped") + self.mark_current_step_skipped() + + self.step(6) + if attributes.HoldTime.attribute_id in attribute_list: + hold_time_dut = await self.read_occ_attribute_expect_success(endpoint=endpoint, attribute=attributes.HoldTime) + hold_time_limits_dut = await self.read_occ_attribute_expect_success(endpoint=endpoint, attribute=attributes.HoldTimeLimits) + + asserts.assert_less_equal(hold_time_dut, hold_time_limits_dut.holdTimeMax, "HoldTime attribute is out of range") + asserts.assert_greater_equal(hold_time_dut, hold_time_limits_dut.holdTimeMin, "HoldTime attribute is out of range") + else: + logging.info("HoldTime not supported. The rest of legacy attribute test can be skipped") + self.skip_all_remaining_steps(7) + return + + self.step(7) + if attributes.PIROccupiedToUnoccupiedDelay.attribute_id in attribute_list: + has_feature_pir = (occupancy_sensor_type_bitmap_dut & + Clusters.OccupancySensing.Bitmaps.OccupancySensorTypeBitmap.kPir) != 0 + has_feature_ultrasonic = (occupancy_sensor_type_bitmap_dut & + Clusters.OccupancySensing.Bitmaps.OccupancySensorTypeBitmap.kUltrasonic) != 0 + has_feature_contact = (occupancy_sensor_type_bitmap_dut & + Clusters.OccupancySensing.Bitmaps.OccupancySensorTypeBitmap.kPhysicalContact) != 0 + if has_feature_pir or (not has_feature_pir and not has_feature_ultrasonic and not has_feature_contact): + pir_otou_delay_dut = await self.read_occ_attribute_expect_success(endpoint=endpoint, attribute=attributes.PIROccupiedToUnoccupiedDelay) + asserts.assert_less_equal(pir_otou_delay_dut, 0xFFFE, "PIROccupiedToUnoccupiedDelay is not in valid range") + asserts.assert_greater_equal(pir_otou_delay_dut, 0, "PIROccupiedToUnoccupiedDelay is not in valid range") + else: + logging.info("PIROccupiedToUnoccupiedDelay conformance failed") + asserts.fail( + f"PIROccupiedToUnoccupiedDelay conformance is incorrect: {has_feature_pir}, {has_feature_ultrasonic}, {has_feature_contact}") + else: + logging.info("PIROccupiedToUnoccupiedDelay not supported. Test step skipped") + self.mark_current_step_skipped() + + self.step(8) + if attributes.PIRUnoccupiedToOccupiedDelay.attribute_id in attribute_list: + has_delay = attributes.PIRUnoccupiedToOccupiedDelay.attribute_id in attribute_list + has_threshold = attributes.PIRUnoccupiedToOccupiedThreshold.attribute_id in attribute_list + asserts.assert_equal(has_delay, has_threshold, "PIRUnoccupiedToOccupiedDelay conformance failure") + pir_utoo_delay_dut = await self.read_occ_attribute_expect_success(endpoint=endpoint, attribute=attributes.PIRUnoccupiedToOccupiedDelay) + asserts.assert_less_equal(pir_utoo_delay_dut, 0xFFFE, "PIRUnoccupiedToOccupiedDelay is not in valid range") + asserts.assert_greater_equal(pir_utoo_delay_dut, 0, "PIRUnoccupiedToOccupiedDelay is not in valid range") + else: + logging.info("PIRUnoccupiedToOccupiedDelay not supported. Test step skipped") + self.mark_current_step_skipped() + + self.step(9) + if attributes.PIRUnoccupiedToOccupiedThreshold.attribute_id in attribute_list: + has_delay = attributes.PIRUnoccupiedToOccupiedDelay.attribute_id in attribute_list + has_threshold = attributes.PIRUnoccupiedToOccupiedThreshold.attribute_id in attribute_list + asserts.assert_equal(has_delay, has_threshold, "PIRUnoccupiedToOccupiedThreshold conformance failure") + pir_utoo_threshold_dut = await self.read_occ_attribute_expect_success(endpoint=endpoint, attribute=attributes.PIRUnoccupiedToOccupiedThreshold) + asserts.assert_less_equal(pir_utoo_threshold_dut, 0xFE, "PIRUnoccupiedToOccupiedThreshold is not in valid range") + asserts.assert_greater_equal(pir_utoo_threshold_dut, 0, "PIRUnoccupiedToOccupiedThreshold is not in valid range") + else: + logging.info("PIRUnoccupiedToOccupiedThreshold not supported. Test step skipped") + self.mark_current_step_skipped() + + self.step(10) + if attributes.UltrasonicOccupiedToUnoccupiedDelay.attribute_id in attribute_list: + has_feature_ultrasonic = (occupancy_sensor_type_bitmap_dut & + Clusters.OccupancySensing.Enums.OccupancySensorTypeEnum.kUltrasonic) != 0 + has_ultrasonic_delay = attributes.UltrasonicOccupiedToUnoccupiedDelay.attribute_id in attribute_list + asserts.assert_equal(has_feature_ultrasonic, has_ultrasonic_delay, "Bad conformance on Ultrasonic bitmap") + + ultrasonic_otou_delay_dut = await self.read_occ_attribute_expect_success(endpoint=endpoint, attribute=attributes.UltrasonicOccupiedToUnoccupiedDelay) + asserts.assert_less_equal(ultrasonic_otou_delay_dut, 0xFFFE, + "UltrasonicOccupiedToUnoccupiedDelay is not in valid range") + asserts.assert_greater_equal(ultrasonic_otou_delay_dut, 0, "UltrasonicOccupiedToUnoccupiedDelay is not in valid range") + + else: + logging.info("UltrasonicOccupiedToUnoccupiedDelay not supported. Test step skipped") + self.mark_current_step_skipped() + + self.step(11) + if attributes.UltrasonicUnoccupiedToOccupiedDelay.attribute_id in attribute_list: + has_delay = attributes.UltrasonicUnoccupiedToOccupiedDelay.attribute_id in attribute_list + has_threshold = attributes.UltrasonicUnoccupiedToOccupiedThreshold.attribute_id in attribute_list + asserts.assert_equal(has_delay, has_threshold, "UltrasonicUnoccupiedToOccupiedDelay conformance failure") + + ultrasonic_utoo_delay_dut = await self.read_occ_attribute_expect_success(endpoint=endpoint, attribute=attributes.UltrasonicUnoccupiedToOccupiedDelay) + asserts.assert_less_equal(ultrasonic_utoo_delay_dut, 0xFFFE, + "UltrasonicUnoccupiedToOccupiedDelay is not in valid range") + asserts.assert_greater_equal(ultrasonic_utoo_delay_dut, 0, "UltrasonicUnoccupiedToOccupiedDelay is not in valid range") + else: + logging.info("UltrasonicUnoccupiedToOccupiedDelay not supported. Test step skipped") + self.mark_current_step_skipped() + + self.step(12) + if attributes.UltrasonicUnoccupiedToOccupiedThreshold.attribute_id in attribute_list: + has_delay = attributes.UltrasonicUnoccupiedToOccupiedDelay.attribute_id in attribute_list + has_threshold = attributes.UltrasonicUnoccupiedToOccupiedThreshold.attribute_id in attribute_list + asserts.assert_equal(has_delay, has_threshold, "UltrasonicUnoccupiedToOccupiedThreshold conformance failure") + + ultrasonic_utoo_threshold_dut = await self.read_occ_attribute_expect_success(endpoint=endpoint, attribute=attributes.UltrasonicUnoccupiedToOccupiedThreshold) + asserts.assert_less_equal(ultrasonic_utoo_threshold_dut, 0xFE, + "UltrasonicUnoccupiedToOccupiedThreshold is not in valid range") + asserts.assert_greater_equal(ultrasonic_utoo_threshold_dut, 0, + "UltrasonicUnoccupiedToOccupiedThreshold is not in valid range") + + else: + logging.info("UltrasonicUnoccupiedToOccupiedThreshold not supported. Test step skipped") + self.mark_current_step_skipped() + + self.step(13) + if attributes.PhysicalContactOccupiedToUnoccupiedDelay.attribute_id in attribute_list: + has_phycon_bitmap = (occupancy_sensor_type_bitmap_dut & + Clusters.OccupancySensing.Enums.OccupancySensorTypeEnum.kPhysicalContact) != 0 + has_phycon_delay = attributes.PhysicalContactOccupiedToUnoccupiedDelay.attribute_id in attribute_list + asserts.assert_equal(has_phycon_bitmap, has_phycon_delay, "Bad conformance on PhysicalContact bitmap") + phycontact_otou_delay_dut = await self.read_occ_attribute_expect_success(endpoint=endpoint, attribute=attributes.PhysicalContactOccupiedToUnoccupiedDelay) + asserts.assert_less_equal(phycontact_otou_delay_dut, 0xFFFE, + "PhysicalContactOccupiedToUnoccupiedDelay is not in valid range") + asserts.assert_greater_equal(phycontact_otou_delay_dut, 0, + "PhysicalContactOccupiedToUnoccupiedDelay is not in valid range") + + else: + logging.info("PhysicalContactOccupiedToUnoccupiedDelay not supported. Test step skipped") + self.mark_current_step_skipped() + + self.step(14) + if attributes.PhysicalContactUnoccupiedToOccupiedDelay.attribute_id in attribute_list: + has_delay = attributes.PhysicalContactUnoccupiedToOccupiedDelay.attribute_id in attribute_list + has_threshold = attributes.PhysicalContactUnoccupiedToOccupiedThreshold.attribute_id in attribute_list + asserts.assert_equal(has_delay, has_threshold, "PhysicalContactUnoccupiedToOccupiedDelay conformance failure") + + phycontact_utoo_delay_dut = await self.read_occ_attribute_expect_success(endpoint=endpoint, attribute=attributes.PhysicalContactUnoccupiedToOccupiedDelay) + asserts.assert_less_equal(phycontact_utoo_delay_dut, 0xFFFE, + "PhysicalContactUnoccupiedToOccupiedDelay is not in valid range") + asserts.assert_greater_equal(phycontact_utoo_delay_dut, 0, + "PhysicalContactUnoccupiedToOccupiedDelay is not in valid range") + + else: + logging.info("PhysicalContactUnoccupiedToOccupiedDelay not supported. Test step skipped") + self.mark_current_step_skipped() + + self.step(15) + if attributes.PhysicalContactUnoccupiedToOccupiedThreshold.attribute_id in attribute_list: + has_delay = attributes.PhysicalContactUnoccupiedToOccupiedDelay.attribute_id in attribute_list + has_threshold = attributes.PhysicalContactUnoccupiedToOccupiedThreshold.attribute_id in attribute_list + asserts.assert_equal(has_delay, has_threshold, "PhysicalContactUnoccupiedToOccupiedThreshold conformance failure") + + phycontact_utoo_threshold_dut = await self.read_occ_attribute_expect_success(endpoint=endpoint, attribute=attributes.PhysicalContactUnoccupiedToOccupiedThreshold) + asserts.assert_less_equal(phycontact_utoo_threshold_dut, 0xFE, + "PhysicalContactUnoccupiedToOccupiedThreshold is not in valid range") + asserts.assert_greater_equal(phycontact_utoo_threshold_dut, 0, + "PhysicalContactUnoccupiedToOccupiedThreshold is not in valid range") + + else: + logging.info("PhysicalContactUnoccupiedToOccupiedThreshold not supported. Test step skipped") + self.mark_current_step_skipped() + + +if __name__ == "__main__": + default_matter_test_main() diff --git a/src/python_testing/TC_OCC_2_2.py b/src/python_testing/TC_OCC_2_2.py index e27bbf30ebcade..9914452342cef3 100644 --- a/src/python_testing/TC_OCC_2_2.py +++ b/src/python_testing/TC_OCC_2_2.py @@ -1,132 +1,132 @@ -# -# Copyright (c) 2024 Project CHIP Authors -# All rights reserved. -# -# 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. -# - -# See https://github.com/project-chip/connectedhomeip/blob/master/docs/testing/python.md#defining-the-ci-test-arguments -# for details about the block below. -# -# === BEGIN CI TEST ARGUMENTS === -# test-runner-runs: run1 -# test-runner-run/run1/app: ${ALL_CLUSTERS_APP} -# test-runner-run/run1/factoryreset: True -# test-runner-run/run1/quiet: True -# test-runner-run/run1/app-args: --discriminator 1234 --KVS kvs1 --trace-to json:${TRACE_APP}.json -# test-runner-run/run1/script-args: --storage-path admin_storage.json --commissioning-method on-network --discriminator 1234 --passcode 20202021 --PICS src/app/tests/suites/certification/ci-pics-values --trace-to json:${TRACE_TEST_JSON}.json --trace-to perfetto:${TRACE_TEST_PERFETTO}.perfetto -# === END CI TEST ARGUMENTS === - -import chip.clusters as Clusters -from matter_testing_support import MatterBaseTest, TestStep, async_test_body, default_matter_test_main -from mobly import asserts - - -class TC_OCC_2_2(MatterBaseTest): - async def read_occ_attribute_expect_success(self, endpoint, attribute): - cluster = Clusters.Objects.OccupancySensing - return await self.read_single_attribute_check_success(endpoint=endpoint, cluster=cluster, attribute=attribute) - - def desc_TC_OCC_2_2(self) -> str: - return "[TC-OCC-2.2] OccupancySensorTypeBitmap and OccupancySensorType interdependency with server as DUT" - - def steps_TC_OCC_2_2(self) -> list[TestStep]: - steps = [ - TestStep(1, "Commissioning, already done", is_commissioning=True), - TestStep(2, "Read OccupancySensorType attribute selection based on FeatureMap Bitmap."), - TestStep(3, "Read OccupancySensorTypeBitmap attribute selection based on FeatureMap Bitmap.") - ] - return steps - - def pics_TC_OCC_2_2(self) -> list[str]: - pics = [ - "OCC.S", - ] - return pics - - @async_test_body - async def test_TC_OCC_2_2(self): - - endpoint = self.user_params.get("endpoint", 1) - - attributes = Clusters.OccupancySensing.Attributes - feature_map = await self.read_occ_attribute_expect_success(endpoint=endpoint, attribute=attributes.FeatureMap) - - self.step(1) - attribute_list = await self.read_occ_attribute_expect_success(endpoint=endpoint, attribute=attributes.AttributeList) - - self.step(2) - # OccupancySensorType will be determined by FeatureMap matching table at 2.7.6.2. - asserts.assert_in(attributes.OccupancySensorType.attribute_id, attribute_list, - "OccupancySensorType attribute is a mandatory attribute.") - occupancy_sensor_type_dut = await self.read_occ_attribute_expect_success(endpoint=endpoint, attribute=attributes.OccupancySensorType) - - # For validation purposes, 2.7.6.2 table describes what feature flags map to what type of sensors - TypeEnum = Clusters.OccupancySensing.Enums.OccupancySensorTypeEnum - - Y = True - N = False - # Map is PIR, US, PHY => expected sensor type - # odd Y/N mapping to make the table align nicely - mappings = { - (N, N, N): TypeEnum.kPir, - (Y, N, N): TypeEnum.kPir, - (N, Y, N): TypeEnum.kUltrasonic, - (Y, Y, N): TypeEnum.kPIRAndUltrasonic, - (N, N, Y): TypeEnum.kPhysicalContact, - (Y, N, Y): TypeEnum.kPir, - (N, Y, Y): TypeEnum.kUltrasonic, - (Y, Y, Y): TypeEnum.kPIRAndUltrasonic, - } - - FeatureBit = Clusters.OccupancySensing.Bitmaps.Feature - expected = mappings.get( - ( - (feature_map & FeatureBit.kPassiveInfrared) != 0, - (feature_map & FeatureBit.kUltrasonic) != 0, - (feature_map & FeatureBit.kPhysicalContact) != 0 - )) - - asserts.assert_equal( - occupancy_sensor_type_dut, - expected, - f"Sensor Type should be f{expected}" - ) - - self.step(3) - # OccupancySensorTypeBitmap will be determined by FeatureMap matching table at 2.7.6.2. - asserts.assert_in(attributes.OccupancySensorTypeBitmap.attribute_id, attribute_list, - "OccupancySensorTypeBitmap attribute is a mandatory attribute.") - - occupancy_sensor_type_bitmap_dut = await self.read_occ_attribute_expect_success(endpoint=endpoint, attribute=attributes.OccupancySensorTypeBitmap) - - # Feature map must match the sensor type bitmap - must_match_bits = [ - (Clusters.OccupancySensing.Bitmaps.OccupancySensorTypeBitmap.kPir, - Clusters.OccupancySensing.Bitmaps.Feature.kPassiveInfrared, "PIR"), - (Clusters.OccupancySensing.Bitmaps.OccupancySensorTypeBitmap.kUltrasonic, - Clusters.OccupancySensing.Bitmaps.Feature.kUltrasonic, "Ultrasonic"), - (Clusters.OccupancySensing.Bitmaps.OccupancySensorTypeBitmap.kPhysicalContact, - Clusters.OccupancySensing.Bitmaps.Feature.kPhysicalContact, "Physical contact"), - ] - - for sensor_bit, feature_bit, name in must_match_bits: - asserts.assert_equal( - (occupancy_sensor_type_bitmap_dut & sensor_bit) != 0, - (feature_map & feature_bit) != 0, - f"Feature bit and sensor bitmap must be equal for {name} (BITMAP: 0x{occupancy_sensor_type_bitmap_dut:02X}, FEATUREMAP: 0x{feature_map:02X})" - ) - - -if __name__ == "__main__": - default_matter_test_main() +# +# Copyright (c) 2024 Project CHIP Authors +# All rights reserved. +# +# 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. +# + +# See https://github.com/project-chip/connectedhomeip/blob/master/docs/testing/python.md#defining-the-ci-test-arguments +# for details about the block below. +# +# === BEGIN CI TEST ARGUMENTS === +# test-runner-runs: run1 +# test-runner-run/run1/app: ${ALL_CLUSTERS_APP} +# test-runner-run/run1/factoryreset: True +# test-runner-run/run1/quiet: True +# test-runner-run/run1/app-args: --discriminator 1234 --KVS kvs1 --trace-to json:${TRACE_APP}.json +# test-runner-run/run1/script-args: --storage-path admin_storage.json --commissioning-method on-network --discriminator 1234 --passcode 20202021 --PICS src/app/tests/suites/certification/ci-pics-values --trace-to json:${TRACE_TEST_JSON}.json --trace-to perfetto:${TRACE_TEST_PERFETTO}.perfetto --endpoint 1 +# === END CI TEST ARGUMENTS === + +import chip.clusters as Clusters +from matter_testing_support import MatterBaseTest, TestStep, async_test_body, default_matter_test_main +from mobly import asserts + + +class TC_OCC_2_2(MatterBaseTest): + async def read_occ_attribute_expect_success(self, endpoint, attribute): + cluster = Clusters.Objects.OccupancySensing + return await self.read_single_attribute_check_success(endpoint=endpoint, cluster=cluster, attribute=attribute) + + def desc_TC_OCC_2_2(self) -> str: + return "[TC-OCC-2.2] OccupancySensorTypeBitmap and OccupancySensorType interdependency with server as DUT" + + def steps_TC_OCC_2_2(self) -> list[TestStep]: + steps = [ + TestStep(1, "Commissioning, already done", is_commissioning=True), + TestStep(2, "Read OccupancySensorType attribute selection based on FeatureMap Bitmap."), + TestStep(3, "Read OccupancySensorTypeBitmap attribute selection based on FeatureMap Bitmap.") + ] + return steps + + def pics_TC_OCC_2_2(self) -> list[str]: + pics = [ + "OCC.S", + ] + return pics + + @async_test_body + async def test_TC_OCC_2_2(self): + endpoint = self.matter_test_config.endpoint + + self.step(1) # Already done, immediately go to step 2 + + self.step(2) + + attributes = Clusters.OccupancySensing.Attributes + feature_map = await self.read_occ_attribute_expect_success(endpoint=endpoint, attribute=attributes.FeatureMap) + attribute_list = await self.read_occ_attribute_expect_success(endpoint=endpoint, attribute=attributes.AttributeList) + + # OccupancySensorType will be determined by FeatureMap matching table at 2.7.6.2. + asserts.assert_in(attributes.OccupancySensorType.attribute_id, attribute_list, + "OccupancySensorType attribute is a mandatory attribute.") + occupancy_sensor_type_dut = await self.read_occ_attribute_expect_success(endpoint=endpoint, attribute=attributes.OccupancySensorType) + + # For validation purposes, 2.7.6.2 table describes what feature flags map to what type of sensors + TypeEnum = Clusters.OccupancySensing.Enums.OccupancySensorTypeEnum + + Y = True + N = False + # Map is PIR, US, PHY => expected sensor type + # odd Y/N mapping to make the table align nicely + mappings = { + (N, N, N): TypeEnum.kPir, + (Y, N, N): TypeEnum.kPir, + (N, Y, N): TypeEnum.kUltrasonic, + (Y, Y, N): TypeEnum.kPIRAndUltrasonic, + (N, N, Y): TypeEnum.kPhysicalContact, + (Y, N, Y): TypeEnum.kPir, + (N, Y, Y): TypeEnum.kUltrasonic, + (Y, Y, Y): TypeEnum.kPIRAndUltrasonic, + } + + FeatureBit = Clusters.OccupancySensing.Bitmaps.Feature + expected = mappings.get( + ( + (feature_map & FeatureBit.kPassiveInfrared) != 0, + (feature_map & FeatureBit.kUltrasonic) != 0, + (feature_map & FeatureBit.kPhysicalContact) != 0 + )) + + asserts.assert_equal( + occupancy_sensor_type_dut, + expected, + f"Sensor Type should be f{expected}" + ) + + self.step(3) + # OccupancySensorTypeBitmap will be determined by FeatureMap matching table at 2.7.6.2. + asserts.assert_in(attributes.OccupancySensorTypeBitmap.attribute_id, attribute_list, + "OccupancySensorTypeBitmap attribute is a mandatory attribute.") + + occupancy_sensor_type_bitmap_dut = await self.read_occ_attribute_expect_success(endpoint=endpoint, attribute=attributes.OccupancySensorTypeBitmap) + + # Feature map must match the sensor type bitmap + must_match_bits = [ + (Clusters.OccupancySensing.Bitmaps.OccupancySensorTypeBitmap.kPir, + Clusters.OccupancySensing.Bitmaps.Feature.kPassiveInfrared, "PIR"), + (Clusters.OccupancySensing.Bitmaps.OccupancySensorTypeBitmap.kUltrasonic, + Clusters.OccupancySensing.Bitmaps.Feature.kUltrasonic, "Ultrasonic"), + (Clusters.OccupancySensing.Bitmaps.OccupancySensorTypeBitmap.kPhysicalContact, + Clusters.OccupancySensing.Bitmaps.Feature.kPhysicalContact, "Physical contact"), + ] + + for sensor_bit, feature_bit, name in must_match_bits: + asserts.assert_equal( + (occupancy_sensor_type_bitmap_dut & sensor_bit) != 0, + (feature_map & feature_bit) != 0, + f"Feature bit and sensor bitmap must be equal for {name} (BITMAP: 0x{occupancy_sensor_type_bitmap_dut:02X}, FEATUREMAP: 0x{feature_map:02X})" + ) + + +if __name__ == "__main__": + default_matter_test_main() diff --git a/src/python_testing/TC_OCC_2_3.py b/src/python_testing/TC_OCC_2_3.py index 0184b670c6b306..63e973b159ca37 100644 --- a/src/python_testing/TC_OCC_2_3.py +++ b/src/python_testing/TC_OCC_2_3.py @@ -1,127 +1,128 @@ -# -# Copyright (c) 2024 Project CHIP Authors -# All rights reserved. -# -# 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. -# - -# See https://github.com/project-chip/connectedhomeip/blob/master/docs/testing/python.md#defining-the-ci-test-arguments -# for details about the block below. -# -# === BEGIN CI TEST ARGUMENTS === -# test-runner-runs: run1 -# test-runner-run/run1/app: ${ALL_CLUSTERS_APP} -# test-runner-run/run1/factoryreset: True -# test-runner-run/run1/quiet: True -# test-runner-run/run1/app-args: --discriminator 1234 --KVS kvs1 --trace-to json:${TRACE_APP}.json -# test-runner-run/run1/script-args: --storage-path admin_storage.json --commissioning-method on-network --discriminator 1234 --passcode 20202021 --PICS src/app/tests/suites/certification/ci-pics-values --trace-to json:${TRACE_TEST_JSON}.json --trace-to perfetto:${TRACE_TEST_PERFETTO}.perfetto -# === END CI TEST ARGUMENTS === - -import logging - -import chip.clusters as Clusters -from matter_testing_support import MatterBaseTest, TestStep, async_test_body, default_matter_test_main -from mobly import asserts - - -class TC_OCC_2_3(MatterBaseTest): - async def read_occ_attribute_expect_success(self, endpoint, attribute): - cluster = Clusters.Objects.OccupancySensing - return await self.read_single_attribute_check_success(endpoint=endpoint, cluster=cluster, attribute=attribute) - - def desc_TC_OCC_2_3(self) -> str: - return "[TC-OCC-2.3] HoldTime Backward Compatibility Test with server as DUT" - - def steps_TC_OCC_2_3(self) -> list[TestStep]: - steps = [ - TestStep(1, "Commission DUT to TH", is_commissioning=True), - TestStep(2, "DUT supports HoldTime attribute. If DUT doesn’t support it, then stop and exit this test case."), - TestStep(3, "Based on the feature flag value table, read OccupancySensorType attribute from DUT"), - TestStep(4, "If TH reads 0 - PIR, TH reads PIROccupiedToUnoccupiedDelay attribute and its value should be same as HoldTime"), - TestStep(5, "If TH reads 1 - Ultrasonic, TH reads UltrasonicOccupiedToUnoccupiedDelay attribute and its value should be same as HoldTime"), - TestStep(6, "If TH reads 2 - PHY, TH reads PhysicalContactOccupiedToUnoccupiedDelay attribute and its value should be same as HoldTime") - ] - return steps - - def pics_TC_OCC_2_3(self) -> list[str]: - pics = [ - "OCC.S", - ] - return pics - - @async_test_body - async def test_TC_OCC_2_3(self): - - endpoint = self.user_params.get("endpoint", 1) - - self.step(1) - attributes = Clusters.OccupancySensing.Attributes - attribute_list = await self.read_occ_attribute_expect_success(endpoint=endpoint, attribute=attributes.AttributeList) - - self.step(2) - if attributes.HoldTime.attribute_id in attribute_list: - occupancy_hold_time_dut = await self.read_occ_attribute_expect_success(endpoint=endpoint, attribute=attributes.HoldTime) - else: - logging.info("No HoldTime attribute supports. Terminate this test case") - - self.step(3) - if attributes.OccupancySensorType.attribute_id in attribute_list: - occupancy_sensor_type_dut = await self.read_occ_attribute_expect_success(endpoint=endpoint, attribute=attributes.OccupancySensorType) - - asserts.assert_less_equal(occupancy_sensor_type_dut, 3, "OccupancySensorType attribute is out of range") - asserts.assert_greater_equal(occupancy_sensor_type_dut, 0, "OccupancySensorType attribute is out of range") - else: - logging.info("OccupancySensorType attribute doesn't exist. Test step skipped") - - if occupancy_sensor_type_dut == Clusters.OccupancySensing.Enums.OccupancySensorTypeEnum.kPir: - self.step(4) - occupancy_pir_otou_delay_dut = await self.read_occ_attribute_expect_success(endpoint=endpoint, attribute=attributes.PIROccupiedToUnoccupiedDelay) - - asserts.assert_equal(occupancy_pir_otou_delay_dut, occupancy_hold_time_dut, - "HoldTime attribute value is not equal to PIROccupiedToUnoccupiedDelay") - self.skip_step(5) - self.skip_step(6) - - elif occupancy_sensor_type_dut == Clusters.OccupancySensing.Enums.OccupancySensorTypeEnum.kUltrasonic: - self.step(4) - occupancy_pir_otou_delay_dut = await self.read_occ_attribute_expect_success(endpoint=endpoint, attribute=attributes.PIROccupiedToUnoccupiedDelay) - - asserts.assert_equal(occupancy_pir_otou_delay_dut, occupancy_hold_time_dut, - "HoldTime attribute value is not equal to PIROccupiedToUnoccupiedDelay") - self.step(5) - occupancy_us_otou_delay_dut = await self.read_occ_attribute_expect_success(endpoint=endpoint, attribute=attributes.UltrasonicOccupiedToUnoccupiedDelay) - - asserts.assert_equal(occupancy_us_otou_delay_dut, occupancy_hold_time_dut, - "HoldTime attribute value is not equal to UltrasonicOccupiedToUnoccupiedDelay") - self.skip_step(6) - - elif occupancy_sensor_type_dut == Clusters.OccupancySensing.Enums.OccupancySensorTypeEnum.kPIRAndUltrasonic: - occupancy_pirus_otou_delay_dut = await self.read_occ_attribute_expect_success(endpoint=endpoint, attribute=attributes.PIROccupiedToUnoccupiedDelay) - - asserts.assert_equal(occupancy_pirus_otou_delay_dut, occupancy_hold_time_dut, - "HoldTime attribute value is not equal to PIROccupiedToUnoccupiedDelay") - - elif occupancy_sensor_type_dut == Clusters.OccupancySensing.Enums.OccupancySensorTypeEnum.kPhysicalContact: - self.skip_step(4) - self.skip_step(5) - self.step(6) - occupancy_phy_otou_delay_dut = await self.read_occ_attribute_expect_success(endpoint=endpoint, attribute=attributes.PhysicalContactOccupiedToUnoccupiedDelay) - - asserts.assert_equal(occupancy_phy_otou_delay_dut, occupancy_hold_time_dut, - "HoldTime attribute value is not equal to PhysicalContactOccupiedToUnoccupiedDelay") - else: - logging.info("OccupancySensorType attribute value is out of range") - - -if __name__ == "__main__": - default_matter_test_main() +# +# Copyright (c) 2024 Project CHIP Authors +# All rights reserved. +# +# 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. +# + +# See https://github.com/project-chip/connectedhomeip/blob/master/docs/testing/python.md#defining-the-ci-test-arguments +# for details about the block below. +# +# === BEGIN CI TEST ARGUMENTS === +# test-runner-runs: run1 +# test-runner-run/run1/app: ${ALL_CLUSTERS_APP} +# test-runner-run/run1/factoryreset: True +# test-runner-run/run1/quiet: True +# test-runner-run/run1/app-args: --discriminator 1234 --KVS kvs1 --trace-to json:${TRACE_APP}.json +# test-runner-run/run1/script-args: --storage-path admin_storage.json --commissioning-method on-network --discriminator 1234 --passcode 20202021 --PICS src/app/tests/suites/certification/ci-pics-values --trace-to json:${TRACE_TEST_JSON}.json --trace-to perfetto:${TRACE_TEST_PERFETTO}.perfetto --endpoint 1 +# === END CI TEST ARGUMENTS === + +import logging + +import chip.clusters as Clusters +from matter_testing_support import MatterBaseTest, TestStep, async_test_body, default_matter_test_main +from mobly import asserts + + +class TC_OCC_2_3(MatterBaseTest): + async def read_occ_attribute_expect_success(self, endpoint, attribute): + cluster = Clusters.Objects.OccupancySensing + return await self.read_single_attribute_check_success(endpoint=endpoint, cluster=cluster, attribute=attribute) + + def desc_TC_OCC_2_3(self) -> str: + return "[TC-OCC-2.3] HoldTime Backward Compatibility Test with server as DUT" + + def steps_TC_OCC_2_3(self) -> list[TestStep]: + steps = [ + TestStep(1, "Commission DUT to TH", is_commissioning=True), + TestStep(2, "DUT supports HoldTime attribute. If DUT doesn’t support it, then stop and exit this test case."), + TestStep(3, "Based on the feature flag value table, read OccupancySensorType attribute from DUT"), + TestStep(4, "If TH reads 0 - PIR, TH reads PIROccupiedToUnoccupiedDelay attribute and its value should be same as HoldTime"), + TestStep(5, "If TH reads 1 - Ultrasonic, TH reads UltrasonicOccupiedToUnoccupiedDelay attribute and its value should be same as HoldTime"), + TestStep(6, "If TH reads 2 - PHY, TH reads PhysicalContactOccupiedToUnoccupiedDelay attribute and its value should be same as HoldTime") + ] + return steps + + def pics_TC_OCC_2_3(self) -> list[str]: + pics = [ + "OCC.S", + ] + return pics + + @async_test_body + async def test_TC_OCC_2_3(self): + endpoint = self.matter_test_config.endpoint + + self.step(1) # Already done, immediately go to step 2 + + self.step(2) + + attributes = Clusters.OccupancySensing.Attributes + attribute_list = await self.read_occ_attribute_expect_success(endpoint=endpoint, attribute=attributes.AttributeList) + + if attributes.HoldTime.attribute_id in attribute_list: + occupancy_hold_time_dut = await self.read_occ_attribute_expect_success(endpoint=endpoint, attribute=attributes.HoldTime) + else: + logging.info("No HoldTime attribute supports. Terminate this test case") + + self.step(3) + if attributes.OccupancySensorType.attribute_id in attribute_list: + occupancy_sensor_type_dut = await self.read_occ_attribute_expect_success(endpoint=endpoint, attribute=attributes.OccupancySensorType) + + asserts.assert_less_equal(occupancy_sensor_type_dut, 3, "OccupancySensorType attribute is out of range") + asserts.assert_greater_equal(occupancy_sensor_type_dut, 0, "OccupancySensorType attribute is out of range") + else: + logging.info("OccupancySensorType attribute doesn't exist. Test step skipped") + + if occupancy_sensor_type_dut == Clusters.OccupancySensing.Enums.OccupancySensorTypeEnum.kPir: + self.step(4) + occupancy_pir_otou_delay_dut = await self.read_occ_attribute_expect_success(endpoint=endpoint, attribute=attributes.PIROccupiedToUnoccupiedDelay) + + asserts.assert_equal(occupancy_pir_otou_delay_dut, occupancy_hold_time_dut, + "HoldTime attribute value is not equal to PIROccupiedToUnoccupiedDelay") + self.skip_step(5) + self.skip_step(6) + + elif occupancy_sensor_type_dut == Clusters.OccupancySensing.Enums.OccupancySensorTypeEnum.kUltrasonic: + self.step(4) + occupancy_pir_otou_delay_dut = await self.read_occ_attribute_expect_success(endpoint=endpoint, attribute=attributes.PIROccupiedToUnoccupiedDelay) + + asserts.assert_equal(occupancy_pir_otou_delay_dut, occupancy_hold_time_dut, + "HoldTime attribute value is not equal to PIROccupiedToUnoccupiedDelay") + self.step(5) + occupancy_us_otou_delay_dut = await self.read_occ_attribute_expect_success(endpoint=endpoint, attribute=attributes.UltrasonicOccupiedToUnoccupiedDelay) + + asserts.assert_equal(occupancy_us_otou_delay_dut, occupancy_hold_time_dut, + "HoldTime attribute value is not equal to UltrasonicOccupiedToUnoccupiedDelay") + self.skip_step(6) + + elif occupancy_sensor_type_dut == Clusters.OccupancySensing.Enums.OccupancySensorTypeEnum.kPIRAndUltrasonic: + occupancy_pirus_otou_delay_dut = await self.read_occ_attribute_expect_success(endpoint=endpoint, attribute=attributes.PIROccupiedToUnoccupiedDelay) + + asserts.assert_equal(occupancy_pirus_otou_delay_dut, occupancy_hold_time_dut, + "HoldTime attribute value is not equal to PIROccupiedToUnoccupiedDelay") + + elif occupancy_sensor_type_dut == Clusters.OccupancySensing.Enums.OccupancySensorTypeEnum.kPhysicalContact: + self.skip_step(4) + self.skip_step(5) + self.step(6) + occupancy_phy_otou_delay_dut = await self.read_occ_attribute_expect_success(endpoint=endpoint, attribute=attributes.PhysicalContactOccupiedToUnoccupiedDelay) + + asserts.assert_equal(occupancy_phy_otou_delay_dut, occupancy_hold_time_dut, + "HoldTime attribute value is not equal to PhysicalContactOccupiedToUnoccupiedDelay") + else: + logging.info("OccupancySensorType attribute value is out of range") + + +if __name__ == "__main__": + default_matter_test_main() diff --git a/src/python_testing/TC_OCC_3_1.py b/src/python_testing/TC_OCC_3_1.py index 3fd082a62bc974..246e3a13f2a211 100644 --- a/src/python_testing/TC_OCC_3_1.py +++ b/src/python_testing/TC_OCC_3_1.py @@ -1,134 +1,129 @@ -# -# Copyright (c) 2024 Project CHIP Authors -# All rights reserved. -# -# 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. -# -# === BEGIN CI TEST ARGUMENTS === -# test-runner-runs: run1 -# test-runner-run/run1/app: ${ALL_CLUSTERS_APP} -# test-runner-run/run1/factoryreset: True -# test-runner-run/run1/quiet: True -# test-runner-run/run1/app-args: --discriminator 1234 --KVS kvs1 --trace-to json:${TRACE_APP}.json -# test-runner-run/run1/script-args: --storage-path admin_storage.json --commissioning-method on-network --discriminator 1234 --passcode 20202021 --trace-to json:${TRACE_TEST_JSON}.json --trace-to perfetto:${TRACE_TEST_PERFETTO}.perfetto --endpoint 1 -# === END CI TEST ARGUMENTS === -# There are CI issues to be followed up for the test cases below that implements manually controlling sensor device for -# the occupancy state ON/OFF change. -# [TC-OCC-3.1] test procedure step 4 -# [TC-OCC-3.2] test precedure step 3c - -import logging -import time -from typing import Any, Optional - -import chip.clusters as Clusters -from chip.interaction_model import Status -from matter_testing_support import MatterBaseTest, TestStep, async_test_body, default_matter_test_main -from mobly import asserts - - -class TC_OCC_3_1(MatterBaseTest): - async def read_occ_attribute_expect_success(self, attribute): - cluster = Clusters.Objects.OccupancySensing - endpoint = self.matter_test_config.endpoint - return await self.read_single_attribute_check_success(endpoint=endpoint, cluster=cluster, attribute=attribute) - - async def write_hold_time(self, hold_time: Optional[Any]) -> Status: - dev_ctrl = self.default_controller - node_id = self.dut_node_id - endpoint = self.matter_test_config.endpoint - - cluster = Clusters.OccupancySensing - write_result = await dev_ctrl.WriteAttribute(node_id, [(endpoint, cluster.Attributes.HoldTime(hold_time))]) - return write_result[0].Status - - def desc_TC_OCC_3_1(self) -> str: - return "[TC-OCC-3.1] Primary functionality with server as DUT" - - def steps_TC_OCC_3_1(self) -> list[TestStep]: - steps = [ - TestStep(1, "Commission DUT to TH and obtain DUT attribute list.", is_commissioning=True), - TestStep(2, "Change DUT HoldTime attribute value to 10 seconds."), - TestStep(3, "Do not trigger DUT occupancy sensing for the period of HoldTime. TH reads Occupancy attribute from DUT."), - TestStep(4, "Trigger DUT occupancy sensing to change the occupancy state and start a timer."), - TestStep(5, "After 10 seconds, TH reads Occupancy attribute from DUT.") - ] - return steps - - def pics_TC_OCC_3_1(self) -> list[str]: - pics = [ - "OCC.S", - ] - return pics - - @async_test_body - async def test_TC_OCC_3_1(self): - hold_time = 10 # 10 seconds for occupancy state hold time - - self.step(1) # commissioning and getting cluster attribute list - cluster = Clusters.OccupancySensing - attributes = cluster.Attributes - attribute_list = await self.read_occ_attribute_expect_success(attribute=attributes.AttributeList) - - has_hold_time = attributes.HoldTime.attribute_id in attribute_list - - self.step(2) - if has_hold_time: - # write 10 as a HoldTime attribute - await self.write_single_attribute(cluster.Attributes.HoldTime(hold_time)) - else: - logging.info("No HoldTime attribute supports. Will test only occupancy attribute triggering functionality") - - self.step(3) - # check if Occupancy attribute is 0 - occupancy_dut = await self.read_occ_attribute_expect_success(attribute=attributes.Occupancy) - - # if occupancy is on, then wait until the sensor occupancy state is 0. - if occupancy_dut == 1: - # Don't trigger occupancy sensor to render occupancy attribute to 0 - if has_hold_time: - time.sleep(hold_time + 2.0) # add some extra 2 seconds to ensure hold time has passed. - else: # a user wait until a sensor specific time to change occupancy attribute to 0. This is the case where the sensor doesn't support HoldTime. - self.wait_for_user_input( - prompt_msg="Type any letter and press ENTER after the sensor occupancy is detection ready state (occupancy attribute = 0)") - - # check sensor occupancy state is 0 for the next test step - occupancy_dut = await self.read_occ_attribute_expect_success(attribute=attributes.Occupancy) - asserts.assert_equal(occupancy_dut, 0, "Occupancy attribute is still 1.") - - self.step(4) - # Trigger occupancy sensor to change Occupancy attribute value to 1 => TESTER ACTION on DUT - self.wait_for_user_input(prompt_msg="Type any letter and press ENTER after a sensor occupancy is triggered.") - - # And then check if Occupancy attribute has changed. - occupancy_dut = await self.read_occ_attribute_expect_success(attribute=attributes.Occupancy) - asserts.assert_equal(occupancy_dut, 1, "Occupancy state is not changed to 1") - - self.step(5) - # check if Occupancy attribute is back to 0 after HoldTime attribute period - # Tester should not be triggering the sensor for this test step. - if has_hold_time: - - # Start a timer based on HoldTime - time.sleep(hold_time + 2.0) # add some extra 2 seconds to ensure hold time has passed. - - occupancy_dut = await self.read_occ_attribute_expect_success(attribute=attributes.Occupancy) - asserts.assert_equal(occupancy_dut, 0, "Occupancy state is not 0 after HoldTime period") - - else: - logging.info("HoldTime attribute not supported. Skip this return to 0 timing test procedure.") - self.skip_step(5) - - -if __name__ == "__main__": - default_matter_test_main() +# +# Copyright (c) 2024 Project CHIP Authors +# All rights reserved. +# +# 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. +# + +# There are CI issues to be followed up for the test cases below that implements manually controlling sensor device for +# the occupancy state ON/OFF change. +# [TC-OCC-3.1] test procedure step 4 +# [TC-OCC-3.2] test precedure step 3c + +import logging +import time +from typing import Any, Optional + +import chip.clusters as Clusters +from chip.interaction_model import Status +from matter_testing_support import MatterBaseTest, TestStep, async_test_body, default_matter_test_main +from mobly import asserts + + +class TC_OCC_3_1(MatterBaseTest): + async def read_occ_attribute_expect_success(self, attribute): + cluster = Clusters.Objects.OccupancySensing + endpoint = self.matter_test_config.endpoint + return await self.read_single_attribute_check_success(endpoint=endpoint, cluster=cluster, attribute=attribute) + + async def write_hold_time(self, hold_time: Optional[Any]) -> Status: + dev_ctrl = self.default_controller + node_id = self.dut_node_id + endpoint = self.matter_test_config.endpoint + + cluster = Clusters.OccupancySensing + write_result = await dev_ctrl.WriteAttribute(node_id, [(endpoint, cluster.Attributes.HoldTime(hold_time))]) + return write_result[0].Status + + def desc_TC_OCC_3_1(self) -> str: + return "[TC-OCC-3.1] Primary functionality with server as DUT" + + def steps_TC_OCC_3_1(self) -> list[TestStep]: + steps = [ + TestStep(1, "Commission DUT to TH and obtain DUT attribute list.", is_commissioning=True), + TestStep(2, "Change DUT HoldTime attribute value to 10 seconds."), + TestStep(3, "Do not trigger DUT occupancy sensing for the period of HoldTime. TH reads Occupancy attribute from DUT."), + TestStep(4, "Trigger DUT occupancy sensing to change the occupancy state and start a timer."), + TestStep(5, "After 10 seconds, TH reads Occupancy attribute from DUT.") + ] + return steps + + def pics_TC_OCC_3_1(self) -> list[str]: + pics = [ + "OCC.S", + ] + return pics + + @async_test_body + async def test_TC_OCC_3_1(self): + hold_time = 10 # 10 seconds for occupancy state hold time + + self.step(1) # Commissioning already done + + self.step(2) + + cluster = Clusters.OccupancySensing + attributes = cluster.Attributes + attribute_list = await self.read_occ_attribute_expect_success(attribute=attributes.AttributeList) + + has_hold_time = attributes.HoldTime.attribute_id in attribute_list + + if has_hold_time: + # write 10 as a HoldTime attribute + await self.write_single_attribute(cluster.Attributes.HoldTime(hold_time)) + else: + logging.info("No HoldTime attribute supports. Will test only occupancy attribute triggering functionality") + + self.step(3) + # check if Occupancy attribute is 0 + occupancy_dut = await self.read_occ_attribute_expect_success(attribute=attributes.Occupancy) + + # if occupancy is on, then wait until the sensor occupancy state is 0. + if occupancy_dut == 1: + # Don't trigger occupancy sensor to render occupancy attribute to 0 + if has_hold_time: + time.sleep(hold_time + 2.0) # add some extra 2 seconds to ensure hold time has passed. + else: # a user wait until a sensor specific time to change occupancy attribute to 0. This is the case where the sensor doesn't support HoldTime. + self.wait_for_user_input( + prompt_msg="Type any letter and press ENTER after the sensor occupancy is detection ready state (occupancy attribute = 0)") + + # check sensor occupancy state is 0 for the next test step + occupancy_dut = await self.read_occ_attribute_expect_success(attribute=attributes.Occupancy) + asserts.assert_equal(occupancy_dut, 0, "Occupancy attribute is still 1.") + + self.step(4) + # Trigger occupancy sensor to change Occupancy attribute value to 1 => TESTER ACTION on DUT + self.wait_for_user_input(prompt_msg="Type any letter and press ENTER after a sensor occupancy is triggered.") + + # And then check if Occupancy attribute has changed. + occupancy_dut = await self.read_occ_attribute_expect_success(attribute=attributes.Occupancy) + asserts.assert_equal(occupancy_dut, 1, "Occupancy state is not changed to 1") + + self.step(5) + # check if Occupancy attribute is back to 0 after HoldTime attribute period + # Tester should not be triggering the sensor for this test step. + if has_hold_time: + + # Start a timer based on HoldTime + time.sleep(hold_time + 2.0) # add some extra 2 seconds to ensure hold time has passed. + + occupancy_dut = await self.read_occ_attribute_expect_success(attribute=attributes.Occupancy) + asserts.assert_equal(occupancy_dut, 0, "Occupancy state is not 0 after HoldTime period") + + else: + logging.info("HoldTime attribute not supported. Skip this return to 0 timing test procedure.") + self.skip_step(5) + + +if __name__ == "__main__": + default_matter_test_main() diff --git a/src/python_testing/TC_OCC_3_2.py b/src/python_testing/TC_OCC_3_2.py index 7e811362e2e2a4..64a588b6eb36e8 100644 --- a/src/python_testing/TC_OCC_3_2.py +++ b/src/python_testing/TC_OCC_3_2.py @@ -1,207 +1,204 @@ -# -# Copyright (c) 2024 Project CHIP (Matter) Authors -# All rights reserved. -# -# 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. -# -# See https://github.com/project-chip/connectedhomeip/blob/master/docs/testing/python.md#defining-the-ci-test-arguments -# for details about the block below. -# -# === BEGIN CI TEST ARGUMENTS === -# test-runner-runs: run1 -# test-runner-run/run1/app: ${ALL_CLUSTERS_APP} -# test-runner-run/run1/factoryreset: True -# test-runner-run/run1/quiet: True -# test-runner-run/run1/app-args: --discriminator 1234 --KVS kvs1 --trace-to json:${TRACE_APP}.json -# test-runner-run/run1/script-args: --storage-path admin_storage.json --commissioning-method on-network --discriminator 1234 --passcode 20202021 --trace-to json:${TRACE_TEST_JSON}.json --trace-to perfetto:${TRACE_TEST_PERFETTO}.perfetto --endpoint 1 -# === END CI TEST ARGUMENTS === - -# TODO: There are CI issues to be followed up for the test cases below that implements manually controlling sensor device for -# the occupancy state ON/OFF change. -# [TC-OCC-3.1] test procedure step 4 -# [TC-OCC-3.2] test precedure step 3a, 3c - -import logging - -import chip.clusters as Clusters -from matter_testing_support import (ClusterAttributeChangeAccumulator, MatterBaseTest, TestStep, async_test_body, - await_sequence_of_reports, default_matter_test_main) -from mobly import asserts - - -class TC_OCC_3_2(MatterBaseTest): - async def read_occ_attribute_expect_success(self, attribute): - cluster = Clusters.Objects.OccupancySensing - endpoint_id = self.matter_test_config.endpoint - return await self.read_single_attribute_check_success(endpoint=endpoint_id, cluster=cluster, attribute=attribute) - - def desc_TC_OCC_3_2(self) -> str: - return "[TC-OCC-3.2] Subscription Report Verification with server as DUT" - - def steps_TC_OCC_3_2(self) -> list[TestStep]: - steps = [ - TestStep(1, "Commission DUT to TH if not already done", is_commissioning=True), - TestStep(2, "TH establishes a wildcard subscription to all attributes on Occupancy Sensing Cluster on the endpoint under test. Subscription min interval = 0 and max interval = 30 seconds."), - TestStep("3a", "Do not trigger DUT for occupancy state change."), - TestStep("3b", "TH reads DUT Occupancy attribute and saves the initial value as initial"), - TestStep("3c", "Trigger DUT to change the occupancy state."), - TestStep("3d", "TH awaits a ReportDataMessage containing an attribute report for DUT Occupancy attribute."), - TestStep("4a", "Check if DUT supports HoldTime attribute, If not supported, then stop and skip the rest of test cases."), - TestStep("4b", "TH reads DUT HoldTime attribute and saves the initial value as initial"), - TestStep("4c", "TH writes a different value to DUT HoldTime attribute."), - TestStep("4d", "TH awaits a ReportDataMessage containing an attribute report for DUT HoldTime attribute."), - TestStep("5a", "Check if DUT supports DUT feature flag PIR or OTHER, If not supported, then stop and skip to 6a."), - TestStep("5b", "TH reads DUT PIROccupiedToUnoccupiedDelay attribute and saves the initial value as initial"), - TestStep("5c", "TH writes a different value to DUT PIROccupiedToUnoccupiedDelay attribute."), - TestStep("5d", "TH awaits a ReportDataMessage containing an attribute report for DUT PIROccupiedToUnoccupiedDelay attribute."), - TestStep("6a", "Check if DUT supports DUT feature flag US, If not supported, then stop and skip to 7a."), - TestStep("6b", "TH reads DUT UltrasonicOccupiedToUnoccupiedDelay attribute and saves the initial value as initial"), - TestStep("6c", "TH writes a different value to DUT UltrasonicOccupiedToUnoccupiedDelay attribute."), - TestStep("6d", "TH awaits a ReportDataMessage containing an attribute report for DUT UltrasonicOccupiedToUnoccupiedDelay attribute."), - TestStep("7a", "Check if DUT supports DUT feature flag PHY, If not supported, terminate this test case."), - TestStep("7b", "TH reads DUT PhysicalContactOccupiedToUnoccupiedDelay attribute and saves the initial value as initial"), - TestStep("7c", "TH writes a different value to DUT PhysicalContactOccupiedToUnoccupiedDelay attribute."), - TestStep("7d", "TH awaits a ReportDataMessage containing an attribute report for DUT PhysicalContactOccupiedToUnoccupiedDelay attribute.") - ] - return steps - - def pics_TC_OCC_3_2(self) -> list[str]: - pics = [ - "OCC.S", - ] - return pics - - @async_test_body - async def test_TC_OCC_3_2(self): - endpoint_id = self.matter_test_config.endpoint - node_id = self.dut_node_id - dev_ctrl = self.default_controller - - post_prompt_settle_delay_seconds = 10.0 - cluster = Clusters.Objects.OccupancySensing - attributes = cluster.Attributes - - self.step(1) - - occupancy_sensor_type_bitmap_dut = await self.read_occ_attribute_expect_success(attribute=attributes.OccupancySensorTypeBitmap) - has_type_pir = ((occupancy_sensor_type_bitmap_dut & cluster.Enums.OccupancySensorTypeEnum.kPir) != 0) or \ - ((occupancy_sensor_type_bitmap_dut & cluster.Enums.OccupancySensorTypeEnum.kPIRAndUltrasonic) != 0) - has_type_ultrasonic = ((occupancy_sensor_type_bitmap_dut & cluster.Enums.OccupancySensorTypeEnum.kUltrasonic) != 0) or \ - ((occupancy_sensor_type_bitmap_dut & cluster.Enums.OccupancySensorTypeEnum.kPIRAndUltrasonic) != 0) - has_type_contact = (occupancy_sensor_type_bitmap_dut & cluster.Enums.OccupancySensorTypeEnum.kPhysicalContact) != 0 - - attribute_list = await self.read_occ_attribute_expect_success(attribute=attributes.AttributeList) - has_pir_timing_attrib = attributes.PIROccupiedToUnoccupiedDelay.attribute_id in attribute_list - has_ultrasonic_timing_attrib = attributes.UltrasonicOccupiedToUnoccupiedDelay.attribute_id in attribute_list - has_contact_timing_attrib = attributes.PhysicalContactOccupiedToUnoccupiedDelay.attribute_id in attribute_list - - self.step(2) - # min interval = 0, and max interval = 30 seconds - attrib_listener = ClusterAttributeChangeAccumulator(Clusters.Objects.OccupancySensing) - await attrib_listener.start(dev_ctrl, node_id, endpoint=endpoint_id, min_interval_sec=0, max_interval_sec=30) - - # TODO - Will add Namepiped to assimilate the manual sensor untrigger here - self.step("3a") - self.wait_for_user_input(prompt_msg="Type any letter and press ENTER after DUT goes back to unoccupied state.") - - self.step("3b") - if attributes.Occupancy.attribute_id in attribute_list: - initial_dut = await self.read_occ_attribute_expect_success(attribute=attributes.Occupancy) - asserts.assert_equal(initial_dut, 0, "Occupancy attribute is still detected state") - - # TODO - Will add Namepiped to assimilate the manual sensor trigger here - self.step("3c") - self.wait_for_user_input( - prompt_msg="Type any letter and press ENTER after the sensor occupancy is triggered and its occupancy state changed.") - - self.step("3d") - await_sequence_of_reports(report_queue=attrib_listener.attribute_queue, endpoint_id=endpoint_id, attribute=cluster.Attributes.Occupancy, sequence=[ - 0, 1], timeout_sec=post_prompt_settle_delay_seconds) - - self.step("4a") - if attributes.HoldTime.attribute_id not in attribute_list: - logging.info("No HoldTime attribute supports. Terminate this test case") - self.skip_all_remaining_steps("4b") - - self.step("4b") - initial_dut = await self.read_occ_attribute_expect_success(attribute=attributes.HoldTime) - - self.step("4c") - # write a different a HoldTime attribute value - diff_val = 12 - await self.write_single_attribute(attributes.HoldTime(diff_val)) - - self.step("4d") - await_sequence_of_reports(report_queue=attrib_listener.attribute_queue, endpoint_id=endpoint_id, attribute=cluster.Attributes.HoldTime, sequence=[ - initial_dut, diff_val], timeout_sec=post_prompt_settle_delay_seconds) - - self.step("5a") - if not has_type_pir or not has_pir_timing_attrib: - logging.info("No PIR timing attribute support. Skip this steps 5b, 5c, 5d") - self.skip_step("5b") - self.skip_step("5c") - self.skip_step("5d") - else: - self.step("5b") - initial_dut = await self.read_occ_attribute_expect_success(attribute=attributes.PIROccupiedToUnoccupiedDelay) - - self.step("5c") - # write the new attribute value - diff_val = 11 - await self.write_single_attribute(attributes.PIROccupiedToUnoccupiedDelay(diff_val)) - - self.step("5d") - await_sequence_of_reports(report_queue=attrib_listener.attribute_queue, endpoint_id=endpoint_id, attribute=cluster.Attributes.PIROccupiedToUnoccupiedDelay, sequence=[ - initial_dut, diff_val], timeout_sec=post_prompt_settle_delay_seconds) - - self.step("6a") - if not has_type_ultrasonic or not has_ultrasonic_timing_attrib: - logging.info("No Ultrasonic timing attribute supports. Skip steps 6b, 6c, 6d") - self.skip_step("6b") - self.skip_step("6c") - self.skip_step("6d") - else: - self.step("6b") - initial_dut = await self.read_occ_attribute_expect_success(attribute=attributes.UltrasonicOccupiedToUnoccupiedDelay) - - self.step("6c") - # write the new attribute value - diff_val = 14 - await self.write_single_attribute(attributes.UltrasonicOccupiedToUnoccupiedDelay(diff_val)) - - self.step("6d") - await_sequence_of_reports(report_queue=attrib_listener.attribute_queue, endpoint_id=endpoint_id, attribute=cluster.Attributes.UltrasonicOccupiedToUnoccupiedDelay, sequence=[ - initial_dut, diff_val], timeout_sec=post_prompt_settle_delay_seconds) - - self.step("7a") - if not has_type_contact or not has_contact_timing_attrib: - logging.info("No Physical contact timing attribute supports. Skip this test case") - self.skip_step("7b") - self.skip_step("7c") - self.skip_step("7d") - else: - self.step("7b") - initial_dut = await self.read_occ_attribute_expect_success(attribute=attributes.PhysicalContactOccupiedToUnoccupiedDelay) - - self.step("7c") - # write the new attribute value - diff_val = 9 - await self.write_single_attribute(attributes.PhysicalContactOccupiedToUnoccupiedDelay(diff_val)) - - self.step("7d") - await_sequence_of_reports(report_queue=attrib_listener.attribute_queue, endpoint_id=endpoint_id, attribute=cluster.Attributes.PhysicalContactOccupiedToUnoccupiedDelay, sequence=[ - initial_dut, diff_val], timeout_sec=post_prompt_settle_delay_seconds) - - -if __name__ == "__main__": - default_matter_test_main() +# +# Copyright (c) 2024 Project CHIP (Matter) Authors +# All rights reserved. +# +# 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. +# + +# TODO: There are CI issues to be followed up for the test cases below that implements manually controlling sensor device for +# the occupancy state ON/OFF change. +# [TC-OCC-3.1] test procedure step 4 +# [TC-OCC-3.2] test precedure step 3a, 3c + +import logging + +import chip.clusters as Clusters +from matter_testing_support import (ClusterAttributeChangeAccumulator, MatterBaseTest, TestStep, async_test_body, + await_sequence_of_reports, default_matter_test_main) +from mobly import asserts + + +class TC_OCC_3_2(MatterBaseTest): + async def read_occ_attribute_expect_success(self, attribute): + cluster = Clusters.Objects.OccupancySensing + endpoint_id = self.matter_test_config.endpoint + return await self.read_single_attribute_check_success(endpoint=endpoint_id, cluster=cluster, attribute=attribute) + + def desc_TC_OCC_3_2(self) -> str: + return "[TC-OCC-3.2] Subscription Report Verification with server as DUT" + + def steps_TC_OCC_3_2(self) -> list[TestStep]: + steps = [ + TestStep(1, "Commission DUT to TH if not already done", is_commissioning=True), + TestStep(2, "TH establishes a wildcard subscription to all attributes on Occupancy Sensing Cluster on the endpoint under test. Subscription min interval = 0 and max interval = 30 seconds."), + TestStep("3a", "Do not trigger DUT for occupancy state change."), + TestStep("3b", "TH reads DUT Occupancy attribute and saves the initial value as initial"), + TestStep("3c", "Trigger DUT to change the occupancy state."), + TestStep("3d", "TH awaits a ReportDataMessage containing an attribute report for DUT Occupancy attribute."), + TestStep("4a", "Check if DUT supports HoldTime attribute, If not supported, then stop and skip the rest of test cases."), + TestStep("4b", "TH reads DUT HoldTime attribute and saves the initial value as initial"), + TestStep("4c", "TH writes a different value to DUT HoldTime attribute."), + TestStep("4d", "TH awaits a ReportDataMessage containing an attribute report for DUT HoldTime attribute."), + TestStep("5a", "Check if DUT supports DUT feature flag PIR or (!PIR & !US & !PHY) and has the PIROccupiedToUnoccupiedDelay attribute, If not supported, then skip 5b, 5c, 5d."), + TestStep("5b", "TH reads DUT PIROccupiedToUnoccupiedDelay attribute and saves the initial value as initial"), + TestStep("5c", "TH writes a different value to DUT PIROccupiedToUnoccupiedDelay attribute."), + TestStep("5d", "TH awaits a ReportDataMessage containing an attribute report for DUT PIROccupiedToUnoccupiedDelay attribute."), + TestStep("6a", "Check if DUT supports DUT feature flag US and has the UltrasonicOccupiedToUnoccupiedDelay attribute. If not supported, then skip 6b, 6c, 6d."), + TestStep("6b", "TH reads DUT UltrasonicOccupiedToUnoccupiedDelay attribute and saves the initial value as initial"), + TestStep("6c", "TH writes a different value to DUT UltrasonicOccupiedToUnoccupiedDelay attribute."), + TestStep("6d", "TH awaits a ReportDataMessage containing an attribute report for DUT UltrasonicOccupiedToUnoccupiedDelay attribute."), + TestStep("7a", "Check if DUT supports DUT feature flag PHY and has the PhysicalContactOccupiedToUnoccupiedDelay attribute. If not supported, skip 7b, 7c, 7d."), + TestStep("7b", "TH reads DUT PhysicalContactOccupiedToUnoccupiedDelay attribute and saves the initial value as initial"), + TestStep("7c", "TH writes a different value to DUT PhysicalContactOccupiedToUnoccupiedDelay attribute."), + TestStep("7d", "TH awaits a ReportDataMessage containing an attribute report for DUT PhysicalContactOccupiedToUnoccupiedDelay attribute.") + ] + return steps + + def pics_TC_OCC_3_2(self) -> list[str]: + pics = [ + "OCC.S", + ] + return pics + + @async_test_body + async def test_TC_OCC_3_2(self): + endpoint_id = self.matter_test_config.endpoint + node_id = self.dut_node_id + dev_ctrl = self.default_controller + + post_prompt_settle_delay_seconds = 10.0 + cluster = Clusters.Objects.OccupancySensing + attributes = cluster.Attributes + + self.step(1) # Commissioning already done + + self.step(2) + feature_map = await self.read_occ_attribute_expect_success(attribute=attributes.FeatureMap) + has_feature_pir = (feature_map & cluster.Bitmaps.Feature.kPassiveInfrared) != 0 + has_feature_ultrasonic = (feature_map & cluster.Bitmaps.Feature.kUltrasonic) != 0 + has_feature_contact = (feature_map & cluster.Bitmaps.Feature.kPhysicalContact) != 0 + + logging.info( + f"Feature map: 0x{feature_map:x}. PIR: {has_feature_pir}, US:{has_feature_ultrasonic}, PHY:{has_feature_contact}") + + attribute_list = await self.read_occ_attribute_expect_success(attribute=attributes.AttributeList) + has_pir_timing_attrib = attributes.PIROccupiedToUnoccupiedDelay.attribute_id in attribute_list + has_ultrasonic_timing_attrib = attributes.UltrasonicOccupiedToUnoccupiedDelay.attribute_id in attribute_list + has_contact_timing_attrib = attributes.PhysicalContactOccupiedToUnoccupiedDelay.attribute_id in attribute_list + logging.info(f"Attribute list: {attribute_list}") + logging.info(f"--> Has PIROccupiedToUnoccupiedDelay: {has_pir_timing_attrib}") + logging.info(f"--> Has UltrasonicOccupiedToUnoccupiedDelay: {has_ultrasonic_timing_attrib}") + logging.info(f"--> Has PhysicalContactOccupiedToUnoccupiedDelay: {has_contact_timing_attrib}") + + # min interval = 0, and max interval = 30 seconds + attrib_listener = ClusterAttributeChangeAccumulator(Clusters.Objects.OccupancySensing) + await attrib_listener.start(dev_ctrl, node_id, endpoint=endpoint_id, min_interval_sec=0, max_interval_sec=30) + + # TODO - Will add Namepiped to assimilate the manual sensor untrigger here + self.step("3a") + self.wait_for_user_input(prompt_msg="Type any letter and press ENTER after DUT goes back to unoccupied state.") + + self.step("3b") + if attributes.Occupancy.attribute_id in attribute_list: + initial_dut = await self.read_occ_attribute_expect_success(attribute=attributes.Occupancy) + asserts.assert_equal(initial_dut, 0, "Occupancy attribute is still detected state") + + # TODO - Will add Namepiped to assimilate the manual sensor trigger here + self.step("3c") + self.wait_for_user_input( + prompt_msg="Type any letter and press ENTER after the sensor occupancy is triggered and its occupancy state changed.") + + self.step("3d") + await_sequence_of_reports(report_queue=attrib_listener.attribute_queue, endpoint_id=endpoint_id, attribute=cluster.Attributes.Occupancy, sequence=[ + 1], timeout_sec=post_prompt_settle_delay_seconds) + + self.step("4a") + if attributes.HoldTime.attribute_id not in attribute_list: + logging.info("No HoldTime attribute supports. Terminate this test case") + self.skip_all_remaining_steps("4b") + + self.step("4b") + initial_dut = await self.read_occ_attribute_expect_success(attribute=attributes.HoldTime) + + self.step("4c") + # write a different a HoldTime attribute value + diff_val = 12 + await self.write_single_attribute(attributes.HoldTime(diff_val)) + + self.step("4d") + await_sequence_of_reports(report_queue=attrib_listener.attribute_queue, endpoint_id=endpoint_id, attribute=cluster.Attributes.HoldTime, sequence=[ + diff_val], timeout_sec=post_prompt_settle_delay_seconds) + + self.step("5a") + + has_no_legacy_features = ((not has_feature_pir) and (not has_feature_ultrasonic) and (not has_feature_contact)) + + if has_pir_timing_attrib and (has_feature_pir or has_no_legacy_features): + self.step("5b") + initial_dut = await self.read_occ_attribute_expect_success(attribute=attributes.PIROccupiedToUnoccupiedDelay) + + self.step("5c") + # write the new attribute value + diff_val = 11 + await self.write_single_attribute(attributes.PIROccupiedToUnoccupiedDelay(diff_val)) + + self.step("5d") + await_sequence_of_reports(report_queue=attrib_listener.attribute_queue, endpoint_id=endpoint_id, attribute=cluster.Attributes.PIROccupiedToUnoccupiedDelay, sequence=[ + diff_val], timeout_sec=post_prompt_settle_delay_seconds) + else: + logging.info("No PIR timing attribute support. Skipping steps 5b, 5c, 5d") + self.skip_step("5b") + self.skip_step("5c") + self.skip_step("5d") + + self.step("6a") + if not has_feature_ultrasonic or not has_ultrasonic_timing_attrib: + logging.info("No Ultrasonic timing attribute supports. Skipping steps 6b, 6c, 6d") + self.skip_step("6b") + self.skip_step("6c") + self.skip_step("6d") + else: + self.step("6b") + initial_dut = await self.read_occ_attribute_expect_success(attribute=attributes.UltrasonicOccupiedToUnoccupiedDelay) + + self.step("6c") + # write the new attribute value + diff_val = 14 + await self.write_single_attribute(attributes.UltrasonicOccupiedToUnoccupiedDelay(diff_val)) + + self.step("6d") + await_sequence_of_reports(report_queue=attrib_listener.attribute_queue, endpoint_id=endpoint_id, attribute=cluster.Attributes.UltrasonicOccupiedToUnoccupiedDelay, sequence=[ + diff_val], timeout_sec=post_prompt_settle_delay_seconds) + + self.step("7a") + if not has_feature_contact or not has_contact_timing_attrib: + logging.info("No Physical contact timing attribute supports. Skipping steps 7b, 7c, 7d") + self.skip_step("7b") + self.skip_step("7c") + self.skip_step("7d") + else: + self.step("7b") + initial_dut = await self.read_occ_attribute_expect_success(attribute=attributes.PhysicalContactOccupiedToUnoccupiedDelay) + + self.step("7c") + # write the new attribute value + diff_val = 9 + await self.write_single_attribute(attributes.PhysicalContactOccupiedToUnoccupiedDelay(diff_val)) + + self.step("7d") + await_sequence_of_reports(report_queue=attrib_listener.attribute_queue, endpoint_id=endpoint_id, attribute=cluster.Attributes.PhysicalContactOccupiedToUnoccupiedDelay, sequence=[ + diff_val], timeout_sec=post_prompt_settle_delay_seconds) + + +if __name__ == "__main__": + default_matter_test_main() diff --git a/src/python_testing/matter_testing_support.py b/src/python_testing/matter_testing_support.py index eb8a6ba20d63b9..d3d810149ce5dd 100644 --- a/src/python_testing/matter_testing_support.py +++ b/src/python_testing/matter_testing_support.py @@ -334,6 +334,15 @@ def wait_for_report(self): asserts.fail(f"[AttributeChangeCallback] Attribute {self._expected_attribute} not found in returned report") +def clear_queue(report_queue: queue.Queue): + """Flush all contents of a report queue. Useful to get back to empty point.""" + while not report_queue.empty(): + try: + report_queue.get(block=False) + except queue.Empty: + break + + def await_sequence_of_reports(report_queue: queue.Queue, endpoint_id: int, attribute: TypedAttributePath, sequence: list[Any], timeout_sec: float): """Given a queue.Queue hooked-up to an attribute change accumulator, await a given expected sequence of attribute reports. @@ -344,6 +353,9 @@ def await_sequence_of_reports(report_queue: queue.Queue, endpoint_id: int, attri - sequence: list of attribute values in order that are expected. - timeout_sec: number of seconds to wait for. + *** WARNING: The queue contains every report since the sub was established. Use + clear_queue to make it empty. *** + This will fail current Mobly test with assertion failure if the data is not as expected in order. Returns nothing on success so the test can go on. @@ -446,6 +458,20 @@ def attribute_report_counts(self) -> dict[ClusterObjects.ClusterAttributeDescrip def attribute_reports(self) -> dict[ClusterObjects.ClusterAttributeDescriptor, AttributeValue]: return self._attribute_reports + def get_last_report(self) -> Optional[Any]: + """Flush entire queue, returning last (newest) report only.""" + last_report: Optional[Any] = None + while True: + try: + last_report = self._q.get(block=False) + except queue.Empty: + return last_report + + def flush_reports(self) -> None: + """Flush entire queue, returning nothing.""" + _ = self.get_last_report() + return + class InternalTestRunnerHooks(TestRunnerHooks): From 010d9821daceb60e601c5174986cb648e62705d1 Mon Sep 17 00:00:00 2001 From: William Date: Fri, 23 Aug 2024 07:46:55 +0100 Subject: [PATCH 11/11] Remove DuplicatedAreas error (#35126) * Removed the DuplicatedAreas error from the XML * Generated code after XML update. * The service are server ignores any duplicate values before calling the delegate. Example was updated accodingly. * Updated test SEAR-1.3 following changes to the duplicated areas error. * Restyled by clang-format * Made select areas const. --------- Co-authored-by: Restyled.io --- .../include/rvc-service-area-delegate.h | 4 +- examples/rvc-app/rvc-common/rvc-app.matter | 5 +- .../src/rvc-service-area-delegate.cpp | 63 +++++------- .../service-area-delegate.h | 6 +- .../service-area-server.cpp | 99 ++++++++++--------- .../data-model/chip/service-area-cluster.xml | 5 +- .../data_model/controller-clusters.matter | 5 +- .../python/chip/clusters/Objects.py | 7 +- .../CHIP/zap-generated/MTRBaseClusters.h | 5 +- src/python_testing/TC_SEAR_1_3.py | 26 ++--- .../zap-generated/cluster-enums-check.h | 1 - .../app-common/zap-generated/cluster-enums.h | 7 +- 12 files changed, 108 insertions(+), 125 deletions(-) diff --git a/examples/rvc-app/rvc-common/include/rvc-service-area-delegate.h b/examples/rvc-app/rvc-common/include/rvc-service-area-delegate.h index c6be8aa5bb55f0..a87d345cb506ba 100644 --- a/examples/rvc-app/rvc-common/include/rvc-service-area-delegate.h +++ b/examples/rvc-app/rvc-common/include/rvc-service-area-delegate.h @@ -79,8 +79,8 @@ class RvcServiceAreaDelegate : public Delegate // command support bool IsSetSelectedAreasAllowed(MutableCharSpan & statusText) override; - bool IsValidSelectAreasSet(const ServiceArea::Commands::SelectAreas::DecodableType & req, - ServiceArea::SelectAreasStatus & areaStatus, MutableCharSpan & statusText) override; + bool IsValidSelectAreasSet(const Span & selectedAreas, ServiceArea::SelectAreasStatus & areaStatus, + MutableCharSpan & statusText) override; bool HandleSkipArea(uint32_t skippedArea, MutableCharSpan & skipStatusText) override; diff --git a/examples/rvc-app/rvc-common/rvc-app.matter b/examples/rvc-app/rvc-common/rvc-app.matter index 29460e1f43c7a1..ee4d751919be22 100644 --- a/examples/rvc-app/rvc-common/rvc-app.matter +++ b/examples/rvc-app/rvc-common/rvc-app.matter @@ -1438,9 +1438,8 @@ provisional cluster ServiceArea = 336 { enum SelectAreasStatus : enum8 { kSuccess = 0; kUnsupportedArea = 1; - kDuplicatedAreas = 2; - kInvalidInMode = 3; - kInvalidSet = 4; + kInvalidInMode = 2; + kInvalidSet = 3; } enum SkipAreaStatus : enum8 { diff --git a/examples/rvc-app/rvc-common/src/rvc-service-area-delegate.cpp b/examples/rvc-app/rvc-common/src/rvc-service-area-delegate.cpp index 9041efd3d9105b..2f2594e17d7686 100644 --- a/examples/rvc-app/rvc-common/src/rvc-service-area-delegate.cpp +++ b/examples/rvc-app/rvc-common/src/rvc-service-area-delegate.cpp @@ -113,23 +113,12 @@ bool RvcServiceAreaDelegate::IsSetSelectedAreasAllowed(MutableCharSpan & statusT return (mIsSetSelectedAreasAllowedDeviceInstance->*mIsSetSelectedAreasAllowedCallback)(statusText); }; -bool RvcServiceAreaDelegate::IsValidSelectAreasSet(const Commands::SelectAreas::DecodableType & req, SelectAreasStatus & areaStatus, +bool RvcServiceAreaDelegate::IsValidSelectAreasSet(const Span & selectedAreas, SelectAreasStatus & areaStatus, MutableCharSpan & statusText) { - // if req is empty list return true. + if (selectedAreas.empty()) { - size_t reqSize; - if (req.newAreas.ComputeSize(&reqSize) != CHIP_NO_ERROR) - { - areaStatus = SelectAreasStatus::kInvalidSet; // todo Not sure this is the correct error to use here - CopyCharSpanToMutableCharSpan("error computing number of selected areas"_span, statusText); - return false; - } - - if (reqSize == 0) - { - return true; - } + return true; } // If there is 1 or 0 supported maps, any combination of areas is valid. @@ -139,42 +128,34 @@ bool RvcServiceAreaDelegate::IsValidSelectAreasSet(const Commands::SelectAreas:: } // Check that all the requested areas are in the same map. - auto newAreasIter = req.newAreas.begin(); - newAreasIter.Next(); - - AreaStructureWrapper tempArea; - uint32_t ignoredIndex; - if (!GetSupportedAreaById(newAreasIter.GetValue(), ignoredIndex, tempArea)) { - areaStatus = SelectAreasStatus::kUnsupportedArea; - CopyCharSpanToMutableCharSpan("unable to find selected area in supported areas"_span, statusText); - return false; - } - - auto mapId = tempArea.mapID.Value(); // It is safe to call `.Value()` as we confirmed that there are at least 2 maps. - - while (newAreasIter.Next()) - { - if (!GetSupportedAreaById(newAreasIter.GetValue(), ignoredIndex, tempArea)) + AreaStructureWrapper tempArea; + uint32_t ignoredIndex; + if (!GetSupportedAreaById(selectedAreas[0], ignoredIndex, tempArea)) { areaStatus = SelectAreasStatus::kUnsupportedArea; CopyCharSpanToMutableCharSpan("unable to find selected area in supported areas"_span, statusText); return false; } - if (tempArea.mapID.Value() != mapId) + auto mapId = tempArea.mapID.Value(); // It is safe to call `.Value()` as we confirmed that there are at least 2 maps. + + for (const auto & areaId : selectedAreas.SubSpan(1)) { - areaStatus = SelectAreasStatus::kInvalidSet; - CopyCharSpanToMutableCharSpan("all selected areas must be in the same map"_span, statusText); - return false; - } - } + if (!GetSupportedAreaById(areaId, ignoredIndex, tempArea)) + { + areaStatus = SelectAreasStatus::kUnsupportedArea; + CopyCharSpanToMutableCharSpan("unable to find selected area in supported areas"_span, statusText); + return false; + } - if (CHIP_NO_ERROR != newAreasIter.GetStatus()) - { - areaStatus = SelectAreasStatus::kInvalidSet; - CopyCharSpanToMutableCharSpan("error processing new areas."_span, statusText); - return false; + if (tempArea.mapID.Value() != mapId) + { + areaStatus = SelectAreasStatus::kInvalidSet; + CopyCharSpanToMutableCharSpan("all selected areas must be in the same map"_span, statusText); + return false; + } + } } return true; diff --git a/src/app/clusters/service-area-server/service-area-delegate.h b/src/app/clusters/service-area-server/service-area-delegate.h index 2e9422840aa847..7fe40bb8fe16f4 100644 --- a/src/app/clusters/service-area-server/service-area-delegate.h +++ b/src/app/clusters/service-area-server/service-area-delegate.h @@ -80,10 +80,10 @@ class Delegate * If the set of locations is invalid, the locationStatus should be set to InvalidSet and * the statusText SHALL include a vendor-defined error description. * - * The caller of this method will ensure that there are no duplicates is the list + * The caller of this method will ensure that there are no duplicates in the list * and that all the locations in the set are valid supported locations. * - * @param[in] req List of new selected locations. + * @param[in] selectedAreas List of new selected locations. * @param[out] locationStatus Success if all checks pass, error code if failure. * @param[out] statusText text describing failure (see description above), size kMaxSizeStatusText. * @return true if success. @@ -91,7 +91,7 @@ class Delegate * @note If the SelectAreas command is allowed when the device is operating and the selected locations change to none, the * device must stop. */ - virtual bool IsValidSelectAreasSet(const Commands::SelectAreas::DecodableType & req, SelectAreasStatus & locationStatus, + virtual bool IsValidSelectAreasSet(const Span & selectedAreas, SelectAreasStatus & locationStatus, MutableCharSpan & statusText) = 0; /** diff --git a/src/app/clusters/service-area-server/service-area-server.cpp b/src/app/clusters/service-area-server/service-area-server.cpp index 2f2efceddd903d..4de8f942e0251d 100644 --- a/src/app/clusters/service-area-server/service-area-server.cpp +++ b/src/app/clusters/service-area-server/service-area-server.cpp @@ -240,64 +240,72 @@ void Instance::HandleSelectAreasCmd(HandlerContext & ctx, const Commands::Select } } + uint32_t selectedAreasBuffer[kMaxNumSelectedAreas]; + auto selectedAreasSpan = Span(selectedAreasBuffer, kMaxNumSelectedAreas); + uint32_t numberOfSelectedAreas = 0; + + // Closure for checking if an area ID exists in the selectedAreasSpan + auto areaAlreadyExists = [&numberOfSelectedAreas, &selectedAreasSpan](uint32_t areaId) { + for (uint32_t i = 0; i < numberOfSelectedAreas; i++) + { + if (areaId == selectedAreasSpan[i]) + { + return true; + } + } + return false; + }; + // if number of selected locations in parameter matches number in attribute - the locations *might* be the same bool matchesCurrentSelectedAreas = (numberOfAreas == mDelegate->GetNumberOfSelectedAreas()); + // do as much parameter validation as we can if (numberOfAreas != 0) { - // do as much parameter validation as we can + uint32_t ignoredIndex = 0; + uint32_t oldSelectedArea; + auto iAreaIter = req.newAreas.begin(); + while (iAreaIter.Next()) { - uint32_t ignoredIndex = 0; - uint32_t oldSelectedArea; - uint32_t i = 0; - auto iAreaIter = req.newAreas.begin(); - while (iAreaIter.Next()) - { - uint32_t aSelectedArea = iAreaIter.GetValue(); + uint32_t selectedArea = iAreaIter.GetValue(); - // each item in this list SHALL match the AreaID field of an entry on the SupportedAreas attribute's list - // If the Status field is set to UnsupportedArea, the StatusText field SHALL be an empty string. - if (!IsSupportedArea(aSelectedArea)) - { - exitResponse(SelectAreasStatus::kUnsupportedArea, ""_span); - return; - } + // If aSelectedArea is already in selectedAreasSpan skip + if (areaAlreadyExists(selectedArea)) + { + continue; + } - // Checking for duplicate locations. - uint32_t j = 0; - auto jAreaIter = req.newAreas.begin(); - while (j < i) - { - jAreaIter.Next(); // Since j < i and i is valid, we can safely call Next() without checking the return value. - if (jAreaIter.GetValue() == aSelectedArea) - { - exitResponse(SelectAreasStatus::kDuplicatedAreas, ""_span); - return; - } - j += 1; - } + // each item in this list SHALL match the AreaID field of an entry on the SupportedAreas attribute's list + // If the Status field is set to UnsupportedArea, the StatusText field SHALL be an empty string. + if (!IsSupportedArea(selectedArea)) + { + exitResponse(SelectAreasStatus::kUnsupportedArea, ""_span); + return; + } - // check to see if parameter list and attribute still match - if (matchesCurrentSelectedAreas) + // check to see if parameter list and attribute still match + if (matchesCurrentSelectedAreas) + { + if (!mDelegate->GetSelectedAreaByIndex(ignoredIndex, oldSelectedArea) || (selectedArea != oldSelectedArea)) { - if (!mDelegate->GetSelectedAreaByIndex(ignoredIndex, oldSelectedArea) || (aSelectedArea != oldSelectedArea)) - { - matchesCurrentSelectedAreas = false; - } + matchesCurrentSelectedAreas = false; } - - i += 1; } - // after iterating with Next through DecodableType - check for failure - if (CHIP_NO_ERROR != iAreaIter.GetStatus()) - { - ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::InvalidCommand); - return; - } + selectedAreasSpan[numberOfSelectedAreas] = selectedArea; + numberOfSelectedAreas += 1; + } + + // after iterating with Next through DecodableType - check for failure + if (CHIP_NO_ERROR != iAreaIter.GetStatus()) + { + ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::InvalidCommand); + return; } } + selectedAreasSpan.reduce_size(numberOfSelectedAreas); + // If the newAreas field is the same as the value of the SelectedAreas attribute // the SelectAreasResponse command SHALL have the Status field set to Success and // the StatusText field MAY be supplied with a human-readable string or include an empty string. @@ -327,7 +335,7 @@ void Instance::HandleSelectAreasCmd(HandlerContext & ctx, const Commands::Select // ask the device to handle SelectAreas Command // (note - locationStatusText to be filled out by delegated function for kInvalidInMode and InvalidSet) auto locationStatus = SelectAreasStatus::kSuccess; - if (!mDelegate->IsValidSelectAreasSet(req, locationStatus, delegateStatusText)) + if (!mDelegate->IsValidSelectAreasSet(selectedAreasSpan, locationStatus, delegateStatusText)) { exitResponse(locationStatus, delegateStatusText); return; @@ -342,11 +350,10 @@ void Instance::HandleSelectAreasCmd(HandlerContext & ctx, const Commands::Select if (numberOfAreas != 0) { - auto locationIter = req.newAreas.begin(); uint32_t ignored; - while (locationIter.Next()) + for (uint32_t areaId : selectedAreasSpan) { - mDelegate->AddSelectedArea(locationIter.GetValue(), ignored); + mDelegate->AddSelectedArea(areaId, ignored); } } diff --git a/src/app/zap-templates/zcl/data-model/chip/service-area-cluster.xml b/src/app/zap-templates/zcl/data-model/chip/service-area-cluster.xml index e4457c3745e2a7..8c2ba832c85604 100644 --- a/src/app/zap-templates/zcl/data-model/chip/service-area-cluster.xml +++ b/src/app/zap-templates/zcl/data-model/chip/service-area-cluster.xml @@ -63,9 +63,8 @@ limitations under the License. - - - + + diff --git a/src/controller/data_model/controller-clusters.matter b/src/controller/data_model/controller-clusters.matter index ede6f1117ed37f..1413833c1b14c7 100644 --- a/src/controller/data_model/controller-clusters.matter +++ b/src/controller/data_model/controller-clusters.matter @@ -6463,9 +6463,8 @@ provisional cluster ServiceArea = 336 { enum SelectAreasStatus : enum8 { kSuccess = 0; kUnsupportedArea = 1; - kDuplicatedAreas = 2; - kInvalidInMode = 3; - kInvalidSet = 4; + kInvalidInMode = 2; + kInvalidSet = 3; } enum SkipAreaStatus : enum8 { diff --git a/src/controller/python/chip/clusters/Objects.py b/src/controller/python/chip/clusters/Objects.py index 391765f3838e7b..9808b113f234b7 100644 --- a/src/controller/python/chip/clusters/Objects.py +++ b/src/controller/python/chip/clusters/Objects.py @@ -31358,14 +31358,13 @@ class OperationalStatusEnum(MatterIntEnum): class SelectAreasStatus(MatterIntEnum): kSuccess = 0x00 kUnsupportedArea = 0x01 - kDuplicatedAreas = 0x02 - kInvalidInMode = 0x03 - kInvalidSet = 0x04 + kInvalidInMode = 0x02 + kInvalidSet = 0x03 # All received enum values that are not listed above will be mapped # to kUnknownEnumValue. This is a helper enum value that should only # be used by code to process how it handles receiving an unknown # enum value. This specific value should never be transmitted. - kUnknownEnumValue = 5, + kUnknownEnumValue = 4, class SkipAreaStatus(MatterIntEnum): kSuccess = 0x00 diff --git a/src/darwin/Framework/CHIP/zap-generated/MTRBaseClusters.h b/src/darwin/Framework/CHIP/zap-generated/MTRBaseClusters.h index c00233470f81d6..f15337ff56f41c 100644 --- a/src/darwin/Framework/CHIP/zap-generated/MTRBaseClusters.h +++ b/src/darwin/Framework/CHIP/zap-generated/MTRBaseClusters.h @@ -19787,9 +19787,8 @@ typedef NS_ENUM(uint8_t, MTRServiceAreaOperationalStatus) { typedef NS_ENUM(uint8_t, MTRServiceAreaSelectAreasStatus) { MTRServiceAreaSelectAreasStatusSuccess MTR_PROVISIONALLY_AVAILABLE = 0x00, MTRServiceAreaSelectAreasStatusUnsupportedArea MTR_PROVISIONALLY_AVAILABLE = 0x01, - MTRServiceAreaSelectAreasStatusDuplicatedAreas MTR_PROVISIONALLY_AVAILABLE = 0x02, - MTRServiceAreaSelectAreasStatusInvalidInMode MTR_PROVISIONALLY_AVAILABLE = 0x03, - MTRServiceAreaSelectAreasStatusInvalidSet MTR_PROVISIONALLY_AVAILABLE = 0x04, + MTRServiceAreaSelectAreasStatusInvalidInMode MTR_PROVISIONALLY_AVAILABLE = 0x02, + MTRServiceAreaSelectAreasStatusInvalidSet MTR_PROVISIONALLY_AVAILABLE = 0x03, } MTR_PROVISIONALLY_AVAILABLE; typedef NS_ENUM(uint8_t, MTRServiceAreaSkipAreaStatus) { diff --git a/src/python_testing/TC_SEAR_1_3.py b/src/python_testing/TC_SEAR_1_3.py index 2ea2e86b7d198d..867cdcbe234353 100644 --- a/src/python_testing/TC_SEAR_1_3.py +++ b/src/python_testing/TC_SEAR_1_3.py @@ -109,15 +109,17 @@ async def test_TC_SEAR_1_3(self): duplicated_areas = [valid_area_id, valid_area_id] - # FIXME need to check if this is the correct name of this status code - await self.send_cmd_select_areas_expect_response(step=3, new_areas=duplicated_areas, expected_response=Clusters.ServiceArea.Enums.SelectAreasStatus.kDuplicatedAreas) + await self.send_cmd_select_areas_expect_response(step=3, new_areas=duplicated_areas, expected_response=Clusters.ServiceArea.Enums.SelectAreasStatus.kSuccess) - await self.send_cmd_select_areas_expect_response(step=4, new_areas=[], expected_response=Clusters.ServiceArea.Enums.SelectAreasStatus.kSuccess) + selected_areas = await self.read_selected_areas(step=4) + asserts.assert_true(selected_areas == [valid_area_id], "SelectedAreas should be empty") - selected_areas = await self.read_selected_areas(step=5) + await self.send_cmd_select_areas_expect_response(step=5, new_areas=[], expected_response=Clusters.ServiceArea.Enums.SelectAreasStatus.kSuccess) + + selected_areas = await self.read_selected_areas(step=6) asserts.assert_true(len(selected_areas) == 0, "SelectedAreas should be empty") - await self.send_cmd_select_areas_expect_response(step=6, new_areas=[invalid_area_id], expected_response=Clusters.ServiceArea.Enums.SelectAreasStatus.kUnsupportedArea) + await self.send_cmd_select_areas_expect_response(step=8, new_areas=[invalid_area_id], expected_response=Clusters.ServiceArea.Enums.SelectAreasStatus.kUnsupportedArea) if self.check_pics("SEAR.S.M.INVALID_STATE_FOR_SELECT_AREAS") and self.check_pics("SEAR.S.M.HAS_MANUAL_SELAREA_STATE_CONTROL"): test_step = "Manually intervene to put the device in a state that prevents it from executing the SelectAreas command" @@ -127,7 +129,7 @@ async def test_TC_SEAR_1_3(self): else: self.wait_for_user_input(prompt_msg=f"{test_step}, and press Enter when done.\n") - await self.send_cmd_select_areas_expect_response(step=8, new_areas=[valid_area_id], expected_response=Clusters.ServiceArea.Enums.SelectAreasStatus.kInvalidInMode) + await self.send_cmd_select_areas_expect_response(step=10, new_areas=[valid_area_id], expected_response=Clusters.ServiceArea.Enums.SelectAreasStatus.kInvalidInMode) if self.check_pics("SEAR.S.M.VALID_STATE_FOR_SELECT_AREAS") and self.check_pics("SEAR.S.M.HAS_MANUAL_SELAREA_STATE_CONTROL"): test_step = f"Manually intervene to put the device in a state that allows it to execute the SelectAreas({supported_area_ids}) command" @@ -137,17 +139,17 @@ async def test_TC_SEAR_1_3(self): else: self.wait_for_user_input(prompt_msg=f"{test_step}, and press Enter when done.\n") - await self.send_cmd_select_areas_expect_response(step=10, new_areas=supported_area_ids, expected_response=Clusters.ServiceArea.Enums.SelectAreasStatus.kSuccess) + await self.send_cmd_select_areas_expect_response(step=11, new_areas=supported_area_ids, expected_response=Clusters.ServiceArea.Enums.SelectAreasStatus.kSuccess) - selected_areas = await self.read_selected_areas(step=11) + selected_areas = await self.read_selected_areas(step=12) asserts.assert_true(len(selected_areas) == len(supported_area_ids), f"SelectedAreas({selected_areas}) should match SupportedAreas({supported_area_ids})") - await self.send_cmd_select_areas_expect_response(step=12, new_areas=supported_area_ids, expected_response=Clusters.ServiceArea.Enums.SelectAreasStatus.kSuccess) + await self.send_cmd_select_areas_expect_response(step=13, new_areas=supported_area_ids, expected_response=Clusters.ServiceArea.Enums.SelectAreasStatus.kSuccess) if self.check_pics("SEAR.S.M.VALID_STATE_FOR_SELECT_AREAS") and self.check_pics("SEAR.S.M.HAS_MANUAL_SELAREA_STATE_CONTROL") and self.check_pics("SEAR.S.M.SELECT_AREAS_WHILE_NON_IDLE"): test_step = f"Manually intervene to put the device in a state that allows it to execute the SelectAreas({valid_area_id}) command, and put the device in a non-idle state" - self.print_step("13", test_step) + self.print_step("14", test_step) if self.is_ci: self.write_to_app_pipe('{"Name": "Reset"}') await self.send_single_cmd(cmd=Clusters.Objects.RvcRunMode.Commands.ChangeToMode(newMode=1), endpoint=self.endpoint) @@ -155,9 +157,9 @@ async def test_TC_SEAR_1_3(self): self.wait_for_user_input(prompt_msg=f"{test_step}, and press Enter when done.\n") if self.check_pics("SEAR.S.F00"): - await self.send_cmd_select_areas_expect_response(step=14, new_areas=[valid_area_id], expected_response=Clusters.ServiceArea.Enums.SelectAreasStatus.kSuccess) + await self.send_cmd_select_areas_expect_response(step=15, new_areas=[valid_area_id], expected_response=Clusters.ServiceArea.Enums.SelectAreasStatus.kSuccess) else: - await self.send_cmd_select_areas_expect_response(step=14, new_areas=[valid_area_id], expected_response=Clusters.ServiceArea.Enums.SelectAreasStatus.kInvalidInMode) + await self.send_cmd_select_areas_expect_response(step=15, new_areas=[valid_area_id], expected_response=Clusters.ServiceArea.Enums.SelectAreasStatus.kInvalidInMode) if __name__ == "__main__": diff --git a/zzz_generated/app-common/app-common/zap-generated/cluster-enums-check.h b/zzz_generated/app-common/app-common/zap-generated/cluster-enums-check.h index f63782c221d827..90d399b8e6fde7 100644 --- a/zzz_generated/app-common/app-common/zap-generated/cluster-enums-check.h +++ b/zzz_generated/app-common/app-common/zap-generated/cluster-enums-check.h @@ -2598,7 +2598,6 @@ static auto __attribute__((unused)) EnsureKnownEnumValue(ServiceArea::SelectArea { case EnumType::kSuccess: case EnumType::kUnsupportedArea: - case EnumType::kDuplicatedAreas: case EnumType::kInvalidInMode: case EnumType::kInvalidSet: return val; diff --git a/zzz_generated/app-common/app-common/zap-generated/cluster-enums.h b/zzz_generated/app-common/app-common/zap-generated/cluster-enums.h index e78f8f4d064648..998a3c931b42b1 100644 --- a/zzz_generated/app-common/app-common/zap-generated/cluster-enums.h +++ b/zzz_generated/app-common/app-common/zap-generated/cluster-enums.h @@ -3825,14 +3825,13 @@ enum class SelectAreasStatus : uint8_t { kSuccess = 0x00, kUnsupportedArea = 0x01, - kDuplicatedAreas = 0x02, - kInvalidInMode = 0x03, - kInvalidSet = 0x04, + kInvalidInMode = 0x02, + kInvalidSet = 0x03, // All received enum values that are not listed above will be mapped // to kUnknownEnumValue. This is a helper enum value that should only // be used by code to process how it handles receiving and unknown // enum value. This specific should never be transmitted. - kUnknownEnumValue = 5, + kUnknownEnumValue = 4, }; // Enum for SkipAreaStatus