112 lines
6.5 KiB
Markdown
112 lines
6.5 KiB
Markdown
# UniFi WiFi data access — what the UOS controller exposes and how to read it
|
|
|
|
This is the data-capability map for the `unifi-wifi` tuning skill, built from live recon of the
|
|
**UOS Server** (`172.16.3.29`, self-hosted UniFi OS / classic Network app, Mongo DB `ace`) using
|
|
the **Cascades** site (`site_id 685f39068e65331c46ef6dd2`) as the hard case (77 APs / 12 switches,
|
|
~550 concurrent wireless clients, severe 2.4GHz neighbor congestion). Access: `infrastructure/uos-server-ssh-key`
|
|
(vaulted) + `.claude/scripts/uos-mongo.sh`. See also [[uos-server]].
|
|
|
|
## Two data planes (the key finding)
|
|
|
|
| Plane | Source | Reach | Holds |
|
|
|---|---|---|---|
|
|
| **Config** | Mongo `ace` via `uos-mongo.sh` | root SSH, available now | radio config, foreign-interference map (`rogue`), channel-plan, device/floorplan |
|
|
| **History** | Mongo `ace_stat` (`db.getSiblingDB('ace_stat')`) | root SSH, available now | per-AP/per-band airtime time-series (`<band>-cu_total/cu_interf/num_sta`, satisfaction, retries) in `stat_hourly`/`stat_daily`; the **roam graph** in `wifi_connectivity_event`; per-client history |
|
|
| **Live (optional)** | Controller Network API (`stat/device`, `stat/sta`) | needs a session / integration key — not wired | *current* utilization + the live RF-neighbor table; nice for before/after validation |
|
|
|
|
> The accumulated **history plane (`ace_stat`)** is the key source for the interference / airtime
|
|
> model — it already holds what the UniFi UI shows. See [interference-model.md](interference-model.md).
|
|
|
|
The live per-AP utilization and per-client RF stats are **NOT persisted in Mongo** (the `device`
|
|
collection carries config but no `radio_table_stats`; the `user`/client collection only keeps
|
|
`last_radio`). So a first-pass audit + channel/interference plan comes entirely from Mongo; the
|
|
live "current airtime / who's unhappy right now" feedback loop needs the local Network API (see
|
|
"Live-stats gap" below).
|
|
|
|
## Plane 1 — Mongo `ace` (available now)
|
|
|
|
### `device` collection (per AP/switch; filter `type:'uap'` for APs)
|
|
Per-AP `radio_table[]` (the config we tune), one entry per radio:
|
|
- `radio`: `ng` = 2.4GHz, `na` = 5GHz, `6e` = 6GHz
|
|
- `channel`, `ht` (width: 20/40/80/160), `tx_power_mode` (auto/low/medium/high/custom)
|
|
- `min_rssi_enabled`, `min_rssi` (sticky-client / roaming floor, e.g. -77)
|
|
- plus `atf_enabled` (airtime fairness), `country_code`, `antenna_table`, `scan_radio_table`,
|
|
`support_wifi6e`, `wifi_caps`, model/firmware, `num_sta` is present at the device level (last reported).
|
|
|
|
Cascades is a **U7-Pro / WiFi-6E** fleet (tri-band ng/na/6e). Example AP: 2.4 ch11/20MHz,
|
|
5GHz ch153/80MHz, 6GHz ch145/160MHz, min_rssi -77 enabled.
|
|
|
|
### `rogue` collection — the interference map (586,688 docs fleet-wide; the gold)
|
|
Every neighbor/over-the-air BSSID an AP has seen, with `band`, `channel`, `ap_mac` (which of our
|
|
APs saw it), `bssid`, `essid`, `rssi`, `age`, `site_id`. Aggregate by channel to quantify
|
|
**co-channel congestion**. Cascades 2.4GHz is brutal:
|
|
|
|
| Band | Channel | Neighbor BSSIDs seen |
|
|
|---|---|---|
|
|
| 2.4 (ng) | **6** | 33,359 |
|
|
| 2.4 (ng) | **1** | 19,275 |
|
|
| 2.4 (ng) | **11** | 16,578 |
|
|
| 5 (na) | 149 | 8,889 |
|
|
| 5 (na) | 157 | 6,964 |
|
|
| 5 (na) | 44 | 5,477 |
|
|
| 6 (6e) | 69 | 86 |
|
|
|
|
Takeaways the skill uses: 2.4GHz is saturated on all three usable channels (so the fix is fewer
|
|
2.4 radios + tight power, not "find a clean channel"); 6GHz is nearly empty (steer capable
|
|
clients up); 5GHz upper band (149/157) is busier than the UNII-1/DFS lower band.
|
|
|
|
### `channelplan` collection
|
|
The controller's auto-channel-plan inputs/outputs per site: `channels_ng`/`channels_na`/`channels_6e`
|
|
(allowed channel lists), `ht_modes_ng/na/6e`, `method`, `optimize`, `exclude_devices`,
|
|
`high_priority_devices`, `date`. Lets the skill read/propose the channel plan the controller will honor.
|
|
|
|
### `user` collection — client inventory/history
|
|
~1,807 client records for Cascades. Holds identity + `last_radio` (band) but **not** live RF; use
|
|
for inventory/segmentation, not live signal.
|
|
|
|
### Config-audit signals already computable from Mongo
|
|
- 2.4GHz width != 20MHz (40MHz on 2.4 in density = self-inflicted overlap).
|
|
- `min_rssi_enabled=false` on 2.4 radios → sticky-client risk. (Cascades: **6 of 77** APs have 2.4 min_rssi disabled.)
|
|
- 2.4 channels not on the 1/6/11 plan; adjacent APs on the same channel.
|
|
- TX-power mode (auto/high) on 2.4 in dense clusters (should be low/medium).
|
|
- Per-AP radio enable: which dense-cluster APs should have their 2.4 radio disabled entirely.
|
|
|
|
## Plane 2 — Live RF/airtime (the gap to wire next)
|
|
|
|
Live data the tuner ideally also wants (current utilization, satisfaction, per-client RSSI/SNR/
|
|
retries, roam events) lives in the **classic Network API**, session-authenticated:
|
|
- `GET /proxy/network/api/s/<site_short>/stat/device` → per-AP `radio_table_stats[]`:
|
|
`cu_total` (channel utilization %), `cu_self_rx`/`cu_self_tx` (our own airtime), `num_sta`,
|
|
`tx_retries`, `satisfaction`, per radio.
|
|
- `GET /proxy/network/api/s/<site_short>/stat/sta` → per-client: `rssi`, `signal`, `noise`,
|
|
`tx_rate`/`rx_rate`, `tx_retries`, `satisfaction`, `radio`/`channel`, `nss`, anomalies.
|
|
|
|
Auth options (none wired yet): (a) a **dedicated read-only local UniFi admin** → login for a
|
|
session cookie; (b) a **Network integration API key** (`X-API-KEY` vs `/proxy/network/integration/v1/...`).
|
|
The cloud Site Manager key does NOT authenticate the local API (401); the existing local "Claude"
|
|
integration key's value is hashed/unrecoverable. **Action for phase 2:** create a dedicated
|
|
read-only admin or integration key on `.29`, vault it (`infrastructure/uos-server-network-api`),
|
|
and read live stats from it. Until then the skill runs config+interference analysis (Plane 1),
|
|
which already covers the 2.4GHz "first problem".
|
|
|
|
## Quick recipes
|
|
|
|
```bash
|
|
# Cascades site id
|
|
bash .claude/scripts/uos-mongo.sh --sites | grep -i casc # 685f39068e65331c46ef6dd2
|
|
|
|
# 2.4GHz neighbor congestion per channel for a site (the interference map)
|
|
cat <<'JS' | bash .claude/scripts/uos-mongo.sh
|
|
db.rogue.aggregate([{$match:{site_id:'685f39068e65331c46ef6dd2',band:'ng'}},
|
|
{$group:{_id:'$channel',neighbors:{$sum:1}}},{$sort:{neighbors:-1}}]).forEach(printjson)
|
|
JS
|
|
|
|
# Per-AP 2.4GHz config audit (channel, width, power, min_rssi)
|
|
cat <<'JS' | bash .claude/scripts/uos-mongo.sh
|
|
db.device.find({site_id:'685f39068e65331c46ef6dd2',type:'uap'},{name:1,radio_table:1}).forEach(function(a){
|
|
(a.radio_table||[]).forEach(function(r){ if(r.radio=='ng')
|
|
print(a.name+" ch="+r.channel+" ht="+r.ht+" pwr="+r.tx_power_mode+" min_rssi="+(r.min_rssi_enabled?r.min_rssi:'OFF')); });
|
|
});
|
|
JS
|
|
```
|