Skip to content

Commit 34d633c

Browse files
[Scenes] Mode Select Cluster Handler (project-chip#30855)
* Added scene handler to mode select cluster * Corrected wrong define usage and applied a fix to attribute-storage function * Applied suggestions
1 parent 9df818b commit 34d633c

File tree

3 files changed

+200
-14
lines changed

3 files changed

+200
-14
lines changed

src/app/clusters/mode-select-server/mode-select-server.cpp

+186-11
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,14 @@
3030
#include <app/util/odd-sized-integers.h>
3131
#include <app/util/util.h>
3232
#include <lib/support/CodeUtils.h>
33+
#include <platform/CHIPDeviceLayer.h>
3334
#include <platform/DiagnosticDataProvider.h>
3435
#include <tracing/macros.h>
3536

37+
#ifdef MATTER_DM_PLUGIN_SCENES_MANAGEMENT
38+
#include <app/clusters/scenes-server/scenes-server.h>
39+
#endif // MATTER_DM_PLUGIN_SCENES_MANAGEMENT
40+
3641
#ifdef MATTER_DM_PLUGIN_ON_OFF
3742
#include <app/clusters/on-off-server/on-off-server.h>
3843
#endif // MATTER_DM_PLUGIN_ON_OFF
@@ -109,35 +114,200 @@ CHIP_ERROR ModeSelectAttrAccess::Read(const ConcreteReadAttributePath & aPath, A
109114
return CHIP_NO_ERROR;
110115
}
111116

112-
} // anonymous namespace
113-
114-
bool emberAfModeSelectClusterChangeToModeCallback(CommandHandler * commandHandler, const ConcreteCommandPath & commandPath,
115-
const ModeSelect::Commands::ChangeToMode::DecodableType & commandData)
117+
Status ChangeToMode(EndpointId endpointId, uint8_t newMode)
116118
{
117119
MATTER_TRACE_SCOPE("ChangeToMode", "ModeSelect");
118120
ChipLogProgress(Zcl, "ModeSelect: Entering emberAfModeSelectClusterChangeToModeCallback");
119-
EndpointId endpointId = commandPath.mEndpointId;
120-
uint8_t newMode = commandData.newMode;
121+
121122
// Check that the newMode matches one of the supported options
122123
const ModeSelect::Structs::ModeOptionStruct::Type * modeOptionPtr;
123124
const ModeSelect::SupportedModesManager * gSupportedModeManager = ModeSelect::getSupportedModesManager();
124125
if (gSupportedModeManager == nullptr)
125126
{
126127
ChipLogError(Zcl, "ModeSelect: SupportedModesManager is NULL");
127-
commandHandler->AddStatus(commandPath, Status::Failure);
128-
return true;
128+
return Status::Failure;
129129
}
130130
Status checkSupportedModeStatus = gSupportedModeManager->getModeOptionByMode(endpointId, newMode, &modeOptionPtr);
131131
if (Status::Success != checkSupportedModeStatus)
132132
{
133133
ChipLogProgress(Zcl, "ModeSelect: Failed to find the option with mode %u", newMode);
134-
commandHandler->AddStatus(commandPath, checkSupportedModeStatus);
135-
return true;
134+
return checkSupportedModeStatus;
136135
}
137136
ModeSelect::Attributes::CurrentMode::Set(endpointId, newMode);
138137

138+
return Status::Success;
139+
}
140+
141+
} // anonymous namespace
142+
143+
#if defined(MATTER_DM_PLUGIN_SCENES_MANAGEMENT) && CHIP_CONFIG_SCENES_USE_DEFAULT_HANDLERS
144+
static constexpr size_t kModeSelectMaxEnpointCount =
145+
MATTER_DM_MODE_SELECT_CLUSTER_SERVER_ENDPOINT_COUNT + CHIP_DEVICE_CONFIG_DYNAMIC_ENDPOINT_COUNT;
146+
147+
static void timerCallback(System::Layer *, void * callbackContext);
148+
static void sceneModeSelectCallback(EndpointId endpoint);
149+
using ModeSelectEndPointPair = scenes::DefaultSceneHandlerImpl::EndpointStatePair<uint8_t>;
150+
using ModeSelectTransitionTimeInterface =
151+
scenes::DefaultSceneHandlerImpl::TransitionTimeInterface<kModeSelectMaxEnpointCount,
152+
MATTER_DM_MODE_SELECT_CLUSTER_SERVER_ENDPOINT_COUNT>;
153+
154+
class DefaultModeSelectSceneHandler : public scenes::DefaultSceneHandlerImpl
155+
{
156+
public:
157+
DefaultSceneHandlerImpl::StatePairBuffer<uint8_t, kModeSelectMaxEnpointCount> mSceneEndpointStatePairs;
158+
// As per spec, 1 attribute is scenable in the mode select cluster
159+
static constexpr uint8_t kScenableAttributeCount = 1;
160+
161+
DefaultModeSelectSceneHandler() = default;
162+
~DefaultModeSelectSceneHandler() override {}
163+
164+
// Default function for the mode select cluster, only puts the mode select cluster ID in the span if supported on the given
165+
// endpoint
166+
virtual void GetSupportedClusters(EndpointId endpoint, Span<ClusterId> & clusterBuffer) override
167+
{
168+
if (emberAfContainsServer(endpoint, ModeSelect::Id) && clusterBuffer.size() >= 1)
169+
{
170+
clusterBuffer[0] = ModeSelect::Id;
171+
clusterBuffer.reduce_size(1);
172+
}
173+
else
174+
{
175+
clusterBuffer.reduce_size(0);
176+
}
177+
}
178+
179+
// Default function for mode select cluster, only checks if mode select is enabled on the endpoint
180+
bool SupportsCluster(EndpointId endpoint, ClusterId cluster) override
181+
{
182+
return (cluster == ModeSelect::Id) && (emberAfContainsServer(endpoint, ModeSelect::Id));
183+
}
184+
185+
/// @brief Serialize the Cluster's EFS value
186+
/// @param [in] endpoint target endpoint
187+
/// @param [in] cluster target cluster
188+
/// @param [out] serializedBytes data to serialize into EFS
189+
/// @return CHIP_NO_ERROR if successfully serialized the data, CHIP_ERROR_INVALID_ARGUMENT otherwise
190+
CHIP_ERROR SerializeSave(EndpointId endpoint, ClusterId cluster, MutableByteSpan & serializedBytes) override
191+
{
192+
using AttributeValuePair = ScenesManagement::Structs::AttributeValuePairStruct::Type;
193+
194+
uint8_t currentMode;
195+
// read CurrentMode value
196+
Status status = Attributes::CurrentMode::Get(endpoint, &currentMode);
197+
if (status != Status::Success)
198+
{
199+
ChipLogError(Zcl, "ERR: reading CurrentMode 0x%02x", to_underlying(status));
200+
return CHIP_ERROR_READ_FAILED;
201+
}
202+
203+
AttributeValuePair pairs[kScenableAttributeCount];
204+
205+
pairs[0].attributeID = Attributes::CurrentMode::Id;
206+
pairs[0].valueUnsigned8.SetValue(currentMode);
207+
208+
app::DataModel::List<AttributeValuePair> attributeValueList(pairs);
209+
210+
return EncodeAttributeValueList(attributeValueList, serializedBytes);
211+
}
212+
213+
/// @brief Default EFS interaction when applying scene to the ModeSelect Cluster
214+
/// @param endpoint target endpoint
215+
/// @param cluster target cluster
216+
/// @param serializedBytes Data from nvm
217+
/// @param timeMs transition time in ms
218+
/// @return CHIP_NO_ERROR if value as expected, CHIP_ERROR_INVALID_ARGUMENT otherwise
219+
CHIP_ERROR ApplyScene(EndpointId endpoint, ClusterId cluster, const ByteSpan & serializedBytes,
220+
scenes::TransitionTimeMs timeMs) override
221+
{
222+
app::DataModel::DecodableList<ScenesManagement::Structs::AttributeValuePairStruct::DecodableType> attributeValueList;
223+
224+
VerifyOrReturnError(cluster == ModeSelect::Id, CHIP_ERROR_INVALID_ARGUMENT);
225+
226+
ReturnErrorOnFailure(DecodeAttributeValueList(serializedBytes, attributeValueList));
227+
228+
size_t attributeCount = 0;
229+
ReturnErrorOnFailure(attributeValueList.ComputeSize(&attributeCount));
230+
VerifyOrReturnError(attributeCount <= kScenableAttributeCount, CHIP_ERROR_BUFFER_TOO_SMALL);
231+
232+
auto pair_iterator = attributeValueList.begin();
233+
while (pair_iterator.Next())
234+
{
235+
auto & decodePair = pair_iterator.GetValue();
236+
VerifyOrReturnError(decodePair.attributeID == Attributes::CurrentMode::Id, CHIP_ERROR_INVALID_ARGUMENT);
237+
VerifyOrReturnError(decodePair.valueUnsigned8.HasValue(), CHIP_ERROR_INVALID_ARGUMENT);
238+
ReturnErrorOnFailure(mSceneEndpointStatePairs.InsertPair(
239+
ModeSelectEndPointPair(endpoint, static_cast<uint8_t>(decodePair.valueUnsigned8.HasValue()))));
240+
}
241+
// Verify that the EFS was completely read
242+
CHIP_ERROR err = pair_iterator.GetStatus();
243+
if (CHIP_NO_ERROR != err)
244+
{
245+
mSceneEndpointStatePairs.RemovePair(endpoint);
246+
return err;
247+
}
248+
249+
VerifyOrReturnError(mTransitionTimeInterface.sceneEventControl(endpoint) != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
250+
DeviceLayer::SystemLayer().StartTimer(chip::System::Clock::Milliseconds32(timeMs), timerCallback,
251+
mTransitionTimeInterface.sceneEventControl(endpoint));
252+
253+
return CHIP_NO_ERROR;
254+
}
255+
256+
private:
257+
ModeSelectTransitionTimeInterface mTransitionTimeInterface =
258+
ModeSelectTransitionTimeInterface(ModeSelect::Id, sceneModeSelectCallback);
259+
};
260+
static DefaultModeSelectSceneHandler sModeSelectSceneHandler;
261+
262+
static void timerCallback(System::Layer *, void * callbackContext)
263+
{
264+
auto control = static_cast<EmberEventControl *>(callbackContext);
265+
(control->callback)(control->endpoint);
266+
}
267+
268+
/**
269+
* @brief This function is a callback to apply the mode that was saved when the ApplyScene was called with a transition time greater
270+
* than 0.
271+
*
272+
* @param endpoint The endpoint ID that the scene mode selection is associated with.
273+
*
274+
*/
275+
static void sceneModeSelectCallback(EndpointId endpoint)
276+
{
277+
ModeSelectEndPointPair savedState;
278+
ReturnOnFailure(sModeSelectSceneHandler.mSceneEndpointStatePairs.GetPair(endpoint, savedState));
279+
ChangeToMode(endpoint, savedState.mValue);
280+
sModeSelectSceneHandler.mSceneEndpointStatePairs.RemovePair(endpoint);
281+
}
282+
283+
#endif // defined(MATTER_DM_PLUGIN_SCENES_MANAGEMENT) && CHIP_CONFIG_SCENES_USE_DEFAULT_HANDLERS
284+
285+
bool emberAfModeSelectClusterChangeToModeCallback(CommandHandler * commandHandler, const ConcreteCommandPath & commandPath,
286+
const ModeSelect::Commands::ChangeToMode::DecodableType & commandData)
287+
{
288+
ChipLogProgress(Zcl, "ModeSelect: Entering emberAfModeSelectClusterChangeToModeCallback");
289+
290+
uint8_t currentMode = 0;
291+
ModeSelect::Attributes::CurrentMode::Get(commandPath.mEndpointId, &currentMode);
292+
#ifdef MATTER_DM_PLUGIN_SCENES_MANAGEMENT
293+
if (currentMode != commandData.newMode)
294+
{
295+
// the scene has been changed (the value of CurrentMode has changed) so
296+
// the current scene as described in the scene table is invalid
297+
ScenesManagement::ScenesServer::Instance().MakeSceneInvalidForAllFabrics(commandPath.mEndpointId);
298+
}
299+
#endif // MATTER_DM_PLUGIN_SCENES_MANAGEMENT
300+
301+
Status status = ChangeToMode(commandPath.mEndpointId, commandData.newMode);
302+
303+
if (Status::Success != status)
304+
{
305+
commandHandler->AddStatus(commandPath, status);
306+
return true;
307+
}
308+
139309
ChipLogProgress(Zcl, "ModeSelect: ChangeToMode successful");
140-
commandHandler->AddStatus(commandPath, Status::Success);
310+
commandHandler->AddStatus(commandPath, status);
141311
return true;
142312
}
143313

@@ -148,6 +318,10 @@ bool emberAfModeSelectClusterChangeToModeCallback(CommandHandler * commandHandle
148318
*/
149319
void emberAfModeSelectClusterServerInitCallback(EndpointId endpointId)
150320
{
321+
#if defined(MATTER_DM_PLUGIN_SCENES_MANAGEMENT) && CHIP_CONFIG_SCENES_USE_DEFAULT_HANDLERS
322+
ScenesManagement::ScenesServer::Instance().RegisterSceneHandler(endpointId, &sModeSelectSceneHandler);
323+
#endif // defined(MATTER_DM_PLUGIN_SCENES_MANAGEMENT) && CHIP_CONFIG_SCENES_USE_DEFAULT_HANDLERS
324+
151325
// StartUp behavior relies on CurrentMode StartUpMode attributes being non-volatile.
152326
if (areStartUpModeAndCurrentModeNonVolatile(endpointId))
153327
{
@@ -161,6 +335,7 @@ void emberAfModeSelectClusterServerInitCallback(EndpointId endpointId)
161335
Status status = Attributes::StartUpMode::Get(endpointId, startUpMode);
162336
if (status == Status::Success && !startUpMode.IsNull())
163337
{
338+
164339
#ifdef MATTER_DM_PLUGIN_ON_OFF
165340
// OnMode with Power Up
166341
// If the On/Off feature is supported and the On/Off cluster attribute StartUpOnOff is present, with a

src/app/tests/suites/TestScenesFabricSceneInfo.yaml

+10
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,11 @@ tests:
253253
},
254254
],
255255
},
256+
{
257+
ClusterID: 0x0050,
258+
AttributeValueList:
259+
[{ AttributeID: 0x0003, ValueUnsigned8: 0x01 }],
260+
},
256261
]
257262
response:
258263
values:
@@ -349,6 +354,11 @@ tests:
349354
},
350355
],
351356
},
357+
{
358+
ClusterID: 0x0050,
359+
AttributeValueList:
360+
[{ AttributeID: 0x0003, ValueUnsigned8: 0x01 }],
361+
},
352362
]
353363

