229 lines
10 KiB
Python
229 lines
10 KiB
Python
# Built In Dependencies
|
|
import time
|
|
import queue
|
|
import sys
|
|
import re
|
|
from argparse import ArgumentParser, ArgumentDefaultsHelpFormatter
|
|
import importlib.util
|
|
import sys
|
|
import os
|
|
|
|
|
|
# Internal Dependencies
|
|
from drawing import draw_outline_border, draw_ids_left, draw_ids_right, draw_app, draw_app_border, DrawingThread
|
|
from monitors import CPUMonitor, MemoryMonitor, BatteryMonitor, DiskMonitor, NetworkMonitor, get_monitor_brightness
|
|
|
|
# External Dependencies
|
|
import numpy as np
|
|
import evdev
|
|
from serial.tools import list_ports
|
|
|
|
|
|
KEY_I = ('KEY_I', 23)
|
|
MODIFIER_KEYS = [('KEY_RIGHTALT', 100), ('KEY_LEFTALT', 56)]
|
|
|
|
def discover_led_devices():
|
|
locations = []
|
|
try:
|
|
device_list = list_ports.comports()
|
|
for device in device_list:
|
|
if 'LED Matrix Input Module' in str(device):
|
|
locations.append((device.location, device.device))
|
|
#location is of form: <bus>-<port>[-<port>]… port is of form x.y:n.m
|
|
# Sort by y:n.m to get the devices in left-right order
|
|
return sorted(locations, key = lambda x: re.sub('^\d+\-\d+\.', '', x[0]))
|
|
except Exception as e:
|
|
print(f"An Exception occured while tring to locate LED Matrix devices. {e}")
|
|
|
|
device = evdev.InputDevice('/dev/input/event7')
|
|
|
|
def main(args):
|
|
led_devices = discover_led_devices()
|
|
if not len(led_devices):
|
|
print("No LED devices found")
|
|
sys.exit(0)
|
|
elif len(led_devices) == 1:
|
|
print(f"Only one LED device found ({led_devices[0]}). Right panel args will be ignored")
|
|
args.top_right = args.bottom_right = "none"
|
|
else:
|
|
print(f"Found LED devices: Left: {led_devices[0]}, Right: {led_devices[1]}")
|
|
locations = list(map(lambda x: x[0], led_devices))
|
|
drawing_queues = []
|
|
|
|
# Track key presses to reveal metrics ID in each panel section
|
|
global alt_pressed
|
|
alt_pressed = False
|
|
global i_pressed
|
|
i_pressed = False
|
|
|
|
# Set up monitors and brightness parameters
|
|
min_background_brightness = 12
|
|
max_background_brightness = 35
|
|
min_foreground_brightness = 24
|
|
max_foreground_brightness = 160
|
|
|
|
cpu_monitor = CPUMonitor()
|
|
memory_monitor = MemoryMonitor()
|
|
battery_monitor = BatteryMonitor()
|
|
disk_monitor = DiskMonitor()
|
|
network_monitor = NetworkMonitor()
|
|
|
|
# Setuop left panel drawing queue
|
|
left_drawing_queue = queue.Queue(2)
|
|
left_drawing_thread = DrawingThread(locations[0], left_drawing_queue)
|
|
left_drawing_thread.start()
|
|
drawing_queues.append(left_drawing_queue)
|
|
|
|
# Setup right panel drawing queue (if present)
|
|
if len(locations) == 2:
|
|
right_drawing_queue = queue.Queue(2)
|
|
right_drawing_thread = DrawingThread(locations[1], right_drawing_queue)
|
|
right_drawing_thread.start()
|
|
drawing_queues.append(right_drawing_queue)
|
|
|
|
def draw_cpu(arg, grid, foreground_value, idx):
|
|
last_cpu_values = cpu_monitor.get()
|
|
draw_app(arg, grid, last_cpu_values, foreground_value, idx)
|
|
|
|
def draw_mem_bat(arg, grid, foreground_value, idx):
|
|
last_memory_values = memory_monitor.get()
|
|
last_battery_values = battery_monitor.get()
|
|
draw_app("mem", grid, last_memory_values, foreground_value, idx)
|
|
draw_app("bat", grid, last_battery_values[0], last_battery_values[1], foreground_value, idx+3)
|
|
|
|
def draw_disk(arg, grid, foreground_value, idx):
|
|
last_disk_read, last_disk_write = disk_monitor.get()
|
|
draw_app(arg, grid, last_disk_read, foreground_value, bar_x_offset=1, y=idx) # Read
|
|
draw_app(arg, grid, last_disk_write, foreground_value, bar_x_offset=5, y=idx) # Write
|
|
|
|
def draw_net(arg, grid, foreground_value, idx):
|
|
last_network_upload, last_network_download = network_monitor.get()
|
|
draw_app(arg, grid, last_network_upload, foreground_value, bar_x_offset=1, y=idx)
|
|
draw_app(arg, grid, last_network_download, foreground_value, bar_x_offset=5, y=idx)
|
|
|
|
app_functions = {
|
|
"cpu": draw_cpu,
|
|
"mem-bat": draw_mem_bat,
|
|
"disk": draw_disk,
|
|
"net": draw_net,
|
|
"none": lambda *x: x # noop
|
|
}
|
|
|
|
#################################################
|
|
### Load app functions from plugins ###
|
|
if not re.search(r"--disable-plugins|-dp", str(sys.argv)):
|
|
plugins_dir = './plugins/'
|
|
for file in os.listdir(plugins_dir):
|
|
if file.endswith('_plugin.py'):
|
|
module_name = re.sub(file, "_plugin.py", "")
|
|
file_path = plugins_dir + file
|
|
spec = importlib.util.spec_from_file_location(module_name, file_path)
|
|
module = importlib.util.module_from_spec(spec)
|
|
sys.modules[module_name] = module
|
|
spec.loader.exec_module(module)
|
|
|
|
for obj in module.app_funcs:
|
|
app_functions[obj["name"]] = obj["fn"]
|
|
#################################################
|
|
|
|
while True:
|
|
try:
|
|
screen_brightness = get_monitor_brightness()
|
|
background_value = int(screen_brightness * (max_background_brightness - min_background_brightness) + min_background_brightness)
|
|
foreground_value = int(screen_brightness * (max_foreground_brightness - min_foreground_brightness) + min_foreground_brightness)
|
|
grid = np.zeros((9,34), dtype = int)
|
|
active_keys = device.active_keys(verbose=True)
|
|
if (MODIFIER_KEYS[0] in active_keys or MODIFIER_KEYS[1] in active_keys) and KEY_I in active_keys and not args.no_key_listener:
|
|
draw_outline_border(grid, background_value)
|
|
draw_ids_left(grid, args.top_left, args.bottom_left, foreground_value)
|
|
left_drawing_queue.put(grid)
|
|
grid = np.zeros((9,34), dtype = int)
|
|
draw_outline_border(grid, background_value)
|
|
draw_ids_right(grid, args.top_right, args.bottom_right, foreground_value)
|
|
right_drawing_queue.put(grid)
|
|
grid = np.zeros((9,34), dtype = int)
|
|
time.sleep(0.1)
|
|
continue
|
|
|
|
# Draw by quadrants (i.e. to top and bottom of left and right panels)
|
|
for i, draw_queue in enumerate(drawing_queues):
|
|
if i == 0:
|
|
panel = 'left'
|
|
_args = [args.top_left, args.bottom_left]
|
|
else:
|
|
panel = 'right'
|
|
_args = [args.top_right, args.bottom_right]
|
|
grid = np.zeros((9,34), dtype = int)
|
|
for j, arg in enumerate(_args):
|
|
if j == 0:
|
|
idx = 0
|
|
loc = 'top'
|
|
else:
|
|
idx = 16
|
|
loc = 'bottom'
|
|
try:
|
|
func = app_functions[arg]
|
|
func(arg, grid, foreground_value, idx)
|
|
except KeyError:
|
|
print(app_functions.keys())
|
|
print(f"Unrecognized display option {arg} for {loc} {panel}")
|
|
if arg == 'mem-bat': arg = 'mem' # Single border draw for mem and bat together
|
|
draw_app_border(arg, grid, background_value, idx)
|
|
draw_queue.put(grid)
|
|
|
|
except KeyboardInterrupt:
|
|
break
|
|
except Exception as e:
|
|
import traceback
|
|
print(f"Error in main loop: {e}")
|
|
traceback.print_exc()
|
|
time.sleep(1.0)
|
|
time.sleep(0.1)
|
|
|
|
print("Exiting")
|
|
|
|
if __name__ == "__main__":
|
|
app_names = ["cpu", "net", "disk", "mem-bat", "none"]
|
|
###############################################################
|
|
### Load additional app names from plugins for arg parser ###
|
|
if not re.search(r"--disable-plugins|-dp", str(sys.argv)):
|
|
plugins_dir = './plugins/'
|
|
for file in os.listdir(plugins_dir):
|
|
if file.endswith('_plugin.py'):
|
|
module_name = re.sub(file, "_plugin.py", "")
|
|
file_path = plugins_dir + file
|
|
spec = importlib.util.spec_from_file_location(module_name, file_path)
|
|
module = importlib.util.module_from_spec(spec)
|
|
sys.modules[module_name] = module
|
|
spec.loader.exec_module(module)
|
|
|
|
app_names += module.metrics_funcs.keys()
|
|
#################################################################
|
|
parser = ArgumentParser(prog="FW LED System Monitor", add_help=False,
|
|
description="Displays system performance metrics in the Framework 16 LED Matrix input module",
|
|
formatter_class=ArgumentDefaultsHelpFormatter)
|
|
mode_group = parser.add_mutually_exclusive_group()
|
|
mode_group.add_argument("--help", "-h", action="help",
|
|
help="Show this help message and exit")
|
|
|
|
addGroup = parser.add_argument_group(title = "Metrics Display Options")
|
|
addGroup.add_argument("--top-left", "-tl", type=str, default="cpu", choices=app_names,
|
|
help="Metrics to display in the top section of the left matrix panel")
|
|
addGroup.add_argument("--bottom-left", "-bl", type=str, default="mem-bat", choices=app_names,
|
|
help="Metrics to display in the bottom section of the left matrix panel")
|
|
addGroup.add_argument("--top-right", "-tr", type=str, default="disk", choices=app_names,
|
|
help="Metrics to display in the top section of the right matrix panel")
|
|
addGroup.add_argument("--bottom-right", "-br", type=str, default="disk", choices=app_names,
|
|
help="Metrics to display in the top section of the right matrix panel")
|
|
|
|
addGroup.add_argument("--no-key-listener", "-nkl", action="store_true", help="Do not listen for key presses")
|
|
addGroup.add_argument("--disable-plugins", "-dp", action="store_true", help="Do not load any plugin code")
|
|
|
|
args = parser.parse_args()
|
|
print(f"top left {args.top_left}")
|
|
print(f"bottom left {args.bottom_left}")
|
|
print(f"top right {args.top_right}")
|
|
print(f"bottom right {args.bottom_right}")
|
|
if args.no_key_listener: print("Key listener disabled")
|
|
|
|
main(args)
|