-
Notifications
You must be signed in to change notification settings - Fork 0
/
app.js
145 lines (115 loc) · 3.77 KB
/
app.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
var PythonShell = require('python-shell');
var _ = require('lodash');
var psTree = require('ps-tree');
var EventEmitter = require('events').EventEmitter;
var util = require('util');
var path = require('path');
var DEBUG = 'node-pi-motion';
var kill = function (pid, signal) {
// Follow default process.kill behavior
signal = signal || 'SIGTERM';
psTree(pid, function(err, children) {
var pids = [pid].concat(_.map(children, _.partialRight(_.pick, 'PID')));
_.forEach(pids, function(tpid) {
try { process.kill(tpid, signal) }
catch (ex) { }
});
});
}
var buildPythonArgs = function (opts) {
var argMap = {
sensitivity: 's',
threshold: 't',
night: 'n',
sleep: 'z'
};
var buildArgs = _.map(opts, function(value, key) {
if (_.isUndefined(argMap[key])) return;
if (_.isBoolean(value)) {
if (value === true) return '-' + argMap[key];
else return;
}
else return '-' + argMap[key] + ' ' + value;
});
return _.compact(buildArgs);
}
function NodePiMotion(opts) {
var self = this;
opts = opts || {};
self.throttle = opts.throttle || 0;
self.autorestart = opts.autorestart || false;
self.verbose = opts.verbose || false;
self.debug = opts.debug || false;
/* Refer to https://github.com/lodash/lodash/issues/222 and
http://benalman.com/projects/jquery-throttle-debounce-plugin
for a discussion about the trailing and leading options */
self.detectedMotion = _.throttle(function() {
self.emit('DetectedMotion');
}, self.throttle, {trailing: false});
EventEmitter.call(self);
if (self.debug) {
setInterval(function() {
self.detectedMotion();
}, self.debug);
return
}
var pythonArgs = buildPythonArgs(opts);
self.pyOptions = {
mode: 'text',
pythonPath: opts.pythonPath || '/usr/bin/python',
pythonOptions: ['-u'],
scriptPath: path.join(__dirname, 'python'),
args: pythonArgs
};
self.pythonChild = new PythonShell('pi-motion-lite.py', self.pyOptions);
self.attachListeners();
}
util.inherits(NodePiMotion, EventEmitter);
NodePiMotion.prototype.attachListeners = function () {
var self = this;
self.pythonChild.on('message', function(message) {
var data;
var split = message.split('-');
message = split[0];
data = split[1];
if (message === 'DetectedMotion') {
self.detectedMotion();
} else if (message === 'ready') {
// When the script emits ready it will also pass the number of seconds before the first check will occur
setTimeout(function() {
self.ready();
}, data * 1000);
}
if (self.verbose) console.log(DEBUG, split.join('-'));
});
self.pythonChild.on('error', function(err) {
if (self.verbose) console.log(DEBUG, 'Python script errored with error: ', err);
// The error event is fired on non 0 exit or when writing to stderr so we want to make sure the
// script has actually exited before eventually restarting it
self.close();
if (self.verbose) console.log(self.pythonChild);
self.emit('error', err);
});
self.pythonChild.on('close', function () {
if (self.verbose) console.log(DEBUG, 'Python script has exited');
if (self.autorestart) {
// Restart the script at a random timeframe in the next five seconds
setTimeout(function() {
self.pythonChild = new PythonShell('pi-motion-lite.py', self.pyOptions);
self.attachListeners();
}, Math.random() * 5000);
}
});
}
NodePiMotion.prototype.close = function () {
var self = this;
if (!self.pythonChild.terminated) {
if (self.verbose) console.log(DEBUG, 'Killing python script...');
self.pythonChild.end()
kill(self.pythonChild.childProcess.pid, 'SIGTERM');
}
}
NodePiMotion.prototype.ready = function () {
this.emit('ready');
}
module.exports = NodePiMotion;