Files
2026-05-03 16:04:32 +02:00
..
2026-04-30 00:57:24 +02:00
2026-04-30 00:57:24 +02:00
2026-04-30 00:57:24 +02:00
2026-04-30 00:57:24 +02:00
2026-04-30 00:57:24 +02:00
2026-04-30 00:57:24 +02:00
2026-04-30 00:57:24 +02:00
2026-04-30 00:57:24 +02:00
2026-04-30 00:57:24 +02:00
2026-04-30 00:57:24 +02:00
2026-04-30 00:57:24 +02:00
2026-04-30 00:57:24 +02:00
2026-04-30 00:57:24 +02:00
2026-04-30 00:57:24 +02:00
2026-04-30 00:57:24 +02:00
2026-04-30 00:57:24 +02:00
2026-04-30 00:57:24 +02:00

BLE Unlock

A GNOME Shell 49 extension that locks and unlocks your session based on the Bluetooth RSSI of a nearby device. When your phone (or any BT device) moves out of range the screen locks; when it comes back in range the screen unlocks after a short delay.

Requirements

  • GNOME Shell 49
  • BlueZ (the Linux Bluetooth stack)
  • Node.js and npm (for building)

Build & Install

cd generated/
npm install          # install TypeScript + type stubs
make                 # compile TypeScript → dist/
make install         # pack extension + gnome-extensions install --force

Reload GNOME Shell after installing, then enable the extension:

gnome-extensions enable ble-unlock@blelock.github.io

Running Tests

make test            # run all vitest tests
npm run test:unit    # unit tests only (thresholds pure logic)
npm run test:integration  # integration tests (state-machine)

Nested-Shell Testing (GNOME 49)

Start a nested GNOME Shell session to test the extension live without risking your main session:

dbus-run-session gnome-shell --devkit --wayland

Inside the nested session, enable the extension:

gnome-extensions enable ble-unlock@blelock.github.io

Run the GJS/Jasmine integration test inside the nested session:

gjs tests/gnome-shell/extension.test.js

GSettings Keys

Key Type Default Description
enabled boolean false Whether BLE monitoring is active
device-address string "" MAC address of the BT device to monitor
rssi-lock-threshold int -75 Screen locks when RSSI falls below this value (dBm)
rssi-unlock-threshold int -65 Screen unlocks when RSSI rises above this value (dBm)
poll-interval int 3 How often to read the BLE RSSI (seconds)

Read or write keys with gsettings:

gsettings set org.gnome.shell.extensions.bleunlock device-address 'AA:BB:CC:DD:EE:FF'
gsettings set org.gnome.shell.extensions.bleunlock enabled true

Architecture

generated/
├── thresholds.ts          # Pure logic: classifySignal / shouldLock / shouldUnlock
├── bleMonitor.ts          # GObject that polls BlueZ RSSI via Gio.DBusProxy
├── quickSettingsMenu.ts   # Quick Settings toggle + device list submenu
├── extension.ts           # Extension enable/disable; wires monitor → screen lock
├── prefs.ts               # GTK4/Adwaita preferences window (separate process)
└── schemas/               # GSettings schema (5 keys)

Two-process boundary:

  • Shell process (extension.ts, bleMonitor.ts, quickSettingsMenu.ts): GI libs only — Clutter, St, Meta, Gio, GLib, GObject. No Gtk/Adw/Gdk.
  • Prefs process (prefs.ts): GTK4 + Adwaita + Gio/GLib only. No Clutter/Meta/St/Shell.

Data flow:

  1. BleMonitor polls org.bluez.Device1.RSSI (cached D-Bus property) every N seconds.
  2. Each poll feeds into thresholds.ts pure functions to detect state transitions.
  3. On transition, BleMonitor.onSignalBelowThreshold() or onSignalAboveThreshold() fires.
  4. extension.ts wires those hooks to org.gnome.ScreenSaver.SetActive.
  5. Unlock is delayed 3 seconds to avoid spurious unlocks on brief signal loss.

Debugging

Watch live extension logs:

journalctl -f -o cat /usr/bin/gnome-shell | grep BleUnlock

Inspect GSettings values:

gsettings list-recursively org.gnome.shell.extensions.bleunlock

Check BlueZ device RSSI manually:

dbus-send --system --print-reply \
  --dest=org.bluez /org/bluez/hci0/dev_AA_BB_CC_DD_EE_FF \
  org.freedesktop.DBus.Properties.Get \
  string:org.bluez.Device1 string:RSSI