2 * Copyright (c) 2010-2022 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.tapocontrol.internal.api;
15 import static org.openhab.binding.tapocontrol.internal.constants.TapoBindingSettings.*;
16 import static org.openhab.binding.tapocontrol.internal.constants.TapoErrorConstants.*;
18 import java.util.concurrent.TimeoutException;
20 import org.eclipse.jdt.annotation.NonNullByDefault;
21 import org.eclipse.jdt.annotation.Nullable;
22 import org.eclipse.jetty.client.HttpClient;
23 import org.eclipse.jetty.client.api.ContentResponse;
24 import org.eclipse.jetty.client.api.Request;
25 import org.eclipse.jetty.client.util.StringContentProvider;
26 import org.eclipse.jetty.http.HttpMethod;
27 import org.openhab.binding.tapocontrol.internal.device.TapoBridgeHandler;
28 import org.openhab.binding.tapocontrol.internal.helpers.PayloadBuilder;
29 import org.openhab.binding.tapocontrol.internal.helpers.TapoErrorHandler;
30 import org.slf4j.Logger;
31 import org.slf4j.LoggerFactory;
33 import com.google.gson.Gson;
34 import com.google.gson.JsonArray;
35 import com.google.gson.JsonObject;
38 * Handler class for TAPO-Cloud connections.
40 * @author Christian Wild - Initial contribution
43 public class TapoCloudConnector {
44 private final Logger logger = LoggerFactory.getLogger(TapoCloudConnector.class);
45 private final TapoBridgeHandler bridge;
46 private final Gson gson = new Gson();
47 private final HttpClient httpClient;
49 private String token = "";
50 private String url = TAPO_CLOUD_URL;
57 public TapoCloudConnector(TapoBridgeHandler bridge, HttpClient httpClient) {
59 this.httpClient = httpClient;
60 this.uid = bridge.getUID().getAsString();
66 * @param tapoError TapoErrorHandler
68 protected void handleError(TapoErrorHandler tapoError) {
69 this.bridge.setError(tapoError);
72 /***********************************
74 * HTTP (Cloud)-Actions
76 ************************************/
79 * LOGIN TO CLOUD (get Token)
81 * @param username unencrypted username
82 * @param password unencrypted password
83 * @return true if login was successfull
85 public Boolean login(String username, String password) {
86 this.token = getToken(username, password, TAPO_TERMINAL_UUID);
87 this.url = TAPO_CLOUD_URL + "?token=" + token;
88 return !this.token.isBlank();
94 public void logout() {
99 * GET TOKEN FROM TAPO-CLOUD
103 * @param terminalUUID
106 private String getToken(String email, String password, String terminalUUID) {
109 /* create login payload */
110 PayloadBuilder plBuilder = new PayloadBuilder();
111 plBuilder.method = "login";
112 plBuilder.addParameter("appType", TAPO_APP_TYPE);
113 plBuilder.addParameter("cloudUserName", email);
114 plBuilder.addParameter("cloudPassword", password);
115 plBuilder.addParameter("terminalUUID", terminalUUID);
116 String payload = plBuilder.getPayload();
118 ContentResponse response = sendCloudRequest(TAPO_CLOUD_URL, payload);
119 if (response != null) {
120 token = getTokenFromResponse(response);
125 private String getTokenFromResponse(ContentResponse response) {
126 /* work with response */
127 if (response.getStatus() == 200) {
128 String rBody = response.getContentAsString();
129 JsonObject jsonObject = gson.fromJson(rBody, JsonObject.class);
130 if (jsonObject != null) {
131 Integer errorCode = jsonObject.get("error_code").getAsInt();
132 if (errorCode == 0) {
133 token = jsonObject.getAsJsonObject("result").get("token").getAsString();
135 /* return errorcode from device */
136 String msg = jsonObject.get("msg").getAsString();
137 handleError(new TapoErrorHandler(errorCode, msg));
138 logger.trace("cloud returns error: '{}'", rBody);
141 handleError(new TapoErrorHandler(ERR_JSON_DECODE_FAIL));
142 logger.trace("unexpected json-response '{}'", rBody);
145 handleError(new TapoErrorHandler(ERR_HTTP_RESPONSE, ERR_HTTP_RESPONSE_MSG));
146 logger.warn("invalid response while login");
154 * @return JsonArray with deviceList
156 public JsonArray getDeviceList() {
158 PayloadBuilder plBuilder = new PayloadBuilder();
159 plBuilder.method = "getDeviceList";
160 String payload = plBuilder.getPayload();
162 ContentResponse response = sendCloudRequest(this.url, payload);
163 if (response != null) {
164 return getDeviceListFromResponse(response);
166 return new JsonArray();
170 * get DeviceList from Contenresponse
175 private JsonArray getDeviceListFromResponse(ContentResponse response) {
176 /* work with response */
177 if (response.getStatus() == 200) {
178 String rBody = response.getContentAsString();
179 JsonObject jsonObject = gson.fromJson(rBody, JsonObject.class);
180 if (jsonObject != null) {
181 /* get errocode (0=success) */
182 Integer errorCode = jsonObject.get("error_code").getAsInt();
183 if (errorCode == 0) {
184 JsonObject result = jsonObject.getAsJsonObject("result");
185 return result.getAsJsonArray("deviceList");
187 /* return errorcode from device */
188 handleError(new TapoErrorHandler(errorCode, "device answers with errorcode"));
189 logger.trace("cloud returns error: '{}'", rBody);
192 logger.trace("enexpected json-response '{}'", rBody);
195 logger.trace("response error '{}'", response.getContentAsString());
197 return new JsonArray();
200 /***********************************
204 ************************************/
206 * SEND SYNCHRON HTTP-REQUEST
208 * @param url url request is sent to
209 * @param payload payload (String) to send
210 * @return ContentResponse of request
213 protected ContentResponse sendCloudRequest(String url, String payload) {
214 Request httpRequest = httpClient.newRequest(url).method(HttpMethod.POST.toString());
217 httpRequest.header("content-type", CONTENT_TYPE_JSON);
218 httpRequest.header("Accept", CONTENT_TYPE_JSON);
220 /* add request body */
221 httpRequest.content(new StringContentProvider(payload, CONTENT_CHARSET), CONTENT_TYPE_JSON);
224 ContentResponse httpResponse = httpRequest.send();
226 } catch (InterruptedException e) {
227 logger.debug("({}) sending request interrupted: {}", uid, e.toString());
228 handleError(new TapoErrorHandler(e));
229 } catch (TimeoutException e) {
230 logger.debug("({}) sending request timeout: {}", uid, e.toString());
231 handleError(new TapoErrorHandler(ERR_CONNECT_TIMEOUT, e.toString()));
232 } catch (Exception e) {
233 logger.debug("({}) sending request failed: {}", uid, e.toString());
234 handleError(new TapoErrorHandler(e));