[velux] Add an action to simultaneously set main and vane positions (#13199)
* [velux] add moveMainAndVane action * [velux] add claridications to read me * [velux] refactor actions and translate * [velux] fix thing lookup Signed-off-by: Andrew Fiddian-Green <software@whitebear.ch>
This commit is contained in:
committed by
GitHub
parent
bb3be91981
commit
41fcdd6c71
@@ -1,43 +0,0 @@
|
||||
/**
|
||||
* 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.action;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* The {@link IVeluxActions} defines rule action interface for rebooting the bridge
|
||||
*
|
||||
* @author Andrew Fiddian-Green - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public interface IVeluxActions {
|
||||
|
||||
/**
|
||||
* Action to send a reboot command to a Velux Bridge
|
||||
*
|
||||
* @return true if the command was sent
|
||||
* @throws IllegalStateException if something is wrong
|
||||
*/
|
||||
Boolean rebootBridge() throws IllegalStateException;
|
||||
|
||||
/**
|
||||
* Action to send a relative move command to a Velux actuator
|
||||
*
|
||||
* @param nodeId the node Id in the bridge
|
||||
* @param relativePercent the target position relative to its current position (-100% <= relativePercent <= +100%)
|
||||
* @return true if the command was sent
|
||||
* @throws NumberFormatException if either of the arguments is not an integer, or out of range
|
||||
* @throws IllegalStateException if anything else is wrong
|
||||
*/
|
||||
Boolean moveRelative(String nodeId, String relativePercent) throws NumberFormatException, IllegalStateException;
|
||||
}
|
||||
@@ -15,9 +15,11 @@ package org.openhab.binding.velux.internal.action;
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.velux.internal.handler.VeluxBridgeHandler;
|
||||
import org.openhab.binding.velux.internal.things.VeluxProduct.ProductBridgeIndex;
|
||||
import org.openhab.core.automation.annotation.ActionInput;
|
||||
import org.openhab.core.automation.annotation.ActionOutput;
|
||||
import org.openhab.core.automation.annotation.RuleAction;
|
||||
import org.openhab.core.library.types.PercentType;
|
||||
import org.openhab.core.thing.binding.ThingActions;
|
||||
import org.openhab.core.thing.binding.ThingActionsScope;
|
||||
import org.openhab.core.thing.binding.ThingHandler;
|
||||
@@ -31,7 +33,7 @@ import org.slf4j.LoggerFactory;
|
||||
*/
|
||||
@ThingActionsScope(name = "velux")
|
||||
@NonNullByDefault
|
||||
public class VeluxActions implements ThingActions, IVeluxActions {
|
||||
public class VeluxActions implements ThingActions {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(VeluxActions.class);
|
||||
|
||||
@@ -49,38 +51,42 @@ public class VeluxActions implements ThingActions, IVeluxActions {
|
||||
return this.bridgeHandler;
|
||||
}
|
||||
|
||||
@Override
|
||||
@RuleAction(label = "reboot Bridge", description = "issues a reboot command to the KLF200 bridge")
|
||||
public @ActionOutput(name = "executing", type = "java.lang.Boolean", label = "executing", description = "indicates the command was issued") Boolean rebootBridge()
|
||||
@RuleAction(label = "@text/action.reboot.label", description = "@text/action.reboot.descr")
|
||||
public @ActionOutput(name = "running", type = "java.lang.Boolean", label = "@text/action.run.label", description = "@text/action.run.descr") Boolean rebootBridge()
|
||||
throws IllegalStateException {
|
||||
logger.trace("rebootBridge(): action called");
|
||||
VeluxBridgeHandler bridge = bridgeHandler;
|
||||
if (bridge == null) {
|
||||
VeluxBridgeHandler bridgeHandler = this.bridgeHandler;
|
||||
if (bridgeHandler == null) {
|
||||
throw new IllegalStateException("Bridge instance is null");
|
||||
}
|
||||
return bridge.runReboot();
|
||||
return bridgeHandler.rebootBridge();
|
||||
}
|
||||
|
||||
@Override
|
||||
@RuleAction(label = "move relative", description = "issues a relative move command to an actuator")
|
||||
public @ActionOutput(name = "executing", type = "java.lang.Boolean", label = "executing", description = "indicates the command was issued") Boolean moveRelative(
|
||||
@ActionInput(name = "nodeId", required = true, label = "nodeId", description = "actuator id in the bridge", type = "java.lang.String") String nodeId,
|
||||
@ActionInput(name = "relativePercent", required = true, label = "relativePercent", description = "position delta from current", type = "java.lang.String") String relativePercent)
|
||||
throws NumberFormatException, IllegalStateException {
|
||||
@RuleAction(label = "@text/action.moveRelative.label", description = "@text/action.moveRelative.descr")
|
||||
public @ActionOutput(name = "running", type = "java.lang.Boolean", label = "@text/action.run.label", description = "@text/action.run.descr") Boolean moveRelative(
|
||||
@ActionInput(name = "nodeId", label = "@text/action.node.label", description = "@text/action.node.descr") @Nullable String nodeId,
|
||||
@ActionInput(name = "relativePercent", label = "@text/action.relative.label", description = "@text/action.relative.descr") @Nullable String relativePercent)
|
||||
throws NumberFormatException, IllegalStateException, IllegalArgumentException {
|
||||
logger.trace("moveRelative(): action called");
|
||||
VeluxBridgeHandler bridge = bridgeHandler;
|
||||
if (bridge == null) {
|
||||
VeluxBridgeHandler bridgeHandler = this.bridgeHandler;
|
||||
if (bridgeHandler == null) {
|
||||
throw new IllegalStateException("Bridge instance is null");
|
||||
}
|
||||
if (nodeId == null) {
|
||||
throw new IllegalArgumentException("Node Id is null");
|
||||
}
|
||||
int node = Integer.parseInt(nodeId);
|
||||
if (node < 0 || node > 200) {
|
||||
throw new NumberFormatException("Node Id out of range");
|
||||
}
|
||||
if (relativePercent == null) {
|
||||
throw new IllegalArgumentException("Relative Percent is null");
|
||||
}
|
||||
int relPct = Integer.parseInt(relativePercent);
|
||||
if (Math.abs(relPct) > 100) {
|
||||
throw new NumberFormatException("Relative Percent out of range");
|
||||
}
|
||||
return bridge.moveRelative(node, relPct);
|
||||
return bridgeHandler.moveRelative(node, relPct);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -93,10 +99,10 @@ public class VeluxActions implements ThingActions, IVeluxActions {
|
||||
*/
|
||||
public static Boolean rebootBridge(@Nullable ThingActions actions)
|
||||
throws IllegalArgumentException, IllegalStateException {
|
||||
if (!(actions instanceof IVeluxActions)) {
|
||||
if (!(actions instanceof VeluxActions)) {
|
||||
throw new IllegalArgumentException("Unsupported action");
|
||||
}
|
||||
return ((IVeluxActions) actions).rebootBridge();
|
||||
return ((VeluxActions) actions).rebootBridge();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -107,14 +113,67 @@ public class VeluxActions implements ThingActions, IVeluxActions {
|
||||
* @param relativePercent the target position relative to its current position (-100% <= relativePercent <= +100%)
|
||||
* @return true if the command was sent
|
||||
* @throws IllegalArgumentException if actions is invalid
|
||||
* @throws NumberFormatException if either of nodeId or relativePercent is not an integer, or out of range
|
||||
* @throws IllegalStateException if anything else is wrong
|
||||
* @throws NumberFormatException if either of nodeId or relativePercent is not an integer, or out of range
|
||||
*/
|
||||
public static Boolean moveRelative(@Nullable ThingActions actions, String nodeId, String relativePercent)
|
||||
public static Boolean moveRelative(@Nullable ThingActions actions, @Nullable String nodeId,
|
||||
@Nullable String relativePercent)
|
||||
throws IllegalArgumentException, NumberFormatException, IllegalStateException {
|
||||
if (!(actions instanceof IVeluxActions)) {
|
||||
if (!(actions instanceof VeluxActions)) {
|
||||
throw new IllegalArgumentException("Unsupported action");
|
||||
}
|
||||
return ((IVeluxActions) actions).moveRelative(nodeId, relativePercent);
|
||||
return ((VeluxActions) actions).moveRelative(nodeId, relativePercent);
|
||||
}
|
||||
|
||||
@RuleAction(label = "@text/action.moveMainAndVane.label", description = "@text/action.moveMainAndVane.descr")
|
||||
public @ActionOutput(name = "running", type = "java.lang.Boolean", label = "@text/action.run.label", description = "@text/action.run.descr") Boolean moveMainAndVane(
|
||||
@ActionInput(name = "thingName", label = "@text/action.thing.label", description = "@text/action.thing.descr") @Nullable String thingName,
|
||||
@ActionInput(name = "mainPercent", label = "@text/action.main.label", description = "@text/action.main.descr") @Nullable Integer mainPercent,
|
||||
@ActionInput(name = "vanePercent", label = "@text/action.vane.label", description = "@text/action.vane.descr") @Nullable Integer vanePercent)
|
||||
throws NumberFormatException, IllegalArgumentException, IllegalStateException {
|
||||
logger.trace("moveMainAndVane(thingName:'{}', mainPercent:{}, vanePercent:{}) action called", thingName,
|
||||
mainPercent, vanePercent);
|
||||
VeluxBridgeHandler bridgeHandler = this.bridgeHandler;
|
||||
if (bridgeHandler == null) {
|
||||
throw new IllegalStateException("Bridge instance is null");
|
||||
}
|
||||
if (thingName == null) {
|
||||
throw new IllegalArgumentException("Thing name is null");
|
||||
}
|
||||
ProductBridgeIndex node = bridgeHandler.getProductBridgeIndex(thingName);
|
||||
if (ProductBridgeIndex.UNKNOWN.equals(node)) {
|
||||
throw new IllegalArgumentException("Bridge does not contain a thing with name " + thingName);
|
||||
}
|
||||
if (mainPercent == null) {
|
||||
throw new IllegalArgumentException("Main perent is null");
|
||||
}
|
||||
PercentType mainPercentType = new PercentType(mainPercent);
|
||||
if (vanePercent == null) {
|
||||
throw new IllegalArgumentException("Vane perent is null");
|
||||
}
|
||||
PercentType vanePercenType = new PercentType(vanePercent);
|
||||
return bridgeHandler.moveMainAndVane(node, mainPercentType, vanePercenType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Action to simultaneously move the shade main position and vane positions.
|
||||
*
|
||||
*
|
||||
* @param actions ThingActions from the caller
|
||||
* @param thingName the name of the thing to be moved (e.g. 'velux:rollershutter:hubid:thingid')
|
||||
* @param mainPercent the desired main position (range 0..100)
|
||||
* @param vanePercent the desired vane position (range 0..100)
|
||||
* @return true if the command was sent
|
||||
* @throws NumberFormatException if any of the arguments are not an integer
|
||||
* @throws IllegalArgumentException if any of the arguments are invalid
|
||||
* @throws IllegalStateException if anything else is wrong
|
||||
*/
|
||||
public static Boolean moveMainAndVane(@Nullable ThingActions actions, @Nullable String thingName,
|
||||
@Nullable Integer mainPercent, @Nullable Integer vanePercent)
|
||||
throws NumberFormatException, IllegalArgumentException, IllegalStateException {
|
||||
if (!(actions instanceof VeluxActions)) {
|
||||
throw new IllegalArgumentException("Unsupported action");
|
||||
}
|
||||
return ((VeluxActions) actions).moveMainAndVane(thingName, mainPercent, vanePercent);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,8 +13,9 @@
|
||||
package org.openhab.binding.velux.internal.handler;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
@@ -43,6 +44,7 @@ import org.openhab.binding.velux.internal.bridge.common.BridgeCommunicationProto
|
||||
import org.openhab.binding.velux.internal.bridge.common.RunProductCommand;
|
||||
import org.openhab.binding.velux.internal.bridge.common.RunReboot;
|
||||
import org.openhab.binding.velux.internal.bridge.json.JsonVeluxBridge;
|
||||
import org.openhab.binding.velux.internal.bridge.slip.FunctionalParameters;
|
||||
import org.openhab.binding.velux.internal.bridge.slip.SlipVeluxBridge;
|
||||
import org.openhab.binding.velux.internal.config.VeluxBridgeConfiguration;
|
||||
import org.openhab.binding.velux.internal.development.Threads;
|
||||
@@ -143,14 +145,14 @@ public class VeluxBridgeHandler extends ExtendedBaseBridgeHandler implements Vel
|
||||
* ***** Default visibility Objects *****
|
||||
*/
|
||||
|
||||
VeluxBridge thisBridge = myJsonBridge;
|
||||
public VeluxBridge thisBridge = myJsonBridge;
|
||||
public BridgeParameters bridgeParameters = new BridgeParameters();
|
||||
Localization localization;
|
||||
public Localization localization;
|
||||
|
||||
/**
|
||||
* Mapping from ChannelUID to class Thing2VeluxActuator, which return Velux device information, probably cached.
|
||||
*/
|
||||
Map<ChannelUID, Thing2VeluxActuator> channel2VeluxActuator = new ConcurrentHashMap<>();
|
||||
public final Map<ChannelUID, Thing2VeluxActuator> channel2VeluxActuator = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* Information retrieved by {@link VeluxBinding#VeluxBinding}.
|
||||
@@ -819,7 +821,7 @@ public class VeluxBridgeHandler extends ExtendedBaseBridgeHandler implements Vel
|
||||
*/
|
||||
@Override
|
||||
public Collection<Class<? extends ThingHandlerService>> getServices() {
|
||||
return Collections.singletonList(VeluxActions.class);
|
||||
return Set.of(VeluxActions.class);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -827,7 +829,7 @@ public class VeluxBridgeHandler extends ExtendedBaseBridgeHandler implements Vel
|
||||
*
|
||||
* @return true if the command could be issued
|
||||
*/
|
||||
public boolean runReboot() {
|
||||
public boolean rebootBridge() {
|
||||
logger.trace("runReboot() called on {}", getThing().getUID());
|
||||
RunReboot bcp = thisBridge.bridgeAPI().runReboot();
|
||||
if (bcp != null) {
|
||||
@@ -907,4 +909,52 @@ public class VeluxBridgeHandler extends ExtendedBaseBridgeHandler implements Vel
|
||||
public boolean isDisposing() {
|
||||
return disposing;
|
||||
}
|
||||
|
||||
/**
|
||||
* Exported method (called by an OpenHAB Rules Action) to simultaneously move the shade main position and the vane
|
||||
* position.
|
||||
*
|
||||
* @param node the node index in the bridge.
|
||||
* @param mainPosition the desired main position.
|
||||
* @param vanePosition the desired vane position.
|
||||
* @return true if the command could be issued.
|
||||
*/
|
||||
public Boolean moveMainAndVane(ProductBridgeIndex node, PercentType mainPosition, PercentType vanePosition) {
|
||||
logger.trace("moveMainAndVane() called on {}", getThing().getUID());
|
||||
RunProductCommand bcp = thisBridge.bridgeAPI().runProductCommand();
|
||||
if (bcp != null) {
|
||||
VeluxProduct product = existingProducts().get(node).clone();
|
||||
FunctionalParameters functionalParameters = null;
|
||||
if (product.supportsVanePosition()) {
|
||||
int vanePos = new VeluxProductPosition(vanePosition).getPositionAsVeluxType();
|
||||
product.setVanePosition(vanePos);
|
||||
functionalParameters = product.getFunctionalParameters();
|
||||
}
|
||||
VeluxProductPosition mainPos = new VeluxProductPosition(mainPosition);
|
||||
bcp.setNodeIdAndParameters(node.toInt(), mainPos, functionalParameters);
|
||||
submitCommunicationsJob(() -> {
|
||||
if (thisBridge.bridgeCommunicate(bcp)) {
|
||||
logger.trace("moveMainAndVane() command {}sucessfully sent to {}",
|
||||
bcp.isCommunicationSuccessful() ? "" : "un", getThing().getUID());
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the bridge product index for a given thing name.
|
||||
*
|
||||
* @param thingName the thing name
|
||||
* @return the bridge product index or ProductBridgeIndex.UNKNOWN if not found.
|
||||
*/
|
||||
public ProductBridgeIndex getProductBridgeIndex(String thingName) {
|
||||
for (Entry<ChannelUID, Thing2VeluxActuator> entry : channel2VeluxActuator.entrySet()) {
|
||||
if (thingName.equals(entry.getKey().getThingUID().getAsString())) {
|
||||
return entry.getValue().getProductBridgeIndex();
|
||||
}
|
||||
}
|
||||
return ProductBridgeIndex.UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -162,3 +162,24 @@ runtime.bridge-offline-login-sequence-failed = Login sequence failed.
|
||||
#
|
||||
channelValue.check-integrity-failed = Integrity check failed: The following scenes are unused:
|
||||
channelValue.check-integrity-ok = Integrity check ok. All scenes are used within Items.
|
||||
#
|
||||
# Actions
|
||||
#
|
||||
action.reboot.label = Reboot Bridge
|
||||
action.reboot.descr = Issues a reboot command to the KLF200 bridge
|
||||
action.moveRelative.label = Move Relative
|
||||
action.moveRelative.descr = Issues a relative move command to an actuator
|
||||
action.moveMainAndVane.label = Move main and vane position simultaneously
|
||||
action.moveMainAndVane.descr = Issues a simultaneous command to move both the main position and the vane position of a shade
|
||||
action.run.label = Executing
|
||||
action.run.descr = Indicates the command was issued
|
||||
action.node.label = Node Id
|
||||
action.node.descr = Actuator Id in the bridge
|
||||
action.relative.label = Relative Percent
|
||||
action.relative.descr = Position delta from current
|
||||
action.thing.label = Thing Name
|
||||
action.thing.descr = UID of the actuator thing to be moved
|
||||
action.main.label = Main Percent
|
||||
action.main.descr = Position percentage to move to
|
||||
action.vane.label = Vane Percent
|
||||
action.vane.descr = Vane position percentage to move to
|
||||
|
||||
Reference in New Issue
Block a user