roundabout,
created on Wednesday, 24 September 2025, 15:24:41 (1758727481),
received on Wednesday, 24 September 2025, 15:24:44 (1758727484)
Author identity: Vlad <vlad.muntoiu@gmail.com>
f5f68d033395fba81a73051c747141673c78cfad
applets/battery/__init__.py
@@ -18,10 +18,12 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
from __future__ import annotations
import threading
import panorama_panel
import locale
from pathlib import Path
from pydbus import SystemBus
from pydbus import SystemBus, SessionBus
import gi
gi.require_version("Gtk", "4.0")
@@ -65,6 +67,8 @@ class BatteryMonitor(panorama_panel.Applet):
config = {}
self.bus = SystemBus()
self.session_bus = SessionBus()
self.notification_service = None
self.upower = self.bus.get("org.freedesktop.UPower")
self.button = Gtk.MenuButton()
@@ -72,10 +76,20 @@ class BatteryMonitor(panorama_panel.Applet):
self.button.set_child(self.icon)
self.append(self.button)
self.low_threshold = config.get("low_threshold", 15/100)
self.low_notification = None
self.critical_threshold = config.get("critical_threshold", 5/100)
self.critical_notification = None
self.popover = Gtk.Popover()
self.popover_content = Gtk.ListBox()
self.update_batteries()
GLib.timeout_add_seconds(10, self.update_batteries)
def update_batteries(self):
self.popover_content.remove_all()
total_energy = max_energy = 0
any_charging = False
@@ -91,18 +105,78 @@ class BatteryMonitor(panorama_panel.Applet):
if device.State == 1:
any_charging = True
if not max_energy:
return False
print(total_energy, max_energy)
self.button.set_tooltip_text(f"{locale.format_string("%d", total_energy / max_energy * 1000)}‰")
self.icon.set_from_icon_name(get_battery_icon(total_energy / max_energy, any_charging))
GLib.idle_add(self.do_notifications, total_energy, max_energy)
return True
def do_notifications(self, total_energy, max_energy):
if self.session_bus.get("org.freedesktop.Notifications"):
self.notification_service = self.session_bus.get("org.freedesktop.Notifications")
else:
return
# This uses threading to make sure it doesn't hang while waiting to deliver the
# notification
if total_energy / max_energy < self.low_threshold:
if not self.low_notification:
def send_notification():
self.low_notification = self.notification_service.Notify(
_("Battery"),
self.low_notification or self.critical_notification or 0,
"battery-low",
_("The energy in the battery is low."),
"",
[],
{},
-1
)
threading.Thread(target=send_notification, daemon=True).start()
elif self.low_notification:
threading.Thread(
target=lambda: self.notification_service.CloseNotification(self.low_notification),
daemon=True
).start()
self.low_notification = None
if total_energy / max_energy < self.critical_threshold:
if not self.critical_notification:
def send_notification():
self.critical_notification = self.notification_service.Notify(
_("Battery"),
self.low_notification or self.critical_notification or 0,
"battery-empty",
_("The energy in the battery is very low; the device will shut down soon."),
"",
[],
{},
-1
)
threading.Thread(target=send_notification, daemon=True).start()
elif self.critical_notification:
threading.Thread(
target=lambda: self.notification_service.CloseNotification(self.critical_notification),
daemon=True
).start()
self.critical_notification = None
def get_config(self):
return {
"icon_size": self.icon.get_pixel_size(),
"low_threshold": self.low_threshold,
"critical_threshold": self.critical_threshold,
}
def shutdown(self, app):
self.publishing.unpublish()
return
config.yaml
@@ -60,6 +60,8 @@ panels:
right:
- BatteryMonitor:
icon_size: 24
low_threshold: 0.15
critical_threshold: 0.05
- Volume:
percentage_reveal: 1000
popdown_after_manual: 2000