[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
button = {
'left-mouse': 1,
'middle-mouse': 2,
'right-mouse': 3,
'wheel-up': 4,
'wheel-down': 5,
"left-mouse": 1,
"middle-mouse": 2,
"right-mouse": 3,
"wheel-up": 4,
"wheel-down": 5,
}
def main():
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('-i', '--id', help='ID of widget to trigger')
parser.add_argument('-m', '--module', help='name of the module to trigger', required=True)
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("-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()
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)
try:
s.connect(f)
s.sendall(json.dumps({
'name': args.module,
'instance': args.id,
'button': button[args.button],
}).encode('ascii'))
s.sendall(
json.dumps(
{
"name": args.module,
"instance": args.id,
"button": button[args.button],
}
).encode("ascii")
)
except Exception as e:
os.remove(f)
if __name__ == '__main__':
if __name__ == "__main__":
main()
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

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

View file

@ -19,58 +19,66 @@ import modules.contrib
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.'
PARAMETER_HELP = 'Provide configuration parameters in the form of <module>.<key>=<value>'
THEME_HELP = 'Specify the theme to use for drawing modules'
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>"
)
THEME_HELP = "Specify the theme to use for drawing modules"
def all_modules():
"""Return a list of available modules"""
result = {}
for path in [ modules.core.__file__, modules.contrib.__file__ ]:
for path in [modules.core.__file__, modules.contrib.__file__]:
path = os.path.dirname(path)
for mod in glob.iglob('{}/*.py'.format(path)):
result[os.path.basename(mod).replace('.py', '')] = 1
for mod in glob.iglob("{}/*.py".format(path)):
result[os.path.basename(mod).replace(".py", "")] = 1
res = list(result.keys())
res.sort()
return res
class print_usage(argparse.Action):
def __init__(self, option_strings, dest, nargs=None, **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):
if value == 'modules':
if value == "modules":
self._args = namespace
self._format = 'plain'
self._format = "plain"
self.print_modules()
elif value == 'modules-markdown':
elif value == "modules-markdown":
self._args = namespace
self._format = 'markdown'
self._format = "markdown"
self.print_modules()
elif value == 'themes':
elif value == "themes":
self.print_themes()
sys.exit(0)
def print_themes(self):
print(', '.join(core.theme.themes()))
print(", ".join(core.theme.themes()))
def print_modules(self):
if self._format == 'markdown':
print('# Table of modules')
print('|Name |Description |')
print('|-----|------------|')
if self._format == "markdown":
print("# Table of modules")
print("|Name |Description |")
print("|-----|------------|")
for m in all_modules():
try:
basepath = os.path.abspath(os.path.join(os.path.dirname(os.path.realpath(__file__)), '..'))
filename = os.path.join(basepath, 'modules', 'core', '{}.py'.format(m))
basepath = os.path.abspath(
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):
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):
log.warning('module {} not found'.format(m))
log.warning("module {} not found".format(m))
continue
doc = None
@ -79,72 +87,126 @@ class print_usage(argparse.Action):
doc = ast.get_docstring(tree)
if not doc:
log.warning('failed to find docstring for {}'.format(m))
log.warning("failed to find docstring for {}".format(m))
continue
if self._format == 'markdown':
doc = doc.replace('<', '\<')
doc = doc.replace('>', '\>')
doc = doc.replace('\n', '<br>')
print('|{} |{} |'.format(m, doc))
if self._format == "markdown":
doc = doc.replace("<", "\<")
doc = doc.replace(">", "\>")
doc = doc.replace("\n", "<br>")
print("|{} |{} |".format(m, doc))
else:
print(textwrap.fill('{}:'.format(m), 80,
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))
print(
textwrap.fill(
"{}:".format(m),
80,
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:
log.warning(e)
class Config(util.store.Store):
def __init__(self, args):
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.add_argument('-m', '--modules', nargs='+', action='append', default=[],
help=MODULE_HELP)
parser.add_argument('-p', '--parameters', nargs='+', action='append', default=[],
help=PARAMETER_HELP)
parser.add_argument('-t', '--theme', default='default', help=THEME_HELP)
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)
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.add_argument(
"-m", "--modules", nargs="+", action="append", default=[], help=MODULE_HELP
)
parser.add_argument(
"-p",
"--parameters",
nargs="+",
action="append",
default=[],
help=PARAMETER_HELP,
)
parser.add_argument("-t", "--theme", default="default", help=THEME_HELP)
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)
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)
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:
if not '=' in param:
log.error('missing value for parameter "{}" - ignoring this parameter'.format(param))
if not "=" in param:
log.error(
'missing value for parameter "{}" - ignoring this parameter'.format(
param
)
)
continue
key, value = param.split('=', 1)
key, value = param.split("=", 1)
self.set(key, value)
def load_config(self, filename):
if os.path.exists(filename):
log.info('loading {}'.format(filename))
log.info("loading {}".format(filename))
tmp = RawConfigParser()
tmp.read(filename)
if tmp.has_section('module-parameters'):
for key, value in tmp.items('module-parameters'):
if tmp.has_section("module-parameters"):
for key, value in tmp.items("module-parameters"):
self.set(key, value)
def modules(self):
return [item for sub in self.__args.modules for item in sub]
def interval(self, default=1):
return util.format.seconds(self.get('interval', default))
return util.format.seconds(self.get("interval", default))
def debug(self):
return self.__args.debug
@ -164,4 +226,5 @@ class Config(util.store.Store):
def autohide(self, name):
return name in self.__args.autohide
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

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

View file

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

View file

