diff --git a/.bin/Scripts/functions/common.py b/.bin/Scripts/functions/common.py index a58fa5ce..4b9727d7 100644 --- a/.bin/Scripts/functions/common.py +++ b/.bin/Scripts/functions/common.py @@ -233,7 +233,8 @@ def major_exception(): def menu_select(title='~ Untitled Menu ~', prompt='Please make a selection', secret_exit=False, - main_entries=[], action_entries=[], disabled_label='DISABLED'): + main_entries=[], action_entries=[], disabled_label='DISABLED', + spacer=''): """Display options in a menu and return selected option as a str.""" # Bail early if not main_entries and not action_entries: @@ -244,7 +245,7 @@ def menu_select(title='~ Untitled Menu ~', title = '{}\n\n{}'.format(global_vars['Title'], title) # Build menu - menu_splash = '{}\n\n'.format(title) + menu_splash = '{}\n{}\n'.format(title, spacer) width = len(str(len(main_entries))) valid_answers = [] if (secret_exit): @@ -255,7 +256,7 @@ def menu_select(title='~ Untitled Menu ~', entry = main_entries[i] # Add Spacer if ('CRLF' in entry): - menu_splash += '\n' + menu_splash += '{}\n'.format(spacer) entry_str = '{number:>{width}}: {name}'.format( number = i+1, width = width, @@ -268,13 +269,13 @@ def menu_select(title='~ Untitled Menu ~', else: valid_answers.append(str(i+1)) menu_splash += '{}\n'.format(entry_str) - menu_splash += '\n' + menu_splash += '{}\n'.format(spacer) # Add action entries for entry in action_entries: # Add Spacer if ('CRLF' in entry): - menu_splash += '\n' + menu_splash += '{}\n'.format(spacer) valid_answers.append(entry['Letter']) menu_splash += '{letter:>{width}}: {name}\n'.format( letter = entry['Letter'].upper(), @@ -554,7 +555,8 @@ def wait_for_process(name, poll_rate=3): def init_global_vars(): """Sets global variables based on system info.""" print_info('Initializing') - os.system('title Wizard Kit') + if psutil.WINDOWS: + os.system('title Wizard Kit') if psutil.LINUX: init_functions = [ ['Checking environment...', set_linux_vars], diff --git a/.bin/Scripts/hw-diags b/.bin/Scripts/hw-diags new file mode 100755 index 00000000..4ce5afdc --- /dev/null +++ b/.bin/Scripts/hw-diags @@ -0,0 +1,43 @@ +#!/bin/bash +# +## Wizard Kit: HW Diagnostics - Menu Launcher + +SESSION_NAME="hw-diags" +WINDOW_NAME="Hardware Diagnostics" +MENU="hw-diags-menu" + +function ask() { + while :; do + read -p "$1 " -r answer + if echo "$answer" | egrep -iq '^(y|yes|sure)$'; then + return 0 + elif echo "$answer" | egrep -iq '^(n|no|nope)$'; then + return 1 + fi + done +} + +die () { + echo "$0:" "$@" >&2 + exit 1 +} + +# Check for running session +if tmux list-session | grep -q "$SESSION_NAME"; then + echo "WARNING: hw-diags tmux session already exists." + echo "" + if ask "Kill current session?"; then + tmux kill-session -t "$SESSION_NAME" || \ + die "Failed to kill session: $SESSION_NAME" + else + echo "Aborted." + echo "" + echo -n "Press Enter to exit... " + read -r + exit 0 + fi +fi + +# Start session +tmux new-session -s "$SESSION_NAME" -n "$WINDOW_NAME" "$MENU" + diff --git a/.bin/Scripts/hw-diags-menu b/.bin/Scripts/hw-diags-menu new file mode 100755 index 00000000..0fc19d77 --- /dev/null +++ b/.bin/Scripts/hw-diags-menu @@ -0,0 +1,200 @@ +#!/bin/python3 +# +## Wizard Kit: HW Diagnostics - Menu + +import libtmux +import json +import os +import sys + +# Init +os.chdir(os.path.dirname(os.path.realpath(__file__))) +sys.path.append(os.getcwd()) +from functions.common import * +init_global_vars() + +# STATIC VARIABLES +sleep(1) +TMUX = libtmux.Server() +SESSION = TMUX.find_where({'session_name': 'hw-diags'}) +WINDOW = SESSION.windows[0] # Should be a safe assumption +PANE = WINDOW.panes[0] # Should be a safe assumption +PROGRESS_FILE = '{}/progress.out'.format(global_vars['LogDir']) +TESTS = { + 'Prime95': { + 'Enabled': False, + 'Status': 'Pending', + }, + 'SMART': { + 'Enabled': False, + 'Quick': False, + }, + 'badblocks': { + 'Enabled': False, + }, + } + +def get_status_color(s): + color = COLORS['CLEAR'] + if s in ['NS', 'Unknown']: + color = COLORS['RED'] + elif s in ['Working', 'Skipped']: + color = COLORS['YELLOW'] + elif s in ['CS']: + color = COLORS['GREEN'] + return color + +def update_progress(color=True): + with open(PROGRESS_FILE, 'w') as f: + if color: + f.write('{BLUE}HW Diagnostics{CLEAR}\n'.format(**COLORS)) + f.write('───────────────\n') + if TESTS['Prime95']['Enabled']: + f.write('{BLUE}Prime95{s_color}{status:>8}{CLEAR}\n'.format( + s_color = get_status_color(TESTS['Prime95']['Status']), + status = status, + **COLORS)) + if TESTS['SMART']['Enabled']: + f.write('{BLUE}SMART{CLEAR}\n'.format(**COLORS)) + for dev, status in sorted(TESTS['SMART']['Devices'].items()): + f.write('{dev}{s_color}{status:>{pad}}{CLEAR}\n'.format( + dev = dev, + pad = 16-len(dev), + s_color = get_status_color(status), + status = status, + **COLORS)) + if TESTS['badblocks']['Enabled']: + f.write('{BLUE}badblocks{CLEAR}\n'.format(**COLORS)) + for dev, status in sorted(TESTS['badblocks']['Devices'].items()): + f.write('{dev}{s_color}{status:>{pad}}{CLEAR}\n'.format( + dev = dev, + pad = 16-len(dev), + s_color = get_status_color(status), + status = status, + **COLORS)) + else: + f.write('HW Diagnostics\n') + f.write('───────────────\n') + if TESTS['Prime95']['Enabled']: + f.write('Prime95{:>8}\n'.format(TESTS['Prime95']['Status'])) + if TESTS['SMART']['Enabled']: + f.write('SMART\n') + for dev, status in sorted(TESTS['SMART']['Devices'].items()): + f.write('{}{:>{}}\n'.format(dev, 16-len(dev), status)) + if TESTS['badblocks']['Enabled']: + f.write('badblocks\n') + for dev, status in sorted(TESTS['badblocks']['Devices'].items()): + f.write('{}{:>{}}\n'.format(dev, 16-len(dev), status)) + +def run_tests(tests): + for t in ['Prime95', 'SMART', 'badblocks']: + if t in tests: + TESTS[t]['Enabled'] = True + TESTS['SMART']['Quick'] = 'Quick' in tests + + # Get (disk) device list + if TESTS['SMART']['Enabled'] or TESTS['badblocks']['Enabled']: + cmd = 'lsblk -J -o NAME,TYPE'.split() + result = run_program(cmd) + json_data = json.loads(result.stdout.decode()) + devs = json_data.get('blockdevices', []) + devs = ['/dev/'+d['name'] for d in devs if d['type'] == 'disk'] + devs = {d: 'Pending' for d in devs} + TESTS['SMART']['Devices'] = devs + TESTS['badblocks']['Devices'] = devs + + # Initialize progress display + update_progress() + + # Run + if TESTS['Prime95']['Enabled']: + run_mprime() + if TESTS['SMART']['Enabled']: + run_smart() + if TESTS['badblocks']['Enabled']: + run_badblocks() + +def run_smart(): + pass + +def run_badblocks(): + pass + +def run_mprime(): + # Set Window layout + window = SESSION.new_window() + pane_sensors = window.panes[0] + pane_mprime = window.split_window(attach=False) + pane_mprime.set_height(10) + pane_progress = window.split_window(attach=False, vertical=False) + pane_progress.set_width(16) + + # Start test + run_program(['apple-fans', 'max']) + pane_sensors.send_keys('watch -c -n1 -t hw-sensors') + pane_progress.send_keys('watch -c -n1 -t "{}"'.format(PROGRESS_FILE)) + pane_mprime.send_keys('mprime -t') + #sleep(MPRIME_LIMIT*60) + sleep(15) + + # Done + window.kill_window() + +def diags_menu(): + diag_modes = [ + {'Name': 'All tests', 'Tests': ['Prime95', 'SMART', 'badblocks']}, + {'Name': 'Prime95', 'Tests': ['Prime95']}, + {'Name': 'SMART & badblocks', 'Tests': ['SMART', 'badblocks']}, + {'Name': 'SMART', 'Tests': ['SMART']}, + {'Name': 'badblocks', 'Tests': ['badblocks']}, + {'Name': 'Quick drive test', 'Tests': ['Quick', 'SMART']}, + ] + actions = [ + {'Letter': 'A', 'Name': 'Audio test'}, + {'Letter': 'N', 'Name': 'Network test'}, + {'Letter': 'M', 'Name': 'Screen Saver - Matrix', 'CRLF': True}, + {'Letter': 'P', 'Name': 'Screen Saver - Pipes'}, + {'Letter': 'Q', 'Name': 'Quit', 'CRLF': True}, + ] + + # Show menu + while True: + selection = menu_select( + title = 'Hardware Diagnostics: Menu', + main_entries = diag_modes, + action_entries = actions, + spacer = '─────────────────────────') + if selection.isnumeric(): + run_tests(diag_modes[int(selection)-1]['Tests']) + elif selection == 'A': + run_program(['hw-diags-audio'], check=False, pipe=False) + sleep(1) + elif selection == 'N': + run_program(['hw-diags-network'], check=False, pipe=False) + sleep(1) + elif selection == 'M': + run_program(['cmatrix', '-abs'], check=False, pipe=False) + elif selection == 'P': + run_program( + 'pipes -t 0 -t 1 -t 2 -t 3 -p 5 -R -r 4000'.split(), + check=False, pipe=False) + elif selection == 'Q': + break + +if __name__ == '__main__': + try: + # Prep + clear_screen() + + # Show menu + diags_menu() + + # Done + #print_standard('\nDone.') + #pause("Press Enter to exit...") + exit_script() + except SystemExit: + pass + except: + major_exception() + diff --git a/.linux_items/include/live/airootfs/etc/skel/.tmux.conf b/.linux_items/include/live/airootfs/etc/skel/.tmux.conf index 29cf429c..a3712835 100644 --- a/.linux_items/include/live/airootfs/etc/skel/.tmux.conf +++ b/.linux_items/include/live/airootfs/etc/skel/.tmux.conf @@ -1,2 +1,7 @@ set -g status off set -g pane-active-border-fg white + +# Window names +set -g set-titles on +set -g set-titles-string '#W' +setw -g automatic-rename