/**************************************************************************************************
  Filename:       zcl_easenlock.c
  Revised:        $Date: 2017-11-28 16:09:46 -0700 (Tue, 28 Nov 2018) $
  Author:         873


  Description:    Zigbee Cluster Library - EasenLock device application.


  Copyright 2016-2017 ZhuHai Easen. All rights reserved.

  This application is a template to get started writing an application
  from scratch.

  Look for the sections marked with "EASENLOCK_TODO" to add application
  specific code.

  Note: if you would like your application to support automatic attribute
  reporting, include the BDB_REPORTING compile flag.
*********************************************************************/

/*********************************************************************
 * INCLUDES
 */

#include <stdio.h>

#include "ZComDef.h"
#include "OSAL.h"
#include "AF.h"
#include "ZDApp.h"
#include "ZDObject.h"
#include "MT_SYS.h"

#include "nwk_util.h"

#include "zcl.h"
#include "zcl_general.h"
#include "zcl_ha.h"
#include "zcl_diagnostic.h"

#include "EasenLock.h"

#include "bdb.h"
#include "bdb_interface.h"
#include "gp_interface.h"

#if defined ( INTER_PAN )
#if defined ( BDB_TL_INITIATOR )
  #include "bdb_touchlink_initiator.h"
#endif // BDB_TL_INITIATOR
#if defined ( BDB_TL_TARGET )
  #include "bdb_touchlink_target.h"
#endif // BDB_TL_TARGET
#endif // INTER_PAN

#if defined ( BDB_TL_INITIATOR ) || defined ( BDB_TL_TARGET )
  #include "bdb_touchlink.h"
#endif

#include "onboard.h"

/* HAL */
#include "hal_lcd.h"
#include "hal_led.h"
#include "hal_key.h"
#include "hal_uart.h"

/*********************************************************************
 * MACROS
 */


/*********************************************************************
 * CONSTANTS
 */


/*********************************************************************
 * TYPEDEFS
 */

/*********************************************************************
 * GLOBAL VARIABLES
 */
byte EasenLock_TaskID;


/*********************************************************************
 * GLOBAL FUNCTIONS
 */
 
/*********************************************************************
 * LOCAL VARIABLES
 */

uint8 gPermitDuration = 0;    // permit joining default to disabled

devStates_t EasenLock_NwkState = DEV_INIT;
uint8 EasenLock_TransID;  // This is the unique message ID (counter)
afAddrType_t EasenLock_Periodic_DstAddr; //default message's destination address
/*********************************************************************
 * LOCAL FUNCTIONS
 */
static void EasenLock_HandleKeys( byte shift, byte keys );
static void EasenLock_BasicResetCB( void );
static void EasenLock_ProcessIdentifyTimeChange( uint8 endpoint );
static void EasenLock_BindNotification( bdbBindNotificationData_t *data );
#if ( defined ( BDB_TL_TARGET ) && (BDB_TOUCHLINK_CAPABILITY_ENABLED == TRUE) )
static void EasenLock_ProcessTouchlinkTargetEnable( uint8 enable );
#endif

static void EasenLock_ProcessCommissioningStatus(bdbCommissioningModeMsg_t *bdbCommissioningModeMsg);

// Functions to process ZCL Foundation incoming Command/Response messages
static void zclEasenLock_ProcessIncomingMsg( zclIncomingMsg_t *msg );
#ifdef ZCL_READ
static uint8 zclEasenLock_ProcessInReadRspCmd( zclIncomingMsg_t *pInMsg );
#endif
#ifdef ZCL_WRITE
static uint8 zclEasenLock_ProcessInWriteRspCmd( zclIncomingMsg_t *pInMsg );
#endif
static uint8 zclEasenLock_ProcessInDefaultRspCmd( zclIncomingMsg_t *pInMsg );
#ifdef ZCL_DISCOVER
static uint8 zclEasenLock_ProcessInDiscCmdsRspCmd( zclIncomingMsg_t *pInMsg );
static uint8 zclEasenLock_ProcessInDiscAttrsRspCmd( zclIncomingMsg_t *pInMsg );
static uint8 zclEasenLock_ProcessInDiscAttrsExtRspCmd( zclIncomingMsg_t *pInMsg );
#endif