@ -11,45 +11,59 @@ RIGHT_MOUSE = 3
WHEEL_UP = 4
WHEEL_DOWN = 5
def button_name(button):
if button == LEFT_MOUSE: return 'left-mouse'
if button == RIGHT_MOUSE: return 'right-mouse'
if button == MIDDLE_MOUSE: return 'middle-mouse'
if button == WHEEL_UP: return 'wheel-up'
if button == WHEEL_DOWN: return 'wheel-down'
return 'n/a'
if button == LEFT_MOUSE:
return "left-mouse"
if button == RIGHT_MOUSE:
return "right-mouse"
if button == MIDDLE_MOUSE:
return "middle-mouse"
if button == WHEEL_UP:
return "wheel-up"
if button == WHEEL_DOWN:
return "wheel-down"
return "n/a"
class Object(object):
def __init__(self):
super(Object, self).__init__()
self.id = str(uuid.uuid4())
def __event_id(obj_id, button):
return '{}::{}'.format(obj_id, button_name(button))
return "{}::{}".format(obj_id, button_name(button))
def __execute(cmd):
try:
util.cli.execute(cmd, wait=False)
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):
event_id = __event_id(obj.id if obj is not None else '', button)
logging.debug('registering callback {}'.format(event_id))
event_id = __event_id(obj.id if obj is not None else "", button)
logging.debug("registering callback {}".format(event_id))
if callable(cmd):
core.event.register(event_id, cmd)
else:
core.event.register(event_id, lambda _: __execute(cmd))
def trigger(event):
if not 'button' in event: return
if not "button" in event:
return
triggered = False
for field in ['instance', 'name']:
if not field in event: continue
if core.event.trigger(__event_id(event[field], event['button']), event):
for field in ["instance", "name"]:
if not field in event:
continue
if core.event.trigger(__event_id(event[field], event["button"]), event):
triggered = True
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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -42,90 +42,88 @@ import util.cli
import util.graph
import util.format
class Module(core.module.Module):
def __init__(self, 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.__colored = util.format.asbool(self.parameter('colored', False))
self.__colored = util.format.asbool(self.parameter("colored", False))
widget_list = []
for widget_name in self.__widget_names:
if widget_name == 'cpu2.maxfreq':
widget = core.widget.Widget(
name=widget_name, full_text=self.maxfreq)
widget.set('type', 'freq')
elif widget_name == 'cpu2.cpuload':
widget = core.widget.Widget(
name=widget_name, full_text=self.cpuload)
widget.set('type', 'load')
elif widget_name == 'cpu2.coresload':
widget = core.widget.Widget(
name=widget_name, full_text=self.coresload)
widget.set('type', 'loads')
elif widget_name == 'cpu2.temp':
widget = core.widget.Widget(
name=widget_name, full_text=self.temp)
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 widget_name == "cpu2.maxfreq":
widget = core.widget.Widget(name=widget_name, full_text=self.maxfreq)
widget.set("type", "freq")
elif widget_name == "cpu2.cpuload":
widget = core.widget.Widget(name=widget_name, full_text=self.cpuload)
widget.set("type", "load")
elif widget_name == "cpu2.coresload":
widget = core.widget.Widget(name=widget_name, full_text=self.coresload)
widget.set("type", "loads")
elif widget_name == "cpu2.temp":
widget = core.widget.Widget(name=widget_name, full_text=self.temp)
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:
widget.set('pango', True)
widget.set("pango", True)
widget_list.append(widget)
self.widgets(widget_list)
self.__temp_pattern = self.parameter('temp_pattern')
self.__temp_pattern = self.parameter("temp_pattern")
if self.__temp_pattern is None:
self.__temp = 'n/a'
self.__fan_pattern = self.parameter('fan_pattern')
self.__temp = "n/a"
self.__fan_pattern = self.parameter("fan_pattern")
if self.__fan_pattern is None:
self.__fan = 'n/a'
self.__fan = "n/a"
# 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
def maxfreq(self, _):
return '{:.2f}GHz'.format(self.__maxfreq)
return "{:.2f}GHz".format(self.__maxfreq)
def cpuload(self, _):
return '{:>3}%'.format(self.__cpuload)
return "{:>3}%".format(self.__cpuload)
def add_color(self, bar):
"""add color as pango markup to a bar"""
if bar in ['', '']:
color = self.theme.color('green', 'green')
elif bar in ['', '']:
color = self.theme.color('yellow', 'yellow')
elif bar in ['', '']:
color = self.theme.color('orange', 'orange')
elif bar in ['', '']:
color = self.theme.color('red', 'red')
if bar in ["", ""]:
color = self.theme.color("green", "green")
elif bar in ["", ""]:
color = self.theme.color("yellow", "yellow")
elif bar in ["", ""]:
color = self.theme.color("orange", "orange")
elif bar in ["", ""]:
color = self.theme.color("red", "red")
colored_bar = '<span foreground="{}">{}</span>'.format(color, bar)
return colored_bar
def coresload(self, _):
mono_bars = [util.graph.hbar(x) for x in self.__coresload]
if not self.__colored:
return ''.join(mono_bars)
return "".join(mono_bars)
colored_bars = [self.add_color(x) for x in mono_bars]
return ''.join(colored_bars)
return "".join(colored_bars)
def temp(self, _):
if self.__temp == 'n/a' or self.__temp == 0:
return 'n/a'
return '{}°C'.format(self.__temp)
if self.__temp == "n/a" or self.__temp == 0:
return "n/a"
return "{}°C".format(self.__temp)
def fanspeed(self, _):
if self.__fanspeed == 'n/a':
return 'n/a'
return '{}RPM'.format(self.__fanspeed)
if self.__fanspeed == "n/a":
return "n/a"
return "{}RPM".format(self.__fanspeed)
def _parse_sensors_output(self):
output = util.cli.execute('sensors -u')
lines = output.split('\n')
temp = 'n/a'
fan = 'n/a'
output = util.cli.execute("sensors -u")
lines = output.split("\n")
temp = "n/a"
fan = "n/a"
temp_line = None
fan_line = None
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:
break
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:
fan = int(fan_line.split(':')[1].strip()[:-4])
fan = int(fan_line.split(":")[1].strip()[:-4])
return temp, fan
def update(self):
if 'cpu2.maxfreq' in self.__widget_names:
if "cpu2.maxfreq" in self.__widget_names:
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))
if 'cpu2.coresload' in self.__widget_names:
if "cpu2.coresload" in self.__widget_names:
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()
def state(self, widget):
"""for having per-widget icons"""
return [widget.get('type', '')]
return [widget.get("type", "")]
# 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
def default_format(module):
default = '%x %X %Z'
if module == 'datetz':
default = '%x %Z'
if module == 'timetz':
default = '%X %Z'
default = "%x %X %Z"
if module == "datetz":
default = "%x %Z"
if module == "timetz":
default = "%X %Z"
return default
class Module(core.module.Module):
def __init__(self, config, theme):
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.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:
default_timezone = tzlocal.get_localzone().zone
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:
self._timezones = util.format.aslist(self.parameter('timezone', default_timezone))
self._timezones = util.format.aslist(
self.parameter("timezone", default_timezone)
)
except:
self._timezones = [default_timezone]
self._current_tz = 0
l = locale.getdefaultlocale()
if not l or l == (None, None):
l = ('en_US', 'UTF-8')
lcl = self.parameter('locale', '.'.join(l))
l = ("en_US", "UTF-8")
lcl = self.parameter("locale", ".".join(l))
try:
locale.setlocale(locale.LC_TIME, lcl.split('.'))
locale.setlocale(locale.LC_TIME, lcl.split("."))
except Exception:
locale.setlocale(locale.LC_TIME, ('en_US', 'UTF-8'))
locale.setlocale(locale.LC_TIME, ("en_US", "UTF-8"))
def default_format(self):
return '%x %X %Z'
return "%x %X %Z"
def get_time(self, widget):
try:
try:
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:
retval = '[Unknown timezone: {}]'.format(self._timezones[self._current_tz].strip())
retval = "[Unknown timezone: {}]".format(
self._timezones[self._current_tz].strip()
)
except Exception as e:
logging.error('unable to get time: {}'.format(str(e)))
retval = '[n/a]'
logging.error("unable to get time: {}".format(str(e)))
retval = "[n/a]"
enc = locale.getpreferredencoding()
if hasattr(retval, 'decode'):
if hasattr(retval, "decode"):
return retval.decode(enc)
return retval
@ -94,4 +104,5 @@ class Module(core.module.Module):
previous_timezone = 0 # wraparound
self._current_tz = previous_timezone
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

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

View file

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

View file

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

View file

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

View file

