Skip to content

v2.10.2 Release Changes - #1318

Merged
tvancott42 merged 22 commits into
mainfrom
dev
Oct 26, 2026
Merged

tvancott42 merged 22 commits into
mainfrom
dev

Conversation

@tvancott42

Copy link
Copy Markdown
Collaborator

Release changes for v2.10.2. Wi-Fi Optimizer metrics work on UniFi Network 11, which serves its Wi-Fi history from a new API: Metrics, Environment, Client Performance's signal history from UniFi Network, and Channel Recommendation's history read it now, and Metrics charts share Monitoring's hover. Device offline alerts confirm the device went dark by ping first: one still passing traffic raises Device Not Reporting, and most of a site going quiet together raises one UniFi Network Not Hearing From Devices alert; alerts left open across a restart of Network Optimizer now close (#1317). Three more cable modems: FRITZ!Box 6690 Cable (#1299, @gmdfalk), ARRIS SURFboard SB6141 (#1303), and Xfinity XB7 (CGM4331), with port 443 connecting to the .jst gateways over HTTPS (#1302, HAR from @papisosa in #1294). Fixes include Security Audit acknowledgements hiding every finding with the same title (#1314, @Mancolt) and a network read as DHCP off on a console that stores the setting as text, a UniFi Console behind a reverse proxy showing as connected while it was down, and double-clicking a client on the Live View map. The license moves to a rolling three-year Change Date from this version on.

Agent

The On-Site Agent's link closure changed only in Core/Helpers/JsonExtensions.cs (GetBoolOrDefault reading string booleans, for Security Audit), and nothing the Agent, AgentProtocol, or Monitoring projects compile calls it. The Agent is unchanged since v2.10.1, so LatestAgentVersion and RequiredAgentVersion stay as they are; the release note carries the v2.10.1 callout forward.

Merge method

main carries one README-only commit not on dev (b3f216a3d, the Docker Pulls badge). git merge-tree origin/main origin/dev is clean, so a rebase merge replays without conflicts and keeps it; dev is reset to main afterwards as usual, then merged forward into release/2.11.

Checks

  • dotnet list package --vulnerable --include-transitive: no vulnerable packages.
  • Feature index: FEATURES.md updated on dev (64a975897).
  • Guided tour: none for this patch.

tvancott42 and others added 22 commits October 25, 2026 11:27
A client already on the map kept the node it was first drawn with. The
snapshot refresh updated only RF fields (band, signal, PHY rate), so a
client added by the AP Agent live overlay, or listed by the console
before its DHCP lease, stayed without an IP until a page reload. With no
IP, double-click had nothing to open.

