2 * Copyright (c) 2010-2022 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 HttpClient httpClient;
56 private @Nullable String connectionID;
57 private @Nullable String connectionGUID;
59 public UnifiedRemoteConnection(HttpClient httpClient, String host) {
60 this.httpClient = httpClient;
61 url = "http://" + host + ":" + WEB_CLIENT_PORT + "/client/";
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();
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);
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);
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) }));
106 public ContentResponse sendKey(String key) throws InterruptedException, ExecutionException, TimeoutException {
107 String remoteID = "";
108 String actionName = "";
112 remoteID = MOUSE_REMOTE;
116 remoteID = MOUSE_REMOTE;
117 actionName = "right";
120 remoteID = POWER_REMOTE;
124 remoteID = POWER_REMOTE;
125 actionName = "unlock";
128 remoteID = POWER_REMOTE;
129 actionName = "sleep";
132 remoteID = POWER_REMOTE;
133 actionName = "shutdown";
136 remoteID = POWER_REMOTE;
137 actionName = "restart";
140 remoteID = POWER_REMOTE;
141 actionName = "logoff";
146 remoteID = MEDIA_REMOTE;
147 actionName = "play_pause";
150 remoteID = MEDIA_REMOTE;
154 remoteID = MEDIA_REMOTE;
155 actionName = "previous";
158 remoteID = MEDIA_REMOTE;
162 remoteID = MEDIA_REMOTE;
163 actionName = "volume_mute";
166 remoteID = MEDIA_REMOTE;
167 actionName = "volume_up";
170 remoteID = MEDIA_REMOTE;
171 actionName = "volume_down";
173 case "BRIGHTNESS_UP":
174 remoteID = MONITOR_REMOTE;
175 actionName = "brightness_up";
177 case "BRIGHTNESS_DOWN":
178 remoteID = MONITOR_REMOTE;
179 actionName = "brightness_down";
182 remoteID = MONITOR_REMOTE;
183 actionName = "turn_off";
186 remoteID = MONITOR_REMOTE;
187 actionName = "turn_on";
201 remoteID = NAVIGATION_REMOTE;
202 actionName = "toggle";
206 JsonArray wrappedValues = null;
208 wrappedValues = wrapValues(new String[] { value });
210 return this.execRemoteAction(remoteID, actionName, wrappedValues);
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);
220 private ContentResponse execRemoteAction(String remoteID, String name, @Nullable JsonElement values)
221 throws InterruptedException, ExecutionException, TimeoutException {
222 JsonObject payload = new JsonObject();
224 JsonObject runInnerPayload = new JsonObject();
225 JsonObject extrasInnerPayload = new JsonObject();
226 if (values != null) {
227 extrasInnerPayload.add("Values", values);
228 runInnerPayload.add("Extras", extrasInnerPayload);
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);
239 private ContentResponse request(JsonObject content)
240 throws InterruptedException, ExecutionException, TimeoutException {
241 Request request = httpClient.newRequest(getPath("request")).method(HttpMethod.POST).timeout(TIMEOUT_SEC,
243 request.header(HttpHeader.CONTENT_TYPE, "application/json");
244 if (connectionID != null) {
245 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) {