Fix for x86_64 build fail
[platform/upstream/connectedhomeip.git] / src / app / clusters / scenes / scenes.cpp
1 /**
2  *
3  *    Copyright (c) 2020 Project CHIP Authors
4  *
5  *    Licensed under the Apache License, Version 2.0 (the "License");
6  *    you may not use this file except in compliance with the License.
7  *    You may obtain a copy of the License at
8  *
9  *        http://www.apache.org/licenses/LICENSE-2.0
10  *
11  *    Unless required by applicable law or agreed to in writing, software
12  *    distributed under the License is distributed on an "AS IS" BASIS,
13  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  *    See the License for the specific language governing permissions and
15  *    limitations under the License.
16  */
17
18 /**
19  *
20  *    Copyright (c) 2020 Silicon Labs
21  *
22  *    Licensed under the Apache License, Version 2.0 (the "License");
23  *    you may not use this file except in compliance with the License.
24  *    You may obtain a copy of the License at
25  *
26  *        http://www.apache.org/licenses/LICENSE-2.0
27  *
28  *    Unless required by applicable law or agreed to in writing, software
29  *    distributed under the License is distributed on an "AS IS" BASIS,
30  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
31  *    See the License for the specific language governing permissions and
32  *    limitations under the License.
33  */
34 /****************************************************************************
35  * @file
36  * @brief Routines for the Scenes plugin, which
37  *implements the server side of the Scenes cluster.
38  *******************************************************************************
39  ******************************************************************************/
40
41 #include "scenes.h"
42 #include "app/util/common.h"
43 #include <app/util/af.h>
44
45 #include "gen/attribute-id.h"
46 #include "gen/attribute-type.h"
47 #include "gen/cluster-id.h"
48 #include "gen/command-id.h"
49
50 #ifdef EMBER_AF_PLUGIN_GROUPS_SERVER
51 #include <app/clusters/groups-server/groups-server.h>
52 #endif
53
54 #ifdef EMBER_AF_PLUGIN_ZLL_SCENES_SERVER
55 #include "../zll-scenes-server/zll-scenes-server.h"
56 #endif
57
58 using namespace chip;
59
60 uint8_t emberAfPluginScenesServerEntriesInUse = 0;
61 #if !defined(EMBER_AF_PLUGIN_SCENES_USE_TOKENS) || defined(EZSP_HOST)
62 EmberAfSceneTableEntry emberAfPluginScenesServerSceneTable[EMBER_AF_PLUGIN_SCENES_TABLE_SIZE];
63 #endif
64
65 static bool readServerAttribute(EndpointId endpoint, ClusterId clusterId, AttributeId attributeId, const char * name,
66                                 uint8_t * data, uint8_t size)
67 {
68     bool success = false;
69     if (emberAfContainsServer(endpoint, clusterId))
70     {
71         EmberAfStatus status = emberAfReadServerAttribute(endpoint, clusterId, attributeId, data, size);
72         if (status == EMBER_ZCL_STATUS_SUCCESS)
73         {
74             success = true;
75         }
76         else
77         {
78             emberAfScenesClusterPrintln("ERR: %ping %p 0x%x", "read", name, status);
79         }
80     }
81     return success;
82 }
83
84 static EmberAfStatus writeServerAttribute(EndpointId endpoint, ClusterId clusterId, AttributeId attributeId, const char * name,
85                                           uint8_t * data, EmberAfAttributeType type)
86 {
87     EmberAfStatus status = emberAfWriteServerAttribute(endpoint, clusterId, attributeId, data, type);
88     if (status != EMBER_ZCL_STATUS_SUCCESS)
89     {
90         emberAfScenesClusterPrintln("ERR: %ping %p 0x%x", "writ", name, status);
91     }
92     return status;
93 }
94
95 bool isEndpointInGroup(EndpointId endpoint, GroupId groupId)
96 {
97 #ifdef EMBER_AF_PLUGIN_GROUPS_SERVER
98     return (groupId == ZCL_SCENES_GLOBAL_SCENE_GROUP_ID || emberAfGroupsClusterEndpointInGroupCallback(endpoint, groupId));
99 #else
100     return (groupId == ZCL_SCENES_GLOBAL_SCENE_GROUP_ID);
101 #endif // EMBER_AF_PLUGIN_GROUPS_SERVER
102 }
103
104 void emberAfScenesClusterServerInitCallback(EndpointId endpoint)
105 {
106 #ifdef EMBER_AF_PLUGIN_SCENES_NAME_SUPPORT
107     {
108         // The high bit of Name Support indicates whether scene names are supported.
109         uint8_t nameSupport = EMBER_BIT(7);
110         writeServerAttribute(endpoint, ZCL_SCENES_CLUSTER_ID, ZCL_SCENE_NAME_SUPPORT_ATTRIBUTE_ID, "name support",
111                              (uint8_t *) &nameSupport, ZCL_BITMAP8_ATTRIBUTE_TYPE);
112     }
113 #endif
114 #if !defined(EMBER_AF_PLUGIN_SCENES_USE_TOKENS) || defined(EZSP_HOST)
115     {
116         uint8_t i;
117         for (i = 0; i < EMBER_AF_PLUGIN_SCENES_TABLE_SIZE; i++)
118         {
119             EmberAfSceneTableEntry entry;
120             emberAfPluginScenesServerRetrieveSceneEntry(entry, i);
121             entry.endpoint = EMBER_AF_SCENE_TABLE_UNUSED_ENDPOINT_ID;
122             emberAfPluginScenesServerSaveSceneEntry(entry, i);
123         }
124         emberAfPluginScenesServerSetNumSceneEntriesInUse(0);
125     }
126 #endif
127     emberAfScenesSetSceneCountAttribute(endpoint, emberAfPluginScenesServerNumSceneEntriesInUse());
128 }
129
130 EmberAfStatus emberAfScenesSetSceneCountAttribute(EndpointId endpoint, uint8_t newCount)
131 {
132     return writeServerAttribute(endpoint, ZCL_SCENES_CLUSTER_ID, ZCL_SCENE_COUNT_ATTRIBUTE_ID, "scene count", (uint8_t *) &newCount,
133                                 ZCL_INT8U_ATTRIBUTE_TYPE);
134 }
135
136 EmberAfStatus emberAfScenesMakeValid(EndpointId endpoint, uint8_t sceneId, GroupId groupId)
137 {
138     EmberAfStatus status;
139     bool valid = true;
140
141     // scene ID
142     status = writeServerAttribute(endpoint, ZCL_SCENES_CLUSTER_ID, ZCL_CURRENT_SCENE_ATTRIBUTE_ID, "current scene",
143                                   (uint8_t *) &sceneId, ZCL_INT8U_ATTRIBUTE_TYPE);
144     if (status != EMBER_ZCL_STATUS_SUCCESS)
145     {
146         return status;
147     }
148
149     // group ID
150     status = writeServerAttribute(endpoint, ZCL_SCENES_CLUSTER_ID, ZCL_CURRENT_GROUP_ATTRIBUTE_ID, "current group",
151                                   (uint8_t *) &groupId, ZCL_INT16U_ATTRIBUTE_TYPE);
152     if (status != EMBER_ZCL_STATUS_SUCCESS)
153     {
154         return status;
155     }
156
157     status = writeServerAttribute(endpoint, ZCL_SCENES_CLUSTER_ID, ZCL_SCENE_VALID_ATTRIBUTE_ID, "scene valid", (uint8_t *) &valid,
158                                   ZCL_BOOLEAN_ATTRIBUTE_TYPE);
159     return status;
160 }
161
162 EmberAfStatus emberAfScenesClusterMakeInvalidCallback(EndpointId endpoint)
163 {
164     bool valid = false;
165     return writeServerAttribute(endpoint, ZCL_SCENES_CLUSTER_ID, ZCL_SCENE_VALID_ATTRIBUTE_ID, "scene valid", (uint8_t *) &valid,
166                                 ZCL_BOOLEAN_ATTRIBUTE_TYPE);
167 }
168
169 void emAfPluginScenesServerPrintInfo(void)
170 {
171     uint8_t i;
172     EmberAfSceneTableEntry entry;
173     emberAfCorePrintln("using 0x%x out of 0x%x table slots", emberAfPluginScenesServerNumSceneEntriesInUse(),
174                        EMBER_AF_PLUGIN_SCENES_TABLE_SIZE);
175     for (i = 0; i < EMBER_AF_PLUGIN_SCENES_TABLE_SIZE; i++)
176     {
177         emberAfPluginScenesServerRetrieveSceneEntry(entry, i);
178         emberAfCorePrint("%x: ", i);
179         if (entry.endpoint != EMBER_AF_SCENE_TABLE_UNUSED_ENDPOINT_ID)
180         {
181             emberAfCorePrint("ep %x grp %2x scene %x tt %d", entry.endpoint, entry.groupId, entry.sceneId, entry.transitionTime);
182             emberAfCorePrint(".%d", entry.transitionTime100ms);
183 #ifdef EMBER_AF_PLUGIN_SCENES_NAME_SUPPORT
184             emberAfCorePrint(" name(%x)\"", emberAfStringLength(entry.name));
185             emberAfCorePrintString(entry.name);
186             emberAfCorePrint("\"");
187 #endif
188 #ifdef ZCL_USING_ON_OFF_CLUSTER_SERVER
189             emberAfCorePrint(" on/off %x", entry.onOffValue);
190 #endif
191 #ifdef ZCL_USING_LEVEL_CONTROL_CLUSTER_SERVER
192             emberAfCorePrint(" lvl %x", entry.currentLevelValue);
193 #endif
194 #ifdef ZCL_USING_THERMOSTAT_CLUSTER_SERVER
195             emberAfCorePrint(" therm %2x %2x %x", entry.occupiedCoolingSetpointValue, entry.occupiedHeatingSetpointValue,
196                              entry.systemModeValue);
197 #endif
198 #ifdef ZCL_USING_COLOR_CONTROL_CLUSTER_SERVER
199             emberAfCorePrint(" color %2x %2x", entry.currentXValue, entry.currentYValue);
200             emberAfCorePrint(" %2x %x %x %x %2x %2x", entry.enhancedCurrentHueValue, entry.currentSaturationValue,
201                              entry.colorLoopActiveValue, entry.colorLoopDirectionValue, entry.colorLoopTimeValue,
202                              entry.colorTemperatureMiredsValue);
203             emberAfCoreFlush();
204 #endif // ZCL_USING_COLOR_CONTROL_CLUSTER_SERVER
205 #ifdef ZCL_USING_DOOR_LOCK_CLUSTER_SERVER
206             emberAfCorePrint(" door %x", entry.lockStateValue);
207 #endif
208 #ifdef ZCL_USING_WINDOW_COVERING_CLUSTER_SERVER
209             emberAfCorePrint(" window %x %x", entry.currentPositionLiftPercentageValue, entry.currentPositionTiltPercentageValue);
210 #endif
211         }
212         emberAfCorePrintln("");
213     }
214 }
215
216 bool emberAfScenesClusterAddSceneCallback(GroupId groupId, uint8_t sceneId, uint16_t transitionTime, uint8_t * sceneName,
217                                           uint8_t * extensionFieldSets)
218 {
219     return emberAfPluginScenesServerParseAddScene(emberAfCurrentCommand(), groupId, sceneId, transitionTime, sceneName,
220                                                   extensionFieldSets);
221 }
222
223 bool emberAfScenesClusterViewSceneCallback(GroupId groupId, uint8_t sceneId)
224 {
225     return emberAfPluginScenesServerParseViewScene(emberAfCurrentCommand(), groupId, sceneId);
226 }
227
228 bool emberAfScenesClusterRemoveSceneCallback(GroupId groupId, uint8_t sceneId)
229 {
230     EmberAfStatus status = EMBER_ZCL_STATUS_NOT_FOUND;
231     EmberStatus sendStatus;
232
233     emberAfScenesClusterPrintln("RX: RemoveScene 0x%2x, 0x%x", groupId, sceneId);
234
235     if (!isEndpointInGroup(emberAfCurrentEndpoint(), groupId))
236     {
237         status = EMBER_ZCL_STATUS_INVALID_FIELD;
238     }
239     else
240     {
241         uint8_t i;
242         for (i = 0; i < EMBER_AF_PLUGIN_SCENES_TABLE_SIZE; i++)
243         {
244             EmberAfSceneTableEntry entry;
245             emberAfPluginScenesServerRetrieveSceneEntry(entry, i);
246             if (entry.endpoint == emberAfCurrentEndpoint() && entry.groupId == groupId && entry.sceneId == sceneId)
247             {
248                 entry.endpoint = EMBER_AF_SCENE_TABLE_UNUSED_ENDPOINT_ID;
249                 emberAfPluginScenesServerSaveSceneEntry(entry, i);
250                 emberAfPluginScenesServerDecrNumSceneEntriesInUse();
251                 emberAfScenesSetSceneCountAttribute(emberAfCurrentEndpoint(), emberAfPluginScenesServerNumSceneEntriesInUse());
252                 status = EMBER_ZCL_STATUS_SUCCESS;
253                 break;
254             }
255         }
256     }
257
258     // Remove Scene commands are only responded to when they are addressed to a
259     // single device.
260     if (emberAfCurrentCommand()->type == EMBER_INCOMING_UNICAST || emberAfCurrentCommand()->type == EMBER_INCOMING_UNICAST_REPLY)
261     {
262         emberAfFillExternalBuffer((ZCL_CLUSTER_SPECIFIC_COMMAND | ZCL_FRAME_CONTROL_SERVER_TO_CLIENT), ZCL_SCENES_CLUSTER_ID,
263                                   ZCL_REMOVE_SCENE_RESPONSE_COMMAND_ID, "uvu", status, groupId, sceneId);
264         sendStatus = emberAfSendResponse();
265         if (EMBER_SUCCESS != sendStatus)
266         {
267             emberAfScenesClusterPrintln("Scenes: failed to send %s response: 0x%x", "remove_scene", sendStatus);
268         }
269     }
270     return true;
271 }
272
273 bool emberAfScenesClusterRemoveAllScenesCallback(GroupId groupId)
274 {
275     EmberAfStatus status = EMBER_ZCL_STATUS_INVALID_FIELD;
276     EmberStatus sendStatus;
277
278     emberAfScenesClusterPrintln("RX: RemoveAllScenes 0x%2x", groupId);
279
280     if (isEndpointInGroup(emberAfCurrentEndpoint(), groupId))
281     {
282         uint8_t i;
283         status = EMBER_ZCL_STATUS_SUCCESS;
284         for (i = 0; i < EMBER_AF_PLUGIN_SCENES_TABLE_SIZE; i++)
285         {
286             EmberAfSceneTableEntry entry;
287             emberAfPluginScenesServerRetrieveSceneEntry(entry, i);
288             if (entry.endpoint == emberAfCurrentEndpoint() && entry.groupId == groupId)
289             {
290                 entry.endpoint = EMBER_AF_SCENE_TABLE_UNUSED_ENDPOINT_ID;
291                 emberAfPluginScenesServerSaveSceneEntry(entry, i);
292                 emberAfPluginScenesServerDecrNumSceneEntriesInUse();
293             }
294         }
295         emberAfScenesSetSceneCountAttribute(emberAfCurrentEndpoint(), emberAfPluginScenesServerNumSceneEntriesInUse());
296     }
297
298     // Remove All Scenes commands are only responded to when they are addressed
299     // to a single device.
300     if (emberAfCurrentCommand()->type == EMBER_INCOMING_UNICAST || emberAfCurrentCommand()->type == EMBER_INCOMING_UNICAST_REPLY)
301     {
302         emberAfFillExternalBuffer((ZCL_CLUSTER_SPECIFIC_COMMAND | ZCL_FRAME_CONTROL_SERVER_TO_CLIENT), ZCL_SCENES_CLUSTER_ID,
303                                   ZCL_REMOVE_ALL_SCENES_RESPONSE_COMMAND_ID, "uv", status, groupId);
304
305         sendStatus = emberAfSendResponse();
306         if (EMBER_SUCCESS != sendStatus)
307         {
308             emberAfScenesClusterPrintln("Scenes: failed to send %s response: 0x%x", "remove_all_scenes", sendStatus);
309         }
310     }
311     return true;
312 }
313
314 bool emberAfScenesClusterStoreSceneCallback(GroupId groupId, uint8_t sceneId)
315 {
316     EmberAfStatus status;
317     EmberStatus sendStatus;
318     emberAfScenesClusterPrintln("RX: StoreScene 0x%2x, 0x%x", groupId, sceneId);
319     status = emberAfScenesClusterStoreCurrentSceneCallback(emberAfCurrentEndpoint(), groupId, sceneId);
320
321     // Store Scene commands are only responded to when they are addressed to a
322     // single device.
323     if (emberAfCurrentCommand()->type == EMBER_INCOMING_UNICAST || emberAfCurrentCommand()->type == EMBER_INCOMING_UNICAST_REPLY)
324     {
325         emberAfFillExternalBuffer((ZCL_CLUSTER_SPECIFIC_COMMAND | ZCL_FRAME_CONTROL_SERVER_TO_CLIENT), ZCL_SCENES_CLUSTER_ID,
326                                   ZCL_STORE_SCENE_RESPONSE_COMMAND_ID, "uvu", status, groupId, sceneId);
327         sendStatus = emberAfSendResponse();
328         if (EMBER_SUCCESS != sendStatus)
329         {
330             emberAfScenesClusterPrintln("Scenes: failed to send %s response: 0x%x", "store_scene", sendStatus);
331         }
332     }
333     return true;
334 }
335
336 bool emberAfScenesClusterRecallSceneCallback(GroupId groupId, uint8_t sceneId, uint16_t transitionTime)
337 {
338     // NOTE: TransitionTime field in the RecallScene command is currently
339     // ignored. Per Zigbee Alliance ZCL 7 (07-5123-07):
340     //
341     // "The transition time determines how long the tranition takes from the
342     // old cluster state to the new cluster state. It is recommended that, where
343     // possible (e.g., it is not possible for attributes with Boolean type),
344     // a gradual transition SHOULD take place from the old to the new state
345     // over this time. However, the exact transition is manufacturer dependent."
346     //
347     // The manufacturer-dependent implementation here is to immediately set
348     // all attributes to their scene-specified values, without regard to the
349     // value of TransitionTime.
350
351     EmberAfStatus status;
352     EmberStatus sendStatus;
353     emberAfScenesClusterPrintln("RX: RecallScene 0x%2x, 0x%x", groupId, sceneId);
354     status = emberAfScenesClusterRecallSavedSceneCallback(emberAfCurrentEndpoint(), groupId, sceneId);
355 #ifdef EMBER_AF_PLUGIN_ZLL_SCENES_SERVER
356     if (status == EMBER_ZCL_STATUS_SUCCESS)
357     {
358         emberAfPluginZllScenesServerRecallSceneZllExtensions(emberAfCurrentEndpoint());
359     }
360 #endif
361     sendStatus = emberAfSendImmediateDefaultResponse(status);
362     if (EMBER_SUCCESS != sendStatus)
363     {
364         emberAfScenesClusterPrintln("Scenes: failed to send %s: 0x%x", "default_response", sendStatus);
365     }
366     return true;
367 }
368
369 bool emberAfScenesClusterGetSceneMembershipCallback(GroupId groupId)
370 {
371     EmberAfStatus status = EMBER_ZCL_STATUS_SUCCESS;
372     EmberStatus sendStatus;
373     uint8_t sceneCount = 0;
374
375     emberAfScenesClusterPrintln("RX: GetSceneMembership 0x%2x", groupId);
376
377     if (!isEndpointInGroup(emberAfCurrentEndpoint(), groupId))
378     {
379         status = EMBER_ZCL_STATUS_INVALID_FIELD;
380     }
381
382     // The status, capacity, and group id are always included in the response, but
383     // the scene count and scene list are only included if the group id matched.
384     emberAfFillExternalBuffer(
385         (ZCL_CLUSTER_SPECIFIC_COMMAND | ZCL_FRAME_CONTROL_SERVER_TO_CLIENT | EMBER_AF_DEFAULT_RESPONSE_POLICY_RESPONSES),
386         ZCL_SCENES_CLUSTER_ID, ZCL_GET_SCENE_MEMBERSHIP_RESPONSE_COMMAND_ID, "uuv", status,
387         (EMBER_AF_PLUGIN_SCENES_TABLE_SIZE - emberAfPluginScenesServerNumSceneEntriesInUse()), // capacity
388         groupId);
389     if (status == EMBER_ZCL_STATUS_SUCCESS)
390     {
391         uint8_t i, sceneList[EMBER_AF_PLUGIN_SCENES_TABLE_SIZE];
392         for (i = 0; i < EMBER_AF_PLUGIN_SCENES_TABLE_SIZE; i++)
393         {
394             EmberAfSceneTableEntry entry;
395             emberAfPluginScenesServerRetrieveSceneEntry(entry, i);
396             if (entry.endpoint == emberAfCurrentEndpoint() && entry.groupId == groupId)
397             {
398                 sceneList[sceneCount] = entry.sceneId;
399                 sceneCount++;
400             }
401         }
402         emberAfPutInt8uInResp(sceneCount);
403         for (i = 0; i < sceneCount; i++)
404         {
405             emberAfPutInt8uInResp(sceneList[i]);
406         }
407     }
408
409     // Get Scene Membership commands are only responded to when they are
410     // addressed to a single device or when an entry in the table matches.
411     if (emberAfCurrentCommand()->type == EMBER_INCOMING_UNICAST || emberAfCurrentCommand()->type == EMBER_INCOMING_UNICAST_REPLY ||
412         sceneCount != 0)
413     {
414         sendStatus = emberAfSendResponse();
415         if (EMBER_SUCCESS != sendStatus)
416         {
417             emberAfScenesClusterPrintln("Scenes: failed to send %s response: 0x%x", "get_scene_membership", sendStatus);
418         }
419     }
420     return true;
421 }
422
423 EmberAfStatus emberAfScenesClusterStoreCurrentSceneCallback(EndpointId endpoint, GroupId groupId, uint8_t sceneId)
424 {
425     EmberAfSceneTableEntry entry;
426     uint8_t i, index = EMBER_AF_SCENE_TABLE_NULL_INDEX;
427
428     if (!isEndpointInGroup(endpoint, groupId))
429     {
430         return EMBER_ZCL_STATUS_INVALID_FIELD;
431     }
432
433     for (i = 0; i < EMBER_AF_PLUGIN_SCENES_TABLE_SIZE; i++)
434     {
435         emberAfPluginScenesServerRetrieveSceneEntry(entry, i);
436         if (entry.endpoint == endpoint && entry.groupId == groupId && entry.sceneId == sceneId)
437         {
438             index = i;
439             break;
440         }
441         else if (index == EMBER_AF_SCENE_TABLE_NULL_INDEX && entry.endpoint == EMBER_AF_SCENE_TABLE_UNUSED_ENDPOINT_ID)
442         {
443             index = i;
444         }
445     }
446
447     // If the target index is still zero, the table is full.
448     if (index == EMBER_AF_SCENE_TABLE_NULL_INDEX)
449     {
450         return EMBER_ZCL_STATUS_INSUFFICIENT_SPACE;
451     }
452
453     emberAfPluginScenesServerRetrieveSceneEntry(entry, index);
454
455     // When creating a new entry or refreshing an existing one, the extension
456     // fields are updated with the current state of other clusters on the device.
457 #ifdef ZCL_USING_ON_OFF_CLUSTER_SERVER
458     entry.hasOnOffValue = readServerAttribute(endpoint, ZCL_ON_OFF_CLUSTER_ID, ZCL_ON_OFF_ATTRIBUTE_ID, "on/off",
459                                               (uint8_t *) &entry.onOffValue, sizeof(entry.onOffValue));
460 #endif
461 #ifdef ZCL_USING_LEVEL_CONTROL_CLUSTER_SERVER
462     entry.hasCurrentLevelValue =
463         readServerAttribute(endpoint, ZCL_LEVEL_CONTROL_CLUSTER_ID, ZCL_CURRENT_LEVEL_ATTRIBUTE_ID, "current level",
464                             (uint8_t *) &entry.currentLevelValue, sizeof(entry.currentLevelValue));
465 #endif
466 #ifdef ZCL_USING_THERMOSTAT_CLUSTER_SERVER
467     entry.hasOccupiedCoolingSetpointValue = readServerAttribute(
468         endpoint, ZCL_THERMOSTAT_CLUSTER_ID, ZCL_OCCUPIED_COOLING_SETPOINT_ATTRIBUTE_ID, "occupied cooling setpoint",
469         (uint8_t *) &entry.occupiedCoolingSetpointValue, sizeof(entry.occupiedCoolingSetpointValue));
470     entry.hasOccupiedHeatingSetpointValue = readServerAttribute(
471         endpoint, ZCL_THERMOSTAT_CLUSTER_ID, ZCL_OCCUPIED_HEATING_SETPOINT_ATTRIBUTE_ID, "occupied heating setpoint",
472         (uint8_t *) &entry.occupiedHeatingSetpointValue, sizeof(entry.occupiedHeatingSetpointValue));
473     entry.hasSystemModeValue = readServerAttribute(endpoint, ZCL_THERMOSTAT_CLUSTER_ID, ZCL_SYSTEM_MODE_ATTRIBUTE_ID, "system mode",
474                                                    (uint8_t *) &entry.systemModeValue, sizeof(entry.systemModeValue));
475 #endif
476 #ifdef ZCL_USING_COLOR_CONTROL_CLUSTER_SERVER
477     entry.hasCurrentXValue = readServerAttribute(endpoint, ZCL_COLOR_CONTROL_CLUSTER_ID, ZCL_COLOR_CONTROL_CURRENT_X_ATTRIBUTE_ID,
478                                                  "current x", (uint8_t *) &entry.currentXValue, sizeof(entry.currentXValue));
479     entry.hasCurrentYValue = readServerAttribute(endpoint, ZCL_COLOR_CONTROL_CLUSTER_ID, ZCL_COLOR_CONTROL_CURRENT_Y_ATTRIBUTE_ID,
480                                                  "current y", (uint8_t *) &entry.currentYValue, sizeof(entry.currentYValue));
481     entry.hasEnhancedCurrentHueValue = readServerAttribute(
482         endpoint, ZCL_COLOR_CONTROL_CLUSTER_ID, ZCL_COLOR_CONTROL_ENHANCED_CURRENT_HUE_ATTRIBUTE_ID, "enhanced current hue",
483         (uint8_t *) &entry.enhancedCurrentHueValue, sizeof(entry.enhancedCurrentHueValue));
484     entry.hasCurrentSaturationValue =
485         readServerAttribute(endpoint, ZCL_COLOR_CONTROL_CLUSTER_ID, ZCL_COLOR_CONTROL_CURRENT_SATURATION_ATTRIBUTE_ID,
486                             "current saturation", (uint8_t *) &entry.currentSaturationValue, sizeof(entry.currentSaturationValue));
487     entry.hasColorLoopActiveValue =
488         readServerAttribute(endpoint, ZCL_COLOR_CONTROL_CLUSTER_ID, ZCL_COLOR_CONTROL_COLOR_LOOP_ACTIVE_ATTRIBUTE_ID,
489                             "color loop active", (uint8_t *) &entry.colorLoopActiveValue, sizeof(entry.colorLoopActiveValue));
490     entry.hasColorLoopDirectionValue = readServerAttribute(
491         endpoint, ZCL_COLOR_CONTROL_CLUSTER_ID, ZCL_COLOR_CONTROL_COLOR_LOOP_DIRECTION_ATTRIBUTE_ID, "color loop direction",
492         (uint8_t *) &entry.colorLoopDirectionValue, sizeof(entry.colorLoopDirectionValue));
493     entry.hasColorLoopTimeValue =
494         readServerAttribute(endpoint, ZCL_COLOR_CONTROL_CLUSTER_ID, ZCL_COLOR_CONTROL_COLOR_LOOP_TIME_ATTRIBUTE_ID,
495                             "color loop time", (uint8_t *) &entry.colorLoopTimeValue, sizeof(entry.colorLoopTimeValue));
496     entry.hasColorTemperatureMiredsValue = readServerAttribute(
497         endpoint, ZCL_COLOR_CONTROL_CLUSTER_ID, ZCL_COLOR_CONTROL_COLOR_TEMPERATURE_ATTRIBUTE_ID, "color temp mireds",
498         (uint8_t *) &entry.colorTemperatureMiredsValue, sizeof(entry.colorTemperatureMiredsValue));
499 #endif // ZCL_USING_COLOR_CONTROL_CLUSTER_SERVER
500 #ifdef ZCL_USING_DOOR_LOCK_CLUSTER_SERVER
501     entry.hasLockStateValue = readServerAttribute(endpoint, ZCL_DOOR_LOCK_CLUSTER_ID, ZCL_LOCK_STATE_ATTRIBUTE_ID, "lock state",
502                                                   (uint8_t *) &entry.lockStateValue, sizeof(entry.lockStateValue));
503 #endif
504 #ifdef ZCL_USING_WINDOW_COVERING_CLUSTER_SERVER
505     entry.hasCurrentPositionLiftPercentageValue = readServerAttribute(
506         endpoint, ZCL_WINDOW_COVERING_CLUSTER_ID, ZCL_CURRENT_LIFT_PERCENTAGE_ATTRIBUTE_ID, "current position lift percentage",
507         (uint8_t *) &entry.currentPositionLiftPercentageValue, sizeof(entry.currentPositionLiftPercentageValue));
508     entry.hasCurrentPositionTiltPercentageValue = readServerAttribute(
509         endpoint, ZCL_WINDOW_COVERING_CLUSTER_ID, ZCL_CURRENT_TILT_PERCENTAGE_ATTRIBUTE_ID, "current position tilt percentage",
510         (uint8_t *) &entry.currentPositionTiltPercentageValue, sizeof(entry.currentPositionTiltPercentageValue));
511 #endif
512
513     // When creating a new entry, the name is set to the null string (i.e., the
514     // length is set to zero) and the transition time is set to zero.  The scene
515     // count must be increased and written to the attribute table when adding a
516     // new scene.  Otherwise, these fields and the count are left alone.
517     if (i != index)
518     {
519         entry.endpoint = endpoint;
520         entry.groupId  = groupId;
521         entry.sceneId  = sceneId;
522 #ifdef EMBER_AF_PLUGIN_SCENES_NAME_SUPPORT
523         entry.name[0] = 0;
524 #endif
525         entry.transitionTime      = 0;
526         entry.transitionTime100ms = 0;
527         emberAfPluginScenesServerIncrNumSceneEntriesInUse();
528         emberAfScenesSetSceneCountAttribute(endpoint, emberAfPluginScenesServerNumSceneEntriesInUse());
529     }
530
531     // Save the scene entry and mark is as valid by storing its scene and group
532     // ids in the attribute table and setting valid to true.
533     emberAfPluginScenesServerSaveSceneEntry(entry, index);
534     emberAfScenesMakeValid(endpoint, sceneId, groupId);
535     return EMBER_ZCL_STATUS_SUCCESS;
536 }
537
538 EmberAfStatus emberAfScenesClusterRecallSavedSceneCallback(EndpointId endpoint, GroupId groupId, uint8_t sceneId)
539 {
540     if (!isEndpointInGroup(endpoint, groupId))
541     {
542         return EMBER_ZCL_STATUS_INVALID_FIELD;
543     }
544     else
545     {
546         uint8_t i;
547         for (i = 0; i < EMBER_AF_PLUGIN_SCENES_TABLE_SIZE; i++)
548         {
549             EmberAfSceneTableEntry entry;
550             emberAfPluginScenesServerRetrieveSceneEntry(entry, i);
551             if (entry.endpoint == endpoint && entry.groupId == groupId && entry.sceneId == sceneId)
552             {
553 #ifdef ZCL_USING_ON_OFF_CLUSTER_SERVER
554                 if (entry.hasOnOffValue)
555                 {
556                     writeServerAttribute(endpoint, ZCL_ON_OFF_CLUSTER_ID, ZCL_ON_OFF_ATTRIBUTE_ID, "on/off",
557                                          (uint8_t *) &entry.onOffValue, ZCL_BOOLEAN_ATTRIBUTE_TYPE);
558                 }
559 #endif
560 #ifdef ZCL_USING_LEVEL_CONTROL_CLUSTER_SERVER
561                 if (entry.hasCurrentLevelValue)
562                 {
563                     writeServerAttribute(endpoint, ZCL_LEVEL_CONTROL_CLUSTER_ID, ZCL_CURRENT_LEVEL_ATTRIBUTE_ID, "current level",
564                                          (uint8_t *) &entry.currentLevelValue, ZCL_INT8U_ATTRIBUTE_TYPE);
565                 }
566 #endif
567 #ifdef ZCL_USING_THERMOSTAT_CLUSTER_SERVER
568                 if (entry.hasOccupiedCoolingSetpointValue)
569                 {
570                     writeServerAttribute(endpoint, ZCL_THERMOSTAT_CLUSTER_ID, ZCL_OCCUPIED_COOLING_SETPOINT_ATTRIBUTE_ID,
571                                          "occupied cooling setpoint", (uint8_t *) &entry.occupiedCoolingSetpointValue,
572                                          ZCL_INT16S_ATTRIBUTE_TYPE);
573                 }
574                 if (entry.hasOccupiedHeatingSetpointValue)
575                 {
576                     writeServerAttribute(endpoint, ZCL_THERMOSTAT_CLUSTER_ID, ZCL_OCCUPIED_HEATING_SETPOINT_ATTRIBUTE_ID,
577                                          "occupied heating setpoint", (uint8_t *) &entry.occupiedHeatingSetpointValue,
578                                          ZCL_INT16S_ATTRIBUTE_TYPE);
579                 }
580                 if (entry.hasSystemModeValue)
581                 {
582                     writeServerAttribute(endpoint, ZCL_THERMOSTAT_CLUSTER_ID, ZCL_SYSTEM_MODE_ATTRIBUTE_ID, "system mode",
583                                          (uint8_t *) &entry.systemModeValue, ZCL_INT8U_ATTRIBUTE_TYPE);
584                 }
585 #endif
586 #ifdef ZCL_USING_COLOR_CONTROL_CLUSTER_SERVER
587                 if (entry.hasCurrentXValue)
588                 {
589                     writeServerAttribute(endpoint, ZCL_COLOR_CONTROL_CLUSTER_ID, ZCL_COLOR_CONTROL_CURRENT_X_ATTRIBUTE_ID,
590                                          "current x", (uint8_t *) &entry.currentXValue, ZCL_INT16U_ATTRIBUTE_TYPE);
591                 }
592                 if (entry.hasCurrentYValue)
593                 {
594                     writeServerAttribute(endpoint, ZCL_COLOR_CONTROL_CLUSTER_ID, ZCL_COLOR_CONTROL_CURRENT_Y_ATTRIBUTE_ID,
595                                          "current y", (uint8_t *) &entry.currentYValue, ZCL_INT16U_ATTRIBUTE_TYPE);
596                 }
597
598                 if (entry.hasEnhancedCurrentHueValue)
599                 {
600                     writeServerAttribute(endpoint, ZCL_COLOR_CONTROL_CLUSTER_ID,
601                                          ZCL_COLOR_CONTROL_ENHANCED_CURRENT_HUE_ATTRIBUTE_ID, "enhanced current hue",
602                                          (uint8_t *) &entry.enhancedCurrentHueValue, ZCL_INT16U_ATTRIBUTE_TYPE);
603                 }
604                 if (entry.hasCurrentSaturationValue)
605                 {
606                     writeServerAttribute(endpoint, ZCL_COLOR_CONTROL_CLUSTER_ID, ZCL_COLOR_CONTROL_CURRENT_SATURATION_ATTRIBUTE_ID,
607                                          "current saturation", (uint8_t *) &entry.currentSaturationValue, ZCL_INT8U_ATTRIBUTE_TYPE);
608                 }
609                 if (entry.hasColorLoopActiveValue)
610                 {
611                     writeServerAttribute(endpoint, ZCL_COLOR_CONTROL_CLUSTER_ID, ZCL_COLOR_CONTROL_COLOR_LOOP_ACTIVE_ATTRIBUTE_ID,
612                                          "color loop active", (uint8_t *) &entry.colorLoopActiveValue, ZCL_INT8U_ATTRIBUTE_TYPE);
613                 }
614                 if (entry.hasColorLoopDirectionValue)
615                 {
616                     writeServerAttribute(endpoint, ZCL_COLOR_CONTROL_CLUSTER_ID,
617                                          ZCL_COLOR_CONTROL_COLOR_LOOP_DIRECTION_ATTRIBUTE_ID, "color loop direction",
618                                          (uint8_t *) &entry.colorLoopDirectionValue, ZCL_INT8U_ATTRIBUTE_TYPE);
619                 }
620                 if (entry.hasColorLoopTimeValue)
621                 {
622                     writeServerAttribute(endpoint, ZCL_COLOR_CONTROL_CLUSTER_ID, ZCL_COLOR_CONTROL_COLOR_LOOP_TIME_ATTRIBUTE_ID,
623                                          "color loop time", (uint8_t *) &entry.colorLoopTimeValue, ZCL_INT16U_ATTRIBUTE_TYPE);
624                 }
625                 if (entry.hasColorTemperatureMiredsValue)
626                 {
627                     writeServerAttribute(endpoint, ZCL_COLOR_CONTROL_CLUSTER_ID, ZCL_COLOR_CONTROL_COLOR_TEMPERATURE_ATTRIBUTE_ID,
628                                          "color temp mireds", (uint8_t *) &entry.colorTemperatureMiredsValue,
629                                          ZCL_INT16U_ATTRIBUTE_TYPE);
630                 }
631 #endif // ZCL_USING_COLOR_CONTROL_CLUSTER_SERVER
632 #ifdef ZCL_USING_DOOR_LOCK_CLUSTER_SERVER
633                 if (entry.hasLockStateValue)
634                 {
635                     writeServerAttribute(endpoint, ZCL_DOOR_LOCK_CLUSTER_ID, ZCL_LOCK_STATE_ATTRIBUTE_ID, "lock state",
636                                          (uint8_t *) &entry.lockStateValue, ZCL_INT8U_ATTRIBUTE_TYPE);
637                 }
638 #endif
639 #ifdef ZCL_USING_WINDOW_COVERING_CLUSTER_SERVER
640                 if (entry.hasCurrentPositionLiftPercentageValue)
641                 {
642                     writeServerAttribute(endpoint, ZCL_WINDOW_COVERING_CLUSTER_ID, ZCL_CURRENT_LIFT_PERCENTAGE_ATTRIBUTE_ID,
643                                          "current position lift percentage", (uint8_t *) &entry.currentPositionLiftPercentageValue,
644                                          ZCL_INT8U_ATTRIBUTE_TYPE);
645                 }
646                 if (entry.hasCurrentPositionTiltPercentageValue)
647                 {
648                     writeServerAttribute(endpoint, ZCL_WINDOW_COVERING_CLUSTER_ID, ZCL_CURRENT_TILT_PERCENTAGE_ATTRIBUTE_ID,
649                                          "current position tilt percentage", (uint8_t *) &entry.currentPositionTiltPercentageValue,
650                                          ZCL_INT8U_ATTRIBUTE_TYPE);
651                 }
652 #endif
653                 emberAfScenesMakeValid(endpoint, sceneId, groupId);
654                 return EMBER_ZCL_STATUS_SUCCESS;
655             }
656         }
657     }
658
659     return EMBER_ZCL_STATUS_NOT_FOUND;
660 }
661
662 bool emberAfPluginScenesServerParseAddScene(const EmberAfClusterCommand * cmd, GroupId groupId, uint8_t sceneId,
663                                             uint16_t transitionTime, uint8_t * sceneName, uint8_t * extensionFieldSets)
664 {
665     EmberAfSceneTableEntry entry;
666     EmberAfStatus status;
667     EmberStatus sendStatus;
668     bool enhanced                  = (cmd->commandId == ZCL_ENHANCED_ADD_SCENE_COMMAND_ID);
669     uint16_t extensionFieldSetsLen = static_cast<uint16_t>(
670         cmd->bufLen -
671         (cmd->payloadStartIndex + sizeof(groupId) + sizeof(sceneId) + sizeof(transitionTime) + emberAfStringLength(sceneName) + 1));
672     uint16_t extensionFieldSetsIndex = 0;
673     EndpointId endpoint              = cmd->apsFrame->destinationEndpoint;
674     uint8_t i, index = EMBER_AF_SCENE_TABLE_NULL_INDEX;
675
676     emberAfScenesClusterPrint("RX: %pAddScene 0x%2x, 0x%x, 0x%2x, \"", (enhanced ? "Enhanced" : ""), groupId, sceneId,
677                               transitionTime);
678     emberAfScenesClusterPrintString(sceneName);
679     emberAfScenesClusterPrint("\", ");
680     emberAfScenesClusterPrintBuffer(extensionFieldSets, extensionFieldSetsLen, false);
681     emberAfScenesClusterPrintln("");
682
683     // Add Scene commands can only reference groups to which we belong.
684     if (!isEndpointInGroup(endpoint, groupId))
685     {
686         status = EMBER_ZCL_STATUS_INVALID_FIELD;
687         goto kickout;
688     }
689
690     for (i = 0; i < EMBER_AF_PLUGIN_SCENES_TABLE_SIZE; i++)
691     {
692         emberAfPluginScenesServerRetrieveSceneEntry(entry, i);
693         if (entry.endpoint == endpoint && entry.groupId == groupId && entry.sceneId == sceneId)
694         {
695             index = i;
696             break;
697         }
698         else if (index == EMBER_AF_SCENE_TABLE_NULL_INDEX && entry.endpoint == EMBER_AF_SCENE_TABLE_UNUSED_ENDPOINT_ID)
699         {
700             index = i;
701         }
702     }
703
704     // If the target index is still zero, the table is full.
705     if (index == EMBER_AF_SCENE_TABLE_NULL_INDEX)
706     {
707         status = EMBER_ZCL_STATUS_INSUFFICIENT_SPACE;
708         goto kickout;
709     }
710
711     emberAfPluginScenesServerRetrieveSceneEntry(entry, index);
712
713     // The transition time is specified in seconds in the regular version of the
714     // command and tenths of a second in the enhanced version.
715     if (enhanced)
716     {
717         entry.transitionTime      = transitionTime / 10;
718         entry.transitionTime100ms = (uint8_t)(transitionTime - entry.transitionTime * 10);
719     }
720     else
721     {
722         entry.transitionTime      = transitionTime;
723         entry.transitionTime100ms = 0;
724     }
725
726 #ifdef EMBER_AF_PLUGIN_SCENES_NAME_SUPPORT
727     emberAfCopyString(entry.name, sceneName, ZCL_SCENES_CLUSTER_MAXIMUM_NAME_LENGTH);
728 #endif
729
730     // When adding a new scene, wipe out all of the extensions before parsing the
731     // extension field sets data.
732     if (i != index)
733     {
734 #ifdef ZCL_USING_ON_OFF_CLUSTER_SERVER
735         entry.hasOnOffValue = false;
736 #endif
737 #ifdef ZCL_USING_LEVEL_CONTROL_CLUSTER_SERVER
738         entry.hasCurrentLevelValue = false;
739 #endif
740 #ifdef ZCL_USING_THERMOSTAT_CLUSTER_SERVER
741         entry.hasOccupiedCoolingSetpointValue = false;
742         entry.hasOccupiedHeatingSetpointValue = false;
743         entry.hasSystemModeValue              = false;
744 #endif
745 #ifdef ZCL_USING_COLOR_CONTROL_CLUSTER_SERVER
746         entry.hasCurrentXValue               = false;
747         entry.hasCurrentYValue               = false;
748         entry.hasEnhancedCurrentHueValue     = false;
749         entry.hasCurrentSaturationValue      = false;
750         entry.hasColorLoopActiveValue        = false;
751         entry.hasColorLoopDirectionValue     = false;
752         entry.hasColorLoopTimeValue          = false;
753         entry.hasColorTemperatureMiredsValue = false;
754 #endif // ZCL_USING_COLOR_CONTROL_CLUSTER_SERVER
755 #ifdef ZCL_USING_DOOR_LOCK_CLUSTER_SERVER
756         entry.hasLockStateValue = false;
757 #endif
758 #ifdef ZCL_USING_WINDOW_COVERING_CLUSTER_SERVER
759         entry.hasCurrentPositionLiftPercentageValue = false;
760         entry.hasCurrentPositionTiltPercentageValue = false;
761 #endif
762     }
763
764     while (extensionFieldSetsIndex < extensionFieldSetsLen)
765     {
766         ClusterId clusterId;
767         uint8_t length;
768
769         // Each extension field set must contain a two-byte cluster id and a one-
770         // byte length.  Otherwise, the command is malformed.
771         if (extensionFieldSetsLen < extensionFieldSetsIndex + 3)
772         {
773             status = EMBER_ZCL_STATUS_MALFORMED_COMMAND;
774             goto kickout;
775         }
776
777         clusterId               = emberAfGetInt16u(extensionFieldSets, extensionFieldSetsIndex, extensionFieldSetsLen);
778         extensionFieldSetsIndex = static_cast<uint16_t>(extensionFieldSetsIndex + 2);
779         length                  = emberAfGetInt8u(extensionFieldSets, extensionFieldSetsIndex, extensionFieldSetsLen);
780         extensionFieldSetsIndex++;
781
782         // If the length is off, the command is also malformed.
783         if (length == 0)
784         {
785             continue;
786         }
787         else if (extensionFieldSetsLen < extensionFieldSetsIndex + length)
788         {
789             status = EMBER_ZCL_STATUS_MALFORMED_COMMAND;
790             goto kickout;
791         }
792
793         switch (clusterId)
794         {
795 #ifdef ZCL_USING_ON_OFF_CLUSTER_SERVER
796         case ZCL_ON_OFF_CLUSTER_ID:
797             // We only know of one extension for the On/Off cluster and it is just one
798             // byte, which means we can skip some logic for this cluster.  If other
799             // extensions are added in this cluster, more logic will be needed here.
800             entry.hasOnOffValue = true;
801             entry.onOffValue    = emberAfGetInt8u(extensionFieldSets, extensionFieldSetsIndex, extensionFieldSetsLen);
802             break;
803 #endif
804 #ifdef ZCL_USING_LEVEL_CONTROL_CLUSTER_SERVER
805         case ZCL_LEVEL_CONTROL_CLUSTER_ID:
806             // We only know of one extension for the Level Control cluster and it is
807             // just one byte, which means we can skip some logic for this cluster.  If
808             // other extensions are added in this cluster, more logic will be needed
809             // here.
810             entry.hasCurrentLevelValue = true;
811             entry.currentLevelValue    = emberAfGetInt8u(extensionFieldSets, extensionFieldSetsIndex, extensionFieldSetsLen);
812             break;
813 #endif
814 #ifdef ZCL_USING_THERMOSTAT_CLUSTER_SERVER
815         case ZCL_THERMOSTAT_CLUSTER_ID:
816             if (length < 2)
817             {
818                 break;
819             }
820             entry.hasOccupiedCoolingSetpointValue = true;
821             entry.occupiedCoolingSetpointValue =
822                 (int16_t) emberAfGetInt16u(extensionFieldSets, extensionFieldSetsIndex, extensionFieldSetsLen);
823             extensionFieldSetsIndex += 2;
824             length -= 2;
825             if (length < 2)
826             {
827                 break;
828             }
829             entry.hasOccupiedHeatingSetpointValue = true;
830             entry.occupiedHeatingSetpointValue =
831                 (int16_t) emberAfGetInt16u(extensionFieldSets, extensionFieldSetsIndex, extensionFieldSetsLen);
832             extensionFieldSetsIndex += 2;
833             length -= 2;
834             if (length < 1)
835             {
836                 break;
837             }
838             entry.hasSystemModeValue = true;
839             entry.systemModeValue    = emberAfGetInt8u(extensionFieldSets, extensionFieldSetsIndex, extensionFieldSetsLen);
840             // If additional Thermostat extensions are added, adjust the index and
841             // length variables here.
842             break;
843 #endif
844 #ifdef ZCL_USING_COLOR_CONTROL_CLUSTER_SERVER
845         case ZCL_COLOR_CONTROL_CLUSTER_ID:
846             if (length < 2)
847             {
848                 break;
849             }
850             entry.hasCurrentXValue  = true;
851             entry.currentXValue     = emberAfGetInt16u(extensionFieldSets, extensionFieldSetsIndex, extensionFieldSetsLen);
852             extensionFieldSetsIndex = static_cast<uint16_t>(extensionFieldSetsIndex + 2);
853             length                  = static_cast<uint8_t>(length - 2);
854             if (length < 2)
855             {
856                 break;
857             }
858             entry.hasCurrentYValue = true;
859             entry.currentYValue    = emberAfGetInt16u(extensionFieldSets, extensionFieldSetsIndex, extensionFieldSetsLen);
860             if (enhanced)
861             {
862                 extensionFieldSetsIndex = static_cast<uint16_t>(extensionFieldSetsIndex + 2);
863                 length                  = static_cast<uint8_t>(length - 2);
864                 ;
865                 if (length < 2)
866                 {
867                     break;
868                 }
869                 entry.hasEnhancedCurrentHueValue = true;
870                 entry.enhancedCurrentHueValue =
871                     emberAfGetInt16u(extensionFieldSets, extensionFieldSetsIndex, extensionFieldSetsLen);
872                 extensionFieldSetsIndex = static_cast<uint16_t>(extensionFieldSetsIndex + 2);
873                 length                  = static_cast<uint8_t>(length - 2);
874                 if (length < 1)
875                 {
876                     break;
877                 }
878                 entry.hasCurrentSaturationValue = true;
879                 entry.currentSaturationValue = emberAfGetInt8u(extensionFieldSets, extensionFieldSetsIndex, extensionFieldSetsLen);
880                 extensionFieldSetsIndex++;
881                 length--;
882                 if (length < 1)
883                 {
884                     break;
885                 }
886                 entry.hasColorLoopActiveValue = true;
887                 entry.colorLoopActiveValue    = emberAfGetInt8u(extensionFieldSets, extensionFieldSetsIndex, extensionFieldSetsLen);
888                 extensionFieldSetsIndex++;
889                 length--;
890                 if (length < 1)
891                 {
892                     break;
893                 }
894                 entry.hasColorLoopDirectionValue = true;
895                 entry.colorLoopDirectionValue = emberAfGetInt8u(extensionFieldSets, extensionFieldSetsIndex, extensionFieldSetsLen);
896                 extensionFieldSetsIndex++;
897                 length--;
898                 if (length < 2)
899                 {
900                     break;
901                 }
902                 entry.hasColorLoopTimeValue = true;
903                 entry.colorLoopTimeValue    = emberAfGetInt16u(extensionFieldSets, extensionFieldSetsIndex, extensionFieldSetsLen);
904                 extensionFieldSetsIndex     = static_cast<uint16_t>(extensionFieldSetsIndex + 2);
905                 length                      = static_cast<uint8_t>(length - 2);
906                 if (length < 2)
907                 {
908                     break;
909                 }
910                 entry.hasColorTemperatureMiredsValue = true;
911                 entry.colorTemperatureMiredsValue =
912                     emberAfGetInt16u(extensionFieldSets, extensionFieldSetsIndex, extensionFieldSetsLen);
913             }
914             // If additional Color Control extensions are added, adjust the index and
915             // length variables here.
916             break;
917 #endif // ZCL_USING_COLOR_CONTROL_CLUSTER_SERVER
918 #ifdef ZCL_USING_DOOR_LOCK_CLUSTER_SERVER
919         case ZCL_DOOR_LOCK_CLUSTER_ID:
920             // We only know of one extension for the Door Lock cluster and it is just
921             // one byte, which means we can skip some logic for this cluster.  If
922             // other extensions are added in this cluster, more logic will be needed
923             // here.
924             entry.hasLockStateValue = true;
925             entry.lockStateValue    = emberAfGetInt8u(extensionFieldSets, extensionFieldSetsIndex, extensionFieldSetsLen);
926             break;
927 #endif
928 #ifdef ZCL_USING_WINDOW_COVERING_CLUSTER_SERVER
929         case ZCL_WINDOW_COVERING_CLUSTER_ID:
930             // If we're here, we know we have at least one byte, so we can skip the
931             // length check for the first field.
932             entry.hasCurrentPositionLiftPercentageValue = true;
933             entry.currentPositionLiftPercentageValue =
934                 emberAfGetInt8u(extensionFieldSets, extensionFieldSetsIndex, extensionFieldSetsLen);
935             extensionFieldSetsIndex++;
936             length--;
937             if (length < 1)
938             {
939                 break;
940             }
941             entry.hasCurrentPositionTiltPercentageValue = true;
942             entry.currentPositionTiltPercentageValue =
943                 emberAfGetInt8u(extensionFieldSets, extensionFieldSetsIndex, extensionFieldSetsLen);
944             // If additional Window Covering extensions are added, adjust the index
945             // and length variables here.
946             break;
947 #endif
948         default:
949             break;
950         }
951
952         extensionFieldSetsIndex = static_cast<uint16_t>(extensionFieldSetsIndex + length);
953     }
954
955     // If we got this far, we either added a new entry or updated an existing one.
956     // If we added, store the basic data and increment the scene count.  In either
957     // case, save the entry.
958     if (i != index)
959     {
960         entry.endpoint = endpoint;
961         entry.groupId  = groupId;
962         entry.sceneId  = sceneId;
963         emberAfPluginScenesServerIncrNumSceneEntriesInUse();
964         emberAfScenesSetSceneCountAttribute(endpoint, emberAfPluginScenesServerNumSceneEntriesInUse());
965     }
966     emberAfPluginScenesServerSaveSceneEntry(entry, index);
967     status = EMBER_ZCL_STATUS_SUCCESS;
968
969 kickout:
970     // Add Scene commands are only responded to when they are addressed to a
971     // single device.
972     if (emberAfCurrentCommand()->type != EMBER_INCOMING_UNICAST && emberAfCurrentCommand()->type != EMBER_INCOMING_UNICAST_REPLY)
973     {
974         return true;
975     }
976     emberAfFillExternalBuffer((ZCL_CLUSTER_SPECIFIC_COMMAND | ZCL_FRAME_CONTROL_SERVER_TO_CLIENT), ZCL_SCENES_CLUSTER_ID,
977                               (enhanced ? ZCL_ENHANCED_ADD_SCENE_RESPONSE_COMMAND_ID : ZCL_ADD_SCENE_RESPONSE_COMMAND_ID), "uvu",
978                               status, groupId, sceneId);
979     sendStatus = emberAfSendResponse();
980     if (EMBER_SUCCESS != sendStatus)
981     {
982         emberAfScenesClusterPrintln("Scenes: failed to send %s response: 0x%x", "add_scene", sendStatus);
983     }
984     return true;
985 }
986
987 bool emberAfPluginScenesServerParseViewScene(const EmberAfClusterCommand * cmd, GroupId groupId, uint8_t sceneId)
988 {
989     EmberAfSceneTableEntry entry = {};
990     EmberAfStatus status         = EMBER_ZCL_STATUS_NOT_FOUND;
991     EmberStatus sendStatus;
992     bool enhanced       = (cmd->commandId == ZCL_ENHANCED_VIEW_SCENE_COMMAND_ID);
993     EndpointId endpoint = cmd->apsFrame->destinationEndpoint;
994
995     emberAfScenesClusterPrintln("RX: %pViewScene 0x%2x, 0x%x", (enhanced ? "Enhanced" : ""), groupId, sceneId);
996
997     // View Scene commands can only reference groups which we belong to.
998     if (!isEndpointInGroup(endpoint, groupId))
999     {
1000         status = EMBER_ZCL_STATUS_INVALID_FIELD;
1001     }
1002     else
1003     {
1004         uint8_t i;
1005         for (i = 0; i < EMBER_AF_PLUGIN_SCENES_TABLE_SIZE; i++)
1006         {
1007             emberAfPluginScenesServerRetrieveSceneEntry(entry, i);
1008             if (entry.endpoint == endpoint && entry.groupId == groupId && entry.sceneId == sceneId)
1009             {
1010                 status = EMBER_ZCL_STATUS_SUCCESS;
1011                 break;
1012             }
1013         }
1014     }
1015
1016     // The status, group id, and scene id are always included in the response, but
1017     // the transition time, name, and extension fields are only included if the
1018     // scene was found.
1019     emberAfFillExternalBuffer(
1020         (ZCL_CLUSTER_SPECIFIC_COMMAND | ZCL_FRAME_CONTROL_SERVER_TO_CLIENT | EMBER_AF_DEFAULT_RESPONSE_POLICY_RESPONSES),
1021         ZCL_SCENES_CLUSTER_ID, (enhanced ? ZCL_ENHANCED_VIEW_SCENE_RESPONSE_COMMAND_ID : ZCL_VIEW_SCENE_RESPONSE_COMMAND_ID), "uvu",
1022         status, groupId, sceneId);
1023     if (status == EMBER_ZCL_STATUS_SUCCESS)
1024     {
1025         // The transition time is returned in seconds in the regular version of the
1026         // command and tenths of a second in the enhanced version.
1027         emberAfPutInt16uInResp(
1028             static_cast<uint16_t>(enhanced ? entry.transitionTime * 10 + entry.transitionTime100ms : entry.transitionTime));
1029 #ifdef EMBER_AF_PLUGIN_SCENES_NAME_SUPPORT
1030         emberAfPutStringInResp(entry.name);
1031 #else
1032         emberAfPutInt8uInResp(0); // name length
1033 #endif
1034 #ifdef ZCL_USING_ON_OFF_CLUSTER_SERVER
1035         if (entry.hasOnOffValue)
1036         {
1037             emberAfPutInt16uInResp(ZCL_ON_OFF_CLUSTER_ID);
1038             emberAfPutInt8uInResp(1); // length
1039             emberAfPutInt8uInResp(entry.onOffValue);
1040         }
1041 #endif
1042 #ifdef ZCL_USING_LEVEL_CONTROL_CLUSTER_SERVER
1043         if (entry.hasCurrentLevelValue)
1044         {
1045             emberAfPutInt16uInResp(ZCL_LEVEL_CONTROL_CLUSTER_ID);
1046             emberAfPutInt8uInResp(1); // length
1047             emberAfPutInt8uInResp(entry.currentLevelValue);
1048         }
1049 #endif
1050 #ifdef ZCL_USING_THERMOSTAT_CLUSTER_SERVER
1051         if (entry.hasOccupiedCoolingSetpointValue)
1052         {
1053             uint8_t * length;
1054             emberAfPutInt16uInResp(ZCL_THERMOSTAT_CLUSTER_ID);
1055             length = &appResponseData[appResponseLength];
1056             emberAfPutInt8uInResp(0); // temporary length
1057             emberAfPutInt16sInResp(entry.occupiedCoolingSetpointValue);
1058             *length += 2;
1059             if (entry.hasOccupiedHeatingSetpointValue)
1060             {
1061                 emberAfPutInt16sInResp(entry.occupiedHeatingSetpointValue);
1062                 *length += 2;
1063                 if (entry.hasSystemModeValue)
1064                 {
1065                     emberAfPutInt8uInResp(entry.systemModeValue);
1066                     (*length)++;
1067                 }
1068             }
1069         }
1070 #endif
1071 #ifdef ZCL_USING_COLOR_CONTROL_CLUSTER_SERVER
1072         if (entry.hasCurrentXValue)
1073         {
1074             uint8_t * length;
1075             emberAfPutInt16uInResp(ZCL_COLOR_CONTROL_CLUSTER_ID);
1076             length = &appResponseData[appResponseLength];
1077             emberAfPutInt8uInResp(0); // temporary length
1078             emberAfPutInt16uInResp(entry.currentXValue);
1079             *length = static_cast<uint8_t>(*length + 2);
1080             if (entry.hasCurrentYValue)
1081             {
1082                 emberAfPutInt16uInResp(entry.currentYValue);
1083                 *length = static_cast<uint8_t>(*length + 2);
1084                 if (enhanced)
1085                 {
1086                     if (entry.hasEnhancedCurrentHueValue)
1087                     {
1088                         emberAfPutInt16uInResp(entry.enhancedCurrentHueValue);
1089                         *length = static_cast<uint8_t>(*length + 2);
1090                         if (entry.hasCurrentSaturationValue)
1091                         {
1092                             emberAfPutInt8uInResp(entry.currentSaturationValue);
1093                             (*length)++;
1094                             if (entry.hasColorLoopActiveValue)
1095                             {
1096                                 emberAfPutInt8uInResp(entry.colorLoopActiveValue);
1097                                 (*length)++;
1098                                 if (entry.hasColorLoopDirectionValue)
1099                                 {
1100                                     emberAfPutInt8uInResp(entry.colorLoopDirectionValue);
1101                                     (*length)++;
1102                                     if (entry.hasColorLoopTimeValue)
1103                                     {
1104                                         emberAfPutInt16uInResp(entry.colorLoopTimeValue);
1105                                         *length = static_cast<uint8_t>(*length + 2);
1106                                         if (entry.hasColorTemperatureMiredsValue)
1107                                         {
1108                                             emberAfPutInt16uInResp(entry.colorTemperatureMiredsValue);
1109                                             *length = static_cast<uint8_t>(*length + 2);
1110                                         }
1111                                     }
1112                                 }
1113                             }
1114                         }
1115                     }
1116                 }
1117             }
1118         }
1119 #endif // ZCL_USING_COLOR_CONTROL_CLUSTER_SERVER
1120 #ifdef ZCL_USING_DOOR_LOCK_CLUSTER_SERVER
1121         if (entry.hasLockStateValue)
1122         {
1123             emberAfPutInt16uInResp(ZCL_DOOR_LOCK_CLUSTER_ID);
1124             emberAfPutInt8uInResp(1); // length
1125             emberAfPutInt8uInResp(entry.lockStateValue);
1126         }
1127 #endif
1128 #ifdef ZCL_USING_WINDOW_COVERING_CLUSTER_SERVER
1129         if (entry.hasCurrentPositionLiftPercentageValue)
1130         {
1131             uint8_t * length;
1132             emberAfPutInt16uInResp(ZCL_WINDOW_COVERING_CLUSTER_ID);
1133             length = &appResponseData[appResponseLength];
1134             emberAfPutInt8uInResp(0); // temporary length
1135             emberAfPutInt8uInResp(entry.currentPositionLiftPercentageValue);
1136             (*length)++;
1137             if (entry.hasCurrentPositionTiltPercentageValue)
1138             {
1139                 emberAfPutInt8uInResp(entry.currentPositionTiltPercentageValue);
1140                 (*length)++;
1141             }
1142         }
1143 #endif
1144     }
1145
1146     // View Scene commands are only responded to when they are addressed to a
1147     // single device.
1148     if (emberAfCurrentCommand()->type != EMBER_INCOMING_UNICAST && emberAfCurrentCommand()->type != EMBER_INCOMING_UNICAST_REPLY)
1149     {
1150         return true;
1151     }
1152     sendStatus = emberAfSendResponse();
1153     if (EMBER_SUCCESS != sendStatus)
1154     {
1155         emberAfScenesClusterPrintln("Scenes: failed to send %s response: 0x%x", "view_scene", sendStatus);
1156     }
1157     return true;
1158 }
1159
1160 void emberAfScenesClusterRemoveScenesInGroupCallback(EndpointId endpoint, GroupId groupId)
1161 {
1162     uint8_t i;
1163     for (i = 0; i < EMBER_AF_PLUGIN_SCENES_TABLE_SIZE; i++)
1164     {
1165         EmberAfSceneTableEntry entry;
1166         emberAfPluginScenesServerRetrieveSceneEntry(entry, i);
1167         if (entry.endpoint == endpoint && entry.groupId == groupId)
1168         {
1169             entry.groupId  = ZCL_SCENES_GLOBAL_SCENE_GROUP_ID;
1170             entry.endpoint = EMBER_AF_SCENE_TABLE_UNUSED_ENDPOINT_ID;
1171             emberAfPluginScenesServerSaveSceneEntry(entry, i);
1172             emberAfPluginScenesServerDecrNumSceneEntriesInUse();
1173             emberAfScenesSetSceneCountAttribute(emberAfCurrentEndpoint(), emberAfPluginScenesServerNumSceneEntriesInUse());
1174         }
1175     }
1176 }