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.ArrayList;
21 import java.util.concurrent.ExecutionException;
22 import java.util.concurrent.TimeoutException;
24 import org.eclipse.jetty.client.HttpClient;
25 import org.eclipse.jetty.client.HttpResponseException;
26 import org.eclipse.jetty.client.api.ContentResponse;
27 import org.eclipse.jetty.http.HttpStatus;
28 import org.openhab.binding.nuki.internal.NukiBindingConstants;
29 import org.openhab.binding.nuki.internal.dto.BridgeApiCallbackAddDto;
30 import org.openhab.binding.nuki.internal.dto.BridgeApiCallbackListDto;
31 import org.openhab.binding.nuki.internal.dto.BridgeApiCallbackRemoveDto;
32 import org.openhab.binding.nuki.internal.dto.BridgeApiInfoDto;
33 import org.openhab.binding.nuki.internal.dto.BridgeApiLockActionDto;
34 import org.openhab.binding.nuki.internal.dto.BridgeApiLockStateDto;
35 import org.openhab.core.config.core.Configuration;
36 import org.slf4j.Logger;
37 import org.slf4j.LoggerFactory;
39 import com.google.gson.Gson;
42 * The {@link NukiHttpClient} class is responsible for getting data from the Nuki Bridge.
44 * @author Markus Katter - Initial contribution
46 public class NukiHttpClient {
48 private final Logger logger = LoggerFactory.getLogger(NukiHttpClient.class);
49 private static final long CACHE_PERIOD = 5;
51 private HttpClient httpClient;
52 private Configuration configuration;
54 private BridgeLockStateResponse bridgeLockStateResponseCache;
56 public NukiHttpClient(HttpClient httpClient, Configuration configuration) {
57 logger.debug("Instantiating NukiHttpClient({})", configuration);
58 this.configuration = configuration;
59 this.httpClient = httpClient;
63 private String prepareUri(String uriTemplate, String... additionalArguments) {
64 String configIp = (String) configuration.get(NukiBindingConstants.CONFIG_IP);
65 BigDecimal configPort = (BigDecimal) configuration.get(NukiBindingConstants.CONFIG_PORT);
66 String configApiToken = (String) configuration.get(NukiBindingConstants.CONFIG_API_TOKEN);
67 ArrayList<String> parameters = new ArrayList<>();
68 parameters.add(configIp);
69 parameters.add(configPort.toString());
70 parameters.add(configApiToken);
71 if (additionalArguments != null) {
72 for (String argument : additionalArguments) {
73 parameters.add(argument);
76 String uri = String.format(uriTemplate, parameters.toArray());
77 logger.trace("prepareUri(...):URI[{}]", uri);
81 private synchronized ContentResponse executeRequest(String uri)
82 throws InterruptedException, ExecutionException, TimeoutException {
83 logger.debug("executeRequest({})", uri);
86 } catch (InterruptedException e) {
88 ContentResponse contentResponse = httpClient.GET(uri);
89 logger.debug("contentResponseAsString[{}]", contentResponse.getContentAsString());
90 return contentResponse;
93 private NukiBaseResponse handleException(Exception e) {
94 if (e instanceof ExecutionException) {
95 if (e.getCause() instanceof HttpResponseException) {
96 HttpResponseException cause = (HttpResponseException) e.getCause();
97 int status = cause.getResponse().getStatus();
98 String reason = cause.getResponse().getReason();
99 logger.debug("HTTP Response Exception! Status[{}] - Reason[{}]! Check your API Token!", status, reason);
100 return new NukiBaseResponse(status, reason);
101 } else if (e.getCause() instanceof InterruptedIOException) {
103 "InterruptedIOException! Exception[{}]! Check IP/Port configuration and if Nuki Bridge is powered on!",
105 return new NukiBaseResponse(HttpStatus.REQUEST_TIMEOUT_408,
106 "InterruptedIOException! Check IP/Port configuration and if Nuki Bridge is powered on!");
107 } else if (e.getCause() instanceof SocketException) {
109 "SocketException! Exception[{}]! Check IP/Port configuration and if Nuki Bridge is powered on!",
111 return new NukiBaseResponse(HttpStatus.NOT_FOUND_404,
112 "SocketException! Check IP/Port configuration and if Nuki Bridge is powered on!");
115 logger.error("Could not handle Exception! Exception[{}]", e.getMessage(), e);
116 return new NukiBaseResponse(HttpStatus.INTERNAL_SERVER_ERROR_500, e.getMessage());
119 public BridgeInfoResponse getBridgeInfo() {
120 logger.debug("getBridgeInfo() in thread {}", Thread.currentThread().getId());
121 String uri = prepareUri(NukiBindingConstants.URI_INFO);
123 ContentResponse contentResponse = executeRequest(uri);
124 int status = contentResponse.getStatus();
125 String response = contentResponse.getContentAsString();
126 logger.debug("getBridgeInfo status[{}] response[{}]", status, response);
127 if (status == HttpStatus.OK_200) {
128 BridgeApiInfoDto bridgeApiInfoDto = gson.fromJson(response, BridgeApiInfoDto.class);
129 logger.debug("getBridgeInfo OK");
130 return new BridgeInfoResponse(status, contentResponse.getReason(), bridgeApiInfoDto);
132 logger.debug("Could not get Bridge Info! Status[{}] - Response[{}]", status, response);
133 return new BridgeInfoResponse(status, contentResponse.getReason(), null);
135 } catch (Exception e) {
136 logger.debug("Could not get Bridge Info! Exception[{}]", e.getMessage());
137 return new BridgeInfoResponse(handleException(e));
141 public BridgeLockStateResponse getBridgeLockState(String nukiId) {
142 logger.debug("getBridgeLockState({}) in thread {}", nukiId, Thread.currentThread().getId());
143 long timestampSecs = Instant.now().getEpochSecond();
144 if (this.bridgeLockStateResponseCache != null
145 && timestampSecs < this.bridgeLockStateResponseCache.getCreated().getEpochSecond() + CACHE_PERIOD) {
146 logger.debug("Returning LockState from cache - now[{}]<created[{}]+cachePeriod[{}]", timestampSecs,
147 this.bridgeLockStateResponseCache.getCreated().getEpochSecond(), CACHE_PERIOD);
148 return bridgeLockStateResponseCache;
150 logger.debug("Requesting LockState from Bridge.");
152 String uri = prepareUri(NukiBindingConstants.URI_LOCKSTATE, nukiId);
154 ContentResponse contentResponse = executeRequest(uri);
155 int status = contentResponse.getStatus();
156 String response = contentResponse.getContentAsString();
157 logger.debug("getBridgeLockState status[{}] response[{}]", status, response);
158 if (status == HttpStatus.OK_200) {
159 BridgeApiLockStateDto bridgeApiLockStateDto = gson.fromJson(response, BridgeApiLockStateDto.class);
160 logger.debug("getBridgeLockState OK");
161 return bridgeLockStateResponseCache = new BridgeLockStateResponse(status, contentResponse.getReason(),
162 bridgeApiLockStateDto);
164 logger.debug("Could not get Lock State! Status[{}] - Response[{}]", status, response);
165 return new BridgeLockStateResponse(status, contentResponse.getReason(), null);
167 } catch (Exception e) {
168 logger.debug("Could not get Bridge Lock State! Exception[{}]", e.getMessage());
169 return new BridgeLockStateResponse(handleException(e));
173 public BridgeLockActionResponse getBridgeLockAction(String nukiId, int lockAction) {
174 logger.debug("getBridgeLockAction({}, {}) in thread {}", nukiId, lockAction, Thread.currentThread().getId());
175 String uri = prepareUri(NukiBindingConstants.URI_LOCKACTION, nukiId, Integer.toString(lockAction));
177 ContentResponse contentResponse = executeRequest(uri);
178 int status = contentResponse.getStatus();
179 String response = contentResponse.getContentAsString();
180 logger.debug("getBridgeLockAction status[{}] response[{}]", status, response);
181 if (status == HttpStatus.OK_200) {
182 BridgeApiLockActionDto bridgeApiLockActionDto = gson.fromJson(response, BridgeApiLockActionDto.class);
183 logger.debug("getBridgeLockAction OK");
184 return new BridgeLockActionResponse(status, contentResponse.getReason(), bridgeApiLockActionDto);
186 logger.debug("Could not execute Lock Action! Status[{}] - Response[{}]", status, response);
187 return new BridgeLockActionResponse(status, contentResponse.getReason(), null);
189 } catch (Exception e) {
190 logger.debug("Could not execute Lock Action! Exception[{}]", e.getMessage());
191 return new BridgeLockActionResponse(handleException(e));
195 public BridgeCallbackAddResponse getBridgeCallbackAdd(String callbackUrl) {
196 logger.debug("getBridgeCallbackAdd({}) in thread {}", callbackUrl, Thread.currentThread().getId());
198 String uri = prepareUri(NukiBindingConstants.URI_CBADD, URLEncoder.encode(callbackUrl, "UTF-8"));
199 ContentResponse contentResponse = executeRequest(uri);
200 int status = contentResponse.getStatus();
201 String response = contentResponse.getContentAsString();
202 logger.debug("getBridgeCallbackAdd status[{}] response[{}]", status, response);
203 if (status == HttpStatus.OK_200) {
204 BridgeApiCallbackAddDto bridgeApiCallbackAddDto = gson.fromJson(response,
205 BridgeApiCallbackAddDto.class);
206 logger.debug("getBridgeCallbackAdd OK");
207 return new BridgeCallbackAddResponse(status, contentResponse.getReason(), bridgeApiCallbackAddDto);
209 logger.debug("Could not execute Callback Add! Status[{}] - Response[{}]", status, response);
210 return new BridgeCallbackAddResponse(status, contentResponse.getReason(), null);
212 } catch (Exception e) {
213 logger.debug("Could not execute Callback Add! Exception[{}]", e.getMessage());
214 return new BridgeCallbackAddResponse(handleException(e));
218 public BridgeCallbackListResponse getBridgeCallbackList() {
219 logger.debug("getBridgeCallbackList() in thread {}", Thread.currentThread().getId());
220 String uri = prepareUri(NukiBindingConstants.URI_CBLIST);
222 ContentResponse contentResponse = executeRequest(uri);
223 int status = contentResponse.getStatus();
224 String response = contentResponse.getContentAsString();
225 logger.debug("getBridgeCallbackList status[{}] response[{}]", status, response);
226 if (status == HttpStatus.OK_200) {
227 BridgeApiCallbackListDto bridgeApiCallbackListDto = gson.fromJson(response,
228 BridgeApiCallbackListDto.class);
229 logger.debug("getBridgeCallbackList OK");
230 return new BridgeCallbackListResponse(status, contentResponse.getReason(), bridgeApiCallbackListDto);
232 logger.debug("Could not execute Callback List! Status[{}] - Response[{}]", status, response);
233 return new BridgeCallbackListResponse(status, contentResponse.getReason(), null);
235 } catch (Exception e) {
236 logger.debug("Could not execute Callback List! Exception[{}]", e.getMessage());
237 return new BridgeCallbackListResponse(handleException(e));
241 public BridgeCallbackRemoveResponse getBridgeCallbackRemove(int id) {
242 logger.debug("getBridgeCallbackRemove({}) in thread {}", id, Thread.currentThread().getId());
244 String uri = prepareUri(NukiBindingConstants.URI_CBREMOVE, Integer.toString(id));
245 ContentResponse contentResponse = executeRequest(uri);
246 int status = contentResponse.getStatus();
247 String response = contentResponse.getContentAsString();
248 logger.debug("getBridgeCallbackRemove status[{}] response[{}]", status, response);
249 if (status == HttpStatus.OK_200) {
250 BridgeApiCallbackRemoveDto bridgeApiCallbackRemoveDto = gson.fromJson(response,
251 BridgeApiCallbackRemoveDto.class);
252 logger.debug("getBridgeCallbackRemove OK");
253 return new BridgeCallbackRemoveResponse(status, contentResponse.getReason(),
254 bridgeApiCallbackRemoveDto);
256 logger.debug("Could not execute Callback Remove! Status[{}] - Response[{}]", status, response);
257 return new BridgeCallbackRemoveResponse(status, contentResponse.getReason(), null);
259 } catch (Exception e) {
260 logger.debug("Could not execute Callback Remove! Exception[{}]", e.getMessage());
261 return new BridgeCallbackRemoveResponse(handleException(e));