ble_link_kit 0.4.0 copy "ble_link_kit: ^0.4.0" to clipboard
ble_link_kit: ^0.4.0 copied to clipboard

Flutter BLE connection framework for multi-protocol scanning, generic GATT transfer, framed channels, provisioning, and resumable OTA.

example/lib/main.dart

import 'dart:async';

import 'package:ble_link_kit/ble_link_kit.dart';
import 'package:flutter/material.dart';

void main() => runApp(const MaterialApp(home: ProvisionDemoPage()));

class ProvisionDemoPage extends StatefulWidget {
  const ProvisionDemoPage({super.key});
  @override
  State<ProvisionDemoPage> createState() => _ProvisionDemoPageState();
}

class _ProvisionDemoPageState extends State<ProvisionDemoPage> {
  final api = BleLinkKit();
  final ssid = TextEditingController(text: 'Demo Wi-Fi');
  final password = TextEditingController();
  final devices = <String, BleDevice>{};
  final subscriptions = <StreamSubscription<Object?>>[];
  BleCapabilities? capabilities;
  String status = 'Configuring demo profile…';
  String? connectedId;
  bool useSpp = false;
  double otaProgress = 0;

  static const operations = ProtocolOperations(
    wifiConfig: 'demo_wifi_config',
    wifiStatus: 'demo_wifi_status',
    firmwareVersion: 'demo_firmware_version',
    otaStart: 'demo_ota_start',
    otaChunkAck: 'demo_ota_chunk_ack',
    otaEnd: 'demo_ota_end',
    otaStatus: 'demo_ota_status',
    otaComplete: 'demo_ota_complete',
    otaFailed: 'demo_ota_failed',
    wifiConnect: 'demo_wifi_connect',
    configReceived: 'demo_config_received',
    wifiConnected: 'demo_wifi_connected',
    wifiFailed: 'demo_wifi_failed',
    otaStartAck: 'demo_ota_start_ack',
  );

  @override
  void initState() {
    super.initState();
    subscriptions.add(
      api.scanEvents.listen((event) {
        if (event.device != null) devices[event.device!.id] = event.device!;
        update('Scan: ${event.state.name}');
      }),
    );
    subscriptions.add(
      api.connectionEvents.listen(
        (event) => update('Connection: ${event.state.name}'),
      ),
    );
    subscriptions.add(
      api.provisionEvents.listen(
        (event) => update('Provision: ${event.state.name}'),
      ),
    );
    subscriptions.add(
      api.otaEvents.listen((event) {
        otaProgress = event.progress;
        update('OTA: ${event.state.name} ${(event.progress * 100).round()}%');
      }),
    );
    initialize();
  }

  Future<void> initialize() async {
    try {
      await api.configure(
        const ProvisionProtocolProfile(
          serviceUuid: '11111111-2222-3333-4444-555555555555',
          commandUuid: '11111111-2222-3333-4444-666666666666',
          eventUuid: '11111111-2222-3333-4444-777777777777',
          deviceNamePrefixes: ['Demo-'],
          operations: operations,
        ),
      );
      capabilities = await api.capabilities;
      update('Ready — grant permission, then scan');
    } on BleLinkKitException catch (error) {
      update('${error.code.name}: ${error.message}');
    }
  }

  void update(String value) {
    if (mounted) setState(() => status = value);
  }

  Future<void> scan() async {
    try {
      if (!await api.requestPermissions()) {
        throw const BleLinkKitException(
          BleErrorCode.permissionDenied,
          'Permission denied',
        );
      }
      devices.clear();
      await api.scan(const ScanOptions(includeSpp: true));
    } on BleLinkKitException catch (error) {
      update('${error.code.name}: ${error.message}');
    }
  }

  Future<void> connect(BleDevice device) async {
    try {
      if (useSpp) {
        await api.sppConnect(device.id);
      } else {
        await api.connect(device.id);
      }
      connectedId = device.id;
      update('Connected to ${device.name}');
    } on BleLinkKitException catch (error) {
      update('${error.code.name}: ${error.message}');
    }
  }

  Future<void> provision() async {
    try {
      final credentials = WifiCredentials(
        ssid: ssid.text,
        password: password.text,
      );
      if (useSpp) {
        await api.sppProvision(credentials);
      } else {
        await api.provision(credentials);
      }
      update('Provision request accepted');
    } on BleLinkKitException catch (error) {
      update('${error.code.name}: ${error.message}');
    }
  }

  Future<void> otaDemo() async {
    try {
      otaProgress = 0;
      await api.startOta(
        List<int>.generate(4096, (index) => index & 0xff),
        targetVersion: 'demo-2.0.0',
      );
    } on BleLinkKitException catch (error) {
      update('${error.code.name}: ${error.message}');
    }
  }

  @override
  void dispose() {
    for (final subscription in subscriptions) {
      subscription.cancel();
    }
    ssid.dispose();
    password.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) => Scaffold(
    appBar: AppBar(title: const Text('BLE Provision + OTA Demo')),
    body: ListView(
      padding: const EdgeInsets.all(16),
      children: [
        Text(status),
        const SizedBox(height: 8),
        if (capabilities?.supportsSpp ?? false)
          SwitchListTile(
            title: const Text('Use Android SPP fallback'),
            value: useSpp,
            onChanged: (value) => setState(() => useSpp = value),
          ),
        FilledButton(
          onPressed: scan,
          child: const Text('Grant permission and scan'),
        ),
        const Divider(),
        ...devices.values.map(
          (device) => ListTile(
            title: Text(device.name.isEmpty ? 'Unnamed device' : device.name),
            subtitle: Text('RSSI ${device.rssi} • ${device.id}'),
            trailing: FilledButton(
              onPressed: () => connect(device),
              child: const Text('Connect'),
            ),
          ),
        ),
        const Divider(),
        TextField(
          controller: ssid,
          decoration: const InputDecoration(labelText: 'SSID'),
        ),
        TextField(
          controller: password,
          obscureText: true,
          decoration: const InputDecoration(labelText: 'Password'),
        ),
        const SizedBox(height: 8),
        FilledButton(
          onPressed: connectedId == null ? null : provision,
          child: const Text('Provision Wi-Fi'),
        ),
        const SizedBox(height: 16),
        LinearProgressIndicator(value: otaProgress),
        FilledButton(
          onPressed: connectedId == null || useSpp ? null : otaDemo,
          child: const Text('Send generated demo firmware'),
        ),
      ],
    ),
  );
}
1
likes
150
points
62
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

Flutter BLE connection framework for multi-protocol scanning, generic GATT transfer, framed channels, provisioning, and resumable OTA.

Repository (GitHub)
View/report issues

Topics

#bluetooth #ble #gatt #scanner #ota

License

MIT (license)

Dependencies

flutter, plugin_platform_interface

More

Packages that depend on ble_link_kit

Packages that implement ble_link_kit