# roomfit_ble

Framework-agnostic BLE communication layer for Roomfit fitness hardware.

A drop-in Flutter package that wraps `flutter_blue_plus` and `roomfit_protocol`
behind a clean, DI-free API. Import this package once and you get:

- Device discovery via `BleScanner`
- Connection management via `BleTransport` (Nordic UART Service)
- Typed command sending via `DeviceGateway`
- Live, typed `DeviceState` stream (motion, weight, battery, automation, safety)
- Pluggable logging via `BleLogger`

No Riverpod, no AppLogger, no `lib/core/` knowledge required. Bring your own DI.

## Installation

In your app's `pubspec.yaml`:

```yaml
dependencies:
  roomfit_ble:
    path: packages/roomfit_ble
```

## Usage

```dart
import 'package:roomfit_ble/roomfit_ble.dart';

Future<void> main() async {
  // 1. Create a scanner. (Optional logger — defaults to BleLogger.noop)
  final scanner = BleScanner();

  // 2. Discover devices.
  scanner.results.listen((devices) {
    for (final d in devices) {
      print('Found ${d.name} (${d.id}) ${d.rssi} dBm');
    }
  });
  await scanner.startScan();

  // 3. Once the user picks one, connect a transport.
  final transport = BleTransport();
  await transport.connect('AA:BB:CC:DD:EE:FF');

  // 4. Wire up the protocol pipeline.
  final link = DeviceLink(
    transport: transport,
    registry: ResponseRegistry.standard(),
  );
  final gateway = DeviceGatewayImpl(link);

  // 5. Subscribe to clean, typed device state.
  gateway.state.listen((state) {
    final m = state.motion;
    print('posL=${m.positionLMm}mm speedL=${m.speedL}mm/s '
          'fLoadL=${m.fLoadLKg}kg voltage=${m.voltage}V');
  });

  // 6. Send commands.
  await gateway.sendCommand(const StartReportCommand());
  await gateway.sendCommand(const SetWeightCommand(/* ... */));

  // 7. Clean up.
  await gateway.dispose();
  await link.dispose();
  await transport.disconnect();
  transport.dispose();
}
```

## Custom Logging

By default the package logs nothing. To route internal logs into your app:

```dart
// Option 1: callback factory (zero boilerplate)
final transport = BleTransport(
  logger: BleLogger.callback(
    tag: 'BleTransport',
    onLog: (level, tag, message, {error, stackTrace}) {
      myAppLogger.log(level.name, '$tag: $message');
    },
  ),
);

// Option 2: implement BleLogger directly (more control)
class MyBleLoggerAdapter implements BleLogger {
  @override
  void info(String message) => MyLog.i(message);
  // ... etc
}
```

## Testing

The package exposes the `Transport` interface from `roomfit_protocol`, so unit
tests can drive the gateway with a mock transport — no real BLE required.

```dart
import 'package:roomfit_ble/roomfit_ble.dart';

class FakeTransport implements Transport {
  final _incoming = StreamController<List<int>>.broadcast();
  // ... implement interface
}

final transport = FakeTransport();
final link = DeviceLink(
  transport: transport,
  registry: ResponseRegistry.standard(),
);
final gateway = DeviceGatewayImpl(link);

// Inject incoming bytes, then assert on gateway.currentState
```

A reference `MockTransport` is provided at
`packages/roomfit_ble/test/support/mock_transport.dart` and used by the
package's own gateway tests.

## Architecture

```
        ┌──────────────────────────────────────┐
        │           Your Application            │
        │  (Riverpod / Provider / get_it / ...) │
        └────────────────┬─────────────────────┘
                         │
        ┌────────────────▼─────────────────────┐
        │             roomfit_ble               │
        │  ┌────────────┐  ┌─────────────────┐ │
        │  │ BleScanner │  │ DeviceGateway   │ │
        │  └────────────┘  └────────┬────────┘ │
        │  ┌────────────────────────▼────────┐ │
        │  │ BleTransport (impl Transport)   │ │
        │  └─────────────────────────────────┘ │
        └────────────────┬─────────────────────┘
                         │
        ┌────────────────▼─────────────────────┐
        │           roomfit_protocol            │
        │   Transport iface · DeviceLink ·      │
        │   PacketBuilder · Command · Response  │
        └────────────────┬─────────────────────┘
                         │
        ┌────────────────▼─────────────────────┐
        │     flutter_blue_plus (BLE plugin)    │
        └──────────────────────────────────────┘
```

## Public API surface

The barrel `package:roomfit_ble/roomfit_ble.dart` re-exports the entire
`roomfit_protocol` API plus the Flutter-side classes. Importing this single
library gives you access to:

- `BleScanner`, `ScannedDevice`
- `BleTransport`
- `DeviceGateway`, `DeviceGatewayImpl`, `PacketLog`, `PacketDirection`
- `DeviceState` and all sub-states (`MotionState`, `WeightState`,
  `BatteryState`, `FirmwareState`, `AutomationState`, `SafetyState`,
  `DeviceConnectionState`)
- `BleLogger`, `BleLogLevel`
- All `roomfit_protocol` symbols (Command/Response classes, `Transport`,
  `DeviceLink`, `ResponseRegistry`, `PacketBuilder`, `CommandCodes`,
  `BleUuids`, `WeightMode`, `PhysicalUnits`, ...)
