@@ -41,15 +41,253 @@ using namespace chip::app::Clusters;
41
41
using namespace chip ::app::Clusters::OnOff;
42
42
using chip::Protocols::InteractionModel::Status;
43
43
44
+ #ifdef EMBER_AF_PLUGIN_LEVEL_CONTROL
45
+ static bool LevelControlWithOnOffFeaturePresent (EndpointId endpoint)
46
+ {
47
+ if (!emberAfContainsServer (endpoint, LevelControl::Id))
48
+ {
49
+ return false ;
50
+ }
51
+
52
+ return LevelControlHasFeature (endpoint, LevelControl::Feature::kOnOff );
53
+ }
54
+ #endif // EMBER_AF_PLUGIN_LEVEL_CONTROL
55
+
56
+ static constexpr size_t kOnOffMaxEnpointCount =
57
+ EMBER_AF_ON_OFF_CLUSTER_SERVER_ENDPOINT_COUNT + CHIP_DEVICE_CONFIG_DYNAMIC_ENDPOINT_COUNT;
58
+
59
+ #ifdef EMBER_AF_PLUGIN_SCENES
60
+ static EmberEventControl sceneHandlerEventControls[kOnOffMaxEnpointCount ];
61
+ static void sceneOnOffCallback (EndpointId endpoint);
62
+
63
+ class DefaultOnOffSceneHandler : public scenes ::DefaultSceneHandlerImpl
64
+ {
65
+ public:
66
+ // / @brief Struct to keep track of the desired state of the OnOff attribute between ApplyScene and
67
+ // / transition time expiration
68
+ struct EndpointStatePair
69
+ {
70
+ EndpointStatePair (EndpointId endpoint = kInvalidEndpointId , bool status = false ) : mEndpoint (endpoint), mState (status) {}
71
+ EndpointId mEndpoint ;
72
+ bool mState ;
73
+ };
74
+
75
+ // / @brief Struct holding an array of EndpointStatePair. Handles insertion, get and removal by EndpointID.
76
+ // / TODO: Implement generic object to handle this boilerplate array manipulation
77
+ struct StatePairBuffer
78
+ {
79
+ bool IsEmpty () const { return (mPairCount == 0 ); }
80
+
81
+ CHIP_ERROR FindPair (const EndpointId endpoint, uint16_t & found_index) const
82
+ {
83
+ VerifyOrReturnError (!IsEmpty (), CHIP_ERROR_NOT_FOUND);
84
+ for (found_index = 0 ; found_index < mPairCount ; found_index++)
85
+ {
86
+ if (endpoint == mStatePairBuffer [found_index].mEndpoint )
87
+ {
88
+ return CHIP_NO_ERROR;
89
+ }
90
+ }
91
+
92
+ return CHIP_ERROR_NOT_FOUND;
93
+ }
94
+
95
+ CHIP_ERROR InsertPair (const EndpointStatePair & status)
96
+ {
97
+ uint16_t idx;
98
+ CHIP_ERROR err = FindPair (status.mEndpoint , idx);
99
+
100
+ if (CHIP_NO_ERROR == err)
101
+ {
102
+ mStatePairBuffer [idx] = status;
103
+ }
104
+ else if (mPairCount < MAX_ENDPOINT_COUNT)
105
+ {
106
+ // if not found, insert at the end
107
+ mStatePairBuffer [mPairCount ] = status;
108
+ mPairCount ++;
109
+ }
110
+ else
111
+ {
112
+ return CHIP_ERROR_NO_MEMORY;
113
+ }
114
+
115
+ return CHIP_NO_ERROR;
116
+ }
117
+
118
+ CHIP_ERROR GetPair (const EndpointId endpoint, EndpointStatePair & status) const
119
+ {
120
+ uint16_t idx;
121
+ ReturnErrorOnFailure (FindPair (endpoint, idx));
122
+
123
+ status = mStatePairBuffer [idx];
124
+ return CHIP_NO_ERROR;
125
+ }
126
+
127
+ // / @brief Removes Pair and decrements Pair count if the endpoint existed in the array
128
+ // / @param endpoint : endpoint id of the pair
129
+ CHIP_ERROR RemovePair (const EndpointId endpoint)
130
+ {
131
+ uint16_t position;
132
+ VerifyOrReturnValue (CHIP_NO_ERROR == FindPair (endpoint, position), CHIP_NO_ERROR);
133
+
134
+ uint16_t nextPos = static_cast <uint16_t >(position + 1 );
135
+ uint16_t moveNum = static_cast <uint16_t >(mPairCount - nextPos);
136
+
137
+ // Compress array after removal, if the removed position is not the last
138
+ if (moveNum)
139
+ {
140
+ memmove (&mStatePairBuffer [position], &mStatePairBuffer [nextPos], sizeof (EndpointStatePair) * moveNum);
141
+ }
142
+
143
+ mPairCount --;
144
+ // Clear last occupied position
145
+ mStatePairBuffer [mPairCount ].mEndpoint = kInvalidEndpointId ;
146
+
147
+ return CHIP_NO_ERROR;
148
+ }
149
+
150
+ uint16_t mPairCount ;
151
+ EndpointStatePair mStatePairBuffer [kOnOffMaxEnpointCount ];
152
+ };
153
+
154
+ StatePairBuffer mSceneEndpointStatePairs ;
155
+ // As per spec, 1 attribute is scenable in the on off cluster
156
+ static constexpr uint8_t scenableAttributeCount = 1 ;
157
+
158
+ DefaultOnOffSceneHandler () = default ;
159
+ ~DefaultOnOffSceneHandler () override {}
160
+
161
+ // Default function for OnOff cluster, only puts the OnOff cluster ID in the span if supported on the given endpoint
162
+ virtual void GetSupportedClusters (EndpointId endpoint, Span<ClusterId> & clusterBuffer) override
163
+ {
164
+ ClusterId * buffer = clusterBuffer.data ();
165
+ if (emberAfContainsServer (endpoint, OnOff::Id) && clusterBuffer.size () >= 1 )
166
+ {
167
+ buffer[0 ] = OnOff::Id;
168
+ clusterBuffer.reduce_size (1 );
169
+ }
170
+ else
171
+ {
172
+ clusterBuffer.reduce_size (0 );
173
+ }
174
+ }
175
+
176
+ // Default function for OnOff cluster, only checks if OnOff is enabled on the endpoint
177
+ bool SupportsCluster (EndpointId endpoint, ClusterId cluster) override
178
+ {
179
+ return (cluster == OnOff::Id) && (emberAfContainsServer (endpoint, OnOff::Id));
180
+ }
181
+
182
+ // / @brief Serialize the Cluster's EFS value
183
+ // / @param endpoint target endpoint
184
+ // / @param cluster target cluster
185
+ // / @param serializedBytes data to serialize into EFS
186
+ // / @return CHIP_NO_ERROR if successfully serialized the data, CHIP_ERROR_INVALID_ARGUMENT otherwise
187
+ CHIP_ERROR SerializeSave (EndpointId endpoint, ClusterId cluster, MutableByteSpan & serializedBytes) override
188
+ {
189
+ using AttributeValuePair = Scenes::Structs::AttributeValuePair::Type;
190
+
191
+ bool currentValue;
192
+ // read current on/off value
193
+ EmberAfStatus status = Attributes::OnOff::Get (endpoint, ¤tValue);
194
+ if (status != EMBER_ZCL_STATUS_SUCCESS)
195
+ {
196
+ ChipLogError (Zcl, " ERR: reading on/off %x" , status);
197
+ return CHIP_ERROR_READ_FAILED;
198
+ }
199
+
200
+ AttributeValuePair pairs[scenableAttributeCount];
201
+
202
+ pairs[0 ].attributeID .SetValue (Attributes::OnOff::Id);
203
+ pairs[0 ].attributeValue = currentValue;
204
+
205
+ app::DataModel::List<AttributeValuePair> attributeValueList (pairs);
206
+
207
+ return EncodeAttributeValueList (attributeValueList, serializedBytes);
208
+ }
209
+
210
+ // / @brief Default EFS interaction when applying scene to the OnOff Cluster
211
+ // / @param endpoint target endpoint
212
+ // / @param cluster target cluster
213
+ // / @param serializedBytes Data from nvm
214
+ // / @param timeMs transition time in ms
215
+ // / @return CHIP_NO_ERROR if value as expected, CHIP_ERROR_INVALID_ARGUMENT otherwise
216
+ CHIP_ERROR ApplyScene (EndpointId endpoint, ClusterId cluster, const ByteSpan & serializedBytes,
217
+ scenes::TransitionTimeMs timeMs) override
218
+ {
219
+ app::DataModel::DecodableList<Scenes::Structs::AttributeValuePair::DecodableType> attributeValueList;
220
+
221
+ VerifyOrReturnError (cluster == OnOff::Id, CHIP_ERROR_INVALID_ARGUMENT);
222
+
223
+ ReturnErrorOnFailure (DecodeAttributeValueList (serializedBytes, attributeValueList));
224
+
225
+ size_t attributeCount = 0 ;
226
+ ReturnErrorOnFailure (attributeValueList.ComputeSize (&attributeCount));
227
+ VerifyOrReturnError (attributeCount <= scenableAttributeCount, CHIP_ERROR_BUFFER_TOO_SMALL);
228
+
229
+ auto pair_iterator = attributeValueList.begin ();
230
+ while (pair_iterator.Next ())
231
+ {
232
+ auto & decodePair = pair_iterator.GetValue ();
233
+ if (decodePair.attributeID .HasValue ())
234
+ {
235
+ // If attribute ID was encoded, verify it is the proper ID for the OnOff attribute
236
+ VerifyOrReturnError (decodePair.attributeID .Value () == Attributes::OnOff::Id, CHIP_ERROR_INVALID_ARGUMENT);
237
+ }
238
+ ReturnErrorOnFailure (
239
+ mSceneEndpointStatePairs .InsertPair (EndpointStatePair (endpoint, static_cast <bool >(decodePair.attributeValue ))));
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
+ OnOffServer::Instance ().scheduleTimerCallbackMs (sceneEventControl (endpoint), timeMs);
250
+
251
+ return CHIP_NO_ERROR;
252
+ }
253
+
254
+ private:
255
+ /* *
256
+ * @brief Configures EventControl callback when setting On Off through scenes callback
257
+ *
258
+ * @param[in] endpoint endpoint to start timer for
259
+ * @return EmberEventControl* configured event control
260
+ */
261
+ EmberEventControl * sceneEventControl (EndpointId endpoint)
262
+ {
263
+ EmberEventControl * controller =
264
+ OnOffServer::Instance ().getEventControl (endpoint, Span<EmberEventControl>(sceneHandlerEventControls));
265
+ VerifyOrReturnValue (controller != nullptr , nullptr );
266
+
267
+ controller->endpoint = endpoint;
268
+ controller->callback = &sceneOnOffCallback;
269
+
270
+ return controller;
271
+ }
272
+ };
273
+ static DefaultOnOffSceneHandler sOnOffSceneHandler ;
274
+
275
+ static void sceneOnOffCallback (EndpointId endpoint)
276
+ {
277
+ DefaultOnOffSceneHandler::EndpointStatePair savedState;
278
+ ReturnOnFailure (sOnOffSceneHandler .mSceneEndpointStatePairs .GetPair (endpoint, savedState));
279
+ chip::CommandId command = (savedState.mState ) ? Commands::On::Id : Commands::Off::Id;
280
+ OnOffServer::Instance ().setOnOffValue (endpoint, command, false );
281
+ ReturnOnFailure (sOnOffSceneHandler .mSceneEndpointStatePairs .RemovePair (endpoint));
282
+ }
283
+ #endif // EMBER_AF_PLUGIN_SCENES
284
+
44
285
/* *********************************************************
45
286
* Attributes Definition
46
287
*********************************************************/
47
288
48
289
static OnOffEffect * firstEffect = nullptr ;
49
290
OnOffServer OnOffServer::instance;
50
-
51
- static constexpr size_t kOnOffMaxEnpointCount =
52
- EMBER_AF_ON_OFF_CLUSTER_SERVER_ENDPOINT_COUNT + CHIP_DEVICE_CONFIG_DYNAMIC_ENDPOINT_COUNT;
53
291
static EmberEventControl gEventControls [kOnOffMaxEnpointCount ];
54
292
55
293
/* *********************************************************
@@ -85,7 +323,7 @@ void OnOffServer::cancelEndpointTimerCallback(EmberEventControl * control)
85
323
86
324
void OnOffServer::cancelEndpointTimerCallback (EndpointId endpoint)
87
325
{
88
- auto control = OnOffServer::getEventControl (endpoint);
326
+ auto control = OnOffServer::getEventControl (endpoint, Span<EmberEventControl>( gEventControls ) );
89
327
if (control)
90
328
{
91
329
cancelEndpointTimerCallback (control);
@@ -107,6 +345,16 @@ OnOffServer & OnOffServer::Instance()
107
345
return instance;
108
346
}
109
347
348
+ chip::scenes::SceneHandler * OnOffServer::GetSceneHandler ()
349
+ {
350
+
351
+ #ifdef EMBER_AF_PLUGIN_SCENES
352
+ return &sOnOffSceneHandler ;
353
+ #else
354
+ return nullptr ;
355
+ #endif // EMBER_AF_PLUGIN_SCENES
356
+ }
357
+
110
358
bool OnOffServer::HasFeature (chip::EndpointId endpoint, Feature feature)
111
359
{
112
360
bool success;
@@ -130,18 +378,6 @@ EmberAfStatus OnOffServer::getOnOffValue(chip::EndpointId endpoint, bool * curre
130
378
return status;
131
379
}
132
380
133
- #ifdef EMBER_AF_PLUGIN_LEVEL_CONTROL
134
- static bool LevelControlWithOnOffFeaturePresent (EndpointId endpoint)
135
- {
136
- if (!emberAfContainsServer (endpoint, LevelControl::Id))
137
- {
138
- return false ;
139
- }
140
-
141
- return LevelControlHasFeature (endpoint, LevelControl::Feature::kOnOff );
142
- }
143
- #endif // EMBER_AF_PLUGIN_LEVEL_CONTROL
144
-
145
381
/* * @brief On/off Cluster Set Value
146
382
*
147
383
* This function is called when the on/off value needs to be set, either through
@@ -194,7 +430,7 @@ EmberAfStatus OnOffServer::setOnOffValue(chip::EndpointId endpoint, chip::Comman
194
430
Attributes::OffWaitTime::Set (endpoint, 0 );
195
431
196
432
// Stop timer on the endpoint
197
- EmberEventControl * event = getEventControl (endpoint);
433
+ EmberEventControl * event = getEventControl (endpoint, Span<EmberEventControl>( gEventControls ) );
198
434
if (event != nullptr )
199
435
{
200
436
cancelEndpointTimerCallback (event);
@@ -307,6 +543,11 @@ void OnOffServer::initOnOffServer(chip::EndpointId endpoint)
307
543
status = setOnOffValue (endpoint, onOffValueForStartUp, true );
308
544
}
309
545
546
+ #ifdef EMBER_AF_PLUGIN_SCENES
547
+ // Registers Scene handlers for the On/Off cluster on the server
548
+ // app::Clusters::Scenes::ScenesServer::Instance().RegisterSceneHandler(OnOffServer::Instance().GetSceneHandler());
549
+ #endif
550
+
310
551
#ifdef EMBER_AF_PLUGIN_MODE_SELECT
311
552
// If OnMode is not a null value, then change the current mode to it.
312
553
if (onOffValueForStartUp && emberAfContainsServer (endpoint, ModeSelect::Id) &&
@@ -322,6 +563,7 @@ void OnOffServer::initOnOffServer(chip::EndpointId endpoint)
322
563
#endif
323
564
}
324
565
#endif // IGNORE_ON_OFF_CLUSTER_START_UP_ON_OFF
566
+
325
567
emberAfPluginOnOffClusterServerPostInitCallback (endpoint);
326
568
}
327
569
@@ -629,7 +871,7 @@ void OnOffServer::updateOnOffTimeCommand(chip::EndpointId endpoint)
629
871
ChipLogProgress (Zcl, " Timer Callback - wait Off Time cycle finished" );
630
872
631
873
// Stop timer on the endpoint
632
- cancelEndpointTimerCallback (getEventControl (endpoint));
874
+ cancelEndpointTimerCallback (getEventControl (endpoint, Span<EmberEventControl>( gEventControls ) ));
633
875
}
634
876
}
635
877
}
@@ -645,28 +887,31 @@ bool OnOffServer::areStartUpOnOffServerAttributesNonVolatile(EndpointId endpoint
645
887
/* *
646
888
* @brief event control object for an endpoint
647
889
*
648
- * @param[in] endpoint
890
+ * @param[in] endpoint target endpoint
891
+ * @param[in] eventControlArray Array where to find the event control
892
+ * @param[in] eventControlArraySize Size of the event control array
649
893
* @return EmberEventControl* configured event control
650
894
*/
651
- EmberEventControl * OnOffServer::getEventControl (EndpointId endpoint)
895
+ EmberEventControl * OnOffServer::getEventControl (EndpointId endpoint, const Span<EmberEventControl> & eventControlArray )
652
896
{
653
897
uint16_t index = emberAfGetClusterServerEndpointIndex (endpoint, OnOff::Id, EMBER_AF_ON_OFF_CLUSTER_SERVER_ENDPOINT_COUNT);
654
- if (index >= ArraySize ( gEventControls ))
898
+ if (index >= eventControlArray. size ( ))
655
899
{
656
900
return nullptr ;
657
901
}
658
- return &gEventControls [index ];
902
+
903
+ return &eventControlArray[index ];
659
904
}
660
905
661
906
/* *
662
- * @brief Configures EnventControl callback when using XY colors
907
+ * @brief Configures EventControl callback when using XY colors
663
908
*
664
909
* @param[in] endpoint endpoint to start timer for
665
910
* @return EmberEventControl* configured event control
666
911
*/
667
912
EmberEventControl * OnOffServer::configureEventControl (EndpointId endpoint)
668
913
{
669
- EmberEventControl * controller = getEventControl (endpoint);
914
+ EmberEventControl * controller = getEventControl (endpoint, Span<EmberEventControl>( gEventControls ) );
670
915
VerifyOrReturnError (controller != nullptr , nullptr );
671
916
672
917
controller->endpoint = endpoint;
0 commit comments