Skip to content

fix(viewer): show the splash page once a WiFi setup portal ends - #3412

Merged
vpetersson merged 4 commits into
masterfrom
agent/anthias-claude/e9c91335a467
Oct 27, 2026
Merged

vpetersson merged 4 commits into
masterfrom
agent/anthias-claude/e9c91335a467

Conversation

@vpetersson-bot

@vpetersson-bot vpetersson-bot commented Oct 23, 2026 •

Copy link
Copy Markdown
Contributor

Issues Fixed

Fixes #3411

Description

A device set up from the WiFi captive portal never showed its address. The boot
splash runs once, minutes before anyone picks a network on their phone, and at
that point the only interface up is the device's own access point — so the one
screen carrying the IP and admin URL went past before there was an address to
put on it. When the portal came down the viewer fell straight through to the
playlist, which on a first boot is empty, and the screen sat on the standby
logo with no way to find the web UI.

  • The wifi-setup reporter arms a one-shot replay on the portal's
    active → inactive edge. asset_loop puts /splash-page up at once,
    holds it until the device has an address someone can reach it on,
    then shows it for the full SPLASH_DELAY before handing the display
    back to the playlist.
  • Only on an answered poll. An unreachable server already drops the setup
    screen deliberately (failing towards playback), but it says nothing about a
    portal having finished — and the splash is served by the server that just
    failed to answer, so arming there would trade the setup screen for a blank
    page.
  • The flag is cleared before the page goes up, so a second portal run starting
    and ending inside the splash window cannot queue a second splash on top of
    the playlist it is finally free to play.
  • The countdown starts when the address appears, not when the portal
    goes down
    (from hardware testing in WiFi setup: the screen shows the standby logo instead of the IP/admin URL after joining WiFi via the portal #3411, where the address was on
    screen for under 30 s of the 60: the device still has to join the
    chosen network and get a lease). The viewer polls the same
    /api/v2/network/ip-addresses endpoint the page uses until it returns
    a non-loopback, non-link-local address that is not the portal's own
    gateway, capped at POST_WIFI_SETUP_ADDRESS_TIMEOUT_S (180 s).
    /api/v2/network/wifi-setup now reports the live portal's gateway
    so the viewer can exclude it once the portal is gone and a lagging
    address cache still lists it.
  • The wait ends early if the portal comes back up, which is what a
    failed join looks like (wifi-connect drops the AP to try the network,
    then re-raises it), so the setup screen is not held off by the splash.
  • The splash page stops polling for addresses 120 s after it loads, so
    an address that arrives near or after that point reloads the page.

Verification

The hardware runs below cover the original change (db37554). The
follow-up that holds the splash until an address appears (fcb079f) is
covered by unit tests and needs a retest on hardware.

Both boards ran the same scenario against the published latest-<board> image
with only src/anthias_viewer/__init__.py overlaid from this commit — the
in-container file was byte-identical to master beforehand, so nothing else
rode along. A portal run was driven at the two boundaries Anthias actually
reads: wifi-connect's --state-file on the shared resin-data volume (with a
live heartbeat) and the portal gateway held on a WiFi interface, both removed
together to represent a successful join. Nothing inside Anthias was stubbed.

Pi 4 (pi4-64, eglfs) x86 (cage/wayland)
Portal up GET /hotspot/ GET /hotspot/ — setup screen with SSID + passphrase
After the join, before no page load; standby logo standby logo, pixel-identical to idle
After the join, after GET /splash-page/, then its IP poll returns the board's LAN address splash page showing the board's address and QR
Splash window one load only, hands back after SPLASH_DELAY same

