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