Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[velux] hub discovery; representation properties; socket lock up issues #8777

Merged
merged 74 commits into from
Dec 1, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
74 commits
Select commit Hold shift + click to select a range
ea00ed9
[velux] set explicit timeouts & keepalives on socket
andrewfg Oct 13, 2020
0a478a7
[velux] implement mdns service
andrewfg Oct 13, 2020
cf0963c
[velux] tweak mdns-sd service
andrewfg Oct 14, 2020
7180ee3
[velux] wip for mdns discovery
andrewfg Oct 15, 2020
0dac4b6
[velux] fix representation property names
andrewfg Oct 15, 2020
30697fe
[velux] fix representation properties
andrewfg Oct 16, 2020
21de8ea
[velux] finalize mdns
andrewfg Oct 16, 2020
0605697
[velux] spotless
andrewfg Oct 16, 2020
cc80393
[velux] use both mDNS and regular DNS to resolve ip addresses
andrewfg Oct 19, 2020
d022752
[velux] code style
andrewfg Oct 21, 2020
cd662ce
[velux] complete class rewrite using asynchronous polling thread
andrewfg Oct 26, 2020
7076baa
[velux] refactor bridgeDirectCommunicate to simplify looping
andrewfg Oct 26, 2020
ca2bd90
[velux] asynchronous polling means Thread.sleep no longer needed
andrewfg Oct 26, 2020
1c23bb3
[velux] modified to use asynchronous polling
andrewfg Oct 26, 2020
063c12f
[velux] add auto discovery to ReadMe.md
andrewfg Oct 26, 2020
860f8c8
[velux] implement closeable
andrewfg Oct 27, 2020
60c7a6e
[velux] logging tweaks
andrewfg Oct 28, 2020
3cf1047
[velux] faster synch of actuator changes
andrewfg Oct 29, 2020
3100487
[velux] latest jar upload
andrewfg Oct 29, 2020
c512683
[velux] comms tweaks
andrewfg Oct 31, 2020
9b650fe
[velux] faster update of channels
andrewfg Oct 31, 2020
0f82b37
[velux] upload latest jar file
andrewfg Oct 31, 2020
8ff22da
[velux] fix mdns discovery
andrewfg Nov 3, 2020
f7278eb
[velux] tweaked logging
andrewfg Nov 3, 2020
f7659df
[velux] upload latest jar file
andrewfg Nov 3, 2020
0d44e91
[velux] use single thread executor instead of thread pool
andrewfg Nov 4, 2020
f9ac8aa
[velux] faster synch of actuator changes
andrewfg Oct 29, 2020
4ef30f3
[velux] shut down task executor
andrewfg Nov 5, 2020
4083f06
[velux] read me
andrewfg Nov 6, 2020
964d3fd
[velux] fix configuration checks
andrewfg Nov 6, 2020
2d43400
[velux] java doc
andrewfg Nov 6, 2020
8a8d42a
[velux] lower logger level
andrewfg Nov 6, 2020
3c6f936
[velux] remove comment
andrewfg Nov 6, 2020
7ed2894
[velux] changes in refresh code
andrewfg Nov 6, 2020
3bcfa77
[velux] fix bug in enum
andrewfg Nov 10, 2020
5155456
[velux] better shutdown code
andrewfg Nov 10, 2020
f7ed1ad
[velux] first start up of polling loop
andrewfg Nov 10, 2020
5c25993
[velux] cosmetics
andrewfg Nov 10, 2020
c73a7ac
[velux] upload latest jar
andrewfg Nov 10, 2020
47de907
[velux] use all possible in motion states
andrewfg Nov 10, 2020
8c853e9
[velux] use all possible in motion states
andrewfg Nov 10, 2020
cbf990f
[velux] implement reboot action
andrewfg Nov 12, 2020
4b77020
[velux] undef position in manual override, and better code style
andrewfg Nov 12, 2020
868cbd4
[velux] upload jar
andrewfg Nov 12, 2020
ebe4201
[velux] tweak getDisplayPosition(); delete isManualOpen()
andrewfg Nov 13, 2020
e8eff12
[vlux] fine tine logic
andrewfg Nov 14, 2020
81e46f1
[velux] upload jar
andrewfg Nov 14, 2020
3924cd5
[velux] information about position channel
andrewfg Nov 15, 2020
1188a0b
[velux] only log is actual socket closing..
andrewfg Nov 15, 2020
e918a54
[velux] added rule example
andrewfg Nov 15, 2020
0469fbb
[velux] clean up properties vs. config params vs. channels
andrewfg Nov 17, 2020
344e294
[velux] code style
andrewfg Nov 17, 2020
9adfd28
[velux] actually save config changes
andrewfg Nov 17, 2020
474b711
[velux] upload jar file
andrewfg Nov 17, 2020
2dd65fc
[velux] longer timeout
andrewfg Nov 17, 2020
815d87e
[velux] deprecated classes marked as such
andrewfg Nov 17, 2020
accba2c
[velux] rewrite bridge discovery
andrewfg Nov 18, 2020
ff2bd01
[velux] actions tweaks
andrewfg Nov 23, 2020
b94b87d
[velux] moveRelative()
andrewfg Nov 23, 2020
70225f6
[velux] upload jar file
andrewfg Nov 23, 2020
68dad57
[velux] fianlize Somfy issue
andrewfg Nov 24, 2020
cdf2b06
[velux] implement code reviewer suggestions
andrewfg Nov 25, 2020
dd262ce
[velux] remove jar from pull request
andrewfg Nov 25, 2020
e58c50a
[velux] implement suggestions by code reviewer
andrewfg Nov 25, 2020
c368e4c
[velux] implement suggestions by code reviewer
andrewfg Nov 25, 2020
1fc1452
[velux] implement suggestions by code reviewer
andrewfg Nov 26, 2020
03aca9f
[velux] remove demonization
andrewfg Nov 26, 2020
6fce1d2
[velux] shutdown
andrewfg Nov 27, 2020
5bc5714
[velux] task safety check
andrewfg Nov 27, 2020
0f23aca
[velux] pass bridge instance down to io poller
andrewfg Nov 28, 2020
51d0f6d
[velux] refactored hub discovery
andrewfg Nov 28, 2020
b2fc1c9
[velux] fix threading issues
andrewfg Nov 29, 2020
11cc28d
[velux] apply reviewer request
andrewfg Nov 30, 2020
248ab01
[velux] jenkins null warning
andrewfg Dec 1, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
110 changes: 95 additions & 15 deletions bundles/org.openhab.binding.velux/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,9 @@ The binding supports the following types of Thing.
## Discovery

