Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Scenes] Endpoint Add and Remove support #30391

Merged
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 @@ -1558,6 +1558,10 @@
(EmberAfGenericClusterFunction) emberAfTimeFormatLocalizationClusterServerInitCallback, \
(EmberAfGenericClusterFunction) MatterTimeFormatLocalizationClusterServerPreAttributeChangedCallback, \
}; \
const EmberAfGenericClusterFunction chipFuncArrayScenesServer[] = { \
(EmberAfGenericClusterFunction) emberAfScenesClusterServerInitCallback, \
(EmberAfGenericClusterFunction) MatterScenesClusterServerShutdownCallback, \
}; \
const EmberAfGenericClusterFunction chipFuncArrayOnOffServer[] = { \
(EmberAfGenericClusterFunction) emberAfOnOffClusterServerInitCallback, \
(EmberAfGenericClusterFunction) MatterOnOffClusterServerShutdownCallback, \
Expand Down Expand Up @@ -2358,8 +2362,8 @@
.attributes = ZAP_ATTRIBUTE_INDEX(238), \
.attributeCount = 9, \
.clusterSize = 13, \
.mask = ZAP_CLUSTER_MASK(SERVER), \
.functions = NULL, \
.mask = ZAP_CLUSTER_MASK(SERVER) | ZAP_CLUSTER_MASK(INIT_FUNCTION) | ZAP_CLUSTER_MASK(SHUTDOWN_FUNCTION), \
.functions = chipFuncArrayScenesServer, \
.acceptedCommandList = ZAP_GENERATED_COMMANDS_INDEX( 94 ), \
.generatedCommandList = ZAP_GENERATED_COMMANDS_INDEX( 102 ), \
.eventList = nullptr, \
Expand Down Expand Up @@ -2930,8 +2934,8 @@
.attributes = ZAP_ATTRIBUTE_INDEX(687), \
.attributeCount = 9, \
.clusterSize = 13, \
.mask = ZAP_CLUSTER_MASK(SERVER), \
.functions = NULL, \
.mask = ZAP_CLUSTER_MASK(SERVER) | ZAP_CLUSTER_MASK(INIT_FUNCTION) | ZAP_CLUSTER_MASK(SHUTDOWN_FUNCTION), \
.functions = chipFuncArrayScenesServer, \
.acceptedCommandList = ZAP_GENERATED_COMMANDS_INDEX( 240 ), \
.generatedCommandList = ZAP_GENERATED_COMMANDS_INDEX( 248 ), \
.eventList = nullptr, \
Expand Down
1 change: 1 addition & 0 deletions src/app/clusters/scenes-server/SceneTable.h
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,7 @@ class SceneTable

// Fabrics
virtual CHIP_ERROR RemoveFabric(FabricIndex fabric_index) = 0;
virtual CHIP_ERROR RemoveEndpoint() = 0;

// Iterators
using SceneEntryIterator = CommonIterator<SceneTableEntry>;
Expand Down
29 changes: 29 additions & 0 deletions src/app/clusters/scenes-server/SceneTableImpl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -905,6 +905,35 @@ CHIP_ERROR DefaultSceneTableImpl::RemoveFabric(FabricIndex fabric_index)
return CHIP_NO_ERROR;
}

CHIP_ERROR DefaultSceneTableImpl::RemoveEndpoint()
{
VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INTERNAL);

for (FabricIndex fabric_index = kMinValidFabricIndex; fabric_index < kMaxValidFabricIndex; fabric_index++)
{
FabricSceneData fabric(mEndpointId, fabric_index);
CHIP_ERROR err = fabric.Load(mStorage);
VerifyOrReturnError(CHIP_NO_ERROR == err || CHIP_ERROR_NOT_FOUND == err, err);
if (CHIP_ERROR_NOT_FOUND == err)
{
continue;
}

SceneIndex idx = 0;
while (idx < mMaxScenesPerFabric)
{
err = RemoveSceneTableEntryAtPosition(mEndpointId, fabric_index, idx);
VerifyOrReturnError(CHIP_NO_ERROR == err || CHIP_ERROR_NOT_FOUND == err, err);
idx++;
};

// Remove fabric scenes on endpoint
ReturnErrorOnFailure(fabric.Delete(mStorage));
}

