# `Meshcore.Client`
[🔗](https://github.com/nyannus/meshcore_ex/blob/v0.1.0/lib/meshcore/client.ex#L1)

The session process behind a MeshCore connection.

One `Meshcore.Client` owns

  * the transport process (and reconnects it when `auto_reconnect` is on),
  * the `Meshcore.Reader` that turns frames into events,
  * the list of event subscribers and of commands waiting for a response,
  * the contact list / self info / device time mirrored from the device,
  * a FIFO lock serialising "mesh requests" (login, status, telemetry…).

You normally use it through `Meshcore` and `Meshcore.Commands`; the functions
here are the lower-level building blocks.

## Fault tolerance

  * Commands are never answered by blocking the process: each one registers
    a *waiter* with a timer, so a silent device produces
    `{:error, %Meshcore.Event{type: :error, payload: %{reason: :timeout}}}`
    rather than a hang.
  * Waiters are failed immediately with `reason: :disconnected` when the link
    drops.
  * Subscribers are monitored; dead ones are dropped automatically.
  * The lock holder is monitored; a crashed holder releases the lock.
  * Decoding errors are contained in `Meshcore.Reader`.
  * A crashed transport is just another lost connection.

# `result`

```elixir
@type result() :: {:ok, Meshcore.Event.t()} | {:error, Meshcore.Event.t()}
```

# `t`

```elixir
@type t() :: GenServer.server()
```

# `child_spec`

Returns a specification to start this module under a supervisor.

See `Supervisor`.

# `command`

```elixir
@spec command(t(), binary(), [Meshcore.Event.type()], keyword()) :: result()
```

Sends `data` and waits for one of the `expected` event types.

With an empty `expected` list the command is fire-and-forget and returns
`{:ok, %Event{type: :ok, payload: %{}}}` immediately.

Options:

  * `:timeout` – milliseconds (default: the client's default timeout)
  * `:touch_on` – event types that restart the timeout (used for contact lists)
  * `:register` – registers a binary request as soon as `:msg_sent` arrives
    (race-free); a map with `:prefix`, `:request_type`, `:timeout`,
    `:min_timeout`, `:context`, `:is_anon`, `:hops`
  * `:then` – after `:msg_sent`, keep waiting for a follow-up event; a map
    with `:types`, `:filters`, `:tag_filter` (boolean), `:timeout`,
    `:min_timeout`

# `connect`

```elixir
@spec connect(t()) :: result() | {:error, term()}
```

Connects the transport and runs the app-start handshake.

# `disconnect`

```elixir
@spec disconnect(t()) :: :ok
```

Disconnects and stops the client process.

# `get`

```elixir
@spec get(t(), atom()) :: term()
```

Reads a value of the mirrored session state. See `Meshcore` getters.

# `put`

```elixir
@spec put(t(), atom(), term()) :: :ok
```

Sets a session setting (`:auto_update_contacts`, `:default_timeout`, `:decrypt_channels`).

# `start_link`

```elixir
@spec start_link(keyword()) :: GenServer.on_start()
```

Starts a client. See `Meshcore.start_link/1` for the options.

# `subscribe`

```elixir
@spec subscribe(t(), atom() | [atom()] | nil, map(), pid()) ::
  {:ok, Meshcore.Subscription.t()}
```

Subscribes `pid` to events. See `Meshcore.subscribe/3`.

# `unsubscribe`

```elixir
@spec unsubscribe(t(), Meshcore.Subscription.t() | reference()) :: :ok
```

Removes a subscription (idempotent).

# `wait_for_event`

```elixir
@spec wait_for_event(
  t(),
  Meshcore.Event.type() | [Meshcore.Event.type()],
  map(),
  timeout_ms :: non_neg_integer() | nil
) :: {:ok, Meshcore.Event.t()} | {:error, :timeout}
```

Waits for an event of `type` (or any of a list of types) whose attributes
match `filters`. Returns `{:ok, event}` or `{:error, :timeout}`.

# `with_lock`

```elixir
@spec with_lock(t(), (-&gt; result)) :: result when result: term()
```

Runs `fun` while holding the per-connection mesh request lock.

The lock is FIFO, released automatically if the holder crashes and
re-entrant for the process that holds it.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
