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