Framebuffer captures on the x86 board (grim through cage's headless output)
confirm the before/after pair visually: standby logo before, splash page with
the address after.

Unrelated note: the splash page's client-side IP poll keeps running after the
viewer navigates to the standby image, because the webview's image path does
not tear down the previous page's JS context. Confirmed pre-existing on a plain
boot splash with no portal involved, so it is left alone here.

Checklist

  • I have performed a self-review of my own code.
  • New and existing unit tests pass locally and on CI with my changes.*
  • I have done an end-to-end test for Raspberry Pi devices.
  • I have tested my changes for x86 devices.
  • I added a documentation for the changes I have made (when necessary).

* One pre-existing failure, not from this change:
tests/test_viewer.py::TestWaitForWaylandSocket::test_probe_reports_not_ready_for_a_non_socket_path
fails under pytest -n auto and passes when run alone. Reproduced identically
on a clean master tree. Everything else: 2559 passed, 3 skipped.

🤖 Generated with Claude Code

A device set up from the captive portal never showed its address. The
boot splash runs once, minutes before anyone picks a network on their
phone, and at that point the only interface up is the device's own
access point -- so the one screen carrying the IP and admin URL went
past before there was an address to put on it. When the portal came
down the viewer fell straight through to the playlist, which on a
first boot is empty, and the screen sat on the standby logo with no
way to find the web UI.

- The reporter arms a one-shot replay on the portal's active->inactive
  edge, and asset_loop shows /splash-page for SPLASH_DELAY before
  handing the display back to the playlist.
- Only on an answered poll. An unreachable server already drops the
  setup screen deliberately, but it says nothing about a portal having
  finished -- and the splash is served by the server that just failed
  to answer.
- The flag is cleared before the page goes up, so a second portal run
  inside the splash window cannot queue a second splash.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: multica-agent <github@multica.ai>
@vpetersson-bot
vpetersson-bot requested a review from a team as a code owner October 23, 2026 20:21
Copilot AI lite review requested due to automatic review settings October 23, 2026 20:21
@vpetersson-bot vpetersson-bot self-assigned this Oct 23, 2026

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Copilot encountered an error: Your billing is not configured or you have Copilot licenses from multiple standalone organizations or enterprises. To use premium requests, select a billing entity via the GitHub site, under Settings > Copilot > Features.

@vpetersson-bot
vpetersson-bot requested a lite review from Copilot October 23, 2026 20:21

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Copilot encountered an error: Your billing is not configured or you have Copilot licenses from multiple standalone organizations or enterprises. To use premium requests, select a billing entity via the GitHub site, under Settings > Copilot > Features.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Copilot encountered an error: Your billing is not configured or you have Copilot licenses from multiple standalone organizations or enterprises. To use premium requests, select a billing entity via the GitHub site, under Settings > Copilot > Features.

vpetersson
vpetersson previously approved these changes Oct 24, 2026
Copilot AI lite review requested due to automatic review settings October 24, 2026 09:07

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Copilot encountered an error: Your billing is not configured or you have Copilot licenses from multiple standalone organizations or enterprises. To use premium requests, select a billing entity via the GitHub site, under Settings > Copilot > Features.

Copilot AI lite review requested due to automatic review settings October 27, 2026 08:12

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Copilot encountered an error: Your billing is not configured or you have Copilot licenses from multiple standalone organizations or enterprises. To use premium requests, select a billing entity via the GitHub site, under Settings > Copilot > Features.

vpetersson-bot and others added 3 commits October 27, 2026 08:31
anthias_viewer doesn't re-export settings, so mypy's strict
attr-defined check rejected viewer.settings. Patch the same object
from its home module, as tests/test_viewer.py already does.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: multica-agent <github@multica.ai>
The replayed splash started its SPLASH_DELAY countdown on the portal's
active -> inactive edge, but at that point the device has only just
started joining the chosen network and has no lease yet. On hardware the
address was on screen for under 30 seconds of the 60 (GH #3411).

The viewer now puts the splash up at once, polls the same
/api/v2/network/ip-addresses endpoint the page uses until an address
someone can reach the device on appears, and only then shows it for the
full SPLASH_DELAY. The wait is capped at POST_WIFI_SETUP_ADDRESS_TIMEOUT_S
(180 s) and ends early if the portal comes back up, which is what a
failed join looks like.

- The portal's own address is excluded: once the portal ends nothing
  strips it from the address list, and the resolvers behind it lag the
  interface. /api/v2/network/wifi-setup now reports the live portal's
  gateway so the viewer can remember it. Loopback, link-local and
  unspecified addresses are excluded too.
- The splash page stops polling for addresses 120 s after it loads, so
  an address that arrives near or after that reloads the page.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: multica-agent <github@multica.ai>
@vpetersson-bot
vpetersson-bot force-pushed the agent/anthias-claude/e9c91335a467 branch from b2ee9fa to ed1c0f4 Compare October 27, 2026 08:32
@sonarqubecloud

Copy link
Copy Markdown

@vpetersson
vpetersson merged commit c8b85a7 into master Oct 27, 2026
9 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.

WiFi setup: the screen shows the standby logo instead of the IP/admin URL after joining WiFi via the portal

3 participants