[core] Refactor engine

This is going to be a bit more comprehensive than anticipated. In order
to cleanly refactor the core and the engine, basically start from
scratch with the implementation.

Goals:

* Test coverage
* Maintain backwards compatibility with module interface as much as
  possible (but still make modules easier to code)
* Simplicity

see #23
This commit is contained in:
Tobi-wan Kenobi 2016-12-03 20:38:54 +01:00
parent 20858991b9
commit a8a6c9bba2
72 changed files with 19 additions and 2155 deletions

View file

@ -1,82 +0,0 @@
# bumblebee-status
bumblebee-status is a modular, theme-able status line generator for the [i3 window manager](https://i3wm.org/).
Focus is on:
* Ease of use (no configuration files!)
* Theme support
* Extensibility (of course...)
I hope you like it and appreciate any kind of feedback: Bug reports, Feature requests, etc. :)
Thanks a lot!
# Documentation
See [the wiki](https://github.com/tobi-wan-kenobi/bumblebee-status/wiki) for documentation.
Other resources:
* A list of [available modules](https://github.com/tobi-wan-kenobi/bumblebee-status/wiki/Available-Modules)
* [How to write a theme](https://github.com/tobi-wan-kenobi/bumblebee-status/wiki/How-to-write-a-theme)
* [How to write a module](https://github.com/tobi-wan-kenobi/bumblebee-status/wiki/How-to-write-a-module)
# Installation
```
$ git clone git://github.com/tobi-wan-kenobi/bumblebee-status
```
# Usage
Next, open your i3wm configuration and modify the *status_command* for your i3bar like this:
```
bar {
status_command = <path to bumblebee-status/bumblebee-status> -m <list of modules> -p <list of module parameters> -t <theme>
}
```
You can retrieve a list of modules and themes by entering:
```
$ cd bumblebee-status
$ ./bumblebee-status -l themes
$ ./bumblebee-status -l modules
```
As a simple example, this is what my i3 configuration looks like:
```
bar {
font pango:Inconsolata 10
position top
tray_output none
status_command ~/.i3/bumblebee-status/bumblebee-status -m nic disk:/ cpu memory battery date time pasink pasource dnf -p time.format="%H:%M CW %V" date.format="%a, %b %d %Y" -t solarized-powerline
}
```
Restart i3wm and - that's it!
# Examples
Here are some screenshots for all themes that currently exist:
Gruvbox Powerline (`-t gruvbox-powerline`) (contributed by [@paxy97](https://github.com/paxy97)):
![Gruvbox Powerline](https://github.com/tobi-wan-kenobi/bumblebee-status/blob/master/screenshots/themes/powerline-gruvbox.png)
Solarized Powerline (`-t solarized-powerline`):
![Solarized Powerline](https://github.com/tobi-wan-kenobi/bumblebee-status/blob/master/screenshots/themes/powerline-solarized.png)
Solarized (`-t solarized`):
![Solarized](https://github.com/tobi-wan-kenobi/bumblebee-status/blob/master/screenshots/themes/solarized.png)
Powerline (`-t powerline`):
![Powerline](https://github.com/tobi-wan-kenobi/bumblebee-status/blob/master/screenshots/themes/powerline.png)
Default (nothing or `-t default`):
![Default](https://github.com/tobi-wan-kenobi/bumblebee-status/blob/master/screenshots/themes/default.png)

View file

@ -1,21 +0,0 @@
#!/usr/bin/bash
if ! type -P fakeroot >/dev/null; then
error 'Cannot find the fakeroot binary.'
exit 1
fi
if [[ -z $CHECKUPDATES_DB ]]; then
CHECKUPDATES_DB="${TMPDIR:-/tmp}/checkup-db-${USER}/"
fi
trap 'rm -f $CHECKUPDATES_DB/db.lck' INT TERM EXIT
DBPath="${DBPath:-/var/lib/pacman/}"
eval $(awk -F' *= *' '$1 ~ /DBPath/ { print $1 "=" $2 }' /etc/pacman.conf)
mkdir -p "$CHECKUPDATES_DB"
ln -s "${DBPath}/local" "$CHECKUPDATES_DB" &> /dev/null
fakeroot -- pacman -Sy --dbpath "$CHECKUPDATES_DB" --logfile /dev/null &> /dev/null
fakeroot pacman -Su -p --dbpath "$CHECKUPDATES_DB"
exit 0

View file

@ -1,26 +0,0 @@
#!/usr/bin/env bash
if [ ! -f ~/.i3/config.template ]; then
cp ~/.i3/config ~/.i3/config.template
else
cp ~/.i3/config.template ~/.i3/config
fi
screens=$(xrandr -q|grep ' connected'| grep -P '\d+x\d+' |cut -d' ' -f1)
echo "screens: $screens"
while read -r line; do
screen=$(echo $line | cut -d' ' -f1)
others=$(echo $screens|tr ' ' '\n'|grep -v $screen|tr '\n' '-'|sed 's/.$//')
if [ -f ~/.i3/config.$screen-$others ]; then
cat ~/.i3/config.$screen-$others >> ~/.i3/config
else
if [ -f ~/.i3/config.$screen ]; then
cat ~/.i3/config.$screen >> ~/.i3/config
fi
fi
done <<< "$screens"
i3-msg restart

View file

@ -1,12 +0,0 @@
#!/usr/bin/env bash
echo $(dirname $(readlink -f "$0"))
i3bar_update=$(dirname $(readlink -f "$0"))/load-i3-bars.sh
xrandr "$@"
if [ -f $i3bar_update ]; then
sleep 1
$i3bar_update
fi

18
bumblebee-status Executable file → Normal file
View file

@ -1,18 +0,0 @@
#!/usr/bin/env python
import sys
import bumblebee.config
import bumblebee.engine
def main():
config = bumblebee.config.Config(sys.argv[1:])
engine = bumblebee.engine.Engine(config)
engine.load_modules()
engine.run()
if __name__ == "__main__":
main()
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -1,123 +1,19 @@
import os
import argparse
import textwrap
import bumblebee.theme
import bumblebee.module
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 = " "*4
def __call__(self, parser, namespace, value, option_string=None):
if value == "modules":
self.print_modules()
elif value == "themes":
self.print_themes()
else:
parser.print_help()
parser.exit()
def print_themes(self):
print(textwrap.fill(", ".join(bumblebee.theme.themes()),
80, initial_indent = self._indent, subsequent_indent = self._indent
))
def print_modules(self):
for m in bumblebee.module.modules():
print(textwrap.fill("{}: {}".format(m.name(), m.description()),
80, initial_indent=self._indent*2, subsequent_indent=self._indent*3))
print("{}Parameters:".format(self._indent*2))
for p in m.parameters():
print(textwrap.fill("* {}".format(p),
80, initial_indent=self._indent*3, subsequent_indent=self._indent*4))
print("")
class ModuleConfig(object):
def __init__(self, config, name, alias):
self._prefix = alias if alias else name
self._config = config
def prefix(self):
return self._prefix
def set(self, name, value):
name = self._prefix + name
return self._config.set(name, value)
def parameter(self, name, default=None):
name = self._prefix + name
return self._config.parameter(name, default)
def increase(self, name, limit, default):
name = self._prefix + name
return self._config.increase(name, limit, default)
MODULE_HELP = ""
class Config(object):
def __init__(self, args):
self._parser = self._parser()
self._store = {}
if len(args) == 0:
self._parser.print_help()
self._parser.exit()
self._args = self._parser.parse_args(args)
for p in self._args.parameters:
key, value = p.split("=")
self.parameter(key, value)
def set(self, name, value):
self._store[name] = value
def parameter(self, name, default=None):
if not name in self._store:
self.set(name, default)
return self._store.get(name, default)
def increase(self, name, limit, default):
if not name in self._store:
self._store[name] = default
return default
self._store[name] += 1
if self._store[name] >= limit:
self._store[name] = default
return self._store[name]
def theme(self):
return self._args.theme
def __init__(self, args = []):
parser = self._create_parser()
self._args = parser.parse_args(args)
def modules(self):
result = []
for m in self._args.modules:
items = m.split(":")
result.append({ "name": items[0], "alias": items[1] if len(items) > 1 else None })
return result
return map(lambda x: { "name": x, "module": x }, self._args.modules)
def _parser(self):
def _create_parser(self):
parser = argparse.ArgumentParser(description="display system data in the i3bar")
parser.add_argument("-m", "--modules", nargs="+",
help="List of modules to load. The order of the list determines "
"their order in the i3bar (from left to right)",
default=[],
)
parser.add_argument("-l", "--list",
help="List: 'modules', 'themes' ",
choices = [ "modules", "themes" ],
action=print_usage,
)
parser.add_argument("-p", "--parameters", nargs="+",
help="Provide configuration parameters to individual modules.",
default=[]
)
parser.add_argument("-t", "--theme", help="Specify which theme to use for "
"drawing the modules",
default="default",
)
parser.add_argument("-m", "--modules", nargs="+", default = [],
help = MODULE_HELP)
return parser
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -1,38 +0,0 @@
import importlib
import bumblebee.theme
import bumblebee.output
import bumblebee.config
import bumblebee.modules
class Engine:
def __init__(self, config):
self._modules = []
self._config = config
self._theme = bumblebee.theme.Theme(config)
self._output = bumblebee.output.output(config)
def load_module(self, modulespec):
name = modulespec["name"]
module = importlib.import_module("bumblebee.modules.{}".format(name))
cfg = bumblebee.config.ModuleConfig(self._config, name, modulespec["alias"])
obj = getattr(module, "Module")(self._output, cfg)
obj.register_callbacks()
return obj
def load_modules(self):
for m in self._config.modules():
self._modules.append(self.load_module(m))
def run(self):
self._output.start()
while True:
self._theme.begin()
for m in self._modules:
self._output.draw(m.widgets(), self._theme)
self._output.flush()
self._output.wait()
self._output.stop()
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -1,61 +0,0 @@
import os
import pkgutil
import importlib
import bumblebee.modules
def modules():
result = []
path = os.path.dirname(bumblebee.modules.__file__)
for mod in [ name for _, name, _ in pkgutil.iter_modules([path])]:
result.append(ModuleDescription(mod))
return result
class ModuleDescription(object):
def __init__(self, name):
self._name = name
self._mod =importlib.import_module("bumblebee.modules.{}".format(name))
def name(self):
return str(self._name)
def description(self):
return getattr(self._mod, "description", lambda: "n/a")()
def parameters(self):
return getattr(self._mod, "parameters", lambda: [ "n/a" ])()
class Module(object):
def __init__(self, output, config):
self._output = output
self._config = config
def register_callbacks(self):
buttons = [
{ "name": "left-click", "id": 1 },
{ "name": "middle-click", "id": 2 },
{ "name": "right-click", "id": 3 },
{ "name": "wheel-up", "id": 4 },
{ "name": "wheel-down", "id": 5 },
]
for button in buttons:
if self._config.parameter(button["name"], None):
output.add_callback(
module=self.instance(),
button=button["id"],
cmd=self._config.parameter(button["name"])
)
def critical(self, widget):
return False
def warning(self, widget):
return False
def state(self, widget):
return "default"
def instance(self, widget=None):
return self._config.prefix()
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -1,52 +0,0 @@
import datetime
import bumblebee.module
import os.path
def description():
return "Displays battery status, percentage and whether it's charging or discharging."
def parameters():
return [ "battery.device: The device to read from (defaults to BAT0)" ]
class Module(bumblebee.module.Module):
def __init__(self, output, config):
super(Module, self).__init__(output, config)
self._battery = config.parameter("device", "BAT0")
self._capacity = 100
self._status = "Unknown"
def widgets(self):
self._AC = False;
self._path = "/sys/class/power_supply/{}".format(self._battery)
if not os.path.exists(self._path):
self._AC = True;
return bumblebee.output.Widget(self,"AC")
with open(self._path + "/capacity") as f:
self._capacity = int(f.read())
self._capacity = self._capacity if self._capacity < 100 else 100
return bumblebee.output.Widget(self,"{:02d}%".format(self._capacity))
def warning(self, widget):
return self._capacity < self._config.parameter("warning", 20)
def critical(self, widget):
return self._capacity < self._config.parameter("critical", 10)
def state(self, widget):
if self._AC:
return "AC"
with open(self._path + "/status") as f:
self._status = f.read().strip()
if self._status == "Discharging":
status = "discharging-{}".format(min([ 10, 25, 50, 80, 100] , key=lambda i:abs(i-self._capacity)))
return status
else:
if self._capacity > 95:
return "charged"
return "charging"
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -1,33 +0,0 @@
import bumblebee.module
def description():
return "Displays brightness percentage"
def parameters():
return [
"brightness.step: Steps (in percent) to increase/decrease brightness on scroll (defaults to 2)",
]
class Module(bumblebee.module.Module):
def __init__(self, output, config):
super(Module, self).__init__(output, config)
self._brightness = 0
self._max = 0
self._percent = 0
step = self._config.parameter("step", 2)
output.add_callback(module=self.instance(), button=4, cmd="xbacklight +{}%".format(step))
output.add_callback(module=self.instance(), button=5, cmd="xbacklight -{}%".format(step))
def widgets(self):
with open("/sys/class/backlight/intel_backlight/brightness") as f:
self._brightness = int(f.read())
with open("/sys/class/backlight/intel_backlight/max_brightness") as f:
self._max = int(f.read())
self._brightness = self._brightness if self._brightness < self._max else self._max
self._percent = int(round(self._brightness * 100 / self._max))
return bumblebee.output.Widget(self, "{:02d}%".format(self._percent))
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -1,38 +0,0 @@
import subprocess
import shlex
import bumblebee.module
def description():
return "Enable/disable auto screen lock."
class Module(bumblebee.module.Module):
def __init__(self, output, config):
super(Module, self).__init__(output, config)
self._activated = 0
output.add_callback(module="caffeine.activate", button=1, cmd=[ 'notify-send "Consuming caffeine"', 'xset s off' ])
output.add_callback(module="caffeine.deactivate", button=1, cmd=[ 'notify-send "Out of coffee"', 'xset s default' ])
def widgets(self):
output = subprocess.check_output(shlex.split("xset q"))
xset_out = output.decode().split("\n")
for line in xset_out:
if line.startswith(" timeout"):
timeout = int(line.split(" ")[4])
if timeout == 0:
self._activated = 1;
else:
self._activated = 0;
break
if self._activated == 0:
return bumblebee.output.Widget(self, "", instance="caffeine.activate")
elif self._activated == 1:
return bumblebee.output.Widget(self, "", instance="caffeine.deactivate")
def state(self, widget):
if self._activated == 1:
return "activated"
else:
return "deactivated"

View file

@ -1,78 +0,0 @@
import string
import datetime
import subprocess
from collections import defaultdict
import bumblebee.util
import bumblebee.module
def description():
return "Displays the current song and artist playing in cmus"
def parameters():
return [
"cmus.format: Format of the displayed song information, arbitrary tags (as available from cmus-remote -Q) can be used (defaults to {artist} - {title} {position}/{duration})"
]
class Module(bumblebee.module.Module):
def __init__(self, output, config):
super(Module, self).__init__(output, config)
self._status = "default"
self._fmt = self._config.parameter("format", "{artist} - {title} {position}/{duration}")
output.add_callback(module="cmus.prev", button=1, cmd="cmus-remote -r")
output.add_callback(module="cmus.next", button=1, cmd="cmus-remote -n")
output.add_callback(module="cmus.shuffle", button=1, cmd="cmus-remote -S")
output.add_callback(module="cmus.repeat", button=1, cmd="cmus-remote -R")
output.add_callback(module=self.instance(), button=1, cmd="cmus-remote -u")
def _loadsong(self):
process = subprocess.Popen(["cmus-remote", "-Q"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
self._query, self._error = process.communicate()
self._query = self._query.decode("utf-8").split("\n")
self._status = "default"
def _tags(self):
tags = defaultdict(lambda: '')
self._repeat = False
self._shuffle = False
for line in self._query:
if line.startswith("status"):
status = line.split(" ", 2)[1]
self._status = status
if line.startswith("tag"):
key, value = line.split(" ", 2)[1:]
tags.update({ key: value })
if line.startswith("duration"):
sec = line.split(" ")[1]
tags.update({ "duration": bumblebee.util.durationfmt(int(sec)) })
if line.startswith("position"):
sec = line.split(" ")[1]
tags.update({ "position": bumblebee.util.durationfmt(int(sec)) })
if line.startswith("set repeat "):
self._repeat = False if line.split(" ")[2] == "false" else True
if line.startswith("set shuffle "):
self._shuffle = False if line.split(" ")[2] == "false" else True
return tags
def widgets(self):
self._loadsong()
tags = self._tags()
return [
bumblebee.output.Widget(self, "", instance="cmus.prev"),
bumblebee.output.Widget(self, string.Formatter().vformat(self._fmt, (), tags)),
bumblebee.output.Widget(self, "", instance="cmus.next"),
bumblebee.output.Widget(self, "", instance="cmus.shuffle"),
bumblebee.output.Widget(self, "", instance="cmus.repeat"),
]
def state(self, widget):
if widget.instance() == "cmus.shuffle":
return "on" if self._shuffle else "off"
if widget.instance() == "cmus.repeat":
return "on" if self._repeat else "off"
return self._status
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -1,30 +0,0 @@
import bumblebee.module
import psutil
def description():
return "Displays CPU utilization across all CPUs."
def parameters():
return [
"cpu.warning: Warning threshold in % of disk usage (defaults to 70%)",
"cpu.critical: Critical threshold in % of disk usage (defaults to 80%)",
]
class Module(bumblebee.module.Module):
def __init__(self, output, config):
super(Module, self).__init__(output, config)
self._perc = psutil.cpu_percent(percpu=False)
output.add_callback(module=self.instance(), button=1, cmd="gnome-system-monitor")
def widgets(self):
self._perc = psutil.cpu_percent(percpu=False)
return bumblebee.output.Widget(self, "{:05.02f}%".format(self._perc))
def warning(self, widget):
return self._perc > self._config.parameter("warning", 70)
def critical(self, widget):
return self._perc > self._config.parameter("critical", 80)
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -1 +0,0 @@
datetime.py

View file

@ -1 +0,0 @@
time.py

View file

@ -1,40 +0,0 @@
import os
import bumblebee.util
import bumblebee.module
def description():
return "Shows free diskspace, total diskspace and the percentage of free disk space."
def parameters():
return [
"disk.warning: Warning threshold in % (defaults to 80%)",
"disk.critical: Critical threshold in % (defaults to 90%)"
]
class Module(bumblebee.module.Module):
def __init__(self, output, config):
super(Module, self).__init__(output, config)
self._path = self._config.parameter("path", "/")
output.add_callback(module=self.instance(), button=1, cmd="nautilus {}".format(self._path))
def widgets(self):
st = os.statvfs(self._path)
self._size = st.f_frsize*st.f_blocks
self._used = self._size - st.f_frsize*st.f_bavail
self._perc = 100.0*self._used/self._size
return bumblebee.output.Widget(self,
"{} {}/{} ({:05.02f}%)".format(self._path,
bumblebee.util.bytefmt(self._used),
bumblebee.util.bytefmt(self._size), self._perc)
)
def warning(self, widget):
return self._perc > self._config.parameter("warning", 80)
def critical(self, widget):
return self._perc > self._config.parameter("critical", 90)
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -1,98 +0,0 @@
from __future__ import absolute_import
import time
import shlex
import threading
import subprocess
import bumblebee.module
import bumblebee.util
def description():
return "Checks DNF for updated packages and displays the number of <security>/<bugfixes>/<enhancements>/<other> pending updates."
def parameters():
return [ "dnf.interval: Time in seconds between two checks for updates (defaults to 1800)" ]
def get_dnf_info(obj):
loops = obj.interval()
for thread in threading.enumerate():
if thread.name == "MainThread":
main = thread
while main.is_alive():
loops += 1
if loops < obj.interval():
time.sleep(1)
continue
loops = 0
try:
res = subprocess.check_output(shlex.split("dnf updateinfo"))
except Exception as e:
break
security = 0
bugfixes = 0
enhancements = 0
other = 0
for line in res.decode().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:
for s in line.split():
if s.isdigit(): bugfixes += int(s)
elif "hancement" in line:
for s in line.split():
if s.isdigit(): enhancements += int(s)
else:
for s in line.split():
if s.isdigit(): other += int(s)
obj.set("security", security)
obj.set("bugfixes", bugfixes)
obj.set("enhancements", enhancements)
obj.set("other", other)
class Module(bumblebee.module.Module):
def __init__(self, output, config):
super(Module, self).__init__(output, config)
self._counter = {}
self._thread = threading.Thread(target=get_dnf_info, args=(self,))
self._thread.start()
def interval(self):
return self._config.parameter("interval", 30*60)
def set(self, what, value):
self._counter[what] = value
def get(self, what):
return self._counter.get(what, 0)
def widgets(self):
result = []
for t in [ "security", "bugfixes", "enhancements", "other" ]:
result.append(str(self.get(t)))
return bumblebee.output.Widget(self, "/".join(result))
def state(self, widget):
total = sum(self._counter.values())
if total == 0: return "good"
return "default"
def warning(self, widget):
total = sum(self._counter.values())
return total > 0
def critical(self, widget):
total = sum(self._counter.values())
return total > 50 or self._counter.get("security", 0) > 0
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -1,67 +0,0 @@
import subprocess
import shlex
import bumblebee.module
import bumblebee.util
def description():
return "Showws current keyboard layout and change it on click."
def parameters():
return [
"layout.lang: pipe-separated list of languages to cycle through (e.g. us|rs|de). Default: en"
]
class Module(bumblebee.module.Module):
def __init__(self, output, config):
super(Module, self).__init__(output, config)
self._languages = self._config.parameter("lang", "en").split("|")
self._idx = 0
output.add_callback(module=self.instance(), button=1, cmd=self.next_keymap)
output.add_callback(module=self.instance(), button=3, cmd=self.prev_keymap)
def next_keymap(self, event, widget):
self._idx = self._idx + 1 if self._idx < len(self._languages) - 1 else 0
self.set_keymap()
def prev_keymap(self, event, widget):
self._idx = self._idx - 1 if self._idx > 0 else len(self._languages) - 1
self.set_keymap()
def set_keymap(self):
tmp = self._languages[self._idx].split(":")
layout = tmp[0]
variant = ""
if len(tmp) > 1:
variant = "-variant {}".format(tmp[1])
bumblebee.util.execute("setxkbmap -layout {} {}".format(layout, variant))
def widgets(self):
res = bumblebee.util.execute("setxkbmap -query")
layout = None
variant = None
for line in res.split("\n"):
if not line:
continue
if "layout" in line:
layout = line.split(":")[1].strip()
if "variant" in line:
variant = line.split(":")[1].strip()
if variant:
layout += ":" + variant
lang = self._languages[self._idx]
if lang != layout:
if layout in self._languages:
self._idx = self._languages.index(layout)
else:
self._languages.append(layout)
self._idx = len(self._languages) - 1
lang = layout
return bumblebee.output.Widget(self, lang)
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -1,37 +0,0 @@
import bumblebee.module
import multiprocessing
import os
def description():
return "Displays system load."
def parameters():
return [
"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 80% of the number of CPUs)"
]
class Module(bumblebee.module.Module):
def __init__(self, output, config):
super(Module, self).__init__(output, config)
self._cpus = 1
try:
self._cpus = multiprocessing.cpu_count()
except multiprocessing.NotImplementedError as e:
pass
output.add_callback(module=self.instance(), button=1, cmd="gnome-system-monitor")
def widgets(self):
self._load = os.getloadavg()
return bumblebee.output.Widget(self, "{:.02f}/{:.02f}/{:.02f}".format(
self._load[0], self._load[1], self._load[2]))
def warning(self, widget):
return self._load[0] > self._config.parameter("warning", self._cpus*0.7)
def critical(self, widget):
return self._load[0] > self._config.parameter("critical", self._cpus*0.8)
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -1,38 +0,0 @@
import psutil
import bumblebee.module
import bumblebee.util
def description():
return "Shows available RAM, total amount of RAM and the percentage of available RAM."
def parameters():
return [
"memory.warning: Warning threshold in % of memory used (defaults to 80%)",
"memory.critical: Critical threshold in % of memory used (defaults to 90%)",
]
class Module(bumblebee.module.Module):
def __init__(self, output, config):
super(Module, self).__init__(output, config)
self._mem = psutil.virtual_memory()
output.add_callback(module=self.instance(), button=1, cmd="gnome-system-monitor")
def widgets(self):
self._mem = psutil.virtual_memory()
used = self._mem.total - self._mem.available
return bumblebee.output.Widget(self, "{}/{} ({:05.02f}%)".format(
bumblebee.util.bytefmt(used),
bumblebee.util.bytefmt(self._mem.total),
self._mem.percent)
)
def warning(self, widget):
return self._mem.percent > self._config.parameter("warning", 80)
def critical(self, widget):
return self._mem.percent > self._config.parameter("critical", 90)
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -1,62 +0,0 @@
import netifaces
import bumblebee.module
def description():
return "Displays the names, IP addresses and status of each available interface."
def parameters():
return [
"nic.exclude: Comma-separated list of interface prefixes to exlude (defaults to: \"lo,virbr,docker,vboxnet,veth\")"
]
class Module(bumblebee.module.Module):
def __init__(self, output, config):
super(Module, self).__init__(output, config)
self._exclude = tuple(filter(len, self._config.parameter("exclude", "lo,virbr,docker,vboxnet,veth").split(",")))
def widgets(self):
result = []
interfaces = [ i for i in netifaces.interfaces() if not i.startswith(self._exclude) ]
for intf in interfaces:
addr = []
state = "down"
try:
if netifaces.AF_INET in netifaces.ifaddresses(intf):
for ip in netifaces.ifaddresses(intf)[netifaces.AF_INET]:
if "addr" in ip and ip["addr"] != "":
addr.append(ip["addr"])
state = "up"
except Exception as e:
addr = []
widget = bumblebee.output.Widget(self, "{} {} {}".format(
intf, state, ", ".join(addr)
))
widget.set("intf", intf)
widget.set("state", state)
result.append(widget)
return result
def _iswlan(self, intf):
# wifi, wlan, wlp, seems to work for me
if intf.startswith("w"): return True
return False
def _istunnel(self, intf):
return intf.startswith("tun")
def state(self, widget):
intf = widget.get("intf")
iftype = "wireless" if self._iswlan(intf) else "wired"
iftype = "tunnel" if self._istunnel(intf) else iftype
return "{}-{}".format(iftype, widget.get("state"))
def warning(self, widget):
return widget.get("state") != "up"
def critical(self, widget):
return widget.get("state") == "down"
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -1,58 +0,0 @@
import bumblebee.module
import subprocess
import os
def description():
return "Displays available updates per repository for pacman."
class Module(bumblebee.module.Module):
def __init__(self, output, config):
super(Module, self).__init__(output, config)
self._count = 0
def widgets(self):
path = os.path.dirname(os.path.abspath(__file__))
if self._count == 0:
self._out = "?/?/?/?"
process = subprocess.Popen([ "{}/../../bin/customupdates".format(path) ], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
self._query, self._error = process.communicate()
if not process.returncode == 0:
self._out = "?/?/?/?"
else:
self._community = 0
self._core = 0
self._extra = 0
self._other = 0
for line in self._query.splitlines():
if line.startswith(b'http'):
if b"community" in line:
self._community += 1
continue
if b"core" in line:
self._core += 1;
continue
if b"extra" in line:
self._extra += 1
continue
self._other += 1
self._out = str(self._core)+"/"+str(self._extra)+"/"+str(self._community)+"/"+str(self._other)
self._count += 1
self._count = 0 if self._count > 300 else self._count
return bumblebee.output.Widget(self, "{}".format(self._out))
def sumUpdates(self):
return self._core + self._community + self._extra + self._other
def critical(self, widget):
#return self._sumUpdates(self)
return self.sumUpdates() > 0
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -1 +0,0 @@
pulseaudio.py

View file

@ -1 +0,0 @@
pulseaudio.py

View file

@ -1,97 +0,0 @@
from __future__ import absolute_import
import re
import time
import shlex
import threading
import subprocess
import bumblebee.module
import bumblebee.util
def description():
return "Periodically checks the RTT of a configurable IP"
def parameters():
return [
"ping.interval: Time in seconds between two RTT checks (defaults to 60)",
"ping.address: IP address to check",
"ping.warning: Threshold for warning state, in seconds (defaults to 1.0)",
"ping.critical: Threshold for critical state, in seconds (defaults to 2.0)",
"ping.timeout: Timeout for waiting for a reply (defaults to 5.0)",
"ping.probes: Number of probes to send (defaults to 5)",
]
def get_rtt(obj):
loops = obj.get("interval")
for thread in threading.enumerate():
if thread.name == "MainThread":
main = thread
interval = obj.get("interval")
while main.is_alive():
loops += 1
if loops < interval:
time.sleep(1)
continue
loops = 0
try:
res = subprocess.check_output(shlex.split("ping -n -q -c {} -W {} {}".format(
obj.get("rtt-probes"), obj.get("rtt-timeout"), obj.get("address")
)))
obj.set("rtt-unreachable", False)
for line in res.decode().split("\n"):
if not line.startswith("rtt"): continue
m = re.search(r'([0-9\.]+)/([0-9\.]+)/([0-9\.]+)/([0-9\.]+)\s+(\S+)', line)
obj.set("rtt-min", float(m.group(1)))
obj.set("rtt-avg", float(m.group(2)))
obj.set("rtt-max", float(m.group(3)))
obj.set("rtt-unit", m.group(5))
except Exception as e:
obj.set("rtt-unreachable", True)
class Module(bumblebee.module.Module):
def __init__(self, output, config):
super(Module, self).__init__(output, config)
self._counter = {}
self.set("address", self._config.parameter("address", "8.8.8.8"))
self.set("interval", self._config.parameter("interval", 60))
self.set("rtt-probes", self._config.parameter("probes", 5))
self.set("rtt-timeout", self._config.parameter("timeout", 5.0))
self._thread = threading.Thread(target=get_rtt, args=(self,))
self._thread.start()
def set(self, what, value):
self._counter[what] = value
def get(self, what):
return self._counter.get(what, 0)
def widgets(self):
text = "{}: {:.1f}{}".format(
self.get("address"),
self.get("rtt-avg"),
self.get("rtt-unit")
)
if self.get("rtt-unreachable"):
text = "{}: unreachable".format(self.get("address"))
return bumblebee.output.Widget(self, text)
def warning(self, widget):
return self.get("rtt-avg") > float(self._config.parameter("warning", 1.0))*1000.0
def critical(self, widget):
if self.get("rtt-unreachable"): return True
return self.get("rtt-avg") > float(self._config.parameter("critical", 2.0))*1000.0
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -1,92 +0,0 @@
import re
import shlex
import subprocess
import bumblebee.module
import bumblebee.util
def description():
module = __name__.split(".")[-1]
if module == "pasink":
return "Shows volume and mute status of the default PulseAudio Sink."
if module == "pasource":
return "Shows volume and mute status of the default PulseAudio Source."
return "See 'pasource'."
def parameters():
return [ "none" ]
class Module(bumblebee.module.Module):
def __init__(self, output, config):
super(Module, self).__init__(output, config)
self._module = self.__module__.split(".")[-1]
self._left = 0
self._right = 0
self._mono = 0
self._mute = False
channel = "sink" if self._module == "pasink" else "source"
output.add_callback(module=self.instance(), button=3,
cmd="pavucontrol")
output.add_callback(module=self.instance(), button=1,
cmd="pactl set-{}-mute @DEFAULT_{}@ toggle".format(channel, channel.upper()))
output.add_callback(module=self.instance(), button=4,
cmd="pactl set-{}-volume @DEFAULT_{}@ +2%".format(channel, channel.upper()))
output.add_callback(module=self.instance(), button=5,
cmd="pactl set-{}-volume @DEFAULT_{}@ -2%".format(channel, channel.upper()))
def widgets(self):
res = subprocess.check_output(shlex.split("pactl info"))
channel = "sinks" if self._module == "pasink" else "sources"
name = None
for line in res.decode().split("\n"):
if line.startswith("Default Sink: ") and channel == "sinks":
name = line[14:]
if line.startswith("Default Source: ") and channel == "sources":
name = line[16:]
res = subprocess.check_output(shlex.split("pactl list {}".format(channel)))
found = False
for line in res.decode().split("\n"):
if "Name:" in line and found == True:
break
if name in line:
found = True
if "Mute:" in line and found == True:
self._mute = False if " no" in line.lower() else True
if "Volume:" in line and found == True:
m = None
if "mono" in line:
m = re.search(r'mono:.*\s*\/\s*(\d+)%', line)
else:
m = re.search(r'left:.*\s*\/\s*(\d+)%.*right:.*\s*\/\s*(\d+)%', line)
if not m: continue
if "mono" in line:
self._mono = m.group(1)
else:
self._left = m.group(1)
self._right = m.group(2)
result = ""
if int(self._mono) > 0:
result = "{}%".format(self._mono)
elif self._left == self._right:
result = "{}%".format(self._left)
else:
result="{}%/{}%".format(self._left, self._right)
return bumblebee.output.Widget(self, result)
def state(self, widget):
return "muted" if self._mute is True else "unmuted"
def warning(self, widget):
return self._mute
def critical(self, widget):
return False
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -1,17 +0,0 @@
import bumblebee.module
import bumblebee.util
def description():
return "Draws a widget with configurable content."
def parameters():
return [ "spacer.text: Text to draw (defaults to '')" ]
class Module(bumblebee.module.Module):
def __init__(self, output, config):
super(Module, self).__init__(output, config)
def widgets(self):
return bumblebee.output.Widget(self, self._config.parameter("text", ""))
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -1,34 +0,0 @@
from __future__ import absolute_import
import datetime
import bumblebee.module
def description():
return "Displays the current time, using the optional format string as input for strftime."
def parameters():
module = __name__.split(".")[-1]
return [
"{}.format: strftime specification (defaults to {})".format(module, default_format(module))
]
def default_format(module):
default = "%x %X"
if module == "date":
default = "%x"
if module == "time":
default = "%X"
return default
class Module(bumblebee.module.Module):
def __init__(self, output, config):
super(Module, self).__init__(output, config)
module = self.__module__.split(".")[-1]
self._fmt = self._config.parameter("format", default_format(module))
def widgets(self):
return bumblebee.output.Widget(self, datetime.datetime.now().strftime(self._fmt))
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -1,89 +0,0 @@
import bumblebee.module
import bumblebee.util
import re
import os
import sys
import subprocess
def description():
return "Shows all connected screens"
def parameters():
return [
]
class Module(bumblebee.module.Module):
def __init__(self, output, config):
super(Module, self).__init__(output, config)
self._widgets = []
def toggle(self, event, widget):
path = os.path.dirname(os.path.abspath(__file__))
toggle_cmd = "{}/../../bin/toggle-display.sh".format(path)
if widget.get("state") == "on":
bumblebee.util.execute("{} --output {} --off".format(toggle_cmd, widget.get("display")))
else:
neighbor = None
for w in self._widgets:
if w.get("state") == "on":
neighbor = w
if event.get("button") == 1:
break
if neighbor == None:
bumblebee.util.execute("{} --output {} --auto".format(toggle_cmd,
widget.get("display")))
else:
bumblebee.util.execute("{} --output {} --auto --{}-of {}".format(toggle_cmd,
widget.get("display"), "left" if event.get("button") == 1 else "right",
neighbor.get("display")))
def widgets(self):
process = subprocess.Popen([ "xrandr", "-q" ], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
output, error = process.communicate()
widgets = []
for line in output.split("\n"):
if not " connected" in line:
continue
display = line.split(" ", 2)[0]
m = re.search(r'\d+x\d+\+(\d+)\+\d+', line)
widget = bumblebee.output.Widget(self, display, instance=display)
widget.set("display", display)
# not optimal (add callback once per interval), but since
# add_callback() just returns if the callback has already
# been registered, it should be "ok"
self._output.add_callback(module=display, button=1,
cmd=self.toggle)
self._output.add_callback(module=display, button=3,
cmd=self.toggle)
if m:
widget.set("state", "on")
widget.set("pos", int(m.group(1)))
else:
widget.set("state", "off")
widget.set("pos", sys.maxint)
widgets.append(widget)
widgets.sort(key=lambda widget : widget.get("pos"))
self._widgets = widgets
return widgets
def state(self, widget):
return widget.get("state", "off")
def warning(self, widget):
return False
def critical(self, widget):
return False
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -1,121 +0,0 @@
import os
import inspect
import threading
import bumblebee.util
def output(args):
import bumblebee.outputs.i3
return bumblebee.outputs.i3.Output(args)
class Widget(object):
def __init__(self, obj, text, instance=None):
self._obj = obj
self._text = text
self._store = {}
self._instance = instance
obj._output.register_widget(self.instance(), self)
def set(self, key, value):
self._store[key] = value
def get(self, key, default=None):
return self._store.get(key, default)
def state(self):
return self._obj.state(self)
def warning(self):
return self._obj.warning(self)
def critical(self):
return self._obj.critical(self)
def module(self):
return self._obj.__module__.split(".")[-1]
def instance(self):
return self._instance if self._instance else getattr(self._obj, "instance")(self)
def text(self):
return self._text
class Command(object):
def __init__(self, command, event, widget):
self._command = command
self._event = event
self._widget = widget
def __call__(self, *args, **kwargs):
if not isinstance(self._command, list):
self._command = [ self._command ]
for cmd in self._command:
if not cmd: continue
if inspect.ismethod(cmd):
cmd(self._event, self._widget)
else:
c = cmd.format(*args, **kwargs)
bumblebee.util.execute(c, False)
class Output(object):
def __init__(self, config):
self._config = config
self._callbacks = {}
self._wait = threading.Condition()
self._wait.acquire()
self._widgets = {}
def register_widget(self, identity, widget):
self._widgets[identity] = widget
def redraw(self):
self._wait.acquire()
self._wait.notify()
self._wait.release()
def add_callback(self, cmd, button, module=None):
if module:
module = module.replace("bumblebee.modules.", "")
if self._callbacks.get((button, module)): return
self._callbacks[(
button,
module,
)] = cmd
def callback(self, event):
cb = self._callbacks.get((
event.get("button", -1),
None,
), None)
cb = self._callbacks.get((
event.get("button", -1),
event.get("instance", event.get("module", None)),
), cb)
identity = event.get("instance", event.get("module", None))
return Command(cb, event, self._widgets.get(identity, None))
def wait(self):
self._wait.wait(self._config.parameter("interval", 1))
def draw(self, widgets, theme):
if not type(widgets) is list:
widgets = [ widgets ]
self._draw(widgets, theme)
def start(self):
pass
def _draw(self, widgets, theme):
pass
def flush(self):
pass
def stop(self):
pass
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -1,76 +0,0 @@
from __future__ import unicode_literals
import os
import sys
import json
import shlex
import threading
import subprocess
import bumblebee.output
def read_input(output):
while True:
line = sys.stdin.readline().strip(",").strip()
if line == "[": continue
if line == "]": break
event = json.loads(line)
cb = output.callback(event)
if cb:
cb(
name=event.get("name", ""),
instance=event.get("instance", ""),
button=event.get("button", -1)
)
output.redraw()
class Output(bumblebee.output.Output):
def __init__(self, args):
super(Output, self).__init__(args)
self._data = []
self.add_callback("i3-msg workspace prev_on_output", 4)
self.add_callback("i3-msg workspace next_on_output", 5)
self._thread = threading.Thread(target=read_input, args=(self,))
self._thread.start()
def start(self):
print(json.dumps({ "version": 1, "click_events": True }) + "[")
def _draw(self, widgets, theme):
for widget in widgets:
if theme.separator(widget):
self._data.append({
u"full_text": theme.separator(widget),
"color": theme.separator_color(widget),
"background": theme.separator_background(widget),
"separator": False,
"separator_block_width": 0,
})
self._data.append({
u"full_text": " {} {} {}".format(
theme.prefix(widget),
widget.text(),
theme.suffix(widget)
),
"color": theme.color(widget),
"background": theme.background(widget),
"name": widget.module(),
"instance": widget.instance(),
"separator": theme.default_separators(widget, False),
"separator_block_width": theme.separator_block_width(widget, 0),
})
theme.next_widget()
def flush(self):
data = json.dumps(self._data)
self._data = []
print(data + ",")
sys.stdout.flush()
def stop(self):
return "]"
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -1,129 +0,0 @@
import os
import copy
import json
import yaml
import glob
def getpath():
return os.path.dirname("{}/../themes/".format(os.path.dirname(os.path.realpath(__file__))))
def themes():
d = getpath()
return [ os.path.basename(f).replace(".json", "") for f in glob.iglob("{}/*.json".format(d)) ]
class Theme:
def __init__(self, config):
self._config = config
self._data = self.get_theme(config.theme())
for iconset in self._data.get("icons", []):
self.merge(self._data, self.get_theme(iconset))
self._defaults = self._data.get("defaults", {})
self._cycles = self._defaults.get("cycle", [])
self.begin()
def get_theme(self, name):
for path in [ getpath(), "{}/icons/".format(getpath()) ]:
if os.path.isfile("{}/{}.yaml".format(path, name)):
with open("{}/{}.yaml".format(path, name)) as f:
return yaml.load(f)
if os.path.isfile("{}/{}.json".format(path, name)):
with open("{}/{}.json".format(path, name)) as f:
return json.load(f)
return None
# algorithm copied from
# http://blog.impressiver.com/post/31434674390/deep-merge-multiple-python-dicts
# nicely done :)
def merge(self, target, *args):
if len(args) > 1:
for item in args:
self.merge(item)
return target
item = args[0]
if not isinstance(item, dict):
return item
for key, value in item.items():
if key in target and isinstance(target[key], dict):
self.merge(target[key], value)
else:
target[key] = copy.deepcopy(value)
return target
def begin(self):
self._config.set("theme.cycleidx", 0)
self._cycle = self._cycles[0] if len(self._cycles) > 0 else {}
self._background = [ None, None ]
def next_widget(self):
self._background[1] = self._background[0]
idx = self._config.increase("theme.cycleidx", len(self._cycles), 0)
self._cycle = self._cycles[idx] if len(self._cycles) > idx else {}
def prefix(self, widget):
return self._get(widget, "prefix", "")
def suffix(self, widget):
return self._get(widget, "suffix", "")
def color(self, widget):
result = self._get(widget, "fg")
if widget.warning():
result = self._get(widget, "fg-warning")
if widget.critical():
result = self._get(widget, "fg-critical")
return result
def background(self, widget):
result = self._get(widget, "bg")
if widget.warning():
result = self._get(widget, "bg-warning")
if widget.critical():
result = self._get(widget, "bg-critical")
self._background[0] = result
return result
def separator(self, widget):
return self._get(widget, "separator")
def default_separators(self, widget, default):
return self._get(widget, "default-separators", default)
def separator_color(self, widget):
return self.background(widget)
def separator_background(self, widget):
return self._background[1]
def separator_block_width(self, widget, default):
return self._get(widget, "separator-block-width", default)
def _get(self, widget, name, default = None):
module = widget.module()
state = widget.state()
inst = widget.instance()
inst = inst.replace("{}.".format(module), "")
module_theme = self._data.get(module, {})
state_theme = module_theme.get("states", {}).get(state, {})
instance_theme = module_theme.get(inst, {})
instance_state_theme = instance_theme.get("states", {}).get(state, {})
value = None
value = self._defaults.get(name, value)
value = self._cycle.get(name, value)
value = module_theme.get(name, value)
value = state_theme.get(name, value)
value = instance_theme.get(name, value)
value = instance_state_theme.get(name, value)
if type(value) is list:
key = "{}{}".format(widget.instance(), value)
idx = self._config.increase(key, len(value), 0)
value = value[idx]
return value if value else default
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -1,33 +0,0 @@
import shlex
import subprocess
try:
from exceptions import RuntimeError
except ImportError:
# Python3 doesn't require this anymore
pass
def bytefmt(num):
for unit in [ "", "Ki", "Mi", "Gi" ]:
if num < 1024.0:
return "{:.2f}{}B".format(num, unit)
num /= 1024.0
return "{:05.2f%}{}GiB".format(num)
def durationfmt(duration):
minutes, seconds = divmod(duration, 60)
hours, minutes = divmod(minutes, 60)
res = "{:02d}:{:02d}".format(minutes, seconds)
if hours > 0: res = "{:02d}:{}".format(hours, res)
return res
def execute(cmd, wait=True):
args = shlex.split(cmd)
p = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
if wait:
out, err = p.communicate()
if p.returncode != 0:
raise RuntimeError("{} exited with {}".format(cmd, p.returncode))
return out.decode("utf-8")
return None

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 860 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 521 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

View file

View file

@ -1,26 +0,0 @@
import unittest
import bumblebee.config
import bumblebee.modules.cpu
class FakeOutput(object):
def add_callback(self, cmd, button, module=None):
pass
class TestCpuModule(unittest.TestCase):
def setUp(self):
output = FakeOutput()
config = bumblebee.config.Config(["-m", "cpu"])
self.cpu = bumblebee.modules.cpu.Module(output, config, None)
def test_documentation(self):
self.assertTrue(hasattr(bumblebee.modules.cpu, "description"))
self.assertTrue(hasattr(bumblebee.modules.cpu, "parameters"))
def test_warning(self):
self.assertTrue(hasattr(self.cpu, "warning"))
def test_critical(self):
self.assertTrue(hasattr(self.cpu, "critical"))
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -1,10 +1,18 @@
import unittest
import bumblebee.config
from bumblebee.config import Config
class TestConfigCreation(unittest.TestCase):
class TestConfig(unittest.TestCase):
def setUp(self):
pass
self.defaultConfig = Config()
self.someSimpleModules = [ "foo", "bar", "baz" ]
def test_no_modules_by_default(self):
self.assertEquals(self.defaultConfig.modules(), [])
def test_load_simple_modules(self):
cfg = Config([ "-m" ] + self.someSimpleModules)
self.assertEquals(cfg.modules(),
map(lambda x: { "name": x, "module": x }, self.someSimpleModules))
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -1,7 +0,0 @@
{
"icons": [ "ascii" ],
"defaults": {
"urgent": true,
"fg": "#aabbcc"
}
}

View file

@ -1,44 +0,0 @@
{
"icons": [ "paxy97", "awesome-fonts" ],
"defaults": {
"prefix": " ",
"suffix" : " ",
"cycle": [
{
"fg": "#ebdbb2",
"bg": "#1d2021"
},
{
"fg": "#fbf1c7",
"bg": "#282828"
}
],
"fg-critical": "#fbf1c7",
"bg-critical": "#cc241d",
"fg-warning": "#1d2021",
"bg-warning": "#d79921",
"default-separators": false,
"separator-block-width": 0
},
"dnf": {
"states": {
"good": {
"fg": "#002b36",
"bg": "#859900"
}
}
},
"battery": {
"states": {
"charged": {
"fg": "#1d2021",
"bg": "#b8bb26"
},
"AC": {
"fg": "#1d2021",
"bg": "#b8bb26"
}
}
}
}

View file

@ -1,119 +0,0 @@
{
"memory": {
"prefix": "ram"
},
"cpu": {
"prefix": "cpu"
},
"disk": {
"prefix": "hdd"
},
"dnf": {
"prefix": "dnf"
},
"brightness": {
"prefix": "o"
},
"cmus": {
"states": {
"playing": {
"prefix": ">"
},
"paused": {
"prefix": "||"
},
"stopped": {
"prefix": "[]"
}
},
"prev": {
"prefix": "|<"
},
"next": {
"prefix": ">|"
},
"shuffle": {
"states": { "on": { "prefix": "S" }, "off": { "prefix": "[s]" } }
},
"repeat": {
"states": { "on": { "prefix": "R" }, "off": { "prefix": "[r]" } }
}
},
"pasink": {
"states": {
"muted": {
"prefix": "audio(mute)"
},
"unmuted": {
"prefix": "audio"
}
}
},
"pasource": {
"states": {
"muted": {
"prefix": "mic(mute)"
},
"unmuted": {
"prefix": "mic"
}
}
},
"nic": {
"states": {
"wireless-up": {
"prefix": "wifi"
},
"wireless-down": {
"prefix": "wifi"
},
"wired-up": {
"prefix": "lan"
},
"wired-down": {
"prefix": "lan"
},
"tunnel-up": {
"prefix": "tun"
},
"tunnel-down": {
"prefix": "tun"
}
}
},
"battery": {
"states": {
"charged": {
"suffix": "full"
},
"charging": {
"suffix": "chr"
},
"AC": {
"suffix": "ac"
},
"discharging-10": {
"prefix": "!",
"suffix": "dis"
},
"discharging-25": {
"suffix": "dis"
},
"discharging-50": {
"suffix": "dis"
},
"discharging-80": {
"suffix": "dis"
},
"discharging-100": {
"suffix": "dis"
}
}
},
"caffeine": {
"states": { "activated": {"prefix": "caf-on" }, "deactivated": { "prefix": "caf-off " } }
},
"xrandr": {
"states": { "on": { "prefix": " off "}, "off": { "prefix": " on "} }
}
}

View file

@ -1,134 +0,0 @@
{
"defaults": {
"separator": ""
},
"date": {
"prefix": ""
},
"time": {
"prefix": ""
},
"memory": {
"prefix": ""
},
"cpu": {
"prefix": ""
},
"disk": {
"prefix": ""
},
"dnf": {
"prefix": ""
},
"brightness": {
"prefix": ""
},
"cmus": {
"states": {
"playing": {
"prefix": ""
},
"paused": {
"prefix": ""
},
"stopped": {
"prefix": ""
}
},
"prev": {
"prefix": ""
},
"next": {
"prefix": ""
},
"shuffle": {
"states": { "on": { "prefix": "" }, "off": { "prefix": "" } }
},
"repeat": {
"states": { "on": { "prefix": "" }, "off": { "prefix": "" } }
}
},
"pasink": {
"states": {
"muted": {
"prefix": ""
},
"unmuted": {
"prefix": ""
}
}
},
"pasource": {
"states": {
"muted": {
"prefix": ""
},
"unmuted": {
"prefix": ""
}
}
},
"nic": {
"states": {
"wireless-up": {
"prefix": ""
},
"wireless-down": {
"prefix": ""
},
"wired-up": {
"prefix": ""
},
"wired-down": {
"prefix": ""
},
"tunnel-up": {
"prefix": ""
},
"tunnel-down": {
"prefix": ""
}
}
},
"battery": {
"states": {
"charged": {
"prefix": "",
"suffix": ""
},
"AC": {
"suffix": ""
},
"charging": {
"prefix": [ "", "", "", "", "" ],
"suffix": ""
},
"discharging-10": {
"prefix": "",
"suffix": ""
},
"discharging-25": {
"prefix": "",
"suffix": ""
},
"discharging-50": {
"prefix": "",
"suffix": ""
},
"discharging-80": {
"prefix": "",
"suffix": ""
},
"discharging-100": {
"prefix": "",
"suffix": ""
}
}
},
"caffeine": {
"states": { "activated": {"prefix": " " }, "deactivated": { "prefix": " " } }
},
"xrandr": {
"states": { "on": { "prefix": " "}, "off": { "prefix": " "} }
}
}

View file

@ -1,5 +0,0 @@
{
"memory": {
"prefix": "  "
}
}

View file

@ -1,41 +0,0 @@
{
"icons": [ "awesome-fonts" ],
"defaults": {
"cycle": [
{
"fg": "#ffd700",
"bg": "#d75f00"
},
{
"fg": "#ffffff",
"bg": "#0087af"
}
],
"fg-critical": "#ffffff",
"bg-critical": "#ff0000",
"fg-warning": "#d75f00",
"bg-warning": "#ffd700",
"default_separators": false
},
"dnf": {
"states": {
"good": {
"fg": "#494949",
"bg": "#41db00"
}
}
},
"battery": {
"states": {
"charged": {
"fg": "#494949",
"bg": "#41db00"
},
"AC": {
"fg": "#494949",
"bg": "#41db00"
}
}
}
}

View file

@ -1,42 +0,0 @@
{
"icons": [ "awesome-fonts" ],
"defaults": {
"cycle": [
{
"fg": "#93a1a1",
"bg": "#002b36"
},
{
"fg": "#eee8d5",
"bg": "#586e75"
}
],
"fg-critical": "#002b36",
"bg-critical": "#dc322f",
"fg-warning": "#002b36",
"bg-warning": "#b58900",
"default-separators": false,
"separator-block-width": 0
},
"dnf": {
"states": {
"good": {
"fg": "#002b36",
"bg": "#859900"
}
}
},
"battery": {
"states": {
"charged": {
"fg": "#002b36",
"bg": "#859900"
},
"AC": {
"fg": "#002b36",
"bg": "#859900"
}
}
}
}

View file

@ -1,41 +0,0 @@
{
"icons": [ "ascii" ],
"defaults": {
"cycle": [
{
"fg": "#93a1a1",
"bg": "#002b36"
},
{
"fg": "#eee8d5",
"bg": "#586e75"
}
],
"fg-critical": "#002b36",
"bg-critical": "#dc322f",
"fg-warning": "#002b36",
"bg-warning": "#b58900",
"default_separators": false,
"separator": ""
},
"dnf": {
"states": {
"good": {
"fg": "#002b36",
"bg": "#859900"
}
}
},
"battery": {
"states": {
"charged": {
"fg": "#002b36",
"bg": "#859900"
},
"AC": {
"fg": "#002b36",
"bg": "#859900"
}
}
}