|
1 |
| -use std::path::Path; |
| 1 | +use std::{path::Path, time::Duration}; |
2 | 2 |
|
| 3 | +use log::{error, info}; |
3 | 4 | use rayhunter::Device;
|
4 | 5 | use serde::Serialize;
|
| 6 | +use tokio::select; |
| 7 | +use tokio_util::{sync::CancellationToken, task::TaskTracker}; |
5 | 8 |
|
6 |
| -use crate::error::RayhunterError; |
| 9 | +use crate::{ |
| 10 | + error::RayhunterError, |
| 11 | + notifications::{Notification, NotificationType}, |
| 12 | +}; |
7 | 13 |
|
8 | 14 | pub mod orbic;
|
9 | 15 | pub mod tmobile;
|
10 | 16 | pub mod wingtech;
|
11 | 17 |
|
| 18 | +const LOW_BATTERY_LEVEL: u8 = 10; |
| 19 | + |
12 | 20 | #[derive(Clone, Copy, PartialEq, Debug, Serialize)]
|
13 | 21 | pub struct BatteryState {
|
14 | 22 | level: u8,
|
@@ -45,3 +53,59 @@ pub async fn get_battery_status(device: &Device) -> Result<BatteryState, Rayhunt
|
45 | 53 | _ => return Err(RayhunterError::FunctionNotSupportedForDeviceError),
|
46 | 54 | })
|
47 | 55 | }
|
| 56 | + |
| 57 | +pub fn run_battery_notification_worker( |
| 58 | + task_tracker: &TaskTracker, |
| 59 | + device: Device, |
| 60 | + notification_channel: tokio::sync::mpsc::Sender<Notification>, |
| 61 | + shutdown_token: CancellationToken, |
| 62 | +) { |
| 63 | + task_tracker.spawn(async move { |
| 64 | + // Don't send a notification initially if the device starts at a low battery level. |
| 65 | + let mut triggered = match get_battery_status(&device).await { |
| 66 | + Err(RayhunterError::FunctionNotSupportedForDeviceError) => { |
| 67 | + info!("Battery level function not supported for device"); |
| 68 | + false |
| 69 | + } |
| 70 | + Err(e) => { |
| 71 | + error!("Failed to get battery status: {e}"); |
| 72 | + true |
| 73 | + } |
| 74 | + Ok(status) => status.level < LOW_BATTERY_LEVEL, |
| 75 | + }; |
| 76 | + |
| 77 | + loop { |
| 78 | + select! { |
| 79 | + _ = shutdown_token.cancelled() => break, |
| 80 | + _ = tokio::time::sleep(Duration::from_secs(15)) => {} |
| 81 | + } |
| 82 | + |
| 83 | + let status = match get_battery_status(&device).await { |
| 84 | + Err(e) => { |
| 85 | + error!("Failed to get battery status: {e}"); |
| 86 | + continue; |
| 87 | + } |
| 88 | + Ok(status) => status, |
| 89 | + }; |
| 90 | + |
| 91 | + // To avoid flapping, if the notification has already been triggered |
| 92 | + // wait until the device has been plugged in and the battery level |
| 93 | + // is high enough to re-enable notifications. |
| 94 | + if triggered && status.is_plugged_in && status.level > LOW_BATTERY_LEVEL { |
| 95 | + triggered = false; |
| 96 | + continue; |
| 97 | + } |
| 98 | + if !triggered && !status.is_plugged_in && status.level <= LOW_BATTERY_LEVEL { |
| 99 | + notification_channel |
| 100 | + .send(Notification::new( |
| 101 | + NotificationType::LowBattery, |
| 102 | + "Rayhunter's battery is low".to_string(), |
| 103 | + None, |
| 104 | + )) |
| 105 | + .await |
| 106 | + .expect("Failed to send to notification channel"); |
| 107 | + triggered = true; |
| 108 | + } |
| 109 | + } |
| 110 | + }); |
| 111 | +} |
0 commit comments