Minor fixes in LifxLightBulb driver (not fully tested yet)
[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                                 int ret = communicationSocket->receiveData(dat, 1024);
445
446                                 // Never forget to release!
447                                 socketMutex.unlock();
448
449                                 // A packed arrived
450                                 if (ret != -1) {
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                         }
463                 }
464
465
466                 //  Sending
467                 //  Device Messages
468                 void sendGetServicePacket() {
469                         LifxHeader header;
470                         header.setSize(36);
471                         header.setTagged(true);
472                         header.setMacAddress(bulbMacAddress);
473                         header.setSource(0);    // randomly picked
474                         header.setAck_required(false);
475                         header.setRes_required(false);
476                         header.setSequence(0);
477                         header.setType(2);
478
479                         char dataBytes[36];
480                         header.getHeaderBytes(dataBytes);
481
482                         sendPacket(dataBytes, 36);
483                 }
484
485
486                 void sendGetHostInfoPacket() {
487                         LifxHeader header;
488                         header.setSize(36);
489                         header.setTagged(false);
490                         header.setMacAddress(bulbMacAddress);
491                         header.setSource(10);   // randomly picked
492                         header.setAck_required(false);
493                         header.setRes_required(false);
494                         header.setSequence(0);
495                         header.setType(12);
496
497                         char dataBytes[36];
498                         header.getHeaderBytes(dataBytes);
499
500                         sendPacket(dataBytes, 36);
501                 }
502
503
504                 void sendGetHostFirmwarePacket() {
505                         LifxHeader header;
506                         header.setSize(36);
507                         header.setTagged(false);
508                         header.setMacAddress(bulbMacAddress);
509                         header.setSource(10);   // randomly picked
510                         header.setAck_required(false);
511                         header.setRes_required(false);
512                         header.setSequence(0);
513                         header.setType(14);
514
515                         char dataBytes[36];
516                         header.getHeaderBytes(dataBytes);
517
518                         sendPacket(dataBytes, 36);
519                 }
520
521
522                 void sendGetWifiInfoPacket() {
523                         LifxHeader header;
524                         header.setSize(36);
525                         header.setTagged(false);
526                         header.setMacAddress(bulbMacAddress);
527                         header.setSource(10);   // randomly picked
528                         header.setAck_required(false);
529                         header.setRes_required(false);
530                         header.setSequence(0);
531                         header.setType(16);
532
533                         char dataBytes[36];
534                         header.getHeaderBytes(dataBytes);
535
536                         sendPacket(dataBytes, 36);
537                 }
538
539
540                 void sendGetWifiFirmwarePacket() {
541                         LifxHeader header;
542                         header.setSize(36);
543                         header.setTagged(false);
544                         header.setMacAddress(bulbMacAddress);
545                         header.setSource(10);   // randomly picked
546                         header.setAck_required(false);
547                         header.setRes_required(false);
548                         header.setSequence(0);
549                         header.setType(18);
550
551                         char dataBytes[36];
552                         header.getHeaderBytes(dataBytes);
553
554                         sendPacket(dataBytes, 36);
555                 }
556
557
558                 void sendGetPowerPacket() {
559                         LifxHeader header;
560                         header.setSize(36);
561                         header.setTagged(false);
562                         header.setMacAddress(bulbMacAddress);
563                         header.setSource(10);   // randomly picked
564                         header.setAck_required(false);
565                         header.setRes_required(false);
566                         header.setSequence(0);
567                         header.setType(20);
568
569                         char dataBytes[36];
570                         header.getHeaderBytes(dataBytes);
571
572                         sendPacket(dataBytes, 36);
573                 }
574
575
576                 void sendSetPowerPacket(int level) {
577                         // Currently only 0 and 65535 are supported
578                         // This is a fix for now
579                         if ((level != 65535) && (level != 0)) {
580                                 cerr << "Invalid parameter values" << endl;
581                                 exit(1);
582                         }
583
584                         if ((level > 65535) || (level < 0)) {
585                                 cerr << "Invalid parameter values" << endl;
586                                 exit(1);
587                         }
588
589                         char packetBytes[38];
590
591                         LifxHeader header;
592                         header.setSize(38);
593                         header.setTagged(false);
594                         header.setMacAddress(bulbMacAddress);
595                         header.setSource(10);   // randomly picked
596                         header.setAck_required(false);
597                         header.setRes_required(false);
598                         header.setSequence(0);
599                         header.setType(21);
600                         char headerBytes[36];
601                         header.getHeaderBytes(headerBytes);
602
603                         for (int i = 0; i < 36; i++) {
604                                 packetBytes[i] = headerBytes[i];
605                         }
606
607                         packetBytes[36] = (char)(level & 0xFF);
608                         packetBytes[37] = (char)((level >> 8) & 0xFF);
609
610                         sendPacket(packetBytes, 38);
611                 }
612
613
614                 void sendGetLabelPacket() {
615                         LifxHeader header;
616                         header.setSize(36);
617                         header.setTagged(false);
618                         header.setMacAddress(bulbMacAddress);
619                         header.setSource(10); // randomly picked
620                         header.setAck_required(false);
621                         header.setRes_required(false);
622                         header.setSequence(0);
623                         header.setType(23);
624
625                         char dataBytes[36];
626                         header.getHeaderBytes(dataBytes);
627
628                         sendPacket(dataBytes, 36);
629                 }
630
631
632                 void sendSetLabelPacket(string label) {
633                         // Currently only 0 and 65535 are supported
634                         // This is a fix for now
635                         if (label.length() != 32) {
636                                 cerr << "Invalid parameter values, label must be 32 bytes long" << endl;
637                                 exit(1);
638                         }
639
640                         char packetBytes[68];
641
642                         LifxHeader header;
643                         header.setSize(68);
644                         header.setTagged(false);
645                         header.setMacAddress(bulbMacAddress);
646                         header.setSource(10); // randomly picked
647                         header.setAck_required(false);
648                         header.setRes_required(false);
649                         header.setSequence(0);
650                         header.setType(24);
651                         char headerBytes[36];
652                         header.getHeaderBytes(headerBytes);
653
654                         for (int i = 0; i < 36; i++) {
655                                 packetBytes[i] = headerBytes[i];
656                         }
657
658                         for (int i = 0; i < 32; i++) {
659                                 packetBytes[i + 36] = label.c_str()[i];
660                         }
661
662                         sendPacket(packetBytes, 68);
663                 }
664
665
666                 void sendGetVersionPacket() {
667                         LifxHeader header;
668                         header.setSize(36);
669                         header.setTagged(false);
670                         header.setMacAddress(bulbMacAddress);
671                         header.setSource(10); // randomly picked
672                         header.setAck_required(false);
673                         header.setRes_required(false);
674                         header.setSequence(0);
675                         header.setType(32);
676
677                         char dataBytes[36];
678                         header.getHeaderBytes(dataBytes);
679
680                         sendPacket(dataBytes, 36);
681                 }
682
683
684                 void sendGetInfoPacket() {
685                         LifxHeader header;
686                         header.setSize(36);
687                         header.setTagged(false);
688                         header.setMacAddress(bulbMacAddress);
689                         header.setSource(10); // randomly picked
690                         header.setAck_required(false);
691                         header.setRes_required(false);
692                         header.setSequence(0);
693                         header.setType(34);
694
695                         char dataBytes[36];
696                         header.getHeaderBytes(dataBytes);
697
698                         sendPacket(dataBytes, 36);
699                 }
700
701
702                 void sendGetLocationPacket() {
703                         LifxHeader header;
704                         header.setSize(36);
705                         header.setTagged(false);
706                         header.setMacAddress(bulbMacAddress);
707                         header.setSource(10); // randomly picked
708                         header.setAck_required(false);
709                         header.setRes_required(false);
710                         header.setSequence(0);
711                         header.setType(34);
712
713                         char dataBytes[36];
714                         header.getHeaderBytes(dataBytes);
715
716                         sendPacket(dataBytes, 36);
717                 }
718
719
720                 void sendGetGroupPacket() {
721                         LifxHeader header;
722                         header.setSize(36);
723                         header.setTagged(false);
724                         header.setMacAddress(bulbMacAddress);
725                         header.setSource(10); // randomly picked
726                         header.setAck_required(false);
727                         header.setRes_required(false);
728                         header.setSequence(0);
729                         header.setType(51);
730
731                         char dataBytes[36];
732                         header.getHeaderBytes(dataBytes);
733
734                         sendPacket(dataBytes, 36);
735                 }
736
737
738                 //  Sending
739                 //  Light Messages
740                 void sendGetLightStatePacket() {
741                         LifxHeader header;
742                         header.setSize(36);
743                         header.setTagged(false);
744                         header.setMacAddress(bulbMacAddress);
745                         header.setSource(10); // randomly picked
746                         header.setAck_required(false);
747                         header.setRes_required(false);
748                         header.setSequence(0);
749                         header.setType(101);
750
751                         char dataBytes[36];
752                         header.getHeaderBytes(dataBytes);
753
754                         sendPacket(dataBytes, 36);
755                 }
756
757
758                 void sendSetLightColorPacket(BulbColor* bulbColor, long duration) {
759
760                         if ((duration > 4294967295l) || (duration < 0)) {
761                                 cerr << "Invalid parameter value, duration out of range (0 - 4294967295)" << endl;
762                                 exit(1);
763                         }
764
765                         char packetBytes[49];
766
767                         LifxHeader header;
768                         header.setSize(49);
769                         header.setTagged(false);
770                         header.setMacAddress(bulbMacAddress);
771                         header.setSource(10); // randomly picked
772                         header.setAck_required(false);
773                         header.setRes_required(false);
774                         header.setSequence(0);
775                         header.setType(102);
776                         char headerBytes[36];
777                         header.getHeaderBytes(headerBytes);
778
779                         for (int i = 0; i < 36; i++) {
780                                 packetBytes[i] = headerBytes[i];
781                         }
782
783                         // 1 reserved packet
784                         packetBytes[37] = (char)(bulbColor->getHue() & 0xFF);
785                         packetBytes[38] = (char)((bulbColor->getHue() >> 8) & 0xFF);
786
787                         packetBytes[39] = (char)(bulbColor->getSaturation() & 0xFF);
788                         packetBytes[40] = (char)((bulbColor->getSaturation() >> 8) & 0xFF);
789
790                         packetBytes[41] = (char)(bulbColor->getBrightness() & 0xFF);
791                         packetBytes[42] = (char)((bulbColor->getBrightness() >> 8) & 0xFF);
792
793                         packetBytes[43] = (char)(bulbColor->getKelvin() & 0xFF);
794                         packetBytes[44] = (char)((bulbColor->getKelvin() >> 8) & 0xFF);
795
796                         packetBytes[45] = (char)((duration >> 0) & 0xFF);
797                         packetBytes[46] = (char)((duration >> 8) & 0xFF);
798                         packetBytes[47] = (char)((duration >> 16) & 0xFF);
799                         packetBytes[48] = (char)((duration >> 24) & 0xFF);
800
801                         sendPacket(packetBytes, 49);
802                         // Avoid memory leak - delete object
803                         delete bulbColor;
804                 }
805
806
807                 void sendGetLightPowerPacket() {
808                         LifxHeader header;
809                         header.setSize(36);
810                         header.setTagged(false);
811                         header.setMacAddress(bulbMacAddress);
812                         header.setSource(10); // randomly picked
813                         header.setAck_required(false);
814                         header.setRes_required(false);
815                         header.setSequence(0);
816                         header.setType(116);
817
818                         char dataBytes[36];
819                         header.getHeaderBytes(dataBytes);
820
821                         sendPacket(dataBytes, 36);
822                 }
823
824
825                 void sendSetLightPowerPacket(int level, long duration) {
826
827                         if ((level > 65535) || (duration > 4294967295l)
828                                     || (level < 0) || (duration < 0)) {
829                                 cerr << "Invalid parameter values" << endl;
830                                 exit(1);
831                         }
832
833                         char packetBytes[42];
834
835
836                         LifxHeader header;
837                         header.setSize(42);
838                         header.setTagged(false);
839                         header.setMacAddress(bulbMacAddress);
840                         header.setSource(10);   // randomly picked
841                         header.setAck_required(false);
842                         header.setRes_required(false);
843                         header.setSequence(0);
844                         header.setType(117);
845                         char headerBytes[36];
846                         header.getHeaderBytes(headerBytes);
847
848                         for (int i = 0; i < 36; i++) {
849                                 packetBytes[i] = headerBytes[i];
850                         }
851
852                         packetBytes[36] = (char)(level & 0xFF);
853                         packetBytes[37] = (char)((level >> 8) & 0xFF);
854
855                         packetBytes[38] = (char)((duration >> 0) & 0xFF);
856                         packetBytes[39] = (char)((duration >> 8) & 0xFF);
857                         packetBytes[40] = (char)((duration >> 16) & 0xFF);
858                         packetBytes[41] = (char)((duration >> 24) & 0xFF);
859
860                         sendPacket(packetBytes, 42);
861                 }
862
863
864                 void sendEchoRequestPacket(char data[64]) {
865
866                         char packetBytes[100];
867
868                         LifxHeader header;
869                         header.setSize(100);
870                         header.setTagged(false);
871                         header.setMacAddress(bulbMacAddress);
872                         header.setSource(10); // randomly picked
873                         header.setAck_required(false);
874                         header.setRes_required(false);
875                         header.setSequence(0);
876                         header.setType(58);
877                         char headerBytes[36];
878                         header.getHeaderBytes(headerBytes);
879
880                         for (int i = 0; i < 36; i++) {
881                                 packetBytes[i] = headerBytes[i];
882                         }
883
884                         for (int i = 0; i < 64; i++) {
885                                 packetBytes[i + 36] = data[i];
886                         }
887
888                         sendPacket(packetBytes, 100);
889                 }
890
891
892                 // Receiving
893                 // Device Messages
894                 DeviceStateService* parseDeviceStateServiceMessage(char* payloadData) {
895                         int service = payloadData[0];
896                         int64_t port = ((payloadData[3] & 0xFF) << 24);
897                         port |= ((payloadData[2] & 0xFF) << 16);
898                         port |= ((payloadData[1] & 0xFF) << 8);
899                         port |= (payloadData[0] & 0xFF);
900
901                         return new DeviceStateService(service, port);
902                 }
903
904
905                 DeviceStateHostInfo* parseDeviceStateHostInfoMessage(char* payloadData) {
906                         long signal = ((payloadData[3] & 0xFF) << 24);
907                         signal |= ((payloadData[2] & 0xFF) << 16);
908                         signal |= ((payloadData[1] & 0xFF) << 8);
909                         signal |= (payloadData[0] & 0xFF);
910
911                         long tx = ((payloadData[7] & 0xFF) << 24);
912                         tx |= ((payloadData[6] & 0xFF) << 16);
913                         tx |= ((payloadData[5] & 0xFF) << 8);
914                         tx |= (payloadData[4] & 0xFF);
915
916                         long rx = ((payloadData[11] & 0xFF) << 24);
917                         rx |= ((payloadData[10] & 0xFF) << 16);
918                         rx |= ((payloadData[9] & 0xFF) << 8);
919                         rx |= (payloadData[8] & 0xFF);
920
921                         return new DeviceStateHostInfo(signal, tx, rx);
922                 }
923
924
925                 DeviceStateHostFirmware* parseDeviceStateHostFirmwareMessage(char* payloadData) {
926                         long build = 0;
927                         for (int i = 0; i < 8; i++) {
928                                 build += ((int64_t) payloadData[i] & 0xffL) << (8 * i);
929                         }
930
931                         // 8 reserved bytes
932
933                         int64_t version = ((payloadData[19] & 0xFF) << 24);
934                         version |= ((payloadData[18] & 0xFF) << 16);
935                         version |= ((payloadData[17] & 0xFF) << 8);
936                         version |= (payloadData[16] & 0xFF);
937
938                         return new DeviceStateHostFirmware(build, version);
939                 }
940
941
942                 DeviceStateWifiInfo* parseDeviceStateWifiInfoMessage(char* payloadData) {
943                         int64_t signal = ((payloadData[3] & 0xFF) << 24);
944                         signal |= ((payloadData[2] & 0xFF) << 16);
945                         signal |= ((payloadData[1] & 0xFF) << 8);
946                         signal |= (payloadData[0] & 0xFF);
947
948                         int64_t tx = ((payloadData[7] & 0xFF) << 24);
949                         tx |= ((payloadData[6] & 0xFF) << 16);
950                         tx |= ((payloadData[5] & 0xFF) << 8);
951                         tx |= (payloadData[4] & 0xFF);
952
953                         int64_t rx = ((payloadData[11] & 0xFF) << 24);
954                         rx |= ((payloadData[10] & 0xFF) << 16);
955                         rx |= ((payloadData[9] & 0xFF) << 8);
956                         rx |= (payloadData[8] & 0xFF);
957
958                         return new DeviceStateWifiInfo(signal, tx, rx);
959                 }
960
961
962                 DeviceStateWifiFirmware* parseDeviceStateWifiFirmwareMessage(char* payloadData) {
963                         long build = 0;
964                         for (int i = 0; i < 8; i++) {
965                                 build += ((int64_t) payloadData[i] & 0xffL) << (8 * i);
966                         }
967
968                         // 8 reserved bytes
969
970                         int64_t version = ((payloadData[19] & 0xFF) << 24);
971                         version |= ((payloadData[18] & 0xFF) << 16);
972                         version |= ((payloadData[17] & 0xFF) << 8);
973                         version |= (payloadData[16] & 0xFF);
974
975                         return new DeviceStateWifiFirmware(build, version);
976                 }
977
978
979                 int parseStatePowerMessage(char* payloadData) {
980                         int level = ((payloadData[1] & 0xFF) << 8);
981                         level |= (payloadData[0] & 0xFF);
982                         return level;
983                 }
984
985
986                 DeviceStateVersion* parseDeviceStateVersionMessage(char* payloadData) {
987                         int64_t vender = ((payloadData[3] & 0xFF) << 24);
988                         vender |= ((payloadData[2] & 0xFF) << 16);
989                         vender |= ((payloadData[1] & 0xFF) << 8);
990                         vender |= (payloadData[0] & 0xFF);
991
992                         int64_t product = ((payloadData[7] & 0xFF) << 24);
993                         product |= ((payloadData[6] & 0xFF) << 16);
994                         product |= ((payloadData[5] & 0xFF) << 8);
995                         product |= (payloadData[4] & 0xFF);
996
997                         int64_t version = ((payloadData[11] & 0xFF) << 24);
998                         version |= ((payloadData[10] & 0xFF) << 16);
999                         version |= ((payloadData[9] & 0xFF) << 8);
1000                         version |= (payloadData[8] & 0xFF);
1001
1002                         return new DeviceStateVersion(vender, product, version);
1003                 }
1004
1005
1006                 DeviceStateInfo* parseDeviceStateInfoMessage(char* payloadData) {
1007                         int64_t time = 0;
1008                         int64_t upTime = 0;
1009                         int64_t downTime = 0;
1010                         for (int i = 0; i < 8; i++) {
1011                                 time += ((int64_t) payloadData[i] & 0xffL) << (8 * i);
1012                                 upTime += ((int64_t) payloadData[i + 8] & 0xffL) << (8 * i);
1013                                 downTime += ((int64_t) payloadData[i + 16] & 0xffL) << (8 * i);
1014                         }
1015
1016                         return new DeviceStateInfo(time, upTime, downTime);
1017                 }
1018
1019
1020                 DeviceStateLocation* parseDeviceStateLocationMessage(char* payloadData) {
1021                         char location[16];
1022                         for (int i = 0; i < 16; i++) {
1023                                 location[i] = payloadData[i];
1024                         }
1025
1026                         char labelBytes[32];
1027                         for (int i = 0; i < 32; i++) {
1028                                 labelBytes[i] = payloadData[i + 16];
1029                         }
1030
1031                         int64_t updatedAt = 0;
1032                         for (int i = 0; i < 8; i++) {
1033                                 updatedAt += ((int64_t) payloadData[48] & 0xffL) << (8 * i);
1034                         }
1035
1036                         string str(labelBytes);
1037                         return new DeviceStateLocation(location, str, updatedAt);
1038                 }
1039
1040
1041                 DeviceStateGroup* parseDeviceStateGroupMessage(char* payloadData) {
1042                         char group[16];
1043                         for (int i = 0; i < 16; i++) {
1044                                 group[i] = payloadData[i];
1045                         }
1046
1047                         char labelBytes[32];
1048                         for (int i = 0; i < 32; i++) {
1049                                 labelBytes[i] = payloadData[i + 16];
1050                         }
1051
1052                         int64_t updatedAt = 0;
1053                         for (int i = 0; i < 8; i++) {
1054                                 updatedAt += ((int64_t) payloadData[48] & 0xffL) << (8 * i);
1055                         }
1056
1057                         string str(labelBytes);
1058                         return new DeviceStateGroup(group, str, updatedAt);
1059                 }
1060
1061
1062                 // Receiving
1063                 // Light Messages
1064                 LightState* parseLightStateMessage(char* payloadData) {
1065
1066                         char colorData[8];
1067                         for (int i = 0; i < 8; i++) {
1068                                 colorData[i] = payloadData[i];
1069                         }
1070                         BulbColor color(colorData);
1071
1072                         int power = ((payloadData[11] & 0xFF) << 8);
1073                         power |= (payloadData[10] & 0xFF);
1074
1075                         string label(payloadData);
1076
1077                         char labelArray[32];
1078                         for (int i = 0; i < 32; i++) {
1079                                 labelArray[i] = payloadData[12 + i];
1080                         }
1081
1082                         return new LightState(&color, power, label);
1083                 }
1084
1085
1086                 int parseLightStatePowerMessage(char* payloadData) {
1087                         int level = ((payloadData[1] & 0xFF) << 8);
1088                         level |= (payloadData[0] & 0xFF);
1089                         return level;
1090                 }
1091
1092
1093                 // Private Handlers
1094                 void handleStateVersionMessageReceived(char* payloadData) {
1095
1096                         DeviceStateVersion* deviceState = parseDeviceStateVersionMessage(payloadData);
1097                         int productNumber = (int)deviceState->getProduct();
1098
1099                         bool isColor = false;
1100
1101                         if (productNumber == 1) {// Original 1000
1102                                 isColor = true;
1103                         } else if (productNumber == 3) {//Color 650
1104                                 isColor = true;
1105                         } else if (productNumber == 10) {// White 800 (Low Voltage)
1106                                 isColor = false;
1107                         } else if (productNumber == 11) {// White 800 (High Voltage)
1108                                 isColor = false;
1109                         } else if (productNumber == 18) {// White 900 BR30 (Low Voltage)
1110                                 isColor = false;
1111                         } else if (productNumber == 20) {// Color 1000 BR30
1112                                 isColor = true;
1113                         } else if (productNumber == 22) {// Color 1000
1114                                 isColor = true;
1115                         }
1116
1117                         if (isColor) {
1118                                 hueLowerBound = 0;
1119                                 hueUpperBound = 65535;
1120                                 saturationLowerBound = 0;
1121                                 saturationUpperBound = 65535;
1122                                 brightnessLowerBound = 0;
1123                                 brightnessUpperBound = 65535;
1124                                 temperatureLowerBound = 2500;
1125                                 temperatureUpperBound = 9000;
1126                         } else {
1127                                 hueLowerBound = 0;
1128                                 hueUpperBound = 0;
1129                                 saturationLowerBound = 0;
1130                                 saturationUpperBound = 0;
1131                                 brightnessLowerBound = 0;
1132                                 brightnessUpperBound = 65535;// still can dim bulb
1133                                 temperatureLowerBound = 2500;
1134                                 temperatureUpperBound = 9000;
1135                         }
1136
1137                         didGetBulbVersion.exchange(true);
1138                         // Avoid memory leak - delete this object
1139                         delete deviceState;
1140                 }
1141
1142
1143                 void handleLightStateMessageReceived(char* payloadData) {
1144                         LightState* lightState = parseLightStateMessage(payloadData);
1145
1146                         BulbColor* color = lightState->getColor();
1147                         int power = lightState->getPower();
1148
1149                         bool bulbWrongColor = false;
1150                         bulbWrongColor = bulbWrongColor || (color->getHue() != currentHue);
1151                         bulbWrongColor = bulbWrongColor || (color->getSaturation() != currentSaturation);
1152                         bulbWrongColor = bulbWrongColor || (color->getBrightness() != currentBrightness);
1153                         bulbWrongColor = bulbWrongColor || (color->getKelvin() != currentTemperature);
1154
1155
1156                         // gets set to true if any of the below if statements are taken
1157                         stateDidChange = false;
1158
1159                         if (bulbWrongColor) {
1160                                 BulbColor* newColor = new BulbColor(currentHue, currentSaturation, currentBrightness, currentTemperature);
1161                                 sendSetLightColorPacket(newColor, 250);
1162                                 // System.out.println("Failed Check 1");
1163                         }
1164
1165                         bulbStateMutex.lock();
1166                         bool bulbIsOnTmp = bulbIsOn;
1167                         bulbStateMutex.unlock();
1168
1169                         if ((!bulbIsOnTmp) && (power != 0)) {
1170                                 turnOff();
1171                                 // System.out.println("Failed Check 2:  " + Integer.toString(power));
1172
1173                         }
1174
1175                         if (bulbIsOnTmp && (power < 65530)) {
1176                                 turnOn();
1177                                 // System.out.println("Failed Check 3:  " + Integer.toString(power));
1178
1179                         }
1180                         // Avoid memory leak - delete object
1181                         delete lightState;
1182                         delete color;
1183                 }
1184
1185 };
1186 #endif