提交蓝牙插件及封装

This commit is contained in:
魏少阳 2023-07-26 09:27:43 +08:00
parent 681ed316bc
commit 32b7f4cb52
16 changed files with 896 additions and 11 deletions

View File

@ -1,5 +1,6 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.star_lock">
package="com.example.star_lock"
xmlns:tools="http://schemas.android.com/tools">
<!-- The INTERNET permission is required for development. Specifically,
the Flutter tool needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc.
@ -8,4 +9,12 @@
<uses-permission android:name="android.permission.WRITE_CONTACTS"/>
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" android:usesPermissionFlags="neverForLocation" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" android:maxSdkVersion="30" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" android:maxSdkVersion="30" />
<uses-permission android:name="android.permission.BLUETOOTH_SCAN"
tools:remove="android:usesPermissionFlags"
tools:targetApi="s" />
</manifest>

View File

@ -1,6 +1,7 @@
# Uncomment this line to define a global platform for your project
# platform :ios, '11.0'
#use_modular_headers!
use_frameworks! :linkage => :static
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true'

View File

@ -11,6 +11,12 @@ PODS:
- path_provider_foundation (0.0.1):
- Flutter
- FlutterMacOS
- Protobuf (3.23.4)
- reactive_ble_mobile (0.0.1):
- Flutter
- Protobuf (~> 3.5)
- SwiftProtobuf (~> 1.0)
- SwiftProtobuf (1.22.0)
- url_launcher_ios (0.0.1):
- Flutter
@ -21,8 +27,14 @@ DEPENDENCIES:
- image_picker_ios (from `.symlinks/plugins/image_picker_ios/ios`)
- package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
- reactive_ble_mobile (from `.symlinks/plugins/reactive_ble_mobile/ios`)
- url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
SPEC REPOS:
trunk:
- Protobuf
- SwiftProtobuf
EXTERNAL SOURCES:
device_info_plus:
:path: ".symlinks/plugins/device_info_plus/ios"
@ -36,6 +48,8 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/package_info_plus/ios"
path_provider_foundation:
:path: ".symlinks/plugins/path_provider_foundation/darwin"
reactive_ble_mobile:
:path: ".symlinks/plugins/reactive_ble_mobile/ios"
url_launcher_ios:
:path: ".symlinks/plugins/url_launcher_ios/ios"
@ -45,9 +59,12 @@ SPEC CHECKSUMS:
flutter_native_contact_picker: bd430ba0fbf82768bb50c2c52a69a65759a8f907
image_picker_ios: 4a8aadfbb6dc30ad5141a2ce3832af9214a705b5
package_info_plus: 6c92f08e1f853dc01228d6f553146438dafcd14e
path_provider_foundation: eaf5b3e458fc0e5fbb9940fb09980e853fe058b8
path_provider_foundation: 29f094ae23ebbca9d3d0cec13889cd9060c0e943
Protobuf: c6bc59bbab3d38a71c67f62d7cb7ca8f8ea4eca1
reactive_ble_mobile: 9ce6723d37ccf701dbffd202d487f23f5de03b4c
SwiftProtobuf: 40bd808372cb8706108f22d28f8ab4a6b9bc6989
url_launcher_ios: 08a3dfac5fb39e8759aeb0abbd5d9480f30fc8b4
PODFILE CHECKSUM: 663715e941f9adb426e33bf9376914006f9ea95b
PODFILE CHECKSUM: 2f695eae20a86ca77f41095e9bd74ba52bd6304c
COCOAPODS: 1.12.1

View File

