a70a982525fcef7bc554df187ce9509eca0d8f58
[iot2.git] / benchmarks / drivers / Cpp / LifxLightBulb / LifxLightBulb.hpp
1 #ifndef _LIFXLIGHTBULB_HPP__
2 #define _LIFXLIGHTBULB_HPP__
3 #include <iostream>
4 #include <atomic>
5 #include <mutex>
6 #include <thread>
7 #include <chrono>
8
9 #include <string.h>
10 #include <time.h>
11
12 #include "LightBulb.hpp"
13 #include "Socket.hpp"
14 #include "IoTRMIUtil.hpp"
15 #include "IoTSet.hpp"
16 #include "IoTUDP.hpp"
17 #include "IoTDeviceAddress.hpp"
18 #include "Iterator.hpp"
19
20 // Helper classes for LifxLightBulb
21 #include "LifxHeader.hpp"
22 #include "BulbColor.hpp"
23 #include "DeviceStateGroup.hpp"
24 #include "DeviceStateHostFirmware.hpp"
25 #include "DeviceStateHostInfo.hpp"
26 #include "DeviceStateInfo.hpp"
27 #include "DeviceStateLocation.hpp"
28 #include "DeviceStateService.hpp"
29 #include "DeviceStateVersion.hpp"
30 #include "DeviceStateWifiFirmware.hpp"
31 #include "DeviceStateWifiInfo.hpp"
32 #include "LightState.hpp"
33
34
35 using namespace std;
36
37 // Driver LifxLightBulb
38 // Implemented based on LightBulb virtual class (interface)
39
40 //std::atomic
41 std::atomic<bool> didAlreadyInit(false);
42 std::atomic<bool> didGetBulbVersion(false);
43
44 class LifxLightBulb //: public LightBulb
45 {
46         private:
47                 // Constants
48                 const static int64_t GET_BULB_VERSION_RESEND_WAIT_SECONDS = 10;
49
50                 // Variables
51                 IoTUDP *communicationSocket;
52                 char bulbMacAddress[8];
53                 //TODO:
54                 //static Semaphore socketMutex = new Semaphore(1);
55                 bool sendSocketFlag = false;
56                 int64_t lastSentGetBulbVersionRequest = 0;      // time last request sent
57
58                 // Current Bulb Values
59                 int currentHue = 0;
60                 int currentSaturation = 0;
61                 int currentBrightness = 65535;
62                 int currentTemperature = 9000;
63                 bool bulbIsOn = false;
64
65                 //std::atomic
66                 atomic<bool> didAlreadyInit;
67                 atomic<bool> didGetBulbVersion;
68
69                 // Mutex locks
70                 mutex socketMutex;
71                 mutex settingBulbColorMutex;
72                 mutex settingBulbTemperatureMutex;
73                 mutex bulbStateMutex;
74
75                 // color and temperature ranges for the bulbs
76                 int hueLowerBound = 0;
77                 int hueUpperBound = 0;
78                 int saturationLowerBound = 0;
79                 int saturationUpperBound = 0;
80                 int brightnessLowerBound = 0;
81                 int brightnessUpperBound = 0;
82                 int temperatureLowerBound = 2500;
83                 int temperatureUpperBound = 9000;
84
85                 // Check if a state change was requested, used to poll the bulb for if the bulb did
86                 // preform the requested state change
87                 bool stateDidChange = false;
88
89                 // Device address
90                 IoTSet<IoTDeviceAddress> lb_addresses;
91
92         public:
93
94                 // Constructor
95                 LifxLightBulb() { 
96                         // LB1 macAddress: d0:73:d5:12:8e:30
97                         // LB1 macAddress: d0:73:d5:02:41:da
98                         string macAddress = "D073D5128E300000"; // bulbMacAddress: [-48, 115, -43, 18, -114, 48, 0, 0]
99                         //string macAddress = "D073D50241DA0000"; // bulbMacAddress: [-48, 115, -43, 2, 65, -38, 0, 0]
100                         /*bulbMacAddress[0] = 0xD0;
101                         bulbMacAddress[1] = 0x73;
102                         bulbMacAddress[2] = 0xD5;
103                         bulbMacAddress[3] = 0x02;
104                         bulbMacAddress[4] = 0x41;
105                         bulbMacAddress[5] = 0xDA;
106                         bulbMacAddress[6] = 0x00; 
107                         bulbMacAddress[7] = 0x00;*/
108
109                         char tmpMacAddress[16];
110                         strcpy(tmpMacAddress, macAddress.c_str());
111                         //test[0] = (char) strtol(strTest.c_str(), NULL, 16);
112                         for(int i=0; i<16; i=i+2) {
113                                 // Take 2 digits and then convert
114                                 char tmpMacByte[2];
115                                 tmpMacByte[0] = tmpMacAddress[i];
116                                 tmpMacByte[1] = tmpMacAddress[i+1];
117                                 bulbMacAddress[i/2] = (char) strtol(tmpMacByte, NULL, 16);
118                         }
119                         //IoTRMIUtil::printBytes(bulbMacAddress, 8, false);
120                 }
121
122
123                 LifxLightBulb(IoTSet<IoTDeviceAddress> _devAddress, string macAddress) {
124
125                         // Initialize macAddress
126                         char tmpMacAddress[16];
127                         strcpy(tmpMacAddress, macAddress.c_str());
128                         //test[0] = (char) strtol(strTest.c_str(), NULL, 16);
129                         for(int i=0; i<16; i=i+2) {
130                                 // Take 2 digits and then convert
131                                 char tmpMacByte[2];
132                                 tmpMacByte[0] = tmpMacAddress[i];
133                                 tmpMacByte[1] = tmpMacAddress[i+1];
134                                 bulbMacAddress[i/2] = (char) strtol(tmpMacByte, NULL, 16);
135                         }
136                         cout << "MAC address is set. Value: ";
137                         IoTRMIUtil::printBytes(bulbMacAddress, 8, false);
138
139                         // Initialize device address
140                         lb_addresses = _devAddress;
141                         cout << "Device address is set! " << endl;
142                 }
143
144
145                 ~LifxLightBulb() {
146
147                         // Clean up
148                         if (communicationSocket != NULL) {
149                 
150                                 delete communicationSocket;
151                                 communicationSocket = NULL;             
152                         }
153                 }
154
155
156                 // Initialize the lightbulb
157                 void init() {
158
159                         if (didAlreadyInit.exchange(true))
160                                 return;
161
162                         unordered_set<IoTDeviceAddress>::const_iterator itr = lb_addresses.begin();
163                         IoTDeviceAddress deviceAddress = *itr;
164                         cout << "Address: " << deviceAddress.getAddress() << endl;
165
166                         // Create IoTUDP socket
167                         communicationSocket = new IoTUDP(deviceAddress);
168
169                         // Launch the worker function in a separate thread.
170                         thread th1 (&LifxLightBulb::workerFunction, this);
171                         th1.join();
172                 }
173
174
175                 void turnOff() {
176
177                         lock_guard<mutex> guard(bulbStateMutex);
178                         bulbIsOn = false;
179                         sendSetLightPowerPacket(0, 0);
180                         stateDidChange = true;
181                         cout << "Turning off lightbulb!" << endl;
182                 }
183
184
185                 void turnOn() {
186
187                         lock_guard<mutex> guard(bulbStateMutex);
188                         bulbIsOn = true;
189                         sendSetLightPowerPacket(65535, 0);
190                         stateDidChange = true;
191                 }
192
193
194                 double getHue() {
195                         double tmp = 0;
196                         settingBulbColorMutex.lock();
197                         tmp = ((double)currentHue / 65535.0) * 360.0;
198                         settingBulbColorMutex.unlock();
199
200                         return tmp;
201                 }
202
203
204                 double getSaturation() {
205                         double tmp = 0;
206                         settingBulbColorMutex.lock();
207                         tmp = ((double)currentSaturation / 65535.0) * 360.0;
208                         settingBulbColorMutex.unlock();
209
210                         return tmp;
211                 }
212
213
214                 double getBrightness() {
215                         double tmp = 0;
216                         settingBulbColorMutex.lock();
217                         tmp = ((double)currentBrightness / 65535.0) * 360.0;
218                         settingBulbColorMutex.unlock();
219
220                         return tmp;
221                 }
222
223
224                 int getTemperature() {
225
226                         int tmp = 0;
227                         settingBulbTemperatureMutex.lock();
228                         tmp = currentTemperature;
229                         settingBulbTemperatureMutex.unlock();
230
231                         return tmp;
232                 }
233
234
235                 double getHueRangeLowerBound() {
236                         if (!didGetBulbVersion) {
237                                 return -1;
238                         }
239                         return ((double)hueLowerBound / 65535.0) * 360.0;
240                 }
241
242
243                 double getHueRangeUpperBound() {
244                         if (!didGetBulbVersion) {
245                                 return -1;
246                         }
247                         return ((double)hueUpperBound / 65535.0) * 360.0;
248                 }
249
250
251                 double getSaturationRangeLowerBound() {
252                         if (!didGetBulbVersion) {
253                                 return -1;
254                         }
255                         return ((double)saturationLowerBound / 65535.0) * 100.0;
256                 }
257
258
259                 double getSaturationRangeUpperBound() {
260                         if (!didGetBulbVersion) {
261                                 return -1;
262                         }
263                         return ((double)saturationUpperBound / 65535.0) * 100.0;
264                 }
265
266
267                 double getBrightnessRangeLowerBound() {
268                         if (!didGetBulbVersion) {
269                                 return -1;
270                         }
271                         return ((double)brightnessLowerBound / 65535.0) * 100.0;
272                 }
273
274
275                 double getBrightnessRangeUpperBound() {
276                         if (!didGetBulbVersion) {
277                                 return -1;
278                         }
279                         return ((double)brightnessUpperBound / 65535.0) * 100.0;
280                 }
281
282
283                 int getTemperatureRangeLowerBound() {
284                         if (!didGetBulbVersion) {
285                                 return -1;
286                         }
287                         return temperatureLowerBound;
288                 }
289
290
291                 int getTemperatureRangeUpperBound() {
292                         if (!didGetBulbVersion) {
293                                 return -1;
294                         }
295                         return temperatureUpperBound;
296                 }
297
298
299                 void setTemperature(int _temperature) {
300
301                         settingBulbTemperatureMutex.lock();
302
303                         BulbColor* newColor = new BulbColor(currentHue, currentSaturation, currentBrightness, _temperature);
304                         sendSetLightColorPacket(newColor, 250);
305
306                         currentTemperature = _temperature;
307                         stateDidChange = true;
308
309                         settingBulbTemperatureMutex.unlock();
310                 }
311
312
313                 void setColor(double _hue, double _saturation, double _brightness) {
314
315                         settingBulbColorMutex.lock();
316
317                         _hue /= 360.0;
318                         _saturation /= 100.0;
319                         _brightness /= 100.0;
320
321
322                         int newHue = (int)(_hue * 65535.0);
323                         int newSaturation = (int)(_saturation * 65535.0);
324                         int newBrightness = (int)(_brightness * 65535.0);
325
326                         BulbColor* newColor = new BulbColor(newHue, newSaturation, newBrightness, currentTemperature);
327                         sendSetLightColorPacket(newColor, 250);
328
329                         currentHue = newHue;
330                         currentSaturation = newSaturation;
331                         currentBrightness = newBrightness;
332                         stateDidChange = true;
333
334                         settingBulbColorMutex.unlock();
335                 }
336
337
338                 bool getState() {
339
340                         bool tmp = false;
341
342                         bulbStateMutex.lock();
343                         tmp = bulbIsOn;
344                         bulbStateMutex.unlock();
345
346                         return tmp;
347                 }
348
349         private:
350                 // Private functions
351
352                 // Communication helpers
353                 void receivedPacket(char* packetData) {
354
355                         char headerBytes[36];
356                         for (int i = 0; i < 36; i++) {
357                                 headerBytes[i] = packetData[i];
358                         }
359
360                         LifxHeader recHeader;
361                         recHeader.setFromBytes(headerBytes);
362
363                         // load the payload bytes (strip away the header)
364                         char payloadBytes[recHeader.getSize()];
365                         for (int i = 36; i < recHeader.getSize(); i++) {
366                                 payloadBytes[i - 36] = packetData[i];
367                         }
368
369                         int type = recHeader.getType();
370                         cout << "Received: " << type;
371
372                         DeviceStateService* dat = NULL;
373                         switch (type) {
374
375                                 case 3:
376                                         dat = parseDeviceStateServiceMessage(payloadBytes);
377                                         cout << "Service: " << dat->getService();
378                                         cout << "Port   : " << dat->getPort();
379                                         // Avoid memory leak - delete this object
380                                         delete dat;     
381                                         break;
382
383                                 case 33:
384                                         handleStateVersionMessageReceived(payloadBytes);
385                                         break;
386
387                                 case 35:
388                                         parseDeviceStateInfoMessage(payloadBytes);
389                                         break;
390
391
392                                 case 107:
393                                         handleLightStateMessageReceived(payloadBytes);
394                                         break;
395
396                                 default:
397                                         cout << "unknown packet Type" << endl;
398                         }
399         
400                 }
401
402
403                 void sendPacket(char* packetData, int len) {
404                         //cout << "sendPacket: About to send" << endl;
405                         lock_guard<mutex> guard(socketMutex);
406                         sendSocketFlag = true;
407                         communicationSocket->sendData(packetData, len);
408                         sendSocketFlag = false;
409                 }
410
411
412                 // Worker function which runs the while loop for receiving data from the bulb.
413                 // Is blocking.
414                 void workerFunction() {
415                         LifxHeader h;
416                         // Need timeout on receives since we are not sure if a packet will be available
417                         // for processing so don't block waiting
418                         // TODO: Check if we can do this here!
419                         //communicationSocket.setSoTimeout(50);
420
421                         turnOff();
422
423                         while (true) {
424
425                                 // Check if we got the bulb version yet
426                                 // could have requested it but message could have gotten lost (UDP)
427                                 if (!didGetBulbVersion) {
428                                         int64_t currentTime = (int64_t) time(NULL);
429                                         if ((currentTime - lastSentGetBulbVersionRequest) > GET_BULB_VERSION_RESEND_WAIT_SECONDS) {
430                                                 // Get the bulb version so we know what type of bulb this is.
431                                                 sendGetVersionPacket();
432                                                 lastSentGetBulbVersionRequest = currentTime;
433                                         }
434                                 }
435
436                                 // Communication resource is busy so try again later
437                                 if (sendSocketFlag) {
438                                         continue;
439                                 }
440
441                                 /*socketMutex.lock();
442
443                                 char dat[1024];
444                                 dat = communicationSocket.receiveData(1024);
445
446                                 // Never forget to release!
447                                 socketMutex.unlock();
448
449                                 // A packed arrived
450                                 if (dat != null) {
451                                         receivedPacket(dat);
452                                 }
453
454                                 // If a state change occurred then request the bulb state to ensure that the
455                                 // bulb did indeed change its state to the correct state
456                                 if (stateDidChange) {
457                                         sendGetLightStatePacket();
458                                 }
459
460                                 // Wait a bit as to not tie up system resources
461                                 this_thread::sleep_for (chrono::milliseconds(100));
462                                 } catch (Exception e) {
463
464                                 }*/
465
466
467                         }
468                 }
469
470
471                 //  Sending
472                 //  Device Messages
473                 void sendGetServicePacket() {
474                         LifxHeader header;
475                         header.setSize(36);
476                         header.setTagged(true);
477                         header.setMacAddress(bulbMacAddress);
478                         header.setSource(0);    // randomly picked
479                         header.setAck_required(false);
480                         header.setRes_required(false);
481                         header.setSequence(0);
482                         header.setType(2);
483
484                         char dataBytes[36];
485                         header.getHeaderBytes(dataBytes);
486
487                         sendPacket(dataBytes, 36);
488                 }
489
490
491                 void sendGetHostInfoPacket() {
492                         LifxHeader header;
493                         header.setSize(36);
494                         header.setTagged(false);
495                         header.setMacAddress(bulbMacAddress);
496                         header.setSource(10);   // randomly picked
497                         header.setAck_required(false);
498                         header.setRes_required(false);
499                         header.setSequence(0);
500                         header.setType(12);
501
502                         char dataBytes[36];
503                         header.getHeaderBytes(dataBytes);
504
505                         sendPacket(dataBytes, 36);
506                 }
507
508
509                 void sendGetHostFirmwarePacket() {
510                         LifxHeader header;
511                         header.setSize(36);
512                         header.setTagged(false);
513                         header.setMacAddress(bulbMacAddress);
514                         header.setSource(10);   // randomly picked
515                         header.setAck_required(false);
516                         header.setRes_required(false);
517                         header.setSequence(0);
518                         header.setType(14);
519
520                         char dataBytes[36];
521                         header.getHeaderBytes(dataBytes);
522
523                         sendPacket(dataBytes, 36);
524                 }
525
526
527                 void sendGetWifiInfoPacket() {
528                         LifxHeader header;
529                         header.setSize(36);
530                         header.setTagged(false);
531                         header.setMacAddress(bulbMacAddress);
532                         header.setSource(10);   // randomly picked
533                         header.setAck_required(false);
534                         header.setRes_required(false);
535                         header.setSequence(0);
536                         header.setType(16);
537
538                         char dataBytes[36];
539                         header.getHeaderBytes(dataBytes);
540
541                         sendPacket(dataBytes, 36);
542                 }
543
544
545                 void sendGetWifiFirmwarePacket() {
546                         LifxHeader header;
547                         header.setSize(36);
548                         header.setTagged(false);
549                         header.setMacAddress(bulbMacAddress);
550                         header.setSource(10);   // randomly picked
551                         header.setAck_required(false);
552                         header.setRes_required(false);
553                         header.setSequence(0);
554                         header.setType(18);
555
556                         char dataBytes[36];
557                         header.getHeaderBytes(dataBytes);
558
559                         sendPacket(dataBytes, 36);
560                 }
561
562
563                 void sendGetPowerPacket() {
564                         LifxHeader header;
565                         header.setSize(36);
566                         header.setTagged(false);
567                         header.setMacAddress(bulbMacAddress);
568                         header.setSource(10);   // randomly picked
569                         header.setAck_required(false);
570                         header.setRes_required(false);
571                         header.setSequence(0);
572                         header.setType(20);
573
574                         char dataBytes[36];
575                         header.getHeaderBytes(dataBytes);
576
577                         sendPacket(dataBytes, 36);
578                 }
579
580
581                 void sendSetPowerPacket(int level) {
582                         // Currently only 0 and 65535 are supported
583                         // This is a fix for now
584                         if ((level != 65535) && (level != 0)) {
585                                 cerr << "Invalid parameter values" << endl;
586                                 exit(1);
587                         }
588
589                         if ((level > 65535) || (level < 0)) {
590                                 cerr << "Invalid parameter values" << endl;
591                                 exit(1);
592                         }
593
594                         char packetBytes[38];
595
596                         LifxHeader header;
597                         header.setSize(38);
598                         header.setTagged(false);
599                         header.setMacAddress(bulbMacAddress);
600                         header.setSource(10);   // randomly picked
601                         header.setAck_required(false);
602                         header.setRes_required(false);
603                         header.setSequence(0);
604                         header.setType(21);
605                         char headerBytes[36];
606                         header.getHeaderBytes(headerBytes);
607
608                         for (int i = 0; i < 36; i++) {
609                                 packetBytes[i] = headerBytes[i];
610                         }
611
612                         packetBytes[36] = (char)(level & 0xFF);
613                         packetBytes[37] = (char)((level >> 8) & 0xFF);
614
615                         sendPacket(packetBytes, 38);
616                 }
617
618
619                 void sendGetLabelPacket() {
620                         LifxHeader header;
621                         header.setSize(36);
622                         header.setTagged(false);
623                         header.setMacAddress(bulbMacAddress);
624                         header.setSource(10); // randomly picked
625                         header.setAck_required(false);
626                         header.setRes_required(false);
627                         header.setSequence(0);
628                         header.setType(23);
629
630                         char dataBytes[36];
631                         header.getHeaderBytes(dataBytes);
632
633                         sendPacket(dataBytes, 36);
634                 }
635
636
637                 void sendSetLabelPacket(string label) {
638                         // Currently only 0 and 65535 are supported
639                         // This is a fix for now
640                         if (label.length() != 32) {
641                                 cerr << "Invalid parameter values, label must be 32 bytes long" << endl;
642                                 exit(1);
643                         }
644
645                         char packetBytes[68];
646
647                         LifxHeader header;
648                         header.setSize(68);
649                         header.setTagged(false);
650                         header.setMacAddress(bulbMacAddress);
651                         header.setSource(10); // randomly picked
652                         header.setAck_required(false);
653                         header.setRes_required(false);
654                         header.setSequence(0);
655                         header.setType(24);
656                         char headerBytes[36];
657                         header.getHeaderBytes(headerBytes);
658
659                         for (int i = 0; i < 36; i++) {
660                                 packetBytes[i] = headerBytes[i];
661                         }
662
663                         for (int i = 0; i < 32; i++) {
664                                 packetBytes[i + 36] = label.c_str()[i];
665                         }
666
667                         sendPacket(packetBytes, 68);
668                 }
669
670
671                 void sendGetVersionPacket() {
672                         LifxHeader header;
673                         header.setSize(36);
674                         header.setTagged(false);
675                         header.setMacAddress(bulbMacAddress);
676                         header.setSource(10); // randomly picked
677                         header.setAck_required(false);
678                         header.setRes_required(false);
679                         header.setSequence(0);
680                         header.setType(32);
681
682                         char dataBytes[36];
683                         header.getHeaderBytes(dataBytes);
684
685                         sendPacket(dataBytes, 36);
686                 }
687
688
689                 void sendGetInfoPacket() {
690                         LifxHeader header;
691                         header.setSize(36);
692                         header.setTagged(false);
693                         header.setMacAddress(bulbMacAddress);
694                         header.setSource(10); // randomly picked
695                         header.setAck_required(false);
696                         header.setRes_required(false);
697                         header.setSequence(0);
698                         header.setType(34);
699
700                         char dataBytes[36];
701                         header.getHeaderBytes(dataBytes);
702
703                         sendPacket(dataBytes, 36);
704                 }
705
706
707                 void sendGetLocationPacket() {
708                         LifxHeader header;
709                         header.setSize(36);
710                         header.setTagged(false);
711                         header.setMacAddress(bulbMacAddress);
712                         header.setSource(10); // randomly picked
713                         header.setAck_required(false);
714                         header.setRes_required(false);
715                         header.setSequence(0);
716                         header.setType(34);
717
718                         char dataBytes[36];
719                         header.getHeaderBytes(dataBytes);
720
721                         sendPacket(dataBytes, 36);
722                 }
723
724
725                 void sendGetGroupPacket() {
726                         LifxHeader header;
727                         header.setSize(36);
728                         header.setTagged(false);
729                         header.setMacAddress(bulbMacAddress);
730                         header.setSource(10); // randomly picked
731                         header.setAck_required(false);
732                         header.setRes_required(false);
733                         header.setSequence(0);
734                         header.setType(51);
735
736                         char dataBytes[36];
737                         header.getHeaderBytes(dataBytes);
738
739                         sendPacket(dataBytes, 36);
740                 }
741
742
743                 //  Sending
744                 //  Light Messages
745                 void sendGetLightStatePacket() {
746                         LifxHeader header;
747                         header.setSize(36);
748                         header.setTagged(false);
749                         header.setMacAddress(bulbMacAddress);
750                         header.setSource(10); // randomly picked
751                         header.setAck_required(false);
752                         header.setRes_required(false);
753                         header.setSequence(0);
754                         header.setType(101);
755
756                         char dataBytes[36];
757                         header.getHeaderBytes(dataBytes);
758
759                         sendPacket(dataBytes, 36);
760                 }
761
762
763                 void sendSetLightColorPacket(BulbColor* bulbColor, long duration) {
764
765                         if ((duration > 4294967295l) || (duration < 0)) {
766                                 cerr << "Invalid parameter value, duration out of range (0 - 4294967295)" << endl;
767                                 exit(1);
768                         }
769
770                         char packetBytes[49];
771
772                         LifxHeader header;
773                         header.setSize(49);
774                         header.setTagged(false);
775                         header.setMacAddress(bulbMacAddress);
776                         header.setSource(10); // randomly picked
777                         header.setAck_required(false);
778                         header.setRes_required(false);
779                         header.setSequence(0);
780                         header.setType(102);
781                         char headerBytes[36];
782                         header.getHeaderBytes(headerBytes);
783
784                         for (int i = 0; i < 36; i++) {
785                                 packetBytes[i] = headerBytes[i];
786                         }
787
788                         // 1 reserved packet
789                         packetBytes[37] = (char)(bulbColor->getHue() & 0xFF);
790                         packetBytes[38] = (char)((bulbColor->getHue() >> 8) & 0xFF);
791
792                         packetBytes[39] = (char)(bulbColor->getSaturation() & 0xFF);
793                         packetBytes[40] = (char)((bulbColor->getSaturation() >> 8) & 0xFF);
794
795                         packetBytes[41] = (char)(bulbColor->getBrightness() & 0xFF);
796                         packetBytes[42] = (char)((bulbColor->getBrightness() >> 8) & 0xFF);
797
798                         packetBytes[43] = (char)(bulbColor->getKelvin() & 0xFF);
799                         packetBytes[44] = (char)((bulbColor->getKelvin() >> 8) & 0xFF);
800
801                         packetBytes[45] = (char)((duration >> 0) & 0xFF);
802                         packetBytes[46] = (char)((duration >> 8) & 0xFF);
803                         packetBytes[47] = (char)((duration >> 16) & 0xFF);
804                         packetBytes[48] = (char)((duration >> 24) & 0xFF);
805
806                         sendPacket(packetBytes, 49);
807                         // Avoid memory leak - delete object
808                         delete bulbColor;
809                 }
810
811
812                 void sendGetLightPowerPacket() {
813                         LifxHeader header;
814                         header.setSize(36);
815                         header.setTagged(false);
816                         header.setMacAddress(bulbMacAddress);
817                         header.setSource(10); // randomly picked
818                         header.setAck_required(false);
819                         header.setRes_required(false);
820                         header.setSequence(0);
821                         header.setType(116);
822
823                         char dataBytes[36];
824                         header.getHeaderBytes(dataBytes);
825
826                         sendPacket(dataBytes, 36);
827                 }
828
829
830                 void sendSetLightPowerPacket(int level, long duration) {
831
832                         if ((level > 65535) || (duration > 4294967295l)
833                                     || (level < 0) || (duration < 0)) {
834                                 cerr << "Invalid parameter values" << endl;
835                                 exit(1);
836                         }
837
838                         char packetBytes[42];
839
840
841                         LifxHeader header;
842                         header.setSize(42);
843                         header.setTagged(false);
844                         header.setMacAddress(bulbMacAddress);
845                         header.setSource(10);   // randomly picked
846                         header.setAck_required(false);
847                         header.setRes_required(false);
848                         header.setSequence(0);
849                         header.setType(117);
850                         char headerBytes[36];
851                         header.getHeaderBytes(headerBytes);
852
853                         for (int i = 0; i < 36; i++) {
854                                 packetBytes[i] = headerBytes[i];
855                         }
856
857                         packetBytes[36] = (char)(level & 0xFF);
858                         packetBytes[37] = (char)((level >> 8) & 0xFF);
859
860                         packetBytes[38] = (char)((duration >> 0) & 0xFF);
861                         packetBytes[39] = (char)((duration >> 8) & 0xFF);
862                         packetBytes[40] = (char)((duration >> 16) & 0xFF);
863                         packetBytes[41] = (char)((duration >> 24) & 0xFF);
864
865                         sendPacket(packetBytes, 42);
866                 }
867
868
869                 void sendEchoRequestPacket(char data[64]) {
870
871                         char packetBytes[100];
872
873                         LifxHeader header;
874                         header.setSize(100);
875                         header.setTagged(false);
876                         header.setMacAddress(bulbMacAddress);
877                         header.setSource(10); // randomly picked
878                         header.setAck_required(false);
879                         header.setRes_required(false);
880                         header.setSequence(0);
881                         header.setType(58);
882                         char headerBytes[36];
883                         header.getHeaderBytes(headerBytes);
884
885                         for (int i = 0; i < 36; i++) {
886                                 packetBytes[i] = headerBytes[i];
887                         }
888
889                         for (int i = 0; i < 64; i++) {
890                                 packetBytes[i + 36] = data[i];
891                         }
892
893                         sendPacket(packetBytes, 100);
894                 }
895
896
897                 // Receiving
898                 // Device Messages
899                 DeviceStateService* parseDeviceStateServiceMessage(char* payloadData) {
900                         int service = payloadData[0];
901                         int64_t port = ((payloadData[3] & 0xFF) << 24);
902                         port |= ((payloadData[2] & 0xFF) << 16);
903                         port |= ((payloadData[1] & 0xFF) << 8);
904                         port |= (payloadData[0] & 0xFF);
905
906                         return new DeviceStateService(service, port);
907                 }
908
909
910                 DeviceStateHostInfo* parseDeviceStateHostInfoMessage(char* payloadData) {
911                         long signal = ((payloadData[3] & 0xFF) << 24);
912                         signal |= ((payloadData[2] & 0xFF) << 16);
913                         signal |= ((payloadData[1] & 0xFF) << 8);
914                         signal |= (payloadData[0] & 0xFF);
915
916                         long tx = ((payloadData[7] & 0xFF) << 24);
917                         tx |= ((payloadData[6] & 0xFF) << 16);
918                         tx |= ((payloadData[5] & 0xFF) << 8);
919                         tx |= (payloadData[4] & 0xFF);
920
921                         long rx = ((payloadData[11] & 0xFF) << 24);
922                         rx |= ((payloadData[10] & 0xFF) << 16);
923                         rx |= ((payloadData[9] & 0xFF) << 8);
924                         rx |= (payloadData[8] & 0xFF);
925
926                         return new DeviceStateHostInfo(signal, tx, rx);
927                 }
928
929
930                 DeviceStateHostFirmware* parseDeviceStateHostFirmwareMessage(char* payloadData) {
931                         long build = 0;
932                         for (int i = 0; i < 8; i++) {
933                                 build += ((int64_t) payloadData[i] & 0xffL) << (8 * i);
934                         }
935
936                         // 8 reserved bytes
937
938                         int64_t version = ((payloadData[19] & 0xFF) << 24);
939                         version |= ((payloadData[18] & 0xFF) << 16);
940                         version |= ((payloadData[17] & 0xFF) << 8);
941                         version |= (payloadData[16] & 0xFF);
942
943                         return new DeviceStateHostFirmware(build, version);
944                 }
945
946
947                 DeviceStateWifiInfo* parseDeviceStateWifiInfoMessage(char* payloadData) {
948                         int64_t signal = ((payloadData[3] & 0xFF) << 24);
949                         signal |= ((payloadData[2] & 0xFF) << 16);
950                         signal |= ((payloadData[1] & 0xFF) << 8);
951                         signal |= (payloadData[0] & 0xFF);
952
953                         int64_t tx = ((payloadData[7] & 0xFF) << 24);
954                         tx |= ((payloadData[6] & 0xFF) << 16);
955                         tx |= ((payloadData[5] & 0xFF) << 8);
956                         tx |= (payloadData[4] & 0xFF);
957
958                         int64_t rx = ((payloadData[11] & 0xFF) << 24);
959                         rx |= ((payloadData[10] & 0xFF) << 16);
960                         rx |= ((payloadData[9] & 0xFF) << 8);
961                         rx |= (payloadData[8] & 0xFF);
962
963                         return new DeviceStateWifiInfo(signal, tx, rx);
964                 }
965
966
967                 DeviceStateWifiFirmware* parseDeviceStateWifiFirmwareMessage(char* payloadData) {
968                         long build = 0;
969                         for (int i = 0; i < 8; i++) {
970                                 build += ((int64_t) payloadData[i] & 0xffL) << (8 * i);
971                         }
972
973                         // 8 reserved bytes
974
975                         int64_t version = ((payloadData[19] & 0xFF) << 24);
976                         version |= ((payloadData[18] & 0xFF) << 16);
977                         version |= ((payloadData[17] & 0xFF) << 8);
978                         version |= (payloadData[16] & 0xFF);
979
980                         return new DeviceStateWifiFirmware(build, version);
981                 }
982
983
984                 int parseStatePowerMessage(char* payloadData) {
985                         int level = ((payloadData[1] & 0xFF) << 8);
986                         level |= (payloadData[0] & 0xFF);
987                         return level;
988                 }
989
990
991                 DeviceStateVersion* parseDeviceStateVersionMessage(char* payloadData) {
992                         int64_t vender = ((payloadData[3] & 0xFF) << 24);
993                         vender |= ((payloadData[2] & 0xFF) << 16);
994                         vender |= ((payloadData[1] & 0xFF) << 8);
995                         vender |= (payloadData[0] & 0xFF);
996
997                         int64_t product = ((payloadData[7] & 0xFF) << 24);
998                         product |= ((payloadData[6] & 0xFF) << 16);
999                         product |= ((payloadData[5] & 0xFF) << 8);
1000                         product |= (payloadData[4] & 0xFF);
1001
1002                         int64_t version = ((payloadData[11] & 0xFF) << 24);
1003                         version |= ((payloadData[10] & 0xFF) << 16);
1004                         version |= ((payloadData[9] & 0xFF) << 8);
1005                         version |= (payloadData[8] & 0xFF);
1006
1007                         return new DeviceStateVersion(vender, product, version);
1008                 }
1009
1010
1011                 DeviceStateInfo* parseDeviceStateInfoMessage(char* payloadData) {
1012                         int64_t time = 0;
1013                         int64_t upTime = 0;
1014                         int64_t downTime = 0;
1015                         for (int i = 0; i < 8; i++) {
1016                                 time += ((int64_t) payloadData[i] & 0xffL) << (8 * i);
1017                                 upTime += ((int64_t) payloadData[i + 8] & 0xffL) << (8 * i);
1018                                 downTime += ((int64_t) payloadData[i + 16] & 0xffL) << (8 * i);
1019                         }
1020
1021                         return new DeviceStateInfo(time, upTime, downTime);
1022                 }
1023
1024
1025                 DeviceStateLocation* parseDeviceStateLocationMessage(char* payloadData) {
1026                         char location[16];
1027                         for (int i = 0; i < 16; i++) {
1028                                 location[i] = payloadData[i];
1029                         }
1030
1031                         char labelBytes[32];
1032                         for (int i = 0; i < 32; i++) {
1033                                 labelBytes[i] = payloadData[i + 16];
1034                         }
1035
1036                         int64_t updatedAt = 0;
1037                         for (int i = 0; i < 8; i++) {
1038                                 updatedAt += ((int64_t) payloadData[48] & 0xffL) << (8 * i);
1039                         }
1040
1041                         string str(labelBytes);
1042                         return new DeviceStateLocation(location, str, updatedAt);
1043                 }
1044
1045
1046                 DeviceStateGroup* parseDeviceStateGroupMessage(char* payloadData) {
1047                         char group[16];
1048                         for (int i = 0; i < 16; i++) {
1049                                 group[i] = payloadData[i];
1050                         }
1051
1052                         char labelBytes[32];
1053                         for (int i = 0; i < 32; i++) {
1054                                 labelBytes[i] = payloadData[i + 16];
1055                         }
1056
1057                         int64_t updatedAt = 0;
1058                         for (int i = 0; i < 8; i++) {
1059                                 updatedAt += ((int64_t) payloadData[48] & 0xffL) << (8 * i);
1060                         }
1061
1062                         string str(labelBytes);
1063                         return new DeviceStateGroup(group, str, updatedAt);
1064                 }
1065
1066
1067                 // Receiving
1068                 // Light Messages
1069                 LightState* parseLightStateMessage(char* payloadData) {
1070
1071                         char colorData[8];
1072                         for (int i = 0; i < 8; i++) {
1073                                 colorData[i] = payloadData[i];
1074                         }
1075                         BulbColor color(colorData);
1076
1077                         int power = ((payloadData[11] & 0xFF) << 8);
1078                         power |= (payloadData[10] & 0xFF);
1079
1080                         string label(payloadData);
1081
1082                         char labelArray[32];
1083                         for (int i = 0; i < 32; i++) {
1084                                 labelArray[i] = payloadData[12 + i];
1085                         }
1086
1087                         return new LightState(&color, power, label);
1088                 }
1089
1090
1091                 int parseLightStatePowerMessage(char* payloadData) {
1092                         int level = ((payloadData[1] & 0xFF) << 8);
1093                         level |= (payloadData[0] & 0xFF);
1094                         return level;
1095                 }
1096
1097
1098                 // Private Handlers
1099                 void handleStateVersionMessageReceived(char* payloadData) {
1100
1101                         DeviceStateVersion* deviceState = parseDeviceStateVersionMessage(payloadData);
1102                         int productNumber = (int)deviceState->getProduct();
1103
1104                         bool isColor = false;
1105
1106                         if (productNumber == 1) {// Original 1000
1107                                 isColor = true;
1108                         } else if (productNumber == 3) {//Color 650
1109                                 isColor = true;
1110                         } else if (productNumber == 10) {// White 800 (Low Voltage)
1111                                 isColor = false;
1112                         } else if (productNumber == 11) {// White 800 (High Voltage)
1113                                 isColor = false;
1114                         } else if (productNumber == 18) {// White 900 BR30 (Low Voltage)
1115                                 isColor = false;
1116                         } else if (productNumber == 20) {// Color 1000 BR30
1117                                 isColor = true;
1118                         } else if (productNumber == 22) {// Color 1000
1119                                 isColor = true;
1120                         }
1121
1122                         if (isColor) {
1123                                 hueLowerBound = 0;
1124                                 hueUpperBound = 65535;
1125                                 saturationLowerBound = 0;
1126                                 saturationUpperBound = 65535;
1127                                 brightnessLowerBound = 0;
1128                                 brightnessUpperBound = 65535;
1129                                 temperatureLowerBound = 2500;
1130                                 temperatureUpperBound = 9000;
1131                         } else {
1132                                 hueLowerBound = 0;
1133                                 hueUpperBound = 0;
1134                                 saturationLowerBound = 0;
1135                                 saturationUpperBound = 0;
1136                                 brightnessLowerBound = 0;
1137                                 brightnessUpperBound = 65535;// still can dim bulb
1138                                 temperatureLowerBound = 2500;
1139                                 temperatureUpperBound = 9000;
1140                         }
1141
1142                         didGetBulbVersion.exchange(true);
1143                         // Avoid memory leak - delete this object
1144                         delete deviceState;
1145                 }
1146
1147
1148                 void handleLightStateMessageReceived(char* payloadData) {
1149                         LightState* lightState = parseLightStateMessage(payloadData);
1150
1151                         BulbColor* color = lightState->getColor();
1152                         int power = lightState->getPower();
1153
1154                         bool bulbWrongColor = false;
1155                         bulbWrongColor = bulbWrongColor || (color->getHue() != currentHue);
1156                         bulbWrongColor = bulbWrongColor || (color->getSaturation() != currentSaturation);
1157                         bulbWrongColor = bulbWrongColor || (color->getBrightness() != currentBrightness);
1158                         bulbWrongColor = bulbWrongColor || (color->getKelvin() != currentTemperature);
1159
1160
1161                         // gets set to true if any of the below if statements are taken
1162                         stateDidChange = false;
1163
1164                         if (bulbWrongColor) {
1165                                 BulbColor* newColor = new BulbColor(currentHue, currentSaturation, currentBrightness, currentTemperature);
1166                                 sendSetLightColorPacket(newColor, 250);
1167                                 // System.out.println("Failed Check 1");
1168                         }
1169
1170                         bulbStateMutex.lock();
1171                         bool bulbIsOnTmp = bulbIsOn;
1172                         bulbStateMutex.unlock();
1173
1174                         if ((!bulbIsOnTmp) && (power != 0)) {
1175                                 turnOff();
1176                                 // System.out.println("Failed Check 2:  " + Integer.toString(power));
1177
1178                         }
1179
1180                         if (bulbIsOnTmp && (power < 65530)) {
1181                                 turnOn();
1182                                 // System.out.println("Failed Check 3:  " + Integer.toString(power));
1183
1184                         }
1185                         // Avoid memory leak - delete object
1186                         delete lightState;
1187                         delete color;
1188                 }
1189
1190 };
1191 #endif