3 * Copyright (c) 2020 Project CHIP Authors
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
9 * http://www.apache.org/licenses/LICENSE-2.0
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.
20 * Copyright (c) 2020 Silicon Labs
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
26 * http://www.apache.org/licenses/LICENSE-2.0
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.
34 /****************************************************************************
36 * @brief Routines for the Scenes plugin, which
37 *implements the server side of the Scenes cluster.
38 *******************************************************************************
39 ******************************************************************************/
42 #include "app/util/common.h"
43 #include <app/util/af.h>
45 #include "gen/attribute-id.h"
46 #include "gen/attribute-type.h"
47 #include "gen/cluster-id.h"
48 #include "gen/command-id.h"
50 #ifdef EMBER_AF_PLUGIN_GROUPS_SERVER
51 #include <app/clusters/groups-server/groups-server.h>
54 #ifdef EMBER_AF_PLUGIN_ZLL_SCENES_SERVER
55 #include "../zll-scenes-server/zll-scenes-server.h"
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];
65 static bool readServerAttribute(EndpointId endpoint, ClusterId clusterId, AttributeId attributeId, const char * name,
66 uint8_t * data, uint8_t size)
69 if (emberAfContainsServer(endpoint, clusterId))
71 EmberAfStatus status = emberAfReadServerAttribute(endpoint, clusterId, attributeId, data, size);
72 if (status == EMBER_ZCL_STATUS_SUCCESS)
78 emberAfScenesClusterPrintln("ERR: %ping %p 0x%x", "read", name, status);
84 static EmberAfStatus writeServerAttribute(EndpointId endpoint, ClusterId clusterId, AttributeId attributeId, const char * name,
85 uint8_t * data, EmberAfAttributeType type)
87 EmberAfStatus status = emberAfWriteServerAttribute(endpoint, clusterId, attributeId, data, type);
88 if (status != EMBER_ZCL_STATUS_SUCCESS)
90 emberAfScenesClusterPrintln("ERR: %ping %p 0x%x", "writ", name, status);
95 bool isEndpointInGroup(EndpointId endpoint, GroupId groupId)
97 #ifdef EMBER_AF_PLUGIN_GROUPS_SERVER
98 return (groupId == ZCL_SCENES_GLOBAL_SCENE_GROUP_ID || emberAfGroupsClusterEndpointInGroupCallback(endpoint, groupId));
100 return (groupId == ZCL_SCENES_GLOBAL_SCENE_GROUP_ID);
101 #endif // EMBER_AF_PLUGIN_GROUPS_SERVER
104 void emberAfScenesClusterServerInitCallback(EndpointId endpoint)
106 #ifdef EMBER_AF_PLUGIN_SCENES_NAME_SUPPORT
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);
114 #if !defined(EMBER_AF_PLUGIN_SCENES_USE_TOKENS) || defined(EZSP_HOST)
117 for (i = 0; i < EMBER_AF_PLUGIN_SCENES_TABLE_SIZE; i++)
119 EmberAfSceneTableEntry entry;
120 emberAfPluginScenesServerRetrieveSceneEntry(entry, i);
121 entry.endpoint = EMBER_AF_SCENE_TABLE_UNUSED_ENDPOINT_ID;
122 emberAfPluginScenesServerSaveSceneEntry(entry, i);
124 emberAfPluginScenesServerSetNumSceneEntriesInUse(0);
127 emberAfScenesSetSceneCountAttribute(endpoint, emberAfPluginScenesServerNumSceneEntriesInUse());
130 EmberAfStatus emberAfScenesSetSceneCountAttribute(EndpointId endpoint, uint8_t newCount)
132 return writeServerAttribute(endpoint, ZCL_SCENES_CLUSTER_ID, ZCL_SCENE_COUNT_ATTRIBUTE_ID, "scene count", (uint8_t *) &newCount,
133 ZCL_INT8U_ATTRIBUTE_TYPE);
136 EmberAfStatus emberAfScenesMakeValid(EndpointId endpoint, uint8_t sceneId, GroupId groupId)
138 EmberAfStatus status;
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)
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)
157 status = writeServerAttribute(endpoint, ZCL_SCENES_CLUSTER_ID, ZCL_SCENE_VALID_ATTRIBUTE_ID, "scene valid", (uint8_t *) &valid,
158 ZCL_BOOLEAN_ATTRIBUTE_TYPE);
162 EmberAfStatus emberAfScenesClusterMakeInvalidCallback(EndpointId endpoint)
165 return writeServerAttribute(endpoint, ZCL_SCENES_CLUSTER_ID, ZCL_SCENE_VALID_ATTRIBUTE_ID, "scene valid", (uint8_t *) &valid,
166 ZCL_BOOLEAN_ATTRIBUTE_TYPE);
169 void emAfPluginScenesServerPrintInfo(void)
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++)
177 emberAfPluginScenesServerRetrieveSceneEntry(entry, i);
178 emberAfCorePrint("%x: ", i);
179 if (entry.endpoint != EMBER_AF_SCENE_TABLE_UNUSED_ENDPOINT_ID)
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("\"");
188 #ifdef ZCL_USING_ON_OFF_CLUSTER_SERVER
189 emberAfCorePrint(" on/off %x", entry.onOffValue);
191 #ifdef ZCL_USING_LEVEL_CONTROL_CLUSTER_SERVER
192 emberAfCorePrint(" lvl %x", entry.currentLevelValue);
194 #ifdef ZCL_USING_THERMOSTAT_CLUSTER_SERVER
195 emberAfCorePrint(" therm %2x %2x %x", entry.occupiedCoolingSetpointValue, entry.occupiedHeatingSetpointValue,
196 entry.systemModeValue);
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);
204 #endif // ZCL_USING_COLOR_CONTROL_CLUSTER_SERVER
205 #ifdef ZCL_USING_DOOR_LOCK_CLUSTER_SERVER
206 emberAfCorePrint(" door %x", entry.lockStateValue);
208 #ifdef ZCL_USING_WINDOW_COVERING_CLUSTER_SERVER
209 emberAfCorePrint(" window %x %x", entry.currentPositionLiftPercentageValue, entry.currentPositionTiltPercentageValue);
212 emberAfCorePrintln("");
216 bool emberAfScenesClusterAddSceneCallback(GroupId groupId, uint8_t sceneId, uint16_t transitionTime, uint8_t * sceneName,
217 uint8_t * extensionFieldSets)
219 return emberAfPluginScenesServerParseAddScene(emberAfCurrentCommand(), groupId, sceneId, transitionTime, sceneName,
223 bool emberAfScenesClusterViewSceneCallback(GroupId groupId, uint8_t sceneId)
225 return emberAfPluginScenesServerParseViewScene(emberAfCurrentCommand(), groupId, sceneId);
228 bool emberAfScenesClusterRemoveSceneCallback(GroupId groupId, uint8_t sceneId)
230 EmberAfStatus status = EMBER_ZCL_STATUS_NOT_FOUND;
231 EmberStatus sendStatus;
233 emberAfScenesClusterPrintln("RX: RemoveScene 0x%2x, 0x%x", groupId, sceneId);
235 if (!isEndpointInGroup(emberAfCurrentEndpoint(), groupId))
237 status = EMBER_ZCL_STATUS_INVALID_FIELD;
242 for (i = 0; i < EMBER_AF_PLUGIN_SCENES_TABLE_SIZE; i++)
244 EmberAfSceneTableEntry entry;
245 emberAfPluginScenesServerRetrieveSceneEntry(entry, i);
246 if (entry.endpoint == emberAfCurrentEndpoint() && entry.groupId == groupId && entry.sceneId == sceneId)
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;
258 // Remove Scene commands are only responded to when they are addressed to a
260 if (emberAfCurrentCommand()->type == EMBER_INCOMING_UNICAST || emberAfCurrentCommand()->type == EMBER_INCOMING_UNICAST_REPLY)
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)
267 emberAfScenesClusterPrintln("Scenes: failed to send %s response: 0x%x", "remove_scene", sendStatus);
273 bool emberAfScenesClusterRemoveAllScenesCallback(GroupId groupId)
275 EmberAfStatus status = EMBER_ZCL_STATUS_INVALID_FIELD;
276 EmberStatus sendStatus;
278 emberAfScenesClusterPrintln("RX: RemoveAllScenes 0x%2x", groupId);
280 if (isEndpointInGroup(emberAfCurrentEndpoint(), groupId))
283 status = EMBER_ZCL_STATUS_SUCCESS;
284 for (i = 0; i < EMBER_AF_PLUGIN_SCENES_TABLE_SIZE; i++)
286 EmberAfSceneTableEntry entry;
287 emberAfPluginScenesServerRetrieveSceneEntry(entry, i);
288 if (entry.endpoint == emberAfCurrentEndpoint() && entry.groupId == groupId)
290 entry.endpoint = EMBER_AF_SCENE_TABLE_UNUSED_ENDPOINT_ID;
291 emberAfPluginScenesServerSaveSceneEntry(entry, i);
292 emberAfPluginScenesServerDecrNumSceneEntriesInUse();
295 emberAfScenesSetSceneCountAttribute(emberAfCurrentEndpoint(), emberAfPluginScenesServerNumSceneEntriesInUse());
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)
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);
305 sendStatus = emberAfSendResponse();
306 if (EMBER_SUCCESS != sendStatus)
308 emberAfScenesClusterPrintln("Scenes: failed to send %s response: 0x%x", "remove_all_scenes", sendStatus);
314 bool emberAfScenesClusterStoreSceneCallback(GroupId groupId, uint8_t sceneId)
316 EmberAfStatus status;
317 EmberStatus sendStatus;
318 emberAfScenesClusterPrintln("RX: StoreScene 0x%2x, 0x%x", groupId, sceneId);
319 status = emberAfScenesClusterStoreCurrentSceneCallback(emberAfCurrentEndpoint(), groupId, sceneId);
321 // Store Scene commands are only responded to when they are addressed to a
323 if (emberAfCurrentCommand()->type == EMBER_INCOMING_UNICAST || emberAfCurrentCommand()->type == EMBER_INCOMING_UNICAST_REPLY)
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)
330 emberAfScenesClusterPrintln("Scenes: failed to send %s response: 0x%x", "store_scene", sendStatus);
336 bool emberAfScenesClusterRecallSceneCallback(GroupId groupId, uint8_t sceneId, uint16_t transitionTime)
338 // NOTE: TransitionTime field in the RecallScene command is currently
339 // ignored. Per Zigbee Alliance ZCL 7 (07-5123-07):
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."
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.
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)
358 emberAfPluginZllScenesServerRecallSceneZllExtensions(emberAfCurrentEndpoint());
361 sendStatus = emberAfSendImmediateDefaultResponse(status);
362 if (EMBER_SUCCESS != sendStatus)
364 emberAfScenesClusterPrintln("Scenes: failed to send %s: 0x%x", "default_response", sendStatus);
369 bool emberAfScenesClusterGetSceneMembershipCallback(GroupId groupId)
371 EmberAfStatus status = EMBER_ZCL_STATUS_SUCCESS;
372 EmberStatus sendStatus;
373 uint8_t sceneCount = 0;
375 emberAfScenesClusterPrintln("RX: GetSceneMembership 0x%2x", groupId);
377 if (!isEndpointInGroup(emberAfCurrentEndpoint(), groupId))
379 status = EMBER_ZCL_STATUS_INVALID_FIELD;
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
389 if (status == EMBER_ZCL_STATUS_SUCCESS)
391 uint8_t i, sceneList[EMBER_AF_PLUGIN_SCENES_TABLE_SIZE];
392 for (i = 0; i < EMBER_AF_PLUGIN_SCENES_TABLE_SIZE; i++)
394 EmberAfSceneTableEntry entry;
395 emberAfPluginScenesServerRetrieveSceneEntry(entry, i);
396 if (entry.endpoint == emberAfCurrentEndpoint() && entry.groupId == groupId)
398 sceneList[sceneCount] = entry.sceneId;
402 emberAfPutInt8uInResp(sceneCount);
403 for (i = 0; i < sceneCount; i++)
405 emberAfPutInt8uInResp(sceneList[i]);
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 ||
414 sendStatus = emberAfSendResponse();
415 if (EMBER_SUCCESS != sendStatus)
417 emberAfScenesClusterPrintln("Scenes: failed to send %s response: 0x%x", "get_scene_membership", sendStatus);
423 EmberAfStatus emberAfScenesClusterStoreCurrentSceneCallback(EndpointId endpoint, GroupId groupId, uint8_t sceneId)
425 EmberAfSceneTableEntry entry;
426 uint8_t i, index = EMBER_AF_SCENE_TABLE_NULL_INDEX;
428 if (!isEndpointInGroup(endpoint, groupId))
430 return EMBER_ZCL_STATUS_INVALID_FIELD;
433 for (i = 0; i < EMBER_AF_PLUGIN_SCENES_TABLE_SIZE; i++)
435 emberAfPluginScenesServerRetrieveSceneEntry(entry, i);
436 if (entry.endpoint == endpoint && entry.groupId == groupId && entry.sceneId == sceneId)
441 else if (index == EMBER_AF_SCENE_TABLE_NULL_INDEX && entry.endpoint == EMBER_AF_SCENE_TABLE_UNUSED_ENDPOINT_ID)
447 // If the target index is still zero, the table is full.
448 if (index == EMBER_AF_SCENE_TABLE_NULL_INDEX)
450 return EMBER_ZCL_STATUS_INSUFFICIENT_SPACE;
453 emberAfPluginScenesServerRetrieveSceneEntry(entry, index);
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));
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));
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));
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));
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));
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.
519 entry.endpoint = endpoint;
520 entry.groupId = groupId;
521 entry.sceneId = sceneId;
522 #ifdef EMBER_AF_PLUGIN_SCENES_NAME_SUPPORT
525 entry.transitionTime = 0;
526 entry.transitionTime100ms = 0;
527 emberAfPluginScenesServerIncrNumSceneEntriesInUse();
528 emberAfScenesSetSceneCountAttribute(endpoint, emberAfPluginScenesServerNumSceneEntriesInUse());
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;
538 EmberAfStatus emberAfScenesClusterRecallSavedSceneCallback(EndpointId endpoint, GroupId groupId, uint8_t sceneId)
540 if (!isEndpointInGroup(endpoint, groupId))
542 return EMBER_ZCL_STATUS_INVALID_FIELD;
547 for (i = 0; i < EMBER_AF_PLUGIN_SCENES_TABLE_SIZE; i++)
549 EmberAfSceneTableEntry entry;
550 emberAfPluginScenesServerRetrieveSceneEntry(entry, i);
551 if (entry.endpoint == endpoint && entry.groupId == groupId && entry.sceneId == sceneId)
553 #ifdef ZCL_USING_ON_OFF_CLUSTER_SERVER
554 if (entry.hasOnOffValue)
556 writeServerAttribute(endpoint, ZCL_ON_OFF_CLUSTER_ID, ZCL_ON_OFF_ATTRIBUTE_ID, "on/off",
557 (uint8_t *) &entry.onOffValue, ZCL_BOOLEAN_ATTRIBUTE_TYPE);
560 #ifdef ZCL_USING_LEVEL_CONTROL_CLUSTER_SERVER
561 if (entry.hasCurrentLevelValue)
563 writeServerAttribute(endpoint, ZCL_LEVEL_CONTROL_CLUSTER_ID, ZCL_CURRENT_LEVEL_ATTRIBUTE_ID, "current level",
564 (uint8_t *) &entry.currentLevelValue, ZCL_INT8U_ATTRIBUTE_TYPE);
567 #ifdef ZCL_USING_THERMOSTAT_CLUSTER_SERVER
568 if (entry.hasOccupiedCoolingSetpointValue)
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);
574 if (entry.hasOccupiedHeatingSetpointValue)
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);
580 if (entry.hasSystemModeValue)
582 writeServerAttribute(endpoint, ZCL_THERMOSTAT_CLUSTER_ID, ZCL_SYSTEM_MODE_ATTRIBUTE_ID, "system mode",
583 (uint8_t *) &entry.systemModeValue, ZCL_INT8U_ATTRIBUTE_TYPE);
586 #ifdef ZCL_USING_COLOR_CONTROL_CLUSTER_SERVER
587 if (entry.hasCurrentXValue)
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);
592 if (entry.hasCurrentYValue)
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);
598 if (entry.hasEnhancedCurrentHueValue)
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);
604 if (entry.hasCurrentSaturationValue)
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);
609 if (entry.hasColorLoopActiveValue)
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);
614 if (entry.hasColorLoopDirectionValue)
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);
620 if (entry.hasColorLoopTimeValue)
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);
625 if (entry.hasColorTemperatureMiredsValue)
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);
631 #endif // ZCL_USING_COLOR_CONTROL_CLUSTER_SERVER
632 #ifdef ZCL_USING_DOOR_LOCK_CLUSTER_SERVER
633 if (entry.hasLockStateValue)
635 writeServerAttribute(endpoint, ZCL_DOOR_LOCK_CLUSTER_ID, ZCL_LOCK_STATE_ATTRIBUTE_ID, "lock state",
636 (uint8_t *) &entry.lockStateValue, ZCL_INT8U_ATTRIBUTE_TYPE);
639 #ifdef ZCL_USING_WINDOW_COVERING_CLUSTER_SERVER
640 if (entry.hasCurrentPositionLiftPercentageValue)
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);
646 if (entry.hasCurrentPositionTiltPercentageValue)
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);
653 emberAfScenesMakeValid(endpoint, sceneId, groupId);
654 return EMBER_ZCL_STATUS_SUCCESS;
659 return EMBER_ZCL_STATUS_NOT_FOUND;
662 bool emberAfPluginScenesServerParseAddScene(const EmberAfClusterCommand * cmd, GroupId groupId, uint8_t sceneId,
663 uint16_t transitionTime, uint8_t * sceneName, uint8_t * extensionFieldSets)
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>(
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;
676 emberAfScenesClusterPrint("RX: %pAddScene 0x%2x, 0x%x, 0x%2x, \"", (enhanced ? "Enhanced" : ""), groupId, sceneId,
678 emberAfScenesClusterPrintString(sceneName);
679 emberAfScenesClusterPrint("\", ");
680 emberAfScenesClusterPrintBuffer(extensionFieldSets, extensionFieldSetsLen, false);
681 emberAfScenesClusterPrintln("");
683 // Add Scene commands can only reference groups to which we belong.
684 if (!isEndpointInGroup(endpoint, groupId))
686 status = EMBER_ZCL_STATUS_INVALID_FIELD;
690 for (i = 0; i < EMBER_AF_PLUGIN_SCENES_TABLE_SIZE; i++)
692 emberAfPluginScenesServerRetrieveSceneEntry(entry, i);
693 if (entry.endpoint == endpoint && entry.groupId == groupId && entry.sceneId == sceneId)
698 else if (index == EMBER_AF_SCENE_TABLE_NULL_INDEX && entry.endpoint == EMBER_AF_SCENE_TABLE_UNUSED_ENDPOINT_ID)
704 // If the target index is still zero, the table is full.
705 if (index == EMBER_AF_SCENE_TABLE_NULL_INDEX)
707 status = EMBER_ZCL_STATUS_INSUFFICIENT_SPACE;
711 emberAfPluginScenesServerRetrieveSceneEntry(entry, index);
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.
717 entry.transitionTime = transitionTime / 10;
718 entry.transitionTime100ms = (uint8_t)(transitionTime - entry.transitionTime * 10);
722 entry.transitionTime = transitionTime;
723 entry.transitionTime100ms = 0;
726 #ifdef EMBER_AF_PLUGIN_SCENES_NAME_SUPPORT
727 emberAfCopyString(entry.name, sceneName, ZCL_SCENES_CLUSTER_MAXIMUM_NAME_LENGTH);
730 // When adding a new scene, wipe out all of the extensions before parsing the
731 // extension field sets data.
734 #ifdef ZCL_USING_ON_OFF_CLUSTER_SERVER
735 entry.hasOnOffValue = false;
737 #ifdef ZCL_USING_LEVEL_CONTROL_CLUSTER_SERVER
738 entry.hasCurrentLevelValue = false;
740 #ifdef ZCL_USING_THERMOSTAT_CLUSTER_SERVER
741 entry.hasOccupiedCoolingSetpointValue = false;
742 entry.hasOccupiedHeatingSetpointValue = false;
743 entry.hasSystemModeValue = false;
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;
758 #ifdef ZCL_USING_WINDOW_COVERING_CLUSTER_SERVER
759 entry.hasCurrentPositionLiftPercentageValue = false;
760 entry.hasCurrentPositionTiltPercentageValue = false;
764 while (extensionFieldSetsIndex < extensionFieldSetsLen)
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)
773 status = EMBER_ZCL_STATUS_MALFORMED_COMMAND;
777 clusterId = emberAfGetInt16u(extensionFieldSets, extensionFieldSetsIndex, extensionFieldSetsLen);
778 extensionFieldSetsIndex = static_cast<uint16_t>(extensionFieldSetsIndex + 2);
779 length = emberAfGetInt8u(extensionFieldSets, extensionFieldSetsIndex, extensionFieldSetsLen);
780 extensionFieldSetsIndex++;
782 // If the length is off, the command is also malformed.
787 else if (extensionFieldSetsLen < extensionFieldSetsIndex + length)
789 status = EMBER_ZCL_STATUS_MALFORMED_COMMAND;
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);
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
810 entry.hasCurrentLevelValue = true;
811 entry.currentLevelValue = emberAfGetInt8u(extensionFieldSets, extensionFieldSetsIndex, extensionFieldSetsLen);
814 #ifdef ZCL_USING_THERMOSTAT_CLUSTER_SERVER
815 case ZCL_THERMOSTAT_CLUSTER_ID:
820 entry.hasOccupiedCoolingSetpointValue = true;
821 entry.occupiedCoolingSetpointValue =
822 (int16_t) emberAfGetInt16u(extensionFieldSets, extensionFieldSetsIndex, extensionFieldSetsLen);
823 extensionFieldSetsIndex += 2;
829 entry.hasOccupiedHeatingSetpointValue = true;
830 entry.occupiedHeatingSetpointValue =
831 (int16_t) emberAfGetInt16u(extensionFieldSets, extensionFieldSetsIndex, extensionFieldSetsLen);
832 extensionFieldSetsIndex += 2;
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.
844 #ifdef ZCL_USING_COLOR_CONTROL_CLUSTER_SERVER
845 case ZCL_COLOR_CONTROL_CLUSTER_ID:
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);
858 entry.hasCurrentYValue = true;
859 entry.currentYValue = emberAfGetInt16u(extensionFieldSets, extensionFieldSetsIndex, extensionFieldSetsLen);
862 extensionFieldSetsIndex = static_cast<uint16_t>(extensionFieldSetsIndex + 2);
863 length = static_cast<uint8_t>(length - 2);
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);
878 entry.hasCurrentSaturationValue = true;
879 entry.currentSaturationValue = emberAfGetInt8u(extensionFieldSets, extensionFieldSetsIndex, extensionFieldSetsLen);
880 extensionFieldSetsIndex++;
886 entry.hasColorLoopActiveValue = true;
887 entry.colorLoopActiveValue = emberAfGetInt8u(extensionFieldSets, extensionFieldSetsIndex, extensionFieldSetsLen);
888 extensionFieldSetsIndex++;
894 entry.hasColorLoopDirectionValue = true;
895 entry.colorLoopDirectionValue = emberAfGetInt8u(extensionFieldSets, extensionFieldSetsIndex, extensionFieldSetsLen);
896 extensionFieldSetsIndex++;
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);
910 entry.hasColorTemperatureMiredsValue = true;
911 entry.colorTemperatureMiredsValue =
912 emberAfGetInt16u(extensionFieldSets, extensionFieldSetsIndex, extensionFieldSetsLen);
914 // If additional Color Control extensions are added, adjust the index and
915 // length variables here.
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
924 entry.hasLockStateValue = true;
925 entry.lockStateValue = emberAfGetInt8u(extensionFieldSets, extensionFieldSetsIndex, extensionFieldSetsLen);
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++;
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.
952 extensionFieldSetsIndex = static_cast<uint16_t>(extensionFieldSetsIndex + length);
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.
960 entry.endpoint = endpoint;
961 entry.groupId = groupId;
962 entry.sceneId = sceneId;
963 emberAfPluginScenesServerIncrNumSceneEntriesInUse();
964 emberAfScenesSetSceneCountAttribute(endpoint, emberAfPluginScenesServerNumSceneEntriesInUse());
966 emberAfPluginScenesServerSaveSceneEntry(entry, index);
967 status = EMBER_ZCL_STATUS_SUCCESS;
970 // Add Scene commands are only responded to when they are addressed to a
972 if (emberAfCurrentCommand()->type != EMBER_INCOMING_UNICAST && emberAfCurrentCommand()->type != EMBER_INCOMING_UNICAST_REPLY)
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)
982 emberAfScenesClusterPrintln("Scenes: failed to send %s response: 0x%x", "add_scene", sendStatus);
987 bool emberAfPluginScenesServerParseViewScene(const EmberAfClusterCommand * cmd, GroupId groupId, uint8_t sceneId)
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;
995 emberAfScenesClusterPrintln("RX: %pViewScene 0x%2x, 0x%x", (enhanced ? "Enhanced" : ""), groupId, sceneId);
997 // View Scene commands can only reference groups which we belong to.
998 if (!isEndpointInGroup(endpoint, groupId))
1000 status = EMBER_ZCL_STATUS_INVALID_FIELD;
1005 for (i = 0; i < EMBER_AF_PLUGIN_SCENES_TABLE_SIZE; i++)
1007 emberAfPluginScenesServerRetrieveSceneEntry(entry, i);
1008 if (entry.endpoint == endpoint && entry.groupId == groupId && entry.sceneId == sceneId)
1010 status = EMBER_ZCL_STATUS_SUCCESS;
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
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)
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);
1032 emberAfPutInt8uInResp(0); // name length
1034 #ifdef ZCL_USING_ON_OFF_CLUSTER_SERVER
1035 if (entry.hasOnOffValue)
1037 emberAfPutInt16uInResp(ZCL_ON_OFF_CLUSTER_ID);
1038 emberAfPutInt8uInResp(1); // length
1039 emberAfPutInt8uInResp(entry.onOffValue);
1042 #ifdef ZCL_USING_LEVEL_CONTROL_CLUSTER_SERVER
1043 if (entry.hasCurrentLevelValue)
1045 emberAfPutInt16uInResp(ZCL_LEVEL_CONTROL_CLUSTER_ID);
1046 emberAfPutInt8uInResp(1); // length
1047 emberAfPutInt8uInResp(entry.currentLevelValue);
1050 #ifdef ZCL_USING_THERMOSTAT_CLUSTER_SERVER
1051 if (entry.hasOccupiedCoolingSetpointValue)
1054 emberAfPutInt16uInResp(ZCL_THERMOSTAT_CLUSTER_ID);
1055 length = &appResponseData[appResponseLength];
1056 emberAfPutInt8uInResp(0); // temporary length
1057 emberAfPutInt16sInResp(entry.occupiedCoolingSetpointValue);
1059 if (entry.hasOccupiedHeatingSetpointValue)
1061 emberAfPutInt16sInResp(entry.occupiedHeatingSetpointValue);
1063 if (entry.hasSystemModeValue)
1065 emberAfPutInt8uInResp(entry.systemModeValue);
1071 #ifdef ZCL_USING_COLOR_CONTROL_CLUSTER_SERVER
1072 if (entry.hasCurrentXValue)
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)
1082 emberAfPutInt16uInResp(entry.currentYValue);
1083 *length = static_cast<uint8_t>(*length + 2);
1086 if (entry.hasEnhancedCurrentHueValue)
1088 emberAfPutInt16uInResp(entry.enhancedCurrentHueValue);
1089 *length = static_cast<uint8_t>(*length + 2);
1090 if (entry.hasCurrentSaturationValue)
1092 emberAfPutInt8uInResp(entry.currentSaturationValue);
1094 if (entry.hasColorLoopActiveValue)
1096 emberAfPutInt8uInResp(entry.colorLoopActiveValue);
1098 if (entry.hasColorLoopDirectionValue)
1100 emberAfPutInt8uInResp(entry.colorLoopDirectionValue);
1102 if (entry.hasColorLoopTimeValue)
1104 emberAfPutInt16uInResp(entry.colorLoopTimeValue);
1105 *length = static_cast<uint8_t>(*length + 2);
1106 if (entry.hasColorTemperatureMiredsValue)
1108 emberAfPutInt16uInResp(entry.colorTemperatureMiredsValue);
1109 *length = static_cast<uint8_t>(*length + 2);
1119 #endif // ZCL_USING_COLOR_CONTROL_CLUSTER_SERVER
1120 #ifdef ZCL_USING_DOOR_LOCK_CLUSTER_SERVER
1121 if (entry.hasLockStateValue)
1123 emberAfPutInt16uInResp(ZCL_DOOR_LOCK_CLUSTER_ID);
1124 emberAfPutInt8uInResp(1); // length
1125 emberAfPutInt8uInResp(entry.lockStateValue);
1128 #ifdef ZCL_USING_WINDOW_COVERING_CLUSTER_SERVER
1129 if (entry.hasCurrentPositionLiftPercentageValue)
1132 emberAfPutInt16uInResp(ZCL_WINDOW_COVERING_CLUSTER_ID);
1133 length = &appResponseData[appResponseLength];
1134 emberAfPutInt8uInResp(0); // temporary length
1135 emberAfPutInt8uInResp(entry.currentPositionLiftPercentageValue);
1137 if (entry.hasCurrentPositionTiltPercentageValue)
1139 emberAfPutInt8uInResp(entry.currentPositionTiltPercentageValue);
1146 // View Scene commands are only responded to when they are addressed to a
1148 if (emberAfCurrentCommand()->type != EMBER_INCOMING_UNICAST && emberAfCurrentCommand()->type != EMBER_INCOMING_UNICAST_REPLY)
1152 sendStatus = emberAfSendResponse();
1153 if (EMBER_SUCCESS != sendStatus)
1155 emberAfScenesClusterPrintln("Scenes: failed to send %s response: 0x%x", "view_scene", sendStatus);
1160 void emberAfScenesClusterRemoveScenesInGroupCallback(EndpointId endpoint, GroupId groupId)
1163 for (i = 0; i < EMBER_AF_PLUGIN_SCENES_TABLE_SIZE; i++)
1165 EmberAfSceneTableEntry entry;
1166 emberAfPluginScenesServerRetrieveSceneEntry(entry, i);
1167 if (entry.endpoint == endpoint && entry.groupId == groupId)
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());