Skip to content

Commit

Permalink
Added scene handler to mode select cluster
Browse files Browse the repository at this point in the history
  • Loading branch information
lpbeliveau-silabs committed Dec 6, 2023
1 parent f6ae79d commit f39eec9
Show file tree
Hide file tree
Showing 3 changed files with 191 additions and 13 deletions.
187 changes: 177 additions & 10 deletions src/app/clusters/mode-select-server/mode-select-server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@
#include <app/clusters/on-off-server/on-off-server.h>
#endif // EMBER_AF_PLUGIN_ON_OFF

#ifdef EMBER_AF_PLUGIN_SCENES
#include <app/clusters/scenes-server/scenes-server.h>
#endif // EMBER_AF_PLUGIN_SCENES

using namespace std;
using namespace chip;
using namespace chip::app;
Expand Down Expand Up @@ -93,25 +97,183 @@ CHIP_ERROR ModeSelectAttrAccess::Read(const ConcreteReadAttributePath & aPath, A
return CHIP_NO_ERROR;
}

} // anonymous namespace

bool emberAfModeSelectClusterChangeToModeCallback(CommandHandler * commandHandler, const ConcreteCommandPath & commandPath,
const ModeSelect::Commands::ChangeToMode::DecodableType & commandData)
Status ChangeToMode(EndpointId endpointId, uint8_t newMode)
{
ChipLogProgress(Zcl, "ModeSelect: Entering emberAfModeSelectClusterChangeToModeCallback");
EndpointId endpointId = commandPath.mEndpointId;
uint8_t newMode = commandData.newMode;
// Check that the newMode matches one of the supported options
const ModeSelect::Structs::ModeOptionStruct::Type * modeOptionPtr;
Status checkSupportedModeStatus =
ModeSelect::getSupportedModesManager()->getModeOptionByMode(endpointId, newMode, &modeOptionPtr);
if (Status::Success != checkSupportedModeStatus)
{
ChipLogProgress(Zcl, "ModeSelect: Failed to find the option with mode %u", newMode);
commandHandler->AddStatus(commandPath, checkSupportedModeStatus);
return checkSupportedModeStatus;
}

uint8_t oldMode = 0;
ModeSelect::Attributes::CurrentMode::Get(endpointId, &oldMode);

return Status::Success;
}

} // anonymous namespace

#if defined(EMBER_AF_PLUGIN_SCENES) && CHIP_CONFIG_SCENES_USE_DEFAULT_HANDLERS
static constexpr size_t kModeSelectMaxEnpointCount =
EMBER_AF_MODE_SELECT_CLUSTER_SERVER_ENDPOINT_COUNT + CHIP_DEVICE_CONFIG_DYNAMIC_ENDPOINT_COUNT;

static void timerCallback(System::Layer *, void * callbackContext);
static void sceneModeSelectCallback(EndpointId endpoint);
using ModeSelectEndPointPair = scenes::DefaultSceneHandlerImpl::EndpointStatePair<uint8_t>;
using ModeSelectTransitionTimeInterface =
scenes::DefaultSceneHandlerImpl::TransitionTimeInterface<kModeSelectMaxEnpointCount,
EMBER_AF_ON_OFF_CLUSTER_SERVER_ENDPOINT_COUNT>;