@ -15,29 +15,33 @@ import core.module
import core.widget
import core.decorators
class Module(core.module.Module):
@core.decorators.every(seconds=5)
def __init__(self, config, theme):
super().__init__(config, theme, core.widget.Widget(self.docker_info))
self.__info = ''
self.__info = ""
def state(self, widget):
state = []
if self.__info == 'OK - 0':
state.append('warning')
elif self.__info in ['n/a', 'daemon off']:
state.append('critical')
if self.__info == "OK - 0":
state.append("warning")
elif self.__info in ["n/a", "daemon off"]:
state.append("critical")
return state
def docker_info(self, widget):
try:
cli = docker.DockerClient(base_url='unix://var/run/docker.sock')
cli = docker.DockerClient(base_url="unix://var/run/docker.sock")
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:
self.__info = 'daemon off'
self.__info = "daemon off"
except Exception:
self.__info = 'n/a'
self.__info = "n/a"
return self.__info
# 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.widget
@ -8,28 +8,27 @@ import core.input
import util.cli
class Module(core.module.Module):
def __init__(self, config, theme):
super().__init__(config, theme, core.widget.Widget(''))
super().__init__(config, theme, core.widget.Widget(""))
self._paused = False
# Make sure that dunst is currently not paused
util.cli.execute('killall -s SIGUSR2 dunst', ignore_errors=True)
core.input.register(self, button=core.input.LEFT_MOUSE,
cmd=self.toggle_status
)
util.cli.execute("killall -s SIGUSR2 dunst", ignore_errors=True)
core.input.register(self, button=core.input.LEFT_MOUSE, cmd=self.toggle_status)
def toggle_status(self, event):
self._paused = not self._paused
try:
if self._paused:
util.cli.execute('killall -s SIGUSR1 dunst')
util.cli.execute("killall -s SIGUSR1 dunst")
else:
util.cli.execute('killall -s SIGUSR2 dunst')
util.cli.execute("killall -s SIGUSR2 dunst")
except:
self._paused = not self._paused # toggling failed
def state(self, widget):
if self._paused:
return ['muted', 'warning']
return ['unmuted']
return ["muted", "warning"]
return ["unmuted"]

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,4 +1,4 @@
#pylint: disable=C0111,R0903
# pylint: disable=C0111,R0903
"""Displays the indicator status, for numlock, scrolllock and capslock
@ -13,17 +13,28 @@ import core.widget
import util.cli
import util.format
class Module(core.module.Module):
def __init__(self, config, theme):
super().__init__(config, theme, [])
self.__include = tuple(filter(len, util.format.aslist(self.parameter('include', 'NumLock,CapsLock'))))
self.__signalType = self.parameter('signaltype') if not self.parameter('signaltype') is None else 'warning'
self.__include = tuple(
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):
status_line = ''
for line in util.cli.execute('xset q', ignore_errors=True).replace(' ', '').split('\n'):
if 'capslock' in line.lower():
status_line = ""
for line in (
util.cli.execute("xset q", ignore_errors=True).replace(" ", "").split("\n")
):
if "capslock" in line.lower():
status_line = line
break
for indicator in self.__include:
@ -32,15 +43,21 @@ class Module(core.module.Module):
widget = core.widget.Widget(name=indicator, module=self)
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)
def state(self, widget):
states = []
if widget.get('status', False):
if widget.get("status", False):
states.append(self.__signalType)
else:
states.append('normal')
states.append("normal")
return states
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

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

View file

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

View file

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

View file

@ -29,17 +29,18 @@ import core.input
import util.cli
class Module(core.module.Module):
def __init__(self, config, theme):
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.RIGHT_MOUSE, cmd='mocp -t shuffle')
self.__format = self.parameter('format', '%state %artist - %song | %ct/%tt')
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")
self.__format = self.parameter("format", "%state %artist - %song | %ct/%tt")
self.__running = False
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):
self.__load_song()
@ -51,4 +52,5 @@ class Module(core.module.Module):
except RuntimeError:
self.__running = False
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

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

View file

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

View file

@ -20,24 +20,30 @@ import core.widget
import util.cli
class Module(core.module.Module):
def __init__(self, config, theme):
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):
return self.__notmuch_count
def state(self, widgets):
if self.__notmuch_count == 0:
return 'empty'
return 'items'
return "empty"
return "items"
def update(self):
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:
self.__notmuch_count = 'n/a'
self.__notmuch_count = "n/a"
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -15,11 +15,12 @@ import core.widget
import util.cli
import util.format
class Module(core.module.Module):
def __init__(self, config, theme):
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):
return self.__utilization
@ -28,49 +29,52 @@ class Module(core.module.Module):
return "not found" in self.__utilization
def update(self):
sp = util.cli.execute('nvidia-smi -q', ignore_errors=True)
sp = util.cli.execute("nvidia-smi -q", ignore_errors=True)
title = ''
usedMem = ''
totalMem = ''
temp = ''
name = 'not found'
clockMem = ''
clockGpu = ''
fanspeed = ''
for item in sp.split('\n'):
title = ""
usedMem = ""
totalMem = ""
temp = ""
name = "not found"
clockMem = ""
clockGpu = ""
fanspeed = ""
for item in sp.split("\n"):
try:
key, val = item.split(':')
key, val = item.split(":")
key, val = key.strip(), val.strip()
if title == 'Clocks':
if key == 'Graphics':
clockGpu = val.split(' ')[0]
elif key == 'Memory':
clockMem = val.split(' ')[0]
if title == 'FB Memory Usage':
if key == 'Total':
totalMem = val.split(' ')[0]
elif key == 'Used':
usedMem = val.split(' ')[0]
elif key == 'GPU Current Temp':
temp = val.split(' ')[0]
elif key == 'Product Name':
if title == "Clocks":
if key == "Graphics":
clockGpu = val.split(" ")[0]
elif key == "Memory":
clockMem = val.split(" ")[0]
if title == "FB Memory Usage":
if key == "Total":
totalMem = val.split(" ")[0]
elif key == "Used":
usedMem = val.split(" ")[0]
elif key == "GPU Current Temp":
temp = val.split(" ")[0]
elif key == "Product Name":
name = val
elif key == 'Fan Speed':
fanspeed = val.split(' ')[0]
elif key == "Fan Speed":
fanspeed = val.split(" ")[0]
except:
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(
name = name,
temp = temp,
mem_used = usedMem,
mem_total = totalMem,
clock_gpu = clockGpu,
clock_mem = clockMem,
fanspeed = fanspeed,
name=name,
temp=temp,
mem_used=usedMem,
mem_total=totalMem,
clock_gpu=clockGpu,
clock_mem=clockMem,
fanspeed=fanspeed,
)
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -27,20 +27,22 @@ import core.module
import core.widget
import core.input
def get_frame(url):
img_bytes = b""
stream = urllib.request.urlopen(url)
while True:
img_bytes += stream.read(1024)
a = img_bytes.find(b'\xff\xd8')
b = img_bytes.find(b'\xff\xd9')
a = img_bytes.find(b"\xff\xd8")
b = img_bytes.find(b"\xff\xd9")
if a != -1 and b != -1:
jpg = img_bytes[a:b+2]
img_bytes = img_bytes[b+2:]
jpg = img_bytes[a : b + 2]
img_bytes = img_bytes[b + 2 :]
img = Image.open(BytesIO(jpg))
return img
return None
class WebcamImagesWorker(threading.Thread):
def __init__(self, url, queue):
threading.Thread.__init__(self)
@ -48,6 +50,7 @@ class WebcamImagesWorker(threading.Thread):
self.__url = url
self.__queue = queue
self.__running = True
def run(self):
while self.__running:
img = get_frame(self.__url)
@ -56,6 +59,7 @@ class WebcamImagesWorker(threading.Thread):
def stop(self):
self.__running = False
class Module(core.module.Module):
@core.decorators.every(seconds=5)
def __init__(self, config, theme):
@ -75,13 +79,20 @@ class Module(core.module.Module):
self.update_status()
core.input.register(self, button=core.input.LEFT_MOUSE,
cmd=self.__show_popup)
core.input.register(self, button=core.input.LEFT_MOUSE, cmd=self.__show_popup)
def octoprint_status(self, widget):
if self.__octoprint_state == "Offline" or self.__octoprint_state == "Unknown":
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):
url = self.__octoprint_address + "/api/" + endpoint
@ -96,7 +107,10 @@ class Module(core.module.Module):
def __get_printer_bed_temperature(self):
printer_info, status_code = self.__get("printer")
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
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)
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()
if actual_bed_temp is None:
actual_bed_temp = "-"
@ -159,7 +175,6 @@ class Module(core.module.Module):
bed_temp = "Bed: " + str(actual_bed_temp) + "/" + str(target_bed_temp) + " °C"
printer_bed_temperature_label.config(text=bed_temp)
tool_temperatures = self.__get_tool_temperatures()
tools_temp = "Tools: "
@ -167,21 +182,29 @@ class Module(core.module.Module):
tools_temp += "-/- °C"
else:
for i, tool_temperature in enumerate(tool_temperatures):
tools_temp += str(tool_temperature[0]) + "/" + str(tool_temperature[1]) + "°C"
if i != len(tool_temperatures)-1:
tools_temp += (
str(tool_temperature[0]) + "/" + str(tool_temperature[1]) + "°C"
)
if i != len(tool_temperatures) - 1:
tools_temp += "\t"
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):
root = tk.Tk()
root.attributes('-type', 'dialog')
root.attributes("-type", "dialog")
root.title("Octoprint")
frame = tk.Frame(root)
if self.__octoprint_webcam:
#load first image synchronous before popup is shown, otherwise tkinter isn't able to layout popup properly
# load first image synchronous before popup is shown, otherwise tkinter isn't able to layout popup properly
img = get_frame(self.__webcam_image_url)
webcam_image = ImageTk.PhotoImage(img)
webcam_image_container = tk.Button(frame, image=webcam_image)
@ -189,26 +212,38 @@ class Module(core.module.Module):
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()
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()
temperatures_label = tk.Label(frame, text="Temperatures", font=('', 25))
temperatures_label = tk.Label(frame, text="Temperatures", font=("", 25))
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()
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()
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.eval('tk::PlaceWindow . center')
root.eval("tk::PlaceWindow . center")
root.mainloop()
def __on_close_popup(self, event):
@ -221,4 +256,5 @@ class Module(core.module.Module):
def state(self, widget):
return []
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -12,7 +12,8 @@ import core.input
import util.cli
possible_orientations = ['normal', 'left', 'inverted', 'right']
possible_orientations = ["normal", "left", "inverted", "right"]
class Module(core.module.Module):
def __init__(self, config, theme):
@ -20,37 +21,42 @@ class Module(core.module.Module):
def update(self):
widgets = self.widgets()
for line in util.cli.execute('xrandr -q').split('\n'):
if not ' connected' in line:
for line in util.cli.execute("xrandr -q").split("\n"):
if not " connected" in line:
continue
display = line.split(' ', 2)[0]
display = line.split(" ", 2)[0]
orientation = 'normal'
orientation = "normal"
for curr_orient in possible_orientations:
if((line.split(' ')).count(curr_orient) > 1):
if (line.split(" ")).count(curr_orient) > 1:
orientation = curr_orient
break
widget = self.widget(display)
if not widget:
widget = core.widget.Widget(full_text=display, name=display)
core.input.register(widget, button=core.input.LEFT_MOUSE, cmd=self.__toggle)
widget.set('orientation', orientation)
core.input.register(
widget, button=core.input.LEFT_MOUSE, cmd=self.__toggle
)
widget.set("orientation", orientation)
widgets.append(widget)
def state(self, widget):
return widget.get('orientation', 'normal')
return widget.get("orientation", "normal")
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
idx = possible_orientations.index(widget.get('orientation'))
idx = possible_orientations.index(widget.get("orientation"))
idx = (idx + 1) % len(possible_orientations)
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

View file

@ -30,17 +30,19 @@ import core.input
class Module(core.module.Module):
REFRESH_DELAY = 600
SCROLL_SPEED = 3
LAYOUT_STYLES_ITEMS = [[1,1,1],[3,3,2],[2,3,3],[3,2,3]]
HISTORY_FILENAME = '.config/i3/rss.hist'
LAYOUT_STYLES_ITEMS = [[1, 1, 1], [3, 3, 2], [2, 3, 3], [3, 2, 3]]
HISTORY_FILENAME = ".config/i3/rss.hist"
def __init__(self, config, theme):
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._response = ''
self._response = ""
self._max_title_length = int(self.parameter('length', 60))
self._max_title_length = int(self.parameter("length", 60))
self._items = []
self._current_item = None
@ -51,76 +53,103 @@ class Module(core.module.Module):
self._state = []
self._newspaper_filename = tempfile.mktemp('.html')
self._newspaper_filename = tempfile.mktemp(".html")
self._last_refresh = 0
self._last_update = 0
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()
def _load_history(self):
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):
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])
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
]
)
def _save_history(self):
if not os.path.exists(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):
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, _):
if self._current_item:
webbrowser.open(self._current_item['link'])
webbrowser.open(self._current_item["link"])
def _check_for_image(self, entry):
image = next(iter([l['href'] for l in entry['links'] if l['rel'] == 'enclosure']), None)
if not image and 'media_content' in entry:
image = next(
iter([l["href"] for l in entry["links"] if l["rel"] == "enclosure"]), None
)
if not image and "media_content" in entry:
try:
media = sorted(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)
media = sorted(
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:
pass
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:
image = match.group(1)
return image if image else ''
return image if image else ""
def _remove_tags(self, txt):
return re.sub('<[^>]*>', '', txt)
return re.sub("<[^>]*>", "", txt)
def _create_item(self, entry, url, feed):
return {'title': self._remove_tags(entry['title'].replace('\n', ' ')),
'link': entry['link'],
'new': True,
'source': url,
'summary': self._remove_tags(entry['summary']),
'feed': feed,
'image': self._check_for_image(entry),
'published': time.mktime(entry.published_parsed) if hasattr(entry, 'published_parsed') else 0}
return {
"title": self._remove_tags(entry["title"].replace("\n", " ")),
"link": entry["link"],
"new": True,
"source": url,
"summary": self._remove_tags(entry["summary"]),
"feed": feed,
"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):
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
self._check_history(new_items, 'ticker')
self._check_history(new_items, "ticker")
# 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
self._items.extend(new_items)
# 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):
if self._feeds_to_update:
@ -129,12 +158,12 @@ class Module(core.module.Module):
self._update_items_from_feed(url)
if not self._feeds_to_update:
self._update_history('ticker')
self._update_history("ticker")
self._save_history()
if not self._current_item:
self._next_item()
elif time.time()-self._last_refresh >= self.REFRESH_DELAY:
elif time.time() - self._last_refresh >= self.REFRESH_DELAY:
# Populate the list with feeds to update
self._feeds_to_update = self._feeds[:]
# Update the refresh time
@ -149,19 +178,27 @@ class Module(core.module.Module):
return
# 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
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)])
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)]
)
def _check_scroll_done(self):
# 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
self._post_delay -= 1
if self._post_delay == 0:
self._current_item['new'] = False
self._current_item["new"] = False
# Mark the previous item as 'old'
self._next_item()
else:
@ -171,7 +208,7 @@ class Module(core.module.Module):
def ticker_update(self, _):
# Only update the ticker once a second
now = time.time()
if now-self._last_update < 1:
if now - self._last_update < 1:
return self._response
self._last_update = now
@ -180,18 +217,20 @@ class Module(core.module.Module):
# If no items were retrieved, return an empty string
if not self._current_item:
return ' '*self._max_title_length
return " " * self._max_title_length
# 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
self._response = self._response.ljust(self._max_title_length)
# Do not immediately scroll
if self._pre_delay > 0:
# Change state during pre_delay for new items
if self._current_item['new']:
self._state = ['warning']
if self._current_item["new"]:
self._state = ["warning"]
self._pre_delay -= 1
return self._response
@ -205,48 +244,76 @@ class Module(core.module.Module):
def _create_news_element(self, item, overlay_title):
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:
logging.error(str(exc))
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 += " <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 += (
" <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 class='summary'>"+item['summary']+"</div>"
element += "<div class='info'><span class='author'>"+item['feed']+"</span><span class='published'>"+timestr+"</span></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>"
return element
def _create_news_section(self, newspaper_items):
style = random.randint(0, 3)
section = "<table><tr class='style"+str(style)+"'>"
section = "<table><tr class='style" + str(style) + "'>"
for i in range(0, 3):
section += "<td><div class='itemcontainer'>"
for _ in range(0, self.LAYOUT_STYLES_ITEMS[style][i]):
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]
section += "</div></td>"
section += "</tr></table>"
return section
def _create_newspaper(self, _):
content = ''
content = ""
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
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:
content += self._create_news_section(newspaper_items)
open(self._newspaper_filename, 'w').write(HTML_TEMPLATE.replace('[[CONTENT]]', content))
webbrowser.open('file://'+self._newspaper_filename)
self._update_history('newspaper')
open(self._newspaper_filename, "w").write(
HTML_TEMPLATE.replace("[[CONTENT]]", content)
)
webbrowser.open("file://" + self._newspaper_filename)
self._update_history("newspaper")
self._save_history()
HTML_TEMPLATE = """<!DOCTYPE html>
<html>
<head>

