Add battery level to web UI

This commit is contained in:
Simon Fondrie-Teitler
2025-08-25 17:19:51 -04:00
committed by Cooper Quintin
parent f49d11f034
commit 663d0abb57
9 changed files with 273 additions and 2 deletions

63
daemon/src/battery/mod.rs Normal file
View File

@@ -0,0 +1,63 @@
use std::path::Path;
use rayhunter::Device;
use serde::Serialize;
use crate::error::RayhunterError;
pub mod orbic;
pub mod tmobile;
pub mod wingtech;
#[derive(Clone, Copy, PartialEq, Debug, Serialize)]
pub enum BatteryLevel {
VeryLow,
Low,
Medium,
High,
Full,
}
#[derive(Clone, Copy, PartialEq, Debug, Serialize)]
pub struct BatteryState {
level: BatteryLevel,
is_plugged_in: bool,
}
async fn is_plugged_in_from_file(path: &Path) -> Result<bool, RayhunterError> {
match tokio::fs::read_to_string(path)
.await
.map_err(RayhunterError::TokioError)?
.chars()
.next()
{
Some('0') => Ok(false),
Some('1') => Ok(true),
_ => Err(RayhunterError::BatteryPluggedInStatusParseError),
}
}
async fn get_level_from_percentage_file(path: &Path) -> Result<BatteryLevel, RayhunterError> {
match tokio::fs::read_to_string(path)
.await
.map_err(RayhunterError::TokioError)?
.trim_end()
.parse()
{
Ok(0..=10) => Ok(BatteryLevel::VeryLow),
Ok(11..=25) => Ok(BatteryLevel::Low),
Ok(26..=50) => Ok(BatteryLevel::Medium),
Ok(51..=75) => Ok(BatteryLevel::High),
Ok(76..=100) => Ok(BatteryLevel::Full),
_ => Err(RayhunterError::BatteryLevelParseError),
}
}
pub async fn get_battery_status(device: &Device) -> Result<BatteryState, RayhunterError> {
Ok(match device {
Device::Orbic => orbic::get_battery_state().await?,
Device::Wingtech => wingtech::get_battery_state().await?,
Device::Tmobile => tmobile::get_battery_state().await?,
_ => return Err(RayhunterError::FunctionNotSupportedForDeviceError),
})
}

View File

@@ -0,0 +1,28 @@
use std::path::Path;
use crate::{
battery::{BatteryLevel, BatteryState, is_plugged_in_from_file},
error::RayhunterError,
};
const BATTERY_LEVEL_FILE: &str = "/sys/kernel/chg_info/level";
const PLUGGED_IN_STATE_FILE: &str = "/sys/kernel/chg_info/chg_en";
pub async fn get_battery_state() -> Result<BatteryState, RayhunterError> {
Ok(BatteryState {
level: match tokio::fs::read_to_string(&BATTERY_LEVEL_FILE)
.await
.map_err(RayhunterError::TokioError)?
.chars()
.next()
{
Some('1') => Ok(BatteryLevel::VeryLow),
Some('2') => Ok(BatteryLevel::Low),
Some('3') => Ok(BatteryLevel::Medium),
Some('4') => Ok(BatteryLevel::High),
Some('5') => Ok(BatteryLevel::Full),
_ => Err(RayhunterError::BatteryLevelParseError),
}?,
is_plugged_in: is_plugged_in_from_file(Path::new(PLUGGED_IN_STATE_FILE)).await?,
})
}

View File

@@ -0,0 +1,16 @@
use std::path::Path;
use crate::{
battery::{BatteryState, get_level_from_percentage_file, is_plugged_in_from_file},
error::RayhunterError,
};
const BATTERY_LEVEL_FILE: &str = "/sys/class/power_supply/bms/capacity";
const PLUGGED_IN_STATE_FILE: &str = "/sys/devices/78d9000.usb/power_supply/usb/online";
pub async fn get_battery_state() -> Result<BatteryState, RayhunterError> {
Ok(BatteryState {
level: get_level_from_percentage_file(Path::new(BATTERY_LEVEL_FILE)).await?,
is_plugged_in: is_plugged_in_from_file(Path::new(PLUGGED_IN_STATE_FILE)).await?,
})
}

View File

@@ -0,0 +1,17 @@
use std::path::Path;
use crate::{
battery::{BatteryState, get_level_from_percentage_file, is_plugged_in_from_file},
error::RayhunterError,
};
const BATTERY_LEVEL_FILE: &str =
"/sys/devices/78b7000.i2c/i2c-3/3-0063/power_supply/cw2017-bat/capacity";
const PLUGGED_IN_STATE_FILE: &str = "/sys/devices/8a00000.ssusb/power_supply/usb/online";
pub async fn get_battery_state() -> Result<BatteryState, RayhunterError> {
Ok(BatteryState {
level: get_level_from_percentage_file(Path::new(BATTERY_LEVEL_FILE)).await?,
is_plugged_in: is_plugged_in_from_file(Path::new(PLUGGED_IN_STATE_FILE)).await?,
})
}