diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/GetProduct.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/GetProduct.java
index 6709302d2..a4c647d28 100644
--- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/GetProduct.java
+++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/GetProduct.java
@@ -13,6 +13,7 @@
package org.openhab.binding.velux.internal.bridge.common;
import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.velux.internal.things.StatusReply;
import org.openhab.binding.velux.internal.things.VeluxProduct;
/**
@@ -47,4 +48,8 @@ public abstract class GetProduct implements BridgeCommunicationProtocol {
* @return veluxProduct as VeluxProduct.
*/
public abstract VeluxProduct getProduct();
+
+ public StatusReply getStatusReply() {
+ return StatusReply.COMMAND_COMPLETED_OK;
+ }
}
diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCgetProductStatus.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCgetProductStatus.java
index 2fd8d4f01..44bd6f59a 100644
--- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCgetProductStatus.java
+++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCgetProductStatus.java
@@ -18,6 +18,7 @@ import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.velux.internal.bridge.common.GetProduct;
import org.openhab.binding.velux.internal.bridge.slip.utils.KLF200Response;
import org.openhab.binding.velux.internal.bridge.slip.utils.Packet;
+import org.openhab.binding.velux.internal.things.StatusReply;
import org.openhab.binding.velux.internal.things.VeluxKLFAPI.Command;
import org.openhab.binding.velux.internal.things.VeluxKLFAPI.CommandNumber;
import org.openhab.binding.velux.internal.things.VeluxProduct;
@@ -81,8 +82,8 @@ public class SCgetProductStatus extends GetProduct implements SlipBridgeCommunic
private boolean success = false;
private boolean finished = false;
-
private VeluxProduct product = VeluxProduct.UNKNOWN;
+ private StatusReply statusReply = StatusReply.COMMAND_COMPLETED_OK;
public SCgetProductStatus() {
logger.debug("SCgetProductStatus(Constructor) called.");
@@ -218,6 +219,7 @@ public class SCgetProductStatus extends GetProduct implements SlipBridgeCommunic
break;
default:
ntfState = VeluxProduct.ProductState.ERROR.value;
+ statusReply = StatusReply.fromCode(ntfStatusReply);
}
break;
}
@@ -281,4 +283,9 @@ public class SCgetProductStatus extends GetProduct implements SlipBridgeCommunic
logger.trace("getProduct(): returning {}.", product);
return product;
}
+
+ @Override
+ public StatusReply getStatusReply() {
+ return statusReply;
+ }
}
diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelActuatorPosition.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelActuatorPosition.java
index d5cba669a..ecc4f059d 100644
--- a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelActuatorPosition.java
+++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelActuatorPosition.java
@@ -14,9 +14,6 @@ package org.openhab.binding.velux.internal.handler;
import static org.openhab.binding.velux.internal.VeluxBindingConstants.*;
-import java.util.Arrays;
-import java.util.List;
-
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.velux.internal.bridge.common.GetProduct;
@@ -24,6 +21,7 @@ import org.openhab.binding.velux.internal.bridge.common.RunProductCommand;
import org.openhab.binding.velux.internal.bridge.slip.FunctionalParameters;
import org.openhab.binding.velux.internal.bridge.slip.SCrunProductCommand;
import org.openhab.binding.velux.internal.handler.utils.Thing2VeluxActuator;
+import org.openhab.binding.velux.internal.things.StatusReply;
import org.openhab.binding.velux.internal.things.VeluxExistingProducts;
import org.openhab.binding.velux.internal.things.VeluxProduct;
import org.openhab.binding.velux.internal.things.VeluxProduct.ProductBridgeIndex;
@@ -71,12 +69,6 @@ final class ChannelActuatorPosition extends ChannelHandlerTemplate {
throw new AssertionError();
}
- /*
- * List of product states that shall be processed
- */
- private static final List STATES_TO_PROCESS = Arrays.asList(ProductState.DONE, ProductState.EXECUTING,
- ProductState.MANUAL, ProductState.UNKNOWN);
-
// Public methods
/**
@@ -105,12 +97,10 @@ final class ChannelActuatorPosition extends ChannelHandlerTemplate {
GetProduct bcp = null;
switch (channelId) {
- case CHANNEL_VANE_POSITION:
- bcp = thisBridgeHandler.thisBridge.bridgeAPI().getProductStatus();
- break;
case CHANNEL_ACTUATOR_POSITION:
case CHANNEL_ACTUATOR_STATE:
- bcp = thisBridgeHandler.thisBridge.bridgeAPI().getProduct();
+ case CHANNEL_VANE_POSITION:
+ bcp = thisBridgeHandler.thisBridge.bridgeAPI().getProductStatus();
default:
// unknown channel, will exit
}
@@ -127,45 +117,68 @@ final class ChannelActuatorPosition extends ChannelHandlerTemplate {
}
VeluxProduct newProduct = bcp.getProduct();
- if (STATES_TO_PROCESS.contains(newProduct.getProductState())) {
- ProductBridgeIndex productBridgeIndex = newProduct.getBridgeProductIndex();
- VeluxExistingProducts existingProducts = thisBridgeHandler.existingProducts();
- VeluxProduct existingProduct = existingProducts.get(productBridgeIndex);
- if (!VeluxProduct.UNKNOWN.equals(existingProduct)) {
- switch (channelId) {
- case CHANNEL_VANE_POSITION:
- case CHANNEL_ACTUATOR_POSITION:
- case CHANNEL_ACTUATOR_STATE: {
- if (existingProducts.update(newProduct)) {
- existingProduct = existingProducts.get(productBridgeIndex);
- int posValue = VeluxProductPosition.VPP_VELUX_UNKNOWN;
- switch (channelId) {
- case CHANNEL_VANE_POSITION:
- posValue = existingProduct.getVaneDisplayPosition();
- break;
- case CHANNEL_ACTUATOR_POSITION:
- case CHANNEL_ACTUATOR_STATE:
- posValue = existingProduct.getDisplayPosition();
- }
- VeluxProductPosition position = new VeluxProductPosition(posValue);
- if (position.isValid()) {
+ ProductBridgeIndex productBridgeIndex = newProduct.getBridgeProductIndex();
+ VeluxExistingProducts existingProducts = thisBridgeHandler.existingProducts();
+ VeluxProduct existingProduct = existingProducts.get(productBridgeIndex);
+ ProductState productState = newProduct.getProductState();
+ switch (productState) {
+ case DONE:
+ case EXECUTING:
+ case MANUAL:
+ case UNKNOWN:
+ if (!VeluxProduct.UNKNOWN.equals(existingProduct)) {
+ switch (channelId) {
+ case CHANNEL_VANE_POSITION:
+ case CHANNEL_ACTUATOR_POSITION:
+ case CHANNEL_ACTUATOR_STATE: {
+ if (existingProducts.update(newProduct)) {
+ existingProduct = existingProducts.get(productBridgeIndex);
+ int posValue = VeluxProductPosition.VPP_VELUX_UNKNOWN;
switch (channelId) {
case CHANNEL_VANE_POSITION:
- newState = position.getPositionAsPercentType(false);
+ posValue = existingProduct.getVaneDisplayPosition();
break;
case CHANNEL_ACTUATOR_POSITION:
- newState = position.getPositionAsPercentType(veluxActuator.isInverted());
- break;
case CHANNEL_ACTUATOR_STATE:
- newState = OnOffType
- .from(position.getPositionAsPercentType(veluxActuator.isInverted())
- .intValue() > 50);
+ posValue = existingProduct.getDisplayPosition();
+ }
+ VeluxProductPosition position = new VeluxProductPosition(posValue);
+ if (position.isValid()) {
+ switch (channelId) {
+ case CHANNEL_VANE_POSITION:
+ newState = position.getPositionAsPercentType(false);
+ break;
+ case CHANNEL_ACTUATOR_POSITION:
+ newState = position
+ .getPositionAsPercentType(veluxActuator.isInverted());
+ break;
+ case CHANNEL_ACTUATOR_STATE:
+ newState = OnOffType.from(
+ position.getPositionAsPercentType(veluxActuator.isInverted())
+ .intValue() > 50);
+ }
}
}
}
}
}
- }
+ break;
+ case WAITING_FOR_POWER:
+ case ERROR:
+ StatusReply statusReply = productState == ProductState.WAITING_FOR_POWER
+ ? StatusReply.NODE_WAITING_FOR_POWER
+ : bcp.getStatusReply();
+ if (statusReply.isError()) {
+ String id = VeluxProduct.UNKNOWN.equals(existingProduct)
+ ? newProduct.getBridgeProductIndex().toString()
+ : existingProduct.getProductUniqueIndex();
+ if (statusReply.isCriticalError()) {
+ LOGGER.warn("Product Id:{} encountered an error with StatusReply:{}", id, statusReply);
+ } else {
+ LOGGER.info("Product Id:{} encountered an error with StatusReply:{}", id, statusReply);
+ }
+ }
+ default:
}
if (newState == null) {
diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/StatusReply.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/StatusReply.java
new file mode 100644
index 000000000..8053306f6
--- /dev/null
+++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/StatusReply.java
@@ -0,0 +1,123 @@
+/**
+ * Copyright (c) 2010-2022 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.velux.internal.things;
+
+import java.util.List;
+import java.util.Map;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ * An enum that describes the various predefined Status Reply code values and their meanings.
+ *
+ * @author Andrew Fiddian-Green - Initial contribution
+ *
+ */
+@NonNullByDefault
+public enum StatusReply {
+ UNKNOWN_STATUS_REPLY(0x00),
+ COMMAND_COMPLETED_OK(0x01),
+ NO_CONTACT(0x02),
+ MANUALLY_OPERATED(0x03),
+ BLOCKED(0x04),
+ WRONG_SYSTEMKEY(0x05),
+ PRIORITY_LEVEL_LOCKED(0x06),
+ REACHED_WRONG_POSITION(0x07),
+ ERROR_DURING_EXECUTION(0x08),
+ NO_EXECUTION(0x09),
+ CALIBRATING(0x0A),
+ POWER_CONSUMPTION_TOO_HIGH(0x0B),
+ POWER_CONSUMPTION_TOO_LOW(0x0C),
+ LOCK_POSITION_OPEN(0x0D),
+ MOTION_TIME_TOO_LONG(0x0E),
+ THERMAL_PROTECTION(0x0F),
+ PRODUCT_NOT_OPERATIONAL(0x10),
+ FILTER_MAINTENANCE_NEEDED(0x11),
+ BATTERY_LEVEL(0x12),
+ TARGET_MODIFIED(0x13),
+ MODE_NOT_IMPLEMENTED(0x14),
+ COMMAND_INCOMPATIBLE_TO_MOVEMENT(0x15),
+ USER_ACTION(0x16),
+ DEAD_BOLT_ERROR(0x17),
+ AUTOMATIC_CYCLE_ENGAGED(0x18),
+ WRONG_LOAD_CONNECTED(0x19),
+ COLOUR_NOT_REACHABLE(0x1A),
+ TARGET_NOT_REACHABLE(0x1B),
+ BAD_INDEX_RECEIVED(0x1C),
+ COMMAND_OVERRULED(0x1D),
+ NODE_WAITING_FOR_POWER(0x1E),
+ INFORMATION_CODE(0xDF),
+ PARAMETER_LIMITED(0xE0),
+ LIMITATION_BY_LOCAL_USER(0xE1),
+ LIMITATION_BY_USER(0xE2),
+ LIMITATION_BY_RAIN(0xE3),
+ LIMITATION_BY_TIMER(0xE4),
+ LIMITATION_BY_UPS(0xE6),
+ LIMITATION_BY_UNKNOWN_DEVICE(0xE7),
+ LIMITATION_BY_SAAC(0xEA),
+ LIMITATION_BY_WIND(0xEB),
+ LIMITATION_BY_MYSELF(0xEC),
+ LIMITATION_BY_AUTOMATIC_CYCLE(0xED),
+ LIMITATION_BY_EMERGENCY(0xEE);
+
+ private final int code;
+
+ private StatusReply(int code) {
+ this.code = code;
+ }
+
+ /*
+ * List of critical errors
+ */
+ private static final List CRITICAL_ERRORS = List.of(BLOCKED, POWER_CONSUMPTION_TOO_HIGH,
+ THERMAL_PROTECTION, LOCK_POSITION_OPEN, PRODUCT_NOT_OPERATIONAL, DEAD_BOLT_ERROR, FILTER_MAINTENANCE_NEEDED,
+ BATTERY_LEVEL, NODE_WAITING_FOR_POWER);
+
+ public int getCode() {
+ return code;
+ }
+
+ private static final Map LOOKUP = Stream.of(StatusReply.values())
+ .collect(Collectors.toMap(StatusReply::getCode, Function.identity()));
+
+ /**
+ * Get the StatusReply value that corresponds to the given status code.
+ *
+ * @param statusReplyCode the status code value
+ * @return the StatusReply value that corresponds to the status code
+ */
+ public static StatusReply fromCode(int statusReplyCode) {
+ return LOOKUP.getOrDefault(statusReplyCode, UNKNOWN_STATUS_REPLY);
+ }
+
+ /**
+ * Check if this Status Reply indicates an error.
+ *
+ * @return true if the status code is an error code.
+ */
+ public boolean isError() {
+ return this != COMMAND_COMPLETED_OK;
+ }
+
+ /**
+ * Check if this Status Reply indicates a critical error.
+ *
+ * @return true if the status code is a critical error code.
+ */
+ public boolean isCriticalError() {
+ return isError() && CRITICAL_ERRORS.contains(this);
+ }
+}