2 * Copyright (c) 2010-2023 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.juicenet.internal.api;
15 import java.util.Arrays;
16 import java.util.HashMap;
17 import java.util.List;
19 import java.util.Objects;
20 import java.util.concurrent.ExecutionException;
21 import java.util.concurrent.TimeUnit;
22 import java.util.concurrent.TimeoutException;
24 import org.eclipse.jdt.annotation.NonNullByDefault;
25 import org.eclipse.jdt.annotation.Nullable;
26 import org.eclipse.jetty.client.HttpClient;
27 import org.eclipse.jetty.client.api.ContentResponse;
28 import org.eclipse.jetty.client.api.Request;
29 import org.eclipse.jetty.client.util.StringContentProvider;
30 import org.eclipse.jetty.http.HttpHeader;
31 import org.eclipse.jetty.http.HttpStatus;
32 import org.openhab.binding.juicenet.internal.api.dto.JuiceNetApiDevice;
33 import org.openhab.binding.juicenet.internal.api.dto.JuiceNetApiDeviceStatus;
34 import org.openhab.binding.juicenet.internal.api.dto.JuiceNetApiInfo;
35 import org.openhab.binding.juicenet.internal.api.dto.JuiceNetApiTouSchedule;
36 import org.openhab.core.thing.ThingUID;
37 import org.slf4j.Logger;
38 import org.slf4j.LoggerFactory;
40 import com.google.gson.Gson;
41 import com.google.gson.JsonElement;
42 import com.google.gson.JsonObject;
43 import com.google.gson.JsonParser;
44 import com.google.gson.JsonSyntaxException;
47 * The {@link JuiceNetApi} is responsible for implementing the api interface to the JuiceNet cloud server
49 * @author Jeff James - Initial contribution
52 public class JuiceNetApi {
53 private final Logger logger = LoggerFactory.getLogger(JuiceNetApi.class);
55 private static final String API_HOST = "https://jbv1-api.emotorwerks.com/";
56 private static final String API_ACCOUNT = API_HOST + "box_pin";
57 private static final String API_DEVICE = API_HOST + "box_api_secure";
58 private static final int REQUEST_TIMEOUT_MS = 10_000;
60 private String apiToken = "";
61 private HttpClient httpClient;
62 private ThingUID bridgeUID;
64 public enum ApiCommand {
65 GET_ACCOUNT_UNITS("get_account_units", API_ACCOUNT),
66 GET_STATE("get_state", API_DEVICE),
67 SET_CHARGING_LIMIT("set_limit", API_DEVICE),
68 GET_SCHEDULE("get_schedule", API_DEVICE),
69 SET_SCHEDULE("set_schedule", API_DEVICE),
70 GET_INFO("get_info", API_DEVICE),
71 SET_OVERRIDE("set_override", API_DEVICE);
76 ApiCommand(String command, String uri) {
77 this.command = command;
82 public JuiceNetApi(HttpClient httpClient, ThingUID bridgeUID) {
83 this.bridgeUID = bridgeUID;
84 this.httpClient = httpClient;
87 public void setApiToken(String apiToken) {
88 this.apiToken = apiToken;
91 public List<JuiceNetApiDevice> queryDeviceList() throws JuiceNetApiException, InterruptedException {
92 JuiceNetApiDevice[] listDevices;
94 JsonObject jsonResponse = postApiCommand(ApiCommand.GET_ACCOUNT_UNITS, null);
96 JsonElement unitsElement = jsonResponse.get("units");
97 if (unitsElement == null) {
98 throw new JuiceNetApiException("getDevices from Juicenet API failed, no 'units' element in response.");
101 listDevices = new Gson().fromJson(unitsElement.getAsJsonArray(), JuiceNetApiDevice[].class);
102 } catch (JsonSyntaxException e) {
103 throw new JuiceNetApiException("getDevices from JuiceNet API failed, invalid JSON list.");
104 } catch (IllegalStateException e) {
105 throw new JuiceNetApiException("getDevices from JuiceNet API failed - did not return valid array.");
108 return Arrays.asList(listDevices);
111 public JuiceNetApiDeviceStatus queryDeviceStatus(String token) throws JuiceNetApiException, InterruptedException {
112 JuiceNetApiDeviceStatus deviceStatus;
114 JsonObject jsonResponse = postApiCommand(ApiCommand.GET_STATE, token);
116 deviceStatus = new Gson().fromJson(jsonResponse, JuiceNetApiDeviceStatus.class);
117 } catch (JsonSyntaxException e) {
118 throw new JuiceNetApiException("queryDeviceStatus from JuiceNet API failed, invalid JSON list.");
119 } catch (IllegalStateException e) {
120 throw new JuiceNetApiException("queryDeviceStatus from JuiceNet API failed - did not return valid array.");
123 return Objects.requireNonNull(deviceStatus);
126 public JuiceNetApiInfo queryInfo(String token) throws InterruptedException, JuiceNetApiException {
127 JuiceNetApiInfo info;
129 JsonObject jsonResponse = postApiCommand(ApiCommand.GET_INFO, token);
131 info = new Gson().fromJson(jsonResponse, JuiceNetApiInfo.class);
132 } catch (JsonSyntaxException e) {
133 throw new JuiceNetApiException("queryInfo from JuiceNet API failed, invalid JSON list.");
134 } catch (IllegalStateException e) {
135 throw new JuiceNetApiException("queryInfo from JuiceNet API failed - did not return valid array.");
138 return Objects.requireNonNull(info);
141 public JuiceNetApiTouSchedule queryTOUSchedule(String token) throws InterruptedException, JuiceNetApiException {
142 JuiceNetApiTouSchedule deviceTouSchedule;
144 JsonObject jsonResponse = postApiCommand(ApiCommand.GET_SCHEDULE, token);
146 deviceTouSchedule = new Gson().fromJson(jsonResponse, JuiceNetApiTouSchedule.class);
147 } catch (JsonSyntaxException e) {
148 throw new JuiceNetApiException("queryTOUSchedule from JuiceNet API failed, invalid JSON list.");
149 } catch (IllegalStateException e) {
150 throw new JuiceNetApiException("queryTOUSchedule from JuiceNet API failed - did not return valid array.");
153 return Objects.requireNonNull(deviceTouSchedule);
156 public void setOverride(String token, int energy_at_plugin, Long override_time, int energy_to_add)
157 throws InterruptedException, JuiceNetApiException {
158 Map<String, Object> params = new HashMap<>();
160 params.put("energy_at_plugin", Integer.toString(energy_at_plugin));
161 params.put("override_time", Long.toString(override_time));
162 params.put("energy_to_add", Integer.toString(energy_to_add));
164 postApiCommand(ApiCommand.SET_OVERRIDE, token, params);
167 public void setCurrentLimit(String token, int limit) throws InterruptedException, JuiceNetApiException {
168 Map<String, Object> params = new HashMap<>();
170 params.put("amperage", Integer.toString(limit));
172 postApiCommand(ApiCommand.SET_OVERRIDE, token, params);
175 public JsonObject postApiCommand(ApiCommand cmd, @Nullable String token)
176 throws InterruptedException, JuiceNetApiException {
177 Map<String, Object> params = new HashMap<>();
179 return postApiCommand(cmd, token, params);
182 public JsonObject postApiCommand(ApiCommand cmd, @Nullable String token, Map<String, Object> params)
183 throws InterruptedException, JuiceNetApiException {
184 Request request = httpClient.POST(cmd.uri);
185 request.timeout(REQUEST_TIMEOUT_MS, TimeUnit.MILLISECONDS);
186 request.header(HttpHeader.CONTENT_TYPE, "application/json");
188 // Add required params
189 params.put("cmd", cmd.command);
190 params.put("device_id", bridgeUID.getAsString());
191 params.put("account_token", apiToken);
194 params.put("token", token);
197 JsonObject jsonResponse;
199 request.content(new StringContentProvider(new Gson().toJson(params)), "application/json");
200 ContentResponse response = request.send();
201 if (response.getStatus() != HttpStatus.OK_200) {
202 throw new JuiceNetApiException(
203 cmd.command + "from JuiceNet API unsuccessful, please check configuation. (HTTP code :"
204 + response.getStatus() + ").");
207 String responseString = response.getContentAsString();
208 logger.trace("{}", responseString);
210 jsonResponse = JsonParser.parseString(responseString).getAsJsonObject();
211 JsonElement successElement = jsonResponse.get("success");
212 if (successElement == null) {
213 throw new JuiceNetApiException(
214 cmd.command + " from JuiceNet API failed, 'success' element missing from response.");
216 boolean success = successElement.getAsBoolean();
219 throw new JuiceNetApiException(cmd.command + " from JuiceNet API failed, please check configuration.");
221 } catch (IllegalStateException e) {
222 throw new JuiceNetApiException(cmd.command + " from JuiceNet API failed, invalid JSON.");
223 } catch (TimeoutException e) {
224 throw new JuiceNetApiException(cmd.command + " from JuiceNet API timeout.");
225 } catch (ExecutionException e) {
226 throw new JuiceNetApiException(cmd.command + " from JuiceNet API execution issue.");