/****************************************************/

// This list should be filled with Application specific Cluster IDs.
const cId_t EasenLock_ClusterList[EasenLock_MAX_CLUSTERS] =
{
  EasenLock_PERIODIC_CLUSTERID, // Default Cluster ID
  EasenLock_FLASH_CLUSTERID     // Not use
};

const SimpleDescriptionFormat_t EasenLock_SimpleDesc =
{
  EasenLock_ENDPOINT,              //  int Endpoint;
  EasenLock_PROFID,                //  uint16 AppProfId[2];
  EasenLock_DEVICEID,              //  uint16 AppDeviceId[2];
  EasenLock_DEVICE_VERSION,        //  int   AppDevVer:4;
  EasenLock_FLAGS,                 //  int   AppFlags:4;
  EasenLock_MAX_CLUSTERS,          //  uint8  AppNumInClusters;
  (cId_t *)EasenLock_ClusterList,  //  uint8 *pAppInClusterList;
  EasenLock_MAX_CLUSTERS,          //  uint8  AppNumInClusters;
  (cId_t *)EasenLock_ClusterList   //  uint8 *pAppInClusterList;
};

// This is the Endpoint/Interface description.  It is defined here, but
// filled-in in EasenLock_Init().  Another way to go would be to fill
// in the structure here and make it a "const" (in code space).  The
// way it's defined in this sample app it is define in RAM.
endPointDesc_t EasenLock_epDesc;




/**************************
  User Function Declare
**************************/

static uint8 SendToEndDevice(uint16 shortAddr);
static uint8 SendToCoord(void);

/*********************************************************************
 * STATUS STRINGS
 */
/*********************************************************************
 * ZCL General Profile Callback table
 */
static zclGeneral_AppCallbacks_t zclEasenLock_CmdCallbacks =
{
  EasenLock_BasicResetCB,                 // Basic Cluster Reset command
  NULL,                                   // Identify Trigger Effect command
  NULL,                                   // On/Off cluster commands
  NULL,                                   // On/Off cluster enhanced command Off with Effect
  NULL,                                   // On/Off cluster enhanced command On with Recall Global Scene
  NULL,                                   // On/Off cluster enhanced command On with Timed Off
#ifdef ZCL_LEVEL_CTRL
  NULL,                                   // Level Control Move to Level command
  NULL,                                   // Level Control Move command
  NULL,                                   // Level Control Step command
  NULL,                                   // Level Control Stop command
#endif
#ifdef ZCL_GROUPS
  NULL,                                   // Group Response commands
#endif
#ifdef ZCL_SCENES
  NULL,                                  // Scene Store Request command
  NULL,                                  // Scene Recall Request command
  NULL,                                  // Scene Response command
#endif
#ifdef ZCL_ALARMS
  NULL,                                  // Alarm (Response) commands
#endif
#ifdef SE_UK_EXT
  NULL,                                  // Get Event Log command
  NULL,                                  // Publish Event Log command
#endif
  NULL,                                  // RSSI Location command
  NULL                                   // RSSI Location Response command
};

/*********************************************************************
 * EASENLOCK_TODO: Add other callback structures for any additional application specific 
 *       Clusters being used, see available callback structures below.
 *
 *       bdbTL_AppCallbacks_t 
 *       zclApplianceControl_AppCallbacks_t 
 *       zclApplianceEventsAlerts_AppCallbacks_t 
 *       zclApplianceStatistics_AppCallbacks_t 
 *       zclElectricalMeasurement_AppCallbacks_t 
 *       zclGeneral_AppCallbacks_t 
 *       zclGp_AppCallbacks_t 
 *       zclHVAC_AppCallbacks_t 
 *       zclLighting_AppCallbacks_t 
 *       zclMS_AppCallbacks_t 
 *       zclPollControl_AppCallbacks_t 
 *       zclPowerProfile_AppCallbacks_t 
 *       zclSS_AppCallbacks_t  
 *
 */

