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.TimeoutException;
23 import org.eclipse.jdt.annotation.NonNullByDefault;
24 import org.eclipse.jdt.annotation.Nullable;
25 import org.eclipse.jetty.client.HttpClient;
26 import org.eclipse.jetty.client.api.ContentResponse;
27 import org.eclipse.jetty.client.api.Request;
28 import org.eclipse.jetty.client.util.StringContentProvider;
29 import org.eclipse.jetty.http.HttpHeader;
30 import org.eclipse.jetty.http.HttpStatus;
31 import org.openhab.binding.juicenet.internal.api.dto.JuiceNetApiDevice;
32 import org.openhab.binding.juicenet.internal.api.dto.JuiceNetApiDeviceStatus;
33 import org.openhab.binding.juicenet.internal.api.dto.JuiceNetApiInfo;
34 import org.openhab.binding.juicenet.internal.api.dto.JuiceNetApiTouSchedule;
35 import org.openhab.core.thing.ThingUID;
36 import org.slf4j.Logger;
37 import org.slf4j.LoggerFactory;
39 import com.google.gson.Gson;
40 import com.google.gson.JsonElement;
41 import com.google.gson.JsonObject;
42 import com.google.gson.JsonParser;
43 import com.google.gson.JsonSyntaxException;
46 * The {@link JuiceNetApi} is responsible for implementing the api interface to the JuiceNet cloud server
48 * @author Jeff James - Initial contribution
51 public class JuiceNetApi {
52 private final Logger logger = LoggerFactory.getLogger(JuiceNetApi.class);
54 private static final String API_HOST = "https://jbv1-api.emotorwerks.com/";
55 private static final String API_ACCOUNT = API_HOST + "box_pin";
56 private static final String API_DEVICE = API_HOST + "box_api_secure";
58 private String apiToken = "";
59 private HttpClient httpClient;
60 private ThingUID bridgeUID;
62 public enum ApiCommand {
63 GET_ACCOUNT_UNITS("get_account_units", API_ACCOUNT),
64 GET_STATE("get_state", API_DEVICE),
65 SET_CHARGING_LIMIT("set_limit", API_DEVICE),
66 GET_SCHEDULE("get_schedule", API_DEVICE),
67 SET_SCHEDULE("set_schedule", API_DEVICE),
68 GET_INFO("get_info", API_DEVICE),
69 SET_OVERRIDE("set_override", API_DEVICE);
74 ApiCommand(String command, String uri) {
75 this.command = command;
80 public JuiceNetApi(HttpClient httpClient, ThingUID bridgeUID) {
81 this.bridgeUID = bridgeUID;
82 this.httpClient = httpClient;
85 public void setApiToken(String apiToken) {
86 this.apiToken = apiToken;
89 public List<JuiceNetApiDevice> queryDeviceList() throws JuiceNetApiException, InterruptedException {
90 JuiceNetApiDevice[] listDevices;
92 JsonObject jsonResponse = postApiCommand(ApiCommand.GET_ACCOUNT_UNITS, null);
94 JsonElement unitsElement = jsonResponse.get("units");
95 if (unitsElement == null) {
96 throw new JuiceNetApiException("getDevices from Juicenet API failed, no 'units' element in response.");
99 listDevices = new Gson().fromJson(unitsElement.getAsJsonArray(), JuiceNetApiDevice[].class);
100 } catch (JsonSyntaxException e) {
101 throw new JuiceNetApiException("getDevices from JuiceNet API failed, invalid JSON list.");
102 } catch (IllegalStateException e) {
103 throw new JuiceNetApiException("getDevices from JuiceNet API failed - did not return valid array.");
106 return Arrays.asList(listDevices);
109 public JuiceNetApiDeviceStatus queryDeviceStatus(String token) throws JuiceNetApiException, InterruptedException {
110 JuiceNetApiDeviceStatus deviceStatus;
112 JsonObject jsonResponse = postApiCommand(ApiCommand.GET_STATE, token);
114 deviceStatus = new Gson().fromJson(jsonResponse, JuiceNetApiDeviceStatus.class);
115 } catch (JsonSyntaxException e) {
116 throw new JuiceNetApiException("queryDeviceStatus from JuiceNet API failed, invalid JSON list.");
117 } catch (IllegalStateException e) {
118 throw new JuiceNetApiException("queryDeviceStatus from JuiceNet API failed - did not return valid array.");
121 return Objects.requireNonNull(deviceStatus);
124 public JuiceNetApiInfo queryInfo(String token) throws InterruptedException, JuiceNetApiException {
125 JuiceNetApiInfo info;
127 JsonObject jsonResponse = postApiCommand(ApiCommand.GET_INFO, token);
129 info = new Gson().fromJson(jsonResponse, JuiceNetApiInfo.class);
130 } catch (JsonSyntaxException e) {
131 throw new JuiceNetApiException("queryInfo from JuiceNet API failed, invalid JSON list.");
132 } catch (IllegalStateException e) {
133 throw new JuiceNetApiException("queryInfo from JuiceNet API failed - did not return valid array.");
136 return Objects.requireNonNull(info);
139 public JuiceNetApiTouSchedule queryTOUSchedule(String token) throws InterruptedException, JuiceNetApiException {
140 JuiceNetApiTouSchedule deviceTouSchedule;
142 JsonObject jsonResponse = postApiCommand(ApiCommand.GET_SCHEDULE, token);
144 deviceTouSchedule = new Gson().fromJson(jsonResponse, JuiceNetApiTouSchedule.class);
145 } catch (JsonSyntaxException e) {
146 throw new JuiceNetApiException("queryTOUSchedule from JuiceNet API failed, invalid JSON list.");
147 } catch (IllegalStateException e) {
148 throw new JuiceNetApiException("queryTOUSchedule from JuiceNet API failed - did not return valid array.");
151 return Objects.requireNonNull(deviceTouSchedule);
154 public void setOverride(String token, int energy_at_plugin, Long override_time, int energy_to_add)
155 throws InterruptedException, JuiceNetApiException {
156 Map<String, Object> params = new HashMap<>();
158 params.put("energy_at_plugin", Integer.toString(energy_at_plugin));
159 params.put("override_time", Long.toString(override_time));
160 params.put("energy_to_add", Integer.toString(energy_to_add));
162 postApiCommand(ApiCommand.SET_OVERRIDE, token, params);
165 public void setCurrentLimit(String token, int limit) throws InterruptedException, JuiceNetApiException {
166 Map<String, Object> params = new HashMap<>();
168 params.put("amperage", Integer.toString(limit));
170 postApiCommand(ApiCommand.SET_OVERRIDE, token, params);
173 public JsonObject postApiCommand(ApiCommand cmd, @Nullable String token)
174 throws InterruptedException, JuiceNetApiException {
175 Map<String, Object> params = new HashMap<>();
177 return postApiCommand(cmd, token, params);
180 public JsonObject postApiCommand(ApiCommand cmd, @Nullable String token, Map<String, Object> params)
181 throws InterruptedException, JuiceNetApiException {
182 Request request = httpClient.POST(cmd.uri);
183 request.header(HttpHeader.CONTENT_TYPE, "application/json");
185 // Add required params
186 params.put("cmd", cmd.command);
187 params.put("device_id", bridgeUID.getAsString());
188 params.put("account_token", apiToken);
191 params.put("token", token);
194 JsonObject jsonResponse;
196 request.content(new StringContentProvider(new Gson().toJson(params)), "application/json");
197 ContentResponse response = request.send();
198 if (response.getStatus() != HttpStatus.OK_200) {
199 throw new JuiceNetApiException(
200 cmd.command + "from JuiceNet API unsuccessful, please check configuation. (HTTP code :"
201 + response.getStatus() + ").");
204 String responseString = response.getContentAsString();
205 logger.trace("{}", responseString);
207 jsonResponse = JsonParser.parseString(responseString).getAsJsonObject();
208 JsonElement successElement = jsonResponse.get("success");
209 if (successElement == null) {
210 throw new JuiceNetApiException(
211 cmd.command + " from JuiceNet API failed, 'success' element missing from response.");
213 boolean success = successElement.getAsBoolean();
216 throw new JuiceNetApiException(cmd.command + " from JuiceNet API failed, please check configuration.");
218 } catch (IllegalStateException e) {
219 throw new JuiceNetApiException(cmd.command + " from JuiceNet API failed, invalid JSON.");
220 } catch (TimeoutException e) {
221 throw new JuiceNetApiException(cmd.command + " from JuiceNet API timeout.");
222 } catch (ExecutionException e) {
223 throw new JuiceNetApiException(cmd.command + " from JuiceNet API execution issue.");