3 * Copyright (c) 2020 Project CHIP Authors
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
20 * Copyright (c) 2020 Silicon Labs
22 * Licensed under the Apache License, Version 2.0 (the "License");
23 * you may not use this file except in compliance with the License.
24 * You may obtain a copy of the License at
26 * http://www.apache.org/licenses/LICENSE-2.0
28 * Unless required by applicable law or agreed to in writing, software
29 * distributed under the License is distributed on an "AS IS" BASIS,
30 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
31 * See the License for the specific language governing permissions and
32 * limitations under the License.
34 /****************************************************************************
36 * @brief Routines for the Color Control Server
38 *******************************************************************************
39 ******************************************************************************/
40 #include "color-control-server.h"
42 #include <app/util/af.h>
44 #include <app/reporting/reporting.h>
45 #include <app/util/af-event.h>
46 #include <app/util/attribute-storage.h>
49 #include "gen/af-structs.h"
50 #include "gen/attribute-id.h"
51 #include "gen/attribute-type.h"
52 #include "gen/cluster-id.h"
56 #define COLOR_TEMP_CONTROL emberAfPluginColorControlServerTempTransitionEventControl
57 #define COLOR_XY_CONTROL emberAfPluginColorControlServerXyTransitionEventControl
58 #define COLOR_HSV_CONTROL emberAfPluginColorControlServerHueSatTransitionEventControl
63 MOVE_MODE_STOP = 0x00,
70 COLOR_MODE_HSV = 0x00,
71 COLOR_MODE_CIE_XY = 0x01,
72 COLOR_MODE_TEMPERATURE = 0x02
79 HSV_TO_TEMPERATURE = 0x02,
81 CIE_XY_TO_CIE_XY = 0x11,
82 CIE_XY_TO_TEMPERATURE = 0x12,
83 TEMPERATURE_TO_HSV = 0x20,
84 TEMPERATURE_TO_CIE_XY = 0x21,
85 TEMPERATURE_TO_TEMPERATURE = 0x22
88 EmberEventControl emberAfPluginColorControlServerTempTransitionEventControl;
89 EmberEventControl emberAfPluginColorControlServerXyTransitionEventControl;
90 EmberEventControl emberAfPluginColorControlServerHueSatTransitionEventControl;
92 #define UPDATE_TIME_MS 100
93 #define TRANSITION_TIME_1S 10
94 #define MIN_CIE_XY_VALUE 0
95 // this value comes directly from the ZCL specification table 5.3
96 #define MAX_CIE_XY_VALUE 0xfeff
97 #define MIN_TEMPERATURE_VALUE 0
98 #define MAX_TEMPERATURE_VALUE 0xfeff
99 #define MIN_HUE_VALUE 0
100 #define MAX_HUE_VALUE 254
101 #define MIN_SATURATION_VALUE 0
102 #define MAX_SATURATION_VALUE 254
103 #define HALF_MAX_UINT8T 127
105 #define MIN_CURRENT_LEVEL 0x01
106 #define MAX_CURRENT_LEVEL 0xFE
108 #define REPORT_FAILED 0xFF
115 uint16_t stepsRemaining;
120 } ColorHueTransitionState;
122 static ColorHueTransitionState colorHueTransitionState;
126 uint16_t initialValue;
127 uint16_t currentValue;
129 uint16_t stepsRemaining;
134 } Color16uTransitionState;
136 static Color16uTransitionState colorXTransitionState;
137 static Color16uTransitionState colorYTransitionState;
139 static Color16uTransitionState colorTempTransitionState;
141 static Color16uTransitionState colorSaturationTransitionState;
143 // Forward declarations:
144 static bool computeNewColor16uValue(Color16uTransitionState * p);
145 static void stopAllColorTransitions(void);
146 static void handleModeSwitch(EndpointId endpoint, uint8_t newColorMode);
147 static bool shouldExecuteIfOff(EndpointId endpoint, uint8_t optionMask, uint8_t optionOverride);
149 #ifdef EMBER_AF_PLUGIN_COLOR_CONTROL_SERVER_HSV
150 static uint8_t addHue(uint8_t hue1, uint8_t hue2);
151 static uint8_t subtractHue(uint8_t hue1, uint8_t hue2);
152 static uint8_t addSaturation(uint8_t saturation1, uint8_t saturation2);
153 static uint8_t subtractSaturation(uint8_t saturation1, uint8_t saturation2);
154 static void initHueSat(EndpointId endpoint);
155 static uint8_t readHue(EndpointId endpoint);
156 static uint8_t readSaturation(EndpointId endpoint);
159 #ifdef EMBER_AF_PLUGIN_COLOR_CONTROL_SERVER_XY
160 static uint16_t findNewColorValueFromStep(uint16_t oldValue, int16_t step);
161 static uint16_t readColorX(EndpointId endpoint);
162 static uint16_t readColorY(EndpointId endpoint);
165 static uint16_t computeTransitionTimeFromStateAndRate(Color16uTransitionState * p, uint16_t rate);
167 // convenient token handling functions
168 static uint8_t readColorMode(EndpointId endpoint)
173 EmberAfStatus status =
175 emberAfReadServerAttribute(endpoint, ZCL_COLOR_CONTROL_CLUSTER_ID, ZCL_COLOR_CONTROL_COLOR_MODE_ATTRIBUTE_ID,
176 (uint8_t *) &colorMode, sizeof(uint8_t));
177 assert(status == EMBER_ZCL_STATUS_SUCCESS);
182 static uint16_t readColorTemperature(EndpointId endpoint)
184 uint16_t colorTemperature;
187 EmberAfStatus status =
189 emberAfReadServerAttribute(endpoint, ZCL_COLOR_CONTROL_CLUSTER_ID, ZCL_COLOR_CONTROL_COLOR_TEMPERATURE_ATTRIBUTE_ID,
190 (uint8_t *) &colorTemperature, sizeof(uint16_t));
191 assert(status == EMBER_ZCL_STATUS_SUCCESS);
193 return colorTemperature;
196 static uint16_t readColorTemperatureMin(EndpointId endpoint)
198 uint16_t colorTemperatureMin;
202 emberAfReadServerAttribute(endpoint, ZCL_COLOR_CONTROL_CLUSTER_ID, ZCL_COLOR_CONTROL_COLOR_TEMP_PHYSICAL_MIN_ATTRIBUTE_ID,
203 (uint8_t *) &colorTemperatureMin, sizeof(uint16_t));
205 if (status != EMBER_ZCL_STATUS_SUCCESS)
207 colorTemperatureMin = MIN_TEMPERATURE_VALUE;
210 return colorTemperatureMin;
213 static uint16_t readColorTemperatureMax(EndpointId endpoint)
215 uint16_t colorTemperatureMax;
219 emberAfReadServerAttribute(endpoint, ZCL_COLOR_CONTROL_CLUSTER_ID, ZCL_COLOR_CONTROL_COLOR_TEMP_PHYSICAL_MAX_ATTRIBUTE_ID,
220 (uint8_t *) &colorTemperatureMax, sizeof(uint16_t));
222 if (status != EMBER_ZCL_STATUS_SUCCESS)
224 colorTemperatureMax = MAX_TEMPERATURE_VALUE;
227 return colorTemperatureMax;
230 static uint16_t readColorTemperatureCoupleToLevelMin(EndpointId endpoint)
232 uint16_t colorTemperatureCoupleToLevelMin;
235 status = emberAfReadServerAttribute(endpoint, ZCL_COLOR_CONTROL_CLUSTER_ID,
236 ZCL_COLOR_CONTROL_TEMPERATURE_LEVEL_MIN_MIREDS_ATTRIBUTE_ID,
237 (uint8_t *) &colorTemperatureCoupleToLevelMin, sizeof(uint16_t));
239 if (status != EMBER_ZCL_STATUS_SUCCESS)
241 // Not less than the physical min.
242 colorTemperatureCoupleToLevelMin = readColorTemperatureMin(endpoint);
245 return colorTemperatureCoupleToLevelMin;
248 static uint8_t readLevelControlCurrentLevel(EndpointId endpoint)
250 uint8_t currentLevel;
253 status = emberAfReadServerAttribute(endpoint, ZCL_LEVEL_CONTROL_CLUSTER_ID, ZCL_CURRENT_LEVEL_ATTRIBUTE_ID,
254 (uint8_t *) ¤tLevel, sizeof(uint8_t));
256 if (status != EMBER_ZCL_STATUS_SUCCESS)
258 currentLevel = 0x7F; // midpoint of range 0x01-0xFE
264 static void writeRemainingTime(EndpointId endpoint, uint16_t remainingTime)
267 EmberAfStatus status =
269 emberAfWriteServerAttribute(endpoint, ZCL_COLOR_CONTROL_CLUSTER_ID, ZCL_COLOR_CONTROL_REMAINING_TIME_ATTRIBUTE_ID,
270 (uint8_t *) &remainingTime, ZCL_INT16U_ATTRIBUTE_TYPE);
271 assert(status == EMBER_ZCL_STATUS_SUCCESS);
274 static void writeColorMode(EndpointId endpoint, uint8_t colorMode)
277 EmberAfStatus status =
279 emberAfWriteServerAttribute(endpoint, ZCL_COLOR_CONTROL_CLUSTER_ID, ZCL_COLOR_CONTROL_ENHANCED_COLOR_MODE_ATTRIBUTE_ID,
280 (uint8_t *) &colorMode, ZCL_INT8U_ATTRIBUTE_TYPE);
281 assert(status == EMBER_ZCL_STATUS_SUCCESS);
286 emberAfWriteServerAttribute(endpoint, ZCL_COLOR_CONTROL_CLUSTER_ID, ZCL_COLOR_CONTROL_COLOR_MODE_ATTRIBUTE_ID,
287 (uint8_t *) &colorMode, ZCL_INT8U_ATTRIBUTE_TYPE);
288 assert(status == EMBER_ZCL_STATUS_SUCCESS);
291 static void writeHue(EndpointId endpoint, uint8_t hue)
294 EmberAfStatus status =
296 emberAfWriteServerAttribute(endpoint, ZCL_COLOR_CONTROL_CLUSTER_ID, ZCL_COLOR_CONTROL_CURRENT_HUE_ATTRIBUTE_ID,
297 (uint8_t *) &hue, ZCL_INT8U_ATTRIBUTE_TYPE);
298 assert(status == EMBER_ZCL_STATUS_SUCCESS);
301 static void writeSaturation(EndpointId endpoint, uint8_t saturation)
304 EmberAfStatus status =
306 emberAfWriteServerAttribute(endpoint, ZCL_COLOR_CONTROL_CLUSTER_ID, ZCL_COLOR_CONTROL_CURRENT_SATURATION_ATTRIBUTE_ID,
307 (uint8_t *) &saturation, ZCL_INT8U_ATTRIBUTE_TYPE);
308 assert(status == EMBER_ZCL_STATUS_SUCCESS);
311 static void writeColorX(EndpointId endpoint, uint16_t colorX)
314 EmberAfStatus status =
316 emberAfWriteServerAttribute(endpoint, ZCL_COLOR_CONTROL_CLUSTER_ID, ZCL_COLOR_CONTROL_CURRENT_X_ATTRIBUTE_ID,
317 (uint8_t *) &colorX, ZCL_INT16U_ATTRIBUTE_TYPE);
318 assert(status == EMBER_ZCL_STATUS_SUCCESS);
321 static void writeColorY(EndpointId endpoint, uint16_t colorY)
324 EmberAfStatus status =
326 emberAfWriteServerAttribute(endpoint, ZCL_COLOR_CONTROL_CLUSTER_ID, ZCL_COLOR_CONTROL_CURRENT_Y_ATTRIBUTE_ID,
327 (uint8_t *) &colorY, ZCL_INT16U_ATTRIBUTE_TYPE);
328 assert(status == EMBER_ZCL_STATUS_SUCCESS);
331 static void writeColorTemperature(EndpointId endpoint, uint16_t colorTemperature)
334 EmberAfStatus status =
336 emberAfWriteServerAttribute(endpoint, ZCL_COLOR_CONTROL_CLUSTER_ID, ZCL_COLOR_CONTROL_COLOR_TEMPERATURE_ATTRIBUTE_ID,
337 (uint8_t *) &colorTemperature, ZCL_INT16U_ATTRIBUTE_TYPE);
338 assert(status == EMBER_ZCL_STATUS_SUCCESS);
341 // -------------------------------------------------------------------------
342 // ****** callback section *******
344 #ifdef EMBER_AF_PLUGIN_COLOR_CONTROL_SERVER_HSV
345 /** @brief Move To Hue And Saturation
349 * @param hue Ver.: always
350 * @param saturation Ver.: always
351 * @param transitionTime Ver.: always
353 bool emberAfColorControlClusterMoveToHueAndSaturationCallback(uint8_t hue, uint8_t saturation, uint16_t transitionTime,
354 uint8_t optionsMask, uint8_t optionsOverride)
356 EndpointId endpoint = emberAfCurrentEndpoint();
357 uint8_t currentHue = readHue(endpoint);
360 if (transitionTime == 0)
365 // limit checking: hue and saturation are 0..254. Spec dictates we ignore
366 // this and report a malformed packet.
367 if (hue > MAX_HUE_VALUE || saturation > MAX_SATURATION_VALUE)
369 emberAfSendImmediateDefaultResponse(EMBER_ZCL_STATUS_MALFORMED_COMMAND);
373 if (!shouldExecuteIfOff(endpoint, optionsMask, optionsOverride))
375 emberAfSendImmediateDefaultResponse(EMBER_ZCL_STATUS_SUCCESS);
379 // compute shortest direction
380 if (hue > currentHue)
382 moveUp = (hue - currentHue) < HALF_MAX_UINT8T;
386 moveUp = (currentHue - hue) > HALF_MAX_UINT8T;
389 // New command. Need to stop any active transitions.
390 stopAllColorTransitions();
392 // Handle color mode transition, if necessary.
393 handleModeSwitch(endpoint, COLOR_MODE_HSV);
395 // now, kick off the state machine.
396 initHueSat(endpoint);
398 colorHueTransitionState.initialHue = currentHue;
399 colorHueTransitionState.currentHue = currentHue;
400 colorHueTransitionState.finalHue = hue;
401 colorHueTransitionState.stepsRemaining = transitionTime;
402 colorHueTransitionState.stepsTotal = transitionTime;
403 colorHueTransitionState.endpoint = endpoint;
404 colorHueTransitionState.up = moveUp;
405 colorHueTransitionState.repeat = false;
407 colorSaturationTransitionState.initialValue = readSaturation(endpoint);
408 colorSaturationTransitionState.currentValue = readSaturation(endpoint);
409 colorSaturationTransitionState.finalValue = saturation;
410 colorSaturationTransitionState.stepsRemaining = transitionTime;
411 colorSaturationTransitionState.stepsTotal = transitionTime;
412 colorSaturationTransitionState.endpoint = endpoint;
413 colorSaturationTransitionState.lowLimit = MIN_SATURATION_VALUE;
414 colorSaturationTransitionState.highLimit = MAX_SATURATION_VALUE;
416 writeRemainingTime(endpoint, transitionTime);
418 // kick off the state machine:
419 emberEventControlSetDelayMS(&COLOR_HSV_CONTROL, UPDATE_TIME_MS);
421 emberAfSendImmediateDefaultResponse(EMBER_ZCL_STATUS_SUCCESS);
425 bool emberAfColorControlClusterMoveHueCallback(uint8_t moveMode, uint8_t rate, uint8_t optionsMask, uint8_t optionsOverride)
427 EndpointId endpoint = emberAfCurrentEndpoint();
429 if (!shouldExecuteIfOff(endpoint, optionsMask, optionsOverride))
431 emberAfSendImmediateDefaultResponse(EMBER_ZCL_STATUS_SUCCESS);
435 // New command. Need to stop any active transitions.
436 stopAllColorTransitions();
438 if (moveMode == EMBER_ZCL_HUE_MOVE_MODE_STOP)
440 emberAfSendImmediateDefaultResponse(EMBER_ZCL_STATUS_SUCCESS);
444 // Handle color mode transition, if necessary.
445 handleModeSwitch(endpoint, COLOR_MODE_HSV);
447 // now, kick off the state machine.
448 initHueSat(endpoint);
450 colorHueTransitionState.initialHue = readHue(endpoint);
451 colorHueTransitionState.currentHue = readHue(endpoint);
452 if (moveMode == EMBER_ZCL_HUE_MOVE_MODE_UP)
454 colorHueTransitionState.finalHue = addHue(readHue(endpoint), rate);
455 colorHueTransitionState.up = true;
457 else if (moveMode == EMBER_ZCL_HUE_MOVE_MODE_DOWN)
459 colorHueTransitionState.finalHue = subtractHue(readHue(endpoint), rate);
460 colorHueTransitionState.up = false;
464 emberAfSendImmediateDefaultResponse(EMBER_ZCL_STATUS_MALFORMED_COMMAND);
467 colorHueTransitionState.stepsRemaining = TRANSITION_TIME_1S;
468 colorHueTransitionState.stepsTotal = TRANSITION_TIME_1S;
469 colorHueTransitionState.endpoint = endpoint;
470 colorHueTransitionState.repeat = true;
471 // hue movement can last forever. Indicate this with a remaining time of
473 writeRemainingTime(endpoint, MAX_INT16U_VALUE);
475 colorSaturationTransitionState.stepsRemaining = 0;
477 // kick off the state machine:
478 emberEventControlSetDelayMS(&COLOR_HSV_CONTROL, UPDATE_TIME_MS);
480 emberAfSendImmediateDefaultResponse(EMBER_ZCL_STATUS_SUCCESS);
484 bool emberAfColorControlClusterMoveSaturationCallback(uint8_t moveMode, uint8_t rate, uint8_t optionsMask, uint8_t optionsOverride)
486 EndpointId endpoint = emberAfCurrentEndpoint();
488 if (!shouldExecuteIfOff(endpoint, optionsMask, optionsOverride))
490 emberAfSendImmediateDefaultResponse(EMBER_ZCL_STATUS_SUCCESS);
494 uint16_t transitionTime;
496 // New command. Need to stop any active transitions.
497 stopAllColorTransitions();
499 if (moveMode == EMBER_ZCL_SATURATION_MOVE_MODE_STOP || rate == 0)
501 emberAfSendImmediateDefaultResponse(EMBER_ZCL_STATUS_SUCCESS);
505 // Handle color mode transition, if necessary.
506 handleModeSwitch(endpoint, COLOR_MODE_HSV);
508 // now, kick off the state machine.
509 initHueSat(endpoint);
511 colorHueTransitionState.stepsRemaining = 0;
513 colorSaturationTransitionState.initialValue = readSaturation(endpoint);
514 colorSaturationTransitionState.currentValue = readSaturation(endpoint);
515 if (moveMode == EMBER_ZCL_SATURATION_MOVE_MODE_UP)
517 colorSaturationTransitionState.finalValue = MAX_SATURATION_VALUE;
521 colorSaturationTransitionState.finalValue = MIN_SATURATION_VALUE;
524 transitionTime = computeTransitionTimeFromStateAndRate(&colorSaturationTransitionState, rate);
526 colorSaturationTransitionState.stepsRemaining = transitionTime;
527 colorSaturationTransitionState.stepsTotal = transitionTime;
528 colorSaturationTransitionState.endpoint = endpoint;
529 colorSaturationTransitionState.lowLimit = MIN_SATURATION_VALUE;
530 colorSaturationTransitionState.highLimit = MAX_SATURATION_VALUE;
532 writeRemainingTime(endpoint, transitionTime);
534 // kick off the state machine:
535 emberEventControlSetDelayMS(&COLOR_HSV_CONTROL, UPDATE_TIME_MS);
537 emberAfSendImmediateDefaultResponse(EMBER_ZCL_STATUS_SUCCESS);
541 bool emberAfColorControlClusterMoveToHueCallback(uint8_t hue, uint8_t hueMoveMode, uint16_t transitionTime, uint8_t optionsMask,
542 uint8_t optionsOverride)
544 EndpointId endpoint = emberAfCurrentEndpoint();
546 if (!shouldExecuteIfOff(endpoint, optionsMask, optionsOverride))
548 emberAfSendImmediateDefaultResponse(EMBER_ZCL_STATUS_SUCCESS);
552 uint8_t currentHue = readHue(endpoint);
555 if (transitionTime == 0)
560 // limit checking: hue and saturation are 0..254. Spec dictates we ignore
561 // this and report a malformed packet.
562 if (hue > MAX_HUE_VALUE)
564 emberAfSendImmediateDefaultResponse(EMBER_ZCL_STATUS_MALFORMED_COMMAND);
568 // For move to hue, the move modes are different from the other move commands.
569 // Need to translate from the move to hue transitions to the internal
573 case EMBER_ZCL_HUE_DIRECTION_SHORTEST_DISTANCE:
574 if (((uint8_t)(currentHue - hue)) > HALF_MAX_UINT8T)
576 direction = MOVE_MODE_UP;
580 direction = MOVE_MODE_DOWN;
583 case EMBER_ZCL_HUE_DIRECTION_LONGEST_DISTANCE:
584 if (((uint8_t)(currentHue - hue)) > HALF_MAX_UINT8T)
586 direction = MOVE_MODE_DOWN;
590 direction = MOVE_MODE_UP;
594 case EMBER_ZCL_HUE_DIRECTION_UP:
595 direction = MOVE_MODE_UP;
597 case EMBER_ZCL_HUE_DIRECTION_DOWN:
598 direction = MOVE_MODE_DOWN;
601 emberAfSendImmediateDefaultResponse(EMBER_ZCL_STATUS_MALFORMED_COMMAND);
605 // New command. Need to stop any active transitions.
606 stopAllColorTransitions();
608 // Handle color mode transition, if necessary.
609 handleModeSwitch(endpoint, COLOR_MODE_HSV);
611 // now, kick off the state machine.
612 initHueSat(endpoint);
614 colorHueTransitionState.initialHue = readHue(endpoint);
615 colorHueTransitionState.currentHue = readHue(endpoint);
616 colorHueTransitionState.finalHue = hue;
617 colorHueTransitionState.stepsRemaining = transitionTime;
618 colorHueTransitionState.stepsTotal = transitionTime;
619 colorHueTransitionState.endpoint = endpoint;
620 colorHueTransitionState.up = (direction == MOVE_MODE_UP);
621 colorHueTransitionState.repeat = false;
623 colorSaturationTransitionState.stepsRemaining = 0;
625 writeRemainingTime(endpoint, transitionTime);
627 // kick off the state machine:
628 emberEventControlSetDelayMS(&COLOR_HSV_CONTROL, UPDATE_TIME_MS);
630 emberAfSendImmediateDefaultResponse(EMBER_ZCL_STATUS_SUCCESS);
634 bool emberAfColorControlClusterMoveToSaturationCallback(uint8_t saturation, uint16_t transitionTime, uint8_t optionsMask,
635 uint8_t optionsOverride)
637 EndpointId endpoint = emberAfCurrentEndpoint();
639 if (!shouldExecuteIfOff(endpoint, optionsMask, optionsOverride))
641 emberAfSendImmediateDefaultResponse(EMBER_ZCL_STATUS_SUCCESS);
645 if (transitionTime == 0)
650 // limit checking: hue and saturation are 0..254. Spec dictates we ignore
651 // this and report a malformed packet.
652 if (saturation > MAX_SATURATION_VALUE)
654 emberAfSendImmediateDefaultResponse(EMBER_ZCL_STATUS_MALFORMED_COMMAND);
658 // New command. Need to stop any active transitions.
659 stopAllColorTransitions();
661 // Handle color mode transition, if necessary.
662 handleModeSwitch(endpoint, COLOR_MODE_HSV);
664 // now, kick off the state machine.
665 initHueSat(endpoint);
667 colorHueTransitionState.stepsRemaining = 0;
669 colorSaturationTransitionState.initialValue = readSaturation(endpoint);
670 colorSaturationTransitionState.currentValue = readSaturation(endpoint);
671 colorSaturationTransitionState.finalValue = saturation;
672 colorSaturationTransitionState.stepsRemaining = transitionTime;
673 colorSaturationTransitionState.stepsTotal = transitionTime;
674 colorSaturationTransitionState.endpoint = endpoint;
675 colorSaturationTransitionState.lowLimit = MIN_SATURATION_VALUE;
676 colorSaturationTransitionState.highLimit = MAX_SATURATION_VALUE;
678 writeRemainingTime(endpoint, transitionTime);
680 // kick off the state machine:
681 emberEventControlSetDelayMS(&COLOR_HSV_CONTROL, UPDATE_TIME_MS);
683 emberAfSendImmediateDefaultResponse(EMBER_ZCL_STATUS_SUCCESS);
687 bool emberAfColorControlClusterStepHueCallback(uint8_t stepMode, uint8_t stepSize, uint8_t transitionTime, uint8_t optionsMask,
688 uint8_t optionsOverride)
690 EndpointId endpoint = emberAfCurrentEndpoint();
692 if (!shouldExecuteIfOff(endpoint, optionsMask, optionsOverride))
694 emberAfSendImmediateDefaultResponse(EMBER_ZCL_STATUS_SUCCESS);
698 uint8_t currentHue = readHue(endpoint);
700 if (transitionTime == 0)
705 // New command. Need to stop any active transitions.
706 stopAllColorTransitions();
708 if (stepMode == MOVE_MODE_STOP)
710 emberAfSendImmediateDefaultResponse(EMBER_ZCL_STATUS_SUCCESS);
714 // Handle color mode transition, if necessary.
715 handleModeSwitch(endpoint, COLOR_MODE_HSV);
717 // now, kick off the state machine.
718 initHueSat(endpoint);
720 colorHueTransitionState.initialHue = currentHue;
721 colorHueTransitionState.currentHue = currentHue;
723 if (stepMode == MOVE_MODE_UP)
725 colorHueTransitionState.finalHue = addHue(currentHue, stepSize);
726 colorHueTransitionState.up = true;
730 colorHueTransitionState.finalHue = subtractHue(currentHue, stepSize);
731 colorHueTransitionState.up = false;
733 colorHueTransitionState.stepsRemaining = transitionTime;
734 colorHueTransitionState.stepsTotal = transitionTime;
735 colorHueTransitionState.endpoint = endpoint;
736 colorHueTransitionState.repeat = false;
738 colorSaturationTransitionState.stepsRemaining = 0;
740 writeRemainingTime(endpoint, transitionTime);
742 // kick off the state machine:
743 emberEventControlSetDelayMS(&COLOR_HSV_CONTROL, UPDATE_TIME_MS);
745 emberAfSendImmediateDefaultResponse(EMBER_ZCL_STATUS_SUCCESS);
749 bool emberAfColorControlClusterStepSaturationCallback(uint8_t stepMode, uint8_t stepSize, uint8_t transitionTime,
750 uint8_t optionsMask, uint8_t optionsOverride)
752 EndpointId endpoint = emberAfCurrentEndpoint();
754 if (!shouldExecuteIfOff(endpoint, optionsMask, optionsOverride))
756 emberAfSendImmediateDefaultResponse(EMBER_ZCL_STATUS_SUCCESS);
760 uint8_t currentSaturation = readSaturation(endpoint);
762 if (transitionTime == 0)
767 // New command. Need to stop any active transitions.
768 stopAllColorTransitions();
770 if (stepMode == MOVE_MODE_STOP)
772 emberAfSendImmediateDefaultResponse(EMBER_ZCL_STATUS_SUCCESS);
776 // Handle color mode transition, if necessary.
777 handleModeSwitch(endpoint, COLOR_MODE_HSV);
779 // now, kick off the state machine.
780 initHueSat(endpoint);
782 colorHueTransitionState.stepsRemaining = 0;
784 colorSaturationTransitionState.initialValue = currentSaturation;
785 colorSaturationTransitionState.currentValue = currentSaturation;
787 if (stepMode == MOVE_MODE_UP)
789 colorSaturationTransitionState.finalValue = addSaturation(currentSaturation, stepSize);
793 colorSaturationTransitionState.finalValue = subtractSaturation(currentSaturation, stepSize);
795 colorSaturationTransitionState.stepsRemaining = transitionTime;
796 colorSaturationTransitionState.stepsTotal = transitionTime;
797 colorSaturationTransitionState.endpoint = endpoint;
798 colorSaturationTransitionState.lowLimit = MIN_SATURATION_VALUE;
799 colorSaturationTransitionState.highLimit = MAX_SATURATION_VALUE;
801 writeRemainingTime(endpoint, transitionTime);
803 // kick off the state machine:
804 emberEventControlSetDelayMS(&COLOR_HSV_CONTROL, UPDATE_TIME_MS);
806 emberAfSendImmediateDefaultResponse(EMBER_ZCL_STATUS_SUCCESS);
810 static uint8_t addSaturation(uint8_t saturation1, uint8_t saturation2)
812 uint16_t saturation16;
814 saturation16 = ((uint16_t) saturation1);
815 saturation16 = static_cast<uint16_t>(saturation16 + static_cast<uint16_t>(saturation2));
817 if (saturation16 > MAX_SATURATION_VALUE)
819 saturation16 = MAX_SATURATION_VALUE;
822 return ((uint8_t) saturation16);
825 static uint8_t subtractSaturation(uint8_t saturation1, uint8_t saturation2)
827 if (saturation2 > saturation1)
832 return static_cast<uint8_t>(saturation1 - saturation2);
835 // any time we call a hue or saturation transition, we need to assume certain
836 // things about the hue and saturation data structures. This function will
837 // properly initialize them.
838 static void initHueSat(EndpointId endpoint)
840 colorHueTransitionState.stepsRemaining = 0;
841 colorHueTransitionState.currentHue = readHue(endpoint);
842 colorHueTransitionState.endpoint = endpoint;
844 colorSaturationTransitionState.stepsRemaining = 0;
845 colorSaturationTransitionState.currentValue = readSaturation(endpoint);
846 colorSaturationTransitionState.endpoint = endpoint;
849 static uint8_t readHue(EndpointId endpoint)
854 EmberAfStatus status =
856 emberAfReadServerAttribute(endpoint, ZCL_COLOR_CONTROL_CLUSTER_ID, ZCL_COLOR_CONTROL_CURRENT_HUE_ATTRIBUTE_ID,
857 (uint8_t *) &hue, sizeof(uint8_t));
858 assert(status == EMBER_ZCL_STATUS_SUCCESS);
863 static uint8_t readSaturation(EndpointId endpoint)
868 EmberAfStatus status =
870 emberAfReadServerAttribute(endpoint, ZCL_COLOR_CONTROL_CLUSTER_ID, ZCL_COLOR_CONTROL_CURRENT_SATURATION_ATTRIBUTE_ID,
871 (uint8_t *) &saturation, sizeof(uint8_t));
872 assert(status == EMBER_ZCL_STATUS_SUCCESS);
877 #endif // #ifdef EMBER_AF_PLUGIN_COLOR_CONTROL_SERVER_HSV
879 #ifdef EMBER_AF_PLUGIN_COLOR_CONTROL_SERVER_XY
881 bool emberAfColorControlClusterMoveToColorCallback(uint16_t colorX, uint16_t colorY, uint16_t transitionTime, uint8_t optionsMask,
882 uint8_t optionsOverride)
884 EndpointId endpoint = emberAfCurrentEndpoint();
886 if (!shouldExecuteIfOff(endpoint, optionsMask, optionsOverride))
888 emberAfSendImmediateDefaultResponse(EMBER_ZCL_STATUS_SUCCESS);
892 if (transitionTime == 0)
897 // New command. Need to stop any active transitions.
898 stopAllColorTransitions();
900 // Handle color mode transition, if necessary.
901 handleModeSwitch(endpoint, COLOR_MODE_CIE_XY);
903 // now, kick off the state machine.
904 colorXTransitionState.initialValue = readColorX(endpoint);
905 colorXTransitionState.currentValue = readColorX(endpoint);
906 colorXTransitionState.finalValue = colorX;
907 colorXTransitionState.stepsRemaining = transitionTime;
908 colorXTransitionState.stepsTotal = transitionTime;
909 colorXTransitionState.endpoint = endpoint;
910 colorXTransitionState.lowLimit = MIN_CIE_XY_VALUE;
911 colorXTransitionState.highLimit = MAX_CIE_XY_VALUE;
913 colorYTransitionState.initialValue = readColorY(endpoint);
914 colorYTransitionState.currentValue = readColorY(endpoint);
915 colorYTransitionState.finalValue = colorY;
916 colorYTransitionState.stepsRemaining = transitionTime;
917 colorYTransitionState.stepsTotal = transitionTime;
918 colorYTransitionState.endpoint = endpoint;
919 colorYTransitionState.lowLimit = MIN_CIE_XY_VALUE;
920 colorYTransitionState.highLimit = MAX_CIE_XY_VALUE;
922 writeRemainingTime(endpoint, transitionTime);
924 // kick off the state machine:
925 emberEventControlSetDelayMS(&COLOR_XY_CONTROL, UPDATE_TIME_MS);
927 emberAfSendImmediateDefaultResponse(EMBER_ZCL_STATUS_SUCCESS);
931 bool emberAfColorControlClusterMoveColorCallback(int16_t rateX, int16_t rateY, uint8_t optionsMask, uint8_t optionsOverride)
933 EndpointId endpoint = emberAfCurrentEndpoint();
935 if (!shouldExecuteIfOff(endpoint, optionsMask, optionsOverride))
937 emberAfSendImmediateDefaultResponse(EMBER_ZCL_STATUS_SUCCESS);
941 uint16_t transitionTimeX, transitionTimeY;
942 uint16_t unsignedRate;
944 // New command. Need to stop any active transitions.
945 stopAllColorTransitions();
947 if (rateX == 0 && rateY == 0)
949 emberAfSendImmediateDefaultResponse(EMBER_ZCL_STATUS_SUCCESS);
953 // Handle color mode transition, if necessary.
954 handleModeSwitch(endpoint, COLOR_MODE_CIE_XY);
956 // now, kick off the state machine.
957 colorXTransitionState.initialValue = readColorX(endpoint);
958 colorXTransitionState.currentValue = colorXTransitionState.initialValue;
961 colorXTransitionState.finalValue = MAX_CIE_XY_VALUE;
962 unsignedRate = (uint16_t) rateX;
966 colorXTransitionState.finalValue = MIN_CIE_XY_VALUE;
967 unsignedRate = (uint16_t)(rateX * -1);
969 transitionTimeX = computeTransitionTimeFromStateAndRate(&colorXTransitionState, unsignedRate);
970 colorXTransitionState.stepsRemaining = transitionTimeX;
971 colorXTransitionState.stepsTotal = transitionTimeX;
972 colorXTransitionState.endpoint = endpoint;
973 colorXTransitionState.lowLimit = MIN_CIE_XY_VALUE;
974 colorXTransitionState.highLimit = MAX_CIE_XY_VALUE;
976 colorYTransitionState.initialValue = readColorY(endpoint);
977 colorYTransitionState.currentValue = colorYTransitionState.initialValue;
980 colorYTransitionState.finalValue = MAX_CIE_XY_VALUE;
981 unsignedRate = (uint16_t) rateY;
985 colorYTransitionState.finalValue = MIN_CIE_XY_VALUE;
986 unsignedRate = (uint16_t)(rateY * -1);
988 transitionTimeY = computeTransitionTimeFromStateAndRate(&colorYTransitionState, unsignedRate);
989 colorYTransitionState.stepsRemaining = transitionTimeY;
990 colorYTransitionState.stepsTotal = transitionTimeY;
991 colorYTransitionState.endpoint = endpoint;
992 colorYTransitionState.lowLimit = MIN_CIE_XY_VALUE;
993 colorYTransitionState.highLimit = MAX_CIE_XY_VALUE;
995 if (transitionTimeX < transitionTimeY)
997 writeRemainingTime(endpoint, transitionTimeX);
1001 writeRemainingTime(endpoint, transitionTimeY);
1004 // kick off the state machine:
1005 emberEventControlSetDelayMS(&COLOR_XY_CONTROL, UPDATE_TIME_MS);
1007 emberAfSendImmediateDefaultResponse(EMBER_ZCL_STATUS_SUCCESS);
1011 bool emberAfColorControlClusterStepColorCallback(int16_t stepX, int16_t stepY, uint16_t transitionTime, uint8_t optionsMask,
1012 uint8_t optionsOverride)
1014 EndpointId endpoint = emberAfCurrentEndpoint();
1016 if (!shouldExecuteIfOff(endpoint, optionsMask, optionsOverride))
1018 emberAfSendImmediateDefaultResponse(EMBER_ZCL_STATUS_SUCCESS);
1022 uint16_t colorX = findNewColorValueFromStep(readColorX(endpoint), stepX);
1023 uint16_t colorY = findNewColorValueFromStep(readColorY(endpoint), stepY);
1025 if (transitionTime == 0)
1030 // New command. Need to stop any active transitions.
1031 stopAllColorTransitions();
1033 // Handle color mode transition, if necessary.
1034 handleModeSwitch(endpoint, COLOR_MODE_CIE_XY);
1036 // now, kick off the state machine.
1037 colorXTransitionState.initialValue = readColorX(endpoint);
1038 colorXTransitionState.currentValue = readColorX(endpoint);
1039 colorXTransitionState.finalValue = colorX;
1040 colorXTransitionState.stepsRemaining = transitionTime;
1041 colorXTransitionState.stepsTotal = transitionTime;
1042 colorXTransitionState.endpoint = endpoint;
1043 colorXTransitionState.lowLimit = MIN_CIE_XY_VALUE;
1044 colorXTransitionState.highLimit = MAX_CIE_XY_VALUE;
1046 colorYTransitionState.initialValue = readColorY(endpoint);
1047 colorYTransitionState.currentValue = readColorY(endpoint);
1048 colorYTransitionState.finalValue = colorY;
1049 colorYTransitionState.stepsRemaining = transitionTime;
1050 colorYTransitionState.stepsTotal = transitionTime;
1051 colorYTransitionState.endpoint = endpoint;
1052 colorYTransitionState.lowLimit = MIN_CIE_XY_VALUE;
1053 colorYTransitionState.highLimit = MAX_CIE_XY_VALUE;
1055 writeRemainingTime(endpoint, transitionTime);
1057 // kick off the state machine:
1058 emberEventControlSetDelayMS(&COLOR_XY_CONTROL, UPDATE_TIME_MS);
1060 emberAfSendImmediateDefaultResponse(EMBER_ZCL_STATUS_SUCCESS);
1064 static uint16_t findNewColorValueFromStep(uint16_t oldValue, int16_t step)
1067 int32_t newValueSigned;
1069 newValueSigned = ((int32_t) oldValue) + ((int32_t) step);
1071 if (newValueSigned < 0)
1075 else if (newValueSigned > MAX_CIE_XY_VALUE)
1077 newValue = MAX_CIE_XY_VALUE;
1081 newValue = (uint16_t) newValueSigned;
1087 static uint16_t readColorX(EndpointId endpoint)
1092 EmberAfStatus status =
1094 emberAfReadServerAttribute(endpoint, ZCL_COLOR_CONTROL_CLUSTER_ID, ZCL_COLOR_CONTROL_CURRENT_X_ATTRIBUTE_ID,
1095 (uint8_t *) &colorX, sizeof(uint16_t));
1096 assert(status == EMBER_ZCL_STATUS_SUCCESS);
1101 static uint16_t readColorY(EndpointId endpoint)
1106 EmberAfStatus status =
1108 emberAfReadServerAttribute(endpoint, ZCL_COLOR_CONTROL_CLUSTER_ID, ZCL_COLOR_CONTROL_CURRENT_Y_ATTRIBUTE_ID,
1109 (uint8_t *) &colorY, sizeof(uint16_t));
1110 assert(status == EMBER_ZCL_STATUS_SUCCESS);
1115 #endif //#ifdef EMBER_AF_PLUGIN_COLOR_CONTROL_SERVER_XY
1117 #ifdef EMBER_AF_PLUGIN_COLOR_CONTROL_SERVER_TEMP
1119 static void moveToColorTemp(EndpointId endpoint, uint16_t colorTemperature, uint16_t transitionTime)
1121 uint16_t temperatureMin = readColorTemperatureMin(endpoint);
1122 uint16_t temperatureMax = readColorTemperatureMax(endpoint);
1124 if (transitionTime == 0)
1129 // New command. Need to stop any active transitions.
1130 stopAllColorTransitions();
1132 // Handle color mode transition, if necessary.
1133 handleModeSwitch(endpoint, COLOR_MODE_TEMPERATURE);
1135 if (colorTemperature < temperatureMin)
1137 colorTemperature = temperatureMin;
1140 if (colorTemperature > temperatureMax)
1142 colorTemperature = temperatureMax;
1145 // now, kick off the state machine.
1146 colorTempTransitionState.initialValue = readColorTemperature(endpoint);
1147 colorTempTransitionState.currentValue = readColorTemperature(endpoint);
1148 colorTempTransitionState.finalValue = colorTemperature;
1149 colorTempTransitionState.stepsRemaining = transitionTime;
1150 colorTempTransitionState.stepsTotal = transitionTime;
1151 colorTempTransitionState.endpoint = endpoint;
1152 colorTempTransitionState.lowLimit = temperatureMin;
1153 colorTempTransitionState.highLimit = temperatureMax;
1155 // kick off the state machine:
1156 emberEventControlSetDelayMS(&COLOR_TEMP_CONTROL, UPDATE_TIME_MS);
1159 bool emberAfColorControlClusterMoveToColorTemperatureCallback(uint16_t colorTemperature, uint16_t transitionTime,
1160 uint8_t optionsMask, uint8_t optionsOverride)
1162 EndpointId endpoint = emberAfCurrentEndpoint();
1164 if (!shouldExecuteIfOff(endpoint, optionsMask, optionsOverride))
1166 emberAfSendImmediateDefaultResponse(EMBER_ZCL_STATUS_SUCCESS);
1170 moveToColorTemp(endpoint, colorTemperature, transitionTime);
1172 emberAfSendImmediateDefaultResponse(EMBER_ZCL_STATUS_SUCCESS);
1176 bool emberAfColorControlClusterMoveColorTemperatureCallback(uint8_t moveMode, uint16_t rate, uint16_t colorTemperatureMinimum,
1177 uint16_t colorTemperatureMaximum, uint8_t optionsMask,
1178 uint8_t optionsOverride)
1180 EndpointId endpoint = emberAfCurrentEndpoint();
1182 if (!shouldExecuteIfOff(endpoint, optionsMask, optionsOverride))
1184 emberAfSendImmediateDefaultResponse(EMBER_ZCL_STATUS_SUCCESS);
1188 uint16_t tempPhysicalMin = readColorTemperatureMin(endpoint);
1189 uint16_t tempPhysicalMax = readColorTemperatureMax(endpoint);
1190 uint16_t transitionTime;
1192 // New command. Need to stop any active transitions.
1193 stopAllColorTransitions();
1195 if (moveMode == MOVE_MODE_STOP)
1197 emberAfSendImmediateDefaultResponse(EMBER_ZCL_STATUS_SUCCESS);
1203 emberAfSendImmediateDefaultResponse(EMBER_ZCL_STATUS_INVALID_FIELD);
1207 if (colorTemperatureMinimum < tempPhysicalMin)
1209 colorTemperatureMinimum = tempPhysicalMin;
1211 if (colorTemperatureMaximum > tempPhysicalMax)
1213 colorTemperatureMaximum = tempPhysicalMax;
1216 // Handle color mode transition, if necessary.
1217 handleModeSwitch(endpoint, COLOR_MODE_TEMPERATURE);
1219 // now, kick off the state machine.
1220 colorTempTransitionState.initialValue = readColorTemperature(endpoint);
1221 colorTempTransitionState.currentValue = readColorTemperature(endpoint);
1222 if (moveMode == MOVE_MODE_UP)
1224 if (tempPhysicalMax > colorTemperatureMaximum)
1226 colorTempTransitionState.finalValue = colorTemperatureMaximum;
1230 colorTempTransitionState.finalValue = tempPhysicalMax;
1235 if (tempPhysicalMin < colorTemperatureMinimum)
1237 colorTempTransitionState.finalValue = colorTemperatureMinimum;
1241 colorTempTransitionState.finalValue = tempPhysicalMin;
1244 transitionTime = computeTransitionTimeFromStateAndRate(&colorTempTransitionState, rate);
1245 colorTempTransitionState.stepsRemaining = transitionTime;
1246 colorTempTransitionState.stepsTotal = transitionTime;
1247 colorTempTransitionState.endpoint = endpoint;
1248 colorTempTransitionState.lowLimit = colorTemperatureMinimum;
1249 colorTempTransitionState.highLimit = colorTemperatureMaximum;
1251 writeRemainingTime(endpoint, transitionTime);
1253 // kick off the state machine:
1254 emberEventControlSetDelayMS(&COLOR_TEMP_CONTROL, UPDATE_TIME_MS);
1256 emberAfSendImmediateDefaultResponse(EMBER_ZCL_STATUS_SUCCESS);
1260 bool emberAfColorControlClusterStepColorTemperatureCallback(uint8_t stepMode, uint16_t stepSize, uint16_t transitionTime,
1261 uint16_t colorTemperatureMinimum, uint16_t colorTemperatureMaximum,
1262 uint8_t optionsMask, uint8_t optionsOverride)
1264 EndpointId endpoint = emberAfCurrentEndpoint();
1266 if (!shouldExecuteIfOff(endpoint, optionsMask, optionsOverride))
1268 emberAfSendImmediateDefaultResponse(EMBER_ZCL_STATUS_SUCCESS);
1272 uint16_t tempPhysicalMin = readColorTemperatureMin(endpoint);
1273 uint16_t tempPhysicalMax = readColorTemperatureMax(endpoint);
1275 if (transitionTime == 0)
1280 // New command. Need to stop any active transitions.
1281 stopAllColorTransitions();
1283 if (stepMode == MOVE_MODE_STOP)
1285 emberAfSendImmediateDefaultResponse(EMBER_ZCL_STATUS_SUCCESS);
1289 if (colorTemperatureMinimum < tempPhysicalMin)
1291 colorTemperatureMinimum = tempPhysicalMin;
1293 if (colorTemperatureMaximum > tempPhysicalMax)
1295 colorTemperatureMaximum = tempPhysicalMax;
1298 // Handle color mode transition, if necessary.
1299 handleModeSwitch(endpoint, COLOR_MODE_TEMPERATURE);
1301 // now, kick off the state machine.
1302 colorTempTransitionState.initialValue = readColorTemperature(endpoint);
1303 colorTempTransitionState.currentValue = readColorTemperature(endpoint);
1304 if (stepMode == MOVE_MODE_UP)
1306 colorTempTransitionState.finalValue = static_cast<uint16_t>(readColorTemperature(endpoint) + stepSize);
1310 colorTempTransitionState.finalValue = static_cast<uint16_t>(readColorTemperature(endpoint) - stepSize);
1312 colorTempTransitionState.stepsRemaining = transitionTime;
1313 colorTempTransitionState.stepsTotal = transitionTime;
1314 colorTempTransitionState.endpoint = endpoint;
1315 colorTempTransitionState.lowLimit = colorTemperatureMinimum;
1316 colorTempTransitionState.highLimit = colorTemperatureMaximum;
1318 writeRemainingTime(endpoint, transitionTime);
1320 // kick off the state machine:
1321 emberEventControlSetDelayMS(&COLOR_TEMP_CONTROL, UPDATE_TIME_MS);
1323 emberAfSendImmediateDefaultResponse(EMBER_ZCL_STATUS_SUCCESS);
1327 void emberAfPluginLevelControlCoupledColorTempChangeCallback(EndpointId endpoint)
1329 // ZCL 5.2.2.1.1 Coupling color temperature to Level Control
1331 // If the Level Control for Lighting cluster identifier 0x0008 is supported
1332 // on the same endpoint as the Color Control cluster and color temperature is
1333 // supported, it is possible to couple changes in the current level to the
1334 // color temperature.
1336 // The CoupleColorTempToLevel bit of the Options attribute of the Level
1337 // Control cluster indicates whether the color temperature is to be linked
1338 // with the CurrentLevel attribute in the Level Control cluster.
1340 // If the CoupleColorTempToLevel bit of the Options attribute of the Level
1341 // Control cluster is equal to 1 and the ColorMode or EnhancedColorMode
1342 // attribute is set to 0x02 (color temperature) then a change in the
1343 // CurrentLevel attribute SHALL affect the ColorTemperatureMireds attribute.
1344 // This relationship is manufacturer specific, with the qualification that
1345 // the maximum value of the CurrentLevel attribute SHALL correspond to a
1346 // ColorTemperatureMired attribute value equal to the
1347 // CoupleColorTempToLevelMinMireds attribute. This relationship is one-way so
1348 // a change to the ColorTemperatureMireds attribute SHALL NOT have any effect
1349 // on the CurrentLevel attribute.
1351 // In order to simulate the behavior of an incandescent bulb, a low value of
1352 // the CurrentLevel attribute SHALL be associated with a high value of the
1353 // ColorTemperatureMireds attribute (i.e., a low value of color temperature
1356 // If the CoupleColorTempToLevel bit of the Options attribute of the Level
1357 // Control cluster is equal to 0, there SHALL be no link between color
1358 // temperature and current level.
1360 if (!emberAfContainsServer(endpoint, ZCL_COLOR_CONTROL_CLUSTER_ID))
1365 if (readColorMode(endpoint) == COLOR_MODE_TEMPERATURE)
1367 uint16_t tempCoupleMin = readColorTemperatureCoupleToLevelMin(endpoint);
1368 uint16_t tempPhysMax = readColorTemperatureMax(endpoint);
1369 uint8_t currentLevel = readLevelControlCurrentLevel(endpoint);
1371 // Scale color temp setting between the coupling min and the physical max.
1372 // Note that mireds varies inversely with level: low level -> high mireds.
1373 // Peg min/MAX level to MAX/min mireds, otherwise interpolate.
1374 uint16_t newColorTemp;
1375 if (currentLevel <= MIN_CURRENT_LEVEL)
1377 newColorTemp = tempPhysMax;
1379 else if (currentLevel >= MAX_CURRENT_LEVEL)
1381 newColorTemp = tempCoupleMin;
1385 uint32_t tempDelta = (((uint32_t) tempPhysMax - (uint32_t) tempCoupleMin) * currentLevel) /
1386 (uint32_t)(MAX_CURRENT_LEVEL - MIN_CURRENT_LEVEL + 1);
1387 newColorTemp = (uint16_t)((uint32_t) tempPhysMax - tempDelta);
1390 // Apply new color temp.
1391 moveToColorTemp(endpoint, newColorTemp, 0);
1395 #endif //#ifdef EMBER_AF_PLUGIN_COLOR_CONTROL_SERVER_TEMP
1397 bool emberAfColorControlClusterStopMoveStepCallback(uint8_t optionsMask, uint8_t optionsOverride)
1399 // Received a stop command. This is all we need to do.
1400 EndpointId endpoint = emberAfCurrentEndpoint();
1402 if (shouldExecuteIfOff(endpoint, optionsMask, optionsOverride))
1404 stopAllColorTransitions();
1407 emberAfSendImmediateDefaultResponse(EMBER_ZCL_STATUS_SUCCESS);
1411 // **************** transition state machines ***********
1413 static void stopAllColorTransitions(void)
1415 emberEventControlSetInactive(&COLOR_TEMP_CONTROL);
1416 emberEventControlSetInactive(&COLOR_XY_CONTROL);
1417 emberEventControlSetInactive(&COLOR_HSV_CONTROL);
1420 void emberAfPluginColorControlServerStopTransition(void)
1422 stopAllColorTransitions();
1425 // The specification says that if we are transitioning from one color mode
1426 // into another, we need to compute the new mode's attribute values from the
1427 // old mode. However, it also says that if the old mode doesn't translate into
1428 // the new mode, this must be avoided.
1429 // I am putting in this function to compute the new attributes based on the old
1431 static void handleModeSwitch(EndpointId endpoint, uint8_t newColorMode)
1433 uint8_t oldColorMode = readColorMode(endpoint);
1434 uint8_t colorModeTransition;
1436 if (oldColorMode == newColorMode)
1442 writeColorMode(endpoint, newColorMode);
1445 colorModeTransition = static_cast<uint8_t>((newColorMode << 4) + oldColorMode);
1447 // Note: It may be OK to not do anything here.
1448 switch (colorModeTransition)
1451 emberAfPluginColorControlServerComputePwmFromXyCallback(endpoint);
1453 case TEMPERATURE_TO_CIE_XY:
1454 emberAfPluginColorControlServerComputePwmFromXyCallback(endpoint);
1457 emberAfPluginColorControlServerComputePwmFromHsvCallback(endpoint);
1459 case TEMPERATURE_TO_HSV:
1460 emberAfPluginColorControlServerComputePwmFromHsvCallback(endpoint);
1462 case HSV_TO_TEMPERATURE:
1463 emberAfPluginColorControlServerComputePwmFromTempCallback(endpoint);
1465 case CIE_XY_TO_TEMPERATURE:
1466 emberAfPluginColorControlServerComputePwmFromTempCallback(endpoint);
1469 // for the following cases, there is no transition.
1471 case CIE_XY_TO_CIE_XY:
1472 case TEMPERATURE_TO_TEMPERATURE:
1478 static uint8_t addHue(uint8_t hue1, uint8_t hue2)
1482 hue16 = ((uint16_t) hue1);
1483 hue16 = static_cast<uint16_t>(hue16 + static_cast<uint16_t>(hue2));
1485 if (hue16 > MAX_HUE_VALUE)
1487 hue16 = static_cast<uint16_t>(hue16 - MAX_HUE_VALUE);
1490 return ((uint8_t) hue16);
1493 static uint8_t subtractHue(uint8_t hue1, uint8_t hue2)
1497 hue16 = ((uint16_t) hue1);
1500 hue16 = static_cast<uint16_t>(hue16 + MAX_HUE_VALUE);
1503 hue16 = static_cast<uint16_t>(hue16 - static_cast<uint16_t>(hue2));
1505 return ((uint8_t) hue16);
1508 static bool computeNewHueValue(ColorHueTransitionState * p)
1513 // exit with a false if hue is not currently moving
1514 if (p->stepsRemaining == 0)
1519 (p->stepsRemaining)--;
1521 if (p->repeat == false)
1523 writeRemainingTime(p->endpoint, p->stepsRemaining);
1526 // are we going up or down?
1527 if (p->finalHue == p->currentHue)
1533 newHue32 = (uint32_t) subtractHue(p->finalHue, p->initialHue);
1534 newHue32 *= ((uint32_t)(p->stepsRemaining));
1535 newHue32 /= ((uint32_t)(p->stepsTotal));
1536 p->currentHue = subtractHue((uint8_t) p->finalHue, (uint8_t) newHue32);
1540 newHue32 = (uint32_t) subtractHue(p->initialHue, p->finalHue);
1541 newHue32 *= ((uint32_t)(p->stepsRemaining));
1542 newHue32 /= ((uint32_t)(p->stepsTotal));
1544 p->currentHue = addHue((uint8_t) p->finalHue, (uint8_t) newHue32);
1547 if (p->stepsRemaining == 0)
1549 if (p->repeat == false)
1551 // we are performing a move to and not a move.
1556 // we are performing a Hue move. Need to compute the new values for the
1557 // next move period.
1560 newHue = subtractHue(p->finalHue, p->initialHue);
1561 newHue = addHue(p->finalHue, newHue);
1563 p->initialHue = p->finalHue;
1564 p->finalHue = newHue;
1568 newHue = subtractHue(p->initialHue, p->finalHue);
1569 newHue = subtractHue(p->finalHue, newHue);
1571 p->initialHue = p->finalHue;
1572 p->finalHue = newHue;
1574 p->stepsRemaining = TRANSITION_TIME_1S;
1580 void emberAfPluginColorControlServerHueSatTransitionEventHandler(void)
1582 EndpointId endpoint = colorHueTransitionState.endpoint;
1583 bool limitReached1, limitReached2;
1585 limitReached1 = computeNewHueValue(&colorHueTransitionState);
1586 limitReached2 = computeNewColor16uValue(&colorSaturationTransitionState);
1588 if (limitReached1 || limitReached2)
1590 stopAllColorTransitions();
1594 emberEventControlSetDelayMS(&COLOR_HSV_CONTROL, UPDATE_TIME_MS);
1597 writeHue(colorHueTransitionState.endpoint, colorHueTransitionState.currentHue);
1598 writeSaturation(colorSaturationTransitionState.endpoint, (uint8_t) colorSaturationTransitionState.currentValue);
1600 emberAfColorControlClusterPrintln("Hue %d Saturation %d endpoint %d", colorHueTransitionState.currentHue,
1601 colorSaturationTransitionState.currentValue, endpoint);
1603 emberAfPluginColorControlServerComputePwmFromHsvCallback(endpoint);
1606 // Return value of true means we need to stop.
1607 static bool computeNewColor16uValue(Color16uTransitionState * p)
1609 uint32_t newValue32u;
1611 if (p->stepsRemaining == 0)
1616 (p->stepsRemaining)--;
1618 writeRemainingTime(p->endpoint, p->stepsRemaining);
1621 if (p->finalValue == p->currentValue)
1625 else if (p->finalValue > p->initialValue)
1627 newValue32u = ((uint32_t)(p->finalValue - p->initialValue));
1628 newValue32u *= ((uint32_t)(p->stepsRemaining));
1629 newValue32u /= ((uint32_t)(p->stepsTotal));
1630 p->currentValue = static_cast<uint16_t>(p->finalValue - static_cast<uint16_t>(newValue32u));
1634 newValue32u = ((uint32_t)(p->initialValue - p->finalValue));
1635 newValue32u *= ((uint32_t)(p->stepsRemaining));
1636 newValue32u /= ((uint32_t)(p->stepsTotal));
1637 p->currentValue = static_cast<uint16_t>(p->finalValue + static_cast<uint16_t>(newValue32u));
1640 if (p->stepsRemaining == 0)
1642 // we have completed our move.
1649 static uint16_t computeTransitionTimeFromStateAndRate(Color16uTransitionState * p, uint16_t rate)
1651 uint32_t transitionTime;
1656 return MAX_INT16U_VALUE;
1659 if (p->currentValue > p->finalValue)
1661 max = p->currentValue;
1662 min = p->finalValue;
1666 max = p->finalValue;
1667 min = p->currentValue;
1670 transitionTime = max - min;
1671 transitionTime *= 10;
1672 transitionTime /= rate;
1674 if (transitionTime > MAX_INT16U_VALUE)
1676 return MAX_INT16U_VALUE;
1679 return (uint16_t) transitionTime;
1682 void emberAfPluginColorControlServerXyTransitionEventHandler(void)
1684 EndpointId endpoint = colorXTransitionState.endpoint;
1685 bool limitReachedX, limitReachedY;
1687 // compute new values for X and Y.
1688 limitReachedX = computeNewColor16uValue(&colorXTransitionState);
1690 limitReachedY = computeNewColor16uValue(&colorYTransitionState);
1692 if (limitReachedX || limitReachedY)
1694 stopAllColorTransitions();
1698 emberEventControlSetDelayMS(&COLOR_XY_CONTROL, UPDATE_TIME_MS);
1701 // update the attributes
1702 writeColorX(colorXTransitionState.endpoint, colorXTransitionState.currentValue);
1703 writeColorY(colorXTransitionState.endpoint, colorYTransitionState.currentValue);
1705 emberAfColorControlClusterPrintln("Color X %d Color Y %d", colorXTransitionState.currentValue,
1706 colorYTransitionState.currentValue);
1708 emberAfPluginColorControlServerComputePwmFromXyCallback(endpoint);
1711 void emberAfPluginColorControlServerTempTransitionEventHandler(void)
1713 EndpointId endpoint = colorTempTransitionState.endpoint;
1716 limitReached = computeNewColor16uValue(&colorTempTransitionState);
1720 stopAllColorTransitions();
1724 emberEventControlSetDelayMS(&COLOR_TEMP_CONTROL, UPDATE_TIME_MS);
1727 writeColorTemperature(colorTempTransitionState.endpoint, colorTempTransitionState.currentValue);
1729 emberAfColorControlClusterPrintln("Color Temperature %d", colorTempTransitionState.currentValue);
1731 emberAfPluginColorControlServerComputePwmFromTempCallback(endpoint);
1734 static bool shouldExecuteIfOff(EndpointId endpoint, uint8_t optionMask, uint8_t optionOverride)
1736 // From 5.2.2.2.1.10 of ZCL7 document 14-0129-15f-zcl-ch-5-lighting.docx:
1737 // "Command execution SHALL NOT continue beyond the Options processing if
1738 // all of these criteria are true:
1739 // - The On/Off cluster exists on the same endpoint as this cluster.
1740 // - The OnOff attribute of the On/Off cluster, on this endpoint, is 0x00
1742 // - The value of the ExecuteIfOff bit is 0."
1744 if (!emberAfContainsServer(endpoint, ZCL_ON_OFF_CLUSTER_ID))
1750 EmberAfStatus status =
1751 emberAfReadServerAttribute(endpoint, ZCL_COLOR_CONTROL_CLUSTER_ID, ZCL_OPTIONS_ATTRIBUTE_ID, &options, sizeof(options));
1752 if (status != EMBER_ZCL_STATUS_SUCCESS)
1754 emberAfColorControlClusterPrintln("Unable to read Options attribute: 0x%X", status);
1755 // If we can't read the attribute, then we should just assume that it has
1756 // its default value.
1761 status = emberAfReadServerAttribute(endpoint, ZCL_ON_OFF_CLUSTER_ID, ZCL_ON_OFF_ATTRIBUTE_ID, (uint8_t *) &on, sizeof(on));
1762 if (status != EMBER_ZCL_STATUS_SUCCESS)
1764 emberAfColorControlClusterPrintln("Unable to read OnOff attribute: 0x%X", status);
1767 // The device is on - hence ExecuteIfOff does not matter
1772 // The OptionsMask & OptionsOverride fields SHALL both be present or both
1773 // omitted in the command. A temporary Options bitmap SHALL be created from
1774 // the Options attribute, using the OptionsMask & OptionsOverride fields, if
1775 // present. Each bit of the temporary Options bitmap SHALL be determined as
1777 // Each bit in the Options attribute SHALL determine the corresponding bit in
1778 // the temporary Options bitmap, unless the OptionsMask field is present and
1779 // has the corresponding bit set to 1, in which case the corresponding bit in
1780 // the OptionsOverride field SHALL determine the corresponding bit in the
1781 // temporary Options bitmap.
1782 // The resulting temporary Options bitmap SHALL then be processed as defined
1783 // in section 5.2.2.2.1.10.
1785 // ---------- The following order is important in decision making -------
1786 // -----------more readable ----------
1788 if (optionMask == 0xFF && optionOverride == 0xFF)
1790 // 0xFF are the default values passed to the command handler when
1791 // the payload is not present - in that case there is use of option
1792 // attribute to decide execution of the command
1793 return READBITS(options, EMBER_ZCL_COLOR_CONTROL_OPTIONS_EXECUTE_IF_OFF);
1795 // ---------- The above is to distinguish if the payload is present or not
1797 if (READBITS(optionMask, EMBER_ZCL_COLOR_CONTROL_OPTIONS_EXECUTE_IF_OFF))
1799 // Mask is present and set in the command payload, this indicates
1800 // use the override as temporary option
1801 return READBITS(optionOverride, EMBER_ZCL_COLOR_CONTROL_OPTIONS_EXECUTE_IF_OFF);
1803 // if we are here - use the option attribute bits
1804 return (READBITS(options, EMBER_ZCL_COLOR_CONTROL_OPTIONS_EXECUTE_IF_OFF));
1807 void emberAfColorControlClusterServerInitCallback(EndpointId endpoint)
1809 #ifdef EMBER_AF_PLUGIN_COLOR_CONTROL_SERVER_TEMP
1810 // 07-5123-07 (i.e. ZCL 7) 5.2.2.2.1.22 StartUpColorTemperatureMireds Attribute
1811 // The StartUpColorTemperatureMireds attribute SHALL define the desired startup color
1812 // temperature values a lamp SHAL use when it is supplied with power and this value SHALL
1813 // be reflected in the ColorTemperatureMireds attribute. In addition, the ColorMode and
1814 // EnhancedColorMode attributes SHALL be set to 0x02 (color temperature). The values of
1815 // the StartUpColorTemperatureMireds attribute are listed in the table below.
1816 // Value Action on power up
1817 // 0x0000-0xffef Set the ColorTemperatureMireds attribute to this value.
1818 // 0xffff Set the ColorTemperatureMireds attribue to its previous value.
1820 // Initialize startUpColorTempMireds to "maintain previous value" value 0xFFFF
1821 uint16_t startUpColorTemp = 0xFFFF;
1822 EmberAfStatus status =
1823 emberAfReadAttribute(endpoint, ZCL_COLOR_CONTROL_CLUSTER_ID, ZCL_START_UP_COLOR_TEMPERATURE_MIREDS_ATTRIBUTE_ID,
1824 CLUSTER_MASK_SERVER, (uint8_t *) &startUpColorTemp, sizeof(startUpColorTemp), NULL);
1825 if (status == EMBER_ZCL_STATUS_SUCCESS)
1827 uint16_t updatedColorTemp = MAX_TEMPERATURE_VALUE;
1828 status = emberAfReadAttribute(endpoint, ZCL_COLOR_CONTROL_CLUSTER_ID, ZCL_COLOR_CONTROL_COLOR_TEMPERATURE_ATTRIBUTE_ID,
1829 CLUSTER_MASK_SERVER, (uint8_t *) &updatedColorTemp, sizeof(updatedColorTemp), NULL);
1830 if (status == EMBER_ZCL_STATUS_SUCCESS)
1832 uint16_t tempPhysicalMin = readColorTemperatureMin(endpoint);
1833 uint16_t tempPhysicalMax = readColorTemperatureMax(endpoint);
1834 if (tempPhysicalMin <= startUpColorTemp && startUpColorTemp <= tempPhysicalMax)
1836 // Apply valid startup color temp value that is within physical limits of device.
1837 // Otherwise, the startup value is outside the device's supported range, and the
1838 // existing setting of ColorTemp attribute will be left unchanged (i.e., treated as
1839 // if startup color temp was set to 0xFFFF).
1840 updatedColorTemp = startUpColorTemp;
1842 emberAfWriteAttribute(endpoint, ZCL_COLOR_CONTROL_CLUSTER_ID, ZCL_COLOR_CONTROL_COLOR_TEMPERATURE_ATTRIBUTE_ID,
1843 CLUSTER_MASK_SERVER, (uint8_t *) &updatedColorTemp, ZCL_INT16U_ATTRIBUTE_TYPE);
1844 if (status == EMBER_ZCL_STATUS_SUCCESS)
1846 // Set ColorMode attributes to reflect ColorTemperature.
1847 uint8_t updateColorMode = EMBER_ZCL_COLOR_MODE_COLOR_TEMPERATURE;
1849 emberAfWriteAttribute(endpoint, ZCL_COLOR_CONTROL_CLUSTER_ID, ZCL_COLOR_CONTROL_COLOR_MODE_ATTRIBUTE_ID,
1850 CLUSTER_MASK_SERVER, &updateColorMode, ZCL_ENUM8_ATTRIBUTE_TYPE);
1851 updateColorMode = EMBER_ZCL_ENHANCED_COLOR_MODE_COLOR_TEMPERATURE;
1852 status = emberAfWriteAttribute(endpoint, ZCL_COLOR_CONTROL_CLUSTER_ID,
1853 ZCL_COLOR_CONTROL_ENHANCED_COLOR_MODE_ATTRIBUTE_ID, CLUSTER_MASK_SERVER,
1854 &updateColorMode, ZCL_ENUM8_ATTRIBUTE_TYPE);
1862 void emberAfPluginColorControlServerComputePwmFromHsvCallback(EndpointId endpoint) {}
1864 void emberAfPluginColorControlServerComputePwmFromTempCallback(EndpointId endpoint) {}
1866 void emberAfPluginColorControlServerComputePwmFromXyCallback(EndpointId endpoint) {}