|  | @@ -0,0 +1,121 @@
 | 
	
		
			
				|  |  | +use std::cmp;
 | 
	
		
			
				|  |  | +use std::fs::File;
 | 
	
		
			
				|  |  | +use std::io::prelude::*;
 | 
	
		
			
				|  |  | +use std::io;
 | 
	
		
			
				|  |  | +use super::Sensor;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +enum Status {
 | 
	
		
			
				|  |  | +    DISCHARGING,
 | 
	
		
			
				|  |  | +    CHARGING,
 | 
	
		
			
				|  |  | +    FULL
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +struct StatFiles {
 | 
	
		
			
				|  |  | +    capacity: File,
 | 
	
		
			
				|  |  | +    status: File
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +struct Stats {
 | 
	
		
			
				|  |  | +    capacity: u32,
 | 
	
		
			
				|  |  | +    status: Status
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +pub struct TPBatterySensor {
 | 
	
		
			
				|  |  | +    files: Vec<StatFiles>,
 | 
	
		
			
				|  |  | +    percentage: u32,
 | 
	
		
			
				|  |  | +    status: Status
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +impl TPBatterySensor {
 | 
	
		
			
				|  |  | +    pub fn new(devices: &Vec<&str>) -> TPBatterySensor {
 | 
	
		
			
				|  |  | +        let files: Vec<StatFiles> = devices.iter()
 | 
	
		
			
				|  |  | +            .flat_map(|dev| open_stats(&dev).ok())
 | 
	
		
			
				|  |  | +            .collect();
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        TPBatterySensor {
 | 
	
		
			
				|  |  | +            files: files,
 | 
	
		
			
				|  |  | +            percentage: 0,
 | 
	
		
			
				|  |  | +            status: Status::DISCHARGING
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    fn parse_status(s: &str) -> Status {
 | 
	
		
			
				|  |  | +        match s {
 | 
	
		
			
				|  |  | +            "Full" => Status::FULL,
 | 
	
		
			
				|  |  | +            "Charging" => Status::CHARGING,
 | 
	
		
			
				|  |  | +            _ => Status::DISCHARGING
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +impl Sensor for TPBatterySensor {
 | 
	
		
			
				|  |  | +    fn icon(&self) -> String {
 | 
	
		
			
				|  |  | +        match self.status {
 | 
	
		
			
				|  |  | +            Status::FULL => "",
 | 
	
		
			
				|  |  | +            Status::CHARGING => "",
 | 
	
		
			
				|  |  | +            Status::DISCHARGING => {
 | 
	
		
			
				|  |  | +                match (self.percentage+1) / 25 {
 | 
	
		
			
				|  |  | +                    0 => "",
 | 
	
		
			
				|  |  | +                    1 => "",
 | 
	
		
			
				|  |  | +                    2 => "",
 | 
	
		
			
				|  |  | +                    _ => ""
 | 
	
		
			
				|  |  | +                }
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +        }.to_string()
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    fn status(&self) -> String {
 | 
	
		
			
				|  |  | +        format!("{}%", cmp::min(100, self.percentage))
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    fn process(&mut self) {
 | 
	
		
			
				|  |  | +        let stats = self.files
 | 
	
		
			
				|  |  | +            .iter_mut()
 | 
	
		
			
				|  |  | +            .flat_map(|file| read_stats(file).ok())
 | 
	
		
			
				|  |  | +            .fold(Stats { capacity: 0, status: Status::FULL }, |acc, elem| Stats {
 | 
	
		
			
				|  |  | +                capacity: acc.capacity + elem.capacity,
 | 
	
		
			
				|  |  | +                status: combine_status(acc.status, elem.status)
 | 
	
		
			
				|  |  | +            });
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        self.percentage = stats.capacity / (self.files.len() as u32);
 | 
	
		
			
				|  |  | +        self.status = stats.status
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +fn combine_status(a: Status, b: Status) -> Status {
 | 
	
		
			
				|  |  | +    match (a, b) {
 | 
	
		
			
				|  |  | +        (_, Status::DISCHARGING) => Status::DISCHARGING,
 | 
	
		
			
				|  |  | +        (Status::DISCHARGING, _) => Status::DISCHARGING,
 | 
	
		
			
				|  |  | +        (_, Status::CHARGING) => Status::CHARGING,
 | 
	
		
			
				|  |  | +        (Status::CHARGING, _) => Status::CHARGING,
 | 
	
		
			
				|  |  | +        _ => Status::FULL
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +fn open_stats(device: &str) -> Result<StatFiles, io::Error> {
 | 
	
		
			
				|  |  | +    let path = format!("/sys/class/power_supply/{}", device);
 | 
	
		
			
				|  |  | +    let capacity_file = try!(File::open(format!("{}/capacity", path)));
 | 
	
		
			
				|  |  | +    let status_file = try!(File::open(format!("{}/status", path)));
 | 
	
		
			
				|  |  | +    Ok(StatFiles {
 | 
	
		
			
				|  |  | +        capacity: capacity_file,
 | 
	
		
			
				|  |  | +        status: status_file
 | 
	
		
			
				|  |  | +    })
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +fn read_stats(files: &mut StatFiles) -> Result<Stats, io::Error> {
 | 
	
		
			
				|  |  | +    let mut s = String::new();
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    assert!(files.capacity.read_to_string(&mut s).is_ok());
 | 
	
		
			
				|  |  | +    let i : u32 = s.trim().parse().unwrap();
 | 
	
		
			
				|  |  | +    assert!(files.capacity.seek(io::SeekFrom::Start(0)).is_ok());
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    s.clear();
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    assert!(files.status.read_to_string(&mut s).is_ok());
 | 
	
		
			
				|  |  | +    assert!(files.capacity.seek(io::SeekFrom::Start(0)).is_ok());
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    Ok(Stats {
 | 
	
		
			
				|  |  | +        capacity: i,
 | 
	
		
			
				|  |  | +        status: TPBatterySensor::parse_status(s.trim())
 | 
	
		
			
				|  |  | +    })
 | 
	
		
			
				|  |  | +}
 |