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