Eric Bower
·
2026-09-02
1#!/usr/bin/env python3
2import json
3import os
4import shutil
5import subprocess
6import sys
7import threading
8import time
9
10def get_sound():
11 try:
12 muted = subprocess.check_output(["pamixer", "--get-mute"], text=True, stderr=subprocess.DEVNULL).strip() == "true"
13 vol = subprocess.check_output(["pamixer", "--get-volume"], text=True, stderr=subprocess.DEVNULL).strip()
14 label = "MUTED" if muted else "VOL"
15 return f"{label} {vol}%"
16 except Exception:
17 return "VOL ?"
18
19def get_battery():
20 if not os.path.exists("/sys/class/power_supply"):
21 return None
22
23 try:
24 for bat in os.listdir("/sys/class/power_supply"):
25 if not bat.startswith("BAT"):
26 continue
27
28 path = f"/sys/class/power_supply/{bat}"
29 cap_file = f"{path}/capacity"
30 if not os.path.exists(cap_file):
31 continue
32
33 with open(cap_file) as f:
34 cap = f.read().strip()
35
36 status = "BAT"
37 stat_file = f"{path}/status"
38 if os.path.exists(stat_file):
39 with open(stat_file) as f:
40 if f.read().strip() == "Charging":
41 status = "CHR"
42
43 return f"{status} {cap}%"
44 except Exception:
45 pass
46
47 return None
48
49def get_brightness():
50 try:
51 act = subprocess.check_output(["brightnessctl", "-c", "backlight", "get"], text=True, stderr=subprocess.DEVNULL).strip()
52 max_b = subprocess.check_output(["brightnessctl", "-c", "backlight", "max"], text=True, stderr=subprocess.DEVNULL).strip()
53 return f"BRT {int(int(act) * 100 / int(max_b))}%"
54 except Exception:
55 return None
56
57def get_wifi_ssid(iface):
58 try:
59 out = subprocess.check_output(["iw", "dev", iface, "link"], text=True, stderr=subprocess.DEVNULL)
60 for line in out.splitlines():
61 if "SSID:" in line:
62 return line.split("SSID:", 1)[1].strip()
63 except Exception:
64 pass
65 return None
66
67def get_net():
68 try:
69 route = subprocess.check_output(["ip", "route", "show", "default"], text=True, stderr=subprocess.DEVNULL)
70 except Exception:
71 return "NET Disconnected"
72
73 if "dev" not in route:
74 return "NET Disconnected"
75
76 iface = route.split("dev")[1].split()[0]
77 if not iface.startswith(("wl", "wlan")):
78 return f"ETH {iface}"
79
80 ssid = get_wifi_ssid(iface)
81 return f"NET {ssid}" if ssid else f"NET {iface}"
82
83def get_time():
84 return time.strftime("%I:%M %m/%d").lstrip("0")
85
86def generate_blocks():
87 blocks = [
88 {"name": "sound", "full_text": get_sound(), "color": "#a6e22e"},
89 ]
90
91 bat = get_battery()
92 if bat:
93 blocks.append({"name": "battery", "full_text": bat, "color": "#e6db74"})
94
95 bright = get_brightness()
96 if bright:
97 blocks.append({"name": "brightness", "full_text": bright, "color": "#66d9ef"})
98
99 blocks.extend([
100 {"name": "wifi", "full_text": get_net(), "color": "#ae81ff"},
101 {"name": "clock", "full_text": get_time(), "color": "#f8f8f2"},
102 ])
103
104 return blocks
105
106def handle_click(block_name, button):
107 if button != 1:
108 return
109
110 if block_name == "wifi":
111 cmd = ["monstar", "-e", "nmtui"] if shutil.which("monstar") else ["foot", "-e", "nmtui"]
112 subprocess.Popen(cmd)
113 elif block_name == "sound":
114 cmd = ["pavucontrol"] if shutil.which("pavucontrol") else ["pamixer", "-t"]
115 subprocess.Popen(cmd)
116
117def listen_clicks():
118 while True:
119 try:
120 line = sys.stdin.readline()
121 if not line:
122 break
123 clean_line = line.strip().lstrip("[,").rstrip(",")
124 if not clean_line:
125 continue
126
127 event = json.loads(clean_line)
128 handle_click(event.get("name"), event.get("button", 1))
129 except Exception:
130 pass
131
132def main():
133 try:
134 print(json.dumps({"version": 1, "click_events": True}))
135 print("[")
136 sys.stdout.flush()
137
138 threading.Thread(target=listen_clicks, daemon=True).start()
139
140 while True:
141 print(json.dumps(generate_blocks()) + ",")
142 sys.stdout.flush()
143 time.sleep(1)
144 except (BrokenPipeError, KeyboardInterrupt):
145 try:
146 sys.stdout.close()
147 except Exception:
148 pass
149 sys.exit(0)
150
151if __name__ == "__main__":
152 main()