What is it?
Hub TCP server for using the IR module via HTTP.
Both to record of new signals with a given name and to play them back.
Using net
since SDK doesn't have the nodejs HTTP-module available.
Why?
I really wanted to be able to call my IR module from my phone homescreen or Google Home (via IFTTT). I got it working so I wanted to share it if anyone else find it helpful
The code is what could be called a POC, so code feedback and/or changes are totally welcome.
How does it work?
- Record a signal
GET {ip}:1338?record={name_of_signal}
- Plays a signal
GET {ip}:1338?cmd={name_of_signal}
- Cancel recording
GET {ip}:1338?cancelRecord=true
Example;
- call
GET 192.168.0.100:1338?record=volume_up
- Press Volume Up on remote towards IR module
- returns
200 OK Signal volume_up stored!
- call
GET 192.168.0.100:1338?cmd=volume_up
- returns
200 OK Signal sent!
- Volume goes up
The code
module.json
{
"name": "IRTCP",
"version": "1.0.0"
}
main.js
// main.js
const net = require("net");
const ir = require("ir");
const datastore = require("datastore");
const utils = require("./utils");
const respondToClient = function(c, statusCode, message) {
c.write(
"HTTP/1.1 " + statusCode + "\r\n" +
"\r\n"
);
c.write(message);
c.end();
}
const handleCmd = function(c, cmd) {
datastore.get(cmd, function(err, strBuffer) {
if (!err && strBuffer) {
var signal = new Uint32Array(utils.str2ab(strBuffer));
if (!ArrayBuffer.isView(signal)) {
return respondToClient(c, "422 Unprocessable Entity", "Unknown signal");
}
ir.play(signal, function(err) {
if (!err) {
return respondToClient(c, "200 OK", "Signal sent");
}
return respondToClient(c, "500 Internal Server Error", "Couldn't send signal");
});
} else {
return respondToClient(c, "422 Unprocessable Entity", "Unknown cmd");
}
});
}
const handleRecord = function(c, name) {
/* TODO: add a timeout for listening for complete */
ir.record();
console.log("recording signal...");
ir.on("recordComplete", function(signal) {
datastore.put(name, utils.ab2str(signal.buffer), function(err) {
if (!err) {
console.log("Signal " + name + " stored!");
return respondToClient(c, "200 OK", "Signal " + name + " stored!");
} else {
return respondToClient(c, "500 Internal Server Error", "Couldn't store signal");
}
});
});
}
var server = net.createServer(function(c) {
console.log('server connected');
c.on('end', function() {
console.log('server disconnected');
});
c.on('data', function(data) {
console.log(data);
var match = data.toString().match(/GET \/\?.[^ ]*/);
if (!match) {
return respondToClient(c, "403 Forbidden", "Forbidden method");
}
var params = utils.parseParams(match[0].split("GET /?")[1]);
if (params.cmd) {
return handleCmd(c, params.cmd);
} else if (params.record && params.record.length > 0) {
return handleRecord(c, params.record);
} else if (params.cancelRecord) {
ir.cancelRecord();
console.log("recording canceled!");
return respondToClient(c, "200 OK", "Recording canceled!");
} else {
return respondToClient(c, "403 Forbidden", "Forbidden params");
}
});
});
server.listen(1338, "0.0.0.0", function() {
console.log('server bound', server.address().port);
});
utils.js
const ab2str = function(buf) {
return String.fromCharCode.apply(null, new Uint16Array(buf));
}
const str2ab = function(str) {
var buf = new ArrayBuffer(str.length*2);
var bufView = new Uint16Array(buf);
for (var i=0, strLen=str.length; i < strLen; i++) {
bufView[i] = str.charCodeAt(i);
}
return buf;
}
const parseParams = function (s) {
var obj = {};
var arr = s.split("&");
var temp;
for (i = 0; i < arr.length; i++) {
temp = arr[i].split("=");
obj[temp[0]] = temp[1];
}
return obj;
};
exports.ab2str = ab2str;
exports.str2ab = str2ab;
exports.parseParams = parseParams;