2 * Copyright (c) 2010-2021 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
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
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.binding.miio.internal.handler;
15 import static org.openhab.binding.miio.internal.MiIoBindingConstants.*;
17 import java.awt.Color;
18 import java.io.IOException;
20 import java.util.ArrayList;
21 import java.util.HashMap;
22 import java.util.LinkedHashSet;
23 import java.util.List;
25 import java.util.Map.Entry;
26 import java.util.concurrent.TimeUnit;
28 import javax.measure.Unit;
29 import javax.measure.format.ParserException;
31 import org.eclipse.jdt.annotation.NonNullByDefault;
32 import org.eclipse.jdt.annotation.Nullable;
33 import org.openhab.binding.miio.internal.MiIoBindingConfiguration;
34 import org.openhab.binding.miio.internal.MiIoCommand;
35 import org.openhab.binding.miio.internal.MiIoQuantiyTypes;
36 import org.openhab.binding.miio.internal.MiIoSendCommand;
37 import org.openhab.binding.miio.internal.Utils;
38 import org.openhab.binding.miio.internal.basic.ActionConditions;
39 import org.openhab.binding.miio.internal.basic.BasicChannelTypeProvider;
40 import org.openhab.binding.miio.internal.basic.CommandParameterType;
41 import org.openhab.binding.miio.internal.basic.Conversions;
42 import org.openhab.binding.miio.internal.basic.MiIoBasicChannel;
43 import org.openhab.binding.miio.internal.basic.MiIoBasicDevice;
44 import org.openhab.binding.miio.internal.basic.MiIoDatabaseWatchService;
45 import org.openhab.binding.miio.internal.basic.MiIoDeviceAction;
46 import org.openhab.binding.miio.internal.basic.MiIoDeviceActionCondition;
47 import org.openhab.binding.miio.internal.cloud.CloudConnector;
48 import org.openhab.binding.miio.internal.transport.MiIoAsyncCommunication;
49 import org.openhab.core.cache.ExpiringCache;
50 import org.openhab.core.library.types.DecimalType;
51 import org.openhab.core.library.types.HSBType;
52 import org.openhab.core.library.types.OnOffType;
53 import org.openhab.core.library.types.PercentType;
54 import org.openhab.core.library.types.QuantityType;
55 import org.openhab.core.library.types.StringType;
56 import org.openhab.core.library.unit.SIUnits;
57 import org.openhab.core.library.unit.Units;
58 import org.openhab.core.thing.Channel;
59 import org.openhab.core.thing.ChannelUID;
60 import org.openhab.core.thing.Thing;
61 import org.openhab.core.thing.binding.builder.ChannelBuilder;
62 import org.openhab.core.thing.binding.builder.ThingBuilder;
63 import org.openhab.core.thing.type.ChannelTypeRegistry;
64 import org.openhab.core.thing.type.ChannelTypeUID;
65 import org.openhab.core.types.Command;
66 import org.openhab.core.types.RefreshType;
67 import org.slf4j.Logger;
68 import org.slf4j.LoggerFactory;
70 import com.google.gson.Gson;
71 import com.google.gson.GsonBuilder;
72 import com.google.gson.JsonArray;
73 import com.google.gson.JsonElement;
74 import com.google.gson.JsonIOException;
75 import com.google.gson.JsonObject;
76 import com.google.gson.JsonPrimitive;
77 import com.google.gson.JsonSyntaxException;
80 * The {@link MiIoBasicHandler} is responsible for handling commands, which are
81 * sent to one of the channels.
83 * @author Marcel Verpaalen - Initial contribution
86 public class MiIoBasicHandler extends MiIoAbstractHandler {
87 private final Logger logger = LoggerFactory.getLogger(MiIoBasicHandler.class);
88 private boolean hasChannelStructure;
90 private final ExpiringCache<Boolean> updateDataCache = new ExpiringCache<>(CACHE_EXPIRY, () -> {
91 miIoScheduler.schedule(this::updateData, 0, TimeUnit.SECONDS);
95 List<MiIoBasicChannel> refreshList = new ArrayList<>();
96 private Map<String, MiIoBasicChannel> refreshListCustomCommands = new HashMap<>();
98 private @Nullable MiIoBasicDevice miioDevice;
99 private Map<ChannelUID, MiIoBasicChannel> actions = new HashMap<>();
100 private ChannelTypeRegistry channelTypeRegistry;
101 private BasicChannelTypeProvider basicChannelTypeProvider;
103 public MiIoBasicHandler(Thing thing, MiIoDatabaseWatchService miIoDatabaseWatchService,
104 CloudConnector cloudConnector, ChannelTypeRegistry channelTypeRegistry,
105 BasicChannelTypeProvider basicChannelTypeProvider) {
106 super(thing, miIoDatabaseWatchService, cloudConnector);
107 this.channelTypeRegistry = channelTypeRegistry;
108 this.basicChannelTypeProvider = basicChannelTypeProvider;
112 public void initialize() {
114 hasChannelStructure = false;
115 isIdentified = false;
116 refreshList = new ArrayList<>();
117 refreshListCustomCommands = new HashMap<>();
121 public void handleCommand(ChannelUID channelUID, Command receivedCommand) {
122 Command command = receivedCommand;
123 if (command == RefreshType.REFRESH) {
124 if (updateDataCache.isExpired()) {
125 logger.debug("Refreshing {}", channelUID);
126 updateDataCache.getValue();
128 logger.debug("Refresh {} skipped. Already refreshing", channelUID);
132 if (channelUID.getId().equals(CHANNEL_COMMAND)) {
133 cmds.put(sendCommand(command.toString()), command.toString());
136 logger.debug("Locating action for {} channel '{}': '{}'", getThing().getUID(), channelUID.getId(), command);
137 if (!actions.isEmpty()) {
138 final MiIoBasicChannel miIoBasicChannel = actions.get(channelUID);
139 if (miIoBasicChannel != null) {
141 for (MiIoDeviceAction action : miIoBasicChannel.getActions()) {
143 JsonElement value = null;
144 JsonArray parameters = action.getParameters().deepCopy();
145 for (int i = 0; i < action.getParameters().size(); i++) {
146 JsonElement p = action.getParameters().get(i);
147 if (p.isJsonPrimitive() && p.getAsString().toLowerCase().contains("$value$")) {
152 String cmd = action.getCommand();
153 CommandParameterType paramType = action.getparameterType();
154 if (command instanceof QuantityType) {
155 QuantityType<?> qtc = null;
157 if (!miIoBasicChannel.getUnit().isBlank()) {
158 Unit<?> unit = MiIoQuantiyTypes.get(miIoBasicChannel.getUnit());
160 qtc = ((QuantityType<?>) command).toUnit(unit);
163 } catch (ParserException e) {
167 command = new DecimalType(qtc.toBigDecimal());
169 logger.debug("Could not convert QuantityType to '{}'", miIoBasicChannel.getUnit());
170 command = new DecimalType(((QuantityType<?>) command).toBigDecimal());
173 if (paramType == CommandParameterType.COLOR) {
174 if (command instanceof HSBType) {
175 HSBType hsb = (HSBType) command;
176 Color color = Color.getHSBColor(hsb.getHue().floatValue() / 360,
177 hsb.getSaturation().floatValue() / 100, hsb.getBrightness().floatValue() / 100);
178 value = new JsonPrimitive(
179 (color.getRed() << 16) + (color.getGreen() << 8) + color.getBlue());
180 } else if (command instanceof DecimalType) {
181 // actually brightness is being set instead of a color
182 value = new JsonPrimitive(((DecimalType) command).toBigDecimal());
183 } else if (command instanceof OnOffType) {
184 value = new JsonPrimitive(command == OnOffType.ON ? 100 : 0);
186 logger.debug("Unsupported command for COLOR: {}", command);
188 } else if (command instanceof OnOffType) {
189 if (paramType == CommandParameterType.ONOFF) {
190 value = new JsonPrimitive(command == OnOffType.ON ? "on" : "off");
191 } else if (paramType == CommandParameterType.ONOFFPARA) {
192 cmd = cmd.replace("*", command == OnOffType.ON ? "on" : "off");
193 value = new JsonArray();
194 } else if (paramType == CommandParameterType.ONOFFBOOL) {
195 boolean boolCommand = command == OnOffType.ON;
196 value = new JsonPrimitive(boolCommand);
197 } else if (paramType == CommandParameterType.ONOFFBOOLSTRING) {
198 value = new JsonPrimitive(command == OnOffType.ON ? "true" : "false");
200 } else if (command instanceof DecimalType) {
201 value = new JsonPrimitive(((DecimalType) command).toBigDecimal());
202 } else if (command instanceof StringType) {
203 if (paramType == CommandParameterType.STRING) {
204 value = new JsonPrimitive(command.toString().toLowerCase());
205 } else if (paramType == CommandParameterType.CUSTOMSTRING) {
206 value = new JsonPrimitive(parameters.get(valuePos).getAsString().replace("$value",
207 command.toString().toLowerCase()));
210 value = new JsonPrimitive(command.toString().toLowerCase());
212 if (paramType == CommandParameterType.EMPTY) {
213 value = new JsonArray();
215 final MiIoDeviceActionCondition miIoDeviceActionCondition = action.getCondition();
216 if (miIoDeviceActionCondition != null) {
217 value = ActionConditions.executeAction(miIoDeviceActionCondition, deviceVariables, value,
220 // Check for miot channel
222 if (action.isMiOtAction()) {
223 value = miotActionTransform(action, miIoBasicChannel, value);
224 } else if (miIoBasicChannel.isMiOt()) {
225 value = miotTransform(miIoBasicChannel, value);
228 if (paramType != CommandParameterType.NONE && paramType != CommandParameterType.ONOFFPARA
230 if (parameters.size() > 0) {
231 parameters.set(valuePos, value);
233 parameters.add(value);
236 if (action.isMiOtAction() && parameters.size() > 0 && parameters.get(0).isJsonObject()) {
237 // hack as unlike any other commands miot actions parameters appear to be send as a json object
238 // instead of a json array
239 cmd = cmd + parameters.get(0).getAsJsonObject().toString();
241 cmd = cmd + parameters.toString();
244 logger.debug("Sending command {}", cmd);
247 if (miIoDeviceActionCondition != null) {
248 logger.debug("Conditional command {} not send, condition '{}' not met", cmd,
249 miIoDeviceActionCondition.getName());
251 logger.debug("Command not send. Value null");
256 logger.debug("Channel Id {} not in mapping.", channelUID.getId());
257 if (logger.isTraceEnabled()) {
258 for (Entry<ChannelUID, MiIoBasicChannel> a : actions.entrySet()) {
259 logger.trace("Available entries: {} : {}", a.getKey(), a.getValue().getFriendlyName());
263 updateDataCache.invalidateValue();
264 miIoScheduler.schedule(() -> {
266 }, 3000, TimeUnit.MILLISECONDS);
268 logger.debug("Actions not loaded yet, or none available");
272 private @Nullable JsonElement miotTransform(MiIoBasicChannel miIoBasicChannel, @Nullable JsonElement value) {
273 JsonObject json = new JsonObject();
274 json.addProperty("did", miIoBasicChannel.getChannel());
275 json.addProperty("siid", miIoBasicChannel.getSiid());
276 json.addProperty("piid", miIoBasicChannel.getPiid());
277 json.add("value", value);
281 private @Nullable JsonElement miotActionTransform(MiIoDeviceAction action, MiIoBasicChannel miIoBasicChannel,
282 @Nullable JsonElement value) {
283 JsonObject json = new JsonObject();
284 json.addProperty("did", miIoBasicChannel.getChannel());
285 json.addProperty("siid", action.getSiid());
286 json.addProperty("aiid", action.getAiid());
288 json.add("in", value);
294 protected synchronized void updateData() {
295 logger.debug("Periodic update for '{}' ({})", getThing().getUID().toString(), getThing().getThingTypeUID());
296 final MiIoAsyncCommunication miioCom = getConnection();
298 if (!hasConnection() || skipUpdate() || miioCom == null) {
301 checkChannelStructure();
303 sendCommand(MiIoCommand.MIIO_INFO);
305 final MiIoBasicDevice midevice = miioDevice;
306 if (midevice != null) {
307 refreshProperties(midevice);
308 refreshCustomProperties(midevice);
311 } catch (Exception e) {
312 logger.debug("Error while updating '{}': ", getThing().getUID().toString(), e);
316 private void refreshCustomProperties(MiIoBasicDevice midevice) {
317 for (MiIoBasicChannel miChannel : refreshListCustomCommands.values()) {
318 sendCommand(miChannel.getChannelCustomRefreshCommand());
322 private boolean refreshProperties(MiIoBasicDevice device) {
323 MiIoCommand command = MiIoCommand.getCommand(device.getDevice().getPropertyMethod());
324 int maxProperties = device.getDevice().getMaxProperties();
325 JsonArray getPropString = new JsonArray();
326 for (MiIoBasicChannel miChannel : refreshList) {
327 if (!isLinked(miChannel.getChannel())) {
328 logger.debug("Skip refresh of channel {} for {} as it is not linked", miChannel.getChannel(),
329 getThing().getUID());
332 JsonElement property;
333 if (miChannel.isMiOt()) {
334 JsonObject json = new JsonObject();
335 json.addProperty("did", miChannel.getProperty());
336 json.addProperty("siid", miChannel.getSiid());
337 json.addProperty("piid", miChannel.getPiid());
340 property = new JsonPrimitive(miChannel.getProperty());
342 getPropString.add(property);
343 if (getPropString.size() >= maxProperties) {
344 sendRefreshProperties(command, getPropString);
345 getPropString = new JsonArray();
348 if (getPropString.size() > 0) {
349 sendRefreshProperties(command, getPropString);
354 private void sendRefreshProperties(MiIoCommand command, JsonArray getPropString) {
355 sendCommand(command, getPropString.toString());
359 * Checks if the channel structure has been build already based on the model data. If not build it.
361 private void checkChannelStructure() {
362 final MiIoBindingConfiguration configuration = this.configuration;
363 if (configuration == null) {
366 if (!hasChannelStructure) {
367 if (configuration.model == null || configuration.model.isEmpty()) {
368 logger.debug("Model needs to be determined");
369 isIdentified = false;
371 hasChannelStructure = buildChannelStructure(configuration.model);
374 if (hasChannelStructure) {
375 refreshList = new ArrayList<>();
376 final MiIoBasicDevice miioDevice = this.miioDevice;
377 if (miioDevice != null) {
378 for (MiIoBasicChannel miChannel : miioDevice.getDevice().getChannels()) {
379 if (miChannel.getRefresh()) {
380 if (miChannel.getChannelCustomRefreshCommand().isBlank()) {
381 refreshList.add(miChannel);
383 String i = miChannel.getChannelCustomRefreshCommand().split("\\[")[0];
384 refreshListCustomCommands.put(i.trim(), miChannel);
393 private boolean buildChannelStructure(String deviceName) {
394 logger.debug("Building Channel Structure for {} - Model: {}", getThing().getUID().toString(), deviceName);
395 URL fn = miIoDatabaseWatchService.getDatabaseUrl(deviceName);
397 logger.warn("Database entry for model '{}' cannot be found.", deviceName);
401 JsonObject deviceMapping = Utils.convertFileToJSON(fn);
402 logger.debug("Using device database: {} for device {}", fn.getFile(), deviceName);
403 Gson gson = new GsonBuilder().serializeNulls().create();
404 miioDevice = gson.fromJson(deviceMapping, MiIoBasicDevice.class);
405 for (Channel ch : getThing().getChannels()) {
406 logger.debug("Current thing channels {}, type: {}", ch.getUID(), ch.getChannelTypeUID());
408 ThingBuilder thingBuilder = editThing();
409 int channelsAdded = 0;
411 // make a map of the actions
412 actions = new HashMap<>();
413 final MiIoBasicDevice device = this.miioDevice;
414 if (device != null) {
415 for (Channel cn : getThing().getChannels()) {
416 logger.trace("Channel '{}' for thing {} already exist... removing", cn.getUID(),
417 getThing().getUID());
418 if (!PERSISTENT_CHANNELS.contains(cn.getUID().getId().toString())) {
419 thingBuilder.withoutChannels(cn);
422 for (MiIoBasicChannel miChannel : device.getDevice().getChannels()) {
423 logger.debug("properties {}", miChannel);
424 if (!miChannel.getType().isEmpty()) {
425 basicChannelTypeProvider.addChannelType(miChannel, deviceName);
426 ChannelUID channelUID = addChannel(thingBuilder, miChannel, deviceName);
427 if (channelUID != null) {
428 actions.put(channelUID, miChannel);
431 logger.debug("Channel for {} ({}) not loaded", miChannel.getChannel(),
432 miChannel.getFriendlyName());
435 logger.debug("Channel {} ({}), not loaded, missing type", miChannel.getChannel(),
436 miChannel.getFriendlyName());
440 // only update if channels were added/removed
441 if (channelsAdded > 0) {
442 logger.debug("Current thing channels added: {}", channelsAdded);
443 updateThing(thingBuilder.build());
446 } catch (JsonIOException | JsonSyntaxException e) {
447 logger.warn("Error parsing database Json", e);
448 } catch (IOException e) {
449 logger.warn("Error reading database file", e);
450 } catch (Exception e) {
451 logger.warn("Error creating channel structure", e);
456 private @Nullable ChannelUID addChannel(ThingBuilder thingBuilder, MiIoBasicChannel miChannel, String model) {
457 String channel = miChannel.getChannel();
458 String dataType = miChannel.getType();
459 if (channel.isEmpty() || dataType.isEmpty()) {
460 logger.info("Channel '{}', UID '{}' cannot be added incorrectly configured database. ", channel,
461 getThing().getUID());
464 ChannelUID channelUID = new ChannelUID(getThing().getUID(), channel);
465 ChannelBuilder newChannel = ChannelBuilder.create(channelUID, dataType).withLabel(miChannel.getFriendlyName());
466 boolean useGeneratedChannelType = false;
467 if (!miChannel.getChannelType().isBlank()) {
468 ChannelTypeUID channelTypeUID = new ChannelTypeUID(miChannel.getChannelType());
469 if (channelTypeRegistry.getChannelType(channelTypeUID) != null) {
470 newChannel = newChannel.withType(channelTypeUID);
471 final LinkedHashSet<String> tags = miChannel.getTags();
472 if (tags != null && tags.size() > 0) {
473 newChannel.withDefaultTags(tags);
476 logger.debug("ChannelType '{}' is not available. Check the Json file for {}", channelTypeUID, model);
477 useGeneratedChannelType = true;
480 useGeneratedChannelType = true;
482 if (useGeneratedChannelType) {
483 newChannel = newChannel
484 .withType(new ChannelTypeUID(BINDING_ID, model.toUpperCase().replace(".", "_") + "_" + channel));
485 final LinkedHashSet<String> tags = miChannel.getTags();
486 if (tags != null && tags.size() > 0) {
487 newChannel.withDefaultTags(tags);
490 thingBuilder.withChannel(newChannel.build());
494 private @Nullable MiIoBasicChannel getChannel(String parameter) {
495 for (MiIoBasicChannel refreshEntry : refreshList) {
496 if (refreshEntry.getProperty().equals(parameter)) {
500 logger.trace("Did not find channel for {} in {}", parameter, refreshList);
504 private void updatePropsFromJsonArray(MiIoSendCommand response) {
505 JsonArray res = response.getResult().getAsJsonArray();
506 JsonArray para = parser.parse(response.getCommandString()).getAsJsonObject().get("params").getAsJsonArray();
507 if (res.size() != para.size()) {
508 logger.debug("Unexpected size different. Request size {}, response size {}. (Req: {}, Resp:{})",
509 para.size(), res.size(), para, res);
512 for (int i = 0; i < para.size(); i++) {
513 // This is a miot parameter
515 final JsonElement paraElement = para.get(i);
516 if (paraElement.isJsonObject()) { // miot channel
517 param = paraElement.getAsJsonObject().get("did").getAsString();
519 param = paraElement.getAsString();
521 JsonElement val = res.get(i);
522 if (val.isJsonNull()) {
523 logger.debug("Property '{}' returned null (is it supported?).", param);
525 } else if (val.isJsonObject()) { // miot channel
526 val = val.getAsJsonObject().get("value");
528 MiIoBasicChannel basicChannel = getChannel(param);
529 updateChannel(basicChannel, param, val);
533 private void updatePropsFromJsonObject(MiIoSendCommand response) {
534 JsonObject res = response.getResult().getAsJsonObject();
535 for (Object k : res.keySet()) {
536 String param = (String) k;
537 JsonElement val = res.get(param);
538 if (val.isJsonNull()) {
539 logger.debug("Property '{}' returned null (is it supported?).", param);
542 MiIoBasicChannel basicChannel = getChannel(param);
543 updateChannel(basicChannel, param, val);
547 private void updateChannel(@Nullable MiIoBasicChannel basicChannel, String param, JsonElement value) {
548 JsonElement val = value;
549 if (basicChannel == null) {
550 logger.debug("Channel not found for {}", param);
553 final String transformation = basicChannel.getTransfortmation();
554 if (transformation != null) {
555 JsonElement transformed = Conversions.execute(transformation, val);
556 logger.debug("Transformed with '{}': {} {} -> {} ", transformation, basicChannel.getFriendlyName(), val,
561 String[] chType = basicChannel.getType().toLowerCase().split(":");
564 quantityTypeUpdate(basicChannel, val, chType.length > 1 ? chType[1] : "");
567 updateState(basicChannel.getChannel(), new PercentType(val.getAsBigDecimal()));
570 updateState(basicChannel.getChannel(), new StringType(val.getAsString()));
573 updateState(basicChannel.getChannel(), val.getAsString().toLowerCase().equals("on")
574 || val.getAsString().toLowerCase().equals("true") ? OnOffType.ON : OnOffType.OFF);
577 Color rgb = new Color(val.getAsInt());
578 HSBType hsb = HSBType.fromRGB(rgb.getRed(), rgb.getGreen(), rgb.getBlue());
579 updateState(basicChannel.getChannel(), hsb);
582 logger.debug("No update logic for channeltype '{}' ", basicChannel.getType());
584 } catch (Exception e) {
585 logger.debug("Error updating {} property {} with '{}' : {}: {}", getThing().getUID(),
586 basicChannel.getChannel(), val, e.getClass().getCanonicalName(), e.getMessage());
587 logger.trace("Property update error detail:", e);
591 private void quantityTypeUpdate(MiIoBasicChannel basicChannel, JsonElement val, String type) {
592 if (!basicChannel.getUnit().isBlank()) {
593 Unit<?> unit = MiIoQuantiyTypes.get(basicChannel.getUnit());
595 logger.debug("'{}' channel '{}' has unit '{}' with symbol '{}'.", getThing().getUID(),
596 basicChannel.getChannel(), basicChannel.getUnit(), unit);
597 updateState(basicChannel.getChannel(), new QuantityType<>(val.getAsBigDecimal(), unit));
600 logger.debug("Unit '{}' used by '{}' channel '{}' is not found.. using default unit.",
601 getThing().getUID(), basicChannel.getUnit(), basicChannel.getChannel());
604 // if no unit is provided or unit not found use default units, these units have so far been seen for miio
606 switch (type.toLowerCase()) {
608 updateState(basicChannel.getChannel(), new QuantityType<>(val.getAsBigDecimal(), SIUnits.CELSIUS));
610 case "electriccurrent":
611 updateState(basicChannel.getChannel(), new QuantityType<>(val.getAsBigDecimal(), Units.AMPERE));
614 updateState(basicChannel.getChannel(), new QuantityType<>(val.getAsBigDecimal(), Units.WATT));
617 updateState(basicChannel.getChannel(), new QuantityType<>(val.getAsBigDecimal(), Units.HOUR));
620 updateState(basicChannel.getChannel(), new DecimalType(val.getAsBigDecimal()));
625 public void onMessageReceived(MiIoSendCommand response) {
626 super.onMessageReceived(response);
627 if (response.isError()) {
631 switch (response.getCommand()) {
637 if (response.getResult().isJsonArray()) {
638 updatePropsFromJsonArray(response);
639 } else if (response.getResult().isJsonObject()) {
640 updatePropsFromJsonObject(response);
644 if (refreshListCustomCommands.containsKey(response.getMethod())) {
645 logger.debug("Processing custom refresh command response for !{}", response.getMethod());
646 final MiIoBasicChannel ch = refreshListCustomCommands.get(response.getMethod());
648 if (response.getResult().isJsonArray()) {
649 JsonArray cmdResponse = response.getResult().getAsJsonArray();
650 final String transformation = ch.getTransfortmation();
651 if (transformation == null || transformation.isBlank()) {
652 JsonElement response0 = cmdResponse.get(0);
653 updateChannel(ch, ch.getChannel(), response0.isJsonPrimitive() ? response0
654 : new JsonPrimitive(response0.toString()));
656 updateChannel(ch, ch.getChannel(), cmdResponse);
659 updateChannel(ch, ch.getChannel(), new JsonPrimitive(response.getResult().toString()));
665 } catch (Exception e) {
666 logger.debug("Error while handing message {}", response.getResponse(), e);