[formatting] reformat using "black -t py34"

getting rid of thinking about consistent formatting...
This commit is contained in:
tobi-wan-kenobi 2020-05-03 11:15:52 +02:00
parent fa98bcbdd1
commit 30c1f712a6
119 changed files with 3961 additions and 3495 deletions

View file

@ -7,34 +7,48 @@ import glob
import socket import socket
button = { button = {
'left-mouse': 1, "left-mouse": 1,
'middle-mouse': 2, "middle-mouse": 2,
'right-mouse': 3, "right-mouse": 3,
'wheel-up': 4, "wheel-up": 4,
'wheel-down': 5, "wheel-down": 5,
} }
def main(): def main():
parser = argparse.ArgumentParser(description='send commands to bumblebee-status') parser = argparse.ArgumentParser(description="send commands to bumblebee-status")
parser.add_argument('-b', '--button', choices=['left-mouse', 'right-mouse', 'middle-mouse', 'wheel-up', 'wheel-down'], help='button to emulate', default='left-mouse') parser.add_argument(
parser.add_argument('-i', '--id', help='ID of widget to trigger') "-b",
parser.add_argument('-m', '--module', help='name of the module to trigger', required=True) "--button",
choices=["left-mouse", "right-mouse", "middle-mouse", "wheel-up", "wheel-down"],
help="button to emulate",
default="left-mouse",
)
parser.add_argument("-i", "--id", help="ID of widget to trigger")
parser.add_argument(
"-m", "--module", help="name of the module to trigger", required=True
)
args = parser.parse_args() args = parser.parse_args()
for f in glob.glob('/tmp/.bumblebee-status.*'): for f in glob.glob("/tmp/.bumblebee-status.*"):
s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
try: try:
s.connect(f) s.connect(f)
s.sendall(json.dumps({ s.sendall(
'name': args.module, json.dumps(
'instance': args.id, {
'button': button[args.button], "name": args.module,
}).encode('ascii')) "instance": args.id,
"button": button[args.button],
}
).encode("ascii")
)
except Exception as e: except Exception as e:
os.remove(f) os.remove(f)
if __name__ == '__main__':
if __name__ == "__main__":
main() main()
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -14,9 +14,10 @@ import core.module
import core.input import core.input
import core.event import core.event
class CommandSocket(object): class CommandSocket(object):
def __init__(self): def __init__(self):
self.__name = '/tmp/.bumblebee-status.{}'.format(os.getpid()) self.__name = "/tmp/.bumblebee-status.{}".format(os.getpid())
self.__socket = None self.__socket = None
def __enter__(self): def __enter__(self):
@ -29,6 +30,7 @@ class CommandSocket(object):
self.__socket.close() self.__socket.close()
os.unlink(self.__name) os.unlink(self.__name)
def handle_input(output): def handle_input(output):
with CommandSocket() as cmdsocket: with CommandSocket() as cmdsocket:
poll = select.poll() poll = select.poll()
@ -44,24 +46,25 @@ def handle_input(output):
tmp, _ = cmdsocket.accept() tmp, _ = cmdsocket.accept()
line = tmp.recv(4096).decode() line = tmp.recv(4096).decode()
tmp.close() tmp.close()
logging.debug('socket event {}'.format(line)) logging.debug("socket event {}".format(line))
else: else:
line = '[' line = "["
while line.startswith('['): while line.startswith("["):
line = sys.stdin.readline().strip(',').strip() line = sys.stdin.readline().strip(",").strip()
logging.info('input event: {}'.format(line)) logging.info("input event: {}".format(line))
try: try:
event = json.loads(line) event = json.loads(line)
core.input.trigger(event) core.input.trigger(event)
if 'name' in event: if "name" in event:
modules[event['name']] = True modules[event["name"]] = True
except ValueError: except ValueError:
pass pass
core.event.trigger('update', modules.keys()) core.event.trigger("update", modules.keys())
core.event.trigger('draw') core.event.trigger("draw")
poll.unregister(sys.stdin.fileno()) poll.unregister(sys.stdin.fileno())
def main(): def main():
config = core.config.Config(sys.argv[1:]) config = core.config.Config(sys.argv[1:])
@ -70,28 +73,28 @@ def main():
logging.basicConfig( logging.basicConfig(
level=level, level=level,
format="[%(asctime)s] %(module)-16s %(levelname)-8s %(message)s", format="[%(asctime)s] %(module)-16s %(levelname)-8s %(message)s",
filename=os.path.abspath(os.path.expanduser(config.logfile())) filename=os.path.abspath(os.path.expanduser(config.logfile())),
) )
else: else:
logging.basicConfig( logging.basicConfig(
level=level, level=level,
format="[%(asctime)s] %(module)-16s %(levelname)-8s %(message)s", format="[%(asctime)s] %(module)-16s %(levelname)-8s %(message)s",
stream=sys.stderr stream=sys.stderr,
) )
theme = core.theme.Theme(config.theme(), config.iconset()) theme = core.theme.Theme(config.theme(), config.iconset())
output = core.output.i3(theme, config) output = core.output.i3(theme, config)
modules = [] modules = []
core.input.register(None, core.input.WHEEL_UP, 'i3-msg workspace prev_on_output') core.input.register(None, core.input.WHEEL_UP, "i3-msg workspace prev_on_output")
core.input.register(None, core.input.WHEEL_DOWN, 'i3-msg workspace next_on_output') core.input.register(None, core.input.WHEEL_DOWN, "i3-msg workspace next_on_output")
input_thread = threading.Thread(target=handle_input, args=(output,)) input_thread = threading.Thread(target=handle_input, args=(output,))
input_thread.daemon = True input_thread.daemon = True
input_thread.start() input_thread.start()
if config.debug(): if config.debug():
modules.append(core.module.load('debug', config, theme)) modules.append(core.module.load("debug", config, theme))
for module in config.modules(): for module in config.modules():
modules.append(core.module.load(module, config, theme)) modules.append(core.module.load(module, config, theme))
@ -100,12 +103,13 @@ def main():
modules.reverse() modules.reverse()
output.modules(modules) output.modules(modules)
core.event.trigger('start') core.event.trigger("start")
while True: while True:
core.event.trigger('update') core.event.trigger("update")
core.event.trigger('draw') core.event.trigger("draw")
output.wait(config.interval()) output.wait(config.interval())
core.event.trigger('stop') core.event.trigger("stop")
if __name__ == "__main__": if __name__ == "__main__":
main() main()
@ -113,9 +117,9 @@ if __name__ == "__main__":
main() main()
except Exception as e: except Exception as e:
output = core.output.i3() output = core.output.i3()
output.modules(core.module.Error(module='main', error=e)) output.modules(core.module.Error(module="main", error=e))
output.draw('start') output.draw("start")
output.update() output.update()
output.draw('statusline') output.draw("statusline")
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -19,9 +19,12 @@ import modules.contrib
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
MODULE_HELP = 'Specify a space-separated list of modules to load. The order of the list determines their order in the i3bar (from left to right). Use <module>:<alias> to provide an alias in case you want to load the same module multiple times, but specify different parameters.' MODULE_HELP = "Specify a space-separated list of modules to load. The order of the list determines their order in the i3bar (from left to right). Use <module>:<alias> to provide an alias in case you want to load the same module multiple times, but specify different parameters."
PARAMETER_HELP = 'Provide configuration parameters in the form of <module>.<key>=<value>' PARAMETER_HELP = (
THEME_HELP = 'Specify the theme to use for drawing modules' "Provide configuration parameters in the form of <module>.<key>=<value>"
)
THEME_HELP = "Specify the theme to use for drawing modules"
def all_modules(): def all_modules():
"""Return a list of available modules""" """Return a list of available modules"""
@ -29,48 +32,53 @@ def all_modules():
for path in [modules.core.__file__, modules.contrib.__file__]: for path in [modules.core.__file__, modules.contrib.__file__]:
path = os.path.dirname(path) path = os.path.dirname(path)
for mod in glob.iglob('{}/*.py'.format(path)): for mod in glob.iglob("{}/*.py".format(path)):
result[os.path.basename(mod).replace('.py', '')] = 1 result[os.path.basename(mod).replace(".py", "")] = 1
res = list(result.keys()) res = list(result.keys())
res.sort() res.sort()
return res return res
class print_usage(argparse.Action): class print_usage(argparse.Action):
def __init__(self, option_strings, dest, nargs=None, **kwargs): def __init__(self, option_strings, dest, nargs=None, **kwargs):
argparse.Action.__init__(self, option_strings, dest, nargs, **kwargs) argparse.Action.__init__(self, option_strings, dest, nargs, **kwargs)
self._indent = ' '*2 self._indent = " " * 2
def __call__(self, parser, namespace, value, option_string=None): def __call__(self, parser, namespace, value, option_string=None):
if value == 'modules': if value == "modules":
self._args = namespace self._args = namespace
self._format = 'plain' self._format = "plain"
self.print_modules() self.print_modules()
elif value == 'modules-markdown': elif value == "modules-markdown":
self._args = namespace self._args = namespace
self._format = 'markdown' self._format = "markdown"
self.print_modules() self.print_modules()
elif value == 'themes': elif value == "themes":
self.print_themes() self.print_themes()
sys.exit(0) sys.exit(0)
def print_themes(self): def print_themes(self):
print(', '.join(core.theme.themes())) print(", ".join(core.theme.themes()))
def print_modules(self): def print_modules(self):
if self._format == 'markdown': if self._format == "markdown":
print('# Table of modules') print("# Table of modules")
print('|Name |Description |') print("|Name |Description |")
print('|-----|------------|') print("|-----|------------|")
for m in all_modules(): for m in all_modules():
try: try:
basepath = os.path.abspath(os.path.join(os.path.dirname(os.path.realpath(__file__)), '..')) basepath = os.path.abspath(
filename = os.path.join(basepath, 'modules', 'core', '{}.py'.format(m)) os.path.join(os.path.dirname(os.path.realpath(__file__)), "..")
)
filename = os.path.join(basepath, "modules", "core", "{}.py".format(m))
if not os.path.exists(filename): if not os.path.exists(filename):
filename = os.path.join(basepath, 'modules', 'contrib', '{}.py'.format(m)) filename = os.path.join(
basepath, "modules", "contrib", "{}.py".format(m)
)
if not os.path.exists(filename): if not os.path.exists(filename):
log.warning('module {} not found'.format(m)) log.warning("module {} not found".format(m))
continue continue
doc = None doc = None
@ -79,72 +87,126 @@ class print_usage(argparse.Action):
doc = ast.get_docstring(tree) doc = ast.get_docstring(tree)
if not doc: if not doc:
log.warning('failed to find docstring for {}'.format(m)) log.warning("failed to find docstring for {}".format(m))
continue continue
if self._format == 'markdown': if self._format == "markdown":
doc = doc.replace('<', '\<') doc = doc.replace("<", "\<")
doc = doc.replace('>', '\>') doc = doc.replace(">", "\>")
doc = doc.replace('\n', '<br>') doc = doc.replace("\n", "<br>")
print('|{} |{} |'.format(m, doc)) print("|{} |{} |".format(m, doc))
else: else:
print(textwrap.fill('{}:'.format(m), 80, print(
initial_indent=self._indent*2, subsequent_indent=self._indent*2)) textwrap.fill(
for line in doc.split('\n'): "{}:".format(m),
print(textwrap.fill(line, 80, 80,
initial_indent=self._indent*3, subsequent_indent=self._indent*6)) initial_indent=self._indent * 2,
subsequent_indent=self._indent * 2,
)
)
for line in doc.split("\n"):
print(
textwrap.fill(
line,
80,
initial_indent=self._indent * 3,
subsequent_indent=self._indent * 6,
)
)
except Exception as e: except Exception as e:
log.warning(e) log.warning(e)
class Config(util.store.Store): class Config(util.store.Store):
def __init__(self, args): def __init__(self, args):
super(Config, self).__init__() super(Config, self).__init__()
parser = argparse.ArgumentParser(description='bumblebee-status is a modular, theme-able status line generator for the i3 window manager. https://github.com/tobi-wan-kenobi/bumblebee-status/wiki') parser = argparse.ArgumentParser(
parser.add_argument('-m', '--modules', nargs='+', action='append', default=[], description="bumblebee-status is a modular, theme-able status line generator for the i3 window manager. https://github.com/tobi-wan-kenobi/bumblebee-status/wiki"
help=MODULE_HELP) )
parser.add_argument('-p', '--parameters', nargs='+', action='append', default=[], parser.add_argument(
help=PARAMETER_HELP) "-m", "--modules", nargs="+", action="append", default=[], help=MODULE_HELP
parser.add_argument('-t', '--theme', default='default', help=THEME_HELP) )
parser.add_argument('-i', '--iconset', default='auto', parser.add_argument(
help='Specify the name of an iconset to use (overrides theme default)') "-p",
parser.add_argument('-a', '--autohide', nargs='+', default=[], "--parameters",
help='Specify a list of modules to hide when not in warning/error state') nargs="+",
parser.add_argument('-d', '--debug', action='store_true', action="append",
help='Add debug fields to i3 output') default=[],
parser.add_argument('-f', '--logfile', help='destination for the debug log file, if -d|--debug is specified; defaults to stderr') help=PARAMETER_HELP,
parser.add_argument('-r', '--right-to-left', action='store_true', help='Draw widgets from right to left, rather than left to right (which is the default)') )
parser.add_argument('-l', '--list', choices=['modules', 'themes', 'modules-markdown'], help='Display a list of available themes or available modules, along with their parameters', parser.add_argument("-t", "--theme", default="default", help=THEME_HELP)
action=print_usage) parser.add_argument(
"-i",
"--iconset",
default="auto",
help="Specify the name of an iconset to use (overrides theme default)",
)
parser.add_argument(
"-a",
"--autohide",
nargs="+",
default=[],
help="Specify a list of modules to hide when not in warning/error state",
)
parser.add_argument(
"-d", "--debug", action="store_true", help="Add debug fields to i3 output"
)
parser.add_argument(
"-f",
"--logfile",
help="destination for the debug log file, if -d|--debug is specified; defaults to stderr",
)
parser.add_argument(
"-r",
"--right-to-left",
action="store_true",
help="Draw widgets from right to left, rather than left to right (which is the default)",
)
parser.add_argument(
"-l",
"--list",
choices=["modules", "themes", "modules-markdown"],
help="Display a list of available themes or available modules, along with their parameters",
action=print_usage,
)
self.__args = parser.parse_args(args) self.__args = parser.parse_args(args)
for cfg in [ '~/.bumblebee-status.conf', '~/.config/bumblebee-status.conf', '~/.config/bumblebee-status/config' ]: for cfg in [
"~/.bumblebee-status.conf",
"~/.config/bumblebee-status.conf",
"~/.config/bumblebee-status/config",
]:
cfg = os.path.expanduser(cfg) cfg = os.path.expanduser(cfg)
self.load_config(cfg) self.load_config(cfg)
parameters = [item for sub in self.__args.parameters for item in sub] parameters = [item for sub in self.__args.parameters for item in sub]
for param in parameters: for param in parameters:
if not '=' in param: if not "=" in param:
log.error('missing value for parameter "{}" - ignoring this parameter'.format(param)) log.error(
'missing value for parameter "{}" - ignoring this parameter'.format(
param
)
)
continue continue
key, value = param.split('=', 1) key, value = param.split("=", 1)
self.set(key, value) self.set(key, value)
def load_config(self, filename): def load_config(self, filename):
if os.path.exists(filename): if os.path.exists(filename):
log.info('loading {}'.format(filename)) log.info("loading {}".format(filename))
tmp = RawConfigParser() tmp = RawConfigParser()
tmp.read(filename) tmp.read(filename)
if tmp.has_section('module-parameters'): if tmp.has_section("module-parameters"):
for key, value in tmp.items('module-parameters'): for key, value in tmp.items("module-parameters"):
self.set(key, value) self.set(key, value)
def modules(self): def modules(self):
return [item for sub in self.__args.modules for item in sub] return [item for sub in self.__args.modules for item in sub]
def interval(self, default=1): def interval(self, default=1):
return util.format.seconds(self.get('interval', default)) return util.format.seconds(self.get("interval", default))
def debug(self): def debug(self):
return self.__args.debug return self.__args.debug
@ -164,4 +226,5 @@ class Config(util.store.Store):
def autohide(self, name): def autohide(self, name):
return name in self.__args.autohide return name in self.__args.autohide
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -1,41 +1,49 @@
import util.format import util.format
def never(init): def never(init):
def call_init(obj, *args, **kwargs): def call_init(obj, *args, **kwargs):
init(obj, *args, **kwargs) init(obj, *args, **kwargs)
if obj.parameter('interval') is None: if obj.parameter("interval") is None:
obj.set('interval', 'never') obj.set("interval", "never")
return call_init return call_init
def every(hours=0, minutes=0, seconds=0): def every(hours=0, minutes=0, seconds=0):
def decorator_init(init): def decorator_init(init):
def call_init(obj, *args, **kwargs): def call_init(obj, *args, **kwargs):
init(obj, *args, **kwargs) init(obj, *args, **kwargs)
if obj.parameter('interval') is None: if obj.parameter("interval") is None:
obj.set('interval', hours*3600 + minutes*60 + seconds) obj.set("interval", hours * 3600 + minutes * 60 + seconds)
return call_init return call_init
return decorator_init return decorator_init
def scrollable(func): def scrollable(func):
def wrapper(module, widget): def wrapper(module, widget):
text = func(module, widget) text = func(module, widget)
if not text: if not text:
return text return text
width = widget.get('theme.width', util.format.asint(module.parameter('width', 30))) width = widget.get(
if util.format.asbool(module.parameter('scrolling.makewide', True)): "theme.width", util.format.asint(module.parameter("width", 30))
widget.set('theme.minwidth', 'A'*width) )
if util.format.asbool(module.parameter("scrolling.makewide", True)):
widget.set("theme.minwidth", "A" * width)
if width < 0 or len(text) <= width: if width < 0 or len(text) <= width:
return text return text
start = widget.get('scrolling.start', 0) start = widget.get("scrolling.start", 0)
bounce = util.format.asbool(module.parameter('scrolling.bounce', True)) bounce = util.format.asbool(module.parameter("scrolling.bounce", True))
scroll_speed = util.format.asint(module.parameter('scrolling.speed', 1)) scroll_speed = util.format.asint(module.parameter("scrolling.speed", 1))
direction = widget.get('scrolling.direction', 'right') direction = widget.get("scrolling.direction", "right")
if direction == 'left': if direction == "left":
scroll_speed = -scroll_speed scroll_speed = -scroll_speed
if start + scroll_speed <= 0: # bounce back if start + scroll_speed <= 0: # bounce back
widget.set('scrolling.direction', 'right') widget.set("scrolling.direction", "right")
next_start = start + scroll_speed next_start = start + scroll_speed
if next_start + width > len(text): if next_start + width > len(text):
@ -43,11 +51,13 @@ def scrollable(func):
next_start = 0 next_start = 0
else: else:
next_start = start - scroll_speed next_start = start - scroll_speed
widget.set('scrolling.direction', 'left') widget.set("scrolling.direction", "left")
widget.set('scrolling.start', next_start) widget.set("scrolling.start", next_start)
return text[start : start + width] return text[start : start + width]
return wrapper return wrapper
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -1,6 +1,6 @@
__callbacks = {} __callbacks = {}
def register(event, callback, *args, **kwargs): def register(event, callback, *args, **kwargs):
cb = callback cb = callback
if len(args) + len(kwargs) > 0: if len(args) + len(kwargs) > 0:
@ -8,12 +8,15 @@ def register(event, callback, *args, **kwargs):
__callbacks.setdefault(event, []).append(cb) __callbacks.setdefault(event, []).append(cb)
def clear(): def clear():
__callbacks.clear() __callbacks.clear()
def trigger(event, *args, **kwargs): def trigger(event, *args, **kwargs):
cb = __callbacks.get(event, []) cb = __callbacks.get(event, [])
if len(cb) == 0: return False if len(cb) == 0:
return False
for callback in cb: for callback in cb:
if len(args) + len(kwargs) == 0: if len(args) + len(kwargs) == 0:
@ -22,4 +25,5 @@ def trigger(event, *args, **kwargs):
callback(*args, **kwargs) callback(*args, **kwargs)
return True return True
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -11,45 +11,59 @@ RIGHT_MOUSE = 3
WHEEL_UP = 4 WHEEL_UP = 4
WHEEL_DOWN = 5 WHEEL_DOWN = 5
def button_name(button): def button_name(button):
if button == LEFT_MOUSE: return 'left-mouse' if button == LEFT_MOUSE:
if button == RIGHT_MOUSE: return 'right-mouse' return "left-mouse"
if button == MIDDLE_MOUSE: return 'middle-mouse' if button == RIGHT_MOUSE:
if button == WHEEL_UP: return 'wheel-up' return "right-mouse"
if button == WHEEL_DOWN: return 'wheel-down' if button == MIDDLE_MOUSE:
return 'n/a' return "middle-mouse"
if button == WHEEL_UP:
return "wheel-up"
if button == WHEEL_DOWN:
return "wheel-down"
return "n/a"
class Object(object): class Object(object):
def __init__(self): def __init__(self):
super(Object, self).__init__() super(Object, self).__init__()
self.id = str(uuid.uuid4()) self.id = str(uuid.uuid4())
def __event_id(obj_id, button): def __event_id(obj_id, button):
return '{}::{}'.format(obj_id, button_name(button)) return "{}::{}".format(obj_id, button_name(button))
def __execute(cmd): def __execute(cmd):
try: try:
util.cli.execute(cmd, wait=False) util.cli.execute(cmd, wait=False)
except Exception as e: except Exception as e:
logging.error('failed to invoke callback: {}'.format(e)) logging.error("failed to invoke callback: {}".format(e))
def register(obj, button=None, cmd=None): def register(obj, button=None, cmd=None):
event_id = __event_id(obj.id if obj is not None else '', button) event_id = __event_id(obj.id if obj is not None else "", button)
logging.debug('registering callback {}'.format(event_id)) logging.debug("registering callback {}".format(event_id))
if callable(cmd): if callable(cmd):
core.event.register(event_id, cmd) core.event.register(event_id, cmd)
else: else:
core.event.register(event_id, lambda _: __execute(cmd)) core.event.register(event_id, lambda _: __execute(cmd))
def trigger(event): def trigger(event):
if not 'button' in event: return if not "button" in event:
return
triggered = False triggered = False
for field in ['instance', 'name']: for field in ["instance", "name"]:
if not field in event: continue if not field in event:
if core.event.trigger(__event_id(event[field], event['button']), event): continue
if core.event.trigger(__event_id(event[field], event["button"]), event):
triggered = True triggered = True
if not triggered: if not triggered:
core.event.trigger(__event_id('', event['button']), event) core.event.trigger(__event_id("", event["button"]), event)
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -7,35 +7,41 @@ import core.widget
import core.decorators import core.decorators
try: try:
error = ModuleNotFoundError('') error = ModuleNotFoundError("")
except Exception as e: except Exception as e:
ModuleNotFoundError = Exception ModuleNotFoundError = Exception
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
def load(module_name, config=core.config.Config([]), theme=None): def load(module_name, config=core.config.Config([]), theme=None):
error = None error = None
module_short, alias = (module_name.split(':') + [module_name])[0:2] module_short, alias = (module_name.split(":") + [module_name])[0:2]
config.set('__alias__', alias) config.set("__alias__", alias)
for namespace in [ 'core', 'contrib' ]: for namespace in ["core", "contrib"]:
try: try:
mod = importlib.import_module('modules.{}.{}'.format(namespace, module_short)) mod = importlib.import_module(
log.debug('importing {} from {}.{}'.format(module_short, namespace, module_short)) "modules.{}.{}".format(namespace, module_short)
return getattr(mod, 'Module')(config, theme) )
log.debug(
"importing {} from {}.{}".format(module_short, namespace, module_short)
)
return getattr(mod, "Module")(config, theme)
except ImportError as e: except ImportError as e:
error = e error = e
log.fatal('failed to import {}: {}'.format(module_name, error)) log.fatal("failed to import {}: {}".format(module_name, error))
return Error(config=config, module=module_name, error=error) return Error(config=config, module=module_name, error=error)
class Module(core.input.Object): class Module(core.input.Object):
def __init__(self, config=core.config.Config([]), theme=None, widgets=[]): def __init__(self, config=core.config.Config([]), theme=None, widgets=[]):
super().__init__() super().__init__()
self.__config = config self.__config = config
self.__widgets = widgets if isinstance(widgets, list) else [widgets] self.__widgets = widgets if isinstance(widgets, list) else [widgets]
self.module_name = self.__module__.split('.')[-1] self.module_name = self.__module__.split(".")[-1]
self.name = self.module_name self.name = self.module_name
self.alias = self.__config.get('__alias__', None) self.alias = self.__config.get("__alias__", None)
self.id = self.alias if self.alias else self.name self.id = self.alias if self.alias else self.name
self.next_update = None self.next_update = None
@ -51,12 +57,12 @@ class Module(core.input.Object):
value = default value = default
for prefix in [self.name, self.module_name, self.alias]: for prefix in [self.name, self.module_name, self.alias]:
value = self.__config.get('{}.{}'.format(prefix, key), value) value = self.__config.get("{}.{}".format(prefix, key), value)
# TODO retrieve from config file # TODO retrieve from config file
return value return value
def set(self, key, value): def set(self, key, value):
self.__config.set('{}.{}'.format(self.name, key), value) self.__config.set("{}.{}".format(self.name, key), value)
def update(self): def update(self):
pass pass
@ -65,8 +71,8 @@ class Module(core.input.Object):
try: try:
self.update() self.update()
except Exception as e: except Exception as e:
self.set('interval', 1) self.set("interval", 1)
module = Error(config=self.__config, module='error', error=str(e)) module = Error(config=self.__config, module="error", error=str(e))
self.__widgets = [module.widget()] self.__widgets = [module.widget()]
self.update = module.update self.update = module.update
@ -75,28 +81,31 @@ class Module(core.input.Object):
self.__widgets = widgets self.__widgets = widgets
return self.__widgets return self.__widgets
def add_widget(self, full_text='', name=None): def add_widget(self, full_text="", name=None):
widget = core.widget.Widget(full_text=full_text, name=name, module=self) widget = core.widget.Widget(full_text=full_text, name=name, module=self)
self.widgets().append(widget) self.widgets().append(widget)
return widget return widget
def widget(self, name=None): def widget(self, name=None):
if not name: return self.widgets()[0] if not name:
return self.widgets()[0]
for w in self.widgets(): for w in self.widgets():
if w.name == name: return w if w.name == name:
return w
return None return None
def state(self, widget): def state(self, widget):
return [] return []
def threshold_state(self, value, warn, crit): def threshold_state(self, value, warn, crit):
if value > float(self.parameter('critical', crit)): if value > float(self.parameter("critical", crit)):
return 'critical' return "critical"
if value > float(self.parameter('warning', warn)): if value > float(self.parameter("warning", warn)):
return 'warning' return "warning"
return None return None
class Error(Module): class Error(Module):
def __init__(self, module, error, config=core.config.Config([]), theme=None): def __init__(self, module, error, config=core.config.Config([]), theme=None):
super().__init__(config, theme, core.widget.Widget(self.full_text)) super().__init__(config, theme, core.widget.Widget(self.full_text))
@ -104,9 +113,10 @@ class Error(Module):
self.__error = error self.__error = error
def full_text(self, widget): def full_text(self, widget):
return '{}: {}'.format(self.__module, self.__error) return "{}: {}".format(self.__module, self.__error)
def state(self, widget): def state(self, widget):
return ['critical'] return ["critical"]
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -7,15 +7,17 @@ import core.event
import util.format import util.format
def dump_json(obj): def dump_json(obj):
return obj.dict() return obj.dict()
def assign(src, dst, key, src_key=None, default=None): def assign(src, dst, key, src_key=None, default=None):
if not src_key: if not src_key:
if key.startswith('_'): if key.startswith("_"):
src_key = key src_key = key
else: else:
src_key = key.replace('_', '-') # automagically replace _ with - src_key = key.replace("_", "-") # automagically replace _ with -
for k in src_key if isinstance(src_key, list) else [src_key]: for k in src_key if isinstance(src_key, list) else [src_key]:
if k in src: if k in src:
@ -24,12 +26,23 @@ def assign(src, dst, key, src_key=None, default=None):
if default is not None: if default is not None:
dst[key] = default dst[key] = default
class block(object): class block(object):
__COMMON_THEME_FIELDS = [ __COMMON_THEME_FIELDS = [
'separator', 'separator-block-width', 'default-separators', "separator",
'border-top', 'border-left', 'border-right', 'border-bottom', "separator-block-width",
'fg', 'bg', 'padding', 'prefix', 'suffix' "default-separators",
"border-top",
"border-left",
"border-right",
"border-bottom",
"fg",
"bg",
"padding",
"prefix",
"suffix",
] ]
def __init__(self, theme, module, widget): def __init__(self, theme, module, widget):
self.__attributes = {} self.__attributes = {}
for key in self.__COMMON_THEME_FIELDS: for key in self.__COMMON_THEME_FIELDS:
@ -37,72 +50,85 @@ class block(object):
if tmp is not None: if tmp is not None:
self.__attributes[key] = tmp self.__attributes[key] = tmp
self.__attributes['name'] = module.id self.__attributes["name"] = module.id
self.__attributes['instance'] = widget.id self.__attributes["instance"] = widget.id
self.__attributes['prev-bg'] = theme.get('bg', 'previous') self.__attributes["prev-bg"] = theme.get("bg", "previous")
def set(self, key, value): def set(self, key, value):
self.__attributes[key] = value self.__attributes[key] = value
def is_pango(self, attr): def is_pango(self, attr):
if isinstance(attr, dict) and 'pango' in attr: if isinstance(attr, dict) and "pango" in attr:
return True return True
return False return False
def pangoize(self, text): def pangoize(self, text):
if not self.is_pango(text): if not self.is_pango(text):
return text return text
self.__attributes['markup'] = 'pango' self.__attributes["markup"] = "pango"
attr = dict(text['pango']) attr = dict(text["pango"])
text = attr.get('full_text', '') text = attr.get("full_text", "")
if 'full_text' in attr: if "full_text" in attr:
del attr['full_text'] del attr["full_text"]
result = '<span' result = "<span"
for key, value in attr.items(): for key, value in attr.items():
result = '{} {}="{}"'.format(result, key, value) result = '{} {}="{}"'.format(result, key, value)
result = '{}>{}</span>'.format(result, text) result = "{}>{}</span>".format(result, text)
return result return result
def dict(self): def dict(self):
result = {} result = {}
assign(self.__attributes, result, 'full_text', ['full_text', 'separator']) assign(self.__attributes, result, "full_text", ["full_text", "separator"])
assign(self.__attributes, result, 'separator', 'default-separators') assign(self.__attributes, result, "separator", "default-separators")
if '_decorator' in self.__attributes: if "_decorator" in self.__attributes:
assign(self.__attributes, result, 'color', 'bg') assign(self.__attributes, result, "color", "bg")
assign(self.__attributes, result, 'background', 'prev-bg') assign(self.__attributes, result, "background", "prev-bg")
result['_decorator'] = True result["_decorator"] = True
else: else:
assign(self.__attributes, result, 'color', 'fg') assign(self.__attributes, result, "color", "fg")
assign(self.__attributes, result, 'background', 'bg') assign(self.__attributes, result, "background", "bg")
if 'full_text' in self.__attributes: if "full_text" in self.__attributes:
result['full_text'] = self.pangoize(result['full_text']) result["full_text"] = self.pangoize(result["full_text"])
result['full_text'] = self.__format(self.__attributes['full_text']) result["full_text"] = self.__format(self.__attributes["full_text"])
for k in [ for k in [
'name', 'instance', 'separator_block_width', 'border', 'border_top', "name",
'border_bottom', 'border_left', 'border_right', 'markup', "instance",
'_raw', '_suffix', '_prefix', 'min_width' "separator_block_width",
"border",
"border_top",
"border_bottom",
"border_left",
"border_right",
"markup",
"_raw",
"_suffix",
"_prefix",
"min_width",
]: ]:
assign(self.__attributes, result, k) assign(self.__attributes, result, k)
return result return result
def __pad(self, text): def __pad(self, text):
padding = self.__attributes.get('padding', '') padding = self.__attributes.get("padding", "")
if not text: return padding if not text:
return '{}{}{}'.format(padding, text, padding) return padding
return "{}{}{}".format(padding, text, padding)
def __format(self, text): def __format(self, text):
if text is None: return None if text is None:
prefix = self.__pad(self.pangoize(self.__attributes.get('prefix'))) return None
suffix = self.__pad(self.pangoize(self.__attributes.get('suffix'))) prefix = self.__pad(self.pangoize(self.__attributes.get("prefix")))
self.set('_prefix', prefix) suffix = self.__pad(self.pangoize(self.__attributes.get("suffix")))
self.set('_suffix', suffix) self.set("_prefix", prefix)
self.set('_raw', text) self.set("_suffix", suffix)
return '{}{}{}'.format(prefix, text, suffix) self.set("_raw", text)
return "{}{}{}".format(prefix, text, suffix)
class i3(object): class i3(object):
def __init__(self, theme=core.theme.Theme(), config=core.config.Config([])): def __init__(self, theme=core.theme.Theme(), config=core.config.Config([])):
@ -110,10 +136,10 @@ class i3(object):
self.__content = {} self.__content = {}
self.__theme = theme self.__theme = theme
self.__config = config self.__config = config
core.event.register('update', self.update) core.event.register("update", self.update)
core.event.register('start', self.draw, 'start') core.event.register("start", self.draw, "start")
core.event.register('draw', self.draw, 'statusline') core.event.register("draw", self.draw, "statusline")
core.event.register('stop', self.draw, 'stop') core.event.register("stop", self.draw, "stop")
def theme(self, new_theme=None): def theme(self, new_theme=None):
if new_theme: if new_theme:
@ -128,58 +154,60 @@ class i3(object):
def draw(self, what, args=None): def draw(self, what, args=None):
cb = getattr(self, what) cb = getattr(self, what)
data = cb(args) if args else cb() data = cb(args) if args else cb()
if 'blocks' in data: if "blocks" in data:
sys.stdout.write(json.dumps(data['blocks'], default=dump_json)) sys.stdout.write(json.dumps(data["blocks"], default=dump_json))
if 'suffix' in data: if "suffix" in data:
sys.stdout.write(data['suffix']) sys.stdout.write(data["suffix"])
sys.stdout.write('\n') sys.stdout.write("\n")
sys.stdout.flush() sys.stdout.flush()
def start(self): def start(self):
return { return {
'blocks': { 'version': 1, 'click_events': True }, "blocks": {"version": 1, "click_events": True},
'suffix': '\n[', "suffix": "\n[",
} }
def stop(self): def stop(self):
return { 'suffix': '\n]' } return {"suffix": "\n]"}
def __separator_block(self, module, widget): def __separator_block(self, module, widget):
if not self.__theme.get('separator'): if not self.__theme.get("separator"):
return [] return []
blk = block(self.__theme, module, widget) blk = block(self.__theme, module, widget)
blk.set('_decorator', True) blk.set("_decorator", True)
return [blk] return [blk]
def __content_block(self, module, widget): def __content_block(self, module, widget):
blk = block(self.__theme, module, widget) blk = block(self.__theme, module, widget)
minwidth = widget.theme('minwidth') minwidth = widget.theme("minwidth")
if minwidth is not None: if minwidth is not None:
try: try:
blk.set('min-width', '-'*int(minwidth)) blk.set("min-width", "-" * int(minwidth))
except: except:
blk.set('min-width', minwidth) blk.set("min-width", minwidth)
blk.set('full_text', self.__content[widget]) blk.set("full_text", self.__content[widget])
if widget.get('pango', False): if widget.get("pango", False):
blk.set('markup', 'pango') blk.set("markup", "pango")
if self.__config.debug(): if self.__config.debug():
state = module.state(widget) state = module.state(widget)
if isinstance(state, list): if isinstance(state, list):
state = ', '.join(state) state = ", ".join(state)
blk.set('__state', state) blk.set("__state", state)
return blk return blk
def blocks(self, module): def blocks(self, module):
blocks = [] blocks = []
for widget in module.widgets(): for widget in module.widgets():
if widget.module and self.__config.autohide(widget.module.name): if widget.module and self.__config.autohide(widget.module.name):
if not any(state in widget.state() for state in [ 'warning', 'critical']): if not any(
state in widget.state() for state in ["warning", "critical"]
):
continue continue
if module.hidden(): if module.hidden():
continue continue
blocks.extend(self.__separator_block(module, widget)) blocks.extend(self.__separator_block(module, widget))
blocks.append(self.__content_block(module, widget)) blocks.append(self.__content_block(module, widget))
core.event.trigger('next-widget') core.event.trigger("next-widget")
return blocks return blocks
# TODO: only updates full text, not the state!? # TODO: only updates full text, not the state!?
@ -189,14 +217,16 @@ class i3(object):
if affected_modules and not module.id in affected_modules: if affected_modules and not module.id in affected_modules:
continue continue
if not affected_modules and module.next_update: if not affected_modules and module.next_update:
if module.parameter('interval', '') == 'never': if module.parameter("interval", "") == "never":
continue continue
if now < module.next_update: if now < module.next_update:
continue continue
if not redraw_only: if not redraw_only:
module.update_wrapper() module.update_wrapper()
if module.parameter('interval', '') != 'never': if module.parameter("interval", "") != "never":
module.next_update = now + util.format.seconds(module.parameter('interval', self.__config.interval())) module.next_update = now + util.format.seconds(
module.parameter("interval", self.__config.interval())
)
for widget in module.widgets(): for widget in module.widgets():
self.__content[widget] = widget.full_text() self.__content[widget] = widget.full_text()
@ -204,12 +234,10 @@ class i3(object):
blocks = [] blocks = []
for module in self.__modules: for module in self.__modules:
blocks.extend(self.blocks(module)) blocks.extend(self.blocks(module))
return { return {"blocks": blocks, "suffix": ","}
'blocks': blocks,
'suffix': ','
}
def wait(self, interval): def wait(self, interval):
time.sleep(interval) time.sleep(interval)
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -12,22 +12,24 @@ log = logging.getLogger(__name__)
THEME_BASE_DIR = os.path.dirname(os.path.realpath(__file__)) THEME_BASE_DIR = os.path.dirname(os.path.realpath(__file__))
PATHS = [ PATHS = [
'.', ".",
os.path.join(THEME_BASE_DIR, '../themes'), os.path.join(THEME_BASE_DIR, "../themes"),
os.path.expanduser('~/.config/bumblebee-status/themes'), os.path.expanduser("~/.config/bumblebee-status/themes"),
] ]
def themes(): def themes():
themes_dict = {} themes_dict = {}
for path in PATHS: for path in PATHS:
for filename in glob.iglob('{}/*.json'.format(path)): for filename in glob.iglob("{}/*.json".format(path)):
if 'test' not in filename: if "test" not in filename:
themes_dict[os.path.basename(filename).replace('.json', '')] = 1 themes_dict[os.path.basename(filename).replace(".json", "")] = 1
result = list(themes_dict.keys()) result = list(themes_dict.keys())
result.sort() result.sort()
return result return result
def merge_replace(value, new_value, key): def merge_replace(value, new_value, key):
if not isinstance(value, dict): if not isinstance(value, dict):
return new_value return new_value
@ -35,12 +37,13 @@ def merge_replace(value, new_value, key):
util.algorithm.merge(value, new_value) util.algorithm.merge(value, new_value)
return value return value
# right now, merging needs explicit pango support :( # right now, merging needs explicit pango support :(
if 'pango' in value: if "pango" in value:
value['pango']['full_text'] = new_value value["pango"]["full_text"] = new_value
return value return value
class Theme(object): class Theme(object):
def __init__(self, name='default', iconset='auto', raw_data=None): def __init__(self, name="default", iconset="auto", raw_data=None):
self.name = name self.name = name
self.__widget_count = 0 self.__widget_count = 0
self.__previous = {} self.__previous = {}
@ -48,15 +51,15 @@ class Theme(object):
self.__keywords = {} self.__keywords = {}
self.__value_idx = {} self.__value_idx = {}
self.__data = raw_data if raw_data else self.load(name) self.__data = raw_data if raw_data else self.load(name)
for icons in self.__data.get('icons', []): for icons in self.__data.get("icons", []):
util.algorithm.merge(self.__data, self.load(icons, 'icons')) util.algorithm.merge(self.__data, self.load(icons, "icons"))
if iconset != 'auto': if iconset != "auto":
util.algorithm.merge(self.__data, self.load(iconset, 'icons')) util.algorithm.merge(self.__data, self.load(iconset, "icons"))
for colors in self.__data.get('colors', []): for colors in self.__data.get("colors", []):
util.algorithm.merge(self.__keywords, self.load_keywords(colors)) util.algorithm.merge(self.__keywords, self.load_keywords(colors))
core.event.register('draw', self.__start) core.event.register("draw", self.__start)
core.event.register('next-widget', self.__next_widget) core.event.register("next-widget", self.__next_widget)
def keywords(self): def keywords(self):
return self.__keywords return self.__keywords
@ -64,17 +67,20 @@ class Theme(object):
def color(self, name, default=None): def color(self, name, default=None):
return self.keywords().get(name, default) return self.keywords().get(name, default)
def load(self, name, subdir=''): def load(self, name, subdir=""):
if isinstance(name, dict): return name # support plain data if isinstance(name, dict):
return name # support plain data
for path in PATHS: for path in PATHS:
theme_file = os.path.join(path, subdir, '{}.json'.format(name)) theme_file = os.path.join(path, subdir, "{}.json".format(name))
result = self.__load_json(theme_file) result = self.__load_json(theme_file)
if result != {}: return result if result != {}:
raise RuntimeError('unable to find theme {}'.format(name)) return result
raise RuntimeError("unable to find theme {}".format(name))
def __load_json(self, filename): def __load_json(self, filename):
filename = os.path.expanduser(filename) filename = os.path.expanduser(filename)
if not os.path.isfile(filename): return {} if not os.path.isfile(filename):
return {}
with io.open(filename) as data: with io.open(filename) as data:
return json.load(data) return json.load(data)
@ -82,15 +88,15 @@ class Theme(object):
try: try:
if isinstance(name, dict): if isinstance(name, dict):
return name return name
if name.lower() == 'wal': if name.lower() == "wal":
wal = self.__load_json('~/.cache/wal/colors.json') wal = self.__load_json("~/.cache/wal/colors.json")
result = {} result = {}
for field in ['special', 'colors']: for field in ["special", "colors"]:
for key in wal.get(field, {}): for key in wal.get(field, {}):
result[key] = wal[field][key] result[key] = wal[field][key]
return result return result
except Exception as e: except Exception as e:
log.error('failed to load colors: {}', e) log.error("failed to load colors: {}", e)
def __start(self): def __start(self):
self.__widget_count = 0 self.__widget_count = 0
@ -107,14 +113,14 @@ class Theme(object):
def get(self, key, widget=None, default=None): def get(self, key, widget=None, default=None):
if not widget: if not widget:
widget = core.widget.Widget('') widget = core.widget.Widget("")
# special handling # special handling
if widget == 'previous': if widget == "previous":
return self.__previous.get(key, None) return self.__previous.get(key, None)
value = default value = default
for option in ['defaults', 'cycle']: for option in ["defaults", "cycle"]:
if option in self.__data: if option in self.__data:
tmp = self.__data[option] tmp = self.__data[option]
if isinstance(tmp, list): if isinstance(tmp, list):
@ -127,7 +133,9 @@ class Theme(object):
value = merge_replace(value, self.__data.get(key, value), key) value = merge_replace(value, self.__data.get(key, value), key)
if widget.module: if widget.module:
value = merge_replace(value, self.get(widget.module.name, None, {}).get(key, value), key) value = merge_replace(
value, self.get(widget.module.name, None, {}).get(key, value), key
)
if not key in widget.state(): if not key in widget.state():
for state in widget.state(): for state in widget.state():
@ -138,11 +146,12 @@ class Theme(object):
value = self.__keywords.get(value, value) value = self.__keywords.get(value, value)
if isinstance(value, list): if isinstance(value, list):
idx = self.__value_idx.get('{}::{}'.format(widget.id, key), 0) % len(value) idx = self.__value_idx.get("{}::{}".format(widget.id, key), 0) % len(value)
self.__value_idx['{}::{}'.format(widget.id, key)] = idx self.__value_idx["{}::{}".format(widget.id, key)] = idx
widget.set(key, idx) widget.set(key, idx)
value = value[idx] value = value[idx]
self.__current[key] = value self.__current[key] = value
return value return value
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -4,8 +4,9 @@ import core.decorators
import util.store import util.store
import util.format import util.format
class Widget(util.store.Store, core.input.Object): class Widget(util.store.Store, core.input.Object):
def __init__(self, full_text='', name=None, module=None): def __init__(self, full_text="", name=None, module=None):
super(Widget, self).__init__() super(Widget, self).__init__()
self.__full_text = full_text self.__full_text = full_text
self.module = module self.module = module
@ -19,15 +20,17 @@ class Widget(util.store.Store, core.input.Object):
def module(self, module): def module(self, module):
self.__module = module self.__module = module
if self.index() < 0: return if self.index() < 0:
return
if module: if module:
custom_ids = util.format.aslist(module.parameter('id')) custom_ids = util.format.aslist(module.parameter("id"))
if len(custom_ids) > self.index(): if len(custom_ids) > self.index():
self.id = custom_ids[self.index()] self.id = custom_ids[self.index()]
def index(self): def index(self):
if not self.module: return 0 if not self.module:
return 0
idx = 0 idx = 0
for w in self.module.widgets(): for w in self.module.widgets():
@ -37,7 +40,7 @@ class Widget(util.store.Store, core.input.Object):
return -1 # not found return -1 # not found
def theme(self, attribute): def theme(self, attribute):
attr = 'theme.{}'.format(attribute) attr = "theme.{}".format(attribute)
if self.module: if self.module:
param = util.format.aslist(self.module.parameter(attr)) param = util.format.aslist(self.module.parameter(attr))
if param and len(param) > self.index(): if param and len(param) > self.index():
@ -54,12 +57,13 @@ class Widget(util.store.Store, core.input.Object):
def state(self): def state(self):
rv = [] rv = []
if self.get('state', None): if self.get("state", None):
tmp = self.get('state') tmp = self.get("state")
rv = tmp[:] if isinstance(tmp, list) else [tmp] rv = tmp[:] if isinstance(tmp, list) else [tmp]
if self.module: if self.module:
tmp = self.module.state(self) tmp = self.module.state(self)
rv.extend(tmp if isinstance(tmp, list) else [tmp]) rv.extend(tmp if isinstance(tmp, list) else [tmp])
return rv return rv
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -10,36 +10,40 @@ import core.widget
import util.cli import util.cli
class Module(core.module.Module): class Module(core.module.Module):
def __init__(self, config, theme): def __init__(self, config, theme):
super().__init__(config, theme, core.widget.Widget(self.volume)) super().__init__(config, theme, core.widget.Widget(self.volume))
self.__level = 'n/a' self.__level = "n/a"
self.__muted = True self.__muted = True
device = self.parameter('device', 'Master,0') device = self.parameter("device", "Master,0")
self._cmdString = 'amixer get {}'.format(device) self._cmdString = "amixer get {}".format(device)
def volume(self, widget): def volume(self, widget):
if self.__level == 'n/a': if self.__level == "n/a":
return self.__level return self.__level
m = re.search(r'([\d]+)\%', self.__level) m = re.search(r"([\d]+)\%", self.__level)
self.__muted = True self.__muted = True
if m: if m:
if m.group(1) != '0' and '[on]' in self.__level: if m.group(1) != "0" and "[on]" in self.__level:
self.__muted = False self.__muted = False
return '{}%'.format(m.group(1)) return "{}%".format(m.group(1))
else: else:
return '0%' return "0%"
def update(self): def update(self):
try: try:
self.__level = util.cli.execute('amixer get {}'.format(self.parameter('device', 'Master,0'))) self.__level = util.cli.execute(
"amixer get {}".format(self.parameter("device", "Master,0"))
)
except Exception as e: except Exception as e:
self.__level = 'n/a' self.__level = "n/a"
def state(self, widget): def state(self, widget):
if self.__muted: if self.__muted:
return ['warning', 'muted'] return ["warning", "muted"]
return ['unmuted'] return ["unmuted"]
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -15,34 +15,39 @@ import core.decorators
import util.cli import util.cli
PATTERN = '{} packages upgraded, {} newly installed, {} to remove and {} not upgraded.' PATTERN = "{} packages upgraded, {} newly installed, {} to remove and {} not upgraded."
def parse_result(to_parse): def parse_result(to_parse):
# We want to line with the iforamtion about package upgrade # We want to line with the iforamtion about package upgrade
line_to_parse = to_parse.split('\n')[-4] line_to_parse = to_parse.split("\n")[-4]
result = re.search('(.+) packages upgraded, (.+) newly installed, (.+) to remove', line_to_parse) result = re.search(
"(.+) packages upgraded, (.+) newly installed, (.+) to remove", line_to_parse
)
return int(result.group(1)), int(result.group(3)) return int(result.group(1)), int(result.group(3))
def get_apt_check_info(module): def get_apt_check_info(module):
widget = module.widget() widget = module.widget()
try: try:
res = util.cli.execute('aptitude full-upgrade --simulate --assume-yes') res = util.cli.execute("aptitude full-upgrade --simulate --assume-yes")
widget.set('error', None) widget.set("error", None)
except (RuntimeError, FileNotFoundError) as e: except (RuntimeError, FileNotFoundError) as e:
widget.set('error', 'unable to query APT: {}'.format(e)) widget.set("error", "unable to query APT: {}".format(e))
return return
to_upgrade = 0 to_upgrade = 0
to_remove = 0 to_remove = 0
try: try:
to_upgrade, to_remove = parse_result(res) to_upgrade, to_remove = parse_result(res)
widget.set('to_upgrade', to_upgrade) widget.set("to_upgrade", to_upgrade)
widget.set('to_remove', to_remove) widget.set("to_remove", to_remove)
except Exception as e: except Exception as e:
widget.set('error', 'parse error: {}'.format(e)) widget.set("error", "parse error: {}".format(e))
core.event.trigger("update", [module.id], redraw_only=True)
core.event.trigger('update', [ module.id ], redraw_only=True)
class Module(core.module.Module): class Module(core.module.Module):
@core.decorators.every(minutes=30) @core.decorators.every(minutes=30)
@ -51,30 +56,32 @@ class Module(core.module.Module):
self.__thread = None self.__thread = None
def updates(self, widget): def updates(self, widget):
if widget.get('error'): if widget.get("error"):
return widget.get('error') return widget.get("error")
return '{} to upgrade, {} to remove'.format( return "{} to upgrade, {} to remove".format(
widget.get('to_upgrade', 0), widget.get('to_remove', 0) widget.get("to_upgrade", 0), widget.get("to_remove", 0)
) )
def update(self): def update(self):
if self.__thread and self.__thread.isAlive(): return if self.__thread and self.__thread.isAlive():
return
self.__thread = threading.Thread(target=get_apt_check_info, args=(self,)) self.__thread = threading.Thread(target=get_apt_check_info, args=(self,))
self.__thread.start() self.__thread.start()
def state(self, widget): def state(self, widget):
cnt = 0 cnt = 0
ret = 'good' ret = "good"
for t in ['to_upgrade', 'to_remove']: for t in ["to_upgrade", "to_remove"]:
cnt += widget.get(t, 0) cnt += widget.get(t, 0)
if cnt > 50: if cnt > 50:
ret = 'critical' ret = "critical"
elif cnt > 0: elif cnt > 0:
ret = 'warning' ret = "warning"
if widget.get('error'): if widget.get("error"):
ret = 'critical' ret = "critical"
return ret return ret
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -10,6 +10,7 @@ import core.decorators
import util.cli import util.cli
class Module(core.module.Module): class Module(core.module.Module):
@core.decorators.every(minutes=60) @core.decorators.every(minutes=60)
def __init__(self, config, theme): def __init__(self, config, theme):
@ -18,7 +19,7 @@ class Module(core.module.Module):
@property @property
def __format(self): def __format(self):
return self.parameter('format', 'Update Arch: {}') return self.parameter("format", "Update Arch: {}")
def utilization(self, widget): def utilization(self, widget):
return self.__format.format(self.__packages) return self.__format.format(self.__packages)
@ -27,10 +28,11 @@ class Module(core.module.Module):
return self.__packages == 0 return self.__packages == 0
def update(self): def update(self):
result = util.cli.execute('checkupdates') result = util.cli.execute("checkupdates")
self.__packages = len(result.split('\n')) - 1 self.__packages = len(result.split("\n")) - 1
def state(self, widget): def state(self, widget):
return self.threshold_state(self.__packages, 1, 100) return self.threshold_state(self.__packages, 1, 100)
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -23,30 +23,27 @@ import util.cli
import util.format import util.format
import util.popup import util.popup
class Module(core.module.Module): class Module(core.module.Module):
def __init__(self, config, theme): def __init__(self, config, theme):
super().__init__(config, theme, core.widget.Widget(self.status)) super().__init__(config, theme, core.widget.Widget(self.status))
device = self.parameter('device', 'hci0') device = self.parameter("device", "hci0")
self.manager = self.parameter('manager', 'blueman-manager') self.manager = self.parameter("manager", "blueman-manager")
self._path = '/sys/class/bluetooth/{}'.format(device) self._path = "/sys/class/bluetooth/{}".format(device)
self._status = 'Off' self._status = "Off"
core.input.register(self, button=core.input.LEFT_MOUSE, core.input.register(self, button=core.input.LEFT_MOUSE, cmd=self.manager)
cmd=self.manager)
# determine whether to use pop-up menu or simply toggle the device on/off # determine whether to use pop-up menu or simply toggle the device on/off
right_click_popup = util.format.asbool( right_click_popup = util.format.asbool(
self.parameter('right_click_popup', True)) self.parameter("right_click_popup", True)
)
if right_click_popup: if right_click_popup:
core.input.register(self, core.input.register(self, button=core.input.RIGHT_MOUSE, cmd=self.popup)
button=core.input.RIGHT_MOUSE,
cmd=self.popup)
else: else:
core.input.register(self, core.input.register(self, button=core.input.RIGHT_MOUSE, cmd=self._toggle)
button=core.input.RIGHT_MOUSE,
cmd=self._toggle)
def status(self, widget): def status(self, widget):
"""Get status.""" """Get status."""
@ -55,35 +52,33 @@ class Module(core.module.Module):
def update(self): def update(self):
"""Update current state.""" """Update current state."""
if not os.path.exists(self._path): if not os.path.exists(self._path):
self._status = '?' self._status = "?"
return return
# search for whichever rfkill directory available # search for whichever rfkill directory available
try: try:
dirnames = next(os.walk(self._path))[1] dirnames = next(os.walk(self._path))[1]
for dirname in dirnames: for dirname in dirnames:
m = re.match(r'rfkill[0-9]+', dirname) m = re.match(r"rfkill[0-9]+", dirname)
if m is not None: if m is not None:
with open(os.path.join(self._path, with open(os.path.join(self._path, dirname, "state"), "r") as f:
dirname,
'state'), 'r') as f:
state = int(f.read()) state = int(f.read())
if state == 1: if state == 1:
self._status = 'On' self._status = "On"
else: else:
self._status = 'Off' self._status = "Off"
return return
except IOError: except IOError:
self._status = '?' self._status = "?"
def popup(self, widget): def popup(self, widget):
"""Show a popup menu.""" """Show a popup menu."""
menu = util.popup.PopupMenu() menu = util.popup.PopupMenu()
if self._status == 'On': if self._status == "On":
menu.add_menuitem('Disable Bluetooth') menu.add_menuitem("Disable Bluetooth")
elif self._status == 'Off': elif self._status == "Off":
menu.add_menuitem('Enable Bluetooth') menu.add_menuitem("Enable Bluetooth")
else: else:
return return
@ -95,32 +90,35 @@ class Module(core.module.Module):
def _toggle(self, widget=None): def _toggle(self, widget=None):
"""Toggle bluetooth state.""" """Toggle bluetooth state."""
if self._status == 'On': if self._status == "On":
state = 'false' state = "false"
else: else:
state = 'true' state = "true"
dst = self.parameter('dbus_destination', 'org.blueman.Mechanism') dst = self.parameter("dbus_destination", "org.blueman.Mechanism")
dst_path = self.parameter('dbus_destination_path', '/') dst_path = self.parameter("dbus_destination_path", "/")
cmd = 'dbus-send --system --print-reply --dest={}'\ cmd = (
' {} org.blueman.Mechanism.SetRfkillState'\ "dbus-send --system --print-reply --dest={}"
' boolean:{}'.format(dst, dst_path, state) " {} org.blueman.Mechanism.SetRfkillState"
" boolean:{}".format(dst, dst_path, state)
)
logging.debug('bt: toggling bluetooth') logging.debug("bt: toggling bluetooth")
util.cli.execute(cmd) util.cli.execute(cmd)
def state(self, widget): def state(self, widget):
"""Get current state.""" """Get current state."""
state = [] state = []
if self._status == '?': if self._status == "?":
state = ['unknown'] state = ["unknown"]
elif self._status == 'On': elif self._status == "On":
state = ['ON'] state = ["ON"]
else: else:
state = ['OFF'] state = ["OFF"]
return state return state
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -20,19 +20,18 @@ import core.input
import util.cli import util.cli
class Module(core.module.Module): class Module(core.module.Module):
def __init__(self, config, theme): def __init__(self, config, theme):
super().__init__(config, theme, core.widget.Widget(self.status)) super().__init__(config, theme, core.widget.Widget(self.status))
self.manager = self.parameter('manager', 'blueman-manager') self.manager = self.parameter("manager", "blueman-manager")
self._status = 'Off' self._status = "Off"
dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
self._bus = dbus.SystemBus() self._bus = dbus.SystemBus()
core.input.register(self, button=core.input.LEFT_MOUSE, core.input.register(self, button=core.input.LEFT_MOUSE, cmd=self.manager)
cmd=self.manager) core.input.register(self, button=core.input.RIGHT_MOUSE, cmd=self._toggle)
core.input.register(self, button=core.input.RIGHT_MOUSE,
cmd=self._toggle)
def status(self, widget): def status(self, widget):
"""Get status.""" """Get status."""
@ -40,58 +39,63 @@ class Module(core.module.Module):
def update(self): def update(self):
"""Update current state.""" """Update current state."""
state = len(subprocess.run(['bluetoothctl', 'list'], stdout=subprocess.PIPE).stdout) state = len(
subprocess.run(["bluetoothctl", "list"], stdout=subprocess.PIPE).stdout
)
if state > 0: if state > 0:
connected_devices = self.get_connected_devices() connected_devices = self.get_connected_devices()
self._status = 'On - {}'.format(connected_devices) self._status = "On - {}".format(connected_devices)
else: else:
self._status = 'Off' self._status = "Off"
adapters_cmd = 'rfkill list | grep Bluetooth' adapters_cmd = "rfkill list | grep Bluetooth"
if not len(subprocess.run(adapters_cmd, shell=True, stdout=subprocess.PIPE).stdout): if not len(
self._status = 'No Adapter Found' subprocess.run(adapters_cmd, shell=True, stdout=subprocess.PIPE).stdout
):
self._status = "No Adapter Found"
return return
def _toggle(self, widget=None): def _toggle(self, widget=None):
"""Toggle bluetooth state.""" """Toggle bluetooth state."""
if 'On' in self._status: if "On" in self._status:
state = 'false' state = "false"
else: else:
state = 'true' state = "true"
cmd = 'dbus-send --system --print-reply --dest=org.blueman.Mechanism /org/blueman/mechanism org.blueman.Mechanism.SetRfkillState boolean:%s' % state cmd = (
"dbus-send --system --print-reply --dest=org.blueman.Mechanism /org/blueman/mechanism org.blueman.Mechanism.SetRfkillState boolean:%s"
% state
)
logging.debug('bt: toggling bluetooth') logging.debug("bt: toggling bluetooth")
core.util.execute(cmd) core.util.execute(cmd)
def state(self, widget): def state(self, widget):
"""Get current state.""" """Get current state."""
state = [] state = []
if self._status == 'No Adapter Found': if self._status == "No Adapter Found":
state.append('critical') state.append("critical")
elif self._status == 'On - 0': elif self._status == "On - 0":
state.append('warning') state.append("warning")
elif 'On' in self._status and not(self._status == 'On - 0'): elif "On" in self._status and not (self._status == "On - 0"):
state.append('ON') state.append("ON")
else: else:
state.append('critical') state.append("critical")
return state return state
def get_connected_devices(self): def get_connected_devices(self):
devices = 0 devices = 0
objects = dbus.Interface( objects = dbus.Interface(
self._bus.get_object('org.bluez', '/'), self._bus.get_object("org.bluez", "/"), "org.freedesktop.DBus.ObjectManager"
'org.freedesktop.DBus.ObjectManager'
).GetManagedObjects() ).GetManagedObjects()
for path, interfaces in objects.items(): for path, interfaces in objects.items():
if 'org.bluez.Device1' in interfaces: if "org.bluez.Device1" in interfaces:
if dbus.Interface( if dbus.Interface(
self._bus.get_object('org.bluez', path), self._bus.get_object("org.bluez", path),
'org.freedesktop.DBus.Properties' "org.freedesktop.DBus.Properties",
).Get( ).Get("org.bluez.Device1", "Connected"):
'org.bluez.Device1', 'Connected'
):
devices += 1 devices += 1
return devices return devices
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -21,10 +21,11 @@ import core.decorators
import util.cli import util.cli
class Module(core.module.Module): class Module(core.module.Module):
@core.decorators.every(minutes=10) @core.decorators.every(minutes=10)
def __init__(self, config, theme): def __init__(self, config, theme):
super().__init__(config, theme, core.widget.Widget('')) super().__init__(config, theme, core.widget.Widget(""))
self.__active = False self.__active = False
self.__xid = None self.__xid = None
@ -32,7 +33,7 @@ class Module(core.module.Module):
core.input.register(self, button=core.input.LEFT_MOUSE, cmd=self.__toggle) core.input.register(self, button=core.input.LEFT_MOUSE, cmd=self.__toggle)
def __check_requirements(self): def __check_requirements(self):
requirements = ['xdotool', 'xprop', 'xdg-screensaver'] requirements = ["xdotool", "xprop", "xdg-screensaver"]
missing = [] missing = []
for tool in requirements: for tool in requirements:
if not shutil.which(tool): if not shutil.which(tool):
@ -40,20 +41,24 @@ class Module(core.module.Module):
return missing return missing
def __get_i3bar_xid(self): def __get_i3bar_xid(self):
xid = util.cli.execute('xdotool search --class \'i3bar\'').partition('\n')[0].strip() xid = (
util.cli.execute("xdotool search --class 'i3bar'")
.partition("\n")[0]
.strip()
)
if xid.isdigit(): if xid.isdigit():
return xid return xid
logging.warning('Module caffeine: xdotool couldn\'t get X window ID of \'i3bar\'.') logging.warning("Module caffeine: xdotool couldn't get X window ID of 'i3bar'.")
return None return None
def __notify(self): def __notify(self):
if not shutil.which('notify-send'): if not shutil.which("notify-send"):
return return
if self.__active: if self.__active:
util.cli.execute('notify-send \'Consuming caffeine\'') util.cli.execute("notify-send 'Consuming caffeine'")
else: else:
util.cli.execute('notify-send \'Out of coffee\'') util.cli.execute("notify-send 'Out of coffee'")
def _suspend_screensaver(self): def _suspend_screensaver(self):
self.__xid = self.__get_i3bar_xid() self.__xid = self.__get_i3bar_xid()
@ -63,7 +68,7 @@ class Module(core.module.Module):
pid = os.fork() pid = os.fork()
if pid == 0: if pid == 0:
os.setsid() os.setsid()
util.cli.execute('xdg-screensaver suspend {}'.format(self.__xid)) util.cli.execute("xdg-screensaver suspend {}".format(self.__xid))
os._exit(0) os._exit(0)
else: else:
os.waitpid(pid, 0) os.waitpid(pid, 0)
@ -71,8 +76,12 @@ class Module(core.module.Module):
def __resume_screensaver(self): def __resume_screensaver(self):
success = True success = True
xprop_path = shutil.which('xprop') xprop_path = shutil.which("xprop")
pids = [ p.pid for p in psutil.process_iter() if p.cmdline() == [xprop_path, '-id', str(self.__xid), '-spy'] ] pids = [
p.pid
for p in psutil.process_iter()
if p.cmdline() == [xprop_path, "-id", str(self.__xid), "-spy"]
]
for pid in pids: for pid in pids:
try: try:
os.kill(pid, 9) os.kill(pid, 9)
@ -82,13 +91,13 @@ class Module(core.module.Module):
def state(self, _): def state(self, _):
if self.__active: if self.__active:
return 'activated' return "activated"
return 'deactivated' return "deactivated"
def __toggle(self, _): def __toggle(self, _):
missing = self.__check_requirements() missing = self.__check_requirements()
if missing: if missing:
logging.warning('Could not run caffeine - missing %s!', ', '.join(missing)) logging.warning("Could not run caffeine - missing %s!", ", ".join(missing))
return return
self.__active = not self.__active self.__active = not self.__active
@ -102,4 +111,5 @@ class Module(core.module.Module):
else: else:
self.__active = not self.__active self.__active = not self.__active
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -42,90 +42,88 @@ import util.cli
import util.graph import util.graph
import util.format import util.format
class Module(core.module.Module): class Module(core.module.Module):
def __init__(self, config, theme): def __init__(self, config, theme):
super().__init__(config, theme, []) super().__init__(config, theme, [])
self.__layout = self.parameter('layout', 'cpu2.maxfreq cpu2.cpuload cpu2.coresload cpu2.temp cpu2.fanspeed') self.__layout = self.parameter(
"layout", "cpu2.maxfreq cpu2.cpuload cpu2.coresload cpu2.temp cpu2.fanspeed"
)
self.__widget_names = self.__layout.split() self.__widget_names = self.__layout.split()
self.__colored = util.format.asbool(self.parameter('colored', False)) self.__colored = util.format.asbool(self.parameter("colored", False))
widget_list = [] widget_list = []
for widget_name in self.__widget_names: for widget_name in self.__widget_names:
if widget_name == 'cpu2.maxfreq': if widget_name == "cpu2.maxfreq":
widget = core.widget.Widget( widget = core.widget.Widget(name=widget_name, full_text=self.maxfreq)
name=widget_name, full_text=self.maxfreq) widget.set("type", "freq")
widget.set('type', 'freq') elif widget_name == "cpu2.cpuload":
elif widget_name == 'cpu2.cpuload': widget = core.widget.Widget(name=widget_name, full_text=self.cpuload)
widget = core.widget.Widget( widget.set("type", "load")
name=widget_name, full_text=self.cpuload) elif widget_name == "cpu2.coresload":
widget.set('type', 'load') widget = core.widget.Widget(name=widget_name, full_text=self.coresload)
elif widget_name == 'cpu2.coresload': widget.set("type", "loads")
widget = core.widget.Widget( elif widget_name == "cpu2.temp":
name=widget_name, full_text=self.coresload) widget = core.widget.Widget(name=widget_name, full_text=self.temp)
widget.set('type', 'loads') widget.set("type", "temp")
elif widget_name == 'cpu2.temp': elif widget_name == "cpu2.fanspeed":
widget = core.widget.Widget( widget = core.widget.Widget(name=widget_name, full_text=self.fanspeed)
name=widget_name, full_text=self.temp) widget.set("type", "fan")
widget.set('type', 'temp')
elif widget_name == 'cpu2.fanspeed':
widget = core.widget.Widget(
name=widget_name, full_text=self.fanspeed)
widget.set('type', 'fan')
if self.__colored: if self.__colored:
widget.set('pango', True) widget.set("pango", True)
widget_list.append(widget) widget_list.append(widget)
self.widgets(widget_list) self.widgets(widget_list)
self.__temp_pattern = self.parameter('temp_pattern') self.__temp_pattern = self.parameter("temp_pattern")
if self.__temp_pattern is None: if self.__temp_pattern is None:
self.__temp = 'n/a' self.__temp = "n/a"
self.__fan_pattern = self.parameter('fan_pattern') self.__fan_pattern = self.parameter("fan_pattern")
if self.__fan_pattern is None: if self.__fan_pattern is None:
self.__fan = 'n/a' self.__fan = "n/a"
# maxfreq is loaded only once at startup # maxfreq is loaded only once at startup
if 'cpu2.maxfreq' in self.__widget_names: if "cpu2.maxfreq" in self.__widget_names:
self.__maxfreq = psutil.cpu_freq().max / 1000 self.__maxfreq = psutil.cpu_freq().max / 1000
def maxfreq(self, _): def maxfreq(self, _):
return '{:.2f}GHz'.format(self.__maxfreq) return "{:.2f}GHz".format(self.__maxfreq)
def cpuload(self, _): def cpuload(self, _):
return '{:>3}%'.format(self.__cpuload) return "{:>3}%".format(self.__cpuload)
def add_color(self, bar): def add_color(self, bar):
"""add color as pango markup to a bar""" """add color as pango markup to a bar"""
if bar in ['', '']: if bar in ["", ""]:
color = self.theme.color('green', 'green') color = self.theme.color("green", "green")
elif bar in ['', '']: elif bar in ["", ""]:
color = self.theme.color('yellow', 'yellow') color = self.theme.color("yellow", "yellow")
elif bar in ['', '']: elif bar in ["", ""]:
color = self.theme.color('orange', 'orange') color = self.theme.color("orange", "orange")
elif bar in ['', '']: elif bar in ["", ""]:
color = self.theme.color('red', 'red') color = self.theme.color("red", "red")
colored_bar = '<span foreground="{}">{}</span>'.format(color, bar) colored_bar = '<span foreground="{}">{}</span>'.format(color, bar)
return colored_bar return colored_bar
def coresload(self, _): def coresload(self, _):
mono_bars = [util.graph.hbar(x) for x in self.__coresload] mono_bars = [util.graph.hbar(x) for x in self.__coresload]
if not self.__colored: if not self.__colored:
return ''.join(mono_bars) return "".join(mono_bars)
colored_bars = [self.add_color(x) for x in mono_bars] colored_bars = [self.add_color(x) for x in mono_bars]
return ''.join(colored_bars) return "".join(colored_bars)
def temp(self, _): def temp(self, _):
if self.__temp == 'n/a' or self.__temp == 0: if self.__temp == "n/a" or self.__temp == 0:
return 'n/a' return "n/a"
return '{}°C'.format(self.__temp) return "{}°C".format(self.__temp)
def fanspeed(self, _): def fanspeed(self, _):
if self.__fanspeed == 'n/a': if self.__fanspeed == "n/a":
return 'n/a' return "n/a"
return '{}RPM'.format(self.__fanspeed) return "{}RPM".format(self.__fanspeed)
def _parse_sensors_output(self): def _parse_sensors_output(self):
output = util.cli.execute('sensors -u') output = util.cli.execute("sensors -u")
lines = output.split('\n') lines = output.split("\n")
temp = 'n/a' temp = "n/a"
fan = 'n/a' fan = "n/a"
temp_line = None temp_line = None
fan_line = None fan_line = None
for line in lines: for line in lines:
@ -136,23 +134,24 @@ class Module(core.module.Module):
if temp_line is not None and fan_line is not None: if temp_line is not None and fan_line is not None:
break break
if temp_line is not None: if temp_line is not None:
temp = round(float(temp_line.split(':')[1].strip())) temp = round(float(temp_line.split(":")[1].strip()))
if fan_line is not None: if fan_line is not None:
fan = int(fan_line.split(':')[1].strip()[:-4]) fan = int(fan_line.split(":")[1].strip()[:-4])
return temp, fan return temp, fan
def update(self): def update(self):
if 'cpu2.maxfreq' in self.__widget_names: if "cpu2.maxfreq" in self.__widget_names:
self.__maxfreq = psutil.cpu_freq().max / 1000 self.__maxfreq = psutil.cpu_freq().max / 1000
if 'cpu2.cpuload' in self.__widget_names: if "cpu2.cpuload" in self.__widget_names:
self.__cpuload = round(psutil.cpu_percent(percpu=False)) self.__cpuload = round(psutil.cpu_percent(percpu=False))
if 'cpu2.coresload' in self.__widget_names: if "cpu2.coresload" in self.__widget_names:
self.__coresload = psutil.cpu_percent(percpu=True) self.__coresload = psutil.cpu_percent(percpu=True)
if 'cpu2.temp' in self.__widget_names or 'cpu2.fanspeed' in self.__widget_names: if "cpu2.temp" in self.__widget_names or "cpu2.fanspeed" in self.__widget_names:
self.__temp, self.__fanspeed = self._parse_sensors_output() self.__temp, self.__fanspeed = self._parse_sensors_output()
def state(self, widget): def state(self, widget):
"""for having per-widget icons""" """for having per-widget icons"""
return [widget.get('type', '')] return [widget.get("type", "")]
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

File diff suppressed because it is too large Load diff

View file

@ -27,58 +27,68 @@ import core.input
import util.format import util.format
def default_format(module): def default_format(module):
default = '%x %X %Z' default = "%x %X %Z"
if module == 'datetz': if module == "datetz":
default = '%x %Z' default = "%x %Z"
if module == 'timetz': if module == "timetz":
default = '%X %Z' default = "%X %Z"
return default return default
class Module(core.module.Module): class Module(core.module.Module):
def __init__(self, config, theme): def __init__(self, config, theme):
super().__init__(config, theme, core.widget.Widget(self.get_time)) super().__init__(config, theme, core.widget.Widget(self.get_time))
core.input.register(self, button=core.input.LEFT_MOUSE, cmd=self.next_tz) core.input.register(self, button=core.input.LEFT_MOUSE, cmd=self.next_tz)
core.input.register(self, button=core.input.RIGHT_MOUSE, cmd=self.prev_tz) core.input.register(self, button=core.input.RIGHT_MOUSE, cmd=self.prev_tz)
self.__fmt = self.parameter('format', self.default_format()) self.__fmt = self.parameter("format", self.default_format())
default_timezone = '' default_timezone = ""
try: try:
default_timezone = tzlocal.get_localzone().zone default_timezone = tzlocal.get_localzone().zone
except Exception as e: except Exception as e:
logging.error('unable to get default timezone: {}'.format(str(e))) logging.error("unable to get default timezone: {}".format(str(e)))
try: try:
self._timezones = util.format.aslist(self.parameter('timezone', default_timezone)) self._timezones = util.format.aslist(
self.parameter("timezone", default_timezone)
)
except: except:
self._timezones = [default_timezone] self._timezones = [default_timezone]
self._current_tz = 0 self._current_tz = 0
l = locale.getdefaultlocale() l = locale.getdefaultlocale()
if not l or l == (None, None): if not l or l == (None, None):
l = ('en_US', 'UTF-8') l = ("en_US", "UTF-8")
lcl = self.parameter('locale', '.'.join(l)) lcl = self.parameter("locale", ".".join(l))
try: try:
locale.setlocale(locale.LC_TIME, lcl.split('.')) locale.setlocale(locale.LC_TIME, lcl.split("."))
except Exception: except Exception:
locale.setlocale(locale.LC_TIME, ('en_US', 'UTF-8')) locale.setlocale(locale.LC_TIME, ("en_US", "UTF-8"))
def default_format(self): def default_format(self):
return '%x %X %Z' return "%x %X %Z"
def get_time(self, widget): def get_time(self, widget):
try: try:
try: try:
tz = pytz.timezone(self._timezones[self._current_tz].strip()) tz = pytz.timezone(self._timezones[self._current_tz].strip())
retval = datetime.datetime.now(tz=tzlocal.get_localzone()).astimezone(tz).strftime(self.__fmt) retval = (
datetime.datetime.now(tz=tzlocal.get_localzone())
.astimezone(tz)
.strftime(self.__fmt)
)
except pytz.exceptions.UnknownTimeZoneError: except pytz.exceptions.UnknownTimeZoneError:
retval = '[Unknown timezone: {}]'.format(self._timezones[self._current_tz].strip()) retval = "[Unknown timezone: {}]".format(
self._timezones[self._current_tz].strip()
)
except Exception as e: except Exception as e:
logging.error('unable to get time: {}'.format(str(e))) logging.error("unable to get time: {}".format(str(e)))
retval = '[n/a]' retval = "[n/a]"
enc = locale.getpreferredencoding() enc = locale.getpreferredencoding()
if hasattr(retval, 'decode'): if hasattr(retval, "decode"):
return retval.decode(enc) return retval.decode(enc)
return retval return retval
@ -94,4 +104,5 @@ class Module(core.module.Module):
previous_timezone = 0 # wraparound previous_timezone = 0 # wraparound
self._current_tz = previous_timezone self._current_tz = previous_timezone
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -10,12 +10,14 @@ Parameters:
import core.decorators import core.decorators
from .datetimetz import Module from .datetimetz import Module
class Module(Module): class Module(Module):
@core.decorators.every(hours=1) @core.decorators.every(hours=1)
def __init__(self, config, theme): def __init__(self, config, theme):
super().__init__(config, theme) super().__init__(config, theme)
def default_format(self): def default_format(self):
return '%x %Z' return "%x %Z"
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -45,66 +45,71 @@ import core.decorators
import util.cli import util.cli
import util.format import util.format
class Module(core.module.Module): class Module(core.module.Module):
def __init__(self, config, theme): def __init__(self, config, theme):
super().__init__(config, theme, core.widget.Widget(self.deadbeef)) super().__init__(config, theme, core.widget.Widget(self.deadbeef))
buttons = {'LEFT_CLICK': core.input.LEFT_MOUSE, buttons = {
'RIGHT_CLICK': core.input.RIGHT_MOUSE, "LEFT_CLICK": core.input.LEFT_MOUSE,
'MIDDLE_CLICK': core.input.MIDDLE_MOUSE, "RIGHT_CLICK": core.input.RIGHT_MOUSE,
'SCROLL_UP': core.input.WHEEL_UP, "MIDDLE_CLICK": core.input.MIDDLE_MOUSE,
'SCROLL_DOWN': core.input.WHEEL_DOWN, "SCROLL_UP": core.input.WHEEL_UP,
"SCROLL_DOWN": core.input.WHEEL_DOWN,
} }
self._song = '' self._song = ""
self._format = self.parameter('format', '{artist} - {title}') self._format = self.parameter("format", "{artist} - {title}")
self._tf_format = self.parameter('tf_format', '') self._tf_format = self.parameter("tf_format", "")
self._show_tf_when_stopped = util.format.asbool(self.parameter('tf_format_if_stopped', False)) self._show_tf_when_stopped = util.format.asbool(
prev_button = self.parameter('previous', 'LEFT_CLICK') self.parameter("tf_format_if_stopped", False)
next_button = self.parameter('next', 'RIGHT_CLICK') )
pause_button = self.parameter('pause', 'MIDDLE_CLICK') prev_button = self.parameter("previous", "LEFT_CLICK")
next_button = self.parameter("next", "RIGHT_CLICK")
pause_button = self.parameter("pause", "MIDDLE_CLICK")
self.now_playing = 'deadbeef --nowplaying %a;%t;%b;%l;%n;%y;%c;%r;%e' self.now_playing = "deadbeef --nowplaying %a;%t;%b;%l;%n;%y;%c;%r;%e"
self.now_playing_tf = 'deadbeef --nowplaying-tf ' self.now_playing_tf = "deadbeef --nowplaying-tf "
cmd = 'deadbeef ' cmd = "deadbeef "
core.input.register(self, button=buttons[prev_button], core.input.register(self, button=buttons[prev_button], cmd=cmd + "--prev")
cmd=cmd + '--prev') core.input.register(self, button=buttons[next_button], cmd=cmd + "--next")
core.input.register(self, button=buttons[next_button], core.input.register(
cmd=cmd + '--next') self, button=buttons[pause_button], cmd=cmd + "--play-pause"
core.input.register(self, button=buttons[pause_button], )
cmd=cmd + '--play-pause')
# modify the tf_format if we don't want it to show on stop # modify the tf_format if we don't want it to show on stop
# this adds conditions to the query itself, rather than # this adds conditions to the query itself, rather than
# polling to see if deadbeef is running # polling to see if deadbeef is running
# doing this reduces the number of calls we have to make # doing this reduces the number of calls we have to make
if self._tf_format and not self._show_tf_when_stopped: if self._tf_format and not self._show_tf_when_stopped:
self._tf_format = '$if($or(%isplaying%,%ispaused%),{query})'.format(query=self._tf_format) self._tf_format = "$if($or(%isplaying%,%ispaused%),{query})".format(
query=self._tf_format
)
@core.decorators.scrollable @core.decorators.scrollable
def deadbeef(self, widget): def deadbeef(self, widget):
return self.string_song return self.string_song
def hidden(self): def hidden(self):
return self.string_song == '' return self.string_song == ""
def update(self): def update(self):
widgets = self.widgets() widgets = self.widgets()
try: try:
if self._tf_format == '': # no tf format set, use the old style if self._tf_format == "": # no tf format set, use the old style
return self.update_standard(widgets) return self.update_standard(widgets)
return self.update_tf(widgets) return self.update_tf(widgets)
except Exception as e: except Exception as e:
logging.exception(e) logging.exception(e)
self._song = 'error' self._song = "error"
def update_tf(self, widgets): def update_tf(self, widgets):
## ensure that deadbeef is actually running ## ensure that deadbeef is actually running
## easiest way to do this is to check --nowplaying for ## easiest way to do this is to check --nowplaying for
## the string 'nothing' ## the string 'nothing'
if util.cli.execute(self.now_playing) == 'nothing': if util.cli.execute(self.now_playing) == "nothing":
self._song = '' self._song = ""
return return
## perform the actual query -- these can be much more sophisticated ## perform the actual query -- these can be much more sophisticated
data = util.cli.execute(self.now_playing_tf + self._tf_format) data = util.cli.execute(self.now_playing_tf + self._tf_format)
@ -112,11 +117,12 @@ class Module(core.module.Module):
def update_standard(self, widgets): def update_standard(self, widgets):
data = util.cli.execute(self.now_playing) data = util.cli.execute(self.now_playing)
if data == 'nothing': if data == "nothing":
self._song = '' self._song = ""
else: else:
data = data.split(';') data = data.split(";")
self._song = self._format.format(artist=data[0], self._song = self._format.format(
artist=data[0],
title=data[1], title=data[1],
album=data[2], album=data[2],
length=data[3], length=data[3],
@ -124,7 +130,8 @@ class Module(core.module.Module):
year=data[5], year=data[5],
comment=data[6], comment=data[6],
copyright=data[7], copyright=data[7],
time=data[8]) time=data[8],
)
@property @property
def string_song(self): def string_song(self):
@ -136,4 +143,5 @@ Returns the current song as a string, either as a unicode() (Python <
return unicode(self._song) return unicode(self._song)
return str(self._song) return str(self._song)
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -19,51 +19,61 @@ import core.module
import core.widget import core.widget
import core.input import core.input
class Module(core.module.Module): class Module(core.module.Module):
def __init__(self, config, theme): def __init__(self, config, theme):
super().__init__(config, theme, core.widget.Widget(self.deezer)) super().__init__(config, theme, core.widget.Widget(self.deezer))
buttons = {'LEFT_CLICK':core.input.LEFT_MOUSE, buttons = {
'RIGHT_CLICK':core.input.RIGHT_MOUSE, "LEFT_CLICK": core.input.LEFT_MOUSE,
'MIDDLE_CLICK':core.input.MIDDLE_MOUSE, "RIGHT_CLICK": core.input.RIGHT_MOUSE,
'SCROLL_UP':core.input.WHEEL_UP, "MIDDLE_CLICK": core.input.MIDDLE_MOUSE,
'SCROLL_DOWN':core.input.WHEEL_DOWN, "SCROLL_UP": core.input.WHEEL_UP,
"SCROLL_DOWN": core.input.WHEEL_DOWN,
} }
self._song = '' self._song = ""
self._format = self.parameter('format', '{artist} - {title}') self._format = self.parameter("format", "{artist} - {title}")
prev_button = self.parameter('previous', 'LEFT_CLICK') prev_button = self.parameter("previous", "LEFT_CLICK")
next_button = self.parameter('next', 'RIGHT_CLICK') next_button = self.parameter("next", "RIGHT_CLICK")
pause_button = self.parameter('pause', 'MIDDLE_CLICK') pause_button = self.parameter("pause", "MIDDLE_CLICK")
cmd = 'dbus-send --session --type=method_call --dest=org.mpris.MediaPlayer2.deezer \ cmd = "dbus-send --session --type=method_call --dest=org.mpris.MediaPlayer2.deezer \
/org/mpris/MediaPlayer2 org.mpris.MediaPlayer2.Player.' /org/mpris/MediaPlayer2 org.mpris.MediaPlayer2.Player."
core.input.register(self, button=buttons[prev_button], core.input.register(self, button=buttons[prev_button], cmd=cmd + "Previous")
cmd=cmd + 'Previous') core.input.register(self, button=buttons[next_button], cmd=cmd + "Next")
core.input.register(self, button=buttons[next_button], core.input.register(self, button=buttons[pause_button], cmd=cmd + "PlayPause")
cmd=cmd + 'Next')
core.input.register(self, button=buttons[pause_button],
cmd=cmd + 'PlayPause')
def deezer(self, widget): def deezer(self, widget):
return str(self._song) return str(self._song)
def hidden(self): def hidden(self):
return str(self._song) == '' return str(self._song) == ""
def update(self): def update(self):
try: try:
bus = dbus.SessionBus() bus = dbus.SessionBus()
deezer = bus.get_object('org.mpris.MediaPlayer2.deezer', '/org/mpris/MediaPlayer2') deezer = bus.get_object(
deezer_iface = dbus.Interface(deezer, 'org.freedesktop.DBus.Properties') "org.mpris.MediaPlayer2.deezer", "/org/mpris/MediaPlayer2"
props = deezer_iface.Get('org.mpris.MediaPlayer2.Player', 'Metadata') )
playback_status = str(deezer_iface.Get('org.mpris.MediaPlayer2.Player', 'PlaybackStatus')) deezer_iface = dbus.Interface(deezer, "org.freedesktop.DBus.Properties")
self._song = self._format.format(album=str(props.get('xesam:album')), props = deezer_iface.Get("org.mpris.MediaPlayer2.Player", "Metadata")
title=str(props.get('xesam:title')), playback_status = str(
artist=','.join(props.get('xesam:artist')), deezer_iface.Get("org.mpris.MediaPlayer2.Player", "PlaybackStatus")
trackNumber=str(props.get('xesam:trackNumber')), )
playbackStatus=u'\u25B6' if playback_status=='Playing' else u'\u258D\u258D' if playback_status=='Paused' else '',) self._song = self._format.format(
album=str(props.get("xesam:album")),
title=str(props.get("xesam:title")),
artist=",".join(props.get("xesam:artist")),
trackNumber=str(props.get("xesam:trackNumber")),
playbackStatus="\u25B6"
if playback_status == "Playing"
else "\u258D\u258D"
if playback_status == "Paused"
else "",
)
except Exception: except Exception:
self._song = '' self._song = ""
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -19,34 +19,41 @@ import core.decorators
import util.cli import util.cli
def get_dnf_info(widget): def get_dnf_info(widget):
res = util.cli.execute('dnf updateinfo', ignore_errors=True) res = util.cli.execute("dnf updateinfo", ignore_errors=True)
security = 0 security = 0
bugfixes = 0 bugfixes = 0
enhancements = 0 enhancements = 0
other = 0 other = 0
for line in res.split('\n'): for line in res.split("\n"):
if not line.startswith(' '): continue if not line.startswith(" "):
elif 'ecurity' in line: continue
elif "ecurity" in line:
for s in line.split(): for s in line.split():
if s.isdigit(): security += int(s) if s.isdigit():
elif 'ugfix' in line: security += int(s)
elif "ugfix" in line:
for s in line.split(): for s in line.split():
if s.isdigit(): bugfixes += int(s) if s.isdigit():
elif 'hancement' in line: bugfixes += int(s)
elif "hancement" in line:
for s in line.split(): for s in line.split():
if s.isdigit(): enhancements += int(s) if s.isdigit():
enhancements += int(s)
else: else:
for s in line.split(): for s in line.split():
if s.isdigit(): other += int(s) if s.isdigit():
other += int(s)
widget.set('security', security) widget.set("security", security)
widget.set('bugfixes', bugfixes) widget.set("bugfixes", bugfixes)
widget.set('enhancements', enhancements) widget.set("enhancements", enhancements)
widget.set('other', other) widget.set("other", other)
core.event.trigger("update", [widget.module.id], redraw_only=True)
core.event.trigger('update', [ widget.module.id ], redraw_only=True)
class Module(core.module.Module): class Module(core.module.Module):
@core.decorators.every(minutes=30) @core.decorators.every(minutes=30)
@ -55,9 +62,9 @@ class Module(core.module.Module):
def updates(self, widget): def updates(self, widget):
result = [] result = []
for t in ['security', 'bugfixes', 'enhancements', 'other']: for t in ["security", "bugfixes", "enhancements", "other"]:
result.append(str(widget.get(t, 0))) result.append(str(widget.get(t, 0)))
return '/'.join(result) return "/".join(result)
def update(self): def update(self):
thread = threading.Thread(target=get_dnf_info, args=(self.widget(),)) thread = threading.Thread(target=get_dnf_info, args=(self.widget(),))
@ -65,11 +72,12 @@ class Module(core.module.Module):
def state(self, widget): def state(self, widget):
cnt = 0 cnt = 0
for t in ['security', 'bugfixes', 'enhancements', 'other']: for t in ["security", "bugfixes", "enhancements", "other"]:
cnt += widget.get(t, 0) cnt += widget.get(t, 0)
if cnt == 0: if cnt == 0:
return 'good' return "good"
if cnt > 50 or widget.get('security', 0) > 0: if cnt > 50 or widget.get("security", 0) > 0:
return 'critical' return "critical"
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -15,29 +15,33 @@ import core.module
import core.widget import core.widget
import core.decorators import core.decorators
class Module(core.module.Module): class Module(core.module.Module):
@core.decorators.every(seconds=5) @core.decorators.every(seconds=5)
def __init__(self, config, theme): def __init__(self, config, theme):
super().__init__(config, theme, core.widget.Widget(self.docker_info)) super().__init__(config, theme, core.widget.Widget(self.docker_info))
self.__info = '' self.__info = ""
def state(self, widget): def state(self, widget):
state = [] state = []
if self.__info == 'OK - 0': if self.__info == "OK - 0":
state.append('warning') state.append("warning")
elif self.__info in ['n/a', 'daemon off']: elif self.__info in ["n/a", "daemon off"]:
state.append('critical') state.append("critical")
return state return state
def docker_info(self, widget): def docker_info(self, widget):
try: try:
cli = docker.DockerClient(base_url='unix://var/run/docker.sock') cli = docker.DockerClient(base_url="unix://var/run/docker.sock")
cli.ping() cli.ping()
self.__info = 'OK - {}'.format(len(cli.containers.list(filters={'status': 'running'}))) self.__info = "OK - {}".format(
len(cli.containers.list(filters={"status": "running"}))
)
except ConnectionError: except ConnectionError:
self.__info = 'daemon off' self.__info = "daemon off"
except Exception: except Exception:
self.__info = 'n/a' self.__info = "n/a"
return self.__info return self.__info
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -1,6 +1,6 @@
# pylint: disable=C0111,R0903 # pylint: disable=C0111,R0903
'''Toggle dunst notifications.''' """Toggle dunst notifications."""
import core.module import core.module
import core.widget import core.widget
@ -8,28 +8,27 @@ import core.input
import util.cli import util.cli
class Module(core.module.Module): class Module(core.module.Module):
def __init__(self, config, theme): def __init__(self, config, theme):
super().__init__(config, theme, core.widget.Widget('')) super().__init__(config, theme, core.widget.Widget(""))
self._paused = False self._paused = False
# Make sure that dunst is currently not paused # Make sure that dunst is currently not paused
util.cli.execute('killall -s SIGUSR2 dunst', ignore_errors=True) util.cli.execute("killall -s SIGUSR2 dunst", ignore_errors=True)
core.input.register(self, button=core.input.LEFT_MOUSE, core.input.register(self, button=core.input.LEFT_MOUSE, cmd=self.toggle_status)
cmd=self.toggle_status
)
def toggle_status(self, event): def toggle_status(self, event):
self._paused = not self._paused self._paused = not self._paused
try: try:
if self._paused: if self._paused:
util.cli.execute('killall -s SIGUSR1 dunst') util.cli.execute("killall -s SIGUSR1 dunst")
else: else:
util.cli.execute('killall -s SIGUSR2 dunst') util.cli.execute("killall -s SIGUSR2 dunst")
except: except:
self._paused = not self._paused # toggling failed self._paused = not self._paused # toggling failed
def state(self, widget): def state(self, widget):
if self._paused: if self._paused:
return ['muted', 'warning'] return ["muted", "warning"]
return ['unmuted'] return ["unmuted"]

View file

@ -24,51 +24,64 @@ import core.decorators
import util.format import util.format
def getfromkrak(coin, currency): def getfromkrak(coin, currency):
abbrev = { abbrev = {
'Btc': ['xbt', 'XXBTZ'], "Btc": ["xbt", "XXBTZ"],
'Eth': ['eth', 'XETHZ'], "Eth": ["eth", "XETHZ"],
'Ltc': ['ltc', 'XLTCZ'], "Ltc": ["ltc", "XLTCZ"],
} }
data = abbrev.get(coin, None) data = abbrev.get(coin, None)
if not data: return if not data:
epair = '{}{}'.format(data[0], currency) return
tickname = '{}{}'.format(data[1], currency.upper()) epair = "{}{}".format(data[0], currency)
tickname = "{}{}".format(data[1], currency.upper())
try: try:
krakenget = requests.get('https://api.kraken.com/0/public/Ticker?pair='+epair).json() krakenget = requests.get(
"https://api.kraken.com/0/public/Ticker?pair=" + epair
).json()
except (RequestException, Exception): except (RequestException, Exception):
return 'No connection' return "No connection"
if not 'result' in krakenget: if not "result" in krakenget:
return 'No data' return "No data"
kethusdask = float(krakenget['result'][tickname]['a'][0]) kethusdask = float(krakenget["result"][tickname]["a"][0])
kethusdbid = float(krakenget['result'][tickname]['b'][0]) kethusdbid = float(krakenget["result"][tickname]["b"][0])
return coin+': '+str((kethusdask+kethusdbid)/2)[0:6] return coin + ": " + str((kethusdask + kethusdbid) / 2)[0:6]
class Module(core.module.Module): class Module(core.module.Module):
@core.decorators.every(minutes=30) @core.decorators.every(minutes=30)
def __init__(self, config, theme): def __init__(self, config, theme):
super().__init__(config, theme, core.widget.Widget(self.curprice)) super().__init__(config, theme, core.widget.Widget(self.curprice))
self.__curprice = '' self.__curprice = ""
self.__getbtc = util.format.asbool(self.parameter('getbtc', True)) self.__getbtc = util.format.asbool(self.parameter("getbtc", True))
self.__geteth = util.format.asbool(self.parameter('geteth', True)) self.__geteth = util.format.asbool(self.parameter("geteth", True))
self.__getltc = util.format.asbool(self.parameter('getltc', True)) self.__getltc = util.format.asbool(self.parameter("getltc", True))
self.__getcur = self.parameter('getcur', 'usd') self.__getcur = self.parameter("getcur", "usd")
core.input.register(self, button=core.input.LEFT_MOUSE, core.input.register(
cmd='xdg-open https://cryptowat.ch/') self, button=core.input.LEFT_MOUSE, cmd="xdg-open https://cryptowat.ch/"
)
def curprice(self, widget): def curprice(self, widget):
return self.__curprice return self.__curprice
def update(self): def update(self):
currency = self.__getcur currency = self.__getcur
btcprice, ethprice, ltcprice = '', '', '' btcprice, ethprice, ltcprice = "", "", ""
if self.__getbtc: if self.__getbtc:
btcprice = getfromkrak('Btc', currency) btcprice = getfromkrak("Btc", currency)
if self.__geteth: if self.__geteth:
ethprice = getfromkrak('Eth', currency) ethprice = getfromkrak("Eth", currency)
if self.__getltc: if self.__getltc:
ltcprice = getfromkrak('Ltc', currency) ltcprice = getfromkrak("Ltc", currency)
self.__curprice = btcprice+' '*(self.__getbtc*self.__geteth)+ethprice+' '*(self.__getltc*max(self.__getbtc, self.__geteth))+ltcprice self.__curprice = (
btcprice
+ " " * (self.__getbtc * self.__geteth)
+ ethprice
+ " " * (self.__getltc * max(self.__getbtc, self.__geteth))
+ ltcprice
)
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -18,6 +18,7 @@ import core.widget
import core.decorators import core.decorators
import core.input import core.input
class Module(core.module.Module): class Module(core.module.Module):
@core.decorators.every(minutes=5) @core.decorators.every(minutes=5)
def __init__(self, config, theme): def __init__(self, config, theme):
@ -25,15 +26,19 @@ class Module(core.module.Module):
self.__count = 0 self.__count = 0
self.__requests = requests.Session() self.__requests = requests.Session()
self.__requests.headers.update({'Authorization':'token {}'.format(self.parameter('token', ''))}) self.__requests.headers.update(
{"Authorization": "token {}".format(self.parameter("token", ""))}
)
cmd = 'xdg-open' cmd = "xdg-open"
if not shutil.which(cmd): if not shutil.which(cmd):
cmd = 'x-www-browser' cmd = "x-www-browser"
core.input.register(
core.input.register(self, button=core.input.LEFT_MOUSE, self,
cmd='{} https://github.com/notifications'.format(cmd)) button=core.input.LEFT_MOUSE,
cmd="{} https://github.com/notifications".format(cmd),
)
core.input.register(self, button=core.input.RIGHT_MOUSE, cmd=self.update) core.input.register(self, button=core.input.RIGHT_MOUSE, cmd=self.update)
def github(self, _): def github(self, _):
@ -42,17 +47,25 @@ class Module(core.module.Module):
def update(self): def update(self):
try: try:
self.__count = 0 self.__count = 0
url = 'https://api.github.com/notifications' url = "https://api.github.com/notifications"
while True: while True:
notifications = self.__requests.get(url) notifications = self.__requests.get(url)
self.__count += len(list(filter(lambda notification: notification['unread'], notifications.json()))) self.__count += len(
next_link = notifications.links.get('next') list(
filter(
lambda notification: notification["unread"],
notifications.json(),
)
)
)
next_link = notifications.links.get("next")
if next_link is not None: if next_link is not None:
url = next_link.get('url') url = next_link.get("url")
else: else:
break break
except Exception: except Exception:
self.__count = 'n/a' self.__count = "n/a"
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -12,42 +12,47 @@ import core.input
import util.cli import util.cli
class Module(core.module.Module): class Module(core.module.Module):
def __init__(self, config, theme): def __init__(self, config, theme):
widgets = [ widgets = [
core.widget.Widget(name='gpmdp.prev'), core.widget.Widget(name="gpmdp.prev"),
core.widget.Widget(name='gpmdp.main', full_text=self.description), core.widget.Widget(name="gpmdp.main", full_text=self.description),
core.widget.Widget(name='gpmdp.next'), core.widget.Widget(name="gpmdp.next"),
] ]
super().__init__(config, theme, widgets) super().__init__(config, theme, widgets)
core.input.register(widgets[0], button=core.input.LEFT_MOUSE, core.input.register(
cmd='playerctl previous') widgets[0], button=core.input.LEFT_MOUSE, cmd="playerctl previous"
core.input.register(widgets[1], button=core.input.LEFT_MOUSE, )
cmd='playerctl play-pause') core.input.register(
core.input.register(widgets[2], button=core.input.LEFT_MOUSE, widgets[1], button=core.input.LEFT_MOUSE, cmd="playerctl play-pause"
cmd='playerctl next') )
core.input.register(
widgets[2], button=core.input.LEFT_MOUSE, cmd="playerctl next"
)
self.__status = None self.__status = None
self.__tags = None self.__tags = None
def description(self, widget): def description(self, widget):
return self.__tags if self.__tags else 'n/a' return self.__tags if self.__tags else "n/a"
def update(self): def update(self):
self.__load_song() self.__load_song()
def state(self, widget): def state(self, widget):
if widget.name == 'gpmdp.prev': if widget.name == "gpmdp.prev":
return 'prev' return "prev"
if widget.name == 'gpmdp.next': if widget.name == "gpmdp.next":
return 'next' return "next"
return self.__status return self.__status
def __load_song(self): def __load_song(self):
info = util.cli.execute('gpmdp-remote current', ignore_errors=True) info = util.cli.execute("gpmdp-remote current", ignore_errors=True)
status = util.cli.execute('gpmdp-remote status', ignore_errors=True) status = util.cli.execute("gpmdp-remote status", ignore_errors=True)
self.__status = status.split('\n')[0].lower() self.__status = status.split("\n")[0].lower()
self.__tags = info.split('\n')[0] self.__tags = info.split("\n")[0]
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -9,12 +9,13 @@ import socket
import core.module import core.module
import core.widget import core.widget
HOST = 'localhost' HOST = "localhost"
PORT = 7634 PORT = 7634
CHUNK_SIZE = 1024 CHUNK_SIZE = 1024
RECORD_SIZE = 5 RECORD_SIZE = 5
SEPARATOR = '|' SEPARATOR = "|"
class Module(core.module.Module): class Module(core.module.Module):
def __init__(self, config, theme): def __init__(self, config, theme):
@ -29,7 +30,7 @@ class Module(core.module.Module):
try: try:
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock: with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
sock.connect((HOST, PORT)) sock.connect((HOST, PORT))
data = '' data = ""
while True: while True:
chunk = sock.recv(CHUNK_SIZE) chunk = sock.recv(CHUNK_SIZE)
if chunk: if chunk:
@ -46,7 +47,7 @@ class Module(core.module.Module):
split data using | separator and remove first item split data using | separator and remove first item
(because the first item is empty) (because the first item is empty)
""" """
parts = data.split('|')[1:] parts = data.split("|")[1:]
return parts return parts
@staticmethod @staticmethod
@ -54,8 +55,9 @@ class Module(core.module.Module):
""" """
partition parts: one device record is five (5) items partition parts: one device record is five (5) items
""" """
per_disk = [parts[i:i+RECORD_SIZE] per_disk = [
for i in range(len(parts))[::RECORD_SIZE]] parts[i : i + RECORD_SIZE] for i in range(len(parts))[::RECORD_SIZE]
]
return per_disk return per_disk
@staticmethod @staticmethod
@ -64,20 +66,20 @@ class Module(core.module.Module):
get device name (without /dev part, to save space on bar) get device name (without /dev part, to save space on bar)
and temperature (in °C) as tuple and temperature (in °C) as tuple
""" """
device_name = device_record[0].split('/')[-1] device_name = device_record[0].split("/")[-1]
device_temp = device_record[2] device_temp = device_record[2]
return (device_name, device_temp) return (device_name, device_temp)
@staticmethod @staticmethod
def __get_hddtemp(device_record): def __get_hddtemp(device_record):
name, temp = device_record name, temp = device_record
hddtemp = '{}+{}°C'.format(name, temp) hddtemp = "{}+{}°C".format(name, temp)
return hddtemp return hddtemp
def __get_hddtemps(self): def __get_hddtemps(self):
data = self.__fetch_data() data = self.__fetch_data()
if data is None: if data is None:
return 'n/a' return "n/a"
parts = self.__get_parts(data) parts = self.__get_parts(data)
per_disk = self.__partition_parts(parts) per_disk = self.__partition_parts(parts)
names_and_temps = [self.__get_name_and_temp(x) for x in per_disk] names_and_temps = [self.__get_name_and_temp(x) for x in per_disk]
@ -87,4 +89,5 @@ class Module(core.module.Module):
def update(self): def update(self):
self.__hddtemps = self.__get_hddtemps() self.__hddtemps = self.__get_hddtemps()
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -8,16 +8,18 @@ import core.module
import core.widget import core.widget
import core.decorators import core.decorators
class Module(core.module.Module): class Module(core.module.Module):
@core.decorators.every(minutes=60) @core.decorators.every(minutes=60)
def __init__(self, config, theme): def __init__(self, config, theme):
super().__init__(config, theme, core.widget.Widget(self.output)) super().__init__(config, theme, core.widget.Widget(self.output))
self.__hname = '' self.__hname = ""
def output(self, _): def output(self, _):
return self.__hname + ' ' + u'\uf233' return self.__hname + " " + "\uf233"
def update(self): def update(self):
self.__hname = platform.node() self.__hname = platform.node()
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -16,21 +16,22 @@ import core.module
import core.widget import core.widget
import core.decorators import core.decorators
class Module(core.module.Module): class Module(core.module.Module):
UNK = 'UNK' UNK = "UNK"
@core.decorators.every(seconds=30) @core.decorators.every(seconds=30)
def __init__(self, config, theme): def __init__(self, config, theme):
super().__init__(config, theme, core.widget.Widget(self.output)) super().__init__(config, theme, core.widget.Widget(self.output))
self.__label = self.parameter('label') self.__label = self.parameter("label")
self.__target = self.parameter('target') self.__target = self.parameter("target")
self.__expect = self.parameter('expect', '200') self.__expect = self.parameter("expect", "200")
def labelize(self, s): def labelize(self, s):
if self.__label is None: if self.__label is None:
return s return s
return '{}: {}'.format(self.__label, s) return "{}: {}".format(self.__label, s)
def getStatus(self): def getStatus(self):
try: try:
@ -46,8 +47,8 @@ class Module(core.module.Module):
if self.__status == self.__expect: if self.__status == self.__expect:
return self.labelize(self.__status) return self.labelize(self.__status)
else: else:
reason = ' != {}'.format(self.__expect) reason = " != {}".format(self.__expect)
return self.labelize('{}{}'.format(self.__status, reason)) return self.labelize("{}{}".format(self.__status, reason))
def output(self, widget): def output(self, widget):
return self.__output return self.__output
@ -58,9 +59,10 @@ class Module(core.module.Module):
def state(self, widget): def state(self, widget):
if self.__status == self.UNK: if self.__status == self.UNK:
return 'warning' return "warning"
if self.__status != self.__expect: if self.__status != self.__expect:
return 'critical' return "critical"
return self.__output return self.__output
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -13,17 +13,28 @@ import core.widget
import util.cli import util.cli
import util.format import util.format
class Module(core.module.Module): class Module(core.module.Module):
def __init__(self, config, theme): def __init__(self, config, theme):
super().__init__(config, theme, []) super().__init__(config, theme, [])
self.__include = tuple(filter(len, util.format.aslist(self.parameter('include', 'NumLock,CapsLock')))) self.__include = tuple(
self.__signalType = self.parameter('signaltype') if not self.parameter('signaltype') is None else 'warning' filter(
len, util.format.aslist(self.parameter("include", "NumLock,CapsLock"))
)
)
self.__signalType = (
self.parameter("signaltype")
if not self.parameter("signaltype") is None
else "warning"
)
def update(self): def update(self):
status_line = '' status_line = ""
for line in util.cli.execute('xset q', ignore_errors=True).replace(' ', '').split('\n'): for line in (
if 'capslock' in line.lower(): util.cli.execute("xset q", ignore_errors=True).replace(" ", "").split("\n")
):
if "capslock" in line.lower():
status_line = line status_line = line
break break
for indicator in self.__include: for indicator in self.__include:
@ -32,15 +43,21 @@ class Module(core.module.Module):
widget = core.widget.Widget(name=indicator, module=self) widget = core.widget.Widget(name=indicator, module=self)
self.widgets().append(widget) self.widgets().append(widget)
widget.set('status', True if '{}:on'.format(indicator.lower()) in status_line.lower() else False) widget.set(
"status",
True
if "{}:on".format(indicator.lower()) in status_line.lower()
else False,
)
widget.full_text(indicator) widget.full_text(indicator)
def state(self, widget): def state(self, widget):
states = [] states = []
if widget.get('status', False): if widget.get("status", False):
states.append(self.__signalType) states.append(self.__signalType)
else: else:
states.append('normal') states.append("normal")
return states return states
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -11,31 +11,30 @@ import core.input
import util.cli import util.cli
class Module(core.module.Module): class Module(core.module.Module):
@core.decorators.every(seconds=60) @core.decorators.every(seconds=60)
def __init__(self, config, theme): def __init__(self, config, theme):
super().__init__(config, theme, core.widget.Widget(self.current_layout)) super().__init__(config, theme, core.widget.Widget(self.current_layout))
core.input.register( core.input.register(self, button=core.input.LEFT_MOUSE, cmd=self.__next_keymap)
self,
button=core.input.LEFT_MOUSE,
cmd=self.__next_keymap)
self.__current_layout = self.__get_current_layout() self.__current_layout = self.__get_current_layout()
def current_layout(self, _): def current_layout(self, _):
return self.__current_layout return self.__current_layout
def __next_keymap(self, event): def __next_keymap(self, event):
util.cli.execute('xkb-switch -n', ignore_errors=True) util.cli.execute("xkb-switch -n", ignore_errors=True)
def __get_current_layout(self): def __get_current_layout(self):
try: try:
res = util.cli.execute('xkb-switch') res = util.cli.execute("xkb-switch")
return res.split('\n')[0] return res.split("\n")[0]
except RuntimeError: except RuntimeError:
return ['n/a'] return ["n/a"]
def update(self): def update(self):
self.__current_layout = self.__get_current_layout() self.__current_layout = self.__get_current_layout()
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -12,14 +12,13 @@ import core.input
import util.cli import util.cli
class Module(core.module.Module): class Module(core.module.Module):
def __init__(self, config, theme): def __init__(self, config, theme):
super().__init__(config, theme, core.widget.Widget(self.current_layout)) super().__init__(config, theme, core.widget.Widget(self.current_layout))
core.input.register(self, button=core.input.LEFT_MOUSE, core.input.register(self, button=core.input.LEFT_MOUSE, cmd=self.__next_keymap)
cmd=self.__next_keymap) core.input.register(self, button=core.input.RIGHT_MOUSE, cmd=self.__prev_keymap)
core.input.register(self, button=core.input.RIGHT_MOUSE,
cmd=self.__prev_keymap)
def __next_keymap(self, event): def __next_keymap(self, event):
self._set_keymap(1) self._set_keymap(1)
@ -29,41 +28,49 @@ class Module(core.module.Module):
def _set_keymap(self, rotation): def _set_keymap(self, rotation):
layouts = self.get_layouts() layouts = self.get_layouts()
if len(layouts) == 1: return # nothing to do if len(layouts) == 1:
return # nothing to do
layouts = layouts[rotation:] + layouts[:rotation] layouts = layouts[rotation:] + layouts[:rotation]
layout_list = [] layout_list = []
variant_list = [] variant_list = []
for l in layouts: for l in layouts:
tmp = l.split(':') tmp = l.split(":")
layout_list.append(tmp[0]) layout_list.append(tmp[0])
variant_list.append(tmp[1] if len(tmp) > 1 else '') variant_list.append(tmp[1] if len(tmp) > 1 else "")
util.cli.execute('setxkbmap -layout {} -variant {}'.format(','.join(layout_list), ','.join(variant_list)), ignore_errors=True) util.cli.execute(
"setxkbmap -layout {} -variant {}".format(
",".join(layout_list), ",".join(variant_list)
),
ignore_errors=True,
)
def get_layouts(self): def get_layouts(self):
try: try:
res = util.cli.execute('setxkbmap -query') res = util.cli.execute("setxkbmap -query")
except RuntimeError: except RuntimeError:
return ['n/a'] return ["n/a"]
layouts = [] layouts = []
variants = [] variants = []
for line in res.split('\n'): for line in res.split("\n"):
if not line: continue if not line:
if 'layout' in line: continue
layouts = line.split(':')[1].strip().split(',') if "layout" in line:
if 'variant' in line: layouts = line.split(":")[1].strip().split(",")
variants = line.split(':')[1].strip().split(',') if "variant" in line:
variants = line.split(":")[1].strip().split(",")
result = [] result = []
for idx, layout in enumerate(layouts): for idx, layout in enumerate(layouts):
if len(variants) > idx and variants[idx]: if len(variants) > idx and variants[idx]:
layout = '{}:{}'.format(layout, variants[idx]) layout = "{}:{}".format(layout, variants[idx])
result.append(layout) result.append(layout)
return result if len(result) > 0 else ['n/a'] return result if len(result) > 0 else ["n/a"]
def current_layout(self, widget): def current_layout(self, widget):
layouts = self.get_layouts() layouts = self.get_layouts()
return layouts[0] return layouts[0]
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -11,18 +11,19 @@ import core.widget
import core.input import core.input
import core.decorators import core.decorators
class Module(core.module.Module): class Module(core.module.Module):
@core.decorators.every(seconds=10) @core.decorators.every(seconds=10)
def __init__(self, config, theme): def __init__(self, config, theme):
super().__init__(config, theme, core.widget.Widget(self.status)) super().__init__(config, theme, core.widget.Widget(self.status))
core.input.register(self, button=core.input.LEFT_MOUSE, core.input.register(self, button=core.input.LEFT_MOUSE, cmd="virt-manager")
cmd='virt-manager')
def status(self, _): def status(self, _):
conn = libvirt.openReadOnly(None) conn = libvirt.openReadOnly(None)
if conn == None: if conn == None:
return 'Failed to open connection to the hypervisor' return "Failed to open connection to the hypervisor"
return 'VMs %s' % (conn.numOfDomains()) return "VMs %s" % (conn.numOfDomains())
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -29,17 +29,18 @@ import core.input
import util.cli import util.cli
class Module(core.module.Module): class Module(core.module.Module):
def __init__(self, config, theme): def __init__(self, config, theme):
super().__init__(config, theme, core.widget.Widget(self.description)) super().__init__(config, theme, core.widget.Widget(self.description))
core.input.register(self, button=core.input.LEFT_MOUSE, cmd='mocp -G') core.input.register(self, button=core.input.LEFT_MOUSE, cmd="mocp -G")
core.input.register(self, button=core.input.RIGHT_MOUSE, cmd='mocp -t shuffle') core.input.register(self, button=core.input.RIGHT_MOUSE, cmd="mocp -t shuffle")
self.__format = self.parameter('format', '%state %artist - %song | %ct/%tt') self.__format = self.parameter("format", "%state %artist - %song | %ct/%tt")
self.__running = False self.__running = False
def description(self, widget): def description(self, widget):
return self.__info if self.__running == True else 'Music On Console Player' return self.__info if self.__running == True else "Music On Console Player"
def update(self): def update(self):
self.__load_song() self.__load_song()
@ -51,4 +52,5 @@ class Module(core.module.Module):
except RuntimeError: except RuntimeError:
self.__running = False self.__running = False
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -52,22 +52,25 @@ import core.decorators
import util.cli import util.cli
class Module(core.module.Module): class Module(core.module.Module):
def __init__(self, config, theme): def __init__(self, config, theme):
super().__init__(config, theme, []) super().__init__(config, theme, [])
self._layout = self.parameter('layout', 'mpd.prev mpd.main mpd.next mpd.shuffle mpd.repeat') self._layout = self.parameter(
"layout", "mpd.prev mpd.main mpd.next mpd.shuffle mpd.repeat"
)
self._fmt = self.parameter('format', '{artist} - {title} {position}/{duration}') self._fmt = self.parameter("format", "{artist} - {title} {position}/{duration}")
self._status = None self._status = None
self._shuffle = False self._shuffle = False
self._repeat = False self._repeat = False
self._tags = defaultdict(lambda: '') self._tags = defaultdict(lambda: "")
if not self.parameter('host'): if not self.parameter("host"):
self._hostcmd = '' self._hostcmd = ""
else: else:
self._hostcmd = ' -h ' + self.parameter('host') self._hostcmd = " -h " + self.parameter("host")
# Create widgets # Create widgets
widget_list = [] widget_list = []
@ -76,19 +79,38 @@ class Module(core.module.Module):
widget = core.widget.Widget(name=widget_name, module=self) widget = core.widget.Widget(name=widget_name, module=self)
widget_list.append(widget) widget_list.append(widget)
if widget_name == 'mpd.prev': if widget_name == "mpd.prev":
widget_map[widget] = {'button': core.input.LEFT_MOUSE, 'cmd': 'mpc prev' + self._hostcmd} widget_map[widget] = {
elif widget_name == 'mpd.main': "button": core.input.LEFT_MOUSE,
widget_map[widget] = {'button': core.input.LEFT_MOUSE, 'cmd': 'mpc toggle' + self._hostcmd} "cmd": "mpc prev" + self._hostcmd,
}
elif widget_name == "mpd.main":
widget_map[widget] = {
"button": core.input.LEFT_MOUSE,
"cmd": "mpc toggle" + self._hostcmd,
}
widget.full_text(self.description) widget.full_text(self.description)
elif widget_name == 'mpd.next': elif widget_name == "mpd.next":
widget_map[widget] = {'button': core.input.LEFT_MOUSE, 'cmd': 'mpc next' + self._hostcmd} widget_map[widget] = {
elif widget_name == 'mpd.shuffle': "button": core.input.LEFT_MOUSE,
widget_map[widget] = {'button': core.input.LEFT_MOUSE, 'cmd': 'mpc random' + self._hostcmd} "cmd": "mpc next" + self._hostcmd,
elif widget_name == 'mpd.repeat': }
widget_map[widget] = {'button': core.input.LEFT_MOUSE, 'cmd': 'mpc repeat' + self._hostcmd} elif widget_name == "mpd.shuffle":
widget_map[widget] = {
"button": core.input.LEFT_MOUSE,
"cmd": "mpc random" + self._hostcmd,
}
elif widget_name == "mpd.repeat":
widget_map[widget] = {
"button": core.input.LEFT_MOUSE,
"cmd": "mpc repeat" + self._hostcmd,
}
else: else:
raise KeyError('The mpd module does not support a {widget_name!r} widget'.format(widget_name=widget_name)) raise KeyError(
"The mpd module does not support a {widget_name!r} widget".format(
widget_name=widget_name
)
)
self.widgets(widget_list) self.widgets(widget_list)
# Register input callbacks # Register input callbacks
@ -106,74 +128,79 @@ class Module(core.module.Module):
self._load_song() self._load_song()
def state(self, widget): def state(self, widget):
if widget.name == 'mpd.shuffle': if widget.name == "mpd.shuffle":
return 'shuffle-on' if self._shuffle else 'shuffle-off' return "shuffle-on" if self._shuffle else "shuffle-off"
if widget.name == 'mpd.repeat': if widget.name == "mpd.repeat":
return 'repeat-on' if self._repeat else 'repeat-off' return "repeat-on" if self._repeat else "repeat-off"
if widget.name == 'mpd.prev': if widget.name == "mpd.prev":
return 'prev' return "prev"
if widget.name == 'mpd.next': if widget.name == "mpd.next":
return 'next' return "next"
return self._status return self._status
def _load_song(self): def _load_song(self):
info = '' info = ""
tags = ['name', tags = [
'artist', "name",
'album', "artist",
'albumartist', "album",
'comment', "albumartist",
'composer', "comment",
'date', "composer",
'originaldate', "date",
'disc', "originaldate",
'genre', "disc",
'performer', "genre",
'title', "performer",
'track', "title",
'time', "track",
'file', "time",
'id', "file",
'prio', "id",
'mtime', "prio",
'mdate'] "mtime",
joinedtags = '\n'.join(['tag {0} %{0}%'.format(tag) for tag in tags]) "mdate",
info = util.cli.execute('mpc -f "{}"{}'.format(joinedtags, self._hostcmd), ignore_errors=True) ]
joinedtags = "\n".join(["tag {0} %{0}%".format(tag) for tag in tags])
info = util.cli.execute(
'mpc -f "{}"{}'.format(joinedtags, self._hostcmd), ignore_errors=True
)
self._tags = defaultdict(lambda: '') self._tags = defaultdict(lambda: "")
self._status = None self._status = None
for line in info.split('\n'): for line in info.split("\n"):
if line.startswith('[playing]'): if line.startswith("[playing]"):
self._status = 'playing' self._status = "playing"
elif line.startswith('[paused]'): elif line.startswith("[paused]"):
self._status = 'paused' self._status = "paused"
if line.startswith('['): if line.startswith("["):
timer = line.split()[2] timer = line.split()[2]
position = timer.split('/')[0] position = timer.split("/")[0]
dur = timer.split('/')[1] dur = timer.split("/")[1]
duration = dur.split(' ')[0] duration = dur.split(" ")[0]
self._tags.update({'position': position}) self._tags.update({"position": position})
self._tags.update({'duration': duration}) self._tags.update({"duration": duration})
if line.startswith('volume'): if line.startswith("volume"):
value = line.split(' ', 2)[1:] value = line.split(" ", 2)[1:]
for option in value: for option in value:
if option.startswith('repeat: on'): if option.startswith("repeat: on"):
self._repeat = True self._repeat = True
elif option.startswith('repeat: off'): elif option.startswith("repeat: off"):
self._repeat = False self._repeat = False
elif option.startswith('random: on'): elif option.startswith("random: on"):
self._shuffle = True self._shuffle = True
elif option.startswith('random: off'): elif option.startswith("random: off"):
self._shuffle = False self._shuffle = False
if line.startswith('tag'): if line.startswith("tag"):
key, value = line.split(' ', 2)[1:] key, value = line.split(" ", 2)[1:]
self._tags.update({key: value}) self._tags.update({key: value})
if key == 'file': if key == "file":
self._tags.update({'file1': os.path.basename(value)}) self._tags.update({"file1": os.path.basename(value)})
self._tags.update( self._tags.update(
{'file2': {"file2": os.path.splitext(os.path.basename(value))[0]}
os.path.splitext(os.path.basename(value))[0]}) )
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -13,24 +13,33 @@ import core.widget
import util.format import util.format
WIDGET_NAME = 'network_traffic' WIDGET_NAME = "network_traffic"
class Module(core.module.Module): class Module(core.module.Module):
def __init__(self, config, theme): def __init__(self, config, theme):
widgets = [ widgets = [
core.widget.Widget(module=self, name='{0}.rx'.format(WIDGET_NAME), full_text=self.download_rate), core.widget.Widget(
core.widget.Widget(module=self, name='{0}.tx'.format(WIDGET_NAME), full_text=self.upload_rate) module=self,
name="{0}.rx".format(WIDGET_NAME),
full_text=self.download_rate,
),
core.widget.Widget(
module=self,
name="{0}.tx".format(WIDGET_NAME),
full_text=self.upload_rate,
),
] ]
super().__init__(config, theme, widgets) super().__init__(config, theme, widgets)
self.widgets()[0].set('theme.minwidth', '0000000KiB/s') self.widgets()[0].set("theme.minwidth", "0000000KiB/s")
self.widgets()[1].set('theme.minwidth', '0000000KiB/s') self.widgets()[1].set("theme.minwidth", "0000000KiB/s")
try: try:
self._bandwidth = BandwidthInfo() self._bandwidth = BandwidthInfo()
self._rate_recv = '?' self._rate_recv = "?"
self._rate_sent = '?' self._rate_sent = "?"
self._bytes_recv = self._bandwidth.bytes_recv() self._bytes_recv = self._bandwidth.bytes_recv()
self._bytes_sent = self._bandwidth.bytes_sent() self._bytes_sent = self._bandwidth.bytes_sent()
except Exception: except Exception:
@ -40,10 +49,10 @@ class Module(core.module.Module):
def state(self, widget): def state(self, widget):
"""Return the widget state""" """Return the widget state"""
if widget.name == '{}.rx'.format(WIDGET_NAME): if widget.name == "{}.rx".format(WIDGET_NAME):
return 'rx' return "rx"
elif widget.name == '{}.tx'.format(WIDGET_NAME): elif widget.name == "{}.tx".format(WIDGET_NAME):
return 'tx' return "tx"
return None return None
@ -52,8 +61,8 @@ class Module(core.module.Module):
bytes_recv = self._bandwidth.bytes_recv() bytes_recv = self._bandwidth.bytes_recv()
bytes_sent = self._bandwidth.bytes_sent() bytes_sent = self._bandwidth.bytes_sent()
self._rate_recv = (bytes_recv - self._bytes_recv) self._rate_recv = bytes_recv - self._bytes_recv
self._rate_sent = (bytes_sent - self._bytes_sent) self._rate_sent = bytes_sent - self._bytes_sent
self._bytes_recv, self._bytes_sent = bytes_recv, bytes_sent self._bytes_recv, self._bytes_sent = bytes_recv, bytes_sent
except Exception: except Exception:
@ -61,10 +70,11 @@ class Module(core.module.Module):
pass pass
def download_rate(self, _): def download_rate(self, _):
return '{}/s'.format(util.format.byte(self._rate_recv)) return "{}/s".format(util.format.byte(self._rate_recv))
def upload_rate(self, _): def upload_rate(self, _):
return '{}/s'.format(util.format.byte(self._rate_sent)) return "{}/s".format(util.format.byte(self._rate_sent))
class BandwidthInfo(object): class BandwidthInfo(object):
"""Get received/sent bytes from network adapter""" """Get received/sent bytes from network adapter"""
@ -85,10 +95,10 @@ class BandwidthInfo(object):
@classmethod @classmethod
def default_network_adapter(cls): def default_network_adapter(cls):
"""Return default active network adapter""" """Return default active network adapter"""
gateway = netifaces.gateways()['default'] gateway = netifaces.gateways()["default"]
if not gateway: if not gateway:
raise 'No default gateway found' raise "No default gateway found"
return gateway[netifaces.AF_INET][1] return gateway[netifaces.AF_INET][1]
@ -97,4 +107,5 @@ class BandwidthInfo(object):
"""Return IO counters""" """Return IO counters"""
return psutil.net_io_counters(pernic=True) return psutil.net_io_counters(pernic=True)
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -20,24 +20,30 @@ import core.widget
import util.cli import util.cli
class Module(core.module.Module): class Module(core.module.Module):
def __init__(self, config, theme): def __init__(self, config, theme):
super().__init__(config, theme, core.widget.Widget(self.output)) super().__init__(config, theme, core.widget.Widget(self.output))
self.__notmuch_count_query = self.parameter('query', 'tag:unread AND NOT path:/.*Trash.*/') self.__notmuch_count_query = self.parameter(
"query", "tag:unread AND NOT path:/.*Trash.*/"
)
def output(self, widget): def output(self, widget):
return self.__notmuch_count return self.__notmuch_count
def state(self, widgets): def state(self, widgets):
if self.__notmuch_count == 0: if self.__notmuch_count == 0:
return 'empty' return "empty"
return 'items' return "items"
def update(self): def update(self):
try: try:
self.__notmuch_count = util.cli.execute('notmuch count {}'.format(self.__notmuch_count_query)).strip() self.__notmuch_count = util.cli.execute(
"notmuch count {}".format(self.__notmuch_count_query)
).strip()
except Exception: except Exception:
self.__notmuch_count = 'n/a' self.__notmuch_count = "n/a"
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -15,11 +15,12 @@ import core.widget
import util.cli import util.cli
import util.format import util.format
class Module(core.module.Module): class Module(core.module.Module):
def __init__(self, config, theme): def __init__(self, config, theme):
super().__init__(config, theme, core.widget.Widget(self.utilization)) super().__init__(config, theme, core.widget.Widget(self.utilization))
self.__utilization = 'Not found: 0 0/0' self.__utilization = "Not found: 0 0/0"
def utilization(self, widget): def utilization(self, widget):
return self.__utilization return self.__utilization
@ -28,41 +29,43 @@ class Module(core.module.Module):
return "not found" in self.__utilization return "not found" in self.__utilization
def update(self): def update(self):
sp = util.cli.execute('nvidia-smi -q', ignore_errors=True) sp = util.cli.execute("nvidia-smi -q", ignore_errors=True)
title = '' title = ""
usedMem = '' usedMem = ""
totalMem = '' totalMem = ""
temp = '' temp = ""
name = 'not found' name = "not found"
clockMem = '' clockMem = ""
clockGpu = '' clockGpu = ""
fanspeed = '' fanspeed = ""
for item in sp.split('\n'): for item in sp.split("\n"):
try: try:
key, val = item.split(':') key, val = item.split(":")
key, val = key.strip(), val.strip() key, val = key.strip(), val.strip()
if title == 'Clocks': if title == "Clocks":
if key == 'Graphics': if key == "Graphics":
clockGpu = val.split(' ')[0] clockGpu = val.split(" ")[0]
elif key == 'Memory': elif key == "Memory":
clockMem = val.split(' ')[0] clockMem = val.split(" ")[0]
if title == 'FB Memory Usage': if title == "FB Memory Usage":
if key == 'Total': if key == "Total":
totalMem = val.split(' ')[0] totalMem = val.split(" ")[0]
elif key == 'Used': elif key == "Used":
usedMem = val.split(' ')[0] usedMem = val.split(" ")[0]
elif key == 'GPU Current Temp': elif key == "GPU Current Temp":
temp = val.split(' ')[0] temp = val.split(" ")[0]
elif key == 'Product Name': elif key == "Product Name":
name = val name = val
elif key == 'Fan Speed': elif key == "Fan Speed":
fanspeed = val.split(' ')[0] fanspeed = val.split(" ")[0]
except: except:
title = item.strip() title = item.strip()
str_format = self.parameter('format', '{name}: {temp}°C {mem_used}/{mem_total} MiB') str_format = self.parameter(
"format", "{name}: {temp}°C {mem_used}/{mem_total} MiB"
)
self.__utilization = str_format.format( self.__utilization = str_format.format(
name=name, name=name,
temp=temp, temp=temp,
@ -73,4 +76,5 @@ class Module(core.module.Module):
fanspeed=fanspeed, fanspeed=fanspeed,
) )
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -27,13 +27,14 @@ import core.module
import core.widget import core.widget
import core.input import core.input
def get_frame(url): def get_frame(url):
img_bytes = b"" img_bytes = b""
stream = urllib.request.urlopen(url) stream = urllib.request.urlopen(url)
while True: while True:
img_bytes += stream.read(1024) img_bytes += stream.read(1024)
a = img_bytes.find(b'\xff\xd8') a = img_bytes.find(b"\xff\xd8")
b = img_bytes.find(b'\xff\xd9') b = img_bytes.find(b"\xff\xd9")
if a != -1 and b != -1: if a != -1 and b != -1:
jpg = img_bytes[a : b + 2] jpg = img_bytes[a : b + 2]
img_bytes = img_bytes[b + 2 :] img_bytes = img_bytes[b + 2 :]
@ -41,6 +42,7 @@ def get_frame(url):
return img return img
return None return None
class WebcamImagesWorker(threading.Thread): class WebcamImagesWorker(threading.Thread):
def __init__(self, url, queue): def __init__(self, url, queue):
threading.Thread.__init__(self) threading.Thread.__init__(self)
@ -48,6 +50,7 @@ class WebcamImagesWorker(threading.Thread):
self.__url = url self.__url = url
self.__queue = queue self.__queue = queue
self.__running = True self.__running = True
def run(self): def run(self):
while self.__running: while self.__running:
img = get_frame(self.__url) img = get_frame(self.__url)
@ -56,6 +59,7 @@ class WebcamImagesWorker(threading.Thread):
def stop(self): def stop(self):
self.__running = False self.__running = False
class Module(core.module.Module): class Module(core.module.Module):
@core.decorators.every(seconds=5) @core.decorators.every(seconds=5)
def __init__(self, config, theme): def __init__(self, config, theme):
@ -75,13 +79,20 @@ class Module(core.module.Module):
self.update_status() self.update_status()
core.input.register(self, button=core.input.LEFT_MOUSE, core.input.register(self, button=core.input.LEFT_MOUSE, cmd=self.__show_popup)
cmd=self.__show_popup)
def octoprint_status(self, widget): def octoprint_status(self, widget):
if self.__octoprint_state == "Offline" or self.__octoprint_state == "Unknown": if self.__octoprint_state == "Offline" or self.__octoprint_state == "Unknown":
return self.__octoprint_state return self.__octoprint_state
return self.__octoprint_state + " | B: " + str(self.__printer_bed_temperature) + "°C" + " | T1: " + str(self.__tool1_temperature) + "°C" return (
self.__octoprint_state
+ " | B: "
+ str(self.__printer_bed_temperature)
+ "°C"
+ " | T1: "
+ str(self.__tool1_temperature)
+ "°C"
)
def __get(self, endpoint): def __get(self, endpoint):
url = self.__octoprint_address + "/api/" + endpoint url = self.__octoprint_address + "/api/" + endpoint
@ -96,7 +107,10 @@ class Module(core.module.Module):
def __get_printer_bed_temperature(self): def __get_printer_bed_temperature(self):
printer_info, status_code = self.__get("printer") printer_info, status_code = self.__get("printer")
if status_code == 200: if status_code == 200:
return printer_info["temperature"]["bed"]["actual"], printer_info["temperature"]["bed"]["target"] return (
printer_info["temperature"]["bed"]["actual"],
printer_info["temperature"]["bed"]["target"],
)
return None, None return None, None
def __get_octoprint_state(self): def __get_octoprint_state(self):
@ -149,7 +163,9 @@ class Module(core.module.Module):
root.after(5, self.__refresh_image, root, webcam_image, webcam_image_container) root.after(5, self.__refresh_image, root, webcam_image, webcam_image_container)
def __refresh_temperatures(self, root, printer_bed_temperature_label, tools_temperature_label): def __refresh_temperatures(
self, root, printer_bed_temperature_label, tools_temperature_label
):
actual_bed_temp, target_bed_temp = self.__get_printer_bed_temperature() actual_bed_temp, target_bed_temp = self.__get_printer_bed_temperature()
if actual_bed_temp is None: if actual_bed_temp is None:
actual_bed_temp = "-" actual_bed_temp = "-"
@ -159,7 +175,6 @@ class Module(core.module.Module):
bed_temp = "Bed: " + str(actual_bed_temp) + "/" + str(target_bed_temp) + " °C" bed_temp = "Bed: " + str(actual_bed_temp) + "/" + str(target_bed_temp) + " °C"
printer_bed_temperature_label.config(text=bed_temp) printer_bed_temperature_label.config(text=bed_temp)
tool_temperatures = self.__get_tool_temperatures() tool_temperatures = self.__get_tool_temperatures()
tools_temp = "Tools: " tools_temp = "Tools: "
@ -167,16 +182,24 @@ class Module(core.module.Module):
tools_temp += "-/- °C" tools_temp += "-/- °C"
else: else:
for i, tool_temperature in enumerate(tool_temperatures): for i, tool_temperature in enumerate(tool_temperatures):
tools_temp += str(tool_temperature[0]) + "/" + str(tool_temperature[1]) + "°C" tools_temp += (
str(tool_temperature[0]) + "/" + str(tool_temperature[1]) + "°C"
)
if i != len(tool_temperatures) - 1: if i != len(tool_temperatures) - 1:
tools_temp += "\t" tools_temp += "\t"
tools_temperature_label.config(text=tools_temp) tools_temperature_label.config(text=tools_temp)
root.after(500, self.__refresh_temperatures, root, printer_bed_temperature_label, tools_temperature_label) root.after(
500,
self.__refresh_temperatures,
root,
printer_bed_temperature_label,
tools_temperature_label,
)
def __show_popup(self, widget): def __show_popup(self, widget):
root = tk.Tk() root = tk.Tk()
root.attributes('-type', 'dialog') root.attributes("-type", "dialog")
root.title("Octoprint") root.title("Octoprint")
frame = tk.Frame(root) frame = tk.Frame(root)
if self.__octoprint_webcam: if self.__octoprint_webcam:
@ -189,26 +212,38 @@ class Module(core.module.Module):
self.__webcam_images_queue = queue.Queue() self.__webcam_images_queue = queue.Queue()
self.__webcam_images_worker = WebcamImagesWorker(self.__webcam_image_url, self.__webcam_images_queue) self.__webcam_images_worker = WebcamImagesWorker(
self.__webcam_image_url, self.__webcam_images_queue
)
self.__webcam_images_worker.start() self.__webcam_images_worker.start()
else: else:
logging.debug("Not using webcam, as webcam is disabled. Enable with --webcam.") logging.debug(
"Not using webcam, as webcam is disabled. Enable with --webcam."
)
frame.pack() frame.pack()
temperatures_label = tk.Label(frame, text="Temperatures", font=('', 25)) temperatures_label = tk.Label(frame, text="Temperatures", font=("", 25))
temperatures_label.pack() temperatures_label.pack()
printer_bed_temperature_label = tk.Label(frame, text="Bed: -/- °C", font=('', 15)) printer_bed_temperature_label = tk.Label(
frame, text="Bed: -/- °C", font=("", 15)
)
printer_bed_temperature_label.pack() printer_bed_temperature_label.pack()
tools_temperature_label = tk.Label(frame, text="Tools: -/- °C", font=('', 15)) tools_temperature_label = tk.Label(frame, text="Tools: -/- °C", font=("", 15))
tools_temperature_label.pack() tools_temperature_label.pack()
root.after(10, self.__refresh_image, root, webcam_image, webcam_image_container) root.after(10, self.__refresh_image, root, webcam_image, webcam_image_container)
root.after(500, self.__refresh_temperatures, root, printer_bed_temperature_label, tools_temperature_label) root.after(
500,
self.__refresh_temperatures,
root,
printer_bed_temperature_label,
tools_temperature_label,
)
root.bind("<Destroy>", self.__on_close_popup) root.bind("<Destroy>", self.__on_close_popup)
root.eval('tk::PlaceWindow . center') root.eval("tk::PlaceWindow . center")
root.mainloop() root.mainloop()
def __on_close_popup(self, event): def __on_close_popup(self, event):
@ -221,4 +256,5 @@ class Module(core.module.Module):
def state(self, widget): def state(self, widget):
return [] return []
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -22,20 +22,21 @@ import util.format
# list of repositories. # list of repositories.
# the last one should always be other # the last one should always be other
repos = ['core', 'extra', 'community', 'multilib', 'testing', 'other'] repos = ["core", "extra", "community", "multilib", "testing", "other"]
def get_pacman_info(widget, path): def get_pacman_info(widget, path):
cmd = '{}/../../bin/pacman-updates'.format(path) cmd = "{}/../../bin/pacman-updates".format(path)
if not os.path.exists(cmd): if not os.path.exists(cmd):
cmd = '/usr/share/bumblebee-status/bin/pacman-update' cmd = "/usr/share/bumblebee-status/bin/pacman-update"
result = util.cli.execute(cmd, ignore_errors=True) result = util.cli.execute(cmd, ignore_errors=True)
count = len(repos) * [0] count = len(repos) * [0]
for line in result.splitlines(): for line in result.splitlines():
if line.startswith(('http', 'rsync')): if line.startswith(("http", "rsync")):
for i in range(len(repos) - 1): for i in range(len(repos) - 1):
if '/' + repos[i] + '/' in line: if "/" + repos[i] + "/" in line:
count[i] += 1 count[i] += 1
break break
else: else:
@ -43,7 +44,8 @@ def get_pacman_info(widget, path):
for i in range(len(repos)): for i in range(len(repos)):
widget.set(repos[i], count[i]) widget.set(repos[i], count[i])
core.event.trigger('update', [ widget.module.id ], redraw_only=True) core.event.trigger("update", [widget.module.id], redraw_only=True)
class Module(core.module.Module): class Module(core.module.Module):
@core.decorators.every(minutes=30) @core.decorators.every(minutes=30)
@ -51,9 +53,9 @@ class Module(core.module.Module):
super().__init__(config, theme, core.widget.Widget(self.updates)) super().__init__(config, theme, core.widget.Widget(self.updates))
def updates(self, widget): def updates(self, widget):
if util.format.asbool(self.parameter('sum')): if util.format.asbool(self.parameter("sum")):
return str(sum(map(lambda x: widget.get(x, 0), repos))) return str(sum(map(lambda x: widget.get(x, 0), repos)))
return '/'.join(map(lambda x: str(widget.get(x, 0)), repos)) return "/".join(map(lambda x: str(widget.get(x, 0)), repos))
def update(self): def update(self):
path = os.path.dirname(os.path.abspath(__file__)) path = os.path.dirname(os.path.abspath(__file__))
@ -61,11 +63,14 @@ class Module(core.module.Module):
thread.start() thread.start()
def state(self, widget): def state(self, widget):
weightedCount = sum(map(lambda x: (len(repos)-x[0]) * widget.get(x[1], 0), enumerate(repos))) weightedCount = sum(
map(lambda x: (len(repos) - x[0]) * widget.get(x[1], 0), enumerate(repos))
)
if weightedCount < 10: if weightedCount < 10:
return 'good' return "good"
return self.threshold_state(weightedCount, 100, 150) return self.threshold_state(weightedCount, 100, 150)
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -12,30 +12,36 @@ import core.module
import core.widget import core.widget
import core.input import core.input
class Module(core.module.Module): class Module(core.module.Module):
@core.decorators.every(minutes=1) @core.decorators.every(minutes=1)
def __init__(self, config, theme): def __init__(self, config, theme):
super().__init__(config, theme, core.widget.Widget(self.pihole_status)) super().__init__(config, theme, core.widget.Widget(self.pihole_status))
self._pihole_address = self.parameter('address', '') self._pihole_address = self.parameter("address", "")
self._pihole_pw_hash = self.parameter('pwhash', '') self._pihole_pw_hash = self.parameter("pwhash", "")
self._pihole_status = None self._pihole_status = None
self._ads_blocked_today = '-' self._ads_blocked_today = "-"
self.update_pihole_status() self.update_pihole_status()
core.input.register(self, button=core.input.LEFT_MOUSE, core.input.register(
cmd=self.toggle_pihole_status) self, button=core.input.LEFT_MOUSE, cmd=self.toggle_pihole_status
)
def pihole_status(self, widget): def pihole_status(self, widget):
if self._pihole_status is None: if self._pihole_status is None:
return 'pi-hole unknown' return "pi-hole unknown"
return 'pi-hole {}'.format('up {} blocked'.format(self._ads_blocked_today) if self._pihole_status else 'down') return "pi-hole {}".format(
"up {} blocked".format(self._ads_blocked_today)
if self._pihole_status
else "down"
)
def update_pihole_status(self): def update_pihole_status(self):
try: try:
data = requests.get(self._pihole_address + '/admin/api.php?summary').json() data = requests.get(self._pihole_address + "/admin/api.php?summary").json()
self._pihole_status = True if data['status'] == 'enabled' else False self._pihole_status = True if data["status"] == "enabled" else False
self._ads_blocked_today = data['ads_blocked_today'] self._ads_blocked_today = data["ads_blocked_today"]
except Exception as e: except Exception as e:
self._pihole_status = None self._pihole_status = None
@ -44,17 +50,24 @@ class Module(core.module.Module):
try: try:
req = None req = None
if self._pihole_status: if self._pihole_status:
req = requests.get(self._pihole_address + '/admin/api.php?disable&auth=' + self._pihole_pw_hash) req = requests.get(
self._pihole_address
+ "/admin/api.php?disable&auth="
+ self._pihole_pw_hash
)
else: else:
req = requests.get(self._pihole_address + '/admin/api.php?enable&auth=' + self._pihole_pw_hash) req = requests.get(
self._pihole_address
+ "/admin/api.php?enable&auth="
+ self._pihole_pw_hash
)
if req is not None: if req is not None:
if req.status_code == 200: if req.status_code == 200:
status = req.json()['status'] status = req.json()["status"]
self._pihole_status = False if status == 'disabled' else True self._pihole_status = False if status == "disabled" else True
except: except:
pass pass
def update(self): def update(self):
self.update_pihole_status() self.update_pihole_status()
@ -62,7 +75,8 @@ class Module(core.module.Module):
if self._pihole_status is None: if self._pihole_status is None:
return [] return []
elif self._pihole_status: elif self._pihole_status:
return ['enabled'] return ["enabled"]
return ['disabled', 'warning'] return ["disabled", "warning"]
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -26,34 +26,35 @@ import core.input
import util.cli import util.cli
class Module(core.module.Module): class Module(core.module.Module):
def __init__(self, config, theme): def __init__(self, config, theme):
super().__init__(config, theme, core.widget.Widget(self.text)) super().__init__(config, theme, core.widget.Widget(self.text))
# Parameters # Parameters
self.__work_period = int(self.parameter('work', 25)) self.__work_period = int(self.parameter("work", 25))
self.__break_period = int(self.parameter('break', 5)) self.__break_period = int(self.parameter("break", 5))
self.__time_format = self.parameter('format', '%m:%s') self.__time_format = self.parameter("format", "%m:%s")
self.__notify_cmd = self.parameter('notify', '') self.__notify_cmd = self.parameter("notify", "")
# TODO: Handle time formats more gracefully. This is kludge. # TODO: Handle time formats more gracefully. This is kludge.
self.display_seconds_p = False self.display_seconds_p = False
self.display_minutes_p = False self.display_minutes_p = False
if '%s' in self.__time_format: if "%s" in self.__time_format:
self.display_seconds_p = True self.display_seconds_p = True
if '%m' in self.__time_format: if "%m" in self.__time_format:
self.display_minutes_p = True self.display_minutes_p = True
self.remaining_time = datetime.timedelta(minutes=self.__work_period) self.remaining_time = datetime.timedelta(minutes=self.__work_period)
self.time = None self.time = None
self.pomodoro = { 'state':'OFF', 'type': ''} self.pomodoro = {"state": "OFF", "type": ""}
self.__text = self.remaining_time_str() + self.pomodoro['type'] self.__text = self.remaining_time_str() + self.pomodoro["type"]
core.input.register(self, button=core.input.LEFT_MOUSE, core.input.register(
cmd=self.timer_play_pause) self, button=core.input.LEFT_MOUSE, cmd=self.timer_play_pause
core.input.register(self, button=core.input.RIGHT_MOUSE, )
cmd=self.timer_reset) core.input.register(self, button=core.input.RIGHT_MOUSE, cmd=self.timer_reset)
def remaining_time_str(self): def remaining_time_str(self):
if self.display_seconds_p and self.display_minutes_p: if self.display_seconds_p and self.display_minutes_p:
@ -65,58 +66,62 @@ class Module(core.module.Module):
minutes = 0 minutes = 0
seconds = self.remaining_time.seconds seconds = self.remaining_time.seconds
minutes = '{:2d}'.format(minutes) minutes = "{:2d}".format(minutes)
seconds = '{:02d}'.format(seconds) seconds = "{:02d}".format(seconds)
return self.__time_format.replace('%m',minutes).replace('%s',seconds)+' ' return self.__time_format.replace("%m", minutes).replace("%s", seconds) + " "
def text(self, widget): def text(self, widget):
return '{}'.format(self.__text) return "{}".format(self.__text)
def update(self): def update(self):
if self.pomodoro['state'] == 'ON': if self.pomodoro["state"] == "ON":
timediff = (datetime.datetime.now() - self.time) timediff = datetime.datetime.now() - self.time
if timediff.seconds >= 0: if timediff.seconds >= 0:
self.remaining_time -= timediff self.remaining_time -= timediff
self.time = datetime.datetime.now() self.time = datetime.datetime.now()
if self.remaining_time.total_seconds() <= 0: if self.remaining_time.total_seconds() <= 0:
self.notify() self.notify()
if self.pomodoro['type'] == 'Work': if self.pomodoro["type"] == "Work":
self.pomodoro['type'] = 'Break' self.pomodoro["type"] = "Break"
self.remaining_time = datetime.timedelta(minutes=self.__break_period) self.remaining_time = datetime.timedelta(
elif self.pomodoro['type'] == 'Break': minutes=self.__break_period
self.pomodoro['type'] = 'Work' )
elif self.pomodoro["type"] == "Break":
self.pomodoro["type"] = "Work"
self.remaining_time = datetime.timedelta(minutes=self.__work_period) self.remaining_time = datetime.timedelta(minutes=self.__work_period)
self.__text = self.remaining_time_str() + self.pomodoro['type'] self.__text = self.remaining_time_str() + self.pomodoro["type"]
def notify(self): def notify(self):
if self.__notify_cmd: if self.__notify_cmd:
util.cli.execute(self.__notify_cmd) util.cli.execute(self.__notify_cmd)
def timer_play_pause(self, widget): def timer_play_pause(self, widget):
if self.pomodoro['state'] == 'OFF': if self.pomodoro["state"] == "OFF":
self.pomodoro = {'state': 'ON', 'type': 'Work'} self.pomodoro = {"state": "ON", "type": "Work"}
self.remaining_time = datetime.timedelta(minutes=self.__work_period) self.remaining_time = datetime.timedelta(minutes=self.__work_period)
self.time = datetime.datetime.now() self.time = datetime.datetime.now()
elif self.pomodoro['state'] == 'ON': elif self.pomodoro["state"] == "ON":
self.pomodoro['state'] = 'PAUSED' self.pomodoro["state"] = "PAUSED"
self.remaining_time -= (datetime.datetime.now() - self.time) self.remaining_time -= datetime.datetime.now() - self.time
self.time = datetime.datetime.now() self.time = datetime.datetime.now()
elif self.pomodoro['state'] == 'PAUSED': elif self.pomodoro["state"] == "PAUSED":
self.pomodoro['state'] = 'ON' self.pomodoro["state"] = "ON"
self.time = datetime.datetime.now() self.time = datetime.datetime.now()
def timer_reset(self, widget): def timer_reset(self, widget):
if self.pomodoro['state'] == 'ON' or self.pomodoro['state'] == 'PAUSED': if self.pomodoro["state"] == "ON" or self.pomodoro["state"] == "PAUSED":
self.pomodoro = {'state':'OFF', 'type': '' } self.pomodoro = {"state": "OFF", "type": ""}
self.remaining_time = datetime.timedelta(minutes=self.__work_period) self.remaining_time = datetime.timedelta(minutes=self.__work_period)
def state(self, widget): def state(self, widget):
state = []; state = []
state.append(self.pomodoro['state'].lower()) state.append(self.pomodoro["state"].lower())
if self.pomodoro['state'] == 'ON' or self.pomodoro['state'] == 'OFF': if self.pomodoro["state"] == "ON" or self.pomodoro["state"] == "OFF":
state.append(self.pomodoro['type'].lower()) state.append(self.pomodoro["type"].lower())
return state return state
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -31,36 +31,37 @@ import core.input
import util.cli import util.cli
class Module(core.module.Module): class Module(core.module.Module):
def __init__(self, config, theme): def __init__(self, config, theme):
super().__init__(config, theme, core.widget.Widget(self.query)) super().__init__(config, theme, core.widget.Widget(self.query))
core.input.register(self, button=core.input.LEFT_MOUSE, core.input.register(self, button=core.input.LEFT_MOUSE, cmd=self.__chooseNvidia)
cmd=self.__chooseNvidia) core.input.register(self, button=core.input.RIGHT_MOUSE, cmd=self.__chooseIntel)
core.input.register(self, button=core.input.RIGHT_MOUSE,
cmd=self.__chooseIntel)
self.nvidiastring = self.parameter('nvidiastring', 'nv') self.nvidiastring = self.parameter("nvidiastring", "nv")
self.intelstring = self.parameter('intelstring', 'it') self.intelstring = self.parameter("intelstring", "it")
def __chooseNvidia(self, event): def __chooseNvidia(self, event):
util.cli.execute('sudo prime-select nvidia') util.cli.execute("sudo prime-select nvidia")
def __chooseIntel(self, event): def __chooseIntel(self, event):
util.cli.execute('sudo prime-select intel') util.cli.execute("sudo prime-select intel")
def query(self, widget): def query(self, widget):
try: try:
res = util.cli.execute('prime-select query') res = util.cli.execute("prime-select query")
except RuntimeError: except RuntimeError:
return 'n/a' return "n/a"
for line in res.split('\n'): for line in res.split("\n"):
if not line: continue if not line:
if 'nvidia' in line: continue
if "nvidia" in line:
return self.nvidiastring return self.nvidiastring
if 'intel' in line: if "intel" in line:
return self.intelstring return self.intelstring
return 'n/a' return "n/a"
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -21,6 +21,7 @@ import util.format
import re import re
class Module(core.module.Module): class Module(core.module.Module):
def __init__(self, config, theme): def __init__(self, config, theme):
super().__init__(config, theme, core.widget.Widget(self.get_progress_text)) super().__init__(config, theme, core.widget.Widget(self.get_progress_text))
@ -28,33 +29,31 @@ class Module(core.module.Module):
def get_progress_text(self, widget): def get_progress_text(self, widget):
if self.update_progress_info(widget): if self.update_progress_info(widget):
width = util.format.asint(self.parameter('barwidth', 8)) width = util.format.asint(self.parameter("barwidth", 8))
count = round((width * widget.get('per')) / 100) count = round((width * widget.get("per")) / 100)
filledchar = self.parameter('barfilledchar', '#') filledchar = self.parameter("barfilledchar", "#")
emptychar = self.parameter('baremptychar', '-') emptychar = self.parameter("baremptychar", "-")
bar = '[{}{}]'.format( bar = "[{}{}]".format(filledchar * count, emptychar * (width - count))
filledchar * count,
emptychar * (width - count)
)
str_format = self.parameter('format', '{bar} {cmd} {arg}') str_format = self.parameter("format", "{bar} {cmd} {arg}")
return str_format.format( return str_format.format(
bar=bar, bar=bar,
pid = widget.get('pid'), pid=widget.get("pid"),
cmd = widget.get('cmd'), cmd=widget.get("cmd"),
arg = widget.get('arg'), arg=widget.get("arg"),
percentage = widget.get('per'), percentage=widget.get("per"),
quantity = widget.get('qty'), quantity=widget.get("qty"),
speed = widget.get('spd'), speed=widget.get("spd"),
time = widget.get('tim') time=widget.get("tim"),
) )
else: else:
return self.parameter('placeholder', 'n/a') return self.parameter("placeholder", "n/a")
def update_progress_info(self, widget): def update_progress_info(self, widget):
"""Update widget's informations about the copy""" """Update widget's informations about the copy"""
if not self.__active: return if not self.__active:
return
# These regex extracts following groups: # These regex extracts following groups:
# 1. pid # 1. pid
@ -64,39 +63,44 @@ class Module(core.module.Module):
# 5. quantity (.. unit / .. unit formated) # 5. quantity (.. unit / .. unit formated)
# 6. speed # 6. speed
# 7. time remaining # 7. time remaining
extract_nospeed = re.compile('\[ *(\d*)\] ([a-zA-Z]*) (.*)\n\t(\d*\.*\d*)% \((.*)\)\n.*') extract_nospeed = re.compile(
extract_wtspeed = re.compile('\[ *(\d*)\] ([a-zA-Z]*) (.*)\n\t(\d*\.*\d*)% \((.*)\) (\d*\.\d .*) remaining (\d*:\d*:\d*)\n.*') "\[ *(\d*)\] ([a-zA-Z]*) (.*)\n\t(\d*\.*\d*)% \((.*)\)\n.*"
)
extract_wtspeed = re.compile(
"\[ *(\d*)\] ([a-zA-Z]*) (.*)\n\t(\d*\.*\d*)% \((.*)\) (\d*\.\d .*) remaining (\d*:\d*:\d*)\n.*"
)
try: try:
raw = util.cli.execute('progress -qW 0.1') raw = util.cli.execute("progress -qW 0.1")
result = extract_wtspeed.match(raw) result = extract_wtspeed.match(raw)
if not result: if not result:
# Abord speed measures # Abord speed measures
raw = util.cli.execute('progress -q') raw = util.cli.execute("progress -q")
result = extract_nospeed.match(raw) result = extract_nospeed.match(raw)
widget.set('spd', '???.? B/s') widget.set("spd", "???.? B/s")
widget.set('tim', '??:??:??') widget.set("tim", "??:??:??")
else: else:
widget.set('spd', result.group(6)) widget.set("spd", result.group(6))
widget.set('tim', result.group(7)) widget.set("tim", result.group(7))
widget.set('pid', int(result.group(1))) widget.set("pid", int(result.group(1)))
widget.set('cmd', result.group(2)) widget.set("cmd", result.group(2))
widget.set('arg', result.group(3)) widget.set("arg", result.group(3))
widget.set('per', float(result.group(4))) widget.set("per", float(result.group(4)))
widget.set('qty', result.group(5)) widget.set("qty", result.group(5))
return True return True
except Exception: except Exception:
return False return False
def update(self): def update(self):
self.__active = bool(util.cli.execute('progress -q')) self.__active = bool(util.cli.execute("progress -q"))
def state(self, widget): def state(self, widget):
if self.__active: if self.__active:
return 'copying' return "copying"
return 'pending' return "pending"
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -7,13 +7,13 @@ import core.decorators
import util.location import util.location
class Module(core.module.Module): class Module(core.module.Module):
@core.decorators.every(minutes=60) @core.decorators.every(minutes=60)
def __init__(self, config, theme): def __init__(self, config, theme):
super().__init__(config, theme, core.widget.Widget(self.public_ip)) super().__init__(config, theme, core.widget.Widget(self.public_ip))
self.__ip = '' self.__ip = ""
def public_ip(self, widget): def public_ip(self, widget):
return self.__ip return self.__ip
@ -22,6 +22,7 @@ class Module(core.module.Module):
try: try:
self.__ip = util.location.public_ip() self.__ip = util.location.public_ip()
except Exception: except Exception:
self.__ip = 'n/a' self.__ip = "n/a"
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -12,7 +12,8 @@ import core.input
import util.cli import util.cli
possible_orientations = ['normal', 'left', 'inverted', 'right'] possible_orientations = ["normal", "left", "inverted", "right"]
class Module(core.module.Module): class Module(core.module.Module):
def __init__(self, config, theme): def __init__(self, config, theme):
@ -20,37 +21,42 @@ class Module(core.module.Module):
def update(self): def update(self):
widgets = self.widgets() widgets = self.widgets()
for line in util.cli.execute('xrandr -q').split('\n'): for line in util.cli.execute("xrandr -q").split("\n"):
if not ' connected' in line: if not " connected" in line:
continue continue
display = line.split(' ', 2)[0] display = line.split(" ", 2)[0]
orientation = 'normal' orientation = "normal"
for curr_orient in possible_orientations: for curr_orient in possible_orientations:
if((line.split(' ')).count(curr_orient) > 1): if (line.split(" ")).count(curr_orient) > 1:
orientation = curr_orient orientation = curr_orient
break break
widget = self.widget(display) widget = self.widget(display)
if not widget: if not widget:
widget = core.widget.Widget(full_text=display, name=display) widget = core.widget.Widget(full_text=display, name=display)
core.input.register(widget, button=core.input.LEFT_MOUSE, cmd=self.__toggle) core.input.register(
widget.set('orientation', orientation) widget, button=core.input.LEFT_MOUSE, cmd=self.__toggle
)
widget.set("orientation", orientation)
widgets.append(widget) widgets.append(widget)
def state(self, widget): def state(self, widget):
return widget.get('orientation', 'normal') return widget.get("orientation", "normal")
def __toggle(self, event): def __toggle(self, event):
widget = self.widget_by_id(event['instance']) widget = self.widget_by_id(event["instance"])
# compute new orientation based on current orientation # compute new orientation based on current orientation
idx = possible_orientations.index(widget.get('orientation')) idx = possible_orientations.index(widget.get("orientation"))
idx = (idx + 1) % len(possible_orientations) idx = (idx + 1) % len(possible_orientations)
new_orientation = possible_orientations[idx] new_orientation = possible_orientations[idx]
widget.set('orientation', new_orientation) widget.set("orientation", new_orientation)
util.cli.execute(
"xrandr --output {} --rotation {}".format(widget.name, new_orientation)
)
util.cli.execute('xrandr --output {} --rotation {}'.format(widget.name, new_orientation))
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -31,16 +31,18 @@ class Module(core.module.Module):
REFRESH_DELAY = 600 REFRESH_DELAY = 600
SCROLL_SPEED = 3 SCROLL_SPEED = 3
LAYOUT_STYLES_ITEMS = [[1, 1, 1], [3, 3, 2], [2, 3, 3], [3, 2, 3]] LAYOUT_STYLES_ITEMS = [[1, 1, 1], [3, 3, 2], [2, 3, 3], [3, 2, 3]]
HISTORY_FILENAME = '.config/i3/rss.hist' HISTORY_FILENAME = ".config/i3/rss.hist"
def __init__(self, config, theme): def __init__(self, config, theme):
super().__init__(config, theme, core.widget.Widget(self.ticker_update)) super().__init__(config, theme, core.widget.Widget(self.ticker_update))
self._feeds = self.parameter('feeds', 'https://www.espn.com/espn/rss/news').split(' ') self._feeds = self.parameter(
"feeds", "https://www.espn.com/espn/rss/news"
).split(" ")
self._feeds_to_update = [] self._feeds_to_update = []
self._response = '' self._response = ""
self._max_title_length = int(self.parameter('length', 60)) self._max_title_length = int(self.parameter("length", 60))
self._items = [] self._items = []
self._current_item = None self._current_item = None
@ -51,76 +53,103 @@ class Module(core.module.Module):
self._state = [] self._state = []
self._newspaper_filename = tempfile.mktemp('.html') self._newspaper_filename = tempfile.mktemp(".html")
self._last_refresh = 0 self._last_refresh = 0
self._last_update = 0 self._last_update = 0
core.input.register(self, button=core.input.LEFT_MOUSE, cmd=self._open) core.input.register(self, button=core.input.LEFT_MOUSE, cmd=self._open)
core.input.register(self, button=core.input.RIGHT_MOUSE, cmd=self._create_newspaper) core.input.register(
self, button=core.input.RIGHT_MOUSE, cmd=self._create_newspaper
)
self._history = {'ticker': {}, 'newspaper': {}} self._history = {"ticker": {}, "newspaper": {}}
self._load_history() self._load_history()
def _load_history(self): def _load_history(self):
if os.path.isfile(self.HISTORY_FILENAME): if os.path.isfile(self.HISTORY_FILENAME):
self._history = json.loads(open(self.HISTORY_FILENAME, 'r').read()) self._history = json.loads(open(self.HISTORY_FILENAME, "r").read())
def _update_history(self, group): def _update_history(self, group):
sources = set([i['source'] for i in self._items]) sources = set([i["source"] for i in self._items])
self._history[group] = dict([[s, [i['title'] for i in self._items if i['source'] == s]] for s in sources]) self._history[group] = dict(
[
[s, [i["title"] for i in self._items if i["source"] == s]]
for s in sources
]
)
def _save_history(self): def _save_history(self):
if not os.path.exists(os.path.dirname(self.HISTORY_FILENAME)): if not os.path.exists(os.path.dirname(self.HISTORY_FILENAME)):
os.makedirs(os.path.dirname(self.HISTORY_FILENAME)) os.makedirs(os.path.dirname(self.HISTORY_FILENAME))
open(self.HISTORY_FILENAME, 'w').write(json.dumps(self._history)) open(self.HISTORY_FILENAME, "w").write(json.dumps(self._history))
def _check_history(self, items, group): def _check_history(self, items, group):
for i in items: for i in items:
i['new'] = not (i['source'] in self._history[group] and i['title'] in self._history[group][i['source']]) i["new"] = not (
i["source"] in self._history[group]
and i["title"] in self._history[group][i["source"]]
)
def _open(self, _): def _open(self, _):
if self._current_item: if self._current_item:
webbrowser.open(self._current_item['link']) webbrowser.open(self._current_item["link"])
def _check_for_image(self, entry): def _check_for_image(self, entry):
image = next(iter([l['href'] for l in entry['links'] if l['rel'] == 'enclosure']), None) image = next(
if not image and 'media_content' in entry: iter([l["href"] for l in entry["links"] if l["rel"] == "enclosure"]), None
)
if not image and "media_content" in entry:
try: try:
media = sorted(entry['media_content'], key=lambda i: i['height'] if 'height' in i else 0, reverse=True) media = sorted(
image = next(iter([i['url'] for i in media if i['medium'] == 'image']), None) entry["media_content"],
key=lambda i: i["height"] if "height" in i else 0,
reverse=True,
)
image = next(
iter([i["url"] for i in media if i["medium"] == "image"]), None
)
except Exception: except Exception:
pass pass
if not image: if not image:
match = re.search(r"<img[^>]*src\s*=['\']*([^\s^>^'^\']*)['\']*", entry['summary']) match = re.search(
r"<img[^>]*src\s*=['\']*([^\s^>^'^\']*)['\']*", entry["summary"]
)
if match: if match:
image = match.group(1) image = match.group(1)
return image if image else '' return image if image else ""
def _remove_tags(self, txt): def _remove_tags(self, txt):
return re.sub('<[^>]*>', '', txt) return re.sub("<[^>]*>", "", txt)
def _create_item(self, entry, url, feed): def _create_item(self, entry, url, feed):
return {'title': self._remove_tags(entry['title'].replace('\n', ' ')), return {
'link': entry['link'], "title": self._remove_tags(entry["title"].replace("\n", " ")),
'new': True, "link": entry["link"],
'source': url, "new": True,
'summary': self._remove_tags(entry['summary']), "source": url,
'feed': feed, "summary": self._remove_tags(entry["summary"]),
'image': self._check_for_image(entry), "feed": feed,
'published': time.mktime(entry.published_parsed) if hasattr(entry, 'published_parsed') else 0} "image": self._check_for_image(entry),
"published": time.mktime(entry.published_parsed)
if hasattr(entry, "published_parsed")
else 0,
}
def _update_items_from_feed(self, url): def _update_items_from_feed(self, url):
parser = feedparser.parse(url) parser = feedparser.parse(url)
new_items = [self._create_item(entry, url, parser['feed']['title']) for entry in parser['entries']] new_items = [
self._create_item(entry, url, parser["feed"]["title"])
for entry in parser["entries"]
]
# Check history # Check history
self._check_history(new_items, 'ticker') self._check_history(new_items, "ticker")
# Remove the previous items # Remove the previous items
self._items = [i for i in self._items if i['source'] != url] self._items = [i for i in self._items if i["source"] != url]
# Add the new items # Add the new items
self._items.extend(new_items) self._items.extend(new_items)
# Sort the items on publish date # Sort the items on publish date
self._items.sort(key=lambda i: i['published'], reverse=True) self._items.sort(key=lambda i: i["published"], reverse=True)
def _check_for_refresh(self): def _check_for_refresh(self):
if self._feeds_to_update: if self._feeds_to_update:
@ -129,7 +158,7 @@ class Module(core.module.Module):
self._update_items_from_feed(url) self._update_items_from_feed(url)
if not self._feeds_to_update: if not self._feeds_to_update:
self._update_history('ticker') self._update_history("ticker")
self._save_history() self._save_history()
if not self._current_item: if not self._current_item:
@ -149,19 +178,27 @@ class Module(core.module.Module):
return return
# Index of the current element # Index of the current element
idx = self._items.index(self._current_item) if self._current_item in self._items else - 1 idx = (
self._items.index(self._current_item)
if self._current_item in self._items
else -1
)
# First show new items, else show next # First show new items, else show next
new_items = [i for i in self._items if i['new']] new_items = [i for i in self._items if i["new"]]
self._current_item = next(iter(new_items), self._items[(idx+1) % len(self._items)]) self._current_item = next(
iter(new_items), self._items[(idx + 1) % len(self._items)]
)
def _check_scroll_done(self): def _check_scroll_done(self):
# Check if the complete title has been shown # Check if the complete title has been shown
if self._ticker_offset + self._max_title_length > len(self._current_item['title']): if self._ticker_offset + self._max_title_length > len(
self._current_item["title"]
):
# Do not immediately show next item after scroll # Do not immediately show next item after scroll
self._post_delay -= 1 self._post_delay -= 1
if self._post_delay == 0: if self._post_delay == 0:
self._current_item['new'] = False self._current_item["new"] = False
# Mark the previous item as 'old' # Mark the previous item as 'old'
self._next_item() self._next_item()
else: else:
@ -180,18 +217,20 @@ class Module(core.module.Module):
# If no items were retrieved, return an empty string # If no items were retrieved, return an empty string
if not self._current_item: if not self._current_item:
return ' '*self._max_title_length return " " * self._max_title_length
# Prepare a substring of the item title # Prepare a substring of the item title
self._response = self._current_item['title'][self._ticker_offset:self._ticker_offset+self._max_title_length] self._response = self._current_item["title"][
self._ticker_offset : self._ticker_offset + self._max_title_length
]
# Add spaces if too short # Add spaces if too short
self._response = self._response.ljust(self._max_title_length) self._response = self._response.ljust(self._max_title_length)
# Do not immediately scroll # Do not immediately scroll
if self._pre_delay > 0: if self._pre_delay > 0:
# Change state during pre_delay for new items # Change state during pre_delay for new items
if self._current_item['new']: if self._current_item["new"]:
self._state = ['warning'] self._state = ["warning"]
self._pre_delay -= 1 self._pre_delay -= 1
return self._response return self._response
@ -205,17 +244,38 @@ class Module(core.module.Module):
def _create_news_element(self, item, overlay_title): def _create_news_element(self, item, overlay_title):
try: try:
timestr = '' if item['published'] == 0 else str(time.ctime(item['published'])) timestr = (
"" if item["published"] == 0 else str(time.ctime(item["published"]))
)
except Exception as exc: except Exception as exc:
logging.error(str(exc)) logging.error(str(exc))
raise e raise e
element = "<div class='item' onclick=window.open('"+item['link']+"')>" element = "<div class='item' onclick=window.open('" + item["link"] + "')>"
element += "<div class='titlecontainer'>" element += "<div class='titlecontainer'>"
element += " <img "+("" if item['image'] else "class='noimg' ")+"src='"+item['image']+"'>" element += (
element += " <div class='title"+(" overlay" if overlay_title else "")+"'>"+("<span class='star'>&#x2605;</span>" if item['new'] else "")+item['title']+"</div>" " <img "
+ ("" if item["image"] else "class='noimg' ")
+ "src='"
+ item["image"]
+ "'>"
)
element += (
" <div class='title"
+ (" overlay" if overlay_title else "")
+ "'>"
+ ("<span class='star'>&#x2605;</span>" if item["new"] else "")
+ item["title"]
+ "</div>"
)
element += "</div>" element += "</div>"
element += "<div class='summary'>"+item['summary']+"</div>" element += "<div class='summary'>" + item["summary"] + "</div>"
element += "<div class='info'><span class='author'>"+item['feed']+"</span><span class='published'>"+timestr+"</span></div>" element += (
"<div class='info'><span class='author'>"
+ item["feed"]
+ "</span><span class='published'>"
+ timestr
+ "</span></div>"
)
element += "</div>" element += "</div>"
return element return element
@ -226,27 +286,34 @@ class Module(core.module.Module):
section += "<td><div class='itemcontainer'>" section += "<td><div class='itemcontainer'>"
for _ in range(0, self.LAYOUT_STYLES_ITEMS[style][i]): for _ in range(0, self.LAYOUT_STYLES_ITEMS[style][i]):
if newspaper_items: if newspaper_items:
section += self._create_news_element(newspaper_items[0], self.LAYOUT_STYLES_ITEMS[style][i] != 3) section += self._create_news_element(
newspaper_items[0], self.LAYOUT_STYLES_ITEMS[style][i] != 3
)
del newspaper_items[0] del newspaper_items[0]
section += "</div></td>" section += "</div></td>"
section += "</tr></table>" section += "</tr></table>"
return section return section
def _create_newspaper(self, _): def _create_newspaper(self, _):
content = '' content = ""
newspaper_items = self._items[:] newspaper_items = self._items[:]
self._check_history(newspaper_items, 'newspaper') self._check_history(newspaper_items, "newspaper")
# Make sure new items are always listed first, independent of publish date # Make sure new items are always listed first, independent of publish date
newspaper_items.sort(key=lambda i: i['published']+(10000000 if i['new'] else 0), reverse=True) newspaper_items.sort(
key=lambda i: i["published"] + (10000000 if i["new"] else 0), reverse=True
)
while newspaper_items: while newspaper_items:
content += self._create_news_section(newspaper_items) content += self._create_news_section(newspaper_items)
open(self._newspaper_filename, 'w').write(HTML_TEMPLATE.replace('[[CONTENT]]', content)) open(self._newspaper_filename, "w").write(
webbrowser.open('file://'+self._newspaper_filename) HTML_TEMPLATE.replace("[[CONTENT]]", content)
self._update_history('newspaper') )
webbrowser.open("file://" + self._newspaper_filename)
self._update_history("newspaper")
self._save_history() self._save_history()
HTML_TEMPLATE = """<!DOCTYPE html> HTML_TEMPLATE = """<!DOCTYPE html>
<html> <html>
<head> <head>

View file

@ -27,105 +27,119 @@ import core.input
import util.cli import util.cli
import util.format import util.format
class Module(core.module.Module): class Module(core.module.Module):
def __init__(self, config, theme): def __init__(self, config, theme):
super().__init__(config, theme, core.widget.Widget(self.temperature)) super().__init__(config, theme, core.widget.Widget(self.temperature))
self._temperature = 'unknown' self._temperature = "unknown"
self._mhz = 'n/a' self._mhz = "n/a"
self._match_number = int(self.parameter('match_number', '-1')) self._match_number = int(self.parameter("match_number", "-1"))
self._match_pattern = self.parameter('match_pattern', None) self._match_pattern = self.parameter("match_pattern", None)
self._pattern = re.compile(r'^\s*{}:\s*([\d.]+)$'.format(self.parameter('match', 'temp1_input')), re.MULTILINE) self._pattern = re.compile(
self._json = util.format.asbool(self.parameter('json', False)) r"^\s*{}:\s*([\d.]+)$".format(self.parameter("match", "temp1_input")),
self._freq = util.format.asbool(self.parameter('show_freq', True)) re.MULTILINE,
core.input.register(self, button=core.input.LEFT_MOUSE, cmd='xsensors') )
self._json = util.format.asbool(self.parameter("json", False))
self._freq = util.format.asbool(self.parameter("show_freq", True))
core.input.register(self, button=core.input.LEFT_MOUSE, cmd="xsensors")
self.determine_method() self.determine_method()
def determine_method(self): def determine_method(self):
if self.parameter('path') != None and self._json == False: if self.parameter("path") != None and self._json == False:
self.use_sensors = False # use thermal zone self.use_sensors = False # use thermal zone
else: else:
# try to use output of sensors -u # try to use output of sensors -u
try: try:
output = util.cli.execute('sensors -u') output = util.cli.execute("sensors -u")
self.use_sensors = True self.use_sensors = True
log.debug('Sensors command available') log.debug("Sensors command available")
except FileNotFoundError as e: except FileNotFoundError as e:
log.info('Sensors command not available, using /sys/class/thermal/thermal_zone*/') log.info(
"Sensors command not available, using /sys/class/thermal/thermal_zone*/"
)
self.use_sensors = False self.use_sensors = False
def _get_temp_from_sensors(self): def _get_temp_from_sensors(self):
if self._json == True: if self._json == True:
try: try:
output = json.loads(util.cli.execute('sensors -j')) output = json.loads(util.cli.execute("sensors -j"))
for key in self.parameter('path').split('/'): for key in self.parameter("path").split("/"):
output = output[key] output = output[key]
return int(float(output)) return int(float(output))
except Exception as e: except Exception as e:
logging.error('unable to read sensors: {}'.format(str(e))) logging.error("unable to read sensors: {}".format(str(e)))
return 'unknown' return "unknown"
else: else:
output = util.cli.execute('sensors -u') output = util.cli.execute("sensors -u")
if self._match_pattern: if self._match_pattern:
temp_pattern = self.parameter('match', 'temp1_input') temp_pattern = self.parameter("match", "temp1_input")
match = re.search(r'{}.+{}:\s*([\d.]+)$'.format(self._match_pattern, temp_pattern), output.replace('\n', '')) match = re.search(
r"{}.+{}:\s*([\d.]+)$".format(self._match_pattern, temp_pattern),
output.replace("\n", ""),
)
if match: if match:
return int(float(match.group(1))) return int(float(match.group(1)))
else: else:
return 'unknown' return "unknown"
match = self._pattern.findall(output) match = self._pattern.findall(output)
if match: if match:
return int(float(match[self._match_number])) return int(float(match[self._match_number]))
return 'unknown' return "unknown"
def get_temp(self): def get_temp(self):
if self.use_sensors: if self.use_sensors:
temperature = self._get_temp_from_sensors() temperature = self._get_temp_from_sensors()
log.debug('Retrieve temperature from sensors -u') log.debug("Retrieve temperature from sensors -u")
else: else:
try: try:
temperature = open(self.parameter('path', '/sys/class/thermal/thermal_zone0/temp')).read()[:2] temperature = open(
log.debug('retrieved temperature from /sys/class/') self.parameter("path", "/sys/class/thermal/thermal_zone0/temp")
).read()[:2]
log.debug("retrieved temperature from /sys/class/")
# TODO: Iterate through all thermal zones to determine the correct one and use its value # TODO: Iterate through all thermal zones to determine the correct one and use its value
# https://unix.stackexchange.com/questions/304845/discrepancy-between-number-of-cores-and-thermal-zones-in-sys-class-thermal # https://unix.stackexchange.com/questions/304845/discrepancy-between-number-of-cores-and-thermal-zones-in-sys-class-thermal
except IOError: except IOError:
temperature = 'unknown' temperature = "unknown"
log.info('Can not determine temperature, please install lm-sensors') log.info("Can not determine temperature, please install lm-sensors")
return temperature return temperature
def get_mhz(self): def get_mhz(self):
mhz = None mhz = None
try: try:
output = open('/sys/devices/system/cpu/cpufreq/policy0/scaling_cur_freq').read() output = open(
"/sys/devices/system/cpu/cpufreq/policy0/scaling_cur_freq"
).read()
mhz = int(float(output) / 1000.0) mhz = int(float(output) / 1000.0)
except: except:
output = open('/proc/cpuinfo').read() output = open("/proc/cpuinfo").read()
m = re.search(r'cpu MHz\s+:\s+(\d+)', output) m = re.search(r"cpu MHz\s+:\s+(\d+)", output)
if m: if m:
mhz = int(m.group(1)) mhz = int(m.group(1))
else: else:
m = re.search(r'BogoMIPS\s+:\s+(\d+)', output) m = re.search(r"BogoMIPS\s+:\s+(\d+)", output)
if m: if m:
return '{} BogoMIPS'.format(int(m.group(1))) return "{} BogoMIPS".format(int(m.group(1)))
if not mhz: if not mhz:
return 'n/a' return "n/a"
if mhz < 1000: if mhz < 1000:
return '{} MHz'.format(mhz) return "{} MHz".format(mhz)
else: else:
return '{:0.01f} GHz'.format(float(mhz)/1000.0) return "{:0.01f} GHz".format(float(mhz) / 1000.0)
def temperature(self, _): def temperature(self, _):
if self._freq: if self._freq:
return u'{}°c @ {}'.format(self._temperature, self._mhz) return "{}°c @ {}".format(self._temperature, self._mhz)
else: else:
return u'{}°c'.format(self._temperature) return "{}°c".format(self._temperature)
def update(self): def update(self):
self._temperature = self.get_temp() self._temperature = self.get_temp()
if self._freq: if self._freq:
self._mhz = self.get_mhz() self._mhz = self.get_mhz()
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -33,15 +33,16 @@ import core.input
import util.format import util.format
import util.cli import util.cli
class Module(core.module.Module): class Module(core.module.Module):
def __init__(self, config, theme): def __init__(self, config, theme):
super().__init__(config, theme, core.widget.Widget(self.get_output)) super().__init__(config, theme, core.widget.Widget(self.get_output))
self.__command = self.parameter('command', 'echo "no command configured"') self.__command = self.parameter("command", 'echo "no command configured"')
self.__async = util.format.asbool(self.parameter('async')) self.__async = util.format.asbool(self.parameter("async"))
if self.__async: if self.__async:
self.__output = 'please wait...' self.__output = "please wait..."
self.__current_thread = threading.Thread() self.__current_thread = threading.Thread()
# LMB and RMB will update output regardless of timer # LMB and RMB will update output regardless of timer
@ -66,13 +67,16 @@ class Module(core.module.Module):
# spawn new thread to execute command and pass callback method to get output from it # spawn new thread to execute command and pass callback method to get output from it
self.__current_thread = threading.Thread( self.__current_thread = threading.Thread(
target=lambda obj, cmd: obj.set_output(util.cli.execute(cmd, ignore_errors=True)), target=lambda obj, cmd: obj.set_output(
args=(self, self.__command) util.cli.execute(cmd, ignore_errors=True)
),
args=(self, self.__command),
) )
self.__current_thread.start() self.__current_thread.start()
def state(self, _): def state(self, _):
if self.__output == 'no command configured': if self.__output == "no command configured":
return 'warning' return "warning"
# vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4 # vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4

View file

@ -19,22 +19,23 @@ Parameters:
import logging import logging
LINK = 'https://github.com/tobi-wan-kenobi/bumblebee-status/wiki' LINK = "https://github.com/tobi-wan-kenobi/bumblebee-status/wiki"
LABEL = 'Click me' LABEL = "Click me"
import core.module import core.module
import core.widget import core.widget
import core.input import core.input
import core.decorators import core.decorators
class Module(core.module.Module): class Module(core.module.Module):
@core.decorators.every(minutes=60) @core.decorators.every(minutes=60)
def __init__(self, config, theme): def __init__(self, config, theme):
super().__init__(config, theme, []) super().__init__(config, theme, [])
self.__labels = self.parameter('labels', '{}'.format(LABEL)) self.__labels = self.parameter("labels", "{}".format(LABEL))
self.__cmds = self.parameter('cmds', 'firefox {}'.format(LINK)) self.__cmds = self.parameter("cmds", "firefox {}".format(LINK))
self.__delim = self.parameter('delim', ';') self.__delim = self.parameter("delim", ";")
self.update_widgets() self.update_widgets()
@ -51,9 +52,11 @@ class Module(core.module.Module):
# report possible problem as a warning # report possible problem as a warning
if len(cmds) is not len(labels): if len(cmds) is not len(labels):
logging.warning('shortcut: the number of commands does not match '\ logging.warning(
'the number of provided labels.') "shortcut: the number of commands does not match "
logging.warning('cmds : %s, labels : %s', cmds, labels) "the number of provided labels."
)
logging.warning("cmds : %s, labels : %s", cmds, labels)
for idx in range(0, num_shortcuts): for idx in range(0, num_shortcuts):
cmd = cmds[idx] cmd = cmds[idx]
@ -64,4 +67,5 @@ class Module(core.module.Module):
widgets.append(widget) widgets.append(widget)
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -20,99 +20,107 @@ import core.decorators
import util.cli import util.cli
class Module(core.module.Module): class Module(core.module.Module):
@core.decorators.every(minutes=5) @core.decorators.every(minutes=5)
def __init__(self, config, theme): def __init__(self, config, theme):
super().__init__(config, theme, []) super().__init__(config, theme, [])
self.devices = self.list_devices() self.devices = self.list_devices()
self.display = self.parameter('display', 'combined') self.display = self.parameter("display", "combined")
self.drives = self.parameter('drives', 'sda') self.drives = self.parameter("drives", "sda")
self.widgets(self.create_widgets()) self.widgets(self.create_widgets())
def create_widgets(self): def create_widgets(self):
widgets = [] widgets = []
if self.display == 'combined': if self.display == "combined":
widget = core.widget.Widget(module=self) widget = core.widget.Widget(module=self)
widget.set('device', 'combined') widget.set("device", "combined")
widget.set('assessment', self.combined()) widget.set("assessment", self.combined())
self.output(widget) self.output(widget)
widgets.append(widget) widgets.append(widget)
else: else:
for device in self.devices: for device in self.devices:
if self.display == 'singles' and device not in self.drives: if self.display == "singles" and device not in self.drives:
continue continue
widget = core.widget.Widget(module=self) widget = core.widget.Widget(module=self)
widget.set('device', device) widget.set("device", device)
widget.set('assessment', self.smart(device)) widget.set("assessment", self.smart(device))
self.output(widget) self.output(widget)
widgets.append(widget) widgets.append(widget)
return widgets return widgets
def update(self): def update(self):
for widget in self.widgets(): for widget in self.widgets():
device = widget.get('device') device = widget.get("device")
if device == 'combined': if device == "combined":
widget.set('assessment', self.combined()) widget.set("assessment", self.combined())
self.output(widget) self.output(widget)
else: else:
widget.set('assessment', self.smart(device)) widget.set("assessment", self.smart(device))
self.output(widget) self.output(widget)
def output(self, widget): def output(self, widget):
device = widget.get('device') device = widget.get("device")
assessment = widget.get('assessment') assessment = widget.get("assessment")
widget.full_text('{}: {}'.format(device, assessment)) widget.full_text("{}: {}".format(device, assessment))
def state(self, widget): def state(self, widget):
states = [] states = []
assessment = widget.get('assessment') assessment = widget.get("assessment")
if assessment == 'Pre-fail': if assessment == "Pre-fail":
states.append('warning') states.append("warning")
if assessment == 'Fail': if assessment == "Fail":
states.append('critical') states.append("critical")
return states return states
def combined(self): def combined(self):
for device in self.devices: for device in self.devices:
result = self.smart(device) result = self.smart(device)
if result == 'Fail': if result == "Fail":
return 'Fail' return "Fail"
if result == 'Pre-fail': if result == "Pre-fail":
return 'Pre-fail' return "Pre-fail"
return 'OK' return "OK"
def list_devices(self): def list_devices(self):
for (root, folders, files) in os.walk('/dev'): for (root, folders, files) in os.walk("/dev"):
if root == '/dev': if root == "/dev":
devices = {''.join(filter(lambda i: i.isdigit() == False, file)) for file in files if 'sd' in file} devices = {
nvme = {file for file in files if('nvme0n' in file and 'p' not in file)} "".join(filter(lambda i: i.isdigit() == False, file))
for file in files
if "sd" in file
}
nvme = {
file for file in files if ("nvme0n" in file and "p" not in file)
}
devices.update(nvme) devices.update(nvme)
return devices return devices
def smart(self, disk_name): def smart(self, disk_name):
smartctl = shutil.which('smartctl') smartctl = shutil.which("smartctl")
assessment = None assessment = None
output = util.cli.execute('sudo {} --health {}'.format( output = util.cli.execute(
smartctl, os.path.join('/dev/', disk_name) "sudo {} --health {}".format(smartctl, os.path.join("/dev/", disk_name))
)) )
output = output.split('\n') output = output.split("\n")
line = output[4] line = output[4]
if 'SMART' in line: if "SMART" in line:
if any([i in line for i in ['PASSED', 'OK']]): if any([i in line for i in ["PASSED", "OK"]]):
assessment = 'OK' assessment = "OK"
else: else:
assessment = 'Fail' assessment = "Fail"
if assessment == 'OK': if assessment == "OK":
output = util.cli.execute('sudo {} -A {}'.format( output = util.cli.execute(
smartctl, os.path.join('/dev/', disk_name) "sudo {} -A {}".format(smartctl, os.path.join("/dev/", disk_name))
)) )
output = output.split('\n') output = output.split("\n")
for line in output: for line in output:
if 'Pre-fail' in line: if "Pre-fail" in line:
assessment = 'Pre-fail' assessment = "Pre-fail"
return assessment return assessment
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -36,6 +36,7 @@ import core.widget
import core.input import core.input
import core.decorators import core.decorators
def formatStringBuilder(s, json): def formatStringBuilder(s, json):
""" """
Parses Format Strings Parses Format Strings
@ -43,14 +44,14 @@ def formatStringBuilder(s, json):
s -> format string s -> format string
json -> the spaceapi response object json -> the spaceapi response object
""" """
identifiers = re.findall('%%.*?%%', s) identifiers = re.findall("%%.*?%%", s)
for i in identifiers: for i in identifiers:
ic = i[2:-2] # Discard %% ic = i[2:-2] # Discard %%
j = ic.split('%') j = ic.split("%")
# Only neither of, or both true AND false may be overwritten # Only neither of, or both true AND false may be overwritten
if len(j) != 3 and len(j) != 1: if len(j) != 3 and len(j) != 1:
return 'INVALID FORMAT STRING' return "INVALID FORMAT STRING"
if len(j) == 1: # no overwrite if len(j) == 1: # no overwrite
s = s.replace(i, json[j[0]]) s = s.replace(i, json[j[0]])
@ -66,30 +67,28 @@ class Module(core.module.Module):
def __init__(self, config, theme): def __init__(self, config, theme):
super().__init__(config, theme, core.widget.Widget(self.getState)) super().__init__(config, theme, core.widget.Widget(self.getState))
core.input.register( core.input.register(self, button=core.input.LEFT_MOUSE, cmd=self.__forceReload)
self, button=core.input.LEFT_MOUSE, cmd=self.__forceReload
)
self.__data = {} self.__data = {}
self.__error = None self.__error = None
self.__thread = None self.__thread = None
# The URL representing the api endpoint # The URL representing the api endpoint
self.__url = self.parameter('url', default='http://club.entropia.de/spaceapi') self.__url = self.parameter("url", default="http://club.entropia.de/spaceapi")
self._format = self.parameter( self._format = self.parameter(
'format', default=u'%%space%%: %%state.open%Open%Closed%%' "format", default="%%space%%: %%state.open%Open%Closed%%"
) )
def state(self, widget): def state(self, widget):
try: try:
if self.__error is not None: if self.__error is not None:
return ['critical'] return ["critical"]
elif self.__data['state.open']: elif self.__data["state.open"]:
return ['warning'] return ["warning"]
else: else:
return [] return []
except KeyError: except KeyError:
return ['critical'] return ["critical"]
def update(self): def update(self):
if not self.__thread or self.__thread.is_alive() == False: if not self.__thread or self.__thread.is_alive() == False:
@ -104,7 +103,7 @@ class Module(core.module.Module):
try: try:
text = formatStringBuilder(self._format, self.__data) text = formatStringBuilder(self._format, self.__data)
except KeyError: except KeyError:
text = 'KeyError' text = "KeyError"
return text return text
def get_api_async(self): def get_api_async(self):
@ -115,19 +114,19 @@ class Module(core.module.Module):
self.__data = self.__flatten(json.loads(request.text)) self.__data = self.__flatten(json.loads(request.text))
self.__error = None self.__error = None
except requests.exceptions.Timeout: except requests.exceptions.Timeout:
self.__error = 'Timeout' self.__error = "Timeout"
except requests.exceptions.HTTPError: except requests.exceptions.HTTPError:
self.__error = 'HTTP Error' self.__error = "HTTP Error"
except ValueError: except ValueError:
self.__error = 'Not a JSON response' self.__error = "Not a JSON response"
core.event.trigger('update', [ self.id ], redraw_only=True) core.event.trigger("update", [self.id], redraw_only=True)
# left_mouse_button handler # left_mouse_button handler
def __forceReload(self, event): def __forceReload(self, event):
if self.__thread: if self.__thread:
self.__thread.raise_exception() self.__thread.raise_exception()
self.__error = 'RELOADING' self.__error = "RELOADING"
core.event.trigger('update', [ self.id ], redraw_only=True) core.event.trigger("update", [self.id], redraw_only=True)
# Flattens the JSON structure recursively, e.g. ['space']['open'] # Flattens the JSON structure recursively, e.g. ['space']['open']
# becomes ['space.open'] # becomes ['space.open']
@ -138,7 +137,7 @@ class Module(core.module.Module):
if type(value) is dict: if type(value) is dict:
flattened_key = self.__flatten(value) flattened_key = self.__flatten(value)
for fk in flattened_key: for fk in flattened_key:
out[key + '.' + fk] = flattened_key[fk] out[key + "." + fk] = flattened_key[fk]
else: else:
out[key] = value out[key] = value
return out return out

View file

@ -21,54 +21,63 @@ import core.widget
import core.input import core.input
import core.decorators import core.decorators
class Module(core.module.Module): class Module(core.module.Module):
def __init__(self, config, theme): def __init__(self, config, theme):
super().__init__(config, theme, core.widget.Widget(self.spotify)) super().__init__(config, theme, core.widget.Widget(self.spotify))
buttons = {'LEFT_CLICK':core.input.LEFT_MOUSE, buttons = {
'RIGHT_CLICK':core.input.RIGHT_MOUSE, "LEFT_CLICK": core.input.LEFT_MOUSE,
'MIDDLE_CLICK':core.input.MIDDLE_MOUSE, "RIGHT_CLICK": core.input.RIGHT_MOUSE,
'SCROLL_UP':core.input.WHEEL_UP, "MIDDLE_CLICK": core.input.MIDDLE_MOUSE,
'SCROLL_DOWN':core.input.WHEEL_DOWN, "SCROLL_UP": core.input.WHEEL_UP,
"SCROLL_DOWN": core.input.WHEEL_DOWN,
} }
self.__song = '' self.__song = ""
self.__format = self.parameter('format', '{artist} - {title}') self.__format = self.parameter("format", "{artist} - {title}")
prev_button = self.parameter('previous', 'LEFT_CLICK') prev_button = self.parameter("previous", "LEFT_CLICK")
next_button = self.parameter('next', 'RIGHT_CLICK') next_button = self.parameter("next", "RIGHT_CLICK")
pause_button = self.parameter('pause', 'MIDDLE_CLICK') pause_button = self.parameter("pause", "MIDDLE_CLICK")
cmd = 'dbus-send --session --type=method_call --dest=org.mpris.MediaPlayer2.spotify \ cmd = "dbus-send --session --type=method_call --dest=org.mpris.MediaPlayer2.spotify \
/org/mpris/MediaPlayer2 org.mpris.MediaPlayer2.Player.' /org/mpris/MediaPlayer2 org.mpris.MediaPlayer2.Player."
core.input.register(self, button=buttons[prev_button], core.input.register(self, button=buttons[prev_button], cmd=cmd + "Previous")
cmd=cmd + 'Previous') core.input.register(self, button=buttons[next_button], cmd=cmd + "Next")
core.input.register(self, button=buttons[next_button], core.input.register(self, button=buttons[pause_button], cmd=cmd + "PlayPause")
cmd=cmd + 'Next')
core.input.register(self, button=buttons[pause_button],
cmd=cmd + 'PlayPause')
@core.decorators.scrollable @core.decorators.scrollable
def spotify(self, widget): def spotify(self, widget):
return self.string_song return self.string_song
def hidden(self): def hidden(self):
return self.string_song == '' return self.string_song == ""
def update(self): def update(self):
try: try:
bus = dbus.SessionBus() bus = dbus.SessionBus()
spotify = bus.get_object('org.mpris.MediaPlayer2.spotify', '/org/mpris/MediaPlayer2') spotify = bus.get_object(
spotify_iface = dbus.Interface(spotify, 'org.freedesktop.DBus.Properties') "org.mpris.MediaPlayer2.spotify", "/org/mpris/MediaPlayer2"
props = spotify_iface.Get('org.mpris.MediaPlayer2.Player', 'Metadata') )
playback_status = str(spotify_iface.Get('org.mpris.MediaPlayer2.Player', 'PlaybackStatus')) spotify_iface = dbus.Interface(spotify, "org.freedesktop.DBus.Properties")
self.__song = self.__format.format(album=str(props.get('xesam:album')), props = spotify_iface.Get("org.mpris.MediaPlayer2.Player", "Metadata")
title=str(props.get('xesam:title')), playback_status = str(
artist=','.join(props.get('xesam:artist')), spotify_iface.Get("org.mpris.MediaPlayer2.Player", "PlaybackStatus")
trackNumber=str(props.get('xesam:trackNumber')), )
playbackStatus=u'\u25B6' if playback_status=='Playing' else u'\u258D\u258D' if playback_status=='Paused' else '',) self.__song = self.__format.format(
album=str(props.get("xesam:album")),
title=str(props.get("xesam:title")),
artist=",".join(props.get("xesam:artist")),
trackNumber=str(props.get("xesam:trackNumber")),
playbackStatus="\u25B6"
if playback_status == "Playing"
else "\u258D\u258D"
if playback_status == "Paused"
else "",
)
except Exception: except Exception:
self.__song = '' self.__song = ""
@property @property
def string_song(self): def string_song(self):
@ -76,4 +85,5 @@ class Module(core.module.Module):
return unicode(self.__song) return unicode(self.__song)
return str(self.__song) return str(self.__song)
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -22,39 +22,44 @@ import core.decorators
import util.format import util.format
class Module(core.module.Module): class Module(core.module.Module):
@core.decorators.every(hours=1) @core.decorators.every(hours=1)
def __init__(self, config, theme): def __init__(self, config, theme):
super().__init__(config, theme, core.widget.Widget(self.value)) super().__init__(config, theme, core.widget.Widget(self.value))
self.__symbols = self.parameter('symbols', '') self.__symbols = self.parameter("symbols", "")
self.__change = util.format.asbool(self.parameter('change', True)) self.__change = util.format.asbool(self.parameter("change", True))
self.__value = None self.__value = None
def value(self, widget): def value(self, widget):
results = [] results = []
if not self.__value: if not self.__value:
return 'n/a' return "n/a"
data = json.loads(self.__value) data = json.loads(self.__value)
for symbol in data['quoteResponse']['result']: for symbol in data["quoteResponse"]["result"]:
valkey = 'regularMarketChange' if self.__change else 'regularMarketPrice' valkey = "regularMarketChange" if self.__change else "regularMarketPrice"
sym = symbol.get('symbol', 'n/a') sym = symbol.get("symbol", "n/a")
currency = symbol.get('currency', 'USD') currency = symbol.get("currency", "USD")
val = 'n/a' if not valkey in symbol else '{:.2f}'.format(symbol[valkey]) val = "n/a" if not valkey in symbol else "{:.2f}".format(symbol[valkey])
results.append('{} {} {}'.format(sym, val, currency)) results.append("{} {} {}".format(sym, val, currency))
return u' '.join(results) return " ".join(results)
def fetch(self): def fetch(self):
if self.__symbols: if self.__symbols:
url = 'https://query1.finance.yahoo.com/v7/finance/quote?symbols=' url = "https://query1.finance.yahoo.com/v7/finance/quote?symbols="
url += self.__symbols + '&fields=regularMarketPrice,currency,regularMarketChange' url += (
self.__symbols
+ "&fields=regularMarketPrice,currency,regularMarketChange"
)
return urllib.request.urlopen(url).read().strip() return urllib.request.urlopen(url).read().strip()
else: else:
logging.error('unable to retrieve stock exchange rate') logging.error("unable to retrieve stock exchange rate")
return None return None
def update(self): def update(self):
self.__value = self.fetch() self.__value = self.fetch()
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -23,13 +23,14 @@ import core.decorators
import util.location import util.location
class Module(core.module.Module): class Module(core.module.Module):
@core.decorators.every(hours=1) @core.decorators.every(hours=1)
def __init__(self, config, theme): def __init__(self, config, theme):
super().__init__(config, theme, core.widget.Widget(self.suntimes)) super().__init__(config, theme, core.widget.Widget(self.suntimes))
lat = self.parameter('lat', None) lat = self.parameter("lat", None)
lon = self.parameter('lon', None) lon = self.parameter("lon", None)
self.__sun = None self.__sun = None
if not lat or not lon: if not lat or not lon:
@ -40,12 +41,13 @@ class Module(core.module.Module):
def suntimes(self, _): def suntimes(self, _):
if self.__sunset and self.__sunrise: if self.__sunset and self.__sunrise:
if self.__isup: if self.__isup:
return u'\u21A7{} \u21A5{}'.format( return "\u21A7{} \u21A5{}".format(
self.__sunset.strftime('%H:%M'), self.__sunset.strftime("%H:%M"), self.__sunrise.strftime("%H:%M")
self.__sunrise.strftime('%H:%M')) )
return u'\u21A5{} \u21A7{}'.format(self.__sunrise.strftime('%H:%M'), return "\u21A5{} \u21A7{}".format(
self.__sunset.strftime('%H:%M')) self.__sunrise.strftime("%H:%M"), self.__sunset.strftime("%H:%M")
return 'n/a' )
return "n/a"
def __calculate_times(self): def __calculate_times(self):
self.__isup = False self.__isup = False
@ -55,13 +57,13 @@ class Module(core.module.Module):
try: try:
self.__sunrise = self.__sun.get_local_sunrise_time() self.__sunrise = self.__sun.get_local_sunrise_time()
except SunTimeException: except SunTimeException:
self.__sunrise = 'no sunrise' self.__sunrise = "no sunrise"
order_matters = False order_matters = False
try: try:
self.__sunset = self.__sun.get_local_sunset_time() self.__sunset = self.__sun.get_local_sunset_time()
except SunTimeException: except SunTimeException:
self.__sunset = 'no sunset' self.__sunset = "no sunset"
order_matters = False order_matters = False
if not order_matters: if not order_matters:
@ -74,19 +76,20 @@ class Module(core.module.Module):
self.__sunrise = self.__sun.get_local_sunrise_time(tomorrow) self.__sunrise = self.__sun.get_local_sunrise_time(tomorrow)
self.__sunset = self.__sun.get_local_sunset_time(tomorrow) self.__sunset = self.__sun.get_local_sunset_time(tomorrow)
except SunTimeException: except SunTimeException:
self.__sunrise = 'no sunrise' self.__sunrise = "no sunrise"
self.__sunset = 'no sunset' self.__sunset = "no sunset"
elif now > self.__sunrise: elif now > self.__sunrise:
tomorrow = (now + datetime.timedelta(days=1)).date() tomorrow = (now + datetime.timedelta(days=1)).date()
try: try:
self.__sunrise = self.__sun.get_local_sunrise_time(tomorrow) self.__sunrise = self.__sun.get_local_sunrise_time(tomorrow)
except SunTimeException: except SunTimeException:
self.__sunrise = 'no sunrise' self.__sunrise = "no sunrise"
return return
self.__isup = True self.__isup = True
def update(self): def update(self):
self.__calculate_times() self.__calculate_times()
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -28,7 +28,7 @@ try:
import tkinter as tk import tkinter as tk
from tkinter import messagebox as tkmessagebox from tkinter import messagebox as tkmessagebox
except ImportError: except ImportError:
logging.warning('failed to import tkinter - bumblebee popups won\'t work!') logging.warning("failed to import tkinter - bumblebee popups won't work!")
import core.module import core.module
import core.widget import core.widget
@ -39,18 +39,18 @@ import util.cli
import util.popup import util.popup
import util.format import util.format
class Module(core.module.Module): class Module(core.module.Module):
@core.decorators.every(minutes=60) @core.decorators.every(minutes=60)
def __init__(self, config, theme): def __init__(self, config, theme):
super().__init__(config, theme, core.widget.Widget(self.text)) super().__init__(config, theme, core.widget.Widget(self.text))
self.__confirm = util.format.asbool(self.parameter('confirm', True)) self.__confirm = util.format.asbool(self.parameter("confirm", True))
core.input.register(self, button=core.input.LEFT_MOUSE, core.input.register(self, button=core.input.LEFT_MOUSE, cmd=self.popup)
cmd=self.popup)
def text(self, widget): def text(self, widget):
return '' return ""
def __on_command(self, header, text, command): def __on_command(self, header, text, command):
do_it = True do_it = True
@ -65,26 +65,49 @@ class Module(core.module.Module):
if do_it: if do_it:
util.cli.execute(command) util.cli.execute(command)
def popup(self, widget): def popup(self, widget):
menu = util.popup.menu() menu = util.popup.menu()
reboot_cmd = self.parameter('reboot', 'reboot') reboot_cmd = self.parameter("reboot", "reboot")
shutdown_cmd = self.parameter('shutdown', 'shutdown -h now') shutdown_cmd = self.parameter("shutdown", "shutdown -h now")
logout_cmd = self.parameter('logout', 'i3exit logout') logout_cmd = self.parameter("logout", "i3exit logout")
switch_user_cmd = self.parameter('switch_user', 'i3exit switch_user') switch_user_cmd = self.parameter("switch_user", "i3exit switch_user")
lock_cmd = self.parameter('lock', 'i3exit lock') lock_cmd = self.parameter("lock", "i3exit lock")
suspend_cmd = self.parameter('suspend', 'i3exit suspend') suspend_cmd = self.parameter("suspend", "i3exit suspend")
hibernate_cmd = self.parameter('hibernate', 'i3exit hibernate') hibernate_cmd = self.parameter("hibernate", "i3exit hibernate")
menu.add_menuitem('shutdown', callback=functools.partial(self.__on_command, 'Shutdown', 'Shutdown?', shutdown_cmd)) menu.add_menuitem(
menu.add_menuitem('reboot', callback=functools.partial(self.__on_command, 'Reboot', 'Reboot?', reboot_cmd)) "shutdown",
menu.add_menuitem('log out', callback=functools.partial(self.__on_command, 'Log out', 'Log out?', 'i3exit logout')) callback=functools.partial(
self.__on_command, "Shutdown", "Shutdown?", shutdown_cmd
),
)
menu.add_menuitem(
"reboot",
callback=functools.partial(
self.__on_command, "Reboot", "Reboot?", reboot_cmd
),
)
menu.add_menuitem(
"log out",
callback=functools.partial(
self.__on_command, "Log out", "Log out?", "i3exit logout"
),
)
# don't ask for these # don't ask for these
menu.add_menuitem('switch user', callback=functools.partial(util.cli.execute, switch_user_cmd)) menu.add_menuitem(
menu.add_menuitem('lock', callback=functools.partial(util.cli.execute, lock_cmd)) "switch user", callback=functools.partial(util.cli.execute, switch_user_cmd)
menu.add_menuitem('suspend', callback=functools.partial(util.cli.execute, suspend_cmd)) )
menu.add_menuitem('hibernate', callback=functools.partial(util.cli.execute, hibernate_cmd)) menu.add_menuitem(
"lock", callback=functools.partial(util.cli.execute, lock_cmd)
)
menu.add_menuitem(
"suspend", callback=functools.partial(util.cli.execute, suspend_cmd)
)
menu.add_menuitem(
"hibernate", callback=functools.partial(util.cli.execute, hibernate_cmd)
)
menu.show(widget, 0, 0) menu.show(widget, 0, 0)
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -13,24 +13,26 @@ import core.module
import core.widget import core.widget
import core.decorators import core.decorators
class Module(core.module.Module): class Module(core.module.Module):
def __init__(self, config, theme): def __init__(self, config, theme):
super().__init__(config, theme, core.widget.Widget(self.output)) super().__init__(config, theme, core.widget.Widget(self.output))
self.__pending_tasks = '0' self.__pending_tasks = "0"
def update(self): def update(self):
"""Return a string with the number of pending tasks from TaskWarrior.""" """Return a string with the number of pending tasks from TaskWarrior."""
try: try:
taskrc = self.parameter('taskrc', '~/.taskrc') taskrc = self.parameter("taskrc", "~/.taskrc")
w = TaskWarrior(config_filename=taskrc) w = TaskWarrior(config_filename=taskrc)
pending_tasks = w.filter_tasks({'status': 'pending'}) pending_tasks = w.filter_tasks({"status": "pending"})
self.__pending_tasks = str(len(pending_tasks)) self.__pending_tasks = str(len(pending_tasks))
except: except:
self.__pending_tasks = 'n/a' self.__pending_tasks = "n/a"
def output(self, _): def output(self, _):
"""Format the task counter to output in bumblebee.""" """Format the task counter to output in bumblebee."""
return '{}'.format(self.__pending_tasks) return "{}".format(self.__pending_tasks)
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -10,12 +10,14 @@ Parameters:
import core.decorators import core.decorators
from .datetimetz import Module from .datetimetz import Module
class Module(Module): class Module(Module):
@core.decorators.every(seconds=59) # ensures one update per minute @core.decorators.every(seconds=59) # ensures one update per minute
def __init__(self, config, theme): def __init__(self, config, theme):
super().__init__(config, theme) super().__init__(config, theme)
def default_format(self): def default_format(self):
return '%X %Z' return "%X %Z"
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -18,7 +18,7 @@ try:
except ImportError: except ImportError:
pass pass
no_title = 'n/a' no_title = "n/a"
import core.module import core.module
import core.widget import core.widget
@ -26,24 +26,32 @@ import core.decorators
import util.format import util.format
class Module(core.module.Module): class Module(core.module.Module):
def __init__(self, config, theme): def __init__(self, config, theme):
super().__init__(config, theme, []) super().__init__(config, theme, [])
# parsing of parameters # parsing of parameters
self.__scroll = util.format.asbool(self.parameter('scroll', False)) self.__scroll = util.format.asbool(self.parameter("scroll", False))
self.__max = int(self.parameter('max', 64)) self.__max = int(self.parameter("max", 64))
self.__placeholder = self.parameter('placeholder', '...') self.__placeholder = self.parameter("placeholder", "...")
self.__title = '' self.__title = ""
# set output of the module # set output of the module
self.widgets([core.widget.Widget(full_text= self.widgets(
self.__scrolling_focused_title if self.__scroll else self.__focused_title)]) [
core.widget.Widget(
full_text=self.__scrolling_focused_title
if self.__scroll
else self.__focused_title
)
]
)
# create a connection with i3ipc # create a connection with i3ipc
self.__i3 = i3ipc.Connection() self.__i3 = i3ipc.Connection()
# event is called both on focus change and title change # event is called both on focus change and title change
self.__i3.on('window', lambda __p_i3, __p_e: self.__pollTitle()) self.__i3.on("window", lambda __p_i3, __p_e: self.__pollTitle())
# begin listening for events # begin listening for events
threading.Thread(target=self.__i3.main).start() threading.Thread(target=self.__i3.main).start()
@ -69,9 +77,12 @@ class Module(core.module.Module):
if not self.__scroll: if not self.__scroll:
# cut the text if it is too long # cut the text if it is too long
if len(self.__full_title) > self.__max: if len(self.__full_title) > self.__max:
self.__title = self.__full_title[0:self.__max - len(self.__placeholder)] self.__title = self.__full_title[
self.__title = '{}{}'.format(self.__title, self.__placeholder) 0 : self.__max - len(self.__placeholder)
]
self.__title = "{}{}".format(self.__title, self.__placeholder)
else: else:
self.__title = self.__full_title self.__title = self.__full_title
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -12,13 +12,16 @@ import core.module
import core.widget import core.widget
import core.input import core.input
class Module(core.module.Module): class Module(core.module.Module):
def __init__(self, config, theme): def __init__(self, config, theme):
super().__init__(config, theme, core.widget.Widget(self.output)) super().__init__(config, theme, core.widget.Widget(self.output))
self.__doc = os.path.expanduser(self.parameter('file', '~/Documents/todo.txt')) self.__doc = os.path.expanduser(self.parameter("file", "~/Documents/todo.txt"))
self.__todos = self.count_items() self.__todos = self.count_items()
core.input.register(self, button=core.input.LEFT_MOUSE, cmd='xdg-open {}'.format(self.__doc)) core.input.register(
self, button=core.input.LEFT_MOUSE, cmd="xdg-open {}".format(self.__doc)
)
def output(self, widget): def output(self, widget):
return str(self.__todos) return str(self.__todos)
@ -28,8 +31,8 @@ class Module(core.module.Module):
def state(self, widgets): def state(self, widgets):
if self.__todos == 0: if self.__todos == 0:
return 'empty' return "empty"
return 'items' return "items"
def count_items(self): def count_items(self):
try: try:
@ -41,4 +44,5 @@ class Module(core.module.Module):
except Exception: except Exception:
return 0 return 0
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -24,37 +24,47 @@ import core.widget
import util.format import util.format
import util.graph import util.graph
class Module(core.module.Module): class Module(core.module.Module):
def __init__(self, config, theme): def __init__(self, config, theme):
widgets = [] widgets = []
super().__init__(config, theme, widgets) super().__init__(config, theme, widgets)
self._exclude = tuple(filter(len, util.format.aslist(self.parameter('exclude', 'lo,virbr,docker,vboxnet,veth')))) self._exclude = tuple(
self._status = '' filter(
len,
util.format.aslist(
self.parameter("exclude", "lo,virbr,docker,vboxnet,veth")
),
)
)
self._status = ""
self._showname = util.format.asbool(self.parameter('showname', True)) self._showname = util.format.asbool(self.parameter("showname", True))
self._format = self.parameter('format', '{:.2f}') self._format = self.parameter("format", "{:.2f}")
self._prev = {} self._prev = {}
self._states = {} self._states = {}
self._lastcheck = 0 self._lastcheck = 0
self._states['include'] = [] self._states["include"] = []
self._states['exclude'] = [] self._states["exclude"] = []
for state in tuple(filter(len, util.format.aslist(self.parameter('states', '')))): for state in tuple(
if state[0] == '^': filter(len, util.format.aslist(self.parameter("states", "")))
self._states['exclude'].append(state[1:]) ):
if state[0] == "^":
self._states["exclude"].append(state[1:])
else: else:
self._states['include'].append(state) self._states["include"].append(state)
self._graphlen = int(self.parameter('graphlen', 0)) self._graphlen = int(self.parameter("graphlen", 0))
if self._graphlen > 0: if self._graphlen > 0:
self._graphdata = {} self._graphdata = {}
self._first_run = True self._first_run = True
self._update_widgets(widgets) self._update_widgets(widgets)
def state(self, widget): def state(self, widget):
if 'traffic.rx' in widget.name: if "traffic.rx" in widget.name:
return 'rx' return "rx"
if 'traffic.tx' in widget.name: if "traffic.tx" in widget.name:
return 'tx' return "tx"
return self._status return self._status
def update(self): def update(self):
@ -74,8 +84,8 @@ class Module(core.module.Module):
retval = [] retval = []
try: try:
for ip in netifaces.ifaddresses(intf).get(netifaces.AF_INET, []): for ip in netifaces.ifaddresses(intf).get(netifaces.AF_INET, []):
if ip.get('addr', '') != '': if ip.get("addr", "") != "":
retval.append(ip.get('addr')) retval.append(ip.get("addr"))
except Exception: except Exception:
return [] return []
return retval return retval
@ -85,66 +95,80 @@ class Module(core.module.Module):
computes theme.minwidth string computes theme.minwidth string
based on traffic.format and traffic.graphlen parameters based on traffic.format and traffic.graphlen parameters
""" """
minwidth_str = '' minwidth_str = ""
if self._graphlen > 0: if self._graphlen > 0:
graph_len = int(self._graphlen / 2) graph_len = int(self._graphlen / 2)
graph_prefix = '0' * graph_len graph_prefix = "0" * graph_len
minwidth_str += graph_prefix minwidth_str += graph_prefix
minwidth_str += '1000' minwidth_str += "1000"
try: try:
length = int(re.match('{:\.(\d+)f}', self._format).group(1)) length = int(re.match("{:\.(\d+)f}", self._format).group(1))
if length > 0: if length > 0:
minwidth_str += '.' + '0' * length minwidth_str += "." + "0" * length
except AttributeError: except AttributeError:
# return default value # return default value
return '1000.00KiB/s' return "1000.00KiB/s"
finally: finally:
minwidth_str += 'KiB/s' minwidth_str += "KiB/s"
return minwidth_str return minwidth_str
def _update_widgets(self, widgets): def _update_widgets(self, widgets):
interfaces = [i for i in netifaces.interfaces() if not i.startswith(self._exclude)] interfaces = [
i for i in netifaces.interfaces() if not i.startswith(self._exclude)
]
del widgets[:] del widgets[:]
counters = psutil.net_io_counters(pernic=True) counters = psutil.net_io_counters(pernic=True)
now = time.time() now = time.time()
timediff = now - (self._lastcheck if self._lastcheck else now) timediff = now - (self._lastcheck if self._lastcheck else now)
if timediff <= 0: timediff = 1 if timediff <= 0:
timediff = 1
self._lastcheck = now self._lastcheck = now
for interface in interfaces: for interface in interfaces:
if self._graphlen > 0: if self._graphlen > 0:
if interface not in self._graphdata: if interface not in self._graphdata:
self._graphdata[interface] = { self._graphdata[interface] = {
'rx': [0] * self._graphlen, "rx": [0] * self._graphlen,
'tx': [0] * self._graphlen} "tx": [0] * self._graphlen,
if not interface: interface = 'lo' }
state = 'down' if not interface:
interface = "lo"
state = "down"
if len(self.get_addresses(interface)) > 0: if len(self.get_addresses(interface)) > 0:
state = 'up' state = "up"
elif util.format.asbool(self.parameter('hide_down', True)): elif util.format.asbool(self.parameter("hide_down", True)):
continue continue
if len(self._states['exclude']) > 0 and state in self._states['exclude']: continue if len(self._states["exclude"]) > 0 and state in self._states["exclude"]:
if len(self._states['include']) > 0 and state not in self._states['include']: continue continue
if (
len(self._states["include"]) > 0
and state not in self._states["include"]
):
continue
data = { data = {
'rx': counters[interface].bytes_recv, "rx": counters[interface].bytes_recv,
'tx': counters[interface].bytes_sent, "tx": counters[interface].bytes_sent,
} }
name = 'traffic-{}'.format(interface) name = "traffic-{}".format(interface)
if self._showname: if self._showname:
self.create_widget(widgets, name, interface) self.create_widget(widgets, name, interface)
for direction in ['rx', 'tx']: for direction in ["rx", "tx"]:
name = 'traffic.{}-{}'.format(direction, interface) name = "traffic.{}-{}".format(direction, interface)
widget = self.create_widget(widgets, name, attributes={'theme.minwidth': self.get_minwidth_str()}) widget = self.create_widget(
widgets,
name,
attributes={"theme.minwidth": self.get_minwidth_str()},
)
prev = self._prev.get(name, 0) prev = self._prev.get(name, 0)
bspeed = (int(data[direction]) - int(prev)) / timediff bspeed = (int(data[direction]) - int(prev)) / timediff
speed = util.format.byte(bspeed, self._format) speed = util.format.byte(bspeed, self._format)
txtspeed = '{0}/s'.format(speed) txtspeed = "{0}/s".format(speed)
if self._graphlen > 0: if self._graphlen > 0:
# skip first value returned by psutil, because it is # skip first value returned by psutil, because it is
# giant and ruins the grapth ratio until it gets pushed # giant and ruins the grapth ratio until it gets pushed
@ -152,10 +176,16 @@ class Module(core.module.Module):
if self._first_run is True: if self._first_run is True:
self._first_run = False self._first_run = False
else: else:
self._graphdata[interface][direction] = self._graphdata[interface][direction][1:] self._graphdata[interface][direction] = self._graphdata[
interface
][direction][1:]
self._graphdata[interface][direction].append(bspeed) self._graphdata[interface][direction].append(bspeed)
txtspeed = '{}{}'.format(util.graph.braille(self._graphdata[interface][direction]), txtspeed) txtspeed = "{}{}".format(
util.graph.braille(self._graphdata[interface][direction]),
txtspeed,
)
widget.full_text(txtspeed) widget.full_text(txtspeed)
self._prev[name] = data[direction] self._prev[name] = data[direction]
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -9,14 +9,15 @@ import core.decorators
import util.cli import util.cli
class Module(core.module.Module): class Module(core.module.Module):
@core.decorators.every(minutes=60) @core.decorators.every(minutes=60)
def __init__(self, config, theme): def __init__(self, config, theme):
super().__init__(config, theme, core.widget.Widget('')) super().__init__(config, theme, core.widget.Widget(""))
self.__paused = False self.__paused = False
# Make sure that twmn is currently not paused # Make sure that twmn is currently not paused
util.cli.execute('killall -SIGUSR2 twmnd', ignore_errors=True) util.cli.execute("killall -SIGUSR2 twmnd", ignore_errors=True)
core.input.register(self, button=core.input.LEFT_MOUSE, cmd=self.toggle_status) core.input.register(self, button=core.input.LEFT_MOUSE, cmd=self.toggle_status)
def toggle_status(self, event): def toggle_status(self, event):
@ -24,15 +25,16 @@ class Module(core.module.Module):
try: try:
if self.__paused: if self.__paused:
util.cli.execute('systemctl --user start twmnd') util.cli.execute("systemctl --user start twmnd")
else: else:
util.cli.execute('systemctl --user stop twmnd') util.cli.execute("systemctl --user stop twmnd")
except: except:
self.__paused = not self.__paused # toggling failed self.__paused = not self.__paused # toggling failed
def state(self, widget): def state(self, widget):
if self.__paused: if self.__paused:
return ['muted'] return ["muted"]
return ['unmuted'] return ["unmuted"]
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -7,17 +7,19 @@ from datetime import timedelta
import core.module import core.module
import core.widget import core.widget
class Module(core.module.Module): class Module(core.module.Module):
def __init__(self, config, theme): def __init__(self, config, theme):
super().__init__(config, theme, core.widget.Widget(self.output)) super().__init__(config, theme, core.widget.Widget(self.output))
self.__uptime = '' self.__uptime = ""
def output(self, _): def output(self, _):
return '{}'.format(self.__uptime) return "{}".format(self.__uptime)
def update(self): def update(self):
with open('/proc/uptime', 'r') as f: with open("/proc/uptime", "r") as f:
uptime_seconds = int(float(f.readline().split()[0])) uptime_seconds = int(float(f.readline().split()[0]))
self.__uptime = timedelta(seconds=uptime_seconds) self.__uptime = timedelta(seconds=uptime_seconds)
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -25,6 +25,7 @@ import core.input
import util.cli import util.cli
import util.popup import util.popup
class Module(core.module.Module): class Module(core.module.Module):
def __init__(self, config, theme): def __init__(self, config, theme):
super().__init__(config, theme, core.widget.Widget(self.vpn_status)) super().__init__(config, theme, core.widget.Widget(self.vpn_status))
@ -32,74 +33,82 @@ class Module(core.module.Module):
self.__connected_vpn_profile = None self.__connected_vpn_profile = None
self.__selected_vpn_profile = None self.__selected_vpn_profile = None
res = util.cli.execute('nmcli -g NAME,TYPE c') res = util.cli.execute("nmcli -g NAME,TYPE c")
lines = res.splitlines() lines = res.splitlines()
self.__vpn_profiles = [] self.__vpn_profiles = []
for line in lines: for line in lines:
info = line.split(':') info = line.split(":")
try: try:
if self.__isvpn(info[1]): if self.__isvpn(info[1]):
self.__vpn_profiles.append(info[0]) self.__vpn_profiles.append(info[0])
except: except:
pass pass
core.input.register(self, button=core.input.LEFT_MOUSE, core.input.register(self, button=core.input.LEFT_MOUSE, cmd=self.popup)
cmd=self.popup)
def __isvpn(self, connection_type): def __isvpn(self, connection_type):
return connection_type in ['vpn', 'wireguard'] return connection_type in ["vpn", "wireguard"]
def update(self): def update(self):
try: try:
res = util.cli.execute('nmcli -g NAME,TYPE,DEVICE con') res = util.cli.execute("nmcli -g NAME,TYPE,DEVICE con")
lines = res.splitlines() lines = res.splitlines()
self.__connected_vpn_profile = None self.__connected_vpn_profile = None
for line in lines: for line in lines:
info = line.split(':') info = line.split(":")
if self.__isvpn(info[1]) and info[2] != '': if self.__isvpn(info[1]) and info[2] != "":
self.__connected_vpn_profile = info[0] self.__connected_vpn_profile = info[0]
except Exception as e: except Exception as e:
logging.exception('Could not get VPN status') logging.exception("Could not get VPN status")
self.__connected_vpn_profile = None self.__connected_vpn_profile = None
def vpn_status(self, widget): def vpn_status(self, widget):
if self.__connected_vpn_profile is None: if self.__connected_vpn_profile is None:
return 'off' return "off"
return self.__connected_vpn_profile return self.__connected_vpn_profile
def __on_vpndisconnect(self): def __on_vpndisconnect(self):
try: try:
util.cli.execute('nmcli c down \'{vpn}\'' util.cli.execute(
.format(vpn=self.__connected_vpn_profile)) "nmcli c down '{vpn}'".format(vpn=self.__connected_vpn_profile)
)
self.__connected_vpn_profile = None self.__connected_vpn_profile = None
except Exception as e: except Exception as e:
logging.exception('Could not disconnect VPN connection') logging.exception("Could not disconnect VPN connection")
def __on_vpnconnect(self, name): def __on_vpnconnect(self, name):
self.__selected_vpn_profile = name self.__selected_vpn_profile = name
try: try:
util.cli.execute('nmcli c up \'{vpn}\'' util.cli.execute(
.format(vpn=self.__selected_vpn_profile)) "nmcli c up '{vpn}'".format(vpn=self.__selected_vpn_profile)
)
self.__connected_vpn_profile = name self.__connected_vpn_profile = name
except Exception as e: except Exception as e:
logging.exception('Could not establish VPN connection') logging.exception("Could not establish VPN connection")
self.__connected_vpn_profile = None self.__connected_vpn_profile = None
def popup(self, widget): def popup(self, widget):
menu = util.popup.menu() menu = util.popup.menu()
if self.__connected_vpn_profile is not None: if self.__connected_vpn_profile is not None:
menu.add_menuitem('Disconnect', callback=self.__on_vpndisconnect) menu.add_menuitem("Disconnect", callback=self.__on_vpndisconnect)
for vpn_profile in self.__vpn_profiles: for vpn_profile in self.__vpn_profiles:
if self.__connected_vpn_profile is not None and self.__connected_vpn_profile == vpn_profile: if (
self.__connected_vpn_profile is not None
and self.__connected_vpn_profile == vpn_profile
):
continue continue
menu.add_menuitem(vpn_profile, callback=functools.partial(self.__on_vpnconnect, vpn_profile)) menu.add_menuitem(
vpn_profile,
callback=functools.partial(self.__on_vpnconnect, vpn_profile),
)
menu.show(widget) menu.show(widget)
def state(self, widget): def state(self, widget):
return [] return []
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -17,40 +17,42 @@ import core.decorators
import util.cli import util.cli
class Module(core.module.Module): class Module(core.module.Module):
@core.decorators.every(minutes=60) @core.decorators.every(minutes=60)
def __init__(self, config, theme): def __init__(self, config, theme):
super().__init__(config, theme, core.widget.Widget(self.text)) super().__init__(config, theme, core.widget.Widget(self.text))
self.__tracking = False self.__tracking = False
self.__project = '' self.__project = ""
core.input.register(self, button=core.input.LEFT_MOUSE, cmd=self.toggle) core.input.register(self, button=core.input.LEFT_MOUSE, cmd=self.toggle)
def toggle(self, widget): def toggle(self, widget):
self.__project = 'hit' self.__project = "hit"
if self.__tracking: if self.__tracking:
util.cli.execute('watson stop') util.cli.execute("watson stop")
else: else:
util.cli.execute('watson restart') util.cli.execute("watson restart")
self.__tracking = not self.__tracking self.__tracking = not self.__tracking
def text(self, widget): def text(self, widget):
if self.__tracking: if self.__tracking:
return self.__project return self.__project
else: else:
return 'Paused' return "Paused"
def update(self): def update(self):
output = util.cli.execute('watson status') output = util.cli.execute("watson status")
if re.match('No project started', output): if re.match("No project started", output):
self.__tracking = False self.__tracking = False
return return
self.__tracking = True self.__tracking = True
m = re.search(r'Project (.+) started', output) m = re.search(r"Project (.+) started", output)
self.__project = m.group(1) self.__project = m.group(1)
def state(self, widget): def state(self, widget):
return 'on' if self.__tracking else 'off' return "on" if self.__tracking else "off"
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -28,30 +28,37 @@ import re
import requests import requests
from requests.exceptions import RequestException from requests.exceptions import RequestException
class Module(core.module.Module): class Module(core.module.Module):
@core.decorators.every(minutes=15) @core.decorators.every(minutes=15)
def __init__(self, config, theme): def __init__(self, config, theme):
super().__init__(config, theme, core.widget.Widget(self.output)) super().__init__(config, theme, core.widget.Widget(self.output))
self.__temperature = 0 self.__temperature = 0
self.__apikey = self.parameter('apikey', 'af7bfe22287c652d032a3064ffa44088') self.__apikey = self.parameter("apikey", "af7bfe22287c652d032a3064ffa44088")
self.__location = util.format.aslist(self.parameter('location', 'auto')) self.__location = util.format.aslist(self.parameter("location", "auto"))
self.__index = 0 self.__index = 0
self.__showcity = util.format.asbool(self.parameter('showcity', True)) self.__showcity = util.format.asbool(self.parameter("showcity", True))
self.__showminmax = util.format.asbool(self.parameter('showminmax', False)) self.__showminmax = util.format.asbool(self.parameter("showminmax", False))
self.__unit = self.parameter('unit', 'metric') self.__unit = self.parameter("unit", "metric")
self.__valid = False self.__valid = False
core.input.register(self, button=core.input.LEFT_MOUSE, cmd=self.__next_location) core.input.register(
core.input.register(self, button=core.input.RIGHT_MOUSE, cmd=self.__prev_location) self, button=core.input.LEFT_MOUSE, cmd=self.__next_location
)
core.input.register(
self, button=core.input.RIGHT_MOUSE, cmd=self.__prev_location
)
def __next_location(self, event): def __next_location(self, event):
self.__index = (self.__index + 1) % len(self.__location) self.__index = (self.__index + 1) % len(self.__location)
self.update() self.update()
def __prev_location(self, event): def __prev_location(self, event):
self.__index = len(self.__location) - 1 if self.__index <= 0 else self.__index - 1 self.__index = (
len(self.__location) - 1 if self.__index <= 0 else self.__index - 1
)
self.update() self.update()
def temperature(self): def temperature(self):
@ -64,15 +71,22 @@ class Module(core.module.Module):
return util.format.astemperature(self.__tempmax, self.__unit) return util.format.astemperature(self.__tempmax, self.__unit)
def city(self): def city(self):
city = re.sub('[_-]', ' ', self.__city) city = re.sub("[_-]", " ", self.__city)
return u'{} '.format(city) return "{} ".format(city)
def output(self, widget): def output(self, widget):
if not self.__valid: if not self.__valid:
return u'?' return "?"
if self.__showminmax: if self.__showminmax:
self.__showcity = False self.__showcity = False
return self.city() + self.temperature() + ' Hi:' + self.tempmax() + ' Lo:' + self.tempmin() return (
self.city()
+ self.temperature()
+ " Hi:"
+ self.tempmax()
+ " Lo:"
+ self.tempmin()
)
elif self.__showcity: elif self.__showcity:
return self.city() + self.temperature() return self.city() + self.temperature()
else: else:
@ -80,42 +94,51 @@ class Module(core.module.Module):
def state(self, widget): def state(self, widget):
if self.__valid: if self.__valid:
if 'thunderstorm' in self.__weather: if "thunderstorm" in self.__weather:
return ['thunder'] return ["thunder"]
elif 'drizzle' in self.__weather: elif "drizzle" in self.__weather:
return ['rain'] return ["rain"]
elif 'rain' in self.__weather: elif "rain" in self.__weather:
return ['rain'] return ["rain"]
elif 'snow' in self.__weather: elif "snow" in self.__weather:
return ['snow'] return ["snow"]
elif 'sleet' in self.__weather: elif "sleet" in self.__weather:
return ['sleet'] return ["sleet"]
elif 'clear' in self.__weather: elif "clear" in self.__weather:
return ['clear'] return ["clear"]
elif 'cloud' in self.__weather: elif "cloud" in self.__weather:
return ['clouds'] return ["clouds"]
return [] return []
def update(self): def update(self):
try: try:
weather_url = 'http://api.openweathermap.org/data/2.5/weather?appid={}'.format(self.__apikey) weather_url = "http://api.openweathermap.org/data/2.5/weather?appid={}".format(
weather_url = '{}&units={}'.format(weather_url, self.__unit) self.__apikey
if self.__location[self.__index] == 'auto': )
weather_url = "{}&units={}".format(weather_url, self.__unit)
if self.__location[self.__index] == "auto":
coord = util.location.coordinates() coord = util.location.coordinates()
weather_url = '{url}&lat={lat}&lon={lon}'.format(url=weather_url, lat=coord[0], lon=coord[1]) weather_url = "{url}&lat={lat}&lon={lon}".format(
url=weather_url, lat=coord[0], lon=coord[1]
)
elif self.__location[self.__index].isdigit(): elif self.__location[self.__index].isdigit():
weather_url = '{url}&id={id}'.format(url=weather_url, id=self.__location[self.__index]) weather_url = "{url}&id={id}".format(
url=weather_url, id=self.__location[self.__index]
)
else: else:
weather_url = '{url}&q={city}'.format(url=weather_url, city=self.__location[self.__index]) weather_url = "{url}&q={city}".format(
url=weather_url, city=self.__location[self.__index]
)
weather = requests.get(weather_url).json() weather = requests.get(weather_url).json()
self.__city = weather['name'] self.__city = weather["name"]
self.__temperature = int(weather['main']['temp']) self.__temperature = int(weather["main"]["temp"])
self.__tempmin = int(weather['main']['temp_min']) self.__tempmin = int(weather["main"]["temp_min"])
self.__tempmax = int(weather['main']['temp_max']) self.__tempmax = int(weather["main"]["temp_max"])
self.__weather = weather['weather'][0]['main'].lower() self.__weather = weather["weather"][0]["main"].lower()
self.__valid = True self.__valid = True
except Exception: except Exception:
self.__valid = False self.__valid = False
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -7,12 +7,16 @@ import core.widget
import core.input import core.input
import core.decorators import core.decorators
class Module(core.module.Module): class Module(core.module.Module):
@core.decorators.never @core.decorators.never
def __init__(self, config, theme): def __init__(self, config, theme):
super().__init__(config, theme, core.widget.Widget('xkcd')) super().__init__(config, theme, core.widget.Widget("xkcd"))
core.input.register(self, button=core.input.LEFT_MOUSE, core.input.register(
cmd="xdg-open https://c.xkcd.com/random/comic/" self,
button=core.input.LEFT_MOUSE,
cmd="xdg-open https://c.xkcd.com/random/comic/",
) )
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -15,21 +15,25 @@ import core.module
import core.widget import core.widget
import core.decorators import core.decorators
class Module(core.module.Module): class Module(core.module.Module):
@core.decorators.every(seconds=5) @core.decorators.every(seconds=5)
def __init__(self, config, theme): def __init__(self, config, theme):
super().__init__(config, theme, core.widget.Widget(self.keystate)) super().__init__(config, theme, core.widget.Widget(self.keystate))
self.__keystate = 'No YubiKey' self.__keystate = "No YubiKey"
def keystate(self, widget): def keystate(self, widget):
return self.__keystate return self.__keystate
def update(self): def update(self):
try: try:
self.__keystate = "YubiKey: " + str(yubico.find_yubikey(debug=False).serial()) self.__keystate = "YubiKey: " + str(
yubico.find_yubikey(debug=False).serial()
)
except yubico.yubico_exception.YubicoError: except yubico.yubico_exception.YubicoError:
self.__keystate = "No YubiKey" self.__keystate = "No YubiKey"
except Exception: except Exception:
self.__keystate = 'n/a' self.__keystate = "n/a"
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -36,44 +36,79 @@ import core.widget
import util.cli import util.cli
import util.format import util.format
class Module(core.module.Module): class Module(core.module.Module):
def __init__(self, config, theme): def __init__(self, config, theme):
super().__init__(config, theme, []) super().__init__(config, theme, [])
self._includelist = set(filter(lambda x: len(x) > 0, self._includelist = set(
util.format.aslist(self.parameter('list', default='')))) filter(
self._format = self.parameter('format', default='{name} {shortstatus} {used}/{size} ' + lambda x: len(x) > 0,
'({percentfree}%)') util.format.aslist(self.parameter("list", default="")),
self._usesudo = util.format.asbool(self.parameter('sudo', default=False)) )
self._showio = util.format.asbool(self.parameter('showio', default=True)) )
self._ioformat = self.parameter('ioformat', default='{band}') self._format = self.parameter(
self._warnfree = int(self.parameter('warnfree', default=10)) "format", default="{name} {shortstatus} {used}/{size} " + "({percentfree}%)"
)
self._usesudo = util.format.asbool(self.parameter("sudo", default=False))
self._showio = util.format.asbool(self.parameter("showio", default=True))
self._ioformat = self.parameter("ioformat", default="{band}")
self._warnfree = int(self.parameter("warnfree", default=10))
def update(self): def update(self):
widgets = self.widgets() widgets = self.widgets()
zfs_version_path = '/sys/module/zfs/version' zfs_version_path = "/sys/module/zfs/version"
# zpool list -H: List all zpools, use script mode (no headers and tabs as separators). # zpool list -H: List all zpools, use script mode (no headers and tabs as separators).
try: try:
with open(zfs_version_path, 'r') as zfs_mod_version: with open(zfs_version_path, "r") as zfs_mod_version:
zfs_version = zfs_mod_version.readline().rstrip().split('-')[0] zfs_version = zfs_mod_version.readline().rstrip().split("-")[0]
except IOError: except IOError:
# ZFS isn't installed or the module isn't loaded, stub the version # ZFS isn't installed or the module isn't loaded, stub the version
zfs_version = '0.0.0' zfs_version = "0.0.0"
logging.error('ZFS version information not found at {}, check the module is loaded.'.format(zfs_version_path)) logging.error(
"ZFS version information not found at {}, check the module is loaded.".format(
zfs_version_path
)
)
raw_zpools = util.cli.execute(('sudo ' if self._usesudo else '') + 'zpool list -H').split('\n') raw_zpools = util.cli.execute(
("sudo " if self._usesudo else "") + "zpool list -H"
).split("\n")
for widget in widgets: for widget in widgets:
widget.set('visited', False) widget.set("visited", False)
for raw_zpool in raw_zpools: for raw_zpool in raw_zpools:
try: try:
# Ignored fields (assigned to _) are 'expandsz' and 'altroot', also 'ckpoint' in ZFS 0.8.0+ # Ignored fields (assigned to _) are 'expandsz' and 'altroot', also 'ckpoint' in ZFS 0.8.0+
if parse_version(zfs_version) < parse_version('0.8.0'): if parse_version(zfs_version) < parse_version("0.8.0"):
name, size, alloc, free, _, frag, cap, dedup, health, _ = raw_zpool.split('\t') (
name,
size,
alloc,
free,
_,
frag,
cap,
dedup,
health,
_,
) = raw_zpool.split("\t")
else: else:
name, size, alloc, free, _, _, frag, cap, dedup, health, _ = raw_zpool.split('\t') (
cap = cap.rstrip('%') name,
size,
alloc,
free,
_,
_,
frag,
cap,
dedup,
health,
_,
) = raw_zpool.split("\t")
cap = cap.rstrip("%")
percentuse = int(cap) percentuse = int(cap)
percentfree = 100 - percentuse percentfree = 100 - percentuse
# There is a command, zpool iostat, which is however blocking and was therefore # There is a command, zpool iostat, which is however blocking and was therefore
@ -83,7 +118,7 @@ class Module(core.module.Module):
# (and timestamp) during each widget update, and during the next widget update we # (and timestamp) during each widget update, and during the next widget update we
# use them to compute delta of transferred bytes, and using the last and current # use them to compute delta of transferred bytes, and using the last and current
# timestamp the rate at which they have been transferred. # timestamp the rate at which they have been transferred.
with open('/proc/spl/kstat/zfs/{}/io'.format(name), 'r') as f: with open("/proc/spl/kstat/zfs/{}/io".format(name), "r") as f:
# Third row provides data we need, we are interested in the first 4 values. # Third row provides data we need, we are interested in the first 4 values.
# More info about this file can be found here: # More info about this file can be found here:
# https://github.com/zfsonlinux/zfs/blob/master/lib/libspl/include/sys/kstat.h#L580 # https://github.com/zfsonlinux/zfs/blob/master/lib/libspl/include/sys/kstat.h#L580
@ -100,12 +135,12 @@ class Module(core.module.Module):
widget = self.widget(name) widget = self.widget(name)
if not widget: if not widget:
widget = core.widget.Widget(name=name) widget = core.widget.Widget(name=name)
widget.set('last_iostat', [0, 0, 0, 0]) widget.set("last_iostat", [0, 0, 0, 0])
widget.set('last_timestamp', 0) widget.set("last_timestamp", 0)
widgets.append(widget) widgets.append(widget)
delta_iostat = [b - a for a, b in zip(iostat, widget.get('last_iostat'))] delta_iostat = [b - a for a, b in zip(iostat, widget.get("last_iostat"))]
widget.set('last_iostat', iostat) widget.set("last_iostat", iostat)
# From docs: # From docs:
# > Note that even though the time is always returned as a floating point number, not # > Note that even though the time is always returned as a floating point number, not
@ -114,8 +149,8 @@ class Module(core.module.Module):
# Also, during one update cycle the reported I/O may be garbage if the system time # Also, during one update cycle the reported I/O may be garbage if the system time
# was changed. # was changed.
timestamp = time.time() timestamp = time.time()
delta_timestamp = widget.get('last_timestamp') - timestamp delta_timestamp = widget.get("last_timestamp") - timestamp
widget.set('last_timestamp', time.time()) widget.set("last_timestamp", time.time())
# abs is there because sometimes the result is -0 # abs is there because sometimes the result is -0
rate_iostat = [abs(x / delta_timestamp) for x in delta_iostat] rate_iostat = [abs(x / delta_timestamp) for x in delta_iostat]
@ -123,17 +158,26 @@ class Module(core.module.Module):
# theme.minwidth is not set since these values are not expected to change # theme.minwidth is not set since these values are not expected to change
# rapidly # rapidly
widget.full_text(self._format.format(name=name, used=alloc, left=free, size=size, widget.full_text(
percentfree=percentfree, percentuse=percentuse, self._format.format(
name=name,
used=alloc,
left=free,
size=size,
percentfree=percentfree,
percentuse=percentuse,
status=health, status=health,
shortstatus=self._shortstatus(health), shortstatus=self._shortstatus(health),
fragpercent=frag, deduppercent=dedup)) fragpercent=frag,
widget.set('state', health) deduppercent=dedup,
widget.set('percentfree', percentfree) )
widget.set('visited', True) )
widget.set("state", health)
widget.set("percentfree", percentfree)
widget.set("visited", True)
if self._showio: if self._showio:
wname, rname = [name + x for x in ['__write', '__read']] wname, rname = [name + x for x in ["__write", "__read"]]
widget_w = self.widget(wname) widget_w = self.widget(wname)
widget_r = self.widget(rname) widget_r = self.widget(rname)
if not widget_w or not widget_r: if not widget_w or not widget_r:
@ -141,30 +185,40 @@ class Module(core.module.Module):
widget_w = core.widget.Widget(name=wname) widget_w = core.widget.Widget(name=wname)
widgets.extend([widget_r, widget_w]) widgets.extend([widget_r, widget_w])
for w in [widget_r, widget_w]: for w in [widget_r, widget_w]:
w.set('theme.minwidth', self._ioformat.format(ops=9999, w.set(
band=util.format.bytefmt(999.99*(1024**2)))) "theme.minwidth",
w.set('visited', True) self._ioformat.format(
widget_w.full_text(self._ioformat.format(ops=round(writes), ops=9999, band=util.format.bytefmt(999.99 * (1024 ** 2))
band=util.format.bytefmt(nwritten))) ),
widget_r.full_text(self._ioformat.format(ops=round(reads), )
band=util.format.bytefmt(nread))) w.set("visited", True)
widget_w.full_text(
self._ioformat.format(
ops=round(writes), band=util.format.bytefmt(nwritten)
)
)
widget_r.full_text(
self._ioformat.format(
ops=round(reads), band=util.format.bytefmt(nread)
)
)
for widget in widgets: for widget in widgets:
if widget.get('visited') is False: if widget.get("visited") is False:
widgets.remove(widget) widgets.remove(widget)
self.widgets(widgets) self.widgets(widgets)
def state(self, widget): def state(self, widget):
if widget.name.endswith('__read'): if widget.name.endswith("__read"):
return 'poolread' return "poolread"
elif widget.name.endswith('__write'): elif widget.name.endswith("__write"):
return 'poolwrite' return "poolwrite"
state = widget.get('state') state = widget.get("state")
if state == 'FAULTED': if state == "FAULTED":
return [state, 'critical'] return [state, "critical"]
elif state == 'DEGRADED' or widget.get('percentfree') < self._warnfree: elif state == "DEGRADED" or widget.get("percentfree") < self._warnfree:
return [state, 'warning'] return [state, "warning"]
return state return state
@ -177,13 +231,14 @@ class Module(core.module.Module):
# configuration. A faulted pool has corrupted metadata, or one or more faulted devices, and # configuration. A faulted pool has corrupted metadata, or one or more faulted devices, and
# insufficient replicas to continue functioning. # insufficient replicas to continue functioning.
shortstate = { shortstate = {
'DEGRADED': 'DEG', "DEGRADED": "DEG",
'FAULTED': 'FLT', "FAULTED": "FLT",
'ONLINE': 'ONL', "ONLINE": "ONL",
} }
try: try:
return shortstate[status] return shortstate[status]
except KeyError: except KeyError:
return '' return ""
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -1,6 +1,6 @@
# pylint: disable=C0111,R0903 # pylint: disable=C0111,R0903
'''Displays volume and mute status and controls for PulseAudio devices. Use wheel up and down to change volume, left click mutes, right click opens pavucontrol. """Displays volume and mute status and controls for PulseAudio devices. Use wheel up and down to change volume, left click mutes, right click opens pavucontrol.
Aliases: pasink (use this to control output instead of input), pasource Aliases: pasink (use this to control output instead of input), pasource
@ -16,7 +16,7 @@ Requires the following executable:
* pulseaudio * pulseaudio
* pactl * pactl
* pavucontrol * pavucontrol
''' """
import re import re
import logging import logging
@ -29,15 +29,18 @@ import util.cli
import util.graph import util.graph
import util.format import util.format
class Module(core.module.Module): class Module(core.module.Module):
def __init__(self, config, theme, channel): def __init__(self, config, theme, channel):
super().__init__(config, theme, core.widget.Widget(self.volume)) super().__init__(config, theme, core.widget.Widget(self.volume))
if util.format.asbool(self.parameter('autostart', False)): if util.format.asbool(self.parameter("autostart", False)):
util.cli.execute('pulseaudio --start', ignore_errors=True) util.cli.execute("pulseaudio --start", ignore_errors=True)
self._change = util.format.asint(self.parameter('percent_change', '2%').strip('%'), 0, 100) self._change = util.format.asint(
self._limit = util.format.asint(self.parameter('limit', '0%').strip('%'), 0) self.parameter("percent_change", "2%").strip("%"), 0, 100
)
self._limit = util.format.asint(self.parameter("limit", "0%").strip("%"), 0)
self._left = 0 self._left = 0
self._right = 0 self._right = 0
@ -45,131 +48,153 @@ class Module(core.module.Module):
self._mute = False self._mute = False
self._failed = False self._failed = False
self._channel = channel self._channel = channel
self._showbars = util.format.asbool(self.parameter('showbars', 0)) self._showbars = util.format.asbool(self.parameter("showbars", 0))
self._patterns = [ self._patterns = [
{'expr': 'Name:', 'callback': (lambda line: False)}, {"expr": "Name:", "callback": (lambda line: False)},
{'expr': 'Mute:', 'callback': (lambda line: self.mute(False if ' no' in line.lower() else True))}, {
{'expr': 'Volume:', 'callback': self.getvolume}, "expr": "Mute:",
"callback": (
lambda line: self.mute(False if " no" in line.lower() else True)
),
},
{"expr": "Volume:", "callback": self.getvolume},
] ]
core.input.register(self, button=core.input.RIGHT_MOUSE, cmd='pavucontrol') core.input.register(self, button=core.input.RIGHT_MOUSE, cmd="pavucontrol")
events = [ events = [
{'type': 'mute', 'action': self.toggle, 'button': core.input.LEFT_MOUSE}, {"type": "mute", "action": self.toggle, "button": core.input.LEFT_MOUSE},
{'type': 'volume', 'action': self.increase_volume, 'button': core.input.WHEEL_UP}, {
{'type': 'volume', 'action': self.decrease_volume, 'button': core.input.WHEEL_DOWN}, "type": "volume",
"action": self.increase_volume,
"button": core.input.WHEEL_UP,
},
{
"type": "volume",
"action": self.decrease_volume,
"button": core.input.WHEEL_DOWN,
},
] ]
for event in events: for event in events:
core.input.register(self, button=event['button'], cmd=event['action']) core.input.register(self, button=event["button"], cmd=event["action"])
def set_volume(self, amount): def set_volume(self, amount):
util.cli.execute('pactl set-{}-{} @DEFAULT_{}@ {}'.format( util.cli.execute(
self._channel, 'volume', self._channel.upper(), amount)) "pactl set-{}-{} @DEFAULT_{}@ {}".format(
self._channel, "volume", self._channel.upper(), amount
)
)
def increase_volume(self, event): def increase_volume(self, event):
if self._limit > 0: # we need to check the limit if self._limit > 0: # we need to check the limit
left = int(self._left) left = int(self._left)
right = int(self._right) right = int(self._right)
if left + self._change >= self._limit or right + self._change >= self._limit: if (
left + self._change >= self._limit
or right + self._change >= self._limit
):
if left == right: if left == right:
# easy case, just set to limit # easy case, just set to limit
self.set_volume('{}%'.format(self._limit)) self.set_volume("{}%".format(self._limit))
return return
else: else:
# don't adjust anymore, since i don't know how to update only one channel # don't adjust anymore, since i don't know how to update only one channel
return return
self.set_volume('+{}%'.format(self._change)) self.set_volume("+{}%".format(self._change))
def decrease_volume(self, event): def decrease_volume(self, event):
self.set_volume('-{}%'.format(self._change)) self.set_volume("-{}%".format(self._change))
def toggle(self, event): def toggle(self, event):
util.cli.execute('pactl set-{}-mute @DEFAULT_{}@ toggle'.format( util.cli.execute(
self._channel, self._channel.upper())) "pactl set-{}-mute @DEFAULT_{}@ toggle".format(
self._channel, self._channel.upper()
)
)
def mute(self, value): def mute(self, value):
self._mute = value self._mute = value
def getvolume(self, line): def getvolume(self, line):
if 'mono' in line: if "mono" in line:
m = re.search(r'mono:.*\s*\/\s*(\d+)%', line) m = re.search(r"mono:.*\s*\/\s*(\d+)%", line)
if m: if m:
self._mono = m.group(1) self._mono = m.group(1)
else: else:
m = re.search(r'left:.*\s*\/\s*(\d+)%.*right:.*\s*\/\s*(\d+)%', line) m = re.search(r"left:.*\s*\/\s*(\d+)%.*right:.*\s*\/\s*(\d+)%", line)
if m: if m:
self._left = m.group(1) self._left = m.group(1)
self._right = m.group(2) self._right = m.group(2)
def _default_device(self): def _default_device(self):
output = util.cli.execute('pactl info') output = util.cli.execute("pactl info")
pattern = 'Default {}: '.format('Sink' if self._channel == 'sink' else 'Source') pattern = "Default {}: ".format("Sink" if self._channel == "sink" else "Source")
for line in output.split('\n'): for line in output.split("\n"):
if line.startswith(pattern): if line.startswith(pattern):
return line.replace(pattern, '') return line.replace(pattern, "")
logging.error('no pulseaudio device found') logging.error("no pulseaudio device found")
return 'n/a' return "n/a"
def volume(self, widget): def volume(self, widget):
if self._failed == True: if self._failed == True:
return 'n/a' return "n/a"
if int(self._mono) > 0: if int(self._mono) > 0:
vol = '{}%'.format(self._mono) vol = "{}%".format(self._mono)
if self._showbars: if self._showbars:
vol = '{} {}'.format( vol = "{} {}".format(vol, util.graph.hbar(float(self._mono)))
vol, util.graph.hbar(float(self._mono)))
return vol return vol
elif self._left == self._right: elif self._left == self._right:
vol = '{}%'.format(self._left) vol = "{}%".format(self._left)
if self._showbars: if self._showbars:
vol = '{} {}'.format( vol = "{} {}".format(vol, util.graph.hbar(float(self._left)))
vol, util.graph.hbar(float(self._left)))
return vol return vol
else: else:
vol = '{}%/{}%'.format(self._left, self._right) vol = "{}%/{}%".format(self._left, self._right)
if self._showbars: if self._showbars:
vol = '{} {}{}'.format( vol = "{} {}{}".format(
vol, vol,
util.graph.hbar(float(self._left)), util.graph.hbar(float(self._left)),
util.graph.hbar(float(self._right))) util.graph.hbar(float(self._right)),
)
return vol return vol
def update(self): def update(self):
try: try:
self._failed = False self._failed = False
channel = 'sinks' if self._channel == 'sink' else 'sources' channel = "sinks" if self._channel == "sink" else "sources"
device = self._default_device() device = self._default_device()
result = util.cli.execute('pactl list {}'.format(channel)) result = util.cli.execute("pactl list {}".format(channel))
found = False found = False
for line in result.split('\n'): for line in result.split("\n"):
if 'Name: {}'.format(device) in line: if "Name: {}".format(device) in line:
found = True found = True
continue continue
if found is False: if found is False:
continue continue
for pattern in self._patterns: for pattern in self._patterns:
if not pattern['expr'] in line: if not pattern["expr"] in line:
continue continue
if pattern['callback'](line) is False and found == True: if pattern["callback"](line) is False and found == True:
return return
except Exception as e: except Exception as e:
self._failed = True self._failed = True
logging.exception(e) logging.exception(e)
if util.format.asbool(self.parameter('autostart', False)): if util.format.asbool(self.parameter("autostart", False)):
util.cli.execute('pulseaudio --start', ignore_errors=True) util.cli.execute("pulseaudio --start", ignore_errors=True)
else: else:
raise e raise e
def state(self, widget): def state(self, widget):
if self._mute: if self._mute:
return ['warning', 'muted'] return ["warning", "muted"]
if int(self._left) > int(100): if int(self._left) > int(100):
return ['critical', 'unmuted'] return ["critical", "unmuted"]
return ['unmuted'] return ["unmuted"]
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -17,7 +17,8 @@ import core.input
import util.format import util.format
class UPowerManager():
class UPowerManager:
def __init__(self): def __init__(self):
self.UPOWER_NAME = "org.freedesktop.UPower" self.UPOWER_NAME = "org.freedesktop.UPower"
self.UPOWER_PATH = "/org/freedesktop/UPower" self.UPOWER_PATH = "/org/freedesktop/UPower"
@ -56,64 +57,100 @@ class UPowerManager():
battery_proxy = self.bus.get_object(self.UPOWER_NAME, battery) battery_proxy = self.bus.get_object(self.UPOWER_NAME, battery)
battery_proxy_interface = dbus.Interface(battery_proxy, self.DBUS_PROPERTIES) battery_proxy_interface = dbus.Interface(battery_proxy, self.DBUS_PROPERTIES)
hasHistory = battery_proxy_interface.Get(self.UPOWER_NAME + ".Device", "HasHistory") hasHistory = battery_proxy_interface.Get(
hasStatistics = battery_proxy_interface.Get(self.UPOWER_NAME + ".Device", "HasStatistics") self.UPOWER_NAME + ".Device", "HasHistory"
isPresent = battery_proxy_interface.Get(self.UPOWER_NAME + ".Device", "IsPresent") )
isRechargable = battery_proxy_interface.Get(self.UPOWER_NAME + ".Device", "IsRechargeable") hasStatistics = battery_proxy_interface.Get(
self.UPOWER_NAME + ".Device", "HasStatistics"
)
isPresent = battery_proxy_interface.Get(
self.UPOWER_NAME + ".Device", "IsPresent"
)
isRechargable = battery_proxy_interface.Get(
self.UPOWER_NAME + ".Device", "IsRechargeable"
)
online = battery_proxy_interface.Get(self.UPOWER_NAME + ".Device", "Online") online = battery_proxy_interface.Get(self.UPOWER_NAME + ".Device", "Online")
powersupply = battery_proxy_interface.Get(self.UPOWER_NAME + ".Device", "PowerSupply") powersupply = battery_proxy_interface.Get(
self.UPOWER_NAME + ".Device", "PowerSupply"
)
capacity = battery_proxy_interface.Get(self.UPOWER_NAME + ".Device", "Capacity") capacity = battery_proxy_interface.Get(self.UPOWER_NAME + ".Device", "Capacity")
energy = battery_proxy_interface.Get(self.UPOWER_NAME + ".Device", "Energy") energy = battery_proxy_interface.Get(self.UPOWER_NAME + ".Device", "Energy")
energyempty = battery_proxy_interface.Get(self.UPOWER_NAME + ".Device", "EnergyEmpty") energyempty = battery_proxy_interface.Get(
energyfull = battery_proxy_interface.Get(self.UPOWER_NAME + ".Device", "EnergyFull") self.UPOWER_NAME + ".Device", "EnergyEmpty"
energyfulldesign = battery_proxy_interface.Get(self.UPOWER_NAME + ".Device", "EnergyFullDesign") )
energyrate = battery_proxy_interface.Get(self.UPOWER_NAME + ".Device", "EnergyRate") energyfull = battery_proxy_interface.Get(
luminosity = battery_proxy_interface.Get(self.UPOWER_NAME + ".Device", "Luminosity") self.UPOWER_NAME + ".Device", "EnergyFull"
percentage = battery_proxy_interface.Get(self.UPOWER_NAME + ".Device", "Percentage") )
temperature = battery_proxy_interface.Get(self.UPOWER_NAME + ".Device", "Temperature") energyfulldesign = battery_proxy_interface.Get(
self.UPOWER_NAME + ".Device", "EnergyFullDesign"
)
energyrate = battery_proxy_interface.Get(
self.UPOWER_NAME + ".Device", "EnergyRate"
)
luminosity = battery_proxy_interface.Get(
self.UPOWER_NAME + ".Device", "Luminosity"
)
percentage = battery_proxy_interface.Get(
self.UPOWER_NAME + ".Device", "Percentage"
)
temperature = battery_proxy_interface.Get(
self.UPOWER_NAME + ".Device", "Temperature"
)
voltage = battery_proxy_interface.Get(self.UPOWER_NAME + ".Device", "Voltage") voltage = battery_proxy_interface.Get(self.UPOWER_NAME + ".Device", "Voltage")
timetoempty = battery_proxy_interface.Get(self.UPOWER_NAME + ".Device", "TimeToEmpty") timetoempty = battery_proxy_interface.Get(
timetofull = battery_proxy_interface.Get(self.UPOWER_NAME + ".Device", "TimeToFull") self.UPOWER_NAME + ".Device", "TimeToEmpty"
)
timetofull = battery_proxy_interface.Get(
self.UPOWER_NAME + ".Device", "TimeToFull"
)
iconname = battery_proxy_interface.Get(self.UPOWER_NAME + ".Device", "IconName") iconname = battery_proxy_interface.Get(self.UPOWER_NAME + ".Device", "IconName")
model = battery_proxy_interface.Get(self.UPOWER_NAME + ".Device", "Model") model = battery_proxy_interface.Get(self.UPOWER_NAME + ".Device", "Model")
nativepath = battery_proxy_interface.Get(self.UPOWER_NAME + ".Device", "NativePath") nativepath = battery_proxy_interface.Get(
self.UPOWER_NAME + ".Device", "NativePath"
)
serial = battery_proxy_interface.Get(self.UPOWER_NAME + ".Device", "Serial") serial = battery_proxy_interface.Get(self.UPOWER_NAME + ".Device", "Serial")
vendor = battery_proxy_interface.Get(self.UPOWER_NAME + ".Device", "Vendor") vendor = battery_proxy_interface.Get(self.UPOWER_NAME + ".Device", "Vendor")
state = battery_proxy_interface.Get(self.UPOWER_NAME + ".Device", "State") state = battery_proxy_interface.Get(self.UPOWER_NAME + ".Device", "State")
technology = battery_proxy_interface.Get(self.UPOWER_NAME + ".Device", "Technology") technology = battery_proxy_interface.Get(
self.UPOWER_NAME + ".Device", "Technology"
)
battype = battery_proxy_interface.Get(self.UPOWER_NAME + ".Device", "Type") battype = battery_proxy_interface.Get(self.UPOWER_NAME + ".Device", "Type")
warninglevel = battery_proxy_interface.Get(self.UPOWER_NAME + ".Device", "WarningLevel") warninglevel = battery_proxy_interface.Get(
updatetime = battery_proxy_interface.Get(self.UPOWER_NAME + ".Device", "UpdateTime") self.UPOWER_NAME + ".Device", "WarningLevel"
)
updatetime = battery_proxy_interface.Get(
self.UPOWER_NAME + ".Device", "UpdateTime"
)
information_table = { information_table = {
'HasHistory': hasHistory, "HasHistory": hasHistory,
'HasStatistics': hasStatistics, "HasStatistics": hasStatistics,
'IsPresent': isPresent, "IsPresent": isPresent,
'IsRechargeable': isRechargable, "IsRechargeable": isRechargable,
'Online': online, "Online": online,
'PowerSupply': powersupply, "PowerSupply": powersupply,
'Capacity': capacity, "Capacity": capacity,
'Energy': energy, "Energy": energy,
'EnergyEmpty': energyempty, "EnergyEmpty": energyempty,
'EnergyFull': energyfull, "EnergyFull": energyfull,
'EnergyFullDesign': energyfulldesign, "EnergyFullDesign": energyfulldesign,
'EnergyRate': energyrate, "EnergyRate": energyrate,
'Luminosity': luminosity, "Luminosity": luminosity,
'Percentage': percentage, "Percentage": percentage,
'Temperature': temperature, "Temperature": temperature,
'Voltage': voltage, "Voltage": voltage,
'TimeToEmpty': timetoempty, "TimeToEmpty": timetoempty,
'TimeToFull': timetofull, "TimeToFull": timetofull,
'IconName': iconname, "IconName": iconname,
'Model': model, "Model": model,
'NativePath': nativepath, "NativePath": nativepath,
'Serial': serial, "Serial": serial,
'Vendor': vendor, "Vendor": vendor,
'State': state, "State": state,
'Technology': technology, "Technology": technology,
'Type': battype, "Type": battype,
'WarningLevel': warninglevel, "WarningLevel": warninglevel,
'UpdateTime': updatetime "UpdateTime": updatetime,
} }
return information_table return information_table
@ -122,40 +159,48 @@ class UPowerManager():
upower_proxy = self.bus.get_object(self.UPOWER_NAME, self.UPOWER_PATH) upower_proxy = self.bus.get_object(self.UPOWER_NAME, self.UPOWER_PATH)
upower_interface = dbus.Interface(upower_proxy, self.DBUS_PROPERTIES) upower_interface = dbus.Interface(upower_proxy, self.DBUS_PROPERTIES)
is_lid_present = bool(upower_interface.Get(self.UPOWER_NAME, 'LidIsPresent')) is_lid_present = bool(upower_interface.Get(self.UPOWER_NAME, "LidIsPresent"))
return is_lid_present return is_lid_present
def is_lid_closed(self): def is_lid_closed(self):
upower_proxy = self.bus.get_object(self.UPOWER_NAME, self.UPOWER_PATH) upower_proxy = self.bus.get_object(self.UPOWER_NAME, self.UPOWER_PATH)
upower_interface = dbus.Interface(upower_proxy, self.DBUS_PROPERTIES) upower_interface = dbus.Interface(upower_proxy, self.DBUS_PROPERTIES)
is_lid_closed = bool(upower_interface.Get(self.UPOWER_NAME, 'LidIsClosed')) is_lid_closed = bool(upower_interface.Get(self.UPOWER_NAME, "LidIsClosed"))
return is_lid_closed return is_lid_closed
def on_battery(self): def on_battery(self):
upower_proxy = self.bus.get_object(self.UPOWER_NAME, self.UPOWER_PATH) upower_proxy = self.bus.get_object(self.UPOWER_NAME, self.UPOWER_PATH)
upower_interface = dbus.Interface(upower_proxy, self.DBUS_PROPERTIES) upower_interface = dbus.Interface(upower_proxy, self.DBUS_PROPERTIES)
on_battery = bool(upower_interface.Get(self.UPOWER_NAME, 'OnBattery')) on_battery = bool(upower_interface.Get(self.UPOWER_NAME, "OnBattery"))
return on_battery return on_battery
def has_wakeup_capabilities(self): def has_wakeup_capabilities(self):
upower_proxy = self.bus.get_object(self.UPOWER_NAME, self.UPOWER_PATH + "/Wakeups") upower_proxy = self.bus.get_object(
self.UPOWER_NAME, self.UPOWER_PATH + "/Wakeups"
)
upower_interface = dbus.Interface(upower_proxy, self.DBUS_PROPERTIES) upower_interface = dbus.Interface(upower_proxy, self.DBUS_PROPERTIES)
has_wakeup_capabilities = bool(upower_interface.Get(self.UPOWER_NAME + '.Wakeups', 'HasCapability')) has_wakeup_capabilities = bool(
upower_interface.Get(self.UPOWER_NAME + ".Wakeups", "HasCapability")
)
return has_wakeup_capabilities return has_wakeup_capabilities
def get_wakeups_data(self): def get_wakeups_data(self):
upower_proxy = self.bus.get_object(self.UPOWER_NAME, self.UPOWER_PATH + "/Wakeups") upower_proxy = self.bus.get_object(
upower_interface = dbus.Interface(upower_proxy, self.UPOWER_NAME + '.Wakeups') self.UPOWER_NAME, self.UPOWER_PATH + "/Wakeups"
)
upower_interface = dbus.Interface(upower_proxy, self.UPOWER_NAME + ".Wakeups")
data = upower_interface.GetData() data = upower_interface.GetData()
return data return data
def get_wakeups_total(self): def get_wakeups_total(self):
upower_proxy = self.bus.get_object(self.UPOWER_NAME, self.UPOWER_PATH + "/Wakeups") upower_proxy = self.bus.get_object(
upower_interface = dbus.Interface(upower_proxy, self.UPOWER_NAME + '.Wakeups') self.UPOWER_NAME, self.UPOWER_PATH + "/Wakeups"
)
upower_interface = dbus.Interface(upower_proxy, self.UPOWER_NAME + ".Wakeups")
data = upower_interface.GetTotal() data = upower_interface.GetTotal()
return data return data
@ -166,7 +211,7 @@ class UPowerManager():
state = int(battery_proxy_interface.Get(self.UPOWER_NAME + ".Device", "State")) state = int(battery_proxy_interface.Get(self.UPOWER_NAME + ".Device", "State"))
if (state == 1): if state == 1:
return True return True
else: else:
return False return False
@ -177,21 +222,22 @@ class UPowerManager():
state = int(battery_proxy_interface.Get(self.UPOWER_NAME + ".Device", "State")) state = int(battery_proxy_interface.Get(self.UPOWER_NAME + ".Device", "State"))
if (state == 0): if state == 0:
return "Unknown" return "Unknown"
elif (state == 1): elif state == 1:
return "Loading" return "Loading"
elif (state == 2): elif state == 2:
return "Discharging" return "Discharging"
elif (state == 3): elif state == 3:
return "Empty" return "Empty"
elif (state == 4): elif state == 4:
return "Fully charged" return "Fully charged"
elif (state == 5): elif state == 5:
return "Pending charge" return "Pending charge"
elif (state == 6): elif state == 6:
return "Pending discharge" return "Pending discharge"
class Module(core.module.Module): class Module(core.module.Module):
def __init__(self, config, theme): def __init__(self, config, theme):
super().__init__(config, theme, core.widget.Widget(self.capacity)) super().__init__(config, theme, core.widget.Widget(self.capacity))
@ -201,11 +247,11 @@ class Module(core.module.Module):
self.device = self.power.get_display_device() self.device = self.power.get_display_device()
except Exception as e: except Exception as e:
logging.exception("unable to get battery display device: {}".format(str(e))) logging.exception("unable to get battery display device: {}".format(str(e)))
core.input.register(self, button=core.input.LEFT_MOUSE, core.input.register(
cmd="gnome-power-statistics") self, button=core.input.LEFT_MOUSE, cmd="gnome-power-statistics"
)
self._showremaining = util.format.asbool( self._showremaining = util.format.asbool(self.parameter("showremaining", True))
self.parameter("showremaining", True))
def capacity(self, widget): def capacity(self, widget):
widget.set("capacity", -1) widget.set("capacity", -1)
@ -227,14 +273,20 @@ class Module(core.module.Module):
interface = dbus.Interface(proxy, p.DBUS_PROPERTIES) interface = dbus.Interface(proxy, p.DBUS_PROPERTIES)
state = int(interface.Get(p.UPOWER_NAME + ".Device", "State")) state = int(interface.Get(p.UPOWER_NAME + ".Device", "State"))
# state: 1 => charging, 2 => discharging, other => don't care # state: 1 => charging, 2 => discharging, other => don't care
remain = int(interface.Get( remain = int(
p.UPOWER_NAME+".Device", ["TimeToFull", "TimeToEmpty"][state-1])) interface.Get(
p.UPOWER_NAME + ".Device",
["TimeToFull", "TimeToEmpty"][state - 1],
)
)
remain = util.format.duration(remain, compact=True, unit=True) remain = util.format.duration(remain, compact=True, unit=True)
output = "{} {}".format(output, remain) output = "{} {}".format(output, remain)
except IndexError: except IndexError:
pass pass
except Exception as e: except Exception as e:
logging.exception("unable to get battery remaining time: {}".format(str(e))) logging.exception(
"unable to get battery remaining time: {}".format(str(e))
)
return output return output
@ -258,9 +310,17 @@ class Module(core.module.Module):
except Exception as e: except Exception as e:
logging.exception("unable to get charge value: {}".format(str(e))) logging.exception("unable to get charge value: {}".format(str(e)))
if charge == "Discharging": if charge == "Discharging":
state.append("discharging-{}".format(min([10, 25, 50, 80, 100], key=lambda i: abs(i - capacity)))) state.append(
"discharging-{}".format(
min([10, 25, 50, 80, 100], key=lambda i: abs(i - capacity))
)
)
elif charge == "Unknown": elif charge == "Unknown":
state.append("unknown-{}".format(min([10, 25, 50, 80, 100], key=lambda i: abs(i - capacity)))) state.append(
"unknown-{}".format(
min([10, 25, 50, 80, 100], key=lambda i: abs(i - capacity))
)
)
else: else:
if capacity > 95: if capacity > 95:
state.append("charged") state.append("charged")
@ -268,4 +328,5 @@ class Module(core.module.Module):
state.append("charging") state.append("charging")
return state return state
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -15,6 +15,7 @@ Parameters:
import os import os
import glob import glob
import logging import logging
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
try: try:
@ -28,6 +29,7 @@ import core.input
import util.format import util.format
class BatteryManager(object): class BatteryManager(object):
def remaining(self): def remaining(self):
try: try:
@ -41,20 +43,19 @@ class BatteryManager(object):
return -1 return -1
def read(self, battery, component, default=None): def read(self, battery, component, default=None):
path = '/sys/class/power_supply/{}'.format(battery) path = "/sys/class/power_supply/{}".format(battery)
if not os.path.exists(path): if not os.path.exists(path):
return default return default
try: try:
with open('{}/{}'.format(path, component)) as f: with open("{}/{}".format(path, component)) as f:
return f.read().strip() return f.read().strip()
except IOError: except IOError:
return 'n/a' return "n/a"
return default return default
def capacity(self, battery): def capacity(self, battery):
capacity = self.read(battery, 'capacity', 100) capacity = self.read(battery, "capacity", 100)
if capacity != 'n/a': if capacity != "n/a":
capacity = int(capacity) capacity = int(capacity)
return capacity if capacity < 100 else 100 return capacity if capacity < 100 else 100
@ -64,37 +65,41 @@ class BatteryManager(object):
full = 0 full = 0
for battery in batteries: for battery in batteries:
try: try:
with open('/sys/class/power_supply/{}/energy_full'.format(battery)) as f: with open(
"/sys/class/power_supply/{}/energy_full".format(battery)
) as f:
full += int(f.read()) full += int(f.read())
with open('/sys/class/power_supply/{}/energy_now'.format(battery)) as f: with open("/sys/class/power_supply/{}/energy_now".format(battery)) as f:
now += int(f.read()) now += int(f.read())
except IOError: except IOError:
return 'n/a' return "n/a"
return int(float(now) / float(full) * 100.0) return int(float(now) / float(full) * 100.0)
def isac(self, battery): def isac(self, battery):
path = '/sys/class/power_supply/{}'.format(battery) path = "/sys/class/power_supply/{}".format(battery)
return not os.path.exists(path) return not os.path.exists(path)
def isac_any(self, batteries): def isac_any(self, batteries):
for battery in batteries: for battery in batteries:
if self.isac(battery): return True if self.isac(battery):
return True
return False return False
def consumption(self, battery): def consumption(self, battery):
consumption = self.read(battery, 'power_now', 'n/a') consumption = self.read(battery, "power_now", "n/a")
if consumption == 'n/a': if consumption == "n/a":
return 'n/a' return "n/a"
return '{}W'.format(int(consumption)/1000000) return "{}W".format(int(consumption) / 1000000)
def charge(self, battery): def charge(self, battery):
return self.read(battery, 'status', 'n/a') return self.read(battery, "status", "n/a")
def charge_any(self, batteries): def charge_any(self, batteries):
for battery in batteries: for battery in batteries:
if self.charge(battery) == 'Discharging': if self.charge(battery) == "Discharging":
return 'Discharging' return "Discharging"
return 'Charged' return "Charged"
class Module(core.module.Module): class Module(core.module.Module):
def __init__(self, config, theme): def __init__(self, config, theme):
@ -103,81 +108,104 @@ class Module(core.module.Module):
self.__manager = BatteryManager() self.__manager = BatteryManager()
self._batteries = util.format.aslist(self.parameter('device', 'auto')) self._batteries = util.format.aslist(self.parameter("device", "auto"))
if self._batteries[0] == 'auto': if self._batteries[0] == "auto":
self._batteries = [ os.path.basename(battery) for battery in glob.glob('/sys/class/power_supply/BAT*') ] self._batteries = [
os.path.basename(battery)
for battery in glob.glob("/sys/class/power_supply/BAT*")
]
if len(self._batteries) == 0: if len(self._batteries) == 0:
raise Exceptions('no batteries configured/found') raise Exceptions("no batteries configured/found")
core.input.register(self, button=core.input.LEFT_MOUSE, core.input.register(
cmd='gnome-power-statistics') self, button=core.input.LEFT_MOUSE, cmd="gnome-power-statistics"
)
if util.format.asbool(self.parameter('compact-devices', False)): if util.format.asbool(self.parameter("compact-devices", False)):
widget = core.widget.Widget(full_text=self.capacity, name='all-batteries', module=self) widget = core.widget.Widget(
full_text=self.capacity, name="all-batteries", module=self
)
widgets.append(widget) widgets.append(widget)
else: else:
for battery in self._batteries: for battery in self._batteries:
log.debug('adding new widget for {}'.format(battery)) log.debug("adding new widget for {}".format(battery))
widget = core.widget.Widget(full_text=self.capacity, name=battery, module=self) widget = core.widget.Widget(
full_text=self.capacity, name=battery, module=self
)
widgets.append(widget) widgets.append(widget)
for w in self.widgets(): for w in self.widgets():
if util.format.asbool(self.parameter('decorate', True)) == False: if util.format.asbool(self.parameter("decorate", True)) == False:
widget.set('theme.exclude', 'suffix') widget.set("theme.exclude", "suffix")
def capacity(self, widget): def capacity(self, widget):
if widget.name == 'all-batteries': if widget.name == "all-batteries":
capacity = self.__manager.capacity_all(self._batteries) capacity = self.__manager.capacity_all(self._batteries)
else: else:
capacity = self.__manager.capacity(widget.name) capacity = self.__manager.capacity(widget.name)
widget.set('capacity', capacity) widget.set("capacity", capacity)
widget.set('ac', self.__manager.isac_any(self._batteries)) widget.set("ac", self.__manager.isac_any(self._batteries))
widget.set('theme.minwidth', '100%') widget.set("theme.minwidth", "100%")
# Read power conumption # Read power conumption
if util.format.asbool(self.parameter('showpowerconsumption', False)): if util.format.asbool(self.parameter("showpowerconsumption", False)):
output = '{}% ({})'.format(capacity, self.__manager.consumption(widget.name)) output = "{}% ({})".format(
capacity, self.__manager.consumption(widget.name)
)
else: else:
output = '{}%'.format(capacity) output = "{}%".format(capacity)
if util.format.asbool(self.parameter('showremaining', True))\ if (
and self.__manager.charge(widget.name) == 'Discharging': util.format.asbool(self.parameter("showremaining", True))
and self.__manager.charge(widget.name) == "Discharging"
):
remaining = self.__manager.remaining() remaining = self.__manager.remaining()
if remaining >= 0: if remaining >= 0:
output = '{} {}'.format(output, util.format.duration(remaining, compact=True, unit=True)) output = "{} {}".format(
output, util.format.duration(remaining, compact=True, unit=True)
)
if util.format.asbool(self.parameter('showdevice', False)): if util.format.asbool(self.parameter("showdevice", False)):
output = '{} ({})'.format(output, widget.name) output = "{} ({})".format(output, widget.name)
return output return output
def state(self, widget): def state(self, widget):
state = [] state = []
capacity = widget.get('capacity') capacity = widget.get("capacity")
if capacity < 0: if capacity < 0:
log.debug('battery state: {}'.format(state)) log.debug("battery state: {}".format(state))
return ['critical', 'unknown'] return ["critical", "unknown"]
if capacity < int(self.parameter('critical', 10)): if capacity < int(self.parameter("critical", 10)):
state.append('critical') state.append("critical")
elif capacity < int(self.parameter('warning', 20)): elif capacity < int(self.parameter("warning", 20)):
state.append('warning') state.append("warning")
if widget.get('ac'): if widget.get("ac"):
state.append('AC') state.append("AC")
else: else:
if widget.name == 'all-batteries': if widget.name == "all-batteries":
charge = self.__manager.charge_any(self._batteries) charge = self.__manager.charge_any(self._batteries)
else: else:
charge = self.__manager.charge(widget.name) charge = self.__manager.charge(widget.name)
if charge == 'Discharging': if charge == "Discharging":
state.append('discharging-{}'.format(min([10, 25, 50, 80, 100], key=lambda i: abs(i-capacity)))) state.append(
elif charge == 'Unknown': "discharging-{}".format(
state.append('unknown-{}'.format(min([10, 25, 50, 80, 100], key=lambda i: abs(i-capacity)))) min([10, 25, 50, 80, 100], key=lambda i: abs(i - capacity))
)
)
elif charge == "Unknown":
state.append(
"unknown-{}".format(
min([10, 25, 50, 80, 100], key=lambda i: abs(i - capacity))
)
)
else: else:
if capacity > 95: if capacity > 95:
state.append('charged') state.append("charged")
else: else:
state.append('charging') state.append("charging")
return state return state
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -17,27 +17,29 @@ import core.decorators
import util.cli import util.cli
class Module(core.module.Module): class Module(core.module.Module):
@core.decorators.every(seconds=30) @core.decorators.every(seconds=30)
def __init__(self, config, theme): def __init__(self, config, theme):
super().__init__(config, theme, core.widget.Widget(self.brightness)) super().__init__(config, theme, core.widget.Widget(self.brightness))
self.__brightness = 'n/a' self.__brightness = "n/a"
self.__readcmd = None self.__readcmd = None
step = self.parameter('step', 2) step = self.parameter("step", 2)
if shutil.which('light'): if shutil.which("light"):
self.__readcmd = self.__light self.__readcmd = self.__light
self.register_cmd('light -A {}%'.format(step), self.register_cmd("light -A {}%".format(step), "light -U {}%".format(step))
'light -U {}%'.format(step)) elif shutil.which("brightnessctl"):
elif shutil.which('brightnessctl'):
self.__readcmd = self.__brightnessctl self.__readcmd = self.__brightnessctl
self.register_cmd('brightnessctl s {}%+'.format(step), self.register_cmd(
'brightnessctl s {}%-'.format(step)) "brightnessctl s {}%+".format(step), "brightnessctl s {}%-".format(step)
)
else: else:
self.__readcmd = self.__xbacklight self.__readcmd = self.__xbacklight
self.register_cmd('xbacklight +{}%'.format(step), self.register_cmd(
'xbacklight -{}%'.format(step)) "xbacklight +{}%".format(step), "xbacklight -{}%".format(step)
)
def register_cmd(self, up_cmd, down_cmd): def register_cmd(self, up_cmd, down_cmd):
core.input.register(self, button=core.input.WHEEL_UP, cmd=up_cmd) core.input.register(self, button=core.input.WHEEL_UP, cmd=up_cmd)
@ -47,20 +49,21 @@ class Module(core.module.Module):
return self.__brightness return self.__brightness
def __light(self): def __light(self):
return util.cli.execute('light').strip() return util.cli.execute("light").strip()
def __brightnessctl(self): def __brightnessctl(self):
m = util.cli.execute('brightnessctl m').strip() m = util.cli.execute("brightnessctl m").strip()
g = util.cli.execute('brightnessctl g').strip() g = util.cli.execute("brightnessctl g").strip()
return float(g) / float(m) * 100.0 return float(g) / float(m) * 100.0
def __xbacklight(self): def __xbacklight(self):
return util.cli.execute('xbacklight -get').strip() return util.cli.execute("xbacklight -get").strip()
def update(self): def update(self):
try: try:
self.__brightness = '{:3.0f}%'.format(float(self.__readcmd())) self.__brightness = "{:3.0f}%".format(float(self.__readcmd()))
except: except:
self.__brightness = 'n/a' self.__brightness = "n/a"
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -32,18 +32,21 @@ import core.decorators
import util.cli import util.cli
import util.format import util.format
class Module(core.module.Module): class Module(core.module.Module):
def __init__(self, config, theme): def __init__(self, config, theme):
super().__init__(config, theme, []) super().__init__(config, theme, [])
self._layout = self.parameter('layout', 'cmus.prev cmus.main cmus.next cmus.shuffle cmus.repeat') self._layout = self.parameter(
self._fmt = self.parameter('format', '{artist} - {title} {position}/{duration}') "layout", "cmus.prev cmus.main cmus.next cmus.shuffle cmus.repeat"
self._server = self.parameter('server', None) )
self._passwd = self.parameter('passwd', None) self._fmt = self.parameter("format", "{artist} - {title} {position}/{duration}")
self._server = self.parameter("server", None)
self._passwd = self.parameter("passwd", None)
self._status = None self._status = None
self._shuffle = False self._shuffle = False
self._repeat = False self._repeat = False
self._tags = defaultdict(lambda: '') self._tags = defaultdict(lambda: "")
# Create widgets # Create widgets
widget_list = [] widget_list = []
@ -51,25 +54,48 @@ class Module(core.module.Module):
for widget_name in self._layout.split(): for widget_name in self._layout.split():
widget = core.widget.Widget(name=widget_name, module=self) widget = core.widget.Widget(name=widget_name, module=self)
widget_list.append(widget) widget_list.append(widget)
self._cmd = 'cmus-remote' self._cmd = "cmus-remote"
if self._server is not None: if self._server is not None:
self._cmd = '{cmd} --server {server}'.format(cmd=self._cmd, server=self._server) self._cmd = "{cmd} --server {server}".format(
cmd=self._cmd, server=self._server
)
if self._passwd is not None: if self._passwd is not None:
self._cmd = '{cmd} --passwd {passwd}'.format(cmd=self._cmd, passwd=self._passwd) self._cmd = "{cmd} --passwd {passwd}".format(
cmd=self._cmd, passwd=self._passwd
)
if widget_name == 'cmus.prev': if widget_name == "cmus.prev":
widget_map[widget] = {'button': core.input.LEFT_MOUSE, 'cmd': '{cmd} -r'.format(cmd=self._cmd)} widget_map[widget] = {
elif widget_name == 'cmus.main': "button": core.input.LEFT_MOUSE,
widget_map[widget] = {'button': core.input.LEFT_MOUSE, 'cmd': '{cmd} -u'.format(cmd=self._cmd)} "cmd": "{cmd} -r".format(cmd=self._cmd),
}
elif widget_name == "cmus.main":
widget_map[widget] = {
"button": core.input.LEFT_MOUSE,
"cmd": "{cmd} -u".format(cmd=self._cmd),
}
widget.full_text(self.description) widget.full_text(self.description)
elif widget_name == 'cmus.next': elif widget_name == "cmus.next":
widget_map[widget] = {'button': core.input.LEFT_MOUSE, 'cmd': '{cmd} -n'.format(cmd=self._cmd)} widget_map[widget] = {
elif widget_name == 'cmus.shuffle': "button": core.input.LEFT_MOUSE,
widget_map[widget] = {'button': core.input.LEFT_MOUSE, 'cmd': '{cmd} -S'.format(cmd=self._cmd)} "cmd": "{cmd} -n".format(cmd=self._cmd),
elif widget_name == 'cmus.repeat': }
widget_map[widget] = {'button': core.input.LEFT_MOUSE, 'cmd': '{cmd} -R'.format(cmd=self._cmd)} elif widget_name == "cmus.shuffle":
widget_map[widget] = {
"button": core.input.LEFT_MOUSE,
"cmd": "{cmd} -S".format(cmd=self._cmd),
}
elif widget_name == "cmus.repeat":
widget_map[widget] = {
"button": core.input.LEFT_MOUSE,
"cmd": "{cmd} -R".format(cmd=self._cmd),
}
else: else:
raise KeyError('The cmus module does not support a {widget_name!r} widget'.format(widget_name=widget_name)) raise KeyError(
"The cmus module does not support a {widget_name!r} widget".format(
widget_name=widget_name
)
)
self.widgets(widget_list) self.widgets(widget_list)
# Register input callbacks # Register input callbacks
@ -88,44 +114,45 @@ class Module(core.module.Module):
def state(self, widget): def state(self, widget):
returns = { returns = {
'cmus.shuffle': 'shuffle-on' if self._shuffle else 'shuffle-off', "cmus.shuffle": "shuffle-on" if self._shuffle else "shuffle-off",
'cmus.repeat': 'repeat-on' if self._repeat else 'repeat-off', "cmus.repeat": "repeat-on" if self._repeat else "repeat-off",
'cmus.prev': 'prev', "cmus.prev": "prev",
'cmus.next': 'next', "cmus.next": "next",
} }
return returns.get(widget.name, self._status) return returns.get(widget.name, self._status)
def _eval_line(self, line): def _eval_line(self, line):
if line.startswith('file '): if line.startswith("file "):
full_file = line[5:] full_file = line[5:]
file1 = os.path.basename(full_file) file1 = os.path.basename(full_file)
file2 = os.path.splitext(file1)[0] file2 = os.path.splitext(file1)[0]
self._tags.update({'file': full_file}) self._tags.update({"file": full_file})
self._tags.update({'file1': file1}) self._tags.update({"file1": file1})
self._tags.update({'file2': file2}) self._tags.update({"file2": file2})
return return
name, key, value = (line.split(' ', 2) + [None, None])[:3] name, key, value = (line.split(" ", 2) + [None, None])[:3]
if name == 'status': if name == "status":
self._status = key self._status = key
if name == 'tag': if name == "tag":
self._tags.update({key: value}) self._tags.update({key: value})
if name in ['duration', 'position']: if name in ["duration", "position"]:
self._tags.update({name: util.format.duration(int(key))}) self._tags.update({name: util.format.duration(int(key))})
if name == 'set' and key == 'repeat': if name == "set" and key == "repeat":
self._repeat = value == 'true' self._repeat = value == "true"
if name == 'set' and key == 'shuffle': if name == "set" and key == "shuffle":
self._shuffle = value == 'true' self._shuffle = value == "true"
def _load_song(self): def _load_song(self):
info = '' info = ""
try: try:
info = util.cli.execute('{cmd} -Q'.format(cmd=self._cmd)) info = util.cli.execute("{cmd} -Q".format(cmd=self._cmd))
except RuntimeError: except RuntimeError:
self._status = None self._status = None
self._tags = defaultdict(lambda: '') self._tags = defaultdict(lambda: "")
for line in info.split('\n'): for line in info.split("\n"):
self._eval_line(line) self._eval_line(line)
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -1,12 +1,12 @@
# pylint: disable=C0111,R0903 # pylint: disable=C0111,R0903
'''Displays CPU utilization across all CPUs. """Displays CPU utilization across all CPUs.
Parameters: Parameters:
* cpu.warning : Warning threshold in % of CPU usage (defaults to 70%) * cpu.warning : Warning threshold in % of CPU usage (defaults to 70%)
* cpu.critical: Critical threshold in % of CPU usage (defaults to 80%) * cpu.critical: Critical threshold in % of CPU usage (defaults to 80%)
* cpu.format : Format string (defaults to '{:.01f}%') * cpu.format : Format string (defaults to '{:.01f}%')
''' """
import psutil import psutil
@ -14,17 +14,19 @@ import core.module
import core.widget import core.widget
import core.input import core.input
class Module(core.module.Module): class Module(core.module.Module):
def __init__(self, config, theme): def __init__(self, config, theme):
super().__init__(config, theme, core.widget.Widget(self.utilization)) super().__init__(config, theme, core.widget.Widget(self.utilization))
self.widget().set('theme.minwidth', self._format.format(100.0-10e-20)) self.widget().set("theme.minwidth", self._format.format(100.0 - 10e-20))
self._utilization = psutil.cpu_percent(percpu=False) self._utilization = psutil.cpu_percent(percpu=False)
core.input.register(self, button=core.input.LEFT_MOUSE, core.input.register(
cmd='gnome-system-monitor') self, button=core.input.LEFT_MOUSE, cmd="gnome-system-monitor"
)
@property @property
def _format(self): def _format(self):
return self.parameter('format', '{:.01f}%') return self.parameter("format", "{:.01f}%")
def utilization(self, _): def utilization(self, _):
return self._format.format(self._utilization) return self._format.format(self._utilization)
@ -35,4 +37,5 @@ class Module(core.module.Module):
def state(self, _): def state(self, _):
return self.threshold_state(self._utilization, 70, 80) return self.threshold_state(self._utilization, 70, 80)
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -10,12 +10,14 @@ Parameters:
import core.decorators import core.decorators
from .datetime import Module from .datetime import Module
class Module(Module): class Module(Module):
@core.decorators.every(hours=1) @core.decorators.every(hours=1)
def __init__(self, config, theme): def __init__(self, config, theme):
super().__init__(config, theme) super().__init__(config, theme)
def default_format(self): def default_format(self):
return '%x' return "%x"
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -1,11 +1,11 @@
# pylint: disable=C0111,R0903 # pylint: disable=C0111,R0903
'''Displays the current date and time. """Displays the current date and time.
Parameters: Parameters:
* datetime.format: strftime()-compatible formatting string * datetime.format: strftime()-compatible formatting string
* datetime.locale: locale to use rather than the system default * datetime.locale: locale to use rather than the system default
''' """
from __future__ import absolute_import from __future__ import absolute_import
import datetime import datetime
@ -15,29 +15,31 @@ import core.module
import core.widget import core.widget
import core.input import core.input
class Module(core.module.Module): class Module(core.module.Module):
def __init__(self, config, theme): def __init__(self, config, theme):
super().__init__(config, theme, core.widget.Widget(self.full_text)) super().__init__(config, theme, core.widget.Widget(self.full_text))
core.input.register(self, button=core.input.LEFT_MOUSE, cmd='calendar') core.input.register(self, button=core.input.LEFT_MOUSE, cmd="calendar")
self._fmt = self.parameter('format', self.default_format()) self._fmt = self.parameter("format", self.default_format())
l = locale.getdefaultlocale() l = locale.getdefaultlocale()
if not l or l == (None, None): if not l or l == (None, None):
l = ('en_US', 'UTF-8') l = ("en_US", "UTF-8")
lcl = self.parameter('locale', '.'.join(l)) lcl = self.parameter("locale", ".".join(l))
try: try:
locale.setlocale(locale.LC_TIME, lcl.split('.')) locale.setlocale(locale.LC_TIME, lcl.split("."))
except Exception as e: except Exception as e:
locale.setlocale(locale.LC_TIME, ('en_US', 'UTF-8')) locale.setlocale(locale.LC_TIME, ("en_US", "UTF-8"))
def default_format(self): def default_format(self):
return '%x %X' return "%x %X"
def full_text(self, widget): def full_text(self, widget):
enc = locale.getpreferredencoding() enc = locale.getpreferredencoding()
retval = datetime.datetime.now().strftime(self._fmt) retval = datetime.datetime.now().strftime(self._fmt)
if hasattr(retval, 'decode'): if hasattr(retval, "decode"):
return retval.decode(enc) return retval.decode(enc)
return retval return retval
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -8,15 +8,17 @@ import core.module
import core.widget import core.widget
import core.decorators import core.decorators
class Module(core.module.Module): class Module(core.module.Module):
@core.decorators.every(minutes=60) @core.decorators.every(minutes=60)
def __init__(self, config, theme): def __init__(self, config, theme):
super().__init__(config, theme, core.widget.Widget(self.full_text)) super().__init__(config, theme, core.widget.Widget(self.full_text))
def full_text(self, widgets): def full_text(self, widgets):
return 'debug' return "debug"
def state(self, widget): def state(self, widget):
return 'warning' return "warning"
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -1,6 +1,6 @@
# pylint: disable=C0111,R0903 # pylint: disable=C0111,R0903
'''Shows free diskspace, total diskspace and the percentage of free disk space. """Shows free diskspace, total diskspace and the percentage of free disk space.
Parameters: Parameters:
* disk.warning: Warning threshold in % of disk space (defaults to 80%) * disk.warning: Warning threshold in % of disk space (defaults to 80%)
@ -8,7 +8,7 @@ Parameters:
* disk.path: Path to calculate disk usage from (defaults to /) * disk.path: Path to calculate disk usage from (defaults to /)
* disk.open: Which application / file manager to launch (default xdg-open) * disk.open: Which application / file manager to launch (default xdg-open)
* disk.format: Format string, tags {path}, {used}, {left}, {size} and {percent} (defaults to '{path} {used}/{size} ({percent:05.02f}%)') * disk.format: Format string, tags {path}, {used}, {left}, {size} and {percent} (defaults to '{path} {used}/{size} ({percent:05.02f}%)')
''' """
import os import os
@ -18,20 +18,24 @@ import core.input
import util.format import util.format
class Module(core.module.Module): class Module(core.module.Module):
def __init__(self, config, theme): def __init__(self, config, theme):
super().__init__(config, theme, core.widget.Widget(self.diskspace)) super().__init__(config, theme, core.widget.Widget(self.diskspace))
self._path = self.parameter('path', '/') self._path = self.parameter("path", "/")
self._format = self.parameter('format', '{used}/{size} ({percent:05.02f}%)') self._format = self.parameter("format", "{used}/{size} ({percent:05.02f}%)")
self._used = 0 self._used = 0
self._left = 0 self._left = 0
self._size = 0 self._size = 0
self._percent = 0 self._percent = 0
core.input.register(self, button=core.input.LEFT_MOUSE, core.input.register(
cmd='{} {}'.format(self.parameter('open', 'xdg-open'), self._path)) self,
button=core.input.LEFT_MOUSE,
cmd="{} {}".format(self.parameter("open", "xdg-open"), self._path),
)
def diskspace(self, widget): def diskspace(self, widget):
used_str = util.format.byte(self._used) used_str = util.format.byte(self._used)
@ -39,20 +43,23 @@ class Module(core.module.Module):
left_str = util.format.byte(self._left) left_str = util.format.byte(self._left)
percent_str = self._percent percent_str = self._percent
return self._format.format(path=self._path, return self._format.format(
path=self._path,
used=used_str, used=used_str,
left=left_str, left=left_str,
size=size_str, size=size_str,
percent=percent_str) percent=percent_str,
)
def update(self): def update(self):
st = os.statvfs(self._path) st = os.statvfs(self._path)
self._size = st.f_blocks * st.f_frsize self._size = st.f_blocks * st.f_frsize
self._used = (st.f_blocks - st.f_bfree) * st.f_frsize self._used = (st.f_blocks - st.f_bfree) * st.f_frsize
self._left = self._size - self._used; self._left = self._size - self._used
self._percent = 100.0 * self._used / self._size self._percent = 100.0 * self._used / self._size
def state(self, widget): def state(self, widget):
return self.threshold_state(self._percent, 80, 90) return self.threshold_state(self._percent, 80, 90)
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -8,23 +8,26 @@ import core.module
import core.widget import core.widget
import core.event import core.event
class Module(core.module.Module): class Module(core.module.Module):
def __init__(self, config, theme): def __init__(self, config, theme):
super().__init__(config, theme, core.widget.Widget(self.full_text)) super().__init__(config, theme, core.widget.Widget(self.full_text))
self.__error = '' self.__error = ""
self.__state = 'critical' self.__state = "critical"
core.event.register('error', self.__set_error) core.event.register("error", self.__set_error)
def full_text(self, widgets): def full_text(self, widgets):
return self.__error return self.__error
def __set_error(self, error='n/a', state='critical'): def __set_error(self, error="n/a", state="critical"):
self.__error = error self.__error = error
self.__state = state self.__state = state
def state(self, widget): def state(self, widget):
if self.__error: return [self.__state] if self.__error:
return [self.__state]
return [] return []
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -16,6 +16,7 @@ import core.widget
import util.cli import util.cli
class Module(core.module.Module): class Module(core.module.Module):
def __init__(self, config, theme): def __init__(self, config, theme):
super().__init__(config, theme, []) super().__init__(config, theme, [])
@ -33,25 +34,33 @@ class Module(core.module.Module):
directory = self.__get_git_root(directory) directory = self.__get_git_root(directory)
repo = pygit2.Repository(directory) repo = pygit2.Repository(directory)
new_widgets.append(core.widget.Widget(name='git.main', full_text=repo.head.shorthand)) new_widgets.append(
core.widget.Widget(name="git.main", full_text=repo.head.shorthand)
)
for filepath, flags in repo.status().items(): for filepath, flags in repo.status().items():
if flags == pygit2.GIT_STATUS_WT_NEW or \ if (
flags == pygit2.GIT_STATUS_INDEX_NEW: flags == pygit2.GIT_STATUS_WT_NEW
state['new'] = True or flags == pygit2.GIT_STATUS_INDEX_NEW
if flags == pygit2.GIT_STATUS_WT_DELETED or \ ):
flags == pygit2.GIT_STATUS_INDEX_DELETED: state["new"] = True
state['deleted'] = True if (
if flags == pygit2.GIT_STATUS_WT_MODIFIED or \ flags == pygit2.GIT_STATUS_WT_DELETED
flags == pygit2.GIT_STATUS_INDEX_MODIFIED: or flags == pygit2.GIT_STATUS_INDEX_DELETED
state['modified'] = True ):
state["deleted"] = True
if (
flags == pygit2.GIT_STATUS_WT_MODIFIED
or flags == pygit2.GIT_STATUS_INDEX_MODIFIED
):
state["modified"] = True
self.__error = False self.__error = False
if 'new' in state: if "new" in state:
new_widgets.append(core.widget.Widget(name='git.new')) new_widgets.append(core.widget.Widget(name="git.new"))
if 'modified' in state: if "modified" in state:
new_widgets.append(core.widget.Widget(name='git.modified')) new_widgets.append(core.widget.Widget(name="git.modified"))
if 'deleted' in state: if "deleted" in state:
new_widgets.append(core.widget.Widget(name='git.deleted')) new_widgets.append(core.widget.Widget(name="git.deleted"))
self.widgets().clear() self.widgets().clear()
self.widget(new_widgets) self.widget(new_widgets)
@ -60,7 +69,7 @@ class Module(core.module.Module):
self.__error = True self.__error = True
def state(self, widget): def state(self, widget):
return widget.name.split('.')[1] return widget.name.split(".")[1]
def __get_git_root(self, directory): def __get_git_root(self, directory):
while len(directory) > 1: while len(directory) > 1:
@ -69,4 +78,5 @@ class Module(core.module.Module):
directory = "/".join(directory.split("/")[0:-1]) directory = "/".join(directory.split("/")[0:-1])
return "/" return "/"
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -8,6 +8,7 @@ import core.module
import core.widget import core.widget
import core.decorators import core.decorators
class Module(core.module.Module): class Module(core.module.Module):
@core.decorators.every(minutes=60) @core.decorators.every(minutes=60)
def __init__(self, config, theme): def __init__(self, config, theme):
@ -16,4 +17,5 @@ class Module(core.module.Module):
def full_text(self, widgets): def full_text(self, widgets):
return platform.release() return platform.release()
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -15,6 +15,7 @@ Parameters:
from xkbgroup import * from xkbgroup import *
import logging import logging
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
import core.module import core.module
@ -23,15 +24,14 @@ import core.input
import util.format import util.format
class Module(core.module.Module): class Module(core.module.Module):
def __init__(self, config, theme): def __init__(self, config, theme):
super().__init__(config, theme, core.widget.Widget(self.current_layout)) super().__init__(config, theme, core.widget.Widget(self.current_layout))
core.input.register(self, button=core.input.LEFT_MOUSE, core.input.register(self, button=core.input.LEFT_MOUSE, cmd=self.__next_keymap)
cmd=self.__next_keymap) core.input.register(self, button=core.input.RIGHT_MOUSE, cmd=self.__prev_keymap)
core.input.register(self, button=core.input.RIGHT_MOUSE, self.__show_variant = util.format.asbool(self.parameter("show_variant", True))
cmd=self.__prev_keymap)
self.__show_variant = util.format.asbool(self.parameter('show_variant', True))
def __next_keymap(self, event): def __next_keymap(self, event):
self.__set_keymap(1) self.__set_keymap(1)
@ -41,7 +41,8 @@ class Module(core.module.Module):
def __set_keymap(self, rotation): def __set_keymap(self, rotation):
xkb = XKeyboard() xkb = XKeyboard()
if xkb.groups_count < 2: return # nothing to do if xkb.groups_count < 2:
return # nothing to do
layouts = xkb.groups_symbols layouts = xkb.groups_symbols
idx = layouts.index(xkb.group_symbol) idx = layouts.index(xkb.group_symbol)
xkb.group_symbol = str(layouts[(idx + rotation) % len(layouts)]) xkb.group_symbol = str(layouts[(idx + rotation) % len(layouts)])
@ -49,13 +50,22 @@ class Module(core.module.Module):
def current_layout(self, widget): def current_layout(self, widget):
try: try:
xkb = XKeyboard() xkb = XKeyboard()
log.debug('group num: {}'.format(xkb.group_num)) log.debug("group num: {}".format(xkb.group_num))
name = xkb.group_name if util.format.asbool(self.parameter('showname'), False) else xkb.group_symbol name = (
xkb.group_name
if util.format.asbool(self.parameter("showname"), False)
else xkb.group_symbol
)
if self.__show_variant: if self.__show_variant:
return '{} ({})'.format(name, xkb.group_variant) if xkb.group_variant else name return (
"{} ({})".format(name, xkb.group_variant)
if xkb.group_variant
else name
)
return name return name
except Exception as e: except Exception as e:
print('got exception: {}'.format(e)) print("got exception: {}".format(e))
return 'n/a' return "n/a"
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -1,11 +1,11 @@
# pylint: disable=C0111,R0903 # pylint: disable=C0111,R0903
'''Displays system load. """Displays system load.
Parameters: Parameters:
* load.warning : Warning threshold for the one-minute load average (defaults to 70% of the number of CPUs) * load.warning : Warning threshold for the one-minute load average (defaults to 70% of the number of CPUs)
* load.critical: Critical threshold for the one-minute load average (defaults to 80% of the number of CPUs) * load.critical: Critical threshold for the one-minute load average (defaults to 80% of the number of CPUs)
''' """
import os import os
import multiprocessing import multiprocessing
@ -13,6 +13,7 @@ import multiprocessing
import core.module import core.module
import core.input import core.input
class Module(core.module.Module): class Module(core.module.Module):
def __init__(self, config, theme): def __init__(self, config, theme):
super().__init__(config, theme, core.widget.Widget(self.load)) super().__init__(config, theme, core.widget.Widget(self.load))
@ -21,11 +22,12 @@ class Module(core.module.Module):
self._cpus = multiprocessing.cpu_count() self._cpus = multiprocessing.cpu_count()
except NotImplementedError as e: except NotImplementedError as e:
self._cpus = 1 self._cpus = 1
core.input.register(self, button=core.input.LEFT_MOUSE, core.input.register(
cmd='gnome-system-monitor') self, button=core.input.LEFT_MOUSE, cmd="gnome-system-monitor"
)
def load(self, widget): def load(self, widget):
return '{:.02f}/{:.02f}/{:.02f}'.format( return "{:.02f}/{:.02f}/{:.02f}".format(
self._load[0], self._load[1], self._load[2] self._load[0], self._load[1], self._load[2]
) )
@ -35,4 +37,5 @@ class Module(core.module.Module):
def state(self, widget): def state(self, widget):
return self.threshold_state(self._load[0], self._cpus * 0.7, self._cpus * 0.8) return self.threshold_state(self._load[0], self._cpus * 0.7, self._cpus * 0.8)
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -1,13 +1,13 @@
# pylint: disable=C0111,R0903 # pylint: disable=C0111,R0903
'''Displays available RAM, total amount of RAM and percentage available. """Displays available RAM, total amount of RAM and percentage available.
Parameters: Parameters:
* memory.warning : Warning threshold in % of memory used (defaults to 80%) * memory.warning : Warning threshold in % of memory used (defaults to 80%)
* memory.critical: Critical threshold in % of memory used (defaults to 90%) * memory.critical: Critical threshold in % of memory used (defaults to 90%)
* memory.format: Format string (defaults to '{used}/{total} ({percent:05.02f}%)') * memory.format: Format string (defaults to '{used}/{total} ({percent:05.02f}%)')
* memory.usedonly: Only show the amount of RAM in use (defaults to False). Same as memory.format='{used}' * memory.usedonly: Only show the amount of RAM in use (defaults to False). Same as memory.format='{used}'
''' """
import re import re
@ -17,49 +17,61 @@ import core.input
import util.format import util.format
class Module(core.module.Module): class Module(core.module.Module):
def __init__(self, config, theme): def __init__(self, config, theme):
super().__init__(config, theme, core.widget.Widget(self.memory_usage)) super().__init__(config, theme, core.widget.Widget(self.memory_usage))
core.input.register(self, button=core.input.LEFT_MOUSE, core.input.register(
cmd='gnome-system-monitor') self, button=core.input.LEFT_MOUSE, cmd="gnome-system-monitor"
)
@property @property
def _format(self): def _format(self):
if util.format.asbool(self.parameter('usedonly', False)): if util.format.asbool(self.parameter("usedonly", False)):
return '{used}' return "{used}"
else: else:
return self.parameter('format', '{used}/{total} ({percent:05.02f}%)') return self.parameter("format", "{used}/{total} ({percent:05.02f}%)")
def memory_usage(self, widget): def memory_usage(self, widget):
return self._format.format(**self._mem) return self._format.format(**self._mem)
def update(self): def update(self):
data = {} data = {}
with open('/proc/meminfo', 'r') as f: with open("/proc/meminfo", "r") as f:
for line in f: for line in f:
tmp = re.split(r'[:\s]+', line) tmp = re.split(r"[:\s]+", line)
value = int(tmp[1]) value = int(tmp[1])
if tmp[2] == 'kB': value = value*1024 if tmp[2] == "kB":
if tmp[2] == 'mB': value = value*1024*1024 value = value * 1024
if tmp[2] == 'gB': value = value*1024*1024*1024 if tmp[2] == "mB":
value = value * 1024 * 1024
if tmp[2] == "gB":
value = value * 1024 * 1024 * 1024
data[tmp[0]] = value data[tmp[0]] = value
if 'MemAvailable' in data: if "MemAvailable" in data:
used = data['MemTotal'] - data['MemAvailable'] used = data["MemTotal"] - data["MemAvailable"]
else: else:
used = data['MemTotal'] - data['MemFree'] - data['Buffers'] - data['Cached'] - data['Slab'] used = (
data["MemTotal"]
- data["MemFree"]
- data["Buffers"]
- data["Cached"]
- data["Slab"]
)
self._mem = { self._mem = {
'total': util.format.byte(data['MemTotal']), "total": util.format.byte(data["MemTotal"]),
'available': util.format.byte(data['MemAvailable']), "available": util.format.byte(data["MemAvailable"]),
'free': util.format.byte(data['MemFree']), "free": util.format.byte(data["MemFree"]),
'used': util.format.byte(used), "used": util.format.byte(used),
'percent': float(used)/float(data['MemTotal'])*100.0 "percent": float(used) / float(data["MemTotal"]) * 100.0,
} }
def state(self, widget): def state(self, widget):
if self._mem['percent'] > float(self.parameter('critical', 90)): if self._mem["percent"] > float(self.parameter("critical", 90)):
return 'critical' return "critical"
if self._mem['percent'] > float(self.parameter('warning', 80)): if self._mem["percent"] > float(self.parameter("warning", 80)):
return 'warning' return "warning"
return None return None
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -22,22 +22,30 @@ import core.decorators
import util.cli import util.cli
import util.format import util.format
class Module(core.module.Module): class Module(core.module.Module):
@core.decorators.every(seconds=10) @core.decorators.every(seconds=10)
def __init__(self, config, theme): def __init__(self, config, theme):
widgets = [] widgets = []
super().__init__(config, theme, widgets) super().__init__(config, theme, widgets)
self._exclude = tuple(filter(len, self.parameter('exclude', 'lo,virbr,docker,vboxnet,veth,br').split(','))) self._exclude = tuple(
self._include = self.parameter('include', '').split(',') filter(
len,
self.parameter("exclude", "lo,virbr,docker,vboxnet,veth,br").split(","),
)
)
self._include = self.parameter("include", "").split(",")
self._states = { 'include': [], 'exclude': [] } self._states = {"include": [], "exclude": []}
for state in tuple(filter(len, util.format.aslist(self.parameter('states', '')))): for state in tuple(
if state[0] == '^': filter(len, util.format.aslist(self.parameter("states", "")))
self._states['exclude'].append(state[1:]) ):
if state[0] == "^":
self._states["exclude"].append(state[1:])
else: else:
self._states['include'].append(state) self._states["include"].append(state)
self._format = self.parameter('format','{intf} {state} {ip} {ssid}'); self._format = self.parameter("format", "{intf} {state} {ip} {ssid}")
self.iwgetid = shutil.which('iwgetid') self.iwgetid = shutil.which("iwgetid")
self._update_widgets(widgets) self._update_widgets(widgets)
def update(self): def update(self):
@ -46,71 +54,91 @@ class Module(core.module.Module):
def state(self, widget): def state(self, widget):
states = [] states = []
if widget.get('state') == 'down': if widget.get("state") == "down":
states.append('critical') states.append("critical")
elif widget.get('state') != 'up': elif widget.get("state") != "up":
states.append('warning') states.append("warning")
intf = widget.get('intf') intf = widget.get("intf")
iftype = 'wireless' if self._iswlan(intf) else 'wired' iftype = "wireless" if self._iswlan(intf) else "wired"
iftype = 'tunnel' if self._istunnel(intf) else iftype iftype = "tunnel" if self._istunnel(intf) else iftype
states.append('{}-{}'.format(iftype, widget.get('state'))) states.append("{}-{}".format(iftype, widget.get("state")))
return states return states
def _iswlan(self, intf): def _iswlan(self, intf):
# wifi, wlan, wlp, seems to work for me # wifi, wlan, wlp, seems to work for me
if intf.startswith('w'): return True if intf.startswith("w"):
return True
return False return False
def _istunnel(self, intf): def _istunnel(self, intf):
return intf.startswith('tun') or intf.startswith('wg') return intf.startswith("tun") or intf.startswith("wg")
def get_addresses(self, intf): def get_addresses(self, intf):
retval = [] retval = []
try: try:
for ip in netifaces.ifaddresses(intf).get(netifaces.AF_INET, []): for ip in netifaces.ifaddresses(intf).get(netifaces.AF_INET, []):
if ip.get('addr', '') != '': if ip.get("addr", "") != "":
retval.append(ip.get('addr')) retval.append(ip.get("addr"))
except Exception: except Exception:
return [] return []
return retval return retval
def _update_widgets(self, widgets): def _update_widgets(self, widgets):
interfaces = [i for i in netifaces.interfaces() if not i.startswith(self._exclude)] interfaces = [
i for i in netifaces.interfaces() if not i.startswith(self._exclude)
]
interfaces.extend([i for i in netifaces.interfaces() if i in self._include]) interfaces.extend([i for i in netifaces.interfaces() if i in self._include])
for widget in widgets: for widget in widgets:
widget.set('visited', False) widget.set("visited", False)
for intf in interfaces: for intf in interfaces:
addr = [] addr = []
state = 'down' state = "down"
for ip in self.get_addresses(intf): for ip in self.get_addresses(intf):
addr.append(ip) addr.append(ip)
state = 'up' state = "up"
if len(self._states['exclude']) > 0 and state in self._states['exclude']: continue if len(self._states["exclude"]) > 0 and state in self._states["exclude"]:
if len(self._states['include']) > 0 and state not in self._states['include']: continue continue
if (
len(self._states["include"]) > 0
and state not in self._states["include"]
):
continue
widget = self.widget(intf) widget = self.widget(intf)
if not widget: if not widget:
widget = core.widget.Widget(name=intf, module=self) widget = core.widget.Widget(name=intf, module=self)
widgets.append(widget) widgets.append(widget)
# join/split is used to get rid of multiple whitespaces (in case SSID is not available, for instance # join/split is used to get rid of multiple whitespaces (in case SSID is not available, for instance
widget.full_text(' '.join(self._format.format(ip=', '.join(addr),intf=intf,state=state,ssid=self.get_ssid(intf)).split())) widget.full_text(
widget.set('intf', intf) " ".join(
widget.set('state', state) self._format.format(
widget.set('visited', True) ip=", ".join(addr),
intf=intf,
state=state,
ssid=self.get_ssid(intf),
).split()
)
)
widget.set("intf", intf)
widget.set("state", state)
widget.set("visited", True)
for widget in widgets: for widget in widgets:
if widget.get('visited') is False: if widget.get("visited") is False:
widgets.remove(widget) widgets.remove(widget)
def get_ssid(self, intf): def get_ssid(self, intf):
if self._iswlan(intf) and self.iwgetid: if self._iswlan(intf) and self.iwgetid:
return util.cli.execute('{} -r {}'.format(self.iwgetid, intf), ignore_errors=True) return util.cli.execute(
return '' "{} -r {}".format(self.iwgetid, intf), ignore_errors=True
)
return ""
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -1,7 +1,9 @@
from .__pulseaudio import Module from .__pulseaudio import Module
class Module(Module): class Module(Module):
def __init__(self, config, theme): def __init__(self, config, theme):
super().__init__(config, theme, 'sink') super().__init__(config, theme, "sink")
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -1,7 +1,9 @@
from .__pulseaudio import Module from .__pulseaudio import Module
class Module(Module): class Module(Module):
def __init__(self, config, theme): def __init__(self, config, theme):
super().__init__(config, theme, 'source') super().__init__(config, theme, "source")
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -24,30 +24,39 @@ import core.decorators
import util.cli import util.cli
def get_rtt(module, widget): def get_rtt(module, widget):
try: try:
widget.set('rtt-unreachable', False) widget.set("rtt-unreachable", False)
res = util.cli.execute('ping -n -q -c {} -W {} {}'.format( res = util.cli.execute(
widget.get('rtt-probes'), widget.get('rtt-timeout'), widget.get('address') "ping -n -q -c {} -W {} {}".format(
)) widget.get("rtt-probes"),
widget.get("rtt-timeout"),
widget.get("address"),
)
)
for line in res.split('\n'): for line in res.split("\n"):
if line.startswith('{} packets transmitted'.format(widget.get('rtt-probes'))): if line.startswith(
m = re.search(r'(\d+)% packet loss', line) "{} packets transmitted".format(widget.get("rtt-probes"))
):
m = re.search(r"(\d+)% packet loss", line)
widget.set('packet-loss', m.group(1)) widget.set("packet-loss", m.group(1))
if not line.startswith('rtt'): continue if not line.startswith("rtt"):
m = re.search(r'([0-9\.]+)/([0-9\.]+)/([0-9\.]+)/([0-9\.]+)\s+(\S+)', line) continue
m = re.search(r"([0-9\.]+)/([0-9\.]+)/([0-9\.]+)/([0-9\.]+)\s+(\S+)", line)
widget.set('rtt-min', float(m.group(1))) widget.set("rtt-min", float(m.group(1)))
widget.set('rtt-avg', float(m.group(2))) widget.set("rtt-avg", float(m.group(2)))
widget.set('rtt-max', float(m.group(3))) widget.set("rtt-max", float(m.group(3)))
widget.set('rtt-unit', m.group(5)) widget.set("rtt-unit", m.group(5))
except Exception as e: except Exception as e:
widget.set('rtt-unreachable', True) widget.set("rtt-unreachable", True)
core.event.trigger("update", [module.id], redraw_only=True)
core.event.trigger('update', [ module.id ], redraw_only=True)
class Module(core.module.Module): class Module(core.module.Module):
@core.decorators.every(seconds=60) @core.decorators.every(seconds=60)
@ -56,29 +65,31 @@ class Module(core.module.Module):
widget = self.widget() widget = self.widget()
widget.set('address', self.parameter('address', '8.8.8.8')) widget.set("address", self.parameter("address", "8.8.8.8"))
widget.set('rtt-probes', self.parameter('probes', 5)) widget.set("rtt-probes", self.parameter("probes", 5))
widget.set('rtt-timeout', self.parameter('timeout', 5.0)) widget.set("rtt-timeout", self.parameter("timeout", 5.0))
widget.set('rtt-avg', 0.0) widget.set("rtt-avg", 0.0)
widget.set('rtt-unit', '') widget.set("rtt-unit", "")
widget.set('packet-loss', 0) widget.set("packet-loss", 0)
def rtt(self, widget): def rtt(self, widget):
if widget.get('rtt-unreachable'): if widget.get("rtt-unreachable"):
return '{}: unreachable'.format(widget.get('address')) return "{}: unreachable".format(widget.get("address"))
return '{}: {:.1f}{} ({}%)'.format( return "{}: {:.1f}{} ({}%)".format(
widget.get('address'), widget.get("address"),
widget.get('rtt-avg'), widget.get("rtt-avg"),
widget.get('rtt-unit'), widget.get("rtt-unit"),
widget.get('packet-loss') widget.get("packet-loss"),
) )
def state(self, widget): def state(self, widget):
if widget.get('rtt-unreachable'): return ['critical'] if widget.get("rtt-unreachable"):
return self.threshold_state(widget.get('rtt-avg'), 1000.0, 2000.0) return ["critical"]
return self.threshold_state(widget.get("rtt-avg"), 1000.0, 2000.0)
def update(self): def update(self):
thread = threading.Thread(target=get_rtt, args=(self, self.widget(),)) thread = threading.Thread(target=get_rtt, args=(self, self.widget(),))
thread.start() thread.start()
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -24,45 +24,49 @@ import core.decorators
import util.cli import util.cli
import util.location import util.location
def get_redshift_value(module): def get_redshift_value(module):
widget = module.widget() widget = module.widget()
location = module.parameter('location', 'auto') location = module.parameter("location", "auto")
lat = module.parameter('lat', None) lat = module.parameter("lat", None)
lon = module.parameter('lon', None) lon = module.parameter("lon", None)
# Even if location method is set to manual, if we have no lat or lon, # Even if location method is set to manual, if we have no lat or lon,
# fall back to the geoclue2 method. # fall back to the geoclue2 method.
if location == 'manual' and (lat is None or lon is None): if location == "manual" and (lat is None or lon is None):
location = 'geoclue2' location = "geoclue2"
command = ['redshift', '-p'] command = ["redshift", "-p"]
if location == 'manual': if location == "manual":
command.extend(['-l', '{}:{}'.format(lat, lon)]) command.extend(["-l", "{}:{}".format(lat, lon)])
if location == 'geoclue2': if location == "geoclue2":
command.extend(['-l', 'geoclue2']) command.extend(["-l", "geoclue2"])
try: try:
res = util.cli.execute(' '.join(command)) res = util.cli.execute(" ".join(command))
except Exception: except Exception:
res = '' res = ""
widget.set('temp', 'n/a') widget.set("temp", "n/a")
widget.set('transition', '') widget.set("transition", "")
widget.set('state', 'day') widget.set("state", "day")
for line in res.split('\n'): for line in res.split("\n"):
line = line.lower() line = line.lower()
if 'temperature' in line: if "temperature" in line:
widget.set('temp', line.split(' ')[2]) widget.set("temp", line.split(" ")[2])
if 'period' in line: if "period" in line:
state = line.split(' ')[1] state = line.split(" ")[1]
if 'day' in state: if "day" in state:
widget.set('state', 'day') widget.set("state", "day")
elif 'night' in state: elif "night" in state:
widget.set('state', 'night') widget.set("state", "night")
else: else:
widget.set('state', 'transition') widget.set("state", "transition")
match = re.search(r'(\d+)\.\d+% ([a-z]+)', line) match = re.search(r"(\d+)\.\d+% ([a-z]+)", line)
widget.set('transition', '({}% {})'.format(match.group(1), match.group(2))) widget.set(
core.event.trigger('update', [ widget.module.id ], redraw_only=True) "transition", "({}% {})".format(match.group(1), match.group(2))
)
core.event.trigger("update", [widget.module.id], redraw_only=True)
class Module(core.module.Module): class Module(core.module.Module):
@core.decorators.every(seconds=10) @core.decorators.every(seconds=10)
@ -71,24 +75,24 @@ class Module(core.module.Module):
self.__thread = None self.__thread = None
if self.parameter('location', '') == 'ipinfo': if self.parameter("location", "") == "ipinfo":
# override lon/lat with ipinfo # override lon/lat with ipinfo
try: try:
location = util.location.coordinates() location = util.location.coordinates()
self.set('lat', location[0]) self.set("lat", location[0])
self.set('lon', location[1]) self.set("lon", location[1])
self.set('location', 'manual') self.set("location", "manual")
except Exception: except Exception:
# Fall back to geoclue2. # Fall back to geoclue2.
self.set('location', 'geoclue2') self.set("location", "geoclue2")
self._text = '' self._text = ""
def text(self, widget): def text(self, widget):
val = widget.get('temp', 'n/a') val = widget.get("temp", "n/a")
transition = widget.get('transition', '') transition = widget.get("transition", "")
if transition: if transition:
val = '{} {}'.format(val, transition) val = "{} {}".format(val, transition)
return val return val
def update(self): def update(self):
@ -98,6 +102,7 @@ class Module(core.module.Module):
self.__thread.start() self.__thread.start()
def state(self, widget): def state(self, widget):
return widget.get('state', None) return widget.get("state", None)
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -26,11 +26,12 @@ import core.widget
import util.cli import util.cli
import util.format import util.format
class Module(core.module.Module): class Module(core.module.Module):
def __init__(self, config, theme): def __init__(self, config, theme):
super().__init__(config, theme, []) super().__init__(config, theme, [])
self.__chip = self.parameter('chip', '') self.__chip = self.parameter("chip", "")
self.__data = {} self.__data = {}
self.__update() self.__update()
@ -42,31 +43,45 @@ class Module(core.module.Module):
self.__update_widget(widget) self.__update_widget(widget)
def state(self, widget): def state(self, widget):
widget_type = widget.get('type', '') widget_type = widget.get("type", "")
try: try:
data = self.__data[widget.get('adapter')][widget.get('package')][widget.get('field')] data = self.__data[widget.get("adapter")][widget.get("package")][
if 'crit' in data and float(data['input']) > float(data['crit']): widget.get("field")
return ['critical', widget_type] ]
if 'max' in data and float(data['input']) > float(data['max']): if "crit" in data and float(data["input"]) > float(data["crit"]):
return ['warning', widget_type] return ["critical", widget_type]
if "max" in data and float(data["input"]) > float(data["max"]):
return ["warning", widget_type]
except: except:
pass pass
return [widget_type] return [widget_type]
def __create_widgets(self): def __create_widgets(self):
show_temp = util.format.asbool(self.parameter('showtemp', True)) show_temp = util.format.asbool(self.parameter("showtemp", True))
show_fan = util.format.asbool(self.parameter('showfan', True)) show_fan = util.format.asbool(self.parameter("showfan", True))
show_other = util.format.asbool(self.parameter('showother', False)) show_other = util.format.asbool(self.parameter("showother", False))
include_chip = tuple(filter(len, util.format.aslist(self.parameter('chip_include', '')))) include_chip = tuple(
exclude_chip = tuple(filter(len, util.format.aslist(self.parameter('chip_exclude', '')))) filter(len, util.format.aslist(self.parameter("chip_include", "")))
include_field = tuple(filter(len, util.format.aslist(self.parameter('field_include', '')))) )
exclude_field = tuple(filter(len, util.format.aslist(self.parameter('field_exclude', '')))) exclude_chip = tuple(
include_chip_field = tuple(filter(len, util.format.aslist(self.parameter('chip_field_include', '')))) filter(len, util.format.aslist(self.parameter("chip_exclude", "")))
exclude_chip_field = tuple(filter(len, util.format.aslist(self.parameter('chip_field_exclude', '')))) )
include_field = tuple(
filter(len, util.format.aslist(self.parameter("field_include", "")))
)
exclude_field = tuple(
filter(len, util.format.aslist(self.parameter("field_exclude", "")))
)
include_chip_field = tuple(
filter(len, util.format.aslist(self.parameter("chip_field_include", "")))
)
exclude_chip_field = tuple(
filter(len, util.format.aslist(self.parameter("chip_field_exclude", "")))
)
if util.format.asbool(self.parameter('showcpu', True)): if util.format.asbool(self.parameter("showcpu", True)):
widget = self.add_widget(full_text=self.__cpu) widget = self.add_widget(full_text=self.__cpu)
widget.set('type', 'cpu') widget.set("type", "cpu")
for adapter in self.__data: for adapter in self.__data:
if include_chip or exclude_chip: if include_chip or exclude_chip:
@ -79,23 +94,27 @@ class Module(core.module.Module):
if include_chip_field: if include_chip_field:
try: try:
if all([i.split('.')[0] not in adapter for i in include_chip_field]): if all(
[i.split(".")[0] not in adapter for i in include_chip_field]
):
continue continue
except: except:
pass pass
for package in self.__data[adapter]: for package in self.__data[adapter]:
if util.format.asbool(self.parameter('showname', False)): if util.format.asbool(self.parameter("showname", False)):
widget = self.add_widget(full_text=package) widget = self.add_widget(full_text=package)
widget.set('data', self.__data[adapter][package]) widget.set("data", self.__data[adapter][package])
widget.set('package', package) widget.set("package", package)
widget.set('field', '') widget.set("field", "")
widget.set('adapter', adapter) widget.set("adapter", adapter)
for field in self.__data[adapter][package]: for field in self.__data[adapter][package]:
if include_field or exclude_field: if include_field or exclude_field:
if include_field: if include_field:
if all([included not in field for included in include_field]): if all(
[included not in field for included in include_field]
):
continue continue
else: else:
if any([excluded in field for excluded in exclude_field]): if any([excluded in field for excluded in exclude_field]):
@ -104,62 +123,79 @@ class Module(core.module.Module):
try: try:
if include_chip_field or exclude_chip_field: if include_chip_field or exclude_chip_field:
if include_chip_field: if include_chip_field:
if all([i.split('.')[1] not in field for i in include_chip_field if i.split('.')[0] in adapter]): if all(
[
i.split(".")[1] not in field
for i in include_chip_field
if i.split(".")[0] in adapter
]
):
continue continue
else: else:
if any([i.split('.')[1] in field for i in exclude_chip_field if i.split('.')[0] in adapter]): if any(
[
i.split(".")[1] in field
for i in exclude_chip_field
if i.split(".")[0] in adapter
]
):
continue continue
except: except:
pass pass
widget = None widget = None
if 'temp' in field and show_temp: if "temp" in field and show_temp:
# seems to be a temperature # seems to be a temperature
widget = self.add_widget() widget = self.add_widget()
widget.set('type', 'temp') widget.set("type", "temp")
if 'fan' in field and show_fan: if "fan" in field and show_fan:
# seems to be a fan # seems to be a fan
widget = self.add_widget() widget = self.add_widget()
widget.set('type', 'fan') widget.set("type", "fan")
elif show_other: elif show_other:
# everything else # everything else
widget = self.add_widget() widget = self.add_widget()
widget.set('type', 'other') widget.set("type", "other")
if widget: if widget:
widget.set('package', package) widget.set("package", package)
widget.set('field', field) widget.set("field", field)
widget.set('adapter', adapter) widget.set("adapter", adapter)
def __update_widget(self, widget): def __update_widget(self, widget):
if widget.get('field', '') == '': if widget.get("field", "") == "":
return # nothing to do return # nothing to do
data = self.__data[widget.get('adapter')][widget.get('package')][widget.get('field')] data = self.__data[widget.get("adapter")][widget.get("package")][
if 'temp' in widget.get('field'): widget.get("field")
widget.full_text(u'{:0.01f}°C'.format(data['input'])) ]
elif 'fan' in widget.get('field'): if "temp" in widget.get("field"):
widget.full_text(u'{:0.0f}RPM'.format(data['input'])) widget.full_text("{:0.01f}°C".format(data["input"]))
elif "fan" in widget.get("field"):
widget.full_text("{:0.0f}RPM".format(data["input"]))
else: else:
widget.full_text(u'{:0.0f}'.format(data['input'])) widget.full_text("{:0.0f}".format(data["input"]))
def __update(self): def __update(self):
output = util.cli.execute('sensors -u {}'.format(self.__chip), ignore_errors=True) output = util.cli.execute(
"sensors -u {}".format(self.__chip), ignore_errors=True
)
self.__data = self.__parse(output) self.__data = self.__parse(output)
def __parse(self, data): def __parse(self, data):
output = {} output = {}
package = '' package = ""
adapter = None adapter = None
chip = None chip = None
for line in data.split('\n'): for line in data.split("\n"):
if 'Adapter' in line: if "Adapter" in line:
# new adapter # new adapter
line = line.replace('Adapter: ', '') line = line.replace("Adapter: ", "")
output[chip + ' ' + line] = {} output[chip + " " + line] = {}
adapter = chip + ' ' + line adapter = chip + " " + line
chip = line # default - line before adapter is always the chip chip = line # default - line before adapter is always the chip
if not adapter: continue if not adapter:
key, value = (line.split(':') + ['', ''])[:2] continue
if not line.startswith(' '): key, value = (line.split(":") + ["", ""])[:2]
if not line.startswith(" "):
# assume this starts a new package # assume this starts a new package
if package in output[adapter] and output[adapter][package] == {}: if package in output[adapter] and output[adapter][package] == {}:
del output[adapter][package] del output[adapter][package]
@ -168,7 +204,7 @@ class Module(core.module.Module):
else: else:
# feature for this chip # feature for this chip
try: try:
name, variant = (key.strip().split('_', 1) + ['',''])[:2] name, variant = (key.strip().split("_", 1) + ["", ""])[:2]
if not name in output[adapter][package]: if not name in output[adapter][package]:
output[adapter][package][name] = {} output[adapter][package][name] = {}
if variant: if variant:
@ -181,23 +217,26 @@ class Module(core.module.Module):
def __cpu(self, _): def __cpu(self, _):
mhz = None mhz = None
try: try:
output = open('/sys/devices/system/cpu/cpufreq/policy0/scaling_cur_freq').read() output = open(
"/sys/devices/system/cpu/cpufreq/policy0/scaling_cur_freq"
).read()
mhz = int(float(output) / 1000.0) mhz = int(float(output) / 1000.0)
except: except:
output = open('/proc/cpuinfo').read() output = open("/proc/cpuinfo").read()
m = re.search(r'cpu MHz\s+:\s+(\d+)', output) m = re.search(r"cpu MHz\s+:\s+(\d+)", output)
if m: if m:
mhz = int(m.group(1)) mhz = int(m.group(1))
else: else:
m = re.search(r'BogoMIPS\s+:\s+(\d+)', output) m = re.search(r"BogoMIPS\s+:\s+(\d+)", output)
if m: if m:
return '{} BogoMIPS'.format(int(m.group(1))) return "{} BogoMIPS".format(int(m.group(1)))
if not mhz: if not mhz:
return 'n/a' return "n/a"
if mhz < 1000: if mhz < 1000:
return '{} MHz'.format(mhz) return "{} MHz".format(mhz)
else: else:
return '{:0.01f} GHz'.format(float(mhz)/1000.0) return "{:0.01f} GHz".format(float(mhz) / 1000.0)
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -10,6 +10,7 @@ import core.module
import core.widget import core.widget
import core.decorators import core.decorators
class Module(core.module.Module): class Module(core.module.Module):
@core.decorators.every(minutes=60) @core.decorators.every(minutes=60)
def __init__(self, config, theme): def __init__(self, config, theme):
@ -19,4 +20,5 @@ class Module(core.module.Module):
def text(self, _): def text(self, _):
return self.__text return self.__text
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -6,8 +6,10 @@
import core.widget import core.widget
import core.module import core.module
class Module(core.module.Module): class Module(core.module.Module):
def __init__(self, config, theme): def __init__(self, config, theme):
super().__init__(config=config, theme=theme, widgets=core.widget.Widget('test')) super().__init__(config=config, theme=theme, widgets=core.widget.Widget("test"))
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -10,12 +10,14 @@ Parameters:
import core.decorators import core.decorators
from .datetime import Module from .datetime import Module
class Module(Module): class Module(Module):
@core.decorators.every(seconds=59) # ensures one update per minute @core.decorators.every(seconds=59) # ensures one update per minute
def __init__(self, config, theme): def __init__(self, config, theme):
super().__init__(config, theme) super().__init__(config, theme)
def default_format(self): def default_format(self):
return '%X' return "%X"
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -29,30 +29,39 @@ import core.event
import util.cli import util.cli
import util.popup import util.popup
def build_menu(parent, current_directory, callback): def build_menu(parent, current_directory, callback):
with os.scandir(current_directory) as it: with os.scandir(current_directory) as it:
for entry in it: for entry in it:
if entry.name.startswith('.'): continue if entry.name.startswith("."):
continue
if entry.is_file(): if entry.is_file():
name = entry.name[:entry.name.rfind('.')] name = entry.name[: entry.name.rfind(".")]
parent.add_menuitem(name, callback=lambda : callback(os.path.join(current_directory, name))) parent.add_menuitem(
name,
callback=lambda: callback(os.path.join(current_directory, name)),
)
else: else:
submenu = util.popup.menu(parent, leave=False) submenu = util.popup.menu(parent, leave=False)
build_menu(submenu, os.path.join(current_directory, entry.name), callback) build_menu(
submenu, os.path.join(current_directory, entry.name), callback
)
parent.add_cascade(entry.name, submenu) parent.add_cascade(entry.name, submenu)
class Module(core.module.Module): class Module(core.module.Module):
def __init__(self, config, theme): def __init__(self, config, theme):
super().__init__(config, theme, core.widget.Widget(self.text)) super().__init__(config, theme, core.widget.Widget(self.text))
self.__duration = int(self.parameter('duration', 30)) self.__duration = int(self.parameter("duration", 30))
self.__offx = int(self.parameter('offx', 0)) self.__offx = int(self.parameter("offx", 0))
self.__offy = int(self.parameter('offy', 0)) self.__offy = int(self.parameter("offy", 0))
self.__path = os.path.expanduser(self.parameter('location', '~/.password-store/')) self.__path = os.path.expanduser(
self.parameter("location", "~/.password-store/")
)
self.__reset() self.__reset()
core.input.register(self, button=core.input.LEFT_MOUSE, core.input.register(self, button=core.input.LEFT_MOUSE, cmd=self.popup)
cmd=self.popup)
def popup(self, widget): def popup(self, widget):
menu = util.popup.menu(leave=False) menu = util.popup.menu(leave=False)
@ -62,14 +71,17 @@ class Module(core.module.Module):
def __reset(self): def __reset(self):
self.__timer = None self.__timer = None
self.__text = str(self.parameter('text', '<click-for-password>')) self.__text = str(self.parameter("text", "<click-for-password>"))
def __callback(self, secret_name): def __callback(self, secret_name):
secret_name = secret_name.replace(self.__path, '') # remove common path secret_name = secret_name.replace(self.__path, "") # remove common path
if self.__timer: if self.__timer:
self.__timer.cancel() self.__timer.cancel()
res = util.cli.execute('pass -c {}'.format(secret_name), wait=False, res = util.cli.execute(
env={ 'PASSWORD_STORE_CLIP_TIME': self.__duration }) "pass -c {}".format(secret_name),
wait=False,
env={"PASSWORD_STORE_CLIP_TIME": self.__duration},
)
self.__timer = threading.Timer(self.__duration, self.__reset) self.__timer = threading.Timer(self.__duration, self.__reset)
self.__timer.start() self.__timer.start()
self.__start = int(time.time()) self.__start = int(time.time())
@ -77,7 +89,10 @@ class Module(core.module.Module):
def text(self, widget): def text(self, widget):
if self.__timer: if self.__timer:
return '{} ({}s)'.format(self.__text, self.__duration - (int(time.time()) - self.__start)) return "{} ({}s)".format(
self.__text, self.__duration - (int(time.time()) - self.__start)
)
return self.__text return self.__text
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -1,6 +1,6 @@
# pylint: disable=C0111,R0903 # pylint: disable=C0111,R0903
'''Shows a widget for each connected screen and allows the user to enable/disable screens. """Shows a widget for each connected screen and allows the user to enable/disable screens.
Parameters: Parameters:
* xrandr.overwrite_i3config: If set to 'true', this module assembles a new i3 config * xrandr.overwrite_i3config: If set to 'true', this module assembles a new i3 config
@ -14,7 +14,7 @@ Requires the following python module:
Requires the following executable: Requires the following executable:
* xrandr * xrandr
''' """
import os import os
import re import re
@ -33,17 +33,18 @@ try:
except: except:
pass pass
class Module(core.module.Module): class Module(core.module.Module):
@core.decorators.every(seconds=5) # takes up to 5s to detect a new screen @core.decorators.every(seconds=5) # takes up to 5s to detect a new screen
def __init__(self, config, theme): def __init__(self, config, theme):
widgets = [] widgets = []
super().__init__(config, theme, widgets) super().__init__(config, theme, widgets)
self._autoupdate = util.format.asbool(self.parameter('autoupdate', True)) self._autoupdate = util.format.asbool(self.parameter("autoupdate", True))
self._needs_update = True self._needs_update = True
try: try:
i3.Subscription(self._output_update, 'output') i3.Subscription(self._output_update, "output")
except: except:
pass pass
@ -58,31 +59,33 @@ class Module(core.module.Module):
self._needs_update = False self._needs_update = False
for line in util.cli.execute('xrandr -q').split('\n'): for line in util.cli.execute("xrandr -q").split("\n"):
if not ' connected' in line: if not " connected" in line:
continue continue
display = line.split(' ', 2)[0] display = line.split(" ", 2)[0]
m = re.search(r'\d+x\d+\+(\d+)\+\d+', line) m = re.search(r"\d+x\d+\+(\d+)\+\d+", line)
widget = self.widget(display) widget = self.widget(display)
if not widget: if not widget:
widget = core.widget.Widget(full_text=display, name=display, module=self) widget = core.widget.Widget(
full_text=display, name=display, module=self
)
core.input.register(widget, button=1, cmd=self._toggle) core.input.register(widget, button=1, cmd=self._toggle)
core.input.register(widget, button=3, cmd=self._toggle) core.input.register(widget, button=3, cmd=self._toggle)
new_widgets.append(widget) new_widgets.append(widget)
widget.set('state', 'on' if m else 'off') widget.set("state", "on" if m else "off")
widget.set('pos', int(m.group(1)) if m else sys.maxsize) widget.set("pos", int(m.group(1)) if m else sys.maxsize)
self.widgets(new_widgets) self.widgets(new_widgets)
if self._autoupdate == False: if self._autoupdate == False:
widget = core.widget.Widget(full_text='', module=self) widget = core.widget.Widget(full_text="", module=self)
widget.set('state', 'refresh') widget.set("state", "refresh")
core.input.register(widget, button=1, cmd=self._refresh) core.input.register(widget, button=1, cmd=self._refresh)
self.widgets().append(widget) self.widgets().append(widget)
def state(self, widget): def state(self, widget):
return widget.get('state', 'off') return widget.get("state", "off")
def _refresh(self, event): def _refresh(self, event):
self._needs_update = True self._needs_update = True
@ -91,26 +94,50 @@ class Module(core.module.Module):
self._refresh(self, event) self._refresh(self, event)
path = os.path.dirname(os.path.abspath(__file__)) path = os.path.dirname(os.path.abspath(__file__))
if util.format.asbool(self.parameter('overwrite_i3config', False)) == True: if util.format.asbool(self.parameter("overwrite_i3config", False)) == True:
toggle_cmd = '{}/../../bin/toggle-display.sh'.format(path) toggle_cmd = "{}/../../bin/toggle-display.sh".format(path)
else: else:
toggle_cmd = 'xrandr' toggle_cmd = "xrandr"
widget = self.widget_by_id(event['instance']) widget = self.widget_by_id(event["instance"])
if widget.get('state') == 'on': if widget.get("state") == "on":
util.cli.execute('{} --output {} --off'.format(toggle_cmd, widget.name)) util.cli.execute("{} --output {} --off".format(toggle_cmd, widget.name))
else: else:
first_neighbor = next((widget for widget in self.widgets() if widget.get('state') == 'on'), None) first_neighbor = next(
last_neighbor = next((widget for widget in reversed(self.widgets()) if widget.get('state') == 'on'), None) (widget for widget in self.widgets() if widget.get("state") == "on"),
None,
)
last_neighbor = next(
(
widget
for widget in reversed(self.widgets())
if widget.get("state") == "on"
),
None,
)
neighbor = first_neighbor if event['button'] == core.input.LEFT_MOUSE else last_neighbor neighbor = (
first_neighbor
if event["button"] == core.input.LEFT_MOUSE
else last_neighbor
)
if neighbor is None: if neighbor is None:
util.cli.execute('{} --output {} --auto'.format(toggle_cmd, widget.name)) util.cli.execute(
"{} --output {} --auto".format(toggle_cmd, widget.name)
)
else: else:
util.cli.execute('{} --output {} --auto --{}-of {}'.format(toggle_cmd, widget.name, util.cli.execute(
'left' if event.get('button') == core.input.LEFT_MOUSE else 'right', "{} --output {} --auto --{}-of {}".format(
neighbor.name)) toggle_cmd,
widget.name,
"left"
if event.get("button") == core.input.LEFT_MOUSE
else "right",
neighbor.name,
)
)
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -3,74 +3,83 @@ import unittest
import core.config import core.config
class config(unittest.TestCase): class config(unittest.TestCase):
def setUp(self): def setUp(self):
self.someModules = [ 'b', 'x', 'a' ] self.someModules = ["b", "x", "a"]
self.moreModules = [ 'this', 'module', 'here' ] self.moreModules = ["this", "module", "here"]
self.someTheme = 'some-theme' self.someTheme = "some-theme"
self.someIconset = 'some-iconset' self.someIconset = "some-iconset"
self.defaultConfig = core.config.Config([]) self.defaultConfig = core.config.Config([])
def test_module(self): def test_module(self):
cfg = core.config.Config([ '-m' ] + self.someModules) cfg = core.config.Config(["-m"] + self.someModules)
self.assertEqual(self.someModules, cfg.modules()) self.assertEqual(self.someModules, cfg.modules())
def test_module_ordering_maintained(self): def test_module_ordering_maintained(self):
cfg = core.config.Config([ '-m' ] + self.someModules + [ '-m' ] + self.moreModules) cfg = core.config.Config(["-m"] + self.someModules + ["-m"] + self.moreModules)
self.assertEqual(self.someModules + self.moreModules, cfg.modules()) self.assertEqual(self.someModules + self.moreModules, cfg.modules())
def test_default_interval(self): def test_default_interval(self):
self.assertEqual(1, self.defaultConfig.interval()) self.assertEqual(1, self.defaultConfig.interval())
def test_interval(self): def test_interval(self):
cfg = core.config.Config([ '-p', 'interval=4']) cfg = core.config.Config(["-p", "interval=4"])
self.assertEqual(4, cfg.interval()) self.assertEqual(4, cfg.interval())
def test_float_interval(self): def test_float_interval(self):
cfg = core.config.Config([ '-p', 'interval=0.5']) cfg = core.config.Config(["-p", "interval=0.5"])
self.assertEqual(0.5, cfg.interval()) self.assertEqual(0.5, cfg.interval())
def test_default_theme(self): def test_default_theme(self):
self.assertEqual('default', self.defaultConfig.theme()) self.assertEqual("default", self.defaultConfig.theme())
def test_theme(self): def test_theme(self):
cfg = core.config.Config(['-t', self.someTheme]) cfg = core.config.Config(["-t", self.someTheme])
self.assertEqual(self.someTheme, cfg.theme()) self.assertEqual(self.someTheme, cfg.theme())
def test_default_iconset(self): def test_default_iconset(self):
self.assertEqual('auto', self.defaultConfig.iconset()) self.assertEqual("auto", self.defaultConfig.iconset())
def test_iconset(self): def test_iconset(self):
cfg = core.config.Config(['-i', self.someIconset]) cfg = core.config.Config(["-i", self.someIconset])
self.assertEqual(self.someIconset, cfg.iconset()) self.assertEqual(self.someIconset, cfg.iconset())
def test_right_to_left(self): def test_right_to_left(self):
cfg = core.config.Config(['-r']) cfg = core.config.Config(["-r"])
self.assertTrue(cfg.reverse()) self.assertTrue(cfg.reverse())
self.assertFalse(self.defaultConfig.reverse()) self.assertFalse(self.defaultConfig.reverse())
def test_logfile(self): def test_logfile(self):
cfg = core.config.Config(['-f', 'my-custom-logfile']) cfg = core.config.Config(["-f", "my-custom-logfile"])
self.assertEquals(None, self.defaultConfig.logfile()) self.assertEquals(None, self.defaultConfig.logfile())
self.assertEquals('my-custom-logfile', cfg.logfile()) self.assertEquals("my-custom-logfile", cfg.logfile())
def test_all_modules(self): def test_all_modules(self):
modules = core.config.all_modules() modules = core.config.all_modules()
self.assertGreater(len(modules), 0) self.assertGreater(len(modules), 0)
for module in modules: for module in modules:
pyname = '{}.py'.format(module) pyname = "{}.py".format(module)
base = os.path.abspath(os.path.join(os.path.dirname(os.path.realpath(__file__)), '..', '..', 'modules')) base = os.path.abspath(
self.assertTrue(os.path.exists(os.path.join(base, 'contrib', pyname)) or os.path.exists(os.path.join(base, 'core', pyname))) os.path.join(
os.path.dirname(os.path.realpath(__file__)), "..", "..", "modules"
)
)
self.assertTrue(
os.path.exists(os.path.join(base, "contrib", pyname))
or os.path.exists(os.path.join(base, "core", pyname))
)
def test_list_output(self): def test_list_output(self):
with unittest.mock.patch('core.config.sys') as sys: with unittest.mock.patch("core.config.sys") as sys:
cfg = core.config.Config([ '-l', 'themes' ]) cfg = core.config.Config(["-l", "themes"])
cfg = core.config.Config([ '-l', 'modules' ]) cfg = core.config.Config(["-l", "modules"])
cfg = core.config.Config([ '-l', 'modules-markdown' ]) cfg = core.config.Config(["-l", "modules-markdown"])
# TODO: think of some plausibility testing here # TODO: think of some plausibility testing here
def test_missing_parameter(self): def test_missing_parameter(self):
cfg = core.config.Config([ '-p', 'test.key' ]) cfg = core.config.Config(["-p", "test.key"])
self.assertEqual('no-value-set', cfg.get('test.key', 'no-value-set')) self.assertEqual("no-value-set", cfg.get("test.key", "no-value-set"))
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -5,59 +5,62 @@ import core.widget
import core.module import core.module
import core.config import core.config
class TestModule(core.module.Module): class TestModule(core.module.Module):
def __init__(self, config=None, theme=None): def __init__(self, config=None, theme=None):
config = core.config.Config([]) config = core.config.Config([])
super().__init__(config, theme, core.widget.Widget(self.get)) super().__init__(config, theme, core.widget.Widget(self.get))
self.text = '' self.text = ""
@core.decorators.scrollable @core.decorators.scrollable
def get(self, widget): def get(self, widget):
return self.text return self.text
class config(unittest.TestCase): class config(unittest.TestCase):
def setUp(self): def setUp(self):
self.module = TestModule() self.module = TestModule()
self.widget = self.module.widget() self.widget = self.module.widget()
self.width = 10 self.width = 10
self.module.set('width', self.width) self.module.set("width", self.width)
def test_no_text(self): def test_no_text(self):
self.assertEqual('', self.module.text) self.assertEqual("", self.module.text)
self.assertEqual('', self.module.get(self.widget)) self.assertEqual("", self.module.get(self.widget))
def test_smaller(self): def test_smaller(self):
self.module.text = 'test' self.module.text = "test"
self.assertLess(len(self.module.text), self.width) self.assertLess(len(self.module.text), self.width)
self.assertEqual('test', self.module.get(self.widget)) self.assertEqual("test", self.module.get(self.widget))
def test_bigger(self): def test_bigger(self):
self.module.text = 'abcdefghijklmnopqrst' self.module.text = "abcdefghijklmnopqrst"
self.assertGreater(len(self.module.text), self.width) self.assertGreater(len(self.module.text), self.width)
self.assertEqual(self.module.text[: self.width], self.module.get(self.widget)) self.assertEqual(self.module.text[: self.width], self.module.get(self.widget))
def test_bounce(self): def test_bounce(self):
self.module.text = 'abcd' self.module.text = "abcd"
self.module.set('width', 2) self.module.set("width", 2)
self.assertEqual('ab', self.module.get(self.widget)) self.assertEqual("ab", self.module.get(self.widget))
self.assertEqual('bc', self.module.get(self.widget)) self.assertEqual("bc", self.module.get(self.widget))
self.assertEqual('cd', self.module.get(self.widget)) self.assertEqual("cd", self.module.get(self.widget))
self.assertEqual('bc', self.module.get(self.widget)) self.assertEqual("bc", self.module.get(self.widget))
self.assertEqual('ab', self.module.get(self.widget)) self.assertEqual("ab", self.module.get(self.widget))
self.assertEqual('bc', self.module.get(self.widget)) self.assertEqual("bc", self.module.get(self.widget))
self.assertEqual('cd', self.module.get(self.widget)) self.assertEqual("cd", self.module.get(self.widget))
self.assertEqual('bc', self.module.get(self.widget)) self.assertEqual("bc", self.module.get(self.widget))
self.assertEqual('ab', self.module.get(self.widget)) self.assertEqual("ab", self.module.get(self.widget))
def test_nobounce(self): def test_nobounce(self):
self.module.set('scrolling.bounce', False) self.module.set("scrolling.bounce", False)
self.module.text = 'abcd' self.module.text = "abcd"
self.module.set('width', 2) self.module.set("width", 2)
self.assertEqual('ab', self.module.get(self.widget)) self.assertEqual("ab", self.module.get(self.widget))
self.assertEqual('bc', self.module.get(self.widget)) self.assertEqual("bc", self.module.get(self.widget))
self.assertEqual('cd', self.module.get(self.widget)) self.assertEqual("cd", self.module.get(self.widget))
self.assertEqual('ab', self.module.get(self.widget)) self.assertEqual("ab", self.module.get(self.widget))
self.assertEqual('bc', self.module.get(self.widget)) self.assertEqual("bc", self.module.get(self.widget))
self.assertEqual('cd', self.module.get(self.widget)) self.assertEqual("cd", self.module.get(self.widget))
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

Some files were not shown because too many files have changed in this diff Show more