optional mini HW monitor in tray - phwmon.py
parent
8a1d39641e
commit
fd00494c8e
|
@ -1,6 +1,6 @@
|
|||
#!/bin/bash
|
||||
|
||||
if [ `pidof picom` ]; then
|
||||
if [ $(pidof picom) ]; then
|
||||
echo "Stopping picom"
|
||||
killall picom
|
||||
else
|
||||
|
|
|
@ -33,6 +33,27 @@ VBoxManage list -s vms | cut -f 2 -d "\"" | sort -f | while read vm
|
|||
printf "%s\n%s\n" "^sep()" "$REFRESH,/usr/bin/mabox-obstart virtualboxes" >>$HOME/.config/mabox/vboxes.csv
|
||||
fi
|
||||
}
|
||||
|
||||
phwmon() {
|
||||
#kill phwmon.py if running
|
||||
if phwmonpid=$(pgrep -f phwmon.py); then
|
||||
kill $phwmonpid
|
||||
fi
|
||||
. $HOME/.config/mabox/mabox.conf
|
||||
if [ $phwmon_monitor == true ];then
|
||||
|
||||
[[ $phwmon_cpu == true ]] && cpu="--cpu" || cpu=""
|
||||
[[ $phwmon_mem == true ]] && mem="--mem" || mem=""
|
||||
[[ $phwmon_swap == true ]] && swap="--swap" || swap=""
|
||||
[[ $phwmon_net == true ]] && net="--net" || net=""
|
||||
[[ $phwmon_io == true ]] && io="--io" || io=""
|
||||
|
||||
phwmon.py ${cpu} ${mem} ${swap} ${net} ${io} --task_manager lxtask
|
||||
|
||||
fi
|
||||
|
||||
}
|
||||
|
||||
startopenbox() {
|
||||
# Copy only new files from /etc/xdg/autostart/
|
||||
config_dir=${XDG_CONFIG_HOME:-$HOME/.config}
|
||||
|
@ -42,14 +63,31 @@ rsync -aq --ignore-existing /etc/xdg/autostart/ $config_dir/autostart
|
|||
# Run mwelcome if not disaled
|
||||
[ ! -f "$HOME/.config/mabox/.mwelcome" ] && mwelcome &
|
||||
|
||||
. $HOME/.config/mabox/mabox.conf
|
||||
#Set config variables if not set or empty; ":" means do nothing
|
||||
# NEW CONFIG VARIABLES - SET defaults at openbox start
|
||||
[[ -v phwmon_monitor ]] && : || mb-setvar phwmon_monitor=false
|
||||
[[ -v phwmon_cpu ]] && : || mb-setvar phwmon_cpu=true
|
||||
[[ -v phwmon_mem ]] && :|| mb-setvar phwmon_mem=true
|
||||
[[ -v phwmon_swap ]] && : || mb-setvar phwmon_swap=false
|
||||
[[ -v phwmon_net ]] && : || mb-setvar phwmon_net=true
|
||||
[[ -v phwmon_io ]] && : || mb-setvar phwmon_io=false
|
||||
[[ -v places_tint2pipe ]] && : || mb-setvar places_tint2pipe=true
|
||||
[[ -v places_quicknav ]] && : || mb-setvar places_quicknav=true
|
||||
[[ -v places_bookmarks ]] && : || mb-setvar places_bookmarks=true
|
||||
|
||||
virtualboxes
|
||||
if command -v phwmon.py &>/dev/null; then
|
||||
phwmon
|
||||
fi
|
||||
}
|
||||
|
||||
case "$1" in
|
||||
startopenbox) startopenbox;;
|
||||
virtualboxes) virtualboxes;;
|
||||
phwmon) phwmon;;
|
||||
*)
|
||||
echo -e "Usage $(basename "$0") startopenbox|virtualboxes" >&2
|
||||
echo -e "Usage $(basename "$0") startopenbox|virtualboxes|phwmon" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
|
|
@ -0,0 +1,373 @@
|
|||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# License: GPLv2
|
||||
|
||||
import gi
|
||||
gi.require_version('Gtk', '3.0')
|
||||
from gi.repository import Gtk, Gdk, GdkPixbuf, GLib
|
||||
|
||||
import argparse
|
||||
import cairo
|
||||
import subprocess
|
||||
|
||||
import psutil
|
||||
|
||||
def normalize_color_hex(s):
|
||||
if s[0] == "#":
|
||||
s = s[1:]
|
||||
if len(s) == 3:
|
||||
s = s[0] + s[0] + s[1] + s[1] + s[2] + s[2]
|
||||
if len(s) == 4:
|
||||
s = s[0] + s[0] + s[1] + s[1] + s[2] + s[2] + s[3] + s[3]
|
||||
if len(s) == 6:
|
||||
s += "ff"
|
||||
assert(len(s) == 8)
|
||||
return s
|
||||
|
||||
def color_hex_to_float(s):
|
||||
s = normalize_color_hex(s)
|
||||
return [int(s[0:2], 16)/255.0, int(s[2:4], 16)/255.0, int(s[4:6], 16)/255.0, int(s[6:8], 16)/255.0]
|
||||
|
||||
def color_hex_to_int(s):
|
||||
s = normalize_color_hex(s)
|
||||
return int(s, 16)
|
||||
|
||||
def bytes2human(n):
|
||||
# http://code.activestate.com/recipes/578019
|
||||
# >>> bytes2human(10000)
|
||||
# '9.8 K'
|
||||
# >>> bytes2human(100001221)
|
||||
# '95.4 M'
|
||||
symbols = ('K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y')
|
||||
prefix = {}
|
||||
for i, s in enumerate(symbols):
|
||||
prefix[s] = 1 << (i + 1) * 10
|
||||
for s in reversed(symbols):
|
||||
if n >= prefix[s]:
|
||||
value = float(n) / prefix[s]
|
||||
return '%.1f %sB' % (value, s)
|
||||
return "%s B" % n
|
||||
|
||||
|
||||
# Parameters
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--cpu", help="Show a CPU activity graph", dest="cpu", action="store_true")
|
||||
parser.add_argument("--cpu_alert_max", help="Blink when CPU load goes above defined percentage. Default: 100", dest="cpu_alert_max", default=100, type=int)
|
||||
parser.add_argument("--core", help="Show a CPU activity graph for each logical CPU core", dest="core", action="store_true")
|
||||
parser.add_argument("--mem", help="Show a memory usage graph", dest="mem", action="store_true")
|
||||
parser.add_argument("--mem_percent", help="Tooltip: Show used memory in percentage", dest="mem_percent", action="store_true")
|
||||
parser.add_argument("--mem_alert_max", help="Blink when memory usage goes above defined percentage. Default: 100", dest="mem_alert_max", default=100, type=int)
|
||||
parser.add_argument("--swap", help="Show a swap usage graph", dest="swap", action="store_true")
|
||||
parser.add_argument("--swap_percent", help="Tooltip: Show used swap in percentage", dest="swap_percent", action="store_true")
|
||||
parser.add_argument("--swap_alert_max", help="Blink when swap usage goes above defined percentage. Default: 100", dest="swap_alert_max", default=100, type=int)
|
||||
parser.add_argument("--net", help="Show a network usage graph", dest="net", action="store_true")
|
||||
parser.add_argument("--net_scale", help="Maximum value for the network usage graph, in Mbps. Default: 40.", default=40, type=int)
|
||||
parser.add_argument("--net_alert_max", help="Blink when network usage goes above defined percentage. Default: 100 ", dest="net_alert_max", default=100, type=int)
|
||||
parser.add_argument("--io", help="Show a disk I/O graph", dest="io", action="store_true")
|
||||
parser.add_argument("--io_scale", help="Maximum value for the disk I/O graph, in MB/s. Default: 100.", default=100, type=int)
|
||||
parser.add_argument("--io_alert_max", help="Blink when disk I/O goes above defined percentage. Default: 100", dest="io_alert_max", default=100, type=int)
|
||||
|
||||
parser.add_argument("--size", help="Icon size in pixels. Default: 22.", default=22, type=int)
|
||||
parser.add_argument("--invert", help="Try to invert order of icons ( might not work as it depends highly on the system tray used )", dest="invert", action="store_true")
|
||||
parser.add_argument("--interval", help="Refresh interval in miliseconds. Default: 1000 (1sec).", default=1000, type=int)
|
||||
parser.add_argument("--alert_interval", help="Alert blink interval in miliseconds. Default: 1000 (1sec).", default=1000, type=int)
|
||||
parser.add_argument("--bg", help="Background color (RGBA hex). Default: #DDDDDD60.", default="#DDDDDD60")
|
||||
parser.add_argument("--fg_cpu", help="CPU graph color (RGBA hex). Default: #70b433.", default="#70b433")
|
||||
parser.add_argument("--fg_mem", help="Memory graph color (RGBA hex). Default: #efc541.", default="#efc541")
|
||||
parser.add_argument("--fg_swap", help="Swap graph color (RGBA hex). Default: #ff81ca.", default="#ff81ca")
|
||||
parser.add_argument("--fg_net", help="Network graph color (RGBA hex). Default: #368aeb.", default="#368aeb")
|
||||
parser.add_argument("--fg_io", help="Disk I/O graph color (RGBA hex). Default: #ff5e56.", default="#ff5e56")
|
||||
parser.add_argument("--fg_alert", help="Alert color (RGBA hex). Default: #ff0000cc.", default="#ff0000cc")
|
||||
|
||||
parser.add_argument("--task_manager", help="Task manager to execute on left click. Default: None.")
|
||||
|
||||
parser.set_defaults(cpu=False)
|
||||
parser.set_defaults(core=False)
|
||||
parser.set_defaults(mem=False)
|
||||
parser.set_defaults(swap=False)
|
||||
parser.set_defaults(net=False)
|
||||
parser.set_defaults(io=False)
|
||||
args = parser.parse_args()
|
||||
|
||||
w = h = args.size
|
||||
invert = args.invert
|
||||
interval = args.interval
|
||||
alertInterval = args.alert_interval
|
||||
bgCol = color_hex_to_int(args.bg)
|
||||
fgCpu = color_hex_to_float(args.fg_cpu)
|
||||
fgRam = color_hex_to_float(args.fg_mem)
|
||||
fgSwap = color_hex_to_float(args.fg_swap)
|
||||
fgNet = color_hex_to_float(args.fg_net)
|
||||
fgDiskIo = color_hex_to_float(args.fg_io)
|
||||
fgAlert = color_hex_to_int(args.fg_alert)
|
||||
|
||||
cpuEnabled = args.cpu or args.core
|
||||
cpuAlertMax = args.cpu_alert_max
|
||||
mergeCpus = not args.core
|
||||
ramEnabled = args.mem
|
||||
memPercent = args.mem_percent
|
||||
memAlertMax = args.mem_alert_max
|
||||
swapEnabled = args.swap
|
||||
swapPercent = args.swap_percent
|
||||
swapAlertMax = args.swap_alert_max
|
||||
netEnabled = args.net
|
||||
netScale = args.net_scale
|
||||
netAlertMax = args.net_alert_max
|
||||
diskIoEnabled = args.io
|
||||
diskIoScale = args.io_scale
|
||||
diskIoAlertMax = args.io_alert_max
|
||||
taskMgr = args.task_manager
|
||||
|
||||
if not cpuEnabled and not ramEnabled and not swapEnabled and not netEnabled and not diskIoEnabled:
|
||||
cpuEnabled = mergeCpus = ramEnabled = swapEnabled = netEnabled = diskIoEnabled = True
|
||||
|
||||
class HardwareMonitor:
|
||||
alertState = False
|
||||
|
||||
def __init__(self):
|
||||
if invert:
|
||||
self.initDiskIo()
|
||||
self.initNet()
|
||||
self.initSwap()
|
||||
self.initRam()
|
||||
self.initCpus()
|
||||
self.drawDiskIo()
|
||||
self.drawNet()
|
||||
self.drawSwap()
|
||||
self.drawRam()
|
||||
self.drawCpus()
|
||||
else:
|
||||
self.initCpus()
|
||||
self.initRam()
|
||||
self.initSwap()
|
||||
self.initNet()
|
||||
self.initDiskIo()
|
||||
self.drawCpus()
|
||||
self.drawRam()
|
||||
self.drawSwap()
|
||||
self.drawNet()
|
||||
self.drawDiskIo()
|
||||
GLib.timeout_add(interval, self.update)
|
||||
GLib.timeout_add(alertInterval, self.alertFlip)
|
||||
|
||||
def alertFlip(self):
|
||||
self.alertState = not self.alertState
|
||||
return True
|
||||
|
||||
def rightClickEvent(self, icon, button, time):
|
||||
menu = Gtk.Menu()
|
||||
quit = Gtk.MenuItem("Quit")
|
||||
quit.connect("activate", Gtk.main_quit)
|
||||
menu.append(quit)
|
||||
menu.show_all()
|
||||
menu.popup(None, None, Gtk.status_icon_position_menu, button, time, icon)
|
||||
|
||||
def leftClickEvent (self, icon):
|
||||
if not taskMgr:
|
||||
return
|
||||
subprocess.Popen(taskMgr)
|
||||
|
||||
def initCpus(self):
|
||||
if not cpuEnabled:
|
||||
return
|
||||
if mergeCpus:
|
||||
self.cpus = [[0 for x in range(w)]]
|
||||
psutil.cpu_percent(percpu=mergeCpus)
|
||||
else:
|
||||
self.cpus = [[0 for x in range(w)] for c in range(len(psutil.cpu_percent(percpu=not mergeCpus)))]
|
||||
self.cpuIcons = [Gtk.StatusIcon() for c in range(len(self.cpus))]
|
||||
for c in range(len(self.cpus)):
|
||||
self.cpuIcons[c].set_title("hwmon 1 cpu{0}".format("" if mergeCpus else (" " + str(c+1))))
|
||||
self.cpuIcons[c].connect("popup-menu", self.rightClickEvent)
|
||||
self.cpuIcons[c].connect("activate", self.leftClickEvent)
|
||||
self.cpuIcons[c].set_visible(True)
|
||||
|
||||
def updateCpus(self):
|
||||
if not cpuEnabled:
|
||||
return
|
||||
vals = psutil.cpu_percent(percpu=not mergeCpus)
|
||||
if mergeCpus:
|
||||
vals = [vals]
|
||||
for c in range(len(vals)):
|
||||
self.cpus[c].append(vals[c])
|
||||
self.cpus[c].pop(0)
|
||||
self.cpuIcons[c].set_tooltip_text("CPU{0}: {1}%".format("" if mergeCpus else (" " + str(c+1)), vals[c]))
|
||||
|
||||
def drawCpus(self):
|
||||
if not cpuEnabled:
|
||||
return
|
||||
for c in range(len(self.cpus)):
|
||||
self.drawGraph(self.cpus[c], self.cpuIcons[c], bgCol, fgCpu)
|
||||
self.drawAlert(self.cpus[c], self.cpuIcons[c], fgAlert, cpuAlertMax)
|
||||
|
||||
def initRam(self):
|
||||
if not ramEnabled:
|
||||
return
|
||||
self.ram = [0 for x in range(w)]
|
||||
self.ramIcon = Gtk.StatusIcon()
|
||||
self.ramIcon.set_title("hwmon 2 memory")
|
||||
self.ramIcon.connect("popup-menu", self.rightClickEvent)
|
||||
self.ramIcon.connect("activate", self.leftClickEvent)
|
||||
self.ramIcon.set_visible(True)
|
||||
|
||||
def updateRam(self):
|
||||
if not ramEnabled:
|
||||
return
|
||||
mem = psutil.virtual_memory()
|
||||
total = mem[0]
|
||||
used = mem[3]
|
||||
used_percent = mem[2]
|
||||
self.ram.append(used_percent)
|
||||
self.ram.pop(0)
|
||||
if memPercent:
|
||||
self.ramIcon.set_tooltip_text("Memory: %d%% used of %s" % (used_percent, bytes2human(total)))
|
||||
else:
|
||||
self.ramIcon.set_tooltip_text("Memory: %s used of %s" % (bytes2human(used), bytes2human(total)))
|
||||
|
||||
def drawRam(self):
|
||||
if not ramEnabled:
|
||||
return
|
||||
self.drawGraph(self.ram, self.ramIcon, bgCol, fgRam)
|
||||
self.drawAlert(self.ram, self.ramIcon, fgAlert, memAlertMax)
|
||||
|
||||
def initSwap(self):
|
||||
if not swapEnabled:
|
||||
return
|
||||
self.swap = [0 for x in range(w)]
|
||||
self.swapIcon = Gtk.StatusIcon()
|
||||
self.swapIcon.set_title("hwmon 3 swap")
|
||||
self.swapIcon.connect("popup-menu", self.rightClickEvent)
|
||||
self.swapIcon.connect("activate", self.leftClickEvent)
|
||||
|
||||
def updateSwap(self):
|
||||
if not swapEnabled:
|
||||
return
|
||||
swap = psutil.swap_memory()
|
||||
total = swap[0]
|
||||
used = swap[1]
|
||||
used_percent = swap[3]
|
||||
if bool(self.swapIcon.get_visible()) != bool(total):
|
||||
self.swapIcon.set_visible(total)
|
||||
self.swap.append(used_percent)
|
||||
self.swap.pop(0)
|
||||
if swapPercent:
|
||||
self.swapIcon.set_tooltip_text("Swap: %d%% used of %s" % (used_percent, bytes2human(total)))
|
||||
else:
|
||||
self.swapIcon.set_tooltip_text("Swap: %s used of %s" % (bytes2human(used), bytes2human(total)))
|
||||
|
||||
def drawSwap(self):
|
||||
if not swapEnabled:
|
||||
return
|
||||
self.drawGraph(self.swap, self.swapIcon, bgCol, fgSwap)
|
||||
self.drawAlert(self.swap, self.swapIcon, fgAlert, swapAlertMax)
|
||||
|
||||
def initNet(self):
|
||||
if not netEnabled:
|
||||
return
|
||||
self.net = [0 for x in range(w)]
|
||||
v = psutil.net_io_counters(pernic=False)
|
||||
self.netBytes = v[0] + v[1]
|
||||
self.netIcon = Gtk.StatusIcon()
|
||||
self.netIcon.set_title("hwmon 4 network")
|
||||
self.netIcon.connect("popup-menu", self.rightClickEvent)
|
||||
self.netIcon.connect("activate", self.leftClickEvent)
|
||||
self.netIcon.set_visible(True)
|
||||
|
||||
def updateNet(self):
|
||||
if not netEnabled:
|
||||
return
|
||||
v = psutil.net_io_counters(pernic=False)
|
||||
v = v[0] + v[1]
|
||||
delta = v - self.netBytes
|
||||
self.netBytes = v
|
||||
self.net.append(delta * 8 / 1.0e6)
|
||||
self.net.pop(0)
|
||||
self.netIcon.set_tooltip_text("Network: %.1f Mb/s" % (delta * 8 / 1.0e6))
|
||||
|
||||
def drawNet(self):
|
||||
if not netEnabled:
|
||||
return
|
||||
self.drawGraph(self.net, self.netIcon, bgCol, fgNet, netScale)
|
||||
self.drawAlert(self.net, self.netIcon, fgAlert, netAlertMax, netScale)
|
||||
|
||||
def initDiskIo(self):
|
||||
if not diskIoEnabled:
|
||||
return
|
||||
self.diskIo = [0 for x in range(w)]
|
||||
v = psutil.disk_io_counters(perdisk=False)
|
||||
self.diskIoBytes = v[2] + v[3]
|
||||
self.diskIoIcon = Gtk.StatusIcon()
|
||||
self.diskIoIcon.set_title("hwmon 5 disk i/o")
|
||||
self.diskIoIcon.connect("popup-menu", self.rightClickEvent)
|
||||
self.diskIoIcon.connect("activate", self.leftClickEvent)
|
||||
self.diskIoIcon.set_visible(True)
|
||||
|
||||
def updateDiskIo(self):
|
||||
if not diskIoEnabled:
|
||||
return
|
||||
v = psutil.disk_io_counters(perdisk=False)
|
||||
v = v[2] + v[3]
|
||||
delta = v - self.diskIoBytes
|
||||
self.diskIoBytes = v
|
||||
self.diskIo.append(delta / 1.0e6 / 10)
|
||||
self.diskIo.pop(0)
|
||||
partitions = psutil.disk_partitions(all=False)
|
||||
strPartitions = ""
|
||||
for part in psutil.disk_partitions(all=False):
|
||||
if 'cdrom' in part.opts or part.fstype == '':
|
||||
continue
|
||||
usage = psutil.disk_usage(part.mountpoint)
|
||||
strPartitions += "\n%s %d%% of %s (%s)" % (part.mountpoint, int(usage.percent), bytes2human(usage.total), part.fstype)
|
||||
self.diskIoIcon.set_tooltip_text("Disk I/O: %.1f MB/s\n%s" % (delta / 1.0e6 / 10, strPartitions))
|
||||
|
||||
def drawDiskIo(self):
|
||||
if not diskIoEnabled:
|
||||
return
|
||||
self.drawGraph(self.diskIo, self.diskIoIcon, bgCol, fgDiskIo, diskIoScale)
|
||||
self.drawAlert(self.diskIo, self.diskIoIcon, fgAlert, diskIoAlertMax, diskIoScale)
|
||||
|
||||
def drawGraph(self, graph, icon, bgCol, fgCol, max=100):
|
||||
bg = GdkPixbuf.Pixbuf.new(GdkPixbuf.Colorspace.RGB, True, 8, w, h)
|
||||
bg.fill(bgCol)
|
||||
|
||||
surface = cairo.ImageSurface(cairo.FORMAT_RGB24, w, h)
|
||||
cr = cairo.Context(surface)
|
||||
cr.set_source_rgba(fgCol[2], fgCol[1], fgCol[0], fgCol[3])
|
||||
for x in range(w):
|
||||
y = int(round(graph[x]/max * h))
|
||||
if y:
|
||||
cr.move_to(x, h)
|
||||
cr.line_to(x, h - y)
|
||||
cr.stroke()
|
||||
|
||||
pixbuf = GdkPixbuf.Pixbuf.new_from_data(surface.get_data(), GdkPixbuf.Colorspace.RGB, True, 8, w, h, w * 4, None, None)
|
||||
pixbuf.composite(bg, 0, 0, w, h, 0, 0, 1, 1, GdkPixbuf.InterpType.NEAREST, 255)
|
||||
icon.set_from_pixbuf(bg)
|
||||
|
||||
def drawAlert(self, graph, icon, fgCol, threshold, max=100):
|
||||
if graph[-1] < threshold or not self.alertState:
|
||||
return
|
||||
bg = icon.get_pixbuf()
|
||||
fg = GdkPixbuf.Pixbuf.new(GdkPixbuf.Colorspace.RGB, True, 8, w, h)
|
||||
fg.fill(fgCol)
|
||||
fg.composite(bg, 0, 0, w, h, 0, 0, 1, 1, GdkPixbuf.InterpType.NEAREST, 255)
|
||||
icon.set_from_pixbuf(bg)
|
||||
|
||||
def update(self):
|
||||
self.updateCpus()
|
||||
self.updateRam()
|
||||
self.updateSwap()
|
||||
self.updateNet()
|
||||
self.updateDiskIo()
|
||||
self.drawCpus()
|
||||
self.drawRam()
|
||||
self.drawSwap()
|
||||
self.drawNet()
|
||||
self.drawDiskIo()
|
||||
return True
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
HardwareMonitor()
|
||||
Gtk.main()
|
Loading…
Reference in New Issue