]> git.basschouten.com Git - openhab-addons.git/blob
d2dc3ab967ad1330e945c51726025c55c21377bc
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2020 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.daikin.internal;
14
15 import java.io.IOException;
16 import java.util.HashMap;
17 import java.util.Map;
18 import java.util.concurrent.TimeUnit;
19 import java.util.stream.Collectors;
20
21 import org.eclipse.jdt.annotation.NonNullByDefault;
22 import org.eclipse.jdt.annotation.Nullable;
23 import org.eclipse.jetty.client.HttpClient;
24 import org.eclipse.jetty.client.api.ContentResponse;
25 import org.eclipse.jetty.client.api.Request;
26 import org.eclipse.jetty.http.HttpMethod;
27 import org.eclipse.jetty.http.HttpStatus;
28 import org.openhab.binding.daikin.internal.api.BasicInfo;
29 import org.openhab.binding.daikin.internal.api.ControlInfo;
30 import org.openhab.binding.daikin.internal.api.EnergyInfoYear;
31 import org.openhab.binding.daikin.internal.api.Enums.SpecialModeKind;
32 import org.openhab.binding.daikin.internal.api.SensorInfo;
33 import org.openhab.binding.daikin.internal.api.airbase.AirbaseBasicInfo;
34 import org.openhab.binding.daikin.internal.api.airbase.AirbaseControlInfo;
35 import org.openhab.binding.daikin.internal.api.airbase.AirbaseModelInfo;
36 import org.openhab.binding.daikin.internal.api.airbase.AirbaseZoneInfo;
37 import org.openhab.core.io.net.http.HttpUtil;
38 import org.slf4j.Logger;
39 import org.slf4j.LoggerFactory;
40
41 /**
42  * Handles performing the actual HTTP requests for communicating with Daikin air conditioning units.
43  *
44  * @author Tim Waterhouse - Initial Contribution
45  * @author Paul Smedley <paul@smedley.id.au> - Modifications to support Airbase Controllers
46  * @author Jimmy Tanagra - Add support for https and Daikin's uuid authentication
47  *
48  */
49 @NonNullByDefault
50 public class DaikinWebTargets {
51     private static final int TIMEOUT_MS = 30000;
52
53     private String getBasicInfoUri;
54     private String setControlInfoUri;
55     private String getControlInfoUri;
56     private String getSensorInfoUri;
57     private String registerUuidUri;
58     private String getEnergyInfoYearUri;
59     private String setSpecialModeUri;
60
61     private String setAirbaseControlInfoUri;
62     private String getAirbaseControlInfoUri;
63     private String getAirbaseSensorInfoUri;
64     private String getAirbaseBasicInfoUri;
65     private String getAirbaseModelInfoUri;
66     private String getAirbaseZoneInfoUri;
67     private String setAirbaseZoneInfoUri;
68
69     private @Nullable String uuid;
70     private final @Nullable HttpClient httpClient;
71
72     private Logger logger = LoggerFactory.getLogger(DaikinWebTargets.class);
73
74     public DaikinWebTargets(@Nullable HttpClient httpClient, @Nullable String host, @Nullable Boolean secure,
75             @Nullable String uuid) {
76         this.httpClient = httpClient;
77         this.uuid = uuid;
78
79         String baseUri = (secure != null && secure.booleanValue() ? "https://" : "http://") + host + "/";
80         getBasicInfoUri = baseUri + "common/basic_info";
81         setControlInfoUri = baseUri + "aircon/set_control_info";
82         getControlInfoUri = baseUri + "aircon/get_control_info";
83         getSensorInfoUri = baseUri + "aircon/get_sensor_info";
84         registerUuidUri = baseUri + "common/register_terminal";
85         getEnergyInfoYearUri = baseUri + "aircon/get_year_power_ex";
86         setSpecialModeUri = baseUri + "aircon/set_special_mode";
87
88         // Daikin Airbase API
89         getAirbaseBasicInfoUri = baseUri + "skyfi/common/basic_info";
90         setAirbaseControlInfoUri = baseUri + "skyfi/aircon/set_control_info";
91         getAirbaseControlInfoUri = baseUri + "skyfi/aircon/get_control_info";
92         getAirbaseSensorInfoUri = baseUri + "skyfi/aircon/get_sensor_info";
93         getAirbaseModelInfoUri = baseUri + "skyfi/aircon/get_model_info";
94         getAirbaseZoneInfoUri = baseUri + "skyfi/aircon/get_zone_setting";
95         setAirbaseZoneInfoUri = baseUri + "skyfi/aircon/set_zone_setting";
96     }
97
98     // Standard Daikin API
99     public BasicInfo getBasicInfo() throws DaikinCommunicationException {
100         String response = invoke(getBasicInfoUri);
101         return BasicInfo.parse(response);
102     }
103
104     public ControlInfo getControlInfo() throws DaikinCommunicationException {
105         String response = invoke(getControlInfoUri);
106         return ControlInfo.parse(response);
107     }
108
109     public void setControlInfo(ControlInfo info) throws DaikinCommunicationException {
110         Map<String, String> queryParams = info.getParamString();
111         invoke(setControlInfoUri, queryParams);
112     }
113
114     public SensorInfo getSensorInfo() throws DaikinCommunicationException {
115         String response = invoke(getSensorInfoUri);
116         return SensorInfo.parse(response);
117     }
118
119     public void registerUuid(String key) throws DaikinCommunicationException {
120         Map<String, String> params = new HashMap<>();
121         params.put("key", key);
122         String response = invoke(registerUuidUri, params);
123         logger.debug("registerUuid result: {}", response);
124     }
125
126     public EnergyInfoYear getEnergyInfoYear() throws DaikinCommunicationException {
127         String response = invoke(getEnergyInfoYearUri);
128         return EnergyInfoYear.parse(response);
129     }
130
131     public boolean setSpecialMode(SpecialModeKind specialModeKind, boolean state) throws DaikinCommunicationException {
132         Map<String, String> queryParams = new HashMap<>();
133         queryParams.put("spmode_kind", String.valueOf(specialModeKind.getValue()));
134         queryParams.put("set_spmode", state ? "1" : "0");
135         String response = invoke(setSpecialModeUri, queryParams);
136         return !response.contains("ret=OK");
137     }
138
139     // Daikin Airbase API
140     public AirbaseControlInfo getAirbaseControlInfo() throws DaikinCommunicationException {
141         String response = invoke(getAirbaseControlInfoUri);
142         return AirbaseControlInfo.parse(response);
143     }
144
145     public void setAirbaseControlInfo(AirbaseControlInfo info) throws DaikinCommunicationException {
146         Map<String, String> queryParams = info.getParamString();
147         invoke(setAirbaseControlInfoUri, queryParams);
148     }
149
150     public SensorInfo getAirbaseSensorInfo() throws DaikinCommunicationException {
151         String response = invoke(getAirbaseSensorInfoUri);
152         return SensorInfo.parse(response);
153     }
154
155     public AirbaseBasicInfo getAirbaseBasicInfo() throws DaikinCommunicationException {
156         String response = invoke(getAirbaseBasicInfoUri);
157         return AirbaseBasicInfo.parse(response);
158     }
159
160     public AirbaseModelInfo getAirbaseModelInfo() throws DaikinCommunicationException {
161         String response = invoke(getAirbaseModelInfoUri);
162         return AirbaseModelInfo.parse(response);
163     }
164
165     public AirbaseZoneInfo getAirbaseZoneInfo() throws DaikinCommunicationException {
166         String response = invoke(getAirbaseZoneInfoUri);
167         return AirbaseZoneInfo.parse(response);
168     }
169
170     public void setAirbaseZoneInfo(AirbaseZoneInfo zoneinfo) throws DaikinCommunicationException {
171         Map<String, String> queryParams = zoneinfo.getParamString();
172         invoke(setAirbaseZoneInfoUri, queryParams);
173     }
174
175     private String invoke(String uri) throws DaikinCommunicationException {
176         return invoke(uri, new HashMap<>());
177     }
178
179     private String invoke(String uri, Map<String, String> params) throws DaikinCommunicationException {
180         String uriWithParams = uri + paramsToQueryString(params);
181         logger.debug("Calling url: {}", uriWithParams);
182         String response;
183         synchronized (this) {
184             try {
185                 if (httpClient != null) {
186                     response = executeUrl(uriWithParams);
187                 } else {
188                     // a fall back method
189                     logger.debug("Using HttpUtil fall scback");
190                     response = HttpUtil.executeUrl("GET", uriWithParams, TIMEOUT_MS);
191                 }
192             } catch (DaikinCommunicationException ex) {
193                 throw ex;
194             } catch (IOException ex) {
195                 // Response will also be set to null if parsing in executeUrl fails so we use null here to make the
196                 // error check below consistent.
197                 response = null;
198             }
199         }
200
201         if (response == null) {
202             throw new DaikinCommunicationException("Daikin controller returned error while invoking " + uriWithParams);
203         }
204
205         return response;
206     }
207
208     private String executeUrl(String url) throws DaikinCommunicationException {
209         try {
210             Request request = httpClient.newRequest(url).method(HttpMethod.GET).timeout(TIMEOUT_MS,
211                     TimeUnit.MILLISECONDS);
212             if (uuid != null) {
213                 request.header("X-Daikin-uuid", uuid);
214                 logger.debug("Header: X-Daikin-uuid: {}", uuid);
215             }
216             ContentResponse response = request.send();
217
218             if (response.getStatus() == HttpStatus.FORBIDDEN_403) {
219                 throw new DaikinCommunicationForbiddenException("Daikin controller access denied. Check uuid/key.");
220             }
221
222             if (response.getStatus() != HttpStatus.OK_200) {
223                 logger.debug("Daikin controller HTTP status: {} - {}", response.getStatus(), response.getReason());
224             }
225
226             return response.getContentAsString();
227         } catch (DaikinCommunicationException e) {
228             throw e;
229         } catch (Exception e) {
230             throw new DaikinCommunicationException("Daikin HTTP error", e);
231         }
232     }
233
234     private String paramsToQueryString(Map<String, String> params) {
235         if (params.isEmpty()) {
236             return "";
237         }
238
239         return "?" + params.entrySet().stream().map(param -> param.getKey() + "=" + param.getValue())
240                 .collect(Collectors.joining("&"));
241     }
242 }