From af99b4960a9bf1043234354d685ed23818c14aaa Mon Sep 17 00:00:00 2001 From: Alexander Kassouni Date: Sat, 29 Aug 2026 23:53:40 -0700 Subject: new laptop --- users/kassouni/palette/bluetooth.nix | 80 ++++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100644 users/kassouni/palette/bluetooth.nix (limited to 'users/kassouni/palette/bluetooth.nix') diff --git a/users/kassouni/palette/bluetooth.nix b/users/kassouni/palette/bluetooth.nix new file mode 100644 index 0000000..58cdf2f --- /dev/null +++ b/users/kassouni/palette/bluetooth.nix @@ -0,0 +1,80 @@ +{ pkgs, config, ...}: +{ + + kassouni.palette.commands = [ + { + name = "toggle bluetooth device"; + icon = "󰂯"; + # `jc --bluetoothctl` turns `bluetoothctl devices` into JSON, so the menu is + # built from parsed records rather than by splitting "Device " + # lines. That subcommand leaves every status field empty, so connectedness + # comes from a second `devices Connected` call intersected on address. + exe = pkgs.writers.writePython3 "bluetooth-toggle" { + # The interpolated store paths below are longer than 79 columns. + flakeIgnore = [ "E501" ]; + } '' + import html + import json + import subprocess + import sys + + BT = "${pkgs.bluez}/bin/bluetoothctl" + JC = "${pkgs.jc}/bin/jc" + ROFI = "${pkgs.rofi}/bin/rofi" + + + def devices(*args): + """Parse `bluetoothctl devices [args]` into a list of records.""" + listed = subprocess.run( + [BT, "devices", *args], capture_output=True, text=True + ) + parsed = subprocess.run( + [JC, "--bluetoothctl"], + input=listed.stdout, + capture_output=True, + text=True, + ) + return json.loads(parsed.stdout or "[]") + + + def label(device, connected): + """Pango markup row. Names are escaped, they are not our text.""" + name = html.escape(device["name"]) + if device["address"] in connected: + name += ' connected' + return name + + + def main(): + known = devices() + if not known: + return 0 + + # Older bluez has no `devices Connected`; that call just comes back + # empty there, which degrades to "nothing is connected". + connected = {d["address"] for d in devices("Connected")} + + rows = [label(d, connected) for d in known] + choice = subprocess.run( + [ROFI, "-dmenu", "-i", "-markup-rows", "-format", "i", + "-p", "bluetooth"], + input="\n".join(rows), + capture_output=True, + text=True, + ) + if choice.returncode != 0: + return 1 + + device = known[int(choice.stdout.strip())] + action = ( + "disconnect" if device["address"] in connected else "connect" + ) + return subprocess.run([BT, action, device["address"]]).returncode + + + if __name__ == "__main__": + sys.exit(main()) + ''; + } + ]; +} -- cgit v1.3.1