Skip to content

Release: WAN data usage tracking, audit improvements, and fixes#374

Merged
tvancott42 merged 4 commits intomainfrom
dev
Feb 27, 2026
Merged

Release: WAN data usage tracking, audit improvements, and fixes#374
tvancott42 merged 4 commits intomainfrom
dev

Conversation

@tvancott42
Copy link
Collaborator

@tvancott42 tvancott42 commented Feb 27, 2026

Summary

Test plan

  • Verify WAN data usage tab loads and shows WAN interfaces
  • Enable tracking on a WAN, confirm snapshots begin recording
  • Verify usage bar, billing cycle dates, and days remaining display correctly
  • Test manual usage adjustment with positive and negative values
  • Run security audit and verify 802.1X ports with curated VLANs are not flagged
  • Verify hardware acceleration warning suppressed with NetFlow enabled

tvancott42 and others added 4 commits February 26, 2026 16:05
Ports with 802.1X/RADIUS MAC-based auth legitimately need tagged VLANs
for dynamic VLAN assignment. If the admin has curated a custom VLAN set,
skip entirely. If "Allow All" is configured, downgrade to Informational
severity with score impact 2 (instead of Recommended/6).

Add overrideSeverity and overrideScoreImpact to CreateIssue base method
so individual rules can downgrade specific cases without changing the
rule's default severity.
…#372)

NetFlow requires CPU-based packet inspection, making Hardware
Acceleration incompatible. Skip the recommendation when the controller
settings show NetFlow is active.

Also adds [VendorSpecific] attribute to Core for tracking raw-JSON
parsing spots that need strong typing and eventual vendor abstraction.
* Add WAN data usage tracking with billing cycle alerts

Poll WAN byte counters every 2 minutes, store snapshots, calculate
billing-cycle usage, and alert when thresholds are crossed. New Data
Usage tab in Alerts page with per-WAN config (cap, warning %, billing
day) and usage progress bars. Handles counter resets on gateway reboot.

* Use friendly WAN names from network config and make alert rule enable atomic

- Resolve WAN friendly names via ConnectionService.GetNetworksAsync() matching
  by network group (same pattern as WAN Speed Test and Adaptive SQM)
- Move EnsureAlertRulesEnabledAsync before SaveChangesAsync so config save
  and alert rule enable are atomic in a single transaction

* Use friendly WAN names from network config, fix ordinal formatting

- Use NetworkInfo from GetNetworksAsync() as WAN source (same as speed
  test dropdowns) instead of device-level GatewayWanInterface
- Key configs by WanNetworkgroup (WAN, WAN2) not device key (wan1, wan2)
- Map device byte counters to network groups via WanKeyToNetworkGroup()
- Add DisplayFormatters.FormatOrdinal() for correct ordinal suffixes
  (21st, 22nd, 23rd instead of 21th, 22th, 23th)
- Add tests for WanKeyToNetworkGroup and FormatOrdinal

* Replace inline ordinal logic in FloorPlanEditor with DisplayFormatters.FormatOrdinal

* Fix FormatOrdinal for negative numbers using Math.Abs

Prod-vetted FloorPlanEditor code used Math.Abs() for ordinal suffix
logic. Without it, -11 would incorrectly get "st" instead of "th"
because C# modulo preserves sign (-11 % 100 = -11, missing the
11/12/13 teen check).

* Fix Data Usage tab: load on fresh page load, fix WAN status dots, show interface name

- Add data-usage case to OnInitializedAsync so F5 loads WAN data
- Fetch live WAN up/down status from device API instead of relying on
  summary (which only exists for enabled WANs)
- Show interface name in parens next to friendly name: "Starlink (WAN)"
- Fix "UniFi controller" to "UniFi Console" in user-facing text

* Fix Schedule tab missing WAN interfaces when navigated to from another tab

* Consolidate NormalizeWanDisplay into shared DisplayFormatters helper

Move WAN -> WAN1 display normalization from inline WanSpeedTest method
to DisplayFormatters.NormalizeWanDisplay(). Used by both WanSpeedTest
and Data Usage tab for consistent WAN interface labels.

* Show auto-managed alert rules notice with link to Rules tab

* Use gateway uptime to establish baseline data usage on first snapshot

When tracking is first enabled for a WAN, if the gateway booted within
the current billing cycle, the cumulative byte counters represent all
usage since boot (which is all within this cycle). Mark the first
snapshot as IsBaseline so the usage calculation includes the raw bytes
as starting usage instead of requiring deltas to accumulate from zero.

