Compare commits
2 Commits
4c76d2b618
...
d0a6b6db77
Author | SHA1 | Date | |
---|---|---|---|
d0a6b6db77 | |||
3395154916 |
31
Cargo.lock
generated
31
Cargo.lock
generated
@ -8,6 +8,12 @@ version = "1.0.98"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.2.19"
|
||||
@ -17,6 +23,18 @@ dependencies = [
|
||||
"shlex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "cfg_aliases"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
|
||||
|
||||
[[package]]
|
||||
name = "dbus"
|
||||
version = "0.9.7"
|
||||
@ -56,6 +74,18 @@ version = "2.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
||||
|
||||
[[package]]
|
||||
name = "nix"
|
||||
version = "0.30.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"cfg-if",
|
||||
"cfg_aliases",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pkg-config"
|
||||
version = "0.3.32"
|
||||
@ -77,6 +107,7 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"dbus",
|
||||
"nix",
|
||||
"serde",
|
||||
"serde_json",
|
||||
]
|
||||
|
@ -10,5 +10,6 @@ publish = false
|
||||
[dependencies]
|
||||
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)
|
||||
|
@ -1,9 +1,8 @@
|
||||
use anyhow::Result;
|
||||
use dbus::arg;
|
||||
use std::collections::HashMap;
|
||||
use std::time::Duration;
|
||||
|
||||
#[derive(Default, serde::Serialize)]
|
||||
#[derive(serde::Serialize)]
|
||||
pub struct BluetoothInfo {
|
||||
pub name: String,
|
||||
pub address: String,
|
||||
@ -32,8 +31,8 @@ impl BluetoothInfo {
|
||||
}
|
||||
|
||||
pub fn get_all_bluetooth_devices(
|
||||
connection: dbus::blocking::Connection,
|
||||
) -> Result<Vec<BluetoothInfo>> {
|
||||
connection: &dbus::blocking::Connection,
|
||||
) -> Vec<BluetoothInfo> {
|
||||
let proxy = connection.with_proxy("org.bluez", "/", Duration::from_millis(5000));
|
||||
let (result,): (HashMap<dbus::Path<'static>, HashMap<String, arg::PropMap>>,) = proxy
|
||||
.method_call(
|
||||
@ -47,5 +46,5 @@ pub fn get_all_bluetooth_devices(
|
||||
.filter(|(_, value)| value.contains_key("org.bluez.Device1"))
|
||||
.flat_map(|(_, value)| BluetoothInfo::new(value))
|
||||
.collect();
|
||||
Ok(result)
|
||||
result
|
||||
}
|
||||
|
15
src/main.rs
15
src/main.rs
@ -1,18 +1,27 @@
|
||||
use anyhow::Result;
|
||||
use bluetooth::BluetoothInfo;
|
||||
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,
|
||||
power: PowerInfo
|
||||
}
|
||||
|
||||
fn main() -> Result<()> {
|
||||
let connection = Connection::new_system()?;
|
||||
let bluetooth = bluetooth::get_all_bluetooth_devices(connection)?;
|
||||
let info = SystemInfo { bluetooth };
|
||||
let bluetooth = bluetooth::get_all_bluetooth_devices(&connection);
|
||||
let user = UserInfo::new(&connection).unwrap_or_default();
|
||||
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,
|
||||
}
|
||||
}
|
||||
}
|
28
src/user_info.rs
Normal file
28
src/user_info.rs
Normal file
@ -0,0 +1,28 @@
|
||||
use dbus::blocking;
|
||||
|
||||
#[derive(serde::Serialize, Default)]
|
||||
pub struct UserInfo {
|
||||
pub real_name: String,
|
||||
pub username: String,
|
||||
pub icon: String,
|
||||
}
|
||||
|
||||
impl UserInfo {
|
||||
pub fn new(connection: &blocking::Connection) -> anyhow::Result<Self> {
|
||||
let uid = nix::unistd::Uid::current();
|
||||
let proxy = connection.with_proxy(
|
||||
"org.freedesktop.Accounts",
|
||||
format!("/org/freedesktop/Accounts/User{}", uid),
|
||||
std::time::Duration::from_millis(5000),
|
||||
);
|
||||
use blocking::stdintf::org_freedesktop_dbus::Properties;
|
||||
let icon: String = proxy.get("org.freedesktop.Accounts.User", "IconFile")?;
|
||||
let real_name: String = proxy.get("org.freedesktop.Accounts.User", "RealName")?;
|
||||
let username: String = proxy.get("org.freedesktop.Accounts.User", "UserName")?;
|
||||
Ok(Self {
|
||||
real_name,
|
||||
username,
|
||||
icon,
|
||||
})
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user