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;
14 //import iotcode.annotation.*;
15 import iotcode.interfaces.LightBulb;
16 import iotruntime.IoTUDP;
17 import iotruntime.slave.IoTDeviceAddress;
18 import iotruntime.slave.IoTSet;
20 // String to byte conversion
21 import javax.xml.bind.DatatypeConverter;
23 import iotchecker.qual.*;
25 public class LifxLightBulb implements LightBulb {
27 /*******************************************************************************************************************************************
31 *******************************************************************************************************************************************/
32 public static final long GET_BULB_VERSION_RESEND_WAIT_SECONDS = 10;
36 /*******************************************************************************************************************************************
40 *******************************************************************************************************************************************/
41 private IoTUDP communicationSockect;
42 private byte[] bulbMacAddress = new byte[8];
43 static Semaphore socketMutex = new Semaphore(1);
44 static boolean sendSocketFlag = false;
45 private long lastSentGetBulbVersionRequest = 0; // time last request sent
47 // Current Bulb Values
48 private int currentHue = 0;
49 private int currentSaturation = 0;
50 private int currentBrightness = 65535;
51 private int currentTemperature = 9000;
52 private boolean bulbIsOn = false;
56 private AtomicBoolean didAlreadyInit = new AtomicBoolean(false);
58 private AtomicBoolean didGetBulbVersion = new AtomicBoolean(false);
59 static Semaphore settingBulbColorMutex = new Semaphore(1);
60 static Semaphore settingBulbTempuraturerMutex = new Semaphore(1);
61 static Semaphore bulbStateMutex = new Semaphore(1);
63 // color and temperature ranges for the bulbs
64 private int hueLowerBound = 0;
65 private int hueUpperBound = 0;
66 private int saturationLowerBound = 0;
67 private int saturationUpperBound = 0;
68 private int brightnessLowerBound = 0;
69 private int brightnessUpperBound = 0;
70 private int temperatureLowerBound = 2500;
71 private int temperatureUpperBound = 9000;
75 // Check if a state change was requested, used to poll the bulb for if the bulb did
76 // preform the requested state change
77 private boolean stateDidChange = false;
79 /*******************************************************************************************************************************************
83 *******************************************************************************************************************************************/
85 // Main worker thread will do the receive loop
86 Thread workerThread = null;
88 /*******************************************************************************************************************************************
90 ** IoT Sets and Relations
92 *******************************************************************************************************************************************/
94 // IoTSet of Device Addresses.
95 // Will be filled with only 1 address.
96 @config private IoTSet<IoTDeviceAddress> lb_addresses;
99 * Used for testing only
101 public LifxLightBulb(IoTUDP udp, byte[] macAddress) {
102 communicationSockect = udp;
103 bulbMacAddress = macAddress;
106 public LifxLightBulb(String macAddress) {
107 communicationSockect = null;
109 // Set the Mac Address to a default value
110 // Probably not needed for anything
111 /*bulbMacAdd[0] = (byte)0x00;
112 bulbMacAdd[1] = (byte)0x00;
113 bulbMacAdd[2] = (byte)0x00;
114 bulbMacAdd[3] = (byte)0x00;
115 bulbMacAdd[4] = (byte)0x00;
116 bulbMacAdd[5] = (byte)0x00;
117 bulbMacAdd[6] = (byte)0x00;
118 bulbMacAdd[7] = (byte)0x00;*/
120 bulbMacAddress = DatatypeConverter.parseHexBinary(macAddress);
125 /*******************************************************************************************************************************************
129 *******************************************************************************************************************************************/
130 private void sendGetServicePacket() {
131 LifxHeader header = new LifxHeader();
133 header.setTagged(true);
134 header.setMacAddress(bulbMacAddress);
135 header.setSource(0); // randomly picked
136 header.setAck_required(false);
137 header.setRes_required(false);
138 header.setSequence(0);
141 byte[] dataBytes = header.getHeaderBytes();
142 sendPacket(dataBytes);
145 private void sendGetHostInfoPacket() {
146 LifxHeader header = new LifxHeader();
148 header.setTagged(false);
149 header.setMacAddress(bulbMacAddress);
150 header.setSource(10); // randomly picked
151 header.setAck_required(false);
152 header.setRes_required(false);
153 header.setSequence(0);
156 byte[] dataBytes = header.getHeaderBytes();
157 sendPacket(dataBytes);
160 private void sendGetHostFirmwarePacket() {
161 LifxHeader header = new LifxHeader();
163 header.setTagged(false);
164 header.setMacAddress(bulbMacAddress);
165 header.setSource(10); // randomly picked
166 header.setAck_required(false);
167 header.setRes_required(false);
168 header.setSequence(0);
171 byte[] dataBytes = header.getHeaderBytes();
172 sendPacket(dataBytes);
175 private void sendGetWifiInfoPacket() {
176 LifxHeader header = new LifxHeader();
178 header.setTagged(false);
179 header.setMacAddress(bulbMacAddress);
180 header.setSource(10); // randomly picked
181 header.setAck_required(false);
182 header.setRes_required(false);
183 header.setSequence(0);
186 byte[] dataBytes = header.getHeaderBytes();
187 sendPacket(dataBytes);
190 private void sendGetWifiFirmwarePacket() {
191 LifxHeader header = new LifxHeader();
193 header.setTagged(false);
194 header.setMacAddress(bulbMacAddress);
195 header.setSource(10); // randomly picked
196 header.setAck_required(false);
197 header.setRes_required(false);
198 header.setSequence(0);
201 byte[] dataBytes = header.getHeaderBytes();
202 sendPacket(dataBytes);
205 private void sendGetPowerPacket() {
206 LifxHeader header = new LifxHeader();
208 header.setTagged(false);
209 header.setMacAddress(bulbMacAddress);
210 header.setSource(10); // randomly picked
211 header.setAck_required(false);
212 header.setRes_required(false);
213 header.setSequence(0);
216 byte[] dataBytes = header.getHeaderBytes();
217 sendPacket(dataBytes);
220 private void sendSetPowerPacket(int level) {
221 // Currently only 0 and 65535 are supported
222 // This is a fix for now
223 if ((level != 65535) && (level != 0)) {
224 throw new InvalidParameterException("Invalid parameter values");
227 if ((level > 65535) || (level < 0)) {
228 throw new InvalidParameterException("Invalid parameter values");
231 byte[] packetBytes = new byte[38];
233 LifxHeader header = new LifxHeader();
235 header.setTagged(false);
236 header.setMacAddress(bulbMacAddress);
237 header.setSource(10); // randomly picked
238 header.setAck_required(false);
239 header.setRes_required(false);
240 header.setSequence(0);
242 byte[] headerBytes = header.getHeaderBytes();
244 for (int i = 0; i < 36; i++) {
245 packetBytes[i] = headerBytes[i];
248 packetBytes[36] = (byte)(level & 0xFF);
249 packetBytes[37] = (byte)((level >> 8) & 0xFF);
251 sendPacket(packetBytes);
254 private void sendGetLabelPacket() {
255 LifxHeader header = new LifxHeader();
257 header.setTagged(false);
258 header.setMacAddress(bulbMacAddress);
259 header.setSource(10); // randomly picked
260 header.setAck_required(false);
261 header.setRes_required(false);
262 header.setSequence(0);
265 byte[] dataBytes = header.getHeaderBytes();
266 sendPacket(dataBytes);
269 private void sendSetLabelPacket(String label) {
270 // Currently only 0 and 65535 are supported
271 // This is a fix for now
272 if (label.length() != 32) {
273 throw new InvalidParameterException("Invalid parameter values, label must be 32 bytes long");
276 byte[] packetBytes = new byte[68];
278 LifxHeader header = new LifxHeader();
280 header.setTagged(false);
281 header.setMacAddress(bulbMacAddress);
282 header.setSource(10); // randomly picked
283 header.setAck_required(false);
284 header.setRes_required(false);
285 header.setSequence(0);
287 byte[] headerBytes = header.getHeaderBytes();
289 for (int i = 0; i < 36; i++) {
290 packetBytes[i] = headerBytes[i];
293 for (int i = 0; i < 32; i++) {
294 packetBytes[i + 36] = label.getBytes()[i];
297 sendPacket(packetBytes);
300 private void sendGetVersionPacket() {
301 LifxHeader header = new LifxHeader();
303 header.setTagged(false);
304 header.setMacAddress(bulbMacAddress);
305 header.setSource(10); // randomly picked
306 header.setAck_required(false);
307 header.setRes_required(false);
308 header.setSequence(0);
311 byte[] dataBytes = header.getHeaderBytes();
312 sendPacket(dataBytes);
315 private void sendGetInfoPacket() {
316 LifxHeader header = new LifxHeader();
318 header.setTagged(false);
319 header.setMacAddress(bulbMacAddress);
320 header.setSource(10); // randomly picked
321 header.setAck_required(false);
322 header.setRes_required(false);
323 header.setSequence(0);
326 byte[] dataBytes = header.getHeaderBytes();
327 sendPacket(dataBytes);
330 private void sendGetLocationPacket() {
331 LifxHeader header = new LifxHeader();
333 header.setTagged(false);
334 header.setMacAddress(bulbMacAddress);
335 header.setSource(10); // randomly picked
336 header.setAck_required(false);
337 header.setRes_required(false);
338 header.setSequence(0);
341 byte[] dataBytes = header.getHeaderBytes();
342 sendPacket(dataBytes);
345 private void sendGetGroupPacket() {
346 LifxHeader header = new LifxHeader();
348 header.setTagged(false);
349 header.setMacAddress(bulbMacAddress);
350 header.setSource(10); // randomly picked
351 header.setAck_required(false);
352 header.setRes_required(false);
353 header.setSequence(0);
356 byte[] dataBytes = header.getHeaderBytes();
357 sendPacket(dataBytes);
361 /*******************************************************************************************************************************************
365 *******************************************************************************************************************************************/
366 private void sendGetLightStatePacket() {
367 LifxHeader header = new LifxHeader();
369 header.setTagged(false);
370 header.setMacAddress(bulbMacAddress);
371 header.setSource(10); // randomly picked
372 header.setAck_required(false);
373 header.setRes_required(false);
374 header.setSequence(0);
377 byte[] dataBytes = header.getHeaderBytes();
378 sendPacket(dataBytes);
381 private void sendSetLightColorPacket(BulbColor bulbColor, long duration) {
383 if ((duration > 4294967295l) || (duration < 0)) {
384 throw new InvalidParameterException("Invalid parameter value, duration out of range (0 - 4294967295)");
387 byte[] packetBytes = new byte[49];
389 LifxHeader header = new LifxHeader();
391 header.setTagged(false);
392 header.setMacAddress(bulbMacAddress);
393 header.setSource(10); // randomly picked
394 header.setAck_required(false);
395 header.setRes_required(false);
396 header.setSequence(0);
398 byte[] headerBytes = header.getHeaderBytes();
400 for (int i = 0; i < 36; i++) {
401 packetBytes[i] = headerBytes[i];
405 packetBytes[37] = (byte)(bulbColor.getHue() & 0xFF);
406 packetBytes[38] = (byte)((bulbColor.getHue() >> 8) & 0xFF);
408 packetBytes[39] = (byte)(bulbColor.getSaturation() & 0xFF);
409 packetBytes[40] = (byte)((bulbColor.getSaturation() >> 8) & 0xFF);
411 packetBytes[41] = (byte)(bulbColor.getBrightness() & 0xFF);
412 packetBytes[42] = (byte)((bulbColor.getBrightness() >> 8) & 0xFF);
414 packetBytes[43] = (byte)(bulbColor.getKelvin() & 0xFF);
415 packetBytes[44] = (byte)((bulbColor.getKelvin() >> 8) & 0xFF);
417 packetBytes[45] = (byte)((duration >> 0) & 0xFF);
418 packetBytes[46] = (byte)((duration >> 8) & 0xFF);
419 packetBytes[47] = (byte)((duration >> 16) & 0xFF);
420 packetBytes[48] = (byte)((duration >> 24) & 0xFF);
422 sendPacket(packetBytes);
425 private void sendGetLightPowerPacket() {
426 LifxHeader header = new LifxHeader();
428 header.setTagged(false);
429 header.setMacAddress(bulbMacAddress);
430 header.setSource(10); // randomly picked
431 header.setAck_required(false);
432 header.setRes_required(false);
433 header.setSequence(0);
436 byte[] dataBytes = header.getHeaderBytes();
437 sendPacket(dataBytes);
440 private void sendSetLightPowerPacket(int level, long duration) {
442 if ((level > 65535) || (duration > 4294967295l)
443 || (level < 0) || (duration < 0)) {
444 throw new InvalidParameterException("Invalid parameter values");
447 byte[] packetBytes = new byte[42];
450 LifxHeader header = new LifxHeader();
452 header.setTagged(false);
453 header.setMacAddress(bulbMacAddress);
454 header.setSource(10); // randomly picked
455 header.setAck_required(false);
456 header.setRes_required(false);
457 header.setSequence(0);
459 byte[] headerBytes = header.getHeaderBytes();
461 for (int i = 0; i < 36; i++) {
462 packetBytes[i] = headerBytes[i];
465 packetBytes[36] = (byte)(level & 0xFF);
466 packetBytes[37] = (byte)((level >> 8) & 0xFF);
468 packetBytes[38] = (byte)((duration >> 0) & 0xFF);
469 packetBytes[39] = (byte)((duration >> 8) & 0xFF);
470 packetBytes[40] = (byte)((duration >> 16) & 0xFF);
471 packetBytes[41] = (byte)((duration >> 24) & 0xFF);
473 sendPacket(packetBytes);
476 private void sendEchoRequestPacket(byte[] data) {
477 // Currently only 0 and 65535 are supported
478 // This is a fix for now
479 if (data.length != 64) {
480 throw new InvalidParameterException("Invalid parameter values, must have 64 bytes");
483 byte[] packetBytes = new byte[100];
485 LifxHeader header = new LifxHeader();
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);
494 byte[] headerBytes = header.getHeaderBytes();
496 for (int i = 0; i < 36; i++) {
497 packetBytes[i] = headerBytes[i];
500 for (int i = 0; i < 64; i++) {
501 packetBytes[i + 36] = data[i];
504 sendPacket(packetBytes);
508 /*******************************************************************************************************************************************
512 *******************************************************************************************************************************************/
513 private DeviceStateService parseDeviceStateServiceMessage(LifxHeader header, byte[] payloadData) {
514 int service = payloadData[0];
515 long port = ((payloadData[3] & 0xFF) << 24);
516 port |= ((payloadData[2] & 0xFF) << 16);
517 port |= ((payloadData[1] & 0xFF) << 8);
518 port |= (payloadData[0] & 0xFF);
520 return new DeviceStateService(service, port);
523 private DeviceStateHostInfo parseDeviceStateHostInfoMessage(LifxHeader header, byte[] payloadData) {
524 long signal = ((payloadData[3] & 0xFF) << 24);
525 signal |= ((payloadData[2] & 0xFF) << 16);
526 signal |= ((payloadData[1] & 0xFF) << 8);
527 signal |= (payloadData[0] & 0xFF);
529 long tx = ((payloadData[7] & 0xFF) << 24);
530 tx |= ((payloadData[6] & 0xFF) << 16);
531 tx |= ((payloadData[5] & 0xFF) << 8);
532 tx |= (payloadData[4] & 0xFF);
534 long rx = ((payloadData[11] & 0xFF) << 24);
535 rx |= ((payloadData[10] & 0xFF) << 16);
536 rx |= ((payloadData[9] & 0xFF) << 8);
537 rx |= (payloadData[8] & 0xFF);
539 return new DeviceStateHostInfo(signal, tx, rx);
542 private DeviceStateHostFirmware parseDeviceStateHostFirmwareMessage(LifxHeader header, byte[] payloadData) {
544 for (int i = 0; i < 8; i++) {
545 build += ((long) payloadData[i] & 0xffL) << (8 * i);
550 long version = ((payloadData[19] & 0xFF) << 24);
551 version |= ((payloadData[18] & 0xFF) << 16);
552 version |= ((payloadData[17] & 0xFF) << 8);
553 version |= (payloadData[16] & 0xFF);
555 return new DeviceStateHostFirmware(build, version);
558 private DeviceStateWifiInfo parseDeviceStateWifiInfoMessage(LifxHeader header, byte[] payloadData) {
559 long signal = ((payloadData[3] & 0xFF) << 24);
560 signal |= ((payloadData[2] & 0xFF) << 16);
561 signal |= ((payloadData[1] & 0xFF) << 8);
562 signal |= (payloadData[0] & 0xFF);
564 long tx = ((payloadData[7] & 0xFF) << 24);
565 tx |= ((payloadData[6] & 0xFF) << 16);
566 tx |= ((payloadData[5] & 0xFF) << 8);
567 tx |= (payloadData[4] & 0xFF);
569 long rx = ((payloadData[11] & 0xFF) << 24);
570 rx |= ((payloadData[10] & 0xFF) << 16);
571 rx |= ((payloadData[9] & 0xFF) << 8);
572 rx |= (payloadData[8] & 0xFF);
574 return new DeviceStateWifiInfo(signal, tx, rx);
577 private DeviceStateWifiFirmware parseDeviceStateWifiFirmwareMessage(LifxHeader header, byte[] payloadData) {
579 for (int i = 0; i < 8; i++) {
580 build += ((long) payloadData[i] & 0xffL) << (8 * i);
585 long version = ((payloadData[19] & 0xFF) << 24);
586 version |= ((payloadData[18] & 0xFF) << 16);
587 version |= ((payloadData[17] & 0xFF) << 8);
588 version |= (payloadData[16] & 0xFF);
590 return new DeviceStateWifiFirmware(build, version);
593 private int parseStatePowerMessage(LifxHeader header, byte[] payloadData) {
594 int level = ((payloadData[1] & 0xFF) << 8);
595 level |= (payloadData[0] & 0xFF);
599 private String parseStateLabelMessage(LifxHeader header, byte[] payloadData) {
600 return new String(payloadData);
604 private DeviceStateVersion parseDeviceStateVersionMessage(LifxHeader header, byte[] payloadData) {
605 long vender = ((payloadData[3] & 0xFF) << 24);
606 vender |= ((payloadData[2] & 0xFF) << 16);
607 vender |= ((payloadData[1] & 0xFF) << 8);
608 vender |= (payloadData[0] & 0xFF);
610 long product = ((payloadData[7] & 0xFF) << 24);
611 product |= ((payloadData[6] & 0xFF) << 16);
612 product |= ((payloadData[5] & 0xFF) << 8);
613 product |= (payloadData[4] & 0xFF);
615 long version = ((payloadData[11] & 0xFF) << 24);
616 version |= ((payloadData[10] & 0xFF) << 16);
617 version |= ((payloadData[9] & 0xFF) << 8);
618 version |= (payloadData[8] & 0xFF);
620 return new DeviceStateVersion(vender, product, version);
623 private DeviceStateInfo parseDeviceStateInfoMessage(LifxHeader header, byte[] payloadData) {
627 for (int i = 0; i < 8; i++) {
628 time += ((long) payloadData[i] & 0xffL) << (8 * i);
629 upTime += ((long) payloadData[i + 8] & 0xffL) << (8 * i);
630 downTime += ((long) payloadData[i + 16] & 0xffL) << (8 * i);
633 return new DeviceStateInfo(time, upTime, downTime);
636 private DeviceStateLocation parseDeviceStateLocationMessage(LifxHeader header, byte[] payloadData) {
637 byte[] location = new byte[16];
638 for (int i = 0; i < 16; i++) {
639 location[i] = payloadData[i];
642 byte[] labelBytes = new byte[32];
643 for (int i = 0; i < 32; i++) {
644 labelBytes[i] = payloadData[i + 16];
648 for (int i = 0; i < 8; i++) {
649 updatedAt += ((long) payloadData[48] & 0xffL) << (8 * i);
652 return new DeviceStateLocation(location, new String(labelBytes), updatedAt);
655 private DeviceStateGroup parseDeviceStateGroupMessage(LifxHeader header, byte[] payloadData) {
656 byte[] group = new byte[16];
657 for (int i = 0; i < 16; i++) {
658 group[i] = payloadData[i];
661 byte[] labelBytes = new byte[32];
662 for (int i = 0; i < 32; i++) {
663 labelBytes[i] = payloadData[i + 16];
667 for (int i = 0; i < 8; i++) {
668 updatedAt += ((long) payloadData[48] & 0xffL) << (8 * i);
671 return new DeviceStateGroup(group, new String(labelBytes), updatedAt);
674 private byte[] parseDeviceEchoResponseMessage(LifxHeader header, byte[] payloadData) {
678 /*******************************************************************************************************************************************
682 *******************************************************************************************************************************************/
683 private LightState parseLightStateMessage(LifxHeader header, byte[] payloadData) {
685 byte[] colorData = new byte[8];
686 for (int i = 0; i < 8; i++) {
687 colorData[i] = payloadData[i];
689 BulbColor color = new BulbColor(colorData);
691 int power = ((payloadData[11] & 0xFF) << 8);
692 power |= (payloadData[10] & 0xFF);
694 String label = new String(payloadData);
696 byte[] labelArray = new byte[32];
697 for (int i = 0; i < 32; i++) {
698 labelArray[i] = payloadData[12 + i];
701 return new LightState(color, power, label);
704 private int parseLightStatePowerMessage(LifxHeader header, byte[] payloadData) {
705 int level = ((payloadData[1] & 0xFF) << 8);
706 level |= (payloadData[0] & 0xFF);
711 /*******************************************************************************************************************************************
715 *******************************************************************************************************************************************/
716 private void handleStateVersionMessageRecieved(LifxHeader header, byte[] payloadData) {
718 DeviceStateVersion deviceState = parseDeviceStateVersionMessage(header, payloadData);
719 int productNumber = (int)deviceState.getProduct();
721 boolean isColor = false;
723 if (productNumber == 1) { // Original 1000
725 } else if (productNumber == 3) { //Color 650
727 } else if (productNumber == 10) { // White 800 (Low Voltage)
729 } else if (productNumber == 11) { // White 800 (High Voltage)
731 } else if (productNumber == 18) { // White 900 BR30 (Low Voltage)
733 } else if (productNumber == 20) { // Color 1000 BR30
735 } else if (productNumber == 22) { // Color 1000
741 hueUpperBound = 65535;
742 saturationLowerBound = 0;
743 saturationUpperBound = 65535;
744 brightnessLowerBound = 0;
745 brightnessUpperBound = 65535;
746 temperatureLowerBound = 2500;
747 temperatureUpperBound = 9000;
751 saturationLowerBound = 0;
752 saturationUpperBound = 0;
753 brightnessLowerBound = 0;
754 brightnessUpperBound = 65535; // still can dim bulb
755 temperatureLowerBound = 2500;
756 temperatureUpperBound = 9000;
759 didGetBulbVersion.set(true);
763 private void handleLightStateMessageRecieved(LifxHeader header, byte[] payloadData) {
764 LightState lightState = parseLightStateMessage(header, payloadData);
766 BulbColor color = lightState.getColor();
767 int power = lightState.getPower();
769 boolean bulbWrongColor = false;
770 bulbWrongColor = bulbWrongColor || (color.getHue() != currentHue);
771 bulbWrongColor = bulbWrongColor || (color.getSaturation() != currentSaturation);
772 bulbWrongColor = bulbWrongColor || (color.getBrightness() != currentBrightness);
773 bulbWrongColor = bulbWrongColor || (color.getKelvin() != currentTemperature);
776 // gets set to true if any of the below if statements are taken
777 stateDidChange = false;
779 if (bulbWrongColor) {
780 BulbColor newColor = new BulbColor(currentHue, currentSaturation, currentBrightness, currentTemperature);
781 sendSetLightColorPacket(newColor, 250);
782 // System.out.println("Failed Check 1");
786 bulbStateMutex.acquire();
787 } catch (Exception e) {
790 boolean bulbIsOnTmp = bulbIsOn;
791 bulbStateMutex.release();
793 if ((!bulbIsOnTmp) && (power != 0)) {
795 // System.out.println("Failed Check 2: " + Integer.toString(power));
799 if (bulbIsOnTmp && (power < 65530)) {
801 // System.out.println("Failed Check 3: " + Integer.toString(power));
806 /*******************************************************************************************************************************************
808 ** Light Bulb Interface Methods
810 *******************************************************************************************************************************************/
811 public double getHue() {
814 settingBulbColorMutex.acquire();
815 tmp = ((double)currentHue / 65535.0) * 360.0;
816 } catch (Exception e) {
819 settingBulbColorMutex.release();
825 public double getSaturation() {
828 settingBulbColorMutex.acquire();
829 tmp = ((double)currentSaturation / 65535.0) * 360.0;
830 } catch (Exception e) {
833 settingBulbColorMutex.release();
839 public double getBrightness() {
842 settingBulbColorMutex.acquire();
843 tmp = ((double)currentBrightness / 65535.0) * 360.0;
844 } catch (Exception e) {
847 settingBulbColorMutex.release();
852 public int getTemperature() {
856 settingBulbTempuraturerMutex.acquire();
857 tmp = currentTemperature;
858 } catch (Exception e) {
861 settingBulbTempuraturerMutex.release();
866 public double getHueRangeLowerBound() {
867 if (!didGetBulbVersion.get()) {
870 return ((double)hueLowerBound / 65535.0) * 360.0;
873 public double getHueRangeUpperBound() {
874 if (!didGetBulbVersion.get()) {
877 return ((double)hueUpperBound / 65535.0) * 360.0;
880 public double getSaturationRangeLowerBound() {
881 if (!didGetBulbVersion.get()) {
884 return ((double)saturationLowerBound / 65535.0) * 100.0;
887 public double getSaturationRangeUpperBound() {
888 if (!didGetBulbVersion.get()) {
891 return ((double)saturationUpperBound / 65535.0) * 100.0;
894 public double getBrightnessRangeLowerBound() {
895 if (!didGetBulbVersion.get()) {
898 return ((double)brightnessLowerBound / 65535.0) * 100.0;
901 public double getBrightnessRangeUpperBound() {
902 if (!didGetBulbVersion.get()) {
905 return ((double)brightnessUpperBound / 65535.0) * 100.0;
908 public int getTemperatureRangeLowerBound() {
909 if (!didGetBulbVersion.get()) {
912 return temperatureLowerBound;
915 public int getTemperatureRangeUpperBound() {
916 if (!didGetBulbVersion.get()) {
919 return temperatureUpperBound;
922 public void setTemperature(int _temperature) {
925 settingBulbTempuraturerMutex.acquire();
926 } catch (Exception e) {
930 BulbColor newColor = new BulbColor(currentHue, currentSaturation, currentBrightness, _temperature);
931 sendSetLightColorPacket(newColor, 250);
933 currentTemperature = _temperature;
934 stateDidChange = true;
936 settingBulbTempuraturerMutex.release();
939 public void setColor(double _hue, double _saturation, double _brightness) {
942 settingBulbColorMutex.acquire();
943 } catch (Exception e) {
949 _saturation /= 100.0;
950 _brightness /= 100.0;
953 int newHue = (int)(_hue * 65535.0);
954 int newSaturation = (int)(_saturation * 65535.0);
955 int newBrightness = (int)(_brightness * 65535.0);
957 BulbColor newColor = new BulbColor(newHue, newSaturation, newBrightness, currentTemperature);
958 sendSetLightColorPacket(newColor, 250);
961 currentSaturation = newSaturation;
962 currentBrightness = newBrightness;
963 stateDidChange = true;
965 settingBulbColorMutex.release();
969 public void turnOff() {
972 bulbStateMutex.acquire();
974 sendSetLightPowerPacket(0, 0);
975 stateDidChange = true;
976 } catch (Exception e) {
980 bulbStateMutex.release();
983 public void turnOn() {
985 bulbStateMutex.acquire();
987 sendSetLightPowerPacket(65535, 0);
988 stateDidChange = true;
990 } catch (Exception e) {
995 bulbStateMutex.release();
998 public boolean getState() {
1000 boolean tmp = false;
1002 bulbStateMutex.acquire();
1004 } catch (Exception e) {
1005 e.printStackTrace();
1008 bulbStateMutex.release();
1014 /*******************************************************************************************************************************************
1016 ** Communication Helpers
1018 *******************************************************************************************************************************************/
1019 private void recievedPacket(byte[] packetData) {
1021 byte[] headerBytes = new byte[36];
1022 for (int i = 0; i < 36; i++) {
1023 headerBytes[i] = packetData[i];
1026 LifxHeader recHeader = new LifxHeader();
1027 recHeader.setFromBytes(headerBytes);
1029 // load the payload bytes (strip away the header)
1030 byte[] payloadBytes = new byte[recHeader.getSize()];
1031 for (int i = 36; i < recHeader.getSize(); i++) {
1032 payloadBytes[i - 36] = packetData[i];
1035 System.out.println("Received: " + Integer.toString(recHeader.getType()));
1037 switch (recHeader.getType()) {
1039 DeviceStateService dat = parseDeviceStateServiceMessage(recHeader, payloadBytes);
1040 // System.out.println("Service: " + Integer.toString(dat.getService()));
1041 // System.out.println("Port : " + Long.toString(dat.getPort()));
1046 handleStateVersionMessageRecieved(recHeader, payloadBytes);
1050 parseDeviceStateInfoMessage(recHeader, payloadBytes);
1055 handleLightStateMessageRecieved(recHeader, payloadBytes);
1059 // System.out.println("unknown packet Type");
1064 private void sendPacket(byte[] packetData) {
1065 // System.out.println("About to send");
1066 sendSocketFlag = true;
1069 socketMutex.acquire();
1070 } catch (InterruptedException e) {
1071 System.out.println("mutex Error");
1075 communicationSockect.sendData(packetData);
1077 } catch (IOException e) {
1078 System.out.println("Socket Send Error");
1081 sendSocketFlag = false;
1082 socketMutex.release();
1087 * Worker function which runs the while loop for receiving data from the bulb.
1090 private void workerFunction() {
1091 LifxHeader h = new LifxHeader();
1094 // Need timeout on receives since we are not sure if a packet will be available
1095 // for processing so don't block waiting
1096 communicationSockect.setSoTimeout(50);
1097 } catch (IOException e) {
1100 // Start the bulb in the off state
1106 // Check if we got the bulb version yet
1107 // could have requested it but message could have gotten lost (UDP)
1108 if (!didGetBulbVersion.get()) {
1109 long currentTime = (new Date().getTime()) / 1000;
1110 if ((currentTime - lastSentGetBulbVersionRequest) > GET_BULB_VERSION_RESEND_WAIT_SECONDS) {
1111 // Get the bulb version so we know what type of bulb this is.
1112 sendGetVersionPacket();
1113 lastSentGetBulbVersionRequest = currentTime;
1117 // Communication resource is busy so try again later
1118 if (sendSocketFlag) {
1123 socketMutex.acquire();
1124 } catch (InterruptedException e) {
1129 dat = communicationSockect.recieveData(1024);
1130 } catch (java.net.SocketTimeoutException e) {
1133 } catch (IOException e) {
1134 // Problem but might be able to recover??
1135 e.printStackTrace();
1139 // Never forget to release!
1140 socketMutex.release();
1144 recievedPacket(dat);
1147 // If a state change occurred then request the bulb state to ensure that the
1148 // bulb did indeed change its state to the correct state
1149 if (stateDidChange) {
1150 sendGetLightStatePacket();
1153 // Wait a bit as to not tie up system resources
1156 } catch (Exception e) {
1165 public void init() {
1167 if (didAlreadyInit.compareAndSet(false, true) == false) {
1168 return; // already init
1172 // Get the bulb address from the IoTSet
1173 Iterator itr = lb_addresses.iterator();
1174 IoTDeviceAddress deviceAddress = (IoTDeviceAddress)itr.next();
1176 System.out.println("Address: " + deviceAddress.getCompleteAddress());
1178 // Create the communication channel
1179 communicationSockect = new IoTUDP(deviceAddress);
1181 } catch (IOException e) {
1182 e.printStackTrace();
1185 // Launch the worker function in a separate thread.
1186 workerThread = new Thread(new Runnable() {
1191 workerThread.start();