/*********************************************************************
 * @fn          EasenLock_Init
 *
 * @brief       Initialization function for the zclGeneral layer.
 *
 * @param       none
 *
 * @return      none
 */
void EasenLock_Init( byte task_id )
{
  EasenLock_TaskID = task_id;

  // This app is part of the Home Automation Profile
  bdb_RegisterSimpleDescriptor( &zclEasenLock_SimpleDesc );

  // Register the ZCL General Cluster Library callback functions
  zclGeneral_RegisterCmdCallbacks( EasenLock_ENDPOINT, &zclEasenLock_CmdCallbacks );
  
  // EASENLOCK_TODO: Register other cluster command callbacks here

  // Register the application's attribute list
  zcl_registerAttrList( EasenLock_ENDPOINT, zclEasenLock_NumAttributes, zclEasenLock_Attrs );

  // Register the Application to receive the unprocessed Foundation command/response messages
  zcl_registerForMsg( EasenLock_TaskID );

#ifdef ZCL_DISCOVER
  // Register the application's command list
  zcl_registerCmdList( EasenLock_ENDPOINT, zclCmdsArraySize, zclEasenLock_Cmds );
#endif

  // Register for all key events - This app will handle all key events
  RegisterForKeys( EasenLock_TaskID );

  bdb_RegisterCommissioningStatusCB( EasenLock_ProcessCommissioningStatus );
  bdb_RegisterIdentifyTimeChangeCB( EasenLock_ProcessIdentifyTimeChange );
  bdb_RegisterBindNotificationCB( EasenLock_BindNotification );

#if ( defined ( BDB_TL_TARGET ) && (BDB_TOUCHLINK_CAPABILITY_ENABLED == TRUE) )
  bdb_RegisterTouchlinkTargetEnableCB( EasenLock_ProcessTouchlinkTargetEnable );
#endif

#ifdef ZCL_DIAGNOSTIC
  // Register the application's callback function to read/write attribute data.
  // This is only required when the attribute data format is unknown to ZCL.
  zcl_registerReadWriteCB( EASENLOCK_ENDPOINT, zclDiagnostic_ReadWriteAttrCB, NULL );

  if ( zclDiagnostic_InitStats() == ZSuccess )
  {
    // Here the user could start the timer to save Diagnostics to NV
  }
#endif
  
  EasenLock_Periodic_DstAddr.addrMode = (afAddrMode_t)AddrBroadcast;
  EasenLock_Periodic_DstAddr.endPoint = EasenLock_ENDPOINT;
  EasenLock_Periodic_DstAddr.addr.shortAddr = 0xFFFF;
 
  // Fill out the endpoint description.
  EasenLock_epDesc.endPoint = EasenLock_ENDPOINT;
  EasenLock_epDesc.task_id = &EasenLock_TaskID;
  EasenLock_epDesc.simpleDesc
            = (SimpleDescriptionFormat_t *)&EasenLock_SimpleDesc;
  EasenLock_epDesc.latencyReq = noLatencyReqs;

  // Register the endpoint description with the AF
  afRegister( &EasenLock_epDesc );

  // Register for all key events - This app will handle all key events
  RegisterForKeys( EasenLock_TaskID );
  
  
  printf("Starting...\n");

  bdb_StartCommissioning(BDB_COMMISSIONING_MODE_NWK_STEERING );

}

/*********************************************************************
 * @fn          EasenLock_event_loop
 *
 * @brief       Event Loop Processor for EasenLock.
 *
 * @param       none
 *
 * @return      none
 */
