]> git.basschouten.com Git - openhab-addons.git/blob
08a3e5c046709ceaa80fd6add234a16e33b6de38
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2022 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.nanoleaf.internal;
14
15 import static org.openhab.binding.nanoleaf.internal.NanoleafBindingConstants.API_ADD_USER;
16 import static org.openhab.binding.nanoleaf.internal.NanoleafBindingConstants.API_V1_BASE_URL;
17
18 import java.net.URI;
19 import java.net.URISyntaxException;
20 import java.nio.ByteBuffer;
21 import java.nio.charset.StandardCharsets;
22 import java.util.Iterator;
23 import java.util.concurrent.ExecutionException;
24 import java.util.concurrent.TimeUnit;
25 import java.util.concurrent.TimeoutException;
26 import java.util.regex.Matcher;
27 import java.util.regex.Pattern;
28
29 import org.eclipse.jdt.annotation.NonNullByDefault;
30 import org.eclipse.jdt.annotation.Nullable;
31 import org.eclipse.jetty.client.HttpClient;
32 import org.eclipse.jetty.client.HttpResponseException;
33 import org.eclipse.jetty.client.api.ContentResponse;
34 import org.eclipse.jetty.client.api.Request;
35 import org.eclipse.jetty.http.HttpMethod;
36 import org.eclipse.jetty.http.HttpScheme;
37 import org.eclipse.jetty.http.HttpStatus;
38 import org.openhab.binding.nanoleaf.internal.config.NanoleafControllerConfig;
39 import org.slf4j.Logger;
40 import org.slf4j.LoggerFactory;
41
42 /**
43  * The {@link OpenAPIUtils} offers helper methods to support API communication with the controller
44  *
45  * @author Martin Raepple - Initial contribution
46  */
47 @NonNullByDefault
48 public class OpenAPIUtils {
49     private static final Logger LOGGER = LoggerFactory.getLogger(OpenAPIUtils.class);
50     private static final Pattern FIRMWARE_VERSION_PATTERN = Pattern.compile("(\\d+)\\.(\\d+)\\.(\\d+)");
51     private static final Pattern FIRMWARE_VERSION_PATTERN_BETA = Pattern.compile("(\\d+)\\.(\\d+)\\.(\\d+)-(\\d+)");
52     private static final long CONNECT_TIMEOUT = 10L;
53
54     public static Request requestBuilder(HttpClient httpClient, NanoleafControllerConfig controllerConfig,
55             String apiOperation, HttpMethod method) throws NanoleafException {
56         URI requestURI = getUri(controllerConfig, apiOperation, null);
57         LOGGER.trace("RequestBuilder: Sending Request {}:{} {} \n op: {}  method: {}", new Object[] {
58                 requestURI.getHost(), requestURI.getPort(), requestURI.getPath(), apiOperation, method.toString() });
59         return httpClient.newRequest(requestURI).method(method).timeout(CONNECT_TIMEOUT, TimeUnit.SECONDS);
60     }
61
62     public static URI getUri(NanoleafControllerConfig controllerConfig, String apiOperation, @Nullable String query)
63             throws NanoleafException {
64         String path;
65
66         // get network settings from configuration
67         String address = controllerConfig.address;
68         int port = controllerConfig.port;
69
70         if (API_ADD_USER.equals(apiOperation)) {
71             path = String.format("%s%s", API_V1_BASE_URL, apiOperation);
72         } else {
73             String authToken = controllerConfig.authToken;
74             if (authToken == null) {
75                 throw new NanoleafUnauthorizedException("No authentication token found in configuration");
76             }
77
78             path = String.format("%s/%s%s", API_V1_BASE_URL, authToken, apiOperation);
79         }
80
81         try {
82             URI requestURI = new URI(HttpScheme.HTTP.asString(), (String) null, address, port, path, query,
83                     (String) null);
84             return requestURI;
85         } catch (URISyntaxException var8) {
86             LOGGER.warn("URI could not be parsed with path {}", path);
87             throw new NanoleafException("Wrong URI format for API request");
88         }
89     }
90
91     public static ContentResponse sendOpenAPIRequest(Request request) throws NanoleafException {
92         try {
93             traceSendRequest(request);
94             ContentResponse openAPIResponse = request.send();
95             if (LOGGER.isTraceEnabled()) {
96                 LOGGER.trace("API response from Nanoleaf controller: {}", openAPIResponse.getContentAsString());
97             }
98             LOGGER.debug("API response code: {}", openAPIResponse.getStatus());
99             int responseStatus = openAPIResponse.getStatus();
100             if (responseStatus != HttpStatus.OK_200 && responseStatus != HttpStatus.NO_CONTENT_204) {
101                 if (openAPIResponse.getStatus() == HttpStatus.UNAUTHORIZED_401) {
102                     throw new NanoleafUnauthorizedException("OpenAPI request unauthorized");
103                 } else if (openAPIResponse.getStatus() == HttpStatus.NOT_FOUND_404) {
104                     throw new NanoleafNotFoundException("OpenAPI request did not get any result back");
105                 } else if (openAPIResponse.getStatus() == HttpStatus.BAD_REQUEST_400) {
106                     throw new NanoleafBadRequestException(
107                             String.format("Nanoleaf did not expect this request. HTTP response code %s",
108                                     openAPIResponse.getStatus()));
109                 } else {
110                     throw new NanoleafException(String.format("OpenAPI request failed. HTTP response code %s",
111                             openAPIResponse.getStatus()));
112                 }
113             } else {
114                 return openAPIResponse;
115             }
116         } catch (ExecutionException ee) {
117             Throwable cause = ee.getCause();
118             if (cause != null && cause instanceof HttpResponseException
119                     && ((HttpResponseException) cause).getResponse().getStatus() == HttpStatus.UNAUTHORIZED_401) {
120                 LOGGER.debug("OpenAPI request unauthorized. Invalid authorization token.");
121                 throw new NanoleafUnauthorizedException("Invalid authorization token");
122             } else {
123                 throw new NanoleafException("Failed to send OpenAPI request (final)", ee);
124             }
125         } catch (TimeoutException te) {
126             LOGGER.debug("OpenAPI request failed with timeout", te);
127             throw new NanoleafException("Failed to send OpenAPI request: Timeout", te);
128         } catch (InterruptedException ie) {
129             throw new NanoleafInterruptedException("OpenAPI request has been interrupted", ie);
130         }
131     }
132
133     private static void traceSendRequest(Request request) {
134         if (LOGGER.isTraceEnabled()) {
135             LOGGER.trace("Sending Request {} {}", request.getURI(),
136                     request.getQuery() == null ? "no query parameters" : request.getQuery());
137             LOGGER.trace("Request method:{} uri:{} params{}\n", request.getMethod(), request.getURI(),
138                     request.getParams());
139             if (request.getContent() != null) {
140                 Iterator<ByteBuffer> iter = request.getContent().iterator();
141                 while (iter.hasNext()) {
142                     ByteBuffer buffer = iter.next();
143                     LOGGER.trace("Content {}", StandardCharsets.UTF_8.decode(buffer).toString());
144                 }
145             }
146
147         }
148     }
149
150     public static boolean checkRequiredFirmware(@Nullable String modelId, @Nullable String currentFirmwareVersion) {
151         if (modelId != null && currentFirmwareVersion != null) {
152             int[] currentVer = getFirmwareVersionNumbers(currentFirmwareVersion);
153             int[] requiredVer = getFirmwareVersionNumbers("NL22".equals(modelId) ? "1.5.0" : "1.1.0");
154
155             for (int i = 0; i < currentVer.length; ++i) {
156                 if (currentVer[i] != requiredVer[i]) {
157                     return (currentVer[i] > requiredVer[i]);
158                 }
159             }
160
161             return true;
162         } else {
163             return false;
164         }
165     }
166
167     public static int[] getFirmwareVersionNumbers(String firmwareVersion) throws IllegalArgumentException {
168         LOGGER.debug("firmwareVersion: {}", firmwareVersion);
169         Matcher m = FIRMWARE_VERSION_PATTERN.matcher(firmwareVersion);
170         if (m.matches()) {
171             return new int[] { Integer.parseInt(m.group(1)), Integer.parseInt(m.group(2)),
172                     Integer.parseInt(m.group(3)) };
173         } else {
174             m = FIRMWARE_VERSION_PATTERN_BETA.matcher(firmwareVersion);
175             if (m.matches()) {
176                 return new int[] { Integer.parseInt(m.group(1)), Integer.parseInt(m.group(2)),
177                         Integer.parseInt(m.group(3)), Integer.parseInt(m.group(4)) };
178             } else {
179                 throw new IllegalArgumentException("Malformed controller firmware version " + firmwareVersion);
180             }
181         }
182     }
183 }