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:
dbus-daemon --system(MuonTrap.Daemon) — owns the system bus at/run/dbus/system_bus_socket.Bluez.BusReady— a one-line gate that blocks until the bus socket exists, sobluetoothdnever races the bus.:rest_for_onere-runs it (andbluetoothd) ifdbus-daemonrestarts.bluetoothd(MuonTrap.Daemon) — claimsorg.bluez, drives the adapter via the kernel mgmt socket. Run with-E(experimental) — required forAdvertisementMonitorManager1passive scanning and the GATTMTUproperty.Bluez.Client— persistentrebusclient owning the discovery session; adverts fan out through itson_advertisement:fun.Bluez.Agent— default NoInputNoOutput pairing agent.Bluez.Gatt(+ itsTask.Supervisor) — active connections + GATT client; results flow through itson_gatt_event:fun.- With
audio: true(the default):bluealsad(MuonTrap.Daemon, A2DP source) andBluez.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). 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 forBluez.Client(on_advertisement:,pubsub:).gatt:— keyword opts forBluez.Gatt(on_gatt_event:,on_connections_changed:).audio:— boolean (defaulttrue): start thebluealsaddaemon +Bluez.BlueAlsaclient. Requires bluez-alsa (v4 recommended) on the device.blue_alsa:— keyword opts forBluez.BlueAlsa(pubsub:).extra_children:— host child specs appended at the end of the tree. Under:rest_for_onethey 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, ornil(auto: lowest-index adapter). Written to:persistent_termbefore the children start. Hosts that switch radios at runtime may instead publish the term themselves underBluez.DevicePath.desired_adapter_key/0BEFORE (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 — foraudio: true— bluez-alsa v4. - Writable
/run/dbusand/data/bluetooth(prepare_runtime/0creates both and a machine-id before the daemons launch; on a read-only rootfs, point/var/lib/bluetoothat/data/bluetoothvia an overlay symlink sobluetoothdcan 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.
Summary
Functions
Returns a specification to start this module under a supervisor.
The child list init/1 supervises, exposed for child-order tests. See
the moduledoc for the supported opts.
Create the writable dirs the daemons need before they launch. Idempotent;
safe to call on every (re)start of this supervisor (init/1 does).
System-bus socket path the rebus clients connect to.
Functions
Returns a specification to start this module under a supervisor.
See Supervisor.
@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.
@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).
@spec socket_path() :: String.t()
System-bus socket path the rebus clients connect to.