]> git.basschouten.com Git - openhab-addons.git/blob
a818add11f67a6957e5eb68c93796a20b7bcc894
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2024 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 && "success".equals(ar.getStatus().getResult())) {
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("""
105                 query QUERY { user { locations{ id name \
106                  rooms { id roomName runMode overrideDur targetTemp currentTemp \
107                  thermostat4ies{ deviceSN lastPoll }}}}}\
108                 """);
109     }
110
111     /**
112      * Call the API to set a temperature override on a specific room
113      *
114      * @param locationId Id of the location
115      * @param roomId Id of the room
116      * @param temperature Temperature to set * 10
117      * @param duration Duration in minutes of the override
118      * @throws MyWarmupApiException API callout error
119      */
120     public void setOverride(String locationId, String roomId, int temperature, Integer duration)
121             throws MyWarmupApiException {
122         callWarmupGraphQL(String.format("mutation{deviceOverride(lid:%s,rid:%s,temperature:%d,minutes:%d)}", locationId,
123                 roomId, temperature, duration));
124     }
125
126     /**
127      * Call the API to toggle frost protection mode on a specific room
128      *
129      * @param locationId Id of the location
130      * @param roomId Id of the room
131      * @param command Temperature to set
132      * @throws MyWarmupApiException API callout error
133      */
134     public void toggleFrostProtectionMode(String locationId, String roomId, OnOffType command)
135             throws MyWarmupApiException {
136         callWarmupGraphQL(String.format("mutation{turn%s(lid:%s,rid:%s){id}}", command == OnOffType.ON ? "Off" : "On",
137                 locationId, roomId));
138     }
139
140     private QueryResponseDTO callWarmupGraphQL(String body) throws MyWarmupApiException {
141         validateSession();
142         ContentResponse response = callWarmup(WarmupBindingConstants.QUERY_ENDPOINT, "{\"query\": \"" + body + "\"}",
143                 true);
144
145         QueryResponseDTO qr = GSON.fromJson(response.getContentAsString(), QueryResponseDTO.class);
146
147         if (qr != null && "success".equals(qr.getStatus())) {
148             return qr;
149         } else {
150             throw new MyWarmupApiException("Unexpected reponse from API");
151         }
152     }
153
154     private synchronized ContentResponse callWarmup(String endpoint, String body, Boolean authenticated)
155             throws MyWarmupApiException {
156         try {
157             final Request request = httpClient.newRequest(endpoint);
158
159             request.method(HttpMethod.POST);
160
161             request.getHeaders().remove(HttpHeader.USER_AGENT);
162             request.header(HttpHeader.USER_AGENT, WarmupBindingConstants.USER_AGENT);
163             request.header(HttpHeader.CONTENT_TYPE, "application/json");
164             request.header("App-Token", WarmupBindingConstants.APP_TOKEN);
165             if (authenticated) {
166                 request.header("Warmup-Authorization", authToken);
167             }
168
169             request.content(new StringContentProvider(body));
170
171             request.timeout(10, TimeUnit.SECONDS);
172
173             logger.trace("Sending body to My Warmup: Endpoint {}, Body {}", endpoint, body);
174             ContentResponse response = request.send();
175             logger.trace("Response from my warmup: Status {}, Body {}", response.getStatus(),
176                     response.getContentAsString());
177
178             if (response.getStatus() == HttpStatus.OK_200) {
179                 return response;
180             } else if (response.getStatus() == HttpStatus.UNAUTHORIZED_401) {
181                 logger.debug("Authentication failure {} {}", response.getStatus(), response.getContentAsString());
182                 authToken = null;
183                 throw new MyWarmupApiException("Authentication failure");
184             } else {
185                 logger.debug("Unexpected response {} {}", response.getStatus(), response.getContentAsString());
186             }
187             throw new MyWarmupApiException("Callout failed");
188         } catch (InterruptedException | TimeoutException | ExecutionException e) {
189             throw new MyWarmupApiException(e);
190         }
191     }
192 }