196 lines
7.0 KiB
Python
Executable File
196 lines
7.0 KiB
Python
Executable File
#!/usr/bin/python
|
|
# pylint: disable=missing-module-docstring
|
|
import json
|
|
import sys
|
|
import time
|
|
import dbus
|
|
import click
|
|
|
|
FBOOL = ('no', 'yes')
|
|
PROPERTIES = {
|
|
'BatteryLevel': ('unknown', 'none', 'low', 'critical', 'normal', 'high', 'full'),
|
|
'Capacity': None,
|
|
'Energy': None,
|
|
'EnergyEmpty': None,
|
|
'EnergyFull': None,
|
|
'EnergyFullDesign': None,
|
|
'EnergyRate': None,
|
|
'HasHistory': FBOOL,
|
|
'HasStatistics': FBOOL,
|
|
'IconName': None,
|
|
'IsPresent': FBOOL,
|
|
'IsRechargeable': FBOOL,
|
|
'Luminosity': None,
|
|
'Model': None,
|
|
'NativePath': None,
|
|
'Online': FBOOL,
|
|
'Percentage': None,
|
|
'PowerSupply': FBOOL,
|
|
'Serial': None,
|
|
'State': ('unknown', 'charging', 'discharging', 'empty', 'fully charged',
|
|
'pending charge','pending discharge'),
|
|
'Technology': ('unknown', 'lithium ion', 'lithium polymer', 'lithium iron phosphate',
|
|
'lead acid', 'nickel cadmium', 'nickel metal hydride'),
|
|
'Temperature': None,
|
|
'TimeToEmpty': None,
|
|
'TimeToFull': None,
|
|
'Type': ('unknown', 'line-power', 'battery', 'ups', 'monitor', 'mouse', 'keyboard',
|
|
'pda', 'phone', 'media-player', 'tablet', 'computer', 'gaming_input',
|
|
'pen', 'touchpad', 'modem', 'network', 'headset', 'speakers',
|
|
'headphones', 'video', 'other_audio', 'remote_control', 'printer', 'scanner',
|
|
'camera', 'wearable', 'toy', 'bluetooth-generic'),
|
|
'UpdateTime': None,
|
|
'Vendor': None,
|
|
'Voltage': None,
|
|
'WarningLevel': ('unknown', 'none', 'discharging', 'low', 'critical', 'action')
|
|
}
|
|
|
|
|
|
def get_tooltip(_type):
|
|
#TOOLTIP_OTHER=""" luminosity: {Luminosity}""" I don't have a way to test this property
|
|
header = ('native-path: {NativePath}'
|
|
'\npower supply: {PowerSupply}'
|
|
'\nupdated: {UpdateTime}'
|
|
'\nhas history: {HasHistory}'
|
|
'\nhas statistics: {HasStatistics}')
|
|
|
|
body = ('\n{Type}'
|
|
'\n warning-level: {WarningLevel}'
|
|
'\n icon-name: {IconName}')
|
|
|
|
if _type == 'line-power':
|
|
body += '\n online: {Online}'
|
|
|
|
else:
|
|
header += ('\nmodel: {Model}'
|
|
'\nserial: {Serial}')
|
|
|
|
body += ('\n percentage: {Percentage}%'
|
|
'\n present: {IsPresent}')
|
|
|
|
if _type == "battery":
|
|
header += ('\nvendor: {Vendor}')
|
|
body += ('\n state: {State}'
|
|
'\n rechargeable: {IsRechargeable}'
|
|
'\n energy: {Energy} Wh'
|
|
'\n energy-empty: {EnergyEmpty} Wh'
|
|
'\n energy-full: {EnergyFull} Wh'
|
|
'\n energy-full-design: {EnergyFullDesign} Wh'
|
|
'\n energy-rate: {EnergyRate} W'
|
|
'\n voltage: {Voltage} V'
|
|
'\n capacity: {Capacity}%'
|
|
'\n technology: {Technology}'
|
|
'\n temperature: {Temperature}'
|
|
'\n time-to-empty: {TimeToEmpty}'
|
|
'\n time-to-full: {TimeToFull}'
|
|
'\n battery-level: {BatteryLevel}')
|
|
|
|
return f'{header}{body}'
|
|
|
|
|
|
def device_info(bus, device):
|
|
"""Lookup device properties"""
|
|
result = {}
|
|
device_proxy = bus.get_object('org.freedesktop.UPower', device)
|
|
device_interface = dbus.Interface(device_proxy, 'org.freedesktop.DBus.Properties')
|
|
for _property, friendly_name in PROPERTIES.items():
|
|
try:
|
|
data = device_interface.Get('org.freedesktop.UPower.Device', _property)
|
|
if _property == 'UpdateTime':
|
|
result[_property] = time.ctime(data)
|
|
else:
|
|
result[_property] = friendly_name[data] if friendly_name else data
|
|
except (dbus.exceptions.DBusException, IndexError):
|
|
result[_property] = 'none'
|
|
|
|
return result
|
|
|
|
|
|
def get_devices(bus):
|
|
"""Retrieve list of Upower devices"""
|
|
devices_proxy = bus.get_object('org.freedesktop.UPower', '/org/freedesktop/UPower')
|
|
devices_interface = dbus.Interface(devices_proxy, 'org.freedesktop.UPower')
|
|
devices = devices_interface.EnumerateDevices()
|
|
|
|
return devices
|
|
|
|
|
|
def get_device(bus, devices, device):
|
|
"""Retrieve Upower device using path or model"""
|
|
|
|
if device in devices:
|
|
return device
|
|
|
|
for path in devices:
|
|
inspect = device_info(bus, path)
|
|
if inspect.get('Model') == device:
|
|
return path
|
|
|
|
raise Exception("Device Not Found")
|
|
|
|
|
|
def output_devices(bus, devices):
|
|
"""Output device list"""
|
|
for device in devices:
|
|
print(f'{device}\t{device_info(bus, device).get("Model")}')
|
|
sys.exit(0)
|
|
|
|
def check_device(key, info):
|
|
sys.exit(FBOOL.index(info.get(key.replace('{','').replace('}', ''))))
|
|
|
|
|
|
@click.command()
|
|
@click.option('--list-devices', is_flag=True, help='List devices and models')
|
|
@click.option('--check', help='Exists using boolean values for device')
|
|
@click.option('--device', '--model', help='Path or Model')
|
|
@click.option('--text', show_default=True, default="{Model}")
|
|
@click.option('--alt', show_default=True, default="{BatteryLevel}")
|
|
@click.option('--tooltip', default=None, help="Similar to upower -i <device>")
|
|
@click.option('--class', '_class', show_default=True, default="{BatteryLevel}")
|
|
@click.option('--percentage', show_default=True, default="{Percentage:.0f}")
|
|
def main(list_devices, check, device, text, alt, tooltip, _class, percentage):
|
|
"""
|
|
TEXT can be replaced using one or more {KEY}\n
|
|
{BatteryLevel} {Capacity} {Energy} {EnergyEmpty} {EnergyFull}
|
|
{EnergyFullDesign} {EnergyRate} {HasHistory} {HasStatistics}
|
|
{IconName} {IsPresent} {IsRechargeable} {Luminosity}
|
|
{Model} {NativePath} {Online} {Percentage} {PowerSupply}
|
|
{Serial} {State} {Technology} {Temperature} {TimeToEmpty}
|
|
{TimeToFull} {Type} {UpdateTime} {Vendor} {Voltage}
|
|
{WarningLevel}
|
|
|
|
Example: supower.py --model 'MX Master 2S' --tooltip '{State}'
|
|
supower.py --model '/org/freedesktop/UPower/devices/line_power_AC' --check Online
|
|
|
|
"""
|
|
exit = 0
|
|
bus = dbus.SystemBus()
|
|
devices = get_devices(bus)
|
|
|
|
if list_devices:
|
|
output_devices(bus, devices)
|
|
|
|
try:
|
|
device = get_device(bus, devices, device)
|
|
info = device_info(bus, device)
|
|
if check:
|
|
check_device(check, info)
|
|
|
|
output = {
|
|
"text": text.format(**info),
|
|
"alt": alt.format(**info),
|
|
"tooltip": (tooltip if tooltip else get_tooltip(info['Type'])).format(**info),
|
|
"class": _class.format(**info),
|
|
"percentage": float(percentage.format(**info))
|
|
}
|
|
except Exception as error:
|
|
output = {"text": f'Error {device.split("/")[-1]} {error}', 'tooltip': f'{error}'}
|
|
exit = 2
|
|
|
|
print(json.dumps(output))
|
|
sys.exit(exit)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
# pylint: disable=no-value-for-parameter
|
|
main() |