From 0e0bbdb567c8a31d28cd497a9f3bd794a98847f5 Mon Sep 17 00:00:00 2001 From: Julian Purse Date: Thu, 14 Jan 2021 12:35:32 +0200 Subject: [PATCH] adding websocket functionality --- .gitignore | 2 + README.md | 44 ++++++ pom.xml | 8 +- .../realzimboguy/ewelink/api/EweLink.java | 70 +++++++++- .../ewelink/api/EweLinkWebSocketClient.java | 59 ++++++++ .../ewelink/api/model/devices/Params.java | 37 +++++ .../ewelink/api/wss/WssLogin.java | 112 +++++++++++++++ .../ewelink/api/wss/WssResponse.java | 12 ++ .../ewelink/api/wss/wssrsp/Config.java | 45 +++++++ .../ewelink/api/wss/wssrsp/WssRspMsg.java | 127 ++++++++++++++++++ .../realzimboguy/ewelink/api/TestCode.java | 82 +++++++++++ src/test/resources/log4j2.xml | 17 +++ 12 files changed, 609 insertions(+), 6 deletions(-) create mode 100644 src/main/java/com/github/realzimboguy/ewelink/api/EweLinkWebSocketClient.java create mode 100644 src/main/java/com/github/realzimboguy/ewelink/api/wss/WssLogin.java create mode 100644 src/main/java/com/github/realzimboguy/ewelink/api/wss/WssResponse.java create mode 100644 src/main/java/com/github/realzimboguy/ewelink/api/wss/wssrsp/Config.java create mode 100644 src/main/java/com/github/realzimboguy/ewelink/api/wss/wssrsp/WssRspMsg.java create mode 100644 src/test/java/com/github/realzimboguy/ewelink/api/TestCode.java create mode 100644 src/test/resources/log4j2.xml diff --git a/.gitignore b/.gitignore index e699ba2..d1edc6d 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,5 @@ target/ src/test/java/com/github/realzimboguy/ewelink/api/TestAppPrivate.java src/test/java/com/github/realzimboguy/ewelink/api/TestApp.java + +.idea/ diff --git a/README.md b/README.md index 43612e8..8ad2533 100644 --- a/README.md +++ b/README.md @@ -43,6 +43,8 @@ You must perform a login before calling any other methods, the methods exposed a public Status setDeviceStatusByName(String name, String status) throws Exception public Status setDeviceStatus(String deviceId, String status) throws Exception + + public void getWebSocket(WssResponse wssResponse) throws Exception sample @@ -64,6 +66,48 @@ sample System.out.println(eweLink.getDevice("10009ce53b")); System.out.println(eweLink.setDeviceStatusByName("Pool Tank","off")); + + eweLink.getWebSocket(new WssResponse() { + + @Override + public void onMessage(String s) { + //if you want the raw json data + System.out.println("on message in test raw:" + s); + + } + + @Override + public void onMessageParsed(WssRspMsg rsp) { + + if (rsp.getError() == null) { + + //normal scenario + StringBuilder sb = new StringBuilder(); + sb.append("Device:").append(rsp.getDeviceid()).append(" - "); + if (rsp.getParams() != null) { + sb.append("Switch:").append(rsp.getParams().getSwitch()).append(" - "); + sb.append("Voltage:").append(rsp.getParams().getVoltage()).append(" - "); + sb.append("Power:").append(rsp.getParams().getPower()).append(" - "); + sb.append("Current:").append(rsp.getParams().getCurrent()).append(" - "); + } + + System.out.println(sb.toString()); + + } else if (rsp.getError() == 0) { + //this is from a login response + System.out.println("login success"); + } else if (rsp.getError() > 0) { + System.out.println("login error:" + rsp.toString()); + } + } + + @Override + public void onError(String error) { + System.out.println("onError in test, this should never be called"); + System.out.println(error); + + } + }); } catch (Exception e) { diff --git a/pom.xml b/pom.xml index 991683e..0cfc069 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ com.github.realzimboguy.ewelink.api ewelink-api-java - 2.0.1-RELEASE + 2.1.0-RELEASE jar ewelink-api-java @@ -78,6 +78,11 @@ log4j-slf4j-impl 2.7 + + org.java-websocket + Java-WebSocket + 1.5.1 + @@ -120,6 +125,7 @@ + org.codehaus.mojo build-helper-maven-plugin diff --git a/src/main/java/com/github/realzimboguy/ewelink/api/EweLink.java b/src/main/java/com/github/realzimboguy/ewelink/api/EweLink.java index 5944dcf..0ad0367 100644 --- a/src/main/java/com/github/realzimboguy/ewelink/api/EweLink.java +++ b/src/main/java/com/github/realzimboguy/ewelink/api/EweLink.java @@ -6,6 +6,8 @@ import com.github.realzimboguy.ewelink.api.model.StatusChange; import com.github.realzimboguy.ewelink.api.model.login.LoginRequest; import com.github.realzimboguy.ewelink.api.model.login.LoginResponse; +import com.github.realzimboguy.ewelink.api.wss.WssLogin; +import com.github.realzimboguy.ewelink.api.wss.WssResponse; import com.google.gson.Gson; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -14,6 +16,7 @@ import javax.crypto.spec.SecretKeySpec; import javax.net.ssl.HttpsURLConnection; import java.io.*; +import java.net.URI; import java.net.URL; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; @@ -25,12 +28,12 @@ public class EweLink { Logger logger = LoggerFactory.getLogger(EweLink.class); - private String region; + private static String region; private String email; private String password; private int activityTimer; private String baseUrl = "https://eu-api.coolkit.cc:8080/api/"; - private static final String APP_ID = "YzfeftUVcZ6twZw1OoVKPRFYTrGEg01Q"; + public static final String APP_ID = "YzfeftUVcZ6twZw1OoVKPRFYTrGEg01Q"; private static final String APP_SECRET = "4G91qSoboqYO4Y0XJ0LPPKIsq8reHdfa"; private static boolean isLoggedIn = false; private static long lastActivity = 0L; @@ -39,8 +42,10 @@ public class EweLink { private static String accessToken; private static String apiKey; + private static WssResponse clientWssResponse; - + private static EweLinkWebSocketClient eweLinkWebSocketClient = null; + private static Thread webSocketMonitorThread = null; Gson gson = new Gson(); public EweLink(String region,String email, String password, int activityTimer) { @@ -50,8 +55,8 @@ public EweLink(String region,String email, String password, int activityTimer) { if (region!= null) { baseUrl = "https://" + region + "-api.coolkit.cc:8080/api/"; } - if (activityTimer == 0) { - activityTimer = 1; + if (activityTimer < 30) { + activityTimer = 30; } this.activityTimer = activityTimer; @@ -133,6 +138,25 @@ public void login() throws Exception{ } + public void getWebSocket(WssResponse wssResponse) throws Exception { + if (!isLoggedIn){ + throw new Exception("Not Logged In, please call login Method"); + } + + eweLinkWebSocketClient = new EweLinkWebSocketClient(new URI("wss://"+ region+"-pconnect3.coolkit.cc:8080/api/ws")); + clientWssResponse = wssResponse; + eweLinkWebSocketClient.setWssResponse(clientWssResponse); + eweLinkWebSocketClient.setWssLogin(gson.toJson(new WssLogin(accessToken,apiKey,APP_ID,Util.getNonce()))); + eweLinkWebSocketClient.connect(); + + if(webSocketMonitorThread == null) { + webSocketMonitorThread = new Thread(new WebSocketMonitor()); + webSocketMonitorThread.start(); + } + + + } + public Devices getDevices() throws Exception { if (!isLoggedIn){ @@ -442,6 +466,42 @@ private static String getAuthMac (String data) throws UnsupportedEncodingExcepti } + public class WebSocketMonitor implements Runnable + { + + Logger logger = LoggerFactory.getLogger(WebSocketMonitor.class); + Gson gson = new Gson(); + + + @Override + public void run() { + + logger.info("Websocket Monitor Thread start"); + + while (true) { + try { + Thread.sleep(30000); + logger.debug("send websocket ping"); + eweLinkWebSocketClient.send("ping"); + + } catch (Exception e) { + logger.error("Error in sening websocket ping:",e); + logger.info("Try reconnect to websocket"); + try { + eweLinkWebSocketClient = new EweLinkWebSocketClient(new URI("wss://"+ region+"-pconnect3.coolkit.cc:8080/api/ws")); + eweLinkWebSocketClient.setWssResponse(clientWssResponse); + eweLinkWebSocketClient.setWssLogin(gson.toJson(new WssLogin(accessToken,apiKey,APP_ID,Util.getNonce()))); + eweLinkWebSocketClient.connect(); + + }catch (Exception c) { + logger.error("Error trying to reconnect:",c); + } + } + } + } + } + + } diff --git a/src/main/java/com/github/realzimboguy/ewelink/api/EweLinkWebSocketClient.java b/src/main/java/com/github/realzimboguy/ewelink/api/EweLinkWebSocketClient.java new file mode 100644 index 0000000..6821ce9 --- /dev/null +++ b/src/main/java/com/github/realzimboguy/ewelink/api/EweLinkWebSocketClient.java @@ -0,0 +1,59 @@ +package com.github.realzimboguy.ewelink.api; + +import com.github.realzimboguy.ewelink.api.wss.WssLogin; +import com.github.realzimboguy.ewelink.api.wss.WssResponse; +import com.github.realzimboguy.ewelink.api.wss.wssrsp.WssRspMsg; +import com.google.gson.Gson; +import org.java_websocket.client.WebSocketClient; +import org.java_websocket.handshake.ServerHandshake; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.net.URI; + +public class EweLinkWebSocketClient extends WebSocketClient { + + Logger logger = LoggerFactory.getLogger(EweLinkWebSocketClient.class); + + private WssResponse wssResponse; + private String wssLogin; + Gson gson = new Gson(); + + public void setWssResponse(WssResponse wssResponse) { + this.wssResponse = wssResponse; + } + + public void setWssLogin(String wssLogin) { + this.wssLogin = wssLogin; + } + + public EweLinkWebSocketClient(URI serverUri) { + super(serverUri); + } + + @Override + public void onOpen(ServerHandshake serverHandshake) { + send(wssLogin); + } + + @Override + public void onMessage(String s) { + if (s!= null && s.equalsIgnoreCase("pong")){ + //swallow this as its just a ping/pong + logger.debug(s); + }else { + wssResponse.onMessage(s); + wssResponse.onMessageParsed(gson.fromJson(s, WssRspMsg.class)); + } + } + + @Override + public void onClose(int i, String s, boolean b) { + logger.warn("WS onCloseCalled, system will self-recover {} {} {}",i,s,b); + } + + @Override + public void onError(Exception e) { + wssResponse.onError(e.getMessage()); + } +} diff --git a/src/main/java/com/github/realzimboguy/ewelink/api/model/devices/Params.java b/src/main/java/com/github/realzimboguy/ewelink/api/model/devices/Params.java index b2a9135..53fc1ca 100644 --- a/src/main/java/com/github/realzimboguy/ewelink/api/model/devices/Params.java +++ b/src/main/java/com/github/realzimboguy/ewelink/api/model/devices/Params.java @@ -1,6 +1,8 @@ package com.github.realzimboguy.ewelink.api.model.devices; +import com.google.gson.annotations.SerializedName; + import java.util.HashMap; import java.util.List; import java.util.Map; @@ -9,6 +11,7 @@ public class Params { private BindInfos bindInfos; private String sledOnline; + @SerializedName("switch") private String _switch; private String power; private String voltage; @@ -269,4 +272,38 @@ public void setAdditionalProperty(String name, Object value) { this.additionalProperties.put(name, value); } + @Override + public String toString() { + return "Params{" + + "bindInfos=" + bindInfos + + ", sledOnline='" + sledOnline + '\'' + + ", _switch='" + _switch + '\'' + + ", power='" + power + '\'' + + ", voltage='" + voltage + '\'' + + ", current='" + current + '\'' + + ", fwVersion='" + fwVersion + '\'' + + ", staMac='" + staMac + '\'' + + ", rssi=" + rssi + + ", init=" + init + + ", alarmType='" + alarmType + '\'' + + ", alarmVValue=" + alarmVValue + + ", alarmCValue=" + alarmCValue + + ", alarmPValue=" + alarmPValue + + ", oneKwh='" + oneKwh + '\'' + + ", uiActive=" + uiActive + + ", timeZone=" + timeZone + + ", version=" + version + + ", startup='" + startup + '\'' + + ", pulse='" + pulse + '\'' + + ", pulseWidth=" + pulseWidth + + ", timers=" + timers + + ", hundredDaysKwh='" + hundredDaysKwh + '\'' + + ", onlyDevice=" + onlyDevice + + ", ssid='" + ssid + '\'' + + ", bssid='" + bssid + '\'' + + ", endTime='" + endTime + '\'' + + ", startTime='" + startTime + '\'' + + ", additionalProperties=" + additionalProperties + + '}'; + } } diff --git a/src/main/java/com/github/realzimboguy/ewelink/api/wss/WssLogin.java b/src/main/java/com/github/realzimboguy/ewelink/api/wss/WssLogin.java new file mode 100644 index 0000000..c1c16db --- /dev/null +++ b/src/main/java/com/github/realzimboguy/ewelink/api/wss/WssLogin.java @@ -0,0 +1,112 @@ +package com.github.realzimboguy.ewelink.api.wss; + +import java.util.Date; + +public class WssLogin { + + private String action = "userOnline"; + private String at; + private String apikey; + private String appid; + private String nonce; + private Long ts; + private String userAgent = "app"; + private Long sequence; + private int version = 8; + + public WssLogin(String at, String apikey, String appid,String nonce) { + this.at = at; + this.apikey = apikey; + this.appid = appid; + this.nonce = nonce; + ts = new Date().getTime() / 1000; + sequence = new Date().getTime(); + } + + public String getAction() { + return action; + } + + public void setAction(String action) { + this.action = action; + } + + public String getAt() { + return at; + } + + public void setAt(String at) { + this.at = at; + } + + public String getApikey() { + return apikey; + } + + public void setApikey(String apikey) { + this.apikey = apikey; + } + + public String getAppid() { + return appid; + } + + public void setAppid(String appid) { + this.appid = appid; + } + + public String getNonce() { + return nonce; + } + + public void setNonce(String nonce) { + this.nonce = nonce; + } + + public Long getTs() { + return ts; + } + + public void setTs(Long ts) { + this.ts = ts; + } + + public String getUserAgent() { + return userAgent; + } + + public void setUserAgent(String userAgent) { + this.userAgent = userAgent; + } + + public Long getSequence() { + return sequence; + } + + public void setSequence(Long sequence) { + this.sequence = sequence; + } + + public int getVersion() { + return version; + } + + public void setVersion(int version) { + this.version = version; + } + + @Override + public String toString() { + return "WssLogin{" + + "action='" + action + '\'' + + ", at='" + at + '\'' + + ", apikey='" + apikey + '\'' + + ", appid='" + appid + '\'' + + ", nonce='" + nonce + '\'' + + ", ts=" + ts + + ", userAgent='" + userAgent + '\'' + + ", sequence=" + sequence + + ", version=" + version + + '}'; + } +} diff --git a/src/main/java/com/github/realzimboguy/ewelink/api/wss/WssResponse.java b/src/main/java/com/github/realzimboguy/ewelink/api/wss/WssResponse.java new file mode 100644 index 0000000..e808389 --- /dev/null +++ b/src/main/java/com/github/realzimboguy/ewelink/api/wss/WssResponse.java @@ -0,0 +1,12 @@ +package com.github.realzimboguy.ewelink.api.wss; + +import com.github.realzimboguy.ewelink.api.wss.wssrsp.WssRspMsg; + +public interface WssResponse { + + void onMessage(String s); + + void onMessageParsed(WssRspMsg rsp); + + void onError(String error); +} diff --git a/src/main/java/com/github/realzimboguy/ewelink/api/wss/wssrsp/Config.java b/src/main/java/com/github/realzimboguy/ewelink/api/wss/wssrsp/Config.java new file mode 100644 index 0000000..8d7c421 --- /dev/null +++ b/src/main/java/com/github/realzimboguy/ewelink/api/wss/wssrsp/Config.java @@ -0,0 +1,45 @@ + +package com.github.realzimboguy.ewelink.api.wss.wssrsp; + +import java.util.HashMap; +import java.util.Map; + +public class Config { + + private Integer hb; + private Integer hbInterval; + private Map additionalProperties = new HashMap(); + + public Integer getHb() { + return hb; + } + + public void setHb(Integer hb) { + this.hb = hb; + } + + public Integer getHbInterval() { + return hbInterval; + } + + public void setHbInterval(Integer hbInterval) { + this.hbInterval = hbInterval; + } + + public Map getAdditionalProperties() { + return this.additionalProperties; + } + + public void setAdditionalProperty(String name, Object value) { + this.additionalProperties.put(name, value); + } + + @Override + public String toString() { + return "Config{" + + "hb=" + hb + + ", hbInterval=" + hbInterval + + ", additionalProperties=" + additionalProperties + + '}'; + } +} diff --git a/src/main/java/com/github/realzimboguy/ewelink/api/wss/wssrsp/WssRspMsg.java b/src/main/java/com/github/realzimboguy/ewelink/api/wss/wssrsp/WssRspMsg.java new file mode 100644 index 0000000..d02378f --- /dev/null +++ b/src/main/java/com/github/realzimboguy/ewelink/api/wss/wssrsp/WssRspMsg.java @@ -0,0 +1,127 @@ + +package com.github.realzimboguy.ewelink.api.wss.wssrsp; + +import com.github.realzimboguy.ewelink.api.model.devices.Params; + +import java.util.HashMap; +import java.util.Map; + +public class WssRspMsg { + + private Integer error; + private String action; + private String deviceid; + private String apikey; + private String userAgent; + private Params params; + private String from; + private Config config; + private String seq; + private String sequence; + private Map additionalProperties = new HashMap(); + + public Integer getError() { + return error; + } + + public void setError(Integer error) { + this.error = error; + } + + public String getAction() { + return action; + } + + public void setAction(String action) { + this.action = action; + } + + public String getDeviceid() { + return deviceid; + } + + public void setDeviceid(String deviceid) { + this.deviceid = deviceid; + } + + public String getApikey() { + return apikey; + } + + public void setApikey(String apikey) { + this.apikey = apikey; + } + + public String getUserAgent() { + return userAgent; + } + + public void setUserAgent(String userAgent) { + this.userAgent = userAgent; + } + + public Params getParams() { + return params; + } + + public void setParams(Params params) { + this.params = params; + } + + public String getFrom() { + return from; + } + + public void setFrom(String from) { + this.from = from; + } + + public Config getConfig() { + return config; + } + + public void setConfig(Config config) { + this.config = config; + } + + public String getSeq() { + return seq; + } + + public void setSeq(String seq) { + this.seq = seq; + } + + public String getSequence() { + return sequence; + } + + public void setSequence(String sequence) { + this.sequence = sequence; + } + + public Map getAdditionalProperties() { + return this.additionalProperties; + } + + public void setAdditionalProperty(String name, Object value) { + this.additionalProperties.put(name, value); + } + + @Override + public String toString() { + return "WssRspMsg{" + + "error=" + error + + ", action='" + action + '\'' + + ", deviceid='" + deviceid + '\'' + + ", apikey='" + apikey + '\'' + + ", userAgent='" + userAgent + '\'' + + ", params=" + params + + ", from='" + from + '\'' + + ", config=" + config + + ", seq='" + seq + '\'' + + ", sequence='" + sequence + '\'' + + ", additionalProperties=" + additionalProperties + + '}'; + } +} diff --git a/src/test/java/com/github/realzimboguy/ewelink/api/TestCode.java b/src/test/java/com/github/realzimboguy/ewelink/api/TestCode.java new file mode 100644 index 0000000..c266257 --- /dev/null +++ b/src/test/java/com/github/realzimboguy/ewelink/api/TestCode.java @@ -0,0 +1,82 @@ +package com.github.realzimboguy.ewelink.api; + +import com.github.realzimboguy.ewelink.api.model.devices.DeviceItem; +import com.github.realzimboguy.ewelink.api.model.devices.Devices; +import com.github.realzimboguy.ewelink.api.wss.WssResponse; +import com.github.realzimboguy.ewelink.api.wss.wssrsp.WssRspMsg; + +public class TestCode { + + public static void main(String[] args) { + + + EweLink eweLink = new EweLink("eu", "asdf@gmail.com", "asdf", 60); + + try { + eweLink.login(); + + Devices getDevices = eweLink.getDevices(); + + for (DeviceItem devicelist : getDevices.getDevicelist()) { + System.out.println(devicelist.getDeviceid()); + System.out.println(devicelist.getName()); + + System.out.println(eweLink.getDeviceStatus(devicelist.getDeviceid())); + + } + + eweLink.getWebSocket(new WssResponse() { + + @Override + public void onMessage(String s) { + //if you want the raw json data + System.out.println("on message in test raw:" + s); + + } + + @Override + public void onMessageParsed(WssRspMsg rsp) { + + if (rsp.getError() == null) { + + //normal scenario + StringBuilder sb = new StringBuilder(); + sb.append("Device:").append(rsp.getDeviceid()).append(" - "); + if (rsp.getParams() != null) { + sb.append("Switch:").append(rsp.getParams().getSwitch()).append(" - "); + sb.append("Voltage:").append(rsp.getParams().getVoltage()).append(" - "); + sb.append("Power:").append(rsp.getParams().getPower()).append(" - "); + sb.append("Current:").append(rsp.getParams().getCurrent()).append(" - "); + } + + System.out.println(sb.toString()); + + } else if (rsp.getError() == 0) { + //this is from a login response + System.out.println("login success"); + } else if (rsp.getError() > 0) { + System.out.println("login error:" + rsp.toString()); + } + } + + @Override + public void onError(String error) { + System.out.println("onError in test, this should never be called"); + System.out.println(error); + + } + }); + + +// System.out.println(eweLink.getDevice("10009ce53b")); + +// System.out.println(eweLink.setDeviceStatusByName("Pool Tank","off")); + + + } catch (Exception e) { + e.printStackTrace(); + } + + } + +} diff --git a/src/test/resources/log4j2.xml b/src/test/resources/log4j2.xml new file mode 100644 index 0000000..2820c0c --- /dev/null +++ b/src/test/resources/log4j2.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + +