]> git.basschouten.com Git - openhab-addons.git/blob
149014c56ba1cf34a69d1a909199937a4af8eb82
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2022 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.tapocontrol.internal.api;
14
15 import static org.openhab.binding.tapocontrol.internal.constants.TapoBindingSettings.*;
16 import static org.openhab.binding.tapocontrol.internal.constants.TapoErrorConstants.*;
17
18 import java.util.concurrent.TimeoutException;
19
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;
32
33 import com.google.gson.Gson;
34 import com.google.gson.JsonArray;
35 import com.google.gson.JsonObject;
36
37 /**
38  * Handler class for TAPO-Cloud connections.
39  *
40  * @author Christian Wild - Initial contribution
41  */
42 @NonNullByDefault
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;
48
49     private String token = "";
50     private String url = TAPO_CLOUD_URL;
51     private String uid;
52
53     /**
54      * INIT CLASS
55      * 
56      */
57     public TapoCloudConnector(TapoBridgeHandler bridge, HttpClient httpClient) {
58         this.bridge = bridge;
59         this.httpClient = httpClient;
60         this.uid = bridge.getUID().getAsString();
61     }
62
63     /**
64      * handle error
65      * 
66      * @param tapoError TapoErrorHandler
67      */
68     protected void handleError(TapoErrorHandler tapoError) {
69         this.bridge.setError(tapoError);
70     }
71
72     /***********************************
73      *
74      * HTTP (Cloud)-Actions
75      *
76      ************************************/
77
78     /**
79      * LOGIN TO CLOUD (get Token)
80      * 
81      * @param username unencrypted username
82      * @param password unencrypted password
83      * @return true if login was successfull
84      */
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();
89     }
90
91     /**
92      * logout
93      */
94     public void logout() {
95         this.token = "";
96     }
97
98     /**
99      * GET TOKEN FROM TAPO-CLOUD
100      * 
101      * @param email
102      * @param password
103      * @param terminalUUID
104      * @return
105      */
106     private String getToken(String email, String password, String terminalUUID) {
107         String token = "";
108
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();
117
118         ContentResponse response = sendCloudRequest(TAPO_CLOUD_URL, payload);
119         if (response != null) {
120             token = getTokenFromResponse(response);
121         }
122         return token;
123     }
124
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();
134                 } else {
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);
139                 }
140             } else {
141                 handleError(new TapoErrorHandler(ERR_JSON_DECODE_FAIL));
142                 logger.trace("unexpected json-response '{}'", rBody);
143             }
144         } else {
145             handleError(new TapoErrorHandler(ERR_HTTP_RESPONSE, ERR_HTTP_RESPONSE_MSG));
146             logger.warn("invalid response while login");
147             token = "";
148         }
149         return token;
150     }
151
152     /**
153      * 
154      * @return JsonArray with deviceList
155      */
156     public JsonArray getDeviceList() {
157         /* create payload */
158         PayloadBuilder plBuilder = new PayloadBuilder();
159         plBuilder.method = "getDeviceList";
160         String payload = plBuilder.getPayload();
161
162         ContentResponse response = sendCloudRequest(this.url, payload);
163         if (response != null) {
164             return getDeviceListFromResponse(response);
165         }
166         return new JsonArray();
167     }
168
169     /**
170      * get DeviceList from Contenresponse
171      * 
172      * @param response
173      * @return
174      */
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");
186                 } else {
187                     /* return errorcode from device */
188                     handleError(new TapoErrorHandler(errorCode, "device answers with errorcode"));
189                     logger.trace("cloud returns error: '{}'", rBody);
190                 }
191             } else {
192                 logger.trace("enexpected json-response '{}'", rBody);
193             }
194         } else {
195             logger.trace("response error '{}'", response.getContentAsString());
196         }
197         return new JsonArray();
198     }
199
200     /***********************************
201      *
202      * HTTP-ACTIONS
203      *
204      ************************************/
205     /**
206      * SEND SYNCHRON HTTP-REQUEST
207      * 
208      * @param url url request is sent to
209      * @param payload payload (String) to send
210      * @return ContentResponse of request
211      */
212     @Nullable
213     protected ContentResponse sendCloudRequest(String url, String payload) {
214         Request httpRequest = httpClient.newRequest(url).method(HttpMethod.POST.toString());
215
216         /* set header */
217         httpRequest.header("content-type", CONTENT_TYPE_JSON);
218         httpRequest.header("Accept", CONTENT_TYPE_JSON);
219
220         /* add request body */
221         httpRequest.content(new StringContentProvider(payload, CONTENT_CHARSET), CONTENT_TYPE_JSON);
222
223         try {
224             ContentResponse httpResponse = httpRequest.send();
225             return httpResponse;
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));
235         }
236         return null;
237     }
238 }