2 * Copyright (c) 2010-2023 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
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
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.binding.somneo.internal;
15 import static org.openhab.binding.somneo.internal.SomneoBindingConstants.*;
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;
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;
46 import com.google.gson.Gson;
49 * The {@link SomneoHttpConnector} is responsible for sending commands.
51 * @author Michael Myrcik - Initial contribution
54 public class SomneoHttpConnector {
56 private Logger logger = LoggerFactory.getLogger(SomneoHttpConnector.class);
58 private static final int REQUEST_TIMEOUT_MS = 5000;
60 private static final String DEFAULT_CONTENT_TYPE = "application/json";
62 private final Gson gson = new Gson();
64 private final HttpClient httpClient;
66 private final String urlBase;
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);
73 public SensorData fetchSensorData() throws TimeoutException, InterruptedException, ExecutionException {
74 return executeUrl("GET", SENSORS_ENDPOINT, SensorData.class);
77 public LightData fetchLightData() throws TimeoutException, InterruptedException, ExecutionException {
78 return executeUrl("GET", LIGHT_ENDPOINT, LightData.class);
81 public SunsetData fetchSunsetData() throws TimeoutException, InterruptedException, ExecutionException {
82 return executeUrl("GET", SUNSET_ENDPOINT, SunsetData.class);
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);
91 executeUrl("PUT", LIGHT_ENDPOINT, data);
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);
101 executeUrl("PUT", LIGHT_ENDPOINT, data);
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);
110 executeUrl("PUT", LIGHT_ENDPOINT, data);
113 public void switchSunsetProgram(boolean state) throws TimeoutException, InterruptedException, ExecutionException {
114 final SunsetData data = new SunsetData();
115 data.setState(state);
117 executeUrl("PUT", SUNSET_ENDPOINT, data);
120 public void setSunsetLightIntensity(int percent) throws TimeoutException, InterruptedException, ExecutionException {
121 final SunsetData data = new SunsetData();
122 data.setLightIntensity(percent);
124 executeUrl("PUT", SUNSET_ENDPOINT, data);
127 public void setSunsetDuration(int duration) throws TimeoutException, InterruptedException, ExecutionException {
128 final SunsetData data = new SunsetData();
129 data.setDurationInMin(duration);
131 executeUrl("PUT", SUNSET_ENDPOINT, data);
134 public void setSunsetColorSchema(int value) throws TimeoutException, InterruptedException, ExecutionException {
135 final SunsetData data = new SunsetData();
136 data.setColorSchema(value);
138 executeUrl("PUT", SUNSET_ENDPOINT, data);
141 public void setSunsetAmbientNoise(String option) throws TimeoutException, InterruptedException, ExecutionException {
142 final SunsetData data = new SunsetData();
143 data.setAmbientNoise(option);
145 executeUrl("PUT", SUNSET_ENDPOINT, data);
148 public void setSunsetVolume(int percent) throws TimeoutException, InterruptedException, ExecutionException {
149 final SunsetData data = new SunsetData();
150 data.setSoundVolume(percent);
152 executeUrl("PUT", SUNSET_ENDPOINT, data);
155 public RelaxData fetchRelaxData() throws TimeoutException, InterruptedException, ExecutionException {
156 return executeUrl("GET", RELAX_ENDPOINT, RelaxData.class);
159 public void setRelaxVolume(int percent) throws TimeoutException, InterruptedException, ExecutionException {
160 final RelaxData data = new RelaxData();
161 data.setSoundVolume(percent);
163 executeUrl("PUT", RELAX_ENDPOINT, data);
166 public void setRelaxLightIntensity(int percent) throws TimeoutException, InterruptedException, ExecutionException {
167 final RelaxData data = new RelaxData();
168 data.setLightIntensity(percent);
170 executeUrl("PUT", RELAX_ENDPOINT, data);
173 public void switchRelaxProgram(boolean state) throws TimeoutException, InterruptedException, ExecutionException {
174 final RelaxData data = new RelaxData();
175 data.setState(state);
177 executeUrl("PUT", RELAX_ENDPOINT, data);
180 public void setRelaxBreathingRate(int value) throws TimeoutException, InterruptedException, ExecutionException {
181 final RelaxData data = new RelaxData();
182 data.setBreathingRate(value);
184 executeUrl("PUT", RELAX_ENDPOINT, data);
187 public void setRelaxDuration(int value) throws TimeoutException, InterruptedException, ExecutionException {
188 final RelaxData data = new RelaxData();
189 data.setDurationInMin(value);
191 executeUrl("PUT", RELAX_ENDPOINT, data);
194 public void setRelaxGuidanceType(int value) throws TimeoutException, InterruptedException, ExecutionException {
195 final RelaxData data = new RelaxData();
196 data.setGuidanceType(value);
198 executeUrl("PUT", RELAX_ENDPOINT, data);
201 public AudioData fetchAudioData() throws TimeoutException, InterruptedException, ExecutionException {
202 return executeUrl("GET", AUDIO_ENDPOINT, AudioData.class);
205 public void switchRadio(boolean state) throws TimeoutException, InterruptedException, ExecutionException {
206 final AudioData data = new AudioData();
213 executeUrl("PUT", AUDIO_ENDPOINT, data);
216 public void switchAux(boolean state) throws TimeoutException, InterruptedException, ExecutionException {
217 final AudioData data = new AudioData();
224 executeUrl("PUT", AUDIO_ENDPOINT, data);
227 public void setAudioVolume(int percent) throws TimeoutException, InterruptedException, ExecutionException {
228 final AudioData data = new AudioData();
229 data.setVolume(percent);
231 executeUrl("PUT", AUDIO_ENDPOINT, data);
234 public void setRadioChannel(String preset) throws TimeoutException, InterruptedException, ExecutionException {
235 final AudioData data = new AudioData();
237 data.setRadioPreset(preset);
239 executeUrl("PUT", AUDIO_ENDPOINT, data);
242 public RadioData getRadioData() throws TimeoutException, InterruptedException, ExecutionException {
243 RadioData data = new RadioData();
253 data = executeUrl("GET", RADIO_ENDPOINT, RadioData.class);
254 } while (data.isSeeking()); // Wait until seek is finished
259 public void radioSeekUp() throws TimeoutException, InterruptedException, ExecutionException {
260 final RadioData data = new RadioData();
263 executeUrl("PUT", RADIO_ENDPOINT, data);
266 public void radioSeekDown() throws TimeoutException, InterruptedException, ExecutionException {
267 final RadioData data = new RadioData();
268 data.setCmdSeekDown();
270 executeUrl("PUT", RADIO_ENDPOINT, data);
273 public DeviceData fetchDeviceData() throws TimeoutException, InterruptedException, ExecutionException {
274 return executeUrl("GET", DEVICE_ENDPOINT, DeviceData.class);
277 public WifiData fetchWifiData() throws TimeoutException, InterruptedException, ExecutionException {
278 return executeUrl("GET", WIFI_ENDPOINT, WifiData.class);
281 public FirmwareData fetchFirmwareData() throws TimeoutException, InterruptedException, ExecutionException {
282 return executeUrl("GET", FIRMWARE_ENDPOINT, FirmwareData.class);
285 public TimerData fetchTimerData() throws TimeoutException, InterruptedException, ExecutionException {
286 return executeUrl("GET", TIMER_ENDPOINT, TimerData.class);
289 public PresetData fetchPresetData() throws TimeoutException, InterruptedException, ExecutionException {
290 return executeUrl("GET", PRESET_ENDPOINT, PresetData.class);
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);
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);
307 * Executes the given <code>url</code> with the given <code>httpMethod</code>
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.
314 * @throws ExecutionException
315 * @throws InterruptedException
316 * @throws UnsupportedEncodingException
317 * @throws Exception when the request execution failed, timed out or it was interrupted
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);
324 final Request request = httpClient.newRequest(url).method(method).timeout(REQUEST_TIMEOUT_MS,
325 TimeUnit.MILLISECONDS);
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);
332 logger.trace("Request for url '{}':\r\n{}", url, content);
334 logger.trace("Request for url '{}'", url);
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);
344 final String encoding = response.getEncoding() != null ? response.getEncoding().replaceAll("\"", "").trim()
345 : StandardCharsets.UTF_8.name();
348 String responseBody = new String(response.getContent(), encoding);
349 logger.trace("Response for url '{}':\r\n{}", url, responseBody);
351 } catch (UnsupportedEncodingException e) {
352 logger.warn("Get response content failed!", e);