Compare commits

...

10 commits

14 changed files with 174 additions and 111 deletions

1
.gitignore vendored
View file

@ -1,3 +1,4 @@
plugins/custom*.py
*.iml *.iml
*.swp *.swp
*.wpr *.wpr

View file

@ -1,4 +1,14 @@
# Typical plugin properties:
# - name: plugin name (always required, all other properties are optional)
# note that a plugin can be used more than once (i.e. different
# timezones in the date plugin)
# - title: title representation on the panel
# - hide_ok: false to force display value even if it is below problem threshold
# - problem: a threshold value to make a value urgent and / or display
# the value on the panel
output_format: i3 output_format: i3
plugins: plugins:
- name: ping - name: ping
title: NET title: NET
@ -6,15 +16,27 @@ plugins:
- de-ber-as20647.anchors.atlas.ripe.net - de-ber-as20647.anchors.atlas.ripe.net
- nl-ams-as1101.anchors.atlas.ripe.net - nl-ams-as1101.anchors.atlas.ripe.net
- uk-boh-as196745.anchors.atlas.ripe.net - uk-boh-as196745.anchors.atlas.ripe.net
hide_ok: false
- name: disk - name: disk
partition: / partition: /
problem: 80 problem: 80
hide_ok: false hide_ok: false
- name: disk - name: disk
partition: /home partition: /home
problem: 90 problem: 90
- name: pacman
- name: mem - name: mem
- name: load - name: load
- name: batt
- name: date - name: date
format: '%a %d %H:%M' format: '%a %d %H:%M'
- name: date
title: UTC
tz: UTC
format: '%H:%M'

View file

@ -2,30 +2,33 @@ import threading
import time import time
def parse_config(config, defaults): PLUGIN_DEFAULTS = {'freq': 1, 'hide_ok': True}
result = dict()
for key in defaults:
result[key] = config[key] if key in config else defaults[key]
return result
class PluginThreadCommon: class PluginThreadCommon:
def __init__(self, config, defaults=dict()): def __init__(self, config, defaults=None):
if 'freq' not in defaults:
defaults['freq'] = 1
if 'hide_ok' not in defaults:
defaults['hide_ok'] = True
self.conf = parse_config(config, defaults)
self.status = dict() self.status = dict()
self.conf = dict()
self.conf.update(PLUGIN_DEFAULTS)
if defaults:
self.conf.update(defaults)
self.conf.update(config)
self.hide = False self.hide = False
self.thread = threading.Thread(target=self.run) self.thread = threading.Thread(target=self.run)
self.thread.daemon = True self.thread.daemon = True
def format_status(self, status, urgent=False):
if 'title' in self.conf and self.conf['title']:
full_text = '{}: {}'.format(self.conf['title'], status)
else:
full_text = status
self.status.update({'full_text': full_text, 'urgent': urgent})
def start(self): def start(self):
self.thread.start() self.thread.start()
def main(self): def main(self):
self.status['full_text'] = 'placeholder' pass
def run(self): def run(self):
while True: while True:

View file

@ -1,29 +1,31 @@
import plugins import plugins
BATTERY_DIR = '/sys/class/power_supply/BAT0/' BATT_DEFAULTS = {
'title': 'BAT',
'problem': 15,
'symbol_charging': '\u2191',
'symbol_discharging': '\u2193'
}
class PluginThread(plugins.PluginThreadCommon): class PluginThread(plugins.PluginThreadCommon):
def __init__(self, config): def __init__(self, config):
defaults = { super(PluginThread, self).__init__(config, BATT_DEFAULTS)
'problem': 15,
'symbol_charging': '\u2191',
'symbol_discharging': '\u2193'
}
super(PluginThread, self).__init__(config, defaults)
def main(self): def main(self):
with \ with \
open(BATTERY_DIR + 'capacity', 'r') as batt_capacity, \ open('/sys/class/power_supply/BAT0/capacity', 'r') as bcap, \
open(BATTERY_DIR + 'status', 'r') as batt_status: open('/sys/class/power_supply/BAT0/status', 'r') as bstat:
status = batt_status.read().strip() capacity = bcap.readline().strip()
capacity = batt_capacity.read().strip() status_value = bstat.readline().strip()
if status != 'Discharging':
if status_value != 'Discharging':
symbol = self.conf['symbol_charging'] symbol = self.conf['symbol_charging']
self.status['urgent'] = False urgent = False
else: else:
symbol = self.conf['symbol_discharging'] symbol = self.conf['symbol_discharging']
self.status['urgent'] = float(capacity) <= self.conf['problem'] urgent = float(capacity) <= self.conf['problem']
self.status['full_text'] = 'BAT: ' + capacity + '% ' + symbol status = '{}% {}'.format(capacity, symbol)
self.format_status(status, urgent)

