#!/usr/bin/env python3 import runpy from datetime import datetime from os import getenv, getpid from signal import signal from subprocess import CalledProcessError, check_output, run from time import sleep, time crypto = runpy.run_path(getenv("HOME", "") + "/bin/crypto") def get_stdout(command: list[str]) -> str: try: return check_output(command).decode().rstrip() except CalledProcessError: return "" def update_crypto(): try: fees = crypto["get_btc_fees"]() coins = crypto["get_coins_values"]() except: OUT["cryto"] = "" return OUT["crypto"] = f"{fees} {coins}" def update_cpu(): loadavg: str with open("/proc/loadavg") as f: loadavg = f.read().split()[0] OUT["cpu"] = f" {loadavg}" def update_memory(): kbtot: int kbavail: int gbused: float lines: list[str] with open("/proc/meminfo") as f: lines = f.read().split("\n") kbtot = int(lines[0].split()[1]) kbavail = int(lines[2].split()[1]) gbused = (kbtot - kbavail) / 1024 / 1024 OUT["ram"] = f" {gbused:.1f}G" def update_bat(): dir = "/sys/class/power_supply/BAT0" status: str capacity: str with open(dir + "/status") as f: status = "" if f.read() == "Charging\n" else "" with open(dir + "/capacity") as f: capacity = f.read().rstrip() OUT["bat"] = f"{status} {capacity}%" def update_vol(): vol = get_stdout(["wpctl", "get-volume", "@DEFAULT_AUDIO_SINK@"]) while not vol: vol = get_stdout(["wpctl", "get-volume", "@DEFAULT_AUDIO_SINK@"]) sleep(1) if "MUTED" in vol: OUT["vol"] = "🔇" else: OUT["vol"] = f" {int(float(vol.split()[1]) * 100)}" def update_wlp(): sig = 0 ssid = "" lines: list[str] with open("/proc/net/wireless") as f: lines = f.read().split("\n") for l in lines: if l.startswith("wlp"): sig = float(l.split()[2]) break lines = get_stdout(["nmcli", "device", "wifi", "show-password"]).split("\n") for l in lines: if l.startswith("SSID: "): ssid = l[6:] break # https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/blob/d9b06a95/src/libnmc-base/nm-client-utils.c#L628 if sig > 56: # 70 is max OUT["wlp"] = f"▂▄▆█ {ssid}" elif sig > 38: OUT["wlp"] = f"▂▄▆_ {ssid}" elif sig > 21: OUT["wlp"] = f"▂▄__ {ssid}" elif sig > 3: OUT["wlp"] = f"▂___ {ssid}" else: OUT["wlp"] = f"____ {ssid}" def update_time(): OUT["time"] = datetime.now().strftime("%a %m/%d %R") def display(): s = f"{OUT.get('crypto')} | {OUT.get('cpu')} | {OUT.get('ram')} | {OUT.get('vol')} | {OUT.get('wlp')} | {OUT.get('bat')} | {OUT.get('time')}" if getenv("XDG_SESSION_TYPE") == "wayland": print(s) else: run(["xsetroot", "-name", s]) def handler(signum: int, _): if signum == 34: update_vol() # elif signum == 35: # pass display() def setup_sig(): cache_dir = getenv("XDG_CACHE_HOME") or f"{getenv('HOME')}/.cache" with open(cache_dir + "/pidofbar", "w") as f: f.write(str(getpid())) # kill -m "$(cat ~/.cache/pidofbar)" signal(34, handler) OUT = dict() def main(): sec = 0 setup_sig() # modules that don't update on their own need to be run at the start for # getting their initial value update_vol() # TODO async? while True: if not sec % 300: update_crypto() if not sec % 10: update_time() update_cpu() update_memory() update_bat() update_wlp() display() sec += 10 sleep(10 - time() % 10) if __name__ == "__main__": main()