]> git.basschouten.com Git - openhab-addons.git/blob
2bd07f6c70fc0c83ecd59a2412c285a7a16febb3
[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.km200.internal.handler;
14
15 import java.math.BigDecimal;
16 import java.util.ArrayList;
17 import java.util.Arrays;
18 import java.util.Collections;
19 import java.util.HashMap;
20 import java.util.List;
21 import java.util.Map;
22
23 import org.eclipse.jdt.annotation.Nullable;
24 import org.openhab.binding.km200.internal.KM200Device;
25 import org.openhab.binding.km200.internal.KM200ServiceObject;
26 import org.openhab.core.types.StateOption;
27 import org.slf4j.Logger;
28 import org.slf4j.LoggerFactory;
29
30 import com.google.gson.JsonArray;
31 import com.google.gson.JsonObject;
32
33 /**
34  * The KM200SwitchProgramService representing a switch program service with its all capabilities
35  *
36  * @author Markus Eckhardt - Initial contribution
37  * @implNote {@code @NonNullByDefault} is not working here because of the switchMap array handling
38  */
39
40 public class KM200SwitchProgramServiceHandler {
41     private final Logger logger = LoggerFactory.getLogger(KM200SwitchProgramServiceHandler.class);
42
43     private int maxNbOfSwitchPoints = 8;
44     private int maxNbOfSwitchPointsPerDay = 8;
45     private int switchPointTimeRaster = 10;
46     private String setpointProperty = "";
47     private String positiveSwitch = "";
48     private String negativeSwitch = "";
49
50     protected final Integer MIN_TIME = 0;
51     protected final Integer MAX_TIME = 1430;
52     protected static final String TYPE_MONDAY = "Mo";
53     protected static final String TYPE_TUESDAY = "Tu";
54     protected static final String TYPE_WEDNESDAY = "We";
55     protected static final String TYPE_THURSDAY = "Th";
56     protected static final String TYPE_FRIDAY = "Fr";
57     protected static final String TYPE_SATURDAY = "Sa";
58     protected static final String TYPE_SUNDAY = "Su";
59
60     private String activeDay = TYPE_MONDAY;
61     private Integer activeCycle = 1;
62
63     /* Night- and daylist for all weekdays */
64     public Map<String, Map<String, List<Integer>>> switchMap = new HashMap<>();
65
66     /* List with all days */
67     private static List<String> days = new ArrayList<>(Arrays.asList(TYPE_MONDAY, TYPE_TUESDAY, TYPE_WEDNESDAY,
68             TYPE_THURSDAY, TYPE_FRIDAY, TYPE_SATURDAY, TYPE_SUNDAY));
69
70     public static List<StateOption> daysList = List.of(new StateOption(TYPE_MONDAY, "Monday"),
71             new StateOption(TYPE_TUESDAY, "Tuesday"), new StateOption(TYPE_WEDNESDAY, "Wednesday"),
72             new StateOption(TYPE_THURSDAY, "Thursday"), new StateOption(TYPE_FRIDAY, "Friday"),
73             new StateOption(TYPE_SATURDAY, "Saturday"), new StateOption(TYPE_SUNDAY, "Sunday"));
74
75     /* List with setpoints */
76     private List<String> setpoints = new ArrayList<>();
77
78     /**
79      * This function inits the week list
80      */
81     void initWeeklist(String setpoint) {
82         Map<String, List<Integer>> weekMap = switchMap.get(setpoint);
83         if (weekMap == null) {
84             weekMap = new HashMap<>();
85             for (String day : days) {
86                 weekMap.put(day, new ArrayList<>());
87             }
88             switchMap.put(setpoint, weekMap);
89         }
90     }
91
92     /**
93      * This function adds a switch to the switchmap
94      */
95     void addSwitch(String day, String setpoint, int time) {
96         logger.trace("Adding day: {} setpoint: {} time: {}", day, setpoint, time);
97         if (!days.contains(day)) {
98             logger.warn("This type of weekday is not supported, get day: {}", day);
99             throw new IllegalArgumentException("This type of weekday is not supported, get day: " + day);
100         }
101         if (!setpoints.contains(setpoint)) {
102             if (setpoints.size() == 2 && "on".compareTo(setpoint) == 0) {
103                 if ("high".compareTo(setpoints.get(0)) == 0 && "off".compareTo(setpoints.get(1)) == 0) {
104                     if (("on".compareTo(positiveSwitch) == 0 && "off".compareTo(negativeSwitch) == 0)
105                             || ("off".compareTo(positiveSwitch) == 0 && "on".compareTo(negativeSwitch) == 0)) {
106                         logger.info(
107                                 "!!! Wrong configuration on device. 'on' instead of 'high' in switch program. It seems that's a firmware problem-> ignoring it !!!");
108                     } else {
109                         throw new IllegalArgumentException(
110                                 "This type of setpoint is not supported, get setpoint: " + setpoint);
111                     }
112                 }
113             }
114         }
115         Map<String, List<Integer>> weekMap = switchMap.get(setpoint);
116         if (weekMap == null) {
117             initWeeklist(setpoint);
118             weekMap = switchMap.get(setpoint);
119         }
120         if (weekMap != null) {
121             List<Integer> dayList = weekMap.get(day);
122             if (dayList != null) {
123                 dayList.add(time);
124                 Collections.sort(dayList);
125             }
126         }
127     }
128
129     /**
130      * This function removes all switches from the switchmap
131      *
132      */
133     void removeAllSwitches() {
134         switchMap.clear();
135     }
136
137     public void setMaxNbOfSwitchPoints(Integer nbr) {
138         maxNbOfSwitchPoints = nbr;
139     }
140
141     public void setMaxNbOfSwitchPointsPerDay(Integer nbr) {
142         maxNbOfSwitchPointsPerDay = nbr;
143     }
144
145     public void setSwitchPointTimeRaster(Integer raster) {
146         switchPointTimeRaster = raster;
147     }
148
149     public void setSetpointProperty(String property) {
150         setpointProperty = property;
151     }
152
153     /**
154      * This function sets the day
155      */
156     public void setActiveDay(String day) {
157         if (!days.contains(day)) {
158             logger.warn("This type of weekday is not supported, get day: {}", day);
159             return;
160         }
161         activeDay = day;
162     }
163
164     /**
165      * This function sets the cycle
166      */
167     public void setActiveCycle(Integer cycle) {
168         if (cycle > this.getMaxNbOfSwitchPoints() / 2 || cycle > this.getMaxNbOfSwitchPointsPerDay() / 2 || cycle < 1) {
169             logger.warn("The value of cycle is not valid, get cycle: {}", cycle);
170             return;
171         }
172         /* limit the cycle to the next one after last (for creating a new one) */
173         if (cycle > (getNbrCycles() + 1) || getNbrCycles() == 0) {
174             activeCycle = getNbrCycles() + 1;
175         } else {
176             activeCycle = cycle;
177         }
178     }
179
180     /**
181      * This function sets the positive switch to the selected day and cycle
182      */
183     public void setActivePositiveSwitch(Integer time) {
184         Integer actTime;
185         if (time < MIN_TIME) {
186             actTime = MIN_TIME;
187         } else if (time > MAX_TIME) {
188             actTime = MAX_TIME;
189         } else {
190             actTime = time;
191         }
192         synchronized (switchMap) {
193             Map<String, List<Integer>> week = switchMap.get(getPositiveSwitch());
194             if (week != null) {
195                 List<Integer> daysList = week.get(getActiveDay());
196                 if (daysList != null) {
197                     Integer actC = getActiveCycle();
198                     Integer nbrC = getNbrCycles();
199                     Integer nSwitch = null;
200                     boolean newS = false;
201                     if (nbrC < actC) {
202                         /* new Switch */
203                         newS = true;
204                     }
205                     if (switchMap.get(getNegativeSwitch()).get(getActiveDay()).size() < actC) {
206                         nSwitch = 0;
207                     } else {
208                         nSwitch = switchMap.get(getNegativeSwitch()).get(getActiveDay()).get(actC - 1);
209                     }
210                     /* The positiv switch cannot be higher then the negative */
211                     if (actTime > (nSwitch - getSwitchPointTimeRaster()) && nSwitch > 0) {
212                         actTime = nSwitch;
213                         if (nSwitch < MAX_TIME) {
214                             actTime -= getSwitchPointTimeRaster();
215                         }
216                     }
217                     /* Check whether the time would overlap with the previous one */
218                     if (actC > 1) {
219                         Integer nPrevSwitch = switchMap.get(getNegativeSwitch()).get(getActiveDay()).get(actC - 2);
220                         /* The positiv switch cannot be lower then the previous negative */
221                         if (actTime < (nPrevSwitch + getSwitchPointTimeRaster())) {
222                             actTime = nPrevSwitch + getSwitchPointTimeRaster();
223                         }
224                     }
225                     if (newS) {
226                         daysList.add(actTime);
227                     } else {
228                         daysList.set(actC - 1, actTime);
229                     }
230                     checkRemovement();
231                 }
232             }
233         }
234     }
235
236     /**
237      * This function sets the negative switch to the selected day and cycle
238      */
239     public void setActiveNegativeSwitch(Integer time) {
240         Integer actTime;
241         if (time < MIN_TIME) {
242             actTime = MIN_TIME;
243         } else if (time > MAX_TIME) {
244             actTime = MAX_TIME;
245         } else {
246             actTime = time;
247         }
248         synchronized (switchMap) {
249             Map<String, List<Integer>> week = switchMap.get(getNegativeSwitch());
250             if (week != null) {
251                 List<Integer> daysList = week.get(getActiveDay());
252                 if (daysList != null) {
253                     Integer nbrC = getNbrCycles();
254                     Integer actC = getActiveCycle();
255                     Integer pSwitch = null;
256                     boolean newS = false;
257                     if (nbrC < actC) {
258                         /* new Switch */
259                         newS = true;
260                     }
261                     /* Check whether the positive switch is existing too */
262                     if (switchMap.get(getPositiveSwitch()).get(getActiveDay()).size() < actC) {
263                         /* No -> new Switch */
264                         pSwitch = 0;
265                     } else {
266                         pSwitch = switchMap.get(getPositiveSwitch()).get(getActiveDay()).get(actC - 1);
267                     }
268                     /* The negative switch cannot be lower then the positive */
269                     if (actTime < (pSwitch + getSwitchPointTimeRaster())) {
270                         actTime = pSwitch + getSwitchPointTimeRaster();
271                     }
272                     /* Check whether the time would overlap with the next one */
273                     if (nbrC > actC) {
274                         Integer pNextSwitch = switchMap.get(getPositiveSwitch()).get(getActiveDay()).get(actC);
275                         /* The negative switch cannot be higher then the next positive switch */
276                         if (actTime > (pNextSwitch - getSwitchPointTimeRaster()) && pNextSwitch > 0) {
277                             actTime = pNextSwitch - getSwitchPointTimeRaster();
278                         }
279                     }
280                     if (newS) {
281                         daysList.add(actTime);
282                     } else {
283                         daysList.set(actC - 1, actTime);
284                     }
285                     checkRemovement();
286                 }
287             }
288         }
289     }
290
291     /**
292      * This function checks whether the actual cycle have to be removed (Both times set to MAX_TIME)
293      */
294     void checkRemovement() {
295         if (getActiveNegativeSwitch().equals(MAX_TIME) && getActivePositiveSwitch().equals(MAX_TIME)
296                 && getNbrCycles() > 0) {
297             switchMap.get(getNegativeSwitch()).get(getActiveDay()).remove(getActiveCycle() - 1);
298             switchMap.get(getPositiveSwitch()).get(getActiveDay()).remove(getActiveCycle() - 1);
299         }
300     }
301
302     /**
303      * This function determines the positive and negative switch point names
304      */
305     public boolean determineSwitchNames(KM200Device device) {
306         if (!setpointProperty.isEmpty()) {
307             KM200ServiceObject setpObject = device.getServiceObject(setpointProperty);
308             if (null != setpObject) {
309                 if (setpObject.serviceTreeMap.keySet().isEmpty()) {
310                     return false;
311                 }
312                 for (String key : setpObject.serviceTreeMap.keySet()) {
313                     setpoints.add(key);
314                 }
315             } else {
316                 return false;
317             }
318         }
319         return true;
320     }
321
322     /**
323      * This function updates objects the switching points
324      */
325     public void updateSwitches(JsonObject nodeRoot, KM200Device device) {
326         synchronized (switchMap) {
327             /* Update the list of switching points */
328             removeAllSwitches();
329             JsonArray sPoints = nodeRoot.get("switchPoints").getAsJsonArray();
330             logger.trace("sPoints: {}", nodeRoot);
331             if (positiveSwitch.isEmpty() || negativeSwitch.isEmpty()) {
332                 /* First start. Determine the positive and negative switching points */
333                 if (sPoints.size() > 0) {
334                     for (int i = 0; i < sPoints.size(); i++) {
335                         JsonObject subJSON = sPoints.get(i).getAsJsonObject();
336                         String setpoint = subJSON.get("setpoint").getAsString();
337                         if (positiveSwitch.isEmpty() || negativeSwitch.isEmpty()) {
338                             positiveSwitch = setpoint;
339                             negativeSwitch = setpoint;
340                         } else {
341                             negativeSwitch = setpoint;
342                         }
343                         if (!positiveSwitch.equals(negativeSwitch)) {
344                             break;
345                         }
346                     }
347                 } else {
348                     if (!setpointProperty.isEmpty()) {
349                         BigDecimal firstVal = null;
350                         KM200ServiceObject setpObject = device.getServiceObject(setpointProperty);
351                         if (null != setpObject) {
352                             logger.debug("No switch points set. Use alternative way. {}", nodeRoot);
353                             for (String key : setpoints) {
354                                 if (positiveSwitch.isEmpty() || negativeSwitch.isEmpty()) {
355                                     positiveSwitch = key;
356                                     negativeSwitch = key;
357                                     firstVal = (BigDecimal) setpObject.serviceTreeMap.get(key).getValue();
358                                 } else {
359                                     BigDecimal nextVal = (BigDecimal) setpObject.serviceTreeMap.get(key).getValue();
360                                     if (null != nextVal && null != firstVal) {
361                                         if (nextVal.compareTo(firstVal) > 0) {
362                                             positiveSwitch = key;
363                                         } else {
364                                             negativeSwitch = key;
365                                         }
366                                     }
367                                 }
368                                 if (!positiveSwitch.equalsIgnoreCase(negativeSwitch)) {
369                                     break;
370                                 }
371                             }
372                         }
373                     }
374                 }
375             }
376             logger.debug("Positive switch: {}", positiveSwitch);
377             logger.debug("Negative switch: {}", negativeSwitch);
378             Map<String, List<Integer>> weekMap = null;
379             weekMap = switchMap.get(positiveSwitch);
380             if (weekMap == null) {
381                 initWeeklist(positiveSwitch);
382             }
383             weekMap = switchMap.get(negativeSwitch);
384             if (weekMap == null) {
385                 initWeeklist(negativeSwitch);
386             }
387             for (int i = 0; i < sPoints.size(); i++) {
388                 JsonObject subJSON = sPoints.get(i).getAsJsonObject();
389                 String day = subJSON.get("dayOfWeek").getAsString();
390                 String setpoint = subJSON.get("setpoint").getAsString();
391                 Integer time = subJSON.get("time").getAsInt();
392                 addSwitch(day, setpoint, time);
393             }
394         }
395     }
396
397     /**
398      * This function updates objects JSONData on the actual set switch points.
399      */
400     public @Nullable JsonObject getUpdatedJSONData(KM200ServiceObject parObject) {
401         synchronized (switchMap) {
402             boolean prepareNewOnly = false;
403             JsonArray sPoints = new JsonArray();
404             for (String day : days) {
405                 if (switchMap.get(getPositiveSwitch()).containsKey(day)
406                         && switchMap.get(getNegativeSwitch()).containsKey(day)) {
407                     Integer j;
408                     Integer minDays = Math.min(switchMap.get(getPositiveSwitch()).get(day).size(),
409                             switchMap.get(getNegativeSwitch()).get(day).size());
410                     for (j = 0; j < minDays; j++) {
411                         JsonObject tmpObj = new JsonObject();
412                         tmpObj.addProperty("dayOfWeek", day);
413                         tmpObj.addProperty("setpoint", getPositiveSwitch());
414                         tmpObj.addProperty("time", switchMap.get(getPositiveSwitch()).get(day).get(j));
415                         sPoints.add(tmpObj);
416                         tmpObj = new JsonObject();
417                         tmpObj.addProperty("dayOfWeek", day);
418                         tmpObj.addProperty("setpoint", getNegativeSwitch());
419                         tmpObj.addProperty("time", switchMap.get(getNegativeSwitch()).get(day).get(j));
420                         sPoints.add(tmpObj);
421                     }
422
423                     /* Check whether one object for a new cycle is already created */
424                     if (switchMap.get(getPositiveSwitch()).get(day).size() > minDays) {
425                         JsonObject tmpObj = new JsonObject();
426                         tmpObj.addProperty("dayOfWeek", day);
427                         tmpObj.addProperty("setpoint", getPositiveSwitch());
428                         tmpObj.addProperty("time", switchMap.get(getPositiveSwitch()).get(day).get(j));
429                         sPoints.add(tmpObj);
430                         prepareNewOnly = true;
431                     } else if (switchMap.get(getNegativeSwitch()).get(day).size() > minDays) {
432                         JsonObject tmpObj = new JsonObject();
433                         tmpObj.addProperty("dayOfWeek", day);
434                         tmpObj.addProperty("setpoint", getNegativeSwitch());
435                         tmpObj.addProperty("time", switchMap.get(getNegativeSwitch()).get(day).get(j));
436                         sPoints.add(tmpObj);
437                         prepareNewOnly = true;
438                     }
439                 }
440             }
441             logger.debug("New switching points: {}", sPoints);
442             JsonObject switchRoot = parObject.getJSONData();
443             if (null != switchRoot) {
444                 switchRoot.remove("switchPoints");
445                 switchRoot.add("switchPoints", sPoints);
446                 parObject.setJSONData(switchRoot);
447             } else {
448                 logger.debug("Jsojnoject switchRoot not found");
449             }
450             /* Preparation for are new cycle, don't sent it to the device */
451             if (prepareNewOnly) {
452                 return null;
453             } else {
454                 return switchRoot;
455             }
456         }
457     }
458
459     int getMaxNbOfSwitchPoints() {
460         return maxNbOfSwitchPoints;
461     }
462
463     int getMaxNbOfSwitchPointsPerDay() {
464         return maxNbOfSwitchPointsPerDay;
465     }
466
467     public int getSwitchPointTimeRaster() {
468         return switchPointTimeRaster;
469     }
470
471     public @Nullable String getSetpointProperty() {
472         return setpointProperty;
473     }
474
475     public @Nullable String getPositiveSwitch() {
476         return positiveSwitch;
477     }
478
479     public @Nullable String getNegativeSwitch() {
480         return negativeSwitch;
481     }
482
483     /**
484      * This function returns the number of cycles
485      */
486     public Integer getNbrCycles() {
487         synchronized (switchMap) {
488             Map<String, List<Integer>> weekP = switchMap.get(getPositiveSwitch());
489             Map<String, List<Integer>> weekN = switchMap.get(getNegativeSwitch());
490             if (weekP != null && weekN != null) {
491                 if (weekP.isEmpty() && weekN.isEmpty()) {
492                     return 0;
493                 }
494                 List<Integer> daysListP = weekP.get(getActiveDay());
495                 List<Integer> daysListN = weekN.get(getActiveDay());
496                 if (daysListP != null && daysListN != null) {
497                     return Math.min(daysListP.size(), daysListN.size());
498                 } else {
499                     return 0;
500                 }
501             } else {
502                 return 0;
503             }
504         }
505     }
506
507     /**
508      * This function returns the selected day
509      */
510     public String getActiveDay() {
511         return activeDay;
512     }
513
514     /**
515      * This function returns the selected cycle
516      */
517     public Integer getActiveCycle() {
518         return activeCycle;
519     }
520
521     /**
522      * This function returns the positive switch to the selected day and cycle
523      */
524     public Integer getActivePositiveSwitch() {
525         synchronized (switchMap) {
526             Map<String, List<Integer>> week = switchMap.get(getPositiveSwitch());
527             if (week != null) {
528                 List<Integer> daysList = week.get(getActiveDay());
529                 if (daysList != null && !daysList.isEmpty()) {
530                     Integer cycl = getActiveCycle();
531                     if (cycl <= daysList.size()) {
532                         return (daysList.get(getActiveCycle() - 1));
533                     }
534                 }
535             }
536         }
537         return 0;
538     }
539
540     /**
541      * This function returns the negative switch to the selected day and cycle
542      */
543     public Integer getActiveNegativeSwitch() {
544         synchronized (switchMap) {
545             Map<String, List<Integer>> week = switchMap.get(getNegativeSwitch());
546             if (week != null) {
547                 List<Integer> daysList = week.get(getActiveDay());
548                 if (daysList != null && !daysList.isEmpty()) {
549                     Integer cycl = getActiveCycle();
550                     if (cycl <= daysList.size()) {
551                         return (daysList.get(getActiveCycle() - 1));
552                     }
553                 }
554             }
555         }
556         return 0;
557     }
558 }