* Add manual usage adjustment with explicit Save button

- Consolidate migrations into single AddWanDataUsageTables migration
- Add ManualAdjustmentGb to config (additive offset for known usage)
- Add IsBaseline to snapshots (gateway uptime baseline detection)
- Replace auto-save on every field change with explicit Save button
- Lock manual adjustment field by default, click Edit to unlock
- Include ManualAdjustmentGb in usage total calculation

* Fix adjustment UX: rename Lock to Done, hide Save button when not dirty

* Align Usage Adjustment label with other config field labels

* Simplify adjustment field: readonly by default, click to unlock, locks on save/reload

* Vertically center status dots with WAN name

* Re-create data usage alert rules if deleted by user

EnsureAlertRulesEnabledAsync only enabled existing rules but did
nothing if they had been deleted. Now re-creates them as enabled.

* Fix ManualAdjustmentGb not persisting and usage bar missing on fresh load

SaveConfigAsync update path was missing ManualAdjustmentGb copy to
existing entity. GetCurrentUsage was synchronous and returned empty
list before background poll ran. Added async fallback that calculates
from DB snapshots when in-memory cache is empty.

* Invalidate usage cache after config save, rename adjustment to Prior Usage

Bar wasn't updating after saving a manual adjustment because
GetCurrentUsageAsync returned stale cached summaries. Clear cache
after SaveConfigAsync so the next load recalculates from DB.

Renamed "Usage Adjustment" to "Prior Usage" with clearer tooltip.

* Fix prior usage field losing focus on click

Single input element with toggled readonly attribute instead of
swapping between two elements. Uses onfocus to unlock so the same
element stays focused.

* Rename Billing Cycle Day to Usage Resets On with tooltip

Users don't know what day to enter - the old label was ambiguous.
New label asks when the ISP resets data usage, with tooltip guidance
to check the carrier renewal/due date.

* Fix tooltip icon vertical alignment and prevent field label wrapping

Tooltip icon-sm was sitting low on field labels - added
vertical-align: text-top. Schedule field labels now use
white-space: nowrap to prevent wrapping.

* Fix data usage config fields layout - all 5 fields on one row

Display Name gets flex:2 (wide), numeric fields (Data Cap, Warning,
Prior Usage) get compact sizing with min-width 110px so all fields
fit on a single row without wrapping.

* Constrain compact field width to 140px max

* Show Save button immediately on input, not on blur

Added @oninput to mark fields dirty as the user types. The @onchange
still handles the actual value commit on blur. Save Changes button
now appears instantly when any field is modified.

* Remove Display Name field - name comes from UniFi network config

Users already set the WAN name in UniFi Console, having an editable
Display Name here is redundant. The stored name is still set from
the UniFi network name on creation for use in alert messages.

* Stop config fields from stretching full width on desktop

All 4 fields now use compact sizing so the row doesn't span 100%.
Usage Resets On gets slightly wider max (180px) for the dropdown.

* Auto-refresh Data Usage tab every 30 seconds

The refresh timer already handled active and schedule tabs but not
data-usage. Users staying on the tab now see updated usage.

* Trigger immediate poll on enable and show Save button

When toggling tracking on, immediately poll the device API for a
first snapshot so usage is calculated right away instead of showing 0.
Also marks config dirty so the Save button is visible for the user
to configure cap, billing day, etc.

* Always calculate usage from DB instead of using in-memory cache

The cache caused stale data after toggling tracking on. The DB query
is cheap (small indexed table) so just always calculate fresh.
- Add SemaphoreSlim to serialize PollAndRecordAsync (background loop + UI trigger)
- Use ExecuteDeleteAsync in DeleteConfigAsync to avoid loading all snapshots into memory
- Reset ManualAdjustmentGb to 0 on billing cycle rollover
- Clamp DataCapGb to >= 0 server-side
- Floor usedGb at 0 so negative adjustments can't produce negative display
- Use Math.Ceiling for DaysRemaining so last day of cycle shows "1" not "0"
- Add CancellationToken to GetCurrentUsageAsync
- Only set _dataUsageLoaded on success so tab retries on console disconnect
- Rename "Prior Usage" to "Usage Adjustment" and allow negative values
@tvancott42 tvancott42 merged commit 3348696 into main Feb 27, 2026
1 check 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