diff --git a/src/bar_modules/bar_module_power.rs b/src/bar_modules/bar_module_power.rs new file mode 100644 index 0000000..9bd7999 --- /dev/null +++ b/src/bar_modules/bar_module_power.rs @@ -0,0 +1,142 @@ +use std::process::{Stdio, Command}; +//use std::collections::HashMap; +use regex::Regex; + +use crate::common; +use crate::bar_modules; + +#[derive(Clone)] +pub struct UnibarModulePower { + opts: common::AppOptions, + power_info: PowerData, +} + +#[derive(Debug, Default, Clone)] +struct PowerData { + charging: bool, + level: u64, +} + +impl PowerData { + pub fn new() -> Self { + PowerData { + charging: true, + level: 100, + } + } +} + +impl UnibarModulePower { + // -------------------- + pub fn new(o :common::AppOptions) -> Self { + UnibarModulePower { + opts: o, + power_info: PowerData::new(), + } + } +} + +impl bar_modules::BarModuleActions for UnibarModulePower { + + // -------------------- + fn generate_data(&mut self) { + // Following command is used to get power data: + // % upower -i /org/freedesktop/UPower/devices/DisplayDevice + // + // In UPower, the "display device" is a special object that represents the combined + // power status that should be shown in desktop environments. + // + // Purpose: + // + // It acts as a central point of information for the power icon displayed in your + // system's notification area or taskbar. + // Instead of desktop environments having to query all individual power sources, + // they can access this single "display device" to get a summary of the overall + // power status. + // + // Key characteristics: + // + // Composite device: It combines information from multiple power sources + // (like batteries, UPS) to provide a unified view. + // Object path: It has a dedicated object path, typically + // /org/freedesktop/UPower/devices/DisplayDevice. + // Properties: It exposes properties like Type, State, Percentage, TimeToEmpty, + // TimeToFull, and IconName to describe the device's status and + // icon representation. + // Dynamic Type: Unlike real devices, the Type of the display device can + // change (e.g., from Battery to UPS) depending on the current power situation. + // + // In essence, the "display device" simplifies power management for desktop environments + // by providing a convenient interface to show the relevant power information to the user. + // + let power_dump = Command::new("upower") + .arg("--dump") + .stdout(Stdio::piped()) + .output() + .unwrap(); + let power_str = String::from_utf8(power_dump.stdout).unwrap(); + let lines: Vec<&str> = power_str.split('\n').map(|s| s.trim()).collect(); + let mut found_device: bool = false; + + // let devices: HashMap>::new(); + // let cur_dev: HashMap::new(); + + for line in lines { + if line.is_empty() { continue }; + + let dev_re = Regex::new(r"^Device:\s+(.*)").unwrap(); + if let Some(caps) = dev_re.captures(line) { + if let Some((last, _els)) = &caps[1].split('/').collect::>().split_last() { + if self.opts.debug { + println!("-----> Power dump - Device - {:#?}", last); + } + if last.to_string() == "DisplayDevice" { + found_device = true; + } + } + } + + if found_device { + let level_re = Regex::new(r"^percentage:\s+(\d+)").unwrap(); + if let Some(caps) = level_re.captures(line) { + self.power_info.level = caps[1].parse().unwrap_or(0); + if self.opts.debug { + println!("-----> Power dump - level - {:#?}", self.power_info.level); + } + } + + let state_re = Regex::new(r"^state:\s+(\S+)").unwrap(); + if let Some(caps) = state_re.captures(line) { + self.power_info.charging = caps[1].to_string() != "discharging"; + if self.opts.debug { + println!("-----> Power dump - charging - {:#?}", self.power_info.charging); + } + } + } + // if self.opts.debug { + // println!("-----> Power dump {:#?}", line); + // } + } + } + + // -------------------- + fn get_content(&self) -> String { + // return format!("{}", parts.join(" ")); + return format!("{:.0}%", self.power_info.level); + } + + // -------------------- + fn get_icon(&self) -> String { + match self.power_info.charging { + true => return "🔌".to_string(), + false => return "🔋".to_string() + } + } +} + +impl bar_modules::BarModuleDebug for UnibarModulePower { + + // -------------------- + fn post_debug(&self) { + } +} diff --git a/src/bar_modules/mod.rs b/src/bar_modules/mod.rs index 96a0160..0917787 100644 --- a/src/bar_modules/mod.rs +++ b/src/bar_modules/mod.rs @@ -22,3 +22,4 @@ pub mod bar_module_music; pub mod bar_module_network; pub mod bar_module_memory; pub mod bar_module_cpu; +pub mod bar_module_power; diff --git a/src/main.rs b/src/main.rs index 40332d0..bb8dc67 100644 --- a/src/main.rs +++ b/src/main.rs @@ -57,6 +57,7 @@ impl Unibar { Box::new(bar_modules::bar_module_music::UnibarModuleMusic::new(self.opts.clone())), Box::new(bar_modules::bar_module_memory::UnibarModuleMemory::new(self.opts.clone())), Box::new(bar_modules::bar_module_cpu::UnibarModuleCpu::new(self.opts.clone())), + Box::new(bar_modules::bar_module_power::UnibarModulePower::new(self.opts.clone())), Box::new(bar_modules::bar_module_network::UnibarModuleNetwork::new(self.opts.clone())), Box::new(bar_modules::bar_module_weather::UnibarModuleWeather::new(self.opts.clone())), ]; @@ -88,6 +89,7 @@ impl Unibar { Box::new(bar_modules::bar_module_memory::UnibarModuleMemory::new(self.opts.clone())), Box::new(bar_modules::bar_module_network::UnibarModuleNetwork::new(self.opts.clone())), Box::new(bar_modules::bar_module_cpu::UnibarModuleCpu::new(self.opts.clone())), + Box::new(bar_modules::bar_module_power::UnibarModulePower::new(self.opts.clone())), ]; for md in bar_modules_debugged { md.post_debug();