23
plugins/cmd.py Normal file
View file

@ -0,0 +1,23 @@
import plugins
import subprocess
PACMAN_DEFAULTS = {
'cmd': ('/usr/bin/echo', 'I am cmd'),
'title': 'CMD', 'freq': 15
}
class PluginThread(plugins.PluginThreadCommon):
def __init__(self, config):
super(PluginThread, self).__init__(config, PACMAN_DEFAULTS)
def main(self):
proc = subprocess.Popen(
self.conf['cmd'], stdout=subprocess.PIPE,
stderr=subprocess.DEVNULL, stdin=subprocess.DEVNULL,
encoding='UTF-8'
)
out = proc.communicate()[0].strip().splitlines()[0]
self.format_status(out)

View file

@ -2,15 +2,19 @@ import datetime
import plugins import plugins
DATE_DEFAULTS = {'format': '%c'}
class PluginThread(plugins.PluginThreadCommon): class PluginThread(plugins.PluginThreadCommon):
def __init__(self, config): def __init__(self, config):
defaults = {'format': '%c', 'tz': None} super(PluginThread, self).__init__(config, DATE_DEFAULTS)
super(PluginThread, self).__init__(config, defaults)
self.timezone = None self.timezone = None
if self.conf['tz']: if 'tz' in self.conf:
import pytz import pytz
self.timezone = pytz.timezone(self.conf['tz']) self.timezone = pytz.timezone(self.conf['tz'])
def main(self): def main(self):
now = datetime.datetime.now(tz=self.timezone) now = datetime.datetime\
self.status['full_text'] = now.strftime(self.conf['format']) .now(tz=self.timezone)\
.strftime(self.conf['format'])
self.format_status(now)

View file

@ -2,10 +2,14 @@ import plugins
import psutil import psutil
DISK_DEFAULTS = {'partition': '/', 'problem': 80, 'freq': 15}
class PluginThread(plugins.PluginThreadCommon): class PluginThread(plugins.PluginThreadCommon):
def __init__(self, config): def __init__(self, config):
defaults = {'partition': '/', 'problem': 80, 'freq': 15} super(PluginThread, self).__init__(config, DISK_DEFAULTS)
super(PluginThread, self).__init__(config, defaults) if 'title' not in self.conf:
self.conf['title'] = self.conf['partition']
def main(self): def main(self):
du_stat = psutil.disk_usage(self.conf['partition']) du_stat = psutil.disk_usage(self.conf['partition'])
@ -15,6 +19,6 @@ class PluginThread(plugins.PluginThreadCommon):
else: else:
self.hide = True self.hide = True
self.status['urgent'] = False self.status['urgent'] = False
du_free = str(round(du_stat.free / 2**30, 2))
disk_usage = self.conf['partition'] + ': ' + du_free + 'G' status = '{:.2f}G'.format(du_stat.free / 2**30)
self.status['full_text'] = disk_usage self.format_status(status)

24
plugins/filecat.py Normal file
View file

