Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@

#include <app-common/zap-generated/ids/Attributes.h>
#include <app-common/zap-generated/ids/Clusters.h>
#include <app/clusters/resource-monitoring-server/resource-monitoring-cluster-objects.h>
#include <app/clusters/resource-monitoring-server/resource-monitoring-server.h>
#include <resource-monitoring-delegates.h>

Expand Down
5 changes: 4 additions & 1 deletion scripts/attribute_persistence_provider_exclusions.txt
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,15 @@ src/app/clusters/chime-server/MigrateChimeServerStorage.h
src/app/clusters/unit-localization-server/CodegenIntegration.cpp
src/app/clusters/unit-localization-server/MigrateUnitLocalizationServerStorage.cpp
src/app/clusters/unit-localization-server/MigrateUnitLocalizationServerStorage.h
# ResourceMonitoring migration
src/app/clusters/resource-monitoring-server/CodegenResourceMonitoringCluster.cpp
src/app/clusters/resource-monitoring-server/MigrateResourceMonitoringServerStorage.cpp
src/app/clusters/resource-monitoring-server/MigrateResourceMonitoringServerStorage.h

# Cluster server implementations that currently use SafeAttributePersistenceProvider
src/app/clusters/boolean-state-configuration-server/BooleanStateConfigurationCluster.cpp
src/app/clusters/boolean-state-configuration-server/tests/TestBooleanStateConfigurationCluster.cpp
src/app/clusters/camera-av-stream-management-server/CameraAVStreamManagementCluster.h
src/app/clusters/mode-base-server/mode-base-server.cpp
src/app/clusters/resource-monitoring-server/ResourceMonitoringCluster.cpp
src/app/clusters/resource-monitoring-server/tests/TestResourceMonitoringCluster.cpp
src/app/clusters/thread-network-directory-server/thread-network-directory-server.cpp
1 change: 1 addition & 0 deletions src/app/clusters/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ source_set("clusters") {
"operational-credentials-server",
"push-av-stream-transport-server",
"relative-humidity-measurement-server",
"resource-monitoring-server",
"scenes-server",
"software-diagnostics-server",
"soil-measurement-server",
Expand Down
4 changes: 4 additions & 0 deletions src/app/clusters/resource-monitoring-server/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ import("//build_overrides/chip.gni")

source_set("resource-monitoring-server") {
sources = [
"MigrateResourceMonitoringServerStorage.cpp",
"MigrateResourceMonitoringServerStorage.h",
"ResourceMonitoringCluster.cpp",
"ResourceMonitoringCluster.h",
"ResourceMonitoringDelegate.h",
Expand All @@ -24,7 +26,9 @@ source_set("resource-monitoring-server") {
]

deps = [
"${chip_root}/src/app:attribute-persistence",
"${chip_root}/src/app/data-model-provider",
"${chip_root}/src/app/persistence:migration",
"${chip_root}/src/app/server",
"${chip_root}/src/app/server-cluster",
"${chip_root}/zzz_generated/app-common/clusters/ActivatedCarbonFilterMonitoring",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@

#pragma once

#include <app/clusters/resource-monitoring-server/ResourceMonitoringCluster.h>
#include <app/clusters/resource-monitoring-server/CodegenResourceMonitoringCluster.h>
#include <app/server-cluster/ServerClusterInterfaceRegistry.h>

namespace chip {
Expand Down Expand Up @@ -78,7 +78,7 @@ struct Instance
bool GetInPlaceIndicator() const { return mCluster.Cluster().GetInPlaceIndicator(); }
DataModel::Nullable<uint32_t> GetLastChangedTime() const { return mCluster.Cluster().GetLastChangedTime(); }

chip::app::RegisteredServerCluster<chip::app::Clusters::ResourceMonitoring::ResourceMonitoringCluster> mCluster;
chip::app::RegisteredServerCluster<chip::app::Clusters::ResourceMonitoring::CodegenResourceMonitoringCluster> mCluster;
};

} // namespace ResourceMonitoring
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*
*
* Copyright (c) 2026 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 <app/SafeAttributePersistenceProvider.h>
#include <app/clusters/resource-monitoring-server/CodegenResourceMonitoringCluster.h>
#include <app/clusters/resource-monitoring-server/MigrateResourceMonitoringServerStorage.h>
#include <lib/support/CodeUtils.h>

using namespace chip;
using namespace chip::app;
using namespace chip::app::Clusters;

namespace chip {
namespace app {
namespace Clusters {
namespace ResourceMonitoring {

CHIP_ERROR CodegenResourceMonitoringCluster::Startup(ServerClusterContext & context)
{
// Migrate attributes for this cluster from SafeAttribute to AttributePersistence.
// This is done at Startup time when the persistence providers are guaranteed to be available.
SafeAttributePersistenceProvider * srcProvider = GetSafeAttributePersistenceProvider();
AttributePersistenceProvider & dstProvider = context.attributeStorage;

if (srcProvider != nullptr)
{
LogErrorOnFailure(ResourceMonitoring::MigrateResourceMonitoringServerStorage(mPath.mEndpointId, mPath.mClusterId,
*srcProvider, dstProvider));
}

return ResourceMonitoringCluster::Startup(context);
}

} // namespace ResourceMonitoring
} // namespace Clusters
} // namespace app
} // namespace chip
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
*
* Copyright (c) 2026 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.
*/

#pragma once

#include <app/clusters/resource-monitoring-server/ResourceMonitoringCluster.h>

namespace chip {
namespace app {
namespace Clusters {
namespace ResourceMonitoring {

/**
* A ResourceMonitoringCluster subclass that performs storage migration during Startup.
* This ensures the persistence providers are available when migration runs.
*/
class CodegenResourceMonitoringCluster : public ResourceMonitoringCluster
{
public:
using ResourceMonitoringCluster::ResourceMonitoringCluster;

CHIP_ERROR Startup(ServerClusterContext & context) override;
};

} // namespace ResourceMonitoring
} // namespace Clusters
} // namespace app
} // namespace chip
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/*
*
* Copyright (c) 2026 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.
*/

#include <app/clusters/resource-monitoring-server/MigrateResourceMonitoringServerStorage.h>
#include <clusters/ActivatedCarbonFilterMonitoring/Attributes.h>
#include <clusters/HepaFilterMonitoring/Attributes.h>

namespace chip {
namespace app {
namespace Clusters {
namespace ResourceMonitoring {

CHIP_ERROR MigrateResourceMonitoringServerStorage(EndpointId endpointId, ClusterId clusterId,
SafeAttributePersistenceProvider & safeProvider,
AttributePersistenceProvider & dstProvider)
{
// LastChangedTime attribute ID is the same for both HepaFilterMonitoring and ActivatedCarbonFilterMonitoring
static_assert(HepaFilterMonitoring::Attributes::LastChangedTime::Id ==
ActivatedCarbonFilterMonitoring::Attributes::LastChangedTime::Id);

static constexpr AttrMigrationData attributesToUpdate[] = {
{ HepaFilterMonitoring::Attributes::LastChangedTime::Id, sizeof(uint32_t), true /* isScalar */ },
};

// We need to provide a buffer with enough space for the attributes that will be migrated.
static constexpr size_t kBufferSize = MaxAttrMigrationValueSize(attributesToUpdate);
static_assert(kBufferSize > 0, "All migration attributes have zero valueSize");
uint8_t attributeBuffer[kBufferSize] = {};
MutableByteSpan buffer(attributeBuffer);

return MigrateFromSafeToAttributePersistenceProvider(safeProvider, dstProvider, { endpointId, clusterId },
Span(attributesToUpdate), buffer);
}

} // namespace ResourceMonitoring
} // namespace Clusters
} // namespace app
} // namespace chip
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
*
* Copyright (c) 2026 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 <app/persistence/AttributePersistenceMigration.h>
#include <lib/core/DataModelTypes.h>

namespace chip {
namespace app {

namespace Clusters {
namespace ResourceMonitoring {

/**
* Migrates ResourceMonitoring cluster attributes from SafeAttributePersistenceProvider to AttributePersistenceProvider.
*
* @param endpointId The endpoint on which the cluster exists.
* @param clusterId The ID of the ResourceMonitoring cluster (HepaFilterMonitoring or ActivatedCarbonFilterMonitoring).
* @param safeProvider The source SafeAttributePersistenceProvider.
* @param dstProvider The destination AttributePersistenceProvider.
* @return CHIP_ERROR indicating success or failure of the migration.
*/
CHIP_ERROR MigrateResourceMonitoringServerStorage(EndpointId endpointId, ClusterId clusterId,
SafeAttributePersistenceProvider & safeProvider,
AttributePersistenceProvider & dstProvider);

} // namespace ResourceMonitoring
} // namespace Clusters
} // namespace app
} // namespace chip
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@

#include <app/clusters/resource-monitoring-server/ResourceMonitoringCluster.h>

#include <app/SafeAttributePersistenceProvider.h>
#include <app/persistence/AttributePersistence.h>
#include <app/server-cluster/AttributeListBuilder.h>
#include <clusters/ActivatedCarbonFilterMonitoring/Metadata.h>
Expand Down Expand Up @@ -68,15 +67,9 @@ DataModel::ActionReturnStatus ResourceMonitoringCluster::WriteImpl(const DataMod
{
case ResourceMonitoring::Attributes::LastChangedTime::Id: {

Attributes::LastChangedTime::TypeInfo::Type newLastChangedTime;
ReturnErrorOnFailure(decoder.Decode(newLastChangedTime));

VerifyOrReturnValue(newLastChangedTime != mLastChangedTime, DataModel::ActionReturnStatus::FixedStatus::kWriteSuccessNoOp);

ReturnErrorOnFailure(chip::app::GetSafeAttributePersistenceProvider()->WriteScalarValue(
ConcreteAttributePath(GetEndpointId(), GetClusterId(), Attributes::LastChangedTime::Id), newLastChangedTime));
mLastChangedTime = newLastChangedTime;
return Status::Success;
VerifyOrReturnError(mContext != nullptr, Status::InvalidInState);
AttributePersistence attrPersistence{ mContext->attributeStorage };
return attrPersistence.DecodeAndStoreNativeEndianValue(request.path, decoder, mLastChangedTime);
}

default:
Expand Down Expand Up @@ -220,6 +213,7 @@ Protocols::InteractionModel::Status ResourceMonitoringCluster::UpdateInPlaceIndi
chip::Protocols::InteractionModel::Status
ResourceMonitoringCluster::UpdateLastChangedTime(DataModel::Nullable<uint32_t> aNewLastChangedTime)
{
VerifyOrReturnError(mContext != nullptr, Status::InvalidInState);
auto oldLastchangedTime = mLastChangedTime;
mLastChangedTime = aNewLastChangedTime;
VerifyOrReturnValue(mLastChangedTime != oldLastchangedTime, Status::Success);
Expand All @@ -229,43 +223,45 @@ ResourceMonitoringCluster::UpdateLastChangedTime(DataModel::Nullable<uint32_t> a
static_assert(kAttributeId == HepaFilterMonitoring::Attributes::LastChangedTime::Id);
static_assert(kAttributeId == ActivatedCarbonFilterMonitoring::Attributes::LastChangedTime::Id);

ReturnValueOnFailure(chip::app::GetSafeAttributePersistenceProvider()->WriteScalarValue(
ConcreteAttributePath(mPath.mEndpointId, mPath.mClusterId, kAttributeId), mLastChangedTime),
Status::Failure);
NumericAttributeTraits<uint32_t>::StorageType storageValue;
DataModel::NullableToStorage(mLastChangedTime, storageValue);

ReturnValueOnFailure(
mContext->attributeStorage.WriteValue(ConcreteAttributePath(mPath.mEndpointId, mPath.mClusterId, kAttributeId),
{ reinterpret_cast<const uint8_t *>(&storageValue), sizeof(storageValue) }),
Status::Failure);
NotifyAttributeChanged(kAttributeId);

return Protocols::InteractionModel::Status::Success;
}

void ResourceMonitoringCluster::LoadPersistentAttributes()
{
CHIP_ERROR err = CHIP_NO_ERROR;
VerifyOrReturn(mContext != nullptr);

// same attribute ID for all clusters
constexpr AttributeId kAttributeId = HepaFilterMonitoring::Attributes::LastChangedTime::Id;
static_assert(kAttributeId == HepaFilterMonitoring::Attributes::LastChangedTime::Id);
static_assert(kAttributeId == ActivatedCarbonFilterMonitoring::Attributes::LastChangedTime::Id);

err = chip::app::GetSafeAttributePersistenceProvider()->ReadScalarValue(
ConcreteAttributePath(mPath.mEndpointId, mPath.mClusterId, kAttributeId), mLastChangedTime);
AttributePersistence attrPersistence{ mContext->attributeStorage };
const DataModel::Nullable<uint32_t> defaultValue = DataModel::Nullable<uint32_t>();

if (err == CHIP_NO_ERROR)
if (!attrPersistence.LoadNativeEndianValue<uint32_t>(ConcreteAttributePath(mPath.mEndpointId, mPath.mClusterId, kAttributeId),
mLastChangedTime, defaultValue))
{
if (mLastChangedTime.IsNull())
{
ChipLogDetail(Zcl, "ResourceMonitoring: Loaded LastChangedTime as null");
}
else
{
ChipLogDetail(Zcl, "ResourceMonitoring: Loaded LastChangedTime as %lu",
(long unsigned int) mLastChangedTime.Value()); // on some platforms uint32_t is a long, cast it to
// unsigned long on all platforms to prevent CI errors
}
// If we cannot find the previous LastChangedTime, we will assume it to be null.
ChipLogDetail(Zcl, "ResourceMonitoring: Unable to load the LastChangedTime from the KVS. Assuming null");
}
else if (mLastChangedTime.IsNull())
{
ChipLogDetail(Zcl, "ResourceMonitoring: Loaded LastChangedTime as null");
}
else
{
// If we cannot find the previous LastChangedTime, we will assume it to be null.
ChipLogDetail(Zcl, "ResourceMonitoring: Unable to load the LastChangedTime from the KVS. Assuming null");
ChipLogDetail(Zcl, "ResourceMonitoring: Loaded LastChangedTime as %lu",
(long unsigned int) mLastChangedTime.Value()); // on some platforms uint32_t is a long, cast it to
// unsigned long on all platforms to prevent CI errors
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,7 @@ TARGET_SOURCES(
PRIVATE
"${CLUSTER_DIR}/CodegenIntegration.h"
"${CLUSTER_DIR}/CodegenIntegration.cpp"
"${CLUSTER_DIR}/CodegenResourceMonitoringCluster.cpp"
"${CLUSTER_DIR}/CodegenResourceMonitoringCluster.h"
"${CLUSTER_DIR}/resource-monitoring-server.h"
)

# These are the things that BUILD.gn dependencies would pull
TARGET_SOURCES(
${APP_TARGET}
PRIVATE
"${CLUSTER_DIR}/replacement-product-list-manager.h"
"${CLUSTER_DIR}/resource-monitoring-cluster-objects.cpp"
"${CLUSTER_DIR}/resource-monitoring-cluster-objects.h"
"${CLUSTER_DIR}/ResourceMonitoringCluster.cpp"
"${CLUSTER_DIR}/ResourceMonitoringCluster.h"
"${CLUSTER_DIR}/ResourceMonitoringDelegate.h"
)
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,7 @@
app_config_dependent_sources = [
"CodegenIntegration.cpp",
"CodegenIntegration.h",
"CodegenResourceMonitoringCluster.cpp",
"CodegenResourceMonitoringCluster.h",
"resource-monitoring-server.h",
]
Loading
Loading