Improve NVMe serial parsing and restructure MergerFS display
All checks were successful
Build and Release / build-and-release (push) Successful in 1m25s

- Fix NVMe serial number parsing to handle whitespace variations
- Move mount point to MergerFS header, remove drive count
- Restructure data drives to same level as parity with Data_1, Data_2 labels
- Remove "Total:" label from pool usage line
- Update parity to use closing tree symbol as last item
This commit is contained in:
Christoffer Martinsson 2025-11-25 11:28:54 +01:00
parent dc1105eefe
commit 267654fda4
6 changed files with 40 additions and 33 deletions

6
Cargo.lock generated
View File

@ -279,7 +279,7 @@ checksum = "a1d728cc89cf3aee9ff92b05e62b19ee65a02b5702cff7d5a377e32c6ae29d8d"
[[package]] [[package]]
name = "cm-dashboard" name = "cm-dashboard"
version = "0.1.155" version = "0.1.156"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"chrono", "chrono",
@ -301,7 +301,7 @@ dependencies = [
[[package]] [[package]]
name = "cm-dashboard-agent" name = "cm-dashboard-agent"
version = "0.1.155" version = "0.1.156"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"async-trait", "async-trait",
@ -324,7 +324,7 @@ dependencies = [
[[package]] [[package]]
name = "cm-dashboard-shared" name = "cm-dashboard-shared"
version = "0.1.155" version = "0.1.156"
dependencies = [ dependencies = [
"chrono", "chrono",
"serde", "serde",

View File

@ -1,6 +1,6 @@
[package] [package]
name = "cm-dashboard-agent" name = "cm-dashboard-agent"
version = "0.1.155" version = "0.1.156"
edition = "2021" edition = "2021"
[dependencies] [dependencies]

View File

@ -457,11 +457,15 @@ impl DiskCollector {
} }
} }
// Serial number parsing // Serial number parsing (both SATA and NVMe)
if line.starts_with("Serial Number:") { if line.contains("Serial Number:") {
if let Some(serial_part) = line.split("Serial Number:").nth(1) { if let Some(serial_part) = line.split("Serial Number:").nth(1) {
if let Some(serial_str) = serial_part.split_whitespace().next() { let serial_str = serial_part.trim();
serial_number = Some(serial_str.to_string()); if !serial_str.is_empty() {
// Take first whitespace-separated token
if let Some(serial) = serial_str.split_whitespace().next() {
serial_number = Some(serial.to_string());
}
} }
} }
} }

View File

@ -1,6 +1,6 @@
[package] [package]
name = "cm-dashboard" name = "cm-dashboard"
version = "0.1.155" version = "0.1.156"
edition = "2021" edition = "2021"
[dependencies] [dependencies]

View File

@ -391,8 +391,8 @@ impl SystemWidget {
pool.name.clone() pool.name.clone()
} }
} else { } else {
// For mergerfs pools, show pool name with format like "mergerfs (2+1):" // For mergerfs pools, show pool type with mount point
format!("{}:", pool.pool_type) format!("mergerfs {}:", pool.mount_point)
}; };
let pool_spans = StatusIcons::create_status_spans(pool.status.clone(), &pool_label); let pool_spans = StatusIcons::create_status_spans(pool.status.clone(), &pool_label);
@ -431,7 +431,7 @@ impl SystemWidget {
// └─ Mount: /srv/media // └─ Mount: /srv/media
// Pool total usage // Pool total usage
let total_text = format!("Total: {:.0}% {:.1}GB/{:.1}GB", let total_text = format!("{:.0}% {:.1}GB/{:.1}GB",
pool.usage_percent.unwrap_or(0.0), pool.usage_percent.unwrap_or(0.0),
pool.used_gb.unwrap_or(0.0), pool.used_gb.unwrap_or(0.0),
pool.total_gb.unwrap_or(0.0) pool.total_gb.unwrap_or(0.0)
@ -442,20 +442,30 @@ impl SystemWidget {
total_spans.extend(StatusIcons::create_status_spans(Status::Ok, &total_text)); total_spans.extend(StatusIcons::create_status_spans(Status::Ok, &total_text));
lines.push(Line::from(total_spans)); lines.push(Line::from(total_spans));
// Data Disks section // Data drives - at same level as parity
if !pool.data_drives.is_empty() { for (i, drive) in pool.data_drives.iter().enumerate() {
lines.push(Line::from(vec![ let mut drive_details = Vec::new();
Span::styled(" ├─ ", Typography::tree()), if let Some(temp) = drive.temperature {
Span::styled("Data Disks:", Typography::secondary()) drive_details.push(format!("T: {}°C", temp as i32));
]));
for (i, drive) in pool.data_drives.iter().enumerate() {
let is_last = i == pool.data_drives.len() - 1;
let tree_symbol = if is_last { " │ └─ " } else { " │ ├─ " };
render_mergerfs_drive(drive, tree_symbol, &mut lines);
} }
if let Some(wear) = drive.wear_percent {
drive_details.push(format!("W: {}%", wear as i32));
}
let drive_text = if !drive_details.is_empty() {
format!("Data_{}: {} {}", i + 1, drive.name, drive_details.join(" "))
} else {
format!("Data_{}: {}", i + 1, drive.name)
};
let mut data_spans = vec![
Span::styled(" ├─ ", Typography::tree()),
];
data_spans.extend(StatusIcons::create_status_spans(drive.status.clone(), &drive_text));
lines.push(Line::from(data_spans));
} }
// Parity section // Parity drives - last item
if !pool.parity_drives.is_empty() { if !pool.parity_drives.is_empty() {
for drive in &pool.parity_drives { for drive in &pool.parity_drives {
let mut drive_details = Vec::new(); let mut drive_details = Vec::new();
@ -465,7 +475,7 @@ impl SystemWidget {
if let Some(wear) = drive.wear_percent { if let Some(wear) = drive.wear_percent {
drive_details.push(format!("W: {}%", wear as i32)); drive_details.push(format!("W: {}%", wear as i32));
} }
let drive_text = if !drive_details.is_empty() { let drive_text = if !drive_details.is_empty() {
format!("Parity: {} {}", drive.name, drive_details.join(" ")) format!("Parity: {} {}", drive.name, drive_details.join(" "))
} else { } else {
@ -473,19 +483,12 @@ impl SystemWidget {
}; };
let mut parity_spans = vec![ let mut parity_spans = vec![
Span::styled(" ", Typography::tree()), Span::styled(" ", Typography::tree()),
]; ];
parity_spans.extend(StatusIcons::create_status_spans(drive.status.clone(), &drive_text)); parity_spans.extend(StatusIcons::create_status_spans(drive.status.clone(), &drive_text));
lines.push(Line::from(parity_spans)); lines.push(Line::from(parity_spans));
} }
} }
// Mount point
lines.push(Line::from(vec![
Span::styled(" └─ ", Typography::tree()),
Span::styled("Mount: ", Typography::secondary()),
Span::styled(&pool.mount_point, Typography::secondary())
]));
} }
} }

View File

@ -1,6 +1,6 @@
[package] [package]
name = "cm-dashboard-shared" name = "cm-dashboard-shared"
version = "0.1.155" version = "0.1.156"
edition = "2021" edition = "2021"
[dependencies] [dependencies]