Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
robvanderleek committed Dec 21, 2013
1 parent fe3bb39 commit 3bf5e89
Show file tree
Hide file tree
Showing 10 changed files with 577 additions and 0 deletions.
6 changes: 6 additions & 0 deletions install.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#!/bin/sh
mvn compile assembly:single
TARGET="$HOME/Desktop/lifx.jar"
cp target/*with-dependencies.jar $HOME/Desktop/lifx.jar
echo "Launchable jar (lifx.jar) copied to $TARGET"
echo "Installation done."
91 changes: 91 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
<project xmlns="https://maven.apache.org/POM/4.0.0" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://maven.apache.org/POM/4.0.0 https://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>Lifx</groupId>
<artifactId>Lifx</artifactId>
<packaging>jar</packaging>
<version>1.0-SNAPSHOT</version>
<name>Lifx</name>
<url>https://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.0.2</version>
<configuration>
<source>1.5</source>
<target>1.5</target>
</configuration>
</plugin>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<archive>
<manifest>
<mainClass>lifx.Main</mainClass>
</manifest>
</archive>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
</plugin>
</plugins>
<resources>
<resource>
<directory>${basedir}/src/main/resources</directory>
<filtering>false</filtering>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
<include>**/Lifx-icon.png</include>
</includes>
</resource>
</resources>
<testResources>
<testResource>
<directory>src/test/resources</directory>
</testResource>
</testResources>
</build>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.uispec4j</groupId>
<artifactId>uispec4j</artifactId>
<classifier>jdk16</classifier>
<version>2.4</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.1</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.4</version>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.1</version>
</dependency>
<dependency>
<groupId>net.sourceforge.collections</groupId>
<artifactId>collections-generic</artifactId>
<version>4.01</version>
</dependency>
</dependencies>
</project>
27 changes: 27 additions & 0 deletions src/main/java/lifx/Bulb.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package lifx;

import java.net.InetAddress;

public class Bulb {
private final InetAddress inetAddress;
private final byte[] macAddress;

public Bulb(InetAddress inetAddress, byte[] macAddress) {
this.inetAddress = inetAddress;
this.macAddress = macAddress;
}

public InetAddress getInetAddress() {
return inetAddress;
}

public byte[] getMacAddress() {
return macAddress;
}

public String getMacAddressAsString() {
return String.format("%02x:%02x:%02x:%02x:%02x:%02x", macAddress[0], macAddress[1], macAddress[2],
macAddress[3], macAddress[4], macAddress[5]);
}

}
135 changes: 135 additions & 0 deletions src/main/java/lifx/Main.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
package lifx;

import java.io.DataOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.InterfaceAddress;
import java.net.NetworkInterface;
import java.net.Socket;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

public class Main {
private static final Log LOG = LogFactory.getLog(Main.class);
private static final int PORT = 56700;
private static PrintStream OUT = System.out;

private static void printUsage() {
OUT.println("Usage:");
OUT.println(" java -jar lifx.jar <command>");
OUT.println("");
OUT.println("Where command can be:");
OUT.println(" scan:");
OUT.println(" Scan local network for a gateway bulb");
OUT.println(" switch <mac-address> [on|off]:");
OUT.println(" Switch selected bulb on/off");
}

public static void main(String[] args) throws IOException {
if (args.length < 1) {
printUsage();
System.exit(1);
}
if (args[0].equalsIgnoreCase("scan")) {
new Main().scan();
} else if (args[0].equalsIgnoreCase("switch")) {
if (args.length != 3 || (!(args[2].equalsIgnoreCase("on") || args[2].equalsIgnoreCase("off")))) {
printUsage();
} else {
new Main().powerSwitchBulb(parseMacAddress(args[1]), args[2].equalsIgnoreCase("on") ? true : false);
}
}
}

private static byte[] parseMacAddress(String macAddress) {
byte[] result = new byte[6];
result[0] = (byte)(Integer.parseInt(macAddress.substring(0, 2), 16));
result[1] = (byte)(Integer.parseInt(macAddress.substring(3, 5), 16));
result[2] = (byte)(Integer.parseInt(macAddress.substring(6, 8), 16));
result[3] = (byte)(Integer.parseInt(macAddress.substring(9, 11), 16));
result[4] = (byte)(Integer.parseInt(macAddress.substring(12, 14), 16));
result[5] = (byte)(Integer.parseInt(macAddress.substring(15, 17), 16));
return result;
}

private void scan() {
Bulb gatewayBulb = discoverGatewayBulb();
if (gatewayBulb == null) {
OUT.println("No LifX gateway bulb found!");
} else {
OUT.println("Found LifX gateway bulb!");
OUT.println(" IP address: " + gatewayBulb.getInetAddress());
OUT.println(" Mac address: " + gatewayBulb.getMacAddressAsString());
}
}

private void powerSwitchBulb(byte[] macAddress, boolean on) throws IOException {
Bulb gatewayBulb = discoverGatewayBulb();
PowerManagementPacket packet = new PowerManagementPacket(macAddress, on);
Socket socket = new Socket(gatewayBulb.getInetAddress(), PORT);
DataOutputStream dataOutputStream = new DataOutputStream(socket.getOutputStream());
dataOutputStream.write(packet.toByteArray());
socket.close();
}

private Bulb discoverGatewayBulb() {
LOG.info("Discovering gateway bulb...");
List<InetAddress> networkBroadcastAddresses = getNetworkBroadcastAddresses();
for (InetAddress broadcastAddress : networkBroadcastAddresses) {
Bulb result = discoverGatewayBulb(broadcastAddress);
if (result != null) {
LOG.info("Found gateway bulb! (" + result.getInetAddress() + ")");
return result;
}
}
return null;
}

private Bulb discoverGatewayBulb(InetAddress broadcastAddress) {
Bulb result = null;
Packet packet = new Packet();
byte[] byteArray = packet.toByteArray();
try {
DatagramSocket socket = new DatagramSocket(PORT);
DatagramPacket datagramPacket = new DatagramPacket(byteArray, 0, byteArray.length, broadcastAddress, PORT);
socket.send(datagramPacket);
DatagramPacket answer = new DatagramPacket(byteArray, 0, byteArray.length);
socket.receive(answer);
if (answer.getAddress().isSiteLocalAddress()) {
socket.receive(answer);
}
Packet answerLifxPacket = Packet.fromDatagramPacket(answer);
result = new Bulb(answer.getAddress(), answerLifxPacket.getGatewayMac());
} catch (SocketException e) {} catch (IOException e) {}
return result;
}

List<InetAddress> getNetworkBroadcastAddresses() {
List<InetAddress> result = new ArrayList<InetAddress>();
try {
List<NetworkInterface> networkInterfaces = Collections.list(NetworkInterface.getNetworkInterfaces());
for (NetworkInterface networkInterface : networkInterfaces) {
if (!networkInterface.isLoopback() && networkInterface.isUp()) {
List<InterfaceAddress> interfaceAddresses = networkInterface.getInterfaceAddresses();
for (InterfaceAddress interfaceAddress : interfaceAddresses) {
InetAddress broadcast = interfaceAddress.getBroadcast();
if (broadcast != null) {
result.add(broadcast);
}
}
}
}
} catch (SocketException e) {
LOG.error("Could not retreive broadcast addresses from available network interfaces");
}
return result;
}
}
80 changes: 80 additions & 0 deletions src/main/java/lifx/Packet.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package lifx;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.DatagramPacket;

