[formatting] reformat using "black -t py34"

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

File diff suppressed because it is too large Load diff

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -27,27 +27,30 @@ import core.module
import core.widget
import core.input
def get_frame(url):
img_bytes = b""
stream = urllib.request.urlopen(url)
img_bytes = b""
stream = urllib.request.urlopen(url)
while True:
img_bytes += stream.read(1024)
a = img_bytes.find(b'\xff\xd8')
b = img_bytes.find(b'\xff\xd9')
a = img_bytes.find(b"\xff\xd8")
b = img_bytes.find(b"\xff\xd9")
if a != -1 and b != -1:
jpg = img_bytes[a:b+2]
img_bytes = img_bytes[b+2:]
jpg = img_bytes[a : b + 2]
img_bytes = img_bytes[b + 2 :]
img = Image.open(BytesIO(jpg))
return img
return None
class WebcamImagesWorker(threading.Thread):
def __init__(self, url, queue):
threading.Thread.__init__(self)
self.__url = url
self.__queue = queue
self.__running = True
def run(self):
while self.__running:
img = get_frame(self.__url)
@ -56,6 +59,7 @@ class WebcamImagesWorker(threading.Thread):
def stop(self):
self.__running = False
class Module(core.module.Module):
@core.decorators.every(seconds=5)
def __init__(self, config, theme):
@ -69,34 +73,44 @@ class Module(core.module.Module):
self.__webcam_images_worker = None
self.__webcam_image_url = self.__octoprint_address + "/webcam/?action=stream"
self.__webcam_images_queue = None
self.__printer_bed_temperature = "-"
self.__tool1_temperature = "-"
self.update_status()
core.input.register(self, button=core.input.LEFT_MOUSE,
cmd=self.__show_popup)
core.input.register(self, button=core.input.LEFT_MOUSE, cmd=self.__show_popup)
def octoprint_status(self, widget):
if self.__octoprint_state == "Offline" or self.__octoprint_state == "Unknown":
return self.__octoprint_state
return self.__octoprint_state + " | B: " + str(self.__printer_bed_temperature) + "°C" + " | T1: " + str(self.__tool1_temperature) + "°C"
return (
self.__octoprint_state
+ " | B: "
+ str(self.__printer_bed_temperature)
+ "°C"
+ " | T1: "
+ str(self.__tool1_temperature)
+ "°C"
)
def __get(self, endpoint):
url = self.__octoprint_address + "/api/" + endpoint
headers = {"X-Api-Key": self.__octoprint_api_token}
resp = requests.get(url, headers=headers)
try:
return resp.json(), resp.status_code
except simplejson.errors.JSONDecodeError:
return None, resp.status_code
def __get_printer_bed_temperature(self):
printer_info, status_code = self.__get("printer")
if status_code == 200:
return printer_info["temperature"]["bed"]["actual"], printer_info["temperature"]["bed"]["target"]
return (
printer_info["temperature"]["bed"]["actual"],
printer_info["temperature"]["bed"]["target"],
)
return None, None
def __get_octoprint_state(self):
@ -109,7 +123,7 @@ class Module(core.module.Module):
printer_info, status_code = self.__get("printer")
if status_code == 200:
temperatures = printer_info["temperature"]
tool_id = 0
while True:
try:
@ -122,9 +136,9 @@ class Module(core.module.Module):
def update_status(self):
try:
self.__octoprint_state = self.__get_octoprint_state()
self.__octoprint_state = self.__get_octoprint_state()
actual_temp, _ = self.__get_printer_bed_temperature()
actual_temp, _ = self.__get_printer_bed_temperature()
if actual_temp is None:
actual_temp = "-"
self.__printer_bed_temperature = str(actual_temp)
@ -149,17 +163,18 @@ class Module(core.module.Module):
root.after(5, self.__refresh_image, root, webcam_image, webcam_image_container)
def __refresh_temperatures(self, root, printer_bed_temperature_label, tools_temperature_label):
def __refresh_temperatures(
self, root, printer_bed_temperature_label, tools_temperature_label
):
actual_bed_temp, target_bed_temp = self.__get_printer_bed_temperature()
if actual_bed_temp is None:
actual_bed_temp = "-"
if target_bed_temp is None:
target_bed_temp = "-"
bed_temp = "Bed: " + str(actual_bed_temp) + "/" + str(target_bed_temp) + " °C"
bed_temp = "Bed: " + str(actual_bed_temp) + "/" + str(target_bed_temp) + " °C"
printer_bed_temperature_label.config(text=bed_temp)
tool_temperatures = self.__get_tool_temperatures()
tools_temp = "Tools: "
@ -167,48 +182,68 @@ class Module(core.module.Module):
tools_temp += "-/- °C"
else:
for i, tool_temperature in enumerate(tool_temperatures):
tools_temp += str(tool_temperature[0]) + "/" + str(tool_temperature[1]) + "°C"
if i != len(tool_temperatures)-1:
tools_temp += (
str(tool_temperature[0]) + "/" + str(tool_temperature[1]) + "°C"
)
if i != len(tool_temperatures) - 1:
tools_temp += "\t"
tools_temperature_label.config(text=tools_temp)
root.after(500, self.__refresh_temperatures, root, printer_bed_temperature_label, tools_temperature_label)
root.after(
500,
self.__refresh_temperatures,
root,
printer_bed_temperature_label,
tools_temperature_label,
)
def __show_popup(self, widget):
root = tk.Tk()
root.attributes('-type', 'dialog')
root.attributes("-type", "dialog")
root.title("Octoprint")
frame = tk.Frame(root)
frame = tk.Frame(root)
if self.__octoprint_webcam:
#load first image synchronous before popup is shown, otherwise tkinter isn't able to layout popup properly
# load first image synchronous before popup is shown, otherwise tkinter isn't able to layout popup properly
img = get_frame(self.__webcam_image_url)
webcam_image = ImageTk.PhotoImage(img)
webcam_image_container = tk.Button(frame, image=webcam_image)
webcam_image_container = tk.Button(frame, image=webcam_image)
webcam_image_container.pack()
self.__webcam_images_queue = queue.Queue()
self.__webcam_images_worker = WebcamImagesWorker(self.__webcam_image_url, self.__webcam_images_queue)
self.__webcam_images_worker = WebcamImagesWorker(
self.__webcam_image_url, self.__webcam_images_queue
)
self.__webcam_images_worker.start()
else:
logging.debug("Not using webcam, as webcam is disabled. Enable with --webcam.")
logging.debug(
"Not using webcam, as webcam is disabled. Enable with --webcam."
)
frame.pack()
temperatures_label = tk.Label(frame, text="Temperatures", font=('', 25))
temperatures_label = tk.Label(frame, text="Temperatures", font=("", 25))
temperatures_label.pack()
printer_bed_temperature_label = tk.Label(frame, text="Bed: -/- °C", font=('', 15))
printer_bed_temperature_label.pack()
tools_temperature_label = tk.Label(frame, text="Tools: -/- °C", font=('', 15))
tools_temperature_label.pack()
printer_bed_temperature_label = tk.Label(
frame, text="Bed: -/- °C", font=("", 15)
)
printer_bed_temperature_label.pack()
tools_temperature_label = tk.Label(frame, text="Tools: -/- °C", font=("", 15))
tools_temperature_label.pack()
root.after(10, self.__refresh_image, root, webcam_image, webcam_image_container)
root.after(500, self.__refresh_temperatures, root, printer_bed_temperature_label, tools_temperature_label)
root.after(
500,
self.__refresh_temperatures,
root,
printer_bed_temperature_label,
tools_temperature_label,
)
root.bind("<Destroy>", self.__on_close_popup)
root.eval('tk::PlaceWindow . center')
root.eval("tk::PlaceWindow . center")
root.mainloop()
def __on_close_popup(self, event):
@ -219,6 +254,7 @@ class Module(core.module.Module):
self.update_status()
def state(self, widget):
return []
return []
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -12,24 +12,27 @@ import core.module
import core.widget
import core.input
class Module(core.module.Module):
def __init__(self, config, theme):
super().__init__(config, theme, core.widget.Widget(self.output))
self.__doc = os.path.expanduser(self.parameter('file', '~/Documents/todo.txt'))
self.__doc = os.path.expanduser(self.parameter("file", "~/Documents/todo.txt"))
self.__todos = self.count_items()
core.input.register(self, button=core.input.LEFT_MOUSE, cmd='xdg-open {}'.format(self.__doc))
core.input.register(
self, button=core.input.LEFT_MOUSE, cmd="xdg-open {}".format(self.__doc)
)
def output(self, widget):
return str(self.__todos)
return str(self.__todos)
def update(self):
self.__todos = self.count_items()
self.__todos = self.count_items()
def state(self, widgets):
if self.__todos == 0:
return 'empty'
return 'items'
return "empty"
return "items"
def count_items(self):
try:
@ -37,8 +40,9 @@ class Module(core.module.Module):
with open(self.__doc) as f:
for i, l in enumerate(f):
pass
return i+1
return i + 1
except Exception:
return 0
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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