]> git.basschouten.com Git - openhab-addons.git/blob
ae5c468ddb08d6e9429bfbda88dc1aea090bf538
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2020 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 static org.openhab.binding.km200.internal.KM200BindingConstants.*;
16
17 import java.math.BigDecimal;
18 import java.math.RoundingMode;
19 import java.time.ZonedDateTime;
20 import java.util.HashMap;
21 import java.util.List;
22 import java.util.Map;
23
24 import org.eclipse.jdt.annotation.NonNullByDefault;
25 import org.eclipse.jdt.annotation.Nullable;
26 import org.openhab.binding.km200.internal.KM200Device;
27 import org.openhab.binding.km200.internal.KM200ServiceObject;
28 import org.openhab.core.library.CoreItemFactory;
29 import org.openhab.core.library.types.DateTimeType;
30 import org.openhab.core.library.types.DecimalType;
31 import org.openhab.core.library.types.OnOffType;
32 import org.openhab.core.library.types.StringType;
33 import org.openhab.core.types.Command;
34 import org.openhab.core.types.State;
35 import org.slf4j.Logger;
36 import org.slf4j.LoggerFactory;
37
38 import com.google.gson.JsonArray;
39 import com.google.gson.JsonObject;
40 import com.google.gson.JsonParseException;
41 import com.google.gson.JsonParser;
42
43 /**
44  * The KM200DataHandler is managing the data handling between the device and items
45  *
46  * @author Markus Eckhardt - Initial contribution
47  */
48 @NonNullByDefault
49 public class KM200DataHandler {
50     private final Logger logger = LoggerFactory.getLogger(KM200DataHandler.class);
51     private final JsonParser jsonParser = new JsonParser();
52
53     private final KM200Device remoteDevice;
54
55     public KM200DataHandler(KM200Device remoteDevice) {
56         this.remoteDevice = remoteDevice;
57     }
58
59     /**
60      * This function checks the state of a service on the device
61      */
62     public @Nullable State getProvidersState(String service, String itemType, Map<String, String> itemPara) {
63         synchronized (remoteDevice) {
64             String type = null;
65             KM200ServiceObject object = null;
66             JsonObject jsonNode = null;
67
68             logger.trace("Check state of: {}  item: {}", service, itemType);
69             if (remoteDevice.getBlacklistMap().contains(service)) {
70                 logger.warn("Service on blacklist: {}", service);
71                 return null;
72             }
73             if (remoteDevice.containsService(service)) {
74                 object = remoteDevice.getServiceObject(service);
75                 if (null == object) {
76                     logger.warn("Serviceobject does not exist");
77                     return null;
78                 }
79                 if (object.getReadable() == 0) {
80                     logger.warn("Service is listed as protected (reading is not possible): {}", service);
81                     return null;
82                 }
83                 type = object.getServiceType();
84             } else {
85                 logger.warn("Service is not in the determined device service list: {}", service);
86                 return null;
87             }
88             /* Needs to be updated? */
89             if (object.getVirtual() == 0) {
90                 if (!object.getUpdated()) {
91                     jsonNode = remoteDevice.getServiceNode(service);
92                     if (jsonNode == null || jsonNode.isJsonNull()) {
93                         logger.warn("Communication is not possible!");
94                         return null;
95                     }
96                     object.setJSONData(jsonNode);
97                     object.setUpdated(true);
98                 } else {
99                     /* If already updated then use the saved data */
100                     jsonNode = object.getJSONData();
101                 }
102             } else {
103                 /* For using of virtual services only one receive on the parent service is needed */
104                 String parent = object.getParent();
105                 if (null != parent) {
106                     KM200ServiceObject objParent = remoteDevice.getServiceObject(parent);
107                     if (null != objParent) {
108                         if (!objParent.getUpdated()) {
109                             /* If it's a virtual service then receive the data from parent service */
110                             jsonNode = remoteDevice.getServiceNode(parent);
111                             if (jsonNode == null || jsonNode.isJsonNull()) {
112                                 logger.warn("Communication is not possible!");
113                                 return null;
114                             }
115                             objParent.setJSONData(jsonNode);
116                             objParent.setUpdated(true);
117                             object.setUpdated(true);
118                         } else {
119                             /* If already updated then use the saved data */
120                             jsonNode = objParent.getJSONData();
121                         }
122                     }
123                 }
124             }
125             if (null != jsonNode) {
126                 return parseJSONData(jsonNode, type, service, itemType, itemPara);
127             } else {
128                 return null;
129             }
130         }
131     }
132
133     /**
134      * This function parses the receviced JSON Data and return the right state
135      */
136     public @Nullable State parseJSONData(JsonObject nodeRoot, String type, String service, String itemType,
137             Map<String, String> itemPara) {
138         State state = null;
139         KM200ServiceObject object = remoteDevice.getServiceObject(service);
140         if (null == object) {
141             return null;
142         }
143         String parent = object.getParent();
144
145         logger.trace("parseJSONData service: {}, data: {}", service, nodeRoot);
146         /* Now parsing of the JSON String depending on its type and the type of binding item */
147         try {
148             if (nodeRoot.toString().length() == 2) {
149                 logger.warn("Get empty reply");
150                 return null;
151             }
152             switch (type) {
153                 case DATA_TYPE_STRING_VALUE: /* Check whether the type is a single value containing a string value */
154                     logger.debug("parseJSONData type string value: {} Type: {}", nodeRoot, itemType.toString());
155                     String sVal = nodeRoot.get("value").getAsString();
156                     object.setValue(sVal);
157                     /* Switch Binding */
158                     if ("Switch".equals(itemType)) {
159                         // type is definitely correct here
160                         Map<String, String> switchNames = itemPara;
161                         if (switchNames.containsKey("on")) {
162                             if (sVal.equals(switchNames.get("off"))) {
163                                 state = OnOffType.OFF;
164                             } else if (sVal.equals(switchNames.get("on"))) {
165                                 state = OnOffType.ON;
166                             }
167                         } else if (switchNames.isEmpty()) {
168                             logger.debug("No switch item configuration");
169                             return null;
170                         } else {
171                             logger.warn("Switch-Item only on configured on/off string values: {}", nodeRoot);
172                             return null;
173                         }
174                         /* NumberItem Binding */
175                     } else if (CoreItemFactory.NUMBER.equals(itemType)) {
176                         try {
177                             state = new DecimalType(Float.parseFloat(sVal));
178                         } catch (NumberFormatException e) {
179                             logger.warn("Conversion of the string value to Decimal wasn't possible, data: {} error: {}",
180                                     nodeRoot, e.getMessage());
181                             return null;
182                         }
183                         /* DateTimeItem Binding */
184                     } else if (CoreItemFactory.DATETIME.equals(itemType)) {
185                         try {
186                             state = new DateTimeType(sVal);
187                         } catch (IllegalArgumentException e) {
188                             logger.warn(
189                                     "Conversion of the string value to DateTime wasn't possible, data: {} error: {}",
190                                     nodeRoot, e.getMessage());
191                             return null;
192                         }
193                         /* StringItem Binding */
194                     } else if (CoreItemFactory.STRING.equals(itemType)) {
195                         state = new StringType(sVal);
196                     } else {
197                         logger.info("Bindingtype not supported for string values: {}", itemType.getClass());
198                         return null;
199                     }
200                     return state;
201                 case DATA_TYPE_FLOAT_VALUE: /* Check whether the type is a single value containing a float value */
202                     logger.trace("state of type float value: {}", nodeRoot);
203                     Object bdVal = null;
204                     try {
205                         bdVal = new BigDecimal(nodeRoot.get("value").getAsString()).setScale(1, RoundingMode.HALF_UP);
206                     } catch (NumberFormatException e) {
207                         bdVal = Double.NaN;
208                     }
209                     object.setValue(bdVal);
210                     /* NumberItem Binding */
211                     if (CoreItemFactory.NUMBER.equals(itemType)) {
212                         if (bdVal instanceof Double) { // Checking whether
213                             state = new DecimalType((Double) bdVal);
214                         } else {
215                             state = new DecimalType(((Number) bdVal).doubleValue());
216                         }
217                         /* StringItem Binding */
218                     } else if (CoreItemFactory.STRING.equals(itemType)) {
219                         state = new StringType(bdVal.toString());
220                     } else {
221                         logger.info("Bindingtype not supported for float values: {}", itemType.getClass());
222                         return null;
223                     }
224                     return state;
225                 case DATA_TYPE_SWITCH_PROGRAM: /* Check whether the type is a switchProgram */
226                     KM200SwitchProgramServiceHandler sPService = null;
227                     logger.trace("state of type switchProgram: {}", nodeRoot);
228                     if (null != parent) {
229                         KM200ServiceObject objParent = remoteDevice.getServiceObject(parent);
230                         if (null != objParent) {
231                             /* Get the KM200SwitchProgramService class object with all specific parameters */
232                             if (object.getVirtual() == 0) {
233                                 sPService = ((KM200SwitchProgramServiceHandler) object.getValueParameter());
234                             } else {
235                                 sPService = ((KM200SwitchProgramServiceHandler) objParent.getValueParameter());
236                             }
237                             if (null != sPService) {
238                                 /* Update the switches inside the KM200SwitchProgramService */
239                                 sPService.updateSwitches(nodeRoot, remoteDevice);
240
241                                 /* the parsing of switch program-services have to be outside, using json in strings */
242                                 if (object.getVirtual() == 1) {
243                                     return this.getVirtualState(object, itemType, service);
244                                 } else {
245                                     /*
246                                      * if access to the parent non virtual service the return the switchPoints jsonarray
247                                      */
248                                     if (CoreItemFactory.STRING.equals(itemType)) {
249                                         state = new StringType(
250                                                 nodeRoot.get("switchPoints").getAsJsonArray().toString());
251                                     } else {
252                                         logger.info(
253                                                 "Bindingtype not supported for switchProgram, only json over strings supported: {}",
254                                                 itemType.getClass());
255                                         return null;
256                                     }
257                                     return state;
258                                 }
259                             }
260                         }
261                     }
262                     return null;
263                 case DATA_TYPE_ERROR_LIST: /* Check whether the type is a errorList */
264                     KM200ErrorServiceHandler eService = null;
265                     logger.trace("state of type errorList: {}", nodeRoot);
266                     if (null != parent) {
267                         KM200ServiceObject objParent = remoteDevice.getServiceObject(parent);
268                         if (null != objParent) {
269                             /* Get the KM200ErrorService class object with all specific parameters */
270                             if (object.getVirtual() == 0) {
271                                 eService = ((KM200ErrorServiceHandler) object.getValueParameter());
272                             } else {
273                                 eService = ((KM200ErrorServiceHandler) objParent.getValueParameter());
274                             }
275                             if (null != eService) {
276                                 /* Update the switches inside the KM200SwitchProgramService */
277                                 eService.updateErrors(nodeRoot);
278
279                                 /* the parsing of switch program-services have to be outside, using json in strings */
280                                 if (object.getVirtual() == 1) {
281                                     return this.getVirtualState(object, itemType, service);
282                                 } else {
283                                     /*
284                                      * if access to the parent non virtual service the return the switchPoints jsonarray
285                                      */
286                                     if (CoreItemFactory.STRING.equals(itemType)) {
287                                         state = new StringType(nodeRoot.get("values").getAsJsonArray().toString());
288                                     } else {
289                                         logger.info(
290                                                 "Bindingtype not supported for error list, only json over strings is supported: {}",
291                                                 itemType.getClass());
292                                         return null;
293                                     }
294                                 }
295                             }
296                         }
297                     }
298                 case DATA_TYPE_Y_RECORDING: /* Check whether the type is a yRecording */
299                     logger.info("state of: type yRecording is not supported yet: {}", nodeRoot);
300                     /* have to be completed */
301                     break;
302                 case DATA_TYPE_SYSTEM_INFO: /* Check whether the type is a systeminfo */
303                     logger.info("state of: type systeminfo is not supported yet: {}", nodeRoot);
304                     /* have to be completed */
305                     break;
306                 case DATA_TYPE_ARRAY_DATA: /* Check whether the type is a arrayData */
307                     logger.info("state of: type arrayData is not supported yet: {}", nodeRoot);
308                     /* have to be completed */
309                     break;
310                 case DATA_TYPE_E_MONITORING_LIST: /* Check whether the type is a eMonitoringList */
311                     logger.info("state of: type eMonitoringList is not supported yet: {}", nodeRoot);
312                     /* have to be completed */
313                     break;
314             }
315         } catch (JsonParseException e) {
316             logger.warn("Parsingexception in JSON, data: {} error: {} ", nodeRoot, e.getMessage());
317         }
318         return null;
319     }
320
321     /**
322      * This function checks the virtual state of a service
323      */
324     private @Nullable State getVirtualState(KM200ServiceObject object, String itemType, String service) {
325         State state = null;
326         String type = object.getServiceType();
327         String parent = object.getParent();
328
329         if (null != parent) {
330             KM200ServiceObject objParent = remoteDevice.getServiceObject(parent);
331             if (null != objParent) {
332                 logger.trace("Check virtual state of: {} type: {} item: {}", service, type, itemType);
333                 switch (type) {
334                     case DATA_TYPE_SWITCH_PROGRAM:
335                         KM200SwitchProgramServiceHandler sPService = ((KM200SwitchProgramServiceHandler) objParent
336                                 .getValueParameter());
337                         if (null != sPService) {
338                             String[] servicePath = service.split("/");
339                             String virtService = servicePath[servicePath.length - 1];
340                             if ("weekday".equals(virtService)) {
341                                 if (CoreItemFactory.STRING.equals(itemType)) {
342                                     String actDay = sPService.getActiveDay();
343                                     state = new StringType(actDay);
344                                 } else {
345                                     logger.info("Bindingtype not supported for day service: {}", itemType.getClass());
346                                     return null;
347                                 }
348                             } else if ("nbrCycles".equals(virtService)) {
349                                 if (CoreItemFactory.NUMBER.equals(itemType)) {
350                                     Integer nbrCycles = sPService.getNbrCycles();
351                                     state = new DecimalType(nbrCycles);
352                                 } else {
353                                     logger.info("Bindingtype not supported for nbrCycles service: {}",
354                                             itemType.getClass());
355                                     return null;
356                                 }
357                             } else if ("cycle".equals(virtService)) {
358                                 if (CoreItemFactory.NUMBER.equals(itemType)) {
359                                     Integer cycle = sPService.getActiveCycle();
360                                     state = new DecimalType(cycle);
361                                 } else {
362                                     logger.info("Bindingtype not supported for cycle service: {}", itemType.getClass());
363                                     return null;
364                                 }
365                             } else if (virtService.equals(sPService.getPositiveSwitch())) {
366                                 if (CoreItemFactory.NUMBER.equals(itemType)) {
367                                     Integer minutes = sPService.getActivePositiveSwitch();
368                                     state = new DecimalType(minutes);
369                                 } else if (CoreItemFactory.DATETIME.equals(itemType)) {
370                                     Integer minutes = sPService.getActivePositiveSwitch();
371                                     ZonedDateTime rightNow = ZonedDateTime.now();
372                                     rightNow.minusHours(rightNow.getHour());
373                                     rightNow.minusMinutes(rightNow.getMinute());
374                                     rightNow.plusSeconds(minutes * 60 - rightNow.getOffset().getTotalSeconds());
375                                     state = new DateTimeType(rightNow);
376                                 } else {
377                                     logger.info("Bindingtype not supported for cycle service: {}", itemType);
378                                     return null;
379                                 }
380                             } else if (virtService.equals(sPService.getNegativeSwitch())) {
381                                 if (CoreItemFactory.NUMBER.equals(itemType)) {
382                                     Integer minutes = sPService.getActiveNegativeSwitch();
383                                     state = new DecimalType(minutes);
384                                 } else if (CoreItemFactory.DATETIME.equals(itemType)) {
385                                     Integer minutes = sPService.getActiveNegativeSwitch();
386                                     ZonedDateTime rightNow = ZonedDateTime.now();
387                                     rightNow.minusHours(rightNow.getHour());
388                                     rightNow.minusMinutes(rightNow.getMinute());
389                                     rightNow.plusSeconds(minutes * 60 - rightNow.getOffset().getTotalSeconds());
390                                     state = new DateTimeType(rightNow);
391                                 } else {
392                                     logger.info("Bindingtype not supported for cycle service: {}", itemType.getClass());
393                                     return null;
394                                 }
395                             }
396                             return state;
397                         } else {
398                             return null;
399                         }
400                     case DATA_TYPE_ERROR_LIST:
401                         KM200ErrorServiceHandler eService = ((KM200ErrorServiceHandler) objParent.getValueParameter());
402                         if (null != eService) {
403                             String[] nServicePath = service.split("/");
404                             String nVirtService = nServicePath[nServicePath.length - 1];
405                             /* Go through the parameters and read the values */
406                             switch (nVirtService) {
407                                 case "nbrErrors":
408                                     if (CoreItemFactory.NUMBER.equals(itemType)) {
409                                         Integer nbrErrors = eService.getNbrErrors();
410                                         state = new DecimalType(nbrErrors);
411                                     } else {
412                                         logger.info("Bindingtype not supported for error number service: {}",
413                                                 itemType.getClass());
414                                         return null;
415                                     }
416                                     break;
417                                 case "error":
418                                     if (CoreItemFactory.NUMBER.equals(itemType)) {
419                                         Integer actError = eService.getActiveError();
420                                         state = new DecimalType(actError);
421                                     } else {
422                                         logger.info("Bindingtype not supported for error service: {}",
423                                                 itemType.getClass());
424                                         return null;
425                                     }
426                                     break;
427                                 case "errorString":
428                                     if (CoreItemFactory.STRING.equals(itemType)) {
429                                         String errorString = eService.getErrorString();
430                                         if (errorString == null) {
431                                             return null;
432                                         }
433                                         state = new StringType(errorString);
434                                     } else {
435                                         logger.info("Bindingtype not supported for error string service: {}",
436                                                 itemType.getClass());
437                                         return null;
438                                     }
439                                     break;
440                             }
441                             return state;
442                         } else {
443                             return null;
444                         }
445                 }
446             }
447         }
448         return null;
449     }
450
451     /**
452      * This function sets the state of a service on the device
453      */
454     public @Nullable JsonObject sendProvidersState(String service, Command command, String itemType, Object itemPara) {
455         synchronized (remoteDevice) {
456             String type;
457             KM200ServiceObject object;
458             JsonObject newObject;
459
460             logger.debug("Prepare item for send: {} zitem: {}", service, itemType);
461             if (remoteDevice.getBlacklistMap().contains(service)) {
462                 logger.debug("Service on blacklist: {}", service);
463                 return null;
464             }
465             if (remoteDevice.containsService(service)) {
466                 object = remoteDevice.getServiceObject(service);
467                 if (null == object) {
468                     logger.debug("Object is null");
469                     return null;
470                 }
471                 if (object.getWriteable() == 0) {
472                     logger.warn("Service is listed as read-only: {}", service);
473                     return null;
474                 }
475                 type = object.getServiceType();
476             } else {
477                 logger.warn("Service is not in the determined device service list: {}", service);
478                 return null;
479             }
480             /* The service is availible, set now the values depeding on the item and binding type */
481             logger.trace("state of: {} type: {}", command, type);
482             /* Binding is a NumberItem */
483             if (CoreItemFactory.NUMBER.equals(itemType)) {
484                 BigDecimal bdVal = ((DecimalType) command).toBigDecimal();
485                 /* Check the capabilities of this service */
486                 if (object.getValueParameter() != null) {
487                     // type is definitely correct here
488                     @SuppressWarnings("unchecked")
489                     List<BigDecimal> valParas = (List<BigDecimal>) object.getValueParameter();
490                     if (null != valParas) {
491                         BigDecimal minVal = valParas.get(0);
492                         BigDecimal maxVal = valParas.get(1);
493                         if (bdVal.compareTo(minVal) < 0) {
494                             bdVal = minVal;
495                         }
496                         if (bdVal.compareTo(maxVal) > 0) {
497                             bdVal = maxVal;
498                         }
499                     }
500                 }
501                 newObject = new JsonObject();
502                 if (DATA_TYPE_FLOAT_VALUE.equals(type)) {
503                     newObject.addProperty("value", bdVal);
504                 } else if (DATA_TYPE_STRING_VALUE.equals(type)) {
505                     newObject.addProperty("value", bdVal.toString());
506                 } else if (DATA_TYPE_SWITCH_PROGRAM.equals(type) && object.getVirtual() == 1) {
507                     /* A switchProgram as NumberItem is always virtual */
508                     newObject = sendVirtualState(object, service, command, itemType);
509                 } else if (DATA_TYPE_ERROR_LIST.equals(type) && object.getVirtual() == 1) {
510                     /* A errorList as NumberItem is always virtual */
511                     newObject = sendVirtualState(object, service, command, itemType);
512                 } else {
513                     logger.info("Not supported type for numberItem: {}", type);
514                 }
515                 /* Binding is a StringItem */
516             } else if (CoreItemFactory.STRING.equals(itemType)) {
517                 String val = ((StringType) command).toString();
518                 newObject = new JsonObject();
519                 /* Check the capabilities of this service */
520                 if (object.getValueParameter() != null) {
521                     // type is definitely correct here
522                     @SuppressWarnings("unchecked")
523                     List<String> valParas = (List<String>) object.getValueParameter();
524                     if (null != valParas) {
525                         if (!valParas.contains(val)) {
526                             logger.warn("Parameter is not in the service parameterlist: {}", val);
527                             return null;
528                         }
529                     }
530                 }
531                 if (DATA_TYPE_STRING_VALUE.equals(type)) {
532                     newObject.addProperty("value", val);
533                 } else if (DATA_TYPE_FLOAT_VALUE.equals(type)) {
534                     newObject.addProperty("value", Float.parseFloat(val));
535                 } else if (DATA_TYPE_SWITCH_PROGRAM.equals(type)) {
536                     if (object.getVirtual() == 1) {
537                         newObject = sendVirtualState(object, service, command, itemType);
538                     } else {
539                         /* The JSONArray of switch items can be send directly */
540                         try {
541                             /* Check whether this input string is a valid JSONArray */
542                             JsonArray userArray = (JsonArray) jsonParser.parse(val);
543                             newObject = userArray.getAsJsonObject();
544                         } catch (JsonParseException e) {
545                             logger.warn("The input for the switchProgram is not a valid JSONArray : {}",
546                                     e.getMessage());
547                             return null;
548                         }
549                     }
550                 } else {
551                     logger.info("Not supported type for stringItem: {}", type);
552                 }
553                 /* Binding is a DateTimeItem */
554             } else if (CoreItemFactory.DATETIME.equals(itemType)) {
555                 String val = ((DateTimeType) command).toString();
556                 newObject = new JsonObject();
557                 if (DATA_TYPE_STRING_VALUE.equals(type)) {
558                     newObject.addProperty("value", val);
559                 } else if (DATA_TYPE_SWITCH_PROGRAM.equals(type)) {
560                     newObject = sendVirtualState(object, service, command, itemType);
561                 } else {
562                     logger.info("Not supported type for dateTimeItem: {}", type);
563                 }
564             } else if ("Switch".equals(itemType)) {
565                 String val = null;
566                 newObject = new JsonObject();
567                 // type is definitely correct here
568                 @SuppressWarnings("unchecked")
569                 Map<String, String> switchNames = (HashMap<String, String>) itemPara;
570                 if (switchNames.containsKey("on")) {
571                     if (command == OnOffType.OFF) {
572                         val = switchNames.get("off");
573                     } else if (command == OnOffType.ON) {
574                         val = switchNames.get("on");
575                     }
576                     // type is definitely correct here
577                     @SuppressWarnings("unchecked")
578                     List<String> valParas = (List<String>) object.getValueParameter();
579                     if (null != valParas) {
580                         if (!valParas.contains(val)) {
581                             logger.warn("Parameter is not in the service parameterlist: {}", val);
582                             return null;
583                         }
584                     }
585                 } else if (switchNames.isEmpty()) {
586                     logger.debug("No switch item configuration");
587                     return null;
588                 } else {
589                     logger.info("Switch-Item only on configured on/off string values {}", command);
590                     return null;
591                 }
592                 if (DATA_TYPE_STRING_VALUE.equals(type)) {
593                     newObject.addProperty("value", val);
594                 } else {
595                     logger.info("Not supported type for SwitchItem:{}", type);
596                 }
597             } else {
598                 logger.info("Bindingtype not supported: {}", itemType.getClass());
599                 return null;
600             }
601             /* If some data is availible then we have to send it to device */
602             if (newObject != null && newObject.toString().length() > 2) {
603                 logger.trace("Send Data: {}", newObject);
604                 return newObject;
605             } else {
606                 return null;
607             }
608         }
609     }
610
611     /**
612      * This function sets the state of a virtual service
613      */
614     public @Nullable JsonObject sendVirtualState(KM200ServiceObject object, String service, Command command,
615             String itemType) {
616         JsonObject newObject = null;
617         String type = null;
618         logger.trace("Check virtual state of: {} type: {} item: {}", service, type, itemType);
619         String parent = object.getParent();
620         if (null != parent) {
621             KM200ServiceObject objParent = remoteDevice.getServiceObject(parent);
622             if (null != objParent) {
623                 type = object.getServiceType();
624                 /* Binding is a StringItem */
625                 if (CoreItemFactory.STRING.equals(itemType)) {
626                     String val = ((StringType) command).toString();
627                     switch (type) {
628                         case DATA_TYPE_SWITCH_PROGRAM:
629                             KM200SwitchProgramServiceHandler sPService = ((KM200SwitchProgramServiceHandler) objParent
630                                     .getValueParameter());
631                             if (null != sPService) {
632                                 String[] servicePath = service.split("/");
633                                 String virtService = servicePath[servicePath.length - 1];
634                                 if ("weekday".equals(virtService)) {
635                                     /* Only parameter changing without communication to device */
636                                     sPService.setActiveDay(val);
637                                 }
638                             }
639                             break;
640                     }
641                     /* Binding is a NumberItem */
642                 } else if (CoreItemFactory.NUMBER.equals(itemType)) {
643                     Integer val = ((DecimalType) command).intValue();
644                     switch (type) {
645                         case DATA_TYPE_SWITCH_PROGRAM:
646                             KM200SwitchProgramServiceHandler sPService = ((KM200SwitchProgramServiceHandler) objParent
647                                     .getValueParameter());
648                             if (null != sPService) {
649                                 String[] servicePath = service.split("/");
650                                 String virtService = servicePath[servicePath.length - 1];
651                                 if ("cycle".equals(virtService)) {
652                                     /* Only parameter changing without communication to device */
653                                     sPService.setActiveCycle(val);
654                                 } else if (virtService.equals(sPService.getPositiveSwitch())) {
655                                     sPService.setActivePositiveSwitch(val);
656                                     /* Create a JSON Array from current switch configuration */
657                                     newObject = sPService.getUpdatedJSONData(objParent);
658                                 } else if (virtService.equals(sPService.getNegativeSwitch())) {
659                                     sPService.setActiveNegativeSwitch(val);
660                                     /* Create a JSON Array from current switch configuration */
661                                     newObject = sPService.getUpdatedJSONData(objParent);
662                                 }
663                             }
664                             break;
665                         case DATA_TYPE_ERROR_LIST:
666                             KM200ErrorServiceHandler eService = ((KM200ErrorServiceHandler) objParent
667                                     .getValueParameter());
668                             if (null != eService) {
669                                 String[] nServicePath = service.split("/");
670                                 String nVirtService = nServicePath[nServicePath.length - 1];
671                                 if ("error".equals(nVirtService)) {
672                                     /* Only parameter changing without communication to device */
673                                     eService.setActiveError(val);
674                                 }
675                             }
676                             break;
677                     }
678                 } else if (CoreItemFactory.DATETIME.equals(itemType)) {
679                     ZonedDateTime swTime = ((DateTimeType) command).getZonedDateTime();
680                     KM200SwitchProgramServiceHandler sPService = ((KM200SwitchProgramServiceHandler) objParent
681                             .getValueParameter());
682                     if (null != sPService) {
683                         String[] servicePath = service.split("/");
684                         String virtService = servicePath[servicePath.length - 1];
685                         Integer minutes = swTime.getHour() * 60 + swTime.getMinute()
686                                 + swTime.getOffset().getTotalSeconds() % 60;
687                         minutes = (minutes % sPService.getSwitchPointTimeRaster())
688                                 * sPService.getSwitchPointTimeRaster();
689                         if (virtService.equals(sPService.getPositiveSwitch())) {
690                             sPService.setActivePositiveSwitch(minutes);
691                         }
692                         if (virtService.equals(sPService.getNegativeSwitch())) {
693                             sPService.setActiveNegativeSwitch(minutes);
694                         }
695                         /* Create a JSON Array from current switch configuration */
696                         newObject = sPService.getUpdatedJSONData(objParent);
697                     }
698                 }
699                 return newObject;
700             }
701         }
702         return null;
703     }
704 }