# `Bluez`
[🔗](https://github.com/bbangert/bluez/blob/v0.1.0/lib/bluez.ex#L1)

Brings up the Linux BlueZ stack — `dbus-daemon`, `bluetoothd`, and
(optionally) bluez-alsa — under one supervisor, with Elixir clients for
BLE scanning (`Bluez.Client`), active GATT connections (`Bluez.Gatt`),
pairing (`Bluez.Agent`), and A2DP audio PCM discovery (`Bluez.BlueAlsa`).

Built for Nerves devices (read-only rootfs, `MuonTrap`-supervised
daemons) but has no Nerves dependency; any Linux host where the calling
application may own the system D-Bus instance works.

Children, started `:rest_for_one`:

  1. `dbus-daemon --system` (`MuonTrap.Daemon`) — owns the system bus at
     `/run/dbus/system_bus_socket`.
  2. `Bluez.BusReady` — a one-line gate that blocks until the bus socket
     exists, so `bluetoothd` never races the bus. `:rest_for_one` re-runs
     it (and `bluetoothd`) if `dbus-daemon` restarts.
  3. `bluetoothd` (`MuonTrap.Daemon`) — claims `org.bluez`, drives the
     adapter via the kernel mgmt socket. Run with `-E` (experimental) —
     required for `AdvertisementMonitorManager1` passive scanning and the
     GATT `MTU` property.
  4. `Bluez.Client` — persistent `rebus` client owning the discovery
     session; adverts fan out through its `on_advertisement:` fun.
  5. `Bluez.Agent` — default NoInputNoOutput pairing agent.
  6. `Bluez.Gatt` (+ its `Task.Supervisor`) — active connections + GATT
     client; results flow through its `on_gatt_event:` fun.
  7. With `audio: true` (the default): `bluealsad` (`MuonTrap.Daemon`,
     A2DP source) and `Bluez.BlueAlsa` (org.bluealsa client). Placed
     after the scanning/GATT clients so an audio-daemon fault never
     restarts the scanning stack — the children that follow *do* restart
     with it under `:rest_for_one`, which is intended (same audio path).
  8. `extra_children:` — host-supplied child specs, appended last.

## Options

All are optional; pass them to `start_link/1` (usually via a child spec
`{Bluez, opts}`):

  * `client:` — keyword opts for `Bluez.Client` (`on_advertisement:`,
    `pubsub:`).
  * `gatt:` — keyword opts for `Bluez.Gatt` (`on_gatt_event:`,
    `on_connections_changed:`).
  * `audio:` — boolean (default `true`): start the `bluealsad` daemon +
    `Bluez.BlueAlsa` client. Requires bluez-alsa (v4 recommended) on the
    device.
  * `blue_alsa:` — keyword opts for `Bluez.BlueAlsa` (`pubsub:`).
  * `extra_children:` — host child specs appended at the end of the tree.
    Under `:rest_for_one` they restart with the audio path, and a fault
    there never disturbs the scanning/GATT stack above them. Ordering
    within the slot is the caller's contract.
  * `desired_adapter:` — MAC string (`"AA:BB:CC:DD:EE:FF"`) of the radio
    to drive, or `nil` (auto: lowest-index adapter). Written to
    `:persistent_term` before the children start. Hosts that switch
    radios at runtime may instead publish the term themselves under
    `Bluez.DevicePath.desired_adapter_key/0` BEFORE (re)starting this
    supervisor — the opt and the pre-published term coexist (the opt,
    when present, wins by writing last).
  * `dbus_daemon_path:` — dbus-daemon binary (default
    `/usr/bin/dbus-daemon`).
  * `bluetoothd_path:` — bluetoothd binary (default
    `/usr/libexec/bluetooth/bluetoothd`).
  * `bluealsad_paths:` — candidate bluez-alsa daemon binaries, first
    existing wins (default `["/usr/bin/bluealsad", "/usr/bin/bluealsa"]`
    — the daemon was renamed in bluez-alsa v4).

## Runtime requirements

  * BlueZ ≥ 5.66 (`bluetoothd -E`), dbus, and — for `audio: true` —
    bluez-alsa v4.
  * Writable `/run/dbus` and `/data/bluetooth` (`prepare_runtime/0`
    creates both and a machine-id before the daemons launch; on a
    read-only rootfs, point `/var/lib/bluetooth` at `/data/bluetooth`
    via an overlay symlink so `bluetoothd` can persist adapter state).
  * This supervisor OWNS the system bus: don't run it next to a distro
    dbus/bluetoothd.

## Public-API `catch :exit` idiom

The synchronous read APIs (`Bluez.Client.adapters_info/0`,
`Bluez.BlueAlsa.pcms/0`, `Bluez.Gatt.connections_free/0`, …) are meant
to be wrapped by hosts in `catch :exit` so callers work while the stack
is down. Be aware what that swallows: it converts BOTH the
process-not-running exit AND a call timeout into the same "subsystem
off" default — a wedged server renders as a disabled subsystem rather
than raising. Accept that tradeoff knowingly, or catch only
`:exit, {:timeout, _}` separately where the distinction matters.

# `child_spec`

Returns a specification to start this module under a supervisor.

See `Supervisor`.

# `children`

```elixir
@spec children(keyword()) :: [Supervisor.child_spec() | {module(), term()} | module()]
```

The child list `init/1` supervises, exposed for child-order tests. See
the moduledoc for the supported opts.

# `prepare_runtime`

```elixir
@spec prepare_runtime() :: :ok
```

Create the writable dirs the daemons need before they launch. Idempotent;
safe to call on every (re)start of this supervisor (`init/1` does).

# `socket_path`

```elixir
@spec socket_path() :: String.t()
```

System-bus socket path the rebus clients connect to.

# `start_link`

---

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