execute method
Future<VtjCommandResult<TResult> >
execute(
- BluetoothCharacteristic writeCharacteristic,
- BluetoothCharacteristic replyCharacteristic
Execute the command on the device.
This default implementation handles the common execution pattern:
- Validates using validate hook
- Clears stale data from the reply characteristic
- Sets up a stream listener filtered by commandId
- Writes the request and waits for response with timeout
Override this method only for commands with special execution needs (e.g., EnterDfuBootloaderCommand which expects disconnection).
Implementation
Future<VtjCommandResult<TResult>> execute(
BluetoothCharacteristic writeCharacteristic,
BluetoothCharacteristic replyCharacteristic,
) async {
// Run validation hook
final validationError = validate();
if (validationError != null) return validationError;
final completer = Completer<VtjCommandResult<TResult>>();
late StreamSubscription subscription;
try {
await _clearStaleData(replyCharacteristic);
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);
return await completer.future.timeout(
timeout,
onTimeout: () {
subscription.cancel();
return const VtjCommandResult.failure('Command timeout', 408);
},
);
} catch (e) {
subscription.cancel();
if (!completer.isCompleted) {
return VtjCommandResult.failure('Command failed: $e', null);
}
return completer.future;
}
}