From 903bbd40825c0dc74cabf5a78449e21cb2372dbe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20L=27hopital?= Date: Sat, 30 Sep 2023 11:04:47 +0200 Subject: [PATCH] [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 --- .../netatmo/internal/api/dto/HomeData.java | 5 +- .../internal/handler/ApiBridgeHandler.java | 2 +- .../capability/CacheWeatherCapability.java | 59 +++++++++++++++++++ .../handler/capability/MeasureCapability.java | 23 ++++---- .../handler/capability/RefreshCapability.java | 18 +++--- .../handler/capability/WeatherCapability.java | 9 +-- 6 files changed, 89 insertions(+), 27 deletions(-) create mode 100644 bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/capability/CacheWeatherCapability.java diff --git a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/api/dto/HomeData.java b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/api/dto/HomeData.java index 94bba8e54..824497a90 100644 --- a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/api/dto/HomeData.java +++ b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/api/dto/HomeData.java @@ -86,7 +86,7 @@ public class HomeData extends NAThing implements NAModule, LocationEx { private @Nullable String timezone; private NAObjectMap rooms = new NAObjectMap<>(); - private NAObjectMap modules = new NAObjectMap<>(); + private @Nullable NAObjectMap modules; @Override public ModuleType getType() { @@ -118,7 +118,8 @@ public class HomeData extends NAThing implements NAModule, LocationEx { } public NAObjectMap getModules() { - return modules; + NAObjectMap local = modules; + return local != null ? local : new NAObjectMap<>(); } public Set getFeatures() { diff --git a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/ApiBridgeHandler.java b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/ApiBridgeHandler.java index 105801775..340059da0 100644 --- a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/ApiBridgeHandler.java +++ b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/ApiBridgeHandler.java @@ -302,7 +302,7 @@ public class ApiBridgeHandler extends BaseBridgeHandler { public synchronized T executeUri(URI uri, HttpMethod method, Class clazz, @Nullable String payload, @Nullable String contentType, int retryCount) throws NetatmoException { 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); diff --git a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/capability/CacheWeatherCapability.java b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/capability/CacheWeatherCapability.java new file mode 100644 index 000000000..24c7260c3 --- /dev/null +++ b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/capability/CacheWeatherCapability.java @@ -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 { + private final Logger logger = LoggerFactory.getLogger(CacheWeatherCapability.class); + private final Duration validity; + + private List lastResult = List.of(); + private Instant requestTS = Instant.MIN; + + public CacheWeatherCapability(CommonInterface handler, Duration validity) { + super(handler, WeatherApi.class); + this.validity = validity; + } + + @Override + protected List 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 getFreshData(WeatherApi api); +} diff --git a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/capability/MeasureCapability.java b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/capability/MeasureCapability.java index 9e1c3b2b5..738ae2acf 100644 --- a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/capability/MeasureCapability.java +++ b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/capability/MeasureCapability.java @@ -14,6 +14,7 @@ package org.openhab.binding.netatmo.internal.handler.capability; import static org.openhab.binding.netatmo.internal.utils.ChannelTypeUtils.*; +import java.time.Duration; import java.time.ZonedDateTime; import java.util.HashMap; import java.util.List; @@ -42,12 +43,12 @@ import org.slf4j.LoggerFactory; * */ @NonNullByDefault -public class MeasureCapability extends RestCapability { +public class MeasureCapability extends CacheWeatherCapability { private final Logger logger = LoggerFactory.getLogger(MeasureCapability.class); private final Map measures = new HashMap<>(); public MeasureCapability(CommonInterface handler, List helpers) { - super(handler, WeatherApi.class); + super(handler, Duration.ofMinutes(30)); MeasuresChannelHelper measureChannelHelper = (MeasuresChannelHelper) helpers.stream() .filter(c -> c instanceof MeasuresChannelHelper).findFirst() .orElseThrow(() -> new IllegalArgumentException( @@ -55,15 +56,6 @@ public class MeasureCapability extends RestCapability { measureChannelHelper.setMeasures(measures); } - @Override - public List 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) { measures.clear(); handler.getActiveChannels().filter(channel -> !channel.getConfiguration().getProperties().isEmpty()) @@ -90,4 +82,13 @@ public class MeasureCapability extends RestCapability { } }); } + + @Override + protected List 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(); + } } diff --git a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/capability/RefreshCapability.java b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/capability/RefreshCapability.java index 684fc4089..258f10748 100644 --- a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/capability/RefreshCapability.java +++ b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/capability/RefreshCapability.java @@ -16,6 +16,7 @@ import static java.time.temporal.ChronoUnit.*; import java.time.Duration; import java.time.Instant; +import java.time.ZonedDateTime; import java.util.Optional; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; @@ -47,7 +48,7 @@ public class RefreshCapability extends Capability { private Instant dataTimeStamp = Instant.now(); private Instant dataTimeStamp0 = Instant.MIN; private Optional> refreshJob = Optional.empty(); - private final boolean refreshConfigured; + private boolean refreshConfigured; public RefreshCapability(CommonInterface handler, ScheduledExecutorService scheduler, int refreshInterval) { super(handler); @@ -86,27 +87,26 @@ public class RefreshCapability extends Capability { if (probing()) { dataTimeStamp0 = Instant.MIN; } - } else if (refreshConfigured) { - delay = dataValidity.getSeconds(); } 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; - logger.debug("Module refreshed, next one in {} s", delay); + logger.debug("Module refreshed, next one in {}s", delay); freeJobAndReschedule(delay); } @Override protected void updateNAThing(NAThing newData) { super.updateNAThing(newData); - newData.getLastSeen().ifPresent(timestamp -> { - Instant tsInstant = timestamp.toInstant(); + newData.getLastSeen().map(ZonedDateTime::toInstant).ifPresent(tsInstant -> { if (probing()) { if (Instant.MIN.equals(dataTimeStamp0)) { dataTimeStamp0 = tsInstant; logger.debug("First data timestamp is {}", dataTimeStamp0); } else if (tsInstant.isAfter(dataTimeStamp0)) { dataValidity = Duration.between(dataTimeStamp0, tsInstant); + refreshConfigured = true; logger.debug("Data validity period identified to be {}", dataValidity); } else { 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) { refreshJob.ifPresent(job -> job.cancel(false)); - refreshJob = delay == 0 ? Optional.empty() - : Optional.of(scheduler.schedule(() -> proceedWithUpdate(), delay, TimeUnit.SECONDS)); + refreshJob = Optional + .ofNullable(delay == 0 ? null : scheduler.schedule(() -> proceedWithUpdate(), delay, TimeUnit.SECONDS)); } } diff --git a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/capability/WeatherCapability.java b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/capability/WeatherCapability.java index da64056e8..9d6a1b7e0 100644 --- a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/capability/WeatherCapability.java +++ b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/capability/WeatherCapability.java @@ -12,6 +12,7 @@ */ package org.openhab.binding.netatmo.internal.handler.capability; +import java.time.Duration; import java.util.List; import org.eclipse.jdt.annotation.NonNullByDefault; @@ -23,21 +24,21 @@ import org.slf4j.Logger; 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 * */ @NonNullByDefault -public class WeatherCapability extends RestCapability { +public class WeatherCapability extends CacheWeatherCapability { private final Logger logger = LoggerFactory.getLogger(WeatherCapability.class); public WeatherCapability(CommonInterface handler) { - super(handler, WeatherApi.class); + super(handler, Duration.ofSeconds(2)); } @Override - protected List updateReadings(WeatherApi api) { + protected List getFreshData(WeatherApi api) { try { return List.of(owned ? api.getOwnedStationData(handler.getId()) : api.getStationData(handler.getId())); } catch (NetatmoException e) {