]> git.basschouten.com Git - openhab-addons.git/blob
0b063b83eefc177d919583b9da788d187cef9ab8
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2020 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.net.InetSocketAddress;
16 import java.util.Arrays;
17 import java.util.HashMap;
18 import java.util.HashSet;
19 import java.util.List;
20 import java.util.Map;
21 import java.util.Set;
22 import java.util.concurrent.CopyOnWriteArrayList;
23 import java.util.concurrent.ScheduledExecutorService;
24 import java.util.concurrent.ScheduledFuture;
25 import java.util.concurrent.TimeUnit;
26 import java.util.concurrent.locks.ReentrantLock;
27
28 import org.eclipse.jdt.annotation.NonNullByDefault;
29 import org.eclipse.jdt.annotation.Nullable;
30 import org.openhab.binding.lifx.internal.fields.MACAddress;
31 import org.openhab.binding.lifx.internal.handler.LifxLightHandler.CurrentLightState;
32 import org.openhab.binding.lifx.internal.listener.LifxPropertiesUpdateListener;
33 import org.openhab.binding.lifx.internal.protocol.GetHostFirmwareRequest;
34 import org.openhab.binding.lifx.internal.protocol.GetVersionRequest;
35 import org.openhab.binding.lifx.internal.protocol.GetWifiFirmwareRequest;
36 import org.openhab.binding.lifx.internal.protocol.Packet;
37 import org.openhab.binding.lifx.internal.protocol.Product;
38 import org.openhab.binding.lifx.internal.protocol.StateHostFirmwareResponse;
39 import org.openhab.binding.lifx.internal.protocol.StateVersionResponse;
40 import org.openhab.binding.lifx.internal.protocol.StateWifiFirmwareResponse;
41 import org.slf4j.Logger;
42 import org.slf4j.LoggerFactory;
43
44 /**
45  * The {@link LifxLightPropertiesUpdater} updates the light properties when a light goes online. When packets get lost
46  * the requests are resent when the {@code UPDATE_INTERVAL} elapses.
47  *
48  * @author Wouter Born - Update light properties when online
49  */
50 @NonNullByDefault
51 public class LifxLightPropertiesUpdater {
52
53     private final Logger logger = LoggerFactory.getLogger(LifxLightPropertiesUpdater.class);
54
55     private static final int UPDATE_INTERVAL = 15;
56
57     private final String logId;
58     private final @Nullable InetSocketAddress ipAddress;
59     private final @Nullable MACAddress macAddress;
60     private final CurrentLightState currentLightState;
61     private final LifxLightCommunicationHandler communicationHandler;
62
63     private final List<LifxPropertiesUpdateListener> propertiesUpdateListeners = new CopyOnWriteArrayList<>();
64
65     private final List<Packet> requestPackets = Arrays.asList(new GetVersionRequest(), new GetHostFirmwareRequest(),
66             new GetWifiFirmwareRequest());
67     private final Set<Integer> receivedPacketTypes = new HashSet<>();
68
69     private final ReentrantLock lock = new ReentrantLock();
70     private final ScheduledExecutorService scheduler;
71     private @Nullable ScheduledFuture<?> updateJob;
72
73     private final Map<String, String> properties = new HashMap<>();
74     private boolean updating;
75     private boolean wasOnline;
76
77     public LifxLightPropertiesUpdater(LifxLightContext context, LifxLightCommunicationHandler communicationHandler) {
78         this.logId = context.getLogId();
79         this.macAddress = context.getConfiguration().getMACAddress();
80         this.ipAddress = context.getConfiguration().getHost();
81         this.currentLightState = context.getCurrentLightState();
82         this.scheduler = context.getScheduler();
83         this.communicationHandler = communicationHandler;
84     }
85
86     public void updateProperties() {
87         if (propertiesUpdateListeners.isEmpty()) {
88             logger.debug("{} : Not updating properties because there are no listeners", logId);
89             return;
90         }
91
92         try {
93             lock.lock();
94
95             boolean isOnline = currentLightState.isOnline();
96             if (isOnline) {
97                 if (!wasOnline) {
98                     logger.debug("{} : Updating light properties", logId);
99                     properties.clear();
100                     receivedPacketTypes.clear();
101                     updating = true;
102                     updateHostProperty();
103                     updateMACAddressProperty();
104                     sendPropertyRequestPackets();
105                 } else if (updating && !receivedAllResponsePackets()) {
106                     logger.debug("{} : Resending requests for missing response packets", logId);
107                     sendPropertyRequestPackets();
108                 }
109             }
110
111             wasOnline = isOnline;
112         } catch (Exception e) {
113             logger.error("Error occurred while polling online state of a light ({})", logId, e);
114         } finally {
115             lock.unlock();
116         }
117     }
118
119     private void updateHostProperty() {
120         InetSocketAddress host = communicationHandler.getIpAddress();
121         if (host == null) {
122             host = ipAddress;
123         }
124         if (host != null) {
125             properties.put(LifxBindingConstants.PROPERTY_HOST, host.getHostString());
126         }
127     }
128
129     private void updateMACAddressProperty() {
130         MACAddress mac = communicationHandler.getMACAddress();
131         if (mac == null) {
132             mac = macAddress;
133         }
134         if (mac != null) {
135             properties.put(LifxBindingConstants.PROPERTY_MAC_ADDRESS, mac.getAsLabel());
136         }
137     }
138
139     private void sendPropertyRequestPackets() {
140         for (Packet packet : requestPackets) {
141             if (!receivedPacketTypes.contains(packet.expectedResponses()[0])) {
142                 communicationHandler.sendPacket(packet);
143             }
144         }
145     }
146
147     public void handleResponsePacket(Packet packet) {
148         if (!updating) {
149             return;
150         }
151
152         if (packet instanceof StateVersionResponse) {
153             Product product = Product.getProductFromProductID(((StateVersionResponse) packet).getProduct());
154             long productVersion = ((StateVersionResponse) packet).getVersion();
155
156             properties.put(LifxBindingConstants.PROPERTY_PRODUCT_ID, Long.toString(product.getID()));
157             properties.put(LifxBindingConstants.PROPERTY_PRODUCT_NAME, product.getName());
158             properties.put(LifxBindingConstants.PROPERTY_PRODUCT_VERSION, Long.toString(productVersion));
159             properties.put(LifxBindingConstants.PROPERTY_VENDOR_ID, Long.toString(product.getVendor().getID()));
160             properties.put(LifxBindingConstants.PROPERTY_VENDOR_NAME, product.getVendor().getName());
161
162             receivedPacketTypes.add(packet.getPacketType());
163         } else if (packet instanceof StateHostFirmwareResponse) {
164             String hostVersion = ((StateHostFirmwareResponse) packet).getVersion().toString();
165             properties.put(LifxBindingConstants.PROPERTY_HOST_VERSION, hostVersion);
166             receivedPacketTypes.add(packet.getPacketType());
167         } else if (packet instanceof StateWifiFirmwareResponse) {
168             String wifiVersion = ((StateWifiFirmwareResponse) packet).getVersion().toString();
169             properties.put(LifxBindingConstants.PROPERTY_WIFI_VERSION, wifiVersion);
170             receivedPacketTypes.add(packet.getPacketType());
171         }
172
173         if (receivedAllResponsePackets()) {
174             updating = false;
175             propertiesUpdateListeners.forEach(listener -> listener.handlePropertiesUpdate(properties));
176             logger.debug("{} : Finished updating light properties", logId);
177         }
178     }
179
180     private boolean receivedAllResponsePackets() {
181         return requestPackets.size() == receivedPacketTypes.size();
182     }
183
184     public void addPropertiesUpdateListener(LifxPropertiesUpdateListener listener) {
185         propertiesUpdateListeners.add(listener);
186     }
187
188     public void removePropertiesUpdateListener(LifxPropertiesUpdateListener listener) {
189         propertiesUpdateListeners.remove(listener);
190     }
191
192     public void start() {
193         try {
194             lock.lock();
195             communicationHandler.addResponsePacketListener(this::handleResponsePacket);
196             ScheduledFuture<?> localUpdateJob = updateJob;
197             if (localUpdateJob == null || localUpdateJob.isCancelled()) {
198                 updateJob = scheduler.scheduleWithFixedDelay(this::updateProperties, 0, UPDATE_INTERVAL,
199                         TimeUnit.SECONDS);
200             }
201         } catch (Exception e) {
202             logger.error("Error occurred while starting properties update job for a light ({})", logId, e);
203         } finally {
204             lock.unlock();
205         }
206     }
207
208     public void stop() {
209         try {
210             lock.lock();
211             communicationHandler.removeResponsePacketListener(this::handleResponsePacket);
212             ScheduledFuture<?> localUpdateJob = updateJob;
213             if (localUpdateJob != null && !localUpdateJob.isCancelled()) {
214                 localUpdateJob.cancel(true);
215                 updateJob = null;
216             }
217         } catch (Exception e) {
218             logger.error("Error occurred while stopping properties update job for a light ({})", logId, e);
219         } finally {
220             lock.unlock();
221         }
222     }
223 }