mirror of
https://github.com/xzeldon/razer-battery-report.git
synced 2025-04-07 19:57:08 +03:00
Compare commits
4 Commits
Author | SHA1 | Date | |
---|---|---|---|
49cc773919 | |||
ed501dc296 | |||
4cbeabf4b9 | |||
ee4a5fb375 |
773
Cargo.lock
generated
773
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "razer-battery-report"
|
name = "razer-battery-report"
|
||||||
version = "0.2.4"
|
version = "0.3.0"
|
||||||
authors = ["xzeldon <contact@zeldon.ru>"]
|
authors = ["xzeldon <contact@zeldon.ru>"]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
description = "Razer Battery Level Tray Indicator"
|
description = "Razer Battery Level Tray Indicator"
|
||||||
@ -38,3 +38,6 @@ winapi = { version = "0.3.9", features = ["winuser", "wincon", "consoleapi"] }
|
|||||||
|
|
||||||
# Efficient synchronization primitives (e.g. Mutex, RwLock and etc.)
|
# Efficient synchronization primitives (e.g. Mutex, RwLock and etc.)
|
||||||
parking_lot = "0.12"
|
parking_lot = "0.12"
|
||||||
|
|
||||||
|
# Desktop notifications
|
||||||
|
notify-rust = "4.11.4"
|
||||||
|
@ -43,7 +43,7 @@ To build, you must have [Rust](https://www.rust-lang.org/) and
|
|||||||
|
|
||||||
- [x] Tray Applet
|
- [x] Tray Applet
|
||||||
- [ ] Force update devices button in tray menu
|
- [ ] Force update devices button in tray menu
|
||||||
- [ ] Colored tray icons for different battery levels
|
- [x] Colored tray icons for different battery levels
|
||||||
- [x] Show log window button in tray menu
|
- [x] Show log window button in tray menu
|
||||||
- [x] Further reduce CPU usage by using Event Loop Proxy events (more info [here](https://github.com/tauri-apps/tray-icon/issues/83#issuecomment-1697773065))
|
- [x] Further reduce CPU usage by using Event Loop Proxy events (more info [here](https://github.com/tauri-apps/tray-icon/issues/83#issuecomment-1697773065))
|
||||||
- [x] Prebuilt Binary
|
- [x] Prebuilt Binary
|
||||||
|
@ -7,6 +7,7 @@ mod console;
|
|||||||
mod controller;
|
mod controller;
|
||||||
mod devices;
|
mod devices;
|
||||||
mod manager;
|
mod manager;
|
||||||
|
mod notify;
|
||||||
mod tray;
|
mod tray;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
53
src/notify.rs
Normal file
53
src/notify.rs
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
use notify_rust::Notification;
|
||||||
|
|
||||||
|
pub struct Notify {
|
||||||
|
app_name: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Notify {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
Self {
|
||||||
|
app_name: String::from("Razer Battery Report"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn battery_low(
|
||||||
|
&self,
|
||||||
|
device_name: &str,
|
||||||
|
battery_level: i32,
|
||||||
|
) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
Notification::new()
|
||||||
|
.summary(&self.app_name)
|
||||||
|
.body(&format!(
|
||||||
|
"{}: Battery low ({}%)",
|
||||||
|
device_name, battery_level
|
||||||
|
))
|
||||||
|
.show()?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn battery_full(&self, device_name: &str) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
Notification::new()
|
||||||
|
.summary(&self.app_name)
|
||||||
|
.body(&format!("{}: Battery fully charged", device_name))
|
||||||
|
.show()?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn device_connected(&self, device_name: &str) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
Notification::new()
|
||||||
|
.summary(&self.app_name)
|
||||||
|
.body(&format!("{}: Connected", device_name))
|
||||||
|
.show()?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn device_disconnecred(&self, device_name: &str) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
Notification::new()
|
||||||
|
.summary(&self.app_name)
|
||||||
|
.body(&format!("{}: Disconnected", device_name))
|
||||||
|
.show()?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
59
src/tray.rs
59
src/tray.rs
@ -5,7 +5,7 @@ use std::{
|
|||||||
time::Duration,
|
time::Duration,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{console::DebugConsole, manager::DeviceManager};
|
use crate::{console::DebugConsole, manager::DeviceManager, notify::Notify};
|
||||||
use log::{error, info, trace};
|
use log::{error, info, trace};
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use tao::event_loop::{EventLoopBuilder, EventLoopProxy};
|
use tao::event_loop::{EventLoopBuilder, EventLoopProxy};
|
||||||
@ -17,6 +17,9 @@ use tray_icon::{
|
|||||||
const BATTERY_UPDATE_INTERVAL: Duration = Duration::from_secs(300); // 5 min
|
const BATTERY_UPDATE_INTERVAL: Duration = Duration::from_secs(300); // 5 min
|
||||||
const DEVICE_FETCH_INTERVAL: Duration = Duration::from_secs(5);
|
const DEVICE_FETCH_INTERVAL: Duration = Duration::from_secs(5);
|
||||||
|
|
||||||
|
const BATTERY_CRITICAL_LEVEL: i32 = 5;
|
||||||
|
const BATTERY_LOW_LEVEL: i32 = 15;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct MemoryDevice {
|
pub struct MemoryDevice {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
@ -82,7 +85,7 @@ impl TrayInner {
|
|||||||
) {
|
) {
|
||||||
let tray_builder = TrayIconBuilder::new()
|
let tray_builder = TrayIconBuilder::new()
|
||||||
.with_menu(Box::new(tray_menu.clone()))
|
.with_menu(Box::new(tray_menu.clone()))
|
||||||
.with_tooltip("Service is running")
|
.with_tooltip("Search for devices")
|
||||||
.with_icon(icon)
|
.with_icon(icon)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
@ -97,6 +100,7 @@ pub struct TrayApp {
|
|||||||
device_manager: Arc<Mutex<DeviceManager>>,
|
device_manager: Arc<Mutex<DeviceManager>>,
|
||||||
devices: Arc<Mutex<HashMap<u32, MemoryDevice>>>,
|
devices: Arc<Mutex<HashMap<u32, MemoryDevice>>>,
|
||||||
tray_inner: TrayInner,
|
tray_inner: TrayInner,
|
||||||
|
notify: Arc<Notify>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@ -111,6 +115,7 @@ impl TrayApp {
|
|||||||
device_manager: Arc::new(Mutex::new(DeviceManager::new())),
|
device_manager: Arc::new(Mutex::new(DeviceManager::new())),
|
||||||
devices: Arc::new(Mutex::new(HashMap::new())),
|
devices: Arc::new(Mutex::new(HashMap::new())),
|
||||||
tray_inner: TrayInner::new(Arc::new(debug_console)),
|
tray_inner: TrayInner::new(Arc::new(debug_console)),
|
||||||
|
notify: Arc::new(Notify::new()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -141,6 +146,7 @@ impl TrayApp {
|
|||||||
fn spawn_device_fetch_thread(&self, proxy: EventLoopProxy<TrayEvent>) {
|
fn spawn_device_fetch_thread(&self, proxy: EventLoopProxy<TrayEvent>) {
|
||||||
let devices = Arc::clone(&self.devices);
|
let devices = Arc::clone(&self.devices);
|
||||||
let device_manager = Arc::clone(&self.device_manager);
|
let device_manager = Arc::clone(&self.device_manager);
|
||||||
|
let notify = Arc::clone(&self.notify);
|
||||||
|
|
||||||
thread::spawn(move || {
|
thread::spawn(move || {
|
||||||
let mut last_devices = HashSet::new();
|
let mut last_devices = HashSet::new();
|
||||||
@ -154,6 +160,7 @@ impl TrayApp {
|
|||||||
for id in removed_devices {
|
for id in removed_devices {
|
||||||
if let Some(device) = devices.remove(&id) {
|
if let Some(device) = devices.remove(&id) {
|
||||||
info!("Device removed: {}", device.name);
|
info!("Device removed: {}", device.name);
|
||||||
|
let _ = notify.device_disconnecred(&device.name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -162,6 +169,7 @@ impl TrayApp {
|
|||||||
if let Some(name) = device_manager.lock().get_device_name(id) {
|
if let Some(name) = device_manager.lock().get_device_name(id) {
|
||||||
devices.insert(id, MemoryDevice::new(name.clone(), id));
|
devices.insert(id, MemoryDevice::new(name.clone(), id));
|
||||||
info!("New device: {}", name);
|
info!("New device: {}", name);
|
||||||
|
let _ = notify.device_connected(&name);
|
||||||
} else {
|
} else {
|
||||||
error!("Failed to get device name for id: {}", id);
|
error!("Failed to get device name for id: {}", id);
|
||||||
}
|
}
|
||||||
@ -201,6 +209,7 @@ impl TrayApp {
|
|||||||
let tray_icon = Arc::clone(&self.tray_inner.tray_icon);
|
let tray_icon = Arc::clone(&self.tray_inner.tray_icon);
|
||||||
let debug_console = Arc::clone(&self.tray_inner.debug_console);
|
let debug_console = Arc::clone(&self.tray_inner.debug_console);
|
||||||
let menu_items = Arc::clone(&self.tray_inner.menu_items);
|
let menu_items = Arc::clone(&self.tray_inner.menu_items);
|
||||||
|
let notify = Arc::clone(&self.notify);
|
||||||
|
|
||||||
let menu_channel = MenuEvent::receiver();
|
let menu_channel = MenuEvent::receiver();
|
||||||
|
|
||||||
@ -212,7 +221,7 @@ impl TrayApp {
|
|||||||
TrayInner::build_tray(&tray_icon, &tray_menu, icon.clone());
|
TrayInner::build_tray(&tray_icon, &tray_menu, icon.clone());
|
||||||
}
|
}
|
||||||
tao::event::Event::UserEvent(TrayEvent::DeviceUpdate(device_ids)) => {
|
tao::event::Event::UserEvent(TrayEvent::DeviceUpdate(device_ids)) => {
|
||||||
Self::update(&devices, &device_manager, &device_ids, &tray_icon);
|
Self::update(&devices, &device_manager, &device_ids, &tray_icon, ¬ify);
|
||||||
}
|
}
|
||||||
tao::event::Event::UserEvent(TrayEvent::MenuEvent(event)) => {
|
tao::event::Event::UserEvent(TrayEvent::MenuEvent(event)) => {
|
||||||
let menu_items = menu_items.lock();
|
let menu_items = menu_items.lock();
|
||||||
@ -241,11 +250,33 @@ impl TrayApp {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_battery_icon(battery_level: i32, is_charging: bool) -> tray_icon::Icon {
|
||||||
|
let icon = match (battery_level, is_charging) {
|
||||||
|
(lvl, _) if lvl <= BATTERY_CRITICAL_LEVEL && !is_charging => {
|
||||||
|
include_bytes!("../assets/mouse_red.png").to_vec()
|
||||||
|
}
|
||||||
|
(lvl, _) if lvl <= BATTERY_LOW_LEVEL && !is_charging => {
|
||||||
|
include_bytes!("../assets/mouse_yellow.png").to_vec()
|
||||||
|
}
|
||||||
|
|
||||||
|
_ => include_bytes!("../assets/mouse_white.png").to_vec(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let image = image::load_from_memory(&icon)
|
||||||
|
.expect("Failed to open icon")
|
||||||
|
.into_rgba8();
|
||||||
|
let (width, height) = image.dimensions();
|
||||||
|
let rgba = image.into_raw();
|
||||||
|
|
||||||
|
tray_icon::Icon::from_rgba(rgba, width, height).expect("Failed to create icon")
|
||||||
|
}
|
||||||
|
|
||||||
fn update(
|
fn update(
|
||||||
devices: &Arc<Mutex<HashMap<u32, MemoryDevice>>>,
|
devices: &Arc<Mutex<HashMap<u32, MemoryDevice>>>,
|
||||||
manager: &Arc<Mutex<DeviceManager>>,
|
manager: &Arc<Mutex<DeviceManager>>,
|
||||||
device_ids: &[u32],
|
device_ids: &[u32],
|
||||||
tray_icon: &Arc<Mutex<Option<TrayIcon>>>,
|
tray_icon: &Arc<Mutex<Option<TrayIcon>>>,
|
||||||
|
notify: &Arc<Notify>,
|
||||||
) {
|
) {
|
||||||
let mut devices = devices.lock();
|
let mut devices = devices.lock();
|
||||||
let manager = manager.lock();
|
let manager = manager.lock();
|
||||||
@ -263,7 +294,18 @@ impl TrayApp {
|
|||||||
device.battery_level = battery_level;
|
device.battery_level = battery_level;
|
||||||
device.is_charging = is_charging;
|
device.is_charging = is_charging;
|
||||||
|
|
||||||
Self::check_notify(device);
|
Self::check_notify(device, notify);
|
||||||
|
|
||||||
|
if device.old_battery_level != battery_level
|
||||||
|
|| device.is_charging != is_charging
|
||||||
|
{
|
||||||
|
let new_icon = Self::get_battery_icon(battery_level, is_charging);
|
||||||
|
if let Some(tray_icon) = tray_icon.lock().as_mut() {
|
||||||
|
tray_icon
|
||||||
|
.set_icon(Some(new_icon))
|
||||||
|
.expect("Failed to update tray icon");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if let Some(tray_icon) = tray_icon.lock().as_mut() {
|
if let Some(tray_icon) = tray_icon.lock().as_mut() {
|
||||||
let _ = tray_icon
|
let _ = tray_icon
|
||||||
@ -274,16 +316,18 @@ impl TrayApp {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_notify(device: &MemoryDevice) {
|
fn check_notify(device: &MemoryDevice, notify: &Notify) {
|
||||||
if device.battery_level == -1 {
|
if device.battery_level == -1 {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if !device.is_charging
|
if !device.is_charging
|
||||||
&& (device.battery_level <= 5
|
&& (device.battery_level <= BATTERY_CRITICAL_LEVEL
|
||||||
|| (device.old_battery_level > 15 && device.battery_level <= 15))
|
|| (device.old_battery_level > BATTERY_LOW_LEVEL
|
||||||
|
&& device.battery_level <= BATTERY_LOW_LEVEL))
|
||||||
{
|
{
|
||||||
info!("{}: Battery low ({}%)", device.name, device.battery_level);
|
info!("{}: Battery low ({}%)", device.name, device.battery_level);
|
||||||
|
let _ = notify.battery_low(&device.name, device.battery_level);
|
||||||
} else if device.old_battery_level <= 99
|
} else if device.old_battery_level <= 99
|
||||||
&& device.battery_level == 100
|
&& device.battery_level == 100
|
||||||
&& device.is_charging
|
&& device.is_charging
|
||||||
@ -292,6 +336,7 @@ impl TrayApp {
|
|||||||
"{}: Battery fully charged ({}%)",
|
"{}: Battery fully charged ({}%)",
|
||||||
device.name, device.battery_level
|
device.name, device.battery_level
|
||||||
);
|
);
|
||||||
|
let _ = notify.battery_full(&device.name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user