@ -9,7 +9,7 @@
/* Begin PBXBuildFile section */
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
3ED61D98CC08F93BC18EEFD0 /* libPods-Runner.a in Frameworks */ = {isa = PBXBuildFile; fileRef = B7537A79A618D0BBB434A17B /* libPods-Runner.a */; };
3EF1E85D6F1EE0C0DCF8449F /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 09D8B2FA2B26BA5BFF31AB2A /* Pods_Runner.framework */; };
978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; };
97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; };
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
@ -31,6 +31,7 @@
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
09D8B2FA2B26BA5BFF31AB2A /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
@ -46,7 +47,6 @@
97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
99F6494C6B1A24363CCC6D32 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; };
B7537A79A618D0BBB434A17B /* libPods-Runner.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Runner.a"; sourceTree = BUILT_PRODUCTS_DIR; };
BDB5B400176768766E85D5EB /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; };
DC20461C9C4624BEB2DCD779 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; };
/* End PBXFileReference section */
@ -56,7 +56,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
3ED61D98CC08F93BC18EEFD0 /* libPods-Runner.a in Frameworks */,
3EF1E85D6F1EE0C0DCF8449F /* Pods_Runner.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -76,7 +76,7 @@
9304F75C378DB3447BB2408C /* Frameworks */ = {
isa = PBXGroup;
children = (
B7537A79A618D0BBB434A17B /* libPods-Runner.a */,
09D8B2FA2B26BA5BFF31AB2A /* Pods_Runner.framework */,
);
name = Frameworks;
sourceTree = "<group>";
@ -349,6 +349,7 @@
isa = XCBuildConfiguration;
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = YES;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = 7NLFRKNVY3;
@ -475,6 +476,7 @@
isa = XCBuildConfiguration;
baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = YES;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = 7NLFRKNVY3;
@ -494,6 +496,7 @@
isa = XCBuildConfiguration;
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = YES;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = 7NLFRKNVY3;

View File

@ -14,6 +14,10 @@
</array>
<key>NSContactsUsageDescription</key>
<string>Reason we need access to the contact list</string>
<key>NSBluetoothAlwaysUsageDescription</key>
<string>The app uses bluetooth to find, connect and transfer data between different devices</string>
<key>NSBluetoothPeripheralUsageDescription</key>
<string>The app uses bluetooth to find, connect and transfer data between different devices</string>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key>

View File

@ -2,10 +2,10 @@ import 'package:flutter/material.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart';
import 'package:get/get_core/src/get_main.dart';
import 'package:star_lock/tools/app_manager.dart';
import 'package:star_lock/tools/device_info_service.dart';
import 'package:star_lock/tools/platform_info_services.dart';
import 'package:star_lock/tools/reactiveBlueTool/getx_ble.dart';
import 'package:star_lock/tools/store_service.dart';
import 'package:star_lock/translations/app_dept.dart';
import 'package:star_lock/translations/trans_lib.dart';
@ -19,6 +19,8 @@ void main() async {
//
await _initTranslation();
GetxBle();
runApp(MyApp());
}

View File

@ -47,7 +47,8 @@ class _UnlockQRCodePageState extends State<UnlockQRCodePage> {
// data:'二维码',
// size:300,
// ),
QrImage(data: 'www.baidu.com', size: 300.w),
// QrImage(data: 'www.baidu.com', size: 300.w),
QrImageView(data: 'www.baidu.com', size: 300.w),
SizedBox(height: 50.w,),
SubmitBtn(btnName: TranslationLoader.lanKeys!.share!.tr,
borderRadius: 20.w,

View File

@ -1,4 +1,7 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_reactive_ble/flutter_reactive_ble.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart';
import 'package:star_lock/app_settings/app_colors.dart';
@ -7,6 +10,7 @@ import 'package:star_lock/mine/mineSet/mineSet_page.dart';
import '../../appRouters.dart';
import '../../baseWidget.dart';
import '../../mine/mine_page.dart';
import '../../tools/reactiveBlueTool/getx_ble.dart';
import '../../tools/titleAppBar.dart';
import '../../translations/trans_lib.dart';
@ -18,6 +22,26 @@ class StarLockMain extends StatefulWidget {
}
class _StarLockMainState extends State<StarLockMain> with BaseWidget{
// final logic = Get.put(GetxBle());
StreamSubscription<DiscoveredDevice>? _streamSubscription;
StreamSubscription<ConnectionStateUpdate>? _connectionStreamSubscription;
final flutterReactiveBle = FlutterReactiveBle();
List<DiscoveredDevice> deviceList = [];
@override
void setState(VoidCallback fn) {
// TODO: implement setState
super.setState(fn);
// BleScanner.to.startScan([
//
// ]);
GetxBle.call.scanner.startScan([]);
}
@override
Widget build(BuildContext context) {
return Scaffold(

View File

@ -0,0 +1,65 @@
import 'dart:async';
import 'package:flutter_reactive_ble/flutter_reactive_ble.dart';
import 'package:get/get.dart';
class BleDeviceConnector extends GetxController {
BleDeviceConnector({
required FlutterReactiveBle ble,
required void Function(String message) logMessage,
}) : _ble = ble,
_logMessage = logMessage {
rxBleConnectionState.bindStream(_deviceConnectionController.stream);
}
final FlutterReactiveBle _ble;
final void Function(String message) _logMessage;
final rxBleConnectionState = Rx<ConnectionStateUpdate>(
const ConnectionStateUpdate(
deviceId: "",
connectionState: DeviceConnectionState.disconnected,
failure: null));
final _deviceConnectionController = StreamController<ConnectionStateUpdate>();
late StreamSubscription<ConnectionStateUpdate> _connection;
Future<void> connect(String deviceMAC) async {
_logMessage('Start connecting to $deviceMAC');
_connection = _ble.connectToDevice(id: deviceMAC).listen(
(update) {
_logMessage(
'ConnectionState for device $deviceMAC : ${update.connectionState}');
_deviceConnectionController.add(update);
},
onError: (Object e) =>
_logMessage('Connecting to device $deviceMAC resulted in error $e'),
);
}
Future<void> disconnect(String deviceMAC) async {
try {
_logMessage('disconnecting to device: $deviceMAC');
await _connection.cancel();
} on Exception catch (e, _) {
_logMessage("Error disconnecting from a device: $e");
} finally {
// Since [_connection] subscription is terminated, the "disconnected" state cannot be received and propagated
_deviceConnectionController.add(
ConnectionStateUpdate(
deviceId: deviceMAC,
connectionState: DeviceConnectionState.disconnected,
failure: null,
),
);
}
}
@override
Future<void> onClose() async {
rxBleConnectionState.close();
await _deviceConnectionController.close();
super.onClose();
}
}

View File

@ -0,0 +1,82 @@
import 'dart:async';
import 'package:flutter_reactive_ble/flutter_reactive_ble.dart';
import 'package:get/get.dart';
class BleDeviceInteractor extends GetxController {
BleDeviceInteractor({
required FlutterReactiveBle ble,
required void Function(String message) logMessage,
}) : _ble = ble,
_logMessage = logMessage;
final FlutterReactiveBle _ble;
final void Function(String message) _logMessage;
Future<List<DiscoveredService>> discoverServices(String deviceId) async {
try {
_logMessage('Start discovering services for: $deviceId');
final result = await _ble.discoverServices(deviceId);
_logMessage('Discovering services finished');
return result;
} on Exception catch (e) {
_logMessage('Error occurred when discovering services: $e');
rethrow;
}
}
Future<List<int>> readCharacteristic(
QualifiedCharacteristic characteristic) async {
try {
final result = await _ble.readCharacteristic(characteristic);
_logMessage('Read ${characteristic.characteristicId}: value = $result');
return result;
} on Exception catch (e, s) {
_logMessage(
'Error occurred when reading ${characteristic.characteristicId} : $e',
);
// ignore: avoid_print
print(s);
rethrow;
}
}
Future<void> writeCharacteristicWithResponse(
QualifiedCharacteristic characteristic, List<int> value) async {
try {
_logMessage(
'Write with response value : $value to ${characteristic.characteristicId}');
await _ble.writeCharacteristicWithResponse(characteristic, value: value);
} on Exception catch (e, s) {
_logMessage(
'Error occurred when writing ${characteristic.characteristicId} : $e',
);
// ignore: avoid_print
print(s);
rethrow;
}
}
Future<void> writeCharacteristicWithoutResponse(
QualifiedCharacteristic characteristic, List<int> value) async {
try {
await _ble.writeCharacteristicWithoutResponse(characteristic,
value: value);
_logMessage(
'Write without response value: $value to ${characteristic.characteristicId}');
} on Exception catch (e, s) {
_logMessage(
'Error occurred when writing ${characteristic.characteristicId} : $e',
);
// ignore: avoid_print
print(s);
rethrow;
}
}
Stream<List<int>> subScribeToCharacteristic(
QualifiedCharacteristic characteristic) {
_logMessage('Subscribing to: ${characteristic.characteristicId} ');
return _ble.subscribeToCharacteristic(characteristic);
}
}

View File

@ -0,0 +1,28 @@
import 'package:flutter_reactive_ble/flutter_reactive_ble.dart';
import 'package:get/get.dart';
class BleLogger extends GetxController {
BleLogger({
required FlutterReactiveBle ble,
}) : _ble = ble;
final FlutterReactiveBle _ble;
final Rx<List<String>> rxMessages = Rx<List<String>>([]);
final RxBool rxVerboseLogging = false.obs;
void addToLog(String message) {
final now = DateTime.now();
final formattedDate = '${now.year}-${now.month}-${now.day} ${now.hour}:${now.minute}:${now.second}';
rxMessages.value.add('$formattedDate - $message');
}
void clearLogs() => rxMessages.value.clear();
void verboseLoggingUpdate() {
if (rxVerboseLogging.value) {
_ble.logLevel = LogLevel.verbose;
} else {
_ble.logLevel = LogLevel.none;
}
}
}

View File

@ -0,0 +1,114 @@
import 'dart:async';
import 'package:flutter_reactive_ble/flutter_reactive_ble.dart';
import 'package:get/get.dart';
import 'package:meta/meta.dart';
///
class BleScanner extends GetxController {
BleScanner({
required FlutterReactiveBle ble,
required void Function(String message) logMessage,
}) : _ble = ble,
_logMessage = logMessage {
rxBleScannerState.bindStream(_stateStreamController.stream);
}
// static BleScanner get to => Get.find<BleScanner>();
final FlutterReactiveBle _ble;
/// FlutterReactiveBle
final void Function(String message) _logMessage;
///
final StreamController<BleScannerState> _stateStreamController =
StreamController();
StreamSubscription? _subscription;
/// 使 Getx Rx
final rxBleScannerState = Rx<BleScannerState>(
const BleScannerState(discoveredDevices: [], scanIsInProgress: false));
final _devices = <DiscoveredDevice>[];
///
void startScan(List<Uuid> serviceIds) {
_logMessage('Start ble discovery');
_devices.clear();
_subscription?.cancel();
_subscription = _ble.scanForDevices(withServices: serviceIds).listen((device) {
final knownDeviceIndex = _devices.indexWhere((d) => d.id == device.id);
if (knownDeviceIndex >= 0) {
_devices[knownDeviceIndex] = device;
} else {
// if (device.name.toLowerCase().contains((filter.name ?? "".toLowerCase()))) {
// if (device.id.contains((filter.mac ?? ""))) {
// if (device.rssi >= (filter.rssi ?? -100)) {
_devices.add(device);
// }
// }
// }
}
_pushState();
}, onError: (Object e) => _logMessage('Device scan fails with error: $e'));
_pushState();
}
///
void _pushState() {
_stateStreamController.add(
BleScannerState(
discoveredDevices: _devices,
scanIsInProgress: _subscription != null,
),
);
}
///
Future<void> stopScan() async {
_logMessage('Stop ble discovery');
await _subscription?.cancel();
_subscription = null;
_pushState();
}
/// GetxController
@override
Future<void> onClose() async {
rxBleScannerState.close();
await _stateStreamController.close();
super.onClose();
}
}
///
@immutable
class BleScannerState {
const BleScannerState({
required this.discoveredDevices,
required this.scanIsInProgress,
});
final List<DiscoveredDevice> discoveredDevices;
final bool scanIsInProgress;
}
///
class BleScannerFilter {
String? name;
String? mac;
List<Uuid>? serviceId;
int? rssi;
BleScannerFilter({
this.name,
this.mac,
this.serviceId,
this.rssi,
});
}

View File

@ -0,0 +1,485 @@
class BleSigGattUuids {
static const Map serviceUuids = {
"1800": "Generic Access",
"1811": "Alert Notification Service",
"1815": "Automation IO",
"180F": "Battery Service",
"1810": "Blood Pressure",
"181B": "Body Composition",
"181E": "Bond Management Service",
"181F": "Continuous Glucose Monitoring",
"1805": "Current Time Service",
"1818": "Cycling Power",
"1816": "Cycling Speed and Cadence",
"180A": "Device Information",
"181A": "Environmental Sensing",
"1826": "Fitness Machine",
"1801": "Generic Attribute",
"1808": "Glucose",
"1809": "Health Thermometer",
"180D": "Heart Rate",
"1823": "HTTP Proxy",
"1812": "Human Interface Device",
"1802": "Immediate Alert",
"1821": "Indoor Positioning",
"183A": "Insulin Delivery",
"1820": "Internet Protocol Support Service",
"1803": "Link Loss",
"1819": "Location and Navigation",
"1827": "Mesh Provisioning Service",
"1828": "Mesh Proxy Service",
"1807": "Next DST Change Service",
"1825": "Object Transfer Service",
"180E": "Phone Alert Status Service",
"1822": "Pulse Oximeter Service",
"1829": "Reconnection Configuration",
"1806": "Reference Time Update Service",
"1814": "Running Speed and Cadence",
"1813": "Scan Parameters",
"1824": "Transport Discovery",
"1804": "Tx Power",
"181C": "User Data",
"181D": "Weight Scale",
"183B": "Binary Sensor",
"183C": "Emergency Configuration",
"183E": "Physical Activity Monitor",
"1843": "Audio Input Control",
"1844": "Volume Control",
"1845": "Volume Offset Control",
"1846": "Coordinated Set Identification",
"1847": "Device Time",
"1848": "Media Control",
"1849": "Generic Media Control",
"184A": "Constant Tone Extension",
"184B": "Telephone Bearer",
"184C": "Generic Telephone Bearer",
"184D": "Microphone Control",
"184E": "Audio Stream Control",
"184F": "Broadcast Audio Scan",
"1850": "Published Audio Capabilities",
"1851": "Published Audio Data",
"1852": "Broadcast Audio Announcement",
"1853": "Common Audio",
"1854": "Hearing Access",
"1855": "TMAS"
};
static const Map characteristicUuids = {
"2A7E": "Aerobic Heart Rate Lower Limit",
"2A84": "Aerobic Heart Rate Upper Limit",
"2A7F": "Aerobic Threshold",
"2A80": "Age",
"2A5A": "Aggregate",
"2A43": "Alert Category ID",
"2A42": "Alert Category ID Bit Mask",
"2A06": "Alert Level",
"2A44": "Alert Notification Control Point",
"2A3F": "Alert Status",
"2AB3": "Altitude",
"2A81": "Anaerobic Heart Rate Lower Limit",
"2A82": "Anaerobic Heart Rate Upper Limit",
"2A83": "Anaerobic Threshold",
"2A58": "Analog",
"2A59": "Analog Output",
"2A73": "Apparent Wind Direction",
"2A72": "Apparent Wind Speed",
"2A01": "Appearance",
"2AA3": "Barometric Pressure Trend",
"2A19": "Battery Level",
"2A1B": "Battery Level State",
"2A1A": "Battery Power State",
"2A49": "Blood Pressure Feature",
"2A35": "Blood Pressure Measurement",
"2A9B": "Body Composition Feature",
"2A9C": "Body Composition Measurement",
"2A38": "Body Sensor Location",
"2AA4": "Bond Management Control Point",
"2AA5": "Bond Management Features",
"2A22": "Boot Keyboard Input Report",
"2A32": "Boot Keyboard Output Report",
"2A33": "Boot Mouse Input Report",
"2AA6": "Central Address Resolution",
"2AA8": "CGM Feature",
"2AA7": "CGM Measurement",
"2AAB": "CGM Session Run Time",
"2AAA": "CGM Session Start Time",
"2AAC": "CGM Specific Ops Control Point",
"2AA9": "CGM Status",
"2ACE": "Cross Trainer Data",
"2A5C": "CSC Feature",
"2A5B": "CSC Measurement",
"2A2B": "Current Time",
"2A66": "Cycling Power Control Point",
"2A65": "Cycling Power Feature",
"2A63": "Cycling Power Measurement",
"2A64": "Cycling Power Vector",
"2A99": "Database Change Increment",
"2A85": "Date of Birth",
"2A86": "Date of Threshold Assessment",
"2A08": "Date Time",
"2A0A": "Day Date Time",
"2A09": "Day of Week",
"2A7D": "Descriptor Value Changed",
"2A00": "Device Name",
"2A7B": "Dew Point",
"2A56": "Digital",
"2A57": "Digital Output",
"2A0D": "DST Offset",
"2A6C": "Elevation",
"2A87": "Email Address",
"2A0B": "Exact Time 100",
"2A0C": "Exact Time 256",
"2A88": "Fat Burn Heart Rate Lower Limit",
"2A89": "Fat Burn Heart Rate Upper Limit",
"2A26": "Firmware Revision String",
"2A8A": "First Name",
"2AD9": "Fitness Machine Control Point",
"2ACC": "Fitness Machine Feature",
"2ADA": "Fitness Machine Status",
"2A8B": "Five Zone Heart Rate Limits",
"2AB2": "Floor Number",
"2A8C": "Gender",
"2A51": "Glucose Feature",
"2A18": "Glucose Measurement",
"2A34": "Glucose Measurement Context",
"2A74": "Gust Factor",
"2A27": "Hardware Revision String",
"2A39": "Heart Rate Control Point",
"2A8D": "Heart Rate Max",
"2A37": "Heart Rate Measurement",
"2A7A": "Heat Index",
"2A8E": "Height",
"2A4C": "HID Control Point",
"2A4A": "HID Information",
"2A8F": "Hip Circumference",
"2ABA": "HTTP Control Point",
"2AB9": "HTTP Entity Body",
"2AB7": "HTTP Headers",
"2AB8": "HTTP Status Code",
"2ABB": "HTTPS Security",
"2A6F": "Humidity",
"2B22": "IDD Annunciation Status",
"2B25": "IDD Command Control Point",
"2B26": "IDD Command Data",
"2B23": "IDD Features",
"2B28": "IDD History Data",
"2B27": "IDD Record Access Control Point",
"2B21": "IDD Status",
"2B20": "IDD Status Changed",
"2B24": "IDD Status Reader Control Point",
"2A2A": "IEEE 11073-20601 Regulatory Certification Data List",
"2AD2": "Indoor Bike Data",
"2AAD": "Indoor Positioning Configuration",
"2A36": "Intermediate Cuff Pressure",
"2A1E": "Intermediate Temperature",
"2A77": "Irradiance",
"2AA2": "Language",
"2A90": "Last Name",
"2AAE": "Latitude",
"2A6B": "LN Control Point",
"2A6A": "LN Feature",
"2AB1": "Local East Coordinate",
"2AB0": "Local North Coordinate",
"2A0F": "Local Time Information",
"2A67": "Location and Speed Characteristic",
"2AB5": "Location Name",
"2AAF": "Longitude",
"2A2C": "Magnetic Declination",
"2AA0": "Magnetic Flux Density - 2D",
"2AA1": "Magnetic Flux Density - 3D",
"2A29": "Manufacturer Name String",
"2A91": "Maximum Recommended Heart Rate",
"2A21": "Measurement Interval",
"2A24": "Model Number String",
"2A68": "Navigation",
"2A3E": "Network Availability",
"2A46": "New Alert",
"2AC5": "Object Action Control Point",
"2AC8": "Object Changed",
"2AC1": "Object First-Created",
"2AC3": "Object ID",
"2AC2": "Object Last-Modified",
"2AC6": "Object List Control Point",
"2AC7": "Object List Filter",
"2ABE": "Object Name",
"2AC4": "Object Properties",
"2AC0": "Object Size",
"2ABF": "Object Type",
"2ABD": "OTS Feature",
"2A04": "Peripheral Preferred Connection Parameters",
"2A02": "Peripheral Privacy Flag",
"2A5F": "PLX Continuous Measurement Characteristic",
"2A60": "PLX Features",
"2A5E": "PLX Spot-Check Measurement",
"2A50": "PnP ID",
"2A75": "Pollen Concentration",
"2A2F": "Position 2D",
"2A30": "Position 3D",
"2A69": "Position Quality",
"2A6D": "Pressure",
"2A4E": "Protocol Mode",
"2A62": "Pulse Oximetry Control Point",
"2A78": "Rainfall",
"2B1D": "RC Feature",
"2B1E": "RC Settings",
"2A03": "Reconnection Address",
"2B1F": "Reconnection Configuration Control Point",
"2A52": "Record Access Control Point",
"2A14": "Reference Time Information",
"2A3A": "Removable",
"2A4D": "Report",
"2A4B": "Report Map",
"2AC9": "Resolvable Private Address Only",
"2A92": "Resting Heart Rate",
"2A40": "Ringer Control point",
"2A41": "Ringer Setting",
"2AD1": "Rower Data",
"2A54": "RSC Feature",
"2A53": "RSC Measurement",
"2A55": "SC Control Point",
"2A4F": "Scan Interval Window",
"2A31": "Scan Refresh",
"2A3C": "Scientific Temperature Celsius",
"2A10": "Secondary Time Zone",
"2A5D": "Sensor Location",
"2A05": "Service Changed",
"2A0E": "Time Zone",
"2A11": "Time with DST",
"2A12": "Time Accuracy",
"2A13": "Time Source",
"2A15": "Time Broadcast",
"2A16": "Time Update Control Point",
"2A17": "Time Update State",
"2A25": "Serial Number String",
"2A3B": "Service Required",
"2A28": "Software Revision String",
"2A93": "Sport Type for Aerobic and Anaerobic Thresholds",
"2AD0": "Stair Climber Data",
"2ACF": "Step Climber Data",
"2A3D": "String",
"2AD7": "Supported Heart Rate Range",
"2AD5": "Supported Inclination Range",
"2A47": "Supported New Alert Category",
"2AD8": "Supported Power Range",
"2AD6": "Supported Resistance Level Range",
"2AD4": "Supported Speed Range",
"2A48": "Supported Unread Alert Category",
"2A23": "System ID",
"2ABC": "TDS Control Point",
"2A6E": "Temperature",
"2A1F": "Temperature Celsius",
"2A20": "Temperature Fahrenheit",
"2A1C": "Temperature Measurement",
"2A1D": "Temperature Type",
"2A94": "Three Zone Heart Rate Limits",
"2A70": "True Wind Speed",
"2A71": "True Wind Direction",
"2A95": "Two Zone Heart Rate Limit",
"2A07": "Tx Power Level",
"2AB4": "Uncertainty",
"2A45": "Unread Alert Status",
"2AB6": "URI",
"2A9F": "User Control Point",
"2A9A": "User Index",
"2A76": "UV Index",
"2A79": "Wind Chill",
"2A96": "VO2 Max",
"2A97": "Waist Circumference",
"2A98": "Weight",
"2A9D": "Weight Measurement",
"2A9E": "Weight Scale Feature",
"2ACD": "Treadmill Data",
"2AD3": "Training Status",
"2AE0": "Average Current",
"2AE1": "Average Voltage",
"2AE2": "Boolean",
"2AE3": "Chromatic Distance From Planckian",
"2AE4": "Chromaticity Coordinates",
"2AE5": "Chromaticity In CCT And Duv Values",
"2AE6": "Chromaticity Tolerance",
"2AE7": "CIE 13.3-1995 Color Rendering Index",
"2AE8": "Coefficient",
"2AE9": "Correlated Color Temperature",
"2AEA": "Count 16",
"2AEB": "Count 24",
"2AEC": "Country Code",
"2AED": "Date UTC",
"2AEE": "Electric Current",
"2AEF": "Electric Current Range",
"2AF0": "Electric Current Specification",
"2AF1": "Electric Current Statistics",
"2AF2": "Energy",
"2AF3": "Energy In A Period Of Day",
"2AF4": "Event Statistics",
"2AF5": "Fixed String 16",
"2AF6": "Fixed String 24",
"2AF7": "Fixed String 36",
"2AF8": "Fixed String 8",
"2AF9": "Generic Level",
"2AFA": "Global Trade Item Number",
"2AFB": "Illuminance",
"2AFC": "Luminous Efficacy",
"2AFD": "Luminous Energy",
"2AFE": "Luminous Exposure",
"2AFF": "Luminous Flux",
"2B00": "Luminous Flux Range",
"2B01": "Luminous Intensity",
"2B02": "B02 Mass Flow",
"2B03": "Perceived Lightness",
"2B04": "Percentage 8",
"2B05": "Power",
"2B06": "Power Specification",
"2B07": "Relative Runtime In A Current Range",
"2B08": "Relative Runtime In A Generic Level Range",
"2B09": "Relative Value In A Voltage Range",
"2B0A": "Relative Value In An Illuminance Range",
"2B0B": "Relative Value In A Period Of Day",
"2B0C": "Relative Value In A Temperature Range",
"2B0D": "Temperature 8",
"2B0E": "Temperature 8 In A Period Of Day",
"2B0F": "Temperature 8 Statistics",
"2B10": "Temperature Range",
"2B11": "Temperature Statistics",
"2B12": "Time Decihour 8",
"2B13": "Time Exponential 8",
"2B14": "Time Hour 24",
"2B15": "Time Millisecond 24",
"2B16": "Time Second 16",
"2B17": "Time Second 8",
"2B18": "Voltage",
"2B19": "Voltage Specification",
"2B1A": "Volume Flow",
"2B1B": "Volume Flow",
"2B1C": "Volume Flow In A Period Of Day",
"2B29": "Client Supported Features",
"2B2A": "Database Hash",
"2B2B": "BSS Control Point",
"2B2C": "BSS Response",
"2B2D": "Emergency ID",
"2B2E": "Emergency Text",
"2B34": "Enhanced Blood Pressure Measurement",
"2B35": "Enhanced Intermediate Cuff Pressure",
"2B36": "Blood Pressure Record",
"2B38": "BR-EDR Handover Data",
"2B39": "Bluetooth SIG Data",
"2B3A": "Server Supported Features",
"2B3B": "Physical Activity Monitor Features",
"2B3C": "General Activity Instantaneous Data",
"2B3D": "General Activity Summary Data",
"2B3E": "CardioRespiratory Activity Instantaneous Data",
"2B3F": "CardioRespiratory Activity Summary Data",
"2B40": "Step Counter Activity Summary Data",
"2B41": "Sleep Activity Instantaneous Data",
"2B42": "Sleep Activity Summary Data",
"2B43": "Physical Activity Monitor Control Point",
"2B44": "Activity Current Session",
"2B45": "Physical Activity Session Descriptor",
"2B46": "Preferred Units",
"2B47": "High Resolution Height",
"2B48": "Middle Name",
"2B49": "Stride Length",
"2B4A": "Handedness",
"2B4B": "Device Wearing Position",
"2B4C": "Four Zone Heart Rate Limits",
"2B4D": "High Intensity Exercise Threshold",
"2B4E": "Activity Goal",
"2B4F": "Sedentary Interval Notification",
"2B50": "Caloric Intake",
"2B51": "TMAP Role",
"2B77": "Audio Input State",
"2B78": "Audio Input Control Point",
"2B79": "Audio Input Type",
"2B7A": "Audio Input Status",
"2B7B": "Audio Input Control Point",
"2B7C": "Audio Input Description",
"2B7D": "Volume State",
"2B7E": "Volume Control Point",
"2B7F": "Volume Flags",
"2B80": "Volume Offset State",
"2B81": "Audio Location",
"2B82": "Volume Offset Control Point",
"2B83": "Audio Output State",
"2B84": "Set Identity Resolving Key",
"2B85": "Coordinated Set Size",
"2B86": "Set Member Lock",
"2B87": "Set Member Rank",
"2B8E": "Device Time Feature",
"2B8F": "Device Time Parameters",
"2B90": "Device Time",
"2B91": "Device Time Control Point",
"2B92": "Time Change Log Data",
"2B93": "Media Player Name",
"2B94": "Media Player Icon Object ID",
"2B95": "Media Player Icon URL",
"2B96": "Track Changed",
"2B97": "Track Title",
"2B98": "Track Duration",
"2B99": "Track Position",
"2B9A": "Playback Speed",
"2B9B": "Seeking Speed",
"2B9C": "Current Track Segments Object ID",
"2B9D": "Current Track Object ID",
"2B9E": "Next Track Object ID",
"2B9F": "Parent Group Object ID",
"2BA0": "Current Group Object ID",
"2BA1": "Playing Order",
"2BA2": "Playing Orders Supported",
"2BA3": "Media State",
"2BA4": "Media Control Point",
"2BA5": "Media Control Point Opcodes Supported",
"2BA6": "Search Results Object ID",
"2BA7": "Search Control Point",
"2BA9": "Media Player Icon Object Type",
"2BAA": "Track Segments Object Type",
"2BAB": "Track Object Type",
"2BAC": "Group Object Type",
"2BAD": "Constant Tone Extension Enable",
"2BAE": "Advertising Constant Tone Extension Minimum Length",
"2BAF": "Advertising Constant Tone Extension Minimum Transmit Count",
"2BB0": "Advertising Constant Tone Extension Transmit Duration",
"2BB1": "Advertising Constant Tone Extension Interval",
"2BB2": "Advertising Constant Tone Extension PHY",
"2BB3": "Bearer Provider Name",
"2BB4": "Bearer UCI",
"2BB5": "Bearer Technology",
"2BB6": "Bearer URI Schemes Supported List",
"2BB7": "Bearer Signal Strength",
"2BB8": "Bearer Signal Strength Reporting Interval",
"2BB9": "Bearer List Current Calls",
"2BBA": "Content Control ID",
"2BBB": "Status Flags",
"2BBC": "Incoming Call Target Bearer URI",
"2BBD": "Call State",
"2BBE": "Call Control Point",
"2BBF": "Call Control Point Optional Opcodes",
"2BC0": "Termination Reason",
"2BC1": "Incoming Call",
"2BC2": "Call Friendly Name",
"2BC3": "Mute",
"2BC4": "Sink ASE",
"2BC5": "Source ASE",
"2BC6": "ASE Control Point",
"2BC7": "Broadcast Audio Scan Control Point",
"2BC8": "Broadcast Receive State",
"2BC9": "Sink PAC",
"2BCA": "Sink Audio Locations",
"2BCB": "Source PAC",
"2BCC": "Source Audio Locations",
"2BCD": "Available Audio Contexts",
"2BCE": "Supported Audio Contexts",
"2BCF": "Ammonia Concentration",
"2BD0": "Carbon Monoxide Concentration",
"2BD1": "Nitrogen Dioxide Concentration",
"2BD2": "Nitrogen Dioxide Concentration",
"2BD3": "Non-Methane Volatile Organic Compounds Concentration",
"2BD4": "Ozone Concentration",
"2BD5": "Particulate Matter - PM1 Concentration",
"2BD6": "Particulate Matter - PM2.5 Concentration",
"2BD7": "Particulate Matter - PM10 Concentration",
"2BD8": "Sulfur Dioxide Concentration",
"2BD9": "Sulfur Hexafluoride Concentration",
"2BDA": "Hearing Aid Features",
"2BDB": "Hearing Aid Preset Control Point",
"2BDC": "Hearing Aid Preset",
};
}

View File

@ -0,0 +1,12 @@
import 'package:flutter_reactive_ble/flutter_reactive_ble.dart';
import 'package:get/get.dart';
class BleStatusMonitor extends GetxController {
BleStatusMonitor(this._ble) {
rxBleStatus.bindStream(_ble.statusStream);
}
final FlutterReactiveBle _ble;
final Rx<BleStatus> rxBleStatus = BleStatus.unknown.obs;
}

View File

@ -0,0 +1,34 @@
library getx_ble;
import 'package:flutter_reactive_ble/flutter_reactive_ble.dart';
import 'ble_device_connector.dart';
import 'ble_device_interactor.dart';
import 'ble_logger.dart';
import 'ble_scanner.dart';
import 'package:get/get.dart';
import 'ble_status_monitor.dart';
export 'ble_device_connector.dart';
export 'ble_device_interactor.dart';
export 'ble_logger.dart';
export 'ble_scanner.dart';
export 'ble_status_monitor.dart';
class GetxBle extends GetxController {
static GetxBle get call => Get.find<GetxBle>();
final ble = FlutterReactiveBle();
late final BleLogger bleLogger;
late final BleStatusMonitor bleStatusMonitor;
late final BleScanner scanner;
late final BleDeviceConnector connector;
late final BleDeviceInteractor interactor;
GetxBle() {
bleLogger = BleLogger(ble: ble);
bleStatusMonitor = BleStatusMonitor(ble);
scanner = BleScanner(ble: ble, logMessage: bleLogger.addToLog);
connector = BleDeviceConnector(ble: ble, logMessage: bleLogger.addToLog);
interactor = BleDeviceInteractor(ble: ble, logMessage: bleLogger.addToLog);
}
}

View File

@ -57,7 +57,7 @@ dependencies:
#底部弹出选择器
flutter_picker: ^2.1.0
#生成二维码
qr_flutter: ^4.0.0
qr_flutter: ^4.1.0
#底部选择
flutter_pickers: ^2.1.9
#万年历
@ -66,6 +66,10 @@ dependencies:
image_picker: ^1.0.1
#跳转到外部
url_launcher: ^6.1.12
#蓝牙
flutter_reactive_ble: ^5.1.1
# flutter_blue_plus: ^1.10.5
dev_dependencies:
flutter_test:
sdk: flutter