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