From 17791421c49f1202f7a48545e23eb3baabfb072e Mon Sep 17 00:00:00 2001 From: Admin Pupkin Date: Wed, 20 May 2026 22:20:47 +0300 Subject: [PATCH] redbear-info: Add thermal, fan, and C-state health dashboard items Reads from /scheme/acpi/thermal/, /scheme/acpi/fan/, and /scheme/acpi/cstates/ plus /scheme/sys/cstate_policy to populate the --health dashboard with hardware thermal status, fan activity, and CPU power-management state. --- .../system/redbear-info/source/src/main.rs | 70 +++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/local/recipes/system/redbear-info/source/src/main.rs b/local/recipes/system/redbear-info/source/src/main.rs index f0966b0f1b..ef6e6d1780 100644 --- a/local/recipes/system/redbear-info/source/src/main.rs +++ b/local/recipes/system/redbear-info/source/src/main.rs @@ -2859,6 +2859,76 @@ fn collect_health_items(runtime: &Runtime, report: &Report<'_>) -> Vec = thermal_zones + .iter() + .filter_map(|zone| { + read_trimmed(runtime, &format!("/scheme/acpi/thermal/{zone}/temperature")) + }) + .collect(); + let avg_temp = temps.iter().filter_map(|t| t.parse::().ok()).sum::() + / temps.len().max(1) as f64; + let state = if avg_temp > 85.0 { + HealthState::Critical + } else if avg_temp > 70.0 { + HealthState::Warning + } else { + HealthState::Healthy + }; + items.push(HealthItem { + label: "Thermal", + state, + detail: format!("{} zone(s), avg {:.1}°C", thermal_zones.len(), avg_temp), + }); + } else { + items.push(HealthItem { + label: "Thermal", + state: HealthState::Warning, + detail: "no thermal zones".to_string(), + }); + } + + let fans = runtime.read_dir_names("/scheme/acpi/fan").unwrap_or_default(); + if !fans.is_empty() { + let active = fans + .iter() + .filter(|fan| { + read_trimmed(runtime, &format!("/scheme/acpi/fan/{fan}/status")) + .map(|s| s == "on") + .unwrap_or(false) + }) + .count(); + items.push(HealthItem { + label: "Fans", + state: HealthState::Healthy, + detail: format!("{} fan(s), {} active", fans.len(), active), + }); + } else { + items.push(HealthItem { + label: "Fans", + state: HealthState::Warning, + detail: "no fan devices".to_string(), + }); + } + + let cstate_policy = read_trimmed(runtime, "/scheme/sys/cstate_policy"); + let cstates = runtime.read_dir_names("/scheme/acpi/cstates").unwrap_or_default(); + if !cstates.is_empty() { + let max_policy = cstate_policy.as_deref().unwrap_or("unlimited"); + items.push(HealthItem { + label: "C-states", + state: HealthState::Healthy, + detail: format!("{} processor(s), policy={}", cstates.len(), max_policy), + }); + } else { + items.push(HealthItem { + label: "C-states", + state: HealthState::Warning, + detail: "no C-state surface".to_string(), + }); + } + items }