/** * Copyright (c) 2014-2017 by the respective copyright holders. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html */ package org.eclipse.smarthome.binding.lifx.internal; import java.nio.ByteBuffer; import java.time.Duration; import java.time.LocalDateTime; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.ReentrantLock; import org.eclipse.smarthome.binding.lifx.LifxBindingConstants; import org.eclipse.smarthome.binding.lifx.handler.LifxLightHandler.CurrentLightState; import org.eclipse.smarthome.binding.lifx.internal.fields.MACAddress; import org.eclipse.smarthome.binding.lifx.internal.listener.LifxResponsePacketListener; import org.eclipse.smarthome.binding.lifx.internal.protocol.GetEchoRequest; import org.eclipse.smarthome.binding.lifx.internal.protocol.GetServiceRequest; import org.eclipse.smarthome.binding.lifx.internal.protocol.Packet; import org.eclipse.smarthome.core.common.ThreadPoolManager; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * The {@link LifxLightOnlineStateUpdater} sets the state of a light offline when it no longer responds to echo packets. * * @author Wouter Born - Extracted class from LifxLightHandler */ public class LifxLightOnlineStateUpdater implements LifxResponsePacketListener { private static final int ECHO_POLLING_INTERVAL = 15; private static final int MAXIMUM_POLLING_RETRIES = 3; private final Logger logger = LoggerFactory.getLogger(LifxLightOnlineStateUpdater.class); private final String macAsHex; private final CurrentLightState currentLightState; private final LifxLightCommunicationHandler communicationHandler; private final ReentrantLock lock = new ReentrantLock(); private final ScheduledExecutorService scheduler = ThreadPoolManager .getScheduledPool(LifxBindingConstants.THREADPOOL_NAME); private ScheduledFuture<?> echoJob; private LocalDateTime lastSeen = LocalDateTime.MIN; private int unansweredEchoPackets; public LifxLightOnlineStateUpdater(MACAddress macAddress, CurrentLightState currentLightState, LifxLightCommunicationHandler communicationHandler) { this.macAsHex = macAddress.getHex(); this.currentLightState = currentLightState; this.communicationHandler = communicationHandler; } private Runnable echoRunnable = new Runnable() { @Override public void run() { try { lock.lock(); logger.trace("{} : Polling", macAsHex); if (currentLightState.isOnline()) { if (Duration.between(lastSeen, LocalDateTime.now()).getSeconds() > ECHO_POLLING_INTERVAL) { if (unansweredEchoPackets < MAXIMUM_POLLING_RETRIES) { ByteBuffer payload = ByteBuffer.allocate(Long.SIZE / 8); payload.putLong(System.currentTimeMillis()); GetEchoRequest request = new GetEchoRequest(); request.setResponseRequired(true); request.setPayload(payload); communicationHandler.sendPacket(request); unansweredEchoPackets++; } else { currentLightState.setOfflineByCommunicationError(); unansweredEchoPackets = 0; } } } else { // are we not configured? let's broadcast instead logger.trace("{} : The light is not online, let's broadcast instead", macAsHex); GetServiceRequest packet = new GetServiceRequest(); communicationHandler.broadcastPacket(packet); } } catch (Exception e) { logger.error("Error occurred while polling online state", e); } finally { lock.unlock(); } } }; public void start() { try { lock.lock(); communicationHandler.addResponsePacketListener(this); if (echoJob == null || echoJob.isCancelled()) { echoJob = scheduler.scheduleWithFixedDelay(echoRunnable, 0, ECHO_POLLING_INTERVAL, TimeUnit.SECONDS); } } catch (Exception e) { logger.error("Error occurred while starting online state poller", e); } finally { lock.unlock(); } } public void stop() { try { lock.lock(); communicationHandler.removeResponsePacketListener(this); if (echoJob != null && !echoJob.isCancelled()) { echoJob.cancel(true); echoJob = null; } } catch (Exception e) { logger.error("Error occurred while stopping online state poller", e); } finally { lock.unlock(); } } @Override public void handleResponsePacket(Packet packet) { lastSeen = LocalDateTime.now(); unansweredEchoPackets = 0; } }