]> git.basschouten.com Git - openhab-addons.git/blob
b4411672d80a1692ebbf403610c216af600b79ea
[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.electroluxair.internal.api;
14
15 import java.util.Map;
16 import java.util.concurrent.ExecutionException;
17 import java.util.concurrent.TimeoutException;
18
19 import org.eclipse.jdt.annotation.NonNullByDefault;
20 import org.eclipse.jetty.client.HttpClient;
21 import org.eclipse.jetty.client.api.ContentResponse;
22 import org.eclipse.jetty.client.api.Request;
23 import org.eclipse.jetty.client.util.StringContentProvider;
24 import org.eclipse.jetty.http.HttpHeader;
25 import org.eclipse.jetty.http.HttpMethod;
26 import org.eclipse.jetty.http.HttpStatus;
27 import org.openhab.binding.electroluxair.internal.ElectroluxAirBridgeConfiguration;
28 import org.openhab.binding.electroluxair.internal.ElectroluxAirException;
29 import org.openhab.binding.electroluxair.internal.dto.ElectroluxPureA9DTO;
30 import org.openhab.binding.electroluxair.internal.dto.ElectroluxPureA9DTO.AppliancesInfo;
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.JsonElement;
37 import com.google.gson.JsonObject;
38 import com.google.gson.JsonParser;
39 import com.google.gson.JsonSyntaxException;
40 import com.google.gson.annotations.SerializedName;
41
42 /**
43  * The {@link ElectroluxDeltaAPI} class defines the Elextrolux Delta API
44  *
45  * @author Jan Gustafsson - Initial contribution
46  */
47 @NonNullByDefault
48 public class ElectroluxDeltaAPI {
49     private static final String CLIENT_URL = "https://electrolux-wellbeing-client.vercel.app/api/mu52m5PR9X";
50     private static final String SERVICE_URL = "https://api.delta.electrolux.com/api/";
51     private static final String JSON_CONTENT_TYPE = "application/json";
52     private static final String LOGIN = "Users/Login";
53     private static final int MAX_RETRIES = 3;
54
55     private final Logger logger = LoggerFactory.getLogger(ElectroluxDeltaAPI.class);
56     private final Gson gson;
57     private final HttpClient httpClient;
58     private final ElectroluxAirBridgeConfiguration configuration;
59     private String authToken = "";
60
61     public ElectroluxDeltaAPI(ElectroluxAirBridgeConfiguration configuration, Gson gson, HttpClient httpClient) {
62         this.gson = gson;
63         this.configuration = configuration;
64         this.httpClient = httpClient;
65     }
66
67     public boolean refresh(Map<String, ElectroluxPureA9DTO> electroluxAirThings) {
68         try {
69             // Login
70             login();
71             // Get all appliances
72             String json = getAppliances();
73             JsonArray jsonArray = JsonParser.parseString(json).getAsJsonArray();
74
75             for (JsonElement jsonElement : jsonArray) {
76                 String pncId = jsonElement.getAsJsonObject().get("pncId").getAsString();
77
78                 // Get appliance info
79                 String jsonApplianceInfo = getAppliancesInfo(pncId);
80                 AppliancesInfo appliancesInfo = gson.fromJson(jsonApplianceInfo, AppliancesInfo.class);
81
82                 // Get applicance data
83                 ElectroluxPureA9DTO dto = getAppliancesData(pncId, ElectroluxPureA9DTO.class);
84                 if (appliancesInfo != null) {
85                     dto.setApplicancesInfo(appliancesInfo);
86                 }
87                 electroluxAirThings.put(dto.getTwin().getProperties().getReported().deviceId, dto);
88             }
89             return true;
90         } catch (ElectroluxAirException e) {
91             logger.warn("Failed to refresh! {}", e.getMessage());
92         }
93         return false;
94     }
95
96     public boolean workModePowerOff(String pncId) {
97         String commandJSON = "{ \"WorkMode\": \"PowerOff\" }";
98         try {
99             return sendCommand(commandJSON, pncId);
100         } catch (ElectroluxAirException e) {
101             logger.warn("Work mode powerOff failed {}", e.getMessage());
102         }
103         return false;
104     }
105
106     public boolean workModeAuto(String pncId) {
107         String commandJSON = "{ \"WorkMode\": \"Auto\" }";
108         try {
109             return sendCommand(commandJSON, pncId);
110         } catch (ElectroluxAirException e) {
111             logger.warn("Work mode auto failed {}", e.getMessage());
112         }
113         return false;
114     }
115
116     public boolean workModeManual(String pncId) {
117         String commandJSON = "{ \"WorkMode\": \"Manual\" }";
118         try {
119             return sendCommand(commandJSON, pncId);
120         } catch (ElectroluxAirException e) {
121             logger.warn("Work mode manual failed {}", e.getMessage());
122         }
123         return false;
124     }
125
126     public boolean setFanSpeedLevel(String pncId, int fanSpeedLevel) {
127         if (fanSpeedLevel < 1 && fanSpeedLevel > 10) {
128             return false;
129         } else {
130             String commandJSON = "{ \"Fanspeed\": " + fanSpeedLevel + "}";
131             try {
132                 return sendCommand(commandJSON, pncId);
133             } catch (ElectroluxAirException e) {
134                 logger.warn("Work mode manual failed {}", e.getMessage());
135             }
136         }
137         return false;
138     }
139
140     public boolean setIonizer(String pncId, String ionizerStatus) {
141         String commandJSON = "{ \"Ionizer\": " + ionizerStatus + "}";
142         try {
143             return sendCommand(commandJSON, pncId);
144         } catch (ElectroluxAirException e) {
145             logger.warn("Work mode manual failed {}", e.getMessage());
146         }
147         return false;
148     }
149
150     private void login() throws ElectroluxAirException {
151         // Fetch ClientToken
152         Request request = httpClient.newRequest(CLIENT_URL).method(HttpMethod.GET);
153
154         request.header(HttpHeader.ACCEPT, JSON_CONTENT_TYPE);
155         request.header(HttpHeader.CONTENT_TYPE, JSON_CONTENT_TYPE);
156
157         logger.debug("HTTP GET Request {}.", request.toString());
158         try {
159             ContentResponse httpResponse = request.send();
160             if (httpResponse.getStatus() != HttpStatus.OK_200) {
161                 throw new ElectroluxAirException("Failed to login " + httpResponse.getContentAsString());
162             }
163             String json = httpResponse.getContentAsString();
164             JsonObject jsonObject = JsonParser.parseString(json).getAsJsonObject();
165             String clientToken = jsonObject.get("accessToken").getAsString();
166
167             // Login using ClientToken
168             json = "{ \"Username\": \"" + configuration.username + "\",  \"Password\": \"" + configuration.password
169                     + "\" }";
170             request = httpClient.newRequest(SERVICE_URL + LOGIN).method(HttpMethod.POST);
171             request.header(HttpHeader.ACCEPT, JSON_CONTENT_TYPE);
172             request.header(HttpHeader.CONTENT_TYPE, JSON_CONTENT_TYPE);
173             request.header(HttpHeader.AUTHORIZATION, "Bearer " + clientToken);
174             request.content(new StringContentProvider(json), JSON_CONTENT_TYPE);
175
176             logger.debug("HTTP POST Request {}.", request.toString());
177
178             httpResponse = request.send();
179             if (httpResponse.getStatus() != HttpStatus.OK_200) {
180                 throw new ElectroluxAirException("Failed to login " + httpResponse.getContentAsString());
181             }
182             // Fetch AccessToken
183             json = httpResponse.getContentAsString();
184             jsonObject = JsonParser.parseString(json).getAsJsonObject();
185             this.authToken = jsonObject.get("accessToken").getAsString();
186         } catch (InterruptedException | TimeoutException | ExecutionException e) {
187             throw new ElectroluxAirException(e);
188         }
189     }
190
191     private String getFromApi(String uri) throws ElectroluxAirException, InterruptedException {
192         try {
193             for (int i = 0; i < MAX_RETRIES; i++) {
194                 try {
195                     Request request = httpClient.newRequest(SERVICE_URL + uri).method(HttpMethod.GET);
196                     request.header(HttpHeader.AUTHORIZATION, "Bearer " + authToken);
197                     request.header(HttpHeader.ACCEPT, JSON_CONTENT_TYPE);
198                     request.header(HttpHeader.CONTENT_TYPE, JSON_CONTENT_TYPE);
199
200                     ContentResponse response = request.send();
201                     String content = response.getContentAsString();
202                     logger.trace("API response: {}", content);
203
204                     if (response.getStatus() != HttpStatus.OK_200) {
205                         logger.debug("getFromApi failed, HTTP status: {}", response.getStatus());
206                         login();
207                     } else {
208                         return content;
209                     }
210                 } catch (TimeoutException e) {
211                     logger.debug("TimeoutException error in get: {}", e.getMessage());
212                 }
213             }
214             throw new ElectroluxAirException("Failed to fetch from API!");
215         } catch (JsonSyntaxException | ElectroluxAirException | ExecutionException e) {
216             throw new ElectroluxAirException(e);
217         }
218     }
219
220     private String getAppliances() throws ElectroluxAirException {
221         String uri = "Domains/Appliances";
222         try {
223             return getFromApi(uri);
224         } catch (ElectroluxAirException | InterruptedException e) {
225             throw new ElectroluxAirException(e);
226         }
227     }
228
229     private String getAppliancesInfo(String pncId) throws ElectroluxAirException {
230         String uri = "AppliancesInfo/" + pncId;
231         try {
232             return getFromApi(uri);
233         } catch (ElectroluxAirException | InterruptedException e) {
234             throw new ElectroluxAirException(e);
235         }
236     }
237
238     private <T> T getAppliancesData(String pncId, Class<T> dto) throws ElectroluxAirException {
239         String uri = "Appliances/" + pncId;
240         String json;
241
242         try {
243             json = getFromApi(uri);
244         } catch (ElectroluxAirException | InterruptedException e) {
245             throw new ElectroluxAirException(e);
246         }
247         return gson.fromJson(json, dto);
248     }
249
250     private boolean sendCommand(String commandJSON, String pncId) throws ElectroluxAirException {
251         String uri = "Appliances/" + pncId + "/Commands";
252         try {
253             for (int i = 0; i < MAX_RETRIES; i++) {
254                 try {
255                     Request request = httpClient.newRequest(SERVICE_URL + uri).method(HttpMethod.PUT);
256                     request.header(HttpHeader.AUTHORIZATION, "Bearer " + authToken);
257                     request.header(HttpHeader.ACCEPT, JSON_CONTENT_TYPE);
258                     request.header(HttpHeader.CONTENT_TYPE, JSON_CONTENT_TYPE);
259                     request.content(new StringContentProvider(commandJSON), JSON_CONTENT_TYPE);
260
261                     ContentResponse response = request.send();
262                     String content = response.getContentAsString();
263                     logger.trace("API response: {}", content);
264
265                     if (response.getStatus() != HttpStatus.OK_200) {
266                         logger.debug("sendCommand failed, HTTP status: {}", response.getStatus());
267                         login();
268                     } else {
269                         CommandResponseDTO commandResponse = gson.fromJson(content, CommandResponseDTO.class);
270                         if (commandResponse != null) {
271                             if (commandResponse.code == 200000) {
272                                 return true;
273                             } else {
274                                 logger.warn("Failed to send command, error code: {}, description: {}",
275                                         commandResponse.code, commandResponse.codeDescription);
276                                 return false;
277                             }
278                         } else {
279                             logger.warn("Failed to send command, commandResponse is null!");
280                             return false;
281                         }
282                     }
283                 } catch (TimeoutException | InterruptedException e) {
284                     logger.warn("TimeoutException error in get");
285                 }
286             }
287         } catch (JsonSyntaxException | ElectroluxAirException | ExecutionException e) {
288             throw new ElectroluxAirException(e);
289         }
290         return false;
291     }
292
293     @SuppressWarnings("unused")
294     private static class CommandResponseDTO {
295         public int code;
296         public String codeDescription = "";
297         public String information = "";
298         public String message = "";
299         public PayloadDTO payload = new PayloadDTO();
300         public int status;
301     }
302
303     private static class PayloadDTO {
304         @SerializedName("Ok")
305         public boolean ok;
306         @SerializedName("Response")
307         public ResponseDTO response = new ResponseDTO();
308     }
309
310     private static class ResponseDTO {
311         @SerializedName("Workmode")
312         public String workmode = "";
313     }
314 }