/** @brief Identify Cluster Identify Query Response * * * * @param timeout Ver.: always */ boolean emberAfIdentifyClusterIdentifyQueryResponseCallback(int16u timeout) { // TODO: !!! IMPORTANT !!! add handling for several responses // now the state machine might be broken as we use only one global variable // for storing incoming connection information like Short ID and endpoint // // ignore broadcasts from yourself and from devices that are not // in the identifying state const EmberAfClusterCommand *const current_cmd = emberAfCurrentCommand(); if (emberAfGetNodeId() != current_cmd->source && timeout != 0) { emberAfDebugPrintln("DEBUG: Got ID Query response"); emberAfDebugPrintln("DEBUG: Sender 0x%2X", emberAfCurrentCommand()->source); // Queue is empty, so we can start commissioning process // otherwise we push it in the queue and will process later if (GetQueueSize() == 0) { // ID Query received -> go to the discover state for getting clusters info emberAfDebugPrintln("DEBUG: QUEUE IS EMPTY"); SetNextState(SC_EZ_DISCOVER); SetNextEvent(SC_EZEV_CHECK_CLUSTERS); emberEventControlSetActive(StateMachineEvent); } // Store information about endpoint and short ID of the incoming response // for further processing in the Matching (SC_EZ_MATCH) state SetInConnBaseInfo(current_cmd->source, current_cmd->apsFrame->sourceEndpoint); emberAfSendImmediateDefaultResponse(EMBER_ZCL_STATUS_SUCCESS); } return TRUE; }
bool emberAfEventsClusterPublishEventLogCallback(uint16_t totalNumberOfEvents, uint8_t commandIndex, uint8_t totalCommands, uint8_t logPayloadControl, uint8_t* logPayload) { emberAfEventsClusterPrint("RX: PublishEventLog 0x%2x, 0x%x, 0x%x, 0x%x", totalNumberOfEvents, commandIndex, totalCommands, logPayloadControl); #if defined(EMBER_AF_PRINT_ENABLE) && defined(EMBER_AF_PRINT_EVENTS_CLUSTER) uint16_t logPayloadLen = (emberAfCurrentCommand()->bufLen - (emberAfCurrentCommand()->payloadStartIndex + sizeof(totalNumberOfEvents) + sizeof(commandIndex) + sizeof(totalCommands) + sizeof(logPayloadControl))); uint16_t logPayloadIndex = 0; uint8_t i; if (NULL != logPayload) { for (i = 0; i < numberOfEvents(logPayloadControl); i++) { uint8_t logId; uint16_t eventId; uint32_t eventTime; uint8_t *eventData; uint8_t eventDataLen; logId = emberAfGetInt8u(logPayload, logPayloadIndex, logPayloadLen); logPayloadIndex++; eventId = emberAfGetInt16u(logPayload, logPayloadIndex, logPayloadLen); logPayloadIndex += 2; eventTime = emberAfGetInt32u(logPayload, logPayloadIndex, logPayloadLen); logPayloadIndex += 4; eventData = logPayload + logPayloadIndex; eventDataLen = emberAfGetInt8u(logPayload, logPayloadIndex, logPayloadLen); logPayloadIndex += (1 + eventDataLen); emberAfEventsClusterPrint(" ["); emberAfEventsClusterPrint("0x%x, 0x%2x, 0x%4x, 0x%x", logId, eventId, eventTime, eventDataLen); if (eventDataLen > 0) { emberAfEventsClusterPrint(", "); emberAfEventsClusterPrintString(eventData); } emberAfEventsClusterPrint("]"); } } emberAfEventsClusterPrintln(""); #endif // defined(EMBER_AF_PRINT_ENABLE) && defined(EMBER_AF_PRINT_EVENTS_CLUSTER) emberAfSendImmediateDefaultResponse(EMBER_ZCL_STATUS_SUCCESS); return true; }
bool emberAfScenesClusterEnhancedViewSceneCallback(uint16_t groupId, uint8_t sceneId) { return emberAfPluginScenesServerParseViewScene(emberAfCurrentCommand(), groupId, sceneId); }
bool emberAfSimpleMeteringClusterConfigureMirrorCallback(uint32_t issuerEventId, uint32_t reportingInterval, uint8_t mirrorNotificationReporting, uint8_t notificationScheme) { EmberAfStatus status = EMBER_ZCL_STATUS_SUCCESS; uint8_t endpoint = emberAfCurrentEndpoint(); EmberEUI64 sendersEui; uint8_t index; emberAfSimpleMeteringClusterPrintln("ConfigureMirror on endpoint 0x%x", endpoint); if (EMBER_SUCCESS != emberLookupEui64ByNodeId(emberAfCurrentCommand()->source, sendersEui)) { emberAfSimpleMeteringClusterPrintln("Error: Meter Mirror plugin cannot determine EUI64 for node ID 0x%2X", emberAfCurrentCommand()->source); status = EMBER_ZCL_STATUS_FAILURE; goto kickout; } index = findMirrorIndex(sendersEui); if (index == INVALID_INDEX) { emberAfSimpleMeteringClusterPrint("Error: Meter mirror plugin received unknown report from "); emberAfPrintBigEndianEui64(sendersEui); emberAfSimpleMeteringClusterPrintln(""); status = EMBER_ZCL_STATUS_NOT_AUTHORIZED; goto kickout; } if (mirrorList[index].issuerEventId == 0 || issuerEventId > mirrorList[index].issuerEventId) { if(notificationScheme > 0x02) { status = EMBER_ZCL_STATUS_INVALID_FIELD; goto kickout; } mirrorList[index].issuerEventId = issuerEventId; mirrorList[index].reportingInterval = reportingInterval; mirrorList[index].mirrorNotificationReporting = mirrorNotificationReporting; mirrorList[index].notificationScheme = notificationScheme; } kickout: emberAfSendImmediateDefaultResponse(status); return true; }
bool emberAfScenesClusterEnhancedAddSceneResponseCallback(uint8_t status, uint16_t groupId, uint8_t sceneId) { return emberAfPluginScenesClientParseAddSceneResponse(emberAfCurrentCommand(), status, groupId, sceneId); }
bool emberAfScenesClusterEnhancedAddSceneCallback(uint16_t groupId, uint8_t sceneId, uint16_t transitionTime, uint8_t *sceneName, uint8_t *extensionFieldSets) { return emberAfPluginScenesServerParseAddScene(emberAfCurrentCommand(), groupId, sceneId, transitionTime, sceneName, extensionFieldSets); }
bool emberAfScenesClusterEnhancedViewSceneResponseCallback(uint8_t status, uint16_t groupId, uint8_t sceneId, uint16_t transitionTime, uint8_t *sceneName, uint8_t *extensionFieldSets) { return emberAfPluginScenesClientParseViewSceneResponse(emberAfCurrentCommand(), status, groupId, sceneId, transitionTime, sceneName, extensionFieldSets); }
/** @brief Is Protocol Supported * * This function is called by the Tunneling server plugin whenever a Request * Tunnel command is received. The application should return true if the * protocol is supported and false otherwise. * * @param protocolId The identifier of the metering communication protocol for * which the tunnel is requested. Ver.: always * @param manufacturerCode The manufacturer code for manufacturer-defined * protocols or 0xFFFF in unused. Ver.: always */ bool emberAfPluginTunnelingServerIsProtocolSupportedCallback(uint8_t protocolId, uint16_t manufacturerCode) { EmberEUI64 remoteDeviceId; emberAfDebugPrintln("CHF: ServerIsProtocolSupported:0x%x 0x%2x", protocolId, manufacturerCode); // Since the tunneling cluster server code does not pass the EUI64 or the // node ID of the remote end of the tunnel so we need to look them up. // Luckily this callback is called in the context of the RequestTunnel // command processing and we look into the command for this info. emberLookupEui64ByNodeId(emberAfCurrentCommand()->source, remoteDeviceId); return (GBCS_TUNNELING_PROTOCOL_ID == protocolId && GBCS_TUNNELING_MANUFACTURER_CODE == manufacturerCode && emAfPluginCommsHubFunctionTunnelAcceptCallback(remoteDeviceId)); }
bool emberAfPrepaymentClusterGetPrepaySnapshotCallback( uint32_t earliestStartTime, uint32_t latestEndTime, uint8_t snapshotOffset, uint32_t snapshotCause ){ EmberNodeId nodeId; uint8_t srcEndpoint, dstEndpoint; emberAfPrepaymentClusterPrintln("RX: GetPrepaySnapshot, st=0x%4x, offset=%d, cause=%d", earliestStartTime, snapshotOffset, snapshotCause ); nodeId = emberAfCurrentCommand()->source; srcEndpoint = emberAfGetCommandApsFrame()->destinationEndpoint; dstEndpoint = emberAfGetCommandApsFrame()->sourceEndpoint; emberAfPrepaymentClusterPrintln("... from 0x%2x, ep=%d", nodeId, dstEndpoint ); emberAfPluginPrepaymentServerGetSnapshotCallback( nodeId, srcEndpoint, dstEndpoint, earliestStartTime, latestEndTime, snapshotOffset, snapshotCause ); return true; }
bool emberAfReportAttributesCallback(EmberAfClusterId clusterId, uint8_t *buffer, uint16_t bufLen) { EmberEUI64 sendersEui; uint16_t bufIndex = 0; uint8_t endpoint; uint8_t index; bool attributeReportingComplete = false; if (EMBER_SUCCESS != emberLookupEui64ByNodeId(emberAfCurrentCommand()->source, sendersEui)) { emberAfSimpleMeteringClusterPrintln("Error: Meter Mirror plugin cannot determine EUI64 for node ID 0x%2X", emberAfCurrentCommand()->source); emberAfSendImmediateDefaultResponse(EMBER_ZCL_STATUS_FAILURE); return true; } if (emberAfCurrentCommand()->direction == ZCL_DIRECTION_CLIENT_TO_SERVER) { emberAfSimpleMeteringClusterPrintln("Error: Meter Mirror Plugin does not accept client to server attributes.\n", emberAfCurrentCommand()->direction); emberAfSendImmediateDefaultResponse(EMBER_ZCL_STATUS_FAILURE); return true; } index = findMirrorIndex(sendersEui); if (index == INVALID_INDEX) { emberAfSimpleMeteringClusterPrint("Error: Meter mirror plugin received unknown report from "); emberAfPrintBigEndianEui64(sendersEui); emberAfSimpleMeteringClusterPrintln(""); emberAfSendImmediateDefaultResponse(EMBER_ZCL_STATUS_NOT_AUTHORIZED); return true; } if (emberAfCurrentCommand()->mfgSpecific) { // Here is where we could handle a MFG specific Report attributes and interpret // it. This code does not do that, just politely returns an error. emberAfSimpleMeteringClusterPrintln("Error: Unknown MFG Code for mirror: 0x%2X", emberAfCurrentCommand()->mfgCode); emberAfSendImmediateDefaultResponse(EMBER_ZCL_STATUS_UNSUP_MANUF_GENERAL_COMMAND); return true; } endpoint = (index + EMBER_AF_PLUGIN_METER_MIRROR_ENDPOINT_START); while (bufIndex + ATTRIBUTE_OVERHEAD < bufLen) { EmberAfStatus status; EmberAfAttributeId attributeId; EmberAfAttributeType dataType; uint8_t dataSize; attributeId = (EmberAfAttributeId)emberAfGetInt16u(buffer, bufIndex, bufLen); bufIndex += 2; dataType = (EmberAfAttributeType)emberAfGetInt8u(buffer, bufIndex, bufLen); bufIndex++; // For strings, the data size is the length of the string (specified by the // first byte of data) plus one for the length byte itself. For everything // else, the size is just the size of the data type. dataSize = (emberAfIsThisDataTypeAStringType(dataType) ? emberAfStringLength(buffer + bufIndex) + 1 : emberAfGetDataSize(dataType)); { #if (BIGENDIAN_CPU) uint8_t data[ATTRIBUTE_LARGEST]; if (isThisDataTypeSentLittleEndianOTA(dataType)) { emberReverseMemCopy(data, buffer + bufIndex, dataSize); } else { MEMMOVE(data, buffer + bufIndex, dataSize); } #else uint8_t *data = buffer + bufIndex; #endif if (attributeId == ZCL_ATTRIBUTE_REPORTING_STATUS_ATTRIBUTE_ID) { // From the SE 1.2a Specification within the ConfigureMirror command // description.. // 1. On powering up, the BOMD will send one or more Report Attribute // commands to the Metering client on the mirror endpoint. The last // attribute to be reported to the mirror shall be an Attribute // Reporting Status attribute, as defined in section A.2. // 2. If MirrorReportAttributeResponse is enabled, the server does not // need to request an APS ACK. If the server requests an APS ACK, // the Metering client on the mirror endpoint shall respond first // with an APS ACK and then send the MirrorReportAttributeResponse. // // If Mirror Notification Reporting is set to false, the // MirrorReportAttributeResponse command shall not be enabled; the // Metering server may poll the Notification flags by means of a normal // ReadAttribute command, as shown in Figure D 29: attributeReportingComplete = (data[0] == EMBER_ZCL_ATTRIBUTE_REPORTING_STATUS_ATTRIBUTE_REPORTING_COMPLETE); status = EMBER_ZCL_STATUS_SUCCESS; } else { if (attributeId == ZCL_METERING_DEVICE_TYPE_ATTRIBUTE_ID && data[0] < EMBER_ZCL_METERING_DEVICE_TYPE_MIRRORED_ELECTRIC_METERING) { data[0] += 127; } status = emberAfWriteServerAttribute(endpoint, clusterId, attributeId, data, dataType); } } emberAfSimpleMeteringClusterPrintln("Mirror attribute 0x%2x: 0x%x", attributeId, status); bufIndex += dataSize; } // If reporting is complete then callback to the application so that if it needs // to do any post processing on the reported attributes it can do it now. if (attributeReportingComplete) { emberAfPluginMeterMirrorReportingCompleteCallback(endpoint); } // Notification flags emberAfSimpleMeteringClusterPrintln("Mirror reporting ep: 0x%x, reporting: 0x%x, scheme: 0x%x", endpoint, mirrorList[index].mirrorNotificationReporting, mirrorList[index].notificationScheme); if (mirrorList[index].mirrorNotificationReporting && attributeReportingComplete) { if (mirrorList[index].notificationScheme == EMBER_ZCL_NOTIFICATION_SCHEME_PREDEFINED_NOTIFICATION_SCHEME_A || mirrorList[index].notificationScheme == EMBER_ZCL_NOTIFICATION_SCHEME_PREDEFINED_NOTIFICATION_SCHEME_B) { return sendMirrorReportAttributeResponse(endpoint, index); } else { // TODO: for custom notification schemes callback to application // return emberAfMeterMirrorSendMirrorReportAttributeResponseCallback(...) } } emberAfSendImmediateDefaultResponse(EMBER_ZCL_STATUS_SUCCESS); return true; }
/** @brief Tunnel Opened * * This function is called by the Tunneling server plugin whenever a tunnel is * opened. Clients may open tunnels by sending a Request Tunnel command. * * @param tunnelId The identifier of the tunnel that has been opened. Ver.: * always * @param protocolId The identifier of the metering communication protocol for * the tunnel. Ver.: always * @param manufacturerCode The manufacturer code for manufacturer-defined * protocols or 0xFFFF in unused. Ver.: always * @param flowControlSupport true is flow control support is requested or false * if it is not. Ver.: always * @param maximumIncomingTransferSize The maximum incoming transfer size of the * client. Ver.: always */ void emberAfPluginTunnelingServerTunnelOpenedCallback(uint16_t tunnelId, uint8_t protocolId, uint16_t manufacturerCode, bool flowControlSupport, uint16_t maximumIncomingTransferSize) { EmberEUI64 remoteDeviceId; EmberNodeId remoteNodeId; uint8_t tunnelIndex; emberAfDebugPrintln("CHF: ServerTunnelOpened:0x%x,0x%2x", tunnelId, maximumIncomingTransferSize); // Since the tunneling cluster server code does not pass the EUI64 or the // node ID of the remote end of the tunnel so we need to look them up. // Luckily this callback is called in the context of the RequestTunnel // command processing and we look into the command for this info. remoteNodeId = emberAfCurrentCommand()->source; emberLookupEui64ByNodeId(emberAfCurrentCommand()->source, remoteDeviceId); // We only support one tunnel to a given remote device/endpoint so if we // already have a tunnel lets work with it. tunnelIndex = findTunnelByDeviceId(remoteDeviceId); if (tunnelIndex == EM_AF_PLUGIN_COMMS_HUB_FUNCTION_NULL_TUNNEL_INDEX) { // Find a slot in the tunnels table for the new tunnel tunnelIndex = findUnusedTunnel(); } if (tunnelIndex != EM_AF_PLUGIN_COMMS_HUB_FUNCTION_NULL_TUNNEL_INDEX) { MEMCOPY(tunnels[tunnelIndex].remoteDeviceId, remoteDeviceId, EUI64_SIZE); tunnels[tunnelIndex].remoteNodeId = remoteNodeId; tunnels[tunnelIndex].remoteEndpoint = emberAfCurrentCommand()->apsFrame->sourceEndpoint; tunnels[tunnelIndex].type = SERVER_TUNNEL; tunnels[tunnelIndex].state = ACTIVE_TUNNEL; tunnels[tunnelIndex].tunnelId = tunnelId; tunnels[tunnelIndex].timeoutMSec = 0; // Per GBCS v0.8 section 10.2.2, Devices supporting the Tunneling Cluster // as a Server shall have a MaximumIncomingTransferSize set to 1500 octets, // in line with the ZSE default. All Devices supporting the Tunneling // Cluster shall use this value in any RequestTunnelResponse command and // any RequestTunnel command. // // So rather than bring down the tunnel in the case when the maximumIncomingTransferSize // is less than 1500 we'll just log a warning message. if (maximumIncomingTransferSize < 1500) { emberAfPluginCommsHubFunctionPrintln("Warning: tunnel opened but MaximumIncomingTransferSize of client is %d but should be 1500", maximumIncomingTransferSize); } return; } // This is a misconfiguration or a bug in the code calling this API. Either // the tunnel client plugin limit is set too low for the number of tunnels // required or the code that is calling this function is in error. Either way, // we'll print the error and return false indicating that the tunnel was // not created. emberAfPluginCommsHubFunctionPrintln("%p%p%p", "Error: ", "Tunnel Opened failed: ", "Too many tunnels"); }
bool emberAfPrepaymentClusterChangePaymentModeCallback(uint32_t providerId, uint32_t issuerEventId, uint32_t implementationDateTime, PaymentControlConfiguration proposedPaymentControlConfiguration, uint32_t cutOffValue){ // The requester can be obtained with emberAfResponseDestination; EmberNodeId nodeId; uint8_t endpoint; uint8_t srcEndpoint, dstEndpoint; FriendlyCredit friendlyCredit; uint32_t friendlyCreditCalendarId; uint32_t emergencyCreditLimit; uint32_t emergencyCreditThreshold; uint8_t dataType; uint8_t i; emberAfPrepaymentClusterPrintln("RX: ChangePaymentMode, pid=0x%4x, eid=0x%4x, cfg=0x%2x", providerId, issuerEventId, proposedPaymentControlConfiguration); endpoint = emberAfCurrentEndpoint(); if( cutOffValue != CUTOFF_UNCHANGED ){ #ifdef ZCL_USING_PREPAYMENT_CLUSTER_CUT_OFF_VALUE_ATTRIBUTE emberAfWriteAttribute( endpoint, ZCL_PREPAYMENT_CLUSTER_ID, ZCL_CUT_OFF_VALUE_ATTRIBUTE_ID, CLUSTER_MASK_SERVER, (uint8_t *)&cutOffValue, ZCL_INT32S_ATTRIBUTE_TYPE ); #endif } emberAfPrepaymentSchedulePrepaymentMode( emberAfCurrentEndpoint(), providerId, issuerEventId, implementationDateTime, proposedPaymentControlConfiguration ); // Setup the friendly credit & emergency credit limit attributes. #ifdef EMBER_AF_PLUGIN_CALENDAR_CLIENT i = emberAfPluginCalendarClientGetCalendarIndexByType( endpoint, EMBER_ZCL_CALENDAR_TYPE_FRIENDLY_CREDIT_CALENDAR ); friendlyCredit = ( i < EMBER_AF_PLUGIN_CALENDAR_CLIENT_CALENDARS ) ? 0x01 : 0x00; friendlyCreditCalendarId = emberAfPluginCalendarClientGetCalendarId( endpoint, i ); #else friendlyCredit = 0x00; friendlyCreditCalendarId = EMBER_AF_PLUGIN_CALENDAR_CLIENT_INVALID_CALENDAR_ID; #endif #if (!defined ZCL_USING_PREPAYMENT_CLUSTER_EMERGENCY_CREDIT_LIMIT_ALLOWANCE_ATTRIBUTE) || \ (!defined ZCL_USING_PREPAYMENT_CLUSTER_EMERGENCY_CREDIT_THRESHOLD_ATTRIBUTE) #error "Prepayment Emergency Credit Limit/Allowance and Threshold attributes required for this plugin!" #endif emberAfReadAttribute( emberAfCurrentEndpoint(), ZCL_PREPAYMENT_CLUSTER_ID, ZCL_EMERGENCY_CREDIT_LIMIT_ALLOWANCE_ATTRIBUTE_ID, CLUSTER_MASK_SERVER, (uint8_t *)&emergencyCreditLimit, 4, &dataType ); emberAfReadAttribute( emberAfCurrentEndpoint(), ZCL_PREPAYMENT_CLUSTER_ID, ZCL_EMERGENCY_CREDIT_THRESHOLD_ATTRIBUTE_ID, CLUSTER_MASK_SERVER, (uint8_t *)&emergencyCreditThreshold, 4, &dataType ); nodeId = emberAfCurrentCommand()->source; srcEndpoint = emberAfGetCommandApsFrame()->destinationEndpoint; dstEndpoint = emberAfGetCommandApsFrame()->sourceEndpoint; emberAfSetCommandEndpoints( srcEndpoint, dstEndpoint ); emberAfFillCommandPrepaymentClusterChangePaymentModeResponse( friendlyCredit, friendlyCreditCalendarId, emergencyCreditLimit, emergencyCreditThreshold ); emberAfSendCommandUnicast( EMBER_OUTGOING_DIRECT, nodeId ); return true; }
bool emberAfScenesClusterCopySceneCallback(uint8_t mode, uint16_t groupIdFrom, uint8_t sceneIdFrom, uint16_t groupIdTo, uint8_t sceneIdTo) { EmberAfStatus status = EMBER_ZCL_STATUS_INVALID_FIELD; bool copyAllScenes = (mode & ZCL_SCENES_CLUSTER_MODE_COPY_ALL_SCENES_MASK); uint8_t i; emberAfScenesClusterPrintln("RX: CopyScene 0x%x, 0x%2x, 0x%x, 0x%2x, 0x%x", mode, groupIdFrom, sceneIdFrom, groupIdTo, sceneIdTo); // If a group id is specified but this endpoint isn't in it, take no action. if ((groupIdFrom != ZCL_SCENES_GLOBAL_SCENE_GROUP_ID && !emberAfGroupsClusterEndpointInGroupCallback(emberAfCurrentEndpoint(), groupIdFrom)) || (groupIdTo != ZCL_SCENES_GLOBAL_SCENE_GROUP_ID && !emberAfGroupsClusterEndpointInGroupCallback(emberAfCurrentEndpoint(), groupIdTo))) { status = EMBER_ZCL_STATUS_INVALID_FIELD; goto kickout; } for (i = 0; i < EMBER_AF_PLUGIN_SCENES_TABLE_SIZE; i++) { EmberAfSceneTableEntry from; emberAfPluginScenesServerRetrieveSceneEntry(from, i); if (from.endpoint == emberAfCurrentEndpoint() && from.groupId == groupIdFrom && (copyAllScenes || from.sceneId == sceneIdFrom)) { uint8_t j, index = EMBER_AF_SCENE_TABLE_NULL_INDEX; for (j = 0; j < EMBER_AF_PLUGIN_SCENES_TABLE_SIZE; j++) { EmberAfSceneTableEntry to; if (i == j) { continue; } emberAfPluginScenesServerRetrieveSceneEntry(to, j); if (to.endpoint == emberAfCurrentEndpoint() && to.groupId == groupIdTo && to.sceneId == (copyAllScenes ? from.sceneId : sceneIdTo)) { index = j; break; } else if (index == EMBER_AF_SCENE_TABLE_NULL_INDEX && to.endpoint == EMBER_AF_SCENE_TABLE_UNUSED_ENDPOINT_ID) { index = j; } } // If the target index is still zero, the table is full. if (index == EMBER_AF_SCENE_TABLE_NULL_INDEX) { status = EMBER_ZCL_STATUS_INSUFFICIENT_SPACE; goto kickout; } // Save the "from" entry to the "to" index. This makes a copy of "from" // with the correct group and scene ids and leaves the original in tact. from.groupId = groupIdTo; if (!copyAllScenes) { from.sceneId = sceneIdTo; } emberAfPluginScenesServerSaveSceneEntry(from, index); if (j != index) { emberAfPluginScenesServerIncrNumSceneEntriesInUse(); emberAfScenesSetSceneCountAttribute(emberAfCurrentEndpoint(), emberAfPluginScenesServerNumSceneEntriesInUse()); } // If we aren't copying all scenes, we can stop here. status = EMBER_ZCL_STATUS_SUCCESS; if (!copyAllScenes) { goto kickout; } } } kickout: // Copy Scene commands are only responded to when they are addressed to a // single device. if (emberAfCurrentCommand()->type == EMBER_INCOMING_UNICAST || emberAfCurrentCommand()->type == EMBER_INCOMING_UNICAST_REPLY) { emberAfFillCommandScenesClusterCopySceneResponse(status, groupIdFrom, sceneIdFrom); emberAfSendResponse(); } return true; }