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