To simplify the initial provisioning, the binding provides one thing which can be found by autodiscovery.
Unfortunately there is no way to discover Velux bridges themselves within the local network.
But after configuring a Velux Bridge, it is possible to discover all scenes and actuators like windows and rollershutters in that hub.
The binding will automatically discover Velux Bridges within the local network, and place them in the Inbox.
Once a Velux Bridge has been discovered, you will need to enter the `password` Configuration Parameter (see below) before the binding can communicate with it.
And once the Velux Bridge is fully configured, the binding will automatically discover all its respective scenes and actuators (like windows and rollershutters), and place them in the Inbox.

## Thing Configuration

Expand All @@ -51,7 +52,7 @@ In addition there are some optional Configuration Parameters.
|-------------------------|------------------|:--------:|--------------------------------------------------------------|
| ipAddress | | Yes | Hostname or address for accessing the Velux Bridge. |
| password | velux123 | Yes | Password for authentication against the Velux Bridge.(\*\*) |
| timeoutMsecs | 500 | No | Communication timeout in milliseconds. |
| timeoutMsecs | 2000 | No | Communication timeout in milliseconds. |
| protocol | slip | No | Underlying communication protocol (http/https/slip). |
| tcpPort | 51200 | No | TCP port (80 or 51200) for accessing the Velux Bridge. |
| retries | 5 | No | Number of retries during I/O. |
Expand Down Expand Up @@ -89,7 +90,7 @@ In addition there are some optional Configuration Parameters.

