Compare commits
10 commits
de6888a5d2
...
8e1cea8a01
Author | SHA1 | Date | |
---|---|---|---|
|
8e1cea8a01 | ||
|
94502d2d61 | ||
|
b03a588182 | ||
|
d824345ba1 | ||
|
bd549b0cc9 | ||
|
9902696f1f | ||
|
c3700821af | ||
|
5e6519a51e | ||
|
151c94c5f3 | ||
|
7459d29c68 |
14 changed files with 174 additions and 111 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1,3 +1,4 @@
|
||||||
|
plugins/custom*.py
|
||||||
*.iml
|
*.iml
|
||||||
*.swp
|
*.swp
|
||||||
*.wpr
|
*.wpr
|
||||||
|
|
24
conf.yaml
24
conf.yaml
|
@ -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'
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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
23
plugins/cmd.py
Normal 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)
|
|
@ -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)
|
||||||
|
|
|
@ -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
24
plugins/filecat.py
Normal 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)
|
|
@ -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:
|
|
@ -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)
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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))
|
|
|
@ -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)
|
||||||
|
|
12
vdstatus
12
vdstatus
|
@ -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)
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue