0

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.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
user3185748
  • 2,478
  • 8
  • 27
  • 43
  • Like it said, you're writing after you've ended the stream. This line is the culprit: `py.stdin.end();` – Mike Cluck Sep 12 '16 at 20:33
  • Is that to say that I don't need to use `py.stdin.end();` at all? I thought this was the line that closed the child process I created? – user3185748 Sep 12 '16 at 20:36
  • That all depends. What that does is it closes the input stream. If you want to close the process, you'll need to make sure you re-open it every time you want to run that program again. So it depends on if your Python script takes multiple commands through stdin or if it processes one then quits. If it's the former, remove `py.stdin.end()`. If it's the latter, you'll need to re-spawn the process every time you want to run that Python program. – Mike Cluck Sep 12 '16 at 20:39
  • 1
    Ahh, that makes a lot of sense. My case is the latter, and sure enough once I moved the spawning of the child process to the beginning of my `setOn` function everything works as expected. Thanks so much for your help! If you wish to submit a formal answer, I would be happy to approve it. – user3185748 Sep 12 '16 at 20:46

1 Answers1

1

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']);
    ...
}
Mike Cluck
  • 31,869
  • 13
  • 80
  • 91