<!-- Source: https://medius.k4tech.net/library/discovery -->
# Discovery

_Find and open one box out of several_

With more than one box plugged in, [`find`](/library/connection.md#open) just opens the first match. These calls enumerate every box and open a specific one by a stable [identity](/library/discovery.md#identity), or by the kind of device it clones.

See also: [connecting](/library/connection.md), [choosing a port](/library/guides/connection.md#choosing-a-port), and the box [handshake](/native/connection.md#handshake).

## list

_Enumerate every connected box_

```text
fn list() -> Vec<BoxInfo>
```

_Blocks_

Opens each connected box in turn, handshakes, reads its [`Version`](/library/types/structs.md#version) (with the box MAC and name) and cloned [`DeviceInfo`](/library/types/structs.md#device-info), then closes it, returning one [`BoxInfo`](/library/discovery.md#box-info) per box. Use it to show a picker, or to choose a box yourself.

#### EXAMPLE

```rust
use medius::Device;

for b in Device::list() {
    // id() is the box MAC hex; name() is its readable label; b.device displays as "VVVV:PPPP product".
    println!("{}  {}  {}  {}", b.id(), b.name(), b.device, b.port.path);
}
```

## open_by_id

_Open the box with a given identity_

```text
fn open_by_id(id: &str) -> Result<Device>
```

_Blocks_

Opens the box whose identity matches `id`: either the device MAC hex (from [`Version::mac_hex`](/library/types/structs.md#version)) or the CH343 [serial](/library/types/structs.md#port-info). Returns [`Error::NotFound`](/library/types/errors.md) when no connected box matches.

#### EXAMPLE

```rust
use medius::Device;

// the MAC hex printed by Device::list(), stable across replugs:
let device = Device::open_by_id("123456789abc")?;
```

## find_mouse_box

_Open the first box cloning a mouse_

```text
fn find_mouse_box() -> Result<Device>
```

_Blocks_

Opens the first box whose clone's [`DeviceKind`](/library/types/enums.md#device-kind) is a mouse. Handy when one box clones a mouse and another a keyboard: it grabs the right one without naming an id. Returns [`Error::NotFound`](/library/types/errors.md) if no connected box matches.

#### EXAMPLE

```rust
use medius::Device;

let mouse_box = Device::find_mouse_box()?;
mouse_box.move_rel(10, 0)?;
```

## find_keyboard_box

_Open the first box cloning a keyboard_

```text
fn find_keyboard_box() -> Result<Device>
```

_Blocks_

The keyboard counterpart of [`find_mouse_box`](/library/discovery.md#find-mouse-box): opens the first box whose clone is a keyboard.

#### EXAMPLE

```rust
use medius::{Device, Key};

let kbd_box = Device::find_keyboard_box()?;
kbd_box.press(Key::A)?;
```

## find_where

_Open the first box matching a predicate_

```text
fn find_where(pred: impl Fn(&BoxInfo) -> bool) -> Result<Device>
```

_Blocks_

The general form the `find_*_box` helpers build on: opens the first box whose [`BoxInfo`](/library/discovery.md#box-info) satisfies `pred`. Match on any field, e.g. a specific vendor id or product string. Returns [`Error::NotFound`](/library/types/errors.md) when none match.

#### EXAMPLE

```rust
use medius::Device;

// the box cloning a Logitech device:
let device = Device::find_where(|b| b.device.vid == 0x046D)?;
```

## BoxInfo

_One discovered box_

One entry from [`Device::list`](/library/discovery.md#list) (and the value[`find_where`](/library/discovery.md#find-where)'s predicate sees): the box's control port, firmware version, and the device it clones.

| Field | Type | Meaning |
| --- | --- | --- |
| `port` | [`PortInfo`](/library/types/structs.md#port-info) | The control port (path + CH343 serial). |
| `version` | [`Version`](/library/types/structs.md#version) | The firmware version, with the box MAC and [name](/library/options.md#set-name). |
| `device` | [`DeviceInfo`](/library/types/structs.md#device-info) | The device it clones. |

| Method | Returns | Meaning |
| --- | --- | --- |
| `id()` | `String` | The box identity: the MAC hex, as passed to [`open_by_id`](/library/discovery.md#open-by-id). |
| `name()` | `&str` | The box's human-readable [name](/library/options.md#set-name) (from `version.name`), a display label rather than an opener key. |
| `serial()` | `Option<&str>` | The CH343 adapter's serial, when it has one. |

## Identity & reconnect

_The same physical box, across replugs_

A box has a stable identity: the device chip's base MAC (from [`Version::mac_hex`](/library/types/structs.md#version)) and the CH343 adapter's serial. Serial-port paths renumber when you replug, but the identity does not, so [`open_by_id`](/library/discovery.md#open-by-id) re-finds the same box every time.

Opening a box anchors [`reconnect`](/library/lifecycle.md#reconnect) to that identity. An automatic reconnect re-finds the _same_ physical box even if the ports renumbered, and never adopts a different box that happens to be plugged in.

The readable partner to that MAC is the box's [name](/library/options.md#set-name) (from [`Version::name`](/library/types/structs.md#version)), a display label for telling boxes apart, not an opener key.

## On AsyncDevice

_The same discovery, awaitable device_info_

```text
fn AsyncDevice::list() -> Vec<BoxInfo>
```

```text
fn AsyncDevice::open_by_id(id: &str) -> Result<AsyncDevice>
```

```text
fn AsyncDevice::find_mouse_box() -> Result<AsyncDevice>
```

```text
fn AsyncDevice::find_keyboard_box() -> Result<AsyncDevice>
```

_Blocks_

[`AsyncDevice`](/library/features/async.md) mirrors the discovery constructors; they block on the per-box handshake, like their [`Device`](/library/connection.md#async) counterparts. The reply-reading [`device_info`](/library/requests.md#device-info) query is the awaitable part.

```rust
use medius::AsyncDevice;

let device = AsyncDevice::find_mouse_box()?; // blocks on the handshake
let info = futures::executor::block_on(device.device_info())?; // awaits the reply
```
