Skip to content
This repository was archived by the owner on Jul 13, 2022. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down
23 changes: 23 additions & 0 deletions harmony-span.js
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -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) {
Expand Down
24 changes: 22 additions & 2 deletions public/config/scripts.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ var Button = Backbone.Model.extend({
mqttTopic: "",
mqttMessage: "",
action: "",
enabled: false
enabled: false,
scriptLocation: ""
}
});

Expand Down Expand Up @@ -41,6 +42,10 @@ var ButtonRowView = Backbone.View.extend({
"<td><code>[<strong><%= action %></strong>] <%= mqttTopic %></code></td>\n" +
"<td><%= mqttMessage %></td>\n" +
"<% } %>" +
"<% if (action == \"SCRIPT\") { %>" +
"<td><code>[<strong><%= action %></strong>] <%= scriptLocation %></code></td>\n" +
"<td><%= scriptLocation %></td>\n" +
"<% } %>" +
"<td><%= enabled ? \"yes\" : \"no\" %></td>\n" +
"<td><a href=\"#button/<%= id %>\" class=\"edit btn btn-sm btn-primary\">edit</a></td>"
),
Expand Down Expand Up @@ -112,6 +117,7 @@ var ButtonConfigView = Backbone.View.extend({
"<div class=\"form-group\">\n" +
"<label for=\"action\">Action</label>\n" +
"<select class=\"select-action form-control\" id=\"action\">\n" +
"<option value=\"SCRIPT\"<% if(mode == \"SCRIPT\") { %> selected<% } %>>Run script at given location</option>\n" +
"<option value=\"GET\"<% if(mode == \"GET\") { %> selected<% } %>>Call Webhook with HTTP GET</option>\n" +
"<option value=\"POST\"<% if(mode == \"POST\") { %> selected<% } %>>Call Webhook with HTTP POST</option>\n" +
"<option value=\"MQTT\"<% if(mode == \"MQTT\") { %> selected<% } %>>Send MQTT Message</option>\n" +
Expand All @@ -135,12 +141,17 @@ var ButtonConfigView = Backbone.View.extend({
"<% } else if (mode == \"MQTT\") { %>" +
"<div class=\"form-group\">\n" +
"<label for=\"mqttTopic\">MQTT Topic</label>\n" +
"<input type=\"text\" class=\"form-control\" id=\"mqttTopic\" value=\"<%= mqttTopic %>\" placeholder=\"E.g. harmony\" requird>\n" +
"<input type=\"text\" class=\"form-control\" id=\"mqttTopic\" value=\"<%= mqttTopic %>\" placeholder=\"E.g. harmony\" required>\n" +
"</div>\n" +
"<div class=\"form-group\">\n" +
"<label for=\"mqttTopic\">MQTT Message</label>\n" +
"<textarea class=\"form-control\" id=\"mqttMessage\" required><%= mqttMessage %></textarea>\n" +
"</div>\n" +
"<% } else if (mode == \"SCRIPT\") { %>" +
"<div class=\"form-group\">\n" +
"<label for=\"scriptLocation\">Script Location</label>\n" +
"<input type=\"text\" class=\"form-control\" id=\"scriptLocation\" value=\"<%= scriptLocation %>\" placeholder=\"E.g. ./scripts/DisneyPlus.sh\" required>\n" +
"</div>\n" +
"<% } %>" +
"<div class=\"form-group form-check\">\n" +
" <input type=\"checkbox\" class=\"form-check-input\" id=\"enabled\"<% if(enabled) { %> checked<% } %>>\n" +
Expand Down Expand Up @@ -201,6 +212,15 @@ var ButtonConfigView = Backbone.View.extend({
mqttMessage : this.$el.find("#mqttMessage").val().trim(),
enabled: enabled
});
} else if (action == "SCRIPT") {
this.model.clear();
this.model.set({
id: id,
name: name,
action: action,
scriptLocation: this.$el.find("#scriptLocation").val().trim(),
enabled: enabled
});
}
this.model.save();
},
Expand Down