/*
* DeviceS20.java
*
* This class was written by Jens Schröder.
* Webpage: https://server47.de
*
* You may use this code in your own - non commercial - code as long as you don't remove this header.
*/
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.InterfaceAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Observable;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.xml.bind.DatatypeConverter;
public class DeviceS20
{
protected static final String broadcastAddress =
"255.255.255.255";
protected final static int s20Port = 10000;
protected final static String magicKey =
"6864";
protected final static int queueTTL = 2;
// Incoming packets will be removed after x seconds if they are not taken
protected final static int maxSleepTime = 4000;
// Maximum total waiting time in milliseconds
protected final static int sleepStep = 10;
// Time between checks if response has arrived in milliseconds
protected final static int querySwitchTollerance = 30;
// seconds
protected final static int switchAttempts = 3;
protected InetAddress ipAddress;
protected String macAddress;
protected static long timestampLastDiscovery;
protected static boolean stopRequested =
false;
protected static boolean listenerRunning =
false;
protected static CopyOnWriteArrayList
queue = new CopyOnWriteArrayList();
// protected static Listener listener;
protected static Thread listenerThread;
static DatagramSocket datagramSendSocket = null;
protected static synchronized CopyOnWriteArrayList getQueue()
{
return queue;
}
public void setIpAddress(InetAddress ipAddress)
{
this.ipAddress = ipAddress;
}
public InetAddress getIpAddress()
{
return ipAddress;
}
String name;
public boolean state;
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
public String getMacAddress()
{
return macAddress;
}
public void setMacAddress(String macAddress)
{
this.macAddress = macAddress.toLowerCase();
}
public boolean getState()
{
return state;
}
public void setState(boolean state)
{
this.state = state;
}
public static DeviceS20 getByIpAddress(String singleIpAddress) throws UnknownHostException
{
InetAddress singleAddress = InetAddress.getByName(singleIpAddress);
return queryDevice(singleAddress).get(0);
}
public String getReverseMacAddress()
{
String mac = this.getMacAddress();
StringBuilder revMac = new StringBuilder();
int pos = mac.length() - 2;
// ACCF238342EE
do
{
revMac.append(mac.substring(pos, pos+2));
pos = pos - 2;
}
while(pos >= 0);
// log("Reverse Mac: " + revMac.toString().toLowerCase());
return revMac.toString().toLowerCase();
}
public static ArrayList discoverAllDevices()
{
// TODO: Replace by a more generic address:
try
{
// for (InetAddress bcAddress : listOfBroadcasts)
// {
// https://en.wikipedia.org/wiki/Broadcast_address
InetAddress broadcastAddressForUse = InetAddress.getByName(broadcastAddress);
// InetAddress broadcastAddressForUse = InetAddress.getByName(bcAddress);
// }
return queryDevice(broadcastAddressForUse);
}
catch(UnknownHostException e)
{
return null;
}
}
protected static ArrayList queryDevice(InetAddress singleIpOrBroadcastAddress)
{
/*
* get1() and queryDevice() send the same data out. Just for performance reasons get1() gives a shit.
* If real data is required queryDevice() has to be used.
*/
/*
* Gerät einstecken, blinkt jetzt schnell rot
* Taste gedrückt halten, blinkt jetzt schnell blau
* Gerät hat jetzt einen Access Point aufgemacht, da reingehen (siehe Bilder)
* Jetzt in die Anwendung -> Wifi Config -> fertig
*/
/*
* Send this to 255.255.255.255
* 68:64:00:06:71:61
*/
log("Sending discovery to " + singleIpOrBroadcastAddress.toString(), 5);
verifyListenerIsRunning();
final String discoverCommand = "00067161";
// DatagramSocket datagramSendSocket = null;
try
{
byte[] buffer = hexStringToByteArray(magicKey + discoverCommand);
DatagramPacket packet = new DatagramPacket(buffer, buffer.length, singleIpOrBroadcastAddress, s20Port);
// datagramSendSocket = new DatagramSocket();
// datagramSendSocket.send(packet);
getDatagramSendSocket().send(packet);
/*
* Als Antwort kommt jetzt:
* Aus:
* 6864002A716100ACCF2383BEE6202020202020E6BE8323CFAC202020202020534F43303035ADB029DA00
* An: ---------- Unterschiede
* 6864002A716100ACCF2383BEE6202020202020E6BE8323CFAC202020202020534F4330303572B129DA01
*
* 68:64:00:2a:71:61:00:ac:cf:23:83:be:e6:20:20:20:20:20:20:e6:be:83:23:cf:ac:20:20:20:20:20:20:53:4f:43:30:30:35:e6:f5:28:da:00
* 68:64:00:2A:71:61:00:AC:CF:23:83:BE:E6:20:20:20:20:20:20:E6:BE:83:23:CF:AC:20:20:20:20:20:20:53:4F:43:30:30:35:BE:41:3E:DA:00
* ----- Magic Key
* -- Message length
* ----------- ? (evtl. Command code)
* ------------------ MAC address
* ----------------- MAC padding
* ------------------ reverse mac (blocks, not characters)
* mac padding -----------------
* ------------------------------- ?
* - State on/off
*/
ArrayList respondingDevices = new ArrayList();
Thread.sleep(maxSleepTime);
Iterator iter = getQueue().iterator();
while(iter.hasNext())
// int queueSize = getQueue().size();
// for(int i=0; i < queueSize; i++)
{
// QueuePacket currentPacket = getQueue().get(i);
QueuePacket currentPacket = iter.next();
if(currentPacket.getSender().equals(singleIpOrBroadcastAddress) && currentPacket.getValue().length() >= "6864002A716100ACCF2383BEE6202020202020E6BE8323CFAC202020202020534F43303035ADB029DA00".length())
{
currentPacket.takeFromQueue();
DeviceS20 newDevice = new DeviceS20();
newDevice.setIpAddress(currentPacket.sender);
String macAddress = currentPacket.getValue().substring(14, 14+12);
newDevice.setMacAddress(macAddress);
newDevice.setIpAddress(currentPacket.getSender());
boolean state = false;
// log(currentPacket.getValue().substring(83, 84));
if(Integer.parseInt(currentPacket.getValue().substring(83, 84)) == 1)
state = true;
newDevice.setState(state);
boolean found = false;
for(DeviceS20 oneDevice : respondingDevices)
{
if(oneDevice.getMacAddress().equalsIgnoreCase(macAddress))
{
found = true;
break;
}
}
if(!found)
respondingDevices.add(newDevice);
// 686400067161
// 6864002A716100
// ACCF238342EE
// Compensate for the packet being taken from the queue
// i--;
// queueSize--;
}
}
ArrayList threads = new ArrayList();
for(DeviceS20 respondedDevice : respondingDevices)
{
Thread t = new Thread(new DeviceQuerrier(respondedDevice));
t.setName("Thread-DeviceQuerrier-respondedDevice-" + respondedDevice.getName());
threads.add(t);
t.start();
}
// Wait for all threads to finish.
log("Waiting for all query threads to finish...", 5);
for(Thread t : threads)
t.join();
log("All query threads finished.", 5);
// datagramSendSocket.close();
timestampLastDiscovery = Calendar.getInstance().getTimeInMillis();
return respondingDevices;
}
catch(Exception e)
{
e.printStackTrace();
return null;
}
// finally
// {
// try
// {
// datagramSendSocket.close();
// }
// catch(Exception e)
// {}
// }
}
protected static void queryDevice(DeviceS20 device)
{
try
{
get2(device);
}
catch (Exception e)
{
// give a shit
}
try
{
get3(device);
}
catch (Exception e)
{
// give a shit
}
try
{
get4(device);
}
catch (Exception e)
{
// give a shit
}
try
{
get5(device);
}
catch (Exception e)
{
// give a shit
}
try
{
get6(device);
}
catch (Exception e)
{
// give a shit
}
try
{
String response = get7DeviceName(device);
device.setName(response);
}
catch (Exception e)
{
// give a shit
}
try
{
get8(device);
}
catch (Exception e)
{
// give a shit
}
}
static class DeviceQuerrier implements Runnable
{
DeviceS20 device;
public DeviceQuerrier(DeviceS20 device)
{
this.device = device;
}
@Override
public void run()
{
if(this.device != null)
queryDevice(this.device);
else
log("No device specified.", 5);
}
}
protected static synchronized DatagramSocket getDatagramSendSocket() throws SocketException
{
if(datagramSendSocket == null || datagramSendSocket.isClosed())
datagramSendSocket = new DatagramSocket();
return datagramSendSocket;
}
protected static void get1(DeviceS20 dev)
{
/*
* get1() and queryDevice() send the same data out. Just for performance reasons get1() gives a shit.
* If real data is required queryDevice() has to be used.
*/
log("get1() for " + dev.toString(), 5);
verifyListenerIsRunning();
final String discoverCommand = "00067161";
// DatagramSocket datagramSendSocket = null;
// if(datagramSendSocket == null)
// datagramSendSocket = null;
try
{
log("Sending discovery to " + dev.getIpAddress().toString(), 4);
byte[] buffer = hexStringToByteArray(magicKey + discoverCommand);
DatagramPacket packet = new DatagramPacket(buffer, buffer.length, dev.getIpAddress(), s20Port);
// datagramSendSocket = new DatagramSocket();
// datagramSendSocket.send(packet);
getDatagramSendSocket().send(packet);
/*
* Als Antwort kommt jetzt:
* Aus:
* 6864002A716100ACCF2383BEE6202020202020E6BE8323CFAC202020202020534F43303035ADB029DA00
* An: ---------- Unterschiede
* 6864002A716100ACCF2383BEE6202020202020E6BE8323CFAC202020202020534F4330303572B129DA01
*
* 68:64:00:2a:71:61:00:ac:cf:23:83:be:e6:20:20:20:20:20:20:e6:be:83:23:cf:ac:20:20:20:20:20:20:53:4f:43:30:30:35:e6:f5:28:da:00
* 68:64:00:2A:71:61:00:AC:CF:23:83:BE:E6:20:20:20:20:20:20:E6:BE:83:23:CF:AC:20:20:20:20:20:20:53:4F:43:30:30:35:BE:41:3E:DA:00
* ----- Magic Key
* -- Message length
* ----------- ? (evtl. Command code)
* ------------------ MAC address
* ----------------- MAC padding
* ------------------ reverse mac (blocks, not characters)
* mac padding -----------------
* ------------------------------- ?
* - State on/off
*/
int currentSleepTime = 0;
do
{
Thread.sleep(sleepStep);
Iterator iter = getQueue().iterator();
while(iter.hasNext())
// int queueSize = getQueue().size();
// for(int i=0; i < queueSize; i++)
{
// Miscellaneous.logEvent("Checking packet " + String.valueOf(i+1), 5);
// QueuePacket currentPacket = getQueue().get(i);
QueuePacket currentPacket = iter.next();
if(currentPacket.getSender().equals(dev.getIpAddress()) && currentPacket.getValue().length() >= "6864002A716100ACCF2383BEE6202020202020E6BE8323CFAC202020202020534F43303035ADB029DA00".length())
{
log("Found my packet.", 5);
currentPacket.takeFromQueue();
String macAddress = currentPacket.getValue().substring(14, 14+12);
if(dev.getMacAddress().equalsIgnoreCase(macAddress))
return;
// Compensate for the packet being taken from the queue
// i--;
// queueSize--;
}
// else
// Miscellaneous.logEvent("Not my packet.", 5);
}
currentSleepTime += sleepStep;
}
while(currentSleepTime <= maxSleepTime);
log("Expected packet not received.", 4);
}
catch(Exception e)
{
e.printStackTrace();
return;
}
// finally
// {
// try
// {
// datagramSendSocket.close();
// }
// catch(Exception e)
// {}
// }
}
private static String get2(DeviceS20 dev) throws InterruptedException, IOException
{
log("get2() for " + dev.toString(), 5);
// Send this as command 2:
// 68:64:00:1e:63:6c:ac:cf:23:83:42:ee:20:20:20:20:20:20:ee:42:83:23:cf:ac:20:20:20:20:20:20
// Example response2: 6864001e636caccf238342ee202020202020ee428323cfac202020202020
String discoverCommand2 = magicKey + "001e636c" + dev.getMacAddress() + "202020202020" + dev.getReverseMacAddress() + "202020202020";
byte[] buffer2 = hexStringToByteArray(discoverCommand2);
DatagramPacket packet = new DatagramPacket(buffer2, buffer2.length, dev.getIpAddress(), s20Port);
// DatagramSocket datagramSendSocket = new DatagramSocket();
// datagramSendSocket.send(packet);
getDatagramSendSocket().send(packet);
Thread.sleep(500); // just to be on the save side
// datagramSendSocket.close();
return null;
/*
// response 2:
// 68:64:00:2a:71:61:00:ac:cf:23:83:42:ee:20:20:20:20:20:20:ee:42:83:23:cf:ac:20:20:20:20:20:20:53:4f:43:30:30:35:c1:0e:61:da:00
// 6864002a716100accf238342ee202020202020ee428323cfac202020202020534f43303035c10e61da00; //Windows-Programm
// 68640018636CACCF238342EE202020202020000000000000AC202020202020534F43303035937173DA00 //Meins
// 68:64:00:2a:71:61:00:ac:cf:23:83:42:ee:20:20:20:20:20:20:ee:42:83:23:cf:ac:20:20:20:20:20:20:53:4f:43:30:30:35:e6:d5:64:da:00
String expectedResult = magicKey + "[a-zA-Z0-9]{10}" + dev.getMacAddress().toLowerCase() + "202020202020" + dev.getReverseMacAddress() + "202020202020" + "[a-zA-Z0-9]{12}";
int currentSleepTime = 0;
do
{
Thread.sleep(sleepStep);
for(QueuePacket onePacket : getQueue())
{
// log("Checking packet...");
if(onePacket.getSender().equals(dev.getIpAddress()))
{
if(onePacket.getValue().toLowerCase().matches(expectedResult.toLowerCase()))
{
log("Found packet...");
datagramSendSocket.close();
return onePacket.getValue();
}
}
}
// log("Done with packet check round...");
currentSleepTime += sleepStep;
// log("waiting loop");
}
while(currentSleepTime <= maxSleepTime);
log("Couldn't find packet...");
datagramSendSocket.close();
return expectedResult;
*/
}
private static String get3(DeviceS20 respondedDevice) throws IOException, InterruptedException
{
log("get3() for " + respondedDevice.toString(), 5);
// command 3: 68:64:00:1e:63:6c:ac:cf:23:83:42:ee:20:20:20:20:20:20:ee:42:83:23:cf:ac:20:20:20:20:20:20
// 6864001e636caccf238342ee202020202020ee428323cfac202020202020
String command3 = magicKey + "001e636c" + respondedDevice.getMacAddress() + "202020202020" + respondedDevice.getReverseMacAddress() + "202020202020";
byte[] buffer = hexStringToByteArray(command3);
DatagramPacket packet = new DatagramPacket(buffer, buffer.length, respondedDevice.getIpAddress(), s20Port);
// DatagramSocket datagramSendSocket = new DatagramSocket();
// datagramSendSocket.send(packet);
getDatagramSendSocket().send(packet);
int currentSleepTime = 0;
do
{
Thread.sleep(sleepStep);
Iterator iter = getQueue().iterator();
while(iter.hasNext())
// for(QueuePacket response3 : getQueue())
{
QueuePacket response3 = iter.next();
if(response3.getSender().equals(respondedDevice.getIpAddress()))
{
// if(response3.getValue().toLowerCase().startsWith(expectedResult.toLowerCase()))
// {
// datagramSendSocket.close();
return null;
}
}
currentSleepTime += sleepStep;
}
while(currentSleepTime <= maxSleepTime);
// datagramSendSocket.close();
return command3;
}
private static String get4(DeviceS20 respondedDevice) throws IOException, InterruptedException
{
log("get4() for " + respondedDevice.toString(), 5);
// 68:64:00:1e:63:6c:ac:cf:23:83:42:ee:20:20:20:20:20:20:ee:42:83:23:cf:ac:20:20:20:20:20:20
// 6864001e636caccf238342ee202020202020ee428323cfac202020202020
String command4 = magicKey + "001e636c" + respondedDevice.getMacAddress() + "202020202020" + respondedDevice.getReverseMacAddress() + "202020202020";
byte[] buffer = hexStringToByteArray(command4);
DatagramPacket packet = new DatagramPacket(buffer, buffer.length, respondedDevice.getIpAddress(), s20Port);
// DatagramSocket datagramSendSocket = new DatagramSocket();
// datagramSendSocket.send(packet);
getDatagramSendSocket().send(packet);
// Example response 4: 68:64:00:2a:71:61:00:ac:cf:23:83:42:ee:20:20:20:20:20:20:ee:42:83:23:cf:ac:20:20:20:20:20:20:53:4f:43:30:30:35:e2:1a:61:da:00
// 6864002a716100accf238342ee202020202020ee428323cfac202020202020534f43303035e21a61da00
String expectedResult = magicKey + "002a716100" + respondedDevice.getMacAddress() + "202020202020" + respondedDevice.getReverseMacAddress() + "202020202020";
int currentSleepTime = 0;
do
{
Thread.sleep(sleepStep);
Iterator iter = getQueue().iterator();
while(iter.hasNext())
// for(QueuePacket response4 : getQueue())
{
QueuePacket response4 = iter.next();
if(response4.getSender().equals(respondedDevice.getIpAddress()))
{
if(response4.getValue().toLowerCase().startsWith(expectedResult.toLowerCase()))
{
// datagramSendSocket.close();
return null;
}
}
}
currentSleepTime += sleepStep;
}
while(currentSleepTime <= maxSleepTime);
// datagramSendSocket.close();
return expectedResult;
}
private static String get5(DeviceS20 respondedDevice) throws InterruptedException, IOException
{
log("get5() for " + respondedDevice.toString(), 5);
// 68:64:00:1e:63:6c:ac:cf:23:83:42:ee:20:20:20:20:20:20:ee:42:83:23:cf:ac:20:20:20:20:20:20
// 6864001e636caccf238342ee202020202020ee428323cfac202020202020
String command5 = magicKey + "001e636c" + respondedDevice.getMacAddress() + "202020202020" + respondedDevice.getReverseMacAddress() + "202020202020";
byte[] buffer = hexStringToByteArray(command5);
DatagramPacket packet = new DatagramPacket(buffer, buffer.length, respondedDevice.getIpAddress(), s20Port);
// DatagramSocket datagramSendSocket = new DatagramSocket();
// datagramSendSocket.send(packet);
getDatagramSendSocket().send(packet);
// Response 5: 68:64:00:2a:71:61:00:ac:cf:23:83:42:ee:20:20:20:20:20:20:ee:42:83:23:cf:ac:20:20:20:20:20:20:53:4f:43:30:30:35:e2:1a:61:da:00
// 6864002a716100accf238342ee202020202020ee428323cfac202020202020534f43303035e21a61da00
String expectedResult = magicKey + "002a716100" + respondedDevice.getMacAddress() + "202020202020" + respondedDevice.getReverseMacAddress() + "202020202020";
int currentSleepTime = 0;
do
{
Thread.sleep(sleepStep);
Iterator iter = getQueue().iterator();
while(iter.hasNext())
// for(QueuePacket response5 : getQueue())
{
QueuePacket response5 = iter.next();
if(response5.getSender().equals(respondedDevice.getIpAddress()))
{
if(response5.getValue().toLowerCase().startsWith(expectedResult.toLowerCase()))
{
// datagramSendSocket.close();
return null;
}
}
}
currentSleepTime += sleepStep;
}
while(currentSleepTime <= maxSleepTime);
// datagramSendSocket.close();
return expectedResult;
}
private static String get6(DeviceS20 respondedDevice) throws IOException, InterruptedException
{
log("get6() for " + respondedDevice.toString(), 5);
// Command 5: 68:64:00:1d:72:74:ac:cf:23:83:42:ee:20:20:20:20:20:20:72:00:00:00:01:00:00:00:00:00:00
// 6864001d7274accf238342ee2020202020207200000001000000000000
String command6 = magicKey + "001d7274" + respondedDevice.getMacAddress() + "2020202020207200000001000000000000";
byte[] buffer = hexStringToByteArray(command6);
DatagramPacket packet = new DatagramPacket(buffer, buffer.length, respondedDevice.getIpAddress(), s20Port);
// DatagramSocket datagramSendSocket = new DatagramSocket();
// datagramSendSocket.send(packet);
getDatagramSendSocket().send(packet);
// Response 6: 68:64:00:24:72:74:ac:cf:23:83:42:ee:20:20:20:20:20:20:01:00:00:00:00:01:00:01:00:00:06:00:04:00:04:00:04:00
// 686400247274accf238342ee202020202020010000000001000100000600040004000400
String expectedResult = magicKey + "00247274" + respondedDevice.getMacAddress() + "202020202020";
int currentSleepTime = 0;
do
{
Thread.sleep(sleepStep);
Iterator iter = getQueue().iterator();
while(iter.hasNext())
// for(QueuePacket response6 : getQueue())
{
QueuePacket response6 = iter.next();
if(response6.getSender().equals(respondedDevice.getIpAddress()))
{
if(response6.getValue().toLowerCase().startsWith(expectedResult.toLowerCase()))
{
// datagramSendSocket.close();
return null;
}
}
}
currentSleepTime += sleepStep;
}
while(currentSleepTime <= maxSleepTime);
// datagramSendSocket.close();
return expectedResult;
}
private static String get7DeviceName(DeviceS20 dev) throws IOException, InterruptedException
{
log("get7DeviceName() for " + dev.toString(), 5);
// 68:64:00:1d:72:74:ac:cf:23:83:42:ee:20:20:20:20:20:20:00:00:00:00:04:00:00:00:00:00:00
// 6864001d7274accf238342ee2020202020200000000004000000000000
String command7 = magicKey + "001d7274" + dev.getMacAddress() + "2020202020200000000004000000000000";
byte[] buffer = hexStringToByteArray(command7);
DatagramPacket packet = new DatagramPacket(buffer, buffer.length, dev.getIpAddress(), s20Port);
// DatagramSocket datagramSendSocket = new DatagramSocket();
// datagramSendSocket.send(packet);
getDatagramSendSocket().send(packet);
Thread.sleep(maxSleepTime);
// 68:64:00:dc:72:74:ac:cf:23:83:42:ee:20:20:20:20:20:20:01:00:00:00:00:04:00:01:00:00:be:00:01:00:43:25:ac:cf:23:83:42:ee:20:20:20:20:20:20:ee:42:83:23:cf:ac:20:20:20:20:20:20:38:38:38:38:38:38:20:20:20:20:20:20:4c:69:63:68:74:65:72:6b:65:74:74:65:20:20:20:20:04:00:20:00:00:00:1a:00:00:00:05:00:00:00:10:27:34:1c:19:ff:10:27:76:69:63:65:6e:74:65:72:2e:6f:72:76:69:62:6f:2e:63:6f:6d:20:20:20:20:20:20:20:20:20:20:20:20:20:20:20:20:20:20:20:20:20:c0:a8:01:c8:c0:a8:01:01:ff:ff:ff:00:01:01:00:01:00:ff:00:00:00:00:00:00:00:00:00:00:00:00:00:00:30:30:30:30:30:30:30:30:30:30:30:30:30:30:30:30:30:30:30:30:30:30:30:30:30:30:30:30:30:30:30:30:30:30:30:30:30:30:30:30
// 686400dc7274accf238342ee20202020202001000000000400010000be0001004325accf238342ee202020202020ee428323cfac2020202020203838383838382020202020204c6963687465726b65747465202020200400200000001a000000050000001027341c19ff1027766963656e7465722e6f727669626f2e636f6d202020202020202020202020202020202020202020c0a801c8c0a80101ffffff000101000100ff000000000000000000000000000030303030303030303030303030303030303030303030303030303030303030303030303030303030
String expectedResult = magicKey + "00dc7274" + dev.getMacAddress() + "202020202020" + "[a-zA-Z0-9]{32}" + dev.getMacAddress() + "202020202020" + dev.getReverseMacAddress() + "202020202020383838383838202020202020";
// expectedResult = expectedResult.toLowerCase();
int currentSleepTime = 0;
do
{
Thread.sleep(sleepStep);
Iterator iter = getQueue().iterator();
while(iter.hasNext())
// for(QueuePacket responsePacket : getQueue())
{
QueuePacket responsePacket = iter.next();
if(responsePacket.getSender().equals(dev.getIpAddress()))
{
log(responsePacket.getValue().toLowerCase(), 5);
Pattern p = Pattern.compile("^" + expectedResult + "*");
Matcher m = p.matcher(responsePacket.getValue().toLowerCase());
if(m.find())
{
int pos1 = "686400dc7274accf238342ee20202020202001000000000400010000be0001004325accf238342ee202020202020ee428323cfac202020202020383838383838202020202020".length();
int pos2 = responsePacket.getValue().indexOf("20202020", pos1);
String deviceNameHex = responsePacket.getValue().substring(pos1, pos2);
String converted = convertHexToString(deviceNameHex);
log("Found device name: " + deviceNameHex + ", converted: " + converted, 5);
// dev.setName(converted);
// datagramSendSocket.close();
return converted;
}
}
}
currentSleepTime += sleepStep;
}
while(currentSleepTime <= maxSleepTime);
// datagramSendSocket.close();
return expectedResult;
}
private static void get8(DeviceS20 respondedDevice) throws InterruptedException, IOException
{
log("get8() for " + respondedDevice.toString(), 5);
// 68:64:00:1d:72:74:ac:cf:23:83:42:ee:20:20:20:20:20:20:00:00:00:00:03:00:00:00:00:00:00
// 6864001d7274accf238342ee2020202020200000000003000000000000
String command8 = magicKey + "001d7274" + respondedDevice.getMacAddress() + "2020202020200000000004000000000000";
byte[] buffer = hexStringToByteArray(command8);
DatagramPacket packet = new DatagramPacket(buffer, buffer.length, respondedDevice.getIpAddress(), s20Port);
// DatagramSocket datagramSendSocket = new DatagramSocket();
// datagramSendSocket.send(packet);
getDatagramSendSocket().send(packet);
// 68:64:00:1c:72:74:ac:cf:23:83:42:ee:20:20:20:20:20:20:01:00:00:00:00:03:00:01:00:00
// 6864001c7274accf238342ee20202020202001000000000300010000
String expectedResult = magicKey + "001c7274" + respondedDevice.getMacAddress() + "20202020202001000000000300010000";
int currentSleepTime = 0;
do
{
Thread.sleep(sleepStep);
Iterator iter = getQueue().iterator();
while(iter.hasNext())
// for(QueuePacket response8 : getQueue())
{
QueuePacket response8 = iter.next();
if(response8.getSender().equals(respondedDevice.getIpAddress()))
{
if(response8.getValue().toLowerCase().startsWith(expectedResult.toLowerCase()))
{
log("Query complete for device.", 4);
}
}
}
currentSleepTime += sleepStep;
}
while(currentSleepTime <= maxSleepTime);
// datagramSendSocket.close();
}
protected void prepForSwitchCommand() throws IOException, InterruptedException
{
log("prepForSwitchCommand()", 5);
if(Calendar.getInstance().getTimeInMillis() - ((long)querySwitchTollerance)*1000 <= timestampLastDiscovery)
{
log("still in tollerance", 5);
return;
}
else
{
log("not in tollerance", 5);
try
{
get1(this);
}
catch (Exception e)
{
// give a shit
}
log("get1() finish", 5);
Thread.sleep(300);
try
{
get2(this);
}
catch (Exception e)
{
// give a shit
}
Thread.sleep(300);
log("get2() finish", 5);
}
}
public boolean switchDeviceOrRoom(boolean desiredState, boolean useForce, boolean shouldCommandBeRelayed)
{
String desStateString;
if(desiredState)
desStateString = "on";
else
desStateString = "off";
for(int i = 1; i <= switchAttempts; i++)
{
verifyListenerIsRunning();
try
{
prepForSwitchCommand();
// Thread.sleep(3000);
}
catch (IOException e1)
{
e1.printStackTrace();
}
catch (InterruptedException e1)
{
e1.printStackTrace();
}
// String macAddress = "accf2383bee6";
String macAddress = this.getMacAddress();
macAddress = macAddress.toLowerCase();
log("Switching device " + macAddress + " " + desStateString, 3);
StringBuilder commandBuilder = new StringBuilder();
commandBuilder.append(magicKey);
commandBuilder.append("00"); //length, always seems to be zeroes
commandBuilder.append("176463");
commandBuilder.append(macAddress); //mac
commandBuilder.append("202020202020"); //mac padding
// commandBuilder.append(new StringBuilder(macAddress).reverse().toString());
// commandBuilder.append("202020202020"); //reverse mac padding
commandBuilder.append("000000000");
commandBuilder.append(String.valueOf(boolToInt(desiredState)));
DatagramSocket datagramSendSocket = null;
log("Sending switch packet with content \"" + commandBuilder.toString() + "\" to socket " + this.getIpAddress().getHostAddress(), 4);
try
{
byte[] buffer = hexStringToByteArray(commandBuilder.toString());
DatagramPacket packet = new DatagramPacket(buffer, buffer.length, this.getIpAddress(), s20Port);
datagramSendSocket = new DatagramSocket();
datagramSendSocket.send(packet);
try
{
datagramSendSocket.close();
}
catch(Exception e)
{}
// 68:64:00:17:64:63:ac:cf:23:83:42:ee:20:20:20:20:20:20:01:00:00:00:00
// 68:64:00:17:73:66:ac:cf:23:83:42:ee:20:20:20:20:20:20:00:00:00:00:01
// 68:64:00:17:73:66:ac:cf:23:83:42:ee:20:20:20:20:20:20:00:00:00:00:01
/*
* Wait for the response to come, however we don't give a fuck as the
* response comes several times with different states.
*/
// Example: 686400177366accf238342ee2020202020200000000001
String expectedResult = magicKey + "00177366" + this.getMacAddress() + "202020202020" + "000000000";
if(desiredState)
expectedResult += "1";
else
expectedResult += "0";
log("Waiting for confirmation packet...", 4);
int currentSleepTime = 0;
do
{
Thread.sleep(sleepStep);
Iterator iter = getQueue().iterator();
while(iter.hasNext())
// for(QueuePacket onePacket : getQueue())
{
QueuePacket onePacket = iter.next();
if(onePacket.getSender().equals(ipAddress))
{
if(onePacket.getValue().toLowerCase().startsWith(expectedResult))
{
log("Device switched successfully.", 3);
this.setState(desiredState);
return true;
}
}
}
/*for(QueuePacket onePacket : getQueue())
{
if(onePacket.getSender().equals(ipAddress))
{
if(onePacket.getValue().toLowerCase().startsWith(expectedResult))
{
log("Device switched successfully.", 3);
this.setState(desiredState);
if(!Settings.serverRelay | shouldCommandBeRelayed) // Either relaying is inactive or this node is the one relaying
saveState();
return true;
}
}
}*/
currentSleepTime += sleepStep;
}
while(currentSleepTime <= maxSleepTime);
log("Confirmation packet not received.", 4);
log("Forcibly checking status to compensate for missing confirmation packet.", 4);
this.prepForSwitchCommand();
if(this.getState() == desiredState) // We didn't get a confirmation packet, but switch worked.
{
return true;
}
}
catch(Exception e)
{
log("Error switching device:", 3);
e.printStackTrace();
return false;
}
log("Device " + macAddress + " could not be switched " + desStateString + ". This was attempt " + String.valueOf(i) + " / " + String.valueOf(switchAttempts), 4);
}
return false;
}
public static byte[] hexStringToByteArray(String s)
{
return DatatypeConverter.parseHexBinary(s);
}
public static String bytesToHex(byte[] bytes, int bundleSize)
{
String packetMessage = javax.xml.bind.DatatypeConverter.printHexBinary(bytes);
int beginOfZeroes;
for(beginOfZeroes = packetMessage.length() -1 ; beginOfZeroes >= 0; beginOfZeroes--)
{
if(!String.valueOf(packetMessage.charAt(beginOfZeroes)).equals("0"))
{
beginOfZeroes++;
break;
}
}
return packetMessage.substring(0, beginOfZeroes+2);
}
public static String convertHexToString(String hex)
{
StringBuilder sb = new StringBuilder();
StringBuilder temp = new StringBuilder();
//49204c6f7665204a617661 split into two characters 49, 20, 4c...
for( int i=0; i {
//grab the hex in pairs
String output = hex.substring(i, (i + 2));
//convert hex to decimal
int decimal = Integer.parseInt(output, 16);
//convert the decimal to character
sb.append((char)decimal);
temp.append(decimal);
}
log("Decimal : " + temp.toString(), 5);
return sb.toString();
}
/*
* SUPER:
* id
* name
* room
* state
* unitcode
* */
public static int boolToInt(boolean in)
{
if(in)
return 1;
else
return 0;
}
public static class Listener implements Runnable
{
static TimerTask queueCleaner;
static Timer queueCleanerTimer;
static boolean queueCleanerRunning = false;
static HashSet listOfBroadcasts;
static HashSet listOfOwnAddresses;
public static Timer getQueueCleanerTimer()
{
return queueCleanerTimer;
}
protected synchronized static void startQueueCleaner()
{
if(!queueCleanerRunning && queue.size() > 0)
{
queueCleaner = new CleanUpTask();
queueCleanerTimer = new Timer();
log("Starting queue cleanup background task.", 5);
queueCleanerTimer.scheduleAtFixedRate(queueCleaner, 1000, (long)queueTTL * 1500);
queueCleanerRunning = true;
}
}
protected void discoverBroadcastAddresses()
{
listOfBroadcasts = new HashSet();
listOfOwnAddresses = new HashSet();
Enumeration list;
try
{
list = NetworkInterface.getNetworkInterfaces();
while(list.hasMoreElements())
{
NetworkInterface iface = (NetworkInterface) list.nextElement();
if(iface == null)
continue;
if(!iface.isLoopback() && iface.isUp())
{
Iterator it = iface.getInterfaceAddresses().iterator();
while (it.hasNext())
{
InterfaceAddress address = (InterfaceAddress) it.next();
//log("Found address: " + address);
if(address == null)
continue;
InetAddress broadcast = address.getBroadcast();
if(broadcast != null)
{
// log("Found broadcast: " + broadcast);
listOfBroadcasts.add(broadcast);
}
InetAddress ownAddress = address.getAddress();
if(ownAddress != null)
{
// log("Found broadcast: " + broadcast);
listOfOwnAddresses.add(ownAddress);
}
}
}
}
}
catch (SocketException ex)
{
System.err.println("Error while getting network interfaces");
ex.printStackTrace();
}
}
protected synchronized static void stopQueueCleaner()
{
if(queueCleanerRunning)
{
log("Stopping queue cleanup background task.", 5);
getQueueCleanerTimer().cancel();
queueCleanerRunning = false;
}
}
@Override
public void run()
{
log("Starting UDP listener on port " + String.valueOf(s20Port) + "...", 3);
if(listOfBroadcasts == null)
discoverBroadcastAddresses();
try
{
DatagramSocket datagramReceiveSocket = null;
byte[] buffer = new byte[100];
DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
datagramReceiveSocket = new DatagramSocket(s20Port);
listenerRunning = true;
while(!stopRequested)
{
// log("Waiting for package...", 5);
datagramReceiveSocket.receive(packet);
for (InetAddress ownAddress : listOfOwnAddresses)
{
if(ownAddress.getAddress().equals(packet.getAddress().getAddress()))
{
break;
}
}
boolean foundBroadcastPacket = false;
for (InetAddress broadcastAddress : listOfBroadcasts)
{
if(broadcastAddress.getAddress().equals(packet.getAddress().getAddress()))
{
foundBroadcastPacket = true;
break;
}
}
if(!foundBroadcastPacket && !packet.getAddress().getHostAddress().endsWith("255"))
{
byte[] result = packet.getData();
String message = bytesToHex(result, packet.getLength());
if(message.startsWith(magicKey))
{
QueuePacket queuePacket = new QueuePacket();
queuePacket.setTimestamp(Calendar.getInstance());
queuePacket.setValue(extractRelevantPart(message));
queuePacket.setSender(packet.getSocketAddress().toString());
// Example for SocketAddress:
// /192.168.xxx.xxx:10000
queuePacket.setSender(packet.getAddress());
getQueue().add(queuePacket);
startQueueCleaner();
log("Received, Num-Elements " + String.valueOf(getQueue().size()) + ", Origin: " + queuePacket.getSender().getHostName() + ", Content: " + message, 5);
}
else
log("Received invalid packet. Dropping this:" + message, 5);
}
}
datagramReceiveSocket.close();
}
catch(SocketException e)
{
e.printStackTrace();
}
catch (IOException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
finally
{
log("Exiting UDP listener on port " + String.valueOf(s20Port) + "...", 3);
listenerRunning = false;
}
}
static class CleanUpTask extends TimerTask
{
@Override
public void run()
{
if(queue.size() > 0)
cleanUpQueue();
}
void cleanUpQueue()
{
log("Running queue cleanup sweep. Amount of queue elements now: " + String.valueOf(getQueue().size()), 5);
Calendar now = Calendar.getInstance();
Iterator iter = getQueue().iterator();
while(iter.hasNext())
// int queueSize = queue.size();
// for(int i=0; i < queueSize; i++)
{
QueuePacket currentPacket = iter.next();
if(currentPacket.getTimestamp().getTimeInMillis() < (now.getTimeInMillis() - ((long)queueTTL * 1000)))
{
log("Packet " + currentPacket.getValue() + " was not used. It will be removed from the incoming queue.", 5);
queue.remove(currentPacket);
}
// Compensate for the packet being taken from the queue
// i--;
// queueSize--;
}
log("Queue cleanup sweep finished. Amount of queue elements now: " + String.valueOf(getQueue().size()), 5);
if(getQueue().size() == 0)
{
log("Queue empty.", 5);
stopQueueCleaner();
}
}
}
}
/*
* Discovery
* Broadcast Paket an 255.255.255.255
* UDP, Source 52802, Destination Port 10000
* 68:64:00:06:71:61 variiert nicht bei weiteren Aufrufen
* ----- Magic Key
* -- Message length
*
* Absender MAC in dem Fall: 00-25-22-D2-42-CD
*
* Antwort:
* 68:64:00:2a:71:61:00:ac:cf:23:83:be:e6:20:20:20:20:20:20:e6:be:83:23:cf:ac:20:20:20:20:20:20:53:4f:43:30:30:35:e6:f5:28:da:00
* ----- Magic Key
* -- Message length
* ----------- ?
* ------------------ MAC address
* ----------------- MAC padding
* ------------------ reverse mac
* mac padding -----------------
* UDP, Source: 10000, Destination 10000
*
*
*
*
* Port ist immer 10000
* Standard Passwort: 888888
*
* 68:64:00:1e:63:6c:ac:cf:23:83:be:e6:20:20:20:20:20:20:e6:be:83:23:cf:ac:20:20:20:20:20:20
* ----------------- MAC
* ----------------- MAC padding
* ------------------ reverse mac
* ----------------- mac padding
* Magic Key is 68 64 (hexadecimals) and is used to distinguish these UDP packets
* from any other packets that are send over UDP port 10000
*
* Auschalten:
* 68:64:00:17:64:63:ac:cf:23:83:be:e6:20:20:20:20:20:20:00:00:00:00:00
* Einschalten:
* 68:64:00:17:64:63:ac:cf:23:83:be:e6:20:20:20:20:20:20:00:00:00:00:01
*
* 192.168.198.29 ac-cf-23-83-be-e6 dynamic
*
* 68:64:00:1e:63:6c:ac:cf:23:83:be:e6:20:20:20:20:20:20:e6:be:83:23:cf:ac:20:20:20:20:20:20
* ----- Magic Key
* -- Message length
* -------- ?
*
* ------------------ MAC address
* ----------------- MAC padding
* ------------------ reverse mac
* mac padding -----------------
*
*
*
* MAC address ac:cf:23:83:be:e6
*
*
*
*
*
*
*
*
*
*
*/
public static class QueuePacket extends Observable
{
Calendar timestamp;
InetAddress sender;
String value;
public Calendar getTimestamp()
{
return timestamp;
}
public void setTimestamp(Calendar timestamp)
{
this.timestamp = timestamp;
}
public String getValue()
{
return value;
}
public void setValue(String value)
{
this.value = value;
}
public InetAddress getSender()
{
return sender;
}
public void setSender(InetAddress sender)
{
this.sender = sender;
}
public void setSender(String sender)
{
// StringBuilder stripped = new StringBuilder();
//
// for(int i = 0; i < sender.length(); i++)
// {
// if(sender.charAt(i). .matches("^[a-zA-Z0-9]+$"))
// stripped.append(sender.charAt(i));
// }
//
// this.setSender(InetAddress.getByName(stripped.toString()));
InetAddress address = null;
try
{
// String temp = sender.substring(1, sender.lastIndexOf(':'));
address = InetAddress.getByName(sender.substring(1, sender.lastIndexOf(':')));
}
catch(UnknownHostException e)
{}
this.setSender(address);
}
public synchronized void takeFromQueue()
{
// log("Packet about to be taken from queue. Current size: " + String.valueOf(queue.size()));
getQueue().remove(this);
// log("Packet taken from queue. New size: " + String.valueOf(queue.size()));
}
}
static String extractRelevantPart(String message)
{
return message;
/*int pos1 = 0;
int pos2 = 0;
if(message.contains(" "))
pos2 = message.indexOf(' ') - 1;
for(int i=0; i < message.length(); i++)
{
if(!Character.toString(message.charAt(i)).matches("^[a-zA-Z0-9]+$"))
{
pos2 = i;
break;
}
}
return message.substring(pos1, pos2);*/
}
static void verifyListenerIsRunning()
{
log("verifyListenerIsRunning()", 5);
if(!listenerRunning)
{
/*
* This used to be a global var. Testing local if consuming less memory.
*/
Listener listener = new Listener();
listenerThread = new Thread(listener);
listenerThread.setName("Thread-listenerThread");
listenerThread.start();
while(!listenerRunning)
{
try
{
log("Waiting for listener to start...", 4);
Thread.sleep(10);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
log("Listener started.", 3);
}
log("verifyListenerIsRunning() finished", 5);
}
static void stopListener()
{
if(listenerRunning)
{
try
{
Thread.sleep(5000);
log("Killing listener...", 4);
Listener.stopQueueCleaner();
listenerThread.interrupt();
if(listenerThread.isInterrupted())
log("Listener killed.", 4);
}
catch(Exception e)
{
// log("Error killing listener thread.");
}
}
}
@Override
public String toString()
{
return "Device responded - IP address: " + this.getIpAddress().getHostAddress() + ", MAC address: " + this.getMacAddress() + ", Name: " + getName() + ", State: " + getStringForBoolean(state);
}
static void log(String text, int logLevel)
{
System.out.println(text);
}
public static String getStringForBoolean(boolean state)
{
if(state)
return "on";
else
return "off";
}
}