Notes:

1. To enable a complete invertion of all parameter values (i.e. for Velux windows), use the property `inverted` or add a trailing star to the eight-byte serial number. For an example, see below at item `Velux DG Window Bathroom`.
1. To enable a complete inversion of all parameter values (i.e. for Velux windows), use the property `inverted` or add a trailing star to the eight-byte serial number. For an example, see below at item `Velux DG Window Bathroom`.

2. Somfy devices do not provide a valid serial number to the Velux KLF200 gateway. The bridge reports a registration of the serial number 00:00:00:00:00:00:00:00. Therefore the binding implements a fallback to allow an item specification with a actuator `name` instead of actuator serial number whenever such an invalid serial number occurs. For an example, see below at item `Velux OG Somfy Shutter`.

Expand All @@ -99,9 +100,10 @@ The Velux Bridge in API version one (firmware version 0.1.1.*) allows activating
So besides the bridge, only one real Thing type exists, namely "scene".
This type of Thing is configured by means of its scene name in the hub.

| Configuration Parameter | Default | Required | Description |
|-------------------------|------------------------|:--------:|-----------------------------------------------------------|
| sceneName | | Yes | Name of the scene in the hub. |
| Configuration Parameter | Default | Required | Description |
|-------------------------|------------------------|:--------:|-----------------------------------------------------------------------|
| sceneName | | Yes | Name of the scene in the hub. |
| velocity | | No | The speed at which the scene will be executed (deafult, silent, fast) |

### Thing Configuration for "vshutter"

Expand All @@ -128,7 +130,7 @@ The supported Channels and their associated channel types are shown below.
| downtime | Number | Time interval (sec) between last successful and most recent device interaction. |
| doDetection | Switch | Command to activate bridge detection mode. |

### Channels for "window", "rollershutter" Things
### Channels for "window" / "rollershutter" Things

The supported Channels and their associated channel types are shown below.

Expand All @@ -138,6 +140,15 @@ The supported Channels and their associated channel types are shown below.
| limitMinimum | Rollershutter | Minimum limit position of the window or device. |
| limitMaximum | Rollershutter | Maximum limit position of the window or device. |

The `position` Channel indicates the open/close state of the window (resp. roller shutter) in percent (0% .. 100%) as follows..

- As a general rule the display is the actual physical position.
- If it is moving towards a new target position, the display is the target position.
- After the movement has completed, the display is the final physical position.
- If a window is opened manually, the display is `UNDEF`.
- In case of errors (e.g. window jammed) the display is `UNDEF`.
- If a Somfy actuator is commanded to its 'favorite' position via a Somfy remote control, under some circumstances the display is `UNDEF`. See also Rules below.

### Channels for "actuator" Things

The supported Channels and their associated channel types are shown below.
Expand All @@ -149,6 +160,8 @@ The supported Channels and their associated channel types are shown below.
| limitMinimum | Rollershutter | Minimum limit position of the window or device. |
| limitMaximum | Rollershutter | Maximum limit position of the window or device. |

See the section above for "window" / "rollershutter" Things for further information concerning the `position` Channel.

### Channels for "scene" Things

The supported Channels and their associated channel types are shown below.
Expand All @@ -166,6 +179,8 @@ The supported Channel and its associated channel type is shown below.
|--------------|---------------|-----------------------------------------|
| position | Rollershutter | Position of the virtual roller shutter. |

See the section above for "window" / "rollershutter" Things for further information concerning the `position` Channel.

### Channels for "information" Thing

The supported Channel and its associated channel type is shown below.
Expand All @@ -187,13 +202,13 @@ The bridge Thing provides the following properties.

