]> git.basschouten.com Git - openhab-addons.git/blob
58d96ad4040f706cc9979a896755729b5d506202
[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.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         // Build posts for dxsEntries part
191         String dxsEntriesCall = inverterConfig.url + "/api/dxs.json?dxsEntries=" + channelConfigs.get(0).dxsEntries;
192         for (int i = 1; i < channelConfigs.size(); i++) {
193             dxsEntriesCall += ("&dxsEntries=" + channelConfigs.get(i).dxsEntries);
194         }
195         String jsonDxsEntriesResponse = callURL(dxsEntriesCall, httpClient);
196         SecondGenerationDxsEntriesContainerDTO dxsEntriesContainer = gson.fromJson(jsonDxsEntriesResponse,
197                 SecondGenerationDxsEntriesContainerDTO.class);
198
199         String[] channelPosts = new String[23];
200         int channelPostsCounter = 0;
201         for (SecondGenerationDxsEntries dxsentries : dxsEntriesContainer.dxsEntries) {
202             channelPosts[channelPostsCounter] = dxsentries.getName();
203             channelPostsCounter++;
204         }
205         channelPostsTemp = List.of(channelPosts);
206
207         // Build posts for dxsEntriesExt part
208         String dxsEntriesCallExt = inverterConfig.url + "/api/dxs.json?dxsEntries="
209                 + channelConfigsExt.get(0).dxsEntries;
210         for (int i = 1; i < channelConfigs.size(); i++) {
211             dxsEntriesCallExt += ("&dxsEntries=" + channelConfigsExt.get(i).dxsEntries);
212         }
213         String jsonDxsEntriesResponseExt = callURL(dxsEntriesCallExt, httpClient);
214         SecondGenerationDxsEntriesContainerDTO dxsEntriesContainerExt = gson.fromJson(jsonDxsEntriesResponseExt,
215                 SecondGenerationDxsEntriesContainerDTO.class);
216         String[] channelPostsExt = new String[23];
217         int channelPostsCounterExt = 0;
218         for (SecondGenerationDxsEntries dxsentriesExt : dxsEntriesContainerExt.dxsEntries) {
219             channelPostsExt[channelPostsCounterExt] = dxsentriesExt.getName();
220             channelPostsCounterExt++;
221         }
222         channelPostsTempExt = List.of(channelPostsExt);
223
224         // Build posts for dxsEntriesExtExt part
225         String dxsEntriesCallExtExt = inverterConfig.url + "/api/dxs.json?dxsEntries="
226                 + channelConfigsExtExt.get(0).dxsEntries;
227         for (int i = 1; i < channelConfigsExtExt.size(); i++) {
228             dxsEntriesCallExtExt += ("&dxsEntries=" + channelConfigsExtExt.get(i).dxsEntries);
229         }
230         String jsonDxsEntriesResponseExtExt = callURL(dxsEntriesCallExtExt, httpClient);
231         SecondGenerationDxsEntriesContainerDTO dxsEntriesContainerExtExt = gson.fromJson(jsonDxsEntriesResponseExtExt,
232                 SecondGenerationDxsEntriesContainerDTO.class);
233         String[] channelPostsExtExt = new String[3];
234         int channelPostsCounterExtExt = 0;
235         for (SecondGenerationDxsEntries dxsentriesExtExt : dxsEntriesContainerExtExt.dxsEntries) {
236             channelPostsExtExt[channelPostsCounterExtExt] = dxsentriesExtExt.getName();
237             channelPostsCounterExtExt++;
238         }
239         channelPostsTempExtExt = List.of(channelPostsExtExt);
240
241         // Concatenate posts for all parts except configurable channels
242         channelPostsTempAll = combinePostsLists(channelPostsTemp, channelPostsTempExt, channelPostsTempExtExt);
243         String[] channelPostsTempAll1 = channelPostsTempAll.toArray(new String[0]);
244
245         // Build posts for dxsEntriesConfigureable part
246         String[] channelPostsConfigurable = new String[5];
247         if (inverterConfig.hasBattery) {
248             String dxsEntriesCallConfigurable = inverterConfig.url + "/api/dxs.json?dxsEntries="
249                     + channelConfigsConfigurable.get(0).dxsEntries;
250             for (int i = 1; i < channelConfigsConfigurable.size(); i++) {
251                 dxsEntriesCallConfigurable += ("&dxsEntries=" + channelConfigsConfigurable.get(i).dxsEntries);
252             }
253             String jsonDxsEntriesResponseConfigurable = callURL(dxsEntriesCallConfigurable, httpClient);
254             SecondGenerationDxsEntriesContainerDTO dxsEntriesContainerConfigurable = gson
255                     .fromJson(jsonDxsEntriesResponseConfigurable, SecondGenerationDxsEntriesContainerDTO.class);
256             int channelPostsCounterConfigurable = 0;
257             for (SecondGenerationDxsEntries dxsentriesConfigurable : dxsEntriesContainerConfigurable.dxsEntries) {
258                 channelPostsConfigurable[channelPostsCounterConfigurable] = dxsentriesConfigurable.getName();
259                 channelPostsCounterConfigurable++;
260             }
261         }
262
263         // Create and update actual values for non-configurable channels
264         if (!inverterConfig.hasBattery) {
265             channelConfigsAll = combineChannelConfigLists(channelConfigs, channelConfigsExt, channelConfigsExtExt);
266             int channelValuesCounterAll = 0;
267             for (SecondGenerationChannelConfiguration cConfig : channelConfigsAll) {
268                 String channel = cConfig.id;
269                 updateState(channel, getState(channelPostsTempAll1[channelValuesCounterAll], cConfig.unit));
270                 channelValuesCounterAll++;
271             }
272         }
273         // Create and update actual values for all channels
274         if (inverterConfig.hasBattery) {
275             // Part for updating non-configurable channels
276             channelConfigsAll = combineChannelConfigLists(channelConfigs, channelConfigsExt, channelConfigsExtExt);
277             // Update the non-configurable channels
278             int channelValuesCounterAll = 0;
279             for (SecondGenerationChannelConfiguration cConfig : channelConfigsAll) {
280                 String channel = cConfig.id;
281                 updateState(channel, getState(channelPostsTempAll1[channelValuesCounterAll], cConfig.unit));
282                 channelValuesCounterAll++;
283             }
284
285             // Part for updating configurable channels
286             int channelValuesCounterConfigurable = 0;
287             for (SecondGenerationChannelConfiguration cConfig : channelConfigsConfigurable) {
288                 String channel = cConfig.id;
289                 String value = channelPostsConfigurable[channelValuesCounterConfigurable];
290                 int dxsEntriesCheckCounter = 3;
291                 if (cConfig.dxsEntries.equals("33556484")) {
292                     dxsEntriesCheckCounter = 1;
293                 }
294                 if (cConfig.dxsEntries.equals("33556482")) {
295                     dxsEntriesCheckCounter = 2;
296                 }
297                 switch (dxsEntriesCheckCounter) {
298                     case 1:
299                         if ("false".equals(value)) {
300                             updateState(channel, OnOffType.OFF);
301                         }
302                         if ("true".equals(value)) {
303                             updateState(channel, OnOffType.ON);
304                         }
305                         channelValuesCounterConfigurable++;
306                         break;
307                     case 2:
308                         if ("false".equals(value)) {
309                             State stateFalse = new StringType("0");
310                             updateState(channel, stateFalse);
311                         }
312                         if ("true".equals(value)) {
313                             State stateTrue = new StringType("1");
314                             updateState(channel, stateTrue);
315                         }
316                         channelValuesCounterConfigurable++;
317                         break;
318                     case 3:
319                         State stateOther = getState(channelPostsConfigurable[channelValuesCounterConfigurable],
320                                 cConfig.unit);
321                         updateState(channel, stateOther);
322                         channelValuesCounterConfigurable++;
323                         break;
324                 }
325             }
326         }
327     }
328
329     // Help method of handleCommand to with SecondGenerationConfigurationHandler.executeConfigurationChanges method send
330     // configuration changes.
331     private final void preSetExecuteConfigurationChanges(HttpClient httpClientHandleCommand, String url,
332             String username, String password, String dxsEntriesConf, String valueConfiguration) {
333         try {
334             SecondGenerationConfigurationHandler.executeConfigurationChanges(httpClientHandleCommand, url, username,
335                     password, dxsEntriesConf, valueConfiguration);
336         } catch (InterruptedException e) {
337             Thread.currentThread().interrupt();
338             logger.debug("Connection to inverter interrupted during configuration");
339         } catch (ExecutionException | TimeoutException | NoSuchAlgorithmException e) {
340             logger.debug("Connection to inverter disturbed during configuration");
341         }
342     }
343
344     // Method callURL connect to inverter for value scraping
345     private final String callURL(String dxsEntriesCall, HttpClient httpClient) {
346         String jsonDxsResponse = "";
347         try {
348             jsonDxsResponse = httpClient.GET(dxsEntriesCall).getContentAsString().replace("null", "0.000000");
349         } catch (InterruptedException e2) {
350             Thread.currentThread().interrupt();
351             logger.debug("Connection to inverter interrupted during scrape");
352         } catch (ExecutionException | TimeoutException e2) {
353             logger.debug("Connection to inverter disturbed during scrape");
354         }
355         return jsonDxsResponse;
356     }
357
358     // Method getState is used for non-configurable values
359     private State getState(String value, @Nullable Unit<?> unit) {
360         if (unit == null) {
361             return new StringType(value);
362         } else {
363             try {
364                 return new QuantityType<>(new BigDecimal(value), unit);
365             } catch (NumberFormatException getStateException) {
366                 logger.debug("Error parsing value '{}: {}'", value, getStateException.getMessage());
367                 return UnDefType.UNDEF;
368             }
369         }
370     }
371
372     // Method stringToSeconds transform given time in 00:16 syntax to seconds syntax
373     private static long stringToSeconds(String stringTime) {
374         long secondsMin = Long.parseLong(stringTime.substring(3, 5)) * 60;
375         long secondsHrs = Long.parseLong(stringTime.substring(0, 2)) * 3600;
376         return secondsMin + secondsHrs;
377     }
378
379     // Method to concatenate channelConfigs Lists to one List
380     @SafeVarargs
381     private final List<SecondGenerationChannelConfiguration> combineChannelConfigLists(
382             List<SecondGenerationChannelConfiguration>... args) {
383         List<SecondGenerationChannelConfiguration> combinedChannelConfigLists = new ArrayList<>();
384         for (List<SecondGenerationChannelConfiguration> list : args) {
385             for (SecondGenerationChannelConfiguration i : list) {
386                 combinedChannelConfigLists.add(i);
387             }
388         }
389         return combinedChannelConfigLists;
390     }
391
392     // Method to concatenate channelPosts Lists to one List
393     @SafeVarargs
394     private final List<String> combinePostsLists(List<String>... args) {
395         List<String> combinedPostsLists = new ArrayList<>();
396         for (List<String> list : args) {
397             for (String i : list) {
398                 combinedPostsLists.add(i);
399             }
400         }
401         return combinedPostsLists;
402     }
403 }