2 * Copyright (c) 2010-2023 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.zway.internal.handler;
15 import static de.fh_zwickau.informatik.sensor.ZWayConstants.*;
16 import static org.openhab.binding.zway.internal.ZWayBindingConstants.*;
18 import java.util.Calendar;
19 import java.util.HashMap;
20 import java.util.List;
22 import java.util.concurrent.ScheduledFuture;
23 import java.util.concurrent.TimeUnit;
25 import org.openhab.binding.zway.internal.ZWayBindingConstants;
26 import org.openhab.binding.zway.internal.converter.ZWayDeviceStateConverter;
27 import org.openhab.core.library.types.DecimalType;
28 import org.openhab.core.library.types.HSBType;
29 import org.openhab.core.library.types.OnOffType;
30 import org.openhab.core.library.types.PercentType;
31 import org.openhab.core.library.types.StopMoveType;
32 import org.openhab.core.library.types.UpDownType;
33 import org.openhab.core.thing.Bridge;
34 import org.openhab.core.thing.Channel;
35 import org.openhab.core.thing.ChannelUID;
36 import org.openhab.core.thing.Thing;
37 import org.openhab.core.thing.ThingStatus;
38 import org.openhab.core.thing.ThingStatusDetail;
39 import org.openhab.core.thing.ThingStatusInfo;
40 import org.openhab.core.thing.binding.BaseThingHandler;
41 import org.openhab.core.thing.binding.ThingHandler;
42 import org.openhab.core.thing.binding.builder.ChannelBuilder;
43 import org.openhab.core.thing.binding.builder.ThingBuilder;
44 import org.openhab.core.thing.type.ChannelTypeUID;
45 import org.openhab.core.types.Command;
46 import org.openhab.core.types.RefreshType;
47 import org.slf4j.Logger;
48 import org.slf4j.LoggerFactory;
50 import de.fh_zwickau.informatik.sensor.model.devices.Device;
51 import de.fh_zwickau.informatik.sensor.model.devices.DeviceList;
52 import de.fh_zwickau.informatik.sensor.model.devices.types.Battery;
53 import de.fh_zwickau.informatik.sensor.model.devices.types.Doorlock;
54 import de.fh_zwickau.informatik.sensor.model.devices.types.SensorBinary;
55 import de.fh_zwickau.informatik.sensor.model.devices.types.SensorDiscrete;
56 import de.fh_zwickau.informatik.sensor.model.devices.types.SensorMultilevel;
57 import de.fh_zwickau.informatik.sensor.model.devices.types.SwitchBinary;
58 import de.fh_zwickau.informatik.sensor.model.devices.types.SwitchControl;
59 import de.fh_zwickau.informatik.sensor.model.devices.types.SwitchMultilevel;
60 import de.fh_zwickau.informatik.sensor.model.devices.types.SwitchRGBW;
61 import de.fh_zwickau.informatik.sensor.model.devices.types.SwitchToggle;
62 import de.fh_zwickau.informatik.sensor.model.devices.types.Thermostat;
63 import de.fh_zwickau.informatik.sensor.model.devices.types.ToggleButton;
64 import de.fh_zwickau.informatik.sensor.model.zwaveapi.devices.ZWaveDevice;
67 * The {@link ZWayDeviceHandler} is responsible for handling commands, which are
68 * sent to one of the channels.
70 * @author Patrick Hecker - Initial contribution, remove observer mechanism
71 * @author Johannes Einig - Now uses the bridge handler cached device list
73 public abstract class ZWayDeviceHandler extends BaseThingHandler {
74 private final Logger logger = LoggerFactory.getLogger(getClass());
76 private DevicePolling devicePolling;
77 private ScheduledFuture<?> pollingJob;
78 protected Calendar lastUpdate;
80 protected abstract void refreshLastUpdate();
83 * Initialize polling job
85 private class Initializer implements Runnable {
89 // https://community.openhab.org/t/oh2-major-bug-with-scheduled-jobs/12350/11
90 // If any execution of the task encounters an exception, subsequent executions are
91 // suppressed. Otherwise, the task will only terminate via cancellation or
92 // termination of the executor.
94 // Z-Way bridge have to be ONLINE because configuration is needed
95 ZWayBridgeHandler zwayBridgeHandler = getZWayBridgeHandler();
96 if (zwayBridgeHandler == null || !zwayBridgeHandler.getThing().getStatus().equals(ThingStatus.ONLINE)) {
97 logger.debug("Z-Way bridge handler not found or not ONLINE.");
101 // Initialize device polling
102 if (pollingJob == null || pollingJob.isCancelled()) {
103 logger.debug("Starting polling job at intervall {}",
104 zwayBridgeHandler.getZWayBridgeConfiguration().getPollingInterval());
105 pollingJob = scheduler.scheduleWithFixedDelay(devicePolling, 10,
106 zwayBridgeHandler.getZWayBridgeConfiguration().getPollingInterval(), TimeUnit.SECONDS);
108 // Called when thing or bridge updated ...
109 logger.debug("Polling is allready active");
111 } catch (Throwable t) {
112 if (t instanceof Exception) {
113 logger.error("{}", t.getMessage());
114 } else if (t instanceof Error) {
115 logger.error("{}", t.getMessage());
117 logger.error("Unexpected error");
119 if (getThing().getStatus() == ThingStatus.ONLINE) {
120 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.HANDLER_INITIALIZING_ERROR,
121 "Error occurred when starting polling.");
127 private class Disposer implements Runnable {
131 // Z-Way bridge have to be ONLINE because configuration is needed
132 ZWayBridgeHandler zwayBridgeHandler = getZWayBridgeHandler();
133 if (zwayBridgeHandler == null || !zwayBridgeHandler.getThing().getStatus().equals(ThingStatus.ONLINE)) {
134 logger.debug("Z-Way bridge handler not found or not ONLINE.");
136 // status update will remove finally
137 updateStatus(ThingStatus.REMOVED);
142 // status update will remove finally
143 updateStatus(ThingStatus.REMOVED);
147 public ZWayDeviceHandler(Thing thing) {
150 devicePolling = new DevicePolling();
153 protected synchronized ZWayBridgeHandler getZWayBridgeHandler() {
154 Bridge bridge = getBridge();
155 if (bridge == null) {
158 ThingHandler handler = bridge.getHandler();
159 if (handler instanceof ZWayBridgeHandler) {
160 return (ZWayBridgeHandler) handler;
167 public void initialize() {
170 // Start an extra thread to check the connection, because it takes sometimes more
171 // than 5000 milliseconds and the handler will suspend (ThingStatus.UNINITIALIZED).
172 scheduler.execute(new Initializer());
176 public void dispose() {
177 if (pollingJob != null && !pollingJob.isCancelled()) {
178 pollingJob.cancel(true);
186 public void handleRemoval() {
187 logger.debug("Handle removal Z-Way device ...");
189 // Start an extra thread, because it takes sometimes more
190 // than 5000 milliseconds and the handler will suspend (ThingStatus.UNINITIALIZED).
191 scheduler.execute(new Disposer());
193 // super.handleRemoval() called in every case in scheduled task ...
197 public void bridgeStatusChanged(ThingStatusInfo bridgeStatusInfo) {
198 // Only called if status ONLINE or OFFLINE
199 logger.debug("Z-Way bridge status changed: {}", bridgeStatusInfo);
201 if (bridgeStatusInfo.getStatus().equals(ThingStatus.OFFLINE)) {
202 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE, "Bridge status is offline.");
203 } else if (bridgeStatusInfo.getStatus().equals(ThingStatus.ONLINE)) {
204 // Initialize thing, if all OK the status of device thing will be ONLINE
206 // Start an extra thread to check the connection, because it takes sometimes more
207 // than 5000 milliseconds and the handler will suspend (ThingStatus.UNINITIALIZED).
208 scheduler.execute(new Initializer());
212 private class DevicePolling implements Runnable {
215 logger.debug("Starting polling for device: {}", getThing().getLabel());
216 if (getThing().getStatus().equals(ThingStatus.ONLINE)) {
217 // Refresh device states
218 for (Channel channel : getThing().getChannels()) {
219 logger.debug("Checking link state of channel: {}", channel.getLabel());
220 if (isLinked(channel.getUID().getId())) {
221 logger.debug("Refresh items that linked with channel: {}", channel.getLabel());
223 // https://community.openhab.org/t/oh2-major-bug-with-scheduled-jobs/12350/11
224 // If any execution of the task encounters an exception, subsequent executions are
225 // suppressed. Otherwise, the task will only terminate via cancellation or
226 // termination of the executor.
228 refreshChannel(channel);
229 } catch (Throwable t) {
230 if (t instanceof Exception) {
231 logger.error("Error occurred when performing polling:{}", t.getMessage());
232 } else if (t instanceof Error) {
233 logger.error("Error occurred when performing polling:{}", t.getMessage());
235 logger.error("Error occurred when performing polling: Unexpected error");
237 if (getThing().getStatus() == ThingStatus.ONLINE) {
238 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.NONE,
239 "Error occurred when performing polling.");
243 logger.debug("Polling for device: {} not possible (channel {} not linked", thing.getLabel(),
248 // Refresh last update
251 logger.debug("Polling not possible, Z-Way device isn't ONLINE");
256 private synchronized void setLocation() {
257 Map<String, String> properties = getThing().getProperties();
258 // Load location from properties
259 String location = properties.get(ZWayBindingConstants.DEVICE_PROP_LOCATION);
260 if (location != null && !location.equals("") && getThing().getLocation() == null) {
261 logger.debug("Set location to {}", location);
262 ThingBuilder thingBuilder = editThing();
263 thingBuilder.withLocation(location);
264 thingBuilder.withLabel(thing.getLabel());
265 updateThing(thingBuilder.build());
269 protected void refreshAllChannels() {
270 scheduler.execute(new DevicePolling());
273 private void refreshChannel(Channel channel) {
274 // Check Z-Way bridge handler
275 ZWayBridgeHandler zwayBridgeHandler = getZWayBridgeHandler();
276 if (zwayBridgeHandler == null || !zwayBridgeHandler.getThing().getStatus().equals(ThingStatus.ONLINE)) {
277 logger.debug("Z-Way bridge handler not found or not ONLINE.");
281 // Check device id associated with channel
282 String deviceId = channel.getProperties().get("deviceId");
283 if (deviceId != null) {
284 // Load and check device from Z-Way server
285 DeviceList deviceList = zwayBridgeHandler.getZWayApi().getDevices();
286 if (deviceList != null) {
287 // 1.) Load only the current value from Z-Way server
288 Device device = deviceList.getDeviceById(deviceId);
289 if (device == null) {
290 logger.debug("ZAutomation device not found.");
295 updateState(channel.getUID(), ZWayDeviceStateConverter.toState(device, channel));
296 } catch (IllegalArgumentException iae) {
298 "IllegalArgumentException ({}) during refresh channel for device: {} (level: {}) with channel: {}",
299 iae.getMessage(), device.getMetrics().getTitle(), device.getMetrics().getLevel(),
300 channel.getChannelTypeUID());
302 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.NONE,
303 "Channel refresh for device: " + device.getMetrics().getTitle() + " (level: "
304 + device.getMetrics().getLevel() + ") with channel: " + channel.getChannelTypeUID()
307 // 2.) Trigger update function, soon as the value has been updated, openHAB will be notified
310 } catch (Exception e) {
311 logger.debug("{} doesn't support update (triggered during refresh channel)",
312 device.getMetrics().getTitle());
315 logger.warn("Devices not loaded");
318 // Check channel for command classes
319 // Channel thermostat mode
320 if (channel.getUID().equals(new ChannelUID(getThing().getUID(), THERMOSTAT_MODE_CC_CHANNEL))) {
321 // Load physical device
322 String nodeIdString = channel.getProperties().get("nodeId");
323 ZWaveDevice physicalDevice = nodeIdString == null ? null
324 : zwayBridgeHandler.getZWayApi().getZWaveDevice(Integer.parseInt(nodeIdString));
325 if (physicalDevice != null) {
326 updateState(channel.getUID(), new DecimalType(physicalDevice.getInstances().get0()
327 .getCommandClasses().get64().getData().getMode().getValue()));
334 public void channelLinked(ChannelUID channelUID) {
335 logger.debug("Z-Way device channel linked: {}", channelUID);
337 ZWayBridgeHandler zwayBridgeHandler = getZWayBridgeHandler();
338 if (zwayBridgeHandler == null || !zwayBridgeHandler.getThing().getStatus().equals(ThingStatus.ONLINE)) {
339 logger.debug("Z-Way bridge handler not found or not ONLINE.");
343 // Method called when channel linked and not when server started!!!
345 super.channelLinked(channelUID); // performs a refresh command
349 public void channelUnlinked(ChannelUID channelUID) {
350 logger.debug("Z-Way device channel unlinked: {}", channelUID);
352 ZWayBridgeHandler zwayBridgeHandler = getZWayBridgeHandler();
353 if (zwayBridgeHandler == null || !zwayBridgeHandler.getThing().getStatus().equals(ThingStatus.ONLINE)) {
354 logger.debug("Z-Way bridge handler not found or not ONLINE.");
358 super.channelUnlinked(channelUID);
362 public void handleCommand(ChannelUID channelUID, final Command command) {
363 logger.debug("Handle command for channel: {} with command: {}", channelUID.getId(), command.toString());
365 // Check Z-Way bridge handler
366 ZWayBridgeHandler zwayBridgeHandler = getZWayBridgeHandler();
367 if (zwayBridgeHandler == null || !zwayBridgeHandler.getThing().getStatus().equals(ThingStatus.ONLINE)) {
368 logger.debug("Z-Way bridge handler not found or not ONLINE.");
372 // Load device id from channel's properties for the compatibility of ZAutomation and ZWave devices
373 final Channel channel = getThing().getChannel(channelUID.getId());
374 final String deviceId = channel.getProperties().get("deviceId");
376 if (deviceId != null) {
377 DeviceList deviceList = zwayBridgeHandler.getDeviceList();
378 if (deviceList != null) {
379 Device device = deviceList.getDeviceById(deviceId);
380 if (device == null) {
381 logger.debug("ZAutomation device not found.");
386 if (command instanceof RefreshType) {
387 logger.debug("Handle command: RefreshType");
389 refreshChannel(channel);
391 if (device instanceof Battery) {
392 // possible commands: update()
393 } else if (device instanceof Doorlock) {
394 // possible commands: open(), close()
395 if (command instanceof OnOffType) {
396 logger.debug("Handle command: OnOffType");
397 if (command.equals(OnOffType.ON)) {
399 } else if (command.equals(OnOffType.OFF)) {
403 } else if (device instanceof SensorBinary) {
404 // possible commands: update()
405 } else if (device instanceof SensorMultilevel) {
406 // possible commands: update()
407 } else if (device instanceof SwitchBinary) {
408 // possible commands: update(), on(), off()
409 if (command instanceof OnOffType) {
410 logger.debug("Handle command: OnOffType");
412 if (command.equals(OnOffType.ON)) {
414 } else if (command.equals(OnOffType.OFF)) {
418 } else if (device instanceof SwitchMultilevel) {
419 // possible commands: update(), on(), up(), off(), down(), min(), max(), upMax(),
420 // increase(), decrease(), exact(level), exactSmooth(level, duration), stop(), startUp(),
422 if (command instanceof DecimalType || command instanceof PercentType) {
423 logger.debug("Handle command: DecimalType");
425 device.exact(command.toString());
426 } else if (command instanceof UpDownType) {
427 if (command.equals(UpDownType.UP)) {
428 logger.debug("Handle command: UpDownType.Up");
431 } else if (command.equals(UpDownType.DOWN)) {
432 logger.debug("Handle command: UpDownType.Down");
436 } else if (command instanceof StopMoveType) {
437 logger.debug("Handle command: StopMoveType");
440 } else if (command instanceof OnOffType) {
441 logger.debug("Handle command: OnOffType");
443 if (command.equals(OnOffType.ON)) {
445 } else if (command.equals(OnOffType.OFF)) {
449 } else if (device instanceof SwitchRGBW) {
450 // possible commands: on(), off(), exact(red, green, blue)
451 if (command instanceof HSBType) {
452 logger.debug("Handle command: HSBType");
454 HSBType hsb = (HSBType) command;
457 if (hsb.getBrightness().intValue() > 0) {
458 if (device.getMetrics().getLevel().toLowerCase().equals("off")) {
463 int red = (int) Math.round(255 * (hsb.getRed().doubleValue() / 100));
464 int green = (int) Math.round(255 * (hsb.getGreen().doubleValue() / 100));
465 int blue = (int) Math.round(255 * (hsb.getBlue().doubleValue() / 100));
467 device.exact(red, green, blue);
472 } else if (device instanceof Thermostat) {
473 if (command instanceof DecimalType) {
474 logger.debug("Handle command: DecimalType");
476 device.exact(command.toString());
478 } else if (device instanceof SwitchControl) {
479 // possible commands: on(), off(), exact(level), upstart(), upstop(), downstart(),
481 if (command instanceof OnOffType) {
482 logger.debug("Handle command: OnOffType");
484 if (command.equals(OnOffType.ON)) {
486 } else if (command.equals(OnOffType.OFF)) {
490 } else if (device instanceof ToggleButton || device instanceof SwitchToggle) {
491 // possible commands: on(), off(), exact(level), upstart(), upstop(), downstart(),
493 if (command instanceof OnOffType) {
494 logger.debug("Handle command: OnOffType");
496 if (command.equals(OnOffType.ON)) {
498 } // no else - only ON command is sent to Z-Way
502 } catch (UnsupportedOperationException e) {
503 logger.warn("Unknown command: {}", e.getMessage());
506 logger.warn("Devices not loaded");
508 } else if (channel.getUID().equals(new ChannelUID(getThing().getUID(), THERMOSTAT_MODE_CC_CHANNEL))) {
509 // Load physical device
510 if (command instanceof DecimalType) {
511 String nodeIdString = channel.getProperties().get("nodeId");
512 logger.debug("Handle command: DecimalType");
513 if (nodeIdString != null) {
514 zwayBridgeHandler.getZWayApi().getZWaveDeviceThermostatModeSet(Integer.parseInt(nodeIdString),
515 Integer.parseInt(command.toString()));
517 } else if (command instanceof RefreshType) {
518 logger.debug("Handle command: RefreshType");
519 refreshChannel(channel);
524 protected synchronized void addDeviceAsChannel(Device device) {
527 // Device.metrics.probeType
529 // Device.metrics.icon
533 // Default, depends on device type
535 if (device != null) {
536 logger.debug("Add virtual device as channel: {}", device.getMetrics().getTitle());
538 HashMap<String, String> properties = new HashMap<>();
539 properties.put("deviceId", device.getDeviceId());
542 String acceptedItemType = "";
544 // 1. Set basically channel types without further information
545 if (device instanceof Battery) {
546 id = BATTERY_CHANNEL;
547 acceptedItemType = "Number";
548 } else if (device instanceof Doorlock) {
549 id = DOORLOCK_CHANNEL;
550 acceptedItemType = "Switch";
551 } else if (device instanceof SensorBinary) {
552 id = SENSOR_BINARY_CHANNEL;
553 acceptedItemType = "Switch";
554 } else if (device instanceof SensorMultilevel) {
555 id = SENSOR_MULTILEVEL_CHANNEL;
556 acceptedItemType = "Number";
557 } else if (device instanceof SwitchBinary) {
558 id = SWITCH_BINARY_CHANNEL;
559 acceptedItemType = "Switch";
560 } else if (device instanceof SwitchMultilevel) {
561 id = SWITCH_MULTILEVEL_CHANNEL;
562 acceptedItemType = "Dimmer";
563 } else if (device instanceof SwitchRGBW) {
564 id = SWITCH_COLOR_CHANNEL;
565 acceptedItemType = "Color";
566 } else if (device instanceof Thermostat) {
567 id = THERMOSTAT_SET_POINT_CHANNEL;
568 acceptedItemType = "Number";
569 } else if (device instanceof SwitchControl) {
570 id = SWITCH_CONTROL_CHANNEL;
571 acceptedItemType = "Switch";
572 } else if (device instanceof ToggleButton || device instanceof SwitchToggle) {
573 id = SWITCH_CONTROL_CHANNEL;
574 acceptedItemType = "Switch";
575 } else if (device instanceof SensorDiscrete) {
576 id = SENSOR_DISCRETE_CHANNEL;
577 acceptedItemType = "Number";
580 // 2. Check if device information includes further information about sensor type
581 if (!device.getProbeType().equals("")) {
582 if (device instanceof SensorMultilevel) {
583 switch (device.getProbeType()) {
584 case PROBE_TYPE_TEMPERATURE:
585 id = SENSOR_TEMPERATURE_CHANNEL;
586 acceptedItemType = "Number";
588 case PROBE_TYPE_LUMINOSITY:
589 id = SENSOR_LUMINOSITY_CHANNEL;
590 acceptedItemType = "Number";
592 case PROBE_TYPE_HUMIDITY:
593 id = SENSOR_HUMIDITY_CHANNEL;
594 acceptedItemType = "Number";
596 case PROBE_TYPE_BAROMETER:
597 id = SENSOR_BAROMETER_CHANNEL;
598 acceptedItemType = "Number";
600 case PROBE_TYPE_ULTRAVIOLET:
601 id = SENSOR_ULTRAVIOLET_CHANNEL;
602 acceptedItemType = "Number";
604 case PROBE_TYPE_ENERGY:
605 id = SENSOR_ENERGY_CHANNEL;
606 acceptedItemType = "Number";
608 case PROBE_TYPE_METER_ELECTRIC_KILOWATT_PER_HOUR:
609 id = SENSOR_METER_KWH_CHANNEL;
610 acceptedItemType = "Number";
612 case PROBE_TYPE_METER_ELECTRIC_WATT:
613 id = SENSOR_METER_W_CHANNEL;
614 acceptedItemType = "Number";
619 } else if (device instanceof SensorBinary) {
620 switch (device.getProbeType()) {
621 case PROBE_TYPE_GENERAL_PURPOSE:
622 if (device.getMetrics().getIcon().equals(ICON_MOTION)) {
623 id = SENSOR_MOTION_CHANNEL;
624 acceptedItemType = "Switch";
627 case PROBE_TYPE_SMOKE:
628 id = SENSOR_SMOKE_CHANNEL;
629 acceptedItemType = "Switch";
632 id = SENSOR_CO_CHANNEL;
633 acceptedItemType = "Switch";
635 case PROBE_TYPE_FLOOD:
636 id = SENSOR_FLOOD_CHANNEL;
637 acceptedItemType = "Switch";
639 case PROBE_TYPE_COOLING:
642 case PROBE_TYPE_TAMPER:
643 id = SENSOR_TAMPER_CHANNEL;
644 acceptedItemType = "Switch";
646 case PROBE_TYPE_DOOR_WINDOW:
647 id = SENSOR_DOOR_WINDOW_CHANNEL;
648 acceptedItemType = "Contact";
650 case PROBE_TYPE_MOTION:
651 id = SENSOR_MOTION_CHANNEL;
652 acceptedItemType = "Switch";
657 } else if (device instanceof SwitchMultilevel) {
658 switch (device.getProbeType()) {
659 case PROBE_TYPE_SWITCH_COLOR_COLD_WHITE:
660 id = SWITCH_COLOR_TEMPERATURE_CHANNEL;
661 acceptedItemType = "Dimmer";
663 case PROBE_TYPE_SWITCH_COLOR_SOFT_WHITE:
664 id = SWITCH_COLOR_TEMPERATURE_CHANNEL;
665 acceptedItemType = "Dimmer";
667 case PROBE_TYPE_MOTOR:
668 id = SWITCH_ROLLERSHUTTER_CHANNEL;
669 acceptedItemType = "Rollershutter";
673 } else if (device instanceof SwitchBinary) {
674 switch (device.getProbeType()) {
675 case PROBE_TYPE_THERMOSTAT_MODE:
676 id = THERMOSTAT_MODE_CHANNEL;
677 acceptedItemType = "Switch";
683 } else if (!device.getMetrics().getProbeTitle().equals("")) {
684 if (device instanceof SensorMultilevel) {
685 switch (device.getMetrics().getProbeTitle()) {
686 case PROBE_TITLE_CO2_LEVEL:
687 id = SENSOR_CO2_CHANNEL;
688 acceptedItemType = "Number";
694 } else if (!device.getMetrics().getIcon().equals("")) {
695 if (device instanceof SwitchBinary) {
696 switch (device.getMetrics().getIcon()) {
698 id = SWITCH_POWER_OUTLET_CHANNEL;
699 acceptedItemType = "Switch";
706 // Eventually take account of the command classes
709 // If at least one rule could mapped to a channel
710 if (!id.equals("")) {
711 addChannel(id, acceptedItemType, device.getMetrics().getTitle(), properties);
714 "Channel for virtual device added with channel id: {}, accepted item type: {} and title: {}",
715 id, acceptedItemType, device.getMetrics().getTitle());
717 // Thing status will not be updated because thing could have more than one channel
718 logger.warn("No channel for virtual device added: {}", device);
723 private synchronized void addChannel(String id, String acceptedItemType, String label,
724 HashMap<String, String> properties) {
725 boolean channelExists = false;
727 // Check if a channel for this virtual device exist. Attention: same channel type could multiple assigned to a
728 // thing. That's why not check the existence of channel type.
729 List<Channel> channels = getThing().getChannels();
730 for (Channel channel : channels) {
731 if (channel.getProperties().get("deviceId") != null
732 && channel.getProperties().get("deviceId").equals(properties.get("deviceId"))) {
733 channelExists = true;
737 if (!channelExists) {
738 ThingBuilder thingBuilder = editThing();
739 ChannelTypeUID channelTypeUID = new ChannelTypeUID(BINDING_ID, id);
740 Channel channel = ChannelBuilder
741 .create(new ChannelUID(getThing().getUID(), id + "-" + properties.get("deviceId")),
743 .withType(channelTypeUID).withLabel(label).withProperties(properties).build();
744 thingBuilder.withChannel(channel);
745 thingBuilder.withLabel(thing.getLabel());
746 updateThing(thingBuilder.build());
750 protected synchronized void addCommandClassThermostatModeAsChannel(Map<Integer, String> modes, Integer nodeId) {
751 logger.debug("Add command class thermostat mode as channel");
753 ChannelUID channelUID = new ChannelUID(getThing().getUID(), THERMOSTAT_MODE_CC_CHANNEL);
755 boolean channelExists = false;
757 // Check if a channel for this virtual device exist. Attention: same channel type could multiple assigned to a
758 // thing. That's why not check the existence of channel type.
759 List<Channel> channels = getThing().getChannels();
760 for (Channel channel : channels) {
761 if (channel.getUID().equals(channelUID)) {
762 channelExists = true;
766 if (!channelExists) {
767 // Prepare properties (convert modes map)
768 HashMap<String, String> properties = new HashMap<>();
770 // Add node id (for refresh and command handling)
771 properties.put("nodeId", nodeId.toString());
774 ThingBuilder thingBuilder = editThing();
776 Channel channel = ChannelBuilder.create(channelUID, "Number")
777 .withType(new ChannelTypeUID(BINDING_ID, THERMOSTAT_MODE_CC_CHANNEL))
778 .withLabel("Thermostat mode (Command Class)").withDescription("Possible modes: " + modes.toString())
779 .withProperties(properties).build();
780 thingBuilder.withChannel(channel);
781 thingBuilder.withLabel(thing.getLabel());
782 updateThing(thingBuilder.build());