unifi-channel-planning · git:20261014.12b4c6e · 2026-10-14 · sha256 6cba32e7a2362715

unifi-channel-planning git:20261014.12b4c6eA

Immutable. This exact content never changes and is served at /api/v1/blob/6cba32e7a2362715.

---
name: unifi-channel-planning
description: >
  Build an AI-driven low-interference WiFi channel plan from UniFi MCP neighbor
  observations. Use when the task is RF optimization, channel conflict
  reduction, co-channel interference mitigation, or per-AP channel assignment.
---

# UniFi Channel Planning

Use this skill to produce a deterministic channel plan from MCP output.

Primary intent:

- Reduce overlap and co-channel contention.
- Keep assignments reproducible and explainable.
- Provide safe change rollout with validation.

## Required MCP Inputs

| Tool | Purpose |
|---|---|
| `list_sites` | Select target site |
| `list_devices_by_type` | Get managed AP inventory (`type=uap`) |
| `list_site_internal_ap_neighbors_v2` | Build internal AP-to-AP graph for one site in one call |

## Conditional / Optional MCP Inputs

| Tool | When to use |
|---|---|
| `list_neighboring_aps` | Optional external RF tie-breaker or explanation; do not fetch by default |
| `list_ap_neighbors_v2` | Per-AP drill-down, debugging, or partial-result validation |
| `get_device_details` | When radio configuration, current channel, width, or regulatory capabilities are not available from inventory |

Optional execution tool:

| Tool | Purpose |
|---|---|
| `set_ap_radio_channel` | Apply proposed channel per AP/radio |

## Data Contract For Planning

Expected fields from neighbor observations:

- `ap_mac`: AP that observed the neighbor
- `bssid` or `mac`: observed AP/BSSID identity
- `channel`: observed neighbor channel number
- `signal`: RSSI (dBm)
- `last_seen`: freshness indicator
- `radio` or `band`: required radio/band dimension (`ng`/2.4 GHz or `na`/5 GHz)
- `channel_width` or `ht`: channel width, required when available for 5 GHz

If any field is missing:

- Drop records without `ap_mac`, `channel`, or `signal`.
- Do not use records without a band/radio dimension for channel assignment.
- Keep records without width only for a conservative 20 MHz fallback and mark
  the affected recommendation medium-confidence.
- Keep processing with partial data and log dropped count.

## Correct Data Source Split (Critical)

Use two distinct datasets and do not mix them:

1. Internal AP-to-AP graph (for channel assignment):

- Primary source: `list_site_internal_ap_neighbors_v2(site_id, start_ms, end_ms, min_rssi)`
- The tool already executes v2 per-AP neighbor queries and keeps only rows where neighbor `mac` is a managed AP MAC from the same site.
- Optional fallback/debug: `list_ap_neighbors_v2` per AP.
- This is the authoritative input for "which site AP sees which other site AP and how strong".

2. External RF pressure (optional tie-breaker):

- Source: `list_neighboring_aps` (`stat/rogueap`)
- Represents foreign BSSIDs around the site.
- Use only as additional congestion context, not as the internal AP graph.

## Data Volume Controls

Keep the MCP interaction bounded and return summaries rather than raw payloads:

- Use a fixed planning window, normally 24 hours; shorten it when the graph or
  response is too large.
- Pass `min_rssi` during collection (`-85` by default, `-80` for dense sites)
  instead of collecting all weak observations and filtering after output.
- Prefer `list_site_internal_ap_neighbors_v2` because it aggregates the
  per-AP v2 calls server-side. Do not repeat `list_ap_neighbors_v2` for every
  AP unless debugging a specific AP or validating a partial result.
- Request AP inventory with an explicit `limit` and `offset` when supported;
  continue in pages until the managed AP set is complete.
- Call `list_neighboring_aps` only when external RF pressure is needed for a
  tie-breaker or explanation. It can contain hundreds of rows and must never
  be dumped into the planning response by default.
- Normalize, deduplicate, score, and sort before presenting results. Return
  metadata, counts, top conflict drivers, and a compact per-AP summary instead
  of raw JSON.
- Cap displayed neighbors per AP to the strongest relevant observations and
  report the total count plus omitted-row count. Offer per-AP drill-down only
  on request.
- If a response is still too large, process APs or pages in deterministic
  batches and merge the normalized results before optimization; never silently
  truncate the graph.

Required collection metadata:

- `start_ms`, `end_ms`, and `min_rssi`
- managed AP count and collected edge count
- dropped, skipped, paged, and omitted-row counts
- whether external rogue-AP data was collected

When physical AP locations are unavailable:

- Do not block planning on a floor plan, coordinates, or manually supplied
  zones.
- Use the normalized internal v2 graph as the measurable overlap proxy:
  observed AP pairs, RSSI, freshness, band, channel, and width.
