Last active
December 7, 2024 14:49
-
-
Save agent4788/81beb25cdcdbf7e9371361ca87d3b04a to your computer and use it in GitHub Desktop.
Mit dieser kleinen API kann man die TP-Link HS100 und HS110 Wlan Steckdosen direkt aus Java ansteuern.
This file contains hidden or 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 de.test; | |
import com.google.gson.JsonElement; | |
import com.google.gson.JsonObject; | |
import com.google.gson.JsonParser; | |
import java.io.IOException; | |
import java.io.InputStream; | |
import java.io.OutputStream; | |
import java.net.InetAddress; | |
import java.net.Socket; | |
import java.nio.ByteBuffer; | |
import java.util.HashMap; | |
import java.util.Map; | |
/** | |
* TP-Link HS100 | |
* | |
* @author Oliver Kleditzsch | |
* @copyright Copyright (c) 2016, Oliver Kleditzsch | |
*/ | |
public class HS100 { | |
public static final String COMMAND_SWITCH_ON = "{\"system\":{\"set_relay_state\":{\"state\":1}}}}"; | |
public static final String COMMAND_SWITCH_OFF = "{\"system\":{\"set_relay_state\":{\"state\":0}}}}"; | |
public static final String COMMAND_INFO = "{\"system\":{\"get_sysinfo\":null}}"; | |
/** | |
* Status an | |
*/ | |
public static final int STATE_ON = 1; | |
/** | |
* Status aus | |
*/ | |
public static final int STATE_OFF = 2; | |
/** | |
* IP Adresse der Steckdose | |
*/ | |
private String ip; | |
/** | |
* Port | |
*/ | |
private int port = 9999; | |
/** | |
* @param ip IP Adresse | |
*/ | |
public HS100(String ip) { | |
this.ip = ip; | |
} | |
/** | |
* @param ip IP Adresse | |
* @param port TCP Port Nummer | |
*/ | |
public HS100(String ip, int port) { | |
this.ip = ip; | |
this.port = port; | |
} | |
/** | |
* gibt die IP Adresse der Steckdose zurück | |
* | |
* @return IP Adresse | |
*/ | |
public String getIp() { | |
return ip; | |
} | |
/** | |
* setzt die IP Adresse der Steckdose | |
* | |
* @param ip IP Adresse | |
*/ | |
public void setIp(String ip) { | |
this.ip = ip; | |
} | |
/** | |
* gibt den Port der Steckdose zurück | |
* | |
* @return Port | |
*/ | |
public int getPort() { | |
return port; | |
} | |
/** | |
* setzt den Port der Steckdose | |
* | |
* @param port Port | |
*/ | |
public void setPort(int port) { | |
this.port = port; | |
} | |
/** | |
* prüft ob die Steckdose im Netzwerk erreichbar ist | |
* | |
* @return Erreichbarkeit | |
*/ | |
public boolean isPresent() { | |
try { | |
InetAddress ip = InetAddress.getByName(getIp()); | |
return ip.isReachable(500); | |
} catch (IOException ex) {} | |
return false; | |
} | |
/** | |
* prüft ob die Steckdose im Netzwerk erreichbar ist | |
* | |
* @param timeout Timeout (Wartezeit in ms) | |
* @return Erreichbarkeit | |
*/ | |
public boolean isPresent(int timeout) { | |
try { | |
InetAddress ip = InetAddress.getByName(getIp()); | |
return ip.isReachable(timeout); | |
} catch (IOException ex) {} | |
return false; | |
} | |
/** | |
* sendet einen Einschaltbefehl | |
* | |
* @return true bei Erfolg | |
*/ | |
public boolean switchOn() throws IOException { | |
String jsonData = sendCommand(COMMAND_SWITCH_ON); | |
if(jsonData.length() > 0) { | |
JsonObject jo = new JsonParser().parse(jsonData).getAsJsonObject(); | |
int errorCode = jo.get("system").getAsJsonObject().get("set_relay_state").getAsJsonObject().get("err_code").getAsInt(); | |
return errorCode == 0; | |
} | |
return false; | |
} | |
/** | |
* sendet einen Ausschaltbefehl | |
* | |
* @return true bei Erfolg | |
*/ | |
public boolean switchOff() throws IOException { | |
String jsonData = sendCommand(COMMAND_SWITCH_OFF); | |
if(jsonData.length() > 0) { | |
JsonObject jo = new JsonParser().parse(jsonData).getAsJsonObject(); | |
int errorCode = jo.get("system").getAsJsonObject().get("set_relay_state").getAsJsonObject().get("err_code").getAsInt(); | |
return errorCode == 0; | |
} | |
return false; | |
} | |
/** | |
* fragt den aktuellen Status der Steckodse ab | |
* | |
* @return STATE_ON oder STATE_OFF | |
*/ | |
public int readState() throws IOException { | |
String jsonData = sendCommand(COMMAND_INFO); | |
if(jsonData.length() > 0) { | |
JsonObject jo = new JsonParser().parse(jsonData).getAsJsonObject(); | |
int state = jo.get("system").getAsJsonObject().get("get_sysinfo").getAsJsonObject().get("relay_state").getAsInt(); | |
return state == 1 ? STATE_ON : STATE_OFF; | |
} | |
return STATE_OFF; | |
} | |
/** | |
* gibt eine Map mit den Systeminformationen der Steckdose zurück | |
* | |
* @return Map mit Systeminformationen | |
*/ | |
public Map<String, String> getInfo() throws IOException { | |
Map<String, String> result = new HashMap<>(); | |
String jsonData = sendCommand(COMMAND_INFO); | |
if(jsonData.length() > 0) { | |
JsonObject jo = new JsonParser().parse(jsonData).getAsJsonObject(); | |
JsonObject systemInfo = jo.get("system").getAsJsonObject().get("get_sysinfo").getAsJsonObject(); | |
for(Map.Entry<String, JsonElement> entry : systemInfo.entrySet()) { | |
result.put(entry.getKey(), entry.getValue().getAsString()); | |
} | |
} | |
return result; | |
} | |
/** | |
* sendet eine Befel an die Steckdose | |
* | |
* @param command Befehl | |
* @return Json STring mit dem Antwortobjekt | |
* @throws IOException | |
*/ | |
protected String sendCommand(String command) throws IOException { | |
Socket socket = new Socket(ip, port); | |
OutputStream outputStream = socket.getOutputStream(); | |
outputStream.write(encryptWithHeader(command)); | |
InputStream inputStream = socket.getInputStream(); | |
String data = decrypt(inputStream); | |
outputStream.close(); | |
inputStream.close(); | |
socket.close(); | |
return data; | |
} | |
private String decrypt(InputStream inputStream) throws IOException { | |
int in; | |
int key = 0x2B; | |
int nextKey; | |
StringBuilder sb = new StringBuilder(); | |
while((in = inputStream.read()) != -1) { | |
nextKey = in; | |
in = in ^ key; | |
key = nextKey; | |
sb.append((char) in); | |
} | |
return "{" + sb.toString().substring(5); | |
} | |
private int[] encrypt(String command) { | |
int[] buffer = new int[command.length()]; | |
int key = 0xAB; | |
for(int i = 0; i < command.length(); i++) { | |
buffer[i] = command.charAt(i) ^ key; | |
key = buffer[i]; | |
} | |
return buffer; | |
} | |
private byte[] encryptWithHeader(String command) { | |
int[] data = encrypt(command); | |
byte[] bufferHeader = ByteBuffer.allocate(4).putInt(command.length()).array(); | |
ByteBuffer byteBuffer = ByteBuffer.allocate(bufferHeader.length + data.length).put(bufferHeader); | |
for(int in : data) { | |
byteBuffer.put((byte) in); | |
} | |
return byteBuffer.array(); | |
} | |
} |
This file contains hidden or 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 de.test; | |
import com.google.gson.JsonObject; | |
import com.google.gson.JsonParser; | |
import java.io.IOException; | |
/** | |
* TP-Link HS100 | |
* | |
* @author Oliver Kleditzsch | |
* @copyright Copyright (c) 2016, Oliver Kleditzsch | |
*/ | |
public class HS110 extends HS100 { | |
private static final String COMMAND_ENERGY = "{\"emeter\":{\"get_realtime\":null}}"; | |
/** | |
* Energiedaten | |
*/ | |
public class EnergyData { | |
/** | |
* aktuelle Spannung in Volt | |
*/ | |
private double nowVoltage; | |
/** | |
* aktueller Strom in Ampere | |
*/ | |
private double nowCurrent; | |
/** | |
* aktuelle Leistung in Watt | |
*/ | |
private double nowPower; | |
/** | |
* Energieverbrauch in Kilo Watt Stunden | |
*/ | |
private double energy; | |
public EnergyData() { | |
} | |
/** | |
* @param nowVoltage aktuelle Spannung in Volt | |
* @param nowCurrent aktueller Strom in Ampere | |
* @param nowPower aktuelle Leistung in Watt | |
* @param energy Energieverbrauch in Kilo Watt Stunden | |
*/ | |
public EnergyData(double nowVoltage, double nowCurrent, double nowPower, double energy) { | |
this.nowVoltage = nowVoltage; | |
this.nowCurrent = nowCurrent; | |
this.nowPower = nowPower; | |
this.energy = energy; | |
} | |
/** | |
* gibt die aktuelle Spannung zurück | |
* | |
* @return aktuelle Spannung in Volt | |
*/ | |
public double getNowVoltage() { | |
return nowVoltage; | |
} | |
/** | |
* setzt die aktuelle Spannung | |
* | |
* @param nowVoltage aktuelle Spannung in Volt | |
*/ | |
public void setNowVoltage(double nowVoltage) { | |
this.nowVoltage = nowVoltage; | |
} | |
/** | |
* gibt den aktuellen Strom zurück | |
* | |
* @return aktueller Strom in Ampere | |
*/ | |
public double getNowCurrent() { | |
return nowCurrent; | |
} | |
/** | |
* setzt den aktuellen Strom | |
* | |
* @param nowCurrent aktueller Strom in Ampere | |
*/ | |
public void setNowCurrent(double nowCurrent) { | |
this.nowCurrent = nowCurrent; | |
} | |
/** | |
* gibt die aktuelle Leistung zurück | |
* | |
* @return aktuelle Leistung in Watt | |
*/ | |
public double getNowPower() { | |
return nowPower; | |
} | |
/** | |
* setzt die aktuelle Leistung | |
* | |
* @param nowPower aktuelle Leistung in Watt | |
*/ | |
public void setNowPower(double nowPower) { | |
this.nowPower = nowPower; | |
} | |
/** | |
* gibt den Energieverbrauch zurück | |
* | |
* @return Energieverbrauch in Kilo Watt Stunden | |
*/ | |
public double getEnergy() { | |
return energy; | |
} | |
/** | |
* setzt den Energieverbrauch | |
* | |
* @param energy Energieverbrauch in Kilo Watt Stunden | |
*/ | |
public void setEnergy(double energy) { | |
this.energy = energy; | |
} | |
} | |
/** | |
* @param ip IP Adresse | |
*/ | |
public HS110(String ip) { | |
super(ip); | |
} | |
/** | |
* @param ip IP Adresse | |
* @param port TCP Port Nummer | |
*/ | |
public HS110(String ip, int port) { | |
super(ip, port); | |
} | |
/** | |
* gint die Energiedaten zurück | |
* | |
* @return Energiedaten | |
* @throws IOException | |
*/ | |
public EnergyData getEnergyData() throws IOException { | |
EnergyData energyData = new EnergyData(); | |
String jsonData = sendCommand(COMMAND_ENERGY); | |
if(jsonData.length() > 0) { | |
JsonObject jo = new JsonParser().parse(jsonData).getAsJsonObject(); | |
JsonObject energyDataJson = jo.get("emeter").getAsJsonObject().get("get_realtime").getAsJsonObject(); | |
energyData.setNowCurrent(energyDataJson.get("current").getAsDouble()); | |
energyData.setNowVoltage(energyDataJson.get("voltage").getAsDouble()); | |
energyData.setNowPower(energyDataJson.get("power").getAsDouble()); | |
energyData.setEnergy(energyDataJson.get("total").getAsDouble()); | |
} | |
return energyData; | |
} | |
} |
This file contains hidden or 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 de.test; | |
/** | |
* TP-Link HS100/HS110 Beispielprogramm | |
* | |
* @author Oliver Kleditzsch | |
* @copyright Copyright (c) 2016, Oliver Kleditzsch | |
*/ | |
import java.io.*; | |
import java.util.Map; | |
public class Test { | |
public static void main(String[] args) throws IOException, InterruptedException { | |
HS110 hs100 = new HS110("192.168.10.20"); | |
System.out.println("Switch On"); | |
hs100.switchOn(); | |
System.out.println("new state: " + (hs100.readState() == HS100.STATE_ON ? "on" : "off")); | |
System.out.println(); | |
Thread.sleep(2000); | |
System.out.println("Switch Off"); | |
hs100.switchOff(); | |
System.out.println("new state: " + (hs100.readState() == HS100.STATE_ON ? "on" : "off")); | |
System.out.println(); | |
Thread.sleep(2000); | |
System.out.println("Switch On"); | |
hs100.switchOn(); | |
System.out.println("new state: " + (hs100.readState() == HS100.STATE_ON ? "on" : "off")); | |
System.out.println(); | |
System.out.println("Energy Data:"); | |
HS110.EnergyData energyData = hs100.getEnergyData(); | |
System.out.println("Voltage: " + energyData.getNowVoltage() + "V"); | |
System.out.println("Current: " + energyData.getNowCurrent() + "A"); | |
System.out.println("Power: " + energyData.getNowPower() + "W"); | |
System.out.println("Energy: " + energyData.getEnergy() + "kWh"); | |
System.out.println(); | |
System.out.println("System Data:"); | |
Map<String, String> sysinfo = hs100.getInfo(); | |
for(Map.Entry<String, String> entry : sysinfo.entrySet()) { | |
System.out.println(entry.getKey() + " -> " + entry.getValue()); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Zumindest switch on/off funktioniert noch mit der aktuellsten Version (bei mir 1.5.6).