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