2 * Copyright (c) 2010-2020 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
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
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.binding.unifiedremote.internal;
15 import java.util.UUID;
16 import java.util.concurrent.ExecutionException;
17 import java.util.concurrent.TimeUnit;
18 import java.util.concurrent.TimeoutException;
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;
31 import com.google.gson.JsonArray;
32 import com.google.gson.JsonElement;
33 import com.google.gson.JsonObject;
34 import com.google.gson.JsonParser;
37 * The {@link UnifiedRemoteConnection} Handles Remote Server Communications
39 * @author Miguel Alvarez - Initial contribution
42 public class UnifiedRemoteConnection {
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";
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;
60 public UnifiedRemoteConnection(HttpClient httpClient, String host) {
61 this.httpClient = httpClient;
62 url = "http://" + host + ":" + WEB_CLIENT_PORT + "/client/";
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();
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);
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);
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) }));
107 public ContentResponse sendKey(String key) throws InterruptedException, ExecutionException, TimeoutException {
108 String remoteID = "";
109 String actionName = "";
113 remoteID = MOUSE_REMOTE;
117 remoteID = MOUSE_REMOTE;
118 actionName = "right";
121 remoteID = POWER_REMOTE;
125 remoteID = POWER_REMOTE;
126 actionName = "unlock";
129 remoteID = POWER_REMOTE;
130 actionName = "sleep";
133 remoteID = POWER_REMOTE;
134 actionName = "shutdown";
137 remoteID = POWER_REMOTE;
138 actionName = "restart";
141 remoteID = POWER_REMOTE;
142 actionName = "logoff";
147 remoteID = MEDIA_REMOTE;
148 actionName = "play_pause";
151 remoteID = MEDIA_REMOTE;
155 remoteID = MEDIA_REMOTE;
156 actionName = "previous";
159 remoteID = MEDIA_REMOTE;
163 remoteID = MEDIA_REMOTE;
164 actionName = "volume_mute";
167 remoteID = MEDIA_REMOTE;
168 actionName = "volume_up";
171 remoteID = MEDIA_REMOTE;
172 actionName = "volume_down";
174 case "BRIGHTNESS_UP":
175 remoteID = MONITOR_REMOTE;
176 actionName = "brightness_up";
178 case "BRIGHTNESS_DOWN":
179 remoteID = MONITOR_REMOTE;
180 actionName = "brightness_down";
183 remoteID = MONITOR_REMOTE;
184 actionName = "turn_off";
187 remoteID = MONITOR_REMOTE;
188 actionName = "turn_on";
202 remoteID = NAVIGATION_REMOTE;
203 actionName = "toggle";
207 JsonArray wrappedValues = null;
209 wrappedValues = wrapValues(new String[] { value });
211 return this.execRemoteAction(remoteID, actionName, wrappedValues);
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);
221 private ContentResponse execRemoteAction(String remoteID, String name, @Nullable JsonElement values)
222 throws InterruptedException, ExecutionException, TimeoutException {
223 JsonObject payload = new JsonObject();
225 JsonObject runInnerPayload = new JsonObject();
226 JsonObject extrasInnerPayload = new JsonObject();
227 if (values != null) {
228 extrasInnerPayload.add("Values", values);
229 runInnerPayload.add("Extras", extrasInnerPayload);
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);
240 private ContentResponse request(JsonObject content)
241 throws InterruptedException, ExecutionException, TimeoutException {
242 Request request = httpClient.newRequest(getPath("request")).method(HttpMethod.POST).timeout(TIMEOUT_SEC,
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();
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);
263 private String getPath(String path) {