import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

public class Packet {
private static final Log LOG = LogFactory.getLog(Packet.class);
private byte[] size = new byte[] {0x24, 0x00};
private byte[] protocol = new byte[] {0x00, 0x34};
private byte[] reserved = new byte[] {0x00, 0x00, 0x00, 0x00};
private byte[] targetMac = new byte[] {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
private byte[] reserved2 = new byte[] {0x00, 0x00};
private byte[] gatewayMac = new byte[] {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
private byte[] reserved3 = new byte[] {0x00, 0x00};
private byte[] timestamp = new byte[] {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
private byte[] type = new byte[] {0x02, 0x00};
private byte[] reserved4 = new byte[] {0x00, 0x00};

public Packet() {}

public Packet(byte[] targetMac, byte[] type) {
this.targetMac = targetMac;
this.gatewayMac = targetMac;
this.type = type;
}

public Packet(byte[] targetMac, byte[] gatewayMac, byte[] timestamp, byte[] type) {
this.targetMac = targetMac;
this.gatewayMac = gatewayMac;
this.timestamp = timestamp;
this.type = type;
}

public byte[] getTargetMac() {
return targetMac;
}

public byte[] getGatewayMac() {
return gatewayMac;
}

public byte[] getTimestamp() {
return timestamp;
}

public byte[] getType() {
return type;
}

public byte[] toByteArray() {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
try {
outputStream.write(size);
outputStream.write(protocol);
outputStream.write(reserved);
outputStream.write(targetMac);
outputStream.write(reserved2);
outputStream.write(gatewayMac);
outputStream.write(reserved3);
outputStream.write(timestamp);
outputStream.write(type);
outputStream.write(reserved4);
} catch (IOException e) {
LOG.error("Could not build Lifx packet");
}
return outputStream.toByteArray();
}

public static Packet fromDatagramPacket(DatagramPacket datagramPacket) {
byte[] data = datagramPacket.getData();
return new Packet(ArrayUtils.subarray(data, 8, 13), ArrayUtils.subarray(data, 16, 22), ArrayUtils.subarray(
data, 23, 31), ArrayUtils.subarray(data, 31, 33));
}

}
34 changes: 34 additions & 0 deletions src/main/java/lifx/PowerManagementPacket.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package lifx;

import java.io.IOException;

import org.apache.commons.io.output.ByteArrayOutputStream;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

public class PowerManagementPacket extends Packet {
private static final Log LOG = LogFactory.getLog(PowerManagementPacket.class);
private final boolean on;

public PowerManagementPacket(byte[] targetMacAddress, boolean on) {
super(targetMacAddress, new byte[] {0x15, 0x00});
this.on = on;
}

@Override
public byte[] toByteArray() {
byte[] result = new byte[] {};
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
try {
outputStream.write(super.toByteArray());
outputStream.write(on ? 0x01 : 0x00);
outputStream.write(0x00);
result = outputStream.toByteArray();
outputStream.close();
result[0] = 0x26;
} catch (IOException e) {
LOG.error("Error building packet");
}
return result;
}
}
Loading

0 comments on commit 3bf5e89

Please sign in to comment.