battery notify script
the script, originally written in bash, is rewrited into python, whose UPower implements (from `wogscpar/upower-python`) make me lazy to check out again.
This commit is contained in:
197
bin/.battery-warn
Executable file
197
bin/.battery-warn
Executable file
@@ -0,0 +1,197 @@
|
||||
#!/usr/bin/env python
|
||||
# SPDX-License-Identifier: CC0-1.0
|
||||
# Copyright © 2021 mpan; <https://mpan.pl/>; CC0 1.0 (THIS SCRIPT!)
|
||||
# Context: <https://bbs.archlinux.org/viewtopic.php?id=269453>
|
||||
# Reference: https://github.com/wogscpar/upower-python
|
||||
|
||||
# -*- encoding: utf-8 -*-
|
||||
# @File : .battery-warn
|
||||
# @Time : 2025/12/29 00:32:53
|
||||
# @Author : SilverAg.L
|
||||
|
||||
# bash pipeline was still too complicated.
|
||||
# Dependencies (pacman): python-dbus, libnotify, pipewire-audio
|
||||
|
||||
import dbus
|
||||
|
||||
from os import getenv
|
||||
from subprocess import run as start_process
|
||||
|
||||
# region config
|
||||
AUD_OUT_EMB = ( # my laptop embedded speaker
|
||||
"alsa_output.pci-0000_00_1f"
|
||||
".3-platform-skl_hda_dsp_generic.HiFi__Speaker__sink"
|
||||
)
|
||||
# better searched by editor like VSCode.
|
||||
AUD_FILE = "~/.local/share/.low_power.wav".replace("~", getenv("HOME"), 1)
|
||||
# endregion config
|
||||
|
||||
|
||||
class UPowerManager():
|
||||
def __init__(self):
|
||||
self.UPOWER_NAME = "org.freedesktop.UPower"
|
||||
self.UPOWER_PATH = "/org/freedesktop/UPower"
|
||||
|
||||
self.DBUS_PROPERTIES = "org.freedesktop.DBus.Properties"
|
||||
self.bus = dbus.SystemBus()
|
||||
|
||||
def __upower(self, subpath=None, *, interface=None):
|
||||
if not subpath:
|
||||
subpath = ""
|
||||
upower_proxy = self.bus.get_object(
|
||||
self.UPOWER_NAME,
|
||||
self.UPOWER_PATH + subpath
|
||||
)
|
||||
if interface is None:
|
||||
interface = self.UPOWER_NAME
|
||||
return dbus.Interface(upower_proxy, interface)
|
||||
|
||||
def __battery(self, battery):
|
||||
battery_proxy = self.bus.get_object(self.UPOWER_NAME, battery)
|
||||
return dbus.Interface(battery_proxy, self.DBUS_PROPERTIES)
|
||||
|
||||
def detect_devices(self):
|
||||
return self.__upower().EnumerateDevices()
|
||||
|
||||
def get_display_device(self):
|
||||
return self.__upower().GetDisplayDevice()
|
||||
|
||||
def get_critical_action(self):
|
||||
return self.__upower().GetCriticalAction()
|
||||
|
||||
def get_device_percentage(self, battery):
|
||||
return self.__battery(battery).Get(
|
||||
self.UPOWER_NAME + ".Device", "Percentage")
|
||||
|
||||
def get_full_device_information(self, battery):
|
||||
def get_property(prop_name):
|
||||
return self.__battery(battery).Get(
|
||||
self.UPOWER_NAME + ".Device", prop_name)
|
||||
|
||||
return {
|
||||
'HasHistory': get_property("HasHistory"),
|
||||
'HasStatistics': get_property("HasStatistics"),
|
||||
'IsPresent': get_property("IsPresent"),
|
||||
'IsRechargeable': get_property("IsRechargeable"),
|
||||
'Online': get_property("Online"),
|
||||
'PowerSupply': get_property("PowerSupply"),
|
||||
'Capacity': get_property("Capacity"),
|
||||
'Energy': get_property("Energy"),
|
||||
'EnergyEmpty': get_property("EnergyEmpty"),
|
||||
'EnergyFull': get_property("EnergyFull"),
|
||||
'EnergyFullDesign': get_property("EnergyFullDesign"),
|
||||
'EnergyRate': get_property("EnergyRate"),
|
||||
'Luminosity': get_property("Luminosity"),
|
||||
'Percentage': get_property("Percentage"),
|
||||
'Temperature': get_property("Temperature"),
|
||||
'Voltage': get_property("Voltage"),
|
||||
'TimeToEmpty': get_property("TimeToEmpty"),
|
||||
'TimeToFull': get_property("TimeToFull"),
|
||||
'IconName': get_property("IconName"),
|
||||
'Model': get_property("Model"),
|
||||
'NativePath': get_property("NativePath"),
|
||||
'Serial': get_property("Serial"),
|
||||
'Vendor': get_property("Vendor"),
|
||||
'State': get_property("State"),
|
||||
'Technology': get_property("Technology"),
|
||||
'Type': get_property("Type"),
|
||||
'WarningLevel': get_property("WarningLevel"),
|
||||
'UpdateTime': get_property("UpdateTime")
|
||||
}
|
||||
|
||||
def is_lid_present(self):
|
||||
return bool(self.__upower(interface=self.DBUS_PROPERTIES).Get(
|
||||
self.UPOWER_NAME, 'LidIsPresent'))
|
||||
|
||||
def is_lid_closed(self):
|
||||
return bool(self.__upower(interface=self.DBUS_PROPERTIES).Get(
|
||||
self.UPOWER_NAME, 'LidIsClosed'))
|
||||
|
||||
def on_battery(self):
|
||||
return bool(self.__upower(interface=self.DBUS_PROPERTIES).Get(
|
||||
self.UPOWER_NAME, 'OnBattery'))
|
||||
|
||||
def has_wakeup_capabilities(self):
|
||||
return bool(self.__upower(
|
||||
"/Wakeups",
|
||||
interface=self.DBUS_PROPERTIES
|
||||
).Get(self.UPOWER_NAME + '.Wakeups', 'HasCapability'))
|
||||
|
||||
def get_wakeups_data(self):
|
||||
return self.__upower(
|
||||
"/Wakeups",
|
||||
interface=self.UPOWER_NAME + '.Wakeups'
|
||||
).GetData()
|
||||
|
||||
def get_wakeups_total(self):
|
||||
return self.__upower(
|
||||
"/Wakeups",
|
||||
interface=self.UPOWER_NAME + '.Wakeups'
|
||||
).GetTotal()
|
||||
|
||||
def is_loading(self, battery):
|
||||
state = int(self.__battery(battery).Get(
|
||||
self.UPOWER_NAME + ".Device", "State"))
|
||||
|
||||
return state == 1
|
||||
|
||||
def get_state(self, battery):
|
||||
state = int(self.__battery(battery).Get(
|
||||
self.UPOWER_NAME + ".Device", "State"))
|
||||
|
||||
return {
|
||||
0: "Unknown",
|
||||
1: "Loading",
|
||||
2: "Discharging",
|
||||
3: "Empty",
|
||||
4: "Fully charged",
|
||||
5: "Pending charge",
|
||||
6: "Pending discharge"
|
||||
}.get(state, "Unknown")
|
||||
|
||||
|
||||
def push_notification(title, message, timeout=10000):
|
||||
BUS_NAME = "org.freedesktop.Notifications"
|
||||
OBJECT_PATH = "/org/freedesktop/Notifications"
|
||||
INTERFACE = BUS_NAME
|
||||
|
||||
notify = dbus.Interface(
|
||||
dbus.SessionBus().get_object(BUS_NAME, OBJECT_PATH),
|
||||
INTERFACE
|
||||
)
|
||||
notify.Notify(
|
||||
"battery-warn-script", # app_name
|
||||
0, # replaces_id
|
||||
"", # app_icon
|
||||
title, # summary
|
||||
message, # body
|
||||
[], # actions
|
||||
{}, # hints
|
||||
timeout # expire_timeout
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
upowr = UPowerManager()
|
||||
if not upowr.on_battery():
|
||||
exit(0)
|
||||
|
||||
devPaths = upowr.detect_devices()
|
||||
low_power_detected = False
|
||||
for devPath in devPaths:
|
||||
info = upowr.get_full_device_information(devPath)
|
||||
if info['Type'] != 2: # 2 means battery
|
||||
continue
|
||||
# seems waybar visual is 1% lower than actual.
|
||||
if info['Percentage'] < 21:
|
||||
push_notification(
|
||||
"Power Hint",
|
||||
f"{info['NativePath']} is running out. Recharge soon!"
|
||||
)
|
||||
low_power_detected = True
|
||||
if low_power_detected:
|
||||
start_process([
|
||||
"pw-play",
|
||||
f"--target={AUD_OUT_EMB}",
|
||||
AUD_FILE
|
||||
])
|
||||
@@ -43,3 +43,6 @@
|
||||
|
||||
- `hoyocloud-chromium-userscript.js`顾名思义,用于**在 Chrome 里**(firefox 不需要)游玩米哈游云游戏的油猴脚本。
|
||||
参见 [Bilibili 专栏](https://www.bilibili.com/opus/842314310196658193)。
|
||||
|
||||
- `bin/.battery-warn`虽说也是自用,但一是配置项并不算敏感,稍微改改`config`段也可以泛用;二是参考文献写得有点啰嗦,我懒得再缝第二遍。
|
||||
个人建议用于**定时任务(cron 或 systemd timer)**。
|
||||
|
||||
Reference in New Issue
Block a user