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
|
||||
*.swp
|
||||
*.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
|
||||
|
||||
plugins:
|
||||
- name: ping
|
||||
title: NET
|
||||
|
@ -6,15 +16,27 @@ plugins:
|
|||
- de-ber-as20647.anchors.atlas.ripe.net
|
||||
- nl-ams-as1101.anchors.atlas.ripe.net
|
||||
- uk-boh-as196745.anchors.atlas.ripe.net
|
||||
hide_ok: false
|
||||
|
||||
- name: disk
|
||||
partition: /
|
||||
problem: 80
|
||||
hide_ok: false
|
||||
|
||||
- name: disk
|
||||
partition: /home
|
||||
problem: 90
|
||||
- name: pacman
|
||||
|
||||
- name: mem
|
||||
|
||||
- name: load
|
||||
|
||||
- name: batt
|
||||
|
||||
- name: date
|
||||
format: '%a %d %H:%M'
|
||||
|
||||
- name: date
|
||||
title: UTC
|
||||
tz: UTC
|
||||
format: '%H:%M'
|
||||
|
|
|
@ -2,30 +2,33 @@ import threading
|
|||
import time
|
||||
|
||||
|
||||
def parse_config(config, defaults):
|
||||
result = dict()
|
||||
for key in defaults:
|
||||
result[key] = config[key] if key in config else defaults[key]
|
||||
return result
|
||||
PLUGIN_DEFAULTS = {'freq': 1, 'hide_ok': True}
|
||||
|
||||
|
||||
class PluginThreadCommon:
|
||||
def __init__(self, config, defaults=dict()):
|
||||
if 'freq' not in defaults:
|
||||
defaults['freq'] = 1
|
||||
if 'hide_ok' not in defaults:
|
||||
defaults['hide_ok'] = True
|
||||
self.conf = parse_config(config, defaults)
|
||||
def __init__(self, config, defaults=None):
|
||||
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.thread = threading.Thread(target=self.run)
|
||||
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):
|
||||
self.thread.start()
|
||||
|
||||
def main(self):
|
||||
self.status['full_text'] = 'placeholder'
|
||||
pass
|
||||
|
||||
def run(self):
|
||||
while True:
|
||||
|
|
|
@ -1,29 +1,31 @@
|
|||
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):
|
||||
def __init__(self, config):
|
||||
defaults = {
|
||||
'problem': 15,
|
||||
'symbol_charging': '\u2191',
|
||||
'symbol_discharging': '\u2193'
|
||||
}
|
||||
super(PluginThread, self).__init__(config, defaults)
|
||||
super(PluginThread, self).__init__(config, BATT_DEFAULTS)
|
||||
|
||||
def main(self):
|
||||
with \
|
||||
open(BATTERY_DIR + 'capacity', 'r') as batt_capacity, \
|
||||
open(BATTERY_DIR + 'status', 'r') as batt_status:
|
||||
status = batt_status.read().strip()
|
||||
capacity = batt_capacity.read().strip()
|
||||
if status != 'Discharging':
|
||||
open('/sys/class/power_supply/BAT0/capacity', 'r') as bcap, \
|
||||
open('/sys/class/power_supply/BAT0/status', 'r') as bstat:
|
||||
capacity = bcap.readline().strip()
|
||||
status_value = bstat.readline().strip()
|
||||
|
||||
if status_value != 'Discharging':
|
||||
symbol = self.conf['symbol_charging']
|
||||
self.status['urgent'] = False
|
||||
urgent = False
|
||||
else:
|
||||
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
|
||||
|
||||
|
||||
DATE_DEFAULTS = {'format': '%c'}
|
||||
|
||||
|
||||
class PluginThread(plugins.PluginThreadCommon):
|
||||
def __init__(self, config):
|
||||
defaults = {'format': '%c', 'tz': None}
|
||||
super(PluginThread, self).__init__(config, defaults)
|
||||
super(PluginThread, self).__init__(config, DATE_DEFAULTS)
|
||||
self.timezone = None
|
||||
if self.conf['tz']:
|
||||
if 'tz' in self.conf:
|
||||
import pytz
|
||||
self.timezone = pytz.timezone(self.conf['tz'])
|
||||
|
||||
def main(self):
|
||||
now = datetime.datetime.now(tz=self.timezone)
|
||||
self.status['full_text'] = now.strftime(self.conf['format'])
|
||||
now = datetime.datetime\
|
||||
.now(tz=self.timezone)\
|
||||
.strftime(self.conf['format'])
|
||||
self.format_status(now)
|
||||
|
|
|
@ -2,10 +2,14 @@ import plugins
|
|||
import psutil
|
||||
|
||||
|
||||
DISK_DEFAULTS = {'partition': '/', 'problem': 80, 'freq': 15}
|
||||
|
||||
|
||||
class PluginThread(plugins.PluginThreadCommon):
|
||||
def __init__(self, config):
|
||||
defaults = {'partition': '/', 'problem': 80, 'freq': 15}
|
||||
super(PluginThread, self).__init__(config, defaults)
|
||||
super(PluginThread, self).__init__(config, DISK_DEFAULTS)
|
||||
if 'title' not in self.conf:
|
||||
self.conf['title'] = self.conf['partition']
|
||||
|
||||
def main(self):
|
||||
du_stat = psutil.disk_usage(self.conf['partition'])
|
||||
|
@ -15,6 +19,6 @@ class PluginThread(plugins.PluginThreadCommon):
|
|||
else:
|
||||
self.hide = True
|
||||
self.status['urgent'] = False
|
||||
du_free = str(round(du_stat.free / 2**30, 2))
|
||||
disk_usage = self.conf['partition'] + ': ' + du_free + 'G'
|
||||
self.status['full_text'] = disk_usage
|
||||
|
||||
status = '{:.2f}G'.format(du_stat.free / 2**30)
|
||||
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
|
||||
|
||||
|
||||
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):
|
||||
def __init__(self, config):
|
||||
defaults = {'freq': 120, 'retry': 3}
|
||||
super(PluginThread, self).__init__(config, defaults)
|
||||
super(PluginThread, self).__init__(config, FORTUNE_DEFAULTS)
|
||||
self.retry = False
|
||||
|
||||
|
||||
def main(self):
|
||||
try:
|
||||
req = requests.get(URI, timeout=2)
|
||||
advice = req.json()['text'] if req.status_code == 200 else 'N/A'
|
||||
req = requests.get(self.conf['uri'], timeout=2)
|
||||
fortune = req.json()['text'] if req.status_code == 200 else 'N/A'
|
||||
self.retry = False
|
||||
except requests.exceptions.Timeout:
|
||||
advice = 'N/A (timeout)'
|
||||
fortune = 'N/A (timeout)'
|
||||
self.retry = True
|
||||
except requests.exceptions.ConnectionError:
|
||||
advice = 'N/A (offline)'
|
||||
fortune = 'N/A (offline)'
|
||||
self.retry = True
|
||||
self.status['full_text'] = advice
|
||||
|
||||
self.format_status(fortune)
|
||||
|
||||
def run(self):
|
||||
while True:
|
|
@ -2,17 +2,21 @@ import os
|
|||
import plugins
|
||||
|
||||
|
||||
LOAD_DEFAULTS = {'title': 'LOAD', 'freq': 20, 'problem': 1}
|
||||
|
||||
|
||||
class PluginThread(plugins.PluginThreadCommon):
|
||||
def __init__(self, config):
|
||||
defaults = {'freq': 20, 'problem': 1}
|
||||
super(PluginThread, self).__init__(config, defaults)
|
||||
super(PluginThread, self).__init__(config, LOAD_DEFAULTS)
|
||||
|
||||
def main(self):
|
||||
loads = os.getloadavg()
|
||||
if loads[0] >= self.conf['problem']:
|
||||
self.hide = False
|
||||
self.status['urgent'] = True
|
||||
urgent = True
|
||||
else:
|
||||
self.hide = True
|
||||
self.status['urgent'] = False
|
||||
self.status['full_text'] = 'LA: {:.2f} {:.2f} {:.2f}'.format(*loads)
|
||||
urgent = False
|
||||
status = '{:.2f} {:.2f} {:.2f}'.format(*loads)
|
||||
|
||||
self.format_status(status, urgent)
|
||||
|
|
|
@ -2,18 +2,21 @@ import psutil
|
|||
import plugins
|
||||
|
||||
|
||||
MEM_DEFAULTS = {'title': 'RAM', 'problem': 85}
|
||||
|
||||
|
||||
class PluginThread(plugins.PluginThreadCommon):
|
||||
def __init__(self, config):
|
||||
defaults = {'problem': 85}
|
||||
super(PluginThread, self).__init__(config, defaults)
|
||||
super(PluginThread, self).__init__(config, MEM_DEFAULTS)
|
||||
|
||||
def main(self):
|
||||
mem_stat = psutil.virtual_memory()
|
||||
if mem_stat.percent > self.conf['problem']:
|
||||
self.hide = False
|
||||
self.status['urgent'] = True
|
||||
urgent = True
|
||||
else:
|
||||
self.hide = True
|
||||
self.status['urgent'] = False
|
||||
mem_available = round(mem_stat.available / 2**30, 2)
|
||||
self.status['full_text'] = 'RAM: {:.2f}G'.format(mem_available)
|
||||
urgent = False
|
||||
|
||||
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 subprocess
|
||||
import random
|
||||
import plugins
|
||||
|
||||
|
||||
PING_DEFAULTS = {
|
||||
'hosts': tuple(), 'title': 'PING', 'timeout': 150
|
||||
}
|
||||
|
||||
|
||||
class PluginThread(plugins.PluginThreadCommon):
|
||||
def __init__(self, config):
|
||||
defaults = {'hosts': list(), 'title': 'PING', 'timeout': 150}
|
||||
super(PluginThread, self).__init__(config, defaults)
|
||||
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
|
||||
super(PluginThread, self).__init__(config, PING_DEFAULTS)
|
||||
self.ping_cmd = ('fping', '-c1', '-qt' + str(self.conf['timeout']))
|
||||
|
||||
def main(self):
|
||||
random.shuffle(self.conf['hosts'])
|
||||
for host in self.conf['hosts']:
|
||||
fping = 'fping -qc1t' + str(self.conf['timeout'])\
|
||||
+ ' ' + host + ' &>/dev/null'
|
||||
response = os.system(fping)
|
||||
if response == 0:
|
||||
self.format_status('on')
|
||||
break
|
||||
self.format_status('off')
|
||||
host = random.choice(self.conf['hosts'])
|
||||
fping = subprocess.run(
|
||||
(*self.ping_cmd, host),
|
||||
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL
|
||||
)
|
||||
if fping.returncode == 0:
|
||||
self.format_status('up')
|
||||
else:
|
||||
self.format_status('down', True)
|
||||
|
|
12
vdstatus
12
vdstatus
|
@ -6,7 +6,6 @@ import os
|
|||
import sys
|
||||
import time
|
||||
import yaml
|
||||
import plugins
|
||||
|
||||
|
||||
DEFAULT_CONFIG = os.path.join(os.environ['HOME'], '.config/vdstatus/conf.yaml')
|
||||
|
@ -28,10 +27,10 @@ def parse_arguments():
|
|||
|
||||
class PluginRunner:
|
||||
def __init__(self, config_file=DEFAULT_CONFIG):
|
||||
config = dict()
|
||||
self.conf = dict()
|
||||
self.conf.update(DEFAULTS)
|
||||
with open(config_file) as config_data:
|
||||
config = yaml.load(config_data)
|
||||
self.conf = plugins.parse_config(config, DEFAULTS)
|
||||
self.conf.update(yaml.safe_load(config_data))
|
||||
self.plugins_loaded = list()
|
||||
self.format_output = self.format_term
|
||||
for plugin in self.conf['plugins']:
|
||||
|
@ -50,8 +49,9 @@ class PluginRunner:
|
|||
outputs = list()
|
||||
for plugin in self.plugins_loaded:
|
||||
if \
|
||||
not plugin.conf['hide_ok'] or \
|
||||
not plugin.hide:
|
||||
'full_text' in plugin.status and (
|
||||
not plugin.conf['hide_ok'] or not plugin.hide
|
||||
):
|
||||
outputs.append(plugin.status)
|
||||
print(self.format_output(outputs), flush=True)
|
||||
|
||||
|
|
Loading…
Reference in a new issue