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