]> git.basschouten.com Git - openhab-addons.git/blob
3b35644eaf7bfcb4b286698c09edcbb16ce93459
[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.lib.structure.devices.impl;
14
15 import java.util.ArrayList;
16 import java.util.Collections;
17 import java.util.HashMap;
18 import java.util.HashSet;
19 import java.util.LinkedList;
20 import java.util.List;
21 import java.util.Map;
22 import java.util.Map.Entry;
23 import java.util.Set;
24
25 import org.apache.commons.lang.StringUtils;
26 import org.openhab.binding.digitalstrom.internal.DigitalSTROMBindingConstants;
27 import org.openhab.binding.digitalstrom.internal.lib.GeneralLibConstance;
28 import org.openhab.binding.digitalstrom.internal.lib.config.Config;
29 import org.openhab.binding.digitalstrom.internal.lib.event.types.EventItem;
30 import org.openhab.binding.digitalstrom.internal.lib.listener.DeviceStatusListener;
31 import org.openhab.binding.digitalstrom.internal.lib.serverconnection.constants.JSONApiResponseKeysEnum;
32 import org.openhab.binding.digitalstrom.internal.lib.structure.devices.AbstractGeneralDeviceInformations;
33 import org.openhab.binding.digitalstrom.internal.lib.structure.devices.Device;
34 import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.DeviceConstants;
35 import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.DeviceSceneSpec;
36 import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.DeviceStateUpdate;
37 import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.constants.ChangeableDeviceConfigEnum;
38 import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.constants.DeviceBinarayInputEnum;
39 import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.constants.FuncNameAndColorGroupEnum;
40 import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.constants.FunctionalColorGroupEnum;
41 import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.constants.OutputModeEnum;
42 import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.constants.SensorEnum;
43 import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.impl.DSID;
44 import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.impl.DeviceBinaryInput;
45 import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.impl.DeviceSensorValue;
46 import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.impl.DeviceStateUpdateImpl;
47 import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.impl.JSONDeviceSceneSpecImpl;
48 import org.openhab.binding.digitalstrom.internal.lib.structure.scene.InternalScene;
49 import org.openhab.binding.digitalstrom.internal.lib.structure.scene.constants.SceneEnum;
50 import org.slf4j.Logger;
51 import org.slf4j.LoggerFactory;
52
53 import com.google.gson.JsonArray;
54 import com.google.gson.JsonElement;
55 import com.google.gson.JsonObject;
56
57 /**
58  * The {@link DeviceImpl} is the implementation of the {@link Device}.
59  *
60  * @author Michael Ochel - Initial contribution
61  * @author Matthias Siegele - Initial contribution
62  */
63 public class DeviceImpl extends AbstractGeneralDeviceInformations implements Device {
64
65     private final Logger logger = LoggerFactory.getLogger(DeviceImpl.class);
66
67     private Config config;
68
69     private DSID meterDSID;
70     private int zoneId = 0;
71     private List<Short> groupList = new LinkedList<>();
72
73     private FunctionalColorGroupEnum functionalGroup;
74     private FuncNameAndColorGroupEnum functionalName;
75     private String hwInfo;
76
77     private OutputModeEnum outputMode;
78
79     private boolean isOn = false;
80     private boolean isOpen = true;
81     private short outputValue = 0;
82     private short maxOutputValue = DeviceConstants.DEFAULT_MAX_OUTPUTVALUE;
83     private short minOutputValue = 0;
84
85     private short slatAngle = 0;
86     private final short maxSlatAngle = DeviceConstants.MAX_SLAT_ANGLE;
87     private final short minSlatAngle = DeviceConstants.MIN_SLAT_ANGLE;
88
89     private int slatPosition = 0;
90     private final int maxSlatPosition = DeviceConstants.MAX_ROLLERSHUTTER;
91     private final int minSlatPosition = DeviceConstants.MIN_ROLLERSHUTTER;
92
93     private final List<DeviceSensorValue> deviceSensorValues = Collections.synchronizedList(new ArrayList<>());
94     private final List<DeviceBinaryInput> deviceBinaryInputs = Collections.synchronizedList(new ArrayList<>());
95     private final List<SensorEnum> devicePowerSensorTypes = new ArrayList<>();
96     private final List<SensorEnum> deviceClimateSensorTypes = new ArrayList<>();
97
98     // for scenes
99     private short activeSceneNumber = -1;
100     private InternalScene activeScene;
101     private InternalScene lastScene;
102     private int outputValueBeforeSceneCall = 0;
103     private short slatAngleBeforeSceneCall = 0;
104     private boolean lastCallWasUndo = false;
105
106     private final Map<Short, DeviceSceneSpec> sceneConfigMap = Collections.synchronizedMap(new HashMap<>());
107     private final Map<Short, Integer[]> sceneOutputMap = Collections.synchronizedMap(new HashMap<>());
108
109     // saves outstanding commands
110     private final List<DeviceStateUpdate> deviceStateUpdates = Collections.synchronizedList(new LinkedList<>());
111
112     /*
113      * Saves the refresh priorities and reading initialized flag of power sensors as an matrix.
114      * The first array fields are 0 = active power, 1 = output current, 2 = electric meter, 3 = power consumption and in
115      * each field is a
116      * string array with the fields 0 = refresh priority 1 = reading initial flag (true = reading is initialized,
117      * otherwise false)
118      */
119     private final Object[] powerSensorRefresh = new Object[] { new String[] { Config.REFRESH_PRIORITY_NEVER, "false" },
120             new String[] { Config.REFRESH_PRIORITY_NEVER, "false" },
121             new String[] { Config.REFRESH_PRIORITY_NEVER, "false" },
122             new String[] { Config.REFRESH_PRIORITY_NEVER, "false" } };
123
124     public static final int REFRESH_PRIORITY_ARRAY_FIELD = 0;
125     public static final int READING_INITIALIZED_ARRAY_FIELD = 1;
126
127     public static final int REFRESH_ACTIVE_POWER_ARRAY_FIELD = 0;
128     public static final int REFRESH_OUTPUT_CURRENT_ARRAY_FIELD = 1;
129     public static final int REFRESH_ELECTRIC_METER_ARRAY_FIELD = 2;
130     public static final int REFRESH_POWER_CONSUMPTION_ARRAY_FIELD = 3;
131     /*
132      * Cache the last power sensor value to get power sensor value directly
133      * the key is the output value and the value is an Integer array for the sensor values (0 = active power, 1 =
134      * output current, 2 = power consumption, 3 = output current high)
135      */
136     private final Map<Short, Integer[]> cachedSensorPowerValues = Collections.synchronizedMap(new HashMap<>());
137
138     public static final int ACTIVE_POWER_ARRAY_FIELD = 0;
139     public static final int OUTPUT_CURRENT_ARRAY_FIELD = 1;
140     public static final int POWER_CONSUMPTION_ARRAY_FIELD = 2;
141     public static final int OUTPUT_CURRENT_HIGH_ARRAY_FIELD = 3;
142
143     // Preparing for the advance device property setting "Turn 'switched' output off if value below:", but the
144     // configuration currently not work in digitalSTROM, because of that the value is fix 1.
145     private final int switchPercentOff = 1;
146
147     /**
148      * Creates a new {@link DeviceImpl} from the given DigitalSTROM-Device {@link JsonObject}.
149      *
150      * @param deviceJsonObject json response of the digitalSTROM-Server, must not be null
151      */
152     public DeviceImpl(JsonObject deviceJsonObject) {
153         super(deviceJsonObject);
154         if (deviceJsonObject.get(JSONApiResponseKeysEnum.METER_DSID.getKey()) != null) {
155             this.meterDSID = new DSID(deviceJsonObject.get(JSONApiResponseKeysEnum.METER_DSID.getKey()).getAsString());
156         } else if (deviceJsonObject.get(JSONApiResponseKeysEnum.DS_METER_DSID.getKey()) != null) {
157             this.meterDSID = new DSID(
158                     deviceJsonObject.get(JSONApiResponseKeysEnum.DS_METER_DSID.getKey()).getAsString());
159         }
160         if (deviceJsonObject.get(JSONApiResponseKeysEnum.HW_INFO.getKey()) != null) {
161             this.hwInfo = deviceJsonObject.get(JSONApiResponseKeysEnum.HW_INFO.getKey()).getAsString();
162         } else if (deviceJsonObject.get(JSONApiResponseKeysEnum.HW_INFO_UPPER_HW.getKey()) != null) {
163             this.hwInfo = deviceJsonObject.get(JSONApiResponseKeysEnum.HW_INFO_UPPER_HW.getKey()).getAsString();
164         }
165         if (deviceJsonObject.get(JSONApiResponseKeysEnum.ON.getKey()) != null) {
166             if (!isShade()) {
167                 this.isOn = deviceJsonObject.get(JSONApiResponseKeysEnum.ON.getKey()).getAsBoolean();
168             } else {
169                 this.isOpen = deviceJsonObject.get(JSONApiResponseKeysEnum.ON.getKey()).getAsBoolean();
170             }
171         }
172         if (deviceJsonObject.get(JSONApiResponseKeysEnum.ZONE_ID.getKey()) != null) {
173             zoneId = deviceJsonObject.get(JSONApiResponseKeysEnum.ZONE_ID.getKey()).getAsInt();
174         } else if (deviceJsonObject.get(JSONApiResponseKeysEnum.ZONE_ID_Lower_Z.getKey()) != null) {
175             zoneId = deviceJsonObject.get(JSONApiResponseKeysEnum.ZONE_ID_Lower_Z.getKey()).getAsInt();
176         }
177         JsonElement groups = deviceJsonObject.get(JSONApiResponseKeysEnum.GROUPS.getKey());
178         if (groups != null && groups.isJsonArray()) {
179             JsonArray array = deviceJsonObject.get(JSONApiResponseKeysEnum.GROUPS.getKey()).getAsJsonArray();
180             for (int i = 0; i < array.size(); i++) {
181                 if (array.get(i) != null) {
182                     initAddGroup(array.get(i).getAsShort());
183                 }
184             }
185         } else if (groups != null && groups.isJsonObject()) {
186             for (Entry<String, JsonElement> entry : deviceJsonObject.get(JSONApiResponseKeysEnum.GROUPS.getKey())
187                     .getAsJsonObject().entrySet()) {
188                 initAddGroup(entry.getValue().getAsJsonObject().get(JSONApiResponseKeysEnum.ID.getKey()).getAsShort());
189             }
190         }
191         if (deviceJsonObject.get(JSONApiResponseKeysEnum.OUTPUT_MODE.getKey()) != null) {
192             int tmp = deviceJsonObject.get(JSONApiResponseKeysEnum.OUTPUT_MODE.getKey()).getAsInt();
193             if (tmp != -1) {
194                 if (OutputModeEnum.containsMode(tmp)) {
195                     outputMode = OutputModeEnum.getMode(tmp);
196                 }
197             }
198         }
199         if (deviceJsonObject.get(JSONApiResponseKeysEnum.SENSOR_INPUTS.getKey()) != null
200                 && deviceJsonObject.get(JSONApiResponseKeysEnum.SENSOR_INPUTS.getKey()).isJsonObject()) {
201             JsonObject jObj = deviceJsonObject.get(JSONApiResponseKeysEnum.SENSOR_INPUTS.getKey()).getAsJsonObject();
202             for (Entry<String, JsonElement> entry : jObj.entrySet()) {
203                 if (entry.getValue().isJsonObject()) {
204                     JsonObject sensorType = entry.getValue().getAsJsonObject();
205                     if (sensorType.get(JSONApiResponseKeysEnum.TYPE.getKey()) != null) {
206                         if (SensorEnum
207                                 .containsSensor(sensorType.get(JSONApiResponseKeysEnum.TYPE.getKey()).getAsShort())) {
208                             setDeviceSensorValue(new DeviceSensorValue(entry.getValue().getAsJsonObject()));
209                         }
210                     }
211                 }
212             }
213         }
214         if (deviceJsonObject.get(JSONApiResponseKeysEnum.BINARY_INPUTS.getKey()) != null
215                 && deviceJsonObject.get(JSONApiResponseKeysEnum.BINARY_INPUTS.getKey()).isJsonObject()) {
216             JsonObject jObj = deviceJsonObject.get(JSONApiResponseKeysEnum.BINARY_INPUTS.getKey()).getAsJsonObject();
217             for (Entry<String, JsonElement> entry : jObj.entrySet()) {
218                 if (entry.getValue().isJsonObject()) {
219                     JsonObject binaryInput = entry.getValue().getAsJsonObject();
220                     deviceBinaryInputs.add(new DeviceBinaryInput(binaryInput));
221                 }
222             }
223         }
224         init();
225     }
226
227     private void initAddGroup(Short groupID) {
228         if (groupID != -1) {
229             this.groupList.add(groupID);
230             if (FuncNameAndColorGroupEnum.containsColorGroup(groupID)) {
231                 if (this.functionalName == null
232                         || !FuncNameAndColorGroupEnum.getMode(groupID).equals(FuncNameAndColorGroupEnum.JOKER)) {
233                     this.functionalName = FuncNameAndColorGroupEnum.getMode(groupID);
234                     this.functionalGroup = functionalName.getFunctionalColor();
235                 }
236             }
237         }
238     }
239
240     private void init() {
241         if (groupList.contains((short) 1)) {
242             maxOutputValue = DeviceConstants.MAX_OUTPUT_VALUE_LIGHT;
243             if (this.isDimmable()) {
244                 minOutputValue = DeviceConstants.MIN_DIM_VALUE;
245             }
246         } else {
247             maxOutputValue = DeviceConstants.DEFAULT_MAX_OUTPUTVALUE;
248             minOutputValue = 0;
249         }
250         if (isOn) {
251             outputValue = DeviceConstants.DEFAULT_MAX_OUTPUTVALUE;
252         }
253     }
254
255     @Override
256     public synchronized DSID getMeterDSID() {
257         return this.meterDSID;
258     }
259
260     @Override
261     public synchronized void setMeterDSID(String meterDSID) {
262         this.meterDSID = new DSID(meterDSID);
263         informListenerAboutConfigChange(ChangeableDeviceConfigEnum.METER_DSID);
264     }
265
266     @Override
267     public String getHWinfo() {
268         return hwInfo;
269     }
270
271     @Override
272     public List<Short> getGroups() {
273         return new LinkedList<>(groupList);
274     }
275
276     @Override
277     public void addGroup(Short groupID) {
278         if (!this.groupList.contains(groupID)) {
279             this.groupList.add(groupID);
280         }
281         informListenerAboutConfigChange(ChangeableDeviceConfigEnum.GROUPS);
282     }
283
284     @Override
285     public void setGroups(List<Short> newGroupList) {
286         if (newGroupList != null) {
287             this.groupList = newGroupList;
288         }
289         informListenerAboutConfigChange(ChangeableDeviceConfigEnum.GROUPS);
290     }
291
292     @Override
293     public synchronized int getZoneId() {
294         return zoneId;
295     }
296
297     @Override
298     public synchronized void setZoneId(int zoneID) {
299         this.zoneId = zoneID;
300         informListenerAboutConfigChange(ChangeableDeviceConfigEnum.ZONE_ID);
301     }
302
303     @Override
304     public synchronized boolean isOn() {
305         return isOn;
306     }
307
308     @Override
309     public synchronized void setIsOn(boolean flag) {
310         if (flag) {
311             this.deviceStateUpdates.add(new DeviceStateUpdateImpl(DeviceStateUpdate.ON_OFF, 1));
312         } else {
313             this.deviceStateUpdates.add(new DeviceStateUpdateImpl(DeviceStateUpdate.ON_OFF, -1));
314         }
315     }
316
317     @Override
318     public synchronized boolean isOpen() {
319         return this.isOpen;
320     }
321
322     @Override
323     public synchronized void setIsOpen(boolean flag) {
324         if (flag) {
325             this.deviceStateUpdates.add(new DeviceStateUpdateImpl(DeviceStateUpdate.OPEN_CLOSE, 1));
326             if (isBlind()) {
327                 this.deviceStateUpdates.add(new DeviceStateUpdateImpl(DeviceStateUpdate.OPEN_CLOSE_ANGLE, 1));
328             }
329         } else {
330             this.deviceStateUpdates.add(new DeviceStateUpdateImpl(DeviceStateUpdate.OPEN_CLOSE, -1));
331             if (isBlind()) {
332                 this.deviceStateUpdates.add(new DeviceStateUpdateImpl(DeviceStateUpdate.OPEN_CLOSE_ANGLE, -1));
333             }
334         }
335     }
336
337     @Override
338     public synchronized void setOutputValue(short value) {
339         if (!isShade()) {
340             if (value <= 0) {
341                 this.deviceStateUpdates.add(new DeviceStateUpdateImpl(DeviceStateUpdate.ON_OFF, -1));
342
343             } else if (value > maxOutputValue) {
344                 this.deviceStateUpdates.add(new DeviceStateUpdateImpl(DeviceStateUpdate.ON_OFF, 1));
345             } else {
346                 this.deviceStateUpdates.add(new DeviceStateUpdateImpl(DeviceStateUpdate.OUTPUT, value));
347             }
348         }
349     }
350
351     @Override
352     public synchronized boolean isDimmable() {
353         return OutputModeEnum.outputModeIsDimmable(outputMode);
354     }
355
356     @Override
357     public synchronized boolean isSwitch() {
358         return OutputModeEnum.outputModeIsSwitch(outputMode);
359     }
360
361     @Override
362     public synchronized boolean isDeviceWithOutput() {
363         return this.outputMode != null && !this.outputMode.equals(OutputModeEnum.DISABLED);
364     }
365
366     @Override
367     public boolean isSensorDevice() {
368         return !isDeviceWithOutput() && !deviceClimateSensorTypes.isEmpty();
369     }
370
371     @Override
372     public boolean isHeatingDevice() {
373         return functionalName.equals(FuncNameAndColorGroupEnum.HEATING);
374     }
375
376     @Override
377     public boolean isTemperatureControlledDevice() {
378         return functionalName.equals(FuncNameAndColorGroupEnum.TEMPERATION_CONTROL);
379     }
380
381     @Override
382     public boolean isShade() {
383         return OutputModeEnum.outputModeIsShade(outputMode);
384     }
385
386     @Override
387     public boolean isBlind() {
388         return outputMode.equals(OutputModeEnum.POSITION_CON_US);
389     }
390
391     @Override
392     public synchronized FunctionalColorGroupEnum getFunctionalColorGroup() {
393         return this.functionalGroup;
394     }
395
396     @Override
397     public synchronized void setFunctionalColorGroup(FunctionalColorGroupEnum fuctionalColorGroup) {
398         this.functionalGroup = fuctionalColorGroup;
399         informListenerAboutConfigChange(ChangeableDeviceConfigEnum.FUNCTIONAL_GROUP);
400     }
401
402     @Override
403     public OutputModeEnum getOutputMode() {
404         return outputMode;
405     }
406
407     @Override
408     public synchronized void setOutputMode(OutputModeEnum newOutputMode) {
409         this.outputMode = newOutputMode;
410         informListenerAboutConfigChange(ChangeableDeviceConfigEnum.OUTPUT_MODE);
411     }
412
413     @Override
414     public synchronized void increase() {
415         if (isDimmable()) {
416             deviceStateUpdates.add(new DeviceStateUpdateImpl(DeviceStateUpdate.OUTPUT_INCREASE, 0));
417         }
418         if (isShade()) {
419             this.deviceStateUpdates.add(new DeviceStateUpdateImpl(DeviceStateUpdate.SLAT_INCREASE, 0));
420             if (isBlind()) {
421                 this.deviceStateUpdates.add(new DeviceStateUpdateImpl(DeviceStateUpdate.SLAT_ANGLE_DECREASE, 0));
422             }
423         }
424     }
425
426     @Override
427     public synchronized void decrease() {
428         if (isDimmable()) {
429             this.deviceStateUpdates.add(new DeviceStateUpdateImpl(DeviceStateUpdate.OUTPUT_DECREASE, 0));
430         }
431         if (isShade()) {
432             this.deviceStateUpdates.add(new DeviceStateUpdateImpl(DeviceStateUpdate.SLAT_DECREASE, 0));
433             if (isBlind()) {
434                 this.deviceStateUpdates.add(new DeviceStateUpdateImpl(DeviceStateUpdate.SLAT_ANGLE_DECREASE, 0));
435             }
436         }
437     }
438
439     @Override
440     public synchronized void increaseSlatAngle() {
441         if (isBlind()) {
442             this.deviceStateUpdates.add(new DeviceStateUpdateImpl(DeviceStateUpdate.SLAT_ANGLE_DECREASE, 1));
443         }
444     }
445
446     @Override
447     public synchronized void decreaseSlatAngle() {
448         if (isBlind()) {
449             this.deviceStateUpdates.add(new DeviceStateUpdateImpl(DeviceStateUpdate.SLAT_ANGLE_DECREASE, 1));
450         }
451     }
452
453     @Override
454     public synchronized short getOutputValue() {
455         return outputValue;
456     }
457
458     @Override
459     public short getMaxOutputValue() {
460         return maxOutputValue;
461     }
462
463     @Override
464     public short getMinOutputValue() {
465         return minOutputValue;
466     }
467
468     @Override
469     public synchronized int getSlatPosition() {
470         return slatPosition;
471     }
472
473     @Override
474     public synchronized short getAnglePosition() {
475         return slatAngle;
476     }
477
478     @Override
479     public synchronized void setAnglePosition(int angle) {
480         if (angle == slatAngle) {
481             return;
482         }
483         if (angle < minSlatAngle) {
484             this.deviceStateUpdates.add(new DeviceStateUpdateImpl(DeviceStateUpdate.SLAT_ANGLE, minSlatAngle));
485         } else if (angle > this.maxSlatPosition) {
486             this.deviceStateUpdates.add(new DeviceStateUpdateImpl(DeviceStateUpdate.SLAT_ANGLE, maxSlatAngle));
487         } else {
488             this.deviceStateUpdates.add(new DeviceStateUpdateImpl(DeviceStateUpdate.SLAT_ANGLE, angle));
489         }
490     }
491
492     @Override
493     public synchronized void setSlatPosition(int position) {
494         if (position == this.slatPosition) {
495             return;
496         }
497         if (position < minSlatPosition) {
498             this.deviceStateUpdates.add(new DeviceStateUpdateImpl(DeviceStateUpdate.SLATPOSITION, minSlatPosition));
499         } else if (position > this.maxSlatPosition) {
500             this.deviceStateUpdates.add(new DeviceStateUpdateImpl(DeviceStateUpdate.SLATPOSITION, maxSlatPosition));
501         } else {
502             this.deviceStateUpdates.add(new DeviceStateUpdateImpl(DeviceStateUpdate.SLATPOSITION, position));
503         }
504     }
505
506     private short getDimmStep() {
507         if (isDimmable()) {
508             return DeviceConstants.DIM_STEP_LIGHT;
509         } else if (isShade()) {
510             return DeviceConstants.MOVE_STEP_ROLLERSHUTTER;
511         } else {
512             return DeviceConstants.DEFAULT_MOVE_STEP;
513         }
514     }
515
516     @Override
517     public int getMaxSlatPosition() {
518         return maxSlatPosition;
519     }
520
521     @Override
522     public int getMinSlatPosition() {
523         return minSlatPosition;
524     }
525
526     @Override
527     public int getMaxSlatAngle() {
528         return maxSlatAngle;
529     }
530
531     @Override
532     public int getMinSlatAngle() {
533         return minSlatAngle;
534     }
535
536     /* Begin-Scenes */
537
538     @Override
539     public synchronized void callInternalScene(InternalScene scene) {
540         if (lastCallWasUndo) {
541             lastScene = null;
542             if (activeScene != null) {
543                 activeScene.deactivateSceneByDevice();
544             }
545             activeScene = null;
546         }
547         internalCallScene(scene.getSceneID());
548         activeScene = scene;
549         lastCallWasUndo = false;
550     }
551
552     @Override
553     public void checkSceneConfig(Short sceneNumber, short prio) {
554         if (isDeviceWithOutput()) {
555             if (!containsSceneConfig(sceneNumber)) {
556                 deviceStateUpdates.add(new DeviceStateUpdateImpl(DeviceStateUpdate.UPDATE_SCENE_CONFIG,
557                         new Short[] { sceneNumber, prio }));
558
559             }
560             if (sceneOutputMap.get(sceneNumber) == null) {
561                 deviceStateUpdates.add(new DeviceStateUpdateImpl(DeviceStateUpdate.UPDATE_SCENE_OUTPUT,
562                         new Short[] { sceneNumber, prio }));
563             }
564         }
565     }
566
567     @Override
568     public synchronized void undoInternalScene(InternalScene scene) {
569         logger.debug("undo Scene {} dSID {}", scene.getSceneID(), dsid.getValue());
570         if (activeScene != null && activeScene.equals(scene)) {
571             if (lastCallWasUndo) {
572                 lastScene = null;
573                 return;
574             }
575             if (this.lastScene != null && !lastScene.equals(activeScene)) {
576                 activeScene = lastScene;
577                 lastScene = null;
578                 activeScene.activateSceneByDevice();
579             } else {
580                 internalUndoScene();
581                 logger.debug("internalUndo Scene dSID {}", dsid.getValue());
582                 this.activeScene = null;
583             }
584             internalUndoScene();
585             lastCallWasUndo = true;
586         }
587     }
588
589     @Override
590     public synchronized void callScene(Short sceneNumber) {
591         this.deviceStateUpdates.add(new DeviceStateUpdateImpl(DeviceStateUpdate.UPDATE_CALL_SCENE, sceneNumber));
592     }
593
594     @Override
595     public synchronized void internalCallScene(Short sceneNumber) {
596         logger.debug("call Scene id {} dSID {}", sceneNumber, dsid.getValue());
597         if (isDeviceWithOutput()) {
598             activeSceneNumber = sceneNumber;
599             informLastSceneAboutSceneCall(sceneNumber);
600             if (!isShade()) {
601                 outputValueBeforeSceneCall = this.outputValue;
602             } else {
603                 outputValueBeforeSceneCall = this.slatPosition;
604                 if (isBlind()) {
605                     slatAngleBeforeSceneCall = this.slatAngle;
606                 }
607             }
608             if (!checkSceneNumber(sceneNumber)) {
609                 if (containsSceneConfig(sceneNumber)) {
610                     if (doIgnoreScene(sceneNumber)) {
611                         return;
612                     }
613                 } else {
614                     this.deviceStateUpdates.add(new DeviceStateUpdateImpl(DeviceStateUpdate.UPDATE_SCENE_CONFIG,
615                             new Short[] { sceneNumber, GeneralLibConstance.HIGHEST_READ_OUT_PRIORITY }));
616                 }
617                 if (sceneOutputMap.get(sceneNumber) != null) {
618                     if (!isShade()) {
619                         updateInternalDeviceState(new DeviceStateUpdateImpl(DeviceStateUpdate.OUTPUT,
620                                 sceneOutputMap.get(sceneNumber)[GeneralLibConstance.SCENE_ARRAY_INDEX_VALUE]));
621                     } else {
622                         updateInternalDeviceState(new DeviceStateUpdateImpl(DeviceStateUpdate.SLATPOSITION,
623                                 sceneOutputMap.get(sceneNumber)[GeneralLibConstance.SCENE_ARRAY_INDEX_VALUE]));
624                         if (isBlind()) {
625                             updateInternalDeviceState(new DeviceStateUpdateImpl(DeviceStateUpdate.SLAT_ANGLE,
626                                     sceneOutputMap.get(sceneNumber)[GeneralLibConstance.SCENE_ARRAY_INDEX_ANGLE]));
627                         }
628                     }
629                 } else {
630                     this.deviceStateUpdates.add(new DeviceStateUpdateImpl(DeviceStateUpdate.UPDATE_SCENE_OUTPUT,
631                             new Short[] { sceneNumber, GeneralLibConstance.HIGHEST_READ_OUT_PRIORITY }));
632                 }
633             }
634
635         }
636     }
637
638     private boolean checkSceneNumber(Short sceneNumber) {
639         if (SceneEnum.containsScene(sceneNumber)) {
640             if (this.outputMode.equals(OutputModeEnum.POWERSAVE)) {
641                 switch (SceneEnum.getScene(sceneNumber)) {
642                     case ABSENT:
643                     case DEEP_OFF:
644                     case SLEEPING:
645                         this.updateInternalDeviceState(new DeviceStateUpdateImpl(DeviceStateUpdate.ON_OFF, -1));
646                         return true;
647                     case AREA_1_OFF:
648                     case AREA_2_OFF:
649                     case AREA_3_OFF:
650                     case AREA_4_OFF:
651                     case PRESET_0:
652                     case PRESET_10:
653                     case PRESET_20:
654                     case PRESET_30:
655                     case PRESET_40:
656                         return true;
657                     default:
658                         break;
659                 }
660             }
661             if (this.outputMode.equals(OutputModeEnum.WIPE)) {
662                 switch (SceneEnum.getScene(sceneNumber)) {
663                     case STANDBY:
664                     case AUTO_STANDBY:
665                     case AREA_1_OFF:
666                     case AREA_2_OFF:
667                     case AREA_3_OFF:
668                     case AREA_4_OFF:
669                     case PRESET_0:
670                     case PRESET_10:
671                     case PRESET_20:
672                     case PRESET_30:
673                     case PRESET_40:
674                         this.updateInternalDeviceState(new DeviceStateUpdateImpl(DeviceStateUpdate.ON_OFF, -1));
675                         return true;
676                     default:
677                         break;
678                 }
679             }
680             switch (SceneEnum.getScene(sceneNumber)) {
681                 // on scenes
682                 case DEVICE_ON:
683                 case MAXIMUM:
684                     if (!isShade()) {
685                         this.updateInternalDeviceState(new DeviceStateUpdateImpl(DeviceStateUpdate.ON_OFF, 1));
686                     } else {
687                         this.updateInternalDeviceState(new DeviceStateUpdateImpl(DeviceStateUpdate.OPEN_CLOSE, 1));
688                         if (isBlind()) {
689                             this.updateInternalDeviceState(
690                                     new DeviceStateUpdateImpl(DeviceStateUpdate.OPEN_CLOSE_ANGLE, 1));
691                         }
692                     }
693                     return true;
694                 // off scenes
695                 case MINIMUM:
696                 case DEVICE_OFF:
697                 case AUTO_OFF:
698                     if (!isShade()) {
699                         this.updateInternalDeviceState(new DeviceStateUpdateImpl(DeviceStateUpdate.ON_OFF, -1));
700                     } else {
701                         this.updateInternalDeviceState(new DeviceStateUpdateImpl(DeviceStateUpdate.OPEN_CLOSE, -1));
702                         if (isBlind()) {
703                             this.updateInternalDeviceState(
704                                     new DeviceStateUpdateImpl(DeviceStateUpdate.OPEN_CLOSE_ANGLE, -1));
705                         }
706                     }
707                     return true;
708                 // increase scenes
709                 case INCREMENT:
710                 case AREA_1_INCREMENT:
711                 case AREA_2_INCREMENT:
712                 case AREA_3_INCREMENT:
713                 case AREA_4_INCREMENT:
714                     if (isDimmable()) {
715                         if (outputValue == maxOutputValue) {
716                             return true;
717                         }
718                         this.updateInternalDeviceState(new DeviceStateUpdateImpl(DeviceStateUpdate.OUTPUT_INCREASE, 0));
719                     }
720                     if (isShade()) {
721                         if (slatPosition == maxSlatPosition) {
722                             return true;
723                         }
724                         this.updateInternalDeviceState(new DeviceStateUpdateImpl(DeviceStateUpdate.SLAT_INCREASE, 0));
725                         if (isBlind()) {
726                             if (slatAngle == maxSlatAngle) {
727                                 return true;
728                             }
729                             updateInternalDeviceState(
730                                     new DeviceStateUpdateImpl(DeviceStateUpdate.SLAT_ANGLE_INCREASE, 0));
731                         }
732                     }
733                     return true;
734                 // decrease scenes
735                 case DECREMENT:
736                 case AREA_1_DECREMENT:
737                 case AREA_2_DECREMENT:
738                 case AREA_3_DECREMENT:
739                 case AREA_4_DECREMENT:
740                     if (isDimmable()) {
741                         if (outputValue == minOutputValue) {
742                             return true;
743                         }
744                         updateInternalDeviceState(new DeviceStateUpdateImpl(DeviceStateUpdate.OUTPUT_DECREASE, 0));
745                     }
746                     if (isShade()) {
747                         if (slatPosition == minSlatPosition) {
748                             return true;
749                         }
750                         updateInternalDeviceState(new DeviceStateUpdateImpl(DeviceStateUpdate.SLAT_DECREASE, 0));
751                         if (isBlind()) {
752                             if (slatAngle == minSlatAngle) {
753                                 return true;
754                             }
755                             this.updateInternalDeviceState(
756                                     new DeviceStateUpdateImpl(DeviceStateUpdate.SLAT_ANGLE_INCREASE, 0));
757                         }
758                     }
759                     return true;
760                 // Stop scenes
761                 case AREA_1_STOP:
762                 case AREA_2_STOP:
763                 case AREA_3_STOP:
764                 case AREA_4_STOP:
765                 case DEVICE_STOP:
766                 case STOP:
767                     this.deviceStateUpdates.add(new DeviceStateUpdateImpl(DeviceStateUpdate.UPDATE_OUTPUT_VALUE, 0));
768                     return true;
769                 // Area Stepping continue scenes
770                 case AREA_STEPPING_CONTINUE:
771                     return true;
772                 default:
773                     return false;
774             }
775         }
776         return false;
777     }
778
779     private Integer[] getStandartSceneOutput(short sceneNumber) {
780         if (SceneEnum.getScene(sceneNumber) != null) {
781             switch (SceneEnum.getScene(sceneNumber)) {
782                 case DEVICE_ON:
783                 case MAXIMUM:
784                     if (!isShade()) {
785                         return new Integer[] { (int) maxOutputValue, -1 };
786                     } else {
787                         if (isBlind()) {
788                             return new Integer[] { (int) maxSlatPosition, (int) maxSlatAngle };
789                         } else {
790                             return new Integer[] { (int) maxSlatPosition, -1 };
791                         }
792                     }
793                     // off scenes
794                 case MINIMUM:
795                 case DEVICE_OFF:
796                 case AUTO_OFF:
797                     if (!isShade()) {
798                         return new Integer[] { (int) 0, -1 };
799                     } else {
800                         if (isBlind()) {
801                             return new Integer[] { (int) 0, 0 };
802                         } else {
803                             return new Integer[] { (int) 0, -1 };
804                         }
805                     }
806                 default:
807                     break;
808             }
809         }
810         return null;
811     }
812
813     private void informLastSceneAboutSceneCall(short sceneNumber) {
814         if (this.activeScene != null && this.activeScene.getSceneID() != sceneNumber) {
815             this.activeScene.deactivateSceneByDevice();
816             this.lastScene = this.activeScene;
817             this.activeScene = null;
818         }
819     }
820
821     @Override
822     public synchronized void undoScene() {
823         this.deviceStateUpdates
824                 .add(new DeviceStateUpdateImpl(DeviceStateUpdate.UPDATE_UNDO_SCENE, this.activeSceneNumber));
825     }
826
827     @Override
828     public synchronized void internalUndoScene() {
829         if (!isShade()) {
830             updateInternalDeviceState(
831                     new DeviceStateUpdateImpl(DeviceStateUpdate.OUTPUT, this.outputValueBeforeSceneCall));
832         } else {
833             updateInternalDeviceState(
834                     new DeviceStateUpdateImpl(DeviceStateUpdate.SLATPOSITION, this.outputValueBeforeSceneCall));
835             if (isBlind()) {
836                 updateInternalDeviceState(
837                         new DeviceStateUpdateImpl(DeviceStateUpdate.SLAT_ANGLE, this.slatAngleBeforeSceneCall));
838             }
839         }
840
841         if (activeSceneNumber != -1) {
842             activeSceneNumber = -1;
843         }
844     }
845
846     @Override
847     public InternalScene getAcitiveScene() {
848         return this.activeScene;
849     }
850
851     @Override
852     public Integer[] getSceneOutputValue(short sceneId) {
853         synchronized (sceneOutputMap) {
854             if (sceneOutputMap.containsKey(sceneId)) {
855                 return sceneOutputMap.get(sceneId);
856             }
857         }
858         return new Integer[] { -1, -1 };
859     }
860
861     @Override
862     public void setSceneOutputValue(short sceneId, int value) {
863         internalSetSceneOutputValue(sceneId, value, -1);
864         if (listener != null) {
865             listener.onSceneConfigAdded(sceneId);
866         }
867     }
868
869     @Override
870     public void setSceneOutputValue(short sceneId, int value, int angle) {
871         internalSetSceneOutputValue(sceneId, value, angle);
872         if (listener != null) {
873             listener.onSceneConfigAdded(sceneId);
874         }
875     }
876
877     private void internalSetSceneOutputValue(short sceneId, int value, int angle) {
878         synchronized (sceneOutputMap) {
879             sceneOutputMap.put(sceneId, new Integer[] { value, angle });
880         }
881         if (activeSceneNumber == sceneId) {
882             internalCallScene(sceneId);
883         }
884     }
885
886     @Override
887     public List<Short> getSavedScenes() {
888         Set<Short> bothKeySet = new HashSet<>(sceneOutputMap.keySet());
889         bothKeySet.addAll(sceneConfigMap.keySet());
890         return new LinkedList<>(bothKeySet);
891     }
892
893     @Override
894     public void addSceneConfig(short sceneId, DeviceSceneSpec sceneSpec) {
895         if (sceneSpec != null) {
896             synchronized (sceneConfigMap) {
897                 sceneConfigMap.put(sceneId, sceneSpec);
898                 if (listener != null) {
899                     listener.onSceneConfigAdded(sceneId);
900                 }
901             }
902         }
903     }
904
905     @Override
906     public DeviceSceneSpec getSceneConfig(short sceneId) {
907         synchronized (sceneConfigMap) {
908             return sceneConfigMap.get(sceneId);
909         }
910     }
911
912     @Override
913     public boolean doIgnoreScene(short sceneId) {
914         synchronized (sceneConfigMap) {
915             if (this.sceneConfigMap.containsKey(sceneId)) {
916                 return this.sceneConfigMap.get(sceneId).isDontCare();
917             }
918         }
919         return false;
920     }
921
922     @Override
923     public boolean containsSceneConfig(short sceneId) {
924         synchronized (sceneConfigMap) {
925             return sceneConfigMap.containsKey(sceneId);
926         }
927     }
928
929     /* End-Scenes */
930
931     @Override
932     public boolean equals(Object obj) {
933         if (obj instanceof Device) {
934             Device device = (Device) obj;
935             return device.getDSID().equals(this.getDSID());
936         }
937         return false;
938     }
939
940     @Override
941     public int hashCode() {
942         return this.getDSID().hashCode();
943     }
944
945     @Override
946     public boolean isPowerSensorUpToDate(SensorEnum powerSensorType) {
947         if (powerSensorType != null && SensorEnum.isPowerSensor(powerSensorType)) {
948             boolean isUpToDate = true;
949             if (powerSensorType.equals(SensorEnum.ACTIVE_POWER)) {
950                 isUpToDate = (outputMode != null && outputMode.equals(OutputModeEnum.WIPE) && !isOn)
951                         || (isOn && !isShade()) && !checkPowerSensorRefreshPriorityNever(powerSensorType)
952                                 ? checkSensorRefreshTime(powerSensorType)
953                                 : true;
954             }
955             if (powerSensorType.equals(SensorEnum.ELECTRIC_METER)) {
956                 isUpToDate = (isOn || getDeviceSensorValue(powerSensorType).getDsValue() == 0) && !isShade()
957                         && !checkPowerSensorRefreshPriorityNever(powerSensorType)
958                                 ? checkSensorRefreshTime(powerSensorType)
959                                 : true;
960             }
961             isUpToDate = isOn && !isShade() && !checkPowerSensorRefreshPriorityNever(powerSensorType)
962                     ? checkSensorRefreshTime(powerSensorType)
963                     : true;
964             if (!isUpToDate) {
965                 if (!getSensorDataReadingInitialized(powerSensorType)) {
966                     deviceStateUpdates.add(new DeviceStateUpdateImpl(powerSensorType, 0));
967                     setSensorDataReadingInitialized(powerSensorType, true);
968                 }
969                 return false;
970             }
971             return true;
972         }
973         throw new IllegalArgumentException("powerSensorType is null or not a power sensor type.");
974     }
975
976     private boolean checkSensorRefreshTime(SensorEnum sensorType) {
977         if (sensorType != null) {
978             DeviceSensorValue devSenVal = getDeviceSensorValue(sensorType);
979             if (devSenVal.getValid()) {
980                 int refresh = Config.DEFAULT_SENSORDATA_REFRESH_INTERVAL;
981                 if (config != null) {
982                     refresh = config.getSensordataRefreshInterval();
983                 }
984                 return (devSenVal.getTimestamp().getTime() + refresh) > System.currentTimeMillis();
985             }
986         }
987         return false;
988     }
989
990     @Override
991     public boolean isSensorDataUpToDate() {
992         boolean isUpToDate = true;
993         for (SensorEnum sensorType : devicePowerSensorTypes) {
994             isUpToDate = isPowerSensorUpToDate(sensorType);
995         }
996         return isUpToDate;
997     }
998
999     @Override
1000     public void setSensorDataRefreshPriority(String activePowerRefreshPriority, String electricMeterRefreshPriority,
1001             String outputCurrentRefreshPriority) {
1002         if (checkPriority(activePowerRefreshPriority)) {
1003             ((String[]) powerSensorRefresh[REFRESH_ACTIVE_POWER_ARRAY_FIELD])[REFRESH_PRIORITY_ARRAY_FIELD] = activePowerRefreshPriority;
1004         }
1005         if (checkPriority(outputCurrentRefreshPriority)) {
1006             ((String[]) powerSensorRefresh[REFRESH_OUTPUT_CURRENT_ARRAY_FIELD])[REFRESH_PRIORITY_ARRAY_FIELD] = outputCurrentRefreshPriority;
1007         }
1008         if (checkPriority(electricMeterRefreshPriority)) {
1009             ((String[]) powerSensorRefresh[REFRESH_ELECTRIC_METER_ARRAY_FIELD])[REFRESH_PRIORITY_ARRAY_FIELD] = electricMeterRefreshPriority;
1010         }
1011     }
1012
1013     @Override
1014     public void setSensorDataRefreshPriority(SensorEnum powerSensorType, String refreshPriority) {
1015         if (checkPriority(refreshPriority)) {
1016             String[] powerSensorRefresh = getPowerSensorRefresh(powerSensorType);
1017             if (powerSensorRefresh != null) {
1018                 powerSensorRefresh[REFRESH_ACTIVE_POWER_ARRAY_FIELD] = refreshPriority;
1019             }
1020         }
1021     }
1022
1023     @Override
1024     public String getPowerSensorRefreshPriority(SensorEnum powerSensorType) {
1025         if (powerSensorType.equals(SensorEnum.ACTIVE_POWER) && outputMode.equals(OutputModeEnum.WIPE) && !isOn) {
1026             return Config.REFRESH_PRIORITY_LOW;
1027         }
1028         String[] powerSensorRefresh = getPowerSensorRefresh(powerSensorType);
1029         if (powerSensorRefresh != null) {
1030             return powerSensorRefresh[REFRESH_PRIORITY_ARRAY_FIELD];
1031         }
1032         return null;
1033     }
1034
1035     @Override
1036     public boolean checkPowerSensorRefreshPriorityNever(SensorEnum powerSensorType) {
1037         if (getPowerSensorRefreshPriority(powerSensorType) != null) {
1038             return getPowerSensorRefreshPriority(powerSensorType).equals(Config.REFRESH_PRIORITY_NEVER);
1039         }
1040         return true;
1041     }
1042
1043     private void setAllSensorDataRefreshPrioritiesToNever() {
1044         for (short i = 0; i < powerSensorRefresh.length; i++) {
1045             ((String[]) powerSensorRefresh[i])[REFRESH_PRIORITY_ARRAY_FIELD] = Config.REFRESH_PRIORITY_NEVER;
1046         }
1047     }
1048
1049     private void setSensorDataReadingInitialized(SensorEnum powerSensorType, Boolean flag) {
1050         String[] powerSensorRefresh = getPowerSensorRefresh(powerSensorType);
1051         if (powerSensorRefresh != null) {
1052             powerSensorRefresh[READING_INITIALIZED_ARRAY_FIELD] = flag.toString();
1053         }
1054     }
1055
1056     private boolean getSensorDataReadingInitialized(SensorEnum powerSensorType) {
1057         String[] powerSensorRefresh = getPowerSensorRefresh(powerSensorType);
1058         if (powerSensorRefresh != null) {
1059             return Boolean.valueOf(powerSensorRefresh[READING_INITIALIZED_ARRAY_FIELD]);
1060         }
1061         return false;
1062     }
1063
1064     private String[] getPowerSensorRefresh(SensorEnum powerSensorType) {
1065         switch (powerSensorType) {
1066             case ACTIVE_POWER:
1067                 return (String[]) powerSensorRefresh[REFRESH_ACTIVE_POWER_ARRAY_FIELD];
1068             case OUTPUT_CURRENT:
1069                 return (String[]) powerSensorRefresh[REFRESH_OUTPUT_CURRENT_ARRAY_FIELD];
1070             case ELECTRIC_METER:
1071                 return (String[]) powerSensorRefresh[REFRESH_ELECTRIC_METER_ARRAY_FIELD];
1072             case POWER_CONSUMPTION:
1073                 return (String[]) powerSensorRefresh[REFRESH_POWER_CONSUMPTION_ARRAY_FIELD];
1074             default:
1075                 return null;
1076         }
1077     }
1078
1079     private boolean checkPriority(String priority) {
1080         switch (priority) {
1081             case Config.REFRESH_PRIORITY_HIGH:
1082             case Config.REFRESH_PRIORITY_MEDIUM:
1083             case Config.REFRESH_PRIORITY_LOW:
1084             case Config.REFRESH_PRIORITY_NEVER:
1085                 return true;
1086             default:
1087                 logger.error("Sensor data update priority do not exist! Please check the input!");
1088                 return false;
1089         }
1090     }
1091
1092     @Override
1093     public boolean isDeviceUpToDate() {
1094         isSensorDataUpToDate();
1095         return this.deviceStateUpdates.isEmpty();
1096     }
1097
1098     @Override
1099     public DeviceStateUpdate getNextDeviceUpdateState() {
1100         return !this.deviceStateUpdates.isEmpty() ? this.deviceStateUpdates.remove(0) : null;
1101     }
1102
1103     private int internalSetOutputValue(int value) {
1104         if (isShade()) {
1105             slatPosition = value;
1106             if (slatPosition <= 0) {
1107                 slatPosition = 0;
1108                 isOpen = false;
1109             } else {
1110                 isOpen = true;
1111             }
1112             return slatPosition;
1113         } else {
1114             outputValue = (short) value;
1115             if (outputValue <= 0) {
1116                 internalSetOff();
1117             } else {
1118                 if (isSwitch()) {
1119                     if (outputValue < switchPercentOff) {
1120                         internalSetOff();
1121                         isOn = false;
1122                     } else {
1123                         isOn = true;
1124                         setCachedMeterData();
1125                     }
1126                 } else {
1127                     isOn = true;
1128                     setCachedMeterData();
1129                 }
1130             }
1131             return outputValue;
1132         }
1133     }
1134
1135     private void internalSetOff() {
1136         this.isOn = false;
1137         logger.debug("internal set OFF ");
1138         if (!checkPowerSensorRefreshPriorityNever(SensorEnum.ACTIVE_POWER)) {
1139             if (getSensorDataReadingInitialized(SensorEnum.ACTIVE_POWER)) {
1140                 deviceStateUpdates.add(new DeviceStateUpdateImpl(SensorEnum.ACTIVE_POWER, -1));
1141                 logger.debug("internal set sensor to 0");
1142             }
1143             setDsSensorValue(SensorEnum.ACTIVE_POWER, 0);
1144         }
1145         if (!checkPowerSensorRefreshPriorityNever(SensorEnum.OUTPUT_CURRENT)) {
1146             if (getSensorDataReadingInitialized(SensorEnum.OUTPUT_CURRENT)) {
1147                 deviceStateUpdates.add(new DeviceStateUpdateImpl(SensorEnum.OUTPUT_CURRENT, -1));
1148             }
1149             setDsSensorValue(SensorEnum.OUTPUT_CURRENT, 0);
1150         }
1151         if (!checkPowerSensorRefreshPriorityNever(SensorEnum.POWER_CONSUMPTION)) {
1152             if (getSensorDataReadingInitialized(SensorEnum.POWER_CONSUMPTION)) {
1153                 deviceStateUpdates.add(new DeviceStateUpdateImpl(SensorEnum.POWER_CONSUMPTION, -1));
1154             }
1155             setDsSensorValue(SensorEnum.POWER_CONSUMPTION, 0);
1156         }
1157     }
1158
1159     private short internalSetAngleValue(int value) {
1160         if (value < 0) {
1161             slatAngle = 0;
1162         }
1163         if (value > maxSlatAngle) {
1164             slatAngle = maxSlatAngle;
1165         } else {
1166             slatAngle = (short) value;
1167         }
1168         return slatAngle;
1169     }
1170
1171     // Device sensors
1172     @Override
1173     public List<SensorEnum> getSensorTypes() {
1174         List<SensorEnum> list = new ArrayList<>(devicePowerSensorTypes);
1175         list.addAll(deviceClimateSensorTypes);
1176         return list;
1177     }
1178
1179     @Override
1180     public List<SensorEnum> getPowerSensorTypes() {
1181         return devicePowerSensorTypes;
1182     }
1183
1184     @Override
1185     public List<SensorEnum> getClimateSensorTypes() {
1186         return deviceClimateSensorTypes;
1187     }
1188
1189     @Override
1190     public List<DeviceSensorValue> getDeviceSensorValues() {
1191         return deviceSensorValues;
1192     }
1193
1194     @Override
1195     public boolean supportsSensorType(SensorEnum sensorType) {
1196         if (sensorType != null) {
1197             return getSensorTypes().contains(sensorType);
1198         }
1199         return false;
1200     }
1201
1202     @Override
1203     public void setDeviceSensorValue(DeviceSensorValue deviceSensorValue) {
1204         if (deviceSensorValue != null) {
1205             int index = deviceSensorValues.indexOf(deviceSensorValue);
1206             if (index < 0) {
1207                 deviceSensorValues.add(deviceSensorValue);
1208                 if (SensorEnum.isPowerSensor(deviceSensorValue.getSensorType())) {
1209                     devicePowerSensorTypes.add(deviceSensorValue.getSensorType());
1210                 } else {
1211                     deviceClimateSensorTypes.add(deviceSensorValue.getSensorType());
1212                 }
1213             } else {
1214                 if (deviceSensorValue.getTimestamp().after(deviceSensorValues.get(index).getTimestamp())) {
1215                     logger.debug("set deviceSeneorValue, new deviceSensorValue is: {}", deviceSensorValue.toString());
1216                     deviceSensorValues.set(index, deviceSensorValue);
1217                     checkSensorValueSet(deviceSensorValue, true);
1218                 }
1219             }
1220         }
1221     }
1222
1223     @Override
1224     public void setDeviceSensorByEvent(EventItem event) {
1225         DeviceSensorValue devSenVal = new DeviceSensorValue(event.getProperties());
1226         SensorEnum sensorType = devSenVal.getSensorType();
1227         if (!isEchoSensor(sensorType)) {
1228             logger.debug("Event is no echo, set values {} for sensorType {}", devSenVal, devSenVal.getSensorType());
1229             if (SensorEnum.isPowerSensor(sensorType) && getSensorDataReadingInitialized(sensorType)) {
1230                 logger.debug("SensorJob was initialized, remove sensorjob for sensorType: {}",
1231                         devSenVal.getSensorType());
1232                 deviceStateUpdates.add(new DeviceStateUpdateImpl(sensorType, -1));
1233             }
1234             setDeviceSensorValue(devSenVal);
1235         } else {
1236             logger.debug("Event is echo remove sensorType {} from echoBox", devSenVal.getSensorType());
1237             sensorEchoBox.remove(devSenVal.getSensorType());
1238         }
1239     }
1240
1241     private boolean isEchoSensor(SensorEnum sensorType) {
1242         return sensorEchoBox != null ? sensorEchoBox.contains(sensorType) : false;
1243     }
1244
1245     private List<SensorEnum> sensorEchoBox = Collections.synchronizedList(new LinkedList<>());
1246
1247     @Override
1248     public void setDeviceSensorDsValueBySensorJob(SensorEnum sensorType, Integer value) {
1249         logger.debug("sensorJob for device {} is executet", dsid.getValue());
1250         if (isSensorEchoBoxEnabled()) {
1251             // temperature resolution is not correct, so waiting for device sensor-event
1252             if (!sensorType.toString().contains("TEMPERATURE")) {
1253                 logger.debug("echoBox is enabled, add sensorType {} to echoBox", sensorType);
1254                 sensorEchoBox.add(sensorType);
1255             } else {
1256                 logger.debug("echoBox is enabled, ignoring temperation update and wait for sensor Event");
1257                 return;
1258             }
1259         }
1260         setDsSensorValue(sensorType, value);
1261     }
1262
1263     @Override
1264     public void enableSensorEchoBox() {
1265         if (sensorEchoBox == null) {
1266             sensorEchoBox = Collections.synchronizedList(new LinkedList<>());
1267         }
1268     }
1269
1270     @Override
1271     public void disableSensorEchoBox() {
1272         sensorEchoBox = null;
1273     }
1274
1275     @Override
1276     public boolean isSensorEchoBoxEnabled() {
1277         return sensorEchoBox != null;
1278     }
1279
1280     @Override
1281     public DeviceSensorValue getDeviceSensorValue(SensorEnum sensorType) {
1282         if (sensorType != null) {
1283             for (DeviceSensorValue devSenVal : deviceSensorValues) {
1284                 if (devSenVal.getSensorType().equals(sensorType)) {
1285                     return devSenVal;
1286                 }
1287             }
1288         }
1289         return null;
1290     }
1291
1292     @Override
1293     public DeviceSensorValue getDeviceSensorValue(Short sensorIndex) {
1294         if (sensorIndex != null) {
1295             for (DeviceSensorValue devSenVal : deviceSensorValues) {
1296                 if (devSenVal.getSensorIndex().equals(sensorIndex)) {
1297                     return devSenVal;
1298                 }
1299             }
1300         }
1301         return null;
1302     }
1303
1304     @Override
1305     public Short getSensorIndex(SensorEnum sensorType) {
1306         if (sensorType != null) {
1307             DeviceSensorValue devSenVal = getDeviceSensorValue(sensorType);
1308             return devSenVal != null ? devSenVal.getSensorIndex() : null;
1309         }
1310         return null;
1311     }
1312
1313     @Override
1314     public SensorEnum getSensorType(Short sensorIndex) {
1315         if (sensorIndex != null) {
1316             DeviceSensorValue devSenVal = getDeviceSensorValue(sensorIndex);
1317             return devSenVal != null ? devSenVal.getSensorType() : null;
1318         }
1319         return null;
1320     }
1321
1322     @Override
1323     public Integer getDsSensorValue(SensorEnum sensorType) {
1324         return getDsSensorValue((Object) sensorType);
1325     }
1326
1327     @Override
1328     public Integer getDsSensorValue(Short sensorIndex) {
1329         return getDsSensorValue((Object) sensorIndex);
1330     }
1331
1332     @Override
1333     public Float getFloatSensorValue(Short sensorIndex) {
1334         return getFloatSensorValue((Object) sensorIndex);
1335     }
1336
1337     @Override
1338     public Float getFloatSensorValue(SensorEnum sensorType) {
1339         return getFloatSensorValue((Object) sensorType);
1340     }
1341
1342     @Override
1343     public boolean setFloatSensorValue(SensorEnum sensorType, Float floatSensorValue) {
1344         return checkAndSetSensorValue(sensorType, null, floatSensorValue);
1345     }
1346
1347     @Override
1348     public boolean setFloatSensorValue(Short sensorIndex, Float floatSensorValue) {
1349         return checkAndSetSensorValue(sensorIndex, null, floatSensorValue);
1350     }
1351
1352     @Override
1353     public boolean setDsSensorValue(Short sensorIndex, Integer dSSensorValue) {
1354         return checkAndSetSensorValue(sensorIndex, dSSensorValue, null);
1355     }
1356
1357     @Override
1358     public boolean setDsSensorValue(SensorEnum sensorType, Integer dSSensorValue) {
1359         return checkAndSetSensorValue(sensorType, dSSensorValue, null);
1360     }
1361
1362     @Override
1363     public boolean setDsSensorValue(Short sensorIndex, Integer dSSensorValue, Float floatSensorValue) {
1364         return checkAndSetSensorValue(sensorIndex, dSSensorValue, floatSensorValue);
1365     }
1366
1367     @Override
1368     public boolean setDsSensorValue(SensorEnum sensorType, Integer dSSensorValue, Float floatSensorValue) {
1369         return checkAndSetSensorValue(sensorType, dSSensorValue, floatSensorValue);
1370     }
1371
1372     @Override
1373     public boolean hasSensors() {
1374         return hasClimateSensors() || hasPowerSensors();
1375     }
1376
1377     @Override
1378     public boolean hasClimateSensors() {
1379         return !deviceClimateSensorTypes.isEmpty();
1380     }
1381
1382     @Override
1383     public boolean hasPowerSensors() {
1384         return !devicePowerSensorTypes.isEmpty();
1385     }
1386
1387     // Sensor get/set helper methods
1388     private DeviceSensorValue getDeviceSensorValueForGet(Object obj) {
1389         return checkHighOutputCurrent(getDeviceSensorValueForSet(obj));
1390     }
1391
1392     private DeviceSensorValue getDeviceSensorValueForSet(Object obj) {
1393         if (obj instanceof Short) {
1394             return getDeviceSensorValue((Short) obj);
1395         } else {
1396             return getDeviceSensorValue((SensorEnum) obj);
1397         }
1398     }
1399
1400     private Integer getDsSensorValue(Object obj) {
1401         if (obj != null) {
1402             DeviceSensorValue devSenVal = checkPowerSensor(getDeviceSensorValueForGet(obj));
1403             return devSenVal != null && devSenVal.getValid() ? devSenVal.getDsValue() : null;
1404         }
1405         return null;
1406     }
1407
1408     private Float getFloatSensorValue(Object obj) {
1409         if (obj != null) {
1410             DeviceSensorValue devSenVal = checkPowerSensor(getDeviceSensorValueForGet(obj));
1411             return devSenVal != null && devSenVal.getValid() ? devSenVal.getFloatValue() : null;
1412         }
1413         return null;
1414     }
1415
1416     private DeviceSensorValue checkPowerSensor(DeviceSensorValue devSenVal) {
1417         if (devSenVal != null && SensorEnum.isPowerSensor(devSenVal.getSensorType())) {
1418             if (!devSenVal.getSensorType().equals(SensorEnum.ELECTRIC_METER)
1419                     && !(SensorEnum.isPowerSensor(devSenVal.getSensorType()) && isOn)) {
1420                 devSenVal.setDsValue(0);
1421             }
1422         }
1423         return devSenVal;
1424     }
1425
1426     /**
1427      * Checks output current sensor to return automatically high output current sensor, if the sensor exists.
1428      *
1429      * @param devSenVal
1430      * @return output current high DeviceSensorValue or the given DeviceSensorValue
1431      */
1432     private DeviceSensorValue checkHighOutputCurrent(DeviceSensorValue devSenVal) {
1433         if (devSenVal != null && devSenVal.getSensorType().equals(SensorEnum.OUTPUT_CURRENT)
1434                 && devSenVal.getDsValue() == SensorEnum.OUTPUT_CURRENT.getMax().intValue()
1435                 && devicePowerSensorTypes.contains(SensorEnum.OUTPUT_CURRENT_H)) {
1436             return getDeviceSensorValue(SensorEnum.OUTPUT_CURRENT_H);
1437         }
1438         return devSenVal;
1439     }
1440
1441     private boolean checkAndSetSensorValue(Object obj, Integer dsValue, Float floatValue) {
1442         boolean isSet = false;
1443         if (obj != null) {
1444             DeviceSensorValue devSenVal = getDeviceSensorValueForSet(obj);
1445             if (devSenVal != null) {
1446                 if (dsValue != null && floatValue != null) {
1447                     isSet = devSenVal.setValues(floatValue, dsValue);
1448                 } else if (dsValue != null) {
1449                     isSet = devSenVal.setDsValue(dsValue);
1450                 } else if (floatValue != null) {
1451                     isSet = devSenVal.setFloatValue(floatValue);
1452                 }
1453                 logger.debug("check devSenVal {} isSet={}", devSenVal.toString(), isSet);
1454                 checkSensorValueSet(devSenVal, isSet);
1455             }
1456         }
1457         return isSet;
1458     }
1459
1460     private void checkSensorValueSet(DeviceSensorValue devSenVal, boolean isSet) {
1461         if (devSenVal != null) {
1462             if (isSet) {
1463                 if (outputMode.equals(OutputModeEnum.WIPE) && !isOn
1464                         && devSenVal.getSensorType().equals(SensorEnum.ACTIVE_POWER)) {
1465                     int standby = Config.DEFAULT_STANDBY_ACTIVE_POWER;
1466                     if (config != null) {
1467                         standby = config.getStandbyActivePower();
1468                     }
1469                     if (devSenVal.getDsValue() > standby) {
1470                         this.updateInternalDeviceState(new DeviceStateUpdateImpl(DeviceStateUpdate.ON_OFF, 1));
1471                     }
1472                 }
1473                 if (SensorEnum.isPowerSensor(devSenVal.getSensorType())) {
1474                     addPowerSensorCache(devSenVal);
1475                 }
1476                 informListenerAboutStateUpdate(
1477                         new DeviceStateUpdateImpl(devSenVal.getSensorType(), devSenVal.getFloatValue()));
1478             }
1479             setSensorDataReadingInitialized(devSenVal.getSensorType(), false);
1480         }
1481     }
1482
1483     private void addPowerSensorCache(DeviceSensorValue newDevSenVal) {
1484         Integer[] cachedPowerValues = cachedSensorPowerValues.get(outputValue);
1485         if (cachedPowerValues == null) {
1486             cachedPowerValues = new Integer[4];
1487         }
1488         switch (newDevSenVal.getSensorType()) {
1489             case ACTIVE_POWER:
1490                 cachedPowerValues[ACTIVE_POWER_ARRAY_FIELD] = newDevSenVal.getDsValue();
1491                 break;
1492             case OUTPUT_CURRENT:
1493                 cachedPowerValues[OUTPUT_CURRENT_ARRAY_FIELD] = newDevSenVal.getDsValue();
1494                 break;
1495             case OUTPUT_CURRENT_H:
1496                 cachedPowerValues[OUTPUT_CURRENT_HIGH_ARRAY_FIELD] = newDevSenVal.getDsValue();
1497                 break;
1498             case POWER_CONSUMPTION:
1499                 cachedPowerValues[POWER_CONSUMPTION_ARRAY_FIELD] = newDevSenVal.getDsValue();
1500                 break;
1501             default:
1502                 return;
1503         }
1504         this.cachedSensorPowerValues.put(outputValue, cachedPowerValues);
1505     }
1506
1507     @Override
1508     public synchronized void updateInternalDeviceState(DeviceStateUpdate deviceStateUpdate) {
1509         DeviceStateUpdate deviceStateUpdateInt = internalSetOutputValue(deviceStateUpdate);
1510         if (deviceStateUpdateInt != null) {
1511             validateActiveScene();
1512             informListenerAboutStateUpdate(deviceStateUpdate);
1513         }
1514     }
1515
1516     private DeviceStateUpdate internalSetOutputValue(DeviceStateUpdate deviceStateUpdate) {
1517         if (deviceStateUpdate == null) {
1518             return null;
1519         }
1520         logger.debug("internal set outputvalue");
1521         switch (deviceStateUpdate.getType()) {
1522             case DeviceStateUpdate.OUTPUT_DECREASE:
1523                 return new DeviceStateUpdateImpl(DeviceStateUpdate.OUTPUT_DECREASE,
1524                         internalSetOutputValue(outputValue - getDimmStep()));
1525             case DeviceStateUpdate.OUTPUT_INCREASE:
1526                 return new DeviceStateUpdateImpl(DeviceStateUpdate.OUTPUT_INCREASE,
1527                         internalSetOutputValue(outputValue + getDimmStep()));
1528             case DeviceStateUpdate.OUTPUT:
1529                 internalSetOutputValue(deviceStateUpdate.getValueAsInteger());
1530                 break;
1531             case DeviceStateUpdate.ON_OFF:
1532                 if (deviceStateUpdate.getValueAsInteger() < 0) {
1533                     internalSetOutputValue(0);
1534                 } else {
1535                     internalSetOutputValue(maxOutputValue);
1536                 }
1537                 break;
1538             case DeviceStateUpdate.OPEN_CLOSE:
1539                 if (deviceStateUpdate.getValueAsInteger() < 0) {
1540                     internalSetOutputValue(0);
1541                 } else {
1542                     internalSetOutputValue(maxSlatPosition);
1543                 }
1544                 break;
1545             case DeviceStateUpdate.OPEN_CLOSE_ANGLE:
1546                 if (deviceStateUpdate.getValueAsInteger() < 0) {
1547                     internalSetAngleValue(0);
1548                 } else {
1549                     internalSetAngleValue(maxSlatAngle);
1550                 }
1551                 break;
1552             case DeviceStateUpdate.SLAT_DECREASE:
1553                 return new DeviceStateUpdateImpl(DeviceStateUpdate.SLAT_DECREASE,
1554                         internalSetOutputValue(slatPosition - getDimmStep()));
1555             case DeviceStateUpdate.SLAT_INCREASE:
1556                 return new DeviceStateUpdateImpl(DeviceStateUpdate.SLAT_INCREASE,
1557                         internalSetOutputValue(slatPosition + getDimmStep()));
1558             case DeviceStateUpdate.SLATPOSITION:
1559                 internalSetOutputValue(deviceStateUpdate.getValueAsInteger());
1560                 break;
1561             case DeviceStateUpdate.SLAT_ANGLE_DECREASE:
1562                 return new DeviceStateUpdateImpl(DeviceStateUpdate.SLAT_ANGLE_DECREASE,
1563                         internalSetAngleValue(slatAngle - DeviceConstants.ANGLE_STEP_SLAT));
1564             case DeviceStateUpdate.SLAT_ANGLE_INCREASE:
1565                 return new DeviceStateUpdateImpl(DeviceStateUpdate.SLAT_ANGLE_INCREASE,
1566                         internalSetAngleValue(slatAngle + DeviceConstants.ANGLE_STEP_SLAT));
1567             case DeviceStateUpdate.SLAT_ANGLE:
1568                 internalSetAngleValue(deviceStateUpdate.getValueAsInteger());
1569                 break;
1570             case DeviceStateUpdate.UPDATE_CALL_SCENE:
1571                 this.internalCallScene(deviceStateUpdate.getValueAsShort());
1572                 return null;
1573             case DeviceStateUpdate.UPDATE_UNDO_SCENE:
1574                 this.internalUndoScene();
1575                 return null;
1576             default:
1577                 if (deviceStateUpdate.isSensorUpdateType()) {
1578                     SensorEnum sensorType = deviceStateUpdate.getTypeAsSensorEnum();
1579                     setFloatSensorValue(sensorType, deviceStateUpdate.getValueAsFloat());
1580                 }
1581                 return null;
1582         }
1583         return deviceStateUpdate;
1584     }
1585
1586     private void validateActiveScene() {
1587         if (activeScene == null) {
1588             return;
1589         }
1590         Integer[] sceneOutput = getStandartSceneOutput(activeScene.getSceneID());
1591         if (sceneOutput == null) {
1592             sceneOutput = sceneOutputMap.get(activeScene.getSceneID());
1593         }
1594         if (sceneOutput != null) {
1595             boolean outputChanged = false;
1596             if (isShade()) {
1597                 if (isBlind() && sceneOutput[1] != slatAngle) {
1598                     logger.debug("Scene output angle: {} setted output value {}", sceneOutput[1], slatAngle);
1599                     outputChanged = true;
1600                 }
1601                 if (sceneOutput[0] != slatPosition) {
1602                     logger.debug("Scene output value: {} setted output value {}", sceneOutput[0], slatPosition);
1603                     outputChanged = true;
1604                 }
1605             } else {
1606                 if (sceneOutput[0] != outputValue) {
1607                     logger.debug("Scene output value: {} setted output value {}", sceneOutput[0], outputValue);
1608                     outputChanged = true;
1609                 }
1610             }
1611             if (outputChanged) {
1612                 logger.debug("Device output from Device with dSID {} changed deactivate scene {}", dsid.getValue(),
1613                         activeScene.getID());
1614                 activeScene.deviceSceneChanged((short) -1);
1615                 lastScene = null;
1616                 activeScene = null;
1617             }
1618         } else {
1619             lastScene = null;
1620         }
1621     }
1622
1623     @Override
1624     public DeviceStatusListener unregisterDeviceStatusListener() {
1625         setAllSensorDataRefreshPrioritiesToNever();
1626         return super.unregisterDeviceStatusListener();
1627     }
1628
1629     private void setCachedMeterData() {
1630         logger.debug("load cached sensor data device with dsid {}", dsid.getValue());
1631         Integer[] cachedSensorData = this.cachedSensorPowerValues.get(this.getOutputValue());
1632         if (cachedSensorData != null) {
1633             if (cachedSensorData[ACTIVE_POWER_ARRAY_FIELD] != null
1634                     && !checkPowerSensorRefreshPriorityNever(SensorEnum.ACTIVE_POWER)) {
1635                 informListenerAboutStateUpdate(new DeviceStateUpdateImpl(SensorEnum.ACTIVE_POWER,
1636                         (float) cachedSensorData[ACTIVE_POWER_ARRAY_FIELD]));
1637
1638             }
1639             if (cachedSensorData[OUTPUT_CURRENT_ARRAY_FIELD] != null
1640                     && !checkPowerSensorRefreshPriorityNever(SensorEnum.OUTPUT_CURRENT)) {
1641                 if (cachedSensorData[OUTPUT_CURRENT_ARRAY_FIELD] == SensorEnum.OUTPUT_CURRENT.getMax().intValue()
1642                         && devicePowerSensorTypes.contains(SensorEnum.OUTPUT_CURRENT_H)) {
1643                     informListenerAboutStateUpdate(new DeviceStateUpdateImpl(SensorEnum.OUTPUT_CURRENT,
1644                             cachedSensorData[OUTPUT_CURRENT_HIGH_ARRAY_FIELD]));
1645                 } else {
1646                     informListenerAboutStateUpdate(new DeviceStateUpdateImpl(SensorEnum.OUTPUT_CURRENT,
1647                             cachedSensorData[OUTPUT_CURRENT_ARRAY_FIELD]));
1648                 }
1649             }
1650             if (cachedSensorData[ACTIVE_POWER_ARRAY_FIELD] != null
1651                     && !checkPowerSensorRefreshPriorityNever(SensorEnum.POWER_CONSUMPTION)) {
1652                 informListenerAboutStateUpdate(
1653                         new DeviceStateUpdateImpl(SensorEnum.ACTIVE_POWER, cachedSensorData[ACTIVE_POWER_ARRAY_FIELD]));
1654             }
1655         }
1656     }
1657
1658     /**
1659      * if an {@link DeviceStatusListener} is registered inform him about the new state otherwise do nothing.
1660      *
1661      * @param deviceStateUpdate
1662      */
1663     private void informListenerAboutStateUpdate(DeviceStateUpdate deviceStateUpdate) {
1664         if (listener != null) {
1665             listener.onDeviceStateChanged(correctDeviceStatusUpdate(deviceStateUpdate));
1666         }
1667     }
1668
1669     private DeviceStateUpdate correctDeviceStatusUpdate(DeviceStateUpdate deviceStateUpdate) {
1670         if (isSwitch() && deviceStateUpdate.getType().equals(DeviceStateUpdate.OUTPUT)) {
1671             if (deviceStateUpdate.getValueAsInteger() >= switchPercentOff) {
1672                 return new DeviceStateUpdateImpl(DeviceStateUpdate.ON_OFF, DeviceStateUpdate.ON_VALUE);
1673             } else {
1674                 return new DeviceStateUpdateImpl(DeviceStateUpdate.ON_OFF, DeviceStateUpdate.OFF_VALUE);
1675             }
1676         }
1677         return deviceStateUpdate;
1678     }
1679
1680     private void informListenerAboutConfigChange(ChangeableDeviceConfigEnum changedConfig) {
1681         if (listener != null) {
1682             listener.onDeviceConfigChanged(changedConfig);
1683             logger.debug("Inform listener about device config {} changed", changedConfig.toString());
1684         }
1685     }
1686
1687     @SuppressWarnings("null")
1688     @Override
1689     public void saveConfigSceneSpecificationIntoDevice(Map<String, String> propertries) {
1690         if (propertries != null) {
1691             String sceneSave;
1692             for (String key : propertries.keySet()) {
1693                 if (key.startsWith(DigitalSTROMBindingConstants.DEVICE_SCENE)) {
1694                     try {
1695                         short sceneID = Short.parseShort((String) key
1696                                 .subSequence(DigitalSTROMBindingConstants.DEVICE_SCENE.length(), key.length()));
1697                         sceneSave = propertries.get(key);
1698                         if (StringUtils.isNotBlank(sceneSave)) {
1699                             logger.debug("Find saved scene configuration for device with dSID {} and sceneID {}", dsid,
1700                                     key);
1701                             String[] sceneParm = sceneSave.replace(" ", "").split(",");
1702                             JSONDeviceSceneSpecImpl sceneSpecNew = null;
1703                             int sceneValue = -1;
1704                             int sceneAngle = -1;
1705                             for (int j = 0; j < sceneParm.length; j++) {
1706                                 String[] sceneParmSplit = sceneParm[j].split(":");
1707                                 switch (sceneParmSplit[0]) {
1708                                     case "Scene":
1709                                         sceneSpecNew = new JSONDeviceSceneSpecImpl(sceneParmSplit[1]);
1710                                         break;
1711                                     case "dontcare":
1712                                         sceneSpecNew.setDontcare(Boolean.parseBoolean(sceneParmSplit[1]));
1713                                         break;
1714                                     case "localPrio":
1715                                         sceneSpecNew.setLocalPrio(Boolean.parseBoolean(sceneParmSplit[1]));
1716                                         break;
1717                                     case "specialMode":
1718                                         sceneSpecNew.setSpecialMode(Boolean.parseBoolean(sceneParmSplit[1]));
1719                                         break;
1720                                     case "sceneValue":
1721                                         sceneValue = Integer.parseInt(sceneParmSplit[1]);
1722                                         break;
1723                                     case "sceneAngle":
1724                                         sceneAngle = Integer.parseInt(sceneParmSplit[1]);
1725                                         break;
1726                                 }
1727                             }
1728                             if (sceneValue > -1) {
1729                                 logger.debug(
1730                                         "Saved sceneValue {}, sceneAngle {} for scene id {} into device with dsid {}",
1731                                         sceneValue, sceneAngle, sceneID, getDSID().getValue());
1732                                 internalSetSceneOutputValue(sceneID, sceneValue, sceneAngle);
1733                                 deviceStateUpdates.add(new DeviceStateUpdateImpl(DeviceStateUpdate.UPDATE_SCENE_OUTPUT,
1734                                         new Short[] { sceneID, (short) -1 }));
1735                             }
1736                             if (sceneSpecNew != null) {
1737                                 logger.debug("Saved sceneConfig: [{}] for scene id {} into device with dsid {}",
1738                                         sceneSpecNew.toString(), sceneID, getDSID().getValue());
1739                                 synchronized (sceneConfigMap) {
1740                                     sceneConfigMap.put(sceneID, sceneSpecNew);
1741                                 }
1742                                 deviceStateUpdates.add(new DeviceStateUpdateImpl(DeviceStateUpdate.UPDATE_SCENE_CONFIG,
1743                                         new Short[] { sceneID, (short) -1 }));
1744                             }
1745                         }
1746                     } catch (NumberFormatException e) {
1747                         // ignore
1748                     }
1749                 }
1750             }
1751         }
1752     }
1753
1754     @SuppressWarnings("null")
1755     @Override
1756     public void saveConfigSceneSpecificationIntoDevice(String propertries) {
1757         String[] scenes = propertries.split("\n");
1758         for (int i = 0; i < scenes.length; i++) {
1759             logger.debug("Find saved scene configuration for device with dSID {} and sceneID {}", dsid, i);
1760             String[] sceneIdToConfig = scenes[i].replaceAll(" ", "").split("=");
1761             String[] sceneParm = sceneIdToConfig[1].split(",");
1762             JSONDeviceSceneSpecImpl sceneSpecNew = null;
1763             int sceneValue = -1;
1764             int sceneAngle = -1;
1765             for (int j = 0; j < sceneParm.length; j++) {
1766                 String[] sceneParmSplit = sceneParm[j].split(":");
1767                 switch (sceneParmSplit[0]) {
1768                     case "Scene":
1769                         sceneSpecNew = new JSONDeviceSceneSpecImpl(sceneParmSplit[1]);
1770                         break;
1771                     case "dontcare":
1772                         sceneSpecNew.setDontcare(Boolean.parseBoolean(sceneParmSplit[1]));
1773                         break;
1774                     case "localPrio":
1775                         sceneSpecNew.setLocalPrio(Boolean.parseBoolean(sceneParmSplit[1]));
1776                         break;
1777                     case "specialMode":
1778                         sceneSpecNew.setSpecialMode(Boolean.parseBoolean(sceneParmSplit[1]));
1779                         break;
1780                     case "sceneValue":
1781                         sceneValue = Integer.parseInt(sceneParmSplit[1]);
1782                         break;
1783                     case "sceneAngle":
1784                         sceneAngle = Integer.parseInt(sceneParmSplit[1]);
1785                         break;
1786                 }
1787             }
1788             if (sceneValue > -1) {
1789                 logger.debug("Saved sceneValue {}, sceneAngle {} for scene id {} into device with dsid {}", sceneValue,
1790                         sceneAngle, i, getDSID().getValue());
1791                 synchronized (sceneOutputMap) {
1792                     sceneOutputMap.put(sceneSpecNew.getScene().getSceneNumber(),
1793                             new Integer[] { sceneValue, sceneAngle });
1794                 }
1795             }
1796             if (sceneSpecNew != null) {
1797                 logger.debug("Saved sceneConfig: [{}] for scene id {} into device with dsid {}",
1798                         sceneSpecNew.toString(), i, getDSID().getValue());
1799                 synchronized (sceneConfigMap) {
1800                     sceneConfigMap.put(sceneSpecNew.getScene().getSceneNumber(), sceneSpecNew);
1801                 }
1802             }
1803         }
1804     }
1805
1806     @Override
1807     public void setConfig(Config config) {
1808         this.config = config;
1809     }
1810
1811     private String powerSensorRefreshToString() {
1812         String powSenRef = "";
1813         for (int i = 0; i < powerSensorRefresh.length; i++) {
1814             powSenRef = powSenRef + " [" + i + "]=Prio: "
1815                     + ((String[]) powerSensorRefresh[i])[REFRESH_PRIORITY_ARRAY_FIELD] + ", Initialized: "
1816                     + ((String[]) powerSensorRefresh[i])[READING_INITIALIZED_ARRAY_FIELD] + " ";
1817         }
1818         return powSenRef;
1819     }
1820
1821     @Override
1822     public boolean isBinaryInputDevice() {
1823         return !deviceBinaryInputs.isEmpty();
1824     }
1825
1826     @Override
1827     public List<DeviceBinaryInput> getBinaryInputs() {
1828         return deviceBinaryInputs;
1829     }
1830
1831     @Override
1832     public DeviceBinaryInput getBinaryInput(DeviceBinarayInputEnum binaryInputType) {
1833         if (binaryInputType != null) {
1834             for (DeviceBinaryInput binInput : deviceBinaryInputs) {
1835                 if (binaryInputType.getBinaryInputType().equals(binInput.getInputType())) {
1836                     return binInput;
1837                 }
1838             }
1839         }
1840         return null;
1841     }
1842
1843     @Override
1844     public Short getBinaryInputState(DeviceBinarayInputEnum binaryInputType) {
1845         DeviceBinaryInput devBinInput = getBinaryInput(binaryInputType);
1846         if (devBinInput != null) {
1847             return devBinInput.getState();
1848         }
1849         return null;
1850     }
1851
1852     @Override
1853     public boolean setBinaryInputState(DeviceBinarayInputEnum binaryInputType, Short newState) {
1854         DeviceBinaryInput devBinInput = getBinaryInput(binaryInputType);
1855         if (devBinInput != null) {
1856             devBinInput.setState(newState);
1857             informListenerAboutStateUpdate(new DeviceStateUpdateImpl(binaryInputType, newState));
1858             return true;
1859         }
1860         return false;
1861     }
1862
1863     @Override
1864     public void setBinaryInputs(List<DeviceBinaryInput> newBinaryInputs) {
1865         this.deviceBinaryInputs.clear();
1866         this.deviceBinaryInputs.addAll(newBinaryInputs);
1867         informListenerAboutConfigChange(ChangeableDeviceConfigEnum.BINARY_INPUTS);
1868     }
1869
1870     @Override
1871     public String toString() {
1872         return "DeviceImpl [meterDSID=" + meterDSID + ", zoneId=" + zoneId + ", groupList=" + groupList
1873                 + ", functionalGroup=" + functionalGroup + ", functionalName=" + functionalName + ", hwInfo=" + hwInfo
1874                 + ", getName()=" + getName() + ", getDSID()=" + getDSID() + ", getDSUID()=" + getDSUID()
1875                 + ", isPresent()=" + isPresent() + ", isValide()=" + isValid() + ", getDisplayID()=" + getDisplayID()
1876                 + ", outputMode=" + outputMode + ", getSensorTypes()=" + getSensorTypes() + ", getDeviceSensorValues()="
1877                 + getDeviceSensorValues() + ", powerSensorRefresh=" + powerSensorRefreshToString() + "]";
1878     }
1879 }