#!/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 ") @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()