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.local;
15 import java.time.Duration;
16 import java.util.List;
18 import java.util.concurrent.ConcurrentHashMap;
19 import java.util.concurrent.ScheduledFuture;
20 import java.util.concurrent.TimeUnit;
22 import org.eclipse.jetty.client.HttpClient;
23 import org.openhab.binding.tellstick.internal.conf.TelldusLocalConfiguration;
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.local.dto.LocalDataTypeValueDTO;
29 import org.openhab.binding.tellstick.internal.local.dto.TellstickLocalDeviceDTO;
30 import org.openhab.binding.tellstick.internal.local.dto.TellstickLocalDevicesDTO;
31 import org.openhab.binding.tellstick.internal.local.dto.TellstickLocalSensorDTO;
32 import org.openhab.binding.tellstick.internal.local.dto.TellstickLocalSensorEventDTO;
33 import org.openhab.binding.tellstick.internal.local.dto.TellstickLocalSensorsDTO;
34 import org.openhab.core.cache.ExpiringCache;
35 import org.openhab.core.thing.Bridge;
36 import org.openhab.core.thing.ChannelUID;
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 TelldusLocalBridgeHandler} is the handler for Telldus Local API (Tellstick ZNET v1/v2) and connects it
50 * to the framework. All {@link TelldusDevicesHandler}s use the
51 * {@link TelldusLocalDeviceController} to execute the actual commands.
53 * @author Jan Gustafsson - Initial contribution
55 public class TelldusLocalBridgeHandler extends BaseBridgeHandler implements TelldusBridgeHandler {
57 private final Logger logger = LoggerFactory.getLogger(TelldusLocalBridgeHandler.class);
59 private TellstickLocalDevicesDTO deviceList = null;
60 private TellstickLocalSensorsDTO sensorList = null;
61 private TelldusLocalDeviceController controller = null;
62 private Set<DeviceStatusListener> deviceStatusListeners = ConcurrentHashMap.newKeySet();
63 private final HttpClient httpClient;
64 private ScheduledFuture<?> pollingJob;
66 * Use cache for refresh command to not update again when call is made within 10 seconds of previous call.
68 private final ExpiringCache<Boolean> refreshCache = new ExpiringCache<>(Duration.ofSeconds(10),
69 this::refreshDeviceList);
71 public TelldusLocalBridgeHandler(Bridge bridge, HttpClient httpClient) {
73 this.httpClient = httpClient;
77 public void initialize() {
78 TelldusLocalConfiguration configuration = getConfigAs(TelldusLocalConfiguration.class);
79 this.controller = new TelldusLocalDeviceController(configuration, httpClient);
80 pollingJob = scheduler.scheduleWithFixedDelay(this::refreshDeviceList, 11, configuration.refreshInterval,
81 TimeUnit.MILLISECONDS);
82 updateStatus(ThingStatus.UNKNOWN);
86 public void dispose() {
87 if (pollingJob != null) {
88 pollingJob.cancel(true);
90 if (this.controller != null) {
91 this.controller.dispose();
98 private boolean refreshDeviceList() {
100 updateDevices(deviceList);
101 updateSensors(sensorList);
102 updateStatus(ThingStatus.ONLINE);
104 } catch (TellstickException | InterruptedException e) {
105 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
110 private synchronized void updateDevices(TellstickLocalDevicesDTO previouslist)
111 throws TellstickException, InterruptedException {
112 TellstickLocalDevicesDTO newList = controller
113 .callRestMethod(TelldusLocalDeviceController.HTTP_LOCAL_API_DEVICES, TellstickLocalDevicesDTO.class);
114 if (newList.getDevices() != null) {
115 logger.debug("Device list {}", newList.getDevices());
116 if (previouslist == null) {
117 for (TellstickLocalDeviceDTO device : newList.getDevices()) {
118 device.setUpdated(true);
119 synchronized (deviceStatusListeners) {
120 for (DeviceStatusListener listener : deviceStatusListeners) {
121 listener.onDeviceAdded(getThing(), device);
125 this.deviceList = newList;
127 for (TellstickLocalDeviceDTO device : newList.getDevices()) {
128 int index = previouslist.getDevices().indexOf(device);
129 logger.debug("Device:{} found at {}", device, index);
131 TellstickLocalDeviceDTO orgDevice = previouslist.getDevices().get(index);
132 if (device.getState() != orgDevice.getState()) {
133 orgDevice.setState(device.getState());
134 orgDevice.setStatevalue(device.getStatevalue());
135 orgDevice.setUpdated(true);
138 logger.debug("New Device - Adding:{}", device);
139 previouslist.getDevices().add(device);
140 device.setUpdated(true);
141 synchronized (deviceStatusListeners) {
142 for (DeviceStatusListener listener : deviceStatusListeners) {
143 listener.onDeviceAdded(getThing(), device);
150 for (TellstickLocalDeviceDTO device : deviceList.getDevices()) {
151 if (device.isUpdated()) {
152 synchronized (deviceStatusListeners) {
153 for (DeviceStatusListener listener : deviceStatusListeners) {
154 listener.onDeviceStateChanged(getThing(), device,
155 new TellstickDeviceEvent(device, null, null, null, System.currentTimeMillis()));
158 device.setUpdated(false);
162 logger.debug("updateDevices, rest API returned null");
166 private synchronized void updateSensors(TellstickLocalSensorsDTO previouslist)
167 throws TellstickException, InterruptedException {
168 TellstickLocalSensorsDTO newList = controller
169 .callRestMethod(TelldusLocalDeviceController.HTTP_LOCAL_API_SENSORS, TellstickLocalSensorsDTO.class);
170 logger.debug("Updated sensors:{}", newList.getSensors());
171 if (newList.getSensors() != null) {
172 if (previouslist == null) {
173 this.sensorList = newList;
174 for (TellstickLocalSensorDTO sensor : sensorList.getSensors()) {
175 sensor.setUpdated(true);
176 synchronized (deviceStatusListeners) {
177 for (DeviceStatusListener listener : deviceStatusListeners) {
178 listener.onDeviceAdded(getThing(), sensor);
183 for (TellstickLocalSensorDTO sensor : previouslist.getSensors()) {
184 sensor.setUpdated(false);
187 for (TellstickLocalSensorDTO sensor : newList.getSensors()) {
188 int index = this.sensorList.getSensors().indexOf(sensor);
190 TellstickLocalSensorDTO orgSensor = this.sensorList.getSensors().get(index);
191 orgSensor.setData(sensor.getData());
192 orgSensor.setUpdated(true);
193 sensor.setUpdated(true);
195 this.sensorList.getSensors().add(sensor);
196 sensor.setUpdated(true);
197 synchronized (deviceStatusListeners) {
198 for (DeviceStatusListener listener : deviceStatusListeners) {
199 listener.onDeviceAdded(getThing(), sensor);
205 for (TellstickLocalSensorDTO sensor : sensorList.getSensors()) {
206 if (sensor.getData() != null && sensor.isUpdated()) {
207 synchronized (deviceStatusListeners) {
208 for (DeviceStatusListener listener : deviceStatusListeners) {
209 for (LocalDataTypeValueDTO type : sensor.getData()) {
210 listener.onDeviceStateChanged(getThing(), sensor,
211 new TellstickLocalSensorEventDTO(sensor.getId(), type.getValue(), type,
212 sensor.getProtocol(), sensor.getModel(), System.currentTimeMillis()));
216 sensor.setUpdated(false);
223 public void handleCommand(ChannelUID channelUID, Command command) {
224 if (command instanceof RefreshType) {
225 refreshCache.getValue();
230 public boolean registerDeviceStatusListener(DeviceStatusListener deviceStatusListener) {
231 if (deviceStatusListener == null) {
232 throw new IllegalArgumentException("It's not allowed to pass a null deviceStatusListener.");
234 return deviceStatusListeners.add(deviceStatusListener);
238 public boolean unregisterDeviceStatusListener(DeviceStatusListener deviceStatusListener) {
239 return deviceStatusListeners.remove(deviceStatusListener);
242 private Device getDevice(String id, List<TellstickLocalDeviceDTO> devices) {
243 for (Device device : devices) {
244 if (device.getId() == Integer.valueOf(id)) {
251 private Device getSensor(String id, List<TellstickLocalSensorDTO> sensors) {
252 for (Device sensor : sensors) {
253 if (sensor.getId() == Integer.valueOf(id)) {
261 public Device getDevice(String serialNumber) {
262 return getDevice(serialNumber, getDevices());
265 private List<TellstickLocalDeviceDTO> getDevices() {
266 if (deviceList == null) {
269 return deviceList.getDevices();
273 public Device getSensor(String deviceUUId) {
274 Device result = null;
275 if (sensorList != null) {
276 result = getSensor(deviceUUId, sensorList.getSensors());
282 public void rescanTelldusDevices() {
283 this.deviceList = null;
284 this.sensorList = null;
289 public TelldusDeviceController getController() {