[opensprinkler] Make http connection more resilient (#14998)

Signed-off-by: Bernhard Kreuz <bernhard@kreuz.wien>
Signed-off-by: wzbfyb <57131473+wzbfyb@users.noreply.github.com>
Co-authored-by: Hilbrand Bouwkamp <hilbrand@h72.nl>
Co-authored-by: Laurent Garnier <lg.hc@free.fr>
This commit is contained in:
wzbfyb 2023-12-14 21:55:12 +01:00 committed by GitHub
parent 7b16ef1de8
commit 4a8cb5fac9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 64 additions and 13 deletions

View File

@ -28,6 +28,8 @@ Due to this method used, it is very slow at finding devices and can saturate net
- port: Port the OpenSprinkler device is listening on. Usually 80. - port: Port the OpenSprinkler device is listening on. Usually 80.
- password: Admin password of the API. Factory default is: opendoor - password: Admin password of the API. Factory default is: opendoor
- refresh: Number of seconds in between refreshing the Thing state with the API. - refresh: Number of seconds in between refreshing the Thing state with the API.
- timeout: (optional) Number of seconds to wait for a timeout when calling the OpenSprinkler HTTP API.
- retry: (optional) Number of retries on connection timeouts.
- basicUsername: (optional) Only needed when the OpenSprinkler device is behind a basic auth enforcing reverse proxy. - basicUsername: (optional) Only needed when the OpenSprinkler device is behind a basic auth enforcing reverse proxy.
- basicPassword: (optional) Only needed when the OpenSprinkler device is behind a basic auth enforcing reverse proxy. - basicPassword: (optional) Only needed when the OpenSprinkler device is behind a basic auth enforcing reverse proxy.

View File

@ -48,6 +48,8 @@ public class OpenSprinklerBindingConstants {
public static final String JSON_OPTION_STATION_COUNT = "nstations"; public static final String JSON_OPTION_STATION_COUNT = "nstations";
public static final String JSON_OPTION_RESULT = "result"; public static final String JSON_OPTION_RESULT = "result";
public static final int DEFAULT_REFRESH_RATE = 60; public static final int DEFAULT_REFRESH_RATE = 60;
public static final int DEFAULT_TIMEOUT = 5;
public static final int DEFFAULT_RETRIES = 3;
public static final int DISCOVERY_THREAD_POOL_SIZE = 15; public static final int DISCOVERY_THREAD_POOL_SIZE = 15;
public static final boolean DISCOVERY_DEFAULT_AUTO_DISCOVER = false; public static final boolean DISCOVERY_DEFAULT_AUTO_DISCOVER = false;
public static final int DISCOVERY_DEFAULT_TIMEOUT_RATE = 500; public static final int DISCOVERY_DEFAULT_TIMEOUT_RATE = 500;

View File

@ -12,7 +12,15 @@
*/ */
package org.openhab.binding.opensprinkler.internal.api; package org.openhab.binding.opensprinkler.internal.api;
import static org.openhab.binding.opensprinkler.internal.OpenSprinklerBindingConstants.*; import static org.openhab.binding.opensprinkler.internal.OpenSprinklerBindingConstants.CMD_DISABLE_MANUAL_MODE;
import static org.openhab.binding.opensprinkler.internal.OpenSprinklerBindingConstants.CMD_ENABLE_MANUAL_MODE;
import static org.openhab.binding.opensprinkler.internal.OpenSprinklerBindingConstants.CMD_OPTIONS_INFO;
import static org.openhab.binding.opensprinkler.internal.OpenSprinklerBindingConstants.CMD_PASSWORD;
import static org.openhab.binding.opensprinkler.internal.OpenSprinklerBindingConstants.CMD_STATION_INFO;
import static org.openhab.binding.opensprinkler.internal.OpenSprinklerBindingConstants.CMD_STATUS_INFO;
import static org.openhab.binding.opensprinkler.internal.OpenSprinklerBindingConstants.DEFAULT_STATION_COUNT;
import static org.openhab.binding.opensprinkler.internal.OpenSprinklerBindingConstants.HTTPS_REQUEST_URL_PREFIX;
import static org.openhab.binding.opensprinkler.internal.OpenSprinklerBindingConstants.HTTP_REQUEST_URL_PREFIX;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
@ -379,22 +387,34 @@ class OpenSprinklerHttpApiV100 implements OpenSprinklerApi {
} else { } else {
location = url; location = url;
} }
ContentResponse response; ContentResponse response = null;
try { int retriesLeft = Math.max(1, config.retry);
response = withGeneralProperties(httpClient.newRequest(location)).timeout(5, TimeUnit.SECONDS) boolean connectionSuccess = false;
.method(HttpMethod.GET).send(); while (connectionSuccess == false && retriesLeft > 0) {
} catch (InterruptedException | TimeoutException | ExecutionException e) { retriesLeft--;
throw new CommunicationApiException("Request to OpenSprinkler device failed: " + e.getMessage()); try {
response = withGeneralProperties(httpClient.newRequest(location))
.timeout(config.timeout, TimeUnit.SECONDS).method(HttpMethod.GET).send();
connectionSuccess = true;
} catch (InterruptedException | TimeoutException | ExecutionException e) {
logger.warn("Request to OpenSprinkler device failed (retries left: {}): {}", retriesLeft,
e.getMessage());
}
} }
if (response.getStatus() != HTTP_OK_CODE) { if (connectionSuccess == false) {
throw new CommunicationApiException("Request to OpenSprinkler device failed");
}
if (response != null && response.getStatus() != HTTP_OK_CODE) {
throw new CommunicationApiException( throw new CommunicationApiException(
"Error sending HTTP GET request to " + url + ". Got response code: " + response.getStatus()); "Error sending HTTP GET request to " + url + ". Got response code: " + response.getStatus());
} else if (response != null) {
String content = response.getContentAsString();
if ("{\"result\":2}".equals(content)) {
throw new UnauthorizedApiException("Unauthorized, check your password is correct");
}
return content;
} }
String content = response.getContentAsString(); return "";
if ("{\"result\":2}".equals(content)) {
throw new UnauthorizedApiException("Unauthorized, check your password is correct");
}
return content;
} }
private Request withGeneralProperties(Request request) { private Request withGeneralProperties(Request request) {

View File

@ -43,6 +43,17 @@ public class OpenSprinklerHttpInterfaceConfig {
* Number of seconds in between refreshes from the OpenSprinkler device. * Number of seconds in between refreshes from the OpenSprinkler device.
*/ */
public int refresh = 60; public int refresh = 60;
/**
* Number of seconds for connection timeouts
*/
public int timeout = 5;
/**
* Number of retries in case of connection timeouts
*/
public int retry = 3;
/** /**
* The basic auth username to use when the OpenSprinkler device is behind a reverse proxy with basic auth enabled. * The basic auth username to use when the OpenSprinkler device is behind a reverse proxy with basic auth enabled.
*/ */

View File

@ -26,6 +26,10 @@ thing-type.config.opensprinkler.http.port.label = Port
thing-type.config.opensprinkler.http.port.description = Port of the OpenSprinkler Web API interface. thing-type.config.opensprinkler.http.port.description = Port of the OpenSprinkler Web API interface.
thing-type.config.opensprinkler.http.refresh.label = Refresh Interval thing-type.config.opensprinkler.http.refresh.label = Refresh Interval
thing-type.config.opensprinkler.http.refresh.description = Specifies the refresh interval in seconds. thing-type.config.opensprinkler.http.refresh.description = Specifies the refresh interval in seconds.
thing-type.config.opensprinkler.http.retry.label = Retry Count
thing-type.config.opensprinkler.http.retry.description = Specifies the number of retries on connection timeouts.
thing-type.config.opensprinkler.http.timeout.label = Timeout
thing-type.config.opensprinkler.http.timeout.description = Specifies the connection timeout in seconds.
thing-type.config.opensprinkler.station.stationIndex.label = Station Index thing-type.config.opensprinkler.station.stationIndex.label = Station Index
thing-type.config.opensprinkler.station.stationIndex.description = The index of the station, starting with 0, of the station. thing-type.config.opensprinkler.station.stationIndex.description = The index of the station, starting with 0, of the station.

View File

@ -32,6 +32,18 @@
<description>Specifies the refresh interval in seconds.</description> <description>Specifies the refresh interval in seconds.</description>
<default>60</default> <default>60</default>
</parameter> </parameter>
<parameter name="retry" type="integer">
<label>Retry Count</label>
<description>Specifies the number of retries on connection timeouts.</description>
<default>3</default>
<advanced>true</advanced>
</parameter>
<parameter name="timeout" type="integer" unit="s">
<label>Timeout</label>
<description>Specifies the connection timeout in seconds.</description>
<default>5</default>
<advanced>true</advanced>
</parameter>
<parameter name="basicUsername" type="text"> <parameter name="basicUsername" type="text">
<label>Basic Auth Username</label> <label>Basic Auth Username</label>
<description>Used if the OpenSprinkler device is behind a basic auth protected reverse proxy.</description> <description>Used if the OpenSprinkler device is behind a basic auth protected reverse proxy.</description>