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.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.ApplicationGroup;
36 import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.constants.ChangeableDeviceConfigEnum;
37 import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.constants.DeviceBinarayInputEnum;
38 import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.constants.OutputModeEnum;
39 import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.constants.SensorEnum;
40 import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.impl.DeviceBinaryInput;
41 import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.impl.DeviceStateUpdateImpl;
42 import org.openhab.binding.digitalstrom.internal.providers.DsChannelTypeProvider;
43 import org.openhab.core.config.core.Configuration;
44 import org.openhab.core.library.types.DecimalType;
45 import org.openhab.core.library.types.IncreaseDecreaseType;
46 import org.openhab.core.library.types.OnOffType;
47 import org.openhab.core.library.types.PercentType;
48 import org.openhab.core.library.types.StopMoveType;
49 import org.openhab.core.library.types.StringType;
50 import org.openhab.core.library.types.UpDownType;
51 import org.openhab.core.thing.Bridge;
52 import org.openhab.core.thing.Channel;
53 import org.openhab.core.thing.ChannelUID;
54 import org.openhab.core.thing.Thing;
55 import org.openhab.core.thing.ThingStatus;
56 import org.openhab.core.thing.ThingStatusDetail;
57 import org.openhab.core.thing.ThingStatusInfo;
58 import org.openhab.core.thing.ThingTypeUID;
59 import org.openhab.core.thing.binding.BaseThingHandler;
60 import org.openhab.core.thing.binding.ThingHandler;
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.ChannelTypeUID;
64 import org.openhab.core.types.Command;
65 import org.openhab.core.types.RefreshType;
66 import org.slf4j.Logger;
67 import org.slf4j.LoggerFactory;
70 * The {@link DeviceHandler} is responsible for handling the configuration, load supported channels of a
71 * digitalSTROM device and handling commands, which are sent to one of the channels. <br>
73 * For that it uses the {@link BridgeHandler} and the {@link DeviceStateUpdate} mechanism of the {@link Device} to
74 * execute the actual command and implements the {@link DeviceStatusListener} to get informed about changes from the
75 * accompanying {@link Device}.
77 * @author Michael Ochel - Initial contribution
78 * @author Matthias Siegele - Initial contribution
80 public class DeviceHandler extends BaseThingHandler implements DeviceStatusListener {
82 private final Logger logger = LoggerFactory.getLogger(DeviceHandler.class);
85 * Contains all supported thing types of this handler, will be filled by DsDeviceThingTypeProvider.
87 public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = new HashSet<>();
89 public static final String TWO_STAGE_SWITCH_IDENTICATOR = "2";
90 public static final String THREE_STAGE_SWITCH_IDENTICATOR = "3";
93 private Device device;
94 private BridgeHandler dssBridgeHandler;
96 private Command lastComand;
97 private String currentChannel;
98 private List<String> loadedSensorChannels;
101 * Creates a new {@link DeviceHandler}.
103 * @param thing must not be null
105 public DeviceHandler(Thing thing) {
110 public void initialize() {
111 logger.debug("Initializing DeviceHandler.");
112 dSID = (String) getConfig().get(DigitalSTROMBindingConstants.DEVICE_DSID);
113 if (dSID != null && !dSID.isBlank()) {
114 final Bridge bridge = getBridge();
115 if (bridge != null) {
116 bridgeStatusChanged(bridge.getStatusInfo());
118 // Set status to OFFLINE if no bridge is available e.g. because the bridge has been removed and the
119 // Thing was reinitialized.
120 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.NONE, "Bridge is missing!");
123 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "dSID is missing");
128 public void dispose() {
129 logger.debug("Handler disposed... unregister DeviceStatusListener");
131 if (dssBridgeHandler != null) {
132 dssBridgeHandler.unregisterDeviceStatusListener(this);
135 if (device != null) {
136 device.setSensorDataRefreshPriority(Config.REFRESH_PRIORITY_NEVER, Config.REFRESH_PRIORITY_NEVER,
137 Config.REFRESH_PRIORITY_NEVER);
143 public void handleRemoval() {
144 if (getDssBridgeHandler() != null) {
145 this.dssBridgeHandler.childThingRemoved(dSID);
147 updateStatus(ThingStatus.REMOVED);
151 public void thingUpdated(Thing thing) {
153 if (device == null) {
159 public void bridgeStatusChanged(ThingStatusInfo bridgeStatusInfo) {
160 if (bridgeStatusInfo.getStatus().equals(ThingStatus.ONLINE)) {
162 if (getDssBridgeHandler() != null) {
163 if (device == null) {
164 updateStatus(ThingStatus.ONLINE, ThingStatusDetail.CONFIGURATION_PENDING,
165 "waiting for listener registration");
166 dssBridgeHandler.registerDeviceStatusListener(this);
168 updateStatus(ThingStatus.ONLINE);
171 updateStatus(ThingStatus.OFFLINE);
174 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "No dSID is set!");
177 if (bridgeStatusInfo.getStatus().equals(ThingStatus.OFFLINE)) {
178 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE);
180 logger.debug("Set status to {}", getThing().getStatusInfo());
184 public void handleCommand(ChannelUID channelUID, Command command) {
185 BridgeHandler dssBridgeHandler = getDssBridgeHandler();
186 if (dssBridgeHandler == null) {
187 logger.debug("BridgeHandler not found. Cannot handle command without bridge.");
191 if (device == null) {
193 "Device not known on StructureManager or DeviceStatusListener is not registerd. Cannot handle command.");
197 if (command instanceof RefreshType) {
199 SensorEnum sensorType = SensorEnum.valueOf(channelUID.getId());
200 dssBridgeHandler.sendComandsToDSS(device, new DeviceStateUpdateImpl(sensorType, 1));
201 } catch (IllegalArgumentException e) {
202 dssBridgeHandler.sendComandsToDSS(device,
203 new DeviceStateUpdateImpl(DeviceStateUpdate.REFRESH_OUTPUT, 0));
205 } else if (!device.isShade()) {
206 if (DsChannelTypeProvider.isOutputChannel(channelUID.getId())) {
207 if (command instanceof PercentType) {
208 device.setOutputValue(
209 (short) fromPercentToValue(((PercentType) command).intValue(), device.getMaxOutputValue()));
210 } else if (command instanceof OnOffType) {
211 if (OnOffType.ON.equals(command)) {
212 device.setIsOn(true);
214 device.setIsOn(false);
216 } else if (command instanceof IncreaseDecreaseType) {
217 if (IncreaseDecreaseType.INCREASE.equals(command)) {
222 } else if (command instanceof StringType) {
223 device.setOutputValue(Short.parseShort(((StringType) command).toString()));
226 logger.debug("Command sent to an unknown channel id: {}", channelUID);
229 if (channelUID.getId().contains(DsChannelTypeProvider.ANGLE)) {
230 if (command instanceof PercentType) {
231 device.setAnglePosition(
232 (short) fromPercentToValue(((PercentType) command).intValue(), device.getMaxSlatAngle()));
233 } else if (command instanceof OnOffType) {
234 if (OnOffType.ON.equals(command)) {
235 device.setAnglePosition(device.getMaxSlatAngle());
237 device.setAnglePosition(device.getMinSlatAngle());
239 } else if (command instanceof IncreaseDecreaseType) {
240 if (IncreaseDecreaseType.INCREASE.equals(command)) {
241 device.increaseSlatAngle();
243 device.decreaseSlatAngle();
246 } else if (channelUID.getId().contains(DsChannelTypeProvider.SHADE)) {
247 if (command instanceof PercentType) {
248 int percent = ((PercentType) command).intValue();
249 if (!device.getHWinfo().equals("GR-KL200")) {
250 percent = 100 - percent;
252 device.setSlatPosition(fromPercentToValue(percent, device.getMaxSlatPosition()));
253 this.lastComand = command;
254 } else if (command instanceof StopMoveType) {
255 if (StopMoveType.MOVE.equals(command)) {
256 handleCommand(channelUID, this.lastComand);
258 dssBridgeHandler.stopOutputValue(device);
260 } else if (command instanceof UpDownType) {
261 if (UpDownType.UP.equals(command)) {
262 device.setIsOpen(true);
263 this.lastComand = command;
265 device.setIsOpen(false);
266 this.lastComand = command;
270 logger.debug("Command sent to an unknown channel id: {}", channelUID);
275 private int fromPercentToValue(int percent, int max) {
276 if (percent < 0 || percent == 0) {
279 if (max < 0 || max == 0) {
282 return (int) (max * ((float) percent / 100));
285 private synchronized BridgeHandler getDssBridgeHandler() {
286 if (this.dssBridgeHandler == null) {
287 Bridge bridge = getBridge();
288 if (bridge == null) {
289 logger.debug("Bride cannot be found");
292 ThingHandler handler = bridge.getHandler();
294 if (handler instanceof BridgeHandler) {
295 dssBridgeHandler = (BridgeHandler) handler;
300 return dssBridgeHandler;
303 private boolean sensorChannelsLoaded() {
304 return loadedSensorChannels != null && !loadedSensorChannels.isEmpty();
308 public synchronized void onDeviceStateChanged(DeviceStateUpdate deviceStateUpdate) {
309 if (device != null) {
310 if (deviceStateUpdate != null) {
311 if (sensorChannelsLoaded()) {
312 if (deviceStateUpdate.isSensorUpdateType()) {
313 updateState(getSensorChannelID(deviceStateUpdate.getTypeAsSensorEnum()),
314 new DecimalType(deviceStateUpdate.getValueAsFloat()));
315 logger.debug("Update state");
318 if (deviceStateUpdate.isBinarayInputType()) {
319 if (deviceStateUpdate.getValueAsShort() == 1) {
320 updateState(getBinaryInputChannelID(deviceStateUpdate.getTypeAsDeviceBinarayInputEnum()),
323 updateState(getBinaryInputChannelID(deviceStateUpdate.getTypeAsDeviceBinarayInputEnum()),
328 if (!device.isShade()) {
329 if (currentChannel != null) {
330 switch (deviceStateUpdate.getType()) {
331 case DeviceStateUpdate.OUTPUT_DECREASE:
332 case DeviceStateUpdate.OUTPUT_INCREASE:
333 case DeviceStateUpdate.OUTPUT:
334 if (currentChannel.contains(DsChannelTypeProvider.DIMMER)) {
335 if (deviceStateUpdate.getValueAsInteger() > 0) {
336 updateState(currentChannel, new PercentType(fromValueToPercent(
337 deviceStateUpdate.getValueAsInteger(), device.getMaxOutputValue())));
339 updateState(currentChannel, OnOffType.OFF);
341 } else if (currentChannel.contains(DsChannelTypeProvider.STAGE)) {
342 if (currentChannel.contains(TWO_STAGE_SWITCH_IDENTICATOR)) {
343 updateState(currentChannel,
344 new StringType(convertStageValue((short) 2, device.getOutputValue())));
346 updateState(currentChannel,
347 new StringType(convertStageValue((short) 3, device.getOutputValue())));
351 case DeviceStateUpdate.ON_OFF:
352 if (currentChannel.contains(DsChannelTypeProvider.STAGE)) {
353 onDeviceStateChanged(new DeviceStateUpdateImpl(DeviceStateUpdate.OUTPUT,
354 device.getOutputValue()));
356 if (deviceStateUpdate.getValueAsInteger() > 0) {
357 updateState(currentChannel, OnOffType.ON);
359 updateState(currentChannel, OnOffType.OFF);
368 switch (deviceStateUpdate.getType()) {
369 case DeviceStateUpdate.SLAT_DECREASE:
370 case DeviceStateUpdate.SLAT_INCREASE:
371 case DeviceStateUpdate.SLATPOSITION:
372 percent = fromValueToPercent(deviceStateUpdate.getValueAsInteger(),
373 device.getMaxSlatPosition());
375 case DeviceStateUpdate.OPEN_CLOSE:
376 if (deviceStateUpdate.getValueAsInteger() > 0) {
380 case DeviceStateUpdate.OPEN_CLOSE_ANGLE:
381 if (device.isBlind() && currentChannel != null) {
382 if (deviceStateUpdate.getValueAsInteger() > 0) {
383 updateState(currentChannel, PercentType.HUNDRED);
385 updateState(currentChannel, PercentType.ZERO);
389 case DeviceStateUpdate.SLAT_ANGLE_DECREASE:
390 case DeviceStateUpdate.SLAT_ANGLE_INCREASE:
391 case DeviceStateUpdate.SLAT_ANGLE:
392 if (device.isBlind() && currentChannel != null) {
393 updateState(currentChannel,
394 new PercentType(fromValueToPercent(deviceStateUpdate.getValueAsInteger(),
395 device.getMaxSlatAngle())));
401 if (!device.getHWinfo().equals("GR-KL210")) {
402 percent = 100 - percent;
404 updateState(DsChannelTypeProvider.SHADE, new PercentType(percent));
406 logger.debug("Update state");
411 private int fromValueToPercent(int value, int max) {
412 if (value <= 0 || max <= 0) {
415 int percentValue = new BigDecimal(value * ((float) 100 / max)).setScale(0, RoundingMode.HALF_UP).intValue();
416 return percentValue < 0 ? 0 : percentValue > 100 ? 100 : percentValue;
420 public synchronized void onDeviceRemoved(GeneralDeviceInformation device) {
421 if (device instanceof Device) {
422 this.device = (Device) device;
423 if (this.getThing().getStatus().equals(ThingStatus.ONLINE)) {
424 if (!((Device) device).isPresent()) {
425 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.NONE,
426 "Device is not present in the digitalSTROM-System.");
428 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.NONE,
429 "Device is not avaible in the digitalSTROM-System.");
433 logger.debug("Set status to {}", getThing().getStatus());
438 public synchronized void onDeviceAdded(GeneralDeviceInformation device) {
439 if (device instanceof Device) {
440 this.device = (Device) device;
441 if (this.device.isPresent()) {
442 ThingStatusInfo statusInfo = this.dssBridgeHandler.getThing().getStatusInfo();
443 updateStatus(statusInfo.getStatus(), statusInfo.getStatusDetail(), statusInfo.getDescription());
444 logger.debug("Set status to {}", getThing().getStatus());
446 // load scene configurations persistently into the thing
447 for (Short i : this.device.getSavedScenes()) {
448 onSceneConfigAdded(i);
450 logger.debug("Load saved scene specification into device");
451 this.device.saveConfigSceneSpecificationIntoDevice(getThing().getProperties());
453 checkDeviceInfoProperties(this.device);
454 // load sensor priorities into the device and load sensor channels of the thing
455 if (!this.device.isShade()) {
456 loadSensorChannels();
457 // check and load output channel of the thing
458 checkOutputChannel();
459 } else if (this.device.isBlind()) {
460 // load channel for set the angle of jalousie devices
461 ApplicationGroup.Color color = ((Device) device).getFunctionalColorGroup() != null
462 ? ((Device) device).getFunctionalColorGroup().getColor()
464 String channelTypeID = DsChannelTypeProvider.getOutputChannelTypeID(color,
465 ((Device) device).getOutputMode(), ((Device) device).getOutputChannels());
466 loadOutputChannel(new ChannelTypeUID(BINDING_ID, channelTypeID),
467 DsChannelTypeProvider.getItemType(channelTypeID));
470 // load first channel values
471 onDeviceStateInitial(this.device);
475 onDeviceRemoved(device);
479 * Updates device info properties.
481 * @param device (must not be null)
483 private void checkDeviceInfoProperties(Device device) {
484 boolean propertiesChanged = false;
485 Map<String, String> properties = editProperties();
487 if (device.getName() != null) {
488 properties.put(DigitalSTROMBindingConstants.DEVICE_NAME, device.getName());
489 propertiesChanged = true;
491 if (device.getDSUID() != null) {
492 properties.put(DigitalSTROMBindingConstants.DEVICE_UID, device.getDSUID());
493 propertiesChanged = true;
495 if (device.getHWinfo() != null) {
496 properties.put(DigitalSTROMBindingConstants.DEVICE_HW_INFO, device.getHWinfo());
497 propertiesChanged = true;
499 if (device.getZoneId() != -1) {
500 properties.put(DigitalSTROMBindingConstants.DEVICE_ZONE_ID, device.getZoneId() + "");
501 propertiesChanged = true;
503 if (device.getGroups() != null) {
504 properties.put(DigitalSTROMBindingConstants.DEVICE_GROUPS, device.getGroups().toString());
505 propertiesChanged = true;
507 if (device.getOutputMode() != null) {
508 properties.put(DigitalSTROMBindingConstants.DEVICE_OUTPUT_MODE, device.getOutputMode().toString());
509 propertiesChanged = true;
511 if (device.getFunctionalColorGroup() != null) {
512 properties.put(DigitalSTROMBindingConstants.DEVICE_FUNCTIONAL_COLOR_GROUP,
513 device.getFunctionalColorGroup().toString());
514 propertiesChanged = true;
516 if (device.getMeterDSID() != null) {
517 properties.put(DigitalSTROMBindingConstants.DEVICE_METER_ID, device.getMeterDSID().toString());
518 propertiesChanged = true;
520 if (!device.getBinaryInputs().isEmpty()) {
521 properties.put(DigitalSTROMBindingConstants.DEVICE_BINARAY_INPUTS, getBinarayInputList());
522 propertiesChanged = true;
524 if (propertiesChanged) {
525 super.updateProperties(properties);
526 propertiesChanged = false;
530 private String getBinarayInputList() {
531 List<String> binarayInputs = new ArrayList<>(device.getBinaryInputs().size());
532 for (DeviceBinaryInput binInput : device.getBinaryInputs()) {
533 DeviceBinarayInputEnum devBinInp = DeviceBinarayInputEnum.getdeviceBinarayInput(binInput.getInputType());
534 if (devBinInp != null) {
535 binarayInputs.add(devBinInp.toString().toLowerCase());
538 return binarayInputs.toString();
541 private void loadSensorChannels() {
542 if (device != null && device.isPresent()) {
543 // load sensor priorities into the device
544 boolean configChanged = false;
545 Configuration config = getThing().getConfiguration();
546 logger.debug("Add sensor priorities to the device");
548 String activePowerPrio = Config.REFRESH_PRIORITY_NEVER;
549 if (config.get(DigitalSTROMBindingConstants.ACTIVE_POWER_REFRESH_PRIORITY) != null) {
550 activePowerPrio = config.get(DigitalSTROMBindingConstants.ACTIVE_POWER_REFRESH_PRIORITY).toString();
552 config.put(DigitalSTROMBindingConstants.ACTIVE_POWER_REFRESH_PRIORITY, Config.REFRESH_PRIORITY_NEVER);
553 configChanged = true;
555 // By devices with output mode WIPE the active power always will be read out to check, if the device is not
556 // in standby any more.
557 if (OutputModeEnum.WIPE.equals(device.getOutputMode())
558 && activePowerPrio.equals(Config.REFRESH_PRIORITY_NEVER)) {
559 config.put(DigitalSTROMBindingConstants.ACTIVE_POWER_REFRESH_PRIORITY, Config.REFRESH_PRIORITY_LOW);
560 configChanged = true;
563 String outputCurrentPrio = Config.REFRESH_PRIORITY_NEVER;
564 if (config.get(DigitalSTROMBindingConstants.OUTPUT_CURRENT_REFRESH_PRIORITY) != null) {
565 outputCurrentPrio = config.get(DigitalSTROMBindingConstants.OUTPUT_CURRENT_REFRESH_PRIORITY).toString();
567 config.put(DigitalSTROMBindingConstants.OUTPUT_CURRENT_REFRESH_PRIORITY, Config.REFRESH_PRIORITY_NEVER);
568 configChanged = true;
571 String electricMeterPrio = Config.REFRESH_PRIORITY_NEVER;
572 if (config.get(DigitalSTROMBindingConstants.ELECTRIC_METER_REFRESH_PRIORITY) != null) {
573 electricMeterPrio = config.get(DigitalSTROMBindingConstants.ELECTRIC_METER_REFRESH_PRIORITY).toString();
575 config.put(DigitalSTROMBindingConstants.ELECTRIC_METER_REFRESH_PRIORITY, Config.REFRESH_PRIORITY_NEVER);
576 configChanged = true;
580 super.updateConfiguration(config);
581 configChanged = false;
584 device.setSensorDataRefreshPriority(activePowerPrio, electricMeterPrio, outputCurrentPrio);
586 "add sensor prioritys: active power = {}, output current = {}, electric meter = {} to device with id {}",
587 activePowerPrio, outputCurrentPrio, electricMeterPrio, device.getDSID());
589 // check and load sensor channels of the thing
590 checkSensorChannel();
594 private boolean addLoadedSensorChannel(String sensorChannelType) {
595 if (loadedSensorChannels == null) {
596 loadedSensorChannels = new LinkedList<>();
598 if (!loadedSensorChannels.contains(sensorChannelType.toString())) {
599 return loadedSensorChannels.add(sensorChannelType.toString());
604 private boolean removeLoadedSensorChannel(String sensorChannelType) {
605 if (loadedSensorChannels == null) {
608 return loadedSensorChannels.remove(sensorChannelType);
611 private boolean isSensorChannelLoaded(String sensorChannelType) {
612 if (loadedSensorChannels == null) {
615 return loadedSensorChannels.contains(sensorChannelType);
618 private void checkSensorChannel() {
619 List<Channel> channelList = new LinkedList<>(this.getThing().getChannels());
621 boolean channelListChanged = false;
623 // if sensor channels with priority never are loaded delete these channels
624 if (!channelList.isEmpty()) {
625 Iterator<Channel> channelInter = channelList.iterator();
626 while (channelInter.hasNext()) {
627 Channel channel = channelInter.next();
628 String channelID = channel.getUID().getId();
629 if (channelID.startsWith(DsChannelTypeProvider.BINARY_INPUT_PRE)) {
630 DeviceBinarayInputEnum devBinInput = getBinaryInput(channelID);
631 if (device.getBinaryInput(devBinInput) != null) {
632 addLoadedSensorChannel(channelID);
634 logger.debug("remove {} binary input channel", channelID);
635 channelInter.remove();
636 channelListChanged = removeLoadedSensorChannel(channelID);
639 SensorEnum sensorType = getSensorEnum(channelID);
640 if (sensorType != null) {
641 if (SensorEnum.isPowerSensor(sensorType)) {
642 if (device.checkPowerSensorRefreshPriorityNever(sensorType)) {
643 logger.debug("remove {} sensor channel", channelID);
644 channelInter.remove();
645 channelListChanged = removeLoadedSensorChannel(channelID);
647 addLoadedSensorChannel(channelID);
650 if (device.supportsSensorType(sensorType)) {
651 addLoadedSensorChannel(channelID);
653 logger.debug("remove {} sensor channel", channelID);
654 channelInter.remove();
655 removeLoadedSensorChannel(channelID);
656 channelListChanged = true;
663 for (SensorEnum sensorType : device.getPowerSensorTypes()) {
664 if (!device.checkPowerSensorRefreshPriorityNever(sensorType)
665 && !isSensorChannelLoaded(getSensorChannelID(sensorType))) {
666 logger.debug("create {} sensor channel", sensorType.toString());
667 channelList.add(getSensorChannel(sensorType));
668 channelListChanged = addLoadedSensorChannel(getSensorChannelID(sensorType));
671 if (device.hasClimateSensors()) {
672 for (SensorEnum sensorType : device.getClimateSensorTypes()) {
673 if (!isSensorChannelLoaded(getSensorChannelID(sensorType))) {
674 logger.debug("create {} sensor channel", sensorType.toString());
675 channelList.add(getSensorChannel(sensorType));
676 channelListChanged = addLoadedSensorChannel(getSensorChannelID(sensorType));
680 if (device.isBinaryInputDevice()) {
681 for (DeviceBinaryInput binInput : device.getBinaryInputs()) {
682 DeviceBinarayInputEnum binInputType = DeviceBinarayInputEnum
683 .getdeviceBinarayInput(binInput.getInputType());
684 if (binInputType != null && !isSensorChannelLoaded(getBinaryInputChannelID(binInputType))) {
685 logger.debug("create {} sensor channel", binInputType.toString());
686 channelList.add(getBinaryChannel(binInputType));
687 channelListChanged = addLoadedSensorChannel(getBinaryInputChannelID(binInputType));
692 if (channelListChanged) {
693 logger.debug("load new channel list");
694 ThingBuilder thingBuilder = editThing();
695 thingBuilder.withChannels(channelList);
696 updateThing(thingBuilder.build());
700 private Channel getSensorChannel(SensorEnum sensorType) {
701 return ChannelBuilder.create(getSensorChannelUID(sensorType), "Number")
702 .withType(DsChannelTypeProvider.getSensorChannelUID(sensorType)).build();
705 private Channel getBinaryChannel(DeviceBinarayInputEnum binaryInputType) {
706 return ChannelBuilder.create(getBinaryInputChannelUID(binaryInputType), "Switch")
707 .withType(DsChannelTypeProvider.getBinaryInputChannelUID(binaryInputType)).build();
710 private void checkOutputChannel() {
711 if (device == null) {
712 logger.debug("Can not load a channel without a device!");
715 // if the device have no output channel or it is disabled all output channels will be deleted
716 if (!device.isDeviceWithOutput()) {
717 loadOutputChannel(null, null);
719 ApplicationGroup.Color color = device.getFunctionalColorGroup() != null
720 ? device.getFunctionalColorGroup().getColor()
722 String channelTypeID = DsChannelTypeProvider.getOutputChannelTypeID(color, device.getOutputMode(),
723 device.getOutputChannels());
724 logger.debug("load channel: typeID={}, itemType={}",
725 DsChannelTypeProvider.getOutputChannelTypeID(device.getFunctionalColorGroup().getColor(),
726 device.getOutputMode(), device.getOutputChannels()),
727 DsChannelTypeProvider.getItemType(channelTypeID));
728 if (channelTypeID != null && (currentChannel == null || !currentChannel.equals(channelTypeID))) {
729 loadOutputChannel(new ChannelTypeUID(BINDING_ID, channelTypeID),
730 DsChannelTypeProvider.getItemType(channelTypeID));
734 private void loadOutputChannel(ChannelTypeUID channelTypeUID, String acceptedItemType) {
735 if (channelTypeUID == null || acceptedItemType == null) {
738 currentChannel = channelTypeUID.getId();
740 List<Channel> channelList = new LinkedList<>(this.getThing().getChannels());
741 boolean channelIsAlreadyLoaded = false;
742 boolean channelListChanged = false;
744 if (!channelList.isEmpty()) {
745 Iterator<Channel> channelInter = channelList.iterator();
746 while (channelInter.hasNext()) {
747 Channel channel = channelInter.next();
748 if (DsChannelTypeProvider.isOutputChannel(channel.getUID().getId())) {
749 if (!channel.getUID().getId().equals(currentChannel)
750 && !(device.isShade() && channel.getUID().getId().equals(DsChannelTypeProvider.SHADE))) {
751 channelInter.remove();
752 channelListChanged = true;
754 if (!channel.getUID().getId().equals(DsChannelTypeProvider.SHADE)) {
755 channelIsAlreadyLoaded = true;
762 if (!channelIsAlreadyLoaded && currentChannel != null) {
763 Channel channel = ChannelBuilder
764 .create(new ChannelUID(this.getThing().getUID(), channelTypeUID.getId()), acceptedItemType)
765 .withType(channelTypeUID).build();
766 channelList.add(channel);
767 channelListChanged = true;
770 if (channelListChanged) {
771 ThingBuilder thingBuilder = editThing();
772 thingBuilder.withChannels(channelList);
773 updateThing(thingBuilder.build());
774 logger.debug("load channel: {} with item: {}", channelTypeUID.getAsString(), acceptedItemType);
778 private ChannelUID getSensorChannelUID(SensorEnum sensorType) {
779 return new ChannelUID(getThing().getUID(), getSensorChannelID(sensorType));
782 private String getSensorChannelID(SensorEnum sensorType) {
783 return sensorType.toString().toLowerCase();
786 private ChannelUID getBinaryInputChannelUID(DeviceBinarayInputEnum binaryInputType) {
787 return new ChannelUID(getThing().getUID(), getBinaryInputChannelID(binaryInputType));
790 private String getBinaryInputChannelID(DeviceBinarayInputEnum binaryInputType) {
791 return DsChannelTypeProvider.BINARY_INPUT_PRE + binaryInputType.toString().toLowerCase();
794 private DeviceBinarayInputEnum getBinaryInput(String channelID) {
796 return DeviceBinarayInputEnum
797 .valueOf(channelID.replace(DsChannelTypeProvider.BINARY_INPUT_PRE, "").toUpperCase());
798 } catch (IllegalArgumentException e) {
803 private SensorEnum getSensorEnum(String channelID) {
805 return SensorEnum.valueOf(channelID.toUpperCase());
806 } catch (IllegalArgumentException e) {
812 public void channelLinked(ChannelUID channelUID) {
813 if (device != null) {
814 SensorEnum sensorType = getSensorEnum(channelUID.getId());
815 if (sensorType != null) {
816 Float val = device.getFloatSensorValue(sensorType);
818 updateState(channelUID, new DecimalType(val));
821 Short val = device.getBinaryInputState(getBinaryInput(channelUID.getId()));
824 updateState(channelUID, OnOffType.ON);
826 updateState(channelUID, OnOffType.OFF);
830 if (channelUID.getId().contains(DsChannelTypeProvider.DIMMER)) {
832 updateState(channelUID,
833 new PercentType(fromValueToPercent(device.getOutputValue(), device.getMaxOutputValue())));
835 updateState(channelUID, new PercentType(0));
839 if (channelUID.getId().contains(DsChannelTypeProvider.SWITCH)) {
841 updateState(channelUID, OnOffType.ON);
843 updateState(channelUID, OnOffType.OFF);
847 if (channelUID.getId().contains(DsChannelTypeProvider.SHADE)) {
848 updateState(channelUID,
849 new PercentType(fromValueToPercent(device.getSlatPosition(), device.getMaxSlatPosition())));
852 if (channelUID.getId().contains(DsChannelTypeProvider.ANGLE)) {
853 updateState(channelUID,
854 new PercentType(fromValueToPercent(device.getAnglePosition(), device.getMaxSlatAngle())));
857 if (channelUID.getId().contains(DsChannelTypeProvider.STAGE)) {
858 if (channelUID.getId().contains(TWO_STAGE_SWITCH_IDENTICATOR)) {
859 updateState(channelUID, new StringType(convertStageValue((short) 2, device.getOutputValue())));
862 if (channelUID.getId().contains(THREE_STAGE_SWITCH_IDENTICATOR)) {
863 updateState(channelUID, new StringType(convertStageValue((short) 3, device.getOutputValue())));
870 private String convertStageValue(short stage, short value) {
874 return OPTION_COMBINED_BOTH_OFF;
875 } else if (value >= 85 && value < 170) {
876 return OPTION_COMBINED_FIRST_ON;
877 } else if (value >= 170 && value <= 255) {
878 return OPTION_COMBINED_BOTH_ON;
882 return OPTION_COMBINED_BOTH_OFF;
883 } else if (value >= 64 && value < 128) {
884 return OPTION_COMBINED_FIRST_ON;
885 } else if (value >= 128 && value < 192) {
886 return OPTION_COMBINED_SECOND_ON;
887 } else if (value >= 192 && value <= 255) {
888 return OPTION_COMBINED_BOTH_ON;
894 private void onDeviceStateInitial(Device device) {
895 if (device != null) {
896 if (currentChannel != null) {
897 if (isLinked(currentChannel)) {
898 channelLinked(new ChannelUID(getThing().getUID(), currentChannel));
901 if (!device.isShade()) {
902 if (loadedSensorChannels != null) {
903 for (String sensor : loadedSensorChannels) {
904 Channel channel = getThing().getChannel(sensor);
905 if (channel != null && isLinked(sensor)) {
906 channelLinked(channel.getUID());
911 if (isLinked(DsChannelTypeProvider.SHADE)) {
912 channelLinked(new ChannelUID(getThing().getUID(), DsChannelTypeProvider.SHADE));
919 public synchronized void onSceneConfigAdded(short sceneId) {
920 if (device != null) {
921 String saveScene = "";
922 DeviceSceneSpec sceneSpec = device.getSceneConfig(sceneId);
923 if (sceneSpec != null) {
924 saveScene = sceneSpec.toString();
927 Integer[] sceneValue = device.getSceneOutputValue(sceneId);
928 if (sceneValue[GeneralLibConstance.SCENE_ARRAY_INDEX_VALUE] != -1) {
929 saveScene = saveScene + ", sceneValue: " + sceneValue[0];
931 if (sceneValue[GeneralLibConstance.SCENE_ARRAY_INDEX_ANGLE] != -1) {
932 saveScene = saveScene + ", sceneAngle: " + sceneValue[1];
934 String key = DigitalSTROMBindingConstants.DEVICE_SCENE + sceneId;
935 if (!saveScene.isEmpty()) {
936 logger.debug("Save scene configuration: [{}] to thing with UID {}", saveScene, getThing().getUID());
937 super.updateProperty(key, saveScene);
938 // persist the new property
939 // super.updateThing(editThing().build());
945 public void onDeviceConfigChanged(ChangeableDeviceConfigEnum whichConfig) {
946 if (whichConfig != null) {
947 switch (whichConfig) {
949 super.updateProperty(DEVICE_NAME, device.getName());
952 super.updateProperty(DEVICE_METER_ID, device.getMeterDSID().getValue());
955 super.updateProperty(DEVICE_ZONE_ID, device.getZoneId() + "");
958 super.updateProperty(DEVICE_GROUPS, device.getGroups().toString());
960 case FUNCTIONAL_GROUP:
961 super.updateProperty(DEVICE_FUNCTIONAL_COLOR_GROUP, device.getFunctionalColorGroup().toString());
962 checkOutputChannel();
965 super.updateProperty(DEVICE_OUTPUT_MODE, device.getOutputMode().toString());
966 checkOutputChannel();
969 super.updateProperty(DEVICE_BINARAY_INPUTS, getBinarayInputList());
970 checkSensorChannel();
979 public String getDeviceStatusListenerID() {