uint16 EasenLock_event_loop( uint8 task_id, uint16 events )
{
  afIncomingMSGPacket_t *MSGpkt;

  (void)task_id;  // Intentionally unreferenced parameter

  if ( events & SYS_EVENT_MSG )
  {
    while ( (MSGpkt = (afIncomingMSGPacket_t *)osal_msg_receive( EasenLock_TaskID )) )
    {
       printf("Receive SYSTEM EVETS = 0x%x\n", MSGpkt->hdr.event);
      switch ( MSGpkt->hdr.event )
      {
        case ZCL_INCOMING_MSG:
          // Incoming ZCL Foundation command/response messages
          printf("ZCL Receive Message\n");
          zclEasenLock_ProcessIncomingMsg( (zclIncomingMsg_t *)MSGpkt );
          break;
          
        // Received when a messages is received (OTA) for this endpoint
        case AF_INCOMING_MSG_CMD:
          SendToCoord();
          printf("Receive Message\n");
          break;
          
        case AF_DATA_CONFIRM_CMD: 
          break;
          
        case KEY_CHANGE:
          EasenLock_HandleKeys( ((keyChange_t *)MSGpkt)->state, ((keyChange_t *)MSGpkt)->keys );
          break;

        case ZDO_STATE_CHANGE:
          EasenLock_NwkState = (devStates_t)(MSGpkt->hdr.status);
          printf("ZDO_STATE_CHANGE = %d\n",EasenLock_NwkState);
          // now on the network
          if ( (EasenLock_NwkState == DEV_ZB_COORD) ||
               (EasenLock_NwkState == DEV_ROUTER)   ||
               (EasenLock_NwkState == DEV_END_DEVICE) )
          {

          }
          break;

        default:
          break;
      }

      // Release the memory
      osal_msg_deallocate( (uint8 *)MSGpkt );
    }

    // return unprocessed events
    return (events ^ SYS_EVENT_MSG);
  }

  if ( events & EASENLOCK_MAIN_SCREEN_EVT )
  {


    return ( events ^ EASENLOCK_MAIN_SCREEN_EVT );
  }
  
#if ZG_BUILD_ENDDEVICE_TYPE    
  if ( events & EASENLOCK_END_DEVICE_REJOIN_EVT )
  {
    bdb_ZedAttemptRecoverNwk();
    return ( events ^ EASENLOCK_END_DEVICE_REJOIN_EVT );
  }
#endif

  /* EASENLOCK_TODO: handle app events here */
  
  
//  if ( events & EASENLOCK_EVT_1 )
//  {
//    // toggle LED 2 state, start another timer for 500ms
//    HalLedSet ( HAL_LED_2, HAL_LED_MODE_TOGGLE );
//    osal_start_timerEx( EasenLock_TaskID, EASENLOCK_EVT_1, 500 );
//    
//    return ( events ^ EASENLOCK_EVT_1 );
//  }
  
  if ( events & EASENLOCK_START_BDB_COMMISSIONING )
  {
   
    bdb_StartCommissioning( BDB_COMMISSIONING_MODE_NWK_FORMATION | 
                            BDB_COMMISSIONING_MODE_NWK_STEERING | 
                            BDB_COMMISSIONING_MODE_FINDING_BINDING | 
                            BDB_COMMISSIONING_MODE_INITIATOR_TL );
    
    return ( events ^ EASENLOCK_START_BDB_COMMISSIONING );
  }
  
  // Discard unknown events
  return 0;
}


/*********************************************************************
 * @fn      EasenLock_HandleKeys
 *
 * @brief   Handles all key events for this device.
 *
 * @param   shift - true if in shift/alt.
 * @param   keys - bit field for key events. Valid entries:
 *                 HAL_KEY_SW_5
 *                 HAL_KEY_SW_4
 *                 HAL_KEY_SW_2
 *                 HAL_KEY_SW_1
 *
 * @return  none
 */
