# Bluetooth BLE / GATT

> A pilha BLE/GATT assíncrona (BluetoothManager) que substituiu o JSR-82/SPP.

Source: https://crossfly.dev/pt/docs/data-and-services/device-bluetooth-ble — CrossFly documentation. Full docs as one file: https://crossfly.dev/llms-full.txt

# Bluetooth BLE / GATT

O CrossFly usa uma pilha **BLE/GATT assíncrona** (`dev.crossfly.device.bluetooth`), que **substituiu**
o antigo JSR-82/SPP. A modelagem é baseada em `CompletableFuture`.

## ⚠️ Nota de honestidade (leia antes de codar)
No **core**, várias operações são **esqueleto sobre estado em memória** — `BluetoothConnectionBuilder.build()`
cria streams vazios, `GattCharacteristic.read()/write()` só tocam o cache local, e `discoverServices()`
retorna lista vazia. O **transporte de rádio real** vem dos launchers de plataforma (Android/iOS). A
**modelagem** (UUIDs, futures, hierarquia service→characteristic→descriptor) já é definitiva; conte com
a API, mas teste em dispositivo real por plataforma.

## Descoberta e conexão
```java
BluetoothManager bt = BluetoothManager.getInstance();   // throws IOException

// escanear (assíncrono)
bt.scanForDevices(10, TimeUnit.SECONDS, new BluetoothManager.ScanCallback() {
   public void onSuccess(List<BluetoothDevice> devices) { /* escolher um */ }
   public void onError(String error) { Debug.log(error); }
});

// conectar (assíncrono) — retorna uma BluetoothConnection
bt.connect(device, new BluetoothManager.ConnectionCallback() {
   public void onSuccess(BluetoothConnection conn) { usar(conn); }
   public void onError(String error) { Debug.log(error); }
});
```
Há variantes **`*Sync`** (`scanForDevicesSync`, `connectSync`) — use **apenas em worker thread**, nunca
na paint thread.

## GATT — ler bateria e assinar notificações
Modelo **service → characteristic → descriptor**, I/O via `CompletableFuture`:
```java
device.discoverServices(services -> {
   GattService bateria = device.getService("0000180F-0000-1000-8000-00805F9B34FB");
   GattCharacteristic nivel = bateria.getCharacteristic("00002A19-0000-1000-8000-00805F9B34FB");

   nivel.read().thenAccept(bytes -> {
      int pct = bytes[0] & 0xFF;
      Component.onUI(() -> label.setText(pct + "%"));
   });

   nivel.enableNotifications(bytes ->                     // assina mudanças
      Component.onUI(() -> label.setText((bytes[0] & 0xFF) + "%")));
});
```
`GattCharacteristic` expõe `read()` → `CompletableFuture<byte[]>`, `write(byte[])` → `CompletableFuture<Void>`,
`enableNotifications(Consumer<byte[]>)`, e os testes de capacidade `canRead()`/`canWrite()`/`canNotify()`.

## `BluetoothConnection` é `AutoCloseable`
```java
try (BluetoothConnection conn = bt.connectSync(device)) {   // só em worker thread
   conn.write("PING".getBytes());
   byte[] resp = conn.read();
}   // fecha sozinho
```

## Boas práticas
- **Assíncrono na UI** (`connect(...)`), **`*Sync` só em worker**.
- Callbacks/futures voltam à UI com `Component.onUI(...)`.
- `try-with-resources` na `BluetoothConnection`.
- **Teste em dispositivo real** por plataforma (o core é modelagem; o rádio vive no launcher).

## Erros comuns
- Chamar variantes `*Sync` na paint thread (bloqueia a UI).
- Esperar que o **core** faça I/O de rádio real (ele é esqueleto; o transporte é da plataforma).
- Tocar a UI dentro do `thenAccept`/callback sem `onUI(...)`.

## Veja também
- [Som e utilidades de dispositivo](/docs/data-and-services/device-sound-and-misc) · [Permissões por plataforma](/docs/platforms)
