Skip to content

feat: Instant Privacy (USP MAC whitelist) - #690

Merged
AustinChangLinksys merged 2 commits into
dev-2.2.0from
hank/usp-instant-privacy
Mar 17, 2026
Merged

feat: Instant Privacy (USP MAC whitelist)#690
AustinChangLinksys merged 2 commits into
dev-2.2.0from
hank/usp-instant-privacy

Conversation

@HankYuLinksys

Copy link
Copy Markdown
Collaborator

Summary

  • Add Instant Privacy feature page under the USP stack — allows users to lock WiFi access to only currently connected devices with a single toggle
  • New USP YAML definition (mac_filter_access_points) for reading and writing WiFi AP MAC filter settings via TR-181
  • 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)
  • 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

- 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
@qodo-code-review

Copy link
Copy Markdown

Review Summary by Qodo

feat: Instant Privacy (USP MAC whitelist)

✨ Enhancement

Grey Divider

Walkthroughs

Description
• 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
Diagram
flowchart 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"]
Loading

Grey Divider

File Changes

1. lib/generated/wan_settings.g.dart Formatting +59/-110

Code formatting and style improvements

• Reformatted code for improved readability by collapsing multi-line expressions onto single lines
• Adjusted indentation in toString() method for consistency
• Added blank line after imports section

lib/generated/wan_settings.g.dart


2. lib/usp_page/instant_privacy/views/instant_privacy_view.dart ✨ Enhancement +400/-0

Instant Privacy feature page UI implementation

• New Instant Privacy feature page with toggle to lock WiFi to currently connected devices
• Displays connected devices preview when disabled and allowed devices list when enabled
• Implements "Add device manually" dialog with MAC address validation and duplicate detection
• Confirmation dialogs for enable/disable operations with error handling

lib/usp_page/instant_privacy/views/instant_privacy_view.dart


3. lib/generated/port_triggering.g.dart Formatting +28/-65

Code formatting and style improvements

• Reformatted code by collapsing multi-line conditional checks and assignments to single lines
• Added blank line after imports and at end of file
• Improved code consistency across method signatures

lib/generated/port_triggering.g.dart


View more (38)
4. lib/generated/wi_fi_radios.g.dart Formatting +27/-78

Code formatting and style improvements

• Reformatted code by consolidating multi-line expressions and conditionals
• Added blank line after imports and at end of file
• Improved readability of boolean checks and parameter assignments

lib/generated/wi_fi_radios.g.dart


5. lib/generated/port_forwarding.g.dart Formatting +27/-66

Code formatting and style improvements

• Reformatted code by collapsing multi-line expressions to single lines
• Added blank line after imports and at end of file
• Standardized indentation in method signatures and conditionals

lib/generated/port_forwarding.g.dart


6. lib/generated/ipv6port_service.g.dart Formatting +27/-68

Code formatting and style improvements

• Reformatted code by consolidating multi-line conditional checks and assignments
• Added blank line after imports and at end of file
• Improved consistency in parameter handling across methods

lib/generated/ipv6port_service.g.dart


7. lib/generated/multi_interface_traffic_stats.g.dart Formatting +35/-93

Code formatting and style improvements

• Reformatted factory method and toString() by collapsing multi-line expressions
• Added blank line after imports
• Adjusted indentation for consistency in string concatenation

lib/generated/multi_interface_traffic_stats.g.dart


8. lib/generated/static_routing.g.dart Formatting +21/-53

Code formatting and style improvements

• Reformatted code by consolidating multi-line conditionals and assignments
• Added blank line after imports and at end of file
• Improved readability of method signatures and parameter handling

lib/generated/static_routing.g.dart


9. lib/generated/ipv6settings.g.dart Formatting +22/-45

Code formatting and style improvements

• Reformatted factory method and toString() by collapsing multi-line expressions
• Added blank line after imports
• Adjusted indentation in method signatures and parameter assignments

lib/generated/ipv6settings.g.dart


10. lib/generated/connected_devices.g.dart Formatting +12/-30

Code formatting and style improvements

• Reformatted code by consolidating multi-line conditionals and method signatures
• Added blank line after imports and at end of file
• Improved consistency in subscription method declarations

lib/generated/connected_devices.g.dart


11. lib/generated/lan_network_info.g.dart Formatting +25/-42

Code formatting and style improvements

• Reformatted factory method and toString() by collapsing multi-line expressions
• Added blank line after imports
• Adjusted indentation for consistency in parameter assignments

lib/generated/lan_network_info.g.dart


12. lib/generated/wi_fi_access_points.g.dart Formatting +18/-54

Code formatting and style improvements