class DefaultModeSelectSceneHandler : public scenes::DefaultSceneHandlerImpl
{
public:
DefaultSceneHandlerImpl::StatePairBuffer<uint8_t, kModeSelectMaxEnpointCount> mSceneEndpointStatePairs;
// As per spec, 1 attribute is scenable in the mode select cluster
static constexpr uint8_t scenableAttributeCount = 1;

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

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

// Default function for mode select cluster, only checks if mode select is enabled on the endpoint
bool SupportsCluster(EndpointId endpoint, ClusterId cluster) override
{
return (cluster == ModeSelect::Id) && (emberAfContainsServer(endpoint, ModeSelect::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;

uint8_t currentMode;
// read CurrentMode value
EmberAfStatus status = Attributes::CurrentMode::Get(endpoint, &currentMode);
if (status != EMBER_ZCL_STATUS_SUCCESS)
{
ChipLogError(Zcl, "ERR: reading CurrentMode %x", status);
return CHIP_ERROR_READ_FAILED;
}

AttributeValuePair pairs[scenableAttributeCount];

pairs[0].attributeID = Attributes::CurrentMode::Id;
pairs[0].attributeValue = currentMode;

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

return EncodeAttributeValueList(attributeValueList, serializedBytes);
}

/// @brief Default EFS interaction when applying scene to the ModeSelect 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;

VerifyOrReturnError(cluster == ModeSelect::Id, CHIP_ERROR_INVALID_ARGUMENT);

ReturnErrorOnFailure(DecodeAttributeValueList(serializedBytes, attributeValueList));

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

auto pair_iterator = attributeValueList.begin();
while (pair_iterator.Next())
{
auto & decodePair = pair_iterator.GetValue();
VerifyOrReturnError(decodePair.attributeID == Attributes::CurrentMode::Id, CHIP_ERROR_INVALID_ARGUMENT);
ReturnErrorOnFailure(mSceneEndpointStatePairs.InsertPair(
ModeSelectEndPointPair(endpoint, static_cast<uint8_t>(decodePair.attributeValue))));
}
// Verify that the EFS was completely read
CHIP_ERROR err = pair_iterator.GetStatus();
if (CHIP_NO_ERROR != err)
{
mSceneEndpointStatePairs.RemovePair(endpoint);
return err;
}

DeviceLayer::SystemLayer().StartTimer(chip::System::Clock::Milliseconds32(timeMs), timerCallback,
mTransitionTimeInterface.sceneEventControl(endpoint));

return CHIP_NO_ERROR;
}

private:
ModeSelectTransitionTimeInterface mTransitionTimeInterface =
ModeSelectTransitionTimeInterface(Attributes::CurrentMode::Id, sceneModeSelectCallback);
};
static DefaultModeSelectSceneHandler sModeSelectSceneHandler;

static void timerCallback(System::Layer *, void * callbackContext)
{
auto control = static_cast<EmberEventControl *>(callbackContext);
(control->callback)(control->endpoint);
}

static void sceneModeSelectCallback(EndpointId endpoint)
{
ModeSelectEndPointPair savedState;
ReturnOnFailure(sModeSelectSceneHandler.mSceneEndpointStatePairs.GetPair(endpoint, savedState));
ChangeToMode(endpoint, savedState.mValue);
sModeSelectSceneHandler.mSceneEndpointStatePairs.RemovePair(endpoint);
}

#endif // defined(EMBER_AF_PLUGIN_SCENES) && CHIP_CONFIG_SCENES_USE_DEFAULT_HANDLERS

bool emberAfModeSelectClusterChangeToModeCallback(CommandHandler * commandHandler, const ConcreteCommandPath & commandPath,
const ModeSelect::Commands::ChangeToMode::DecodableType & commandData)
{
ChipLogProgress(Zcl, "ModeSelect: Entering emberAfModeSelectClusterChangeToModeCallback");

uint8_t currentMode = 0;
ModeSelect::Attributes::CurrentMode::Get(commandPath.mEndpointId, &currentMode);
#ifdef EMBER_AF_PLUGIN_SCENES
if (currentMode != commandData.newMode)
{
// the scene has been changed (the value of CurrentMode has changed) so
// the current scene as described in the scene table is invalid
Scenes::ScenesServer::Instance().MakeSceneInvalidForAllFabrics(commandPath.mEndpointId);
}
#endif // EMBER_AF_PLUGIN_SCENES

Status status = ChangeToMode(commandPath.mEndpointId, commandData.newMode);

if (Status::Success != status)
{
ChipLogProgress(Zcl, "ModeSelect: Failed to find the option with mode %u", commandData.newMode);
commandHandler->AddStatus(commandPath, status);
return true;
}
ModeSelect::Attributes::CurrentMode::Set(endpointId, newMode);

ChipLogProgress(Zcl, "ModeSelect: ChangeToMode successful");
commandHandler->AddStatus(commandPath, Status::Success);
Expand All @@ -138,6 +300,11 @@ void emberAfModeSelectClusterServerInitCallback(EndpointId endpointId)
EmberAfStatus status = Attributes::StartUpMode::Get(endpointId, startUpMode);
if (status == EMBER_ZCL_STATUS_SUCCESS && !startUpMode.IsNull())
{

#if defined(EMBER_AF_PLUGIN_SCENES) && CHIP_CONFIG_SCENES_USE_DEFAULT_HANDLERS
app::Clusters::Scenes::ScenesServer::Instance().RegisterSceneHandler(endpointId, &sModeSelectSceneHandler);
#endif // defined(EMBER_AF_PLUGIN_SCENES) && CHIP_CONFIG_SCENES_USE_DEFAULT_HANDLERS

#ifdef EMBER_AF_PLUGIN_ON_OFF
// OnMode with Power Up
// If the On/Off feature is supported and the On/Off cluster attribute StartUpOnOff is present, with a
Expand Down
10 changes: 10 additions & 0 deletions src/app/tests/suites/certification/Test_TC_S_2_2.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -475,6 +475,16 @@ tests:
},
],
},
{
ClusterID: 0x0050,
AttributeValueList:
[
{
AttributeID: 0x0003,
AttributeValue: 0x00,
},
],
},
]

- label:
Expand Down
7 changes: 4 additions & 3 deletions src/lib/core/CHIPConfig.h
Original file line number Diff line number Diff line change
Expand Up @@ -1429,11 +1429,12 @@ extern const char CHIP_NON_PRODUCTION_MARKER[];
#endif

/**
* @brief The maximum number of clusters per scene, defaults to 3 for a typical usecase (onOff + level control + color control
* cluster). Needs to be changed in case a greater number of clusters is chosen.
* @brief The maximum number of clusters per scene, we recommend using 3 for a typical use case (onOff + level control + color
* control cluster). Needs to be changed in case a greater number of clusters is chosen. Now set to 4 to allow for the addition of
* the mode select cluster.
*/
#ifndef CHIP_CONFIG_SCENES_MAX_CLUSTERS_PER_SCENE
#define CHIP_CONFIG_SCENES_MAX_CLUSTERS_PER_SCENE 3
#define CHIP_CONFIG_SCENES_MAX_CLUSTERS_PER_SCENE 4
#endif

/**
Expand Down

0 comments on commit f39eec9

Please sign in to comment.