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, liresysctl dev.cpu |grep temperature - AMD :
kldload amdtemp, liresysctl dev.amdtemp.0 |grep core
Sur Linux, utilisez sysfs :
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.
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 :
- FreeBSD :
pkg install py311-python-xlib - Linux : installer
python3-xlibvia 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
Aucun commentaire pour le moment.