Watch multiple batteries

This commit is contained in:
Felix Ortmann 2021-02-12 10:10:30 +01:00
parent fddd1777bf
commit 5e57dc3023
3 changed files with 391 additions and 25 deletions

347
Cargo.lock generated
View file

@ -50,7 +50,9 @@ version = "0.2.0"
dependencies = [
"clap",
"crossbeam-channel",
"futures",
"notify-rust",
"tokio",
]
[[package]]
@ -82,6 +84,12 @@ version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a"
[[package]]
name = "bytes"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b700ce4376041dcd0a327fd0097c41095743c4c8af8887265942faf1100bd040"
[[package]]
name = "cc"
version = "1.0.66"
@ -176,6 +184,101 @@ dependencies = [
"winapi",
]
[[package]]
name = "futures"
version = "0.3.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da9052a1a50244d8d5aa9bf55cbc2fb6f357c86cc52e46c62ed390a7180cf150"
dependencies = [
"futures-channel",
"futures-core",
"futures-executor",
"futures-io",
"futures-sink",
"futures-task",
"futures-util",
]
[[package]]
name = "futures-channel"
version = "0.3.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2d31b7ec7efab6eefc7c57233bb10b847986139d88cc2f5a02a1ae6871a1846"
dependencies = [
"futures-core",
"futures-sink",
]
[[package]]
name = "futures-core"
version = "0.3.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "79e5145dde8da7d1b3892dad07a9c98fc04bc39892b1ecc9692cf53e2b780a65"
[[package]]
name = "futures-executor"
version = "0.3.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e9e59fdc009a4b3096bf94f740a0f2424c082521f20a9b08c5c07c48d90fd9b9"
dependencies = [
"futures-core",
"futures-task",
"futures-util",
]
[[package]]
name = "futures-io"
version = "0.3.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28be053525281ad8259d47e4de5de657b25e7bac113458555bb4b70bc6870500"
[[package]]
name = "futures-macro"
version = "0.3.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c287d25add322d9f9abdcdc5927ca398917996600182178774032e9f8258fedd"
dependencies = [
"proc-macro-hack",
"proc-macro2",
"quote 1.0.8",
"syn 1.0.60",
]
[[package]]
name = "futures-sink"
version = "0.3.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "caf5c69029bda2e743fddd0582d1083951d65cc9539aebf8812f36c3491342d6"
[[package]]
name = "futures-task"
version = "0.3.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13de07eb8ea81ae445aca7b69f5f7bf15d7bf4912d8ca37d6645c77ae8a58d86"
dependencies = [
"once_cell",
]
[[package]]
name = "futures-util"
version = "0.3.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "632a8cd0f2a4b3fdea1657f08bde063848c3bd00f9bbf6e256b8be78802e624b"
dependencies = [
"futures-channel",
"futures-core",
"futures-io",
"futures-macro",
"futures-sink",
"futures-task",
"memchr",
"pin-project-lite",
"pin-utils",
"proc-macro-hack",
"proc-macro-nested",
"slab",
]
[[package]]
name = "getrandom"
version = "0.1.15"
@ -196,6 +299,15 @@ dependencies = [
"libc",
]
[[package]]
name = "instant"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61124eeebbd69b8190558df225adf7e4caafce0d743919e5d6b19652314ec5ec"
dependencies = [
"cfg-if 1.0.0",
]
[[package]]
name = "lazy_static"
version = "1.4.0"
@ -217,6 +329,24 @@ dependencies = [
"pkg-config",
]
[[package]]
name = "lock_api"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd96ffd135b2fd7b973ac026d28085defbe8983df057ced3eb4f2130b0831312"
dependencies = [
"scopeguard",
]
[[package]]
name = "log"
version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
dependencies = [
"cfg-if 1.0.0",
]
[[package]]
name = "mac-notification-sys"
version = "0.3.0"
@ -238,6 +368,35 @@ dependencies = [
"libc",
]
[[package]]
name = "memchr"
version = "2.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525"
[[package]]
name = "mio"
version = "0.7.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e50ae3f04d169fcc9bde0b547d1c205219b7157e07ded9c5aff03e0637cb3ed7"
dependencies = [
"libc",
"log",
"miow",
"ntapi",
"winapi",
]
[[package]]
name = "miow"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a33c1b55807fbed163481b5ba66db4b2fa6cde694a5027be10fb724206c5897"
dependencies = [
"socket2",
"winapi",
]
[[package]]
name = "notify-rust"
version = "4.0.0"
@ -249,6 +408,15 @@ dependencies = [
"winrt-notification",
]
[[package]]
name = "ntapi"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f6bb902e437b6d86e03cce10a7e2af662292c5dfef23b65899ea3ac9354ad44"
dependencies = [
"winapi",
]
[[package]]
name = "num-integer"
version = "0.1.44"
@ -268,6 +436,16 @@ dependencies = [
"autocfg",
]
[[package]]
name = "num_cpus"
version = "1.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3"
dependencies = [
"hermit-abi",
"libc",
]
[[package]]
name = "objc"
version = "0.2.7"
@ -297,18 +475,91 @@ dependencies = [
"objc",
]
[[package]]
name = "once_cell"
version = "1.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13bd41f508810a131401606d54ac32a467c97172d74ba7662562ebba5ad07fa0"
[[package]]
name = "parking_lot"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d7744ac029df22dca6284efe4e898991d28e3085c706c972bcd7da4a27a15eb"
dependencies = [
"instant",
"lock_api",
"parking_lot_core",
]
[[package]]
name = "parking_lot_core"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ccb628cad4f84851442432c60ad8e1f607e29752d0bf072cbd0baf28aa34272"
dependencies = [
"cfg-if 1.0.0",
"instant",
"libc",
"redox_syscall",
"smallvec",
"winapi",
]
[[package]]
name = "pin-project-lite"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "439697af366c49a6d0a010c56a0d97685bc140ce0d377b13a2ea2aa42d64a827"
[[package]]
name = "pin-utils"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]]
name = "pkg-config"
version = "0.3.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c"
[[package]]
name = "proc-macro-hack"
version = "0.5.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5"
[[package]]
name = "proc-macro-nested"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc881b2c22681370c6a780e47af9840ef841837bc98118431d4e1868bd0c1086"
[[package]]
name = "proc-macro2"
version = "1.0.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71"
dependencies = [
"unicode-xid 0.2.1",
]
[[package]]
name = "quote"
version = "0.3.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a"
[[package]]
name = "quote"
version = "1.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "991431c3519a3f36861882da93630ce66b52918dcf1b8e2fd66b397fc96f28df"
dependencies = [
"proc-macro2",
]
[[package]]
name = "redox_syscall"
version = "0.1.57"
@ -338,6 +589,44 @@ dependencies = [
"crossbeam-utils",
]
[[package]]
name = "scopeguard"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
[[package]]
name = "signal-hook-registry"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "16f1d0fef1604ba8f7a073c7e701f213e056707210e9020af4528e0101ce11a6"
dependencies = [
"libc",
]
[[package]]
name = "slab"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8"
[[package]]
name = "smallvec"
version = "1.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e"
[[package]]
name = "socket2"
version = "0.3.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "122e570113d28d773067fab24266b66753f6ea915758651696b6e35e49f88d6e"
dependencies = [
"cfg-if 1.0.0",
"libc",
"winapi",
]
[[package]]
name = "strsim"
version = "0.8.0"
@ -356,8 +645,8 @@ version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3384590878eb0cab3b128e844412e2d010821e7e091211b9d87324173ada7db8"
dependencies = [
"quote",
"syn",
"quote 0.3.15",
"syn 0.11.11",
]
[[package]]
@ -366,9 +655,20 @@ version = "0.11.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3b891b9015c88c576343b9b3e41c2c11a51c219ef067b264bd9c8aa9b441dad"
dependencies = [
"quote",
"quote 0.3.15",
"synom",
"unicode-xid",
"unicode-xid 0.0.4",
]
[[package]]
name = "syn"
version = "1.0.60"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c700597eca8a5a762beb35753ef6b94df201c81cca676604f547495a0d7f0081"
dependencies = [
"proc-macro2",
"quote 1.0.8",
"unicode-xid 0.2.1",
]
[[package]]
@ -377,7 +677,7 @@ version = "0.11.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a393066ed9010ebaed60b9eafa373d4b1baac186dd7e008555b0f702b51945b6"
dependencies = [
"unicode-xid",
"unicode-xid 0.0.4",
]
[[package]]
@ -400,6 +700,37 @@ dependencies = [
"winapi",
]
[[package]]
name = "tokio"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e8190d04c665ea9e6b6a0dc45523ade572c088d2e6566244c1122671dbf4ae3a"
dependencies = [
"autocfg",
"bytes",
"libc",
"memchr",
"mio",
"num_cpus",
"once_cell",
"parking_lot",
"pin-project-lite",
"signal-hook-registry",
"tokio-macros",
"winapi",
]
[[package]]
name = "tokio-macros"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "caf7b11a536f46a809a8a9f0bb4237020f70ecbf115b842360afb127ea2fda57"
dependencies = [
"proc-macro2",
"quote 1.0.8",
"syn 1.0.60",
]
[[package]]
name = "unicode-width"
version = "0.1.8"
@ -412,6 +743,12 @@ version = "0.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8c1f860d7d29cf02cb2f3f359fd35991af3d30bac52c57d265a3c461074cb4dc"
[[package]]
name = "unicode-xid"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"
[[package]]
name = "vec_map"
version = "0.8.2"

