khora_infra/platform/
sysinfo_impl.rs

1// Copyright 2025 eraflo
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! sysinfo-based implementation of the HardwareMonitor trait.
16
17use khora_core::platform::{BatteryLevel, HardwareMonitor, ThermalStatus};
18use std::sync::{Arc, Mutex};
19use sysinfo::{Components, System};
20
21/// A hardware monitor that uses the `sysinfo` crate.
22pub struct SysinfoMonitor {
23    system: Arc<Mutex<System>>,
24}
25
26impl SysinfoMonitor {
27    /// Creates a new SysinfoMonitor.
28    pub fn new() -> Self {
29        let mut system = System::new_all();
30        system.refresh_all();
31        Self {
32            system: Arc::new(Mutex::new(system)),
33        }
34    }
35
36    /// Refreshes the underlying system data.
37    pub fn refresh(&self) {
38        if let Ok(mut system) = self.system.lock() {
39            system.refresh_cpu_all();
40            // refresh_components is now handled by new_with_refreshed_list in thermal_status or similar
41        }
42    }
43}
44
45impl HardwareMonitor for SysinfoMonitor {
46    fn thermal_status(&self) -> ThermalStatus {
47        let components = Components::new_with_refreshed_list();
48        let mut max_temp = 0.0;
49
50        for component in &components {
51            let label = component.label().to_lowercase();
52            if label.contains("cpu") || label.contains("core") {
53                if let Some(temp) = component.temperature() {
54                    max_temp = f32::max(max_temp, temp);
55                }
56            }
57        }
58
59        if max_temp == 0.0 {
60            return ThermalStatus::Cool; // Unknown or unavailable
61        }
62
63        if max_temp > 90.0 {
64            ThermalStatus::Critical
65        } else if max_temp > 80.0 {
66            ThermalStatus::Throttling
67        } else if max_temp > 60.0 {
68            ThermalStatus::Warm
69        } else {
70            ThermalStatus::Cool
71        }
72    }
73
74    fn battery_level(&self) -> BatteryLevel {
75        // sysinfo doesn't easily expose battery on all platforms via System.
76        // For v0.1 we return Mains.
77        BatteryLevel::Mains
78    }
79
80    fn cpu_load(&self) -> f32 {
81        if let Ok(system) = self.system.lock() {
82            system.global_cpu_usage() / 100.0
83        } else {
84            0.0
85        }
86    }
87}
88
89impl Default for SysinfoMonitor {
90    fn default() -> Self {
91        Self::new()
92    }
93}