]> git.basschouten.com Git - openhab-addons.git/blob
47551dd0a231019858d8ff391082f5adb5233e71
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2023 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.doorbird.internal.api;
14
15 import java.io.IOException;
16 import java.time.Duration;
17 import java.time.ZonedDateTime;
18 import java.util.concurrent.ExecutionException;
19 import java.util.concurrent.TimeUnit;
20 import java.util.concurrent.TimeoutException;
21
22 import org.eclipse.jdt.annotation.NonNullByDefault;
23 import org.eclipse.jdt.annotation.Nullable;
24 import org.eclipse.jetty.client.HttpClient;
25 import org.eclipse.jetty.client.api.ContentResponse;
26 import org.eclipse.jetty.client.api.Request;
27 import org.eclipse.jetty.http.HttpHeader;
28 import org.eclipse.jetty.http.HttpMethod;
29 import org.eclipse.jetty.http.HttpStatus;
30 import org.openhab.core.io.net.http.HttpRequestBuilder;
31 import org.openhab.core.library.types.RawType;
32 import org.slf4j.Logger;
33 import org.slf4j.LoggerFactory;
34
35 import com.google.gson.Gson;
36 import com.google.gson.JsonSyntaxException;
37
38 /**
39  * The {@link DoorbirdAPI} class exposes the functionality provided by the Doorbird API.
40  *
41  * @author Mark Hilbush - Initial contribution
42  */
43 @NonNullByDefault
44 public final class DoorbirdAPI {
45     private static final long API_REQUEST_TIMEOUT_SECONDS = 16L;
46
47     // Single Gson instance shared by multiple classes
48     private static final Gson GSON = new Gson();
49
50     private final Logger logger = LoggerFactory.getLogger(DoorbirdAPI.class);
51
52     private @Nullable Authorization authorization;
53     private @Nullable HttpClient httpClient;
54
55     public static Gson getGson() {
56         return (GSON);
57     }
58
59     public static <T> T fromJson(String json, Class<T> dataClass) {
60         return GSON.fromJson(json, dataClass);
61     }
62
63     public void setAuthorization(String doorbirdHost, String userId, String userPassword) {
64         this.authorization = new Authorization(doorbirdHost, userId, userPassword);
65     }
66
67     public void setHttpClient(HttpClient httpClient) {
68         this.httpClient = httpClient;
69     }
70
71     public @Nullable DoorbirdInfo getDoorbirdInfo() {
72         DoorbirdInfo doorbirdInfo = null;
73         try {
74             String infoResponse = executeGetRequest("/bha-api/info.cgi");
75             logger.debug("Doorbird returned json response: {}", infoResponse);
76             doorbirdInfo = new DoorbirdInfo(infoResponse);
77         } catch (IOException e) {
78             logger.info("Unable to communicate with Doorbird: {}", e.getMessage());
79         } catch (JsonSyntaxException e) {
80             logger.info("Unable to parse Doorbird response: {}", e.getMessage());
81         } catch (DoorbirdUnauthorizedException e) {
82             logAuthorizationError("getDoorbirdName");
83         }
84         return doorbirdInfo;
85     }
86
87     public @Nullable SipStatus getSipStatus() {
88         SipStatus sipStatus = null;
89         try {
90             String statusResponse = executeGetRequest("/bha-api/sip.cgi&action=status");
91             logger.debug("Doorbird returned json response: {}", statusResponse);
92             sipStatus = new SipStatus(statusResponse);
93         } catch (IOException e) {
94             logger.info("Unable to communicate with Doorbird: {}", e.getMessage());
95         } catch (JsonSyntaxException e) {
96             logger.info("Unable to parse Doorbird response: {}", e.getMessage());
97         } catch (DoorbirdUnauthorizedException e) {
98             logAuthorizationError("getSipStatus");
99         }
100         return sipStatus;
101     }
102
103     public void lightOn() {
104         try {
105             String response = executeGetRequest("/bha-api/light-on.cgi");
106             logger.debug("Response={}", response);
107         } catch (IOException e) {
108             logger.debug("IOException turning on light: {}", e.getMessage());
109         } catch (DoorbirdUnauthorizedException e) {
110             logAuthorizationError("lightOn");
111         }
112     }
113
114     public void restart() {
115         try {
116             String response = executeGetRequest("/bha-api/restart.cgi");
117             logger.debug("Response={}", response);
118         } catch (IOException e) {
119             logger.debug("IOException restarting device: {}", e.getMessage());
120         } catch (DoorbirdUnauthorizedException e) {
121             logAuthorizationError("restart");
122         }
123     }
124
125     public void sipHangup() {
126         try {
127             String response = executeGetRequest("/bha-api/sip.cgi?action=hangup");
128             logger.debug("Response={}", response);
129         } catch (IOException e) {
130             logger.debug("IOException hanging up SIP call: {}", e.getMessage());
131         } catch (DoorbirdUnauthorizedException e) {
132             logAuthorizationError("sipHangup");
133         }
134     }
135
136     public @Nullable DoorbirdImage downloadCurrentImage() {
137         return downloadImage("/bha-api/image.cgi");
138     }
139
140     public @Nullable DoorbirdImage downloadDoorbellHistoryImage(String imageNumber) {
141         return downloadImage("/bha-api/history.cgi?event=doorbell&index=" + imageNumber);
142     }
143
144     public @Nullable DoorbirdImage downloadMotionHistoryImage(String imageNumber) {
145         return downloadImage("/bha-api/history.cgi?event=motionsensor&index=" + imageNumber);
146     }
147
148     public void openDoorController(String controllerId, String doorNumber) {
149         openDoor("/bha-api/open-door.cgi?r=" + controllerId + "@" + doorNumber);
150     }
151
152     public void openDoorDoorbell(String doorNumber) {
153         openDoor("/bha-api/open-door.cgi?r=" + doorNumber);
154     }
155
156     private void openDoor(String urlFragment) {
157         try {
158             String response = executeGetRequest(urlFragment);
159             logger.debug("Response={}", response);
160         } catch (IOException e) {
161             logger.debug("IOException opening door: {}", e.getMessage());
162         } catch (DoorbirdUnauthorizedException e) {
163             logAuthorizationError("openDoor");
164         }
165     }
166
167     private @Nullable synchronized DoorbirdImage downloadImage(String urlFragment) {
168         Authorization auth = authorization;
169         if (auth == null) {
170             logAuthorizationError("downloadImage");
171             return null;
172         }
173         HttpClient client = httpClient;
174         if (client == null) {
175             logger.info("Unable to download image because httpClient is not set");
176             return null;
177         }
178         String errorMsg;
179         try {
180             String url = buildUrl(auth, urlFragment);
181             logger.debug("Downloading image from doorbird: {}", url);
182             Request request = client.newRequest(url);
183             request.method(HttpMethod.GET);
184             request.header("Authorization", "Basic " + auth.getAuthorization());
185             request.timeout(API_REQUEST_TIMEOUT_SECONDS, TimeUnit.SECONDS);
186
187             ContentResponse contentResponse = request.send();
188             switch (contentResponse.getStatus()) {
189                 case HttpStatus.OK_200:
190                     DoorbirdImage doorbirdImage = new DoorbirdImage();
191                     doorbirdImage.setImage(new RawType(contentResponse.getContent(),
192                             contentResponse.getHeaders().get(HttpHeader.CONTENT_TYPE)));
193                     doorbirdImage.setTimestamp(convertXTimestamp(contentResponse.getHeaders().get("X-Timestamp")));
194                     return doorbirdImage;
195
196                 default:
197                     errorMsg = String.format("HTTP GET failed: %d, %s", contentResponse.getStatus(),
198                             contentResponse.getReason());
199                     break;
200             }
201         } catch (TimeoutException e) {
202             errorMsg = "TimeoutException: Call to Doorbird API timed out";
203         } catch (ExecutionException e) {
204             errorMsg = String.format("ExecutionException: %s", e.getMessage());
205         } catch (InterruptedException e) {
206             errorMsg = String.format("InterruptedException: %s", e.getMessage());
207             Thread.currentThread().interrupt();
208         }
209         logger.debug("{}", errorMsg);
210         return null;
211     }
212
213     private long convertXTimestamp(@Nullable String timestamp) {
214         // Convert Unix Epoch string timestamp to long value
215         // Use current time if passed null string or if conversion fails
216         long value;
217         if (timestamp != null) {
218             try {
219                 value = Integer.parseInt(timestamp);
220             } catch (NumberFormatException e) {
221                 logger.debug("X-Timestamp header is not a number: {}", timestamp);
222                 value = ZonedDateTime.now().toEpochSecond();
223             }
224         } else {
225             value = ZonedDateTime.now().toEpochSecond();
226         }
227         return value;
228     }
229
230     private String buildUrl(Authorization auth, String path) {
231         return "http://" + auth.getHost() + path;
232     }
233
234     private synchronized String executeGetRequest(String urlFragment)
235             throws IOException, DoorbirdUnauthorizedException {
236         Authorization auth = authorization;
237         if (auth == null) {
238             throw new DoorbirdUnauthorizedException();
239         }
240         String url = buildUrl(auth, urlFragment);
241         logger.debug("Executing doorbird API request: {}", url);
242         // @formatter:off
243         return HttpRequestBuilder.getFrom(url)
244             .withTimeout(Duration.ofSeconds(API_REQUEST_TIMEOUT_SECONDS))
245             .withHeader("Authorization", "Basic " + auth.getAuthorization())
246             .withHeader("charset", "utf-8")
247             .withHeader("Accept-language", "en-us")
248             .getContentAsString();
249         // @formatter:on
250     }
251
252     private void logAuthorizationError(String operation) {
253         logger.info("Authorization info is not set or is incorrect on call to '{}' API", operation);
254     }
255 }