• Reformatted code by consolidating multi-line conditionals and method signatures
• Added blank line after imports and at end of file
• Improved readability of parameter assignments and update methods

lib/generated/wi_fi_access_points.g.dart


13. lib/generated/dmz.g.dart Formatting +18/-44

Code formatting and style improvements

• Reformatted code by collapsing multi-line conditionals and assignments
• Added blank line after imports and at end of file
• Standardized indentation in method signatures and parameter handling

lib/generated/dmz.g.dart


14. lib/generated/data_elements_network.g.dart Formatting +12/-27

Code formatting and style improvements

• Reformatted code by consolidating multi-line conditionals and sort operations
• Added blank line after imports and at end of file
• Improved consistency in nested loop and assignment formatting

lib/generated/data_elements_network.g.dart


15. lib/generated/dhcp_reservations.g.dart Formatting +16/-37

Code formatting and style improvements

• Reformatted code by collapsing multi-line conditionals and method signatures
• Added blank line after imports and at end of file
• Improved readability of parameter assignments and update methods

lib/generated/dhcp_reservations.g.dart


16. lib/usp_page/instant_privacy/services/instant_privacy_service.dart ✨ Enhancement +139/-0

Instant Privacy service with MAC filtering logic

• New stateless service for Instant Privacy feature with MAC address validation and normalization
• Provides helpers to transform codegen models to UI models and build update descriptors
• Implements MAC filtering enable/disable/add operations for all APs atomically
• Includes MAC address regex validation and colon-separated canonical formatting

lib/usp_page/instant_privacy/services/instant_privacy_service.dart


17. lib/usp_page/instant_privacy/providers/instant_privacy_notifier.dart ✨ Enhancement +127/-0

Instant Privacy state management and operations

• New async notifier managing Instant Privacy state with enable(), disable(), and addMac()
 operations
• Fetches connected devices and MAC filter AP settings, enriches allowed devices with friendly
 hostnames
• Implements atomic updates across all APs with toggle locking during in-progress operations
• Provides error handling and state invalidation on successful operations

lib/usp_page/instant_privacy/providers/instant_privacy_notifier.dart


18. lib/generated/vendor_log_files.g.dart Formatting +7/-18

Code formatting and style improvements

• Reformatted code by consolidating multi-line conditionals and assignments
• Added blank line after imports and at end of file
• Improved consistency in parameter handling

lib/generated/vendor_log_files.g.dart


19. lib/generated/mac_filter_access_points.g.dart ✨ Enhancement +98/-0

New MAC filter access points codegen generation

• New generated codegen file for WiFi access point MAC address filtering control
• Defines MacFilterAccessPoint class with instance path, SSID reference, MAC control enabled flag,
 and allowed MAC address list
• Implements MacFilterAccessPointUpdate descriptor for atomic updates via USP Set messages
• Provides fetch(), update(), and updateMany() methods with allowPartial parameter support

lib/generated/mac_filter_access_points.g.dart


20. lib/generated/wifi_clients.g.dart Formatting +11/-31

Code formatting and style improvements

• Code formatting and line-wrapping refactoring for improved readability
• Consolidates multi-line conditional checks and assignments onto single lines
• No functional logic changes; purely formatting updates

lib/generated/wifi_clients.g.dart


21. lib/generated/wi_fi_ssids.g.dart Formatting +10/-27

Code formatting and style improvements

• Code formatting refactoring with improved line wrapping and indentation
• Consolidates multi-line expressions and conditional checks
• Adds trailing blank line for consistency

lib/generated/wi_fi_ssids.g.dart


22. lib/generated/firewall_chain_rules.g.dart Formatting +10/-25

Code formatting and style improvements

• Code formatting refactoring with improved line wrapping
• Consolidates multi-line conditional checks and method signatures
• Adds trailing blank line for consistency

lib/generated/firewall_chain_rules.g.dart


23. lib/generated/admin_users.g.dart Formatting +9/-23

Code formatting and style improvements

• Code formatting refactoring with improved line wrapping
• Consolidates multi-line conditional checks and parameter assignments
• Adds trailing blank line for consistency

lib/generated/admin_users.g.dart


24. lib/generated/system_info.g.dart Formatting +19/-34

Code formatting and style improvements

• Code formatting refactoring with improved line wrapping and indentation
• Consolidates multi-line assignments and method calls
• Improves toString() method formatting with consistent indentation

lib/generated/system_info.g.dart


25. lib/generated/time_settings.g.dart Formatting +12/-16

Code formatting and style improvements

• Code formatting refactoring with improved line wrapping
• Consolidates multi-line conditional checks and assignments
• Improves toString() method formatting with consistent indentation

