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 * TODO: 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;
114 bulbMacAddress = DatatypeConverter.parseHexBinary(macAddress);
119 /*******************************************************************************************************************************************
123 *******************************************************************************************************************************************/
124 private void sendGetServicePacket() {
125 LifxHeader header = new LifxHeader();
127 header.setTagged(true);
128 header.setMacAddress(bulbMacAddress);
129 header.setSource(0); // randomly picked
130 header.setAck_required(false);
131 header.setRes_required(false);
132 header.setSequence(0);
135 byte[] dataBytes = header.getHeaderBytes();
136 sendPacket(dataBytes);
139 private void sendGetHostInfoPacket() {
140 LifxHeader header = new LifxHeader();
142 header.setTagged(false);
143 header.setMacAddress(bulbMacAddress);
144 header.setSource(10); // randomly picked
145 header.setAck_required(false);
146 header.setRes_required(false);
147 header.setSequence(0);
150 byte[] dataBytes = header.getHeaderBytes();
151 sendPacket(dataBytes);
154 private void sendGetHostFirmwarePacket() {
155 LifxHeader header = new LifxHeader();
157 header.setTagged(false);
158 header.setMacAddress(bulbMacAddress);
159 header.setSource(10); // randomly picked
160 header.setAck_required(false);
161 header.setRes_required(false);
162 header.setSequence(0);
165 byte[] dataBytes = header.getHeaderBytes();
166 sendPacket(dataBytes);
169 private void sendGetWifiInfoPacket() {
170 LifxHeader header = new LifxHeader();
172 header.setTagged(false);
173 header.setMacAddress(bulbMacAddress);
174 header.setSource(10); // randomly picked
175 header.setAck_required(false);
176 header.setRes_required(false);
177 header.setSequence(0);
180 byte[] dataBytes = header.getHeaderBytes();
181 sendPacket(dataBytes);
184 private void sendGetWifiFirmwarePacket() {
185 LifxHeader header = new LifxHeader();
187 header.setTagged(false);
188 header.setMacAddress(bulbMacAddress);
189 header.setSource(10); // randomly picked
190 header.setAck_required(false);
191 header.setRes_required(false);
192 header.setSequence(0);
195 byte[] dataBytes = header.getHeaderBytes();
196 sendPacket(dataBytes);
199 private void sendGetPowerPacket() {
200 LifxHeader header = new LifxHeader();
202 header.setTagged(false);
203 header.setMacAddress(bulbMacAddress);
204 header.setSource(10); // randomly picked
205 header.setAck_required(false);
206 header.setRes_required(false);
207 header.setSequence(0);
210 byte[] dataBytes = header.getHeaderBytes();
211 sendPacket(dataBytes);
214 private void sendSetPowerPacket(int level) {
215 // Currently only 0 and 65535 are supported
216 // This is a fix for now
217 if ((level != 65535) && (level != 0)) {
218 throw new InvalidParameterException("Invalid parameter values");
221 if ((level > 65535) || (level < 0)) {
222 throw new InvalidParameterException("Invalid parameter values");
225 byte[] packetBytes = new byte[38];
227 LifxHeader header = new LifxHeader();
229 header.setTagged(false);
230 header.setMacAddress(bulbMacAddress);
231 header.setSource(10); // randomly picked
232 header.setAck_required(false);
233 header.setRes_required(false);
234 header.setSequence(0);
236 byte[] headerBytes = header.getHeaderBytes();
238 for (int i = 0; i < 36; i++) {
239 packetBytes[i] = headerBytes[i];
242 packetBytes[36] = (byte)(level & 0xFF);
243 packetBytes[37] = (byte)((level >> 8) & 0xFF);
245 sendPacket(packetBytes);
248 private void sendGetLabelPacket() {
249 LifxHeader header = new LifxHeader();
251 header.setTagged(false);
252 header.setMacAddress(bulbMacAddress);
253 header.setSource(10); // randomly picked
254 header.setAck_required(false);
255 header.setRes_required(false);
256 header.setSequence(0);
259 byte[] dataBytes = header.getHeaderBytes();
260 sendPacket(dataBytes);
263 private void sendSetLabelPacket(String label) {
264 // Currently only 0 and 65535 are supported
265 // This is a fix for now
266 if (label.length() != 32) {
267 throw new InvalidParameterException("Invalid parameter values, label must be 32 bytes long");
270 byte[] packetBytes = new byte[68];
272 LifxHeader header = new LifxHeader();
274 header.setTagged(false);
275 header.setMacAddress(bulbMacAddress);
276 header.setSource(10); // randomly picked
277 header.setAck_required(false);
278 header.setRes_required(false);
279 header.setSequence(0);
281 byte[] headerBytes = header.getHeaderBytes();
283 for (int i = 0; i < 36; i++) {
284 packetBytes[i] = headerBytes[i];
287 for (int i = 0; i < 32; i++) {
288 packetBytes[i + 36] = label.getBytes()[i];
291 sendPacket(packetBytes);
294 private void sendGetVersionPacket() {
295 LifxHeader header = new LifxHeader();
297 header.setTagged(false);
298 header.setMacAddress(bulbMacAddress);
299 header.setSource(10); // randomly picked
300 header.setAck_required(false);
301 header.setRes_required(false);
302 header.setSequence(0);
305 byte[] dataBytes = header.getHeaderBytes();
306 sendPacket(dataBytes);
309 private void sendGetInfoPacket() {
310 LifxHeader header = new LifxHeader();
312 header.setTagged(false);
313 header.setMacAddress(bulbMacAddress);
314 header.setSource(10); // randomly picked
315 header.setAck_required(false);
316 header.setRes_required(false);
317 header.setSequence(0);
320 byte[] dataBytes = header.getHeaderBytes();
321 sendPacket(dataBytes);
324 private void sendGetLocationPacket() {
325 LifxHeader header = new LifxHeader();
327 header.setTagged(false);
328 header.setMacAddress(bulbMacAddress);
329 header.setSource(10); // randomly picked
330 header.setAck_required(false);
331 header.setRes_required(false);
332 header.setSequence(0);
335 byte[] dataBytes = header.getHeaderBytes();
336 sendPacket(dataBytes);
339 private void sendGetGroupPacket() {
340 LifxHeader header = new LifxHeader();
342 header.setTagged(false);
343 header.setMacAddress(bulbMacAddress);
344 header.setSource(10); // randomly picked
345 header.setAck_required(false);
346 header.setRes_required(false);
347 header.setSequence(0);
350 byte[] dataBytes = header.getHeaderBytes();
351 sendPacket(dataBytes);
355 /*******************************************************************************************************************************************
359 *******************************************************************************************************************************************/
360 private void sendGetLightStatePacket() {
361 LifxHeader header = new LifxHeader();
363 header.setTagged(false);
364 header.setMacAddress(bulbMacAddress);
365 header.setSource(10); // randomly picked
366 header.setAck_required(false);
367 header.setRes_required(false);
368 header.setSequence(0);
371 byte[] dataBytes = header.getHeaderBytes();
372 sendPacket(dataBytes);
375 private void sendSetLightColorPacket(BulbColor bulbColor, long duration) {
377 if ((duration > 4294967295l) || (duration < 0)) {
378 throw new InvalidParameterException("Invalid parameter value, duration out of range (0 - 4294967295)");
381 byte[] packetBytes = new byte[49];
383 LifxHeader header = new LifxHeader();
385 header.setTagged(false);
386 header.setMacAddress(bulbMacAddress);
387 header.setSource(10); // randomly picked
388 header.setAck_required(false);
389 header.setRes_required(false);
390 header.setSequence(0);
392 byte[] headerBytes = header.getHeaderBytes();
394 for (int i = 0; i < 36; i++) {
395 packetBytes[i] = headerBytes[i];
399 packetBytes[37] = (byte)(bulbColor.getHue() & 0xFF);
400 packetBytes[38] = (byte)((bulbColor.getHue() >> 8) & 0xFF);
402 packetBytes[39] = (byte)(bulbColor.getSaturation() & 0xFF);
403 packetBytes[40] = (byte)((bulbColor.getSaturation() >> 8) & 0xFF);
405 packetBytes[41] = (byte)(bulbColor.getBrightness() & 0xFF);
406 packetBytes[42] = (byte)((bulbColor.getBrightness() >> 8) & 0xFF);
408 packetBytes[43] = (byte)(bulbColor.getKelvin() & 0xFF);
409 packetBytes[44] = (byte)((bulbColor.getKelvin() >> 8) & 0xFF);
411 packetBytes[45] = (byte)((duration >> 0) & 0xFF);
412 packetBytes[46] = (byte)((duration >> 8) & 0xFF);
413 packetBytes[47] = (byte)((duration >> 16) & 0xFF);
414 packetBytes[48] = (byte)((duration >> 24) & 0xFF);
416 sendPacket(packetBytes);
419 private void sendGetLightPowerPacket() {
420 LifxHeader header = new LifxHeader();
422 header.setTagged(false);
423 header.setMacAddress(bulbMacAddress);
424 header.setSource(10); // randomly picked
425 header.setAck_required(false);
426 header.setRes_required(false);
427 header.setSequence(0);
430 byte[] dataBytes = header.getHeaderBytes();
431 sendPacket(dataBytes);
434 private void sendSetLightPowerPacket(int level, long duration) {
436 if ((level > 65535) || (duration > 4294967295l)
437 || (level < 0) || (duration < 0)) {
438 throw new InvalidParameterException("Invalid parameter values");
441 byte[] packetBytes = new byte[42];
444 LifxHeader header = new LifxHeader();
446 header.setTagged(false);
447 header.setMacAddress(bulbMacAddress);
448 header.setSource(10); // randomly picked
449 header.setAck_required(false);
450 header.setRes_required(false);
451 header.setSequence(0);
453 byte[] headerBytes = header.getHeaderBytes();
455 for (int i = 0; i < 36; i++) {
456 packetBytes[i] = headerBytes[i];
459 packetBytes[36] = (byte)(level & 0xFF);
460 packetBytes[37] = (byte)((level >> 8) & 0xFF);
462 packetBytes[38] = (byte)((duration >> 0) & 0xFF);
463 packetBytes[39] = (byte)((duration >> 8) & 0xFF);
464 packetBytes[40] = (byte)((duration >> 16) & 0xFF);
465 packetBytes[41] = (byte)((duration >> 24) & 0xFF);
467 System.out.println(Arrays.toString(packetBytes));
469 sendPacket(packetBytes);
472 private void sendEchoRequestPacket(byte[] data) {
473 // Currently only 0 and 65535 are supported
474 // This is a fix for now
475 if (data.length != 64) {
476 throw new InvalidParameterException("Invalid parameter values, must have 64 bytes");
479 byte[] packetBytes = new byte[100];
481 LifxHeader header = new LifxHeader();
483 header.setTagged(false);
484 header.setMacAddress(bulbMacAddress);
485 header.setSource(10); // randomly picked
486 header.setAck_required(false);
487 header.setRes_required(false);
488 header.setSequence(0);
490 byte[] headerBytes = header.getHeaderBytes();
492 for (int i = 0; i < 36; i++) {
493 packetBytes[i] = headerBytes[i];
496 for (int i = 0; i < 64; i++) {
497 packetBytes[i + 36] = data[i];
500 sendPacket(packetBytes);
504 /*******************************************************************************************************************************************
508 *******************************************************************************************************************************************/
509 private DeviceStateService parseDeviceStateServiceMessage(LifxHeader header, byte[] payloadData) {
510 int service = payloadData[0];
511 long port = ((payloadData[3] & 0xFF) << 24);
512 port |= ((payloadData[2] & 0xFF) << 16);
513 port |= ((payloadData[1] & 0xFF) << 8);
514 port |= (payloadData[0] & 0xFF);
516 return new DeviceStateService(service, port);
519 private DeviceStateHostInfo parseDeviceStateHostInfoMessage(LifxHeader header, byte[] payloadData) {
520 long signal = ((payloadData[3] & 0xFF) << 24);
521 signal |= ((payloadData[2] & 0xFF) << 16);
522 signal |= ((payloadData[1] & 0xFF) << 8);
523 signal |= (payloadData[0] & 0xFF);
525 long tx = ((payloadData[7] & 0xFF) << 24);
526 tx |= ((payloadData[6] & 0xFF) << 16);
527 tx |= ((payloadData[5] & 0xFF) << 8);
528 tx |= (payloadData[4] & 0xFF);
530 long rx = ((payloadData[11] & 0xFF) << 24);
531 rx |= ((payloadData[10] & 0xFF) << 16);
532 rx |= ((payloadData[9] & 0xFF) << 8);
533 rx |= (payloadData[8] & 0xFF);
535 return new DeviceStateHostInfo(signal, tx, rx);
538 private DeviceStateHostFirmware parseDeviceStateHostFirmwareMessage(LifxHeader header, byte[] payloadData) {
540 for (int i = 0; i < 8; i++) {
541 build += ((long) payloadData[i] & 0xffL) << (8 * i);
546 long version = ((payloadData[19] & 0xFF) << 24);
547 version |= ((payloadData[18] & 0xFF) << 16);
548 version |= ((payloadData[17] & 0xFF) << 8);
549 version |= (payloadData[16] & 0xFF);
551 return new DeviceStateHostFirmware(build, version);
554 private DeviceStateWifiInfo parseDeviceStateWifiInfoMessage(LifxHeader header, byte[] payloadData) {
555 long signal = ((payloadData[3] & 0xFF) << 24);
556 signal |= ((payloadData[2] & 0xFF) << 16);
557 signal |= ((payloadData[1] & 0xFF) << 8);
558 signal |= (payloadData[0] & 0xFF);
560 long tx = ((payloadData[7] & 0xFF) << 24);
561 tx |= ((payloadData[6] & 0xFF) << 16);
562 tx |= ((payloadData[5] & 0xFF) << 8);
563 tx |= (payloadData[4] & 0xFF);
565 long rx = ((payloadData[11] & 0xFF) << 24);
566 rx |= ((payloadData[10] & 0xFF) << 16);
567 rx |= ((payloadData[9] & 0xFF) << 8);
568 rx |= (payloadData[8] & 0xFF);
570 return new DeviceStateWifiInfo(signal, tx, rx);
573 private DeviceStateWifiFirmware parseDeviceStateWifiFirmwareMessage(LifxHeader header, byte[] payloadData) {
575 for (int i = 0; i < 8; i++) {
576 build += ((long) payloadData[i] & 0xffL) << (8 * i);
581 long version = ((payloadData[19] & 0xFF) << 24);
582 version |= ((payloadData[18] & 0xFF) << 16);
583 version |= ((payloadData[17] & 0xFF) << 8);
584 version |= (payloadData[16] & 0xFF);
586 return new DeviceStateWifiFirmware(build, version);
589 private int parseStatePowerMessage(LifxHeader header, byte[] payloadData) {
590 int level = ((payloadData[1] & 0xFF) << 8);
591 level |= (payloadData[0] & 0xFF);
595 private String parseStateLabelMessage(LifxHeader header, byte[] payloadData) {
596 return new String(payloadData);
600 private DeviceStateVersion parseDeviceStateVersionMessage(LifxHeader header, byte[] payloadData) {
601 long vender = ((payloadData[3] & 0xFF) << 24);
602 vender |= ((payloadData[2] & 0xFF) << 16);
603 vender |= ((payloadData[1] & 0xFF) << 8);
604 vender |= (payloadData[0] & 0xFF);
606 long product = ((payloadData[7] & 0xFF) << 24);
607 product |= ((payloadData[6] & 0xFF) << 16);
608 product |= ((payloadData[5] & 0xFF) << 8);
609 product |= (payloadData[4] & 0xFF);
611 long version = ((payloadData[11] & 0xFF) << 24);
612 version |= ((payloadData[10] & 0xFF) << 16);
613 version |= ((payloadData[9] & 0xFF) << 8);
614 version |= (payloadData[8] & 0xFF);
616 return new DeviceStateVersion(vender, product, version);
619 private DeviceStateInfo parseDeviceStateInfoMessage(LifxHeader header, byte[] payloadData) {
623 for (int i = 0; i < 8; i++) {
624 time += ((long) payloadData[i] & 0xffL) << (8 * i);
625 upTime += ((long) payloadData[i + 8] & 0xffL) << (8 * i);
626 downTime += ((long) payloadData[i + 16] & 0xffL) << (8 * i);
629 return new DeviceStateInfo(time, upTime, downTime);
632 private DeviceStateLocation parseDeviceStateLocationMessage(LifxHeader header, byte[] payloadData) {
633 byte[] location = new byte[16];
634 for (int i = 0; i < 16; i++) {
635 location[i] = payloadData[i];
638 byte[] labelBytes = new byte[32];
639 for (int i = 0; i < 32; i++) {
640 labelBytes[i] = payloadData[i + 16];
644 for (int i = 0; i < 8; i++) {
645 updatedAt += ((long) payloadData[48] & 0xffL) << (8 * i);
648 return new DeviceStateLocation(location, new String(labelBytes), updatedAt);
651 private DeviceStateGroup parseDeviceStateGroupMessage(LifxHeader header, byte[] payloadData) {
652 byte[] group = new byte[16];
653 for (int i = 0; i < 16; i++) {
654 group[i] = payloadData[i];
657 byte[] labelBytes = new byte[32];
658 for (int i = 0; i < 32; i++) {
659 labelBytes[i] = payloadData[i + 16];
663 for (int i = 0; i < 8; i++) {
664 updatedAt += ((long) payloadData[48] & 0xffL) << (8 * i);
667 return new DeviceStateGroup(group, new String(labelBytes), updatedAt);
670 private byte[] parseDeviceEchoResponseMessage(LifxHeader header, byte[] payloadData) {
674 /*******************************************************************************************************************************************
678 *******************************************************************************************************************************************/
679 private LightState parseLightStateMessage(LifxHeader header, byte[] payloadData) {
681 byte[] colorData = new byte[8];
682 for (int i = 0; i < 8; i++) {
683 colorData[i] = payloadData[i];
685 BulbColor color = new BulbColor(colorData);
687 int power = ((payloadData[11] & 0xFF) << 8);
688 power |= (payloadData[10] & 0xFF);
690 String label = new String(payloadData);
692 byte[] labelArray = new byte[32];
693 for (int i = 0; i < 32; i++) {
694 labelArray[i] = payloadData[12 + i];
697 return new LightState(color, power, label);
700 private int parseLightStatePowerMessage(LifxHeader header, byte[] payloadData) {
701 int level = ((payloadData[1] & 0xFF) << 8);
702 level |= (payloadData[0] & 0xFF);
707 /*******************************************************************************************************************************************
711 *******************************************************************************************************************************************/
712 private void handleStateVersionMessageRecieved(LifxHeader header, byte[] payloadData) {
714 DeviceStateVersion deviceState = parseDeviceStateVersionMessage(header, payloadData);
715 int productNumber = (int)deviceState.getProduct();
717 boolean isColor = false;
719 if (productNumber == 1) {// Original 1000
721 } else if (productNumber == 3) {//Color 650
723 } else if (productNumber == 10) {// White 800 (Low Voltage)
725 } else if (productNumber == 11) {// White 800 (High Voltage)
727 } else if (productNumber == 18) {// White 900 BR30 (Low Voltage)
729 } else if (productNumber == 20) {// Color 1000 BR30
731 } else if (productNumber == 22) {// Color 1000
737 hueUpperBound = 65535;
738 saturationLowerBound = 0;
739 saturationUpperBound = 65535;
740 brightnessLowerBound = 0;
741 brightnessUpperBound = 65535;
742 temperatureLowerBound = 2500;
743 temperatureUpperBound = 9000;
747 saturationLowerBound = 0;
748 saturationUpperBound = 0;
749 brightnessLowerBound = 0;
750 brightnessUpperBound = 65535;// still can dim bulb
751 temperatureLowerBound = 2500;
752 temperatureUpperBound = 9000;
755 didGetBulbVersion.set(true);
759 private void handleLightStateMessageRecieved(LifxHeader header, byte[] payloadData) {
760 LightState lightState = parseLightStateMessage(header, payloadData);
762 BulbColor color = lightState.getColor();
763 int power = lightState.getPower();
765 boolean bulbWrongColor = false;
766 bulbWrongColor = bulbWrongColor || (color.getHue() != currentHue);
767 bulbWrongColor = bulbWrongColor || (color.getSaturation() != currentSaturation);
768 bulbWrongColor = bulbWrongColor || (color.getBrightness() != currentBrightness);
769 bulbWrongColor = bulbWrongColor || (color.getKelvin() != currentTemperature);
772 // gets set to true if any of the below if statements are taken
773 stateDidChange = false;
775 if (bulbWrongColor) {
776 BulbColor newColor = new BulbColor(currentHue, currentSaturation, currentBrightness, currentTemperature);
777 sendSetLightColorPacket(newColor, 250);
778 // System.out.println("Failed Check 1");
782 bulbStateMutex.acquire();
783 } catch (Exception e) {
786 boolean bulbIsOnTmp = bulbIsOn;
787 bulbStateMutex.release();
789 if ((!bulbIsOnTmp) && (power != 0)) {
791 // System.out.println("Failed Check 2: " + Integer.toString(power));
795 if (bulbIsOnTmp && (power < 65530)) {
797 // System.out.println("Failed Check 3: " + Integer.toString(power));
802 /*******************************************************************************************************************************************
804 ** Light Bulb Interface Methods
806 *******************************************************************************************************************************************/
807 public double getHue() {
810 settingBulbColorMutex.acquire();
811 tmp = ((double)currentHue / 65535.0) * 360.0;
812 } catch (Exception e) {
815 settingBulbColorMutex.release();
821 public double getSaturation() {
824 settingBulbColorMutex.acquire();
825 tmp = ((double)currentSaturation / 65535.0) * 360.0;
826 } catch (Exception e) {
829 settingBulbColorMutex.release();
835 public double getBrightness() {
838 settingBulbColorMutex.acquire();
839 tmp = ((double)currentBrightness / 65535.0) * 360.0;
840 } catch (Exception e) {
843 settingBulbColorMutex.release();
848 public int getTemperature() {
852 settingBulbTempuraturerMutex.acquire();
853 tmp = currentTemperature;
854 } catch (Exception e) {
857 settingBulbTempuraturerMutex.release();
862 public double getHueRangeLowerBound() {
863 if (!didGetBulbVersion.get()) {
866 return ((double)hueLowerBound / 65535.0) * 360.0;
869 public double getHueRangeUpperBound() {
870 if (!didGetBulbVersion.get()) {
873 return ((double)hueUpperBound / 65535.0) * 360.0;
876 public double getSaturationRangeLowerBound() {
877 if (!didGetBulbVersion.get()) {
880 return ((double)saturationLowerBound / 65535.0) * 100.0;
883 public double getSaturationRangeUpperBound() {
884 if (!didGetBulbVersion.get()) {
887 return ((double)saturationUpperBound / 65535.0) * 100.0;
890 public double getBrightnessRangeLowerBound() {
891 if (!didGetBulbVersion.get()) {
894 return ((double)brightnessLowerBound / 65535.0) * 100.0;
897 public double getBrightnessRangeUpperBound() {
898 if (!didGetBulbVersion.get()) {
901 return ((double)brightnessUpperBound / 65535.0) * 100.0;
904 public int getTemperatureRangeLowerBound() {
905 if (!didGetBulbVersion.get()) {
908 return temperatureLowerBound;
911 public int getTemperatureRangeUpperBound() {
912 if (!didGetBulbVersion.get()) {
915 return temperatureUpperBound;
918 public void setTemperature(int _temperature) {
921 settingBulbTempuraturerMutex.acquire();
922 } catch (Exception e) {
926 BulbColor newColor = new BulbColor(currentHue, currentSaturation, currentBrightness, _temperature);
927 sendSetLightColorPacket(newColor, 250);
929 currentTemperature = _temperature;
930 stateDidChange = true;
932 settingBulbTempuraturerMutex.release();
935 public void setColor(double _hue, double _saturation, double _brightness) {
938 settingBulbColorMutex.acquire();
939 } catch (Exception e) {
945 _saturation /= 100.0;
946 _brightness /= 100.0;
949 int newHue = (int)(_hue * 65535.0);
950 int newSaturation = (int)(_saturation * 65535.0);
951 int newBrightness = (int)(_brightness * 65535.0);
953 BulbColor newColor = new BulbColor(newHue, newSaturation, newBrightness, currentTemperature);
954 sendSetLightColorPacket(newColor, 250);
957 currentSaturation = newSaturation;
958 currentBrightness = newBrightness;
959 stateDidChange = true;
961 settingBulbColorMutex.release();
965 public void turnOff() {
968 bulbStateMutex.acquire();
970 sendSetLightPowerPacket(0, 0);
971 stateDidChange = true;
972 } catch (Exception e) {
976 bulbStateMutex.release();
979 public void turnOn() {
981 bulbStateMutex.acquire();
983 sendSetLightPowerPacket(65535, 0);
984 stateDidChange = true;
986 } catch (Exception e) {
991 bulbStateMutex.release();
994 public boolean getState() {
998 bulbStateMutex.acquire();
1000 } catch (Exception e) {
1001 e.printStackTrace();
1004 bulbStateMutex.release();
1010 /*******************************************************************************************************************************************
1012 ** Communication Helpers
1014 *******************************************************************************************************************************************/
1015 private void recievedPacket(byte[] packetData) {
1017 byte[] headerBytes = new byte[36];
1018 for (int i = 0; i < 36; i++) {
1019 headerBytes[i] = packetData[i];
1022 LifxHeader recHeader = new LifxHeader();
1023 recHeader.setFromBytes(headerBytes);
1025 // load the payload bytes (strip away the header)
1026 byte[] payloadBytes = new byte[recHeader.getSize()];
1027 for (int i = 36; i < recHeader.getSize(); i++) {
1028 payloadBytes[i - 36] = packetData[i];
1031 System.out.println("Received: " + Integer.toString(recHeader.getType()));
1033 switch (recHeader.getType()) {
1035 DeviceStateService dat = parseDeviceStateServiceMessage(recHeader, payloadBytes);
1036 // System.out.println("Service: " + Integer.toString(dat.getService()));
1037 // System.out.println("Port : " + Long.toString(dat.getPort()));
1042 handleStateVersionMessageRecieved(recHeader, payloadBytes);
1046 parseDeviceStateInfoMessage(recHeader, payloadBytes);
1051 handleLightStateMessageRecieved(recHeader, payloadBytes);
1055 // System.out.println("unknown packet Type");
1060 private void sendPacket(byte[] packetData) {
1061 // System.out.println("About to send");
1062 sendSocketFlag = true;
1065 socketMutex.acquire();
1066 } catch (InterruptedException e) {
1067 System.out.println("mutex Error");
1071 communicationSockect.sendData(packetData);
1073 } catch (IOException e) {
1074 System.out.println("Socket Send Error");
1077 sendSocketFlag = false;
1078 socketMutex.release();
1083 * Worker function which runs the while loop for receiving data from the bulb.
1086 private void workerFunction() {
1087 LifxHeader h = new LifxHeader();
1090 // Need timeout on receives since we are not sure if a packet will be available
1091 // for processing so don't block waiting
1092 communicationSockect.setSoTimeout(50);
1093 } catch (IOException e) {
1096 // Start the bulb in the off state
1101 // Check if we got the bulb version yet
1102 // could have requested it but message could have gotten lost (UDP)
1103 if (!didGetBulbVersion.get()) {
1104 long currentTime = (new Date().getTime()) / 1000;
1105 if ((currentTime - lastSentGetBulbVersionRequest) > GET_BULB_VERSION_RESEND_WAIT_SECONDS) {
1106 // Get the bulb version so we know what type of bulb this is.
1107 sendGetVersionPacket();
1108 lastSentGetBulbVersionRequest = currentTime;
1112 // Communication resource is busy so try again later
1113 if (sendSocketFlag) {
1118 socketMutex.acquire();
1119 } catch (InterruptedException e) {
1124 dat = communicationSockect.recieveData(1024);
1125 } catch (java.net.SocketTimeoutException e) {
1128 } catch (IOException e) {
1129 // Problem but might be able to recover??
1130 e.printStackTrace();
1134 // Never forget to release!
1135 socketMutex.release();
1139 recievedPacket(dat);
1142 // If a state change occurred then request the bulb state to ensure that the
1143 // bulb did indeed change its state to the correct state
1144 if (stateDidChange) {
1145 sendGetLightStatePacket();
1148 // Wait a bit as to not tie up system resources
1151 } catch (Exception e) {
1160 public void init() {
1162 if (didAlreadyInit.compareAndSet(false, true) == false) {
1163 return; // already init
1167 // Get the bulb address from the IoTSet
1168 Iterator itr = lb_addresses.iterator();
1169 IoTDeviceAddress deviceAddress = (IoTDeviceAddress)itr.next();
1171 System.out.println("Address: " + deviceAddress.getCompleteAddress());
1173 // Create the communication channel
1174 communicationSockect = new IoTUDP(deviceAddress);
1176 } catch (IOException e) {
1177 e.printStackTrace();
1180 // Launch the worker function in a separate thread.
1181 workerThread = new Thread(new Runnable() {
1186 workerThread.start();
1191 /* TODO: Uncomment this part to do light bulb test
1192 public static void main(String[] args) throws Exception {
1194 System.out.println("Executing main function!");
1195 IoTDeviceAddress iotDevAdd = new IoTDeviceAddress(args[0], 12345, 56700, false, false);
1196 Set<IoTDeviceAddress> set = new HashSet<IoTDeviceAddress>();
1198 IoTSet<IoTDeviceAddress> iotset = new IoTSet<IoTDeviceAddress>(set);
1199 LifxLightBulb lb = new LifxLightBulb(iotset, "D073D5128E300000");
1203 for (int i = 0; i < 2; i++) {
1205 System.out.println("Turning off!");
1208 System.out.println("Turning on!");
1212 } catch(Exception ex) {
1213 ex.printStackTrace();