Moode Forum
Using the Flirc USB for remote control with mOde 5.4+ - Printable Version

+- Moode Forum (http://moodeaudio.org/forum)
+-- Forum: moOde audio player (http://moodeaudio.org/forum/forumdisplay.php?fid=3)
+--- Forum: FAQ and Guides (http://moodeaudio.org/forum/forumdisplay.php?fid=9)
+--- Thread: Using the Flirc USB for remote control with mOde 5.4+ (/showthread.php?tid=1529)



Using the Flirc USB for remote control with mOde 5.4+ - TheOldPresbyope - 07-12-2019

[For the time being this is an on-going exploration. It will be converted to a proper HowTo when some odds-n-ends have been taken care of. This note assumes you are moderately Linux literate.]


The Flirc USB is an IR-to-USB adapter which "allows you to pair any remote control with your computer or media center. Just walk through our super simple cross platform pairing application, and you're done. Use your previously paired remote with no additional software on any machine with flirc" [quoting from the flirc.tv website].

Well, I didn't exactly find it super simple---there were some interesting bumps in the road---but I'm well along my way to a solution which allows me to control a number of moOde's playback functions from an IR-remote I had on hand (an older-model Roku IR-controller, as it happens, with 12 buttons).

I've implemented the usual play/pause, previous/next track, volume up/down, mute/unmute, load default playlist, load favorites playlist functions, and still have four buttons left over for functions I haven't thought about yet.

My approach differs from @didier31's FLIRC-USB for remote control in that I'm running a headless moOde installation without local display and wanted to avoid adding X-stuff I knew I didn't need. I chose to take advantage of a useful Python module instead. Candidly, I took this approach because I wanted to see if I could make it work and not because I thought there was anything wrong with his prior work. Courses for horses, as they say.

There's nothing special about my particular IR-remote and nothing special about the specific functions I chose to implement.

The Flirc presents to the operating system as a USB-keyboard input device. This makes life pretty simple---mostly.

Summary of steps:

  1. program the Flirc to output specific keycodes in response to button presses on the remote.
  2. write a Python script to react to events with these keycodes, invoking mpc or Tim's utility scripts.
  3. use systemd/udev to start the script when the Flirc is present at boot or hotplugged.
I've got 1) and 2) in hand and am working on a solution for 3) I can be happy with.

Some Details:

Nope, I'm not going to turn this into a travelogue of how I got from point A to point B by meandering through the forest. I'll put the gory details somewhere, maybe a personal blog, for those who care.

But I have to point out one detail. The Flirc documentation and software implies you can mix-and-match mimicking standard keyboard keys (like a-z), which they colorfully call "valid commands", and media-control keys (like play/pause). Well, yeah, you can but this turns out to involve two different USB endpoints, one for media-control and one for keyboard. 

At this stage of the game I wanted to deal with only one endpoint, and there weren't enough media-control codes available from Flirc (or at least I could find no way to get to them) for my needs, so I programmed all my buttons to emit keyboard codes (KEY_A, KEY_B, KEY_C, etc.), as you'll see in the script below. No one but the script sees the actual values so it doesn't matter unless you intend to move the Flirc back and forth between moOde and some other computer or media control center which expects to receive actual media-control commands as defined in the USB spec.

The script itself is an anticlimax:

Code:
pi@moode54b2:~ $ more Flirc-moode.py
#!/usr/bin/python3
import os, asyncio,evdev,subprocess,time

# Hackery by TheOldPresbyope
# - control some moOde playback functions from a Flirc USB

# the following endpoint tracks media keys:
#flirc=evdev.InputDevice('/dev/input/by-id/usb-flirc.tv_flirc-event-if01')
# the following endpoint tracks "valid command" keys
flirc=evdev.InputDevice('/dev/input/by-id/usb-flirc.tv_flirc-if01-event-kbd')

# make sure Raspbian doesn't consume the keyboard input before we do
flirc.grab()

# paired with my Roku remote to have the following outputs
# *** had to rework to use only "valid command" keys so could watch
# *** just one endpoint
#
#   Roku button                 Flirc output     moOde function
# "left arrow button"           -> "KEY_A"       => favorites
# "home button"                 -> "KEY_B"       => default playlist
# "up angle button"             -> "KEY_C"       => volume up
# "left-angle bracket button"   -> "KEY_D"       => previous
# "OK button"                   -> "KEY_E"       => mute/unmute
# "right angle bracket button"  -> "KEY_F"       => next
# "down angle button"           -> "KEY_G"       => volume down
# "redo button"                 -> "KEY_H"       => nothing yet
# "asterisk button"             -> "KEY_I"       => nothing yet
# "double-left diamond button"  -> "KEY_J"       => nothing yet
### careful: flirc_util 3.22.4 says "k" is not a valid letter!!!
# "play/pause button"           -> "KEY_L"       => play/pause
# "double-right diamond button" -> "KEY_M"       => nothing yet

for event in flirc.read_loop():
    if event.type == evdev.ecodes.EV_KEY:
        attrib = evdev.categorize(event)
        if attrib.keystate == 1:
            if attrib.keycode == 'KEY_F':
                subprocess.run(['mpc','next'])
            elif attrib.keycode == 'KEY_D':
                subprocess.run(['mpc','prev'])
            elif attrib.keycode == 'KEY_C':
                subprocess.run(['/var/www/vol.sh','-up','10'])
            elif attrib.keycode == 'KEY_G':
                subprocess.run(['/var/www/vol.sh','-dn','10'])
            elif attrib.keycode == 'KEY_L':
                subprocess.run(['mpc','toggle'])
            elif attrib.keycode == 'KEY_A':
                subprocess.run(['mpc','clear'])
                time.sleep(0.1)
                subprocess.run(['mpc','load','Favorites'])
            elif attrib.keycode == 'KEY_B':
                subprocess.run(['mpc','clear'])
                time.sleep(0.1)
                subprocess.run(['mpc','load','Default Playlist'])
            elif attrib.keycode == 'KEY_E':
                subprocess.run(['/var/www/vol.sh','-mute'])



Easy peasy. As you can see, this is mostly a simple case statement, except Python doesn't have a case statement! There's alternatives to the cascading if/elif construction but they're too razzle-dazzle for me, at least at this stage. The ordering of the tests got scrambled because of my reworking from media-control keycodes. It doesn't matter programmatically but it looks odd and will get fixed before the HowTo appears. The script loops endlessly until you kill it explicitly, you unplug the Flirc, or you reboot moOde.

So, to try it out, you first need to program your Flirc on another host to generate the keycodes you want with the IR-remote you have (see flirc.tv for software and docs). If you choose a different scheme from mine you'll need to do your homework on the keycodes actually emitted (hint: they're not in the Flirc docs).

For moOde, you need Raspbian Buster for its up-to-date Python3 and evdev module. This means Tim's moOde 5.4b2 soon-to-be moOde 6.

Install evdev

Code:
sudo apt-get install python3-evdev


Plug in the Flirc on your moOde player. Copy the script to somewhere convenient like pi's home directory, calling it what you want; change the key values to match your Flirc configration; and make the script executable (chmod +x) for convenience. Since I don't yet have a systemd/udev solution, you'll have to start the script manually or add it to moOde's startup activity (not recommended but if you never remove the Flirc this would work). For mine, this means:

Code:
./Flirc-moode.py


Start pressing buttons and watch moOde responds. Note that you'll also start seeing the output from mpc commands since you've invoked the script from the command line. That'll go away with systemd/udev. You can play with redirecting output to the bit bucket if desired.

Final thought:

Almost exactly the same script works with my Satechi Bluetooth Multi-Media Remote. Unfortunately, the systemd/udev work is harder here because of the intermediary Bluetooth controller and because the Satechi goes to sleep after a period of inactivity. More to come.

Regards,
Kent