Skip to content

Add board discovery example; make discovery fast and reliable - #15

Merged
mullinmax merged 5 commits into
mainfrom
claude/network-board-discovery-cxftkq
Jul 18, 2026
Merged

Add board discovery example; make discovery fast and reliable#15
mullinmax merged 5 commits into
mainfrom
claude/network-board-discovery-cxftkq

Conversation

@mullinmax

@mullinmax mullinmax commented Jul 18, 2026

Copy link
Copy Markdown
Contributor

What

  1. Adds examples/discover_boards.py, a runnable script that finds every Vector board on the network and prints information about each one (IP, name, firmware version, configured game, whether a game is live). Boards are queried concurrently; unreachable boards are reported inline. If nothing is found automatically, the script prompts for one board's IP and enumerates the rest from that board's /api/network/peers table.
  2. Overhauls the library's discover() to fix unreliable single-board discovery and to return results within ~2 s instead of depending on a long listen window.

Root cause of the discovery failures

Cross-checked against the firmware's discovery service (src/common/discovery.py + the phew scheduler on warped-pinball/vector, branch claude/unicast-memory-stream):

  • Boards service their discovery socket every 1.5 s, but a lone board never broadcasts FULL unprovoked — on its own it only says HELLO every 15 s. So finding a single board depended entirely on the client's one OFFLINE probe actually reaching it.
  • 255.255.255.255 leaves only the default-route interface; on a multi-homed Windows machine that is often not the network the boards are on, so the probe never arrived.
  • Two boards "worked" only because their mutual chatter produces FULL broadcasts the client can passively overhear.
  • FULL replies are broadcast, never unicast — but a board answering a PING sends the PONG unicast to the sender's IP on port 37020, which survives networks that filter broadcasts toward the client.

Discovery changes

  • Probe with PING + OFFLINE(self) every 1.5 s (matching the boards' service cadence). Either frame provokes the registry into broadcasting FULL without registering the client as a peer; PING additionally earns a unicast PONG.
  • Probe from every local interface, to the limited broadcast and each subnet's /24 directed broadcast, so the right network is always reached on multi-homed machines.
  • Parse HELLO and PONG replies, not just FULL. If boards are heard directly but no FULL arrives within a short settle window (0.5 s), the complete list is fetched from a heard board over unicast HTTP (/api/network/peers).
  • First FULL frame still ends discovery immediately; the 20 s default timeout is now only a worst-case cap. Typical result: all boards within ~2 s.

Testing

  • ruff check passes; full test suite passes (244 tests), including new tests for the PING/OFFLINE probes, directed-broadcast targets, HELLO/PONG sightings, own-probe loopback filtering, and the HTTP peer-table fallback.
  • Example exercised end-to-end against faked sockets/transports for the listing, enrichment, prompt-fallback, and error paths.

🤖 Generated with Claude Code

https://claude.ai/code/session_01SFN2z3hJYtQbnRVmsJuRkd

Add examples/discover_boards.py, which finds every Vector board on the
LAN via UDP discovery and then connects to each over HTTP to print its
IP, name, firmware version, and game state. Boards are queried
concurrently and unreachable boards are reported inline. Document it in
docs/examples.md.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SFN2z3hJYtQbnRVmsJuRkd
@github-actions

github-actions Bot commented Jul 18, 2026

Copy link
Copy Markdown

Coverage report

Total coverage: 100.00% (0.00% vs base 100.00%)

claude added 2 commits July 18, 2026 14:29
When the UDP broadcast finds no boards -- common on phone hotspots, guest
Wi-Fi, and some travel routers that block broadcast between clients -- the
example now prompts for one board's IP and enumerates the rest from that
board's /api/network/peers table over unicast HTTP. The peer payload is
parsed defensively (its exact JSON shape can vary), and the supplied IP is
always included so it works even if the peer table is empty. Document the
fallback in docs/examples.md.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SFN2z3hJYtQbnRVmsJuRkd
Root cause of unreliable single-board discovery, from the firmware's
discovery service: a lone board never broadcasts FULL unprovoked (it only
says HELLO every 15 s), so discovery depended entirely on our one OFFLINE
probe reaching the board -- and 255.255.255.255 leaves only the
default-route interface, which on a multi-homed Windows machine is often
not the network the board is on. Two boards worked because their mutual
chatter produces FULL broadcasts to overhear.

Discovery now:
- probes with PING as well as OFFLINE; any board hearing a PING replies
  with a unicast PONG to our port, which survives broadcast filtering
  toward us, and the registry still broadcasts FULL
- sends probes from every local interface, to the limited broadcast and
  each subnet's /24 directed broadcast, so the right network is always
  reached
- parses HELLO and PONG replies, not just FULL; if boards are heard but
  no FULL arrives within a short settle window, the complete list is
  fetched from a heard board over unicast HTTP (/api/network/peers)
- re-probes every 1.5 s to match the boards' discovery service cadence,
  so results typically arrive within about two seconds

Update tests for the new probe/reply/fallback logic and refresh the
discovery notes in docs/machine.md.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SFN2z3hJYtQbnRVmsJuRkd
@mullinmax mullinmax changed the title Add network board discovery example Add board discovery example; make discovery fast and reliable Jul 18, 2026
Add tests/test_discovery_e2e.py: a fake board thread that speaks the
firmware's discovery protocol over real UDP loopback sockets, so
discover() is exercised with genuine network I/O on whatever OS runs the
tests. Covers the fast FULL-reply path, named lookup, the PONG-only ->
HTTP peers fallback, and the silent-network timeout. Running this file
on a machine separates "library broken on this OS" from "network is
blocking discovery".

Two Windows fixes found while auditing for platform differences:

- On Windows, recvfrom raises ConnectionResetError (WSAECONNRESET) when
  an earlier *sent* datagram bounced with ICMP port-unreachable; the
  receive loop treated any OSError as fatal and silently stopped
  listening. Now tolerated and listening continues.

- requests honors system/environment proxy settings by default, so on
  machines with a proxy configured (corporate, VPN, WPAD auto-detect)
  HTTP to a board's private IP was routed to a proxy that cannot reach
  it. HttpTransport now disables proxy lookup for private, loopback, and
  link-local addresses; hostnames, public IPs, and caller-supplied
  sessions keep their behavior.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SFN2z3hJYtQbnRVmsJuRkd
@mullinmax
mullinmax marked this pull request as ready for review July 18, 2026 19:24
Cover the paths the discovery overhaul left untested: the real
_local_ips interface merge and its getaddrinfo failure fallback, the
_probe_sockets bind success path, probe sockets carrying probes and
being closed by discover(), a second direct sighting reusing the first
settle clock, and a non-dict /api/network/peers payload.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SFN2z3hJYtQbnRVmsJuRkd
@mullinmax
mullinmax merged commit 4073760 into main Jul 18, 2026
11 checks passed
@mullinmax
mullinmax deleted the claude/network-board-discovery-cxftkq branch July 18, 2026 19:28
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