main dotfiles / dot_config / sway / figbar_status.py
Eric Bower  ·  2026-10-08
  1#!/usr/bin/env python3
  2import json
  3import os
  4import subprocess
  5import sys
  6import time
  7
  8def get_sound():
  9    try:
 10        muted = subprocess.check_output(["pamixer", "--get-mute"], text=True, stderr=subprocess.DEVNULL).strip() == "true"
 11        vol = subprocess.check_output(["pamixer", "--get-volume"], text=True, stderr=subprocess.DEVNULL).strip()
 12        label = "MUTED" if muted else "VOL"
 13        return f"[[vol]]{label} {vol}%"
 14    except Exception:
 15        return "[[vol]]VOL ?"
 16
 17def get_battery():
 18    if not os.path.exists("/sys/class/power_supply"):
 19        return None
 20
 21    try:
 22        for bat in os.listdir("/sys/class/power_supply"):
 23            if not bat.startswith("BAT"):
 24                continue
 25
 26            path = f"/sys/class/power_supply/{bat}"
 27            cap_file = f"{path}/capacity"
 28            if not os.path.exists(cap_file):
 29                continue
 30
 31            with open(cap_file) as f:
 32                cap = f.read().strip()
 33
 34            status = "BAT"
 35            stat_file = f"{path}/status"
 36            if os.path.exists(stat_file):
 37                with open(stat_file) as f:
 38                    if f.read().strip() == "Charging":
 39                        status = "CHR"
 40
 41            out = f"[[bat]]{status} {cap}%"
 42            if int(cap) < 20:
 43                return f"{out} ^"
 44            return out
 45    except Exception:
 46        pass
 47
 48    return None
 49
 50def get_brightness():
 51    try:
 52        act = subprocess.check_output(["brightnessctl", "-c", "backlight", "get"], text=True, stderr=subprocess.DEVNULL).strip()
 53        max_b = subprocess.check_output(["brightnessctl", "-c", "backlight", "max"], text=True, stderr=subprocess.DEVNULL).strip()
 54        return f"[[brt]]BRT {int(int(act) * 100 / int(max_b))}%"
 55    except Exception:
 56        return None
 57
 58def get_wifi_ssid(iface):
 59    try:
 60        out = subprocess.check_output(["iw", "dev", iface, "link"], text=True, stderr=subprocess.DEVNULL)
 61        for line in out.splitlines():
 62            if "SSID:" in line:
 63                return line.split("SSID:", 1)[1].strip()
 64    except Exception:
 65        pass
 66    return None
 67
 68def get_net():
 69    try:
 70        route = subprocess.check_output(["ip", "route", "show", "default"], text=True, stderr=subprocess.DEVNULL)
 71    except Exception:
 72        return "[[net]]NET Disconnected"
 73
 74    if "dev" not in route:
 75        return "[[net]]NET Disconnected"
 76
 77    iface = route.split("dev")[1].split()[0]
 78    if not iface.startswith(("wl", "wlan")):
 79        return f"[[net]]ETH {iface}"
 80
 81    ssid = get_wifi_ssid(iface)
 82    return f"[[net]]NET {ssid}" if ssid else f"[[net]]NET {iface}"
 83
 84def get_time():
 85    return f"[[time]]{time.strftime('%I:%M %m/%d').lstrip('0')}"
 86
 87def get_workspaces():
 88    try:
 89        out = subprocess.check_output(["swaymsg", "-t", "get_workspaces"], text=True, stderr=subprocess.DEVNULL)
 90        workspaces = json.loads(out)
 91    except Exception:
 92        return None
 93
 94    if not workspaces:
 95        return None
 96
 97    tags = [
 98        f"^[[ws_{w['name']}]] {w['name']} ^" if w.get("focused") else f"[[ws_{w['name']}]] {w['name']} "
 99        for w in sorted(workspaces, key=lambda x: x.get("num", 0))
100    ]
101    return "".join(tags)
102
103def generate_line():
104    status_parts = [p for p in [get_sound(), get_battery(), get_brightness(), get_net(), get_time()] if p]
105    right_status = "  |  ".join(status_parts) + "  "
106
107    ws = get_workspaces()
108    if not ws:
109        return right_status
110
111    return f"{ws}    {right_status}"
112
113def main():
114    try:
115        while True:
116            print(generate_line(), flush=True)
117            time.sleep(1)
118    except (BrokenPipeError, KeyboardInterrupt):
119        try:
120            sys.stdout.close()
121        except Exception:
122            pass
123        sys.exit(0)
124
125if __name__ == "__main__":
126    main()