Re-resolve the NanoKVM USB stick when it moves to another port - #43
Merged
Merged
Conversation
Device.videoDeviceUniqueID and serialDevicePath both persist a USB location rather than device identity: a UVC uniqueID embeds the locationID it enumerated at, and /dev/cu.usbserial-NNNN encodes the same thing. Replug the stick into a different port and both saved values dangle — AVCaptureDevice(uniqueID:) returns nil, open() returns ENOENT — so connect() fails with "may have been unplugged" while the hardware sits there working. Resolve both against what is actually attached before opening anything. The camera falls back to the single attached device sharing the saved uniqueID's VID/PID tail. The serial bridge then falls back to the port hanging off that camera's parent hub: on a NanoKVM-USB the capture chip and the CH340 are two functions behind the stick's own hub, so the pairing is exact rather than a guess. Ambiguity — two identical sticks — still fails, now saying to re-select in Edit Device. USBLocationID carries the locationID arithmetic as pure functions so it is testable without hardware; USBSerialPort now reports the locationID that the IOKit parent walk was already positioned to read. Also correct CLAUDE.md: the macOS app is not sandboxed. The App Sandbox was dropped to get raw /dev/cu.* access, and device.camera is the only entitlement. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013CARgLMkYsjjJYFysqzWih
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.
Problem
Connecting to a NanoKVM USB device failed with "Could not find USB video capture device … It may have been unplugged" while the stick sat there working fine.
Device.videoDeviceUniqueIDandserialDevicePathboth persist a USB location, not device identity:uniqueIDembeds thelocationIDit enumerated at/dev/cu.usbserial-NNNNencodes the same thingMove the stick to a different port and both saved values dangle. Confirmed against the hardware — the saved entry pointed at
0x1120000345f2131//dev/cu.usbserial-1140, the stick now enumerates as0x2120000345f2131//dev/cu.usbserial-2140:Fix
Re-resolve both identifiers against what is actually attached, before opening anything.
uniqueID's VID/PID tail (345f2131= MACROSILICON MS2131).Ambiguity (two identical sticks attached) still fails, now with an error telling you to re-select in Edit Device.
USBLocationIDcarries thelocationIDarithmetic as pure functions so it is testable without hardware.USBSerialPortnow reports thelocationIDthat the IOKit parent walk was already positioned to read.Verification
Feeding the resolvers the actual stale values from
devices.jsonrecovers the current device:Confirmed working end-to-end against the stick by @thirteen37.
7 new unit tests. KVMCore package, macOS and iPadOS suites all pass.
Also
Corrects a stale claim in CLAUDE.md: the macOS app is not sandboxed — the App Sandbox was dropped to get raw
/dev/cu.*access, anddevice.camerais the only entitlement.Not included
The session re-resolves on every connect rather than writing the resolved values back, so Edit Device still shows the old ID as "(not connected)" until you re-pick. Connecting works regardless.
🤖 Generated with Claude Code
https://claude.ai/code/session_013CARgLMkYsjjJYFysqzWih