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 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.LifxProduct.Features;
27 import org.openhab.binding.lifx.internal.dto.GetColorZonesRequest;
28 import org.openhab.binding.lifx.internal.dto.GetHevCycleRequest;
29 import org.openhab.binding.lifx.internal.dto.GetLightInfraredRequest;
30 import org.openhab.binding.lifx.internal.dto.GetRequest;
31 import org.openhab.binding.lifx.internal.dto.GetTileEffectRequest;
32 import org.openhab.binding.lifx.internal.dto.GetWifiInfoRequest;
33 import org.openhab.binding.lifx.internal.dto.HevCycleState;
34 import org.openhab.binding.lifx.internal.dto.Packet;
35 import org.openhab.binding.lifx.internal.dto.StateHevCycleResponse;
36 import org.openhab.binding.lifx.internal.dto.StateLightInfraredResponse;
37 import org.openhab.binding.lifx.internal.dto.StateLightPowerResponse;
38 import org.openhab.binding.lifx.internal.dto.StateMultiZoneResponse;
39 import org.openhab.binding.lifx.internal.dto.StatePowerResponse;
40 import org.openhab.binding.lifx.internal.dto.StateResponse;
41 import org.openhab.binding.lifx.internal.dto.StateTileEffectResponse;
42 import org.openhab.binding.lifx.internal.dto.StateWifiInfoResponse;
43 import org.openhab.binding.lifx.internal.fields.HSBK;
44 import org.openhab.binding.lifx.internal.handler.LifxLightHandler.CurrentLightState;
45 import org.openhab.core.library.types.PercentType;
46 import org.slf4j.Logger;
47 import org.slf4j.LoggerFactory;
50 * The {@link LifxLightCurrentStateUpdater} sends packets to a light in order to update the {@code currentLightState} to
51 * the actual light state.
53 * @author Wouter Born - Initial contribution
56 public class LifxLightCurrentStateUpdater {
58 private static final int STATE_POLLING_INTERVAL = 3;
60 private final Logger logger = LoggerFactory.getLogger(LifxLightCurrentStateUpdater.class);
62 private final String logId;
63 private final Features features;
64 private final CurrentLightState currentLightState;
65 private final ScheduledExecutorService scheduler;
66 private final LifxLightCommunicationHandler communicationHandler;
68 private final ReentrantLock lock = new ReentrantLock();
70 private boolean wasOnline;
71 private boolean updateSignalStrength;
73 private @Nullable ScheduledFuture<?> statePollingJob;
75 public LifxLightCurrentStateUpdater(LifxLightContext context, LifxLightCommunicationHandler communicationHandler) {
76 this.logId = context.getLogId();
77 this.features = context.getFeatures();
78 this.currentLightState = context.getCurrentLightState();
79 this.scheduler = context.getScheduler();
80 this.communicationHandler = communicationHandler;
83 public void pollLightState() {
86 if (currentLightState.isOnline()) {
87 logger.trace("{} : Polling the state of the light", logId);
88 sendLightStateRequests();
90 logger.trace("{} : The light is not online, there is no point polling it", logId);
92 wasOnline = currentLightState.isOnline();
93 } catch (Exception e) {
94 logger.error("Error occurred while polling light state", e);
100 public void setUpdateSignalStrength(boolean updateSignalStrength) {
101 this.updateSignalStrength = updateSignalStrength;
104 public void start() {
107 communicationHandler.addResponsePacketListener(this::handleResponsePacket);
108 ScheduledFuture<?> localStatePollingJob = statePollingJob;
109 if (localStatePollingJob == null || localStatePollingJob.isCancelled()) {
110 statePollingJob = scheduler.scheduleWithFixedDelay(this::pollLightState, 0, STATE_POLLING_INTERVAL,
113 } catch (Exception e) {
114 logger.error("Error occurred while starting light state updater", e);
123 communicationHandler.removeResponsePacketListener(this::handleResponsePacket);
124 ScheduledFuture<?> localStatePollingJob = statePollingJob;
125 if (localStatePollingJob != null && !localStatePollingJob.isCancelled()) {
126 localStatePollingJob.cancel(true);
127 statePollingJob = null;
129 } catch (Exception e) {
130 logger.error("Error occurred while stopping light state updater", e);
136 private void sendLightStateRequests() {
137 communicationHandler.sendPacket(new GetRequest());
139 if (features.hasFeature(HEV)) {
140 communicationHandler.sendPacket(new GetHevCycleRequest());
142 if (features.hasFeature(INFRARED)) {
143 communicationHandler.sendPacket(new GetLightInfraredRequest());
145 if (features.hasFeature(MULTIZONE)) {
146 communicationHandler.sendPacket(new GetColorZonesRequest());
148 if (features.hasFeature(TILE_EFFECT)) {
149 communicationHandler.sendPacket(new GetTileEffectRequest());
151 if (updateSignalStrength) {
152 communicationHandler.sendPacket(new GetWifiInfoRequest());
156 public void handleResponsePacket(Packet packet) {
160 if (packet instanceof StateResponse response) {
161 handleLightStatus(response);
162 } else if (packet instanceof StatePowerResponse response) {
163 handlePowerStatus(response);
164 } else if (packet instanceof StateLightPowerResponse response) {
165 handleLightPowerStatus(response);
166 } else if (packet instanceof StateHevCycleResponse response) {
167 handleHevCycleStatus(response);
168 } else if (packet instanceof StateLightInfraredResponse response) {
169 handleInfraredStatus(response);
170 } else if (packet instanceof StateMultiZoneResponse response) {
171 handleMultiZoneStatus(response);
172 } else if (packet instanceof StateTileEffectResponse response) {
173 handleTileEffectStatus(response);
174 } else if (packet instanceof StateWifiInfoResponse response) {
175 handleWifiInfoStatus(response);
178 currentLightState.setOnline();
180 if (currentLightState.isOnline() && !wasOnline) {
182 logger.trace("{} : The light just went online, immediately polling the state of the light", logId);
183 sendLightStateRequests();
190 private void handleLightStatus(StateResponse packet) {
191 currentLightState.setColor(packet.getColor(), MIN_ZONE_INDEX);
192 currentLightState.setPowerState(packet.getPower());
195 private void handlePowerStatus(StatePowerResponse packet) {
196 currentLightState.setPowerState(packet.getState());
199 private void handleLightPowerStatus(StateLightPowerResponse packet) {
200 currentLightState.setPowerState(packet.getState());
203 private void handleHevCycleStatus(StateHevCycleResponse packet) {
204 HevCycleState hevCycleState = new HevCycleState(!packet.getRemaining().isZero(), packet.getDuration());
205 currentLightState.setHevCycleState(hevCycleState);
208 private void handleInfraredStatus(StateLightInfraredResponse packet) {
209 PercentType infrared = infraredToPercentType(packet.getInfrared());
210 currentLightState.setInfrared(infrared);
213 private void handleMultiZoneStatus(StateMultiZoneResponse packet) {
214 HSBK[] colors = currentLightState.getColors();
215 if (colors.length != packet.getCount()) {
216 colors = new HSBK[packet.getCount()];
218 for (int i = 0; i < packet.getColors().length && packet.getIndex() + i < colors.length; i++) {
219 colors[packet.getIndex() + i] = packet.getColors()[i];
222 currentLightState.setColors(colors);
225 private void handleTileEffectStatus(StateTileEffectResponse packet) {
226 currentLightState.setTileEffect(packet.getEffect());
229 private void handleWifiInfoStatus(StateWifiInfoResponse packet) {
230 currentLightState.setSignalStrength(packet.getSignalStrength());