user3185748 user3185748 - 2 months ago 55
Javascript Question

How to call Python function from Node.JS

I'm working on making a Homebridge plugin for a project.

Homebridge
is a Node.JS server which I have running on a Raspberry Pi which emulates an Apple HomeKit Bridge.

Using this link, I was able to execute Python code from the following Node.JS code:

var Service, Characteristic;

var spawn = require('child_process').spawn;
var py = spawn('python', ['/home/pi/Desktop/RFbulb/nRF24L01PLUS.py']);
var data = [10,10,10];
var dataString = '';

var RFstatus = true;

module.exports = function(homebridge) {
Service = homebridge.hap.Service;
Characteristic = homebridge.hap.Characteristic;

homebridge.registerAccessory("homebridge-RFbulb", "RFbulb", RFbulbAccessory);
}

function RFbulbAccessory(log, config) {
this.log = log;
this.config = config;
this.name = config["name"];
this.address = config["address"];

this.service = new Service.Lightbulb(this.name);
this.service
.getCharacteristic(Characteristic.On)
.on('get', this.getOn.bind(this))
.on('set', this.setOn.bind(this));
}

RFbulbAccessory.prototype.setOn = function(on, callback) { // This is the function throwing the error
var state = on ? "on": "off";
if (state == "on") {
data = [1,parseInt(this.address, 10),100];
dataString = '';
py.stdout.on('data', function(data) {
dataString += data.toString();
});
py.stdout.on('end', function() {
console.log(dataString);
});
py.stdin.write(JSON.stringify(data));
py.stdin.end();
RFstatus = true;
}
callback(null);
}

RFbulbAccessory.prototype.getServices = function() {
return [this.service];
}


Interestingly enough, when I activate the
setOn
function the first time (for example to turn the device on) it works fine, but when I activate the
setOn
function a second time (to turn the device off) I get the following errors and the server exits:

events.js:141
throw er; // Unhandled 'error' event
^

Error: write after end
at writeAfterEnd (_stream_writable.js:166:12)
at Socket.Writable.write (_stream_writable.js:211:5)
at Socket.write (net.js:642:40)
at RFbulbAccessory.setOn (/usr/lib/node_modules/homebridge-RFbulb/index.js:47:12)
at emitThree (events.js:97:13)
at emit (events.js:175:7)
at Characteristic.setValue (/usr/lib/node_modules/homebridge/node_modules/hap-nodejs/lib/Characteristic.js:155:10)
at Bridge.<anonymous> (/usr/lib/node_modules/homebridge/node_modules/hap-nodejs/lib/Accessory.js:710:22)
at Array.forEach (native)
at Bridge.Accessory._handleSetCharacteristics (/usr/lib/node_modules/homebridge/node_modules/hap-nodejs/lib/Accessory.js:655:8)


What could be causing this error? Especially since the function appears to work fine for a single use.

Answer

You're getting that error because you're closing the input stream:

py.stdin.end();

After a stream has been closed, you can no longer write to it like you are here:

py.stdin.write(JSON.stringify(data));

If the Python program you're running accepts multiple commands over STDIN then simply remove the py.stdin.end() line.

However, it's likely that your Python program runs once then completes. If that's the case, you will need to respawn the process every time you want the program to run.

if (state === "on") {
    py = spawn('python', ['/home/pi/Desktop/RFbulb/nRF24L01PLUS.py']);
    ...
}