Both maps now copy ip, name, ssid, network, and isGuest from each
refreshed snapshot onto the persisting client node: the 2D map in
_updateSnapshotData, the 3D map in the incremental snapshot path.
GetIssueKey used Title|DeviceName|Port. Firewall, DNS, and network-level findings have no port, so dismissing one hid every finding with the same title (all Rule Order Issues, each WAN's DNS findings on one gateway). Findings with no port and no client now append a hash of the message. Existing dismissals of those findings resurface once; port and client keys are unchanged.
UPnP mappings, static port forwards, Management VLAN reservations, and DNS device checks put live counts in the message, so a message-hashed key resurfaced them every audit. Those types now key on the network (empty for the gateway-wide ones) via DismissalScope.
Each version now converts to Apache 2.0 three years after it is published, in place of the fixed 2028-01-01. Versions already published keep 2028-01-01. Updated in LICENSE, the README summary, and the installer's LICENSE.txt and License.rtf.
DeviceStateAlertEvaluator kept its announced-offline flag only in memory.
A restart while a device was dark reset it, so the device's return
published nothing and its device.offline stayed open for good: Alerts,
Site Health, and Change Control incidents all read it as still offline.

The evaluator now reads the site's unresolved alerts once before its
first evaluation and marks every device whose device.offline has no
later device.recovered as announced. The return publishes the recovery,
and a device still dark at start-up is not announced a second time.
Some consoles store a flag as the string "true" (seen on a default LAN's
dhcpd_enabled). GetBoolOrDefault took only JSON bools, so the audit read that
network as DHCP off and skipped its DHCP checks. It now parses "true" / "false"
as well, for every caller.
…pen by a restart (#1317)

"Disconnected" in UniFi Network only means a device stopped informing. A
Network restart or an inform URL change does that to every device at once
with no traffic lost, and NO announced each one as device.offline, which Site
Health then scored as lost availability.

Device offline confirmation
- On the first offline observation the evaluator checks the device on the
  data plane: its fabric target's latest result when under 30 s old, else a
  3-ping ICMP probe from the vantage that owns the site's path measurement
  (this server, or the On-Site Agent over the existing ProbeRequest; no
  protocol change). The result is in hand by the second observation, so
  device.offline goes out no later than before.
- No answer, or a probe that could not run: device.offline, as before.
- Answers: held as management-only. Re-informing inside 10 minutes publishes
  nothing. Past the hold: device.unmanaged (Warning), closed by device.managed,
  never device.recovered.
- A held or unmanaged device that stops answering is device.offline at once,
  with unmanaged_since in its context.
- At least half of a site's devices (and at least 3) held together collapse
  into one console.devices_unmanaged, closed by console.devices_managed; any
  still out when the rest return split into their own alerts. Decided in a new
  end-of-pass call from both device state loops.
- MonitoringAlertEvaluator indexes the latest fabric result per MAC for the
  lookup. New default rules for the four types; rule editor catalog and
  Device Stats marks updated.

Restart recovery
Every paired evaluator kept its open state in memory only, so a restart
mid-outage left the alert open for good. OpenAlertSeed reads the site's
unresolved openings (7-day lookback) and the recoveries since, and each
evaluator starts a subject open when its opening has no later recovery:
device state, monitoring targets, WAN outage (per WAN and the all-WANs
rollup), health checks, Starlink (each rule; a seeded outage burst is held
until its window refills), WAN link speed, On-Site Agent, and console
connection. AlertPairs in NetworkOptimizer.Alerts maps each opening to its
recovery. Agent alerts now carry agent_id, and Starlink dish faults
self_test_regressed, so they can be paired back.
…wn now reads as down

With UniFi OS Server stopped behind Traefik, every request got an instant
502 from the proxy. The silent-console breaker only counts timeouts, and
failed sign-ins only fed the connection alert, so IsConnected stayed true:
no page showed the console down, the background reconnect (which runs only
while disconnected) never ran, and every page call signed in again on its
502 - 711 sign-ins in three minutes, enough to hit the console's login
attempt limit.

Sign-ins failing past the alert's window (two failures at least 50 s apart)
now also mark the console unresponsive, with the sign-in error as the
reason. Pages show the banner, calls stop signing in per call, and the
background reconnect brings it back.
…n-in, recover in 10 s

The banner waited on the alert's 50 s window, so a console behind a
reverse proxy sat "connected" for a minute after it went down. That window
exists to keep blips out of notifications, not out of the page state.

A sign-in that never reached the console (502/503/504 from the proxy, or a
refused, failed, or timed-out connection) now marks it down at once. A
rejected sign-in (401/403, login limit) and certificate errors do not; the
console connection alert keeps its 50 s window.

The background reconnect while down runs every 10 s instead of 30 s, so
the banner clears within about 10 s of the console answering again.
… not 10

The hold only runs while the console is up and lists the device disconnected; a console or Network that is down skips the device pass entirely. So it only has to cover the re-inform gap after Network returns, which ran 1-2.5 minutes in testing. The active-device window drops to 2 minutes so a device removed while held still falls out before its hold expires.
The 2.10.0 step's anchor was only on the card, which folds to one line once every step is
done. On those sites the step never showed and carried into every later release's offer.
Network 11 moved Wi-Fi time series to v2/api/stats/recent/series.
stat/report still answers rc ok there, with no rows, so Wi-Fi Optimizer
metrics, AP metrics, and client history showed no data.

The client probes v2/api/stats/schemas once per Console connect: 200
means the stats API, 404 means stat/report (Network 10). On the stats
API the site, AP, and user report calls query wirelessBySite,
wirelessBySiteByDevice, and activityBySiteByClient, and
StatsReportTranslator rebuilds the legacy rows, so no caller changes.

- The client schema is in the client's view: its rx retries, attempts,
  and packets map to the legacy (AP-side) tx attrs.
- tx_rate takes the most common rate tier; the stats API has no average.
- The console picks the bucket from the range (5 min up to a day, then
  hourly, then 8 hours), and holds about 31 days.
The stats API picks its own bucket from the range: 5 minutes for a range
up to a day. Callers count report rows as hours or days, so an hourly
request over a short window came back as 12 rows per hour. Channel
memory's 6-hour collection then recorded each console hour 12 times.

Points finer than the requested bucket (hourly, daily) now merge into
it: durations, packets, and bytes sum; max and distinct counts take the
max; min the min; other values a duration-weighted mean. Nested band and
AP entries merge by their key fields.
The band charts now use the same tooltip as Monitoring and the speed test
charts: hover dots on every line, rows in series order (Airtime,
Interference, TX Retries, Channel), percentages, and Ch N.

The shared dot pass skips a transparent series: Channel is drawn
transparent to carry its value into the tooltip, and would otherwise
get a dot ring floating on the plot.
…the AP

Checked on Network 11.0.94 against stat/sta and AP Agent counters, per
client over the same buckets. The activityBySiteByClient counters are in
the client's view (rxPackets, rxBytes, wifiRxRetryPackets, and
wifiRxAttemptPackets match the AP's tx counters), so their mapping
stands. mostCommonRxRateTierKbps matches stat/sta's rx_rate (client to
AP), not tx_rate, so it now fills rx_rate. There is no AP-to-client rate
in the stats API, so tx_rate stays empty.
* Add FRITZ!Box Cable DOCSIS monitoring (#1)

Adds FRITZ!Box Cable as a selectable DOCSIS provider, using the existing modem monitor, encrypted credentials and site device dialer. Verified with a FRITZ!Box 6690 Cable running FRITZ!OS 8.40; other models and firmware remain unverified.

The provider uses AVM challenge-response authentication, reads `docInfo` channel data and logs out its own session after each operation. It maps downstream/upstream RF readings, normalizes MSE/MER and frequency units, and reads optional model/firmware metadata. Settings changes only add the provider option, labels and help; existing configuration defaults and credential/save/test workflows are unchanged.

Following the Netgear/UCI convention, FEC totals include DOCSIS 3.0 channels only. OFDM/OFDMA RF readings are included, with frequency bands represented by their midpoint. No shared interface, model, database, Influx, chart or scoring changes are required.

## Validation

- 40 new tests cover AVM authentication vectors, captured channel data, conversions, counter scope, device routing, failures, cancellation, overlapping sessions and logout.
- All 157 selected new/existing cable-modem, model-label and routed-device regression tests passed.
- Solution Release build passed with .NET SDK 10.0.200. On Apple Silicon, existing protobuf artifacts were generated using the pinned gRPC package's Linux ARM64 tools before the native build; no build-system changes are included.
- Full native solution run: 12,479 passed and 23 failed. The exact same 23 failing tests reproduced when rebuilding untouched base commit `6f116ec` and running the affected test classes under the same host conditions (locale-dependent formatting/parsing and macOS AES-CCM support).
- A harness referencing the actual compiled Web/Monitoring/Core assemblies successfully ran TestConnection and two polls against the 6690, returning 26 downstream and 4 upstream channels. Credentials and sessions are absent from the committed fixture.

## Remaining acceptance

The deployed Settings save → scheduled polling → dashboard/Influx history path has not been exercised. Live device access was tested from the development host; site routing is covered by tests. Lock status is inferred from assigned channels; registration state, uptime, event logs and upstream symbol rate are left unavailable where the source does not report them.

* Reuse FRITZ!Box sessions and address upstream review feedback

---------

Co-authored-by: falkhq-admin <accounts@falkhq.de>
Poll_HttpsAcceptsDeviceCertificateAndStillUsesSiteDialer failed on Windows
with "the platform does not support ephemeral keys": SChannel cannot serve
TLS from a certificate made by CertificateRequest.CreateSelfSigned. The
test server now round-trips it through a PFX. CI (Ubuntu) passed either way.
* refactor: move Netgear's lenient raw-HTTP reader into a shared LenientHttp helper

The ARRIS SB6141 runs the same NET-DK/1.0 web server as the Netgear CM700, whose header-byte corruption forced the raw-socket fallback. Moving the generic part (malformed-response detection, the HTTP/1.0 raw GET, the tolerant response parser, and the header/body split) into LenientHttp lets the SURFboard provider reuse it instead of carrying a second copy.

The Netgear XSRF cookie handshake stays in NetgearCmProvider and now calls the helper. Netgear behavior is unchanged. The six ParseRawHttpResponse tests move to LenientHttpTests unchanged; the FetchViaRawSocketAsync socket tests stay with Netgear.

* feat: ARRIS SURFboard SB6141 support in the ARRIS Surfboard (HTTP) provider

The SB6141 serves neither the SB8200 nor the SB6183 status page. Its /cmSignalData.htm holds three transposed tables (Downstream, Upstream, Signal Stats (Codewords)) with one channel per column, and a note table nested in the downstream Power Level label. Pointed at that page, the SB6183 parser read the upstream table as downstream and reported one junk upstream channel.

The provider now tries the SB6141 after the SB8200 and SB6183 steps:
- The step accepts /cmSignalData.htm only when it carries both 'Bonding Channel Value' and 'Signal Stats (Codewords)'.
- A Status Page Path set to that page is parsed as an SB6141 by content, not by the step that fetched it.
- An SB6183 page that parses to no channels lets the SB6141 step run first, and is still reported Ok as before when no SB6141 answers. An SB6183 page with channels never reaches the new step.
- A detected SB6141 is remembered per CacheKey, so later polls skip the TLS and /RgConnect.asp probes against the fragile NET-DK server.
- Fetches fall back to LenientHttp when HttpClient rejects a malformed response, because NET-DK/1.0 corrupts header lines on the CM700.

Parsing: channels are read per column and joined to codewords by Channel ID. A listed channel is Locked (the page has no lock column); a column with frequency 0 is skipped. Upstream Ranging Status 'Success' maps to Locked, symbol rate is stored in sym/s, and the burst-profile list keeps the highest-order modulation (64QAM). Model and firmware come from /cmHelpData.htm, status and uptime from /indexData.htm; a failure on either page does not fail the poll.

Also: README lists the SB6141, and the Settings Status Page Path placeholder for this provider reads 'Detected automatically' instead of /cgi-bin/status, which no supported model uses.

Fixtures are from a user's HAR; the serial number is zeroed and the address page is not included.

* ARRIS SB6141: write the non-breaking space as an escape, not a literal character
* Xfinity Gateway provider: HTTPS for the XB7 / CGM4331

The XB7 (Technicolor CGM4331) serves the same .jst UI, login, and DOCSIS
tables as the XB8/XB10, CGA4332, and CGM4981, but over HTTPS with a
self-signed certificate. The provider only spoke plain HTTP, so no port or
host setting could reach it (#1294).

- Port 443 now means HTTPS, as in the Technicolor CGA, Sagemcom, and
  Vodafone providers, and the gateway's self-signed certificate is accepted.
- On the default port (blank or 80), HTTP still goes first, so gateways that
  answer it keep one login. HTTPS on 443 follows when HTTP fails to connect,
  or when the login POST was redirected to HTTPS (the redirect turns the POST
  into a GET and drops the credentials, so the login is repeated).
- The scheme that served stats is remembered per modem, next to the
  discovered status page path. It is dropped when a poll fails or the port
  setting changes, so the next poll probes again.
- Label adds XB7: "Xfinity XB7/XB8/XB10, Comcast Business CGA4332, Cox
  CGM4981 (HTTP)". Settings search gains xb7, cgm4331, and rogers (the
  reporter's unit carries Rogers branding). README lists the XB7.

Tests: a trimmed CGM4331 network_setup.jst fixture (34 DS / 5 US, OFDMA
typed from the Modulation row, primary-channel codeword echo cleared,
model "XB7 (CGM4331COM)"), and the scheme selection: origin per port and
scheme, fallback only from HTTP on the default port, and the per-modem
scheme cache.

* Xfinity Gateway provider: drop the HTTP-to-HTTPS fallback

The reporter's curl output shows the XB7 / CGM4331 serves its UI over plain
HTTP on port 80 (200, login page) as well as HTTPS on 443. The HAR was HTTPS
only because the browser upgraded the session. The fallback and per-modem
scheme cache solved a problem this gateway does not have, so they go.

Kept: port 443 selects HTTPS with the gateway's certificate accepted, the
XB7 label, and the CGM4331 fixture and parse tests.
Supported Hardware: FRITZ!Box 6690 Cable, ARRIS SB6141, and the Xfinity XB7 (with HTTP or HTTPS on the .jst gateways). Alerts & Schedule: device offline confirmed by ping first, Device Not Reporting, and UniFi Network Not Hearing From Devices.
@tvancott42
tvancott42 merged commit 736a718 into main Oct 26, 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.

2 participants