pub mod config;
pub mod notification;
use notification::{BatteryFullNotification, Urgency};
use std::fs::File;
use std::io::prelude::*;
use std::{collections::HashMap, process::Command};
pub type Battery = str;
pub const DEFAULT_BATTERY: &Battery = "BAT0";
pub fn get_charging_status_path(battery: Option<&Battery>) -> String {
let battery = battery.as_deref().unwrap_or(DEFAULT_BATTERY);
format!("/sys/class/power_supply/{}/status", battery)
}
pub fn get_power_status_path(battery: Option<&Battery>) -> String {
let battery = battery.as_deref().unwrap_or(DEFAULT_BATTERY);
format!("/sys/class/power_supply/{}/capacity", battery)
}
pub fn get_current_power(battery: Option<&Battery>) -> u32 {
let power_status_path = get_power_status_path(battery);
let mut file = File::open(power_status_path).unwrap();
let mut contents = String::new();
file.read_to_string(&mut contents).unwrap();
contents.trim().parse().expect("failed to parse number")
}
#[derive(Debug)]
pub enum ChargingStatus {
Charging,
Discharging,
Full,
Unknown,
}
impl ChargingStatus {
fn as_str(&self) -> &'static str {
match *self {
ChargingStatus::Charging => "charging",
ChargingStatus::Discharging => "discharging",
ChargingStatus::Full => "full",
ChargingStatus::Unknown => "unknown",
}
}
fn as_string(&self) -> String {
self.as_str().to_owned()
}
}
pub fn get_status_charging(battery: Option<&Battery>) -> String {
let status_charging_path = get_charging_status_path(battery);
let mut file = File::open(status_charging_path).unwrap();
let mut contents = String::new();
file.read_to_string(&mut contents).unwrap();
match contents.trim() {
"Charging" => ChargingStatus::Charging.as_string(),
"Discharging" => ChargingStatus::Discharging.as_string(),
"Full" | "Not charging" => ChargingStatus::Full.as_string(),
_ => ChargingStatus::Unknown.as_string(),
}
}
pub fn send_message(title: &str, message: &str, urgency: &Urgency, time_secs: Option<u32>) {
let mut notification = notify_rust::Notification::new();
notification
.summary(title)
.body(message)
.urgency(notify_rust::Urgency::from(urgency));
if let Some(wait_time) = time_secs {
notification.timeout(notify_rust::Timeout::Milliseconds(wait_time * 1000));
}
notification.show().unwrap();
}
pub fn run_command(command: &str) {
let args_res = shell_words::split(command);
if args_res.is_err() {
eprintln!(
"Could not run command: {}, err: {:?}",
command.to_owned(),
args_res
);
return;
}
let actual_args = args_res.unwrap();
match actual_args.as_slice() {
[first, rest @ ..] => {
let output = Command::new(first)
.args(rest)
.output()
.unwrap_or_else(|_| panic!("Failed to run command {}", command));
if !output.status.success() {
eprintln!("status: {}", output.status);
eprintln!("stderr: {}", String::from_utf8_lossy(&output.stderr));
eprintln!("stdout: {}", String::from_utf8_lossy(&output.stdout));
}
}
[] => {
eprintln!("Missing command for running");
}
}
}
pub fn send_notification(level: &u32, notification: ¬ification::Notification) {
let title = notification
.title
.clone()
.unwrap_or("Battery Status".to_string());
let message = notification.message.clone().unwrap_or("{}".to_string());
let percent = format!("{}", level);
send_message(
&title.replace("{}", &percent),
&message.replace("{}", &percent),
¬ification.urgency,
notification.time_secs,
);
if notification.command.is_some() {
run_command(notification.command.as_ref().unwrap());
}
}
pub fn notify_now(level: &u32) {
let percent = format!("{}%", level);
let default_wait_time = 10; send_message(
"Battery Status",
&percent,
&Urgency::Normal,
Some(default_wait_time),
);
}
pub fn find_lowest_threshold(
current: u32,
notified: &HashMap<u32, notification::Notification>,
) -> Option<u32> {
let keys = notified.keys().cloned().collect::<Vec<_>>();
keys.into_iter().filter(|&key| key >= current).min()
}
pub fn reset_other_notifications(
threshold_val: &u32,
notified: &mut HashMap<u32, notification::Notification>,
) {
for (key, notification) in notified.iter_mut() {
if *key != *threshold_val {
notification.notified = false;
}
}
}
pub fn check_notify_full_battery(
current: &u32,
last: &u32,
full_notification: &mut BatteryFullNotification,
) {
if full_notification.notified || !full_notification.enabled {
return;
}
if *last >= *current {
full_notification.notified = false;
return;
}
let title = full_notification
.title
.clone()
.unwrap_or("Battery Status".to_string());
let message = full_notification
.message
.clone()
.unwrap_or("Fully Charged 100%".to_string());
if *current >= 100 {
send_message(&title, &message, &full_notification.urgency, None);
if full_notification.command.is_some() {
run_command(full_notification.command.as_ref().unwrap());
}
full_notification.notified = true;
}
}