summaryrefslogtreecommitdiff
path: root/services/SystemUsage.qml
diff options
context:
space:
mode:
Diffstat (limited to 'services/SystemUsage.qml')
-rw-r--r--services/SystemUsage.qml165
1 files changed, 141 insertions, 24 deletions
diff --git a/services/SystemUsage.qml b/services/SystemUsage.qml
index bd02da3..1144932 100644
--- a/services/SystemUsage.qml
+++ b/services/SystemUsage.qml
@@ -8,24 +8,50 @@ import QtQuick
Singleton {
id: root
+ // CPU properties
+ property string cpuName: ""
property real cpuPerc
property real cpuTemp
+
+ // GPU properties
readonly property string gpuType: Config.services.gpuType.toUpperCase() || autoGpuType
property string autoGpuType: "NONE"
+ property string gpuName: ""
property real gpuPerc
property real gpuTemp
+
+ // Memory properties
property real memUsed
property real memTotal
readonly property real memPerc: memTotal > 0 ? memUsed / memTotal : 0
- property real storageUsed
- property real storageTotal
- property real storagePerc: storageTotal > 0 ? storageUsed / storageTotal : 0
+
+ // Storage properties (aggregated)
+ readonly property real storagePerc: {
+ let totalUsed = 0;
+ let totalSize = 0;
+ for (const disk of disks) {
+ totalUsed += disk.used;
+ totalSize += disk.total;
+ }
+ return totalSize > 0 ? totalUsed / totalSize : 0;
+ }
+
+ // Individual disks: Array of { mount, used, total, free, perc }
+ property var disks: []
property real lastCpuIdle
property real lastCpuTotal
property int refCount
+ function cleanCpuName(name: string): string {
+ return name.replace(/\(R\)/gi, "").replace(/\(TM\)/gi, "").replace(/CPU/gi, "").replace(/\d+th Gen /gi, "").replace(/\d+nd Gen /gi, "").replace(/\d+rd Gen /gi, "").replace(/\d+st Gen /gi, "").replace(/Core /gi, "").replace(/Processor/gi, "").replace(/\s+/g, " ").trim();
+ }
+
+ function cleanGpuName(name: string): string {
+ return name.replace(/NVIDIA GeForce /gi, "").replace(/NVIDIA /gi, "").replace(/AMD Radeon /gi, "").replace(/AMD /gi, "").replace(/Intel /gi, "").replace(/\(R\)/gi, "").replace(/\(TM\)/gi, "").replace(/Graphics/gi, "").replace(/\s+/g, " ").trim();
+ }
+
function formatKib(kib: real): var {
const mib = 1024;
const gib = 1024 ** 2;
@@ -54,7 +80,7 @@ Singleton {
Timer {
running: root.refCount > 0
- interval: 3000
+ interval: Config.dashboard.resourceUpdateInterval
repeat: true
triggeredOnStart: true
onTriggered: {
@@ -66,6 +92,18 @@ Singleton {
}
}
+ // One-time CPU info detection (name)
+ FileView {
+ id: cpuinfoInit
+
+ path: "/proc/cpuinfo"
+ onLoaded: {
+ const nameMatch = text().match(/model name\s*:\s*(.+)/);
+ if (nameMatch)
+ root.cpuName = root.cleanCpuName(nameMatch[1]);
+ }
+ }
+
FileView {
id: stat
@@ -101,41 +139,120 @@ Singleton {
Process {
id: storage
- command: ["sh", "-c", "df | grep '^/dev/' | awk '{print $1, $3, $4}'"]
+ // Get physical disks with aggregated usage from their partitions
+ // lsblk outputs: NAME SIZE TYPE FSUSED FSSIZE in bytes
+ command: ["lsblk", "-b", "-o", "NAME,SIZE,TYPE,FSUSED,FSSIZE", "-P"]
stdout: StdioCollector {
onStreamFinished: {
- const deviceMap = new Map();
+ const diskMap = {}; // Map disk name -> { name, totalSize, used, fsTotal }
+ const lines = text.trim().split("\n");
- for (const line of text.trim().split("\n")) {
+ for (const line of lines) {
if (line.trim() === "")
continue;
- const parts = line.trim().split(/\s+/);
- if (parts.length >= 3) {
- const device = parts[0];
- const used = parseInt(parts[1], 10) || 0;
- const avail = parseInt(parts[2], 10) || 0;
+ // Parse KEY="VALUE" format
+ const nameMatch = line.match(/NAME="([^"]+)"/);
+ const sizeMatch = line.match(/SIZE="([^"]+)"/);
+ const typeMatch = line.match(/TYPE="([^"]+)"/);
+ const fsusedMatch = line.match(/FSUSED="([^"]*)"/);
+ const fssizeMatch = line.match(/FSSIZE="([^"]*)"/);
+
+ if (!nameMatch || !typeMatch)
+ continue;
+
+ const name = nameMatch[1];
+ const type = typeMatch[1];
+ const size = parseInt(sizeMatch?.[1] || "0", 10);
+ const fsused = parseInt(fsusedMatch?.[1] || "0", 10);
+ const fssize = parseInt(fssizeMatch?.[1] || "0", 10);
+
+ if (type === "disk") {
+ // Skip zram (swap) devices
+ if (name.startsWith("zram"))
+ continue;
- // Only keep the entry with the largest total space for each device
- if (!deviceMap.has(device) || (used + avail) > (deviceMap.get(device).used + deviceMap.get(device).avail)) {
- deviceMap.set(device, {
- used: used,
- avail: avail
- });
+ // Initialize disk entry
+ if (!diskMap[name]) {
+ diskMap[name] = {
+ name: name,
+ totalSize: size,
+ used: 0,
+ fsTotal: 0
+ };
+ }
+ } else if (type === "part") {
+ // Find parent disk (remove trailing numbers/p+numbers)
+ let parentDisk = name.replace(/p?\d+$/, "");
+ // For nvme devices like nvme0n1p1, parent is nvme0n1
+ if (name.match(/nvme\d+n\d+p\d+/))
+ parentDisk = name.replace(/p\d+$/, "");
+
+ // Aggregate partition usage to parent disk
+ if (diskMap[parentDisk]) {
+ diskMap[parentDisk].used += fsused;
+ diskMap[parentDisk].fsTotal += fssize;
}
}
}
+ // Convert map to sorted array
+ const diskList = [];
let totalUsed = 0;
- let totalAvail = 0;
+ let totalSize = 0;
+
+ for (const diskName of Object.keys(diskMap).sort()) {
+ const disk = diskMap[diskName];
+ // Use filesystem total if available, otherwise use disk size
+ const total = disk.fsTotal > 0 ? disk.fsTotal : disk.totalSize;
+ const used = disk.used;
+ const perc = total > 0 ? used / total : 0;
- for (const [device, stats] of deviceMap) {
- totalUsed += stats.used;
- totalAvail += stats.avail;
+ // Convert bytes to KiB for consistency with formatKib
+ diskList.push({
+ mount: disk.name // Using 'mount' property for compatibility
+ ,
+ used: used / 1024,
+ total: total / 1024,
+ free: (total - used) / 1024,
+ perc: perc
+ });
+
+ totalUsed += used;
+ totalSize += total;
}
- root.storageUsed = totalUsed;
- root.storageTotal = totalUsed + totalAvail;
+ root.disks = diskList;
+ }
+ }
+ }
+
+ // GPU name detection (one-time)
+ Process {
+ id: gpuNameDetect
+
+ running: true
+ command: ["sh", "-c", "nvidia-smi --query-gpu=name --format=csv,noheader 2>/dev/null || lspci 2>/dev/null | grep -i 'vga\\|3d\\|display' | head -1"]
+ stdout: StdioCollector {
+ onStreamFinished: {
+ const output = text.trim();
+ if (!output)
+ return;
+
+ // Check if it's from nvidia-smi (clean GPU name)
+ if (output.toLowerCase().includes("nvidia") || output.toLowerCase().includes("geforce") || output.toLowerCase().includes("rtx") || output.toLowerCase().includes("gtx")) {
+ root.gpuName = root.cleanGpuName(output);
+ } else {
+ // Parse lspci output: extract name from brackets or after colon
+ const bracketMatch = output.match(/\[([^\]]+)\]/);
+ if (bracketMatch) {
+ root.gpuName = root.cleanGpuName(bracketMatch[1]);
+ } else {
+ const colonMatch = output.match(/:\s*(.+)/);
+ if (colonMatch)
+ root.gpuName = root.cleanGpuName(colonMatch[1]);
+ }
+ }
}
}
}