return CHIP_NO_ERROR;
}

/// @brief wrapper function around emberAfGetClustersFromEndpoint to allow testing, shimmed in test configuration because
/// emberAfGetClusterFromEndpoint relies on <app/util/attribute-storage.h>, which relies on zap generated files
uint8_t DefaultSceneTableImpl::GetClustersFromEndpoint(ClusterId * clusterList, uint8_t listLen)
Expand Down
2 changes: 2 additions & 0 deletions src/app/clusters/scenes-server/SceneTableImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ static_assert(kMaxScenesPerEndpoint <= CHIP_CONFIG_MAX_SCENES_TABLE_SIZE,
"CHIP_CONFIG_MAX_SCENES_TABLE_SIZE in CHIPConfig.h if you really need more scenes");
static_assert(kMaxScenesPerEndpoint >= 16, "Per spec, kMaxScenesPerEndpoint must be at least 16");
static constexpr uint16_t kMaxScenesPerFabric = (kMaxScenesPerEndpoint - 1) / 2;
static constexpr uint8_t kMaxFabrics = CHIP_CONFIG_MAX_FABRICS;

using clusterId = chip::ClusterId;

Expand Down Expand Up @@ -146,6 +147,7 @@ class DefaultSceneTableImpl : public SceneTable<scenes::ExtensionFieldSetsImpl>

// Fabrics
CHIP_ERROR RemoveFabric(FabricIndex fabric_index) override;
CHIP_ERROR RemoveEndpoint() override;

// Iterators
SceneEntryIterator * IterateSceneEntries(FabricIndex fabric_index) override;
Expand Down
86 changes: 50 additions & 36 deletions src/app/clusters/scenes-server/scenes-server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -116,42 +116,6 @@ CHIP_ERROR ScenesServer::Init()
SceneTable * sceneTable = scenes::GetSceneTableImpl();
ReturnErrorOnFailure(sceneTable->Init(&chip::Server::GetInstance().GetPersistentStorage()));

for (auto endpoint : EnabledEndpointsWithServerCluster(Id))
{
uint32_t featureMap = 0;
EmberAfStatus status = Attributes::FeatureMap::Get(endpoint, &featureMap);
if (EMBER_ZCL_STATUS_SUCCESS == status)
{
// According to spec, bit 7 MUST match feature bit 0 (SceneNames)
BitMask<NameSupportBitmap> nameSupport = (featureMap & to_underlying(Feature::kSceneNames))
? BitMask<NameSupportBitmap>(NameSupportBitmap::kSceneNames)
: BitMask<NameSupportBitmap>();
status = Attributes::NameSupport::Set(endpoint, nameSupport);
if (EMBER_ZCL_STATUS_SUCCESS != status)
{
ChipLogDetail(Zcl, "ERR: setting NameSupport on Endpoint %hu Status: %x", endpoint, status);
}
}
else
{
ChipLogDetail(Zcl, "ERR: getting the scenes FeatureMap on Endpoint %hu Status: %x", endpoint, status);
}

// Explicit AttributeValuePairs and TableSize features are mandatory for matter so we force-set them here
featureMap |= (to_underlying(Feature::kExplicit) | to_underlying(Feature::kTableSize));
status = Attributes::FeatureMap::Set(endpoint, featureMap);
if (EMBER_ZCL_STATUS_SUCCESS != status)
{
ChipLogDetail(Zcl, "ERR: setting the scenes FeatureMap on Endpoint %hu Status: %x", endpoint, status);
}

status = Attributes::LastConfiguredBy::SetNull(endpoint);
if (EMBER_ZCL_STATUS_SUCCESS != status)
{
ChipLogDetail(Zcl, "ERR: setting LastConfiguredBy on Endpoint %hu Status: %x", endpoint, status);
}
}

mIsInitialized = true;
return CHIP_NO_ERROR;
}
Expand Down Expand Up @@ -919,6 +883,56 @@ void ScenesServer::HandleCopyScene(HandlerContext & ctx, const Commands::CopySce
} // namespace app
} // namespace chip

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

