#!/usr/bin/env python import json import shutil import subprocess import sys import typing __all__ = ["_mem", "_backlight", "_hwtemp"] _BITS: list[tuple[int, str, typing.Callable]] = [] def _bit(idx): def wrapper(func): _BITS.append((idx, func.__name__.replace("_", ""), func)) return func return wrapper @_bit(6) def _mem(): with open("/proc/meminfo") as meminfo_fp: attrs = {} for line in meminfo_fp.readlines(): parts = line.split(":", 2) attrs[parts[0].strip()] = parts[1].strip() avail = int(attrs["MemAvailable"].split()[0]) total = int(attrs["MemTotal"].split()[0]) used = int(((total - avail) / total) * 100) color = None if used > 75: color = "#ff0000" if used < 25: color = "#00ff00" return "M:{}%".format(used), color @_bit(0) def _backlight(dev_dir="/sys/class/backlight/intel_backlight"): with open("{}/brightness".format(dev_dir)) as br_fp: with open("{}/max_brightness".format(dev_dir)) as mb_fp: value = int(br_fp.read().strip()) max_value = int(mb_fp.read().strip()) pct = int(float(float(value) / float(max_value)) * 100) color = None if pct >= 75: color = "#ffaa00" if pct <= 25: color = "#5599ff" return "☼:{}%".format(pct), color ACPI_IBM_FAN = "/proc/acpi/ibm/fan" @_bit(7) def _hwtemp(fan=ACPI_IBM_FAN): acpi_exe = shutil.which("acpi") if acpi_exe is None: return None, None raw_temp = subprocess.run( [acpi_exe, "-t"], text=True, capture_output=True, check=False, ) if raw_temp.returncode != 0 or raw_temp.stdout is None: return None, None temps_by_zone = {} for line in raw_temp.stdout.splitlines(): zone_id, value = line.split(":") human_temp = value.split(",")[-1] temps_by_zone[zone_id.lower().lstrip("thermal ")] = float( human_temp.lower().strip().rstrip("degrees c") ) temps = list(temps_by_zone.values()) avg_temp = float(sum(temps)) / len(temps) color = "#5599ff" if avg_temp > 75: color = "#ff0000" if avg_temp >= 50: color = "#ffaa00" fan_level = "unset" try: with open(fan) as fan_fp: for line in fan_fp.readlines(): if not line.startswith("level:\t\t"): continue fan_level = int(line.replace("level:\t\t", "")) except (OSError, IOError) as exc: sys.stderr.write(str(exc) + "\n") return "T:{:.1f}°C L:{}".format(avg_temp, fan_level), color def _print_line(message): sys.stdout.write(message + "\n") sys.stdout.flush() def _read_line(): try: line = sys.stdin.readline().strip() if not line: sys.exit(3) return line except KeyboardInterrupt: sys.exit() def main(bits=_BITS): _print_line(_read_line()) _print_line(_read_line()) while True: line, prefix = _read_line(), "" if line.startswith(","): line, prefix = line[1:], "," loaded = json.loads(line) for idx, name, func in bits: try: value, color = func() if value is None: continue record = dict(full_text=str(value), name=name) if color is not None: record.update(dict(color=color)) loaded.insert(idx, record) except Exception as exc: sys.stderr.write(str(exc) + "\n") sys.stderr.flush() _print_line(prefix + json.dumps(loaded)) if __name__ == "__main__": main()