]> git.basschouten.com Git - openhab-addons.git/blob
61f44f742b35e2e7d2641eebe2a7fcbb58ed0102
[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.draytonwiser.internal.api;
14
15 import static org.openhab.binding.draytonwiser.internal.DraytonWiserBindingConstants.*;
16
17 import java.util.concurrent.ExecutionException;
18 import java.util.concurrent.TimeUnit;
19 import java.util.concurrent.TimeoutException;
20
21 import org.eclipse.jdt.annotation.NonNullByDefault;
22 import org.eclipse.jdt.annotation.Nullable;
23 import org.eclipse.jetty.client.HttpClient;
24 import org.eclipse.jetty.client.api.ContentResponse;
25 import org.eclipse.jetty.client.util.StringContentProvider;
26 import org.eclipse.jetty.http.HttpMethod;
27 import org.eclipse.jetty.http.HttpStatus;
28 import org.openhab.binding.draytonwiser.internal.handler.HeatHubConfiguration;
29 import org.openhab.binding.draytonwiser.internal.model.DomainDTO;
30 import org.openhab.binding.draytonwiser.internal.model.StationDTO;
31 import org.slf4j.Logger;
32 import org.slf4j.LoggerFactory;
33
34 import com.google.gson.FieldNamingPolicy;
35 import com.google.gson.Gson;
36 import com.google.gson.GsonBuilder;
37 import com.google.gson.JsonSyntaxException;
38
39 /**
40  * Class with api specific call code.
41  *
42  * @author Andrew Schofield - Initial contribution
43  * @author Hilbrand Bouwkamp - Moved Api specific code to it's own class
44  */
45 @NonNullByDefault
46 public class DraytonWiserApi {
47
48     public static final Gson GSON = new GsonBuilder().setFieldNamingStrategy(FieldNamingPolicy.UPPER_CAMEL_CASE)
49             .create();
50
51     private final Logger logger = LoggerFactory.getLogger(DraytonWiserApi.class);
52     private final HttpClient httpClient;
53
54     private HeatHubConfiguration configuration = new HeatHubConfiguration();
55     private int failCount;
56
57     public DraytonWiserApi(final HttpClient httpClient) {
58         this.httpClient = httpClient;
59     }
60
61     public void setConfiguration(final HeatHubConfiguration configuration) {
62         this.configuration = configuration;
63     }
64
65     public @Nullable StationDTO getStation() throws DraytonWiserApiException {
66         final ContentResponse response = sendMessageToHeatHub(STATION_ENDPOINT, HttpMethod.GET);
67
68         return response == null ? null : GSON.fromJson(response.getContentAsString(), StationDTO.class);
69     }
70
71     public @Nullable DomainDTO getDomain() throws DraytonWiserApiException {
72         final ContentResponse response = sendMessageToHeatHub(DOMAIN_ENDPOINT, HttpMethod.GET);
73
74         if (response == null) {
75             return null;
76         }
77
78         try {
79             return GSON.fromJson(response.getContentAsString(), DomainDTO.class);
80         } catch (final JsonSyntaxException e) {
81             logger.debug("Could not parse Json content: {}", e.getMessage(), e);
82             return null;
83         }
84     }
85
86     public void setRoomSetPoint(final int roomId, final int setPoint) throws DraytonWiserApiException {
87         final String payload = "{\"RequestOverride\":{\"Type\":\"Manual\", \"SetPoint\":" + setPoint + "}}";
88
89         sendMessageToHeatHub(ROOMS_ENDPOINT + roomId, "PATCH", payload);
90     }
91
92     public void setRoomManualMode(final int roomId, final boolean manualMode) throws DraytonWiserApiException {
93         String payload = "{\"Mode\":\"" + (manualMode ? "Manual" : "Auto") + "\"}";
94         sendMessageToHeatHub(ROOMS_ENDPOINT + roomId, "PATCH", payload);
95         payload = "{\"RequestOverride\":{\"Type\":\"None\",\"Originator\" :\"App\",\"DurationMinutes\":0,\"SetPoint\":0}}";
96         sendMessageToHeatHub(ROOMS_ENDPOINT + roomId, "PATCH", payload);
97     }
98
99     public void setRoomWindowStateDetection(final int roomId, final boolean windowStateDetection)
100             throws DraytonWiserApiException {
101         final String payload = windowStateDetection ? "true" : "false";
102         sendMessageToHeatHub(ROOMS_ENDPOINT + roomId + "/WindowDetectionActive", "PATCH", payload);
103     }
104
105     public void setRoomBoostActive(final int roomId, final int setPoint, final int duration)
106             throws DraytonWiserApiException {
107         final String payload = "{\"RequestOverride\":{\"Type\":\"Manual\",\"Originator\" :\"App\",\"DurationMinutes\":"
108                 + duration + ",\"SetPoint\":" + setPoint + "}}";
109         sendMessageToHeatHub(ROOMS_ENDPOINT + roomId, "PATCH", payload);
110     }
111
112     public void setRoomBoostInactive(final int roomId) throws DraytonWiserApiException {
113         final String payload = "{\"RequestOverride\":{\"Type\":\"None\",\"Originator\" :\"App\",\"DurationMinutes\":0,\"SetPoint\":0}}";
114         sendMessageToHeatHub(ROOMS_ENDPOINT + roomId, "PATCH", payload);
115     }
116
117     public void setHotWaterManualMode(final boolean manualMode) throws DraytonWiserApiException {
118         String payload = "{\"Mode\":\"" + (manualMode ? "Manual" : "Auto") + "\"}";
119         sendMessageToHeatHub(HOTWATER_ENDPOINT + "2", "PATCH", payload);
120         payload = "{\"RequestOverride\":{\"Type\":\"None\",\"Originator\" :\"App\",\"DurationMinutes\":0,\"SetPoint\":0}}";
121         sendMessageToHeatHub(HOTWATER_ENDPOINT + "2", "PATCH", payload);
122     }
123
124     public void setHotWaterSetPoint(final int setPoint) throws DraytonWiserApiException {
125         final String payload = "{\"RequestOverride\":{\"Type\":\"Manual\", \"SetPoint\":" + setPoint + "}}";
126         sendMessageToHeatHub(HOTWATER_ENDPOINT + "2", "PATCH", payload);
127     }
128
129     public void setHotWaterBoostActive(final int duration) throws DraytonWiserApiException {
130         final String payload = "{\"RequestOverride\":{\"Type\":\"Manual\",\"Originator\" :\"App\",\"DurationMinutes\":"
131                 + duration + ",\"SetPoint\":1100}}";
132         sendMessageToHeatHub(HOTWATER_ENDPOINT + "2", "PATCH", payload);
133     }
134
135     public void setHotWaterBoostInactive() throws DraytonWiserApiException {
136         final String payload = "{\"RequestOverride\":{\"Type\":\"None\",\"Originator\" :\"App\",\"DurationMinutes\":0,\"SetPoint\":0}}";
137         sendMessageToHeatHub(HOTWATER_ENDPOINT + "2", "PATCH", payload);
138     }
139
140     public void setAwayMode(final boolean awayMode) throws DraytonWiserApiException {
141         final int setPoint = configuration.awaySetPoint * 10;
142
143         String payload = "{\"Type\":" + (awayMode ? "2" : "0") + ", \"setPoint\":" + (awayMode ? setPoint : "0") + "}";
144         sendMessageToHeatHub(SYSTEM_ENDPOINT + "RequestOverride", "PATCH", payload);
145         payload = "{\"Type\":" + (awayMode ? "2" : "0") + ", \"setPoint\":" + (awayMode ? "-200" : "0") + "}";
146         sendMessageToHeatHub(HOTWATER_ENDPOINT + "2/RequestOverride", "PATCH", payload);
147     }
148
149     public void setDeviceLocked(final int deviceId, final boolean locked) throws DraytonWiserApiException {
150         final String payload = locked ? "true" : "false";
151         sendMessageToHeatHub(DEVICE_ENDPOINT + deviceId + "/DeviceLockEnabled", "PATCH", payload);
152     }
153
154     public void setEcoMode(final boolean ecoMode) throws DraytonWiserApiException {
155         final String payload = "{\"EcoModeEnabled\":" + ecoMode + "}";
156         sendMessageToHeatHub(SYSTEM_ENDPOINT, "PATCH", payload);
157     }
158
159     public void setSmartPlugManualMode(final int id, final boolean manualMode) throws DraytonWiserApiException {
160         final String payload = "{\"Mode\":\"" + (manualMode ? "Manual" : "Auto") + "\"}";
161         sendMessageToHeatHub(SMARTPLUG_ENDPOINT + id, "PATCH", payload);
162     }
163
164     public void setSmartPlugOutputState(final int id, final boolean outputState) throws DraytonWiserApiException {
165         final String payload = "{\"RequestOutput\":\"" + (outputState ? "On" : "Off") + "\"}";
166         sendMessageToHeatHub(SMARTPLUG_ENDPOINT + id, "PATCH", payload);
167     }
168
169     public void setSmartPlugAwayAction(final int id, final boolean awayAction) throws DraytonWiserApiException {
170         final String payload = "{\"AwayAction\":\"" + (awayAction ? "Off" : "NoChange") + "\"}";
171         sendMessageToHeatHub(SMARTPLUG_ENDPOINT + id, "PATCH", payload);
172     }
173
174     public void setComfortMode(final boolean comfortMode) throws DraytonWiserApiException {
175         final String payload = "{\"ComfortModeEnabled\":" + comfortMode + "}";
176         sendMessageToHeatHub(SYSTEM_ENDPOINT, "PATCH", payload);
177     }
178
179     private synchronized @Nullable ContentResponse sendMessageToHeatHub(final String path, final HttpMethod method)
180             throws DraytonWiserApiException {
181         return sendMessageToHeatHub(path, method.asString(), "");
182     }
183
184     private synchronized @Nullable ContentResponse sendMessageToHeatHub(final String path, final String method,
185             final String content) throws DraytonWiserApiException {
186         // we need to keep track of the number of times that the heat hub has "failed" to respond.
187         // we only actually report a failure if we hit an error state 3 or more times
188         try {
189             logger.debug("Sending message to heathub: {}", path);
190             final StringContentProvider contentProvider = new StringContentProvider(content);
191             final ContentResponse response = httpClient
192                     .newRequest("http://" + configuration.networkAddress + "/" + path).method(method)
193                     .header("SECRET", configuration.secret).content(contentProvider).timeout(10, TimeUnit.SECONDS)
194                     .send();
195
196             if (logger.isTraceEnabled()) {
197                 logger.trace("Reponse (Status:{}): {}", response.getStatus(), response.getContentAsString());
198             }
199             if (response.getStatus() == HttpStatus.OK_200) {
200                 failCount = 0;
201                 return response;
202             } else if (response.getStatus() == HttpStatus.UNAUTHORIZED_401) {
203                 failCount++;
204                 if (failCount > 2) {
205                     throw new DraytonWiserApiException("Invalid authorization token");
206                 }
207             } else {
208                 failCount++;
209                 if (failCount > 2) {
210                     throw new DraytonWiserApiException("Heathub didn't repond after " + failCount + " retries");
211                 }
212             }
213         } catch (final TimeoutException e) {
214             failCount++;
215             if (failCount > 2) {
216                 logger.debug("Heathub didn't repond in time: {}", e.getMessage());
217                 throw new DraytonWiserApiException("Heathub didn't repond in time", e);
218             }
219         } catch (final InterruptedException e) {
220             Thread.currentThread().interrupt();
221         } catch (final ExecutionException e) {
222             logger.debug("Execution Exception: {}", e.getMessage(), e);
223             throw new DraytonWiserApiException(e.getMessage(), e);
224         } catch (final RuntimeException e) {
225             logger.debug("Unexpected error: {}", e.getMessage(), e);
226             throw new DraytonWiserApiException(e.getMessage(), e);
227         }
228         return null;
229     }
230 }