feat(power): add battery status
This commit is contained in:
parent
69e03bc414
commit
fe52744387
5 changed files with 182 additions and 5 deletions
|
|
@ -51,6 +51,7 @@ tokio.workspace = true
|
|||
tracing = "0.1.40"
|
||||
tracing-subscriber = "0.3.18"
|
||||
udev = "0.9.0"
|
||||
upower_dbus = { git = "https://github.com/pop-os/dbus-settings-bindings" }
|
||||
url = "2.5.2"
|
||||
xkb-data = "0.2.1"
|
||||
zbus = { version = "4.4.0", features = ["tokio"] }
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
use chrono::Duration;
|
||||
use futures::FutureExt;
|
||||
use zbus::Connection;
|
||||
|
||||
mod ppdaemon;
|
||||
|
|
@ -226,3 +228,123 @@ async fn get_power_profiles_proxy<'a>() -> Result<ppdaemon::PowerProfilesProxy<'
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Clone)]
|
||||
pub struct Battery {
|
||||
pub icon_name: String,
|
||||
pub is_present: bool,
|
||||
pub percent: f64,
|
||||
pub on_battery: bool,
|
||||
pub remaining_duration: Duration,
|
||||
pub remaining_time: String,
|
||||
}
|
||||
|
||||
async fn get_device_proxy<'a>() -> Result<upower_dbus::DeviceProxy<'a>, zbus::Error> {
|
||||
let connection = match Connection::system().await {
|
||||
Ok(c) => c,
|
||||
Err(e) => {
|
||||
tracing::error!("zbus connection failed. {e}");
|
||||
return Err(e);
|
||||
}
|
||||
};
|
||||
|
||||
match upower_dbus::UPowerProxy::new(&connection).await {
|
||||
Ok(p) => p.get_display_device().await,
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
}
|
||||
|
||||
async fn get_on_battery_status() -> Result<bool, zbus::Error> {
|
||||
let connection = match Connection::system().await {
|
||||
Ok(c) => c,
|
||||
Err(e) => {
|
||||
tracing::error!("zbus connection failed. {e}");
|
||||
return Err(e);
|
||||
}
|
||||
};
|
||||
|
||||
match upower_dbus::UPowerProxy::new(&connection).await {
|
||||
Ok(p) => p.on_battery().await,
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
}
|
||||
|
||||
impl Battery {
|
||||
pub async fn update_battery() -> Self {
|
||||
let proxy = get_device_proxy().await;
|
||||
|
||||
if let Ok(proxy) = proxy {
|
||||
let mut remaining_duration: Duration = Duration::default();
|
||||
|
||||
let (is_present, percentage, on_battery) = futures::join!(
|
||||
proxy.is_present().map(Result::unwrap_or_default),
|
||||
proxy.percentage().map(Result::unwrap_or_default),
|
||||
get_on_battery_status().map(Result::unwrap_or_default)
|
||||
);
|
||||
|
||||
let percent = percentage.clamp(0.0, 100.0);
|
||||
|
||||
if on_battery {
|
||||
if let Ok(time) = proxy.time_to_empty().await {
|
||||
if let Ok(dur) = Duration::from_std(std::time::Duration::from_secs(time as u64))
|
||||
{
|
||||
remaining_duration = dur;
|
||||
}
|
||||
}
|
||||
} else if let Ok(time) = proxy.time_to_full().await {
|
||||
if let Ok(dur) = Duration::from_std(std::time::Duration::from_secs(time as u64)) {
|
||||
remaining_duration = dur;
|
||||
}
|
||||
}
|
||||
|
||||
let battery_percent = if percent > 95.0 {
|
||||
100
|
||||
} else if percent > 80.0 {
|
||||
90
|
||||
} else if percent > 65.0 {
|
||||
80
|
||||
} else if percent > 35.0 {
|
||||
50
|
||||
} else if percent > 20.0 {
|
||||
35
|
||||
} else if percent > 14.0 {
|
||||
20
|
||||
} else if percent > 9.0 {
|
||||
10
|
||||
} else if percent > 5.0 {
|
||||
5
|
||||
} else {
|
||||
0
|
||||
};
|
||||
let charging = if on_battery { "" } else { "charging-" };
|
||||
|
||||
let icon_name =
|
||||
format!("cosmic-applet-battery-level-{battery_percent}-{charging}symbolic",);
|
||||
|
||||
let remaining_time = |duration: Duration| {
|
||||
let total_seconds = duration.num_seconds();
|
||||
|
||||
let hours = total_seconds / 3600;
|
||||
let minutes = (total_seconds % 3600) / 60;
|
||||
let seconds = total_seconds % 60;
|
||||
|
||||
fl!(
|
||||
"battery",
|
||||
"remaining-time",
|
||||
time = format!("{:02}:{:02}:{:02}", hours, minutes, seconds)
|
||||
)
|
||||
};
|
||||
|
||||
return Battery {
|
||||
icon_name,
|
||||
is_present,
|
||||
percent,
|
||||
on_battery,
|
||||
remaining_duration,
|
||||
remaining_time: remaining_time(remaining_duration),
|
||||
};
|
||||
}
|
||||
|
||||
Battery::default()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,15 +1,20 @@
|
|||
mod backend;
|
||||
use self::backend::{GetCurrentPowerProfile, SetPowerProfile};
|
||||
use backend::PowerProfile;
|
||||
|
||||
use cosmic::widget;
|
||||
use self::backend::{GetCurrentPowerProfile, SetPowerProfile};
|
||||
use backend::{Battery, PowerProfile};
|
||||
|
||||
use chrono::TimeDelta;
|
||||
use cosmic::iced_widget::row;
|
||||
use cosmic::widget::{self, column, text};
|
||||
use cosmic::{widget::settings, Apply};
|
||||
use cosmic_settings_page::{self as page, section, Section};
|
||||
use slab::Slab;
|
||||
use slotmap::SlotMap;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Page;
|
||||
pub struct Page {
|
||||
battery: Battery,
|
||||
}
|
||||
|
||||
impl page::Page<crate::pages::Message> for Page {
|
||||
fn info(&self) -> page::Info {
|
||||
|
|
@ -22,13 +27,29 @@ impl page::Page<crate::pages::Message> for Page {
|
|||
&self,
|
||||
sections: &mut SlotMap<section::Entity, Section<crate::pages::Message>>,
|
||||
) -> Option<page::Content> {
|
||||
Some(vec![sections.insert(profiles())])
|
||||
Some(vec![
|
||||
sections.insert(battery_info()),
|
||||
sections.insert(profiles()),
|
||||
])
|
||||
}
|
||||
|
||||
fn on_enter(
|
||||
&mut self,
|
||||
_page: cosmic_settings_page::Entity,
|
||||
_sender: tokio::sync::mpsc::Sender<crate::pages::Message>,
|
||||
) -> cosmic::Command<crate::pages::Message> {
|
||||
cosmic::command::future(async move {
|
||||
let battery = Battery::update_battery().await;
|
||||
Message::UpdateBattery(battery)
|
||||
})
|
||||
.map(crate::pages::Message::Power)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum Message {
|
||||
PowerProfileChange(PowerProfile),
|
||||
UpdateBattery(Battery),
|
||||
}
|
||||
|
||||
impl Page {
|
||||
|
|
@ -43,10 +64,37 @@ impl Page {
|
|||
runtime.block_on(b.set_power_profile(p));
|
||||
}
|
||||
}
|
||||
Message::UpdateBattery(battery) => self.battery = battery,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
fn battery_info() -> Section<crate::pages::Message> {
|
||||
let descriptions = Slab::new();
|
||||
|
||||
Section::default()
|
||||
.title(fl!("battery"))
|
||||
.descriptions(descriptions)
|
||||
.show_while::<Page>(|page| page.battery.is_present)
|
||||
.view::<Page>(move |_binder, page, section| {
|
||||
let battery_icon = widget::icon::from_name(page.battery.icon_name.clone());
|
||||
let battery_percent = widget::text(format!("{}%", page.battery.percent));
|
||||
|
||||
let battery_time =
|
||||
widget::text(if page.battery.remaining_duration > TimeDelta::zero() {
|
||||
&page.battery.remaining_time
|
||||
} else {
|
||||
""
|
||||
});
|
||||
|
||||
column::with_capacity(2)
|
||||
.spacing(8)
|
||||
.push(text::heading(§ion.title))
|
||||
.push(row!(battery_icon, battery_percent, battery_time).spacing(8))
|
||||
.into()
|
||||
})
|
||||
}
|
||||
|
||||
fn profiles() -> Section<crate::pages::Message> {
|
||||
let mut descriptions = Slab::new();
|
||||
|
||||
|
|
|
|||
|
|
@ -293,6 +293,9 @@ profile = Profile
|
|||
power = Power & Battery
|
||||
.desc = Manage power settings
|
||||
|
||||
battery = Battery
|
||||
.remaining-time = ({ $time } left)
|
||||
|
||||
power-mode = Power Mode
|
||||
.battery = Extended battery life
|
||||
.battery-desc = Reduced power usage and silent performance.
|
||||
|
|
|
|||
|
|
@ -307,6 +307,9 @@ profile = Profil
|
|||
power = Zasilanie
|
||||
.desc = Zarządzaj ustawieniami zasilania
|
||||
|
||||
battery = Bateria
|
||||
.remaining-time = ({ $time } pozostało)
|
||||
|
||||
power-mode = Profile Zasilania
|
||||
.performance = Tryb Wysokowydajny
|
||||
.balanced = Tryb Zbalansowany
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue