2 * Copyright (c) 2010-2020 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.nuki.internal.dataexchange;
15 import java.io.InterruptedIOException;
16 import java.math.BigDecimal;
17 import java.net.SocketException;
18 import java.net.URLEncoder;
19 import java.time.Instant;
20 import java.util.concurrent.ExecutionException;
21 import java.util.concurrent.TimeoutException;
23 import org.eclipse.jetty.client.HttpClient;
24 import org.eclipse.jetty.client.HttpResponseException;
25 import org.eclipse.jetty.client.api.ContentResponse;
26 import org.eclipse.jetty.http.HttpStatus;
27 import org.openhab.binding.nuki.internal.NukiBindingConstants;
28 import org.openhab.binding.nuki.internal.dto.BridgeApiCallbackAddDto;
29 import org.openhab.binding.nuki.internal.dto.BridgeApiCallbackListDto;
30 import org.openhab.binding.nuki.internal.dto.BridgeApiCallbackRemoveDto;
31 import org.openhab.binding.nuki.internal.dto.BridgeApiInfoDto;
32 import org.openhab.binding.nuki.internal.dto.BridgeApiLockActionDto;
33 import org.openhab.binding.nuki.internal.dto.BridgeApiLockStateDto;
34 import org.openhab.core.config.core.Configuration;
35 import org.slf4j.Logger;
36 import org.slf4j.LoggerFactory;
38 import com.google.gson.Gson;
41 * The {@link NukiHttpClient} class is responsible for getting data from the Nuki Bridge.
43 * @author Markus Katter - Initial contribution
45 public class NukiHttpClient {
47 private final Logger logger = LoggerFactory.getLogger(NukiHttpClient.class);
48 private static final long CACHE_PERIOD = 5;
50 private HttpClient httpClient;
51 private Configuration configuration;
53 private BridgeLockStateResponse bridgeLockStateResponseCache;
55 public NukiHttpClient(HttpClient httpClient, Configuration configuration) {
56 logger.debug("Instantiating NukiHttpClient({})", configuration);
57 this.configuration = configuration;
58 this.httpClient = httpClient;
62 private String prepareUri(String uriTemplate, String... additionalArguments) {
63 String configIp = (String) configuration.get(NukiBindingConstants.CONFIG_IP);
64 BigDecimal configPort = (BigDecimal) configuration.get(NukiBindingConstants.CONFIG_PORT);
65 String configApiToken = (String) configuration.get(NukiBindingConstants.CONFIG_API_TOKEN);
66 String[] parameters = new String[additionalArguments.length + 3];
67 parameters[0] = configIp;
68 parameters[1] = configPort.toString();
69 parameters[2] = configApiToken;
70 System.arraycopy(additionalArguments, 0, parameters, 3, additionalArguments.length);
71 String uri = String.format(uriTemplate, parameters);
72 logger.trace("prepareUri(...):URI[{}]", uri);
76 private synchronized ContentResponse executeRequest(String uri)
77 throws InterruptedException, ExecutionException, TimeoutException {
78 logger.debug("executeRequest({})", uri);
81 } catch (InterruptedException e) {
83 ContentResponse contentResponse = httpClient.GET(uri);
84 logger.debug("contentResponseAsString[{}]", contentResponse.getContentAsString());
85 return contentResponse;
88 private NukiBaseResponse handleException(Exception e) {
89 if (e instanceof ExecutionException) {
90 if (e.getCause() instanceof HttpResponseException) {
91 HttpResponseException cause = (HttpResponseException) e.getCause();
92 int status = cause.getResponse().getStatus();
93 String reason = cause.getResponse().getReason();
94 logger.debug("HTTP Response Exception! Status[{}] - Reason[{}]! Check your API Token!", status, reason);
95 return new NukiBaseResponse(status, reason);
96 } else if (e.getCause() instanceof InterruptedIOException) {
98 "InterruptedIOException! Exception[{}]! Check IP/Port configuration and if Nuki Bridge is powered on!",
100 return new NukiBaseResponse(HttpStatus.REQUEST_TIMEOUT_408,
101 "InterruptedIOException! Check IP/Port configuration and if Nuki Bridge is powered on!");
102 } else if (e.getCause() instanceof SocketException) {
104 "SocketException! Exception[{}]! Check IP/Port configuration and if Nuki Bridge is powered on!",
106 return new NukiBaseResponse(HttpStatus.NOT_FOUND_404,
107 "SocketException! Check IP/Port configuration and if Nuki Bridge is powered on!");
110 logger.error("Could not handle Exception! Exception[{}]", e.getMessage(), e);
111 return new NukiBaseResponse(HttpStatus.INTERNAL_SERVER_ERROR_500, e.getMessage());
114 public BridgeInfoResponse getBridgeInfo() {
115 logger.debug("getBridgeInfo() in thread {}", Thread.currentThread().getId());
116 String uri = prepareUri(NukiBindingConstants.URI_INFO);
118 ContentResponse contentResponse = executeRequest(uri);
119 int status = contentResponse.getStatus();
120 String response = contentResponse.getContentAsString();
121 logger.debug("getBridgeInfo status[{}] response[{}]", status, response);
122 if (status == HttpStatus.OK_200) {
123 BridgeApiInfoDto bridgeApiInfoDto = gson.fromJson(response, BridgeApiInfoDto.class);
124 logger.debug("getBridgeInfo OK");
125 return new BridgeInfoResponse(status, contentResponse.getReason(), bridgeApiInfoDto);
127 logger.debug("Could not get Bridge Info! Status[{}] - Response[{}]", status, response);
128 return new BridgeInfoResponse(status, contentResponse.getReason(), null);
130 } catch (Exception e) {
131 logger.debug("Could not get Bridge Info! Exception[{}]", e.getMessage());
132 return new BridgeInfoResponse(handleException(e));
136 public BridgeLockStateResponse getBridgeLockState(String nukiId) {
137 logger.debug("getBridgeLockState({}) in thread {}", nukiId, Thread.currentThread().getId());
138 long timestampSecs = Instant.now().getEpochSecond();
139 if (this.bridgeLockStateResponseCache != null
140 && timestampSecs < this.bridgeLockStateResponseCache.getCreated().getEpochSecond() + CACHE_PERIOD) {
141 logger.debug("Returning LockState from cache - now[{}]<created[{}]+cachePeriod[{}]", timestampSecs,
142 this.bridgeLockStateResponseCache.getCreated().getEpochSecond(), CACHE_PERIOD);
143 return bridgeLockStateResponseCache;
145 logger.debug("Requesting LockState from Bridge.");
147 String uri = prepareUri(NukiBindingConstants.URI_LOCKSTATE, nukiId);
149 ContentResponse contentResponse = executeRequest(uri);
150 int status = contentResponse.getStatus();
151 String response = contentResponse.getContentAsString();
152 logger.debug("getBridgeLockState status[{}] response[{}]", status, response);
153 if (status == HttpStatus.OK_200) {
154 BridgeApiLockStateDto bridgeApiLockStateDto = gson.fromJson(response, BridgeApiLockStateDto.class);
155 logger.debug("getBridgeLockState OK");
156 return bridgeLockStateResponseCache = new BridgeLockStateResponse(status, contentResponse.getReason(),
157 bridgeApiLockStateDto);
159 logger.debug("Could not get Lock State! Status[{}] - Response[{}]", status, response);
160 return new BridgeLockStateResponse(status, contentResponse.getReason(), null);
162 } catch (Exception e) {
163 logger.debug("Could not get Bridge Lock State! Exception[{}]", e.getMessage());
164 return new BridgeLockStateResponse(handleException(e));
168 public BridgeLockActionResponse getBridgeLockAction(String nukiId, int lockAction) {
169 logger.debug("getBridgeLockAction({}, {}) in thread {}", nukiId, lockAction, Thread.currentThread().getId());
170 String uri = prepareUri(NukiBindingConstants.URI_LOCKACTION, nukiId, Integer.toString(lockAction));
172 ContentResponse contentResponse = executeRequest(uri);
173 int status = contentResponse.getStatus();
174 String response = contentResponse.getContentAsString();
175 logger.debug("getBridgeLockAction status[{}] response[{}]", status, response);
176 if (status == HttpStatus.OK_200) {
177 BridgeApiLockActionDto bridgeApiLockActionDto = gson.fromJson(response, BridgeApiLockActionDto.class);
178 logger.debug("getBridgeLockAction OK");
179 return new BridgeLockActionResponse(status, contentResponse.getReason(), bridgeApiLockActionDto);
181 logger.debug("Could not execute Lock Action! Status[{}] - Response[{}]", status, response);
182 return new BridgeLockActionResponse(status, contentResponse.getReason(), null);
184 } catch (Exception e) {
185 logger.debug("Could not execute Lock Action! Exception[{}]", e.getMessage());
186 return new BridgeLockActionResponse(handleException(e));
190 public BridgeCallbackAddResponse getBridgeCallbackAdd(String callbackUrl) {
191 logger.debug("getBridgeCallbackAdd({}) in thread {}", callbackUrl, Thread.currentThread().getId());
193 String uri = prepareUri(NukiBindingConstants.URI_CBADD, URLEncoder.encode(callbackUrl, "UTF-8"));
194 ContentResponse contentResponse = executeRequest(uri);
195 int status = contentResponse.getStatus();
196 String response = contentResponse.getContentAsString();
197 logger.debug("getBridgeCallbackAdd status[{}] response[{}]", status, response);
198 if (status == HttpStatus.OK_200) {
199 BridgeApiCallbackAddDto bridgeApiCallbackAddDto = gson.fromJson(response,
200 BridgeApiCallbackAddDto.class);
201 logger.debug("getBridgeCallbackAdd OK");
202 return new BridgeCallbackAddResponse(status, contentResponse.getReason(), bridgeApiCallbackAddDto);
204 logger.debug("Could not execute Callback Add! Status[{}] - Response[{}]", status, response);
205 return new BridgeCallbackAddResponse(status, contentResponse.getReason(), null);
207 } catch (Exception e) {
208 logger.debug("Could not execute Callback Add! Exception[{}]", e.getMessage());
209 return new BridgeCallbackAddResponse(handleException(e));
213 public BridgeCallbackListResponse getBridgeCallbackList() {
214 logger.debug("getBridgeCallbackList() in thread {}", Thread.currentThread().getId());
215 String uri = prepareUri(NukiBindingConstants.URI_CBLIST);
217 ContentResponse contentResponse = executeRequest(uri);
218 int status = contentResponse.getStatus();
219 String response = contentResponse.getContentAsString();
220 logger.debug("getBridgeCallbackList status[{}] response[{}]", status, response);
221 if (status == HttpStatus.OK_200) {
222 BridgeApiCallbackListDto bridgeApiCallbackListDto = gson.fromJson(response,
223 BridgeApiCallbackListDto.class);
224 logger.debug("getBridgeCallbackList OK");
225 return new BridgeCallbackListResponse(status, contentResponse.getReason(), bridgeApiCallbackListDto);
227 logger.debug("Could not execute Callback List! Status[{}] - Response[{}]", status, response);
228 return new BridgeCallbackListResponse(status, contentResponse.getReason(), null);
230 } catch (Exception e) {
231 logger.debug("Could not execute Callback List! Exception[{}]", e.getMessage());
232 return new BridgeCallbackListResponse(handleException(e));
236 public BridgeCallbackRemoveResponse getBridgeCallbackRemove(int id) {
237 logger.debug("getBridgeCallbackRemove({}) in thread {}", id, Thread.currentThread().getId());
239 String uri = prepareUri(NukiBindingConstants.URI_CBREMOVE, Integer.toString(id));
240 ContentResponse contentResponse = executeRequest(uri);
241 int status = contentResponse.getStatus();
242 String response = contentResponse.getContentAsString();
243 logger.debug("getBridgeCallbackRemove status[{}] response[{}]", status, response);
244 if (status == HttpStatus.OK_200) {
245 BridgeApiCallbackRemoveDto bridgeApiCallbackRemoveDto = gson.fromJson(response,
246 BridgeApiCallbackRemoveDto.class);
247 logger.debug("getBridgeCallbackRemove OK");
248 return new BridgeCallbackRemoveResponse(status, contentResponse.getReason(),
249 bridgeApiCallbackRemoveDto);
251 logger.debug("Could not execute Callback Remove! Status[{}] - Response[{}]", status, response);
252 return new BridgeCallbackRemoveResponse(status, contentResponse.getReason(), null);
254 } catch (Exception e) {
255 logger.debug("Could not execute Callback Remove! Exception[{}]", e.getMessage());
256 return new BridgeCallbackRemoveResponse(handleException(e));