- Do not claim that two APs are physically distant or in separate areas unless
  that is reported by UniFi or supplied by the operator.
- Lower confidence for conclusions that depend on unobserved coverage or
  client roaming between APs.
- Prefer no-op or small staged changes when the graph is sparse; the planner
  may still produce a deterministic plan from the telemetry that is available.

## Planning Rules

1. Scope by band:

- 2.4 GHz candidates: channels 1-14 (practical non-overlap: 1, 6, 11)
- 5 GHz candidates: local policy set (for example 36/40/44/48 and DFS set if allowed).
- Plan each band/radio independently. Never score a 2.4 GHz observation against
  a 5 GHz candidate or use one band to fill missing data in the other.
- Derive occupied channel ranges from primary channel and width. For 20 MHz,
  use one channel block; for 40/80 MHz, include every overlapping 20 MHz block
  in the configured channel range.
- Before scoring, convert every UniFi radio configuration into a canonical
  `occupied_20mhz_channels` set.
- Do not infer occupied blocks solely from a numeric primary channel. Use the
  UniFi-reported primary/control/center channel and width representation.
- If UniFi does not provide enough information to determine occupied 20 MHz
  blocks unambiguously, mark the radio medium-confidence and use the
  conservative supported interpretation.

2. Filter weak observations:

- Default threshold: keep neighbors with `signal >= -85`.
- For dense environments, use `>= -80`.

3. Build per-AP conflict score:

- For each AP and candidate channel, sum weighted conflicts from observed neighbors on same or overlapping channels.
- Stronger RSSI contributes higher penalty.
- Internal AP-to-AP penalties must be weighted higher than external rogue BSSID penalties.

4. Deterministic assignment:

- Sort APs by descending neighbor pressure (most constrained first).
- Evaluate assignments against the complete internal AP graph, not each AP in
  isolation. Re-score all graph edges after every proposed assignment so a
  locally good choice cannot create a global co-channel cluster.
- Use deterministic greedy global optimization: assign the next most
  constrained AP, then recompute the complete graph score after each assignment.
- Select the candidate with the lowest total graph penalty. Use descending
  pressure and ascending channel order as deterministic tie-breakers.
- Tie-breaker order must be fixed (for example ascending channel number).

Optimization order is strict:

1. Minimize the complete RF conflict score.
2. Among equivalent or near-equivalent scores, minimize channel concentration
  and prefer a balanced population such as `3/3/2` for eight APs on three
  2.4 GHz channels.
3. Minimize the number of changes.
4. Use ascending channel order as the final deterministic tie-breaker.

Default score tolerance:

- Relative tolerance: `2%`
- Absolute tolerance: `0.01`
- A candidate is near-equivalent when `candidate_score <= best_score * 1.02`
  or `candidate_score - best_score <= 0.01`.

For a global 2.4 GHz assignment, score every internal edge using the proposed
channels of both APs:

- Same channel: `rssi_weight(signal)`
- Adjacent 2.4 GHz channel: `0.5 * rssi_weight(signal)`
- Non-overlapping channel: `0`

For 5 GHz, score overlap between the occupied channel ranges. A shared 20 MHz
block receives the same base penalty, scaled by the fraction of overlapping
blocks. An 80 MHz AP therefore contributes more spectrum pressure than a 20
MHz AP when the ranges overlap.

Use deterministic RSSI weight classes instead of assuming a linear RF effect:

- `signal >= -55`: weight `4.0`
- `-65 <= signal < -55`: weight `3.0`
- `-75 <= signal < -65`: weight `2.0`
- `-85 <= signal < -75`: weight `1.0`

Multiply same-channel or overlap penalties by this weight. Keep the raw RSSI
in the output reason so the score remains explainable.

Before optimization, normalize the internal graph:

- Treat AP-A seeing AP-B and AP-B seeing AP-A as one undirected physical pair.
- Deduplicate repeated observations within the planning window.
- Use the most recent valid RSSI per undirected pair, retaining band and width
  separately. A configurable freshness window may retain the strongest valid
  observation as secondary context, but must not replace the current value.
- Never infer the observer's current channel from the `channel` field: it is
  the observed neighbor/BSSID channel only.

Do not sum an observer's neighbor rows against a candidate while leaving all
other APs at their current channels. That produces misleading results such as
moving several APs to channel 11 simply because each AP was evaluated alone.

5. Guardrails:

- Avoid changing all APs at once.
- Keep at least one stable channel anchor per area.
- With enough APs to use the practical 2.4 GHz set, keep every candidate
  channel represented unless measured RF data clearly justifies abandoning one.
- When topology and RF scores permit, prefer a balanced channel population. For
  eight APs on three channels, prefer counts of `3/3/2` (in deterministic
  channel order) over a `4/3/1` or more concentrated distribution.
