]> git.basschouten.com Git - openhab-addons.git/blob
4c7cc1f2266001340ca106b62d734154c971a255
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2021 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 final JsonParser jsonParser = new JsonParser();
56     private HttpClient httpClient;
57     private @Nullable String connectionID;
58     private @Nullable String connectionGUID;
59
60     public UnifiedRemoteConnection(HttpClient httpClient, String host) {
61         this.httpClient = httpClient;
62         url = "http://" + host + ":" + WEB_CLIENT_PORT + "/client/";
63     }
64
65     public void authenticate() throws InterruptedException, ExecutionException, TimeoutException {
66         ContentResponse response = null;
67         connectionGUID = "web-" + UUID.randomUUID().toString();
68         response = httpClient.newRequest(getPath("connect")).method(HttpMethod.GET)
69                 .timeout(TIMEOUT_SEC, TimeUnit.SECONDS).send();
70         JsonObject responseBody = jsonParser.parse(response.getContentAsString()).getAsJsonObject();
71         connectionID = responseBody.get("id").getAsString();
72
73         String password = UUID.randomUUID().toString();
74         JsonObject authPayload = new JsonObject();
75         authPayload.addProperty("Action", 0);
76         authPayload.addProperty("Request", 0);
77         authPayload.addProperty("Version", 10);
78         authPayload.addProperty("Password", password);
79         authPayload.addProperty("Platform", "web");
80         authPayload.addProperty("Source", connectionGUID);
81         request(authPayload);
82
83         JsonObject capabilitiesPayload = new JsonObject();
84         JsonObject capabilitiesInnerPayload = new JsonObject();
85         capabilitiesInnerPayload.addProperty("Actions", true);
86         capabilitiesInnerPayload.addProperty("Sync", true);
87         capabilitiesInnerPayload.addProperty("Grid", true);
88         capabilitiesInnerPayload.addProperty("Fast", false);
89         capabilitiesInnerPayload.addProperty("Loading", true);
90         capabilitiesInnerPayload.addProperty("Encryption2", true);
91         capabilitiesPayload.add("Capabilities", capabilitiesInnerPayload);
92         capabilitiesPayload.addProperty("Action", 1);
93         capabilitiesPayload.addProperty("Request", 1);
94         capabilitiesPayload.addProperty("Source", connectionGUID);
95         request(capabilitiesPayload);
96     }
97
98     public ContentResponse mouseMove(String jsonIntArray)
99             throws InterruptedException, ExecutionException, TimeoutException {
100         JsonArray cordinates = jsonParser.parse(jsonIntArray).getAsJsonArray();
101         int x = cordinates.get(0).getAsInt();
102         int y = cordinates.get(1).getAsInt();
103         return this.execRemoteAction("Relmtech.Basic Input", "delta",
104                 wrapValues(new String[] { "0", Integer.toString(x), Integer.toString(y) }));
105     }
106
107     public ContentResponse sendKey(String key) throws InterruptedException, ExecutionException, TimeoutException {
108         String remoteID = "";
109         String actionName = "";
110         String value = null;
111         switch (key) {
112             case "LEFT_CLICK":
113                 remoteID = MOUSE_REMOTE;
114                 actionName = "left";
115                 break;
116             case "RIGHT_CLICK":
117                 remoteID = MOUSE_REMOTE;
118                 actionName = "right";
119                 break;
120             case "LOCK":
121                 remoteID = POWER_REMOTE;
122                 actionName = "lock";
123                 break;
124             case "UNLOCK":
125                 remoteID = POWER_REMOTE;
126                 actionName = "unlock";
127                 break;
128             case "SLEEP":
129                 remoteID = POWER_REMOTE;
130                 actionName = "sleep";
131                 break;
132             case "SHUTDOWN":
133                 remoteID = POWER_REMOTE;
134                 actionName = "shutdown";
135                 break;
136             case "RESTART":
137                 remoteID = POWER_REMOTE;
138                 actionName = "restart";
139                 break;
140             case "LOGOFF":
141                 remoteID = POWER_REMOTE;
142                 actionName = "logoff";
143                 break;
144             case "PLAY/PAUSE":
145             case "PLAY":
146             case "PAUSE":
147                 remoteID = MEDIA_REMOTE;
148                 actionName = "play_pause";
149                 break;
150             case "NEXT":
151                 remoteID = MEDIA_REMOTE;
152                 actionName = "next";
153                 break;
154             case "PREVIOUS":
155                 remoteID = MEDIA_REMOTE;
156                 actionName = "previous";
157                 break;
158             case "STOP":
159                 remoteID = MEDIA_REMOTE;
160                 actionName = "stop";
161                 break;
162             case "VOLUME_MUTE":
163                 remoteID = MEDIA_REMOTE;
164                 actionName = "volume_mute";
165                 break;
166             case "VOLUME_UP":
167                 remoteID = MEDIA_REMOTE;
168                 actionName = "volume_up";
169                 break;
170             case "VOLUME_DOWN":
171                 remoteID = MEDIA_REMOTE;
172                 actionName = "volume_down";
173                 break;
174             case "BRIGHTNESS_UP":
175                 remoteID = MONITOR_REMOTE;
176                 actionName = "brightness_up";
177                 break;
178             case "BRIGHTNESS_DOWN":
179                 remoteID = MONITOR_REMOTE;
180                 actionName = "brightness_down";
181                 break;
182             case "MONITOR_OFF":
183                 remoteID = MONITOR_REMOTE;
184                 actionName = "turn_off";
185                 break;
186             case "MONITOR_ON":
187                 remoteID = MONITOR_REMOTE;
188                 actionName = "turn_on";
189                 break;
190             case "ESCAPE":
191             case "SPACE":
192             case "BACK":
193             case "LWIN":
194             case "CONTROL":
195             case "TAB":
196             case "MENU":
197             case "RETURN":
198             case "UP":
199             case "DOWN":
200             case "LEFT":
201             case "RIGHT":
202                 remoteID = NAVIGATION_REMOTE;
203                 actionName = "toggle";
204                 value = key;
205                 break;
206         }
207         JsonArray wrappedValues = null;
208         if (value != null) {
209             wrappedValues = wrapValues(new String[] { value });
210         }
211         return this.execRemoteAction(remoteID, actionName, wrappedValues);
212     }
213
214     public ContentResponse keepAlive() throws InterruptedException, ExecutionException, TimeoutException {
215         JsonObject payload = new JsonObject();
216         payload.addProperty("KeepAlive", true);
217         payload.addProperty("Source", connectionGUID);
218         return request(payload);
219     }
220
221     private ContentResponse execRemoteAction(String remoteID, String name, @Nullable JsonElement values)
222             throws InterruptedException, ExecutionException, TimeoutException {
223         JsonObject payload = new JsonObject();
224
225         JsonObject runInnerPayload = new JsonObject();
226         JsonObject extrasInnerPayload = new JsonObject();
227         if (values != null) {
228             extrasInnerPayload.add("Values", values);
229             runInnerPayload.add("Extras", extrasInnerPayload);
230         }
231         runInnerPayload.addProperty("Name", name);
232         payload.addProperty("ID", remoteID);
233         payload.addProperty("Action", 7);
234         payload.addProperty("Request", 7);
235         payload.add("Run", runInnerPayload);
236         payload.addProperty("Source", connectionGUID);
237         return request(payload);
238     }
239
240     private ContentResponse request(JsonObject content)
241             throws InterruptedException, ExecutionException, TimeoutException {
242         Request request = httpClient.newRequest(getPath("request")).method(HttpMethod.POST).timeout(TIMEOUT_SEC,
243                 TimeUnit.SECONDS);
244         request.header(HttpHeader.CONTENT_TYPE, "application/json");
245         if (connectionID != null)
246             request.header(CONNECTION_ID_HEADER, connectionID);
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 }