From bc4cb47a4cc66cb1c56e0a836e2522f3060d9658 Mon Sep 17 00:00:00 2001 From: Mortdecai Date: Thu, 1 Oct 2026 23:26:54 -0400 Subject: [PATCH] =?UTF-8?q?feat:=20kitty=20status=20bar=20=E2=80=94=20CPU?= =?UTF-8?q?=20temp=20+=20battery=20(hidden=20when=20sensor=20absent)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 (1M context) --- kitty/tab_bar.py | 50 ++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 48 insertions(+), 2 deletions(-) diff --git a/kitty/tab_bar.py b/kitty/tab_bar.py index dd885d1..b80b8f3 100644 --- a/kitty/tab_bar.py +++ b/kitty/tab_bar.py @@ -2,9 +2,11 @@ Flat tab style (no powerline separators) + right-aligned status area. Shows user@ prefix for non-seth users. -Status: git branch, CPU%, memory, uptime, hostname, clock. +Status: git branch, CPU% + temp, memory, uptime, hostname, battery, clock. +Temp and battery hide themselves when the sensor is missing (steel141 has no battery). """ +import glob import os import subprocess from datetime import datetime @@ -29,11 +31,15 @@ _ICON_MEM = "\uf035b" _ICON_UP = "\uf0aa" _ICON_HOST = "\uf108" _ICON_CLOCK = "\uf017" +_ICON_TEMP = "\uf2c9" +_ICON_BAT = "\uf240" +_ICON_CHG = "\uf0e7" # Colors _ORANGE = as_rgb(0xD35400) _BLACK = as_rgb(0x000000) _DIM = as_rgb(0x666666) +_RED = as_rgb(0xFF3030) _TAB_BG = as_rgb(0x1A1A1A) _BAR_BG = as_rgb(0x0D0D0D) @@ -80,6 +86,39 @@ def _get_cpu_percent() -> str: return "?" +# Intel = coretemp, AMD = k10temp; temp1 is the package/Tctl sensor on both. +# hwmon numbers move between boots, so resolve once at kitty start. +_CPU_TEMP = next( + (os.path.join(os.path.dirname(n), "temp1_input") + for n in glob.glob("/sys/class/hwmon/hwmon*/name") + if open(n).read().strip() in ("coretemp", "k10temp")), + None, +) +# BAT* only: mice/keyboards also show up in power_supply as hid*/hidpp_battery*. +_BAT = next(iter(sorted(glob.glob("/sys/class/power_supply/BAT*"))), None) + + +def _get_cpu_temp() -> str: + try: + with open(_CPU_TEMP) as f: + return f"{int(f.read()) // 1000}\u00b0" + except Exception: + return "" + + +def _get_battery() -> tuple[str, str, bool]: + """(icon, '60%', low) or ('', '', False) when there's no battery.""" + try: + with open(f"{_BAT}/capacity") as f: + cap = int(f.read()) + with open(f"{_BAT}/status") as f: + status = f.read().strip() + except Exception: + return "", "", False + on_ac = status in ("Charging", "Full", "Not charging") + return (_ICON_CHG if on_ac else _ICON_BAT), f"{cap}%", (not on_ac and cap <= 20) + + def _get_memory() -> str: try: info = {} @@ -160,10 +199,17 @@ def draw_tab( if branch: cells += [(_ICON_BRANCH + " ", _ORANGE), (branch, _DIM), (sep, _DIM)] - cells += [(_ICON_CPU + " ", _ORANGE), (_get_cpu_percent(), _DIM), (sep, _DIM)] + cells += [(_ICON_CPU + " ", _ORANGE), (_get_cpu_percent(), _DIM)] + temp = _get_cpu_temp() + if temp: + cells += [(" " + _ICON_TEMP + " ", _ORANGE), (temp, _DIM)] + cells += [(sep, _DIM)] cells += [(_ICON_MEM + " ", _ORANGE), (_get_memory(), _DIM), (sep, _DIM)] cells += [(_ICON_UP + " ", _ORANGE), (_get_uptime(), _DIM), (sep, _DIM)] cells += [(_ICON_HOST + " ", _ORANGE), (_hostname, _DIM), (sep, _DIM)] + bat_icon, bat, low = _get_battery() + if bat: + cells += [(bat_icon + " ", _RED if low else _ORANGE), (bat, _RED if low else _DIM), (sep, _DIM)] cells += [(_ICON_CLOCK + " ", _ORANGE), (datetime.now().strftime("%H:%M"), _DIM), (" ", _DIM)] total_len = sum(len(t) for t, _ in cells)