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.tellstick.internal.live;
15 import java.time.Duration;
16 import java.time.Instant;
17 import java.util.List;
19 import java.util.concurrent.ConcurrentHashMap;
20 import java.util.concurrent.ScheduledFuture;
21 import java.util.concurrent.TimeUnit;
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;
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.
53 * @author Jarle Hjortland - Initial contribution
55 public class TelldusLiveBridgeHandler extends BaseBridgeHandler implements TelldusBridgeHandler {
57 private static final int REFRESH_DELAY = 10;
59 private final Logger logger = LoggerFactory.getLogger(TelldusLiveBridgeHandler.class);
61 private TellstickNetDevices deviceList = null;
62 private TellstickNetSensors sensorList = null;
63 private TelldusLiveDeviceController controller = new TelldusLiveDeviceController();
64 private Set<DeviceStatusListener> deviceStatusListeners = ConcurrentHashMap.newKeySet();
66 private int nbRefresh;
67 private long sumRefreshDuration;
68 private long minRefreshDuration = 1_000_000;
69 private long maxRefreshDuration;
71 public TelldusLiveBridgeHandler(Bridge bridge) {
75 private ScheduledFuture<?> pollingJob;
76 private ScheduledFuture<?> immediateRefreshJob;
79 public void dispose() {
80 logger.debug("Live Handler disposed.");
81 if (pollingJob != null) {
82 pollingJob.cancel(true);
84 if (this.controller != null) {
85 this.controller.dispose();
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);
103 private synchronized void startAutomaticRefresh(long refreshInterval) {
104 if (pollingJob != null && !pollingJob.isCancelled()) {
105 pollingJob.cancel(true);
107 pollingJob = scheduler.scheduleWithFixedDelay(this::refreshDeviceList, 0, refreshInterval,
108 TimeUnit.MILLISECONDS);
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);
121 synchronized void refreshDeviceList() {
122 Instant start = Instant.now();
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());
131 monitorAdditionalRefresh(start, Instant.now());
134 private void monitorAdditionalRefresh(Instant start, Instant end) {
135 if (!logger.isDebugEnabled()) {
138 long duration = Duration.between(start, end).toMillis();
139 sumRefreshDuration += duration;
141 if (duration < minRefreshDuration) {
142 minRefreshDuration = duration;
144 if (duration > maxRefreshDuration) {
145 maxRefreshDuration = duration;
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());
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);
167 this.deviceList = newList;
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);
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);
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);
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()));
198 device.setUpdated(false);
202 logger.debug("updateDevices, rest API returned null");
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);
221 logger.debug("Update sensors, reset updated flag");
222 for (TellstickNetSensor sensor : previouslist.getSensors()) {
223 sensor.setUpdated(false);
225 logger.debug("Update sensors, reset updated flag1");
227 for (TellstickNetSensor sensor : newList.getSensors()) {
228 int index = this.sensorList.getSensors().indexOf(sensor);
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);
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);
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()));
259 sensor.setUpdated(false);
266 public void handleCommand(ChannelUID channelUID, Command command) {
267 if (command instanceof RefreshType) {
268 scheduleImmediateRefresh();
273 public void handleRemoval() {
274 super.handleRemoval();
278 public void thingUpdated(Thing thing) {
279 super.thingUpdated(thing);
283 public boolean registerDeviceStatusListener(DeviceStatusListener deviceStatusListener) {
284 if (deviceStatusListener == null) {
285 throw new IllegalArgumentException("It's not allowed to pass a null deviceStatusListener.");
287 return deviceStatusListeners.add(deviceStatusListener);
291 public boolean unregisterDeviceStatusListener(DeviceStatusListener deviceStatusListener) {
292 return deviceStatusListeners.remove(deviceStatusListener);
295 private Device getDevice(String id, List<TellstickNetDevice> devices) {
296 for (Device device : devices) {
297 if (device.getId() == Integer.valueOf(id)) {
304 private Device getSensor(String id, List<TellstickNetSensor> sensors) {
305 for (Device sensor : sensors) {
306 if (sensor.getId() == Integer.valueOf(id)) {
314 public Device getDevice(String serialNumber) {
315 return getDevice(serialNumber, getDevices());
318 private List<TellstickNetDevice> getDevices() {
319 if (deviceList == null) {
322 return deviceList.getDevices();
326 public Device getSensor(String deviceUUId) {
327 Device result = null;
328 if (sensorList != null) {
329 result = getSensor(deviceUUId, sensorList.getSensors());
335 public void rescanTelldusDevices() {
336 this.deviceList = null;
337 this.sensorList = null;
342 public TelldusDeviceController getController() {