Skip to content

Commit

Permalink
Manually backport openhab#16489
Browse files Browse the repository at this point in the history
Signed-off-by: Jacob Laursen <jacob-github@vindvejr.dk>
  • Loading branch information
jlaur committed Mar 21, 2024
1 parent 2763b54 commit 18fe34f
Show file tree
Hide file tree
Showing 11 changed files with 43 additions and 45 deletions.
4 changes: 3 additions & 1 deletion bundles/org.openhab.binding.netatmo/pom.xml
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">

<modelVersion>4.0.0</modelVersion>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
public enum ConfigurationLevel {
EMPTY_CLIENT_ID("@text/conf-error-no-client-id"),
EMPTY_CLIENT_SECRET("@text/conf-error-no-client-secret"),
REFRESH_TOKEN_NEEDED("@text/conf-error-grant-needed"),
REFRESH_TOKEN_NEEDED("@text/conf-error-grant-needed [ \"%s\" ]"),
COMPLETED("");

public String message;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@
@NonNullByDefault
public class ApiBridgeHandler extends BaseBridgeHandler {
private static final int TIMEOUT_S = 20;
private static final int API_LIMIT_INTERVAL_S = 3600;

private final Logger logger = LoggerFactory.getLogger(ApiBridgeHandler.class);
private final AuthenticationApi connectApi = new AuthenticationApi(this);
Expand Down Expand Up @@ -210,8 +211,7 @@ private boolean authenticate(@Nullable String code, @Nullable String redirectUri
startAuthorizationFlow();
return false;
} catch (IOException e) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
prepareReconnection(code, redirectUri);
prepareReconnection(getConfiguration().reconnectInterval, e.getMessage(), code, redirectUri);
return false;
}

Expand All @@ -232,18 +232,20 @@ private void startAuthorizationFlow() {
servlet.startListening();
grantServlet = Optional.of(servlet);
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
ConfigurationLevel.REFRESH_TOKEN_NEEDED.message);
ConfigurationLevel.REFRESH_TOKEN_NEEDED.message.formatted(servlet.getPath()));
}

public ApiHandlerConfiguration getConfiguration() {
return getConfigAs(ApiHandlerConfiguration.class);
}

private void prepareReconnection(@Nullable String code, @Nullable String redirectUri) {
private void prepareReconnection(int delay, @Nullable String message, @Nullable String code,
@Nullable String redirectUri) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, message);
connectApi.dispose();
freeConnectJob();
connectJob = Optional.of(scheduler.schedule(() -> openConnection(code, redirectUri),
getConfiguration().reconnectInterval, TimeUnit.SECONDS));
connectJob = Optional.of(scheduler.schedule(() -> openConnection(code, redirectUri), delay, TimeUnit.SECONDS));
logger.debug("Reconnection scheduled in {} seconds", delay);
}

