]> git.basschouten.com Git - openhab-addons.git/commitdiff
[velux] Implement new API, and log critical device errors (#13212)
authorAndrew Fiddian-Green <software@whitebear.ch>
Fri, 12 Aug 2022 07:26:51 +0000 (08:26 +0100)
committerGitHub <noreply@github.com>
Fri, 12 Aug 2022 07:26:51 +0000 (09:26 +0200)
* [velux] use new API, and log StatusReply codes
Signed-off-by: Andrew Fiddian-Green <software@whitebear.ch>
Signed-off-by: Andrew Fiddian-Green <software@whitebear.ch>
bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/GetProduct.java
bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCgetProductStatus.java
bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelActuatorPosition.java
bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/StatusReply.java [new file with mode: 0644]

index 6709302d2c1678af6f1cdff1852fe841883ecf0b..a4c647d2804d44617a27aeec9a8ff136a88a5349 100644 (file)
@@ -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 <b>veluxProduct</b> as VeluxProduct.
      */
     public abstract VeluxProduct getProduct();
+
+    public StatusReply getStatusReply() {
+        return StatusReply.COMMAND_COMPLETED_OK;
+    }
 }
index 2fd8d4f015dd5ada2fd019006e342dce8fbeee0c..44bd6f59a1f613aeadd2ecaf6cf60df2fe717cc0 100644 (file)
@@ -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;
+    }
 }
index d5cba669abb6562c408db69e0d3c1c8d69e0ec7e..ecc4f059d74a281565151dfd5df4c4717c0a5e7e 100644 (file)
@@ -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<ProductState> 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 (file)
index 0000000..8053306
--- /dev/null
@@ -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<StatusReply> 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<Integer, StatusReply> 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);
+    }
+}