i3xs/src/widgets/power.rs

108 lines
3.4 KiB
Rust

//! ::::::::::::::
//! /sys/class/power_supply/hidpp_battery_8/uevent
//! ::::::::::::::
//! POWER_SUPPLY_NAME=hidpp_battery_8
//! POWER_SUPPLY_TYPE=Battery
//! POWER_SUPPLY_ONLINE=1
//! POWER_SUPPLY_STATUS=Discharging
//! POWER_SUPPLY_SCOPE=Device
//! POWER_SUPPLY_MODEL_NAME=M570
//! POWER_SUPPLY_MANUFACTURER=Logitech
//! POWER_SUPPLY_SERIAL_NUMBER=1028-26-d9-b5-38
//! POWER_SUPPLY_CAPACITY=65
use std::collections::HashMap;
use glob::glob;
use i3monkit::{Block, Widget, WidgetUpdate};
use thiserror::Error;
#[derive(Debug, Default)]
pub struct PowerSupply {}
const POWER_SUPPLY_GLOB: &'static str = "/sys/class/power_supply/*/uevent";
#[derive(Error, Debug)]
enum PowerSupplyError {
#[error("IO error {0}")]
IoError(#[from] std::io::Error),
#[error("glob error {0}")]
GlobError(#[from] glob::GlobError),
#[error("pattern error {0}")]
PatternError(#[from] glob::PatternError),
#[error("UTF-8 error {0}")]
Utf8Error(#[from] std::str::Utf8Error),
#[error("parse number error {0}")]
ParseIntErro(#[from] std::num::ParseIntError),
#[error("parse error {0}")]
ParseError(String),
}
impl PowerSupply {
fn get_update(&mut self) -> Result<Vec<String>, PowerSupplyError> {
const BATT_100: &'static str = "&#xf240;";
const BATT_75: &'static str = "&#xf241;";
const BATT_50: &'static str = "&#xf242;";
const BATT_25: &'static str = "&#xf243;";
const BATT_0: &'static str = "&#xf244;";
let mut res = Vec::new();
for ps in glob(POWER_SUPPLY_GLOB)? {
let bytes = std::fs::read(&ps?)?;
let values: HashMap<&str, &str> = std::str::from_utf8(&bytes)?
.lines()
.map(|l| {
l.split_once('=').ok_or(PowerSupplyError::ParseError(
"missing = in uevent line".to_string(),
))
})
.collect::<Result<_, _>>()?;
let cap: u32 = values
.get("POWER_SUPPLY_CAPACITY")
.unwrap_or(&"0")
.parse()?;
let power = if cap >= 90 {
BATT_100
} else if cap >= 70 {
BATT_75
} else if cap >= 40 {
BATT_50
} else if cap >= 20 {
BATT_25
} else {
BATT_0
};
res.push(format!(
"{} {}: <span face=\"pango:Font Awesome 5 Free\">{}</span>",
values
.get("POWER_SUPPLY_MANUFACTURER")
.unwrap_or(&"Unknown mfg"),
values
.get("POWER_SUPPLY_MODEL_NAME")
.unwrap_or(&"Unknown model"),
power,
));
}
Ok(res)
}
}
impl Widget for PowerSupply {
fn update(&mut self) -> Option<WidgetUpdate> {
match self.get_update() {
Ok(statuses) => {
let mut data = Block::new();
data.use_pango();
for status in statuses {
data.append_full_text(&status);
}
return Some(WidgetUpdate {
refresh_interval: std::time::Duration::new(1, 0),
data: Some(data),
});
}
Err(err) => eprintln!("Failed to update: {}", err),
}
None
}
}