07-12-2019, 06:34 PM
(This post was last modified: 07-18-2019, 03:00 PM by TheOldPresbyope.
Edit Reason: revisions: see headnotes for summary
)
[Revision 2 - add -M option to 'at' so it never try to send email to user]
[Revision 1 - add missing package, make scripts executable, change variable name in script, remind to reboot, add postscript]
The Satechi Bluetooth Multi-Media Remote is billed by Satechi as "the ultimate multi-media companion for your Bluetooth iOS device." (https://satechi.net/products/satechi-blu...dia-remote)
Irrespective of their marketing hype, it's a general-purpose BT remote which can be connected up to control a number of moOde playback functions. Its only shortcoming to my way of thinking is its price---as befits an "iOS" accessory, it costs as much as an RPi3B+---but I originally bought mine for a different purpose, so sunk cost and all that.
There are eight buttons on the device which I've mapped to play/pause, previous track and next track, volume up and volume down, mute/unmute, load Favorites, and load Default Playlist.
Prerequisites:
Installation:
[The following assumes a moderate level of Linux literacy, such as recognizing I'm using the cat command to list the contents of files rather than showing the steps to create the files, knowing how to mark files executable, etc. If there's a demand, I'll pull together a build script which does it all.]
First, install the Python evdev package and the "at" package
Three additional files are needed. A udev rules file, a one-line utility startup shell script, and a small Python script.
The rules file goes into /etc/udev/rules.d
[Note added in post-edit: This forum's formatting software folded the long lines, making it appear this file has four lines. In actuality, there are only two lines, each beginning "ACTION".]
Why 42? If you know the Hitchhiker's Guide to the Galaxy you needn't ask.
The utility startup shell script and the Python script both go into /usr/local/bin (Linux-heads know this is not the only place they could go, but the rules file and shell script have to be adjusted accordingly). Make them executable, of course.
The utility startup shell script is simply
The Python script is
As I mentioned in my earlier post about the Flirc USB, these cascading if/elif statements are basically just one big case statement, Python style.
If one wanted to use a different Bluetooth remote, one would have to determine the keycodes being output and adjust the mappings in the Python script accordingly. (With luck, all multi-media remotes emit the same basic set of keycodes but ya never know.) As well, one would have to determine the specifics of the new remote's BT attributes and adjust the rules file accordingly.
Now reboot to set the machinery in motion. (Linux pros know it's only necessary to force udev to reload the rules but let's keep it simple.)
Final thought
Read my comments and note in the Python script. A shortcoming of the Satechi going to sleep is that it takes a moment for everything to start up on an initial button press. The first press or two get lost in the shuffle, so to speak, but within a few seconds moOde is responsive to the remote. I haven't thought of a fix. I don't believe this hiatus can be blamed on Python compiling the script into its intermediary form on each invocation but someday I'll get around to testing this conjecture. Meanwhile, it's nice having a remote which doesn't need to be pointed at a sensor.
Regards,
Kent
Postscript: By default, bluetoothctl requires the entry of a PIN from the Satechi remote during pairing. Handily, the Satechi has a pull-down cover revealing a miniature number keypad for doing exactly that! I seem to recall there's a way to bypass this challenge-response scheme---it may even have been discussed in another thread last year---but I didn't try.
[Revision 1 - add missing package, make scripts executable, change variable name in script, remind to reboot, add postscript]
The Satechi Bluetooth Multi-Media Remote is billed by Satechi as "the ultimate multi-media companion for your Bluetooth iOS device." (https://satechi.net/products/satechi-blu...dia-remote)
Irrespective of their marketing hype, it's a general-purpose BT remote which can be connected up to control a number of moOde playback functions. Its only shortcoming to my way of thinking is its price---as befits an "iOS" accessory, it costs as much as an RPi3B+---but I originally bought mine for a different purpose, so sunk cost and all that.
There are eight buttons on the device which I've mapped to play/pause, previous track and next track, volume up and volume down, mute/unmute, load Favorites, and load Default Playlist.
Prerequisites:
- Raspbian Buster (for up-to-date Python3 and Python evdev module)
- moOde Bluetooth controller present and enabled (I'm developing on an RPi3B+)
- The Satechi remote paired with, connected to, and trusted by the moOde Bluetooth subsystem (using bluetoothctl). [See postscript] This need be done only once unless/until the moOde BT controller is reset. Rebooting is not an issue in this context.
Installation:
[The following assumes a moderate level of Linux literacy, such as recognizing I'm using the cat command to list the contents of files rather than showing the steps to create the files, knowing how to mark files executable, etc. If there's a demand, I'll pull together a build script which does it all.]
First, install the Python evdev package and the "at" package
Code:
sudo apt-get install python3-evdev at
Three additional files are needed. A udev rules file, a one-line utility startup shell script, and a small Python script.
- Contrary to what I conjectured in an earlier post about the Flirc USB, the udev rules turned out to be easy even though the Satechi goes to sleep after a period of inactivity. Using the "at now" construction in an intermediary startup shell script allows the invoked Python script to continue running after udev finishes and kills the startup script. This allowed me to avoid using systemd (although using systemd is considered the "right" approach by the purists).
The rules file goes into /etc/udev/rules.d
Code:
pi@moode54b2:~ $ ls -l /etc/udev/rules.d/42-satechi.rules
-rw-r--r-- 1 root root 316 Jul 12 13:10 /etc/udev/rules.d/42-satechi.rules
pi@moode54b2:~ $ cat /etc/udev/rules.d/42-satechi.rules
ACTION=="add", KERNEL=="event*", SUBSYSTEM=="input", ATTRS{name}=="Bluetooth Media Control & Camera Shutter Click Consumer Control", SYMLINK+="SBMMR"
ACTION=="add", KERNEL=="event*", SUBSYSTEM=="input", ATTRS{name}=="Bluetooth Media Control & Camera Shutter Click Consumer Control", RUN+="/usr/local/bin/satechi.sh"
[Note added in post-edit: This forum's formatting software folded the long lines, making it appear this file has four lines. In actuality, there are only two lines, each beginning "ACTION".]
Why 42? If you know the Hitchhiker's Guide to the Galaxy you needn't ask.
The utility startup shell script and the Python script both go into /usr/local/bin (Linux-heads know this is not the only place they could go, but the rules file and shell script have to be adjusted accordingly). Make them executable, of course.
Code:
pi@moode54b2:~ $ ls -l /usr/local/bin/sa*
-rwxr-xr-x 1 root root 3275 Jul 12 12:58 /usr/local/bin/satechi.py
-rwxr-xr-x 1 root root 50 Jul 12 12:58 /usr/local/bin/satechi.sh
The utility startup shell script is simply
Code:
pi@moode54b2:~ $ cat /usr/local/bin/satechi.sh
#!/bin/sh
echo /usr/local/bin/satechi.py | at -M now
The Python script is
Code:
pi@moode54b2:~ $ cat /usr/local/bin/satechi.py
#!/usr/bin/python3
import evdev,subprocess,time
# Hackery by TheOldPresbyope, 20190712
# - Control some moOde playback functions using a Satechi Bluetooth Multi-Media Remote
# Prerequisites:
# - Raspbian Buster (for up-to-date Python3 and Python evdev module)
# - moOde Bluetooth controller present and enabled (I'm developing on an RPi3B+)
# - The Satechi remote is paired with, connected to, and trusted by the
# moOde Bluetooth subsystem (using bluetoothctl). This need be done
# only once unless/until the moOde BT controller is reset.
# Basics:
# - Initially, the Satechi remote is asleep.
# - A companion udev rule is triggered when the Satechi is awakened by a
# button press and the signal is detected by moOde's BT controller;
# the rule creates a symlink /dev/SBMMR for convenience and
# invokes a companion helper script which in turn starts this script
# - When the Satechi goes back to sleep after a period of inactivity, udev
# removes the symlink and this script dies (error ends, actually) when
# it can no longer find it.
# - The indirect methon used to invoke this Python script allows it to
# run forever as long as the Satechi is awake, or until the
# script is killed either by the root user or by rebooting.
# NOTE: it seems to take some seconds after an initial button-press before
# all the machinery is working and moOde starts responding.
# Not sure why. Sadly, the first press or two is lost in the shuffle.
# Maybe there's a fix, maybe not.
# Revision 1: change object-instance name to satechi for clarity
satechi=evdev.InputDevice('/dev/SBMMR')
# Make sure Raspbian doesn't consume the inputs before we do
satechi.grab()
# Loop forever looking for key-down events from the Satechi, mapping the
# resulting keycodes into moOde operations.
# The keycodes being transmitted were determined through testing.
# The keycodes are burned into the Satechi microcode and can't be changed.
# Mostly the button-icons are self-explanatory. The two which aren't are a
# a button whose icon looks like an open rectangle, whose keycode I mapped
# to "load Favorites" and a button whose icon looks vaguely like a
# keyboard whose keycode I mapped to "load Default Playlist".
for event in satechi.read_loop():
if event.type == evdev.ecodes.EV_KEY:
attrib = evdev.categorize(event)
if attrib.keystate == 1:
if attrib.keycode == 'KEY_NEXTSONG':
subprocess.run(['mpc','next'])
elif attrib.keycode == 'KEY_PREVIOUSSONG':
subprocess.run(['mpc','prev'])
elif attrib.keycode == 'KEY_VOLUMEUP':
subprocess.run(['/var/www/vol.sh','-up','10'])
elif attrib.keycode == 'KEY_VOLUMEDOWN':
subprocess.run(['/var/www/vol.sh','-dn','10'])
elif attrib.keycode == 'KEY_PLAYPAUSE':
subprocess.run(['mpc','toggle'])
elif attrib.keycode == 'KEY_HOMEPAGE':
subprocess.run(['mpc','clear'])
time.sleep(0.1)
subprocess.run(['mpc','load','Default Playlist'])
elif attrib.keycode == 'KEY_EJECTCD':
subprocess.run(['mpc','clear'])
time.sleep(0.1)
subprocess.run(['mpc','load', 'Favorites'])
# careful: the Satechi returns two keycodes in a Python list
# when the mute button is pressed
elif 'KEY_MUTE' in attrib.keycode:
subprocess.run(['/var/www/vol.sh','-mute'])
As I mentioned in my earlier post about the Flirc USB, these cascading if/elif statements are basically just one big case statement, Python style.
If one wanted to use a different Bluetooth remote, one would have to determine the keycodes being output and adjust the mappings in the Python script accordingly. (With luck, all multi-media remotes emit the same basic set of keycodes but ya never know.) As well, one would have to determine the specifics of the new remote's BT attributes and adjust the rules file accordingly.
Now reboot to set the machinery in motion. (Linux pros know it's only necessary to force udev to reload the rules but let's keep it simple.)
Final thought
Read my comments and note in the Python script. A shortcoming of the Satechi going to sleep is that it takes a moment for everything to start up on an initial button press. The first press or two get lost in the shuffle, so to speak, but within a few seconds moOde is responsive to the remote. I haven't thought of a fix. I don't believe this hiatus can be blamed on Python compiling the script into its intermediary form on each invocation but someday I'll get around to testing this conjecture. Meanwhile, it's nice having a remote which doesn't need to be pointed at a sensor.
Regards,
Kent
Postscript: By default, bluetoothctl requires the entry of a PIN from the Satechi remote during pairing. Handily, the Satechi has a pull-down cover revealing a miniature number keypad for doing exactly that! I seem to recall there's a way to bypass this challenge-response scheme---it may even have been discussed in another thread last year---but I didn't try.