Skip to content

Commit

Permalink
read only uevent file, clean
Browse files Browse the repository at this point in the history
  • Loading branch information
doums committed Oct 19, 2020
1 parent 1b9d824 commit f1d9d09
Show file tree
Hide file tree
Showing 2 changed files with 99 additions and 66 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "bato"
version = "0.1.2"
version = "0.1.3"
authors = ["pierre <dommerc.pierre@gmail.com>"]
edition = "2018"
links = "notilus"
Expand Down
163 changes: 98 additions & 65 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,21 @@ use serde::{Deserialize, Serialize};
use std::convert::TryFrom;
use std::env;
use std::ffi::CString;
use std::fs;
use std::fs::{self, File};
use std::io::{self, prelude::*, BufReader};
use std::ptr;

const APP_NAME: &str = "bato";
const SYS_PATH: &str = "/sys/class/power_supply/";
const BAT_NAME: &str = "BAT0";
const CHARGE_PREFIX: &str = "charge";
const ENERGY_PREFIX: &str = "energy";
const FULL_ATTRIBUTE: &str = "full";
const FULL_DESIGN_ATTRIBUTE: &str = "full_design";
const NOW_ATTRIBUTE: &str = "now";
const UEVENT: &str = "uevent";
const POWER_SUPPLY: &str = "POWER_SUPPLY";
const CHARGE_PREFIX: &str = "CHARGE";
const ENERGY_PREFIX: &str = "ENERGY";
const FULL_ATTRIBUTE: &str = "FULL";
const FULL_DESIGN_ATTRIBUTE: &str = "FULL_DESIGN";
const NOW_ATTRIBUTE: &str = "NOW";
const STATUS_ATTRIBUTE: &str = "POWER_SUPPLY_STATUS";

#[derive(Debug, Serialize, Deserialize, Clone, Copy)]
#[repr(C)]
Expand Down Expand Up @@ -52,20 +56,20 @@ pub struct Config {
discharging: Option<Notification>,
}

pub struct Bato<'a> {
bat_name: String,
attribute_prefix: &'a str,
pub struct Bato {
uevent: String,
now_attribute: String,
full_attribute: String,
notification: *mut NotifyNotification,
config: Config,
critical_notified: bool,
low_notified: bool,
full_design: bool,
full_notified: bool,
status_notified: bool,
previous_status: String,
}