private void freeConnectJob() {
Expand Down Expand Up @@ -307,7 +309,7 @@ public synchronized <T> T executeUri(URI uri, HttpMethod method, Class<T> clazz,
Request request = httpClient.newRequest(uri).method(method).timeout(TIMEOUT_S, TimeUnit.SECONDS);

if (!authenticate(null, null)) {
prepareReconnection(null, null);
prepareReconnection(getConfiguration().reconnectInterval, null, null, "@text/status-bridge-offline");
throw new NetatmoException("Not authenticated");
}
connectApi.getAuthorization().ifPresent(auth -> request.header(HttpHeader.AUTHORIZATION, auth));
Expand Down Expand Up @@ -353,22 +355,17 @@ public synchronized <T> T executeUri(URI uri, HttpMethod method, Class<T> clazz,
throw exception;
} catch (NetatmoException e) {
if (e.getStatusCode() == ServiceError.MAXIMUM_USAGE_REACHED) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
prepareReconnection(null, null);
prepareReconnection(API_LIMIT_INTERVAL_S,
"@text/maximum-usage-reached [ \"%d\" ]".formatted(API_LIMIT_INTERVAL_S), null, null);
}
throw e;
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
throw new NetatmoException("Request interrupted");
} catch (TimeoutException | ExecutionException e) {
} catch (InterruptedException | TimeoutException | ExecutionException e) {
if (retryCount > 0) {
logger.debug("Request timedout, retry counter: {}", retryCount);
logger.debug("Request error, retry counter: {}", retryCount);
return executeUri(uri, method, clazz, payload, contentType, retryCount - 1);
}
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "@text/request-time-out");
prepareReconnection(null, null);
throw new NetatmoException(String.format("%s: \"%s\"", e.getClass().getName(), e.getMessage()));
prepareReconnection(getConfiguration().reconnectInterval, "@text/request-time-out", null, e.getMessage());
throw new NetatmoException("%s: \"%s\"".formatted(e.getClass().getName(), e.getMessage()));
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ protected List<NAObject> updateReadings(AircareApi api) {
try {
return List.of(api.getHomeCoach(handler.getId()));
} catch (NetatmoException e) {
logger.warn("Error retrieving home-coach data '{}' : {}", handler.getId(), e.getMessage());
logger.warn("Error retrieving home-coach data '{}': {}", handler.getId(), e.getMessage());
}
return List.of();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,13 +43,16 @@ public CacheWeatherCapability(CommonInterface handler, Duration validity) {
}

@Override
protected List<NAObject> updateReadings(WeatherApi api) {
protected synchronized List<NAObject> updateReadings(WeatherApi api) {
Instant now = Instant.now();

if (requestTS.plus(validity).isBefore(now)) {
logger.debug("Requesting fresh data");
lastResult = getFreshData(api);
requestTS = now;
logger.debug("{} requesting fresh data for {}", getClass().getSimpleName(), handler.getId());
List<NAObject> result = getFreshData(api);
if (!result.isEmpty()) {
lastResult = result;
requestTS = now;
}
}

return lastResult;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,7 @@ public ParentUpdateCapability(CommonInterface handler) {
public void initialize() {
job = Optional.of(handler.getScheduler().schedule(() -> {
logger.debug("Requesting parents data update for Thing {}", handler.getId());
CommonInterface bridgeHandler = handler.getBridgeHandler();
if (bridgeHandler != null) {
bridgeHandler.expireData();
}
expireData();
}, DEFAULT_DELAY_S, TimeUnit.SECONDS));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@
public class RefreshCapability extends Capability {
private static final Duration DEFAULT_DELAY = Duration.of(20, SECONDS);
private static final Duration PROBING_INTERVAL = Duration.of(120, SECONDS);
private static final Duration OFFLINE_INTERVAL = Duration.of(15, MINUTES);

private final Logger logger = LoggerFactory.getLogger(RefreshCapability.class);

Expand Down Expand Up @@ -82,18 +81,16 @@ private boolean probing() {
private void proceedWithUpdate() {
handler.proceedWithUpdate();
long delay;
if (!ThingStatus.ONLINE.equals(handler.getThing().getStatus())) {
logger.debug("Module is not ONLINE; special refresh interval is used");
delay = OFFLINE_INTERVAL.toSeconds();
if (probing()) {
dataTimeStamp0 = Instant.MIN;
}
if (!ThingStatus.ONLINE.equals(thing.getStatus())) {
logger.debug("Module is not ONLINE; refresh stopped");
dataTimeStamp0 = Instant.MIN;
delay = 0;
} else {
delay = refreshConfigured ? dataValidity.getSeconds()
: (probing() ? PROBING_INTERVAL : dataValidity.minus(dataAge()).plus(DEFAULT_DELAY)).toSeconds();
delay = delay < 2 ? PROBING_INTERVAL.toSeconds() : delay;
logger.debug("Module refreshed, next one in {}s", delay);
}
delay = delay < 2 ? PROBING_INTERVAL.toSeconds() : delay;
logger.debug("Module refreshed, next one in {}s", delay);
freeJobAndReschedule(delay);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,6 @@
import org.openhab.binding.netatmo.internal.api.WeatherApi;
import org.openhab.binding.netatmo.internal.api.dto.NAObject;
import org.openhab.binding.netatmo.internal.handler.CommonInterface;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* {@link WeatherCapability} give the ability to read weather station API
Expand All @@ -31,7 +29,6 @@
*/
@NonNullByDefault
public class WeatherCapability extends CacheWeatherCapability {
private final Logger logger = LoggerFactory.getLogger(WeatherCapability.class);

public WeatherCapability(CommonInterface handler) {
super(handler, Duration.ofSeconds(2));
Expand All @@ -42,7 +39,7 @@ protected List<NAObject> getFreshData(WeatherApi api) {
try {
return List.of(owned ? api.getOwnedStationData(handler.getId()) : api.getStationData(handler.getId()));
} catch (NetatmoException e) {
logger.warn("Error retrieving weather data '{}' : {}", handler.getId(), e.getMessage());
statusReason = "Error retrieving weather data '%s' : %s".formatted(handler.getId(), e.getMessage());
}
return List.of();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,9 @@ public abstract class NetatmoServlet extends HttpServlet {

private final Logger logger = LoggerFactory.getLogger(this.getClass());
private final HttpService httpService;
private final String path;

protected final ApiBridgeHandler handler;
protected final String path;

public NetatmoServlet(ApiBridgeHandler handler, HttpService httpService, String localPath) {
this.path = BASE_PATH + localPath + "/" + handler.getId();
Expand All @@ -60,4 +60,8 @@ public void dispose() {
httpService.unregister(path);
this.destroy();
}

public String getPath() {
return path;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ public WebhookServlet(ApiBridgeHandler handler, HttpService httpService, NADeser
@Override
public void startListening() {
super.startListening();
URI uri = UriBuilder.fromUri(webHookUrl).path(path + webHookPostfix).build();
URI uri = UriBuilder.fromUri(webHookUrl).path(getPath() + webHookPostfix).build();
try {
logger.info("Setting up WebHook at Netatmo to {}", uri.toString());
hookSet = securityApi.addwebhook(uri);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -459,12 +459,13 @@ config.refreshInterval.description = The refresh interval to poll Netatmo API (i

conf-error-no-client-id = Cannot connect to Netatmo bridge as no client id is available in the configuration
conf-error-no-client-secret = Cannot connect to Netatmo bridge as no client secret is available in the configuration
conf-error-grant-needed = Configuration incomplete, please grant the binding to Netatmo Connect.
conf-error-grant-needed = Complete the configuration by granting the binding to Netatmo Connect : `{0}`.
status-bridge-offline = Bridge is not connected to Netatmo API
device-not-connected = Thing is not reachable
data-over-limit = Data seems quite old
request-time-out = Request timed out - will attempt reconnection later
deserialization-unknown = Deserialization lead to an unknown code
maximum-usage-reached = Maximum usage reached, will reconnect in {0} seconds.

homestatus-unknown-error = Unknown error
homestatus-internal-error = Internal error
Expand Down

0 comments on commit 18fe34f

Please sign in to comment.