working with live dashboard

This commit is contained in:
2025-12-06 17:42:26 -08:00
parent 4096f7165c
commit 94c31f4be3
7 changed files with 168 additions and 84 deletions

View File

@ -8,6 +8,8 @@ ssd_health_packages:
- python3-venv - python3-venv
- sqlite3 - sqlite3
- jq - jq
- hdparm
- redis-tools
# autologin vars # autologin vars
autologin_password: "kingduy" autologin_password: "kingduy"
@ -31,9 +33,9 @@ extra_options: |
# kiosk service vars # kiosk service vars
kiosk_service_name: "drive_check" kiosk_service_name: "drive_check"
kiosk_service_exe: "{{ service_folder }}/drive_check.sh" kiosk_service_exe: "{{ service_folder }}/drive_check_service.sh"
kiosk_service_templates: kiosk_service_templates:
- chrome_website: "http://0.0.0.0:{{ container_http_port }}" - chrome_website: "http://0.0.0.0:80"
service_name: ssd_dashboard service_name: ssd_dashboard
service_description: "SSD Health Web Dashboard" service_description: "SSD Health Web Dashboard"
user_data_dir: "" user_data_dir: ""

View File

@ -23,6 +23,28 @@ body {
cursor: pointer; cursor: pointer;
} }
a,
a:link,
a:visited {
color: #3498db; /* Default link color (blue) */
text-decoration: none;
transition: color 0.2s ease;
}
/* Visited links (distinct yet in the same palette) */
a:visited {
color: #9b59b6; /* Soft purple */
}
/* Hover state bright, engaging, and contrasting */
a:hover {
color: #1abc9c; /* Turquoisegreen */
}
/* Active state immediate feedback */
a:active {
color: #e74c3c; /* Warm red */
}
table, th, td { table, th, td {
border: 1px solid black; border: 1px solid black;

View File

@ -65,7 +65,7 @@ def get_host_stats(as_json=False):
cpu_temp = run_command(cpu_temp_command, zero_only=True) cpu_temp = run_command(cpu_temp_command, zero_only=True)
cpu_temp_stripped = re.sub(r'\u00b0C', '', cpu_temp) cpu_temp_stripped = re.sub(r'\u00b0C', '', cpu_temp)
cpu_temp_fixed = f"{cpu_temp_stripped} C" cpu_temp_fixed = f"{cpu_temp_stripped} C"
ip_address_command = "ip -o -4 ad | grep -e eth -e tun | awk '{print $2\": \" $4}'" ip_address_command = "ip -o -4 ad | grep -v -e docker -e 127.0.0.1 | awk '{print $2\": \" $4}'"
ip_addresses = run_command(ip_address_command, zero_only=True) ip_addresses = run_command(ip_address_command, zero_only=True)
time_now_command = "date +%r" time_now_command = "date +%r"
time_now = run_command(time_now_command, zero_only=True) time_now = run_command(time_now_command, zero_only=True)
@ -79,6 +79,19 @@ def get_host_stats(as_json=False):
"ip_addresses": ip_addresses, "ip_addresses": ip_addresses,
"time": time_now "time": time_now
}] }]
if check_for_battery():
battery_level_command = "acpi | grep Battery | awk {print'$3 \" \" $4'}"
battery_level = run_command(battery_level_command, zero_only=True)
stats = [{
"memory_total": total_memory,
"memory_used": used_memory,
"memory_free": free_memory,
"cpu_load": cpu_load,
"cpu_temp": cpu_temp_fixed,
"ip_addresses": ip_addresses,
"battery_level": battery_level,
"time": time_now
}]
if debug_output: if debug_output:
print("=== Current Host Stats ===") print("=== Current Host Stats ===")
print(json.dumps(stats, indent=2)) print(json.dumps(stats, indent=2))
@ -192,7 +205,10 @@ def run_command(cmd, zero_only=False):
# Split the output into lines and store it in an array # Split the output into lines and store it in an array
output_lines = [line for line in output.split('\n') if line] output_lines = [line for line in output.split('\n') if line]
# Return result # Return result
return output_lines[0] if zero_only else output_lines try:
return output_lines[0] if zero_only else output_lines
except:
return output_lines
# Function to return all drive records in database # Function to return all drive records in database
def get_all_drive_records(as_json=True): def get_all_drive_records(as_json=True):
@ -234,6 +250,14 @@ def check_serial_exists(serial):
print(serial_check) print(serial_check)
return bool(query_db(serial_check)) return bool(query_db(serial_check))
def check_for_battery():
battery_check_command = "acpi | grep Battery | awk {print'$1'}"
battery_check = run_command(battery_check_command, zero_only=True)
if battery_check == 'Battery':
return True
else:
return False
#################################################### ####################################################
### Flask Routes ### Flask Routes
#################################################### ####################################################

