2 * Copyright (c) 2010-2023 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
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
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.binding.lifx.internal;
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;
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;
32 * The {@link LifxLightOnlineStateUpdater} sets the state of a light offline when it no longer responds to echo packets.
34 * @author Wouter Born - Initial contribution
37 public class LifxLightOnlineStateUpdater {
39 private static final int ECHO_POLLING_INTERVAL = 15;
40 private static final int MAXIMUM_POLLING_RETRIES = 3;
42 private final Logger logger = LoggerFactory.getLogger(LifxLightOnlineStateUpdater.class);
44 private final String logId;
45 private final CurrentLightState currentLightState;
46 private final ScheduledExecutorService scheduler;
47 private final LifxLightCommunicationHandler communicationHandler;
49 private final ReentrantLock lock = new ReentrantLock();
51 private @Nullable ScheduledFuture<?> echoJob;
52 private LocalDateTime lastSeen = LocalDateTime.MIN;
53 private int unansweredEchoPackets;
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;
62 public void sendEchoPackets() {
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++;
72 currentLightState.setOfflineByCommunicationError();
73 unansweredEchoPackets = 0;
77 if (communicationHandler.isBroadcastEnabled()) {
78 logger.trace("{} : Light is not online, broadcasting request", logId);
79 communicationHandler.broadcastPacket(new GetServiceRequest());
81 logger.trace("{} : Light is not online, unicasting request", logId);
82 communicationHandler.sendPacket(new GetServiceRequest());
85 } catch (Exception e) {
86 logger.error("Error occurred while polling the online state of a light ({})", logId, e);
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,
101 } catch (Exception e) {
102 logger.error("Error occurred while starting online state poller for a light ({})", logId, e);
111 communicationHandler.removeResponsePacketListener(this::handleResponsePacket);
112 ScheduledFuture<?> localEchoJob = echoJob;
113 if (localEchoJob != null && !localEchoJob.isCancelled()) {
114 localEchoJob.cancel(true);
117 } catch (Exception e) {
118 logger.error("Error occurred while stopping online state poller for a light ({})", logId, e);
124 public void handleResponsePacket(Packet packet) {
125 lastSeen = LocalDateTime.now();
126 unansweredEchoPackets = 0;
127 currentLightState.setOnline();