impl<'a> Bato<'a> {
impl Bato {
pub fn with_config(mut config: Config) -> Result<Self, Error> {
let bat_name = if let Some(v) = &config.bat_name {
String::from(v)
Expand All @@ -91,14 +95,21 @@ impl<'a> Bato<'a> {
if let Some(v) = config.full_design {
full_design = v;
}
let attribute_prefix = find_attribute_prefix(&bat_name)?;
let full_attr = match full_design {
true => FULL_DESIGN_ATTRIBUTE,
false => FULL_ATTRIBUTE,
};
let uevent = format!("{}{}/{}", SYS_PATH, &bat_name, UEVENT);
let attribute_prefix = find_attribute_prefix(&uevent)?;
let now_attribute = format!("{}_{}_{}", POWER_SUPPLY, attribute_prefix, NOW_ATTRIBUTE);
let full_attribute = format!("{}_{}_{}", POWER_SUPPLY, attribute_prefix, full_attr);
Ok(Bato {
bat_name,
attribute_prefix,
uevent,
now_attribute,
full_attribute,
config,
critical_notified: false,
notification: ptr::null_mut(),
full_design,
low_notified: false,
full_notified: false,
status_notified: false,
Expand All @@ -115,21 +126,37 @@ impl<'a> Bato<'a> {
Ok(())
}

fn attribute(&self, attribute: &str) -> Result<i32, Error> {
read_and_parse(&format!(
"{}{}/{}_{}",
SYS_PATH, self.bat_name, self.attribute_prefix, attribute
))
fn parse_attributes(&self) -> Result<(i32, i32, String), Error> {
let file = File::open(&self.uevent)?;
let f = BufReader::new(file);
let mut now = None;
let mut full = None;
let mut status = None;
for line in f.lines() {
if now.is_none() {
now = parse_attribute(&line, &self.now_attribute);
}
if full.is_none() {
full = parse_attribute(&line, &self.full_attribute);
}
if status.is_none() {
status = parse_status(&line);
}
}
if now.is_none() || full.is_none() || status.is_none() {
return Err(Error::new(format!(
"unable to parse the required attributes in {}",
self.uevent
)));
}
Ok((now.unwrap(), full.unwrap(), status.unwrap()))
}

pub fn check(&mut self) -> Result<(), Error> {
let mut current_notification: Option<&Notification> = None;
let capacity = match self.full_design {
true => self.attribute(FULL_DESIGN_ATTRIBUTE)?,
false => self.attribute(FULL_ATTRIBUTE)?,
} as u64;
let energy = self.attribute(NOW_ATTRIBUTE)? as u64;
let status = read_and_trim(&format!("{}{}/status", SYS_PATH, self.bat_name))?;
let (energy, capacity, status) = self.parse_attributes()?;
let capacity = capacity as u64;
let energy = energy as u64;
let battery_level = u32::try_from(100_u64 * energy / capacity)?;
if status == "Charging" && self.previous_status != "Charging" && !self.status_notified {
self.status_notified = true;
Expand Down Expand Up @@ -193,37 +220,57 @@ impl<'a> Bato<'a> {
}
}

fn find_attribute_prefix<'a, 'b>(bat_name: &'a str) -> Result<&'b str, Error> {
let entries: Vec<String> = fs::read_dir(format!("{}{}", SYS_PATH, bat_name))?
.map(|res| {
res.map_or("error".to_string(), |e| {
e.file_name().into_string().unwrap()
})
})
.filter(|v| v != "error")
.collect();
let energy_now_attribute = format!("{}_{}", ENERGY_PREFIX, NOW_ATTRIBUTE);
let energy_full_attribute = format!("{}_{}", ENERGY_PREFIX, FULL_ATTRIBUTE);
let energy_full_design_attribute = format!("{}_{}", ENERGY_PREFIX, FULL_DESIGN_ATTRIBUTE);
let charge_now_attribute = format!("{}_{}", CHARGE_PREFIX, NOW_ATTRIBUTE);
let charge_full_attribute = format!("{}_{}", CHARGE_PREFIX, FULL_ATTRIBUTE);
let charge_full_design_attribute = format!("{}_{}", CHARGE_PREFIX, FULL_DESIGN_ATTRIBUTE);
fn parse_attribute(line: &io::Result<String>, attribute: &str) -> Option<i32> {
if let Ok(l) = line {
if l.starts_with(&attribute) {
let s = l.split('=').nth(1);
if let Some(v) = s {
return v.parse::<i32>().ok();
}
}
}
None
}

fn parse_status(line: &io::Result<String>) -> Option<String> {
if let Ok(l) = line {
if l.starts_with(&STATUS_ATTRIBUTE) {
return l.split('=').nth(1).map(|s| s.to_string());
}
}
None
}

fn find_attribute_prefix<'a, 'b>(path: &'a str) -> Result<&'b str, Error> {
let content = fs::read_to_string(path)?;
let mut unit = None;
if entries.iter().any(|v| v == &energy_now_attribute)
&& entries.iter().any(|v| v == &energy_full_attribute)
&& entries.iter().any(|v| v == &energy_full_design_attribute)
{
if content.contains(&format!(
"{}_{}_{}=",
POWER_SUPPLY, ENERGY_PREFIX, FULL_DESIGN_ATTRIBUTE
)) && content.contains(&format!(
"{}_{}_{}=",
POWER_SUPPLY, ENERGY_PREFIX, FULL_ATTRIBUTE
)) && content.contains(&format!(
"{}_{}_{}=",
POWER_SUPPLY, ENERGY_PREFIX, NOW_ATTRIBUTE
)) {
unit = Some(ENERGY_PREFIX);
} else if entries.iter().any(|v| v == &charge_now_attribute)
&& entries.iter().any(|v| v == &charge_full_attribute)
&& entries.iter().any(|v| v == &charge_full_design_attribute)
{
} else if content.contains(&format!(
"{}_{}_{}=",
POWER_SUPPLY, CHARGE_PREFIX, FULL_DESIGN_ATTRIBUTE
)) && content.contains(&format!(
"{}_{}_{}=",
POWER_SUPPLY, CHARGE_PREFIX, FULL_ATTRIBUTE
)) && content.contains(&format!(
"{}_{}_{}=",
POWER_SUPPLY, CHARGE_PREFIX, NOW_ATTRIBUTE
)) {
unit = Some(CHARGE_PREFIX);
}
unit.ok_or_else(|| {
Error::new(format!(
"unable to find the required files under {}{}",
SYS_PATH, BAT_NAME
"unable to find the required attributes in {}",
path
))
})
}
Expand All @@ -237,17 +284,3 @@ pub fn deserialize_config() -> Result<Config, Error> {
.map_err(|err| format!("while deserializing the config file, {}", err))?;
Ok(config)
}

fn read_and_trim(file: &str) -> Result<String, Error> {
let content = fs::read_to_string(file)
.map_err(|err| format!("error while reading the file \"{}\": {}", file, err))?;
Ok(content.trim().to_string())
}

fn read_and_parse(file: &str) -> Result<i32, Error> {
let content = read_and_trim(file)?;
let data = content
.parse::<i32>()
.map_err(|err| format!("error while parsing the file \"{}\": {}", file, err))?;
Ok(data)
}

0 comments on commit f1d9d09

Please sign in to comment.