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"] }
|
dbus = { version = "0.9.7", features = ["vendored"] }
|
||||||
nix = { version = "0.30.1", features = ["user"] }
|
nix = { version = "0.30.1", features = ["user"] }
|
||||||
serde = { version = "1.0.219", features = ["derive"] }
|
serde = { version = "1.0.219", features = ["derive"] }
|
||||||
serde_json = "1.0.140"
|
serde_json = "1.0.140"
|
@ -1,8 +1,7 @@
|
|||||||
# pumo-system-info
|
# pumo-system-info
|
||||||
|
|
||||||
A Rust-based utility for gathering and presenting system information
|
A Rust-based utility for gathering and presenting system information
|
||||||
in JSON format. For now, it only retrieves Bluetooth-related
|
in JSON format.
|
||||||
information.
|
|
||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
@ -15,6 +14,7 @@ be used with my [eww](https://elkowar.github.io/eww/) configuration.
|
|||||||
## Prerequisites
|
## Prerequisites
|
||||||
- DBus
|
- DBus
|
||||||
- BlueZ (for retrieving Bluetooth-related information)
|
- BlueZ (for retrieving Bluetooth-related information)
|
||||||
|
- UPower (for retrieving power information)
|
||||||
- Rust (specified in `rustup-toolchain.toml`)
|
- Rust (specified in `rustup-toolchain.toml`)
|
||||||
- Nix package manager with flakes (optional, for building and managing
|
- Nix package manager with flakes (optional, for building and managing
|
||||||
dependencies)
|
dependencies)
|
||||||
|
@ -3,21 +3,25 @@ use dbus::blocking::Connection;
|
|||||||
|
|
||||||
use bluetooth::BluetoothInfo;
|
use bluetooth::BluetoothInfo;
|
||||||
use user_info::UserInfo;
|
use user_info::UserInfo;
|
||||||
|
use power::PowerInfo;
|
||||||
|
|
||||||
mod bluetooth;
|
mod bluetooth;
|
||||||
mod user_info;
|
mod user_info;
|
||||||
|
mod power;
|
||||||
|
|
||||||
#[derive(serde::Serialize)]
|
#[derive(serde::Serialize)]
|
||||||
struct SystemInfo {
|
struct SystemInfo {
|
||||||
bluetooth: Vec<BluetoothInfo>,
|
bluetooth: Vec<BluetoothInfo>,
|
||||||
user: UserInfo
|
user: UserInfo,
|
||||||
|
power: PowerInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() -> Result<()> {
|
fn main() -> Result<()> {
|
||||||
let connection = Connection::new_system()?;
|
let connection = Connection::new_system()?;
|
||||||
let bluetooth = bluetooth::get_all_bluetooth_devices(&connection);
|
let bluetooth = bluetooth::get_all_bluetooth_devices(&connection);
|
||||||
let user = UserInfo::new(&connection).unwrap_or_default();
|
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)?);
|
println!("{}", serde_json::to_string(&info)?);
|
||||||
Ok(())
|
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