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