Skip to main content

framework_tool_tui/framework/
info.rs

1use dmidecode::Structure;
2use framework_lib::chromium_ec::commands::FpLedBrightnessLevel;
3use framework_lib::power::PowerInfo;
4use framework_lib::power::UsbChargingType;
5use framework_lib::power::UsbPdPowerInfo;
6use framework_lib::power::UsbPowerRoles;
7use framework_lib::smbios::Platform;
8use framework_lib::smbios::SmbiosStore;
9
10#[derive(Default)]
11pub struct FrameworkInfo {
12    pub charge_percentage: Option<u32>,
13    pub charger_voltage: Option<u32>,
14    pub charger_current: Option<u32>,
15    pub design_capacity: Option<u32>,
16    pub last_full_charge_capacity: Option<u32>,
17    pub cycle_count: Option<u32>,
18    pub capacity_loss_percentage: Option<f32>,
19    pub capacity_loss_per_cycle: Option<f32>,
20    pub is_charging: bool,
21    pub is_ac_connected: bool,
22    pub charging_status: &'static str,
23    pub max_charge_limit: Option<u8>,
24    pub is_microphone_enabled: bool,
25    pub is_camera_enabled: bool,
26    pub fp_brightness_percentage: Option<u8>,
27    // pub fp_brightness_level: Option<FpLedBrightnessLevel>,
28    pub kb_brightness_percentage: Option<u8>,
29    pub smbios_version: Option<String>,
30    pub smbios_release_date: Option<String>,
31    pub smbios_vendor: Option<String>,
32    pub pd_ports: PdPortsInfo,
33    pub fan_rpm: Option<Vec<u16>>,
34    pub platform: Option<Platform>,
35}
36
37impl FrameworkInfo {
38    #[allow(clippy::too_many_arguments)]
39    pub fn new(
40        power: &Option<PowerInfo>,
41        charge_limit: &Option<(u8, u8)>,
42        privacy: &Option<(bool, bool)>,
43        fp_brightness: &Option<(u8, Option<FpLedBrightnessLevel>)>,
44        kb_brightness: Option<u8>,
45        smbios: &Option<SmbiosStore>,
46        pd_ports: Vec<Option<UsbPdPowerInfo>>,
47        fan_rpm: Option<Vec<u16>>,
48        platform: Option<Platform>,
49    ) -> Self {
50        Self {
51            charge_percentage: charge_percentage(power),
52            charger_voltage: charger_voltage(power),
53            charger_current: charger_current(power),
54            design_capacity: design_capacity(power),
55            last_full_charge_capacity: last_full_charge_capacity(power),
56            cycle_count: cycle_count(power),
57            capacity_loss_percentage: capacity_loss_percentage(power),
58            capacity_loss_per_cycle: capacity_loss_per_cycle(power),
59            is_charging: is_charging(power),
60            is_ac_connected: is_ac_connected(power),
61            charging_status: charging_status(power),
62            max_charge_limit: max_charge_limit(charge_limit),
63            is_microphone_enabled: is_microphone_enabled(privacy),
64            is_camera_enabled: is_camera_enabled(privacy),
65            fp_brightness_percentage: fp_brightness_percentage(fp_brightness),
66            // fp_brightness_level: fp_brightness_level(fp_brightness),
67            kb_brightness_percentage: kb_brightness_percentage(kb_brightness),
68            smbios_version: smbios_version(smbios),
69            smbios_release_date: smbios_release_date(smbios),
70            smbios_vendor: smbios_vendor(smbios),
71            pd_ports: pd_ports_info(pd_ports),
72            fan_rpm,
73            platform,
74        }
75    }
76}
77
78#[derive(Default)]
79pub struct PdPortsInfo {
80    pub left_back: Option<PdPortInfo>,
81    pub left_front: Option<PdPortInfo>,
82    pub right_back: Option<PdPortInfo>,
83    pub right_front: Option<PdPortInfo>,
84}
85
86#[derive(Default)]
87pub struct PdPortInfo {
88    pub role: String,
89    pub dualrole: String,
90    pub charging_type: String,
91    pub max_power: u32,
92    pub voltage_now: f32,
93    pub voltage_max: f32,
94    pub current_limit: u16,
95    pub current_max: u16,
96}
97
98fn charge_percentage(power: &Option<PowerInfo>) -> Option<u32> {
99    power.as_ref().and_then(|power| {
100        power
101            .battery
102            .as_ref()
103            .map(|battery| battery.charge_percentage)
104    })
105}
106
107fn charger_voltage(power: &Option<PowerInfo>) -> Option<u32> {
108    power.as_ref().and_then(|power| {
109        power
110            .battery
111            .as_ref()
112            .map(|battery| battery.present_voltage)
113    })
114}
115
116fn charger_current(power: &Option<PowerInfo>) -> Option<u32> {
117    power
118        .as_ref()
119        .and_then(|power| power.battery.as_ref().map(|battery| battery.present_rate))
120}
121
122fn design_capacity(power: &Option<PowerInfo>) -> Option<u32> {
123    power.as_ref().and_then(|power| {
124        power
125            .battery
126            .as_ref()
127            .map(|battery| battery.design_capacity)
128    })
129}
130
131fn last_full_charge_capacity(power: &Option<PowerInfo>) -> Option<u32> {
132    power.as_ref().and_then(|power| {
133        power
134            .battery
135            .as_ref()
136            .map(|battery| battery.last_full_charge_capacity)
137    })
138}
139
140fn cycle_count(power: &Option<PowerInfo>) -> Option<u32> {
141    power
142        .as_ref()
143        .and_then(|power| power.battery.as_ref().map(|battery| battery.cycle_count))
144}
145
146fn is_charging(power: &Option<PowerInfo>) -> bool {
147    power
148        .as_ref()
149        .and_then(|power| power.battery.as_ref().map(|battery| battery.charging))
150        .unwrap_or(false)
151}
152
153fn is_ac_connected(power: &Option<PowerInfo>) -> bool {
154    power
155        .as_ref()
156        .map(|power| power.ac_present)
157        .unwrap_or(false)
158}
159
160fn capacity_loss_percentage(power: &Option<PowerInfo>) -> Option<f32> {
161    match (design_capacity(power), last_full_charge_capacity(power)) {
162        (Some(design), Some(last)) => Some(((design as f32 - last as f32) / design as f32) * 100.0),
163        _ => None,
164    }
165}
166
167fn capacity_loss_per_cycle(power: &Option<PowerInfo>) -> Option<f32> {
168    match (capacity_loss_percentage(power), cycle_count(power)) {
169        (Some(loss_percentage), Some(cycle_count)) => Some(loss_percentage / (cycle_count as f32)),
170        _ => None,
171    }
172}
173
174fn charging_status(power: &Option<PowerInfo>) -> &'static str {
175    match (is_charging(power), is_ac_connected(power)) {
176        (true, true) => "Charging",
177        (false, true) => "Fully charged",
178        (false, false) => "Discharging",
179        (true, false) => "Unknown",
180    }
181}
182
183fn max_charge_limit(charge_limit: &Option<(u8, u8)>) -> Option<u8> {
184    charge_limit.as_ref().map(|charge_limit| charge_limit.1)
185}
186
187fn is_microphone_enabled(privacy: &Option<(bool, bool)>) -> bool {
188    privacy.as_ref().map(|privacy| privacy.0).unwrap_or(false)
189}
190
191fn is_camera_enabled(privacy: &Option<(bool, bool)>) -> bool {
192    privacy.as_ref().map(|privacy| privacy.1).unwrap_or(false)
193}
194
195fn fp_brightness_percentage(
196    fp_brightness: &Option<(u8, Option<FpLedBrightnessLevel>)>,
197) -> Option<u8> {
198    fp_brightness.as_ref().map(|fp_brightness| fp_brightness.0)
199}
200
201// fn fp_brightness_level(
202//     fp_brightness: &Option<(u8, Option<FpLedBrightnessLevel>)>,
203// ) -> Option<FpLedBrightnessLevel> {
204//     fp_brightness
205//         .as_ref()
206//         .and_then(|fp_brightness| fp_brightness.1 )
207// }
208
209fn kb_brightness_percentage(kb_brightness: Option<u8>) -> Option<u8> {
210    kb_brightness
211}
212
213fn smbios_version(smbios: &Option<SmbiosStore>) -> Option<String> {
214    smbios.as_ref().and_then(|smbios| {
215        smbios.structures().find_map(|result| match result {
216            Ok(Structure::Bios(data)) if !data.bios_version.is_empty() => {
217                Some(data.bios_version.to_string())
218            }
219            _ => None,
220        })
221    })
222}
223
224fn smbios_release_date(smbios: &Option<SmbiosStore>) -> Option<String> {
225    smbios.as_ref().and_then(|smbios| {
226        smbios.structures().find_map(|result| match result {
227            Ok(Structure::Bios(data)) if !data.bios_release_date.is_empty() => {
228                Some(data.bios_release_date.to_string())
229            }
230            _ => None,
231        })
232    })
233}
234
235fn smbios_vendor(smbios: &Option<SmbiosStore>) -> Option<String> {
236    smbios.as_ref().and_then(|smbios| {
237        smbios.structures().find_map(|result| match result {
238            Ok(Structure::Bios(data)) if !data.vendor.is_empty() => Some(data.vendor.to_string()),
239            _ => None,
240        })
241    })
242}
243
244fn pd_ports_info(pd_ports: Vec<Option<UsbPdPowerInfo>>) -> PdPortsInfo {
245    let left_back = pd_ports
246        .get(3)
247        .and_then(|port| port.as_ref().map(pd_port_info));
248    let left_front = pd_ports
249        .get(2)
250        .and_then(|port| port.as_ref().map(pd_port_info));
251    let right_back = pd_ports
252        .first()
253        .and_then(|port| port.as_ref().map(pd_port_info));
254    let right_front = pd_ports
255        .get(1)
256        .and_then(|port| port.as_ref().map(pd_port_info));
257
258    PdPortsInfo {
259        left_back,
260        left_front,
261        right_back,
262        right_front,
263    }
264}
265
266fn pd_port_info(pd_port: &UsbPdPowerInfo) -> PdPortInfo {
267    let role = match pd_port.role {
268        UsbPowerRoles::Disconnected => "Disconnected".to_string(),
269        UsbPowerRoles::Source => "Source".to_string(),
270        UsbPowerRoles::Sink => "Sink".to_string(),
271        UsbPowerRoles::SinkNotCharging => "Sink (Not Charging)".to_string(),
272    };
273    let dualrole = if pd_port.dualrole {
274        "DRP".to_string()
275    } else {
276        "Charger".to_string()
277    };
278    let charging_type = match pd_port.charging_type {
279        UsbChargingType::None => "None".to_string(),
280        UsbChargingType::PD => "PD".to_string(),
281        UsbChargingType::TypeC => "Type-C".to_string(),
282        UsbChargingType::Proprietary => "Proprietary".to_string(),
283        UsbChargingType::Bc12Dcp => "BC1.2 DCP".to_string(),
284        UsbChargingType::Bc12Cdp => "BC1.2 CDP".to_string(),
285        UsbChargingType::Bc12Sdp => "BC1.2 SDP".to_string(),
286        UsbChargingType::Other => "Other".to_string(),
287        UsbChargingType::VBus => "VBUS".to_string(),
288        UsbChargingType::Unknown => "Unknown".to_string(),
289    };
290    let max_power = pd_port.max_power / 1000;
291    let voltage_now = pd_port.meas.voltage_now as f32 / 1000.0;
292    let voltage_max = pd_port.meas.voltage_max as f32 / 1000.0;
293
294    PdPortInfo {
295        role,
296        dualrole,
297        charging_type,
298        max_power,
299        voltage_now,
300        voltage_max,
301        current_limit: pd_port.meas.current_lim,
302        current_max: pd_port.meas.current_max,
303    }
304}