#!/usr/bin/env python3
from __future__ import absolute_import, division, print_function
import argparse
import os
import os.path
import subprocess
import sys
import time
def eprint(*args, **kwargs):
kwargs['file'] = sys.stderr
print(*args, **kwargs)
def bat_value(fname):
f = os.path.join(batdir, fname)
if not os.access(f, os.R_OK):
eprint('Could not access "%s".' % f)
sys.exit(1)
val = 0.0
try:
val = abs(float(open(f).read()))
except IOError:
eprint('Could not open "%s".' % f)
sys.exit(1)
except ValueError:
eprint(
'The value "%s", returned from "%s", is not a valid float.'
% (open(f).read(), f))
return val
def output():
full, now, pow_now = bat_value(ffull), bat_value(fnow), bat_value(fpow_now)
charged = '%d' % (100 * (now / full))
# If the battery isn't discharging, just report 'AC' instead of
# the time remaining. We also consider it on A/C when the charge is
# a very high percentage of a full charge. This is because it appears
# that sometimes the status is 'Discharging' even when plugged into
# AC. (For Framework laptop.)
status = open(os.path.join(batdir, 'status')).read().strip()
if status != 'Discharging' or (now / full) > 0.995:
print('%s%% AC' % charged)
sys.stdout.flush()
return
seconds_rem = (now * 60 * 60) / pow_now
minutes_rem = '%d' % ((seconds_rem / 60) % 60)
hours_rem = '%d' % (seconds_rem / 60 / 60)
def rem():
if conf.short_rem:
return '%hh%mm'
return '%h hours, %m minutes'
format_str = ''
if conf.format_str:
format_str = conf.format_str
elif conf.output_type == 'charge':
format_str = '%c%'
elif conf.output_type == 'rem':
format_str = rem()
else:
format_str = rem() + ' %c%'
assert(len(format_str) > 0)
out = format_str
out = out.replace('%c', charged)
out = out.replace('%h', hours_rem)
out = out.replace('%m', minutes_rem)
print(out)
sys.stdout.flush()
if __name__ == '__main__':
parser = argparse.ArgumentParser(
description='Emit battery status charge level and time remaining.',
formatter_class=argparse.ArgumentDefaultsHelpFormatter)
aa = parser.add_argument
aa('-f', '--format-str', type=str, metavar='FORMAT_STRING',
help='A format string overrides the "-s" and "-t" options, and allows '
'complete control over the formatting of the battery status '
'message. Use "%%h" for hours, "%%m" for minutes and "%%c" for '
'charge level. For example, using '
'"%%h hours, %%m minutes (%%c%%)" a sample output '
'might look like "5 hours, 16 minutes (66.90%%)".')
aa('-i', '--interval', type=int, metavar='POLL_INTERVAL_SECONDS',
help='The time, in seconds, between battery status polling. If this '
'option is not specified, the current battery status will be '
'printed once, and the program will exit.')
aa('-s', '--short-rem', action='store_true',
help='When enabled, the "time remaining" display will be shortened '
'form "X hours, Y minutes" to "XhYm".')
aa('-t', '--output-type', choices=['charge', 'rem'], metavar='OUTPUT_TYPE',
help='The type of output to display. If this option is omitted, both '
'the charge level and the time remaining are displayed. If '
'"charge" is specified, only the charge level is displayed. If '
'"rem" is specified, only the time remaining is displayed.')
conf = parser.parse_args()
host = subprocess.check_output(['myhostname']).decode('utf-8').strip()
batdir = ''
ffull = 'energy_full'
fnow = 'energy_now'
fpow_now = 'power_now'
batdir = subprocess.check_output(['batpath']).decode('utf-8').strip()
if host in ('maggie', 'bart'):
ffull = 'charge_full'
fnow = 'charge_now'
fpow_now = 'current_now'
if not os.path.isdir(batdir):
eprint('Battery directory "%s" is not accessible.' % batdir)
sys.exit(1)
if conf.interval is not None and conf.interval > 0:
while True:
output()
time.sleep(conf.interval)
else:
output()