View File

@ -18,8 +18,7 @@
include_tasks: autologin.yaml include_tasks: autologin.yaml
# configure service-mode # configure service-mode
- name: Drive health - configure service-mode, disable autologin - name: Drive health - configure service-mode
when: install_kiosk | bool or service_only | bool
include_tasks: service_mode.yaml include_tasks: service_mode.yaml
# Install chrome kiosk # Install chrome kiosk

View File

@ -1,42 +1,43 @@
--- ---
# This will run the drive_check.sh script as a service instead of the autologin # This will run the drive_check_service.sh script as a service
- name: Service Mode - set service mode vars - name: Service Mode - set service mode vars
set_fact: set_fact:
sleep_time: ".5" sleep_time: ".5"
service_mode: true service_mode: true
- name: "Service Mode - {{ kiosk_service_name }}.service - stop service if running" - name: "Service Mode - drive_check.service - stop service if running"
ignore_errors: yes ignore_errors: yes
systemd: systemd:
name: "{{ kiosk_service_name }}.service" name: "drive_check.service"
state: stopped state: stopped
- name: "Service Mode - template drive_check.sh again" - name: "Service Mode - template drive_check_service.sh "
template: template:
src: drive_check.sh src: drive_check_service.sh
dest: "{{ service_folder }}/drive_check.sh" dest: "{{ service_folder }}/drive_check_service.sh"
mode: 0755 mode: 0755
owner: "{{ autologin_user }}" owner: "{{ autologin_user }}"
group: "{{ autologin_user }}" group: "{{ autologin_user }}"
- name: "Service Mode - template {{ kiosk_service_name }}.service" - name: "Service Mode - template drive_check_service.service"
vars: vars:
service_name: "{{ kiosk_service_name }}" service_name: "drive_check"
service_working_folder: "{{ service_folder }}" service_working_folder: "{{ service_folder }}"
service_exe: "{{ kiosk_service_exe }}" service_exe: "{{ service_folder }}/drive_check_service.sh"
template: template:
src: "service_template.service" src: "service_template.service"
dest: "/etc/systemd/system/{{ kiosk_service_name }}.service" dest: "/etc/systemd/system/drive_check.service"
mode: 0644 mode: 0644
- name: "Service Mode - {{ kiosk_service_name }} - enable and start service api and daemon reload" - name: "Service Mode - drive_check_service - enable and start service api and daemon reload"
systemd: systemd:
daemon_reload: yes daemon_reload: yes
name: "{{ kiosk_service_name }}.service" name: "drive_check.service"
state: started state: started
enabled: yes enabled: yes
- name: Service Mode - remove autologin - name: Service Mode - remove autologin
when: install_kiosk | bool or service_only | bool
include_tasks: no_autologin.yaml include_tasks: no_autologin.yaml
... ...

View File

