]> git.basschouten.com Git - openhab-addons.git/blob
f67fa58324e6a093c6051323abe2b9286fbf4146
[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.warmup.internal.api;
14
15 import java.util.concurrent.ExecutionException;
16 import java.util.concurrent.TimeUnit;
17 import java.util.concurrent.TimeoutException;
18
19 import org.eclipse.jdt.annotation.NonNullByDefault;
20 import org.eclipse.jdt.annotation.Nullable;
21 import org.eclipse.jetty.client.HttpClient;
22 import org.eclipse.jetty.client.api.ContentResponse;
23 import org.eclipse.jetty.client.api.Request;
24 import org.eclipse.jetty.client.util.StringContentProvider;
25 import org.eclipse.jetty.http.HttpHeader;
26 import org.eclipse.jetty.http.HttpMethod;
27 import org.eclipse.jetty.http.HttpStatus;
28 import org.openhab.binding.warmup.internal.WarmupBindingConstants;
29 import org.openhab.binding.warmup.internal.handler.MyWarmupConfigurationDTO;
30 import org.openhab.binding.warmup.internal.model.auth.AuthRequestDTO;
31 import org.openhab.binding.warmup.internal.model.auth.AuthResponseDTO;
32 import org.openhab.binding.warmup.internal.model.query.QueryResponseDTO;
33 import org.openhab.core.library.types.OnOffType;
34 import org.slf4j.Logger;
35 import org.slf4j.LoggerFactory;
36
37 import com.google.gson.Gson;
38
39 /**
40  * The {@link MyWarmupApi} class contains code specific to calling the My Warmup API.
41  *
42  * @author James Melville - Initial contribution
43  */
44 @NonNullByDefault
45 public class MyWarmupApi {
46
47     private static final Gson GSON = new Gson();
48
49     private final Logger logger = LoggerFactory.getLogger(MyWarmupApi.class);
50     private final HttpClient httpClient;
51
52     private MyWarmupConfigurationDTO configuration;
53     private @Nullable String authToken;
54
55     /**
56      * Construct the API client
57      *
58      * @param httpClient HttpClient to make HTTP Calls
59      * @param configuration Thing configuration which contains API credentials
60      */
61     public MyWarmupApi(final HttpClient httpClient, MyWarmupConfigurationDTO configuration) {
62         this.httpClient = httpClient;
63         this.configuration = configuration;
64     }
65
66     /**
67      * Update the configuration, trigger a refresh of the access token
68      *
69      * @param configuration contains username and password
70      */
71     public void setConfiguration(MyWarmupConfigurationDTO configuration) {
72         authToken = null;
73         this.configuration = configuration;
74     }
75
76     private void validateSession() throws MyWarmupApiException {
77         if (authToken == null) {
78             authenticate();
79         }
80     }
81
82     private void authenticate() throws MyWarmupApiException {
83         String body = GSON.toJson(new AuthRequestDTO(configuration.username, configuration.password,
84                 WarmupBindingConstants.AUTH_METHOD, WarmupBindingConstants.AUTH_APP_ID));
85
86         ContentResponse response = callWarmup(WarmupBindingConstants.APP_ENDPOINT, body, false);
87
88         AuthResponseDTO ar = GSON.fromJson(response.getContentAsString(), AuthResponseDTO.class);
89
90         if (ar != null && ar.getStatus() != null && ar.getStatus().getResult().equals("success")) {
91             authToken = ar.getResponse().getToken();
92         } else {
93             throw new MyWarmupApiException("Authentication Failed");
94         }
95     }
96
97     /**
98      * Query the API to get the status of all devices connected to the Bridge.
99      *
100      * @return The {@link QueryResponseDTO} object if retrieved, else null
101      * @throws MyWarmupApiException API callout error
102      */
103     public synchronized QueryResponseDTO getStatus() throws MyWarmupApiException {
104         return callWarmupGraphQL("query QUERY { user { locations{ id name "
105                 + " rooms { id roomName runMode overrideDur targetTemp currentTemp "
106                 + " thermostat4ies{ deviceSN lastPoll }}}}}");
107     }
108
109     /**
110      * Call the API to set a temperature override on a specific room
111      *
112      * @param locationId Id of the location
113      * @param roomId Id of the room
114      * @param temperature Temperature to set * 10
115      * @param duration Duration in minutes of the override
116      * @throws MyWarmupApiException API callout error
117      */
118     public void setOverride(String locationId, String roomId, int temperature, Integer duration)
119             throws MyWarmupApiException {
120         callWarmupGraphQL(String.format("mutation{deviceOverride(lid:%s,rid:%s,temperature:%d,minutes:%d)}", locationId,
121                 roomId, temperature, duration));
122     }
123
124     /**
125      * Call the API to toggle frost protection mode on a specific room
126      *
127      * @param locationId Id of the location
128      * @param roomId Id of the room
129      * @param command Temperature to set
130      * @throws MyWarmupApiException API callout error
131      */
132     public void toggleFrostProtectionMode(String locationId, String roomId, OnOffType command)
133             throws MyWarmupApiException {
134         callWarmupGraphQL(String.format("mutation{turn%s(lid:%s,rid:%s){id}}", command == OnOffType.ON ? "Off" : "On",
135                 locationId, roomId));
136     }
137
138     private QueryResponseDTO callWarmupGraphQL(String body) throws MyWarmupApiException {
139         validateSession();
140         ContentResponse response = callWarmup(WarmupBindingConstants.QUERY_ENDPOINT, "{\"query\": \"" + body + "\"}",
141                 true);
142
143         QueryResponseDTO qr = GSON.fromJson(response.getContentAsString(), QueryResponseDTO.class);
144
145         if (qr != null && qr.getStatus().equals("success")) {
146             return qr;
147         } else {
148             throw new MyWarmupApiException("Unexpected reponse from API");
149         }
150     }
151
152     private synchronized ContentResponse callWarmup(String endpoint, String body, Boolean authenticated)
153             throws MyWarmupApiException {
154         try {
155             final Request request = httpClient.newRequest(endpoint);
156
157             request.method(HttpMethod.POST);
158
159             request.getHeaders().remove(HttpHeader.USER_AGENT);
160             request.header(HttpHeader.USER_AGENT, WarmupBindingConstants.USER_AGENT);
161             request.header(HttpHeader.CONTENT_TYPE, "application/json");
162             request.header("App-Token", WarmupBindingConstants.APP_TOKEN);
163             if (authenticated) {
164                 request.header("Warmup-Authorization", authToken);
165             }
166
167             request.content(new StringContentProvider(body));
168
169             request.timeout(10, TimeUnit.SECONDS);
170
171             logger.trace("Sending body to My Warmup: Endpoint {}, Body {}", endpoint, body);
172             ContentResponse response = request.send();
173             logger.trace("Response from my warmup: Status {}, Body {}", response.getStatus(),
174                     response.getContentAsString());
175
176             if (response.getStatus() == HttpStatus.OK_200) {
177                 return response;
178             } else if (response.getStatus() == HttpStatus.UNAUTHORIZED_401) {
179                 logger.debug("Authentication failure {} {}", response.getStatus(), response.getContentAsString());
180                 authToken = null;
181                 throw new MyWarmupApiException("Authentication failure");
182             } else {
183                 logger.debug("Unexpected response {} {}", response.getStatus(), response.getContentAsString());
184             }
185             throw new MyWarmupApiException("Callout failed");
186         } catch (InterruptedException | TimeoutException | ExecutionException e) {
187             throw new MyWarmupApiException(e);
188         }
189     }
190 }