acpitool/
lib.rs

1use std::error::Error;
2use std::path;
3
4pub struct Config {
5    pub acpi_path: path::PathBuf,
6    pub show_battery: bool,
7    pub show_ac_adapter: bool,
8    pub show_thermal_sensors: bool,
9    pub show_cooling_devices: bool,
10    pub detailed: bool,
11    pub units: acpi_client::Units,
12}
13
14pub fn run(cfg: Config) -> Result<(), Box<dyn Error>> {
15    if cfg.show_battery {
16        let batteries: Vec<acpi_client::BatteryInfo> =
17            match acpi_client::get_battery_info(&cfg.acpi_path.join("power_supply")) {
18                Ok(bat) => bat,
19                Err(e) => {
20                    eprintln!("Application error: {}", e);
21                    std::process::exit(1);
22                }
23            };
24        for bat in batteries {
25            display_battery_info(&bat, cfg.detailed);
26        }
27    }
28    if cfg.show_ac_adapter {
29        let adapters: Vec<acpi_client::ACAdapterInfo> =
30            match acpi_client::get_ac_adapter_info(&cfg.acpi_path.join("power_supply")) {
31                Ok(ac) => ac,
32                Err(e) => {
33                    eprintln!("Application error: {}", e);
34                    std::process::exit(1);
35                }
36            };
37        for ac in adapters {
38            display_ac_adapter_info(&ac);
39        }
40    }
41    if cfg.show_thermal_sensors {
42        let sensors: Vec<acpi_client::ThermalSensor> =
43            match acpi_client::get_thermal_sensor_info(&cfg.acpi_path.join("thermal"), cfg.units) {
44                Ok(tz) => tz,
45                Err(e) => {
46                    eprintln!("Application error: {}", e);
47                    std::process::exit(1);
48                }
49            };
50        for tz in sensors {
51            display_thermal_zone_info(&tz, cfg.detailed);
52        }
53    }
54    if cfg.show_cooling_devices {
55        let devices: Vec<acpi_client::CoolingDevice> =
56            match acpi_client::get_cooling_device_info(&cfg.acpi_path.join("thermal")) {
57                Ok(cd) => cd,
58                Err(e) => {
59                    eprintln!("Application error: {}", e);
60                    std::process::exit(1);
61                }
62            };
63        for cd in devices {
64            display_cooling_device_info(&cd);
65        }
66    }
67
68    Ok(())
69}
70
71fn display_battery_info(bat: &acpi_client::BatteryInfo, detailed: bool) {
72    let state = match &bat.state {
73        acpi_client::ChargingState::Charging => "Charging",
74        acpi_client::ChargingState::Discharging => "Discharging",
75        acpi_client::ChargingState::Full => "Full",
76    };
77    let mut seconds = bat.time_remaining.as_secs();
78    let hours = seconds / 3600;
79    seconds = seconds - hours * 3600;
80    let minutes = seconds / 60;
81    seconds = seconds - minutes * 60;
82    let not_full_string = format!(", {:02}:{:02}:{:02}", hours, minutes, seconds);
83    let charge_time_string = match &bat.state {
84        acpi_client::ChargingState::Charging => {
85            if bat.present_rate > 0 {
86                format!("{} {}", not_full_string, "until charged")
87            } else {
88                format!(", charging at zero rate")
89            }
90        }
91        acpi_client::ChargingState::Discharging => format!("{} {}", not_full_string, "remaining"),
92        _ => String::from(""),
93    };
94    println!(
95        "{}: {}, {:.1}%{}",
96        &bat.name, state, bat.percentage, charge_time_string
97    );
98
99    if detailed {
100        println!(
101            "{}: design capacity {} mAh, last full capacity {} mAh = {}%",
102            &bat.name,
103            bat.design_capacity,
104            bat.last_capacity,
105            (100 * bat.last_capacity) / bat.design_capacity
106        );
107    }
108}
109
110fn display_ac_adapter_info(ac: &acpi_client::ACAdapterInfo) {
111    let status_str = match ac.status {
112        acpi_client::Status::Online => "online",
113        acpi_client::Status::Offline => "offline",
114    };
115    println!("{}: {}", &ac.name, status_str);
116}
117
118fn display_thermal_zone_info(tz: &acpi_client::ThermalSensor, detailed: bool) {
119    let temperature_str = match tz.units {
120        acpi_client::Units::Celsius => "degrees C",
121        acpi_client::Units::Fahrenheit => "degrees F",
122        acpi_client::Units::Kelvin => "kelvin",
123    };
124    println!(
125        "{}: {:.1} {}",
126        &tz.name, tz.current_temperature, temperature_str
127    );
128
129    if detailed {
130        for tp in &tz.trip_points {
131            println!("{}: trip point {} switches to mode {} at temperature {:.1} {}", &tz.name, tp.number, &tp.action_type, tp.temperature, temperature_str);
132        }
133    }
134}
135
136fn display_cooling_device_info(cd: &acpi_client::CoolingDevice) {
137    let state_str = if cd.state.is_some() {
138        format!("{} of {}", cd.state.unwrap().current_state, cd.state.unwrap().max_state)
139    } else {
140        format!("no state information available")
141    };
142    println!("{}: {} {}", &cd.name, &cd.device_type, state_str);
143}