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.nuki.internal.dataexchange;
15 import java.io.InterruptedIOException;
16 import java.net.SocketException;
18 import java.util.Arrays;
19 import java.util.concurrent.ExecutionException;
20 import java.util.concurrent.TimeoutException;
22 import org.eclipse.jdt.annotation.NonNullByDefault;
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.constants.NukiBindingConstants;
28 import org.openhab.binding.nuki.internal.constants.NukiLinkBuilder;
29 import org.openhab.binding.nuki.internal.constants.OpenerAction;
30 import org.openhab.binding.nuki.internal.constants.SmartLockAction;
31 import org.openhab.binding.nuki.internal.dto.BridgeApiCallbackAddDto;
32 import org.openhab.binding.nuki.internal.dto.BridgeApiCallbackListDto;
33 import org.openhab.binding.nuki.internal.dto.BridgeApiCallbackRemoveDto;
34 import org.openhab.binding.nuki.internal.dto.BridgeApiInfoDto;
35 import org.openhab.binding.nuki.internal.dto.BridgeApiListDeviceDto;
36 import org.openhab.binding.nuki.internal.dto.BridgeApiLockActionDto;
37 import org.openhab.binding.nuki.internal.dto.BridgeApiLockStateDto;
38 import org.slf4j.Logger;
39 import org.slf4j.LoggerFactory;
41 import com.google.gson.Gson;
44 * The {@link NukiHttpClient} class is responsible for getting data from the Nuki Bridge.
46 * @author Markus Katter - Initial contribution
47 * @contributer Jan Vybíral - Hashed token authentication
50 public class NukiHttpClient {
52 private final Logger logger = LoggerFactory.getLogger(NukiHttpClient.class);
54 private final HttpClient httpClient;
55 private final Gson gson;
56 private final NukiLinkBuilder linkBuilder;
58 public NukiHttpClient(HttpClient httpClient, NukiLinkBuilder linkBuilder) {
59 logger.debug("Instantiating NukiHttpClient");
60 this.httpClient = httpClient;
61 this.linkBuilder = linkBuilder;
65 private synchronized ContentResponse executeRequest(URI uri)
66 throws InterruptedException, ExecutionException, TimeoutException {
67 logger.debug("executeRequest({})", uri);
68 ContentResponse contentResponse = httpClient.GET(uri);
69 logger.debug("contentResponseAsString[{}]", contentResponse.getContentAsString());
70 return contentResponse;
73 private NukiBaseResponse handleException(Exception e) {
74 if (e instanceof ExecutionException) {
75 Throwable cause = e.getCause();
76 if (cause instanceof HttpResponseException) {
77 HttpResponseException causeException = (HttpResponseException) cause;
78 int status = causeException.getResponse().getStatus();
79 String reason = causeException.getResponse().getReason();
80 logger.debug("HTTP Response Exception! Status[{}] - Reason[{}]! Check your API Token!", status, reason);
81 return new NukiBaseResponse(status, reason);
82 } else if (cause instanceof InterruptedIOException) {
84 "InterruptedIOException! Exception[{}]! Check IP/Port configuration and if Nuki Bridge is powered on!",
86 return new NukiBaseResponse(HttpStatus.REQUEST_TIMEOUT_408,
87 "InterruptedIOException! Check IP/Port configuration and if Nuki Bridge is powered on!");
88 } else if (cause instanceof SocketException) {
90 "SocketException! Exception[{}]! Check IP/Port configuration and if Nuki Bridge is powered on!",
92 return new NukiBaseResponse(HttpStatus.NOT_FOUND_404,
93 "SocketException! Check IP/Port configuration and if Nuki Bridge is powered on!");
96 logger.error("Could not handle Exception! Exception[{}]", e.getMessage(), e);
97 return new NukiBaseResponse(HttpStatus.INTERNAL_SERVER_ERROR_500, e.getMessage());
100 public BridgeInfoResponse getBridgeInfo() {
101 logger.debug("getBridgeInfo() in thread {}", Thread.currentThread().getId());
103 ContentResponse contentResponse = executeRequest(linkBuilder.info());
104 int status = contentResponse.getStatus();
105 String response = contentResponse.getContentAsString();
106 logger.debug("getBridgeInfo status[{}] response[{}]", status, response);
107 if (status == HttpStatus.OK_200) {
108 BridgeApiInfoDto bridgeApiInfoDto = gson.fromJson(response, BridgeApiInfoDto.class);
109 logger.debug("getBridgeInfo OK");
110 return new BridgeInfoResponse(status, contentResponse.getReason(), bridgeApiInfoDto);
112 logger.debug("Could not get Bridge Info! Status[{}] - Response[{}]", status, response);
113 return new BridgeInfoResponse(status, contentResponse.getReason(), null);
115 } catch (Exception e) {
116 logger.debug("Could not get Bridge Info! Exception[{}]", e.getMessage());
117 return new BridgeInfoResponse(handleException(e));
121 public BridgeListResponse getList() {
122 logger.debug("getList() in thread {}", Thread.currentThread().getId());
124 ContentResponse contentResponse = executeRequest(linkBuilder.list());
125 int status = contentResponse.getStatus();
126 String response = contentResponse.getContentAsString();
127 logger.debug("getList status[{}] response[{}]", status, response);
128 if (status == HttpStatus.OK_200) {
129 BridgeApiListDeviceDto[] bridgeApiInfoDtoArray = gson.fromJson(response,
130 BridgeApiListDeviceDto[].class);
131 logger.debug("getList OK");
132 return new BridgeListResponse(status, contentResponse.getReason(),
133 Arrays.asList(bridgeApiInfoDtoArray));
135 logger.debug("Could not get Bridge Info! Status[{}] - Response[{}]", status, response);
136 return new BridgeListResponse(status, contentResponse.getReason(), null);
138 } catch (Exception e) {
139 logger.debug("Could not get List! Exception[{}]", e.getMessage());
140 return new BridgeListResponse(handleException(e));
144 public BridgeLockStateResponse getBridgeLockState(String nukiId, int deviceType) {
145 logger.debug("getBridgeLockState({}) in thread {}", nukiId, Thread.currentThread().getId());
148 ContentResponse contentResponse = executeRequest(linkBuilder.lockState(nukiId, deviceType));
149 int status = contentResponse.getStatus();
150 String response = contentResponse.getContentAsString();
151 logger.debug("getBridgeLockState status[{}] response[{}]", status, response);
152 if (status == HttpStatus.OK_200) {
153 BridgeApiLockStateDto bridgeApiLockStateDto = gson.fromJson(response, BridgeApiLockStateDto.class);
154 logger.debug("getBridgeLockState OK");
155 return new BridgeLockStateResponse(status, contentResponse.getReason(), bridgeApiLockStateDto);
157 logger.debug("Could not get Lock State! Status[{}] - Response[{}]", status, response);
158 return new BridgeLockStateResponse(status, contentResponse.getReason(), null);
160 } catch (Exception e) {
161 logger.debug("Could not get Bridge Lock State!", e);
162 return new BridgeLockStateResponse(handleException(e));
166 public BridgeLockActionResponse getSmartLockAction(String nukiId, SmartLockAction action, int deviceType) {
167 return getBridgeLockAction(nukiId, action.getAction(), deviceType);
170 public BridgeLockActionResponse getOpenerAction(String nukiId, OpenerAction action) {
171 return getBridgeLockAction(nukiId, action.getAction(), NukiBindingConstants.DEVICE_OPENER);
174 private BridgeLockActionResponse getBridgeLockAction(String nukiId, int lockAction, int deviceType) {
175 logger.debug("getBridgeLockAction({}, {}) in thread {}", nukiId, lockAction, Thread.currentThread().getId());
177 ContentResponse contentResponse = executeRequest(linkBuilder.lockAction(nukiId, deviceType, lockAction));
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 ContentResponse contentResponse = executeRequest(linkBuilder.callbackAdd(callbackUrl));
199 int status = contentResponse.getStatus();
200 String response = contentResponse.getContentAsString();
201 logger.debug("getBridgeCallbackAdd status[{}] response[{}]", status, response);
202 if (status == HttpStatus.OK_200) {
203 BridgeApiCallbackAddDto bridgeApiCallbackAddDto = gson.fromJson(response,
204 BridgeApiCallbackAddDto.class);
205 logger.debug("getBridgeCallbackAdd OK");
206 return new BridgeCallbackAddResponse(status, contentResponse.getReason(), bridgeApiCallbackAddDto);
208 logger.debug("Could not execute Callback Add! Status[{}] - Response[{}]", status, response);
209 return new BridgeCallbackAddResponse(status, contentResponse.getReason(), null);
211 } catch (Exception e) {
212 logger.debug("Could not execute Callback Add! Exception[{}]", e.getMessage());
213 return new BridgeCallbackAddResponse(handleException(e));
217 public BridgeCallbackListResponse getBridgeCallbackList() {
218 logger.debug("getBridgeCallbackList() in thread {}", Thread.currentThread().getId());
220 ContentResponse contentResponse = executeRequest(linkBuilder.callbackList());
221 int status = contentResponse.getStatus();
222 String response = contentResponse.getContentAsString();
223 logger.debug("getBridgeCallbackList status[{}] response[{}]", status, response);
224 if (status == HttpStatus.OK_200) {
225 BridgeApiCallbackListDto bridgeApiCallbackListDto = gson.fromJson(response,
226 BridgeApiCallbackListDto.class);
227 logger.debug("getBridgeCallbackList OK");
228 return new BridgeCallbackListResponse(status, contentResponse.getReason(), bridgeApiCallbackListDto);
230 logger.debug("Could not execute Callback List! Status[{}] - Response[{}]", status, response);
231 return new BridgeCallbackListResponse(status, contentResponse.getReason(), null);
233 } catch (Exception e) {
234 logger.debug("Could not execute Callback List! Exception[{}]", e.getMessage());
235 return new BridgeCallbackListResponse(handleException(e));
239 public BridgeCallbackRemoveResponse getBridgeCallbackRemove(int id) {
240 logger.debug("getBridgeCallbackRemove({}) in thread {}", id, Thread.currentThread().getId());
242 ContentResponse contentResponse = executeRequest(linkBuilder.callbackRemove(id));
243 int status = contentResponse.getStatus();
244 String response = contentResponse.getContentAsString();
245 logger.debug("getBridgeCallbackRemove status[{}] response[{}]", status, response);
246 if (status == HttpStatus.OK_200) {
247 BridgeApiCallbackRemoveDto bridgeApiCallbackRemoveDto = gson.fromJson(response,
248 BridgeApiCallbackRemoveDto.class);
249 logger.debug("getBridgeCallbackRemove OK");
250 return new BridgeCallbackRemoveResponse(status, contentResponse.getReason(),
251 bridgeApiCallbackRemoveDto);
253 logger.debug("Could not execute Callback Remove! Status[{}] - Response[{}]", status, response);
254 return new BridgeCallbackRemoveResponse(status, contentResponse.getReason(), null);
256 } catch (Exception e) {
257 logger.debug("Could not execute Callback Remove! Exception[{}]", e.getMessage());
258 return new BridgeCallbackRemoveResponse(handleException(e));