]> git.basschouten.com Git - openhab-addons.git/blob
3b873b811064591a80cac4aaa8dc7b9d93dcf788
[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.kostalinverter.internal.secondgeneration;
14
15 import java.math.BigDecimal;
16 import java.security.NoSuchAlgorithmException;
17 import java.util.ArrayList;
18 import java.util.List;
19 import java.util.concurrent.ExecutionException;
20 import java.util.concurrent.ScheduledFuture;
21 import java.util.concurrent.TimeUnit;
22 import java.util.concurrent.TimeoutException;
23
24 import javax.measure.Unit;
25
26 import org.eclipse.jdt.annotation.NonNullByDefault;
27 import org.eclipse.jdt.annotation.Nullable;
28 import org.eclipse.jetty.client.HttpClient;
29 import org.openhab.core.library.types.OnOffType;
30 import org.openhab.core.library.types.QuantityType;
31 import org.openhab.core.library.types.StringType;
32 import org.openhab.core.thing.ChannelUID;
33 import org.openhab.core.thing.Thing;
34 import org.openhab.core.thing.ThingStatus;
35 import org.openhab.core.thing.ThingStatusDetail;
36 import org.openhab.core.thing.binding.BaseThingHandler;
37 import org.openhab.core.types.Command;
38 import org.openhab.core.types.State;
39 import org.openhab.core.types.UnDefType;
40 import org.slf4j.Logger;
41 import org.slf4j.LoggerFactory;
42
43 import com.google.gson.Gson;
44 import com.google.gson.GsonBuilder;
45
46 /**
47  * The {@link SecondGenerationHandler} is responsible for handling commands, which are
48  * sent to one of the channels, and initiation and refreshing regarded to second generation part of the binding.
49  *
50  *
51  * @author Christian Schneider - Initial contribution
52  * @author Christoph Weitkamp - Incorporated new QuantityType (Units of Measurement)
53  * @author Ã–rjan Backsell - Redesigned regarding Piko1020, Piko New Generation
54  */
55 @NonNullByDefault
56 public class SecondGenerationHandler extends BaseThingHandler {
57
58     private final Logger logger = LoggerFactory.getLogger(SecondGenerationHandler.class);
59
60     private @Nullable ScheduledFuture<?> secondGenerationPoller;
61
62     private final HttpClient httpClient;
63
64     private List<SecondGenerationChannelConfiguration> channelConfigs = new ArrayList<>();
65     private List<SecondGenerationChannelConfiguration> channelConfigsExt = new ArrayList<>();
66     private List<SecondGenerationChannelConfiguration> channelConfigsExtExt = new ArrayList<>();
67     private List<SecondGenerationChannelConfiguration> channelConfigsConfigurable = new ArrayList<>();
68     private List<SecondGenerationChannelConfiguration> channelConfigsAll = new ArrayList<>();
69
70     private List<String> channelPostsTemp = new ArrayList<>();
71     private List<String> channelPostsTempExt = new ArrayList<>();
72     private List<String> channelPostsTempExtExt = new ArrayList<>();
73     private List<String> channelPostsTempAll = new ArrayList<>();
74
75     private SecondGenerationInverterConfig inverterConfig = new SecondGenerationInverterConfig();
76     private Gson gson = new GsonBuilder().setPrettyPrinting().create();
77
78     public SecondGenerationHandler(Thing thing, HttpClient httpClient) {
79         super(thing);
80         this.httpClient = httpClient;
81     }
82
83     @Override
84     public void handleCommand(ChannelUID channelUID, Command command) {
85         String url = inverterConfig.url;
86         String username = inverterConfig.username;
87         String password = inverterConfig.password;
88         String valueConfiguration = "";
89         String dxsEntriesConf = "";
90
91         if (inverterConfig.hasBattery) {
92             switch (channelUID.getId()) {
93                 case SecondGenerationBindingConstants.CHANNEL_BATTERYUSAGECONSUMPTIONSET:
94                     valueConfiguration = command.toString();
95                     dxsEntriesConf = "33556249";
96                     preSetExecuteConfigurationChanges(httpClient, url, username, password, dxsEntriesConf,
97                             valueConfiguration);
98                     break;
99                 case SecondGenerationBindingConstants.CHANNEL_BATTERYUSAGESTRATEGYSET:
100                     valueConfiguration = command.toString();
101                     dxsEntriesConf = "83888896";
102                     preSetExecuteConfigurationChanges(httpClient, url, username, password, dxsEntriesConf,
103                             valueConfiguration);
104                     break;
105                 case SecondGenerationBindingConstants.CHANNEL_SMARTBATTERYCONTROLSET:
106                     if (command.toString().equals("ON")) {
107                         valueConfiguration = "true";
108                     }
109                     if (command.toString().equals("OFF")) {
110                         valueConfiguration = "false";
111                     }
112                     dxsEntriesConf = "33556484";
113                     preSetExecuteConfigurationChanges(httpClient, url, username, password, dxsEntriesConf,
114                             valueConfiguration);
115                     break;
116                 case SecondGenerationBindingConstants.CHANNEL_BATTERYCHARGETIMEFROMSET:
117                     valueConfiguration = command.toString();
118                     String valueConfigurationFromTransformed = String.valueOf(stringToSeconds(valueConfiguration));
119                     dxsEntriesConf = "33556239";
120                     preSetExecuteConfigurationChanges(httpClient, url, username, password, dxsEntriesConf,
121                             valueConfigurationFromTransformed);
122                     break;
123                 case SecondGenerationBindingConstants.CHANNEL_BATTERYCHARGETIMETOSET:
124                     valueConfiguration = command.toString();
125                     String valueConfigurationToTransformed = String.valueOf(stringToSeconds(valueConfiguration));
126                     dxsEntriesConf = "33556240";
127                     preSetExecuteConfigurationChanges(httpClient, url, username, password, dxsEntriesConf,
128                             valueConfigurationToTransformed);
129                     break;
130                 case SecondGenerationBindingConstants.CHANNEL_MAXDEPTHOFDISCHARGESET:
131                     valueConfiguration = command.toString();
132                     dxsEntriesConf = "33556247";
133                     preSetExecuteConfigurationChanges(httpClient, url, username, password, dxsEntriesConf,
134                             valueConfiguration);
135                     break;
136                 case SecondGenerationBindingConstants.CHANNEL_SHADOWMANAGEMENTSET:
137                     valueConfiguration = command.toString();
138                     dxsEntriesConf = "33556483";
139                     preSetExecuteConfigurationChanges(httpClient, url, username, password, dxsEntriesConf,
140                             valueConfiguration);
141                     break;
142                 case SecondGenerationBindingConstants.CHANNEL_EXTERNALMODULECONTROLSET:
143                     valueConfiguration = command.toString();
144                     dxsEntriesConf = "33556482";
145                     preSetExecuteConfigurationChanges(httpClient, url, username, password, dxsEntriesConf,
146                             valueConfiguration);
147                     break;
148             }
149         }
150     }
151
152     @Override
153     public void initialize() {
154         // Set channel configuration parameters
155         channelConfigs = SecondGenerationChannelConfiguration.getChannelConfiguration();
156         channelConfigsExt = SecondGenerationChannelConfiguration.getChannelConfigurationExt();
157         channelConfigsExtExt = SecondGenerationChannelConfiguration.getChannelConfigurationExtExt();
158         channelConfigsConfigurable = SecondGenerationChannelConfiguration.getChannelConfigurationConfigurable();
159
160         // Set inverter configuration parameters
161         inverterConfig = getConfigAs(SecondGenerationInverterConfig.class);
162
163         // Temporary value during initializing
164         updateStatus(ThingStatus.UNKNOWN);
165
166         // Start update as configured
167         secondGenerationPoller = scheduler.scheduleWithFixedDelay(() -> {
168             try {
169                 refresh();
170                 updateStatus(ThingStatus.ONLINE);
171             } catch (RuntimeException scheduleWithFixedDelayException) {
172                 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
173                         scheduleWithFixedDelayException.getClass().getName() + ":"
174                                 + scheduleWithFixedDelayException.getMessage());
175             }
176         }, 0, inverterConfig.refreshInterval, TimeUnit.SECONDS);
177     }
178
179     @Override
180     public void dispose() {
181         final ScheduledFuture<?> secondGenerationLocalPoller = secondGenerationPoller;
182
183         if (secondGenerationLocalPoller != null) {
184             secondGenerationLocalPoller.cancel(true);
185             secondGenerationPoller = null;
186         }
187     }
188
189     private void refresh() {
190         try {
191             // Build posts for dxsEntries part
192             String dxsEntriesCall = inverterConfig.url + "/api/dxs.json?dxsEntries=" + channelConfigs.get(0).dxsEntries;
193             for (int i = 1; i < channelConfigs.size(); i++) {
194                 dxsEntriesCall += ("&dxsEntries=" + channelConfigs.get(i).dxsEntries);
195             }
196             String jsonDxsEntriesResponse = callURL(dxsEntriesCall, httpClient);
197             SecondGenerationDxsEntriesContainerDTO dxsEntriesContainer = gson.fromJson(jsonDxsEntriesResponse,
198                     SecondGenerationDxsEntriesContainerDTO.class);
199
200             String[] channelPosts = new String[23];
201             int channelPostsCounter = 0;
202             for (SecondGenerationDxsEntries dxsentries : dxsEntriesContainer.dxsEntries) {
203                 channelPosts[channelPostsCounter] = dxsentries.getName();
204                 channelPostsCounter++;
205             }
206             channelPostsTemp = List.of(channelPosts);
207
208             // Build posts for dxsEntriesExt part
209             String dxsEntriesCallExt = inverterConfig.url + "/api/dxs.json?dxsEntries="
210                     + channelConfigsExt.get(0).dxsEntries;
211             for (int i = 1; i < channelConfigs.size(); i++) {
212                 dxsEntriesCallExt += ("&dxsEntries=" + channelConfigsExt.get(i).dxsEntries);
213             }
214             String jsonDxsEntriesResponseExt = callURL(dxsEntriesCallExt, httpClient);
215             SecondGenerationDxsEntriesContainerDTO dxsEntriesContainerExt = gson.fromJson(jsonDxsEntriesResponseExt,
216                     SecondGenerationDxsEntriesContainerDTO.class);
217             String[] channelPostsExt = new String[23];
218             int channelPostsCounterExt = 0;
219             for (SecondGenerationDxsEntries dxsentriesExt : dxsEntriesContainerExt.dxsEntries) {
220                 channelPostsExt[channelPostsCounterExt] = dxsentriesExt.getName();
221                 channelPostsCounterExt++;
222             }
223             channelPostsTempExt = List.of(channelPostsExt);
224
225             // Build posts for dxsEntriesExtExt part
226             String dxsEntriesCallExtExt = inverterConfig.url + "/api/dxs.json?dxsEntries="
227                     + channelConfigsExtExt.get(0).dxsEntries;
228             for (int i = 1; i < channelConfigsExtExt.size(); i++) {
229                 dxsEntriesCallExtExt += ("&dxsEntries=" + channelConfigsExtExt.get(i).dxsEntries);
230             }
231             String jsonDxsEntriesResponseExtExt = callURL(dxsEntriesCallExtExt, httpClient);
232             SecondGenerationDxsEntriesContainerDTO dxsEntriesContainerExtExt = gson
233                     .fromJson(jsonDxsEntriesResponseExtExt, SecondGenerationDxsEntriesContainerDTO.class);
234             String[] channelPostsExtExt = new String[3];
235             int channelPostsCounterExtExt = 0;
236             for (SecondGenerationDxsEntries dxsentriesExtExt : dxsEntriesContainerExtExt.dxsEntries) {
237                 channelPostsExtExt[channelPostsCounterExtExt] = dxsentriesExtExt.getName();
238                 channelPostsCounterExtExt++;
239             }
240             channelPostsTempExtExt = List.of(channelPostsExtExt);
241
242             // Concatenate posts for all parts except configurable channels
243             channelPostsTempAll = combinePostsLists(channelPostsTemp, channelPostsTempExt, channelPostsTempExtExt);
244             String[] channelPostsTempAll1 = channelPostsTempAll.toArray(new String[0]);
245
246             // Build posts for dxsEntriesConfigureable part
247             String[] channelPostsConfigurable = new String[5];
248             if (inverterConfig.hasBattery) {
249                 String dxsEntriesCallConfigurable = inverterConfig.url + "/api/dxs.json?dxsEntries="
250                         + channelConfigsConfigurable.get(0).dxsEntries;
251                 for (int i = 1; i < channelConfigsConfigurable.size(); i++) {
252                     dxsEntriesCallConfigurable += ("&dxsEntries=" + channelConfigsConfigurable.get(i).dxsEntries);
253                 }
254                 String jsonDxsEntriesResponseConfigurable = callURL(dxsEntriesCallConfigurable, httpClient);
255                 SecondGenerationDxsEntriesContainerDTO dxsEntriesContainerConfigurable = gson
256                         .fromJson(jsonDxsEntriesResponseConfigurable, SecondGenerationDxsEntriesContainerDTO.class);
257                 int channelPostsCounterConfigurable = 0;
258                 for (SecondGenerationDxsEntries dxsentriesConfigurable : dxsEntriesContainerConfigurable.dxsEntries) {
259                     channelPostsConfigurable[channelPostsCounterConfigurable] = dxsentriesConfigurable.getName();
260                     channelPostsCounterConfigurable++;
261                 }
262             }
263
264             // Create and update actual values for non-configurable channels
265             if (!inverterConfig.hasBattery) {
266                 channelConfigsAll = combineChannelConfigLists(channelConfigs, channelConfigsExt, channelConfigsExtExt);
267                 int channelValuesCounterAll = 0;
268                 for (SecondGenerationChannelConfiguration cConfig : channelConfigsAll) {
269                     String channel = cConfig.id;
270                     updateState(channel, getState(channelPostsTempAll1[channelValuesCounterAll], cConfig.unit));
271                     channelValuesCounterAll++;
272                 }
273             }
274             // Create and update actual values for all channels
275             if (inverterConfig.hasBattery) {
276                 // Part for updating non-configurable channels
277                 channelConfigsAll = combineChannelConfigLists(channelConfigs, channelConfigsExt, channelConfigsExtExt);
278                 // Update the non-configurable channels
279                 int channelValuesCounterAll = 0;
280                 for (SecondGenerationChannelConfiguration cConfig : channelConfigsAll) {
281                     String channel = cConfig.id;
282                     updateState(channel, getState(channelPostsTempAll1[channelValuesCounterAll], cConfig.unit));
283                     channelValuesCounterAll++;
284                 }
285
286                 // Part for updating configurable channels
287                 int channelValuesCounterConfigurable = 0;
288                 for (SecondGenerationChannelConfiguration cConfig : channelConfigsConfigurable) {
289                     String channel = cConfig.id;
290                     String value = channelPostsConfigurable[channelValuesCounterConfigurable];
291                     int dxsEntriesCheckCounter = 3;
292                     if (cConfig.dxsEntries.equals("33556484")) {
293                         dxsEntriesCheckCounter = 1;
294                     }
295                     if (cConfig.dxsEntries.equals("33556482")) {
296                         dxsEntriesCheckCounter = 2;
297                     }
298                     switch (dxsEntriesCheckCounter) {
299                         case 1:
300                             if ("false".equals(value)) {
301                                 updateState(channel, OnOffType.OFF);
302                             }
303                             if ("true".equals(value)) {
304                                 updateState(channel, OnOffType.ON);
305                             }
306                             channelValuesCounterConfigurable++;
307                             break;
308                         case 2:
309                             if ("false".equals(value)) {
310                                 State stateFalse = new StringType("0");
311                                 updateState(channel, stateFalse);
312                             }
313                             if ("true".equals(value)) {
314                                 State stateTrue = new StringType("1");
315                                 updateState(channel, stateTrue);
316                             }
317                             channelValuesCounterConfigurable++;
318                             break;
319                         case 3:
320                             State stateOther = getState(channelPostsConfigurable[channelValuesCounterConfigurable],
321                                     cConfig.unit);
322                             updateState(channel, stateOther);
323                             channelValuesCounterConfigurable++;
324                             break;
325                     }
326                 }
327             }
328         } catch (final RuntimeException e) {
329             logger.debug("Updating inverter status failed: ", e);
330             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
331         }
332     }
333
334     // Help method of handleCommand to with SecondGenerationConfigurationHandler.executeConfigurationChanges method send
335     // configuration changes.
336     private final void preSetExecuteConfigurationChanges(HttpClient httpClientHandleCommand, String url,
337             String username, String password, String dxsEntriesConf, String valueConfiguration) {
338         try {
339             SecondGenerationConfigurationHandler.executeConfigurationChanges(httpClientHandleCommand, url, username,
340                     password, dxsEntriesConf, valueConfiguration);
341         } catch (InterruptedException | ExecutionException | TimeoutException | NoSuchAlgorithmException e) {
342             logger.debug("Connection to inverter disturbed during configuration");
343         }
344     }
345
346     // Method callURL connect to inverter for value scraping
347     private final String callURL(String dxsEntriesCall, HttpClient httpClient) {
348         String jsonDxsResponse = "";
349         try {
350             jsonDxsResponse = httpClient.GET(dxsEntriesCall).getContentAsString();
351         } catch (InterruptedException | ExecutionException | TimeoutException e2) {
352             logger.debug("Connection to inverter disturbed during scrape");
353         }
354         return jsonDxsResponse;
355     }
356
357     // Method getState is used for non-configurable values
358     private State getState(String value, @Nullable Unit<?> unit) {
359         if (unit == null) {
360             return new StringType(value);
361         } else {
362             try {
363                 return new QuantityType<>(new BigDecimal(value), unit);
364             } catch (NumberFormatException getStateException) {
365                 logger.debug("Error parsing value '{}: {}'", value, getStateException.getMessage());
366                 return UnDefType.UNDEF;
367             }
368         }
369     }
370
371     // Method stringToSeconds transform given time in 00:16 syntax to seconds syntax
372     private static long stringToSeconds(String stringTime) {
373         long secondsMin = Long.parseLong(stringTime.substring(3, 5)) * 60;
374         long secondsHrs = Long.parseLong(stringTime.substring(0, 2)) * 3600;
375         return secondsMin + secondsHrs;
376     }
377
378     // Method to concatenate channelConfigs Lists to one List
379     @SafeVarargs
380     private final List<SecondGenerationChannelConfiguration> combineChannelConfigLists(
381             List<SecondGenerationChannelConfiguration>... args) {
382         List<SecondGenerationChannelConfiguration> combinedChannelConfigLists = new ArrayList<>();
383         for (List<SecondGenerationChannelConfiguration> list : args) {
384             for (SecondGenerationChannelConfiguration i : list) {
385                 combinedChannelConfigLists.add(i);
386             }
387         }
388         return combinedChannelConfigLists;
389     }
390
391     // Method to concatenate channelPosts Lists to one List
392     @SafeVarargs
393     private final List<String> combinePostsLists(List<String>... args) {
394         List<String> combinedPostsLists = new ArrayList<>();
395         for (List<String> list : args) {
396             for (String i : list) {
397                 combinedPostsLists.add(i);
398             }
399         }
400         return combinedPostsLists;
401     }
402 }