]> git.basschouten.com Git - openhab-addons.git/blob
468948e074088e055abb237ed9b8a4903f4b4302
[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.tapocontrol.internal.api;
14
15 import static org.openhab.binding.tapocontrol.internal.constants.TapoBindingSettings.*;
16 import static org.openhab.binding.tapocontrol.internal.constants.TapoErrorCode.*;
17
18 import java.util.UUID;
19 import java.util.concurrent.TimeoutException;
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.client.util.StringContentProvider;
27 import org.eclipse.jetty.http.HttpMethod;
28 import org.openhab.binding.tapocontrol.internal.device.TapoBridgeHandler;
29 import org.openhab.binding.tapocontrol.internal.helpers.PayloadBuilder;
30 import org.openhab.binding.tapocontrol.internal.helpers.TapoErrorHandler;
31 import org.slf4j.Logger;
32 import org.slf4j.LoggerFactory;
33
34 import com.google.gson.Gson;
35 import com.google.gson.JsonArray;
36 import com.google.gson.JsonObject;
37
38 /**
39  * Handler class for TAPO-Cloud connections.
40  *
41  * @author Christian Wild - Initial contribution
42  */
43 @NonNullByDefault
44 public class TapoCloudConnector {
45     private final Logger logger = LoggerFactory.getLogger(TapoCloudConnector.class);
46     private final TapoBridgeHandler bridge;
47     private final Gson gson = new Gson();
48     private final HttpClient httpClient;
49
50     private String token = "";
51     private String url = TAPO_CLOUD_URL;
52     private String uid;
53
54     /**
55      * INIT CLASS
56      * 
57      */
58     public TapoCloudConnector(TapoBridgeHandler bridge, HttpClient httpClient) {
59         this.bridge = bridge;
60         this.httpClient = httpClient;
61         this.uid = bridge.getUID().getAsString();
62     }
63
64     /**
65      * handle error
66      * 
67      * @param tapoError TapoErrorHandler
68      */
69     protected void handleError(TapoErrorHandler tapoError) {
70         this.bridge.setError(tapoError);
71     }
72
73     /***********************************
74      *
75      * HTTP (Cloud)-Actions
76      *
77      ************************************/
78
79     /**
80      * LOGIN TO CLOUD (get Token)
81      * 
82      * @param username unencrypted username
83      * @param password unencrypted password
84      * @return true if login was successfull
85      */
86     public Boolean login(String username, String password) {
87         this.token = getToken(username, password, UUID.randomUUID().toString());
88         this.url = TAPO_CLOUD_URL + "?token=" + token;
89         return !this.token.isBlank();
90     }
91
92     /**
93      * logout
94      */
95     public void logout() {
96         this.token = "";
97     }
98
99     /**
100      * GET TOKEN FROM TAPO-CLOUD
101      * 
102      * @param email
103      * @param password
104      * @param terminalUUID
105      * @return
106      */
107     private String getToken(String email, String password, String terminalUUID) {
108         String token = "";
109
110         /* create login payload */
111         PayloadBuilder plBuilder = new PayloadBuilder();
112         plBuilder.method = "login";
113         plBuilder.addParameter("appType", TAPO_APP_TYPE);
114         plBuilder.addParameter("cloudUserName", email);
115         plBuilder.addParameter("cloudPassword", password);
116         plBuilder.addParameter("terminalUUID", terminalUUID);
117         String payload = plBuilder.getPayload();
118
119         ContentResponse response = sendCloudRequest(TAPO_CLOUD_URL, payload);
120         if (response != null) {
121             token = getTokenFromResponse(response);
122         }
123         return token;
124     }
125
126     private String getTokenFromResponse(ContentResponse response) {
127         /* work with response */
128         if (response.getStatus() == 200) {
129             String rBody = response.getContentAsString();
130             JsonObject jsonObject = gson.fromJson(rBody, JsonObject.class);
131             if (jsonObject != null) {
132                 Integer errorCode = jsonObject.get("error_code").getAsInt();
133                 if (errorCode == 0) {
134                     token = jsonObject.getAsJsonObject("result").get("token").getAsString();
135                 } else {
136                     /* return errorcode from device */
137                     String msg = jsonObject.get("msg").getAsString();
138                     handleError(new TapoErrorHandler(errorCode, msg));
139                     logger.trace("cloud returns error: '{}'", rBody);
140                 }
141             } else {
142                 handleError(new TapoErrorHandler(ERR_API_JSON_DECODE_FAIL));
143                 logger.trace("unexpected json-response '{}'", rBody);
144             }
145         } else {
146             handleError(new TapoErrorHandler(ERR_BINDING_HTTP_RESPONSE));
147             logger.warn("invalid response while login");
148             token = "";
149         }
150         return token;
151     }
152
153     /**
154      * 
155      * @return JsonArray with deviceList
156      */
157     public JsonArray getDeviceList() {
158         /* create payload */
159         PayloadBuilder plBuilder = new PayloadBuilder();
160         plBuilder.method = "getDeviceList";
161         String payload = plBuilder.getPayload();
162
163         ContentResponse response = sendCloudRequest(this.url, payload);
164         if (response != null) {
165             return getDeviceListFromResponse(response);
166         }
167         return new JsonArray();
168     }
169
170     /**
171      * get DeviceList from Contenresponse
172      * 
173      * @param response
174      * @return
175      */
176     private JsonArray getDeviceListFromResponse(ContentResponse response) {
177         /* work with response */
178         if (response.getStatus() == 200) {
179             String rBody = response.getContentAsString();
180             JsonObject jsonObject = gson.fromJson(rBody, JsonObject.class);
181             if (jsonObject != null) {
182                 /* get errocode (0=success) */
183                 Integer errorCode = jsonObject.get("error_code").getAsInt();
184                 if (errorCode == 0) {
185                     JsonObject result = jsonObject.getAsJsonObject("result");
186                     return result.getAsJsonArray("deviceList");
187                 } else {
188                     /* return errorcode from device */
189                     handleError(new TapoErrorHandler(errorCode, "device answers with errorcode"));
190                     logger.trace("cloud returns error: '{}'", rBody);
191                 }
192             } else {
193                 logger.trace("enexpected json-response '{}'", rBody);
194             }
195         } else {
196             logger.trace("response error '{}'", response.getContentAsString());
197         }
198         return new JsonArray();
199     }
200
201     /***********************************
202      *
203      * HTTP-ACTIONS
204      *
205      ************************************/
206     /**
207      * SEND SYNCHRON HTTP-REQUEST
208      * 
209      * @param url url request is sent to
210      * @param payload payload (String) to send
211      * @return ContentResponse of request
212      */
213     @Nullable
214     protected ContentResponse sendCloudRequest(String url, String payload) {
215         Request httpRequest = httpClient.newRequest(url).method(HttpMethod.POST.toString());
216
217         /* set header */
218         httpRequest.header("content-type", CONTENT_TYPE_JSON);
219         httpRequest.header("Accept", CONTENT_TYPE_JSON);
220
221         /* add request body */
222         httpRequest.content(new StringContentProvider(payload, CONTENT_CHARSET), CONTENT_TYPE_JSON);
223
224         try {
225             return 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_BINDING_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 }