]> git.basschouten.com Git - openhab-addons.git/blob
1a93df2916f36af028fba780e03e9ac3ed6fcc5e
[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.goecharger.internal.handler;
14
15 import static org.openhab.binding.goecharger.internal.GoEChargerBindingConstants.*;
16
17 import java.util.concurrent.ExecutionException;
18 import java.util.concurrent.TimeUnit;
19 import java.util.concurrent.TimeoutException;
20
21 import javax.measure.quantity.ElectricCurrent;
22 import javax.measure.quantity.Energy;
23
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.http.HttpMethod;
29 import org.openhab.binding.goecharger.internal.GoEChargerBindingConstants;
30 import org.openhab.binding.goecharger.internal.api.GoEStatusResponseBaseDTO;
31 import org.openhab.binding.goecharger.internal.api.GoEStatusResponseDTO;
32 import org.openhab.binding.goecharger.internal.api.GoEStatusResponseV2DTO;
33 import org.openhab.core.library.types.DecimalType;
34 import org.openhab.core.library.types.OnOffType;
35 import org.openhab.core.library.types.QuantityType;
36 import org.openhab.core.library.types.StringType;
37 import org.openhab.core.library.unit.SIUnits;
38 import org.openhab.core.library.unit.Units;
39 import org.openhab.core.thing.ChannelUID;
40 import org.openhab.core.thing.Thing;
41 import org.openhab.core.thing.ThingStatus;
42 import org.openhab.core.thing.ThingStatusDetail;
43 import org.openhab.core.types.Command;
44 import org.openhab.core.types.RefreshType;
45 import org.openhab.core.types.State;
46 import org.openhab.core.types.UnDefType;
47 import org.slf4j.Logger;
48 import org.slf4j.LoggerFactory;
49
50 import com.google.gson.JsonSyntaxException;
51 import com.google.gson.annotations.SerializedName;
52
53 /**
54  * The {@link GoEChargerV2Handler} is responsible for handling commands, which are
55  * sent to one of the channels.
56  *
57  * @author Samuel Brucksch - Initial contribution
58  * @author Reinhard Plaim - Adapt to use API version 2
59  */
60 @NonNullByDefault
61 public class GoEChargerV2Handler extends GoEChargerBaseHandler {
62
63     private final Logger logger = LoggerFactory.getLogger(GoEChargerV2Handler.class);
64
65     private String filter = "";
66
67     public GoEChargerV2Handler(Thing thing, HttpClient httpClient) {
68         super(thing, httpClient);
69     }
70
71     @Override
72     protected State getValue(String channelId, GoEStatusResponseBaseDTO goeResponseBase) {
73         var state = super.getValue(channelId, goeResponseBase);
74         if (state != UnDefType.UNDEF) {
75             return state;
76         }
77
78         var goeResponse = (GoEStatusResponseV2DTO) goeResponseBase;
79         switch (channelId) {
80             case PHASES:
81                 if (goeResponse.phases == null) {
82                     return UnDefType.UNDEF;
83                 }
84                 var phases = "1";
85                 if (goeResponse.phases == 2) {
86                     phases = "3";
87                 }
88                 return new DecimalType(phases);
89             case PWM_SIGNAL:
90                 if (goeResponse.pwmSignal == null) {
91                     return UnDefType.UNDEF;
92                 }
93                 String pwmSignal = null;
94                 switch (goeResponse.pwmSignal) {
95                     case 0:
96                         pwmSignal = "UNKNOWN/ERROR";
97                     case 1:
98                         pwmSignal = "IDLE";
99                         break;
100                     case 2:
101                         pwmSignal = "CHARGING";
102                         break;
103                     case 3:
104                         pwmSignal = "WAITING_FOR_CAR";
105                         break;
106                     case 4:
107                         pwmSignal = "COMPLETE";
108                         break;
109                     case 5:
110                         pwmSignal = "ERROR";
111                     default:
112                 }
113                 return new StringType(pwmSignal);
114             case ERROR:
115                 if (goeResponse.errorCode == null) {
116                     return UnDefType.UNDEF;
117                 }
118                 String error = null;
119                 switch (goeResponse.errorCode) {
120                     case 0:
121                         error = "UNKNOWN/ERROR";
122                     case 1:
123                         error = "IDLE";
124                         break;
125                     case 2:
126                         error = "CHARGING";
127                         break;
128                     case 3:
129                         error = "WAITING_FOR_CAR";
130                         break;
131                     case 4:
132                         error = "COMPLETE";
133                         break;
134                     case 5:
135                         error = "ERROR";
136                     default:
137                 }
138                 return new StringType(error);
139             case TRANSACTION:
140                 if (goeResponse.transaction == null) {
141                     return UnDefType.UNDEF;
142                 }
143                 return new DecimalType(goeResponse.transaction);
144             case ALLOW_CHARGING:
145                 return goeResponse.allowCharging == true ? OnOffType.ON : OnOffType.OFF;
146             case TEMPERATURE_TYPE2_PORT:
147                 if (goeResponse.temperatures == null) {
148                     return UnDefType.UNDEF;
149                 }
150                 return new QuantityType<>(goeResponse.temperatures[0], SIUnits.CELSIUS);
151             case TEMPERATURE_CIRCUIT_BOARD:
152                 if (goeResponse.temperatures == null) {
153                     return UnDefType.UNDEF;
154                 }
155                 return new QuantityType<>(goeResponse.temperatures[1], SIUnits.CELSIUS);
156             case SESSION_CHARGE_CONSUMPTION:
157                 if (goeResponse.sessionChargeConsumption == null) {
158                     return UnDefType.UNDEF;
159                 }
160                 return new QuantityType<>(goeResponse.sessionChargeConsumption / 1000d, Units.KILOWATT_HOUR);
161             case SESSION_CHARGE_CONSUMPTION_LIMIT:
162                 if (goeResponse.sessionChargeConsumptionLimit == null) {
163                     return UnDefType.UNDEF;
164                 }
165                 return new QuantityType<>(goeResponse.sessionChargeConsumptionLimit / 1000d, Units.KILOWATT_HOUR);
166             case TOTAL_CONSUMPTION:
167                 if (goeResponse.totalChargeConsumption == null) {
168                     return UnDefType.UNDEF;
169                 }
170                 return new QuantityType<>((Double) (goeResponse.totalChargeConsumption / 1000d), Units.KILOWATT_HOUR);
171             case VOLTAGE_L1:
172                 if (goeResponse.energy == null) {
173                     return UnDefType.UNDEF;
174                 }
175                 return new QuantityType<>(goeResponse.energy[0], Units.VOLT);
176             case VOLTAGE_L2:
177                 if (goeResponse.energy == null) {
178                     return UnDefType.UNDEF;
179                 }
180                 return new QuantityType<>(goeResponse.energy[1], Units.VOLT);
181             case VOLTAGE_L3:
182                 if (goeResponse.energy == null) {
183                     return UnDefType.UNDEF;
184                 }
185                 return new QuantityType<>(goeResponse.energy[2], Units.VOLT);
186             case CURRENT_L1:
187                 if (goeResponse.energy == null) {
188                     return UnDefType.UNDEF;
189                 }
190                 return new QuantityType<>(goeResponse.energy[4], Units.AMPERE);
191             case CURRENT_L2:
192                 if (goeResponse.energy == null) {
193                     return UnDefType.UNDEF;
194                 }
195                 return new QuantityType<>(goeResponse.energy[5], Units.AMPERE);
196             case CURRENT_L3:
197                 if (goeResponse.energy == null) {
198                     return UnDefType.UNDEF;
199                 }
200                 return new QuantityType<>(goeResponse.energy[6], Units.AMPERE);
201             case POWER_L1:
202                 if (goeResponse.energy == null) {
203                     return UnDefType.UNDEF;
204                 }
205                 return new QuantityType<>(goeResponse.energy[7], Units.WATT);
206             case POWER_L2:
207                 if (goeResponse.energy == null) {
208                     return UnDefType.UNDEF;
209                 }
210                 return new QuantityType<>(goeResponse.energy[8], Units.WATT);
211             case POWER_L3:
212                 if (goeResponse.energy == null) {
213                     return UnDefType.UNDEF;
214                 }
215                 return new QuantityType<>(goeResponse.energy[9], Units.WATT);
216             case POWER_ALL:
217                 if (goeResponse.energy == null) {
218                     return UnDefType.UNDEF;
219                 }
220                 return new QuantityType<>(goeResponse.energy[11], Units.WATT);
221             case FORCE_STATE:
222                 if (goeResponse.forceState == null) {
223                     return UnDefType.UNDEF;
224                 }
225                 return new DecimalType(goeResponse.forceState.toString());
226         }
227         return UnDefType.UNDEF;
228     }
229
230     @Override
231     public void handleCommand(ChannelUID channelUID, Command command) {
232         if (command instanceof RefreshType) {
233             // we can not update single channels and refresh is triggered automatically
234             // anyways
235             return;
236         }
237
238         String key = null;
239         String value = null;
240
241         switch (channelUID.getId()) {
242             case MAX_CURRENT:
243                 key = "amp";
244                 if (command instanceof DecimalType) {
245                     value = String.valueOf(((DecimalType) command).intValue());
246                 } else if (command instanceof QuantityType<?>) {
247                     value = String.valueOf(((QuantityType<ElectricCurrent>) command).toUnit(Units.AMPERE).intValue());
248                 }
249                 break;
250             case SESSION_CHARGE_CONSUMPTION_LIMIT:
251                 key = "dwo";
252                 var multiplier = 1000;
253                 if (command instanceof DecimalType) {
254                     value = String.valueOf(((DecimalType) command).intValue() * multiplier);
255                 } else if (command instanceof QuantityType<?>) {
256                     value = String.valueOf(
257                             ((QuantityType<Energy>) command).toUnit(Units.KILOWATT_HOUR).intValue() * multiplier);
258                 }
259                 break;
260             case PHASES:
261                 key = "psm";
262                 if (command instanceof DecimalType) {
263                     var phases = 1;
264                     var help = (DecimalType) command;
265                     if (help.intValue() == 3) {
266                         // set value 2 for 3 phases
267                         phases = 2;
268                     }
269                     value = String.valueOf(phases);
270                 }
271                 break;
272             case FORCE_STATE:
273                 key = "frc";
274                 if (command instanceof DecimalType) {
275                     value = String.valueOf(((DecimalType) command).intValue());
276                 }
277             case TRANSACTION:
278                 key = "trx";
279                 if (command instanceof DecimalType) {
280                     value = String.valueOf(((DecimalType) command).intValue());
281                 }
282         }
283
284         if (key != null && value != null) {
285             sendData(key, value);
286         } else {
287             logger.warn("Could not update channel {} with key {} and value {}", channelUID.getId(), key, value);
288         }
289     }
290
291     @Override
292     public void initialize() {
293         // only read needed parameters
294         filter = "?filter=";
295         var declaredFields = GoEStatusResponseV2DTO.class.getDeclaredFields();
296         var declaredFieldsBase = GoEStatusResponseV2DTO.class.getSuperclass().getDeclaredFields();
297
298         for (var field : declaredFields) {
299             filter += field.getAnnotation(SerializedName.class).value() + ",";
300         }
301         for (var field : declaredFieldsBase) {
302             filter += field.getAnnotation(SerializedName.class).value() + ",";
303         }
304         filter = filter.substring(0, filter.length() - 1);
305
306         super.initialize();
307     }
308
309     private String getReadUrl() {
310         return GoEChargerBindingConstants.API_URL_V2.replace("%IP%", config.ip.toString()) + filter;
311     }
312
313     private String getWriteUrl(String key, String value) {
314         return GoEChargerBindingConstants.SET_URL_V2.replace("%IP%", config.ip.toString()).replace("%KEY%", key)
315                 .replace("%VALUE%", value);
316     }
317
318     private void sendData(String key, String value) {
319         String urlStr = getWriteUrl(key, value);
320         logger.trace("POST URL = {}", urlStr);
321
322         try {
323             HttpMethod httpMethod = HttpMethod.GET;
324             ContentResponse contentResponse = httpClient.newRequest(urlStr).method(httpMethod)
325                     .timeout(5, TimeUnit.SECONDS).send();
326             String response = contentResponse.getContentAsString();
327
328             logger.trace("{} Response: {}", httpMethod.toString(), response);
329
330             var statusCode = contentResponse.getStatus();
331             if (!(statusCode == 200 || statusCode == 204)) {
332                 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
333                         "@text/unsuccessful.communication-error");
334                 logger.debug("Could not send data, Response {}, StatusCode: {}", response, statusCode);
335             }
336         } catch (InterruptedException ie) {
337             Thread.currentThread().interrupt();
338             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, ie.toString());
339             logger.debug("Could not send data: {}, {}", urlStr, ie.toString());
340         } catch (TimeoutException | ExecutionException | JsonSyntaxException e) {
341             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.toString());
342             logger.debug("Could not send data: {}, {}", urlStr, e.toString());
343         }
344     }
345
346     /**
347      * Request new data from Go-eCharger
348      *
349      * @return the Go-eCharger object mapping the JSON response or null in case of
350      *         error
351      * @throws ExecutionException
352      * @throws TimeoutException
353      * @throws InterruptedException
354      */
355     @Nullable
356     @Override
357     protected GoEStatusResponseBaseDTO getGoEData()
358             throws InterruptedException, TimeoutException, ExecutionException, JsonSyntaxException {
359         String urlStr = getReadUrl();
360         logger.trace("GET URL = {}", urlStr);
361
362         ContentResponse contentResponse = httpClient.newRequest(urlStr).method(HttpMethod.GET)
363                 .timeout(5, TimeUnit.SECONDS).send();
364
365         String response = contentResponse.getContentAsString();
366         logger.trace("GET Response: {}", response);
367
368         if (config.apiVersion == 1) {
369             return gson.fromJson(response, GoEStatusResponseDTO.class);
370         }
371         return gson.fromJson(response, GoEStatusResponseV2DTO.class);
372     }
373
374     protected void updateChannelsAndStatus(@Nullable GoEStatusResponseBaseDTO goeResponse, @Nullable String message) {
375         if (goeResponse == null) {
376             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR, message);
377             allChannels.forEach(channel -> updateState(channel, UnDefType.UNDEF));
378         } else {
379             updateStatus(ThingStatus.ONLINE);
380             allChannels
381                     .forEach(channel -> updateState(channel, getValue(channel, (GoEStatusResponseV2DTO) goeResponse)));
382         }
383     }
384 }