@ -2,27 +2,17 @@
# this is a big loop # this is a big loop
# it shows SSD health data # it shows SSD health data
SERVICE_MODE = {{ service_mode }}
exec 2> /dev/null exec 2> /dev/null
while true; do while true; do
clear clear
# Show IP Info # Show IP Info
echo ===Visit the IP in your browser to view history======
if ! $SERVICE_MODE; then
echo ===Visit the IP in your browser to view history======
fi
ip -o -4 ad | grep -v -e docker -e 127.0.0.1 -e br- | awk '{print $2 "," $4}' | column -s , -t ip -o -4 ad | grep -v -e docker -e 127.0.0.1 -e br- | awk '{print $2 "," $4}' | column -s , -t
# get all disks # get all disks
DISK_LIST=$(ls -lo /dev/sd? | awk '{print $9}') DISK_LIST=$(ls -lo /dev/sd? | awk '{print $9}')
# process each disk # process each disk
IFS=$'\n' read -rd '' -a DISK_ARRAY <<< "$DISK_LIST" IFS=$'\n' read -rd '' -a DISK_ARRAY <<< "$DISK_LIST"
for DISK in "${DISK_ARRAY[@]}"; do for DISK in "${DISK_ARRAY[@]}"; do
# update active drives more frequently
if ! $SERVICE_MODE; then
echo "Issuing request to update drive database"
fi
curl -s "http://172.17.0.1:5000/refresh_active_drives"
# store smartctl data once # store smartctl data once
SMART_DATA=$(smartctl -x $DISK) SMART_DATA=$(smartctl -x $DISK)
NVME_CHECK=$(echo "$SMART_DATA" | grep "NVMe Version") NVME_CHECK=$(echo "$SMART_DATA" | grep "NVMe Version")
@ -41,35 +31,23 @@ while true; do
MODEL=$(echo "$SMART_DATA" | grep "Device Model" | cut -d ":" -f 2 | xargs) MODEL=$(echo "$SMART_DATA" | grep "Device Model" | cut -d ":" -f 2 | xargs)
SMART=$(echo "$SMART_DATA" | grep "self-assessment test result" | cut -d ":" -f 2 | xargs) SMART=$(echo "$SMART_DATA" | grep "self-assessment test result" | cut -d ":" -f 2 | xargs)
FLAVOR="SATA SSD" FLAVOR="SATA SSD"
DRIVE_EXISTS=$(curl -s "http://172.17.0.1:5000/check?serial_lookup=$SERIAL" | jq .serial_number_exists)
if ! $SERVICE_MODE; then
# Display drive data
echo "============ $DISK Disk Info - SSD: ============"
#echo "DRIVE_EXISTS: $DRIVE_EXISTS"
echo "Serial Number: $SERIAL"
echo "Model String: $MODEL"
echo "SMART Check: $SMART"
echo "Disk capacity: $CAPACITY"
echo "TB Written: $TBW TB"
if [ -z "$PLR" ] ; then
echo "Percent Lifetime Remaining data not available"
else
echo "$DISK has $PLR% lifetime remaining"
fi
echo
fi
if [ -x "$TBW"] ; then if [ -x "$TBW"] ; then
TBW="unknown" TBW="unknown"
fi fi
# database handler # Display drive data
if [ "$DRIVE_EXISTS" == "false" ] ; then echo "============ $DISK Disk Info - SSD: ============"
H_MODEL=$(echo $MODEL | sed 's/ /%20/g') #echo "DRIVE_EXISTS: $DRIVE_EXISTS"
H_FLAVOR=$(echo $FLAVOR | sed 's/ /%20/g') echo "Serial Number: $SERIAL"
H_CAPACITY=$(echo $CAPACITY | sed 's/ /%20/g') echo "Model String: $MODEL"
curl -s "http://172.17.0.1:5000/add_drive?serial=$SERIAL&model=$H_MODEL&flavor=$H_FLAVOR&capacity=$H_CAPACITY&TBW=$TBW&smart=$SMART" echo "SMART Check: $SMART"
echo "Disk capacity: $CAPACITY"
echo "TB Written: $TBW TB"
if [ -z "$PLR" ] ; then
echo "Percent Lifetime Remaining data not available"
else else
curl -s "http://172.17.0.1:5000/update_drive?serial=$SERIAL&TBW=$TBW&smart=$SMART" echo "$DISK has $PLR% lifetime remaining"
fi fi
echo
# NVMe Logic # NVMe Logic
elif [ -n "$NVME_CHECK" ] ; then elif [ -n "$NVME_CHECK" ] ; then
# Set Variables # Set Variables
@ -81,41 +59,25 @@ while true; do
CAPACITY=$(echo "$SMART_DATA" | grep "amespace 1 Size" | cut -d '[' -f 2 | sed 's/]//g') CAPACITY=$(echo "$SMART_DATA" | grep "amespace 1 Size" | cut -d '[' -f 2 | sed 's/]//g')
SMART=$(echo "$SMART_DATA" | grep "self-assessment test result" | cut -d ":" -f 2 | xargs) SMART=$(echo "$SMART_DATA" | grep "self-assessment test result" | cut -d ":" -f 2 | xargs)
FLAVOR="NVMe" FLAVOR="NVMe"
DRIVE_EXISTS=$(curl -s "http://172.17.0.1:5000/check?serial_lookup=$SERIAL" | jq .serial_number_exists) if [ -x "$TBW"] ; then
if ! $SERVICE_MODE; then TBW="unknown"
# Display Disk Info
echo "============ $DISK Disk Info - NVMe: ============"
#echo "DRIVE_EXISTS: $DRIVE_EXISTS"
echo "Serial Number: $SERIAL"
echo "Model String: $MODEL"
echo "SMART Check: $SMART"
echo "Disk capacity: $CAPACITY"
echo "TB Written: $TBW TB"
echo "NAND spare blocks: $AVAIL_SPARE"
echo
if [ -x "$TBW"] ; then
TBW="unknown"
fi
fi fi
# database handler # Display Disk Info
if [ "$DRIVE_EXISTS" == "false" ] ; then echo "============ $DISK Disk Info - NVMe: ============"
H_MODEL=$(echo $MODEL | sed 's/ /%20/g') #echo "DRIVE_EXISTS: $DRIVE_EXISTS"
H_FLAVOR=$(echo $FLAVOR | sed 's/ /%20/g') echo "Serial Number: $SERIAL"
H_CAPACITY=$(echo $CAPACITY | sed 's/ /%20/g') echo "Model String: $MODEL"
curl -s "http://172.17.0.1:5000/add_drive?serial=$SERIAL&model=$H_MODEL&flavor=$H_FLAVOR&capacity=$H_CAPACITY&TBW=$TBW&smart=$SMART" echo "SMART Check: $SMART"
else echo "Disk capacity: $CAPACITY"
curl -s "http://172.17.0.1:5000/update_drive?serial=$SERIAL&TBW=$TBW&smart=$SMART" echo "TB Written: $TBW TB"
fi echo "NAND spare blocks: $AVAIL_SPARE"
echo
fi fi
else else
if ! $SERVICE_MODE; then echo "Skipping $DISK, not SATA SSD or NVMe"
echo "Skipping $DISK, not SATA SSD or NVMe"
fi
fi fi
done done
# wait {{ sleep_time }} seconds, loop again # wait {{ sleep_time }} seconds, loop again
if ! $SERVICE_MODE; then echo "Sleeping {{ sleep_time }} seconds"
echo "Sleeping {{ sleep_time }} seconds"
fi
sleep {{ sleep_time }} sleep {{ sleep_time }}
done done

