void buttonEventHandler(void)
{
  emberEventControlSetInactive(buttonEventControl);
  if (halButtonState(BUTTON0) == BUTTON_PRESSED) {
    EmberNetworkStatus state = emberNetworkState();
    if (state == EMBER_JOINED_NETWORK) {
      emberAfFillExternalBuffer((ZCL_CLUSTER_SPECIFIC_COMMAND
                                 | ZCL_FRAME_CONTROL_CLIENT_TO_SERVER),
                                ZCL_ON_OFF_CLUSTER_ID,
                                (on ? ZCL_OFF_COMMAND_ID : ZCL_ON_COMMAND_ID),
                                "");
      emberAfGetCommandApsFrame()->profileId           = emberAfPrimaryProfileId();
      emberAfGetCommandApsFrame()->sourceEndpoint      = emberAfPrimaryEndpoint();
      emberAfGetCommandApsFrame()->destinationEndpoint = EMBER_BROADCAST_ENDPOINT;
      if (emberAfSendCommandUnicastToBindings() == EMBER_SUCCESS) {
        on = !on;
      } else {
        halPlayTune_P(sadTune, TRUE);
      }
    } else if (state == EMBER_NO_NETWORK) {
      halPlayTune_P((emberAfStartSearchForJoinableNetwork() == EMBER_SUCCESS
                     ? waitTune
                     : sadTune),
                    TRUE);
    } else {
      if (REPAIR_LIMIT_MS
          < elapsedTimeInt16u(buttonPressTime,
                              halCommonGetInt16uMillisecondTick())) {
        halPlayTune_P(sadTune, TRUE);
      } else {
        if (state == EMBER_JOINED_NETWORK_NO_PARENT
            && !emberStackIsPerformingRejoin()) {
          halPlayTune_P((emberFindAndRejoinNetwork(TRUE, 0) == EMBER_SUCCESS
                         ? waitTune
                         : sadTune),
                        TRUE);
        }
        emberEventControlSetDelayMS(buttonEventControl, REPAIR_DELAY_MS);
      }
    }
  } else if (halButtonState(BUTTON1) == BUTTON_PRESSED) {
    emberAfEzmodeClientCommission(emberAfPrimaryEndpoint(),
                                  EMBER_AF_EZMODE_COMMISSIONING_CLIENT_TO_SERVER,
                                  clusterIds,
                                  COUNTOF(clusterIds));
  }
}
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;

}
void buttonEventHandler(void)
{
  emberEventControlSetInactive(buttonEventControl);

  EmberNetworkStatus state = emberNetworkState();
  if (state == EMBER_JOINED_NETWORK) {
    emberAfFillExternalBuffer((ZCL_CLUSTER_SPECIFIC_COMMAND
                               | ZCL_FRAME_CONTROL_CLIENT_TO_SERVER),
                              ZCL_ON_OFF_CLUSTER_ID,
                              (on ? ZCL_OFF_COMMAND_ID : ZCL_ON_COMMAND_ID),
                              "");
    emberAfGetCommandApsFrame()->profileId           = emberAfProfileIdFromIndex(0);
    emberAfGetCommandApsFrame()->sourceEndpoint      = emberAfEndpointFromIndex(0);
    emberAfGetCommandApsFrame()->destinationEndpoint = EMBER_BROADCAST_ENDPOINT;
    if (emberAfSendCommandBroadcast(EMBER_SLEEPY_BROADCAST_ADDRESS)
        == EMBER_SUCCESS) {
      on = !on;
    } else {
      halPlayTune_P(sadTune, TRUE);
    }
  } else if (state == EMBER_NO_NETWORK) {
    halPlayTune_P((emberAfZllInitiateTouchLink() == EMBER_SUCCESS
                   ? waitTune
                   : sadTune),
                  TRUE);
  } else {
    int16u elapsed = elapsedTimeInt16u(buttonPressTime,
                                       halCommonGetInt16uMillisecondTick());
    if (REPAIR_LIMIT_MS
        < elapsedTimeInt16u(buttonPressTime,
                            halCommonGetInt16uMillisecondTick())) {
      halPlayTune_P(sadTune, TRUE);
    } else {
      if (state == EMBER_JOINED_NETWORK_NO_PARENT
          && !emberStackIsPerformingRejoin()) {
        halPlayTune_P((emberFindAndRejoinNetwork(TRUE, 0) == EMBER_SUCCESS
                       ? waitTune
                       : sadTune),
                      TRUE);
      }
      emberEventControlSetDelayMS(buttonEventControl, REPAIR_DELAY_MS);
    }
  }
}
bool emberAfMessagingClusterGetLastMessageCallback(void)
{
  uint8_t endpoint = emberAfCurrentEndpoint();
  EmberAfPluginMessagingServerMessage message;
  emberAfMessagingClusterPrintln("RX: GetLastMessage");
  if (emberAfPluginMessagingServerGetMessage(endpoint, &message)) {
    emberAfFillCommandMessagingClusterDisplayMessage(message.messageId,
                                                     message.messageControl,
                                                     message.startTime,
                                                     message.durationInMinutes,
                                                     message.message,
                                                     message.extendedMessageControl);
    emberAfGetCommandApsFrame()->options |= EMBER_APS_OPTION_SOURCE_EUI64;
    emberAfSendResponse();
  } else {
    emberAfSendImmediateDefaultResponse(EMBER_ZCL_STATUS_NOT_FOUND);
  }
  return true;
}
void emberAfPluginMessagingServerCancelMessage(EmberNodeId nodeId,
                                               uint8_t srcEndpoint,
                                               uint8_t dstEndpoint)
{
  EmberStatus status;
  EmberAfPluginMessagingServerMessage message;

  // Nullify the current message before sending the cancellation.
  emberAfPluginMessagingServerSetMessage(srcEndpoint, NULL);

  // Then send the response
  emberAfPluginMessagingServerGetMessage(srcEndpoint, &message);

  emberAfFillCommandMessagingClusterCancelMessage(message.messageId,
                                                  message.messageControl);
  emberAfSetCommandEndpoints(srcEndpoint, dstEndpoint);
  emberAfGetCommandApsFrame()->options |= EMBER_APS_OPTION_SOURCE_EUI64;
  status = emberAfSendCommandUnicast(EMBER_OUTGOING_DIRECT, nodeId);
  if(status != EMBER_SUCCESS) {
    emberAfMessagingClusterPrintln("Error in cancel %x", status);
  }
}
void emberAfPluginMessagingServerDisplayMessage(EmberNodeId nodeId,
                                                uint8_t srcEndpoint,
                                                uint8_t dstEndpoint)
{
  EmberStatus status;
  EmberAfPluginMessagingServerMessage message;
  if (!emberAfPluginMessagingServerGetMessage(srcEndpoint, &message)) {
    emberAfMessagingClusterPrintln("invalid msg");
    return;
  }

  emberAfFillCommandMessagingClusterDisplayMessage(message.messageId,
                                                   message.messageControl,
                                                   message.startTime,
                                                   message.durationInMinutes,
                                                   message.message,
                                                   message.extendedMessageControl);
  emberAfSetCommandEndpoints(srcEndpoint, dstEndpoint);
  emberAfGetCommandApsFrame()->options |= EMBER_APS_OPTION_SOURCE_EUI64;
  status = emberAfSendCommandUnicast(EMBER_OUTGOING_DIRECT, nodeId);
  if(status != EMBER_SUCCESS) {
    emberAfMessagingClusterPrintln("Error in display %x", status);
  }
}
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;
}