diff --git a/addons/binding/org.openhab.binding.exec/ESH-INF/thing/thing-types.xml b/addons/binding/org.openhab.binding.exec/ESH-INF/thing/thing-types.xml index 5497abc7c8105..8a8ae78d2aa7c 100644 --- a/addons/binding/org.openhab.binding.exec/ESH-INF/thing/thing-types.xml +++ b/addons/binding/org.openhab.binding.exec/ESH-INF/thing/thing-types.xml @@ -10,6 +10,7 @@ + @@ -23,15 +24,23 @@ The transformation to apply on the execution result, e.g. REGEX((.*)) + REGEX((.*)) Interval, in seconds, the command will be repeatedly executed + 60 Time out, in seconds, the execution of the command will time out - + 15 + + + + When true, the command will execute each time the state of the input channel changes + false + @@ -42,7 +51,13 @@ Output of the last execution of the command - + + String + + Input that will be passed as second parameter to the command + + + Number The exit value of the last execution of the command diff --git a/addons/binding/org.openhab.binding.exec/README.md b/addons/binding/org.openhab.binding.exec/README.md index 3cf99ae6c8b0e..54846c9767d72 100644 --- a/addons/binding/org.openhab.binding.exec/README.md +++ b/addons/binding/org.openhab.binding.exec/README.md @@ -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 | @@ -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"} ``` \ No newline at end of file diff --git a/addons/binding/org.openhab.binding.exec/src/main/java/org/openhab/binding/exec/ExecBindingConstants.java b/addons/binding/org.openhab.binding.exec/src/main/java/org/openhab/binding/exec/ExecBindingConstants.java index 229fdb0ff1941..f234c56ce6176 100644 --- a/addons/binding/org.openhab.binding.exec/src/main/java/org/openhab/binding/exec/ExecBindingConstants.java +++ b/addons/binding/org.openhab.binding.exec/src/main/java/org/openhab/binding/exec/ExecBindingConstants.java @@ -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"; diff --git a/addons/binding/org.openhab.binding.exec/src/main/java/org/openhab/binding/exec/handler/ExecHandler.java b/addons/binding/org.openhab.binding.exec/src/main/java/org/openhab/binding/exec/handler/ExecHandler.java index 10a790692a00b..1933dd4c474fe 100644 --- a/addons/binding/org.openhab.binding.exec/src/main/java/org/openhab/binding/exec/handler/ExecHandler.java +++ b/addons/binding/org.openhab.binding.exec/src/main/java/org/openhab/binding/exec/handler/ExecHandler.java @@ -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; @@ -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; @@ -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 '(.*?)\((.*)\)' private static final Pattern EXTRACT_FUNCTION_PATTERN = Pattern.compile("(.*?)\\((.*)\\)"); private ScheduledFuture executionJob; + private String lastInput; private static Runtime rt = Runtime.getRuntime(); @@ -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); + } + } + } } } } @@ -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 @@ -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; } @@ -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()); @@ -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); } }