lib/generated/time_settings.g.dart


26. lib/generated/dhcp_clients.g.dart Formatting +7/-19

Code formatting and style improvements

• Code formatting refactoring with improved line wrapping
• Consolidates multi-line conditional checks and assignments
• Adds trailing blank line for consistency

lib/generated/dhcp_clients.g.dart


27. lib/generated/ethernet_interfaces.g.dart Formatting +7/-19

Code formatting and style improvements

• Code formatting refactoring with improved line wrapping
• Consolidates multi-line conditional checks and assignments
• Adds trailing blank line for consistency

lib/generated/ethernet_interfaces.g.dart


28. lib/generated/wan_traffic_stats.g.dart Formatting +10/-23

Code formatting and style improvements

• Code formatting refactoring with improved line wrapping
• Consolidates multi-line assignments and method calls
• Improves toString() method formatting with consistent indentation

lib/generated/wan_traffic_stats.g.dart


29. lib/generated/firmware_images.g.dart Formatting +6/-17

Code formatting and style improvements

• Code formatting refactoring with improved line wrapping
• Consolidates multi-line conditional checks and assignments
• Adds trailing blank line for consistency

lib/generated/firmware_images.g.dart


30. lib/generated/wan_status.g.dart Formatting +11/-16

Code formatting and style improvements

• Code formatting refactoring with improved line wrapping
• Consolidates multi-line assignments and method calls
• Improves toString() method formatting with consistent indentation

lib/generated/wan_status.g.dart


31. lib/usp_page/instant_privacy/providers/instant_privacy_state.dart ✨ Enhancement +69/-0

Instant Privacy state management implementation

• New state management class for Instant Privacy feature page implementing Equatable
• Defines properties for enabled status, connected devices, allowed devices, and toggle lock state
• Provides isToggleDisabled getter implementing business logic (FR-013, FR-016)
• Implements copyWith() method for immutable state updates with selective props for equality

lib/usp_page/instant_privacy/providers/instant_privacy_state.dart


32. lib/usp_page/instant_privacy/models/instant_privacy_device_ui_model.dart ✨ Enhancement +39/-0

Instant Privacy device UI model definition

• New presentation layer model for device representation in Instant Privacy lists
• Stores normalized MAC address and display name (hostname or fallback to MAC)
• Implements Equatable for state comparison and provides serialization methods
• Used for both connected devices (feature OFF) and allowed devices (feature ON) lists

lib/usp_page/instant_privacy/models/instant_privacy_device_ui_model.dart


33. lib/usp_page/instant_privacy/views/components/instant_privacy_device_tile.dart ✨ Enhancement +40/-0

Instant Privacy device tile UI component

• New reusable widget component for displaying device information in tile format
• Shows device icon, display name, and MAC address with appropriate styling
• Used in both connected devices and allowed devices lists

lib/usp_page/instant_privacy/views/components/instant_privacy_device_tile.dart


34. lib/route/constants.dart ⚙️ Configuration changes +2/-0

Route constants for Instant Privacy feature

• Adds uspInstantPrivacy route path constant (/uspInstantPrivacy)
• Adds uspInstantPrivacy route name constant for named navigation

lib/route/constants.dart


35. lib/route/router_provider.dart ⚙️ Configuration changes +1/-0

Router provider import for Instant Privacy view

• Imports new instant_privacy_view.dart module with alias usp_instant_privacy

lib/route/router_provider.dart


36. lib/route/route_usp_dashboard.dart ⚙️ Configuration changes +5/-0

Instant Privacy route registration in dashboard

• Registers new /uspInstantPrivacy route in USP dashboard shell route
• Maps route to InstantPrivacyView widget with proper naming and path configuration

lib/route/route_usp_dashboard.dart


37. lib/usp_page/menu/views/usp_menu_view.dart ✨ Enhancement +6/-0

Instant Privacy menu entry in USP menu

• Adds "Instant Privacy" menu item to USP menu with lock icon
• Provides description "Lock network to currently connected devices"
• Routes to uspInstantPrivacy named route on tap

lib/usp_page/menu/views/usp_menu_view.dart


38. lib/generated/index.dart ⚙️ Configuration changes +2/-0

Export new MAC filter access points codegen

• Exports new mac_filter_access_points.g.dart codegen module

lib/generated/index.dart


39. lib/generated/subscriptions.g.dart Formatting +1/-0

Code formatting consistency

• Adds blank line after file header for consistency

lib/generated/subscriptions.g.dart


40. lib/generated/transforms.g.dart Formatting +1/-0

Code formatting consistency

