execute method

  1. @override
Future<VtjCommandResult<EnterDfuBootloaderResult>> execute(
  1. BluetoothCharacteristic writeCharacteristic,
  2. BluetoothCharacteristic replyCharacteristic
)
override

Custom execute that handles device reboot behavior.

The device will disconnect after entering DFU mode, so we treat timeout and connection errors as success.

Implementation

@override
Future<VtjCommandResult<EnterDfuBootloaderResult>> execute(
  BluetoothCharacteristic writeCharacteristic,
  BluetoothCharacteristic replyCharacteristic,
) async {
  final completer = Completer<VtjCommandResult<EnterDfuBootloaderResult>>();
  late StreamSubscription subscription;

  try {
    await replyCharacteristic.read();

    subscription = replyCharacteristic.lastValueStream.listen((data) {
      if (data.isNotEmpty && data[0] == commandId) {
        final result = parseResponse(Uint8List.fromList(data));
        subscription.cancel();
        if (!completer.isCompleted) {
          completer.complete(result);
        }
      }
    });

    if (!replyCharacteristic.isNotifying) {
      await replyCharacteristic.setNotifyValue(true);
    }

    final request = buildRequest();
    await writeCharacteristic.write(request, withoutResponse: false);

    // Shorter timeout - device will reboot
    return await completer.future.timeout(
      const Duration(seconds: 5),
      onTimeout: () {
        subscription.cancel();
        // Timeout is expected as device reboots
        return const VtjCommandResult.success(EnterDfuBootloaderResult.success());
      },
    );
  } catch (e) {
    subscription.cancel();
    if (!completer.isCompleted) {
      // Connection errors are expected as device reboots
      if (e.toString().contains('disconnect') ||
          e.toString().contains('connection')) {
        return const VtjCommandResult.success(EnterDfuBootloaderResult.success());
      }
      return VtjCommandResult.failure('Command failed: $e', null);
    }
    return completer.future;
  }
}