Last active
February 4, 2017 01:41
-
-
Save davidgraeff/193796ac6a9d266292994f2c26164130 to your computer and use it in GitHub Desktop.
Proxy to milight v6 bridge
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(); | |
// } | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) { | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment