use dmidecode::Structure;
use framework_lib::chromium_ec::commands::FpLedBrightnessLevel;
use framework_lib::power::PowerInfo;
use framework_lib::power::UsbChargingType;
use framework_lib::power::UsbPdPowerInfo;
use framework_lib::power::UsbPowerRoles;
use framework_lib::smbios::Platform;
use framework_lib::smbios::SmbiosStore;
#[derive(Default)]
pub struct FrameworkInfo {
pub charge_percentage: Option<u32>,
pub charger_voltage: Option<u32>,
pub charger_current: Option<u32>,
pub design_capacity: Option<u32>,
pub last_full_charge_capacity: Option<u32>,
pub cycle_count: Option<u32>,
pub capacity_loss_percentage: Option<f32>,
pub capacity_loss_per_cycle: Option<f32>,
pub is_charging: bool,
pub is_ac_connected: bool,
pub charging_status: &'static str,
pub max_charge_limit: Option<u8>,
pub is_microphone_enabled: bool,
pub is_camera_enabled: bool,
pub fp_brightness_percentage: Option<u8>,
pub kb_brightness_percentage: Option<u8>,
pub smbios_version: Option<String>,
pub smbios_release_date: Option<String>,
pub smbios_vendor: Option<String>,
pub pd_ports: PdPortsInfo,
pub fan_rpm: Option<Vec<u16>>,
pub platform: Option<Platform>,
}
impl FrameworkInfo {
#[allow(clippy::too_many_arguments)]
pub fn new(
power: &Option<PowerInfo>,
charge_limit: &Option<(u8, u8)>,
privacy: &Option<(bool, bool)>,
fp_brightness: &Option<(u8, Option<FpLedBrightnessLevel>)>,
kb_brightness: Option<u8>,
smbios: &Option<SmbiosStore>,
pd_ports: Vec<Option<UsbPdPowerInfo>>,
fan_rpm: Option<Vec<u16>>,
platform: Option<Platform>,
) -> Self {
Self {
charge_percentage: charge_percentage(power),
charger_voltage: charger_voltage(power),
charger_current: charger_current(power),
design_capacity: design_capacity(power),
last_full_charge_capacity: last_full_charge_capacity(power),
cycle_count: cycle_count(power),
capacity_loss_percentage: capacity_loss_percentage(power),
capacity_loss_per_cycle: capacity_loss_per_cycle(power),
is_charging: is_charging(power),
is_ac_connected: is_ac_connected(power),
charging_status: charging_status(power),
max_charge_limit: max_charge_limit(charge_limit),
is_microphone_enabled: is_microphone_enabled(privacy),
is_camera_enabled: is_camera_enabled(privacy),
fp_brightness_percentage: fp_brightness_percentage(fp_brightness),
kb_brightness_percentage: kb_brightness_percentage(kb_brightness),
smbios_version: smbios_version(smbios),
smbios_release_date: smbios_release_date(smbios),
smbios_vendor: smbios_vendor(smbios),
pd_ports: pd_ports_info(pd_ports),
fan_rpm,
platform,
}
}
}
#[derive(Default)]
pub struct PdPortsInfo {
pub left_back: Option<PdPortInfo>,
pub left_front: Option<PdPortInfo>,
pub right_back: Option<PdPortInfo>,
pub right_front: Option<PdPortInfo>,
}
#[derive(Default)]
pub struct PdPortInfo {
pub role: String,
pub dualrole: String,
pub charging_type: String,
pub max_power: u32,
pub voltage_now: f32,
pub voltage_max: f32,
pub current_limit: u16,
pub current_max: u16,
}
fn charge_percentage(power: &Option<PowerInfo>) -> Option<u32> {
power.as_ref().and_then(|power| {
power
.battery
.as_ref()
.map(|battery| battery.charge_percentage)
})
}
fn charger_voltage(power: &Option<PowerInfo>) -> Option<u32> {
power.as_ref().and_then(|power| {
power
.battery
.as_ref()
.map(|battery| battery.present_voltage)
})
}
fn charger_current(power: &Option<PowerInfo>) -> Option<u32> {
power
.as_ref()
.and_then(|power| power.battery.as_ref().map(|battery| battery.present_rate))
}
fn design_capacity(power: &Option<PowerInfo>) -> Option<u32> {
power.as_ref().and_then(|power| {
power
.battery
.as_ref()
.map(|battery| battery.design_capacity)
})
}
fn last_full_charge_capacity(power: &Option<PowerInfo>) -> Option<u32> {
power.as_ref().and_then(|power| {
power
.battery
.as_ref()
.map(|battery| battery.last_full_charge_capacity)
})
}
fn cycle_count(power: &Option<PowerInfo>) -> Option<u32> {
power
.as_ref()
.and_then(|power| power.battery.as_ref().map(|battery| battery.cycle_count))
}
fn is_charging(power: &Option<PowerInfo>) -> bool {
power
.as_ref()
.and_then(|power| power.battery.as_ref().map(|battery| battery.charging))
.unwrap_or(false)
}
fn is_ac_connected(power: &Option<PowerInfo>) -> bool {
power
.as_ref()
.map(|power| power.ac_present)
.unwrap_or(false)
}
fn capacity_loss_percentage(power: &Option<PowerInfo>) -> Option<f32> {
match (design_capacity(power), last_full_charge_capacity(power)) {
(Some(design), Some(last)) => Some(((design as f32 - last as f32) / design as f32) * 100.0),
_ => None,
}
}
fn capacity_loss_per_cycle(power: &Option<PowerInfo>) -> Option<f32> {
match (capacity_loss_percentage(power), cycle_count(power)) {
(Some(loss_percentage), Some(cycle_count)) => Some(loss_percentage / (cycle_count as f32)),
_ => None,
}
}
fn charging_status(power: &Option<PowerInfo>) -> &'static str {
match (is_charging(power), is_ac_connected(power)) {
(true, true) => "Charging",
(false, true) => "Fully charged",
(false, false) => "Discharging",
(true, false) => "Unknown",
}
}
fn max_charge_limit(charge_limit: &Option<(u8, u8)>) -> Option<u8> {
charge_limit.as_ref().map(|charge_limit| charge_limit.1)
}
fn is_microphone_enabled(privacy: &Option<(bool, bool)>) -> bool {
privacy.as_ref().map(|privacy| privacy.0).unwrap_or(false)
}
fn is_camera_enabled(privacy: &Option<(bool, bool)>) -> bool {
privacy.as_ref().map(|privacy| privacy.1).unwrap_or(false)
}
fn fp_brightness_percentage(
fp_brightness: &Option<(u8, Option<FpLedBrightnessLevel>)>,
) -> Option<u8> {
fp_brightness.as_ref().map(|fp_brightness| fp_brightness.0)
}
fn kb_brightness_percentage(kb_brightness: Option<u8>) -> Option<u8> {
kb_brightness
}
fn smbios_version(smbios: &Option<SmbiosStore>) -> Option<String> {
smbios.as_ref().and_then(|smbios| {
smbios.structures().find_map(|result| match result {
Ok(Structure::Bios(data)) if !data.bios_version.is_empty() => {
Some(data.bios_version.to_string())
}
_ => None,
})
})
}
fn smbios_release_date(smbios: &Option<SmbiosStore>) -> Option<String> {
smbios.as_ref().and_then(|smbios| {
smbios.structures().find_map(|result| match result {
Ok(Structure::Bios(data)) if !data.bios_release_date.is_empty() => {
Some(data.bios_release_date.to_string())
}
_ => None,
})
})
}
fn smbios_vendor(smbios: &Option<SmbiosStore>) -> Option<String> {
smbios.as_ref().and_then(|smbios| {
smbios.structures().find_map(|result| match result {
Ok(Structure::Bios(data)) if !data.vendor.is_empty() => Some(data.vendor.to_string()),
_ => None,
})
})
}
fn pd_ports_info(pd_ports: Vec<Option<UsbPdPowerInfo>>) -> PdPortsInfo {
let left_back = pd_ports
.get(3)
.and_then(|port| port.as_ref().map(pd_port_info));
let left_front = pd_ports
.get(2)
.and_then(|port| port.as_ref().map(pd_port_info));
let right_back = pd_ports
.first()
.and_then(|port| port.as_ref().map(pd_port_info));
let right_front = pd_ports
.get(1)
.and_then(|port| port.as_ref().map(pd_port_info));
PdPortsInfo {
left_back,
left_front,
right_back,
right_front,
}
}
fn pd_port_info(pd_port: &UsbPdPowerInfo) -> PdPortInfo {
let role = match pd_port.role {
UsbPowerRoles::Disconnected => "Disconnected".to_string(),
UsbPowerRoles::Source => "Source".to_string(),
UsbPowerRoles::Sink => "Sink".to_string(),
UsbPowerRoles::SinkNotCharging => "Sink (Not Charging)".to_string(),
};
let dualrole = if pd_port.dualrole {
"DRP".to_string()
} else {
"Charger".to_string()
};
let charging_type = match pd_port.charging_type {
UsbChargingType::None => "None".to_string(),
UsbChargingType::PD => "PD".to_string(),
UsbChargingType::TypeC => "Type-C".to_string(),
UsbChargingType::Proprietary => "Proprietary".to_string(),
UsbChargingType::Bc12Dcp => "BC1.2 DCP".to_string(),
UsbChargingType::Bc12Cdp => "BC1.2 CDP".to_string(),
UsbChargingType::Bc12Sdp => "BC1.2 SDP".to_string(),
UsbChargingType::Other => "Other".to_string(),
UsbChargingType::VBus => "VBUS".to_string(),
UsbChargingType::Unknown => "Unknown".to_string(),
};
let max_power = pd_port.max_power / 1000;
let voltage_now = pd_port.meas.voltage_now as f32 / 1000.0;
let voltage_max = pd_port.meas.voltage_max as f32 / 1000.0;
PdPortInfo {
role,
dualrole,
charging_type,
max_power,
voltage_now,
voltage_max,
current_limit: pd_port.meas.current_lim,
current_max: pd_port.meas.current_max,
}
}