void emberAfScenesClusterServerInitCallback(EndpointId endpoint)
{
uint32_t featureMap = 0;
EmberAfStatus status = Attributes::FeatureMap::Get(endpoint, &featureMap);
if (EMBER_ZCL_STATUS_SUCCESS == status)
{
// According to spec, bit 7 MUST match feature bit 0 (SceneNames)
BitMask<NameSupportBitmap> nameSupport = (featureMap & to_underlying(Feature::kSceneNames))
? BitMask<NameSupportBitmap>(NameSupportBitmap::kSceneNames)
: BitMask<NameSupportBitmap>();
status = Attributes::NameSupport::Set(endpoint, nameSupport);
if (EMBER_ZCL_STATUS_SUCCESS != status)
{
ChipLogDetail(Zcl, "ERR: setting NameSupport on Endpoint %hu Status: %x", endpoint, status);
}
}
else
{
ChipLogDetail(Zcl, "ERR: getting the scenes FeatureMap on Endpoint %hu Status: %x", endpoint, status);
}

// Explicit AttributeValuePairs and TableSize features are mandatory for matter so we force-set them here
featureMap |= (to_underlying(Feature::kExplicit) | to_underlying(Feature::kTableSize));
status = Attributes::FeatureMap::Set(endpoint, featureMap);
if (EMBER_ZCL_STATUS_SUCCESS != status)
{
ChipLogDetail(Zcl, "ERR: setting the scenes FeatureMap on Endpoint %hu Status: %x", endpoint, status);
}

status = Attributes::LastConfiguredBy::SetNull(endpoint);
if (EMBER_ZCL_STATUS_SUCCESS != status)
{
ChipLogDetail(Zcl, "ERR: setting LastConfiguredBy on Endpoint %hu Status: %x", endpoint, status);
}
}

void MatterScenesClusterServerShutdownCallback(EndpointId endpoint)
{
uint16_t endpointTableSize = 0;
ReturnOnFailure(Attributes::SceneTableSize::Get(endpoint, &endpointTableSize));

// Get Scene Table Instance
SceneTable * sceneTable = scenes::GetSceneTableImpl(endpoint, endpointTableSize);
sceneTable->RemoveEndpoint();
}

void MatterScenesPluginServerInitCallback()
{
CHIP_ERROR err = ScenesServer::Instance().Init();
Expand Down
2 changes: 2 additions & 0 deletions src/app/common/templates/config-data.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ ClustersWithInitFunctions:
- Thermostat
- Mode Select
- Sample MEI
- Scenes

ClustersWithAttributeChangedFunctions:
- Bridged Device Basic
Expand All @@ -80,6 +81,7 @@ ClustersWithShutdownFunctions:
- Level Control
- Color Control
- Sample MEI
- Scenes

ClustersWithPreAttributeChangeFunctions:
- Door Lock
Expand Down
77 changes: 75 additions & 2 deletions src/app/tests/TestSceneTable.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1411,6 +1411,7 @@ void TestEndpointScenes(nlTestSuite * aSuite, void * aContext)
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->SetSceneTableEntry(kFabric2, scene7));
// scene count to Endpoint

// Endpoint 3 still unafected
sceneTable = scenes::GetSceneTableImpl(kTestEndpoint3, defaultTestTableSize);
NL_TEST_ASSERT(aSuite, nullptr != sceneTable);
VerifyOrReturn(nullptr != sceneTable);
Expand All @@ -1419,6 +1420,69 @@ void TestEndpointScenes(nlTestSuite * aSuite, void * aContext)
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->GetRemainingCapacity(kFabric2, fabric_capacity));
NL_TEST_ASSERT(aSuite, defaultTestFabricCapacity == fabric_capacity);

// Fill fabric 1 endpoint 3
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->SetSceneTableEntry(kFabric1, scene1));
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->SetSceneTableEntry(kFabric1, scene2));
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->SetSceneTableEntry(kFabric1, scene3));
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->SetSceneTableEntry(kFabric1, scene4));
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->SetSceneTableEntry(kFabric1, scene5));
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->SetSceneTableEntry(kFabric1, scene6));
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->SetSceneTableEntry(kFabric1, scene7));

// Test removal of Endpoint clears scene on all fabrics for that endpoint
sceneTable = scenes::GetSceneTableImpl(kTestEndpoint2, defaultTestTableSize);
NL_TEST_ASSERT(aSuite, nullptr != sceneTable);
VerifyOrReturn(nullptr != sceneTable);
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->RemoveEndpoint());