View file

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

View file

@ -33,15 +33,16 @@ import core.input
import util.format
import util.cli
class Module(core.module.Module):
def __init__(self, config, theme):
super().__init__(config, theme, core.widget.Widget(self.get_output))
self.__command = self.parameter('command', 'echo "no command configured"')
self.__async = util.format.asbool(self.parameter('async'))
self.__command = self.parameter("command", 'echo "no command configured"')
self.__async = util.format.asbool(self.parameter("async"))
if self.__async:
self.__output = 'please wait...'
self.__output = "please wait..."
self.__current_thread = threading.Thread()
# 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
self.__current_thread = threading.Thread(
target=lambda obj, cmd: obj.set_output(util.cli.execute(cmd, ignore_errors=True)),
args=(self, self.__command)
target=lambda obj, cmd: obj.set_output(
util.cli.execute(cmd, ignore_errors=True)
),
args=(self, self.__command),
)
self.__current_thread.start()
def state(self, _):
if self.__output == 'no command configured':
return 'warning'
if self.__output == "no command configured":
return "warning"
# vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -28,7 +28,7 @@ try:
import tkinter as tk
from tkinter import messagebox as tkmessagebox
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.widget
@ -39,18 +39,18 @@ import util.cli
import util.popup
import util.format
class Module(core.module.Module):
@core.decorators.every(minutes=60)
def __init__(self, config, theme):
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,
cmd=self.popup)
core.input.register(self, button=core.input.LEFT_MOUSE, cmd=self.popup)
def text(self, widget):
return ''
return ""
def __on_command(self, header, text, command):
do_it = True
@ -65,26 +65,49 @@ class Module(core.module.Module):
if do_it:
util.cli.execute(command)
def popup(self, widget):
menu = util.popup.menu()
reboot_cmd = self.parameter('reboot', 'reboot')
shutdown_cmd = self.parameter('shutdown', 'shutdown -h now')
logout_cmd = self.parameter('logout', 'i3exit logout')
switch_user_cmd = self.parameter('switch_user', 'i3exit switch_user')
lock_cmd = self.parameter('lock', 'i3exit lock')
suspend_cmd = self.parameter('suspend', 'i3exit suspend')
hibernate_cmd = self.parameter('hibernate', 'i3exit hibernate')
reboot_cmd = self.parameter("reboot", "reboot")
shutdown_cmd = self.parameter("shutdown", "shutdown -h now")
logout_cmd = self.parameter("logout", "i3exit logout")
switch_user_cmd = self.parameter("switch_user", "i3exit switch_user")
lock_cmd = self.parameter("lock", "i3exit lock")
suspend_cmd = self.parameter("suspend", "i3exit suspend")
hibernate_cmd = self.parameter("hibernate", "i3exit hibernate")
menu.add_menuitem('shutdown', 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'))
menu.add_menuitem(
"shutdown",
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
menu.add_menuitem('switch user', callback=functools.partial(util.cli.execute, switch_user_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.add_menuitem(
"switch user", callback=functools.partial(util.cli.execute, switch_user_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)
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -13,24 +13,26 @@ import core.module
import core.widget
import core.decorators
class Module(core.module.Module):
def __init__(self, config, theme):
super().__init__(config, theme, core.widget.Widget(self.output))
self.__pending_tasks = '0'
self.__pending_tasks = "0"
def update(self):
"""Return a string with the number of pending tasks from TaskWarrior."""
try:
taskrc = self.parameter('taskrc', '~/.taskrc')
taskrc = self.parameter("taskrc", "~/.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))
except:
self.__pending_tasks = 'n/a'
self.__pending_tasks = "n/a"
def output(self, _):
"""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

View file

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

View file

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

View file

@ -12,13 +12,16 @@ import core.module
import core.widget
import core.input
class Module(core.module.Module):
def __init__(self, config, theme):
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()
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):
return str(self.__todos)
@ -28,8 +31,8 @@ class Module(core.module.Module):
def state(self, widgets):
if self.__todos == 0:
return 'empty'
return 'items'
return "empty"
return "items"
def count_items(self):
try:
@ -37,8 +40,9 @@ class Module(core.module.Module):
with open(self.__doc) as f:
for i, l in enumerate(f):
pass
return i+1
return i + 1
except Exception:
return 0
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

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

View file

@ -1,4 +1,4 @@
#pylint: disable=C0111,R0903
# pylint: disable=C0111,R0903
"""Toggle twmn notifications."""
@ -9,14 +9,15 @@ import core.decorators
import util.cli
class Module(core.module.Module):
@core.decorators.every(minutes=60)
def __init__(self, config, theme):
super().__init__(config, theme, core.widget.Widget(''))
super().__init__(config, theme, core.widget.Widget(""))
self.__paused = False
# 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)
def toggle_status(self, event):
@ -24,15 +25,16 @@ class Module(core.module.Module):
try:
if self.__paused:
util.cli.execute('systemctl --user start twmnd')
util.cli.execute("systemctl --user start twmnd")
else:
util.cli.execute('systemctl --user stop twmnd')
util.cli.execute("systemctl --user stop twmnd")
except:
self.__paused = not self.__paused # toggling failed
def state(self, widget):
if self.__paused:
return ['muted']
return ['unmuted']
return ["muted"]
return ["unmuted"]
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -7,17 +7,19 @@ from datetime import timedelta
import core.module
import core.widget
class Module(core.module.Module):
def __init__(self, config, theme):
super().__init__(config, theme, core.widget.Widget(self.output))
self.__uptime = ''
self.__uptime = ""
def output(self, _):
return '{}'.format(self.__uptime)
return "{}".format(self.__uptime)
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]))
self.__uptime = timedelta(seconds = uptime_seconds)
self.__uptime = timedelta(seconds=uptime_seconds)
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

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

View file

@ -17,40 +17,42 @@ import core.decorators
import util.cli
class Module(core.module.Module):
@core.decorators.every(minutes=60)
def __init__(self, config, theme):
super().__init__(config, theme, core.widget.Widget(self.text))
self.__tracking = False
self.__project = ''
self.__project = ""
core.input.register(self, button=core.input.LEFT_MOUSE, cmd=self.toggle)
def toggle(self, widget):
self.__project = 'hit'
self.__project = "hit"
if self.__tracking:
util.cli.execute('watson stop')
util.cli.execute("watson stop")
else:
util.cli.execute('watson restart')
util.cli.execute("watson restart")
self.__tracking = not self.__tracking
def text(self, widget):
if self.__tracking:
return self.__project
else:
return 'Paused'
return "Paused"
def update(self):
output = util.cli.execute('watson status')
if re.match('No project started', output):
output = util.cli.execute("watson status")
if re.match("No project started", output):
self.__tracking = False
return
self.__tracking = True
m = re.search(r'Project (.+) started', output)
m = re.search(r"Project (.+) started", output)
self.__project = m.group(1)
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

View file

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

View file

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

View file

@ -15,21 +15,25 @@ import core.module
import core.widget
import core.decorators
class Module(core.module.Module):
@core.decorators.every(seconds=5)
def __init__(self, config, theme):
super().__init__(config, theme, core.widget.Widget(self.keystate))
self.__keystate = 'No YubiKey'
self.__keystate = "No YubiKey"
def keystate(self, widget):
return self.__keystate
def update(self):
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:
self.__keystate = "No YubiKey"
except Exception:
self.__keystate = 'n/a'
self.__keystate = "n/a"
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -36,46 +36,81 @@ import core.widget
import util.cli
import util.format
class Module(core.module.Module):
def __init__(self, config, theme):
super().__init__(config, theme, [])
self._includelist = set(filter(lambda x: len(x) > 0,
util.format.aslist(self.parameter('list', default=''))))
self._format = self.parameter('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))
self._includelist = set(
filter(
lambda x: len(x) > 0,
util.format.aslist(self.parameter("list", default="")),
)
)
self._format = self.parameter(
"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):
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).
try:
with open(zfs_version_path, 'r') as zfs_mod_version:
zfs_version = zfs_mod_version.readline().rstrip().split('-')[0]
with open(zfs_version_path, "r") as zfs_mod_version:
zfs_version = zfs_mod_version.readline().rstrip().split("-")[0]
except IOError:
# ZFS isn't installed or the module isn't loaded, stub the version
zfs_version = '0.0.0'
logging.error('ZFS version information not found at {}, check the module is loaded.'.format(zfs_version_path))
zfs_version = "0.0.0"
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:
widget.set('visited', False)
widget.set("visited", False)
for raw_zpool in raw_zpools:
try:
# 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'):
name, size, alloc, free, _, frag, cap, dedup, health, _ = raw_zpool.split('\t')
if parse_version(zfs_version) < parse_version("0.8.0"):
(
name,
size,
alloc,
free,
_,
frag,
cap,
dedup,
health,
_,
) = raw_zpool.split("\t")
else:
name, size, alloc, free, _, _, frag, cap, dedup, health, _ = raw_zpool.split('\t')
cap = cap.rstrip('%')
percentuse=int(cap)
percentfree=100-percentuse
(
name,
size,
alloc,
free,
_,
_,
frag,
cap,
dedup,
health,
_,
) = raw_zpool.split("\t")
cap = cap.rstrip("%")
percentuse = int(cap)
percentfree = 100 - percentuse
# There is a command, zpool iostat, which is however blocking and was therefore
# causing issues.
# Instead, we read file `/proc/spl/kstat/zfs/<poolname>/io` which contains
@ -83,7 +118,7 @@ class Module(core.module.Module):
# (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
# 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.
# More info about this file can be found here:
# 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)
if not widget:
widget = core.widget.Widget(name=name)
widget.set('last_iostat', [0, 0, 0, 0])
widget.set('last_timestamp', 0)
widget.set("last_iostat", [0, 0, 0, 0])
widget.set("last_timestamp", 0)
widgets.append(widget)
delta_iostat = [b - a for a, b in zip(iostat, widget.get('last_iostat'))]
widget.set('last_iostat', iostat)
delta_iostat = [b - a for a, b in zip(iostat, widget.get("last_iostat"))]
widget.set("last_iostat", iostat)
# From docs:
# > 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
# was changed.
timestamp = time.time()
delta_timestamp = widget.get('last_timestamp') - timestamp
widget.set('last_timestamp', time.time())
delta_timestamp = widget.get("last_timestamp") - timestamp
widget.set("last_timestamp", time.time())
# abs is there because sometimes the result is -0
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
# rapidly
widget.full_text(self._format.format(name=name, used=alloc, left=free, size=size,
percentfree=percentfree, percentuse=percentuse,
widget.full_text(
self._format.format(
name=name,
used=alloc,
left=free,
size=size,
percentfree=percentfree,
percentuse=percentuse,
status=health,
shortstatus=self._shortstatus(health),
fragpercent=frag, deduppercent=dedup))
widget.set('state', health)
widget.set('percentfree', percentfree)
widget.set('visited', True)
fragpercent=frag,
deduppercent=dedup,
)
)
widget.set("state", health)
widget.set("percentfree", percentfree)
widget.set("visited", True)
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_r = self.widget(rname)
if not widget_w or not widget_r:
@ -141,30 +185,40 @@ class Module(core.module.Module):
widget_w = core.widget.Widget(name=wname)
widgets.extend([widget_r, widget_w])
for w in [widget_r, widget_w]:
w.set('theme.minwidth', self._ioformat.format(ops=9999,
band=util.format.bytefmt(999.99*(1024**2))))
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)))
w.set(
"theme.minwidth",
self._ioformat.format(
ops=9999, band=util.format.bytefmt(999.99 * (1024 ** 2))
),
)
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:
if widget.get('visited') is False:
if widget.get("visited") is False:
widgets.remove(widget)
self.widgets(widgets)
def state(self, widget):
if widget.name.endswith('__read'):
return 'poolread'
elif widget.name.endswith('__write'):
return 'poolwrite'
if widget.name.endswith("__read"):
return "poolread"
elif widget.name.endswith("__write"):
return "poolwrite"
state = widget.get('state')
if state == 'FAULTED':
return [state, 'critical']
elif state == 'DEGRADED' or widget.get('percentfree') < self._warnfree:
return [state, 'warning']
state = widget.get("state")
if state == "FAULTED":
return [state, "critical"]
elif state == "DEGRADED" or widget.get("percentfree") < self._warnfree:
return [state, "warning"]
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
# insufficient replicas to continue functioning.
shortstate = {
'DEGRADED': 'DEG',
'FAULTED': 'FLT',
'ONLINE': 'ONL',
"DEGRADED": "DEG",
"FAULTED": "FLT",
"ONLINE": "ONL",
}
try:
return shortstate[status]
except KeyError:
return ''
return ""
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

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

View file

@ -17,7 +17,8 @@ import core.input
import util.format
class UPowerManager():
class UPowerManager:
def __init__(self):
self.UPOWER_NAME = "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_interface = dbus.Interface(battery_proxy, self.DBUS_PROPERTIES)
hasHistory = battery_proxy_interface.Get(self.UPOWER_NAME + ".Device", "HasHistory")
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")
hasHistory = battery_proxy_interface.Get(
self.UPOWER_NAME + ".Device", "HasHistory"
)
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")
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")
energy = battery_proxy_interface.Get(self.UPOWER_NAME + ".Device", "Energy")
energyempty = battery_proxy_interface.Get(self.UPOWER_NAME + ".Device", "EnergyEmpty")
energyfull = battery_proxy_interface.Get(self.UPOWER_NAME + ".Device", "EnergyFull")
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")
energyempty = battery_proxy_interface.Get(
self.UPOWER_NAME + ".Device", "EnergyEmpty"
)
energyfull = battery_proxy_interface.Get(
self.UPOWER_NAME + ".Device", "EnergyFull"
)
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")
timetoempty = battery_proxy_interface.Get(self.UPOWER_NAME + ".Device", "TimeToEmpty")
timetofull = battery_proxy_interface.Get(self.UPOWER_NAME + ".Device", "TimeToFull")
timetoempty = battery_proxy_interface.Get(
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")
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")
vendor = battery_proxy_interface.Get(self.UPOWER_NAME + ".Device", "Vendor")
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")
warninglevel = battery_proxy_interface.Get(self.UPOWER_NAME + ".Device", "WarningLevel")
updatetime = battery_proxy_interface.Get(self.UPOWER_NAME + ".Device", "UpdateTime")
warninglevel = battery_proxy_interface.Get(
self.UPOWER_NAME + ".Device", "WarningLevel"
)
updatetime = battery_proxy_interface.Get(
self.UPOWER_NAME + ".Device", "UpdateTime"
)
information_table = {
'HasHistory': hasHistory,
'HasStatistics': hasStatistics,
'IsPresent': isPresent,
'IsRechargeable': isRechargable,
'Online': online,
'PowerSupply': powersupply,
'Capacity': capacity,
'Energy': energy,
'EnergyEmpty': energyempty,
'EnergyFull': energyfull,
'EnergyFullDesign': energyfulldesign,
'EnergyRate': energyrate,
'Luminosity': luminosity,
'Percentage': percentage,
'Temperature': temperature,
'Voltage': voltage,
'TimeToEmpty': timetoempty,
'TimeToFull': timetofull,
'IconName': iconname,
'Model': model,
'NativePath': nativepath,
'Serial': serial,
'Vendor': vendor,
'State': state,
'Technology': technology,
'Type': battype,
'WarningLevel': warninglevel,
'UpdateTime': updatetime
"HasHistory": hasHistory,
"HasStatistics": hasStatistics,
"IsPresent": isPresent,
"IsRechargeable": isRechargable,
"Online": online,
"PowerSupply": powersupply,
"Capacity": capacity,
"Energy": energy,
"EnergyEmpty": energyempty,
"EnergyFull": energyfull,
"EnergyFullDesign": energyfulldesign,
"EnergyRate": energyrate,
"Luminosity": luminosity,
"Percentage": percentage,
"Temperature": temperature,
"Voltage": voltage,
"TimeToEmpty": timetoempty,
"TimeToFull": timetofull,
"IconName": iconname,
"Model": model,
"NativePath": nativepath,
"Serial": serial,
"Vendor": vendor,
"State": state,
"Technology": technology,
"Type": battype,
"WarningLevel": warninglevel,
"UpdateTime": updatetime,
}
return information_table
@ -122,40 +159,48 @@ class UPowerManager():
upower_proxy = self.bus.get_object(self.UPOWER_NAME, self.UPOWER_PATH)
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
def is_lid_closed(self):
upower_proxy = self.bus.get_object(self.UPOWER_NAME, self.UPOWER_PATH)
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
def on_battery(self):
upower_proxy = self.bus.get_object(self.UPOWER_NAME, self.UPOWER_PATH)
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
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)
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
def get_wakeups_data(self):
upower_proxy = self.bus.get_object(self.UPOWER_NAME, self.UPOWER_PATH + "/Wakeups")
upower_interface = dbus.Interface(upower_proxy, self.UPOWER_NAME + '.Wakeups')
upower_proxy = self.bus.get_object(
self.UPOWER_NAME, self.UPOWER_PATH + "/Wakeups"
)
upower_interface = dbus.Interface(upower_proxy, self.UPOWER_NAME + ".Wakeups")
data = upower_interface.GetData()
return data
def get_wakeups_total(self):
upower_proxy = self.bus.get_object(self.UPOWER_NAME, self.UPOWER_PATH + "/Wakeups")
upower_interface = dbus.Interface(upower_proxy, self.UPOWER_NAME + '.Wakeups')
upower_proxy = self.bus.get_object(
self.UPOWER_NAME, self.UPOWER_PATH + "/Wakeups"
)
upower_interface = dbus.Interface(upower_proxy, self.UPOWER_NAME + ".Wakeups")
data = upower_interface.GetTotal()
return data
@ -166,7 +211,7 @@ class UPowerManager():
state = int(battery_proxy_interface.Get(self.UPOWER_NAME + ".Device", "State"))
if (state == 1):
if state == 1:
return True
else:
return False
@ -177,21 +222,22 @@ class UPowerManager():
state = int(battery_proxy_interface.Get(self.UPOWER_NAME + ".Device", "State"))
if (state == 0):
if state == 0:
return "Unknown"
elif (state == 1):
elif state == 1:
return "Loading"
elif (state == 2):
elif state == 2:
return "Discharging"
elif (state == 3):
elif state == 3:
return "Empty"
elif (state == 4):
elif state == 4:
return "Fully charged"
elif (state == 5):
elif state == 5:
return "Pending charge"
elif (state == 6):
elif state == 6:
return "Pending discharge"
class Module(core.module.Module):
def __init__(self, config, theme):
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()
except Exception as e:
logging.exception("unable to get battery display device: {}".format(str(e)))
core.input.register(self, button=core.input.LEFT_MOUSE,
cmd="gnome-power-statistics")
core.input.register(
self, button=core.input.LEFT_MOUSE, cmd="gnome-power-statistics"
)
self._showremaining = util.format.asbool(
self.parameter("showremaining", True))
self._showremaining = util.format.asbool(self.parameter("showremaining", True))
def capacity(self, widget):
widget.set("capacity", -1)
@ -225,16 +271,22 @@ class Module(core.module.Module):
p = self.power # an alias to make each line of code shorter
proxy = p.bus.get_object(p.UPOWER_NAME, self.device)
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
remain = int(interface.Get(
p.UPOWER_NAME+".Device", ["TimeToFull", "TimeToEmpty"][state-1]))
remain = int(
interface.Get(
p.UPOWER_NAME + ".Device",
["TimeToFull", "TimeToEmpty"][state - 1],
)
)
remain = util.format.duration(remain, compact=True, unit=True)
output = "{} {}".format(output, remain)
except IndexError:
pass
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
@ -258,9 +310,17 @@ class Module(core.module.Module):
except Exception as e:
logging.exception("unable to get charge value: {}".format(str(e)))
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":
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:
if capacity > 95:
state.append("charged")
@ -268,4 +328,5 @@ class Module(core.module.Module):
state.append("charging")
return state
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

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

View file

@ -17,27 +17,29 @@ import core.decorators
import util.cli
class Module(core.module.Module):
@core.decorators.every(seconds=30)
def __init__(self, config, theme):
super().__init__(config, theme, core.widget.Widget(self.brightness))
self.__brightness = 'n/a'
self.__brightness = "n/a"
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.register_cmd('light -A {}%'.format(step),
'light -U {}%'.format(step))
elif shutil.which('brightnessctl'):
self.register_cmd("light -A {}%".format(step), "light -U {}%".format(step))
elif shutil.which("brightnessctl"):
self.__readcmd = self.__brightnessctl
self.register_cmd('brightnessctl s {}%+'.format(step),
'brightnessctl s {}%-'.format(step))
self.register_cmd(
"brightnessctl s {}%+".format(step), "brightnessctl s {}%-".format(step)
)
else:
self.__readcmd = self.__xbacklight
self.register_cmd('xbacklight +{}%'.format(step),
'xbacklight -{}%'.format(step))
self.register_cmd(
"xbacklight +{}%".format(step), "xbacklight -{}%".format(step)
)
def register_cmd(self, up_cmd, down_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
def __light(self):
return util.cli.execute('light').strip()
return util.cli.execute("light").strip()
def __brightnessctl(self):
m = util.cli.execute('brightnessctl m').strip()
g = util.cli.execute('brightnessctl g').strip()
return float(g)/float(m)*100.0
m = util.cli.execute("brightnessctl m").strip()
g = util.cli.execute("brightnessctl g").strip()
return float(g) / float(m) * 100.0
def __xbacklight(self):
return util.cli.execute('xbacklight -get').strip()
return util.cli.execute("xbacklight -get").strip()
def update(self):
try:
self.__brightness = '{:3.0f}%'.format(float(self.__readcmd()))
self.__brightness = "{:3.0f}%".format(float(self.__readcmd()))
except:
self.__brightness = 'n/a'
self.__brightness = "n/a"
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,6 +1,6 @@
# 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:
* 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.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}%)')
'''
"""
import os
@ -18,20 +18,24 @@ import core.input
import util.format
class Module(core.module.Module):
def __init__(self, config, theme):
super().__init__(config, theme, core.widget.Widget(self.diskspace))
self._path = self.parameter('path', '/')
self._format = self.parameter('format', '{used}/{size} ({percent:05.02f}%)')
self._path = self.parameter("path", "/")
self._format = self.parameter("format", "{used}/{size} ({percent:05.02f}%)")
self._used = 0
self._left = 0
self._size = 0
self._percent = 0
core.input.register(self, button=core.input.LEFT_MOUSE,
cmd='{} {}'.format(self.parameter('open', 'xdg-open'), self._path))
core.input.register(
self,
button=core.input.LEFT_MOUSE,
cmd="{} {}".format(self.parameter("open", "xdg-open"), self._path),
)
def diskspace(self, widget):
used_str = util.format.byte(self._used)
@ -39,20 +43,23 @@ class Module(core.module.Module):
left_str = util.format.byte(self._left)
percent_str = self._percent
return self._format.format(path=self._path,
return self._format.format(
path=self._path,
used=used_str,
left=left_str,
size=size_str,
percent=percent_str)
percent=percent_str,
)
def update(self):
st = os.statvfs(self._path)
self._size = st.f_blocks * st.f_frsize
self._used = (st.f_blocks - st.f_bfree) * st.f_frsize
self._left = self._size - self._used;
self._percent = 100.0 * self._used/self._size
self._left = self._size - self._used
self._percent = 100.0 * self._used / self._size
def state(self, widget):
return self.threshold_state(self._percent, 80, 90)
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

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

View file

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

View file

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

View file

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

View file

@ -1,11 +1,11 @@
# pylint: disable=C0111,R0903
'''Displays system load.
"""Displays system load.
Parameters:
* 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)
'''
"""
import os
import multiprocessing
@ -13,6 +13,7 @@ import multiprocessing
import core.module
import core.input
class Module(core.module.Module):
def __init__(self, config, theme):
super().__init__(config, theme, core.widget.Widget(self.load))
@ -21,11 +22,12 @@ class Module(core.module.Module):
self._cpus = multiprocessing.cpu_count()
except NotImplementedError as e:
self._cpus = 1
core.input.register(self, button=core.input.LEFT_MOUSE,
cmd='gnome-system-monitor')
core.input.register(
self, button=core.input.LEFT_MOUSE, cmd="gnome-system-monitor"
)
def load(self, widget):
return '{:.02f}/{:.02f}/{:.02f}'.format(
return "{:.02f}/{:.02f}/{:.02f}".format(
self._load[0], self._load[1], self._load[2]
)
@ -33,6 +35,7 @@ class Module(core.module.Module):
self._load = os.getloadavg()
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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -6,8 +6,10 @@
import core.widget
import core.module
class Module(core.module.Module):
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

View file

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

View file

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

View file

@ -1,6 +1,6 @@
# 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:
* 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:
* xrandr
'''
"""
import os
import re
@ -33,17 +33,18 @@ try:
except:
pass
class Module(core.module.Module):
@core.decorators.every(seconds=5) # takes up to 5s to detect a new screen
def __init__(self, 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
try:
i3.Subscription(self._output_update, 'output')
i3.Subscription(self._output_update, "output")
except:
pass
@ -58,31 +59,33 @@ class Module(core.module.Module):
self._needs_update = False
for line in util.cli.execute('xrandr -q').split('\n'):
if not ' connected' in line:
for line in util.cli.execute("xrandr -q").split("\n"):
if not " connected" in line:
continue
display = line.split(' ', 2)[0]
m = re.search(r'\d+x\d+\+(\d+)\+\d+', line)
display = line.split(" ", 2)[0]
m = re.search(r"\d+x\d+\+(\d+)\+\d+", line)
widget = self.widget(display)
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=3, cmd=self._toggle)
new_widgets.append(widget)
widget.set('state', 'on' if m else 'off')
widget.set('pos', int(m.group(1)) if m else sys.maxsize)
widget.set("state", "on" if m else "off")
widget.set("pos", int(m.group(1)) if m else sys.maxsize)
self.widgets(new_widgets)
if self._autoupdate == False:
widget = core.widget.Widget(full_text='', module=self)
widget.set('state', 'refresh')
widget = core.widget.Widget(full_text="", module=self)
widget.set("state", "refresh")
core.input.register(widget, button=1, cmd=self._refresh)
self.widgets().append(widget)
def state(self, widget):
return widget.get('state', 'off')
return widget.get("state", "off")
def _refresh(self, event):
self._needs_update = True
@ -91,26 +94,50 @@ class Module(core.module.Module):
self._refresh(self, event)
path = os.path.dirname(os.path.abspath(__file__))
if util.format.asbool(self.parameter('overwrite_i3config', False)) == True:
toggle_cmd = '{}/../../bin/toggle-display.sh'.format(path)
if util.format.asbool(self.parameter("overwrite_i3config", False)) == True:
toggle_cmd = "{}/../../bin/toggle-display.sh".format(path)
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':
util.cli.execute('{} --output {} --off'.format(toggle_cmd, widget.name))
if widget.get("state") == "on":
util.cli.execute("{} --output {} --off".format(toggle_cmd, widget.name))
else:
first_neighbor = next((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)
first_neighbor = next(
(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:
util.cli.execute('{} --output {} --auto'.format(toggle_cmd, widget.name))
util.cli.execute(
"{} --output {} --auto".format(toggle_cmd, widget.name)
)
else:
util.cli.execute('{} --output {} --auto --{}-of {}'.format(toggle_cmd, widget.name,
'left' if event.get('button') == core.input.LEFT_MOUSE else 'right',
neighbor.name))
util.cli.execute(
"{} --output {} --auto --{}-of {}".format(
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

View file

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

View file

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

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