]> git.basschouten.com Git - openhab-addons.git/blob
7da333721b3cf712b599237e7cfd15d0238e6ba8
[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  */
50 @NonNullByDefault
51 public class EcowattRestApi {
52
53     private static final String ECOWATT_API_TOKEN_URL = "https://digital.iservices.rte-france.com/token/oauth/";
54     private static final String ECOWATT_API_GET_SIGNALS_URL = "https://digital.iservices.rte-france.com/open_api/ecowatt/v4/signals";
55
56     private final Logger logger = LoggerFactory.getLogger(EcowattRestApi.class);
57
58     private final OAuthFactory oAuthFactory;
59     private final HttpClient httpClient;
60     private final Gson gson;
61     private OAuthClientService authService;
62     private String authServiceHandle;
63
64     public EcowattRestApi(OAuthFactory oAuthFactory, HttpClient httpClient, String authServiceHandle, String idClient,
65             String idSecret) {
66         this.oAuthFactory = oAuthFactory;
67         this.httpClient = httpClient;
68         GsonBuilder gsonBuilder = new GsonBuilder();
69         gson = gsonBuilder.registerTypeAdapter(ZonedDateTime.class,
70                 (JsonDeserializer<ZonedDateTime>) (json, type, jsonDeserializationContext) -> OffsetDateTime
71                         .parse(json.getAsJsonPrimitive().getAsString()).toZonedDateTime())
72                 .create();
73         this.authService = oAuthFactory.createOAuthClientService(authServiceHandle, ECOWATT_API_TOKEN_URL, null,
74                 idClient, idSecret, null, true);
75         this.authServiceHandle = authServiceHandle;
76     }
77
78     public EcowattApiResponse getSignals() throws CommunicationException, EcowattApiLimitException {
79         logger.debug("API request signals");
80         String token = authenticate().getAccessToken();
81
82         final Request request = httpClient.newRequest(ECOWATT_API_GET_SIGNALS_URL).method(HttpMethod.GET)
83                 .header(HttpHeader.AUTHORIZATION, "Bearer " + token).timeout(10, TimeUnit.SECONDS);
84
85         ContentResponse response;
86         try {
87             response = request.send();
88         } catch (TimeoutException | ExecutionException e) {
89             throw new CommunicationException("@text/exception.api-request-failed", e);
90         } catch (InterruptedException e) {
91             Thread.currentThread().interrupt();
92             throw new CommunicationException("@text/exception.api-request-failed", e);
93         }
94
95         int statusCode = response.getStatus();
96
97         logger.trace("API response statusCode={} content={}", statusCode, response.getContentAsString());
98
99         if (statusCode == HttpStatus.TOO_MANY_REQUESTS_429) {
100             int retryAfter = -1;
101             if (response.getHeaders().contains(HttpHeader.RETRY_AFTER)) {
102                 try {
103                     retryAfter = Integer.parseInt(response.getHeaders().get(HttpHeader.RETRY_AFTER));
104                 } catch (NumberFormatException e) {
105                 }
106             }
107             throw new EcowattApiLimitException(retryAfter, "@text/exception.api-limit-reached");
108         } else if (statusCode != HttpStatus.OK_200) {
109             throw new CommunicationException("@text/exception.api-request-failed-params", statusCode,
110                     response.getContentAsString());
111         }
112
113         try {
114             EcowattApiResponse deserializedResp = gson.fromJson(response.getContentAsString(),
115                     EcowattApiResponse.class);
116             if (deserializedResp == null) {
117                 throw new CommunicationException("@text/exception.empty-api-response");
118             }
119             return deserializedResp;
120         } catch (JsonSyntaxException e) {
121             throw new CommunicationException("@text/exception.parsing-api-response-failed", e);
122         }
123     }
124
125     private AccessTokenResponse authenticate() throws CommunicationException {
126         try {
127             AccessTokenResponse result = authService.getAccessTokenResponse();
128             if (result == null || result.isExpired(Instant.now(), 120)) {
129                 logger.debug("Authentication required");
130                 result = authService.getAccessTokenByClientCredentials(null);
131             }
132             logger.debug("Token {} of type {} created on {} expiring after {} seconds", result.getAccessToken(),
133                     result.getTokenType(), result.getCreatedOn(), result.getExpiresIn());
134             return result;
135         } catch (OAuthException | IOException | OAuthResponseException e) {
136             throw new CommunicationException("@text/exception.authentication-failed", e);
137         }
138     }
139
140     public void dispose() {
141         oAuthFactory.ungetOAuthService(authServiceHandle);
142     }
143 }