diff --git a/README.md b/README.md index 55d68b8..edf2e13 100644 --- a/README.md +++ b/README.md @@ -36,8 +36,12 @@ In the Advanced Options menu (orange button), you can also add a body and header At this point you can do whatever you want with the buttons - To make your lights turn on and off when you play and pause a movie, just add the HarmonySpan buttons that are linked to your 'lights on' and 'lights off' webhook URLs with the Pause and Play buttons on the activity you use to watch a movie. For more information, check Logitech's documentation on creating button sequences. https://support.myharmony.com/en-es/creating-button-sequences +To trigger a shell script, edit a button, choose SCRIPT from the dropdown, enter the location of your script and check Enabled. You will need to ensure file permissions are appropriately set to allow Harmony Span to execute your scripts. + Click the Submit Settings button once you fill in all of the fields for the button you're working with. Switching buttons without submitting your changes will delete the new values you put in for the first button. + + ## How It Works Logitech allows you to control 'IP devices' over your local network (think Roku, NVIDIA SHIELD, Apple TV) rather than controlling them with IR. This works because each device broadcasts to a local Simple Service Discovery Protocol (SSDP) IP (``239.255.255.250``, chances are you can see traffic on it in your network with Wireshark) to let other devices in the network know that they can be controlled. diff --git a/harmony-span.js b/harmony-span.js index 1df0128..fad91b6 100644 --- a/harmony-span.js +++ b/harmony-span.js @@ -8,6 +8,7 @@ const request = require("request"); const mqtt = require("mqtt"); const yaml = require("node-yaml"); const ip = require("ip"); +const { exec } = require("child_process"); const CONFIG_FILE = "config.yaml"; const DEFAULT_HOST = "127.0.0.1"; @@ -216,6 +217,28 @@ function triggerAction(buttonFunction) { let button = conf.buttons[buttonIndex]; if (button.enabled) { switch (button.action) { + case "SCRIPT": + // check if script file exists + if (fs.existsSync(button.scriptLocation)) { + // file exists, so try to run it + colorout.log("debug", "Running script file: " + button.scriptLocation); + exec(button.scriptLocation, (error, stdout, stderr) => { + if (error){ + colorout.log("error", "Error running script file " + button.scriptLocation + ". Error Message:\n" + error.message); + return; + } + if (stderr){ + colorout.log("error", "Error running script file " + button.scriptLocation + ". STDERR:\n" + stderr); + return; + } + colorout.log("debug", "Output of script file " + button.scriptLocation + ":\n" + stdout); + }); + } + else{ + // file doesn't exist + colorout.log("error", "Script file " + button.scriptLocation + " doesn't exist.") + } + break; case "GET": request(button.url, function(error, response, body) { if (error != null) { diff --git a/public/config/scripts.js b/public/config/scripts.js index 0fb305d..5ad484a 100644 --- a/public/config/scripts.js +++ b/public/config/scripts.js @@ -8,7 +8,8 @@ var Button = Backbone.Model.extend({ mqttTopic: "", mqttMessage: "", action: "", - enabled: false + enabled: false, + scriptLocation: "" } }); @@ -41,6 +42,10 @@ var ButtonRowView = Backbone.View.extend({ "
[<%= action %>] <%= mqttTopic %>[<%= action %>] <%= scriptLocation %>