Bash · 2461 bytes Raw Blame History
1 #!/usr/bin/env bash
2 set -euo pipefail
3
4 # Get tailscale status
5 status_output=$(tailscale status 2>&1) || {
6 echo '{"text":"TS: down","tooltip":"Tailscale not running","class":"disconnected"}'
7 exit 0
8 }
9
10 # Check if we're connected (if status command worked, we have output)
11 if [[ -z "$status_output" ]] || echo "$status_output" | grep -qi "stopped\|not running"; then
12 echo '{"text":"TS: off","tooltip":"Tailscale is not running","class":"disconnected"}'
13 exit 0
14 fi
15
16 # Get current hostname
17 my_hostname=$(tailscale status --self --json 2>/dev/null | jq -r '.Self.HostName // empty' 2>/dev/null) || my_hostname=""
18
19 # Count online devices (lines that don't contain "offline")
20 online_count=$(echo "$status_output" | grep -v "^#" | grep -v "offline" | grep -c "^[0-9]" 2>/dev/null) || online_count=0
21 total_count=$(echo "$status_output" | grep -v "^#" | grep -c "^[0-9]" 2>/dev/null) || total_count=0
22
23 # Build tooltip with device list
24 tooltip="Tailnet Devices ($online_count/$total_count online)"
25 tooltip+=$'\n'"─────────────────────────────"
26
27 while IFS= read -r line; do
28 # Skip comments and empty lines
29 [[ "$line" =~ ^# ]] && continue
30 [[ -z "$line" ]] && continue
31
32 # Parse: IP hostname user OS status...
33 ip=$(echo "$line" | awk '{print $1}')
34 hostname=$(echo "$line" | awk '{print $2}')
35 os=$(echo "$line" | awk '{print $4}')
36
37 # Skip if not a valid IP line
38 [[ ! "$ip" =~ ^100\. ]] && continue
39
40 # Check if online or offline
41 if echo "$line" | grep -q "offline"; then
42 status_icon="○"
43 # Extract last seen info (already includes "ago")
44 last_seen=$(echo "$line" | grep -oP 'last seen \K[^,]+' | sed 's/ *$//' || echo "")
45 device_info="$status_icon $hostname ($os) - offline"
46 [[ -n "$last_seen" ]] && device_info="$status_icon $hostname ($os) - $last_seen"
47 else
48 status_icon="●"
49 device_info="$status_icon $hostname ($os) - $ip"
50 fi
51
52 # Mark current device
53 if [[ "$hostname" == "$my_hostname" ]]; then
54 device_info="$device_info [this device]"
55 fi
56
57 tooltip+=$'\n'"$device_info"
58 done <<< "$status_output"
59
60 # Determine class based on status
61 if [[ "$online_count" -gt 0 ]]; then
62 class="connected"
63 else
64 class="partial"
65 fi
66
67 # Output JSON
68 jq -cn --arg text "TS: $online_count" --arg tooltip "$tooltip" --arg class "$class" \
69 '{text:$text, tooltip:$tooltip, class:$class}'