feat: Instant Privacy (USP MAC whitelist) - #690
Conversation
- Add Instant Privacy feature: one-tap MAC whitelist that locks WiFi access to currently connected devices only - New USP YAML definition for WiFi AP MAC filter settings (MACAddressControlEnabled + AllowedMACAddress + SSIDReference anchor) - Regenerate all codegen files to include mac_filter_access_points.g.dart - Add route and menu entry for Instant Privacy page - Enrich allowed devices display with hostnames; fall back to "Unknown Device" for manually added MACs with no known hostname
Review Summary by Qodofeat: Instant Privacy (USP MAC whitelist)
WalkthroughsDescription• Add **Instant Privacy** feature page under the USP stack — allows users to lock WiFi access to only currently connected devices with a single toggle • Implement UspInstantPrivacyService for MAC list transformation, device enrichment, and AP update descriptor building • Implement UspInstantPrivacyNotifier with enable(), disable(), and addMac() operations — all atomic across all APs (allowPartial: false) • New USP YAML definition (mac_filter_access_points) for reading and writing WiFi AP MAC filter settings via TR-181 • Toggle is disabled when no devices are connected (hard block per spec) and locked during in-progress operations • Allowed devices shown with friendly hostnames; manually added devices fall back to "Unknown Device" • Add "Add device manually" dialog with inline MAC format validation and duplicate detection • Register /uspInstantPrivacy route and add menu entry in USP Menu • Fix codegen filter issue where disabled APs (all-zero values) were incorrectly excluded from parsed results by adding SSIDReference as an always-present anchor field • Regenerate all codegen files with updated usp-codegen version and formatting improvements Diagramflowchart LR
A["Connected Devices"] -->|"MAC filtering"| B["InstantPrivacyService"]
B -->|"Transform & validate"| C["InstantPrivacyNotifier"]
C -->|"Atomic updates"| D["MacFilterAccessPoints"]
D -->|"USP Set"| E["WiFi APs"]
F["InstantPrivacyView"] -->|"User interaction"| C
F -->|"Display"| G["Device Tiles"]
File Changes1. lib/generated/wan_settings.g.dart
|
Code Review by Qodo
1. uspInstantPrivacyProvider wrong directory
|
| final uspInstantPrivacyProvider = | ||
| AsyncNotifierProvider<UspInstantPrivacyNotifier, UspInstantPrivacyState>( | ||
| UspInstantPrivacyNotifier.new, | ||
| ); |
There was a problem hiding this comment.
1. uspinstantprivacyprovider wrong directory 📘 Rule violation ⛯ Reliability
A new Riverpod state provider (uspInstantPrivacyProvider) is introduced under lib/usp_page/... instead of lib/providers/. This conflicts with the required provider centralization and can make state management harder to maintain consistently.
Agent Prompt
## Issue description
A new Riverpod state provider (`uspInstantPrivacyProvider`) was added outside `lib/providers/`, which violates the architecture requirement to centralize state providers under `lib/providers/`.
## Issue Context
The provider currently lives under the feature folder (`lib/usp_page/.../providers`). To comply, either relocate the provider definition under `lib/providers/` or introduce a `lib/providers/` entry-point that defines/exports it, then update any imports to use the centralized path.
## Fix Focus Areas
- lib/usp_page/instant_privacy/providers/instant_privacy_notifier.dart[10-13]
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
| /// Instant Privacy page — one-tap MAC whitelist to lock the network to | ||
| /// currently connected devices only. | ||
| class InstantPrivacyView extends ConsumerWidget { | ||
| const InstantPrivacyView({super.key}); |
There was a problem hiding this comment.
2. instantprivacyview outside lib/page 📘 Rule violation ⛯ Reliability
A new feature page (InstantPrivacyView) is introduced under lib/usp_page/... instead of lib/page/<feature_name>/. This violates the required feature page placement convention and can reduce UI discoverability/consistency.
Agent Prompt
## Issue description
The new `InstantPrivacyView` page was added outside `lib/page/<feature_name>/`, which violates the project feature-page placement convention.
## Issue Context
To comply, move the new Instant Privacy UI page (and its related UI components, if they are considered part of the page structure) into an appropriate feature directory under `lib/page/`, then update go_router route builders/imports to reference the relocated page.
## Fix Focus Areas
- lib/usp_page/instant_privacy/views/instant_privacy_view.dart[14-17]
- lib/route/route_usp_dashboard.dart[60-64]
- lib/route/router_provider.dart[76-76]
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
| /// Builds update descriptors to ADD [newMac] to the existing allowed list. | ||
| /// | ||
| /// Reads the current list from the first AP (all APs share the same list), | ||
| /// appends [newMac] if not already present, and updates every AP. | ||
| /// Precondition: [newMac] is already validated and normalized. | ||
| List<MacFilterAccessPointUpdate> buildAddMacUpdates( | ||
| String newMac, | ||
| MacFilterAccessPoints data, | ||
| ) { | ||
| if (data.items.isEmpty) return []; | ||
|
|
||
| final existing = data.items.first.allowedMACAddress | ||
| .split(',') | ||
| .map((m) => m.trim()) | ||
| .where((m) => m.isNotEmpty) | ||
| .map(normalizeMac) | ||
| .toList(); | ||
|
|
||
| if (existing.contains(newMac)) return []; | ||
|
|
||
| final updated = [...existing, newMac].join(','); | ||
| return data.items | ||
| .map((ap) => MacFilterAccessPointUpdate( | ||
| instancePath: ap.instancePath, | ||
| macAddressControlEnabled: true, | ||
| allowedMACAddress: updated, | ||
| )) | ||
| .toList(); | ||
| } |
There was a problem hiding this comment.
3. Per-ap allowlist overwritten 🐞 Bug ✓ Correctness
UspInstantPrivacyService.buildAddMacUpdates() reads AllowedMACAddress from only the first access point but then writes the updated list to every AP, which can silently overwrite per-AP allowlists. This conflicts with the TR-181 definition describing AllowedMACAddress as the allowlist for a specific access point, causing wrong UI and configuration loss on routers where AP allowlists differ.
Agent Prompt
### Issue description
`AllowedMACAddress` is modeled as per-access-point, but Instant Privacy reads it only from the first AP and then writes the resulting list to all APs. This can overwrite AP-specific allowlists and show an incorrect allowed-device list.
### Issue Context
The TR-181/YAML definition describes `AllowedMACAddress` as the list of MACs allowed to connect to *this access point*. The current implementation assumes all APs share the same list.
### Fix Focus Areas
- lib/usp_page/instant_privacy/services/instant_privacy_service.dart[43-59]
- lib/usp_page/instant_privacy/services/instant_privacy_service.dart[98-126]
- definitions/wifi/mac_filter_access_points.yaml[12-28]
### Implementation notes
- Update `allowedDevices()` to parse allowlists from **all** APs (e.g., union + dedupe, and optionally warn if lists differ).
- Update `buildAddMacUpdates()` to compute `existing` per AP from `ap.allowedMACAddress` and then build an update per AP with its own updated list.
- If the feature truly requires a single global list, encode that explicitly (and/or enforce it by reconciling mismatches deliberately, not implicitly).
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
AustinChangLinksys
left a comment
There was a problem hiding this comment.
Looks Good To Me
Summary
mac_filter_access_points) for reading and writing WiFi AP MAC filter settings via TR-181UspInstantPrivacyServicefor MAC list transformation, device enrichment, and AP update descriptor buildingUspInstantPrivacyNotifierwithenable,disable, andaddMacoperations — all atomic across all APs (allowPartial: false)/uspInstantPrivacyroute and add menu entry in USP MenuSSIDReferenceas an always-present anchor field