]> git.basschouten.com Git - openhab-addons.git/blob
37a68fabfd448533e3924faace250fdfd1990765
[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.ecowatt.internal.restapi;
14
15 import java.io.IOException;
16 import java.time.Instant;
17 import java.time.OffsetDateTime;
18 import java.time.ZonedDateTime;
19 import java.util.concurrent.ExecutionException;
20 import java.util.concurrent.TimeUnit;
21 import java.util.concurrent.TimeoutException;
22
23 import org.eclipse.jdt.annotation.NonNullByDefault;
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.binding.ecowatt.internal.exception.EcowattApiLimitException;
31 import org.openhab.core.auth.client.oauth2.AccessTokenResponse;
32 import org.openhab.core.auth.client.oauth2.OAuthClientService;
33 import org.openhab.core.auth.client.oauth2.OAuthException;
34 import org.openhab.core.auth.client.oauth2.OAuthFactory;
35 import org.openhab.core.auth.client.oauth2.OAuthResponseException;
36 import org.openhab.core.i18n.CommunicationException;
37 import org.slf4j.Logger;
38 import org.slf4j.LoggerFactory;
39
40 import com.google.gson.Gson;
41 import com.google.gson.GsonBuilder;
42 import com.google.gson.JsonDeserializer;
43 import com.google.gson.JsonSyntaxException;
44
45 /**
46  * The {@link EcowattRestApi} is responsible for handling all communication with the Ecowatt REST API
47  *
48  * @author Laurent Garnier - Initial contribution
49  * @author Laurent Garnier - Add support for different API versions
50  */
51 @NonNullByDefault
52 public class EcowattRestApi {
53
54     private static final String ECOWATT_API_TOKEN_URL = "https://digital.iservices.rte-france.com/token/oauth/";
55     private static final String ECOWATT_API_GET_SIGNALS_URL = "https://digital.iservices.rte-france.com/open_api/ecowatt/v%d/signals";
56
57     private final Logger logger = LoggerFactory.getLogger(EcowattRestApi.class);
58
59     private final OAuthFactory oAuthFactory;
60     private final HttpClient httpClient;
61     private final Gson gson;
62     private final String apiUrl;
63     private OAuthClientService authService;
64     private String authServiceHandle;
65
66     public EcowattRestApi(OAuthFactory oAuthFactory, HttpClient httpClient, String authServiceHandle, String idClient,
67             String idSecret, int apiVersion) {
68         this.oAuthFactory = oAuthFactory;
69         this.httpClient = httpClient;
70         GsonBuilder gsonBuilder = new GsonBuilder();
71         gson = gsonBuilder.registerTypeAdapter(ZonedDateTime.class,
72                 (JsonDeserializer<ZonedDateTime>) (json, type, jsonDeserializationContext) -> OffsetDateTime
73                         .parse(json.getAsJsonPrimitive().getAsString()).toZonedDateTime())
74                 .create();
75         this.authService = oAuthFactory.createOAuthClientService(authServiceHandle, ECOWATT_API_TOKEN_URL, null,
76                 idClient, idSecret, null, true);
77         this.authServiceHandle = authServiceHandle;
78         this.apiUrl = ECOWATT_API_GET_SIGNALS_URL.formatted(apiVersion);
79     }
80
81     public EcowattApiResponse getSignals() throws CommunicationException, EcowattApiLimitException {
82         logger.debug("API request {}", apiUrl);
83         String token = authenticate().getAccessToken();
84
85         final Request request = httpClient.newRequest(apiUrl).method(HttpMethod.GET)
86                 .header(HttpHeader.AUTHORIZATION, "Bearer " + token).timeout(10, TimeUnit.SECONDS);
87
88         ContentResponse response;
89         try {
90             response = request.send();
91         } catch (TimeoutException | ExecutionException e) {
92             throw new CommunicationException("@text/exception.api-request-failed", e);
93         } catch (InterruptedException e) {
94             Thread.currentThread().interrupt();
95             throw new CommunicationException("@text/exception.api-request-failed", e);
96         }
97
98         int statusCode = response.getStatus();
99
100         logger.trace("API response statusCode={} content={}", statusCode, response.getContentAsString());
101
102         if (statusCode == HttpStatus.TOO_MANY_REQUESTS_429) {
103             int retryAfter = -1;
104             if (response.getHeaders().contains(HttpHeader.RETRY_AFTER)) {
105                 try {
106                     retryAfter = Integer.parseInt(response.getHeaders().get(HttpHeader.RETRY_AFTER));
107                 } catch (NumberFormatException e) {
108                 }
109             }
110             throw new EcowattApiLimitException(retryAfter, "@text/exception.api-limit-reached");
111         } else if (statusCode != HttpStatus.OK_200) {
112             throw new CommunicationException("@text/exception.api-request-failed-params", statusCode,
113                     response.getContentAsString());
114         }
115
116         try {
117             EcowattApiResponse deserializedResp = gson.fromJson(response.getContentAsString(),
118                     EcowattApiResponse.class);
119             if (deserializedResp == null) {
120                 throw new CommunicationException("@text/exception.empty-api-response");
121             }
122             return deserializedResp;
123         } catch (JsonSyntaxException e) {
124             throw new CommunicationException("@text/exception.parsing-api-response-failed", e);
125         }
126     }
127
128     private AccessTokenResponse authenticate() throws CommunicationException {
129         try {
130             AccessTokenResponse result = authService.getAccessTokenResponse();
131             if (result == null || result.isExpired(Instant.now(), 120)) {
132                 logger.debug("Authentication required");
133                 result = authService.getAccessTokenByClientCredentials(null);
134             }
135             logger.debug("Token {} of type {} created on {} expiring after {} seconds", result.getAccessToken(),
136                     result.getTokenType(), result.getCreatedOn(), result.getExpiresIn());
137             return result;
138         } catch (OAuthException | IOException | OAuthResponseException e) {
139             throw new CommunicationException("@text/exception.authentication-failed", e);
140         }
141     }
142
143     public void dispose() {
144         oAuthFactory.ungetOAuthService(authServiceHandle);
145     }
146 }