Jump to content

Keene Electronics IR Anywhere


mapeter

Recommended Posts

Those ISY994 users that have an ISY994 IR can trigger programs when an IR code is received.  Here is an option for those that don’t have and ISY994 IR, or who want to trigger scenes & programs when a play/pause button is pressed for another device.  This will also work for those of you that want to program buttons on your Harmony remote to trigger ISY programs since Logitech has yet to give us the ability to fire http requests from the hub.)
 
I just received a Keene Electronics IR Anywhere and wrote the sample program below in node.js.  The sample program receives IR codes and then shoots http requests to the ISY.  The IR Anywhere receives IR codes and then sends them to a UDP listener on the network. (You can also use two of them to send IR long distance over an IP network.  One of them acts as a receiver and the other as a transmitter.)  The device cost just under USD$100 and took just over a week to arrive from the UK.
 
Below is an example of the data received from the IR Anywhere when the DirecTV Pause button is pressed:
K 240A 16D9 04D0 0495 04D0 023B 027A 0239 0297 0476 04D2 0221 0294 021E 02B2 045A 04F0 0476 04F0 01EE 2000
K 240A 0B3A 050D 0445 050F 01EE 02E2 01D2 02E4 0429 050F 01EE 02E2 01D2 02C9 0444 050F 0445 050F 01EE 2000
K 240A 0B27 050F 0445 050F 01EE 02C9 01EC 02E2 042B 050F 01EC 02E4 01D0 02C9 0444 050F 0447 050F 01EC 2000
K 240A 0B27 050F 0447 050F 01EC 02E4 01D0 02E4 0429 050F 01EE 02C9 01EA 02E4 0429 050F 0445 050F 01EE 2000

The remote I was using sends the pause command four times when the button was pressed.  As such, the IR command was sent to the UDP listener program four times.  If you look closely, you will notice that the data on the four lines is not the same.  This means that your program cannot do a simple string compare to determine if an IR code was received.

 
Rather than looking at the absolute values, we need to decode the bits within the data.  I am not going to go into the detail of the how to read the data, but I have attached the IR Anywhere API guide at the end of this post.  The bulk of the data received specifies the timing in microseconds of the data bits.  Through some very unscientific testing, I decided to consider any value greater than decimal 1000 (hex 3e8) as a one, and any value less than it a zero.  If you were to highlight the values above that were greater than 03E8, you would notice that the same blocks of data are highlighted on each of the four rows.  Using this, the data bits for a DirecTV Pause command is “111000011001000010”.  If you were to start from the left and group every eight bits into a hex value, you would get “e1902”.  So, we can compare the code we receive to “e1902” to know that we received a pause command.  (Those of you that are software developers probably figured out that grouping left-to-right is not correct and the program should go from right-to-left.  It may not be technically correct, but it works…)
 
Once we have the match, we can fire the http REST request to the ISY.  As mentioned above, the same IR code is received multiple times when the button is pressed.  We do not want to fire the same http request multiple times.  So, a one second timer is used as a throttle to prevent duplicate requests from being fired off.  Using the example above, when the first IR request is received from the IR Anywhere, the http request is fired to the ISY.  Then the one second timer is started that prevents subsequent http requests from being fired.  During this second the other three IR commands are received and basically ignored.
 
Below is the output of the sample program for the example above:
 
Rcvd: K 240A 16CB 04EE 048F 04D5 021E 02B2 0204 02B2 0458 04F2 0200 02CF 0428 02E4 01D0 02E4 01D2 050F 01EE 2000
Freq: 36 Length: 18 IR Code: e1902  Bit Text = 111000011001000010
**** DirecTV Pause - Turning on Family Room Lights ****

Rcvd: K 240A 0B27 050F 0445 050F 01EE 02E2 01D2 02E4 0429 050F 01EE 02E2 042B 02C7 01EE 02E2 01D2 050F 01EE 2000
Freq: 36 Length: 18 IR Code: e1902  Bit Text = 111000011001000010

Rcvd: K 240A 0B57 04F0 0474 04F0 0202 02B7 01FD 02B4 0444 050F 01EF 02E2 042B 02C9 01EA 02C7 01EE 050F 01EE 2000
Freq: 36 Length: 18 IR Code: e1902  Bit Text = 111000011001000010

Rcvd: K 240A 0B27 050F 0445 050F 01EE 02C7 01EE 02E2 042B 050F 01EE 02E2 042B 02E2 01D2 02E2 01D3 050F 01EE 2000
Freq: 36 Length: 18 IR Code: e1902  Bit Text = 111000011001000010

I tested a Denon AVR mute command.  It has a much longer IR command:

