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.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.openhab.binding.digitalstrom.internal.DigitalSTROMBindingConstants;
27 import org.openhab.binding.digitalstrom.internal.lib.GeneralLibConstance;
28 import org.openhab.binding.digitalstrom.internal.lib.config.Config;
29 import org.openhab.binding.digitalstrom.internal.lib.listener.DeviceStatusListener;
30 import org.openhab.binding.digitalstrom.internal.lib.structure.devices.Device;
31 import org.openhab.binding.digitalstrom.internal.lib.structure.devices.GeneralDeviceInformation;
32 import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.DeviceSceneSpec;
33 import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.DeviceStateUpdate;
34 import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.constants.ChangeableDeviceConfigEnum;
35 import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.constants.DeviceBinarayInputEnum;
36 import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.constants.OutputModeEnum;
37 import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.constants.SensorEnum;
38 import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.impl.DeviceBinaryInput;
39 import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.impl.DeviceStateUpdateImpl;
40 import org.openhab.binding.digitalstrom.internal.providers.DsChannelTypeProvider;
41 import org.openhab.core.config.core.Configuration;
42 import org.openhab.core.library.types.DecimalType;
43 import org.openhab.core.library.types.IncreaseDecreaseType;
44 import org.openhab.core.library.types.OnOffType;
45 import org.openhab.core.library.types.PercentType;
46 import org.openhab.core.library.types.StopMoveType;
47 import org.openhab.core.library.types.StringType;
48 import org.openhab.core.library.types.UpDownType;
49 import org.openhab.core.thing.Bridge;
50 import org.openhab.core.thing.Channel;
51 import org.openhab.core.thing.ChannelUID;
52 import org.openhab.core.thing.Thing;
53 import org.openhab.core.thing.ThingStatus;
54 import org.openhab.core.thing.ThingStatusDetail;
55 import org.openhab.core.thing.ThingStatusInfo;
56 import org.openhab.core.thing.ThingTypeUID;
57 import org.openhab.core.thing.binding.BaseThingHandler;
58 import org.openhab.core.thing.binding.ThingHandler;
59 import org.openhab.core.thing.binding.builder.ChannelBuilder;
60 import org.openhab.core.thing.binding.builder.ThingBuilder;
61 import org.openhab.core.thing.type.ChannelTypeUID;
62 import org.openhab.core.types.Command;
63 import org.openhab.core.types.RefreshType;
64 import org.slf4j.Logger;
65 import org.slf4j.LoggerFactory;
68 * The {@link DeviceHandler} is responsible for handling the configuration, load supported channels of a
69 * digitalSTROM device and handling commands, which are sent to one of the channels. <br>
71 * For that it uses the {@link BridgeHandler} and the {@link DeviceStateUpdate} mechanism of the {@link Device} to
72 * execute the actual command and implements the {@link DeviceStatusListener} to get informed about changes from the
73 * accompanying {@link Device}.
75 * @author Michael Ochel - Initial contribution
76 * @author Matthias Siegele - Initial contribution
78 public class DeviceHandler extends BaseThingHandler implements DeviceStatusListener {
80 private final Logger logger = LoggerFactory.getLogger(DeviceHandler.class);
83 * Contains all supported thing types of this handler, will be filled by DsDeviceThingTypeProvider.
85 public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = new HashSet<>();
87 public static final String TWO_STAGE_SWITCH_IDENTICATOR = "2";
88 public static final String THREE_STAGE_SWITCH_IDENTICATOR = "3";
91 private Device device;
92 private BridgeHandler dssBridgeHandler;
94 private Command lastComand;
95 private String currentChannel;
96 private List<String> loadedSensorChannels;
99 * Creates a new {@link DeviceHandler}.
101 * @param thing must not be null
103 public DeviceHandler(Thing thing) {
108 public void initialize() {
109 logger.debug("Initializing DeviceHandler.");
110 dSID = (String) getConfig().get(DigitalSTROMBindingConstants.DEVICE_DSID);
111 if (dSID != null && !dSID.isBlank()) {
112 final Bridge bridge = getBridge();
113 if (bridge != null) {
114 bridgeStatusChanged(bridge.getStatusInfo());
116 // Set status to OFFLINE if no bridge is available e.g. because the bridge has been removed and the
117 // Thing was reinitialized.
118 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.NONE, "Bridge is missing!");
121 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "dSID is missing");
126 public void dispose() {
127 logger.debug("Handler disposed... unregister DeviceStatusListener");
129 if (dssBridgeHandler != null) {
130 dssBridgeHandler.unregisterDeviceStatusListener(this);
133 if (device != null) {
134 device.setSensorDataRefreshPriority(Config.REFRESH_PRIORITY_NEVER, Config.REFRESH_PRIORITY_NEVER,
135 Config.REFRESH_PRIORITY_NEVER);
141 public void handleRemoval() {
142 if (getDssBridgeHandler() != null) {
143 this.dssBridgeHandler.childThingRemoved(dSID);
145 updateStatus(ThingStatus.REMOVED);
149 public void thingUpdated(Thing thing) {
151 if (device == null) {
157 public void bridgeStatusChanged(ThingStatusInfo bridgeStatusInfo) {
158 if (bridgeStatusInfo.getStatus().equals(ThingStatus.ONLINE)) {
160 if (getDssBridgeHandler() != null) {
161 if (device == null) {
162 updateStatus(ThingStatus.ONLINE, ThingStatusDetail.CONFIGURATION_PENDING,
163 "waiting for listener registration");
164 dssBridgeHandler.registerDeviceStatusListener(this);
166 updateStatus(ThingStatus.ONLINE);
169 updateStatus(ThingStatus.OFFLINE);
172 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "No dSID is set!");
175 if (bridgeStatusInfo.getStatus().equals(ThingStatus.OFFLINE)) {
176 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE);
178 logger.debug("Set status to {}", getThing().getStatusInfo());
182 public void handleCommand(ChannelUID channelUID, Command command) {
183 BridgeHandler dssBridgeHandler = getDssBridgeHandler();
184 if (dssBridgeHandler == null) {
185 logger.debug("BridgeHandler not found. Cannot handle command without bridge.");
189 if (device == null) {
191 "Device not known on StructureManager or DeviceStatusListener is not registerd. Cannot handle command.");
195 if (command instanceof RefreshType) {
197 SensorEnum sensorType = SensorEnum.valueOf(channelUID.getId());
198 dssBridgeHandler.sendComandsToDSS(device, new DeviceStateUpdateImpl(sensorType, 1));
199 } catch (IllegalArgumentException e) {
200 dssBridgeHandler.sendComandsToDSS(device,
201 new DeviceStateUpdateImpl(DeviceStateUpdate.REFRESH_OUTPUT, 0));
203 } else if (!device.isShade()) {
204 if (DsChannelTypeProvider.isOutputChannel(channelUID.getId())) {
205 if (command instanceof PercentType) {
206 device.setOutputValue(
207 (short) fromPercentToValue(((PercentType) command).intValue(), device.getMaxOutputValue()));
208 } else if (command instanceof OnOffType) {
209 if (OnOffType.ON.equals(command)) {
210 device.setIsOn(true);
212 device.setIsOn(false);
214 } else if (command instanceof IncreaseDecreaseType) {
215 if (IncreaseDecreaseType.INCREASE.equals(command)) {
220 } else if (command instanceof StringType) {
221 device.setOutputValue(Short.parseShort(((StringType) command).toString()));
224 logger.debug("Command sent to an unknown channel id: {}", channelUID);
227 if (channelUID.getId().contains(DsChannelTypeProvider.ANGLE)) {
228 if (command instanceof PercentType) {
229 device.setAnglePosition(
230 (short) fromPercentToValue(((PercentType) command).intValue(), device.getMaxSlatAngle()));
231 } else if (command instanceof OnOffType) {
232 if (OnOffType.ON.equals(command)) {
233 device.setAnglePosition(device.getMaxSlatAngle());
235 device.setAnglePosition(device.getMinSlatAngle());
237 } else if (command instanceof IncreaseDecreaseType) {
238 if (IncreaseDecreaseType.INCREASE.equals(command)) {
239 device.increaseSlatAngle();
241 device.decreaseSlatAngle();
244 } else if (channelUID.getId().contains(DsChannelTypeProvider.SHADE)) {
245 if (command instanceof PercentType) {
246 int percent = ((PercentType) command).intValue();
247 if (!device.getHWinfo().equals("GR-KL200")) {
248 percent = 100 - percent;
250 device.setSlatPosition(fromPercentToValue(percent, device.getMaxSlatPosition()));
251 this.lastComand = command;
252 } else if (command instanceof StopMoveType) {
253 if (StopMoveType.MOVE.equals(command)) {
254 handleCommand(channelUID, this.lastComand);
256 dssBridgeHandler.stopOutputValue(device);
258 } else if (command instanceof UpDownType) {
259 if (UpDownType.UP.equals(command)) {
260 device.setIsOpen(true);
261 this.lastComand = command;
263 device.setIsOpen(false);
264 this.lastComand = command;
268 logger.debug("Command sent to an unknown channel id: {}", channelUID);
273 private int fromPercentToValue(int percent, int max) {
274 if (percent < 0 || percent == 0) {
277 if (max < 0 || max == 0) {
280 return (int) (max * ((float) percent / 100));
283 private synchronized BridgeHandler getDssBridgeHandler() {
284 if (this.dssBridgeHandler == null) {
285 Bridge bridge = getBridge();
286 if (bridge == null) {
287 logger.debug("Bride cannot be found");
290 ThingHandler handler = bridge.getHandler();
292 if (handler instanceof BridgeHandler) {
293 dssBridgeHandler = (BridgeHandler) handler;
298 return dssBridgeHandler;
301 private boolean sensorChannelsLoaded() {
302 return loadedSensorChannels != null && !loadedSensorChannels.isEmpty();
306 public synchronized void onDeviceStateChanged(DeviceStateUpdate deviceStateUpdate) {
307 if (device != null) {
308 if (deviceStateUpdate != null) {
309 if (sensorChannelsLoaded()) {
310 if (deviceStateUpdate.isSensorUpdateType()) {
311 updateState(getSensorChannelID(deviceStateUpdate.getTypeAsSensorEnum()),
312 new DecimalType(deviceStateUpdate.getValueAsFloat()));
313 logger.debug("Update state");
316 if (deviceStateUpdate.isBinarayInputType()) {
317 if (deviceStateUpdate.getValueAsShort() == 1) {
318 updateState(getBinaryInputChannelID(deviceStateUpdate.getTypeAsDeviceBinarayInputEnum()),
321 updateState(getBinaryInputChannelID(deviceStateUpdate.getTypeAsDeviceBinarayInputEnum()),
326 if (!device.isShade()) {
327 if (currentChannel != null) {
328 switch (deviceStateUpdate.getType()) {
329 case DeviceStateUpdate.OUTPUT_DECREASE:
330 case DeviceStateUpdate.OUTPUT_INCREASE:
331 case DeviceStateUpdate.OUTPUT:
332 if (currentChannel.contains(DsChannelTypeProvider.DIMMER)) {
333 if (deviceStateUpdate.getValueAsInteger() > 0) {
334 updateState(currentChannel, new PercentType(fromValueToPercent(
335 deviceStateUpdate.getValueAsInteger(), device.getMaxOutputValue())));
337 updateState(currentChannel, OnOffType.OFF);
339 } else if (currentChannel.contains(DsChannelTypeProvider.STAGE)) {
340 if (currentChannel.contains(TWO_STAGE_SWITCH_IDENTICATOR)) {
341 updateState(currentChannel,
342 new StringType(convertStageValue((short) 2, device.getOutputValue())));
344 updateState(currentChannel,
345 new StringType(convertStageValue((short) 3, device.getOutputValue())));
349 case DeviceStateUpdate.ON_OFF:
350 if (currentChannel.contains(DsChannelTypeProvider.STAGE)) {
351 onDeviceStateChanged(new DeviceStateUpdateImpl(DeviceStateUpdate.OUTPUT,
352 device.getOutputValue()));
354 if (deviceStateUpdate.getValueAsInteger() > 0) {
355 updateState(currentChannel, OnOffType.ON);
357 updateState(currentChannel, OnOffType.OFF);
366 switch (deviceStateUpdate.getType()) {
367 case DeviceStateUpdate.SLAT_DECREASE:
368 case DeviceStateUpdate.SLAT_INCREASE:
369 case DeviceStateUpdate.SLATPOSITION:
370 percent = fromValueToPercent(deviceStateUpdate.getValueAsInteger(),
371 device.getMaxSlatPosition());
373 case DeviceStateUpdate.OPEN_CLOSE:
374 if (deviceStateUpdate.getValueAsInteger() > 0) {
378 case DeviceStateUpdate.OPEN_CLOSE_ANGLE:
379 if (device.isBlind() && currentChannel != null) {
380 if (deviceStateUpdate.getValueAsInteger() > 0) {
381 updateState(currentChannel, PercentType.HUNDRED);
383 updateState(currentChannel, PercentType.ZERO);
387 case DeviceStateUpdate.SLAT_ANGLE_DECREASE:
388 case DeviceStateUpdate.SLAT_ANGLE_INCREASE:
389 case DeviceStateUpdate.SLAT_ANGLE:
390 if (device.isBlind() && currentChannel != null) {
391 updateState(currentChannel,
392 new PercentType(fromValueToPercent(deviceStateUpdate.getValueAsInteger(),
393 device.getMaxSlatAngle())));
399 if (!device.getHWinfo().equals("GR-KL210")) {
400 percent = 100 - percent;
402 updateState(DsChannelTypeProvider.SHADE, new PercentType(percent));
404 logger.debug("Update state");
409 private int fromValueToPercent(int value, int max) {
410 if (value <= 0 || max <= 0) {
413 int percentValue = new BigDecimal(value * ((float) 100 / max)).setScale(0, BigDecimal.ROUND_HALF_UP).intValue();
414 return percentValue < 0 ? 0 : percentValue > 100 ? 100 : percentValue;
418 public synchronized void onDeviceRemoved(GeneralDeviceInformation device) {
419 if (device instanceof Device) {
420 this.device = (Device) device;
421 if (this.getThing().getStatus().equals(ThingStatus.ONLINE)) {
422 if (!((Device) device).isPresent()) {
423 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.NONE,
424 "Device is not present in the digitalSTROM-System.");
426 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.NONE,
427 "Device is not avaible in the digitalSTROM-System.");
431 logger.debug("Set status to {}", getThing().getStatus());
436 public synchronized void onDeviceAdded(GeneralDeviceInformation device) {
437 if (device instanceof Device) {
438 this.device = (Device) device;
439 if (this.device.isPresent()) {
440 ThingStatusInfo statusInfo = this.dssBridgeHandler.getThing().getStatusInfo();
441 updateStatus(statusInfo.getStatus(), statusInfo.getStatusDetail(), statusInfo.getDescription());
442 logger.debug("Set status to {}", getThing().getStatus());
444 // load scene configurations persistently into the thing
445 for (Short i : this.device.getSavedScenes()) {
446 onSceneConfigAdded(i);
448 logger.debug("Load saved scene specification into device");
449 this.device.saveConfigSceneSpecificationIntoDevice(getThing().getProperties());
451 checkDeviceInfoProperties(this.device);
452 // load sensor priorities into the device and load sensor channels of the thing
453 if (!this.device.isShade()) {
454 loadSensorChannels();
455 // check and load output channel of the thing
456 checkOutputChannel();
457 } else if (this.device.isBlind()) {
458 // load channel for set the angle of jalousie devices
459 String channelTypeID = DsChannelTypeProvider.getOutputChannelTypeID(
460 ((Device) device).getFunctionalColorGroup(), ((Device) device).getOutputMode());
461 loadOutputChannel(new ChannelTypeUID(BINDING_ID, channelTypeID),
462 DsChannelTypeProvider.getItemType(channelTypeID));
465 // load first channel values
466 onDeviceStateInitial(this.device);
470 onDeviceRemoved(device);
474 * Updates device info properties.
476 * @param device (must not be null)
478 private void checkDeviceInfoProperties(Device device) {
479 boolean propertiesChanged = false;
480 Map<String, String> properties = editProperties();
482 if (device.getName() != null) {
483 properties.put(DigitalSTROMBindingConstants.DEVICE_NAME, device.getName());
484 propertiesChanged = true;
486 if (device.getDSUID() != null) {
487 properties.put(DigitalSTROMBindingConstants.DEVICE_UID, device.getDSUID());
488 propertiesChanged = true;
490 if (device.getHWinfo() != null) {
491 properties.put(DigitalSTROMBindingConstants.DEVICE_HW_INFO, device.getHWinfo());
492 propertiesChanged = true;
494 if (device.getZoneId() != -1) {
495 properties.put(DigitalSTROMBindingConstants.DEVICE_ZONE_ID, device.getZoneId() + "");
496 propertiesChanged = true;
498 if (device.getGroups() != null) {
499 properties.put(DigitalSTROMBindingConstants.DEVICE_GROUPS, device.getGroups().toString());
500 propertiesChanged = true;
502 if (device.getOutputMode() != null) {
503 properties.put(DigitalSTROMBindingConstants.DEVICE_OUTPUT_MODE, device.getOutputMode().toString());
504 propertiesChanged = true;
506 if (device.getFunctionalColorGroup() != null) {
507 properties.put(DigitalSTROMBindingConstants.DEVICE_FUNCTIONAL_COLOR_GROUP,
508 device.getFunctionalColorGroup().toString());
509 propertiesChanged = true;
511 if (device.getMeterDSID() != null) {
512 properties.put(DigitalSTROMBindingConstants.DEVICE_METER_ID, device.getMeterDSID().toString());
513 propertiesChanged = true;
515 if (!device.getBinaryInputs().isEmpty()) {
516 properties.put(DigitalSTROMBindingConstants.DEVICE_BINARAY_INPUTS, getBinarayInputList());
517 propertiesChanged = true;
519 if (propertiesChanged) {
520 super.updateProperties(properties);
521 propertiesChanged = false;
525 private String getBinarayInputList() {
526 List<String> binarayInputs = new ArrayList<>(device.getBinaryInputs().size());
527 for (DeviceBinaryInput binInput : device.getBinaryInputs()) {
528 DeviceBinarayInputEnum devBinInp = DeviceBinarayInputEnum.getdeviceBinarayInput(binInput.getInputType());
529 if (devBinInp != null) {
530 binarayInputs.add(devBinInp.toString().toLowerCase());
533 return binarayInputs.toString();
536 private void loadSensorChannels() {
537 if (device != null && device.isPresent()) {
538 // load sensor priorities into the device
539 boolean configChanged = false;
540 Configuration config = getThing().getConfiguration();
541 logger.debug("Add sensor priorities to the device");
543 String activePowerPrio = Config.REFRESH_PRIORITY_NEVER;
544 if (config.get(DigitalSTROMBindingConstants.ACTIVE_POWER_REFRESH_PRIORITY) != null) {
545 activePowerPrio = config.get(DigitalSTROMBindingConstants.ACTIVE_POWER_REFRESH_PRIORITY).toString();
547 config.put(DigitalSTROMBindingConstants.ACTIVE_POWER_REFRESH_PRIORITY, Config.REFRESH_PRIORITY_NEVER);
548 configChanged = true;
550 // By devices with output mode WIPE the active power always will be read out to check, if the device is not
551 // in standby any more.
552 if (OutputModeEnum.WIPE.equals(device.getOutputMode())
553 && activePowerPrio.equals(Config.REFRESH_PRIORITY_NEVER)) {
554 config.put(DigitalSTROMBindingConstants.ACTIVE_POWER_REFRESH_PRIORITY, Config.REFRESH_PRIORITY_LOW);
555 configChanged = true;
558 String outputCurrentPrio = Config.REFRESH_PRIORITY_NEVER;
559 if (config.get(DigitalSTROMBindingConstants.OUTPUT_CURRENT_REFRESH_PRIORITY) != null) {
560 outputCurrentPrio = config.get(DigitalSTROMBindingConstants.OUTPUT_CURRENT_REFRESH_PRIORITY).toString();
562 config.put(DigitalSTROMBindingConstants.OUTPUT_CURRENT_REFRESH_PRIORITY, Config.REFRESH_PRIORITY_NEVER);
563 configChanged = true;
566 String electricMeterPrio = Config.REFRESH_PRIORITY_NEVER;
567 if (config.get(DigitalSTROMBindingConstants.ELECTRIC_METER_REFRESH_PRIORITY) != null) {
568 electricMeterPrio = config.get(DigitalSTROMBindingConstants.ELECTRIC_METER_REFRESH_PRIORITY).toString();
570 config.put(DigitalSTROMBindingConstants.ELECTRIC_METER_REFRESH_PRIORITY, Config.REFRESH_PRIORITY_NEVER);
571 configChanged = true;
575 super.updateConfiguration(config);
576 configChanged = false;
579 device.setSensorDataRefreshPriority(activePowerPrio, electricMeterPrio, outputCurrentPrio);
581 "add sensor prioritys: active power = {}, output current = {}, electric meter = {} to device with id {}",
582 activePowerPrio, outputCurrentPrio, electricMeterPrio, device.getDSID());
584 // check and load sensor channels of the thing
585 checkSensorChannel();
589 private boolean addLoadedSensorChannel(String sensorChannelType) {
590 if (loadedSensorChannels == null) {
591 loadedSensorChannels = new LinkedList<>();
593 if (!loadedSensorChannels.contains(sensorChannelType.toString())) {
594 return loadedSensorChannels.add(sensorChannelType.toString());
599 private boolean removeLoadedSensorChannel(String sensorChannelType) {
600 if (loadedSensorChannels == null) {
603 return loadedSensorChannels.remove(sensorChannelType);
606 private boolean isSensorChannelLoaded(String sensorChannelType) {
607 if (loadedSensorChannels == null) {
610 return loadedSensorChannels.contains(sensorChannelType);
613 private void checkSensorChannel() {
614 List<Channel> channelList = new LinkedList<>(this.getThing().getChannels());
616 boolean channelListChanged = false;
618 // if sensor channels with priority never are loaded delete these channels
619 if (!channelList.isEmpty()) {
620 Iterator<Channel> channelInter = channelList.iterator();
621 while (channelInter.hasNext()) {
622 Channel channel = channelInter.next();
623 String channelID = channel.getUID().getId();
624 if (channelID.startsWith(DsChannelTypeProvider.BINARY_INPUT_PRE)) {
625 DeviceBinarayInputEnum devBinInput = getBinaryInput(channelID);
626 if (device.getBinaryInput(devBinInput) != null) {
627 addLoadedSensorChannel(channelID);
629 logger.debug("remove {} binary input channel", channelID);
630 channelInter.remove();
631 channelListChanged = removeLoadedSensorChannel(channelID);
634 SensorEnum sensorType = getSensorEnum(channelID);
635 if (sensorType != null) {
636 if (SensorEnum.isPowerSensor(sensorType)) {
637 if (device.checkPowerSensorRefreshPriorityNever(sensorType)) {
638 logger.debug("remove {} sensor channel", channelID);
639 channelInter.remove();
640 channelListChanged = removeLoadedSensorChannel(channelID);
642 addLoadedSensorChannel(channelID);
645 if (device.supportsSensorType(sensorType)) {
646 addLoadedSensorChannel(channelID);
648 logger.debug("remove {} sensor channel", channelID);
649 channelInter.remove();
650 removeLoadedSensorChannel(channelID);
651 channelListChanged = true;
658 for (SensorEnum sensorType : device.getPowerSensorTypes()) {
659 if (!device.checkPowerSensorRefreshPriorityNever(sensorType)
660 && !isSensorChannelLoaded(getSensorChannelID(sensorType))) {
661 logger.debug("create {} sensor channel", sensorType.toString());
662 channelList.add(getSensorChannel(sensorType));
663 channelListChanged = addLoadedSensorChannel(getSensorChannelID(sensorType));
666 if (device.hasClimateSensors()) {
667 for (SensorEnum sensorType : device.getClimateSensorTypes()) {
668 if (!isSensorChannelLoaded(getSensorChannelID(sensorType))) {
669 logger.debug("create {} sensor channel", sensorType.toString());
670 channelList.add(getSensorChannel(sensorType));
671 channelListChanged = addLoadedSensorChannel(getSensorChannelID(sensorType));
675 if (device.isBinaryInputDevice()) {
676 for (DeviceBinaryInput binInput : device.getBinaryInputs()) {
677 DeviceBinarayInputEnum binInputType = DeviceBinarayInputEnum
678 .getdeviceBinarayInput(binInput.getInputType());
679 if (binInputType != null && !isSensorChannelLoaded(getBinaryInputChannelID(binInputType))) {
680 logger.debug("create {} sensor channel", binInputType.toString());
681 channelList.add(getBinaryChannel(binInputType));
682 channelListChanged = addLoadedSensorChannel(getBinaryInputChannelID(binInputType));
687 if (channelListChanged) {
688 logger.debug("load new channel list");
689 ThingBuilder thingBuilder = editThing();
690 thingBuilder.withChannels(channelList);
691 updateThing(thingBuilder.build());
695 private Channel getSensorChannel(SensorEnum sensorType) {
696 return ChannelBuilder.create(getSensorChannelUID(sensorType), "Number")
697 .withType(DsChannelTypeProvider.getSensorChannelUID(sensorType)).build();
700 private Channel getBinaryChannel(DeviceBinarayInputEnum binaryInputType) {
701 return ChannelBuilder.create(getBinaryInputChannelUID(binaryInputType), "Switch")
702 .withType(DsChannelTypeProvider.getBinaryInputChannelUID(binaryInputType)).build();
705 private void checkOutputChannel() {
706 if (device == null) {
707 logger.debug("Can not load a channel without a device!");
710 // if the device have no output channel or it is disabled all output channels will be deleted
711 if (!device.isDeviceWithOutput()) {
712 loadOutputChannel(null, null);
714 String channelTypeID = DsChannelTypeProvider.getOutputChannelTypeID(device.getFunctionalColorGroup(),
715 device.getOutputMode());
716 logger.debug("load channel: typeID={}, itemType={}",
717 DsChannelTypeProvider.getOutputChannelTypeID(device.getFunctionalColorGroup(), device.getOutputMode()),
718 DsChannelTypeProvider.getItemType(channelTypeID));
719 if (channelTypeID != null && (currentChannel == null || !currentChannel.equals(channelTypeID))) {
720 loadOutputChannel(new ChannelTypeUID(BINDING_ID, channelTypeID),
721 DsChannelTypeProvider.getItemType(channelTypeID));
725 private void loadOutputChannel(ChannelTypeUID channelTypeUID, String acceptedItemType) {
726 if (channelTypeUID == null || acceptedItemType == null) {
729 currentChannel = channelTypeUID.getId();
731 List<Channel> channelList = new LinkedList<>(this.getThing().getChannels());
732 boolean channelIsAlreadyLoaded = false;
733 boolean channelListChanged = false;
735 if (!channelList.isEmpty()) {
736 Iterator<Channel> channelInter = channelList.iterator();
737 while (channelInter.hasNext()) {
738 Channel channel = channelInter.next();
739 if (DsChannelTypeProvider.isOutputChannel(channel.getUID().getId())) {
740 if (!channel.getUID().getId().equals(currentChannel)
741 && !(device.isShade() && channel.getUID().getId().equals(DsChannelTypeProvider.SHADE))) {
742 channelInter.remove();
743 channelListChanged = true;
745 if (!channel.getUID().getId().equals(DsChannelTypeProvider.SHADE)) {
746 channelIsAlreadyLoaded = true;
753 if (!channelIsAlreadyLoaded && currentChannel != null) {
754 Channel channel = ChannelBuilder
755 .create(new ChannelUID(this.getThing().getUID(), channelTypeUID.getId()), acceptedItemType)
756 .withType(channelTypeUID).build();
757 channelList.add(channel);
758 channelListChanged = true;
761 if (channelListChanged) {
762 ThingBuilder thingBuilder = editThing();
763 thingBuilder.withChannels(channelList);
764 updateThing(thingBuilder.build());
765 logger.debug("load channel: {} with item: {}", channelTypeUID.getAsString(), acceptedItemType);
769 private ChannelUID getSensorChannelUID(SensorEnum sensorType) {
770 return new ChannelUID(getThing().getUID(), getSensorChannelID(sensorType));
773 private String getSensorChannelID(SensorEnum sensorType) {
774 return sensorType.toString().toLowerCase();
777 private ChannelUID getBinaryInputChannelUID(DeviceBinarayInputEnum binaryInputType) {
778 return new ChannelUID(getThing().getUID(), getBinaryInputChannelID(binaryInputType));
781 private String getBinaryInputChannelID(DeviceBinarayInputEnum binaryInputType) {
782 return DsChannelTypeProvider.BINARY_INPUT_PRE + binaryInputType.toString().toLowerCase();
785 private DeviceBinarayInputEnum getBinaryInput(String channelID) {
787 return DeviceBinarayInputEnum
788 .valueOf(channelID.replace(DsChannelTypeProvider.BINARY_INPUT_PRE, "").toUpperCase());
789 } catch (IllegalArgumentException e) {
794 private SensorEnum getSensorEnum(String channelID) {
796 return SensorEnum.valueOf(channelID.toUpperCase());
797 } catch (IllegalArgumentException e) {
803 public void channelLinked(ChannelUID channelUID) {
804 if (device != null) {
805 SensorEnum sensorType = getSensorEnum(channelUID.getId());
806 if (sensorType != null) {
807 Float val = device.getFloatSensorValue(sensorType);
809 updateState(channelUID, new DecimalType(val));
812 Short val = device.getBinaryInputState(getBinaryInput(channelUID.getId()));
815 updateState(channelUID, OnOffType.ON);
817 updateState(channelUID, OnOffType.OFF);
821 if (channelUID.getId().contains(DsChannelTypeProvider.DIMMER)) {
823 updateState(channelUID,
824 new PercentType(fromValueToPercent(device.getOutputValue(), device.getMaxOutputValue())));
826 updateState(channelUID, new PercentType(0));
830 if (channelUID.getId().contains(DsChannelTypeProvider.SWITCH)) {
832 updateState(channelUID, OnOffType.ON);
834 updateState(channelUID, OnOffType.OFF);
838 if (channelUID.getId().contains(DsChannelTypeProvider.SHADE)) {
839 updateState(channelUID,
840 new PercentType(fromValueToPercent(device.getSlatPosition(), device.getMaxSlatPosition())));
843 if (channelUID.getId().contains(DsChannelTypeProvider.ANGLE)) {
844 updateState(channelUID,
845 new PercentType(fromValueToPercent(device.getAnglePosition(), device.getMaxSlatAngle())));
848 if (channelUID.getId().contains(DsChannelTypeProvider.STAGE)) {
849 if (channelUID.getId().contains(TWO_STAGE_SWITCH_IDENTICATOR)) {
850 updateState(channelUID, new StringType(convertStageValue((short) 2, device.getOutputValue())));
853 if (channelUID.getId().contains(THREE_STAGE_SWITCH_IDENTICATOR)) {
854 updateState(channelUID, new StringType(convertStageValue((short) 3, device.getOutputValue())));
861 private String convertStageValue(short stage, short value) {
865 return OPTION_COMBINED_BOTH_OFF;
866 } else if (value >= 85 && value < 170) {
867 return OPTION_COMBINED_FIRST_ON;
868 } else if (value >= 170 && value <= 255) {
869 return OPTION_COMBINED_BOTH_ON;
873 return OPTION_COMBINED_BOTH_OFF;
874 } else if (value >= 64 && value < 128) {
875 return OPTION_COMBINED_FIRST_ON;
876 } else if (value >= 128 && value < 192) {
877 return OPTION_COMBINED_SECOND_ON;
878 } else if (value >= 192 && value <= 255) {
879 return OPTION_COMBINED_BOTH_ON;
885 private void onDeviceStateInitial(Device device) {
886 if (device != null) {
887 if (currentChannel != null) {
888 if (isLinked(currentChannel)) {
889 channelLinked(new ChannelUID(getThing().getUID(), currentChannel));
892 if (!device.isShade()) {
893 if (loadedSensorChannels != null) {
894 for (String sensor : loadedSensorChannels) {
895 Channel channel = getThing().getChannel(sensor);
896 if (channel != null && isLinked(sensor)) {
897 channelLinked(channel.getUID());
902 if (isLinked(DsChannelTypeProvider.SHADE)) {
903 channelLinked(new ChannelUID(getThing().getUID(), DsChannelTypeProvider.SHADE));
910 public synchronized void onSceneConfigAdded(short sceneId) {
911 if (device != null) {
912 String saveScene = "";
913 DeviceSceneSpec sceneSpec = device.getSceneConfig(sceneId);
914 if (sceneSpec != null) {
915 saveScene = sceneSpec.toString();
918 Integer[] sceneValue = device.getSceneOutputValue(sceneId);
919 if (sceneValue[GeneralLibConstance.SCENE_ARRAY_INDEX_VALUE] != -1) {
920 saveScene = saveScene + ", sceneValue: " + sceneValue[0];
922 if (sceneValue[GeneralLibConstance.SCENE_ARRAY_INDEX_ANGLE] != -1) {
923 saveScene = saveScene + ", sceneAngle: " + sceneValue[1];
925 String key = DigitalSTROMBindingConstants.DEVICE_SCENE + sceneId;
926 if (!saveScene.isEmpty()) {
927 logger.debug("Save scene configuration: [{}] to thing with UID {}", saveScene, getThing().getUID());
928 super.updateProperty(key, saveScene);
929 // persist the new property
930 // super.updateThing(editThing().build());
936 public void onDeviceConfigChanged(ChangeableDeviceConfigEnum whichConfig) {
937 if (whichConfig != null) {
938 switch (whichConfig) {
940 super.updateProperty(DEVICE_NAME, device.getName());
943 super.updateProperty(DEVICE_METER_ID, device.getMeterDSID().getValue());
946 super.updateProperty(DEVICE_ZONE_ID, device.getZoneId() + "");
949 super.updateProperty(DEVICE_GROUPS, device.getGroups().toString());
951 case FUNCTIONAL_GROUP:
952 super.updateProperty(DEVICE_FUNCTIONAL_COLOR_GROUP, device.getFunctionalColorGroup().toString());
953 checkOutputChannel();
956 super.updateProperty(DEVICE_OUTPUT_MODE, device.getOutputMode().toString());
957 checkOutputChannel();
960 super.updateProperty(DEVICE_BINARAY_INPUTS, getBinarayInputList());
961 checkSensorChannel();
970 public String getDeviceStatusListenerID() {