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