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()