Eric Bower
·
2026-09-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 return f"[[bat]]{status} {cap}%"
42 except Exception:
43 pass
44
45 return None
46
47def get_brightness():
48 try:
49 act = subprocess.check_output(["brightnessctl", "-c", "backlight", "get"], text=True, stderr=subprocess.DEVNULL).strip()
50 max_b = subprocess.check_output(["brightnessctl", "-c", "backlight", "max"], text=True, stderr=subprocess.DEVNULL).strip()
51 return f"[[brt]]BRT {int(int(act) * 100 / int(max_b))}%"
52 except Exception:
53 return None
54
55def get_wifi_ssid(iface):
56 try:
57 out = subprocess.check_output(["iw", "dev", iface, "link"], text=True, stderr=subprocess.DEVNULL)
58 for line in out.splitlines():
59 if "SSID:" in line:
60 return line.split("SSID:", 1)[1].strip()
61 except Exception:
62 pass
63 return None
64
65def get_net():
66 try:
67 route = subprocess.check_output(["ip", "route", "show", "default"], text=True, stderr=subprocess.DEVNULL)
68 except Exception:
69 return "[[net]]NET Disconnected"
70
71 if "dev" not in route:
72 return "[[net]]NET Disconnected"
73
74 iface = route.split("dev")[1].split()[0]
75 if not iface.startswith(("wl", "wlan")):
76 return f"[[net]]ETH {iface}"
77
78 ssid = get_wifi_ssid(iface)
79 return f"[[net]]NET {ssid}" if ssid else f"[[net]]NET {iface}"
80
81def get_time():
82 return f"[[time]]{time.strftime('%I:%M %m/%d').lstrip('0')}"
83
84def get_workspaces():
85 try:
86 out = subprocess.check_output(["swaymsg", "-t", "get_workspaces"], text=True, stderr=subprocess.DEVNULL)
87 workspaces = json.loads(out)
88 except Exception:
89 return None
90
91 if not workspaces:
92 return None
93
94 tags = [
95 f"^[[ws_{w['name']}]] {w['name']} ^" if w.get("focused") else f"[[ws_{w['name']}]] {w['name']} "
96 for w in sorted(workspaces, key=lambda x: x.get("num", 0))
97 ]
98 return "".join(tags)
99
100def generate_line():
101 status_parts = [p for p in [get_sound(), get_battery(), get_brightness(), get_net(), get_time()] if p]
102 right_status = " | ".join(status_parts) + " "
103
104 ws = get_workspaces()
105 if not ws:
106 return right_status
107
108 return f"{ws} {right_status}"
109
110def main():
111 try:
112 while True:
113 print(generate_line(), flush=True)
114 time.sleep(1)
115 except (BrokenPipeError, KeyboardInterrupt):
116 try:
117 sys.stdout.close()
118 except Exception:
119 pass
120 sys.exit(0)
121
122if __name__ == "__main__":
123 main()