354364
- label: "Read the FabricSceneInfo attribute (0x0007) "

src/lib/core/CHIPConfig.h

+4-3
Original file line numberDiff line numberDiff line change
@@ -1485,11 +1485,12 @@ extern const char CHIP_NON_PRODUCTION_MARKER[];
14851485
#endif
14861486

14871487
/**
1488-
* @brief The maximum number of clusters per scene, defaults to 3 for a typical usecase (onOff + level control + color control
1489-
* cluster). Needs to be changed in case a greater number of clusters is chosen.
1488+
* @brief The maximum number of clusters per scene, we recommend using 4 for a typical use case (onOff + level control + color
1489+
* control cluster + mode selec cluster). Needs to be changed in case a greater number of clusters is chosen. In the event the
1490+
* device does not need to support the mode select cluster, the maximum number of clusters per scene should be set to 3.
14901491
*/
14911492
#ifndef CHIP_CONFIG_SCENES_MAX_CLUSTERS_PER_SCENE
1492-
#define CHIP_CONFIG_SCENES_MAX_CLUSTERS_PER_SCENE 3
1493+
#define CHIP_CONFIG_SCENES_MAX_CLUSTERS_PER_SCENE 4
14931494
#endif
14941495

14951496
/**

0 commit comments

Comments
 (0)