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.radiothermostat.internal.communication;
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.*;
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;
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;
39 * Class for communicating with the RadioThermostat web interface
41 * @author Michael Lobstein - Initial contribution
44 public class RadioThermostatConnector {
45 private final Logger logger = LoggerFactory.getLogger(RadioThermostatConnector.class);
47 private static final String URL = "http://%hostName%/%resource%";
49 private final HttpClient httpClient;
50 private final List<RadioThermostatEventListener> listeners = new CopyOnWriteArrayList<>();
52 private @Nullable String hostName;
54 public RadioThermostatConnector(HttpClient httpClient) {
55 this.httpClient = httpClient;
58 public void setThermostatHostName(@Nullable String hostName) {
59 this.hostName = hostName;
63 * Add a listener to the list of listeners to be notified with events
65 * @param listener the listener
67 public void addEventListener(RadioThermostatEventListener listener) {
68 listeners.add(listener);
72 * Remove a listener from the list of listeners to be notified with events
74 * @param listener the listener
76 public void removeEventListener(RadioThermostatEventListener listener) {
77 listeners.remove(listener);
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
84 * @param resouce the url of the json resource on the thermostat
86 public void getAsyncThermostatData(String resource) {
87 String urlStr = buildRequestURL(resource);
89 httpClient.newRequest(urlStr).method(GET).timeout(30, TimeUnit.SECONDS).send(new BufferingResponseListener() {
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);
97 dispatchKeyValue(KEY_ERROR, "");
104 * Sends a command to the thermostat
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
111 public String sendCommand(String cmdKey, @Nullable String cmdVal, String resource) {
112 return sendCommand(cmdKey, cmdVal, null, resource);
116 * Sends a command to the thermostat
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
124 public String sendCommand(@Nullable String cmdKey, @Nullable String cmdVal, @Nullable String cmdJson,
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);
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");
138 ContentResponse contentResponse = request.send();
139 int httpStatus = contentResponse.getStatus();
141 if (httpStatus != OK_200) {
142 throw new RadioThermostatHttpException("Thermostat HTTP response code was: " + httpStatus);
144 output = contentResponse.getContentAsString();
145 } catch (RadioThermostatHttpException | InterruptedException | TimeoutException | ExecutionException e) {
146 logger.warn("Error executing thermostat command: {}, {}", postJson, e.getMessage());
153 * Build request URL from configuration data
155 * @return a valid URL for the thermostat's JSON interface
157 private String buildRequestURL(String resource) {
158 String hostName = this.hostName;
159 if (hostName == null) {
160 throw new IllegalStateException("hostname must not be null");
162 String urlStr = URL.replace("%hostName%", hostName);
163 urlStr = urlStr.replace("%resource%", resource);
169 * Dispatch an event (key, value) to the event listeners
172 * @param value the value
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);