2 * Copyright (c) 2010-2023 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;
23 import java.util.Optional;
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;
54 import com.google.gson.JsonArray;
55 import com.google.gson.JsonElement;
56 import com.google.gson.JsonObject;
59 * The {@link DeviceImpl} is the implementation of the {@link Device}.
61 * @author Michael Ochel - Initial contribution
62 * @author Matthias Siegele - Initial contribution
64 public class DeviceImpl extends AbstractGeneralDeviceInformations implements Device {
66 private final Logger logger = LoggerFactory.getLogger(DeviceImpl.class);
68 private Config config;
70 private DSID meterDSID;
71 private int zoneId = 0;
72 private List<ApplicationGroup> groupList = new LinkedList<>();
74 private String hwInfo;
76 private OutputModeEnum outputMode;
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;
84 private short slatAngle = 0;
85 private final short maxSlatAngle = DeviceConstants.MAX_SLAT_ANGLE;
86 private final short minSlatAngle = DeviceConstants.MIN_SLAT_ANGLE;
88 private int slatPosition = 0;
89 private final int maxSlatPosition = DeviceConstants.MAX_ROLLERSHUTTER;
90 private final int minSlatPosition = DeviceConstants.MIN_ROLLERSHUTTER;
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<>());
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;
106 private final Map<Short, DeviceSceneSpec> sceneConfigMap = Collections.synchronizedMap(new HashMap<>());
107 private final Map<Short, Integer[]> sceneOutputMap = Collections.synchronizedMap(new HashMap<>());
109 // saves outstanding commands
110 private final List<DeviceStateUpdate> deviceStateUpdates = Collections.synchronizedList(new LinkedList<>());
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)
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" } };
124 public static final int REFRESH_PRIORITY_ARRAY_FIELD = 0;
125 public static final int READING_INITIALIZED_ARRAY_FIELD = 1;
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;
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
137 private final Map<Short, Integer[]> cachedSensorPowerValues = Collections.synchronizedMap(new HashMap<>());
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;
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
148 private final int switchPercentOff = 1;
151 * Creates a new {@link DeviceImpl} from the given DigitalSTROM-Device
152 * {@link JsonObject}.
154 * @param deviceJsonObject json response of the digitalSTROM-Server, must not be
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());
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();
170 if (deviceJsonObject.get(JSONApiResponseKeysEnum.ON.getKey()) != null) {
172 this.isOn = deviceJsonObject.get(JSONApiResponseKeysEnum.ON.getKey()).getAsBoolean();
174 this.isOpen = deviceJsonObject.get(JSONApiResponseKeysEnum.ON.getKey()).getAsBoolean();
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();
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());
190 } else if (groups != null && groups.isJsonObject()) {
191 for (Entry<String, JsonElement> entry : deviceJsonObject.get(JSONApiResponseKeysEnum.GROUPS.getKey())
192 .getAsJsonObject().entrySet()) {
194 entry.getValue().getAsJsonObject().get(JSONApiResponseKeysEnum.ID.getKey()).getAsShort());
197 if (deviceJsonObject.get(JSONApiResponseKeysEnum.OUTPUT_MODE.getKey()) != null) {
198 int tmp = deviceJsonObject.get(JSONApiResponseKeysEnum.OUTPUT_MODE.getKey()).getAsInt();
200 if (OutputModeEnum.containsMode(tmp)) {
201 outputMode = OutputModeEnum.getMode(tmp);
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) {
213 .containsSensor(sensorType.get(JSONApiResponseKeysEnum.TYPE.getKey()).getAsShort())) {
214 setDeviceSensorValue(new DeviceSensorValue(entry.getValue().getAsJsonObject()));
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));
231 outputChannels.addAll(DSJsonParser.getOutputChannels(deviceJsonObject));
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;
243 maxOutputValue = DeviceConstants.DEFAULT_MAX_OUTPUTVALUE;
247 outputValue = DeviceConstants.DEFAULT_MAX_OUTPUTVALUE;
252 public synchronized DSID getMeterDSID() {
253 return this.meterDSID;
257 public synchronized void setMeterDSID(String meterDSID) {
258 this.meterDSID = new DSID(meterDSID);
259 informListenerAboutConfigChange(ChangeableDeviceConfigEnum.METER_DSID);
263 public String getHWinfo() {
268 public List<Short> getGroups() {
269 LinkedList<Short> linkedList = new LinkedList<>();
270 groupList.forEach(group -> linkedList.add(group.getId()));
275 * Adds the ApplicationGroup of the given groupId to the internal list
278 * @return true if the groupId is a valid ApplicationGroup id, false otherwise
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);
288 return group != null;
292 public void addGroup(Short groupID) {
293 addGroupToList(groupID);
294 informListenerAboutConfigChange(ChangeableDeviceConfigEnum.GROUPS);
298 public void setGroups(List<Short> newGroupList) {
299 if (newGroupList != null) {
301 newGroupList.forEach(groupId -> groupList.add(ApplicationGroup.getGroup(groupId)));
303 informListenerAboutConfigChange(ChangeableDeviceConfigEnum.GROUPS);
307 public synchronized int getZoneId() {
312 public synchronized void setZoneId(int zoneID) {
313 this.zoneId = zoneID;
314 informListenerAboutConfigChange(ChangeableDeviceConfigEnum.ZONE_ID);
318 public synchronized boolean isOn() {
323 public synchronized void setIsOn(boolean flag) {
325 this.deviceStateUpdates.add(new DeviceStateUpdateImpl(DeviceStateUpdate.ON_OFF, 1));
327 this.deviceStateUpdates.add(new DeviceStateUpdateImpl(DeviceStateUpdate.ON_OFF, -1));
332 public synchronized boolean isOpen() {
337 public synchronized void setIsOpen(boolean flag) {
339 this.deviceStateUpdates.add(new DeviceStateUpdateImpl(DeviceStateUpdate.OPEN_CLOSE, 1));
341 this.deviceStateUpdates.add(new DeviceStateUpdateImpl(DeviceStateUpdate.OPEN_CLOSE_ANGLE, 1));
344 this.deviceStateUpdates.add(new DeviceStateUpdateImpl(DeviceStateUpdate.OPEN_CLOSE, -1));
346 this.deviceStateUpdates.add(new DeviceStateUpdateImpl(DeviceStateUpdate.OPEN_CLOSE_ANGLE, -1));
352 public synchronized void setOutputValue(short value) {
355 this.deviceStateUpdates.add(new DeviceStateUpdateImpl(DeviceStateUpdate.ON_OFF, -1));
357 } else if (value > maxOutputValue) {
358 this.deviceStateUpdates.add(new DeviceStateUpdateImpl(DeviceStateUpdate.ON_OFF, 1));
360 this.deviceStateUpdates.add(new DeviceStateUpdateImpl(DeviceStateUpdate.OUTPUT, value));
366 public synchronized boolean isDimmable() {
367 return OutputModeEnum.outputModeIsDimmable(outputMode);
371 public synchronized boolean isSwitch() {
372 return OutputModeEnum.outputModeIsSwitch(outputMode);
376 public synchronized boolean isDeviceWithOutput() {
377 return this.outputMode != null && !this.outputMode.equals(OutputModeEnum.DISABLED);
381 public boolean isSensorDevice() {
382 return !isDeviceWithOutput() && !deviceClimateSensorTypes.isEmpty();
386 public boolean isHeatingDevice() {
387 return groupList.contains(ApplicationGroup.HEATING);
391 public boolean isTemperatureControlledDevice() {
392 return groupList.contains(ApplicationGroup.TEMPERATURE_CONTROL);
396 public boolean isShade() {
397 return OutputModeEnum.outputModeIsShade(outputMode);
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));
408 public synchronized ApplicationGroup getFunctionalColorGroup() {
409 Optional<ApplicationGroup> applicationGroup = groupList.stream().findFirst();
410 return applicationGroup.isPresent() ? applicationGroup.get() : null;
414 public synchronized void setFunctionalColorGroup(ApplicationGroup functionalColorGroup) {
415 groupList.add(functionalColorGroup);
416 informListenerAboutConfigChange(ChangeableDeviceConfigEnum.FUNCTIONAL_GROUP);
420 public OutputModeEnum getOutputMode() {
425 public List<OutputChannelEnum> getOutputChannels() {
426 return outputChannels;
430 public synchronized void setOutputMode(OutputModeEnum newOutputMode) {
431 this.outputMode = newOutputMode;
432 informListenerAboutConfigChange(ChangeableDeviceConfigEnum.OUTPUT_MODE);
436 public synchronized void increase() {
438 deviceStateUpdates.add(new DeviceStateUpdateImpl(DeviceStateUpdate.OUTPUT_INCREASE, 0));
441 this.deviceStateUpdates.add(new DeviceStateUpdateImpl(DeviceStateUpdate.SLAT_INCREASE, 0));
443 this.deviceStateUpdates.add(new DeviceStateUpdateImpl(DeviceStateUpdate.SLAT_ANGLE_DECREASE, 0));
449 public synchronized void decrease() {
451 this.deviceStateUpdates.add(new DeviceStateUpdateImpl(DeviceStateUpdate.OUTPUT_DECREASE, 0));
454 this.deviceStateUpdates.add(new DeviceStateUpdateImpl(DeviceStateUpdate.SLAT_DECREASE, 0));
456 this.deviceStateUpdates.add(new DeviceStateUpdateImpl(DeviceStateUpdate.SLAT_ANGLE_DECREASE, 0));
462 public synchronized void increaseSlatAngle() {
464 this.deviceStateUpdates.add(new DeviceStateUpdateImpl(DeviceStateUpdate.SLAT_ANGLE_DECREASE, 1));
469 public synchronized void decreaseSlatAngle() {
471 this.deviceStateUpdates.add(new DeviceStateUpdateImpl(DeviceStateUpdate.SLAT_ANGLE_DECREASE, 1));
476 public synchronized short getOutputValue() {
481 public short getMaxOutputValue() {
482 return maxOutputValue;
486 public short getMinOutputValue() {
487 return minOutputValue;
491 public synchronized int getSlatPosition() {
496 public synchronized short getAnglePosition() {
501 public synchronized void setAnglePosition(int angle) {
502 if (angle == slatAngle) {
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));
510 this.deviceStateUpdates.add(new DeviceStateUpdateImpl(DeviceStateUpdate.SLAT_ANGLE, angle));
515 public synchronized void setSlatPosition(int position) {
516 if (position == this.slatPosition) {
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));
524 this.deviceStateUpdates.add(new DeviceStateUpdateImpl(DeviceStateUpdate.SLATPOSITION, position));
528 private short getDimmStep() {
530 return DeviceConstants.DIM_STEP_LIGHT;
531 } else if (isShade()) {
532 return DeviceConstants.MOVE_STEP_ROLLERSHUTTER;
534 return DeviceConstants.DEFAULT_MOVE_STEP;
539 public int getMaxSlatPosition() {
540 return maxSlatPosition;
544 public int getMinSlatPosition() {
545 return minSlatPosition;
549 public int getMaxSlatAngle() {
554 public int getMinSlatAngle() {
561 public synchronized void callInternalScene(InternalScene scene) {
562 if (lastCallWasUndo) {
564 if (activeScene != null) {
565 activeScene.deactivateSceneByDevice();
569 internalCallScene(scene.getSceneID());
571 lastCallWasUndo = false;
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 }));
582 if (sceneOutputMap.get(sceneNumber) == null) {
583 deviceStateUpdates.add(new DeviceStateUpdateImpl(DeviceStateUpdate.UPDATE_SCENE_OUTPUT,
584 new Short[] { sceneNumber, prio }));
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) {
597 if (this.lastScene != null && !lastScene.equals(activeScene)) {
598 activeScene = lastScene;
600 activeScene.activateSceneByDevice();
603 logger.debug("internalUndo Scene dSID {}", dsid.getValue());
604 this.activeScene = null;
607 lastCallWasUndo = true;
612 public synchronized void callScene(Short sceneNumber) {
613 this.deviceStateUpdates.add(new DeviceStateUpdateImpl(DeviceStateUpdate.UPDATE_CALL_SCENE, sceneNumber));
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);
623 outputValueBeforeSceneCall = this.outputValue;
625 outputValueBeforeSceneCall = this.slatPosition;
627 slatAngleBeforeSceneCall = this.slatAngle;
630 if (!checkSceneNumber(sceneNumber)) {
631 if (containsSceneConfig(sceneNumber)) {
632 if (doIgnoreScene(sceneNumber)) {
636 this.deviceStateUpdates.add(new DeviceStateUpdateImpl(DeviceStateUpdate.UPDATE_SCENE_CONFIG,
637 new Short[] { sceneNumber, GeneralLibConstance.HIGHEST_READ_OUT_PRIORITY }));
639 if (sceneOutputMap.get(sceneNumber) != null) {
641 updateInternalDeviceState(new DeviceStateUpdateImpl(DeviceStateUpdate.OUTPUT,
642 sceneOutputMap.get(sceneNumber)[GeneralLibConstance.SCENE_ARRAY_INDEX_VALUE]));
644 updateInternalDeviceState(new DeviceStateUpdateImpl(DeviceStateUpdate.SLATPOSITION,
645 sceneOutputMap.get(sceneNumber)[GeneralLibConstance.SCENE_ARRAY_INDEX_VALUE]));
647 updateInternalDeviceState(new DeviceStateUpdateImpl(DeviceStateUpdate.SLAT_ANGLE,
648 sceneOutputMap.get(sceneNumber)[GeneralLibConstance.SCENE_ARRAY_INDEX_ANGLE]));
652 this.deviceStateUpdates.add(new DeviceStateUpdateImpl(DeviceStateUpdate.UPDATE_SCENE_OUTPUT,
653 new Short[] { sceneNumber, GeneralLibConstance.HIGHEST_READ_OUT_PRIORITY }));
660 private boolean checkSceneNumber(Short sceneNumber) {
661 if (SceneEnum.containsScene(sceneNumber)) {
662 if (this.outputMode.equals(OutputModeEnum.POWERSAVE)) {
663 switch (SceneEnum.getScene(sceneNumber)) {
667 this.updateInternalDeviceState(new DeviceStateUpdateImpl(DeviceStateUpdate.ON_OFF, -1));
683 if (this.outputMode.equals(OutputModeEnum.WIPE)) {
684 switch (SceneEnum.getScene(sceneNumber)) {
696 this.updateInternalDeviceState(new DeviceStateUpdateImpl(DeviceStateUpdate.ON_OFF, -1));
702 switch (SceneEnum.getScene(sceneNumber)) {
707 this.updateInternalDeviceState(new DeviceStateUpdateImpl(DeviceStateUpdate.ON_OFF, 1));
709 this.updateInternalDeviceState(new DeviceStateUpdateImpl(DeviceStateUpdate.OPEN_CLOSE, 1));
711 this.updateInternalDeviceState(
712 new DeviceStateUpdateImpl(DeviceStateUpdate.OPEN_CLOSE_ANGLE, 1));
721 this.updateInternalDeviceState(new DeviceStateUpdateImpl(DeviceStateUpdate.ON_OFF, -1));
723 this.updateInternalDeviceState(new DeviceStateUpdateImpl(DeviceStateUpdate.OPEN_CLOSE, -1));
725 this.updateInternalDeviceState(
726 new DeviceStateUpdateImpl(DeviceStateUpdate.OPEN_CLOSE_ANGLE, -1));
732 case AREA_1_INCREMENT:
733 case AREA_2_INCREMENT:
734 case AREA_3_INCREMENT:
735 case AREA_4_INCREMENT:
737 if (outputValue == maxOutputValue) {
740 this.updateInternalDeviceState(new DeviceStateUpdateImpl(DeviceStateUpdate.OUTPUT_INCREASE, 0));
743 if (slatPosition == maxSlatPosition) {
746 this.updateInternalDeviceState(new DeviceStateUpdateImpl(DeviceStateUpdate.SLAT_INCREASE, 0));
748 if (slatAngle == maxSlatAngle) {
751 updateInternalDeviceState(
752 new DeviceStateUpdateImpl(DeviceStateUpdate.SLAT_ANGLE_INCREASE, 0));
758 case AREA_1_DECREMENT:
759 case AREA_2_DECREMENT:
760 case AREA_3_DECREMENT:
761 case AREA_4_DECREMENT:
763 if (outputValue == minOutputValue) {
766 updateInternalDeviceState(new DeviceStateUpdateImpl(DeviceStateUpdate.OUTPUT_DECREASE, 0));
769 if (slatPosition == minSlatPosition) {
772 updateInternalDeviceState(new DeviceStateUpdateImpl(DeviceStateUpdate.SLAT_DECREASE, 0));
774 if (slatAngle == minSlatAngle) {
777 this.updateInternalDeviceState(
778 new DeviceStateUpdateImpl(DeviceStateUpdate.SLAT_ANGLE_INCREASE, 0));
789 this.deviceStateUpdates.add(new DeviceStateUpdateImpl(DeviceStateUpdate.UPDATE_OUTPUT_VALUE, 0));
791 // Area Stepping continue scenes
792 case AREA_STEPPING_CONTINUE:
801 private Integer[] getStandartSceneOutput(short sceneNumber) {
802 if (SceneEnum.getScene(sceneNumber) != null) {
803 switch (SceneEnum.getScene(sceneNumber)) {
807 return new Integer[] { (int) maxOutputValue, -1 };
810 return new Integer[] { (int) maxSlatPosition, (int) maxSlatAngle };
812 return new Integer[] { (int) maxSlatPosition, -1 };
820 return new Integer[] { (int) 0, -1 };
823 return new Integer[] { (int) 0, 0 };
825 return new Integer[] { (int) 0, -1 };
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;
844 public synchronized void undoScene() {
845 this.deviceStateUpdates
846 .add(new DeviceStateUpdateImpl(DeviceStateUpdate.UPDATE_UNDO_SCENE, this.activeSceneNumber));
850 public synchronized void internalUndoScene() {
852 updateInternalDeviceState(
853 new DeviceStateUpdateImpl(DeviceStateUpdate.OUTPUT, this.outputValueBeforeSceneCall));
855 updateInternalDeviceState(
856 new DeviceStateUpdateImpl(DeviceStateUpdate.SLATPOSITION, this.outputValueBeforeSceneCall));
858 updateInternalDeviceState(
859 new DeviceStateUpdateImpl(DeviceStateUpdate.SLAT_ANGLE, this.slatAngleBeforeSceneCall));
863 if (activeSceneNumber != -1) {
864 activeSceneNumber = -1;
869 public InternalScene getAcitiveScene() {
870 return this.activeScene;
874 public Integer[] getSceneOutputValue(short sceneId) {
875 synchronized (sceneOutputMap) {
876 if (sceneOutputMap.containsKey(sceneId)) {
877 return sceneOutputMap.get(sceneId);
880 return new Integer[] { -1, -1 };
884 public void setSceneOutputValue(short sceneId, int value) {
885 internalSetSceneOutputValue(sceneId, value, -1);
886 if (listener != null) {
887 listener.onSceneConfigAdded(sceneId);
892 public void setSceneOutputValue(short sceneId, int value, int angle) {
893 internalSetSceneOutputValue(sceneId, value, angle);
894 if (listener != null) {
895 listener.onSceneConfigAdded(sceneId);
899 private void internalSetSceneOutputValue(short sceneId, int value, int angle) {
900 synchronized (sceneOutputMap) {
901 sceneOutputMap.put(sceneId, new Integer[] { value, angle });
903 if (activeSceneNumber == sceneId) {
904 internalCallScene(sceneId);
909 public List<Short> getSavedScenes() {
910 Set<Short> bothKeySet = new HashSet<>(sceneOutputMap.keySet());
911 bothKeySet.addAll(sceneConfigMap.keySet());
912 return new LinkedList<>(bothKeySet);
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);
928 public DeviceSceneSpec getSceneConfig(short sceneId) {
929 synchronized (sceneConfigMap) {
930 return sceneConfigMap.get(sceneId);
935 public boolean doIgnoreScene(short sceneId) {
936 synchronized (sceneConfigMap) {
937 if (this.sceneConfigMap.containsKey(sceneId)) {
938 return this.sceneConfigMap.get(sceneId).isDontCare();
945 public boolean containsSceneConfig(short sceneId) {
946 synchronized (sceneConfigMap) {
947 return sceneConfigMap.containsKey(sceneId);
954 public boolean equals(Object obj) {
955 if (obj instanceof Device) {
956 Device device = (Device) obj;
957 return device.getDSID().equals(this.getDSID());
963 public int hashCode() {
964 return this.getDSID().hashCode();
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)
977 if (powerSensorType.equals(SensorEnum.ELECTRIC_METER)) {
978 isUpToDate = (isOn || getDeviceSensorValue(powerSensorType).getDsValue() == 0) && !isShade()
979 && !checkPowerSensorRefreshPriorityNever(powerSensorType)
980 ? checkSensorRefreshTime(powerSensorType)
983 isUpToDate = isOn && !isShade() && !checkPowerSensorRefreshPriorityNever(powerSensorType)
984 ? checkSensorRefreshTime(powerSensorType)
987 if (!getSensorDataReadingInitialized(powerSensorType)) {
988 deviceStateUpdates.add(new DeviceStateUpdateImpl(powerSensorType, 0));
989 setSensorDataReadingInitialized(powerSensorType, true);
995 throw new IllegalArgumentException("powerSensorType is null or not a power sensor type.");
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();
1006 return (devSenVal.getTimestamp().getTime() + refresh) > System.currentTimeMillis();
1013 public boolean isSensorDataUpToDate() {
1014 boolean isUpToDate = true;
1015 for (SensorEnum sensorType : devicePowerSensorTypes) {
1016 isUpToDate = isPowerSensorUpToDate(sensorType);
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;
1027 if (checkPriority(outputCurrentRefreshPriority)) {
1028 ((String[]) powerSensorRefresh[REFRESH_OUTPUT_CURRENT_ARRAY_FIELD])[REFRESH_PRIORITY_ARRAY_FIELD] = outputCurrentRefreshPriority;
1030 if (checkPriority(electricMeterRefreshPriority)) {
1031 ((String[]) powerSensorRefresh[REFRESH_ELECTRIC_METER_ARRAY_FIELD])[REFRESH_PRIORITY_ARRAY_FIELD] = electricMeterRefreshPriority;
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;
1046 public String getPowerSensorRefreshPriority(SensorEnum powerSensorType) {
1047 if (powerSensorType.equals(SensorEnum.ACTIVE_POWER) && outputMode.equals(OutputModeEnum.WIPE) && !isOn) {
1048 return Config.REFRESH_PRIORITY_LOW;
1050 String[] powerSensorRefresh = getPowerSensorRefresh(powerSensorType);
1051 if (powerSensorRefresh != null) {
1052 return powerSensorRefresh[REFRESH_PRIORITY_ARRAY_FIELD];
1058 public boolean checkPowerSensorRefreshPriorityNever(SensorEnum powerSensorType) {
1059 if (getPowerSensorRefreshPriority(powerSensorType) != null) {
1060 return getPowerSensorRefreshPriority(powerSensorType).equals(Config.REFRESH_PRIORITY_NEVER);
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;
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();
1078 private boolean getSensorDataReadingInitialized(SensorEnum powerSensorType) {
1079 String[] powerSensorRefresh = getPowerSensorRefresh(powerSensorType);
1080 if (powerSensorRefresh != null) {
1081 return Boolean.valueOf(powerSensorRefresh[READING_INITIALIZED_ARRAY_FIELD]);
1086 private String[] getPowerSensorRefresh(SensorEnum powerSensorType) {
1087 switch (powerSensorType) {
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];
1101 private boolean checkPriority(String 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:
1109 logger.error("Sensor data update priority do not exist! Please check the input!");
1115 public boolean isDeviceUpToDate() {
1116 isSensorDataUpToDate();
1117 return this.deviceStateUpdates.isEmpty();
1121 public DeviceStateUpdate getNextDeviceUpdateState() {
1122 return !this.deviceStateUpdates.isEmpty() ? this.deviceStateUpdates.remove(0) : null;
1125 private int internalSetOutputValue(int value) {
1127 slatPosition = value;
1128 if (slatPosition <= 0) {
1134 return slatPosition;
1136 outputValue = (short) value;
1137 if (outputValue <= 0) {
1141 if (outputValue < switchPercentOff) {
1146 setCachedMeterData();
1150 setCachedMeterData();
1157 private void internalSetOff() {
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");
1165 setDsSensorValue(SensorEnum.ACTIVE_POWER, 0);
1167 if (!checkPowerSensorRefreshPriorityNever(SensorEnum.OUTPUT_CURRENT)) {
1168 if (getSensorDataReadingInitialized(SensorEnum.OUTPUT_CURRENT)) {
1169 deviceStateUpdates.add(new DeviceStateUpdateImpl(SensorEnum.OUTPUT_CURRENT, -1));
1171 setDsSensorValue(SensorEnum.OUTPUT_CURRENT, 0);
1173 if (!checkPowerSensorRefreshPriorityNever(SensorEnum.POWER_CONSUMPTION)) {
1174 if (getSensorDataReadingInitialized(SensorEnum.POWER_CONSUMPTION)) {
1175 deviceStateUpdates.add(new DeviceStateUpdateImpl(SensorEnum.POWER_CONSUMPTION, -1));
1177 setDsSensorValue(SensorEnum.POWER_CONSUMPTION, 0);
1181 private short internalSetAngleValue(int value) {
1185 if (value > maxSlatAngle) {
1186 slatAngle = maxSlatAngle;
1188 slatAngle = (short) value;
1195 public List<SensorEnum> getSensorTypes() {
1196 List<SensorEnum> list = new ArrayList<>(devicePowerSensorTypes);
1197 list.addAll(deviceClimateSensorTypes);
1202 public List<SensorEnum> getPowerSensorTypes() {
1203 return devicePowerSensorTypes;
1207 public List<SensorEnum> getClimateSensorTypes() {
1208 return deviceClimateSensorTypes;
1212 public List<DeviceSensorValue> getDeviceSensorValues() {
1213 return deviceSensorValues;
1217 public boolean supportsSensorType(SensorEnum sensorType) {
1218 if (sensorType != null) {
1219 return getSensorTypes().contains(sensorType);
1225 public void setDeviceSensorValue(DeviceSensorValue deviceSensorValue) {
1226 if (deviceSensorValue != null) {
1227 int index = deviceSensorValues.indexOf(deviceSensorValue);
1229 deviceSensorValues.add(deviceSensorValue);
1230 if (SensorEnum.isPowerSensor(deviceSensorValue.getSensorType())) {
1231 devicePowerSensorTypes.add(deviceSensorValue.getSensorType());
1233 deviceClimateSensorTypes.add(deviceSensorValue.getSensorType());
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);
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));
1256 setDeviceSensorValue(devSenVal);
1258 logger.debug("Event is echo remove sensorType {} from echoBox", devSenVal.getSensorType());
1259 sensorEchoBox.remove(devSenVal.getSensorType());
1263 private boolean isEchoSensor(SensorEnum sensorType) {
1264 return sensorEchoBox != null ? sensorEchoBox.contains(sensorType) : false;
1267 private List<SensorEnum> sensorEchoBox = Collections.synchronizedList(new LinkedList<>());
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);
1278 logger.debug("echoBox is enabled, ignoring temperation update and wait for sensor Event");
1282 setDsSensorValue(sensorType, value);
1286 public void enableSensorEchoBox() {
1287 if (sensorEchoBox == null) {
1288 sensorEchoBox = Collections.synchronizedList(new LinkedList<>());
1293 public void disableSensorEchoBox() {
1294 sensorEchoBox = null;
1298 public boolean isSensorEchoBoxEnabled() {
1299 return sensorEchoBox != null;
1303 public DeviceSensorValue getDeviceSensorValue(SensorEnum sensorType) {
1304 if (sensorType != null) {
1305 for (DeviceSensorValue devSenVal : deviceSensorValues) {
1306 if (devSenVal.getSensorType().equals(sensorType)) {
1315 public DeviceSensorValue getDeviceSensorValue(Short sensorIndex) {
1316 if (sensorIndex != null) {
1317 for (DeviceSensorValue devSenVal : deviceSensorValues) {
1318 if (devSenVal.getSensorIndex().equals(sensorIndex)) {
1327 public Short getSensorIndex(SensorEnum sensorType) {
1328 if (sensorType != null) {
1329 DeviceSensorValue devSenVal = getDeviceSensorValue(sensorType);
1330 return devSenVal != null ? devSenVal.getSensorIndex() : null;
1336 public SensorEnum getSensorType(Short sensorIndex) {
1337 if (sensorIndex != null) {
1338 DeviceSensorValue devSenVal = getDeviceSensorValue(sensorIndex);
1339 return devSenVal != null ? devSenVal.getSensorType() : null;
1345 public Integer getDsSensorValue(SensorEnum sensorType) {
1346 return getDsSensorValue((Object) sensorType);
1350 public Integer getDsSensorValue(Short sensorIndex) {
1351 return getDsSensorValue((Object) sensorIndex);
1355 public Float getFloatSensorValue(Short sensorIndex) {
1356 return getFloatSensorValue((Object) sensorIndex);
1360 public Float getFloatSensorValue(SensorEnum sensorType) {
1361 return getFloatSensorValue((Object) sensorType);
1365 public boolean setFloatSensorValue(SensorEnum sensorType, Float floatSensorValue) {
1366 return checkAndSetSensorValue(sensorType, null, floatSensorValue);
1370 public boolean setFloatSensorValue(Short sensorIndex, Float floatSensorValue) {
1371 return checkAndSetSensorValue(sensorIndex, null, floatSensorValue);
1375 public boolean setDsSensorValue(Short sensorIndex, Integer dSSensorValue) {
1376 return checkAndSetSensorValue(sensorIndex, dSSensorValue, null);
1380 public boolean setDsSensorValue(SensorEnum sensorType, Integer dSSensorValue) {
1381 return checkAndSetSensorValue(sensorType, dSSensorValue, null);
1385 public boolean setDsSensorValue(Short sensorIndex, Integer dSSensorValue, Float floatSensorValue) {
1386 return checkAndSetSensorValue(sensorIndex, dSSensorValue, floatSensorValue);
1390 public boolean setDsSensorValue(SensorEnum sensorType, Integer dSSensorValue, Float floatSensorValue) {
1391 return checkAndSetSensorValue(sensorType, dSSensorValue, floatSensorValue);
1395 public boolean hasSensors() {
1396 return hasClimateSensors() || hasPowerSensors();
1400 public boolean hasClimateSensors() {
1401 return !deviceClimateSensorTypes.isEmpty();
1405 public boolean hasPowerSensors() {
1406 return !devicePowerSensorTypes.isEmpty();
1409 // Sensor get/set helper methods
1410 private DeviceSensorValue getDeviceSensorValueForGet(Object obj) {
1411 return checkHighOutputCurrent(getDeviceSensorValueForSet(obj));
1414 private DeviceSensorValue getDeviceSensorValueForSet(Object obj) {
1415 if (obj instanceof Short) {
1416 return getDeviceSensorValue((Short) obj);
1418 return getDeviceSensorValue((SensorEnum) obj);
1422 private Integer getDsSensorValue(Object obj) {
1424 DeviceSensorValue devSenVal = checkPowerSensor(getDeviceSensorValueForGet(obj));
1425 return devSenVal != null && devSenVal.getValid() ? devSenVal.getDsValue() : null;
1430 private Float getFloatSensorValue(Object obj) {
1432 DeviceSensorValue devSenVal = checkPowerSensor(getDeviceSensorValueForGet(obj));
1433 return devSenVal != null && devSenVal.getValid() ? devSenVal.getFloatValue() : null;
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);
1449 * Checks output current sensor to return automatically high output current
1450 * sensor, if the sensor exists.
1453 * @return output current high DeviceSensorValue or the given DeviceSensorValue
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);
1464 private boolean checkAndSetSensorValue(Object obj, Integer dsValue, Float floatValue) {
1465 boolean isSet = false;
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);
1476 logger.debug("check devSenVal {} isSet={}", devSenVal.toString(), isSet);
1477 checkSensorValueSet(devSenVal, isSet);
1483 private void checkSensorValueSet(DeviceSensorValue devSenVal, boolean isSet) {
1484 if (devSenVal != null) {
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();
1492 if (devSenVal.getDsValue() > standby) {
1493 this.updateInternalDeviceState(new DeviceStateUpdateImpl(DeviceStateUpdate.ON_OFF, 1));
1496 if (SensorEnum.isPowerSensor(devSenVal.getSensorType())) {
1497 addPowerSensorCache(devSenVal);
1499 informListenerAboutStateUpdate(
1500 new DeviceStateUpdateImpl(devSenVal.getSensorType(), devSenVal.getFloatValue()));
1502 setSensorDataReadingInitialized(devSenVal.getSensorType(), false);
1506 private void addPowerSensorCache(DeviceSensorValue newDevSenVal) {
1507 Integer[] cachedPowerValues = cachedSensorPowerValues.get(outputValue);
1508 if (cachedPowerValues == null) {
1509 cachedPowerValues = new Integer[4];
1511 switch (newDevSenVal.getSensorType()) {
1513 cachedPowerValues[ACTIVE_POWER_ARRAY_FIELD] = newDevSenVal.getDsValue();
1515 case OUTPUT_CURRENT:
1516 cachedPowerValues[OUTPUT_CURRENT_ARRAY_FIELD] = newDevSenVal.getDsValue();
1518 case OUTPUT_CURRENT_H:
1519 cachedPowerValues[OUTPUT_CURRENT_HIGH_ARRAY_FIELD] = newDevSenVal.getDsValue();
1521 case POWER_CONSUMPTION:
1522 cachedPowerValues[POWER_CONSUMPTION_ARRAY_FIELD] = newDevSenVal.getDsValue();
1527 this.cachedSensorPowerValues.put(outputValue, cachedPowerValues);
1531 public synchronized void updateInternalDeviceState(DeviceStateUpdate deviceStateUpdate) {
1532 DeviceStateUpdate deviceStateUpdateInt = internalSetOutputValue(deviceStateUpdate);
1533 if (deviceStateUpdateInt != null) {
1534 validateActiveScene();
1535 informListenerAboutStateUpdate(deviceStateUpdate);
1539 private DeviceStateUpdate internalSetOutputValue(DeviceStateUpdate deviceStateUpdate) {
1540 if (deviceStateUpdate == null) {
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());
1554 case DeviceStateUpdate.ON_OFF:
1555 if (deviceStateUpdate.getValueAsInteger() < 0) {
1556 internalSetOutputValue(0);
1558 internalSetOutputValue(maxOutputValue);
1561 case DeviceStateUpdate.OPEN_CLOSE:
1562 if (deviceStateUpdate.getValueAsInteger() < 0) {
1563 internalSetOutputValue(0);
1565 internalSetOutputValue(maxSlatPosition);
1568 case DeviceStateUpdate.OPEN_CLOSE_ANGLE:
1569 if (deviceStateUpdate.getValueAsInteger() < 0) {
1570 internalSetAngleValue(0);
1572 internalSetAngleValue(maxSlatAngle);
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());
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());
1593 case DeviceStateUpdate.UPDATE_CALL_SCENE:
1594 this.internalCallScene(deviceStateUpdate.getValueAsShort());
1596 case DeviceStateUpdate.UPDATE_UNDO_SCENE:
1597 this.internalUndoScene();
1600 if (deviceStateUpdate.isSensorUpdateType()) {
1601 SensorEnum sensorType = deviceStateUpdate.getTypeAsSensorEnum();
1602 setFloatSensorValue(sensorType, deviceStateUpdate.getValueAsFloat());
1606 return deviceStateUpdate;
1609 private void validateActiveScene() {
1610 if (activeScene == null) {
1613 Integer[] sceneOutput = getStandartSceneOutput(activeScene.getSceneID());
1614 if (sceneOutput == null) {
1615 sceneOutput = sceneOutputMap.get(activeScene.getSceneID());
1617 if (sceneOutput != null) {
1618 boolean outputChanged = false;
1620 if (isBlind() && sceneOutput[1] != slatAngle) {
1621 logger.debug("Scene output angle: {} setted output value {}", sceneOutput[1], slatAngle);
1622 outputChanged = true;
1624 if (sceneOutput[0] != slatPosition) {
1625 logger.debug("Scene output value: {} setted output value {}", sceneOutput[0], slatPosition);
1626 outputChanged = true;
1629 if (sceneOutput[0] != outputValue) {
1630 logger.debug("Scene output value: {} setted output value {}", sceneOutput[0], outputValue);
1631 outputChanged = true;
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);
1647 public DeviceStatusListener unregisterDeviceStatusListener() {
1648 setAllSensorDataRefreshPrioritiesToNever();
1649 return super.unregisterDeviceStatusListener();
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]));
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]));
1669 informListenerAboutStateUpdate(new DeviceStateUpdateImpl(SensorEnum.OUTPUT_CURRENT,
1670 cachedSensorData[OUTPUT_CURRENT_ARRAY_FIELD]));
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]));
1682 * if a {@link DeviceStatusListener} is registered inform him about the new
1683 * state otherwise do nothing.
1685 * @param deviceStateUpdate
1687 private void informListenerAboutStateUpdate(DeviceStateUpdate deviceStateUpdate) {
1688 if (listener != null) {
1689 listener.onDeviceStateChanged(correctDeviceStatusUpdate(deviceStateUpdate));
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);
1698 return new DeviceStateUpdateImpl(DeviceStateUpdate.ON_OFF, DeviceStateUpdate.OFF_VALUE);
1701 return deviceStateUpdate;
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());
1711 @SuppressWarnings("null")
1713 public void saveConfigSceneSpecificationIntoDevice(Map<String, String> propertries) {
1714 if (propertries != null) {
1716 for (String key : propertries.keySet()) {
1717 if (key.startsWith(DigitalSTROMBindingConstants.DEVICE_SCENE)) {
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,
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]) {
1733 sceneSpecNew = new JSONDeviceSceneSpecImpl(sceneParmSplit[1]);
1736 sceneSpecNew.setDontcare(Boolean.parseBoolean(sceneParmSplit[1]));
1739 sceneSpecNew.setLocalPrio(Boolean.parseBoolean(sceneParmSplit[1]));
1742 sceneSpecNew.setSpecialMode(Boolean.parseBoolean(sceneParmSplit[1]));
1745 sceneValue = Integer.parseInt(sceneParmSplit[1]);
1748 sceneAngle = Integer.parseInt(sceneParmSplit[1]);
1752 if (sceneValue > -1) {
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 }));
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);
1766 deviceStateUpdates.add(new DeviceStateUpdateImpl(DeviceStateUpdate.UPDATE_SCENE_CONFIG,
1767 new Short[] { sceneID, (short) -1 }));
1770 } catch (NumberFormatException e) {
1778 @SuppressWarnings("null")
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]) {
1793 sceneSpecNew = new JSONDeviceSceneSpecImpl(sceneParmSplit[1]);
1796 sceneSpecNew.setDontcare(Boolean.parseBoolean(sceneParmSplit[1]));
1799 sceneSpecNew.setLocalPrio(Boolean.parseBoolean(sceneParmSplit[1]));
1802 sceneSpecNew.setSpecialMode(Boolean.parseBoolean(sceneParmSplit[1]));
1805 sceneValue = Integer.parseInt(sceneParmSplit[1]);
1808 sceneAngle = Integer.parseInt(sceneParmSplit[1]);
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 });
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);
1831 public void setConfig(Config config) {
1832 this.config = config;
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] + " ";
1846 public boolean isBinaryInputDevice() {
1847 return !deviceBinaryInputs.isEmpty();
1851 public List<DeviceBinaryInput> getBinaryInputs() {
1852 return deviceBinaryInputs;
1856 public DeviceBinaryInput getBinaryInput(DeviceBinarayInputEnum binaryInputType) {
1857 if (binaryInputType != null) {
1858 for (DeviceBinaryInput binInput : deviceBinaryInputs) {
1859 if (binaryInputType.getBinaryInputType().equals(binInput.getInputType())) {
1868 public Short getBinaryInputState(DeviceBinarayInputEnum binaryInputType) {
1869 DeviceBinaryInput devBinInput = getBinaryInput(binaryInputType);
1870 if (devBinInput != null) {
1871 return devBinInput.getState();
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));
1888 public void setBinaryInputs(List<DeviceBinaryInput> newBinaryInputs) {
1889 this.deviceBinaryInputs.clear();
1890 this.deviceBinaryInputs.addAll(newBinaryInputs);
1891 informListenerAboutConfigChange(ChangeableDeviceConfigEnum.BINARY_INPUTS);
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() + "]";