Last active
January 8, 2016 09:19
-
-
Save winny-/83a94c921740a70934ed to your computer and use it in GitHub Desktop.
A horrid DWM status script in python that does't spin up your disk every iteration
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
# -*- coding: utf-8 -*- | |
# ex: set tabstop=4 expandtab ai: | |
from __future__ import print_function | |
from ctypes import cdll, c_char_p | |
import time | |
from datetime import datetime | |
from os import listdir, path | |
from collections import namedtuple | |
import dbus | |
import sys | |
import struct | |
import socket | |
import subprocess | |
battery_status = namedtuple('battery_status', ['name', 'status', 'percent', 'time_left']) | |
network_status = namedtuple('network_status', ['type', 'ipv4', 'ssid', 'strength']) | |
PROPERTIES_INTERFACE = 'org.freedesktop.DBus.Properties' | |
UPOWER_BUS = 'org.freedesktop.UPower' | |
UPOWER_INTERFACE = UPOWER_BUS | |
UPOWER_DEVICE_INTERFACE = UPOWER_BUS + '.Device' | |
UPOWER_PATH = '/org/freedesktop/UPower' | |
UPOWER_DEVICE_STATUS_MAP = { | |
0: 'Unknown', | |
1: 'Charging', | |
2: 'Discharging', | |
3: 'Empty', | |
4: 'Full', | |
5: 'Unknown', | |
6: 'Unknown', | |
} | |
NETWORKMANAGER_BUS = 'org.freedesktop.NetworkManager' | |
NETWORKMANAGER_PATH = '/org/freedesktop/NetworkManager' | |
NETWORKMANAGER_INTERFACE = NETWORKMANAGER_BUS | |
NETWORKMANAGER_CONNECTION_ACTIVE_INTERFACE = NETWORKMANAGER_INTERFACE + '.Connection.Active' | |
NETWORKMANAGER_DEVICE_INTERFACE = NETWORKMANAGER_INTERFACE + '.Device' | |
NETWORKMANAGER_IP4CONFIG_INTERFACE = NETWORKMANAGER_INTERFACE + '.IP4Config' | |
NETWORKMANAGER_DEVICE_WIRELESS_INTERFACE = NETWORKMANAGER_DEVICE_INTERFACE + '.Wireless' | |
NETWORKMANAGER_ACCESSPOINT_INTERFACE = NETWORKMANAGER_INTERFACE + '.AccessPoint' | |
PITHOS_BUS = 'net.kevinmehall.Pithos' | |
PITHOS_INTERFACE = PITHOS_BUS | |
PITHOS_PATH = '/net/kevinmehall/Pithos' | |
def truncate(s, length, end='...'): | |
if len(s) <= length + len(end): | |
return s | |
return ''.join([s[:length], end]) | |
def construct_store_name_fn(display_name=None): | |
if display_name is not None: | |
display_name = c_char_p(display_name.encode()) | |
Xlib = cdll.LoadLibrary('libX11.so') | |
display = Xlib.XOpenDisplay(display_name) | |
if display is None: | |
raise RuntimeError('Could not open display.') | |
screen = Xlib.XDefaultScreen(display) | |
root = Xlib.XRootWindow(display, screen) | |
def store_name(name): | |
Xlib.XStoreName(display, root, c_char_p(name.encode('iso-8859-1'))) | |
Xlib.XFlush(display) | |
return store_name | |
def xsetroot_store_name(name): | |
subprocess.call(['xsetroot', '-name', name]) | |
def get_batteries_status_sysfs(): | |
ps = '/sys/class/power_supply' | |
batteries = [] | |
for f in listdir(ps): | |
if not f.startswith('BAT'): | |
continue | |
with open(path.join(ps, f, 'uevent')) as fu: | |
uevent = dict(line.strip().replace('POWER_SUPPLY_', '', 1).split('=', 1) | |
for line in fu.readlines()) | |
if uevent['PRESENT'] != '1': | |
continue | |
charge_now = float(uevent['CHARGE_NOW']) | |
charge_full = float(uevent['CHARGE_FULL']) | |
current_now = float(uevent['CURRENT_NOW']) | |
status = uevent['STATUS'] | |
percent = min(charge_now / charge_full, 1.0) | |
if status == 'Charging': | |
time_left = (charge_full - charge_now) / current_now * 3600.0 | |
elif status == 'Discharging': | |
time_left = charge_now / current_now * 3600.0 | |
else: | |
time_left = 0 | |
batteries.append(battery_status(uevent['NAME'], | |
status, | |
percent, | |
int(time_left))) | |
return batteries | |
def get_system_bus(): | |
global system_bus | |
try: | |
system_bus | |
except NameError: | |
system_bus = dbus.SystemBus() | |
return system_bus | |
def get_session_bus(): | |
global session_bus | |
try: | |
session_bus | |
except NameError: | |
session_bus = dbus.SessionBus() | |
return session_bus | |
def get_props(object_, interface): | |
return object_.GetAll(interface, dbus_interface=PROPERTIES_INTERFACE) | |
def get_prop(object_, interface, prop): | |
return object_.Get(interface, prop, dbus_interface=PROPERTIES_INTERFACE) | |
def get_batteries_status_dbus(): | |
system_bus = get_system_bus() | |
try: | |
up = system_bus.get_object(UPOWER_BUS, UPOWER_PATH) | |
devices = up.EnumerateDevices(dbus_interface=UPOWER_INTERFACE) | |
except dbus.exceptions.DBusException: | |
return [] | |
L = [] | |
for dev in devices: | |
d = system_bus.get_object(UPOWER_BUS, dev) | |
props = get_props(d, UPOWER_DEVICE_INTERFACE) | |
if not (props['Type'] == 2 and props['IsPresent']): | |
continue | |
t = props['TimeToFull'] if int(props['State']) in [0, 1, 4, 5] else props['TimeToEmpty'] | |
L.append(battery_status(path.basename(dev), | |
UPOWER_DEVICE_STATUS_MAP[int(props['State'])], | |
props['Percentage'] / 100.0, | |
t)) | |
return L | |
def get_network_status_dbus(): | |
system_bus = get_system_bus() | |
nm = system_bus.get_object(NETWORKMANAGER_BUS, NETWORKMANAGER_PATH) | |
props = get_props(nm, NETWORKMANAGER_INTERFACE) | |
if not props['NetworkingEnabled'] or props['Connectivity'] < 2: | |
return None | |
for ac in props['ActiveConnections']: | |
active = system_bus.get_object(NETWORKMANAGER_BUS, ac) | |
aprops = get_props(active, NETWORKMANAGER_CONNECTION_ACTIVE_INTERFACE) | |
if aprops['State'] != 2 or aprops['Vpn'] or not aprops['Default']: | |
continue | |
devs = aprops['Devices'] | |
for dev in devs: | |
try: | |
d = system_bus.get_object(NETWORKMANAGER_BUS, dev) | |
dprops = get_props(d, NETWORKMANAGER_DEVICE_INTERFACE) | |
type_ = dprops['DeviceType'] | |
ipv4_cfg = system_bus.get_object(NETWORKMANAGER_BUS, dprops['Ip4Config']) | |
except dbus.exceptions.DBusException: | |
continue | |
try: | |
addresses = get_prop(ipv4_cfg, NETWORKMANAGER_IP4CONFIG_INTERFACE, 'AddressData') | |
address = addresses[0]['address'] | |
except dbus.exceptions.DBusException: | |
network_order = get_prop(ipv4_cfg, NETWORKMANAGER_IP4CONFIG_INTERFACE, 'Addresses')[0][0] | |
address = socket.inet_ntoa(struct.pack('@I', network_order)) | |
if type_ == 1: | |
return network_status('ethernet', address, None, None) | |
elif type_ == 2: | |
ap_path = get_prop(d, | |
NETWORKMANAGER_DEVICE_WIRELESS_INTERFACE, | |
'ActiveAccessPoint') | |
ap = system_bus.get_object(NETWORKMANAGER_BUS, ap_path) | |
ap_props = get_props(ap, NETWORKMANAGER_ACCESSPOINT_INTERFACE) | |
return network_status( | |
'wireless', | |
str(address), | |
''.join(chr(b) for b in ap_props['Ssid']), | |
ap_props['Strength'] / 100.0, | |
) | |
else: | |
return None | |
def get_now_playing(): | |
return get_pithos_now_playing() | |
def get_pithos_now_playing(): | |
session_bus = get_session_bus() | |
try: | |
pithos = session_bus.get_object(PITHOS_BUS, PITHOS_PATH) | |
current_song = pithos.GetCurrentSong(dbus_interface=PITHOS_INTERFACE) | |
except dbus.exceptions.DBusException: | |
return None | |
return dict(**current_song) | |
# XXX: Not working. Always returns 20C. | |
def get_cpu_temperature(): | |
with open('/sys/class/thermal/thermal_zone0/temp') as f: | |
s = f.read() | |
thousandths = int(s) | |
return thousandths / 1000.0 | |
def format_seconds(s): | |
hours = s // 3600 | |
s -= hours * 3600 | |
minutes = s // 60 | |
seconds = s - (minutes * 60) | |
return '{}{}m'.format( | |
'{}h'.format(hours) if hours else '', | |
minutes, | |
) | |
def sleep_until_next_minute(): | |
t = time.time() | |
then = t + 60 | |
then -= (then % 60) | |
difference = then - t | |
if difference <= 0.0: | |
return | |
time.sleep(difference) | |
def format_battery(battery, with_name=False): | |
L = [] | |
if with_name: | |
L.append(battery.name) | |
L.append(battery.status[0]) | |
if not battery.status.lower().startswith('full'): | |
L.extend([ | |
'{:2.0%}'.format(battery.percent), | |
format_seconds(battery.time_left), | |
]) | |
return ' '.join(L) | |
def format_network(network): | |
if network is None: | |
return 'no networking' | |
if network.type == 'ethernet': | |
return 'ethernet' | |
elif network.type == 'wireless': | |
return 'wifi {} {:2.0%}'.format(truncate(network.ssid, 12), network.strength) | |
else: | |
raise RuntimeError('unknown network type') | |
def status_bar(sep=' | '): | |
L = [] | |
playing = get_now_playing() | |
if playing is not None: | |
L.append('{} by {}'.format( | |
truncate(playing['title'], 20), | |
truncate(playing['artist'], 20), | |
)) | |
#L.append('{:.0f}C'.format(get_cpu_temperature())) | |
L.append(format_network(get_network_status_dbus())) | |
batteries = get_batteries_status_dbus() | |
if batteries: | |
L.append(sep.join(format_battery(b, with_name=len(batteries) > 1) | |
for b in batteries)) | |
L.append('{:%a %b %d %H:%M}'.format(datetime.now())) | |
return sep.join(L) | |
def show_cli(interval=1): | |
try: | |
while True: | |
sys.stdout.write('\r') | |
sys.stdout.write(status_bar()) | |
sys.stdout.flush() | |
time.sleep(interval) | |
except KeyboardInterrupt as e: | |
sys.stdout.write('\n') | |
raise e | |
def main(): | |
#store_name = construct_store_name_fn() | |
store_name = xsetroot_store_name | |
while True: | |
s = status_bar() | |
store_name(s) | |
time.sleep(30) | |
# sleep_until_next_minute() | |
if __name__ == '__main__': | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment