]> git.basschouten.com Git - openhab-addons.git/blob
684fc4089aba1ed9baa58fb4a18ed8a7826283b7
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2023 Contributors to the openHAB project
3  *
4  * See the NOTICE file(s) distributed with this work for additional
5  * information.
6  *
7  * This program and the accompanying materials are made available under the
8  * terms of the Eclipse Public License 2.0 which is available at
9  * http://www.eclipse.org/legal/epl-2.0
10  *
11  * SPDX-License-Identifier: EPL-2.0
12  */
13 package org.openhab.binding.netatmo.internal.handler.capability;
14
15 import static java.time.temporal.ChronoUnit.*;
16
17 import java.time.Duration;
18 import java.time.Instant;
19 import java.util.Optional;
20 import java.util.concurrent.ScheduledExecutorService;
21 import java.util.concurrent.ScheduledFuture;
22 import java.util.concurrent.TimeUnit;
23
24 import org.eclipse.jdt.annotation.NonNullByDefault;
25 import org.openhab.binding.netatmo.internal.api.dto.NAThing;
26 import org.openhab.binding.netatmo.internal.handler.CommonInterface;
27 import org.openhab.core.thing.ThingStatus;
28 import org.slf4j.Logger;
29 import org.slf4j.LoggerFactory;
30
31 /**
32  * {@link RefreshCapability} is the class used to embed the refreshing needs calculation for devices
33  *
34  * @author GaĆ«l L'hopital - Initial contribution
35  *
36  */
37 @NonNullByDefault
38 public class RefreshCapability extends Capability {
39     private static final Duration DEFAULT_DELAY = Duration.of(20, SECONDS);
40     private static final Duration PROBING_INTERVAL = Duration.of(120, SECONDS);
41     private static final Duration OFFLINE_INTERVAL = Duration.of(15, MINUTES);
42
43     private final Logger logger = LoggerFactory.getLogger(RefreshCapability.class);
44     private final ScheduledExecutorService scheduler;
45
46     private Duration dataValidity;
47     private Instant dataTimeStamp = Instant.now();
48     private Instant dataTimeStamp0 = Instant.MIN;
49     private Optional<ScheduledFuture<?>> refreshJob = Optional.empty();
50     private final boolean refreshConfigured;
51
52     public RefreshCapability(CommonInterface handler, ScheduledExecutorService scheduler, int refreshInterval) {
53         super(handler);
54         this.scheduler = scheduler;
55         this.dataValidity = Duration.ofSeconds(Math.max(0, refreshInterval));
56         this.refreshConfigured = !probing();
57         freeJobAndReschedule(2);
58     }
59
60     @Override
61     public void dispose() {
62         freeJobAndReschedule(0);
63         super.dispose();
64     }
65
66     @Override
67     public void expireData() {
68         dataTimeStamp = Instant.now().minus(dataValidity);
69         freeJobAndReschedule(1);
70     }
71
72     private Duration dataAge() {
73         return Duration.between(dataTimeStamp, Instant.now());
74     }
75
76     private boolean probing() {
77         return dataValidity.getSeconds() <= 0;
78     }
79
80     private void proceedWithUpdate() {
81         handler.proceedWithUpdate();
82         long delay;
83         if (!ThingStatus.ONLINE.equals(handler.getThing().getStatus())) {
84             logger.debug("Module is not ONLINE; special refresh interval is used");
85             delay = OFFLINE_INTERVAL.toSeconds();
86             if (probing()) {
87                 dataTimeStamp0 = Instant.MIN;
88             }
89         } else if (refreshConfigured) {
90             delay = dataValidity.getSeconds();
91         } else {
92             delay = (probing() ? PROBING_INTERVAL : dataValidity.minus(dataAge()).plus(DEFAULT_DELAY)).toSeconds();
93         }
94         delay = delay < 2 ? PROBING_INTERVAL.toSeconds() : delay;
95         logger.debug("Module refreshed, next one in {} s", delay);
96         freeJobAndReschedule(delay);
97     }
98
99     @Override
100     protected void updateNAThing(NAThing newData) {
101         super.updateNAThing(newData);
102         newData.getLastSeen().ifPresent(timestamp -> {
103             Instant tsInstant = timestamp.toInstant();
104             if (probing()) {
105                 if (Instant.MIN.equals(dataTimeStamp0)) {
106                     dataTimeStamp0 = tsInstant;
107                     logger.debug("First data timestamp is {}", dataTimeStamp0);
108                 } else if (tsInstant.isAfter(dataTimeStamp0)) {
109                     dataValidity = Duration.between(dataTimeStamp0, tsInstant);
110                     logger.debug("Data validity period identified to be {}", dataValidity);
111                 } else {
112                     logger.debug("Data validity period not yet found - data timestamp unchanged");
113                 }
114             }
115             dataTimeStamp = tsInstant;
116         });
117     }
118
119     private void freeJobAndReschedule(long delay) {
120         refreshJob.ifPresent(job -> job.cancel(false));
121         refreshJob = delay == 0 ? Optional.empty()
122                 : Optional.of(scheduler.schedule(() -> proceedWithUpdate(), delay, TimeUnit.SECONDS));
123     }
124 }