@ -0,0 +1,24 @@
import plugins
FILECAT_DEFAULTS = {
'filename': '/etc/hostname', 'title': 'CAT',
'freq': 60, 'nofile': 'unavailable'
}
class PluginThread(plugins.PluginThreadCommon):
def __init__(self, config):
super(PluginThread, self).__init__(config, FILECAT_DEFAULTS)
self.hide = False
def main(self):
try:
with open(self.conf['filename'], 'r') as datafile:
contents = datafile.read().strip()
urgent = False
except FileNotFoundError:
contents = self.conf['nofile']
urgent = True
self.format_status(contents, urgent)

View file

@ -3,27 +3,30 @@ import requests
import time import time
URI = 'http://fucking-great-advice.ru/api/random' FORTUNE_DEFAULTS = {
'uri': 'http://fucking-great-advice.ru/api/random',
'freq': 120, 'retry': 3
}
class PluginThread(plugins.PluginThreadCommon): class PluginThread(plugins.PluginThreadCommon):
def __init__(self, config): def __init__(self, config):
defaults = {'freq': 120, 'retry': 3} super(PluginThread, self).__init__(config, FORTUNE_DEFAULTS)
super(PluginThread, self).__init__(config, defaults)
self.retry = False self.retry = False
def main(self): def main(self):
try: try:
req = requests.get(URI, timeout=2) req = requests.get(self.conf['uri'], timeout=2)
advice = req.json()['text'] if req.status_code == 200 else 'N/A' fortune = req.json()['text'] if req.status_code == 200 else 'N/A'
self.retry = False self.retry = False
except requests.exceptions.Timeout: except requests.exceptions.Timeout:
advice = 'N/A (timeout)' fortune = 'N/A (timeout)'
self.retry = True self.retry = True
except requests.exceptions.ConnectionError: except requests.exceptions.ConnectionError:
advice = 'N/A (offline)' fortune = 'N/A (offline)'
self.retry = True self.retry = True
self.status['full_text'] = advice
self.format_status(fortune)
def run(self): def run(self):
while True: while True:

View file

@ -2,17 +2,21 @@ import os
import plugins import plugins
LOAD_DEFAULTS = {'title': 'LOAD', 'freq': 20, 'problem': 1}
class PluginThread(plugins.PluginThreadCommon): class PluginThread(plugins.PluginThreadCommon):
def __init__(self, config): def __init__(self, config):
defaults = {'freq': 20, 'problem': 1} super(PluginThread, self).__init__(config, LOAD_DEFAULTS)
super(PluginThread, self).__init__(config, defaults)
def main(self): def main(self):
loads = os.getloadavg() loads = os.getloadavg()
if loads[0] >= self.conf['problem']: if loads[0] >= self.conf['problem']:
self.hide = False self.hide = False
self.status['urgent'] = True urgent = True
else: else:
self.hide = True self.hide = True
self.status['urgent'] = False urgent = False
self.status['full_text'] = 'LA: {:.2f} {:.2f} {:.2f}'.format(*loads) status = '{:.2f} {:.2f} {:.2f}'.format(*loads)
self.format_status(status, urgent)

View file

@ -2,18 +2,21 @@ import psutil
import plugins import plugins
MEM_DEFAULTS = {'title': 'RAM', 'problem': 85}
class PluginThread(plugins.PluginThreadCommon): class PluginThread(plugins.PluginThreadCommon):
def __init__(self, config): def __init__(self, config):
defaults = {'problem': 85} super(PluginThread, self).__init__(config, MEM_DEFAULTS)
super(PluginThread, self).__init__(config, defaults)
def main(self): def main(self):
mem_stat = psutil.virtual_memory() mem_stat = psutil.virtual_memory()
if mem_stat.percent > self.conf['problem']: if mem_stat.percent > self.conf['problem']:
self.hide = False self.hide = False
self.status['urgent'] = True urgent = True
else: else:
self.hide = True self.hide = True
self.status['urgent'] = False urgent = False
mem_available = round(mem_stat.available / 2**30, 2)
self.status['full_text'] = 'RAM: {:.2f}G'.format(mem_available) status = '{:.2f}G'.format(mem_stat.available / 2**30)
self.format_status(status, urgent)

