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