]> git.basschouten.com Git - openhab-addons.git/blob
12dd947f2d99ae379466228dee62ecb7aca21bd3
[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 String URL = "http://%hostName%/%resource%";
48
49     private final HttpClient httpClient;
50     private final List<RadioThermostatEventListener> listeners = new CopyOnWriteArrayList<>();
51
52     private @Nullable String hostName;
53
54     public RadioThermostatConnector(HttpClient httpClient) {
55         this.httpClient = httpClient;
56     }
57
58     public void setThermostatHostName(@Nullable String hostName) {
59         this.hostName = hostName;
60     }
61
62     /**
63      * Add a listener to the list of listeners to be notified with events
64      *
65      * @param listener the listener
66      */
67     public void addEventListener(RadioThermostatEventListener listener) {
68         listeners.add(listener);
69     }
70
71     /**
72      * Remove a listener from the list of listeners to be notified with events
73      *
74      * @param listener the listener
75      */
76     public void removeEventListener(RadioThermostatEventListener listener) {
77         listeners.remove(listener);
78     }
79
80     /**
81      * Send an asynchronous http call to the thermostat, the response will be send to the
82      * event listeners as a RadioThermostat event when it is finally received
83      *
84      * @param resouce the url of the json resource on the thermostat
85      */
86     public void getAsyncThermostatData(String resource) {
87         String urlStr = buildRequestURL(resource);
88
89         httpClient.newRequest(urlStr).method(GET).timeout(30, TimeUnit.SECONDS).send(new BufferingResponseListener() {
90             @Override
91             public void onComplete(@Nullable Result result) {
92                 if (result != null && !result.isFailed()) {
93                     String response = getContentAsString();
94                     logger.debug("thermostatResponse = {}", response);
95                     dispatchKeyValue(resource, response);
96                 } else {
97                     dispatchKeyValue(KEY_ERROR, "");
98                 }
99             }
100         });
101     }
102
103     /**
104      * Sends a command to the thermostat
105      *
106      * @param the JSON attribute key for the value to be updated
107      * @param the value to be updated in the thermostat
108      * @param the end point URI to use for the command
109      * @return the JSON response string from the thermostat
110      */
111     public String sendCommand(String cmdKey, @Nullable String cmdVal, String resource) {
112         return sendCommand(cmdKey, cmdVal, null, resource);
113     }
114
115     /**
116      * Sends a command to the thermostat
117      *
118      * @param the JSON attribute key for the value to be updated
119      * @param the value to be updated in the thermostat
120      * @param JSON string to send directly to the thermostat instead of a key/value pair
121      * @param the end point URI to use for the command
122      * @return the JSON response string from the thermostat
123      */
124     public String sendCommand(@Nullable String cmdKey, @Nullable String cmdVal, @Nullable String cmdJson,
125             String resource) {
126         // if we got a cmdJson string send that, otherwise build the json from the key and val params
127         String postJson = cmdJson != null ? cmdJson : "{\"" + cmdKey + "\":" + cmdVal + "}";
128         String urlStr = buildRequestURL(resource);
129
130         String output = "";
131
132         try {
133             Request request = httpClient.POST(urlStr);
134             request.header(HttpHeader.ACCEPT, "text/plain");
135             request.header(HttpHeader.CONTENT_TYPE, "text/plain");
136             request.content(new StringContentProvider(postJson), "application/json");
137
138             ContentResponse contentResponse = request.send();
139             int httpStatus = contentResponse.getStatus();
140
141             if (httpStatus != OK_200) {
142                 throw new RadioThermostatHttpException("Thermostat HTTP response code was: " + httpStatus);
143             }
144             output = contentResponse.getContentAsString();
145         } catch (RadioThermostatHttpException | InterruptedException | TimeoutException | ExecutionException e) {
146             logger.warn("Error executing thermostat command: {}, {}", postJson, e.getMessage());
147         }
148
149         return output;
150     }
151
152     /**
153      * Build request URL from configuration data
154      *
155      * @return a valid URL for the thermostat's JSON interface
156      */
157     private String buildRequestURL(String resource) {
158         String hostName = this.hostName;
159         if (hostName == null) {
160             throw new IllegalStateException("hostname must not be null");
161         }
162         String urlStr = URL.replace("%hostName%", hostName);
163         urlStr = urlStr.replace("%resource%", resource);
164
165         return urlStr;
166     }
167
168     /**
169      * Dispatch an event (key, value) to the event listeners
170      *
171      * @param key the key
172      * @param value the value
173      */
174     private void dispatchKeyValue(String key, String value) {
175         RadioThermostatEvent event = new RadioThermostatEvent(this, key, value);
176         for (RadioThermostatEventListener listener : listeners) {
177             listener.onNewMessageEvent(event);
178         }
179     }
180 }