]> git.basschouten.com Git - openhab-addons.git/blob
1ab4b9dc7c8b189fdb409b77aa3fcadd1f5cd81b
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2021 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.local;
14
15 import java.math.BigDecimal;
16 import java.math.RoundingMode;
17 import java.util.concurrent.ExecutionException;
18 import java.util.concurrent.TimeoutException;
19
20 import org.eclipse.jetty.client.HttpClient;
21 import org.eclipse.jetty.client.api.ContentResponse;
22 import org.eclipse.jetty.client.api.Request;
23 import org.eclipse.jetty.http.HttpMethod;
24 import org.openhab.binding.tellstick.internal.TelldusBindingException;
25 import org.openhab.binding.tellstick.internal.conf.TelldusLocalConfiguration;
26 import org.openhab.binding.tellstick.internal.handler.TelldusDeviceController;
27 import org.openhab.binding.tellstick.internal.local.dto.TelldusLocalResponseDTO;
28 import org.openhab.binding.tellstick.internal.local.dto.TellstickLocalDeviceDTO;
29 import org.openhab.core.library.types.IncreaseDecreaseType;
30 import org.openhab.core.library.types.OnOffType;
31 import org.openhab.core.library.types.PercentType;
32 import org.openhab.core.types.Command;
33 import org.openhab.core.types.State;
34 import org.slf4j.Logger;
35 import org.slf4j.LoggerFactory;
36 import org.tellstick.JNA;
37 import org.tellstick.device.TellstickDevice;
38 import org.tellstick.device.TellstickDeviceEvent;
39 import org.tellstick.device.TellstickException;
40 import org.tellstick.device.TellstickSensorEvent;
41 import org.tellstick.device.iface.Device;
42 import org.tellstick.device.iface.DeviceChangeListener;
43 import org.tellstick.device.iface.SensorListener;
44 import org.tellstick.device.iface.SwitchableDevice;
45
46 import com.google.gson.Gson;
47 import com.google.gson.JsonSyntaxException;
48
49 /**
50  * {@link TelldusLocalDeviceController} handles the communication with Telldus Local API (Tellstick ZNET v1/v2)
51  * This controller uses JSON based Rest API to communicate with Telldus Local API.
52  *
53  * @author Jan Gustafsson - Initial contribution
54  */
55 public class TelldusLocalDeviceController implements DeviceChangeListener, SensorListener, TelldusDeviceController {
56     private final Logger logger = LoggerFactory.getLogger(TelldusLocalDeviceController.class);
57     private long lastSend = 0;
58     public static final long DEFAULT_INTERVAL_BETWEEN_SEND_SEC = 250;
59     static final int REQUEST_TIMEOUT_MS = 5000;
60     private final HttpClient httpClient;
61     private final Gson gson = new Gson();
62     private String localApiUrl;
63     private String authorizationHeader = "Bearer ";
64     static final String HTTP_LOCAL_API = "api/";
65     static final String HTTP_LOCAL_API_DEVICES = HTTP_LOCAL_API + "devices/list?supportedMethods=19&includeIgnored=0";
66     static final String HTTP_LOCAL_API_SENSORS = HTTP_LOCAL_API
67             + "sensors/list?includeValues=1&includeScale=1&includeUnit=1&includeIgnored=0";
68     static final String HTTP_LOCAL_API_SENSOR_INFO = HTTP_LOCAL_API + "sensor/info";
69     static final String HTTP_LOCAL_API_DEVICE_DIM = HTTP_LOCAL_API + "device/dim?id=%d&level=%d";
70     static final String HTTP_LOCAL_API_DEVICE_TURNOFF = HTTP_LOCAL_API + "device/turnOff?id=%d";
71     static final String HTTP_LOCAL_DEVICE_TURNON = HTTP_LOCAL_API + "device/turnOn?id=%d";
72     private static final int MAX_RETRIES = 3;
73
74     public TelldusLocalDeviceController(TelldusLocalConfiguration configuration, HttpClient httpClient) {
75         this.httpClient = httpClient;
76         localApiUrl = "http://" + configuration.ipAddress + "/";
77         authorizationHeader = authorizationHeader + configuration.accessToken;
78     }
79
80     @Override
81     public void dispose() {
82     }
83
84     @Override
85     public void handleSendEvent(Device device, int resendCount, boolean isdimmer, Command command)
86             throws TellstickException {
87         logger.debug("Send {} to {}", command, device);
88         try {
89             if (device instanceof TellstickLocalDeviceDTO) {
90                 if (command == OnOffType.ON) {
91                     turnOn(device);
92                 } else if (command == OnOffType.OFF) {
93                     turnOff(device);
94                 } else if (command instanceof PercentType) {
95                     dim(device, (PercentType) command);
96                 } else if (command instanceof IncreaseDecreaseType) {
97                     increaseDecrease(device, ((IncreaseDecreaseType) command));
98                 }
99             } else if (device instanceof SwitchableDevice) {
100                 if (command == OnOffType.ON) {
101                     if (isdimmer) {
102                         logger.trace("Turn off first in case it is allready on");
103                         turnOff(device);
104                     }
105                     turnOn(device);
106                 } else if (command == OnOffType.OFF) {
107                     turnOff(device);
108                 }
109             } else {
110                 logger.warn("Cannot send to {}", device);
111             }
112         } catch (InterruptedException e) {
113             logger.debug("OH is shut-down.");
114         }
115     }
116
117     private void increaseDecrease(Device dev, IncreaseDecreaseType increaseDecreaseType)
118             throws TellstickException, InterruptedException {
119         String strValue = ((TellstickDevice) dev).getData();
120         double value = 0;
121         if (strValue != null) {
122             value = Double.valueOf(strValue);
123         }
124         int percent = (int) Math.round((value / 255) * 100);
125         if (IncreaseDecreaseType.INCREASE == increaseDecreaseType) {
126             percent = Math.min(percent + 10, 100);
127         } else if (IncreaseDecreaseType.DECREASE == increaseDecreaseType) {
128             percent = Math.max(percent - 10, 0);
129         }
130         dim(dev, new PercentType(percent));
131     }
132
133     private void dim(Device dev, PercentType command) throws TellstickException, InterruptedException {
134         double value = command.doubleValue();
135
136         // 0 means OFF and 100 means ON
137         if (value == 0 && dev instanceof TellstickLocalDeviceDTO) {
138             turnOff(dev);
139         } else if (value == 100 && dev instanceof TellstickLocalDeviceDTO) {
140             turnOn(dev);
141         } else if (dev instanceof TellstickLocalDeviceDTO
142                 && (((TellstickLocalDeviceDTO) dev).getMethods() & JNA.CLibrary.TELLSTICK_DIM) > 0) {
143             long tdVal = Math.round((value / 100) * 255);
144             TelldusLocalResponseDTO response = callRestMethod(
145                     String.format(HTTP_LOCAL_API_DEVICE_DIM, dev.getId(), tdVal), TelldusLocalResponseDTO.class);
146             handleResponse((TellstickLocalDeviceDTO) dev, response);
147         } else {
148             throw new TelldusBindingException("Cannot send DIM to " + dev);
149         }
150     }
151
152     private void turnOff(Device dev) throws TellstickException, InterruptedException {
153         if (dev instanceof TellstickLocalDeviceDTO) {
154             TelldusLocalResponseDTO response = callRestMethod(String.format(HTTP_LOCAL_API_DEVICE_TURNOFF, dev.getId()),
155                     TelldusLocalResponseDTO.class);
156             handleResponse((TellstickLocalDeviceDTO) dev, response);
157         } else {
158             throw new TelldusBindingException("Cannot send OFF to " + dev);
159         }
160     }
161
162     private void handleResponse(TellstickLocalDeviceDTO device, TelldusLocalResponseDTO response)
163             throws TellstickException {
164         if (response == null || (response.getStatus() == null && response.getError() == null)) {
165             throw new TelldusBindingException("No response " + response);
166         } else if (response.getError() != null) {
167             device.setUpdated(true);
168             throw new TelldusBindingException("Error " + response.getError());
169         } else if (!response.getStatus().trim().equals("success")) {
170             throw new TelldusBindingException("Response " + response.getStatus());
171         }
172     }
173
174     private void turnOn(Device dev) throws TellstickException, InterruptedException {
175         if (dev instanceof TellstickLocalDeviceDTO) {
176             TelldusLocalResponseDTO response = callRestMethod(String.format(HTTP_LOCAL_DEVICE_TURNON, dev.getId()),
177                     TelldusLocalResponseDTO.class);
178             handleResponse((TellstickLocalDeviceDTO) dev, response);
179         } else {
180             throw new TelldusBindingException("Cannot send ON to " + dev);
181         }
182     }
183
184     @Override
185     public State calcState(Device dev) {
186         TellstickLocalDeviceDTO device = (TellstickLocalDeviceDTO) dev;
187         State st = null;
188
189         switch (device.getState()) {
190             case JNA.CLibrary.TELLSTICK_TURNON:
191                 st = OnOffType.ON;
192                 break;
193             case JNA.CLibrary.TELLSTICK_TURNOFF:
194                 st = OnOffType.OFF;
195                 break;
196             case JNA.CLibrary.TELLSTICK_DIM:
197                 BigDecimal dimValue = new BigDecimal(device.getStatevalue());
198                 if (dimValue.intValue() == 0) {
199                     st = OnOffType.OFF;
200                 } else if (dimValue.intValue() >= 255) {
201                     st = OnOffType.ON;
202                 } else {
203                     st = OnOffType.ON;
204                 }
205                 break;
206             default:
207                 logger.warn("Could not handle {} for {}", device.getState(), device);
208         }
209
210         return st;
211     }
212
213     @Override
214     public BigDecimal calcDimValue(Device device) {
215         BigDecimal dimValue = BigDecimal.ZERO;
216         switch (((TellstickLocalDeviceDTO) device).getState()) {
217             case JNA.CLibrary.TELLSTICK_TURNON:
218                 dimValue = new BigDecimal(100);
219                 break;
220             case JNA.CLibrary.TELLSTICK_TURNOFF:
221                 break;
222             case JNA.CLibrary.TELLSTICK_DIM:
223                 dimValue = new BigDecimal(((TellstickLocalDeviceDTO) device).getStatevalue());
224                 dimValue = dimValue.multiply(new BigDecimal(100));
225                 dimValue = dimValue.divide(new BigDecimal(255), 0, RoundingMode.HALF_UP);
226                 break;
227             default:
228                 logger.warn("Could not handle {} for {}", (((TellstickLocalDeviceDTO) device).getState()), device);
229         }
230         return dimValue;
231     }
232
233     public long getLastSend() {
234         return lastSend;
235     }
236
237     public void setLastSend(long currentTimeMillis) {
238         lastSend = currentTimeMillis;
239     }
240
241     @Override
242     public void onRequest(TellstickSensorEvent newDevices) {
243         setLastSend(newDevices.getTimestamp());
244     }
245
246     @Override
247     public void onRequest(TellstickDeviceEvent newDevices) {
248         setLastSend(newDevices.getTimestamp());
249     }
250
251     <T> T callRestMethod(String uri, Class<T> response) throws TelldusLocalException, InterruptedException {
252         T resultObj = null;
253         try {
254             for (int i = 0; i < MAX_RETRIES; i++) {
255                 try {
256                     resultObj = innerCallRest(localApiUrl + uri, response);
257                     break;
258                 } catch (TimeoutException e) {
259                     logger.warn("TimeoutException error in get");
260                 }
261             }
262         } catch (JsonSyntaxException e) {
263             throw new TelldusLocalException(e);
264         } catch (ExecutionException e) {
265             throw new TelldusLocalException(e);
266         }
267         return resultObj;
268     }
269
270     private <T> T innerCallRest(String uri, Class<T> json)
271             throws ExecutionException, InterruptedException, TimeoutException, JsonSyntaxException {
272         logger.trace("HTTP GET: {}", uri);
273
274         Request request = httpClient.newRequest(uri).method(HttpMethod.GET);
275         request.header("Authorization", authorizationHeader);
276
277         ContentResponse response = request.send();
278         String content = response.getContentAsString();
279         logger.trace("API response: {}", content);
280
281         return gson.fromJson(content, json);
282     }
283 }