Skip to content

Commit

Permalink
[Scenes] Endpoint Add and Remove support (#30391)
Browse files Browse the repository at this point in the history
* Moved the endpoint init code to the ember af init callback in scenes and updated config-data to signal scenes uses those init callbacks

* Added temporary logs to see why test fails in CI
  • Loading branch information
lpbeliveau-silabs authored and pull[bot] committed Jan 6, 2024
1 parent e954ae0 commit 6246964
Show file tree
Hide file tree
Showing 7 changed files with 167 additions and 42 deletions.
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 @@ -285,6 +285,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

0 comments on commit 6246964

Please sign in to comment.