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