]> git.basschouten.com Git - openhab-addons.git/blob
6228b2c98cd5b05eef8aa6e82e1dcff794bdae7b
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2021 Contributors to the openHAB project
3  *
4  * See the NOTICE file(s) distributed with this work for additional
5  * information.
6  *
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
10  *
11  * SPDX-License-Identifier: EPL-2.0
12  */
13 package org.openhab.binding.digitalstrom.internal.handler;
14
15 import static org.openhab.binding.digitalstrom.internal.DigitalSTROMBindingConstants.*;
16
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;
24 import java.util.Map;
25 import java.util.Set;
26
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;
67
68 /**
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>
71  * <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}.
75  *
76  * @author Michael Ochel - Initial contribution
77  * @author Matthias Siegele - Initial contribution
78  */
79 public class DeviceHandler extends BaseThingHandler implements DeviceStatusListener {
80
81     private final Logger logger = LoggerFactory.getLogger(DeviceHandler.class);
82
83     /**
84      * Contains all supported thing types of this handler, will be filled by DsDeviceThingTypeProvider.
85      */
86     public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = new HashSet<>();
87
88     public static final String TWO_STAGE_SWITCH_IDENTICATOR = "2";
89     public static final String THREE_STAGE_SWITCH_IDENTICATOR = "3";
90
91     private String dSID;
92     private Device device;
93     private BridgeHandler dssBridgeHandler;
94
95     private Command lastComand;
96     private String currentChannel;
97     private List<String> loadedSensorChannels;
98
99     /**
100      * Creates a new {@link DeviceHandler}.
101      *
102      * @param thing must not be null
103      */
104     public DeviceHandler(Thing thing) {
105         super(thing);
106     }
107
108     @Override
109     public void initialize() {
110         logger.debug("Initializing DeviceHandler.");
111         dSID = (String) getConfig().get(DigitalSTROMBindingConstants.DEVICE_DSID);
112         if (dSID != null && !dSID.isBlank()) {
113             final Bridge bridge = getBridge();
114             if (bridge != null) {
115                 bridgeStatusChanged(bridge.getStatusInfo());
116             } else {
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!");
120             }
121         } else {
122             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "dSID is missing");
123         }
124     }
125
126     @Override
127     public void dispose() {
128         logger.debug("Handler disposed... unregister DeviceStatusListener");
129         if (dSID != null) {
130             if (dssBridgeHandler != null) {
131                 dssBridgeHandler.unregisterDeviceStatusListener(this);
132             }
133         }
134         if (device != null) {
135             device.setSensorDataRefreshPriority(Config.REFRESH_PRIORITY_NEVER, Config.REFRESH_PRIORITY_NEVER,
136                     Config.REFRESH_PRIORITY_NEVER);
137         }
138         device = null;
139     }
140
141     @Override
142     public void handleRemoval() {
143         if (getDssBridgeHandler() != null) {
144             this.dssBridgeHandler.childThingRemoved(dSID);
145         }
146         updateStatus(ThingStatus.REMOVED);
147     }
148
149     @Override
150     public void thingUpdated(Thing thing) {
151         this.thing = thing;
152         if (device == null) {
153             initialize();
154         }
155     }
156
157     @Override
158     public void bridgeStatusChanged(ThingStatusInfo bridgeStatusInfo) {
159         if (bridgeStatusInfo.getStatus().equals(ThingStatus.ONLINE)) {
160             if (dSID != null) {
161                 if (getDssBridgeHandler() != null) {
162                     if (device == null) {
163                         updateStatus(ThingStatus.ONLINE, ThingStatusDetail.CONFIGURATION_PENDING,
164                                 "waiting for listener registration");
165                         dssBridgeHandler.registerDeviceStatusListener(this);
166                     } else {
167                         updateStatus(ThingStatus.ONLINE);
168                     }
169                 } else {
170                     updateStatus(ThingStatus.OFFLINE);
171                 }
172             } else {
173                 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "No dSID is set!");
174             }
175         }
176         if (bridgeStatusInfo.getStatus().equals(ThingStatus.OFFLINE)) {
177             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE);
178         }
179         logger.debug("Set status to {}", getThing().getStatusInfo());
180     }
181
182     @Override
183     public void handleCommand(ChannelUID channelUID, Command command) {
184         BridgeHandler dssBridgeHandler = getDssBridgeHandler();
185         if (dssBridgeHandler == null) {
186             logger.debug("BridgeHandler not found. Cannot handle command without bridge.");
187             return;
188         }
189
190         if (device == null) {
191             logger.debug(
192                     "Device not known on StructureManager or DeviceStatusListener is not registerd. Cannot handle command.");
193             return;
194         }
195
196         if (command instanceof RefreshType) {
197             try {
198                 SensorEnum sensorType = SensorEnum.valueOf(channelUID.getId());
199                 dssBridgeHandler.sendComandsToDSS(device, new DeviceStateUpdateImpl(sensorType, 1));
200             } catch (IllegalArgumentException e) {
201                 dssBridgeHandler.sendComandsToDSS(device,
202                         new DeviceStateUpdateImpl(DeviceStateUpdate.REFRESH_OUTPUT, 0));
203             }
204         } else if (!device.isShade()) {
205             if (DsChannelTypeProvider.isOutputChannel(channelUID.getId())) {
206                 if (command instanceof PercentType) {
207                     device.setOutputValue(
208                             (short) fromPercentToValue(((PercentType) command).intValue(), device.getMaxOutputValue()));
209                 } else if (command instanceof OnOffType) {
210                     if (OnOffType.ON.equals(command)) {
211                         device.setIsOn(true);
212                     } else {
213                         device.setIsOn(false);
214                     }
215                 } else if (command instanceof IncreaseDecreaseType) {
216                     if (IncreaseDecreaseType.INCREASE.equals(command)) {
217                         device.increase();
218                     } else {
219                         device.decrease();
220                     }
221                 } else if (command instanceof StringType) {
222                     device.setOutputValue(Short.parseShort(((StringType) command).toString()));
223                 }
224             } else {
225                 logger.debug("Command sent to an unknown channel id: {}", channelUID);
226             }
227         } else {
228             if (channelUID.getId().contains(DsChannelTypeProvider.ANGLE)) {
229                 if (command instanceof PercentType) {
230                     device.setAnglePosition(
231                             (short) fromPercentToValue(((PercentType) command).intValue(), device.getMaxSlatAngle()));
232                 } else if (command instanceof OnOffType) {
233                     if (OnOffType.ON.equals(command)) {
234                         device.setAnglePosition(device.getMaxSlatAngle());
235                     } else {
236                         device.setAnglePosition(device.getMinSlatAngle());
237                     }
238                 } else if (command instanceof IncreaseDecreaseType) {
239                     if (IncreaseDecreaseType.INCREASE.equals(command)) {
240                         device.increaseSlatAngle();
241                     } else {
242                         device.decreaseSlatAngle();
243                     }
244                 }
245             } else if (channelUID.getId().contains(DsChannelTypeProvider.SHADE)) {
246                 if (command instanceof PercentType) {
247                     int percent = ((PercentType) command).intValue();
248                     if (!device.getHWinfo().equals("GR-KL200")) {
249                         percent = 100 - percent;
250                     }
251                     device.setSlatPosition(fromPercentToValue(percent, device.getMaxSlatPosition()));
252                     this.lastComand = command;
253                 } else if (command instanceof StopMoveType) {
254                     if (StopMoveType.MOVE.equals(command)) {
255                         handleCommand(channelUID, this.lastComand);
256                     } else {
257                         dssBridgeHandler.stopOutputValue(device);
258                     }
259                 } else if (command instanceof UpDownType) {
260                     if (UpDownType.UP.equals(command)) {
261                         device.setIsOpen(true);
262                         this.lastComand = command;
263                     } else {
264                         device.setIsOpen(false);
265                         this.lastComand = command;
266                     }
267                 }
268             } else {
269                 logger.debug("Command sent to an unknown channel id: {}", channelUID);
270             }
271         }
272     }
273
274     private int fromPercentToValue(int percent, int max) {
275         if (percent < 0 || percent == 0) {
276             return 0;
277         }
278         if (max < 0 || max == 0) {
279             return 0;
280         }
281         return (int) (max * ((float) percent / 100));
282     }
283
284     private synchronized BridgeHandler getDssBridgeHandler() {
285         if (this.dssBridgeHandler == null) {
286             Bridge bridge = getBridge();
287             if (bridge == null) {
288                 logger.debug("Bride cannot be found");
289                 return null;
290             }
291             ThingHandler handler = bridge.getHandler();
292
293             if (handler instanceof BridgeHandler) {
294                 dssBridgeHandler = (BridgeHandler) handler;
295             } else {
296                 return null;
297             }
298         }
299         return dssBridgeHandler;
300     }
301
302     private boolean sensorChannelsLoaded() {
303         return loadedSensorChannels != null && !loadedSensorChannels.isEmpty();
304     }
305
306     @Override
307     public synchronized void onDeviceStateChanged(DeviceStateUpdate deviceStateUpdate) {
308         if (device != null) {
309             if (deviceStateUpdate != null) {
310                 if (sensorChannelsLoaded()) {
311                     if (deviceStateUpdate.isSensorUpdateType()) {
312                         updateState(getSensorChannelID(deviceStateUpdate.getTypeAsSensorEnum()),
313                                 new DecimalType(deviceStateUpdate.getValueAsFloat()));
314                         logger.debug("Update state");
315                         return;
316                     }
317                     if (deviceStateUpdate.isBinarayInputType()) {
318                         if (deviceStateUpdate.getValueAsShort() == 1) {
319                             updateState(getBinaryInputChannelID(deviceStateUpdate.getTypeAsDeviceBinarayInputEnum()),
320                                     OnOffType.ON);
321                         } else {
322                             updateState(getBinaryInputChannelID(deviceStateUpdate.getTypeAsDeviceBinarayInputEnum()),
323                                     OnOffType.OFF);
324                         }
325                     }
326                 }
327                 if (!device.isShade()) {
328                     if (currentChannel != null) {
329                         switch (deviceStateUpdate.getType()) {
330                             case DeviceStateUpdate.OUTPUT_DECREASE:
331                             case DeviceStateUpdate.OUTPUT_INCREASE:
332                             case DeviceStateUpdate.OUTPUT:
333                                 if (currentChannel.contains(DsChannelTypeProvider.DIMMER)) {
334                                     if (deviceStateUpdate.getValueAsInteger() > 0) {
335                                         updateState(currentChannel, new PercentType(fromValueToPercent(
336                                                 deviceStateUpdate.getValueAsInteger(), device.getMaxOutputValue())));
337                                     } else {
338                                         updateState(currentChannel, OnOffType.OFF);
339                                     }
340                                 } else if (currentChannel.contains(DsChannelTypeProvider.STAGE)) {
341                                     if (currentChannel.contains(TWO_STAGE_SWITCH_IDENTICATOR)) {
342                                         updateState(currentChannel,
343                                                 new StringType(convertStageValue((short) 2, device.getOutputValue())));
344                                     } else {
345                                         updateState(currentChannel,
346                                                 new StringType(convertStageValue((short) 3, device.getOutputValue())));
347                                     }
348                                 }
349                                 break;
350                             case DeviceStateUpdate.ON_OFF:
351                                 if (currentChannel.contains(DsChannelTypeProvider.STAGE)) {
352                                     onDeviceStateChanged(new DeviceStateUpdateImpl(DeviceStateUpdate.OUTPUT,
353                                             device.getOutputValue()));
354                                 }
355                                 if (deviceStateUpdate.getValueAsInteger() > 0) {
356                                     updateState(currentChannel, OnOffType.ON);
357                                 } else {
358                                     updateState(currentChannel, OnOffType.OFF);
359                                 }
360                                 break;
361                             default:
362                                 return;
363                         }
364                     }
365                 } else {
366                     int percent = 0;
367                     switch (deviceStateUpdate.getType()) {
368                         case DeviceStateUpdate.SLAT_DECREASE:
369                         case DeviceStateUpdate.SLAT_INCREASE:
370                         case DeviceStateUpdate.SLATPOSITION:
371                             percent = fromValueToPercent(deviceStateUpdate.getValueAsInteger(),
372                                     device.getMaxSlatPosition());
373                             break;
374                         case DeviceStateUpdate.OPEN_CLOSE:
375                             if (deviceStateUpdate.getValueAsInteger() > 0) {
376                                 percent = 100;
377                             }
378                             break;
379                         case DeviceStateUpdate.OPEN_CLOSE_ANGLE:
380                             if (device.isBlind() && currentChannel != null) {
381                                 if (deviceStateUpdate.getValueAsInteger() > 0) {
382                                     updateState(currentChannel, PercentType.HUNDRED);
383                                 } else {
384                                     updateState(currentChannel, PercentType.ZERO);
385                                 }
386                             }
387                             return;
388                         case DeviceStateUpdate.SLAT_ANGLE_DECREASE:
389                         case DeviceStateUpdate.SLAT_ANGLE_INCREASE:
390                         case DeviceStateUpdate.SLAT_ANGLE:
391                             if (device.isBlind() && currentChannel != null) {
392                                 updateState(currentChannel,
393                                         new PercentType(fromValueToPercent(deviceStateUpdate.getValueAsInteger(),
394                                                 device.getMaxSlatAngle())));
395                             }
396                             return;
397                         default:
398                             return;
399                     }
400                     if (!device.getHWinfo().equals("GR-KL210")) {
401                         percent = 100 - percent;
402                     }
403                     updateState(DsChannelTypeProvider.SHADE, new PercentType(percent));
404                 }
405                 logger.debug("Update state");
406             }
407         }
408     }
409
410     private int fromValueToPercent(int value, int max) {
411         if (value <= 0 || max <= 0) {
412             return 0;
413         }
414         int percentValue = new BigDecimal(value * ((float) 100 / max)).setScale(0, RoundingMode.HALF_UP).intValue();
415         return percentValue < 0 ? 0 : percentValue > 100 ? 100 : percentValue;
416     }
417
418     @Override
419     public synchronized void onDeviceRemoved(GeneralDeviceInformation device) {
420         if (device instanceof Device) {
421             this.device = (Device) device;
422             if (this.getThing().getStatus().equals(ThingStatus.ONLINE)) {
423                 if (!((Device) device).isPresent()) {
424                     updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.NONE,
425                             "Device is not present in the digitalSTROM-System.");
426                 } else {
427                     updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.NONE,
428                             "Device is not avaible in the digitalSTROM-System.");
429                 }
430
431             }
432             logger.debug("Set status to {}", getThing().getStatus());
433         }
434     }
435
436     @Override
437     public synchronized void onDeviceAdded(GeneralDeviceInformation device) {
438         if (device instanceof Device) {
439             this.device = (Device) device;
440             if (this.device.isPresent()) {
441                 ThingStatusInfo statusInfo = this.dssBridgeHandler.getThing().getStatusInfo();
442                 updateStatus(statusInfo.getStatus(), statusInfo.getStatusDetail(), statusInfo.getDescription());
443                 logger.debug("Set status to {}", getThing().getStatus());
444
445                 // load scene configurations persistently into the thing
446                 for (Short i : this.device.getSavedScenes()) {
447                     onSceneConfigAdded(i);
448                 }
449                 logger.debug("Load saved scene specification into device");
450                 this.device.saveConfigSceneSpecificationIntoDevice(getThing().getProperties());
451
452                 checkDeviceInfoProperties(this.device);
453                 // load sensor priorities into the device and load sensor channels of the thing
454                 if (!this.device.isShade()) {
455                     loadSensorChannels();
456                     // check and load output channel of the thing
457                     checkOutputChannel();
458                 } else if (this.device.isBlind()) {
459                     // load channel for set the angle of jalousie devices
460                     String channelTypeID = DsChannelTypeProvider.getOutputChannelTypeID(
461                             ((Device) device).getFunctionalColorGroup().getColor(), ((Device) device).getOutputMode(),
462                             ((Device) device).getOutputChannels());
463                     loadOutputChannel(new ChannelTypeUID(BINDING_ID, channelTypeID),
464                             DsChannelTypeProvider.getItemType(channelTypeID));
465                 }
466
467                 // load first channel values
468                 onDeviceStateInitial(this.device);
469                 return;
470             }
471         }
472         onDeviceRemoved(device);
473     }
474
475     /**
476      * Updates device info properties.
477      *
478      * @param device (must not be null)
479      */
480     private void checkDeviceInfoProperties(Device device) {
481         boolean propertiesChanged = false;
482         Map<String, String> properties = editProperties();
483         // check device info
484         if (device.getName() != null) {
485             properties.put(DigitalSTROMBindingConstants.DEVICE_NAME, device.getName());
486             propertiesChanged = true;
487         }
488         if (device.getDSUID() != null) {
489             properties.put(DigitalSTROMBindingConstants.DEVICE_UID, device.getDSUID());
490             propertiesChanged = true;
491         }
492         if (device.getHWinfo() != null) {
493             properties.put(DigitalSTROMBindingConstants.DEVICE_HW_INFO, device.getHWinfo());
494             propertiesChanged = true;
495         }
496         if (device.getZoneId() != -1) {
497             properties.put(DigitalSTROMBindingConstants.DEVICE_ZONE_ID, device.getZoneId() + "");
498             propertiesChanged = true;
499         }
500         if (device.getGroups() != null) {
501             properties.put(DigitalSTROMBindingConstants.DEVICE_GROUPS, device.getGroups().toString());
502             propertiesChanged = true;
503         }
504         if (device.getOutputMode() != null) {
505             properties.put(DigitalSTROMBindingConstants.DEVICE_OUTPUT_MODE, device.getOutputMode().toString());
506             propertiesChanged = true;
507         }
508         if (device.getFunctionalColorGroup() != null) {
509             properties.put(DigitalSTROMBindingConstants.DEVICE_FUNCTIONAL_COLOR_GROUP,
510                     device.getFunctionalColorGroup().toString());
511             propertiesChanged = true;
512         }
513         if (device.getMeterDSID() != null) {
514             properties.put(DigitalSTROMBindingConstants.DEVICE_METER_ID, device.getMeterDSID().toString());
515             propertiesChanged = true;
516         }
517         if (!device.getBinaryInputs().isEmpty()) {
518             properties.put(DigitalSTROMBindingConstants.DEVICE_BINARAY_INPUTS, getBinarayInputList());
519             propertiesChanged = true;
520         }
521         if (propertiesChanged) {
522             super.updateProperties(properties);
523             propertiesChanged = false;
524         }
525     }
526
527     private String getBinarayInputList() {
528         List<String> binarayInputs = new ArrayList<>(device.getBinaryInputs().size());
529         for (DeviceBinaryInput binInput : device.getBinaryInputs()) {
530             DeviceBinarayInputEnum devBinInp = DeviceBinarayInputEnum.getdeviceBinarayInput(binInput.getInputType());
531             if (devBinInp != null) {
532                 binarayInputs.add(devBinInp.toString().toLowerCase());
533             }
534         }
535         return binarayInputs.toString();
536     }
537
538     private void loadSensorChannels() {
539         if (device != null && device.isPresent()) {
540             // load sensor priorities into the device
541             boolean configChanged = false;
542             Configuration config = getThing().getConfiguration();
543             logger.debug("Add sensor priorities to the device");
544
545             String activePowerPrio = Config.REFRESH_PRIORITY_NEVER;
546             if (config.get(DigitalSTROMBindingConstants.ACTIVE_POWER_REFRESH_PRIORITY) != null) {
547                 activePowerPrio = config.get(DigitalSTROMBindingConstants.ACTIVE_POWER_REFRESH_PRIORITY).toString();
548             } else {
549                 config.put(DigitalSTROMBindingConstants.ACTIVE_POWER_REFRESH_PRIORITY, Config.REFRESH_PRIORITY_NEVER);
550                 configChanged = true;
551             }
552             // By devices with output mode WIPE the active power always will be read out to check, if the device is not
553             // in standby any more.
554             if (OutputModeEnum.WIPE.equals(device.getOutputMode())
555                     && activePowerPrio.equals(Config.REFRESH_PRIORITY_NEVER)) {
556                 config.put(DigitalSTROMBindingConstants.ACTIVE_POWER_REFRESH_PRIORITY, Config.REFRESH_PRIORITY_LOW);
557                 configChanged = true;
558             }
559
560             String outputCurrentPrio = Config.REFRESH_PRIORITY_NEVER;
561             if (config.get(DigitalSTROMBindingConstants.OUTPUT_CURRENT_REFRESH_PRIORITY) != null) {
562                 outputCurrentPrio = config.get(DigitalSTROMBindingConstants.OUTPUT_CURRENT_REFRESH_PRIORITY).toString();
563             } else {
564                 config.put(DigitalSTROMBindingConstants.OUTPUT_CURRENT_REFRESH_PRIORITY, Config.REFRESH_PRIORITY_NEVER);
565                 configChanged = true;
566             }
567
568             String electricMeterPrio = Config.REFRESH_PRIORITY_NEVER;
569             if (config.get(DigitalSTROMBindingConstants.ELECTRIC_METER_REFRESH_PRIORITY) != null) {
570                 electricMeterPrio = config.get(DigitalSTROMBindingConstants.ELECTRIC_METER_REFRESH_PRIORITY).toString();
571             } else {
572                 config.put(DigitalSTROMBindingConstants.ELECTRIC_METER_REFRESH_PRIORITY, Config.REFRESH_PRIORITY_NEVER);
573                 configChanged = true;
574             }
575
576             if (configChanged) {
577                 super.updateConfiguration(config);
578                 configChanged = false;
579             }
580
581             device.setSensorDataRefreshPriority(activePowerPrio, electricMeterPrio, outputCurrentPrio);
582             logger.debug(
583                     "add sensor prioritys: active power = {}, output current = {}, electric meter = {} to device with id {}",
584                     activePowerPrio, outputCurrentPrio, electricMeterPrio, device.getDSID());
585
586             // check and load sensor channels of the thing
587             checkSensorChannel();
588         }
589     }
590
591     private boolean addLoadedSensorChannel(String sensorChannelType) {
592         if (loadedSensorChannels == null) {
593             loadedSensorChannels = new LinkedList<>();
594         }
595         if (!loadedSensorChannels.contains(sensorChannelType.toString())) {
596             return loadedSensorChannels.add(sensorChannelType.toString());
597         }
598         return false;
599     }
600
601     private boolean removeLoadedSensorChannel(String sensorChannelType) {
602         if (loadedSensorChannels == null) {
603             return false;
604         }
605         return loadedSensorChannels.remove(sensorChannelType);
606     }
607
608     private boolean isSensorChannelLoaded(String sensorChannelType) {
609         if (loadedSensorChannels == null) {
610             return false;
611         }
612         return loadedSensorChannels.contains(sensorChannelType);
613     }
614
615     private void checkSensorChannel() {
616         List<Channel> channelList = new LinkedList<>(this.getThing().getChannels());
617
618         boolean channelListChanged = false;
619
620         // if sensor channels with priority never are loaded delete these channels
621         if (!channelList.isEmpty()) {
622             Iterator<Channel> channelInter = channelList.iterator();
623             while (channelInter.hasNext()) {
624                 Channel channel = channelInter.next();
625                 String channelID = channel.getUID().getId();
626                 if (channelID.startsWith(DsChannelTypeProvider.BINARY_INPUT_PRE)) {
627                     DeviceBinarayInputEnum devBinInput = getBinaryInput(channelID);
628                     if (device.getBinaryInput(devBinInput) != null) {
629                         addLoadedSensorChannel(channelID);
630                     } else {
631                         logger.debug("remove {} binary input channel", channelID);
632                         channelInter.remove();
633                         channelListChanged = removeLoadedSensorChannel(channelID);
634                     }
635                 } else {
636                     SensorEnum sensorType = getSensorEnum(channelID);
637                     if (sensorType != null) {
638                         if (SensorEnum.isPowerSensor(sensorType)) {
639                             if (device.checkPowerSensorRefreshPriorityNever(sensorType)) {
640                                 logger.debug("remove {} sensor channel", channelID);
641                                 channelInter.remove();
642                                 channelListChanged = removeLoadedSensorChannel(channelID);
643                             } else {
644                                 addLoadedSensorChannel(channelID);
645                             }
646                         } else {
647                             if (device.supportsSensorType(sensorType)) {
648                                 addLoadedSensorChannel(channelID);
649                             } else {
650                                 logger.debug("remove {} sensor channel", channelID);
651                                 channelInter.remove();
652                                 removeLoadedSensorChannel(channelID);
653                                 channelListChanged = true;
654                             }
655                         }
656                     }
657                 }
658             }
659         }
660         for (SensorEnum sensorType : device.getPowerSensorTypes()) {
661             if (!device.checkPowerSensorRefreshPriorityNever(sensorType)
662                     && !isSensorChannelLoaded(getSensorChannelID(sensorType))) {
663                 logger.debug("create {} sensor channel", sensorType.toString());
664                 channelList.add(getSensorChannel(sensorType));
665                 channelListChanged = addLoadedSensorChannel(getSensorChannelID(sensorType));
666             }
667         }
668         if (device.hasClimateSensors()) {
669             for (SensorEnum sensorType : device.getClimateSensorTypes()) {
670                 if (!isSensorChannelLoaded(getSensorChannelID(sensorType))) {
671                     logger.debug("create {} sensor channel", sensorType.toString());
672                     channelList.add(getSensorChannel(sensorType));
673                     channelListChanged = addLoadedSensorChannel(getSensorChannelID(sensorType));
674                 }
675             }
676         }
677         if (device.isBinaryInputDevice()) {
678             for (DeviceBinaryInput binInput : device.getBinaryInputs()) {
679                 DeviceBinarayInputEnum binInputType = DeviceBinarayInputEnum
680                         .getdeviceBinarayInput(binInput.getInputType());
681                 if (binInputType != null && !isSensorChannelLoaded(getBinaryInputChannelID(binInputType))) {
682                     logger.debug("create {} sensor channel", binInputType.toString());
683                     channelList.add(getBinaryChannel(binInputType));
684                     channelListChanged = addLoadedSensorChannel(getBinaryInputChannelID(binInputType));
685                 }
686             }
687         }
688
689         if (channelListChanged) {
690             logger.debug("load new channel list");
691             ThingBuilder thingBuilder = editThing();
692             thingBuilder.withChannels(channelList);
693             updateThing(thingBuilder.build());
694         }
695     }
696
697     private Channel getSensorChannel(SensorEnum sensorType) {
698         return ChannelBuilder.create(getSensorChannelUID(sensorType), "Number")
699                 .withType(DsChannelTypeProvider.getSensorChannelUID(sensorType)).build();
700     }
701
702     private Channel getBinaryChannel(DeviceBinarayInputEnum binaryInputType) {
703         return ChannelBuilder.create(getBinaryInputChannelUID(binaryInputType), "Switch")
704                 .withType(DsChannelTypeProvider.getBinaryInputChannelUID(binaryInputType)).build();
705     }
706
707     private void checkOutputChannel() {
708         if (device == null) {
709             logger.debug("Can not load a channel without a device!");
710             return;
711         }
712         // if the device have no output channel or it is disabled all output channels will be deleted
713         if (!device.isDeviceWithOutput()) {
714             loadOutputChannel(null, null);
715         }
716         String channelTypeID = DsChannelTypeProvider.getOutputChannelTypeID(device.getFunctionalColorGroup().getColor(),
717                 device.getOutputMode(), device.getOutputChannels());
718         logger.debug("load channel: typeID={}, itemType={}",
719                 DsChannelTypeProvider.getOutputChannelTypeID(device.getFunctionalColorGroup().getColor(),
720                         device.getOutputMode(), device.getOutputChannels()),
721                 DsChannelTypeProvider.getItemType(channelTypeID));
722         if (channelTypeID != null && (currentChannel == null || !currentChannel.equals(channelTypeID))) {
723             loadOutputChannel(new ChannelTypeUID(BINDING_ID, channelTypeID),
724                     DsChannelTypeProvider.getItemType(channelTypeID));
725         }
726     }
727
728     private void loadOutputChannel(ChannelTypeUID channelTypeUID, String acceptedItemType) {
729         if (channelTypeUID == null || acceptedItemType == null) {
730             return;
731         }
732         currentChannel = channelTypeUID.getId();
733
734         List<Channel> channelList = new LinkedList<>(this.getThing().getChannels());
735         boolean channelIsAlreadyLoaded = false;
736         boolean channelListChanged = false;
737
738         if (!channelList.isEmpty()) {
739             Iterator<Channel> channelInter = channelList.iterator();
740             while (channelInter.hasNext()) {
741                 Channel channel = channelInter.next();
742                 if (DsChannelTypeProvider.isOutputChannel(channel.getUID().getId())) {
743                     if (!channel.getUID().getId().equals(currentChannel)
744                             && !(device.isShade() && channel.getUID().getId().equals(DsChannelTypeProvider.SHADE))) {
745                         channelInter.remove();
746                         channelListChanged = true;
747                     } else {
748                         if (!channel.getUID().getId().equals(DsChannelTypeProvider.SHADE)) {
749                             channelIsAlreadyLoaded = true;
750                         }
751                     }
752                 }
753             }
754         }
755
756         if (!channelIsAlreadyLoaded && currentChannel != null) {
757             Channel channel = ChannelBuilder
758                     .create(new ChannelUID(this.getThing().getUID(), channelTypeUID.getId()), acceptedItemType)
759                     .withType(channelTypeUID).build();
760             channelList.add(channel);
761             channelListChanged = true;
762         }
763
764         if (channelListChanged) {
765             ThingBuilder thingBuilder = editThing();
766             thingBuilder.withChannels(channelList);
767             updateThing(thingBuilder.build());
768             logger.debug("load channel: {} with item: {}", channelTypeUID.getAsString(), acceptedItemType);
769         }
770     }
771
772     private ChannelUID getSensorChannelUID(SensorEnum sensorType) {
773         return new ChannelUID(getThing().getUID(), getSensorChannelID(sensorType));
774     }
775
776     private String getSensorChannelID(SensorEnum sensorType) {
777         return sensorType.toString().toLowerCase();
778     }
779
780     private ChannelUID getBinaryInputChannelUID(DeviceBinarayInputEnum binaryInputType) {
781         return new ChannelUID(getThing().getUID(), getBinaryInputChannelID(binaryInputType));
782     }
783
784     private String getBinaryInputChannelID(DeviceBinarayInputEnum binaryInputType) {
785         return DsChannelTypeProvider.BINARY_INPUT_PRE + binaryInputType.toString().toLowerCase();
786     }
787
788     private DeviceBinarayInputEnum getBinaryInput(String channelID) {
789         try {
790             return DeviceBinarayInputEnum
791                     .valueOf(channelID.replace(DsChannelTypeProvider.BINARY_INPUT_PRE, "").toUpperCase());
792         } catch (IllegalArgumentException e) {
793             return null;
794         }
795     }
796
797     private SensorEnum getSensorEnum(String channelID) {
798         try {
799             return SensorEnum.valueOf(channelID.toUpperCase());
800         } catch (IllegalArgumentException e) {
801             return null;
802         }
803     }
804
805     @Override
806     public void channelLinked(ChannelUID channelUID) {
807         if (device != null) {
808             SensorEnum sensorType = getSensorEnum(channelUID.getId());
809             if (sensorType != null) {
810                 Float val = device.getFloatSensorValue(sensorType);
811                 if (val != null) {
812                     updateState(channelUID, new DecimalType(val));
813                 }
814             }
815             Short val = device.getBinaryInputState(getBinaryInput(channelUID.getId()));
816             if (val != null) {
817                 if (val == 1) {
818                     updateState(channelUID, OnOffType.ON);
819                 } else {
820                     updateState(channelUID, OnOffType.OFF);
821                 }
822             }
823
824             if (channelUID.getId().contains(DsChannelTypeProvider.DIMMER)) {
825                 if (device.isOn()) {
826                     updateState(channelUID,
827                             new PercentType(fromValueToPercent(device.getOutputValue(), device.getMaxOutputValue())));
828                 } else {
829                     updateState(channelUID, new PercentType(0));
830                 }
831                 return;
832             }
833             if (channelUID.getId().contains(DsChannelTypeProvider.SWITCH)) {
834                 if (device.isOn()) {
835                     updateState(channelUID, OnOffType.ON);
836                 } else {
837                     updateState(channelUID, OnOffType.OFF);
838                 }
839                 return;
840             }
841             if (channelUID.getId().contains(DsChannelTypeProvider.SHADE)) {
842                 updateState(channelUID,
843                         new PercentType(fromValueToPercent(device.getSlatPosition(), device.getMaxSlatPosition())));
844                 return;
845             }
846             if (channelUID.getId().contains(DsChannelTypeProvider.ANGLE)) {
847                 updateState(channelUID,
848                         new PercentType(fromValueToPercent(device.getAnglePosition(), device.getMaxSlatAngle())));
849                 return;
850             }
851             if (channelUID.getId().contains(DsChannelTypeProvider.STAGE)) {
852                 if (channelUID.getId().contains(TWO_STAGE_SWITCH_IDENTICATOR)) {
853                     updateState(channelUID, new StringType(convertStageValue((short) 2, device.getOutputValue())));
854                     return;
855                 }
856                 if (channelUID.getId().contains(THREE_STAGE_SWITCH_IDENTICATOR)) {
857                     updateState(channelUID, new StringType(convertStageValue((short) 3, device.getOutputValue())));
858                     return;
859                 }
860             }
861         }
862     }
863
864     private String convertStageValue(short stage, short value) {
865         switch (stage) {
866             case 2:
867                 if (value < 85) {
868                     return OPTION_COMBINED_BOTH_OFF;
869                 } else if (value >= 85 && value < 170) {
870                     return OPTION_COMBINED_FIRST_ON;
871                 } else if (value >= 170 && value <= 255) {
872                     return OPTION_COMBINED_BOTH_ON;
873                 }
874             case 3:
875                 if (value < 64) {
876                     return OPTION_COMBINED_BOTH_OFF;
877                 } else if (value >= 64 && value < 128) {
878                     return OPTION_COMBINED_FIRST_ON;
879                 } else if (value >= 128 && value < 192) {
880                     return OPTION_COMBINED_SECOND_ON;
881                 } else if (value >= 192 && value <= 255) {
882                     return OPTION_COMBINED_BOTH_ON;
883                 }
884         }
885         return null;
886     }
887
888     private void onDeviceStateInitial(Device device) {
889         if (device != null) {
890             if (currentChannel != null) {
891                 if (isLinked(currentChannel)) {
892                     channelLinked(new ChannelUID(getThing().getUID(), currentChannel));
893                 }
894             }
895             if (!device.isShade()) {
896                 if (loadedSensorChannels != null) {
897                     for (String sensor : loadedSensorChannels) {
898                         Channel channel = getThing().getChannel(sensor);
899                         if (channel != null && isLinked(sensor)) {
900                             channelLinked(channel.getUID());
901                         }
902                     }
903                 }
904             } else {
905                 if (isLinked(DsChannelTypeProvider.SHADE)) {
906                     channelLinked(new ChannelUID(getThing().getUID(), DsChannelTypeProvider.SHADE));
907                 }
908             }
909         }
910     }
911
912     @Override
913     public synchronized void onSceneConfigAdded(short sceneId) {
914         if (device != null) {
915             String saveScene = "";
916             DeviceSceneSpec sceneSpec = device.getSceneConfig(sceneId);
917             if (sceneSpec != null) {
918                 saveScene = sceneSpec.toString();
919             }
920
921             Integer[] sceneValue = device.getSceneOutputValue(sceneId);
922             if (sceneValue[GeneralLibConstance.SCENE_ARRAY_INDEX_VALUE] != -1) {
923                 saveScene = saveScene + ", sceneValue: " + sceneValue[0];
924             }
925             if (sceneValue[GeneralLibConstance.SCENE_ARRAY_INDEX_ANGLE] != -1) {
926                 saveScene = saveScene + ", sceneAngle: " + sceneValue[1];
927             }
928             String key = DigitalSTROMBindingConstants.DEVICE_SCENE + sceneId;
929             if (!saveScene.isEmpty()) {
930                 logger.debug("Save scene configuration: [{}] to thing with UID {}", saveScene, getThing().getUID());
931                 super.updateProperty(key, saveScene);
932                 // persist the new property
933                 // super.updateThing(editThing().build());
934             }
935         }
936     }
937
938     @Override
939     public void onDeviceConfigChanged(ChangeableDeviceConfigEnum whichConfig) {
940         if (whichConfig != null) {
941             switch (whichConfig) {
942                 case DEVICE_NAME:
943                     super.updateProperty(DEVICE_NAME, device.getName());
944                     break;
945                 case METER_DSID:
946                     super.updateProperty(DEVICE_METER_ID, device.getMeterDSID().getValue());
947                     break;
948                 case ZONE_ID:
949                     super.updateProperty(DEVICE_ZONE_ID, device.getZoneId() + "");
950                     break;
951                 case GROUPS:
952                     super.updateProperty(DEVICE_GROUPS, device.getGroups().toString());
953                     break;
954                 case FUNCTIONAL_GROUP:
955                     super.updateProperty(DEVICE_FUNCTIONAL_COLOR_GROUP, device.getFunctionalColorGroup().toString());
956                     checkOutputChannel();
957                     break;
958                 case OUTPUT_MODE:
959                     super.updateProperty(DEVICE_OUTPUT_MODE, device.getOutputMode().toString());
960                     checkOutputChannel();
961                     break;
962                 case BINARY_INPUTS:
963                     super.updateProperty(DEVICE_BINARAY_INPUTS, getBinarayInputList());
964                     checkSensorChannel();
965                     break;
966                 default:
967                     break;
968             }
969         }
970     }
971
972     @Override
973     public String getDeviceStatusListenerID() {
974         return this.dSID;
975     }
976 }