]> git.basschouten.com Git - openhab-addons.git/blob
1875d474f7afe924357344f9a01ecd0d3c1a01ad
[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.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     private static final Pattern FIRMWARE_VERSION_PATTERN_BETA = Pattern.compile("(\\d+)\\.(\\d+)\\.(\\d+)-(\\d+)");
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 {}:{} {} ", requestURI.getHost(), requestURI.getPort(),
58                 requestURI.getPath());
59
60         return httpClient.newRequest(requestURI).method(method);
61     }
62
63     public static URI getUri(NanoleafControllerConfig controllerConfig, String apiOperation, @Nullable String query)
64             throws NanoleafException {
65         String path;
66
67         // get network settings from configuration
68         String address = controllerConfig.address;
69         int port = controllerConfig.port;
70
71         if (API_ADD_USER.equals(apiOperation)) {
72             path = String.format("%s%s", API_V1_BASE_URL, apiOperation);
73         } else {
74             String authToken = controllerConfig.authToken;
75             if (authToken != null) {
76                 path = String.format("%s/%s%s", API_V1_BASE_URL, authToken, apiOperation);
77             } else {
78                 throw new NanoleafUnauthorizedException("No authentication token found in configuration");
79             }
80         }
81         URI requestURI;
82         try {
83             requestURI = new URI(HttpScheme.HTTP.asString(), null, address, port, path, query, null);
84         } catch (URISyntaxException use) {
85             LOGGER.warn("URI could not be parsed with path {}", path);
86             throw new NanoleafException("Wrong URI format for API request");
87         }
88         return requestURI;
89     }
90
91     public static ContentResponse sendOpenAPIRequest(Request request) throws NanoleafException {
92         try {
93             traceSendRequest(request);
94             ContentResponse openAPIResponse;
95             openAPIResponse = request.send();
96             if (LOGGER.isTraceEnabled()) {
97                 LOGGER.trace("API response from Nanoleaf controller: {}", openAPIResponse.getContentAsString());
98             }
99             LOGGER.debug("API response code: {}", openAPIResponse.getStatus());
100             int responseStatus = openAPIResponse.getStatus();
101             if (responseStatus == HttpStatus.OK_200 || responseStatus == HttpStatus.NO_CONTENT_204) {
102                 return openAPIResponse;
103             } else {
104                 if (openAPIResponse.getStatus() == HttpStatus.UNAUTHORIZED_401) {
105                     throw new NanoleafUnauthorizedException("OpenAPI request unauthorized");
106                 } else if (openAPIResponse.getStatus() == HttpStatus.NOT_FOUND_404) {
107                     throw new NanoleafNotFoundException("OpenAPI request did not get any result back");
108                 } else if (openAPIResponse.getStatus() == HttpStatus.BAD_REQUEST_400) {
109                     throw new NanoleafBadRequestException(
110                             String.format("Nanoleaf did not expect this request. HTTP response code %s",
111                                     openAPIResponse.getStatus()));
112                 } else {
113                     throw new NanoleafException(String.format("OpenAPI request failed. HTTP response code %s",
114                             openAPIResponse.getStatus()));
115                 }
116             }
117         } catch (ExecutionException | TimeoutException clientException) {
118             if (clientException.getCause() instanceof HttpResponseException
119                     && ((HttpResponseException) clientException.getCause()).getResponse()
120                             .getStatus() == HttpStatus.UNAUTHORIZED_401) {
121                 LOGGER.warn("OpenAPI request unauthorized. Invalid authorization token.");
122                 throw new NanoleafUnauthorizedException("Invalid authorization token");
123             }
124             throw new NanoleafException("Failed to send OpenAPI request", clientException);
125         } catch (InterruptedException interruptedException) {
126             throw new NanoleafInterruptedException("OpenAPI request has been interrupted", interruptedException);
127         }
128     }
129
130     private static void traceSendRequest(Request request) {
131         if (!LOGGER.isTraceEnabled()) {
132             return;
133         }
134         LOGGER.trace("Sending Request {} {}", request.getURI(),
135                 request.getQuery() == null ? "no query parameters" : request.getQuery());
136         LOGGER.trace("Request method:{} uri:{} params{}\n", request.getMethod(), request.getURI(), request.getParams());
137         if (request.getContent() != null) {
138             Iterator<ByteBuffer> iter = request.getContent().iterator();
139             if (iter != null) {
140                 while (iter.hasNext()) {
141                     @Nullable
142                     ByteBuffer buffer = iter.next();
143                     LOGGER.trace("Content {}", StandardCharsets.UTF_8.decode(buffer).toString());
144                 }
145             }
146         }
147     }
148
149     public static boolean checkRequiredFirmware(@Nullable String modelId, @Nullable String currentFirmwareVersion) {
150         if (modelId == null || currentFirmwareVersion == null) {
151             return false;
152         }
153         int[] currentVer = getFirmwareVersionNumbers(currentFirmwareVersion);
154
155         int[] requiredVer = getFirmwareVersionNumbers(
156                 MODEL_ID_LIGHTPANELS.equals(modelId) ? API_MIN_FW_VER_LIGHTPANELS : API_MIN_FW_VER_CANVAS);
157
158         for (int i = 0; i < currentVer.length; i++) {
159             if (currentVer[i] != requiredVer[i]) {
160                 return currentVer[i] > requiredVer[i];
161             }
162         }
163         return true;
164     }
165
166     public static int[] getFirmwareVersionNumbers(String firmwareVersion) throws IllegalArgumentException {
167         LOGGER.debug("firmwareVersion: {}", firmwareVersion);
168         Matcher m = FIRMWARE_VERSION_PATTERN.matcher(firmwareVersion);
169
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 }