static void EasenLock_HandleKeys( byte shift, byte keys )
{
  
  if ( keys & HAL_KEY_SW_1 )
  {
    printf("KEY PRESS 1\n");
#if (ZDO_COORDINATOR)    
    SendToEndDevice(0);
#else
    SendToCoord();
#endif    
  }
  // Start the BDB commissioning method
  if ( keys & HAL_KEY_SW_2 )
  {
    bdb_StartCommissioning(BDB_COMMISSIONING_MODE_NWK_FORMATION | BDB_COMMISSIONING_MODE_NWK_STEERING | BDB_COMMISSIONING_MODE_FINDING_BINDING | BDB_COMMISSIONING_MODE_INITIATOR_TL);
  }
  if ( keys & HAL_KEY_SW_3 )
  {
    // touchlink target commissioning, if enabled  
#if ( defined ( BDB_TL_TARGET ) && (BDB_TOUCHLINK_CAPABILITY_ENABLED == TRUE) )
    bdb_StartCommissioning(BDB_COMMISSIONING_MODE_FINDING_BINDING);
    touchLinkTarget_EnableCommissioning( 30000 );
#endif
    
  }
  if ( keys & HAL_KEY_SW_4 )
  {
    
  }
  if ( keys & HAL_KEY_SW_5 )
  {
    bdb_resetLocalAction();
  }

}
/*********************************************************************
 * @fn      EasenLock_ProcessCommissioningStatus
 *
 * @brief   Callback in which the status of the commissioning process are reported
 *
 * @param   bdbCommissioningModeMsg - Context message of the status of a commissioning process
 *
 * @return  none
 */
static void EasenLock_ProcessCommissioningStatus(bdbCommissioningModeMsg_t *bdbCommissioningModeMsg)
{
  switch(bdbCommissioningModeMsg->bdbCommissioningMode)
  {
    case BDB_COMMISSIONING_FORMATION:
      if(bdbCommissioningModeMsg->bdbCommissioningStatus == BDB_COMMISSIONING_SUCCESS)
      {
        //After formation, perform nwk steering again plus the remaining commissioning modes that has not been process yet
        bdb_StartCommissioning(BDB_COMMISSIONING_MODE_NWK_STEERING | bdbCommissioningModeMsg->bdbRemainingCommissioningModes);
      }
      else
      {
        //Want to try other channels?
        //try with bdb_setChannelAttribute
      }
    break;
    case BDB_COMMISSIONING_NWK_STEERING:
      if(bdbCommissioningModeMsg->bdbCommissioningStatus == BDB_COMMISSIONING_SUCCESS)
      {
        //YOUR JOB:
        //We are on the nwk, what now?
      }
      else
      {
        //See the possible errors for nwk steering procedure
        //No suitable networks found
        //Want to try other channels?
        //try with bdb_setChannelAttribute
        osal_start_timerEx( EasenLock_TaskID, EASENLOCK_START_BDB_COMMISSIONING, 5000 );
      }
    break;
    case BDB_COMMISSIONING_FINDING_BINDING:
      if(bdbCommissioningModeMsg->bdbCommissioningStatus == BDB_COMMISSIONING_SUCCESS)
      {
        //YOUR JOB:
      }
      else
      {
        //YOUR JOB:
        //retry?, wait for user interaction?
      }
    break;
    case BDB_COMMISSIONING_INITIALIZATION:
      //Initialization notification can only be successful. Failure on initialization
      //only happens for ZED and is notified as BDB_COMMISSIONING_PARENT_LOST notification

      //YOUR JOB:
      //We are on a network, what now?

    break;
#if ZG_BUILD_ENDDEVICE_TYPE    
    case BDB_COMMISSIONING_PARENT_LOST:
      if(bdbCommissioningModeMsg->bdbCommissioningStatus == BDB_COMMISSIONING_NETWORK_RESTORED)
      {
        //We did recover from losing parent
      }
      else
      {
        //Parent not found, attempt to rejoin again after a fixed delay
        osal_start_timerEx(EasenLock_TaskID, EASENLOCK_END_DEVICE_REJOIN_EVT, EASENLOCK_END_DEVICE_REJOIN_DELAY);
      }
    break;
#endif 
  }
}

