]> git.basschouten.com Git - openhab-addons.git/blob
3a3892b34d715ab4fb9a149bea65b34b5de2fa4f
[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.lifx.internal;
14
15 import java.time.Duration;
16 import java.time.LocalDateTime;
17 import java.util.concurrent.ScheduledExecutorService;
18 import java.util.concurrent.ScheduledFuture;
19 import java.util.concurrent.TimeUnit;
20 import java.util.concurrent.locks.ReentrantLock;
21
22 import org.eclipse.jdt.annotation.NonNullByDefault;
23 import org.eclipse.jdt.annotation.Nullable;
24 import org.openhab.binding.lifx.internal.dto.GetEchoRequest;
25 import org.openhab.binding.lifx.internal.dto.GetServiceRequest;
26 import org.openhab.binding.lifx.internal.dto.Packet;
27 import org.openhab.binding.lifx.internal.handler.LifxLightHandler.CurrentLightState;
28 import org.slf4j.Logger;
29 import org.slf4j.LoggerFactory;
30
31 /**
32  * The {@link LifxLightOnlineStateUpdater} sets the state of a light offline when it no longer responds to echo packets.
33  *
34  * @author Wouter Born - Initial contribution
35  */
36 @NonNullByDefault
37 public class LifxLightOnlineStateUpdater {
38
39     private static final int ECHO_POLLING_INTERVAL = 15;
40     private static final int MAXIMUM_POLLING_RETRIES = 3;
41
42     private final Logger logger = LoggerFactory.getLogger(LifxLightOnlineStateUpdater.class);
43
44     private final String logId;
45     private final CurrentLightState currentLightState;
46     private final ScheduledExecutorService scheduler;
47     private final LifxLightCommunicationHandler communicationHandler;
48
49     private final ReentrantLock lock = new ReentrantLock();
50
51     private @Nullable ScheduledFuture<?> echoJob;
52     private LocalDateTime lastSeen = LocalDateTime.MIN;
53     private int unansweredEchoPackets;
54
55     public LifxLightOnlineStateUpdater(LifxLightContext context, LifxLightCommunicationHandler communicationHandler) {
56         this.logId = context.getLogId();
57         this.scheduler = context.getScheduler();
58         this.currentLightState = context.getCurrentLightState();
59         this.communicationHandler = communicationHandler;
60     }
61
62     public void sendEchoPackets() {
63         try {
64             lock.lock();
65             logger.trace("{} : Polling light state", logId);
66             if (currentLightState.isOnline()) {
67                 if (Duration.between(lastSeen, LocalDateTime.now()).getSeconds() > ECHO_POLLING_INTERVAL) {
68                     if (unansweredEchoPackets < MAXIMUM_POLLING_RETRIES) {
69                         communicationHandler.sendPacket(GetEchoRequest.currentTimeEchoRequest());
70                         unansweredEchoPackets++;
71                     } else {
72                         currentLightState.setOfflineByCommunicationError();
73                         unansweredEchoPackets = 0;
74                     }
75                 }
76             } else {
77                 if (communicationHandler.isBroadcastEnabled()) {
78                     logger.trace("{} : Light is not online, broadcasting request", logId);
79                     communicationHandler.broadcastPacket(new GetServiceRequest());
80                 } else {
81                     logger.trace("{} : Light is not online, unicasting request", logId);
82                     communicationHandler.sendPacket(new GetServiceRequest());
83                 }
84             }
85         } catch (Exception e) {
86             logger.error("Error occurred while polling the online state of a light ({})", logId, e);
87         } finally {
88             lock.unlock();
89         }
90     }
91
92     public void start() {
93         try {
94             lock.lock();
95             communicationHandler.addResponsePacketListener(this::handleResponsePacket);
96             ScheduledFuture<?> localEchoJob = echoJob;
97             if (localEchoJob == null || localEchoJob.isCancelled()) {
98                 echoJob = scheduler.scheduleWithFixedDelay(this::sendEchoPackets, 0, ECHO_POLLING_INTERVAL,
99                         TimeUnit.SECONDS);
100             }
101         } catch (Exception e) {
102             logger.error("Error occurred while starting online state poller for a light ({})", logId, e);
103         } finally {
104             lock.unlock();
105         }
106     }
107
108     public void stop() {
109         try {
110             lock.lock();
111             communicationHandler.removeResponsePacketListener(this::handleResponsePacket);
112             ScheduledFuture<?> localEchoJob = echoJob;
113             if (localEchoJob != null && !localEchoJob.isCancelled()) {
114                 localEchoJob.cancel(true);
115                 echoJob = null;
116             }
117         } catch (Exception e) {
118             logger.error("Error occurred while stopping online state poller for a light ({})", logId, e);
119         } finally {
120             lock.unlock();
121         }
122     }
123
124     public void handleResponsePacket(Packet packet) {
125         lastSeen = LocalDateTime.now();
126         unansweredEchoPackets = 0;
127         currentLightState.setOnline();
128     }
129 }