2 * Copyright (c) 2010-2021 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
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
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.binding.digitalstrom.internal.lib.structure.devices.impl;
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;
22 import java.util.Map.Entry;
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.ApplicationGroup;
37 import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.constants.ChangeableDeviceConfigEnum;
38 import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.constants.DeviceBinarayInputEnum;
39 import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.constants.OutputChannelEnum;
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.openhab.binding.digitalstrom.internal.lib.util.DSJsonParser;
50 import org.slf4j.Logger;
51 import org.slf4j.LoggerFactory;
53 import com.google.gson.JsonArray;
54 import com.google.gson.JsonElement;
55 import com.google.gson.JsonObject;
58 * The {@link DeviceImpl} is the implementation of the {@link Device}.
60 * @author Michael Ochel - Initial contribution
61 * @author Matthias Siegele - Initial contribution
63 public class DeviceImpl extends AbstractGeneralDeviceInformations implements Device {
65 private final Logger logger = LoggerFactory.getLogger(DeviceImpl.class);
67 private Config config;
69 private DSID meterDSID;
70 private int zoneId = 0;
71 private List<ApplicationGroup> groupList = new LinkedList<>();
73 private String hwInfo;
75 private OutputModeEnum outputMode;
77 private boolean isOn = false;
78 private boolean isOpen = true;
79 private short outputValue = 0;
80 private short maxOutputValue = DeviceConstants.DEFAULT_MAX_OUTPUTVALUE;
81 private short minOutputValue = 0;
83 private short slatAngle = 0;
84 private final short maxSlatAngle = DeviceConstants.MAX_SLAT_ANGLE;
85 private final short minSlatAngle = DeviceConstants.MIN_SLAT_ANGLE;
87 private int slatPosition = 0;
88 private final int maxSlatPosition = DeviceConstants.MAX_ROLLERSHUTTER;
89 private final int minSlatPosition = DeviceConstants.MIN_ROLLERSHUTTER;
91 private final List<DeviceSensorValue> deviceSensorValues = Collections.synchronizedList(new ArrayList<>());
92 private final List<DeviceBinaryInput> deviceBinaryInputs = Collections.synchronizedList(new ArrayList<>());
93 private final List<SensorEnum> devicePowerSensorTypes = new ArrayList<>();
94 private final List<SensorEnum> deviceClimateSensorTypes = new ArrayList<>();
95 private final List<OutputChannelEnum> outputChannels = Collections.synchronizedList(new ArrayList<>());
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;
105 private final Map<Short, DeviceSceneSpec> sceneConfigMap = Collections.synchronizedMap(new HashMap<>());
106 private final Map<Short, Integer[]> sceneOutputMap = Collections.synchronizedMap(new HashMap<>());
108 // saves outstanding commands
109 private final List<DeviceStateUpdate> deviceStateUpdates = Collections.synchronizedList(new LinkedList<>());
112 * Saves the refresh priorities and reading initialized flag of power sensors as
113 * an matrix. The first array fields are 0 = active power, 1 = output current, 2
114 * = electric meter, 3 = power consumption and in each field is a string array
115 * with the fields 0 = refresh priority 1 = reading initial flag (true = reading
116 * is initialized, otherwise false)
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" } };
123 public static final int REFRESH_PRIORITY_ARRAY_FIELD = 0;
124 public static final int READING_INITIALIZED_ARRAY_FIELD = 1;
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;
131 * Cache the last power sensor value to get power sensor value directly the key
132 * is the output value and the value is an Integer array for the sensor values
133 * (0 = active power, 1 = output current, 2 = power consumption, 3 = output
136 private final Map<Short, Integer[]> cachedSensorPowerValues = Collections.synchronizedMap(new HashMap<>());
138 public static final int ACTIVE_POWER_ARRAY_FIELD = 0;
139 public static final int OUTPUT_CURRENT_ARRAY_FIELD = 1;
140 public static final int POWER_CONSUMPTION_ARRAY_FIELD = 2;
141 public static final int OUTPUT_CURRENT_HIGH_ARRAY_FIELD = 3;
143 // Preparing for the advance device property setting "Turn 'switched' output off
144 // if value below:", but the
145 // configuration currently not work in digitalSTROM, because of that the value
147 private final int switchPercentOff = 1;
150 * Creates a new {@link DeviceImpl} from the given DigitalSTROM-Device
151 * {@link JsonObject}.
153 * @param deviceJsonObject json response of the digitalSTROM-Server, must not be
156 public DeviceImpl(JsonObject deviceJsonObject) {
157 super(deviceJsonObject);
158 if (deviceJsonObject.get(JSONApiResponseKeysEnum.METER_DSID.getKey()) != null) {
159 this.meterDSID = new DSID(deviceJsonObject.get(JSONApiResponseKeysEnum.METER_DSID.getKey()).getAsString());
160 } else if (deviceJsonObject.get(JSONApiResponseKeysEnum.DS_METER_DSID.getKey()) != null) {
161 this.meterDSID = new DSID(
162 deviceJsonObject.get(JSONApiResponseKeysEnum.DS_METER_DSID.getKey()).getAsString());
164 if (deviceJsonObject.get(JSONApiResponseKeysEnum.HW_INFO.getKey()) != null) {
165 this.hwInfo = deviceJsonObject.get(JSONApiResponseKeysEnum.HW_INFO.getKey()).getAsString();
166 } else if (deviceJsonObject.get(JSONApiResponseKeysEnum.HW_INFO_UPPER_HW.getKey()) != null) {
167 this.hwInfo = deviceJsonObject.get(JSONApiResponseKeysEnum.HW_INFO_UPPER_HW.getKey()).getAsString();
169 if (deviceJsonObject.get(JSONApiResponseKeysEnum.ON.getKey()) != null) {
171 this.isOn = deviceJsonObject.get(JSONApiResponseKeysEnum.ON.getKey()).getAsBoolean();
173 this.isOpen = deviceJsonObject.get(JSONApiResponseKeysEnum.ON.getKey()).getAsBoolean();
176 if (deviceJsonObject.get(JSONApiResponseKeysEnum.ZONE_ID.getKey()) != null) {
177 zoneId = deviceJsonObject.get(JSONApiResponseKeysEnum.ZONE_ID.getKey()).getAsInt();
178 } else if (deviceJsonObject.get(JSONApiResponseKeysEnum.ZONE_ID_Lower_Z.getKey()) != null) {
179 zoneId = deviceJsonObject.get(JSONApiResponseKeysEnum.ZONE_ID_Lower_Z.getKey()).getAsInt();
181 JsonElement groups = deviceJsonObject.get(JSONApiResponseKeysEnum.GROUPS.getKey());
182 if (groups != null && groups.isJsonArray()) {
183 JsonArray array = deviceJsonObject.get(JSONApiResponseKeysEnum.GROUPS.getKey()).getAsJsonArray();
184 for (int i = 0; i < array.size(); i++) {
185 if (array.get(i) != null) {
186 addGroupToList(array.get(i).getAsShort());
189 } else if (groups != null && groups.isJsonObject()) {
190 for (Entry<String, JsonElement> entry : deviceJsonObject.get(JSONApiResponseKeysEnum.GROUPS.getKey())
191 .getAsJsonObject().entrySet()) {
193 entry.getValue().getAsJsonObject().get(JSONApiResponseKeysEnum.ID.getKey()).getAsShort());
196 if (deviceJsonObject.get(JSONApiResponseKeysEnum.OUTPUT_MODE.getKey()) != null) {
197 int tmp = deviceJsonObject.get(JSONApiResponseKeysEnum.OUTPUT_MODE.getKey()).getAsInt();
199 if (OutputModeEnum.containsMode(tmp)) {
200 outputMode = OutputModeEnum.getMode(tmp);
204 if (deviceJsonObject.get(JSONApiResponseKeysEnum.SENSOR_INPUTS.getKey()) != null
205 && deviceJsonObject.get(JSONApiResponseKeysEnum.SENSOR_INPUTS.getKey()).isJsonObject()) {
206 JsonObject jObj = deviceJsonObject.get(JSONApiResponseKeysEnum.SENSOR_INPUTS.getKey()).getAsJsonObject();
207 for (Entry<String, JsonElement> entry : jObj.entrySet()) {
208 if (entry.getValue().isJsonObject()) {
209 JsonObject sensorType = entry.getValue().getAsJsonObject();
210 if (sensorType.get(JSONApiResponseKeysEnum.TYPE.getKey()) != null) {
212 .containsSensor(sensorType.get(JSONApiResponseKeysEnum.TYPE.getKey()).getAsShort())) {
213 setDeviceSensorValue(new DeviceSensorValue(entry.getValue().getAsJsonObject()));
219 if (deviceJsonObject.get(JSONApiResponseKeysEnum.BINARY_INPUTS.getKey()) != null
220 && deviceJsonObject.get(JSONApiResponseKeysEnum.BINARY_INPUTS.getKey()).isJsonObject()) {
221 JsonObject jObj = deviceJsonObject.get(JSONApiResponseKeysEnum.BINARY_INPUTS.getKey()).getAsJsonObject();
222 for (Entry<String, JsonElement> entry : jObj.entrySet()) {
223 if (entry.getValue().isJsonObject()) {
224 JsonObject binaryInput = entry.getValue().getAsJsonObject();
225 deviceBinaryInputs.add(new DeviceBinaryInput(binaryInput));
230 outputChannels.addAll(DSJsonParser.getOutputChannels(deviceJsonObject));
235 private void init() {
236 if (groupList.contains(ApplicationGroup.LIGHTS)) {
237 maxOutputValue = DeviceConstants.MAX_OUTPUT_VALUE_LIGHT;
238 if (this.isDimmable()) {
239 minOutputValue = DeviceConstants.MIN_DIM_VALUE;
242 maxOutputValue = DeviceConstants.DEFAULT_MAX_OUTPUTVALUE;
246 outputValue = DeviceConstants.DEFAULT_MAX_OUTPUTVALUE;
251 public synchronized DSID getMeterDSID() {
252 return this.meterDSID;
256 public synchronized void setMeterDSID(String meterDSID) {
257 this.meterDSID = new DSID(meterDSID);
258 informListenerAboutConfigChange(ChangeableDeviceConfigEnum.METER_DSID);
262 public String getHWinfo() {
267 public List<Short> getGroups() {
268 LinkedList<Short> linkedList = new LinkedList<>();
269 groupList.forEach(group -> linkedList.add(group.getId()));
274 * Adds the ApplicationGroup of the given groupId to the internal list
277 * @return true if the groupId is a valid ApplicationGroup id, false otherwise
279 private boolean addGroupToList(Short groupID) {
280 ApplicationGroup group = ApplicationGroup.getGroup(groupID);
281 if (!ApplicationGroup.UNDEFINED.equals(group)) {
282 if (!this.groupList.contains(group)) {
283 this.groupList.add(group);
287 return group != null;
291 public void addGroup(Short groupID) {
292 addGroupToList(groupID);
293 informListenerAboutConfigChange(ChangeableDeviceConfigEnum.GROUPS);
297 public void setGroups(List<Short> newGroupList) {
298 if (newGroupList != null) {
300 newGroupList.forEach(groupId -> groupList.add(ApplicationGroup.getGroup(groupId)));
302 informListenerAboutConfigChange(ChangeableDeviceConfigEnum.GROUPS);
306 public synchronized int getZoneId() {
311 public synchronized void setZoneId(int zoneID) {
312 this.zoneId = zoneID;
313 informListenerAboutConfigChange(ChangeableDeviceConfigEnum.ZONE_ID);
317 public synchronized boolean isOn() {
322 public synchronized void setIsOn(boolean flag) {
324 this.deviceStateUpdates.add(new DeviceStateUpdateImpl(DeviceStateUpdate.ON_OFF, 1));
326 this.deviceStateUpdates.add(new DeviceStateUpdateImpl(DeviceStateUpdate.ON_OFF, -1));
331 public synchronized boolean isOpen() {
336 public synchronized void setIsOpen(boolean flag) {
338 this.deviceStateUpdates.add(new DeviceStateUpdateImpl(DeviceStateUpdate.OPEN_CLOSE, 1));
340 this.deviceStateUpdates.add(new DeviceStateUpdateImpl(DeviceStateUpdate.OPEN_CLOSE_ANGLE, 1));
343 this.deviceStateUpdates.add(new DeviceStateUpdateImpl(DeviceStateUpdate.OPEN_CLOSE, -1));
345 this.deviceStateUpdates.add(new DeviceStateUpdateImpl(DeviceStateUpdate.OPEN_CLOSE_ANGLE, -1));
351 public synchronized void setOutputValue(short value) {
354 this.deviceStateUpdates.add(new DeviceStateUpdateImpl(DeviceStateUpdate.ON_OFF, -1));
356 } else if (value > maxOutputValue) {
357 this.deviceStateUpdates.add(new DeviceStateUpdateImpl(DeviceStateUpdate.ON_OFF, 1));
359 this.deviceStateUpdates.add(new DeviceStateUpdateImpl(DeviceStateUpdate.OUTPUT, value));
365 public synchronized boolean isDimmable() {
366 return OutputModeEnum.outputModeIsDimmable(outputMode);
370 public synchronized boolean isSwitch() {
371 return OutputModeEnum.outputModeIsSwitch(outputMode);
375 public synchronized boolean isDeviceWithOutput() {
376 return this.outputMode != null && !this.outputMode.equals(OutputModeEnum.DISABLED);
380 public boolean isSensorDevice() {
381 return !isDeviceWithOutput() && !deviceClimateSensorTypes.isEmpty();
385 public boolean isHeatingDevice() {
386 return groupList.contains(ApplicationGroup.HEATING);
390 public boolean isTemperatureControlledDevice() {
391 return groupList.contains(ApplicationGroup.TEMPERATURE_CONTROL);
395 public boolean isShade() {
396 return OutputModeEnum.outputModeIsShade(outputMode);
400 public boolean isBlind() {
401 return (outputMode == OutputModeEnum.POSITION_CON || outputMode == OutputModeEnum.POSITION_CON_US)
402 && (outputChannels.contains(OutputChannelEnum.SHADE_OPENING_ANGLE_INDOOR)
403 || outputChannels.contains(OutputChannelEnum.SHADE_OPENING_ANGLE_OUTSIDE));
407 public synchronized ApplicationGroup getFunctionalColorGroup() {
408 return groupList.stream().findFirst().get();
412 public synchronized void setFunctionalColorGroup(ApplicationGroup functionalColorGroup) {
413 groupList.add(functionalColorGroup);
414 informListenerAboutConfigChange(ChangeableDeviceConfigEnum.FUNCTIONAL_GROUP);
418 public OutputModeEnum getOutputMode() {
423 public List<OutputChannelEnum> getOutputChannels() {
424 return outputChannels;
428 public synchronized void setOutputMode(OutputModeEnum newOutputMode) {
429 this.outputMode = newOutputMode;
430 informListenerAboutConfigChange(ChangeableDeviceConfigEnum.OUTPUT_MODE);
434 public synchronized void increase() {
436 deviceStateUpdates.add(new DeviceStateUpdateImpl(DeviceStateUpdate.OUTPUT_INCREASE, 0));
439 this.deviceStateUpdates.add(new DeviceStateUpdateImpl(DeviceStateUpdate.SLAT_INCREASE, 0));
441 this.deviceStateUpdates.add(new DeviceStateUpdateImpl(DeviceStateUpdate.SLAT_ANGLE_DECREASE, 0));
447 public synchronized void decrease() {
449 this.deviceStateUpdates.add(new DeviceStateUpdateImpl(DeviceStateUpdate.OUTPUT_DECREASE, 0));
452 this.deviceStateUpdates.add(new DeviceStateUpdateImpl(DeviceStateUpdate.SLAT_DECREASE, 0));
454 this.deviceStateUpdates.add(new DeviceStateUpdateImpl(DeviceStateUpdate.SLAT_ANGLE_DECREASE, 0));
460 public synchronized void increaseSlatAngle() {
462 this.deviceStateUpdates.add(new DeviceStateUpdateImpl(DeviceStateUpdate.SLAT_ANGLE_DECREASE, 1));
467 public synchronized void decreaseSlatAngle() {
469 this.deviceStateUpdates.add(new DeviceStateUpdateImpl(DeviceStateUpdate.SLAT_ANGLE_DECREASE, 1));
474 public synchronized short getOutputValue() {
479 public short getMaxOutputValue() {
480 return maxOutputValue;
484 public short getMinOutputValue() {
485 return minOutputValue;
489 public synchronized int getSlatPosition() {
494 public synchronized short getAnglePosition() {
499 public synchronized void setAnglePosition(int angle) {
500 if (angle == slatAngle) {
503 if (angle < minSlatAngle) {
504 this.deviceStateUpdates.add(new DeviceStateUpdateImpl(DeviceStateUpdate.SLAT_ANGLE, minSlatAngle));
505 } else if (angle > this.maxSlatPosition) {
506 this.deviceStateUpdates.add(new DeviceStateUpdateImpl(DeviceStateUpdate.SLAT_ANGLE, maxSlatAngle));
508 this.deviceStateUpdates.add(new DeviceStateUpdateImpl(DeviceStateUpdate.SLAT_ANGLE, angle));
513 public synchronized void setSlatPosition(int position) {
514 if (position == this.slatPosition) {
517 if (position < minSlatPosition) {
518 this.deviceStateUpdates.add(new DeviceStateUpdateImpl(DeviceStateUpdate.SLATPOSITION, minSlatPosition));
519 } else if (position > this.maxSlatPosition) {
520 this.deviceStateUpdates.add(new DeviceStateUpdateImpl(DeviceStateUpdate.SLATPOSITION, maxSlatPosition));
522 this.deviceStateUpdates.add(new DeviceStateUpdateImpl(DeviceStateUpdate.SLATPOSITION, position));
526 private short getDimmStep() {
528 return DeviceConstants.DIM_STEP_LIGHT;
529 } else if (isShade()) {
530 return DeviceConstants.MOVE_STEP_ROLLERSHUTTER;
532 return DeviceConstants.DEFAULT_MOVE_STEP;
537 public int getMaxSlatPosition() {
538 return maxSlatPosition;
542 public int getMinSlatPosition() {
543 return minSlatPosition;
547 public int getMaxSlatAngle() {
552 public int getMinSlatAngle() {
559 public synchronized void callInternalScene(InternalScene scene) {
560 if (lastCallWasUndo) {
562 if (activeScene != null) {
563 activeScene.deactivateSceneByDevice();
567 internalCallScene(scene.getSceneID());
569 lastCallWasUndo = false;
573 public void checkSceneConfig(Short sceneNumber, short prio) {
574 if (isDeviceWithOutput()) {
575 if (!containsSceneConfig(sceneNumber)) {
576 deviceStateUpdates.add(new DeviceStateUpdateImpl(DeviceStateUpdate.UPDATE_SCENE_CONFIG,
577 new Short[] { sceneNumber, prio }));
580 if (sceneOutputMap.get(sceneNumber) == null) {
581 deviceStateUpdates.add(new DeviceStateUpdateImpl(DeviceStateUpdate.UPDATE_SCENE_OUTPUT,
582 new Short[] { sceneNumber, prio }));
588 public synchronized void undoInternalScene(InternalScene scene) {
589 logger.debug("undo Scene {} dSID {}", scene.getSceneID(), dsid.getValue());
590 if (activeScene != null && activeScene.equals(scene)) {
591 if (lastCallWasUndo) {
595 if (this.lastScene != null && !lastScene.equals(activeScene)) {
596 activeScene = lastScene;
598 activeScene.activateSceneByDevice();
601 logger.debug("internalUndo Scene dSID {}", dsid.getValue());
602 this.activeScene = null;
605 lastCallWasUndo = true;
610 public synchronized void callScene(Short sceneNumber) {
611 this.deviceStateUpdates.add(new DeviceStateUpdateImpl(DeviceStateUpdate.UPDATE_CALL_SCENE, sceneNumber));
615 public synchronized void internalCallScene(Short sceneNumber) {
616 logger.debug("call Scene id {} dSID {}", sceneNumber, dsid.getValue());
617 if (isDeviceWithOutput()) {
618 activeSceneNumber = sceneNumber;
619 informLastSceneAboutSceneCall(sceneNumber);
621 outputValueBeforeSceneCall = this.outputValue;
623 outputValueBeforeSceneCall = this.slatPosition;
625 slatAngleBeforeSceneCall = this.slatAngle;
628 if (!checkSceneNumber(sceneNumber)) {
629 if (containsSceneConfig(sceneNumber)) {
630 if (doIgnoreScene(sceneNumber)) {
634 this.deviceStateUpdates.add(new DeviceStateUpdateImpl(DeviceStateUpdate.UPDATE_SCENE_CONFIG,
635 new Short[] { sceneNumber, GeneralLibConstance.HIGHEST_READ_OUT_PRIORITY }));
637 if (sceneOutputMap.get(sceneNumber) != null) {
639 updateInternalDeviceState(new DeviceStateUpdateImpl(DeviceStateUpdate.OUTPUT,
640 sceneOutputMap.get(sceneNumber)[GeneralLibConstance.SCENE_ARRAY_INDEX_VALUE]));
642 updateInternalDeviceState(new DeviceStateUpdateImpl(DeviceStateUpdate.SLATPOSITION,
643 sceneOutputMap.get(sceneNumber)[GeneralLibConstance.SCENE_ARRAY_INDEX_VALUE]));
645 updateInternalDeviceState(new DeviceStateUpdateImpl(DeviceStateUpdate.SLAT_ANGLE,
646 sceneOutputMap.get(sceneNumber)[GeneralLibConstance.SCENE_ARRAY_INDEX_ANGLE]));
650 this.deviceStateUpdates.add(new DeviceStateUpdateImpl(DeviceStateUpdate.UPDATE_SCENE_OUTPUT,
651 new Short[] { sceneNumber, GeneralLibConstance.HIGHEST_READ_OUT_PRIORITY }));
658 private boolean checkSceneNumber(Short sceneNumber) {
659 if (SceneEnum.containsScene(sceneNumber)) {
660 if (this.outputMode.equals(OutputModeEnum.POWERSAVE)) {
661 switch (SceneEnum.getScene(sceneNumber)) {
665 this.updateInternalDeviceState(new DeviceStateUpdateImpl(DeviceStateUpdate.ON_OFF, -1));
681 if (this.outputMode.equals(OutputModeEnum.WIPE)) {
682 switch (SceneEnum.getScene(sceneNumber)) {
694 this.updateInternalDeviceState(new DeviceStateUpdateImpl(DeviceStateUpdate.ON_OFF, -1));
700 switch (SceneEnum.getScene(sceneNumber)) {
705 this.updateInternalDeviceState(new DeviceStateUpdateImpl(DeviceStateUpdate.ON_OFF, 1));
707 this.updateInternalDeviceState(new DeviceStateUpdateImpl(DeviceStateUpdate.OPEN_CLOSE, 1));
709 this.updateInternalDeviceState(
710 new DeviceStateUpdateImpl(DeviceStateUpdate.OPEN_CLOSE_ANGLE, 1));
719 this.updateInternalDeviceState(new DeviceStateUpdateImpl(DeviceStateUpdate.ON_OFF, -1));
721 this.updateInternalDeviceState(new DeviceStateUpdateImpl(DeviceStateUpdate.OPEN_CLOSE, -1));
723 this.updateInternalDeviceState(
724 new DeviceStateUpdateImpl(DeviceStateUpdate.OPEN_CLOSE_ANGLE, -1));
730 case AREA_1_INCREMENT:
731 case AREA_2_INCREMENT:
732 case AREA_3_INCREMENT:
733 case AREA_4_INCREMENT:
735 if (outputValue == maxOutputValue) {
738 this.updateInternalDeviceState(new DeviceStateUpdateImpl(DeviceStateUpdate.OUTPUT_INCREASE, 0));
741 if (slatPosition == maxSlatPosition) {
744 this.updateInternalDeviceState(new DeviceStateUpdateImpl(DeviceStateUpdate.SLAT_INCREASE, 0));
746 if (slatAngle == maxSlatAngle) {
749 updateInternalDeviceState(
750 new DeviceStateUpdateImpl(DeviceStateUpdate.SLAT_ANGLE_INCREASE, 0));
756 case AREA_1_DECREMENT:
757 case AREA_2_DECREMENT:
758 case AREA_3_DECREMENT:
759 case AREA_4_DECREMENT:
761 if (outputValue == minOutputValue) {
764 updateInternalDeviceState(new DeviceStateUpdateImpl(DeviceStateUpdate.OUTPUT_DECREASE, 0));
767 if (slatPosition == minSlatPosition) {
770 updateInternalDeviceState(new DeviceStateUpdateImpl(DeviceStateUpdate.SLAT_DECREASE, 0));
772 if (slatAngle == minSlatAngle) {
775 this.updateInternalDeviceState(
776 new DeviceStateUpdateImpl(DeviceStateUpdate.SLAT_ANGLE_INCREASE, 0));
787 this.deviceStateUpdates.add(new DeviceStateUpdateImpl(DeviceStateUpdate.UPDATE_OUTPUT_VALUE, 0));
789 // Area Stepping continue scenes
790 case AREA_STEPPING_CONTINUE:
799 private Integer[] getStandartSceneOutput(short sceneNumber) {
800 if (SceneEnum.getScene(sceneNumber) != null) {
801 switch (SceneEnum.getScene(sceneNumber)) {
805 return new Integer[] { (int) maxOutputValue, -1 };
808 return new Integer[] { (int) maxSlatPosition, (int) maxSlatAngle };
810 return new Integer[] { (int) maxSlatPosition, -1 };
818 return new Integer[] { (int) 0, -1 };
821 return new Integer[] { (int) 0, 0 };
823 return new Integer[] { (int) 0, -1 };
833 private void informLastSceneAboutSceneCall(short sceneNumber) {
834 if (this.activeScene != null && this.activeScene.getSceneID() != sceneNumber) {
835 this.activeScene.deactivateSceneByDevice();
836 this.lastScene = this.activeScene;
837 this.activeScene = null;
842 public synchronized void undoScene() {
843 this.deviceStateUpdates
844 .add(new DeviceStateUpdateImpl(DeviceStateUpdate.UPDATE_UNDO_SCENE, this.activeSceneNumber));
848 public synchronized void internalUndoScene() {
850 updateInternalDeviceState(
851 new DeviceStateUpdateImpl(DeviceStateUpdate.OUTPUT, this.outputValueBeforeSceneCall));
853 updateInternalDeviceState(
854 new DeviceStateUpdateImpl(DeviceStateUpdate.SLATPOSITION, this.outputValueBeforeSceneCall));
856 updateInternalDeviceState(
857 new DeviceStateUpdateImpl(DeviceStateUpdate.SLAT_ANGLE, this.slatAngleBeforeSceneCall));
861 if (activeSceneNumber != -1) {
862 activeSceneNumber = -1;
867 public InternalScene getAcitiveScene() {
868 return this.activeScene;
872 public Integer[] getSceneOutputValue(short sceneId) {
873 synchronized (sceneOutputMap) {
874 if (sceneOutputMap.containsKey(sceneId)) {
875 return sceneOutputMap.get(sceneId);
878 return new Integer[] { -1, -1 };
882 public void setSceneOutputValue(short sceneId, int value) {
883 internalSetSceneOutputValue(sceneId, value, -1);
884 if (listener != null) {
885 listener.onSceneConfigAdded(sceneId);
890 public void setSceneOutputValue(short sceneId, int value, int angle) {
891 internalSetSceneOutputValue(sceneId, value, angle);
892 if (listener != null) {
893 listener.onSceneConfigAdded(sceneId);
897 private void internalSetSceneOutputValue(short sceneId, int value, int angle) {
898 synchronized (sceneOutputMap) {
899 sceneOutputMap.put(sceneId, new Integer[] { value, angle });
901 if (activeSceneNumber == sceneId) {
902 internalCallScene(sceneId);
907 public List<Short> getSavedScenes() {
908 Set<Short> bothKeySet = new HashSet<>(sceneOutputMap.keySet());
909 bothKeySet.addAll(sceneConfigMap.keySet());
910 return new LinkedList<>(bothKeySet);
914 public void addSceneConfig(short sceneId, DeviceSceneSpec sceneSpec) {
915 if (sceneSpec != null) {
916 synchronized (sceneConfigMap) {
917 sceneConfigMap.put(sceneId, sceneSpec);
918 if (listener != null) {
919 listener.onSceneConfigAdded(sceneId);
926 public DeviceSceneSpec getSceneConfig(short sceneId) {
927 synchronized (sceneConfigMap) {
928 return sceneConfigMap.get(sceneId);
933 public boolean doIgnoreScene(short sceneId) {
934 synchronized (sceneConfigMap) {
935 if (this.sceneConfigMap.containsKey(sceneId)) {
936 return this.sceneConfigMap.get(sceneId).isDontCare();
943 public boolean containsSceneConfig(short sceneId) {
944 synchronized (sceneConfigMap) {
945 return sceneConfigMap.containsKey(sceneId);
952 public boolean equals(Object obj) {
953 if (obj instanceof Device) {
954 Device device = (Device) obj;
955 return device.getDSID().equals(this.getDSID());
961 public int hashCode() {
962 return this.getDSID().hashCode();
966 public boolean isPowerSensorUpToDate(SensorEnum powerSensorType) {
967 if (powerSensorType != null && SensorEnum.isPowerSensor(powerSensorType)) {
968 boolean isUpToDate = true;
969 if (powerSensorType.equals(SensorEnum.ACTIVE_POWER)) {
970 isUpToDate = (outputMode != null && outputMode.equals(OutputModeEnum.WIPE) && !isOn)
971 || (isOn && !isShade()) && !checkPowerSensorRefreshPriorityNever(powerSensorType)
972 ? checkSensorRefreshTime(powerSensorType)
975 if (powerSensorType.equals(SensorEnum.ELECTRIC_METER)) {
976 isUpToDate = (isOn || getDeviceSensorValue(powerSensorType).getDsValue() == 0) && !isShade()
977 && !checkPowerSensorRefreshPriorityNever(powerSensorType)
978 ? checkSensorRefreshTime(powerSensorType)
981 isUpToDate = isOn && !isShade() && !checkPowerSensorRefreshPriorityNever(powerSensorType)
982 ? checkSensorRefreshTime(powerSensorType)
985 if (!getSensorDataReadingInitialized(powerSensorType)) {
986 deviceStateUpdates.add(new DeviceStateUpdateImpl(powerSensorType, 0));
987 setSensorDataReadingInitialized(powerSensorType, true);
993 throw new IllegalArgumentException("powerSensorType is null or not a power sensor type.");
996 private boolean checkSensorRefreshTime(SensorEnum sensorType) {
997 if (sensorType != null) {
998 DeviceSensorValue devSenVal = getDeviceSensorValue(sensorType);
999 if (devSenVal.getValid()) {
1000 int refresh = Config.DEFAULT_SENSORDATA_REFRESH_INTERVAL;
1001 if (config != null) {
1002 refresh = config.getSensordataRefreshInterval();
1004 return (devSenVal.getTimestamp().getTime() + refresh) > System.currentTimeMillis();
1011 public boolean isSensorDataUpToDate() {
1012 boolean isUpToDate = true;
1013 for (SensorEnum sensorType : devicePowerSensorTypes) {
1014 isUpToDate = isPowerSensorUpToDate(sensorType);
1020 public void setSensorDataRefreshPriority(String activePowerRefreshPriority, String electricMeterRefreshPriority,
1021 String outputCurrentRefreshPriority) {
1022 if (checkPriority(activePowerRefreshPriority)) {
1023 ((String[]) powerSensorRefresh[REFRESH_ACTIVE_POWER_ARRAY_FIELD])[REFRESH_PRIORITY_ARRAY_FIELD] = activePowerRefreshPriority;
1025 if (checkPriority(outputCurrentRefreshPriority)) {
1026 ((String[]) powerSensorRefresh[REFRESH_OUTPUT_CURRENT_ARRAY_FIELD])[REFRESH_PRIORITY_ARRAY_FIELD] = outputCurrentRefreshPriority;
1028 if (checkPriority(electricMeterRefreshPriority)) {
1029 ((String[]) powerSensorRefresh[REFRESH_ELECTRIC_METER_ARRAY_FIELD])[REFRESH_PRIORITY_ARRAY_FIELD] = electricMeterRefreshPriority;
1034 public void setSensorDataRefreshPriority(SensorEnum powerSensorType, String refreshPriority) {
1035 if (checkPriority(refreshPriority)) {
1036 String[] powerSensorRefresh = getPowerSensorRefresh(powerSensorType);
1037 if (powerSensorRefresh != null) {
1038 powerSensorRefresh[REFRESH_ACTIVE_POWER_ARRAY_FIELD] = refreshPriority;
1044 public String getPowerSensorRefreshPriority(SensorEnum powerSensorType) {
1045 if (powerSensorType.equals(SensorEnum.ACTIVE_POWER) && outputMode.equals(OutputModeEnum.WIPE) && !isOn) {
1046 return Config.REFRESH_PRIORITY_LOW;
1048 String[] powerSensorRefresh = getPowerSensorRefresh(powerSensorType);
1049 if (powerSensorRefresh != null) {
1050 return powerSensorRefresh[REFRESH_PRIORITY_ARRAY_FIELD];
1056 public boolean checkPowerSensorRefreshPriorityNever(SensorEnum powerSensorType) {
1057 if (getPowerSensorRefreshPriority(powerSensorType) != null) {
1058 return getPowerSensorRefreshPriority(powerSensorType).equals(Config.REFRESH_PRIORITY_NEVER);
1063 private void setAllSensorDataRefreshPrioritiesToNever() {
1064 for (short i = 0; i < powerSensorRefresh.length; i++) {
1065 ((String[]) powerSensorRefresh[i])[REFRESH_PRIORITY_ARRAY_FIELD] = Config.REFRESH_PRIORITY_NEVER;
1069 private void setSensorDataReadingInitialized(SensorEnum powerSensorType, Boolean flag) {
1070 String[] powerSensorRefresh = getPowerSensorRefresh(powerSensorType);
1071 if (powerSensorRefresh != null) {
1072 powerSensorRefresh[READING_INITIALIZED_ARRAY_FIELD] = flag.toString();
1076 private boolean getSensorDataReadingInitialized(SensorEnum powerSensorType) {
1077 String[] powerSensorRefresh = getPowerSensorRefresh(powerSensorType);
1078 if (powerSensorRefresh != null) {
1079 return Boolean.valueOf(powerSensorRefresh[READING_INITIALIZED_ARRAY_FIELD]);
1084 private String[] getPowerSensorRefresh(SensorEnum powerSensorType) {
1085 switch (powerSensorType) {
1087 return (String[]) powerSensorRefresh[REFRESH_ACTIVE_POWER_ARRAY_FIELD];
1088 case OUTPUT_CURRENT:
1089 return (String[]) powerSensorRefresh[REFRESH_OUTPUT_CURRENT_ARRAY_FIELD];
1090 case ELECTRIC_METER:
1091 return (String[]) powerSensorRefresh[REFRESH_ELECTRIC_METER_ARRAY_FIELD];
1092 case POWER_CONSUMPTION:
1093 return (String[]) powerSensorRefresh[REFRESH_POWER_CONSUMPTION_ARRAY_FIELD];
1099 private boolean checkPriority(String priority) {
1101 case Config.REFRESH_PRIORITY_HIGH:
1102 case Config.REFRESH_PRIORITY_MEDIUM:
1103 case Config.REFRESH_PRIORITY_LOW:
1104 case Config.REFRESH_PRIORITY_NEVER:
1107 logger.error("Sensor data update priority do not exist! Please check the input!");
1113 public boolean isDeviceUpToDate() {
1114 isSensorDataUpToDate();
1115 return this.deviceStateUpdates.isEmpty();
1119 public DeviceStateUpdate getNextDeviceUpdateState() {
1120 return !this.deviceStateUpdates.isEmpty() ? this.deviceStateUpdates.remove(0) : null;
1123 private int internalSetOutputValue(int value) {
1125 slatPosition = value;
1126 if (slatPosition <= 0) {
1132 return slatPosition;
1134 outputValue = (short) value;
1135 if (outputValue <= 0) {
1139 if (outputValue < switchPercentOff) {
1144 setCachedMeterData();
1148 setCachedMeterData();
1155 private void internalSetOff() {
1157 logger.debug("internal set OFF ");
1158 if (!checkPowerSensorRefreshPriorityNever(SensorEnum.ACTIVE_POWER)) {
1159 if (getSensorDataReadingInitialized(SensorEnum.ACTIVE_POWER)) {
1160 deviceStateUpdates.add(new DeviceStateUpdateImpl(SensorEnum.ACTIVE_POWER, -1));
1161 logger.debug("internal set sensor to 0");
1163 setDsSensorValue(SensorEnum.ACTIVE_POWER, 0);
1165 if (!checkPowerSensorRefreshPriorityNever(SensorEnum.OUTPUT_CURRENT)) {
1166 if (getSensorDataReadingInitialized(SensorEnum.OUTPUT_CURRENT)) {
1167 deviceStateUpdates.add(new DeviceStateUpdateImpl(SensorEnum.OUTPUT_CURRENT, -1));
1169 setDsSensorValue(SensorEnum.OUTPUT_CURRENT, 0);
1171 if (!checkPowerSensorRefreshPriorityNever(SensorEnum.POWER_CONSUMPTION)) {
1172 if (getSensorDataReadingInitialized(SensorEnum.POWER_CONSUMPTION)) {
1173 deviceStateUpdates.add(new DeviceStateUpdateImpl(SensorEnum.POWER_CONSUMPTION, -1));
1175 setDsSensorValue(SensorEnum.POWER_CONSUMPTION, 0);
1179 private short internalSetAngleValue(int value) {
1183 if (value > maxSlatAngle) {
1184 slatAngle = maxSlatAngle;
1186 slatAngle = (short) value;
1193 public List<SensorEnum> getSensorTypes() {
1194 List<SensorEnum> list = new ArrayList<>(devicePowerSensorTypes);
1195 list.addAll(deviceClimateSensorTypes);
1200 public List<SensorEnum> getPowerSensorTypes() {
1201 return devicePowerSensorTypes;
1205 public List<SensorEnum> getClimateSensorTypes() {
1206 return deviceClimateSensorTypes;
1210 public List<DeviceSensorValue> getDeviceSensorValues() {
1211 return deviceSensorValues;
1215 public boolean supportsSensorType(SensorEnum sensorType) {
1216 if (sensorType != null) {
1217 return getSensorTypes().contains(sensorType);
1223 public void setDeviceSensorValue(DeviceSensorValue deviceSensorValue) {
1224 if (deviceSensorValue != null) {
1225 int index = deviceSensorValues.indexOf(deviceSensorValue);
1227 deviceSensorValues.add(deviceSensorValue);
1228 if (SensorEnum.isPowerSensor(deviceSensorValue.getSensorType())) {
1229 devicePowerSensorTypes.add(deviceSensorValue.getSensorType());
1231 deviceClimateSensorTypes.add(deviceSensorValue.getSensorType());
1234 if (deviceSensorValue.getTimestamp().after(deviceSensorValues.get(index).getTimestamp())) {
1235 logger.debug("set deviceSeneorValue, new deviceSensorValue is: {}", deviceSensorValue.toString());
1236 deviceSensorValues.set(index, deviceSensorValue);
1237 checkSensorValueSet(deviceSensorValue, true);
1244 public void setDeviceSensorByEvent(EventItem event) {
1245 DeviceSensorValue devSenVal = new DeviceSensorValue(event.getProperties());
1246 SensorEnum sensorType = devSenVal.getSensorType();
1247 if (!isEchoSensor(sensorType)) {
1248 logger.debug("Event is no echo, set values {} for sensorType {}", devSenVal, devSenVal.getSensorType());
1249 if (SensorEnum.isPowerSensor(sensorType) && getSensorDataReadingInitialized(sensorType)) {
1250 logger.debug("SensorJob was initialized, remove sensorjob for sensorType: {}",
1251 devSenVal.getSensorType());
1252 deviceStateUpdates.add(new DeviceStateUpdateImpl(sensorType, -1));
1254 setDeviceSensorValue(devSenVal);
1256 logger.debug("Event is echo remove sensorType {} from echoBox", devSenVal.getSensorType());
1257 sensorEchoBox.remove(devSenVal.getSensorType());
1261 private boolean isEchoSensor(SensorEnum sensorType) {
1262 return sensorEchoBox != null ? sensorEchoBox.contains(sensorType) : false;
1265 private List<SensorEnum> sensorEchoBox = Collections.synchronizedList(new LinkedList<>());
1268 public void setDeviceSensorDsValueBySensorJob(SensorEnum sensorType, Integer value) {
1269 logger.debug("sensorJob for device {} is executet", dsid.getValue());
1270 if (isSensorEchoBoxEnabled()) {
1271 // temperature resolution is not correct, so waiting for device sensor-event
1272 if (!sensorType.toString().contains("TEMPERATURE")) {
1273 logger.debug("echoBox is enabled, add sensorType {} to echoBox", sensorType);
1274 sensorEchoBox.add(sensorType);
1276 logger.debug("echoBox is enabled, ignoring temperation update and wait for sensor Event");
1280 setDsSensorValue(sensorType, value);
1284 public void enableSensorEchoBox() {
1285 if (sensorEchoBox == null) {
1286 sensorEchoBox = Collections.synchronizedList(new LinkedList<>());
1291 public void disableSensorEchoBox() {
1292 sensorEchoBox = null;
1296 public boolean isSensorEchoBoxEnabled() {
1297 return sensorEchoBox != null;
1301 public DeviceSensorValue getDeviceSensorValue(SensorEnum sensorType) {
1302 if (sensorType != null) {
1303 for (DeviceSensorValue devSenVal : deviceSensorValues) {
1304 if (devSenVal.getSensorType().equals(sensorType)) {
1313 public DeviceSensorValue getDeviceSensorValue(Short sensorIndex) {
1314 if (sensorIndex != null) {
1315 for (DeviceSensorValue devSenVal : deviceSensorValues) {
1316 if (devSenVal.getSensorIndex().equals(sensorIndex)) {
1325 public Short getSensorIndex(SensorEnum sensorType) {
1326 if (sensorType != null) {
1327 DeviceSensorValue devSenVal = getDeviceSensorValue(sensorType);
1328 return devSenVal != null ? devSenVal.getSensorIndex() : null;
1334 public SensorEnum getSensorType(Short sensorIndex) {
1335 if (sensorIndex != null) {
1336 DeviceSensorValue devSenVal = getDeviceSensorValue(sensorIndex);
1337 return devSenVal != null ? devSenVal.getSensorType() : null;
1343 public Integer getDsSensorValue(SensorEnum sensorType) {
1344 return getDsSensorValue((Object) sensorType);
1348 public Integer getDsSensorValue(Short sensorIndex) {
1349 return getDsSensorValue((Object) sensorIndex);
1353 public Float getFloatSensorValue(Short sensorIndex) {
1354 return getFloatSensorValue((Object) sensorIndex);
1358 public Float getFloatSensorValue(SensorEnum sensorType) {
1359 return getFloatSensorValue((Object) sensorType);
1363 public boolean setFloatSensorValue(SensorEnum sensorType, Float floatSensorValue) {
1364 return checkAndSetSensorValue(sensorType, null, floatSensorValue);
1368 public boolean setFloatSensorValue(Short sensorIndex, Float floatSensorValue) {
1369 return checkAndSetSensorValue(sensorIndex, null, floatSensorValue);
1373 public boolean setDsSensorValue(Short sensorIndex, Integer dSSensorValue) {
1374 return checkAndSetSensorValue(sensorIndex, dSSensorValue, null);
1378 public boolean setDsSensorValue(SensorEnum sensorType, Integer dSSensorValue) {
1379 return checkAndSetSensorValue(sensorType, dSSensorValue, null);
1383 public boolean setDsSensorValue(Short sensorIndex, Integer dSSensorValue, Float floatSensorValue) {
1384 return checkAndSetSensorValue(sensorIndex, dSSensorValue, floatSensorValue);
1388 public boolean setDsSensorValue(SensorEnum sensorType, Integer dSSensorValue, Float floatSensorValue) {
1389 return checkAndSetSensorValue(sensorType, dSSensorValue, floatSensorValue);
1393 public boolean hasSensors() {
1394 return hasClimateSensors() || hasPowerSensors();
1398 public boolean hasClimateSensors() {
1399 return !deviceClimateSensorTypes.isEmpty();
1403 public boolean hasPowerSensors() {
1404 return !devicePowerSensorTypes.isEmpty();
1407 // Sensor get/set helper methods
1408 private DeviceSensorValue getDeviceSensorValueForGet(Object obj) {
1409 return checkHighOutputCurrent(getDeviceSensorValueForSet(obj));
1412 private DeviceSensorValue getDeviceSensorValueForSet(Object obj) {
1413 if (obj instanceof Short) {
1414 return getDeviceSensorValue((Short) obj);
1416 return getDeviceSensorValue((SensorEnum) obj);
1420 private Integer getDsSensorValue(Object obj) {
1422 DeviceSensorValue devSenVal = checkPowerSensor(getDeviceSensorValueForGet(obj));
1423 return devSenVal != null && devSenVal.getValid() ? devSenVal.getDsValue() : null;
1428 private Float getFloatSensorValue(Object obj) {
1430 DeviceSensorValue devSenVal = checkPowerSensor(getDeviceSensorValueForGet(obj));
1431 return devSenVal != null && devSenVal.getValid() ? devSenVal.getFloatValue() : null;
1436 private DeviceSensorValue checkPowerSensor(DeviceSensorValue devSenVal) {
1437 if (devSenVal != null && SensorEnum.isPowerSensor(devSenVal.getSensorType())) {
1438 if (!devSenVal.getSensorType().equals(SensorEnum.ELECTRIC_METER)
1439 && !(SensorEnum.isPowerSensor(devSenVal.getSensorType()) && isOn)) {
1440 devSenVal.setDsValue(0);
1447 * Checks output current sensor to return automatically high output current
1448 * sensor, if the sensor exists.
1451 * @return output current high DeviceSensorValue or the given DeviceSensorValue
1453 private DeviceSensorValue checkHighOutputCurrent(DeviceSensorValue devSenVal) {
1454 if (devSenVal != null && devSenVal.getSensorType().equals(SensorEnum.OUTPUT_CURRENT)
1455 && devSenVal.getDsValue() == SensorEnum.OUTPUT_CURRENT.getMax().intValue()
1456 && devicePowerSensorTypes.contains(SensorEnum.OUTPUT_CURRENT_H)) {
1457 return getDeviceSensorValue(SensorEnum.OUTPUT_CURRENT_H);
1462 private boolean checkAndSetSensorValue(Object obj, Integer dsValue, Float floatValue) {
1463 boolean isSet = false;
1465 DeviceSensorValue devSenVal = getDeviceSensorValueForSet(obj);
1466 if (devSenVal != null) {
1467 if (dsValue != null && floatValue != null) {
1468 isSet = devSenVal.setValues(floatValue, dsValue);
1469 } else if (dsValue != null) {
1470 isSet = devSenVal.setDsValue(dsValue);
1471 } else if (floatValue != null) {
1472 isSet = devSenVal.setFloatValue(floatValue);
1474 logger.debug("check devSenVal {} isSet={}", devSenVal.toString(), isSet);
1475 checkSensorValueSet(devSenVal, isSet);
1481 private void checkSensorValueSet(DeviceSensorValue devSenVal, boolean isSet) {
1482 if (devSenVal != null) {
1484 if (outputMode.equals(OutputModeEnum.WIPE) && !isOn
1485 && devSenVal.getSensorType().equals(SensorEnum.ACTIVE_POWER)) {
1486 int standby = Config.DEFAULT_STANDBY_ACTIVE_POWER;
1487 if (config != null) {
1488 standby = config.getStandbyActivePower();
1490 if (devSenVal.getDsValue() > standby) {
1491 this.updateInternalDeviceState(new DeviceStateUpdateImpl(DeviceStateUpdate.ON_OFF, 1));
1494 if (SensorEnum.isPowerSensor(devSenVal.getSensorType())) {
1495 addPowerSensorCache(devSenVal);
1497 informListenerAboutStateUpdate(
1498 new DeviceStateUpdateImpl(devSenVal.getSensorType(), devSenVal.getFloatValue()));
1500 setSensorDataReadingInitialized(devSenVal.getSensorType(), false);
1504 private void addPowerSensorCache(DeviceSensorValue newDevSenVal) {
1505 Integer[] cachedPowerValues = cachedSensorPowerValues.get(outputValue);
1506 if (cachedPowerValues == null) {
1507 cachedPowerValues = new Integer[4];
1509 switch (newDevSenVal.getSensorType()) {
1511 cachedPowerValues[ACTIVE_POWER_ARRAY_FIELD] = newDevSenVal.getDsValue();
1513 case OUTPUT_CURRENT:
1514 cachedPowerValues[OUTPUT_CURRENT_ARRAY_FIELD] = newDevSenVal.getDsValue();
1516 case OUTPUT_CURRENT_H:
1517 cachedPowerValues[OUTPUT_CURRENT_HIGH_ARRAY_FIELD] = newDevSenVal.getDsValue();
1519 case POWER_CONSUMPTION:
1520 cachedPowerValues[POWER_CONSUMPTION_ARRAY_FIELD] = newDevSenVal.getDsValue();
1525 this.cachedSensorPowerValues.put(outputValue, cachedPowerValues);
1529 public synchronized void updateInternalDeviceState(DeviceStateUpdate deviceStateUpdate) {
1530 DeviceStateUpdate deviceStateUpdateInt = internalSetOutputValue(deviceStateUpdate);
1531 if (deviceStateUpdateInt != null) {
1532 validateActiveScene();
1533 informListenerAboutStateUpdate(deviceStateUpdate);
1537 private DeviceStateUpdate internalSetOutputValue(DeviceStateUpdate deviceStateUpdate) {
1538 if (deviceStateUpdate == null) {
1541 logger.debug("internal set outputvalue");
1542 switch (deviceStateUpdate.getType()) {
1543 case DeviceStateUpdate.OUTPUT_DECREASE:
1544 return new DeviceStateUpdateImpl(DeviceStateUpdate.OUTPUT_DECREASE,
1545 internalSetOutputValue(outputValue - getDimmStep()));
1546 case DeviceStateUpdate.OUTPUT_INCREASE:
1547 return new DeviceStateUpdateImpl(DeviceStateUpdate.OUTPUT_INCREASE,
1548 internalSetOutputValue(outputValue + getDimmStep()));
1549 case DeviceStateUpdate.OUTPUT:
1550 internalSetOutputValue(deviceStateUpdate.getValueAsInteger());
1552 case DeviceStateUpdate.ON_OFF:
1553 if (deviceStateUpdate.getValueAsInteger() < 0) {
1554 internalSetOutputValue(0);
1556 internalSetOutputValue(maxOutputValue);
1559 case DeviceStateUpdate.OPEN_CLOSE:
1560 if (deviceStateUpdate.getValueAsInteger() < 0) {
1561 internalSetOutputValue(0);
1563 internalSetOutputValue(maxSlatPosition);
1566 case DeviceStateUpdate.OPEN_CLOSE_ANGLE:
1567 if (deviceStateUpdate.getValueAsInteger() < 0) {
1568 internalSetAngleValue(0);
1570 internalSetAngleValue(maxSlatAngle);
1573 case DeviceStateUpdate.SLAT_DECREASE:
1574 return new DeviceStateUpdateImpl(DeviceStateUpdate.SLAT_DECREASE,
1575 internalSetOutputValue(slatPosition - getDimmStep()));
1576 case DeviceStateUpdate.SLAT_INCREASE:
1577 return new DeviceStateUpdateImpl(DeviceStateUpdate.SLAT_INCREASE,
1578 internalSetOutputValue(slatPosition + getDimmStep()));
1579 case DeviceStateUpdate.SLATPOSITION:
1580 internalSetOutputValue(deviceStateUpdate.getValueAsInteger());
1582 case DeviceStateUpdate.SLAT_ANGLE_DECREASE:
1583 return new DeviceStateUpdateImpl(DeviceStateUpdate.SLAT_ANGLE_DECREASE,
1584 internalSetAngleValue(slatAngle - DeviceConstants.ANGLE_STEP_SLAT));
1585 case DeviceStateUpdate.SLAT_ANGLE_INCREASE:
1586 return new DeviceStateUpdateImpl(DeviceStateUpdate.SLAT_ANGLE_INCREASE,
1587 internalSetAngleValue(slatAngle + DeviceConstants.ANGLE_STEP_SLAT));
1588 case DeviceStateUpdate.SLAT_ANGLE:
1589 internalSetAngleValue(deviceStateUpdate.getValueAsInteger());
1591 case DeviceStateUpdate.UPDATE_CALL_SCENE:
1592 this.internalCallScene(deviceStateUpdate.getValueAsShort());
1594 case DeviceStateUpdate.UPDATE_UNDO_SCENE:
1595 this.internalUndoScene();
1598 if (deviceStateUpdate.isSensorUpdateType()) {
1599 SensorEnum sensorType = deviceStateUpdate.getTypeAsSensorEnum();
1600 setFloatSensorValue(sensorType, deviceStateUpdate.getValueAsFloat());
1604 return deviceStateUpdate;
1607 private void validateActiveScene() {
1608 if (activeScene == null) {
1611 Integer[] sceneOutput = getStandartSceneOutput(activeScene.getSceneID());
1612 if (sceneOutput == null) {
1613 sceneOutput = sceneOutputMap.get(activeScene.getSceneID());
1615 if (sceneOutput != null) {
1616 boolean outputChanged = false;
1618 if (isBlind() && sceneOutput[1] != slatAngle) {
1619 logger.debug("Scene output angle: {} setted output value {}", sceneOutput[1], slatAngle);
1620 outputChanged = true;
1622 if (sceneOutput[0] != slatPosition) {
1623 logger.debug("Scene output value: {} setted output value {}", sceneOutput[0], slatPosition);
1624 outputChanged = true;
1627 if (sceneOutput[0] != outputValue) {
1628 logger.debug("Scene output value: {} setted output value {}", sceneOutput[0], outputValue);
1629 outputChanged = true;
1632 if (outputChanged) {
1633 logger.debug("Device output from Device with dSID {} changed deactivate scene {}", dsid.getValue(),
1634 activeScene.getID());
1635 activeScene.deviceSceneChanged((short) -1);
1645 public DeviceStatusListener unregisterDeviceStatusListener() {
1646 setAllSensorDataRefreshPrioritiesToNever();
1647 return super.unregisterDeviceStatusListener();
1650 private void setCachedMeterData() {
1651 logger.debug("load cached sensor data device with dsid {}", dsid.getValue());
1652 Integer[] cachedSensorData = this.cachedSensorPowerValues.get(this.getOutputValue());
1653 if (cachedSensorData != null) {
1654 if (cachedSensorData[ACTIVE_POWER_ARRAY_FIELD] != null
1655 && !checkPowerSensorRefreshPriorityNever(SensorEnum.ACTIVE_POWER)) {
1656 informListenerAboutStateUpdate(new DeviceStateUpdateImpl(SensorEnum.ACTIVE_POWER,
1657 (float) cachedSensorData[ACTIVE_POWER_ARRAY_FIELD]));
1660 if (cachedSensorData[OUTPUT_CURRENT_ARRAY_FIELD] != null
1661 && !checkPowerSensorRefreshPriorityNever(SensorEnum.OUTPUT_CURRENT)) {
1662 if (cachedSensorData[OUTPUT_CURRENT_ARRAY_FIELD] == SensorEnum.OUTPUT_CURRENT.getMax().intValue()
1663 && devicePowerSensorTypes.contains(SensorEnum.OUTPUT_CURRENT_H)) {
1664 informListenerAboutStateUpdate(new DeviceStateUpdateImpl(SensorEnum.OUTPUT_CURRENT,
1665 cachedSensorData[OUTPUT_CURRENT_HIGH_ARRAY_FIELD]));
1667 informListenerAboutStateUpdate(new DeviceStateUpdateImpl(SensorEnum.OUTPUT_CURRENT,
1668 cachedSensorData[OUTPUT_CURRENT_ARRAY_FIELD]));
1671 if (cachedSensorData[ACTIVE_POWER_ARRAY_FIELD] != null
1672 && !checkPowerSensorRefreshPriorityNever(SensorEnum.POWER_CONSUMPTION)) {
1673 informListenerAboutStateUpdate(
1674 new DeviceStateUpdateImpl(SensorEnum.ACTIVE_POWER, cachedSensorData[ACTIVE_POWER_ARRAY_FIELD]));
1680 * if an {@link DeviceStatusListener} is registered inform him about the new
1681 * state otherwise do nothing.
1683 * @param deviceStateUpdate
1685 private void informListenerAboutStateUpdate(DeviceStateUpdate deviceStateUpdate) {
1686 if (listener != null) {
1687 listener.onDeviceStateChanged(correctDeviceStatusUpdate(deviceStateUpdate));
1691 private DeviceStateUpdate correctDeviceStatusUpdate(DeviceStateUpdate deviceStateUpdate) {
1692 if (isSwitch() && deviceStateUpdate.getType().equals(DeviceStateUpdate.OUTPUT)) {
1693 if (deviceStateUpdate.getValueAsInteger() >= switchPercentOff) {
1694 return new DeviceStateUpdateImpl(DeviceStateUpdate.ON_OFF, DeviceStateUpdate.ON_VALUE);
1696 return new DeviceStateUpdateImpl(DeviceStateUpdate.ON_OFF, DeviceStateUpdate.OFF_VALUE);
1699 return deviceStateUpdate;
1702 private void informListenerAboutConfigChange(ChangeableDeviceConfigEnum changedConfig) {
1703 if (listener != null) {
1704 listener.onDeviceConfigChanged(changedConfig);
1705 logger.debug("Inform listener about device config {} changed", changedConfig.toString());
1709 @SuppressWarnings("null")
1711 public void saveConfigSceneSpecificationIntoDevice(Map<String, String> propertries) {
1712 if (propertries != null) {
1714 for (String key : propertries.keySet()) {
1715 if (key.startsWith(DigitalSTROMBindingConstants.DEVICE_SCENE)) {
1717 short sceneID = Short.parseShort((String) key
1718 .subSequence(DigitalSTROMBindingConstants.DEVICE_SCENE.length(), key.length()));
1719 sceneSave = propertries.get(key);
1720 if (sceneSave != null && !sceneSave.isBlank()) {
1721 logger.debug("Find saved scene configuration for device with dSID {} and sceneID {}", dsid,
1723 String[] sceneParm = sceneSave.replace(" ", "").split(",");
1724 JSONDeviceSceneSpecImpl sceneSpecNew = null;
1725 int sceneValue = -1;
1726 int sceneAngle = -1;
1727 for (int j = 0; j < sceneParm.length; j++) {
1728 String[] sceneParmSplit = sceneParm[j].split(":");
1729 switch (sceneParmSplit[0]) {
1731 sceneSpecNew = new JSONDeviceSceneSpecImpl(sceneParmSplit[1]);
1734 sceneSpecNew.setDontcare(Boolean.parseBoolean(sceneParmSplit[1]));
1737 sceneSpecNew.setLocalPrio(Boolean.parseBoolean(sceneParmSplit[1]));
1740 sceneSpecNew.setSpecialMode(Boolean.parseBoolean(sceneParmSplit[1]));
1743 sceneValue = Integer.parseInt(sceneParmSplit[1]);
1746 sceneAngle = Integer.parseInt(sceneParmSplit[1]);
1750 if (sceneValue > -1) {
1752 "Saved sceneValue {}, sceneAngle {} for scene id {} into device with dsid {}",
1753 sceneValue, sceneAngle, sceneID, getDSID().getValue());
1754 internalSetSceneOutputValue(sceneID, sceneValue, sceneAngle);
1755 deviceStateUpdates.add(new DeviceStateUpdateImpl(DeviceStateUpdate.UPDATE_SCENE_OUTPUT,
1756 new Short[] { sceneID, (short) -1 }));
1758 if (sceneSpecNew != null) {
1759 logger.debug("Saved sceneConfig: [{}] for scene id {} into device with dsid {}",
1760 sceneSpecNew.toString(), sceneID, getDSID().getValue());
1761 synchronized (sceneConfigMap) {
1762 sceneConfigMap.put(sceneID, sceneSpecNew);
1764 deviceStateUpdates.add(new DeviceStateUpdateImpl(DeviceStateUpdate.UPDATE_SCENE_CONFIG,
1765 new Short[] { sceneID, (short) -1 }));
1768 } catch (NumberFormatException e) {
1776 @SuppressWarnings("null")
1778 public void saveConfigSceneSpecificationIntoDevice(String propertries) {
1779 String[] scenes = propertries.split("\n");
1780 for (int i = 0; i < scenes.length; i++) {
1781 logger.debug("Find saved scene configuration for device with dSID {} and sceneID {}", dsid, i);
1782 String[] sceneIdToConfig = scenes[i].replaceAll(" ", "").split("=");
1783 String[] sceneParm = sceneIdToConfig[1].split(",");
1784 JSONDeviceSceneSpecImpl sceneSpecNew = null;
1785 int sceneValue = -1;
1786 int sceneAngle = -1;
1787 for (int j = 0; j < sceneParm.length; j++) {
1788 String[] sceneParmSplit = sceneParm[j].split(":");
1789 switch (sceneParmSplit[0]) {
1791 sceneSpecNew = new JSONDeviceSceneSpecImpl(sceneParmSplit[1]);
1794 sceneSpecNew.setDontcare(Boolean.parseBoolean(sceneParmSplit[1]));
1797 sceneSpecNew.setLocalPrio(Boolean.parseBoolean(sceneParmSplit[1]));
1800 sceneSpecNew.setSpecialMode(Boolean.parseBoolean(sceneParmSplit[1]));
1803 sceneValue = Integer.parseInt(sceneParmSplit[1]);
1806 sceneAngle = Integer.parseInt(sceneParmSplit[1]);
1810 if (sceneValue > -1) {
1811 logger.debug("Saved sceneValue {}, sceneAngle {} for scene id {} into device with dsid {}", sceneValue,
1812 sceneAngle, i, getDSID().getValue());
1813 synchronized (sceneOutputMap) {
1814 sceneOutputMap.put(sceneSpecNew.getScene().getSceneNumber(),
1815 new Integer[] { sceneValue, sceneAngle });
1818 if (sceneSpecNew != null) {
1819 logger.debug("Saved sceneConfig: [{}] for scene id {} into device with dsid {}",
1820 sceneSpecNew.toString(), i, getDSID().getValue());
1821 synchronized (sceneConfigMap) {
1822 sceneConfigMap.put(sceneSpecNew.getScene().getSceneNumber(), sceneSpecNew);
1829 public void setConfig(Config config) {
1830 this.config = config;
1833 private String powerSensorRefreshToString() {
1834 String powSenRef = "";
1835 for (int i = 0; i < powerSensorRefresh.length; i++) {
1836 powSenRef = powSenRef + " [" + i + "]=Prio: "
1837 + ((String[]) powerSensorRefresh[i])[REFRESH_PRIORITY_ARRAY_FIELD] + ", Initialized: "
1838 + ((String[]) powerSensorRefresh[i])[READING_INITIALIZED_ARRAY_FIELD] + " ";
1844 public boolean isBinaryInputDevice() {
1845 return !deviceBinaryInputs.isEmpty();
1849 public List<DeviceBinaryInput> getBinaryInputs() {
1850 return deviceBinaryInputs;
1854 public DeviceBinaryInput getBinaryInput(DeviceBinarayInputEnum binaryInputType) {
1855 if (binaryInputType != null) {
1856 for (DeviceBinaryInput binInput : deviceBinaryInputs) {
1857 if (binaryInputType.getBinaryInputType().equals(binInput.getInputType())) {
1866 public Short getBinaryInputState(DeviceBinarayInputEnum binaryInputType) {
1867 DeviceBinaryInput devBinInput = getBinaryInput(binaryInputType);
1868 if (devBinInput != null) {
1869 return devBinInput.getState();
1875 public boolean setBinaryInputState(DeviceBinarayInputEnum binaryInputType, Short newState) {
1876 DeviceBinaryInput devBinInput = getBinaryInput(binaryInputType);
1877 if (devBinInput != null) {
1878 devBinInput.setState(newState);
1879 informListenerAboutStateUpdate(new DeviceStateUpdateImpl(binaryInputType, newState));
1886 public void setBinaryInputs(List<DeviceBinaryInput> newBinaryInputs) {
1887 this.deviceBinaryInputs.clear();
1888 this.deviceBinaryInputs.addAll(newBinaryInputs);
1889 informListenerAboutConfigChange(ChangeableDeviceConfigEnum.BINARY_INPUTS);
1893 public String toString() {
1894 return "DeviceImpl [meterDSID=" + meterDSID + ", zoneId=" + zoneId + ", groupList=" + groupList + ", hwInfo="
1895 + hwInfo + ", getName()=" + getName() + ", getDSID()=" + getDSID() + ", getDSUID()=" + getDSUID()
1896 + ", isPresent()=" + isPresent() + ", isValide()=" + isValid() + ", getDisplayID()=" + getDisplayID()
1897 + ", outputMode=" + outputMode + ", getSensorTypes()=" + getSensorTypes() + ", getDeviceSensorValues()="
1898 + getDeviceSensorValues() + ", powerSensorRefresh=" + powerSensorRefreshToString() + "]";