/*********************************************************************
 * @fn      EasenLock_ProcessIdentifyTimeChange
 *
 * @brief   Called to process any change to the IdentifyTime attribute.
 *
 * @param   endpoint - in which the identify has change
 *
 * @return  none
 */
static void EasenLock_ProcessIdentifyTimeChange( uint8 endpoint )
{
  (void) endpoint;

  if ( zclEasenLock_IdentifyTime > 0 )
  {
    HalLedBlink ( HAL_LED_2, 0xFF, HAL_LED_DEFAULT_DUTY_CYCLE, HAL_LED_DEFAULT_FLASH_TIME );
  }
  else
  {
    HalLedSet ( HAL_LED_2, HAL_LED_MODE_OFF );
  }
}

/*********************************************************************
 * @fn      EasenLock_BindNotification
 *
 * @brief   Called when a new bind is added.
 *
 * @param   data - pointer to new bind data
 *
 * @return  none
 */
static void EasenLock_BindNotification( bdbBindNotificationData_t *data )
{
  // EASENLOCK_TODO: process the new bind information
}


/*********************************************************************
 * @fn      EasenLock_ProcessTouchlinkTargetEnable
 *
 * @brief   Called to process when the touchlink target functionality
 *          is enabled or disabled
 *
 * @param   none
 *
 * @return  none
 */
#if ( defined ( BDB_TL_TARGET ) && (BDB_TOUCHLINK_CAPABILITY_ENABLED == TRUE) )
static void EasenLock_ProcessTouchlinkTargetEnable( uint8 enable )
{
  if ( enable )
  {
    HalLedSet ( HAL_LED_1, HAL_LED_MODE_ON );
  }
  else
  {
    HalLedSet ( HAL_LED_1, HAL_LED_MODE_OFF );
  }
}
#endif

/*********************************************************************
 * @fn      EasenLock_BasicResetCB
 *
 * @brief   Callback from the ZCL General Cluster Library
 *          to set all the Basic Cluster attributes to default values.
 *
 * @param   none
 *
 * @return  none
 */
static void EasenLock_BasicResetCB( void )
{

  /* EASENLOCK_TODO: remember to update this function with any
     application-specific cluster attribute variables */
  
  EasenLock_ResetAttributesToDefaultValues();
  
}
/******************************************************************************
 *
 *  Functions for processing ZCL Foundation incoming Command/Response messages
 *
 *****************************************************************************/

/*********************************************************************
 * @fn      zclEasenLock_ProcessIncomingMsg
 *
 * @brief   Process ZCL Foundation incoming message
 *
 * @param   pInMsg - pointer to the received message
 *
 * @return  none
 */
static void zclEasenLock_ProcessIncomingMsg( zclIncomingMsg_t *pInMsg )
{
  switch ( pInMsg->zclHdr.commandID )
  {
#ifdef ZCL_READ
    case ZCL_CMD_READ_RSP:
      zclEasenLock_ProcessInReadRspCmd( pInMsg );
      break;
#endif
#ifdef ZCL_WRITE
    case ZCL_CMD_WRITE_RSP:
      zclEasenLock_ProcessInWriteRspCmd( pInMsg );
      break;
#endif
    case ZCL_CMD_CONFIG_REPORT:
    case ZCL_CMD_CONFIG_REPORT_RSP:
    case ZCL_CMD_READ_REPORT_CFG:
    case ZCL_CMD_READ_REPORT_CFG_RSP:
    case ZCL_CMD_REPORT:
      //bdb_ProcessIncomingReportingMsg( pInMsg );
      break;
      
    case ZCL_CMD_DEFAULT_RSP:
      zclEasenLock_ProcessInDefaultRspCmd( pInMsg );
      break;
#ifdef ZCL_DISCOVER
    case ZCL_CMD_DISCOVER_CMDS_RECEIVED_RSP:
      zclEasenLock_ProcessInDiscCmdsRspCmd( pInMsg );
      break;

    case ZCL_CMD_DISCOVER_CMDS_GEN_RSP:
      zclEasenLock_ProcessInDiscCmdsRspCmd( pInMsg );
      break;

    case ZCL_CMD_DISCOVER_ATTRS_RSP:
      zclEasenLock_ProcessInDiscAttrsRspCmd( pInMsg );
      break;

    case ZCL_CMD_DISCOVER_ATTRS_EXT_RSP:
      zclEasenLock_ProcessInDiscAttrsExtRspCmd( pInMsg );
      break;
#endif
    default:
      break;
  }

  if ( pInMsg->attrCmd )
    osal_mem_free( pInMsg->attrCmd );
}

