FieldStation42 can be controlled with any IR remote using a Flirc USB receiver, or with a keyboard directly. You can also change channels and read player status from external scripts using a simple socket file interface.
Quick Start with Flirc USB
Flirc USB lets you use any IR remote with your Raspberry Pi. It maps remote buttons to keyboard keys, which FieldStation42's remote controller script then translates into commands.
1. Get and Program Flirc
Pick up a Flirc USB receiver and install the Flirc software on your computer. Use the Flirc GUI to program your remote buttons to the following keyboard keys:
| Remote Button | Map to Key |
|---|---|
| Numbers 0-9 | 0 through 9 |
| Home / Guide | Home |
| Volume Up | Right Arrow |
| Volume Down | Left Arrow |
| Mute | m |
| Channel Up | Up Arrow |
| Channel Down | Down Arrow |
| Navigate Up | Page Up |
| Navigate Down | Page Down |
| Select / OK | Enter |
| Last Channel | Backspace |
| Subtitles On/Off | v |
| Subtitle Track | j |
| Audio Track | a |
| Power / Stop | End |
Then plug the Flirc receiver into your Raspberry Pi.
2. Install Dependencies
pip install evdev requests
3. Run the Remote Controller
python3 fs42/pi/remote_controller.py
On Linux Mint, Ubuntu, and some other distributions you need sudo to access input devices. When using sudo, make sure to point at the virtual environment's Python directly rather than the system Python:
sudo env/bin/python3 fs42/pi/remote_controller.py
Using sudo python3 instead will use the system Python and won't have access to the packages installed in your FieldStation42 environment.
Remote Functions Reference
| Function | Default Key | Description |
|---|---|---|
| Numbers 0-9 | 0-9 |
Channel selection |
| Show Guide | Home |
Display program guide |
| Volume Up | Right Arrow |
Increase volume by 2% |
| Volume Down | Left Arrow |
Decrease volume by 2% |
| Mute | m |
Mute or unmute volume |
| Channel Up | Up Arrow |
Next channel |
| Channel Down | Down Arrow |
Previous channel |
| Navigation Up | Page Up |
Next item on web channels such as PPV |
| Navigation Down | Page Down |
Previous item on web channels |
| Confirm | Enter |
Select the current item on web channels |
| Last Channel | Backspace |
Jump back to the previously watched channel |
| Toggle Subtitles | v |
Show or hide subtitles |
| Cycle Subtitles | j |
Step through the available subtitle tracks |
| Cycle Audio | a |
Step through the available audio tracks |
| Power/Stop | End |
Toggle the FieldStation42 services on and off |
| Exit | Esc |
Exit remote controller |
Channel Selection
- Press
1, wait one second: switch to channel 1 - Press
1,2, wait one second: switch to channel 12 - Press
1,2,3: immediately switch to channel 123
If the station you are on has parental controls and is waiting for its PIN, the number keys enter PIN digits instead of selecting a channel. Channel up/down, the guide button, and the last-channel button still work, so you can leave the station without entering the PIN.
Web Channel Navigation
The navigation and confirm keys control web channels such as PPV. The remote controller sends them through the FieldStation42 server to the web page on screen, so they work the same way as the nav pad on the web remote. On ordinary channels they do nothing.
Enter does two jobs. While you are typing a channel number, it tunes to that channel straight away instead of waiting. Otherwise it is sent to the web channel as confirm.
Subtitles and Audio Tracks
The subtitle and audio keys act on whatever is playing right now. j steps through each subtitle track in the current file and then back to off, while v hides and restores subtitles without changing which track is selected. a steps through the audio tracks, which is useful for media carrying alternate language dubs or commentary.
Your track choice sticks for the rest of the program. The player reapplies it each time the program resumes after a commercial break, and clears it when the next program starts. Only choices made while the program itself is on screen are remembered - pressing these keys during a commercial or bump affects just that clip and is not carried forward. Nothing is written to disk, so selections are lost when the player restarts.
Most media has a single audio track and no subtitles at all - on those files, pressing these keys will not appear to do anything because there is nothing to cycle to.
The same subtitle and audio cycling is available on the web remote.
The Power Button
By default the power key does not simply stop the player. USE_SYSTEMCTL is enabled at the top of remote_controller.py, so pressing it checks whether fs42.service is running and then starts or stops every service listed in SYSTEMCTL_TO_TOGGLE - the player, the cable box and the OSD. That gives a real power button that brings the whole system up and down.
If you would rather have the key only stop the player, set use_systemctl to false in the settings file below and it will call the player's stop command instead. The list of services it toggles can be changed there too.
The Settings File
Create runtime/remote_controller.json to change how the remote controller behaves without editing the script. The file is optional - when it is missing, the built-in defaults are used.
{
"use_systemctl": true,
"systemctl_to_toggle": [
"fs42.service",
"fs42-cable-box.service",
"fs42-osd.service"
],
"key_mappings": {
"volume_up": "equal",
"volume_down": "minus"
}
}
| Setting | Type | Description |
|---|---|---|
use_systemctl |
boolean | Whether the power key toggles services instead of only stopping the player |
systemctl_to_toggle |
list | Service names the power key starts and stops |
key_mappings |
object | Function names mapped to key names - see Customizing Key Mappings below |
Every setting is optional and anything you leave out keeps its default, so a file containing nothing but use_systemctl is perfectly valid. Note that JSON booleans are lowercase true and false, not Python's True and False.
The file is read once when the remote controller starts, so restart it after making changes. If it cannot be read - a stray comma, a missing brace - the remote controller reports the error and carries on with the defaults rather than refusing to start.
Customizing Key Mappings
You don't need Flirc to use the remote controller. Any keyboard works. Add a key_mappings block to runtime/remote_controller.json listing only the functions you want to change:
{
"key_mappings": {
"volume_up": "equal",
"volume_down": "minus"
}
}
Everything you leave out keeps its default key. The example above moves volume onto the = and - keys while the guide stays on Home, mute stays on m, and the rest are untouched.
These are the function names and the defaults you are overriding:
{
"key_mappings": {
"show_guide": "home",
"volume_up": "right",
"volume_down": "left",
"channel_up": "up",
"channel_down": "down",
"navigation_up": "pageup",
"navigation_down": "pagedown",
"confirm": "enter",
"last_channel": "backspace",
"mute": "m",
"toggle_subtitles": "v",
"cycle_subtitles": "j",
"cycle_audio": "a",
"power_stop": "end",
"exit": "esc"
}
}
The number keys are always handled as channel entry and are not part of key_mappings.
Editing DEFAULT_KEY_MAPPINGS at the top of remote_controller.py still works, but the settings file is the better place - it survives updates to the code.
Taking a Key From Another Function
If you assign a key that another function holds by default, that function gives the key up rather than competing for it. Mapping channel_up to right means the right arrow changes channels, and volume up is left with no key at all:
{
"key_mappings": {
"channel_up": "right"
}
}
When you are rearranging keys this way, map the displaced function somewhere too:
{
"key_mappings": {
"channel_up": "right",
"volume_up": "equal"
}
}
Available Key Names
Any key your device sends can be mapped. Key names are the Linux key names in lowercase without the KEY_ prefix, so KEY_PAGEUP is pageup and KEY_KPPLUS is kpplus. The most common ones:
- Letters:
athroughz - Numbers:
0through9 - Function keys:
f1throughf24 - Arrows:
up,down,left,right - Navigation:
home,end,pageup,pagedown,insert,delete - Common:
space,enter,tab,backspace,esc - Punctuation:
minus,equal,comma,dot,slash,semicolon,apostrophe,grave,leftbrace,rightbrace,backslash - Keypad:
kp0throughkp9,kpplus,kpminus,kpasterisk,kpslash,kpdot(the keypad Enter key is treated asenter) - Modifiers:
leftshift,rightshift,leftctrl,rightctrl,leftalt,rightalt,leftmeta,rightmeta
To find the name of a particular key, run the remote controller and press it. Every key press is printed along with its name:
DEBUG: Key pressed: kpplus (code: 78)
A key without a Linux key name is reported by its code number instead, and that number can be used as the key name, for example "mute": "250".
Selecting an Input Device
By default the remote controller auto-detects your Flirc device, or falls back to the first available input device. If it picks the wrong one, or you want to be explicit, start by listing what's available:
python3 fs42/pi/remote_controller.py --list-devices
On systems that require sudo for input device access:
sudo env/bin/python3 fs42/pi/remote_controller.py --list-devices
You'll see output like this:
Available input devices:
0: Flirc (/dev/input/event4)
1: Logitech M510 (/dev/input/event3)
2: Power Button (/dev/input/event1)
Using default device: Flirc
Then use -d to specify which device to use, by index, name pattern, or full path:
# By index
python3 fs42/pi/remote_controller.py -d 0
# By name pattern (matches first device containing that text)
python3 fs42/pi/remote_controller.py -d flirc
python3 fs42/pi/remote_controller.py -d keyboard
# By full device path
python3 fs42/pi/remote_controller.py -d /dev/input/event4
You can also set the device via environment variable instead of passing -d every time:
FS42_INPUT_DEVICE=flirc python3 fs42/pi/remote_controller.py
Listening to All Devices
Use -d all to listen to every keyboard and remote at once, for example a Flirc receiver and a wireless keyboard together:
python3 fs42/pi/remote_controller.py -d all
Devices plugged in after the remote controller starts are picked up automatically, and devices that are unplugged are dropped. Devices the remote controller does not have permission to open are skipped.
In this mode the remote controller takes exclusive control of each device it listens to, so other programs, including the desktop and console, will not see key presses from those devices while it is running.
If you run the remote controller as a service, set the device through the FS42_INPUT_DEVICE environment variable. Run:
systemctl --user edit fs42-remote-controller.service
Add these lines in the editable area at the top of the file, then save and exit:
[Service]
Environment=FS42_INPUT_DEVICE=all
Then restart the service:
systemctl --user restart fs42-remote-controller.service
The same approach works for any other device setting, such as FS42_INPUT_DEVICE=flirc.
Server Location
If FieldStation42 is running on a different machine, set the host and port:
FS42_HOST=192.168.1.100 FS42_PORT=8080 python3 remote_controller.py
Or edit the values directly at the top of remote_controller.py.
If API security is on, the remote controller reads the key from confs/main_config.json automatically. When it runs on a different machine, pass the key as well:
FS42_HOST=192.168.1.100 FS42_API_KEY=YOUR_KEY python3 remote_controller.py
Button Callbacks
You can run a script or shell command whenever a button is pressed. Create runtime/remote_callback_map.json:
{
"up": "echo Up pressed",
"end": "path/to/power_pressed.sh"
}
Any valid shell command works here, including Python scripts.
The keys in this file are key names, not function names - use end rather than power_stop, and backspace rather than last_channel. A callback runs in addition to the normal function for that key, so mapping end still toggles the services as usual. The file is read once when the remote controller starts, so restart it after making changes.
Troubleshooting
Permission denied: Run with sudo env/bin/python3 to access input devices. Using sudo python3 will use the system Python instead of the virtual environment.
No devices found: Check that Flirc is plugged in and verify with ls /dev/input/event*. Run sudo evtest to see what keys your device actually sends.
Remote not responding: Verify FieldStation42 is running, check the host and port settings, and use the Flirc GUI to confirm key mappings are set correctly. Use the "Full Keyboard" controller template in the Flirc GUI for best results.
Changing Channels from External Scripts
FieldStation42 monitors runtime/channel.socket for JSON-formatted channel commands. This lets any external script or program change the channel without going through the remote controller.
Tune to a specific channel:
{"command": "direct", "channel": 42}
Tune up one channel:
{"command": "up", "channel": -1}
Tune down one channel:
{"command": "down", "channel": -1}
Example: Python Script
import json
command = {"command": "direct", "channel": 42}
with open('runtime/channel.socket', 'w') as fp:
fp.write(json.dumps(command))
Reading Player Status
The player publishes its current status to runtime/play_status.socket as JSON. External scripts can read this to know what is currently playing.
Player is active:
{"status": "playing", "network_name": "MyStation", "channel_number": 42, "timestamp": "2025-02-03T08:13:46.431558", "awaiting_pin": false, "pin_digits_entered": 0}
Player is stopped:
{"status": "stopped", "network_name": "", "channel_number": -1, "timestamp": "2025-02-03T08:13:46.431558"}