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.*;
16 // String to byte conversion
17 import javax.xml.bind.DatatypeConverter;
20 public class LifxLightBulb implements LightBulb {
22 /*******************************************************************************************************************************************
26 *******************************************************************************************************************************************/
27 public static final long GET_BULB_VERSION_RESEND_WAIT_SECONDS = 10;
31 /*******************************************************************************************************************************************
35 *******************************************************************************************************************************************/
36 private IoTUDP communicationSockect;
37 private byte[] bulbMacAddress = new byte[8];
38 static Semaphore socketMutex = new Semaphore(1);
39 static boolean sendSocketFlag = false;
40 private long lastSentGetBulbVersionRequest = 0; // time last request sent
42 // Current Bulb Values
43 private int currentHue = 0;
44 private int currentSaturation = 0;
45 private int currentBrightness = 65535;
46 private int currentTemperature = 9000;
47 private boolean bulbIsOn = false;
51 private AtomicBoolean didAlreadyInit = new AtomicBoolean(false);
53 private AtomicBoolean didGetBulbVersion = new AtomicBoolean(false);
54 static Semaphore settingBulbColorMutex = new Semaphore(1);
55 static Semaphore settingBulbTempuraturerMutex = new Semaphore(1);
56 static Semaphore bulbStateMutex = new Semaphore(1);
58 // color and temperature ranges for the bulbs
59 private int hueLowerBound = 0;
60 private int hueUpperBound = 0;
61 private int saturationLowerBound = 0;
62 private int saturationUpperBound = 0;
63 private int brightnessLowerBound = 0;
64 private int brightnessUpperBound = 0;
65 private int temperatureLowerBound = 2500;
66 private int temperatureUpperBound = 9000;
70 // Check if a state change was requested, used to poll the bulb for if the bulb did
71 // preform the requested state change
72 private boolean stateDidChange = false;
74 /*******************************************************************************************************************************************
78 *******************************************************************************************************************************************/
80 // Main worker thread will do the receive loop
81 Thread workerThread = null;
83 /*******************************************************************************************************************************************
85 ** IoT Sets and Relations
87 *******************************************************************************************************************************************/
89 // IoTSet of Device Addresses.
90 // Will be filled with only 1 address.
91 private IoTSet<IoTDeviceAddress> lb_addresses;
94 * Used for testing only
96 public LifxLightBulb(IoTUDP udp, byte[] macAddress) {
97 communicationSockect = udp;
98 bulbMacAddress = macAddress;
101 public LifxLightBulb(String macAddress) {
102 communicationSockect = null;
104 // Set the Mac Address to a default value
105 // Probably not needed for anything
106 /*bulbMacAdd[0] = (byte)0x00;
107 bulbMacAdd[1] = (byte)0x00;
108 bulbMacAdd[2] = (byte)0x00;
109 bulbMacAdd[3] = (byte)0x00;
110 bulbMacAdd[4] = (byte)0x00;
111 bulbMacAdd[5] = (byte)0x00;
112 bulbMacAdd[6] = (byte)0x00;
113 bulbMacAdd[7] = (byte)0x00;*/
115 bulbMacAddress = DatatypeConverter.parseHexBinary(macAddress);
120 /*******************************************************************************************************************************************
124 *******************************************************************************************************************************************/
125 private void sendGetServicePacket() {
126 LifxHeader header = new LifxHeader();
128 header.setTagged(true);
129 header.setMacAddress(bulbMacAddress);
130 header.setSource(0); // randomly picked
131 header.setAck_required(false);
132 header.setRes_required(false);
133 header.setSequence(0);
136 byte[] dataBytes = header.getHeaderBytes();
137 sendPacket(dataBytes);
140 private void sendGetHostInfoPacket() {
141 LifxHeader header = new LifxHeader();
143 header.setTagged(false);
144 header.setMacAddress(bulbMacAddress);
145 header.setSource(10); // randomly picked
146 header.setAck_required(false);
147 header.setRes_required(false);
148 header.setSequence(0);
151 byte[] dataBytes = header.getHeaderBytes();
152 sendPacket(dataBytes);
155 private void sendGetHostFirmwarePacket() {
156 LifxHeader header = new LifxHeader();
158 header.setTagged(false);
159 header.setMacAddress(bulbMacAddress);
160 header.setSource(10); // randomly picked
161 header.setAck_required(false);
162 header.setRes_required(false);
163 header.setSequence(0);
166 byte[] dataBytes = header.getHeaderBytes();
167 sendPacket(dataBytes);
170 private void sendGetWifiInfoPacket() {
171 LifxHeader header = new LifxHeader();
173 header.setTagged(false);
174 header.setMacAddress(bulbMacAddress);
175 header.setSource(10); // randomly picked
176 header.setAck_required(false);
177 header.setRes_required(false);
178 header.setSequence(0);
181 byte[] dataBytes = header.getHeaderBytes();
182 sendPacket(dataBytes);
185 private void sendGetWifiFirmwarePacket() {
186 LifxHeader header = new LifxHeader();
188 header.setTagged(false);
189 header.setMacAddress(bulbMacAddress);
190 header.setSource(10); // randomly picked
191 header.setAck_required(false);
192 header.setRes_required(false);
193 header.setSequence(0);
196 byte[] dataBytes = header.getHeaderBytes();
197 sendPacket(dataBytes);
200 private void sendGetPowerPacket() {
201 LifxHeader header = new LifxHeader();
203 header.setTagged(false);
204 header.setMacAddress(bulbMacAddress);
205 header.setSource(10); // randomly picked
206 header.setAck_required(false);
207 header.setRes_required(false);
208 header.setSequence(0);
211 byte[] dataBytes = header.getHeaderBytes();
212 sendPacket(dataBytes);
215 private void sendSetPowerPacket(int level) {
216 // Currently only 0 and 65535 are supported
217 // This is a fix for now
218 if ((level != 65535) && (level != 0)) {
219 throw new InvalidParameterException("Invalid parameter values");
222 if ((level > 65535) || (level < 0)) {
223 throw new InvalidParameterException("Invalid parameter values");
226 byte[] packetBytes = new byte[38];
228 LifxHeader header = new LifxHeader();
230 header.setTagged(false);
231 header.setMacAddress(bulbMacAddress);
232 header.setSource(10); // randomly picked
233 header.setAck_required(false);
234 header.setRes_required(false);
235 header.setSequence(0);
237 byte[] headerBytes = header.getHeaderBytes();
239 for (int i = 0; i < 36; i++) {
240 packetBytes[i] = headerBytes[i];
243 packetBytes[36] = (byte)(level & 0xFF);
244 packetBytes[37] = (byte)((level >> 8) & 0xFF);
246 sendPacket(packetBytes);
249 private void sendGetLabelPacket() {
250 LifxHeader header = new LifxHeader();
252 header.setTagged(false);
253 header.setMacAddress(bulbMacAddress);
254 header.setSource(10); // randomly picked
255 header.setAck_required(false);
256 header.setRes_required(false);
257 header.setSequence(0);
260 byte[] dataBytes = header.getHeaderBytes();
261 sendPacket(dataBytes);
264 private void sendSetLabelPacket(String label) {
265 // Currently only 0 and 65535 are supported
266 // This is a fix for now
267 if (label.length() != 32) {
268 throw new InvalidParameterException("Invalid parameter values, label must be 32 bytes long");
271 byte[] packetBytes = new byte[68];
273 LifxHeader header = new LifxHeader();
275 header.setTagged(false);
276 header.setMacAddress(bulbMacAddress);
277 header.setSource(10); // randomly picked
278 header.setAck_required(false);
279 header.setRes_required(false);
280 header.setSequence(0);
282 byte[] headerBytes = header.getHeaderBytes();
284 for (int i = 0; i < 36; i++) {
285 packetBytes[i] = headerBytes[i];
288 for (int i = 0; i < 32; i++) {
289 packetBytes[i + 36] = label.getBytes()[i];
292 sendPacket(packetBytes);
295 private void sendGetVersionPacket() {
296 LifxHeader header = new LifxHeader();
298 header.setTagged(false);
299 header.setMacAddress(bulbMacAddress);
300 header.setSource(10); // randomly picked
301 header.setAck_required(false);
302 header.setRes_required(false);
303 header.setSequence(0);
306 byte[] dataBytes = header.getHeaderBytes();
307 sendPacket(dataBytes);
310 private void sendGetInfoPacket() {
311 LifxHeader header = new LifxHeader();
313 header.setTagged(false);
314 header.setMacAddress(bulbMacAddress);
315 header.setSource(10); // randomly picked
316 header.setAck_required(false);
317 header.setRes_required(false);
318 header.setSequence(0);
321 byte[] dataBytes = header.getHeaderBytes();
322 sendPacket(dataBytes);
325 private void sendGetLocationPacket() {
326 LifxHeader header = new LifxHeader();
328 header.setTagged(false);
329 header.setMacAddress(bulbMacAddress);
330 header.setSource(10); // randomly picked
331 header.setAck_required(false);
332 header.setRes_required(false);
333 header.setSequence(0);
336 byte[] dataBytes = header.getHeaderBytes();
337 sendPacket(dataBytes);
340 private void sendGetGroupPacket() {
341 LifxHeader header = new LifxHeader();
343 header.setTagged(false);
344 header.setMacAddress(bulbMacAddress);
345 header.setSource(10); // randomly picked
346 header.setAck_required(false);
347 header.setRes_required(false);
348 header.setSequence(0);
351 byte[] dataBytes = header.getHeaderBytes();
352 sendPacket(dataBytes);
356 /*******************************************************************************************************************************************
360 *******************************************************************************************************************************************/
361 private void sendGetLightStatePacket() {
362 LifxHeader header = new LifxHeader();
364 header.setTagged(false);
365 header.setMacAddress(bulbMacAddress);
366 header.setSource(10); // randomly picked
367 header.setAck_required(false);
368 header.setRes_required(false);
369 header.setSequence(0);
372 byte[] dataBytes = header.getHeaderBytes();
373 sendPacket(dataBytes);
376 private void sendSetLightColorPacket(BulbColor bulbColor, long duration) {
378 if ((duration > 4294967295l) || (duration < 0)) {
379 throw new InvalidParameterException("Invalid parameter value, duration out of range (0 - 4294967295)");
382 byte[] packetBytes = new byte[49];
384 LifxHeader header = new LifxHeader();
386 header.setTagged(false);
387 header.setMacAddress(bulbMacAddress);
388 header.setSource(10); // randomly picked
389 header.setAck_required(false);
390 header.setRes_required(false);
391 header.setSequence(0);
393 byte[] headerBytes = header.getHeaderBytes();
395 for (int i = 0; i < 36; i++) {
396 packetBytes[i] = headerBytes[i];
400 packetBytes[37] = (byte)(bulbColor.getHue() & 0xFF);
401 packetBytes[38] = (byte)((bulbColor.getHue() >> 8) & 0xFF);
403 packetBytes[39] = (byte)(bulbColor.getSaturation() & 0xFF);
404 packetBytes[40] = (byte)((bulbColor.getSaturation() >> 8) & 0xFF);
406 packetBytes[41] = (byte)(bulbColor.getBrightness() & 0xFF);
407 packetBytes[42] = (byte)((bulbColor.getBrightness() >> 8) & 0xFF);
409 packetBytes[43] = (byte)(bulbColor.getKelvin() & 0xFF);
410 packetBytes[44] = (byte)((bulbColor.getKelvin() >> 8) & 0xFF);
412 packetBytes[45] = (byte)((duration >> 0) & 0xFF);
413 packetBytes[46] = (byte)((duration >> 8) & 0xFF);
414 packetBytes[47] = (byte)((duration >> 16) & 0xFF);
415 packetBytes[48] = (byte)((duration >> 24) & 0xFF);
417 sendPacket(packetBytes);
420 private void sendGetLightPowerPacket() {
421 LifxHeader header = new LifxHeader();
423 header.setTagged(false);
424 header.setMacAddress(bulbMacAddress);
425 header.setSource(10); // randomly picked
426 header.setAck_required(false);
427 header.setRes_required(false);
428 header.setSequence(0);
431 byte[] dataBytes = header.getHeaderBytes();
432 sendPacket(dataBytes);
435 private void sendSetLightPowerPacket(int level, long duration) {
437 if ((level > 65535) || (duration > 4294967295l)
438 || (level < 0) || (duration < 0)) {
439 throw new InvalidParameterException("Invalid parameter values");
442 byte[] packetBytes = new byte[42];
445 LifxHeader header = new LifxHeader();
447 header.setTagged(false);
448 header.setMacAddress(bulbMacAddress);
449 header.setSource(10); // randomly picked
450 header.setAck_required(false);
451 header.setRes_required(false);
452 header.setSequence(0);
454 byte[] headerBytes = header.getHeaderBytes();
456 for (int i = 0; i < 36; i++) {
457 packetBytes[i] = headerBytes[i];
460 packetBytes[36] = (byte)(level & 0xFF);
461 packetBytes[37] = (byte)((level >> 8) & 0xFF);
463 packetBytes[38] = (byte)((duration >> 0) & 0xFF);
464 packetBytes[39] = (byte)((duration >> 8) & 0xFF);
465 packetBytes[40] = (byte)((duration >> 16) & 0xFF);
466 packetBytes[41] = (byte)((duration >> 24) & 0xFF);
468 sendPacket(packetBytes);
471 private void sendEchoRequestPacket(byte[] data) {
472 // Currently only 0 and 65535 are supported
473 // This is a fix for now
474 if (data.length != 64) {
475 throw new InvalidParameterException("Invalid parameter values, must have 64 bytes");
478 byte[] packetBytes = new byte[100];
480 LifxHeader header = new LifxHeader();
482 header.setTagged(false);
483 header.setMacAddress(bulbMacAddress);
484 header.setSource(10); // randomly picked
485 header.setAck_required(false);
486 header.setRes_required(false);
487 header.setSequence(0);
489 byte[] headerBytes = header.getHeaderBytes();
491 for (int i = 0; i < 36; i++) {
492 packetBytes[i] = headerBytes[i];
495 for (int i = 0; i < 64; i++) {
496 packetBytes[i + 36] = data[i];
499 sendPacket(packetBytes);
503 /*******************************************************************************************************************************************
507 *******************************************************************************************************************************************/
508 private DeviceStateService parseDeviceStateServiceMessage(LifxHeader header, byte[] payloadData) {
509 int service = payloadData[0];
510 long port = ((payloadData[3] & 0xFF) << 24);
511 port |= ((payloadData[2] & 0xFF) << 16);
512 port |= ((payloadData[1] & 0xFF) << 8);
513 port |= (payloadData[0] & 0xFF);
515 return new DeviceStateService(service, port);
518 private DeviceStateHostInfo parseDeviceStateHostInfoMessage(LifxHeader header, byte[] payloadData) {
519 long signal = ((payloadData[3] & 0xFF) << 24);
520 signal |= ((payloadData[2] & 0xFF) << 16);
521 signal |= ((payloadData[1] & 0xFF) << 8);
522 signal |= (payloadData[0] & 0xFF);
524 long tx = ((payloadData[7] & 0xFF) << 24);
525 tx |= ((payloadData[6] & 0xFF) << 16);
526 tx |= ((payloadData[5] & 0xFF) << 8);
527 tx |= (payloadData[4] & 0xFF);
529 long rx = ((payloadData[11] & 0xFF) << 24);
530 rx |= ((payloadData[10] & 0xFF) << 16);
531 rx |= ((payloadData[9] & 0xFF) << 8);
532 rx |= (payloadData[8] & 0xFF);
534 return new DeviceStateHostInfo(signal, tx, rx);
537 private DeviceStateHostFirmware parseDeviceStateHostFirmwareMessage(LifxHeader header, byte[] payloadData) {
539 for (int i = 0; i < 8; i++) {
540 build += ((long) payloadData[i] & 0xffL) << (8 * i);
545 long version = ((payloadData[19] & 0xFF) << 24);
546 version |= ((payloadData[18] & 0xFF) << 16);
547 version |= ((payloadData[17] & 0xFF) << 8);
548 version |= (payloadData[16] & 0xFF);
550 return new DeviceStateHostFirmware(build, version);
553 private DeviceStateWifiInfo parseDeviceStateWifiInfoMessage(LifxHeader header, byte[] payloadData) {
554 long signal = ((payloadData[3] & 0xFF) << 24);
555 signal |= ((payloadData[2] & 0xFF) << 16);
556 signal |= ((payloadData[1] & 0xFF) << 8);
557 signal |= (payloadData[0] & 0xFF);
559 long tx = ((payloadData[7] & 0xFF) << 24);
560 tx |= ((payloadData[6] & 0xFF) << 16);
561 tx |= ((payloadData[5] & 0xFF) << 8);
562 tx |= (payloadData[4] & 0xFF);
564 long rx = ((payloadData[11] & 0xFF) << 24);
565 rx |= ((payloadData[10] & 0xFF) << 16);
566 rx |= ((payloadData[9] & 0xFF) << 8);
567 rx |= (payloadData[8] & 0xFF);
569 return new DeviceStateWifiInfo(signal, tx, rx);
572 private DeviceStateWifiFirmware parseDeviceStateWifiFirmwareMessage(LifxHeader header, byte[] payloadData) {
574 for (int i = 0; i < 8; i++) {
575 build += ((long) payloadData[i] & 0xffL) << (8 * i);
580 long version = ((payloadData[19] & 0xFF) << 24);
581 version |= ((payloadData[18] & 0xFF) << 16);
582 version |= ((payloadData[17] & 0xFF) << 8);
583 version |= (payloadData[16] & 0xFF);
585 return new DeviceStateWifiFirmware(build, version);
588 private int parseStatePowerMessage(LifxHeader header, byte[] payloadData) {
589 int level = ((payloadData[1] & 0xFF) << 8);
590 level |= (payloadData[0] & 0xFF);
594 private String parseStateLabelMessage(LifxHeader header, byte[] payloadData) {
595 return new String(payloadData);
599 private DeviceStateVersion parseDeviceStateVersionMessage(LifxHeader header, byte[] payloadData) {
600 long vender = ((payloadData[3] & 0xFF) << 24);
601 vender |= ((payloadData[2] & 0xFF) << 16);
602 vender |= ((payloadData[1] & 0xFF) << 8);
603 vender |= (payloadData[0] & 0xFF);
605 long product = ((payloadData[7] & 0xFF) << 24);
606 product |= ((payloadData[6] & 0xFF) << 16);
607 product |= ((payloadData[5] & 0xFF) << 8);
608 product |= (payloadData[4] & 0xFF);
610 long version = ((payloadData[11] & 0xFF) << 24);
611 version |= ((payloadData[10] & 0xFF) << 16);
612 version |= ((payloadData[9] & 0xFF) << 8);
613 version |= (payloadData[8] & 0xFF);
615 return new DeviceStateVersion(vender, product, version);
618 private DeviceStateInfo parseDeviceStateInfoMessage(LifxHeader header, byte[] payloadData) {
622 for (int i = 0; i < 8; i++) {
623 time += ((long) payloadData[i] & 0xffL) << (8 * i);
624 upTime += ((long) payloadData[i + 8] & 0xffL) << (8 * i);
625 downTime += ((long) payloadData[i + 16] & 0xffL) << (8 * i);
628 return new DeviceStateInfo(time, upTime, downTime);
631 private DeviceStateLocation parseDeviceStateLocationMessage(LifxHeader header, byte[] payloadData) {
632 byte[] location = new byte[16];
633 for (int i = 0; i < 16; i++) {
634 location[i] = payloadData[i];
637 byte[] labelBytes = new byte[32];
638 for (int i = 0; i < 32; i++) {
639 labelBytes[i] = payloadData[i + 16];
643 for (int i = 0; i < 8; i++) {
644 updatedAt += ((long) payloadData[48] & 0xffL) << (8 * i);
647 return new DeviceStateLocation(location, new String(labelBytes), updatedAt);
650 private DeviceStateGroup parseDeviceStateGroupMessage(LifxHeader header, byte[] payloadData) {
651 byte[] group = new byte[16];
652 for (int i = 0; i < 16; i++) {
653 group[i] = payloadData[i];
656 byte[] labelBytes = new byte[32];
657 for (int i = 0; i < 32; i++) {
658 labelBytes[i] = payloadData[i + 16];
662 for (int i = 0; i < 8; i++) {
663 updatedAt += ((long) payloadData[48] & 0xffL) << (8 * i);
666 return new DeviceStateGroup(group, new String(labelBytes), updatedAt);
669 private byte[] parseDeviceEchoResponseMessage(LifxHeader header, byte[] payloadData) {
673 /*******************************************************************************************************************************************
677 *******************************************************************************************************************************************/
678 private LightState parseLightStateMessage(LifxHeader header, byte[] payloadData) {
680 byte[] colorData = new byte[8];
681 for (int i = 0; i < 8; i++) {
682 colorData[i] = payloadData[i];
684 BulbColor color = new BulbColor(colorData);
686 int power = ((payloadData[11] & 0xFF) << 8);
687 power |= (payloadData[10] & 0xFF);
689 String label = new String(payloadData);
691 byte[] labelArray = new byte[32];
692 for (int i = 0; i < 32; i++) {
693 labelArray[i] = payloadData[12 + i];
696 return new LightState(color, power, label);
699 private int parseLightStatePowerMessage(LifxHeader header, byte[] payloadData) {
700 int level = ((payloadData[1] & 0xFF) << 8);
701 level |= (payloadData[0] & 0xFF);
706 /*******************************************************************************************************************************************
710 *******************************************************************************************************************************************/
711 private void handleStateVersionMessageRecieved(LifxHeader header, byte[] payloadData) {
713 DeviceStateVersion deviceState = parseDeviceStateVersionMessage(header, payloadData);
714 int productNumber = (int)deviceState.getProduct();
716 boolean isColor = false;
718 if (productNumber == 1) { // Original 1000
720 } else if (productNumber == 3) { //Color 650
722 } else if (productNumber == 10) { // White 800 (Low Voltage)
724 } else if (productNumber == 11) { // White 800 (High Voltage)
726 } else if (productNumber == 18) { // White 900 BR30 (Low Voltage)
728 } else if (productNumber == 20) { // Color 1000 BR30
730 } else if (productNumber == 22) { // Color 1000
736 hueUpperBound = 65535;
737 saturationLowerBound = 0;
738 saturationUpperBound = 65535;
739 brightnessLowerBound = 0;
740 brightnessUpperBound = 65535;
741 temperatureLowerBound = 2500;
742 temperatureUpperBound = 9000;
746 saturationLowerBound = 0;
747 saturationUpperBound = 0;
748 brightnessLowerBound = 0;
749 brightnessUpperBound = 65535; // still can dim bulb
750 temperatureLowerBound = 2500;
751 temperatureUpperBound = 9000;
754 didGetBulbVersion.set(true);
758 private void handleLightStateMessageRecieved(LifxHeader header, byte[] payloadData) {
759 LightState lightState = parseLightStateMessage(header, payloadData);
761 BulbColor color = lightState.getColor();
762 int power = lightState.getPower();
764 boolean bulbWrongColor = false;
765 bulbWrongColor = bulbWrongColor || (color.getHue() != currentHue);
766 bulbWrongColor = bulbWrongColor || (color.getSaturation() != currentSaturation);
767 bulbWrongColor = bulbWrongColor || (color.getBrightness() != currentBrightness);
768 bulbWrongColor = bulbWrongColor || (color.getKelvin() != currentTemperature);
771 // gets set to true if any of the below if statements are taken
772 stateDidChange = false;
774 if (bulbWrongColor) {
775 BulbColor newColor = new BulbColor(currentHue, currentSaturation, currentBrightness, currentTemperature);
776 sendSetLightColorPacket(newColor, 250);
777 // System.out.println("Failed Check 1");
781 bulbStateMutex.acquire();
782 } catch (Exception e) {
785 boolean bulbIsOnTmp = bulbIsOn;
786 bulbStateMutex.release();
788 if ((!bulbIsOnTmp) && (power != 0)) {
790 // System.out.println("Failed Check 2: " + Integer.toString(power));
794 if (bulbIsOnTmp && (power < 65530)) {
796 // System.out.println("Failed Check 3: " + Integer.toString(power));
801 /*******************************************************************************************************************************************
803 ** Light Bulb Interface Methods
805 *******************************************************************************************************************************************/
806 public double getHue() {
809 settingBulbColorMutex.acquire();
810 tmp = ((double)currentHue / 65535.0) * 360.0;
811 } catch (Exception e) {
814 settingBulbColorMutex.release();
820 public double getSaturation() {
823 settingBulbColorMutex.acquire();
824 tmp = ((double)currentSaturation / 65535.0) * 360.0;
825 } catch (Exception e) {
828 settingBulbColorMutex.release();
834 public double getBrightness() {
837 settingBulbColorMutex.acquire();
838 tmp = ((double)currentBrightness / 65535.0) * 360.0;
839 } catch (Exception e) {
842 settingBulbColorMutex.release();
847 public int getTemperature() {
851 settingBulbTempuraturerMutex.acquire();
852 tmp = currentTemperature;
853 } catch (Exception e) {
856 settingBulbTempuraturerMutex.release();
861 public double getHueRangeLowerBound() {
862 if (!didGetBulbVersion.get()) {
865 return ((double)hueLowerBound / 65535.0) * 360.0;
868 public double getHueRangeUpperBound() {
869 if (!didGetBulbVersion.get()) {
872 return ((double)hueUpperBound / 65535.0) * 360.0;
875 public double getSaturationRangeLowerBound() {
876 if (!didGetBulbVersion.get()) {
879 return ((double)saturationLowerBound / 65535.0) * 100.0;
882 public double getSaturationRangeUpperBound() {
883 if (!didGetBulbVersion.get()) {
886 return ((double)saturationUpperBound / 65535.0) * 100.0;
889 public double getBrightnessRangeLowerBound() {
890 if (!didGetBulbVersion.get()) {
893 return ((double)brightnessLowerBound / 65535.0) * 100.0;
896 public double getBrightnessRangeUpperBound() {
897 if (!didGetBulbVersion.get()) {
900 return ((double)brightnessUpperBound / 65535.0) * 100.0;
903 public int getTemperatureRangeLowerBound() {
904 if (!didGetBulbVersion.get()) {
907 return temperatureLowerBound;
910 public int getTemperatureRangeUpperBound() {
911 if (!didGetBulbVersion.get()) {
914 return temperatureUpperBound;
917 public void setTemperature(int _temperature) {
920 settingBulbTempuraturerMutex.acquire();
921 } catch (Exception e) {
925 BulbColor newColor = new BulbColor(currentHue, currentSaturation, currentBrightness, _temperature);
926 sendSetLightColorPacket(newColor, 250);
928 currentTemperature = _temperature;
929 stateDidChange = true;
931 settingBulbTempuraturerMutex.release();
934 public void setColor(double _hue, double _saturation, double _brightness) {
937 settingBulbColorMutex.acquire();
938 } catch (Exception e) {
944 _saturation /= 100.0;
945 _brightness /= 100.0;
948 int newHue = (int)(_hue * 65535.0);
949 int newSaturation = (int)(_saturation * 65535.0);
950 int newBrightness = (int)(_brightness * 65535.0);
952 BulbColor newColor = new BulbColor(newHue, newSaturation, newBrightness, currentTemperature);
953 sendSetLightColorPacket(newColor, 250);
956 currentSaturation = newSaturation;
957 currentBrightness = newBrightness;
958 stateDidChange = true;
960 settingBulbColorMutex.release();
964 public void turnOff() {
967 bulbStateMutex.acquire();
969 sendSetLightPowerPacket(0, 0);
970 stateDidChange = true;
971 } catch (Exception e) {
975 bulbStateMutex.release();
978 public void turnOn() {
980 bulbStateMutex.acquire();
982 sendSetLightPowerPacket(65535, 0);
983 stateDidChange = true;
985 } catch (Exception e) {
990 bulbStateMutex.release();
993 public boolean getState() {
997 bulbStateMutex.acquire();
999 } catch (Exception e) {
1000 e.printStackTrace();
1003 bulbStateMutex.release();
1009 /*******************************************************************************************************************************************
1011 ** Communication Helpers
1013 *******************************************************************************************************************************************/
1014 private void recievedPacket(byte[] packetData) {
1016 byte[] headerBytes = new byte[36];
1017 for (int i = 0; i < 36; i++) {
1018 headerBytes[i] = packetData[i];
1021 LifxHeader recHeader = new LifxHeader();
1022 recHeader.setFromBytes(headerBytes);
1024 // load the payload bytes (strip away the header)
1025 byte[] payloadBytes = new byte[recHeader.getSize()];
1026 for (int i = 36; i < recHeader.getSize(); i++) {
1027 payloadBytes[i - 36] = packetData[i];
1030 System.out.println("Received: " + Integer.toString(recHeader.getType()));
1032 switch (recHeader.getType()) {
1034 DeviceStateService dat = parseDeviceStateServiceMessage(recHeader, payloadBytes);
1035 // System.out.println("Service: " + Integer.toString(dat.getService()));
1036 // System.out.println("Port : " + Long.toString(dat.getPort()));
1041 handleStateVersionMessageRecieved(recHeader, payloadBytes);
1045 parseDeviceStateInfoMessage(recHeader, payloadBytes);
1050 handleLightStateMessageRecieved(recHeader, payloadBytes);
1054 // System.out.println("unknown packet Type");
1059 private void sendPacket(byte[] packetData) {
1060 // System.out.println("About to send");
1061 sendSocketFlag = true;
1064 socketMutex.acquire();
1065 } catch (InterruptedException e) {
1066 System.out.println("mutex Error");
1070 communicationSockect.sendData(packetData);
1072 } catch (IOException e) {
1073 System.out.println("Socket Send Error");
1076 sendSocketFlag = false;
1077 socketMutex.release();
1082 * Worker function which runs the while loop for receiving data from the bulb.
1085 private void workerFunction() {
1086 LifxHeader h = new LifxHeader();
1089 // Need timeout on receives since we are not sure if a packet will be available
1090 // for processing so don't block waiting
1091 communicationSockect.setSoTimeout(50);
1092 } catch (IOException e) {
1095 // 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();