2 * Copyright (c) 2010-2021 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 static org.openhab.binding.lifx.internal.LifxBindingConstants.MIN_ZONE_INDEX;
16 import static org.openhab.binding.lifx.internal.LifxProduct.Feature.*;
17 import static org.openhab.binding.lifx.internal.util.LifxMessageUtil.infraredToPercentType;
19 import java.util.concurrent.ScheduledExecutorService;
20 import java.util.concurrent.ScheduledFuture;
21 import java.util.concurrent.TimeUnit;
22 import java.util.concurrent.locks.ReentrantLock;
24 import org.eclipse.jdt.annotation.NonNullByDefault;
25 import org.eclipse.jdt.annotation.Nullable;
26 import org.openhab.binding.lifx.internal.dto.GetColorZonesRequest;
27 import org.openhab.binding.lifx.internal.dto.GetLightInfraredRequest;
28 import org.openhab.binding.lifx.internal.dto.GetRequest;
29 import org.openhab.binding.lifx.internal.dto.GetTileEffectRequest;
30 import org.openhab.binding.lifx.internal.dto.GetWifiInfoRequest;
31 import org.openhab.binding.lifx.internal.dto.Packet;
32 import org.openhab.binding.lifx.internal.dto.StateLightInfraredResponse;
33 import org.openhab.binding.lifx.internal.dto.StateLightPowerResponse;
34 import org.openhab.binding.lifx.internal.dto.StateMultiZoneResponse;
35 import org.openhab.binding.lifx.internal.dto.StatePowerResponse;
36 import org.openhab.binding.lifx.internal.dto.StateResponse;
37 import org.openhab.binding.lifx.internal.dto.StateTileEffectResponse;
38 import org.openhab.binding.lifx.internal.dto.StateWifiInfoResponse;
39 import org.openhab.binding.lifx.internal.fields.HSBK;
40 import org.openhab.binding.lifx.internal.handler.LifxLightHandler.CurrentLightState;
41 import org.openhab.core.library.types.PercentType;
42 import org.slf4j.Logger;
43 import org.slf4j.LoggerFactory;
46 * The {@link LifxLightCurrentStateUpdater} sends packets to a light in order to update the {@code currentLightState} to
47 * the actual light state.
49 * @author Wouter Born - Initial contribution
52 public class LifxLightCurrentStateUpdater {
54 private static final int STATE_POLLING_INTERVAL = 3;
56 private final Logger logger = LoggerFactory.getLogger(LifxLightCurrentStateUpdater.class);
58 private final String logId;
59 private final LifxProduct product;
60 private final CurrentLightState currentLightState;
61 private final ScheduledExecutorService scheduler;
62 private final LifxLightCommunicationHandler communicationHandler;
64 private final ReentrantLock lock = new ReentrantLock();
66 private boolean wasOnline;
67 private boolean updateSignalStrength;
69 private @Nullable ScheduledFuture<?> statePollingJob;
71 public LifxLightCurrentStateUpdater(LifxLightContext context, LifxLightCommunicationHandler communicationHandler) {
72 this.logId = context.getLogId();
73 this.product = context.getProduct();
74 this.currentLightState = context.getCurrentLightState();
75 this.scheduler = context.getScheduler();
76 this.communicationHandler = communicationHandler;
79 public void pollLightState() {
82 if (currentLightState.isOnline()) {
83 logger.trace("{} : Polling the state of the light", logId);
84 sendLightStateRequests();
86 logger.trace("{} : The light is not online, there is no point polling it", logId);
88 wasOnline = currentLightState.isOnline();
89 } catch (Exception e) {
90 logger.error("Error occurred while polling light state", e);
96 public void setUpdateSignalStrength(boolean updateSignalStrength) {
97 this.updateSignalStrength = updateSignalStrength;
100 public void start() {
103 communicationHandler.addResponsePacketListener(this::handleResponsePacket);
104 ScheduledFuture<?> localStatePollingJob = statePollingJob;
105 if (localStatePollingJob == null || localStatePollingJob.isCancelled()) {
106 statePollingJob = scheduler.scheduleWithFixedDelay(this::pollLightState, 0, STATE_POLLING_INTERVAL,
109 } catch (Exception e) {
110 logger.error("Error occurred while starting light state updater", e);
119 communicationHandler.removeResponsePacketListener(this::handleResponsePacket);
120 ScheduledFuture<?> localStatePollingJob = statePollingJob;
121 if (localStatePollingJob != null && !localStatePollingJob.isCancelled()) {
122 localStatePollingJob.cancel(true);
123 statePollingJob = null;
125 } catch (Exception e) {
126 logger.error("Error occurred while stopping light state updater", e);
132 private void sendLightStateRequests() {
133 communicationHandler.sendPacket(new GetRequest());
135 if (product.hasFeature(INFRARED)) {
136 communicationHandler.sendPacket(new GetLightInfraredRequest());
138 if (product.hasFeature(MULTIZONE)) {
139 communicationHandler.sendPacket(new GetColorZonesRequest());
141 if (product.hasFeature(TILE_EFFECT)) {
142 communicationHandler.sendPacket(new GetTileEffectRequest());
144 if (updateSignalStrength) {
145 communicationHandler.sendPacket(new GetWifiInfoRequest());
149 public void handleResponsePacket(Packet packet) {
153 if (packet instanceof StateResponse) {
154 handleLightStatus((StateResponse) packet);
155 } else if (packet instanceof StatePowerResponse) {
156 handlePowerStatus((StatePowerResponse) packet);
157 } else if (packet instanceof StateLightPowerResponse) {
158 handleLightPowerStatus((StateLightPowerResponse) packet);
159 } else if (packet instanceof StateLightInfraredResponse) {
160 handleInfraredStatus((StateLightInfraredResponse) packet);
161 } else if (packet instanceof StateMultiZoneResponse) {
162 handleMultiZoneStatus((StateMultiZoneResponse) packet);
163 } else if (packet instanceof StateWifiInfoResponse) {
164 handleWifiInfoStatus((StateWifiInfoResponse) packet);
165 } else if (packet instanceof StateTileEffectResponse) {
166 handleTileEffectStatus((StateTileEffectResponse) packet);
169 currentLightState.setOnline();
171 if (currentLightState.isOnline() && !wasOnline) {
173 logger.trace("{} : The light just went online, immediately polling the state of the light", logId);
174 sendLightStateRequests();
181 private void handleLightStatus(StateResponse packet) {
182 currentLightState.setColor(packet.getColor(), MIN_ZONE_INDEX);
183 currentLightState.setPowerState(packet.getPower());
186 private void handlePowerStatus(StatePowerResponse packet) {
187 currentLightState.setPowerState(packet.getState());
190 private void handleLightPowerStatus(StateLightPowerResponse packet) {
191 currentLightState.setPowerState(packet.getState());
194 private void handleInfraredStatus(StateLightInfraredResponse packet) {
195 PercentType infrared = infraredToPercentType(packet.getInfrared());
196 currentLightState.setInfrared(infrared);
199 private void handleMultiZoneStatus(StateMultiZoneResponse packet) {
200 HSBK[] colors = currentLightState.getColors();
201 if (colors.length != packet.getCount()) {
202 colors = new HSBK[packet.getCount()];
204 for (int i = 0; i < packet.getColors().length && packet.getIndex() + i < colors.length; i++) {
205 colors[packet.getIndex() + i] = packet.getColors()[i];
208 currentLightState.setColors(colors);
211 private void handleWifiInfoStatus(StateWifiInfoResponse packet) {
212 currentLightState.setSignalStrength(packet.getSignalStrength());
215 private void handleTileEffectStatus(StateTileEffectResponse packet) {
216 currentLightState.setTileEffect(packet.getEffect());