Skip to content

Commit

Permalink
Exec 2.0 Enhancements (#1502)
Browse files Browse the repository at this point in the history
* Exec 2.0 : Fix stream reading
* Exec 2.0 : Add autorun config parameter, add input channel, improve documentation

Signed-off-by: Karel Goderis <[email protected]>
  • Loading branch information
kgoderis authored and kaikreuzer committed Nov 28, 2016
1 parent ca53471 commit 039997e
Show file tree
Hide file tree
Showing 4 changed files with 77 additions and 18 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

<channels>
<channel id="output" typeId="output"/>
<channel id="input" typeId="input"/>
<channel id="exit" typeId="exit"/>
<channel id="run" typeId="running"/>
<channel id="lastexecution" typeId="lastexecution" />
Expand All @@ -23,15 +24,23 @@
<parameter name="transform" type="text" required="false">
<label>Transform</label>
<description>The transformation to apply on the execution result, e.g. REGEX((.*))</description>
<default>REGEX((.*))</default>
</parameter>
<parameter name="interval" type="integer" required="false">
<label>Interval</label>
<description>Interval, in seconds, the command will be repeatedly executed</description>
<default>60</default>
</parameter>
<parameter name="timeout" type="integer" required="false">
<label>Timeout</label>
<description>Time out, in seconds, the execution of the command will time out</description>
</parameter>
<default>15</default>
</parameter>
<parameter name="autorun" type="boolean" required="false">
<label>Autorun</label>
<description>When true, the command will execute each time the state of the input channel changes</description>
<default>false</default>
</parameter>
</config-description>

</thing-type>
Expand All @@ -42,7 +51,13 @@
<description>Output of the last execution of the command</description>
<state readOnly="true"></state>
</channel-type>
<channel-type id="exit">
<channel-type id="input">
<item-type>String</item-type>
<label>Input</label>
<description>Input that will be passed as second parameter to the command</description>
<state readOnly="false"></state>
</channel-type>
<channel-type id="exit">
<item-type>Number</item-type>
<label>Exit Value</label>
<description>The exit value of the last execution of the command</description>
Expand Down
22 changes: 15 additions & 7 deletions addons/binding/org.openhab.binding.exec/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,26 +4,34 @@ This binding integrates the possibility to execute arbitrary shell commands.

## Supported Things

Currently, the binding supports a single Thing, being the 'command' thing.
Currently, the binding supports a single type of Thing, being the ```command``` Thing.

## Binding Configuration

The binding does not require any specific configuration

Note that the commands are executed in the context and with the privileges of the process running the java virtual machine. It is not advised to run the virtual machine as superuser/root

## Thing Configuration

The command Thing requires the command to execute on the shell, and optionally one can specify a transformation to apply on the execution result, an interval, in seconds, the command will be repeatedly executed and lastly a time-out, in seconds, the execution of the command will time out
The command Thing requires the command to execute on the shell, and optionally one can specify a transformation to apply on the execution result, an interval, in seconds, the command will be repeatedly executed, a time-out, in seconds, the execution of the command will time out, and lastly, a boolean parameter to make the command execute immediately every time the state of the input channel has changed. For each command a separate Thing has to be defined

```
Thing exec:command:apc [ command="/usr/local/bin/apcaccess status", interval=15, timeout=5]
Thing exec:command:apc [command="/usr/local/bin/apcaccess status", interval=15, timeout=5, autorun=false]
```

```command``` itseld can be enhanced using the well known syntax of the java.util.Formatter class. The following parameters are automatically added:

the current date (as java.util.Date, example: %1$tY-%1$tm-%1$td)
the current State of the input channel (see below, example: %2$s)

## Channels

All devices support the following channels:
All Things support the following channels:

| Channel Type ID | Item Type | Description |
|-----------------|------------------------|--------------|----------------- |------------- |
| input | String | Input parameter to provide to the command |
| output | String | Output of the last execution of the command |
| exit | Number | The exit value of the last execution of the command |
| run | Switch | Send ON to execute the command and the current state tells whether it is running or not |
Expand All @@ -34,15 +42,15 @@ All devices support the following channels:
demo.Things:

```
Thing exec:command:apc [ command="/usr/local/bin/apcaccess status", interval=15, timeout=5]
Thing exec:command:apc [command="/usr/local/bin/apcaccess status", interval=15, timeout=5]
Thing exec:command:myscript [command="php ./configurations/scripts/script.php %2$s", transform="REGEX((.*?))"]
```

demo.items:

```
String APCRaw "[%s]" (All) {channel="exec:command:apc:output"}
Switch APCRunning { channel="exec:command:apc:running"}
Switch APCRunning { channel="exec:command:apc:run"}
Number APCExitValue {channel="exec:command:apc:exit"}
Switch APCExecute {channel="exec:command:apc:execute"}
DateTime APCLastExecution {channel="exec:command:apc:lastexecution"}
```
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ public class ExecBindingConstants {

// List of all Channel ids
public final static String OUTPUT = "output";
public final static String INPUT = "input";
public final static String EXIT = "exit";
public final static String RUN = "run";
public final static String LAST_EXECUTION = "lastexecution";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,14 @@
*/
package org.openhab.binding.exec.handler;

import static org.openhab.binding.exec.ExecBindingConstants.*;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.math.BigDecimal;
import java.util.Calendar;
import java.util.IllegalFormatException;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
Expand All @@ -32,7 +35,6 @@
import org.eclipse.smarthome.core.transform.TransformationService;
import org.eclipse.smarthome.core.types.Command;
import org.eclipse.smarthome.core.types.RefreshType;
import org.openhab.binding.exec.ExecBindingConstants;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand All @@ -51,11 +53,13 @@ public class ExecHandler extends BaseThingHandler {
public static final String TIME_OUT = "timeout";
public static final String COMMAND = "command";
public static final String TRANSFORM = "transform";
public static final String AUTORUN = "autorun";

// RegEx to extract a parse a function String <code>'(.*?)\((.*)\)'</code>
private static final Pattern EXTRACT_FUNCTION_PATTERN = Pattern.compile("(.*?)\\((.*)\\)");

private ScheduledFuture<?> executionJob;
private String lastInput;

private static Runtime rt = Runtime.getRuntime();

Expand All @@ -69,12 +73,25 @@ public void handleCommand(ChannelUID channelUID, Command command) {
if (command instanceof RefreshType) {
// Placeholder for later refinement
} else {
if (channelUID.getId().equals(ExecBindingConstants.RUN)) {
if (channelUID.getId().equals(RUN)) {
if (command instanceof OnOffType) {
if (command == OnOffType.ON) {
scheduler.schedule(periodicExecutionRunnable, 0, TimeUnit.SECONDS);
}
}
} else if (channelUID.getId().equals(INPUT)) {
if (command instanceof StringType) {
String previousInput = lastInput;
lastInput = command.toString();
if (lastInput != null && !lastInput.equals(previousInput)) {
if (getConfig().get(AUTORUN) != null && ((Boolean) getConfig().get(AUTORUN)).booleanValue()) {
lastInput = command.toString();
logger.trace("Executing command '{}' after a change of the input channel to '{}'",
getConfig().get(COMMAND), command.toString());
scheduler.schedule(periodicExecutionRunnable, 0, TimeUnit.SECONDS);
}
}
}
}
}
}
Expand Down Expand Up @@ -115,7 +132,7 @@ public void run() {

if (commandLine != null && !commandLine.isEmpty()) {

updateState(ExecBindingConstants.RUN, OnOffType.ON);
updateState(RUN, OnOffType.ON);

// For some obscure reason, when using Apache Common Exec, or using a straight implementation of
// Runtime.Exec(), on Mac OS X (Yosemite and El Capitan), there seems to be a lock race condition
Expand All @@ -126,14 +143,30 @@ public void run() {
// problem for external commands that generate a lot of output, but this will be dependent on the limits
// of the underlying operating system.

try {
if (lastInput != null) {
commandLine = String.format(commandLine, Calendar.getInstance().getTime(), lastInput);
} else {
commandLine = String.format(commandLine, Calendar.getInstance().getTime());
}
} catch (IllegalFormatException e) {
logger.error(
"An exception occurred while formatting the command line with the current time and input values : '{}'",
e.getMessage());
updateState(RUN, OnOffType.OFF);
return;
}

logger.trace("The command to be executed will be '{}'", commandLine);

Process proc = null;
try {
proc = rt.exec(commandLine.toString());
} catch (Exception e) {
logger.error("An exception occured while executing '{}' : '{}'",
new Object[] { commandLine.toString(), e.getMessage() });
updateState(ExecBindingConstants.RUN, OnOffType.OFF);
updateState(ExecBindingConstants.OUTPUT, new StringType(e.getMessage()));
updateState(RUN, OnOffType.OFF);
updateState(OUTPUT, new StringType(e.getMessage()));
return;
}

Expand Down Expand Up @@ -180,8 +213,10 @@ public void run() {
proc.destroyForcibly();
}

updateState(ExecBindingConstants.RUN, OnOffType.OFF);
updateState(ExecBindingConstants.EXIT, new DecimalType(proc.exitValue()));
updateState(RUN, OnOffType.OFF);
updateState(EXIT, new DecimalType(proc.exitValue()));

outputBuilder.append(errorBuilder.toString());

outputBuilder.append(errorBuilder.toString());

Expand All @@ -192,10 +227,10 @@ public void run() {
transformedResponse = transformResponse(transformedResponse, transformation);
}

updateState(ExecBindingConstants.OUTPUT, new StringType(transformedResponse));
updateState(OUTPUT, new StringType(transformedResponse));

DateTimeType stampType = new DateTimeType(Calendar.getInstance());
updateState(ExecBindingConstants.LAST_EXECUTION, stampType);
updateState(LAST_EXECUTION, stampType);

}
}
Expand Down

0 comments on commit 039997e

Please sign in to comment.