Fresh start - excluded large ROM JSON files
This commit is contained in:
21
skills/unifi/scripts/alerts.sh
Normal file
21
skills/unifi/scripts/alerts.sh
Normal 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'
|
||||
28
skills/unifi/scripts/clients.sh
Normal file
28
skills/unifi/scripts/clients.sh
Normal 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
|
||||
285
skills/unifi/scripts/dashboard.sh
Normal file
285
skills/unifi/scripts/dashboard.sh
Normal 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"
|
||||
28
skills/unifi/scripts/devices.sh
Normal file
28
skills/unifi/scripts/devices.sh
Normal 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
|
||||
27
skills/unifi/scripts/health.sh
Normal file
27
skills/unifi/scripts/health.sh
Normal 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
|
||||
4
skills/unifi/scripts/test_cookie.txt
Normal file
4
skills/unifi/scripts/test_cookie.txt
Normal 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.
|
||||
|
||||
26
skills/unifi/scripts/top-apps.sh
Normal file
26
skills/unifi/scripts/top-apps.sh
Normal 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'
|
||||
94
skills/unifi/scripts/unifi-api.sh
Normal file
94
skills/unifi/scripts/unifi-api.sh
Normal 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
|
||||
Reference in New Issue
Block a user