From 47b2783152208b40148d66a96f946dadc831986b Mon Sep 17 00:00:00 2001 From: William Date: Thu, 22 Aug 2024 23:46:47 +0100 Subject: [PATCH 01/32] 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 02/32] 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 03/32] [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 04/32] 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 05/32] 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 From 79a6aa350f845ffd1746c43a2ddcbd6e831f8027 Mon Sep 17 00:00:00 2001 From: Boris Zbarsky Date: Fri, 23 Aug 2024 03:55:01 -0400 Subject: [PATCH 06/32] Mark return types nullable in MTRDevice_XPC when nil can be returned. (#35152) --- src/darwin/Framework/CHIP/MTRDevice_XPC.mm | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/darwin/Framework/CHIP/MTRDevice_XPC.mm b/src/darwin/Framework/CHIP/MTRDevice_XPC.mm index bc3b0a6b26bc4b..85be0db3436df9 100644 --- a/src/darwin/Framework/CHIP/MTRDevice_XPC.mm +++ b/src/darwin/Framework/CHIP/MTRDevice_XPC.mm @@ -163,10 +163,10 @@ - (oneway void)deviceConfigurationChanged:(NSNumber *)nodeID MTR_DEVICE_SIMPLE_REMOTE_XPC_GETTER(state, MTRDeviceState, MTRDeviceStateUnknown, getStateWithReply) MTR_DEVICE_SIMPLE_REMOTE_XPC_GETTER(deviceCachePrimed, BOOL, NO, getDeviceCachePrimedWithReply) -MTR_DEVICE_SIMPLE_REMOTE_XPC_GETTER(estimatedStartTime, NSDate *, nil, getEstimatedStartTimeWithReply) -MTR_DEVICE_SIMPLE_REMOTE_XPC_GETTER(estimatedSubscriptionLatency, NSNumber *, nil, getEstimatedSubscriptionLatencyWithReply) +MTR_DEVICE_SIMPLE_REMOTE_XPC_GETTER(estimatedStartTime, NSDate * _Nullable, nil, getEstimatedStartTimeWithReply) +MTR_DEVICE_SIMPLE_REMOTE_XPC_GETTER(estimatedSubscriptionLatency, NSNumber * _Nullable, nil, getEstimatedSubscriptionLatencyWithReply) -typedef NSDictionary * readAttributeResponseType; +typedef NSDictionary * _Nullable readAttributeResponseType; MTR_DEVICE_COMPLEX_REMOTE_XPC_GETTER(readAttributeWithEndpointID : (NSNumber *) endpointID clusterID : (NSNumber *) clusterID attributeID @@ -226,7 +226,7 @@ - (void)_invokeCommandWithEndpointID:(NSNumber *)endpointID // Not Supported via XPC //- (oneway void)deviceController:(NSUUID *)controller nodeID:(NSNumber *)nodeID openCommissioningWindowWithSetupPasscode:(NSNumber *)setupPasscode discriminator:(NSNumber *)discriminator duration:(NSNumber *)duration completion:(MTRDeviceOpenCommissioningWindowHandler)completion; -MTR_DEVICE_SIMPLE_REMOTE_XPC_GETTER(clientDataKeys, NSArray *, nil, getClientDataKeysWithReply) +MTR_DEVICE_SIMPLE_REMOTE_XPC_GETTER(clientDataKeys, NSArray * _Nullable, nil, getClientDataKeysWithReply) MTR_DEVICE_COMPLEX_REMOTE_XPC_GETTER(clientDataForKey : (NSString *) key, id _Nullable, nil, clientDataForKey : key withReply) From 9462066993f3c4f7cbecb12832617c3a3b930af4 Mon Sep 17 00:00:00 2001 From: C Freeman Date: Fri, 23 Aug 2024 04:23:38 -0400 Subject: [PATCH 07/32] check (#34998) --- src/python_testing/matter_testing_support.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/python_testing/matter_testing_support.py b/src/python_testing/matter_testing_support.py index d3d810149ce5dd..4127ead64c3083 100644 --- a/src/python_testing/matter_testing_support.py +++ b/src/python_testing/matter_testing_support.py @@ -1838,6 +1838,14 @@ def whole_node_runner(self: MatterBaseTest, *args, **kwargs): EndpointCheckFunction = typing.Callable[[Clusters.Attribute.AsyncReadTransaction.ReadResponse, int], bool] +def get_cluster_from_attribute(attribute: ClusterObjects.ClusterAttributeDescriptor) -> ClusterObjects.Cluster: + return ClusterObjects.ALL_CLUSTERS[attribute.cluster_id] + + +def get_cluster_from_command(command: ClusterObjects.ClusterCommand) -> ClusterObjects.Cluster: + return ClusterObjects.ALL_CLUSTERS[command.cluster_id] + + def _has_cluster(wildcard, endpoint, cluster: ClusterObjects.Cluster) -> bool: try: return cluster in wildcard.attributes[endpoint] @@ -1870,7 +1878,7 @@ def has_cluster(cluster: ClusterObjects.ClusterObjectDescriptor) -> EndpointChec def _has_attribute(wildcard, endpoint, attribute: ClusterObjects.ClusterAttributeDescriptor) -> bool: - cluster = getattr(Clusters, attribute.__qualname__.split('.')[-3]) + cluster = get_cluster_from_attribute(attribute) try: attr_list = wildcard.attributes[endpoint][cluster][cluster.Attributes.AttributeList] return attribute.attribute_id in attr_list From aa3e9fcb48ee0d1d22448fd5296bdd8f80d482d4 Mon Sep 17 00:00:00 2001 From: Wang Qixiang <43193572+wqx6@users.noreply.github.com> Date: Fri, 23 Aug 2024 16:39:40 +0800 Subject: [PATCH 08/32] minimal_mdns: Fix filter for Compressed Fabric Id when browsing operational nodes (#35063) * minimal_mdns: Fix filter for Compressed Fabric Id when browsing operational nodes * Add check for subtype number --- src/lib/dnssd/Resolver_ImplMinimalMdns.cpp | 12 +++++++++++- src/lib/shell/commands/Dns.cpp | 16 +++++++++++++--- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/src/lib/dnssd/Resolver_ImplMinimalMdns.cpp b/src/lib/dnssd/Resolver_ImplMinimalMdns.cpp index 8b2b30e3184291..8e913f2addb1c1 100644 --- a/src/lib/dnssd/Resolver_ImplMinimalMdns.cpp +++ b/src/lib/dnssd/Resolver_ImplMinimalMdns.cpp @@ -544,7 +544,17 @@ CHIP_ERROR MinMdnsResolver::BuildQuery(QueryBuilder & builder, const ActiveResol switch (data.type) { case DiscoveryType::kOperational: - qname = CheckAndAllocateQName(kOperationalServiceName, kOperationalProtocol, kLocalDomain); + if (data.filter.type == DiscoveryFilterType::kCompressedFabricId) + { + char subtypeStr[Common::kSubTypeMaxLength + 1]; + ReturnErrorOnFailure(MakeServiceSubtype(subtypeStr, sizeof(subtypeStr), data.filter)); + qname = CheckAndAllocateQName(subtypeStr, kSubtypeServiceNamePart, kOperationalServiceName, kOperationalProtocol, + kLocalDomain); + } + else + { + qname = CheckAndAllocateQName(kOperationalServiceName, kOperationalProtocol, kLocalDomain); + } break; case DiscoveryType::kCommissionableNode: if (data.filter.type == DiscoveryFilterType::kNone) diff --git a/src/lib/shell/commands/Dns.cpp b/src/lib/shell/commands/Dns.cpp index 18e0841dccce83..917f94f849d865 100644 --- a/src/lib/shell/commands/Dns.cpp +++ b/src/lib/shell/commands/Dns.cpp @@ -214,13 +214,23 @@ bool ParseSubType(int argc, char ** argv, Dnssd::DiscoveryFilter & filter) case 'C': filterType = Dnssd::DiscoveryFilterType::kCommissioningMode; break; + case 'I': + filterType = Dnssd::DiscoveryFilterType::kCompressedFabricId; + break; default: return false; } - uint16_t code; - VerifyOrReturnError(ArgParser::ParseInt(subtype + 2, code), false); - + uint64_t code = 0; + if (filterType == Dnssd::DiscoveryFilterType::kCompressedFabricId) + { + VerifyOrReturnError(ArgParser::ParseInt(subtype + 2, code, 16), false); + VerifyOrReturnValue(code != 0, false); + } + else + { + VerifyOrReturnError(ArgParser::ParseInt(subtype + 2, code), false); + } filter = Dnssd::DiscoveryFilter(filterType, code); return true; } From 085138295acd3af46931a8197d9e564dc11becac Mon Sep 17 00:00:00 2001 From: C Freeman Date: Fri, 23 Aug 2024 04:39:55 -0400 Subject: [PATCH 09/32] Docs re-organization (#34765) * Docs re-organization The second phase of the handbook involves linking over to the SDK docs from the handbook site. Before we do this, the SDK documentation needs a re-org and an update. This is a continuation of the re-org, following the platform updates. Remaining: Zap, tools, examples, IDs. Done in this PR: - remove API - this is a 4 year old design doc for a test dispatch system that was never implemented. This does not make sense to keep in the docs folder since it gives the incorrect impression that this system was implemented at some point. In reality, our testing is quite different and is decently well documented in the testing section. - rename discussion to product consideration. This contains only the ipv6 considerations doc, which isn't a discussion. Instead, we're going to use this location to land documents related to product considerations that affect matter, but are provided outside of the SDK, ex. required network stack support (current doc), and factory considerations (coming later) for things like DACs, individually provisioned values, QR codes, config options, certification declaration inclusion, DCL. - move designing cluster for testing and portability from testing to cluster design - it just makes more sense there. - Creating a tips and troubleshooting section to land small guides, move in avahi troubleshooting and guide around discovery from a host computer. - remove quick start guide - everything in here is outdated, we have a getting started guide and a readme that covers all the getting started material. Added references to that in the readme - * couple more refs * remove readme --- docs/QUICK_START.md | 52 ----- docs/README.md | 4 + docs/api/device_runner.md | 103 ---------- docs/api/device_runner_dispatch.md | 183 ------------------ docs/api/index.md | 7 - .../img/plant_uml_source.txt | 0 .../img/unit_testable_clusters.png | Bin .../unit_testable_clusters_all_classes.png | Bin .../img/unit_testable_clusters_context.png | Bin .../img/unit_testable_clusters_driver.png | Bin .../img/unit_testable_clusters_logic.png | Bin .../img/unit_testable_clusters_server.png | Bin docs/cluster_and_device_type_dev/index.md | 1 + .../unit_testing_clusters.md | 0 docs/getting_started/index.md | 1 - docs/guides/README.md | 56 ------ docs/guides/index.md | 4 - docs/index.md | 5 +- .../index.md | 0 .../lwip_ipv6.md | 0 docs/testing/index.md | 1 - docs/testing/integration_test_utilities.md | 7 +- .../discovery_from_a_host_computer.md | 0 docs/tips_and_troubleshooting/index.md | 8 + .../troubleshooting_avahi.md | 0 25 files changed, 19 insertions(+), 413 deletions(-) delete mode 100644 docs/QUICK_START.md delete mode 100644 docs/api/device_runner.md delete mode 100644 docs/api/device_runner_dispatch.md delete mode 100644 docs/api/index.md rename docs/{testing => cluster_and_device_type_dev}/img/plant_uml_source.txt (100%) rename docs/{testing => cluster_and_device_type_dev}/img/unit_testable_clusters.png (100%) rename docs/{testing => cluster_and_device_type_dev}/img/unit_testable_clusters_all_classes.png (100%) rename docs/{testing => cluster_and_device_type_dev}/img/unit_testable_clusters_context.png (100%) rename docs/{testing => cluster_and_device_type_dev}/img/unit_testable_clusters_driver.png (100%) rename docs/{testing => cluster_and_device_type_dev}/img/unit_testable_clusters_logic.png (100%) rename docs/{testing => cluster_and_device_type_dev}/img/unit_testable_clusters_server.png (100%) rename docs/{testing => cluster_and_device_type_dev}/unit_testing_clusters.md (100%) delete mode 100644 docs/guides/README.md rename docs/{discussion => product_considerations}/index.md (100%) rename docs/{discussion => product_considerations}/lwip_ipv6.md (100%) rename docs/{getting_started => tips_and_troubleshooting}/discovery_from_a_host_computer.md (100%) create mode 100644 docs/tips_and_troubleshooting/index.md rename docs/{guides => tips_and_troubleshooting}/troubleshooting_avahi.md (100%) diff --git a/docs/QUICK_START.md b/docs/QUICK_START.md deleted file mode 100644 index d5d80703c5e61f..00000000000000 --- a/docs/QUICK_START.md +++ /dev/null @@ -1,52 +0,0 @@ -# Quick Start - -## Demo Overview - -The Matter reference implementation contains support for a number of examples -and platforms. - -## Wi-Fi Nodes - -|
Controller / Admin
|
Node
| Description | -| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| [**chip-tool**](https://github.com/project-chip/connectedhomeip/blob/master/examples/chip-tool/README.md) (Linux / Mac)
Includes docs for all the cluster commands supported
| **all-clusters-app**
  • [M5Stack](https://github.com/project-chip/connectedhomeip/blob/master/examples/all-clusters-app/esp32/README.md) (ESP)
  • [Linux](https://github.com/project-chip/connectedhomeip/tree/master/examples/all-clusters-app/linux) simulation | Use the command line tool on a laptop to pair with and control an embedded Wi-Fi platform. This demo supports the “all-clusters-app”, so it provides the basic onoff light test and more. | -| [**chip-repl**](https://github.com/project-chip/connectedhomeip/blob/master/src/controller/python/README.md) | **all-clusters-app**
  • [M5Stack](https://github.com/project-chip/connectedhomeip/blob/master/examples/all-clusters-app/esp32/README.md) (ESP)
  • [Linux](https://github.com/project-chip/connectedhomeip/tree/master/examples/all-clusters-app/linux) simulation | Same as above, but uses the Python CHIP REPL as Controller Node. | - -## Thread Nodes - -Use one of the controllers listed above and then a Border Router and Node -combination listed below. - -|
    Border Router
    |
    Node
    | Description | -| -------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| [**ot-br**](https://openthread.io/guides/border-router/build)
    Thread Border Router
  • RasPi
  • BeagleBone | **lighting-app**
  • [Nordic nRF5x](https://github.com/project-chip/connectedhomeip/tree/master/examples/lighting-app/nrfconnect/README.md)
  • [NXP K32W](https://github.com/project-chip/connectedhomeip/tree/master/examples/lighting-app/nxp/k32w0/README.md)
  • [Qorvo QPG6100](https://github.com/project-chip/connectedhomeip/tree/master/examples/lighting-app/qpg)
  • [Silicon Labs EFR32](https://github.com/project-chip/connectedhomeip/tree/master/examples/lighting-app/silabs/README.md) | The Lighting example is supported by many of the available Thread platforms. See the chip-tool controller instructions for how to actuate the light on/off cluster. | -| [**ot-br**](https://openthread.io/guides/border-router/build)
    Thread Border Router
  • RasPi
  • BeagleBone | **lock-app**
  • [Nordic nRF5x](https://github.com/project-chip/connectedhomeip/tree/master/examples/lock-app/nrfconnect/README.md)
  • [Qorvo QPG6100](https://github.com/project-chip/connectedhomeip/tree/master/examples/lock-app/qpg)
  • [Silicon Labs EFR32](https://github.com/project-chip/connectedhomeip/tree/master/examples/lock-app/efr32/README.md)
  • [TI CC13x2x7](https://github.com/project-chip/connectedhomeip/tree/master/examples/lock-app/cc13x2x7_26x2x7/README.md) | The Lock example is supported by many of the available Thread and Wi-Fi platforms. | - -## Controllers - -### chip-tool - -This section summarizes how to run some common scenarios with the -[**chip-tool**](https://github.com/project-chip/connectedhomeip/blob/master/examples/chip-tool/README.md) -controller. - -#### IP Pairing - -`chip-tool pairing onnetwork ${NODE_ID_TO_ASSIGN} 20202021` will use PASE over -IP to commission a device and assign `${NODE_ID_TO_ASSIGN}` (which must be a -decimal number or a 0x-prefixed hex number) as its node id. - -NOTE: On Linux, if the device is actually running after unit tests ran you have -to use `chip-tool pairing onnetwork desired-node-id 34567890`, because the unit -tests change the device configuration. - -NOTE: to run both the Node and Controller as separate processes on the same -Linux or Mac machine, build the all-clusters-app with Bluetooth LE disabled as -follows: - -`scripts/examples/gn_build_example.sh examples/all-clusters-app/linux out/debug/standalone/ chip_config_network_layer_ble=false` - -#### Automated CASE tests - -`chip-tool tests Test_TC_OO_1_1` will run a suite of tests that use CASE To -communicate with a paired `all-clusters-app` peer node. diff --git a/docs/README.md b/docs/README.md index e4ec9c8f3a5523..e5aad317e6d556 100644 --- a/docs/README.md +++ b/docs/README.md @@ -2,6 +2,10 @@ ## Building and Developing +- A quick start guide to building a demo application and controller is + available in the [Getting started](./getting_started/) guide +- A guide to new cluster and device type development is available in the + [New Cluster and Device Type Developement Guide](./cluster_and_device_type_dev/) - Documentation about building from the command line can be found in [the build guide](guides/BUILDING.md) - Documentation about running [cirque](https://github.com/openweave/cirque) diff --git a/docs/api/device_runner.md b/docs/api/device_runner.md deleted file mode 100644 index db9c5993eff6d8..00000000000000 --- a/docs/api/device_runner.md +++ /dev/null @@ -1,103 +0,0 @@ -# CHIP on-device testing - -_Requirements and high-level design_ - -## Background - -The ability to run tests on actual and emulated hardware is paramount in -embedded projects. CHIP is no exception. We want on-device testing to be a first -class goal of CHIP architecture. On-device testing requirements apply both to -Continuous Integration testing for main CHIP software stack development and to -eventual CHIP product certification. This document explores the requirements and -evaluates potential solutions. - -## Overview of requirements - -A good device test infrastructure is built on four pillars. - -### Pillar 1: Using a test framework - -A test framework provides a testing structure that developers can follow and -potentially reduces some of the burden of test setup and teardown (less -boilerplate). Support for state-oriented and asynchronous structuring of tests -would be beneficial. Many test frameworks leverage scripting languages such as -Python to simplify the quick development of tests and to leverage rich sets of -libraries for device/systems access and results generation. - -### Pillar 2: Dispatching tests - -Tests can run on lab machines or on the developer's local workstation. Tests can -be triggered manually by the developer or as a result of completion of a -changeset built on a continuous integration (CI) server. CHIP involves multiple -stakeholders, many of which will want to contribute to the testing efforts with -lab capacity. The infrastructure therefore must be prepared for -cross-organization test dispatch. - -To facilitate uniform dispatch of tests we will probably need a simple -request/response protocol. Potentially HTTPS based and RESTful. Due to the long -running nature of device tests the response for a test scheduling request could -be a test ID, not the test result. That ID could be used to query the test -status, subscribe for notifications on status changes and to pull the test -results. Core aspects of such a scheme include the conventions for request -artifacts contents and minimum expected results contents once the run is -complete. - -### Pillar 3: Interacting with devices - -The test host environment has to reset devices, flash images on them, issue -commands, monitor status and collect test results. It may also need to integrate -both virtual (simulated) and real devices together. This can at first be done in -an ad-hoc way per platform but eventually we can go into device access -abstraction, i.e. define a common device testing interface which CHIP-compliant -devices can expose. The test host has to be prepared for driving multiple -devices at the same time for a single test, e.g. for tests that check -communication between multiple devices. - -### Pillar 4: Collecting results - -Ideally, test results are output in standard formats and similar or analogous -results between different devices and tests are output the same way. This -ensures reusability of code that processes similar data while allowing -aggregation of results across different dimensions. Failed tests must propagate -errors from device platform layers all the way to the CHIP stack and present -errors and potential stack traces in a standard result format. As the purpose of -on-device tests is to capture bugs, it is important that the test outputs -highlight the failure reason(s) and developers don't have to browse through -thousands of lines of logs to find the one line that sheds light on why a test -failed. - -## Priorities - -In the spirit of CHIP's charter, it would be great to see something taking-off -as soon as possible, to support continuous testing of the evolving CHIP stack. -We could then improve on that first iteration, even if we have to throw away -some temporary concepts and code. - -Test dispatch (Pillar 2) arises as the highest priority, because all other -pillars can have ad-hoc solutions. The first need is an interface between a -CircleCI job and a test execution host at a participating organization. This -would enable dispatching tests to a variety of existing in-house infrastructure, -while retaining common request/response protocols to shield the CI system from -implementation details of each lab. - -The next most important goal is to provide a test framework (Pillar 1). With a -standard framework developers can start writing tests, even if those tests will -be device specific and of ad-hoc input and output format. The general structure -of tests will however be present and later the tests can be adapted to standard -interactions (Pillar 3) and result formats (Pillar 4). - -Specifying result formats (Pillar 4) for the most common outputs -(success/failure, failure reason, stack trace, memory and CPU usage time series, -pcaps of network traffic, etc.) will be an ongoing effort. The simplest output -formats can be specified together with the test framework. - -Lastly, we want to look into a common device interaction interface that would -enable reusing tests between different devices. - -## Baseline hardware platforms for CHIP - -The TSG is targeting the following platforms/boards for early bringup: - -- Nordic nRF52 board -- SiLabs `XXXX` board -- Espressif ESP32 `XXXX` board diff --git a/docs/api/device_runner_dispatch.md b/docs/api/device_runner_dispatch.md deleted file mode 100644 index fff2634b41a3fc..00000000000000 --- a/docs/api/device_runner_dispatch.md +++ /dev/null @@ -1,183 +0,0 @@ -# CHIP on-device test dispatch - -This document expands on and provides design for on-device test dispatch. The -CHIP on-device testing document states that dispatching should involve a HTTPS -based RESTful protocol that could be integrated with CircleCI. - -## Definitions - -**Test run**: Tests instantiation on a test host, consisting of host-side test -binaries and test scripts as well as one or more device-side binaries. - -**Test host**: A computing execution environment provided for the purposes of -running tests by a party participating in the scheme. - -## Scope - -The scope of this proposal is to support running tests against a known set of -canonical devices and platforms for the purposes of developing core common code -in the CHIP project GitHub repository. - -This proposal does not preclude a stakeholder running their own tests against -their own hardware or lab in any way they see fit. The goal is merely to provide -a common way for volunteer organizations to register test infrastructure and -dispatch capabilities to be used by the wider CHIP developer community. - -Authentication is not considered strictly part of the test dispatch protocol. -However it is mandated that some form of authentication takes place before any -of the test dispatch operations are issued. Throughout this document it is -assumed that proper authentication and authorization is ensured by the server -hosting the test dispatch service. - -## Objectives - -- **Provide a centralized API** for the dispatching of tests against - potentially distributed lab infrastructure, which CI systems (i.e. CircleCI) - or individual developers may not be directly configured to access. -- **Decouple test execution from the CI environment**, especially for tests - requiring complex setups, such as radio traffic capture. -- **Enable** common adoption of **aligned methodologies** for both - certification-style tests and development-support tests (pre/post-submit - testing in pull requests). - -### Certification or pre-certification tests - -Certification tests are required to have canonical test suite names. - -Here the host side test binaries and scripts are fixed and the device-side -binary can vary by device type. The objective of certification testing is to run -a known fixed set of tests against new and existing devices. Dispatching of -certification tests involves specifying the canonical test suite name and -providing the requisite arguments, such as device-side binary and device type. - -### Development support tests - -Development support test suites are required to have canonical names, but they -may, during execution, check-out the actual test script from a given PR, or from -the artifacts uploaded for the test job. - -The test is executed against a pull request and may target many device types. -Therefore, both host-side and device-side artifacts may vary and have to be -uploaded in the respective argument to test dispatch operation. Dispatching of -development support test suites therefore involves specifying a canonical test -suite name, the PR URL, pre-built artifacts (host side and device-side) and -optional test-specific arguments. - -### Common constraints for dispatch - -In order to support running tests, some common arguments are required to -determine during dispatch whether a given combination of targets can be -supported. - -These constraints include: - -- A canonical device type list to determine whether a target runner has all - the targets needed. (Note that new hardware developers may provide a - non-canonical device type for running their own certification on their own - lab. Canonical device types exist for development support tests.) -- An optional node ID (unique UUID) to force execution on a given registered - infrastructure for test purposes. - -Example of canonical test suite names: - -- RendezVousTest: loads binaries on HW, validates that assumptions about - RendezVous advertising payload are still valid. -- BasicCHIPRegression: loads binaries on HW, validates against regressions on - multiple axes of the test. Potentially runs updated tests scripts from the - PR itself. - -## Aggregator Dispatch Interface API - -We conceptualize an aggregator service where all the tests are sent to be -further dispatched to or pulled by participating infrastructure/lab providers. - -For the prototype phase the aggregator may be the same service that runs tests -as well, i.e. further dispatch/pull/registration may not happen in the -prototype. - -This is the API which CircleCI and individual test developers can use. There may -be other APIs (e.g. administrator API) to the aggregator that provide richer -functionality. For now we don't discuss those. The API for communication between -the aggregator and test labs is to be specified at a later time. - -The goal of decoupling dispatch from execution is to avoid coupling running the -tests to a given lab’s or organization’s infrastructure. The dispatch interface -API provides a separation of concerns of “what to run”/“what happened” versus -“how to run”/“where to run”. - -### Resources and operations - -/available_test_suites - Collection resource, all the canonical test suite -names. - -- GET gets the list of known canonical test suite names - -/dispatch - Collection resource, all the currently running test executions. - -- POST dispatches a new test, returning its URI with the test run identifier - 'job_id'. - Arguments - Canonical Test Suite name e.g. - "CHIP_basic_test_suite" - ZIP file upload of artifacts (device-side and, if - needed, host-side) - Some common required inputs (see section below) - - Test-suite-specific configuration/contents may also be provided for the test - suite executor to use. - Maximum time the client is willing to wait for - results (seconds) - In case of execution timed out, the test job would be - considered FAILED, due to time-out. - Maximum time the client is willing to - wait for test to start (seconds) - In case of time-out to dispatch, the test - job would be considered ABORTED in the results as opposed to FAILED - - Authentication requirements may cause a given requestor to be throttled by - internal logic. - -/status/ - Member resource, an individual test. - -- GET returns the status of the test: scheduled, running, finished, aborted. -- DELETE cancels the test. - -/status//results - Collection resource, all the files resulting from the -test run. - -- GET returns the list of (file_name, file_id) pairs. - - Only mandatory file: - - test_suite_results.json - - Normalized test results, see section below. - -/status//results/ - Member resource, and individual result -file. - -- GET returns the contents of the file. - -### /dispatch arguments - -**test_suite_name**: _Required_. Name of the test suite. - -**host_artifacts**: _Only required for development support tests_. A file (most -likely a ZIP file) corresponding to all the scripts and binaries to be run by -the test host. The test host must be able to unzip, recognize and execute -contents. - -**device_artifacts**: _Required_. A file (most likely a ZIP file) corresponding -to all the binaries to be flashed on the device. The test host must be able to -unzip, recognize and flash contents. - -**timeout_for_results_seconds**: _Required_. The maximum amount of time in -seconds the client is willing to wait for results. After this much time the test -can be killed by the test host. - -**timeout_for_start_seconds**: _Required_. The maximum amount of time in seconds -the client is willing to wait for the test to start. If after dispatch the test -does not start after this much time the test can be descheduled by the -aggregator. - -### test_suite_results.json contents - -TBD. - -### Aggregator-to-lab dispatch API - -TBD. - -### Lab Registration Interface API - -Once agreement on the dispatch API is cemented, the API to register a given -executor with certain tests and devices capabilities can be defined. - -TBD. diff --git a/docs/api/index.md b/docs/api/index.md deleted file mode 100644 index c139642dc4ed68..00000000000000 --- a/docs/api/index.md +++ /dev/null @@ -1,7 +0,0 @@ -# API - -```{toctree} -:glob: - -* -``` diff --git a/docs/testing/img/plant_uml_source.txt b/docs/cluster_and_device_type_dev/img/plant_uml_source.txt similarity index 100% rename from docs/testing/img/plant_uml_source.txt rename to docs/cluster_and_device_type_dev/img/plant_uml_source.txt diff --git a/docs/testing/img/unit_testable_clusters.png b/docs/cluster_and_device_type_dev/img/unit_testable_clusters.png similarity index 100% rename from docs/testing/img/unit_testable_clusters.png rename to docs/cluster_and_device_type_dev/img/unit_testable_clusters.png diff --git a/docs/testing/img/unit_testable_clusters_all_classes.png b/docs/cluster_and_device_type_dev/img/unit_testable_clusters_all_classes.png similarity index 100% rename from docs/testing/img/unit_testable_clusters_all_classes.png rename to docs/cluster_and_device_type_dev/img/unit_testable_clusters_all_classes.png diff --git a/docs/testing/img/unit_testable_clusters_context.png b/docs/cluster_and_device_type_dev/img/unit_testable_clusters_context.png similarity index 100% rename from docs/testing/img/unit_testable_clusters_context.png rename to docs/cluster_and_device_type_dev/img/unit_testable_clusters_context.png diff --git a/docs/testing/img/unit_testable_clusters_driver.png b/docs/cluster_and_device_type_dev/img/unit_testable_clusters_driver.png similarity index 100% rename from docs/testing/img/unit_testable_clusters_driver.png rename to docs/cluster_and_device_type_dev/img/unit_testable_clusters_driver.png diff --git a/docs/testing/img/unit_testable_clusters_logic.png b/docs/cluster_and_device_type_dev/img/unit_testable_clusters_logic.png similarity index 100% rename from docs/testing/img/unit_testable_clusters_logic.png rename to docs/cluster_and_device_type_dev/img/unit_testable_clusters_logic.png diff --git a/docs/testing/img/unit_testable_clusters_server.png b/docs/cluster_and_device_type_dev/img/unit_testable_clusters_server.png similarity index 100% rename from docs/testing/img/unit_testable_clusters_server.png rename to docs/cluster_and_device_type_dev/img/unit_testable_clusters_server.png diff --git a/docs/cluster_and_device_type_dev/index.md b/docs/cluster_and_device_type_dev/index.md index 9159cdb3e511d5..cf43d02651378c 100644 --- a/docs/cluster_and_device_type_dev/index.md +++ b/docs/cluster_and_device_type_dev/index.md @@ -14,3 +14,4 @@ types in the SDK. - [Cluster and device type development](./cluster_and_device_type_dev.md) - [How To Add New Device Types & Clusters](how_to_add_new_dts_and_clusters.md) +- [Cluster Server design](./unit_testing_clusters.md) diff --git a/docs/testing/unit_testing_clusters.md b/docs/cluster_and_device_type_dev/unit_testing_clusters.md similarity index 100% rename from docs/testing/unit_testing_clusters.md rename to docs/cluster_and_device_type_dev/unit_testing_clusters.md diff --git a/docs/getting_started/index.md b/docs/getting_started/index.md index cca67e19b0c8fc..ea881141ff43ec 100644 --- a/docs/getting_started/index.md +++ b/docs/getting_started/index.md @@ -14,4 +14,3 @@ The following docs are a brief introduction to SDK development. - [Running your first example](./first_example.md) - [SDK Basics](./SDKBasics.md) - [ZAP](./zap.md) -- [Discover from a host computer](./discovery_from_a_host_computer.md) diff --git a/docs/guides/README.md b/docs/guides/README.md deleted file mode 100644 index a00cc83e280c91..00000000000000 --- a/docs/guides/README.md +++ /dev/null @@ -1,56 +0,0 @@ -# Guides - -## Platform Guides - -- [Android - Building](./android_building.md) -- [Apple - Testing with iPhone, iPad, macOS, Apple TV, HomePod, Watch, etc](./darwin.md) -- [ASR - Getting Started Guide](./asr_getting_started_guide.md) -- [Espressif (ESP32) - Getting Started Guide](./esp32/README.md) -- [Infineon PSoC6 - Software Update](./infineon_psoc6_software_update.md) -- [Infineon Trust M Provisioning](./infineon_trustm_provisioning.md) -- [Linux - Simulated Devices](./simulated_device_linux.md) -- [mbedOS - Adding a new target](./mbedos_add_new_target.md) -- [mbedOS - Commissioning](./mbedos_commissioning.md) -- [mbedOS - Platform Overview](./mbedos_platform_overview.md) -- [nRF Connect - Android Commissioning](./nrfconnect_android_commissioning.md) -- [nRF Connect - CLI Guide](./nrfconnect_examples_cli.md) -- [nRF Connect - Configuration](./nrfconnect_examples_configuration.md) -- [nRF Connect - Factory Data Configuration](./nrfconnect_factory_data_configuration.md) -- [nRF Connect - Platform Overview](./nrfconnect_platform_overview.md) -- [nRF Connect - Software Update](./nrfconnect_examples_software_update.md) -- [NXP - Getting Started Guide](./nxp/README.md) -- [Silicon Labs - Documentation](https://siliconlabs.github.io/matter/latest/index.html) -- [Silicon Labs - Getting Started](./silabs_getting_started.md) -- [Silicon Labs - Software Update](./silabs_efr32_software_update.md) -- [Silicon Labs - CLI Guide](./silabs_cli_guide.md) -- [STMicroelectronics (STM32)](./stm32_getting_started_guide.md) -- [TI - Platform Overview](./ti/ti_matter_overview.md) -- [Open IoT SDK - Platform Overview](./openiotsdk_platform_overview.md) -- [Open IoT SDK - Examples](./openiotsdk_examples.md) -- [Open IoT SDK - Unit Tests](./openiotsdk_unit_tests.md) -- [Open IoT SDK - Commissioning](./openiotsdk_commissioning.md) -- [Open IoT SDK - Software Update](./openiotsdk_examples_software_update.md) - -## Development Guides - -- [Access Control](./access-control-guide.md) - -## Setup Guides - -- [Open Thread - Hardware suggestions](./openthread_rcp_nrf_dongle.md) -- [Open Thread - Setting up a Pi as a border router](./openthread_border_router_pi.md) - -## Troubleshooting Guides - -- [Avahi - Troubleshooting](./troubleshooting_avahi.md) - -## Tool Guides - -- [chip-tool](./chip_tool_guide.md) -- [Python Matter-Repl](./matter-repl.md) -- [python-chip-controller - Advanced](./python_chip_controller_advanced_usage.md) -- [python-chip-controller - Building](./python_chip_controller_building.md) - -## Build Guides - -- [Building](./BUILDING.md) diff --git a/docs/guides/index.md b/docs/guides/index.md index 8f747ab27e684f..14d9caf7e21839 100644 --- a/docs/guides/index.md +++ b/docs/guides/index.md @@ -60,7 +60,3 @@ ti/ti_matter_overview - [Open Thread - Hardware suggestions](./openthread_rcp_nrf_dongle.md) - [Open Thread - Setting up a Raspberry Pi as a Border Router](./openthread_border_router_pi.md) - -## Troubleshooting Guides - -- [Avahi - Troubleshooting](./troubleshooting_avahi.md) diff --git a/docs/index.md b/docs/index.md index 1f8e806dc4cce9..f4b40622714eae 100644 --- a/docs/index.md +++ b/docs/index.md @@ -5,18 +5,17 @@ :caption: Contents :hidden: -QUICK_START PROJECT_FLOW VSCODE_DEVELOPMENT -api/index ci-cd/index -discussion/index getting_started/index cluster_and_device_type_dev/index guides/index style/index examples/index +product_considerations/index testing/index +tips_and_troubleshooting/index tools/index BUG_REPORT code_generation diff --git a/docs/discussion/index.md b/docs/product_considerations/index.md similarity index 100% rename from docs/discussion/index.md rename to docs/product_considerations/index.md diff --git a/docs/discussion/lwip_ipv6.md b/docs/product_considerations/lwip_ipv6.md similarity index 100% rename from docs/discussion/lwip_ipv6.md rename to docs/product_considerations/lwip_ipv6.md diff --git a/docs/testing/index.md b/docs/testing/index.md index d8ebe92da46f40..b3124f8349dc5c 100644 --- a/docs/testing/index.md +++ b/docs/testing/index.md @@ -37,7 +37,6 @@ from the global ember and message delivery layers. ![](./img/unit_tests.png) - [Unit tests](./unit_testing.md) -- [Designing clusters for unit testing and portability](./unit_testing_clusters.md) ## PICS and PIXIT diff --git a/docs/testing/integration_test_utilities.md b/docs/testing/integration_test_utilities.md index e50ad511caa28b..019a3cf44bc29b 100644 --- a/docs/testing/integration_test_utilities.md +++ b/docs/testing/integration_test_utilities.md @@ -6,9 +6,10 @@ on devices for the purposes of testing. When using any of these utilities it is important to inject the errors at the point where they are running through the MOST code that they can. -If the cluster uses the [ClusterLogic](./unit_testing_clusters.md) pattern, this -means injecting errors as close as possible to the driver layer, rather than -catching errors in the server. +If the cluster uses the +[ClusterLogic](../cluster_and_device_type_dev/unit_testing_clusters.md) pattern, +this means injecting errors as close as possible to the driver layer, rather +than catching errors in the server. ## TestEventTriggers diff --git a/docs/getting_started/discovery_from_a_host_computer.md b/docs/tips_and_troubleshooting/discovery_from_a_host_computer.md similarity index 100% rename from docs/getting_started/discovery_from_a_host_computer.md rename to docs/tips_and_troubleshooting/discovery_from_a_host_computer.md diff --git a/docs/tips_and_troubleshooting/index.md b/docs/tips_and_troubleshooting/index.md new file mode 100644 index 00000000000000..f2f4a7a417cb7d --- /dev/null +++ b/docs/tips_and_troubleshooting/index.md @@ -0,0 +1,8 @@ +# Tips and Troubleshooting + +```{toctree} +:glob: +:maxdepth: 1 + +* +``` diff --git a/docs/guides/troubleshooting_avahi.md b/docs/tips_and_troubleshooting/troubleshooting_avahi.md similarity index 100% rename from docs/guides/troubleshooting_avahi.md rename to docs/tips_and_troubleshooting/troubleshooting_avahi.md From c17fd97dad04a154ce6d26b0a8ffc4e868fdfa23 Mon Sep 17 00:00:00 2001 From: Hasty Granbery Date: Fri, 23 Aug 2024 06:57:11 -0700 Subject: [PATCH 10/32] [HVAC] Sync atomic write error order with spec (#34936) * Add support for Presets attributes and commands to the Thermostat cluster Clean up the Thermostat cluster and remove the TemperatureSetpointHoldPolicy attribute and SetTemperatureSetpointHoldPolicy command * Restyled by whitespace * Restyled by clang-format * Restyled by gn. * Fix build error for Linux configure build of all-clusters-app * Fix Darwin CI issues Editorial fixes * Restyled by clang-format * More fixes * Restyled by clang-format * BUILD.gn fixes for CI * Apply suggestions from code review Co-authored-by: Boris Zbarsky * Address review comments. * Restyled by clang-format * Regenerate Thermostat XML from spec * Move atomic enum to global-enums.xml, actually # Conflicts: # src/app/zap-templates/zcl/data-model/chip/global-structs.xml * Regenerate XML and convert thermostat-server to atomic writes * Pull in ACCapacityFormat typo un-fix * Update Test_TC_TSTAT_1_1 to know about AtomicResponse command. * Restyled patch * Fix weird merge with upstream * Fix emberAfIsTypeSigned not understanding temperature type * Merge fixes from atomic write branch * Relocate thermostat-manager sample code to all-clusters-common * Fix g++ build error on linux * Fix C formatter for long int, cast whole expression * Sync cast fix with master * Add thermostat-common dependency to thermostat app under linux * Remove MatterPostAttributeChangeCallback from thermostat-manager, as it conflicts with other implementations * Convert Atomic enums and structs to global * Restyled patch * Apply suggestions from code review Co-authored-by: Boris Zbarsky * Regen with alchemy 0.6.1 * Updates based on comments * Add TC_MCORE_FS_1_3.py test implementation (#34650) * Fix most TC-SWTCH-2.4 remaining issues (#34677) - Move 2.4 in a better place in the file - Add test steps properly - Allow default button press position override Issue #34656 Testing done: - Test still passes on DUT with automation * Initial test script for Fabric Sync TC_MCORE_FS_1_2 (#34675) * Initial test script for Fabric Sync TC_MCORE_FS_1_2 * Apply suggestions from code review Co-authored-by: C Freeman * Address Review Comments * Address review comments * Fix default timeout after other timeouts changed * Restyled by autopep8 * Fix linter error --------- Co-authored-by: C Freeman Co-authored-by: Restyled.io * Test automation for FabricSync ICD BridgedDeviceBasicInfoCluster (#34628) * WIP Bridged ICD, commissioning to both fabrics * wip testing sending KeepActive * wip most steps implemented * using SIGSTOP and SIGCONT to control ICD server pausing * Update src/python_testing/TC_BRBINFO_4_1.py Co-authored-by: Terence Hampson * comments addressed * more comments addressed * lint pass * Update src/python_testing/TC_BRBINFO_4_1.py Co-authored-by: C Freeman * comments addressed, incl TH_SERVER configurable * added setupQRCode and setupManualCode as options for DUT commissioning * Restyled by autopep8 * Restyled by isort * Update src/python_testing/TC_BRBINFO_4_1.py Co-authored-by: Terence Hampson * Update src/python_testing/TC_BRBINFO_4_1.py Co-authored-by: Terence Hampson * Update src/python_testing/TC_BRBINFO_4_1.py Co-authored-by: Terence Hampson * comments addressed * Restyled by autopep8 --------- Co-authored-by: Terence Hampson Co-authored-by: C Freeman Co-authored-by: Restyled.io * ServiceArea test scripts (#34548) * initial commit * fix bugs * fix issues reported by the linter * fix bug in checking for unique areaDesc * add TC 1.5 * Update src/python_testing/TC_SEAR_1_2.py Co-authored-by: William * Update src/python_testing/TC_SEAR_1_2.py Co-authored-by: William * address code review comments * fix issue introduced by the previous commit * address code review feedback * Update src/python_testing/TC_SEAR_1_2.py Co-authored-by: Kiel Oleson * address code review feedback * remove PICS checked by the TC_SEAR_1.6 * more code review updates * Restyled by autopep8 --------- Co-authored-by: William Co-authored-by: Kiel Oleson Co-authored-by: Restyled.io * Remove manual tests for Thermostat presets (#34679) * Dump details about leaked ExchangeContexts before aborting (#34617) * Dump details about leaked ExchangeContexts before aborting This is implemented via a VerifyOrDieWithObject() variant of the existing VerifyOrDie() macro that calls a DumpToLog() method on the provided object if it exists (otherwise this is simply a no-op). If CHIP_CONFIG_VERBOSE_VERIFY_OR_DIE is not enabled, VerifyOrDieWithObject() simply behaves like a plain VerifyOrDie(). DumpToLog() implementations can use ChipLogFormatRtti to log type information about an object (usually a delegate); if RTTI is disabled this simply outputs whether the object was null or not. * Address review comments * Make gcc happy and improve documentation * Remove unused include * Fix compile error without CHIP_CONFIG_VERBOSE_VERIFY_OR_DIE * Avoid unused parameter warning * [TI] CC13x4_26x4 build fixes (#34682) * lwip pbuf, map file, and hex creation when OTA is disabled * added cc13x4 family define around the non OTA hex creation * whitespace fix * reversed custom factoy data flash with cc13x4 check * more whitespace fixes * [ICD] Add missing polling function to NoWifi connectivity manager (#34684) * Add missing polling function to NoWifi connectivity manager * Update GenericConnectivityManagerImpl_NoWiFi.h Co-authored-by: Boris Zbarsky --------- Co-authored-by: Boris Zbarsky * [OPSTATE] Add Q test script for CountdownTime (#34632) * Add Q test * Added test to test set * Remove unused var * Restyled by autopep8 * Restyled by isort * Fix name * Use pics over other method * Removed unused stuff * Added pipe commands * Fix reset * Get example to report appropriate changes. * WiP * Added some comments * Changes to make things work * Removed dev msgs * Missed some * Removed dev msgs * Straggler * Restyled by clang-format * Restyled by autopep8 * Restyled by isort * Commented unused var * Update examples/all-clusters-app/linux/AllClustersCommandDelegate.cpp * Fix bug --------- Co-authored-by: Restyled.io * YAML update to BRBINFO, ProductId (#34513) * Bridged Device Information Cluster, Attribute ProductID test reflects marking as O, not X * Update src/app/tests/suites/certification/Test_TC_BRBINFO_2_1.yaml Co-authored-by: Terence Hampson * corrected pics * corrected pics * WIP Bridged ICD, commissioning to both fabrics * wip testing sending KeepActive * update to bridged-device-basic-information.xml and zap generated files * removed unrelated file --------- Co-authored-by: Terence Hampson Co-authored-by: Andrei Litvin * Fix simplified Linux tv-casting-app gn build error. (#34692) * adding parallel execution to restyle-diff (#34663) * adding parallel execution to restyle-diff * using xargs to call restyle-paths * fixing Copyright year * restyle the restyler * Add some bits to exercise global structs/enums to Unit Testing cluster. (#34540) * Adds things to the Unit Testing cluster XML. * This requires those things to be enabled in all-clusters-app, all-clusters-minimal-app, and one of the chef contact sensors to pass CI. * That requires an implementation in test-cluster-server * At which point might as well add a YAML test to exercise it all. * [Silabs] Port platform specific Multi-Chip OTA work (#34440) * Pull request #1836: Cherry multi ota Merge in WMN_TOOLS/matter from cherry-multi-ota to silabs_slc_1.3 Squashed commit of the following: commit 4320bb46571658bc44fb82345348265def394991 Author: Michael Rupp Date: Fri May 10 14:26:07 2024 -0400 remove some unwanted diffs in provision files commit be160931dc600de7e7ead378b70d6a43c3945e46 Author: Michael Rupp Date: Fri May 10 14:24:25 2024 -0400 revert changes to generator.project.mak commit 14b6605887166e6d5284a61feb2bf407d850bdcf Author: Michael Rupp Date: Fri May 10 13:06:12 2024 -0400 revert NVM key changes and script changes ... and 8 more commits * Restyled by whitespace * Restyled by clang-format * Restyled by gn * Restyled by autopep8 * remove unused libs caught by linter * update doctree with new readmes * rerun CI, cirque failing for unknown reasons * fix include guards in provision examples * Restyled by clang-format --------- Co-authored-by: Restyled.io * Add python tests for Thermostat presets feature (#34693) * Add python tests for Thermostat presets feature * Restyled by autopep8 * Restyled by isort * Update the PICS code for presets attribute --------- Co-authored-by: Restyled.io * removing unneccessary git fetch (#34698) * Restyle patch * Regen to fix ordering of global structs * Apply suggestions from code review Co-authored-by: Boris Zbarsky * Return correct AtomicResponse when committing or rolling back * Patch tests for atomic write of presets * Fix tests to work with the new setup. Specific changes: * Enable SetActivePresetRequest command in all-clusters-app. * Fix assignment of a PresetStructWithOwnedMembers to another PresetStructWithOwnedMembers to actually work correctly. * Move constraint checks that happen on write from commit to write. * Fix sending of atomic responses to not have use-stack-after-return. * Fix PICS for the tests involved. * Fix PICS values for atomic requests * Remove PresetsSchedulesEditable and QueuedPreset from various places * Restyled patch * Restyled patch, again * Remove PICS value for PresetsSchedulesEditable * clang-tidy fixes * clang-tidy fixes * Clear associated atomic writes when fabric is removed * Add tests for fabric removal and lockout of clients outside of atomic write * Python linter * Restyled patch * Clear timer when fabric is removed * Check for open atomic write before resetting * Revert auto delegate declaration on lines where there's no collision * Allow Thermostat delegate to provide timeout for atomic requests * Relocate thermostat example code to thermostat-common * Remove thermostat-manager code, replace with thermostat delegate * Sync atomic write error order with spec * Restyle patch * Drop memset of atomic write sessions * Add PreCommit stage to allow rollback of multiple attributes when only one fails * Separate OnTimerExpired method, vs ResetWrite * Method documentation * Apply suggestions from code review Co-authored-by: Nivi Sarkar <55898241+nivi-apple@users.noreply.github.com> * Remove unused InWrite check * Drop imcode alias * Switch AtomicWriteState to enum class * DRY up atomic write manager * Apply suggestions from code review Co-authored-by: Nivi Sarkar <55898241+nivi-apple@users.noreply.github.com> * Drop duplicate doc comments * Rename GetAtomicWriteScopedNodeId to GetAtomicWriteOriginatorScopedNodeId * Updates based on comments * Add MatterReportingAttributeChangeCallback calls for updated attributes * Relocate thermostat example code to thermostat-common, and remove thermostat-manager * Merge atomic write code back into thermostat-server * Apply suggestions from code review Co-authored-by: Boris Zbarsky * Fix build after suggestions * Actually track attribute IDs associated with atomic write * Only commit presets if all attribute precommits were successful * Fix scope on err * Add documentation to methods * Remove duplicate preset check. * Move various functions into anonymous namespaces, or Thermostat namespace * Drop impossible non-atomic attribute status after rollback * Namespace workaround for compilers on other platforms * Apply suggestions from code review --------- Co-authored-by: Nivedita Sarkar Co-authored-by: Restyled.io Co-authored-by: Nivi Sarkar <55898241+nivi-apple@users.noreply.github.com> Co-authored-by: Boris Zbarsky Co-authored-by: Terence Hampson Co-authored-by: Tennessee Carmel-Veilleux Co-authored-by: Chris Letnick Co-authored-by: C Freeman Co-authored-by: Douglas Rocha Ferraz Co-authored-by: Petru Lauric <81822411+plauric@users.noreply.github.com> Co-authored-by: William Co-authored-by: Kiel Oleson Co-authored-by: Karsten Sperling <113487422+ksperling-apple@users.noreply.github.com> Co-authored-by: Anu Biradar <104591549+abiradarti@users.noreply.github.com> Co-authored-by: mkardous-silabs <84793247+mkardous-silabs@users.noreply.github.com> Co-authored-by: Rob Bultman Co-authored-by: Andrei Litvin Co-authored-by: Shao Ling Tan <161761051+shaoltan-amazon@users.noreply.github.com> Co-authored-by: Amine Alami <43780877+Alami-Amine@users.noreply.github.com> Co-authored-by: Michael Rupp <95718139+mykrupp@users.noreply.github.com> --- examples/all-clusters-app/linux/BUILD.gn | 4 +- examples/thermostat/linux/BUILD.gn | 8 +- .../linux/include/thermostat-manager.h | 73 -- examples/thermostat/linux/main.cpp | 16 +- .../thermostat/linux/thermostat-manager.cpp | 497 -------- .../thermostat/thermostat-common/BUILD.gn | 4 + .../include/thermostat-delegate-impl.h | 6 +- .../src}/thermostat-delegate-impl.cpp | 88 +- src/app/chip_data_model.gni | 2 + .../thermostat-server/thermostat-delegate.h | 13 +- .../thermostat-server-atomic.cpp | 644 ++++++++++ .../thermostat-server-presets.cpp | 546 ++++++++ .../thermostat-server/thermostat-server.cpp | 1113 ++--------------- .../thermostat-server/thermostat-server.h | 163 ++- src/python_testing/TC_TSTAT_4_2.py | 50 +- 15 files changed, 1542 insertions(+), 1685 deletions(-) delete mode 100644 examples/thermostat/linux/include/thermostat-manager.h delete mode 100644 examples/thermostat/linux/thermostat-manager.cpp rename examples/thermostat/{linux => thermostat-common}/include/thermostat-delegate-impl.h (92%) rename examples/thermostat/{linux => thermostat-common/src}/thermostat-delegate-impl.cpp (75%) create mode 100644 src/app/clusters/thermostat-server/thermostat-server-atomic.cpp create mode 100644 src/app/clusters/thermostat-server/thermostat-server-presets.cpp diff --git a/examples/all-clusters-app/linux/BUILD.gn b/examples/all-clusters-app/linux/BUILD.gn index ed228b51b2cb32..baac52014d3c3b 100644 --- a/examples/all-clusters-app/linux/BUILD.gn +++ b/examples/all-clusters-app/linux/BUILD.gn @@ -75,7 +75,7 @@ source_set("chip-all-clusters-common") { "${chip_root}/examples/energy-management-app/energy-management-common/energy-evse/src/EnergyEvseTargetsStore.cpp", "${chip_root}/examples/energy-management-app/energy-management-common/energy-evse/src/energy-evse-mode.cpp", "${chip_root}/examples/energy-management-app/energy-management-common/energy-reporting/src/ElectricalPowerMeasurementDelegate.cpp", - "${chip_root}/examples/thermostat/linux/thermostat-delegate-impl.cpp", + "${chip_root}/examples/thermostat/thermostat-common/src/thermostat-delegate-impl.cpp", "AllClustersCommandDelegate.cpp", "AllClustersCommandDelegate.h", "AppOptions.cpp", @@ -102,7 +102,7 @@ source_set("chip-all-clusters-common") { "${chip_root}/examples/energy-management-app/energy-management-common/device-energy-management/include", "${chip_root}/examples/energy-management-app/energy-management-common/energy-evse/include", "${chip_root}/examples/energy-management-app/energy-management-common/energy-reporting/include", - "${chip_root}/examples/thermostat/linux/include", + "${chip_root}/examples/thermostat/thermostat-common/include", ] if (chip_enable_pw_rpc) { diff --git a/examples/thermostat/linux/BUILD.gn b/examples/thermostat/linux/BUILD.gn index 71c0eccfcfae50..0683b39abb4cc6 100644 --- a/examples/thermostat/linux/BUILD.gn +++ b/examples/thermostat/linux/BUILD.gn @@ -17,11 +17,10 @@ import("//build_overrides/chip.gni") executable("thermostat-app") { sources = [ + "${chip_root}/examples/thermostat/thermostat-common/src/thermostat-delegate-impl.cpp", "include/low-power/LowPowerManager.cpp", "include/low-power/LowPowerManager.h", "main.cpp", - "thermostat-delegate-impl.cpp", - "thermostat-manager.cpp", ] deps = [ @@ -30,7 +29,10 @@ executable("thermostat-app") { "${chip_root}/src/lib", ] - include_dirs = [ "include" ] + include_dirs = [ + "include", + "${chip_root}/examples/thermostat/thermostat-common/include", + ] cflags = [ "-Wconversion" ] diff --git a/examples/thermostat/linux/include/thermostat-manager.h b/examples/thermostat/linux/include/thermostat-manager.h deleted file mode 100644 index 274f66c66917cf..00000000000000 --- a/examples/thermostat/linux/include/thermostat-manager.h +++ /dev/null @@ -1,73 +0,0 @@ -/* - * - * 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. - */ - -#pragma once - -#include - -class ThermostatManager -{ -public: - CHIP_ERROR Init(); - - /// @brief Callback called when any attribute changed on the device - void AttributeChangeHandler(chip::EndpointId endpointId, chip::ClusterId clusterId, chip::AttributeId attributeId, - uint8_t * value, uint16_t size); - - chip::app::Clusters::Thermostat::SystemModeEnum GetSystemMode(); - chip::app::Clusters::Thermostat::ThermostatRunningModeEnum GetRunningMode(); - int16_t GetCurrentTemperature(); - int16_t GetCurrentHeatingSetPoint(); - int16_t GetCurrentCoolingSetPoint(); - uint8_t GetNumberOfPresets(); - CHIP_ERROR SetSystemMode(chip::app::Clusters::Thermostat::SystemModeEnum systemMode); - CHIP_ERROR SetRunningMode(chip::app::Clusters::Thermostat::ThermostatRunningModeEnum runningMode); - CHIP_ERROR SetCurrentTemperature(int16_t temperature); - CHIP_ERROR SetCurrentHeatingSetPoint(int16_t heatingSetpoint); - CHIP_ERROR SetCurrentCoolingSetPoint(int16_t coolingSetpoint); - -private: - friend ThermostatManager & ThermostatMgr(); - - chip::app::Clusters::Thermostat::SystemModeEnum mSystemMode; - chip::app::Clusters::Thermostat::ThermostatRunningModeEnum mRunningMode; - int16_t mLocalTemperature; - int16_t mOccupiedCoolingSetpoint; - int16_t mOccupiedHeatingSetpoint; - uint8_t mOccupiedSetback; - - static ThermostatManager sThermostatMgr; - - /// @brief attribute handler for the thermostat endpoint - void ThermostatEndpointAttributeChangeHandler(chip::ClusterId clusterId, chip::AttributeId attributeId, uint8_t * value, - uint16_t size); - void ThermostatClusterAttributeChangeHandler(chip::AttributeId attributeId, uint8_t * value, uint16_t size); - void LocalTemperatureMeasurementEndpointAttributeChangeHandler(chip::ClusterId clusterId, chip::AttributeId attributeId, - uint8_t * value, uint16_t size); - void LocalTemperatureMeasurementClusterAttributeChangeHandler(chip::AttributeId attributeId, uint8_t * value, uint16_t size); - - /// @brief Main method that evaluates the current thermostat state and updates attributes - void EvalThermostatState(); - void UpdateRunningModeForHeating(); - void UpdateRunningModeForCooling(); -}; - -inline ThermostatManager & ThermostatMgr() -{ - return ThermostatManager::sThermostatMgr; -} diff --git a/examples/thermostat/linux/main.cpp b/examples/thermostat/linux/main.cpp index 2279f02bef3963..b9f82696e8ce79 100644 --- a/examples/thermostat/linux/main.cpp +++ b/examples/thermostat/linux/main.cpp @@ -22,8 +22,6 @@ #include #include -#include "thermostat-manager.h" - using namespace chip; using namespace chip::app; // using namespace chip::app::Clusters; @@ -76,19 +74,7 @@ void ApplicationShutdown() {} int main(int argc, char * argv[]) { - if (ChipLinuxAppInit(argc, argv) != 0) - { - return -1; - } - ChipLogProgress(Zcl, "Starting Thermostat Manager"); - CHIP_ERROR err = ThermostatManager().Init(); - - if (err != CHIP_NO_ERROR) - { - ChipLogError(AppServer, "Failed to initialize thermostat manager: %" CHIP_ERROR_FORMAT, err.Format()); - chip::DeviceLayer::PlatformMgr().Shutdown(); - return -1; - } + VerifyOrDie(ChipLinuxAppInit(argc, argv) == 0); ChipLinuxAppMainLoop(); return 0; } diff --git a/examples/thermostat/linux/thermostat-manager.cpp b/examples/thermostat/linux/thermostat-manager.cpp deleted file mode 100644 index ea1f4375c1649d..00000000000000 --- a/examples/thermostat/linux/thermostat-manager.cpp +++ /dev/null @@ -1,497 +0,0 @@ -/* - * - * 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. - */ - -/********************************************************** - * Includes - *********************************************************/ - -#include -#include - -#include -#include -#include -#include - -/********************************************************** - * Defines and Constants - *********************************************************/ - -using namespace chip; -using namespace chip::app; -using namespace chip::app::DataModel; -using namespace chip::Controller; -using namespace chip::app::Clusters; -using namespace chip::app::Clusters::Thermostat; -using namespace chip::app::Clusters::Thermostat::Structs; -using namespace chip::app::Clusters::Thermostat::Attributes; -using namespace chip::app::Clusters::TemperatureMeasurement; -using namespace chip::app::Clusters::TemperatureMeasurement::Attributes; -using namespace Protocols::InteractionModel; - -using namespace chip::DeviceLayer; - -static constexpr EndpointId kThermostatEndpoint = 1; - -static constexpr uint16_t kMaxIntervalCeilingSeconds = 3600; - -static const char * SystemModeString(SystemModeEnum systemMode); -static const char * RunningModeString(ThermostatRunningModeEnum runningMode); - -/********************************************************** - * Variable declarations - *********************************************************/ - -ThermostatManager ThermostatManager::sThermostatMgr; - -namespace { - -template -static void OnAttributeChangeReported(const ConcreteDataAttributePath & path, const DecodableAttributeType & value); - -template <> -void OnAttributeChangeReported(const ConcreteDataAttributePath & path, - const MeasuredValue::TypeInfo::DecodableType & value) -{ - ClusterId clusterId = path.mClusterId; - if (clusterId != TemperatureMeasurement::Id) - { - ChipLogError(AppServer, - "Attribute change reported for TemperatureMeasurement cluster on incorrect cluster id " ChipLogFormatMEI, - ChipLogValueMEI(clusterId)); - return; - } - - AttributeId attributeId = path.mAttributeId; - if (attributeId != MeasuredValue::Id) - { - ChipLogError(AppServer, - "Attribute change reported for TemperatureMeasurement cluster for incorrect attribute" ChipLogFormatMEI, - ChipLogValueMEI(attributeId)); - return; - } - - if (!value.IsNull()) - { - ChipLogDetail(AppServer, "Attribute change reported for TemperatureMeasurement cluster - MeasuredValue is %d", - value.Value()); - } -} - -static void OnError(const ConcreteDataAttributePath * path, ChipError err) -{ - ChipLogError(AppServer, - "Subscribing to cluster Id " ChipLogFormatMEI " and attribute Id " ChipLogFormatMEI - " failed with error %" CHIP_ERROR_FORMAT, - ChipLogValueMEI(path->mClusterId), ChipLogValueMEI(path->mAttributeId), err.Format()); -} - -static void OnSubscriptionEstablished(const ReadClient & client, unsigned int value) -{ - ChipLogDetail(AppServer, "OnSubscriptionEstablished with subscription Id: %d", value); -} - -template -void SubscribeToAttribute(ClusterId clusterId, AttributeId attributeId, const EmberBindingTableEntry & binding, - OperationalDeviceProxy * peer_device) -{ - VerifyOrReturn(peer_device->GetSecureSession().HasValue(), - ChipLogError(AppServer, "SubscribeToAttribute failed. Secure session is null")); - - SubscribeAttribute( - peer_device->GetExchangeManager(), peer_device->GetSecureSession().Value(), binding.remote, clusterId, attributeId, - &OnAttributeChangeReported, &OnError, 0, kMaxIntervalCeilingSeconds, &OnSubscriptionEstablished, - nullptr, true /* fabricFiltered */, false /* keepExistingSubscription */); -} - -static void ThermostatBoundDeviceChangedHandler(const EmberBindingTableEntry & binding, OperationalDeviceProxy * peer_device, - void * context) -{ - VerifyOrReturn(binding.clusterId.has_value(), ChipLogError(AppServer, "Cluster Id is null")); - ClusterId clusterId = binding.clusterId.value(); - - switch (clusterId) - { - case TemperatureMeasurement::Id: - - // Subscribe to the MeasuredValue attribute - SubscribeToAttribute(clusterId, MeasuredValue::Id, binding, peer_device); - break; - default: - ChipLogError(AppServer, "Unsupported Cluster Id"); - break; - } -} - -void NotifyBoundClusterChangedForAllClusters() -{ - BindingManager::GetInstance().NotifyBoundClusterChanged(kThermostatEndpoint, TemperatureMeasurement::Id, nullptr); -} - -static void OnPlatformChipDeviceEvent(const DeviceLayer::ChipDeviceEvent * event, intptr_t arg) -{ - if (event->Type == DeviceLayer::DeviceEventType::kBindingsChangedViaCluster) - { - NotifyBoundClusterChangedForAllClusters(); - } -} - -void InitBindingManager(intptr_t context) -{ - auto & server = Server::GetInstance(); - CHIP_ERROR error = BindingManager::GetInstance().Init( - { &server.GetFabricTable(), server.GetCASESessionManager(), &server.GetPersistentStorage() }); - - if (error != CHIP_NO_ERROR) - { - ChipLogError(AppServer, "Failed to init binding manager"); - } - - BindingManager::GetInstance().RegisterBoundDeviceChangedHandler(ThermostatBoundDeviceChangedHandler); - NotifyBoundClusterChangedForAllClusters(); -} - -} // anonymous namespace - -CHIP_ERROR ThermostatManager::Init() -{ - // Init binding manager - - DeviceLayer::PlatformMgr().AddEventHandler(OnPlatformChipDeviceEvent, reinterpret_cast(this)); - DeviceLayer::PlatformMgr().ScheduleWork(InitBindingManager); - - mLocalTemperature = GetCurrentTemperature(); - mSystemMode = GetSystemMode(); - mRunningMode = GetRunningMode(); - mOccupiedCoolingSetpoint = GetCurrentCoolingSetPoint(); - mOccupiedHeatingSetpoint = GetCurrentHeatingSetPoint(); - // TODO: Gotta expose this properly on attribute - mOccupiedSetback = 5; // 0.5 C - - ChipLogError(AppServer, - "Initialized a thermostat with \n " - "mSystemMode: %u (%s) \n mRunningMode: %u (%s) \n mLocalTemperature: %d \n mOccupiedHeatingSetpoint: %d \n " - "mOccupiedCoolingSetpoint: %d" - "NumberOfPresets: %d", - to_underlying(mSystemMode), SystemModeString(mSystemMode), to_underlying(mRunningMode), - RunningModeString(mRunningMode), mLocalTemperature, mOccupiedHeatingSetpoint, mOccupiedCoolingSetpoint, - GetNumberOfPresets()); - - // TODO: Should this be called later? - EvalThermostatState(); - - return CHIP_NO_ERROR; -} - -void ThermostatManager::AttributeChangeHandler(EndpointId endpointId, ClusterId clusterId, AttributeId attributeId, uint8_t * value, - uint16_t size) -{ - switch (endpointId) - { - case kThermostatEndpoint: - ThermostatEndpointAttributeChangeHandler(clusterId, attributeId, value, size); - break; - - default: - ChipLogError(AppServer, "Attribute change reported for Thermostat on incorrect endpoint. Ignoring."); - break; - } -} - -void ThermostatManager::ThermostatEndpointAttributeChangeHandler(ClusterId clusterId, AttributeId attributeId, uint8_t * value, - uint16_t size) -{ - switch (clusterId) - { - case Thermostat::Id: - ThermostatClusterAttributeChangeHandler(attributeId, value, size); - break; - - default: - ChipLogError(AppServer, - "Attribute change reported for Thermostat on incorrect cluster for the thermostat endpoint. Ignoring."); - break; - } -} - -void ThermostatManager::ThermostatClusterAttributeChangeHandler(AttributeId attributeId, uint8_t * value, uint16_t size) -{ - switch (attributeId) - { - case LocalTemperature::Id: { - memcpy(&mLocalTemperature, value, size); - ChipLogError(AppServer, "Local temperature changed to %d", mLocalTemperature); - EvalThermostatState(); - } - break; - - case OccupiedCoolingSetpoint::Id: { - memcpy(&mOccupiedCoolingSetpoint, value, size); - ChipLogError(AppServer, "Cooling temperature changed to %d", mOccupiedCoolingSetpoint); - EvalThermostatState(); - } - break; - - case OccupiedHeatingSetpoint::Id: { - memcpy(&mOccupiedHeatingSetpoint, value, size); - ChipLogError(AppServer, "Heating temperature changed to %d", mOccupiedHeatingSetpoint); - EvalThermostatState(); - } - break; - - case SystemMode::Id: { - mSystemMode = static_cast(*value); - ChipLogError(AppServer, "System mode changed to %u (%s)", *value, SystemModeString(mSystemMode)); - EvalThermostatState(); - } - break; - - case ThermostatRunningMode::Id: { - mRunningMode = static_cast(*value); - ChipLogError(AppServer, "Running mode changed to %u (%s)", *value, RunningModeString(mRunningMode)); - } - break; - - default: { - ChipLogError(AppServer, "Unhandled thermostat attribute %u", static_cast(attributeId)); - return; - } - break; - } -} - -SystemModeEnum ThermostatManager::GetSystemMode() -{ - SystemModeEnum systemMode; - SystemMode::Get(kThermostatEndpoint, &systemMode); - return systemMode; -} - -ThermostatRunningModeEnum ThermostatManager::GetRunningMode() -{ - ThermostatRunningModeEnum runningMode; - ThermostatRunningMode::Get(kThermostatEndpoint, &runningMode); - return runningMode; -} - -int16_t ThermostatManager::GetCurrentTemperature() -{ - DataModel::Nullable currentTemperature; - currentTemperature.SetNull(); - LocalTemperature::Get(kThermostatEndpoint, currentTemperature); - return currentTemperature.ValueOr(0); -} - -int16_t ThermostatManager::GetCurrentHeatingSetPoint() -{ - int16_t heatingSetpoint; - OccupiedHeatingSetpoint::Get(kThermostatEndpoint, &heatingSetpoint); - return heatingSetpoint; -} - -int16_t ThermostatManager::GetCurrentCoolingSetPoint() -{ - int16_t coolingSetpoint; - OccupiedCoolingSetpoint::Get(kThermostatEndpoint, &coolingSetpoint); - return coolingSetpoint; -} - -uint8_t ThermostatManager::GetNumberOfPresets() -{ - return ThermostatDelegate::GetInstance().GetNumberOfPresets(); -} - -CHIP_ERROR ThermostatManager::SetSystemMode(SystemModeEnum systemMode) -{ - uint8_t systemModeValue = to_underlying(systemMode); - if (mSystemMode == systemMode) - { - ChipLogDetail(AppServer, "Already in system mode: %u (%s)", systemModeValue, SystemModeString(systemMode)); - return CHIP_NO_ERROR; - } - - ChipLogError(AppServer, "Setting system mode: %u (%s)", systemModeValue, SystemModeString(systemMode)); - return CHIP_ERROR_IM_GLOBAL_STATUS_VALUE(SystemMode::Set(kThermostatEndpoint, systemMode)); -} - -CHIP_ERROR ThermostatManager::SetRunningMode(ThermostatRunningModeEnum runningMode) -{ - uint8_t runningModeValue = to_underlying(runningMode); - if (mRunningMode == runningMode) - { - ChipLogDetail(AppServer, "Already in running mode: %u (%s)", runningModeValue, RunningModeString(runningMode)); - return CHIP_NO_ERROR; - } - - ChipLogError(AppServer, "Setting running mode: %u (%s)", runningModeValue, RunningModeString(runningMode)); - return CHIP_ERROR_IM_GLOBAL_STATUS_VALUE(ThermostatRunningMode::Set(kThermostatEndpoint, runningMode)); -} - -CHIP_ERROR ThermostatManager::SetCurrentTemperature(int16_t temperature) -{ - return CHIP_ERROR_IM_GLOBAL_STATUS_VALUE(LocalTemperature::Set(kThermostatEndpoint, temperature)); -} - -CHIP_ERROR ThermostatManager::SetCurrentHeatingSetPoint(int16_t heatingSetpoint) -{ - return CHIP_ERROR_IM_GLOBAL_STATUS_VALUE(OccupiedHeatingSetpoint::Set(kThermostatEndpoint, heatingSetpoint)); -} - -CHIP_ERROR ThermostatManager::SetCurrentCoolingSetPoint(int16_t coolingSetpoint) -{ - return CHIP_ERROR_IM_GLOBAL_STATUS_VALUE(OccupiedCoolingSetpoint::Set(kThermostatEndpoint, coolingSetpoint)); -} - -void ThermostatManager::EvalThermostatState() -{ - ChipLogError(AppServer, - "Eval Thermostat Running Mode \n " - "mSystemMode: %u (%s) \n mRunningMode: %u (%s) \n mLocalTemperature: %d \n mOccupiedHeatingSetpoint: %d \n " - "mOccupiedCoolingSetpoint: %d", - to_underlying(mSystemMode), SystemModeString(mSystemMode), to_underlying(mRunningMode), - RunningModeString(mRunningMode), mLocalTemperature, mOccupiedHeatingSetpoint, mOccupiedCoolingSetpoint); - - switch (mSystemMode) - { - case SystemModeEnum::kOff: { - SetRunningMode(ThermostatRunningModeEnum::kOff); - break; - } - case SystemModeEnum::kHeat: { - UpdateRunningModeForHeating(); - break; - } - case SystemModeEnum::kCool: { - UpdateRunningModeForCooling(); - break; - } - case SystemModeEnum::kAuto: { - UpdateRunningModeForHeating(); - UpdateRunningModeForCooling(); - break; - } - default: - break; - } -} - -void ThermostatManager::UpdateRunningModeForHeating() -{ - const int16_t heatingOnThreshold = mOccupiedHeatingSetpoint - static_cast(mOccupiedSetback * 10); - const int16_t heatingOffThreshold = mOccupiedHeatingSetpoint + static_cast(mOccupiedSetback * 10); - - if (mRunningMode == ThermostatRunningModeEnum::kHeat) - { - if (mLocalTemperature >= heatingOffThreshold) - { - ChipLogDetail(AppServer, "Eval Heat - Turning off"); - SetRunningMode(ThermostatRunningModeEnum::kOff); - } - else - { - ChipLogDetail(AppServer, "Eval Heat - Keep Heating"); - } - } - else - { - if (mLocalTemperature <= heatingOnThreshold) - { - ChipLogDetail(AppServer, "Eval Heat - Turn on"); - SetRunningMode(ThermostatRunningModeEnum::kHeat); - } - else - { - ChipLogDetail(AppServer, "Eval Heat - Nothing to do"); - } - } -} - -void ThermostatManager::UpdateRunningModeForCooling() -{ - const int16_t coolingOffThreshold = mOccupiedCoolingSetpoint - static_cast(mOccupiedSetback * 10); - const int16_t coolingOnThreshold = mOccupiedCoolingSetpoint + static_cast(mOccupiedSetback * 10); - - if (mRunningMode == ThermostatRunningModeEnum::kCool) - { - if (mLocalTemperature <= coolingOffThreshold) - { - ChipLogDetail(AppServer, "Eval Cool - Turning off"); - SetRunningMode(ThermostatRunningModeEnum::kOff); - } - else - { - ChipLogDetail(AppServer, "Eval Cool - Keep Cooling"); - } - } - else - { - if (mLocalTemperature >= coolingOnThreshold) - { - ChipLogDetail(AppServer, "Eval Cool - Turn on"); - SetRunningMode(ThermostatRunningModeEnum::kCool); - } - else - { - ChipLogDetail(AppServer, "Eval Cool - Nothing to do"); - } - } -} - -static const char * SystemModeString(SystemModeEnum systemMode) -{ - switch (systemMode) - { - case SystemModeEnum::kOff: - return "Off"; - case SystemModeEnum::kAuto: - return "Auto"; - case SystemModeEnum::kCool: - return "Cool"; - case SystemModeEnum::kHeat: - return "Heat"; - default: - return "Unknown"; - } -} - -static const char * RunningModeString(ThermostatRunningModeEnum runningMode) -{ - switch (runningMode) - { - case ThermostatRunningModeEnum::kOff: - return "Off"; - case ThermostatRunningModeEnum::kCool: - return "Cool"; - case ThermostatRunningModeEnum::kHeat: - return "Heat"; - default: - return "Unknown"; - } -} - -void emberAfThermostatClusterInitCallback(EndpointId endpoint) -{ - ChipLogProgress(Zcl, "Starting Thermostat Manager"); - ThermostatManager().Init(); - - // Register the delegate for the Thermostat - auto & delegate = ThermostatDelegate::GetInstance(); - // Set the default delegate for endpoint kThermostatEndpoint. - VerifyOrDie(endpoint == kThermostatEndpoint); - SetDefaultDelegate(endpoint, &delegate); -} diff --git a/examples/thermostat/thermostat-common/BUILD.gn b/examples/thermostat/thermostat-common/BUILD.gn index 93a0c7540fb93a..1f8f839b4a4f3a 100644 --- a/examples/thermostat/thermostat-common/BUILD.gn +++ b/examples/thermostat/thermostat-common/BUILD.gn @@ -16,6 +16,10 @@ import("//build_overrides/chip.gni") import("${chip_root}/src/app/chip_data_model.gni") +config("config") { + include_dirs = [ "include" ] +} + chip_data_model("thermostat-common") { zap_file = "thermostat.zap" is_server = true diff --git a/examples/thermostat/linux/include/thermostat-delegate-impl.h b/examples/thermostat/thermostat-common/include/thermostat-delegate-impl.h similarity index 92% rename from examples/thermostat/linux/include/thermostat-delegate-impl.h rename to examples/thermostat/thermostat-common/include/thermostat-delegate-impl.h index 6bf9d02cea462f..9edf13f839df44 100644 --- a/examples/thermostat/linux/include/thermostat-delegate-impl.h +++ b/examples/thermostat/thermostat-common/include/thermostat-delegate-impl.h @@ -44,9 +44,7 @@ class ThermostatDelegate : public Delegate public: static inline ThermostatDelegate & GetInstance() { return sInstance; } - std::optional - GetAtomicWriteTimeout(DataModel::DecodableList attributeRequests, - System::Clock::Milliseconds16 timeoutRequest) override; + std::optional GetMaxAtomicWriteTimeout(chip::AttributeId attributeId) override; CHIP_ERROR GetPresetTypeAtIndex(size_t index, Structs::PresetTypeStruct::Type & presetType) override; @@ -64,7 +62,7 @@ class ThermostatDelegate : public Delegate CHIP_ERROR GetPendingPresetAtIndex(size_t index, PresetStructWithOwnedMembers & preset) override; - CHIP_ERROR ApplyPendingPresets() override; + CHIP_ERROR CommitPendingPresets() override; void ClearPendingPresetList() override; diff --git a/examples/thermostat/linux/thermostat-delegate-impl.cpp b/examples/thermostat/thermostat-common/src/thermostat-delegate-impl.cpp similarity index 75% rename from examples/thermostat/linux/thermostat-delegate-impl.cpp rename to examples/thermostat/thermostat-common/src/thermostat-delegate-impl.cpp index b931db20b7c4f7..8c411cd5a9176e 100644 --- a/examples/thermostat/linux/thermostat-delegate-impl.cpp +++ b/examples/thermostat/thermostat-common/src/thermostat-delegate-impl.cpp @@ -17,7 +17,6 @@ */ #include -#include #include #include @@ -36,34 +35,12 @@ ThermostatDelegate::ThermostatDelegate() mNextFreeIndexInPresetsList = 0; mNextFreeIndexInPendingPresetsList = 0; - InitializePresetTypes(); InitializePresets(); memset(mActivePresetHandleData, 0, sizeof(mActivePresetHandleData)); mActivePresetHandleDataSize = 0; } -void ThermostatDelegate::InitializePresetTypes() -{ - PresetScenarioEnum presetScenarioEnumArray[kMaxNumberOfPresetTypes] = { - PresetScenarioEnum::kOccupied, PresetScenarioEnum::kUnoccupied, PresetScenarioEnum::kSleep, - PresetScenarioEnum::kWake, PresetScenarioEnum::kVacation, PresetScenarioEnum::kGoingToSleep - }; - static_assert(ArraySize(presetScenarioEnumArray) <= ArraySize(mPresetTypes)); - - uint8_t index = 0; - for (PresetScenarioEnum presetScenario : presetScenarioEnumArray) - { - mPresetTypes[index].presetScenario = presetScenario; - mPresetTypes[index].numberOfPresets = kMaxNumberOfPresetsOfEachType; - mPresetTypes[index].presetTypeFeatures = - (presetScenario == PresetScenarioEnum::kOccupied || presetScenario == PresetScenarioEnum::kUnoccupied) - ? PresetTypeFeaturesBitmap::kAutomatic - : PresetTypeFeaturesBitmap::kSupportsNames; - index++; - } -} - void ThermostatDelegate::InitializePresets() { // Initialize the presets with 2 built in presets - occupied and unoccupied. @@ -94,9 +71,26 @@ void ThermostatDelegate::InitializePresets() CHIP_ERROR ThermostatDelegate::GetPresetTypeAtIndex(size_t index, PresetTypeStruct::Type & presetType) { - if (index < ArraySize(mPresetTypes)) + static PresetTypeStruct::Type presetTypes[] = { + { .presetScenario = PresetScenarioEnum::kOccupied, + .numberOfPresets = kMaxNumberOfPresetsOfEachType, + .presetTypeFeatures = to_underlying(PresetTypeFeaturesBitmap::kAutomatic) }, + { .presetScenario = PresetScenarioEnum::kUnoccupied, + .numberOfPresets = kMaxNumberOfPresetsOfEachType, + .presetTypeFeatures = to_underlying(PresetTypeFeaturesBitmap::kAutomatic) }, + { .presetScenario = PresetScenarioEnum::kSleep, + .numberOfPresets = kMaxNumberOfPresetsOfEachType, + .presetTypeFeatures = to_underlying(PresetTypeFeaturesBitmap::kSupportsNames) }, + { .presetScenario = PresetScenarioEnum::kWake, + .numberOfPresets = kMaxNumberOfPresetsOfEachType, + .presetTypeFeatures = to_underlying(PresetTypeFeaturesBitmap::kSupportsNames) }, + { .presetScenario = PresetScenarioEnum::kVacation, + .numberOfPresets = kMaxNumberOfPresetsOfEachType, + .presetTypeFeatures = to_underlying(PresetTypeFeaturesBitmap::kSupportsNames) }, + }; + if (index < ArraySize(presetTypes)) { - presetType = mPresetTypes[index]; + presetType = presetTypes[index]; return CHIP_NO_ERROR; } return CHIP_ERROR_PROVIDER_LIST_EXHAUSTED; @@ -158,45 +152,19 @@ CHIP_ERROR ThermostatDelegate::SetActivePresetHandle(const DataModel::Nullable -ThermostatDelegate::GetAtomicWriteTimeout(DataModel::DecodableList attributeRequests, - System::Clock::Milliseconds16 timeoutRequest) +std::optional ThermostatDelegate::GetMaxAtomicWriteTimeout(chip::AttributeId attributeId) { - auto attributeIdsIter = attributeRequests.begin(); - bool requestedPresets = false, requestedSchedules = false; - while (attributeIdsIter.Next()) - { - auto & attributeId = attributeIdsIter.GetValue(); - - switch (attributeId) - { - case Attributes::Presets::Id: - requestedPresets = true; - break; - case Attributes::Schedules::Id: - requestedSchedules = true; - break; - default: - return System::Clock::Milliseconds16(0); - } - } - if (attributeIdsIter.GetStatus() != CHIP_NO_ERROR) - { - return System::Clock::Milliseconds16(0); - } - auto timeout = System::Clock::Milliseconds16(0); - if (requestedPresets) + switch (attributeId) { + case Attributes::Presets::Id: // If the client expects to edit the presets, then we'll give it 3 seconds to do so - timeout += std::chrono::milliseconds(3000); - } - if (requestedSchedules) - { + return std::chrono::milliseconds(3000); + case Attributes::Schedules::Id: // If the client expects to edit the schedules, then we'll give it 9 seconds to do so - timeout += std::chrono::milliseconds(9000); + return std::chrono::milliseconds(9000); + default: + return std::nullopt; } - // If the client requested an even smaller timeout, then use that one - return std::min(timeoutRequest, timeout); } void ThermostatDelegate::InitializePendingPresets() @@ -238,7 +206,7 @@ CHIP_ERROR ThermostatDelegate::GetPendingPresetAtIndex(size_t index, PresetStruc return CHIP_ERROR_PROVIDER_LIST_EXHAUSTED; } -CHIP_ERROR ThermostatDelegate::ApplyPendingPresets() +CHIP_ERROR ThermostatDelegate::CommitPendingPresets() { mNextFreeIndexInPresetsList = 0; for (uint8_t indexInPendingPresets = 0; indexInPendingPresets < mNextFreeIndexInPendingPresetsList; indexInPendingPresets++) diff --git a/src/app/chip_data_model.gni b/src/app/chip_data_model.gni index 3e4448a3bee467..01d47a47cb7184 100644 --- a/src/app/chip_data_model.gni +++ b/src/app/chip_data_model.gni @@ -428,6 +428,8 @@ template("chip_data_model") { ] } else if (cluster == "thermostat-server") { sources += [ + "${_app_root}/clusters/${cluster}/${cluster}-atomic.cpp", + "${_app_root}/clusters/${cluster}/${cluster}-presets.cpp", "${_app_root}/clusters/${cluster}/${cluster}.cpp", "${_app_root}/clusters/${cluster}/${cluster}.h", "${_app_root}/clusters/${cluster}/PresetStructWithOwnedMembers.cpp", diff --git a/src/app/clusters/thermostat-server/thermostat-delegate.h b/src/app/clusters/thermostat-server/thermostat-delegate.h index 0f89f69468099b..ccb690a34fba60 100644 --- a/src/app/clusters/thermostat-server/thermostat-delegate.h +++ b/src/app/clusters/thermostat-server/thermostat-delegate.h @@ -39,15 +39,12 @@ class Delegate virtual ~Delegate() = default; /** - * @brief Get the maximum timeout for atomically writing to a set of attributes + * @brief Get the maximum timeout for atomically writing to an attribute * - * @param[in] attributeRequests The list of attributes to write to. - * @param[out] timeoutRequest The timeout proposed by the client. - * @return The maximum allowed timeout; zero if the request is invalid. + * @param[in] attributeId The attribute to write to. + * @return The maximum allowed timeout; nullopt if the request is invalid. */ - virtual std::optional - GetAtomicWriteTimeout(DataModel::DecodableList attributeRequests, - System::Clock::Milliseconds16 timeoutRequest) = 0; + virtual std::optional GetMaxAtomicWriteTimeout(chip::AttributeId attributeId) = 0; /** * @brief Get the preset type at a given index in the PresetTypes attribute @@ -129,7 +126,7 @@ class Delegate * @return CHIP_ERROR if the updates to the presets attribute failed to commit for some reason. * */ - virtual CHIP_ERROR ApplyPendingPresets() = 0; + virtual CHIP_ERROR CommitPendingPresets() = 0; /** * @brief Clears the pending presets list. diff --git a/src/app/clusters/thermostat-server/thermostat-server-atomic.cpp b/src/app/clusters/thermostat-server/thermostat-server-atomic.cpp new file mode 100644 index 00000000000000..2a6e52e504887e --- /dev/null +++ b/src/app/clusters/thermostat-server/thermostat-server-atomic.cpp @@ -0,0 +1,644 @@ +/** + * + * Copyright (c) 2024 Project CHIP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "thermostat-server.h" + +#include + +using namespace chip; +using namespace chip::app; +using namespace chip::app::Clusters; +using namespace chip::app::Clusters::Thermostat; +using namespace chip::app::Clusters::Thermostat::Attributes; +using namespace chip::app::Clusters::Thermostat::Structs; +using namespace chip::app::Clusters::Globals::Structs; +using namespace chip::Protocols::InteractionModel; + +namespace chip { +namespace app { +namespace Clusters { +namespace Thermostat { + +extern ThermostatAttrAccess gThermostatAttrAccess; + +/** + * @brief Callback that is called when the timeout for editing the presets expires. + * + * @param[in] systemLayer The system layer. + * @param[in] callbackContext The context passed to the timer callback. + */ +void TimerExpiredCallback(System::Layer * systemLayer, void * callbackContext) +{ + EndpointId endpoint = static_cast(reinterpret_cast(callbackContext)); + gThermostatAttrAccess.ResetAtomicWrite(endpoint); +} + +/** + * @brief Schedules a timer for the given timeout in milliseconds. + * + * @param[in] endpoint The endpoint to use. + * @param[in] timeoutMilliseconds The timeout in milliseconds. + */ +void ScheduleTimer(EndpointId endpoint, System::Clock::Milliseconds16 timeout) +{ + DeviceLayer::SystemLayer().StartTimer(timeout, TimerExpiredCallback, + reinterpret_cast(static_cast(endpoint))); +} + +/** + * @brief Clears the currently scheduled timer. + * + * @param[in] endpoint The endpoint to use. + */ +void ClearTimer(EndpointId endpoint) +{ + DeviceLayer::SystemLayer().CancelTimer(TimerExpiredCallback, reinterpret_cast(static_cast(endpoint))); +} + +/** + * @brief Get the source scoped node id. + * + * @param[in] commandObj The command handler object. + * + * @return The scoped node id of the source node. If the scoped node id is not retreived, return ScopedNodeId(). + */ +ScopedNodeId GetSourceScopedNodeId(CommandHandler * commandObj) +{ + ScopedNodeId sourceNodeId = ScopedNodeId(); + auto sessionHandle = commandObj->GetExchangeContext()->GetSessionHandle(); + + if (sessionHandle->IsSecureSession()) + { + sourceNodeId = sessionHandle->AsSecureSession()->GetPeer(); + } + else if (sessionHandle->IsGroupSession()) + { + sourceNodeId = sessionHandle->AsIncomingGroupSession()->GetPeer(); + } + return sourceNodeId; +} + +/** + * @brief Counts the number of attribute requests + * + * @param attributeRequests The decodable list of attribute IDs + * @param attributeRequestCount The total number of attribute requests + * @param requestedPresets Whether the Presets attribute was requested + * @param requestedSchedules Whether the Schedules attribute was requested + * @return true if the attribute list was counted + * @return false if there was an error reading the list + */ +bool CountAttributeRequests(const DataModel::DecodableList attributeRequests, size_t & attributeRequestCount, + bool & requestedPresets, bool & requestedSchedules) +{ + attributeRequestCount = 0; + requestedPresets = false; + requestedSchedules = false; + auto attributeIdsIter = attributeRequests.begin(); + while (attributeIdsIter.Next()) + { + auto & attributeId = attributeIdsIter.GetValue(); + switch (attributeId) + { + case Presets::Id: + requestedPresets = true; + break; + case Schedules::Id: + requestedSchedules = true; + break; + default: + break; + } + attributeRequestCount++; + } + return attributeIdsIter.GetStatus() == CHIP_NO_ERROR; +} + +/// @brief Builds the list of attribute statuses to return from an AtomicRequest invocation +/// @param endpoint The associated endpoint for the AtomicRequest invocation +/// @param attributeRequests The list of requested attributes +/// @param attributeStatusCount The number of attribute statuses in attributeStatuses +/// @param attributeStatuses The status of each requested attribute, plus additional attributes if needed +/// @return Status::Success if the request is valid, an error status if it is not +Status BuildAttributeStatuses(const EndpointId endpoint, const DataModel::DecodableList attributeRequests, + Platform::ScopedMemoryBufferWithSize & attributeStatuses) +{ + + bool requestedPresets = false, requestedSchedules = false; + size_t attributeStatusCount = 0; + if (!CountAttributeRequests(attributeRequests, attributeStatusCount, requestedPresets, requestedSchedules)) + { + // We errored reading the list + return Status::InvalidCommand; + } + if (attributeStatusCount == 0) + { + // List can't be empty + return Status::InvalidCommand; + } + attributeStatuses.Alloc(attributeStatusCount); + for (size_t i = 0; i < attributeStatusCount; ++i) + { + attributeStatuses[i].attributeID = kInvalidAttributeId; + attributeStatuses[i].statusCode = 0; + } + auto attributeIdsIter = attributeRequests.begin(); + size_t index = 0; + while (attributeIdsIter.Next()) + { + auto & attributeId = attributeIdsIter.GetValue(); + + for (size_t i = 0; i < index; ++i) + { + auto & attributeStatus = attributeStatuses[i]; + if (attributeStatus.attributeID == attributeId) + { + // Double-requesting an attribute is invalid + return Status::InvalidCommand; + } + } + attributeStatuses[index].attributeID = attributeId; + attributeStatuses[index].statusCode = to_underlying(Status::Success); + index++; + } + if (attributeIdsIter.GetStatus() != CHIP_NO_ERROR) + { + return Status::InvalidCommand; + } + for (size_t i = 0; i < index; ++i) + { + auto & attributeStatus = attributeStatuses[i]; + const EmberAfAttributeMetadata * metadata = + emberAfLocateAttributeMetadata(endpoint, Thermostat::Id, attributeStatus.attributeID); + + if (metadata == nullptr) + { + // This is not a valid attribute on the Thermostat cluster on the supplied endpoint + return Status::InvalidCommand; + } + } + return Status::Success; +} + +bool ThermostatAttrAccess::InAtomicWrite(EndpointId endpoint, Optional attributeId) +{ + + uint16_t ep = + emberAfGetClusterServerEndpointIndex(endpoint, Thermostat::Id, MATTER_DM_THERMOSTAT_CLUSTER_SERVER_ENDPOINT_COUNT); + + if (ep >= ArraySize(mAtomicWriteSessions)) + { + return false; + } + auto & atomicWriteSession = mAtomicWriteSessions[ep]; + if (atomicWriteSession.state != AtomicWriteState::Open) + { + return false; + } + if (!attributeId.HasValue()) + { + return true; + } + for (size_t i = 0; i < atomicWriteSession.attributeIds.AllocatedSize(); ++i) + { + if (atomicWriteSession.attributeIds[i] == attributeId.Value()) + { + return true; + } + } + return false; +} + +bool ThermostatAttrAccess::InAtomicWrite(EndpointId endpoint, const Access::SubjectDescriptor & subjectDescriptor, + Optional attributeId) +{ + if (!InAtomicWrite(endpoint, attributeId)) + { + return false; + } + return subjectDescriptor.authMode == Access::AuthMode::kCase && + GetAtomicWriteOriginatorScopedNodeId(endpoint) == ScopedNodeId(subjectDescriptor.subject, subjectDescriptor.fabricIndex); +} + +bool ThermostatAttrAccess::InAtomicWrite(EndpointId endpoint, CommandHandler * commandObj, Optional attributeId) +{ + if (!InAtomicWrite(endpoint, attributeId)) + { + return false; + } + ScopedNodeId sourceNodeId = GetSourceScopedNodeId(commandObj); + return GetAtomicWriteOriginatorScopedNodeId(endpoint) == sourceNodeId; +} + +bool ThermostatAttrAccess::InAtomicWrite( + EndpointId endpoint, CommandHandler * commandObj, + Platform::ScopedMemoryBufferWithSize & attributeStatuses) +{ + uint16_t ep = + emberAfGetClusterServerEndpointIndex(endpoint, Thermostat::Id, MATTER_DM_THERMOSTAT_CLUSTER_SERVER_ENDPOINT_COUNT); + + if (ep >= ArraySize(mAtomicWriteSessions)) + { + return false; + } + auto & atomicWriteSession = mAtomicWriteSessions[ep]; + if (atomicWriteSession.state != AtomicWriteState::Open) + { + return false; + } + if (atomicWriteSession.attributeIds.AllocatedSize() == 0 || + atomicWriteSession.attributeIds.AllocatedSize() != attributeStatuses.AllocatedSize()) + { + return false; + } + for (size_t i = 0; i < atomicWriteSession.attributeIds.AllocatedSize(); ++i) + { + bool hasAttribute = false; + auto attributeId = atomicWriteSession.attributeIds[i]; + for (size_t j = 0; j < attributeStatuses.AllocatedSize(); ++j) + { + auto & attributeStatus = attributeStatuses[j]; + if (attributeStatus.attributeID == attributeId) + { + hasAttribute = true; + break; + } + } + if (!hasAttribute) + { + return false; + } + } + return true; +} + +bool ThermostatAttrAccess::SetAtomicWrite( + EndpointId endpoint, ScopedNodeId originatorNodeId, AtomicWriteState state, + Platform::ScopedMemoryBufferWithSize & attributeStatuses) +{ + uint16_t ep = + emberAfGetClusterServerEndpointIndex(endpoint, Thermostat::Id, MATTER_DM_THERMOSTAT_CLUSTER_SERVER_ENDPOINT_COUNT); + + if (ep >= ArraySize(mAtomicWriteSessions)) + { + return false; + } + + auto & atomicWriteSession = mAtomicWriteSessions[ep]; + atomicWriteSession.endpointId = endpoint; + if (!atomicWriteSession.attributeIds.Alloc(attributeStatuses.AllocatedSize())) + { + atomicWriteSession.state = AtomicWriteState::Closed; + atomicWriteSession.nodeId = ScopedNodeId(); + return false; + } + + atomicWriteSession.state = state; + atomicWriteSession.nodeId = originatorNodeId; + + for (size_t i = 0; i < attributeStatuses.AllocatedSize(); ++i) + { + atomicWriteSession.attributeIds[i] = attributeStatuses[i].attributeID; + } + return true; +} + +void ThermostatAttrAccess::ResetAtomicWrite(EndpointId endpoint) +{ + auto delegate = GetDelegate(endpoint); + if (delegate != nullptr) + { + delegate->ClearPendingPresetList(); + } + ClearTimer(endpoint); + uint16_t ep = + emberAfGetClusterServerEndpointIndex(endpoint, Thermostat::Id, MATTER_DM_THERMOSTAT_CLUSTER_SERVER_ENDPOINT_COUNT); + + if (ep >= ArraySize(mAtomicWriteSessions)) + { + return; + } + auto & atomicWriteSession = mAtomicWriteSessions[ep]; + atomicWriteSession.state = AtomicWriteState::Closed; + atomicWriteSession.endpointId = endpoint; + atomicWriteSession.nodeId = ScopedNodeId(); + atomicWriteSession.attributeIds.Free(); +} + +ScopedNodeId ThermostatAttrAccess::GetAtomicWriteOriginatorScopedNodeId(const EndpointId endpoint) +{ + ScopedNodeId originatorNodeId = ScopedNodeId(); + uint16_t ep = + emberAfGetClusterServerEndpointIndex(endpoint, Thermostat::Id, MATTER_DM_THERMOSTAT_CLUSTER_SERVER_ENDPOINT_COUNT); + + if (ep < ArraySize(mAtomicWriteSessions)) + { + originatorNodeId = mAtomicWriteSessions[ep].nodeId; + } + return originatorNodeId; +} + +void SendAtomicResponse(CommandHandler * commandObj, const ConcreteCommandPath & commandPath, Status status, + const Platform::ScopedMemoryBufferWithSize & attributeStatuses, + Optional timeout = NullOptional) +{ + Commands::AtomicResponse::Type response; + response.statusCode = to_underlying(status); + response.attributeStatus = + DataModel::List(attributeStatuses.Get(), attributeStatuses.AllocatedSize()); + response.timeout = timeout; + commandObj->AddResponse(commandPath, response); +} + +void ThermostatAttrAccess::BeginAtomicWrite(CommandHandler * commandObj, const ConcreteCommandPath & commandPath, + const Commands::AtomicRequest::DecodableType & commandData) +{ + EndpointId endpoint = commandPath.mEndpointId; + + auto delegate = GetDelegate(endpoint); + + if (delegate == nullptr) + { + ChipLogError(Zcl, "Delegate is null"); + commandObj->AddStatus(commandPath, Status::InvalidInState); + return; + } + + Platform::ScopedMemoryBufferWithSize attributeStatuses; + auto status = BuildAttributeStatuses(endpoint, commandData.attributeRequests, attributeStatuses); + if (status != Status::Success) + { + commandObj->AddStatus(commandPath, status); + return; + } + + if (InAtomicWrite(endpoint, commandObj)) + { + // This client already has an open atomic write + commandObj->AddStatus(commandPath, Status::InvalidInState); + return; + } + + if (!commandData.timeout.HasValue()) + { + commandObj->AddStatus(commandPath, Status::InvalidCommand); + return; + } + + auto maximumTimeout = System::Clock::Milliseconds16(0); + auto attributeIdsIter = commandData.attributeRequests.begin(); + while (attributeIdsIter.Next()) + { + auto & attributeId = attributeIdsIter.GetValue(); + switch (attributeId) + { + case Presets::Id: + case Schedules::Id: + auto attributeTimeout = delegate->GetMaxAtomicWriteTimeout(attributeId); + + if (attributeTimeout.has_value()) + { + // Add to the maximum timeout + maximumTimeout += attributeTimeout.value(); + } + break; + } + } + + status = Status::Success; + for (size_t i = 0; i < attributeStatuses.AllocatedSize(); ++i) + { + auto & attributeStatus = attributeStatuses[i]; + auto statusCode = Status::Success; + switch (attributeStatus.attributeID) + { + case Presets::Id: + case Schedules::Id: + statusCode = InAtomicWrite(endpoint, MakeOptional(attributeStatus.attributeID)) ? Status::Busy : Status::Success; + break; + default: + statusCode = Status::InvalidCommand; + break; + } + if (statusCode != Status::Success) + { + status = Status::Failure; + } + attributeStatus.statusCode = to_underlying(statusCode); + } + + auto timeout = std::min(System::Clock::Milliseconds16(commandData.timeout.Value()), maximumTimeout); + if (timeout.count() == 0) + { + commandObj->AddStatus(commandPath, Status::InvalidInState); + return; + } + + if (status == Status::Success) + { + if (!SetAtomicWrite(endpoint, GetSourceScopedNodeId(commandObj), AtomicWriteState::Open, attributeStatuses)) + { + for (size_t i = 0; i < attributeStatuses.AllocatedSize(); ++i) + { + attributeStatuses[i].statusCode = to_underlying(Status::ResourceExhausted); + } + status = Status::Failure; + } + else + { + // This is a valid request to open an atomic write. Tell the delegate it + // needs to keep track of a pending preset list now. + delegate->InitializePendingPresets(); + ScheduleTimer(endpoint, timeout); + } + } + + SendAtomicResponse(commandObj, commandPath, status, attributeStatuses, MakeOptional(timeout.count())); +} + +void ThermostatAttrAccess::CommitAtomicWrite(CommandHandler * commandObj, const ConcreteCommandPath & commandPath, + const Commands::AtomicRequest::DecodableType & commandData) +{ + EndpointId endpoint = commandPath.mEndpointId; + auto delegate = GetDelegate(endpoint); + + if (delegate == nullptr) + { + ChipLogError(Zcl, "Delegate is null"); + commandObj->AddStatus(commandPath, Status::InvalidInState); + return; + } + + Platform::ScopedMemoryBufferWithSize attributeStatuses; + auto status = BuildAttributeStatuses(endpoint, commandData.attributeRequests, attributeStatuses); + if (status != Status::Success) + { + commandObj->AddStatus(commandPath, status); + return; + } + + if (!InAtomicWrite(endpoint, commandObj, attributeStatuses)) + { + commandObj->AddStatus(commandPath, Status::InvalidInState); + return; + } + + status = Status::Success; + for (size_t i = 0; i < attributeStatuses.AllocatedSize(); ++i) + { + auto & attributeStatus = attributeStatuses[i]; + auto statusCode = Status::Success; + switch (attributeStatus.attributeID) + { + case Presets::Id: + statusCode = PrecommitPresets(endpoint); + break; + case Schedules::Id: + statusCode = Status::Success; + break; + default: + commandObj->AddStatus(commandPath, Status::InvalidInState); + return; + } + attributeStatus.statusCode = to_underlying(statusCode); + if (statusCode != Status::Success) + { + status = Status::Failure; + } + } + + if (status == Status::Success) + { + for (size_t i = 0; i < attributeStatuses.AllocatedSize(); ++i) + { + auto & attributeStatus = attributeStatuses[i]; + auto statusCode = Status::Success; + CHIP_ERROR err; + switch (attributeStatus.attributeID) + { + case Presets::Id: + err = delegate->CommitPendingPresets(); + if (err != CHIP_NO_ERROR) + { + statusCode = Status::InvalidInState; + } + break; + case Schedules::Id: + break; + default: + // Not reachable, since we returned in this situation above. + break; + } + attributeStatus.statusCode = to_underlying(statusCode); + if (statusCode != Status::Success) + { + status = Status::Failure; + } + } + } + + ResetAtomicWrite(endpoint); + SendAtomicResponse(commandObj, commandPath, status, attributeStatuses); +} + +void ThermostatAttrAccess::RollbackAtomicWrite(CommandHandler * commandObj, const ConcreteCommandPath & commandPath, + const Commands::AtomicRequest::DecodableType & commandData) +{ + + EndpointId endpoint = commandPath.mEndpointId; + auto delegate = GetDelegate(endpoint); + + if (delegate == nullptr) + { + ChipLogError(Zcl, "Delegate is null"); + commandObj->AddStatus(commandPath, Status::InvalidInState); + return; + } + + Platform::ScopedMemoryBufferWithSize attributeStatuses; + auto status = BuildAttributeStatuses(endpoint, commandData.attributeRequests, attributeStatuses); + if (status != Status::Success) + { + commandObj->AddStatus(commandPath, status); + return; + } + + if (!InAtomicWrite(endpoint, commandObj, attributeStatuses)) + { + // There's no open atomic write + commandObj->AddStatus(commandPath, Status::InvalidInState); + return; + } + + ResetAtomicWrite(endpoint); + + for (size_t i = 0; i < attributeStatuses.AllocatedSize(); ++i) + { + attributeStatuses[i].statusCode = to_underlying(Status::Success); + } + + SendAtomicResponse(commandObj, commandPath, status, attributeStatuses); +} + +void MatterThermostatClusterServerShutdownCallback(EndpointId endpoint) +{ + ChipLogProgress(Zcl, "Shutting down thermostat server cluster on endpoint %d", endpoint); + gThermostatAttrAccess.ResetAtomicWrite(endpoint); +} + +bool emberAfThermostatClusterAtomicRequestCallback(CommandHandler * commandObj, const ConcreteCommandPath & commandPath, + const Clusters::Thermostat::Commands::AtomicRequest::DecodableType & commandData) +{ + auto & requestType = commandData.requestType; + + // If we've gotten this far, then the client has manage permission to call AtomicRequest, which is also the + // privilege necessary to write to the atomic attributes, so no need to check + + switch (requestType) + { + case Globals::AtomicRequestTypeEnum::kBeginWrite: + gThermostatAttrAccess.BeginAtomicWrite(commandObj, commandPath, commandData); + return true; + case Globals::AtomicRequestTypeEnum::kCommitWrite: + gThermostatAttrAccess.CommitAtomicWrite(commandObj, commandPath, commandData); + return true; + case Globals::AtomicRequestTypeEnum::kRollbackWrite: + gThermostatAttrAccess.RollbackAtomicWrite(commandObj, commandPath, commandData); + return true; + case Globals::AtomicRequestTypeEnum::kUnknownEnumValue: + commandObj->AddStatus(commandPath, Status::InvalidCommand); + return true; + } + + return false; +} + +} // namespace Thermostat +} // namespace Clusters +} // namespace app +} // namespace chip + +bool emberAfThermostatClusterAtomicRequestCallback(CommandHandler * commandObj, const ConcreteCommandPath & commandPath, + const Clusters::Thermostat::Commands::AtomicRequest::DecodableType & commandData) +{ + return Thermostat::emberAfThermostatClusterAtomicRequestCallback(commandObj, commandPath, commandData); +} + +void MatterThermostatClusterServerShutdownCallback(EndpointId endpoint) +{ + Thermostat::MatterThermostatClusterServerShutdownCallback(endpoint); +} diff --git a/src/app/clusters/thermostat-server/thermostat-server-presets.cpp b/src/app/clusters/thermostat-server/thermostat-server-presets.cpp new file mode 100644 index 00000000000000..e57c2f9c95f8fd --- /dev/null +++ b/src/app/clusters/thermostat-server/thermostat-server-presets.cpp @@ -0,0 +1,546 @@ +/** + * + * Copyright (c) 2024 Project CHIP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "thermostat-server.h" + +#include + +using namespace chip; +using namespace chip::app; +using namespace chip::app::Clusters; +using namespace chip::app::Clusters::Thermostat; +using namespace chip::app::Clusters::Thermostat::Attributes; +using namespace chip::app::Clusters::Thermostat::Structs; +using namespace chip::app::Clusters::Globals::Structs; +using namespace chip::Protocols::InteractionModel; + +namespace { + +/** + * @brief Check if a preset is valid. + * + * @param[in] preset The preset to check. + * + * @return true If the preset is valid i.e the PresetHandle (if not null) fits within size constraints and the presetScenario enum + * value is valid. Otherwise, return false. + */ +bool IsValidPresetEntry(const PresetStruct::Type & preset) +{ + // Check that the preset handle is not too long. + if (!preset.presetHandle.IsNull() && preset.presetHandle.Value().size() > kPresetHandleSize) + { + return false; + } + + // Ensure we have a valid PresetScenario. + return (preset.presetScenario != PresetScenarioEnum::kUnknownEnumValue); +} + +/** + * @brief Checks if the preset is built-in + * + * @param[in] preset The preset to check. + * + * @return true If the preset is built-in, false otherwise. + */ +bool IsBuiltIn(const PresetStructWithOwnedMembers & preset) +{ + return preset.GetBuiltIn().ValueOr(false); +} + +/** + * @brief Checks if the presets are matching i.e the presetHandles are the same. + * + * @param[in] preset The preset to check. + * @param[in] presetToMatch The preset to match with. + * + * @return true If the presets match, false otherwise. If both preset handles are null, returns false + */ +bool PresetHandlesExistAndMatch(const PresetStructWithOwnedMembers & preset, const PresetStructWithOwnedMembers & presetToMatch) +{ + return !preset.GetPresetHandle().IsNull() && !presetToMatch.GetPresetHandle().IsNull() && + preset.GetPresetHandle().Value().data_equal(presetToMatch.GetPresetHandle().Value()); +} + +/** + * @brief Finds an entry in the pending presets list that matches a preset. + * The presetHandle of the two presets must match. + * + * @param[in] delegate The delegate to use. + * @param[in] presetToMatch The preset to match with. + * + * @return true if a matching entry was found in the pending presets list, false otherwise. + */ +bool MatchingPendingPresetExists(Delegate * delegate, const PresetStructWithOwnedMembers & presetToMatch) +{ + VerifyOrReturnValue(delegate != nullptr, false); + + for (uint8_t i = 0; true; i++) + { + PresetStructWithOwnedMembers preset; + CHIP_ERROR err = delegate->GetPendingPresetAtIndex(i, preset); + + if (err == CHIP_ERROR_PROVIDER_LIST_EXHAUSTED) + { + break; + } + if (err != CHIP_NO_ERROR) + { + ChipLogError(Zcl, "MatchingPendingPresetExists: GetPendingPresetAtIndex failed with error %" CHIP_ERROR_FORMAT, + err.Format()); + return false; + } + + if (PresetHandlesExistAndMatch(preset, presetToMatch)) + { + return true; + } + } + return false; +} + +/** + * @brief Finds and returns an entry in the Presets attribute list that matches + * a preset, if such an entry exists. The presetToMatch must have a preset handle. + * + * @param[in] delegate The delegate to use. + * @param[in] presetToMatch The preset to match with. + * @param[out] matchingPreset The preset in the Presets attribute list that has the same PresetHandle as the presetToMatch. + * + * @return true if a matching entry was found in the presets attribute list, false otherwise. + */ +bool GetMatchingPresetInPresets(Delegate * delegate, const PresetStruct::Type & presetToMatch, + PresetStructWithOwnedMembers & matchingPreset) +{ + VerifyOrReturnValue(delegate != nullptr, false); + + for (uint8_t i = 0; true; i++) + { + CHIP_ERROR err = delegate->GetPresetAtIndex(i, matchingPreset); + + if (err == CHIP_ERROR_PROVIDER_LIST_EXHAUSTED) + { + break; + } + if (err != CHIP_NO_ERROR) + { + ChipLogError(Zcl, "GetMatchingPresetInPresets: GetPresetAtIndex failed with error %" CHIP_ERROR_FORMAT, err.Format()); + return false; + } + + // Note: presets coming from our delegate always have a handle. + if (presetToMatch.presetHandle.Value().data_equal(matchingPreset.GetPresetHandle().Value())) + { + return true; + } + } + return false; +} + +/** + * @brief Returns the length of the list of presets if the pending presets were to be applied. The size of the pending presets list + * calculated, after all the constraint checks are done, is the new size of the updated Presets attribute since the pending + * preset list is expected to have all existing presets with or without edits plus new presets. + * This is called before changes are actually applied. + * + * @param[in] delegate The delegate to use. + * + * @return count of the updated Presets attribute if the pending presets were applied to it. Return 0 for error cases. + */ +uint8_t CountNumberOfPendingPresets(Delegate * delegate) +{ + uint8_t numberOfPendingPresets = 0; + + VerifyOrReturnValue(delegate != nullptr, 0); + + for (uint8_t i = 0; true; i++) + { + PresetStructWithOwnedMembers pendingPreset; + CHIP_ERROR err = delegate->GetPendingPresetAtIndex(i, pendingPreset); + + if (err == CHIP_ERROR_PROVIDER_LIST_EXHAUSTED) + { + break; + } + if (err != CHIP_NO_ERROR) + { + ChipLogError(Zcl, "CountNumberOfPendingPresets: GetPendingPresetAtIndex failed with error %" CHIP_ERROR_FORMAT, + err.Format()); + return 0; + } + numberOfPendingPresets++; + } + + return numberOfPendingPresets; +} + +/** + * @brief Checks if the presetScenario is present in the PresetTypes attribute. + * + * @param[in] delegate The delegate to use. + * @param[in] presetScenario The presetScenario to match with. + * + * @return true if the presetScenario is found, false otherwise. + */ +bool PresetScenarioExistsInPresetTypes(Delegate * delegate, PresetScenarioEnum presetScenario) +{ + VerifyOrReturnValue(delegate != nullptr, false); + + for (uint8_t i = 0; true; i++) + { + PresetTypeStruct::Type presetType; + auto err = delegate->GetPresetTypeAtIndex(i, presetType); + if (err != CHIP_NO_ERROR) + { + return false; + } + + if (presetType.presetScenario == presetScenario) + { + return true; + } + } + return false; +} + +/** + * @brief Returns the count of preset entries in the pending presets list that have the matching presetHandle. + * @param[in] delegate The delegate to use. + * @param[in] presetHandleToMatch The preset handle to match. + * + * @return count of the number of presets found with the matching presetHandle. Returns 0 if no matching presets were found. + */ +uint8_t CountPresetsInPendingListWithPresetHandle(Delegate * delegate, const ByteSpan & presetHandleToMatch) +{ + uint8_t count = 0; + VerifyOrReturnValue(delegate != nullptr, count); + + for (uint8_t i = 0; true; i++) + { + PresetStructWithOwnedMembers preset; + auto err = delegate->GetPendingPresetAtIndex(i, preset); + if (err != CHIP_NO_ERROR) + { + return count; + } + + DataModel::Nullable presetHandle = preset.GetPresetHandle(); + if (!presetHandle.IsNull() && presetHandle.Value().data_equal(presetHandleToMatch)) + { + count++; + } + } + return count; +} + +/** + * @brief Checks if the presetType for the given preset scenario supports name in the presetTypeFeatures bitmap. + * + * @param[in] delegate The delegate to use. + * @param[in] presetScenario The presetScenario to match with. + * + * @return true if the presetType for the given preset scenario supports name, false otherwise. + */ +bool PresetTypeSupportsNames(Delegate * delegate, PresetScenarioEnum scenario) +{ + VerifyOrReturnValue(delegate != nullptr, false); + + for (uint8_t i = 0; true; i++) + { + PresetTypeStruct::Type presetType; + auto err = delegate->GetPresetTypeAtIndex(i, presetType); + if (err != CHIP_NO_ERROR) + { + return false; + } + + if (presetType.presetScenario == scenario) + { + return (presetType.presetTypeFeatures.Has(PresetTypeFeaturesBitmap::kSupportsNames)); + } + } + return false; +} + +/** + * @brief Checks if the given preset handle is present in the presets attribute + * @param[in] delegate The delegate to use. + * @param[in] presetHandleToMatch The preset handle to match with. + * + * @return true if the given preset handle is present in the presets attribute list, false otherwise. + */ +bool IsPresetHandlePresentInPresets(Delegate * delegate, const ByteSpan & presetHandleToMatch) +{ + VerifyOrReturnValue(delegate != nullptr, false); + + PresetStructWithOwnedMembers matchingPreset; + for (uint8_t i = 0; true; i++) + { + CHIP_ERROR err = delegate->GetPresetAtIndex(i, matchingPreset); + + if (err == CHIP_ERROR_PROVIDER_LIST_EXHAUSTED) + { + return false; + } + + if (err != CHIP_NO_ERROR) + { + ChipLogError(Zcl, "IsPresetHandlePresentInPresets: GetPresetAtIndex failed with error %" CHIP_ERROR_FORMAT, + err.Format()); + return false; + } + + if (!matchingPreset.GetPresetHandle().IsNull() && matchingPreset.GetPresetHandle().Value().data_equal(presetHandleToMatch)) + { + return true; + } + } + return false; +} + +} // namespace + +namespace chip { +namespace app { +namespace Clusters { +namespace Thermostat { + +extern ThermostatAttrAccess gThermostatAttrAccess; +extern int16_t EnforceHeatingSetpointLimits(int16_t HeatingSetpoint, EndpointId endpoint); +extern int16_t EnforceCoolingSetpointLimits(int16_t CoolingSetpoint, EndpointId endpoint); + +Status ThermostatAttrAccess::SetActivePreset(EndpointId endpoint, DataModel::Nullable presetHandle) +{ + + auto delegate = GetDelegate(endpoint); + + if (delegate == nullptr) + { + ChipLogError(Zcl, "Delegate is null"); + return Status::InvalidInState; + } + + // If the preset handle passed in the command is not present in the Presets attribute, return INVALID_COMMAND. + if (!presetHandle.IsNull() && !IsPresetHandlePresentInPresets(delegate, presetHandle.Value())) + { + return Status::InvalidCommand; + } + + CHIP_ERROR err = delegate->SetActivePresetHandle(presetHandle); + + if (err != CHIP_NO_ERROR) + { + ChipLogError(Zcl, "Failed to set ActivePresetHandle with error %" CHIP_ERROR_FORMAT, err.Format()); + return StatusIB(err).mStatus; + } + + return Status::Success; +} + +CHIP_ERROR ThermostatAttrAccess::AppendPendingPreset(Thermostat::Delegate * delegate, const PresetStruct::Type & preset) +{ + if (!IsValidPresetEntry(preset)) + { + return CHIP_IM_GLOBAL_STATUS(ConstraintError); + } + + if (preset.presetHandle.IsNull()) + { + if (IsBuiltIn(preset)) + { + return CHIP_IM_GLOBAL_STATUS(ConstraintError); + } + } + else + { + auto & presetHandle = preset.presetHandle.Value(); + + // Per spec we need to check that: + // (a) There is an existing non-pending preset with this handle. + PresetStructWithOwnedMembers matchingPreset; + if (!GetMatchingPresetInPresets(delegate, preset, matchingPreset)) + { + return CHIP_IM_GLOBAL_STATUS(NotFound); + } + + // (b) There is no existing pending preset with this handle. + if (CountPresetsInPendingListWithPresetHandle(delegate, presetHandle) > 0) + { + return CHIP_IM_GLOBAL_STATUS(ConstraintError); + } + + // (c)/(d) The built-in fields do not have a mismatch. + // TODO: What's the story with nullability on the BuiltIn field? + if (!preset.builtIn.IsNull() && !matchingPreset.GetBuiltIn().IsNull() && + preset.builtIn.Value() != matchingPreset.GetBuiltIn().Value()) + { + return CHIP_IM_GLOBAL_STATUS(ConstraintError); + } + } + + if (!PresetScenarioExistsInPresetTypes(delegate, preset.presetScenario)) + { + return CHIP_IM_GLOBAL_STATUS(ConstraintError); + } + + if (preset.name.HasValue() && !PresetTypeSupportsNames(delegate, preset.presetScenario)) + { + return CHIP_IM_GLOBAL_STATUS(ConstraintError); + } + + return delegate->AppendToPendingPresetList(preset); +} + +Status ThermostatAttrAccess::PrecommitPresets(EndpointId endpoint) +{ + auto delegate = GetDelegate(endpoint); + + if (delegate == nullptr) + { + ChipLogError(Zcl, "Delegate is null"); + return Status::InvalidInState; + } + + CHIP_ERROR err = CHIP_NO_ERROR; + + // For each preset in the presets attribute, check that the matching preset in the pending presets list does not + // violate any spec constraints. + for (uint8_t i = 0; true; i++) + { + PresetStructWithOwnedMembers preset; + err = delegate->GetPresetAtIndex(i, preset); + + if (err == CHIP_ERROR_PROVIDER_LIST_EXHAUSTED) + { + break; + } + if (err != CHIP_NO_ERROR) + { + ChipLogError(Zcl, + "emberAfThermostatClusterCommitPresetsSchedulesRequestCallback: GetPresetAtIndex failed with error " + "%" CHIP_ERROR_FORMAT, + err.Format()); + return Status::InvalidInState; + } + + bool found = MatchingPendingPresetExists(delegate, preset); + + // If a built in preset in the Presets attribute list is removed and not found in the pending presets list, return + // CONSTRAINT_ERROR. + if (IsBuiltIn(preset) && !found) + { + return Status::ConstraintError; + } + } + + // If there is an ActivePresetHandle set, find the preset in the pending presets list that matches the ActivePresetHandle + // attribute. If a preset is not found with the same presetHandle, return INVALID_IN_STATE. If there is no ActivePresetHandle + // attribute set, continue with other checks. + uint8_t buffer[kPresetHandleSize]; + MutableByteSpan activePresetHandleSpan(buffer); + auto activePresetHandle = DataModel::MakeNullable(activePresetHandleSpan); + + err = delegate->GetActivePresetHandle(activePresetHandle); + + if (err != CHIP_NO_ERROR) + { + return Status::InvalidInState; + } + + if (!activePresetHandle.IsNull()) + { + uint8_t count = CountPresetsInPendingListWithPresetHandle(delegate, activePresetHandle.Value()); + if (count == 0) + { + return Status::InvalidInState; + } + } + + // For each preset in the pending presets list, check that the preset does not violate any spec constraints. + for (uint8_t i = 0; true; i++) + { + PresetStructWithOwnedMembers pendingPreset; + err = delegate->GetPendingPresetAtIndex(i, pendingPreset); + + if (err == CHIP_ERROR_PROVIDER_LIST_EXHAUSTED) + { + break; + } + if (err != CHIP_NO_ERROR) + { + ChipLogError(Zcl, + "emberAfThermostatClusterCommitPresetsSchedulesRequestCallback: GetPendingPresetAtIndex failed with error " + "%" CHIP_ERROR_FORMAT, + err.Format()); + return Status::InvalidInState; + } + + // Enforce the Setpoint Limits for both the cooling and heating setpoints in the pending preset. + // TODO: This code does not work, because it's modifying our temporary copy. + Optional coolingSetpointValue = pendingPreset.GetCoolingSetpoint(); + if (coolingSetpointValue.HasValue()) + { + pendingPreset.SetCoolingSetpoint(MakeOptional(EnforceCoolingSetpointLimits(coolingSetpointValue.Value(), endpoint))); + } + + Optional heatingSetpointValue = pendingPreset.GetHeatingSetpoint(); + if (heatingSetpointValue.HasValue()) + { + pendingPreset.SetHeatingSetpoint(MakeOptional(EnforceHeatingSetpointLimits(heatingSetpointValue.Value(), endpoint))); + } + } + + uint8_t totalCount = CountNumberOfPendingPresets(delegate); + + uint8_t numberOfPresetsSupported = delegate->GetNumberOfPresets(); + + if (numberOfPresetsSupported == 0) + { + ChipLogError(Zcl, "emberAfThermostatClusterCommitPresetsSchedulesRequestCallback: Failed to get NumberOfPresets"); + return Status::InvalidInState; + } + + // If the expected length of the presets attribute with the applied changes exceeds the total number of presets supported, + // return RESOURCE_EXHAUSTED. Note that the changes are not yet applied. + if (numberOfPresetsSupported > 0 && totalCount > numberOfPresetsSupported) + { + return Status::ResourceExhausted; + } + + // TODO: Check if the number of presets for each presetScenario exceeds the max number of presets supported for that + // scenario. We plan to support only one preset for each presetScenario for our use cases so defer this for re-evaluation. + return Status::Success; +} + +bool emberAfThermostatClusterSetActivePresetRequestCallback(CommandHandler * commandObj, const ConcreteCommandPath & commandPath, + const Commands::SetActivePresetRequest::DecodableType & commandData) +{ + auto status = gThermostatAttrAccess.SetActivePreset(commandPath.mEndpointId, commandData.presetHandle); + commandObj->AddStatus(commandPath, status); + return true; +} + +} // namespace Thermostat +} // namespace Clusters +} // namespace app +} // namespace chip + +bool emberAfThermostatClusterSetActivePresetRequestCallback(CommandHandler * commandObj, const ConcreteCommandPath & commandPath, + const Commands::SetActivePresetRequest::DecodableType & commandData) +{ + return Thermostat::emberAfThermostatClusterSetActivePresetRequestCallback(commandObj, commandPath, commandData); +} diff --git a/src/app/clusters/thermostat-server/thermostat-server.cpp b/src/app/clusters/thermostat-server/thermostat-server.cpp index 8fe70211eeda1a..fe8ccf8aab3cf0 100644 --- a/src/app/clusters/thermostat-server/thermostat-server.cpp +++ b/src/app/clusters/thermostat-server/thermostat-server.cpp @@ -30,7 +30,6 @@ #include #include #include -#include using namespace chip; using namespace chip::app; @@ -38,8 +37,7 @@ using namespace chip::app::Clusters; using namespace chip::app::Clusters::Thermostat; using namespace chip::app::Clusters::Thermostat::Structs; using namespace chip::app::Clusters::Thermostat::Attributes; - -using imcode = Protocols::InteractionModel::Status; +using namespace Protocols::InteractionModel; constexpr int16_t kDefaultAbsMinHeatSetpointLimit = 700; // 7C (44.5 F) is the default constexpr int16_t kDefaultAbsMaxHeatSetpointLimit = 3000; // 30C (86 F) is the default @@ -69,381 +67,16 @@ constexpr int8_t kDefaultDeadBand = 25; // 2.5C is the default #define FEATURE_MAP_DEFAULT FEATURE_MAP_HEAT | FEATURE_MAP_COOL | FEATURE_MAP_AUTO -namespace { - -ThermostatAttrAccess gThermostatAttrAccess; - static_assert(kThermostatEndpointCount <= kEmberInvalidEndpointIndex, "Thermostat Delegate table size error"); Delegate * gDelegateTable[kThermostatEndpointCount] = { nullptr }; -Delegate * GetDelegate(EndpointId endpoint) -{ - uint16_t ep = - emberAfGetClusterServerEndpointIndex(endpoint, Thermostat::Id, MATTER_DM_THERMOSTAT_CLUSTER_SERVER_ENDPOINT_COUNT); - return (ep >= ArraySize(gDelegateTable) ? nullptr : gDelegateTable[ep]); -} - -/** - * @brief Check if a preset is valid. - * - * @param[in] preset The preset to check. - * - * @return true If the preset is valid i.e the PresetHandle (if not null) fits within size constraints and the presetScenario enum - * value is valid. Otherwise, return false. - */ -bool IsValidPresetEntry(const PresetStruct::Type & preset) -{ - // Check that the preset handle is not too long. - if (!preset.presetHandle.IsNull() && preset.presetHandle.Value().size() > kPresetHandleSize) - { - return false; - } - - // Ensure we have a valid PresetScenario. - return (preset.presetScenario != PresetScenarioEnum::kUnknownEnumValue); -} - -/** - * @brief Callback that is called when the timeout for editing the presets expires. - * - * @param[in] systemLayer The system layer. - * @param[in] callbackContext The context passed to the timer callback. - */ -void TimerExpiredCallback(System::Layer * systemLayer, void * callbackContext) -{ - EndpointId endpoint = static_cast(reinterpret_cast(callbackContext)); - - Delegate * delegate = GetDelegate(endpoint); - VerifyOrReturn(delegate != nullptr, ChipLogError(Zcl, "Delegate is null. Unable to handle timer expired")); - - delegate->ClearPendingPresetList(); - gThermostatAttrAccess.SetAtomicWrite(endpoint, ScopedNodeId(), kAtomicWriteState_Closed); -} - -/** - * @brief Schedules a timer for the given timeout in milliseconds. - * - * @param[in] endpoint The endpoint to use. - * @param[in] timeoutMilliseconds The timeout in milliseconds. - */ -void ScheduleTimer(EndpointId endpoint, System::Clock::Milliseconds16 timeout) -{ - DeviceLayer::SystemLayer().StartTimer(timeout, TimerExpiredCallback, - reinterpret_cast(static_cast(endpoint))); -} - -/** - * @brief Clears the currently scheduled timer. - * - * @param[in] endpoint The endpoint to use. - */ -void ClearTimer(EndpointId endpoint) -{ - DeviceLayer::SystemLayer().CancelTimer(TimerExpiredCallback, reinterpret_cast(static_cast(endpoint))); -} - -/** - * @brief Checks if the preset is built-in - * - * @param[in] preset The preset to check. - * - * @return true If the preset is built-in, false otherwise. - */ -bool IsBuiltIn(const PresetStructWithOwnedMembers & preset) -{ - return preset.GetBuiltIn().ValueOr(false); -} - -/** - * @brief Checks if the presets are matching i.e the presetHandles are the same. - * - * @param[in] preset The preset to check. - * @param[in] presetToMatch The preset to match with. - * - * @return true If the presets match, false otherwise. If both preset handles are null, returns false - */ -bool PresetHandlesExistAndMatch(const PresetStructWithOwnedMembers & preset, const PresetStructWithOwnedMembers & presetToMatch) -{ - return !preset.GetPresetHandle().IsNull() && !presetToMatch.GetPresetHandle().IsNull() && - preset.GetPresetHandle().Value().data_equal(presetToMatch.GetPresetHandle().Value()); -} - -/** - * @brief Get the source scoped node id. - * - * @param[in] commandObj The command handler object. - * - * @return The scoped node id of the source node. If the scoped node id is not retreived, return ScopedNodeId(). - */ -ScopedNodeId GetSourceScopedNodeId(CommandHandler * commandObj) -{ - ScopedNodeId sourceNodeId = ScopedNodeId(); - auto sessionHandle = commandObj->GetExchangeContext()->GetSessionHandle(); - - if (sessionHandle->IsSecureSession()) - { - sourceNodeId = sessionHandle->AsSecureSession()->GetPeer(); - } - else if (sessionHandle->IsGroupSession()) - { - sourceNodeId = sessionHandle->AsIncomingGroupSession()->GetPeer(); - } - return sourceNodeId; -} - -/** - * @brief Discards pending atomic writes and atomic state. - * - * @param[in] delegate The delegate to use. - * @param[in] endpoint The endpoint to use. - * - */ -void resetAtomicWrite(Delegate * delegate, EndpointId endpoint) -{ - if (delegate != nullptr) - { - delegate->ClearPendingPresetList(); - } - ClearTimer(endpoint); - gThermostatAttrAccess.SetAtomicWrite(endpoint, ScopedNodeId(), kAtomicWriteState_Closed); -} - -/** - * @brief Finds an entry in the pending presets list that matches a preset. - * The presetHandle of the two presets must match. - * - * @param[in] delegate The delegate to use. - * @param[in] presetToMatch The preset to match with. - * - * @return true if a matching entry was found in the pending presets list, false otherwise. - */ -bool MatchingPendingPresetExists(Delegate * delegate, const PresetStructWithOwnedMembers & presetToMatch) -{ - VerifyOrReturnValue(delegate != nullptr, false); - - for (uint8_t i = 0; true; i++) - { - PresetStructWithOwnedMembers preset; - CHIP_ERROR err = delegate->GetPendingPresetAtIndex(i, preset); - - if (err == CHIP_ERROR_PROVIDER_LIST_EXHAUSTED) - { - break; - } - if (err != CHIP_NO_ERROR) - { - ChipLogError(Zcl, "MatchingPendingPresetExists: GetPendingPresetAtIndex failed with error %" CHIP_ERROR_FORMAT, - err.Format()); - return false; - } - - if (PresetHandlesExistAndMatch(preset, presetToMatch)) - { - return true; - } - } - return false; -} - -/** - * @brief Finds and returns an entry in the Presets attribute list that matches - * a preset, if such an entry exists. The presetToMatch must have a preset handle. - * - * @param[in] delegate The delegate to use. - * @param[in] presetToMatch The preset to match with. - * @param[out] matchingPreset The preset in the Presets attribute list that has the same PresetHandle as the presetToMatch. - * - * @return true if a matching entry was found in the presets attribute list, false otherwise. - */ -bool GetMatchingPresetInPresets(Delegate * delegate, const PresetStruct::Type & presetToMatch, - PresetStructWithOwnedMembers & matchingPreset) -{ - VerifyOrReturnValue(delegate != nullptr, false); - - for (uint8_t i = 0; true; i++) - { - CHIP_ERROR err = delegate->GetPresetAtIndex(i, matchingPreset); - - if (err == CHIP_ERROR_PROVIDER_LIST_EXHAUSTED) - { - break; - } - if (err != CHIP_NO_ERROR) - { - ChipLogError(Zcl, "GetMatchingPresetInPresets: GetPresetAtIndex failed with error %" CHIP_ERROR_FORMAT, err.Format()); - return false; - } - - // Note: presets coming from our delegate always have a handle. - if (presetToMatch.presetHandle.Value().data_equal(matchingPreset.GetPresetHandle().Value())) - { - return true; - } - } - return false; -} - -/** - * @brief Checks if the given preset handle is present in the presets attribute - * @param[in] delegate The delegate to use. - * @param[in] presetHandleToMatch The preset handle to match with. - * - * @return true if the given preset handle is present in the presets attribute list, false otherwise. - */ -bool IsPresetHandlePresentInPresets(Delegate * delegate, const ByteSpan & presetHandleToMatch) -{ - VerifyOrReturnValue(delegate != nullptr, false); - - PresetStructWithOwnedMembers matchingPreset; - for (uint8_t i = 0; true; i++) - { - CHIP_ERROR err = delegate->GetPresetAtIndex(i, matchingPreset); - - if (err == CHIP_ERROR_PROVIDER_LIST_EXHAUSTED) - { - return false; - } - - if (err != CHIP_NO_ERROR) - { - ChipLogError(Zcl, "IsPresetHandlePresentInPresets: GetPresetAtIndex failed with error %" CHIP_ERROR_FORMAT, - err.Format()); - return false; - } - - if (!matchingPreset.GetPresetHandle().IsNull() && matchingPreset.GetPresetHandle().Value().data_equal(presetHandleToMatch)) - { - return true; - } - } - return false; -} - -/** - * @brief Returns the length of the list of presets if the pending presets were to be applied. The size of the pending presets list - * calculated, after all the constraint checks are done, is the new size of the updated Presets attribute since the pending - * preset list is expected to have all existing presets with or without edits plus new presets. - * This is called before changes are actually applied. - * - * @param[in] delegate The delegate to use. - * - * @return count of the updated Presets attribute if the pending presets were applied to it. Return 0 for error cases. - */ -uint8_t CountNumberOfPendingPresets(Delegate * delegate) -{ - uint8_t numberOfPendingPresets = 0; - - VerifyOrReturnValue(delegate != nullptr, 0); - - for (uint8_t i = 0; true; i++) - { - PresetStructWithOwnedMembers pendingPreset; - CHIP_ERROR err = delegate->GetPendingPresetAtIndex(i, pendingPreset); - - if (err == CHIP_ERROR_PROVIDER_LIST_EXHAUSTED) - { - break; - } - if (err != CHIP_NO_ERROR) - { - ChipLogError(Zcl, "CountNumberOfPendingPresets: GetPendingPresetAtIndex failed with error %" CHIP_ERROR_FORMAT, - err.Format()); - return 0; - } - numberOfPendingPresets++; - } - - return numberOfPendingPresets; -} - -/** - * @brief Checks if the presetScenario is present in the PresetTypes attribute. - * - * @param[in] delegate The delegate to use. - * @param[in] presetScenario The presetScenario to match with. - * - * @return true if the presetScenario is found, false otherwise. - */ -bool PresetScenarioExistsInPresetTypes(Delegate * delegate, PresetScenarioEnum presetScenario) -{ - VerifyOrReturnValue(delegate != nullptr, false); - - for (uint8_t i = 0; true; i++) - { - PresetTypeStruct::Type presetType; - auto err = delegate->GetPresetTypeAtIndex(i, presetType); - if (err != CHIP_NO_ERROR) - { - return false; - } - - if (presetType.presetScenario == presetScenario) - { - return true; - } - } - return false; -} - -/** - * @brief Returns the count of preset entries in the pending presets list that have the matching presetHandle. - * @param[in] delegate The delegate to use. - * @param[in] presetHandleToMatch The preset handle to match. - * - * @return count of the number of presets found with the matching presetHandle. Returns 0 if no matching presets were found. - */ -uint8_t CountPresetsInPendingListWithPresetHandle(Delegate * delegate, const ByteSpan & presetHandleToMatch) -{ - uint8_t count = 0; - VerifyOrReturnValue(delegate != nullptr, count); - - for (uint8_t i = 0; true; i++) - { - PresetStructWithOwnedMembers preset; - auto err = delegate->GetPendingPresetAtIndex(i, preset); - if (err != CHIP_NO_ERROR) - { - return count; - } - - DataModel::Nullable presetHandle = preset.GetPresetHandle(); - if (!presetHandle.IsNull() && presetHandle.Value().data_equal(presetHandleToMatch)) - { - count++; - } - } - return count; -} - -/** - * @brief Checks if the presetType for the given preset scenario supports name in the presetTypeFeatures bitmap. - * - * @param[in] delegate The delegate to use. - * @param[in] presetScenario The presetScenario to match with. - * - * @return true if the presetType for the given preset scenario supports name, false otherwise. - */ -bool PresetTypeSupportsNames(Delegate * delegate, PresetScenarioEnum scenario) -{ - VerifyOrReturnValue(delegate != nullptr, false); - - for (uint8_t i = 0; true; i++) - { - PresetTypeStruct::Type presetType; - auto err = delegate->GetPresetTypeAtIndex(i, presetType); - if (err != CHIP_NO_ERROR) - { - return false; - } +namespace chip { +namespace app { +namespace Clusters { +namespace Thermostat { - if (presetType.presetScenario == scenario) - { - return (presetType.presetTypeFeatures.Has(PresetTypeFeaturesBitmap::kSupportsNames)); - } - } - return false; -} +ThermostatAttrAccess gThermostatAttrAccess; int16_t EnforceHeatingSetpointLimits(int16_t HeatingSetpoint, EndpointId endpoint) { @@ -461,7 +94,7 @@ int16_t EnforceHeatingSetpointLimits(int16_t HeatingSetpoint, EndpointId endpoin // Note that the limits are initialized above per the spec limits // if they are not present Get() will not update the value so the defaults are used - imcode status; + Status status; // https://github.com/CHIP-Specifications/connectedhomeip-spec/issues/3724 // behavior is not specified when Abs * values are not present and user values are present @@ -471,24 +104,24 @@ int16_t EnforceHeatingSetpointLimits(int16_t HeatingSetpoint, EndpointId endpoin // if a attribute is not present then it's default shall be used. status = AbsMinHeatSetpointLimit::Get(endpoint, &AbsMinHeatSetpointLimit); - if (status != imcode::Success) + if (status != Status::Success) { ChipLogError(Zcl, "Warning: AbsMinHeatSetpointLimit missing using default"); } status = AbsMaxHeatSetpointLimit::Get(endpoint, &AbsMaxHeatSetpointLimit); - if (status != imcode::Success) + if (status != Status::Success) { ChipLogError(Zcl, "Warning: AbsMaxHeatSetpointLimit missing using default"); } status = MinHeatSetpointLimit::Get(endpoint, &MinHeatSetpointLimit); - if (status != imcode::Success) + if (status != Status::Success) { MinHeatSetpointLimit = AbsMinHeatSetpointLimit; } status = MaxHeatSetpointLimit::Get(endpoint, &MaxHeatSetpointLimit); - if (status != imcode::Success) + if (status != Status::Success) { MaxHeatSetpointLimit = AbsMaxHeatSetpointLimit; } @@ -532,7 +165,7 @@ int16_t EnforceCoolingSetpointLimits(int16_t CoolingSetpoint, EndpointId endpoin // Note that the limits are initialized above per the spec limits // if they are not present Get() will not update the value so the defaults are used - imcode status; + Status status; // https://github.com/CHIP-Specifications/connectedhomeip-spec/issues/3724 // behavior is not specified when Abs * values are not present and user values are present @@ -542,25 +175,25 @@ int16_t EnforceCoolingSetpointLimits(int16_t CoolingSetpoint, EndpointId endpoin // if a attribute is not present then it's default shall be used. status = AbsMinCoolSetpointLimit::Get(endpoint, &AbsMinCoolSetpointLimit); - if (status != imcode::Success) + if (status != Status::Success) { ChipLogError(Zcl, "Warning: AbsMinCoolSetpointLimit missing using default"); } status = AbsMaxCoolSetpointLimit::Get(endpoint, &AbsMaxCoolSetpointLimit); - if (status != imcode::Success) + if (status != Status::Success) { ChipLogError(Zcl, "Warning: AbsMaxCoolSetpointLimit missing using default"); } status = MinCoolSetpointLimit::Get(endpoint, &MinCoolSetpointLimit); - if (status != imcode::Success) + if (status != Status::Success) { MinCoolSetpointLimit = AbsMinCoolSetpointLimit; } status = MaxCoolSetpointLimit::Get(endpoint, &MaxCoolSetpointLimit); - if (status != imcode::Success) + if (status != Status::Success) { MaxCoolSetpointLimit = AbsMaxCoolSetpointLimit; } @@ -587,81 +220,22 @@ int16_t EnforceCoolingSetpointLimits(int16_t CoolingSetpoint, EndpointId endpoin return CoolingSetpoint; } -} // anonymous namespace - -namespace chip { -namespace app { -namespace Clusters { -namespace Thermostat { - -void SetDefaultDelegate(EndpointId endpoint, Delegate * delegate) -{ - uint16_t ep = - emberAfGetClusterServerEndpointIndex(endpoint, Thermostat::Id, MATTER_DM_THERMOSTAT_CLUSTER_SERVER_ENDPOINT_COUNT); - // if endpoint is found, add the delegate in the delegate table - if (ep < ArraySize(gDelegateTable)) - { - gDelegateTable[ep] = delegate; - } -} - -void ThermostatAttrAccess::SetAtomicWrite(EndpointId endpoint, ScopedNodeId originatorNodeId, AtomicWriteState state) -{ - uint16_t ep = - emberAfGetClusterServerEndpointIndex(endpoint, Thermostat::Id, MATTER_DM_THERMOSTAT_CLUSTER_SERVER_ENDPOINT_COUNT); - - if (ep < ArraySize(mAtomicWriteSessions)) - { - mAtomicWriteSessions[ep].state = state; - mAtomicWriteSessions[ep].endpointId = endpoint; - mAtomicWriteSessions[ep].nodeId = originatorNodeId; - } -} - -bool ThermostatAttrAccess::InAtomicWrite(EndpointId endpoint) +Delegate * GetDelegate(EndpointId endpoint) { - bool inAtomicWrite = false; uint16_t ep = emberAfGetClusterServerEndpointIndex(endpoint, Thermostat::Id, MATTER_DM_THERMOSTAT_CLUSTER_SERVER_ENDPOINT_COUNT); - - if (ep < ArraySize(mAtomicWriteSessions)) - { - inAtomicWrite = (mAtomicWriteSessions[ep].state == kAtomicWriteState_Open); - } - return inAtomicWrite; -} - -bool ThermostatAttrAccess::InAtomicWrite(const Access::SubjectDescriptor & subjectDescriptor, EndpointId endpoint) -{ - if (!InAtomicWrite(endpoint)) - { - return false; - } - return subjectDescriptor.authMode == Access::AuthMode::kCase && - GetAtomicWriteScopedNodeId(endpoint) == ScopedNodeId(subjectDescriptor.subject, subjectDescriptor.fabricIndex); -} - -bool ThermostatAttrAccess::InAtomicWrite(CommandHandler * commandObj, EndpointId endpoint) -{ - if (!InAtomicWrite(endpoint)) - { - return false; - } - ScopedNodeId sourceNodeId = GetSourceScopedNodeId(commandObj); - return GetAtomicWriteScopedNodeId(endpoint) == sourceNodeId; + return (ep >= ArraySize(gDelegateTable) ? nullptr : gDelegateTable[ep]); } -ScopedNodeId ThermostatAttrAccess::GetAtomicWriteScopedNodeId(EndpointId endpoint) +void SetDefaultDelegate(EndpointId endpoint, Delegate * delegate) { - ScopedNodeId originatorNodeId = ScopedNodeId(); uint16_t ep = emberAfGetClusterServerEndpointIndex(endpoint, Thermostat::Id, MATTER_DM_THERMOSTAT_CLUSTER_SERVER_ENDPOINT_COUNT); - - if (ep < ArraySize(mAtomicWriteSessions)) + // if endpoint is found, add the delegate in the delegate table + if (ep < ArraySize(gDelegateTable)) { - originatorNodeId = mAtomicWriteSessions[ep].nodeId; + gDelegateTable[ep] = delegate; } - return originatorNodeId; } CHIP_ERROR ThermostatAttrAccess::Read(const ConcreteReadAttributePath & aPath, AttributeValueEncoder & aEncoder) @@ -669,7 +243,7 @@ CHIP_ERROR ThermostatAttrAccess::Read(const ConcreteReadAttributePath & aPath, A VerifyOrDie(aPath.mClusterId == Thermostat::Id); uint32_t ourFeatureMap; - bool localTemperatureNotExposedSupported = (FeatureMap::Get(aPath.mEndpointId, &ourFeatureMap) == imcode::Success) && + bool localTemperatureNotExposedSupported = (FeatureMap::Get(aPath.mEndpointId, &ourFeatureMap) == Status::Success) && ((ourFeatureMap & to_underlying(Feature::kLocalTemperatureNotExposed)) != 0); switch (aPath.mAttributeId) @@ -684,8 +258,8 @@ CHIP_ERROR ThermostatAttrAccess::Read(const ConcreteReadAttributePath & aPath, A if (localTemperatureNotExposedSupported) { BitMask valueRemoteSensing; - imcode status = RemoteSensing::Get(aPath.mEndpointId, &valueRemoteSensing); - if (status != imcode::Success) + Status status = RemoteSensing::Get(aPath.mEndpointId, &valueRemoteSensing); + if (status != Status::Success) { StatusIB statusIB(status); return statusIB.ToChipError(); @@ -725,7 +299,7 @@ CHIP_ERROR ThermostatAttrAccess::Read(const ConcreteReadAttributePath & aPath, A VerifyOrReturnError(delegate != nullptr, CHIP_ERROR_INCORRECT_STATE, ChipLogError(Zcl, "Delegate is null")); auto & subjectDescriptor = aEncoder.GetSubjectDescriptor(); - if (InAtomicWrite(subjectDescriptor, aPath.mEndpointId)) + if (InAtomicWrite(aPath.mEndpointId, subjectDescriptor, MakeOptional(aPath.mAttributeId))) { return aEncoder.EncodeList([delegate](const auto & encoder) -> CHIP_ERROR { for (uint8_t i = 0; true; i++) @@ -801,12 +375,12 @@ CHIP_ERROR ThermostatAttrAccess::Write(const ConcreteDataAttributePath & aPath, VerifyOrReturnError(delegate != nullptr, CHIP_ERROR_INCORRECT_STATE, ChipLogError(Zcl, "Delegate is null")); // Presets are not editable, return INVALID_IN_STATE. - VerifyOrReturnError(InAtomicWrite(endpoint), CHIP_IM_GLOBAL_STATUS(InvalidInState), + VerifyOrReturnError(InAtomicWrite(endpoint, MakeOptional(aPath.mAttributeId)), CHIP_IM_GLOBAL_STATUS(InvalidInState), ChipLogError(Zcl, "Presets are not editable")); // OK, we're in an atomic write, make sure the requesting node is the same one that started the atomic write, // otherwise return BUSY. - if (!InAtomicWrite(subjectDescriptor, endpoint)) + if (!InAtomicWrite(endpoint, subjectDescriptor, MakeOptional(aPath.mAttributeId))) { ChipLogError(Zcl, "Another node is editing presets. Server is busy. Try again later"); return CHIP_IM_GLOBAL_STATUS(Busy); @@ -848,14 +422,14 @@ CHIP_ERROR ThermostatAttrAccess::Write(const ConcreteDataAttributePath & aPath, } // This is not an atomic attribute, so check to make sure we don't have an atomic write going for this client - if (InAtomicWrite(subjectDescriptor, endpoint)) + if (InAtomicWrite(endpoint, subjectDescriptor)) { ChipLogError(Zcl, "Can not write to non-atomic attributes during atomic write"); return CHIP_IM_GLOBAL_STATUS(InvalidInState); } uint32_t ourFeatureMap; - bool localTemperatureNotExposedSupported = (FeatureMap::Get(aPath.mEndpointId, &ourFeatureMap) == imcode::Success) && + bool localTemperatureNotExposedSupported = (FeatureMap::Get(aPath.mEndpointId, &ourFeatureMap) == Status::Success) && ((ourFeatureMap & to_underlying(Feature::kLocalTemperatureNotExposed)) != 0); switch (aPath.mAttributeId) @@ -869,7 +443,7 @@ CHIP_ERROR ThermostatAttrAccess::Write(const ConcreteDataAttributePath & aPath, { return CHIP_IM_GLOBAL_STATUS(ConstraintError); } - imcode status = RemoteSensing::Set(aPath.mEndpointId, valueRemoteSensing); + Status status = RemoteSensing::Set(aPath.mEndpointId, valueRemoteSensing); StatusIB statusIB(status); return statusIB.ToChipError(); } @@ -882,73 +456,14 @@ CHIP_ERROR ThermostatAttrAccess::Write(const ConcreteDataAttributePath & aPath, return CHIP_NO_ERROR; } -CHIP_ERROR ThermostatAttrAccess::AppendPendingPreset(Thermostat::Delegate * delegate, const PresetStruct::Type & preset) -{ - if (!IsValidPresetEntry(preset)) - { - return CHIP_IM_GLOBAL_STATUS(ConstraintError); - } - - if (preset.presetHandle.IsNull()) - { - if (IsBuiltIn(preset)) - { - return CHIP_IM_GLOBAL_STATUS(ConstraintError); - } - } - else - { - auto & presetHandle = preset.presetHandle.Value(); - - // Per spec we need to check that: - // (a) There is an existing non-pending preset with this handle. - PresetStructWithOwnedMembers matchingPreset; - if (!GetMatchingPresetInPresets(delegate, preset, matchingPreset)) - { - return CHIP_IM_GLOBAL_STATUS(NotFound); - } - - // (b) There is no existing pending preset with this handle. - if (CountPresetsInPendingListWithPresetHandle(delegate, presetHandle) > 0) - { - return CHIP_IM_GLOBAL_STATUS(ConstraintError); - } - - // (c)/(d) The built-in fields do not have a mismatch. - // TODO: What's the story with nullability on the BuiltIn field? - if (!preset.builtIn.IsNull() && !matchingPreset.GetBuiltIn().IsNull() && - preset.builtIn.Value() != matchingPreset.GetBuiltIn().Value()) - { - return CHIP_IM_GLOBAL_STATUS(ConstraintError); - } - } - - if (!PresetScenarioExistsInPresetTypes(delegate, preset.presetScenario)) - { - return CHIP_IM_GLOBAL_STATUS(ConstraintError); - } - - if (preset.name.HasValue() && !PresetTypeSupportsNames(delegate, preset.presetScenario)) - { - return CHIP_IM_GLOBAL_STATUS(ConstraintError); - } - - return delegate->AppendToPendingPresetList(preset); -} - void ThermostatAttrAccess::OnFabricRemoved(const FabricTable & fabricTable, FabricIndex fabricIndex) { for (size_t i = 0; i < ArraySize(mAtomicWriteSessions); ++i) { - auto atomicWriteState = mAtomicWriteSessions[i]; - if (atomicWriteState.state == kAtomicWriteState_Open && atomicWriteState.nodeId.GetFabricIndex() == fabricIndex) + auto & atomicWriteState = mAtomicWriteSessions[i]; + if (atomicWriteState.state == AtomicWriteState::Open && atomicWriteState.nodeId.GetFabricIndex() == fabricIndex) { - auto delegate = GetDelegate(atomicWriteState.endpointId); - if (delegate == nullptr) - { - continue; - } - resetAtomicWrite(delegate, atomicWriteState.endpointId); + ResetAtomicWrite(atomicWriteState.endpointId); } } } @@ -1003,7 +518,7 @@ MatterThermostatClusterServerPreAttributeChangedCallback(const app::ConcreteAttr bool CoolSupported = false; bool OccupancySupported = false; - if (FeatureMap::Get(endpoint, &OurFeatureMap) != imcode::Success) + if (FeatureMap::Get(endpoint, &OurFeatureMap) != Status::Success) OurFeatureMap = FEATURE_MAP_DEFAULT; if (OurFeatureMap & 1 << 5) // Bit 5 is Auto Mode supported @@ -1020,63 +535,63 @@ MatterThermostatClusterServerPreAttributeChangedCallback(const app::ConcreteAttr if (AutoSupported) { - if (MinSetpointDeadBand::Get(endpoint, &DeadBand) != imcode::Success) + if (MinSetpointDeadBand::Get(endpoint, &DeadBand) != Status::Success) { DeadBand = kDefaultDeadBand; } DeadBandTemp = static_cast(DeadBand * 10); } - if (AbsMinCoolSetpointLimit::Get(endpoint, &AbsMinCoolSetpointLimit) != imcode::Success) + if (AbsMinCoolSetpointLimit::Get(endpoint, &AbsMinCoolSetpointLimit) != Status::Success) AbsMinCoolSetpointLimit = kDefaultAbsMinCoolSetpointLimit; - if (AbsMaxCoolSetpointLimit::Get(endpoint, &AbsMaxCoolSetpointLimit) != imcode::Success) + if (AbsMaxCoolSetpointLimit::Get(endpoint, &AbsMaxCoolSetpointLimit) != Status::Success) AbsMaxCoolSetpointLimit = kDefaultAbsMaxCoolSetpointLimit; - if (MinCoolSetpointLimit::Get(endpoint, &MinCoolSetpointLimit) != imcode::Success) + if (MinCoolSetpointLimit::Get(endpoint, &MinCoolSetpointLimit) != Status::Success) MinCoolSetpointLimit = AbsMinCoolSetpointLimit; - if (MaxCoolSetpointLimit::Get(endpoint, &MaxCoolSetpointLimit) != imcode::Success) + if (MaxCoolSetpointLimit::Get(endpoint, &MaxCoolSetpointLimit) != Status::Success) MaxCoolSetpointLimit = AbsMaxCoolSetpointLimit; - if (AbsMinHeatSetpointLimit::Get(endpoint, &AbsMinHeatSetpointLimit) != imcode::Success) + if (AbsMinHeatSetpointLimit::Get(endpoint, &AbsMinHeatSetpointLimit) != Status::Success) AbsMinHeatSetpointLimit = kDefaultAbsMinHeatSetpointLimit; - if (AbsMaxHeatSetpointLimit::Get(endpoint, &AbsMaxHeatSetpointLimit) != imcode::Success) + if (AbsMaxHeatSetpointLimit::Get(endpoint, &AbsMaxHeatSetpointLimit) != Status::Success) AbsMaxHeatSetpointLimit = kDefaultAbsMaxHeatSetpointLimit; - if (MinHeatSetpointLimit::Get(endpoint, &MinHeatSetpointLimit) != imcode::Success) + if (MinHeatSetpointLimit::Get(endpoint, &MinHeatSetpointLimit) != Status::Success) MinHeatSetpointLimit = AbsMinHeatSetpointLimit; - if (MaxHeatSetpointLimit::Get(endpoint, &MaxHeatSetpointLimit) != imcode::Success) + if (MaxHeatSetpointLimit::Get(endpoint, &MaxHeatSetpointLimit) != Status::Success) MaxHeatSetpointLimit = AbsMaxHeatSetpointLimit; if (CoolSupported) - if (OccupiedCoolingSetpoint::Get(endpoint, &OccupiedCoolingSetpoint) != imcode::Success) + if (OccupiedCoolingSetpoint::Get(endpoint, &OccupiedCoolingSetpoint) != Status::Success) { ChipLogError(Zcl, "Error: Can not read Occupied Cooling Setpoint"); - return imcode::Failure; + return Status::Failure; } if (HeatSupported) - if (OccupiedHeatingSetpoint::Get(endpoint, &OccupiedHeatingSetpoint) != imcode::Success) + if (OccupiedHeatingSetpoint::Get(endpoint, &OccupiedHeatingSetpoint) != Status::Success) { ChipLogError(Zcl, "Error: Can not read Occupied Heating Setpoint"); - return imcode::Failure; + return Status::Failure; } if (CoolSupported && OccupancySupported) - if (UnoccupiedCoolingSetpoint::Get(endpoint, &UnoccupiedCoolingSetpoint) != imcode::Success) + if (UnoccupiedCoolingSetpoint::Get(endpoint, &UnoccupiedCoolingSetpoint) != Status::Success) { ChipLogError(Zcl, "Error: Can not read Unoccupied Cooling Setpoint"); - return imcode::Failure; + return Status::Failure; } if (HeatSupported && OccupancySupported) - if (UnoccupiedHeatingSetpoint::Get(endpoint, &UnoccupiedHeatingSetpoint) != imcode::Success) + if (UnoccupiedHeatingSetpoint::Get(endpoint, &UnoccupiedHeatingSetpoint) != Status::Success) { ChipLogError(Zcl, "Error: Can not read Unoccupied Heating Setpoint"); - return imcode::Failure; + return Status::Failure; } switch (attributePath.mAttributeId) @@ -1084,143 +599,143 @@ MatterThermostatClusterServerPreAttributeChangedCallback(const app::ConcreteAttr case OccupiedHeatingSetpoint::Id: { requested = static_cast(chip::Encoding::LittleEndian::Get16(value)); if (!HeatSupported) - return imcode::UnsupportedAttribute; + return Status::UnsupportedAttribute; if (requested < AbsMinHeatSetpointLimit || requested < MinHeatSetpointLimit || requested > AbsMaxHeatSetpointLimit || requested > MaxHeatSetpointLimit) - return imcode::InvalidValue; + return Status::InvalidValue; if (AutoSupported) { if (requested > OccupiedCoolingSetpoint - DeadBandTemp) - return imcode::InvalidValue; + return Status::InvalidValue; } - return imcode::Success; + return Status::Success; } case OccupiedCoolingSetpoint::Id: { requested = static_cast(chip::Encoding::LittleEndian::Get16(value)); if (!CoolSupported) - return imcode::UnsupportedAttribute; + return Status::UnsupportedAttribute; if (requested < AbsMinCoolSetpointLimit || requested < MinCoolSetpointLimit || requested > AbsMaxCoolSetpointLimit || requested > MaxCoolSetpointLimit) - return imcode::InvalidValue; + return Status::InvalidValue; if (AutoSupported) { if (requested < OccupiedHeatingSetpoint + DeadBandTemp) - return imcode::InvalidValue; + return Status::InvalidValue; } - return imcode::Success; + return Status::Success; } case UnoccupiedHeatingSetpoint::Id: { requested = static_cast(chip::Encoding::LittleEndian::Get16(value)); if (!(HeatSupported && OccupancySupported)) - return imcode::UnsupportedAttribute; + return Status::UnsupportedAttribute; if (requested < AbsMinHeatSetpointLimit || requested < MinHeatSetpointLimit || requested > AbsMaxHeatSetpointLimit || requested > MaxHeatSetpointLimit) - return imcode::InvalidValue; + return Status::InvalidValue; if (AutoSupported) { if (requested > UnoccupiedCoolingSetpoint - DeadBandTemp) - return imcode::InvalidValue; + return Status::InvalidValue; } - return imcode::Success; + return Status::Success; } case UnoccupiedCoolingSetpoint::Id: { requested = static_cast(chip::Encoding::LittleEndian::Get16(value)); if (!(CoolSupported && OccupancySupported)) - return imcode::UnsupportedAttribute; + return Status::UnsupportedAttribute; if (requested < AbsMinCoolSetpointLimit || requested < MinCoolSetpointLimit || requested > AbsMaxCoolSetpointLimit || requested > MaxCoolSetpointLimit) - return imcode::InvalidValue; + return Status::InvalidValue; if (AutoSupported) { if (requested < UnoccupiedHeatingSetpoint + DeadBandTemp) - return imcode::InvalidValue; + return Status::InvalidValue; } - return imcode::Success; + return Status::Success; } case MinHeatSetpointLimit::Id: { requested = static_cast(chip::Encoding::LittleEndian::Get16(value)); if (!HeatSupported) - return imcode::UnsupportedAttribute; + return Status::UnsupportedAttribute; if (requested < AbsMinHeatSetpointLimit || requested > MaxHeatSetpointLimit || requested > AbsMaxHeatSetpointLimit) - return imcode::InvalidValue; + return Status::InvalidValue; if (AutoSupported) { if (requested > MinCoolSetpointLimit - DeadBandTemp) - return imcode::InvalidValue; + return Status::InvalidValue; } - return imcode::Success; + return Status::Success; } case MaxHeatSetpointLimit::Id: { requested = static_cast(chip::Encoding::LittleEndian::Get16(value)); if (!HeatSupported) - return imcode::UnsupportedAttribute; + return Status::UnsupportedAttribute; if (requested < AbsMinHeatSetpointLimit || requested < MinHeatSetpointLimit || requested > AbsMaxHeatSetpointLimit) - return imcode::InvalidValue; + return Status::InvalidValue; if (AutoSupported) { if (requested > MaxCoolSetpointLimit - DeadBandTemp) - return imcode::InvalidValue; + return Status::InvalidValue; } - return imcode::Success; + return Status::Success; } case MinCoolSetpointLimit::Id: { requested = static_cast(chip::Encoding::LittleEndian::Get16(value)); if (!CoolSupported) - return imcode::UnsupportedAttribute; + return Status::UnsupportedAttribute; if (requested < AbsMinCoolSetpointLimit || requested > MaxCoolSetpointLimit || requested > AbsMaxCoolSetpointLimit) - return imcode::InvalidValue; + return Status::InvalidValue; if (AutoSupported) { if (requested < MinHeatSetpointLimit + DeadBandTemp) - return imcode::InvalidValue; + return Status::InvalidValue; } - return imcode::Success; + return Status::Success; } case MaxCoolSetpointLimit::Id: { requested = static_cast(chip::Encoding::LittleEndian::Get16(value)); if (!CoolSupported) - return imcode::UnsupportedAttribute; + return Status::UnsupportedAttribute; if (requested < AbsMinCoolSetpointLimit || requested < MinCoolSetpointLimit || requested > AbsMaxCoolSetpointLimit) - return imcode::InvalidValue; + return Status::InvalidValue; if (AutoSupported) { if (requested < MaxHeatSetpointLimit + DeadBandTemp) - return imcode::InvalidValue; + return Status::InvalidValue; } - return imcode::Success; + return Status::Success; } case MinSetpointDeadBand::Id: { requested = *value; if (!AutoSupported) - return imcode::UnsupportedAttribute; + return Status::UnsupportedAttribute; if (requested < 0 || requested > 25) - return imcode::InvalidValue; - return imcode::Success; + return Status::InvalidValue; + return Status::Success; } case ControlSequenceOfOperation::Id: { uint8_t requestedCSO; requestedCSO = *value; if (requestedCSO > to_underlying(ControlSequenceOfOperationEnum::kCoolingAndHeatingWithReheat)) - return imcode::InvalidValue; - return imcode::Success; + return Status::InvalidValue; + return Status::Success; } case SystemMode::Id: { ControlSequenceOfOperationEnum ControlSequenceOfOperation; - imcode status = ControlSequenceOfOperation::Get(endpoint, &ControlSequenceOfOperation); - if (status != imcode::Success) + Status status = ControlSequenceOfOperation::Get(endpoint, &ControlSequenceOfOperation); + if (status != Status::Success) { - return imcode::InvalidValue; + return Status::InvalidValue; } auto RequestedSystemMode = static_cast(*value); if (ControlSequenceOfOperation > ControlSequenceOfOperationEnum::kCoolingAndHeatingWithReheat || RequestedSystemMode > SystemModeEnum::kFanOnly) { - return imcode::InvalidValue; + return Status::InvalidValue; } switch (ControlSequenceOfOperation) @@ -1228,22 +743,22 @@ MatterThermostatClusterServerPreAttributeChangedCallback(const app::ConcreteAttr case ControlSequenceOfOperationEnum::kCoolingOnly: case ControlSequenceOfOperationEnum::kCoolingWithReheat: if (RequestedSystemMode == SystemModeEnum::kHeat || RequestedSystemMode == SystemModeEnum::kEmergencyHeat) - return imcode::InvalidValue; + return Status::InvalidValue; else - return imcode::Success; + return Status::Success; case ControlSequenceOfOperationEnum::kHeatingOnly: case ControlSequenceOfOperationEnum::kHeatingWithReheat: if (RequestedSystemMode == SystemModeEnum::kCool || RequestedSystemMode == SystemModeEnum::kPrecooling) - return imcode::InvalidValue; + return Status::InvalidValue; else - return imcode::Success; + return Status::Success; default: - return imcode::Success; + return Status::Success; } } default: - return imcode::Success; + return Status::Success; } } @@ -1279,363 +794,6 @@ bool emberAfThermostatClusterSetActiveScheduleRequestCallback( return false; } -bool emberAfThermostatClusterSetActivePresetRequestCallback( - CommandHandler * commandObj, const ConcreteCommandPath & commandPath, - const Clusters::Thermostat::Commands::SetActivePresetRequest::DecodableType & commandData) -{ - EndpointId endpoint = commandPath.mEndpointId; - Delegate * delegate = GetDelegate(endpoint); - - if (delegate == nullptr) - { - ChipLogError(Zcl, "Delegate is null"); - commandObj->AddStatus(commandPath, imcode::InvalidInState); - return true; - } - - DataModel::Nullable newPresetHandle = commandData.presetHandle; - - // If the preset handle passed in the command is not present in the Presets attribute, return INVALID_COMMAND. - if (!newPresetHandle.IsNull() && !IsPresetHandlePresentInPresets(delegate, newPresetHandle.Value())) - { - commandObj->AddStatus(commandPath, imcode::InvalidCommand); - return true; - } - - CHIP_ERROR err = delegate->SetActivePresetHandle(newPresetHandle); - - if (err != CHIP_NO_ERROR) - { - ChipLogError(Zcl, "Failed to set ActivePresetHandle with error %" CHIP_ERROR_FORMAT, err.Format()); - commandObj->AddStatus(commandPath, StatusIB(err).mStatus); - return true; - } - - commandObj->AddStatus(commandPath, imcode::Success); - return true; -} - -bool validAtomicAttributes(const Commands::AtomicRequest::DecodableType & commandData, bool requireBoth) -{ - auto attributeIdsIter = commandData.attributeRequests.begin(); - bool requestedPresets = false, requestedSchedules = false; - while (attributeIdsIter.Next()) - { - auto & attributeId = attributeIdsIter.GetValue(); - - switch (attributeId) - { - case Presets::Id: - if (requestedPresets) // Double-requesting an attribute is invalid - { - return false; - } - requestedPresets = true; - break; - case Schedules::Id: - if (requestedSchedules) // Double-requesting an attribute is invalid - { - return false; - } - requestedSchedules = true; - break; - default: - return false; - } - } - if (attributeIdsIter.GetStatus() != CHIP_NO_ERROR) - { - return false; - } - if (requireBoth) - { - return (requestedPresets && requestedSchedules); - } - // If the atomic request doesn't contain at least one of these attributes, it's invalid - return (requestedPresets || requestedSchedules); -} - -void sendAtomicResponse(CommandHandler * commandObj, const ConcreteCommandPath & commandPath, imcode status, imcode presetsStatus, - imcode schedulesStatus, Optional timeout = NullOptional) -{ - Commands::AtomicResponse::Type response; - Globals::Structs::AtomicAttributeStatusStruct::Type attributeStatus[] = { - { .attributeID = Presets::Id, .statusCode = to_underlying(presetsStatus) }, - { .attributeID = Schedules::Id, .statusCode = to_underlying(schedulesStatus) } - }; - response.statusCode = to_underlying(status); - response.attributeStatus = attributeStatus; - response.timeout = timeout; - commandObj->AddResponse(commandPath, response); -} - -void handleAtomicBegin(CommandHandler * commandObj, const ConcreteCommandPath & commandPath, - const Commands::AtomicRequest::DecodableType & commandData) -{ - EndpointId endpoint = commandPath.mEndpointId; - - Delegate * delegate = GetDelegate(endpoint); - - if (delegate == nullptr) - { - ChipLogError(Zcl, "Delegate is null"); - commandObj->AddStatus(commandPath, imcode::InvalidInState); - return; - } - - if (gThermostatAttrAccess.InAtomicWrite(commandObj, endpoint)) - { - // This client already has an open atomic write - commandObj->AddStatus(commandPath, imcode::InvalidInState); - return; - } - - if (!commandData.timeout.HasValue()) - { - commandObj->AddStatus(commandPath, imcode::InvalidCommand); - return; - } - - if (!validAtomicAttributes(commandData, false)) - { - commandObj->AddStatus(commandPath, imcode::InvalidCommand); - return; - } - - if (gThermostatAttrAccess.InAtomicWrite(endpoint)) - { - sendAtomicResponse(commandObj, commandPath, imcode::Failure, imcode::Busy, imcode::Busy); - return; - } - - // This is a valid request to open an atomic write. Tell the delegate it - // needs to keep track of a pending preset list now. - delegate->InitializePendingPresets(); - - auto timeout = - delegate->GetAtomicWriteTimeout(commandData.attributeRequests, System::Clock::Milliseconds16(commandData.timeout.Value())); - - if (!timeout.has_value()) - { - commandObj->AddStatus(commandPath, imcode::InvalidCommand); - return; - } - ScheduleTimer(endpoint, timeout.value()); - gThermostatAttrAccess.SetAtomicWrite(endpoint, GetSourceScopedNodeId(commandObj), kAtomicWriteState_Open); - sendAtomicResponse(commandObj, commandPath, imcode::Success, imcode::Success, imcode::Success, - MakeOptional(timeout.value().count())); -} - -imcode commitPresets(Delegate * delegate, EndpointId endpoint) -{ - CHIP_ERROR err = CHIP_NO_ERROR; - - // For each preset in the presets attribute, check that the matching preset in the pending presets list does not - // violate any spec constraints. - for (uint8_t i = 0; true; i++) - { - PresetStructWithOwnedMembers preset; - err = delegate->GetPresetAtIndex(i, preset); - - if (err == CHIP_ERROR_PROVIDER_LIST_EXHAUSTED) - { - break; - } - if (err != CHIP_NO_ERROR) - { - ChipLogError(Zcl, - "emberAfThermostatClusterCommitPresetsSchedulesRequestCallback: GetPresetAtIndex failed with error " - "%" CHIP_ERROR_FORMAT, - err.Format()); - return imcode::InvalidInState; - } - - bool found = MatchingPendingPresetExists(delegate, preset); - - // If a built in preset in the Presets attribute list is removed and not found in the pending presets list, return - // CONSTRAINT_ERROR. - if (IsBuiltIn(preset) && !found) - { - return imcode::ConstraintError; - } - } - - // If there is an ActivePresetHandle set, find the preset in the pending presets list that matches the ActivePresetHandle - // attribute. If a preset is not found with the same presetHandle, return INVALID_IN_STATE. If there is no ActivePresetHandle - // attribute set, continue with other checks. - uint8_t buffer[kPresetHandleSize]; - MutableByteSpan activePresetHandleSpan(buffer); - auto activePresetHandle = DataModel::MakeNullable(activePresetHandleSpan); - - err = delegate->GetActivePresetHandle(activePresetHandle); - - if (err != CHIP_NO_ERROR) - { - return imcode::InvalidInState; - } - - if (!activePresetHandle.IsNull()) - { - uint8_t count = CountPresetsInPendingListWithPresetHandle(delegate, activePresetHandle.Value()); - if (count == 0) - { - return imcode::InvalidInState; - } - } - - // For each preset in the pending presets list, check that the preset does not violate any spec constraints. - for (uint8_t i = 0; true; i++) - { - PresetStructWithOwnedMembers pendingPreset; - err = delegate->GetPendingPresetAtIndex(i, pendingPreset); - - if (err == CHIP_ERROR_PROVIDER_LIST_EXHAUSTED) - { - break; - } - if (err != CHIP_NO_ERROR) - { - ChipLogError(Zcl, - "emberAfThermostatClusterCommitPresetsSchedulesRequestCallback: GetPendingPresetAtIndex failed with error " - "%" CHIP_ERROR_FORMAT, - err.Format()); - return imcode::InvalidInState; - } - - // Enforce the Setpoint Limits for both the cooling and heating setpoints in the pending preset. - // TODO: This code does not work, because it's modifying our temporary copy. - Optional coolingSetpointValue = pendingPreset.GetCoolingSetpoint(); - if (coolingSetpointValue.HasValue()) - { - pendingPreset.SetCoolingSetpoint(MakeOptional(EnforceCoolingSetpointLimits(coolingSetpointValue.Value(), endpoint))); - } - - Optional heatingSetpointValue = pendingPreset.GetHeatingSetpoint(); - if (heatingSetpointValue.HasValue()) - { - pendingPreset.SetHeatingSetpoint(MakeOptional(EnforceHeatingSetpointLimits(heatingSetpointValue.Value(), endpoint))); - } - } - - uint8_t totalCount = CountNumberOfPendingPresets(delegate); - - uint8_t numberOfPresetsSupported = delegate->GetNumberOfPresets(); - - if (numberOfPresetsSupported == 0) - { - ChipLogError(Zcl, "emberAfThermostatClusterCommitPresetsSchedulesRequestCallback: Failed to get NumberOfPresets"); - return imcode::InvalidInState; - } - - // If the expected length of the presets attribute with the applied changes exceeds the total number of presets supported, - // return RESOURCE_EXHAUSTED. Note that the changes are not yet applied. - if (numberOfPresetsSupported > 0 && totalCount > numberOfPresetsSupported) - { - return imcode::ResourceExhausted; - } - - // TODO: Check if the number of presets for each presetScenario exceeds the max number of presets supported for that - // scenario. We plan to support only one preset for each presetScenario for our use cases so defer this for re-evaluation. - - // Call the delegate API to apply the pending presets to the presets attribute and update it. - err = delegate->ApplyPendingPresets(); - - if (err != CHIP_NO_ERROR) - { - return imcode::InvalidInState; - } - - return imcode::Success; -} - -void handleAtomicCommit(CommandHandler * commandObj, const ConcreteCommandPath & commandPath, - const Commands::AtomicRequest::DecodableType & commandData) -{ - if (!validAtomicAttributes(commandData, true)) - { - commandObj->AddStatus(commandPath, imcode::InvalidCommand); - return; - } - EndpointId endpoint = commandPath.mEndpointId; - bool inAtomicWrite = gThermostatAttrAccess.InAtomicWrite(commandObj, endpoint); - if (!inAtomicWrite) - { - commandObj->AddStatus(commandPath, imcode::InvalidInState); - return; - } - - Delegate * delegate = GetDelegate(endpoint); - - if (delegate == nullptr) - { - ChipLogError(Zcl, "Delegate is null"); - commandObj->AddStatus(commandPath, imcode::InvalidInState); - return; - } - - auto presetsStatus = commitPresets(delegate, endpoint); - // TODO: copy over schedules code - auto schedulesStatus = imcode::Success; - resetAtomicWrite(delegate, endpoint); - imcode status = (presetsStatus == imcode::Success && schedulesStatus == imcode::Success) ? imcode::Success : imcode::Failure; - sendAtomicResponse(commandObj, commandPath, status, presetsStatus, schedulesStatus); -} - -void handleAtomicRollback(CommandHandler * commandObj, const ConcreteCommandPath & commandPath, - const Commands::AtomicRequest::DecodableType & commandData) -{ - if (!validAtomicAttributes(commandData, true)) - { - commandObj->AddStatus(commandPath, imcode::InvalidCommand); - return; - } - EndpointId endpoint = commandPath.mEndpointId; - bool inAtomicWrite = gThermostatAttrAccess.InAtomicWrite(commandObj, endpoint); - if (!inAtomicWrite) - { - commandObj->AddStatus(commandPath, imcode::InvalidInState); - return; - } - - Delegate * delegate = GetDelegate(endpoint); - - if (delegate == nullptr) - { - ChipLogError(Zcl, "Delegate is null"); - commandObj->AddStatus(commandPath, imcode::InvalidInState); - return; - } - resetAtomicWrite(delegate, endpoint); - sendAtomicResponse(commandObj, commandPath, imcode::Success, imcode::Success, imcode::Success); -} - -bool emberAfThermostatClusterAtomicRequestCallback(CommandHandler * commandObj, const ConcreteCommandPath & commandPath, - const Clusters::Thermostat::Commands::AtomicRequest::DecodableType & commandData) -{ - auto & requestType = commandData.requestType; - - // If we've gotten this far, then the client has manage permission to call AtomicRequest, which is also the - // privilege necessary to write to the atomic attributes, so no need to check - - switch (requestType) - { - case Globals::AtomicRequestTypeEnum::kBeginWrite: - handleAtomicBegin(commandObj, commandPath, commandData); - return true; - case Globals::AtomicRequestTypeEnum::kCommitWrite: - handleAtomicCommit(commandObj, commandPath, commandData); - return true; - case Globals::AtomicRequestTypeEnum::kRollbackWrite: - handleAtomicRollback(commandObj, commandPath, commandData); - return true; - case Globals::AtomicRequestTypeEnum::kUnknownEnumValue: - commandObj->AddStatus(commandPath, imcode::InvalidCommand); - return true; - } - - return false; -} - bool emberAfThermostatClusterSetpointRaiseLowerCallback(app::CommandHandler * commandObj, const app::ConcreteCommandPath & commandPath, const Commands::SetpointRaiseLower::DecodableType & commandData) @@ -1646,9 +804,9 @@ bool emberAfThermostatClusterSetpointRaiseLowerCallback(app::CommandHandler * co EndpointId aEndpointId = commandPath.mEndpointId; int16_t HeatingSetpoint = kDefaultHeatingSetpoint, CoolingSetpoint = kDefaultCoolingSetpoint; // Set to defaults to be safe - imcode status = imcode::Failure; - imcode WriteCoolingSetpointStatus = imcode::Failure; - imcode WriteHeatingSetpointStatus = imcode::Failure; + Status status = Status::Failure; + Status WriteCoolingSetpointStatus = Status::Failure; + Status WriteHeatingSetpointStatus = Status::Failure; int16_t DeadBandTemp = 0; int8_t DeadBand = 0; uint32_t OurFeatureMap; @@ -1656,7 +814,7 @@ bool emberAfThermostatClusterSetpointRaiseLowerCallback(app::CommandHandler * co bool HeatSupported = false; bool CoolSupported = false; - if (FeatureMap::Get(aEndpointId, &OurFeatureMap) != imcode::Success) + if (FeatureMap::Get(aEndpointId, &OurFeatureMap) != Status::Success) OurFeatureMap = FEATURE_MAP_DEFAULT; if (OurFeatureMap & 1 << 5) // Bit 5 is Auto Mode supported @@ -1670,7 +828,7 @@ bool emberAfThermostatClusterSetpointRaiseLowerCallback(app::CommandHandler * co if (AutoSupported) { - if (MinSetpointDeadBand::Get(aEndpointId, &DeadBand) != imcode::Success) + if (MinSetpointDeadBand::Get(aEndpointId, &DeadBand) != Status::Success) DeadBand = kDefaultDeadBand; DeadBandTemp = static_cast(DeadBand * 10); } @@ -1681,13 +839,13 @@ bool emberAfThermostatClusterSetpointRaiseLowerCallback(app::CommandHandler * co if (HeatSupported && CoolSupported) { int16_t DesiredCoolingSetpoint, CoolLimit, DesiredHeatingSetpoint, HeatLimit; - if (OccupiedCoolingSetpoint::Get(aEndpointId, &CoolingSetpoint) == imcode::Success) + if (OccupiedCoolingSetpoint::Get(aEndpointId, &CoolingSetpoint) == Status::Success) { DesiredCoolingSetpoint = static_cast(CoolingSetpoint + amount * 10); CoolLimit = static_cast(DesiredCoolingSetpoint - EnforceCoolingSetpointLimits(DesiredCoolingSetpoint, aEndpointId)); { - if (OccupiedHeatingSetpoint::Get(aEndpointId, &HeatingSetpoint) == imcode::Success) + if (OccupiedHeatingSetpoint::Get(aEndpointId, &HeatingSetpoint) == Status::Success) { DesiredHeatingSetpoint = static_cast(HeatingSetpoint + amount * 10); HeatLimit = static_cast(DesiredHeatingSetpoint - @@ -1709,12 +867,12 @@ bool emberAfThermostatClusterSetpointRaiseLowerCallback(app::CommandHandler * co } } WriteCoolingSetpointStatus = OccupiedCoolingSetpoint::Set(aEndpointId, DesiredCoolingSetpoint); - if (WriteCoolingSetpointStatus != imcode::Success) + if (WriteCoolingSetpointStatus != Status::Success) { ChipLogError(Zcl, "Error: SetOccupiedCoolingSetpoint failed!"); } WriteHeatingSetpointStatus = OccupiedHeatingSetpoint::Set(aEndpointId, DesiredHeatingSetpoint); - if (WriteHeatingSetpointStatus != imcode::Success) + if (WriteHeatingSetpointStatus != Status::Success) { ChipLogError(Zcl, "Error: SetOccupiedHeatingSetpoint failed!"); } @@ -1726,12 +884,12 @@ bool emberAfThermostatClusterSetpointRaiseLowerCallback(app::CommandHandler * co if (CoolSupported && !HeatSupported) { - if (OccupiedCoolingSetpoint::Get(aEndpointId, &CoolingSetpoint) == imcode::Success) + if (OccupiedCoolingSetpoint::Get(aEndpointId, &CoolingSetpoint) == Status::Success) { CoolingSetpoint = static_cast(CoolingSetpoint + amount * 10); CoolingSetpoint = EnforceCoolingSetpointLimits(CoolingSetpoint, aEndpointId); WriteCoolingSetpointStatus = OccupiedCoolingSetpoint::Set(aEndpointId, CoolingSetpoint); - if (WriteCoolingSetpointStatus != imcode::Success) + if (WriteCoolingSetpointStatus != Status::Success) { ChipLogError(Zcl, "Error: SetOccupiedCoolingSetpoint failed!"); } @@ -1740,34 +898,34 @@ bool emberAfThermostatClusterSetpointRaiseLowerCallback(app::CommandHandler * co if (HeatSupported && !CoolSupported) { - if (OccupiedHeatingSetpoint::Get(aEndpointId, &HeatingSetpoint) == imcode::Success) + if (OccupiedHeatingSetpoint::Get(aEndpointId, &HeatingSetpoint) == Status::Success) { HeatingSetpoint = static_cast(HeatingSetpoint + amount * 10); HeatingSetpoint = EnforceHeatingSetpointLimits(HeatingSetpoint, aEndpointId); WriteHeatingSetpointStatus = OccupiedHeatingSetpoint::Set(aEndpointId, HeatingSetpoint); - if (WriteHeatingSetpointStatus != imcode::Success) + if (WriteHeatingSetpointStatus != Status::Success) { ChipLogError(Zcl, "Error: SetOccupiedHeatingSetpoint failed!"); } } } - if ((!HeatSupported || WriteHeatingSetpointStatus == imcode::Success) && - (!CoolSupported || WriteCoolingSetpointStatus == imcode::Success)) - status = imcode::Success; + if ((!HeatSupported || WriteHeatingSetpointStatus == Status::Success) && + (!CoolSupported || WriteCoolingSetpointStatus == Status::Success)) + status = Status::Success; break; case SetpointRaiseLowerModeEnum::kCool: if (CoolSupported) { - if (OccupiedCoolingSetpoint::Get(aEndpointId, &CoolingSetpoint) == imcode::Success) + if (OccupiedCoolingSetpoint::Get(aEndpointId, &CoolingSetpoint) == Status::Success) { CoolingSetpoint = static_cast(CoolingSetpoint + amount * 10); CoolingSetpoint = EnforceCoolingSetpointLimits(CoolingSetpoint, aEndpointId); if (AutoSupported) { // Need to check if we can move the cooling setpoint while maintaining the dead band - if (OccupiedHeatingSetpoint::Get(aEndpointId, &HeatingSetpoint) == imcode::Success) + if (OccupiedHeatingSetpoint::Get(aEndpointId, &HeatingSetpoint) == Status::Success) { if (CoolingSetpoint - HeatingSetpoint < DeadBandTemp) { @@ -1778,10 +936,10 @@ bool emberAfThermostatClusterSetpointRaiseLowerCallback(app::CommandHandler * co { // Desired cooling setpoint is enforcable // Set the new cooling and heating setpoints - if (OccupiedHeatingSetpoint::Set(aEndpointId, HeatingSetpoint) == imcode::Success) + if (OccupiedHeatingSetpoint::Set(aEndpointId, HeatingSetpoint) == Status::Success) { - if (OccupiedCoolingSetpoint::Set(aEndpointId, CoolingSetpoint) == imcode::Success) - status = imcode::Success; + if (OccupiedCoolingSetpoint::Set(aEndpointId, CoolingSetpoint) == Status::Success) + status = Status::Success; } else ChipLogError(Zcl, "Error: SetOccupiedHeatingSetpoint failed!"); @@ -1789,7 +947,7 @@ bool emberAfThermostatClusterSetpointRaiseLowerCallback(app::CommandHandler * co else { ChipLogError(Zcl, "Error: Could Not adjust heating setpoint to maintain dead band!"); - status = imcode::InvalidCommand; + status = Status::InvalidCommand; } } else @@ -1807,20 +965,20 @@ bool emberAfThermostatClusterSetpointRaiseLowerCallback(app::CommandHandler * co ChipLogError(Zcl, "Error: GetOccupiedCoolingSetpoint failed!"); } else - status = imcode::InvalidCommand; + status = Status::InvalidCommand; break; case SetpointRaiseLowerModeEnum::kHeat: if (HeatSupported) { - if (OccupiedHeatingSetpoint::Get(aEndpointId, &HeatingSetpoint) == imcode::Success) + if (OccupiedHeatingSetpoint::Get(aEndpointId, &HeatingSetpoint) == Status::Success) { HeatingSetpoint = static_cast(HeatingSetpoint + amount * 10); HeatingSetpoint = EnforceHeatingSetpointLimits(HeatingSetpoint, aEndpointId); if (AutoSupported) { // Need to check if we can move the cooling setpoint while maintaining the dead band - if (OccupiedCoolingSetpoint::Get(aEndpointId, &CoolingSetpoint) == imcode::Success) + if (OccupiedCoolingSetpoint::Get(aEndpointId, &CoolingSetpoint) == Status::Success) { if (CoolingSetpoint - HeatingSetpoint < DeadBandTemp) { @@ -1831,10 +989,10 @@ bool emberAfThermostatClusterSetpointRaiseLowerCallback(app::CommandHandler * co { // Desired cooling setpoint is enforcable // Set the new cooling and heating setpoints - if (OccupiedCoolingSetpoint::Set(aEndpointId, CoolingSetpoint) == imcode::Success) + if (OccupiedCoolingSetpoint::Set(aEndpointId, CoolingSetpoint) == Status::Success) { - if (OccupiedHeatingSetpoint::Set(aEndpointId, HeatingSetpoint) == imcode::Success) - status = imcode::Success; + if (OccupiedHeatingSetpoint::Set(aEndpointId, HeatingSetpoint) == Status::Success) + status = Status::Success; } else ChipLogError(Zcl, "Error: SetOccupiedCoolingSetpoint failed!"); @@ -1842,7 +1000,7 @@ bool emberAfThermostatClusterSetpointRaiseLowerCallback(app::CommandHandler * co else { ChipLogError(Zcl, "Error: Could Not adjust cooling setpoint to maintain dead band!"); - status = imcode::InvalidCommand; + status = Status::InvalidCommand; } } else @@ -1860,11 +1018,11 @@ bool emberAfThermostatClusterSetpointRaiseLowerCallback(app::CommandHandler * co ChipLogError(Zcl, "Error: GetOccupiedHeatingSetpoint failed!"); } else - status = imcode::InvalidCommand; + status = Status::InvalidCommand; break; default: - status = imcode::InvalidCommand; + status = Status::InvalidCommand; break; } @@ -1877,14 +1035,3 @@ void MatterThermostatPluginServerInitCallback() Server::GetInstance().GetFabricTable().AddFabricDelegate(&gThermostatAttrAccess); AttributeAccessInterfaceRegistry::Instance().Register(&gThermostatAttrAccess); } - -void MatterThermostatClusterServerShutdownCallback(EndpointId endpoint) -{ - ChipLogProgress(Zcl, "Shutting down thermostat server cluster on endpoint %d", endpoint); - Delegate * delegate = GetDelegate(endpoint); - - if (delegate != nullptr) - { - resetAtomicWrite(delegate, endpoint); - } -} diff --git a/src/app/clusters/thermostat-server/thermostat-server.h b/src/app/clusters/thermostat-server/thermostat-server.h index ddede8a9bb13f9..cc941cfa766d92 100644 --- a/src/app/clusters/thermostat-server/thermostat-server.h +++ b/src/app/clusters/thermostat-server/thermostat-server.h @@ -26,6 +26,7 @@ #include "thermostat-delegate.h" +#include #include #include @@ -34,25 +35,62 @@ namespace app { namespace Clusters { namespace Thermostat { +enum class AtomicWriteState +{ + Closed = 0, + Open, +}; + static constexpr size_t kThermostatEndpointCount = MATTER_DM_THERMOSTAT_CLUSTER_SERVER_ENDPOINT_COUNT + CHIP_DEVICE_CONFIG_DYNAMIC_ENDPOINT_COUNT; -enum AtomicWriteState -{ - kAtomicWriteState_Closed = 0, - kAtomicWriteState_Open, -}; /** * @brief Thermostat Attribute Access Interface. */ class ThermostatAttrAccess : public chip::app::AttributeAccessInterface, public chip::FabricTable::Delegate { + public: ThermostatAttrAccess() : AttributeAccessInterface(Optional::Missing(), Thermostat::Id) {} CHIP_ERROR Read(const ConcreteReadAttributePath & aPath, AttributeValueEncoder & aEncoder) override; CHIP_ERROR Write(const ConcreteDataAttributePath & aPath, chip::app::AttributeValueDecoder & aDecoder) override; +private: + /** + * @brief Set the Active Preset to a given preset handle, or null + * + * @param endpoint The endpoint + * @param presetHandle The handle of the preset to set active, or null to clear the active preset + * @return Success if the active preset was updated, an error code if not + */ + Protocols::InteractionModel::Status SetActivePreset(EndpointId endpoint, DataModel::Nullable presetHandle); + + /** + * @brief Apply a preset to the pending lists of presets during an atomic write + * + * @param delegate The current ThermostatDelegate + * @param preset The preset to append + * @return CHIP_NO_ERROR if successful, an error code if not + */ + CHIP_ERROR AppendPendingPreset(Thermostat::Delegate * delegate, const Structs::PresetStruct::Type & preset); + + /** + * @brief Verifies if the pending presets for a given endpoint are valid + * + * @param endpoint The endpoint + * @return Success if the list of pending presets is valid, an error code if not + */ + Protocols::InteractionModel::Status PrecommitPresets(EndpointId endpoint); + + /** + * @brief Callback for when the server is removed from a given fabric; all associated atomic writes are reset + * + * @param fabricTable The fabric table + * @param fabricIndex The fabric index + */ + void OnFabricRemoved(const FabricTable & fabricTable, FabricIndex fabricIndex) override; + /** * @brief Gets the scoped node id of the originator that sent the last successful * AtomicRequest of type BeginWrite for the given endpoint. @@ -61,7 +99,7 @@ class ThermostatAttrAccess : public chip::app::AttributeAccessInterface, public * * @return the scoped node id for the given endpoint if set. Otherwise returns ScopedNodeId(). */ - ScopedNodeId GetAtomicWriteScopedNodeId(EndpointId endpoint); + ScopedNodeId GetAtomicWriteOriginatorScopedNodeId(EndpointId endpoint); /** * @brief Sets the atomic write state for the given endpoint and originatorNodeId @@ -69,46 +107,119 @@ class ThermostatAttrAccess : public chip::app::AttributeAccessInterface, public * @param[in] endpoint The endpoint. * @param[in] originatorNodeId The originator scoped node id. * @param[in] state Whether or not an atomic write is open or closed. + * @param attributeStatuses The set of attribute status structs the atomic write should be associated with + * @return true if it was able to update the atomic write state + * @return false if it was unable to update the atomic write state */ - void SetAtomicWrite(EndpointId endpoint, ScopedNodeId originatorNodeId, AtomicWriteState state); + bool + SetAtomicWrite(EndpointId endpoint, ScopedNodeId originatorNodeId, AtomicWriteState state, + Platform::ScopedMemoryBufferWithSize & attributeStatuses); /** - * @brief Gets whether an atomic write is in progress for the given endpoint + * @brief Sets the atomic write state for the given endpoint and originatorNodeId * - * @param[in] endpoint The endpoint. + */ + /** + * @brief Resets the atomic write for a given endpoint * - * @return Whether an atomic write is in progress for the given endpoint + * @param endpoint The endpoint */ - bool InAtomicWrite(EndpointId endpoint); + void ResetAtomicWrite(EndpointId endpoint); /** - * @brief Gets whether an atomic write is in progress for the given endpoint + * @brief Checks if a given endpoint has an atomic write open, optionally filtered by an attribute ID * - * @param[in] subjectDescriptor The subject descriptor. - * @param[in] endpoint The endpoint. + * @param endpoint The endpoint + * @param attributeId The optional attribute ID to filter on + * @return true if the endpoint has an open atomic write + * @return false if the endpoint does not have an open atomic write + */ + bool InAtomicWrite(EndpointId endpoint, Optional attributeId = NullOptional); + + /** + * @brief Checks if a given endpoint has an atomic write open for a given subject descriptor, optionally filtered by an + * attribute ID * - * @return Whether an atomic write is in progress for the given endpoint + * @param endpoint The endpoint + * @param subjectDescriptor The subject descriptor for the client making a read or write request + * @param attributeId The optional attribute ID to filter on + * @return true if the endpoint has an open atomic write + * @return false if the endpoint does not have an open atomic write */ - bool InAtomicWrite(const Access::SubjectDescriptor & subjectDescriptor, EndpointId endpoint); + bool InAtomicWrite(EndpointId endpoint, const Access::SubjectDescriptor & subjectDescriptor, + Optional attributeId = NullOptional); /** - * @brief Gets whether an atomic write is in progress for the given endpoint + * @brief Checks if a given endpoint has an atomic write open for a given command invocation, optionally filtered by an + * attribute ID * - * @param[in] commandObj The command handler. - * @param[in] endpoint The endpoint. + * @param endpoint The endpoint + * @param commandObj The CommandHandler for the invoked command + * @param attributeId The optional attribute ID to filter on + * @return true if the endpoint has an open atomic write + * @return false if the endpoint does not have an open atomic write + */ + bool InAtomicWrite(EndpointId endpoint, CommandHandler * commandObj, Optional attributeId = NullOptional); + + /** + * @brief Checks if a given endpoint has an atomic write open for a given command invocation and a list of attributes * - * @return Whether an atomic write is in progress for the given endpoint + * @param endpoint The endpoint + * @param commandObj The CommandHandler for the invoked command + * @param attributeStatuses The list of attribute statuses whose attributeIds must match the open atomic write + * @return true if the endpoint has an open atomic write + * @return false if the endpoint does not have an open atomic write */ - bool InAtomicWrite(CommandHandler * commandObj, EndpointId endpoint); + bool + InAtomicWrite(EndpointId endpoint, CommandHandler * commandObj, + Platform::ScopedMemoryBufferWithSize & attributeStatuses); -private: - CHIP_ERROR AppendPendingPreset(Thermostat::Delegate * delegate, const Structs::PresetStruct::Type & preset); + /** + * @brief Handles an AtomicRequest of type BeginWrite + * + * @param commandObj The AtomicRequest command handler + * @param commandPath The path for the Atomic Request command + * @param commandData The payload data for the Atomic Request + */ + void BeginAtomicWrite(CommandHandler * commandObj, const ConcreteCommandPath & commandPath, + const Commands::AtomicRequest::DecodableType & commandData); - void OnFabricRemoved(const FabricTable & fabricTable, FabricIndex fabricIndex) override; + /** + * @brief Handles an AtomicRequest of type CommitWrite + * + * @param commandObj The AtomicRequest command handler + * @param commandPath The path for the Atomic Request command + * @param commandData The payload data for the Atomic Request + */ + void CommitAtomicWrite(CommandHandler * commandObj, const ConcreteCommandPath & commandPath, + const Commands::AtomicRequest::DecodableType & commandData); + + /** + * @brief Handles an AtomicRequest of type RollbackWrite + * + * @param commandObj The AtomicRequest command handler + * @param commandPath The path for the Atomic Request command + * @param commandData The payload data for the Atomic Request + */ + void RollbackAtomicWrite(CommandHandler * commandObj, const ConcreteCommandPath & commandPath, + const Commands::AtomicRequest::DecodableType & commandData); + + friend void TimerExpiredCallback(System::Layer * systemLayer, void * callbackContext); + + friend void MatterThermostatClusterServerShutdownCallback(EndpointId endpoint); + + friend bool emberAfThermostatClusterSetActivePresetRequestCallback( + CommandHandler * commandObj, const ConcreteCommandPath & commandPath, + const Clusters::Thermostat::Commands::SetActivePresetRequest::DecodableType & commandData); + + friend bool + emberAfThermostatClusterAtomicRequestCallback(CommandHandler * commandObj, const ConcreteCommandPath & commandPath, + const Clusters::Thermostat::Commands::AtomicRequest::DecodableType & commandData); struct AtomicWriteSession { - AtomicWriteState state = kAtomicWriteState_Closed; + AtomicWriteState state = AtomicWriteState::Closed; + Platform::ScopedMemoryBufferWithSize attributeIds; ScopedNodeId nodeId; EndpointId endpointId = kInvalidEndpointId; }; @@ -124,6 +235,8 @@ class ThermostatAttrAccess : public chip::app::AttributeAccessInterface, public */ void SetDefaultDelegate(EndpointId endpoint, Delegate * delegate); +Delegate * GetDelegate(EndpointId endpoint); + } // namespace Thermostat } // namespace Clusters } // namespace app diff --git a/src/python_testing/TC_TSTAT_4_2.py b/src/python_testing/TC_TSTAT_4_2.py index 5c289ced50604e..563d6f3f2eddfc 100644 --- a/src/python_testing/TC_TSTAT_4_2.py +++ b/src/python_testing/TC_TSTAT_4_2.py @@ -61,15 +61,30 @@ class TC_TSTAT_4_2(MatterBaseTest): def check_atomic_response(self, response: object, expected_status: Status = Status.Success, expected_overall_status: Status = Status.Success, - expected_preset_status: Status = Status.Success): + expected_preset_status: Status = Status.Success, + expected_schedules_status: Status = None, + expected_timeout: int = None): asserts.assert_equal(expected_status, Status.Success, "We expected we had a valid response") asserts.assert_equal(response.statusCode, expected_overall_status, "Response should have the right overall status") found_preset_status = False + found_schedules_status = False for attrStatus in response.attributeStatus: if attrStatus.attributeID == cluster.Attributes.Presets.attribute_id: asserts.assert_equal(attrStatus.statusCode, expected_preset_status, "Preset attribute should have the right status") found_preset_status = True + if attrStatus.attributeID == cluster.Attributes.Schedules.attribute_id: + asserts.assert_equal(attrStatus.statusCode, expected_schedules_status, + "Schedules attribute should have the right status") + found_schedules_status = True + if expected_timeout is not None: + asserts.assert_equal(response.timeout, expected_timeout, + "Timeout should have the right value") + asserts.assert_true(found_preset_status, "Preset attribute should have a status") + if expected_schedules_status is not None: + asserts.assert_true(found_schedules_status, "Schedules attribute should have a status") + asserts.assert_equal(attrStatus.statusCode, expected_schedules_status, + "Schedules attribute should have the right status") asserts.assert_true(found_preset_status, "Preset attribute should have a status") async def write_presets(self, @@ -87,17 +102,21 @@ async def write_presets(self, async def send_atomic_request_begin_command(self, dev_ctrl: ChipDeviceCtrl = None, endpoint: int = None, + timeout: int = 1800, expected_status: Status = Status.Success, expected_overall_status: Status = Status.Success, - expected_preset_status: Status = Status.Success): + expected_preset_status: Status = Status.Success, + expected_schedules_status: Status = None, + expected_timeout: int = None): try: response = await self.send_single_cmd(cmd=cluster.Commands.AtomicRequest(requestType=Globals.Enums.AtomicRequestTypeEnum.kBeginWrite, attributeRequests=[ cluster.Attributes.Presets.attribute_id], - timeout=1800), + timeout=timeout), dev_ctrl=dev_ctrl, endpoint=endpoint) - self.check_atomic_response(response, expected_status, expected_overall_status, expected_preset_status) + self.check_atomic_response(response, expected_status, expected_overall_status, + expected_preset_status, expected_schedules_status, expected_timeout) except InteractionModelError as e: asserts.assert_equal(e.status, expected_status, "Unexpected error returned") @@ -107,13 +126,15 @@ async def send_atomic_request_commit_command(self, endpoint: int = None, expected_status: Status = Status.Success, expected_overall_status: Status = Status.Success, - expected_preset_status: Status = Status.Success): + expected_preset_status: Status = Status.Success, + expected_schedules_status: Status = None): try: response = await self.send_single_cmd(cmd=cluster.Commands.AtomicRequest(requestType=Globals.Enums.AtomicRequestTypeEnum.kCommitWrite, - attributeRequests=[cluster.Attributes.Presets.attribute_id, cluster.Attributes.Schedules.attribute_id]), + attributeRequests=[cluster.Attributes.Presets.attribute_id]), dev_ctrl=dev_ctrl, endpoint=endpoint) - self.check_atomic_response(response, expected_status, expected_overall_status, expected_preset_status) + self.check_atomic_response(response, expected_status, expected_overall_status, + expected_preset_status, expected_schedules_status) except InteractionModelError as e: asserts.assert_equal(e.status, expected_status, "Unexpected error returned") @@ -122,13 +143,16 @@ async def send_atomic_request_rollback_command(self, endpoint: int = None, expected_status: Status = Status.Success, expected_overall_status: Status = Status.Success, - expected_preset_status: Status = Status.Success): + expected_preset_status: Status = Status.Success, + expected_schedules_status: Status = None): try: response = await self.send_single_cmd(cmd=cluster.Commands.AtomicRequest(requestType=Globals.Enums.AtomicRequestTypeEnum.kRollbackWrite, - attributeRequests=[cluster.Attributes.Presets.attribute_id, cluster.Attributes.Schedules.attribute_id]), + attributeRequests=[cluster.Attributes.Presets.attribute_id]), dev_ctrl=dev_ctrl, endpoint=endpoint) - self.check_atomic_response(response, expected_status, expected_overall_status, expected_preset_status) + self.check_atomic_response(response, expected_status, expected_overall_status, + expected_preset_status, expected_schedules_status) + except InteractionModelError as e: asserts.assert_equal(e.status, expected_status, "Unexpected error returned") @@ -219,7 +243,6 @@ async def test_TC_TSTAT_4_2(self): await self.write_presets(endpoint=endpoint, presets=new_presets, expected_status=Status.InvalidInState) self.step("3") - if self.pics_guard(self.check_pics("TSTAT.S.F08") and self.check_pics("TSTAT.S.A0050") and self.check_pics("TSTAT.S.Cfe.Rsp")): await self.send_atomic_request_begin_command() @@ -260,7 +283,7 @@ async def test_TC_TSTAT_4_2(self): if self.pics_guard(self.check_pics("TSTAT.S.F08") and self.check_pics("TSTAT.S.A0050") and self.check_pics("TSTAT.S.Cfe.Rsp")): # Send the AtomicRequest begin command - await self.send_atomic_request_begin_command() + await self.send_atomic_request_begin_command(timeout=5000, expected_timeout=3000) # Write to the presets attribute after removing a built in preset from the list. Remove the first entry. test_presets = new_presets_with_handle.copy() @@ -406,10 +429,7 @@ async def test_TC_TSTAT_4_2(self): self.step("14") if self.pics_guard(self.check_pics("TSTAT.S.F08") and self.check_pics("TSTAT.S.A0050") and self.check_pics("TSTAT.S.Cfe.Rsp")): - - # Send the AtomicRequest begin command await self.send_atomic_request_begin_command() - # Send the AtomicRequest begin command from separate controller, which should receive busy status = await self.send_atomic_request_begin_command(dev_ctrl=secondary_controller, expected_overall_status=Status.Failure, expected_preset_status=Status.Busy) From fe34e8198d8b27058b7d446516f5737e190d7326 Mon Sep 17 00:00:00 2001 From: Wang Qixiang <43193572+wqx6@users.noreply.github.com> Date: Fri, 23 Aug 2024 23:17:10 +0800 Subject: [PATCH 11/32] esp32: fix compiling when enabling ble commissioner (#35085) * esp32: fix compiling when enabling ble commissioner * Restyled by clang-format --------- Co-authored-by: Restyled.io --- src/platform/ESP32/nimble/BLEManagerImpl.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/platform/ESP32/nimble/BLEManagerImpl.cpp b/src/platform/ESP32/nimble/BLEManagerImpl.cpp index 4f6ab4f73f7200..d4d90f45362dbe 100644 --- a/src/platform/ESP32/nimble/BLEManagerImpl.cpp +++ b/src/platform/ESP32/nimble/BLEManagerImpl.cpp @@ -659,7 +659,7 @@ CHIP_ERROR BLEManagerImpl::SendWriteRequest(BLE_CONNECTION_OBJECT conId, const C if (pBuf->DataLength() > UINT16_MAX) { ChipLogError(Ble, "Buffer data Length is too long"); - return false; + return CHIP_ERROR_INVALID_ARGUMENT; } rc = ble_gattc_write_flat(conId, chr->chr.val_handle, pBuf->Start(), static_cast(pBuf->DataLength()), OnWriteComplete, this); @@ -1746,12 +1746,12 @@ void BLEManagerImpl::DriveBLEState(intptr_t arg) #ifdef CONFIG_ENABLE_ESP32_BLE_CONTROLLER CHIP_ERROR BLEManagerImpl::HandleRXNotify(struct ble_gap_event * ble_event) { - size_t dataLen = OS_MBUF_PKTLEN(ble_event->notify_rx.om); + uint16_t dataLen = OS_MBUF_PKTLEN(ble_event->notify_rx.om); System::PacketBufferHandle buf = System::PacketBufferHandle::New(dataLen, 0); VerifyOrReturnError(!buf.IsNull(), CHIP_ERROR_NO_MEMORY); - VerifyOrExit(buf->AvailableDataLength() >= data_len, err = CHIP_ERROR_BUFFER_TOO_SMALL); - ble_hs_mbuf_to_flat(ble_event->notify_rx.om, buf->Start(), data_len, NULL); - buf->SetDataLength(data_len); + VerifyOrReturnError(buf->AvailableDataLength() >= dataLen, CHIP_ERROR_BUFFER_TOO_SMALL); + ble_hs_mbuf_to_flat(ble_event->notify_rx.om, buf->Start(), dataLen, NULL); + buf->SetDataLength(dataLen); ChipLogDetail(DeviceLayer, "Indication received, conn = %d", ble_event->notify_rx.conn_handle); From 9520bef5d88e4ec9f208390182dce8d1b3f8a664 Mon Sep 17 00:00:00 2001 From: Boris Zbarsky Date: Fri, 23 Aug 2024 12:06:22 -0400 Subject: [PATCH 12/32] Remove writeAttributeWithEndpointID implementation from MTRDevice. (#35170) This is implemented (differently) by the different subclasses. Once this implementation is removed, removeExpectedValueForAttributePath becomes unused and can be removed. Also removes the unused setExpectedValues declaration in MTRDevice_Internal.h and the implementations of it. --- src/darwin/Framework/CHIP/MTRDevice.mm | 136 ++---------------- .../Framework/CHIP/MTRDevice_Concrete.mm | 5 - .../Framework/CHIP/MTRDevice_Internal.h | 4 - 3 files changed, 9 insertions(+), 136 deletions(-) diff --git a/src/darwin/Framework/CHIP/MTRDevice.mm b/src/darwin/Framework/CHIP/MTRDevice.mm index 3f2acce5f5587e..f913f29388f5fb 100644 --- a/src/darwin/Framework/CHIP/MTRDevice.mm +++ b/src/darwin/Framework/CHIP/MTRDevice.mm @@ -1831,11 +1831,12 @@ static BOOL AttributeHasChangesOmittedQuality(MTRAttributePath * attributePath) attributeID:(NSNumber *)attributeID params:(MTRReadParams * _Nullable)params { -#define ErrorStr "MTRDevice readAttributeWithEndpointID:clusterID:attributeID:params: must be handled by subclasses" - MTR_LOG_ERROR(ErrorStr); +#define MTRDeviceErrorStr "MTRDevice readAttributeWithEndpointID:clusterID:attributeID:params: must be handled by subclasses" + MTR_LOG_ERROR(MTRDeviceErrorStr); #ifdef DEBUG - NSAssert(NO, @ErrorStr); + NSAssert(NO, @MTRDeviceErrorStr); #endif // DEBUG +#undef MTRDeviceErrorStr return nil; } @@ -1846,120 +1847,12 @@ - (void)writeAttributeWithEndpointID:(NSNumber *)endpointID expectedValueInterval:(NSNumber *)expectedValueInterval timedWriteTimeout:(NSNumber * _Nullable)timeout { - if (timeout) { - timeout = MTRClampedNumber(timeout, @(1), @(UINT16_MAX)); - } - expectedValueInterval = MTRClampedNumber(expectedValueInterval, @(1), @(UINT32_MAX)); - MTRAttributePath * attributePath = [MTRAttributePath attributePathWithEndpointID:endpointID - clusterID:clusterID - - attributeID:attributeID]; - - __block BOOL useValueAsExpectedValue = YES; +#define MTRDeviceErrorStr "MTRDevice writeAttributeWithEndpointID:clusterID:attributeID:value:expectedValueInterval:timedWriteTimeout: must be handled by subclasses" + MTR_LOG_ERROR(MTRDeviceErrorStr); #ifdef DEBUG - os_unfair_lock_lock(&self->_lock); - [self _callFirstDelegateSynchronouslyWithBlock:^(id delegate) { - if ([delegate respondsToSelector:@selector(unitTestShouldSkipExpectedValuesForWrite:)]) { - useValueAsExpectedValue = ![delegate unitTestShouldSkipExpectedValuesForWrite:self]; - } - }]; - os_unfair_lock_unlock(&self->_lock); -#endif - - uint64_t expectedValueID = 0; - if (useValueAsExpectedValue) { - // Commit change into expected value cache - NSDictionary * newExpectedValueDictionary = @{ MTRAttributePathKey : attributePath, MTRDataKey : value }; - [self setExpectedValues:@[ newExpectedValueDictionary ] - expectedValueInterval:expectedValueInterval - expectedValueID:&expectedValueID]; - } - - MTRAsyncWorkItem * workItem = [[MTRAsyncWorkItem alloc] initWithQueue:self.queue]; - uint64_t workItemID = workItem.uniqueID; // capture only the ID, not the work item - NSNumber * nodeID = _nodeID; - - // Write request data is an array of items (for now always length 1). Each - // item is an array containing: - // - // [ attribute path, value, timedWriteTimeout, expectedValueID ] - // - // where expectedValueID is stored as NSNumber and NSNull represents nil timeouts - auto * writeData = @[ attributePath, [value copy], timeout ?: [NSNull null], @(expectedValueID) ]; - - NSMutableArray * writeRequests = [NSMutableArray arrayWithObject:writeData]; - - [workItem setBatchingID:MTRDeviceWorkItemBatchingWriteID data:writeRequests handler:^(id opaqueDataCurrent, id opaqueDataNext) { - mtr_hide(self); // don't capture self accidentally - NSMutableArray * writeRequestsCurrent = opaqueDataCurrent; - NSMutableArray * writeRequestsNext = opaqueDataNext; - - if (writeRequestsCurrent.count != 1) { - // Very unexpected! - MTR_LOG_ERROR("Batching write attribute work item [%llu]: Unexpected write request count %lu", workItemID, static_cast(writeRequestsCurrent.count)); - return MTRNotBatched; - } - - MTRBatchingOutcome outcome = MTRNotBatched; - while (writeRequestsNext.count) { - // If paths don't match, we cannot replace the earlier write - // with the later one. - if (![writeRequestsNext[0][MTRDeviceWriteRequestFieldPathIndex] - isEqual:writeRequestsCurrent[0][MTRDeviceWriteRequestFieldPathIndex]]) { - MTR_LOG("Batching write attribute work item [%llu]: cannot replace with next work item due to path mismatch", workItemID); - return outcome; - } - - // Replace our one request with the first one from the next item. - auto writeItem = writeRequestsNext.firstObject; - [writeRequestsNext removeObjectAtIndex:0]; - [writeRequestsCurrent replaceObjectAtIndex:0 withObject:writeItem]; - MTR_LOG("Batching write attribute work item [%llu]: replaced with new write value %@ [0x%016llX]", - workItemID, writeItem, nodeID.unsignedLongLongValue); - outcome = MTRBatchedPartially; - } - NSCAssert(writeRequestsNext.count == 0, @"should have batched everything or returned early"); - return MTRBatchedFully; - }]; - // The write operation will install a duplicate check handler, to return NO for "isDuplicate". Since a write operation may - // change values, only read requests after this should be considered for duplicate requests. - [workItem setDuplicateTypeID:MTRDeviceWorkItemDuplicateReadTypeID handler:^(id opaqueItemData, BOOL * isDuplicate, BOOL * stop) { - *isDuplicate = NO; - *stop = YES; - }]; - [workItem setReadyHandler:^(MTRDevice * self, NSInteger retryCount, MTRAsyncWorkCompletionBlock completion) { - MTRBaseDevice * baseDevice = [self newBaseDevice]; - // Make sure to use writeRequests here, because that's what our batching - // handler will modify as needed. - NSCAssert(writeRequests.count == 1, @"Incorrect number of write requests: %lu", static_cast(writeRequests.count)); - - auto * request = writeRequests[0]; - MTRAttributePath * path = request[MTRDeviceWriteRequestFieldPathIndex]; - - id timedWriteTimeout = request[MTRDeviceWriteRequestFieldTimeoutIndex]; - if (timedWriteTimeout == [NSNull null]) { - timedWriteTimeout = nil; - } - - [baseDevice - writeAttributeWithEndpointID:path.endpoint - clusterID:path.cluster - attributeID:path.attribute - value:request[MTRDeviceWriteRequestFieldValueIndex] - timedWriteTimeout:timedWriteTimeout - queue:self.queue - completion:^(NSArray *> * _Nullable values, NSError * _Nullable error) { - if (error) { - MTR_LOG_ERROR("Write attribute work item [%llu] failed: %@", workItemID, error); - if (useValueAsExpectedValue) { - NSNumber * expectedValueID = request[MTRDeviceWriteRequestFieldExpectedValueIDIndex]; - [self removeExpectedValueForAttributePath:attributePath expectedValueID:expectedValueID.unsignedLongLongValue]; - } - } - completion(MTRAsyncWorkComplete); - }]; - }]; - [_asyncWorkQueue enqueueWorkItem:workItem descriptionWithFormat:@"write %@ 0x%llx 0x%llx", endpointID, clusterID.unsignedLongLongValue, attributeID.unsignedLongLongValue]; + NSAssert(NO, @MTRDeviceErrorStr); +#endif // DEBUG +#undef MTRDeviceErrorStr } - (void)invokeCommandWithEndpointID:(NSNumber *)endpointID @@ -2796,11 +2689,6 @@ - (NSArray *)_getAttributesToReportWithNewExpectedValues:(NSArray *> *)values expectedValueInterval:(NSNumber *)expectedValueInterval -{ - [self setExpectedValues:values expectedValueInterval:expectedValueInterval expectedValueID:nil]; -} - // expectedValueID is an out-argument that returns an identifier to be used when removing expected values - (void)setExpectedValues:(NSArray *> *)values expectedValueInterval:(NSNumber *)expectedValueInterval @@ -2833,12 +2721,6 @@ - (void)removeExpectedValuesForAttributePaths:(NSArray *)att } } -- (void)removeExpectedValueForAttributePath:(MTRAttributePath *)attributePath expectedValueID:(uint64_t)expectedValueID -{ - std::lock_guard lock(_lock); - [self _removeExpectedValueForAttributePath:attributePath expectedValueID:expectedValueID]; -} - - (void)_removeExpectedValueForAttributePath:(MTRAttributePath *)attributePath expectedValueID:(uint64_t)expectedValueID { os_unfair_lock_assert_owner(&self->_lock); diff --git a/src/darwin/Framework/CHIP/MTRDevice_Concrete.mm b/src/darwin/Framework/CHIP/MTRDevice_Concrete.mm index 12614ea4091b0c..b7e7bf78027b4f 100644 --- a/src/darwin/Framework/CHIP/MTRDevice_Concrete.mm +++ b/src/darwin/Framework/CHIP/MTRDevice_Concrete.mm @@ -3644,11 +3644,6 @@ - (NSArray *)_getAttributesToReportWithNewExpectedValues:(NSArray *> *)values expectedValueInterval:(NSNumber *)expectedValueInterval -{ - [self setExpectedValues:values expectedValueInterval:expectedValueInterval expectedValueID:nil]; -} - // expectedValueID is an out-argument that returns an identifier to be used when removing expected values - (void)setExpectedValues:(NSArray *> *)values expectedValueInterval:(NSNumber *)expectedValueInterval diff --git a/src/darwin/Framework/CHIP/MTRDevice_Internal.h b/src/darwin/Framework/CHIP/MTRDevice_Internal.h index b6a59ac9321d42..dae183eec217f6 100644 --- a/src/darwin/Framework/CHIP/MTRDevice_Internal.h +++ b/src/darwin/Framework/CHIP/MTRDevice_Internal.h @@ -125,10 +125,6 @@ MTR_DIRECT_MEMBERS - (instancetype)initForSubclassesWithNodeID:(NSNumber *)nodeID controller:(MTRDeviceController *)controller; - (instancetype)initWithNodeID:(NSNumber *)nodeID controller:(MTRDeviceController *)controller; -// Called from MTRClusters for writes and commands -- (void)setExpectedValues:(NSArray *> *)values - expectedValueInterval:(NSNumber *)expectedValueIntervalMs; - // called by controller to clean up and shutdown - (void)invalidate; From c1b3304fa69f356f21b8803c572c670c0ad1be17 Mon Sep 17 00:00:00 2001 From: Boris Zbarsky Date: Fri, 23 Aug 2024 13:16:06 -0400 Subject: [PATCH 13/32] Address followup issues for preset/atomic-write implementation. (#35175) * Puts some file-local functions in an anonymous namespace. * Fixes the "is this attribute supported?" check to correctly check for global attributes that are not in Ember metadata. * Moves the comment explaining why it's OK to skip the spec-required ACL check to the place where that check is being skipped. * Removes a non-spec-compliant "error if the timeout is 0" bit. Fixes https://github.com/project-chip/connectedhomeip/issues/35168 --- src/app/GlobalAttributes.h | 13 ++++++++ .../thermostat-server-atomic.cpp | 31 ++++++++++++------- 2 files changed, 33 insertions(+), 11 deletions(-) diff --git a/src/app/GlobalAttributes.h b/src/app/GlobalAttributes.h index 5096792309a880..461294267f38c0 100644 --- a/src/app/GlobalAttributes.h +++ b/src/app/GlobalAttributes.h @@ -40,5 +40,18 @@ constexpr AttributeId GlobalAttributesNotInMetadata[] = { static_assert(ArrayIsSorted(GlobalAttributesNotInMetadata), "Array of global attribute ids must be sorted"); +inline bool IsSupportedGlobalAttributeNotInMetadata(AttributeId attributeId) +{ + for (auto & attr : GlobalAttributesNotInMetadata) + { + if (attr == attributeId) + { + return true; + } + } + + return false; +} + } // namespace app } // namespace chip diff --git a/src/app/clusters/thermostat-server/thermostat-server-atomic.cpp b/src/app/clusters/thermostat-server/thermostat-server-atomic.cpp index 2a6e52e504887e..19a6ffa3387e12 100644 --- a/src/app/clusters/thermostat-server/thermostat-server-atomic.cpp +++ b/src/app/clusters/thermostat-server/thermostat-server-atomic.cpp @@ -17,6 +17,7 @@ #include "thermostat-server.h" +#include #include using namespace chip; @@ -47,6 +48,8 @@ void TimerExpiredCallback(System::Layer * systemLayer, void * callbackContext) gThermostatAttrAccess.ResetAtomicWrite(endpoint); } +namespace { + /** * @brief Schedules a timer for the given timeout in milliseconds. * @@ -185,15 +188,25 @@ Status BuildAttributeStatuses(const EndpointId endpoint, const DataModel::Decoda const EmberAfAttributeMetadata * metadata = emberAfLocateAttributeMetadata(endpoint, Thermostat::Id, attributeStatus.attributeID); - if (metadata == nullptr) + if (metadata != nullptr) + { + // This is definitely an attribute we know about. + continue; + } + + if (IsSupportedGlobalAttributeNotInMetadata(attributeStatus.attributeID)) { - // This is not a valid attribute on the Thermostat cluster on the supplied endpoint - return Status::InvalidCommand; + continue; } + + // This is not a valid attribute on the Thermostat cluster on the supplied endpoint + return Status::InvalidCommand; } return Status::Success; } +} // anonymous namespace + bool ThermostatAttrAccess::InAtomicWrite(EndpointId endpoint, Optional attributeId) { @@ -422,6 +435,10 @@ void ThermostatAttrAccess::BeginAtomicWrite(CommandHandler * commandObj, const C status = Status::Success; for (size_t i = 0; i < attributeStatuses.AllocatedSize(); ++i) { + // If we've gotten this far, then the client has manage permission to call AtomicRequest, + // which is also the privilege necessary to write to the atomic attributes, so no need to do + // the "If the client does not have sufficient privilege to write to the attribute" check + // from the spec. auto & attributeStatus = attributeStatuses[i]; auto statusCode = Status::Success; switch (attributeStatus.attributeID) @@ -442,11 +459,6 @@ void ThermostatAttrAccess::BeginAtomicWrite(CommandHandler * commandObj, const C } auto timeout = std::min(System::Clock::Milliseconds16(commandData.timeout.Value()), maximumTimeout); - if (timeout.count() == 0) - { - commandObj->AddStatus(commandPath, Status::InvalidInState); - return; - } if (status == Status::Success) { @@ -605,9 +617,6 @@ bool emberAfThermostatClusterAtomicRequestCallback(CommandHandler * commandObj, { auto & requestType = commandData.requestType; - // If we've gotten this far, then the client has manage permission to call AtomicRequest, which is also the - // privilege necessary to write to the atomic attributes, so no need to check - switch (requestType) { case Globals::AtomicRequestTypeEnum::kBeginWrite: From b3507cebe7defed94c3394b8bf3d574115279531 Mon Sep 17 00:00:00 2001 From: Anush Nadathur Date: Fri, 23 Aug 2024 12:28:24 -0700 Subject: [PATCH 14/32] [Darwin] Enable taking assertion on MTRDeviceController (#35148) * [Darwin] Enable taking assertion on MTRDeviceController - Added ability to take assertion on MTRDeviceController to keep it running until all assertions are removed - A request to shutdown will take place only if there are no assertions are present * Fixed format string * Account for existing controller that is asserted in factory when creating a new one * Simplified to use lock for assertion tracking * Fixed build error * Removed unneeded includes * Fixed bugs with wrong match logic * resytle fixes * Restyle fixes * Apply suggestions from code review Co-authored-by: Boris Zbarsky * Fixed build failure from review suggestions and added comment per review feedback --------- Co-authored-by: Boris Zbarsky --- .../Framework/CHIP/MTRDeviceController.mm | 94 +++++++++++++++++++ .../CHIP/MTRDeviceControllerFactory.mm | 19 ++++ .../CHIP/MTRDeviceControllerStartupParams.mm | 23 +++++ ...TRDeviceControllerStartupParams_Internal.h | 4 + .../CHIP/MTRDeviceController_Internal.h | 40 ++++++++ 5 files changed, 180 insertions(+) diff --git a/src/darwin/Framework/CHIP/MTRDeviceController.mm b/src/darwin/Framework/CHIP/MTRDeviceController.mm index 11fd481b48b2fc..4630ff7fe00b4f 100644 --- a/src/darwin/Framework/CHIP/MTRDeviceController.mm +++ b/src/darwin/Framework/CHIP/MTRDeviceController.mm @@ -46,6 +46,7 @@ #import "MTRSetupPayload.h" #import "MTRTimeUtils.h" #import "MTRUnfairLock.h" +#import "MTRUtilities.h" #import "NSDataSpanConversion.h" #import "NSStringSpanConversion.h" #import @@ -129,6 +130,11 @@ @implementation MTRDeviceController { std::atomic> _storedCompressedFabricID; MTRP256KeypairBridge _signingKeypairBridge; MTRP256KeypairBridge _operationalKeypairBridge; + + // Counters to track assertion status and access controlled by the _assertionLock + NSUInteger _keepRunningAssertionCounter; + BOOL _shutdownPending; + os_unfair_lock _assertionLock; } - (os_unfair_lock_t)deviceMapLock @@ -142,6 +148,11 @@ - (instancetype)initForSubclasses // nothing, as superclass of MTRDeviceController is NSObject } _underlyingDeviceMapLock = OS_UNFAIR_LOCK_INIT; + + // Setup assertion variables + _keepRunningAssertionCounter = 0; + _shutdownPending = NO; + _assertionLock = OS_UNFAIR_LOCK_INIT; return self; } @@ -178,6 +189,12 @@ - (instancetype)initWithFactory:(MTRDeviceControllerFactory *)factory // Make sure our storage is all set up to work as early as possible, // before we start doing anything else with the controller. _uniqueIdentifier = uniqueIdentifier; + + // Setup assertion variables + _keepRunningAssertionCounter = 0; + _shutdownPending = NO; + _assertionLock = OS_UNFAIR_LOCK_INIT; + if (storageDelegate != nil) { if (storageDelegateQueue == nil) { MTR_LOG_ERROR("storageDelegate provided without storageDelegateQueue"); @@ -295,6 +312,9 @@ - (instancetype)initWithFactory:(MTRDeviceControllerFactory *)factory _storedFabricIndex = chip::kUndefinedFabricIndex; _storedCompressedFabricID = std::nullopt; + self.nodeID = nil; + self.fabricID = nil; + self.rootPublicKey = nil; _storageBehaviorConfiguration = storageBehaviorConfiguration; } @@ -311,8 +331,69 @@ - (BOOL)isRunning return _cppCommissioner != nullptr; } +- (BOOL)matchesPendingShutdownWithParams:(MTRDeviceControllerParameters *)parameters +{ + if (!parameters.operationalCertificate || !parameters.rootCertificate) { + return FALSE; + } + NSNumber * nodeID = [MTRDeviceControllerParameters nodeIDFromNOC:parameters.operationalCertificate]; + NSNumber * fabricID = [MTRDeviceControllerParameters fabricIDFromNOC:parameters.operationalCertificate]; + NSData * publicKey = [MTRDeviceControllerParameters publicKeyFromCertificate:parameters.rootCertificate]; + + std::lock_guard lock(_assertionLock); + + // If any of the local above are nil, the return will be false since MTREqualObjects handles them correctly + return _keepRunningAssertionCounter > 0 && _shutdownPending && MTREqualObjects(nodeID, self.nodeID) && MTREqualObjects(fabricID, self.fabricID) && MTREqualObjects(publicKey, self.rootPublicKey); +} + +- (void)addRunAssertion +{ + std::lock_guard lock(_assertionLock); + + // Only take an assertion if running + if ([self isRunning]) { + ++_keepRunningAssertionCounter; + MTR_LOG("%@ Adding keep running assertion, total %lu", self, static_cast(_keepRunningAssertionCounter)); + } +} + +- (void)removeRunAssertion; +{ + std::lock_guard lock(_assertionLock); + + if (_keepRunningAssertionCounter > 0) { + --_keepRunningAssertionCounter; + MTR_LOG("%@ Removing keep running assertion, total %lu", self, static_cast(_keepRunningAssertionCounter)); + + if ([self isRunning] && _keepRunningAssertionCounter == 0 && _shutdownPending) { + MTR_LOG("%@ All assertions removed and shutdown is pending, shutting down", self); + [self finalShutdown]; + } + } +} + +- (void)clearPendingShutdown +{ + std::lock_guard lock(_assertionLock); + _shutdownPending = NO; +} + - (void)shutdown { + std::lock_guard lock(_assertionLock); + + if (_keepRunningAssertionCounter > 0) { + MTR_LOG("%@ Pending shutdown since %lu assertions are present", self, static_cast(_keepRunningAssertionCounter)); + _shutdownPending = YES; + return; + } + [self finalShutdown]; +} + +- (void)finalShutdown +{ + os_unfair_lock_assert_owner(&_assertionLock); + MTR_LOG("%@ shutdown called", self); if (_cppCommissioner == nullptr) { // Already shut down. @@ -369,11 +450,16 @@ - (void)shutDownCppController // shuts down. _storedFabricIndex = chip::kUndefinedFabricIndex; _storedCompressedFabricID = std::nullopt; + self.nodeID = nil; + self.fabricID = nil; + self.rootPublicKey = nil; + delete commissionerToShutDown; if (_operationalCredentialsDelegate != nil) { _operationalCredentialsDelegate->SetDeviceCommissioner(nullptr); } } + _shutdownPending = NO; } - (void)deinitFromFactory @@ -609,6 +695,14 @@ - (BOOL)startup:(MTRDeviceControllerStartupParamsInternal *)startupParams self->_storedFabricIndex = fabricIdx; self->_storedCompressedFabricID = _cppCommissioner->GetCompressedFabricId(); + + chip::Crypto::P256PublicKey rootPublicKey; + if (_cppCommissioner->GetRootPublicKey(rootPublicKey) == CHIP_NO_ERROR) { + self.rootPublicKey = [NSData dataWithBytes:rootPublicKey.Bytes() length:rootPublicKey.Length()]; + self.nodeID = @(_cppCommissioner->GetNodeId()); + self.fabricID = @(_cppCommissioner->GetFabricId()); + } + commissionerInitialized = YES; MTR_LOG("%@ startup succeeded for nodeID 0x%016llX", self, self->_cppCommissioner->GetNodeId()); diff --git a/src/darwin/Framework/CHIP/MTRDeviceControllerFactory.mm b/src/darwin/Framework/CHIP/MTRDeviceControllerFactory.mm index 5b089b392074e6..bd0b90450b9ac4 100644 --- a/src/darwin/Framework/CHIP/MTRDeviceControllerFactory.mm +++ b/src/darwin/Framework/CHIP/MTRDeviceControllerFactory.mm @@ -1133,12 +1133,31 @@ - (void)operationalInstanceAdded:(chip::PeerId &)operationalID } } +- (nullable MTRDeviceController *)_findControllerWithPendingShutdownMatchingParams:(MTRDeviceControllerParameters *)parameters +{ + std::lock_guard lock(_controllersLock); + for (MTRDeviceController * controller in _controllers) { + if ([controller matchesPendingShutdownWithParams:parameters]) { + MTR_LOG("%@ Found existing controller %@ that is pending shutdown and matching parameters, re-using it", self, controller); + [controller clearPendingShutdown]; + return controller; + } + } + return nil; +} + - (nullable MTRDeviceController *)initializeController:(MTRDeviceController *)controller withParameters:(MTRDeviceControllerParameters *)parameters error:(NSError * __autoreleasing *)error { [self _assertCurrentQueueIsNotMatterQueue]; + // If there is a controller already running with matching parameters that is conceptually shut down from the API consumer's viewpoint, re-use it. + MTRDeviceController * existingController = [self _findControllerWithPendingShutdownMatchingParams:parameters]; + if (existingController) { + return existingController; + } + return [self _startDeviceController:controller startupParams:parameters fabricChecker:^MTRDeviceControllerStartupParamsInternal *( diff --git a/src/darwin/Framework/CHIP/MTRDeviceControllerStartupParams.mm b/src/darwin/Framework/CHIP/MTRDeviceControllerStartupParams.mm index c5923ad4e54619..5850a8c4efe384 100644 --- a/src/darwin/Framework/CHIP/MTRDeviceControllerStartupParams.mm +++ b/src/darwin/Framework/CHIP/MTRDeviceControllerStartupParams.mm @@ -306,6 +306,29 @@ - (void)setOTAProviderDelegate:(id)otaProviderDelegate q _otaProviderDelegateQueue = queue; } ++ (nullable NSNumber *)nodeIDFromNOC:(MTRCertificateDERBytes)noc +{ + NSNumber * nodeID = nil; + ExtractNodeIDFromNOC(noc, &nodeID); + return nodeID; +} + ++ (nullable NSNumber *)fabricIDFromNOC:(MTRCertificateDERBytes)noc +{ + NSNumber * fabricID = nil; + ExtractFabricIDFromNOC(noc, &fabricID); + return fabricID; +} + ++ (nullable NSData *)publicKeyFromCertificate:(MTRCertificateDERBytes)certificate +{ + Crypto::P256PublicKey pubKey; + if (ExtractPubkeyFromX509Cert(AsByteSpan(certificate), pubKey) != CHIP_NO_ERROR) { + return nil; + } + return [NSData dataWithBytes:pubKey.Bytes() length:pubKey.Length()]; +} + @end @implementation MTRDeviceControllerExternalCertificateParameters diff --git a/src/darwin/Framework/CHIP/MTRDeviceControllerStartupParams_Internal.h b/src/darwin/Framework/CHIP/MTRDeviceControllerStartupParams_Internal.h index 6b8c762633578a..0b4065f491873b 100644 --- a/src/darwin/Framework/CHIP/MTRDeviceControllerStartupParams_Internal.h +++ b/src/darwin/Framework/CHIP/MTRDeviceControllerStartupParams_Internal.h @@ -85,6 +85,10 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, strong, readonly, nullable) id otaProviderDelegate; @property (nonatomic, strong, readonly, nullable) dispatch_queue_t otaProviderDelegateQueue; ++ (nullable NSNumber *)nodeIDFromNOC:(MTRCertificateDERBytes)noc; ++ (nullable NSNumber *)fabricIDFromNOC:(MTRCertificateDERBytes)noc; ++ (nullable NSData *)publicKeyFromCertificate:(MTRCertificateDERBytes)certificate; + @end @interface MTRDeviceControllerStartupParamsInternal : MTRDeviceControllerStartupParams diff --git a/src/darwin/Framework/CHIP/MTRDeviceController_Internal.h b/src/darwin/Framework/CHIP/MTRDeviceController_Internal.h index 54d5cfd8d340fa..e625d13a80e77e 100644 --- a/src/darwin/Framework/CHIP/MTRDeviceController_Internal.h +++ b/src/darwin/Framework/CHIP/MTRDeviceController_Internal.h @@ -45,6 +45,7 @@ #import #import +@class MTRDeviceControllerParameters; @class MTRDeviceControllerStartupParamsInternal; @class MTRDeviceControllerFactory; @class MTRDevice; @@ -117,6 +118,21 @@ NS_ASSUME_NONNULL_BEGIN */ @property (nonatomic, readonly) MTRAsyncWorkQueue * concurrentSubscriptionPool; +/** + * Fabric ID tied to controller + */ +@property (nonatomic, retain, nullable) NSNumber * fabricID; + +/** + * Node ID tied to controller + */ +@property (nonatomic, retain, nullable) NSNumber * nodeID; + +/** + * Root Public Key tied to controller + */ +@property (nonatomic, retain, nullable) NSData * rootPublicKey; + /** * Init a newly created controller. * @@ -289,6 +305,30 @@ NS_ASSUME_NONNULL_BEGIN */ - (void)directlyGetSessionForNode:(chip::NodeId)nodeID completion:(MTRInternalDeviceConnectionCallback)completion; +/** + * Takes an assertion to keep the controller running. If `-[MTRDeviceController shutdown]` is called while an assertion + * is held, the shutdown will be honored only after all assertions are released. Invoking this method multiple times increases + * the number of assertions and needs to be matched with equal amount of '-[MTRDeviceController removeRunAssertion]` to release + * the assertion. + */ +- (void)addRunAssertion; + +/** + * Removes an assertion to allow the controller to shutdown once all assertions have been released. + * Invoking this method once all assertions have been released in a noop. + */ +- (void)removeRunAssertion; + +/** + * This method returns TRUE if this controller matches the fabric reference and node ID as listed in the parameters. + */ +- (BOOL)matchesPendingShutdownWithParams:(MTRDeviceControllerParameters *)parameters; + +/** + * Clear any pending shutdown request. + */ +- (void)clearPendingShutdown; + @end /** From 0b5af2a0bcdbc1c0f96b239b631a8eb3148fce12 Mon Sep 17 00:00:00 2001 From: Junior Martinez <67972863+jmartinez-silabs@users.noreply.github.com> Date: Fri, 23 Aug 2024 16:31:26 -0400 Subject: [PATCH 15/32] Cleanup/updates tied with latest sdk update (#35140) --- examples/platform/silabs/FreeRTOSConfig.h | 2 +- src/platform/silabs/ConfigurationManagerImpl.cpp | 2 +- .../silabs/efr32/Efr32PsaOpaqueKeypair.cpp | 15 +++++---------- .../silabs/efr32/OTAImageProcessorImpl.cpp | 2 +- .../silabs/multi-ota/OTACustomProcessor.cpp | 2 +- .../silabs/multi-ota/OTAFirmwareProcessor.cpp | 2 +- .../multi-ota/OTAMultiImageProcessorImpl.cpp | 2 +- .../silabs/platformAbstraction/GsdkSpam.cpp | 16 +++++++++++++++- src/test_driver/efr32/include/FreeRTOSConfig.h | 2 +- 9 files changed, 27 insertions(+), 18 deletions(-) diff --git a/examples/platform/silabs/FreeRTOSConfig.h b/examples/platform/silabs/FreeRTOSConfig.h index b27c775d9b7a54..1c2db9621f61ee 100644 --- a/examples/platform/silabs/FreeRTOSConfig.h +++ b/examples/platform/silabs/FreeRTOSConfig.h @@ -118,8 +118,8 @@ extern uint32_t SystemCoreClock; #include "RTE_Components.h" #include CMSIS_device_header -#include "em_assert.h" #include "em_device.h" +#include "sl_assert.h" #endif #if defined(SL_COMPONENT_CATALOG_PRESENT) diff --git a/src/platform/silabs/ConfigurationManagerImpl.cpp b/src/platform/silabs/ConfigurationManagerImpl.cpp index 5a511038c799ed..122799952acab5 100644 --- a/src/platform/silabs/ConfigurationManagerImpl.cpp +++ b/src/platform/silabs/ConfigurationManagerImpl.cpp @@ -121,7 +121,7 @@ CHIP_ERROR ConfigurationManagerImpl::GetBootReason(uint32_t & bootReason) matterBootCause = BootReasonType::kPowerOnReboot; } else if (rebootCause & EMU_RSTCAUSE_AVDDBOD || rebootCause & EMU_RSTCAUSE_DVDDBOD || rebootCause & EMU_RSTCAUSE_DECBOD || - rebootCause & EMU_RSTCAUSE_VREGIN || rebootCause & EMU_RSTCAUSE_IOVDD0BOD || rebootCause & EMU_RSTCAUSE_DVDDLEBOD) + rebootCause & EMU_RSTCAUSE_IOVDD0BOD || rebootCause & EMU_RSTCAUSE_DVDDLEBOD) { matterBootCause = BootReasonType::kBrownOutReset; } diff --git a/src/platform/silabs/efr32/Efr32PsaOpaqueKeypair.cpp b/src/platform/silabs/efr32/Efr32PsaOpaqueKeypair.cpp index 7a74c137596070..542142cd1a1d5c 100644 --- a/src/platform/silabs/efr32/Efr32PsaOpaqueKeypair.cpp +++ b/src/platform/silabs/efr32/Efr32PsaOpaqueKeypair.cpp @@ -18,6 +18,7 @@ #include "Efr32OpaqueKeypair.h" #include "em_device.h" #include +#include #include #include @@ -47,14 +48,6 @@ namespace Internal { static_assert((kEFR32OpaqueKeyIdPersistentMax - kEFR32OpaqueKeyIdPersistentMin) < PSA_KEY_ID_FOR_MATTER_SIZE, "Not enough PSA range to store all allowed opaque key IDs"); -#if defined(SEMAILBOX_PRESENT) && (_SILICON_LABS_SECURITY_FEATURE == _SILICON_LABS_SECURITY_FEATURE_VAULT) -#define PSA_CRYPTO_LOCATION_FOR_DEVICE PSA_KEY_LOCATION_SL_SE_OPAQUE -#elif defined(CRYPTOACC_PRESENT) && defined(SEPUF_PRESENT) && defined(SL_TRUSTZONE_NONSECURE) -#define PSA_CRYPTO_LOCATION_FOR_DEVICE PSA_KEY_LOCATION_SL_CRYPTOACC_OPAQUE -#else -#define PSA_CRYPTO_LOCATION_FOR_DEVICE PSA_KEY_LOCATION_LOCAL_STORAGE -#endif - static void _log_PSA_error(psa_status_t status) { if (status != PSA_SUCCESS) @@ -190,7 +183,8 @@ CHIP_ERROR EFR32OpaqueKeypair::Create(EFR32OpaqueKeyId opaque_id, EFR32OpaqueKey if (opaque_id == kEFR32OpaqueKeyIdVolatile) { psa_set_key_lifetime( - &attr, PSA_KEY_LIFETIME_FROM_PERSISTENCE_AND_LOCATION(PSA_KEY_LIFETIME_VOLATILE, PSA_CRYPTO_LOCATION_FOR_DEVICE)); + &attr, + PSA_KEY_LIFETIME_FROM_PERSISTENCE_AND_LOCATION(PSA_KEY_LIFETIME_VOLATILE, sl_psa_get_most_secure_key_location())); } else { @@ -210,7 +204,8 @@ CHIP_ERROR EFR32OpaqueKeypair::Create(EFR32OpaqueKeyId opaque_id, EFR32OpaqueKey psa_set_key_id(&attr, key_id); psa_set_key_lifetime( - &attr, PSA_KEY_LIFETIME_FROM_PERSISTENCE_AND_LOCATION(PSA_KEY_LIFETIME_PERSISTENT, PSA_CRYPTO_LOCATION_FOR_DEVICE)); + &attr, + PSA_KEY_LIFETIME_FROM_PERSISTENCE_AND_LOCATION(PSA_KEY_LIFETIME_PERSISTENT, sl_psa_get_most_secure_key_location())); } switch (usage) diff --git a/src/platform/silabs/efr32/OTAImageProcessorImpl.cpp b/src/platform/silabs/efr32/OTAImageProcessorImpl.cpp index 598ee5a2093605..846d4f720ac930 100644 --- a/src/platform/silabs/efr32/OTAImageProcessorImpl.cpp +++ b/src/platform/silabs/efr32/OTAImageProcessorImpl.cpp @@ -22,7 +22,7 @@ extern "C" { #include "btl_interface.h" -#include "em_bus.h" // For CORE_CRITICAL_SECTION +#include "sl_core.h" #if SL_WIFI #include "spi_multiplex.h" #endif // SL_WIFI diff --git a/src/platform/silabs/multi-ota/OTACustomProcessor.cpp b/src/platform/silabs/multi-ota/OTACustomProcessor.cpp index 85ceb7d44d4eaf..ef761d003e0499 100644 --- a/src/platform/silabs/multi-ota/OTACustomProcessor.cpp +++ b/src/platform/silabs/multi-ota/OTACustomProcessor.cpp @@ -24,7 +24,7 @@ extern "C" { #include "btl_interface.h" -#include "em_bus.h" // For CORE_CRITICAL_SECTION +#include "sl_core.h" #if SL_WIFI #include "spi_multiplex.h" #endif // SL_WIFI diff --git a/src/platform/silabs/multi-ota/OTAFirmwareProcessor.cpp b/src/platform/silabs/multi-ota/OTAFirmwareProcessor.cpp index 3fc66e53913b87..d8545d01f40996 100644 --- a/src/platform/silabs/multi-ota/OTAFirmwareProcessor.cpp +++ b/src/platform/silabs/multi-ota/OTAFirmwareProcessor.cpp @@ -24,7 +24,7 @@ extern "C" { #include "btl_interface.h" -#include "em_bus.h" // For CORE_CRITICAL_SECTION +#include "sl_core.h" #if SL_WIFI #include "spi_multiplex.h" #endif // SL_WIFI diff --git a/src/platform/silabs/multi-ota/OTAMultiImageProcessorImpl.cpp b/src/platform/silabs/multi-ota/OTAMultiImageProcessorImpl.cpp index 9dfb42fc879c4b..a95cb4e1ada4b4 100644 --- a/src/platform/silabs/multi-ota/OTAMultiImageProcessorImpl.cpp +++ b/src/platform/silabs/multi-ota/OTAMultiImageProcessorImpl.cpp @@ -32,7 +32,7 @@ static chip::OTAMultiImageProcessorImpl gImageProcessor; extern "C" { #include "btl_interface.h" -#include "em_bus.h" // For CORE_CRITICAL_SECTION +#include "sl_core.h" #if SL_WIFI #include "spi_multiplex.h" #endif // SL_WIFI diff --git a/src/platform/silabs/platformAbstraction/GsdkSpam.cpp b/src/platform/silabs/platformAbstraction/GsdkSpam.cpp index 514003305868cf..eb8704ecfc1116 100644 --- a/src/platform/silabs/platformAbstraction/GsdkSpam.cpp +++ b/src/platform/silabs/platformAbstraction/GsdkSpam.cpp @@ -17,7 +17,11 @@ #include +#if defined(_SILICON_LABS_32B_SERIES_2) #include "em_rmu.h" +#else +#include "sl_hal_emu.h" +#endif #include "sl_system_kernel.h" #ifdef ENABLE_WSTK_LEDS @@ -71,9 +75,19 @@ SilabsPlatform::SilabsButtonCb SilabsPlatform::mButtonCallback = nullptr; CHIP_ERROR SilabsPlatform::Init(void) { +#ifdef _SILICON_LABS_32B_SERIES_2 + // Read the cause of last reset. mRebootCause = RMU_ResetCauseGet(); - // Clear register so it does accumualate the causes of each reset + + // Clear the register, as the causes cumulate over resets. RMU_ResetCauseClear(); +#else + // Read the cause of last reset. + mRebootCause = sl_hal_emu_get_reset_cause(); + + // Clear the register, as the causes cumulate over resets. + sl_hal_emu_clear_reset_cause(); +#endif // _SILICON_LABS_32B_SERIES_2 #if SILABS_LOG_OUT_UART && defined(SL_CATALOG_CLI_PRESENT) sl_iostream_set_default(sl_iostream_stdio_handle); diff --git a/src/test_driver/efr32/include/FreeRTOSConfig.h b/src/test_driver/efr32/include/FreeRTOSConfig.h index 7a02673c27b5b0..5cd17b4a7257e0 100644 --- a/src/test_driver/efr32/include/FreeRTOSConfig.h +++ b/src/test_driver/efr32/include/FreeRTOSConfig.h @@ -108,8 +108,8 @@ extern "C" { #include "RTE_Components.h" #include CMSIS_device_header -#include "em_assert.h" #include "em_device.h" +#include "sl_assert.h" #if defined(SL_COMPONENT_CATALOG_PRESENT) #include "sl_component_catalog.h" From f01ec93099f1d3f9c542ff50d2ff1c3cbdb52271 Mon Sep 17 00:00:00 2001 From: chirag-silabs <100861685+chirag-silabs@users.noreply.github.com> Date: Sat, 24 Aug 2024 02:02:50 +0530 Subject: [PATCH 16/32] [Silabs] Adding the gn build header for the lwip IPv4 and RS9116 (#35142) * adding the gn build header for the lwip and rs9116 * Restyled by clang-format --------- Co-authored-by: Restyled.io --- .../platform/silabs/efr32/rs911x/rsi_wlan_config.h | 4 ++++ src/lwip/silabs/lwipopts-rs911x.h | 10 ++++++++++ src/lwip/silabs/lwipopts-wf200.h | 10 ++++++++++ 3 files changed, 24 insertions(+) diff --git a/examples/platform/silabs/efr32/rs911x/rsi_wlan_config.h b/examples/platform/silabs/efr32/rs911x/rsi_wlan_config.h index 96bd71bb80539a..b851cd79580a22 100644 --- a/examples/platform/silabs/efr32/rs911x/rsi_wlan_config.h +++ b/examples/platform/silabs/efr32/rs911x/rsi_wlan_config.h @@ -20,6 +20,10 @@ #include "rsi_wlan_defines.h" +#if (SL_MATTER_GN_BUILD == 0) +#include "sl_matter_wifi_config.h" +#endif // SL_MATTER_GN_BUILD + //! Enable feature #define RSI_ENABLE 1 //! Disable feature diff --git a/src/lwip/silabs/lwipopts-rs911x.h b/src/lwip/silabs/lwipopts-rs911x.h index 4f8075461b0c6d..c563a706ac9737 100644 --- a/src/lwip/silabs/lwipopts-rs911x.h +++ b/src/lwip/silabs/lwipopts-rs911x.h @@ -32,6 +32,10 @@ #include +#if (SL_MATTER_GN_BUILD == 0) +#include "sl_matter_wifi_config.h" +#endif // SL_MATTER_GN_BUILD + #define NO_SYS 0 #define MEM_ALIGNMENT (4) #define MEMP_NUM_TCP_SEG (TCP_SND_QUEUELEN + 1) @@ -90,6 +94,12 @@ #define MEMP_NUM_NETCONN (0) +#if CHIP_DEVICE_CONFIG_ENABLE_IPV4 +#define LWIP_IPV4 1 +#else +#define LWIP_IPV4 0 +#endif // CHIP_DEVICE_CONFIG_ENABLE_IPV4 + #ifndef LWIP_ARP #define LWIP_ARP (LWIP_IPV4) #endif /* LWIP_ARP */ diff --git a/src/lwip/silabs/lwipopts-wf200.h b/src/lwip/silabs/lwipopts-wf200.h index cc9b45b144278a..9ea7e4f6bbf07c 100644 --- a/src/lwip/silabs/lwipopts-wf200.h +++ b/src/lwip/silabs/lwipopts-wf200.h @@ -32,6 +32,10 @@ #include +#if (SL_MATTER_GN_BUILD == 0) +#include "sl_matter_wifi_config.h" +#endif // SL_MATTER_GN_BUILD + #define NO_SYS 0 #define MEM_ALIGNMENT (4) #define MEMP_NUM_TCP_SEG (TCP_SND_QUEUELEN + 1) @@ -91,6 +95,12 @@ #define MEMP_NUM_NETCONN (0) +#if CHIP_DEVICE_CONFIG_ENABLE_IPV4 +#define LWIP_IPV4 1 +#else +#define LWIP_IPV4 0 +#endif // CHIP_DEVICE_CONFIG_ENABLE_IPV4 + #ifndef LWIP_ARP #define LWIP_ARP (LWIP_IPV4) #endif /* LWIP_ARP */ From 26e59b68b795ff36f1e8a14dd602c46d1e442c57 Mon Sep 17 00:00:00 2001 From: chirag-silabs <100861685+chirag-silabs@users.noreply.github.com> Date: Sat, 24 Aug 2024 02:17:24 +0530 Subject: [PATCH 17/32] [Silabs] [WiFi] Fixing the retry logic for the WiFi Devices (#34225) * Modifying the retry logic for the wifi devices * Baseapplication.cpp modification of the delegate fix * Restyled by clang-format * removing the soc and sleepy condition * Restyled by clang-format * Restyled by gn * moving the variable to efr32_sdk.gni to make it available everywhere * correcting the path of the 917 ncp file * fixing the build for the 917 ncp * addressing the review comments * fixing the build and replacing the define * removing the unused else case * Update third_party/silabs/efr32_sdk.gni Co-authored-by: mkardous-silabs <84793247+mkardous-silabs@users.noreply.github.com> * addressing the review comments * Restyled by whitespace * Restyled by clang-format * fixing the build * fixing the efr32 build --------- Co-authored-by: Restyled.io Co-authored-by: mkardous-silabs <84793247+mkardous-silabs@users.noreply.github.com> --- examples/platform/silabs/BaseApplication.cpp | 3 + examples/platform/silabs/BaseApplication.h | 5 +- examples/platform/silabs/SiWx917/BUILD.gn | 4 +- .../silabs/SiWx917/SiWx917/sl_wifi_if.cpp | 22 +- examples/platform/silabs/efr32/BUILD.gn | 2 - .../platform/silabs/efr32/rs911x/rs9117.gni | 2 +- .../platform/silabs/efr32/rs911x/rs911x.gni | 2 +- .../platform/silabs/efr32/rs911x/rsi_if.c | 103 +++----- .../platform/silabs/efr32/wf200/host_if.cpp | 28 +-- .../platform/silabs/efr32/wf200/wf200.gni | 2 +- examples/platform/silabs/wfx_rsi.h | 1 - .../platform/silabs}/wifi/wfx_notify.cpp | 130 +++++------ .../silabs/SiWx917/wifi/wfx_host_events.h | 8 +- .../silabs/efr32/wifi/wfx_host_events.h | 13 +- src/platform/silabs/efr32/wifi/wfx_notify.cpp | 220 ------------------ third_party/silabs/efr32_sdk.gni | 2 + 16 files changed, 131 insertions(+), 416 deletions(-) rename {src/platform/silabs/SiWx917 => examples/platform/silabs}/wifi/wfx_notify.cpp (64%) delete mode 100644 src/platform/silabs/efr32/wifi/wfx_notify.cpp diff --git a/examples/platform/silabs/BaseApplication.cpp b/examples/platform/silabs/BaseApplication.cpp index 99123c5bf764d1..8cf5b3891dfe8e 100644 --- a/examples/platform/silabs/BaseApplication.cpp +++ b/examples/platform/silabs/BaseApplication.cpp @@ -190,6 +190,9 @@ void BaseApplicationDelegate::OnCommissioningWindowClosed() #endif // CHIP_CONFIG_ENABLE_ICD_SERVER && SLI_SI917 if (BaseApplication::GetProvisionStatus()) { + // After the device is provisioned and the commissioning passed + // resetting the isCommissioningStarted to false + isComissioningStarted = false; #ifdef DISPLAY_ENABLED #ifdef QR_CODE_ENABLED SilabsLCD::Screen_e screen; diff --git a/examples/platform/silabs/BaseApplication.h b/examples/platform/silabs/BaseApplication.h index 9052e9355aab90..5d757b8ca4672d 100644 --- a/examples/platform/silabs/BaseApplication.h +++ b/examples/platform/silabs/BaseApplication.h @@ -65,9 +65,12 @@ class BaseApplicationDelegate : public AppDelegate, public chip::FabricTable::Delegate { +public: + bool isCommissioningInProgress() { return isComissioningStarted; } + private: // AppDelegate - bool isComissioningStarted; + bool isComissioningStarted = false; void OnCommissioningSessionStarted() override; void OnCommissioningSessionStopped() override; void OnCommissioningWindowClosed() override; diff --git a/examples/platform/silabs/SiWx917/BUILD.gn b/examples/platform/silabs/SiWx917/BUILD.gn index 80d247b56c4df4..2780e2ecbcc16e 100644 --- a/examples/platform/silabs/SiWx917/BUILD.gn +++ b/examples/platform/silabs/SiWx917/BUILD.gn @@ -50,8 +50,6 @@ declare_args() { # Sanity check assert(chip_enable_wifi) - -silabs_common_plat_dir = "${chip_root}/examples/platform/silabs" silabs_plat_si91x_wifi_dir = "${chip_root}/src/platform/silabs/SiWx917/wifi" import("${silabs_common_plat_dir}/args.gni") @@ -193,10 +191,10 @@ source_set("siwx917-common") { "${silabs_common_plat_dir}/SoftwareFaultReports.cpp", "${silabs_common_plat_dir}/silabs_utils.cpp", "${silabs_common_plat_dir}/syscalls_stubs.cpp", + "${silabs_common_plat_dir}/wifi/wfx_notify.cpp", "${silabs_plat_si91x_wifi_dir}/dhcp_client.cpp", "${silabs_plat_si91x_wifi_dir}/ethernetif.cpp", "${silabs_plat_si91x_wifi_dir}/lwip_netif.cpp", - "${silabs_plat_si91x_wifi_dir}/wfx_notify.cpp", "SiWx917/sl_wifi_if.cpp", "SiWx917/wfx_rsi_host.cpp", ] diff --git a/examples/platform/silabs/SiWx917/SiWx917/sl_wifi_if.cpp b/examples/platform/silabs/SiWx917/SiWx917/sl_wifi_if.cpp index 03b51540cf4b6d..92d5ca4808195c 100644 --- a/examples/platform/silabs/SiWx917/SiWx917/sl_wifi_if.cpp +++ b/examples/platform/silabs/SiWx917/SiWx917/sl_wifi_if.cpp @@ -101,11 +101,6 @@ bool hasNotifiedIPV4 = false; #endif /* CHIP_DEVICE_CONFIG_ENABLE_IPV4 */ bool hasNotifiedWifiConnectivity = false; -/* Declare a flag to differentiate between after boot-up first IP connection or reconnection */ -bool is_wifi_disconnection_event = false; - -/* Declare a variable to hold connection time intervals */ -uint32_t retryInterval = WLAN_MIN_RETRY_TIMER_MS; volatile bool scan_results_complete = false; volatile bool bg_scan_results_complete = false; extern osSemaphoreId_t sl_rs_ble_init_sem; @@ -247,12 +242,7 @@ sl_status_t join_callback_handler(sl_wifi_event_t event, char * result, uint32_t callback_status = *(sl_status_t *) result; ChipLogError(DeviceLayer, "join_callback_handler: failed: 0x%lx", static_cast(callback_status)); wfx_rsi.dev_state &= ~(WFX_RSI_ST_STA_CONNECTED); - wfx_retry_interval_handler(is_wifi_disconnection_event, wfx_rsi.join_retries++); - if (is_wifi_disconnection_event || wfx_rsi.join_retries <= WFX_RSI_CONFIG_MAX_JOIN) - { - WfxEvent.eventType = WFX_EVT_STA_START_JOIN; - WfxPostEvent(&WfxEvent); - } + wfx_retry_connection(++wfx_rsi.join_retries); return SL_STATUS_FAIL; } /* @@ -264,10 +254,7 @@ sl_status_t join_callback_handler(sl_wifi_event_t event, char * result, uint32_t WfxEvent.eventType = WFX_EVT_STA_CONN; WfxPostEvent(&WfxEvent); wfx_rsi.join_retries = 0; - retryInterval = WLAN_MIN_RETRY_TIMER_MS; - // Once the join passes setting the disconnection event to true to differentiate between the first connection and reconnection - is_wifi_disconnection_event = true; - callback_status = SL_STATUS_OK; + callback_status = SL_STATUS_OK; return SL_STATUS_OK; } @@ -693,12 +680,11 @@ static sl_status_t wfx_rsi_do_join(void) // failure only happens when the firmware returns an error ChipLogError(DeviceLayer, "wfx_rsi_do_join: sl_wifi_connect failed: 0x%lx", static_cast(status)); - VerifyOrReturnError((is_wifi_disconnection_event || wfx_rsi.join_retries <= MAX_JOIN_RETRIES_COUNT), status); + VerifyOrReturnError((wfx_rsi.join_retries <= MAX_JOIN_RETRIES_COUNT), status); wfx_rsi.dev_state &= ~(WFX_RSI_ST_STA_CONNECTING | WFX_RSI_ST_STA_CONNECTED); ChipLogProgress(DeviceLayer, "wfx_rsi_do_join: retry attempt %d", wfx_rsi.join_retries); - wfx_retry_interval_handler(is_wifi_disconnection_event, wfx_rsi.join_retries); - wfx_rsi.join_retries++; + wfx_retry_connection(++wfx_rsi.join_retries); event.eventType = WFX_EVT_STA_START_JOIN; WfxPostEvent(&event); return status; diff --git a/examples/platform/silabs/efr32/BUILD.gn b/examples/platform/silabs/efr32/BUILD.gn index 22b7458588d97c..3691091a0bc3c7 100644 --- a/examples/platform/silabs/efr32/BUILD.gn +++ b/examples/platform/silabs/efr32/BUILD.gn @@ -48,8 +48,6 @@ declare_args() { sl_test_event_trigger_enable_key = "00112233445566778899AABBCCDDEEFF" } -silabs_common_plat_dir = "${chip_root}/examples/platform/silabs" - import("${silabs_common_plat_dir}/args.gni") # Sanity check diff --git a/examples/platform/silabs/efr32/rs911x/rs9117.gni b/examples/platform/silabs/efr32/rs911x/rs9117.gni index c068e7aa3efaff..356b72f55f75d4 100644 --- a/examples/platform/silabs/efr32/rs911x/rs9117.gni +++ b/examples/platform/silabs/efr32/rs911x/rs9117.gni @@ -8,7 +8,7 @@ rs911x_src_plat = [ "${examples_plat_dir}/rs911x/hal/rsi_hal_mcu_interrupt.c", "${examples_plat_dir}/rs911x/hal/sl_si91x_ncp_utility.c", "${examples_plat_dir}/rs911x/hal/efx32_ncp_host.c", - "${silabs_plat_efr32_wifi_dir}/wfx_notify.cpp", + "${silabs_common_plat_dir}/wifi/wfx_notify.cpp", ] rs9117_inc_plat = [ diff --git a/examples/platform/silabs/efr32/rs911x/rs911x.gni b/examples/platform/silabs/efr32/rs911x/rs911x.gni index ebf7c546f6a068..54507de66e0ced 100644 --- a/examples/platform/silabs/efr32/rs911x/rs911x.gni +++ b/examples/platform/silabs/efr32/rs911x/rs911x.gni @@ -9,7 +9,7 @@ rs911x_src_plat = [ "${examples_plat_dir}/rs911x/hal/rsi_hal_mcu_ioports.c", "${examples_plat_dir}/rs911x/hal/rsi_hal_mcu_timer.c", "${examples_plat_dir}/rs911x/hal/efx_spi.c", - "${silabs_plat_efr32_wifi_dir}/wfx_notify.cpp", + "${silabs_common_plat_dir}/wifi/wfx_notify.cpp", ] # diff --git a/examples/platform/silabs/efr32/rs911x/rsi_if.c b/examples/platform/silabs/efr32/rs911x/rsi_if.c index e4d6f51bf0ded9..f0121285383d00 100644 --- a/examples/platform/silabs/efr32/rs911x/rsi_if.c +++ b/examples/platform/silabs/efr32/rs911x/rsi_if.c @@ -70,12 +70,6 @@ bool hasNotifiedIPV4 = false; #endif /* CHIP_DEVICE_CONFIG_ENABLE_IPV4 */ bool hasNotifiedWifiConnectivity = false; -/* Declare a flag to differentiate between after boot-up first IP connection or reconnection */ -bool is_wifi_disconnection_event = false; - -/* Declare a variable to hold connection time intervals */ -uint32_t retryInterval = WLAN_MIN_RETRY_TIMER_MS; - #if (RSI_BLE_ENABLE) extern rsi_semaphore_handle_t sl_rs_ble_init_sem; #endif @@ -279,13 +273,8 @@ static void wfx_rsi_join_cb(uint16_t status, const uint8_t * buf, const uint16_t /* * We should enable retry.. (Need config variable for this) */ - SILABS_LOG("%s: failed. retry: %d", __func__, wfx_rsi.join_retries); - wfx_retry_interval_handler(is_wifi_disconnection_event, wfx_rsi.join_retries++); - if (is_wifi_disconnection_event || wfx_rsi.join_retries <= WFX_RSI_CONFIG_MAX_JOIN) - { - WfxEvent.eventType = WFX_EVT_STA_START_JOIN; - WfxPostEvent(&WfxEvent); - } + SILABS_LOG("wfx_rsi_join_cb: failed. retry: %d", wfx_rsi.join_retries); + wfx_retry_connection(++wfx_rsi.join_retries); } else { @@ -293,11 +282,10 @@ static void wfx_rsi_join_cb(uint16_t status, const uint8_t * buf, const uint16_t * Join was complete - Do the DHCP */ memset(&temp_reset, 0, sizeof(wfx_wifi_scan_ext_t)); - SILABS_LOG("%s: join completed.", __func__); + SILABS_LOG("wfx_rsi_join_cb: join completed."); WfxEvent.eventType = WFX_EVT_STA_CONN; WfxPostEvent(&WfxEvent); wfx_rsi.join_retries = 0; - retryInterval = WLAN_MIN_RETRY_TIMER_MS; } } @@ -313,12 +301,11 @@ static void wfx_rsi_join_cb(uint16_t status, const uint8_t * buf, const uint16_t *********************************************************************/ static void wfx_rsi_join_fail_cb(uint16_t status, uint8_t * buf, uint32_t len) { - SILABS_LOG("%s: error: failed status: %02x", __func__, status); + SILABS_LOG("wfx_rsi_join_fail_cb: error: failed status: %02x", status); WfxEvent_t WfxEvent; wfx_rsi.join_retries += 1; wfx_rsi.dev_state &= ~(WFX_RSI_ST_STA_CONNECTING | WFX_RSI_ST_STA_CONNECTED); - is_wifi_disconnection_event = true; - WfxEvent.eventType = WFX_EVT_STA_START_JOIN; + WfxEvent.eventType = WFX_EVT_STA_START_JOIN; WfxPostEvent(&WfxEvent); } /************************************************************************************* @@ -354,23 +341,23 @@ static int32_t wfx_rsi_init(void) uint8_t buf[RSI_RESPONSE_HOLD_BUFF_SIZE]; extern void rsi_hal_board_init(void); - SILABS_LOG("%s: starting(HEAP_SZ = %d)", __func__, SL_HEAP_SIZE); + SILABS_LOG("wfx_rsi_init: starting(HEAP_SZ = %d)", SL_HEAP_SIZE); //! Driver initialization status = rsi_driver_init(wfx_rsi_drv_buf, WFX_RSI_BUF_SZ); if ((status < RSI_DRIVER_STATUS) || (status > WFX_RSI_BUF_SZ)) { - SILABS_LOG("%s: error: RSI Driver initialization failed with status: %02x", __func__, status); + SILABS_LOG("wfx_rsi_init: error: RSI Driver initialization failed with status: %02x", status); return status; } - SILABS_LOG("%s: rsi_device_init", __func__); + SILABS_LOG("wfx_rsi_init: rsi_device_init", __func__); /* ! Redpine module intialisation */ if ((status = rsi_device_init(LOAD_NWP_FW)) != RSI_SUCCESS) { - SILABS_LOG("%s: error: rsi_device_init failed with status: %02x", __func__, status); + SILABS_LOG("wfx_rsi_init: error: rsi_device_init failed with status: %02x", status); return status; } - SILABS_LOG("%s: start wireless drv task", __func__); + SILABS_LOG("wfx_rsi_init: start wireless drv task", __func__); /* * Create the driver task */ @@ -378,7 +365,7 @@ static int32_t wfx_rsi_init(void) WLAN_TASK_PRIORITY, driverRsiTaskStack, &driverRsiTaskBuffer); if (NULL == wfx_rsi.drv_task) { - SILABS_LOG("%s: error: rsi_wireless_driver_task failed", __func__); + SILABS_LOG("wfx_rsi_init: error: rsi_wireless_driver_task failed", __func__); return RSI_ERROR_INVALID_PARAM; } @@ -389,40 +376,40 @@ static int32_t wfx_rsi_init(void) if ((status = rsi_wireless_init(OPER_MODE_0, COEX_MODE_0)) != RSI_SUCCESS) { #endif - SILABS_LOG("%s: error: Initialize WiSeConnect failed with status: %02x", __func__, status); + SILABS_LOG("wfx_rsi_init: error: Initialize WiSeConnect failed with status: %02x", status); return status; } - SILABS_LOG("%s: get FW version..", __func__); + SILABS_LOG("wfx_rsi_init: get FW version..", __func__); /* * Get the MAC and other info to let the user know about it. */ if (rsi_wlan_get(RSI_FW_VERSION, buf, sizeof(buf)) != RSI_SUCCESS) { - SILABS_LOG("%s: error: rsi_wlan_get(RSI_FW_VERSION) failed with status: %02x", __func__, status); + SILABS_LOG("wfx_rsi_init: error: rsi_wlan_get(RSI_FW_VERSION) failed with status: %02x", status); return status; } buf[sizeof(buf) - 1] = 0; - SILABS_LOG("%s: RSI firmware version: %s", __func__, buf); + SILABS_LOG("wfx_rsi_init: RSI firmware version: %s", buf); //! Send feature frame if ((status = rsi_send_feature_frame()) != RSI_SUCCESS) { - SILABS_LOG("%s: error: rsi_send_feature_frame failed with status: %02x", __func__, status); + SILABS_LOG("wfx_rsi_init: error: rsi_send_feature_frame failed with status: %02x", status); return status; } - SILABS_LOG("%s: sent rsi_send_feature_frame", __func__); + SILABS_LOG("wfx_rsi_init: sent rsi_send_feature_frame", __func__); /* initializes wlan radio parameters and WLAN supplicant parameters. */ (void) rsi_wlan_radio_init(); /* Required so we can get MAC address */ if ((status = rsi_wlan_get(RSI_MAC_ADDRESS, &wfx_rsi.sta_mac.octet[0], RESP_BUFF_SIZE)) != RSI_SUCCESS) { - SILABS_LOG("%s: error: rsi_wlan_get failed with status: %02x", __func__, status); + SILABS_LOG("wfx_rsi_init: error: rsi_wlan_get failed with status: %02x", status); return status; } - SILABS_LOG("%s: WLAN: MAC %02x:%02x:%02x %02x:%02x:%02x", __func__, wfx_rsi.sta_mac.octet[0], wfx_rsi.sta_mac.octet[1], + SILABS_LOG("wfx_rsi_init: WLAN: MAC %02x:%02x:%02x %02x:%02x:%02x", wfx_rsi.sta_mac.octet[0], wfx_rsi.sta_mac.octet[1], wfx_rsi.sta_mac.octet[2], wfx_rsi.sta_mac.octet[3], wfx_rsi.sta_mac.octet[4], wfx_rsi.sta_mac.octet[5]); // Create the message queue @@ -445,12 +432,12 @@ static int32_t wfx_rsi_init(void) */ if ((status = rsi_wlan_register_callbacks(RSI_JOIN_FAIL_CB, wfx_rsi_join_fail_cb)) != RSI_SUCCESS) { - SILABS_LOG("%s: RSI callback register join failed with status: %02x", __func__, status); + SILABS_LOG("wfx_rsi_init: RSI callback register join failed with status: %02x", status); return status; } if ((status = rsi_wlan_register_callbacks(RSI_WLAN_DATA_RECEIVE_NOTIFY_CB, wfx_rsi_wlan_pkt_cb)) != RSI_SUCCESS) { - SILABS_LOG("%s: RSI callback register data-notify failed with status: %02x", __func__, status); + SILABS_LOG("wfx_rsi_init: RSI callback register data-notify failed with status: %02x", status); return status; } @@ -459,7 +446,7 @@ static int32_t wfx_rsi_init(void) #endif wfx_rsi.dev_state |= WFX_RSI_ST_DEV_READY; - SILABS_LOG("%s: RSI: OK", __func__); + SILABS_LOG("wfx_rsi_init: RSI: OK", __func__); return RSI_SUCCESS; } @@ -488,7 +475,7 @@ static void wfx_rsi_save_ap_info() // translation #else /* !WIFI_ENABLE_SECURITY_WPA3_TRANSITION */ wfx_rsi.sec.security = WFX_SEC_WPA2; #endif /* WIFI_ENABLE_SECURITY_WPA3_TRANSITION */ - SILABS_LOG("%s: warn: failed with status: %02x", __func__, status); + SILABS_LOG("wfx_rsi_save_ap_info: warn: failed with status: %02x", status); return; } wfx_rsi.sec.security = WFX_SEC_UNSPECIFIED; @@ -524,7 +511,8 @@ static void wfx_rsi_save_ap_info() // translation break; } - SILABS_LOG("%s: WLAN: connecting to %s, sec=%d, status=%02x", __func__, &wfx_rsi.sec.ssid[0], wfx_rsi.sec.security, status); + SILABS_LOG("wfx_rsi_save_ap_info: WLAN: connecting to %s, sec=%d, status=%02x", &wfx_rsi.sec.ssid[0], wfx_rsi.sec.security, + status); } /******************************************************************************************** @@ -541,7 +529,7 @@ static void wfx_rsi_do_join(void) if (wfx_rsi.dev_state & (WFX_RSI_ST_STA_CONNECTING | WFX_RSI_ST_STA_CONNECTED)) { - SILABS_LOG("%s: not joining - already in progress", __func__); + SILABS_LOG("wfx_rsi_do_join: not joining - already in progress"); } else { @@ -564,11 +552,11 @@ static void wfx_rsi_do_join(void) connect_security_mode = RSI_OPEN; break; default: - SILABS_LOG("%s: error: unknown security type.", __func__); + SILABS_LOG("wfx_rsi_do_join: error: unknown security type."); return; } - SILABS_LOG("%s: WLAN: connecting to %s, sec=%d", __func__, &wfx_rsi.sec.ssid[0], wfx_rsi.sec.security); + SILABS_LOG("wfx_rsi_do_join: WLAN: connecting to %s, sec=%d", &wfx_rsi.sec.ssid[0], wfx_rsi.sec.security); /* * Join the network @@ -580,33 +568,18 @@ static void wfx_rsi_do_join(void) if ((status = rsi_wlan_register_callbacks(RSI_JOIN_FAIL_CB, wfx_rsi_join_fail_cb)) != RSI_SUCCESS) { - SILABS_LOG("%s: RSI callback register join failed with status: %02x", __func__, status); + SILABS_LOG("wfx_rsi_do_join: RSI callback register join failed with status: %02x", status); } /* Try to connect Wifi with given Credentials * untill there is a success or maximum number of tries allowed */ - while (is_wifi_disconnection_event || wfx_rsi.join_retries <= WFX_RSI_CONFIG_MAX_JOIN) + if ((status = rsi_wlan_connect_async((int8_t *) &wfx_rsi.sec.ssid[0], connect_security_mode, &wfx_rsi.sec.passkey[0], + wfx_rsi_join_cb)) != RSI_SUCCESS) { - /* Call rsi connect call with given ssid and password - * And check there is a success - */ - if ((status = rsi_wlan_connect_async((int8_t *) &wfx_rsi.sec.ssid[0], connect_security_mode, &wfx_rsi.sec.passkey[0], - wfx_rsi_join_cb)) != RSI_SUCCESS) - { - - wfx_rsi.dev_state &= ~WFX_RSI_ST_STA_CONNECTING; - SILABS_LOG("%s: rsi_wlan_connect_async failed with status: %02x on try %d", __func__, status, wfx_rsi.join_retries); - - wfx_retry_interval_handler(is_wifi_disconnection_event, wfx_rsi.join_retries); - wfx_rsi.join_retries++; - } - else - { - SILABS_LOG("%s: starting JOIN to %s after %d tries\n", __func__, (char *) &wfx_rsi.sec.ssid[0], - wfx_rsi.join_retries); - break; // exit while loop - } + wfx_rsi.dev_state &= ~WFX_RSI_ST_STA_CONNECTING; + SILABS_LOG("wfx_rsi_do_join: rsi_wlan_connect_async failed with status: %02x on try %d", status, wfx_rsi.join_retries); + wfx_retry_connection(++wfx_rsi.join_retries); } } } @@ -701,7 +674,7 @@ void ProcessEvent(WfxEvent_t inEvent) switch (inEvent.eventType) { case WFX_EVT_STA_CONN: - SILABS_LOG("%s: starting LwIP STA", __func__); + SILABS_LOG("Starting LwIP STA"); wfx_rsi.dev_state |= WFX_RSI_ST_STA_CONNECTED; ResetDHCPNotificationFlags(); wfx_lwip_set_sta_link_up(); @@ -715,7 +688,7 @@ void ProcessEvent(WfxEvent_t inEvent) // TODO: This event is not being posted anywhere, seems to be a dead code or we are missing something wfx_rsi.dev_state &= ~(WFX_RSI_ST_STA_READY | WFX_RSI_ST_STA_CONNECTING | WFX_RSI_ST_STA_CONNECTED | WFX_RSI_ST_STA_DHCP_DONE); - SILABS_LOG("%s: disconnect notify", __func__); + SILABS_LOG("Disconnect notify"); /* TODO: Implement disconnect notify */ ResetDHCPNotificationFlags(); wfx_lwip_set_sta_link_down(); // Internally dhcpclient_poll(netif) -> @@ -813,7 +786,7 @@ void wfx_rsi_task(void * arg) uint32_t rsi_status = wfx_rsi_init(); if (rsi_status != RSI_SUCCESS) { - SILABS_LOG("%s: error: wfx_rsi_init with status: %02x", __func__, rsi_status); + SILABS_LOG("wfx_rsi_task: error: wfx_rsi_init with status: %02x", rsi_status); return; } WfxEvent_t wfxEvent; @@ -853,7 +826,7 @@ void wfx_dhcp_got_ipv4(uint32_t ip) wfx_rsi.ip4_addr[1] = (ip >> 8) & HEX_VALUE_FF; wfx_rsi.ip4_addr[2] = (ip >> 16) & HEX_VALUE_FF; wfx_rsi.ip4_addr[3] = (ip >> 24) & HEX_VALUE_FF; - SILABS_LOG("%s: DHCP OK: IP=%d.%d.%d.%d", __func__, wfx_rsi.ip4_addr[0], wfx_rsi.ip4_addr[1], wfx_rsi.ip4_addr[2], + SILABS_LOG("wfx_dhcp_got_ipv4: DHCP OK: IP=%d.%d.%d.%d", wfx_rsi.ip4_addr[0], wfx_rsi.ip4_addr[1], wfx_rsi.ip4_addr[2], wfx_rsi.ip4_addr[3]); /* Notify the Connectivity Manager - via the app */ wfx_rsi.dev_state |= WFX_RSI_ST_STA_DHCP_DONE; diff --git a/examples/platform/silabs/efr32/wf200/host_if.cpp b/examples/platform/silabs/efr32/wf200/host_if.cpp index ac3ad07773d9d3..44e65e2728fa4b 100644 --- a/examples/platform/silabs/efr32/wf200/host_if.cpp +++ b/examples/platform/silabs/efr32/wf200/host_if.cpp @@ -99,12 +99,6 @@ bool hasNotifiedWifiConnectivity = false; static uint8_t retryJoin = 0; bool retryInProgress = false; -/* Declare a flag to differentiate between after boot-up first IP connection or reconnection */ -bool is_wifi_disconnection_event = false; - -/* Declare a variable to hold connection time intervals */ -uint32_t retryInterval = WLAN_MIN_RETRY_TIMER_MS; - #ifdef SL_WFX_CONFIG_SCAN static struct scan_result_holder { @@ -401,14 +395,14 @@ static void sl_wfx_connect_callback(sl_wfx_connect_ind_body_t connect_indication } } - if ((status != WFM_STATUS_SUCCESS) && (!is_wifi_disconnection_event ? (retryJoin < MAX_JOIN_RETRIES_COUNT) : true)) + if (status != WFM_STATUS_SUCCESS) { retryJoin += 1; retryInProgress = false; SILABS_LOG("WFX Retry to connect to network count: %d", retryJoin); sl_wfx_context->state = static_cast(static_cast(sl_wfx_context->state) & ~static_cast(SL_WFX_STARTED)); - xEventGroupSetBits(sl_wfx_event_group, SL_WFX_RETRY_CONNECT); + wfx_retry_connection(retryJoin); } } @@ -424,9 +418,8 @@ static void sl_wfx_disconnect_callback(uint8_t * mac, uint16_t reason) SILABS_LOG("WFX Disconnected %d\r\n", reason); sl_wfx_context->state = static_cast(static_cast(sl_wfx_context->state) & ~static_cast(SL_WFX_STA_INTERFACE_CONNECTED)); - retryInProgress = false; - is_wifi_disconnection_event = true; - xEventGroupSetBits(sl_wfx_event_group, SL_WFX_RETRY_CONNECT); + retryInProgress = false; + wfx_retry_connection(retryJoin); } #ifdef SL_WFX_CONFIG_SOFTAP @@ -541,13 +534,8 @@ static void wfx_events_task(void * p_arg) pdTRUE, pdFALSE, pdMS_TO_TICKS(250)); /* 250 msec delay converted to ticks */ if (flags & SL_WFX_RETRY_CONNECT) { - if (!retryInProgress) - { - retryInProgress = true; - wfx_retry_interval_handler(is_wifi_disconnection_event, retryJoin); - SILABS_LOG("WFX sending the connect command"); - wfx_connect_to_ap(); - } + SILABS_LOG("WFX sending the connect command"); + wfx_connect_to_ap(); } if (wifi_extra & WE_ST_STA_CONN) @@ -599,8 +587,7 @@ static void wfx_events_task(void * p_arg) hasNotifiedWifiConnectivity = false; SILABS_LOG("WIFI: Connected to AP"); wifi_extra |= WE_ST_STA_CONN; - retryJoin = 0; - retryInterval = WLAN_MIN_RETRY_TIMER_MS; + retryJoin = 0; wfx_lwip_set_sta_link_up(); #if CHIP_CONFIG_ENABLE_ICD_SERVER if (!(wfx_get_wifi_state() & SL_WFX_AP_INTERFACE_UP)) @@ -750,6 +737,7 @@ static void wfx_wifi_hw_start(void) /* Initialize the LwIP stack */ SILABS_LOG("WF200:Start LWIP"); wfx_lwip_start(); + wfx_started_notify(); wifiContext.state = SL_WFX_STARTED; /* Really this is a bit mask */ SILABS_LOG("WF200:ready.."); } diff --git a/examples/platform/silabs/efr32/wf200/wf200.gni b/examples/platform/silabs/efr32/wf200/wf200.gni index 7e3b4ae6f18d75..307b6815374c38 100644 --- a/examples/platform/silabs/efr32/wf200/wf200.gni +++ b/examples/platform/silabs/efr32/wf200/wf200.gni @@ -4,7 +4,7 @@ import("${efr32_sdk_build_root}/efr32_sdk.gni") wf200_plat_incs = [ "${examples_plat_dir}/wf200" ] wf200_plat_src = [ - "${silabs_plat_efr32_wifi_dir}/wfx_notify.cpp", + "${silabs_common_plat_dir}/wifi/wfx_notify.cpp", "${examples_plat_dir}/wf200/sl_wfx_task.c", "${examples_plat_dir}/wf200/wf200_init.c", "${examples_plat_dir}/wf200/efr_spi.c", diff --git a/examples/platform/silabs/wfx_rsi.h b/examples/platform/silabs/wfx_rsi.h index c559e1e7610880..502dd1a96e772d 100644 --- a/examples/platform/silabs/wfx_rsi.h +++ b/examples/platform/silabs/wfx_rsi.h @@ -31,7 +31,6 @@ #define WFX_RSI_WLAN_TASK_SZ (1024 + 512 + 256) /* Stack for the WLAN task */ #define WFX_RSI_TASK_SZ (1024 + 1024) /* Stack for the WFX/RSI task */ #define WFX_RSI_BUF_SZ (1024 * 10) /* May need tweak */ -#define WFX_RSI_CONFIG_MAX_JOIN (5) /* Max join retries */ // TODO: Default values are usually in minutes, but this is in ms. Confirm if this is correct #define WFX_RSI_DHCP_POLL_INTERVAL (250) /* Poll interval in ms for DHCP */ #define WFX_RSI_NUM_TIMERS (2) /* Number of RSI timers to alloc */ diff --git a/src/platform/silabs/SiWx917/wifi/wfx_notify.cpp b/examples/platform/silabs/wifi/wfx_notify.cpp similarity index 64% rename from src/platform/silabs/SiWx917/wifi/wfx_notify.cpp rename to examples/platform/silabs/wifi/wfx_notify.cpp index ded8e8389d256d..a37b102c334804 100644 --- a/src/platform/silabs/SiWx917/wifi/wfx_notify.cpp +++ b/examples/platform/silabs/wifi/wfx_notify.cpp @@ -15,6 +15,9 @@ * limitations under the License. */ +#include "AppConfig.h" +#include "BaseApplication.h" +#include #include #include #include @@ -29,32 +32,33 @@ #include "wfx_rsi.h" #endif -#if SL_ICD_ENABLED -#ifdef __cplusplus -extern "C" { -#endif - -#ifdef __cplusplus -} -#endif -#endif // SL_ICD_ENABLED - #include -// #include -#include -#include using namespace ::chip; using namespace ::chip::DeviceLayer; -#include - -extern uint32_t retryInterval; +namespace { +constexpr uint8_t kWlanMinRetryIntervalsInSec = 1; +constexpr uint8_t kWlanMaxRetryIntervalsInSec = 60; +constexpr uint8_t kWlanRetryIntervalInSec = 5; +uint8_t retryInterval = kWlanMinRetryIntervalsInSec; +osTimerId_t sRetryTimer; +} // namespace /* * Notifications to the upper-layer * All done in the context of the RSI/WiFi task (rsi_if.c) */ +static void RetryConnectionTimerHandler(void * arg) +{ +#if CHIP_CONFIG_ENABLE_ICD_SERVER && SLI_SI91X_MCU_INTERFACE + wfx_rsi_power_save(RSI_ACTIVE, HIGH_PERFORMANCE); +#endif // CHIP_CONFIG_ENABLE_ICD_SERVER && SLI_SI91X_MCU_INTERFACE + if (wfx_connect_to_ap() != SL_STATUS_OK) + { + ChipLogError(DeviceLayer, "wfx_connect_to_ap() failed."); + } +} /*********************************************************************************** * @fn wfx_started_notify() * @brief @@ -67,6 +71,10 @@ void wfx_started_notify() sl_wfx_startup_ind_t evt; sl_wfx_mac_address_t mac; + // Creating a timer which will be used to retry connection with AP + sRetryTimer = osTimerNew(RetryConnectionTimerHandler, osTimerOnce, NULL, NULL); + VerifyOrReturn(sRetryTimer != NULL); + memset(&evt, 0, sizeof(evt)); evt.header.id = SL_WFX_STARTUP_IND_ID; evt.header.length = sizeof evt; @@ -90,13 +98,7 @@ void wfx_connected_notify(int32_t status, sl_wfx_mac_address_t * ap) { sl_wfx_connect_ind_t evt; - if (status != SUCCESS_STATUS) - { - ChipLogProgress(DeviceLayer, "%s: error: failed status: %ld.", __func__, status); - return; - } - - ChipLogProgress(DeviceLayer, "%s: connected.", __func__); + VerifyOrReturn(status != SUCCESS_STATUS); memset(&evt, 0, sizeof(evt)); evt.header.id = SL_WFX_CONNECT_IND_ID; @@ -143,16 +145,6 @@ void wfx_ipv6_notify(int got_ip) eventData.header.id = got_ip ? IP_EVENT_GOT_IP6 : IP_EVENT_STA_LOST_IP; eventData.header.length = sizeof(eventData.header); PlatformMgrImpl().HandleWFXSystemEvent(IP_EVENT, &eventData); - - /* So the other threads can run and have the connectivity OK */ - if (got_ip) - { - /* Should remember this */ - vTaskDelay(1); - chip::DeviceLayer::PlatformMgr().LockChipStack(); - chip::app::DnssdServer::Instance().StartServer(/*Dnssd::CommissioningMode::kEnabledBasic*/); - chip::DeviceLayer::PlatformMgr().UnlockChipStack(); - } } /************************************************************************************** @@ -170,67 +162,67 @@ void wfx_ip_changed_notify(int got_ip) eventData.header.id = got_ip ? IP_EVENT_STA_GOT_IP : IP_EVENT_STA_LOST_IP; eventData.header.length = sizeof(eventData.header); PlatformMgrImpl().HandleWFXSystemEvent(IP_EVENT, &eventData); - - /* So the other threads can run and have the connectivity OK */ - if (got_ip) - { - /* Should remember this */ - vTaskDelay(1); - chip::DeviceLayer::PlatformMgr().LockChipStack(); - chip::app::DnssdServer::Instance().StartServer(/*Dnssd::CommissioningMode::kEnabledBasic*/); - chip::DeviceLayer::PlatformMgr().UnlockChipStack(); - } } /************************************************************************************** - * @fn void wfx_retry_interval_handler(bool is_wifi_disconnection_event, uint16_t retryJoin) + * @fn void wfx_retry_connection(uint16_t retryAttempt) * @brief - * Based on condition will delay for a certain period of time. - * @param[in] is_wifi_disconnection_event, retryJoin + * During commissioning, we retry to join the network MAX_JOIN_RETRIES_COUNT times. + * If DUT is disconnected from the AP or device is power cycled, then retry connection + * with AP continously after a certain time interval. + * @param[in] retryAttempt * @return None ********************************************************************************************/ -void wfx_retry_interval_handler(bool is_wifi_disconnection_event, uint16_t retryJoin) +void wfx_retry_connection(uint16_t retryAttempt) { - if (!is_wifi_disconnection_event) + // During commissioning, we retry to join the network MAX_JOIN_RETRIES_COUNT + if (BaseApplication::sAppDelegate.isCommissioningInProgress()) { - /* After the reboot or a commissioning time device failed to connect with AP. - * Device will retry to connect with AP upto WFX_RSI_CONFIG_MAX_JOIN retries. - */ - if (retryJoin < MAX_JOIN_RETRIES_COUNT) + if (retryAttempt < MAX_JOIN_RETRIES_COUNT) { - ChipLogProgress(DeviceLayer, "wfx_retry_interval_handler : Next attempt after %d Seconds", - CONVERT_MS_TO_SEC(WLAN_RETRY_TIMER_MS)); -#if SL_ICD_ENABLED - // TODO: cleanup the retry logic MATTER-1921 - if (!chip::Server::GetInstance().GetCommissioningWindowManager().IsCommissioningWindowOpen()) + ChipLogProgress(DeviceLayer, "wfx_retry_connection : Next attempt after %d Seconds", kWlanRetryIntervalInSec); + if (osTimerStart(sRetryTimer, pdMS_TO_TICKS(CONVERT_SEC_TO_MS(kWlanRetryIntervalInSec))) != osOK) { - wfx_rsi_power_save(RSI_SLEEP_MODE_8, STANDBY_POWER_SAVE_WITH_RAM_RETENTION); + ChipLogProgress(DeviceLayer, "Failed to start retry timer"); + // Sending the join command if retry timer failed to start + if (wfx_connect_to_ap() != SL_STATUS_OK) + { + ChipLogError(DeviceLayer, "wfx_connect_to_ap() failed."); + } + return; } -#endif // SL_ICD_ENABLED - vTaskDelay(pdMS_TO_TICKS(WLAN_RETRY_TIMER_MS)); } else { - ChipLogProgress(DeviceLayer, "Connect failed after max %d tries", retryJoin); + ChipLogProgress(DeviceLayer, "Connect failed after max %d tries", retryAttempt); } } else { - /* After disconnection + /* After disconnection or power cycle the DUT * At the telescopic time interval device try to reconnect with AP, upto WLAN_MAX_RETRY_TIMER_MS intervals * are telescopic. If interval exceed WLAN_MAX_RETRY_TIMER_MS then it will try to reconnect at * WLAN_MAX_RETRY_TIMER_MS intervals. */ - if (retryInterval > WLAN_MAX_RETRY_TIMER_MS) + if (retryInterval > kWlanMaxRetryIntervalsInSec) + { + retryInterval = kWlanMaxRetryIntervalsInSec; + } + if (osTimerStart(sRetryTimer, pdMS_TO_TICKS(CONVERT_SEC_TO_MS(retryInterval))) != osOK) { - retryInterval = WLAN_MAX_RETRY_TIMER_MS; + ChipLogProgress(DeviceLayer, "Failed to start retry timer"); + // Sending the join command if retry timer failed to start + if (wfx_connect_to_ap() != SL_STATUS_OK) + { + ChipLogError(DeviceLayer, "wfx_connect_to_ap() failed."); + } + return; } - ChipLogProgress(DeviceLayer, "wfx_retry_interval_handler : Next attempt after %ld Seconds", - CONVERT_MS_TO_SEC(retryInterval)); -#if SL_ICD_ENABLED +#if CHIP_CONFIG_ENABLE_ICD_SERVER && SLI_SI91X_MCU_INTERFACE wfx_rsi_power_save(RSI_SLEEP_MODE_8, STANDBY_POWER_SAVE_WITH_RAM_RETENTION); -#endif // SL_ICD_ENABLED - vTaskDelay(pdMS_TO_TICKS(retryInterval)); +#endif // CHIP_CONFIG_ENABLE_ICD_SERVER && SLI_SI91X_MCU_INTERFACE + ChipLogProgress(DeviceLayer, "wfx_retry_connection : Next attempt after %d Seconds", retryInterval); retryInterval += retryInterval; + return; } } diff --git a/src/platform/silabs/SiWx917/wifi/wfx_host_events.h b/src/platform/silabs/SiWx917/wifi/wfx_host_events.h index beee667f58f261..319a1c508e658d 100644 --- a/src/platform/silabs/SiWx917/wifi/wfx_host_events.h +++ b/src/platform/silabs/SiWx917/wifi/wfx_host_events.h @@ -60,11 +60,7 @@ #define BLE_DRIVER_TASK_PRIORITY (2) #define MAX_JOIN_RETRIES_COUNT (5) -// WLAN retry time intervals in milli seconds -#define WLAN_MAX_RETRY_TIMER_MS 30000 -#define WLAN_MIN_RETRY_TIMER_MS 1000 -#define WLAN_RETRY_TIMER_MS 5000 -#define CONVERT_MS_TO_SEC(TimeInMS) (TimeInMS / 1000) +#define CONVERT_SEC_TO_MS(TimeInS) (TimeInS * 1000) // WLAN related Macros #define ETH_FRAME (0) @@ -255,7 +251,7 @@ void sl_button_on_change(uint8_t btn, uint8_t btnAction); #endif /* SL_ICD_ENABLED */ void wfx_ipv6_notify(int got_ip); -void wfx_retry_interval_handler(bool is_wifi_disconnection_event, uint16_t retryJoin); +void wfx_retry_connection(uint16_t retryAttempt); #ifdef __cplusplus } diff --git a/src/platform/silabs/efr32/wifi/wfx_host_events.h b/src/platform/silabs/efr32/wifi/wfx_host_events.h index 24e5b6ea707a7a..51f96d7b9dbc73 100644 --- a/src/platform/silabs/efr32/wifi/wfx_host_events.h +++ b/src/platform/silabs/efr32/wifi/wfx_host_events.h @@ -139,12 +139,10 @@ typedef struct __attribute__((__packed__)) sl_wfx_mib_req_s #define WLAN_TASK_PRIORITY 1 #define WLAN_DRIVER_TASK_PRIORITY 1 #define BLE_DRIVER_TASK_PRIORITY 1 -#define MAX_JOIN_RETRIES_COUNT 5 #else /* WF200 */ #define WLAN_TASK_STACK_SIZE 1024 #define WLAN_TASK_PRIORITY 1 -#define MAX_JOIN_RETRIES_COUNT 5 #endif // RS911X_WIFI // MAX SSID LENGTH excluding NULL character @@ -152,11 +150,10 @@ typedef struct __attribute__((__packed__)) sl_wfx_mib_req_s // MAX PASSKEY LENGTH including NULL character #define WFX_MAX_PASSKEY_LENGTH (64) -// WLAN retry time intervals in milli seconds -#define WLAN_MAX_RETRY_TIMER_MS 30000 -#define WLAN_MIN_RETRY_TIMER_MS 1000 -#define WLAN_RETRY_TIMER_MS 5000 -#define CONVERT_MS_TO_SEC(TimeInMS) (TimeInMS / 1000) +#define CONVERT_SEC_TO_MS(TimeInS) (TimeInS * 1000) + +// WLAN MAX retry +#define MAX_JOIN_RETRIES_COUNT 5 // WLAN related Macros #define ETH_FRAME 0 @@ -388,7 +385,7 @@ void sl_wfx_host_gpio_init(void); sl_status_t sl_wfx_host_process_event(sl_wfx_generic_message_t * event_payload); #endif -void wfx_retry_interval_handler(bool is_wifi_disconnection_event, uint16_t retryJoin); +void wfx_retry_connection(uint16_t retryAttempt); #ifdef __cplusplus } diff --git a/src/platform/silabs/efr32/wifi/wfx_notify.cpp b/src/platform/silabs/efr32/wifi/wfx_notify.cpp deleted file mode 100644 index fd183100e7b913..00000000000000 --- a/src/platform/silabs/efr32/wifi/wfx_notify.cpp +++ /dev/null @@ -1,220 +0,0 @@ -/* - * - * Copyright (c) 2022 Project CHIP Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include -#include - -#include "em_bus.h" -#include "em_cmu.h" -#include "em_gpio.h" -#include "em_ldma.h" -#include "em_usart.h" -#include "gpiointerrupt.h" - -#include "FreeRTOS.h" -#include "event_groups.h" -#include "task.h" - -#include "wfx_host_events.h" - -#ifdef RS911X_WIFI -#include "wfx_rsi.h" -#endif - -#include -#include -#include -#include - -using namespace ::chip; -using namespace ::chip::DeviceLayer; - -extern uint32_t retryInterval; - -/* - * Notifications to the upper-layer - * All done in the context of the RSI/WiFi task (rsi_if.c) - */ - -/*********************************************************************************** - * @fn wfx_started_notify() - * @brief - * Wifi device started notification - * @param[in]: None - * @return None - *************************************************************************************/ -void wfx_started_notify() -{ - sl_wfx_startup_ind_t evt; - sl_wfx_mac_address_t mac; - - memset(&evt, 0, sizeof(evt)); - evt.header.id = SL_WFX_STARTUP_IND_ID; - evt.header.length = sizeof evt; - evt.body.status = 0; - wfx_get_wifi_mac_addr(SL_WFX_STA_INTERFACE, &mac); - memcpy(&evt.body.mac_addr[0], &mac.octet[0], MAC_ADDRESS_FIRST_OCTET); - - PlatformMgrImpl().HandleWFXSystemEvent(WIFI_EVENT, (sl_wfx_generic_message_t *) &evt); -} - -/*********************************************************************************** - * @fn void wfx_connected_notify(int32_t status, sl_wfx_mac_address_t *ap) - * @brief - * For now we are not notifying anything other than AP Mac - - * Other stuff such as DTIM etc. may be required for later - * @param[in] status: - * @param[in] ap: access point - * @return None - *************************************************************************************/ -void wfx_connected_notify(int32_t status, sl_wfx_mac_address_t * ap) -{ - sl_wfx_connect_ind_t evt; - - if (status != SUCCESS_STATUS) - { - ChipLogProgress(DeviceLayer, "%s: error: failed status: %ld.", __func__, status); - return; - } - - ChipLogProgress(DeviceLayer, "%s: connected.", __func__); - - memset(&evt, 0, sizeof(evt)); - evt.header.id = SL_WFX_CONNECT_IND_ID; - evt.header.length = sizeof evt; - -#ifdef RS911X_WIFI - evt.body.channel = wfx_rsi.ap_chan; -#endif - memcpy(&evt.body.mac[0], &ap->octet[0], MAC_ADDRESS_FIRST_OCTET); - - PlatformMgrImpl().HandleWFXSystemEvent(WIFI_EVENT, (sl_wfx_generic_message_t *) &evt); -} - -/************************************************************************************** - * @fn void wfx_disconnected_notify(int32_t status) - * @brief - * notification of disconnection - * @param[in] status: - * @return None - ********************************************************************************************/ -void wfx_disconnected_notify(int32_t status) -{ - sl_wfx_disconnect_ind_t evt; - - memset(&evt, 0, sizeof(evt)); - evt.header.id = SL_WFX_DISCONNECT_IND_ID; - evt.header.length = sizeof evt; - evt.body.reason = status; - PlatformMgrImpl().HandleWFXSystemEvent(WIFI_EVENT, (sl_wfx_generic_message_t *) &evt); -} - -/************************************************************************************** - * @fn void wfx_ipv6_notify(int got_ip) - * @brief - * notification of ipv6 - * @param[in] got_ip: - * @return None - ********************************************************************************************/ -void wfx_ipv6_notify(int got_ip) -{ - sl_wfx_generic_message_t eventData; - - memset(&eventData, 0, sizeof(eventData)); - eventData.header.id = got_ip ? IP_EVENT_GOT_IP6 : IP_EVENT_STA_LOST_IP; - eventData.header.length = sizeof(eventData.header); - PlatformMgrImpl().HandleWFXSystemEvent(IP_EVENT, &eventData); - - /* So the other threads can run and have the connectivity OK */ - if (got_ip) - { - /* Should remember this */ - vTaskDelay(1); - chip::DeviceLayer::PlatformMgr().LockChipStack(); - chip::app::DnssdServer::Instance().StartServer(/*Dnssd::CommissioningMode::kEnabledBasic*/); - chip::DeviceLayer::PlatformMgr().UnlockChipStack(); - } -} - -/************************************************************************************** - * @fn void wfx_ip_changed_notify(int got_ip) - * @brief - * notification of ip change - * @param[in] got_ip: - * @return None - ********************************************************************************************/ -void wfx_ip_changed_notify(int got_ip) -{ - sl_wfx_generic_message_t eventData; - - memset(&eventData, 0, sizeof(eventData)); - eventData.header.id = got_ip ? IP_EVENT_STA_GOT_IP : IP_EVENT_STA_LOST_IP; - eventData.header.length = sizeof(eventData.header); - PlatformMgrImpl().HandleWFXSystemEvent(IP_EVENT, &eventData); - - /* So the other threads can run and have the connectivity OK */ - if (got_ip) - { - /* Should remember this */ - vTaskDelay(1); - chip::DeviceLayer::PlatformMgr().LockChipStack(); - chip::app::DnssdServer::Instance().StartServer(/*Dnssd::CommissioningMode::kEnabledBasic*/); - chip::DeviceLayer::PlatformMgr().UnlockChipStack(); - } -} - -/************************************************************************************** - * @fn void wfx_retry_interval_handler(bool is_wifi_disconnection_event, uint16_t retryJoin) - * @brief - * Based on condition will delay for a certain period of time. - * @param[in] is_wifi_disconnection_event, retryJoin - * @return None - ********************************************************************************************/ -void wfx_retry_interval_handler(bool is_wifi_disconnection_event, uint16_t retryJoin) -{ - if (!is_wifi_disconnection_event) - { - /* After the reboot or a commissioning time device failed to connect with AP. - * Device will retry to connect with AP upto WFX_RSI_CONFIG_MAX_JOIN retries. - */ - if (retryJoin < MAX_JOIN_RETRIES_COUNT) - { - ChipLogProgress(DeviceLayer, "%s: Next attempt after %d Seconds", __func__, CONVERT_MS_TO_SEC(WLAN_RETRY_TIMER_MS)); - vTaskDelay(pdMS_TO_TICKS(WLAN_RETRY_TIMER_MS)); - } - else - { - ChipLogProgress(DeviceLayer, "Connect failed after max %d tries", retryJoin); - } - } - else - { - /* After disconnection - * At the telescopic time interval device try to reconnect with AP, upto WLAN_MAX_RETRY_TIMER_MS intervals - * are telescopic. If interval exceed WLAN_MAX_RETRY_TIMER_MS then it will try to reconnect at - * WLAN_MAX_RETRY_TIMER_MS intervals. - */ - if (retryInterval > WLAN_MAX_RETRY_TIMER_MS) - { - retryInterval = WLAN_MAX_RETRY_TIMER_MS; - } - ChipLogProgress(DeviceLayer, "%s: Next attempt after %ld Seconds", __func__, CONVERT_MS_TO_SEC(retryInterval)); - vTaskDelay(pdMS_TO_TICKS(retryInterval)); - retryInterval += retryInterval; - } -} diff --git a/third_party/silabs/efr32_sdk.gni b/third_party/silabs/efr32_sdk.gni index 617dfd10facb95..ce7b09e3e012ac 100644 --- a/third_party/silabs/efr32_sdk.gni +++ b/third_party/silabs/efr32_sdk.gni @@ -89,6 +89,8 @@ declare_args() { examples_plat_dir = "${chip_root}/examples/platform/silabs/efr32" silabs_plat_efr32_wifi_dir = "${chip_root}/src/platform/silabs/efr32/wifi" +silabs_common_plat_dir = "${chip_root}/examples/platform/silabs" + is_series_2 = silabs_family == "mgm24" || silabs_family == "efr32mg24" || silabs_family == "efr32mg26" From be0009e75879cb61e57ad09e7d2e67677d52f679 Mon Sep 17 00:00:00 2001 From: waqar rashid <6962179+wickywaka@users.noreply.github.com> Date: Sat, 24 Aug 2024 01:33:21 +0200 Subject: [PATCH 18/32] Fixed missing trailing zero in example documentation (#35149) Co-authored-by: Boris Zbarsky --- docs/getting_started/first_example.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/getting_started/first_example.md b/docs/getting_started/first_example.md index 4aba0d4e480204..fefa1916c2ef83 100644 --- a/docs/getting_started/first_example.md +++ b/docs/getting_started/first_example.md @@ -92,7 +92,7 @@ codes, discriminator and passcode from the logs. Open a new terminal to use chip tool. Commission the device using: -`./out/linux-x64-chip-tool/chip-tool pairing code 0x12344321 MT:-24J0AFN00KA0648G0` +`./out/linux-x64-chip-tool/chip-tool pairing code 0x12344321 MT:-24J0AFN00KA0648G00` NOTE: pairing is the old name for commissioning. 0x12344321 is the node ID you want to assign to the node. 0x12344321 is the default for testing. From a93f4b6a98535cd03905a06032ad362f20c48faf Mon Sep 17 00:00:00 2001 From: chirag-silabs <100861685+chirag-silabs@users.noreply.github.com> Date: Sat, 24 Aug 2024 06:45:25 +0530 Subject: [PATCH 19/32] [Silabs] Fixing the build for the custom boards on NCP (#35055) * fixing the build for the custom boards on ncp * Restyled by clang-format * applying the restyler * addressing the review comments and adding sl_custom_board.h file * adding the warning message for 917 NCP * Restyled by whitespace * Restyled by clang-format * Apply suggestions from code review Co-authored-by: Yufeng Wang * Restyled by clang-format --------- Co-authored-by: Restyled.io Co-authored-by: Junior Martinez <67972863+jmartinez-silabs@users.noreply.github.com> Co-authored-by: Yufeng Wang --- .../silabs/efr32/rs911x/hal/efx32_ncp_host.c | 3 + .../silabs/efr32/rs911x/hal/efx_spi.c | 19 ++--- .../rs911x/hal/rsi_board_configuration.h | 3 +- .../efr32/rs911x/hal/sl_board_configuration.h | 3 +- .../efr32/rs911x/hal/sl_si91x_ncp_utility.c | 4 +- .../platform/silabs/efr32/sl_custom_board.h | 71 +++++++++++++++++++ .../platform/silabs/efr32/wf200/efr_spi.c | 5 -- .../silabs/efr32/wf200/sl_wfx_board.h | 3 +- 8 files changed, 88 insertions(+), 23 deletions(-) create mode 100644 examples/platform/silabs/efr32/sl_custom_board.h diff --git a/examples/platform/silabs/efr32/rs911x/hal/efx32_ncp_host.c b/examples/platform/silabs/efr32/rs911x/hal/efx32_ncp_host.c index fded8defa30dce..0a2fad7d0deb6c 100644 --- a/examples/platform/silabs/efr32/rs911x/hal/efx32_ncp_host.c +++ b/examples/platform/silabs/efr32/rs911x/hal/efx32_ncp_host.c @@ -36,7 +36,10 @@ #include "sl_power_manager.h" #endif +#ifdef SL_BOARD_NAME #include "sl_board_control.h" +#endif // SL_BOARD_NAME + #include "sl_si91x_ncp_utility.h" #include "spi_multiplex.h" diff --git a/examples/platform/silabs/efr32/rs911x/hal/efx_spi.c b/examples/platform/silabs/efr32/rs911x/hal/efx_spi.c index 6c03b6f1f4835b..1f10640e04bad8 100644 --- a/examples/platform/silabs/efr32/rs911x/hal/efx_spi.c +++ b/examples/platform/silabs/efr32/rs911x/hal/efx_spi.c @@ -35,7 +35,6 @@ #include "em_gpio.h" #include "em_ldma.h" #include "gpiointerrupt.h" -#include "sl_board_control.h" #include "sl_device_init_clocks.h" #include "sl_device_init_hfxo.h" #include "sl_spidrv_instances.h" @@ -47,6 +46,10 @@ #include "wfx_host_events.h" #include "wfx_rsi.h" +#ifdef SL_BOARD_NAME +#include "sl_board_control.h" +#endif // SL_BOARD_NAME + #if defined(SL_CATALOG_POWER_MANAGER_PRESENT) #include "sl_power_manager.h" #endif @@ -65,18 +68,12 @@ #include "sl_mx25_flash_shutdown_usart_config.h" #endif // SL_MX25CTRL_MUX -#if defined(EFR32MG24) #include "em_eusart.h" #include "sl_spidrv_eusart_exp_config.h" #include "spi_multiplex.h" -#else -#error "Unknown platform" -#endif -#if defined(EFR32MG24) #define SL_SPIDRV_HANDLE sl_spidrv_eusart_exp_handle #define SL_SPIDRV_EXP_BITRATE_MULTIPLEXED SL_SPIDRV_EUSART_EXP_BITRATE -#endif #define CONCAT(A, B) (A##B) #define SPI_CLOCK(N) CONCAT(cmuClock_USART, N) @@ -113,10 +110,8 @@ void sl_wfx_host_gpio_init(void) // Enable GPIO clock. CMU_ClockEnable(cmuClock_GPIO, true); -#if defined(EFR32MG24) // Set CS pin to high/inactive GPIO_PinModeSet(SL_SPIDRV_EUSART_EXP_CS_PORT, SL_SPIDRV_EUSART_EXP_CS_PIN, gpioModePushPull, PINOUT_SET); -#endif // EFR32MG24 GPIO_PinModeSet(WFX_RESET_PIN.port, WFX_RESET_PIN.pin, gpioModePushPull, PINOUT_SET); GPIO_PinModeSet(WFX_SLEEP_CONFIRM_PIN.port, WFX_SLEEP_CONFIRM_PIN.pin, gpioModePushPull, PINOUT_CLEAR); @@ -195,9 +190,7 @@ sl_status_t sl_wfx_host_spi_cs_assert(void) if (!spi_enabled) // Reduce sl_spidrv_init_instances { sl_spidrv_init_instances(); -#if defined(EFR32MG24) GPIO_PinOutClear(SL_SPIDRV_EUSART_EXP_CS_PORT, SL_SPIDRV_EUSART_EXP_CS_PIN); -#endif // EFR32MG24 spi_enabled = true; } return SL_STATUS_OK; @@ -211,11 +204,9 @@ sl_status_t sl_wfx_host_spi_cs_deassert(void) status = SPIDRV_DeInit(SL_SPIDRV_HANDLE); if (SL_STATUS_OK == status) { -#if defined(EFR32MG24) GPIO_PinOutSet(SL_SPIDRV_EUSART_EXP_CS_PORT, SL_SPIDRV_EUSART_EXP_CS_PIN); GPIO->EUSARTROUTE[SL_SPIDRV_EUSART_EXP_PERIPHERAL_NO].ROUTEEN = PINOUT_CLEAR; -#endif // EFR32MG24 - spi_enabled = false; + spi_enabled = false; } } #if SL_SPICTRL_MUX diff --git a/examples/platform/silabs/efr32/rs911x/hal/rsi_board_configuration.h b/examples/platform/silabs/efr32/rs911x/hal/rsi_board_configuration.h index 4d8d9da1dd36a9..ed04acf5b3fa40 100644 --- a/examples/platform/silabs/efr32/rs911x/hal/rsi_board_configuration.h +++ b/examples/platform/silabs/efr32/rs911x/hal/rsi_board_configuration.h @@ -35,7 +35,8 @@ typedef struct #elif defined(EFR32MG24_BRD4187C) || defined(BRD4187C) #include "brd4187c.h" #else -#error "Need SPI Pins" +#include "sl_custom_board.h" +#warning "Modify sl_custom_board.h configuration file to match your hardware SPIDRV USART peripheral" #endif /* EFR32MG24_BRD4186C */ #endif /* _RSI_BOARD_CONFIGURATION_H_ */ diff --git a/examples/platform/silabs/efr32/rs911x/hal/sl_board_configuration.h b/examples/platform/silabs/efr32/rs911x/hal/sl_board_configuration.h index c1dceab06d8228..317bdc5bd22aa4 100644 --- a/examples/platform/silabs/efr32/rs911x/hal/sl_board_configuration.h +++ b/examples/platform/silabs/efr32/rs911x/hal/sl_board_configuration.h @@ -36,7 +36,8 @@ typedef struct #elif defined(EFR32MG24_BRD4187C) || defined(BRD4187C) #include "brd4187c.h" #else -#error "Need SPI Pins" +#include "sl_custom_board.h" +#warning "Modify sl_custom_board.h configuration file to match your hardware SPIDRV USART peripheral" #endif #if EXP_BOARD #define RESET_PIN PIN(A, 6) diff --git a/examples/platform/silabs/efr32/rs911x/hal/sl_si91x_ncp_utility.c b/examples/platform/silabs/efr32/rs911x/hal/sl_si91x_ncp_utility.c index 4b876883c12b0f..b16bbdc47579e0 100644 --- a/examples/platform/silabs/efr32/rs911x/hal/sl_si91x_ncp_utility.c +++ b/examples/platform/silabs/efr32/rs911x/hal/sl_si91x_ncp_utility.c @@ -36,7 +36,9 @@ #include "spidrv.h" #include "task.h" +#ifdef SL_BOARD_NAME #include "sl_board_control.h" +#endif // SL_BOARD_NAME #include "sl_device_init_clocks.h" #include "sl_device_init_hfxo.h" @@ -242,4 +244,4 @@ sl_status_t sl_wfx_host_spiflash_cs_deassert(void) GPIO_PinOutSet(SL_MX25_FLASH_SHUTDOWN_CS_PORT, SL_MX25_FLASH_SHUTDOWN_CS_PIN); return SL_STATUS_OK; } -#endif // SL_MX25CTRL_MUX \ No newline at end of file +#endif // SL_MX25CTRL_MUX diff --git a/examples/platform/silabs/efr32/sl_custom_board.h b/examples/platform/silabs/efr32/sl_custom_board.h new file mode 100644 index 00000000000000..420b715de9ba76 --- /dev/null +++ b/examples/platform/silabs/efr32/sl_custom_board.h @@ -0,0 +1,71 @@ +/* + * This file is used to set the pins for the SPI for Custom boards + * The SPI pins are defined in the file + * + * !!!! MODIFY THIS FILE TO THE CORRECT PINS !!!! + */ + +#ifndef _CUSTOM_BOARD_H_ +#define _CUSTOM_BOARD_H_ + +#define WAKE_INDICATOR_PIN PIN(D, 2) +#ifdef RS911X_WIFI +// SPI ports and pins +#define EUS1MOSI_PORT gpioPortC +#define EUS1MOSI_PIN 1 +#define EUS1MISO_PORT gpioPortC +#define EUS1MISO_PIN 2 +#define EUS1SCLK_PORT gpioPortC +#define EUS1SCLK_PIN 3 +#define EUS1CS_PORT gpioPortC +#define EUS1CS_PIN 0 + +#define MY_USART EUSART1 +#define MY_USART_CLOCK cmuClock_EUSART1 +#define MY_USART_TX_SIGNAL dmadrvPeripheralSignal_EUSART1_TXBL +#define MY_USART_RX_SIGNAL dmadrvPeripheralSignal_EUSART1_RXDATAV + +#define WFX_RESET_PIN PIN(A, 6) +#define WFX_INTERRUPT_PIN PIN(A, 7) +#ifdef EXP_BOARD +#define WFX_SLEEP_CONFIRM_PIN PIN(D, 2) /* Expansion header pin7 */ +#else +#define WFX_SLEEP_CONFIRM_PIN PIN(A, 5) +#endif /* EXP_BOARD */ +#define SL_WFX_HOST_PINOUT_SPI_IRQ 5 + +#else /* WF200 */ + +#define PIN_OUT_SET 1 +#define PIN_OUT_CLEAR 0 + +#define MY_USART USART0 +#define MY_USART_CLOCK cmuClock_USART0 +#define MY_USART_TX_SIGNAL dmadrvPeripheralSignal_USART0_TXBL +#define MY_USART_RX_SIGNAL dmadrvPeripheralSignal_USART0_RXDATAV + +#define SL_WFX_HOST_PINOUT_RESET_PORT gpioPortA +#define SL_WFX_HOST_PINOUT_RESET_PIN 5 +#define SL_WFX_HOST_PINOUT_SPI_WIRQ_PORT gpioPortA /* SPI IRQ port */ +#define SL_WFX_HOST_PINOUT_SPI_WIRQ_PIN 8 /* SPI IRQ pin */ +#define SL_WFX_HOST_PINOUT_WUP_PORT gpioPortB +#define SL_WFX_HOST_PINOUT_WUP_PIN 5 + +#define SL_WFX_HOST_PINOUT_SPI_TX_PORT gpioPortC +#define SL_WFX_HOST_PINOUT_SPI_TX_PIN 1 +#define SL_WFX_HOST_PINOUT_SPI_TX_LOC 1 + +#define SL_WFX_HOST_PINOUT_SPI_RX_PORT gpioPortC +#define SL_WFX_HOST_PINOUT_SPI_RX_PIN 2 +#define SL_WFX_HOST_PINOUT_SPI_RX_LOC 1 + +#define SL_WFX_HOST_PINOUT_SPI_CLK_PORT gpioPortC +#define SL_WFX_HOST_PINOUT_SPI_CLK_PIN 3 +#define SL_WFX_HOST_PINOUT_SPI_CLK_LOC 1 + +#define SL_WFX_HOST_PINOUT_SPI_CS_PORT gpioPortC +#define SL_WFX_HOST_PINOUT_SPI_CS_PIN 0 +#define SL_WFX_HOST_PINOUT_SPI_CS_LOC 1 + +#endif /* WF200/9116 */ +#endif /* _CUSTOM_BOARD_H_ */ diff --git a/examples/platform/silabs/efr32/wf200/efr_spi.c b/examples/platform/silabs/efr32/wf200/efr_spi.c index 122652e96da39d..3ef34c928e0363 100644 --- a/examples/platform/silabs/efr32/wf200/efr_spi.c +++ b/examples/platform/silabs/efr32/wf200/efr_spi.c @@ -98,11 +98,9 @@ sl_status_t sl_wfx_host_init_bus(void) * EUSARTROUTE register to do this. */ -#if defined(EFR32MG24) GPIO->USARTROUTE[0].ROUTEEN = GPIO_USART_ROUTEEN_RXPEN | // MISO GPIO_USART_ROUTEEN_TXPEN | // MOSI GPIO_USART_ROUTEEN_CLKPEN; -#endif spi_sem = xSemaphoreCreateBinaryStatic(&xEfrSpiSemaBuffer); xSemaphoreGive(spi_sem); @@ -338,11 +336,8 @@ void sl_wfx_host_gpio_init(void) { // Enable GPIO clock. CMU_ClockEnable(cmuClock_GPIO, true); - -#if defined(EFR32MG24) // configure WF200 CS pin. GPIO_PinModeSet(SL_SPIDRV_EXP_CS_PORT, SL_SPIDRV_EXP_CS_PIN, gpioModePushPull, 1); -#endif // Configure WF200 reset pin. GPIO_PinModeSet(SL_WFX_HOST_PINOUT_RESET_PORT, SL_WFX_HOST_PINOUT_RESET_PIN, gpioModePushPull, 0); // Configure WF200 WUP pin. diff --git a/examples/platform/silabs/efr32/wf200/sl_wfx_board.h b/examples/platform/silabs/efr32/wf200/sl_wfx_board.h index 3cbed666d68eab..35ce2f633d0c59 100644 --- a/examples/platform/silabs/efr32/wf200/sl_wfx_board.h +++ b/examples/platform/silabs/efr32/wf200/sl_wfx_board.h @@ -25,6 +25,7 @@ #elif defined(EFR32MG24_BRD4187C) || defined(BRD4187C) || defined(EFR32MG24_BRD4187A) || defined(BRD4187A) #include "brd4187c.h" #else -#error "Need SPI Pins" +#include "sl_custom_board.h" +#warning "Modify sl_custom_board.h configuration file to match your hardware SPIDRV USART peripheral" #endif #endif /* _SL_WFX_BOARD_H_ */ From 05b60aa86a6d71aa3f8d011652f8b06cbea8ba3f Mon Sep 17 00:00:00 2001 From: Anush Nadathur Date: Sat, 24 Aug 2024 13:54:25 -0700 Subject: [PATCH 20/32] [Darwin] Adding missing property on MTRDeviceController_XPC (#35188) - Added controllerID to match the OverXPC interface --- src/darwin/Framework/CHIP/MTRDeviceController_XPC.mm | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/darwin/Framework/CHIP/MTRDeviceController_XPC.mm b/src/darwin/Framework/CHIP/MTRDeviceController_XPC.mm index 67049b280d81f3..d72dc211b70b4f 100644 --- a/src/darwin/Framework/CHIP/MTRDeviceController_XPC.mm +++ b/src/darwin/Framework/CHIP/MTRDeviceController_XPC.mm @@ -78,6 +78,11 @@ - (NSXPCInterface *)_interfaceForClientProtocol return interface; } +- (id)controllerXPCID +{ + return [self.uniqueIdentifier UUIDString]; +} + - (id)initWithUniqueIdentifier:(NSUUID *)UUID xpConnectionBlock:(NSXPCConnection * (^)(void) )connectionBlock { if (self = [super initForSubclasses]) { From a296b6609ec064b201cca9880c577f0197217d43 Mon Sep 17 00:00:00 2001 From: Yufeng Wang Date: Sun, 25 Aug 2024 19:16:00 -0700 Subject: [PATCH 21/32] [Fabric-Bridge] Add state machine to CommissionerControlDelegate to guard various unexpected commands (#35186) * Add state machine to CommissionerControlDelegate to guard various unexpected commands * Restyled by whitespace * Restyled by clang-format * Do not reset state when commissionNode fail to allow retry --------- Co-authored-by: Restyled.io --- .../linux/CommissionerControl.cpp | 67 ++++++++++++++++--- .../linux/include/CommissionerControl.h | 13 ++++ 2 files changed, 71 insertions(+), 9 deletions(-) diff --git a/examples/fabric-bridge-app/linux/CommissionerControl.cpp b/examples/fabric-bridge-app/linux/CommissionerControl.cpp index 3613aeec9305e4..0466ab8b711b69 100644 --- a/examples/fabric-bridge-app/linux/CommissionerControl.cpp +++ b/examples/fabric-bridge-app/linux/CommissionerControl.cpp @@ -46,8 +46,33 @@ namespace app { namespace Clusters { namespace CommissionerControl { +void CommissionerControlDelegate::ResetDelegateState() +{ + // Reset the step to the initial state + mNextStep = Step::kIdle; + + // Reset identifiers and product information + mRequestId = 0; + mClientNodeId = kUndefinedNodeId; + mVendorId = VendorId::Unspecified; + mProductId = 0; + + // Clear the label buffer and optional label + memset(mLabelBuffer, 0, sizeof(mLabelBuffer)); + mLabel.ClearValue(); + + // Reset PBKDF salt and PAKE passcode verifier buffers + mPBKDFSalt = ByteSpan(); + memset(mPBKDFSaltBuffer, 0, sizeof(mPBKDFSaltBuffer)); + + mPAKEPasscodeVerifier = ByteSpan(); + memset(mPAKEPasscodeVerifierBuffer, 0, sizeof(mPAKEPasscodeVerifierBuffer)); +} + CHIP_ERROR CommissionerControlDelegate::HandleCommissioningApprovalRequest(const CommissioningApprovalRequest & request) { + VerifyOrReturnError(mNextStep == Step::kIdle, CHIP_ERROR_INCORRECT_STATE); + CommissionerControl::Events::CommissioningRequestResult::Type result; result.requestId = request.requestId; result.clientNodeId = request.clientNodeId; @@ -86,19 +111,34 @@ CHIP_ERROR CommissionerControlDelegate::HandleCommissioningApprovalRequest(const mLabel.ClearValue(); } - return CommissionerControlServer::Instance().GenerateCommissioningRequestResultEvent(result); + CHIP_ERROR err = CommissionerControlServer::Instance().GenerateCommissioningRequestResultEvent(result); + + if (err == CHIP_NO_ERROR) + { + mNextStep = Step::kWaitCommissionNodeRequest; + } + else + { + ResetDelegateState(); + } + + return err; } CHIP_ERROR CommissionerControlDelegate::ValidateCommissionNodeCommand(NodeId clientNodeId, uint64_t requestId) { CHIP_ERROR err = CHIP_NO_ERROR; + VerifyOrReturnError(mNextStep == Step::kWaitCommissionNodeRequest, CHIP_ERROR_INCORRECT_STATE); + // Verify if the CommissionNode command is sent from the same NodeId as the RequestCommissioningApproval. VerifyOrExit(mClientNodeId == clientNodeId, err = CHIP_ERROR_WRONG_NODE_ID); // Verify if the provided RequestId matches the value provided to the RequestCommissioningApproval. VerifyOrExit(mRequestId == requestId, err = CHIP_ERROR_INCORRECT_STATE); + mNextStep = Step::kStartCommissionNode; + exit: return err; } @@ -129,20 +169,29 @@ CHIP_ERROR CommissionerControlDelegate::GetCommissioningWindowParams(Commissioni CHIP_ERROR CommissionerControlDelegate::HandleCommissionNode(const CommissioningWindowParams & params, const Optional & ipAddress, const Optional & port) { + CHIP_ERROR err = CHIP_NO_ERROR; + ChipLogProgress(NotSpecified, "CommissionerControlDelegate::HandleCommissionNode"); + VerifyOrReturnError(mNextStep == Step::kStartCommissionNode, CHIP_ERROR_INCORRECT_STATE); + #if defined(PW_RPC_FABRIC_BRIDGE_SERVICE) && PW_RPC_FABRIC_BRIDGE_SERVICE - return CommissionNode(Controller::CommissioningWindowPasscodeParams() - .SetSetupPIN(kSetupPinCode) - .SetTimeout(params.commissioningTimeout) - .SetDiscriminator(params.discriminator) - .SetIteration(params.iterations) - .SetSalt(params.salt), - mVendorId, mProductId); + err = CommissionNode(Controller::CommissioningWindowPasscodeParams() + .SetSetupPIN(kSetupPinCode) + .SetTimeout(params.commissioningTimeout) + .SetDiscriminator(params.discriminator) + .SetIteration(params.iterations) + .SetSalt(params.salt), + mVendorId, mProductId); #else ChipLogProgress(NotSpecified, "Failed to reverse commission bridge: PW_RPC_FABRIC_BRIDGE_SERVICE not defined"); - return CHIP_ERROR_NOT_IMPLEMENTED; + err = CHIP_ERROR_NOT_IMPLEMENTED; #endif // defined(PW_RPC_FABRIC_BRIDGE_SERVICE) && PW_RPC_FABRIC_BRIDGE_SERVICE + + // Reset the delegate's state to prepare for a new commissioning sequence. + ResetDelegateState(); + + return err; } } // namespace CommissionerControl diff --git a/examples/fabric-bridge-app/linux/include/CommissionerControl.h b/examples/fabric-bridge-app/linux/include/CommissionerControl.h index 633be761ba1004..486668ffa212f7 100644 --- a/examples/fabric-bridge-app/linux/include/CommissionerControl.h +++ b/examples/fabric-bridge-app/linux/include/CommissionerControl.h @@ -38,8 +38,21 @@ class CommissionerControlDelegate : public Delegate ~CommissionerControlDelegate() = default; private: + enum class Step : uint8_t + { + // Ready to start reverse commissioning. + kIdle, + // Wait for the commission node command. + kWaitCommissionNodeRequest, + // Need to commission node. + kStartCommissionNode, + }; + + void ResetDelegateState(); + static constexpr size_t kLabelBufferSize = 64; + Step mNextStep = Step::kIdle; uint64_t mRequestId = 0; NodeId mClientNodeId = kUndefinedNodeId; VendorId mVendorId = VendorId::Unspecified; From acba7f83e2891e7d1bcb8f3e11a9d938d38f28e5 Mon Sep 17 00:00:00 2001 From: Nivi Sarkar <55898241+nivi-apple@users.noreply.github.com> Date: Sun, 25 Aug 2024 23:55:45 -0700 Subject: [PATCH 22/32] =?UTF-8?q?Move=20the=20constraint=20check=20for=20n?= =?UTF-8?q?umber=20of=20presets=20not=20to=20exceed=20the=20num=E2=80=A6?= =?UTF-8?q?=20(#35167)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Move the constraint check for number of presets not to exceed the number of presets supported from the atomic write commit command handling to the write request * Minor fix * Do not check if totalExpectedCount > 0 since its an unsigned integer - Also no need to check if numberOfPresetsSupported is 0. * Update src/app/clusters/thermostat-server/thermostat-server-presets.cpp * Update src/app/clusters/thermostat-server/thermostat-server-presets.cpp --------- Co-authored-by: Boris Zbarsky --- .../thermostat-server-presets.cpp | 33 ++++++++----------- 1 file changed, 14 insertions(+), 19 deletions(-) diff --git a/src/app/clusters/thermostat-server/thermostat-server-presets.cpp b/src/app/clusters/thermostat-server/thermostat-server-presets.cpp index e57c2f9c95f8fd..a56c94fa49834d 100644 --- a/src/app/clusters/thermostat-server/thermostat-server-presets.cpp +++ b/src/app/clusters/thermostat-server/thermostat-server-presets.cpp @@ -402,6 +402,20 @@ CHIP_ERROR ThermostatAttrAccess::AppendPendingPreset(Thermostat::Delegate * dele return CHIP_IM_GLOBAL_STATUS(ConstraintError); } + // Before adding this preset to the pending presets, if the expected length of the pending presets' list + // exceeds the total number of presets supported, return RESOURCE_EXHAUSTED. Note that the preset has not been appended yet. + + uint8_t numberOfPendingPresets = CountNumberOfPendingPresets(delegate); + + // We will be adding one more preset, so reject if the length is already at max. + if (numberOfPendingPresets >= delegate->GetNumberOfPresets()) + { + return CHIP_IM_GLOBAL_STATUS(ResourceExhausted); + } + + // TODO #34556 : Check if the number of presets for each presetScenario exceeds the max number of presets supported for that + // scenario. We plan to support only one preset for each presetScenario for our use cases so defer this for re-evaluation. + return delegate->AppendToPendingPresetList(preset); } @@ -504,25 +518,6 @@ Status ThermostatAttrAccess::PrecommitPresets(EndpointId endpoint) } } - uint8_t totalCount = CountNumberOfPendingPresets(delegate); - - uint8_t numberOfPresetsSupported = delegate->GetNumberOfPresets(); - - if (numberOfPresetsSupported == 0) - { - ChipLogError(Zcl, "emberAfThermostatClusterCommitPresetsSchedulesRequestCallback: Failed to get NumberOfPresets"); - return Status::InvalidInState; - } - - // If the expected length of the presets attribute with the applied changes exceeds the total number of presets supported, - // return RESOURCE_EXHAUSTED. Note that the changes are not yet applied. - if (numberOfPresetsSupported > 0 && totalCount > numberOfPresetsSupported) - { - return Status::ResourceExhausted; - } - - // TODO: Check if the number of presets for each presetScenario exceeds the max number of presets supported for that - // scenario. We plan to support only one preset for each presetScenario for our use cases so defer this for re-evaluation. return Status::Success; } From 7ddba3619f7b352131a238cb53b14a0580f01714 Mon Sep 17 00:00:00 2001 From: Lazar Kovacic Date: Mon, 26 Aug 2024 19:41:46 +0200 Subject: [PATCH 23/32] TV App - Add validation for invalid product and vendor id. (#35157) * Add validation for pid&vid * Restyled by clang-format --------- Co-authored-by: Restyled.io --- src/controller/CommissionerDiscoveryController.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/controller/CommissionerDiscoveryController.cpp b/src/controller/CommissionerDiscoveryController.cpp index baf8e14bfd4e37..1e338b43dc796f 100644 --- a/src/controller/CommissionerDiscoveryController.cpp +++ b/src/controller/CommissionerDiscoveryController.cpp @@ -155,6 +155,13 @@ void CommissionerDiscoveryController::OnUserDirectedCommissioningRequest(UDCClie return; } + if (state.GetProductId() == 0 && state.GetVendorId() == 0) + { + // this is an invalid request and should be ignored + ChipLogDetail(Controller, "Ignoring the request as it's invalid. product and vendor id cannot be 0"); + return; + } + mReady = false; Platform::CopyString(mCurrentInstance, state.GetInstanceName()); mPendingConsent = true; @@ -163,7 +170,7 @@ void CommissionerDiscoveryController::OnUserDirectedCommissioningRequest(UDCClie sizeof(rotatingIdString)); if (err != CHIP_NO_ERROR) { - ChipLogError(AppServer, "On UDC: could not convert rotating id to hex"); + ChipLogError(Controller, "On UDC: could not convert rotating id to hex"); rotatingIdString[0] = '\0'; } else From aa508e607f34c9efdb71c0defedf94f954dd6c7b Mon Sep 17 00:00:00 2001 From: Anush Nadathur Date: Mon, 26 Aug 2024 16:16:36 -0700 Subject: [PATCH 24/32] [Darwin] Find cached controllers when creating existing controller on fabric as well (#35205) - Missed another call site in #35148 --- src/darwin/Framework/CHIP/MTRDeviceController.mm | 10 +++++----- .../Framework/CHIP/MTRDeviceControllerFactory.mm | 12 +++++++++--- .../Framework/CHIP/MTRDeviceController_Internal.h | 2 +- 3 files changed, 15 insertions(+), 9 deletions(-) diff --git a/src/darwin/Framework/CHIP/MTRDeviceController.mm b/src/darwin/Framework/CHIP/MTRDeviceController.mm index 4630ff7fe00b4f..650aba4b24a700 100644 --- a/src/darwin/Framework/CHIP/MTRDeviceController.mm +++ b/src/darwin/Framework/CHIP/MTRDeviceController.mm @@ -331,14 +331,14 @@ - (BOOL)isRunning return _cppCommissioner != nullptr; } -- (BOOL)matchesPendingShutdownWithParams:(MTRDeviceControllerParameters *)parameters +- (BOOL)matchesPendingShutdownControllerWithOperationalCertificate:(nullable MTRCertificateDERBytes)operationalCertificate andRootCertificate:(nullable MTRCertificateDERBytes)rootCertificate { - if (!parameters.operationalCertificate || !parameters.rootCertificate) { + if (!operationalCertificate || !rootCertificate) { return FALSE; } - NSNumber * nodeID = [MTRDeviceControllerParameters nodeIDFromNOC:parameters.operationalCertificate]; - NSNumber * fabricID = [MTRDeviceControllerParameters fabricIDFromNOC:parameters.operationalCertificate]; - NSData * publicKey = [MTRDeviceControllerParameters publicKeyFromCertificate:parameters.rootCertificate]; + NSNumber * nodeID = [MTRDeviceControllerParameters nodeIDFromNOC:operationalCertificate]; + NSNumber * fabricID = [MTRDeviceControllerParameters fabricIDFromNOC:operationalCertificate]; + NSData * publicKey = [MTRDeviceControllerParameters publicKeyFromCertificate:rootCertificate]; std::lock_guard lock(_assertionLock); diff --git a/src/darwin/Framework/CHIP/MTRDeviceControllerFactory.mm b/src/darwin/Framework/CHIP/MTRDeviceControllerFactory.mm index bd0b90450b9ac4..00b59f525b0f16 100644 --- a/src/darwin/Framework/CHIP/MTRDeviceControllerFactory.mm +++ b/src/darwin/Framework/CHIP/MTRDeviceControllerFactory.mm @@ -660,6 +660,12 @@ - (MTRDeviceController * _Nullable)createControllerOnExistingFabric:(MTRDeviceCo return nil; } + // If there is a controller already running with matching parameters that is conceptually shut down from the API consumer's viewpoint, re-use it. + MTRDeviceController * existingController = [self _findPendingShutdownControllerWithOperationalCertificate:startupParams.operationalCertificate andRootCertificate:startupParams.rootCertificate]; + if (existingController) { + return existingController; + } + return [self _startDeviceController:[MTRDeviceController alloc] startupParams:startupParams fabricChecker:^MTRDeviceControllerStartupParamsInternal *( @@ -1133,11 +1139,11 @@ - (void)operationalInstanceAdded:(chip::PeerId &)operationalID } } -- (nullable MTRDeviceController *)_findControllerWithPendingShutdownMatchingParams:(MTRDeviceControllerParameters *)parameters +- (nullable MTRDeviceController *)_findPendingShutdownControllerWithOperationalCertificate:(nullable MTRCertificateDERBytes)operationalCertificate andRootCertificate:(nullable MTRCertificateDERBytes)rootCertificate { std::lock_guard lock(_controllersLock); for (MTRDeviceController * controller in _controllers) { - if ([controller matchesPendingShutdownWithParams:parameters]) { + if ([controller matchesPendingShutdownControllerWithOperationalCertificate:operationalCertificate andRootCertificate:rootCertificate]) { MTR_LOG("%@ Found existing controller %@ that is pending shutdown and matching parameters, re-using it", self, controller); [controller clearPendingShutdown]; return controller; @@ -1153,7 +1159,7 @@ - (nullable MTRDeviceController *)initializeController:(MTRDeviceController *)co [self _assertCurrentQueueIsNotMatterQueue]; // If there is a controller already running with matching parameters that is conceptually shut down from the API consumer's viewpoint, re-use it. - MTRDeviceController * existingController = [self _findControllerWithPendingShutdownMatchingParams:parameters]; + MTRDeviceController * existingController = [self _findPendingShutdownControllerWithOperationalCertificate:parameters.operationalCertificate andRootCertificate:parameters.rootCertificate]; if (existingController) { return existingController; } diff --git a/src/darwin/Framework/CHIP/MTRDeviceController_Internal.h b/src/darwin/Framework/CHIP/MTRDeviceController_Internal.h index e625d13a80e77e..f37d6fbf08c6d9 100644 --- a/src/darwin/Framework/CHIP/MTRDeviceController_Internal.h +++ b/src/darwin/Framework/CHIP/MTRDeviceController_Internal.h @@ -322,7 +322,7 @@ NS_ASSUME_NONNULL_BEGIN /** * This method returns TRUE if this controller matches the fabric reference and node ID as listed in the parameters. */ -- (BOOL)matchesPendingShutdownWithParams:(MTRDeviceControllerParameters *)parameters; +- (BOOL)matchesPendingShutdownControllerWithOperationalCertificate:(nullable MTRCertificateDERBytes)operationalCertificate andRootCertificate:(nullable MTRCertificateDERBytes)rootCertificate; /** * Clear any pending shutdown request. From e20355aeb38ccd004a300900730fe843306672f1 Mon Sep 17 00:00:00 2001 From: Boris Zbarsky Date: Mon, 26 Aug 2024 20:37:09 -0400 Subject: [PATCH 25/32] Remove handling of the unitTestInject* bits in base MTRDevice. (#35180) These injections need to be handled by subclasses, because the subclasses do very different report processing and have different mechanisms for getting reports. Once these implementations are removed, the following become unreachable and can be removed: * _handleEventReport * _handleReportBegin * _handleAttributeReport * _handleReportEnd Once those are removed, the following become unreachable or unused and can be removed: * _receivingReport (always false) * _receivingPrimingReport (always false, because _receivingReport is always false) * _changeState * _callDelegateDeviceCachePrimed * unreportedEvents * _getAttributesToReportWithReportedValues Once those are removed, the following become unreachable or unused and can be removed: * _setCachedAttributeValue * _noteDataVersion * _deviceConfigurationChanged * estimatedStartTimeFromGeneralDiagnosticsUpTime * _attributeAffectsDeviceConfiguration * _pruneStoredDataForPath Once those are removed, the following become unreachable or unused and can be removed: * _pruneEndpointsIn * _pruneClustersIn * _pruneAttributesIn * _attributePathAffectsDescriptionData * _addInformationalAttributesToCurrentMetricScope * AttributeHasChangesOmittedQuality Once those are removed, the following become unreachable or unused and can be removed: * _removeAttributes * _removeClusters Once those are removed, the following become unreachable or unused and can be removed: * _removeCachedAttribute --- src/darwin/Framework/CHIP/MTRDevice.mm | 742 +------------------------ 1 file changed, 3 insertions(+), 739 deletions(-) diff --git a/src/darwin/Framework/CHIP/MTRDevice.mm b/src/darwin/Framework/CHIP/MTRDevice.mm index f913f29388f5fb..134042f974dd54 100644 --- a/src/darwin/Framework/CHIP/MTRDevice.mm +++ b/src/darwin/Framework/CHIP/MTRDevice.mm @@ -260,9 +260,6 @@ @interface MTRDevice () @property (nonatomic, readonly) os_unfair_lock timeSyncLock; @property (nonatomic) chip::FabricIndex fabricIndex; -@property (nonatomic) NSMutableArray *> * unreportedEvents; -@property (nonatomic) BOOL receivingReport; -@property (nonatomic) BOOL receivingPrimingReport; // TODO: instead of all the BOOL properties that are some facet of the state, move to internal state machine that has (at least): // Actively receiving report @@ -287,8 +284,6 @@ @interface MTRDevice () @property (nonatomic) BOOL timeUpdateScheduled; -@property (nonatomic) NSDate * estimatedStartTimeFromGeneralDiagnosticsUpTime; - @property (nonatomic) NSMutableDictionary * temporaryMetaDataCache; @end @@ -327,10 +322,6 @@ @implementation MTRDevice { // right now (because they have been evicted). NSMutableSet * _persistedClusters; - // This boolean keeps track of any device configuration changes received in an attribute report. - // If this is true when the report ends, we notify the delegate. - BOOL _deviceConfigurationChanged; - // Storage behavior configuration and variables to keep track of the logic // _clusterDataPersistenceFirstScheduledTime is used to track the start time of the delay between // report and persistence. @@ -371,6 +362,7 @@ - (instancetype)initForSubclassesWithNodeID:(NSNumber *)nodeID controller:(MTRDe _delegates = [NSMutableSet set]; _deviceController = controller; _nodeID = nodeID; + _state = MTRDeviceStateUnknown; } return self; @@ -820,41 +812,6 @@ - (void)_callFirstDelegateSynchronouslyWithBlock:(void (^)(id } #endif -- (void)_callDelegateDeviceCachePrimed -{ - os_unfair_lock_assert_owner(&self->_lock); - [self _callDelegatesWithBlock:^(id delegate) { - if ([delegate respondsToSelector:@selector(deviceCachePrimed:)]) { - [delegate deviceCachePrimed:self]; - } - }]; -} - -// assume lock is held -- (void)_changeState:(MTRDeviceState)state -{ - os_unfair_lock_assert_owner(&self->_lock); - MTRDeviceState lastState = _state; - _state = state; - if (lastState != state) { - if (state != MTRDeviceStateReachable) { - MTR_LOG("%@ reachability state change %lu => %lu, set estimated start time to nil", self, static_cast(lastState), - static_cast(state)); - _estimatedStartTime = nil; - _estimatedStartTimeFromGeneralDiagnosticsUpTime = nil; - } else { - MTR_LOG( - "%@ reachability state change %lu => %lu", self, static_cast(lastState), static_cast(state)); - } - [self _callDelegatesWithBlock:^(id delegate) { - [delegate device:self stateChanged:state]; - }]; - } else { - MTR_LOG( - "%@ Not reporting reachability state change, since no change in state %lu => %lu", self, static_cast(lastState), static_cast(state)); - } -} - #ifdef DEBUG - (MTRInternalDeviceState)_getInternalState { @@ -906,20 +863,6 @@ - (BOOL)_deviceUsesThread return (networkCommissioningClusterFeatureMapValue & MTRNetworkCommissioningFeatureThreadNetworkInterface) != 0 ? YES : NO; } -- (void)_handleReportBegin -{ - std::lock_guard lock(_lock); - - _receivingReport = YES; - if (_state != MTRDeviceStateReachable) { - [self _changeState:MTRDeviceStateReachable]; - } - - // If we currently don't have an established subscription, this must be a - // priming report. - _receivingPrimingReport = YES; -} - - (NSDictionary *)_clusterDataToPersistSnapshot { os_unfair_lock_assert_owner(&self->_lock); @@ -1202,49 +1145,6 @@ - (void)setStorageBehaviorConfiguration:(MTRDeviceStorageBehaviorConfiguration * [self _resetStorageBehaviorState]; } -- (void)_handleReportEnd -{ - std::lock_guard lock(_lock); - _receivingReport = NO; - _receivingPrimingReport = NO; - _estimatedStartTimeFromGeneralDiagnosticsUpTime = nil; - - [self _scheduleClusterDataPersistence]; - - // After the handling of the report, if we detected a device configuration change, notify the delegate - // of the same. - if (_deviceConfigurationChanged) { - [self _callDelegatesWithBlock:^(id delegate) { - if ([delegate respondsToSelector:@selector(deviceConfigurationChanged:)]) { - [delegate deviceConfigurationChanged:self]; - } - }]; - _deviceConfigurationChanged = NO; - } - - // Do this after the _deviceConfigurationChanged check, so that we don't - // call deviceConfigurationChanged: immediately after telling our delegate - // we are now primed. - // - // TODO: Maybe we shouldn't dispatch deviceConfigurationChanged: for the - // initial priming bits? - if (!_deviceCachePrimed) { - // This is the end of the priming sequence of data reports, so we have - // all the data for the device now. - _deviceCachePrimed = YES; - [self _callDelegateDeviceCachePrimed]; - } - -// For unit testing only -#ifdef DEBUG - [self _callDelegatesWithBlock:^(id testDelegate) { - if ([testDelegate respondsToSelector:@selector(unitTestReportEndForDevice:)]) { - [testDelegate unitTestReportEndForDevice:self]; - } - }]; -#endif -} - - (BOOL)_interestedPaths:(NSArray * _Nullable)interestedPaths includesAttributePath:(MTRAttributePath *)attributePath { for (id interestedPath in interestedPaths) { @@ -1316,29 +1216,15 @@ - (void)_reportAttributes:(NSArray *> *)attributes } } -- (void)_handleAttributeReport:(NSArray *> *)attributeReport fromSubscription:(BOOL)isFromSubscription -{ - std::lock_guard lock(_lock); - - // _getAttributesToReportWithReportedValues will log attribute paths reported - [self _reportAttributes:[self _getAttributesToReportWithReportedValues:attributeReport fromSubscription:isFromSubscription]]; -} - #ifdef DEBUG - (void)unitTestInjectEventReport:(NSArray *> *)eventReport { - dispatch_async(self.queue, ^{ - [self _handleEventReport:eventReport]; - }); + NSAssert(NO, @"Unit test injection of reports needs to be handled by subclasses"); } - (void)unitTestInjectAttributeReport:(NSArray *> *)attributeReport fromSubscription:(BOOL)isFromSubscription { - dispatch_async(self.queue, ^{ - [self _handleReportBegin]; - [self _handleAttributeReport:attributeReport fromSubscription:isFromSubscription]; - [self _handleReportEnd]; - }); + NSAssert(NO, @"Unit test injection of reports needs to be handled by subclasses"); } #endif @@ -1396,111 +1282,6 @@ - (BOOL)_interestedPaths:(NSArray * _Nullable)interestedPaths includesEventPath: return filteredEvents; } -- (void)_handleEventReport:(NSArray *> *)eventReport -{ - std::lock_guard lock(_lock); - - NSDate * oldEstimatedStartTime = _estimatedStartTime; - // Combine with previous unreported events, if they exist - NSMutableArray * reportToReturn; - if (_unreportedEvents) { - reportToReturn = _unreportedEvents; - } else { - reportToReturn = [NSMutableArray array]; - } - for (NSDictionary * eventDict in eventReport) { - // Whenever a StartUp event is received, reset the estimated start time - // New subscription case - // - Starts Unreachable - // - Start CASE and send subscription request - // - Receive priming report ReportBegin - // - Optionally receive UpTime attribute - update time and save start time estimate - // - Optionally receive StartUp event - // - Set estimated system time from event receipt time, or saved UpTime estimate if exists - // - ReportEnd handler clears the saved start time estimate based on UpTime - // Subscription dropped from client point of view case - // - Starts Unreachable - // - Resubscribe happens after some time, and then same as the above - // Server resuming subscription after reboot case - // - Starts Reachable - // - Receive priming report ReportBegin - // - Optionally receive UpTime attribute - update time and save value - // - Optionally receive StartUp event - // - Set estimated system time from event receipt time, or saved UpTime estimate if exists - // - ReportEnd handler clears the saved start time estimate based on UpTime - // Server resuming subscription after timeout case - // - Starts Reachable - // - Receive priming report ReportBegin - // - Optionally receive UpTime attribute - update time and save value - // - ReportEnd handler clears the saved start time estimate based on UpTime - MTREventPath * eventPath = eventDict[MTREventPathKey]; - BOOL isStartUpEvent = (eventPath.cluster.unsignedLongValue == MTRClusterIDTypeBasicInformationID) - && (eventPath.event.unsignedLongValue == MTREventIDTypeClusterBasicInformationEventStartUpID); - if (isStartUpEvent) { - if (_estimatedStartTimeFromGeneralDiagnosticsUpTime) { - // If UpTime was received, make use of it as mark of system start time - MTR_LOG("%@ StartUp event: set estimated start time forward to %@", self, - _estimatedStartTimeFromGeneralDiagnosticsUpTime); - _estimatedStartTime = _estimatedStartTimeFromGeneralDiagnosticsUpTime; - } else { - // If UpTime was not received, reset estimated start time in case of reboot - MTR_LOG("%@ StartUp event: set estimated start time to nil", self); - _estimatedStartTime = nil; - } - } - - // If event time is of MTREventTimeTypeSystemUpTime type, then update estimated start time as needed - NSNumber * eventTimeTypeNumber = eventDict[MTREventTimeTypeKey]; - if (!eventTimeTypeNumber) { - MTR_LOG_ERROR("%@ Event %@ missing event time type", self, eventDict); - continue; - } - MTREventTimeType eventTimeType = (MTREventTimeType) eventTimeTypeNumber.unsignedIntegerValue; - if (eventTimeType == MTREventTimeTypeSystemUpTime) { - NSNumber * eventTimeValueNumber = eventDict[MTREventSystemUpTimeKey]; - if (!eventTimeValueNumber) { - MTR_LOG_ERROR("%@ Event %@ missing event time value", self, eventDict); - continue; - } - NSTimeInterval eventTimeValue = eventTimeValueNumber.doubleValue; - NSDate * potentialSystemStartTime = [NSDate dateWithTimeIntervalSinceNow:-eventTimeValue]; - if (!_estimatedStartTime || ([potentialSystemStartTime compare:_estimatedStartTime] == NSOrderedAscending)) { - _estimatedStartTime = potentialSystemStartTime; - } - } - - NSMutableDictionary * eventToReturn = eventDict.mutableCopy; - if (_receivingPrimingReport) { - eventToReturn[MTREventIsHistoricalKey] = @(YES); - } else { - eventToReturn[MTREventIsHistoricalKey] = @(NO); - } - - [reportToReturn addObject:eventToReturn]; - } - if (oldEstimatedStartTime != _estimatedStartTime) { - MTR_LOG("%@ updated estimated start time to %@", self, _estimatedStartTime); - } - - __block BOOL delegatesCalled = NO; - [self _iterateDelegatesWithBlock:^(MTRDeviceDelegateInfo * delegateInfo) { - // _iterateDelegatesWithBlock calls this with an autorelease pool, and so temporary filtered event reports don't bloat memory - NSArray *> * filteredEvents = [self _filteredEvents:reportToReturn forInterestedPaths:delegateInfo.interestedPathsForEvents]; - if (filteredEvents.count) { - [delegateInfo callDelegateWithBlock:^(id delegate) { - [delegate device:self receivedEventReport:filteredEvents]; - }]; - delegatesCalled = YES; - } - }]; - if (delegatesCalled) { - _unreportedEvents = nil; - } else { - // save unreported events - _unreportedEvents = reportToReturn; - } -} - #ifdef DEBUG - (void)unitTestClearClusterData { @@ -1614,63 +1395,6 @@ - (MTRDeviceDataValueDictionary _Nullable)_cachedAttributeValueForPath:(MTRAttri return clusterData.attributes[path.attribute]; } -- (void)_setCachedAttributeValue:(MTRDeviceDataValueDictionary _Nullable)value forPath:(MTRAttributePath *)path fromSubscription:(BOOL)isFromSubscription -{ - os_unfair_lock_assert_owner(&self->_lock); - - // We need an actual MTRClusterPath, not a subclass, to do _clusterDataForPath. - auto * clusterPath = [MTRClusterPath clusterPathWithEndpointID:path.endpoint clusterID:path.cluster]; - - MTRDeviceClusterData * clusterData = [self _clusterDataForPath:clusterPath]; - if (clusterData == nil) { - if (value == nil) { - // Nothing to do. - return; - } - - clusterData = [[MTRDeviceClusterData alloc] init]; - } - - [clusterData storeValue:value forAttribute:path.attribute]; - - if ([self _attributePathAffectsDescriptionData:path]) { - [self _updateAttributeDependentDescriptionData]; - } - - if (value != nil - && isFromSubscription - && !_receivingPrimingReport - && AttributeHasChangesOmittedQuality(path)) { - // Do not persist new values for Changes Omitted Quality (aka C Quality) - // attributes unless they're part of a Priming Report or from a read response. - // (removals are OK) - - // log when a device violates expectations for Changes Omitted Quality attributes. - using namespace chip::Tracing::DarwinFramework; - MATTER_LOG_METRIC_BEGIN(kMetricUnexpectedCQualityUpdate); - [self _addInformationalAttributesToCurrentMetricScope]; - MATTER_LOG_METRIC_END(kMetricUnexpectedCQualityUpdate); - - return; - } - - if (_clusterDataToPersist == nil) { - _clusterDataToPersist = [NSMutableDictionary dictionary]; - } - _clusterDataToPersist[clusterPath] = clusterData; -} - -- (void)_removeCachedAttribute:(NSNumber *)attributeID fromCluster:(MTRClusterPath *)clusterPath -{ - os_unfair_lock_assert_owner(&self->_lock); - - if (_clusterDataToPersist == nil) { - return; - } - auto * clusterData = _clusterDataToPersist[clusterPath]; - [clusterData removeValueForAttribute:attributeID]; -} - #ifdef DEBUG - (void)unitTestResetSubscription { @@ -1700,132 +1424,6 @@ - (NSUInteger)unitTestNonnullDelegateCount #pragma mark Device Interactions -// Helper function to determine whether an attribute has "Changes Omitted" quality, which indicates that past the priming report in -// a subscription, this attribute is not expected to be reported when its value changes -// * TODO: xml+codegen version to replace this hardcoded list. -static BOOL AttributeHasChangesOmittedQuality(MTRAttributePath * attributePath) -{ - switch (attributePath.cluster.unsignedLongValue) { - case MTRClusterEthernetNetworkDiagnosticsID: - switch (attributePath.attribute.unsignedLongValue) { - case MTRClusterEthernetNetworkDiagnosticsAttributePacketRxCountID: - case MTRClusterEthernetNetworkDiagnosticsAttributePacketTxCountID: - case MTRClusterEthernetNetworkDiagnosticsAttributeTxErrCountID: - case MTRClusterEthernetNetworkDiagnosticsAttributeCollisionCountID: - case MTRClusterEthernetNetworkDiagnosticsAttributeOverrunCountID: - case MTRClusterEthernetNetworkDiagnosticsAttributeCarrierDetectID: - case MTRClusterEthernetNetworkDiagnosticsAttributeTimeSinceResetID: - return YES; - default: - return NO; - } - case MTRClusterGeneralDiagnosticsID: - switch (attributePath.attribute.unsignedLongValue) { - case MTRClusterGeneralDiagnosticsAttributeUpTimeID: - case MTRClusterGeneralDiagnosticsAttributeTotalOperationalHoursID: - return YES; - default: - return NO; - } - case MTRClusterThreadNetworkDiagnosticsID: - switch (attributePath.attribute.unsignedLongValue) { - case MTRClusterThreadNetworkDiagnosticsAttributeOverrunCountID: - case MTRClusterThreadNetworkDiagnosticsAttributeDetachedRoleCountID: - case MTRClusterThreadNetworkDiagnosticsAttributeChildRoleCountID: - case MTRClusterThreadNetworkDiagnosticsAttributeRouterRoleCountID: - case MTRClusterThreadNetworkDiagnosticsAttributeLeaderRoleCountID: - case MTRClusterThreadNetworkDiagnosticsAttributeAttachAttemptCountID: - case MTRClusterThreadNetworkDiagnosticsAttributePartitionIdChangeCountID: - case MTRClusterThreadNetworkDiagnosticsAttributeBetterPartitionAttachAttemptCountID: - case MTRClusterThreadNetworkDiagnosticsAttributeParentChangeCountID: - case MTRClusterThreadNetworkDiagnosticsAttributeTxTotalCountID: - case MTRClusterThreadNetworkDiagnosticsAttributeTxUnicastCountID: - case MTRClusterThreadNetworkDiagnosticsAttributeTxBroadcastCountID: - case MTRClusterThreadNetworkDiagnosticsAttributeTxAckRequestedCountID: - case MTRClusterThreadNetworkDiagnosticsAttributeTxAckedCountID: - case MTRClusterThreadNetworkDiagnosticsAttributeTxNoAckRequestedCountID: - case MTRClusterThreadNetworkDiagnosticsAttributeTxDataCountID: - case MTRClusterThreadNetworkDiagnosticsAttributeTxDataPollCountID: - case MTRClusterThreadNetworkDiagnosticsAttributeTxBeaconCountID: - case MTRClusterThreadNetworkDiagnosticsAttributeTxBeaconRequestCountID: - case MTRClusterThreadNetworkDiagnosticsAttributeTxOtherCountID: - case MTRClusterThreadNetworkDiagnosticsAttributeTxRetryCountID: - case MTRClusterThreadNetworkDiagnosticsAttributeTxDirectMaxRetryExpiryCountID: - case MTRClusterThreadNetworkDiagnosticsAttributeTxIndirectMaxRetryExpiryCountID: - case MTRClusterThreadNetworkDiagnosticsAttributeTxErrCcaCountID: - case MTRClusterThreadNetworkDiagnosticsAttributeTxErrAbortCountID: - case MTRClusterThreadNetworkDiagnosticsAttributeTxErrBusyChannelCountID: - case MTRClusterThreadNetworkDiagnosticsAttributeRxTotalCountID: - case MTRClusterThreadNetworkDiagnosticsAttributeRxUnicastCountID: - case MTRClusterThreadNetworkDiagnosticsAttributeRxBroadcastCountID: - case MTRClusterThreadNetworkDiagnosticsAttributeRxDataCountID: - case MTRClusterThreadNetworkDiagnosticsAttributeRxDataPollCountID: - case MTRClusterThreadNetworkDiagnosticsAttributeRxBeaconCountID: - case MTRClusterThreadNetworkDiagnosticsAttributeRxBeaconRequestCountID: - case MTRClusterThreadNetworkDiagnosticsAttributeRxOtherCountID: - case MTRClusterThreadNetworkDiagnosticsAttributeRxAddressFilteredCountID: - case MTRClusterThreadNetworkDiagnosticsAttributeRxDestAddrFilteredCountID: - case MTRClusterThreadNetworkDiagnosticsAttributeRxDuplicatedCountID: - case MTRClusterThreadNetworkDiagnosticsAttributeRxErrNoFrameCountID: - case MTRClusterThreadNetworkDiagnosticsAttributeRxErrUnknownNeighborCountID: - case MTRClusterThreadNetworkDiagnosticsAttributeRxErrInvalidSrcAddrCountID: - case MTRClusterThreadNetworkDiagnosticsAttributeRxErrSecCountID: - case MTRClusterThreadNetworkDiagnosticsAttributeRxErrFcsCountID: - case MTRClusterThreadNetworkDiagnosticsAttributeRxErrOtherCountID: - return YES; - default: - return NO; - } - case MTRClusterWiFiNetworkDiagnosticsID: - switch (attributePath.attribute.unsignedLongValue) { - case MTRClusterWiFiNetworkDiagnosticsAttributeRssiID: - case MTRClusterWiFiNetworkDiagnosticsAttributeBeaconLostCountID: - case MTRClusterWiFiNetworkDiagnosticsAttributeBeaconRxCountID: - case MTRClusterWiFiNetworkDiagnosticsAttributePacketMulticastRxCountID: - case MTRClusterWiFiNetworkDiagnosticsAttributePacketMulticastTxCountID: - case MTRClusterWiFiNetworkDiagnosticsAttributePacketUnicastRxCountID: - case MTRClusterWiFiNetworkDiagnosticsAttributePacketUnicastTxCountID: - case MTRClusterWiFiNetworkDiagnosticsAttributeCurrentMaxRateID: - case MTRClusterWiFiNetworkDiagnosticsAttributeOverrunCountID: - return YES; - default: - return NO; - } - case MTRClusterOperationalCredentialsID: - switch (attributePath.attribute.unsignedLongValue) { - case MTRClusterOperationalCredentialsAttributeNOCsID: - case MTRClusterOperationalCredentialsAttributeTrustedRootCertificatesID: - return YES; - default: - return NO; - } - case MTRClusterPowerSourceID: - switch (attributePath.attribute.unsignedLongValue) { - case MTRClusterPowerSourceAttributeWiredAssessedInputVoltageID: - case MTRClusterPowerSourceAttributeWiredAssessedInputFrequencyID: - case MTRClusterPowerSourceAttributeWiredAssessedCurrentID: - case MTRClusterPowerSourceAttributeBatVoltageID: - case MTRClusterPowerSourceAttributeBatPercentRemainingID: - case MTRClusterPowerSourceAttributeBatTimeRemainingID: - case MTRClusterPowerSourceAttributeBatTimeToFullChargeID: - case MTRClusterPowerSourceAttributeBatChargingCurrentID: - return YES; - default: - return NO; - } - case MTRClusterTimeSynchronizationID: - switch (attributePath.attribute.unsignedLongValue) { - case MTRClusterTimeSynchronizationAttributeUTCTimeID: - case MTRClusterTimeSynchronizationAttributeLocalTimeID: - return YES; - default: - return NO; - } - default: - return NO; - } -} - - (NSDictionary * _Nullable)readAttributeWithEndpointID:(NSNumber *)endpointID clusterID:(NSNumber *)clusterID attributeID:(NSNumber *)attributeID @@ -2196,304 +1794,6 @@ - (NSDictionary *)_dataValueWithoutDataVersion:(NSDictionary *)attributeValue } } -// Update cluster data version and also note the change, so at onReportEnd it can be persisted -- (void)_noteDataVersion:(NSNumber *)dataVersion forClusterPath:(MTRClusterPath *)clusterPath -{ - os_unfair_lock_assert_owner(&self->_lock); - - BOOL dataVersionChanged = NO; - // Update data version used for subscription filtering - MTRDeviceClusterData * clusterData = [self _clusterDataForPath:clusterPath]; - if (!clusterData) { - clusterData = [[MTRDeviceClusterData alloc] initWithDataVersion:dataVersion attributes:nil]; - dataVersionChanged = YES; - } else if (![clusterData.dataVersion isEqualToNumber:dataVersion]) { - clusterData.dataVersion = dataVersion; - dataVersionChanged = YES; - } - - if (dataVersionChanged) { - if (_clusterDataToPersist == nil) { - _clusterDataToPersist = [NSMutableDictionary dictionary]; - } - _clusterDataToPersist[clusterPath] = clusterData; - } -} - -- (BOOL)_attributeAffectsDeviceConfiguration:(MTRAttributePath *)attributePath -{ - // Check for attributes in the descriptor cluster that affect device configuration. - if (attributePath.cluster.unsignedLongValue == MTRClusterIDTypeDescriptorID) { - switch (attributePath.attribute.unsignedLongValue) { - case MTRAttributeIDTypeClusterDescriptorAttributePartsListID: - case MTRAttributeIDTypeClusterDescriptorAttributeServerListID: - case MTRAttributeIDTypeClusterDescriptorAttributeDeviceTypeListID: { - return YES; - } - } - } - - // Check for global attributes that affect device configuration. - switch (attributePath.attribute.unsignedLongValue) { - case MTRAttributeIDTypeGlobalAttributeAcceptedCommandListID: - case MTRAttributeIDTypeGlobalAttributeAttributeListID: - case MTRAttributeIDTypeGlobalAttributeClusterRevisionID: - case MTRAttributeIDTypeGlobalAttributeFeatureMapID: - return YES; - } - return NO; -} - -- (void)_removeClusters:(NSSet *)clusterPathsToRemove - doRemoveFromDataStore:(BOOL)doRemoveFromDataStore -{ - os_unfair_lock_assert_owner(&self->_lock); - - [_persistedClusters minusSet:clusterPathsToRemove]; - - for (MTRClusterPath * path in clusterPathsToRemove) { - [_persistedClusterData removeObjectForKey:path]; - [_clusterDataToPersist removeObjectForKey:path]; - if (doRemoveFromDataStore) { - [self.deviceController.controllerDataStore clearStoredClusterDataForNodeID:self.nodeID endpointID:path.endpoint clusterID:path.cluster]; - } - } -} - -- (void)_removeAttributes:(NSSet *)attributes fromCluster:(MTRClusterPath *)clusterPath -{ - os_unfair_lock_assert_owner(&self->_lock); - - for (NSNumber * attribute in attributes) { - [self _removeCachedAttribute:attribute fromCluster:clusterPath]; - } - // Just clear out the NSCache entry for this cluster, so we'll load it from storage as needed. - [_persistedClusterData removeObjectForKey:clusterPath]; - [self.deviceController.controllerDataStore removeAttributes:attributes fromCluster:clusterPath forNodeID:self.nodeID]; -} - -- (void)_pruneEndpointsIn:(MTRDeviceDataValueDictionary)previousPartsListValue - missingFrom:(MTRDeviceDataValueDictionary)newPartsListValue -{ - // If the parts list changed and one or more endpoints were removed, remove all the - // clusters for all those endpoints from our data structures. - // Also remove those endpoints from the data store. - NSMutableSet * toBeRemovedEndpoints = [NSMutableSet setWithArray:[self arrayOfNumbersFromAttributeValue:previousPartsListValue]]; - NSSet * endpointsOnDevice = [NSSet setWithArray:[self arrayOfNumbersFromAttributeValue:newPartsListValue]]; - [toBeRemovedEndpoints minusSet:endpointsOnDevice]; - - for (NSNumber * endpoint in toBeRemovedEndpoints) { - NSMutableSet * clusterPathsToRemove = [[NSMutableSet alloc] init]; - for (MTRClusterPath * path in _persistedClusters) { - if ([path.endpoint isEqualToNumber:endpoint]) { - [clusterPathsToRemove addObject:path]; - } - } - [self _removeClusters:clusterPathsToRemove doRemoveFromDataStore:NO]; - [self.deviceController.controllerDataStore clearStoredClusterDataForNodeID:self.nodeID endpointID:endpoint]; - } -} - -- (void)_pruneClustersIn:(MTRDeviceDataValueDictionary)previousServerListValue - missingFrom:(MTRDeviceDataValueDictionary)newServerListValue - forEndpoint:(NSNumber *)endpointID -{ - // If the server list changed and clusters were removed, remove those clusters from our data structures. - // Also remove them from the data store. - NSMutableSet * toBeRemovedClusters = [NSMutableSet setWithArray:[self arrayOfNumbersFromAttributeValue:previousServerListValue]]; - NSSet * clustersStillOnEndpoint = [NSSet setWithArray:[self arrayOfNumbersFromAttributeValue:newServerListValue]]; - [toBeRemovedClusters minusSet:clustersStillOnEndpoint]; - - NSMutableSet * clusterPathsToRemove = [[NSMutableSet alloc] init]; - for (MTRClusterPath * path in _persistedClusters) { - if ([path.endpoint isEqualToNumber:endpointID] && [toBeRemovedClusters containsObject:path.cluster]) { - [clusterPathsToRemove addObject:path]; - } - } - [self _removeClusters:clusterPathsToRemove doRemoveFromDataStore:YES]; -} - -- (void)_pruneAttributesIn:(MTRDeviceDataValueDictionary)previousAttributeListValue - missingFrom:(MTRDeviceDataValueDictionary)newAttributeListValue - forCluster:(MTRClusterPath *)clusterPath -{ - // If the attribute list changed and attributes were removed, remove the attributes from our - // data structures. - NSMutableSet * toBeRemovedAttributes = [NSMutableSet setWithArray:[self arrayOfNumbersFromAttributeValue:previousAttributeListValue]]; - NSSet * attributesStillInCluster = [NSSet setWithArray:[self arrayOfNumbersFromAttributeValue:newAttributeListValue]]; - - [toBeRemovedAttributes minusSet:attributesStillInCluster]; - [self _removeAttributes:toBeRemovedAttributes fromCluster:clusterPath]; -} - -- (void)_pruneStoredDataForPath:(MTRAttributePath *)attributePath - missingFrom:(MTRDeviceDataValueDictionary)newAttributeDataValue -{ - os_unfair_lock_assert_owner(&self->_lock); - - if (![self _dataStoreExists] && !_clusterDataToPersist.count) { - MTR_LOG_DEBUG("%@ No data store to prune from", self); - return; - } - - // Check if parts list changed or server list changed for the descriptor cluster or the attribute list changed for a cluster. - // If yes, we might need to prune any deleted endpoints, clusters or attributes from the storage and persisted cluster data. - if (attributePath.cluster.unsignedLongValue == MTRClusterIDTypeDescriptorID) { - if (attributePath.attribute.unsignedLongValue == MTRAttributeIDTypeClusterDescriptorAttributePartsListID && [attributePath.endpoint isEqualToNumber:@(kRootEndpointId)]) { - [self _pruneEndpointsIn:[self _cachedAttributeValueForPath:attributePath] missingFrom:newAttributeDataValue]; - return; - } - - if (attributePath.attribute.unsignedLongValue == MTRAttributeIDTypeClusterDescriptorAttributeServerListID) { - [self _pruneClustersIn:[self _cachedAttributeValueForPath:attributePath] missingFrom:newAttributeDataValue forEndpoint:attributePath.endpoint]; - return; - } - } - - if (attributePath.attribute.unsignedLongValue == MTRAttributeIDTypeGlobalAttributeAttributeListID) { - [self _pruneAttributesIn:[self _cachedAttributeValueForPath:attributePath] missingFrom:newAttributeDataValue forCluster:[MTRClusterPath clusterPathWithEndpointID:attributePath.endpoint clusterID:attributePath.cluster]]; - } -} - -// assume lock is held -- (NSArray *)_getAttributesToReportWithReportedValues:(NSArray *> *)reportedAttributeValues fromSubscription:(BOOL)isFromSubscription -{ - os_unfair_lock_assert_owner(&self->_lock); - - NSMutableArray * attributesToReport = [NSMutableArray array]; - NSMutableArray * attributePathsToReport = [NSMutableArray array]; - for (NSDictionary * attributeResponseValue in reportedAttributeValues) { - MTRAttributePath * attributePath = attributeResponseValue[MTRAttributePathKey]; - NSDictionary * attributeDataValue = attributeResponseValue[MTRDataKey]; - NSError * attributeError = attributeResponseValue[MTRErrorKey]; - NSDictionary * previousValue; - - // sanity check either data value or error must exist - if (!attributeDataValue && !attributeError) { - MTR_LOG("%@ report %@ no data value or error: %@", self, attributePath, attributeResponseValue); - continue; - } - - // Additional signal to help mark events as being received during priming report in the event the device rebooted and we get a subscription resumption priming report without noticing it became unreachable first - if (_receivingReport && AttributeHasChangesOmittedQuality(attributePath)) { - _receivingPrimingReport = YES; - } - - // check if value is different than cache, and report if needed - BOOL shouldReportAttribute = NO; - - // if this is an error, report and purge cache - if (attributeError) { - shouldReportAttribute = YES; - previousValue = [self _cachedAttributeValueForPath:attributePath]; - MTR_LOG_ERROR("%@ report %@ error %@ purge expected value %@ read cache %@", self, attributePath, attributeError, - _expectedValueCache[attributePath], previousValue); - _expectedValueCache[attributePath] = nil; - // TODO: Is this clearing business really what we want? - [self _setCachedAttributeValue:nil forPath:attributePath fromSubscription:isFromSubscription]; - } else { - // First separate data version and restore data value to a form without data version - NSNumber * dataVersion = attributeDataValue[MTRDataVersionKey]; - MTRClusterPath * clusterPath = [MTRClusterPath clusterPathWithEndpointID:attributePath.endpoint clusterID:attributePath.cluster]; - if (dataVersion) { - // Remove data version from what we cache in memory - attributeDataValue = [self _dataValueWithoutDataVersion:attributeDataValue]; - } - - previousValue = [self _cachedAttributeValueForPath:attributePath]; -#ifdef DEBUG - __block BOOL readCacheValueChanged = ![self _attributeDataValue:attributeDataValue isEqualToDataValue:previousValue]; -#else - BOOL readCacheValueChanged = ![self _attributeDataValue:attributeDataValue isEqualToDataValue:previousValue]; -#endif - // Now that we have grabbed previousValue, update our cache with the attribute value. - if (readCacheValueChanged) { - if (dataVersion) { - [self _noteDataVersion:dataVersion forClusterPath:clusterPath]; - } - - [self _pruneStoredDataForPath:attributePath missingFrom:attributeDataValue]; - - if (!_deviceConfigurationChanged) { - _deviceConfigurationChanged = [self _attributeAffectsDeviceConfiguration:attributePath]; - if (_deviceConfigurationChanged) { - MTR_LOG("%@ device configuration changed due to changes in attribute %@", self, attributePath); - } - } - - [self _setCachedAttributeValue:attributeDataValue forPath:attributePath fromSubscription:isFromSubscription]; - } - -#ifdef DEBUG - // Unit test only code. - if (!readCacheValueChanged) { - [self _callFirstDelegateSynchronouslyWithBlock:^(id delegate) { - if ([delegate respondsToSelector:@selector(unitTestForceAttributeReportsIfMatchingCache:)]) { - readCacheValueChanged = [delegate unitTestForceAttributeReportsIfMatchingCache:self]; - } - }]; - } -#endif // DEBUG - - NSArray * expectedValue = _expectedValueCache[attributePath]; - - // Report the attribute if a read would get a changed value. This happens - // when our cached value changes and no expected value exists. - if (readCacheValueChanged && !expectedValue) { - shouldReportAttribute = YES; - } - - if (!shouldReportAttribute) { - // If an expected value exists, the attribute will not be reported at this time. - // When the expected value interval expires, the correct value will be reported, - // if needed. - if (expectedValue) { - MTR_LOG("%@ report %@ value filtered - expected value still present", self, attributePath); - } else { - MTR_LOG("%@ report %@ value filtered - same as read cache", self, attributePath); - } - } - - // If General Diagnostics UpTime attribute, update the estimated start time as needed. - if ((attributePath.cluster.unsignedLongValue == MTRClusterGeneralDiagnosticsID) - && (attributePath.attribute.unsignedLongValue == MTRClusterGeneralDiagnosticsAttributeUpTimeID)) { - // verify that the uptime is indeed the data type we want - if ([attributeDataValue[MTRTypeKey] isEqual:MTRUnsignedIntegerValueType]) { - NSNumber * upTimeNumber = attributeDataValue[MTRValueKey]; - NSTimeInterval upTime = upTimeNumber.unsignedLongLongValue; // UpTime unit is defined as seconds in the spec - NSDate * potentialSystemStartTime = [NSDate dateWithTimeIntervalSinceNow:-upTime]; - NSDate * oldSystemStartTime = _estimatedStartTime; - if (!_estimatedStartTime || ([potentialSystemStartTime compare:_estimatedStartTime] == NSOrderedAscending)) { - MTR_LOG("%@ General Diagnostics UpTime %.3lf: estimated start time %@ => %@", self, upTime, - oldSystemStartTime, potentialSystemStartTime); - _estimatedStartTime = potentialSystemStartTime; - } - - // Save estimate in the subscription resumption case, for when StartUp event uses it - _estimatedStartTimeFromGeneralDiagnosticsUpTime = potentialSystemStartTime; - } - } - } - - if (shouldReportAttribute) { - if (previousValue) { - NSMutableDictionary * mutableAttributeResponseValue = attributeResponseValue.mutableCopy; - mutableAttributeResponseValue[MTRPreviousDataKey] = previousValue; - [attributesToReport addObject:mutableAttributeResponseValue]; - } else { - [attributesToReport addObject:attributeResponseValue]; - } - [attributePathsToReport addObject:attributePath]; - } - } - - if (attributePathsToReport.count > 0) { - MTR_LOG("%@ report from reported values %@", self, attributePathsToReport); - } - - return attributesToReport; -} - #ifdef DEBUG - (NSUInteger)unitTestAttributeCount { @@ -2872,44 +2172,8 @@ - (nullable NSNumber *)_informationalProductID return [self _informationalNumberAtAttributePath:productIDPath]; } -- (void)_addInformationalAttributesToCurrentMetricScope -{ - os_unfair_lock_assert_owner(&self->_lock); - - using namespace chip::Tracing::DarwinFramework; - MATTER_LOG_METRIC(kMetricDeviceVendorID, [self _informationalVendorID].unsignedShortValue); - MATTER_LOG_METRIC(kMetricDeviceProductID, [self _informationalProductID].unsignedShortValue); - BOOL usesThread = [self _deviceUsesThread]; - MATTER_LOG_METRIC(kMetricDeviceUsesThread, usesThread); -} - #pragma mark - Description handling -- (BOOL)_attributePathAffectsDescriptionData:(MTRAttributePath *)path -{ - // Technically this does not need to be called while locked, but in - // practice it is, and we want to make sure it's clear that this function - // should never start taking our data lock. - os_unfair_lock_assert_owner(&_lock); - - switch (path.cluster.unsignedLongLongValue) { - case MTRClusterIDTypeBasicInformationID: { - switch (path.attribute.unsignedLongLongValue) { - case MTRAttributeIDTypeClusterBasicInformationAttributeVendorIDID: - case MTRAttributeIDTypeClusterBasicInformationAttributeProductIDID: - return YES; - default: - return NO; - } - } - case MTRClusterIDTypeNetworkCommissioningID: { - return path.attribute.unsignedLongLongValue == MTRAttributeIDTypeGlobalAttributeFeatureMapID; - } - default: - return NO; - } -} - - (void)_updateAttributeDependentDescriptionData { os_unfair_lock_assert_owner(&_lock); From 4c78d079ea30b1dedc2293e293b56d014b4b2a04 Mon Sep 17 00:00:00 2001 From: Karsten Sperling <113487422+ksperling-apple@users.noreply.github.com> Date: Tue, 27 Aug 2024 13:26:19 +1200 Subject: [PATCH 26/32] darwin-framework-tool: Add support for paseonly pairing (#35206) This includes code-paseonly and by-index-paseonly commands, as as interacting with devices over PASE by preferring an existing MTRBaseDevice connected over PASE to creating a new CASE connection when looking up a device by node id. --- .../commands/clusters/ModelCommandBridge.mm | 5 +- .../commands/common/CHIPCommandBridge.h | 7 ++- .../commands/common/CHIPCommandBridge.mm | 8 +++ .../delay/WaitForCommissioneeCommand.mm | 5 +- .../commands/pairing/Commands.h | 34 ++++++++--- .../pairing/DeviceControllerDelegateBridge.mm | 5 ++ .../commands/pairing/PairingCommandBridge.h | 39 ++++++------ .../commands/pairing/PairingCommandBridge.mm | 60 ++++++++++--------- 8 files changed, 98 insertions(+), 65 deletions(-) diff --git a/examples/darwin-framework-tool/commands/clusters/ModelCommandBridge.mm b/examples/darwin-framework-tool/commands/clusters/ModelCommandBridge.mm index 97b7c09806d200..47a29f80c3a4e5 100644 --- a/examples/darwin-framework-tool/commands/clusters/ModelCommandBridge.mm +++ b/examples/darwin-framework-tool/commands/clusters/ModelCommandBridge.mm @@ -25,9 +25,10 @@ CHIP_ERROR ModelCommand::RunCommand() { - MTRDeviceController * commissioner = CurrentCommissioner(); ChipLogProgress(chipTool, "Sending command to node 0x" ChipLogFormatX64, ChipLogValueX64(mNodeId)); - auto * device = [MTRBaseDevice deviceWithNodeID:@(mNodeId) controller:commissioner]; + auto * device = BaseDeviceWithNodeId(mNodeId); + VerifyOrReturnError(device != nil, CHIP_ERROR_INCORRECT_STATE); + CHIP_ERROR err = SendCommand(device, mEndPointId); if (err != CHIP_NO_ERROR) { diff --git a/examples/darwin-framework-tool/commands/common/CHIPCommandBridge.h b/examples/darwin-framework-tool/commands/common/CHIPCommandBridge.h index 6cb580ded9c35f..9458d304dbebb2 100644 --- a/examples/darwin-framework-tool/commands/common/CHIPCommandBridge.h +++ b/examples/darwin-framework-tool/commands/common/CHIPCommandBridge.h @@ -17,6 +17,7 @@ */ #pragma once + #import #include #include @@ -26,8 +27,6 @@ #include "../provider/OTAProviderDelegate.h" -#pragma once - inline constexpr char kIdentityAlpha[] = "alpha"; inline constexpr char kIdentityBeta[] = "beta"; inline constexpr char kIdentityGamma[] = "gamma"; @@ -91,6 +90,10 @@ class CHIPCommandBridge : public Command { MTRDeviceController * GetCommissioner(const char * identity); + // Returns the MTRBaseDevice for the specified node ID. + // Will utilize an existing PASE connection if the device is being commissioned. + MTRBaseDevice * BaseDeviceWithNodeId(chip::NodeId nodeId); + // Will log the given string and given error (as progress if success, error // if failure). void LogNSError(const char * logString, NSError * error); diff --git a/examples/darwin-framework-tool/commands/common/CHIPCommandBridge.mm b/examples/darwin-framework-tool/commands/common/CHIPCommandBridge.mm index bb232e3772135e..8b9a5d3e9f1eb2 100644 --- a/examples/darwin-framework-tool/commands/common/CHIPCommandBridge.mm +++ b/examples/darwin-framework-tool/commands/common/CHIPCommandBridge.mm @@ -195,6 +195,14 @@ MTRDeviceController * CHIPCommandBridge::GetCommissioner(const char * identity) { return mControllers[identity]; } +MTRBaseDevice * CHIPCommandBridge::BaseDeviceWithNodeId(chip::NodeId nodeId) +{ + MTRDeviceController * controller = CurrentCommissioner(); + VerifyOrReturnValue(controller != nil, nil); + return [controller deviceBeingCommissionedWithNodeID:@(nodeId) error:nullptr] + ?: [MTRBaseDevice deviceWithNodeID:@(nodeId) controller:controller]; +} + void CHIPCommandBridge::StopCommissioners() { for (auto & pair : mControllers) { diff --git a/examples/darwin-framework-tool/commands/delay/WaitForCommissioneeCommand.mm b/examples/darwin-framework-tool/commands/delay/WaitForCommissioneeCommand.mm index 6bc25defa21d1a..f3c15c98e8e0b0 100644 --- a/examples/darwin-framework-tool/commands/delay/WaitForCommissioneeCommand.mm +++ b/examples/darwin-framework-tool/commands/delay/WaitForCommissioneeCommand.mm @@ -22,10 +22,7 @@ CHIP_ERROR WaitForCommissioneeCommand::RunCommand() { - MTRDeviceController * commissioner = CurrentCommissioner(); - VerifyOrReturnError(nil != commissioner, CHIP_ERROR_INCORRECT_STATE); - - auto * base_device = [MTRBaseDevice deviceWithNodeID:@(mNodeId) controller:commissioner]; + auto * base_device = BaseDeviceWithNodeId(mNodeId); VerifyOrReturnError(base_device != nil, CHIP_ERROR_INCORRECT_STATE); if (mExpireExistingSession.ValueOr(true)) { diff --git a/examples/darwin-framework-tool/commands/pairing/Commands.h b/examples/darwin-framework-tool/commands/pairing/Commands.h index bc1bccdfd0ce43..4923a55105c68d 100644 --- a/examples/darwin-framework-tool/commands/pairing/Commands.h +++ b/examples/darwin-framework-tool/commands/pairing/Commands.h @@ -28,38 +28,52 @@ class PairCode : public PairingCommandBridge { public: - PairCode() : PairingCommandBridge("code", PairingMode::Code, PairingNetworkType::None) {} + PairCode() : PairingCommandBridge("code", PairingMode::Code, CommissioningType::NoNetwork) {} +}; + +class PairCodePASEOnly : public PairingCommandBridge +{ +public: + PairCodePASEOnly() : PairingCommandBridge("code-paseonly", PairingMode::Code, CommissioningType::None) {} }; class PairCodeWifi : public PairingCommandBridge { public: - PairCodeWifi() : PairingCommandBridge("code-wifi", PairingMode::Code, PairingNetworkType::WiFi) {} + PairCodeWifi() : PairingCommandBridge("code-wifi", PairingMode::Code, CommissioningType::WiFi) {} }; class PairCodeThread : public PairingCommandBridge { public: - PairCodeThread() : PairingCommandBridge("code-thread", PairingMode::Code, PairingNetworkType::Thread) {} + PairCodeThread() : PairingCommandBridge("code-thread", PairingMode::Code, CommissioningType::Thread) {} }; class PairBleWiFi : public PairingCommandBridge { public: - PairBleWiFi() : PairingCommandBridge("ble-wifi", PairingMode::Ble, PairingNetworkType::WiFi) {} + PairBleWiFi() : PairingCommandBridge("ble-wifi", PairingMode::Ble, CommissioningType::WiFi) {} }; class PairBleThread : public PairingCommandBridge { public: - PairBleThread() : PairingCommandBridge("ble-thread", PairingMode::Ble, PairingNetworkType::Thread) {} + PairBleThread() : PairingCommandBridge("ble-thread", PairingMode::Ble, CommissioningType::Thread) {} }; class PairAlreadyDiscoveredByIndex : public PairingCommandBridge { public: PairAlreadyDiscoveredByIndex() : - PairingCommandBridge("by-index", PairingMode::AlreadyDiscoveredByIndex, PairingNetworkType::None) + PairingCommandBridge("by-index", PairingMode::AlreadyDiscoveredByIndex, CommissioningType::NoNetwork) + {} +}; + +class PairAlreadyDiscoveredByIndexPASEOnly : public PairingCommandBridge +{ +public: + PairAlreadyDiscoveredByIndexPASEOnly() : + PairingCommandBridge("by-index-paseonly", PairingMode::AlreadyDiscoveredByIndex, CommissioningType::None) {} }; @@ -67,7 +81,7 @@ class PairAlreadyDiscoveredByIndexWithWiFi : public PairingCommandBridge { public: PairAlreadyDiscoveredByIndexWithWiFi() : - PairingCommandBridge("by-index-with-wifi", PairingMode::AlreadyDiscoveredByIndex, PairingNetworkType::WiFi) + PairingCommandBridge("by-index-with-wifi", PairingMode::AlreadyDiscoveredByIndex, CommissioningType::WiFi) {} }; @@ -75,14 +89,14 @@ class PairAlreadyDiscoveredByIndexWithThread : public PairingCommandBridge { public: PairAlreadyDiscoveredByIndexWithThread() : - PairingCommandBridge("by-index-with-thread", PairingMode::AlreadyDiscoveredByIndex, PairingNetworkType::Thread) + PairingCommandBridge("by-index-with-thread", PairingMode::AlreadyDiscoveredByIndex, CommissioningType::Thread) {} }; class Unpair : public PairingCommandBridge { public: - Unpair() : PairingCommandBridge("unpair", PairingMode::None, PairingNetworkType::None) {} + Unpair() : PairingCommandBridge("unpair", PairingMode::Unpair, CommissioningType::None) {} }; void registerCommandsPairing(Commands & commands) @@ -91,11 +105,13 @@ void registerCommandsPairing(Commands & commands) commands_list clusterCommands = { make_unique(), + make_unique(), make_unique(), make_unique(), make_unique(), make_unique(), make_unique(), + make_unique(), make_unique(), make_unique(), make_unique(), diff --git a/examples/darwin-framework-tool/commands/pairing/DeviceControllerDelegateBridge.mm b/examples/darwin-framework-tool/commands/pairing/DeviceControllerDelegateBridge.mm index 52f0fd4bb10a17..92ff56a2c72b3d 100644 --- a/examples/darwin-framework-tool/commands/pairing/DeviceControllerDelegateBridge.mm +++ b/examples/darwin-framework-tool/commands/pairing/DeviceControllerDelegateBridge.mm @@ -50,6 +50,11 @@ - (void)controller:(MTRDeviceController *)controller commissioningSessionEstabli } ChipLogProgress(chipTool, "Pairing Success"); ChipLogProgress(chipTool, "PASE establishment successful"); + if (_params == nil) { + _commandBridge->SetCommandExitStatus(nil); + return; + } + NSError * commissionError; [_commissioner commissionNodeWithID:@(_deviceID) commissioningParams:_params error:&commissionError]; if (commissionError != nil) { diff --git a/examples/darwin-framework-tool/commands/pairing/PairingCommandBridge.h b/examples/darwin-framework-tool/commands/pairing/PairingCommandBridge.h index 4ca6c4c2b73582..7502ba653d11fb 100644 --- a/examples/darwin-framework-tool/commands/pairing/PairingCommandBridge.h +++ b/examples/darwin-framework-tool/commands/pairing/PairingCommandBridge.h @@ -22,15 +22,16 @@ enum class PairingMode { - None, + Unpair, Code, Ble, AlreadyDiscoveredByIndex, }; -enum class PairingNetworkType +enum class CommissioningType { - None, + None, // establish PASE only + NoNetwork, WiFi, Thread, Ethernet, @@ -39,27 +40,28 @@ enum class PairingNetworkType class PairingCommandBridge : public CHIPCommandBridge { public: - PairingCommandBridge(const char * commandName, PairingMode mode, PairingNetworkType networkType) : - CHIPCommandBridge(commandName), mPairingMode(mode), mNetworkType(networkType) + PairingCommandBridge(const char * commandName, PairingMode mode, CommissioningType commissioningType) : + CHIPCommandBridge(commandName), mPairingMode(mode), mCommissioningType(commissioningType) { AddArgument("node-id", 0, UINT64_MAX, &mNodeId); - switch (networkType) + switch (commissioningType) { - case PairingNetworkType::None: - case PairingNetworkType::Ethernet: + case CommissioningType::None: + case CommissioningType::NoNetwork: + case CommissioningType::Ethernet: break; - case PairingNetworkType::WiFi: + case CommissioningType::WiFi: AddArgument("ssid", &mSSID); AddArgument("password", &mPassword); break; - case PairingNetworkType::Thread: + case CommissioningType::Thread: AddArgument("operationalDataset", &mOperationalDataset); break; } switch (mode) { - case PairingMode::None: + case PairingMode::Unpair: break; case PairingMode::Code: AddArgument("payload", &mOnboardingPayload); @@ -74,17 +76,16 @@ class PairingCommandBridge : public CHIPCommandBridge break; } - if (mode != PairingMode::None) + if (commissioningType != CommissioningType::None) { AddArgument("country-code", &mCountryCode, "Country code to use to set the Basic Information cluster's Location attribute"); + AddArgument("use-device-attestation-delegate", 0, 1, &mUseDeviceAttestationDelegate, + "If true, use a device attestation delegate that always wants to be notified about attestation results. " + "Defaults to false."); + AddArgument("device-attestation-failsafe-time", 0, UINT16_MAX, &mDeviceAttestationFailsafeTime, + "If set, the time to extend the failsafe to before calling the device attestation delegate"); } - - AddArgument("use-device-attestation-delegate", 0, 1, &mUseDeviceAttestationDelegate, - "If true, use a device attestation delegate that always wants to be notified about attestation results. " - "Defaults to false."); - AddArgument("device-attestation-failsafe-time", 0, UINT16_MAX, &mDeviceAttestationFailsafeTime, - "If set, the time to extend the failsafe to before calling the device attestation delegate"); } /////////// CHIPCommandBridge Interface ///////// @@ -99,7 +100,7 @@ class PairingCommandBridge : public CHIPCommandBridge void SetUpDeviceControllerDelegate(); const PairingMode mPairingMode; - const PairingNetworkType mNetworkType; + const CommissioningType mCommissioningType; chip::ByteSpan mOperationalDataset; chip::ByteSpan mSSID; chip::ByteSpan mPassword; diff --git a/examples/darwin-framework-tool/commands/pairing/PairingCommandBridge.mm b/examples/darwin-framework-tool/commands/pairing/PairingCommandBridge.mm index 2ab95a9f5b5e68..122cb24184cc99 100644 --- a/examples/darwin-framework-tool/commands/pairing/PairingCommandBridge.mm +++ b/examples/darwin-framework-tool/commands/pairing/PairingCommandBridge.mm @@ -48,40 +48,43 @@ - (void)deviceAttestationCompletedForController:(MTRDeviceController *)controlle void PairingCommandBridge::SetUpDeviceControllerDelegate() { - dispatch_queue_t callbackQueue = dispatch_queue_create("com.chip.pairing", DISPATCH_QUEUE_SERIAL); CHIPToolDeviceControllerDelegate * deviceControllerDelegate = [[CHIPToolDeviceControllerDelegate alloc] init]; - MTRCommissioningParameters * params = [[MTRCommissioningParameters alloc] init]; - MTRDeviceController * commissioner = CurrentCommissioner(); - + [deviceControllerDelegate setCommandBridge:this]; [deviceControllerDelegate setDeviceID:mNodeId]; - switch (mNetworkType) { - case PairingNetworkType::None: - case PairingNetworkType::Ethernet: - break; - case PairingNetworkType::WiFi: - [params setWifiSSID:[NSData dataWithBytes:mSSID.data() length:mSSID.size()]]; - [params setWifiCredentials:[NSData dataWithBytes:mPassword.data() length:mPassword.size()]]; - break; - case PairingNetworkType::Thread: - [params setThreadOperationalDataset:[NSData dataWithBytes:mOperationalDataset.data() length:mOperationalDataset.size()]]; - break; - } - if (mUseDeviceAttestationDelegate.ValueOr(false)) { - params.deviceAttestationDelegate = [[NoOpAttestationDelegate alloc] init]; - if (mDeviceAttestationFailsafeTime.HasValue()) { - params.failSafeTimeout = @(mDeviceAttestationFailsafeTime.Value()); + if (mCommissioningType != CommissioningType::None) { + MTRCommissioningParameters * params = [[MTRCommissioningParameters alloc] init]; + switch (mCommissioningType) { + case CommissioningType::None: + case CommissioningType::NoNetwork: + case CommissioningType::Ethernet: + break; + case CommissioningType::WiFi: + [params setWifiSSID:[NSData dataWithBytes:mSSID.data() length:mSSID.size()]]; + [params setWifiCredentials:[NSData dataWithBytes:mPassword.data() length:mPassword.size()]]; + break; + case CommissioningType::Thread: + [params setThreadOperationalDataset:[NSData dataWithBytes:mOperationalDataset.data() length:mOperationalDataset.size()]]; + break; } - } - if (mCountryCode.HasValue()) { - params.countryCode = [NSString stringWithUTF8String:mCountryCode.Value()]; + if (mUseDeviceAttestationDelegate.ValueOr(false)) { + params.deviceAttestationDelegate = [[NoOpAttestationDelegate alloc] init]; + if (mDeviceAttestationFailsafeTime.HasValue()) { + params.failSafeTimeout = @(mDeviceAttestationFailsafeTime.Value()); + } + } + + if (mCountryCode.HasValue()) { + params.countryCode = [NSString stringWithUTF8String:mCountryCode.Value()]; + } + + [deviceControllerDelegate setParams:params]; } - [deviceControllerDelegate setCommandBridge:this]; - [deviceControllerDelegate setParams:params]; + MTRDeviceController * commissioner = CurrentCommissioner(); [deviceControllerDelegate setCommissioner:commissioner]; - + dispatch_queue_t callbackQueue = dispatch_queue_create("com.chip.pairing", DISPATCH_QUEUE_SERIAL); [commissioner setDeviceControllerDelegate:deviceControllerDelegate queue:callbackQueue]; } @@ -89,7 +92,7 @@ - (void)deviceAttestationCompletedForController:(MTRDeviceController *)controlle { NSError * error; switch (mPairingMode) { - case PairingMode::None: + case PairingMode::Unpair: Unpair(); break; case PairingMode::Code: @@ -155,8 +158,7 @@ - (void)deviceAttestationCompletedForController:(MTRDeviceController *)controlle void PairingCommandBridge::Unpair() { dispatch_queue_t callbackQueue = dispatch_queue_create("com.chip-tool.command", DISPATCH_QUEUE_SERIAL); - MTRDeviceController * commissioner = CurrentCommissioner(); - auto * device = [MTRBaseDevice deviceWithNodeID:@(mNodeId) controller:commissioner]; + auto * device = BaseDeviceWithNodeId(mNodeId); ChipLogProgress(chipTool, "Attempting to unpair device %llu", mNodeId); MTRBaseClusterOperationalCredentials * opCredsCluster = From ea679d2dc674f576f4d391d1d71af1489010e580 Mon Sep 17 00:00:00 2001 From: Wang Qixiang <43193572+wqx6@users.noreply.github.com> Date: Tue, 27 Aug 2024 11:36:52 +0800 Subject: [PATCH 27/32] esp32: update esp_rcp_update version to 1.2.0 (#35114) * esp32: update esp_rcp_update version to 1.2.0 * change for ESP32ThreadInit * fix compile error when lwip ipv6 address number > 8 --- .../esp32/components/chip/idf_component.yml | 2 +- .../platform/esp32/common/Esp32ThreadInit.cpp | 4 +- .../platform/esp32/common/Esp32ThreadInit.h | 4 +- .../thread-br-app/esp32/create_ota_image.py | 92 ------------------- .../thread-br-app/esp32/main/CMakeLists.txt | 10 -- .../thread-br-app/esp32/sdkconfig.defaults | 5 +- .../ESP32/DiagnosticDataProviderImpl.cpp | 3 +- src/platform/ESP32/OpenthreadLauncher.cpp | 14 ++- src/platform/ESP32/OpenthreadLauncher.h | 6 +- 9 files changed, 19 insertions(+), 121 deletions(-) delete mode 100755 examples/thread-br-app/esp32/create_ota_image.py diff --git a/config/esp32/components/chip/idf_component.yml b/config/esp32/components/chip/idf_component.yml index b56040ea3aaa07..aeffec708dba8c 100644 --- a/config/esp32/components/chip/idf_component.yml +++ b/config/esp32/components/chip/idf_component.yml @@ -27,7 +27,7 @@ dependencies: - if: "target != esp32h2" espressif/esp_rcp_update: - version: "1.0.3" + version: "1.2.0" rules: - if: "idf_version >=5.0" diff --git a/examples/platform/esp32/common/Esp32ThreadInit.cpp b/examples/platform/esp32/common/Esp32ThreadInit.cpp index f02f91c06d5538..985f7a3d4ac31b 100644 --- a/examples/platform/esp32/common/Esp32ThreadInit.cpp +++ b/examples/platform/esp32/common/Esp32ThreadInit.cpp @@ -42,7 +42,7 @@ void ESPOpenThreadInit() .host_config = ESP_OPENTHREAD_DEFAULT_HOST_CONFIG(), .port_config = ESP_OPENTHREAD_DEFAULT_PORT_CONFIG(), }; -#ifdef CONFIG_OPENTHREAD_BORDER_ROUTER +#if defined(CONFIG_OPENTHREAD_BORDER_ROUTER) && defined(CONFIG_AUTO_UPDATE_RCP) esp_vfs_spiffs_conf_t rcp_fw_conf = { .base_path = "/rcp_fw", .partition_label = "rcp_fw", .max_files = 10, .format_if_mount_failed = false }; @@ -53,7 +53,7 @@ void ESPOpenThreadInit() } esp_rcp_update_config_t rcp_update_config = ESP_OPENTHREAD_RCP_UPDATE_CONFIG(); openthread_init_br_rcp(&rcp_update_config); -#endif // CONFIG_OPENTHREAD_BORDER_ROUTER +#endif // CONFIG_OPENTHREAD_BORDER_ROUTER && CONFIG_AUTO_UPDATE_RCP set_openthread_platform_config(&config); if (ThreadStackMgr().InitThreadStack() != CHIP_NO_ERROR) diff --git a/examples/platform/esp32/common/Esp32ThreadInit.h b/examples/platform/esp32/common/Esp32ThreadInit.h index affca51392f65a..a29fd5ff62afa8 100644 --- a/examples/platform/esp32/common/Esp32ThreadInit.h +++ b/examples/platform/esp32/common/Esp32ThreadInit.h @@ -84,7 +84,7 @@ .storage_partition_name = "nvs", .netif_queue_size = 10, .task_queue_size = 10, \ } -#ifdef CONFIG_OPENTHREAD_BORDER_ROUTER +#if defined(CONFIG_OPENTHREAD_BORDER_ROUTER) && defined(CONFIG_AUTO_UPDATE_RCP) #include #define RCP_FIRMWARE_DIR "/spiffs/ot_rcp" @@ -93,7 +93,7 @@ .rcp_type = RCP_TYPE_ESP32H2_UART, .uart_rx_pin = 17, .uart_tx_pin = 18, .uart_port = 1, .uart_baudrate = 115200, \ .reset_pin = 7, .boot_pin = 8, .update_baudrate = 460800, .firmware_dir = "/rcp_fw/ot_rcp", .target_chip = ESP32H2_CHIP, \ } -#endif // CONFIG_OPENTHREAD_BORDER_ROUTER +#endif // CONFIG_OPENTHREAD_BORDER_ROUTER && CONFIG_AUTO_UPDATE_RCP #endif // CONFIG_OPENTHREAD_ENABLED diff --git a/examples/thread-br-app/esp32/create_ota_image.py b/examples/thread-br-app/esp32/create_ota_image.py deleted file mode 100755 index 56f7caa81f1b85..00000000000000 --- a/examples/thread-br-app/esp32/create_ota_image.py +++ /dev/null @@ -1,92 +0,0 @@ -#!/usr/bin/env python3 - -import argparse -import os -import pathlib -import struct - -FILETAG_RCP_VERSION = 0 -FILETAG_RCP_FLASH_ARGS = 1 -FILETAG_RCP_BOOTLOADER = 2 -FILETAG_RCP_PARTITION_TABLE = 3 -FILETAG_RCP_FIRMWARE = 4 -FILETAG_BR_OTA_IMAGE = 5 -FILETAG_IMAGE_HEADER = 0xff - -HEADER_ENTRY_SIZE = 3 * 4 -RCP_IMAGE_HEADER_SIZE = HEADER_ENTRY_SIZE * 6 -RCP_FLASH_ARGS_SIZE = 2 * 4 * 3 - - -def append_subfile_header(fout, tag, size, offset): - fout.write(struct.pack(' 0: - fout.write(data) - data = fin.read(buf_size) - - -def append_flash_args(fout, flash_args_path): - with open(flash_args_path, 'r') as f: - # skip first line - next(f) - partition_info_list = [line.split() for line in f] - for offset, partition_file in partition_info_list: - offset = int(offset, 0) - if partition_file.find('bootloader') >= 0: - fout.write(struct.pack('= 0: - fout.write(struct.pack('= LWIP_IPV6_NUM_ADDRESSES, "Not enough space for our addresses."); auto addr_count = esp_netif_get_all_ip6(ifa, ip6_addr); if (addr_count < 0) { diff --git a/src/platform/ESP32/OpenthreadLauncher.cpp b/src/platform/ESP32/OpenthreadLauncher.cpp index d40e228954c5ee..63f1bac825214f 100644 --- a/src/platform/ESP32/OpenthreadLauncher.cpp +++ b/src/platform/ESP32/OpenthreadLauncher.cpp @@ -138,7 +138,7 @@ static void ot_task_worker(void * context) vTaskDelete(NULL); } -#ifdef CONFIG_OPENTHREAD_BORDER_ROUTER +#if defined(CONFIG_OPENTHREAD_BORDER_ROUTER) && defined(CONFIG_AUTO_UPDATE_RCP) static constexpr size_t kRcpVersionMaxSize = 100; static const char * TAG = "RCP_UPDATE"; @@ -183,16 +183,14 @@ static void try_update_ot_rcp(const esp_openthread_platform_config_t * config) esp_restart(); } } -#endif // CONFIG_OPENTHREAD_BORDER_ROUTER static void rcp_failure_handler(void) { -#ifdef CONFIG_OPENTHREAD_BORDER_ROUTER esp_rcp_mark_image_unusable(); try_update_ot_rcp(s_platform_config); -#endif // CONFIG_OPENTHREAD_BORDER_ROUTER esp_rcp_reset(); } +#endif // CONFIG_OPENTHREAD_BORDER_ROUTER && CONFIG_AUTO_UPDATE_RCP esp_err_t set_openthread_platform_config(esp_openthread_platform_config_t * config) { @@ -208,7 +206,7 @@ esp_err_t set_openthread_platform_config(esp_openthread_platform_config_t * conf return ESP_OK; } -#ifdef CONFIG_OPENTHREAD_BORDER_ROUTER +#if defined(CONFIG_OPENTHREAD_BORDER_ROUTER) && defined(CONFIG_AUTO_UPDATE_RCP) esp_err_t openthread_init_br_rcp(const esp_rcp_update_config_t * update_config) { esp_err_t err = ESP_OK; @@ -219,7 +217,7 @@ esp_err_t openthread_init_br_rcp(const esp_rcp_update_config_t * update_config) esp_openthread_register_rcp_failure_handler(rcp_failure_handler); return err; } -#endif // CONFIG_OPENTHREAD_BORDER_ROUTER +#endif // CONFIG_OPENTHREAD_BORDER_ROUTER && CONFIG_AUTO_UPDATE_RCP esp_err_t openthread_init_stack(void) { @@ -236,9 +234,9 @@ esp_err_t openthread_init_stack(void) assert(s_platform_config); // Initialize the OpenThread stack ESP_ERROR_CHECK(esp_openthread_init(s_platform_config)); -#ifdef CONFIG_OPENTHREAD_BORDER_ROUTER +#if defined(CONFIG_OPENTHREAD_BORDER_ROUTER) && defined(CONFIG_AUTO_UPDATE_RCP) try_update_ot_rcp(s_platform_config); -#endif // CONFIG_OPENTHREAD_BORDER_ROUTER +#endif // CONFIG_OPENTHREAD_BORDER_ROUTER && CONFIG_AUTO_UPDATE_RCP #ifdef CONFIG_OPENTHREAD_CLI esp_openthread_matter_cli_init(); cli_command_transmit_task(); diff --git a/src/platform/ESP32/OpenthreadLauncher.h b/src/platform/ESP32/OpenthreadLauncher.h index f6a7eb5d210cec..a15125f948d1dc 100644 --- a/src/platform/ESP32/OpenthreadLauncher.h +++ b/src/platform/ESP32/OpenthreadLauncher.h @@ -19,13 +19,13 @@ #include #include -#include #include #include -#ifdef CONFIG_OPENTHREAD_BORDER_ROUTER +#if defined(CONFIG_OPENTHREAD_BORDER_ROUTER) && defined(CONFIG_AUTO_UPDATE_RCP) +#include esp_err_t openthread_init_br_rcp(const esp_rcp_update_config_t * update_config); -#endif +#endif // CONFIG_OPENTHREAD_BORDER_ROUTER && CONFIG_AUTO_UPDATE_RCP esp_err_t set_openthread_platform_config(esp_openthread_platform_config_t * config); esp_err_t openthread_init_stack(void); esp_err_t openthread_launch_task(void); From 37137158f6d96e6531e715cd89bfaed78e99b9b4 Mon Sep 17 00:00:00 2001 From: Nivi Sarkar <55898241+nivi-apple@users.noreply.github.com> Date: Mon, 26 Aug 2024 23:07:11 -0700 Subject: [PATCH 28/32] =?UTF-8?q?Fix=20the=20PICS=20code=20for=20atomic=20?= =?UTF-8?q?response=20to=20be=20lower=20case=20and=20update=20the=E2=80=A6?= =?UTF-8?q?=20(#35204)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fix the PICS code for atomic response to be lower case and update the PICS code for the Generated command list for the presets and schedules feature * Updated the PICS code for atomic response in PICS.yaml --- src/app/tests/suites/certification/PICS.yaml | 2 +- src/app/tests/suites/certification/Test_TC_TSTAT_1_1.yaml | 5 +++-- src/app/tests/suites/certification/ci-pics-values | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/app/tests/suites/certification/PICS.yaml b/src/app/tests/suites/certification/PICS.yaml index c575bd348fc518..be995be90b30b7 100644 --- a/src/app/tests/suites/certification/PICS.yaml +++ b/src/app/tests/suites/certification/PICS.yaml @@ -6541,7 +6541,7 @@ PICS: id: TSTAT.S.C01.Tx - label: "Does the device implement sending the AtomicResponse command?" - id: TSTAT.S.CFD.Tx + id: TSTAT.S.Cfd.Tx # # server / features diff --git a/src/app/tests/suites/certification/Test_TC_TSTAT_1_1.yaml b/src/app/tests/suites/certification/Test_TC_TSTAT_1_1.yaml index 35da0e04be380f..dc16d0d8a6c4bb 100644 --- a/src/app/tests/suites/certification/Test_TC_TSTAT_1_1.yaml +++ b/src/app/tests/suites/certification/Test_TC_TSTAT_1_1.yaml @@ -627,8 +627,9 @@ tests: contains: [6, 254] - label: - "Step 7a: TH reads from the DUT the GeneratedCommandList attribute." - PICS: " !TSTAT.S.C04.Rsp && !TSTAT.S.C02.Rsp " + "Step 7a: TH reads Feature dependent(TSTAT.S.F08(PRES)) commands in + the GeneratedCommandList attribute." + PICS: TSTAT.S.Cfe.Rsp command: "readAttribute" attribute: "GeneratedCommandList" response: diff --git a/src/app/tests/suites/certification/ci-pics-values b/src/app/tests/suites/certification/ci-pics-values index 9109f30bec4d73..373f8e3c9aea9c 100644 --- a/src/app/tests/suites/certification/ci-pics-values +++ b/src/app/tests/suites/certification/ci-pics-values @@ -2001,7 +2001,7 @@ TSTAT.S.C03.Rsp=0 TSTAT.S.C04.Rsp=0 TSTAT.S.C06.Rsp=1 TSTAT.S.Cfe.Rsp=1 -TSTAT.S.CFD.Tx=1 +TSTAT.S.Cfd.Tx=1 # Client TSTAT.C=0 From c305e3f60cb7b5b491b64cb550a1a3d16fb0fc69 Mon Sep 17 00:00:00 2001 From: C Freeman Date: Tue, 27 Aug 2024 02:43:05 -0400 Subject: [PATCH 29/32] TC-LVL-2.3: Update expected number of reports (#35179) please see https://github.com/CHIP-Specifications/chip-test-plans/pull/4456#discussion_r1715722836 --- src/python_testing/TC_LVL_2_3.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/python_testing/TC_LVL_2_3.py b/src/python_testing/TC_LVL_2_3.py index f3fac953b4d715..d45c459f1352d3 100644 --- a/src/python_testing/TC_LVL_2_3.py +++ b/src/python_testing/TC_LVL_2_3.py @@ -57,8 +57,8 @@ def steps_TC_LVL_2_3(self) -> list[TestStep]: TestStep(7, f"{THcommand} MoveToLevelWithOnOff with Level field set to maxLevel, TransitionTime field set to 100 (10s) and remaining fields set to 0", test_plan_support.verify_success()), TestStep(8, "TH stores the reported values of CurrentLevel in all incoming reports for CurrentLevel attribute, that contains data in reportedCurrentLevelValuesList, over a period of 30 seconds."), - TestStep(9, "TH verifies that reportedCurrentLevelValuesList does not contain more than 10 entries for CurrentLevel", - "reportedCurrentLevelValuesList has 10 or less entries in the list"), + TestStep(9, "TH verifies that reportedCurrentLevelValuesList does not contain more than 12 entries for CurrentLevel", + "reportedCurrentLevelValuesList has 12 or fewer entries in the list"), TestStep(10, "If reportedCurrentLevelValuesList only contain a single entry, TH verifies the value of the entry is equal to maxLevel", "The entry in reportedCurrentLevelValuesList is equal to maxLevel"), TestStep(11, "If reportedCurrentLevelValuesList contains two or more entries, TH verifies the value of the first entry is larger than startCurrentLevel", @@ -133,7 +133,7 @@ async def test_TC_LVL_2_3(self): self.step(9) count = sub_handler.attribute_report_counts[lvl.Attributes.CurrentLevel] - asserts.assert_less_equal(count, 10, "Received more than 10 reports for CurrentLevel") + asserts.assert_less_equal(count, 12, "Received more than 12 reports for CurrentLevel") asserts.assert_greater(count, 0, "Did not receive any reports for CurrentLevel") self.step(10) From d428c90b7239cf8ad2d04932a15c7d9ef6b6bfd8 Mon Sep 17 00:00:00 2001 From: Alex Tsitsiura Date: Tue, 27 Aug 2024 11:38:44 +0300 Subject: [PATCH 30/32] [Telink] Add b95 flash overlay (#35133) --- .../telink/tlsr9258a_2m_flash.overlay | 37 ++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) mode change 120000 => 100644 src/platform/telink/tlsr9258a_2m_flash.overlay diff --git a/src/platform/telink/tlsr9258a_2m_flash.overlay b/src/platform/telink/tlsr9258a_2m_flash.overlay deleted file mode 120000 index 3b425c1198b983..00000000000000 --- a/src/platform/telink/tlsr9258a_2m_flash.overlay +++ /dev/null @@ -1 +0,0 @@ -tlsr9518adk80d_2m_flash.overlay \ No newline at end of file diff --git a/src/platform/telink/tlsr9258a_2m_flash.overlay b/src/platform/telink/tlsr9258a_2m_flash.overlay new file mode 100644 index 00000000000000..a87ebba57e874b --- /dev/null +++ b/src/platform/telink/tlsr9258a_2m_flash.overlay @@ -0,0 +1,36 @@ +&flash { + reg = <0x20000000 0x200000>; + + partitions { + /delete-node/ partition@0; + /delete-node/ partition@20000; + /delete-node/ partition@88000; + /delete-node/ partition@f0000; + /delete-node/ partition@f4000; + /delete-node/ partition@fe000; + boot_partition: partition@0 { + label = "mcuboot"; + reg = <0x00000000 0x13000>; + }; + slot0_partition: partition@13000 { + label = "image-0"; + reg = <0x13000 0xef000>; + }; + factory_partition: partition@102000 { + label = "factory-data"; + reg = <0x102000 0x1000>; + }; + storage_partition: partition@103000 { + label = "storage"; + reg = <0x103000 0xc000>; + }; + slot1_partition: partition@10f000 { + label = "image-1"; + reg = <0x10f000 0xef000>; + }; + vendor_partition: partition@1fe000 { + label = "vendor-data"; + reg = <0x1fe000 0x2000>; + }; + }; +}; From 8a40c1337db1d85c69453bd31d3d0be676eeb22f Mon Sep 17 00:00:00 2001 From: Alex Tsitsiura Date: Tue, 27 Aug 2024 11:42:11 +0300 Subject: [PATCH 31/32] riscv: telink: add ota event. (#35118) - add all the ota event in ota impl. Signed-off-by: Haiwen Xia Co-authored-by: Haiwen Xia --- src/platform/telink/OTAImageProcessorImpl.cpp | 24 ++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/src/platform/telink/OTAImageProcessorImpl.cpp b/src/platform/telink/OTAImageProcessorImpl.cpp index b5649820dace72..b1ce54748e268e 100644 --- a/src/platform/telink/OTAImageProcessorImpl.cpp +++ b/src/platform/telink/OTAImageProcessorImpl.cpp @@ -29,6 +29,22 @@ static struct stream_flash_ctx stream; namespace chip { +namespace { + +void PostOTAStateChangeEvent(DeviceLayer::OtaState newState) +{ + DeviceLayer::ChipDeviceEvent otaChange; + otaChange.Type = DeviceLayer::DeviceEventType::kOtaStateChanged; + otaChange.OtaStateChanged.newState = newState; + CHIP_ERROR error = DeviceLayer::PlatformMgr().PostEvent(&otaChange); + + if (error != CHIP_NO_ERROR) + { + ChipLogError(SoftwareUpdate, "Error while posting OtaChange event %" CHIP_ERROR_FORMAT, error.Format()); + } +} +} // namespace + namespace DeviceLayer { CHIP_ERROR OTAImageProcessorImpl::PrepareDownload() @@ -60,6 +76,7 @@ CHIP_ERROR OTAImageProcessorImpl::PrepareDownloadImpl() ChipLogError(SoftwareUpdate, "stream_flash_init failed (err %d)", err); } + PostOTAStateChangeEvent(DeviceLayer::kOtaDownloadInProgress); return System::MapErrorZephyr(err); } @@ -72,13 +89,14 @@ CHIP_ERROR OTAImageProcessorImpl::Finalize() ChipLogError(SoftwareUpdate, "stream_flash_buffered_write failed (err %d)", err); } + PostOTAStateChangeEvent(DeviceLayer::kOtaDownloadComplete); return System::MapErrorZephyr(err); } CHIP_ERROR OTAImageProcessorImpl::Abort() { ChipLogError(SoftwareUpdate, "Image upgrade aborted"); - + PostOTAStateChangeEvent(DeviceLayer::kOtaDownloadAborted); return CHIP_NO_ERROR; } @@ -87,6 +105,7 @@ CHIP_ERROR OTAImageProcessorImpl::Apply() // Schedule update of image int err = boot_request_upgrade(BOOT_UPGRADE_PERMANENT); + PostOTAStateChangeEvent(DeviceLayer::kOtaApplyInProgress); #ifdef CONFIG_CHIP_OTA_REQUESTOR_REBOOT_ON_APPLY if (!err) { @@ -101,6 +120,7 @@ CHIP_ERROR OTAImageProcessorImpl::Apply() } else { + PostOTAStateChangeEvent(DeviceLayer::kOtaApplyFailed); return System::MapErrorZephyr(err); } #else @@ -131,6 +151,7 @@ CHIP_ERROR OTAImageProcessorImpl::ProcessBlock(ByteSpan & aBlock) else { mDownloader->EndDownload(error); + PostOTAStateChangeEvent(DeviceLayer::kOtaDownloadFailed); } }); } @@ -149,6 +170,7 @@ bool OTAImageProcessorImpl::IsFirstImageRun() CHIP_ERROR OTAImageProcessorImpl::ConfirmCurrentImage() { + PostOTAStateChangeEvent(DeviceLayer::kOtaApplyComplete); return System::MapErrorZephyr(boot_write_img_confirmed()); } From d185778af022627a366d6d68662ef6aada9be861 Mon Sep 17 00:00:00 2001 From: shgutte <102281713+shgutte@users.noreply.github.com> Date: Tue, 27 Aug 2024 14:21:43 +0530 Subject: [PATCH 32/32] Added changes for ble connection interval (#35198) --- src/platform/silabs/rs911x/BLEManagerImpl.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/platform/silabs/rs911x/BLEManagerImpl.cpp b/src/platform/silabs/rs911x/BLEManagerImpl.cpp index 17047186eaf7fd..75853c8dce5155 100644 --- a/src/platform/silabs/rs911x/BLEManagerImpl.cpp +++ b/src/platform/silabs/rs911x/BLEManagerImpl.cpp @@ -62,8 +62,8 @@ extern "C" { #include #endif -#define BLE_MIN_CONNECTION_INTERVAL_MS 45 -#define BLE_MAX_CONNECTION_INTERVAL_MS 45 +#define BLE_MIN_CONNECTION_INTERVAL_MS 24 +#define BLE_MAX_CONNECTION_INTERVAL_MS 40 #define BLE_SLAVE_LATENCY_MS 0 #define BLE_TIMEOUT_MS 400 #define BLE_DEFAULT_TIMER_PERIOD_MS (1)