Fresh start - excluded large ROM JSON files

This commit is contained in:
OpenClaw Agent
2026-04-11 09:45:12 -05:00
commit 5deb387aa6
395 changed files with 47744 additions and 0 deletions

View File

@@ -0,0 +1,21 @@
#!/usr/bin/env bash
# Recent UniFi alarms/alerts
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "$SCRIPT_DIR/unifi-api.sh"
LIMIT="${1:-20}"
data=$(unifi_get "/api/s/$UNIFI_SITE/stat/alarm")
echo "$data" | jq -r --argjson limit "$LIMIT" '
["TIME", "KEY", "MESSAGE", "AP/DEVICE"],
["----", "---", "-------", "----------"],
(.data[:$limit][] | [
(.datetime | strftime("%Y-%m-%d %H:%M")),
.key,
(.msg // "N/A"),
(.ap_name // .gw_name // .sw_name // "N/A")
]) | @tsv
' | column -t -s $'\t'

View File

@@ -0,0 +1,28 @@
#!/usr/bin/env bash
# List active UniFi clients
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "$SCRIPT_DIR/unifi-api.sh"
FORMAT="${1:-table}"
data=$(unifi_get "/api/s/$UNIFI_SITE/stat/sta")
if [ "$FORMAT" = "json" ]; then
echo "$data"
else
# Table format
echo "$data" | jq -r '
["HOSTNAME", "IP", "MAC", "AP", "SIGNAL", "RX/TX (Mbps)"],
["--------", "--", "---", "--", "------", "------------"],
(.data[] | [
(.hostname // .name // "Unknown"),
.ip,
.mac,
(.ap_mac // "N/A")[0:17],
((.signal // 0 | tostring) + " dBm"),
(((.rx_rate // 0) / 1000 | floor | tostring) + "/" + ((.tx_rate // 0) / 1000 | floor | tostring))
]) | @tsv
' | column -t -s $'\t'
fi

View File

@@ -0,0 +1,285 @@
#!/bin/bash
# UniFi Network Dashboard - Comprehensive overview
# Usage: bash dashboard.sh [json]
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "$SCRIPT_DIR/unifi-api.sh"
OUTPUT_FILE="$HOME/clawd/memory/bank/unifi-inventory.md"
# Disable strict mode for dashboard (we handle errors gracefully)
set +e
OUTPUT_JSON="${1:-}"
# Ensure output directory exists
mkdir -p "$(dirname "$OUTPUT_FILE")"
# Create shared session for all requests
export UNIFI_COOKIE_FILE=$(mktemp)
trap "rm -f '$UNIFI_COOKIE_FILE'" EXIT
unifi_login "$UNIFI_COOKIE_FILE"
# Fetch all data upfront (using full API path format)
HEALTH=$(unifi_get "/api/s/$UNIFI_SITE/stat/health")
DEVICES=$(unifi_get "/api/s/$UNIFI_SITE/stat/device")
CLIENTS=$(unifi_get "/api/s/$UNIFI_SITE/stat/sta")
PORTFWD=$(unifi_get "/api/s/$UNIFI_SITE/rest/portforward")
FWRULES=$(unifi_get "/api/s/$UNIFI_SITE/rest/firewallrule")
NETWORKS=$(unifi_get "/api/s/$UNIFI_SITE/rest/networkconf")
WLANS=$(unifi_get "/api/s/$UNIFI_SITE/rest/wlanconf")
ALARMS=$(unifi_get "/api/s/$UNIFI_SITE/stat/alarm")
ROUTING=$(unifi_get "/api/s/$UNIFI_SITE/stat/routing")
SYSINFO=$(unifi_get "/api/s/$UNIFI_SITE/stat/sysinfo")
# Debug: Dump all JSON to a file for troubleshooting
jq -n \
--argjson health "$HEALTH" \
--argjson devices "$DEVICES" \
--argjson clients "$CLIENTS" \
--argjson portforward "$PORTFWD" \
--argjson networks "$NETWORKS" \
--argjson wlans "$WLANS" \
'{health: $health, devices: $devices, clients: $clients, networks: $networks, wlans: $wlans}' \
> dashboard_debug_dump.json 2>/dev/null
if [ "$OUTPUT_JSON" = "json" ]; then
jq -n \
--argjson health "$HEALTH" \
--argjson devices "$DEVICES" \
--argjson clients "$CLIENTS" \
--argjson portforward "$PORTFWD" \
--argjson firewall "$FWRULES" \
--argjson networks "$NETWORKS" \
--argjson wlans "$WLANS" \
--argjson alarms "$ALARMS" \
--argjson routing "$ROUTING" \
--argjson sysinfo "$SYSINFO" \
'{health: $health, devices: $devices, clients: $clients, portforward: $portforward, firewall: $firewall, networks: $networks, wlans: $wlans, alarms: $alarms, routing: $routing, sysinfo: $sysinfo}'
exit 0
fi
# Generate dashboard output and save to file
{
echo "╔══════════════════════════════════════════════════════════════════════════════╗"
echo "║ UNIFI NETWORK DASHBOARD ║"
echo "╚══════════════════════════════════════════════════════════════════════════════╝"
echo ""
# System Info
echo "┌─────────────────────────────────────────────────────────────────────────────┐"
echo "│ SYSTEM INFO │"
echo "└─────────────────────────────────────────────────────────────────────────────┘"
echo "$SYSINFO" | jq -r '
.data[0] |
"Version: \(.version // "N/A") | Hostname: \(.hostname // "N/A") | Timezone: \(.timezone // "N/A")"
' 2>/dev/null || echo " Unable to fetch system info"
echo ""
# Health Overview
echo "┌─────────────────────────────────────────────────────────────────────────────┐"
echo "│ HEALTH STATUS │"
echo "└─────────────────────────────────────────────────────────────────────────────┘"
echo "$HEALTH" | jq -r '
.data[] |
"\(.subsystem | ascii_upcase): \(if .status == "ok" then "OK" else "\(.status)" end) | Gateways: \(.gw_mac // "N/A") | Clients: \(.num_user // 0)"
' 2>/dev/null | head -10
echo ""
# Devices
echo "┌─────────────────────────────────────────────────────────────────────────────┐"
echo "│ UNIFI DEVICES │"
echo "└─────────────────────────────────────────────────────────────────────────────┘"
printf "%-20s %-10s %-15s %-8s %-10s %-8s\n" "NAME" "MODEL" "IP" "STATE" "UPTIME" "CLIENTS"
printf "%-20s %-10s %-15s %-8s %-10s %-8s\n" "----" "-----" "--" "-----" "------" "-------"
echo "$DEVICES" | jq -r '
.data[] |
[
((.name // .hostname // .mac)[:20]),
((.model // "N/A")[:10]),
((.ip // "N/A")[:15]),
(if .state == 1 then "OK" else "FAIL" end),
(((.uptime // 0) / 3600 | floor | tostring + "h")),
((.num_sta // 0 | tostring))
] | @tsv
' 2>/dev/null | while IFS=$'\t' read -r name model ip state uptime clients; do
printf "%-20s %-10s %-15s %-8s %-10s %-8s\n" "$name" "$model" "$ip" "$state" "$uptime" "$clients"
done
echo ""
# Client Summary
TOTAL_CLIENTS=$(echo "$CLIENTS" | jq '.data | length' 2>/dev/null || echo 0)
WIRED_CLIENTS=$(echo "$CLIENTS" | jq '[.data[] | select(.is_wired == true)] | length' 2>/dev/null || echo 0)
WIFI_CLIENTS=$(echo "$CLIENTS" | jq '[.data[] | select(.is_wired == false)] | length' 2>/dev/null || echo 0)
echo "┌─────────────────────────────────────────────────────────────────────────────┐"
echo "│ CLIENTS: $TOTAL_CLIENTS total ($WIRED_CLIENTS wired, $WIFI_CLIENTS wireless) │"
echo "└─────────────────────────────────────────────────────────────────────────────┘"
printf "%-25s %-15s %-18s %-10s %-12s\n" "HOSTNAME" "IP" "MAC" "TYPE" "TX/RX MB/s"
printf "%-25s %-15s %-18s %-10s %-12s\n" "--------" "--" "---" "----" "----------"
echo "$CLIENTS" | jq -r '
.data | sort_by(-(.["wired-rx_bytes"] // .rx_bytes // 0)) | .[0:15] | .[] |
[
((.name // .hostname // .mac // "Unknown") | tostring | .[:25]),
((.ip // .last_ip // "N/A") | tostring | .[:15]),
((.mac // "N/A") | tostring | .[:18]),
(if .is_wired == true then "Wired" else "WiFi" end),
"\( ((.["tx_bytes-r"] // .["wired-tx_bytes-r"] // 0) / 1000000 * 10 | floor / 10) )/\( ((.["rx_bytes-r"] // .["wired-rx_bytes-r"] // 0) / 1000000 * 10 | floor / 10) )"
] | @tsv
' 2>/dev/null | while IFS=$'\t' read -r hostname ip mac type rate; do
printf "%-25s %-15s %-18s %-10s %-12s\n" "$hostname" "$ip" "$mac" "$type" "$rate"
done || echo " (error parsing client data)"
echo " (showing top 15 by traffic)"
echo ""
# Networks
echo "┌─────────────────────────────────────────────────────────────────────────────┐"
echo "│ NETWORKS │"
echo "└─────────────────────────────────────────────────────────────────────────────┘"
printf "%-25s %-8s %-18s %-10s %-15s\n" "NAME" "VLAN" "SUBNET" "PURPOSE" "DHCP"
printf "%-25s %-8s %-18s %-10s %-15s\n" "----" "----" "------" "-------" "----"
# Check if networks returned an error
if echo "$NETWORKS" | jq -e '.error' >/dev/null 2>&1; then
echo " (API returned 401 - REST endpoints may require additional permissions)"
else
echo "$NETWORKS" | jq -r '
.data[] |
[
((.name // "N/A") | tostring | .[:25]),
((.vlan // "-") | tostring | .[:8]),
((.ip_subnet // "N/A") | tostring | .[:18]),
((.purpose // "N/A") | tostring | .[:10]),
(if .dhcpd_enabled == true then "Enabled" else "Disabled" end)
] | @tsv
' 2>/dev/null | while IFS=$'\t' read -r name vlan subnet purpose dhcp; do
printf "%-25s %-8s %-18s %-10s %-15s\n" "$name" "$vlan" "$subnet" "$purpose" "$dhcp"
done
fi
echo ""
# WLANs
echo "┌─────────────────────────────────────────────────────────────────────────────┐"
echo "│ WIRELESS NETWORKS │"
echo "└─────────────────────────────────────────────────────────────────────────────┘"
printf "%-30s %-10s %-15s %-10s\n" "SSID" "ENABLED" "SECURITY" "BAND"
printf "%-30s %-10s %-15s %-10s\n" "----" "-------" "--------" "----"
# Check if wlans returned an error
if echo "$WLANS" | jq -e '.error' >/dev/null 2>&1; then
echo " (API returned 401 - REST endpoints may require additional permissions)"
else
echo "$WLANS" | jq -r '
.data[] |
[
((.name // "N/A") | tostring | .[:30]),
(if .enabled == true then "YES" else "NO" end),
((.security // "open") | tostring | .[:15]),
((if .wlan_band then .wlan_band else "both" end) | tostring | .[:10])
] | @tsv
' 2>/dev/null | while IFS=$'\t' read -r ssid enabled security band; do
printf "%-30s %-10s %-15s %-10s\n" "$ssid" "$enabled" "$security" "$band"
done
fi
echo ""
# Port Forwards
echo "┌─────────────────────────────────────────────────────────────────────────────┐"
echo "│ PORT FORWARDS │"
echo "└─────────────────────────────────────────────────────────────────────────────┘"
PF_COUNT=$(echo "$PORTFWD" | jq '.data | length' 2>/dev/null || echo 0)
if [ "$PF_COUNT" -eq 0 ]; then
echo " No port forwards configured"
else
printf "%-25s %-10s %-8s %-15s %-8s %-10s\n" "NAME" "ENABLED" "PROTO" "FWD TO" "PORT" "SRC PORT"
printf "%-25s %-10s %-8s %-15s %-8s %-10s\n" "----" "-------" "-----" "------" "----" "--------"
echo "$PORTFWD" | jq -r '
.data[] |
[
((.name // "N/A") | tostring | .[:25]),
(if .enabled == true then "YES" else "NO" end),
((.proto // "tcp") | tostring | .[:8]),
((.fwd // "N/A") | tostring | .[:15]),
((.fwd_port // "-") | tostring | .[:8]),
((.src_port // .dst_port // "-") | tostring | .[:10])
] | @tsv
' 2>/dev/null | while IFS=$'\t' read -r name enabled proto fwd port srcport; do
printf "%-25s %-10s %-8s %-15s %-8s %-10s\n" "$name" "$enabled" "$proto" "$fwd" "$port" "$srcport"
done
fi
echo ""
# Firewall Rules
echo "┌─────────────────────────────────────────────────────────────────────────────┐"
echo "│ FIREWALL RULES │"
echo "└─────────────────────────────────────────────────────────────────────────────┘"
FW_COUNT=$(echo "$FWRULES" | jq '.data | length' 2>/dev/null || echo 0)
if [ "$FW_COUNT" -eq 0 ]; then
echo " No custom firewall rules configured"
else
printf "%-25s %-10s %-10s %-10s %-10s %-10s\n" "NAME" "ENABLED" "ACTION" "PROTO" "RULESET" "INDEX"
printf "%-25s %-10s %-10s %-10s %-10s %-10s\n" "----" "-------" "------" "-----" "-------" "-----"
echo "$FWRULES" | jq -r '
.data | sort_by(.rule_index) | .[] |
[
((.name // "N/A") | tostring | .[:25]),
(if .enabled == true then "YES" else "NO" end),
((.action // "N/A") | tostring | .[:10]),
((.protocol // "all") | tostring | .[:10]),
((.ruleset // "N/A") | tostring | .[:10]),
((.rule_index // 0) | tostring)
] | @tsv
' 2>/dev/null | while IFS=$'\t' read -r name enabled action proto ruleset idx; do
printf "%-25s %-10s %-10s %-10s %-10s %-10s\n" "$name" "$enabled" "$action" "$proto" "$ruleset" "$idx"
done
fi
echo ""
# Static Routes
echo "┌─────────────────────────────────────────────────────────────────────────────┐"
echo "│ ROUTES │"
echo "└─────────────────────────────────────────────────────────────────────────────┘"
ROUTE_COUNT=$(echo "$ROUTING" | jq '.data | length' 2>/dev/null || echo 0)
if [ "$ROUTE_COUNT" -eq 0 ]; then
echo " No static routes (using default)"
else
printf "%-20s %-18s %-15s %-12s %-10s\n" "NAME" "DESTINATION" "NEXT HOP" "INTERFACE" "METRIC"
printf "%-20s %-18s %-15s %-12s %-10s\n" "----" "-----------" "--------" "---------" "------"
echo "$ROUTING" | jq -r '
.data[] |
[
((.name // .pfx // "N/A") | tostring | .[:20]),
((.pfx // "N/A") | tostring | .[:18]),
((.nh[0].t // .nh[0].gw // "N/A") | tostring | .[:15]),
((.nh[0].intf_name // "N/A") | tostring | .[:12]),
((.metric // 0) | tostring)
] | @tsv
' 2>/dev/null | head -10 | while IFS=$'\t' read -r name dest nexthop intf metric; do
printf "%-20s %-18s %-15s %-12s %-10s\n" "$name" "$dest" "$nexthop" "$intf" "$metric"
done
fi
echo ""
# Recent Alarms
echo "┌─────────────────────────────────────────────────────────────────────────────┐"
echo "│ RECENT ALARMS (last 10) │"
echo "└─────────────────────────────────────────────────────────────────────────────┘"
ALARM_COUNT=$(echo "$ALARMS" | jq '.data | length' 2>/dev/null || echo 0)
if [ "$ALARM_COUNT" -eq 0 ]; then
echo " No recent alarms"
else
printf "%-20s %-50s\n" "TIME" "MESSAGE"
printf "%-20s %-50s\n" "----" "-------"
echo "$ALARMS" | jq -r '
.data | sort_by(-.time) | .[0:10] | .[] |
[
((.datetime // (.time | todate? // "N/A"))[:20]),
((.msg // .key // "N/A")[:50])
] | @tsv
' 2>/dev/null | while IFS=$'\t' read -r time msg; do
printf "%-20s %-50s\n" "$time" "$msg"
done
fi
echo ""
echo "╔══════════════════════════════════════════════════════════════════════════════╗"
echo "║ Dashboard generated at $(date '+%Y-%m-%d %H:%M:%S')"
echo "╚══════════════════════════════════════════════════════════════════════════════╝"
} | tee "$OUTPUT_FILE"

View File

@@ -0,0 +1,28 @@
#!/usr/bin/env bash
# List UniFi devices (APs, switches, gateway)
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "$SCRIPT_DIR/unifi-api.sh"
FORMAT="${1:-table}"
data=$(unifi_get "/api/s/$UNIFI_SITE/stat/device")
if [ "$FORMAT" = "json" ]; then
echo "$data"
else
# Table format
echo "$data" | jq -r '
["NAME", "MODEL", "IP", "STATE", "UPTIME", "CLIENTS"],
["----", "-----", "--", "-----", "------", "-------"],
(.data[] | [
.name // .mac,
.model,
.ip,
.state_name // .state,
(.uptime | if . then (. / 3600 | floor | tostring) + "h" else "N/A" end),
(.num_sta // 0 | tostring)
]) | @tsv
' | column -t -s $'\t'
fi

View File

@@ -0,0 +1,27 @@
#!/usr/bin/env bash
# UniFi site health summary
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "$SCRIPT_DIR/unifi-api.sh"
FORMAT="${1:-table}"
data=$(unifi_get "/api/s/$UNIFI_SITE/stat/health")
if [ "$FORMAT" = "json" ]; then
echo "$data"
else
# Table format
echo "$data" | jq -r '
["SUBSYSTEM", "STATUS", "# UP", "# ADOPTED", "# DISCONNECTED"],
["---------", "------", "----", "---------", "--------------"],
(.data[] | [
.subsystem,
.status,
(.num_user // .num_ap // .num_sw // .num_gw // 0 | tostring),
(.num_adopted // 0 | tostring),
(.num_disconnected // 0 | tostring)
]) | @tsv
' | column -t -s $'\t'
fi

View File

@@ -0,0 +1,4 @@
# Netscape HTTP Cookie File
# https://curl.se/docs/http-cookies.html
# This file was generated by libcurl! Edit at your own risk.

View File

@@ -0,0 +1,26 @@
#!/usr/bin/env bash
# Top applications by traffic (DPI)
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "$SCRIPT_DIR/unifi-api.sh"
LIMIT="${1:-10}"
data=$(unifi_get "/api/s/$UNIFI_SITE/stat/sitedpi")
echo "$data" | jq -r --argjson limit "$LIMIT" '
["APP", "CATEGORY", "RX (GB)", "TX (GB)", "TOTAL (GB)"],
["---", "--------", "-------", "-------", "----------"],
(.data[0].by_app // []
| sort_by(-.tx_bytes + -.rx_bytes)
| .[:$limit][]
| [
.app,
.cat,
((.rx_bytes // 0) / 1073741824 | . * 100 | floor / 100 | tostring),
((.tx_bytes // 0) / 1073741824 | . * 100 | floor / 100 | tostring),
(((.rx_bytes // 0) + (.tx_bytes // 0)) / 1073741824 | . * 100 | floor / 100 | tostring)
]
) | @tsv
' | column -t -s $'\t'

View File

@@ -0,0 +1,94 @@
#!/usr/bin/env bash
# UniFi API helper - handles login and authenticated calls
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
CONFIG_FILE="${UNIFI_CONFIG_FILE:-$HOME/.clawdbot/credentials/unifi/config.json}"
# Load config
if [ ! -f "$CONFIG_FILE" ]; then
echo "Error: UniFi not configured. Create $CONFIG_FILE with {\"url\": \"https://...\", \"username\": \"...\", \"password\": \"...\", \"site\": \"default\"}" >&2
exit 1
fi
UNIFI_URL=$(jq -r '.url' "$CONFIG_FILE")
UNIFI_USER=$(jq -r '.username' "$CONFIG_FILE")
UNIFI_PASS=$(jq -r '.password' "$CONFIG_FILE")
UNIFI_SITE=$(jq -r '.site // "default"' "$CONFIG_FILE")
# Login and store cookie
# Usage: unifi_login [cookie_file_path]
unifi_login() {
local cookie_file="${1:-${UNIFI_COOKIE_FILE:-$(mktemp)}}"
# If it's a temp file we created just now, export it so subsequent calls use it
if [ -z "${UNIFI_COOKIE_FILE:-}" ]; then
export UNIFI_COOKIE_FILE="$cookie_file"
fi
local payload
payload=$(jq -nc --arg username "$UNIFI_USER" --arg password "$UNIFI_PASS" '{username:$username,password:$password}')
# Try UniFi OS login first, fallback to classic controller
local login_response
login_response=$(curl -sk -c "$cookie_file" \
-H "Content-Type: application/json" \
-X POST \
"$UNIFI_URL/api/auth/login" \
--data "$payload" 2>/dev/null)
# Check if UniFi OS login succeeded
if echo "$login_response" | jq -e '.meta.rc == "ok"' >/dev/null 2>&1; then
return 0
fi
# Fallback to classic controller login
curl -sk -c "$cookie_file" \
-H "Content-Type: application/json" \
-X POST \
"$UNIFI_URL/api/login" \
--data "$payload" >/dev/null 2>&1
if [ ! -s "$cookie_file" ]; then
echo "Error: Login failed (empty cookie file)" >&2
return 1
fi
}
# Make authenticated GET request
# Usage: unifi_get <endpoint>
# Endpoint should be like "stat/sta" or "rest/portforward" - site path is added automatically
# Uses UNIFI_COOKIE_FILE if set, otherwise logs in temporarily
unifi_get() {
local endpoint="$1"
local temp_cookie=false
# Ensure we have a cookie
if [ -z "${UNIFI_COOKIE_FILE:-}" ] || [ ! -f "$UNIFI_COOKIE_FILE" ]; then
temp_cookie=true
export UNIFI_COOKIE_FILE=$(mktemp)
unifi_login "$UNIFI_COOKIE_FILE"
fi
# Handle both old format (/api/s/site/...) and new format (stat/...)
local full_url
if [[ "$endpoint" == /api/* ]]; then
# Old format - use as-is
full_url="$UNIFI_URL$endpoint"
else
# New format - try UniFi OS first, fallback to classic
full_url="$UNIFI_URL/proxy/network/api/s/$UNIFI_SITE/$endpoint"
fi
curl -sk -b "$UNIFI_COOKIE_FILE" "$full_url"
# Cleanup if we created a temp cookie just for this request
if [ "$temp_cookie" = true ]; then
rm -f "$UNIFI_COOKIE_FILE"
unset UNIFI_COOKIE_FILE
fi
}
export -f unifi_login
export -f unifi_get
export UNIFI_URL UNIFI_SITE