IRTCP v1.0.0 (Hub server for using IR module via HTTP)



  • 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?

    1. Record a signal GET {ip}:1338?record={name_of_signal}
    2. Plays a signal GET {ip}:1338?cmd={name_of_signal}
    3. Cancel recording GET {ip}:1338?cancelRecord=true

    Example;

    1. call GET 192.168.0.100:1338?record=volume_up
    2. Press Volume Up on remote towards IR module
    3. returns 200 OK Signal volume_up stored!
    4. call GET 192.168.0.100:1338?cmd=volume_up
    5. returns 200 OK Signal sent!
    6. 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, 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;
    


  • @Emil Thanks, and thank you for quick responses on questions


  • FlicTeam

    Nice!
    This is great work, like to see more of this.



  • I forgot to add how I'm calling this from my homecreen of my phone.
    I'm using HTTP Shortcuts-app widget.


Log in to reply