Rcvd: K 2432 0CDD 0693 01A3 01E3 0186 01E3 016E 050A 018A 01FA 016E 050C 0186 01FD 016C 050C 0186 0200 0169 0200 016A 050F 0167 020D 014D 020A 013C 053F 0154 053C 0158 021F 0138 021F 013A 021F 013A 021F 013A 021F 013A 021F 013A 021F 013A 021F 013A 053C 0156 021F 013A 053F 0156 021D 013C 021F 013A 021F 0138 021F 013A 053F 0154 021F 013A 021F 013A 053F 0156 053C 0156 053F 0156 021D 013C 053C 0158 021F 0138 021F 013A 021F 013A 021F 013A 053F 0156 053C 0174 0203 013A 053C 0175 051F 018F 0505 018D 01F8 0158 2000
Freq: 36 Length: 98 IR Code: 8222828022082a20a2a0  Bit Text = 10000010001000100000100000101000000000000000001000100000000010000010101000100000000010100010101000

Hopefully this will be of value to some of you.  I am planning to use the IR Anywhere to also control an HTD Lync system.  The Lync does not have discrete IR commands for each of the zones.  My plan is to use a program similar to the one below to map discrete volume, input source, etc commands for each zone and have the appropriate http request fired to the Lync controller.  (I am not going have Lync Keypads in all of my rooms.)

 
// IRAnywhere sample program with simple ISY994 integration
//
// This is modeled from the UDP Server code located at 
//			https://www.hacksparrow.com/node-js-udp-server-and-client-example.html
//
// todo: parse from right to left instead of left to right so the hex code is "more correct"

// The following port and host should match the "Computer IP Address" in the IRAnywhere "Configuration" screen
var PORT = 65432;
var HOST = '192.168.0.XXX';

// The following is the information for the ISY
var isyUsername = 'username';
var isyPassword = 'password';
var isyAddress = '192.168.0.XXX';
var isyPort = 80;
var isyRqs = ''

// Create the UDP server and start listening
var dgram = require('dgram');
var server = dgram.createSocket('udp4');

server.on('listening', function () {
    var address = server.address();
    console.log('UDP Server listening on ' + address.address + ":" + address.port);
});

// Used to send requests to the ISY and other devices via http
var http = require('http');

// Process the IR codes that are received from the IRAnywhere
server.on('message', function (message, remote) {
	var carrierFrequency = parseInt(message.toString().substr(2,2),16);
	var codeLength = (parseInt(message.toString().substr(4,2),16) - 1)*2;
	
	console.log('\nRcvd: ' + message.toString().substr(0,message.toString().length-1));

	// midBit is used to determine zeros bits from one bits.  The value is somewhat arbitrary, but appears
	//   to work well
	var midBit = 1000;

	var irCodeText = '';
	var irCodeByte = '';
	var irCode = '';

	var curPos = 0;
	var curBit = 0;

	// Sometimes data is received with no IR code.	Skip processing if this happens
	if (codeLength > 0) {
	
		//Parse the string and determine the zero bits and the one bits
		while (curPos < codeLength) {
			curBit = parseInt(message.toString().substr((curPos*5)+12,4),16);
			if (curBit < midBit) {irCodeByte += '0'};
			if (curBit > midBit) {irCodeByte += '1'};
			curPos++;

			//Every eight bits, create a byte and add it to the irCodeText and irCode
			if (irCodeByte.length ==  {
				irCodeText += irCodeByte;
				irCode += '' + parseInt(irCodeByte, 2).toString(16);
				irCodeByte = '';
			}
			
		}

		//Process any final bits that haven't been added to the irCodeText and irCode
		if (irCodeByte.length > 0) {
			irCodeText += irCodeByte;
			irCode += '' + parseInt(irCodeByte, 2).toString(16);
		}
		
		//Write out the code to the console
		console.log('Freq: ' + carrierFrequency + ' Length: ' + codeLength + ' IR Code: ' + irCode + '  Bit Text = ' + irCodeText);
		
		//Process the IR code received
		processIR(irCode);

	}
	
});

// Process the IR codes that were received
processIR = function(irCode) {
	
	if (allowRequests) {
	
		// DirecTV Pause
		if (irCode == 'e1902') {
			console.log('**** DirecTV Pause - Turning on Family Room Lights ****');
			sendIsyRequest('/rest/nodes/10040/cmd/DON');
		}
		
		// DirecTV Play
		if (irCode == 'e1872') {
			console.log('**** DirecTV Play - Turning off Family Room Lights ****');
			sendIsyRequest('/rest/nodes/10040/cmd/DOF');
		}

		// Denon AVR Mute
		if (irCode == '8222828022082a20a2a0') {
			console.log('**** Denon Mute ****');
		}

		sleep();
	}
}

// Send a request to the ISY.  We don't care about the http response
sendIsyRequest = function(isyRequest) {
	var options = {
		host: isyAddress,
    	port: isyPort,
    	path: isyRequest,
    	headers: {
     		'Authorization': 'Basic ' + new Buffer(isyUsername + ':' + isyPassword).toString('base64')
		}         
	};
	http.request(options, callback).end();
}

callback = function(response) {
	
}

var allowRequests = true;

//Since IR codes are received mulitple times, use a timer to keep the program from
//   reacting to the same code multiple times.
sleep = function() {
	allowRequests = false;
	setTimeout(resetHold, 1000);
}

resetHold = function() {
	allowRequests = true;
}

server.bind(PORT, HOST);

 
 

API documentation.pdf

Link to comment

Archived

This topic is now archived and is closed to further replies.


×
×
  • Create New...