1 package iotcode.LifxLightBulb;
3 // Standard Java Packages
6 import java.util.concurrent.Semaphore;
7 import java.security.InvalidParameterException;
9 import java.util.Iterator;
10 import java.util.concurrent.atomic.AtomicBoolean;
12 import java.util.Arrays;
14 import java.util.HashSet;
17 import iotcode.annotation.*;
18 import iotcode.interfaces.LightBulb;
19 import iotruntime.IoTUDP;
20 import iotruntime.slave.IoTDeviceAddress;
21 import iotruntime.slave.IoTSet;
23 // String to byte conversion
24 import javax.xml.bind.DatatypeConverter;
26 public class LifxLightBulb implements LightBulb {
28 /*******************************************************************************************************************************************
32 *******************************************************************************************************************************************/
33 public static final long GET_BULB_VERSION_RESEND_WAIT_SECONDS = 10;
37 /*******************************************************************************************************************************************
41 *******************************************************************************************************************************************/
42 private IoTUDP communicationSockect;
43 private byte[] bulbMacAddress = new byte[8];
44 static Semaphore socketMutex = new Semaphore(1);
45 static boolean sendSocketFlag = false;
46 private long lastSentGetBulbVersionRequest = 0; // time last request sent
48 // Current Bulb Values
49 private int currentHue = 0;
50 private int currentSaturation = 0;
51 private int currentBrightness = 65535;
52 private int currentTemperature = 9000;
53 private boolean bulbIsOn = false;
57 private AtomicBoolean didAlreadyInit = new AtomicBoolean(false);
59 private AtomicBoolean didGetBulbVersion = new AtomicBoolean(false);
60 static Semaphore settingBulbColorMutex = new Semaphore(1);
61 static Semaphore settingBulbTempuraturerMutex = new Semaphore(1);
62 static Semaphore bulbStateMutex = new Semaphore(1);
64 // color and temperature ranges for the bulbs
65 private int hueLowerBound = 0;
66 private int hueUpperBound = 0;
67 private int saturationLowerBound = 0;
68 private int saturationUpperBound = 0;
69 private int brightnessLowerBound = 0;
70 private int brightnessUpperBound = 0;
71 private int temperatureLowerBound = 2500;
72 private int temperatureUpperBound = 9000;
76 // Check if a state change was requested, used to poll the bulb for if the bulb did
77 // preform the requested state change
78 private boolean stateDidChange = false;
80 /*******************************************************************************************************************************************
84 *******************************************************************************************************************************************/
86 // Main worker thread will do the receive loop
87 Thread workerThread = null;
89 /*******************************************************************************************************************************************
91 ** IoT Sets and Relations
93 *******************************************************************************************************************************************/
95 // IoTSet of Device Addresses.
96 // Will be filled with only 1 address.
97 @config private IoTSet<IoTDeviceAddress> lb_addresses;
100 * Used for testing only
102 /*public LifxLightBulb(IoTUDP udp, byte[] macAddress) {
103 communicationSockect = udp;
104 bulbMacAddress = macAddress;
107 public LifxLightBulb(IoTSet<IoTDeviceAddress> _lb_addresses, String macAddress) {
109 lb_addresses = _lb_addresses;
112 public LifxLightBulb(String macAddress) {
113 communicationSockect = null;
115 // Set the Mac Address to a default value
116 // Probably not needed for anything
117 /*bulbMacAdd[0] = (byte)0x00;
118 bulbMacAdd[1] = (byte)0x00;
119 bulbMacAdd[2] = (byte)0x00;
120 bulbMacAdd[3] = (byte)0x00;
121 bulbMacAdd[4] = (byte)0x00;
122 bulbMacAdd[5] = (byte)0x00;
123 bulbMacAdd[6] = (byte)0x00;
124 bulbMacAdd[7] = (byte)0x00;*/
126 bulbMacAddress = DatatypeConverter.parseHexBinary(macAddress);
131 /*******************************************************************************************************************************************
135 *******************************************************************************************************************************************/
136 private void sendGetServicePacket() {
137 LifxHeader header = new LifxHeader();
139 header.setTagged(true);
140 header.setMacAddress(bulbMacAddress);
141 header.setSource(0); // randomly picked
142 header.setAck_required(false);
143 header.setRes_required(false);
144 header.setSequence(0);
147 byte[] dataBytes = header.getHeaderBytes();
148 sendPacket(dataBytes);
151 private void sendGetHostInfoPacket() {
152 LifxHeader header = new LifxHeader();
154 header.setTagged(false);
155 header.setMacAddress(bulbMacAddress);
156 header.setSource(10); // randomly picked
157 header.setAck_required(false);
158 header.setRes_required(false);
159 header.setSequence(0);
162 byte[] dataBytes = header.getHeaderBytes();
163 sendPacket(dataBytes);
166 private void sendGetHostFirmwarePacket() {
167 LifxHeader header = new LifxHeader();
169 header.setTagged(false);
170 header.setMacAddress(bulbMacAddress);
171 header.setSource(10); // randomly picked
172 header.setAck_required(false);
173 header.setRes_required(false);
174 header.setSequence(0);
177 byte[] dataBytes = header.getHeaderBytes();
178 sendPacket(dataBytes);
181 private void sendGetWifiInfoPacket() {
182 LifxHeader header = new LifxHeader();
184 header.setTagged(false);
185 header.setMacAddress(bulbMacAddress);
186 header.setSource(10); // randomly picked
187 header.setAck_required(false);
188 header.setRes_required(false);
189 header.setSequence(0);
192 byte[] dataBytes = header.getHeaderBytes();
193 sendPacket(dataBytes);
196 private void sendGetWifiFirmwarePacket() {
197 LifxHeader header = new LifxHeader();
199 header.setTagged(false);
200 header.setMacAddress(bulbMacAddress);
201 header.setSource(10); // randomly picked
202 header.setAck_required(false);
203 header.setRes_required(false);
204 header.setSequence(0);
207 byte[] dataBytes = header.getHeaderBytes();
208 sendPacket(dataBytes);
211 private void sendGetPowerPacket() {
212 LifxHeader header = new LifxHeader();
214 header.setTagged(false);
215 header.setMacAddress(bulbMacAddress);
216 header.setSource(10); // randomly picked
217 header.setAck_required(false);
218 header.setRes_required(false);
219 header.setSequence(0);
222 byte[] dataBytes = header.getHeaderBytes();
223 sendPacket(dataBytes);
226 private void sendSetPowerPacket(int level) {
227 // Currently only 0 and 65535 are supported
228 // This is a fix for now
229 if ((level != 65535) && (level != 0)) {
230 throw new InvalidParameterException("Invalid parameter values");
233 if ((level > 65535) || (level < 0)) {
234 throw new InvalidParameterException("Invalid parameter values");
237 byte[] packetBytes = new byte[38];
239 LifxHeader header = new LifxHeader();
241 header.setTagged(false);
242 header.setMacAddress(bulbMacAddress);
243 header.setSource(10); // randomly picked
244 header.setAck_required(false);
245 header.setRes_required(false);
246 header.setSequence(0);
248 byte[] headerBytes = header.getHeaderBytes();
250 for (int i = 0; i < 36; i++) {
251 packetBytes[i] = headerBytes[i];
254 packetBytes[36] = (byte)(level & 0xFF);
255 packetBytes[37] = (byte)((level >> 8) & 0xFF);
257 sendPacket(packetBytes);
260 private void sendGetLabelPacket() {
261 LifxHeader header = new LifxHeader();
263 header.setTagged(false);
264 header.setMacAddress(bulbMacAddress);
265 header.setSource(10); // randomly picked
266 header.setAck_required(false);
267 header.setRes_required(false);
268 header.setSequence(0);
271 byte[] dataBytes = header.getHeaderBytes();
272 sendPacket(dataBytes);
275 private void sendSetLabelPacket(String label) {
276 // Currently only 0 and 65535 are supported
277 // This is a fix for now
278 if (label.length() != 32) {
279 throw new InvalidParameterException("Invalid parameter values, label must be 32 bytes long");
282 byte[] packetBytes = new byte[68];
284 LifxHeader header = new LifxHeader();
286 header.setTagged(false);
287 header.setMacAddress(bulbMacAddress);
288 header.setSource(10); // randomly picked
289 header.setAck_required(false);
290 header.setRes_required(false);
291 header.setSequence(0);
293 byte[] headerBytes = header.getHeaderBytes();
295 for (int i = 0; i < 36; i++) {
296 packetBytes[i] = headerBytes[i];
299 for (int i = 0; i < 32; i++) {
300 packetBytes[i + 36] = label.getBytes()[i];
303 sendPacket(packetBytes);
306 private void sendGetVersionPacket() {
307 LifxHeader header = new LifxHeader();
309 header.setTagged(false);
310 header.setMacAddress(bulbMacAddress);
311 header.setSource(10); // randomly picked
312 header.setAck_required(false);
313 header.setRes_required(false);
314 header.setSequence(0);
317 byte[] dataBytes = header.getHeaderBytes();
318 sendPacket(dataBytes);
321 private void sendGetInfoPacket() {
322 LifxHeader header = new LifxHeader();
324 header.setTagged(false);
325 header.setMacAddress(bulbMacAddress);
326 header.setSource(10); // randomly picked
327 header.setAck_required(false);
328 header.setRes_required(false);
329 header.setSequence(0);
332 byte[] dataBytes = header.getHeaderBytes();
333 sendPacket(dataBytes);
336 private void sendGetLocationPacket() {
337 LifxHeader header = new LifxHeader();
339 header.setTagged(false);
340 header.setMacAddress(bulbMacAddress);
341 header.setSource(10); // randomly picked
342 header.setAck_required(false);
343 header.setRes_required(false);
344 header.setSequence(0);
347 byte[] dataBytes = header.getHeaderBytes();
348 sendPacket(dataBytes);
351 private void sendGetGroupPacket() {
352 LifxHeader header = new LifxHeader();
354 header.setTagged(false);
355 header.setMacAddress(bulbMacAddress);
356 header.setSource(10); // randomly picked
357 header.setAck_required(false);
358 header.setRes_required(false);
359 header.setSequence(0);
362 byte[] dataBytes = header.getHeaderBytes();
363 sendPacket(dataBytes);
367 /*******************************************************************************************************************************************
371 *******************************************************************************************************************************************/
372 private void sendGetLightStatePacket() {
373 LifxHeader header = new LifxHeader();
375 header.setTagged(false);
376 header.setMacAddress(bulbMacAddress);
377 header.setSource(10); // randomly picked
378 header.setAck_required(false);
379 header.setRes_required(false);
380 header.setSequence(0);
383 byte[] dataBytes = header.getHeaderBytes();
384 sendPacket(dataBytes);
387 private void sendSetLightColorPacket(BulbColor bulbColor, long duration) {
389 if ((duration > 4294967295l) || (duration < 0)) {
390 throw new InvalidParameterException("Invalid parameter value, duration out of range (0 - 4294967295)");
393 byte[] packetBytes = new byte[49];
395 LifxHeader header = new LifxHeader();
397 header.setTagged(false);
398 header.setMacAddress(bulbMacAddress);
399 header.setSource(10); // randomly picked
400 header.setAck_required(false);
401 header.setRes_required(false);
402 header.setSequence(0);
404 byte[] headerBytes = header.getHeaderBytes();
406 for (int i = 0; i < 36; i++) {
407 packetBytes[i] = headerBytes[i];
411 packetBytes[37] = (byte)(bulbColor.getHue() & 0xFF);
412 packetBytes[38] = (byte)((bulbColor.getHue() >> 8) & 0xFF);
414 packetBytes[39] = (byte)(bulbColor.getSaturation() & 0xFF);
415 packetBytes[40] = (byte)((bulbColor.getSaturation() >> 8) & 0xFF);
417 packetBytes[41] = (byte)(bulbColor.getBrightness() & 0xFF);
418 packetBytes[42] = (byte)((bulbColor.getBrightness() >> 8) & 0xFF);
420 packetBytes[43] = (byte)(bulbColor.getKelvin() & 0xFF);
421 packetBytes[44] = (byte)((bulbColor.getKelvin() >> 8) & 0xFF);
423 packetBytes[45] = (byte)((duration >> 0) & 0xFF);
424 packetBytes[46] = (byte)((duration >> 8) & 0xFF);
425 packetBytes[47] = (byte)((duration >> 16) & 0xFF);
426 packetBytes[48] = (byte)((duration >> 24) & 0xFF);
428 sendPacket(packetBytes);
431 private void sendGetLightPowerPacket() {
432 LifxHeader header = new LifxHeader();
434 header.setTagged(false);
435 header.setMacAddress(bulbMacAddress);
436 header.setSource(10); // randomly picked
437 header.setAck_required(false);
438 header.setRes_required(false);
439 header.setSequence(0);
442 byte[] dataBytes = header.getHeaderBytes();
443 sendPacket(dataBytes);
446 private void sendSetLightPowerPacket(int level, long duration) {
448 if ((level > 65535) || (duration > 4294967295l)
449 || (level < 0) || (duration < 0)) {
450 throw new InvalidParameterException("Invalid parameter values");
453 byte[] packetBytes = new byte[42];
456 LifxHeader header = new LifxHeader();
458 header.setTagged(false);
459 header.setMacAddress(bulbMacAddress);
460 header.setSource(10); // randomly picked
461 header.setAck_required(false);
462 header.setRes_required(false);
463 header.setSequence(0);
465 byte[] headerBytes = header.getHeaderBytes();
467 for (int i = 0; i < 36; i++) {
468 packetBytes[i] = headerBytes[i];
471 packetBytes[36] = (byte)(level & 0xFF);
472 packetBytes[37] = (byte)((level >> 8) & 0xFF);
474 packetBytes[38] = (byte)((duration >> 0) & 0xFF);
475 packetBytes[39] = (byte)((duration >> 8) & 0xFF);
476 packetBytes[40] = (byte)((duration >> 16) & 0xFF);
477 packetBytes[41] = (byte)((duration >> 24) & 0xFF);
479 System.out.println(Arrays.toString(packetBytes));
481 sendPacket(packetBytes);
484 private void sendEchoRequestPacket(byte[] data) {
485 // Currently only 0 and 65535 are supported
486 // This is a fix for now
487 if (data.length != 64) {
488 throw new InvalidParameterException("Invalid parameter values, must have 64 bytes");
491 byte[] packetBytes = new byte[100];
493 LifxHeader header = new LifxHeader();
495 header.setTagged(false);
496 header.setMacAddress(bulbMacAddress);
497 header.setSource(10); // randomly picked
498 header.setAck_required(false);
499 header.setRes_required(false);
500 header.setSequence(0);
502 byte[] headerBytes = header.getHeaderBytes();
504 for (int i = 0; i < 36; i++) {
505 packetBytes[i] = headerBytes[i];
508 for (int i = 0; i < 64; i++) {
509 packetBytes[i + 36] = data[i];
512 sendPacket(packetBytes);
516 /*******************************************************************************************************************************************
520 *******************************************************************************************************************************************/
521 private DeviceStateService parseDeviceStateServiceMessage(LifxHeader header, byte[] payloadData) {
522 int service = payloadData[0];
523 long port = ((payloadData[3] & 0xFF) << 24);
524 port |= ((payloadData[2] & 0xFF) << 16);
525 port |= ((payloadData[1] & 0xFF) << 8);
526 port |= (payloadData[0] & 0xFF);
528 return new DeviceStateService(service, port);
531 private DeviceStateHostInfo parseDeviceStateHostInfoMessage(LifxHeader header, byte[] payloadData) {
532 long signal = ((payloadData[3] & 0xFF) << 24);
533 signal |= ((payloadData[2] & 0xFF) << 16);
534 signal |= ((payloadData[1] & 0xFF) << 8);
535 signal |= (payloadData[0] & 0xFF);
537 long tx = ((payloadData[7] & 0xFF) << 24);
538 tx |= ((payloadData[6] & 0xFF) << 16);
539 tx |= ((payloadData[5] & 0xFF) << 8);
540 tx |= (payloadData[4] & 0xFF);
542 long rx = ((payloadData[11] & 0xFF) << 24);
543 rx |= ((payloadData[10] & 0xFF) << 16);
544 rx |= ((payloadData[9] & 0xFF) << 8);
545 rx |= (payloadData[8] & 0xFF);
547 return new DeviceStateHostInfo(signal, tx, rx);
550 private DeviceStateHostFirmware parseDeviceStateHostFirmwareMessage(LifxHeader header, byte[] payloadData) {
552 for (int i = 0; i < 8; i++) {
553 build += ((long) payloadData[i] & 0xffL) << (8 * i);
558 long version = ((payloadData[19] & 0xFF) << 24);
559 version |= ((payloadData[18] & 0xFF) << 16);
560 version |= ((payloadData[17] & 0xFF) << 8);
561 version |= (payloadData[16] & 0xFF);
563 return new DeviceStateHostFirmware(build, version);
566 private DeviceStateWifiInfo parseDeviceStateWifiInfoMessage(LifxHeader header, byte[] payloadData) {
567 long signal = ((payloadData[3] & 0xFF) << 24);
568 signal |= ((payloadData[2] & 0xFF) << 16);
569 signal |= ((payloadData[1] & 0xFF) << 8);
570 signal |= (payloadData[0] & 0xFF);
572 long tx = ((payloadData[7] & 0xFF) << 24);
573 tx |= ((payloadData[6] & 0xFF) << 16);
574 tx |= ((payloadData[5] & 0xFF) << 8);
575 tx |= (payloadData[4] & 0xFF);
577 long rx = ((payloadData[11] & 0xFF) << 24);
578 rx |= ((payloadData[10] & 0xFF) << 16);
579 rx |= ((payloadData[9] & 0xFF) << 8);
580 rx |= (payloadData[8] & 0xFF);
582 return new DeviceStateWifiInfo(signal, tx, rx);
585 private DeviceStateWifiFirmware parseDeviceStateWifiFirmwareMessage(LifxHeader header, byte[] payloadData) {
587 for (int i = 0; i < 8; i++) {
588 build += ((long) payloadData[i] & 0xffL) << (8 * i);
593 long version = ((payloadData[19] & 0xFF) << 24);
594 version |= ((payloadData[18] & 0xFF) << 16);
595 version |= ((payloadData[17] & 0xFF) << 8);
596 version |= (payloadData[16] & 0xFF);
598 return new DeviceStateWifiFirmware(build, version);
601 private int parseStatePowerMessage(LifxHeader header, byte[] payloadData) {
602 int level = ((payloadData[1] & 0xFF) << 8);
603 level |= (payloadData[0] & 0xFF);
607 private String parseStateLabelMessage(LifxHeader header, byte[] payloadData) {
608 return new String(payloadData);
612 private DeviceStateVersion parseDeviceStateVersionMessage(LifxHeader header, byte[] payloadData) {
613 long vender = ((payloadData[3] & 0xFF) << 24);
614 vender |= ((payloadData[2] & 0xFF) << 16);
615 vender |= ((payloadData[1] & 0xFF) << 8);
616 vender |= (payloadData[0] & 0xFF);
618 long product = ((payloadData[7] & 0xFF) << 24);
619 product |= ((payloadData[6] & 0xFF) << 16);
620 product |= ((payloadData[5] & 0xFF) << 8);
621 product |= (payloadData[4] & 0xFF);
623 long version = ((payloadData[11] & 0xFF) << 24);
624 version |= ((payloadData[10] & 0xFF) << 16);
625 version |= ((payloadData[9] & 0xFF) << 8);
626 version |= (payloadData[8] & 0xFF);
628 return new DeviceStateVersion(vender, product, version);
631 private DeviceStateInfo parseDeviceStateInfoMessage(LifxHeader header, byte[] payloadData) {
635 for (int i = 0; i < 8; i++) {
636 time += ((long) payloadData[i] & 0xffL) << (8 * i);
637 upTime += ((long) payloadData[i + 8] & 0xffL) << (8 * i);
638 downTime += ((long) payloadData[i + 16] & 0xffL) << (8 * i);
641 return new DeviceStateInfo(time, upTime, downTime);
644 private DeviceStateLocation parseDeviceStateLocationMessage(LifxHeader header, byte[] payloadData) {
645 byte[] location = new byte[16];
646 for (int i = 0; i < 16; i++) {
647 location[i] = payloadData[i];
650 byte[] labelBytes = new byte[32];
651 for (int i = 0; i < 32; i++) {
652 labelBytes[i] = payloadData[i + 16];
656 for (int i = 0; i < 8; i++) {
657 updatedAt += ((long) payloadData[48] & 0xffL) << (8 * i);
660 return new DeviceStateLocation(location, new String(labelBytes), updatedAt);
663 private DeviceStateGroup parseDeviceStateGroupMessage(LifxHeader header, byte[] payloadData) {
664 byte[] group = new byte[16];
665 for (int i = 0; i < 16; i++) {
666 group[i] = payloadData[i];
669 byte[] labelBytes = new byte[32];
670 for (int i = 0; i < 32; i++) {
671 labelBytes[i] = payloadData[i + 16];
675 for (int i = 0; i < 8; i++) {
676 updatedAt += ((long) payloadData[48] & 0xffL) << (8 * i);
679 return new DeviceStateGroup(group, new String(labelBytes), updatedAt);
682 private byte[] parseDeviceEchoResponseMessage(LifxHeader header, byte[] payloadData) {
686 /*******************************************************************************************************************************************
690 *******************************************************************************************************************************************/
691 private LightState parseLightStateMessage(LifxHeader header, byte[] payloadData) {
693 byte[] colorData = new byte[8];
694 for (int i = 0; i < 8; i++) {
695 colorData[i] = payloadData[i];
697 BulbColor color = new BulbColor(colorData);
699 int power = ((payloadData[11] & 0xFF) << 8);
700 power |= (payloadData[10] & 0xFF);
702 String label = new String(payloadData);
704 byte[] labelArray = new byte[32];
705 for (int i = 0; i < 32; i++) {
706 labelArray[i] = payloadData[12 + i];
709 return new LightState(color, power, label);
712 private int parseLightStatePowerMessage(LifxHeader header, byte[] payloadData) {
713 int level = ((payloadData[1] & 0xFF) << 8);
714 level |= (payloadData[0] & 0xFF);
719 /*******************************************************************************************************************************************
723 *******************************************************************************************************************************************/
724 private void handleStateVersionMessageRecieved(LifxHeader header, byte[] payloadData) {
726 DeviceStateVersion deviceState = parseDeviceStateVersionMessage(header, payloadData);
727 int productNumber = (int)deviceState.getProduct();
729 boolean isColor = false;
731 if (productNumber == 1) {// Original 1000
733 } else if (productNumber == 3) {//Color 650
735 } else if (productNumber == 10) {// White 800 (Low Voltage)
737 } else if (productNumber == 11) {// White 800 (High Voltage)
739 } else if (productNumber == 18) {// White 900 BR30 (Low Voltage)
741 } else if (productNumber == 20) {// Color 1000 BR30
743 } else if (productNumber == 22) {// Color 1000
749 hueUpperBound = 65535;
750 saturationLowerBound = 0;
751 saturationUpperBound = 65535;
752 brightnessLowerBound = 0;
753 brightnessUpperBound = 65535;
754 temperatureLowerBound = 2500;
755 temperatureUpperBound = 9000;
759 saturationLowerBound = 0;
760 saturationUpperBound = 0;
761 brightnessLowerBound = 0;
762 brightnessUpperBound = 65535;// still can dim bulb
763 temperatureLowerBound = 2500;
764 temperatureUpperBound = 9000;
767 didGetBulbVersion.set(true);
771 private void handleLightStateMessageRecieved(LifxHeader header, byte[] payloadData) {
772 LightState lightState = parseLightStateMessage(header, payloadData);
774 BulbColor color = lightState.getColor();
775 int power = lightState.getPower();
777 boolean bulbWrongColor = false;
778 bulbWrongColor = bulbWrongColor || (color.getHue() != currentHue);
779 bulbWrongColor = bulbWrongColor || (color.getSaturation() != currentSaturation);
780 bulbWrongColor = bulbWrongColor || (color.getBrightness() != currentBrightness);
781 bulbWrongColor = bulbWrongColor || (color.getKelvin() != currentTemperature);
784 // gets set to true if any of the below if statements are taken
785 stateDidChange = false;
787 if (bulbWrongColor) {
788 BulbColor newColor = new BulbColor(currentHue, currentSaturation, currentBrightness, currentTemperature);
789 sendSetLightColorPacket(newColor, 250);
790 // System.out.println("Failed Check 1");
794 bulbStateMutex.acquire();
795 } catch (Exception e) {
798 boolean bulbIsOnTmp = bulbIsOn;
799 bulbStateMutex.release();
801 if ((!bulbIsOnTmp) && (power != 0)) {
803 // System.out.println("Failed Check 2: " + Integer.toString(power));
807 if (bulbIsOnTmp && (power < 65530)) {
809 // System.out.println("Failed Check 3: " + Integer.toString(power));
814 /*******************************************************************************************************************************************
816 ** Light Bulb Interface Methods
818 *******************************************************************************************************************************************/
819 public double getHue() {
822 settingBulbColorMutex.acquire();
823 tmp = ((double)currentHue / 65535.0) * 360.0;
824 } catch (Exception e) {
827 settingBulbColorMutex.release();
833 public double getSaturation() {
836 settingBulbColorMutex.acquire();
837 tmp = ((double)currentSaturation / 65535.0) * 360.0;
838 } catch (Exception e) {
841 settingBulbColorMutex.release();
847 public double getBrightness() {
850 settingBulbColorMutex.acquire();
851 tmp = ((double)currentBrightness / 65535.0) * 360.0;
852 } catch (Exception e) {
855 settingBulbColorMutex.release();
860 public int getTemperature() {
864 settingBulbTempuraturerMutex.acquire();
865 tmp = currentTemperature;
866 } catch (Exception e) {
869 settingBulbTempuraturerMutex.release();
874 public double getHueRangeLowerBound() {
875 if (!didGetBulbVersion.get()) {
878 return ((double)hueLowerBound / 65535.0) * 360.0;
881 public double getHueRangeUpperBound() {
882 if (!didGetBulbVersion.get()) {
885 return ((double)hueUpperBound / 65535.0) * 360.0;
888 public double getSaturationRangeLowerBound() {
889 if (!didGetBulbVersion.get()) {
892 return ((double)saturationLowerBound / 65535.0) * 100.0;
895 public double getSaturationRangeUpperBound() {
896 if (!didGetBulbVersion.get()) {
899 return ((double)saturationUpperBound / 65535.0) * 100.0;
902 public double getBrightnessRangeLowerBound() {
903 if (!didGetBulbVersion.get()) {
906 return ((double)brightnessLowerBound / 65535.0) * 100.0;
909 public double getBrightnessRangeUpperBound() {
910 if (!didGetBulbVersion.get()) {
913 return ((double)brightnessUpperBound / 65535.0) * 100.0;
916 public int getTemperatureRangeLowerBound() {
917 if (!didGetBulbVersion.get()) {
920 return temperatureLowerBound;
923 public int getTemperatureRangeUpperBound() {
924 if (!didGetBulbVersion.get()) {
927 return temperatureUpperBound;
930 public void setTemperature(int _temperature) {
933 settingBulbTempuraturerMutex.acquire();
934 } catch (Exception e) {
938 BulbColor newColor = new BulbColor(currentHue, currentSaturation, currentBrightness, _temperature);
939 sendSetLightColorPacket(newColor, 250);
941 currentTemperature = _temperature;
942 stateDidChange = true;
944 settingBulbTempuraturerMutex.release();
947 public void setColor(double _hue, double _saturation, double _brightness) {
950 settingBulbColorMutex.acquire();
951 } catch (Exception e) {
957 _saturation /= 100.0;
958 _brightness /= 100.0;
961 int newHue = (int)(_hue * 65535.0);
962 int newSaturation = (int)(_saturation * 65535.0);
963 int newBrightness = (int)(_brightness * 65535.0);
965 BulbColor newColor = new BulbColor(newHue, newSaturation, newBrightness, currentTemperature);
966 sendSetLightColorPacket(newColor, 250);
969 currentSaturation = newSaturation;
970 currentBrightness = newBrightness;
971 stateDidChange = true;
973 settingBulbColorMutex.release();
977 public void turnOff() {
980 bulbStateMutex.acquire();
982 sendSetLightPowerPacket(0, 0);
983 stateDidChange = true;
984 } catch (Exception e) {
988 bulbStateMutex.release();
991 public void turnOn() {
993 bulbStateMutex.acquire();
995 sendSetLightPowerPacket(65535, 0);
996 stateDidChange = true;
998 } catch (Exception e) {
1003 bulbStateMutex.release();
1006 public boolean getState() {
1008 boolean tmp = false;
1010 bulbStateMutex.acquire();
1012 } catch (Exception e) {
1013 e.printStackTrace();
1016 bulbStateMutex.release();
1022 /*******************************************************************************************************************************************
1024 ** Communication Helpers
1026 *******************************************************************************************************************************************/
1027 private void recievedPacket(byte[] packetData) {
1029 byte[] headerBytes = new byte[36];
1030 for (int i = 0; i < 36; i++) {
1031 headerBytes[i] = packetData[i];
1034 LifxHeader recHeader = new LifxHeader();
1035 recHeader.setFromBytes(headerBytes);
1037 // load the payload bytes (strip away the header)
1038 byte[] payloadBytes = new byte[recHeader.getSize()];
1039 for (int i = 36; i < recHeader.getSize(); i++) {
1040 payloadBytes[i - 36] = packetData[i];
1043 System.out.println("Received: " + Integer.toString(recHeader.getType()));
1045 switch (recHeader.getType()) {
1047 DeviceStateService dat = parseDeviceStateServiceMessage(recHeader, payloadBytes);
1048 // System.out.println("Service: " + Integer.toString(dat.getService()));
1049 // System.out.println("Port : " + Long.toString(dat.getPort()));
1054 handleStateVersionMessageRecieved(recHeader, payloadBytes);
1058 parseDeviceStateInfoMessage(recHeader, payloadBytes);
1063 handleLightStateMessageRecieved(recHeader, payloadBytes);
1067 // System.out.println("unknown packet Type");
1072 private void sendPacket(byte[] packetData) {
1073 // System.out.println("About to send");
1074 sendSocketFlag = true;
1077 socketMutex.acquire();
1078 } catch (InterruptedException e) {
1079 System.out.println("mutex Error");
1083 communicationSockect.sendData(packetData);
1085 } catch (IOException e) {
1086 System.out.println("Socket Send Error");
1089 sendSocketFlag = false;
1090 socketMutex.release();
1095 * Worker function which runs the while loop for receiving data from the bulb.
1098 private void workerFunction() {
1099 LifxHeader h = new LifxHeader();
1102 // Need timeout on receives since we are not sure if a packet will be available
1103 // for processing so don't block waiting
1104 communicationSockect.setSoTimeout(50);
1105 } catch (IOException e) {
1108 // Start the bulb in the off state
1113 // Check if we got the bulb version yet
1114 // could have requested it but message could have gotten lost (UDP)
1115 if (!didGetBulbVersion.get()) {
1116 long currentTime = (new Date().getTime()) / 1000;
1117 if ((currentTime - lastSentGetBulbVersionRequest) > GET_BULB_VERSION_RESEND_WAIT_SECONDS) {
1118 // Get the bulb version so we know what type of bulb this is.
1119 sendGetVersionPacket();
1120 lastSentGetBulbVersionRequest = currentTime;
1124 // Communication resource is busy so try again later
1125 if (sendSocketFlag) {
1130 socketMutex.acquire();
1131 } catch (InterruptedException e) {
1136 dat = communicationSockect.recieveData(1024);
1137 } catch (java.net.SocketTimeoutException e) {
1140 } catch (IOException e) {
1141 // Problem but might be able to recover??
1142 e.printStackTrace();
1146 // Never forget to release!
1147 socketMutex.release();
1151 recievedPacket(dat);
1154 // If a state change occurred then request the bulb state to ensure that the
1155 // bulb did indeed change its state to the correct state
1156 if (stateDidChange) {
1157 sendGetLightStatePacket();
1160 // Wait a bit as to not tie up system resources
1163 } catch (Exception e) {
1172 public void init() {
1174 if (didAlreadyInit.compareAndSet(false, true) == false) {
1175 return; // already init
1179 // Get the bulb address from the IoTSet
1180 Iterator itr = lb_addresses.iterator();
1181 IoTDeviceAddress deviceAddress = (IoTDeviceAddress)itr.next();
1183 System.out.println("Address: " + deviceAddress.getCompleteAddress());
1185 // Create the communication channel
1186 communicationSockect = new IoTUDP(deviceAddress);
1188 } catch (IOException e) {
1189 e.printStackTrace();
1192 // Launch the worker function in a separate thread.
1193 workerThread = new Thread(new Runnable() {
1198 workerThread.start();
1203 /*public static void main(String[] args) throws Exception {
1205 System.out.println("Executing main function!");
1206 IoTDeviceAddress iotDevAdd = new IoTDeviceAddress("192.168.2.126", 12345, 56700, false, false);
1207 Set<IoTDeviceAddress> set = new HashSet<IoTDeviceAddress>();
1209 IoTSet<IoTDeviceAddress> iotset = new IoTSet<IoTDeviceAddress>(set);
1210 LifxLightBulb lb = new LifxLightBulb(iotset, "D073D5128E300000");