Created
July 3, 2026 07:19
-
-
Save rcarmo/e82804e068751586b947ae7ad075c00a to your computer and use it in GitHub Desktop.
Stupidly Visible Temperature Display
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env python3 | |
| """ | |
| Print the lm-sensors "Package id 0" temperature in a huge terminal font. | |
| Usage: | |
| ./scripts/package-temp-big.py # watch, run sensors every 2s, large font | |
| ./scripts/package-temp-big.py -i 5 # watch every 5s | |
| ./scripts/package-temp-big.py --scale 3 # even larger | |
| ./scripts/package-temp-big.py --once # one-shot | |
| sensors | ./scripts/package-temp-big.py --stdin | |
| No external Python dependencies. Requires `sensors` from lm-sensors unless using --stdin. | |
| """ | |
| from __future__ import annotations | |
| import argparse | |
| import re | |
| import shutil | |
| import subprocess | |
| import sys | |
| import time | |
| from typing import Iterable | |
| # 7-line block digits. Keep glyphs monospace-friendly. | |
| FONT = { | |
| "0": [" █████ ", "██ ██", "██ ███", "██ █ ██", "███ ██", "██ ██", " █████ "], | |
| "1": [" ██ ", " ███ ", " ██ ", " ██ ", " ██ ", " ██ ", "██████ "], | |
| "2": [" █████ ", "██ ██", " ██", " ███ ", " ██ ", " ██ ", "███████"], | |
| "3": ["██████ ", " ██", " ██", " █████ ", " ██", " ██", "██████ "], | |
| "4": ["██ ██", "██ ██", "██ ██", "███████", " ██", " ██", " ██"], | |
| "5": ["███████", "██ ", "██ ", "██████ ", " ██", "██ ██", " █████ "], | |
| "6": [" █████ ", "██ ██", "██ ", "██████ ", "██ ██", "██ ██", " █████ "], | |
| "7": ["███████", " ██", " ██ ", " ██ ", " ██ ", " ██ ", "██ "], | |
| "8": [" █████ ", "██ ██", "██ ██", " █████ ", "██ ██", "██ ██", " █████ "], | |
| "9": [" █████ ", "██ ██", "██ ██", " ██████", " ██", "██ ██", " █████ "], | |
| ".": [" ", " ", " ", " ", " ", " ██ ", " ██ "], | |
| "+": [" ", " ██ ", " ██ ", "███████", " ██ ", " ██ ", " "], | |
| "-": [" ", " ", " ", "███████", " ", " ", " "], | |
| "°": [" ███ ", "█ █ ", "█ █ ", " ███ ", " ", " ", " "], | |
| "C": [" █████ ", "██ ██", "██ ", "██ ", "██ ", "██ ██", " █████ "], | |
| " ": [" "] * 7, | |
| } | |
| PACKAGE_RE = re.compile( | |
| r"^\s*Package\s+id\s+0\s*:\s*\+?(-?\d+(?:\.\d+)?)\s*°?C", | |
| re.IGNORECASE | re.MULTILINE, | |
| ) | |
| def run_sensors() -> str: | |
| if not shutil.which("sensors"): | |
| raise RuntimeError("`sensors` not found. Install lm-sensors first, e.g. `sudo apt install lm-sensors`.") | |
| proc = subprocess.run(["sensors"], text=True, capture_output=True, check=False) | |
| if proc.returncode != 0: | |
| raise RuntimeError(proc.stderr.strip() or "`sensors` failed") | |
| return proc.stdout | |
| def parse_package_temp(output: str) -> str: | |
| match = PACKAGE_RE.search(output) | |
| if not match: | |
| raise RuntimeError('Could not find a "Package id 0" temperature in `sensors` output.') | |
| value = match.group(1) | |
| # Avoid noisy trailing .0 unless lm-sensors reports a useful decimal. | |
| if value.endswith(".0"): | |
| value = value[:-2] | |
| return value | |
| def read_package_temp(stdin: bool = False) -> str: | |
| output = sys.stdin.read() if stdin else run_sensors() | |
| return parse_package_temp(output) | |
| def colour_for_temp(temp: float) -> str: | |
| if temp >= 90: | |
| return "\033[1;31m" # bright red | |
| if temp >= 75: | |
| return "\033[38;5;208m" # orange | |
| if temp >= 60: | |
| return "\033[1;33m" # yellow | |
| return "\033[1;36m" # cyan | |
| def render_big(text: str, scale: int = 2) -> str: | |
| """Render text with simple integer scaling. | |
| scale=2 is intentionally huge but still fits most terminals for values like 63°C. | |
| """ | |
| scale = max(1, scale) | |
| rows: list[str] = [] | |
| for glyph_row in range(7): | |
| line = "" | |
| for char in text: | |
| glyph = FONT.get(char, FONT[" "]) | |
| expanded = "".join(ch * scale for ch in glyph[glyph_row]) | |
| line += expanded + (" " * (2 * scale)) | |
| for _ in range(scale): | |
| rows.append(line.rstrip()) | |
| return "\n".join(rows) | |
| def print_temp(clear: bool = False, stdin: bool = False, scale: int = 2) -> None: | |
| temp_s = read_package_temp(stdin=stdin) | |
| temp_f = float(temp_s) | |
| label = f"{temp_s}°C" | |
| if clear: | |
| print("\033[2J\033[H", end="") | |
| print(colour_for_temp(temp_f) + render_big(label, scale=scale) + "\033[0m") | |
| print(f"Package id 0: {temp_s}°C") | |
| def main(argv: Iterable[str] | None = None) -> int: | |
| parser = argparse.ArgumentParser(description="Show lm-sensors Package id 0 temperature in a huge terminal font.") | |
| parser.add_argument("--once", action="store_true", help="run sensors once and exit") | |
| parser.add_argument("-i", "--interval", type=float, default=2.0, help="refresh interval in seconds (default: 2)") | |
| parser.add_argument("-s", "--scale", type=int, default=2, help="font scale factor (default: 2)") | |
| parser.add_argument("--stdin", action="store_true", help="parse sensors output from stdin instead of running sensors") | |
| args = parser.parse_args(argv) | |
| try: | |
| if args.stdin and not args.once: | |
| args.once = True | |
| if args.stdin: | |
| print_temp(clear=False, stdin=True, scale=args.scale) | |
| elif args.once: | |
| print_temp(clear=False, scale=args.scale) | |
| else: | |
| while True: | |
| print_temp(clear=True, scale=args.scale) | |
| time.sleep(args.interval) | |
| except KeyboardInterrupt: | |
| return 0 | |
| except RuntimeError as exc: | |
| print(f"error: {exc}", file=sys.stderr) | |
| return 1 | |
| return 0 | |
| if __name__ == "__main__": | |
| raise SystemExit(main()) | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment