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] Mode Select Cluster Handler #30855

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
197 changes: 186 additions & 11 deletions src/app/clusters/mode-select-server/mode-select-server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,14 @@
#include <app/util/odd-sized-integers.h>
#include <app/util/util.h>
#include <lib/support/CodeUtils.h>
#include <platform/CHIPDeviceLayer.h>
#include <platform/DiagnosticDataProvider.h>
#include <tracing/macros.h>

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

#ifdef MATTER_DM_PLUGIN_ON_OFF
#include <app/clusters/on-off-server/on-off-server.h>
#endif // MATTER_DM_PLUGIN_ON_OFF
Expand Down Expand Up @@ -109,35 +114,200 @@ 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)
{
MATTER_TRACE_SCOPE("ChangeToMode", "ModeSelect");
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;
const ModeSelect::SupportedModesManager * gSupportedModeManager = ModeSelect::getSupportedModesManager();
if (gSupportedModeManager == nullptr)
{
ChipLogError(Zcl, "ModeSelect: SupportedModesManager is NULL");
commandHandler->AddStatus(commandPath, Status::Failure);
return true;
return Status::Failure;
}
Status checkSupportedModeStatus = gSupportedModeManager->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 true;
return checkSupportedModeStatus;
}
ModeSelect::Attributes::CurrentMode::Set(endpointId, newMode);

return Status::Success;
}

} // anonymous namespace

#if defined(MATTER_DM_PLUGIN_SCENES_MANAGEMENT) && CHIP_CONFIG_SCENES_USE_DEFAULT_HANDLERS
lpbeliveau-silabs marked this conversation as resolved.
Show resolved Hide resolved
static constexpr size_t kModeSelectMaxEnpointCount =
MATTER_DM_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,
MATTER_DM_MODE_SELECT_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 kScenableAttributeCount = 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
{
if (emberAfContainsServer(endpoint, ModeSelect::Id) && clusterBuffer.size() >= 1)
{
clusterBuffer[0] = ModeSelect::Id;
clusterBuffer.reduce_size(1);
}
else
{
clusterBuffer.reduce_size(0);
}
lpbeliveau-silabs marked this conversation as resolved.
Show resolved Hide resolved
}

// 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 [in] endpoint target endpoint
/// @param [in] cluster target cluster
/// @param [out] 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
lpbeliveau-silabs marked this conversation as resolved.
Show resolved Hide resolved
{
using AttributeValuePair = ScenesManagement::Structs::AttributeValuePairStruct::Type;

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

AttributeValuePair pairs[kScenableAttributeCount];

pairs[0].attributeID = Attributes::CurrentMode::Id;
pairs[0].valueUnsigned8.SetValue(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<ScenesManagement::Structs::AttributeValuePairStruct::DecodableType> attributeValueList;

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

ReturnErrorOnFailure(DecodeAttributeValueList(serializedBytes, attributeValueList));

size_t attributeCount = 0;
ReturnErrorOnFailure(attributeValueList.ComputeSize(&attributeCount));
VerifyOrReturnError(attributeCount <= kScenableAttributeCount, 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);
lpbeliveau-silabs marked this conversation as resolved.
Show resolved Hide resolved
VerifyOrReturnError(decodePair.valueUnsigned8.HasValue(), CHIP_ERROR_INVALID_ARGUMENT);
ReturnErrorOnFailure(mSceneEndpointStatePairs.InsertPair(
ModeSelectEndPointPair(endpoint, static_cast<uint8_t>(decodePair.valueUnsigned8.HasValue()))));
}
// Verify that the EFS was completely read
CHIP_ERROR err = pair_iterator.GetStatus();
if (CHIP_NO_ERROR != err)
{
mSceneEndpointStatePairs.RemovePair(endpoint);
return err;
}

VerifyOrReturnError(mTransitionTimeInterface.sceneEventControl(endpoint) != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
DeviceLayer::SystemLayer().StartTimer(chip::System::Clock::Milliseconds32(timeMs), timerCallback,
mTransitionTimeInterface.sceneEventControl(endpoint));

return CHIP_NO_ERROR;
}

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

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

/**
* @brief This function is a callback to apply the mode that was saved when the ApplyScene was called with a transition time greater
* than 0.
*
* @param endpoint The endpoint ID that the scene mode selection is associated with.
*
*/
static void sceneModeSelectCallback(EndpointId endpoint)
lpbeliveau-silabs marked this conversation as resolved.
Show resolved Hide resolved
{
ModeSelectEndPointPair savedState;
ReturnOnFailure(sModeSelectSceneHandler.mSceneEndpointStatePairs.GetPair(endpoint, savedState));
ChangeToMode(endpoint, savedState.mValue);
sModeSelectSceneHandler.mSceneEndpointStatePairs.RemovePair(endpoint);
}

#endif // defined(MATTER_DM_PLUGIN_SCENES_MANAGEMENT) && 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 MATTER_DM_PLUGIN_SCENES_MANAGEMENT
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
ScenesManagement::ScenesServer::Instance().MakeSceneInvalidForAllFabrics(commandPath.mEndpointId);
}
#endif // MATTER_DM_PLUGIN_SCENES_MANAGEMENT

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

if (Status::Success != status)
{
commandHandler->AddStatus(commandPath, status);
return true;
}

ChipLogProgress(Zcl, "ModeSelect: ChangeToMode successful");
commandHandler->AddStatus(commandPath, Status::Success);
commandHandler->AddStatus(commandPath, status);
return true;
}

Expand All @@ -148,6 +318,10 @@ bool emberAfModeSelectClusterChangeToModeCallback(CommandHandler * commandHandle
*/
void emberAfModeSelectClusterServerInitCallback(EndpointId endpointId)
{
#if defined(MATTER_DM_PLUGIN_SCENES_MANAGEMENT) && CHIP_CONFIG_SCENES_USE_DEFAULT_HANDLERS
ScenesManagement::ScenesServer::Instance().RegisterSceneHandler(endpointId, &sModeSelectSceneHandler);
#endif // defined(MATTER_DM_PLUGIN_SCENES_MANAGEMENT) && CHIP_CONFIG_SCENES_USE_DEFAULT_HANDLERS

// StartUp behavior relies on CurrentMode StartUpMode attributes being non-volatile.
if (areStartUpModeAndCurrentModeNonVolatile(endpointId))
{
Expand All @@ -161,6 +335,7 @@ void emberAfModeSelectClusterServerInitCallback(EndpointId endpointId)
Status status = Attributes::StartUpMode::Get(endpointId, startUpMode);
if (status == Status::Success && !startUpMode.IsNull())
{

#ifdef MATTER_DM_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/TestScenesFabricSceneInfo.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,11 @@ tests:
},
],
},
{
ClusterID: 0x0050,
AttributeValueList:
[{ AttributeID: 0x0003, ValueUnsigned8: 0x01 }],
},
]
response:
values:
Expand Down Expand Up @@ -349,6 +354,11 @@ tests:
},
],
},
{
ClusterID: 0x0050,
AttributeValueList:
[{ AttributeID: 0x0003, ValueUnsigned8: 0x01 }],
},
]

- label: "Read the FabricSceneInfo attribute (0x0007) "
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 @@ -1485,11 +1485,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 4 for a typical use case (onOff + level control + color
* control cluster + mode selec cluster). Needs to be changed in case a greater number of clusters is chosen. In the event the
* device does not need to support the mode select cluster, the maximum number of clusters per scene should be set to 3.
*/
#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
Loading