Convert between wire BLE addresses and BlueZ device object paths.
ESPHome-style hosts identify peripherals by the 48-bit MAC packed MSB-first into a
uint64 (0xAABBCCDDEEFF); BlueZ identifies them by object path
(/org/bluez/hciX/dev_AA_BB_CC_DD_EE_FF). Host-testable; the only
process-external input is the adapter path below.
Adapter path
Which hciX the whole BlueZ subtree drives is resolved in two steps
(the kernel exposes no BT MAC in sysfs, so the MAC → adapter mapping
only exists once bluetoothd answers):
- The host publishes the user-selected radio MAC (or
nil= auto) as:persistent_term(desired_adapter_key/0) before (re)starting the subtree — directly, or via thedesired_adapter:opt onBluez.start_link/1. Bluez.Client— already waiting for the daemon in its setup phase — matches that MAC against theAdapter1objects and writes the resolved object path (adapter_path_key/0), falling back to the lowest-index adapter when the MAC is absent.
The path is then consistent for the lifetime of a subtree incarnation
(crash-restarts re-resolve against the same desired MAC). The default —
term never written, e.g. host tests or pre-setup — is /org/bluez/hci0.
Reading the term costs nanoseconds, so callers just call
adapter_path/0 per use rather than caching it.
Summary
Functions
Object path of the BlueZ adapter all device paths hang off — wherever
the host pointed the subtree, /org/bluez/hci0 by default.
The :persistent_term key the resolved adapter path is published
under. Written by Bluez.Client at setup (and tests).
The selected radio MAC (nil = auto/first).
The :persistent_term key holding the user-selected radio MAC
("AA:BB:CC:DD:EE:FF" | nil = auto). Written by the host before
each subtree start (or via the desired_adapter: opt); read via
desired_adapter/0 during Bluez.Client setup.
Build the device object path for a packed MAC address.
Parse a device object path back into a packed MAC address.
Whether address is a representable 48-bit MAC. The wire type is uint64,
so a hostile client can send values from_address/1 would refuse —
validate before converting.
Functions
@spec adapter_path() :: String.t()
Object path of the BlueZ adapter all device paths hang off — wherever
the host pointed the subtree, /org/bluez/hci0 by default.
@spec adapter_path_key() :: tuple()
The :persistent_term key the resolved adapter path is published
under. Written by Bluez.Client at setup (and tests).
@spec desired_adapter() :: String.t() | nil
The selected radio MAC (nil = auto/first).
@spec desired_adapter_key() :: tuple()
The :persistent_term key holding the user-selected radio MAC
("AA:BB:CC:DD:EE:FF" | nil = auto). Written by the host before
each subtree start (or via the desired_adapter: opt); read via
desired_adapter/0 during Bluez.Client setup.
@spec from_address(non_neg_integer()) :: String.t()
Build the device object path for a packed MAC address.
iex> Bluez.DevicePath.from_address(0xAABBCCDDEEFF)
"/org/bluez/hci0/dev_AA_BB_CC_DD_EE_FF"
@spec to_address(String.t()) :: {:ok, non_neg_integer()} | :error
Parse a device object path back into a packed MAC address.
iex> Bluez.DevicePath.to_address("/org/bluez/hci0/dev_AA_BB_CC_DD_EE_FF") == {:ok, 0xAABBCCDDEEFF}
trueReturns :error for anything that isn't a device path under the current
adapter (including child paths like .../dev_X/service000a).
Whether address is a representable 48-bit MAC. The wire type is uint64,
so a hostile client can send values from_address/1 would refuse —
validate before converting.