blob: 82e3ddbd218418ccdf87359d9c108e216dbe1522 [file] [log] [blame]
/**
* Copyright (c) 2025 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/clusters/scenes-server/CodegenIntegration.h>
#include <app-common/zap-generated/attributes/Accessors.h>
#include <app/clusters/scenes-server/SceneTable.h>
#include <app/clusters/scenes-server/SceneTableImpl.h>
#include <app/clusters/scenes-server/ScenesManagementCluster.h>
#include <app/server/Server.h>
#include <app/static-cluster-config/ScenesManagement.h>
#include <app/util/attribute-storage.h>
#include <app/util/config.h>
#include <app/util/endpoint-config-api.h>
#include <data-model-providers/codegen/ClusterIntegration.h>
#include <data-model-providers/codegen/CodegenDataModelProvider.h>
// Cluster configuration sets values based on this. Ensure config is valid.
// This means it is NOT sufficient to just set the SCENES_MANAGEMENT_TABLE_SIZE in ZAP, but rather
// the CHIP_CONFIG must be updated.
#if defined(SCENES_MANAGEMENT_TABLE_SIZE) && SCENES_MANAGEMENT_TABLE_SIZE
static_assert(chip::scenes::kMaxScenesPerEndpoint >= SCENES_MANAGEMENT_TABLE_SIZE,
"ZAP configuration for scenes table should be at most CHIP_CONFIG_MAX_SCENES_TABLE_SIZE");
#endif
using SceneTable = chip::scenes::SceneTable<chip::scenes::ExtensionFieldSetsImpl>;
namespace chip::app::Clusters::ScenesManagement {
namespace {
class DefaultScenesManagementTableProvider : public ScenesManagementTableProvider
{
public:
void SetParameters(EndpointId endpointId, uint16_t endpointTableSize)
{
mEndpointId = endpointId;
mEndpointTableSize = endpointTableSize;
}
EndpointId GetEndpointId() const { return mEndpointId; }
ScenesManagementSceneTable * Take() override { return scenes::GetSceneTableImpl(mEndpointId, mEndpointTableSize); }
void Release(ScenesManagementSceneTable *) override {}
private:
EndpointId mEndpointId = kInvalidEndpointId;
uint16_t mEndpointTableSize = scenes::kMaxScenesPerEndpoint;
};
constexpr size_t kScenesManagementFixedClusterCount = StaticApplicationConfig::kFixedClusterConfig.size();
constexpr size_t kScenesManagementMaxClusterCount = kScenesManagementFixedClusterCount + CHIP_DEVICE_CONFIG_DYNAMIC_ENDPOINT_COUNT;
LazyRegisteredServerCluster<ScenesManagementCluster> gServers[kScenesManagementMaxClusterCount];
DefaultScenesManagementTableProvider gTableProviders[kScenesManagementMaxClusterCount];
class IntegrationDelegate : public CodegenClusterIntegration::Delegate
{
public:
ServerClusterRegistration & CreateRegistration(EndpointId endpointId, unsigned clusterInstanceIndex,
uint32_t optionalAttributeBits, uint32_t featureMap) override
{
uint16_t endpointTableSize = 0;
VerifyOrDie(Attributes::SceneTableSize::Get(endpointId, &endpointTableSize) ==
Protocols::InteractionModel::Status::Success);
// Guaranteed non-null: if CreateRegistration is called, this means the ember cluster
// was found (because we have a valid clusterInstanceIndex)
const EmberAfCluster * cluster = emberAfFindServerCluster(endpointId, ScenesManagement::Id);
bool supportsCopyScene = false;
// NOTE: acceptedCommandList is KNOWN to not be nullptr because ScenesManagement should have mandatory commands
// as such we iterate here directly without an 'empty list' (i.e. nullptr) check.
for (const CommandId * cmd = cluster->acceptedCommandList; *cmd != kInvalidCommandId; cmd++)
{
if (*cmd == ScenesManagement::Commands::CopyScene::Id)
{
supportsCopyScene = true;
break;
}
}
gTableProviders[clusterInstanceIndex].SetParameters(endpointId, endpointTableSize);
gServers[clusterInstanceIndex].Create(endpointId,
ScenesManagementCluster::Context{
.groupDataProvider = Credentials::GetGroupDataProvider(),
.fabricTable = &Server::GetInstance().GetFabricTable(),
.features = BitMask<ScenesManagement::Feature>(featureMap),
.sceneTableProvider = gTableProviders[clusterInstanceIndex],
.supportsCopyScene = supportsCopyScene,
});
return gServers[clusterInstanceIndex].Registration();
}
ServerClusterInterface * FindRegistration(unsigned clusterInstanceIndex) override
{
VerifyOrReturnValue(gServers[clusterInstanceIndex].IsConstructed(), nullptr);
return &gServers[clusterInstanceIndex].Cluster();
}
void ReleaseRegistration(unsigned clusterInstanceIndex) override { gServers[clusterInstanceIndex].Destroy(); }
};
} // namespace
ScenesManagementCluster * FindClusterOnEndpoint(EndpointId endpointId)
{
IntegrationDelegate integrationDelegate;
ServerClusterInterface * cluster = CodegenClusterIntegration::FindClusterOnEndpoint(
{
.endpointId = endpointId,
.clusterId = ScenesManagement::Id,
.fixedClusterInstanceCount = kScenesManagementFixedClusterCount,
.maxClusterInstanceCount = kScenesManagementMaxClusterCount,
},
integrationDelegate);
return static_cast<ScenesManagementCluster *>(cluster);
}
ScenesServer & ScenesServer::Instance()
{
static ScenesServer gInstance;
return gInstance;
}
bool ScenesServer::IsHandlerRegistered(EndpointId aEndpointId, scenes::SceneHandler * handler)
{
SceneTable * sceneTable = scenes::GetSceneTableImpl(aEndpointId);
return sceneTable->mHandlerList.Contains(handler);
}
void ScenesServer::RegisterSceneHandler(EndpointId aEndpointId, scenes::SceneHandler * handler)
{
SceneTable * sceneTable = scenes::GetSceneTableImpl(aEndpointId);
if (!IsHandlerRegistered(aEndpointId, handler))
{
sceneTable->RegisterHandler(handler);
}
}
void ScenesServer::UnregisterSceneHandler(EndpointId aEndpointId, scenes::SceneHandler * handler)
{
SceneTable * sceneTable = scenes::GetSceneTableImpl(aEndpointId);
if (IsHandlerRegistered(aEndpointId, handler))
{
sceneTable->UnregisterHandler(handler);
}
}
void ScenesServer::GroupWillBeRemoved(FabricIndex aFabricIx, EndpointId aEndpointId, GroupId aGroupId)
{
ScenesManagementCluster * cluster = FindClusterOnEndpoint(aEndpointId);
VerifyOrReturn(cluster != nullptr);
LogErrorOnFailure(cluster->GroupWillBeRemoved(aFabricIx, aGroupId));
}
void ScenesServer::MakeSceneInvalid(EndpointId aEndpointId, FabricIndex aFabricIx)
{
ScenesManagementCluster * cluster = FindClusterOnEndpoint(aEndpointId);
VerifyOrReturn(cluster != nullptr);
LogErrorOnFailure(cluster->MakeSceneInvalid(aFabricIx));
}
void ScenesServer::MakeSceneInvalidForAllFabrics(EndpointId aEndpointId)
{
ScenesManagementCluster * cluster = FindClusterOnEndpoint(aEndpointId);
VerifyOrReturn(cluster != nullptr);
LogErrorOnFailure(cluster->MakeSceneInvalidForAllFabrics());
}
void ScenesServer::StoreCurrentScene(FabricIndex aFabricIx, EndpointId aEndpointId, GroupId aGroupId, SceneId aSceneId)
{
ScenesManagementCluster * cluster = FindClusterOnEndpoint(aEndpointId);
VerifyOrReturn(cluster != nullptr);
LogErrorOnFailure(cluster->StoreCurrentScene(aFabricIx, aGroupId, aSceneId));
}
void ScenesServer::RecallScene(FabricIndex aFabricIx, EndpointId aEndpointId, GroupId aGroupId, SceneId aSceneId)
{
ScenesManagementCluster * cluster = FindClusterOnEndpoint(aEndpointId);
VerifyOrReturn(cluster != nullptr);
LogErrorOnFailure(cluster->RecallScene(aFabricIx, aGroupId, aSceneId));
}
void ScenesServer::RemoveFabric(EndpointId aEndpointId, FabricIndex aFabricIndex)
{
ScenesManagementCluster * cluster = FindClusterOnEndpoint(aEndpointId);
VerifyOrReturn(cluster != nullptr);
LogErrorOnFailure(cluster->RemoveFabric(aFabricIndex));
}
} // namespace chip::app::Clusters::ScenesManagement
using namespace chip;
using namespace chip::app;
using namespace chip::app::Clusters;
using namespace chip::app::Clusters::ScenesManagement;
void MatterScenesManagementClusterInitCallback(EndpointId endpointId)
{
IntegrationDelegate integrationDelegate;
CodegenClusterIntegration::RegisterServer(
{
.endpointId = endpointId,
.clusterId = ScenesManagement::Id,
.fixedClusterInstanceCount = kScenesManagementFixedClusterCount,
.maxClusterInstanceCount = kScenesManagementMaxClusterCount,
.fetchFeatureMap = true,
.fetchOptionalAttributes = false,
},
integrationDelegate);
}
void MatterScenesManagementClusterShutdownCallback(EndpointId endpointId, MatterClusterShutdownType shutdownType)
{
IntegrationDelegate integrationDelegate;
CodegenClusterIntegration::UnregisterServer(
{
.endpointId = endpointId,
.clusterId = ScenesManagement::Id,
.fixedClusterInstanceCount = kScenesManagementFixedClusterCount,
.maxClusterInstanceCount = kScenesManagementMaxClusterCount,
},
integrationDelegate, shutdownType);
}
void MatterScenesManagementPluginServerInitCallback() {}