Fix for x86_64 build fail
[platform/upstream/connectedhomeip.git] / src / app / clusters / color-control-server / color-control-server.cpp
1 /**
2  *
3  *    Copyright (c) 2020 Project CHIP Authors
4  *
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
8  *
9  *        http://www.apache.org/licenses/LICENSE-2.0
10  *
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.
16  */
17
18 /**
19  *
20  *    Copyright (c) 2020 Silicon Labs
21  *
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
25  *
26  *        http://www.apache.org/licenses/LICENSE-2.0
27  *
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.
33  */
34 /****************************************************************************
35  * @file
36  * @brief Routines for the Color Control Server
37  *plugin.
38  *******************************************************************************
39  ******************************************************************************/
40 #include "color-control-server.h"
41
42 #include <app/util/af.h>
43
44 #include <app/reporting/reporting.h>
45 #include <app/util/af-event.h>
46 #include <app/util/attribute-storage.h>
47 #include <assert.h>
48
49 #include "gen/af-structs.h"
50 #include "gen/attribute-id.h"
51 #include "gen/attribute-type.h"
52 #include "gen/cluster-id.h"
53
54 using namespace chip;
55
56 #define COLOR_TEMP_CONTROL emberAfPluginColorControlServerTempTransitionEventControl
57 #define COLOR_XY_CONTROL emberAfPluginColorControlServerXyTransitionEventControl
58 #define COLOR_HSV_CONTROL emberAfPluginColorControlServerHueSatTransitionEventControl
59
60 // move mode
61 enum
62 {
63     MOVE_MODE_STOP = 0x00,
64     MOVE_MODE_UP   = 0x01,
65     MOVE_MODE_DOWN = 0x03
66 };
67
68 enum
69 {
70     COLOR_MODE_HSV         = 0x00,
71     COLOR_MODE_CIE_XY      = 0x01,
72     COLOR_MODE_TEMPERATURE = 0x02
73 };
74
75 enum
76 {
77     HSV_TO_HSV                 = 0x00,
78     HSV_TO_CIE_XY              = 0x01,
79     HSV_TO_TEMPERATURE         = 0x02,
80     CIE_XY_TO_HSV              = 0x10,
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
86 };
87
88 EmberEventControl emberAfPluginColorControlServerTempTransitionEventControl;
89 EmberEventControl emberAfPluginColorControlServerXyTransitionEventControl;
90 EmberEventControl emberAfPluginColorControlServerHueSatTransitionEventControl;
91
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
104
105 #define MIN_CURRENT_LEVEL 0x01
106 #define MAX_CURRENT_LEVEL 0xFE
107
108 #define REPORT_FAILED 0xFF
109
110 typedef struct
111 {
112     uint8_t initialHue;
113     uint8_t currentHue;
114     uint8_t finalHue;
115     uint16_t stepsRemaining;
116     uint16_t stepsTotal;
117     EndpointId endpoint;
118     bool up;
119     bool repeat;
120 } ColorHueTransitionState;
121
122 static ColorHueTransitionState colorHueTransitionState;
123
124 typedef struct
125 {
126     uint16_t initialValue;
127     uint16_t currentValue;
128     uint16_t finalValue;
129     uint16_t stepsRemaining;
130     uint16_t stepsTotal;
131     uint16_t lowLimit;
132     uint16_t highLimit;
133     EndpointId endpoint;
134 } Color16uTransitionState;
135
136 static Color16uTransitionState colorXTransitionState;
137 static Color16uTransitionState colorYTransitionState;
138
139 static Color16uTransitionState colorTempTransitionState;
140
141 static Color16uTransitionState colorSaturationTransitionState;
142
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);
148
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);
157 #endif
158
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);
163 #endif
164
165 static uint16_t computeTransitionTimeFromStateAndRate(Color16uTransitionState * p, uint16_t rate);
166
167 // convenient token handling functions
168 static uint8_t readColorMode(EndpointId endpoint)
169 {
170     uint8_t colorMode;
171
172 #ifndef NDEBUG
173     EmberAfStatus status =
174 #endif // NDEBUG
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);
178
179     return colorMode;
180 }
181
182 static uint16_t readColorTemperature(EndpointId endpoint)
183 {
184     uint16_t colorTemperature;
185
186 #ifndef NDEBUG
187     EmberAfStatus status =
188 #endif // NDEBUG
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);
192
193     return colorTemperature;
194 }
195
196 static uint16_t readColorTemperatureMin(EndpointId endpoint)
197 {
198     uint16_t colorTemperatureMin;
199     EmberStatus status;
200
201     status =
202         emberAfReadServerAttribute(endpoint, ZCL_COLOR_CONTROL_CLUSTER_ID, ZCL_COLOR_CONTROL_COLOR_TEMP_PHYSICAL_MIN_ATTRIBUTE_ID,
203                                    (uint8_t *) &colorTemperatureMin, sizeof(uint16_t));
204
205     if (status != EMBER_ZCL_STATUS_SUCCESS)
206     {
207         colorTemperatureMin = MIN_TEMPERATURE_VALUE;
208     }
209
210     return colorTemperatureMin;
211 }
212
213 static uint16_t readColorTemperatureMax(EndpointId endpoint)
214 {
215     uint16_t colorTemperatureMax;
216     EmberStatus status;
217
218     status =
219         emberAfReadServerAttribute(endpoint, ZCL_COLOR_CONTROL_CLUSTER_ID, ZCL_COLOR_CONTROL_COLOR_TEMP_PHYSICAL_MAX_ATTRIBUTE_ID,
220                                    (uint8_t *) &colorTemperatureMax, sizeof(uint16_t));
221
222     if (status != EMBER_ZCL_STATUS_SUCCESS)
223     {
224         colorTemperatureMax = MAX_TEMPERATURE_VALUE;
225     }
226
227     return colorTemperatureMax;
228 }
229
230 static uint16_t readColorTemperatureCoupleToLevelMin(EndpointId endpoint)
231 {
232     uint16_t colorTemperatureCoupleToLevelMin;
233     EmberStatus status;
234
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));
238
239     if (status != EMBER_ZCL_STATUS_SUCCESS)
240     {
241         // Not less than the physical min.
242         colorTemperatureCoupleToLevelMin = readColorTemperatureMin(endpoint);
243     }
244
245     return colorTemperatureCoupleToLevelMin;
246 }
247
248 static uint8_t readLevelControlCurrentLevel(EndpointId endpoint)
249 {
250     uint8_t currentLevel;
251     EmberStatus status;
252
253     status = emberAfReadServerAttribute(endpoint, ZCL_LEVEL_CONTROL_CLUSTER_ID, ZCL_CURRENT_LEVEL_ATTRIBUTE_ID,
254                                         (uint8_t *) &currentLevel, sizeof(uint8_t));
255
256     if (status != EMBER_ZCL_STATUS_SUCCESS)
257     {
258         currentLevel = 0x7F; // midpoint of range 0x01-0xFE
259     }
260
261     return currentLevel;
262 }
263
264 static void writeRemainingTime(EndpointId endpoint, uint16_t remainingTime)
265 {
266 #ifndef NDEBUG
267     EmberAfStatus status =
268 #endif // NDEBUG
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);
272 }
273
274 static void writeColorMode(EndpointId endpoint, uint8_t colorMode)
275 {
276 #ifndef NDEBUG
277     EmberAfStatus status =
278 #endif // NDEBUG
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);
282
283 #ifndef NDEBUG
284     status =
285 #endif // NDEBUG
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);
289 }
290
291 static void writeHue(EndpointId endpoint, uint8_t hue)
292 {
293 #ifndef NDEBUG
294     EmberAfStatus status =
295 #endif // NDEBUG
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);
299 }
300
301 static void writeSaturation(EndpointId endpoint, uint8_t saturation)
302 {
303 #ifndef NDEBUG
304     EmberAfStatus status =
305 #endif // NDEBUG
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);
309 }
310
311 static void writeColorX(EndpointId endpoint, uint16_t colorX)
312 {
313 #ifndef NDEBUG
314     EmberAfStatus status =
315 #endif // NDEBUG
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);
319 }
320
321 static void writeColorY(EndpointId endpoint, uint16_t colorY)
322 {
323 #ifndef NDEBUG
324     EmberAfStatus status =
325 #endif // NDEBUG
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);
329 }
330
331 static void writeColorTemperature(EndpointId endpoint, uint16_t colorTemperature)
332 {
333 #ifndef NDEBUG
334     EmberAfStatus status =
335 #endif // NDEBUG
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);
339 }
340
341 // -------------------------------------------------------------------------
342 // ****** callback section *******
343
344 #ifdef EMBER_AF_PLUGIN_COLOR_CONTROL_SERVER_HSV
345 /** @brief Move To Hue And Saturation
346  *
347  *
348  *
349  * @param hue   Ver.: always
350  * @param saturation   Ver.: always
351  * @param transitionTime   Ver.: always
352  */
353 bool emberAfColorControlClusterMoveToHueAndSaturationCallback(uint8_t hue, uint8_t saturation, uint16_t transitionTime,
354                                                               uint8_t optionsMask, uint8_t optionsOverride)
355 {
356     EndpointId endpoint = emberAfCurrentEndpoint();
357     uint8_t currentHue  = readHue(endpoint);
358     bool moveUp;
359
360     if (transitionTime == 0)
361     {
362         transitionTime++;
363     }
364
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)
368     {
369         emberAfSendImmediateDefaultResponse(EMBER_ZCL_STATUS_MALFORMED_COMMAND);
370         return true;
371     }
372
373     if (!shouldExecuteIfOff(endpoint, optionsMask, optionsOverride))
374     {
375         emberAfSendImmediateDefaultResponse(EMBER_ZCL_STATUS_SUCCESS);
376         return true;
377     }
378
379     // compute shortest direction
380     if (hue > currentHue)
381     {
382         moveUp = (hue - currentHue) < HALF_MAX_UINT8T;
383     }
384     else
385     {
386         moveUp = (currentHue - hue) > HALF_MAX_UINT8T;
387     }
388
389     // New command.  Need to stop any active transitions.
390     stopAllColorTransitions();
391
392     // Handle color mode transition, if necessary.
393     handleModeSwitch(endpoint, COLOR_MODE_HSV);
394
395     // now, kick off the state machine.
396     initHueSat(endpoint);
397
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;
406
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;
415
416     writeRemainingTime(endpoint, transitionTime);
417
418     // kick off the state machine:
419     emberEventControlSetDelayMS(&COLOR_HSV_CONTROL, UPDATE_TIME_MS);
420
421     emberAfSendImmediateDefaultResponse(EMBER_ZCL_STATUS_SUCCESS);
422     return true;
423 }
424
425 bool emberAfColorControlClusterMoveHueCallback(uint8_t moveMode, uint8_t rate, uint8_t optionsMask, uint8_t optionsOverride)
426 {
427     EndpointId endpoint = emberAfCurrentEndpoint();
428
429     if (!shouldExecuteIfOff(endpoint, optionsMask, optionsOverride))
430     {
431         emberAfSendImmediateDefaultResponse(EMBER_ZCL_STATUS_SUCCESS);
432         return true;
433     }
434
435     // New command.  Need to stop any active transitions.
436     stopAllColorTransitions();
437
438     if (moveMode == EMBER_ZCL_HUE_MOVE_MODE_STOP)
439     {
440         emberAfSendImmediateDefaultResponse(EMBER_ZCL_STATUS_SUCCESS);
441         return true;
442     }
443
444     // Handle color mode transition, if necessary.
445     handleModeSwitch(endpoint, COLOR_MODE_HSV);
446
447     // now, kick off the state machine.
448     initHueSat(endpoint);
449
450     colorHueTransitionState.initialHue = readHue(endpoint);
451     colorHueTransitionState.currentHue = readHue(endpoint);
452     if (moveMode == EMBER_ZCL_HUE_MOVE_MODE_UP)
453     {
454         colorHueTransitionState.finalHue = addHue(readHue(endpoint), rate);
455         colorHueTransitionState.up       = true;
456     }
457     else if (moveMode == EMBER_ZCL_HUE_MOVE_MODE_DOWN)
458     {
459         colorHueTransitionState.finalHue = subtractHue(readHue(endpoint), rate);
460         colorHueTransitionState.up       = false;
461     }
462     else
463     {
464         emberAfSendImmediateDefaultResponse(EMBER_ZCL_STATUS_MALFORMED_COMMAND);
465         return true;
466     }
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
472     // maxint.
473     writeRemainingTime(endpoint, MAX_INT16U_VALUE);
474
475     colorSaturationTransitionState.stepsRemaining = 0;
476
477     // kick off the state machine:
478     emberEventControlSetDelayMS(&COLOR_HSV_CONTROL, UPDATE_TIME_MS);
479
480     emberAfSendImmediateDefaultResponse(EMBER_ZCL_STATUS_SUCCESS);
481     return true;
482 }
483
484 bool emberAfColorControlClusterMoveSaturationCallback(uint8_t moveMode, uint8_t rate, uint8_t optionsMask, uint8_t optionsOverride)
485 {
486     EndpointId endpoint = emberAfCurrentEndpoint();
487
488     if (!shouldExecuteIfOff(endpoint, optionsMask, optionsOverride))
489     {
490         emberAfSendImmediateDefaultResponse(EMBER_ZCL_STATUS_SUCCESS);
491         return true;
492     }
493
494     uint16_t transitionTime;
495
496     // New command.  Need to stop any active transitions.
497     stopAllColorTransitions();
498
499     if (moveMode == EMBER_ZCL_SATURATION_MOVE_MODE_STOP || rate == 0)
500     {
501         emberAfSendImmediateDefaultResponse(EMBER_ZCL_STATUS_SUCCESS);
502         return true;
503     }
504
505     // Handle color mode transition, if necessary.
506     handleModeSwitch(endpoint, COLOR_MODE_HSV);
507
508     // now, kick off the state machine.
509     initHueSat(endpoint);
510
511     colorHueTransitionState.stepsRemaining = 0;
512
513     colorSaturationTransitionState.initialValue = readSaturation(endpoint);
514     colorSaturationTransitionState.currentValue = readSaturation(endpoint);
515     if (moveMode == EMBER_ZCL_SATURATION_MOVE_MODE_UP)
516     {
517         colorSaturationTransitionState.finalValue = MAX_SATURATION_VALUE;
518     }
519     else
520     {
521         colorSaturationTransitionState.finalValue = MIN_SATURATION_VALUE;
522     }
523
524     transitionTime = computeTransitionTimeFromStateAndRate(&colorSaturationTransitionState, rate);
525
526     colorSaturationTransitionState.stepsRemaining = transitionTime;
527     colorSaturationTransitionState.stepsTotal     = transitionTime;
528     colorSaturationTransitionState.endpoint       = endpoint;
529     colorSaturationTransitionState.lowLimit       = MIN_SATURATION_VALUE;
530     colorSaturationTransitionState.highLimit      = MAX_SATURATION_VALUE;
531
532     writeRemainingTime(endpoint, transitionTime);
533
534     // kick off the state machine:
535     emberEventControlSetDelayMS(&COLOR_HSV_CONTROL, UPDATE_TIME_MS);
536
537     emberAfSendImmediateDefaultResponse(EMBER_ZCL_STATUS_SUCCESS);
538     return true;
539 }
540
541 bool emberAfColorControlClusterMoveToHueCallback(uint8_t hue, uint8_t hueMoveMode, uint16_t transitionTime, uint8_t optionsMask,
542                                                  uint8_t optionsOverride)
543 {
544     EndpointId endpoint = emberAfCurrentEndpoint();
545
546     if (!shouldExecuteIfOff(endpoint, optionsMask, optionsOverride))
547     {
548         emberAfSendImmediateDefaultResponse(EMBER_ZCL_STATUS_SUCCESS);
549         return true;
550     }
551
552     uint8_t currentHue = readHue(endpoint);
553     uint8_t direction;
554
555     if (transitionTime == 0)
556     {
557         transitionTime++;
558     }
559
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)
563     {
564         emberAfSendImmediateDefaultResponse(EMBER_ZCL_STATUS_MALFORMED_COMMAND);
565         return true;
566     }
567
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
570     // representation.
571     switch (hueMoveMode)
572     {
573     case EMBER_ZCL_HUE_DIRECTION_SHORTEST_DISTANCE:
574         if (((uint8_t)(currentHue - hue)) > HALF_MAX_UINT8T)
575         {
576             direction = MOVE_MODE_UP;
577         }
578         else
579         {
580             direction = MOVE_MODE_DOWN;
581         }
582         break;
583     case EMBER_ZCL_HUE_DIRECTION_LONGEST_DISTANCE:
584         if (((uint8_t)(currentHue - hue)) > HALF_MAX_UINT8T)
585         {
586             direction = MOVE_MODE_DOWN;
587         }
588         else
589         {
590             direction = MOVE_MODE_UP;
591         }
592         break;
593         break;
594     case EMBER_ZCL_HUE_DIRECTION_UP:
595         direction = MOVE_MODE_UP;
596         break;
597     case EMBER_ZCL_HUE_DIRECTION_DOWN:
598         direction = MOVE_MODE_DOWN;
599         break;
600     default:
601         emberAfSendImmediateDefaultResponse(EMBER_ZCL_STATUS_MALFORMED_COMMAND);
602         return true;
603     }
604
605     // New command.  Need to stop any active transitions.
606     stopAllColorTransitions();
607
608     // Handle color mode transition, if necessary.
609     handleModeSwitch(endpoint, COLOR_MODE_HSV);
610
611     // now, kick off the state machine.
612     initHueSat(endpoint);
613
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;
622
623     colorSaturationTransitionState.stepsRemaining = 0;
624
625     writeRemainingTime(endpoint, transitionTime);
626
627     // kick off the state machine:
628     emberEventControlSetDelayMS(&COLOR_HSV_CONTROL, UPDATE_TIME_MS);
629
630     emberAfSendImmediateDefaultResponse(EMBER_ZCL_STATUS_SUCCESS);
631     return true;
632 }
633
634 bool emberAfColorControlClusterMoveToSaturationCallback(uint8_t saturation, uint16_t transitionTime, uint8_t optionsMask,
635                                                         uint8_t optionsOverride)
636 {
637     EndpointId endpoint = emberAfCurrentEndpoint();
638
639     if (!shouldExecuteIfOff(endpoint, optionsMask, optionsOverride))
640     {
641         emberAfSendImmediateDefaultResponse(EMBER_ZCL_STATUS_SUCCESS);
642         return true;
643     }
644
645     if (transitionTime == 0)
646     {
647         transitionTime++;
648     }
649
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)
653     {
654         emberAfSendImmediateDefaultResponse(EMBER_ZCL_STATUS_MALFORMED_COMMAND);
655         return true;
656     }
657
658     // New command.  Need to stop any active transitions.
659     stopAllColorTransitions();
660
661     // Handle color mode transition, if necessary.
662     handleModeSwitch(endpoint, COLOR_MODE_HSV);
663
664     // now, kick off the state machine.
665     initHueSat(endpoint);
666
667     colorHueTransitionState.stepsRemaining = 0;
668
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;
677
678     writeRemainingTime(endpoint, transitionTime);
679
680     // kick off the state machine:
681     emberEventControlSetDelayMS(&COLOR_HSV_CONTROL, UPDATE_TIME_MS);
682
683     emberAfSendImmediateDefaultResponse(EMBER_ZCL_STATUS_SUCCESS);
684     return true;
685 }
686
687 bool emberAfColorControlClusterStepHueCallback(uint8_t stepMode, uint8_t stepSize, uint8_t transitionTime, uint8_t optionsMask,
688                                                uint8_t optionsOverride)
689 {
690     EndpointId endpoint = emberAfCurrentEndpoint();
691
692     if (!shouldExecuteIfOff(endpoint, optionsMask, optionsOverride))
693     {
694         emberAfSendImmediateDefaultResponse(EMBER_ZCL_STATUS_SUCCESS);
695         return true;
696     }
697
698     uint8_t currentHue = readHue(endpoint);
699
700     if (transitionTime == 0)
701     {
702         transitionTime++;
703     }
704
705     // New command.  Need to stop any active transitions.
706     stopAllColorTransitions();
707
708     if (stepMode == MOVE_MODE_STOP)
709     {
710         emberAfSendImmediateDefaultResponse(EMBER_ZCL_STATUS_SUCCESS);
711         return true;
712     }
713
714     // Handle color mode transition, if necessary.
715     handleModeSwitch(endpoint, COLOR_MODE_HSV);
716
717     // now, kick off the state machine.
718     initHueSat(endpoint);
719
720     colorHueTransitionState.initialHue = currentHue;
721     colorHueTransitionState.currentHue = currentHue;
722
723     if (stepMode == MOVE_MODE_UP)
724     {
725         colorHueTransitionState.finalHue = addHue(currentHue, stepSize);
726         colorHueTransitionState.up       = true;
727     }
728     else
729     {
730         colorHueTransitionState.finalHue = subtractHue(currentHue, stepSize);
731         colorHueTransitionState.up       = false;
732     }
733     colorHueTransitionState.stepsRemaining = transitionTime;
734     colorHueTransitionState.stepsTotal     = transitionTime;
735     colorHueTransitionState.endpoint       = endpoint;
736     colorHueTransitionState.repeat         = false;
737
738     colorSaturationTransitionState.stepsRemaining = 0;
739
740     writeRemainingTime(endpoint, transitionTime);
741
742     // kick off the state machine:
743     emberEventControlSetDelayMS(&COLOR_HSV_CONTROL, UPDATE_TIME_MS);
744
745     emberAfSendImmediateDefaultResponse(EMBER_ZCL_STATUS_SUCCESS);
746     return true;
747 }
748
749 bool emberAfColorControlClusterStepSaturationCallback(uint8_t stepMode, uint8_t stepSize, uint8_t transitionTime,
750                                                       uint8_t optionsMask, uint8_t optionsOverride)
751 {
752     EndpointId endpoint = emberAfCurrentEndpoint();
753
754     if (!shouldExecuteIfOff(endpoint, optionsMask, optionsOverride))
755     {
756         emberAfSendImmediateDefaultResponse(EMBER_ZCL_STATUS_SUCCESS);
757         return true;
758     }
759
760     uint8_t currentSaturation = readSaturation(endpoint);
761
762     if (transitionTime == 0)
763     {
764         transitionTime++;
765     }
766
767     // New command.  Need to stop any active transitions.
768     stopAllColorTransitions();
769
770     if (stepMode == MOVE_MODE_STOP)
771     {
772         emberAfSendImmediateDefaultResponse(EMBER_ZCL_STATUS_SUCCESS);
773         return true;
774     }
775
776     // Handle color mode transition, if necessary.
777     handleModeSwitch(endpoint, COLOR_MODE_HSV);
778
779     // now, kick off the state machine.
780     initHueSat(endpoint);
781
782     colorHueTransitionState.stepsRemaining = 0;
783
784     colorSaturationTransitionState.initialValue = currentSaturation;
785     colorSaturationTransitionState.currentValue = currentSaturation;
786
787     if (stepMode == MOVE_MODE_UP)
788     {
789         colorSaturationTransitionState.finalValue = addSaturation(currentSaturation, stepSize);
790     }
791     else
792     {
793         colorSaturationTransitionState.finalValue = subtractSaturation(currentSaturation, stepSize);
794     }
795     colorSaturationTransitionState.stepsRemaining = transitionTime;
796     colorSaturationTransitionState.stepsTotal     = transitionTime;
797     colorSaturationTransitionState.endpoint       = endpoint;
798     colorSaturationTransitionState.lowLimit       = MIN_SATURATION_VALUE;
799     colorSaturationTransitionState.highLimit      = MAX_SATURATION_VALUE;
800
801     writeRemainingTime(endpoint, transitionTime);
802
803     // kick off the state machine:
804     emberEventControlSetDelayMS(&COLOR_HSV_CONTROL, UPDATE_TIME_MS);
805
806     emberAfSendImmediateDefaultResponse(EMBER_ZCL_STATUS_SUCCESS);
807     return true;
808 }
809
810 static uint8_t addSaturation(uint8_t saturation1, uint8_t saturation2)
811 {
812     uint16_t saturation16;
813
814     saturation16 = ((uint16_t) saturation1);
815     saturation16 = static_cast<uint16_t>(saturation16 + static_cast<uint16_t>(saturation2));
816
817     if (saturation16 > MAX_SATURATION_VALUE)
818     {
819         saturation16 = MAX_SATURATION_VALUE;
820     }
821
822     return ((uint8_t) saturation16);
823 }
824
825 static uint8_t subtractSaturation(uint8_t saturation1, uint8_t saturation2)
826 {
827     if (saturation2 > saturation1)
828     {
829         return 0;
830     }
831
832     return static_cast<uint8_t>(saturation1 - saturation2);
833 }
834
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)
839 {
840     colorHueTransitionState.stepsRemaining = 0;
841     colorHueTransitionState.currentHue     = readHue(endpoint);
842     colorHueTransitionState.endpoint       = endpoint;
843
844     colorSaturationTransitionState.stepsRemaining = 0;
845     colorSaturationTransitionState.currentValue   = readSaturation(endpoint);
846     colorSaturationTransitionState.endpoint       = endpoint;
847 }
848
849 static uint8_t readHue(EndpointId endpoint)
850 {
851     uint8_t hue;
852
853 #ifndef NDEBUG
854     EmberAfStatus status =
855 #endif // NDEBUG
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);
859
860     return hue;
861 }
862
863 static uint8_t readSaturation(EndpointId endpoint)
864 {
865     uint8_t saturation;
866
867 #ifndef NDEBUG
868     EmberAfStatus status =
869 #endif // NDEBUG
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);
873
874     return saturation;
875 }
876
877 #endif // #ifdef EMBER_AF_PLUGIN_COLOR_CONTROL_SERVER_HSV
878
879 #ifdef EMBER_AF_PLUGIN_COLOR_CONTROL_SERVER_XY
880
881 bool emberAfColorControlClusterMoveToColorCallback(uint16_t colorX, uint16_t colorY, uint16_t transitionTime, uint8_t optionsMask,
882                                                    uint8_t optionsOverride)
883 {
884     EndpointId endpoint = emberAfCurrentEndpoint();
885
886     if (!shouldExecuteIfOff(endpoint, optionsMask, optionsOverride))
887     {
888         emberAfSendImmediateDefaultResponse(EMBER_ZCL_STATUS_SUCCESS);
889         return true;
890     }
891
892     if (transitionTime == 0)
893     {
894         transitionTime++;
895     }
896
897     // New command.  Need to stop any active transitions.
898     stopAllColorTransitions();
899
900     // Handle color mode transition, if necessary.
901     handleModeSwitch(endpoint, COLOR_MODE_CIE_XY);
902
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;
912
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;
921
922     writeRemainingTime(endpoint, transitionTime);
923
924     // kick off the state machine:
925     emberEventControlSetDelayMS(&COLOR_XY_CONTROL, UPDATE_TIME_MS);
926
927     emberAfSendImmediateDefaultResponse(EMBER_ZCL_STATUS_SUCCESS);
928     return true;
929 }
930
931 bool emberAfColorControlClusterMoveColorCallback(int16_t rateX, int16_t rateY, uint8_t optionsMask, uint8_t optionsOverride)
932 {
933     EndpointId endpoint = emberAfCurrentEndpoint();
934
935     if (!shouldExecuteIfOff(endpoint, optionsMask, optionsOverride))
936     {
937         emberAfSendImmediateDefaultResponse(EMBER_ZCL_STATUS_SUCCESS);
938         return true;
939     }
940
941     uint16_t transitionTimeX, transitionTimeY;
942     uint16_t unsignedRate;
943
944     // New command.  Need to stop any active transitions.
945     stopAllColorTransitions();
946
947     if (rateX == 0 && rateY == 0)
948     {
949         emberAfSendImmediateDefaultResponse(EMBER_ZCL_STATUS_SUCCESS);
950         return true;
951     }
952
953     // Handle color mode transition, if necessary.
954     handleModeSwitch(endpoint, COLOR_MODE_CIE_XY);
955
956     // now, kick off the state machine.
957     colorXTransitionState.initialValue = readColorX(endpoint);
958     colorXTransitionState.currentValue = colorXTransitionState.initialValue;
959     if (rateX > 0)
960     {
961         colorXTransitionState.finalValue = MAX_CIE_XY_VALUE;
962         unsignedRate                     = (uint16_t) rateX;
963     }
964     else
965     {
966         colorXTransitionState.finalValue = MIN_CIE_XY_VALUE;
967         unsignedRate                     = (uint16_t)(rateX * -1);
968     }
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;
975
976     colorYTransitionState.initialValue = readColorY(endpoint);
977     colorYTransitionState.currentValue = colorYTransitionState.initialValue;
978     if (rateY > 0)
979     {
980         colorYTransitionState.finalValue = MAX_CIE_XY_VALUE;
981         unsignedRate                     = (uint16_t) rateY;
982     }
983     else
984     {
985         colorYTransitionState.finalValue = MIN_CIE_XY_VALUE;
986         unsignedRate                     = (uint16_t)(rateY * -1);
987     }
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;
994
995     if (transitionTimeX < transitionTimeY)
996     {
997         writeRemainingTime(endpoint, transitionTimeX);
998     }
999     else
1000     {
1001         writeRemainingTime(endpoint, transitionTimeY);
1002     }
1003
1004     // kick off the state machine:
1005     emberEventControlSetDelayMS(&COLOR_XY_CONTROL, UPDATE_TIME_MS);
1006
1007     emberAfSendImmediateDefaultResponse(EMBER_ZCL_STATUS_SUCCESS);
1008     return true;
1009 }
1010
1011 bool emberAfColorControlClusterStepColorCallback(int16_t stepX, int16_t stepY, uint16_t transitionTime, uint8_t optionsMask,
1012                                                  uint8_t optionsOverride)
1013 {
1014     EndpointId endpoint = emberAfCurrentEndpoint();
1015
1016     if (!shouldExecuteIfOff(endpoint, optionsMask, optionsOverride))
1017     {
1018         emberAfSendImmediateDefaultResponse(EMBER_ZCL_STATUS_SUCCESS);
1019         return true;
1020     }
1021
1022     uint16_t colorX = findNewColorValueFromStep(readColorX(endpoint), stepX);
1023     uint16_t colorY = findNewColorValueFromStep(readColorY(endpoint), stepY);
1024
1025     if (transitionTime == 0)
1026     {
1027         transitionTime++;
1028     }
1029
1030     // New command.  Need to stop any active transitions.
1031     stopAllColorTransitions();
1032
1033     // Handle color mode transition, if necessary.
1034     handleModeSwitch(endpoint, COLOR_MODE_CIE_XY);
1035
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;
1045
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;
1054
1055     writeRemainingTime(endpoint, transitionTime);
1056
1057     // kick off the state machine:
1058     emberEventControlSetDelayMS(&COLOR_XY_CONTROL, UPDATE_TIME_MS);
1059
1060     emberAfSendImmediateDefaultResponse(EMBER_ZCL_STATUS_SUCCESS);
1061     return true;
1062 }
1063
1064 static uint16_t findNewColorValueFromStep(uint16_t oldValue, int16_t step)
1065 {
1066     uint16_t newValue;
1067     int32_t newValueSigned;
1068
1069     newValueSigned = ((int32_t) oldValue) + ((int32_t) step);
1070
1071     if (newValueSigned < 0)
1072     {
1073         newValue = 0;
1074     }
1075     else if (newValueSigned > MAX_CIE_XY_VALUE)
1076     {
1077         newValue = MAX_CIE_XY_VALUE;
1078     }
1079     else
1080     {
1081         newValue = (uint16_t) newValueSigned;
1082     }
1083
1084     return newValue;
1085 }
1086
1087 static uint16_t readColorX(EndpointId endpoint)
1088 {
1089     uint16_t colorX;
1090
1091 #ifndef NDEBUG
1092     EmberAfStatus status =
1093 #endif // NDEBUG
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);
1097
1098     return colorX;
1099 }
1100
1101 static uint16_t readColorY(EndpointId endpoint)
1102 {
1103     uint16_t colorY;
1104
1105 #ifndef NDEBUG
1106     EmberAfStatus status =
1107 #endif // NDEBUG
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);
1111
1112     return colorY;
1113 }
1114
1115 #endif //#ifdef EMBER_AF_PLUGIN_COLOR_CONTROL_SERVER_XY
1116
1117 #ifdef EMBER_AF_PLUGIN_COLOR_CONTROL_SERVER_TEMP
1118
1119 static void moveToColorTemp(EndpointId endpoint, uint16_t colorTemperature, uint16_t transitionTime)
1120 {
1121     uint16_t temperatureMin = readColorTemperatureMin(endpoint);
1122     uint16_t temperatureMax = readColorTemperatureMax(endpoint);
1123
1124     if (transitionTime == 0)
1125     {
1126         transitionTime++;
1127     }
1128
1129     // New command.  Need to stop any active transitions.
1130     stopAllColorTransitions();
1131
1132     // Handle color mode transition, if necessary.
1133     handleModeSwitch(endpoint, COLOR_MODE_TEMPERATURE);
1134
1135     if (colorTemperature < temperatureMin)
1136     {
1137         colorTemperature = temperatureMin;
1138     }
1139
1140     if (colorTemperature > temperatureMax)
1141     {
1142         colorTemperature = temperatureMax;
1143     }
1144
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;
1154
1155     // kick off the state machine:
1156     emberEventControlSetDelayMS(&COLOR_TEMP_CONTROL, UPDATE_TIME_MS);
1157 }
1158
1159 bool emberAfColorControlClusterMoveToColorTemperatureCallback(uint16_t colorTemperature, uint16_t transitionTime,
1160                                                               uint8_t optionsMask, uint8_t optionsOverride)
1161 {
1162     EndpointId endpoint = emberAfCurrentEndpoint();
1163
1164     if (!shouldExecuteIfOff(endpoint, optionsMask, optionsOverride))
1165     {
1166         emberAfSendImmediateDefaultResponse(EMBER_ZCL_STATUS_SUCCESS);
1167         return true;
1168     }
1169
1170     moveToColorTemp(endpoint, colorTemperature, transitionTime);
1171
1172     emberAfSendImmediateDefaultResponse(EMBER_ZCL_STATUS_SUCCESS);
1173     return true;
1174 }
1175
1176 bool emberAfColorControlClusterMoveColorTemperatureCallback(uint8_t moveMode, uint16_t rate, uint16_t colorTemperatureMinimum,
1177                                                             uint16_t colorTemperatureMaximum, uint8_t optionsMask,
1178                                                             uint8_t optionsOverride)
1179 {
1180     EndpointId endpoint = emberAfCurrentEndpoint();
1181
1182     if (!shouldExecuteIfOff(endpoint, optionsMask, optionsOverride))
1183     {
1184         emberAfSendImmediateDefaultResponse(EMBER_ZCL_STATUS_SUCCESS);
1185         return true;
1186     }
1187
1188     uint16_t tempPhysicalMin = readColorTemperatureMin(endpoint);
1189     uint16_t tempPhysicalMax = readColorTemperatureMax(endpoint);
1190     uint16_t transitionTime;
1191
1192     // New command.  Need to stop any active transitions.
1193     stopAllColorTransitions();
1194
1195     if (moveMode == MOVE_MODE_STOP)
1196     {
1197         emberAfSendImmediateDefaultResponse(EMBER_ZCL_STATUS_SUCCESS);
1198         return true;
1199     }
1200
1201     if (rate == 0)
1202     {
1203         emberAfSendImmediateDefaultResponse(EMBER_ZCL_STATUS_INVALID_FIELD);
1204         return true;
1205     }
1206
1207     if (colorTemperatureMinimum < tempPhysicalMin)
1208     {
1209         colorTemperatureMinimum = tempPhysicalMin;
1210     }
1211     if (colorTemperatureMaximum > tempPhysicalMax)
1212     {
1213         colorTemperatureMaximum = tempPhysicalMax;
1214     }
1215
1216     // Handle color mode transition, if necessary.
1217     handleModeSwitch(endpoint, COLOR_MODE_TEMPERATURE);
1218
1219     // now, kick off the state machine.
1220     colorTempTransitionState.initialValue = readColorTemperature(endpoint);
1221     colorTempTransitionState.currentValue = readColorTemperature(endpoint);
1222     if (moveMode == MOVE_MODE_UP)
1223     {
1224         if (tempPhysicalMax > colorTemperatureMaximum)
1225         {
1226             colorTempTransitionState.finalValue = colorTemperatureMaximum;
1227         }
1228         else
1229         {
1230             colorTempTransitionState.finalValue = tempPhysicalMax;
1231         }
1232     }
1233     else
1234     {
1235         if (tempPhysicalMin < colorTemperatureMinimum)
1236         {
1237             colorTempTransitionState.finalValue = colorTemperatureMinimum;
1238         }
1239         else
1240         {
1241             colorTempTransitionState.finalValue = tempPhysicalMin;
1242         }
1243     }
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;
1250
1251     writeRemainingTime(endpoint, transitionTime);
1252
1253     // kick off the state machine:
1254     emberEventControlSetDelayMS(&COLOR_TEMP_CONTROL, UPDATE_TIME_MS);
1255
1256     emberAfSendImmediateDefaultResponse(EMBER_ZCL_STATUS_SUCCESS);
1257     return true;
1258 }
1259
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)
1263 {
1264     EndpointId endpoint = emberAfCurrentEndpoint();
1265
1266     if (!shouldExecuteIfOff(endpoint, optionsMask, optionsOverride))
1267     {
1268         emberAfSendImmediateDefaultResponse(EMBER_ZCL_STATUS_SUCCESS);
1269         return true;
1270     }
1271
1272     uint16_t tempPhysicalMin = readColorTemperatureMin(endpoint);
1273     uint16_t tempPhysicalMax = readColorTemperatureMax(endpoint);
1274
1275     if (transitionTime == 0)
1276     {
1277         transitionTime++;
1278     }
1279
1280     // New command.  Need to stop any active transitions.
1281     stopAllColorTransitions();
1282
1283     if (stepMode == MOVE_MODE_STOP)
1284     {
1285         emberAfSendImmediateDefaultResponse(EMBER_ZCL_STATUS_SUCCESS);
1286         return true;
1287     }
1288
1289     if (colorTemperatureMinimum < tempPhysicalMin)
1290     {
1291         colorTemperatureMinimum = tempPhysicalMin;
1292     }
1293     if (colorTemperatureMaximum > tempPhysicalMax)
1294     {
1295         colorTemperatureMaximum = tempPhysicalMax;
1296     }
1297
1298     // Handle color mode transition, if necessary.
1299     handleModeSwitch(endpoint, COLOR_MODE_TEMPERATURE);
1300
1301     // now, kick off the state machine.
1302     colorTempTransitionState.initialValue = readColorTemperature(endpoint);
1303     colorTempTransitionState.currentValue = readColorTemperature(endpoint);
1304     if (stepMode == MOVE_MODE_UP)
1305     {
1306         colorTempTransitionState.finalValue = static_cast<uint16_t>(readColorTemperature(endpoint) + stepSize);
1307     }
1308     else
1309     {
1310         colorTempTransitionState.finalValue = static_cast<uint16_t>(readColorTemperature(endpoint) - stepSize);
1311     }
1312     colorTempTransitionState.stepsRemaining = transitionTime;
1313     colorTempTransitionState.stepsTotal     = transitionTime;
1314     colorTempTransitionState.endpoint       = endpoint;
1315     colorTempTransitionState.lowLimit       = colorTemperatureMinimum;
1316     colorTempTransitionState.highLimit      = colorTemperatureMaximum;
1317
1318     writeRemainingTime(endpoint, transitionTime);
1319
1320     // kick off the state machine:
1321     emberEventControlSetDelayMS(&COLOR_TEMP_CONTROL, UPDATE_TIME_MS);
1322
1323     emberAfSendImmediateDefaultResponse(EMBER_ZCL_STATUS_SUCCESS);
1324     return true;
1325 }
1326
1327 void emberAfPluginLevelControlCoupledColorTempChangeCallback(EndpointId endpoint)
1328 {
1329     // ZCL 5.2.2.1.1 Coupling color temperature to Level Control
1330     //
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.
1335     //
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.
1339     //
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.
1350     //
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
1354     // in kelvins).
1355     //
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.
1359
1360     if (!emberAfContainsServer(endpoint, ZCL_COLOR_CONTROL_CLUSTER_ID))
1361     {
1362         return;
1363     }
1364
1365     if (readColorMode(endpoint) == COLOR_MODE_TEMPERATURE)
1366     {
1367         uint16_t tempCoupleMin = readColorTemperatureCoupleToLevelMin(endpoint);
1368         uint16_t tempPhysMax   = readColorTemperatureMax(endpoint);
1369         uint8_t currentLevel   = readLevelControlCurrentLevel(endpoint);
1370
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)
1376         {
1377             newColorTemp = tempPhysMax;
1378         }
1379         else if (currentLevel >= MAX_CURRENT_LEVEL)
1380         {
1381             newColorTemp = tempCoupleMin;
1382         }
1383         else
1384         {
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);
1388         }
1389
1390         // Apply new color temp.
1391         moveToColorTemp(endpoint, newColorTemp, 0);
1392     }
1393 }
1394
1395 #endif //#ifdef EMBER_AF_PLUGIN_COLOR_CONTROL_SERVER_TEMP
1396
1397 bool emberAfColorControlClusterStopMoveStepCallback(uint8_t optionsMask, uint8_t optionsOverride)
1398 {
1399     // Received a stop command.  This is all we need to do.
1400     EndpointId endpoint = emberAfCurrentEndpoint();
1401
1402     if (shouldExecuteIfOff(endpoint, optionsMask, optionsOverride))
1403     {
1404         stopAllColorTransitions();
1405     }
1406
1407     emberAfSendImmediateDefaultResponse(EMBER_ZCL_STATUS_SUCCESS);
1408     return true;
1409 }
1410
1411 // **************** transition state machines ***********
1412
1413 static void stopAllColorTransitions(void)
1414 {
1415     emberEventControlSetInactive(&COLOR_TEMP_CONTROL);
1416     emberEventControlSetInactive(&COLOR_XY_CONTROL);
1417     emberEventControlSetInactive(&COLOR_HSV_CONTROL);
1418 }
1419
1420 void emberAfPluginColorControlServerStopTransition(void)
1421 {
1422     stopAllColorTransitions();
1423 }
1424
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
1430 // color mode.
1431 static void handleModeSwitch(EndpointId endpoint, uint8_t newColorMode)
1432 {
1433     uint8_t oldColorMode = readColorMode(endpoint);
1434     uint8_t colorModeTransition;
1435
1436     if (oldColorMode == newColorMode)
1437     {
1438         return;
1439     }
1440     else
1441     {
1442         writeColorMode(endpoint, newColorMode);
1443     }
1444
1445     colorModeTransition = static_cast<uint8_t>((newColorMode << 4) + oldColorMode);
1446
1447     // Note:  It may be OK to not do anything here.
1448     switch (colorModeTransition)
1449     {
1450     case HSV_TO_CIE_XY:
1451         emberAfPluginColorControlServerComputePwmFromXyCallback(endpoint);
1452         break;
1453     case TEMPERATURE_TO_CIE_XY:
1454         emberAfPluginColorControlServerComputePwmFromXyCallback(endpoint);
1455         break;
1456     case CIE_XY_TO_HSV:
1457         emberAfPluginColorControlServerComputePwmFromHsvCallback(endpoint);
1458         break;
1459     case TEMPERATURE_TO_HSV:
1460         emberAfPluginColorControlServerComputePwmFromHsvCallback(endpoint);
1461         break;
1462     case HSV_TO_TEMPERATURE:
1463         emberAfPluginColorControlServerComputePwmFromTempCallback(endpoint);
1464         break;
1465     case CIE_XY_TO_TEMPERATURE:
1466         emberAfPluginColorControlServerComputePwmFromTempCallback(endpoint);
1467         break;
1468
1469     // for the following cases, there is no transition.
1470     case HSV_TO_HSV:
1471     case CIE_XY_TO_CIE_XY:
1472     case TEMPERATURE_TO_TEMPERATURE:
1473     default:
1474         return;
1475     }
1476 }
1477
1478 static uint8_t addHue(uint8_t hue1, uint8_t hue2)
1479 {
1480     uint16_t hue16;
1481
1482     hue16 = ((uint16_t) hue1);
1483     hue16 = static_cast<uint16_t>(hue16 + static_cast<uint16_t>(hue2));
1484
1485     if (hue16 > MAX_HUE_VALUE)
1486     {
1487         hue16 = static_cast<uint16_t>(hue16 - MAX_HUE_VALUE);
1488     }
1489
1490     return ((uint8_t) hue16);
1491 }
1492
1493 static uint8_t subtractHue(uint8_t hue1, uint8_t hue2)
1494 {
1495     uint16_t hue16;
1496
1497     hue16 = ((uint16_t) hue1);
1498     if (hue2 > hue1)
1499     {
1500         hue16 = static_cast<uint16_t>(hue16 + MAX_HUE_VALUE);
1501     }
1502
1503     hue16 = static_cast<uint16_t>(hue16 - static_cast<uint16_t>(hue2));
1504
1505     return ((uint8_t) hue16);
1506 }
1507
1508 static bool computeNewHueValue(ColorHueTransitionState * p)
1509 {
1510     uint32_t newHue32;
1511     uint8_t newHue;
1512
1513     // exit with a false if hue is not currently moving
1514     if (p->stepsRemaining == 0)
1515     {
1516         return false;
1517     }
1518
1519     (p->stepsRemaining)--;
1520
1521     if (p->repeat == false)
1522     {
1523         writeRemainingTime(p->endpoint, p->stepsRemaining);
1524     }
1525
1526     // are we going up or down?
1527     if (p->finalHue == p->currentHue)
1528     {
1529         // do nothing
1530     }
1531     else if (p->up)
1532     {
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);
1537     }
1538     else
1539     {
1540         newHue32 = (uint32_t) subtractHue(p->initialHue, p->finalHue);
1541         newHue32 *= ((uint32_t)(p->stepsRemaining));
1542         newHue32 /= ((uint32_t)(p->stepsTotal));
1543
1544         p->currentHue = addHue((uint8_t) p->finalHue, (uint8_t) newHue32);
1545     }
1546
1547     if (p->stepsRemaining == 0)
1548     {
1549         if (p->repeat == false)
1550         {
1551             // we are performing a move to and not a move.
1552             return true;
1553         }
1554         else
1555         {
1556             // we are performing a Hue move.  Need to compute the new values for the
1557             // next move period.
1558             if (p->up)
1559             {
1560                 newHue = subtractHue(p->finalHue, p->initialHue);
1561                 newHue = addHue(p->finalHue, newHue);
1562
1563                 p->initialHue = p->finalHue;
1564                 p->finalHue   = newHue;
1565             }
1566             else
1567             {
1568                 newHue = subtractHue(p->initialHue, p->finalHue);
1569                 newHue = subtractHue(p->finalHue, newHue);
1570
1571                 p->initialHue = p->finalHue;
1572                 p->finalHue   = newHue;
1573             }
1574             p->stepsRemaining = TRANSITION_TIME_1S;
1575         }
1576     }
1577     return false;
1578 }
1579
1580 void emberAfPluginColorControlServerHueSatTransitionEventHandler(void)
1581 {
1582     EndpointId endpoint = colorHueTransitionState.endpoint;
1583     bool limitReached1, limitReached2;
1584
1585     limitReached1 = computeNewHueValue(&colorHueTransitionState);
1586     limitReached2 = computeNewColor16uValue(&colorSaturationTransitionState);
1587
1588     if (limitReached1 || limitReached2)
1589     {
1590         stopAllColorTransitions();
1591     }
1592     else
1593     {
1594         emberEventControlSetDelayMS(&COLOR_HSV_CONTROL, UPDATE_TIME_MS);
1595     }
1596
1597     writeHue(colorHueTransitionState.endpoint, colorHueTransitionState.currentHue);
1598     writeSaturation(colorSaturationTransitionState.endpoint, (uint8_t) colorSaturationTransitionState.currentValue);
1599
1600     emberAfColorControlClusterPrintln("Hue %d Saturation %d endpoint %d", colorHueTransitionState.currentHue,
1601                                       colorSaturationTransitionState.currentValue, endpoint);
1602
1603     emberAfPluginColorControlServerComputePwmFromHsvCallback(endpoint);
1604 }
1605
1606 // Return value of true means we need to stop.
1607 static bool computeNewColor16uValue(Color16uTransitionState * p)
1608 {
1609     uint32_t newValue32u;
1610
1611     if (p->stepsRemaining == 0)
1612     {
1613         return false;
1614     }
1615
1616     (p->stepsRemaining)--;
1617
1618     writeRemainingTime(p->endpoint, p->stepsRemaining);
1619
1620     // handle sign
1621     if (p->finalValue == p->currentValue)
1622     {
1623         // do nothing
1624     }
1625     else if (p->finalValue > p->initialValue)
1626     {
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));
1631     }
1632     else
1633     {
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));
1638     }
1639
1640     if (p->stepsRemaining == 0)
1641     {
1642         // we have completed our move.
1643         return true;
1644     }
1645
1646     return false;
1647 }
1648
1649 static uint16_t computeTransitionTimeFromStateAndRate(Color16uTransitionState * p, uint16_t rate)
1650 {
1651     uint32_t transitionTime;
1652     uint16_t max, min;
1653
1654     if (rate == 0)
1655     {
1656         return MAX_INT16U_VALUE;
1657     }
1658
1659     if (p->currentValue > p->finalValue)
1660     {
1661         max = p->currentValue;
1662         min = p->finalValue;
1663     }
1664     else
1665     {
1666         max = p->finalValue;
1667         min = p->currentValue;
1668     }
1669
1670     transitionTime = max - min;
1671     transitionTime *= 10;
1672     transitionTime /= rate;
1673
1674     if (transitionTime > MAX_INT16U_VALUE)
1675     {
1676         return MAX_INT16U_VALUE;
1677     }
1678
1679     return (uint16_t) transitionTime;
1680 }
1681
1682 void emberAfPluginColorControlServerXyTransitionEventHandler(void)
1683 {
1684     EndpointId endpoint = colorXTransitionState.endpoint;
1685     bool limitReachedX, limitReachedY;
1686
1687     // compute new values for X and Y.
1688     limitReachedX = computeNewColor16uValue(&colorXTransitionState);
1689
1690     limitReachedY = computeNewColor16uValue(&colorYTransitionState);
1691
1692     if (limitReachedX || limitReachedY)
1693     {
1694         stopAllColorTransitions();
1695     }
1696     else
1697     {
1698         emberEventControlSetDelayMS(&COLOR_XY_CONTROL, UPDATE_TIME_MS);
1699     }
1700
1701     // update the attributes
1702     writeColorX(colorXTransitionState.endpoint, colorXTransitionState.currentValue);
1703     writeColorY(colorXTransitionState.endpoint, colorYTransitionState.currentValue);
1704
1705     emberAfColorControlClusterPrintln("Color X %d Color Y %d", colorXTransitionState.currentValue,
1706                                       colorYTransitionState.currentValue);
1707
1708     emberAfPluginColorControlServerComputePwmFromXyCallback(endpoint);
1709 }
1710
1711 void emberAfPluginColorControlServerTempTransitionEventHandler(void)
1712 {
1713     EndpointId endpoint = colorTempTransitionState.endpoint;
1714     bool limitReached;
1715
1716     limitReached = computeNewColor16uValue(&colorTempTransitionState);
1717
1718     if (limitReached)
1719     {
1720         stopAllColorTransitions();
1721     }
1722     else
1723     {
1724         emberEventControlSetDelayMS(&COLOR_TEMP_CONTROL, UPDATE_TIME_MS);
1725     }
1726
1727     writeColorTemperature(colorTempTransitionState.endpoint, colorTempTransitionState.currentValue);
1728
1729     emberAfColorControlClusterPrintln("Color Temperature %d", colorTempTransitionState.currentValue);
1730
1731     emberAfPluginColorControlServerComputePwmFromTempCallback(endpoint);
1732 }
1733
1734 static bool shouldExecuteIfOff(EndpointId endpoint, uint8_t optionMask, uint8_t optionOverride)
1735 {
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
1741     //        (FALSE).
1742     //      - The value of the ExecuteIfOff bit is 0."
1743
1744     if (!emberAfContainsServer(endpoint, ZCL_ON_OFF_CLUSTER_ID))
1745     {
1746         return true;
1747     }
1748
1749     uint8_t options;
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)
1753     {
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.
1757         options = 0x00;
1758     }
1759
1760     bool on;
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)
1763     {
1764         emberAfColorControlClusterPrintln("Unable to read OnOff attribute: 0x%X", status);
1765         return true;
1766     }
1767     // The device is on - hence ExecuteIfOff does not matter
1768     if (on)
1769     {
1770         return true;
1771     }
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
1776     // follows:
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.
1784
1785     // ---------- The following order is important in decision making -------
1786     // -----------more readable ----------
1787     //
1788     if (optionMask == 0xFF && optionOverride == 0xFF)
1789     {
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);
1794     }
1795     // ---------- The above is to distinguish if the payload is present or not
1796
1797     if (READBITS(optionMask, EMBER_ZCL_COLOR_CONTROL_OPTIONS_EXECUTE_IF_OFF))
1798     {
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);
1802     }
1803     // if we are here - use the option attribute bits
1804     return (READBITS(options, EMBER_ZCL_COLOR_CONTROL_OPTIONS_EXECUTE_IF_OFF));
1805 }
1806
1807 void emberAfColorControlClusterServerInitCallback(EndpointId endpoint)
1808 {
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.
1819
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)
1826     {
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)
1831         {
1832             uint16_t tempPhysicalMin = readColorTemperatureMin(endpoint);
1833             uint16_t tempPhysicalMax = readColorTemperatureMax(endpoint);
1834             if (tempPhysicalMin <= startUpColorTemp && startUpColorTemp <= tempPhysicalMax)
1835             {
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;
1841                 status =
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)
1845                 {
1846                     // Set ColorMode attributes to reflect ColorTemperature.
1847                     uint8_t updateColorMode = EMBER_ZCL_COLOR_MODE_COLOR_TEMPERATURE;
1848                     status =
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);
1855                 }
1856             }
1857         }
1858     }
1859 #endif
1860 }
1861
1862 void emberAfPluginColorControlServerComputePwmFromHsvCallback(EndpointId endpoint) {}
1863
1864 void emberAfPluginColorControlServerComputePwmFromTempCallback(EndpointId endpoint) {}
1865
1866 void emberAfPluginColorControlServerComputePwmFromXyCallback(EndpointId endpoint) {}