[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
82
README.md
|
@ -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)
|
|
|
@ -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
|
|
|
@ -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
|
|
|
@ -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
|
@ -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
|
|
|
@ -1,123 +1,19 @@
|
||||||
import os
|
|
||||||
import argparse
|
import argparse
|
||||||
import textwrap
|
|
||||||
|
|
||||||
import bumblebee.theme
|
MODULE_HELP = ""
|
||||||
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)
|
|
||||||
|
|
||||||
class Config(object):
|
class Config(object):
|
||||||
def __init__(self, args):
|
def __init__(self, args = []):
|
||||||
self._parser = self._parser()
|
parser = self._create_parser()
|
||||||
self._store = {}
|
self._args = parser.parse_args(args)
|
||||||
|
|
||||||
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 modules(self):
|
def modules(self):
|
||||||
result = []
|
return map(lambda x: { "name": x, "module": x }, self._args.modules)
|
||||||
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
|
|
||||||
|
|
||||||
def _parser(self):
|
def _create_parser(self):
|
||||||
parser = argparse.ArgumentParser(description="display system data in the i3bar")
|
parser = argparse.ArgumentParser(description="display system data in the i3bar")
|
||||||
parser.add_argument("-m", "--modules", nargs="+",
|
parser.add_argument("-m", "--modules", nargs="+", default = [],
|
||||||
help="List of modules to load. The order of the list determines "
|
help = MODULE_HELP)
|
||||||
"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",
|
|
||||||
)
|
|
||||||
|
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
|
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
|
||||||
|
|
|
@ -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
|
|
|
@ -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
|
|
|
@ -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
|
|
|
@ -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
|
|
|
@ -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"
|
|
||||||
|
|
|
@ -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
|
|
|
@ -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
|
|
|
@ -1 +0,0 @@
|
||||||
datetime.py
|
|
|
@ -1 +0,0 @@
|
||||||
time.py
|
|
|
@ -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
|
|
|
@ -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
|
|
|
@ -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
|
|
|
@ -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
|
|
|
@ -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
|
|
|
@ -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
|
|
|
@ -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
|
|
|
@ -1 +0,0 @@
|
||||||
pulseaudio.py
|
|
|
@ -1 +0,0 @@
|
||||||
pulseaudio.py
|
|
|
@ -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
|
|
|
@ -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
|
|
|
@ -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
|
|
|
@ -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
|
|
|
@ -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
|
|
|
@ -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
|
|
|
@ -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
|
|
|
@ -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
|
|
|
@ -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
|
|
Before Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 4.9 KiB |
Before Width: | Height: | Size: 1.8 KiB |
Before Width: | Height: | Size: 2.8 KiB |
Before Width: | Height: | Size: 4.8 KiB |
Before Width: | Height: | Size: 3.6 KiB |
Before Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 2 KiB |
Before Width: | Height: | Size: 3.5 KiB |
Before Width: | Height: | Size: 2.4 KiB |
Before Width: | Height: | Size: 860 B |
Before Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 2.4 KiB |
Before Width: | Height: | Size: 521 B |
Before Width: | Height: | Size: 7.1 KiB |
Before Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 10 KiB |
Before Width: | Height: | Size: 9.4 KiB |
Before Width: | Height: | Size: 2.4 KiB |
Before Width: | Height: | Size: 2.6 KiB |
|
@ -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
|
|
|
@ -1,10 +1,18 @@
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
import bumblebee.config
|
from bumblebee.config import Config
|
||||||
|
|
||||||
class TestConfigCreation(unittest.TestCase):
|
class TestConfig(unittest.TestCase):
|
||||||
def setUp(self):
|
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
|
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
|
||||||
|
|
|
@ -1,7 +0,0 @@
|
||||||
{
|
|
||||||
"icons": [ "ascii" ],
|
|
||||||
"defaults": {
|
|
||||||
"urgent": true,
|
|
||||||
"fg": "#aabbcc"
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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 "} }
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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": " "} }
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,5 +0,0 @@
|
||||||
{
|
|
||||||
"memory": {
|
|
||||||
"prefix": " "
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|