Skip to content

Commit

Permalink
[Scenes]Added the level-control cluster handler for scenes EFS (#28836)
Browse files Browse the repository at this point in the history
* Added the level-control cluster handler for scenes EFS

* Updated the Null value handling and handling of absence of frequency feature

* optimsed ifdef in on-on handler

* Applied change to allow attribute count increase in the futur
  • Loading branch information
lpbeliveau-silabs authored and pull[bot] committed Sep 11, 2023
1 parent aba9454 commit 5331373
Show file tree
Hide file tree
Showing 6 changed files with 173 additions and 22 deletions.
148 changes: 147 additions & 1 deletion src/app/clusters/level-control/level-control.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,137 @@ static bool shouldExecuteIfOff(EndpointId endpoint, CommandId commandId,
chip::Optional<chip::BitMask<LevelControlOptions>> optionsMask,
chip::Optional<chip::BitMask<LevelControlOptions>> optionsOverride);

#ifdef EMBER_AF_PLUGIN_SCENES
class DefaultLevelControlSceneHandler : public scenes::DefaultSceneHandlerImpl
{
public:
// As per spec, 2 attributes are scenable in the level control cluster
static constexpr uint8_t kLevelMaxScenableAttributes = 2;

DefaultLevelControlSceneHandler() = default;
~DefaultLevelControlSceneHandler() override {}

// Default function for LevelControl cluster, only puts the LevelControl cluster ID in the span if supported on the caller
// endpoint
virtual void GetSupportedClusters(EndpointId endpoint, Span<ClusterId> & clusterBuffer) override
{
if (emberAfContainsServer(endpoint, LevelControl::Id) && clusterBuffer.size() >= 1)
{
clusterBuffer[0] = LevelControl::Id;
clusterBuffer.reduce_size(1);
}
else
{
clusterBuffer.reduce_size(0);
}
}

// Default function for LevelControl cluster, only checks if LevelControl is enabled on the endpoint
bool SupportsCluster(EndpointId endpoint, ClusterId cluster) override
{
return (cluster == LevelControl::Id) && (emberAfContainsServer(endpoint, LevelControl::Id));
}

/// @brief Serialize the Cluster's EFS value
/// @param endpoint target endpoint
/// @param cluster target cluster
/// @param serializedBytes data to serialize into EFS
/// @return CHIP_NO_ERROR if successfully serialized the data, CHIP_ERROR_INVALID_ARGUMENT otherwise
CHIP_ERROR SerializeSave(EndpointId endpoint, ClusterId cluster, MutableByteSpan & serializedBytes) override
{
using AttributeValuePair = Scenes::Structs::AttributeValuePair::Type;

app::DataModel::Nullable<uint8_t> level;
VerifyOrReturnError(EMBER_ZCL_STATUS_SUCCESS == Attributes::CurrentLevel::Get(endpoint, level), CHIP_ERROR_READ_FAILED);

AttributeValuePair pairs[kLevelMaxScenableAttributes];

uint8_t maxLevel;
VerifyOrReturnError(EMBER_ZCL_STATUS_SUCCESS == Attributes::MaxLevel::Get(endpoint, &maxLevel), CHIP_ERROR_READ_FAILED);

pairs[0].attributeID = Attributes::CurrentLevel::Id;
pairs[0].attributeValue = (!level.IsNull()) ? level.Value() : maxLevel + 1;
size_t attributeCount = 1;
if (LevelControlHasFeature(endpoint, LevelControl::Feature::kFrequency))
{
uint16_t frequency;
VerifyOrReturnError(EMBER_ZCL_STATUS_SUCCESS == Attributes::CurrentFrequency::Get(endpoint, &frequency),
CHIP_ERROR_READ_FAILED);
pairs[attributeCount].attributeID = Attributes::CurrentFrequency::Id;
pairs[attributeCount].attributeValue = frequency;
attributeCount++;
}

app::DataModel::List<AttributeValuePair> attributeValueList(pairs, attributeCount);

return EncodeAttributeValueList(attributeValueList, serializedBytes);
}

/// @brief Default EFS interaction when applying scene to the OnOff Cluster
/// @param endpoint target endpoint
/// @param cluster target cluster
/// @param serializedBytes Data from nvm
/// @param timeMs transition time in ms
/// @return CHIP_NO_ERROR if value as expected, CHIP_ERROR_INVALID_ARGUMENT otherwise
CHIP_ERROR ApplyScene(EndpointId endpoint, ClusterId cluster, const ByteSpan & serializedBytes,
scenes::TransitionTimeMs timeMs) override
{
app::DataModel::DecodableList<Scenes::Structs::AttributeValuePair::DecodableType> attributeValueList;

ReturnErrorOnFailure(DecodeAttributeValueList(serializedBytes, attributeValueList));

size_t attributeCount = 0;
ReturnErrorOnFailure(attributeValueList.ComputeSize(&attributeCount));
VerifyOrReturnError(attributeCount <= kLevelMaxScenableAttributes, CHIP_ERROR_BUFFER_TOO_SMALL);

auto pair_iterator = attributeValueList.begin();

// The level control cluster should have a maximum of 2 attributes
uint8_t level = 0;
// TODO : Uncomment when frequency is supported by the level control cluster
// uint16_t frequency;
while (pair_iterator.Next())
{
auto & decodePair = pair_iterator.GetValue();

// If attribute ID was encoded, checks which attribute from LC cluster is there
switch (decodePair.attributeID)
{
case Attributes::CurrentLevel::Id:
level = static_cast<uint8_t>(decodePair.attributeValue);
break;
case Attributes::CurrentFrequency::Id:
// TODO : Uncomment when frequency is supported by the level control cluster
// frequency = static_cast<uint16_t>(decodePair.attributeValue);
break;
default:
return CHIP_ERROR_INVALID_ARGUMENT;
}
}
ReturnErrorOnFailure(pair_iterator.GetStatus());

// TODO : Implement action on frequency when frequency not provisional anymore
// if(LevelControlHasFeature(endpoint, LevelControl::Feature::kFrequency)){}
Status status;
CommandId command = LevelControlHasFeature(endpoint, LevelControl::Feature::kOnOff) ? Commands::MoveToLevelWithOnOff::Id
: Commands::MoveToLevel::Id;

status = moveToLevelHandler(
endpoint, command, level, app::DataModel::MakeNullable<uint16_t>(static_cast<uint16_t>(timeMs / 100)),
chip::Optional<BitMask<LevelControlOptions>>(), chip::Optional<BitMask<LevelControlOptions>>(), INVALID_STORED_LEVEL);

if (status != Status::Success)
{
return CHIP_ERROR_READ_FAILED;
}

return CHIP_NO_ERROR;
}
};
static DefaultLevelControlSceneHandler sLevelControlSceneHandler;

#endif // EMBER_AF_PLUGIN_SCENES

#if !defined(IGNORE_LEVEL_CONTROL_CLUSTER_OPTIONS) && defined(EMBER_AF_PLUGIN_COLOR_CONTROL_SERVER_TEMP)
static void reallyUpdateCoupledColorTemp(EndpointId endpoint);
#define updateCoupledColorTemp(endpoint) reallyUpdateCoupledColorTemp(endpoint)
Expand Down Expand Up @@ -446,7 +577,7 @@ static bool shouldExecuteIfOff(EndpointId endpoint, CommandId commandId,
}

#endif // IGNORE_LEVEL_CONTROL_CLUSTER_OPTIONS
// By default, we return true to continue supporting backwards compatibility.
// By default, we return true to continue supporting backwards compatibility.
return true;
}

Expand Down Expand Up @@ -482,6 +613,16 @@ Status MoveToLevel(EndpointId endpointId, const Commands::MoveToLevel::Decodable
Optional<BitMask<LevelControlOptions>>(optionsOverride),
INVALID_STORED_LEVEL); // Don't revert to the stored level
}

chip::scenes::SceneHandler * GetSceneHandler()
{
#ifdef EMBER_AF_PLUGIN_SCENES
return &sLevelControlSceneHandler;
#else
return nullptr;
#endif // EMBER_AF_PLUGIN_SCENES
}

} // namespace LevelControlServer

bool emberAfLevelControlClusterMoveToLevelWithOnOffCallback(app::CommandHandler * commandObj,
Expand Down Expand Up @@ -1304,6 +1445,11 @@ void emberAfLevelControlClusterServerInitCallback(EndpointId endpoint)
}
}

#ifdef EMBER_AF_PLUGIN_SCENES
// Registers Scene handlers for the level control cluster on the server
app::Clusters::Scenes::ScenesServer::Instance().RegisterSceneHandler(endpoint, LevelControlServer::GetSceneHandler());
#endif

emberAfPluginLevelControlClusterServerPostInitCallback(endpoint);
}

Expand Down
3 changes: 3 additions & 0 deletions src/app/clusters/level-control/level-control.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@

