128 lines
3.9 KiB
Rust
128 lines
3.9 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, ColorRGB, Widget, WidgetUpdate};
|
|
use thiserror::Error;
|
|
|
|
use crate::colors;
|
|
|
|
#[derive(Debug, Default)]
|
|
pub struct PowerSupply {}
|
|
|
|
const POWER_SUPPLY_GLOB: &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: &str = "";
|
|
const BATT_75: &str = "";
|
|
const BATT_50: &str = "";
|
|
const BATT_25: &str = "";
|
|
const BATT_0: &str = "";
|
|
|
|
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_else(|| {
|
|
PowerSupplyError::ParseError("missing = in uevent line".to_string())
|
|
})
|
|
})
|
|
.collect::<Result<_, _>>()?;
|
|
// Skip things that aren't battery powered
|
|
if !values
|
|
.get("POWER_SUPPLY_TYPE")
|
|
.map(|t| t == &"Battery")
|
|
.unwrap_or(false)
|
|
{
|
|
continue;
|
|
}
|
|
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
|
|
};
|
|
|
|
let color = if cap >= 50 {
|
|
colors::WHITE
|
|
} else if cap >= 25 {
|
|
ColorRGB::yellow()
|
|
} else {
|
|
ColorRGB::red()
|
|
};
|
|
res.push(format!(
|
|
r##"{} {}: {}% <span color="{}" 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"),
|
|
cap,
|
|
colors::to_string(color),
|
|
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(60, 0),
|
|
data: Some(data),
|
|
});
|
|
}
|
|
Err(err) => eprintln!("Failed to update: {}", err),
|
|
}
|
|
None
|
|
}
|
|
}
|