From 5e57dc3023a3ebceb2e98057f91af713d1a2da2f Mon Sep 17 00:00:00 2001 From: Felix Ortmann Date: Fri, 12 Feb 2021 10:10:30 +0100 Subject: [PATCH] Watch multiple batteries --- Cargo.lock | 347 +++++++++++++++++++++++++++++++++++++++++++++++++++- Cargo.toml | 4 +- src/main.rs | 65 +++++++--- 3 files changed, 391 insertions(+), 25 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 00ec6e9..8495a2c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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" diff --git a/Cargo.toml b/Cargo.toml index 1eb29c3..61ccf55 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,4 +9,6 @@ edition = "2018" [dependencies] clap = "2" crossbeam-channel = "0" -notify-rust = "4" \ No newline at end of file +futures = "0.3" +notify-rust = "4" +tokio = { version = "1.2", features = ["full"] } \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index d50d3e7..602e529 100644 --- a/src/main.rs +++ b/src/main.rs @@ -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 ") .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::().unwrap(); let step = matches .value_of("notification-step") @@ -56,15 +63,27 @@ fn main() { .unwrap(); let timeout = matches.value_of("timeout").unwrap().parse::().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(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(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); }