Skip to content

Commit

Permalink
Merge pull request #4 from Tom-Hirschberger/development
Browse files Browse the repository at this point in the history
Development
  • Loading branch information
Tom-Hirschberger authored Jan 24, 2023
2 parents ea66514 + cc98206 commit 48c2a16
Show file tree
Hide file tree
Showing 4 changed files with 123 additions and 52 deletions.
3 changes: 2 additions & 1 deletion MMM-CommandToNotification.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ Module.register('MMM-CommandToNotification', {

defaults: {
updateInterval: 30,
commands: []
commands: [],
sync: true
},

start: function () {
Expand Down
9 changes: 6 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,18 @@ Add the following code to your ~/MagicMirror/config/config.js:
| ------- | --- | --- | --- |
| updateInterval | How often should the scripts be iterated (in seconds) | Integer | 30 |
| commands | A array containing the command definition objects | Array | [] |
| sync | Should the commands called one by one (true) or should all be started as fast as possible (false). Can be overriden for each command in the command configuration | Boolean | true |

### Commands

| Option | Description | Mandatory | Type | Default |
| ------- | --- | --- | --- | --- |
| script | Either a absolute path or the path of a script within "scripts" directory | true | String | null |
| script | Either a absolute path or the realtive path of a script starting at the "scripts" directory | true | String | null |
| args | Arguments which should be passed to the script | false | String | "" |
| timeout | Should the script be killed if it does not return within a specific amount of milliseconds? | false | Integer | infinity |
| notifications | A array containing names of the notifications to send if script returns output. If not present the script gets called but no notification will be send. If you want to override the payload instead of using the output please look at the notification section. | false | Array | [] |
| sync | Should the command by run synchronous (next command will be called after this one, true) or asynchronous (directly proceed with the next one and process output when the command finishes, false) | Boolean | true |
| delayNext | Wait some amount of time (milliseconds) before the next command will be processed after this one has called. Make sure to set to updateInterval of the module to a value that is enough time to call and process all commands with all delays summed up! | Integer | 0 |
| conditions | A map containing conditions that need to match to send the notifications | false | Map | null |

### Notifications
Expand Down Expand Up @@ -91,7 +94,7 @@ Add the following example to produce the following result:
updateInterval: 10,
commands: [
{
script: "randomInteger.js",
script: "./randomInteger.js",
args: "-10 10",
timeout: 5,
notifications: [
Expand All @@ -100,7 +103,7 @@ Add the following example to produce the following result:
],
},
{
script: "randomNumberJson.js",
script: "./randomNumberJson.js",
args: "-50 20",
skips: 3,
timeout: 10,
Expand Down
157 changes: 113 additions & 44 deletions node_helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
*/
const NodeHelper = require('node_helper')

const execSync = require('child_process').execSync
const spawnSync = require('child_process').spawnSync
const spawn = require('child_process').spawn
const fs = require('fs')
const path = require('path')
const scriptsDir = path.join(__dirname, '/scripts')
Expand All @@ -20,6 +21,40 @@ module.exports = NodeHelper.create({
self.cmdSkips = {}

console.log(self.name+": "+"Module helper started")
self.asyncOutput = {}
},

sleep: function(milliseconds) {
return new Promise(resolve => setTimeout(resolve, milliseconds));
},

//https://stackoverflow.com/questions/14332721/node-js-spawn-child-process-and-get-terminal-output-live
runScript: function(command, args, options, cmdIdx, curCmdConfig, curNotifications) {
const self = this
let child = spawn(command, args, options);

let scriptOutput = null;

child.stdout.setEncoding('utf8');
child.stdout.on('data', function(data) {
if (scriptOutput == null){
scriptOutput = ""
}
data=data.toString();
scriptOutput+=data;
});

// child.stderr.setEncoding('utf8');
// child.stderr.on('data', function(data) {
// console.log('stderr: ' + data);

// data=data.toString();
// scriptOutput+=data;
// });

child.on('close', function(code) {
self.postProcessCommand(cmdIdx, curCmdConfig, curNotifications, scriptOutput.trim(), code)
});
},

validateConditions: function(curCmdConfig, output, returnCode){
Expand Down Expand Up @@ -68,67 +103,74 @@ module.exports = NodeHelper.create({
}
},

callCommands: function(){
callCommands: async function(){
const self = this
console.log(self.name+": "+"Calling commands")
for (let cmdIdx = 0; cmdIdx < self.config.commands.length; cmdIdx++) {
let curCmdConfig = self.config.commands[cmdIdx]
if(typeof curCmdConfig["script"] !== "undefined"){
let curScript = curCmdConfig["script"]
if((typeof curCmdConfig["skips"] === "undefined") ||
(curCmdConfig["skips"] < self.cmdSkips[cmdIdx])
){
let fullScriptPath = curScript

self.cmdSkips[cmdIdx] = 1
if(!curScript.startsWith("/")){
fullScriptPath = scriptsDir+"/"+curScript
}

let curCommand = curCmdConfig["script"]
let curArgs = curCmdConfig["args"]
if(typeof curCmdConfig["args"] !== "undefined"){
if(Array.isArray(curCmdConfig["args"])){
curArgs = curCmdConfig["args"]
} else {
curArgs = curCmdConfig["args"].split(" ")
}
}
let curNotifications = curCmdConfig["notifications"]

let output = null
let returnCode = 0
let curCommand = fullScriptPath
if(typeof curArgs !== "undefined"){
curCommand += " " + curArgs
let curEncoding = "utf8"
if(typeof curCmdConfig["encoding"] !== "undefined"){
curEncoding = curCmdConfig["encoding"]
}

try {
if(typeof curCmdConfig["timeout"] !== "undefined"){
// console.log(self.name+": Calling "+fullScriptPath+" with timeout of "+curCmdConfig["timeout"])
output = execSync(curCommand, {encoding:"utf8", timeout:curCmdConfig["timeout"], cwd:scriptsDir}).toString()
} else {
// console.log(self.name+": Calling "+fullScriptPath+" without timeout")
output = execSync(curCommand, {encoding:"utf8", cwd:scriptsDir}).toString()
}
}
catch (error) {
returnCode = error.status;
output = error.stdout.toString();
let options = {
shell: true,
encoding: curEncoding,
cwd: scriptsDir,
}

if(output !== null){
if (typeof curNotifications !== "undefined"){
if (self.validateConditions(curCmdConfig, output, returnCode)){
for (let curNotiIdx = 0; curNotiIdx < curNotifications.length; curNotiIdx++){
if (Array.isArray(curNotifications[curNotiIdx])){
if (curNotifications[curNotiIdx].length > 1){
self.sendSocketNotification("RESULT_"+curNotifications[curNotiIdx][0], curNotifications[curNotiIdx][1])
} else {
self.sendSocketNotification("RESULT_"+curNotifications[curNotiIdx][0], output)
}
} else {
self.sendSocketNotification("RESULT_"+curNotifications[curNotiIdx], output)
}
}

if(typeof curCmdConfig["timeout"] !== "undefined"){
options["timeout"] = curCmdConfig["timeout"]
}

let output = null
let returnCode = null
let sync = self.config.sync
if(typeof curCmdConfig["sync"] !== "undefined"){
sync = curCmdConfig["sync"]
}
try {
if (sync){
console.log("Running "+ curCommand + " synchronous")
let spawnOutput = spawnSync(curCommand, curArgs, options)
returnCode = spawnOutput.status
output = spawnOutput.stdout.trim()
if(returnCode == null){
returnCode = 1
output += "Timeout"
}

self.postProcessCommand(cmdIdx, curCmdConfig, curNotifications, output, returnCode)
} else {
console.log(self.name+": "+"The script "+curScript+" has no notifications configured. It had been called but no notification will be send!")
console.log("Running "+curCommand + " asynchronous")
self.runScript(curCommand, curArgs, options, cmdIdx, curCmdConfig, curNotifications)
}

} catch (error) {
console.log(error)
}
if (curCmdConfig["delayNext"] || false){
console.log("Delaying next: "+curCmdConfig["delayNext"])
await self.sleep(curCmdConfig["delayNext"])
}
} else {
// console.log(self.name+": "+"Skipping script: "+curScript)
self.cmdSkips[cmdIdx] += 1
}
} else {
Expand All @@ -137,7 +179,34 @@ module.exports = NodeHelper.create({
}
},

setDefaultsAndStart: async function(){
postProcessCommand: function(cmdIdx, curCmdConfig, curNotifications, output, returnCode){
const self = this
// console.log("Postprocessing cmdIdx: "+cmdIdx)
console.log("curCmdConfig: "+JSON.stringify(curCmdConfig))
// console.log(output)
// console.log(returnCode)
if(output !== null){
if (typeof curNotifications !== "undefined"){
if (self.validateConditions(curCmdConfig, output, returnCode)){
for (let curNotiIdx = 0; curNotiIdx < curNotifications.length; curNotiIdx++){
if (Array.isArray(curNotifications[curNotiIdx])){
if (curNotifications[curNotiIdx].length > 1){
self.sendSocketNotification("RESULT_"+curNotifications[curNotiIdx][0], curNotifications[curNotiIdx][1])
} else {
self.sendSocketNotification("RESULT_"+curNotifications[curNotiIdx][0], output)
}
} else {
self.sendSocketNotification("RESULT_"+curNotifications[curNotiIdx], output)
}
}
}
} else {
console.log(self.name+": "+"The command with idx: "+cmdIdx+" has no notifications configured. It had been called but no notification will be send!")
}
}
},

setDefaultsAndStart: function(){
const self = this
if(self.started){
console.log(self.name+": "+"Setting the defaults")
Expand All @@ -152,7 +221,7 @@ module.exports = NodeHelper.create({
console.log(self.name+": "+"Call the commands initially")
self.callCommands()
console.log(self.name+": "+"Reschedule the next calls")
await self.rescheduleCommandCall()
self.rescheduleCommandCall()
}
} else {
console.log(self.name+": "+"Defaults can not be set because module is not started at the moment")
Expand Down
6 changes: 2 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
{
"name": "MMM-CommandToNotification",
"version": "0.0.3",
"version": "0.0.4",
"description": "",
"main": "MMM-CommandToNotification",
"dependencies": {
"child_process": "latest"
},
"dependencies": {},
"devDependencies": {},
"repository": {
"type": "git",
Expand Down

0 comments on commit 48c2a16

Please sign in to comment.