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