bool emberAfEventsClusterPublishEventCallback(uint8_t logId, uint16_t eventId, uint32_t eventTime, uint8_t eventControl, uint8_t* eventData) { emberAfEventsClusterPrint("RX: PublishEvent 0x%x, 0x%2x, 0x%4x, 0x%x, 0x%x", logId, eventId, eventTime, eventControl, *eventData); #if defined(EMBER_AF_PRINT_ENABLE) && defined(EMBER_AF_PRINT_EVENTS_CLUSTER) uint8_t eventDataLen = emberAfStringLength(eventData); if (eventDataLen > 0) { emberAfEventsClusterPrint(", "); emberAfEventsClusterPrintString(eventData); } emberAfEventsClusterPrintln(""); #endif // defined(EMBER_AF_PRINT_ENABLE) && defined(EMBER_AF_PRINT_EVENTS_CLUSTER) emberAfSendImmediateDefaultResponse(EMBER_ZCL_STATUS_SUCCESS); return true; }
/** @brief Identify Cluster Identify Query Response * * * * @param timeout Ver.: always */ boolean emberAfIdentifyClusterIdentifyQueryResponseCallback(int16u timeout) { // TODO: !!! IMPORTANT !!! add handling for several responses // now the state machine might be broken as we use only one global variable // for storing incoming connection information like Short ID and endpoint // // ignore broadcasts from yourself and from devices that are not // in the identifying state const EmberAfClusterCommand *const current_cmd = emberAfCurrentCommand(); if (emberAfGetNodeId() != current_cmd->source && timeout != 0) { emberAfDebugPrintln("DEBUG: Got ID Query response"); emberAfDebugPrintln("DEBUG: Sender 0x%2X", emberAfCurrentCommand()->source); // Queue is empty, so we can start commissioning process // otherwise we push it in the queue and will process later if (GetQueueSize() == 0) { // ID Query received -> go to the discover state for getting clusters info emberAfDebugPrintln("DEBUG: QUEUE IS EMPTY"); SetNextState(SC_EZ_DISCOVER); SetNextEvent(SC_EZEV_CHECK_CLUSTERS); emberEventControlSetActive(StateMachineEvent); } // Store information about endpoint and short ID of the incoming response // for further processing in the Matching (SC_EZ_MATCH) state SetInConnBaseInfo(current_cmd->source, current_cmd->apsFrame->sourceEndpoint); emberAfSendImmediateDefaultResponse(EMBER_ZCL_STATUS_SUCCESS); } return TRUE; }
bool emberAfEventsClusterClearEventLogResponseCallback(uint8_t clearedEventsLogs) { emberAfEventsClusterPrintln("RX: ClearEventLogResponse 0x%x", clearedEventsLogs); emberAfSendImmediateDefaultResponse(EMBER_ZCL_STATUS_SUCCESS); return true; }
bool emberAfBasicClusterResetToFactoryDefaultsCallback(void) { emberAfBasicClusterPrintln("RX: ResetToFactoryDefaultsCallback"); emberAfResetAttributes(emberAfCurrentEndpoint()); emberAfPluginBasicResetToFactoryDefaultsCallback(emberAfCurrentEndpoint()); emberAfSendImmediateDefaultResponse(EMBER_ZCL_STATUS_SUCCESS); return true; }
bool emberAfGroupsClusterAddGroupResponseCallback(uint8_t status, uint16_t groupId) { emberAfGroupsClusterPrintln("RX: AddGroupResponse 0x%x, 0x%2x", status, groupId); emberAfSendImmediateDefaultResponse(EMBER_ZCL_STATUS_SUCCESS); return true; }
bool emberAfScenesClusterCopySceneResponseCallback(uint8_t status, uint16_t groupIdFrom, uint8_t sceneIdFrom) { emberAfScenesClusterPrintln("RX: CopySceneResponse 0x%x, 0x%2x, 0x%x", status, groupIdFrom, sceneIdFrom); emberAfSendImmediateDefaultResponse(EMBER_ZCL_STATUS_SUCCESS); return true; }
bool emberAfGroupsClusterViewGroupResponseCallback(uint8_t status, uint16_t groupId, uint8_t* groupName) { emberAfGroupsClusterPrint("RX: ViewGroupResponse 0x%x, 0x%2x, \"", status, groupId); emberAfGroupsClusterPrintString(groupName); emberAfGroupsClusterPrintln("\""); emberAfSendImmediateDefaultResponse(EMBER_ZCL_STATUS_SUCCESS); return true; }
bool emberAfMessagingClusterMessageConfirmationCallback(uint32_t messageId, uint32_t confirmationTime, uint8_t messageConfirmationContral, uint8_t *messageResponse) #endif { emberAfMessagingClusterPrintln("RX: MessageConfirmation 0x%4x, 0x%4x", messageId, confirmationTime); emberAfSendImmediateDefaultResponse(EMBER_ZCL_STATUS_SUCCESS); return true; }
bool emberAfEventsClusterPublishEventLogCallback(uint16_t totalNumberOfEvents, uint8_t commandIndex, uint8_t totalCommands, uint8_t logPayloadControl, uint8_t* logPayload) { emberAfEventsClusterPrint("RX: PublishEventLog 0x%2x, 0x%x, 0x%x, 0x%x", totalNumberOfEvents, commandIndex, totalCommands, logPayloadControl); #if defined(EMBER_AF_PRINT_ENABLE) && defined(EMBER_AF_PRINT_EVENTS_CLUSTER) uint16_t logPayloadLen = (emberAfCurrentCommand()->bufLen - (emberAfCurrentCommand()->payloadStartIndex + sizeof(totalNumberOfEvents) + sizeof(commandIndex) + sizeof(totalCommands) + sizeof(logPayloadControl))); uint16_t logPayloadIndex = 0; uint8_t i; if (NULL != logPayload) { for (i = 0; i < numberOfEvents(logPayloadControl); i++) { uint8_t logId; uint16_t eventId; uint32_t eventTime; uint8_t *eventData; uint8_t eventDataLen; logId = emberAfGetInt8u(logPayload, logPayloadIndex, logPayloadLen); logPayloadIndex++; eventId = emberAfGetInt16u(logPayload, logPayloadIndex, logPayloadLen); logPayloadIndex += 2; eventTime = emberAfGetInt32u(logPayload, logPayloadIndex, logPayloadLen); logPayloadIndex += 4; eventData = logPayload + logPayloadIndex; eventDataLen = emberAfGetInt8u(logPayload, logPayloadIndex, logPayloadLen); logPayloadIndex += (1 + eventDataLen); emberAfEventsClusterPrint(" ["); emberAfEventsClusterPrint("0x%x, 0x%2x, 0x%4x, 0x%x", logId, eventId, eventTime, eventDataLen); if (eventDataLen > 0) { emberAfEventsClusterPrint(", "); emberAfEventsClusterPrintString(eventData); } emberAfEventsClusterPrint("]"); } } emberAfEventsClusterPrintln(""); #endif // defined(EMBER_AF_PRINT_ENABLE) && defined(EMBER_AF_PRINT_EVENTS_CLUSTER) emberAfSendImmediateDefaultResponse(EMBER_ZCL_STATUS_SUCCESS); return true; }
bool emberAfGroupsClusterGetGroupMembershipResponseCallback(uint8_t capacity, uint8_t groupCount, uint8_t* groupList) { uint8_t i; emberAfGroupsClusterPrint("RX: GetGroupMembershipResponse 0x%x, 0x%x,", capacity, groupCount); for (i = 0; i < groupCount; i++) { emberAfGroupsClusterPrint(" [0x%2x]", emberAfGetInt16u(groupList + (i << 1), 0, 2)); } emberAfGroupsClusterPrintln(""); emberAfSendImmediateDefaultResponse(EMBER_ZCL_STATUS_SUCCESS); return true; }
bool emberAfSimpleMeteringClusterConfigureMirrorCallback(uint32_t issuerEventId, uint32_t reportingInterval, uint8_t mirrorNotificationReporting, uint8_t notificationScheme) { EmberAfStatus status = EMBER_ZCL_STATUS_SUCCESS; uint8_t endpoint = emberAfCurrentEndpoint(); EmberEUI64 sendersEui; uint8_t index; emberAfSimpleMeteringClusterPrintln("ConfigureMirror on endpoint 0x%x", endpoint); if (EMBER_SUCCESS != emberLookupEui64ByNodeId(emberAfCurrentCommand()->source, sendersEui)) { emberAfSimpleMeteringClusterPrintln("Error: Meter Mirror plugin cannot determine EUI64 for node ID 0x%2X", emberAfCurrentCommand()->source); status = EMBER_ZCL_STATUS_FAILURE; goto kickout; } index = findMirrorIndex(sendersEui); if (index == INVALID_INDEX) { emberAfSimpleMeteringClusterPrint("Error: Meter mirror plugin received unknown report from "); emberAfPrintBigEndianEui64(sendersEui); emberAfSimpleMeteringClusterPrintln(""); status = EMBER_ZCL_STATUS_NOT_AUTHORIZED; goto kickout; } if (mirrorList[index].issuerEventId == 0 || issuerEventId > mirrorList[index].issuerEventId) { if(notificationScheme > 0x02) { status = EMBER_ZCL_STATUS_INVALID_FIELD; goto kickout; } mirrorList[index].issuerEventId = issuerEventId; mirrorList[index].reportingInterval = reportingInterval; mirrorList[index].mirrorNotificationReporting = mirrorNotificationReporting; mirrorList[index].notificationScheme = notificationScheme; } kickout: emberAfSendImmediateDefaultResponse(status); return true; }
bool 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; }
bool emberAfReportAttributesCallback(EmberAfClusterId clusterId, uint8_t *buffer, uint16_t bufLen) { EmberEUI64 sendersEui; uint16_t bufIndex = 0; uint8_t endpoint; uint8_t index; bool attributeReportingComplete = false; if (EMBER_SUCCESS != emberLookupEui64ByNodeId(emberAfCurrentCommand()->source, sendersEui)) { emberAfSimpleMeteringClusterPrintln("Error: Meter Mirror plugin cannot determine EUI64 for node ID 0x%2X", emberAfCurrentCommand()->source); emberAfSendImmediateDefaultResponse(EMBER_ZCL_STATUS_FAILURE); return true; } if (emberAfCurrentCommand()->direction == ZCL_DIRECTION_CLIENT_TO_SERVER) { emberAfSimpleMeteringClusterPrintln("Error: Meter Mirror Plugin does not accept client to server attributes.\n", emberAfCurrentCommand()->direction); emberAfSendImmediateDefaultResponse(EMBER_ZCL_STATUS_FAILURE); return true; } index = findMirrorIndex(sendersEui); if (index == INVALID_INDEX) { emberAfSimpleMeteringClusterPrint("Error: Meter mirror plugin received unknown report from "); emberAfPrintBigEndianEui64(sendersEui); emberAfSimpleMeteringClusterPrintln(""); emberAfSendImmediateDefaultResponse(EMBER_ZCL_STATUS_NOT_AUTHORIZED); return true; } if (emberAfCurrentCommand()->mfgSpecific) { // Here is where we could handle a MFG specific Report attributes and interpret // it. This code does not do that, just politely returns an error. emberAfSimpleMeteringClusterPrintln("Error: Unknown MFG Code for mirror: 0x%2X", emberAfCurrentCommand()->mfgCode); emberAfSendImmediateDefaultResponse(EMBER_ZCL_STATUS_UNSUP_MANUF_GENERAL_COMMAND); return true; } endpoint = (index + EMBER_AF_PLUGIN_METER_MIRROR_ENDPOINT_START); while (bufIndex + ATTRIBUTE_OVERHEAD < bufLen) { EmberAfStatus status; EmberAfAttributeId attributeId; EmberAfAttributeType dataType; uint8_t dataSize; attributeId = (EmberAfAttributeId)emberAfGetInt16u(buffer, bufIndex, bufLen); bufIndex += 2; dataType = (EmberAfAttributeType)emberAfGetInt8u(buffer, bufIndex, bufLen); bufIndex++; // For strings, the data size is the length of the string (specified by the // first byte of data) plus one for the length byte itself. For everything // else, the size is just the size of the data type. dataSize = (emberAfIsThisDataTypeAStringType(dataType) ? emberAfStringLength(buffer + bufIndex) + 1 : emberAfGetDataSize(dataType)); { #if (BIGENDIAN_CPU) uint8_t data[ATTRIBUTE_LARGEST]; if (isThisDataTypeSentLittleEndianOTA(dataType)) { emberReverseMemCopy(data, buffer + bufIndex, dataSize); } else { MEMMOVE(data, buffer + bufIndex, dataSize); } #else uint8_t *data = buffer + bufIndex; #endif if (attributeId == ZCL_ATTRIBUTE_REPORTING_STATUS_ATTRIBUTE_ID) { // From the SE 1.2a Specification within the ConfigureMirror command // description.. // 1. On powering up, the BOMD will send one or more Report Attribute // commands to the Metering client on the mirror endpoint. The last // attribute to be reported to the mirror shall be an Attribute // Reporting Status attribute, as defined in section A.2. // 2. If MirrorReportAttributeResponse is enabled, the server does not // need to request an APS ACK. If the server requests an APS ACK, // the Metering client on the mirror endpoint shall respond first // with an APS ACK and then send the MirrorReportAttributeResponse. // // If Mirror Notification Reporting is set to false, the // MirrorReportAttributeResponse command shall not be enabled; the // Metering server may poll the Notification flags by means of a normal // ReadAttribute command, as shown in Figure D 29: attributeReportingComplete = (data[0] == EMBER_ZCL_ATTRIBUTE_REPORTING_STATUS_ATTRIBUTE_REPORTING_COMPLETE); status = EMBER_ZCL_STATUS_SUCCESS; } else { if (attributeId == ZCL_METERING_DEVICE_TYPE_ATTRIBUTE_ID && data[0] < EMBER_ZCL_METERING_DEVICE_TYPE_MIRRORED_ELECTRIC_METERING) { data[0] += 127; } status = emberAfWriteServerAttribute(endpoint, clusterId, attributeId, data, dataType); } } emberAfSimpleMeteringClusterPrintln("Mirror attribute 0x%2x: 0x%x", attributeId, status); bufIndex += dataSize; } // If reporting is complete then callback to the application so that if it needs // to do any post processing on the reported attributes it can do it now. if (attributeReportingComplete) { emberAfPluginMeterMirrorReportingCompleteCallback(endpoint); } // Notification flags emberAfSimpleMeteringClusterPrintln("Mirror reporting ep: 0x%x, reporting: 0x%x, scheme: 0x%x", endpoint, mirrorList[index].mirrorNotificationReporting, mirrorList[index].notificationScheme); if (mirrorList[index].mirrorNotificationReporting && attributeReportingComplete) { if (mirrorList[index].notificationScheme == EMBER_ZCL_NOTIFICATION_SCHEME_PREDEFINED_NOTIFICATION_SCHEME_A || mirrorList[index].notificationScheme == EMBER_ZCL_NOTIFICATION_SCHEME_PREDEFINED_NOTIFICATION_SCHEME_B) { return sendMirrorReportAttributeResponse(endpoint, index); } else { // TODO: for custom notification schemes callback to application // return emberAfMeterMirrorSendMirrorReportAttributeResponseCallback(...) } } emberAfSendImmediateDefaultResponse(EMBER_ZCL_STATUS_SUCCESS); return true; }
// Move hue to a given hue, taking transitionTime until completed. bool emberAfColorControlClusterMoveToHueCallback(uint8_t hue, uint8_t direction, uint16_t transitionTime) { ColorControlState *state = getColorControlState(emberAfCurrentEndpoint()); EmberAfStatus status; uint8_t currentHue, currentSaturation; emberAfColorControlClusterPrintln("ColorControl: MoveToHue (%x, %x, %2x)", hue, direction, transitionTime); if (state == NULL) { status = EMBER_ZCL_STATUS_FAILURE; goto send_default_response; } // If the color specified is not achievable by the hardware, then the // color shall not be set and a ZCL default response command shall be // generated with status code equal to INVALID_VALUE. status = colorControlReadCurrentSaturation(emberAfCurrentEndpoint(), ¤tSaturation); if (status != EMBER_ZCL_STATUS_SUCCESS) { goto send_default_response; } if(!emberAfPluginColorControlIsColorSupportedCallback(hue, currentSaturation)) { status = EMBER_ZCL_STATUS_INVALID_VALUE; goto send_default_response; } status = colorControlReadCurrentHue(emberAfCurrentEndpoint(), ¤tHue); if (status != EMBER_ZCL_STATUS_SUCCESS) { goto send_default_response; } // Nothing to do, prevents divide-by-zero if ( hue == currentHue ) { status = EMBER_ZCL_STATUS_SUCCESS; goto send_default_response; } // As hue is effectively measured on a circle, the new hue may be moved to in // either direction. The direction of hue change is given by the Direction // field. If Directionis "Shortest distance," the direction is taken that // involves the shortest path round the circle. This case corresponds to // expected normal usage. If Direction is "Longest distance," the direction // is taken that involves the longest path round the circle. This case can be // used for "rainbow effects." In both cases, if both distances are the same, // the Up direction shall be taken. switch (direction) { case EMBER_ZCL_HUE_DIRECTION_SHORTEST_DISTANCE: state->hueMoveDirection = ((uint8_t) (hue - currentHue) <= 127 ? true : false); break; case EMBER_ZCL_HUE_DIRECTION_LONGEST_DISTANCE: state->hueMoveDirection = ((uint8_t) (hue - currentHue) >= 127 ? true : false); break; case EMBER_ZCL_HUE_DIRECTION_UP: state->hueMoveDirection = true; break; case EMBER_ZCL_HUE_DIRECTION_DOWN: state->hueMoveDirection = false; break; default: status = EMBER_ZCL_STATUS_INVALID_FIELD; goto send_default_response; } state->commandId = ZCL_MOVE_TO_HUE_COMMAND_ID; state->elapsedTime = 0; state->hueMoveToLevel = hue; state->transitionTime = transitionTime * MILLISECOND_TICKS_PER_SECOND / 10; state->eventDuration = state->transitionTime / ((state->hueMoveDirection) ? (hue - currentHue) : (currentHue - hue)); // Set the Color Mode attribute to zero as per the spec, ignoring any errors. colorControlSetColorModeToZero(); // Schedule the next tick if (emberAfScheduleServerTick(emberAfCurrentEndpoint(), ZCL_COLOR_CONTROL_CLUSTER_ID, state->eventDuration) != EMBER_SUCCESS) status = EMBER_ZCL_STATUS_SOFTWARE_FAILURE; else status = EMBER_ZCL_STATUS_SUCCESS; state->active = true; goto send_default_response; send_default_response: emberAfSendImmediateDefaultResponse(status); return true; }
// Step hue by one step, taking time as specified. bool emberAfColorControlClusterStepHueCallback(uint8_t stepMode, uint8_t stepSize, uint8_t transitionTime) { ColorControlState *state = getColorControlState(emberAfCurrentEndpoint()); EmberAfStatus status; uint8_t currentHue, currentSaturation; emberAfColorControlClusterPrintln("ColorControl: StepHue (%x, %x, %x)", stepMode, stepSize, transitionTime); if (state == NULL) { status = EMBER_ZCL_STATUS_FAILURE; goto send_default_response; } status = colorControlReadCurrentHue(emberAfCurrentEndpoint(), ¤tHue); if (status != EMBER_ZCL_STATUS_SUCCESS) { goto send_default_response; } // Add or subtract the step size to/from the current hue to get the final hue. // However, the range for hues is 0x00 to 0xFE, so if we land on 0xFF or if we // roll past it, we have to add or subtract one to the final hue, effectively // skipping over the invalid 0xFF. switch (stepMode) { case EMBER_ZCL_HUE_STEP_MODE_UP: state->hueMoveToLevel = currentHue + stepSize; if (stepSize == 0xFF - currentHue) { state->hueMoveToLevel++; } state->hueMoveDirection = true; break; case EMBER_ZCL_HUE_STEP_MODE_DOWN: state->hueMoveToLevel = currentHue - stepSize; if (stepSize == currentHue - 1) { state->hueMoveToLevel--; } state->hueMoveDirection = false; break; default: status = EMBER_ZCL_STATUS_INVALID_FIELD; goto send_default_response; } // If the color specified is not achievable by the hardware, then the // color shall not be set and a ZCL default response command shall be // generated with status code equal to INVALID_VALUE. status = colorControlReadCurrentSaturation(emberAfCurrentEndpoint(), ¤tSaturation); if (status != EMBER_ZCL_STATUS_SUCCESS) { goto send_default_response; } if(!emberAfPluginColorControlIsColorSupportedCallback(state->hueMoveToLevel, currentSaturation) || stepSize == 0 ) { status = EMBER_ZCL_STATUS_INVALID_VALUE; goto send_default_response; } state->commandId = ZCL_STEP_HUE_COMMAND_ID; state->transitionTime = transitionTime * MILLISECOND_TICKS_PER_SECOND / 10; state->elapsedTime = 0; state->eventDuration = state->transitionTime / stepSize; state->active = true; // Set the Color Mode attribute to zero as per the spec, ignoring any errors. colorControlSetColorModeToZero(); //schedule the next tick if (emberAfScheduleServerTick(emberAfCurrentEndpoint(), ZCL_COLOR_CONTROL_CLUSTER_ID, state->eventDuration) != EMBER_SUCCESS) status = EMBER_ZCL_STATUS_SOFTWARE_FAILURE; else status = EMBER_ZCL_STATUS_SUCCESS; goto send_default_response; send_default_response: emberAfSendImmediateDefaultResponse(status); return true; }
// Move saturation to a given saturation, taking transitionTime until completed. bool emberAfColorControlClusterMoveToSaturationCallback(uint8_t saturation, uint16_t transitionTime) { ColorControlState *state = getColorControlState(emberAfCurrentEndpoint()); EmberAfStatus status; uint8_t currentHue, currentSaturation; emberAfColorControlClusterPrintln("ColorControl: MoveToSaturation (%x, %2x)", saturation, transitionTime); if (state == NULL) { status = EMBER_ZCL_STATUS_FAILURE; goto send_default_response; } // If the color specified is not achievable by the hardware, then the // color shall not be set and a ZCL default response command shall be // generated with status code equal to INVALID_VALUE. status = colorControlReadCurrentHue(emberAfCurrentEndpoint(), ¤tHue); if (status != EMBER_ZCL_STATUS_SUCCESS) { goto send_default_response; } if(!emberAfPluginColorControlIsColorSupportedCallback(currentHue, saturation)) { status = EMBER_ZCL_STATUS_INVALID_VALUE; goto send_default_response; } status = colorControlReadCurrentSaturation(emberAfCurrentEndpoint(), ¤tSaturation); if (status != EMBER_ZCL_STATUS_SUCCESS) { goto send_default_response; } // Nothing to do, prevent divide-by-zero if ( saturation == currentSaturation ) { status = EMBER_ZCL_STATUS_SUCCESS; goto send_default_response; } state->commandId = ZCL_MOVE_TO_SATURATION_COMMAND_ID; state->elapsedTime = 0; state->saturationMoveToLevel = saturation; state->satMoveDirection = (saturation > currentSaturation ? true : false); state->eventDuration = state->transitionTime / ((state->satMoveDirection) ? (saturation - currentSaturation) : (currentSaturation - saturation)); // Set the Color Mode attribute to zero as per the spec, ignoring any errors. colorControlSetColorModeToZero(); //schedule the next tick if (emberAfScheduleServerTick(emberAfCurrentEndpoint(), ZCL_COLOR_CONTROL_CLUSTER_ID, state->eventDuration) != EMBER_SUCCESS) status = EMBER_ZCL_STATUS_SOFTWARE_FAILURE; else status = EMBER_ZCL_STATUS_SUCCESS; goto send_default_response; send_default_response: emberAfSendImmediateDefaultResponse(status); return true; }
// Move hue and saturation to a given values, taking time as specified. bool emberAfColorControlClusterMoveToHueAndSaturationCallback(uint8_t hue, uint8_t saturation, uint16_t transitionTime) { ColorControlState *state = getColorControlState(emberAfCurrentEndpoint()); EmberAfStatus status; uint8_t currentSaturation, currentHue, hueDiff, satDiff; emberAfColorControlClusterPrintln("ColorControl: MoveToHueAndSaturation (%x, %x, %2x)", hue, saturation, transitionTime); if (state == NULL) { status = EMBER_ZCL_STATUS_FAILURE; goto send_default_response; } // If the color specified is not achievable by the hardware, then the // color shall not be set and a ZCL default response command shall be // generated with status code equal to INVALID_VALUE. if (!emberAfPluginColorControlIsColorSupportedCallback(hue, saturation)) { status = EMBER_ZCL_STATUS_INVALID_VALUE; goto send_default_response; } status = colorControlReadCurrentSaturation(emberAfCurrentEndpoint(), ¤tSaturation); if (status != EMBER_ZCL_STATUS_SUCCESS) { goto send_default_response; } status = colorControlReadCurrentHue(emberAfCurrentEndpoint(), ¤tHue); if (status != EMBER_ZCL_STATUS_SUCCESS) { goto send_default_response; } state->commandId = ZCL_MOVE_TO_HUE_AND_SATURATION_COMMAND_ID; state->hueMoveToLevel = hue; state->hueMoveDirection = (hue - currentHue <= 127 ? true : false); hueDiff = (state->hueMoveDirection ? hue - currentHue : currentHue - hue); state->saturationMoveToLevel = saturation; state->satMoveDirection = (saturation - currentSaturation <= 127 ? true : false); satDiff = (state->satMoveDirection ? saturation - currentSaturation : currentSaturation - saturation); if (hueDiff == 0) { return emberAfColorControlClusterMoveToSaturationCallback(saturation, transitionTime); } else if (satDiff == 0) { return emberAfColorControlClusterMoveToHueCallback(hue, EMBER_ZCL_HUE_DIRECTION_SHORTEST_DISTANCE, transitionTime); } state->elapsedTime = 0; state->acceleratedHue = hueDiff > satDiff; state->acceleratedMoveRate = (state->acceleratedHue ? hueDiff / satDiff : satDiff / hueDiff); state->transitionTime = transitionTime * MILLISECOND_TICKS_PER_SECOND / 10; state->eventDuration = state->transitionTime / ((state->acceleratedHue) ? hueDiff : satDiff); // Set the Color Mode attribute to zero as per the spec, ignoring any errors. colorControlSetColorModeToZero(); state->active = true; //schedule the next tick if (emberAfScheduleServerTick(emberAfCurrentEndpoint(), ZCL_COLOR_CONTROL_CLUSTER_ID, state->eventDuration) != EMBER_SUCCESS) status = EMBER_ZCL_STATUS_SOFTWARE_FAILURE; else status = EMBER_ZCL_STATUS_SUCCESS; goto send_default_response; send_default_response: emberAfSendImmediateDefaultResponse(status); return true; }
// Step sat by one step, taking time as specified. bool emberAfColorControlClusterStepSaturationCallback(uint8_t stepMode, uint8_t stepSize, uint8_t transitionTime) { ColorControlState *state = getColorControlState(emberAfCurrentEndpoint()); EmberAfStatus status; uint8_t currentHue, currentSaturation; emberAfColorControlClusterPrintln("ColorControl: StepSaturation (%x, %x, %x)", stepMode, stepSize, transitionTime); if (state == NULL) { status = EMBER_ZCL_STATUS_FAILURE; goto send_default_response; } status = colorControlReadCurrentSaturation(emberAfCurrentEndpoint(), ¤tSaturation); if (status != EMBER_ZCL_STATUS_SUCCESS) { goto send_default_response; } // Add or subtract the step size to/from the current saturation to get the // final saturation. However, the range for saturations is 0x00 to 0xFE, so // don't increment or decrement past those bounds. switch (stepMode) { case EMBER_ZCL_SATURATION_STEP_MODE_UP: state->saturationMoveToLevel = (stepSize > 0xFE - currentSaturation ? 0xFE : currentSaturation + stepSize); state->satMoveDirection = true; break; case EMBER_ZCL_SATURATION_STEP_MODE_DOWN: state->saturationMoveToLevel = (stepSize > currentSaturation ? 0x00 : currentSaturation - stepSize); state->satMoveDirection = false; break; default: status = EMBER_ZCL_STATUS_INVALID_FIELD; goto send_default_response; } // If the color specified is not achievable by the hardware, then the // color shall not be set and a ZCL default response command shall be // generated with status code equal to INVALID_VALUE. status = colorControlReadCurrentHue(emberAfCurrentEndpoint(), ¤tHue); if (status != EMBER_ZCL_STATUS_SUCCESS) { goto send_default_response; } if(!emberAfPluginColorControlIsColorSupportedCallback(currentHue, state->saturationMoveToLevel) || stepSize == 0 ) { status = EMBER_ZCL_STATUS_INVALID_VALUE; goto send_default_response; } state->commandId = ZCL_STEP_SATURATION_COMMAND_ID; state->transitionTime = transitionTime * MILLISECOND_TICKS_PER_SECOND / 10; state->elapsedTime = 0; state->eventDuration = state->transitionTime / stepSize; state->active = true; // Set the Color Mode attribute to zero as per the spec, ignoring any errors. colorControlSetColorModeToZero(); //schedule the next tick if (emberAfScheduleServerTick(emberAfCurrentEndpoint(), ZCL_COLOR_CONTROL_CLUSTER_ID, state->eventDuration) != EMBER_SUCCESS) status = EMBER_ZCL_STATUS_SOFTWARE_FAILURE; else status = EMBER_ZCL_STATUS_SUCCESS; goto send_default_response; send_default_response: emberAfSendImmediateDefaultResponse(status); return true; }
// Move sat continuously at the given rate. If mode is stop, then stop. bool emberAfColorControlClusterMoveSaturationCallback(uint8_t moveMode, uint8_t rate) { ColorControlState *state = getColorControlState(emberAfCurrentEndpoint()); EmberAfStatus status; emberAfColorControlClusterPrintln("ColorControl: MoveSaturation (%x, %x)", moveMode, rate); if (state == NULL) { status = EMBER_ZCL_STATUS_FAILURE; goto send_default_response; } // If the rate is set to 0, then the command shall have no effect // and a ZCL default response command shall be generated with status // code equal to INVALID_VALUE. if (rate == 0) { status = EMBER_ZCL_STATUS_INVALID_FIELD; goto send_default_response; } state->active = true; switch (moveMode) { case EMBER_ZCL_SATURATION_MOVE_MODE_STOP: state->active = false; emberAfDeactivateServerTick(emberAfCurrentEndpoint(), ZCL_COLOR_CONTROL_CLUSTER_ID); #ifdef ZCL_USING_COLOR_CONTROL_CLUSTER_COLOR_CONTROL_REMAINING_TIME_ATTRIBUTE colorControlClearRemainingTime(emberAfCurrentEndpoint()); #endif // ZCL_USING_COLOR_CONTROL_CLUSTER_COLOR_CONTROL_REMAINING_TIME_ATTRIBUTE status = EMBER_ZCL_STATUS_SUCCESS; goto send_default_response; case EMBER_ZCL_SATURATION_MOVE_MODE_UP: state->satMoveDirection = true; break; case EMBER_ZCL_SATURATION_MOVE_MODE_DOWN: state->satMoveDirection = false; break; default: status = EMBER_ZCL_STATUS_INVALID_FIELD; goto send_default_response; } state->commandId = ZCL_MOVE_SATURATION_COMMAND_ID; if (state->active) { state->eventDuration = MILLISECOND_TICKS_PER_SECOND / rate; } // Set the Color Mode attribute to zero as per the spec, ignoring any errors. colorControlSetColorModeToZero(); //schedule the next tick if (emberAfScheduleServerTick(emberAfCurrentEndpoint(), ZCL_COLOR_CONTROL_CLUSTER_ID, state->eventDuration) != EMBER_SUCCESS) status = EMBER_ZCL_STATUS_SOFTWARE_FAILURE; else status = EMBER_ZCL_STATUS_SUCCESS; goto send_default_response; send_default_response: emberAfSendImmediateDefaultResponse(status); return true; }