#ifdef ZCL_READ
/*********************************************************************
 * @fn      zclEasenLock_ProcessInReadRspCmd
 *
 * @brief   Process the "Profile" Read Response Command
 *
 * @param   pInMsg - incoming message to process
 *
 * @return  none
 */
static uint8 zclEasenLock_ProcessInReadRspCmd( zclIncomingMsg_t *pInMsg )
{
  zclReadRspCmd_t *readRspCmd;
  uint8 i;

  readRspCmd = (zclReadRspCmd_t *)pInMsg->attrCmd;
  for (i = 0; i < readRspCmd->numAttr; i++)
  {
    // Notify the originator of the results of the original read attributes
    // attempt and, for each successfull request, the value of the requested
    // attribute
  }

  return ( TRUE );
}
#endif // ZCL_READ

#ifdef ZCL_WRITE
/*********************************************************************
 * @fn      zclEasenLock_ProcessInWriteRspCmd
 *
 * @brief   Process the "Profile" Write Response Command
 *
 * @param   pInMsg - incoming message to process
 *
 * @return  none
 */
static uint8 zclEasenLock_ProcessInWriteRspCmd( zclIncomingMsg_t *pInMsg )
{
  zclWriteRspCmd_t *writeRspCmd;
  uint8 i;

  writeRspCmd = (zclWriteRspCmd_t *)pInMsg->attrCmd;
  for ( i = 0; i < writeRspCmd->numAttr; i++ )
  {
    // Notify the device of the results of the its original write attributes
    // command.
  }

  return ( TRUE );
}
#endif // ZCL_WRITE

/*********************************************************************
 * @fn      zclEasenLock_ProcessInDefaultRspCmd
 *
 * @brief   Process the "Profile" Default Response Command
 *
 * @param   pInMsg - incoming message to process
 *
 * @return  none
 */
static uint8 zclEasenLock_ProcessInDefaultRspCmd( zclIncomingMsg_t *pInMsg )
{
  // zclDefaultRspCmd_t *defaultRspCmd = (zclDefaultRspCmd_t *)pInMsg->attrCmd;

  // Device is notified of the Default Response command.
  (void)pInMsg;

  return ( TRUE );
}

#ifdef ZCL_DISCOVER
/*********************************************************************
 * @fn      zclEasenLock_ProcessInDiscCmdsRspCmd
 *
 * @brief   Process the Discover Commands Response Command
 *
 * @param   pInMsg - incoming message to process
 *
 * @return  none
 */
static uint8 zclEasenLock_ProcessInDiscCmdsRspCmd( zclIncomingMsg_t *pInMsg )
{
  zclDiscoverCmdsCmdRsp_t *discoverRspCmd;
  uint8 i;

  discoverRspCmd = (zclDiscoverCmdsCmdRsp_t *)pInMsg->attrCmd;
  for ( i = 0; i < discoverRspCmd->numCmd; i++ )
  {
    // Device is notified of the result of its attribute discovery command.
  }

  return ( TRUE );
}

/*********************************************************************
 * @fn      zclEasenLock_ProcessInDiscAttrsRspCmd
 *
 * @brief   Process the "Profile" Discover Attributes Response Command
 *
 * @param   pInMsg - incoming message to process
 *
 * @return  none
 */
