roundabout,
created on Tuesday, 23 September 2025, 18:37:27 (1758652647),
received on Tuesday, 23 September 2025, 18:37:31 (1758652651)
Author identity: Vlad <vlad.muntoiu@gmail.com>
dc8ba7fb93f066c283d32469ebfbb181095627af
.idea/panorama-panel.iml
@@ -2,6 +2,8 @@
<module type="PYTHON_MODULE" version="4"> <component name="NewModuleRootManager"> <content url="file://$MODULE_DIR$"> <sourceFolder url="file://$MODULE_DIR$/shared" isTestSource="false" /> <sourceFolder url="file://$MODULE_DIR$/shared/panorama_panel.py" isTestSource="false" /><excludeFolder url="file://$MODULE_DIR$/.venv" /> </content> <orderEntry type="jdk" jdkName="Python 3.12 (panorama-panel)" jdkType="Python SDK" />
applets/battery/__init__.py
@@ -0,0 +1,108 @@
""" D-Bus laptop battery monitor applet for the Panorama panel. Copyright 2025, roundabout-host.com <vlad@roundabout-host.com> This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public Licence as published by the Free Software Foundation, either version 3 of the Licence, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public Licence for more details. You should have received a copy of the GNU General Public Licence along with this program. If not, see <https://www.gnu.org/licenses/>. """ from __future__ import annotations import panorama_panel import locale from pathlib import Path from pydbus import SystemBus import gi gi.require_version("Gtk", "4.0") from gi.repository import Gtk, Gdk, GLib module_directory = Path(__file__).resolve().parent locale.bindtextdomain("panorama-battery-monitor", module_directory / "locale") _ = lambda x: locale.dgettext("panorama-battery-monitor", x) def get_battery_icon(fraction, charging): if fraction < 1/32: base_icon = "battery-empty" elif fraction < 1/16: base_icon = "battery-caution" elif fraction < 1/4: base_icon = "battery-low" elif fraction < 1/2: base_icon = "battery-medium" elif fraction < 7/8: base_icon = "battery-good" else: base_icon = "battery-full" if charging: return base_icon + "-charging" return base_icon class BatteryMonitor(panorama_panel.Applet): name = _("Laptop battery") description = _("Check laptop battery charge") def __init__(self, orientation=Gtk.Orientation.HORIZONTAL, config=None): super().__init__(orientation=orientation, config=config) if config is None: config = {} self.bus = SystemBus() self.upower = self.bus.get("org.freedesktop.UPower") self.button = Gtk.MenuButton() self.icon = Gtk.Image(pixel_size=config.get("icon_size", 24)) self.button.set_child(self.icon) self.append(self.button) self.update_batteries() GLib.timeout_add_seconds(10, self.update_batteries) def update_batteries(self): total_energy = max_energy = 0 any_charging = False for device_path in self.upower.EnumerateDevices(): device = self.bus.get("org.freedesktop.UPower", device_path) # Filter only batteries if device.Type == 2: # This is in Wh but we don't care total_energy += device.Energy max_energy += device.EnergyFull if device.State == 1: any_charging = True 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)) return True def get_config(self): return { "icon_size": self.icon.get_pixel_size(), } def shutdown(self, app): self.publishing.unpublish()
config.yaml
@@ -48,10 +48,18 @@ panels:
trigger_name: app-menu icon_name: start-here-symbolic icon_size: 24 - DesktopFileButton: app_id: nemo.desktop - DesktopFileButton: app_id: org.gnome.Terminal.desktop - DesktopFileButton: app_id: chromium.desktopcentre: - NotifierApplet: icon_size: 24 right: - BatteryMonitor: icon_size: 24- Volume: percentage_reveal: 1000 popdown_after_manual: 2000