]> git.basschouten.com Git - openhab-addons.git/blob
97bd21888b14895a1d5f2a00e8a0556f36b546a4
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2022 Contributors to the openHAB project
3  *
4  * See the NOTICE file(s) distributed with this work for additional
5  * information.
6  *
7  * This program and the accompanying materials are made available under the
8  * terms of the Eclipse Public License 2.0 which is available at
9  * http://www.eclipse.org/legal/epl-2.0
10  *
11  * SPDX-License-Identifier: EPL-2.0
12  */
13 package org.openhab.binding.unifiedremote.internal;
14
15 import java.util.UUID;
16 import java.util.concurrent.ExecutionException;
17 import java.util.concurrent.TimeUnit;
18 import java.util.concurrent.TimeoutException;
19
20 import org.eclipse.jdt.annotation.NonNullByDefault;
21 import org.eclipse.jdt.annotation.Nullable;
22 import org.eclipse.jetty.client.HttpClient;
23 import org.eclipse.jetty.client.api.ContentResponse;
24 import org.eclipse.jetty.client.api.Request;
25 import org.eclipse.jetty.client.util.StringContentProvider;
26 import org.eclipse.jetty.http.HttpHeader;
27 import org.eclipse.jetty.http.HttpMethod;
28 import org.slf4j.Logger;
29 import org.slf4j.LoggerFactory;
30
31 import com.google.gson.JsonArray;
32 import com.google.gson.JsonElement;
33 import com.google.gson.JsonObject;
34 import com.google.gson.JsonParser;
35
36 /**
37  * The {@link UnifiedRemoteConnection} Handles Remote Server Communications
38  *
39  * @author Miguel Alvarez - Initial contribution
40  */
41 @NonNullByDefault
42 public class UnifiedRemoteConnection {
43
44     private static final int WEB_CLIENT_PORT = 9510;
45     private static final int TIMEOUT_SEC = 10;
46     private static final String CONNECTION_ID_HEADER = "UR-Connection-ID";
47     private static final String MOUSE_REMOTE = "Relmtech.Basic Input";
48     private static final String NAVIGATION_REMOTE = "Unified.Navigation";
49     private static final String POWER_REMOTE = "Unified.Power";
50     private static final String MEDIA_REMOTE = "Unified.Media";
51     private static final String MONITOR_REMOTE = "Unified.Monitor";
52
53     private Logger logger = LoggerFactory.getLogger(UnifiedRemoteConnection.class);
54     private final String url;
55     private HttpClient httpClient;
56     private @Nullable String connectionID;
57     private @Nullable String connectionGUID;
58
59     public UnifiedRemoteConnection(HttpClient httpClient, String host) {
60         this.httpClient = httpClient;
61         url = "http://" + host + ":" + WEB_CLIENT_PORT + "/client/";
62     }
63
64     public void authenticate() throws InterruptedException, ExecutionException, TimeoutException {
65         ContentResponse response = null;
66         connectionGUID = "web-" + UUID.randomUUID().toString();
67         response = httpClient.newRequest(getPath("connect")).method(HttpMethod.GET)
68                 .timeout(TIMEOUT_SEC, TimeUnit.SECONDS).send();
69         JsonObject responseBody = JsonParser.parseString(response.getContentAsString()).getAsJsonObject();
70         connectionID = responseBody.get("id").getAsString();
71
72         String password = UUID.randomUUID().toString();
73         JsonObject authPayload = new JsonObject();
74         authPayload.addProperty("Action", 0);
75         authPayload.addProperty("Request", 0);
76         authPayload.addProperty("Version", 10);
77         authPayload.addProperty("Password", password);
78         authPayload.addProperty("Platform", "web");
79         authPayload.addProperty("Source", connectionGUID);
80         request(authPayload);
81
82         JsonObject capabilitiesPayload = new JsonObject();
83         JsonObject capabilitiesInnerPayload = new JsonObject();
84         capabilitiesInnerPayload.addProperty("Actions", true);
85         capabilitiesInnerPayload.addProperty("Sync", true);
86         capabilitiesInnerPayload.addProperty("Grid", true);
87         capabilitiesInnerPayload.addProperty("Fast", false);
88         capabilitiesInnerPayload.addProperty("Loading", true);
89         capabilitiesInnerPayload.addProperty("Encryption2", true);
90         capabilitiesPayload.add("Capabilities", capabilitiesInnerPayload);
91         capabilitiesPayload.addProperty("Action", 1);
92         capabilitiesPayload.addProperty("Request", 1);
93         capabilitiesPayload.addProperty("Source", connectionGUID);
94         request(capabilitiesPayload);
95     }
96
97     public ContentResponse mouseMove(String jsonIntArray)
98             throws InterruptedException, ExecutionException, TimeoutException {
99         JsonArray cordinates = JsonParser.parseString(jsonIntArray).getAsJsonArray();
100         int x = cordinates.get(0).getAsInt();
101         int y = cordinates.get(1).getAsInt();
102         return this.execRemoteAction("Relmtech.Basic Input", "delta",
103                 wrapValues(new String[] { "0", Integer.toString(x), Integer.toString(y) }));
104     }
105
106     public ContentResponse sendKey(String key) throws InterruptedException, ExecutionException, TimeoutException {
107         String remoteID = "";
108         String actionName = "";
109         String value = null;
110         switch (key) {
111             case "LEFT_CLICK":
112                 remoteID = MOUSE_REMOTE;
113                 actionName = "left";
114                 break;
115             case "RIGHT_CLICK":
116                 remoteID = MOUSE_REMOTE;
117                 actionName = "right";
118                 break;
119             case "LOCK":
120                 remoteID = POWER_REMOTE;
121                 actionName = "lock";
122                 break;
123             case "UNLOCK":
124                 remoteID = POWER_REMOTE;
125                 actionName = "unlock";
126                 break;
127             case "SLEEP":
128                 remoteID = POWER_REMOTE;
129                 actionName = "sleep";
130                 break;
131             case "SHUTDOWN":
132                 remoteID = POWER_REMOTE;
133                 actionName = "shutdown";
134                 break;
135             case "RESTART":
136                 remoteID = POWER_REMOTE;
137                 actionName = "restart";
138                 break;
139             case "LOGOFF":
140                 remoteID = POWER_REMOTE;
141                 actionName = "logoff";
142                 break;
143             case "PLAY/PAUSE":
144             case "PLAY":
145             case "PAUSE":
146                 remoteID = MEDIA_REMOTE;
147                 actionName = "play_pause";
148                 break;
149             case "NEXT":
150                 remoteID = MEDIA_REMOTE;
151                 actionName = "next";
152                 break;
153             case "PREVIOUS":
154                 remoteID = MEDIA_REMOTE;
155                 actionName = "previous";
156                 break;
157             case "STOP":
158                 remoteID = MEDIA_REMOTE;
159                 actionName = "stop";
160                 break;
161             case "VOLUME_MUTE":
162                 remoteID = MEDIA_REMOTE;
163                 actionName = "volume_mute";
164                 break;
165             case "VOLUME_UP":
166                 remoteID = MEDIA_REMOTE;
167                 actionName = "volume_up";
168                 break;
169             case "VOLUME_DOWN":
170                 remoteID = MEDIA_REMOTE;
171                 actionName = "volume_down";
172                 break;
173             case "BRIGHTNESS_UP":
174                 remoteID = MONITOR_REMOTE;
175                 actionName = "brightness_up";
176                 break;
177             case "BRIGHTNESS_DOWN":
178                 remoteID = MONITOR_REMOTE;
179                 actionName = "brightness_down";
180                 break;
181             case "MONITOR_OFF":
182                 remoteID = MONITOR_REMOTE;
183                 actionName = "turn_off";
184                 break;
185             case "MONITOR_ON":
186                 remoteID = MONITOR_REMOTE;
187                 actionName = "turn_on";
188                 break;
189             case "ESCAPE":
190             case "SPACE":
191             case "BACK":
192             case "LWIN":
193             case "CONTROL":
194             case "TAB":
195             case "MENU":
196             case "RETURN":
197             case "UP":
198             case "DOWN":
199             case "LEFT":
200             case "RIGHT":
201                 remoteID = NAVIGATION_REMOTE;
202                 actionName = "toggle";
203                 value = key;
204                 break;
205         }
206         JsonArray wrappedValues = null;
207         if (value != null) {
208             wrappedValues = wrapValues(new String[] { value });
209         }
210         return this.execRemoteAction(remoteID, actionName, wrappedValues);
211     }
212
213     public ContentResponse keepAlive() throws InterruptedException, ExecutionException, TimeoutException {
214         JsonObject payload = new JsonObject();
215         payload.addProperty("KeepAlive", true);
216         payload.addProperty("Source", connectionGUID);
217         return request(payload);
218     }
219
220     private ContentResponse execRemoteAction(String remoteID, String name, @Nullable JsonElement values)
221             throws InterruptedException, ExecutionException, TimeoutException {
222         JsonObject payload = new JsonObject();
223
224         JsonObject runInnerPayload = new JsonObject();
225         JsonObject extrasInnerPayload = new JsonObject();
226         if (values != null) {
227             extrasInnerPayload.add("Values", values);
228             runInnerPayload.add("Extras", extrasInnerPayload);
229         }
230         runInnerPayload.addProperty("Name", name);
231         payload.addProperty("ID", remoteID);
232         payload.addProperty("Action", 7);
233         payload.addProperty("Request", 7);
234         payload.add("Run", runInnerPayload);
235         payload.addProperty("Source", connectionGUID);
236         return request(payload);
237     }
238
239     private ContentResponse request(JsonObject content)
240             throws InterruptedException, ExecutionException, TimeoutException {
241         Request request = httpClient.newRequest(getPath("request")).method(HttpMethod.POST).timeout(TIMEOUT_SEC,
242                 TimeUnit.SECONDS);
243         request.header(HttpHeader.CONTENT_TYPE, "application/json");
244         if (connectionID != null) {
245             request.header(CONNECTION_ID_HEADER, connectionID);
246         }
247         String stringContent = content.toString();
248         logger.debug("[Request Payload {} ]", stringContent);
249         request.content(new StringContentProvider(stringContent, "utf-8"));
250         return request.send();
251     }
252
253     private JsonArray wrapValues(String[] commandValues) {
254         JsonArray values = new JsonArray();
255         for (String value : commandValues) {
256             JsonObject valueWrapper = new JsonObject();
257             valueWrapper.addProperty("Value", value);
258             values.add(valueWrapper);
259         }
260         return values;
261     }
262
263     private String getPath(String path) {
264         return url + path;
265     }
266 }