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]Added the level-control cluster handler for scenes EFS #28836

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
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;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will be broken if the scene is then recalled with a different maxlevel for some reason.

Is there a reason this is not using the existing attribute-storage-null-handling.h facilities which would let us encode null in an integer?

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)),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do you need both the cast and the explicit template argument? You shouldn't need both...

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But if the level was null when we stored the scene... won't this just ignore the moveToLevelHandler call, at least when MaxLevel is 254? This looks broken.

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) &&
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs some documentation explaining why this logic is here.

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