| Property | Description |
|-------------------|-----------------------------------------------------------------|
| address | IP address of the Bridge |
| check | Result of the check of current item configuration |
| connectionAttempt | Date-Time of last connection attampt |
| connectionSuccess | Date-Time of last successful connection attampt |
| defaultGW | IP address of the Default Gateway of the Bridge |
| DHCP | Flag whether automatic IP configuration is enabled |
| firmware | Software version of the Bridge |
| ipAddress | IP address of the Bridge |
| products | List of all recognized products |
| scenes | List of all defined scenes |
| subnetMask | IP subnetmask of the Bridge |
Expand Down Expand Up @@ -231,12 +246,14 @@ Frame label="Velux Windows" {

[=> download sample sitemaps file for textual configuration](./doc/conf/sitemaps/velux.sitemap)

### Rules
### Rule for closing windows after a period of time

**Rule for closing windows after a period of time**:
Especially in the colder months, it is advisable to close the window after adequate ventilation. Therefore, automatic closing after one minute is good to save on heating costs.
Especially in the colder months, it is advisable to close the window after adequate ventilation.
Therefore, automatic closing after one minute is good to save on heating costs.
However, to allow the case of intentional prolonged opening, an automatic closure is made only with the window fully open.

Example:

```java
rule "V_WINDOW_changed"
when
Expand All @@ -245,14 +262,14 @@ then
logInfo("rules.V_WINDOW", "V_WINDOW_changes() called.")
// Get the sensor value
val Number windowState = V_WINDOW.state as DecimalType
logWarn("rules.V_WINDOW", "Window state is "+windowState+".")
logWarn("rules.V_WINDOW", "Window state is " + windowState + ".")
if (windowState < 80) {
if (windowState == 0) {
logWarn("rules.V_WINDOW", "V-WINDOW changed to fully open.")
var int interval = 1
createTimer(now.plusMinutes(interval)) [ |
createTimer(now.plusMinutes(interval)) [ |
logWarn("rules.V_WINDOW:event", "event-V_WINDOW(): setting V-WINDOW to 100.")
sendCommand(V_WINDOW,100)
sendCommand(V_WINDOW, 100)
V_WINDOW.postUpdate(100)
logWarn("rules.V_WINDOW:event", "event-V_WINDOW done.")
]
Expand All @@ -267,6 +284,69 @@ end

[=> download sample rules file for textual configuration](./doc/conf/rules/velux.rules)

### Rule for rebooting the Bridge

This binding includes a rule action to reboot the Velux Bridge by remote command:

- `boolean isRebooting = rebootBridge()`

_Warning: use this command carefully..._

Example:

```java
rule "Reboot KLF 200"
when
...
then
val veluxActions = getActions("velux", "velux:klf200:myhubname")
if (veluxActions !== null) {
val isRebooting = veluxActions.rebootBridge()
logWarn("Rules", "Velux KLF 200 rebooting: " + isRebooting)
} else {
logWarn("Rules", "Velux KLF 200 actions not found, check thing ID")
}
end
```

### Rule for checking if a Window has been manually opened

In the case that a window has been manually opened, and you then try to move it via the binding, its `position` will become `UNDEF`.
You can exploit this behaviour in a rule to check regularly if a window has been manually opened.

```java
rule "Every 10 minutes, check if window is in manual mode"
when
Time cron "0 0/10 * * * ?" // every 10 minutes
then
if (Velux_Window.state != UNDEF) {
// command the window to its actual position; this will either
// - succeed: the actual position will not change, or
// - fail: the position becomes UNDEF (logged next time this rule executes)
Velux_Window.sendCommand(Velux_Window.state)
} else {
logWarn("Rules", "Velux in Manual mode, trying to close again")
// try to close it
Velux_Window.sendCommand(0)
}
end
```

### Rule for Somfy actuators

If a Somfy actuator is commanded to its 'favorite' position via a Somfy remote control, under some circumstances the display is `UNDEF`.
You can resolve this behaviour in a rule that detects the `UNDEF` position and (re-)commands it to its favorite position.

```java
rule "Somfy Actuator: resolve undefined position"
when
Item Somfy_Actuator changed to UNDEF
then
val favoritePosition = 91
Somfy_Actuator.sendCommand(favoritePosition)
end
```

## Debugging

For those who are interested in more detailed insight of the processing of this binding, a deeper look can be achieved by increased loglevel.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,15 +79,15 @@ public VeluxBinding(@Nullable VeluxBridgeConfiguration uncheckedConfiguration) {
this.password = uncheckedConfiguration.password;
}
logger.trace("VeluxBinding(): checking {}.", VeluxBridgeConfiguration.BRIDGE_TIMEOUT_MSECS);
if ((uncheckedConfiguration.timeoutMsecs > 0) && (uncheckedConfiguration.timeoutMsecs <= 10000)) {
if ((uncheckedConfiguration.timeoutMsecs >= 500) && (uncheckedConfiguration.timeoutMsecs <= 5000)) {
this.timeoutMsecs = uncheckedConfiguration.timeoutMsecs;
}
logger.trace("VeluxBinding(): checking {}.", VeluxBridgeConfiguration.BRIDGE_RETRIES);
if ((uncheckedConfiguration.retries >= 0) && (uncheckedConfiguration.retries <= 10)) {
this.retries = uncheckedConfiguration.retries;
}
logger.trace("VeluxBinding(): checking {}.", VeluxBridgeConfiguration.BRIDGE_REFRESH_MSECS);
if ((uncheckedConfiguration.refreshMSecs > 0) && (uncheckedConfiguration.refreshMSecs <= 10000)) {
if ((uncheckedConfiguration.refreshMSecs >= 1000) && (uncheckedConfiguration.refreshMSecs <= 60000)) {
this.refreshMSecs = uncheckedConfiguration.refreshMSecs;
}
this.isBulkRetrievalEnabled = uncheckedConfiguration.isBulkRetrievalEnabled;
Expand All @@ -106,15 +106,20 @@ public VeluxBinding(@Nullable VeluxBridgeConfiguration uncheckedConfiguration) {
*/
public VeluxBridgeConfiguration checked() {
logger.trace("checked() called.");
// @formatter:off
logger.debug("{}Config[{}={},{}={},{}={},{}={},{}={},{}={},{}={},{}={},{}={},{}={}]",
VeluxBindingConstants.BINDING_ID, VeluxBridgeConfiguration.BRIDGE_PROTOCOL, protocol,
VeluxBridgeConfiguration.BRIDGE_IPADDRESS, this.ipAddress, VeluxBridgeConfiguration.BRIDGE_TCPPORT,
tcpPort, VeluxBridgeConfiguration.BRIDGE_PASSWORD, password.replaceAll(".", "*"),
VeluxBridgeConfiguration.BRIDGE_TIMEOUT_MSECS, timeoutMsecs, VeluxBridgeConfiguration.BRIDGE_RETRIES,
retries, VeluxBridgeConfiguration.BRIDGE_REFRESH_MSECS, refreshMSecs,
VeluxBindingConstants.BINDING_ID,
VeluxBridgeConfiguration.BRIDGE_PROTOCOL, protocol,
VeluxBridgeConfiguration.BRIDGE_IPADDRESS, this.ipAddress,
VeluxBridgeConfiguration.BRIDGE_TCPPORT, tcpPort,
VeluxBridgeConfiguration.BRIDGE_PASSWORD, password.replaceAll(".", "*"),
VeluxBridgeConfiguration.BRIDGE_TIMEOUT_MSECS, timeoutMsecs,
VeluxBridgeConfiguration.BRIDGE_RETRIES, retries,
VeluxBridgeConfiguration.BRIDGE_REFRESH_MSECS, refreshMSecs,
VeluxBridgeConfiguration.BRIDGE_IS_BULK_RETRIEVAL_ENABLED, isBulkRetrievalEnabled,
VeluxBridgeConfiguration.BRIDGE_IS_SEQUENTIAL_ENFORCED, isSequentialEnforced,
VeluxBridgeConfiguration.BRIDGE_PROTOCOL_TRACE_ENABLED, isProtocolTraceEnabled);
// @formatter:off
logger.trace("checked() done.");
return this;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,10 +91,15 @@ public class VeluxBindingConstants {
// Definitions of different set of Things
public static final Set<ThingTypeUID> SUPPORTED_THINGS_BINDING = new HashSet<>(Arrays.asList(THING_TYPE_BINDING));
public static final Set<ThingTypeUID> SUPPORTED_THINGS_BRIDGE = new HashSet<>(Arrays.asList(THING_TYPE_BRIDGE));

public static final Set<ThingTypeUID> SUPPORTED_THINGS_ITEMS = new HashSet<>(
Arrays.asList(THING_TYPE_VELUX_SCENE, THING_TYPE_VELUX_ACTUATOR, THING_TYPE_VELUX_ROLLERSHUTTER,
THING_TYPE_VELUX_WINDOW, THING_TYPE_VELUX_VSHUTTER));

public static final Set<ThingTypeUID> DISCOVERABLE_THINGS = Set.of(THING_TYPE_VELUX_SCENE,
THING_TYPE_VELUX_ACTUATOR, THING_TYPE_VELUX_ROLLERSHUTTER, THING_TYPE_VELUX_WINDOW,
THING_TYPE_VELUX_VSHUTTER, THING_TYPE_BINDING, THING_TYPE_BRIDGE);

// *** List of all Channel ids ***

// List of all binding channel ids
Expand All @@ -113,7 +118,7 @@ public class VeluxBindingConstants {
public static final String PROPERTY_BRIDGE_TIMESTAMP_SUCCESS = "connectionSuccess";
public static final String PROPERTY_BRIDGE_TIMESTAMP_ATTEMPT = "connectionAttempt";
public static final String PROPERTY_BRIDGE_FIRMWARE = "firmware";
public static final String PROPERTY_BRIDGE_IPADDRESS = "ipAddress";
public static final String PROPERTY_BRIDGE_ADDRESS = "address";
public static final String PROPERTY_BRIDGE_SUBNETMASK = "subnetMask";
public static final String PROPERTY_BRIDGE_DEFAULTGW = "defaultGW";
public static final String PROPERTY_BRIDGE_DHCP = "DHCP";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ public enum VeluxItemType {
BRIDGE_DO_DETECTION(VeluxBindingConstants.THING_TYPE_BRIDGE, VeluxBindingConstants.CHANNEL_BRIDGE_DO_DETECTION, TypeFlavor.INITIATOR),

BRIDGE_FIRMWARE(VeluxBindingConstants.THING_TYPE_BRIDGE, VeluxBindingConstants.PROPERTY_BRIDGE_FIRMWARE, TypeFlavor.PROPERTY),
BRIDGE_IPADDRESS(VeluxBindingConstants.THING_TYPE_BRIDGE, VeluxBindingConstants.PROPERTY_BRIDGE_IPADDRESS, TypeFlavor.PROPERTY),
BRIDGE_ADDRESS(VeluxBindingConstants.THING_TYPE_BRIDGE, VeluxBindingConstants.PROPERTY_BRIDGE_ADDRESS, TypeFlavor.PROPERTY),
BRIDGE_SUBNETMASK(VeluxBindingConstants.THING_TYPE_BRIDGE, VeluxBindingConstants.PROPERTY_BRIDGE_SUBNETMASK, TypeFlavor.PROPERTY),
BRIDGE_DEFAULTGW(VeluxBindingConstants.THING_TYPE_BRIDGE, VeluxBindingConstants.PROPERTY_BRIDGE_DEFAULTGW, TypeFlavor.PROPERTY),
BRIDGE_DHCP(VeluxBindingConstants.THING_TYPE_BRIDGE, VeluxBindingConstants.PROPERTY_BRIDGE_DHCP, TypeFlavor.PROPERTY),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/**
* Copyright (c) 2010-2020 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;
}
Loading