back to scripts

whatsapp-health-check.sh

bash 151 lines secrets redacted

Runs a health check and delivers the result to WhatsApp.

Note Live script from my home-lab server. Tokens, IDs, phone numbers and other secrets have been replaced with placeholders like <WHATSAPP_GROUP_ID> — everything else is the real, running code.
#!/usr/bin/env bash
set -u

timestamp=$(date '+%Y-%m-%d %H:%M %Z')
host=$(hostname)
uptime_text=$(uptime -p 2>/dev/null || echo unknown)
load_text=$(awk '{print $1 ", " $2 ", " $3}' /proc/loadavg 2>/dev/null || echo unknown)


power_state="unknown"
power_detail="power status unavailable"
adapters=""
batteries=""
if command -v on_ac_power >/dev/null 2>&1; then
  if on_ac_power >/dev/null 2>&1; then
    power_state="ac"
  else
    power_rc=$?
    if [ "$power_rc" -eq 1 ]; then
      power_state="battery"
    fi
  fi
fi
for supply in /sys/class/power_supply/*; do
  [ -d "$supply" ] || continue
  name=$(basename "$supply")
  type=$(cat "$supply/type" 2>/dev/null || echo unknown)
  case "$type" in
    Mains|USB|USB_C|USB_PD)
      online=$(cat "$supply/online" 2>/dev/null || echo unknown)
      adapters="${adapters}${name} online=${online}; "
      ;;
    Battery)
      status=$(cat "$supply/status" 2>/dev/null || echo unknown)
      capacity=$(cat "$supply/capacity" 2>/dev/null || echo unknown)
      batteries="${batteries}${name} ${status} ${capacity}%; "
      ;;
  esac
done
if [ "$power_state" = unknown ] && printf '%s' "$adapters" | grep -q 'online=1'; then
  power_state="ac"
elif [ "$power_state" = unknown ] && printf '%s' "$batteries" | grep -qi 'Discharging'; then
  power_state="battery"
fi
case "$power_state" in
  ac) power_detail="AC power (${adapters:-adapter unknown}${batteries:+ battery: $batteries})" ;;
  battery) power_detail="ON BATTERY - MAJOR ALERT: faulty charge port risk; server may lose power (${adapters:-adapter unknown}${batteries:+ battery: $batteries})" ;;
  *) power_detail="UNKNOWN (${adapters:-adapter unknown}${batteries:+ battery: $batteries})" ;;
esac

if systemctl is-active --quiet docker; then
  docker_state=active
else
  docker_state=NOT_ACTIVE
fi

running=$(timeout 10s docker ps -q 2>/dev/null | wc -l)
total=$(timeout 10s docker ps -aq 2>/dev/null | wc -l)
problem_containers=$(
  {
    timeout 10s docker ps -a --filter status=exited --filter status=dead --filter status=restarting \
      --format '{{.Names}} ({{.Status}})' 2>/dev/null
    timeout 10s docker ps --filter health=unhealthy --format '{{.Names}} ({{.Status}})' 2>/dev/null
  } | sed '/^$/d' | sort -u
)

failed_units=$(systemctl --failed --no-legend --plain 2>/dev/null | sed '/^$/d' | wc -l)
root_usage=$(df -hP / | awk 'NR==2 {print $5 " used, " $4 " available"}')

mount_status() {
  local path=$1
  if timeout 5s mountpoint -q "$path"; then
    local space
    space=$(timeout 10s df -hP "$path" 2>/dev/null | awk 'NR==2 {print $5 " used, " $4 " available"}')
    echo "mounted (${space:-capacity unavailable})"
  else
    echo "NOT MOUNTED"
  fi
}

nas_status=$(mount_status /mnt/nas)
jelly_status=$(mount_status /mnt/jelly)

gateway_state=$(systemctl --user is-active openclaw-gateway.service 2>/dev/null || echo inactive)

gpu_line=$(timeout 10s nvidia-smi \
  --query-gpu=name,driver_version,memory.used,memory.total,temperature.gpu \
  --format=csv,noheader 2>/dev/null | head -1)
if [ -z "$gpu_line" ]; then
  gpu_line="unavailable"
fi

ollama_processor=$(timeout 10s docker exec ollama ollama ps 2>/dev/null | awk 'NR==2 {print $5 " " $6}')
if [ -z "$ollama_processor" ]; then
  ollama_processor="idle or unavailable"
fi

healthy_named=$(
  timeout 10s docker ps --format '{{.Names}}|{{.Status}}' 2>/dev/null |
    awk -F'|' '
      $1 == "homeassistant" || $1 == "jellyfin" || $1 == "gluetun" ||
      $1 == "pihole" || $1 == "openwebui" {
        print "- " $1 ": " $2
      }
    '
)

if [ "$power_state" = battery ]; then
  overall="MAJOR ALERT"
elif [ "$docker_state" = active ] &&
   [ "$running" -eq "$total" ] &&
   [ "$failed_units" -eq 0 ] &&
   [ -z "$problem_containers" ] &&
   timeout 5s mountpoint -q /mnt/nas &&
   timeout 5s mountpoint -q /mnt/jelly &&
   [ "$gateway_state" = active ]; then
  overall=HEALTHY
else
  overall="NEEDS ATTENTION"
fi

cat <<EOF
SYSTEM HEALTH CHECK
Generated: $timestamp
Host: $host

Overall: $overall
- Uptime: $uptime_text
- Load averages: $load_text
- Power: $power_detail
- Docker: $docker_state; $running/$total containers running
- Failed systemd units: $failed_units
- OpenClaw WhatsApp gateway: $gateway_state
- Server disk: $root_usage
- /mnt/nas: $nas_status
- /mnt/jelly: $jelly_status
- GPU: $gpu_line
- Ollama processor: $ollama_processor
${healthy_named:-}
EOF

if [ -n "$problem_containers" ]; then
  echo
  echo "Container problems:"
  echo "$problem_containers" | sed 's/^/- /'
fi

if timeout 10s df -P /mnt/nas 2>/dev/null | awk 'NR==2 {gsub("%","",$5); exit !($5 >= 90)}'; then
  echo
  echo "Warning: NAS storage is at or above 90% usage."
fi

back to scripts