From 94d55e5d3aa431862ec8dbcd16d669ee1730825a Mon Sep 17 00:00:00 2001 From: Lucien Cartier-Tilet Date: Mon, 10 Aug 2020 18:26:41 +0200 Subject: [PATCH] initial commit --- .gitignore | 1 + Cargo.lock | 470 +++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 13 ++ README.org | 50 +++++ src/battery_state.rs | 105 ++++++++++ src/main.rs | 46 +++++ 6 files changed, 685 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 README.org create mode 100644 src/battery_state.rs create mode 100644 src/main.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..397a84d --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,470 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "arrayref" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" + +[[package]] +name = "arrayvec" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cff77d8686867eceff3105329d4698d96c2391c176d5d03adc90c7389162b5b8" + +[[package]] +name = "autocfg" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" + +[[package]] +name = "base64" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b41b7ea54a0c9d92199de89e20e58d49f02f8e699814ef3fdf266f6f748d15c7" + +[[package]] +name = "battery" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36a698e449024a5d18994a815998bf5e2e4bc1883e35a7d7ba95b6b69ee45907" +dependencies = [ + "cfg-if", + "core-foundation", + "lazycell", + "libc", + "mach", + "nix", + "num-traits", + "uom", + "winapi", +] + +[[package]] +name = "bitflags" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4efd02e230a02e18f92fc2735f44597385ed02ad8f831e7c1c1156ee5e1ab3a5" + +[[package]] +name = "bitflags" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" + +[[package]] +name = "blake2b_simd" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8fb2d74254a3a0b5cac33ac9f8ed0e44aa50378d9dbb2e5d83bd21ed1dc2c8a" +dependencies = [ + "arrayref", + "arrayvec", + "constant_time_eq", +] + +[[package]] +name = "block" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" + +[[package]] +name = "cc" +version = "1.0.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9a06fb2e53271d7c279ec1efea6ab691c35a2ae67ec0d91d7acec0caf13b518" + +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + +[[package]] +name = "chrono" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c74d84029116787153e02106bf53e66828452a4b325cc8652b788b5967c0a0b6" +dependencies = [ + "num-integer", + "num-traits", + "time", +] + +[[package]] +name = "constant_time_eq" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" + +[[package]] +name = "core-foundation" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25b9e03f145fd4f2bf705e07b900cd41fc636598fe5dc452fd0db1441c3f496d" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7ca8a5221364ef15ce201e8ed2f609fc312682a8f4e0e3d4aa5879764e0fa3b" + +[[package]] +name = "crossbeam-utils" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8" +dependencies = [ + "autocfg", + "cfg-if", + "lazy_static", +] + +[[package]] +name = "dbus" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cd9e78c210146a1860f897db03412fd5091fd73100778e43ee255cca252cf32" +dependencies = [ + "libc", + "libdbus-sys", +] + +[[package]] +name = "dirs" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fd78930633bd1c6e35c4b42b1df7b0cbc6bc191146e512bb3bedf243fcc3901" +dependencies = [ + "libc", + "redox_users", + "winapi", +] + +[[package]] +name = "getrandom" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "lazycell" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b294d6fa9ee409a054354afc4352b0b9ef7ca222c69b8812cbea9e7d2bf3783f" + +[[package]] +name = "libc" +version = "0.2.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2f02823cf78b754822df5f7f268fb59822e7296276d3e069d8e8cb26a14bd10" + +[[package]] +name = "libdbus-sys" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc12a3bc971424edbbf7edaf6e5740483444db63aa8e23d3751ff12a30f306f0" +dependencies = [ + "pkg-config", +] + +[[package]] +name = "mac-notification-sys" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dfb6b71a9a89cd38b395d994214297447e8e63b1ba5708a9a2b0b1048ceda76" +dependencies = [ + "cc", + "chrono", + "dirs", + "objc-foundation", +] + +[[package]] +name = "mach" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86dd2487cdfea56def77b88438a2c915fb45113c5319bfe7e14306ca4cd0b0e1" +dependencies = [ + "libc", +] + +[[package]] +name = "malloc_buf" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" +dependencies = [ + "libc", +] + +[[package]] +name = "nix" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b2e0b4f3320ed72aaedb9a5ac838690a8047c7b275da22711fddff4f8a14229" +dependencies = [ + "bitflags 1.2.1", + "cc", + "cfg-if", + "libc", + "void", +] + +[[package]] +name = "notify-rust" +version = "4.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "144acee6a0543dc74893e4b8a33936b5b0a94cc2d4ab024afd0c6daff7afc3c0" +dependencies = [ + "dbus", + "mac-notification-sys", + "winrt-notification", +] + +[[package]] +name = "num-integer" +version = "0.1.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d59457e662d541ba17869cf51cf177c0b5f0cbf476c66bdc90bf1edac4f875b" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac267bcc07f48ee5f8935ab0d24f316fb722d7a1292e2913f0cc196b29ffd611" +dependencies = [ + "autocfg", +] + +[[package]] +name = "objc" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" +dependencies = [ + "malloc_buf", +] + +[[package]] +name = "objc-foundation" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1add1b659e36c9607c7aab864a76c7a4c2760cd0cd2e120f3fb8b952c7e22bf9" +dependencies = [ + "block", + "objc", + "objc_id", +] + +[[package]] +name = "objc_id" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c92d4ddb4bd7b50d730c215ff871754d0da6b2178849f8a2a2ab69712d0c073b" +dependencies = [ + "objc", +] + +[[package]] +name = "pkg-config" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d36492546b6af1463394d46f0c834346f31548646f6ba10849802c9c9a27ac33" + +[[package]] +name = "power-manager" +version = "0.1.0" +dependencies = [ + "battery", + "notify-rust", +] + +[[package]] +name = "quote" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a" + +[[package]] +name = "redox_syscall" +version = "0.1.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" + +[[package]] +name = "redox_users" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09b23093265f8d200fa7b4c2c76297f47e681c655f6f1285a8780d6a022f7431" +dependencies = [ + "getrandom", + "redox_syscall", + "rust-argon2", +] + +[[package]] +name = "rust-argon2" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bc8af4bda8e1ff4932523b94d3dd20ee30a87232323eda55903ffd71d2fb017" +dependencies = [ + "base64", + "blake2b_simd", + "constant_time_eq", + "crossbeam-utils", +] + +[[package]] +name = "strum" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ca6e4730f517e041e547ffe23d29daab8de6b73af4b6ae2a002108169f5e7da" + +[[package]] +name = "strum_macros" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3384590878eb0cab3b128e844412e2d010821e7e091211b9d87324173ada7db8" +dependencies = [ + "quote", + "syn", +] + +[[package]] +name = "syn" +version = "0.11.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3b891b9015c88c576343b9b3e41c2c11a51c219ef067b264bd9c8aa9b441dad" +dependencies = [ + "quote", + "synom", + "unicode-xid", +] + +[[package]] +name = "synom" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a393066ed9010ebaed60b9eafa373d4b1baac186dd7e008555b0f702b51945b6" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "time" +version = "0.1.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "typenum" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33" + +[[package]] +name = "unicode-xid" +version = "0.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c1f860d7d29cf02cb2f3f359fd35991af3d30bac52c57d265a3c461074cb4dc" + +[[package]] +name = "uom" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cec796ec5f7ac557631709079168286056205c51c60aac33f51764bdc7b8dc4" +dependencies = [ + "num-traits", + "typenum", +] + +[[package]] +name = "void" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" + +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "winrt" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e30cba82e22b083dc5a422c2ee77e20dc7927271a0dc981360c57c1453cb48d" +dependencies = [ + "winapi", +] + +[[package]] +name = "winrt-notification" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c31a65da50d792c6f9bd2e3216249566c4fb1d2d34f9b7d2d66d2e93f62a242" +dependencies = [ + "strum", + "strum_macros", + "winapi", + "winrt", + "xml-rs", +] + +[[package]] +name = "xml-rs" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1945e12e16b951721d7976520b0832496ef79c31602c7a29d950de79ba74621" +dependencies = [ + "bitflags 0.9.1", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..fae1ac0 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "pumopm" +version = "0.1.0" +authors = ["Lucien Cartier-Tilet "] +edition = "2018" +description = "Simple power manager for systemd-based Linux" +repository = "https://labs.phundrak.com/phundrak/pumopm" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +battery = "0.7.5" +notify-rust = "4" diff --git a/README.org b/README.org new file mode 100644 index 0000000..87d21b2 --- /dev/null +++ b/README.org @@ -0,0 +1,50 @@ +* Table of Contents :TOC_3_gh: +- [[#about-pumopm][About PumoPM]] + - [[#built-with][Built with]] + - [[#what-does-pumo-mean][What does Pumo mean?]] +- [[#getting-started][Getting started]] +- [[#usage][Usage]] +- [[#roadmap][Roadmap]] +- [[#contributing][Contributing]] +- [[#license][License]] + +* About PumoPM + The Pumo Power Manager (PumoPM for short) is a custom power manager I wrote + for myself in order to have a lightweight one that does exactly what I want, + how I want. Basically: I want to be informed of a low level of battery (I + consider 25% to be low, hence the defaults), very low (again, my preference is + at 15%), and then at critical level (10%) I want the computer to automatically + sleep, and if it stays asleep for a certain duration it will be suspended. + + This project is written for a GNU/Linux operating system running with systemd. + I have currently no plan on making it available on other platforms, except + maybe one day with [[https://guix.gnu.org/][Guix]] (if you don’t value your time, take a look at it, it’s + an awesome operating system, much better than NixOS in my opinion). + +** Built with + [[http://spacemacs.org][file:https://cdn.rawgit.com/syl20bnr/spacemacs/442d025779da2f62fc86c2082703697714db6514/assets/spacemacs-badge.svg]] + + PumoPM is written in pure, stable Rust (at least >= 1.45.2). Its only two + dependencies are the crates [[https://github.com/svartalf/rust-battery][battery]] and [[https://github.com/hoodie/notify-rust][notify-rust]]. + +** What does Pumo mean? + It’s my cat’s name. + +* Getting started + To run the project, you currently only have one option: download the project’s + source code, compile it, and run it. You will need to have [[https://www.rust-lang.org/][Rust installed]]. + Update your Rust installation if needed with ~rustup update~. Then, simply + begin with a ~git clone~, then cd into the project’s directory. You can then + build the project with ~cargo~, and even install it locally if you want to. + #+BEGIN_SRC sh + $ git clone https://labs.phundrak.com/phundrak/pumopm + $ cd pumopm + $ cargo build --release + $ cargo run --release # to run the project + $ cargo install # optional, to install for the user the project + #+END_SRC + +* Usage +* Roadmap +* Contributing +* License diff --git a/src/battery_state.rs b/src/battery_state.rs new file mode 100644 index 0000000..be3e7e6 --- /dev/null +++ b/src/battery_state.rs @@ -0,0 +1,105 @@ +use notify_rust::{Notification, Hint, Urgency}; + +pub struct BatteryState { + manager: battery::Manager, + battery: battery::Battery, + low_level: f32, + very_low_level: f32, + pub is_triggered_low: bool, + pub is_triggered_very_low: bool, +} + +impl BatteryState { + pub fn new(low_level: f32, very_low_level: f32) -> battery::Result { + let manager = battery::Manager::new().unwrap(); + let battery = match manager.batteries().unwrap().next() { + Some(Ok(battery)) => battery, + Some(Err(e)) => { + eprintln!("An error occured: {:?}", e); + return Err(e); + } + None => { + eprintln!("Unable to find any battery"); + use std::io; + return Err(io::Error::from(io::ErrorKind::NotFound).into()); + } + }; + + Ok(Self { + manager, + battery, + low_level, + very_low_level, + is_triggered_low: false, + is_triggered_very_low: false, + }) + } + + fn get_charge(&self) -> f32 { + self.battery.state_of_charge().value * 100.0 + } + + fn reset_levels(&mut self) { + self.is_triggered_low = false; + self.is_triggered_very_low = false; + } + + fn trigger_low(&mut self) { + if !self.is_triggered_low { + self.is_triggered_low = true; + let time = match self.battery.time_to_empty() { + Some(e) => e.value.to_string(), + None => String::from("unknown"), + }; + let level = self.battery.state_of_charge().value * 100.0; + let message = format!("Battery level is low! {} left ({}%)", time, level); + Notification::new() + .summary("Low Battery") + .body(message.as_str()) + .hint(Hint::Category("battery".to_owned())) + .urgency(Urgency::Normal) + .show() + .unwrap(); + } + } + + fn trigger_very_low(&mut self) { + if !self.is_triggered_very_low { + self.is_triggered_very_low = true; + let time = match self.battery.time_to_empty() { + Some(e) => e.value.to_string(), + None => String::from("unknown"), + }; + let level = self.battery.state_of_charge().value * 100.0; + let message = format!("Battery level is very low! {} left ({}%)", time, level); + Notification::new() + .summary("Low Battery") + .body(message.as_str()) + .hint(Hint::Category("battery".to_owned())) + .urgency(Urgency::Critical) + .show() + .unwrap(); + } + } + + pub fn update(&mut self) { + self.manager.refresh(&mut self.battery).unwrap(); + use battery::State::{Discharging, Empty}; + match self.battery.state() { + Discharging | Empty => { + match self.get_charge() { + x if x < self.very_low_level => self.trigger_very_low(), + x if x < self.low_level => self.trigger_low(), + _ => {}, + } + }, + _ => self.reset_levels(), + } + } +} + +impl Default for BatteryState { + fn default() -> Self { + Self::new(15 as f32, 10 as f32).unwrap() + } +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..75c69cd --- /dev/null +++ b/src/main.rs @@ -0,0 +1,46 @@ +mod battery_state; + +use std::{io, thread}; +use std::{process::Command, time::Duration}; + +fn main() { + let mut battery = battery_state::BatteryState::default(); + + loop { + thread::sleep(Duration::from_secs(5)); + battery.update(); + } + + // loop { + // thread::sleep(Duration::from_secs(5)); + // manager.refresh(&mut battery)?; + // let charge = battery.state_of_charge().value; + + // // let charge = charge.value as f32 * 100.0; + + // // Notification::new() + // // .summary("Battery charge") + // // .body(format!("Current battery level is {}", charge).as_str()) + // // .hint(Hint::Category("battery".to_owned())) + // // .urgency(Urgency::Low) + // // .show() + // // .unwrap(); + + // // let result = match charge.value as f32 * 100.0 { + // // x if x < 5.0 => Command::new("sh") + // // .arg("systemctl") + // // .arg("hibernate") + // // .output(), + // // x if x < 10.0 => { + // // Notification::new() + // // .summary("Battery very low") + // // .body(format!("Current battery level is {}", x).as_str()) + // // .hint(Hint::Category("battery".to_owned())) + // // .urgency(Urgency::Critical) + // // .show().unwrap(); + // // }, + // // _ => Ok(()), + // // }; + + // } +}