Proxy to milight v6 bridge
package testing_delete;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.net.UnknownHostException;
import java.util.Date;
import java.util.Enumeration;
public class ProxyCommunication {
boolean willbeclosed = false;
InetAddress milight;
byte milightMAC[] = { 0, 0, 0, 0, 0, 0 };
String networkInterfaceName;
InetAddress localAddr;
InetAddress appAddr;
int appPort;
byte fakeMAC[] = { (byte) 0xAC, (byte) 0xCF, (byte) 0x23, (byte) 0xF5, (byte) 0x7A, (byte) 0xD4 };
DatagramSocket socketFromAppToProxy, socketFromBridgeToProxy;
FileOutputStream file;
public void tests() {
try {
if (isPacketFromBridge("10.1.1.27,ACCF23F57AD4,HF-LPB100")) {
String remote_id = String.format("%02X%02X%02X%02X%02X%02X", milightMAC[0], milightMAC[1],
milightMAC[2], milightMAC[3], milightMAC[4], milightMAC[5]);
System.out.println("works! " + milight.toString() + " " + remote_id);
}
} catch (UnknownHostException e1) {
e1.printStackTrace();
}
}
public void start(NetworkInterface networkInterface)
throws FileNotFoundException, SocketException, UnknownHostException {
Enumeration<InetAddress> e = networkInterface.getInetAddresses();
while (e.hasMoreElements()) {
InetAddress addr = e.nextElement();
if (addr instanceof Inet6Address) {
continue;
}
this.localAddr = addr;
break;
}
if (this.localAddr == null) {
System.err.println("## No ipv4 found on network interface " + networkInterfaceName);
return;
}
System.out.println("## Your IP is " + localAddr.getHostAddress() + " on " + networkInterfaceName);
milight = InetAddress.getByName("255.255.255.255");
if (!runRealBridgeDiscovery()) {
System.err.println("No iBox found!");
return;
}
start(milight, networkInterface.getName());
}
public void start(InetAddress destIP, String networkInterfaceName)
throws FileNotFoundException, SocketException, UnknownHostException {
this.networkInterfaceName = networkInterfaceName;
this.localAddr = InetAddress.getByName("0.0.0.0");
milight = destIP;
String remote_id = String.format("%02X%02X%02X%02X%02X%02X", milightMAC[0], milightMAC[1], milightMAC[2],
milightMAC[3], milightMAC[4], milightMAC[5]);
System.out.println("## Your iBox is at " + milight.toString() + " BridgeID: " + remote_id);
file = new FileOutputStream("milight_" + networkInterfaceName + ".txt");
socketFromAppToProxy = new DatagramSocket(null);
socketFromAppToProxy.setReuseAddress(true);
socketFromAppToProxy.bind(new InetSocketAddress(5987));
socketFromBridgeToProxy = new DatagramSocket();
new Thread(new Runnable() {
@Override
public void run() {
runReadFromApp();
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
runReadFromBridge();
}
}).start();
}
public boolean runRealBridgeDiscovery() {
byte[] discoverbuffer_v6 = "HF-A11ASSISTHREAD".getBytes();
try {
System.out.println("## Discovery receive thread ready on " + networkInterfaceName);
final DatagramPacket discoverPacket_v6 = new DatagramPacket(discoverbuffer_v6, discoverbuffer_v6.length,
milight, 48899);
DatagramSocket datagramSocket = new DatagramSocket();
datagramSocket.setSoTimeout(1000);
byte[] buffer = new byte[1024];
DatagramPacket r_packet = new DatagramPacket(buffer, buffer.length);
// Now loop forever, waiting to receive packets and printing them.
while (!willbeclosed) {
System.out.println("Find iBox bridge on " + networkInterfaceName);
datagramSocket.send(discoverPacket_v6);
try {
r_packet.setLength(buffer.length);
datagramSocket.receive(r_packet);
} catch (SocketTimeoutException e) {
continue;
}
if (isPacketFromBridge(new String(r_packet.getData()))) {
return true;
}
}
} catch (IOException e) {
if (willbeclosed) {
return false;
}
System.out.println(e.getLocalizedMessage());
}
return false;
}
private boolean isPacketFromBridge(String data) throws UnknownHostException {
// example: 10.1.1.27,ACCF23F57AD4,HF-LPB100
String[] msg = data.split(",");
if (msg.length >= 2 && msg[1].length() == 12) {
int version = msg.length >= 3 && msg[2].length() > 0 ? 6 : 3;
if (version != 6) {
System.out.println("Found old milight bridge. Not of use for us though.");
} else {
byte mac[] = new byte[6];
for (int i = 0; i < 6; ++i) {
mac[i] = Integer.valueOf(msg[1].substring(i * 2, i * 2 + 2), 16).byteValue();
}
milight = InetAddress.getByName(msg[0]);
milightMAC = mac;
return true;
}
} else {
System.out.println("Unexpected data received " + data + " on " + networkInterfaceName);
}
return false;
}
public void runReadFromApp() {
try {
byte[] a = new byte[0];
DatagramPacket s_packet = new DatagramPacket(a, a.length);
System.out.println("Read from app thread ready on " + networkInterfaceName);
byte[] buffer = new byte[1024];
DatagramPacket r_packet = new DatagramPacket(buffer, buffer.length);
// Now loop forever, waiting to receive packets from app and sending them to the bridge
while (!willbeclosed) {
r_packet.setLength(buffer.length);
socketFromAppToProxy.receive(r_packet);
appAddr = r_packet.getAddress();
appPort = r_packet.getPort();
int len = r_packet.getLength();
// Translate the fake mac to the actual bridge mac
int macIndex = ArrayUtils.findArray(buffer, len, fakeMAC);
if (macIndex != -1) {
System.out.println("FAKE-MAC at " + String.valueOf(macIndex) + " replaced");
buffer[macIndex + 0] = milightMAC[0];
buffer[macIndex + 1] = milightMAC[1];
buffer[macIndex + 2] = milightMAC[2];
buffer[macIndex + 3] = milightMAC[3];
buffer[macIndex + 4] = milightMAC[4];
buffer[macIndex + 5] = milightMAC[5];
}
logPacket(buffer, len, "From App (" + appAddr.getHostAddress() + ")");
s_packet.setAddress(milight);
s_packet.setPort(5987);
s_packet.setData(buffer, 0, len);
socketFromBridgeToProxy.send(s_packet);
}
} catch (IOException e) {
if (willbeclosed) {
return;
}
System.out.println(e.getLocalizedMessage());
}
}
public void runReadFromBridge() {
try {
byte[] a = new byte[0];
DatagramPacket s_packet = new DatagramPacket(a, a.length);
System.out.println("Read from bridge thread ready on " + networkInterfaceName);
byte[] buffer = new byte[1024];
DatagramPacket r_packet = new DatagramPacket(buffer, buffer.length);
// Now loop forever, waiting to receive packets from app and sending them to the bridge
while (!willbeclosed) {
r_packet.setLength(buffer.length);
socketFromBridgeToProxy.receive(r_packet);
if (appAddr == null) {
System.out.println("Received packet from bridge, but app has to send first packet!");
return;
}
int len = r_packet.getLength();
// Translate the bridge mac to our fake mac
int macIndex = ArrayUtils.findArray(buffer, len, milightMAC);
if (macIndex != -1) {
System.out.println("BRIDGE-MAC at " + String.valueOf(macIndex) + " replaced");
buffer[macIndex + 0] = fakeMAC[0];
buffer[macIndex + 1] = fakeMAC[1];
buffer[macIndex + 2] = fakeMAC[2];
buffer[macIndex + 3] = fakeMAC[3];
buffer[macIndex + 4] = fakeMAC[4];
buffer[macIndex + 5] = fakeMAC[5];
}
logPacket(buffer, len, "From Bridge");
s_packet.setAddress(appAddr);
s_packet.setPort(appPort);
s_packet.setData(buffer, 0, len);
socketFromAppToProxy.send(s_packet);
}
} catch (IOException e) {
if (willbeclosed) {
return;
}
System.out.println(e.getLocalizedMessage());
}
}
protected void logPacket(byte[] data, int len, String reason) {
StringBuffer s = new StringBuffer();
for (int i = 0; i < len; ++i) {
s.append(String.format("%02X ", data[i]));
}
String d = reason + " " + new Date().toString() + "\n" + s.toString() + "\n\n";
System.out.print(d);
try {
file.write(d.getBytes());
file.flush();
} catch (IOException e) {
}
}
}
package testing_delete;
import java.io.FileNotFoundException;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.UnknownHostException;
public class Main {
public static void main(String[] args) {
if (args.length >= 2) {
System.err.println(
"This program will find the iBox bridge automatically. Please do not provide any arguments");
return;
}
System.out.println(
"Please add the iBox to your official Milight app before using this program and check if all commands work as expected.\n"
+ "Open this program and search now for devices and add the newly appeared bridge. Control your bulbs via this new added bridge.\n");
System.out.println(
"You will find a logfile milight.txt with all intercepted messages in the current working directory. Please only submit this file to the forum if you see a line saying BRIDGE-MAC.\n");
System.out.println(
"Please use the following commands in order:\nSwitch app to color mode\nOFF iBox, OFF zone 0, ON iBox, ON zone 0,\nany brightness change iBox, any brightness change zone 0,\nany color change iBox, any color change zone 0");
System.out.println("If there is a white bulb mode in the app, switch to that one and repeat all steps");
System.out.println("Thank you for your help\n");
ProxyCommunication main = new ProxyCommunication();
try {
main.milight = InetAddress.getByName("192.168.1.119");
main.runRealBridgeDiscovery();
main.start(InetAddress.getByName("192.168.1.119"), "tun0");
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SocketException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (UnknownHostException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// try {
// Enumeration<NetworkInterface> e = NetworkInterface.getNetworkInterfaces();
// while (e.hasMoreElements()) {
// NetworkInterface networkInterface = e.nextElement();
// if (networkInterface.isLoopback()) {
// continue;
// }
// new Thread(new Runnable() {
// @Override
// public void run() {
// ProxyCommunication main = new ProxyCommunication();
// try {
// main.start(networkInterface);
// } catch (FileNotFoundException e) {
// // TODO Auto-generated catch block
// e.printStackTrace();
// } catch (SocketException e) {
// // TODO Auto-generated catch block
// e.printStackTrace();
// }
// }
// }).start();
//
// }
// Thread.sleep(1000 * 60 * 5);
// } catch (SocketException e) {
// // TODO Auto-generated catch block
// e.printStackTrace();
// } catch (InterruptedException e1) {
// // TODO Auto-generated catch block
// e1.printStackTrace();
// }
}
}
package testing_delete;
public class ArrayUtils {
/**
* Finds a sub array in a large array
*
* @param largeArray
* @param subArray
* @return index of sub array
*/
public static int findArray(byte[] largeArray, byte[] subArray) {
/* If any of the arrays is empty then not found */
if (largeArray.length == 0 || subArray.length == 0) {
return -1;
}
/* If subarray is larger than large array then not found */
if (subArray.length > largeArray.length) {
return -1;
}
for (int i = 0; i < largeArray.length; i++) {
/* Check if the next element of large array is the same as the first element of subarray */
if (largeArray[i] == subArray[0]) {
boolean subArrayFound = true;
for (int j = 0; j < subArray.length; j++) {
/* If outside of large array or elements not equal then leave the loop */
if (largeArray.length <= i + j || subArray[j] != largeArray[i + j]) {
subArrayFound = false;
break;
}
}
/* Sub array found - return its index */
if (subArrayFound) {
return i;
}
}
}
/* Return default value */
return -1;
}
}