]> git.basschouten.com Git - openhab-addons.git/blob
9c7b14d9aa6a6e8c09b90b21b6bdb458ae5a9784
[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.somneo.internal;
14
15 import static org.openhab.binding.somneo.internal.SomneoBindingConstants.*;
16
17 import java.io.UnsupportedEncodingException;
18 import java.nio.charset.StandardCharsets;
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.jdt.annotation.Nullable;
25 import org.eclipse.jetty.client.HttpClient;
26 import org.eclipse.jetty.client.api.ContentResponse;
27 import org.eclipse.jetty.client.api.Request;
28 import org.eclipse.jetty.client.util.StringContentProvider;
29 import org.eclipse.jetty.http.HttpMethod;
30 import org.eclipse.jetty.http.HttpStatus;
31 import org.openhab.binding.somneo.internal.model.AudioData;
32 import org.openhab.binding.somneo.internal.model.DeviceData;
33 import org.openhab.binding.somneo.internal.model.FirmwareData;
34 import org.openhab.binding.somneo.internal.model.LightData;
35 import org.openhab.binding.somneo.internal.model.PresetData;
36 import org.openhab.binding.somneo.internal.model.RadioData;
37 import org.openhab.binding.somneo.internal.model.RelaxData;
38 import org.openhab.binding.somneo.internal.model.SensorData;
39 import org.openhab.binding.somneo.internal.model.SunsetData;
40 import org.openhab.binding.somneo.internal.model.TimerData;
41 import org.openhab.binding.somneo.internal.model.WifiData;
42 import org.openhab.core.io.net.http.HttpUtil;
43 import org.slf4j.Logger;
44 import org.slf4j.LoggerFactory;
45
46 import com.google.gson.Gson;
47
48 /**
49  * The {@link SomneoHttpConnector} is responsible for sending commands.
50  *
51  * @author Michael Myrcik - Initial contribution
52  */
53 @NonNullByDefault
54 public class SomneoHttpConnector {
55
56     private Logger logger = LoggerFactory.getLogger(SomneoHttpConnector.class);
57
58     private static final int REQUEST_TIMEOUT_MS = 5000;
59
60     private static final String DEFAULT_CONTENT_TYPE = "application/json";
61
62     private final Gson gson = new Gson();
63
64     private final HttpClient httpClient;
65
66     private final String urlBase;
67
68     public SomneoHttpConnector(SomneoConfiguration config, HttpClient httpClient) {
69         this.httpClient = httpClient;
70         this.urlBase = String.format("https://%s:%d/di/v1/products", config.hostname, config.port);
71     }
72
73     public SensorData fetchSensorData() throws TimeoutException, InterruptedException, ExecutionException {
74         return executeUrl("GET", SENSORS_ENDPOINT, SensorData.class);
75     }
76
77     public LightData fetchLightData() throws TimeoutException, InterruptedException, ExecutionException {
78         return executeUrl("GET", LIGHT_ENDPOINT, LightData.class);
79     }
80
81     public SunsetData fetchSunsetData() throws TimeoutException, InterruptedException, ExecutionException {
82         return executeUrl("GET", SUNSET_ENDPOINT, SunsetData.class);
83     }
84
85     public void switchMainLight(boolean state) throws TimeoutException, InterruptedException, ExecutionException {
86         final LightData data = new LightData();
87         data.setMainLight(state);
88         data.setNightLight(false);
89         data.setPreviewLight(false);
90
91         executeUrl("PUT", LIGHT_ENDPOINT, data);
92     }
93
94     public void setMainLightDimmer(int level) throws TimeoutException, InterruptedException, ExecutionException {
95         final LightData data = new LightData();
96         data.setMainLightLevel(level);
97         data.setMainLight(true);
98         data.setNightLight(false);
99         data.setPreviewLight(false);
100
101         executeUrl("PUT", LIGHT_ENDPOINT, data);
102     }
103
104     public void switchNightLight(boolean state) throws TimeoutException, InterruptedException, ExecutionException {
105         final LightData data = new LightData();
106         data.setMainLight(false);
107         data.setNightLight(state);
108         data.setPreviewLight(false);
109
110         executeUrl("PUT", LIGHT_ENDPOINT, data);
111     }
112
113     public void switchSunsetProgram(boolean state) throws TimeoutException, InterruptedException, ExecutionException {
114         final SunsetData data = new SunsetData();
115         data.setState(state);
116
117         executeUrl("PUT", SUNSET_ENDPOINT, data);
118     }
119
120     public void setSunsetLightIntensity(int percent) throws TimeoutException, InterruptedException, ExecutionException {
121         final SunsetData data = new SunsetData();
122         data.setLightIntensity(percent);
123
124         executeUrl("PUT", SUNSET_ENDPOINT, data);
125     }
126
127     public void setSunsetDuration(int duration) throws TimeoutException, InterruptedException, ExecutionException {
128         final SunsetData data = new SunsetData();
129         data.setDurationInMin(duration);
130
131         executeUrl("PUT", SUNSET_ENDPOINT, data);
132     }
133
134     public void setSunsetColorSchema(int value) throws TimeoutException, InterruptedException, ExecutionException {
135         final SunsetData data = new SunsetData();
136         data.setColorSchema(value);
137
138         executeUrl("PUT", SUNSET_ENDPOINT, data);
139     }
140
141     public void setSunsetAmbientNoise(String option) throws TimeoutException, InterruptedException, ExecutionException {
142         final SunsetData data = new SunsetData();
143         data.setAmbientNoise(option);
144
145         executeUrl("PUT", SUNSET_ENDPOINT, data);
146     }
147
148     public void setSunsetVolume(int percent) throws TimeoutException, InterruptedException, ExecutionException {
149         final SunsetData data = new SunsetData();
150         data.setSoundVolume(percent);
151
152         executeUrl("PUT", SUNSET_ENDPOINT, data);
153     }
154
155     public RelaxData fetchRelaxData() throws TimeoutException, InterruptedException, ExecutionException {
156         return executeUrl("GET", RELAX_ENDPOINT, RelaxData.class);
157     }
158
159     public void setRelaxVolume(int percent) throws TimeoutException, InterruptedException, ExecutionException {
160         final RelaxData data = new RelaxData();
161         data.setSoundVolume(percent);
162
163         executeUrl("PUT", RELAX_ENDPOINT, data);
164     }
165
166     public void setRelaxLightIntensity(int percent) throws TimeoutException, InterruptedException, ExecutionException {
167         final RelaxData data = new RelaxData();
168         data.setLightIntensity(percent);
169
170         executeUrl("PUT", RELAX_ENDPOINT, data);
171     }
172
173     public void switchRelaxProgram(boolean state) throws TimeoutException, InterruptedException, ExecutionException {
174         final RelaxData data = new RelaxData();
175         data.setState(state);
176
177         executeUrl("PUT", RELAX_ENDPOINT, data);
178     }
179
180     public void setRelaxBreathingRate(int value) throws TimeoutException, InterruptedException, ExecutionException {
181         final RelaxData data = new RelaxData();
182         data.setBreathingRate(value);
183
184         executeUrl("PUT", RELAX_ENDPOINT, data);
185     }
186
187     public void setRelaxDuration(int value) throws TimeoutException, InterruptedException, ExecutionException {
188         final RelaxData data = new RelaxData();
189         data.setDurationInMin(value);
190
191         executeUrl("PUT", RELAX_ENDPOINT, data);
192     }
193
194     public void setRelaxGuidanceType(int value) throws TimeoutException, InterruptedException, ExecutionException {
195         final RelaxData data = new RelaxData();
196         data.setGuidanceType(value);
197
198         executeUrl("PUT", RELAX_ENDPOINT, data);
199     }
200
201     public AudioData fetchAudioData() throws TimeoutException, InterruptedException, ExecutionException {
202         return executeUrl("GET", AUDIO_ENDPOINT, AudioData.class);
203     }
204
205     public void switchRadio(boolean state) throws TimeoutException, InterruptedException, ExecutionException {
206         final AudioData data = new AudioData();
207         if (state) {
208             data.enableRadio();
209         } else {
210             data.disableAudio();
211         }
212
213         executeUrl("PUT", AUDIO_ENDPOINT, data);
214     }
215
216     public void switchAux(boolean state) throws TimeoutException, InterruptedException, ExecutionException {
217         final AudioData data = new AudioData();
218         if (state) {
219             data.enableAux();
220         } else {
221             data.disableAudio();
222         }
223
224         executeUrl("PUT", AUDIO_ENDPOINT, data);
225     }
226
227     public void setAudioVolume(int percent) throws TimeoutException, InterruptedException, ExecutionException {
228         final AudioData data = new AudioData();
229         data.setVolume(percent);
230
231         executeUrl("PUT", AUDIO_ENDPOINT, data);
232     }
233
234     public void setRadioChannel(String preset) throws TimeoutException, InterruptedException, ExecutionException {
235         final AudioData data = new AudioData();
236         data.enableRadio();
237         data.setRadioPreset(preset);
238
239         executeUrl("PUT", AUDIO_ENDPOINT, data);
240     }
241
242     public RadioData getRadioData() throws TimeoutException, InterruptedException, ExecutionException {
243         RadioData data = new RadioData();
244         int loops = 0;
245         do {
246             if (loops > 20) {
247                 break;
248             }
249             if (loops > 0) {
250                 loops++;
251                 Thread.sleep(250);
252             }
253             data = executeUrl("GET", RADIO_ENDPOINT, RadioData.class);
254         } while (data.isSeeking()); // Wait until seek is finished
255
256         return data;
257     }
258
259     public void radioSeekUp() throws TimeoutException, InterruptedException, ExecutionException {
260         final RadioData data = new RadioData();
261         data.setCmdSeekUp();
262
263         executeUrl("PUT", RADIO_ENDPOINT, data);
264     }
265
266     public void radioSeekDown() throws TimeoutException, InterruptedException, ExecutionException {
267         final RadioData data = new RadioData();
268         data.setCmdSeekDown();
269
270         executeUrl("PUT", RADIO_ENDPOINT, data);
271     }
272
273     public DeviceData fetchDeviceData() throws TimeoutException, InterruptedException, ExecutionException {
274         return executeUrl("GET", DEVICE_ENDPOINT, DeviceData.class);
275     }
276
277     public WifiData fetchWifiData() throws TimeoutException, InterruptedException, ExecutionException {
278         return executeUrl("GET", WIFI_ENDPOINT, WifiData.class);
279     }
280
281     public FirmwareData fetchFirmwareData() throws TimeoutException, InterruptedException, ExecutionException {
282         return executeUrl("GET", FIRMWARE_ENDPOINT, FirmwareData.class);
283     }
284
285     public TimerData fetchTimerData() throws TimeoutException, InterruptedException, ExecutionException {
286         return executeUrl("GET", TIMER_ENDPOINT, TimerData.class);
287     }
288
289     public PresetData fetchPresetData() throws TimeoutException, InterruptedException, ExecutionException {
290         return executeUrl("GET", PRESET_ENDPOINT, PresetData.class);
291     }
292
293     private <T> T executeUrl(String httpMethod, String endpoint, Class<T> classOfT)
294             throws TimeoutException, InterruptedException, ExecutionException {
295         final String responseBody = executeUrl("GET", endpoint, (String) null);
296         final T data = gson.fromJson(responseBody, classOfT);
297         return data;
298     }
299
300     private void executeUrl(String httpMethod, String endpoint, Object data)
301             throws TimeoutException, InterruptedException, ExecutionException {
302         final String content = gson.toJson(data);
303         executeUrl(httpMethod, endpoint, content);
304     }
305
306     /**
307      * Executes the given <code>url</code> with the given <code>httpMethod</code>
308      *
309      * @param httpMethod the HTTP method to use
310      * @param endpoint the url endpoint
311      * @param content the content to be sent to the given <code>url</code> or
312      *            <code>null</code> if no content should be sent.
313      * @return
314      * @throws ExecutionException
315      * @throws InterruptedException
316      * @throws UnsupportedEncodingException
317      * @throws Exception when the request execution failed, timed out or it was interrupted
318      */
319     private String executeUrl(String httpMethod, String endpoint, @Nullable String content)
320             throws TimeoutException, InterruptedException, ExecutionException {
321         final String url = urlBase + endpoint;
322         final HttpMethod method = HttpUtil.createHttpMethod(httpMethod);
323
324         final Request request = httpClient.newRequest(url).method(method).timeout(REQUEST_TIMEOUT_MS,
325                 TimeUnit.MILLISECONDS);
326
327         if (content != null && (HttpMethod.POST.equals(method) || HttpMethod.PUT.equals(method))) {
328             final StringContentProvider stringContentProvider = new StringContentProvider(content,
329                     StandardCharsets.UTF_8);
330             request.content(stringContentProvider, DEFAULT_CONTENT_TYPE);
331
332             logger.trace("Request for url '{}':\r\n{}", url, content);
333         } else {
334             logger.trace("Request for url '{}'", url);
335         }
336
337         final ContentResponse response = request.send();
338         final int statusCode = response.getStatus();
339         if (logger.isDebugEnabled() && statusCode >= HttpStatus.BAD_REQUEST_400) {
340             String statusLine = statusCode + " " + response.getReason();
341             logger.debug("Method failed: {}", statusLine);
342         }
343
344         final String encoding = response.getEncoding() != null ? response.getEncoding().replaceAll("\"", "").trim()
345                 : StandardCharsets.UTF_8.name();
346
347         try {
348             String responseBody = new String(response.getContent(), encoding);
349             logger.trace("Response for url '{}':\r\n{}", url, responseBody);
350             return responseBody;
351         } catch (UnsupportedEncodingException e) {
352             logger.warn("Get response content failed!", e);
353             return "";
354         }
355     }
356 }