#!/usr/bin/env python3
import subprocess
import re
import tkinter as tk
INTERVAL_MS = 2000
topmost = True
RED = "#c01818"
GREEN = "#11a88e"
BG = "#f2f2f2"
TEXT = "#000000"
def get_sensors_text():
return subprocess.check_output(["sensors"], text=True)
def parse_sensors(text):
data = {
"RAM": [],
"WASSER": [],
"AQUAERO": [],
"WLAN": [],
"GPU": [],
"CPU": [],
"SSD": []
}
current_chip = ""
nvme_count = 0
for line in text.splitlines():
s = line.strip()
if not s or s.startswith("Adapter:"):
continue
if not line.startswith(" ") and ":" not in s:
current_chip = s
if "nvme" in current_chip.lower():
nvme_count += 1
continue
chip = current_chip.lower()
# WATER
if "highflownext" in chip:
m = re.match(r"\s*Coolant temp:\s*([+-][0-9.]+)°C", line)
if m:
data["WASSER"].append(
("Coolant", f"{m.group(1)}°C")
)
m = re.match(r"\s*Flow \[dL/h\]:\s*(\d+)", line)
if m:
flow = int(m.group(1)) / 10
data["WASSER"].append(
("Flow", f"{flow:.1f} L/h")
)
# RAM
elif "jc42" in chip:
m = re.match(r"\s*temp1:\s*([+-][0-9.]+)°C", line)
if m:
if "10-18" in chip:
data["RAM"].append(
("RAM 1", f"{m.group(1)}°C")
)
elif "10-19" in chip:
data["RAM"].append(
("RAM 2", f"{m.group(1)}°C")
)
# AQUAERO
elif "aquaero" in chip:
checks = [
(
r"\s*Fan 1 speed:\s*(\d+) RPM",
"Lüfter",
" RPM"
),
(
r"\s*Fan 3 speed:\s*(\d+) RPM",
"Pump 1",
" RPM"
),
(
r"\s*Fan 4 speed:\s*(\d+) RPM",
"Pump 2",
" RPM"
),
(
r"\s*Sensor 1:\s*([+-][0-9.]+)°C",
"Aqua Sensor",
"°C"
),
]
for pattern, name, unit in checks:
m = re.match(pattern, line)
if m:
data["AQUAERO"].append(
(name, f"{m.group(1)}{unit}")
)
# WLAN
elif "iwlwifi" in chip:
m = re.match(r"\s*temp1:\s*([+-][0-9.]+)°C", line)
if m:
data["WLAN"].append(
("WLAN", f"{m.group(1)}°C")
)
# GPU
elif "amdgpu" in chip:
m = re.match(
r"\s*(edge|junction|mem):\s*([+-][0-9.]+)°C",
line
)
if m:
data["GPU"].append(
(m.group(1), f"{m.group(2)}°C")
)
# CPU
elif "k10temp" in chip:
m = re.match(
r"\s*(Tctl|Tccd1):\s*([+-][0-9.]+)°C",
line
)
if m:
data["CPU"].append(
(m.group(1), f"{m.group(2)}°C")
)
# SSD
elif "nvme" in chip:
m = re.match(
r"\s*(Composite|Sensor 2):\s*([+-][0-9.]+)°C",
line
)
if m:
if m.group(1) == "Composite":
if nvme_count == 1:
name = "NVMe"
else:
name = f"NVMe {nvme_count}"
else:
name = "NVMe NAND"
data["SSD"].append(
(name, f"{m.group(2)}°C")
)
return data
def draw_header():
header.delete("all")
header.create_text(
55,
32,
text="XX",
fill=RED,
font=("Arial", 34, "bold"),
anchor="center"
)
header.create_text(
120,
26,
text="hardware",
fill="#111111",
font=("Arial", 18),
anchor="w"
)
# <<< WEITER NACH RECHTS >>>
header.create_text(
330,
26,
text="LUXX",
fill=RED,
font=("Arial", 18),
anchor="w"
)
header.create_text(
120,
55,
text="C",
fill=GREEN,
font=("Arial", 20, "bold"),
anchor="w"
)
header.create_text(
148,
55,
text="CachyOS Monitor-Systems",
fill=GREEN,
font=("Arial", 10, "bold"),
anchor="w"
)
header.create_text(
148,
75,
text="Creater Oefianer",
fill="black",
font=("Arial", 8, "bold"),
anchor="w"
)
def render_data(data):
output.config(state="normal")
output.delete("1.0", "end")
for title in [
"RAM",
"WASSER",
"AQUAERO",
"WLAN",
"GPU",
"CPU",
"SSD"
]:
values = data.get(title, [])
if not values:
continue
output.insert(
"end",
title + "\n",
"section"
)
for name, value in values:
output.insert(
"end",
f"{name:<15}{value:>12}\n",
"normal"
)
output.config(state="disabled")
def update():
try:
render_data(
parse_sensors(
get_sensors_text()
)
)
except Exception as e:
output.config(state="normal")
output.delete("1.0", "end")
output.insert(
"end",
"Fehler:\n" + str(e)
)
output.config(state="disabled")
root.after(INTERVAL_MS, update)
def toggle_topmost(event=None):
global topmost
topmost = not topmost
root.attributes("-topmost", topmost)
root = tk.Tk()
root.title("hardwareLUXX")
root.geometry("520x640")
root.configure(bg=BG)
root.attributes("-topmost", True)
header = tk.Canvas(
root,
height=90,
bg=BG,
highlightthickness=0
)
header.pack(fill="x")
output = tk.Text(
root,
bg=BG,
fg=TEXT,
font=("Monospace", 12),
bd=0,
padx=28,
pady=0,
spacing1=0,
spacing2=0,
spacing3=0,
highlightthickness=0
)
output.tag_config(
"section",
foreground=RED,
font=("Monospace", 12, "bold")
)
output.tag_config(
"normal",
foreground=TEXT,
font=("Monospace", 12)
)
output.pack(fill="both", expand=True)
root.bind(
"<Button-1>",
toggle_topmost
)
draw_header()
update()
root.mainloop()