Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion docs/source/reference/package-apis/drivers/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ Drivers that handle media streams:

- {doc}`uStreamer <ustreamer>` (`jumpstarter-driver-ustreamer`) - Video streaming
- {doc}`Video <video>` (`jumpstarter-driver-video`) - Video interface and HTTP/MJPEG camera sources

- {doc}`NanoKVM-USB <nanokvm-usb>` (`jumpstarter-driver-nanokvm-usb`) - NanoKVM-USB KVM over local USB serial and UVC
### Automotive Diagnostics

Drivers for automotive diagnostic protocols:
Expand Down Expand Up @@ -122,6 +122,7 @@ iscsi.md
mitmproxy.md
netsim.md
network.md
nanokvm-usb.md
noyito-relay.md
obd.md
opendal.md
Expand Down
191 changes: 191 additions & 0 deletions docs/source/reference/package-apis/drivers/nanokvm-usb.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
# NanoKVM-USB Driver

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

please copy this file content to python/packages/jumpstarter-driver-nanokvm-usb/README.md and then create a symlink from docs/source/reference/package-apis/drivers/nanokvm-usb.md to python/packages/jumpstarter-driver-nanokvm-usb/README.md

That's what we do with all the drivers.


`jumpstarter-driver-nanokvm-usb` provides KVM (Keyboard, Video, Mouse) control for
[NanoKVM-USB](https://github.com/sipeed/NanoKVM-USB) devices connected directly
to the exporter host over USB.

Unlike the network-based [NanoKVM](nanokvm.md) driver, this package talks to the
hardware through:

- **USB Serial** (default 57600 baud) for keyboard and mouse HID reports
- **UVC** (USB video class) for HDMI capture as a standard camera device

## Features

- **Video capture**: Snapshots and live JPEG frame streams from the UVC device
- **Keyboard control**: Paste text and press keys via serial HID
- **Mouse control**: Absolute and relative movement, clicks, and scrolling
- **Composite driver**: Access video and HID through a unified `NanoKVMUSB` interface

## Installation

```{code-block} console
:substitutions:
$ pip3 install --extra-index-url {{index_url}} jumpstarter-driver-nanokvm-usb
```

## Configuration

### Basic configuration

```yaml
export:
nanokvm-usb:
type: jumpstarter_driver_nanokvm_usb.driver.NanoKVMUSB
config:
serial_port: "/dev/ttyACM0"
baud_rate: 57600
# Stable UVC path (survives /dev/videoN renumbering); or use an index: 0
video_device: "/dev/v4l/by-path/pci-0000:00:14.0-usbv2-0:3.4.3:1.0-video-index0"
video_width: 1920
video_height: 1080
video_fps: 30
screen_width: 1920
screen_height: 1080
```

### Config parameters

| Parameter | Description | Type | Required | Default |
|-----------|-------------|------|----------|---------|
| serial_port | Serial device path for HID | str | yes | |
| baud_rate | Serial baud rate | int | no | 57600 |
| video_device | UVC device index or path (e.g. `/dev/v4l/by-path/...`) | int/str | no | 0 |
| video_width | Requested capture width | int | no | 1920 |
| video_height | Requested capture height | int | no | 1080 |
| video_fps | Target capture/stream rate (`0` = no stream sleep throttle) | int | no | 30 |
| video_format | `mjpeg_passthrough` or `jpeg` (OpenCV re-encode) | str | no | mjpeg_passthrough |
| video_jpeg_quality | JPEG quality when `video_format=jpeg` (1–100) | int | no | 95 |
| video_discard_stale | Frames to drop before each capture (freshness) | int | no | 1 |
| video_stream_buffer_size | Max buffered frames in exported `stream()` | int | no | 32 |
| v4l2_ctl_executable | Path to `v4l2-ctl` for MJPEG passthrough; default: `PATH` lookup | str | no | (auto) |
| screen_width | Target screen width for relative mouse moves | int | no | 1920 |
| screen_height | Target screen height for relative mouse moves | int | no | 1080 |

MJPEG passthrough requires `v4l-utils` (`v4l2-ctl`) on the exporter host.

## Architecture

The driver is a composite with two child interfaces:

1. **video**: UVC snapshot capture and live frame streaming
2. **hid**: Keyboard and mouse control over USB serial

Both children share a single `NanoKVMUSBDevice` instance on the exporter so the
serial port and camera are opened once.

## Video streaming

The `stream()` driver method is exposed as a Jumpstarter **stream** (not a regular
RPC call). Video does not go to a fixed URL on the exporter; it is tunneled over
the Jumpstarter connection to whichever **client** opens the stream.

### Lifecycle

1. A client calls `video.stream("stream")` (context manager) or `open_stream()`.
2. The exporter starts an async task that captures JPEG frames from UVC and sends
them through the stream.
3. The client reads frames with `stream.receive()` — each message is one JPEG.
4. When the client closes the context (or calls `close()`), the exporter stops
capturing and releases the stream.

While the stream is active, the exporter dedicates a background task to video
capture. This does **not** block the whole exporter process (it is async), but it
does keep the UVC device busy until the client disconnects. HID commands remain
available on the `hid` child during streaming.

For recording, OCR, frame deduplication, and preprocessing without blocking
``jmp shell``, use ``edge-clearance-delivery/video-receiver/`` (``stream-bridge.py`` +
``video-receiver.py``).

### Client examples

Single frame via snapshot:

```python
image = lease.drivers["nanokvm-usb"].video.snapshot()
image.save("screen.jpg")
```

Low-level stream access (raw JPEG bytes):

```python
video = lease.drivers["nanokvm-usb"].video

with video.stream("stream") as stream:
while True:
frame_jpeg = stream.receive()
```

## API reference

### NanoKVMUSBClient

Composite client with `video` and `hid` children.

### NanoKVMUSBVideoClient

```{eval-rst}
.. autoclass:: jumpstarter_driver_nanokvm_usb.client.NanoKVMUSBVideoClient()
:members: snapshot
```

### NanoKVMUSBHIDClient

```{eval-rst}
.. autoclass:: jumpstarter_driver_nanokvm_usb.client.NanoKVMUSBHIDClient()
:members: paste_text, press_key, reset_hid, mouse_move_abs, mouse_move_rel, mouse_click, mouse_scroll
```

## CLI usage

```bash
# Snapshot
j nanokvm-usb video snapshot

# Keyboard
j nanokvm-usb hid paste "Hello, World!"
j nanokvm-usb hid press enter

# Mouse
j nanokvm-usb hid mouse move 0.5 0.5
j nanokvm-usb hid mouse click --button left --x 0.5 --y 0.5
```

## Host requirements

The exporter must run on the machine where the NanoKVM-USB is plugged in.

### Linux permissions

Add your user to the `dialout` group for serial port access:

```bash
sudo usermod -a -G dialout $USER
```

On Arch Linux, use the `uucp` group instead. Log out and back in after changing
group membership.

### Finding devices

**Serial port** (Linux): typically `/dev/ttyACM0` or `/dev/ttyUSB0`

**Video device**: OpenCV camera index or `/dev/video*`

```python
from jumpstarter_driver_nanokvm_usb.video import VideoCapture

for device in VideoCapture.list_devices():
print(device)
```

## Differences from the network NanoKVM driver

| Feature | NanoKVM (network) | NanoKVM-USB |
|---------|-------------------|-------------|
| Connection | HTTP/WebSocket | USB serial + UVC |
| Video stream | MJPEG from device API | UVC capture on exporter host |
| Virtual disk/CD-ROM | Yes | No |
| Device reboot | Yes | No |
| Auth | Username/password | None (local USB) |
3 changes: 3 additions & 0 deletions python/packages/jumpstarter-driver-nanokvm-usb/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
__pycache__/
.coverage
coverage.xml
15 changes: 15 additions & 0 deletions python/packages/jumpstarter-driver-nanokvm-usb/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# NanoKVM-USB Driver

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Please put the contents from the .md you created in the docs file here in this file.

Then remove the .md on the docs directory, and link it to this readme, this is how the other drivers do it.

`jumpstarter-driver-nanokvm-usb` provides KVM (keyboard, video, mouse) for
[NanoKVM-USB](https://github.com/sipeed/NanoKVM-USB) devices on the exporter host
(USB serial HID + UVC).

Full documentation lives in the Jumpstarter docs:
[NanoKVM-USB driver](https://github.com/jumpstarter-dev/jumpstarter/blob/main/docs/source/reference/package-apis/drivers/nanokvm-usb.md)
(`docs/source/reference/package-apis/drivers/nanokvm-usb.md`).

## Installation

```shell
pip3 install --extra-index-url https://pkg.jumpstarter.dev/simple/ jumpstarter-driver-nanokvm-usb
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
apiVersion: jumpstarter.dev/v1alpha1
kind: ExporterConfig
metadata:
namespace: default
name: demo
endpoint: grpc.jumpstarter.example.com:8082
token: "<token>"
export:
nanokvm-usb:
type: jumpstarter_driver_nanokvm_usb.driver.NanoKVMUSB
config:
serial_port: "/dev/ttyACM0"
baud_rate: 57600
video_device: 0
video_width: 1920
video_height: 1080
video_fps: 30
screen_width: 1920
screen_height: 1080
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
from .client import NanoKVMUSBClient, NanoKVMUSBHIDClient, NanoKVMUSBVideoClient
from .driver import NanoKVMUSB, NanoKVMUSBHID, NanoKVMUSBVideo
from .mouse import MouseButton

__all__ = [
"NanoKVMUSB",
"NanoKVMUSBVideo",
"NanoKVMUSBHID",
"NanoKVMUSBClient",
"NanoKVMUSBVideoClient",
"NanoKVMUSBHIDClient",
"MouseButton",
]
Loading
Loading