]> git.basschouten.com Git - openhab-addons.git/blob
95d6826aab5e1503f9faf99137a77bc2d51656ac
[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.net.InetAddress;
19 import java.net.UnknownHostException;
20 import java.security.NoSuchAlgorithmException;
21 import java.util.ArrayList;
22 import java.util.Collections;
23 import java.util.Iterator;
24 import java.util.LinkedHashMap;
25 import java.util.List;
26 import java.util.Map;
27 import java.util.Map.Entry;
28 import java.util.Set;
29 import java.util.concurrent.CopyOnWriteArrayList;
30 import java.util.concurrent.Executors;
31 import java.util.concurrent.ScheduledExecutorService;
32 import java.util.concurrent.TimeUnit;
33
34 import javax.crypto.Cipher;
35
36 import org.eclipse.jdt.annotation.NonNullByDefault;
37 import org.eclipse.jdt.annotation.Nullable;
38 import org.eclipse.jetty.client.HttpClient;
39 import org.openhab.binding.km200.internal.KM200Device;
40 import org.openhab.binding.km200.internal.KM200ServiceObject;
41 import org.openhab.binding.km200.internal.KM200ThingType;
42 import org.openhab.binding.km200.internal.KM200Utils;
43 import org.openhab.core.common.NamedThreadFactory;
44 import org.openhab.core.config.core.Configuration;
45 import org.openhab.core.library.types.DateTimeType;
46 import org.openhab.core.library.types.DecimalType;
47 import org.openhab.core.library.types.StringType;
48 import org.openhab.core.thing.Bridge;
49 import org.openhab.core.thing.Channel;
50 import org.openhab.core.thing.ChannelUID;
51 import org.openhab.core.thing.Thing;
52 import org.openhab.core.thing.ThingStatus;
53 import org.openhab.core.thing.ThingStatusDetail;
54 import org.openhab.core.thing.ThingStatusInfo;
55 import org.openhab.core.thing.ThingTypeUID;
56 import org.openhab.core.thing.binding.BaseBridgeHandler;
57 import org.openhab.core.types.Command;
58 import org.openhab.core.types.State;
59 import org.slf4j.Logger;
60 import org.slf4j.LoggerFactory;
61
62 import com.google.gson.JsonObject;
63 import com.google.gson.JsonParseException;
64
65 /**
66  * The {@link KM200GatewayHandler} is responsible for handling commands, which are
67  * sent to one of the channels.
68  *
69  * @author Markus Eckhardt - Initial contribution
70  */
71 @NonNullByDefault
72 public class KM200GatewayHandler extends BaseBridgeHandler {
73
74     private final Logger logger = LoggerFactory.getLogger(KM200GatewayHandler.class);
75
76     public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Collections.singleton(THING_TYPE_KMDEVICE);
77
78     private final Map<Channel, JsonObject> sendMap = Collections.synchronizedMap(new LinkedHashMap<>());
79
80     private List<KM200GatewayStatusListener> listeners = new CopyOnWriteArrayList<>();
81
82     /**
83      * shared instance of HTTP client for (a)synchronous calls
84      */
85     private ScheduledExecutorService executor;
86     private final KM200Device remoteDevice;
87     private final KM200DataHandler dataHandler;
88     private int readDelay;
89     private int refreshInterval;
90
91     public KM200GatewayHandler(Bridge bridge, HttpClient httpClient) {
92         super(bridge);
93         refreshInterval = 120;
94         readDelay = 100;
95         updateStatus(ThingStatus.UNINITIALIZED, ThingStatusDetail.CONFIGURATION_PENDING);
96         remoteDevice = new KM200Device(httpClient);
97         dataHandler = new KM200DataHandler(remoteDevice);
98         executor = Executors.newScheduledThreadPool(2, new NamedThreadFactory("org.openhab.binding.km200", true));
99     }
100
101     @Override
102     public void handleCommand(ChannelUID channelUID, Command command) {
103         Channel channel = getThing().getChannel(channelUID.getId());
104         if (null != channel) {
105             if (command instanceof DateTimeType || command instanceof DecimalType || command instanceof StringType) {
106                 prepareMessage(thing, channel, command);
107             } else {
108                 logger.warn("Unsupported Command: {} Class: {}", command.toFullString(), command.getClass());
109             }
110         }
111     }
112
113     @Override
114     public void initialize() {
115         try {
116             int maxKeyLen = Cipher.getMaxAllowedKeyLength("AES/ECB/NoPadding");
117             if (maxKeyLen <= 128) {
118                 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
119                         "Java Cryptography Extension (JCE) have to be installed");
120                 return;
121             }
122         } catch (NoSuchAlgorithmException e) {
123             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "AES encoding not supported");
124             return;
125         }
126         if (!getDevice().getInited()) {
127             logger.info("Update KM50/100/200 gateway configuration, it takes a minute....");
128             getConfiguration();
129             if (getDevice().isConfigured()) {
130                 if (!checkConfiguration()) {
131                     return;
132                 }
133                 /* configuration and communication seems to be ok */
134                 readCapabilities();
135                 updateStatus(ThingStatus.ONLINE);
136             } else {
137                 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "No bridge configured");
138                 logger.debug("The KM50/100/200 gateway configuration is not complete");
139                 return;
140             }
141
142             SendKM200Runnable sendRunnable = new SendKM200Runnable(sendMap, getDevice());
143             GetKM200Runnable receivingRunnable = new GetKM200Runnable(sendMap, this, getDevice());
144             if (!executor.isTerminated()) {
145                 executor = Executors.newScheduledThreadPool(2,
146                         new NamedThreadFactory("org.openhab.binding.km200", true));
147                 executor.scheduleWithFixedDelay(receivingRunnable, 30, refreshInterval, TimeUnit.SECONDS);
148                 executor.scheduleWithFixedDelay(sendRunnable, 60, refreshInterval * 2, TimeUnit.SECONDS);
149             }
150         }
151     }
152
153     @Override
154     public void dispose() {
155         executor.shutdown();
156         try {
157             if (!executor.awaitTermination(60000, TimeUnit.SECONDS)) {
158                 logger.debug("Services didn't finish in 60000 seconds!");
159             }
160         } catch (InterruptedException e) {
161             executor.shutdownNow();
162         }
163         synchronized (getDevice()) {
164             getDevice().setInited(false);
165             getDevice().setIP4Address("");
166             getDevice().setCryptKeyPriv("");
167             getDevice().setMD5Salt("");
168             getDevice().setGatewayPassword("");
169             getDevice().setPrivatePassword("");
170             getDevice().serviceTreeMap.clear();
171         }
172         updateStatus(ThingStatus.OFFLINE);
173     }
174
175     @Override
176     public void handleRemoval() {
177         for (Thing actThing : getThing().getThings()) {
178             actThing.setStatusInfo(new ThingStatusInfo(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE, ""));
179         }
180         this.updateStatus(ThingStatus.REMOVED);
181     }
182
183     /**
184      * Gets bridges configuration
185      */
186     private void getConfiguration() {
187         Configuration configuration = getConfig();
188         for (String key : configuration.keySet()) {
189             logger.trace("initialize Key: {} Value: {}", key, configuration.get(key));
190             switch (key) {
191                 case "ip4Address":
192                     String ip = (String) configuration.get("ip4Address");
193                     if (ip != null && !ip.isBlank()) {
194                         try {
195                             InetAddress.getByName(ip);
196                         } catch (UnknownHostException e) {
197                             logger.warn("IP4_address is not valid!: {}", ip);
198                         }
199                         getDevice().setIP4Address(ip);
200                     } else {
201                         logger.debug("No ip4_address configured!");
202                     }
203                     break;
204                 case "privateKey":
205                     String privateKey = (String) configuration.get("privateKey");
206                     if (privateKey != null && !privateKey.isBlank()) {
207                         getDevice().setCryptKeyPriv(privateKey);
208                     }
209                     break;
210                 case "md5Salt":
211                     String md5Salt = (String) configuration.get("md5Salt");
212                     if (md5Salt != null && !md5Salt.isBlank()) {
213                         getDevice().setMD5Salt(md5Salt);
214                     }
215                     break;
216                 case "gatewayPassword":
217                     String gatewayPassword = (String) configuration.get("gatewayPassword");
218                     if (gatewayPassword != null && !gatewayPassword.isBlank()) {
219                         getDevice().setGatewayPassword(gatewayPassword);
220                     }
221                     break;
222                 case "privatePassword":
223                     String privatePassword = (String) configuration.get("privatePassword");
224                     if (privatePassword != null && !privatePassword.isBlank()) {
225                         getDevice().setPrivatePassword(privatePassword);
226                     }
227                     break;
228                 case "refreshInterval":
229                     refreshInterval = ((BigDecimal) configuration.get("refreshInterval")).intValue();
230                     logger.debug("Set refresh interval to: {} seconds.", refreshInterval);
231                     break;
232                 case "readDelay":
233                     readDelay = ((BigDecimal) configuration.get("readDelay")).intValue();
234                     logger.debug("Set read delay to: {} seconds.", readDelay);
235                     break;
236                 case "maxNbrRepeats":
237                     Integer maxNbrRepeats = ((BigDecimal) configuration.get("maxNbrRepeats")).intValue();
238                     logger.debug("Set max. number of repeats to: {} seconds.", maxNbrRepeats);
239                     remoteDevice.setMaxNbrRepeats(maxNbrRepeats);
240                     break;
241             }
242         }
243     }
244
245     /**
246      * Checks bridges configuration
247      */
248     private boolean checkConfiguration() {
249         /* Get HTTP Data from device */
250         JsonObject nodeRoot = remoteDevice.getServiceNode("/gateway/DateTime");
251         if (nodeRoot == null || nodeRoot.isJsonNull()) {
252             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
253                     "No communication possible with gateway");
254             return false;
255         }
256         logger.debug("Test of the communication to the gateway was successful..");
257
258         /* Testing the received data, is decryption working? */
259         try {
260             nodeRoot.get("type").getAsString();
261             nodeRoot.get("id").getAsString();
262         } catch (JsonParseException e) {
263             logger.debug("The data is not readable, check the key and password configuration! {}", e.getMessage());
264             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Wrong gateway configuration");
265             return false;
266         }
267         return true;
268     }
269
270     /**
271      * Reads the devices capabilities and sets the data structures
272      */
273     private void readCapabilities() {
274         KM200VirtualServiceHandler virtualServiceHandler;
275         /* Checking of the device specific services and creating of a service list */
276         for (KM200ThingType thing : KM200ThingType.values()) {
277             String rootPath = thing.getRootPath();
278             if (!rootPath.isEmpty() && (rootPath.indexOf("/", 0) == rootPath.lastIndexOf("/", rootPath.length() - 1))) {
279                 if (remoteDevice.getBlacklistMap().contains(thing.getRootPath())) {
280                     logger.debug("Service on blacklist: {}", thing.getRootPath());
281                     return;
282                 }
283                 KM200ServiceHandler serviceHandler = new KM200ServiceHandler(thing.getRootPath(), null, remoteDevice);
284                 serviceHandler.initObject();
285             }
286         }
287         /* Now init the virtual services */
288         virtualServiceHandler = new KM200VirtualServiceHandler(remoteDevice);
289         virtualServiceHandler.initVirtualObjects();
290         /* Output all available services in the log file */
291         getDevice().listAllServices();
292         updateBridgeProperties();
293         getDevice().setInited(true);
294     }
295
296     /**
297      * Adds a GatewayConnectedListener
298      */
299     public void addGatewayStatusListener(KM200GatewayStatusListener listener) {
300         listeners.add(listener);
301         listener.gatewayStatusChanged(getThing().getStatus());
302     }
303
304     /**
305      * Removes a GatewayConnectedListener
306      */
307     public void removeHubStatusListener(KM200GatewayStatusListener listener) {
308         listeners.remove(listener);
309     }
310
311     /**
312      * Refreshes a channel
313      */
314     public void refreshChannel(Channel channel) {
315         GetSingleKM200Runnable runnable = new GetSingleKM200Runnable(sendMap, this, getDevice(), channel);
316         logger.debug("starting single runnable.");
317         scheduler.submit(runnable);
318     }
319
320     /**
321      * Updates bridges properties
322      */
323     private void updateBridgeProperties() {
324         List<String> propertyServices = new ArrayList<>();
325         propertyServices.add(KM200ThingType.GATEWAY.getRootPath());
326         propertyServices.add(KM200ThingType.SYSTEM.getRootPath());
327         Map<String, String> bridgeProperties = editProperties();
328
329         for (KM200ThingType tType : KM200ThingType.values()) {
330             List<String> asProperties = tType.asBridgeProperties();
331             String rootPath = tType.getRootPath();
332             if (rootPath.isEmpty()) {
333                 continue;
334             }
335             KM200ServiceObject serObj = getDevice().getServiceObject(rootPath);
336             if (null != serObj) {
337                 for (String subKey : asProperties) {
338                     if (serObj.serviceTreeMap.containsKey(subKey)) {
339                         KM200ServiceObject subKeyObj = serObj.serviceTreeMap.get(subKey);
340                         if (subKeyObj != null) {
341                             String subKeyType = subKeyObj.getServiceType();
342                             if (!DATA_TYPE_STRING_VALUE.equals(subKeyType)
343                                     && !DATA_TYPE_FLOAT_VALUE.equals(subKeyType)) {
344                                 continue;
345                             }
346                             if (bridgeProperties.containsKey(subKey)) {
347                                 bridgeProperties.remove(subKey);
348                             }
349                             Object value = subKeyObj.getValue();
350                             logger.trace("Add Property: {}  :{}", subKey, value);
351                             if (null != value) {
352                                 bridgeProperties.put(subKey, value.toString());
353                             }
354                         }
355                     }
356                 }
357             }
358         }
359         updateProperties(bridgeProperties);
360     }
361
362     /**
363      * Prepares a message for sending
364      */
365     public void prepareMessage(Thing thing, Channel channel, Command command) {
366         if (getDevice().getInited()) {
367             JsonObject newObject = null;
368             State state = null;
369             String service = KM200Utils.checkParameterReplacement(channel, getDevice());
370             String chTypes = channel.getAcceptedItemType();
371             if (null == chTypes) {
372                 logger.warn("Channel {} has not accepted item types", channel.getLabel());
373                 return;
374             }
375             logger.trace("handleCommand channel: {} service: {}", channel.getLabel(), service);
376             newObject = dataHandler.sendProvidersState(service, command, chTypes,
377                     KM200Utils.getChannelConfigurationStrings(channel));
378             synchronized (getDevice()) {
379                 KM200ServiceObject serObjekt = getDevice().getServiceObject(service);
380                 if (null != serObjekt) {
381                     if (newObject != null) {
382                         sendMap.put(channel, newObject);
383                     } else if (getDevice().containsService(service) && serObjekt.getVirtual() == 1) {
384                         String parent = serObjekt.getParent();
385                         for (Thing actThing : getThing().getThings()) {
386                             logger.trace("Checking: {}", actThing.getUID().getAsString());
387                             for (Channel tmpChannel : actThing.getChannels()) {
388                                 String tmpChTypes = tmpChannel.getAcceptedItemType();
389                                 if (null == tmpChTypes) {
390                                     logger.warn("Channel {} has not accepted item types", tmpChannel.getLabel());
391                                     return;
392                                 }
393                                 String actService = KM200Utils.checkParameterReplacement(tmpChannel, getDevice());
394                                 KM200ServiceObject actSerObjekt = getDevice().getServiceObject(actService);
395                                 if (null != actSerObjekt) {
396                                     String actParent = actSerObjekt.getParent();
397                                     if (actParent != null && actParent.equals(parent)) {
398                                         state = dataHandler.getProvidersState(actService, tmpChTypes,
399                                                 KM200Utils.getChannelConfigurationStrings(tmpChannel));
400                                         if (state != null) {
401                                             try {
402                                                 updateState(tmpChannel.getUID(), state);
403                                             } catch (IllegalStateException e) {
404                                                 logger.warn("Could not get updated item state", e);
405                                             }
406                                         }
407                                     }
408                                 }
409                             }
410                         }
411                     } else {
412                         logger.debug("Service is not availible: {}", service);
413                     }
414                 }
415             }
416         }
417     }
418
419     /**
420      * Update the children
421      */
422     // Every thing has here a handler
423     private void updateChildren(Map<Channel, JsonObject> sendMap, KM200GatewayHandler gatewayHandler,
424             KM200Device remoteDevice, @Nullable String parent) {
425         State state;
426         synchronized (remoteDevice) {
427             if (parent != null) {
428                 KM200ServiceObject serParObjekt = remoteDevice.getServiceObject(parent);
429                 if (null != serParObjekt) {
430                     serParObjekt.setUpdated(false);
431                 }
432             }
433             for (Thing actThing : gatewayHandler.getThing().getThings()) {
434                 for (Channel actChannel : actThing.getChannels()) {
435                     String actChTypes = actChannel.getAcceptedItemType();
436                     if (null == actChTypes) {
437                         logger.warn("Channel {} has not accepted item types", actChannel.getLabel());
438                         return;
439                     }
440                     logger.trace("Checking: {} Root: {}", actChannel.getUID().getAsString(),
441                             actChannel.getProperties().get("root"));
442                     KM200ThingHandler actHandler = (KM200ThingHandler) actThing.getHandler();
443                     if (actHandler != null) {
444                         if (!actHandler.checkLinked(actChannel)) {
445                             continue;
446                         }
447                     } else {
448                         continue;
449                     }
450                     String tmpService = KM200Utils.checkParameterReplacement(actChannel, remoteDevice);
451                     KM200ServiceObject tmpSerObjekt = remoteDevice.getServiceObject(tmpService);
452                     if (null != tmpSerObjekt) {
453                         if (parent == null || parent.equals(tmpSerObjekt.getParent())) {
454                             synchronized (sendMap) {
455                                 JsonObject obj = sendMap.get(actChannel);
456                                 if (obj != null) {
457                                     state = dataHandler.parseJSONData(obj, tmpSerObjekt.getServiceType(), tmpService,
458                                             actChTypes, KM200Utils.getChannelConfigurationStrings(actChannel));
459                                 } else {
460                                     state = dataHandler.getProvidersState(tmpService, actChTypes,
461                                             KM200Utils.getChannelConfigurationStrings(actChannel));
462                                 }
463                             }
464                             if (state != null) {
465                                 try {
466                                     gatewayHandler.updateState(actChannel.getUID(), state);
467                                 } catch (IllegalStateException e) {
468                                     logger.warn("Could not get updated item state", e);
469                                 }
470                             }
471                         }
472                         try {
473                             Thread.sleep(readDelay);
474                         } catch (InterruptedException e) {
475                             continue;
476                         }
477                     }
478                 }
479             }
480         }
481     }
482
483     /**
484      * Return the device instance.
485      */
486     public KM200Device getDevice() {
487         return remoteDevice;
488     }
489
490     /**
491      * The GetKM200Runnable class get the data from device to all items.
492      */
493     private class GetKM200Runnable implements Runnable {
494
495         private final KM200GatewayHandler gatewayHandler;
496         private final KM200Device remoteDevice;
497         private final Logger logger = LoggerFactory.getLogger(GetKM200Runnable.class);
498         private final Map<Channel, JsonObject> sendMap;
499
500         public GetKM200Runnable(Map<Channel, JsonObject> sendMap, KM200GatewayHandler gatewayHandler,
501                 KM200Device remoteDevice) {
502             this.sendMap = sendMap;
503             this.gatewayHandler = gatewayHandler;
504             this.remoteDevice = remoteDevice;
505         }
506
507         @Override
508         public void run() {
509             logger.debug("GetKM200Runnable");
510             synchronized (remoteDevice) {
511                 if (remoteDevice.getInited()) {
512                     remoteDevice.resetAllUpdates(remoteDevice.serviceTreeMap);
513                     updateChildren(sendMap, gatewayHandler, remoteDevice, null);
514                 }
515             }
516         }
517     }
518
519     /**
520      * The GetKM200Runnable class get the data from device for one channel.
521      */
522     private class GetSingleKM200Runnable implements Runnable {
523
524         private final Logger logger = LoggerFactory.getLogger(GetSingleKM200Runnable.class);
525         private final KM200GatewayHandler gatewayHandler;
526         private final KM200Device remoteDevice;
527         private final Channel channel;
528         private final Map<Channel, JsonObject> sendMap;
529
530         public GetSingleKM200Runnable(Map<Channel, JsonObject> sendMap, KM200GatewayHandler gatewayHandler,
531                 KM200Device remoteDevice, Channel channel) {
532             this.gatewayHandler = gatewayHandler;
533             this.remoteDevice = remoteDevice;
534             this.channel = channel;
535             this.sendMap = sendMap;
536         }
537
538         @Override
539         public void run() {
540             logger.debug("GetKM200Runnable");
541             State state = null;
542             synchronized (remoteDevice) {
543                 synchronized (sendMap) {
544                     if (sendMap.containsKey(channel)) {
545                         return;
546                     }
547                 }
548                 if (remoteDevice.getInited()) {
549                     logger.trace("Checking: {} Root: {}", channel.getUID().getAsString(),
550                             channel.getProperties().get("root"));
551                     String chTypes = channel.getAcceptedItemType();
552                     if (null == chTypes) {
553                         logger.warn("Channel {} has not accepted item types", channel.getLabel());
554                         return;
555                     }
556                     String service = KM200Utils.checkParameterReplacement(channel, remoteDevice);
557                     KM200ServiceObject object = remoteDevice.getServiceObject(service);
558                     if (null != object) {
559                         if (object.getVirtual() == 1) {
560                             String parent = object.getParent();
561                             updateChildren(sendMap, gatewayHandler, remoteDevice, parent);
562                         } else {
563                             object.setUpdated(false);
564                             synchronized (sendMap) {
565                                 KM200ServiceObject serObjekt = remoteDevice.getServiceObject(service);
566                                 if (null != serObjekt) {
567                                     JsonObject obj = sendMap.get(channel);
568                                     if (obj != null) {
569                                         state = dataHandler.parseJSONData(obj, serObjekt.getServiceType(), service,
570                                                 chTypes, KM200Utils.getChannelConfigurationStrings(channel));
571                                     } else {
572                                         state = dataHandler.getProvidersState(service, chTypes,
573                                                 KM200Utils.getChannelConfigurationStrings(channel));
574                                     }
575                                 }
576                                 if (state != null) {
577                                     try {
578                                         gatewayHandler.updateState(channel.getUID(), state);
579                                     } catch (IllegalStateException e) {
580                                         logger.warn("Could not get updated item state", e);
581                                     }
582                                 }
583                             }
584                         }
585                     }
586                 }
587             }
588         }
589     }
590
591     /**
592      * The sendKM200Thread class sends the data to the device.
593      */
594     private class SendKM200Runnable implements Runnable {
595
596         private final Logger logger = LoggerFactory.getLogger(SendKM200Runnable.class);
597         private final Map<Channel, JsonObject> newObject;
598         private final KM200Device remoteDevice;
599
600         public SendKM200Runnable(Map<Channel, JsonObject> newObject, KM200Device remoteDevice) {
601             this.newObject = newObject;
602             this.remoteDevice = remoteDevice;
603         }
604
605         @Override
606         public void run() {
607             logger.debug("Send-Executor started");
608             Map.Entry<Channel, JsonObject> nextEntry;
609             /* Check whether a new entry is availible, if yes then take and remove it */
610             do {
611                 nextEntry = null;
612                 synchronized (remoteDevice) {
613                     synchronized (newObject) {
614                         Iterator<Entry<Channel, JsonObject>> i = newObject.entrySet().iterator();
615                         if (i.hasNext()) {
616                             nextEntry = i.next();
617                             i.remove();
618                         }
619                     }
620                     if (nextEntry != null) {
621                         /* Now send the data to the device */
622                         Channel channel = nextEntry.getKey();
623                         JsonObject newObject = nextEntry.getValue();
624
625                         String service = KM200Utils.checkParameterReplacement(channel, remoteDevice);
626                         KM200ServiceObject object = remoteDevice.getServiceObject(service);
627                         if (null != object) {
628                             if (object.getVirtual() == 0) {
629                                 remoteDevice.setServiceNode(service, newObject);
630                             } else {
631                                 String parent = object.getParent();
632                                 if (null != parent) {
633                                     logger.trace("Sending: {} to : {}", newObject, service);
634                                     remoteDevice.setServiceNode(parent, newObject);
635                                 }
636                             }
637                         }
638                     }
639                 }
640             } while (nextEntry != null);
641         }
642     }
643 }