Zurück zur Startseite

CPU-Temperaturüberwachung mit Python Xlib

Python-Skript mit Xlib zeigt CPU-Temperatur als Overlay in X11 an. Unterstützt FreeBSD (sysctl coretemp/amdtemp) und Linux (sysfs). Minimalistische Implementierung ohne OOP, mit Flächenlöschung.

CPU-Temperatur-Overlay über Xlib: Python-Skript
Advertisement 728x90

CPU-Temperatur-Overlay unter X11 mit Python: Plattformübergreifende Überwachung

Ein ~120-Zeilen-Skript in Python überwacht die CPU-Temperatur und zeigt den Wert direkt auf dem X11-Arbeitsplatz an. Es nutzt die Bibliothek python-xlib, um auf das Root-Fenster oder das Desktop-Fenster zu zeichnen – ohne erhöhte Rechte. Unterstützt FreeBSD (über sysctl mit coretemp/amdtemp-Modulen) und Linux (/sys/class/thermal). Aktualisiert alle 15 Sekunden und löscht den Anzeigebereich bei Strg+C.

Sensor-Daten lesen

Auf FreeBSD sind Temperaturen über sysctl zugänglich, nachdem die Kernelmodule geladen wurden:

  • Intel: kldload coretemp, dann sysctl dev.cpu |grep temperature
  • AMD: kldload amdtemp, dann sysctl dev.amdtemp.0 |grep core

Unter Linux verwendet man sysfs:

Google AdInline article slot
cat /sys/class/thermal/thermal_zone*/temp

Die Werte liegen in Millidegree Celsius vor (durch 1000 teilen). Das Skript analysiert die Ausgabe von subprocess.Popen, formatiert die Temperaturen zu einer komma-getrennten Zeichenkette.

Arbeit mit dem X-Server

Der Trick: Zeichnen in anderen Fenstern, ohne Benachrichtigung auszulösen. Xlib ermöglicht:

  • Abruf des Root-Fensters oder Desktop-Fensters über _NET_CLIENT_LIST.
  • Erstellung eines GC (Graphics Context) mit weißem Text auf schwarzem Hintergrund.
  • Löschen des Bereichs mit fill_rectangle vor dem Rendern.
  • Zeichnen des Textes mit einer Schriftart mit fester Breite mittels draw_text.

Die Funktion get_root_window durchsucht die obersten Fenster, sucht nach 'Desktop' im WM_NAME (KDE/Xfce). Als Fallback dient das Root-Fenster.

Google AdInline article slot

Vollständiger Quellcode

Selbstausführendes Skript (.sh mit Shebang). Einzige Abhängigkeit: python-xlib.

#!/usr/bin/env python3

import Xlib
from Xlib import display, X   # display und X werden nicht automatisch importiert
import subprocess,time,logging

# Logging-Konfiguration
logging.basicConfig(level=logging.INFO)
#logging.basicConfig(level=logging.DEBUG)

# Bildschirmkoordinaten für Overlay
POS_X = 150
POS_Y = 50
# Für FreeBSD und AMD-Systeme
PATTERN = 'sysctl dev.amdtemp.0 |grep core'
# Für FreeBSD mit coretemp-Modul
#PATTERN = 'sysctl dev.cpu |grep temperature'
# Für Linux
#PATTERN = "cat /sys/class/thermal/thermal_zone*/temp | awk '{ print "temp: " ($1 / 1000) "C" }'"
# Schriftart
FONT = '-misc-fixed-medium-r-normal--13-120-75-75-c-70-iso8859-1'
# Aktualisierungsintervall
REFRESH_SECS = 15

last_dim = [0,0]

def get_root_window(d):
    
    screen = d.screen()
    root = screen.root

    windowIDs = root.get_full_property(d.intern_atom('_NET_CLIENT_LIST'), 
                                                  X.AnyPropertyType).value
    logging.debug('Fundene Fenster: %d',len(windowIDs))

    for windowID in windowIDs:
        window = d.create_resource_object('window', windowID)

        try:
            window_name = window.get_wm_name()
            if window_name:
                if 'Desktop' in window_name:
                    logging.debug("Desktop-ID gefunden: %d - Name: %s",
                                                 windowID,window_name)
                    return window
            else:
                logging.debug("ID: %d - Name: Kein WM_NAME-Attribut",
                                                 windowID)

        except X.BadWindow:
            logging.debug("ID: %d - Fenster existiert nicht mehr",windowID)
    return root


def clear_rect(msg):
    global last_dim
    if last_dim[0] > 0:    
     root.fill_rectangle(gc2, POS_X,POS_Y-last_dim[1], 
                          last_dim[0]-20, last_dim[1]+5)
    else:
        text_extents = font.query_text_extents(msg)
        tw = text_extents.overall_width
        th = text_extents.font_ascent + text_extents.font_descent
        root.fill_rectangle(gc2, POS_X, POS_Y-th, tw-20, th+5)
        last_dim = [tw,th]


def draw_message(msg):
    clear_rect(msg)
    root.draw_text(gc, POS_X, POS_Y, msg)
    display.flush()


display = Xlib.display.Display()
screen = display.screen()
root = get_root_window(display)

root_id = root.id
logging.debug("Root-Fenster-ID: %d",root_id)
root.change_attributes(event_mask=X.ExposureMask)
gc = root.create_gc(foreground = screen.white_pixel, 
                    background = screen.black_pixel)
colormap = screen.default_colormap
color = colormap.alloc_named_color('black') 
gc2 = root.create_gc(foreground=color.pixel)

try:
    font = display.open_font(FONT)
    gc.font = font.id
except Exception as e:
    logging.exception(e)
    exit(1)

try:
    while 1:
            process = subprocess.Popen(PATTERN, 
                    shell=True, text=True,
                    stdout=subprocess.PIPE)
            stdout_list = process.communicate()[0].split('\n')

            out = ''
            for s in stdout_list:
                if ':' not in s: continue
                kv = s.split(':')
                if len(out) > 0: out+= ','
                out+= kv[1]

            logging.debug(out)
            draw_message(out.encode())
            time.sleep(REFRESH_SECS)
            
except KeyboardInterrupt:
        x = POS_X // 2
        y = POS_Y // 2
        root.clear_area(x,y,last_dim[0]+x,last_dim[1]+y,True)
        display.flush()

Einstellungen anpassen

Alle Konstanten befinden sich am Anfang des Skripts:

  • POS_X, POS_Y: Position des Overlays auf dem Bildschirm.
  • PATTERN: Befehl zur Sensordatenabfrage (je nach Plattform anpassen).
  • FONT: X11-Schriftart im XLFD-Format.
  • REFRESH_SECS: Aktualisierungsintervall.
  • Logging: auf INFO oder DEBUG setzen.

python-xlib installieren:

Google AdInline article slot
  • FreeBSD: pkg install py311-python-xlib
  • Linux: python3-xlib über apt/yum installieren

Wichtige Vorteile

  • Plattformübergreifend: Funktioniert unter FreeBSD/Linux, Intel/AMD, ohne Wayland.
  • Minimalistisch: Nur 120 Zeilen, kein OOP/Threads, nur eine Abhängigkeit.
  • X11-Hack: Zeichnet auf Root/Desktop ohne Berechtigungen oder visuelle Hinweise.
  • Aufräumen: Nutzt fill_rectangle und clear_area beim Beenden.
  • Parsing: Verwendet subprocess + split für sysctl/sysfs-Daten.

— Editorial Team

Advertisement 728x90

Weiterlesen