Retour à l'accueil

Surveillance de la température CPU avec Python Xlib

Script Python avec Xlib affichant la température CPU en superposition dans X11. Prend en charge FreeBSD (sysctl coretemp/amdtemp) et Linux (sysfs). Implémentation minimaliste sans OOP, avec effacement de zone.

Superposition de température CPU via Xlib : Script Python
Advertisement 728x90

Surimpression de la température du CPU sur X11 avec Python : surveillance multiplateforme

Un script Python d'environ 120 lignes surveille la température du processeur et affiche du texte directement sur le bureau X11. Il utilise la bibliothèque python-xlib pour dessiner sur la fenêtre racine ou le bureau sans nécessiter de privilèges élevés. Compatible avec FreeBSD (via sysctl et les modules coretemp/amdtemp) et Linux (/sys/class/thermal). Actualise toutes les 15 secondes et nettoie l'affichage lors d'un Ctrl-C.

Lecture des données du capteur

Sur FreeBSD, la température est accessible via sysctl après chargement des modules noyau :

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

Sur Linux, utilisez sysfs :

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

Les valeurs sont en millidegrés Celsius (divisez par 1000). Le script analyse la sortie de subprocess.Popen, formate les températures sous forme de chaîne séparée par des virgules.

Interaction avec le serveur X

Astuce clé : dessiner dans d'autres fenêtres sans notification. Xlib permet :

  • Récupérer la fenêtre racine ou le bureau via _NET_CLIENT_LIST.
  • Créer un GC (contexte graphique) avec du texte blanc sur fond noir.
  • Nettoyer la zone avec fill_rectangle avant le rendu.
  • Dessiner le texte avec une police à largeur fixe via draw_text.

La fonction get_root_window parcourt les fenêtres de premier niveau, cherche 'Desktop' dans WM_NAME (KDE/Xfce). En cas d’échec, elle retourne la fenêtre racine.

Google AdInline article slot

Code source complet

Script autonome (.sh avec shebang). Dépendance unique : python-xlib.

#!/usr/bin/env python3

import Xlib
from Xlib import display, X   # display et X ne sont pas importés automatiquement
import subprocess,time,logging

# configuration du logging
logging.basicConfig(level=logging.INFO)
#logging.basicConfig(level=logging.DEBUG)

# coordonnées d'affichage
POS_X = 150
POS_Y = 50
# pour FreeBSD et systèmes AMD
PATTERN = 'sysctl dev.amdtemp.0 |grep core'
# pour FreeBSD avec module coretemp
#PATTERN = 'sysctl dev.cpu |grep temperature'
# pour Linux
#PATTERN = "cat /sys/class/thermal/thermal_zone*/temp | awk '{ print "temp: " ($1 / 1000) "C" }'"
# police
FONT = '-misc-fixed-medium-r-normal--13-120-75-75-c-70-iso8859-1'
# intervalle de rafraîchissement
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('Trouvé %d fenêtres.',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("Trouvé ID bureau : %d - Nom : %s",
                                                 windowID,window_name)
                    return window
            else:
                logging.debug("ID : %d - Nom : Aucun (pas de propriété WM_NAME)",
                                                 windowID)

        except X.BadWindow:
            logging.debug("ID : %d - Fenêtre introuvable",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("ID fenêtre racine : %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()

Personnalisation des paramètres

Toutes les constantes sont en haut du script :

  • POS_X, POS_Y : position de l’overlay à l’écran.
  • PATTERN : commande du capteur (à ajuster selon la plateforme).
  • FONT : police X11 au format XLFD.
  • REFRESH_SECS : fréquence de mise à jour.
  • Logging : définir à INFO ou DEBUG.

Installation de python-xlib :

Google AdInline article slot
  • FreeBSD : pkg install py311-python-xlib
  • Linux : installer python3-xlib via apt/yum

Points forts

  • Multiplateforme : fonctionne sur FreeBSD/Linux, Intel/AMD, sans Wayland.
  • Minimaliste : seulement 120 lignes, pas de OOP/threads, une seule dépendance.
  • Astuce X11 : dessine sur la fenêtre racine/bureau sans permissions ni indicateur visuel.
  • Nettoyage : utilise fill_rectangle et clear_area à la fermeture.
  • Analyse : repose sur subprocess + split pour les données sysctl/sysfs.

— Editorial Team

Advertisement 728x90

Lire ensuite