feat(power): add power information
This commit is contained in:
		
							parent
							
								
									3395154916
								
							
						
					
					
						commit
						d0a6b6db77
					
				@ -12,4 +12,4 @@ anyhow = "1.0.98"
 | 
			
		||||
dbus = { version = "0.9.7", features = ["vendored"] }
 | 
			
		||||
nix = { version = "0.30.1", features = ["user"] }
 | 
			
		||||
serde = { version = "1.0.219", features = ["derive"] }
 | 
			
		||||
serde_json = "1.0.140"
 | 
			
		||||
serde_json = "1.0.140"
 | 
			
		||||
@ -1,8 +1,7 @@
 | 
			
		||||
# pumo-system-info
 | 
			
		||||
 | 
			
		||||
A Rust-based utility for gathering and presenting system information
 | 
			
		||||
in JSON format. For now, it only retrieves Bluetooth-related
 | 
			
		||||
information.
 | 
			
		||||
in JSON format.
 | 
			
		||||
 | 
			
		||||
## Overview
 | 
			
		||||
 | 
			
		||||
@ -15,6 +14,7 @@ be used with my [eww](https://elkowar.github.io/eww/) configuration.
 | 
			
		||||
## Prerequisites
 | 
			
		||||
- DBus
 | 
			
		||||
- BlueZ (for retrieving Bluetooth-related information)
 | 
			
		||||
- UPower (for retrieving power information)
 | 
			
		||||
- Rust (specified in `rustup-toolchain.toml`)
 | 
			
		||||
- Nix package manager with flakes (optional, for building and managing
 | 
			
		||||
  dependencies)
 | 
			
		||||
 | 
			
		||||
@ -3,21 +3,25 @@ use dbus::blocking::Connection;
 | 
			
		||||
 | 
			
		||||
use bluetooth::BluetoothInfo;
 | 
			
		||||
use user_info::UserInfo;
 | 
			
		||||
use power::PowerInfo;
 | 
			
		||||
 | 
			
		||||
mod bluetooth;
 | 
			
		||||
mod user_info;
 | 
			
		||||
mod power;
 | 
			
		||||
 | 
			
		||||
#[derive(serde::Serialize)]
 | 
			
		||||
struct SystemInfo {
 | 
			
		||||
    bluetooth: Vec<BluetoothInfo>,
 | 
			
		||||
    user: UserInfo
 | 
			
		||||
    user: UserInfo,
 | 
			
		||||
    power: PowerInfo
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn main() -> Result<()> {
 | 
			
		||||
    let connection = Connection::new_system()?;
 | 
			
		||||
    let bluetooth = bluetooth::get_all_bluetooth_devices(&connection);
 | 
			
		||||
    let user = UserInfo::new(&connection).unwrap_or_default();
 | 
			
		||||
    let info = SystemInfo { bluetooth, user };
 | 
			
		||||
    let power = PowerInfo::new(&connection);
 | 
			
		||||
    let info = SystemInfo { bluetooth, user, power };
 | 
			
		||||
    println!("{}", serde_json::to_string(&info)?);
 | 
			
		||||
    Ok(())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										172
									
								
								src/power.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										172
									
								
								src/power.rs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,172 @@
 | 
			
		||||
use dbus::{arg, blocking};
 | 
			
		||||
use std::fmt;
 | 
			
		||||
 | 
			
		||||
const TIMEOUT: std::time::Duration = std::time::Duration::from_millis(5000);
 | 
			
		||||
const UPOWER_DEST: &str = "org.freedesktop.UPower";
 | 
			
		||||
const UPOWER_DEVICE: &str = "org.freedesktop.UPower.Device";
 | 
			
		||||
 | 
			
		||||
#[derive(Debug)]
 | 
			
		||||
pub enum BatteryState {
 | 
			
		||||
    Unknown,
 | 
			
		||||
    Charging,
 | 
			
		||||
    Discharging,
 | 
			
		||||
    Empty,
 | 
			
		||||
    FullyCharged,
 | 
			
		||||
    PendingCharge,
 | 
			
		||||
    PendingDischarge,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Default for BatteryState {
 | 
			
		||||
    fn default() -> Self {
 | 
			
		||||
        Self::Unknown
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<u32> for BatteryState {
 | 
			
		||||
    fn from(value: u32) -> Self {
 | 
			
		||||
        match value {
 | 
			
		||||
            1 => Self::Charging,
 | 
			
		||||
            2 => Self::Discharging,
 | 
			
		||||
            3 => Self::Empty,
 | 
			
		||||
            4 => Self::FullyCharged,
 | 
			
		||||
            5 => Self::PendingCharge,
 | 
			
		||||
            6 => Self::PendingDischarge,
 | 
			
		||||
            _ => Self::Unknown,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl fmt::Display for BatteryState {
 | 
			
		||||
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
 | 
			
		||||
        let str = match self {
 | 
			
		||||
            BatteryState::Unknown => "unknown",
 | 
			
		||||
            BatteryState::Charging => "charging",
 | 
			
		||||
            BatteryState::Discharging => "discharging",
 | 
			
		||||
            BatteryState::Empty => "empty",
 | 
			
		||||
            BatteryState::FullyCharged => "fully charged",
 | 
			
		||||
            BatteryState::PendingCharge => "pending charge",
 | 
			
		||||
            BatteryState::PendingDischarge => "pending discharge",
 | 
			
		||||
        };
 | 
			
		||||
        write!(f, "{str}")
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl serde::Serialize for BatteryState {
 | 
			
		||||
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
 | 
			
		||||
    where
 | 
			
		||||
        S: serde::Serializer,
 | 
			
		||||
    {
 | 
			
		||||
        serializer.serialize_str(self.to_string().as_str())
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(serde::Serialize, Default)]
 | 
			
		||||
pub struct BatteryInfo {
 | 
			
		||||
    pub batteries: Vec<Battery>,
 | 
			
		||||
    pub on_battery: bool,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl BatteryInfo {
 | 
			
		||||
    fn get_batteries(
 | 
			
		||||
        connection: &blocking::Connection,
 | 
			
		||||
        proxy: &blocking::Proxy<'_, &blocking::Connection>,
 | 
			
		||||
    ) -> Vec<Battery> {
 | 
			
		||||
        let (batteries,): (Vec<dbus::strings::Path>,) = proxy
 | 
			
		||||
            .method_call(UPOWER_DEST, "EnumerateDevices", ())
 | 
			
		||||
            .unwrap_or_default();
 | 
			
		||||
        let batteries: Vec<Battery> = batteries
 | 
			
		||||
            .iter()
 | 
			
		||||
            .filter_map(|path| match path.clone().into_cstring().into_string() {
 | 
			
		||||
                Err(_) => None,
 | 
			
		||||
                Ok(path) => {
 | 
			
		||||
                    if path.contains("BAT") {
 | 
			
		||||
                        Some(path)
 | 
			
		||||
                    } else {
 | 
			
		||||
                        None
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            })
 | 
			
		||||
            .flat_map(|path| Battery::new(connection, path).ok())
 | 
			
		||||
            .collect();
 | 
			
		||||
        batteries
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn new(connection: &blocking::Connection) -> Self {
 | 
			
		||||
        let proxy = connection.with_proxy(UPOWER_DEST, "/org/freedesktop/UPower", TIMEOUT);
 | 
			
		||||
        use blocking::stdintf::org_freedesktop_dbus::Properties;
 | 
			
		||||
        let on_battery: bool = proxy.get(UPOWER_DEST, "OnBattery").unwrap_or(false);
 | 
			
		||||
        let batteries = Self::get_batteries(connection, &proxy);
 | 
			
		||||
        Self {
 | 
			
		||||
            on_battery,
 | 
			
		||||
            batteries,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(serde::Serialize, Default, Debug)]
 | 
			
		||||
pub struct Battery {
 | 
			
		||||
    pub level: f64,
 | 
			
		||||
    pub icon: String,
 | 
			
		||||
    pub time_to_empty: i64,
 | 
			
		||||
    pub time_to_full: i64,
 | 
			
		||||
    pub state: BatteryState,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Battery {
 | 
			
		||||
    pub fn new(connection: &blocking::Connection, path: String) -> anyhow::Result<Self> {
 | 
			
		||||
        let proxy = connection.with_proxy(UPOWER_DEST, path.clone(), TIMEOUT);
 | 
			
		||||
        use blocking::stdintf::org_freedesktop_dbus::Properties;
 | 
			
		||||
        let state: u32 = proxy.get(UPOWER_DEVICE, "State")?;
 | 
			
		||||
        let result = Self {
 | 
			
		||||
            level: proxy.get(UPOWER_DEVICE, "Percentage")?,
 | 
			
		||||
            icon: proxy.get(UPOWER_DEVICE, "IconName")?,
 | 
			
		||||
            time_to_empty: proxy.get(UPOWER_DEVICE, "TimeToEmpty").unwrap_or_default(),
 | 
			
		||||
            time_to_full: proxy.get(UPOWER_DEVICE, "TimeToFull").unwrap_or_default(),
 | 
			
		||||
            state: state.into(),
 | 
			
		||||
        };
 | 
			
		||||
        Ok(result)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(serde::Serialize, Default)]
 | 
			
		||||
pub struct PowerProfile {
 | 
			
		||||
    pub current: String,
 | 
			
		||||
    pub available: Vec<String>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl PowerProfile {
 | 
			
		||||
    pub fn new(connection: &blocking::Connection) -> anyhow::Result<Self> {
 | 
			
		||||
        let proxy = connection.with_proxy(
 | 
			
		||||
            "org.freedesktop.UPower.PowerProfiles",
 | 
			
		||||
            "/org/freedesktop/UPower/PowerProfiles",
 | 
			
		||||
            TIMEOUT,
 | 
			
		||||
        );
 | 
			
		||||
        use blocking::stdintf::org_freedesktop_dbus::Properties;
 | 
			
		||||
        let current: String = proxy.get("org.freedesktop.UPower.PowerProfiles", "ActiveProfile")?;
 | 
			
		||||
        let available: Vec<arg::PropMap> =
 | 
			
		||||
            proxy.get("org.freedesktop.UPower.PowerProfiles", "Profiles")?;
 | 
			
		||||
        let available: Vec<String> = available
 | 
			
		||||
            .iter()
 | 
			
		||||
            .flat_map(|profile| profile.get("Profile")?.0.as_str())
 | 
			
		||||
            .map(|profile| profile.to_owned())
 | 
			
		||||
            .collect();
 | 
			
		||||
        Ok(Self { current, available })
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(serde::Serialize)]
 | 
			
		||||
pub struct PowerInfo {
 | 
			
		||||
    pub power_profile: PowerProfile,
 | 
			
		||||
    pub batteries: BatteryInfo,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl PowerInfo {
 | 
			
		||||
    pub fn new(connection: &blocking::Connection) -> Self {
 | 
			
		||||
        let power_profile = PowerProfile::new(connection).unwrap_or_default();
 | 
			
		||||
        let batteries = BatteryInfo::new(connection);
 | 
			
		||||
        Self {
 | 
			
		||||
            power_profile,
 | 
			
		||||
            batteries,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user