/********************************************************************* * @fn zclApplianceStatistics_LogNotification_NativeToOta * * @brief Converts from native to OTA format. * * @param disableDefaultRsp - whether to disable the Default Response command * @param pZclPayloadLen - returns zcl payload len * * @return pointer to zcl payload */ uint8 *zclApplianceStatistics_LogNotification_NativeToOta( zclCmdApplianceStatisticsLogNotificationPayload_t *pPayload , uint16 * pZclPayloadLen ) { uint8 *pZclPayload; // OTA ZCL payload uint16 zclPayloadLen; // OTA ZCL payload length uint16 i; // get a buffer large enough to hold the whole packet zclPayloadLen = 12 + (uint16)( pPayload->logLength ); // 12=sizeof(TimeStamp) + sizeof(LogID) + sizeof(LogLength) pZclPayload = zcl_mem_alloc( zclPayloadLen ); if( !pZclPayload ) return NULL; // no memory // convert from Native to OTA format pZclPayload[0] = BREAK_UINT32(pPayload->timeStamp, 0); pZclPayload[1] = BREAK_UINT32(pPayload->timeStamp, 1); pZclPayload[2] = BREAK_UINT32(pPayload->timeStamp, 2); pZclPayload[3] = BREAK_UINT32(pPayload->timeStamp, 3); pZclPayload[4] = BREAK_UINT32(pPayload->logID, 0); pZclPayload[5] = BREAK_UINT32(pPayload->logID, 1); pZclPayload[6] = BREAK_UINT32(pPayload->logID, 2); pZclPayload[7] = BREAK_UINT32(pPayload->logID, 3); pZclPayload[8] = BREAK_UINT32(pPayload->logLength, 0); pZclPayload[9] = BREAK_UINT32(pPayload->logLength, 1); pZclPayload[10] = BREAK_UINT32(pPayload->logLength, 2); pZclPayload[11] = BREAK_UINT32(pPayload->logLength, 3); for( i = 0; i < pPayload->logLength; ++i ) pZclPayload[12 + i] = pPayload->pLogPayload[i]; // return payload and length *pZclPayloadLen = zclPayloadLen; return pZclPayload; }
/********************************************************************* * @fn zclPartition_ConvertOtaToNative_ReadHandshakeParam * * @brief Helper function used to process an incoming ReadHandshakeParam * command. Remember to free memory. * * @param pCmd - (output) the converted command * @param buf - pointer to incoming frame (just after ZCL header) * @param buflen - length of buffer (ZCL payload) * * @return ZStatus_t - ZCL_STATUS_MALFORMED_COMMAND, ZMemError or ZCL_STATUS_SUCCESS */ ZStatus_t zclPartition_ConvertOtaToNative_ReadHandshakeParam( zclCmdReadHandshakeParam_t *pCmd, uint8 *buf, uint8 buflen ) { uint8 offset; uint8 i; // must have at least 1 attribute (clusterID + attrID) if ( buflen < 4 ) { return ( ZCL_STATUS_MALFORMED_COMMAND ); } pCmd->clusterID = BUILD_UINT16( buf[0], buf[1] ); // allocate memory for attribute list pCmd->numAttrs = ( buflen - 2 ) >> 1; // each AttrID is 2 bytes, - length of ClusterID pCmd->pAttrID = zcl_mem_alloc( (pCmd->numAttrs) << 1 ); // allocate 2 bytes per attribute if ( !pCmd->pAttrID ) { return ( ZMemError ); // memory error } // convert attributes to native offset = 2; for ( i = 0; i < pCmd->numAttrs; ++i ) { pCmd->pAttrID[i] = BUILD_UINT16( buf[offset], buf[offset+1] );; offset += 2; } return ( ZCL_STATUS_SUCCESS ); }
/********************************************************************* * @fn zclApplianceStatistics_LogQueueRsp_NativeToOta * * @brief Converts from native to OTA format. * * @param disableDefaultRsp - whether to disable the Default Response command * @param pZclPayloadLen - returns zcl payload len * * @return pointer to zcl payload */ uint8 *zclApplianceStatistics_LogQueueRsp_NativeToOta( zclCmdApplianceStatisticsLogQueueRspPayload_t *pPayload , uint16 * pZclPayloadLen ) { uint8 *pZclPayload; // OTA ZCL payload uint16 zclPayloadLen; // OTA ZCL payload length uint16 i; uint16 offset; // allocate some memory for the ZCL payload zclPayloadLen = 1 + (pPayload->logQueueSize * sizeof(uint32)); // # of LogQueue IDs pZclPayload = zcl_mem_alloc( zclPayloadLen ); if( !pZclPayload ) { return NULL; // no memory } // fill in payload pZclPayload[0] = pPayload->logQueueSize; offset = 1; for( i = 0; i < pPayload->logQueueSize; ++i ) { pZclPayload[offset] = BREAK_UINT32(pPayload->pLogID[i], 0); pZclPayload[offset+1] = BREAK_UINT32(pPayload->pLogID[i], 1); pZclPayload[offset+2] = BREAK_UINT32(pPayload->pLogID[i], 2); pZclPayload[offset+3] = BREAK_UINT32(pPayload->pLogID[i], 3); offset += sizeof( uint32 ); } // return OTA payload and length *pZclPayloadLen = zclPayloadLen; return pZclPayload; }
/********************************************************************* * @fn zclPartition_Send_MultipleAck * * @brief Call to send out Poll Control CheckIn command from ZED to ZR/ZC. The Rsp * will indicate whether to stay awake or go back to sleep. * * @param srcEP - Sending application's endpoint * @param dstAddr - where you want the message to go * @param pCmd - multi ack response * @param disableDefaultRsp - whether to disable the Default Response command * @param seqNum - sequence number * * @return ZStatus_t */ ZStatus_t zclPartition_Send_MultipleAck( uint8 srcEP, afAddrType_t *dstAddr, zclCmdMultipleAck_t *pCmd, uint8 disableDefaultRsp, uint8 seqNum ) { uint8 buflen; uint8 NAckSize; uint8 offset; uint8 i; uint8 *buf; ZStatus_t status; // determine if NACKs are 1 or 2 bytes if ( pCmd->options & ZCL_PARTITION_OPTIONS_NACK_16BIT ) { NAckSize = 2; } else { NAckSize = 1; } // create buffer large enough for multiple ACKs command // ACKOptions is 1 byte, FirstFrameID and NACKIds are 1 or 2 bytes depending on options // [ACKOptions][FirstFrameID][NACKId]...[NACKId] buflen = 1 + NAckSize * ( 1 + pCmd->numNAcks ); buf = zcl_mem_alloc( buflen ); if ( !buf ) { return ( ZMemError ); // memory error } // fill in the buffer buf[0] = pCmd->options; offset = 1; buf[offset++] = LO_UINT16( pCmd->firstFrameID ); if ( NAckSize == 2 ) { buf[offset++] = HI_UINT16( pCmd->firstFrameID ); } for ( i = 0; i < pCmd->numNAcks ; ++i ) { buf[offset++] = LO_UINT16( pCmd->pNAckID[i] ); if ( NAckSize == 2 ) { buf[offset++] = HI_UINT16( pCmd->pNAckID[i] ); } } // send, with payload status = zcl_SendCommand( srcEP, dstAddr, ZCL_CLUSTER_ID_GEN_PARTITION, COMMAND_PARTITION_MULTIPLE_ACK, TRUE, ZCL_FRAME_SERVER_CLIENT_DIR, disableDefaultRsp, 0, seqNum, offset, buf); // done, free the memory zcl_mem_free( buf ); return ( status ); }
/********************************************************************* * @fn zclElectricalMeasurement_Send_GetMeasurementProfileRsp * * @brief Call to send out Electrical Measurement Get Measurement Profile Response. This will * tell the client the appropriate parameters of the measurement profile. * * @param srcEP - Sending application's endpoint * @param dstAddr - where you want the message to go * @param pPayload: * startTime - represents the end time of the most chronologically recent interval being requested * status - table status enumeration lists the valid values returned in status field * profileIntervalPeriod - time frame used to capture parameter for profiling purposes * numberOfIntervalsDelivered - number of intervals the device is returning * attributeID - attribute that has been profiled by the application * intervals - array of intervals that depend on numberOfIntervalsDelivered, type based on attributeID * @param disableDefaultRsp - whether to disable the Default Response command * @param seqNum - sequence number * * @return ZStatus_t */ ZStatus_t zclElectricalMeasurement_Send_GetMeasurementProfileRsp( uint8 srcEP, afAddrType_t *dstAddr, zclElectricalMeasurementGetMeasurementProfileRsp_t *pPayload, uint8 disableDefaultRsp, uint8 seqNum ) { uint8 i; uint8 offset; uint8 *pBuf; // variable length payload uint8 attrRtn; uint8 calculatedAttrLen; uint16 calculatedIntervalSize; uint16 calculatedBufLen; ZStatus_t status; zclAttrRec_t attrRec; // determine if attribute ID is found per EP and cluster ID attrRtn = zclFindAttrRec( dstAddr->endPoint, ZCL_CLUSTER_ID_HA_ELECTRICAL_MEASUREMENT, pPayload->attributeID, &attrRec ); if ( attrRtn == TRUE ) { // if found, determine length of attribute based on given type calculatedAttrLen = zclGetDataTypeLength( attrRec.attr.dataType ); } calculatedIntervalSize = calculatedAttrLen * pPayload->numberOfIntervalsDelivered; // get a buffer large enough to hold the whole packet, including size of variable array calculatedBufLen = ( 9 + calculatedIntervalSize ); pBuf = zcl_mem_alloc( calculatedBufLen ); if ( !pBuf ) { return ( ZMemError ); // no memory, return failure } pBuf[0] = BREAK_UINT32(pPayload->startTime, 0); pBuf[1] = BREAK_UINT32(pPayload->startTime, 1); pBuf[2] = BREAK_UINT32(pPayload->startTime, 2); pBuf[3] = BREAK_UINT32(pPayload->startTime, 3); pBuf[4] = pPayload->status; pBuf[5] = pPayload->profileIntervalPeriod; pBuf[6] = pPayload->numberOfIntervalsDelivered; pBuf[7] = LO_UINT16(pPayload->attributeID); pBuf[8] = HI_UINT16(pPayload->attributeID); offset = 9; for ( i = 0; i < calculatedIntervalSize; i++ ) { pBuf[offset++] = pPayload->pIntervals[i]; } status = zcl_SendCommand( srcEP, dstAddr, ZCL_CLUSTER_ID_HA_ELECTRICAL_MEASUREMENT, COMMAND_ELECTRICAL_MEASUREMENT_GET_MEASUREMENT_PROFILE_RSP, TRUE, ZCL_FRAME_SERVER_CLIENT_DIR, disableDefaultRsp, 0, seqNum, calculatedBufLen, pBuf ); zcl_mem_free( pBuf ); return ( status ); }
/********************************************************************* * @fn zclPartition_ConvertOtaToNative_MultipleAck * * @brief Helper function used to process an incoming MultipleACK * command. Remember to free allocated memory. * * @param pCmd - (output) the converted command * @param buf - pointer to incoming frame (just after ZCL header) * @param buflen - length of buffer (ZCL payload) * * @return ZStatus_t - ZCL_STATUS_MALFORMED_COMMAND, ZMemError or ZCL_STATUS_SUCCESS */ ZStatus_t zclPartition_ConvertOtaToNative_MultipleAck( zclCmdMultipleAck_t *pCmd, uint8 *buf, uint8 buflen ) { uint8 nackSize; uint8 offset; uint8 i; // buffer not large enough! if ( buflen < 2 ) { return ( ZCL_STATUS_MALFORMED_COMMAND ); } pCmd->options = buf[0]; if ( pCmd->options & ZCL_PARTITION_OPTIONS_NACK_16BIT ) { pCmd->firstFrameID = BUILD_UINT16( buf[1], buf[2] ); nackSize = 2; offset = 3; } else { pCmd->firstFrameID = buf[1]; nackSize = 1; offset = 2; } // determine # of NACKs pCmd->numNAcks = ( buflen - offset ) / nackSize; // allocate enough memory for NACK IDs pCmd->pNAckID = zcl_mem_alloc ( pCmd->numNAcks * sizeof(uint16) ); if ( !pCmd->pNAckID ) { return ( ZMemError ); // memory error } // copy in NACK IDs for( i = 0; i < pCmd->numNAcks; ++i) { // copy NACk in native form if ( nackSize == 2 ) { pCmd->pNAckID[i] = BUILD_UINT16( buf[offset], buf[offset+1] ); offset += 2; } else { pCmd->pNAckID[i] = buf[offset++]; } } return ( ZCL_STATUS_SUCCESS ); }
/********************************************************************* * @fn zclPartition_Send_ReadHandshakeParamRsp * * @brief Call to send out Poll Control CheckIn command from ZED to ZR/ZC. The Rsp * will indicate whether to stay awake or go back to sleep. * * @param srcEP - Sending application's endpoint * @param dstAddr - where you want the message to go * @param pCmd - read multi-attribues response * @param disableDefaultRsp - whether to disable the Default Response command * @param seqNum - sequence number * * @return ZStatus_t */ ZStatus_t zclPartition_Send_ReadHandshakeParamRsp( uint8 srcEP, afAddrType_t *dstAddr, zclCmdReadHandshakeParamRsp_t *pCmd, uint8 disableDefaultRsp, uint8 seqNum ) { uint8 buflen; uint8 offset; uint8 i; uint8 *buf; ZStatus_t status; // allocate enough memory for all records. some may end up being short, so we may not use all this buffer. buflen = sizeof( uint16 ) + ( pCmd->numRecords * sizeof ( zclPartitionReadRec_t ) ); buf = zcl_mem_alloc( buflen ); if ( !buf ) { return ( ZMemError ); // memory error } // fill in the buffer buf[0] = LO_UINT16( pCmd->clusterID ); buf[1] = HI_UINT16( pCmd->clusterID ); offset = 2; for ( i = 0; i < pCmd->numRecords; ++i ) { buf[offset++] = LO_UINT16( pCmd->pReadRecord[i].attrID ); buf[offset++] = HI_UINT16( pCmd->pReadRecord[i].attrID ); buf[offset++] = pCmd->pReadRecord[i].status; if ( pCmd->pReadRecord[i].status == ZCL_STATUS_SUCCESS ) { buf[offset++] = pCmd->pReadRecord[i].dataType; // this only works for Partition Cluster because it's attributes are either uint8 or uint16 buf[offset++] = LO_UINT16( pCmd->pReadRecord[i].attr ); if ( pCmd->pReadRecord[i].dataType == ZCL_DATATYPE_UINT16 ) { buf[offset++] = LO_UINT16( pCmd->pReadRecord[i].attr ); } } } buflen = offset; // send, with payload status = zcl_SendCommand( srcEP, dstAddr, ZCL_CLUSTER_ID_GEN_PARTITION, COMMAND_PARTITION_READ_HANDSHAKE_PARAM_RSP, TRUE, ZCL_FRAME_CLIENT_SERVER_DIR, disableDefaultRsp, 0, seqNum, buflen, buf); // done, free the memory zcl_mem_free( buf ); return ( status ); }
/********************************************************************* * @fn zclPartition_Send_WriteHandshakeParam * * @brief Call to send out Poll Control CheckIn command from ZED to ZR/ZC. The Rsp * will indicate whether to stay awake or go back to sleep. * * @param srcEP - Sending application's endpoint * @param dstAddr - where you want the message to go * @param pCmd - what parameters to write * @param disableDefaultRsp - whether to disable the Default Response command * @param seqNum - sequence number * * @return ZStatus_t */ ZStatus_t zclPartition_Send_WriteHandshakeParam( uint8 srcEP, afAddrType_t *dstAddr, zclCmdWriteHandshakeParam_t *pCmd, uint8 disableDefaultRsp, uint8 seqNum ) { uint8 buflen; uint8 offset; uint8 i; uint8 *buf; uint16 clusterID; ZStatus_t status; // allocate the memory. write records are assumed to be either uint8 or uint16 // WriteRecord: [AttrID][AttrType][AttrData] = max 5 bytes per record, if attrType is uint16 or uint8 buflen = PAYLOAD_LEN_WRITE_HANDSHAKE_PARAM + ( pCmd->numRecords * PAYLOAD_LEN_WRITE_REC ); buf = zcl_mem_alloc( buflen ); if ( !buf ) { return ( ZMemError ); // memory error } // fill in the buffer clusterID = pCmd->clusterID; buf[0] = LO_UINT16( clusterID ); buf[1] = HI_UINT16( clusterID ); offset = 2; for ( i = 0; i < pCmd->numRecords; ++i ) { // copy write record to output buffer buf[offset++] = LO_UINT16( pCmd->pWriteRecord[i].attrID ); buf[offset++] = HI_UINT16( pCmd->pWriteRecord[i].attrID ); buf[offset++] = pCmd->pWriteRecord[i].dataType; // all writeable Partition attributes are either uint8 or uint16 buf[offset++] = LO_UINT16( pCmd->pWriteRecord[i].attr ); if ( pCmd->pWriteRecord[i].dataType == ZCL_DATATYPE_UINT16 ) { buf[offset++] = HI_UINT16( pCmd->pWriteRecord[i].attr ); } } // send, with payload status = zcl_SendCommand( srcEP, dstAddr, ZCL_CLUSTER_ID_GEN_PARTITION, COMMAND_PARTITION_WRITE_HANDSHAKE_PARAM, TRUE, ZCL_FRAME_CLIENT_SERVER_DIR, disableDefaultRsp, 0, seqNum, offset, buf); // done, free the memory zcl_mem_free( buf ); return ( status ); }
/********************************************************************* * @fn zclPartition_RegisterCmdCallbacks * * @brief Register applications command callbacks * * @param endpoint - application's endpoint * @param callbacks - pointer to the callback record. * * @return ZMemError if not able to allocate */ ZStatus_t zclPartition_RegisterCmdCallbacks( uint8 endpoint, zclPartition_AppCallbacks_t *callbacks ) { zclPartitionCBRec_t *pNewItem; zclPartitionCBRec_t *pLoop; // Register as a ZCL Plugin if ( zclPartitionPluginRegisted == FALSE ) { zcl_registerPlugin( ZCL_CLUSTER_ID_GEN_PARTITION, ZCL_CLUSTER_ID_GEN_PARTITION, zclPartition_HdlIncoming ); zclPartitionPluginRegisted = TRUE; } // Fill in the new profile list pNewItem = zcl_mem_alloc( sizeof( zclPartitionCBRec_t ) ); if ( pNewItem == NULL ) { return ( ZMemError ); // memory error } pNewItem->next = (zclPartitionCBRec_t *)NULL; pNewItem->endpoint = endpoint; pNewItem->CBs = callbacks; // Find spot in list if ( zclPartitionCBs == NULL ) { zclPartitionCBs = pNewItem; } else { // Look for end of list pLoop = zclPartitionCBs; while ( pLoop->next != NULL ) pLoop = pLoop->next; // Put new item at end of list pLoop->next = pNewItem; } return ( ZSuccess ); }
/********************************************************************* * @fn zclElectricalMeasurement_RegisterCmdCallbacks * * @brief Register applications command callbacks * * @param endpoint - application's endpoint * @param callbacks - pointer to the callback record. * * @return ZMemError if not able to allocate */ ZStatus_t zclElectricalMeasurement_RegisterCmdCallbacks( uint8 endpoint, zclElectricalMeasurement_AppCallbacks_t *callbacks ) { zclElectricalMeasurementCBRec_t *pNewItem; zclElectricalMeasurementCBRec_t *pLoop; // Register as a ZCL Plugin if ( zclElectricalMeasurementPluginRegisted == FALSE ) { zcl_registerPlugin( ZCL_CLUSTER_ID_HA_ELECTRICAL_MEASUREMENT, ZCL_CLUSTER_ID_HA_ELECTRICAL_MEASUREMENT, zclElectricalMeasurement_HdlIncoming ); zclElectricalMeasurementPluginRegisted = TRUE; } // Fill in the new profile list pNewItem = zcl_mem_alloc( sizeof( zclElectricalMeasurementCBRec_t ) ); if ( pNewItem == NULL ) { return ( ZMemError ); } pNewItem->next = (zclElectricalMeasurementCBRec_t *)NULL; pNewItem->endpoint = endpoint; pNewItem->CBs = callbacks; // Find spot in list if ( zclElectricalMeasurementCBs == NULL ) { zclElectricalMeasurementCBs = pNewItem; } else { // Look for end of list pLoop = zclElectricalMeasurementCBs; while ( pLoop->next != NULL ) pLoop = pLoop->next; // Put new item at end of list pLoop->next = pNewItem; } return ( ZSuccess ); }
/********************************************************************* * @fn zclApplianceStatistics_RegisterCmdCallbacks * * @brief Register applications command callbacks * * @param endpoint - application's endpoint * @param callbacks - pointer to the callback record. * * @return ZMemError if not able to allocate */ ZStatus_t zclApplianceStatistics_RegisterCmdCallbacks( uint8 endpoint, zclApplianceStatistics_AppCallbacks_t *callbacks ) { zclApplianceStatisticsCBRec_t *pNewItem; zclApplianceStatisticsCBRec_t *pLoop; // Register as a ZCL Plugin if ( zclApplianceStatisticsPluginRegisted == FALSE ) { zcl_registerPlugin( ZCL_CLUSTER_ID_HA_APPLIANCE_STATISTICS, ZCL_CLUSTER_ID_HA_APPLIANCE_STATISTICS, zclApplianceStatistics_HdlIncoming ); zclApplianceStatisticsPluginRegisted = TRUE; } // Fill in the new profile list pNewItem = zcl_mem_alloc( sizeof( zclApplianceStatisticsCBRec_t ) ); if ( pNewItem == NULL ) { return ( ZMemError ); } pNewItem->next = (zclApplianceStatisticsCBRec_t *)NULL; pNewItem->endpoint = endpoint; pNewItem->CBs = callbacks; // Find spot in list if ( zclApplianceStatisticsCBs == NULL ) { zclApplianceStatisticsCBs = pNewItem; } else { // Look for end of list pLoop = zclApplianceStatisticsCBs; while ( pLoop->next != NULL ) pLoop = pLoop->next; // Put new item at end of list pLoop->next = pNewItem; } return ( ZSuccess ); }
/********************************************************************* * @fn zclPartition_Send_ReadHandshakeParam * * @brief Call to send out Poll Control CheckIn command from ZED to ZR/ZC. The Rsp * will indicate whether to stay awake or go back to sleep. * * @param srcEP - Sending application's endpoint * @param dstAddr - where you want the message to go * @param pCmd - what parameters to read * @param disableDefaultRsp - whether to disable the Default Response command * @param seqNum - sequence number * * @return ZStatus_t */ ZStatus_t zclPartition_Send_ReadHandshakeParam( uint8 srcEP, afAddrType_t *dstAddr, zclCmdReadHandshakeParam_t *pCmd, uint8 disableDefaultRsp, uint8 seqNum ) { uint8 buflen; uint8 offset; uint8 *buf; uint8 i; uint16 clusterID; uint16 attrID; ZStatus_t status; // allocate the memory buflen = sizeof( uint16 ) + pCmd->numAttrs * sizeof( uint16 ); buf = zcl_mem_alloc( buflen ); if ( !buf ) { return ( ZMemError ); // memory error } // fill in the buffer clusterID = pCmd->clusterID; buf[0] = LO_UINT16( clusterID ); buf[1] = HI_UINT16( clusterID ); for ( i = 0; i < pCmd->numAttrs; ++i ) { attrID = pCmd->pAttrID[i]; buf[offset++] = LO_UINT16( attrID ); buf[offset++] = HI_UINT16( attrID ); } // send, with payload status = zcl_SendCommand( srcEP, dstAddr, ZCL_CLUSTER_ID_GEN_PARTITION, COMMAND_PARTITION_READ_HANDSHAKE_PARAM, TRUE, ZCL_FRAME_CLIENT_SERVER_DIR, disableDefaultRsp, 0, seqNum, buflen, buf); // done, free the memory zcl_mem_free( buf ); return ( status ); }
/********************************************************************* * @fn zclPartition_Send_TransferPartitionedFrame * * @brief send a single block (partitioned frame) to a remote receiver * * @param srcEP - Sending application's endpoint * @param dstAddr - where you want the message to go * @param pCmd - the partitioned frame * @param disableDefaultRsp - whether to disable the Default Response command * @param seqNum - sequence number * * @return ZStatus_t or ZCL_STATUS_INSUFFICIENT_SPACE */ ZStatus_t zclPartition_Send_TransferPartitionedFrame( uint8 srcEP, afAddrType_t *dstAddr, zclCmdTransferPartitionedFrame_t *pCmd, uint8 disableDefaultRsp, uint8 seqNum ) { uint8 buflen; uint8 offset; uint8 *buf; uint16 indicator; ZStatus_t status; // allocate the memory large enough to hold the OTA frame with a 2-byte indicator buflen = PAYLOAD_LEN_TRANSFER_PARTITIONED_FRAME + pCmd->frameLen; buf = zcl_mem_alloc( buflen ); if ( !buf ) { return ( ZMemError ); // memory error } // fill in the OTA frame buffer buf[0] = pCmd->fragmentationOptions; indicator = pCmd->partitionIndicator; buf[1] = LO_UINT16(indicator); offset = 2; if ( pCmd->fragmentationOptions & ZCL_PARTITION_OPTIONS_INDICATOR_16BIT ) { buf[offset++] = HI_UINT16(indicator); } buf[offset++] = pCmd->frameLen; // octet string begins with length zcl_memcpy( &buf[offset], pCmd->pFrame, pCmd->frameLen ); offset += pCmd->frameLen; // send, with payload status = zcl_SendCommand( srcEP, dstAddr, ZCL_CLUSTER_ID_GEN_PARTITION, COMMAND_PARTITION_TRANSFER_PARTITIONED_FRAME, TRUE, ZCL_FRAME_CLIENT_SERVER_DIR, disableDefaultRsp, 0, seqNum, offset, buf ); // done, free the memory zcl_mem_free( buf ); return ( status ); }
/********************************************************************* * @fn zclHVAC_RegisterCmdCallbacks * * @brief Register an applications command callbacks * * @param endpoint - application's endpoint * @param callbacks - pointer to the callback record. * * @return ZMemError if not able to allocate */ ZStatus_t zclHVAC_RegisterCmdCallbacks( uint8 endpoint, zclHVAC_AppCallbacks_t *callbacks ) { zclHVACCBRec_t *pNewItem; zclHVACCBRec_t *pLoop; // Register as a ZCL Plugin if ( !zclHVACPluginRegisted ) { zcl_registerPlugin( ZCL_CLUSTER_ID_HVAC_PUMP_CONFIG_CONTROL, ZCL_CLUSTER_ID_HVAC_USER_INTERFACE_CONFIG, zclHVAC_HdlIncoming ); zclHVACPluginRegisted = TRUE; } // Fill in the new profile list pNewItem = zcl_mem_alloc( sizeof( zclHVACCBRec_t ) ); if ( pNewItem == NULL ) return (ZMemError); pNewItem->next = (zclHVACCBRec_t *)NULL; pNewItem->endpoint = endpoint; pNewItem->CBs = callbacks; // Find spot in list if ( zclHVACCBs == NULL ) { zclHVACCBs = pNewItem; } else { // Look for end of list pLoop = zclHVACCBs; while ( pLoop->next != NULL ) pLoop = pLoop->next; // Put new item at end of list pLoop->next = pNewItem; } return ( ZSuccess ); }
/********************************************************************* * @fn zclMS_RegisterCmdCallbacks * * @brief Register an applications command callbacks * * @param endpoint - application's endpoint * @param callbacks - pointer to the callback record. * * @return ZMemError if not able to allocate */ ZStatus_t zclMS_RegisterCmdCallbacks( uint8 endpoint, zclMS_AppCallbacks_t *callbacks ) { zclMSCBRec_t *pNewItem; zclMSCBRec_t *pLoop; // Register as a ZCL Plugin if ( !zclMSPluginRegisted ) { zcl_registerPlugin( ZCL_CLUSTER_ID_MS_ILLUMINANCE_MEASUREMENT, ZCL_CLUSTER_ID_MS_OCCUPANCY_SENSING, zclMS_HdlIncoming ); zclMSPluginRegisted = TRUE; } // Fill in the new profile list pNewItem = zcl_mem_alloc( sizeof( zclMSCBRec_t ) ); if ( pNewItem == NULL ) return (ZMemError); pNewItem->next = (zclMSCBRec_t *)NULL; pNewItem->endpoint = endpoint; pNewItem->CBs = callbacks; // Find spot in list if ( zclMSCBs == NULL ) { zclMSCBs = pNewItem; } else { // Look for end of list pLoop = zclMSCBs; while ( pLoop->next != NULL ) pLoop = pLoop->next; // Put new item at end of list pLoop->next = pNewItem; } return ( ZSuccess ); }
/********************************************************************* * @fn zclElectricalMeasurement_ProcessInCmd_GetMeasurementProfileRsp * * @brief Process in the received Electrical Measurement Get Measurement Profile Response cmd * * @param pInMsg - pointer to the incoming message * @param pCBs - pointer to the application callbacks * * @return ZStatus_t */ static ZStatus_t zclElectricalMeasurement_ProcessInCmd_GetMeasurementProfileRsp( zclIncoming_t *pInMsg, zclElectricalMeasurement_AppCallbacks_t *pCBs ) { uint8 i; uint8 offset; uint16 calculatedIntervalSize; zclElectricalMeasurementGetMeasurementProfileRsp_t cmd; ZStatus_t status; if ( pCBs->pfnElectricalMeasurement_GetMeasurementProfileRsp ) { // determine size of intervals by subtracting size of startTime, status, // profileIntervalPeriod, numberOfIntervalsDelivered, and attributeID from message length calculatedIntervalSize = pInMsg->pDataLen - 9; cmd.pIntervals = zcl_mem_alloc( calculatedIntervalSize ); if ( !cmd.pIntervals ) { return ( ZMemError ); // no memory, return failure } cmd.startTime = BUILD_UINT32( pInMsg->pData[0], pInMsg->pData[1], pInMsg->pData[2], pInMsg->pData[3] ); cmd.status = pInMsg->pData[4]; cmd.profileIntervalPeriod = pInMsg->pData[5]; cmd.numberOfIntervalsDelivered = pInMsg->pData[6]; cmd.attributeID = BUILD_UINT16( pInMsg->pData[7], pInMsg->pData[8] ); offset = 9; for ( i = 0; i < calculatedIntervalSize; i++ ) { cmd.pIntervals[i] = pInMsg->pData[offset++]; } status = ( pCBs->pfnElectricalMeasurement_GetMeasurementProfileRsp( &cmd ) ); zcl_mem_free( cmd.pIntervals ); return status; } return ( ZFailure ); }
/********************************************************************* * @fn zclApplianceStatistics_LogQueueRsp_OtaToNative * * @brief Converts from native to OTA format. Make sure to free pCmd->pLogID if ZSuccess. * * @param pCmd - native format (destination) * @param pZclPayloadLen - OTA ZCL payload (source) * * @return ZSuccess if worked */ ZStatus_t zclApplianceStatistics_LogQueueRsp_OtaToNative( zclCmdApplianceStatisticsLogQueueRspPayload_t *pCmd, uint8 *pData ) { uint8 offset; uint8 i; // convert from OTA (stream) format to native format pCmd->logQueueSize = pData[0]; pCmd->pLogID = zcl_mem_alloc( pCmd->logQueueSize * sizeof(uint32) ); if( !pCmd->pLogID ) { return ( ZMemError ); } offset = 1; for( i = 0; i < pCmd->logQueueSize; ++i ) { pCmd->pLogID[i] = BUILD_UINT32( pData[offset], pData[offset+1], pData[offset+2], pData[offset+3] ); offset += sizeof(uint32); } return ( ZSuccess ); }
/********************************************************************* * @fn zclElectricalMeasurement_ProcessInCmd_GetProfileInfoRsp * * @brief Process in the received Electrical Measurement Get Profile Info Response cmd * * @param pInMsg - pointer to the incoming message * @param pCBs - pointer to the application callbacks * * @return ZStatus_t */ static ZStatus_t zclElectricalMeasurement_ProcessInCmd_GetProfileInfoRsp( zclIncoming_t *pInMsg, zclElectricalMeasurement_AppCallbacks_t *pCBs ) { uint8 i; uint8 offset; uint16 calculatedArraySize; zclElectricalMeasurementGetProfileInfoRsp_t cmd; ZStatus_t status; if ( pCBs->pfnElectricalMeasurement_GetProfileInfoRsp ) { // calculate size of variable array calculatedArraySize = pInMsg->pDataLen - 3; // variable array - 3 bytes of fixed variables cmd.pListOfAttributes = zcl_mem_alloc( calculatedArraySize ); if ( !cmd.pListOfAttributes ) { return ( ZMemError ); // no memory, return failure } cmd.profileCount = pInMsg->pData[0]; cmd.profileIntervalPeriod = pInMsg->pData[1]; cmd.maxNumberOfIntervals = pInMsg->pData[2]; cmd.numberOfAttributes = calculatedArraySize / sizeof( uint16 ); offset = 3; for ( i = 0; i < cmd.numberOfAttributes; i++ ) { cmd.pListOfAttributes[i] = BUILD_UINT16( pInMsg->pData[offset], pInMsg->pData[offset + 1] ); offset += 2; } status = ( pCBs->pfnElectricalMeasurement_GetProfileInfoRsp( &cmd ) ); zcl_mem_free( cmd.pListOfAttributes ); return status; } return ( ZFailure ); }
/********************************************************************* * @fn zclElectricalMeasurement_Send_GetProfileInfoRsp * * @brief Call to send out Electrical Measurement Get Profile Info Response. This will tell * the client the appropriate parameters of the measurement profile. * * @param srcEP - Sending application's endpoint * @param dstAddr - where you want the message to go * @param pPayload: * profileCount - total number of supported profiles * profileIntervalPeriod - indicates the time frame of capture for profiling purposes * maxNumberOfIntervals - maximum number of intervals allowed for the response * pListOfAttributes - represents list of attributes being profiled * @param disableDefaultRsp - whether to disable the Default Response command * @param seqNum - sequence number * * @return ZStatus_t */ ZStatus_t zclElectricalMeasurement_Send_GetProfileInfoRsp( uint8 srcEP, afAddrType_t *dstAddr, zclElectricalMeasurementGetProfileInfoRsp_t *pPayload, uint8 disableDefaultRsp, uint8 seqNum ) { uint8 i; uint8 *pBuf; // variable length payload uint8 offset; uint16 calculatedBufSize; ZStatus_t status; // get a buffer large enough to hold the whole packet calculatedBufSize = ( 3 + ( pPayload->numberOfAttributes * sizeof( uint16 ) ) ); // size of fixed variables plus variable array pBuf = zcl_mem_alloc( calculatedBufSize ); if ( !pBuf ) { return ( ZMemError ); // no memory } pBuf[0] = pPayload->profileCount; pBuf[1] = pPayload->profileIntervalPeriod; pBuf[2] = pPayload->maxNumberOfIntervals; offset = 3; for ( i = 0; i < pPayload->numberOfAttributes; i++ ) { pBuf[offset++] = LO_UINT16(pPayload->pListOfAttributes[i]); pBuf[offset++] = HI_UINT16(pPayload->pListOfAttributes[i]); } status = zcl_SendCommand( srcEP, dstAddr, ZCL_CLUSTER_ID_HA_ELECTRICAL_MEASUREMENT, COMMAND_ELECTRICAL_MEASUREMENT_GET_PROFILE_INFO_RSP, TRUE, ZCL_FRAME_SERVER_CLIENT_DIR, disableDefaultRsp, 0, seqNum, calculatedBufSize, pBuf ); zcl_mem_free( pBuf ); return ( status ); }
/********************************************************************* * @fn zclPartition_ConvertOtaToNative_WriteHandshakeParam * * @brief Helper function used to process an incoming WriteHandshakeParam * command. Remember to free allocated memory. * * @param pCmd - (output) the converted command * @param buf - pointer to incoming frame (just after ZCL header) * @param buflen - length of buffer (ZCL payload) * * @return ZStatus_t - ZCL_STATUS_MALFORMED_COMMAND, ZMemError or ZCL_STATUS_SUCCESS */ ZStatus_t zclPartition_ConvertOtaToNative_WriteHandshakeParam( zclCmdWriteHandshakeParam_t *pCmd, uint8 *buf, uint8 buflen ) { uint8 offset; uint8 dataType; uint8 i; uint8 attrSize; // must have at least 1 attribute (clusterID + attrID) if ( buflen < 4 ) { return ( ZCL_STATUS_MALFORMED_COMMAND ); } pCmd->clusterID = BUILD_UINT16( buf[0], buf[1] ); // allocate memory for attribute list // [clusterID][WriteRecord1][WriteRecord2]... pCmd->numRecords = 0; offset = 2; while ( offset < buflen ) { dataType = buf[offset + 2]; // [AttrID][dataType][Attr] if ( dataType == ZCL_DATATYPE_UINT16 ) { attrSize = 2; } else if ( dataType == ZCL_DATATYPE_UINT8 ) { attrSize = 1; } else { return ( ZCL_STATUS_MALFORMED_COMMAND ); // can't convert invalid attributes } offset += 3 + attrSize; // ignore partial records if ( offset > buflen ) { break; } ++pCmd->numRecords; } // allocate the memory pCmd->pWriteRecord = zcl_mem_alloc( pCmd->numRecords * sizeof( zclPartitionWriteRec_t ) ); if ( !pCmd->pWriteRecord ) { return ( ZMemError ); // memory error } // convert records native offset = 2; for ( i = 0; i < pCmd->numRecords; ++i ) { pCmd->pWriteRecord[i].attrID = BUILD_UINT16( buf[offset], buf[offset+1] ); pCmd->pWriteRecord[i].dataType = dataType = buf[offset+2]; if ( dataType == ZCL_DATATYPE_UINT16 ) { pCmd->pWriteRecord[i].attr = BUILD_UINT16( buf[offset+3], buf[offset+4] ); attrSize = 2; } else if ( dataType == ZCL_DATATYPE_UINT8 ) { pCmd->pWriteRecord[i].attr = buf[offset+3]; attrSize = 1; } // move offset passed write record (2 bytes for attrID, 1 byte for AttrType + size of attr) offset += ( 3 + attrSize ); } return ( ZCL_STATUS_SUCCESS ); }
/********************************************************************* * @fn zclPartition_ConvertOtaToNative_ReadHandshakeParamRsp * * @brief Helper function used to process an incoming ReadHandshakeParamRsp * command. Remember to free allocated memory. * * @param pCmd - (output) the converted command * @param buf - pointer to incoming frame (just after ZCL header) * @param buflen - length of buffer (ZCL payload) * * @return ZStatus_t - ZCL_STATUS_MALFORMED_COMMAND, ZMemError or ZCL_STATUS_SUCCESS */ ZStatus_t zclPartition_ConvertOtaToNative_ReadHandshakeParamRsp( zclCmdReadHandshakeParamRsp_t *pCmd, uint8 *buf, uint8 buflen ) { uint8 status; uint8 dataType; uint8 offset; uint8 i; // must have at least 1 attribute (clusterID + attrID) if ( buflen < 4 ) { return ( ZCL_STATUS_MALFORMED_COMMAND ); } pCmd->clusterID = BUILD_UINT16( buf[0], buf[1] ); // determine # of attributes offset = 2; pCmd->numRecords = 0; while ( offset < buflen ) { // each record begins with [AttrID][Status] status = buf[offset+2]; // status listed in spec as 2 bytes, but it's really 1 byte offset += 3; // skip [AttrID][Status] // followed by attrtype and data if ( status == ZCL_STATUS_SUCCESS ) { // for partition cluster, all attributes are uint8 or uint16 dataType = buf[offset]; if ( dataType == ZCL_DATATYPE_UINT8 ) { offset += 2; } else if ( dataType == ZCL_DATATYPE_UINT16 ) { offset += 3; } else { break; // invalidate data type, we don't know what to do with it. } } ++pCmd->numRecords; } // allocate memory for attribute list pCmd->pReadRecord = zcl_mem_alloc( pCmd->numRecords * sizeof( zclPartitionReadRec_t ) ); if ( !pCmd->pReadRecord ) { return ( ZMemError ); // memory error } // convert attributes records to native offset = 2; for ( i = 0; i < pCmd->numRecords; ++i ) { pCmd->pReadRecord[i].attrID = BUILD_UINT16( buf[offset], buf[offset+1] ); pCmd->pReadRecord[i].status = buf[offset+2]; offset += 3; if ( status == ZCL_STATUS_SUCCESS ) { // for partition cluster, all attributes are uint8 or uint16 pCmd->pReadRecord[i].dataType = dataType = buf[offset]; if ( dataType == ZCL_DATATYPE_UINT8 ) { pCmd->pReadRecord[i].attr = buf[offset+1]; offset += 2; } else { pCmd->pReadRecord[i].attr = BUILD_UINT16( buf[offset+1], buf[offset+2] ); offset += 3; } } } return ( ZCL_STATUS_SUCCESS ); }
/********************************************************************* * @fn zclHVAC_SendGetWeeklyScheduleRsp * * @brief Call to send out a Get Weekly Schedule Response Command * * @param srcEP - Sending application's endpoint * @param dstAddr - where you want the message to go * @param pPayload - payload for Get Weekly Schedule rsp * @param seqNum - transaction sequence number * * @return ZStatus_t */ ZStatus_t zclHVAC_SendGetWeeklyScheduleRsp( uint8 srcEP, afAddrType_t *dstAddr, zclThermostatWeeklySchedule_t *pPayload, uint8 disableDefaultRsp, uint8 seqNum ) { uint8 i; uint8 modeBuff; // buffer to determine how to incorporate heat/cool set points uint8 *pBuf; // variable length payload uint8 offset; uint16 arrayRecordSize; // number of bytes in record array uint16 calculatedBufSize; ZStatus_t status; modeBuff = pPayload->modeForSequence; // store the modeForSequence in buffer // if either the heat or cool set points if( ( modeBuff == HVAC_THERMOSTAT_MODE_HEAT ) || ( modeBuff == HVAC_THERMOSTAT_MODE_COOL ) ) { // calculate the number of transitions multiplied by size of transitionTime and heatSetPoint/coolSetPoint arrayRecordSize = ( pPayload->numberOfTransitionsForSequence * PAYLOAD_LEN_WEEKLY_SCHEDULE_COOL_HEAT_MODE ); } // if both cool and heat set points else if( modeBuff == HVAC_THERMOSTAT_MODE_BOTH ) { // calculate the number of transitions multiplied by size of transitionTime, heatSetPoint, and coolSetPoint arrayRecordSize = ( pPayload->numberOfTransitionsForSequence * PAYLOAD_LEN_WEEKLY_SCHEDULE_BOTH_MODES ); } else { return ( ZFailure ); // invalid data } // get a buffer large enough to hold the whole packet calculatedBufSize = ( PAYLOAD_LEN_WEEKLY_SCHEDULE + arrayRecordSize ); pBuf = zcl_mem_alloc( calculatedBufSize ); if( !pBuf ) { return ( ZFailure ); // no memory } // over-the-air is always little endian. Break into a byte stream. pBuf[0] = pPayload->numberOfTransitionsForSequence; pBuf[1] = pPayload->dayOfWeekForSequence; pBuf[2] = pPayload->modeForSequence; offset = 3; for( i = 0; i < ( pPayload->numberOfTransitionsForSequence ); i++ ) { if( modeBuff == HVAC_THERMOSTAT_MODE_HEAT ) // heat set point { pBuf[offset++] = LO_UINT16( pPayload->sThermostateSequenceMode.psThermostatModeHeat[i].transitionTime ); pBuf[offset++] = HI_UINT16( pPayload->sThermostateSequenceMode.psThermostatModeHeat[i].transitionTime ); pBuf[offset++] = LO_UINT16( pPayload->sThermostateSequenceMode.psThermostatModeHeat[i].heatSetPoint ); pBuf[offset++] = HI_UINT16( pPayload->sThermostateSequenceMode.psThermostatModeHeat[i].heatSetPoint ); } else if( modeBuff == HVAC_THERMOSTAT_MODE_COOL ) // cool set point { pBuf[offset++] = LO_UINT16( pPayload->sThermostateSequenceMode.psThermostatModeCool[i].transitionTime ); pBuf[offset++] = HI_UINT16( pPayload->sThermostateSequenceMode.psThermostatModeCool[i].transitionTime ); pBuf[offset++] = LO_UINT16( pPayload->sThermostateSequenceMode.psThermostatModeCool[i].coolSetPoint ); pBuf[offset++] = HI_UINT16( pPayload->sThermostateSequenceMode.psThermostatModeCool[i].coolSetPoint ); } else if( modeBuff == HVAC_THERMOSTAT_MODE_BOTH ) // both cool and heat set points { pBuf[offset++] = LO_UINT16( pPayload->sThermostateSequenceMode.psThermostatModeBoth[i].transitionTime ); pBuf[offset++] = HI_UINT16( pPayload->sThermostateSequenceMode.psThermostatModeBoth[i].transitionTime ); pBuf[offset++] = LO_UINT16( pPayload->sThermostateSequenceMode.psThermostatModeBoth[i].heatSetPoint ); pBuf[offset++] = HI_UINT16( pPayload->sThermostateSequenceMode.psThermostatModeBoth[i].heatSetPoint ); pBuf[offset++] = LO_UINT16( pPayload->sThermostateSequenceMode.psThermostatModeBoth[i].coolSetPoint ); pBuf[offset++] = HI_UINT16( pPayload->sThermostateSequenceMode.psThermostatModeBoth[i].coolSetPoint ); } else { return ( ZFailure ); // unsupported mode } } status = zcl_SendCommand( srcEP, dstAddr, ZCL_CLUSTER_ID_HVAC_THERMOSTAT, COMMAND_THERMOSTAT_GET_WEEKLY_SCHEDULE_RSP, TRUE, ZCL_FRAME_SERVER_CLIENT_DIR, disableDefaultRsp, 0, seqNum, calculatedBufSize, pBuf ); zcl_mem_free( pBuf ); return status; }
/********************************************************************* * @fn zclThermostat_ProcessInCmd_GetWeeklyScheduleRsp * * @brief Process in the received Get Weekly Schedule Response cmd * * @param pInMsg - pointer to the incoming message * @param pCBs - pointer to the application callbacks * * @return ZStatus_t */ static ZStatus_t zclThermostat_ProcessInCmd_GetWeeklyScheduleRsp( zclIncoming_t *pInMsg, zclHVAC_AppCallbacks_t *pCBs ) { uint8 i; uint8 offset; uint8 modeBuff; // buffer to determine how to incorporate heat/cool set points uint8 numTransitions; // buffer for number of transitions for sequence uint16 arrayRecordSize; zclThermostatWeeklySchedule_t cmd; ZStatus_t status; if ( pCBs->pfnHVAC_GetWeeklyScheduleRsp ) { numTransitions = pInMsg->pData[0]; modeBuff = pInMsg->pData[2]; // store the modeForSequence in buffer // if either the heat or cool set points if( ( modeBuff == HVAC_THERMOSTAT_MODE_HEAT ) || ( modeBuff == HVAC_THERMOSTAT_MODE_COOL ) ) { // calculate the number of transitions multiplied by size of transitionTime and heatSetPoint/coolSetPoint arrayRecordSize = ( numTransitions * 4 ); } // if both cool and heat set points else if( modeBuff == HVAC_THERMOSTAT_MODE_BOTH ) { // calculate the number of transitions multiplied by size of transitionTime, heatSetPoint, and coolSetPoint arrayRecordSize = ( numTransitions * 6 ); } else { return ( ZFailure ); // invalid mode field } // allocate memory cmd.sThermostateSequenceMode.psThermostatModeBoth = zcl_mem_alloc( arrayRecordSize ); if( !cmd.sThermostateSequenceMode.psThermostatModeBoth ) { return ( ZMemError ); // no memory } cmd.numberOfTransitionsForSequence = numTransitions; cmd.dayOfWeekForSequence = pInMsg->pData[1]; cmd.modeForSequence = modeBuff; offset = 3; for( i = 0; i < numTransitions; i++ ) { if( modeBuff == HVAC_THERMOSTAT_MODE_HEAT ) // heat set point { cmd.sThermostateSequenceMode.psThermostatModeHeat[i].transitionTime = BUILD_UINT16( pInMsg->pData[offset], pInMsg->pData[offset + 1] ); cmd.sThermostateSequenceMode.psThermostatModeHeat[i].heatSetPoint = BUILD_UINT16( pInMsg->pData[offset + 2], pInMsg->pData[offset + 3] ); offset += 4; } else if( modeBuff == HVAC_THERMOSTAT_MODE_COOL ) // cool set point { cmd.sThermostateSequenceMode.psThermostatModeCool[i].transitionTime = BUILD_UINT16( pInMsg->pData[offset], pInMsg->pData[offset + 1] ); cmd.sThermostateSequenceMode.psThermostatModeCool[i].coolSetPoint = BUILD_UINT16( pInMsg->pData[offset + 2], pInMsg->pData[offset + 3] ); offset += 4; } else if( modeBuff == HVAC_THERMOSTAT_MODE_BOTH ) // both cool and heat set points { cmd.sThermostateSequenceMode.psThermostatModeBoth[i].transitionTime = BUILD_UINT16( pInMsg->pData[offset], pInMsg->pData[offset + 1] ); cmd.sThermostateSequenceMode.psThermostatModeBoth[i].heatSetPoint = BUILD_UINT16( pInMsg->pData[offset + 2], pInMsg->pData[offset + 3] ); cmd.sThermostateSequenceMode.psThermostatModeBoth[i].coolSetPoint = BUILD_UINT16( pInMsg->pData[offset + 4], pInMsg->pData[offset + 5] ); offset += 6; } else { return ( ZFailure ); // unsupported mode } } status = ( pCBs->pfnHVAC_GetWeeklyScheduleRsp( &cmd ) ); zcl_mem_free( cmd.sThermostateSequenceMode.psThermostatModeBoth ); return status; } return ( ZFailure ); }