]> git.basschouten.com Git - openhab-addons.git/blob
50b174c90ab2bb22aa533b53b0f7f7848cae06bb
[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.radiothermostat.internal.communication;
14
15 import static org.eclipse.jetty.http.HttpMethod.GET;
16 import static org.eclipse.jetty.http.HttpStatus.OK_200;
17 import static org.openhab.binding.radiothermostat.internal.RadioThermostatBindingConstants.*;
18
19 import java.util.List;
20 import java.util.concurrent.CopyOnWriteArrayList;
21 import java.util.concurrent.ExecutionException;
22 import java.util.concurrent.TimeUnit;
23 import java.util.concurrent.TimeoutException;
24
25 import org.eclipse.jdt.annotation.NonNullByDefault;
26 import org.eclipse.jdt.annotation.Nullable;
27 import org.eclipse.jetty.client.HttpClient;
28 import org.eclipse.jetty.client.api.ContentResponse;
29 import org.eclipse.jetty.client.api.Request;
30 import org.eclipse.jetty.client.api.Result;
31 import org.eclipse.jetty.client.util.BufferingResponseListener;
32 import org.eclipse.jetty.client.util.StringContentProvider;
33 import org.eclipse.jetty.http.HttpHeader;
34 import org.openhab.binding.radiothermostat.internal.RadioThermostatHttpException;
35 import org.slf4j.Logger;
36 import org.slf4j.LoggerFactory;
37
38 /**
39  * Class for communicating with the RadioThermostat web interface
40  *
41  * @author Michael Lobstein - Initial contribution
42  */
43 @NonNullByDefault
44 public class RadioThermostatConnector {
45     private final Logger logger = LoggerFactory.getLogger(RadioThermostatConnector.class);
46
47     private static final int REQUEST_TIMEOUT_MS = 10_000;
48     private static final String URL = "http://%s/%s";
49
50     private final HttpClient httpClient;
51     private final List<RadioThermostatEventListener> listeners = new CopyOnWriteArrayList<>();
52
53     private String hostName = BLANK;
54
55     public RadioThermostatConnector(HttpClient httpClient) {
56         this.httpClient = httpClient;
57     }
58
59     public void setThermostatHostName(String hostName) {
60         this.hostName = hostName;
61     }
62
63     /**
64      * Add a listener to the list of listeners to be notified with events
65      *
66      * @param listener the listener
67      */
68     public void addEventListener(RadioThermostatEventListener listener) {
69         listeners.add(listener);
70     }
71
72     /**
73      * Remove a listener from the list of listeners to be notified with events
74      *
75      * @param listener the listener
76      */
77     public void removeEventListener(RadioThermostatEventListener listener) {
78         listeners.remove(listener);
79     }
80
81     /**
82      * Send an asynchronous http call to the thermostat, the response will be send to the
83      * event listeners as a RadioThermostat event when it is finally received
84      *
85      * @param resource the url of the json resource on the thermostat
86      */
87     public void getAsyncThermostatData(String resource) {
88         httpClient.newRequest(buildRequestURL(resource)).method(GET).timeout(30, TimeUnit.SECONDS)
89                 .send(new BufferingResponseListener() {
90                     @Override
91                     public void onComplete(@Nullable Result result) {
92                         if (result != null && !result.isFailed()) {
93                             dispatchKeyValue(resource, getContentAsString());
94                         } else {
95                             dispatchKeyValue(KEY_ERROR, BLANK);
96                         }
97                     }
98                 });
99     }
100
101     /**
102      * Sends a command to the thermostat
103      *
104      * @param cmdKey the JSON attribute key for the value to be updated
105      * @param cmdVal the value to be updated in the thermostat
106      * @param resource the end point URI to use for the command
107      * @return the JSON response string from the thermostat
108      */
109     public String sendCommand(String cmdKey, @Nullable String cmdVal, String resource) {
110         return sendCommand(cmdKey, cmdVal, null, resource);
111     }
112
113     /**
114      * Sends a command to the thermostat
115      *
116      * @param cmdKey the JSON attribute key for the value to be updated
117      * @param cmdVal the value to be updated in the thermostat
118      * @param cmdJson JSON string to send directly to the thermostat instead of a key/value pair
119      * @param resource the end point URI to use for the command
120      * @return the JSON response string from the thermostat
121      */
122     public String sendCommand(@Nullable String cmdKey, @Nullable String cmdVal, @Nullable String cmdJson,
123             String resource) {
124         // if we got a cmdJson string send that, otherwise build the json from the key and val params
125         String postJson = cmdJson != null ? cmdJson : "{\"" + cmdKey + "\":" + cmdVal + "}";
126
127         try {
128             Request request = httpClient.POST(buildRequestURL(resource)).timeout(REQUEST_TIMEOUT_MS,
129                     TimeUnit.MILLISECONDS);
130             request.header(HttpHeader.ACCEPT, "text/plain");
131             request.header(HttpHeader.CONTENT_TYPE, "text/plain");
132             request.content(new StringContentProvider(postJson), "application/json");
133             logger.trace("Sending POST request to '{}', data: {}", resource, postJson);
134
135             ContentResponse contentResponse = request.send();
136
137             if (contentResponse.getStatus() != OK_200) {
138                 throw new RadioThermostatHttpException(
139                         "Thermostat HTTP response code was: " + contentResponse.getStatus());
140             }
141
142             logger.trace("Response: {}", contentResponse.getContentAsString());
143             return contentResponse.getContentAsString();
144         } catch (RadioThermostatHttpException | InterruptedException | TimeoutException | ExecutionException e) {
145             logger.debug("Error executing thermostat command: {}, {}", postJson, e.getMessage());
146             return BLANK;
147         }
148     }
149
150     /**
151      * Build request URL from configuration data
152      *
153      * @return a valid URL for the thermostat's JSON interface
154      */
155     private String buildRequestURL(String resource) {
156         return String.format(URL, hostName, resource);
157     }
158
159     /**
160      * Dispatch an event (key, value) to the event listeners
161      *
162      * @param key the key
163      * @param value the value
164      */
165     private void dispatchKeyValue(String key, String value) {
166         RadioThermostatEvent event = new RadioThermostatEvent(this, key, value);
167         for (RadioThermostatEventListener listener : listeners) {
168             listener.onNewMessageEvent(event);
169         }
170     }
171 }