Nix-Vibe public snapshot (squashed history)
Current state of main at 0240060 feat(hp-laptop): install TeleportFling from its flake. History intentionally collapsed to a single commit; this repo mirrors only the latest state.
This commit is contained in:
@@ -0,0 +1,182 @@
|
||||
#!/usr/bin/env python3
|
||||
"""qs-stats: one-shot system stats probe for the QuickShell stats popup.
|
||||
|
||||
Emits a single JSON object on stdout:
|
||||
{
|
||||
"ts": 1730000000,
|
||||
"cpu": 12.3,
|
||||
"ram": {"used": 8.2, "total": 31.9, "pct": 25.7},
|
||||
"swap": {"used": 0.0, "total": 8.0, "pct": 0.0},
|
||||
"disk": {"used": 156.4, "total": 934.1, "pct": 16.7},
|
||||
"load": [2.01, 2.29, 2.10],
|
||||
"uptime": 7861.0,
|
||||
"temps": [{"name": "TCPU", "temp": 58.0}, ...],
|
||||
"freq": 3.8
|
||||
}
|
||||
|
||||
CPU% is computed from two /proc/stat samples ~250ms apart (mostly an idle
|
||||
sleep: ~free on battery). Memory/swap/disk come from /proc/meminfo +
|
||||
os.statvfs. Temperatures come from /sys/class/thermal (kernel hwmon zones).
|
||||
The bar runs this on a slow 15s timer; the stats popup refreshes every 3s
|
||||
while open.
|
||||
"""
|
||||
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
|
||||
CPU_SAMPLE_MS = 250
|
||||
|
||||
|
||||
def cpu_usage():
|
||||
def sample():
|
||||
with open("/proc/stat") as f:
|
||||
parts = f.readline().split()
|
||||
# cpu user nice system idle iowait irq softirq steal guest guest_nice
|
||||
vals = list(map(int, parts[1:]))
|
||||
idle = vals[3] + vals[4]
|
||||
total = sum(vals)
|
||||
return idle, total
|
||||
|
||||
idle0, total0 = sample()
|
||||
time.sleep(CPU_SAMPLE_MS / 1000.0)
|
||||
idle1, total1 = sample()
|
||||
d_idle = idle1 - idle0
|
||||
d_total = total1 - total0
|
||||
if d_total <= 0:
|
||||
return 0.0
|
||||
return round(100.0 * (1.0 - d_idle / d_total), 1)
|
||||
|
||||
|
||||
def meminfo_gi():
|
||||
mem = {}
|
||||
try:
|
||||
with open("/proc/meminfo") as f:
|
||||
for line in f:
|
||||
key, _, rest = line.partition(":")
|
||||
val = rest.strip().split()[0]
|
||||
mem[key] = int(val) # kB
|
||||
except OSError:
|
||||
return {}
|
||||
|
||||
def gi(kb):
|
||||
return round(kb / 1024.0 / 1024.0, 1)
|
||||
|
||||
ram = {
|
||||
"total": gi(mem.get("MemTotal", 0)),
|
||||
# MemAvailable is the honest "swappable minus thrash" number
|
||||
"available": gi(mem.get("MemAvailable", mem.get("MemFree", 0))),
|
||||
}
|
||||
ram["used"] = round(ram["total"] - ram["available"], 1)
|
||||
ram["pct"] = (
|
||||
round(100.0 * ram["used"] / ram["total"], 1) if ram["total"] > 0 else 0.0
|
||||
)
|
||||
|
||||
swap = {
|
||||
"total": gi(mem.get("SwapTotal", 0)),
|
||||
"free": gi(mem.get("SwapFree", 0)),
|
||||
}
|
||||
swap["used"] = round(swap["total"] - swap["free"], 1)
|
||||
swap["pct"] = (
|
||||
round(100.0 * swap["used"] / swap["total"], 1) if swap["total"] > 0 else 0.0
|
||||
)
|
||||
return ram, swap
|
||||
|
||||
|
||||
def disk_pct(path="/"):
|
||||
try:
|
||||
st = os.statvfs(path)
|
||||
except OSError:
|
||||
return {"used": 0.0, "total": 0.0, "pct": 0.0}
|
||||
total = st.f_blocks * st.f_frsize
|
||||
free = st.f_bavail * st.f_frsize
|
||||
used = total - free
|
||||
return {
|
||||
"used": round(used / 1024.0**3, 1),
|
||||
"total": round(total / 1024.0**3, 1),
|
||||
"pct": round(100.0 * used / total, 1) if total > 0 else 0.0,
|
||||
}
|
||||
|
||||
|
||||
def load_avg():
|
||||
try:
|
||||
return [round(float(x), 2) for x in os.getloadavg()]
|
||||
except OSError:
|
||||
return []
|
||||
|
||||
|
||||
def uptime():
|
||||
try:
|
||||
with open("/proc/uptime") as f:
|
||||
return round(float(f.read().split()[0]), 1)
|
||||
except OSError:
|
||||
return 0.0
|
||||
|
||||
|
||||
def temps():
|
||||
out = []
|
||||
base = "/sys/class/thermal"
|
||||
try:
|
||||
zones = sorted(os.listdir(base))
|
||||
except OSError:
|
||||
return out
|
||||
for z in zones:
|
||||
if not z.startswith("thermal_zone"):
|
||||
continue
|
||||
tpath = os.path.join(base, z)
|
||||
typefile = os.path.join(tpath, "type")
|
||||
tempfile = os.path.join(tpath, "temp")
|
||||
try:
|
||||
with open(typefile) as f:
|
||||
name = f.read().strip()
|
||||
with open(tempfile) as f:
|
||||
millideg = int(f.read().strip())
|
||||
except (OSError, ValueError):
|
||||
continue
|
||||
# Skip the ACPI "INT3400 Thermal" umbrella zone (always ~20C, noise)
|
||||
if "INT3400" in name:
|
||||
continue
|
||||
out.append({"name": name, "temp": round(millideg / 1000.0, 0)})
|
||||
# Keep a sane display order: CPU forward, wifi last
|
||||
def rank(n):
|
||||
n = n.lower()
|
||||
if "cpu" in n or "tctl" in n or "pkg" in n:
|
||||
return 0
|
||||
if "sen" in n:
|
||||
return 1
|
||||
return 2
|
||||
|
||||
out.sort(key=lambda t: (rank(t["name"]), t["name"]))
|
||||
return out
|
||||
|
||||
|
||||
def scaled_vout():
|
||||
# Current CPU P-state / GHz (x86-package-temp zone present ⇒ has cpufreq).
|
||||
try:
|
||||
with open("/sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq") as f:
|
||||
khz = int(f.read().strip())
|
||||
return round(khz / 1_000_000.0, 2)
|
||||
except OSError:
|
||||
return 0.0
|
||||
|
||||
|
||||
def main():
|
||||
ram, swap = meminfo_gi()
|
||||
payload = {
|
||||
"ts": int(time.time()),
|
||||
"cpu": cpu_usage(),
|
||||
"ram": ram,
|
||||
"swap": swap,
|
||||
"disk": disk_pct(),
|
||||
"load": load_avg(),
|
||||
"uptime": uptime(),
|
||||
"temps": temps(),
|
||||
"freq": scaled_vout(),
|
||||
}
|
||||
sys.stdout.write(json.dumps(payload))
|
||||
sys.stdout.write("\n")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user