• Adds blank line after file header for consistency

lib/generated/transforms.g.dart


41. definitions/wifi/mac_filter_access_points.yaml ⚙️ Configuration changes +28/-0

MAC filter access points YAML definition

• New YAML definition file for MAC filter access points codegen generation
• Defines multi-instance WiFi access point parameters for MAC filtering control
• Includes ssidReference as anchor field to prevent all-null filtering, macAddressControlEnabled
 and allowedMACAddress as writable parameters

definitions/wifi/mac_filter_access_points.yaml


Grey Divider

Qodo Logo

@qodo-code-review

qodo-code-review Bot commented Mar 17, 2026

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (3) 📘 Rule violations (2) 📎 Requirement gaps (0)

Grey Divider


Action required

1. uspInstantPrivacyProvider wrong directory 📘 Rule violation ⛯ Reliability
Description
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.
Code

lib/usp_page/instant_privacy/providers/instant_privacy_notifier.dart[R10-13]

+final uspInstantPrivacyProvider =
+    AsyncNotifierProvider<UspInstantPrivacyNotifier, UspInstantPrivacyState>(
+  UspInstantPrivacyNotifier.new,
+);
Evidence
PR Compliance ID 3 requires new/modified state providers to live under lib/providers/. The PR adds
uspInstantPrivacyProvider in
lib/usp_page/instant_privacy/providers/instant_privacy_notifier.dart, which is outside
lib/providers/.

CLAUDE.md
lib/usp_page/instant_privacy/providers/instant_privacy_notifier.dart[10-13]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## 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


2. InstantPrivacyView outside lib/page 📘 Rule violation ⛯ Reliability
Description
A new feature page (InstantPrivacyView) is introduced under lib/usp_page/... instead of
lib/page//. This violates the required feature page placement convention and can reduce UI
discoverability/consistency.
Code

lib/usp_page/instant_privacy/views/instant_privacy_view.dart[R14-17]

+/// Instant Privacy page — one-tap MAC whitelist to lock the network to
+/// currently connected devices only.
+class InstantPrivacyView extends ConsumerWidget {
+  const InstantPrivacyView({super.key});
Evidence
PR Compliance ID 5 requires new feature pages/screens to be placed under lib/page//. The PR adds
the new page at lib/usp_page/instant_privacy/views/instant_privacy_view.dart.

CLAUDE.md
lib/usp_page/instant_privacy/views/instant_privacy_view.dart[14-17]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The new `InstantPrivacyView` page was added outside `lib/page/&amp;amp;lt;feature_name&amp;amp;gt;/`, 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


3. Per-AP allowlist overwritten 🐞 Bug ✓ Correctness
Description
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.
Code

lib/usp_page/instant_privacy/services/instant_privacy_service.dart[R98-126]

+  /// 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();
+  }
Evidence
The YAML definition explicitly scopes AllowedMACAddress to “this access point”, but the service
reads only the first AP’s value and applies it to all APs (read-from-first, write-to-all), which
will overwrite per-AP differences.

definitions/wifi/mac_filter_access_points.yaml[12-28]
lib/usp_page/instant_privacy/services/instant_privacy_service.dart[43-59]
lib/usp_page/instant_privacy/services/instant_privacy_service.dart[98-125]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## 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



Remediation recommended

4. Unvalidated MAC reaches USP 🐞 Bug ⛯ Reliability
Description
UspInstantPrivacyNotifier.addMac() forwards the provided string into
buildAddMacUpdates()/updateMany() without enforcing validation/normalization despite having
utilities for this. While the current dialog validates input, any other caller can pass
invalid/unnormalized values and push malformed allowlists to the router.
Code

lib/usp_page/instant_privacy/providers/instant_privacy_notifier.dart[R105-121]

