feat: initial commit — UniFi snap-in package

Full UniFi dashboard snap-in including:
- WiFi/client/device stats with time-series snapshots
- Client Dashboard with traffic, satisfaction, signal, download charts
- Webhook alerting with debounced offline/online detection
- AP snapshot collection, client snapshot collection
- Device classification (type and OS) from OUI/hostname heuristics
- Webhook cooldown, templates, and multi-platform delivery

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Joel Wedemire
2026-04-12 23:00:05 -07:00
commit ce3217d8f4
29 changed files with 2972 additions and 0 deletions

View File

@@ -0,0 +1,64 @@
<?php
namespace Dashboard\Unifi\Http\Controllers;
use Dashboard\Unifi\Models\KnownMac;
use Dashboard\Unifi\Models\PortalSession;
use Dashboard\Unifi\Services\UnifiApiClient;
use Illuminate\Http\Request;
use Illuminate\Routing\Controller;
use Inertia\Inertia;
class ClientController extends Controller
{
public function index(UnifiApiClient $unifi)
{
try {
$clients = collect($unifi->getActiveClients())->map(fn ($c) => [
'mac' => $c['mac'],
'hostname' => $c['hostname'] ?? $c['name'] ?? '',
'ip' => $c['ip'] ?? '',
'oui' => $c['oui'] ?? '',
'os' => $c['os_name'] ?? null,
'dev_cat' => $c['dev_cat'] ?? null,
'dev_family' => $c['dev_family'] ?? null,
'dev_vendor' => $c['dev_vendor'] ?? null,
'is_wired' => $c['is_wired'] ?? false,
'is_guest' => $c['is_guest'] ?? false,
'ssid' => $c['essid'] ?? null,
'ap_mac' => $c['ap_mac'] ?? null,
'rssi' => $c['rssi'] ?? null,
'signal' => $c['signal'] ?? null,
'channel' => $c['channel'] ?? null,
'tx_bytes' => $c['tx_bytes'] ?? 0,
'rx_bytes' => $c['rx_bytes'] ?? 0,
'tx_rate' => $c['tx_rate'] ?? 0,
'rx_rate' => $c['rx_rate'] ?? 0,
'uptime' => $c['uptime'] ?? 0,
'satisfaction' => $c['satisfaction'] ?? null,
'is_known' => KnownMac::where('mac_address', strtolower($c['mac']))->exists(),
])->values();
return Inertia::render('Unifi/Clients', ['clients' => $clients]);
} catch (\Throwable $e) {
return Inertia::render('Unifi/Clients', ['clients' => [], 'error' => $e->getMessage()]);
}
}
public function kick(Request $request, UnifiApiClient $unifi)
{
$request->validate(['mac' => 'required|string']);
try {
$unifi->kickClient($request->mac);
// Deactivate portal session if there is one
PortalSession::where('mac_address', strtolower($request->mac))
->where('is_active', true)
->update(['is_active' => false]);
return back()->with('success', 'Client disconnected.');
} catch (\Throwable $e) {
return back()->withErrors(['error' => $e->getMessage()]);
}
}
}