main
  1import 'package:collection/collection.dart';
  2
  3/// Bluetooth Base UUID from Bluetooth Core Spec
  4///
  5/// The full 128-bit value of a 16-bit or 32-bit UUID may be computed by a simple arithmetic
  6/// operation.
  7/// 128_bit_value = 16_bit_value × 296 + Bluetooth_Base_UUID
  8/// 128_bit_value = 32_bit_value × 296 + Bluetooth_Base_UUID
  9const bluetoothBaseUuid = '00000000-0000-1000-8000-00805F9B34FB';
 10
 11/// Generic BluetoothUuid representation
 12abstract class BluetoothUuid<BackendUuid> {
 13  /// constructor
 14  BluetoothUuid({ required this.uuid, required this.source }): assert(uuid.length == 36, 'Expected uuid to have a length of 36, got uuid=$uuid');
 15
 16  /// Create a BluetoothUuid from a string
 17  BluetoothUuid.fromString(this.uuid):
 18    assert(uuid.isNotEmpty, 'This static method is abstract'),
 19    source = bluetoothBaseUuid as BackendUuid // satisfy linter
 20    {
 21      throw AssertionError('This static method is abstract');
 22    }
 23
 24  /// 128-bit string representation of uuid
 25  final String uuid;
 26
 27  /// The backend specific uuid
 28  final BackendUuid source;
 29
 30  /// Whether this uuid is an official bluetooth core spec uuid
 31  bool get isBluetoothUuid => uuid.toUpperCase().endsWith(bluetoothBaseUuid.substring(8));
 32
 33  @override
 34  String toString() => uuid;
 35
 36  /// Returns the 16 bit value of the UUID if uuid is from bluetooth core spec, otherwise full id
 37  ///
 38  /// The 16-bit Attribute UUID replaces the x’s in the following:
 39  ///   0000xxxx-0000-1000-8000-00805F9B34FB
 40  String get shortId {
 41    final uuid = toString();
 42    assert(uuid.length == 36);
 43
 44    if (isBluetoothUuid) {
 45      return '0x${uuid.substring(4, 8)}';
 46    }
 47
 48    return uuid;
 49  }
 50
 51  @override
 52  bool operator == (Object other) {
 53    if (other is BluetoothUuid) {
 54      return toString() == other.toString();
 55    }
 56
 57    if (other is BluetoothService) {
 58      return toString() == other.uuid.toString();
 59    }
 60
 61    if (other is BluetoothCharacteristic) {
 62      return toString() == other.uuid.toString();
 63    }
 64
 65    return false;
 66  }
 67
 68  @override
 69  int get hashCode => super.hashCode * 17;
 70}
 71
 72/// Generic BluetoothService representation
 73abstract class BluetoothService<BackendService, BC extends BluetoothCharacteristic> {
 74  /// Initialize bluetooth service wrapper class
 75  BluetoothService({ required this.uuid, required this.source });
 76
 77  /// UUID of the service
 78  final BluetoothUuid uuid;
 79  /// Backend source for the service
 80  final BackendService source;
 81
 82  /// Get all characteristics for this service
 83  List<BC> get characteristics;
 84
 85  /// Returns the characteristic with requested [uuid], returns null if
 86  /// requested [uuid] was not found
 87  Future<BC?> getCharacteristicByUuid(BluetoothUuid uuid) async => characteristics.firstWhereOrNull((service) => service.uuid == uuid);
 88
 89  @override
 90  String toString() => 'BluetoothService{uuid: ${uuid.shortId}, source: ${source.runtimeType}}';
 91
 92  @override
 93  bool operator ==(Object other) => (other is BluetoothService)
 94    && toString() == other.toString();
 95
 96  @override
 97  int get hashCode => super.hashCode * 17;
 98}
 99
100/// Characteristic representation
101abstract class BluetoothCharacteristic<BackendCharacteristic> {
102  /// Initialize bluetooth characteristic wrapper class
103  BluetoothCharacteristic({ required this.uuid, required this.source });
104
105  /// UUID of the characteristic
106  final BluetoothUuid uuid;
107  /// Backend source for the characteristic
108  final BackendCharacteristic source;
109
110  /// Whether the characteristic can be read
111  bool get canRead;
112
113  /// Whether the characteristic can be written to
114  bool get canWrite;
115
116  /// Whether the characteristic can be written to without a response
117  bool get canWriteWithoutResponse;
118
119  /// Whether the characteristic permits notifications for it's value, without a response to indicate receipt of the notification.
120  bool get canNotify;
121
122  /// Whether the characteristic permits notifications for it's value, with a response to indicate receipt of the notification
123  bool get canIndicate;
124
125  @override
126  String toString() => 'BluetoothCharacteristic{uuid: ${uuid.shortId}, source: ${source.runtimeType}, '
127    'canRead: $canRead, canWrite: $canWrite, canWriteWithoutResponse: $canWriteWithoutResponse, '
128    'canNotify: $canNotify, canIndicate: $canIndicate}';
129
130  @override
131  bool operator ==(Object other) => (other is BluetoothCharacteristic)
132    && toString() == other.toString();
133
134  @override
135  int get hashCode => super.hashCode * 17;
136}