2 * Copyright (c) 2010-2020 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.util.ArrayList;
19 import java.util.HashSet;
20 import java.util.Iterator;
21 import java.util.LinkedList;
22 import java.util.List;
26 import org.apache.commons.lang.StringUtils;
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 if (StringUtils.isNotBlank((String) getConfig().get(DigitalSTROMBindingConstants.DEVICE_DSID))) {
112 dSID = getConfig().get(DigitalSTROMBindingConstants.DEVICE_DSID).toString();
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 if (bridgeStatusInfo.getStatus().equals(ThingStatus.REMOVED)) {
180 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.NONE, "Bridge has been removed.");
182 logger.debug("Set status to {}", getThing().getStatusInfo());
186 public void handleCommand(ChannelUID channelUID, Command command) {
187 BridgeHandler dssBridgeHandler = getDssBridgeHandler();
188 if (dssBridgeHandler == null) {
189 logger.debug("BridgeHandler not found. Cannot handle command without bridge.");
193 if (device == null) {
195 "Device not known on StructureManager or DeviceStatusListener is not registerd. Cannot handle command.");
199 if (command instanceof RefreshType) {
201 SensorEnum sensorType = SensorEnum.valueOf(channelUID.getId());
202 dssBridgeHandler.sendComandsToDSS(device, new DeviceStateUpdateImpl(sensorType, 1));
203 } catch (IllegalArgumentException e) {
204 dssBridgeHandler.sendComandsToDSS(device,
205 new DeviceStateUpdateImpl(DeviceStateUpdate.REFRESH_OUTPUT, 0));
207 } else if (!device.isShade()) {
208 if (DsChannelTypeProvider.isOutputChannel(channelUID.getId())) {
209 if (command instanceof PercentType) {
210 device.setOutputValue(
211 (short) fromPercentToValue(((PercentType) command).intValue(), device.getMaxOutputValue()));
212 } else if (command instanceof OnOffType) {
213 if (OnOffType.ON.equals(command)) {
214 device.setIsOn(true);
216 device.setIsOn(false);
218 } else if (command instanceof IncreaseDecreaseType) {
219 if (IncreaseDecreaseType.INCREASE.equals(command)) {
224 } else if (command instanceof StringType) {
225 device.setOutputValue(Short.parseShort(((StringType) command).toString()));
228 logger.debug("Command sent to an unknown channel id: {}", channelUID);
231 if (channelUID.getId().contains(DsChannelTypeProvider.ANGLE)) {
232 if (command instanceof PercentType) {
233 device.setAnglePosition(
234 (short) fromPercentToValue(((PercentType) command).intValue(), device.getMaxSlatAngle()));
235 } else if (command instanceof OnOffType) {
236 if (OnOffType.ON.equals(command)) {
237 device.setAnglePosition(device.getMaxSlatAngle());
239 device.setAnglePosition(device.getMinSlatAngle());
241 } else if (command instanceof IncreaseDecreaseType) {
242 if (IncreaseDecreaseType.INCREASE.equals(command)) {
243 device.increaseSlatAngle();
245 device.decreaseSlatAngle();
248 } else if (channelUID.getId().contains(DsChannelTypeProvider.SHADE)) {
249 if (command instanceof PercentType) {
250 int percent = ((PercentType) command).intValue();
251 if (!device.getHWinfo().equals("GR-KL200")) {
252 percent = 100 - percent;
254 device.setSlatPosition(fromPercentToValue(percent, device.getMaxSlatPosition()));
255 this.lastComand = command;
256 } else if (command instanceof StopMoveType) {
257 if (StopMoveType.MOVE.equals(command)) {
258 handleCommand(channelUID, this.lastComand);
260 dssBridgeHandler.stopOutputValue(device);
262 } else if (command instanceof UpDownType) {
263 if (UpDownType.UP.equals(command)) {
264 device.setIsOpen(true);
265 this.lastComand = command;
267 device.setIsOpen(false);
268 this.lastComand = command;
272 logger.debug("Command sent to an unknown channel id: {}", channelUID);
277 private int fromPercentToValue(int percent, int max) {
278 if (percent < 0 || percent == 0) {
281 if (max < 0 || max == 0) {
284 return (int) (max * ((float) percent / 100));
287 private synchronized BridgeHandler getDssBridgeHandler() {
288 if (this.dssBridgeHandler == null) {
289 Bridge bridge = getBridge();
290 if (bridge == null) {
291 logger.debug("Bride cannot be found");
294 ThingHandler handler = bridge.getHandler();
296 if (handler instanceof BridgeHandler) {
297 dssBridgeHandler = (BridgeHandler) handler;
302 return dssBridgeHandler;
305 private boolean sensorChannelsLoaded() {
306 return loadedSensorChannels != null && !loadedSensorChannels.isEmpty();
310 public synchronized void onDeviceStateChanged(DeviceStateUpdate deviceStateUpdate) {
311 if (device != null) {
312 if (deviceStateUpdate != null) {
313 if (sensorChannelsLoaded()) {
314 if (deviceStateUpdate.isSensorUpdateType()) {
315 updateState(getSensorChannelID(deviceStateUpdate.getTypeAsSensorEnum()),
316 new DecimalType(deviceStateUpdate.getValueAsFloat()));
317 logger.debug("Update ESH-State");
320 if (deviceStateUpdate.isBinarayInputType()) {
321 if (deviceStateUpdate.getValueAsShort() == 1) {
322 updateState(getBinaryInputChannelID(deviceStateUpdate.getTypeAsDeviceBinarayInputEnum()),
325 updateState(getBinaryInputChannelID(deviceStateUpdate.getTypeAsDeviceBinarayInputEnum()),
330 if (!device.isShade()) {
331 if (currentChannel != null) {
332 switch (deviceStateUpdate.getType()) {
333 case DeviceStateUpdate.OUTPUT_DECREASE:
334 case DeviceStateUpdate.OUTPUT_INCREASE:
335 case DeviceStateUpdate.OUTPUT:
336 if (currentChannel.contains(DsChannelTypeProvider.DIMMER)) {
337 if (deviceStateUpdate.getValueAsInteger() > 0) {
338 updateState(currentChannel, new PercentType(fromValueToPercent(
339 deviceStateUpdate.getValueAsInteger(), device.getMaxOutputValue())));
341 updateState(currentChannel, OnOffType.OFF);
343 } else if (currentChannel.contains(DsChannelTypeProvider.STAGE)) {
344 if (currentChannel.contains(TWO_STAGE_SWITCH_IDENTICATOR)) {
345 updateState(currentChannel,
346 new StringType(convertStageValue((short) 2, device.getOutputValue())));
348 updateState(currentChannel,
349 new StringType(convertStageValue((short) 3, device.getOutputValue())));
353 case DeviceStateUpdate.ON_OFF:
354 if (currentChannel.contains(DsChannelTypeProvider.STAGE)) {
355 onDeviceStateChanged(new DeviceStateUpdateImpl(DeviceStateUpdate.OUTPUT,
356 device.getOutputValue()));
358 if (deviceStateUpdate.getValueAsInteger() > 0) {
359 updateState(currentChannel, OnOffType.ON);
361 updateState(currentChannel, OnOffType.OFF);
370 switch (deviceStateUpdate.getType()) {
371 case DeviceStateUpdate.SLAT_DECREASE:
372 case DeviceStateUpdate.SLAT_INCREASE:
373 case DeviceStateUpdate.SLATPOSITION:
374 percent = fromValueToPercent(deviceStateUpdate.getValueAsInteger(),
375 device.getMaxSlatPosition());
377 case DeviceStateUpdate.OPEN_CLOSE:
378 if (deviceStateUpdate.getValueAsInteger() > 0) {
382 case DeviceStateUpdate.OPEN_CLOSE_ANGLE:
383 if (device.isBlind() && currentChannel != null) {
384 if (deviceStateUpdate.getValueAsInteger() > 0) {
385 updateState(currentChannel, PercentType.HUNDRED);
387 updateState(currentChannel, PercentType.ZERO);
391 case DeviceStateUpdate.SLAT_ANGLE_DECREASE:
392 case DeviceStateUpdate.SLAT_ANGLE_INCREASE:
393 case DeviceStateUpdate.SLAT_ANGLE:
394 if (device.isBlind() && currentChannel != null) {
395 updateState(currentChannel,
396 new PercentType(fromValueToPercent(deviceStateUpdate.getValueAsInteger(),
397 device.getMaxSlatAngle())));
403 if (!device.getHWinfo().equals("GR-KL210")) {
404 percent = 100 - percent;
406 updateState(DsChannelTypeProvider.SHADE, new PercentType(percent));
408 logger.debug("Update ESH-State");
413 private int fromValueToPercent(int value, int max) {
414 if (value <= 0 || max <= 0) {
417 int percentValue = new BigDecimal(value * ((float) 100 / max)).setScale(0, BigDecimal.ROUND_HALF_UP).intValue();
418 return percentValue < 0 ? 0 : percentValue > 100 ? 100 : percentValue;
422 public synchronized void onDeviceRemoved(GeneralDeviceInformation device) {
423 if (device instanceof Device) {
424 this.device = (Device) device;
425 if (this.getThing().getStatus().equals(ThingStatus.ONLINE)) {
426 if (!((Device) device).isPresent()) {
427 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.NONE,
428 "Device is not present in the digitalSTROM-System.");
430 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.NONE,
431 "Device is not avaible in the digitalSTROM-System.");
435 logger.debug("Set status to {}", getThing().getStatus());
440 public synchronized void onDeviceAdded(GeneralDeviceInformation device) {
441 if (device instanceof Device) {
442 this.device = (Device) device;
443 if (this.device.isPresent()) {
444 ThingStatusInfo statusInfo = this.dssBridgeHandler.getThing().getStatusInfo();
445 updateStatus(statusInfo.getStatus(), statusInfo.getStatusDetail(), statusInfo.getDescription());
446 logger.debug("Set status to {}", getThing().getStatus());
448 // load scene configurations persistently into the thing
449 for (Short i : this.device.getSavedScenes()) {
450 onSceneConfigAdded(i);
452 logger.debug("Load saved scene specification into device");
453 this.device.saveConfigSceneSpecificationIntoDevice(getThing().getProperties());
455 checkDeviceInfoProperties(this.device);
456 // load sensor priorities into the device and load sensor channels of the thing
457 if (!this.device.isShade()) {
458 loadSensorChannels();
459 // check and load output channel of the thing
460 checkOutputChannel();
461 } else if (this.device.isBlind()) {
462 // load channel for set the angle of jalousie devices
463 String channelTypeID = DsChannelTypeProvider.getOutputChannelTypeID(
464 ((Device) device).getFunctionalColorGroup(), ((Device) device).getOutputMode());
465 loadOutputChannel(new ChannelTypeUID(BINDING_ID, channelTypeID),
466 DsChannelTypeProvider.getItemType(channelTypeID));
469 // load first channel values
470 onDeviceStateInitial(this.device);
474 onDeviceRemoved(device);
478 * Updates device info properties.
480 * @param device (must not be null)
482 private void checkDeviceInfoProperties(Device device) {
483 boolean propertiesChanged = false;
484 Map<String, String> properties = editProperties();
486 if (device.getName() != null) {
487 properties.put(DigitalSTROMBindingConstants.DEVICE_NAME, device.getName());
488 propertiesChanged = true;
490 if (device.getDSUID() != null) {
491 properties.put(DigitalSTROMBindingConstants.DEVICE_UID, device.getDSUID());
492 propertiesChanged = true;
494 if (device.getHWinfo() != null) {
495 properties.put(DigitalSTROMBindingConstants.DEVICE_HW_INFO, device.getHWinfo());
496 propertiesChanged = true;
498 if (device.getZoneId() != -1) {
499 properties.put(DigitalSTROMBindingConstants.DEVICE_ZONE_ID, device.getZoneId() + "");
500 propertiesChanged = true;
502 if (device.getGroups() != null) {
503 properties.put(DigitalSTROMBindingConstants.DEVICE_GROUPS, device.getGroups().toString());
504 propertiesChanged = true;
506 if (device.getOutputMode() != null) {
507 properties.put(DigitalSTROMBindingConstants.DEVICE_OUTPUT_MODE, device.getOutputMode().toString());
508 propertiesChanged = true;
510 if (device.getFunctionalColorGroup() != null) {
511 properties.put(DigitalSTROMBindingConstants.DEVICE_FUNCTIONAL_COLOR_GROUP,
512 device.getFunctionalColorGroup().toString());
513 propertiesChanged = true;
515 if (device.getMeterDSID() != null) {
516 properties.put(DigitalSTROMBindingConstants.DEVICE_METER_ID, device.getMeterDSID().toString());
517 propertiesChanged = true;
519 if (!device.getBinaryInputs().isEmpty()) {
520 properties.put(DigitalSTROMBindingConstants.DEVICE_BINARAY_INPUTS, getBinarayInputList());
521 propertiesChanged = true;
523 if (propertiesChanged) {
524 super.updateProperties(properties);
525 propertiesChanged = false;
529 private String getBinarayInputList() {
530 List<String> binarayInputs = new ArrayList<>(device.getBinaryInputs().size());
531 for (DeviceBinaryInput binInput : device.getBinaryInputs()) {
532 DeviceBinarayInputEnum devBinInp = DeviceBinarayInputEnum.getdeviceBinarayInput(binInput.getInputType());
533 if (devBinInp != null) {
534 binarayInputs.add(devBinInp.toString().toLowerCase());
537 return binarayInputs.toString();
540 private void loadSensorChannels() {
541 if (device != null && device.isPresent()) {
542 // load sensor priorities into the device
543 boolean configChanged = false;
544 Configuration config = getThing().getConfiguration();
545 logger.debug("Add sensor priorities to the device");
547 String activePowerPrio = Config.REFRESH_PRIORITY_NEVER;
548 if (config.get(DigitalSTROMBindingConstants.ACTIVE_POWER_REFRESH_PRIORITY) != null) {
549 activePowerPrio = config.get(DigitalSTROMBindingConstants.ACTIVE_POWER_REFRESH_PRIORITY).toString();
551 config.put(DigitalSTROMBindingConstants.ACTIVE_POWER_REFRESH_PRIORITY, Config.REFRESH_PRIORITY_NEVER);
552 configChanged = true;
554 // By devices with output mode WIPE the active power always will be read out to check, if the device is not
555 // in standby any more.
556 if (OutputModeEnum.WIPE.equals(device.getOutputMode())
557 && activePowerPrio.equals(Config.REFRESH_PRIORITY_NEVER)) {
558 config.put(DigitalSTROMBindingConstants.ACTIVE_POWER_REFRESH_PRIORITY, Config.REFRESH_PRIORITY_LOW);
559 configChanged = true;
562 String outputCurrentPrio = Config.REFRESH_PRIORITY_NEVER;
563 if (config.get(DigitalSTROMBindingConstants.OUTPUT_CURRENT_REFRESH_PRIORITY) != null) {
564 outputCurrentPrio = config.get(DigitalSTROMBindingConstants.OUTPUT_CURRENT_REFRESH_PRIORITY).toString();
566 config.put(DigitalSTROMBindingConstants.OUTPUT_CURRENT_REFRESH_PRIORITY, Config.REFRESH_PRIORITY_NEVER);
567 configChanged = true;
570 String electricMeterPrio = Config.REFRESH_PRIORITY_NEVER;
571 if (config.get(DigitalSTROMBindingConstants.ELECTRIC_METER_REFRESH_PRIORITY) != null) {
572 electricMeterPrio = config.get(DigitalSTROMBindingConstants.ELECTRIC_METER_REFRESH_PRIORITY).toString();
574 config.put(DigitalSTROMBindingConstants.ELECTRIC_METER_REFRESH_PRIORITY, Config.REFRESH_PRIORITY_NEVER);
575 configChanged = true;
579 super.updateConfiguration(config);
580 configChanged = false;
583 device.setSensorDataRefreshPriority(activePowerPrio, electricMeterPrio, outputCurrentPrio);
585 "add sensor prioritys: active power = {}, output current = {}, electric meter = {} to device with id {}",
586 activePowerPrio, outputCurrentPrio, electricMeterPrio, device.getDSID());
588 // check and load sensor channels of the thing
589 checkSensorChannel();
593 private boolean addLoadedSensorChannel(String sensorChannelType) {
594 if (loadedSensorChannels == null) {
595 loadedSensorChannels = new LinkedList<>();
597 if (!loadedSensorChannels.contains(sensorChannelType.toString())) {
598 return loadedSensorChannels.add(sensorChannelType.toString());
603 private boolean removeLoadedSensorChannel(String sensorChannelType) {
604 if (loadedSensorChannels == null) {
607 return loadedSensorChannels.remove(sensorChannelType);
610 private boolean isSensorChannelLoaded(String sensorChannelType) {
611 if (loadedSensorChannels == null) {
614 return loadedSensorChannels.contains(sensorChannelType);
617 private void checkSensorChannel() {
618 List<Channel> channelList = new LinkedList<>(this.getThing().getChannels());
620 boolean channelListChanged = false;
622 // if sensor channels with priority never are loaded delete these channels
623 if (!channelList.isEmpty()) {
624 Iterator<Channel> channelInter = channelList.iterator();
625 while (channelInter.hasNext()) {
626 Channel channel = channelInter.next();
627 String channelID = channel.getUID().getId();
628 if (channelID.startsWith(DsChannelTypeProvider.BINARY_INPUT_PRE)) {
629 DeviceBinarayInputEnum devBinInput = getBinaryInput(channelID);
630 if (device.getBinaryInput(devBinInput) != null) {
631 addLoadedSensorChannel(channelID);
633 logger.debug("remove {} binary input channel", channelID);
634 channelInter.remove();
635 channelListChanged = removeLoadedSensorChannel(channelID);
638 SensorEnum sensorType = getSensorEnum(channelID);
639 if (sensorType != null) {
640 if (SensorEnum.isPowerSensor(sensorType)) {
641 if (device.checkPowerSensorRefreshPriorityNever(sensorType)) {
642 logger.debug("remove {} sensor channel", channelID);
643 channelInter.remove();
644 channelListChanged = removeLoadedSensorChannel(channelID);
646 addLoadedSensorChannel(channelID);
649 if (device.supportsSensorType(sensorType)) {
650 addLoadedSensorChannel(channelID);
652 logger.debug("remove {} sensor channel", channelID);
653 channelInter.remove();
654 removeLoadedSensorChannel(channelID);
655 channelListChanged = true;
662 for (SensorEnum sensorType : device.getPowerSensorTypes()) {
663 if (!device.checkPowerSensorRefreshPriorityNever(sensorType)
664 && !isSensorChannelLoaded(getSensorChannelID(sensorType))) {
665 logger.debug("create {} sensor channel", sensorType.toString());
666 channelList.add(getSensorChannel(sensorType));
667 channelListChanged = addLoadedSensorChannel(getSensorChannelID(sensorType));
670 if (device.hasClimateSensors()) {
671 for (SensorEnum sensorType : device.getClimateSensorTypes()) {
672 if (!isSensorChannelLoaded(getSensorChannelID(sensorType))) {
673 logger.debug("create {} sensor channel", sensorType.toString());
674 channelList.add(getSensorChannel(sensorType));
675 channelListChanged = addLoadedSensorChannel(getSensorChannelID(sensorType));
679 if (device.isBinaryInputDevice()) {
680 for (DeviceBinaryInput binInput : device.getBinaryInputs()) {
681 DeviceBinarayInputEnum binInputType = DeviceBinarayInputEnum
682 .getdeviceBinarayInput(binInput.getInputType());
683 if (binInputType != null && !isSensorChannelLoaded(getBinaryInputChannelID(binInputType))) {
684 logger.debug("create {} sensor channel", binInputType.toString());
685 channelList.add(getBinaryChannel(binInputType));
686 channelListChanged = addLoadedSensorChannel(getBinaryInputChannelID(binInputType));
691 if (channelListChanged) {
692 logger.debug("load new channel list");
693 ThingBuilder thingBuilder = editThing();
694 thingBuilder.withChannels(channelList);
695 updateThing(thingBuilder.build());
699 private Channel getSensorChannel(SensorEnum sensorType) {
700 return ChannelBuilder.create(getSensorChannelUID(sensorType), "Number")
701 .withType(DsChannelTypeProvider.getSensorChannelUID(sensorType)).build();
704 private Channel getBinaryChannel(DeviceBinarayInputEnum binaryInputType) {
705 return ChannelBuilder.create(getBinaryInputChannelUID(binaryInputType), "Switch")
706 .withType(DsChannelTypeProvider.getBinaryInputChannelUID(binaryInputType)).build();
709 private void checkOutputChannel() {
710 if (device == null) {
711 logger.debug("Can not load a channel without a device!");
714 // if the device have no output channel or it is disabled all output channels will be deleted
715 if (!device.isDeviceWithOutput()) {
716 loadOutputChannel(null, null);
718 String channelTypeID = DsChannelTypeProvider.getOutputChannelTypeID(device.getFunctionalColorGroup(),
719 device.getOutputMode());
720 logger.debug("load channel: typeID={}, itemType={}",
721 DsChannelTypeProvider.getOutputChannelTypeID(device.getFunctionalColorGroup(), device.getOutputMode()),
722 DsChannelTypeProvider.getItemType(channelTypeID));
723 if (channelTypeID != null && (currentChannel == null || !currentChannel.equals(channelTypeID))) {
724 loadOutputChannel(new ChannelTypeUID(BINDING_ID, channelTypeID),
725 DsChannelTypeProvider.getItemType(channelTypeID));
729 private void loadOutputChannel(ChannelTypeUID channelTypeUID, String acceptedItemType) {
730 if (channelTypeUID == null || acceptedItemType == null) {
733 currentChannel = channelTypeUID.getId();
735 List<Channel> channelList = new LinkedList<>(this.getThing().getChannels());
736 boolean channelIsAlreadyLoaded = false;
737 boolean channelListChanged = false;
739 if (!channelList.isEmpty()) {
740 Iterator<Channel> channelInter = channelList.iterator();
741 while (channelInter.hasNext()) {
742 Channel eshChannel = channelInter.next();
743 if (DsChannelTypeProvider.isOutputChannel(eshChannel.getUID().getId())) {
744 if (!eshChannel.getUID().getId().equals(currentChannel)
745 && !(device.isShade() && eshChannel.getUID().getId().equals(DsChannelTypeProvider.SHADE))) {
746 channelInter.remove();
747 channelListChanged = true;
749 if (!eshChannel.getUID().getId().equals(DsChannelTypeProvider.SHADE)) {
750 channelIsAlreadyLoaded = true;
757 if (!channelIsAlreadyLoaded && currentChannel != null) {
758 Channel channel = ChannelBuilder
759 .create(new ChannelUID(this.getThing().getUID(), channelTypeUID.getId()), acceptedItemType)
760 .withType(channelTypeUID).build();
761 channelList.add(channel);
762 channelListChanged = true;
765 if (channelListChanged) {
766 ThingBuilder thingBuilder = editThing();
767 thingBuilder.withChannels(channelList);
768 updateThing(thingBuilder.build());
769 logger.debug("load channel: {} with item: {}", channelTypeUID.getAsString(), acceptedItemType);
773 private ChannelUID getSensorChannelUID(SensorEnum sensorType) {
774 return new ChannelUID(getThing().getUID(), getSensorChannelID(sensorType));
777 private String getSensorChannelID(SensorEnum sensorType) {
778 return sensorType.toString().toLowerCase();
781 private ChannelUID getBinaryInputChannelUID(DeviceBinarayInputEnum binaryInputType) {
782 return new ChannelUID(getThing().getUID(), getBinaryInputChannelID(binaryInputType));
785 private String getBinaryInputChannelID(DeviceBinarayInputEnum binaryInputType) {
786 return DsChannelTypeProvider.BINARY_INPUT_PRE + binaryInputType.toString().toLowerCase();
789 private DeviceBinarayInputEnum getBinaryInput(String channelID) {
791 return DeviceBinarayInputEnum
792 .valueOf(channelID.replace(DsChannelTypeProvider.BINARY_INPUT_PRE, "").toUpperCase());
793 } catch (IllegalArgumentException e) {
798 private SensorEnum getSensorEnum(String channelID) {
800 return SensorEnum.valueOf(channelID.toUpperCase());
801 } catch (IllegalArgumentException e) {
807 public void channelLinked(ChannelUID channelUID) {
808 if (device != null) {
809 SensorEnum sensorType = getSensorEnum(channelUID.getId());
810 if (sensorType != null) {
811 Float val = device.getFloatSensorValue(sensorType);
813 updateState(channelUID, new DecimalType(val));
816 Short val = device.getBinaryInputState(getBinaryInput(channelUID.getId()));
819 updateState(channelUID, OnOffType.ON);
821 updateState(channelUID, OnOffType.OFF);
825 if (channelUID.getId().contains(DsChannelTypeProvider.DIMMER)) {
827 updateState(channelUID,
828 new PercentType(fromValueToPercent(device.getOutputValue(), device.getMaxOutputValue())));
830 updateState(channelUID, new PercentType(0));
834 if (channelUID.getId().contains(DsChannelTypeProvider.SWITCH)) {
836 updateState(channelUID, OnOffType.ON);
838 updateState(channelUID, OnOffType.OFF);
842 if (channelUID.getId().contains(DsChannelTypeProvider.SHADE)) {
843 updateState(channelUID,
844 new PercentType(fromValueToPercent(device.getSlatPosition(), device.getMaxSlatPosition())));
847 if (channelUID.getId().contains(DsChannelTypeProvider.ANGLE)) {
848 updateState(channelUID,
849 new PercentType(fromValueToPercent(device.getAnglePosition(), device.getMaxSlatAngle())));
852 if (channelUID.getId().contains(DsChannelTypeProvider.STAGE)) {
853 if (channelUID.getId().contains(TWO_STAGE_SWITCH_IDENTICATOR)) {
854 updateState(channelUID, new StringType(convertStageValue((short) 2, device.getOutputValue())));
857 if (channelUID.getId().contains(THREE_STAGE_SWITCH_IDENTICATOR)) {
858 updateState(channelUID, new StringType(convertStageValue((short) 3, device.getOutputValue())));
865 private String convertStageValue(short stage, short value) {
869 return OPTION_COMBINED_BOTH_OFF;
870 } else if (value >= 85 && value < 170) {
871 return OPTION_COMBINED_FIRST_ON;
872 } else if (value >= 170 && value <= 255) {
873 return OPTION_COMBINED_BOTH_ON;
877 return OPTION_COMBINED_BOTH_OFF;
878 } else if (value >= 64 && value < 128) {
879 return OPTION_COMBINED_FIRST_ON;
880 } else if (value >= 128 && value < 192) {
881 return OPTION_COMBINED_SECOND_ON;
882 } else if (value >= 192 && value <= 255) {
883 return OPTION_COMBINED_BOTH_ON;
889 private void onDeviceStateInitial(Device device) {
890 if (device != null) {
891 if (currentChannel != null) {
892 if (isLinked(currentChannel)) {
893 channelLinked(new ChannelUID(getThing().getUID(), currentChannel));
896 if (!device.isShade()) {
897 if (loadedSensorChannels != null) {
898 for (String sensor : loadedSensorChannels) {
899 Channel channel = getThing().getChannel(sensor);
900 if (channel != null && isLinked(sensor)) {
901 channelLinked(channel.getUID());
906 if (isLinked(DsChannelTypeProvider.SHADE)) {
907 channelLinked(new ChannelUID(getThing().getUID(), DsChannelTypeProvider.SHADE));
914 public synchronized void onSceneConfigAdded(short sceneId) {
915 if (device != null) {
916 String saveScene = "";
917 DeviceSceneSpec sceneSpec = device.getSceneConfig(sceneId);
918 if (sceneSpec != null) {
919 saveScene = sceneSpec.toString();
922 Integer[] sceneValue = device.getSceneOutputValue(sceneId);
923 if (sceneValue[GeneralLibConstance.SCENE_ARRAY_INDEX_VALUE] != -1) {
924 saveScene = saveScene + ", sceneValue: " + sceneValue[0];
926 if (sceneValue[GeneralLibConstance.SCENE_ARRAY_INDEX_ANGLE] != -1) {
927 saveScene = saveScene + ", sceneAngle: " + sceneValue[1];
929 String key = DigitalSTROMBindingConstants.DEVICE_SCENE + sceneId;
930 if (!saveScene.isEmpty()) {
931 logger.debug("Save scene configuration: [{}] to thing with UID {}", saveScene, getThing().getUID());
932 super.updateProperty(key, saveScene);
933 // persist the new property
934 // super.updateThing(editThing().build());
940 public void onDeviceConfigChanged(ChangeableDeviceConfigEnum whichConfig) {
941 if (whichConfig != null) {
942 switch (whichConfig) {
944 super.updateProperty(DEVICE_NAME, device.getName());
947 super.updateProperty(DEVICE_METER_ID, device.getMeterDSID().getValue());
950 super.updateProperty(DEVICE_ZONE_ID, device.getZoneId() + "");
953 super.updateProperty(DEVICE_GROUPS, device.getGroups().toString());
955 case FUNCTIONAL_GROUP:
956 super.updateProperty(DEVICE_FUNCTIONAL_COLOR_GROUP, device.getFunctionalColorGroup().toString());
957 checkOutputChannel();
960 super.updateProperty(DEVICE_OUTPUT_MODE, device.getOutputMode().toString());
961 checkOutputChannel();
964 super.updateProperty(DEVICE_BINARAY_INPUTS, getBinarayInputList());
965 checkSensorChannel();
974 public String getDeviceStatusListenerID() {