#include <app-common/zap-generated/cluster-enums.h>
#include <app-common/zap-generated/cluster-objects.h>
#include <app/clusters/scenes-server/SceneTable.h>
#include <app/util/basic-types.h>

/** @brief Level Control Cluster Server Post Init
Expand All @@ -52,4 +53,6 @@ chip::Protocols::InteractionModel::Status
MoveToLevel(chip::EndpointId endpointId,
const chip::app::Clusters::LevelControl::Commands::MoveToLevel::DecodableType & commandData);

chip::scenes::SceneHandler * GetSceneHandler();

} // namespace LevelControlServer
10 changes: 8 additions & 2 deletions src/app/clusters/on-off-server/on-off-server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -289,7 +289,13 @@ class DefaultOnOffSceneHandler : public scenes::DefaultSceneHandlerImpl
return err;
}

OnOffServer::Instance().scheduleTimerCallbackMs(sceneEventControl(endpoint), timeMs);
#ifdef EMBER_AF_PLUGIN_LEVEL_CONTROL
if (!(LevelControlWithOnOffFeaturePresent(endpoint) &&
Scenes::ScenesServer::Instance().IsHandlerRegistered(endpoint, LevelControlServer::GetSceneHandler())))
#endif
{
OnOffServer::Instance().scheduleTimerCallbackMs(sceneEventControl(endpoint), timeMs);
}

return CHIP_NO_ERROR;
}
Expand Down Expand Up @@ -592,7 +598,7 @@ void OnOffServer::initOnOffServer(chip::EndpointId endpoint)

#ifdef EMBER_AF_PLUGIN_SCENES
// Registers Scene handlers for the On/Off cluster on the server
app::Clusters::Scenes::ScenesServer::Instance().RegisterSceneHandler(OnOffServer::Instance().GetSceneHandler());
app::Clusters::Scenes::ScenesServer::Instance().RegisterSceneHandler(endpoint, OnOffServer::Instance().GetSceneHandler());
#endif

#ifdef EMBER_AF_PLUGIN_MODE_SELECT
Expand Down
12 changes: 4 additions & 8 deletions src/app/clusters/scenes-server/SceneTableImpl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -848,26 +848,22 @@ CHIP_ERROR DefaultSceneTableImpl::SceneSaveEFS(SceneTableEntry & scene)
/// @param scene Scene providing the EFSs (extension field sets)
CHIP_ERROR DefaultSceneTableImpl::SceneApplyEFS(const SceneTableEntry & scene)
{
ExtensionFieldSet EFS;
TransitionTimeMs time;
clusterId cluster;

if (!this->HandlerListEmpty())
{
for (uint8_t i = 0; i < scene.mStorageData.mExtensionFieldSets.GetFieldSetCount(); i++)
{
ExtensionFieldSet EFS;
scene.mStorageData.mExtensionFieldSets.GetFieldSetAtPosition(EFS, i);
cluster = EFS.mID;
time = scene.mStorageData.mSceneTransitionTimeMs;
ByteSpan EFSSpan = MutableByteSpan(EFS.mBytesBuffer, EFS.mUsedBytes);

if (!EFS.IsEmpty())
{
for (auto & handler : mHandlerList)
{
if (handler.SupportsCluster(mEndpointId, cluster))
if (handler.SupportsCluster(mEndpointId, EFS.mID))
{
ReturnErrorOnFailure(handler.ApplyScene(mEndpointId, cluster, EFSSpan, time));
ReturnErrorOnFailure(
handler.ApplyScene(mEndpointId, EFS.mID, EFSSpan, scene.mStorageData.mSceneTransitionTimeMs));
break;
}
}
Expand Down
16 changes: 8 additions & 8 deletions src/app/clusters/scenes-server/scenes-server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -553,27 +553,27 @@ void ScenesServer::RecallScene(FabricIndex aFabricIx, EndpointId aEndpointId, Gr
}
}

bool ScenesServer::IsHandlerRegistered(scenes::SceneHandler * handler)
bool ScenesServer::IsHandlerRegistered(EndpointId aEndpointId, scenes::SceneHandler * handler)
{
SceneTable * sceneTable = scenes::GetSceneTableImpl();
SceneTable * sceneTable = scenes::GetSceneTableImpl(aEndpointId);
return sceneTable->mHandlerList.Contains(handler);
}

void ScenesServer::RegisterSceneHandler(scenes::SceneHandler * handler)
void ScenesServer::RegisterSceneHandler(EndpointId aEndpointId, scenes::SceneHandler * handler)
{
SceneTable * sceneTable = scenes::GetSceneTableImpl();
SceneTable * sceneTable = scenes::GetSceneTableImpl(aEndpointId);

if (!IsHandlerRegistered(handler))
if (!IsHandlerRegistered(aEndpointId, handler))
{
sceneTable->RegisterHandler(handler);
}
}

void ScenesServer::UnregisterSceneHandler(scenes::SceneHandler * handler)
void ScenesServer::UnregisterSceneHandler(EndpointId aEndpointId, scenes::SceneHandler * handler)
{
SceneTable * sceneTable = scenes::GetSceneTableImpl();
SceneTable * sceneTable = scenes::GetSceneTableImpl(aEndpointId);

if (IsHandlerRegistered(handler))
if (IsHandlerRegistered(aEndpointId, handler))
{
sceneTable->UnregisterHandler(handler);
}
Expand Down
6 changes: 3 additions & 3 deletions src/app/clusters/scenes-server/scenes-server.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,9 @@ class ScenesServer : public CommandHandlerInterface, public AttributeAccessInter
void StoreCurrentScene(FabricIndex aFabricIx, EndpointId aEndpointId, GroupId aGroupId, SceneId aSceneId);
void RecallScene(FabricIndex aFabricIx, EndpointId aEndpointId, GroupId aGroupId, SceneId aSceneId);

bool IsHandlerRegistered(scenes::SceneHandler * handler);
void RegisterSceneHandler(scenes::SceneHandler * handler);
void UnregisterSceneHandler(scenes::SceneHandler * handler);
bool IsHandlerRegistered(EndpointId aEndpointId, scenes::SceneHandler * handler);
void RegisterSceneHandler(EndpointId aEndpointId, scenes::SceneHandler * handler);
void UnregisterSceneHandler(EndpointId aEndpointId, scenes::SceneHandler * handler);

private:
ScenesServer() : CommandHandlerInterface(Optional<EndpointId>(), Id), AttributeAccessInterface(Optional<EndpointId>(), Id) {}
Expand Down

0 comments on commit 5331373

Please sign in to comment.