Open terminal, execute command, and enter interactive mode - node.js

I am writing a home automation web app. I have music that streams from Google music to my speakers and I use vlc to output the sound. What I cannot do is seem to pause the song remotely. For example, cvlc url will open and stream the url, then it enters an interactive mode where pause can be typed and it will pause.
When I try to do this from my node.js app, nothing happens. It just continues to play.
var terminal = require('child_process').spawn('bash');
terminal.stdout.on('data', function (data) {
console.log(data);
});
terminal.on('exit', function (code) {
console.log('child process exited with code ' + code);
});
terminal.stdin.write('cvlc "' + req.url +'" & \n');
terminal.stdin.write('pause');

try putting a '\n' after you pause command:
terminal.stdin.write('pause\n');
Also, I am not familiar with cvlc, yet, if you start it in the BG, not sure that you can write to its stdin, hence it might ignore all in all your 'pause' command, did you try invoking it without the '&'?
Also, why start a bash and then run cvlc? Why not:
var terminal = require('child_process').spawn('cvlc');
Last but not least, are you sure the default path is sufficient?

I never actually figured this out via terminal. Instead I used VLC's http server option and then did get requests to control pause and play.
Spawn:
var term = spawn.spawn('cvlc',['-I', 'http',req.url,'localhost:8080']);
Pause:
var req = http.get('http://:password#localhost:8080/requests/status.xml?command=pl_pause', function (r) {
r.on('data', function () { /* do nothing */
});
});
And Play:
var reqd = http.get('http://:password#localhost:8080/requests/status.xml?command=pl_play', function (r) {
r.on('data', function () { /* do nothing */
});
r.on('error', function (err) { /* do nothing */
console.log(err)
});
});
Hope that helps someone with a VLC control problem. I was doing this through a headless Raspberry Pi. I was trying to avoid paying for a Chromecast for my speakers and succeeded.

Related

Can I “listen” for a specific output with child_process?

So far I have gotten my script to execute a windows .bat file with child_process, my issue is that it opens it in the background with no way to “connect” to it to see what happens and debug, is there a way to “listen” for a certain output to happen? For example, if the .bat outputs a “Done!” in the shell at one point, is there a way to make my node.js script detect that certain keyword and run further commands if it does?
Thanks!
Some clarification: The .bat outputs "Done!" and stays running, it doesn't stop, all I want to do is detect that "Done!" so that I can send a message to the user that the server has successfully started
My current code:
exec('D:\\servers\\game_server_1\\start.bat', {shell: true, cwd: 'D:\\servers\\game_server_1'});
Well, if you're trying to do a one and done type of NodeJS script, you can just spawn a process that launches with the given command and exits when all commands completed. This creates a one and done streaming interface that you can monitor. The stdout returns a data buffer that returns the command you ran, unless it's something like START to launch a program-- it returns null. You could just issue a KILL command after the START -- your_program.exe:
const spawn = require('child_process').spawn;
const child = spawn('cmd.exe', ['/c', 'commands.bat']);
let DONE = 0;
const done = () => {
console.log("log it");
DONE++;
};
child.stdout.on('data', function (data) {
console.log('stdout: ' + data);
//it's important to add some type of counter to
//prevent any logic from running twice, since
//this will run twice for any given command
if ( data.toString().includes("DONE") && DONE === 0 ) {
done();
}
});
child.stderr.on('data', function (data) {
console.log('stderr: ' + data);
});
child.on('exit', function (code) {
console.log('child process exited with code ' + code);
});
Keep in mind, when you run a command to launch a program and the program launches, the data buffer will be null in stdout event listener. The error event will only fire if there was an issue with launching the program.
YOUR .BAT:
ECHO starting batch script
//example launching of program
START "" https://localhost:3000
//issue a command after your program launch
ECHO DONE
EXIT
You could also issue an ECHO DONE command right after the command where you launched the program and listen for that, and try and parse out that command from stdout.
You could use a Regular expression.
const { spawn } = require('child_process');
const child = spawn(...);
child.stdout.on('data', function (data) {
console.log('stdout: ' + data);
// Now use a regular expression to detect a done event
// For example
data.toString().match(/Done!/);
});
// Error handling etc. here

child_process.on('close') is sometimes painfully slow

