]> git.basschouten.com Git - openhab-addons.git/blob
31f806d3cff4245d1bfc9184147e55cc6717631a
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2023 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.tellstick.internal.live;
14
15 import java.time.Duration;
16 import java.time.Instant;
17 import java.util.List;
18 import java.util.Set;
19 import java.util.concurrent.ConcurrentHashMap;
20 import java.util.concurrent.ScheduledFuture;
21 import java.util.concurrent.TimeUnit;
22
23 import org.openhab.binding.tellstick.internal.conf.TelldusLiveConfiguration;
24 import org.openhab.binding.tellstick.internal.handler.DeviceStatusListener;
25 import org.openhab.binding.tellstick.internal.handler.TelldusBridgeHandler;
26 import org.openhab.binding.tellstick.internal.handler.TelldusDeviceController;
27 import org.openhab.binding.tellstick.internal.handler.TelldusDevicesHandler;
28 import org.openhab.binding.tellstick.internal.live.xml.DataTypeValue;
29 import org.openhab.binding.tellstick.internal.live.xml.TellstickNetDevice;
30 import org.openhab.binding.tellstick.internal.live.xml.TellstickNetDevices;
31 import org.openhab.binding.tellstick.internal.live.xml.TellstickNetSensor;
32 import org.openhab.binding.tellstick.internal.live.xml.TellstickNetSensorEvent;
33 import org.openhab.binding.tellstick.internal.live.xml.TellstickNetSensors;
34 import org.openhab.core.thing.Bridge;
35 import org.openhab.core.thing.ChannelUID;
36 import org.openhab.core.thing.Thing;
37 import org.openhab.core.thing.ThingStatus;
38 import org.openhab.core.thing.ThingStatusDetail;
39 import org.openhab.core.thing.binding.BaseBridgeHandler;
40 import org.openhab.core.types.Command;
41 import org.openhab.core.types.RefreshType;
42 import org.slf4j.Logger;
43 import org.slf4j.LoggerFactory;
44 import org.tellstick.device.TellstickDeviceEvent;
45 import org.tellstick.device.TellstickException;
46 import org.tellstick.device.iface.Device;
47
48 /**
49  * {@link TelldusLiveBridgeHandler} is the handler for Telldus Live service (Tellstick.NET and ZNET) and connects it
50  * to the framework. All {@link TelldusDevicesHandler}s use the
51  * {@link TelldusLiveDeviceController} to execute the actual commands.
52  *
53  * @author Jarle Hjortland - Initial contribution
54  */
55 public class TelldusLiveBridgeHandler extends BaseBridgeHandler implements TelldusBridgeHandler {
56
57     private static final int REFRESH_DELAY = 10;
58
59     private final Logger logger = LoggerFactory.getLogger(TelldusLiveBridgeHandler.class);
60
61     private TellstickNetDevices deviceList = null;
62     private TellstickNetSensors sensorList = null;
63     private TelldusLiveDeviceController controller = new TelldusLiveDeviceController();
64     private Set<DeviceStatusListener> deviceStatusListeners = ConcurrentHashMap.newKeySet();
65
66     private int nbRefresh;
67     private long sumRefreshDuration;
68     private long minRefreshDuration = 1_000_000;
69     private long maxRefreshDuration;
70
71     public TelldusLiveBridgeHandler(Bridge bridge) {
72         super(bridge);
73     }
74
75     private ScheduledFuture<?> pollingJob;
76     private ScheduledFuture<?> immediateRefreshJob;
77
78     @Override
79     public void dispose() {
80         logger.debug("Live Handler disposed.");
81         if (pollingJob != null) {
82             pollingJob.cancel(true);
83         }
84         if (this.controller != null) {
85             this.controller.dispose();
86         }
87         deviceList = null;
88         sensorList = null;
89         super.dispose();
90     }
91
92     @Override
93     public void initialize() {
94         logger.debug("Initializing TelldusLive bridge handler.");
95         TelldusLiveConfiguration configuration = getConfigAs(TelldusLiveConfiguration.class);
96         this.controller = new TelldusLiveDeviceController();
97         this.controller.connectHttpClient(configuration.publicKey, configuration.privateKey, configuration.token,
98                 configuration.tokenSecret);
99         updateStatus(ThingStatus.UNKNOWN);
100         startAutomaticRefresh(configuration.refreshInterval);
101     }
102
103     private synchronized void startAutomaticRefresh(long refreshInterval) {
104         if (pollingJob != null && !pollingJob.isCancelled()) {
105             pollingJob.cancel(true);
106         }
107         pollingJob = scheduler.scheduleWithFixedDelay(this::refreshDeviceList, 0, refreshInterval,
108                 TimeUnit.MILLISECONDS);
109     }
110
111     private void scheduleImmediateRefresh() {
112         // We schedule in 10 sec, to avoid multiple updates
113         logger.debug("Current remaining delay {}", pollingJob.getDelay(TimeUnit.SECONDS));
114         if (pollingJob.getDelay(TimeUnit.SECONDS) > REFRESH_DELAY) {
115             if (immediateRefreshJob == null || immediateRefreshJob.isDone()) {
116                 immediateRefreshJob = scheduler.schedule(this::refreshDeviceList, REFRESH_DELAY, TimeUnit.SECONDS);
117             }
118         }
119     }
120
121     synchronized void refreshDeviceList() {
122         Instant start = Instant.now();
123         try {
124             updateDevices(deviceList);
125             updateSensors(sensorList);
126             updateStatus(ThingStatus.ONLINE);
127         } catch (Exception e) {
128             logger.warn("Failed to update", e);
129             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
130         }
131         monitorAdditionalRefresh(start, Instant.now());
132     }
133
134     private void monitorAdditionalRefresh(Instant start, Instant end) {
135         if (!logger.isDebugEnabled()) {
136             return;
137         }
138         long duration = Duration.between(start, end).toMillis();
139         sumRefreshDuration += duration;
140         nbRefresh++;
141         if (duration < minRefreshDuration) {
142             minRefreshDuration = duration;
143         }
144         if (duration > maxRefreshDuration) {
145             maxRefreshDuration = duration;
146         }
147         logger.debug(
148                 "{} refresh avg = {} ms min = {} max = {} (request avg = {} ms min = {} max = {}) ({} timeouts {} errors)",
149                 nbRefresh, nbRefresh == 0 ? 0 : sumRefreshDuration / nbRefresh, minRefreshDuration, maxRefreshDuration,
150                 controller.getAverageRequestDuration(), controller.getMinRequestDuration(),
151                 controller.getMaxRequestDuration(), controller.getNbTimeouts(), controller.getNbErrors());
152     }
153
154     private synchronized void updateDevices(TellstickNetDevices previouslist) throws TellstickException {
155         TellstickNetDevices newList = controller.callRestMethod(TelldusLiveDeviceController.HTTP_TELLDUS_DEVICES,
156                 TellstickNetDevices.class);
157         if (newList.getDevices() != null) {
158             logger.debug("Device list {}", newList.getDevices());
159             if (previouslist == null) {
160                 logger.debug("updateDevices, Creating devices.");
161                 for (TellstickNetDevice device : newList.getDevices()) {
162                     device.setUpdated(true);
163                     for (DeviceStatusListener listener : deviceStatusListeners) {
164                         listener.onDeviceAdded(getThing(), device);
165                     }
166                 }
167                 this.deviceList = newList;
168             } else {
169                 logger.debug("updateDevices, Updating devices.");
170                 for (TellstickNetDevice device : newList.getDevices()) {
171                     int index = previouslist.getDevices().indexOf(device);
172                     logger.debug("Device:{} found at {}", device, index);
173                     if (index >= 0) {
174                         TellstickNetDevice orgDevice = previouslist.getDevices().get(index);
175                         if (device.getState() != orgDevice.getState()) {
176                             orgDevice.setState(device.getState());
177                             orgDevice.setStatevalue(device.getStatevalue());
178                             orgDevice.setUpdated(true);
179                         }
180                     } else {
181                         logger.debug("New Device - Adding:{}", device);
182                         previouslist.getDevices().add(device);
183                         device.setUpdated(true);
184                         for (DeviceStatusListener listener : deviceStatusListeners) {
185                             listener.onDeviceAdded(getThing(), device);
186                         }
187                     }
188                 }
189             }
190
191             for (TellstickNetDevice device : deviceList.getDevices()) {
192                 if (device.isUpdated()) {
193                     logger.debug("Updated device:{}", device);
194                     for (DeviceStatusListener listener : deviceStatusListeners) {
195                         listener.onDeviceStateChanged(getThing(), device,
196                                 new TellstickDeviceEvent(device, null, null, null, System.currentTimeMillis()));
197                     }
198                     device.setUpdated(false);
199                 }
200             }
201         } else {
202             logger.debug("updateDevices, rest API returned null");
203         }
204     }
205
206     private synchronized void updateSensors(TellstickNetSensors previouslist) throws TellstickException {
207         TellstickNetSensors newList = controller.callRestMethod(TelldusLiveDeviceController.HTTP_TELLDUS_SENSORS,
208                 TellstickNetSensors.class);
209         logger.debug("Updated sensors:{}", newList.getSensors());
210         if (newList.getSensors() != null) {
211             if (previouslist == null) {
212                 logger.debug("First update of sensors");
213                 this.sensorList = newList;
214                 for (TellstickNetSensor sensor : sensorList.getSensors()) {
215                     sensor.setUpdated(true);
216                     for (DeviceStatusListener listener : deviceStatusListeners) {
217                         listener.onDeviceAdded(getThing(), sensor);
218                     }
219                 }
220             } else {
221                 logger.debug("Update sensors, reset updated flag");
222                 for (TellstickNetSensor sensor : previouslist.getSensors()) {
223                     sensor.setUpdated(false);
224                 }
225                 logger.debug("Update sensors, reset updated flag1");
226
227                 for (TellstickNetSensor sensor : newList.getSensors()) {
228                     int index = this.sensorList.getSensors().indexOf(sensor);
229                     if (index >= 0) {
230                         TellstickNetSensor orgSensor = this.sensorList.getSensors().get(index);
231                         logger.debug("Update sensor {}, prev update {}, new update {}", sensor.getId(),
232                                 orgSensor.getLastUpdated(), sensor.getLastUpdated());
233                         if (sensor.getLastUpdated() > orgSensor.getLastUpdated()) {
234                             logger.debug("Update for sensor:{}", sensor);
235                             orgSensor.setData(sensor.getData());
236                             orgSensor.setLastUpdated(sensor.getLastUpdated());
237                             orgSensor.setUpdated(true);
238                             sensor.setUpdated(true);
239                         }
240                     } else {
241                         logger.debug("Adding sensor {}, new update {}", sensor.getId(), sensor.getLastUpdated());
242                         this.sensorList.getSensors().add(sensor);
243                         sensor.setUpdated(true);
244                         for (DeviceStatusListener listener : deviceStatusListeners) {
245                             listener.onDeviceAdded(getThing(), sensor);
246                         }
247                     }
248                 }
249             }
250             for (TellstickNetSensor sensor : sensorList.getSensors()) {
251                 if (sensor.getData() != null && sensor.isUpdated()) {
252                     for (DeviceStatusListener listener : deviceStatusListeners) {
253                         for (DataTypeValue type : sensor.getData()) {
254                             listener.onDeviceStateChanged(getThing(), sensor,
255                                     new TellstickNetSensorEvent(sensor.getId(), type.getValue(), type,
256                                             sensor.getProtocol(), sensor.getModel(), System.currentTimeMillis()));
257                         }
258                     }
259                     sensor.setUpdated(false);
260                 }
261             }
262         }
263     }
264
265     @Override
266     public void handleCommand(ChannelUID channelUID, Command command) {
267         if (command instanceof RefreshType) {
268             scheduleImmediateRefresh();
269         }
270     }
271
272     @Override
273     public void handleRemoval() {
274         super.handleRemoval();
275     }
276
277     @Override
278     public void thingUpdated(Thing thing) {
279         super.thingUpdated(thing);
280     }
281
282     @Override
283     public boolean registerDeviceStatusListener(DeviceStatusListener deviceStatusListener) {
284         if (deviceStatusListener == null) {
285             throw new IllegalArgumentException("It's not allowed to pass a null deviceStatusListener.");
286         }
287         return deviceStatusListeners.add(deviceStatusListener);
288     }
289
290     @Override
291     public boolean unregisterDeviceStatusListener(DeviceStatusListener deviceStatusListener) {
292         return deviceStatusListeners.remove(deviceStatusListener);
293     }
294
295     private Device getDevice(String id, List<TellstickNetDevice> devices) {
296         for (Device device : devices) {
297             if (device.getId() == Integer.valueOf(id)) {
298                 return device;
299             }
300         }
301         return null;
302     }
303
304     private Device getSensor(String id, List<TellstickNetSensor> sensors) {
305         for (Device sensor : sensors) {
306             if (sensor.getId() == Integer.valueOf(id)) {
307                 return sensor;
308             }
309         }
310         return null;
311     }
312
313     @Override
314     public Device getDevice(String serialNumber) {
315         return getDevice(serialNumber, getDevices());
316     }
317
318     private List<TellstickNetDevice> getDevices() {
319         if (deviceList == null) {
320             refreshDeviceList();
321         }
322         return deviceList.getDevices();
323     }
324
325     @Override
326     public Device getSensor(String deviceUUId) {
327         Device result = null;
328         if (sensorList != null) {
329             result = getSensor(deviceUUId, sensorList.getSensors());
330         }
331         return result;
332     }
333
334     @Override
335     public void rescanTelldusDevices() {
336         this.deviceList = null;
337         this.sensorList = null;
338         refreshDeviceList();
339     }
340
341     @Override
342     public TelldusDeviceController getController() {
343         return controller;
344     }
345 }