[somfytahoma] Improvements to avoid cloud login throttling (#15489)

* [somfytahoma] Improvements to avoid cloud login throttling
* [somfytahoma] add custom message to status display

---------

Signed-off-by: Ondrej Pecta <opecta@gmail.com>
This commit is contained in:
Ondrej Pecta 2023-09-09 12:43:08 +02:00 committed by GitHub
parent 9bbcb85f59
commit c0d66da660
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 30 additions and 5 deletions

View File

@ -390,8 +390,10 @@ public class SomfyTahomaBindingConstants {
public static final String AUTHENTICATION_OAUTH_GRANT_ERROR = "Provided Authorization Grant is invalid."; public static final String AUTHENTICATION_OAUTH_GRANT_ERROR = "Provided Authorization Grant is invalid.";
public static final String AUTHENTICATION_OAUTH_INVALID_GRANT = "error.invalid.grant"; public static final String AUTHENTICATION_OAUTH_INVALID_GRANT = "error.invalid.grant";
public static final String OPENHAB_TOKEN = "openHAB token"; public static final String OPENHAB_TOKEN = "openHAB token";
public static final int SUSPEND_TIME = 120; public static final int SUSPEND_TIME = 300;
public static final int RECONCILIATION_TIME = 600; public static final int RECONCILIATION_TIME = 600;
public static final int LOGIN_LIMIT_TIME = 60;
public static final int MAX_ERRORS = 5;
// Commands // Commands
public static final String COMMAND_MY = "my"; public static final String COMMAND_MY = "my";

View File

@ -19,6 +19,7 @@ import java.net.InetAddress;
import java.net.URLEncoder; import java.net.URLEncoder;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.time.Duration; import java.time.Duration;
import java.time.Instant;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
@ -139,6 +140,12 @@ public class SomfyTahomaBridgeHandler extends BaseBridgeHandler {
// Cloud fallback // Cloud fallback
private boolean cloudFallback = false; private boolean cloudFallback = false;
// Communication errors counter
private int errorsCounter = 0;
// Last login timestamp
private Instant lastLoginTimestamp = Instant.MIN;
/** /**
* Our configuration * Our configuration
*/ */
@ -240,8 +247,8 @@ public class SomfyTahomaBridgeHandler extends BaseBridgeHandler {
return; return;
} }
if (tooManyRequests) { if (tooManyRequests || Instant.now().minusSeconds(LOGIN_LIMIT_TIME).isBefore(lastLoginTimestamp)) {
logger.debug("Skipping login due to too many requests"); logger.debug("Postponing login to avoid throttling");
return; return;
} }
@ -277,6 +284,8 @@ public class SomfyTahomaBridgeHandler extends BaseBridgeHandler {
SomfyTahomaLoginResponse data = gson.fromJson(response.getContentAsString(), SomfyTahomaLoginResponse data = gson.fromJson(response.getContentAsString(),
SomfyTahomaLoginResponse.class); SomfyTahomaLoginResponse.class);
lastLoginTimestamp = Instant.now();
if (data == null) { if (data == null) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
"Received invalid data (login)"); "Received invalid data (login)");
@ -844,7 +853,7 @@ public class SomfyTahomaBridgeHandler extends BaseBridgeHandler {
private Request sendRequestBuilderLocal(String subUrl, HttpMethod method) { private Request sendRequestBuilderLocal(String subUrl, HttpMethod method) {
return httpClient.newRequest(getApiFullUrl(subUrl)).method(method).accept("application/json") return httpClient.newRequest(getApiFullUrl(subUrl)).method(method).accept("application/json")
.header(HttpHeader.AUTHORIZATION, "Bearer " + localToken); .timeout(TAHOMA_TIMEOUT, TimeUnit.SECONDS).header(HttpHeader.AUTHORIZATION, "Bearer " + localToken);
} }
/** /**
@ -1123,6 +1132,7 @@ public class SomfyTahomaBridgeHandler extends BaseBridgeHandler {
response = sendDeleteToTahomaWithCookie(url); response = sendDeleteToTahomaWithCookie(url);
default: default:
} }
errorsCounter = 0;
return classOfT != null ? gson.fromJson(response, classOfT) : null; return classOfT != null ? gson.fromJson(response, classOfT) : null;
} catch (JsonSyntaxException e) { } catch (JsonSyntaxException e) {
logger.debug("Received data: {} is not JSON", response, e); logger.debug("Received data: {} is not JSON", response, e);
@ -1132,14 +1142,27 @@ public class SomfyTahomaBridgeHandler extends BaseBridgeHandler {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Temporarily banned"); updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Temporarily banned");
setTooManyRequests(); setTooManyRequests();
} else if (isEventListenerTimeout(e)) { } else if (isEventListenerTimeout(e)) {
logger.debug("Event listener timeout occurred", e);
reLogin(); reLogin();
} else if (isDevModeReady()) {
// the local gateway is unreachable
errorsCounter++;
logger.debug("Local gateway communication error", e);
discoverGateway();
if (errorsCounter > MAX_ERRORS) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
"Too many communication errors");
}
} else { } else {
logger.debug("Cannot call url: {} with params: {}!", getApiFullUrl(url), urlParameters, e); logger.debug("Cannot call url: {} with params: {}!", getApiFullUrl(url), urlParameters, e);
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR); updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR);
} }
} catch (TimeoutException e) { } catch (TimeoutException e) {
errorsCounter++;
logger.debug("Timeout when calling url: {} with params: {}!", getApiFullUrl(url), urlParameters, e); logger.debug("Timeout when calling url: {} with params: {}!", getApiFullUrl(url), urlParameters, e);
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR); if (errorsCounter > MAX_ERRORS) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Too many timeouts");
}
} catch (InterruptedException e) { } catch (InterruptedException e) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR); updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR);
Thread.currentThread().interrupt(); Thread.currentThread().interrupt();