diff --git a/bundles/org.openhab.binding.linktap/README.md b/bundles/org.openhab.binding.linktap/README.md index 2e0099e3a2f9b..5f9e188df1b84 100644 --- a/bundles/org.openhab.binding.linktap/README.md +++ b/bundles/org.openhab.binding.linktap/README.md @@ -70,14 +70,15 @@ If the gateway cannot publish to openHAB, then the gateway is checked every 2 mi ### Gateway Configuration -| Name | Type | Description | Recommended Values | Required | Advanced | -|-----------------------|--------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------|----------|----------| -| host | String | The hostname / IP address of the gateway device | | Yes | No | -| username | String | The username if set for the gateway device | | No | No | -| password | String | The password if set for the gateway device | | No | No | -| enableMDNS | Switch | On connection whether the mDNS responder should be enabled on the gateway device | true | No | Yes | -| enforceProtocolLimits | Switch | If true data outside of the allowed ranges against the protocol will be logged and not sent | true | No | Yes | -| enableJSONComms | Switch | false by default for backwards compatibility, if using up to date firmware with no other local network applications set this to true, for more efficient communications | true | No | Yes | +| Name | Type | Description | Recommended Values | Required | Advanced | +|------------------------|---------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------|----------|----------| +| host | text | The hostname / IP address of the gateway device | | Yes | No | +| username | text | The username if set for the gateway device | | No | No | +| password | text | The password if set for the gateway device | | No | No | +| enableMDNS | boolean | On connection whether the mDNS responder should be enabled on the gateway device | true | No | Yes | +| enforceProtocolLimits | boolean | If true data outside of the allowed ranges against the protocol will be logged and not sent | true | No | Yes | +| enableJSONComms | boolean | false by default for backwards compatibility, if using up to date firmware with no other local network applications set this to true, for more efficient communications | true | No | Yes | +| gatewayResponseTimeout | integer | For slow or heavily loaded systems this may need increasing, if communication errors are seen (seconds allowed for responses from the gateway) | 3 | No | Yes | **NOTE** When enableMDNS is enabled, upon connection to the gateway option "Enable mDNS responder" is switched on. @@ -153,7 +154,7 @@ There are 4 different areas of channels: - **Device Model**: Q1 ```java -Bridge linktap:gateway:home "LinkTap GW02" [ host="192.168.0.21", enableMDNS=true, enableJSONComms=false, enforceProtocolLimits=true ] { +Bridge linktap:gateway:home "LinkTap GW02" [ host="192.168.0.21", enableMDNS=true, enableJSONComms=false, enforceProtocolLimits=true, gatewayResponseTimeout=3 ] { Thing device TapValve1 "Outdoor Tap 1" [ id="D71BC52E985B1200_1", name="ValveLinker_1", enableAlerts=true ] Thing device TapValve2 "Outdoor Tap 2" [ id="D71BC52E985B1200_2", name="ValveLinker_2", enableAlerts=true ] Thing device TapValve3 "Outdoor Tap 3" [ id="D71BC52E985B1200_3", name="ValveLinker_3", enableAlerts=true ] diff --git a/bundles/org.openhab.binding.linktap/src/main/java/org/openhab/binding/linktap/configuration/LinkTapBridgeConfiguration.java b/bundles/org.openhab.binding.linktap/src/main/java/org/openhab/binding/linktap/configuration/LinkTapBridgeConfiguration.java index ce71ff83064c1..150a6d68c08b3 100644 --- a/bundles/org.openhab.binding.linktap/src/main/java/org/openhab/binding/linktap/configuration/LinkTapBridgeConfiguration.java +++ b/bundles/org.openhab.binding.linktap/src/main/java/org/openhab/binding/linktap/configuration/LinkTapBridgeConfiguration.java @@ -28,4 +28,5 @@ public class LinkTapBridgeConfiguration { public boolean enableMDNS = true; public boolean enableJSONComms = false; public boolean enforceProtocolLimits = true; + public int gatewayResponseTimeout = 3; } diff --git a/bundles/org.openhab.binding.linktap/src/main/java/org/openhab/binding/linktap/internal/LinkTapBindingConstants.java b/bundles/org.openhab.binding.linktap/src/main/java/org/openhab/binding/linktap/internal/LinkTapBindingConstants.java index 48d8c1fd76a91..2db6568c36976 100644 --- a/bundles/org.openhab.binding.linktap/src/main/java/org/openhab/binding/linktap/internal/LinkTapBindingConstants.java +++ b/bundles/org.openhab.binding.linktap/src/main/java/org/openhab/binding/linktap/internal/LinkTapBindingConstants.java @@ -59,6 +59,7 @@ public class LinkTapBindingConstants { public static final String BRIDGE_CONFIG_MDNS_ENABLE = "enableMDNS"; public static final String BRIDGE_CONFIG_NON_HTML_COMM_ENABLE = "enableJSONComms"; public static final String BRIDGE_CONFIG_ENFORCE_COMM_LIMITS = "enforceProtocolLimits"; + public static final String BRIDGE_CONFIG_GATEWAY_RESPONSE_TIMEOUT = "gatewayResponseTimeout"; public static final String DEVICE_PROP_DEV_ID = "deviceId"; public static final String DEVICE_PROP_DEV_NAME = "deviceName"; diff --git a/bundles/org.openhab.binding.linktap/src/main/java/org/openhab/binding/linktap/internal/LinkTapBridgeHandler.java b/bundles/org.openhab.binding.linktap/src/main/java/org/openhab/binding/linktap/internal/LinkTapBridgeHandler.java index 731255c0e0f05..d99a0ce05b4d0 100644 --- a/bundles/org.openhab.binding.linktap/src/main/java/org/openhab/binding/linktap/internal/LinkTapBridgeHandler.java +++ b/bundles/org.openhab.binding.linktap/src/main/java/org/openhab/binding/linktap/internal/LinkTapBridgeHandler.java @@ -143,6 +143,10 @@ public String getLocalizedText(String key, @Nullable Object @Nullable... argumen return Objects.nonNull(result) ? result : key; } + public int getResponseTimeout() { + return config.gatewayResponseTimeout; + } + private void startGwPolling() { synchronized (schedulerLock) { cancelGwPolling(); @@ -379,7 +383,7 @@ public String sendSingleApiRequest(final TLGatewayFrame req) throws TransientCom } final String reqData = LinkTapBindingConstants.GSON.toJson(req); logger.debug("{} = APP BRIDGE -> GW -> Request {}", uid, reqData); - final String respData = api.sendRequest(host, reqData); + final String respData = api.sendRequest(host, getResponseTimeout(), reqData); logger.debug("{} = APP BRIDGE -> GW -> Response {}", uid, respData); final GatewayDeviceResponse gwResponseFrame = LinkTapBindingConstants.GSON.fromJson(respData, GatewayDeviceResponse.class); @@ -429,7 +433,7 @@ private void connect() { final WebServerApi api = WebServerApi.getInstance(); api.setHttpClient(httpClientProvider.getHttpClient()); try { - final Map bridgeProps = api.getBridgeProperities(bridgeKey); + final Map bridgeProps = api.getBridgeProperities(bridgeKey, getResponseTimeout()); if (!bridgeProps.isEmpty()) { final String readGwId = bridgeProps.get(BRIDGE_PROP_GW_ID); if (readGwId != null) { @@ -439,7 +443,7 @@ private void connect() { currentProps.putAll(bridgeProps); updateProperties(currentProps); } else { - if (!api.unlockWebInterface(bridgeKey, config.username, config.password)) { + if (!api.unlockWebInterface(bridgeKey, getResponseTimeout(), config.username, config.password)) { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, getLocalizedText("bridge.error.check-credentials")); return; @@ -481,13 +485,13 @@ private void connect() { final String servletEp = BindingServlet.getServletAddress(localServerAddr, getLocalizedText("warning.no-http-server-port")); final Optional servletEpOpt = (!servletEp.isEmpty()) ? Optional.of(servletEp) : Optional.empty(); - api.configureBridge(hostname, Optional.of(config.enableMDNS), Optional.of(config.enableJSONComms), - servletEpOpt); + api.configureBridge(hostname, getResponseTimeout(), Optional.of(config.enableMDNS), + Optional.of(config.enableJSONComms), servletEpOpt); if (Thread.currentThread().isInterrupted()) { return; } - // Ensure we have a response with data in if not schedule a reconnect in 15 seconds, theres no reason + // Ensure we have a response with data in if not schedule a reconnect in 15 seconds, there's no reason // for a gateway with no devices. if (!getGatewayConfigurationFreshCheck()) { logger.debug("{}", getLocalizedText("bridge.info.awaiting-init")); @@ -532,7 +536,8 @@ private void connect() { } private void scheduleReconnect() { - scheduleReconnect(15); + scheduleReconnect(getResponseTimeout() * 5); // 5 is due to the number of req/resp required for + // connection } public void attemptReconnectIfNeeded() { diff --git a/bundles/org.openhab.binding.linktap/src/main/java/org/openhab/binding/linktap/internal/LinkTapHandler.java b/bundles/org.openhab.binding.linktap/src/main/java/org/openhab/binding/linktap/internal/LinkTapHandler.java index 1df6b2229fe53..5bd5d80881265 100644 --- a/bundles/org.openhab.binding.linktap/src/main/java/org/openhab/binding/linktap/internal/LinkTapHandler.java +++ b/bundles/org.openhab.binding.linktap/src/main/java/org/openhab/binding/linktap/internal/LinkTapHandler.java @@ -235,7 +235,7 @@ public void handleCommand(final ChannelUID channelUID, final Command command) { strStore.put(DEVICE_CHANNEL_OH_VOLUME_LIMIT, String.valueOf(targetValue)); break; } - } else if (command instanceof StringType stringCmd) { + } else if (command instanceof StringType) { switch (channelUID.getId()) { case DEVICE_CHANNEL_CHILD_LOCK: { sendRequest(new LockReq(Integer.valueOf(command.toString()))); diff --git a/bundles/org.openhab.binding.linktap/src/main/java/org/openhab/binding/linktap/internal/TransactionProcessor.java b/bundles/org.openhab.binding.linktap/src/main/java/org/openhab/binding/linktap/internal/TransactionProcessor.java index aeaf25b6205f7..f8392deba1932 100644 --- a/bundles/org.openhab.binding.linktap/src/main/java/org/openhab/binding/linktap/internal/TransactionProcessor.java +++ b/bundles/org.openhab.binding.linktap/src/main/java/org/openhab/binding/linktap/internal/TransactionProcessor.java @@ -239,7 +239,7 @@ public String sendSingleRequest(final LinkTapBridgeHandler handler, final TLGate final String payloadJson = GSON.toJson(request); logger.debug("{} = APP -> GW Request {} -> Payload {}", uid, targetHost, payloadJson); - String response = API.sendRequest(targetHost, GSON.toJson(request)); + String response = API.sendRequest(targetHost, handler.getResponseTimeout(), GSON.toJson(request)); logger.debug("{} = APP -> GW Response {} -> Payload {}", uid, targetHost, response.trim()); GatewayDeviceResponse gatewayFrame = GSON.fromJson(response, GatewayDeviceResponse.class); diff --git a/bundles/org.openhab.binding.linktap/src/main/java/org/openhab/binding/linktap/protocol/http/JettyTraceListener.java b/bundles/org.openhab.binding.linktap/src/main/java/org/openhab/binding/linktap/protocol/http/JettyTraceListener.java new file mode 100644 index 0000000000000..1cfd60df951f2 --- /dev/null +++ b/bundles/org.openhab.binding.linktap/src/main/java/org/openhab/binding/linktap/protocol/http/JettyTraceListener.java @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2010-2025 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.linktap.protocol.http; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.jetty.client.api.Request; +import org.slf4j.Logger; + +/** + * The {@link JettyTraceListener} defines a basic listener that can be utilised for logging jetty client states. + * + * @author David Goodyear - Initial contribution + */ +@NonNullByDefault +public final class JettyTraceListener implements Request.Listener { + + private final Logger logger; + + public JettyTraceListener(final Logger logger) { + this.logger = logger; + } + + @Override + public void onQueued(@Nullable Request request) { + if (request != null) { + logger.trace("HTTP Comms request is queued to be processed to {}", request.getURI()); + } else { + logger.trace("HTTP Comms request is queued to be processed"); + } + Request.Listener.super.onQueued(request); + } + + @Override + public void onBegin(@Nullable Request request) { + if (request != null) { + logger.trace("HTTP Comms request is beginning to be processed to {}", request.getURI()); + } else { + logger.trace("HTTP Comms request is beginning to be processed"); + } + Request.Listener.super.onBegin(request); + } + + @Override + public void onSuccess(@Nullable Request request) { + if (request != null) { + logger.trace("HTTP Comms request has been reported as successful to {}", request.getURI()); + } else { + logger.trace("HTTP Comms request has been reported as successful"); + } + Request.Listener.super.onSuccess(request); + } + + @Override + public void onFailure(@Nullable Request request, @Nullable Throwable failure) { + if (request != null) { + logger.trace("HTTP Comms request has failed {}", request.getHost()); + } else { + logger.trace("HTTP Comms request has failed with error"); + } + if (failure != null) { + logger.trace("HTTP Comms request has failed due to cause {}", failure.toString()); + } + Request.Listener.super.onFailure(request, failure); + } +} diff --git a/bundles/org.openhab.binding.linktap/src/main/java/org/openhab/binding/linktap/protocol/http/WebServerApi.java b/bundles/org.openhab.binding.linktap/src/main/java/org/openhab/binding/linktap/protocol/http/WebServerApi.java index ebb11b9e9ed4b..2cace1898b205 100644 --- a/bundles/org.openhab.binding.linktap/src/main/java/org/openhab/binding/linktap/protocol/http/WebServerApi.java +++ b/bundles/org.openhab.binding.linktap/src/main/java/org/openhab/binding/linktap/protocol/http/WebServerApi.java @@ -87,10 +87,10 @@ public final class WebServerApi { private static final String FIELD_ADMIN_USER = "admin"; private static final String FIELD_ADMIN_USER_PWD = "adminpwd"; - private static final int REQ_TIMEOUT_SECONDS = 3; private static final WebServerApi INSTANCE = new WebServerApi(); private static final String REQ_HDR_APPLICATION_JSON = new MediaType("application", "json", "UTF-8").toString(); private final Logger logger = LoggerFactory.getLogger(WebServerApi.class); + private final JettyTraceListener jettyTraceListener = new JettyTraceListener(logger); private @NonNullByDefault({}) HttpClient httpClient; private @Nullable TranslationProvider translationProvider; @@ -132,11 +132,11 @@ public void setHttpClient(@Nullable HttpClient httpClient) { } } - public Map getBridgeProperities(final String hostname) + public Map getBridgeProperities(final String hostname, final int timeoutSeconds) throws LinkTapException, NotTapLinkGatewayException, TransientCommunicationIssueException { try { final Request request = httpClient.newRequest(URI_HOST_PREFIX + hostname).method(HttpMethod.GET); - final ContentResponse cr = request.timeout(REQ_TIMEOUT_SECONDS, TimeUnit.SECONDS).send(); + final ContentResponse cr = addTraceListener(request).timeout(timeoutSeconds, TimeUnit.SECONDS).send(); if (HttpURLConnection.HTTP_OK != cr.getStatus()) { throw new NotTapLinkGatewayException(UNEXPECTED_STATUS_CODE); } @@ -375,8 +375,9 @@ public String getLocalHttpApiArgs(final Document doc, final Optional tar return ""; } - public boolean configureBridge(final @Nullable String hostname, final Optional mdnsEnable, - final Optional nonHtmlEnable, final Optional localServer) + public boolean configureBridge(final @Nullable String hostname, final int timeoutSeconds, + final Optional mdnsEnable, final Optional nonHtmlEnable, + final Optional localServer) throws InterruptedException, NotTapLinkGatewayException, TransientCommunicationIssueException { try { if (hostname == null) { @@ -384,10 +385,11 @@ public boolean configureBridge(final @Nullable String hostname, final Optionaltrue true + + + For slow or heavily loaded systems this may need increasing, if communication errors are seen (seconds + allowed for responses from the gateway) + 3 + true +