]> git.basschouten.com Git - openhab-addons.git/blob
ee37d31a2d4892f6557bcf86402361ad2ed04d35
[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.util.ArrayList;
19 import java.util.HashSet;
20 import java.util.Iterator;
21 import java.util.LinkedList;
22 import java.util.List;
23 import java.util.Map;
24 import java.util.Set;
25
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;
66
67 /**
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>
70  * <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}.
74  *
75  * @author Michael Ochel - Initial contribution
76  * @author Matthias Siegele - Initial contribution
77  */
78 public class DeviceHandler extends BaseThingHandler implements DeviceStatusListener {
79
80     private final Logger logger = LoggerFactory.getLogger(DeviceHandler.class);
81
82     /**
83      * Contains all supported thing types of this handler, will be filled by DsDeviceThingTypeProvider.
84      */
85     public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = new HashSet<>();
86
87     public static final String TWO_STAGE_SWITCH_IDENTICATOR = "2";
88     public static final String THREE_STAGE_SWITCH_IDENTICATOR = "3";
89
90     private String dSID;
91     private Device device;
92     private BridgeHandler dssBridgeHandler;
93
94     private Command lastComand;
95     private String currentChannel;
96     private List<String> loadedSensorChannels;
97
98     /**
99      * Creates a new {@link DeviceHandler}.
100      *
101      * @param thing must not be null
102      */
103     public DeviceHandler(Thing thing) {
104         super(thing);
105     }
106
107     @Override
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());
115             } else {
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!");
119             }
120         } else {
121             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "dSID is missing");
122         }
123     }
124
125     @Override
126     public void dispose() {
127         logger.debug("Handler disposed... unregister DeviceStatusListener");
128         if (dSID != null) {
129             if (dssBridgeHandler != null) {
130                 dssBridgeHandler.unregisterDeviceStatusListener(this);
131             }
132         }
133         if (device != null) {
134             device.setSensorDataRefreshPriority(Config.REFRESH_PRIORITY_NEVER, Config.REFRESH_PRIORITY_NEVER,
135                     Config.REFRESH_PRIORITY_NEVER);
136         }
137         device = null;
138     }
139
140     @Override
141     public void handleRemoval() {
142         if (getDssBridgeHandler() != null) {
143             this.dssBridgeHandler.childThingRemoved(dSID);
144         }
145         updateStatus(ThingStatus.REMOVED);
146     }
147
148     @Override
149     public void thingUpdated(Thing thing) {
150         this.thing = thing;
151         if (device == null) {
152             initialize();
153         }
154     }
155
156     @Override
157     public void bridgeStatusChanged(ThingStatusInfo bridgeStatusInfo) {
158         if (bridgeStatusInfo.getStatus().equals(ThingStatus.ONLINE)) {
159             if (dSID != null) {
160                 if (getDssBridgeHandler() != null) {
161                     if (device == null) {
162                         updateStatus(ThingStatus.ONLINE, ThingStatusDetail.CONFIGURATION_PENDING,
163                                 "waiting for listener registration");
164                         dssBridgeHandler.registerDeviceStatusListener(this);
165                     } else {
166                         updateStatus(ThingStatus.ONLINE);
167                     }
168                 } else {
169                     updateStatus(ThingStatus.OFFLINE);
170                 }
171             } else {
172                 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "No dSID is set!");
173             }
174         }
175         if (bridgeStatusInfo.getStatus().equals(ThingStatus.OFFLINE)) {
176             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE);
177         }
178         logger.debug("Set status to {}", getThing().getStatusInfo());
179     }
180
181     @Override
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.");
186             return;
187         }
188
189         if (device == null) {
190             logger.debug(
191                     "Device not known on StructureManager or DeviceStatusListener is not registerd. Cannot handle command.");
192             return;
193         }
194
195         if (command instanceof RefreshType) {
196             try {
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));
202             }
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);
211                     } else {
212                         device.setIsOn(false);
213                     }
214                 } else if (command instanceof IncreaseDecreaseType) {
215                     if (IncreaseDecreaseType.INCREASE.equals(command)) {
216                         device.increase();
217                     } else {
218                         device.decrease();
219                     }
220                 } else if (command instanceof StringType) {
221                     device.setOutputValue(Short.parseShort(((StringType) command).toString()));
222                 }
223             } else {
224                 logger.debug("Command sent to an unknown channel id: {}", channelUID);
225             }
226         } else {
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());
234                     } else {
235                         device.setAnglePosition(device.getMinSlatAngle());
236                     }
237                 } else if (command instanceof IncreaseDecreaseType) {
238                     if (IncreaseDecreaseType.INCREASE.equals(command)) {
239                         device.increaseSlatAngle();
240                     } else {
241                         device.decreaseSlatAngle();
242                     }
243                 }
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;
249                     }
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);
255                     } else {
256                         dssBridgeHandler.stopOutputValue(device);
257                     }
258                 } else if (command instanceof UpDownType) {
259                     if (UpDownType.UP.equals(command)) {
260                         device.setIsOpen(true);
261                         this.lastComand = command;
262                     } else {
263                         device.setIsOpen(false);
264                         this.lastComand = command;
265                     }
266                 }
267             } else {
268                 logger.debug("Command sent to an unknown channel id: {}", channelUID);
269             }
270         }
271     }
272
273     private int fromPercentToValue(int percent, int max) {
274         if (percent < 0 || percent == 0) {
275             return 0;
276         }
277         if (max < 0 || max == 0) {
278             return 0;
279         }
280         return (int) (max * ((float) percent / 100));
281     }
282
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");
288                 return null;
289             }
290             ThingHandler handler = bridge.getHandler();
291
292             if (handler instanceof BridgeHandler) {
293                 dssBridgeHandler = (BridgeHandler) handler;
294             } else {
295                 return null;
296             }
297         }
298         return dssBridgeHandler;
299     }
300
301     private boolean sensorChannelsLoaded() {
302         return loadedSensorChannels != null && !loadedSensorChannels.isEmpty();
303     }
304
305     @Override
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");
314                         return;
315                     }
316                     if (deviceStateUpdate.isBinarayInputType()) {
317                         if (deviceStateUpdate.getValueAsShort() == 1) {
318                             updateState(getBinaryInputChannelID(deviceStateUpdate.getTypeAsDeviceBinarayInputEnum()),
319                                     OnOffType.ON);
320                         } else {
321                             updateState(getBinaryInputChannelID(deviceStateUpdate.getTypeAsDeviceBinarayInputEnum()),
322                                     OnOffType.OFF);
323                         }
324                     }
325                 }
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())));
336                                     } else {
337                                         updateState(currentChannel, OnOffType.OFF);
338                                     }
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())));
343                                     } else {
344                                         updateState(currentChannel,
345                                                 new StringType(convertStageValue((short) 3, device.getOutputValue())));
346                                     }
347                                 }
348                                 break;
349                             case DeviceStateUpdate.ON_OFF:
350                                 if (currentChannel.contains(DsChannelTypeProvider.STAGE)) {
351                                     onDeviceStateChanged(new DeviceStateUpdateImpl(DeviceStateUpdate.OUTPUT,
352                                             device.getOutputValue()));
353                                 }
354                                 if (deviceStateUpdate.getValueAsInteger() > 0) {
355                                     updateState(currentChannel, OnOffType.ON);
356                                 } else {
357                                     updateState(currentChannel, OnOffType.OFF);
358                                 }
359                                 break;
360                             default:
361                                 return;
362                         }
363                     }
364                 } else {
365                     int percent = 0;
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());
372                             break;
373                         case DeviceStateUpdate.OPEN_CLOSE:
374                             if (deviceStateUpdate.getValueAsInteger() > 0) {
375                                 percent = 100;
376                             }
377                             break;
378                         case DeviceStateUpdate.OPEN_CLOSE_ANGLE:
379                             if (device.isBlind() && currentChannel != null) {
380                                 if (deviceStateUpdate.getValueAsInteger() > 0) {
381                                     updateState(currentChannel, PercentType.HUNDRED);
382                                 } else {
383                                     updateState(currentChannel, PercentType.ZERO);
384                                 }
385                             }
386                             return;
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())));
394                             }
395                             return;
396                         default:
397                             return;
398                     }
399                     if (!device.getHWinfo().equals("GR-KL210")) {
400                         percent = 100 - percent;
401                     }
402                     updateState(DsChannelTypeProvider.SHADE, new PercentType(percent));
403                 }
404                 logger.debug("Update state");
405             }
406         }
407     }
408
409     private int fromValueToPercent(int value, int max) {
410         if (value <= 0 || max <= 0) {
411             return 0;
412         }
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;
415     }
416
417     @Override
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.");
425                 } else {
426                     updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.NONE,
427                             "Device is not avaible in the digitalSTROM-System.");
428                 }
429
430             }
431             logger.debug("Set status to {}", getThing().getStatus());
432         }
433     }
434
435     @Override
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());
443
444                 // load scene configurations persistently into the thing
445                 for (Short i : this.device.getSavedScenes()) {
446                     onSceneConfigAdded(i);
447                 }
448                 logger.debug("Load saved scene specification into device");
449                 this.device.saveConfigSceneSpecificationIntoDevice(getThing().getProperties());
450
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));
463                 }
464
465                 // load first channel values
466                 onDeviceStateInitial(this.device);
467                 return;
468             }
469         }
470         onDeviceRemoved(device);
471     }
472
473     /**
474      * Updates device info properties.
475      *
476      * @param device (must not be null)
477      */
478     private void checkDeviceInfoProperties(Device device) {
479         boolean propertiesChanged = false;
480         Map<String, String> properties = editProperties();
481         // check device info
482         if (device.getName() != null) {
483             properties.put(DigitalSTROMBindingConstants.DEVICE_NAME, device.getName());
484             propertiesChanged = true;
485         }
486         if (device.getDSUID() != null) {
487             properties.put(DigitalSTROMBindingConstants.DEVICE_UID, device.getDSUID());
488             propertiesChanged = true;
489         }
490         if (device.getHWinfo() != null) {
491             properties.put(DigitalSTROMBindingConstants.DEVICE_HW_INFO, device.getHWinfo());
492             propertiesChanged = true;
493         }
494         if (device.getZoneId() != -1) {
495             properties.put(DigitalSTROMBindingConstants.DEVICE_ZONE_ID, device.getZoneId() + "");
496             propertiesChanged = true;
497         }
498         if (device.getGroups() != null) {
499             properties.put(DigitalSTROMBindingConstants.DEVICE_GROUPS, device.getGroups().toString());
500             propertiesChanged = true;
501         }
502         if (device.getOutputMode() != null) {
503             properties.put(DigitalSTROMBindingConstants.DEVICE_OUTPUT_MODE, device.getOutputMode().toString());
504             propertiesChanged = true;
505         }
506         if (device.getFunctionalColorGroup() != null) {
507             properties.put(DigitalSTROMBindingConstants.DEVICE_FUNCTIONAL_COLOR_GROUP,
508                     device.getFunctionalColorGroup().toString());
509             propertiesChanged = true;
510         }
511         if (device.getMeterDSID() != null) {
512             properties.put(DigitalSTROMBindingConstants.DEVICE_METER_ID, device.getMeterDSID().toString());
513             propertiesChanged = true;
514         }
515         if (!device.getBinaryInputs().isEmpty()) {
516             properties.put(DigitalSTROMBindingConstants.DEVICE_BINARAY_INPUTS, getBinarayInputList());
517             propertiesChanged = true;
518         }
519         if (propertiesChanged) {
520             super.updateProperties(properties);
521             propertiesChanged = false;
522         }
523     }
524
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());
531             }
532         }
533         return binarayInputs.toString();
534     }
535
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");
542
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();
546             } else {
547                 config.put(DigitalSTROMBindingConstants.ACTIVE_POWER_REFRESH_PRIORITY, Config.REFRESH_PRIORITY_NEVER);
548                 configChanged = true;
549             }
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;
556             }
557
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();
561             } else {
562                 config.put(DigitalSTROMBindingConstants.OUTPUT_CURRENT_REFRESH_PRIORITY, Config.REFRESH_PRIORITY_NEVER);
563                 configChanged = true;
564             }
565
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();
569             } else {
570                 config.put(DigitalSTROMBindingConstants.ELECTRIC_METER_REFRESH_PRIORITY, Config.REFRESH_PRIORITY_NEVER);
571                 configChanged = true;
572             }
573
574             if (configChanged) {
575                 super.updateConfiguration(config);
576                 configChanged = false;
577             }
578
579             device.setSensorDataRefreshPriority(activePowerPrio, electricMeterPrio, outputCurrentPrio);
580             logger.debug(
581                     "add sensor prioritys: active power = {}, output current = {}, electric meter = {} to device with id {}",
582                     activePowerPrio, outputCurrentPrio, electricMeterPrio, device.getDSID());
583
584             // check and load sensor channels of the thing
585             checkSensorChannel();
586         }
587     }
588
589     private boolean addLoadedSensorChannel(String sensorChannelType) {
590         if (loadedSensorChannels == null) {
591             loadedSensorChannels = new LinkedList<>();
592         }
593         if (!loadedSensorChannels.contains(sensorChannelType.toString())) {
594             return loadedSensorChannels.add(sensorChannelType.toString());
595         }
596         return false;
597     }
598
599     private boolean removeLoadedSensorChannel(String sensorChannelType) {
600         if (loadedSensorChannels == null) {
601             return false;
602         }
603         return loadedSensorChannels.remove(sensorChannelType);
604     }
605
606     private boolean isSensorChannelLoaded(String sensorChannelType) {
607         if (loadedSensorChannels == null) {
608             return false;
609         }
610         return loadedSensorChannels.contains(sensorChannelType);
611     }
612
613     private void checkSensorChannel() {
614         List<Channel> channelList = new LinkedList<>(this.getThing().getChannels());
615
616         boolean channelListChanged = false;
617
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);
628                     } else {
629                         logger.debug("remove {} binary input channel", channelID);
630                         channelInter.remove();
631                         channelListChanged = removeLoadedSensorChannel(channelID);
632                     }
633                 } else {
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);
641                             } else {
642                                 addLoadedSensorChannel(channelID);
643                             }
644                         } else {
645                             if (device.supportsSensorType(sensorType)) {
646                                 addLoadedSensorChannel(channelID);
647                             } else {
648                                 logger.debug("remove {} sensor channel", channelID);
649                                 channelInter.remove();
650                                 removeLoadedSensorChannel(channelID);
651                                 channelListChanged = true;
652                             }
653                         }
654                     }
655                 }
656             }
657         }
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));
664             }
665         }
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));
672                 }
673             }
674         }
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));
683                 }
684             }
685         }
686
687         if (channelListChanged) {
688             logger.debug("load new channel list");
689             ThingBuilder thingBuilder = editThing();
690             thingBuilder.withChannels(channelList);
691             updateThing(thingBuilder.build());
692         }
693     }
694
695     private Channel getSensorChannel(SensorEnum sensorType) {
696         return ChannelBuilder.create(getSensorChannelUID(sensorType), "Number")
697                 .withType(DsChannelTypeProvider.getSensorChannelUID(sensorType)).build();
698     }
699
700     private Channel getBinaryChannel(DeviceBinarayInputEnum binaryInputType) {
701         return ChannelBuilder.create(getBinaryInputChannelUID(binaryInputType), "Switch")
702                 .withType(DsChannelTypeProvider.getBinaryInputChannelUID(binaryInputType)).build();
703     }
704
705     private void checkOutputChannel() {
706         if (device == null) {
707             logger.debug("Can not load a channel without a device!");
708             return;
709         }
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);
713         }
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));
722         }
723     }
724
725     private void loadOutputChannel(ChannelTypeUID channelTypeUID, String acceptedItemType) {
726         if (channelTypeUID == null || acceptedItemType == null) {
727             return;
728         }
729         currentChannel = channelTypeUID.getId();
730
731         List<Channel> channelList = new LinkedList<>(this.getThing().getChannels());
732         boolean channelIsAlreadyLoaded = false;
733         boolean channelListChanged = false;
734
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;
744                     } else {
745                         if (!channel.getUID().getId().equals(DsChannelTypeProvider.SHADE)) {
746                             channelIsAlreadyLoaded = true;
747                         }
748                     }
749                 }
750             }
751         }
752
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;
759         }
760
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);
766         }
767     }
768
769     private ChannelUID getSensorChannelUID(SensorEnum sensorType) {
770         return new ChannelUID(getThing().getUID(), getSensorChannelID(sensorType));
771     }
772
773     private String getSensorChannelID(SensorEnum sensorType) {
774         return sensorType.toString().toLowerCase();
775     }
776
777     private ChannelUID getBinaryInputChannelUID(DeviceBinarayInputEnum binaryInputType) {
778         return new ChannelUID(getThing().getUID(), getBinaryInputChannelID(binaryInputType));
779     }
780
781     private String getBinaryInputChannelID(DeviceBinarayInputEnum binaryInputType) {
782         return DsChannelTypeProvider.BINARY_INPUT_PRE + binaryInputType.toString().toLowerCase();
783     }
784
785     private DeviceBinarayInputEnum getBinaryInput(String channelID) {
786         try {
787             return DeviceBinarayInputEnum
788                     .valueOf(channelID.replace(DsChannelTypeProvider.BINARY_INPUT_PRE, "").toUpperCase());
789         } catch (IllegalArgumentException e) {
790             return null;
791         }
792     }
793
794     private SensorEnum getSensorEnum(String channelID) {
795         try {
796             return SensorEnum.valueOf(channelID.toUpperCase());
797         } catch (IllegalArgumentException e) {
798             return null;
799         }
800     }
801
802     @Override
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);
808                 if (val != null) {
809                     updateState(channelUID, new DecimalType(val));
810                 }
811             }
812             Short val = device.getBinaryInputState(getBinaryInput(channelUID.getId()));
813             if (val != null) {
814                 if (val == 1) {
815                     updateState(channelUID, OnOffType.ON);
816                 } else {
817                     updateState(channelUID, OnOffType.OFF);
818                 }
819             }
820
821             if (channelUID.getId().contains(DsChannelTypeProvider.DIMMER)) {
822                 if (device.isOn()) {
823                     updateState(channelUID,
824                             new PercentType(fromValueToPercent(device.getOutputValue(), device.getMaxOutputValue())));
825                 } else {
826                     updateState(channelUID, new PercentType(0));
827                 }
828                 return;
829             }
830             if (channelUID.getId().contains(DsChannelTypeProvider.SWITCH)) {
831                 if (device.isOn()) {
832                     updateState(channelUID, OnOffType.ON);
833                 } else {
834                     updateState(channelUID, OnOffType.OFF);
835                 }
836                 return;
837             }
838             if (channelUID.getId().contains(DsChannelTypeProvider.SHADE)) {
839                 updateState(channelUID,
840                         new PercentType(fromValueToPercent(device.getSlatPosition(), device.getMaxSlatPosition())));
841                 return;
842             }
843             if (channelUID.getId().contains(DsChannelTypeProvider.ANGLE)) {
844                 updateState(channelUID,
845                         new PercentType(fromValueToPercent(device.getAnglePosition(), device.getMaxSlatAngle())));
846                 return;
847             }
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())));
851                     return;
852                 }
853                 if (channelUID.getId().contains(THREE_STAGE_SWITCH_IDENTICATOR)) {
854                     updateState(channelUID, new StringType(convertStageValue((short) 3, device.getOutputValue())));
855                     return;
856                 }
857             }
858         }
859     }
860
861     private String convertStageValue(short stage, short value) {
862         switch (stage) {
863             case 2:
864                 if (value < 85) {
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;
870                 }
871             case 3:
872                 if (value < 64) {
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;
880                 }
881         }
882         return null;
883     }
884
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));
890                 }
891             }
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());
898                         }
899                     }
900                 }
901             } else {
902                 if (isLinked(DsChannelTypeProvider.SHADE)) {
903                     channelLinked(new ChannelUID(getThing().getUID(), DsChannelTypeProvider.SHADE));
904                 }
905             }
906         }
907     }
908
909     @Override
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();
916             }
917
918             Integer[] sceneValue = device.getSceneOutputValue(sceneId);
919             if (sceneValue[GeneralLibConstance.SCENE_ARRAY_INDEX_VALUE] != -1) {
920                 saveScene = saveScene + ", sceneValue: " + sceneValue[0];
921             }
922             if (sceneValue[GeneralLibConstance.SCENE_ARRAY_INDEX_ANGLE] != -1) {
923                 saveScene = saveScene + ", sceneAngle: " + sceneValue[1];
924             }
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());
931             }
932         }
933     }
934
935     @Override
936     public void onDeviceConfigChanged(ChangeableDeviceConfigEnum whichConfig) {
937         if (whichConfig != null) {
938             switch (whichConfig) {
939                 case DEVICE_NAME:
940                     super.updateProperty(DEVICE_NAME, device.getName());
941                     break;
942                 case METER_DSID:
943                     super.updateProperty(DEVICE_METER_ID, device.getMeterDSID().getValue());
944                     break;
945                 case ZONE_ID:
946                     super.updateProperty(DEVICE_ZONE_ID, device.getZoneId() + "");
947                     break;
948                 case GROUPS:
949                     super.updateProperty(DEVICE_GROUPS, device.getGroups().toString());
950                     break;
951                 case FUNCTIONAL_GROUP:
952                     super.updateProperty(DEVICE_FUNCTIONAL_COLOR_GROUP, device.getFunctionalColorGroup().toString());
953                     checkOutputChannel();
954                     break;
955                 case OUTPUT_MODE:
956                     super.updateProperty(DEVICE_OUTPUT_MODE, device.getOutputMode().toString());
957                     checkOutputChannel();
958                     break;
959                 case BINARY_INPUTS:
960                     super.updateProperty(DEVICE_BINARAY_INPUTS, getBinarayInputList());
961                     checkSensorChannel();
962                     break;
963                 default:
964                     break;
965             }
966         }
967     }
968
969     @Override
970     public String getDeviceStatusListenerID() {
971         return this.dSID;
972     }
973 }