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}