BlueZ (org.bluez) adapter / device readouts and pairing for Phosphor-based desktop shells.
Exposes the system bus org.bluez surface (per-adapter power / discovery state, per-device pairing / connection state, and the pairing agent) as Qt + QML types. No UI. The shell decides how an adapter toggle, device list, or pairing dialog is rendered.
The library is a D-Bus client built on the generic PhosphorDBus::Client async helper and PhosphorDBus::ObjectManager (BlueZ is ObjectManager-rooted). It observes adapter + device state, offers the discovery / connect / trust / pair write surface, and acts as a D-Bus server for the org.bluez.Agent1 pairing agent.
| Type | Role |
|---|---|
BluetoothAdapter | One org.bluez.Adapter1. address, name, alias, powered, discoverable, pairable, discovering; setPowered(), setDiscoverable(), startDiscovery(), stopDiscovery(), removeDevice(). |
BluetoothDevice | One org.bluez.Device1. address, name, alias, icon, paired, trusted, blocked, connected, rssi, adapter, uuids; connectDevice(), disconnectDevice(), setTrusted(), setBlocked(), pair(), cancelPairing(). |
BluetoothHost | Owns the adapter + device sets via an ObjectManager observer and registers the pairing agent. adapterCount, deviceCount, agent; adapterAt(), deviceAt(); signals on adapter/device add/remove. |
BluetoothAdapterModel | QAbstractListModel over the host's adapters. Roles: adapter, address, name, alias, powered, discoverable, pairable, discovering. |
BluetoothDeviceModel | QAbstractListModel over the host's devices, optionally scoped to one adapter. Roles: device, address, name, alias, icon, paired, trusted, blocked, connected, rssi, adapter, uuids. |
BluetoothAgent | org.bluez.Agent1 (KeyboardDisplay). Emits pinCodeRequested / passkeyRequested / confirmationRequested / authorizationRequested / serviceAuthorizationRequested (+ display signals); answered via respondPinCode() / respondPasskey() / respondConfirmation() / rejectRequest(). |
C++ shell composition root:
#include <PhosphorServiceBluetooth/QmlRegistration.h>
int main(int argc, char** argv)
{
QGuiApplication app(argc, argv);
PhosphorServiceBluetooth::registerQmlTypes();
// ... load shell.qml
}QML consumer (adapter power toggle + device list):
import Phosphor.Service.Bluetooth 1.0
BluetoothHost { id: bt }
BluetoothDeviceModel { id: devices; host: bt }
Switch {
text: "Bluetooth"
enabled: bt.adapterCount > 0
checked: bt.adapterCount > 0 && bt.adapterAt(0).powered
onToggled: if (bt.adapterCount > 0) bt.adapterAt(0).setPowered(checked)
}
Repeater {
model: devices
delegate: Label { text: (alias || name || address) + (connected ? " — connected" : "") }
}QML consumer (pairing dialog wired to the agent):
import Phosphor.Service.Bluetooth 1.0
BluetoothHost { id: bt }
Connections {
target: bt.agent
function onConfirmationRequested(devicePath, passkey, requestId) {
// show a dialog displaying `passkey`, then:
// bt.agent.respondConfirmation(requestId, accepted)
}
}PhosphorDBus::ObjectManager pointed at the BlueZ root (GetManagedObjects + InterfacesAdded / InterfacesRemoved), not per-interface signals. Removing an adapter cascades to its child devices by path prefix.PropertiesChanged tracking, and every write (Properties.Set, StartDiscovery, Connect, Pair, RemoveDevice) go through QDBusPendingCallWatcher / PhosphorDBus::Client. The GUI thread is never blocked. Initial properties arrive in the ObjectManager enumeration, so domain objects need no construction-time round-trip.setPowered / setDiscoverable / setTrusted / setBlocked issue Properties.Set and let BlueZ echo the change back through PropertiesChanged. The cached value (and its NOTIFY) moves only once the daemon actually applied it, so the property never lies about daemon state.BluetoothAgent implements org.bluez.Agent1 directly on the QObject (ExportAllSlots + QDBusContext) rather than via a generated adaptor, so setDelayedReply applies to its calls. The interactive callbacks defer their reply, emit a request signal with a requestId, and are answered later by a consumer. Replies are sent exactly once (value, empty accept, or org.bluez.Error.Rejected), and Cancel answers every in-flight request with Canceled.AgentManager1.RegisterAgent (KeyboardDisplay) and requests default-agent status. If RequestDefaultAgent fails because another agent holds the slot, that is non-fatal, and pairing still works as a non-default agent.PhosphorDBus (private link; async method-call helper + ObjectManager observer)org.bluez on the system bus (the lib loads inert without it, with empty adapter and device lists and no crash)Shipped. The ObjectManager-driven adapter / device enumeration with live PropertiesChanged tracking and cascade removal (BluetoothHost, BluetoothAdapter, BluetoothDevice), the two QAbstractListModels (BluetoothAdapterModel, BluetoothDeviceModel with an adapter filter), the write paths (power / discoverable / discovery, connect / disconnect / trust / block / pair / cancel-pairing / remove), the full org.bluez.Agent1 pairing agent (BluetoothAgent, KeyboardDisplay, delayed-reply interactive callbacks), and the phosphorctl-style CLI demo (examples/phosphor-service-bluetooth-cli: status, list-adapters, list-devices, power, scan, pair, connect, disconnect, trust, untrust, remove) covering the core capabilities (list adapters / devices, scan, pair, connect). The ObjectManager observer added for this service lives in phosphor-dbus and is reused by other services (notifications, session). The smoke harness pins the public contract (model role names, lifecycle, the agent request/response state machine, and a delayed-reply round-trip) deterministically over a session-bus loopback without a daemon. The phosphor-dbus ObjectManager has its own loopback test.