Skip to content

Commit

Permalink
[velux] Implement new API, and log critical device errors (openhab#13212
Browse files Browse the repository at this point in the history
)

* [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>
  • Loading branch information
andrewfg authored and leifbladt committed Oct 15, 2022
1 parent 148a050 commit 30eb6f6
Show file tree
Hide file tree
Showing 4 changed files with 191 additions and 43 deletions.
Expand Up @@ -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;

/**
Expand Down Expand Up @@ -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;
}
}
Expand Up @@ -18,6 +18,7 @@
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;
Expand Down Expand Up @@ -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.");
Expand Down Expand Up @@ -218,6 +219,7 @@ public void setResponse(short responseCommand, byte[] thisResponseData, boolean
break;
default:
ntfState = VeluxProduct.ProductState.ERROR.value;
statusReply = StatusReply.fromCode(ntfStatusReply);
}
break;
}
Expand Down Expand Up @@ -281,4 +283,9 @@ public VeluxProduct getProduct() {
logger.trace("getProduct(): returning {}.", product);
return product;
}

@Override
public StatusReply getStatusReply() {
return statusReply;
}
}
Expand Up @@ -14,16 +14,14 @@

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;
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;
Expand Down Expand Up @@ -71,12 +69,6 @@ private ChannelActuatorPosition() {
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

/**
Expand Down Expand Up @@ -105,12 +97,10 @@ private ChannelActuatorPosition() {

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
}
Expand All @@ -127,45 +117,68 @@ private ChannelActuatorPosition() {
}

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) {
Expand Down
@@ -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);
}
}

0 comments on commit 30eb6f6

Please sign in to comment.