// Check Fabric1 on Endpoint 2
NL_TEST_ASSERT(aSuite, CHIP_ERROR_NOT_FOUND == sceneTable->GetSceneTableEntry(kFabric1, sceneId1, scene));
NL_TEST_ASSERT(aSuite, CHIP_ERROR_NOT_FOUND == sceneTable->GetSceneTableEntry(kFabric1, sceneId2, scene));
NL_TEST_ASSERT(aSuite, CHIP_ERROR_NOT_FOUND == sceneTable->GetSceneTableEntry(kFabric1, sceneId3, scene));
NL_TEST_ASSERT(aSuite, CHIP_ERROR_NOT_FOUND == sceneTable->GetSceneTableEntry(kFabric1, sceneId4, scene));
NL_TEST_ASSERT(aSuite, CHIP_ERROR_NOT_FOUND == sceneTable->GetSceneTableEntry(kFabric1, sceneId5, scene));
NL_TEST_ASSERT(aSuite, CHIP_ERROR_NOT_FOUND == sceneTable->GetSceneTableEntry(kFabric1, sceneId6, scene));
NL_TEST_ASSERT(aSuite, CHIP_ERROR_NOT_FOUND == sceneTable->GetSceneTableEntry(kFabric1, sceneId7, scene));
NL_TEST_ASSERT(aSuite, CHIP_ERROR_NOT_FOUND == sceneTable->GetSceneTableEntry(kFabric2, sceneId1, scene));
NL_TEST_ASSERT(aSuite, CHIP_ERROR_NOT_FOUND == sceneTable->GetSceneTableEntry(kFabric2, sceneId2, scene));
NL_TEST_ASSERT(aSuite, CHIP_ERROR_NOT_FOUND == sceneTable->GetSceneTableEntry(kFabric2, sceneId3, scene));
NL_TEST_ASSERT(aSuite, CHIP_ERROR_NOT_FOUND == sceneTable->GetSceneTableEntry(kFabric2, sceneId4, scene));
NL_TEST_ASSERT(aSuite, CHIP_ERROR_NOT_FOUND == sceneTable->GetSceneTableEntry(kFabric2, sceneId5, scene));
NL_TEST_ASSERT(aSuite, CHIP_ERROR_NOT_FOUND == sceneTable->GetSceneTableEntry(kFabric2, sceneId6, scene));
NL_TEST_ASSERT(aSuite, CHIP_ERROR_NOT_FOUND == sceneTable->GetSceneTableEntry(kFabric2, sceneId7, scene));

// Check Fabric 1 and 2 on Endpoint 1
sceneTable = scenes::GetSceneTableImpl(kTestEndpoint1, defaultTestTableSize);
NL_TEST_ASSERT(aSuite, nullptr != sceneTable);
VerifyOrReturn(nullptr != sceneTable);
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->GetSceneTableEntry(kFabric1, sceneId1, scene));
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->GetSceneTableEntry(kFabric1, sceneId2, scene));
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->GetSceneTableEntry(kFabric1, sceneId3, scene));
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->GetSceneTableEntry(kFabric1, sceneId4, scene));
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->GetSceneTableEntry(kFabric1, sceneId5, scene));
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->GetSceneTableEntry(kFabric1, sceneId6, scene));
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->GetSceneTableEntry(kFabric1, sceneId7, scene));

NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->GetSceneTableEntry(kFabric2, sceneId1, scene));
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->GetSceneTableEntry(kFabric2, sceneId2, scene));
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->GetSceneTableEntry(kFabric2, sceneId3, scene));
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->GetSceneTableEntry(kFabric2, sceneId4, scene));
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->GetSceneTableEntry(kFabric2, sceneId5, scene));
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->GetSceneTableEntry(kFabric2, sceneId6, scene));
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->GetSceneTableEntry(kFabric2, sceneId7, scene));

