[JP2601] Toggle flashlight with double Volume Button press (sf-button-monitor)

This tutorial was put together with the help of Claude (Anthropic’s AI); the commands were tested successfully on my own JP2601, but I make no guarantees for other setups — follow at your own risk, and always double-check what a command does before running it as root.

Tested on the JP2601, recent Sailfish OS build. Uses sf-button-monitor to toggle the flashlight with a double-press of Volume Down while the screen is locked.

Note: this is a double-press, not a true long-press — the tool detects button sequences, not hold duration.

Requirements

  • Developer mode + SSH, or root over local terminal

  • perl and dbus-send (present by default)

1. Get and install the script

git clone https://github.com/teleshoes/sf-button-monitor
cd sf-button-monitor
cp src/sf-button-monitor /usr/local/bin/
chmod +x /usr/local/bin/sf-button-monitor

(Ignore install.sh, it uses sudo which doesn’t exist on Sailfish.)

2. Add JP2601 to the device table

The script picks input devices from a hardcoded table keyed on ssu mo output, and JP2601 isn’t in it by default. Edit the script:

nano src/sf-button-monitor

Add this line inside the $INPUT_DEVICES_BY_MODEL_NAME block:

perl

  jp2601 => ['/dev/input/event0', '/dev/input/event2'],

(On JP2601: Volume Down = event0, Volume Up = event2.)

Reinstall:

cp src/sf-button-monitor /usr/local/bin/sf-button-monitor

3. Config file

Find your defaultuser UID (should be 100000 on stock setups — confirm with id defaultuser):

mkdir -p /root/.config
nano /root/.config/sf-button-monitor.conf
action=cmd(env DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/100000/dbus/user_bus_socket dbus-send --print-reply --dest=org.sailfish.flashlight.provider /org/sailfish/flashlight/provider org.sailfish.flashlight.provider.toggleFlashlight),vd vd,screenLocked

If you prefer Volume + button :

action=cmd(env DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/100000/dbus/user_bus_socket dbus-send --print-reply --dest=org.sailfish.flashlight.provider /org/sailfish/flashlight/provider org.sailfish.flashlight.provider.toggleFlashlight),vu vu,screenLocked

On JP2601, the flashlight D-Bus service is org.sailfish.flashlight.provider (not the older com.jolla.settings.system.flashlight some guides reference) — this is on defaultuser’s session bus, hence the explicit DBUS_SESSION_BUS_ADDRESS.

Quick test in foreground:

sf-button-monitor

Lock the screen, double-press Volume Down, confirm the flashlight toggles, Ctrl+C.

4. systemd service

nano /etc/systemd/system/sf-button-monitor.service

ini

[Unit]
Description=Sailfish Button Monitor (volume/camera hardware button actions)
After=user@100000.service
Wants=user@100000.service

[Service]
Type=simple
Environment=HOME=/root
Environment=DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/100000/dbus/user_bus_socket
ExecStartPre=/bin/sh -c 'while [ ! -S /run/user/100000/dbus/user_bus_socket ]; do sleep 1; done'
ExecStart=/usr/local/bin/sf-button-monitor
Restart=on-failure
RestartSec=3

[Install]
WantedBy=multi-user.target
systemctl daemon-reload
systemctl enable --now sf-button-monitor.service

Two things this fixes that will otherwise break it silently on JP2601:

  • $HOME isn’t set under systemd, so the script can’t find its own config and falls back to a broken default — hence Environment=HOME=/root.

  • On boot, the service can start before defaultuser’s session D-Bus socket exists, breaking the internal MPRIS listener — hence the ExecStartPre wait loop.

Reboot and test without launching anything manually:

reboot

Lock screen, double-press Volume Down. Should just work.

Debugging

journalctl -u sf-button-monitor.service -f

watch this while testing if something’s off.

5 Likes

Awesome, I can’t wait to receive my JP2601 and try that. Just a quick question: any particular reason to choose perl? I personally enjoy perl, but I would expect most people today would choose python.

I didn’t write sf-button-monitor myself, I just used it as-is. It’s teleshoes’s tool, originally written for the Nokia N9 (n9-button-monitor) years ago and later ported to Sailfish. I only adapted the device table and D-Bus calls for the JP2601, didn’t touch the core logic.

Why is HOME=/root and why store the config there instead of the user’s home?

Why use the dbus wait loop instead of proper systemd unit dependencies?

1 Like

I have no idea; I just experimented with the AI ​​until it worked. I’m not a developer, just a tinkerer who was missing this feature. If you have a cleaner solution to suggest, I’d be happy to update the post.

And here you have a perfect example for why AI in coding increases the overall workload instead of decreasing it.

3 Likes

Don’t get me wrong, I think it’s a very neat feature you publish here.

But I also think understanding why something was done a certain way, and how it achieves what is done is not only crucial, but a minimum requirement before publishing AI generated stuff.

7 Likes

Thanks, that’s really productive. If you don’t like it, don’t use it. I’m just a tinkerer—it works, and I have no doubt it’s far from optimal, but the idea was just to open a door, and your sarcasm is pretty discouraging. That said, if you have any constructive suggestions, please don’t hesitate to share them.

1 Like

I don’t really know Linux filesystems well, I don’t speak English very well, and I don’t even know what D-Bus or systemd are. I just tried to tinker with it, and if it’s not optimal, I’m very open to advice and explanations—I’ll gladly update the forum post to make it as good as possible.

But clearly, a lot of the best devs on SFOS have jumped ship, and I know very well that I don’t have the level to do this kind of modification properly. I just wanted to share this little trick hoping it might be useful, suspecting there were probably better ways to do it.

Since you clearly know your stuff, if you’re willing, I’d really appreciate it if you could tell me what isn’t optimal and how to change it in the tutorial to make it better!

1 Like