View file

@ -1,27 +0,0 @@
import plugins
import subprocess
class PluginThread(plugins.PluginThreadCommon):
def __init__(self, config):
defaults = {
'freq': 15,
'problem': 10
}
super(PluginThread, self).__init__(config, defaults)
self.format_status(0)
def format_status(self, count):
self.hide = count == 0
self.status['urgent'] = count >= self.conf['problem']
self.status['full_text'] = 'UPD: ' + str(count)
def main(self):
pacman_qu = subprocess.Popen(
('/usr/bin/pacman', '-Qu'), stdout=subprocess.PIPE,
stderr=subprocess.DEVNULL, stdin=subprocess.DEVNULL,
encoding='UTF-8'
)
out = pacman_qu.communicate()[0].strip().splitlines()
packages = [pkg for pkg in out if not '[ignored]' in pkg]
self.format_status(len(packages))

View file

@ -1,29 +1,26 @@
import os import os
import subprocess
import random import random
import plugins import plugins
PING_DEFAULTS = {
'hosts': tuple(), 'title': 'PING', 'timeout': 150
}
class PluginThread(plugins.PluginThreadCommon): class PluginThread(plugins.PluginThreadCommon):
def __init__(self, config): def __init__(self, config):
defaults = {'hosts': list(), 'title': 'PING', 'timeout': 150} super(PluginThread, self).__init__(config, PING_DEFAULTS)
super(PluginThread, self).__init__(config, defaults) self.ping_cmd = ('fping', '-c1', '-qt' + str(self.conf['timeout']))
self.format_status('n/a')
def format_status(self, state):
self.status['full_text'] = self.conf['title'] + ': ' + state
if state == 'on':
self.status['urgent'] = False
self.hide = True
else:
self.status['urgent'] = True
def main(self): def main(self):
random.shuffle(self.conf['hosts']) host = random.choice(self.conf['hosts'])
for host in self.conf['hosts']: fping = subprocess.run(
fping = 'fping -qc1t' + str(self.conf['timeout'])\ (*self.ping_cmd, host),
+ ' ' + host + ' &>/dev/null' stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL
response = os.system(fping) )
if response == 0: if fping.returncode == 0:
self.format_status('on') self.format_status('up')
break else:
self.format_status('off') self.format_status('down', True)

View file

@ -6,7 +6,6 @@ import os
import sys import sys
import time import time
import yaml import yaml
import plugins
DEFAULT_CONFIG = os.path.join(os.environ['HOME'], '.config/vdstatus/conf.yaml') DEFAULT_CONFIG = os.path.join(os.environ['HOME'], '.config/vdstatus/conf.yaml')
@ -28,10 +27,10 @@ def parse_arguments():
class PluginRunner: class PluginRunner:
def __init__(self, config_file=DEFAULT_CONFIG): def __init__(self, config_file=DEFAULT_CONFIG):
config = dict() self.conf = dict()
self.conf.update(DEFAULTS)
with open(config_file) as config_data: with open(config_file) as config_data:
config = yaml.load(config_data) self.conf.update(yaml.safe_load(config_data))
self.conf = plugins.parse_config(config, DEFAULTS)
self.plugins_loaded = list() self.plugins_loaded = list()
self.format_output = self.format_term self.format_output = self.format_term
for plugin in self.conf['plugins']: for plugin in self.conf['plugins']:
@ -50,8 +49,9 @@ class PluginRunner:
outputs = list() outputs = list()
for plugin in self.plugins_loaded: for plugin in self.plugins_loaded:
if \ if \
not plugin.conf['hide_ok'] or \ 'full_text' in plugin.status and (
not plugin.hide: not plugin.conf['hide_ok'] or not plugin.hide
):
outputs.append(plugin.status) outputs.append(plugin.status)
print(self.format_output(outputs), flush=True) print(self.format_output(outputs), flush=True)