[netatmo] Add a buffer to lower Weather API requests (#15590)
* Add a buffer to lower Weather API requests * Correcting typo identified in PR #15587 after merge --------- Signed-off-by: clinique <gael@lhopital.org>
This commit is contained in:
parent
9d03ec9af5
commit
903bbd4082
|
@ -86,7 +86,7 @@ public class HomeData extends NAThing implements NAModule, LocationEx {
|
||||||
private @Nullable String timezone;
|
private @Nullable String timezone;
|
||||||
|
|
||||||
private NAObjectMap<HomeDataRoom> rooms = new NAObjectMap<>();
|
private NAObjectMap<HomeDataRoom> rooms = new NAObjectMap<>();
|
||||||
private NAObjectMap<HomeDataModule> modules = new NAObjectMap<>();
|
private @Nullable NAObjectMap<HomeDataModule> modules;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ModuleType getType() {
|
public ModuleType getType() {
|
||||||
|
@ -118,7 +118,8 @@ public class HomeData extends NAThing implements NAModule, LocationEx {
|
||||||
}
|
}
|
||||||
|
|
||||||
public NAObjectMap<HomeDataModule> getModules() {
|
public NAObjectMap<HomeDataModule> getModules() {
|
||||||
return modules;
|
NAObjectMap<HomeDataModule> local = modules;
|
||||||
|
return local != null ? local : new NAObjectMap<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Set<FeatureArea> getFeatures() {
|
public Set<FeatureArea> getFeatures() {
|
||||||
|
|
|
@ -302,7 +302,7 @@ public class ApiBridgeHandler extends BaseBridgeHandler {
|
||||||
public synchronized <T> T executeUri(URI uri, HttpMethod method, Class<T> clazz, @Nullable String payload,
|
public synchronized <T> T executeUri(URI uri, HttpMethod method, Class<T> clazz, @Nullable String payload,
|
||||||
@Nullable String contentType, int retryCount) throws NetatmoException {
|
@Nullable String contentType, int retryCount) throws NetatmoException {
|
||||||
try {
|
try {
|
||||||
logger.trace("executeUri {} {} ", method.toString(), uri);
|
logger.debug("executeUri {} {} ", method.toString(), uri);
|
||||||
|
|
||||||
Request request = httpClient.newRequest(uri).method(method).timeout(TIMEOUT_S, TimeUnit.SECONDS);
|
Request request = httpClient.newRequest(uri).method(method).timeout(TIMEOUT_S, TimeUnit.SECONDS);
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,59 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2023 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.netatmo.internal.handler.capability;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
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 CacheWeatherCapability} give the ability to buffer weather related requests and reduce server requests
|
||||||
|
*
|
||||||
|
* @author Gaël L'hopital - Initial contribution
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
|
public abstract class CacheWeatherCapability extends RestCapability<WeatherApi> {
|
||||||
|
private final Logger logger = LoggerFactory.getLogger(CacheWeatherCapability.class);
|
||||||
|
private final Duration validity;
|
||||||
|
|
||||||
|
private List<NAObject> lastResult = List.of();
|
||||||
|
private Instant requestTS = Instant.MIN;
|
||||||
|
|
||||||
|
public CacheWeatherCapability(CommonInterface handler, Duration validity) {
|
||||||
|
super(handler, WeatherApi.class);
|
||||||
|
this.validity = validity;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
return lastResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract List<NAObject> getFreshData(WeatherApi api);
|
||||||
|
}
|
|
@ -14,6 +14,7 @@ package org.openhab.binding.netatmo.internal.handler.capability;
|
||||||
|
|
||||||
import static org.openhab.binding.netatmo.internal.utils.ChannelTypeUtils.*;
|
import static org.openhab.binding.netatmo.internal.utils.ChannelTypeUtils.*;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
import java.time.ZonedDateTime;
|
import java.time.ZonedDateTime;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -42,12 +43,12 @@ import org.slf4j.LoggerFactory;
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
@NonNullByDefault
|
@NonNullByDefault
|
||||||
public class MeasureCapability extends RestCapability<WeatherApi> {
|
public class MeasureCapability extends CacheWeatherCapability {
|
||||||
private final Logger logger = LoggerFactory.getLogger(MeasureCapability.class);
|
private final Logger logger = LoggerFactory.getLogger(MeasureCapability.class);
|
||||||
private final Map<String, State> measures = new HashMap<>();
|
private final Map<String, State> measures = new HashMap<>();
|
||||||
|
|
||||||
public MeasureCapability(CommonInterface handler, List<ChannelHelper> helpers) {
|
public MeasureCapability(CommonInterface handler, List<ChannelHelper> helpers) {
|
||||||
super(handler, WeatherApi.class);
|
super(handler, Duration.ofMinutes(30));
|
||||||
MeasuresChannelHelper measureChannelHelper = (MeasuresChannelHelper) helpers.stream()
|
MeasuresChannelHelper measureChannelHelper = (MeasuresChannelHelper) helpers.stream()
|
||||||
.filter(c -> c instanceof MeasuresChannelHelper).findFirst()
|
.filter(c -> c instanceof MeasuresChannelHelper).findFirst()
|
||||||
.orElseThrow(() -> new IllegalArgumentException(
|
.orElseThrow(() -> new IllegalArgumentException(
|
||||||
|
@ -55,15 +56,6 @@ public class MeasureCapability extends RestCapability<WeatherApi> {
|
||||||
measureChannelHelper.setMeasures(measures);
|
measureChannelHelper.setMeasures(measures);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<NAObject> updateReadings(WeatherApi api) {
|
|
||||||
String bridgeId = handler.getBridgeId();
|
|
||||||
String deviceId = bridgeId != null ? bridgeId : handler.getId();
|
|
||||||
String moduleId = bridgeId != null ? handler.getId() : null;
|
|
||||||
updateMeasures(api, deviceId, moduleId);
|
|
||||||
return List.of();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateMeasures(WeatherApi api, String deviceId, @Nullable String moduleId) {
|
private void updateMeasures(WeatherApi api, String deviceId, @Nullable String moduleId) {
|
||||||
measures.clear();
|
measures.clear();
|
||||||
handler.getActiveChannels().filter(channel -> !channel.getConfiguration().getProperties().isEmpty())
|
handler.getActiveChannels().filter(channel -> !channel.getConfiguration().getProperties().isEmpty())
|
||||||
|
@ -90,4 +82,13 @@ public class MeasureCapability extends RestCapability<WeatherApi> {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected List<NAObject> getFreshData(WeatherApi api) {
|
||||||
|
String bridgeId = handler.getBridgeId();
|
||||||
|
String deviceId = bridgeId != null ? bridgeId : handler.getId();
|
||||||
|
String moduleId = bridgeId != null ? handler.getId() : null;
|
||||||
|
updateMeasures(api, deviceId, moduleId);
|
||||||
|
return List.of();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,7 @@ import static java.time.temporal.ChronoUnit.*;
|
||||||
|
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
|
import java.time.ZonedDateTime;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.concurrent.ScheduledExecutorService;
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
import java.util.concurrent.ScheduledFuture;
|
import java.util.concurrent.ScheduledFuture;
|
||||||
|
@ -47,7 +48,7 @@ public class RefreshCapability extends Capability {
|
||||||
private Instant dataTimeStamp = Instant.now();
|
private Instant dataTimeStamp = Instant.now();
|
||||||
private Instant dataTimeStamp0 = Instant.MIN;
|
private Instant dataTimeStamp0 = Instant.MIN;
|
||||||
private Optional<ScheduledFuture<?>> refreshJob = Optional.empty();
|
private Optional<ScheduledFuture<?>> refreshJob = Optional.empty();
|
||||||
private final boolean refreshConfigured;
|
private boolean refreshConfigured;
|
||||||
|
|
||||||
public RefreshCapability(CommonInterface handler, ScheduledExecutorService scheduler, int refreshInterval) {
|
public RefreshCapability(CommonInterface handler, ScheduledExecutorService scheduler, int refreshInterval) {
|
||||||
super(handler);
|
super(handler);
|
||||||
|
@ -86,27 +87,26 @@ public class RefreshCapability extends Capability {
|
||||||
if (probing()) {
|
if (probing()) {
|
||||||
dataTimeStamp0 = Instant.MIN;
|
dataTimeStamp0 = Instant.MIN;
|
||||||
}
|
}
|
||||||
} else if (refreshConfigured) {
|
|
||||||
delay = dataValidity.getSeconds();
|
|
||||||
} else {
|
} else {
|
||||||
delay = (probing() ? PROBING_INTERVAL : dataValidity.minus(dataAge()).plus(DEFAULT_DELAY)).toSeconds();
|
delay = refreshConfigured ? dataValidity.getSeconds()
|
||||||
|
: (probing() ? PROBING_INTERVAL : dataValidity.minus(dataAge()).plus(DEFAULT_DELAY)).toSeconds();
|
||||||
}
|
}
|
||||||
delay = delay < 2 ? PROBING_INTERVAL.toSeconds() : delay;
|
delay = delay < 2 ? PROBING_INTERVAL.toSeconds() : delay;
|
||||||
logger.debug("Module refreshed, next one in {} s", delay);
|
logger.debug("Module refreshed, next one in {}s", delay);
|
||||||
freeJobAndReschedule(delay);
|
freeJobAndReschedule(delay);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void updateNAThing(NAThing newData) {
|
protected void updateNAThing(NAThing newData) {
|
||||||
super.updateNAThing(newData);
|
super.updateNAThing(newData);
|
||||||
newData.getLastSeen().ifPresent(timestamp -> {
|
newData.getLastSeen().map(ZonedDateTime::toInstant).ifPresent(tsInstant -> {
|
||||||
Instant tsInstant = timestamp.toInstant();
|
|
||||||
if (probing()) {
|
if (probing()) {
|
||||||
if (Instant.MIN.equals(dataTimeStamp0)) {
|
if (Instant.MIN.equals(dataTimeStamp0)) {
|
||||||
dataTimeStamp0 = tsInstant;
|
dataTimeStamp0 = tsInstant;
|
||||||
logger.debug("First data timestamp is {}", dataTimeStamp0);
|
logger.debug("First data timestamp is {}", dataTimeStamp0);
|
||||||
} else if (tsInstant.isAfter(dataTimeStamp0)) {
|
} else if (tsInstant.isAfter(dataTimeStamp0)) {
|
||||||
dataValidity = Duration.between(dataTimeStamp0, tsInstant);
|
dataValidity = Duration.between(dataTimeStamp0, tsInstant);
|
||||||
|
refreshConfigured = true;
|
||||||
logger.debug("Data validity period identified to be {}", dataValidity);
|
logger.debug("Data validity period identified to be {}", dataValidity);
|
||||||
} else {
|
} else {
|
||||||
logger.debug("Data validity period not yet found - data timestamp unchanged");
|
logger.debug("Data validity period not yet found - data timestamp unchanged");
|
||||||
|
@ -118,7 +118,7 @@ public class RefreshCapability extends Capability {
|
||||||
|
|
||||||
private void freeJobAndReschedule(long delay) {
|
private void freeJobAndReschedule(long delay) {
|
||||||
refreshJob.ifPresent(job -> job.cancel(false));
|
refreshJob.ifPresent(job -> job.cancel(false));
|
||||||
refreshJob = delay == 0 ? Optional.empty()
|
refreshJob = Optional
|
||||||
: Optional.of(scheduler.schedule(() -> proceedWithUpdate(), delay, TimeUnit.SECONDS));
|
.ofNullable(delay == 0 ? null : scheduler.schedule(() -> proceedWithUpdate(), delay, TimeUnit.SECONDS));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
*/
|
*/
|
||||||
package org.openhab.binding.netatmo.internal.handler.capability;
|
package org.openhab.binding.netatmo.internal.handler.capability;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
@ -23,21 +24,21 @@ import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@link WeatherCapability} give the ability to read weather station api
|
* {@link WeatherCapability} give the ability to read weather station API
|
||||||
*
|
*
|
||||||
* @author Gaël L'hopital - Initial contribution
|
* @author Gaël L'hopital - Initial contribution
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
@NonNullByDefault
|
@NonNullByDefault
|
||||||
public class WeatherCapability extends RestCapability<WeatherApi> {
|
public class WeatherCapability extends CacheWeatherCapability {
|
||||||
private final Logger logger = LoggerFactory.getLogger(WeatherCapability.class);
|
private final Logger logger = LoggerFactory.getLogger(WeatherCapability.class);
|
||||||
|
|
||||||
public WeatherCapability(CommonInterface handler) {
|
public WeatherCapability(CommonInterface handler) {
|
||||||
super(handler, WeatherApi.class);
|
super(handler, Duration.ofSeconds(2));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected List<NAObject> updateReadings(WeatherApi api) {
|
protected List<NAObject> getFreshData(WeatherApi api) {
|
||||||
try {
|
try {
|
||||||
return List.of(owned ? api.getOwnedStationData(handler.getId()) : api.getStationData(handler.getId()));
|
return List.of(owned ? api.getOwnedStationData(handler.getId()) : api.getStationData(handler.getId()));
|
||||||
} catch (NetatmoException e) {
|
} catch (NetatmoException e) {
|
||||||
|
|
Loading…
Reference in New Issue