2 * Copyright (c) 2010-2023 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.miele.internal;
15 import java.io.StringReader;
17 import java.net.URISyntaxException;
18 import java.util.Random;
19 import java.util.concurrent.ExecutionException;
20 import java.util.concurrent.TimeUnit;
21 import java.util.concurrent.TimeoutException;
23 import org.eclipse.jdt.annotation.NonNullByDefault;
24 import org.eclipse.jetty.client.HttpClient;
25 import org.eclipse.jetty.client.api.ContentResponse;
26 import org.eclipse.jetty.client.api.Request;
27 import org.eclipse.jetty.client.util.StringContentProvider;
28 import org.eclipse.jetty.http.HttpMethod;
29 import org.openhab.binding.miele.internal.exceptions.MieleRpcException;
30 import org.slf4j.Logger;
31 import org.slf4j.LoggerFactory;
33 import com.google.gson.Gson;
34 import com.google.gson.JsonArray;
35 import com.google.gson.JsonElement;
36 import com.google.gson.JsonObject;
37 import com.google.gson.JsonParseException;
38 import com.google.gson.JsonParser;
41 * The {@link MieleGatewayCommunicationController} class is used for communicating with
42 * the XGW 3000 gateway through JSON-RPC.
44 * @author Jacob Laursen - Initial contribution
47 public class MieleGatewayCommunicationController {
48 private static final int REQUEST_TIMEOUT_MS = 10_000;
50 private final URI uri;
51 private final Random rand = new Random();
52 private final Gson gson = new Gson();
53 private final Logger logger = LoggerFactory.getLogger(MieleGatewayCommunicationController.class);
54 private final HttpClient httpClient;
56 public MieleGatewayCommunicationController(HttpClient httpClient, String host) throws URISyntaxException {
57 uri = new URI("http://" + host + "/remote/json-rpc");
58 this.httpClient = httpClient;
61 public JsonElement invokeOperation(FullyQualifiedApplianceIdentifier applianceIdentifier, String modelID,
62 String methodName) throws MieleRpcException {
63 Object[] args = new Object[4];
64 args[0] = applianceIdentifier.getUid();
65 args[1] = MieleBindingConstants.MIELE_CLASS + modelID;
69 return invokeRPC("HDAccess/invokeDCOOperation", args);
72 public JsonElement invokeRPC(String methodName, Object[] args) throws MieleRpcException {
73 JsonElement result = null;
74 JsonObject requestBodyAsJson = new JsonObject();
75 int id = rand.nextInt(Integer.MAX_VALUE);
76 requestBodyAsJson.addProperty("jsonrpc", "2.0");
77 requestBodyAsJson.addProperty("id", id);
78 requestBodyAsJson.addProperty("method", methodName);
80 JsonArray params = new JsonArray();
81 for (Object o : args) {
82 params.add(gson.toJsonTree(o));
84 requestBodyAsJson.add("params", params);
86 String requestBody = requestBodyAsJson.toString();
87 Request request = httpClient.newRequest(uri).method(HttpMethod.POST)
88 .timeout(REQUEST_TIMEOUT_MS, TimeUnit.MILLISECONDS)
89 .content(new StringContentProvider(requestBody), "application/json");
91 String responseData = null;
93 final ContentResponse contentResponse = request.send();
94 final int httpStatus = contentResponse.getStatus();
95 if (httpStatus != 200) {
96 if (httpStatus == 503) {
97 throw new MieleRpcException("Gateway is temporarily unavailable");
99 throw new MieleRpcException("Unexpected HTTP status code " + httpStatus);
101 responseData = contentResponse.getContentAsString();
102 } catch (TimeoutException e) {
103 throw new MieleRpcException("Timeout when calling gateway", e);
104 } catch (ExecutionException e) {
105 throw new MieleRpcException("Failure when calling gateway", e);
106 } catch (InterruptedException e) {
107 Thread.currentThread().interrupt();
108 throw new MieleRpcException("Interrupted while calling gateway", e);
111 logger.trace("The request '{}' yields '{}'", requestBody, responseData);
112 JsonObject parsedResponse = null;
114 parsedResponse = (JsonObject) JsonParser.parseReader(new StringReader(responseData));
115 } catch (JsonParseException e) {
116 throw new MieleRpcException("Error parsing JSON response", e);
119 JsonElement error = parsedResponse.get("error");
120 if (error != null && !error.isJsonNull()) {
121 if (error.isJsonPrimitive()) {
122 throw new MieleRpcException("Remote exception occurred: '" + error.getAsString() + "'");
123 } else if (error.isJsonObject()) {
124 JsonObject o = error.getAsJsonObject();
125 Integer code = (o.has("code") ? o.get("code").getAsInt() : null);
126 String message = (o.has("message") ? o.get("message").getAsString() : null);
127 String data = (o.has("data")
128 ? (o.get("data") instanceof JsonObject ? o.get("data").toString() : o.get("data").getAsString())
130 throw new MieleRpcException(
131 "Remote exception occurred: '" + code + "':'" + message + "':'" + data + "'");
133 throw new MieleRpcException("Unknown remote exception occurred: '" + error.toString() + "'");
137 result = parsedResponse.get("result");
138 if (result == null) {
139 throw new MieleRpcException("Result is missing in response");