I have a electron application that opens a external program (in my case Office), and has to wait for the program to be closed.
the code I wrote works great but sometimes the child_process.on('close') event is fired 10 or 20 seconds after the program has closed. The code is:
const cp = require("child_process");
child = cp.spawn(path/to/Office.exe + ' "' + path/to/myFile.pptx + '"', {shell: true});
child.on('close', function (code) {
//do something
});
Most of the time it reacts after 1 or 2 seconds which is fine, but sometimes it takes up to 20 seconds until I receive the close event. The program closes fast (according to the task manager), but node seems to wait for something.
I also tried child.on('exit'), calling the program with cp.exec()and using the options.stdio: ignore for spawn, as I thought maybe node is waiting for some stream from the child. But that made no difference.
Does anybody know a safe way to speed that process up?
I have tried your code and the close event triggers with a 0.5-2s delay, bearable i would say.
However, the 20s delay did not occur, but if this problem still persists on your end, you can try the approach below, which consists in checking the spawn pid.
const pidExists = (pid) => {
let pidOk = true;
try {
process.kill(pid, 0);
} catch (e) {
pidOk = false;
}
return pidOk;
};
const cp = require("child_process");
// I added the detach option because we won't need that process anymore since we're following the PID.
let child = cp.spawn(path/to/Office.exe + ' "' + path/to/myFile.pptx + '"', {shell: true, detach: true});
let officePID = child.pid; // this is the spawn pid
setInterval(()=>{
if( pidExists(officePID)){
console.log('file is still open', new Date().getTime());
}else{
console.log('file was closed', new Date().getTime());
process.exit(0);
}
}, 500);
This is a better approach since you said that the task manager shows you that the program was closed.

Unable to get output from infinitely running Ruby application spawned from Node child process

I am trying to write a GUI with Node and Electron around an existing ruby command line application. I found an example how to get output from a child process by doing something like:
var spawn = require('child_process').spawn;
var child = spawn('node', ['child.js']);
child.stdout.on('data', function (data) {
console.log('stdout: ' + data);
});
child.stderr.on('data', function (data) {
console.log('stderr: ' + data);
});
And the child.js looks like this
while(true) {
console.log('blah');
}
This is working fine for me, but if I try and do this with a ruby application switching to this
var child = spawn('ruby', ['test.rb']);
and this being the ruby code
while true
sleep 2
puts 'test'
end
I get no output. It seems to hang on the output. I would expect to see 'test' printed every 2 seconds.
Ruby doesn't know you want the output right away, and buffers it for efficiency. If you make it flush the output buffer (IO#flush), it works.
while true
sleep 2
puts 'test'
STDOUT.flush
end
Alternately, you can tell Ruby you want all output right away (IO#sync=):
STDOUT.sync = true
while true
sleep 2
puts 'test'
end
Incidentally, your original code works on JRuby, so the buffering behaviour differs... I did not know that...

node.js and bluetooth barcode-scanner

I am struggling quite a while now to get a solid, long-term connection to a bluetooth barcode scanner from Inateck using node.js. The process is running in the background (linux, no input-focus) that's why I configured the scanner as a SPP device.
The connection is basically working as long as the scanner doesn't automatically switch off to save power, which is after about 5 minutes.
My first approach was to use the bluetooth-serial-port package. It discovers the scanner, reads the barcodes but when the scanner switches off, I don't know how to re-connect. I added an interval timer to check the connection and try to connect again if isOpen() returns false (which works once). When I press the button on the scanner it switches back on and I can re-connect but after a view seconds isOpen() returns false even if the connection is established, and I don't get any further readings. Here is my code:
var btSerial = new (require('bluetooth-serial-port')).BluetoothSerialPort();
var btInterval = null;
btSerial.on('found', function (address, name) {
btSerial.findSerialPortChannel(address, function (channel) {
if (address === '00:06:11:68:15:81') {
btSerial.connect(address, channel, function () {
console.log('connected to ' + address);
btInterval = setInterval(function () {
if (!btSerial.isOpen()) {
btSerial.close();
clearInterval(btInterval);
console.log('lost connection - try to reconnect');
btSerial.inquire();
}
}, 5000);
}, function () {
console.log('cannot connect to ' + address);
});
}
}, function () {
console.log('found nothing');
});
});
btSerial.on('finished', function () {
console.log('finished');
});
btSerial.on('data', function (buffer) {
console.log(buffer.toString('utf-8'));
});
btSerial.inquire();
The output looks like this:
finished
connected to 00:06:11:68:15:81
found nothing
... scanning works ...
lost connection - try to reconnect
finished
connected to 00:06:11:68:15:81
... scanning works ...
lost connection - try to reconnect
finished
... that's it - no more scans ...
^C
An other idea was to use nodes fs() an read directly from '/dev/rfcomm0'.
scanner = fs.createReadStream('/dev/rfcomm0', {bufferSize: 1});
scanner.on('open', function () {
logger.info('Scanner connected');
});
scanner.on('end', function () {
logger.info('End of data stream');
});
scanner.on('close', function () {
logger.info('Scanner disconnected');
});
scanner.on('error', function (error) {
logger.error('Scanner error');
});
scanner.on('data', function (chunk) {
logger.info(chunk.toString('ascii', 0, 13));
}
});
});
Connecting is done by the OS automatically when reading from the device and I do receive the codes via on('data',..). But I do have the same problem when the scanner switches off after a while. I do receive the on('close',..) event but reconnecting using fs.createReadStream() again doesn't work any more.
Maybe someone of you already had to deal which such a problem and can give me a hint how to handle this. I appreciate every suggestion.
Thanks,
Max
That's not the way I wanted to go for but a bash script to launch my node app when the scanner is available, does the job:
#!/bin/bash
echo "Press CTRL+C to stop..."
while :
do
if hcitool scan | grep -q "00:06:11:68:15:81"; then
# BT scanner found
node .
fi
sleep 1
done

