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.digitalstrom.internal.handler;
15 import static org.openhab.binding.digitalstrom.internal.DigitalSTROMBindingConstants.*;
17 import java.math.BigDecimal;
18 import java.math.RoundingMode;
19 import java.util.ArrayList;
20 import java.util.HashSet;
21 import java.util.Iterator;
22 import java.util.LinkedList;
23 import java.util.List;
27 import org.openhab.binding.digitalstrom.internal.DigitalSTROMBindingConstants;
28 import org.openhab.binding.digitalstrom.internal.lib.GeneralLibConstance;
29 import org.openhab.binding.digitalstrom.internal.lib.config.Config;
30 import org.openhab.binding.digitalstrom.internal.lib.listener.DeviceStatusListener;
31 import org.openhab.binding.digitalstrom.internal.lib.structure.devices.Device;
32 import org.openhab.binding.digitalstrom.internal.lib.structure.devices.GeneralDeviceInformation;
33 import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.DeviceSceneSpec;
34 import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.DeviceStateUpdate;
35 import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.constants.ChangeableDeviceConfigEnum;
36 import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.constants.DeviceBinarayInputEnum;
37 import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.constants.OutputModeEnum;
38 import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.constants.SensorEnum;
39 import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.impl.DeviceBinaryInput;
40 import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.impl.DeviceStateUpdateImpl;
41 import org.openhab.binding.digitalstrom.internal.providers.DsChannelTypeProvider;
42 import org.openhab.core.config.core.Configuration;
43 import org.openhab.core.library.types.DecimalType;
44 import org.openhab.core.library.types.IncreaseDecreaseType;
45 import org.openhab.core.library.types.OnOffType;
46 import org.openhab.core.library.types.PercentType;
47 import org.openhab.core.library.types.StopMoveType;
48 import org.openhab.core.library.types.StringType;
49 import org.openhab.core.library.types.UpDownType;
50 import org.openhab.core.thing.Bridge;
51 import org.openhab.core.thing.Channel;
52 import org.openhab.core.thing.ChannelUID;
53 import org.openhab.core.thing.Thing;
54 import org.openhab.core.thing.ThingStatus;
55 import org.openhab.core.thing.ThingStatusDetail;
56 import org.openhab.core.thing.ThingStatusInfo;
57 import org.openhab.core.thing.ThingTypeUID;
58 import org.openhab.core.thing.binding.BaseThingHandler;
59 import org.openhab.core.thing.binding.ThingHandler;
60 import org.openhab.core.thing.binding.builder.ChannelBuilder;
61 import org.openhab.core.thing.binding.builder.ThingBuilder;
62 import org.openhab.core.thing.type.ChannelTypeUID;
63 import org.openhab.core.types.Command;
64 import org.openhab.core.types.RefreshType;
65 import org.slf4j.Logger;
66 import org.slf4j.LoggerFactory;
69 * The {@link DeviceHandler} is responsible for handling the configuration, load supported channels of a
70 * digitalSTROM device and handling commands, which are sent to one of the channels. <br>
72 * For that it uses the {@link BridgeHandler} and the {@link DeviceStateUpdate} mechanism of the {@link Device} to
73 * execute the actual command and implements the {@link DeviceStatusListener} to get informed about changes from the
74 * accompanying {@link Device}.
76 * @author Michael Ochel - Initial contribution
77 * @author Matthias Siegele - Initial contribution
79 public class DeviceHandler extends BaseThingHandler implements DeviceStatusListener {
81 private final Logger logger = LoggerFactory.getLogger(DeviceHandler.class);
84 * Contains all supported thing types of this handler, will be filled by DsDeviceThingTypeProvider.
86 public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = new HashSet<>();
88 public static final String TWO_STAGE_SWITCH_IDENTICATOR = "2";
89 public static final String THREE_STAGE_SWITCH_IDENTICATOR = "3";
92 private Device device;
93 private BridgeHandler dssBridgeHandler;
95 private Command lastComand;
96 private String currentChannel;
97 private List<String> loadedSensorChannels;
100 * Creates a new {@link DeviceHandler}.
102 * @param thing must not be null
104 public DeviceHandler(Thing thing) {
109 public void initialize() {
110 logger.debug("Initializing DeviceHandler.");
111 dSID = (String) getConfig().get(DigitalSTROMBindingConstants.DEVICE_DSID);
112 if (dSID != null && !dSID.isBlank()) {
113 final Bridge bridge = getBridge();
114 if (bridge != null) {
115 bridgeStatusChanged(bridge.getStatusInfo());
117 // Set status to OFFLINE if no bridge is available e.g. because the bridge has been removed and the
118 // Thing was reinitialized.
119 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.NONE, "Bridge is missing!");
122 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "dSID is missing");
127 public void dispose() {
128 logger.debug("Handler disposed... unregister DeviceStatusListener");
130 if (dssBridgeHandler != null) {
131 dssBridgeHandler.unregisterDeviceStatusListener(this);
134 if (device != null) {
135 device.setSensorDataRefreshPriority(Config.REFRESH_PRIORITY_NEVER, Config.REFRESH_PRIORITY_NEVER,
136 Config.REFRESH_PRIORITY_NEVER);
142 public void handleRemoval() {
143 if (getDssBridgeHandler() != null) {
144 this.dssBridgeHandler.childThingRemoved(dSID);
146 updateStatus(ThingStatus.REMOVED);
150 public void thingUpdated(Thing thing) {
152 if (device == null) {
158 public void bridgeStatusChanged(ThingStatusInfo bridgeStatusInfo) {
159 if (bridgeStatusInfo.getStatus().equals(ThingStatus.ONLINE)) {
161 if (getDssBridgeHandler() != null) {
162 if (device == null) {
163 updateStatus(ThingStatus.ONLINE, ThingStatusDetail.CONFIGURATION_PENDING,
164 "waiting for listener registration");
165 dssBridgeHandler.registerDeviceStatusListener(this);
167 updateStatus(ThingStatus.ONLINE);
170 updateStatus(ThingStatus.OFFLINE);
173 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "No dSID is set!");
176 if (bridgeStatusInfo.getStatus().equals(ThingStatus.OFFLINE)) {
177 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE);
179 logger.debug("Set status to {}", getThing().getStatusInfo());
183 public void handleCommand(ChannelUID channelUID, Command command) {
184 BridgeHandler dssBridgeHandler = getDssBridgeHandler();
185 if (dssBridgeHandler == null) {
186 logger.debug("BridgeHandler not found. Cannot handle command without bridge.");
190 if (device == null) {
192 "Device not known on StructureManager or DeviceStatusListener is not registerd. Cannot handle command.");
196 if (command instanceof RefreshType) {
198 SensorEnum sensorType = SensorEnum.valueOf(channelUID.getId());
199 dssBridgeHandler.sendComandsToDSS(device, new DeviceStateUpdateImpl(sensorType, 1));
200 } catch (IllegalArgumentException e) {
201 dssBridgeHandler.sendComandsToDSS(device,
202 new DeviceStateUpdateImpl(DeviceStateUpdate.REFRESH_OUTPUT, 0));
204 } else if (!device.isShade()) {
205 if (DsChannelTypeProvider.isOutputChannel(channelUID.getId())) {
206 if (command instanceof PercentType) {
207 device.setOutputValue(
208 (short) fromPercentToValue(((PercentType) command).intValue(), device.getMaxOutputValue()));
209 } else if (command instanceof OnOffType) {
210 if (OnOffType.ON.equals(command)) {
211 device.setIsOn(true);
213 device.setIsOn(false);
215 } else if (command instanceof IncreaseDecreaseType) {
216 if (IncreaseDecreaseType.INCREASE.equals(command)) {
221 } else if (command instanceof StringType) {
222 device.setOutputValue(Short.parseShort(((StringType) command).toString()));
225 logger.debug("Command sent to an unknown channel id: {}", channelUID);
228 if (channelUID.getId().contains(DsChannelTypeProvider.ANGLE)) {
229 if (command instanceof PercentType) {
230 device.setAnglePosition(
231 (short) fromPercentToValue(((PercentType) command).intValue(), device.getMaxSlatAngle()));
232 } else if (command instanceof OnOffType) {
233 if (OnOffType.ON.equals(command)) {
234 device.setAnglePosition(device.getMaxSlatAngle());
236 device.setAnglePosition(device.getMinSlatAngle());
238 } else if (command instanceof IncreaseDecreaseType) {
239 if (IncreaseDecreaseType.INCREASE.equals(command)) {
240 device.increaseSlatAngle();
242 device.decreaseSlatAngle();
245 } else if (channelUID.getId().contains(DsChannelTypeProvider.SHADE)) {
246 if (command instanceof PercentType) {
247 int percent = ((PercentType) command).intValue();
248 if (!device.getHWinfo().equals("GR-KL200")) {
249 percent = 100 - percent;
251 device.setSlatPosition(fromPercentToValue(percent, device.getMaxSlatPosition()));
252 this.lastComand = command;
253 } else if (command instanceof StopMoveType) {
254 if (StopMoveType.MOVE.equals(command)) {
255 handleCommand(channelUID, this.lastComand);
257 dssBridgeHandler.stopOutputValue(device);
259 } else if (command instanceof UpDownType) {
260 if (UpDownType.UP.equals(command)) {
261 device.setIsOpen(true);
262 this.lastComand = command;
264 device.setIsOpen(false);
265 this.lastComand = command;
269 logger.debug("Command sent to an unknown channel id: {}", channelUID);
274 private int fromPercentToValue(int percent, int max) {
275 if (percent < 0 || percent == 0) {
278 if (max < 0 || max == 0) {
281 return (int) (max * ((float) percent / 100));
284 private synchronized BridgeHandler getDssBridgeHandler() {
285 if (this.dssBridgeHandler == null) {
286 Bridge bridge = getBridge();
287 if (bridge == null) {
288 logger.debug("Bride cannot be found");
291 ThingHandler handler = bridge.getHandler();
293 if (handler instanceof BridgeHandler) {
294 dssBridgeHandler = (BridgeHandler) handler;
299 return dssBridgeHandler;
302 private boolean sensorChannelsLoaded() {
303 return loadedSensorChannels != null && !loadedSensorChannels.isEmpty();
307 public synchronized void onDeviceStateChanged(DeviceStateUpdate deviceStateUpdate) {
308 if (device != null) {
309 if (deviceStateUpdate != null) {
310 if (sensorChannelsLoaded()) {
311 if (deviceStateUpdate.isSensorUpdateType()) {
312 updateState(getSensorChannelID(deviceStateUpdate.getTypeAsSensorEnum()),
313 new DecimalType(deviceStateUpdate.getValueAsFloat()));
314 logger.debug("Update state");
317 if (deviceStateUpdate.isBinarayInputType()) {
318 if (deviceStateUpdate.getValueAsShort() == 1) {
319 updateState(getBinaryInputChannelID(deviceStateUpdate.getTypeAsDeviceBinarayInputEnum()),
322 updateState(getBinaryInputChannelID(deviceStateUpdate.getTypeAsDeviceBinarayInputEnum()),
327 if (!device.isShade()) {
328 if (currentChannel != null) {
329 switch (deviceStateUpdate.getType()) {
330 case DeviceStateUpdate.OUTPUT_DECREASE:
331 case DeviceStateUpdate.OUTPUT_INCREASE:
332 case DeviceStateUpdate.OUTPUT:
333 if (currentChannel.contains(DsChannelTypeProvider.DIMMER)) {
334 if (deviceStateUpdate.getValueAsInteger() > 0) {
335 updateState(currentChannel, new PercentType(fromValueToPercent(
336 deviceStateUpdate.getValueAsInteger(), device.getMaxOutputValue())));
338 updateState(currentChannel, OnOffType.OFF);
340 } else if (currentChannel.contains(DsChannelTypeProvider.STAGE)) {
341 if (currentChannel.contains(TWO_STAGE_SWITCH_IDENTICATOR)) {
342 updateState(currentChannel,
343 new StringType(convertStageValue((short) 2, device.getOutputValue())));
345 updateState(currentChannel,
346 new StringType(convertStageValue((short) 3, device.getOutputValue())));
350 case DeviceStateUpdate.ON_OFF:
351 if (currentChannel.contains(DsChannelTypeProvider.STAGE)) {
352 onDeviceStateChanged(new DeviceStateUpdateImpl(DeviceStateUpdate.OUTPUT,
353 device.getOutputValue()));
355 if (deviceStateUpdate.getValueAsInteger() > 0) {
356 updateState(currentChannel, OnOffType.ON);
358 updateState(currentChannel, OnOffType.OFF);
367 switch (deviceStateUpdate.getType()) {
368 case DeviceStateUpdate.SLAT_DECREASE:
369 case DeviceStateUpdate.SLAT_INCREASE:
370 case DeviceStateUpdate.SLATPOSITION:
371 percent = fromValueToPercent(deviceStateUpdate.getValueAsInteger(),
372 device.getMaxSlatPosition());
374 case DeviceStateUpdate.OPEN_CLOSE:
375 if (deviceStateUpdate.getValueAsInteger() > 0) {
379 case DeviceStateUpdate.OPEN_CLOSE_ANGLE:
380 if (device.isBlind() && currentChannel != null) {
381 if (deviceStateUpdate.getValueAsInteger() > 0) {
382 updateState(currentChannel, PercentType.HUNDRED);
384 updateState(currentChannel, PercentType.ZERO);
388 case DeviceStateUpdate.SLAT_ANGLE_DECREASE:
389 case DeviceStateUpdate.SLAT_ANGLE_INCREASE:
390 case DeviceStateUpdate.SLAT_ANGLE:
391 if (device.isBlind() && currentChannel != null) {
392 updateState(currentChannel,
393 new PercentType(fromValueToPercent(deviceStateUpdate.getValueAsInteger(),
394 device.getMaxSlatAngle())));
400 if (!device.getHWinfo().equals("GR-KL210")) {
401 percent = 100 - percent;
403 updateState(DsChannelTypeProvider.SHADE, new PercentType(percent));
405 logger.debug("Update state");
410 private int fromValueToPercent(int value, int max) {
411 if (value <= 0 || max <= 0) {
414 int percentValue = new BigDecimal(value * ((float) 100 / max)).setScale(0, RoundingMode.HALF_UP).intValue();
415 return percentValue < 0 ? 0 : percentValue > 100 ? 100 : percentValue;
419 public synchronized void onDeviceRemoved(GeneralDeviceInformation device) {
420 if (device instanceof Device) {
421 this.device = (Device) device;
422 if (this.getThing().getStatus().equals(ThingStatus.ONLINE)) {
423 if (!((Device) device).isPresent()) {
424 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.NONE,
425 "Device is not present in the digitalSTROM-System.");
427 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.NONE,
428 "Device is not avaible in the digitalSTROM-System.");
432 logger.debug("Set status to {}", getThing().getStatus());
437 public synchronized void onDeviceAdded(GeneralDeviceInformation device) {
438 if (device instanceof Device) {
439 this.device = (Device) device;
440 if (this.device.isPresent()) {
441 ThingStatusInfo statusInfo = this.dssBridgeHandler.getThing().getStatusInfo();
442 updateStatus(statusInfo.getStatus(), statusInfo.getStatusDetail(), statusInfo.getDescription());
443 logger.debug("Set status to {}", getThing().getStatus());
445 // load scene configurations persistently into the thing
446 for (Short i : this.device.getSavedScenes()) {
447 onSceneConfigAdded(i);
449 logger.debug("Load saved scene specification into device");
450 this.device.saveConfigSceneSpecificationIntoDevice(getThing().getProperties());
452 checkDeviceInfoProperties(this.device);
453 // load sensor priorities into the device and load sensor channels of the thing
454 if (!this.device.isShade()) {
455 loadSensorChannels();
456 // check and load output channel of the thing
457 checkOutputChannel();
458 } else if (this.device.isBlind()) {
459 // load channel for set the angle of jalousie devices
460 String channelTypeID = DsChannelTypeProvider.getOutputChannelTypeID(
461 ((Device) device).getFunctionalColorGroup().getColor(), ((Device) device).getOutputMode(),
462 ((Device) device).getOutputChannels());
463 loadOutputChannel(new ChannelTypeUID(BINDING_ID, channelTypeID),
464 DsChannelTypeProvider.getItemType(channelTypeID));
467 // load first channel values
468 onDeviceStateInitial(this.device);
472 onDeviceRemoved(device);
476 * Updates device info properties.
478 * @param device (must not be null)
480 private void checkDeviceInfoProperties(Device device) {
481 boolean propertiesChanged = false;
482 Map<String, String> properties = editProperties();
484 if (device.getName() != null) {
485 properties.put(DigitalSTROMBindingConstants.DEVICE_NAME, device.getName());
486 propertiesChanged = true;
488 if (device.getDSUID() != null) {
489 properties.put(DigitalSTROMBindingConstants.DEVICE_UID, device.getDSUID());
490 propertiesChanged = true;
492 if (device.getHWinfo() != null) {
493 properties.put(DigitalSTROMBindingConstants.DEVICE_HW_INFO, device.getHWinfo());
494 propertiesChanged = true;
496 if (device.getZoneId() != -1) {
497 properties.put(DigitalSTROMBindingConstants.DEVICE_ZONE_ID, device.getZoneId() + "");
498 propertiesChanged = true;
500 if (device.getGroups() != null) {
501 properties.put(DigitalSTROMBindingConstants.DEVICE_GROUPS, device.getGroups().toString());
502 propertiesChanged = true;
504 if (device.getOutputMode() != null) {
505 properties.put(DigitalSTROMBindingConstants.DEVICE_OUTPUT_MODE, device.getOutputMode().toString());
506 propertiesChanged = true;
508 if (device.getFunctionalColorGroup() != null) {
509 properties.put(DigitalSTROMBindingConstants.DEVICE_FUNCTIONAL_COLOR_GROUP,
510 device.getFunctionalColorGroup().toString());
511 propertiesChanged = true;
513 if (device.getMeterDSID() != null) {
514 properties.put(DigitalSTROMBindingConstants.DEVICE_METER_ID, device.getMeterDSID().toString());
515 propertiesChanged = true;
517 if (!device.getBinaryInputs().isEmpty()) {
518 properties.put(DigitalSTROMBindingConstants.DEVICE_BINARAY_INPUTS, getBinarayInputList());
519 propertiesChanged = true;
521 if (propertiesChanged) {
522 super.updateProperties(properties);
523 propertiesChanged = false;
527 private String getBinarayInputList() {
528 List<String> binarayInputs = new ArrayList<>(device.getBinaryInputs().size());
529 for (DeviceBinaryInput binInput : device.getBinaryInputs()) {
530 DeviceBinarayInputEnum devBinInp = DeviceBinarayInputEnum.getdeviceBinarayInput(binInput.getInputType());
531 if (devBinInp != null) {
532 binarayInputs.add(devBinInp.toString().toLowerCase());
535 return binarayInputs.toString();
538 private void loadSensorChannels() {
539 if (device != null && device.isPresent()) {
540 // load sensor priorities into the device
541 boolean configChanged = false;
542 Configuration config = getThing().getConfiguration();
543 logger.debug("Add sensor priorities to the device");
545 String activePowerPrio = Config.REFRESH_PRIORITY_NEVER;
546 if (config.get(DigitalSTROMBindingConstants.ACTIVE_POWER_REFRESH_PRIORITY) != null) {
547 activePowerPrio = config.get(DigitalSTROMBindingConstants.ACTIVE_POWER_REFRESH_PRIORITY).toString();
549 config.put(DigitalSTROMBindingConstants.ACTIVE_POWER_REFRESH_PRIORITY, Config.REFRESH_PRIORITY_NEVER);
550 configChanged = true;
552 // By devices with output mode WIPE the active power always will be read out to check, if the device is not
553 // in standby any more.
554 if (OutputModeEnum.WIPE.equals(device.getOutputMode())
555 && activePowerPrio.equals(Config.REFRESH_PRIORITY_NEVER)) {
556 config.put(DigitalSTROMBindingConstants.ACTIVE_POWER_REFRESH_PRIORITY, Config.REFRESH_PRIORITY_LOW);
557 configChanged = true;
560 String outputCurrentPrio = Config.REFRESH_PRIORITY_NEVER;
561 if (config.get(DigitalSTROMBindingConstants.OUTPUT_CURRENT_REFRESH_PRIORITY) != null) {
562 outputCurrentPrio = config.get(DigitalSTROMBindingConstants.OUTPUT_CURRENT_REFRESH_PRIORITY).toString();
564 config.put(DigitalSTROMBindingConstants.OUTPUT_CURRENT_REFRESH_PRIORITY, Config.REFRESH_PRIORITY_NEVER);
565 configChanged = true;
568 String electricMeterPrio = Config.REFRESH_PRIORITY_NEVER;
569 if (config.get(DigitalSTROMBindingConstants.ELECTRIC_METER_REFRESH_PRIORITY) != null) {
570 electricMeterPrio = config.get(DigitalSTROMBindingConstants.ELECTRIC_METER_REFRESH_PRIORITY).toString();
572 config.put(DigitalSTROMBindingConstants.ELECTRIC_METER_REFRESH_PRIORITY, Config.REFRESH_PRIORITY_NEVER);
573 configChanged = true;
577 super.updateConfiguration(config);
578 configChanged = false;
581 device.setSensorDataRefreshPriority(activePowerPrio, electricMeterPrio, outputCurrentPrio);
583 "add sensor prioritys: active power = {}, output current = {}, electric meter = {} to device with id {}",
584 activePowerPrio, outputCurrentPrio, electricMeterPrio, device.getDSID());
586 // check and load sensor channels of the thing
587 checkSensorChannel();
591 private boolean addLoadedSensorChannel(String sensorChannelType) {
592 if (loadedSensorChannels == null) {
593 loadedSensorChannels = new LinkedList<>();
595 if (!loadedSensorChannels.contains(sensorChannelType.toString())) {
596 return loadedSensorChannels.add(sensorChannelType.toString());
601 private boolean removeLoadedSensorChannel(String sensorChannelType) {
602 if (loadedSensorChannels == null) {
605 return loadedSensorChannels.remove(sensorChannelType);
608 private boolean isSensorChannelLoaded(String sensorChannelType) {
609 if (loadedSensorChannels == null) {
612 return loadedSensorChannels.contains(sensorChannelType);
615 private void checkSensorChannel() {
616 List<Channel> channelList = new LinkedList<>(this.getThing().getChannels());
618 boolean channelListChanged = false;
620 // if sensor channels with priority never are loaded delete these channels
621 if (!channelList.isEmpty()) {
622 Iterator<Channel> channelInter = channelList.iterator();
623 while (channelInter.hasNext()) {
624 Channel channel = channelInter.next();
625 String channelID = channel.getUID().getId();
626 if (channelID.startsWith(DsChannelTypeProvider.BINARY_INPUT_PRE)) {
627 DeviceBinarayInputEnum devBinInput = getBinaryInput(channelID);
628 if (device.getBinaryInput(devBinInput) != null) {
629 addLoadedSensorChannel(channelID);
631 logger.debug("remove {} binary input channel", channelID);
632 channelInter.remove();
633 channelListChanged = removeLoadedSensorChannel(channelID);
636 SensorEnum sensorType = getSensorEnum(channelID);
637 if (sensorType != null) {
638 if (SensorEnum.isPowerSensor(sensorType)) {
639 if (device.checkPowerSensorRefreshPriorityNever(sensorType)) {
640 logger.debug("remove {} sensor channel", channelID);
641 channelInter.remove();
642 channelListChanged = removeLoadedSensorChannel(channelID);
644 addLoadedSensorChannel(channelID);
647 if (device.supportsSensorType(sensorType)) {
648 addLoadedSensorChannel(channelID);
650 logger.debug("remove {} sensor channel", channelID);
651 channelInter.remove();
652 removeLoadedSensorChannel(channelID);
653 channelListChanged = true;
660 for (SensorEnum sensorType : device.getPowerSensorTypes()) {
661 if (!device.checkPowerSensorRefreshPriorityNever(sensorType)
662 && !isSensorChannelLoaded(getSensorChannelID(sensorType))) {
663 logger.debug("create {} sensor channel", sensorType.toString());
664 channelList.add(getSensorChannel(sensorType));
665 channelListChanged = addLoadedSensorChannel(getSensorChannelID(sensorType));
668 if (device.hasClimateSensors()) {
669 for (SensorEnum sensorType : device.getClimateSensorTypes()) {
670 if (!isSensorChannelLoaded(getSensorChannelID(sensorType))) {
671 logger.debug("create {} sensor channel", sensorType.toString());
672 channelList.add(getSensorChannel(sensorType));
673 channelListChanged = addLoadedSensorChannel(getSensorChannelID(sensorType));
677 if (device.isBinaryInputDevice()) {
678 for (DeviceBinaryInput binInput : device.getBinaryInputs()) {
679 DeviceBinarayInputEnum binInputType = DeviceBinarayInputEnum
680 .getdeviceBinarayInput(binInput.getInputType());
681 if (binInputType != null && !isSensorChannelLoaded(getBinaryInputChannelID(binInputType))) {
682 logger.debug("create {} sensor channel", binInputType.toString());
683 channelList.add(getBinaryChannel(binInputType));
684 channelListChanged = addLoadedSensorChannel(getBinaryInputChannelID(binInputType));
689 if (channelListChanged) {
690 logger.debug("load new channel list");
691 ThingBuilder thingBuilder = editThing();
692 thingBuilder.withChannels(channelList);
693 updateThing(thingBuilder.build());
697 private Channel getSensorChannel(SensorEnum sensorType) {
698 return ChannelBuilder.create(getSensorChannelUID(sensorType), "Number")
699 .withType(DsChannelTypeProvider.getSensorChannelUID(sensorType)).build();
702 private Channel getBinaryChannel(DeviceBinarayInputEnum binaryInputType) {
703 return ChannelBuilder.create(getBinaryInputChannelUID(binaryInputType), "Switch")
704 .withType(DsChannelTypeProvider.getBinaryInputChannelUID(binaryInputType)).build();
707 private void checkOutputChannel() {
708 if (device == null) {
709 logger.debug("Can not load a channel without a device!");
712 // if the device have no output channel or it is disabled all output channels will be deleted
713 if (!device.isDeviceWithOutput()) {
714 loadOutputChannel(null, null);
716 String channelTypeID = DsChannelTypeProvider.getOutputChannelTypeID(device.getFunctionalColorGroup().getColor(),
717 device.getOutputMode(), device.getOutputChannels());
718 logger.debug("load channel: typeID={}, itemType={}",
719 DsChannelTypeProvider.getOutputChannelTypeID(device.getFunctionalColorGroup().getColor(),
720 device.getOutputMode(), device.getOutputChannels()),
721 DsChannelTypeProvider.getItemType(channelTypeID));
722 if (channelTypeID != null && (currentChannel == null || !currentChannel.equals(channelTypeID))) {
723 loadOutputChannel(new ChannelTypeUID(BINDING_ID, channelTypeID),
724 DsChannelTypeProvider.getItemType(channelTypeID));
728 private void loadOutputChannel(ChannelTypeUID channelTypeUID, String acceptedItemType) {
729 if (channelTypeUID == null || acceptedItemType == null) {
732 currentChannel = channelTypeUID.getId();
734 List<Channel> channelList = new LinkedList<>(this.getThing().getChannels());
735 boolean channelIsAlreadyLoaded = false;
736 boolean channelListChanged = false;
738 if (!channelList.isEmpty()) {
739 Iterator<Channel> channelInter = channelList.iterator();
740 while (channelInter.hasNext()) {
741 Channel channel = channelInter.next();
742 if (DsChannelTypeProvider.isOutputChannel(channel.getUID().getId())) {
743 if (!channel.getUID().getId().equals(currentChannel)
744 && !(device.isShade() && channel.getUID().getId().equals(DsChannelTypeProvider.SHADE))) {
745 channelInter.remove();
746 channelListChanged = true;
748 if (!channel.getUID().getId().equals(DsChannelTypeProvider.SHADE)) {
749 channelIsAlreadyLoaded = true;
756 if (!channelIsAlreadyLoaded && currentChannel != null) {
757 Channel channel = ChannelBuilder
758 .create(new ChannelUID(this.getThing().getUID(), channelTypeUID.getId()), acceptedItemType)
759 .withType(channelTypeUID).build();
760 channelList.add(channel);
761 channelListChanged = true;
764 if (channelListChanged) {
765 ThingBuilder thingBuilder = editThing();
766 thingBuilder.withChannels(channelList);
767 updateThing(thingBuilder.build());
768 logger.debug("load channel: {} with item: {}", channelTypeUID.getAsString(), acceptedItemType);
772 private ChannelUID getSensorChannelUID(SensorEnum sensorType) {
773 return new ChannelUID(getThing().getUID(), getSensorChannelID(sensorType));
776 private String getSensorChannelID(SensorEnum sensorType) {
777 return sensorType.toString().toLowerCase();
780 private ChannelUID getBinaryInputChannelUID(DeviceBinarayInputEnum binaryInputType) {
781 return new ChannelUID(getThing().getUID(), getBinaryInputChannelID(binaryInputType));
784 private String getBinaryInputChannelID(DeviceBinarayInputEnum binaryInputType) {
785 return DsChannelTypeProvider.BINARY_INPUT_PRE + binaryInputType.toString().toLowerCase();
788 private DeviceBinarayInputEnum getBinaryInput(String channelID) {
790 return DeviceBinarayInputEnum
791 .valueOf(channelID.replace(DsChannelTypeProvider.BINARY_INPUT_PRE, "").toUpperCase());
792 } catch (IllegalArgumentException e) {
797 private SensorEnum getSensorEnum(String channelID) {
799 return SensorEnum.valueOf(channelID.toUpperCase());
800 } catch (IllegalArgumentException e) {
806 public void channelLinked(ChannelUID channelUID) {
807 if (device != null) {
808 SensorEnum sensorType = getSensorEnum(channelUID.getId());
809 if (sensorType != null) {
810 Float val = device.getFloatSensorValue(sensorType);
812 updateState(channelUID, new DecimalType(val));
815 Short val = device.getBinaryInputState(getBinaryInput(channelUID.getId()));
818 updateState(channelUID, OnOffType.ON);
820 updateState(channelUID, OnOffType.OFF);
824 if (channelUID.getId().contains(DsChannelTypeProvider.DIMMER)) {
826 updateState(channelUID,
827 new PercentType(fromValueToPercent(device.getOutputValue(), device.getMaxOutputValue())));
829 updateState(channelUID, new PercentType(0));
833 if (channelUID.getId().contains(DsChannelTypeProvider.SWITCH)) {
835 updateState(channelUID, OnOffType.ON);
837 updateState(channelUID, OnOffType.OFF);
841 if (channelUID.getId().contains(DsChannelTypeProvider.SHADE)) {
842 updateState(channelUID,
843 new PercentType(fromValueToPercent(device.getSlatPosition(), device.getMaxSlatPosition())));
846 if (channelUID.getId().contains(DsChannelTypeProvider.ANGLE)) {
847 updateState(channelUID,
848 new PercentType(fromValueToPercent(device.getAnglePosition(), device.getMaxSlatAngle())));
851 if (channelUID.getId().contains(DsChannelTypeProvider.STAGE)) {
852 if (channelUID.getId().contains(TWO_STAGE_SWITCH_IDENTICATOR)) {
853 updateState(channelUID, new StringType(convertStageValue((short) 2, device.getOutputValue())));
856 if (channelUID.getId().contains(THREE_STAGE_SWITCH_IDENTICATOR)) {
857 updateState(channelUID, new StringType(convertStageValue((short) 3, device.getOutputValue())));
864 private String convertStageValue(short stage, short value) {
868 return OPTION_COMBINED_BOTH_OFF;
869 } else if (value >= 85 && value < 170) {
870 return OPTION_COMBINED_FIRST_ON;
871 } else if (value >= 170 && value <= 255) {
872 return OPTION_COMBINED_BOTH_ON;
876 return OPTION_COMBINED_BOTH_OFF;
877 } else if (value >= 64 && value < 128) {
878 return OPTION_COMBINED_FIRST_ON;
879 } else if (value >= 128 && value < 192) {
880 return OPTION_COMBINED_SECOND_ON;
881 } else if (value >= 192 && value <= 255) {
882 return OPTION_COMBINED_BOTH_ON;
888 private void onDeviceStateInitial(Device device) {
889 if (device != null) {
890 if (currentChannel != null) {
891 if (isLinked(currentChannel)) {
892 channelLinked(new ChannelUID(getThing().getUID(), currentChannel));
895 if (!device.isShade()) {
896 if (loadedSensorChannels != null) {
897 for (String sensor : loadedSensorChannels) {
898 Channel channel = getThing().getChannel(sensor);
899 if (channel != null && isLinked(sensor)) {
900 channelLinked(channel.getUID());
905 if (isLinked(DsChannelTypeProvider.SHADE)) {
906 channelLinked(new ChannelUID(getThing().getUID(), DsChannelTypeProvider.SHADE));
913 public synchronized void onSceneConfigAdded(short sceneId) {
914 if (device != null) {
915 String saveScene = "";
916 DeviceSceneSpec sceneSpec = device.getSceneConfig(sceneId);
917 if (sceneSpec != null) {
918 saveScene = sceneSpec.toString();
921 Integer[] sceneValue = device.getSceneOutputValue(sceneId);
922 if (sceneValue[GeneralLibConstance.SCENE_ARRAY_INDEX_VALUE] != -1) {
923 saveScene = saveScene + ", sceneValue: " + sceneValue[0];
925 if (sceneValue[GeneralLibConstance.SCENE_ARRAY_INDEX_ANGLE] != -1) {
926 saveScene = saveScene + ", sceneAngle: " + sceneValue[1];
928 String key = DigitalSTROMBindingConstants.DEVICE_SCENE + sceneId;
929 if (!saveScene.isEmpty()) {
930 logger.debug("Save scene configuration: [{}] to thing with UID {}", saveScene, getThing().getUID());
931 super.updateProperty(key, saveScene);
932 // persist the new property
933 // super.updateThing(editThing().build());
939 public void onDeviceConfigChanged(ChangeableDeviceConfigEnum whichConfig) {
940 if (whichConfig != null) {
941 switch (whichConfig) {
943 super.updateProperty(DEVICE_NAME, device.getName());
946 super.updateProperty(DEVICE_METER_ID, device.getMeterDSID().getValue());
949 super.updateProperty(DEVICE_ZONE_ID, device.getZoneId() + "");
952 super.updateProperty(DEVICE_GROUPS, device.getGroups().toString());
954 case FUNCTIONAL_GROUP:
955 super.updateProperty(DEVICE_FUNCTIONAL_COLOR_GROUP, device.getFunctionalColorGroup().toString());
956 checkOutputChannel();
959 super.updateProperty(DEVICE_OUTPUT_MODE, device.getOutputMode().toString());
960 checkOutputChannel();
963 super.updateProperty(DEVICE_BINARAY_INPUTS, getBinarayInputList());
964 checkSensorChannel();
973 public String getDeviceStatusListenerID() {