static uint8 zclEasenLock_ProcessInDiscAttrsRspCmd( zclIncomingMsg_t *pInMsg )
{
  zclDiscoverAttrsRspCmd_t *discoverRspCmd;
  uint8 i;

  discoverRspCmd = (zclDiscoverAttrsRspCmd_t *)pInMsg->attrCmd;
  for ( i = 0; i < discoverRspCmd->numAttr; i++ )
  {
    // Device is notified of the result of its attribute discovery command.
  }

  return ( TRUE );
}

/*********************************************************************
 * @fn      zclEasenLock_ProcessInDiscAttrsExtRspCmd
 *
 * @brief   Process the "Profile" Discover Attributes Extended Response Command
 *
 * @param   pInMsg - incoming message to process
 *
 * @return  none
 */
static uint8 zclEasenLock_ProcessInDiscAttrsExtRspCmd( zclIncomingMsg_t *pInMsg )
{
  zclDiscoverAttrsExtRsp_t *discoverRspCmd;
  uint8 i;

  discoverRspCmd = (zclDiscoverAttrsExtRsp_t *)pInMsg->attrCmd;
  for ( i = 0; i < discoverRspCmd->numAttr; i++ )
  {
    // Device is notified of the result of its attribute discovery command.
  }

  return ( TRUE );
}
#endif // ZCL_DISCOVER

/****************************************************************************
****************************************************************************/


/********************************************
  User Define Functions
********************************************/

/*********************************************************************
 * @fn      SendToEndDevice
 *
 * @brief   Send the message to End Device.
 *
 * @param   shortAddr - short adderss
 *
 * @param   isAck - if none zero, send Ack message to End Device when faied to
 *          call AF_DataRequest() function.
 *
 * @return  none
 */
static uint8 SendToEndDevice(uint16 shortAddr)
{
    int nRet = RET_FAILED;
    uint8 data[50] = "ABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDE";
    if(shortAddr == 0x0000)
    {
        //BroadCast
        EasenLock_Periodic_DstAddr.addrMode = (afAddrMode_t)AddrBroadcast;
        EasenLock_Periodic_DstAddr.endPoint = EasenLock_ENDPOINT;
        EasenLock_Periodic_DstAddr.addr.shortAddr = 0xFFFF;
    }
    else
    {
        //Unicast
        EasenLock_Periodic_DstAddr.addrMode = (afAddrMode_t)Addr16Bit;
        EasenLock_Periodic_DstAddr.endPoint = EasenLock_ENDPOINT;
        EasenLock_Periodic_DstAddr.addr.shortAddr = shortAddr; 
    }
    
    if ( AF_DataRequest( &EasenLock_Periodic_DstAddr, &EasenLock_epDesc,
                       EasenLock_PERIODIC_CLUSTERID,
                       50,
                       (uint8 *)data,
			&EasenLock_TransID,
			0,
                       AF_DEFAULT_RADIUS ) == afStatus_SUCCESS )
    {
        nRet = RET_SUCCESS;
    }

    return nRet;
}

static uint8 SendToCoord(void)
{
    int nRet = RET_FAILED;
    uint8 data[20] = "ABCDEABCDEABCDEABCDE";
     
    //Unicast
    EasenLock_Periodic_DstAddr.addrMode = (afAddrMode_t)Addr16Bit;
    EasenLock_Periodic_DstAddr.endPoint = EasenLock_ENDPOINT;
    EasenLock_Periodic_DstAddr.addr.shortAddr = 0x00; 
    
    if ( AF_DataRequest( &EasenLock_Periodic_DstAddr, (endPointDesc_t *)&EasenLock_epDesc,
               EasenLock_PERIODIC_CLUSTERID,
               20,
               (uint8 *)data,
               &EasenLock_TransID, 
               0/*AF_EN_SECURITY*/, 
               AF_DEFAULT_RADIUS ) == afStatus_SUCCESS )
    {
        nRet = RET_SUCCESS;
    }
    
     HalLedSet(HAL_LED_1, HAL_LED_MODE_OFF); 
    return nRet;
}