feat(social): USB camera hot-plug monitor (Issue #320) #328

Merged
sl-jetson merged 1 commits from sl-jetson/issue-320-camera-hotplug into main 2026-03-03 06:45:24 -05:00
Collaborator

Summary

Closes #320

New ROS2 node camera_hotplug_node that monitors /dev/video* for USB camera connect/disconnect events and publishes status to /saltybot/camera_status.

  • Polls /dev/video* at 2 Hz (configurable via poll_rate)
  • Three status values published as std_msgs/String:
    • "connected" — one or more devices present, stable
    • "disconnected" — no devices found
    • "restarting" — device set changed (hot-swap/reconnect); downstream capture pipelines should restart
  • Reconnect detection: if a camera reappears within restart_grace_s (5 s) of being lost → "restarting" held for restart_hold_s (2 s), then resolves to "connected" or "disconnected"
  • Publishes initial state on boot (no spurious "restarting" at startup)
  • publish_always param to emit on every tick rather than only on change
  • Scan function injected via _scan_fn attribute — allows full offline unit testing without touching filesystem
  • Pure compute_transition() function contains all state-machine logic for isolated testing

Test plan

  • 82/82 offline unit tests pass (test_camera_hotplug.py)
  • scan_video_devices: returns frozenset, handles nonexistent glob
  • Status constants correct strings
  • compute_transition — all DISCONNECTED/CONNECTED/RESTARTING transitions, grace window (inclusive), hold expiry, partial device loss, disconnect_time recording
  • Node init: initial state from scan, publishes immediately, timer registered
  • _scan_cb: connected/disconnected/restarting transitions, publish_always, logging
  • Full lifecycle: boot → connect → disconnect → reconnect → restarting → resolved
  • _publish: all three statuses, message count
  • Source tags, config YAML, launch file, setup.py entry point

🤖 Generated with Claude Code

## Summary Closes #320 New ROS2 node `camera_hotplug_node` that monitors `/dev/video*` for USB camera connect/disconnect events and publishes status to `/saltybot/camera_status`. - Polls `/dev/video*` at 2 Hz (configurable via `poll_rate`) - Three status values published as `std_msgs/String`: - `"connected"` — one or more devices present, stable - `"disconnected"` — no devices found - `"restarting"` — device set changed (hot-swap/reconnect); downstream capture pipelines should restart - Reconnect detection: if a camera reappears within `restart_grace_s` (5 s) of being lost → `"restarting"` held for `restart_hold_s` (2 s), then resolves to `"connected"` or `"disconnected"` - Publishes initial state on boot (no spurious "restarting" at startup) - `publish_always` param to emit on every tick rather than only on change - Scan function injected via `_scan_fn` attribute — allows full offline unit testing without touching filesystem - Pure `compute_transition()` function contains all state-machine logic for isolated testing ## Test plan - [x] 82/82 offline unit tests pass (`test_camera_hotplug.py`) - [x] `scan_video_devices`: returns frozenset, handles nonexistent glob - [x] Status constants correct strings - [x] `compute_transition` — all DISCONNECTED/CONNECTED/RESTARTING transitions, grace window (inclusive), hold expiry, partial device loss, disconnect_time recording - [x] Node init: initial state from scan, publishes immediately, timer registered - [x] `_scan_cb`: connected/disconnected/restarting transitions, `publish_always`, logging - [x] Full lifecycle: boot → connect → disconnect → reconnect → restarting → resolved - [x] `_publish`: all three statuses, message count - [x] Source tags, config YAML, launch file, setup.py entry point 🤖 Generated with [Claude Code](https://claude.com/claude-code)
sl-webui added 1 commit 2026-03-03 00:47:51 -05:00
feat(social): USB camera hot-plug monitor (Issue #320)
Some checks failed
social-bot integration tests / Lint (flake8 + pep257) (push) Failing after 2s
social-bot integration tests / Core integration tests (mock sensors, no GPU) (push) Has been skipped
social-bot integration tests / Lint (flake8 + pep257) (pull_request) Failing after 5s
social-bot integration tests / Core integration tests (mock sensors, no GPU) (pull_request) Has been skipped
social-bot integration tests / Latency profiling (GPU, Orin) (push) Has been cancelled
social-bot integration tests / Latency profiling (GPU, Orin) (pull_request) Has been cancelled
a3386e1694
Polls /dev/video* at 2 Hz, drives a three-state machine
(connected/disconnected/restarting) and publishes to
/saltybot/camera_status (std_msgs/String).  Reconnects within
restart_grace_s (5 s) → 'restarting' held for restart_hold_s (2 s)
to signal downstream capture pipelines to restart.  Scan function
is injected for offline testing. 82/82 tests pass.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
sl-jetson merged commit f506c89960 into main 2026-03-03 06:45:24 -05:00
Sign in to join this conversation.
No Reviewers
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: seb/saltylab-firmware#328
No description provided.