diff --git a/src/main/java/de/malkusch/km200/KM200.java b/src/main/java/de/malkusch/km200/KM200.java
index 1bba3f4..6131940 100644
--- a/src/main/java/de/malkusch/km200/KM200.java
+++ b/src/main/java/de/malkusch/km200/KM200.java
@@ -32,6 +32,30 @@
/**
* This is an API for Bosch/Buderus/Junkers heaters with a KM200 gateway.
*
+ * Example:
+ *
+ *
+ * {@code
+ * var uri = "http://192.168.0.44";
+ * var gatewayPassword = "1234-5678-90ab-cdef";
+ * var privatePassword = "secretExample";
+ * var timeout = Duration.ofSeconds(5);
+ * var salt = "1234567890aabbccddeeff11223344556677889900aabbccddeeffa0a1a2b2d3";
+ *
+ * var km200 = new KM200(uri, timeout, gatewayPassword, privatePassword, salt);
+ *
+ * // Read the heater's time
+ * var time = km200.queryString("/gateway/DateTime");
+ * System.out.println(time);
+ *
+ * // Update the heater's time
+ * km200.update("/gateway/DateTime", LocalDateTime.now());
+ *
+ * // Explore the heater's endpoints
+ * km200.endpoints().forEach(System.out::println);
+ * }
+ *
+ *
* Although code wise this class is thread safe, it is highly recommended to not
* use it concurrently. Chances are that your KM200 gateway itself is not thread
* safe.
@@ -45,18 +69,21 @@ public final class KM200 {
private final Duration timeout;
private final String uri;
- private static final int RETRY_COUNT = 3;
- private static final Duration RETRY_DELAY_MIN = Duration.ofSeconds(1);
- private static final Duration RETRY_DELAY_MAX = Duration.ofSeconds(2);
+ private final FailsafeExecutor> retryQuery;
+ private final FailsafeExecutor> retryUpdate;
- @SafeVarargs
- private static FailsafeExecutor buildRetry(Class extends Throwable>... exceptions) {
- return Failsafe.with( //
- RetryPolicy. builder() //
- .handle(exceptions) //
- .withMaxRetries(RETRY_COUNT) //
- .withDelay(RETRY_DELAY_MIN, RETRY_DELAY_MAX) //
- .build());
+ public static final int RETRY_DEFAULT = 3;
+ public static final int RETRY_DISABLED = 0;
+
+ /**
+ * Configure the KM200 API with a default retry of {@link #RETRY_DEFAULT}.
+ *
+ * @see #KM200(String, int, Duration, String, String, String)
+ **/
+ public KM200(String uri, Duration timeout, String gatewayPassword, String privatePassword, String salt)
+ throws KM200Exception, IOException, InterruptedException {
+
+ this(uri, RETRY_DEFAULT, timeout, gatewayPassword, privatePassword, salt);
}
/**
@@ -65,34 +92,15 @@ RetryPolicy. builder() //
* This will also issue a silent query to /system to verify that you
* configuration is correct.
*
- * Example:
- *
- *
- * {@code
- * var uri = "http://192.168.0.44";
- * var gatewayPassword = "1234-5678-90ab-cdef";
- * var privatePassword = "secretExample";
- * var timeout = Duration.ofSeconds(5);
- * var salt = "1234567890aabbccddeeff11223344556677889900aabbccddeeffa0a1a2b2d3";
- *
- * var km200 = new KM200(uri, timeout, gatewayPassword, privatePassword, salt);
- *
- * // Read the heater's time
- * var time = km200.queryString("/gateway/DateTime");
- * System.out.println(time);
- *
- * // Update the heater's time
- * km200.update("/gateway/DateTime", LocalDateTime.now());
- *
- * // Explore the heater's endpoints
- * km200.endpoints().forEach(System.out::println);
- * }
- *
- *
* @param uri
* The base URI of your KM200 e.g. http://192.168.0.44
+ * @param retries
+ * The amount of retries. Set to {@link #RETRY_DISABLED} to
+ * disable retrying. Retries add a waiting delay between each retry
+ * of several seconds, because the km200 recovers very slowly.
* @param timeout
- * An IO timeout for requests to your heater
+ * An IO timeout for individual requests to your heater. Retries
+ * might block the API longer than this timeout.
* @param gatewayPassword
* The constant gateway password which you need to read out from
* your heater's display e.g. 1234-5678-90ab-cdef
@@ -100,7 +108,7 @@ RetryPolicy. builder() //
* The private password which you did assign in the app. If you
* forgot your private password you can start the "reset internet
* password" flow in the menu of your heater and then reassign a
- * new passwort in the app.
+ * new password in the app.
* @param salt
* The salt in the hexadecimal representation e.g. "12a0b2…"
*
@@ -108,7 +116,7 @@ RetryPolicy. builder() //
* @throws IOException
* @throws InterruptedException
*/
- public KM200(String uri, Duration timeout, String gatewayPassword, String privatePassword, String salt)
+ public KM200(String uri, int retries, Duration timeout, String gatewayPassword, String privatePassword, String salt)
throws KM200Exception, IOException, InterruptedException {
assertNotBlank(uri, "uri must not be blank");
@@ -116,6 +124,7 @@ public KM200(String uri, Duration timeout, String gatewayPassword, String privat
assertNotBlank(gatewayPassword, "gatewayPassword must not be blank");
assertNotBlank(privatePassword, "privatePassword must not be blank");
assertNotBlank(salt, "salt must not be blank");
+ assertNotNegative(retries, "retries must not be negative");
var device = new KM200Device();
device.setCharSet("UTF-8");
@@ -127,6 +136,8 @@ public KM200(String uri, Duration timeout, String gatewayPassword, String privat
this.device = device;
this.comm = new KM200Comm();
+ this.retryQuery = buildRetry(retries, IOException.class, ServerError.class);
+ this.retryUpdate = buildRetry(retries, ServerError.class);
this.timeout = timeout;
this.uri = uri.replaceAll("/*$", "");
this.http = newBuilder().connectTimeout(timeout).cookieHandler(new CookieManager()).followRedirects(ALWAYS)
@@ -167,8 +178,6 @@ public void update(String path, BigDecimal value) throws KM200Exception, IOExcep
update(path, update);
}
- private final FailsafeExecutor> retryUpdate = buildRetry(ServerError.class);
-
private void update(String path, Object update) throws KM200Exception, IOException, InterruptedException {
String json = null;
try {
@@ -189,8 +198,6 @@ private void update(String path, Object update) throws KM200Exception, IOExcepti
}
}
- private final FailsafeExecutor> retryQuery = buildRetry(IOException.class, ServerError.class);;
-
public String query(String path) throws KM200Exception, IOException, InterruptedException {
assertNotBlank(path, "Path must not be blank");
if (!path.startsWith("/")) {
@@ -294,9 +301,28 @@ private HttpRequest.Builder request(String path) {
.timeout(timeout);
}
+ private static final Duration RETRY_DELAY_MIN = Duration.ofSeconds(1);
+ private static final Duration RETRY_DELAY_MAX = Duration.ofSeconds(2);
+
+ @SafeVarargs
+ private static FailsafeExecutor buildRetry(int retries, Class extends Throwable>... exceptions) {
+ return Failsafe.with( //
+ RetryPolicy. builder() //
+ .handle(exceptions) //
+ .withMaxRetries(retries) //
+ .withDelay(RETRY_DELAY_MIN, RETRY_DELAY_MAX) //
+ .build());
+ }
+
private static void assertNotBlank(String var, String message) {
if (requireNonNull(var).isBlank()) {
throw new IllegalArgumentException(message);
}
}
+
+ private static void assertNotNegative(int var, String message) {
+ if (var < 0) {
+ throw new IllegalArgumentException(message);
+ }
+ }
}
diff --git a/src/test/java/de/malkusch/km200/KM200Test.java b/src/test/java/de/malkusch/km200/KM200Test.java
index ce461d5..077739d 100644
--- a/src/test/java/de/malkusch/km200/KM200Test.java
+++ b/src/test/java/de/malkusch/km200/KM200Test.java
@@ -15,6 +15,7 @@
import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo;
import static com.github.tomakehurst.wiremock.client.WireMock.verify;
import static com.github.tomakehurst.wiremock.stubbing.Scenario.STARTED;
+import static de.malkusch.km200.KM200.RETRY_DISABLED;
import static de.malkusch.km200.KM200.USER_AGENT;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
@@ -395,6 +396,26 @@ public void queryShouldWaitWhenRetrying() throws Exception {
verify(4, getRequestedFor(urlEqualTo("/retry-wait")));
}
+ @Test
+ public void queryShouldNotRetryWhenDisabled() throws Exception {
+ stubFor(get("/retry-disabled").willReturn(serverError()));
+ var km200 = new KM200(URI, RETRY_DISABLED, TIMEOUT, GATEWAY_PASSWORD, PRIVATE_PASSWORD, SALT);
+
+ assertThrows(KM200Exception.class, () -> km200.queryString("/retry-disabled"));
+
+ verify(1, getRequestedFor(urlEqualTo("/retry-disabled")));
+ }
+
+ @Test
+ public void updateShouldNotRetryWhenDisabled() throws Exception {
+ stubFor(post("/update-retry-disabled").willReturn(serverError()));
+ var km200 = new KM200(URI, RETRY_DISABLED, TIMEOUT, GATEWAY_PASSWORD, PRIVATE_PASSWORD, SALT);
+
+ assertThrows(KM200Exception.class, () -> km200.update("/update-retry-disabled", 42));
+
+ verify(1, postRequestedFor(urlEqualTo("/update-retry-disabled")));
+ }
+
private static String loadBody(String path) throws IOException {
return resourceToString(path, UTF_8, KM200Test.class.getClassLoader());
}