View File

@ -0,0 +1,74 @@
#!/bin/bash
# this is a big loop to handle the database
exec 2> /dev/null
while true; do
# update active drives more frequently
curl -s "http://172.17.0.1:5000/refresh_active_drives"
clear
# get all disks
DISK_LIST=$(ls -lo /dev/sd? | awk '{print $9}')
# process each disk
IFS=$'\n' read -rd '' -a DISK_ARRAY <<< "$DISK_LIST"
for DISK in "${DISK_ARRAY[@]}"; do
# store smartctl data once
SMART_DATA=$(smartctl -x $DISK)
NVME_CHECK=$(echo "$SMART_DATA" | grep "NVMe Version")
SSD_CHECK=$(echo "$SMART_DATA" | grep "Rotation Rate" | grep "Solid State")
# if either SATA SSD or NVMe
if [ -n "$NVME_CHECK" ] || [ -n "$SSD_CHECK" ]; then
BLOCK_SIZE=$(fdisk -l $DISK | grep 'Sector size' | awk '{print $4}' )
# SATA Logic
if [ -n "$SSD_CHECK" ] ; then
# Set Variables
TBW=$(echo "$SMART_DATA" | grep "Logical Sectors Written" | \
awk -v BLOCK_SIZE="$BLOCK_SIZE" '{print $4 * BLOCK_SIZE / (2 ^ 40)}')
PLR=$(echo "$SMART_DATA" | grep Percent_Lifetime_Remain | awk '{print $4}')
CAPACITY=$(echo "$SMART_DATA" | grep "User Capacity" | cut -d '[' -f 2 | sed 's/]//g')
SERIAL=$(echo "$SMART_DATA" | grep "Serial Number" | cut -d ":" -f 2 | xargs)
MODEL=$(echo "$SMART_DATA" | grep "Device Model" | cut -d ":" -f 2 | xargs)
SMART=$(echo "$SMART_DATA" | grep "self-assessment test result" | cut -d ":" -f 2 | xargs)
FLAVOR="SATA SSD"
DRIVE_EXISTS=$(curl -s "http://172.17.0.1:5000/check?serial_lookup=$SERIAL" | jq .serial_number_exists)
if [ -x "$TBW"] ; then
TBW="unknown"
fi
# database handler
if [ "$DRIVE_EXISTS" == "false" ] ; then
H_MODEL=$(echo $MODEL | sed 's/ /%20/g')
H_FLAVOR=$(echo $FLAVOR | sed 's/ /%20/g')
H_CAPACITY=$(echo $CAPACITY | sed 's/ /%20/g')
curl -s "http://172.17.0.1:5000/add_drive?serial=$SERIAL&model=$H_MODEL&flavor=$H_FLAVOR&capacity=$H_CAPACITY&TBW=$TBW&smart=$SMART"
else
curl -s "http://172.17.0.1:5000/update_drive?serial=$SERIAL&TBW=$TBW&smart=$SMART"
fi
# NVMe Logic
elif [ -n "$NVME_CHECK" ] ; then
# Set Variables
MODEL=$(echo "$SMART_DATA" | grep "Model Number" | cut -d ":" -f 2 | xargs)
SERIAL=$(echo "$SMART_DATA" | grep "Serial Number" | cut -d ":" -f 2 | xargs)
TBW=$(echo "$SMART_DATA" | grep "Data Units Written" | sed 's/,//g' | \
awk -v BLOCK_SIZE="$BLOCK_SIZE" '{print $4 * BLOCK_SIZE / (2 ^ 30)}')
AVAIL_SPARE=$(echo "$SMART_DATA" | grep "Available Spare:" | cut -d ':' -f 2 | xargs)
CAPACITY=$(echo "$SMART_DATA" | grep "amespace 1 Size" | cut -d '[' -f 2 | sed 's/]//g')
SMART=$(echo "$SMART_DATA" | grep "self-assessment test result" | cut -d ":" -f 2 | xargs)
FLAVOR="NVMe"
DRIVE_EXISTS=$(curl -s "http://172.17.0.1:5000/check?serial_lookup=$SERIAL" | jq .serial_number_exists)
if [ -x "$TBW"] ; then
TBW="unknown"
fi
# database handler
if [ "$DRIVE_EXISTS" == "false" ] ; then
H_MODEL=$(echo $MODEL | sed 's/ /%20/g')
H_FLAVOR=$(echo $FLAVOR | sed 's/ /%20/g')
H_CAPACITY=$(echo $CAPACITY | sed 's/ /%20/g')
curl -s "http://172.17.0.1:5000/add_drive?serial=$SERIAL&model=$H_MODEL&flavor=$H_FLAVOR&capacity=$H_CAPACITY&TBW=$TBW&smart=$SMART"
else
curl -s "http://172.17.0.1:5000/update_drive?serial=$SERIAL&TBW=$TBW&smart=$SMART"
fi
fi
fi
done
# sleep and loop again
sleep 0.5
done