Electron kill child_process.exec

I have an electron app that uses child_process.exec to run long running tasks.
I am struggling to manage when the user exits the app during those tasks.
If they exit my app or hit close the child processes continue to run until they finish however the electron app window has already closed and exited.
Is there a way to notify the user that there are process still running and when they have finished then close the app window?
All I have in my main.js is the standard code:
// Quit when all windows are closed.
app.on('window-all-closed', function() {
// On OS X it is common for applications and their menu bar
// to stay active until the user quits explicitly with Cmd + Q
if (process.platform != 'darwin') {
app.quit();
}
});
Should I be adding a check somewhere?
Thanks for your help
EDITED
I cannot seem to get the PID of the child_process until it has finished. This is my child_process code
var loader = child_process.exec(cmd, function(error, stdout, stderr) {
console.log(loader.pid)
if (error) {
console.log(error.message);
}
console.log('Loaded: ', value);
});
Should I be trying to get it in a different way?
So after everyones great comments I was able to update my code with a number of additions to get it to work, so am posting my updates for everyone else.
1) Change from child_process.exec to child_process.spawn
var loader = child_process.spawn('program', options, { detached: true })
2) Use the Electron ipcRenderer to communicate from my module to the main.js script. This allows me to send the PIDs to main.js
ipcRenderer.send('pid-message', loader.pid);
ipcMain.on('pid-message', function(event, arg) {
console.log('Main:', arg);
pids.push(arg);
});
3) Add those PIDs to array
4) In my main.js I added the following code to kill any PIDs that exist in the array before exiting the app.
// App close handler
app.on('before-quit', function() {
pids.forEach(function(pid) {
// A simple pid lookup
ps.kill( pid, function( err ) {
if (err) {
throw new Error( err );
}
else {
console.log( 'Process %s has been killed!', pid );
}
});
});
});
Thanks for everyones help.
ChildProcess emits an exit event when the process has finished - if you keep track of the current processes in an array, and have them remove themselves after the exit event fires, you should be able to just foreach over the remaining ones running ChildProcess.kill() when you exit your app.
This may not be 100% working code/not the best way of doing things, as I'm not in a position to test it right now, but it should be enough to set you down the right path.
var processes = [];
// Adding a process
var newProcess = child_process.exec("mycommand");
processes.push(newProcess);
newProcess.on("exit", function () {
processes.splice(processes.indexOf(newProcess), 1);
});
// App close handler
app.on('window-all-closed', function() {
if (process.platform != 'darwin') {
processes.forEach(function(proc) {
proc.kill();
});
app.quit();
}
});
EDIT: As shreik mentioned in a comment, you could also just store the PIDs in the array instead of the ChildProcess objects, then use process.kill(pid) to kill them. Might be a little more efficient!
Another solution. If you want to keep using exec()
In order to kill the child process running by exec() take a look to the module ps-tree. They exaplain what is happening.
in UNIX, a process may terminate by using the exit call, and it's
parent process may wait for that event by using the wait system call.
the wait system call returns the process identifier of a terminated
child, so that the parent tell which of the possibly many children has
terminated. If the parent terminates, however, all it's children have
assigned as their new parent the init process. Thus, the children
still have a parent to collect their status and execution statistics.
(from "operating system concepts")
SOLUTION: use ps-tree to get all processes that a child_process may have started, so that they
exec() actually works like this:
function exec (cmd, cb) {
spawn('sh', ['-c', cmd]);
...
}
So check the example and adapt it to your needs
var cp = require('child_process'),
psTree = require('ps-tree');
var child = cp.exec("node -e 'while (true);'", function () { /*...*/ });
psTree(child.pid, function (err, children) {
cp.spawn('kill', ['-9'].concat(children.map(function (p) { return p.PID })));
});

Resources