+  /// Adds [mac] to the allowed list across all APs.
+  /// Precondition: [mac] is validated and normalized by the caller.
+  Future<void> addMac(String mac) async {
+    final s = state.valueOrNull;
+    if (s == null || !s.isEnabled) return;
+
+    state = AsyncData(s.copyWith(isToggleLocked: true));
+    try {
+      final usp = ref.read(uspServiceProvider)!;
+      final updates = _svc.buildAddMacUpdates(mac, s.rawMacFilterAps);
+      if (updates.isEmpty) {
+        state = AsyncData(s.copyWith(isToggleLocked: false));
+        return;
+      }
+      await MacFilterAccessPoints.updateMany(usp, updates);
+      logger.d('[USP] Instant Privacy addMac — $mac');
+      ref.invalidateSelf();
Evidence
The notifier explicitly relies on an external precondition for validation, but the service provides
validate/normalize helpers and nothing enforces the precondition at the boundary where router writes
occur.

lib/usp_page/instant_privacy/providers/instant_privacy_notifier.dart[105-121]
lib/usp_page/instant_privacy/services/instant_privacy_service.dart[132-138]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`addMac()` relies on a comment-only precondition (“validated and normalized by the caller”) and can send malformed MAC strings to USP if called by any non-dialog code path.
### Issue Context
Even if the current dialog validates input, the notifier method is a public write boundary that should defensively validate/normalize.
### Fix Focus Areas
- lib/usp_page/instant_privacy/providers/instant_privacy_notifier.dart[105-121]
- lib/usp_page/instant_privacy/services/instant_privacy_service.dart[132-138]
- lib/usp_page/instant_privacy/services/instant_privacy_service.dart[103-126]
### Implementation notes
- In `addMac`, trim/normalize the input and call `validateMac`; if invalid, throw (or return a typed failure) before calling `buildAddMacUpdates`.
- Optionally harden `buildAddMacUpdates` as well (e.g., assert/validate `newMac`) to keep it safe if called from elsewhere.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


5. No APs handled poorly 🐞 Bug ⛯ Reliability
Description
enable() and disable() do not handle the case where MacFilterAccessPoints.fetch() returns zero AP
items: updateMany becomes a no-op (no params) but the notifier still logs success and refreshes.
This produces confusing behavior (toggle appears to act but immediately reverts after refresh)
instead of surfacing an explicit unsupported/empty-data error.
Code

lib/usp_page/instant_privacy/providers/instant_privacy_notifier.dart[R69-81]

+  Future<void> enable() async {
+    final s = state.valueOrNull;
+    if (s == null || s.isEnabled) return;
+
+    state = AsyncData(s.copyWith(isToggleLocked: true));
+    try {
+      final usp = ref.read(uspServiceProvider)!;
+      final macs = s.connectedDevices.map((d) => d.mac).toList();
+      final updates = _svc.buildEnableUpdates(macs, s.rawMacFilterAps);
+      await MacFilterAccessPoints.updateMany(usp, updates);
+      logger.d('[USP] Instant Privacy enabled — ${macs.length} MACs');
+      ref.invalidateSelf();
+    } catch (e) {
Evidence
If rawMacFilterAps.items is empty, the update list/params become empty and the generated
updateMany will skip the USP Set; the notifier currently doesn’t guard that scenario or report it as
an error.

lib/usp_page/instant_privacy/providers/instant_privacy_notifier.dart[67-81]
lib/generated/mac_filter_access_points.g.dart[88-96]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
If `MacFilterAccessPoints.fetch()` yields zero items, enable/disable will call `updateMany` with effectively no parameters, resulting in no USP SET being sent, while still logging success and refreshing.
### Issue Context
Generated `updateMany` only calls `client.set` when the built params map is non-empty.
### Fix Focus Areas
- lib/usp_page/instant_privacy/providers/instant_privacy_notifier.dart[67-103]
- lib/usp_page/instant_privacy/services/instant_privacy_service.dart[65-96]
- lib/generated/mac_filter_access_points.g.dart[88-96]
### Implementation notes
- In `enable()`/`disable()`, guard `s.rawMacFilterAps.items.isEmpty` (or `updates.isEmpty`) and surface a clear error (e.g., throw `StateError(&amp;amp;#x27;No WiFi access points available&amp;amp;#x27;)`).
- Only log &amp;amp;quot;enabled/disabled&amp;amp;quot; after verifying a non-empty SET was sent.
- Optionally, represent “feature unsupported” in state so the UI can disable the toggle with an explanatory message.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

ⓘ The new review experience is currently in Beta. Learn more

Grey Divider

Qodo Logo

Comment on lines +10 to +13
final uspInstantPrivacyProvider =
AsyncNotifierProvider<UspInstantPrivacyNotifier, UspInstantPrivacyState>(
UspInstantPrivacyNotifier.new,
);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

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

Comment on lines +14 to +17
/// Instant Privacy page — one-tap MAC whitelist to lock the network to
/// currently connected devices only.
class InstantPrivacyView extends ConsumerWidget {
const InstantPrivacyView({super.key});

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

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

Comment on lines +98 to +126
/// 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();
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

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 AustinChangLinksys left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks Good To Me

@AustinChangLinksys
AustinChangLinksys merged commit 897cd9b into dev-2.2.0 Mar 17, 2026
2 checks passed
@AustinChangLinksys
AustinChangLinksys deleted the hank/usp-instant-privacy branch March 17, 2026 06:25
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.

2 participants