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 * @author 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 causeException) {
77 int status = causeException.getResponse().getStatus();
78 String reason = causeException.getResponse().getReason();
79 logger.debug("HTTP Response Exception! Status[{}] - Reason[{}]! Check your API Token!", status, reason);
80 return new NukiBaseResponse(status, reason);
81 } else if (cause instanceof InterruptedIOException) {
83 "InterruptedIOException! Exception[{}]! Check IP/Port configuration and if Nuki Bridge is powered on!",
85 return new NukiBaseResponse(HttpStatus.REQUEST_TIMEOUT_408,
86 "InterruptedIOException! Check IP/Port configuration and if Nuki Bridge is powered on!");
87 } else if (cause instanceof SocketException) {
89 "SocketException! Exception[{}]! Check IP/Port configuration and if Nuki Bridge is powered on!",
91 return new NukiBaseResponse(HttpStatus.NOT_FOUND_404,
92 "SocketException! Check IP/Port configuration and if Nuki Bridge is powered on!");
95 logger.error("Could not handle Exception! Exception[{}]", e.getMessage(), e);
96 return new NukiBaseResponse(HttpStatus.INTERNAL_SERVER_ERROR_500, e.getMessage());
99 public BridgeInfoResponse getBridgeInfo() {
100 logger.debug("getBridgeInfo() in thread {}", Thread.currentThread().getId());
102 ContentResponse contentResponse = executeRequest(linkBuilder.info());
103 int status = contentResponse.getStatus();
104 String response = contentResponse.getContentAsString();
105 logger.debug("getBridgeInfo status[{}] response[{}]", status, response);
106 if (status == HttpStatus.OK_200) {
107 BridgeApiInfoDto bridgeApiInfoDto = gson.fromJson(response, BridgeApiInfoDto.class);
108 logger.debug("getBridgeInfo OK");
109 return new BridgeInfoResponse(status, contentResponse.getReason(), bridgeApiInfoDto);
111 logger.debug("Could not get Bridge Info! Status[{}] - Response[{}]", status, response);
112 return new BridgeInfoResponse(status, contentResponse.getReason(), null);
114 } catch (Exception e) {
115 logger.debug("Could not get Bridge Info! Exception[{}]", e.getMessage());
116 return new BridgeInfoResponse(handleException(e));
120 public BridgeListResponse getList() {
121 logger.debug("getList() in thread {}", Thread.currentThread().getId());
123 ContentResponse contentResponse = executeRequest(linkBuilder.list());
124 int status = contentResponse.getStatus();
125 String response = contentResponse.getContentAsString();
126 logger.debug("getList status[{}] response[{}]", status, response);
127 if (status == HttpStatus.OK_200) {
128 BridgeApiListDeviceDto[] bridgeApiInfoDtoArray = gson.fromJson(response,
129 BridgeApiListDeviceDto[].class);
130 logger.debug("getList OK");
131 return new BridgeListResponse(status, contentResponse.getReason(),
132 Arrays.asList(bridgeApiInfoDtoArray));
134 logger.debug("Could not get Bridge Info! Status[{}] - Response[{}]", status, response);
135 return new BridgeListResponse(status, contentResponse.getReason(), null);
137 } catch (Exception e) {
138 logger.debug("Could not get List! Exception[{}]", e.getMessage());
139 return new BridgeListResponse(handleException(e));
143 public BridgeLockStateResponse getBridgeLockState(String nukiId, int deviceType) {
144 logger.debug("getBridgeLockState({}) in thread {}", nukiId, Thread.currentThread().getId());
147 ContentResponse contentResponse = executeRequest(linkBuilder.lockState(nukiId, deviceType));
148 int status = contentResponse.getStatus();
149 String response = contentResponse.getContentAsString();
150 logger.debug("getBridgeLockState status[{}] response[{}]", status, response);
151 if (status == HttpStatus.OK_200) {
152 BridgeApiLockStateDto bridgeApiLockStateDto = gson.fromJson(response, BridgeApiLockStateDto.class);
153 logger.debug("getBridgeLockState OK");
154 return new BridgeLockStateResponse(status, contentResponse.getReason(), bridgeApiLockStateDto);
156 logger.debug("Could not get Lock State! Status[{}] - Response[{}]", status, response);
157 return new BridgeLockStateResponse(status, contentResponse.getReason(), null);
159 } catch (Exception e) {
160 logger.debug("Could not get Bridge Lock State!", e);
161 return new BridgeLockStateResponse(handleException(e));
165 public BridgeLockActionResponse getSmartLockAction(String nukiId, SmartLockAction action, int deviceType) {
166 return getBridgeLockAction(nukiId, action.getAction(), deviceType);
169 public BridgeLockActionResponse getOpenerAction(String nukiId, OpenerAction action) {
170 return getBridgeLockAction(nukiId, action.getAction(), NukiBindingConstants.DEVICE_OPENER);
173 private BridgeLockActionResponse getBridgeLockAction(String nukiId, int lockAction, int deviceType) {
174 logger.debug("getBridgeLockAction({}, {}) in thread {}", nukiId, lockAction, Thread.currentThread().getId());
176 ContentResponse contentResponse = executeRequest(linkBuilder.lockAction(nukiId, deviceType, lockAction));
177 int status = contentResponse.getStatus();
178 String response = contentResponse.getContentAsString();
179 logger.debug("getBridgeLockAction status[{}] response[{}]", status, response);
180 if (status == HttpStatus.OK_200) {
181 BridgeApiLockActionDto bridgeApiLockActionDto = gson.fromJson(response, BridgeApiLockActionDto.class);
182 logger.debug("getBridgeLockAction OK");
183 return new BridgeLockActionResponse(status, contentResponse.getReason(), bridgeApiLockActionDto);
185 logger.debug("Could not execute Lock Action! Status[{}] - Response[{}]", status, response);
186 return new BridgeLockActionResponse(status, contentResponse.getReason(), null);
188 } catch (Exception e) {
189 logger.debug("Could not execute Lock Action! Exception[{}]", e.getMessage());
190 return new BridgeLockActionResponse(handleException(e));
194 public BridgeCallbackAddResponse getBridgeCallbackAdd(String callbackUrl) {
195 logger.debug("getBridgeCallbackAdd({}) in thread {}", callbackUrl, Thread.currentThread().getId());
197 ContentResponse contentResponse = executeRequest(linkBuilder.callbackAdd(callbackUrl));
198 int status = contentResponse.getStatus();
199 String response = contentResponse.getContentAsString();
200 logger.debug("getBridgeCallbackAdd status[{}] response[{}]", status, response);
201 if (status == HttpStatus.OK_200) {
202 BridgeApiCallbackAddDto bridgeApiCallbackAddDto = gson.fromJson(response,
203 BridgeApiCallbackAddDto.class);
204 logger.debug("getBridgeCallbackAdd OK");
205 return new BridgeCallbackAddResponse(status, contentResponse.getReason(), bridgeApiCallbackAddDto);
207 logger.debug("Could not execute Callback Add! Status[{}] - Response[{}]", status, response);
208 return new BridgeCallbackAddResponse(status, contentResponse.getReason(), null);
210 } catch (Exception e) {
211 logger.debug("Could not execute Callback Add! Exception[{}]", e.getMessage());
212 return new BridgeCallbackAddResponse(handleException(e));
216 public BridgeCallbackListResponse getBridgeCallbackList() {
217 logger.debug("getBridgeCallbackList() in thread {}", Thread.currentThread().getId());
219 ContentResponse contentResponse = executeRequest(linkBuilder.callbackList());
220 int status = contentResponse.getStatus();
221 String response = contentResponse.getContentAsString();
222 logger.debug("getBridgeCallbackList status[{}] response[{}]", status, response);
223 if (status == HttpStatus.OK_200) {
224 BridgeApiCallbackListDto bridgeApiCallbackListDto = gson.fromJson(response,
225 BridgeApiCallbackListDto.class);
226 logger.debug("getBridgeCallbackList OK");
227 return new BridgeCallbackListResponse(status, contentResponse.getReason(), bridgeApiCallbackListDto);
229 logger.debug("Could not execute Callback List! Status[{}] - Response[{}]", status, response);
230 return new BridgeCallbackListResponse(status, contentResponse.getReason(), null);
232 } catch (Exception e) {
233 logger.debug("Could not execute Callback List! Exception[{}]", e.getMessage());
234 return new BridgeCallbackListResponse(handleException(e));
238 public BridgeCallbackRemoveResponse getBridgeCallbackRemove(int id) {
239 logger.debug("getBridgeCallbackRemove({}) in thread {}", id, Thread.currentThread().getId());
241 ContentResponse contentResponse = executeRequest(linkBuilder.callbackRemove(id));
242 int status = contentResponse.getStatus();
243 String response = contentResponse.getContentAsString();
244 logger.debug("getBridgeCallbackRemove status[{}] response[{}]", status, response);
245 if (status == HttpStatus.OK_200) {
246 BridgeApiCallbackRemoveDto bridgeApiCallbackRemoveDto = gson.fromJson(response,
247 BridgeApiCallbackRemoveDto.class);
248 logger.debug("getBridgeCallbackRemove OK");
249 return new BridgeCallbackRemoveResponse(status, contentResponse.getReason(),
250 bridgeApiCallbackRemoveDto);
252 logger.debug("Could not execute Callback Remove! Status[{}] - Response[{}]", status, response);
253 return new BridgeCallbackRemoveResponse(status, contentResponse.getReason(), null);
255 } catch (Exception e) {
256 logger.debug("Could not execute Callback Remove! Exception[{}]", e.getMessage());
257 return new BridgeCallbackRemoveResponse(handleException(e));