[LINUX] Lirc and Apple Remote long press hack

UPDATE 2014-04-04
---
I recently needed to rebuild my HTPC. I went for ubuntu 14.04 server as a base.
Teknos suggestion in post #13 below is far simpler to get working and seems to work well.
---

I recently set up a HTPC running Linux on an old macbook pro. I wanted to get the apple remote working with lirc and long press, because I had an apple remote lying around. I couldn't find the solution on the interweb, so I hacked one together myself. Its a python script to proxy the Lirc socket and modify the events to contain long press events (when appropriate). Its rough, but seems to work. (forgive any formatting foibles.. my first posts here)

There a few moving parts (described in the following posts).

Daemon to proxy lirc port

Run XBMC with custom lirc socket

XBMC Lircmap.xml

XBMC keymap.xml

This script is the guts of it. Save it somewhere and remember to make it executable. I have it in /usr/local/bin/lircmangled

Code:

#!/usr/bin/env python

#
# proxy the lirc socket to distinguish between short and long presses
# on apple remote buttons
#
# long press occurs when > n repeat events occur within t time of eachother.
# short press occurs when there is >= t time after an event.
#
# holding apple remote keys generates lirc events with increasing repeat counts.
# clicks on the apple remote keys can generate a few repeat lirc events, and
# if they are fast enough, can also have increasing repeat counts.
# this makes it a bit tricky to find a couple of short clicks followed by a
# long click as this looks like a continuous stream of repeat lirc events for
# the same button. can track this by monitoring the time beteen events and
# resequencing the repeat counts where appropriate
#
# Julian Neil
#
# this software is released under the Do It Yourself License.
# If it doesn't work for you, then fix it.

def found_terminator(self):
with self.lock:
now = time.time()
self.cancel_timer()
bits = self.ibuffer.split(None, 5)
# check for apple remote button presses
if (len(bits) == 4 and 'apple' in bits[3].lower()):
repeats = int(bits[1], 16)
# if this is a repeat event and the time since the last press was < t we need to check for a long press
if (repeats == self.last_repeats + 1 and now - self.last_time < repeat_threshold):
self.hold_repeats += 1
if (self.hold_repeats >= hold_repeats):
# if we get >= n repeats all within time t of eachother consider it a long press
bits[2] += '_HOLD'
bits[1] = "%0.2X" % (self.hold_repeats - hold_repeats)
# dont output repeat hold events for PLAY or MENU
if (self.hold_repeats == hold_repeats or ( 'PLAY' not in bits[2] and 'MENU' not in bits[2] )):
self.mangle_proxy.send(' '.join(bits) + '\n')
self.last_line = None
else:
# not enough repeats yet to distinguish between short and long press
self.last_line = bits
self.start_timer()
else:
# if we have a buffered line, it must have been a short press.. so send it
if (self.last_line):
self.last_line[1]='00'
self.mangle_proxy.send(' '.join(self.last_line) + '\n')

# new button press.. dont know if it is short or long press yet
self.hold_repeats = 0
self.last_line = bits
self.start_timer()

# remember the repeat count and time to check against when we get the next event
self.last_repeats = repeats
self.last_time = now
else:
# not an apple button press.. just forward it
self.mangle_proxy.send(self.ibuffer + '\n')

self.ibuffer = ''

# start a timer so we know when to send a short press
def start_timer(self):
self.timer = threading.Timer(repeat_threshold, self.timeout)
self.timer.start()

Thanks a lot for posting this, Julian! Works quite nicely for me as well. Although I only have it running for 5 minutes by now, it seems do it's job without any big issues. Only thing I noticed is that keeping a button pressed for a prolonged amount of time may sometimes still result in additional events. (Didn't have time to look into it yet though.)

(2012-03-13 16:16)Kissell Wrote: I think this is maybe because my /etc/lirc/lircd.conf file is mapping different names? and those names have to match Lircmap.xml

If that's the case, maybe posting your /etc/lirc/lircd.conf file would be helpful, because I used "irrecord" to create mine, and I'm not sure how to add a HOLD button key in there.

Hi Kissell.

It isn't possible to configure lircd.conf to recognise long presses, or I would have done that instead of resorting to this workaround. This script runs in the background, taking the output from lirc and post-processing it to identify long presses. It then posts output containing long presses (using a format identical to lirc) on a different socket. xbmc then reads this socket instead of the usual lirc socket.

I am using the lircd config for apple remote that comes with the ubuntu lirc package. It utilizes the macmini driver.
It is located at /usr/share/lirc/remotes/apple/lircd.conf.macmini .. included here, but I'm not sure that it will help.

Yes, I have gotten this working in openelec. Although it is somewhat difficult. I had to compile my own openelec build because lircd was remove from the official build way back. (I haven't been tinkering with openelec in about 6 months so this may have changed with newer builds)

I am not an expert linux guy and figured out how to get this working after many many hours. I wanted to make a write up but put it off too long and not I forgot how I got this working. I will try, over the next few weeks to go back through the process and make a writeup. The Apple Remote w/ long presses really is a nice remote.

Cough, cough!
Way easier, and seeing this thread inspired me to prove it. Long keypress support too!
See http://teknogeekz.com/blog/?p=422
And try not to drive up the cost of the Apple IR replacent receivers!

Forgive me if i'm wrong. But, I don't believe this will work on openelec since it's a "Read-Only" operating system and doesn't have your apt-get. Any extra modules would have to be compiled into the OS.