- Treat a mathematically lower score that moves many APs or abandons a channel
  as a candidate for staged review, not as an automatic rollout.
- Respect hardware/regulatory constraints reported by UniFi.

## Example Scoring Heuristic

For each observed neighbor relation:

- Same channel penalty: `p_same = rssi_weight(signal)`
- Adjacent channel penalty (2.4 GHz): `p_adj = 0.5 * rssi_weight(signal)`
- Non-overlapping channel penalty: `0`

Total candidate score:

- `score(ap, ch) = sum(penalties from all neighbors seen by ap on/near ch)`

Lower score is better.

## Output Contract

Return a plan table with:

- `ap_mac`
- `current_channel`
- `proposed_channel`
- `band`
- `confidence` (`high|medium|low`)
- `reason` (top 1-3 conflict drivers)

Also return summary metrics:

- Estimated co-channel conflict delta
- Number of APs changed
- APs skipped with reason

## Safe Rollout Workflow

1. Discovery:

- Run `list_sites` and select `site_id`.
- Build managed AP set (`type=uap`).
- Pull internal graph with `list_site_internal_ap_neighbors_v2` over a fixed window (default 24h).
- If needed, inspect problematic APs with `list_ap_neighbors_v2`.
- Optionally pull `list_neighboring_aps(site_id, min_rssi=-85)` for external context.

2. Plan:

- Generate per-AP candidate ranking and proposed channels.
- Mark risky APs (sparse data, stale `last_seen`, extreme noise).

3. Stage:

- Apply to small batch first with `set_ap_radio_channel`.
- Rollout defaults: first wave maximum 3 AP radios; subsequent waves maximum
  25% of remaining changed radios; never change more than 50% of site radios
  without explicit confirmation.
- Persist the original channel, width, and power settings for every change.
- Wait validation window and compare client health/events.
- If AP instability or client disconnects exceed the configured threshold,
  restore the persisted settings before continuing.

4. Expand:

- Roll out remaining APs in waves.
- Re-run neighbor scan and adjust.

## Validation Checks

After each rollout wave, verify:

- AP online state is stable.
- Client disconnect spikes did not increase.
- Channel reuse distance improved in dense zones.
- No AP is assigned unsupported channel width/channel pair.

Pre-plan data-quality checks:

- Confirm each managed AP has at least one internal neighbor observation in the selected window, otherwise mark low-confidence.
- Treat `channel=auto` as unknown current state. Infer it only when the v2
  graph provides a fresh observation of that AP as a neighbour. Mark the
  inferred current state and resulting recommendation medium-confidence. If
  the AP is not observed as a neighbour, keep it unchanged and mark
  low-confidence.
- Distinguish the observed neighbor's channel from the observer's channel:
  the `channel` field on a v2 edge describes the observed neighbor/BSSID.
- Read the observer's current channel from AP radio configuration or telemetry;
  never infer it from a row where that AP is the observer. An AP configured as
  `Auto` may be inferred from a fresh row where its MAC is the observed
  neighbour, and may be changed to the proposed fixed channel when the plan is
  explicitly accepted.
- Reject impossible RSSI outliers for planning (for example values above -20 dBm or below -95 dBm unless intentionally configured).
- If internal graph is empty, do not derive channel plan from `rogueap` alone.

Confidence must be deterministic:

- `high`: complete internal data, fresh observations, known capabilities, and
  a clear score improvement or a validated no-op.
- `medium`: partial or stale data, inferred `Auto` state, missing width, or a
  marginal score difference.
- `low`: no or very few observations, skipped APs, unknown capabilities, or an
  unknown current channel.

## Failure Handling

- If neighbor dataset is empty for a site, do not auto-assign.
- If an AP has no observations, keep current channel and mark `low` confidence.
- If an AP is configured as `Auto` and its current channel is inferred from a
  fresh neighbour observation, preserve the original `Auto` setting for
  rollback and allow the accepted plan to set an explicit fixed channel.
- On per-AP apply failure, continue with others and report partial status.
- If `list_site_internal_ap_neighbors_v2` reports `skipped_aps`, keep those APs unchanged and mark low confidence.
- If only `rogueap` is available and internal AP graph is missing, return "insufficient internal data" instead of proposing deterministic AP-to-AP assignments.
- If the current configuration is already within the accepted score tolerance,
  return a valid no-op plan with `0 APs changed`; do not manufacture changes.

## Example Prompt Pattern

"For site X, use the internal AP neighbor graph to produce a low-interference
channel plan per AP for 2.4 GHz first. Use channels 1/6/11 only, use minimum
RSSI -85, minimize same-channel conflicts, and return deterministic sorted
results with reasons. Fetch external rogue-AP data only if needed for context."