The L14 exposes a number LEDs that can be controlled by software,
via the Librem embedded controller (EC) driver.
The L14 Rave blog post provides an excellent high-level overview of these LEDs.
Since the EC driver comes preinstalled only in Purism’s own Pure OS distribution,
as of now, it must be manually installed in Qubes.
Installing the L14 EC driver in

Typically the
So, since a VM that needs to trigger these LEDs won’t have direct access to the hardware,
it must access them via
I will be explaining a particular RPC
for controlling the wireless (WiFi/Bluetooth) LED, /sys/class/leds/librem_ec:airplane
.
The techniques for controlling the notification LED are very similar,
but the RGB channels for it are exposed separately as
/sys/class/leds/{blue,green,red}:status
.
As opposed to librem_ec:airplane
, which accepts a binary value,
these RGB channels accept decimal values between 0
and 255
.
ADVANCED QUBES / LINUX STUFF
DO NOT copy-paste any code or shell commands from the internet, unless you understand exactly what they do. You may compromise the security of your system, or damage it otherwise.
Changes in
There are two main changes necessary in
(a) a new Qrexec service to listen to LED trigger requests from AppVMs,
(b) a Qrexec policy to restrict which VMs may call this service.
The Qrexec Service
First, let’s start with the new Qrexec service that would handle requests
for triggering the librem_ec:airplane
LED.
The script itself is pretty straightforward, as I show below.
#!/usr/bin/env bash
# Exit immediately if any of the following commands fail
set -e
# Expect exactly one argument -- the service argument
[ "$#" -eq 1 ]
# Since this is an "airplane mode" indicator,
# we must invert the status code to set the brightness
if [ "$1" == "up" ]; then
echo 0 > /sys/class/leds/librem_ec\:airplane/brightness
elif [ "$1" == "down" ]; then
echo 1 > /sys/class/leds/librem_ec\:airplane/brightness
fi
The service accepts a single argument, which may either be up
or down
,
and sets the brightness of the the librem_ec:airplane
LED accordingly.
Note that, the EC uses the LED to indicate if the laptop is in “airplane mode,”
i.e. “on” when WiFi is disconnected and “off” when WiFi is connected.

But, that just didn’t make sense to me — when I physically turn off the hardware by flipping the switch next to it, then WiFi is disconnected but the LED stays “off,” as there is no power supply to it. Furthermore, the label over the LED makes it look (to me) more like a wireless connection indicator, rather than an airplane mode indicator.
So, I chose the LED to be “on” when WiFi is connected, and “off” otherwise,
and I set the brightness as the inverse of the service argument —
0
brightness for up
argument and 1
brightness for down
.
To make it available to Qrexec as a new service,
this script must exist under /etc/qubes-rpc
and must be marked as executable (chmod +x
).
I used /etc/qubes-rpc/custom.SetWiFiLED
, for instance.
We should be able to control the WiFi/Bluetooth LED now
by invoking this script in
/etc/qubes-rpc/custom.SetWiFiLED up
/etc/qubes-rpc/custom.SetWiFiLED down
The Qrexec Policy
For each Qrexec service,
we must also specify a Qrexec policy to whitelist calls from and to specific VMs.
In this case, we want to allow
sys-net dom0 allow
@anyvm @anyvm deny
Note that we used allow
and not ask
in the first line
to skip a
The policy file should exist under /etc/qubes-rpc/policy
,
and must have the same name as the service file.
I used /etc/qubes-rpc/policy/custom.SetWiFiLED
, for instance.
Changes in
Finally, in
qrexec-client-vm dom0 custom.SetWiFiLED+up
qrexec-client-vm dom0 custom.SetWiFiLED+down
Notice that in Qrexec, the name and the argument for a service are passed together,
delimited by a +
.
If the Qrexec calls execute without errors, and the WiFi/Bluetooth LED toggles as expected, then we may now automate this Qrexec call.
One way to trigger this Qrexec call on WiFi state change
is to use the NetworkManager-dispatcher service.
As an example, I have the following hook
in a new file /etc/NetworkManager/dispatcher.d/99-librem-wifi-led
:
#!/usr/bin/env bash
IF=$1
STATUS=$2
if [[ "$IF" =~ ^wls.* ]]; then
if [ "$STATUS" == "up" ] || [ "$STATUS" == "down" ] ; then
qrexec-client-vm dom0 custom.SetWiFiLED+$STATUS
fi
fi
The script essentially checks if the interface that has changed state
is a WiFi interface (prefixed with wls
in Fedora),
and then makes the Qrexec call with if the new state.
States other than up
and down
, such as pre-up
, dhcp4-change
etc.
are ignored.
The script should be effective immediately, without any restarts.