[miele] Use framework's HTTP client (#12545)
* Refactor to use framework's Jetty HTTP client * Do not try to parse HTML as JSON on failed HTTP calls * Improve handler initialization log messages Signed-off-by: Jacob Laursen <jacob-github@vindvejr.dk>
This commit is contained in:
parent
a2a5c2e6ff
commit
8c6534300a
@ -12,21 +12,19 @@
|
|||||||
*/
|
*/
|
||||||
package org.openhab.binding.miele.internal;
|
package org.openhab.binding.miele.internal;
|
||||||
|
|
||||||
import java.io.BufferedInputStream;
|
|
||||||
import java.io.ByteArrayOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.OutputStream;
|
|
||||||
import java.io.StringReader;
|
import java.io.StringReader;
|
||||||
import java.net.HttpURLConnection;
|
import java.net.URI;
|
||||||
import java.net.MalformedURLException;
|
import java.net.URISyntaxException;
|
||||||
import java.net.URL;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
import java.util.zip.GZIPInputStream;
|
import java.util.concurrent.ExecutionException;
|
||||||
|
import java.util.concurrent.TimeoutException;
|
||||||
|
|
||||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
import org.eclipse.jetty.client.HttpClient;
|
||||||
|
import org.eclipse.jetty.client.api.ContentResponse;
|
||||||
|
import org.eclipse.jetty.client.api.Request;
|
||||||
|
import org.eclipse.jetty.client.util.StringContentProvider;
|
||||||
|
import org.eclipse.jetty.http.HttpMethod;
|
||||||
import org.openhab.binding.miele.internal.exceptions.MieleRpcException;
|
import org.openhab.binding.miele.internal.exceptions.MieleRpcException;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
@ -47,13 +45,15 @@ import com.google.gson.JsonParser;
|
|||||||
@NonNullByDefault
|
@NonNullByDefault
|
||||||
public class MieleGatewayCommunicationController {
|
public class MieleGatewayCommunicationController {
|
||||||
|
|
||||||
private final URL url;
|
private final URI uri;
|
||||||
private final Random rand = new Random();
|
private final Random rand = new Random();
|
||||||
private final Gson gson = new Gson();
|
private final Gson gson = new Gson();
|
||||||
private final Logger logger = LoggerFactory.getLogger(MieleGatewayCommunicationController.class);
|
private final Logger logger = LoggerFactory.getLogger(MieleGatewayCommunicationController.class);
|
||||||
|
private final HttpClient httpClient;
|
||||||
|
|
||||||
public MieleGatewayCommunicationController(String host) throws MalformedURLException {
|
public MieleGatewayCommunicationController(HttpClient httpClient, String host) throws URISyntaxException {
|
||||||
url = new URL("http://" + host + "/remote/json-rpc");
|
uri = new URI("http://" + host + "/remote/json-rpc");
|
||||||
|
this.httpClient = httpClient;
|
||||||
}
|
}
|
||||||
|
|
||||||
public JsonElement invokeOperation(FullyQualifiedApplianceIdentifier applianceIdentifier, String modelID,
|
public JsonElement invokeOperation(FullyQualifiedApplianceIdentifier applianceIdentifier, String modelID,
|
||||||
@ -69,27 +69,43 @@ public class MieleGatewayCommunicationController {
|
|||||||
|
|
||||||
public JsonElement invokeRPC(String methodName, Object[] args) throws MieleRpcException {
|
public JsonElement invokeRPC(String methodName, Object[] args) throws MieleRpcException {
|
||||||
JsonElement result = null;
|
JsonElement result = null;
|
||||||
JsonObject req = new JsonObject();
|
JsonObject requestBodyAsJson = new JsonObject();
|
||||||
int id = rand.nextInt(Integer.MAX_VALUE);
|
int id = rand.nextInt(Integer.MAX_VALUE);
|
||||||
req.addProperty("jsonrpc", "2.0");
|
requestBodyAsJson.addProperty("jsonrpc", "2.0");
|
||||||
req.addProperty("id", id);
|
requestBodyAsJson.addProperty("id", id);
|
||||||
req.addProperty("method", methodName);
|
requestBodyAsJson.addProperty("method", methodName);
|
||||||
|
|
||||||
JsonArray params = new JsonArray();
|
JsonArray params = new JsonArray();
|
||||||
for (Object o : args) {
|
for (Object o : args) {
|
||||||
params.add(gson.toJsonTree(o));
|
params.add(gson.toJsonTree(o));
|
||||||
}
|
}
|
||||||
req.add("params", params);
|
requestBodyAsJson.add("params", params);
|
||||||
|
|
||||||
|
String requestBody = requestBodyAsJson.toString();
|
||||||
|
Request request = httpClient.newRequest(uri).method(HttpMethod.POST)
|
||||||
|
.content(new StringContentProvider(requestBody), "application/json");
|
||||||
|
|
||||||
String requestData = req.toString();
|
|
||||||
String responseData = null;
|
String responseData = null;
|
||||||
try {
|
try {
|
||||||
responseData = post(url, Collections.emptyMap(), requestData);
|
final ContentResponse contentResponse = request.send();
|
||||||
} catch (IOException e) {
|
final int httpStatus = contentResponse.getStatus();
|
||||||
throw new MieleRpcException("Exception occurred while posting data", e);
|
if (httpStatus != 200) {
|
||||||
|
if (httpStatus == 503) {
|
||||||
|
throw new MieleRpcException("Gateway is temporarily unavailable");
|
||||||
|
}
|
||||||
|
throw new MieleRpcException("Unexpected HTTP status code " + httpStatus);
|
||||||
|
}
|
||||||
|
responseData = contentResponse.getContentAsString();
|
||||||
|
} catch (TimeoutException e) {
|
||||||
|
throw new MieleRpcException("Timeout when calling gateway", e);
|
||||||
|
} catch (ExecutionException e) {
|
||||||
|
throw new MieleRpcException("Failure when calling gateway", e);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
throw new MieleRpcException("Interrupted while calling gateway", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.trace("The request '{}' yields '{}'", requestData, responseData);
|
logger.trace("The request '{}' yields '{}'", requestBody, responseData);
|
||||||
JsonObject parsedResponse = null;
|
JsonObject parsedResponse = null;
|
||||||
try {
|
try {
|
||||||
parsedResponse = (JsonObject) JsonParser.parseReader(new StringReader(responseData));
|
parsedResponse = (JsonObject) JsonParser.parseReader(new StringReader(responseData));
|
||||||
@ -107,7 +123,7 @@ public class MieleGatewayCommunicationController {
|
|||||||
String message = (o.has("message") ? o.get("message").getAsString() : null);
|
String message = (o.has("message") ? o.get("message").getAsString() : null);
|
||||||
String data = (o.has("data")
|
String data = (o.has("data")
|
||||||
? (o.get("data") instanceof JsonObject ? o.get("data").toString() : o.get("data").getAsString())
|
? (o.get("data") instanceof JsonObject ? o.get("data").toString() : o.get("data").getAsString())
|
||||||
: null);
|
: "");
|
||||||
throw new MieleRpcException(
|
throw new MieleRpcException(
|
||||||
"Remote exception occurred: '" + code + "':'" + message + "':'" + data + "'");
|
"Remote exception occurred: '" + code + "':'" + message + "':'" + data + "'");
|
||||||
} else {
|
} else {
|
||||||
@ -122,63 +138,4 @@ public class MieleGatewayCommunicationController {
|
|||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private String post(URL url, Map<String, String> headers, String data) throws IOException {
|
|
||||||
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
|
|
||||||
|
|
||||||
for (Map.Entry<String, String> entry : headers.entrySet()) {
|
|
||||||
connection.addRequestProperty(entry.getKey(), entry.getValue());
|
|
||||||
}
|
|
||||||
|
|
||||||
connection.addRequestProperty("Accept-Encoding", "gzip");
|
|
||||||
connection.setRequestMethod("POST");
|
|
||||||
connection.setDoOutput(true);
|
|
||||||
connection.connect();
|
|
||||||
|
|
||||||
OutputStream out = null;
|
|
||||||
|
|
||||||
try {
|
|
||||||
out = connection.getOutputStream();
|
|
||||||
|
|
||||||
out.write(data.getBytes());
|
|
||||||
out.flush();
|
|
||||||
|
|
||||||
int statusCode = connection.getResponseCode();
|
|
||||||
if (statusCode != HttpURLConnection.HTTP_OK) {
|
|
||||||
logger.debug("An unexpected status code was returned: '{}'", statusCode);
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
if (out != null) {
|
|
||||||
out.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
String responseEncoding = connection.getHeaderField("Content-Encoding");
|
|
||||||
responseEncoding = (responseEncoding == null ? "" : responseEncoding.trim());
|
|
||||||
|
|
||||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
|
||||||
|
|
||||||
InputStream in = connection.getInputStream();
|
|
||||||
try {
|
|
||||||
in = connection.getInputStream();
|
|
||||||
if ("gzip".equalsIgnoreCase(responseEncoding)) {
|
|
||||||
in = new GZIPInputStream(in);
|
|
||||||
}
|
|
||||||
in = new BufferedInputStream(in);
|
|
||||||
|
|
||||||
byte[] buff = new byte[1024];
|
|
||||||
int n;
|
|
||||||
while ((n = in.read(buff)) > 0) {
|
|
||||||
bos.write(buff, 0, n);
|
|
||||||
}
|
|
||||||
bos.flush();
|
|
||||||
bos.close();
|
|
||||||
} finally {
|
|
||||||
if (in != null) {
|
|
||||||
in.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return bos.toString();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -23,6 +23,7 @@ import java.util.stream.Stream;
|
|||||||
|
|
||||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
import org.eclipse.jdt.annotation.Nullable;
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
|
import org.eclipse.jetty.client.HttpClient;
|
||||||
import org.openhab.binding.miele.internal.discovery.MieleApplianceDiscoveryService;
|
import org.openhab.binding.miele.internal.discovery.MieleApplianceDiscoveryService;
|
||||||
import org.openhab.binding.miele.internal.handler.CoffeeMachineHandler;
|
import org.openhab.binding.miele.internal.handler.CoffeeMachineHandler;
|
||||||
import org.openhab.binding.miele.internal.handler.DishWasherHandler;
|
import org.openhab.binding.miele.internal.handler.DishWasherHandler;
|
||||||
@ -39,6 +40,7 @@ import org.openhab.core.config.core.Configuration;
|
|||||||
import org.openhab.core.config.discovery.DiscoveryService;
|
import org.openhab.core.config.discovery.DiscoveryService;
|
||||||
import org.openhab.core.i18n.LocaleProvider;
|
import org.openhab.core.i18n.LocaleProvider;
|
||||||
import org.openhab.core.i18n.TranslationProvider;
|
import org.openhab.core.i18n.TranslationProvider;
|
||||||
|
import org.openhab.core.io.net.http.HttpClientFactory;
|
||||||
import org.openhab.core.thing.Bridge;
|
import org.openhab.core.thing.Bridge;
|
||||||
import org.openhab.core.thing.Thing;
|
import org.openhab.core.thing.Thing;
|
||||||
import org.openhab.core.thing.ThingTypeUID;
|
import org.openhab.core.thing.ThingTypeUID;
|
||||||
@ -57,6 +59,7 @@ import org.osgi.service.component.annotations.Reference;
|
|||||||
* handlers.
|
* handlers.
|
||||||
*
|
*
|
||||||
* @author Karel Goderis - Initial contribution
|
* @author Karel Goderis - Initial contribution
|
||||||
|
* @author Jacob Laursen - Refactored to use framework's HTTP client
|
||||||
*/
|
*/
|
||||||
@NonNullByDefault
|
@NonNullByDefault
|
||||||
@Component(service = ThingHandlerFactory.class, configurationPid = "binding.miele")
|
@Component(service = ThingHandlerFactory.class, configurationPid = "binding.miele")
|
||||||
@ -67,14 +70,17 @@ public class MieleHandlerFactory extends BaseThingHandlerFactory {
|
|||||||
MieleApplianceHandler.SUPPORTED_THING_TYPES.stream())
|
MieleApplianceHandler.SUPPORTED_THING_TYPES.stream())
|
||||||
.collect(Collectors.toSet());
|
.collect(Collectors.toSet());
|
||||||
|
|
||||||
|
private final HttpClient httpClient;
|
||||||
private final TranslationProvider i18nProvider;
|
private final TranslationProvider i18nProvider;
|
||||||
private final LocaleProvider localeProvider;
|
private final LocaleProvider localeProvider;
|
||||||
|
|
||||||
private Map<ThingUID, ServiceRegistration<?>> discoveryServiceRegs = new HashMap<>();
|
private Map<ThingUID, ServiceRegistration<?>> discoveryServiceRegs = new HashMap<>();
|
||||||
|
|
||||||
@Activate
|
@Activate
|
||||||
public MieleHandlerFactory(final @Reference TranslationProvider i18nProvider,
|
public MieleHandlerFactory(@Reference final HttpClientFactory httpClientFactory,
|
||||||
final @Reference LocaleProvider localeProvider, ComponentContext componentContext) {
|
final @Reference TranslationProvider i18nProvider, final @Reference LocaleProvider localeProvider,
|
||||||
|
ComponentContext componentContext) {
|
||||||
|
this.httpClient = httpClientFactory.getCommonHttpClient();
|
||||||
this.i18nProvider = i18nProvider;
|
this.i18nProvider = i18nProvider;
|
||||||
this.localeProvider = localeProvider;
|
this.localeProvider = localeProvider;
|
||||||
}
|
}
|
||||||
@ -102,7 +108,7 @@ public class MieleHandlerFactory extends BaseThingHandlerFactory {
|
|||||||
@Override
|
@Override
|
||||||
protected @Nullable ThingHandler createHandler(Thing thing) {
|
protected @Nullable ThingHandler createHandler(Thing thing) {
|
||||||
if (MieleBridgeHandler.SUPPORTED_THING_TYPES.contains(thing.getThingTypeUID())) {
|
if (MieleBridgeHandler.SUPPORTED_THING_TYPES.contains(thing.getThingTypeUID())) {
|
||||||
MieleBridgeHandler handler = new MieleBridgeHandler((Bridge) thing);
|
MieleBridgeHandler handler = new MieleBridgeHandler((Bridge) thing, httpClient);
|
||||||
registerApplianceDiscoveryService(handler);
|
registerApplianceDiscoveryService(handler);
|
||||||
return handler;
|
return handler;
|
||||||
} else if (MieleApplianceHandler.SUPPORTED_THING_TYPES.contains(thing.getThingTypeUID())) {
|
} else if (MieleApplianceHandler.SUPPORTED_THING_TYPES.contains(thing.getThingTypeUID())) {
|
||||||
|
|||||||
@ -127,7 +127,7 @@ public abstract class MieleApplianceHandler<E extends Enum<E> & ApplianceChannel
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void initialize() {
|
public void initialize() {
|
||||||
logger.debug("Initializing Miele appliance handler.");
|
logger.debug("Initializing handler for thing {}", getThing().getUID());
|
||||||
final String applianceId = (String) getThing().getConfiguration().getProperties().get(APPLIANCE_ID);
|
final String applianceId = (String) getThing().getConfiguration().getProperties().get(APPLIANCE_ID);
|
||||||
if (applianceId == null || applianceId.isBlank()) {
|
if (applianceId == null || applianceId.isBlank()) {
|
||||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.CONFIGURATION_ERROR,
|
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.CONFIGURATION_ERROR,
|
||||||
|
|||||||
@ -17,9 +17,9 @@ import static org.openhab.binding.miele.internal.MieleBindingConstants.*;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.DatagramPacket;
|
import java.net.DatagramPacket;
|
||||||
import java.net.InetAddress;
|
import java.net.InetAddress;
|
||||||
import java.net.MalformedURLException;
|
|
||||||
import java.net.MulticastSocket;
|
import java.net.MulticastSocket;
|
||||||
import java.net.SocketTimeoutException;
|
import java.net.SocketTimeoutException;
|
||||||
|
import java.net.URISyntaxException;
|
||||||
import java.net.UnknownHostException;
|
import java.net.UnknownHostException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.IllformedLocaleException;
|
import java.util.IllformedLocaleException;
|
||||||
@ -39,6 +39,7 @@ import java.util.regex.Pattern;
|
|||||||
|
|
||||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
import org.eclipse.jdt.annotation.Nullable;
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
|
import org.eclipse.jetty.client.HttpClient;
|
||||||
import org.openhab.binding.miele.internal.FullyQualifiedApplianceIdentifier;
|
import org.openhab.binding.miele.internal.FullyQualifiedApplianceIdentifier;
|
||||||
import org.openhab.binding.miele.internal.MieleGatewayCommunicationController;
|
import org.openhab.binding.miele.internal.MieleGatewayCommunicationController;
|
||||||
import org.openhab.binding.miele.internal.api.dto.DeviceClassObject;
|
import org.openhab.binding.miele.internal.api.dto.DeviceClassObject;
|
||||||
@ -87,7 +88,8 @@ public class MieleBridgeHandler extends BaseBridgeHandler {
|
|||||||
|
|
||||||
private boolean lastBridgeConnectionState = false;
|
private boolean lastBridgeConnectionState = false;
|
||||||
|
|
||||||
private Gson gson = new Gson();
|
private final HttpClient httpClient;
|
||||||
|
private final Gson gson = new Gson();
|
||||||
private @NonNullByDefault({}) MieleGatewayCommunicationController gatewayCommunication;
|
private @NonNullByDefault({}) MieleGatewayCommunicationController gatewayCommunication;
|
||||||
|
|
||||||
private Set<DiscoveryListener> discoveryListeners = ConcurrentHashMap.newKeySet();
|
private Set<DiscoveryListener> discoveryListeners = ConcurrentHashMap.newKeySet();
|
||||||
@ -99,21 +101,22 @@ public class MieleBridgeHandler extends BaseBridgeHandler {
|
|||||||
private Map<String, HomeDevice> cachedHomeDevicesByApplianceId = new ConcurrentHashMap<>();
|
private Map<String, HomeDevice> cachedHomeDevicesByApplianceId = new ConcurrentHashMap<>();
|
||||||
private Map<String, HomeDevice> cachedHomeDevicesByRemoteUid = new ConcurrentHashMap<>();
|
private Map<String, HomeDevice> cachedHomeDevicesByRemoteUid = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
public MieleBridgeHandler(Bridge bridge) {
|
public MieleBridgeHandler(Bridge bridge, HttpClient httpClient) {
|
||||||
super(bridge);
|
super(bridge);
|
||||||
|
this.httpClient = httpClient;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void initialize() {
|
public void initialize() {
|
||||||
logger.debug("Initializing the Miele bridge handler.");
|
logger.debug("Initializing handler for bridge {}", getThing().getUID());
|
||||||
|
|
||||||
if (!validateConfig(getConfig())) {
|
if (!validateConfig(getConfig())) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
gatewayCommunication = new MieleGatewayCommunicationController((String) getConfig().get(HOST));
|
gatewayCommunication = new MieleGatewayCommunicationController(httpClient, (String) getConfig().get(HOST));
|
||||||
} catch (MalformedURLException e) {
|
} catch (URISyntaxException e) {
|
||||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.CONFIGURATION_ERROR, e.getMessage());
|
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.CONFIGURATION_ERROR, e.getMessage());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user