modified: container/unraid/aoostar-rs-webui.xml
Build And Push Container / build-and-push (push) Successful in 1m45s

modified:   crates/aster-webui/src/system.rs
modified:   crates/aster-webui/src/ui.rs
This commit is contained in:
2026-07-01 00:17:19 +02:00
parent 9c5b309a77
commit 98d11dce68
3 changed files with 60 additions and 45 deletions
+1
View File
@@ -24,6 +24,7 @@
<Config Name="Config Path" Target="/config" Default="/mnt/user/appdata/aoostar-rs/config" Mode="rw" Description="Path containing Monitor3.json and img assets." Type="Path" Display="always" Required="true" Mask="false">/mnt/user/appdata/aoostar-rs/config</Config>
<Config Name="Unraid Cache Mount" Target="/mnt/cache" Default="/mnt/cache" Mode="ro" Description="Read-only bind mount so the WebUI can show real usage for /mnt/cache." Type="Path" Display="advanced" Required="false" Mask="false">/mnt/cache</Config>
<Config Name="Unraid User Mount" Target="/mnt/user" Default="/mnt/user" Mode="ro" Description="Read-only bind mount so the WebUI can show real usage for /mnt/user." Type="Path" Display="advanced" Required="false" Mask="false">/mnt/user</Config>
<Config Name="Host hwmon Mount" Target="/sys/class/hwmon" Default="/sys/class/hwmon" Mode="ro" Description="Optional read-only hwmon bind mount to improve CPU/GPU temperature visibility inside the container." Type="Path" Display="advanced" Required="false" Mask="false">/sys/class/hwmon</Config>
<Config Name="Display Device" Target="/dev/ttyACM0" Default="/dev/ttyACM0" Mode="rw" Description="AOOSTAR display USB serial device." Type="Device" Display="always" Required="true" Mask="false">/dev/ttyACM0</Config>
<Config Name="ASTER_DEVICE" Target="ASTER_DEVICE" Default="/dev/ttyACM0" Mode="" Description="Device path passed into aster-webui." Type="Variable" Display="advanced" Required="false" Mask="false">/dev/ttyACM0</Config>
<Config Name="ASTER_USB" Target="ASTER_USB" Default="" Mode="" Description="Optional USB VID:PID override. Leave blank to use ASTER_DEVICE or the default AOOSTAR USB UART." Type="Variable" Display="advanced" Required="false" Mask="false"></Config>
+29 -31
View File
@@ -1,4 +1,4 @@
use std::sync::OnceLock;
use std::sync::{Mutex, OnceLock};
use ab_glyph::{FontArc, PxScale};
use chrono::Local;
@@ -43,8 +43,16 @@ pub(crate) fn collect_system_view() -> SystemView {
.unwrap_or_else(|| ("n/a".into(), "n/a".into(), "n/a".into()))
}
static SYSTEM: OnceLock<Mutex<System>> = OnceLock::new();
let system_lock = SYSTEM.get_or_init(|| {
let mut sys = System::new_all();
sys.refresh_all();
Mutex::new(sys)
});
let mut sys = system_lock
.lock()
.expect("system sampler mutex must not be poisoned");
sys.refresh_all();
let load_avg = System::load_average();
let total_memory = sys.total_memory();
@@ -184,7 +192,10 @@ fn render_system_panel_cards() -> RgbImage {
84,
"CPU USAGE",
format!("{} %", system.cpu_usage_percent),
format!("Load {}", system.load_avg_one),
format!(
"{} cores | {} procs",
system.cpu_count, system.process_count
),
Rgb([124, 231, 191]),
),
(
@@ -240,34 +251,23 @@ fn render_system_panel_meters() -> RgbImage {
let system = collect_system_view();
let mut canvas = RgbImage::from_pixel(DISPLAY_WIDTH, DISPLAY_HEIGHT, Rgb([9, 13, 17]));
draw_filled_rect_mut(
&mut canvas,
Rect::at(0, 0).of_size(DISPLAY_WIDTH, 56),
Rgb([14, 20, 28]),
);
draw_text_line(
&mut canvas,
Rgb([216, 253, 114]),
24,
14,
28.0,
"HTOP STYLE",
);
draw_meter_row(
&mut canvas,
24,
84,
38,
"CPU USAGE",
&format!("{}%", system.cpu_usage_percent),
parse_metric_percent(&system.cpu_usage_percent),
Rgb([110, 231, 183]),
&format!("Load {} {} cores", system.load_avg_one, system.cpu_count),
&format!(
"{} cores {} procs",
system.cpu_count, system.process_count
),
);
draw_meter_row(
&mut canvas,
24,
178,
150,
"RAM USAGE",
&format!("{}%", system.mem_usage_percent),
parse_metric_percent(&system.mem_usage_percent),
@@ -277,7 +277,7 @@ fn render_system_panel_meters() -> RgbImage {
draw_meter_row(
&mut canvas,
24,
272,
262,
"GPU TEMP",
system.temperature_gpu.as_deref().unwrap_or("-"),
normalize_temperature_bar(system.temperature_gpu.as_deref()),
@@ -306,11 +306,11 @@ pub(crate) fn overlay_system_specs(canvas: &mut RgbImage) {
28,
22.0,
&format!(
"{host_name} CPU {}% RAM {}% C {}% U {}%",
"{host_name} CPU {}% RAM {}% GPU {} C {}%",
system.cpu_usage_percent,
system.mem_usage_percent,
system.temperature_gpu.as_deref().unwrap_or("-"),
system.cache_usage_percent,
system.user_usage_percent
),
);
draw_text_line(
@@ -320,12 +320,10 @@ pub(crate) fn overlay_system_specs(canvas: &mut RgbImage) {
52,
16.0,
&format!(
"Load {} / {} / {} Temp {} / {} Uptime {} {}",
system.load_avg_one,
system.load_avg_five,
system.load_avg_fifteen,
"CPU {} Swap {}% User {}% Uptime {} {}",
system.temperature_cpu.as_deref().unwrap_or("-"),
system.temperature_gpu.as_deref().unwrap_or("-"),
system.swap_usage_percent,
system.user_usage_percent,
system.uptime,
timestamp
),
@@ -370,10 +368,10 @@ fn draw_meter_row(
accent: Rgb<u8>,
meta: &str,
) {
draw_text_line(canvas, accent, x, y, 21.0, label);
draw_text_line(canvas, Rgb([237, 242, 247]), x + 248, y, 21.0, value);
draw_meter_bar(canvas, x, y + 32, 900, 24, percent, accent);
draw_text_line(canvas, Rgb([146, 163, 181]), x, y + 64, 18.0, meta);
draw_text_line(canvas, accent, x, y, 28.0, label);
draw_text_line(canvas, Rgb([237, 242, 247]), x + 600, y, 28.0, value);
draw_meter_bar(canvas, x, y + 42, 900, 30, percent, accent);
draw_text_line(canvas, Rgb([146, 163, 181]), x, y + 82, 20.0, meta);
}
fn draw_meter_bar(
+29 -13
View File
@@ -93,8 +93,21 @@ const INDEX_HTML: &str = r##"<!DOCTYPE html>
letter-spacing: 0;
}
.brand p { margin: 3px 0 0; color: var(--muted); font-size: .9rem; }
.layout { display: grid; grid-template-columns: minmax(0, 1.45fr) 520px; gap: 14px; }
.layout {
display: grid;
grid-template-columns: minmax(0, 1.45fr) 520px;
gap: 14px;
align-items: stretch;
}
.stack { display: grid; gap: 16px; }
.layout > .stack {
height: 100%;
}
.layout > .stack > .panel {
height: 100%;
display: flex;
flex-direction: column;
}
.tabbar {
display: inline-flex;
gap: 6px;
@@ -234,10 +247,13 @@ const INDEX_HTML: &str = r##"<!DOCTYPE html>
.status-grid {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
grid-template-rows: repeat(3, minmax(0, 1fr));
gap: 10px;
height: 100%;
align-self: stretch;
}
.status {
min-height: 84px;
min-height: 0;
display: grid;
align-content: space-between;
background: rgba(16, 22, 30, .86);
@@ -582,8 +598,8 @@ const INDEX_HTML: &str = r##"<!DOCTYPE html>
<div class="card metric-card"><span class="metric-label">Memory</span><strong id="memUsageValue">-</strong><small id="memMetaValue">-</small></div>
<div class="card metric-card"><span class="metric-label">Storage</span><strong id="diskUsageValue">-</strong><small id="diskMetaValue">-</small></div>
<div class="card metric-card"><span class="metric-label">Swap</span><strong id="swapUsageValue">-</strong><small id="swapMetaValue">-</small></div>
<div class="card metric-card"><span class="metric-label">Load Avg</span><strong id="loadValue">-</strong><small id="uptimeValue">-</small></div>
<div class="card metric-card"><span class="metric-label">Temperatures</span><strong id="tempCpuValue">-</strong><small id="tempGpuValue">-</small></div>
<div class="card metric-card"><span class="metric-label">GPU</span><strong id="gpuValue">-</strong><small id="gpuMetaValue">-</small></div>
<div class="card metric-card"><span class="metric-label">System</span><strong id="systemValue">-</strong><small id="systemMetaValue">-</small></div>
</section>
</section>
</section>
@@ -634,10 +650,10 @@ const INDEX_HTML: &str = r##"<!DOCTYPE html>
diskMetaValue: document.getElementById("diskMetaValue"),
swapUsageValue: document.getElementById("swapUsageValue"),
swapMetaValue: document.getElementById("swapMetaValue"),
loadValue: document.getElementById("loadValue"),
uptimeValue: document.getElementById("uptimeValue"),
tempCpuValue: document.getElementById("tempCpuValue"),
tempGpuValue: document.getElementById("tempGpuValue"),
gpuValue: document.getElementById("gpuValue"),
gpuMetaValue: document.getElementById("gpuMetaValue"),
systemValue: document.getElementById("systemValue"),
systemMetaValue: document.getElementById("systemMetaValue"),
upload: document.getElementById("upload"),
apply: document.getElementById("apply"),
disable: document.getElementById("disable"),
@@ -784,17 +800,17 @@ const INDEX_HTML: &str = r##"<!DOCTYPE html>
el.displayErrorValue.textContent = state.display.last_error || "";
el.configPathValue.textContent = data.monitor_path;
el.cpuUsageValue.textContent = `${state.system.cpu_usage_percent || "-"} %`;
el.cpuMetaValue.textContent = `Load ${state.system.load_avg_one || "-"} / ${state.system.load_avg_five || "-"} / ${state.system.load_avg_fifteen || "-"}`;
el.cpuMetaValue.textContent = `${state.system.cpu_count || "-"} cores • ${state.system.process_count || "-"} procs`;
el.memUsageValue.textContent = `${state.system.mem_usage_percent || "-"} %`;
el.memMetaValue.textContent = `${state.system.mem_used || "-"} / ${state.system.mem_total || "-"}`;
el.diskUsageValue.innerHTML = `/mnt/cache ${state.system.cache_usage_percent || "-"}%<br>/mnt/user ${state.system.user_usage_percent || "-"}%`;
el.diskMetaValue.textContent = `${state.system.cache_used || "-"} / ${state.system.cache_total || "-"} | ${state.system.user_used || "-"} / ${state.system.user_total || "-"}`;
el.swapUsageValue.textContent = `${state.system.swap_usage_percent || "-"} %`;
el.swapMetaValue.textContent = `${state.system.swap_used || "-"} / ${state.system.swap_total || "-"}`;
el.loadValue.textContent = `${state.system.load_avg_one || "-"} / ${state.system.load_avg_five || "-"} / ${state.system.load_avg_fifteen || "-"}`;
el.uptimeValue.textContent = `${state.system.cpu_count || "-"} cores • ${state.system.process_count || "-"} procs • Uptime ${state.system.uptime || "-"}`;
el.tempCpuValue.textContent = `CPU ${state.system.temperature_cpu || "-"}`;
el.tempGpuValue.textContent = `GPU ${state.system.temperature_gpu || "-"}`;
el.gpuValue.textContent = state.system.temperature_gpu || "-";
el.gpuMetaValue.textContent = `CPU ${state.system.temperature_cpu || "-"}`;
el.systemValue.textContent = state.system.uptime || "-";
el.systemMetaValue.textContent = `${state.system.cache_usage_percent || "-"}% cache • ${state.system.user_usage_percent || "-"}% user`;
el.displayStatusMirror.textContent = el.displayStatusValue.textContent;
el.displayTargetMirror.textContent = state.display.device || "-";
el.customPanelMirror.textContent = el.customPanelValue.textContent;