Add board discovery example; make discovery fast and reliable - #15
Merged
Conversation
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
Coverage report✅ Total coverage: 100.00% (0.00% vs base |
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
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
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
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/peerstable.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 onwarped-pinball/vector, branchclaude/unicast-memory-stream):255.255.255.255leaves 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.Discovery changes
/api/network/peers).Testing
ruff checkpasses; 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.🤖 Generated with Claude Code
https://claude.ai/code/session_01SFN2z3hJYtQbnRVmsJuRkd