View file

@ -9,4 +9,6 @@ edition = "2018"
[dependencies]
clap = "2"
crossbeam-channel = "0"
futures = "0.3"
notify-rust = "4"
tokio = { version = "1.2", features = ["full"] }

View file

@ -1,19 +1,22 @@
use clap::{App, Arg};
use crossbeam_channel::{select, tick};
use notify_rust::{Notification, Urgency};
use std::fs;
use std::{fs, path, process};
use std::time::Duration;
fn main() {
#[tokio::main]
async fn main() {
// parse CLI options
let matches = App::new("batalert")
.version("0.2.0")
.version("0.3.0")
.author("Felix Ortmann <flx.ortmann@gmail.com>")
.about("Sends D-Bus notification when battery runs low.")
.arg(
Arg::with_name("uevent")
.short("u")
.long("uevent")
.takes_value(true)
.multiple(true)
.default_value("/sys/class/power_supply/BAT0/uevent")
.help("Read the battery capacity from this uevent file."),
)
@ -21,6 +24,7 @@ fn main() {
Arg::with_name("alert")
.short("a")
.long("alert")
.takes_value(true)
.default_value("15")
.help("Send the first notification when battery falls below this threshold"),
)
@ -28,6 +32,7 @@ fn main() {
Arg::with_name("notification-step")
.short("n")
.long("notification-step")
.takes_value(true)
.default_value("3")
.help("Repeat notifications for every n percent the battery discharges"),
)
@ -35,6 +40,7 @@ fn main() {
Arg::with_name("timeout")
.short("t")
.long("timeout")
.takes_value(true)
.default_value("15")
.help("Hide the notification after t seconds"),
)
@ -42,12 +48,13 @@ fn main() {
Arg::with_name("icon")
.short("i")
.long("icon")
.takes_value(true)
.default_value("/usr/share/icons/Adwaita/256x256/legacy/battery-caution.png")
.help("Use this icon (PNG) when displaying notifications"),
)
.get_matches();
let uevt_file = matches.value_of("uevent").unwrap();
let icon = matches.value_of("icon").unwrap();
let uevt_files = matches.values_of("uevent").unwrap();
let icon = matches.value_of("icon").unwrap().to_owned();
let threshold = matches.value_of("alert").unwrap().parse::<i8>().unwrap();
let step = matches
.value_of("notification-step")
@ -56,15 +63,27 @@ fn main() {
.unwrap();
let timeout = matches.value_of("timeout").unwrap().parse::<i32>().unwrap();
// periodic task to compare battery level with configuration
let notify = |cap| send_notification(cap, icon, timeout * 1000);
watch(&uevt_file, threshold, step, notify);
let mut watchers = vec![];
for uevt_file in uevt_files {
if !path::Path::new(&uevt_file).exists() {
println!("File {} does not exist", &uevt_file);
process::exit(1);
}
// periodic task to compare battery level with configuration
let ic = icon.clone();
let file = uevt_file.to_owned().clone();
watchers.push(tokio::spawn(async move {
watch(&file, threshold, step, ic, timeout).await;
}));
}
futures::future::join_all(watchers).await;
}
// Puts a notification to the D-Bus
fn send_notification(cap: i8, icon: &str, timeout: i32) {
fn send_notification(bat_name: String, cap: i8, icon: &str, timeout: i32) {
Notification::new()
.summary(&format!("Battery {}%", cap))
.summary(&format!("Battery {} - {}%", bat_name, cap))
.body("Charge your battery soon to avoid shutdown")
.icon(icon)
.urgency(Urgency::Critical)
@ -73,18 +92,17 @@ fn send_notification(cap: i8, icon: &str, timeout: i32) {
.unwrap();
}
// Checks the current battery percentage. Calls the notification function
// periodically in the configured `step` interval if the battery discharges and
// is below the configured threshold
fn watch<F: Fn(i8)>(uevt_file: &str, threshold: i8, step: i8, notify: F) {
// Periodically checks the battery status and sends notifications to the user if
// the battery discharges and is below the configured threshold.
async fn watch(uevt_file: &str, threshold: i8, step: i8, icon: String, timeout: i32) {
let ticker = tick(Duration::from_millis(5000));
let mut alert_threshold = threshold;
loop {
select! {
recv(ticker) -> _ => {
let (cap, status) = extract_status(&uevt_file);
let (name, cap, status) = extract_info(&uevt_file);
if status.to_lowercase() == "discharging" && cap <= alert_threshold {
notify(cap);
send_notification(name, cap, &icon, timeout * 1000);
alert_threshold = cap - step;
}
else if status.to_lowercase() == "charging" {
@ -95,8 +113,8 @@ fn watch<F: Fn(i8)>(uevt_file: &str, threshold: i8, step: i8, notify: F) {
}
}
// Extracts the battery capacity (percentage) and charging status
fn extract_status(uevt_file: &str) -> (i8, String) {
// Extracts the battery name, capacity (percentage), and charging status
fn extract_info(uevt_file: &str) -> (String, i8, String) {
let contents = fs::read_to_string(uevt_file).expect("Something went wrong reading the file");
let cap = contents
@ -117,5 +135,14 @@ fn extract_status(uevt_file: &str) -> (i8, String) {
.nth(1)
.unwrap()
.to_string();
return (cap, status);
let bat_name = contents
.lines()
.find(|s| s.contains("POWER_SUPPLY_NAME"))
.unwrap()
.split("=")
.nth(1)
.unwrap()
.to_string();
return (bat_name, cap, status);
}