bool emberAfPluginUpdateTcLinkKeyStatusCallback(EmberKeyStatus keyStatus)
{
  if (emAfPluginNetworkSteeringStateUpdateTclk()) {
    emberAfCorePrintln("%p: %p: 0x%X",
                       PLUGIN_NAME,
                       "Trust center link key update status",
                       keyStatus);
    switch(keyStatus) {
    case EMBER_TRUST_CENTER_LINK_KEY_ESTABLISHED:
      // Success! But we should still wait to make sure we verify the key.
      emAfPluginNetworkSteeringStateSetVerifyTclk();
      emberEventControlSetDelayMS(finishSteeringEvent, VERIFY_KEY_TIMEOUT_MS);
      return false;
    case EMBER_TRUST_CENTER_IS_PRE_R21:
    case EMBER_VERIFY_LINK_KEY_SUCCESS:
      // If the trust center is pre-r21, then we don't update the link key.
      // If the key status is that the link key has been verified, then we
      // have successfully updated our trust center link key and we are done!
      emAfPluginNetworkSteeringStateClearVerifyTclk();
      emberEventControlSetDelayMS(finishSteeringEvent, randomJitterMS());
      break;
    default:
      // Failure!
      emberLeaveNetwork();
      cleanupAndStop(EMBER_NO_LINK_KEY_RECEIVED);
    }
    emAfPluginNetworkSteeringStateClearUpdateTclk();
  }

  return true;
}
/*! Callback for Simple Descriptor Request */
static void ProcessServiceDiscovery(
    const EmberAfServiceDiscoveryResult *result) {
  // if we get a matche or a default response handle it
  // otherwise stop commissioning or go to the next incoming device
  if (emberAfHaveDiscoveryResponseStatus(result->status)) {
    EmberAfClusterList *discovered_clusters =
        (EmberAfClusterList *)result->responseData;

    // if our device requested to bind to server clusters (is_server parameter
    // during the SimpleCommissioningStart was FALSE) -> use inClusterList of
    // the incoming device's response
    // otherwise -> outClusterList (as our device has server clusters)
    const uint16_t *inc_clusters_arr = (dev_comm_session.is_server)
                                           ? discovered_clusters->outClusterList
                                           : discovered_clusters->inClusterList;
    // get correct lenght of the incoming clusters array
    const uint8_t inc_clusters_arr_len =
        (dev_comm_session.is_server) ? discovered_clusters->outClusterCount
                                     : discovered_clusters->inClusterCount;
    // init clusters skip mask length for further using
    InitRemoteSkipCluster(inc_clusters_arr_len);
    // check how much clusters our device wants to bind to
    uint8_t supported_clusters =
        CheckSupportedClusters(inc_clusters_arr, inc_clusters_arr_len);

    emberAfDebugPrintln("DEBUG: Supported clusters %d", supported_clusters);
    if (supported_clusters == 0) {
      // we should not do anything with that, just wait maybe another response
      // would come
      SetNextEvent(SC_EZEV_TIMEOUT);
      SetNextState(SC_EZ_WAIT_IDENT_RESP);
      emberEventControlSetDelayMS(
          StateMachineEvent, SIMPLE_COMMISSIONING_IDENTIFY_RESPONSE_WAIT_TIME());
    } else {
      // update our incoming device structure with information about clusters
      SetInDevicesClustersInfo(inc_clusters_arr, inc_clusters_arr_len,
                               supported_clusters);
      // Now we have all information about responded device's clusters
      // Start matching procedure for checking how much of them fit for our
      // device
      SetNextEvent(SC_EZEV_CHECK_CLUSTERS);
      SetNextState(SC_EZ_MATCH);
      emberEventControlSetActive(StateMachineEvent);
    }
  } else {
    // we should not do anything with that, just wait maybe another response
    // would come
    SetNextEvent(SC_EZEV_TIMEOUT);
    SetNextState(SC_EZ_WAIT_IDENT_RESP);
    emberEventControlSetDelayMS(
        StateMachineEvent, SIMPLE_COMMISSIONING_IDENTIFY_RESPONSE_WAIT_TIME());
  }
}
static void rescheduleUserControlEvent(void)
{
  // Look at all the active records and figure out when to schedule the event.
  // If any record should have been sent in the past, we immediately schedule
  // the event to send it as soon as possible.  Otherwise, we find the record
  // that has to be repeated soonest and schedule based on that.  If there are
  // no active records, we are done and cancel the event.
  uint16_t nowMs = halCommonGetInt16uMillisecondTick();
  uint16_t delayMs = MAX_INT16U_VALUE;
  uint8_t i;
  for (i = 0; i < COUNTOF(records); i++) {
    if (records[i].pairingIndex != 0xFF) {
      if (timeGTorEqualInt16u(nowMs, records[i].timeMs)) {
        delayMs = 0;
        break;
      } else {
        uint16_t remainingMs = elapsedTimeInt16u(nowMs, records[i].timeMs);
        if (remainingMs < delayMs) {
          delayMs = remainingMs;
        }
      }
    }
  }
  if (delayMs == MAX_INT16U_VALUE) {
    emberEventControlSetInactive(emberAfPluginRf4ceZrc11OutgoingUserControlEventControl);
  } else if (delayMs == 0) {
    emberEventControlSetActive(emberAfPluginRf4ceZrc11OutgoingUserControlEventControl);
  } else {
    emberEventControlSetDelayMS(emberAfPluginRf4ceZrc11OutgoingUserControlEventControl,
                                delayMs);
  }
}
示例#4
0
void halLedBlinkPattern( uint8_t count, uint8_t length, uint16_t *pattern)
{
  uint8_t i;

  if (length < 2) {
    return;
  }

  turnLedOn(activeLed);

  ledEventState = LED_BLINK_PATTERN;

  if (length > MAX_BLINK_PATTERN_LENGTH) {
    length = MAX_BLINK_PATTERN_LENGTH;
  }

  blinkPatternLength = length;
  ledBlinkCount = count;

  for(i=0; i<blinkPatternLength; i++) {
    blinkPattern[i] = pattern[i];
  }

  emberEventControlSetDelayMS(emberAfPluginLedBlinkLedEventFunctionEventControl,
                              blinkPattern[0]);

  blinkPatternPointer = 1;
}
示例#5
0
void halLedBlinkBlink( uint8_t count, uint16_t blinkTime)
{
  ledBlinkTime = blinkTime;
  turnLedOff(activeLed);
  ledEventState = LED_BLINKING_OFF;
  emberEventControlSetDelayMS(emberAfPluginLedBlinkLedEventFunctionEventControl,
                              ledBlinkTime);
  ledBlinkCount = count;
}
示例#6
0
// Tunnel event handler used to retry previously attempted tunnel creations
void emberAfPluginCommsHubFunctionTunnelEventHandler(void)
{
  uint8_t tunnelIndex;
  uint32_t timeNowMs;
  uint32_t nearestEventTimeoutDelayMs = UINT32_MAX;
  uint32_t currentTunnelTimeoutMs = 0;

  emberEventControlSetInactive(emberAfPluginCommsHubFunctionTunnelEventControl);
  timeNowMs = halCommonGetInt32uMillisecondTick();

  emberAfPluginCommsHubFunctionPrintln("CHF: emberAfPluginCommsHubFunctionTunnelEventHandler");

  // If we're no longer waiting for a tunnel to come up then find which tunnel
  // (or tunnels) are ready for another attempt at tunnel creation
  if (responsePendingIndex == EM_AF_PLUGIN_COMMS_HUB_FUNCTION_NULL_TUNNEL_INDEX) {
    for (tunnelIndex=0; tunnelIndex<EMBER_AF_PLUGIN_COMMS_HUB_FUNCTION_TUNNEL_LIMIT; tunnelIndex++) {
      if (tunnels[tunnelIndex].type == CLIENT_TUNNEL
	        && tunnels[tunnelIndex].state == REQUEST_PENDING_TUNNEL) {
        // JIRA EMAPPFWKV2-1392: Event handler was not registered and was not working properly
        // if timeout time has passed, then request a tunnel, else retry after least time remaining
        if (timeGTorEqualInt32u(timeNowMs, tunnels[tunnelIndex].timeoutMSec)) {
          emberAfPluginCommsHubFunctionPrintln("Retrying tunnel creation to node ID 0x%2x",
                                                tunnels[tunnelIndex].remoteNodeId);
          if (requestTunnel(tunnelIndex)) {
            emberEventControlSetDelayMS(emberAfPluginCommsHubFunctionTunnelEventControl,
                                        MILLISECOND_TICKS_PER_SECOND);
            return;
          }
        } else {
          currentTunnelTimeoutMs = elapsedTimeInt32u(timeNowMs, tunnels[tunnelIndex].timeoutMSec);
          if (currentTunnelTimeoutMs < nearestEventTimeoutDelayMs) {
            nearestEventTimeoutDelayMs = currentTunnelTimeoutMs;
          }
        }
      }
    }
  }

  if (nearestEventTimeoutDelayMs != MAX_INT32U_VALUE) {
    emberEventControlSetDelayMS(emberAfPluginCommsHubFunctionTunnelEventControl, nearestEventTimeoutDelayMs);
  }
}
示例#7
0
// State machine used to debounce the gpio sensor.  This function is called on
// every transition of the gpio sensor's GPIO pin.  A delayed event is used to
// take action on the pin transition.  If the pin changes back to its original
// state before the delayed event can execute, that change is marked as a bounce
// and no further action is taken.
static void sensorStateChangeDebounce(HalGpioSensorState status)
{

  if (status == lastSensorStatus) {
    // we went back to last status before debounce.  don't send the
    // message.
    emberEventControlSetInactive(emberAfPluginGpioSensorDebounceEventControl);
    return;
  }
  if (status == HAL_GPIO_SENSOR_ACTIVE) {
    newSensorStatus = status;
    emberEventControlSetDelayMS(emberAfPluginGpioSensorDebounceEventControl,
                                SENSOR_ASSERT_DEBOUNCE);
    return;
  } else if (status == HAL_GPIO_SENSOR_NOT_ACTIVE) {
    newSensorStatus = status;
    emberEventControlSetDelayMS(emberAfPluginGpioSensorDebounceEventControl,
                                SENSOR_DEASSERT_DEBOUNCE);
    return;
  }
}
示例#8
0
// Shut down for good after the user says it's OK and runs any last-minute logic
static void lvShutdown(void)
{
  uint16_t shutdownVoltage = emberAfPluginLowVoltageShutdownGetVoltage();
  if (emberAfPluginLowVoltageShutdownOkToShutdownCallback(shutdownVoltage)) {
    emberAfPluginLowVoltageShutdownPreShutdownCallback(shutdownVoltage);
    halPowerDown();
    halInternalSleep(SLEEPMODE_NOTIMER);
  } else {
    // User won't let us shut down yet, so check back after a brief interval
    emberEventControlSetDelayMS(myEvent, EMBER_AF_PLUGIN_LOW_VOLTAGE_SHUTDOWN_SHUTDOWN_POSTPONE_DURATION_MS);
  }
}
// This is the state machine that emits one frame at a time according to the
// format in the ir-database. It is controlled by the halInfraredLedStart and
// halInfraredLedStop functions and the event set by itself - only.
void halInfraredLedEventHandler(void)
{
  uint32_t delayMs;

  emberEventControlSetInactive(halInfraredLedEventControl);

  switch(state) {
  case IDLE:
    if (reqStart) {
      state = START;
      emberEventControlSetDelayMS( halInfraredLedEventControl, 1);
    } else if (reqStop) {
      state = STOP;
      emberEventControlSetDelayMS( halInfraredLedEventControl, 1);
    }
    break;

  case START:
    reqStart = false;
    halInternalInfraredLedEmitPress();
    repeateCount = halInternalInfraredLedEmitHeader.uRepeatCount;
    if (repeateCount > 0) {
      repeateCount--;
    }
    state = REPEATE;
    delayMs = irGetDelayToNextFrameInMs(irRepeateIntervalInMs);
    emberEventControlSetDelayMS(halInfraredLedEventControl, delayMs);
    break;

  case REPEATE:
    if (repeateCount > 0) {
      repeateCount--;
      halInternalInfraredLedEmitRepeat();
      delayMs = irGetDelayToNextFrameInMs( irRepeateIntervalInMs);
      emberEventControlSetDelayMS( halInfraredLedEventControl, delayMs);
    } else if (reqStop) {
      state = STOP;
      emberEventControlSetDelayMS(halInfraredLedEventControl, 1);
    }
    else if (halInternalInfraredLedEmitHeader.bRepeatMode) {
      halInternalInfraredLedEmitRepeat();
      delayMs = irGetDelayToNextFrameInMs(irRepeateIntervalInMs);
      emberEventControlSetDelayMS(halInfraredLedEventControl, delayMs);
    } else {
      state = IDLE;
    }
    break;

  case STOP:
    halInternalInfraredLedEmitRelease();
    halInternalInfraredLedEmitToggleStepToNext();
    reqStop = false;
    state = IDLE;
    irdPtrCurrent = NULL;
    irdLenCurrent = 0;
    break;
  }
}
void identifyEventHandler(void)
{
  if (identifyDurationMs == 0) {
    halClearLed(LED);
    emberEventControlSetInactive(identifyEventControl);
  } else {
    halToggleLed(LED);
    if (identifyDurationMs >= MILLISECOND_TICKS_PER_QUARTERSECOND) {
      identifyDurationMs -= MILLISECOND_TICKS_PER_QUARTERSECOND;
    } else {
      identifyDurationMs = 0;
    }
    emberEventControlSetDelayMS(identifyEventControl,
                                MILLISECOND_TICKS_PER_QUARTERSECOND);
  }
}
示例#11
0
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));
  }
}
示例#12
0
void emberAfPluginNetworkSteeringStackStatusCallback(EmberStatus status)
{
  if (emAfPluginNetworkSteeringState
      == EMBER_AF_PLUGIN_NETWORK_STEERING_STATE_NONE) {
    return;
  } else if (status == EMBER_NETWORK_UP) {
    emberAfCorePrintln("%p network joined.", PLUGIN_NAME);
    if (!emAfPluginNetworkSteeringStateUsesDistributedKey()
        && !(emAfPluginNetworkSteeringOptionsMask
             & EMBER_AF_PLUGIN_NETWORK_STEERING_OPTIONS_NO_TCLK_UPDATE)) {
      emAfPluginNetworkSteeringStateSetUpdateTclk();
    }
    emberEventControlSetDelayMS(finishSteeringEvent, randomJitterMS());
    return;
  }

  tryToJoinNetwork();
}
static CommissioningState_t MatchingCheck(void) {
  emberAfDebugPrintln("DEBUG: Matching Check");
  MatchDescriptorReq_t *in_dev = GetTopInDeviceDescriptor();
  assert(in_dev != NULL);
  EmberStatus status =
      emberAfFindIeeeAddress(in_dev->source, ProcessEUI64Discovery);

  if (status == EMBER_SUCCESS) {
    SetNextEvent(SC_EZEV_AWAIT_EUI64);
  } else {
    SetNextEvent(SC_EZEV_CHECK_QUEUE);
  }
  // await for EUI64 response
  emberEventControlSetDelayMS(StateMachineEvent,
                              SIMPLE_COMMISSIONING_EUI64_RESPONSE_WAIT_TIME());

  return SC_EZ_BIND;
}
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);
    }
  }
}
static CommissioningState_t BroadcastIdentifyQuery(void) {
  emberAfDebugPrintln("DEBUG: Broadcast ID Query");
  // Make Identify cluster's command to send an Identify Query
  emberAfFillCommandIdentifyClusterIdentifyQuery();
  emberAfSetCommandEndpoints(dev_comm_session.ep, EMBER_BROADCAST_ENDPOINT);
  // Broadcast Identify Query
  EmberStatus status =
      emberAfSendCommandBroadcast(EMBER_SLEEPY_BROADCAST_ADDRESS);

  if (status != EMBER_SUCCESS) {
    // Exceptional case. Stop commissioning
    SetNextEvent(SC_EZEV_UNKNOWN);
  }

  // Schedule event for awaiting for responses for 1 second
  // TODO: Set hardcoded value as plugin's option parameter as optional value
  emberEventControlSetDelayMS(StateMachineEvent,
                              SIMPLE_COMMISSIONING_IDENTIFY_RESPONSE_WAIT_TIME());
  // If Identify Query responses won't be received state machine just will call
  // Timeout handler
  SetNextEvent(SC_EZEV_TIMEOUT);

  return SC_EZ_WAIT_IDENT_RESP;
}
示例#16
0
void emberZllScanCompleteHandler(EmberStatus status)
{
#ifdef EMBER_AF_PLUGIN_ZLL_COMMISSIONING_LINK_INITIATOR
  if (touchLinkInProgress()) {
    flags |= SCAN_COMPLETE;
    if (status != EMBER_SUCCESS) {
      emberAfAppPrintln("%p%p0x%x",
                        "Error: ",
                        "Touch linking failed: ",
                        status);
      abortTouchLink(EMBER_AF_ZLL_PREEMPTED_BY_STACK);
    } else if (abortingTouchLink()) {
      abortTouchLink(EMBER_AF_ZLL_ABORTED_BY_APPLICATION);
    } else if (targetNetworkFound()) {
      EmberStatus status = setRadioChannel(network.zigbeeNetwork.channel);
      if (status != EMBER_SUCCESS) {
        emberAfAppPrintln("%p%p%p0x%x",
                          "Error: ",
                          "Touch linking failed: ",
                          "could not change channel: ",
                          status);
        abortTouchLink(EMBER_AF_ZLL_CHANNEL_CHANGE_FAILED);
        return;
      }

      // When scanning for the purposes of touch linking or requesting device
      // information and the target has more than one sub device, turn the
      // receiver on (so we can actually receive the response) and send out the
      // first request.  If the target only has one sub device, its data will
      // have already been received in the ScanRequest.
      if ((scanForTouchLink() || scanForDeviceInformation())
          && network.numberSubDevices != 1) {
        emberZllSetRxOnWhenIdle(EMBER_AF_PLUGIN_ZLL_COMMISSIONING_TOUCH_LINK_MILLISECONDS_DELAY);
        status = sendDeviceInformationRequest(0x00); // start index
        if (status != EMBER_SUCCESS) {
          emberAfAppPrintln("%p%p%p0x%x",
                            "Error: ",
                            "Touch linking failed: ",
                            "could not send device information request: ",
                            status);
          if (scanForDeviceInformation()) {
            abortTouchLink(EMBER_AF_ZLL_SENDING_DEVICE_INFORMATION_REQUEST_FAILED);
            return;
          }
        }
      }

      status = sendIdentifyRequest(emAfZllIdentifyDurationS);
      if (status != EMBER_SUCCESS) {
        emberAfAppPrintln("%p%p%p0x%x",
                          "Error: ",
                          "Touch linking failed: ",
                          "could not send identify request: ",
                          status);
        if (scanForIdentify()) {
          abortTouchLink(EMBER_AF_ZLL_SENDING_IDENTIFY_REQUEST_FAILED);
          return;
        }
      }
      emberEventControlSetDelayMS(emberAfPluginZllCommissioningTouchLinkEventControl,
                                  EMBER_AF_PLUGIN_ZLL_COMMISSIONING_TOUCH_LINK_MILLISECONDS_DELAY);
    } else {
      emberAfAppPrintln("%p%p%p",
                        "Error: ",
                        "Touch linking failed: ",
                        "no networks were found");
      abortTouchLink(EMBER_AF_ZLL_NO_NETWORKS_FOUND);
    }
  }
#endif //EMBER_AF_PLUGIN_ZLL_COMMISSIONING_LINK_INITIATOR

#ifdef EMBER_AF_PLUGIN_TEST_HARNESS_Z3
  emAfPluginTestHarnessZ3ZllScanCompleteCallback(status);
#endif
}
示例#17
0
//------------------------------------------------------------------------------
// Plugin event handlers
void emberAfPluginLedBlinkLedEventFunctionEventHandler( void )
{
  switch(ledEventState) {
  case LED_ON:
    // was on.  this must be time to turn it off.
    turnLedOff(activeLed);
    emberEventControlSetInactive(emberAfPluginLedBlinkLedEventFunctionEventControl);
    break;

  case LED_OFF:
    // was on.  this must be time to turn it off.
    turnLedOn(activeLed);
    emberEventControlSetInactive(emberAfPluginLedBlinkLedEventFunctionEventControl);
    break;

  case LED_BLINKING_ON:
    turnLedOff(activeLed);
    if (ledBlinkCount > 0) {
      if (ledBlinkCount != 255) { // blink forever if count is 255
        ledBlinkCount --;
      }
      if (ledBlinkCount > 0) {
        ledEventState = LED_BLINKING_OFF;
        emberEventControlSetDelayMS(emberAfPluginLedBlinkLedEventFunctionEventControl,
                                    ledBlinkTime);

      } else {
        ledEventState = LED_OFF;
        emberEventControlSetInactive(emberAfPluginLedBlinkLedEventFunctionEventControl);
      }
    } else {
      ledEventState = LED_BLINKING_OFF;
      emberEventControlSetDelayMS(emberAfPluginLedBlinkLedEventFunctionEventControl,
                                  ledBlinkTime);
    }
    break;
  case LED_BLINKING_OFF:
    turnLedOn(activeLed);
    ledEventState = LED_BLINKING_ON;
    emberEventControlSetDelayMS(emberAfPluginLedBlinkLedEventFunctionEventControl,
                                ledBlinkTime);
    break;
  case LED_BLINK_PATTERN:
    if (ledBlinkCount == 0) {
      turnLedOff(activeLed);

      ledEventState = LED_OFF;
      emberEventControlSetInactive(emberAfPluginLedBlinkLedEventFunctionEventControl);

      break;
    }

    if (blinkPatternPointer %2 == 1) {
      turnLedOff(activeLed);
    } else {
      turnLedOn(activeLed);
    }

    emberEventControlSetDelayMS(emberAfPluginLedBlinkLedEventFunctionEventControl,
                                blinkPattern[blinkPatternPointer]);

    blinkPatternPointer ++;

    if (blinkPatternPointer >= blinkPatternLength) {
      blinkPatternPointer = 0;
      if (ledBlinkCount != 255) { // blink forever if count is 255
        ledBlinkCount --;
      }
    }

  default:
    break;
  }
}