Skip to content

feat(events): SSID enrichment for Wi-Fi roam + RF stir lines#86

Merged
chenchaoyi merged 2 commits into
mainfrom
feature/wifi-event-ssid-and-name-enrichment
May 18, 2026
Merged

feat(events): SSID enrichment for Wi-Fi roam + RF stir lines#86
chenchaoyi merged 2 commits into
mainfrom
feature/wifi-event-ssid-and-name-enrichment

Conversation

@chenchaoyi
Copy link
Copy Markdown
Owner

Summary

Implements the approved wifi-event-ssid-and-name-enrichment proposal: Wi-Fi event lines (roam + RF stir) now surface the affected SSID. AP-name lookup against aps.yaml is unchanged — populating the inventory is still the user's job, but at minimum the SSID is now visible without any config.

Before / after

Before (user's screenshot from 2026-05-18):

09:49:30  [漫游]  1c:28:af:5d:2b:b4 -> 1c:28:af:5e:9d:b4   [跨 AP 漫游]
09:49:56  [扰动]  ?af:5e:9d 处 RF 扰动   σ 4.8 dB  ·  中

After (same environment, with aps.yaml still empty):

09:49:30  [漫游]  1c:28:af:5d:2b:b4 -> 1c:28:af:5e:9d:b4   [跨 AP 漫游]   SSID:tedo
09:49:56  [扰动]  ?af:5e:9d 处 RF 扰动   σ 4.8 dB  ·  中  ·  SSID tedo_5G

After (same environment, with the BSSIDs mapped in aps.yaml):

09:49:30  [漫游]  AX51-E_4-B2 (2.4G) (1c:28:af:5d:2b:b4) -> AX51-E_4-B2 (5G) (1c:28:af:5e:9d:b4)   [band switch on AX51-E_4-B2: 2.4G -> 5G]   SSID:tedo
09:49:56  [扰动]  AX51-E_4-B2 处 RF 扰动   σ 4.8 dB  ·  中  ·  SSID tedo_5G

What landed

  • Schema: RoamEvent gains previous_ssid / new_ssid; RFStirEvent gains ssid. All default None, appended at the end of each frozen dataclass for backwards-compat.
  • Poller (WiFiPoller): tracks _last_ssid alongside _last_bssid / _last_channel; fills both sides of the emitted RoamEvent.
  • Environment monitor (EnvironmentMonitor): ingest(...) takes ssid= (default None), remembers the latest non-None value per BSSID in its state map, and attaches it to emitted RFStirEvents. Both TUI and CLI ingest sites updated to pass ssid.
  • Renderer (_format_roam_event / _format_rf_stir_event):
    • Roam: single SSID: <name> when both sides equal; SSID: <prev> → <new> when different; segment omitted when both are None or both are "" (hidden).
    • RF stir: · SSID <name> segment appended when event.ssid is non-empty; omitted otherwise.
  • JSONL writer: emits previous_ssid / new_ssid / ssid when set; skips when None so old log entries stay diff-stable.
  • i18n: SSID: {ssid}, SSID: {prev} -> {new}, SSID {ssid} added to EN + ZH catalogs.

OpenSpec change

openspec/changes/wifi-event-ssid-and-name-enrichment/ — modifies events (schema + JSONL keys) and adds an ADDED requirement to tui-shell covering the rendering rules. Drafted as a separate change from fix-bonjour-list-empties-after-ttl (#85) since the two asks are independent.

Test plan

  • uv run pytest605 passed (was 591 → +14 new tests across test_event_log, test_environment, test_poller, test_tui_helpers)
  • uv run python scripts/tui_snapshot.py --mode regression — 12 scenarios pass
  • openspec validate --specs --strict — 20/20 pass
  • openspec validate wifi-event-ssid-and-name-enrichment --strict — pass
  • After merge: live-run, force a roam (toggle Wi-Fi off/on) and an RF stir (microwave / hairdryer near the AP), confirm the rendered event line carries the SSID segment.

🤖 Generated with Claude Code

User report (2026-05-18): the Events panel renders Wi-Fi roam +
RF stir events with raw BSSIDs only. The AP-name half is already
handled when aps.yaml has the BSSID mapped, but the SSID never
surfaces — for a user with multiple SSIDs on the same physical
link (home/guest, office corp/IoT, cafe open/5G) the BSSIDs alone
don't say which network was affected.

Proposal: carry the associated SSID at the moment the event fires
(the poller and environment monitor already have it on the
Connection they observed) and render it on the event line.
Renderer rules:

- RoamEvent: single 'SSID: <name>' when previous_ssid == new_ssid
  (the common band-switch case), 'SSID: <prev> -> <new>' when
  they differ, omitted entirely when both are None or both are ''.
- RFStirEvent: append '. SSID <name>' after the location body
  when event.ssid is non-empty; omit when None/empty.

JSONL serialisation gains 'previous_ssid' / 'new_ssid' / 'ssid'
keys (additive, skipped when None — old log entries diff-stable).

Capabilities touched: events (schema), tui-shell (new rendering
requirement). Drafted as separate from
fix-bonjour-list-empties-after-ttl (PR #85) since the user's two
asks are independent.

Proposal validates strict.
The Events panel was rendering Wi-Fi events as raw BSSIDs only;
for a user with multiple SSIDs on the same physical link the
BSSIDs alone don't say which network was affected. AP-name was
already handled by aps.yaml lookup; SSID never surfaced.

Implementation:

- RoamEvent gains previous_ssid / new_ssid (default None,
  appended at the end of the frozen dataclass for backwards
  compat).
- RFStirEvent gains ssid (same shape).
- WiFiPoller tracks _last_ssid alongside _last_bssid /
  _last_channel and fills both sides of the RoamEvent.
- EnvironmentMonitor.ingest takes optional ssid=, remembers
  the latest non-None per BSSID, and attaches it to emitted
  RFStirEvents. Both TUI and CLI ingest call sites updated
  to pass ssid.
- _format_roam_event appends a single 'SSID: <name>' segment
  when previous_ssid == new_ssid, 'SSID: <prev> -> <new>' when
  different, omitted when both None or both '' (hidden).
- _format_rf_stir_event appends '. SSID <name>' when
  event.ssid is non-empty.
- JSONL writer emits the new keys (previous_ssid, new_ssid,
  ssid) when set; skips when None so old log entries stay
  diff-stable.
- i18n catalog gains 'SSID: {ssid}', 'SSID: {prev} -> {new}',
  'SSID {ssid}' entries (EN + ZH).

OpenSpec change: wifi-event-ssid-and-name-enrichment.
605 pytest (was 591, +14 new tests across event_log,
environment, poller, tui_helpers), regression clean, 20/20
specs validate, change validates strict.
@chenchaoyi chenchaoyi merged commit 28a9f9a into main May 18, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant