Merge feature/pbs-nuc-drive-map: revamped PBS/NUC art + post-outage bay remap
Lint / Shell (shellcheck) (push) Failing after 6s

This commit is contained in:
2026-07-18 15:45:39 -04:00
+182 -32
View File
@@ -575,34 +575,174 @@ generate_micro_layout() {
# #
# Side effects: Calls build_drive_map() to populate DRIVE_MAP # Side effects: Calls build_drive_map() to populate DRIVE_MAP
#------------------------------------------------------------------------------ #------------------------------------------------------------------------------
# ══ driveAtlas :: color + glyph helpers ═══════════════════════════════════════
# da_repeat GLYPH N -- print GLYPH N times (multibyte-safe)
da_repeat() {
local out='' i
for ((i = 0; i < $2; i++)); do out+="$1"; done
printf '%s' "$out"
}
# da_color_init -- set the driveAtlas "hardware schematic" palette globals.
# Structure is steel grey; color encodes STATE only (green=ok, amber/yellow,
# red=hot, blue=cool). All vars are empty strings when color is off, so
# rendering is byte-identical to pure monochrome.
# USE_COLOR=true force on (depth still auto-picked)
# USE_COLOR=false force off
# unset/auto auto: off if NO_COLOR set, stdout not a tty,
# or terminal reports < 256 colors
# Depth: truecolor only when COLORTERM=truecolor/24bit, else 256, else 16.
da_color_init() {
DA_ST='' DA_HL='' DA_DIM='' DA_OK='' DA_YEL='' DA_HOT='' DA_BLU='' DA_CYN=''
DA_SR='' DA_RST=''
local mode="${USE_COLOR:-auto}" ncolors
ncolors="$(tput colors 2>/dev/null)"; ncolors="${ncolors:-0}"
case "$mode" in
false) return 0 ;;
auto)
[ -n "${NO_COLOR-}" ] && return 0
[ ! -t 1 ] && return 0
[ "$ncolors" -lt 256 ] 2>/dev/null && return 0
;;
esac
if [ "${COLORTERM-}" = "truecolor" ] || [ "${COLORTERM-}" = "24bit" ]; then
DA_ST=$'\033[38;2;138;144;151m' # steel mid #8a9097
DA_HL=$'\033[38;2;200;205;211m' # steel bright #c8cdd3
DA_DIM=$'\033[38;2;86;93;102m' # steel shadow #565d66
DA_OK=$'\033[38;2;51;209;122m' # LED green #33d17a
DA_YEL=$'\033[38;2;246;196;69m' # LED amber #f6c445
DA_HOT=$'\033[38;2;255;77;77m' # LED red #ff4d4d
DA_BLU=$'\033[38;2;77;184;255m' # cool blue #4db8ff
DA_CYN=$'\033[38;2;77;216;230m' # cyan accent #4dd8e6
elif [ "$ncolors" -ge 256 ] 2>/dev/null; then
DA_ST=$'\033[38;5;245m' DA_HL=$'\033[38;5;252m' DA_DIM=$'\033[38;5;238m'
DA_OK=$'\033[38;5;40m' DA_YEL=$'\033[38;5;214m' DA_HOT=$'\033[38;5;196m'
DA_BLU=$'\033[38;5;39m' DA_CYN=$'\033[38;5;51m'
else
DA_ST=$'\033[37m' DA_HL=$'\033[1;37m' DA_DIM=$'\033[90m'
DA_OK=$'\033[32m' DA_YEL=$'\033[33m' DA_HOT=$'\033[31m'
DA_BLU=$'\033[34m' DA_CYN=$'\033[36m'
fi
DA_RST=$'\033[0m'
DA_SR="${DA_RST}${DA_ST}" # "return to structure": reset attrs, steel mid
}
# da_gauge PCT WIDTH -- smooth eighth-block capacity gauge over a dim track,
# e.g. "da_gauge 62 15" -> "████████▋░░░░░░". Color it by level at call site
# (DA_OK < 70, DA_YEL < 90, DA_HOT >= 90); pad-then-color as usual.
da_gauge() {
local pct="${1:-0}" w="${2:-10}" full rem cells out='' i
local eighth=('' '▏' '▎' '▍' '▌' '▋' '▊' '▉')
[ "$pct" -lt 0 ] 2>/dev/null && pct=0
[ "$pct" -gt 100 ] 2>/dev/null && pct=100
full=$(( pct * w / 100 )); rem=$(( (pct * w * 8 / 100) % 8 )); cells=$full
for ((i = 0; i < full; i++)); do out+='█'; done
if [ "$rem" -gt 0 ] && [ "$full" -lt "$w" ]; then
out+="${eighth[rem]}"; cells=$((cells + 1))
fi
for ((i = cells; i < w; i++)); do out+='░'; done
printf '%s' "$out"
}
# ══ driveAtlas :: NUC5i5RYB/RYH chassis + drive-slot map ══════════════════════
# 88-col cabinet-projection rendering with live device names from the global
# associative array DRIVE_MAP (keys: int-os usb-fl usb-fr usb-rt usb-rb).
# Occupied ports: solid light boxes, green LED bar + device. EMPTY ports:
# dashed ghost boxes, dim. Alignment: every variable field is padded to its
# final width BEFORE color codes wrap it; borders are literal strings.
generate_nuc_layout() { generate_nuc_layout() {
local hostname="$1"
build_drive_map build_drive_map
local host="${1:-unknown}"
local model=""
if command -v maybe_sudo >/dev/null 2>&1; then
model="$(maybe_sudo dmidecode -s baseboard-product-name 2>/dev/null | head -n1)"
fi
[ -n "$model" ] || model="NUC5i5RYB/RYH"
local nuc_model da_color_init
nuc_model="$(maybe_sudo dmidecode -s baseboard-product-name 2>/dev/null | head -n1)"
[[ -z "$nuc_model" ]] && nuc_model="Intel NUC"
printf "┌─────────────────────────────────────────────────────────────┐\n" # fixed-width header fields (pad/truncate so the frame never moves)
printf "│ %-57s │\n" "$hostname - $nuc_model (Mini PC)" local hostp modelp
printf "│ │\n" printf -v hostp '%-22.22s' "$host"
printf "│ Storage Controllers: │\n" printf -v modelp '%-14.14s' "$model"
while IFS= read -r ctrl; do
[[ -n "$ctrl" ]] && printf "│ %-59.59s│\n" "$ctrl" # per-USB-slot pieces: device field (6 cols: LED bar + 5-char name),
done < <(get_storage_controllers) # box side glyph, label color, border dash -- ghost styling for EMPTY
printf "│ │\n" local fl fr rt rb io fls frs rts rbs flc frc rtc rbc fld frd rtd rbd
printf "│ Internal 2.5\" SATA (OS / boot): │\n" local flt frt flb frb rtt rbb
printf "│ ┌────────────────┐ │\n" local pre key col v p
printf "│ │ %-14s │ │\n" "${DRIVE_MAP[int-os]:-EMPTY}" for pre in fl:usb-fl:B fr:usb-fr:Y rt:usb-rt:B rb:usb-rb:B; do
printf "│ └────────────────┘ │\n" key="${pre#*:}"; key="${key%:*}"; col="${pre##*:}"; pre="${pre%%:*}"
printf "│ │\n" v="${DRIVE_MAP[$key]:-EMPTY}"
printf "│ Rear USB 3.0 (stacked): │\n" if [ "$v" = "EMPTY" ]; then
printf "│ ┌─────────────────────────┐ │\n" printf -v p ' %-5.5s' "$v"
printf "│ │ TOP (3-4): %-9s │ │\n" "${DRIVE_MAP[usb-top]:-EMPTY}" printf -v "$pre" '%s' "${DA_DIM}${p}${DA_SR}"
printf "│ ├─────────────────────────┤ │\n" printf -v "${pre}s" '%s' "${DA_DIM}${DA_SR}"
printf "│ │ BOTTOM (3-3): %-9s │ │\n" "${DRIVE_MAP[usb-bot]:-EMPTY}" printf -v "${pre}c" '%s' "${DA_DIM}"
printf "│ └─────────────────────────┘ │\n" printf -v "${pre}d" '%s' '╌'
printf "└─────────────────────────────────────────────────────────────┘\n" else
printf -v p '%-5.5s' "$v"
printf -v "$pre" '%s' "${DA_OK}${p}${DA_SR}"
printf -v "${pre}s" '%s' '│'
[ "$col" = "Y" ] && printf -v "${pre}c" '%s' "${DA_YEL}" \
|| printf -v "${pre}c" '%s' "${DA_BLU}"
printf -v "${pre}d" '%s' '─'
fi
done
# box edges (11 cols; front tops carry the callout stem junction)
flt="$(da_repeat "$fld" 5)$(da_repeat "$fld" 5)"
frt="$(da_repeat "$frd" 10)"
flb="$(da_repeat "$fld" 11)"; frb="$(da_repeat "$frd" 11)"
rtt="$(da_repeat "$rtd" 11)"; rbb="$(da_repeat "$rbd" 11)"
[ "$fld" = '╌' ] && { flt="${DA_DIM}${flt}${DA_SR}"; flb="${DA_DIM}${flb}${DA_SR}"; }
[ "$frd" = '╌' ] && { frt="${DA_DIM}${frt}${DA_SR}"; frb="${DA_DIM}${frb}${DA_SR}"; }
[ "$rtd" = '╌' ] && rtt="${DA_DIM}${rtt}${DA_SR}"
[ "$rbd" = '╌' ] && rbb="${DA_DIM}${rbb}${DA_SR}"
# internal 2.5" SATA (int-os)
v="${DRIVE_MAP[int-os]:-EMPTY}"
if [ "$v" = "EMPTY" ]; then
printf -v p ' %-5.5s' "$v"; io="${DA_DIM}${p}${DA_SR}"
else
printf -v p '%-5.5s' "$v"; io="${DA_OK}${p}${DA_SR}"
fi
printf "${DA_ST}${DA_HL}┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓${DA_SR}${DA_RST}\n"
printf "${DA_ST}${DA_HL}${DA_SR} ${DA_CYN}driveAtlas${DA_SR} :: ${DA_HL}%s${DA_SR} %s - 115x111x49 mm - tall (RYH) kit ${DA_HL}${DA_SR}${DA_RST}\n" "$hostp" "$modelp"
printf "${DA_ST}${DA_HL}┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛${DA_SR}${DA_RST}\n"
printf " \n"
printf "${DA_ST} FRONT 3/4 ── cool intake side REAR ── hot exhaust side ${DA_RST}\n"
printf " \n"
printf "${DA_ST} ${DA_DIM}▄▄▄▄▄▄ ▄▄▄▄▄▄${DA_SR} < CPU fan vents ${DA_RST}\n"
printf "${DA_ST} ${DA_HOT} v v v hot air${DA_SR} ${DA_RST}\n"
printf "${DA_ST} ${DA_HL}┌──────────────────────────┐${DA_SR} ${DA_HL}╭────────────────────────────────╮${DA_SR} ${DA_RST}\n"
printf "${DA_ST} ${DA_HL}${DA_SR}${DA_DIM}░░░─░░░░░░░─░░░░░░░░─░░░░░${DA_SR}${DA_HL}╱│${DA_SR} ${DA_HL}${DA_SR} ┌%s┐ ${DA_HL}${DA_SR} ${DA_RST}\n" "$rtt"
printf "${DA_ST} ${DA_HL}${DA_SR}${DA_DIM}${DA_SR}${DA_OK}(o)${DA_SR} pwr ${DA_DIM}░░─░░░░░─░░░░░░░─${DA_SR}${DA_HL}${DA_SR}${DA_DIM}${DA_SR}${DA_HL}${DA_SR} ${DA_HL}${DA_SR} (o) ▐═▌ ▐╬▌ %s %s3-4 BLUE${DA_SR} %s ▐▄▌ ${DA_HL}${DA_SR} ${DA_RST}\n" "$rts" "$rtc" "$rts"
printf "${DA_ST} ${DA_HL}┌──────────────────────────┐${DA_SR}${DA_DIM}▒▒${DA_SR}${DA_HL}${DA_SR} ${DA_HL}${DA_SR} DC mDP GbE %s %s %smHDMI${DA_HL}${DA_SR} ${DA_RST}\n" "$rts" "$rt" "$rts"
printf "${DA_ST} ${DA_HL}${DA_SR} ╌ brushed aluminum band ╌${DA_HL}${DA_SR}${DA_DIM}${DA_SR}${DA_HL}${DA_SR} ${DA_HL}${DA_SR} ├───────────┤ ${DA_HL}${DA_SR} ${DA_RST}\n"
printf "${DA_ST} ${DA_HL}${DA_SR} ${DA_BLU}▐███▌${DA_SR} ${DA_YEL}▐███▌${DA_SR} (o) . ${DA_HL}│╱${DA_SR} ${DA_HL}${DA_SR} %s %s3-3 BLUE${DA_SR} %s ${DA_HL}${DA_SR}${DA_HOT}< bakes in${DA_SR} ${DA_RST}\n" "$rbs" "$rbc" "$rbs"
printf "${DA_ST} ${DA_HL}└────┬───────┬─────────────┘${DA_SR} ${DA_HL}${DA_SR} %s %s %s ${DA_HL}${DA_SR}${DA_HOT} CPU exhaust${DA_SR}${DA_RST}\n" "$rbs" "$rb" "$rbs"
printf "${DA_ST} │ │ audio IR ${DA_HL}${DA_SR} └%s┘ ${DA_HL}${DA_SR} ${DA_RST}\n" "$rbb"
printf "${DA_ST} ┌%s┐┌%s┐ ${DA_HL}╰────────────────────────────────╯${DA_SR} ${DA_RST}\n" "$flt" "$frt"
printf "${DA_ST} %s %s3-2 BLUE${DA_SR} %s%s%s3-1? YELLOW${DA_SR}%s ${DA_DIM}▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀${DA_SR} ${DA_RST}\n" "$fls" "$flc" "$fls" "$frs" "$frc" "$frs"
printf "${DA_ST} %s %s %s%s %s %s ${DA_RST}\n" "$fls" "$fl" "$fls" "$frs" "$fr" "$frs"
printf "${DA_ST} %s USB3 data %s%s charging %s ${DA_RST}\n" "$fls" "$fls" "$frs" "$frs"
printf "${DA_ST} └%s┘└%s┘ ${DA_RST}\n" "$flb" "$frb"
printf " \n"
printf "${DA_ST} ${DA_HL}UNDERSIDE / INSIDE${DA_SR} ${DA_HL}NOTES${DA_SR} ${DA_RST}\n"
printf "${DA_ST} ┌──────────────────────────────────┐ ${DA_RST}\n"
printf "${DA_ST} │ internal 2.5\" SATA ── mounted │ - all 4 USB3 jacks hang off ONE xHCI ${DA_RST}\n"
printf "${DA_ST} │ INVERTED in the bottom lid │ controller ── one shared kernel bus ${DA_RST}\n"
printf "${DA_ST} │ %s < int-os - OS / boot │ - M.2 2280 slot ── flat on mainboard ${DA_RST}\n" "$io"
printf "${DA_ST}${DA_DIM} ╌╌╌╌╌ flex cable to mainboard ╌╌ ${DA_SR}│ - \"?\" = port 3-1 id inferred (unverified) ${DA_RST}\n"
printf "${DA_ST} └──────────────────────────────────┘ ${DA_RST}\n"
printf "${DA_ST} ${DA_DIM}o feet o────── VESA ──────o feet o${DA_SR} ${DA_RST}\n"
# optional flourish: controller inventory from the existing helper
if command -v get_storage_controllers >/dev/null 2>&1; then
printf '\n %sSTORAGE CONTROLLERS%s\n' "${DA_HL}" "${DA_RST}"
get_storage_controllers 2>/dev/null | sed "s/^/ /"
fi
} }
#------------------------------------------------------------------------------ #------------------------------------------------------------------------------
@@ -778,18 +918,28 @@ declare -A SERVER_MAPPINGS=(
# pbs # pbs
# Intel NUC5i5RYB - Proxmox Backup Server # Intel NUC5i5RYB - Proxmox Backup Server
# int-os : internal 2.5\" SATA SSD (Samsung MZNLN128) - OS/boot, controller 00:1f.2 # int-os : internal 2.5\" SATA SSD (Samsung MZNLN128) - OS/boot, controller 00:1f.2
# usb-top : TOP rear USB 3.0 NVMe (Patriot P300 / JMicron bridge) - halfTbNVMeMirror # usb-fl : FRONT-LEFT USB 3.0 (blue) NVMe (Patriot P300 / JMicron bridge) - halfTbNVMeMirror
# usb-bot : BOTTOM rear USB 3.0 NVMe (Patriot P300 / JMicron bridge) - halfTbNVMeMirror # usb-fr : FRONT-RIGHT USB 3.0 (yellow, always-on charging) - normally empty
# usb-rt : REAR-TOP USB 3.0 (blue) - now empty
# usb-rb : REAR-BOTTOM USB 3.0 (blue) NVMe (Patriot P300 / JMicron bridge) - halfTbNVMeMirror
# #
# The two USB bridges report an IDENTICAL serial, so these drives are keyed by USB # The two USB bridges report an IDENTICAL serial, so these drives are keyed by USB
# *port path* (usb-0:N = kernel bus3 port N), NOT by-id/serial. Physical top/bottom # *port path* (usb-0:N = kernel bus3 port N), NOT by-id/serial.
# confirmed by an LED blink test on 2026-07-15: # Port jacks (LED-blink verified 2026-07-15; front-left re-verified 2026-07-18):
# TOP rear jack = port 3-4 (usb-0:4) = fw serial P300WCBA24090617615 (label 022411042502235) # REAR-TOP jack = port 3-4 (usb-0:4) [blue]
# BOTTOM rear jack = port 3-3 (usb-0:3) = fw serial P300WCBA24090617536 # REAR-BOTTOM jack = port 3-3 (usb-0:3) [blue] = fw serial P300WCBA24090617536
# FRONT-LEFT jack = port 3-2 (usb-0:2) [blue] = fw serial P300WCBA24090617615
# FRONT-RIGHT jack = port 3-1 (usb-0:1) [yellow] (port # unverified - jack empty)
#
# 2026-07-18: after a power outage the ...615 drive dropped to 0B / hardware-error in the
# hot stacked REAR-TOP bay; moving it to FRONT-LEFT (3-2) revived it. Root cause was
# thermal - the two NVMe USB bridges sat flush against each other in the rear stack.
["pbs"]=" ["pbs"]="
pci-0000:00:1f.2-ata-4 int-os pci-0000:00:1f.2-ata-4 int-os
pci-0000:00:14.0-usb-0:4:1.0-scsi-0:0:0:0 usb-top pci-0000:00:14.0-usb-0:2:1.0-scsi-0:0:0:0 usb-fl
pci-0000:00:14.0-usb-0:3:1.0-scsi-0:0:0:0 usb-bot pci-0000:00:14.0-usb-0:1:1.0-scsi-0:0:0:0 usb-fr
pci-0000:00:14.0-usb-0:4:1.0-scsi-0:0:0:0 usb-rt
pci-0000:00:14.0-usb-0:3:1.0-scsi-0:0:0:0 usb-rb
" "
) )