framework_tool_tui/framework/
info.rs1use 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 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 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
201fn 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}