[remoteopenhab] Introduce things for the remote things and add support for HTTPS (#8909)
* [remoteopenhab] Introduce things for the remote things Signed-off-by: Laurent Garnier <lg.hc@free.fr> * Adapt code to change in core Signed-off-by: Laurent Garnier <lg.hc@free.fr> * Review comment: scheduler.execute Signed-off-by: Laurent Garnier <lg.hc@free.fr> * Fixed tables in README Signed-off-by: Laurent Garnier <lg.hc@free.fr> * Review comment: ThingHandlerService Signed-off-by: Laurent Garnier <lg.hc@free.fr> * Add support for HTTPS communication Signed-off-by: Laurent Garnier <lg.hc@free.fr> * Few changes to satisfy build Signed-off-by: Laurent Garnier <lg.hc@free.fr> * Avoid SSE timeout exception after one minute when no event received Fix #8977 Signed-off-by: Laurent Garnier <lg.hc@free.fr> * Check connection job: do nothing when the thing was ONLINE and the REST API is still reachable Signed-off-by: Laurent Garnier <lg.hc@free.fr> * Review comment: super.deactivate Signed-off-by: Laurent Garnier <lg.hc@free.fr> * Review comments: README Signed-off-by: Laurent Garnier <lg.hc@free.fr> * Review comment: buildChannels parameter renamed Signed-off-by: Laurent Garnier <lg.hc@free.fr> * Imrpoved logging when the REST API is failing Signed-off-by: Laurent Garnier <lg.hc@free.fr> * Review comment: fasle Signed-off-by: Laurent Garnier <lg.hc@free.fr> * Review comment: deprecated "required" tag for channel parameter Signed-off-by: Laurent Garnier <lg.hc@free.fr>
This commit is contained in:
@@ -31,6 +31,14 @@ public class RemoteopenhabBindingConstants {
|
||||
|
||||
// List of all Thing Type UIDs
|
||||
public static final ThingTypeUID BRIDGE_TYPE_SERVER = new ThingTypeUID(BINDING_ID, "server");
|
||||
public static final ThingTypeUID THING_TYPE_THING = new ThingTypeUID(BINDING_ID, "thing");
|
||||
|
||||
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Collections.singleton(BRIDGE_TYPE_SERVER);
|
||||
// All supported Bridge types
|
||||
public static final Set<ThingTypeUID> SUPPORTED_BRIDGE_TYPES_UIDS = Collections.singleton(BRIDGE_TYPE_SERVER);
|
||||
|
||||
// All supported Thing types
|
||||
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Collections.singleton(THING_TYPE_THING);
|
||||
|
||||
// List of all channel types
|
||||
public static final String CHANNEL_TYPE_TRIGGER = "trigger";
|
||||
}
|
||||
|
||||
@@ -12,23 +12,42 @@
|
||||
*/
|
||||
package org.openhab.binding.remoteopenhab.internal;
|
||||
|
||||
import static org.openhab.binding.remoteopenhab.internal.RemoteopenhabBindingConstants.*;
|
||||
import java.net.Socket;
|
||||
import java.security.KeyManagementException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.SSLEngine;
|
||||
import javax.net.ssl.TrustManager;
|
||||
import javax.net.ssl.X509ExtendedTrustManager;
|
||||
import javax.ws.rs.client.ClientBuilder;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.eclipse.jetty.client.HttpClient;
|
||||
import org.openhab.binding.remoteopenhab.internal.handler.RemoteopenhabBridgeHandler;
|
||||
import org.openhab.binding.remoteopenhab.internal.handler.RemoteopenhabThingHandler;
|
||||
import org.openhab.core.config.core.Configuration;
|
||||
import org.openhab.core.io.net.http.HttpClientFactory;
|
||||
import org.openhab.core.thing.Bridge;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
import org.openhab.core.thing.ThingUID;
|
||||
import org.openhab.core.thing.binding.BaseThingHandlerFactory;
|
||||
import org.openhab.core.thing.binding.ThingHandler;
|
||||
import org.openhab.core.thing.binding.ThingHandlerFactory;
|
||||
import org.osgi.service.component.ComponentContext;
|
||||
import org.osgi.service.component.annotations.Activate;
|
||||
import org.osgi.service.component.annotations.Component;
|
||||
import org.osgi.service.component.annotations.Reference;
|
||||
import org.osgi.service.jaxrs.client.SseEventSourceFactory;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.google.gson.FieldNamingPolicy;
|
||||
import com.google.gson.Gson;
|
||||
@@ -44,22 +63,103 @@ import com.google.gson.GsonBuilder;
|
||||
@Component(service = ThingHandlerFactory.class, configurationPid = "binding.remoteopenhab")
|
||||
public class RemoteopenhabHandlerFactory extends BaseThingHandlerFactory {
|
||||
|
||||
private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Stream
|
||||
.concat(RemoteopenhabBindingConstants.SUPPORTED_BRIDGE_TYPES_UIDS.stream(),
|
||||
RemoteopenhabBindingConstants.SUPPORTED_THING_TYPES_UIDS.stream())
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(RemoteopenhabHandlerFactory.class);
|
||||
|
||||
private final HttpClient httpClient;
|
||||
private final ClientBuilder clientBuilder;
|
||||
private final SseEventSourceFactory eventSourceFactory;
|
||||
private final RemoteopenhabChannelTypeProvider channelTypeProvider;
|
||||
private final RemoteopenhabStateDescriptionOptionProvider stateDescriptionProvider;
|
||||
private final Gson jsonParser;
|
||||
|
||||
private HttpClient httpClientTrustingCert;
|
||||
|
||||
@Activate
|
||||
public RemoteopenhabHandlerFactory(final @Reference ClientBuilder clientBuilder,
|
||||
final @Reference SseEventSourceFactory eventSourceFactory,
|
||||
public RemoteopenhabHandlerFactory(final @Reference HttpClientFactory httpClientFactory,
|
||||
final @Reference ClientBuilder clientBuilder, final @Reference SseEventSourceFactory eventSourceFactory,
|
||||
final @Reference RemoteopenhabChannelTypeProvider channelTypeProvider,
|
||||
final @Reference RemoteopenhabStateDescriptionOptionProvider stateDescriptionProvider) {
|
||||
this.httpClient = httpClientFactory.getCommonHttpClient();
|
||||
this.httpClientTrustingCert = httpClientFactory.createHttpClient(RemoteopenhabBindingConstants.BINDING_ID);
|
||||
this.clientBuilder = clientBuilder;
|
||||
this.eventSourceFactory = eventSourceFactory;
|
||||
this.channelTypeProvider = channelTypeProvider;
|
||||
this.stateDescriptionProvider = stateDescriptionProvider;
|
||||
jsonParser = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.IDENTITY).create();
|
||||
this.jsonParser = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.IDENTITY).create();
|
||||
|
||||
try {
|
||||
SSLContext sslContext = SSLContext.getInstance("SSL");
|
||||
|
||||
TrustManager[] trustAllCerts = new TrustManager[] { new X509ExtendedTrustManager() {
|
||||
@Override
|
||||
public void checkClientTrusted(X509Certificate @Nullable [] chain, @Nullable String authType)
|
||||
throws CertificateException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkServerTrusted(X509Certificate @Nullable [] chain, @Nullable String authType)
|
||||
throws CertificateException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public X509Certificate @Nullable [] getAcceptedIssuers() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkClientTrusted(X509Certificate @Nullable [] chain, @Nullable String authType,
|
||||
@Nullable Socket socket) throws CertificateException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkServerTrusted(X509Certificate @Nullable [] chain, @Nullable String authType,
|
||||
@Nullable Socket socket) throws CertificateException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkClientTrusted(X509Certificate @Nullable [] chain, @Nullable String authType,
|
||||
@Nullable SSLEngine engine) throws CertificateException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkServerTrusted(X509Certificate @Nullable [] chain, @Nullable String authType,
|
||||
@Nullable SSLEngine engine) throws CertificateException {
|
||||
}
|
||||
} };
|
||||
sslContext.init(null, trustAllCerts, null);
|
||||
|
||||
this.httpClientTrustingCert.getSslContextFactory().setSslContext(sslContext);
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
logger.warn("An exception occurred while requesting the SSL encryption algorithm : '{}'", e.getMessage(),
|
||||
e);
|
||||
} catch (KeyManagementException e) {
|
||||
logger.warn("An exception occurred while initialising the SSL context : '{}'", e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void activate(ComponentContext componentContext) {
|
||||
super.activate(componentContext);
|
||||
try {
|
||||
httpClientTrustingCert.start();
|
||||
} catch (Exception e) {
|
||||
logger.warn("Unable to start Jetty HttpClient", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void deactivate(ComponentContext componentContext) {
|
||||
try {
|
||||
httpClientTrustingCert.stop();
|
||||
} catch (Exception e) {
|
||||
logger.warn("Unable to stop Jetty HttpClient", e);
|
||||
}
|
||||
super.deactivate(componentContext);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -70,16 +170,36 @@ public class RemoteopenhabHandlerFactory extends BaseThingHandlerFactory {
|
||||
return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Thing createThing(ThingTypeUID thingTypeUID, Configuration configuration,
|
||||
@Nullable ThingUID thingUID, @Nullable ThingUID bridgeUID) {
|
||||
if (thingTypeUID.equals(RemoteopenhabBindingConstants.BRIDGE_TYPE_SERVER)) {
|
||||
return super.createThing(thingTypeUID, configuration, thingUID, null);
|
||||
} else if (RemoteopenhabBindingConstants.SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID)) {
|
||||
ThingUID newThingUID;
|
||||
if (bridgeUID != null && thingUID != null) {
|
||||
newThingUID = new ThingUID(thingTypeUID, bridgeUID, thingUID.getId());
|
||||
} else {
|
||||
newThingUID = thingUID;
|
||||
}
|
||||
return super.createThing(thingTypeUID, configuration, newThingUID, bridgeUID);
|
||||
}
|
||||
throw new IllegalArgumentException(
|
||||
"The thing type " + thingTypeUID + " is not supported by the remote openHAB binding.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a handler for the specific thing.
|
||||
*/
|
||||
@Override
|
||||
protected @Nullable ThingHandler createHandler(Thing thing) {
|
||||
ThingTypeUID thingTypeUID = thing.getThingTypeUID();
|
||||
|
||||
return BRIDGE_TYPE_SERVER.equals(thingTypeUID)
|
||||
? new RemoteopenhabBridgeHandler((Bridge) thing, clientBuilder, eventSourceFactory, channelTypeProvider,
|
||||
stateDescriptionProvider, jsonParser)
|
||||
: null;
|
||||
if (thingTypeUID.equals(RemoteopenhabBindingConstants.BRIDGE_TYPE_SERVER)) {
|
||||
return new RemoteopenhabBridgeHandler((Bridge) thing, httpClient, httpClientTrustingCert, clientBuilder,
|
||||
eventSourceFactory, channelTypeProvider, stateDescriptionProvider, jsonParser);
|
||||
} else if (RemoteopenhabBindingConstants.SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID)) {
|
||||
return new RemoteopenhabThingHandler(thing);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,20 +15,22 @@ package org.openhab.binding.remoteopenhab.internal.config;
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* The {@link RemoteopenhabInstanceConfiguration} is responsible for holding
|
||||
* The {@link RemoteopenhabServerConfiguration} is responsible for holding
|
||||
* configuration informations associated to a remote openHAB server
|
||||
* thing type
|
||||
*
|
||||
* @author Laurent Garnier - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class RemoteopenhabInstanceConfiguration {
|
||||
public class RemoteopenhabServerConfiguration {
|
||||
public static final String HOST = "host";
|
||||
public static final String PORT = "port";
|
||||
public static final String REST_PATH = "restPath";
|
||||
|
||||
public String host = "";
|
||||
public boolean useHttps = false;
|
||||
public int port = 8080;
|
||||
public boolean trustedCertificate = false;
|
||||
public String restPath = "/rest";
|
||||
public String token = "";
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.remoteopenhab.internal.config;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* The {@link RemoteopenhabThingConfiguration} is responsible for holding
|
||||
* configuration informations associated to a remote openHAB thing
|
||||
* thing type
|
||||
*
|
||||
* @author Laurent Garnier - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class RemoteopenhabThingConfiguration {
|
||||
public static final String THING_UID = "thingUID";
|
||||
|
||||
public String thingUID = "";
|
||||
public boolean buildTriggerChannels = true;
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.remoteopenhab.internal.config;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* The {@link RemoteopenhabTriggerChannelConfiguration} is responsible for holding
|
||||
* configuration informations associated to a remote openHAB trigger channel
|
||||
*
|
||||
* @author Laurent Garnier - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class RemoteopenhabTriggerChannelConfiguration {
|
||||
public static final String CHANNEL_UID = "channelUID";
|
||||
|
||||
public String channelUID = "";
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.remoteopenhab.internal.data;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* Part of {@link RemoteopenhabThing} containing the channel definition
|
||||
*
|
||||
* @author Laurent Garnier - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class RemoteopenhabChannel {
|
||||
|
||||
public String uid = "";
|
||||
public String kind = "";
|
||||
public String label = "";
|
||||
public String description = "";
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.remoteopenhab.internal.data;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* Payload from ChannelTriggerEvent events received through the SSE connection.
|
||||
*
|
||||
* @author Laurent Garnier - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class RemoteopenhabChannelTriggerEvent {
|
||||
|
||||
public String channel = "";
|
||||
public @Nullable String event;
|
||||
}
|
||||
@@ -20,7 +20,7 @@ import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
* @author Laurent Garnier - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class Event {
|
||||
public class RemoteopenhabEvent {
|
||||
|
||||
public String type = "";
|
||||
public String topic = "";
|
||||
@@ -20,7 +20,7 @@ import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
* @author Laurent Garnier - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class EventPayload {
|
||||
public class RemoteopenhabEventPayload {
|
||||
|
||||
public String type = "";
|
||||
public String value = "";
|
||||
@@ -22,11 +22,11 @@ import org.eclipse.jdt.annotation.Nullable;
|
||||
* @author Laurent Garnier - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class Item {
|
||||
public class RemoteopenhabItem {
|
||||
|
||||
public String name = "";
|
||||
public String type = "";
|
||||
public String state = "";
|
||||
public String groupType = "";
|
||||
public @Nullable StateDescription stateDescription;
|
||||
public @Nullable RemoteopenhabStateDescription stateDescription;
|
||||
}
|
||||
@@ -19,9 +19,9 @@ import org.eclipse.jdt.annotation.Nullable;
|
||||
*
|
||||
* @author Laurent Garnier - Initial contribution
|
||||
*/
|
||||
public class RestApi {
|
||||
public class RemoteopenhabRestApi {
|
||||
|
||||
public String version;
|
||||
public RestApiEndpoint[] links;
|
||||
public @Nullable RuntimeInfo runtimeInfo;
|
||||
public RemoteopenhabRestApiEndpoint[] links;
|
||||
public @Nullable RemoteopenhabRuntimeInfo runtimeInfo;
|
||||
}
|
||||
@@ -20,7 +20,7 @@ import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
* @author Laurent Garnier - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class RestApiEndpoint {
|
||||
public class RemoteopenhabRestApiEndpoint {
|
||||
|
||||
public String type = "";
|
||||
public String url = "";
|
||||
@@ -20,7 +20,7 @@ import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
* @author Laurent Garnier - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class RuntimeInfo {
|
||||
public class RemoteopenhabRuntimeInfo {
|
||||
|
||||
public String version = "";
|
||||
public String buildString = "";
|
||||
@@ -18,14 +18,14 @@ import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* Part of {@link Item} containing the state description
|
||||
* Part of {@link RemoteopenhabItem} containing the state description
|
||||
*
|
||||
* @author Laurent Garnier - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class StateDescription {
|
||||
public class RemoteopenhabStateDescription {
|
||||
|
||||
public String pattern = "";
|
||||
public boolean readOnly;
|
||||
public @Nullable List<Option> options;
|
||||
public @Nullable List<RemoteopenhabStateOption> options;
|
||||
}
|
||||
@@ -15,12 +15,12 @@ package org.openhab.binding.remoteopenhab.internal.data;
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* Part of {@link StateDescription} containing one state option
|
||||
* Part of {@link RemoteopenhabStateDescription} containing one state option
|
||||
*
|
||||
* @author Laurent Garnier - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class Option {
|
||||
public class RemoteopenhabStateOption {
|
||||
|
||||
public String value = "";
|
||||
public String label = "";
|
||||
@@ -0,0 +1,29 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.remoteopenhab.internal.data;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* Response to the API GET /rest/things/{uid}/status
|
||||
* Also payload from ThingStatusInfoChangedEvent events received through the SSE connection.
|
||||
*
|
||||
* @author Laurent Garnier - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class RemoteopenhabStatusInfo {
|
||||
|
||||
public String status = "";
|
||||
public String statusDetail = "";
|
||||
public String description = "";
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.remoteopenhab.internal.data;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
/**
|
||||
* Response to the API GET /rest/things
|
||||
* Also payload from ThingAddedEvent / ThingRemovedEvent events received through the SSE connection.
|
||||
*
|
||||
* @author Laurent Garnier - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class RemoteopenhabThing {
|
||||
|
||||
public String label = "";
|
||||
@SerializedName("UID")
|
||||
public String uid = "";
|
||||
public @Nullable RemoteopenhabStatusInfo statusInfo;
|
||||
public List<RemoteopenhabChannel> channels = new ArrayList<>();
|
||||
}
|
||||
@@ -13,7 +13,7 @@
|
||||
package org.openhab.binding.remoteopenhab.internal.discovery;
|
||||
|
||||
import static org.openhab.binding.remoteopenhab.internal.RemoteopenhabBindingConstants.*;
|
||||
import static org.openhab.binding.remoteopenhab.internal.config.RemoteopenhabInstanceConfiguration.*;
|
||||
import static org.openhab.binding.remoteopenhab.internal.config.RemoteopenhabServerConfiguration.*;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -50,7 +50,7 @@ public class RemoteopenhabDiscoveryParticipant implements MDNSDiscoveryParticipa
|
||||
|
||||
@Override
|
||||
public Set<ThingTypeUID> getSupportedThingTypeUIDs() {
|
||||
return SUPPORTED_THING_TYPES_UIDS;
|
||||
return SUPPORTED_BRIDGE_TYPES_UIDS;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -89,7 +89,7 @@ public class RemoteopenhabDiscoveryParticipant implements MDNSDiscoveryParticipa
|
||||
ThingUID thingUID = getThingUID(service);
|
||||
if (thingUID != null && ip != null && restPath != null) {
|
||||
String label = "openHAB server";
|
||||
logger.debug("Created a DiscoveryResult for remote openHAB server {} with IP {}", thingUID, ip);
|
||||
logger.debug("Create a DiscoveryResult for remote openHAB server {} with IP {}", thingUID, ip);
|
||||
Map<String, Object> properties = Map.of(HOST, ip, REST_PATH, restPath);
|
||||
result = DiscoveryResultBuilder.create(thingUID).withProperties(properties).withRepresentationProperty(HOST)
|
||||
.withLabel(label).build();
|
||||
|
||||
@@ -0,0 +1,148 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.remoteopenhab.internal.discovery;
|
||||
|
||||
import static org.openhab.binding.remoteopenhab.internal.config.RemoteopenhabThingConfiguration.THING_UID;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.remoteopenhab.internal.RemoteopenhabBindingConstants;
|
||||
import org.openhab.binding.remoteopenhab.internal.data.RemoteopenhabStatusInfo;
|
||||
import org.openhab.binding.remoteopenhab.internal.data.RemoteopenhabThing;
|
||||
import org.openhab.binding.remoteopenhab.internal.exceptions.RemoteopenhabException;
|
||||
import org.openhab.binding.remoteopenhab.internal.handler.RemoteopenhabBridgeHandler;
|
||||
import org.openhab.binding.remoteopenhab.internal.listener.RemoteopenhabThingsDataListener;
|
||||
import org.openhab.binding.remoteopenhab.internal.rest.RemoteopenhabRestClient;
|
||||
import org.openhab.core.config.discovery.AbstractDiscoveryService;
|
||||
import org.openhab.core.config.discovery.DiscoveryResult;
|
||||
import org.openhab.core.config.discovery.DiscoveryResultBuilder;
|
||||
import org.openhab.core.thing.ThingStatus;
|
||||
import org.openhab.core.thing.ThingUID;
|
||||
import org.openhab.core.thing.binding.ThingHandler;
|
||||
import org.openhab.core.thing.binding.ThingHandlerService;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link RemoteopenhabDiscoveryService} is responsible for discovering all the remote things
|
||||
* available in the remote openHAB server.
|
||||
*
|
||||
* @author Laurent Garnier - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class RemoteopenhabDiscoveryService extends AbstractDiscoveryService
|
||||
implements ThingHandlerService, RemoteopenhabThingsDataListener {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(RemoteopenhabDiscoveryService.class);
|
||||
|
||||
private static final int SEARCH_TIME = 10;
|
||||
|
||||
private @NonNullByDefault({}) RemoteopenhabBridgeHandler bridgeHandler;
|
||||
private @NonNullByDefault({}) RemoteopenhabRestClient restClient;
|
||||
|
||||
public RemoteopenhabDiscoveryService() {
|
||||
super(RemoteopenhabBindingConstants.SUPPORTED_THING_TYPES_UIDS, SEARCH_TIME, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setThingHandler(ThingHandler handler) {
|
||||
if (handler instanceof RemoteopenhabBridgeHandler) {
|
||||
this.bridgeHandler = (RemoteopenhabBridgeHandler) handler;
|
||||
this.restClient = bridgeHandler.gestRestClient();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable ThingHandler getThingHandler() {
|
||||
return bridgeHandler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void activate() {
|
||||
ThingHandlerService.super.activate();
|
||||
restClient.addThingsDataListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deactivate() {
|
||||
restClient.removeThingsDataListener(this);
|
||||
super.deactivate();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void startScan() {
|
||||
logger.debug("Starting discovery scan for remote things");
|
||||
if (bridgeHandler.getThing().getStatus() == ThingStatus.ONLINE) {
|
||||
try {
|
||||
List<RemoteopenhabThing> things = restClient.getRemoteThings();
|
||||
ThingUID bridgeUID = bridgeHandler.getThing().getUID();
|
||||
for (RemoteopenhabThing thing : things) {
|
||||
createDiscoveryResult(thing, bridgeUID);
|
||||
}
|
||||
} catch (RemoteopenhabException e) {
|
||||
logger.debug("{}", e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected synchronized void stopScan() {
|
||||
super.stopScan();
|
||||
removeOlderResults(getTimestampOfLastScan(), bridgeHandler.getThing().getUID());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onThingStatusUpdated(String thingUID, RemoteopenhabStatusInfo statusInfo) {
|
||||
// Nothing to do
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onThingAdded(RemoteopenhabThing thing) {
|
||||
createDiscoveryResult(thing, bridgeHandler.getThing().getUID());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onThingRemoved(RemoteopenhabThing thing) {
|
||||
removeDiscoveryResult(thing, bridgeHandler.getThing().getUID());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChannelTriggered(String channelUID, @Nullable String event) {
|
||||
// Nothing to do
|
||||
}
|
||||
|
||||
private void createDiscoveryResult(RemoteopenhabThing thing, ThingUID bridgeUID) {
|
||||
ThingUID thingUID = buildThingUID(thing, bridgeUID);
|
||||
logger.debug("Create a DiscoveryResult for remote openHAB thing {} with thingUID setting {}", thingUID,
|
||||
thing.uid);
|
||||
Map<String, Object> properties = Map.of(THING_UID, thing.uid);
|
||||
DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID).withProperties(properties)
|
||||
.withRepresentationProperty(THING_UID).withBridge(bridgeUID).withLabel(thing.label).build();
|
||||
thingDiscovered(discoveryResult);
|
||||
}
|
||||
|
||||
private void removeDiscoveryResult(RemoteopenhabThing thing, ThingUID bridgeUID) {
|
||||
ThingUID thingUID = buildThingUID(thing, bridgeUID);
|
||||
logger.debug("Remove a DiscoveryResult for remote openHAB thing {} with thingUID setting {}", thingUID,
|
||||
thing.uid);
|
||||
thingRemoved(thingUID);
|
||||
}
|
||||
|
||||
private ThingUID buildThingUID(RemoteopenhabThing thing, ThingUID bridgeUID) {
|
||||
return new ThingUID(RemoteopenhabBindingConstants.THING_TYPE_THING, bridgeUID,
|
||||
thing.uid.replaceAll("[^A-Za-z0-9_]", "_"));
|
||||
}
|
||||
}
|
||||
@@ -20,6 +20,7 @@ import java.time.ZonedDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.format.DateTimeParseException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
@@ -30,13 +31,16 @@ import javax.ws.rs.client.ClientBuilder;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.eclipse.jetty.client.HttpClient;
|
||||
import org.openhab.binding.remoteopenhab.internal.RemoteopenhabChannelTypeProvider;
|
||||
import org.openhab.binding.remoteopenhab.internal.RemoteopenhabStateDescriptionOptionProvider;
|
||||
import org.openhab.binding.remoteopenhab.internal.config.RemoteopenhabInstanceConfiguration;
|
||||
import org.openhab.binding.remoteopenhab.internal.data.Item;
|
||||
import org.openhab.binding.remoteopenhab.internal.data.Option;
|
||||
import org.openhab.binding.remoteopenhab.internal.data.StateDescription;
|
||||
import org.openhab.binding.remoteopenhab.internal.config.RemoteopenhabServerConfiguration;
|
||||
import org.openhab.binding.remoteopenhab.internal.data.RemoteopenhabItem;
|
||||
import org.openhab.binding.remoteopenhab.internal.data.RemoteopenhabStateDescription;
|
||||
import org.openhab.binding.remoteopenhab.internal.data.RemoteopenhabStateOption;
|
||||
import org.openhab.binding.remoteopenhab.internal.discovery.RemoteopenhabDiscoveryService;
|
||||
import org.openhab.binding.remoteopenhab.internal.exceptions.RemoteopenhabException;
|
||||
import org.openhab.binding.remoteopenhab.internal.listener.RemoteopenhabItemsDataListener;
|
||||
import org.openhab.binding.remoteopenhab.internal.listener.RemoteopenhabStreamingDataListener;
|
||||
import org.openhab.binding.remoteopenhab.internal.rest.RemoteopenhabRestClient;
|
||||
import org.openhab.core.library.CoreItemFactory;
|
||||
@@ -58,6 +62,7 @@ import org.openhab.core.thing.ChannelUID;
|
||||
import org.openhab.core.thing.ThingStatus;
|
||||
import org.openhab.core.thing.ThingStatusDetail;
|
||||
import org.openhab.core.thing.binding.BaseBridgeHandler;
|
||||
import org.openhab.core.thing.binding.ThingHandlerService;
|
||||
import org.openhab.core.thing.binding.builder.ChannelBuilder;
|
||||
import org.openhab.core.thing.binding.builder.ThingBuilder;
|
||||
import org.openhab.core.thing.type.AutoUpdatePolicy;
|
||||
@@ -85,7 +90,8 @@ import com.google.gson.Gson;
|
||||
* @author Laurent Garnier - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class RemoteopenhabBridgeHandler extends BaseBridgeHandler implements RemoteopenhabStreamingDataListener {
|
||||
public class RemoteopenhabBridgeHandler extends BaseBridgeHandler
|
||||
implements RemoteopenhabStreamingDataListener, RemoteopenhabItemsDataListener {
|
||||
|
||||
private static final String DATE_FORMAT_PATTERN = "yyyy-MM-dd'T'HH:mm:ss.SSSZ";
|
||||
private static final DateTimeFormatter FORMATTER_DATE = DateTimeFormatter.ofPattern(DATE_FORMAT_PATTERN);
|
||||
@@ -95,35 +101,33 @@ public class RemoteopenhabBridgeHandler extends BaseBridgeHandler implements Rem
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(RemoteopenhabBridgeHandler.class);
|
||||
|
||||
private final ClientBuilder clientBuilder;
|
||||
private final SseEventSourceFactory eventSourceFactory;
|
||||
private final HttpClient httpClientTrustingCert;
|
||||
private final RemoteopenhabChannelTypeProvider channelTypeProvider;
|
||||
private final RemoteopenhabStateDescriptionOptionProvider stateDescriptionProvider;
|
||||
private final Gson jsonParser;
|
||||
|
||||
private final Object updateThingLock = new Object();
|
||||
|
||||
private @NonNullByDefault({}) RemoteopenhabInstanceConfiguration config;
|
||||
private @NonNullByDefault({}) RemoteopenhabServerConfiguration config;
|
||||
|
||||
private @Nullable ScheduledFuture<?> checkConnectionJob;
|
||||
private @Nullable RemoteopenhabRestClient restClient;
|
||||
private RemoteopenhabRestClient restClient;
|
||||
|
||||
public RemoteopenhabBridgeHandler(Bridge bridge, ClientBuilder clientBuilder,
|
||||
SseEventSourceFactory eventSourceFactory, RemoteopenhabChannelTypeProvider channelTypeProvider,
|
||||
public RemoteopenhabBridgeHandler(Bridge bridge, HttpClient httpClient, HttpClient httpClientTrustingCert,
|
||||
ClientBuilder clientBuilder, SseEventSourceFactory eventSourceFactory,
|
||||
RemoteopenhabChannelTypeProvider channelTypeProvider,
|
||||
RemoteopenhabStateDescriptionOptionProvider stateDescriptionProvider, final Gson jsonParser) {
|
||||
super(bridge);
|
||||
this.clientBuilder = clientBuilder;
|
||||
this.eventSourceFactory = eventSourceFactory;
|
||||
this.httpClientTrustingCert = httpClientTrustingCert;
|
||||
this.channelTypeProvider = channelTypeProvider;
|
||||
this.stateDescriptionProvider = stateDescriptionProvider;
|
||||
this.jsonParser = jsonParser;
|
||||
this.restClient = new RemoteopenhabRestClient(httpClient, clientBuilder, eventSourceFactory, jsonParser);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
logger.debug("Initializing remote openHAB handler for bridge {}", getThing().getUID());
|
||||
|
||||
config = getConfigAs(RemoteopenhabInstanceConfiguration.class);
|
||||
config = getConfigAs(RemoteopenhabServerConfiguration.class);
|
||||
|
||||
String host = config.host.trim();
|
||||
if (host.length() == 0) {
|
||||
@@ -147,7 +151,7 @@ public class RemoteopenhabBridgeHandler extends BaseBridgeHandler implements Rem
|
||||
}
|
||||
URL url;
|
||||
try {
|
||||
url = new URL("http", host, config.port, path);
|
||||
url = new URL(config.useHttps ? "https" : "http", host, config.port, path);
|
||||
} catch (MalformedURLException e) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
|
||||
"Invalid REST URL built from the settings in the thing configuration");
|
||||
@@ -160,13 +164,16 @@ public class RemoteopenhabBridgeHandler extends BaseBridgeHandler implements Rem
|
||||
}
|
||||
logger.debug("REST URL = {}", urlStr);
|
||||
|
||||
RemoteopenhabRestClient client = new RemoteopenhabRestClient(clientBuilder, eventSourceFactory, jsonParser,
|
||||
config.token, urlStr);
|
||||
restClient = client;
|
||||
restClient.setRestUrl(urlStr);
|
||||
restClient.setAccessToken(config.token);
|
||||
if (config.useHttps && config.trustedCertificate) {
|
||||
restClient.setHttpClient(httpClientTrustingCert);
|
||||
restClient.setTrustedCertificate(true);
|
||||
}
|
||||
|
||||
updateStatus(ThingStatus.UNKNOWN);
|
||||
|
||||
startCheckConnectionJob(client);
|
||||
startCheckConnectionJob();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -174,7 +181,6 @@ public class RemoteopenhabBridgeHandler extends BaseBridgeHandler implements Rem
|
||||
logger.debug("Disposing remote openHAB handler for bridge {}", getThing().getUID());
|
||||
stopStreamingUpdates();
|
||||
stopCheckConnectionJob();
|
||||
this.restClient = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -182,17 +188,13 @@ public class RemoteopenhabBridgeHandler extends BaseBridgeHandler implements Rem
|
||||
if (getThing().getStatus() != ThingStatus.ONLINE) {
|
||||
return;
|
||||
}
|
||||
RemoteopenhabRestClient client = restClient;
|
||||
if (client == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
if (command instanceof RefreshType) {
|
||||
String state = client.getRemoteItemState(channelUID.getId());
|
||||
String state = restClient.getRemoteItemState(channelUID.getId());
|
||||
updateChannelState(channelUID.getId(), null, state);
|
||||
} else if (isLinked(channelUID)) {
|
||||
client.sendCommandToRemoteItem(channelUID.getId(), command);
|
||||
restClient.sendCommandToRemoteItem(channelUID.getId(), command);
|
||||
String commandStr = command.toFullString();
|
||||
logger.debug("Sending command {} to remote item {} succeeded",
|
||||
commandStr.length() < MAX_STATE_SIZE_FOR_LOGGING ? commandStr
|
||||
@@ -204,11 +206,11 @@ public class RemoteopenhabBridgeHandler extends BaseBridgeHandler implements Rem
|
||||
}
|
||||
}
|
||||
|
||||
private void createChannels(List<Item> items, boolean replace) {
|
||||
private void createChannels(List<RemoteopenhabItem> items, boolean replace) {
|
||||
synchronized (updateThingLock) {
|
||||
int nbGroups = 0;
|
||||
List<Channel> channels = new ArrayList<>();
|
||||
for (Item item : items) {
|
||||
for (RemoteopenhabItem item : items) {
|
||||
String itemType = item.type;
|
||||
boolean readOnly = false;
|
||||
if ("Group".equals(itemType)) {
|
||||
@@ -279,11 +281,11 @@ public class RemoteopenhabBridgeHandler extends BaseBridgeHandler implements Rem
|
||||
}
|
||||
}
|
||||
|
||||
private void removeChannels(List<Item> items) {
|
||||
private void removeChannels(List<RemoteopenhabItem> items) {
|
||||
synchronized (updateThingLock) {
|
||||
int nbRemoved = 0;
|
||||
ThingBuilder thingBuilder = editThing();
|
||||
for (Item item : items) {
|
||||
for (RemoteopenhabItem item : items) {
|
||||
Channel channel = getThing().getChannel(item.name);
|
||||
if (channel != null) {
|
||||
thingBuilder.withoutChannel(channel.getUID());
|
||||
@@ -298,14 +300,14 @@ public class RemoteopenhabBridgeHandler extends BaseBridgeHandler implements Rem
|
||||
}
|
||||
}
|
||||
|
||||
private void setStateOptions(List<Item> items) {
|
||||
for (Item item : items) {
|
||||
private void setStateOptions(List<RemoteopenhabItem> items) {
|
||||
for (RemoteopenhabItem item : items) {
|
||||
Channel channel = getThing().getChannel(item.name);
|
||||
StateDescription descr = item.stateDescription;
|
||||
List<Option> options = descr == null ? null : descr.options;
|
||||
RemoteopenhabStateDescription descr = item.stateDescription;
|
||||
List<RemoteopenhabStateOption> options = descr == null ? null : descr.options;
|
||||
if (channel != null && options != null && options.size() > 0) {
|
||||
List<StateOption> stateOptions = new ArrayList<>();
|
||||
for (Option option : options) {
|
||||
for (RemoteopenhabStateOption option : options) {
|
||||
stateOptions.add(new StateOption(option.value, option.label));
|
||||
}
|
||||
stateDescriptionProvider.setStateOptions(channel.getUID(), stateOptions);
|
||||
@@ -314,19 +316,19 @@ public class RemoteopenhabBridgeHandler extends BaseBridgeHandler implements Rem
|
||||
}
|
||||
}
|
||||
|
||||
public void checkConnection(RemoteopenhabRestClient client) {
|
||||
public void checkConnection() {
|
||||
logger.debug("Try the root REST API...");
|
||||
try {
|
||||
client.tryApi();
|
||||
if (client.getRestApiVersion() == null) {
|
||||
restClient.tryApi();
|
||||
if (restClient.getRestApiVersion() == null) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
|
||||
"OH 1.x server not supported by the binding");
|
||||
} else {
|
||||
List<Item> items = client.getRemoteItems();
|
||||
} else if (getThing().getStatus() != ThingStatus.ONLINE) {
|
||||
List<RemoteopenhabItem> items = restClient.getRemoteItems();
|
||||
|
||||
createChannels(items, true);
|
||||
setStateOptions(items);
|
||||
for (Item item : items) {
|
||||
for (RemoteopenhabItem item : items) {
|
||||
updateChannelState(item.name, null, item.state);
|
||||
}
|
||||
|
||||
@@ -335,20 +337,21 @@ public class RemoteopenhabBridgeHandler extends BaseBridgeHandler implements Rem
|
||||
restartStreamingUpdates();
|
||||
}
|
||||
} catch (RemoteopenhabException e) {
|
||||
logger.debug("{}", e.getMessage());
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
|
||||
stopStreamingUpdates();
|
||||
}
|
||||
}
|
||||
|
||||
private void startCheckConnectionJob(RemoteopenhabRestClient client) {
|
||||
private void startCheckConnectionJob() {
|
||||
ScheduledFuture<?> localCheckConnectionJob = checkConnectionJob;
|
||||
if (localCheckConnectionJob == null || localCheckConnectionJob.isCancelled()) {
|
||||
checkConnectionJob = scheduler.scheduleWithFixedDelay(() -> {
|
||||
long millisSinceLastEvent = System.currentTimeMillis() - client.getLastEventTimestamp();
|
||||
long millisSinceLastEvent = System.currentTimeMillis() - restClient.getLastEventTimestamp();
|
||||
if (millisSinceLastEvent > CONNECTION_TIMEOUT_MILLIS) {
|
||||
logger.debug("Check: Disconnected from streaming events, millisSinceLastEvent={}",
|
||||
logger.debug("Check: Maybe disconnected from streaming events, millisSinceLastEvent={}",
|
||||
millisSinceLastEvent);
|
||||
checkConnection(client);
|
||||
checkConnection();
|
||||
} else {
|
||||
logger.debug("Check: Receiving streaming events, millisSinceLastEvent={}", millisSinceLastEvent);
|
||||
}
|
||||
@@ -365,35 +368,32 @@ public class RemoteopenhabBridgeHandler extends BaseBridgeHandler implements Rem
|
||||
}
|
||||
|
||||
private void restartStreamingUpdates() {
|
||||
RemoteopenhabRestClient client = restClient;
|
||||
if (client != null) {
|
||||
synchronized (client) {
|
||||
stopStreamingUpdates();
|
||||
startStreamingUpdates();
|
||||
}
|
||||
synchronized (restClient) {
|
||||
stopStreamingUpdates();
|
||||
startStreamingUpdates();
|
||||
}
|
||||
}
|
||||
|
||||
private void startStreamingUpdates() {
|
||||
RemoteopenhabRestClient client = restClient;
|
||||
if (client != null) {
|
||||
synchronized (client) {
|
||||
client.addStreamingDataListener(this);
|
||||
client.start();
|
||||
}
|
||||
synchronized (restClient) {
|
||||
restClient.addStreamingDataListener(this);
|
||||
restClient.addItemsDataListener(this);
|
||||
restClient.start();
|
||||
}
|
||||
}
|
||||
|
||||
private void stopStreamingUpdates() {
|
||||
RemoteopenhabRestClient client = restClient;
|
||||
if (client != null) {
|
||||
synchronized (client) {
|
||||
client.stop();
|
||||
client.removeStreamingDataListener(this);
|
||||
}
|
||||
synchronized (restClient) {
|
||||
restClient.stop();
|
||||
restClient.removeStreamingDataListener(this);
|
||||
restClient.removeItemsDataListener(this);
|
||||
}
|
||||
}
|
||||
|
||||
public RemoteopenhabRestClient gestRestClient() {
|
||||
return restClient;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConnected() {
|
||||
updateStatus(ThingStatus.ONLINE);
|
||||
@@ -410,17 +410,17 @@ public class RemoteopenhabBridgeHandler extends BaseBridgeHandler implements Rem
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onItemAdded(Item item) {
|
||||
public void onItemAdded(RemoteopenhabItem item) {
|
||||
createChannels(List.of(item), false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onItemRemoved(Item item) {
|
||||
public void onItemRemoved(RemoteopenhabItem item) {
|
||||
removeChannels(List.of(item));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onItemUpdated(Item newItem, Item oldItem) {
|
||||
public void onItemUpdated(RemoteopenhabItem newItem, RemoteopenhabItem oldItem) {
|
||||
if (!newItem.type.equals(oldItem.type)) {
|
||||
createChannels(List.of(newItem), false);
|
||||
} else {
|
||||
@@ -550,7 +550,6 @@ public class RemoteopenhabBridgeHandler extends BaseBridgeHandler implements Rem
|
||||
logger.debug("updateState {} with {}", channel.getUID(),
|
||||
channelStateStr.length() < MAX_STATE_SIZE_FOR_LOGGING ? channelStateStr
|
||||
: channelStateStr.substring(0, MAX_STATE_SIZE_FOR_LOGGING) + "...");
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -562,4 +561,9 @@ public class RemoteopenhabBridgeHandler extends BaseBridgeHandler implements Rem
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Class<? extends ThingHandlerService>> getServices() {
|
||||
return Collections.singleton(RemoteopenhabDiscoveryService.class);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,234 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.remoteopenhab.internal.handler;
|
||||
|
||||
import static org.openhab.binding.remoteopenhab.internal.RemoteopenhabBindingConstants.*;
|
||||
import static org.openhab.binding.remoteopenhab.internal.config.RemoteopenhabTriggerChannelConfiguration.CHANNEL_UID;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.remoteopenhab.internal.config.RemoteopenhabThingConfiguration;
|
||||
import org.openhab.binding.remoteopenhab.internal.data.RemoteopenhabChannel;
|
||||
import org.openhab.binding.remoteopenhab.internal.data.RemoteopenhabStatusInfo;
|
||||
import org.openhab.binding.remoteopenhab.internal.data.RemoteopenhabThing;
|
||||
import org.openhab.binding.remoteopenhab.internal.exceptions.RemoteopenhabException;
|
||||
import org.openhab.binding.remoteopenhab.internal.listener.RemoteopenhabThingsDataListener;
|
||||
import org.openhab.binding.remoteopenhab.internal.rest.RemoteopenhabRestClient;
|
||||
import org.openhab.core.config.core.Configuration;
|
||||
import org.openhab.core.thing.Bridge;
|
||||
import org.openhab.core.thing.Channel;
|
||||
import org.openhab.core.thing.ChannelUID;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.ThingStatus;
|
||||
import org.openhab.core.thing.ThingStatusDetail;
|
||||
import org.openhab.core.thing.ThingStatusInfo;
|
||||
import org.openhab.core.thing.binding.BaseThingHandler;
|
||||
import org.openhab.core.thing.binding.BridgeHandler;
|
||||
import org.openhab.core.thing.binding.builder.ChannelBuilder;
|
||||
import org.openhab.core.thing.binding.builder.ThingBuilder;
|
||||
import org.openhab.core.thing.type.ChannelKind;
|
||||
import org.openhab.core.thing.type.ChannelTypeUID;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link RemoteopenhabThingHandler} is responsible for handling status updates associated to
|
||||
* any remote thing.
|
||||
*
|
||||
* @author Laurent Garnier - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class RemoteopenhabThingHandler extends BaseThingHandler implements RemoteopenhabThingsDataListener {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(RemoteopenhabThingHandler.class);
|
||||
|
||||
private @Nullable RemoteopenhabRestClient restClient;
|
||||
|
||||
private @NonNullByDefault({}) RemoteopenhabThingConfiguration config;
|
||||
|
||||
public RemoteopenhabThingHandler(Thing thing) {
|
||||
super(thing);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleCommand(ChannelUID channelUID, Command command) {
|
||||
// No state channel defined for this thing type and so no command to handle
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
logger.debug("initializing remote openHAB handler for thing {}", getThing().getUID());
|
||||
Bridge bridge = getBridge();
|
||||
initializeThing(bridge != null ? bridge.getStatus() : null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bridgeStatusChanged(ThingStatusInfo bridgeStatusInfo) {
|
||||
logger.debug("bridgeStatusChanged {} for thing {}", bridgeStatusInfo, getThing().getUID());
|
||||
initializeThing(bridgeStatusInfo.getStatus());
|
||||
}
|
||||
|
||||
private void initializeThing(@Nullable ThingStatus bridgeStatus) {
|
||||
Bridge bridge = getBridge();
|
||||
BridgeHandler bridgeHandler = bridge != null ? bridge.getHandler() : null;
|
||||
RemoteopenhabRestClient oldClient = this.restClient;
|
||||
if (oldClient != null) {
|
||||
oldClient.removeThingsDataListener(this);
|
||||
this.restClient = null;
|
||||
}
|
||||
if (bridgeHandler != null && bridgeStatus != null) {
|
||||
if (bridgeStatus == ThingStatus.ONLINE) {
|
||||
config = getConfigAs(RemoteopenhabThingConfiguration.class);
|
||||
|
||||
String uid = getConfigThingUID();
|
||||
if (uid.length() == 0) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
|
||||
"Undefined thing UID setting in the thing configuration");
|
||||
} else {
|
||||
RemoteopenhabRestClient client = ((RemoteopenhabBridgeHandler) bridgeHandler).gestRestClient();
|
||||
client.addThingsDataListener(this);
|
||||
this.restClient = client;
|
||||
|
||||
updateStatus(ThingStatus.UNKNOWN);
|
||||
|
||||
scheduler.execute(() -> {
|
||||
try {
|
||||
RemoteopenhabThing thing = client.getRemoteThing(uid);
|
||||
createTriggerChannels(thing, config.buildTriggerChannels);
|
||||
RemoteopenhabStatusInfo statusInfo = thing.statusInfo;
|
||||
if (statusInfo != null) {
|
||||
updateThingStatus(uid, statusInfo);
|
||||
}
|
||||
} catch (RemoteopenhabException e) {
|
||||
logger.debug("{}", e.getMessage());
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
|
||||
}
|
||||
});
|
||||
}
|
||||
} else {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE);
|
||||
}
|
||||
} else {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_UNINITIALIZED);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
logger.debug("Disposing remote openHAB handler for thing {}", getThing().getUID());
|
||||
RemoteopenhabRestClient client = this.restClient;
|
||||
if (client != null) {
|
||||
client.removeThingsDataListener(this);
|
||||
this.restClient = null;
|
||||
}
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
private String getConfigThingUID() {
|
||||
return config.thingUID.trim();
|
||||
}
|
||||
|
||||
private void createTriggerChannels(RemoteopenhabThing thing, boolean addNewChannels) {
|
||||
List<Channel> channels = new ArrayList<>();
|
||||
for (RemoteopenhabChannel channelDTO : thing.channels) {
|
||||
if (!"TRIGGER".equals(channelDTO.kind)) {
|
||||
continue;
|
||||
}
|
||||
ChannelTypeUID channelTypeUID = new ChannelTypeUID(BINDING_ID, CHANNEL_TYPE_TRIGGER);
|
||||
ChannelUID channelUID = new ChannelUID(getThing().getUID(),
|
||||
channelDTO.uid.replaceAll("[^A-Za-z0-9_]", "_"));
|
||||
Configuration channelConfig = new Configuration();
|
||||
channelConfig.put(CHANNEL_UID, channelDTO.uid);
|
||||
logger.trace("Create the channel {} of type {}", channelUID, channelTypeUID);
|
||||
channels.add(ChannelBuilder.create(channelUID, null).withType(channelTypeUID).withKind(ChannelKind.TRIGGER)
|
||||
.withLabel(channelDTO.label).withDescription(channelDTO.description)
|
||||
.withConfiguration(channelConfig).build());
|
||||
}
|
||||
if (channels.size() > 0) {
|
||||
ThingBuilder thingBuilder = editThing();
|
||||
int nbRemoved = 0;
|
||||
for (Channel channel : channels) {
|
||||
if (getThing().getChannel(channel.getUID()) != null) {
|
||||
thingBuilder.withoutChannel(channel.getUID());
|
||||
nbRemoved++;
|
||||
}
|
||||
}
|
||||
if (nbRemoved > 0) {
|
||||
logger.debug("{} trigger channels removed for the thing {}", nbRemoved, getThing().getUID());
|
||||
}
|
||||
int nbAdded = 0;
|
||||
if (addNewChannels) {
|
||||
for (Channel channel : channels) {
|
||||
thingBuilder.withChannel(channel);
|
||||
}
|
||||
nbAdded = channels.size();
|
||||
logger.debug("{} trigger channels added for the thing {}", nbAdded, getThing().getUID());
|
||||
}
|
||||
if (nbRemoved > 0 || nbAdded > 0) {
|
||||
updateThing(thingBuilder.build());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onThingStatusUpdated(String thingUID, RemoteopenhabStatusInfo statusInfo) {
|
||||
if (thingUID.equals(getConfigThingUID())) {
|
||||
updateThingStatus(thingUID, statusInfo);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onThingAdded(RemoteopenhabThing thing) {
|
||||
// Nothing to do
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onThingRemoved(RemoteopenhabThing thing) {
|
||||
// Nothing to do
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChannelTriggered(String channelUID, @Nullable String event) {
|
||||
String thingUID = channelUID.substring(0, channelUID.lastIndexOf(":"));
|
||||
if (thingUID.equals(getConfigThingUID())) {
|
||||
for (Channel channel : getThing().getChannels()) {
|
||||
if (channel.getKind() == ChannelKind.TRIGGER
|
||||
&& channelUID.equals(channel.getConfiguration().get(CHANNEL_UID))) {
|
||||
if (event == null) {
|
||||
triggerChannel(channel.getUID());
|
||||
logger.debug("triggerChannel {}", channel.getUID());
|
||||
} else {
|
||||
triggerChannel(channel.getUID(), event);
|
||||
logger.debug("triggerChannel {} with event {}", channel.getUID(), event);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void updateThingStatus(String thingUID, RemoteopenhabStatusInfo statusInfo) {
|
||||
ThingStatus status = ThingStatus.valueOf(statusInfo.status);
|
||||
// All remote status different from UNKNOWN or ONLINE or OFFLINE is considered as OFFLINE
|
||||
if (status != ThingStatus.UNKNOWN && status != ThingStatus.ONLINE && status != ThingStatus.OFFLINE) {
|
||||
status = ThingStatus.OFFLINE;
|
||||
}
|
||||
ThingStatusDetail detail = ThingStatusDetail.valueOf(statusInfo.statusDetail);
|
||||
updateStatus(status, detail, statusInfo.description);
|
||||
logger.debug("updateStatus {} with status {} detail {} description {}", thingUID, status, detail,
|
||||
statusInfo.description);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.remoteopenhab.internal.listener;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.remoteopenhab.internal.data.RemoteopenhabItem;
|
||||
import org.openhab.binding.remoteopenhab.internal.rest.RemoteopenhabRestClient;
|
||||
|
||||
/**
|
||||
* Interface for listeners of events relative to items generated by the {@link RemoteopenhabRestClient}.
|
||||
*
|
||||
* @author Laurent Garnier - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public interface RemoteopenhabItemsDataListener {
|
||||
|
||||
/**
|
||||
* A new ItemStateEvent was published.
|
||||
*/
|
||||
void onItemStateEvent(String itemName, String stateType, String state);
|
||||
|
||||
/**
|
||||
* A new ItemAddedEvent was published.
|
||||
*/
|
||||
void onItemAdded(RemoteopenhabItem item);
|
||||
|
||||
/**
|
||||
* A new ItemRemovedEvent was published.
|
||||
*/
|
||||
void onItemRemoved(RemoteopenhabItem item);
|
||||
|
||||
/**
|
||||
* A new ItemUpdatedEvent was published.
|
||||
*/
|
||||
void onItemUpdated(RemoteopenhabItem newItem, RemoteopenhabItem oldItem);
|
||||
}
|
||||
@@ -13,11 +13,10 @@
|
||||
package org.openhab.binding.remoteopenhab.internal.listener;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.remoteopenhab.internal.data.Item;
|
||||
import org.openhab.binding.remoteopenhab.internal.rest.RemoteopenhabRestClient;
|
||||
|
||||
/**
|
||||
* Interface for listeners of events generated by the {@link RemoteopenhabRestClient}.
|
||||
* Interface for listeners of general operating events generated by the {@link RemoteopenhabRestClient}.
|
||||
*
|
||||
* @author Laurent Garnier - Initial contribution
|
||||
*/
|
||||
@@ -33,24 +32,4 @@ public interface RemoteopenhabStreamingDataListener {
|
||||
* An error message was published.
|
||||
*/
|
||||
void onError(String message);
|
||||
|
||||
/**
|
||||
* A new ItemStateEvent was published.
|
||||
*/
|
||||
void onItemStateEvent(String itemName, String stateType, String state);
|
||||
|
||||
/**
|
||||
* A new ItemAddedEvent was published.
|
||||
*/
|
||||
void onItemAdded(Item item);
|
||||
|
||||
/**
|
||||
* A new ItemRemovedEvent was published.
|
||||
*/
|
||||
void onItemRemoved(Item item);
|
||||
|
||||
/**
|
||||
* A new ItemUpdatedEvent was published.
|
||||
*/
|
||||
void onItemUpdated(Item newItem, Item oldItem);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.remoteopenhab.internal.listener;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.remoteopenhab.internal.data.RemoteopenhabStatusInfo;
|
||||
import org.openhab.binding.remoteopenhab.internal.data.RemoteopenhabThing;
|
||||
import org.openhab.binding.remoteopenhab.internal.rest.RemoteopenhabRestClient;
|
||||
|
||||
/**
|
||||
* Interface for listeners of events relative to things generated by the {@link RemoteopenhabRestClient}.
|
||||
*
|
||||
* @author Laurent Garnier - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public interface RemoteopenhabThingsDataListener {
|
||||
|
||||
/**
|
||||
* A new ThingStatusInfoChangedEvent was published.
|
||||
*/
|
||||
void onThingStatusUpdated(String thingUID, RemoteopenhabStatusInfo statusInfo);
|
||||
|
||||
/**
|
||||
* A new ThingAddedEvent was published.
|
||||
*/
|
||||
void onThingAdded(RemoteopenhabThing thing);
|
||||
|
||||
/**
|
||||
* A new ThingRemovedEvent was published.
|
||||
*/
|
||||
void onThingRemoved(RemoteopenhabThing thing);
|
||||
|
||||
/**
|
||||
* A new ChannelTriggeredEvent was published.
|
||||
*/
|
||||
void onChannelTriggered(String channelUID, @Nullable String event);
|
||||
}
|
||||
@@ -17,12 +17,15 @@ import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Properties;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import javax.net.ssl.HostnameVerifier;
|
||||
import javax.net.ssl.SSLSession;
|
||||
import javax.ws.rs.client.Client;
|
||||
import javax.ws.rs.client.ClientBuilder;
|
||||
import javax.ws.rs.core.HttpHeaders;
|
||||
@@ -31,13 +34,23 @@ import javax.ws.rs.sse.SseEventSource;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.remoteopenhab.internal.data.Event;
|
||||
import org.openhab.binding.remoteopenhab.internal.data.EventPayload;
|
||||
import org.openhab.binding.remoteopenhab.internal.data.Item;
|
||||
import org.openhab.binding.remoteopenhab.internal.data.RestApi;
|
||||
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.InputStreamContentProvider;
|
||||
import org.eclipse.jetty.http.HttpMethod;
|
||||
import org.eclipse.jetty.http.HttpStatus;
|
||||
import org.openhab.binding.remoteopenhab.internal.data.RemoteopenhabChannelTriggerEvent;
|
||||
import org.openhab.binding.remoteopenhab.internal.data.RemoteopenhabEvent;
|
||||
import org.openhab.binding.remoteopenhab.internal.data.RemoteopenhabEventPayload;
|
||||
import org.openhab.binding.remoteopenhab.internal.data.RemoteopenhabItem;
|
||||
import org.openhab.binding.remoteopenhab.internal.data.RemoteopenhabRestApi;
|
||||
import org.openhab.binding.remoteopenhab.internal.data.RemoteopenhabStatusInfo;
|
||||
import org.openhab.binding.remoteopenhab.internal.data.RemoteopenhabThing;
|
||||
import org.openhab.binding.remoteopenhab.internal.exceptions.RemoteopenhabException;
|
||||
import org.openhab.binding.remoteopenhab.internal.listener.RemoteopenhabItemsDataListener;
|
||||
import org.openhab.binding.remoteopenhab.internal.listener.RemoteopenhabStreamingDataListener;
|
||||
import org.openhab.core.io.net.http.HttpUtil;
|
||||
import org.openhab.binding.remoteopenhab.internal.listener.RemoteopenhabThingsDataListener;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.osgi.service.jaxrs.client.SseEventSourceFactory;
|
||||
import org.slf4j.Logger;
|
||||
@@ -62,109 +75,140 @@ public class RemoteopenhabRestClient {
|
||||
private final ClientBuilder clientBuilder;
|
||||
private final SseEventSourceFactory eventSourceFactory;
|
||||
private final Gson jsonParser;
|
||||
private String accessToken;
|
||||
private final String restUrl;
|
||||
|
||||
private final Object startStopLock = new Object();
|
||||
private final List<RemoteopenhabStreamingDataListener> listeners = new CopyOnWriteArrayList<>();
|
||||
private final List<RemoteopenhabItemsDataListener> itemsListeners = new CopyOnWriteArrayList<>();
|
||||
private final List<RemoteopenhabThingsDataListener> thingsListeners = new CopyOnWriteArrayList<>();
|
||||
|
||||
private HttpClient httpClient;
|
||||
private @Nullable String restUrl;
|
||||
private @Nullable String restApiVersion;
|
||||
private @Nullable String restApiItems;
|
||||
private @Nullable String restApiEvents;
|
||||
private Map<String, @Nullable String> apiEndPointsUrls = new HashMap<>();
|
||||
private @Nullable String topicNamespace;
|
||||
private String accessToken;
|
||||
private boolean trustedCertificate;
|
||||
private boolean connected;
|
||||
|
||||
private @Nullable SseEventSource eventSource;
|
||||
private long lastEventTimestamp;
|
||||
|
||||
public RemoteopenhabRestClient(final ClientBuilder clientBuilder, final SseEventSourceFactory eventSourceFactory,
|
||||
final Gson jsonParser, final String accessToken, final String restUrl) {
|
||||
public RemoteopenhabRestClient(final HttpClient httpClient, final ClientBuilder clientBuilder,
|
||||
final SseEventSourceFactory eventSourceFactory, final Gson jsonParser) {
|
||||
this.httpClient = httpClient;
|
||||
this.clientBuilder = clientBuilder;
|
||||
this.eventSourceFactory = eventSourceFactory;
|
||||
this.jsonParser = jsonParser;
|
||||
this.accessToken = accessToken;
|
||||
this.accessToken = "";
|
||||
}
|
||||
|
||||
public void setHttpClient(HttpClient httpClient) {
|
||||
this.httpClient = httpClient;
|
||||
}
|
||||
|
||||
public String getRestUrl() throws RemoteopenhabException {
|
||||
String url = restUrl;
|
||||
if (url == null) {
|
||||
throw new RemoteopenhabException("REST client not correctly setup");
|
||||
}
|
||||
return url;
|
||||
}
|
||||
|
||||
public void setRestUrl(String restUrl) {
|
||||
this.restUrl = restUrl;
|
||||
}
|
||||
|
||||
public void setAccessToken(String accessToken) {
|
||||
this.accessToken = accessToken;
|
||||
}
|
||||
|
||||
public void setTrustedCertificate(boolean trustedCertificate) {
|
||||
this.trustedCertificate = trustedCertificate;
|
||||
}
|
||||
|
||||
public void tryApi() throws RemoteopenhabException {
|
||||
try {
|
||||
Properties httpHeaders = new Properties();
|
||||
if (!accessToken.isEmpty()) {
|
||||
httpHeaders.put(HttpHeaders.AUTHORIZATION, "Bearer " + accessToken);
|
||||
}
|
||||
httpHeaders.put(HttpHeaders.ACCEPT, "application/json");
|
||||
String jsonResponse = HttpUtil.executeUrl("GET", restUrl, httpHeaders, null, null, REQUEST_TIMEOUT);
|
||||
String jsonResponse = executeUrl(HttpMethod.GET, getRestUrl(), "application/json", null, null);
|
||||
if (jsonResponse.isEmpty()) {
|
||||
throw new RemoteopenhabException("Failed to execute the root REST API");
|
||||
throw new RemoteopenhabException("JSON response is empty");
|
||||
}
|
||||
RestApi restApi = jsonParser.fromJson(jsonResponse, RestApi.class);
|
||||
RemoteopenhabRestApi restApi = jsonParser.fromJson(jsonResponse, RemoteopenhabRestApi.class);
|
||||
restApiVersion = restApi.version;
|
||||
logger.debug("REST API version = {}", restApiVersion);
|
||||
restApiItems = null;
|
||||
apiEndPointsUrls.clear();
|
||||
for (int i = 0; i < restApi.links.length; i++) {
|
||||
if ("items".equals(restApi.links[i].type)) {
|
||||
restApiItems = restApi.links[i].url;
|
||||
} else if ("events".equals(restApi.links[i].type)) {
|
||||
restApiEvents = restApi.links[i].url;
|
||||
}
|
||||
apiEndPointsUrls.put(restApi.links[i].type, restApi.links[i].url);
|
||||
}
|
||||
logger.debug("REST API items = {}", restApiItems);
|
||||
logger.debug("REST API events = {}", restApiEvents);
|
||||
logger.debug("REST API items = {}", apiEndPointsUrls.get("items"));
|
||||
logger.debug("REST API things = {}", apiEndPointsUrls.get("things"));
|
||||
logger.debug("REST API events = {}", apiEndPointsUrls.get("events"));
|
||||
topicNamespace = restApi.runtimeInfo != null ? "openhab" : "smarthome";
|
||||
logger.debug("topic namespace = {}", topicNamespace);
|
||||
} catch (RemoteopenhabException e) {
|
||||
throw new RemoteopenhabException(e.getMessage());
|
||||
} catch (JsonSyntaxException e) {
|
||||
throw new RemoteopenhabException("Failed to parse the result of the root REST API", e);
|
||||
} catch (IOException e) {
|
||||
throw new RemoteopenhabException("Failed to execute the root REST API", e);
|
||||
} catch (RemoteopenhabException | JsonSyntaxException e) {
|
||||
throw new RemoteopenhabException("Failed to execute the root REST API: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
public List<Item> getRemoteItems() throws RemoteopenhabException {
|
||||
public List<RemoteopenhabItem> getRemoteItems() throws RemoteopenhabException {
|
||||
try {
|
||||
Properties httpHeaders = new Properties();
|
||||
if (!accessToken.isEmpty()) {
|
||||
httpHeaders.put(HttpHeaders.AUTHORIZATION, "Bearer " + accessToken);
|
||||
String url = String.format("%s?recursive=false", getRestApiUrl("items"));
|
||||
String jsonResponse = executeUrl(HttpMethod.GET, url, "application/json", null, null);
|
||||
if (jsonResponse.isEmpty()) {
|
||||
throw new RemoteopenhabException("JSON response is empty");
|
||||
}
|
||||
httpHeaders.put(HttpHeaders.ACCEPT, "application/json");
|
||||
String url = String.format("%s?recursive=fasle", getRestApiItems());
|
||||
String jsonResponse = HttpUtil.executeUrl("GET", url, httpHeaders, null, null, REQUEST_TIMEOUT);
|
||||
return Arrays.asList(jsonParser.fromJson(jsonResponse, Item[].class));
|
||||
} catch (IOException | JsonSyntaxException e) {
|
||||
throw new RemoteopenhabException("Failed to get the list of remote items using the items REST API", e);
|
||||
return Arrays.asList(jsonParser.fromJson(jsonResponse, RemoteopenhabItem[].class));
|
||||
} catch (RemoteopenhabException | JsonSyntaxException e) {
|
||||
throw new RemoteopenhabException(
|
||||
"Failed to get the list of remote items using the items REST API: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
public String getRemoteItemState(String itemName) throws RemoteopenhabException {
|
||||
try {
|
||||
Properties httpHeaders = new Properties();
|
||||
if (!accessToken.isEmpty()) {
|
||||
httpHeaders.put(HttpHeaders.AUTHORIZATION, "Bearer " + accessToken);
|
||||
}
|
||||
httpHeaders.put(HttpHeaders.ACCEPT, "text/plain");
|
||||
String url = String.format("%s/%s/state", getRestApiItems(), itemName);
|
||||
return HttpUtil.executeUrl("GET", url, httpHeaders, null, null, REQUEST_TIMEOUT);
|
||||
} catch (IOException e) {
|
||||
throw new RemoteopenhabException(
|
||||
"Failed to get the state of remote item " + itemName + " using the items REST API", e);
|
||||
String url = String.format("%s/%s/state", getRestApiUrl("items"), itemName);
|
||||
return executeUrl(HttpMethod.GET, url, "text/plain", null, null);
|
||||
} catch (RemoteopenhabException e) {
|
||||
throw new RemoteopenhabException("Failed to get the state of remote item " + itemName
|
||||
+ " using the items REST API: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
public void sendCommandToRemoteItem(String itemName, Command command) throws RemoteopenhabException {
|
||||
try {
|
||||
Properties httpHeaders = new Properties();
|
||||
if (!accessToken.isEmpty()) {
|
||||
httpHeaders.put(HttpHeaders.AUTHORIZATION, "Bearer " + accessToken);
|
||||
}
|
||||
httpHeaders.put(HttpHeaders.ACCEPT, "application/json");
|
||||
String url = String.format("%s/%s", getRestApiItems(), itemName);
|
||||
String url = String.format("%s/%s", getRestApiUrl("items"), itemName);
|
||||
InputStream stream = new ByteArrayInputStream(command.toFullString().getBytes(StandardCharsets.UTF_8));
|
||||
HttpUtil.executeUrl("POST", url, httpHeaders, stream, "text/plain", REQUEST_TIMEOUT);
|
||||
executeUrl(HttpMethod.POST, url, "application/json", stream, "text/plain");
|
||||
stream.close();
|
||||
} catch (IOException e) {
|
||||
} catch (RemoteopenhabException | IOException e) {
|
||||
throw new RemoteopenhabException("Failed to send command to the remote item " + itemName
|
||||
+ " using the items REST API: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
public List<RemoteopenhabThing> getRemoteThings() throws RemoteopenhabException {
|
||||
try {
|
||||
String jsonResponse = executeUrl(HttpMethod.GET, getRestApiUrl("things"), "application/json", null, null);
|
||||
if (jsonResponse.isEmpty()) {
|
||||
throw new RemoteopenhabException("JSON response is empty");
|
||||
}
|
||||
return Arrays.asList(jsonParser.fromJson(jsonResponse, RemoteopenhabThing[].class));
|
||||
} catch (RemoteopenhabException | JsonSyntaxException e) {
|
||||
throw new RemoteopenhabException(
|
||||
"Failed to send command to the remote item " + itemName + " using the items REST API", e);
|
||||
"Failed to get the list of remote things using the things REST API: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
public RemoteopenhabThing getRemoteThing(String uid) throws RemoteopenhabException {
|
||||
try {
|
||||
String url = String.format("%s/%s", getRestApiUrl("things"), uid);
|
||||
String jsonResponse = executeUrl(HttpMethod.GET, url, "application/json", null, null);
|
||||
if (jsonResponse.isEmpty()) {
|
||||
throw new RemoteopenhabException("JSON response is empty");
|
||||
}
|
||||
return Objects.requireNonNull(jsonParser.fromJson(jsonResponse, RemoteopenhabThing.class));
|
||||
} catch (RemoteopenhabException | JsonSyntaxException e) {
|
||||
throw new RemoteopenhabException(
|
||||
"Failed to get the remote thing " + uid + " using the things REST API: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -172,14 +216,9 @@ public class RemoteopenhabRestClient {
|
||||
return restApiVersion;
|
||||
}
|
||||
|
||||
public String getRestApiItems() {
|
||||
String url = restApiItems;
|
||||
return url != null ? url : restUrl + "/items";
|
||||
}
|
||||
|
||||
public String getRestApiEvents() {
|
||||
String url = restApiEvents;
|
||||
return url != null ? url : restUrl + "/events";
|
||||
private String getRestApiUrl(String endPoint) throws RemoteopenhabException {
|
||||
String url = apiEndPointsUrls.get(endPoint);
|
||||
return url != null ? url : getRestUrl() + "/" + endPoint;
|
||||
}
|
||||
|
||||
public String getTopicNamespace() {
|
||||
@@ -189,7 +228,7 @@ public class RemoteopenhabRestClient {
|
||||
|
||||
public void start() {
|
||||
synchronized (startStopLock) {
|
||||
logger.debug("Opening EventSource {}", getItemsRestSseUrl());
|
||||
logger.debug("Opening EventSource");
|
||||
reopenEventSource();
|
||||
logger.debug("EventSource started");
|
||||
}
|
||||
@@ -197,18 +236,29 @@ public class RemoteopenhabRestClient {
|
||||
|
||||
public void stop() {
|
||||
synchronized (startStopLock) {
|
||||
logger.debug("Closing EventSource {}", getItemsRestSseUrl());
|
||||
logger.debug("Closing EventSource");
|
||||
closeEventSource(0, TimeUnit.SECONDS);
|
||||
logger.debug("EventSource stopped");
|
||||
lastEventTimestamp = 0;
|
||||
}
|
||||
}
|
||||
|
||||
private String getItemsRestSseUrl() {
|
||||
return String.format("%s?topics=%s/items/*/*", getRestApiEvents(), getTopicNamespace());
|
||||
}
|
||||
|
||||
private SseEventSource createEventSource(String restSseUrl) {
|
||||
Client client = clientBuilder.register(new RemoteopenhabStreamingRequestFilter(accessToken)).build();
|
||||
Client client;
|
||||
// Avoid a timeout exception after 1 minute by setting the read timeout to 0 (infinite)
|
||||
if (trustedCertificate) {
|
||||
client = clientBuilder.sslContext(httpClient.getSslContextFactory().getSslContext())
|
||||
.hostnameVerifier(new HostnameVerifier() {
|
||||
@Override
|
||||
public boolean verify(@Nullable String hostname, @Nullable SSLSession session) {
|
||||
return true;
|
||||
}
|
||||
}).readTimeout(0, TimeUnit.SECONDS).register(new RemoteopenhabStreamingRequestFilter(accessToken))
|
||||
.build();
|
||||
} else {
|
||||
client = clientBuilder.readTimeout(0, TimeUnit.SECONDS)
|
||||
.register(new RemoteopenhabStreamingRequestFilter(accessToken)).build();
|
||||
}
|
||||
SseEventSource eventSource = eventSourceFactory.newSource(client.target(restSseUrl));
|
||||
eventSource.register(this::onEvent, this::onError);
|
||||
return eventSource;
|
||||
@@ -216,10 +266,20 @@ public class RemoteopenhabRestClient {
|
||||
|
||||
private void reopenEventSource() {
|
||||
logger.debug("Reopening EventSource");
|
||||
|
||||
String url;
|
||||
try {
|
||||
url = String.format("%s?topics=%s/items/*/*,%s/things/*/*,%s/channels/*/triggered", getRestApiUrl("events"),
|
||||
getTopicNamespace(), getTopicNamespace(), getTopicNamespace());
|
||||
} catch (RemoteopenhabException e) {
|
||||
logger.debug("{}", e.getMessage());
|
||||
return;
|
||||
}
|
||||
|
||||
closeEventSource(10, TimeUnit.SECONDS);
|
||||
|
||||
logger.debug("Opening new EventSource {}", getItemsRestSseUrl());
|
||||
SseEventSource localEventSource = createEventSource(getItemsRestSseUrl());
|
||||
logger.debug("Opening new EventSource {}", url);
|
||||
SseEventSource localEventSource = createEventSource(url);
|
||||
localEventSource.open();
|
||||
|
||||
eventSource = localEventSource;
|
||||
@@ -248,6 +308,22 @@ public class RemoteopenhabRestClient {
|
||||
return listeners.remove(listener);
|
||||
}
|
||||
|
||||
public boolean addItemsDataListener(RemoteopenhabItemsDataListener listener) {
|
||||
return itemsListeners.add(listener);
|
||||
}
|
||||
|
||||
public boolean removeItemsDataListener(RemoteopenhabItemsDataListener listener) {
|
||||
return itemsListeners.remove(listener);
|
||||
}
|
||||
|
||||
public boolean addThingsDataListener(RemoteopenhabThingsDataListener listener) {
|
||||
return thingsListeners.add(listener);
|
||||
}
|
||||
|
||||
public boolean removeThingsDataListener(RemoteopenhabThingsDataListener listener) {
|
||||
return thingsListeners.remove(listener);
|
||||
}
|
||||
|
||||
public long getLastEventTimestamp() {
|
||||
return lastEventTimestamp;
|
||||
}
|
||||
@@ -270,43 +346,75 @@ public class RemoteopenhabRestClient {
|
||||
}
|
||||
|
||||
try {
|
||||
Event event = jsonParser.fromJson(data, Event.class);
|
||||
RemoteopenhabEvent event = jsonParser.fromJson(data, RemoteopenhabEvent.class);
|
||||
String itemName;
|
||||
EventPayload payload;
|
||||
Item item;
|
||||
String thingUID;
|
||||
RemoteopenhabEventPayload payload;
|
||||
RemoteopenhabItem item;
|
||||
RemoteopenhabThing thing;
|
||||
switch (event.type) {
|
||||
case "ItemStateEvent":
|
||||
itemName = extractItemNameFromTopic(event.topic, event.type, "state");
|
||||
payload = jsonParser.fromJson(event.payload, EventPayload.class);
|
||||
listeners.forEach(listener -> listener.onItemStateEvent(itemName, payload.type, payload.value));
|
||||
payload = jsonParser.fromJson(event.payload, RemoteopenhabEventPayload.class);
|
||||
itemsListeners
|
||||
.forEach(listener -> listener.onItemStateEvent(itemName, payload.type, payload.value));
|
||||
break;
|
||||
case "GroupItemStateChangedEvent":
|
||||
itemName = extractItemNameFromTopic(event.topic, event.type, "statechanged");
|
||||
payload = jsonParser.fromJson(event.payload, EventPayload.class);
|
||||
listeners.forEach(listener -> listener.onItemStateEvent(itemName, payload.type, payload.value));
|
||||
payload = jsonParser.fromJson(event.payload, RemoteopenhabEventPayload.class);
|
||||
itemsListeners
|
||||
.forEach(listener -> listener.onItemStateEvent(itemName, payload.type, payload.value));
|
||||
break;
|
||||
case "ItemAddedEvent":
|
||||
itemName = extractItemNameFromTopic(event.topic, event.type, "added");
|
||||
item = Objects.requireNonNull(jsonParser.fromJson(event.payload, Item.class));
|
||||
listeners.forEach(listener -> listener.onItemAdded(item));
|
||||
item = Objects.requireNonNull(jsonParser.fromJson(event.payload, RemoteopenhabItem.class));
|
||||
itemsListeners.forEach(listener -> listener.onItemAdded(item));
|
||||
break;
|
||||
case "ItemRemovedEvent":
|
||||
itemName = extractItemNameFromTopic(event.topic, event.type, "removed");
|
||||
item = Objects.requireNonNull(jsonParser.fromJson(event.payload, Item.class));
|
||||
listeners.forEach(listener -> listener.onItemRemoved(item));
|
||||
item = Objects.requireNonNull(jsonParser.fromJson(event.payload, RemoteopenhabItem.class));
|
||||
itemsListeners.forEach(listener -> listener.onItemRemoved(item));
|
||||
break;
|
||||
case "ItemUpdatedEvent":
|
||||
itemName = extractItemNameFromTopic(event.topic, event.type, "updated");
|
||||
Item[] updItem = jsonParser.fromJson(event.payload, Item[].class);
|
||||
RemoteopenhabItem[] updItem = jsonParser.fromJson(event.payload, RemoteopenhabItem[].class);
|
||||
if (updItem.length == 2) {
|
||||
listeners.forEach(listener -> listener.onItemUpdated(updItem[0], updItem[1]));
|
||||
itemsListeners.forEach(listener -> listener.onItemUpdated(updItem[0], updItem[1]));
|
||||
} else {
|
||||
logger.debug("Invalid payload for event type {} for topic {}", event.type, event.topic);
|
||||
}
|
||||
break;
|
||||
case "ThingStatusInfoChangedEvent":
|
||||
thingUID = extractThingUIDFromTopic(event.topic, event.type, "statuschanged");
|
||||
RemoteopenhabStatusInfo[] updStatus = jsonParser.fromJson(event.payload,
|
||||
RemoteopenhabStatusInfo[].class);
|
||||
if (updStatus.length == 2) {
|
||||
thingsListeners.forEach(listener -> listener.onThingStatusUpdated(thingUID, updStatus[0]));
|
||||
} else {
|
||||
logger.debug("Invalid payload for event type {} for topic {}", event.type, event.topic);
|
||||
}
|
||||
break;
|
||||
case "ThingAddedEvent":
|
||||
thingUID = extractThingUIDFromTopic(event.topic, event.type, "added");
|
||||
thing = Objects.requireNonNull(jsonParser.fromJson(event.payload, RemoteopenhabThing.class));
|
||||
thingsListeners.forEach(listener -> listener.onThingAdded(thing));
|
||||
break;
|
||||
case "ThingRemovedEvent":
|
||||
thingUID = extractThingUIDFromTopic(event.topic, event.type, "removed");
|
||||
thing = Objects.requireNonNull(jsonParser.fromJson(event.payload, RemoteopenhabThing.class));
|
||||
thingsListeners.forEach(listener -> listener.onThingRemoved(thing));
|
||||
break;
|
||||
case "ChannelTriggeredEvent":
|
||||
RemoteopenhabChannelTriggerEvent triggerEvent = jsonParser.fromJson(event.payload,
|
||||
RemoteopenhabChannelTriggerEvent.class);
|
||||
thingsListeners
|
||||
.forEach(listener -> listener.onChannelTriggered(triggerEvent.channel, triggerEvent.event));
|
||||
break;
|
||||
case "ItemStatePredictedEvent":
|
||||
case "ItemStateChangedEvent":
|
||||
case "ItemCommandEvent":
|
||||
case "ThingStatusInfoEvent":
|
||||
case "ThingUpdatedEvent":
|
||||
logger.trace("Ignored event type {} for topic {}", event.type, event.topic);
|
||||
break;
|
||||
default:
|
||||
@@ -334,4 +442,48 @@ public class RemoteopenhabRestClient {
|
||||
}
|
||||
return parts[2];
|
||||
}
|
||||
|
||||
private String extractThingUIDFromTopic(String topic, String eventType, String finalPart)
|
||||
throws RemoteopenhabException {
|
||||
String[] parts = topic.split("/");
|
||||
int expectedNbParts = 4;
|
||||
if (parts.length != expectedNbParts || !getTopicNamespace().equals(parts[0]) || !"things".equals(parts[1])
|
||||
|| !finalPart.equals(parts[parts.length - 1])) {
|
||||
throw new RemoteopenhabException("Invalid event topic " + topic + " for event type " + eventType);
|
||||
}
|
||||
return parts[2];
|
||||
}
|
||||
|
||||
public String executeUrl(HttpMethod httpMethod, String url, String acceptHeader, @Nullable InputStream content,
|
||||
@Nullable String contentType) throws RemoteopenhabException {
|
||||
final Request request = httpClient.newRequest(url).method(httpMethod).timeout(REQUEST_TIMEOUT,
|
||||
TimeUnit.MILLISECONDS);
|
||||
|
||||
request.header(HttpHeaders.ACCEPT, acceptHeader);
|
||||
if (!accessToken.isEmpty()) {
|
||||
request.header(HttpHeaders.AUTHORIZATION, "Bearer " + accessToken);
|
||||
}
|
||||
|
||||
if (content != null && (HttpMethod.POST.equals(httpMethod) || HttpMethod.PUT.equals(httpMethod))
|
||||
&& contentType != null) {
|
||||
try (final InputStreamContentProvider inputStreamContentProvider = new InputStreamContentProvider(
|
||||
content)) {
|
||||
request.content(inputStreamContentProvider, contentType);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
ContentResponse response = request.send();
|
||||
int statusCode = response.getStatus();
|
||||
if (statusCode >= HttpStatus.BAD_REQUEST_400) {
|
||||
String statusLine = statusCode + " " + response.getReason();
|
||||
throw new RemoteopenhabException("HTTP call failed: " + statusLine);
|
||||
}
|
||||
String encoding = response.getEncoding() != null ? response.getEncoding().replaceAll("\"", "").trim()
|
||||
: StandardCharsets.UTF_8.name();
|
||||
return new String(response.getContent(), encoding);
|
||||
} catch (Exception e) {
|
||||
throw new RemoteopenhabException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,14 +18,32 @@
|
||||
<required>true</required>
|
||||
</parameter>
|
||||
|
||||
<parameter name="useHttps" type="boolean">
|
||||
<label>Use HTTPS</label>
|
||||
<description>Set it to true in case you want to use HTTPS to communicate with the remote openHAB server. Default is
|
||||
false.</description>
|
||||
<required>false</required>
|
||||
<default>false</default>
|
||||
<advanced>true</advanced>
|
||||
</parameter>
|
||||
|
||||
<parameter name="port" type="integer">
|
||||
<label>Server HTTP Port</label>
|
||||
<description>The HTTP port to be used to communicate with the remote openHAB server.</description>
|
||||
<description>The HTTP port to use to communicate with the remote openHAB server.</description>
|
||||
<required>true</required>
|
||||
<default>8080</default>
|
||||
<advanced>true</advanced>
|
||||
</parameter>
|
||||
|
||||
<parameter name="trustedCertificate" type="boolean">
|
||||
<label>Trust SSL Certificate</label>
|
||||
<description>Set it to true in case you want to use HTTPS even without a valid SSL certificate provided by your
|
||||
remote server.</description>
|
||||
<required>false</required>
|
||||
<default>false</default>
|
||||
<advanced>true</advanced>
|
||||
</parameter>
|
||||
|
||||
<parameter name="restPath" type="text">
|
||||
<label>REST API Path</label>
|
||||
<description>The subpath of the REST API on the remote openHAB server.</description>
|
||||
@@ -44,4 +62,43 @@
|
||||
</config-description>
|
||||
</bridge-type>
|
||||
|
||||
<thing-type id="thing">
|
||||
<supported-bridge-type-refs>
|
||||
<bridge-type-ref id="server"/>
|
||||
</supported-bridge-type-refs>
|
||||
|
||||
<label>Remote Thing</label>
|
||||
<description>A thing from the remote openHAB server.</description>
|
||||
|
||||
<representation-property>thingUID</representation-property>
|
||||
|
||||
<config-description>
|
||||
<parameter name="thingUID" type="text">
|
||||
<label>Remote Thing UID</label>
|
||||
<description>The thing UID in the remote openHAB server.</description>
|
||||
<required>true</required>
|
||||
</parameter>
|
||||
|
||||
<parameter name="buildTriggerChannels" type="boolean">
|
||||
<label>Automatic Trigger Channels Building</label>
|
||||
<description>If set to true, a trigger channel will be automatically created and linked to each trigger channel from
|
||||
the remote thing.</description>
|
||||
<required>false</required>
|
||||
<default>true</default>
|
||||
<advanced>true</advanced>
|
||||
</parameter>
|
||||
</config-description>
|
||||
</thing-type>
|
||||
|
||||
<channel-type id="trigger">
|
||||
<kind>trigger</kind>
|
||||
<label>Trigger Channel</label>
|
||||
<config-description>
|
||||
<parameter name="channelUID" type="text" required="true">
|
||||
<label>Remote Channel UID</label>
|
||||
<description>The channel UID in the remote openHAB server.</description>
|
||||
</parameter>
|
||||
</config-description>
|
||||
</channel-type>
|
||||
|
||||
</thing:thing-descriptions>
|
||||
|
||||
Reference in New Issue
Block a user