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;
13 import iotcode.annotation.*;
14 import iotcode.interfaces.LightBulb;
15 import iotruntime.IoTUDP;
16 import iotruntime.slave.IoTDeviceAddress;
17 import iotruntime.slave.IoTSet;
19 // String to byte conversion
20 import javax.xml.bind.DatatypeConverter;
22 public class LifxLightBulb implements LightBulb {
24 /*******************************************************************************************************************************************
28 *******************************************************************************************************************************************/
29 public static final long GET_BULB_VERSION_RESEND_WAIT_SECONDS = 10;
33 /*******************************************************************************************************************************************
37 *******************************************************************************************************************************************/
38 private IoTUDP communicationSockect;
39 private byte[] bulbMacAddress = new byte[8];
40 static Semaphore socketMutex = new Semaphore(1);
41 static boolean sendSocketFlag = false;
42 private long lastSentGetBulbVersionRequest = 0; // time last request sent
44 // Current Bulb Values
45 private int currentHue = 0;
46 private int currentSaturation = 0;
47 private int currentBrightness = 65535;
48 private int currentTemperature = 9000;
49 private boolean bulbIsOn = false;
53 private AtomicBoolean didAlreadyInit = new AtomicBoolean(false);
55 private AtomicBoolean didGetBulbVersion = new AtomicBoolean(false);
56 static Semaphore settingBulbColorMutex = new Semaphore(1);
57 static Semaphore settingBulbTempuraturerMutex = new Semaphore(1);
58 static Semaphore bulbStateMutex = new Semaphore(1);
60 // color and temperature ranges for the bulbs
61 private int hueLowerBound = 0;
62 private int hueUpperBound = 0;
63 private int saturationLowerBound = 0;
64 private int saturationUpperBound = 0;
65 private int brightnessLowerBound = 0;
66 private int brightnessUpperBound = 0;
67 private int temperatureLowerBound = 2500;
68 private int temperatureUpperBound = 9000;
72 // Check if a state change was requested, used to poll the bulb for if the bulb did
73 // preform the requested state change
74 private boolean stateDidChange = false;
76 /*******************************************************************************************************************************************
80 *******************************************************************************************************************************************/
82 // Main worker thread will do the receive loop
83 Thread workerThread = null;
85 /*******************************************************************************************************************************************
87 ** IoT Sets and Relations
89 *******************************************************************************************************************************************/
91 // IoTSet of Device Addresses.
92 // Will be filled with only 1 address.
93 @config private IoTSet<IoTDeviceAddress> lb_addresses;
96 * Used for testing only
98 public LifxLightBulb(IoTUDP udp, byte[] macAddress) {
99 communicationSockect = udp;
100 bulbMacAddress = macAddress;
103 public LifxLightBulb(String macAddress) {
104 communicationSockect = null;
106 // Set the Mac Address to a default value
107 // Probably not needed for anything
108 /*bulbMacAdd[0] = (byte)0x00;
109 bulbMacAdd[1] = (byte)0x00;
110 bulbMacAdd[2] = (byte)0x00;
111 bulbMacAdd[3] = (byte)0x00;
112 bulbMacAdd[4] = (byte)0x00;
113 bulbMacAdd[5] = (byte)0x00;
114 bulbMacAdd[6] = (byte)0x00;
115 bulbMacAdd[7] = (byte)0x00;*/
117 bulbMacAddress = DatatypeConverter.parseHexBinary(macAddress);
122 /*******************************************************************************************************************************************
126 *******************************************************************************************************************************************/
127 private void sendGetServicePacket() {
128 LifxHeader header = new LifxHeader();
130 header.setTagged(true);
131 header.setMacAddress(bulbMacAddress);
132 header.setSource(0); // randomly picked
133 header.setAck_required(false);
134 header.setRes_required(false);
135 header.setSequence(0);
138 byte[] dataBytes = header.getHeaderBytes();
139 sendPacket(dataBytes);
142 private void sendGetHostInfoPacket() {
143 LifxHeader header = new LifxHeader();
145 header.setTagged(false);
146 header.setMacAddress(bulbMacAddress);
147 header.setSource(10); // randomly picked
148 header.setAck_required(false);
149 header.setRes_required(false);
150 header.setSequence(0);
153 byte[] dataBytes = header.getHeaderBytes();
154 sendPacket(dataBytes);
157 private void sendGetHostFirmwarePacket() {
158 LifxHeader header = new LifxHeader();
160 header.setTagged(false);
161 header.setMacAddress(bulbMacAddress);
162 header.setSource(10); // randomly picked
163 header.setAck_required(false);
164 header.setRes_required(false);
165 header.setSequence(0);
168 byte[] dataBytes = header.getHeaderBytes();
169 sendPacket(dataBytes);
172 private void sendGetWifiInfoPacket() {
173 LifxHeader header = new LifxHeader();
175 header.setTagged(false);
176 header.setMacAddress(bulbMacAddress);
177 header.setSource(10); // randomly picked
178 header.setAck_required(false);
179 header.setRes_required(false);
180 header.setSequence(0);
183 byte[] dataBytes = header.getHeaderBytes();
184 sendPacket(dataBytes);
187 private void sendGetWifiFirmwarePacket() {
188 LifxHeader header = new LifxHeader();
190 header.setTagged(false);
191 header.setMacAddress(bulbMacAddress);
192 header.setSource(10); // randomly picked
193 header.setAck_required(false);
194 header.setRes_required(false);
195 header.setSequence(0);
198 byte[] dataBytes = header.getHeaderBytes();
199 sendPacket(dataBytes);
202 private void sendGetPowerPacket() {
203 LifxHeader header = new LifxHeader();
205 header.setTagged(false);
206 header.setMacAddress(bulbMacAddress);
207 header.setSource(10); // randomly picked
208 header.setAck_required(false);
209 header.setRes_required(false);
210 header.setSequence(0);
213 byte[] dataBytes = header.getHeaderBytes();
214 sendPacket(dataBytes);
217 private void sendSetPowerPacket(int level) {
218 // Currently only 0 and 65535 are supported
219 // This is a fix for now
220 if ((level != 65535) && (level != 0)) {
221 throw new InvalidParameterException("Invalid parameter values");
224 if ((level > 65535) || (level < 0)) {
225 throw new InvalidParameterException("Invalid parameter values");
228 byte[] packetBytes = new byte[38];
230 LifxHeader header = new LifxHeader();
232 header.setTagged(false);
233 header.setMacAddress(bulbMacAddress);
234 header.setSource(10); // randomly picked
235 header.setAck_required(false);
236 header.setRes_required(false);
237 header.setSequence(0);
239 byte[] headerBytes = header.getHeaderBytes();
241 for (int i = 0; i < 36; i++) {
242 packetBytes[i] = headerBytes[i];
245 packetBytes[36] = (byte)(level & 0xFF);
246 packetBytes[37] = (byte)((level >> 8) & 0xFF);
248 sendPacket(packetBytes);
251 private void sendGetLabelPacket() {
252 LifxHeader header = new LifxHeader();
254 header.setTagged(false);
255 header.setMacAddress(bulbMacAddress);
256 header.setSource(10); // randomly picked
257 header.setAck_required(false);
258 header.setRes_required(false);
259 header.setSequence(0);
262 byte[] dataBytes = header.getHeaderBytes();
263 sendPacket(dataBytes);
266 private void sendSetLabelPacket(String label) {
267 // Currently only 0 and 65535 are supported
268 // This is a fix for now
269 if (label.length() != 32) {
270 throw new InvalidParameterException("Invalid parameter values, label must be 32 bytes long");
273 byte[] packetBytes = new byte[68];
275 LifxHeader header = new LifxHeader();
277 header.setTagged(false);
278 header.setMacAddress(bulbMacAddress);
279 header.setSource(10); // randomly picked
280 header.setAck_required(false);
281 header.setRes_required(false);
282 header.setSequence(0);
284 byte[] headerBytes = header.getHeaderBytes();
286 for (int i = 0; i < 36; i++) {
287 packetBytes[i] = headerBytes[i];
290 for (int i = 0; i < 32; i++) {
291 packetBytes[i + 36] = label.getBytes()[i];
294 sendPacket(packetBytes);
297 private void sendGetVersionPacket() {
298 LifxHeader header = new LifxHeader();
300 header.setTagged(false);
301 header.setMacAddress(bulbMacAddress);
302 header.setSource(10); // randomly picked
303 header.setAck_required(false);
304 header.setRes_required(false);
305 header.setSequence(0);
308 byte[] dataBytes = header.getHeaderBytes();
309 sendPacket(dataBytes);
312 private void sendGetInfoPacket() {
313 LifxHeader header = new LifxHeader();
315 header.setTagged(false);
316 header.setMacAddress(bulbMacAddress);
317 header.setSource(10); // randomly picked
318 header.setAck_required(false);
319 header.setRes_required(false);
320 header.setSequence(0);
323 byte[] dataBytes = header.getHeaderBytes();
324 sendPacket(dataBytes);
327 private void sendGetLocationPacket() {
328 LifxHeader header = new LifxHeader();
330 header.setTagged(false);
331 header.setMacAddress(bulbMacAddress);
332 header.setSource(10); // randomly picked
333 header.setAck_required(false);
334 header.setRes_required(false);
335 header.setSequence(0);
338 byte[] dataBytes = header.getHeaderBytes();
339 sendPacket(dataBytes);
342 private void sendGetGroupPacket() {
343 LifxHeader header = new LifxHeader();
345 header.setTagged(false);
346 header.setMacAddress(bulbMacAddress);
347 header.setSource(10); // randomly picked
348 header.setAck_required(false);
349 header.setRes_required(false);
350 header.setSequence(0);
353 byte[] dataBytes = header.getHeaderBytes();
354 sendPacket(dataBytes);
358 /*******************************************************************************************************************************************
362 *******************************************************************************************************************************************/
363 private void sendGetLightStatePacket() {
364 LifxHeader header = new LifxHeader();
366 header.setTagged(false);
367 header.setMacAddress(bulbMacAddress);
368 header.setSource(10); // randomly picked
369 header.setAck_required(false);
370 header.setRes_required(false);
371 header.setSequence(0);
374 byte[] dataBytes = header.getHeaderBytes();
375 sendPacket(dataBytes);
378 private void sendSetLightColorPacket(BulbColor bulbColor, long duration) {
380 if ((duration > 4294967295l) || (duration < 0)) {
381 throw new InvalidParameterException("Invalid parameter value, duration out of range (0 - 4294967295)");
384 byte[] packetBytes = new byte[49];
386 LifxHeader header = new LifxHeader();
388 header.setTagged(false);
389 header.setMacAddress(bulbMacAddress);
390 header.setSource(10); // randomly picked
391 header.setAck_required(false);
392 header.setRes_required(false);
393 header.setSequence(0);
395 byte[] headerBytes = header.getHeaderBytes();
397 for (int i = 0; i < 36; i++) {
398 packetBytes[i] = headerBytes[i];
402 packetBytes[37] = (byte)(bulbColor.getHue() & 0xFF);
403 packetBytes[38] = (byte)((bulbColor.getHue() >> 8) & 0xFF);
405 packetBytes[39] = (byte)(bulbColor.getSaturation() & 0xFF);
406 packetBytes[40] = (byte)((bulbColor.getSaturation() >> 8) & 0xFF);
408 packetBytes[41] = (byte)(bulbColor.getBrightness() & 0xFF);
409 packetBytes[42] = (byte)((bulbColor.getBrightness() >> 8) & 0xFF);
411 packetBytes[43] = (byte)(bulbColor.getKelvin() & 0xFF);
412 packetBytes[44] = (byte)((bulbColor.getKelvin() >> 8) & 0xFF);
414 packetBytes[45] = (byte)((duration >> 0) & 0xFF);
415 packetBytes[46] = (byte)((duration >> 8) & 0xFF);
416 packetBytes[47] = (byte)((duration >> 16) & 0xFF);
417 packetBytes[48] = (byte)((duration >> 24) & 0xFF);
419 sendPacket(packetBytes);
422 private void sendGetLightPowerPacket() {
423 LifxHeader header = new LifxHeader();
425 header.setTagged(false);
426 header.setMacAddress(bulbMacAddress);
427 header.setSource(10); // randomly picked
428 header.setAck_required(false);
429 header.setRes_required(false);
430 header.setSequence(0);
433 byte[] dataBytes = header.getHeaderBytes();
434 sendPacket(dataBytes);
437 private void sendSetLightPowerPacket(int level, long duration) {
439 if ((level > 65535) || (duration > 4294967295l)
440 || (level < 0) || (duration < 0)) {
441 throw new InvalidParameterException("Invalid parameter values");
444 byte[] packetBytes = new byte[42];
447 LifxHeader header = new LifxHeader();
449 header.setTagged(false);
450 header.setMacAddress(bulbMacAddress);
451 header.setSource(10); // randomly picked
452 header.setAck_required(false);
453 header.setRes_required(false);
454 header.setSequence(0);
456 byte[] headerBytes = header.getHeaderBytes();
458 for (int i = 0; i < 36; i++) {
459 packetBytes[i] = headerBytes[i];
462 packetBytes[36] = (byte)(level & 0xFF);
463 packetBytes[37] = (byte)((level >> 8) & 0xFF);
465 packetBytes[38] = (byte)((duration >> 0) & 0xFF);
466 packetBytes[39] = (byte)((duration >> 8) & 0xFF);
467 packetBytes[40] = (byte)((duration >> 16) & 0xFF);
468 packetBytes[41] = (byte)((duration >> 24) & 0xFF);
470 sendPacket(packetBytes);
473 private void sendEchoRequestPacket(byte[] data) {
474 // Currently only 0 and 65535 are supported
475 // This is a fix for now
476 if (data.length != 64) {
477 throw new InvalidParameterException("Invalid parameter values, must have 64 bytes");
480 byte[] packetBytes = new byte[100];
482 LifxHeader header = new LifxHeader();
484 header.setTagged(false);
485 header.setMacAddress(bulbMacAddress);
486 header.setSource(10); // randomly picked
487 header.setAck_required(false);
488 header.setRes_required(false);
489 header.setSequence(0);
491 byte[] headerBytes = header.getHeaderBytes();
493 for (int i = 0; i < 36; i++) {
494 packetBytes[i] = headerBytes[i];
497 for (int i = 0; i < 64; i++) {
498 packetBytes[i + 36] = data[i];
501 sendPacket(packetBytes);
505 /*******************************************************************************************************************************************
509 *******************************************************************************************************************************************/
510 private DeviceStateService parseDeviceStateServiceMessage(LifxHeader header, byte[] payloadData) {
511 int service = payloadData[0];
512 long port = ((payloadData[3] & 0xFF) << 24);
513 port |= ((payloadData[2] & 0xFF) << 16);
514 port |= ((payloadData[1] & 0xFF) << 8);
515 port |= (payloadData[0] & 0xFF);
517 return new DeviceStateService(service, port);
520 private DeviceStateHostInfo parseDeviceStateHostInfoMessage(LifxHeader header, byte[] payloadData) {
521 long signal = ((payloadData[3] & 0xFF) << 24);
522 signal |= ((payloadData[2] & 0xFF) << 16);
523 signal |= ((payloadData[1] & 0xFF) << 8);
524 signal |= (payloadData[0] & 0xFF);
526 long tx = ((payloadData[7] & 0xFF) << 24);
527 tx |= ((payloadData[6] & 0xFF) << 16);
528 tx |= ((payloadData[5] & 0xFF) << 8);
529 tx |= (payloadData[4] & 0xFF);
531 long rx = ((payloadData[11] & 0xFF) << 24);
532 rx |= ((payloadData[10] & 0xFF) << 16);
533 rx |= ((payloadData[9] & 0xFF) << 8);
534 rx |= (payloadData[8] & 0xFF);
536 return new DeviceStateHostInfo(signal, tx, rx);
539 private DeviceStateHostFirmware parseDeviceStateHostFirmwareMessage(LifxHeader header, byte[] payloadData) {
541 for (int i = 0; i < 8; i++) {
542 build += ((long) payloadData[i] & 0xffL) << (8 * i);
547 long version = ((payloadData[19] & 0xFF) << 24);
548 version |= ((payloadData[18] & 0xFF) << 16);
549 version |= ((payloadData[17] & 0xFF) << 8);
550 version |= (payloadData[16] & 0xFF);
552 return new DeviceStateHostFirmware(build, version);
555 private DeviceStateWifiInfo parseDeviceStateWifiInfoMessage(LifxHeader header, byte[] payloadData) {
556 long signal = ((payloadData[3] & 0xFF) << 24);
557 signal |= ((payloadData[2] & 0xFF) << 16);
558 signal |= ((payloadData[1] & 0xFF) << 8);
559 signal |= (payloadData[0] & 0xFF);
561 long tx = ((payloadData[7] & 0xFF) << 24);
562 tx |= ((payloadData[6] & 0xFF) << 16);
563 tx |= ((payloadData[5] & 0xFF) << 8);
564 tx |= (payloadData[4] & 0xFF);
566 long rx = ((payloadData[11] & 0xFF) << 24);
567 rx |= ((payloadData[10] & 0xFF) << 16);
568 rx |= ((payloadData[9] & 0xFF) << 8);
569 rx |= (payloadData[8] & 0xFF);
571 return new DeviceStateWifiInfo(signal, tx, rx);
574 private DeviceStateWifiFirmware parseDeviceStateWifiFirmwareMessage(LifxHeader header, byte[] payloadData) {
576 for (int i = 0; i < 8; i++) {
577 build += ((long) payloadData[i] & 0xffL) << (8 * i);
582 long version = ((payloadData[19] & 0xFF) << 24);
583 version |= ((payloadData[18] & 0xFF) << 16);
584 version |= ((payloadData[17] & 0xFF) << 8);
585 version |= (payloadData[16] & 0xFF);
587 return new DeviceStateWifiFirmware(build, version);
590 private int parseStatePowerMessage(LifxHeader header, byte[] payloadData) {
591 int level = ((payloadData[1] & 0xFF) << 8);
592 level |= (payloadData[0] & 0xFF);
596 private String parseStateLabelMessage(LifxHeader header, byte[] payloadData) {
597 return new String(payloadData);
601 private DeviceStateVersion parseDeviceStateVersionMessage(LifxHeader header, byte[] payloadData) {
602 long vender = ((payloadData[3] & 0xFF) << 24);
603 vender |= ((payloadData[2] & 0xFF) << 16);
604 vender |= ((payloadData[1] & 0xFF) << 8);
605 vender |= (payloadData[0] & 0xFF);
607 long product = ((payloadData[7] & 0xFF) << 24);
608 product |= ((payloadData[6] & 0xFF) << 16);
609 product |= ((payloadData[5] & 0xFF) << 8);
610 product |= (payloadData[4] & 0xFF);
612 long version = ((payloadData[11] & 0xFF) << 24);
613 version |= ((payloadData[10] & 0xFF) << 16);
614 version |= ((payloadData[9] & 0xFF) << 8);
615 version |= (payloadData[8] & 0xFF);
617 return new DeviceStateVersion(vender, product, version);
620 private DeviceStateInfo parseDeviceStateInfoMessage(LifxHeader header, byte[] payloadData) {
624 for (int i = 0; i < 8; i++) {
625 time += ((long) payloadData[i] & 0xffL) << (8 * i);
626 upTime += ((long) payloadData[i + 8] & 0xffL) << (8 * i);
627 downTime += ((long) payloadData[i + 16] & 0xffL) << (8 * i);
630 return new DeviceStateInfo(time, upTime, downTime);
633 private DeviceStateLocation parseDeviceStateLocationMessage(LifxHeader header, byte[] payloadData) {
634 byte[] location = new byte[16];
635 for (int i = 0; i < 16; i++) {
636 location[i] = payloadData[i];
639 byte[] labelBytes = new byte[32];
640 for (int i = 0; i < 32; i++) {
641 labelBytes[i] = payloadData[i + 16];
645 for (int i = 0; i < 8; i++) {
646 updatedAt += ((long) payloadData[48] & 0xffL) << (8 * i);
649 return new DeviceStateLocation(location, new String(labelBytes), updatedAt);
652 private DeviceStateGroup parseDeviceStateGroupMessage(LifxHeader header, byte[] payloadData) {
653 byte[] group = new byte[16];
654 for (int i = 0; i < 16; i++) {
655 group[i] = payloadData[i];
658 byte[] labelBytes = new byte[32];
659 for (int i = 0; i < 32; i++) {
660 labelBytes[i] = payloadData[i + 16];
664 for (int i = 0; i < 8; i++) {
665 updatedAt += ((long) payloadData[48] & 0xffL) << (8 * i);
668 return new DeviceStateGroup(group, new String(labelBytes), updatedAt);
671 private byte[] parseDeviceEchoResponseMessage(LifxHeader header, byte[] payloadData) {
675 /*******************************************************************************************************************************************
679 *******************************************************************************************************************************************/
680 private LightState parseLightStateMessage(LifxHeader header, byte[] payloadData) {
682 byte[] colorData = new byte[8];
683 for (int i = 0; i < 8; i++) {
684 colorData[i] = payloadData[i];
686 BulbColor color = new BulbColor(colorData);
688 int power = ((payloadData[11] & 0xFF) << 8);
689 power |= (payloadData[10] & 0xFF);
691 String label = new String(payloadData);
693 byte[] labelArray = new byte[32];
694 for (int i = 0; i < 32; i++) {
695 labelArray[i] = payloadData[12 + i];
698 return new LightState(color, power, label);
701 private int parseLightStatePowerMessage(LifxHeader header, byte[] payloadData) {
702 int level = ((payloadData[1] & 0xFF) << 8);
703 level |= (payloadData[0] & 0xFF);
708 /*******************************************************************************************************************************************
712 *******************************************************************************************************************************************/
713 private void handleStateVersionMessageRecieved(LifxHeader header, byte[] payloadData) {
715 DeviceStateVersion deviceState = parseDeviceStateVersionMessage(header, payloadData);
716 int productNumber = (int)deviceState.getProduct();
718 boolean isColor = false;
720 if (productNumber == 1) { // Original 1000
722 } else if (productNumber == 3) { //Color 650
724 } else if (productNumber == 10) { // White 800 (Low Voltage)
726 } else if (productNumber == 11) { // White 800 (High Voltage)
728 } else if (productNumber == 18) { // White 900 BR30 (Low Voltage)
730 } else if (productNumber == 20) { // Color 1000 BR30
732 } else if (productNumber == 22) { // Color 1000
738 hueUpperBound = 65535;
739 saturationLowerBound = 0;
740 saturationUpperBound = 65535;
741 brightnessLowerBound = 0;
742 brightnessUpperBound = 65535;
743 temperatureLowerBound = 2500;
744 temperatureUpperBound = 9000;
748 saturationLowerBound = 0;
749 saturationUpperBound = 0;
750 brightnessLowerBound = 0;
751 brightnessUpperBound = 65535; // still can dim bulb
752 temperatureLowerBound = 2500;
753 temperatureUpperBound = 9000;
756 didGetBulbVersion.set(true);
760 private void handleLightStateMessageRecieved(LifxHeader header, byte[] payloadData) {
761 LightState lightState = parseLightStateMessage(header, payloadData);
763 BulbColor color = lightState.getColor();
764 int power = lightState.getPower();
766 boolean bulbWrongColor = false;
767 bulbWrongColor = bulbWrongColor || (color.getHue() != currentHue);
768 bulbWrongColor = bulbWrongColor || (color.getSaturation() != currentSaturation);
769 bulbWrongColor = bulbWrongColor || (color.getBrightness() != currentBrightness);
770 bulbWrongColor = bulbWrongColor || (color.getKelvin() != currentTemperature);
773 // gets set to true if any of the below if statements are taken
774 stateDidChange = false;
776 if (bulbWrongColor) {
777 BulbColor newColor = new BulbColor(currentHue, currentSaturation, currentBrightness, currentTemperature);
778 sendSetLightColorPacket(newColor, 250);
779 // System.out.println("Failed Check 1");
783 bulbStateMutex.acquire();
784 } catch (Exception e) {
787 boolean bulbIsOnTmp = bulbIsOn;
788 bulbStateMutex.release();
790 if ((!bulbIsOnTmp) && (power != 0)) {
792 // System.out.println("Failed Check 2: " + Integer.toString(power));
796 if (bulbIsOnTmp && (power < 65530)) {
798 // System.out.println("Failed Check 3: " + Integer.toString(power));
803 /*******************************************************************************************************************************************
805 ** Light Bulb Interface Methods
807 *******************************************************************************************************************************************/
808 public double getHue() {
811 settingBulbColorMutex.acquire();
812 tmp = ((double)currentHue / 65535.0) * 360.0;
813 } catch (Exception e) {
816 settingBulbColorMutex.release();
822 public double getSaturation() {
825 settingBulbColorMutex.acquire();
826 tmp = ((double)currentSaturation / 65535.0) * 360.0;
827 } catch (Exception e) {
830 settingBulbColorMutex.release();
836 public double getBrightness() {
839 settingBulbColorMutex.acquire();
840 tmp = ((double)currentBrightness / 65535.0) * 360.0;
841 } catch (Exception e) {
844 settingBulbColorMutex.release();
849 public int getTemperature() {
853 settingBulbTempuraturerMutex.acquire();
854 tmp = currentTemperature;
855 } catch (Exception e) {
858 settingBulbTempuraturerMutex.release();
863 public double getHueRangeLowerBound() {
864 if (!didGetBulbVersion.get()) {
867 return ((double)hueLowerBound / 65535.0) * 360.0;
870 public double getHueRangeUpperBound() {
871 if (!didGetBulbVersion.get()) {
874 return ((double)hueUpperBound / 65535.0) * 360.0;
877 public double getSaturationRangeLowerBound() {
878 if (!didGetBulbVersion.get()) {
881 return ((double)saturationLowerBound / 65535.0) * 100.0;
884 public double getSaturationRangeUpperBound() {
885 if (!didGetBulbVersion.get()) {
888 return ((double)saturationUpperBound / 65535.0) * 100.0;
891 public double getBrightnessRangeLowerBound() {
892 if (!didGetBulbVersion.get()) {
895 return ((double)brightnessLowerBound / 65535.0) * 100.0;
898 public double getBrightnessRangeUpperBound() {
899 if (!didGetBulbVersion.get()) {
902 return ((double)brightnessUpperBound / 65535.0) * 100.0;
905 public int getTemperatureRangeLowerBound() {
906 if (!didGetBulbVersion.get()) {
909 return temperatureLowerBound;
912 public int getTemperatureRangeUpperBound() {
913 if (!didGetBulbVersion.get()) {
916 return temperatureUpperBound;
919 public void setTemperature(int _temperature) {
922 settingBulbTempuraturerMutex.acquire();
923 } catch (Exception e) {
927 BulbColor newColor = new BulbColor(currentHue, currentSaturation, currentBrightness, _temperature);
928 sendSetLightColorPacket(newColor, 250);
930 currentTemperature = _temperature;
931 stateDidChange = true;
933 settingBulbTempuraturerMutex.release();
936 public void setColor(double _hue, double _saturation, double _brightness) {
939 settingBulbColorMutex.acquire();
940 } catch (Exception e) {
946 _saturation /= 100.0;
947 _brightness /= 100.0;
950 int newHue = (int)(_hue * 65535.0);
951 int newSaturation = (int)(_saturation * 65535.0);
952 int newBrightness = (int)(_brightness * 65535.0);
954 BulbColor newColor = new BulbColor(newHue, newSaturation, newBrightness, currentTemperature);
955 sendSetLightColorPacket(newColor, 250);
958 currentSaturation = newSaturation;
959 currentBrightness = newBrightness;
960 stateDidChange = true;
962 settingBulbColorMutex.release();
966 public void turnOff() {
969 bulbStateMutex.acquire();
971 sendSetLightPowerPacket(0, 0);
972 stateDidChange = true;
973 } catch (Exception e) {
977 bulbStateMutex.release();
980 public void turnOn() {
982 bulbStateMutex.acquire();
984 sendSetLightPowerPacket(65535, 0);
985 stateDidChange = true;
987 } catch (Exception e) {
992 bulbStateMutex.release();
995 public boolean getState() {
999 bulbStateMutex.acquire();
1001 } catch (Exception e) {
1002 e.printStackTrace();
1005 bulbStateMutex.release();
1011 /*******************************************************************************************************************************************
1013 ** Communication Helpers
1015 *******************************************************************************************************************************************/
1016 private void recievedPacket(byte[] packetData) {
1018 byte[] headerBytes = new byte[36];
1019 for (int i = 0; i < 36; i++) {
1020 headerBytes[i] = packetData[i];
1023 LifxHeader recHeader = new LifxHeader();
1024 recHeader.setFromBytes(headerBytes);
1026 // load the payload bytes (strip away the header)
1027 byte[] payloadBytes = new byte[recHeader.getSize()];
1028 for (int i = 36; i < recHeader.getSize(); i++) {
1029 payloadBytes[i - 36] = packetData[i];
1032 System.out.println("Received: " + Integer.toString(recHeader.getType()));
1034 switch (recHeader.getType()) {
1036 DeviceStateService dat = parseDeviceStateServiceMessage(recHeader, payloadBytes);
1037 // System.out.println("Service: " + Integer.toString(dat.getService()));
1038 // System.out.println("Port : " + Long.toString(dat.getPort()));
1043 handleStateVersionMessageRecieved(recHeader, payloadBytes);
1047 parseDeviceStateInfoMessage(recHeader, payloadBytes);
1052 handleLightStateMessageRecieved(recHeader, payloadBytes);
1056 // System.out.println("unknown packet Type");
1061 private void sendPacket(byte[] packetData) {
1062 // System.out.println("About to send");
1063 sendSocketFlag = true;
1066 socketMutex.acquire();
1067 } catch (InterruptedException e) {
1068 System.out.println("mutex Error");
1072 communicationSockect.sendData(packetData);
1074 } catch (IOException e) {
1075 System.out.println("Socket Send Error");
1078 sendSocketFlag = false;
1079 socketMutex.release();
1084 * Worker function which runs the while loop for receiving data from the bulb.
1087 private void workerFunction() {
1088 LifxHeader h = new LifxHeader();
1091 // Need timeout on receives since we are not sure if a packet will be available
1092 // for processing so don't block waiting
1093 communicationSockect.setSoTimeout(50);
1094 } catch (IOException e) {
1097 // Start the bulb in the off state
1103 // Check if we got the bulb version yet
1104 // could have requested it but message could have gotten lost (UDP)
1105 if (!didGetBulbVersion.get()) {
1106 long currentTime = (new Date().getTime()) / 1000;
1107 if ((currentTime - lastSentGetBulbVersionRequest) > GET_BULB_VERSION_RESEND_WAIT_SECONDS) {
1108 // Get the bulb version so we know what type of bulb this is.
1109 sendGetVersionPacket();
1110 lastSentGetBulbVersionRequest = currentTime;
1114 // Communication resource is busy so try again later
1115 if (sendSocketFlag) {
1120 socketMutex.acquire();
1121 } catch (InterruptedException e) {
1126 dat = communicationSockect.recieveData(1024);
1127 } catch (java.net.SocketTimeoutException e) {
1130 } catch (IOException e) {
1131 // Problem but might be able to recover??
1132 e.printStackTrace();
1136 // Never forget to release!
1137 socketMutex.release();
1141 recievedPacket(dat);
1144 // If a state change occurred then request the bulb state to ensure that the
1145 // bulb did indeed change its state to the correct state
1146 if (stateDidChange) {
1147 sendGetLightStatePacket();
1150 // Wait a bit as to not tie up system resources
1153 } catch (Exception e) {
1162 public void init() {
1164 if (didAlreadyInit.compareAndSet(false, true) == false) {
1165 return; // already init
1169 // Get the bulb address from the IoTSet
1170 Iterator itr = lb_addresses.iterator();
1171 IoTDeviceAddress deviceAddress = (IoTDeviceAddress)itr.next();
1173 System.out.println("Address: " + deviceAddress.getCompleteAddress());
1175 // Create the communication channel
1176 communicationSockect = new IoTUDP(deviceAddress);
1178 } catch (IOException e) {
1179 e.printStackTrace();
1182 // Launch the worker function in a separate thread.
1183 workerThread = new Thread(new Runnable() {
1188 workerThread.start();