// Check Fabric 1 on Endpoint 3
sceneTable = scenes::GetSceneTableImpl(kTestEndpoint3, defaultTestTableSize);
NL_TEST_ASSERT(aSuite, nullptr != sceneTable);
VerifyOrReturn(nullptr != sceneTable);
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->GetSceneTableEntry(kFabric1, sceneId1, scene));
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->GetSceneTableEntry(kFabric1, sceneId2, scene));
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->GetSceneTableEntry(kFabric1, sceneId3, scene));
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->GetSceneTableEntry(kFabric1, sceneId4, scene));
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->GetSceneTableEntry(kFabric1, sceneId5, scene));
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->GetSceneTableEntry(kFabric1, sceneId6, scene));
NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->GetSceneTableEntry(kFabric1, sceneId7, scene));

// Test removal of fabric clears scene fabric on all endpoints
sceneTable = scenes::GetSceneTableImpl(kTestEndpoint1, defaultTestTableSize);
NL_TEST_ASSERT(aSuite, nullptr != sceneTable);
Expand All @@ -1431,7 +1495,6 @@ void TestEndpointScenes(nlTestSuite * aSuite, void * aContext)
NL_TEST_ASSERT(aSuite, CHIP_ERROR_NOT_FOUND == sceneTable->GetSceneTableEntry(kFabric1, sceneId5, scene));
NL_TEST_ASSERT(aSuite, CHIP_ERROR_NOT_FOUND == sceneTable->GetSceneTableEntry(kFabric1, sceneId6, scene));
NL_TEST_ASSERT(aSuite, CHIP_ERROR_NOT_FOUND == sceneTable->GetSceneTableEntry(kFabric1, sceneId7, scene));
NL_TEST_ASSERT(aSuite, CHIP_ERROR_NOT_FOUND == sceneTable->GetSceneTableEntry(kFabric1, sceneId8, scene));

sceneTable = scenes::GetSceneTableImpl(kTestEndpoint2, defaultTestTableSize);
NL_TEST_ASSERT(aSuite, nullptr != sceneTable);
Expand All @@ -1443,7 +1506,17 @@ void TestEndpointScenes(nlTestSuite * aSuite, void * aContext)
NL_TEST_ASSERT(aSuite, CHIP_ERROR_NOT_FOUND == sceneTable->GetSceneTableEntry(kFabric1, sceneId5, scene));
NL_TEST_ASSERT(aSuite, CHIP_ERROR_NOT_FOUND == sceneTable->GetSceneTableEntry(kFabric1, sceneId6, scene));
NL_TEST_ASSERT(aSuite, CHIP_ERROR_NOT_FOUND == sceneTable->GetSceneTableEntry(kFabric1, sceneId7, scene));
NL_TEST_ASSERT(aSuite, CHIP_ERROR_NOT_FOUND == sceneTable->GetSceneTableEntry(kFabric1, sceneId8, scene));

sceneTable = scenes::GetSceneTableImpl(kTestEndpoint3, defaultTestTableSize);
NL_TEST_ASSERT(aSuite, nullptr != sceneTable);
VerifyOrReturn(nullptr != sceneTable);
NL_TEST_ASSERT(aSuite, CHIP_ERROR_NOT_FOUND == sceneTable->GetSceneTableEntry(kFabric1, sceneId1, scene));
NL_TEST_ASSERT(aSuite, CHIP_ERROR_NOT_FOUND == sceneTable->GetSceneTableEntry(kFabric1, sceneId2, scene));
NL_TEST_ASSERT(aSuite, CHIP_ERROR_NOT_FOUND == sceneTable->GetSceneTableEntry(kFabric1, sceneId3, scene));
NL_TEST_ASSERT(aSuite, CHIP_ERROR_NOT_FOUND == sceneTable->GetSceneTableEntry(kFabric1, sceneId4, scene));
NL_TEST_ASSERT(aSuite, CHIP_ERROR_NOT_FOUND == sceneTable->GetSceneTableEntry(kFabric1, sceneId5, scene));
NL_TEST_ASSERT(aSuite, CHIP_ERROR_NOT_FOUND == sceneTable->GetSceneTableEntry(kFabric1, sceneId6, scene));
NL_TEST_ASSERT(aSuite, CHIP_ERROR_NOT_FOUND == sceneTable->GetSceneTableEntry(kFabric1, sceneId7, scene));

sceneTable->RemoveFabric(kFabric2);

Expand Down
Loading