summaryrefslogtreecommitdiff
path: root/src/main.rs
blob: dbf2a8135a43642c14fbe7b8be24cc63ccc7467f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
use std::str;
use std::fs::OpenOptions;
use std::io::{Result, Read, Write, Error, ErrorKind};
use std::convert::AsRef;
use std::path::Path;
use std::thread::sleep;
use std::time::Duration;

const PATH_ENERGY_NOW: &str = "/sys/class/power_supply/BAT0/energy_now";
const PATH_ENERGY_FULL: &str = "/sys/class/power_supply/BAT0/energy_full_design";
const PATH_LIGHT: &str = "/sys/devices/platform/thinkpad_acpi/leds/tpacpi::power/brightness";

fn error(msg: &str) -> Error {
    Error::new(ErrorKind::Other, msg)
}

macro_rules! mytry {
    ($expr : expr, $msg : expr) => (match $expr {
        Ok(val) => val,
        Err(err) => {
            return Err(Error::new(ErrorKind::Other, format!("{}: {:?}", $msg, err)))
        }
    });
}

fn file_read_integer<P: AsRef<Path>>(path: P) -> Result<u64> {
    let mut buf = [0; 20];
    let mut file = mytry!(OpenOptions::new().read(true).open(path.as_ref()),
        "Failed to open file");
    let len = mytry!(file.read(&mut buf), "Failed to read from file");
    if len < 1 {
        return Err(error("Failed to read value, file is empty"));
    }

    let s = mytry!(str::from_utf8(&buf[0..len]), "UTF-8 error when reading file");
    Ok(mytry!(s.trim().parse(), "Failed to parse String"))
}

fn read_battery_percentage() -> Result<u32> {
    let now = mytry!(file_read_integer(PATH_ENERGY_NOW), "Failed to read energy_now");
    let full = mytry!(file_read_integer(PATH_ENERGY_FULL), "Failed to read energy_full");
    Ok(((now * 100) / full) as u32)
}

fn write_brightness(level: u8) -> Result<()> {
    let string = format!("{}\n", level);
    let mut file = mytry!(OpenOptions::new().write(true).open(PATH_LIGHT), "Failed to open PATH_LIGHT");
    let bytes_written = mytry!(file.write(string.as_bytes()), "Failed to write brightness file");
    if bytes_written == string.len() {
        Ok(())
    } else {
        Err(error("Failed to write all bytes to brightness file"))
    }
}

fn read_brightness() -> Result<u8> {
    let level = mytry!(file_read_integer(PATH_LIGHT), "Failed to read PATH_LIGHT");
    Ok(level as u8)
}

fn indicate_battery_level(level: u32) -> Result<()> {
    let led_state = mytry!(read_brightness(), "Reading brightness failed");
    let flashing_time: u32 = 10000;
    let offtime: u32 = 40;
    let ontime: u32 = 20;
    let n = flashing_time / (level * offtime + level * ontime);
    for _ in 0..n {
        mytry!(write_brightness(0), "Failed to switch off brightness");
        sleep(Duration::from_millis(u64::from(level * offtime)));
        mytry!(write_brightness(1), "Failed to switch on brightness");
        sleep(Duration::from_millis(u64::from(level * ontime)));
    }
    mytry!(write_brightness(led_state), "Failed to re-set brightness");
    Ok(())
}

fn main() {
    loop {
        match read_battery_percentage() {
            Ok(level) => {
                if level < 20 {
                    if let Err(e) = indicate_battery_level(level) {
                        panic!("Error: {}", e);
                    }
                }
            }
            Err(e) => panic!("Error: {}", e),
        }
        sleep(Duration::from_secs(60));
    }
}