123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500 |
- import 'dart:async';
- import 'dart:convert';
- import 'dart:core';
- import 'dart:math';
- import 'dart:typed_data';
- import 'package:buffer/buffer.dart';
- import 'package:flutter/material.dart';
- import 'package:flutter_blue/flutter_blue.dart';
- import 'package:shared_preferences/shared_preferences.dart';
- import 'package:sport/db/step_db.dart';
- import 'package:sport/services/api/inject_api.dart';
- class Bluetooth with ChangeNotifier, InjectApi {
- static final String PREF_KEY = "SHOES";
- factory Bluetooth() => _getInstance();
- static Bluetooth _instance;
- // 获取对象
- static Bluetooth _getInstance() {
- if (_instance == null) {
- // 使用私有的构造方法来创建对象
- _instance = Bluetooth._internal();
- }
- return _instance;
- }
- Bluetooth._internal() {
- //初始化(设置一些默认的)...
- }
- FlutterBlue flutterBlue = FlutterBlue.instance;
- StreamSubscription listenSubscription, periodicSubscription, characteristicSubscription, deviceSubscription, connectedDevicesSubscription;
- String _mac;
- BluetoothDevice _device;
- BluetoothCharacteristic _writeCharacteristic;
- bool _founded = false;
- // ignore: close_sinks
- final StreamController<bool> _queryController = StreamController.broadcast();
- Stream<bool> get queryStream => _queryController.stream;
- // ignore: close_sinks
- final StreamController<bool> _stateController = StreamController.broadcast();
- Stream<bool> get stateStream => _stateController.stream;
- BluetoothDevice get device => _device;
- bool _online = false;
- final ValueNotifier<Map> infoNotifier = ValueNotifier<Map>({});
- final ValueNotifier<Map> dataNotifier = ValueNotifier<Map>({});
- final ValueNotifier<int> electricityNotifier = ValueNotifier<int>(0);
- final ValueNotifier<int> stepNotifier = ValueNotifier<int>(0);
- final ValueNotifier<int> stepTotalNotifier = ValueNotifier<int>(0);
- final ValueNotifier<int> actionNotifier = ValueNotifier<int>(0);
- final ValueNotifier<List<int>> byteNotifier = ValueNotifier<List<int>>([]);
- PartInfo _partInfo;
- bool get isConnected => _online;
- resetData() {
- stepNotifier.value = 0;
- stepTotalNotifier.value = 0;
- actionNotifier.value = 0;
- }
- set device(BluetoothDevice device) {
- _device = device;
- notifyListeners();
- }
- Future<String> saveDevice(BluetoothDevice device) async {
- var id = device.id.toString();
- var prefs = await SharedPreferences.getInstance();
- prefs.setString(PREF_KEY, id);
- _mac = id;
- await connect(device);
- return id;
- }
- Future<String> getHistoryDevice() async {
- var prefs = await SharedPreferences.getInstance();
- return _mac = prefs.getString(PREF_KEY);
- }
- Future listen() async {
- var prefs = await SharedPreferences.getInstance();
- if (!prefs.containsKey("token")) {
- return;
- }
- FlutterBlue flutterBlue = FlutterBlue.instance;
- if (listenSubscription != null) return;
- print("bluetooth -- listen");
- listenSubscription = flutterBlue.state.listen((state) async {
- print("bluetooth -- state $state");
- if (state == BluetoothState.on) {
- connectedDevicesSubscription = Stream.periodic(Duration(seconds: 10)).asyncMap((_) => FlutterBlue.instance.connectedDevices).listen((event) {
- BluetoothDevice d = event.firstWhere((element) => element.id.toString() == _mac, orElse: () => null);
- if (d != null) {
- device = d;
- }
- });
- await connectDevice();
- } else {
- connectedDevicesSubscription?.cancel();
- }
- });
- }
- void timer() {
- // 定时读取设备信息
- periodicSubscription = Stream.periodic(Duration(seconds: 60)).listen((event) async {
- if (device == null) return;
- if (!_online) return;
- await queryDeviceData();
- await queryDeviceStep();
- });
- }
- void disposeDevice() {
- deviceSubscription?.cancel();
- characteristicSubscription?.cancel();
- device?.disconnect();
- print("bluetooth -- disconnect $device");
- _writeCharacteristic = null;
- }
- Future disposeBluetooth() async {
- connectedDevicesSubscription?.cancel();
- periodicSubscription?.cancel();
- listenSubscription?.cancel();
- listenSubscription = null;
- disposeDevice();
- FlutterBlue.instance?.stopScan();
- _founded = false;
- }
- Future disconnectDevice() async {
- device?.disconnect();
- device = null;
- }
- Future connectDevice() async {
- var mac = await getHistoryDevice();
- print("bluetooth -- connect $mac $device");
- if (mac?.isEmpty == true) {
- return;
- }
- if (device != null) {
- connect(device);
- return;
- }
- // Start scanning
- flutterBlue.startScan(timeout: Duration(seconds: 10));
- flutterBlue.scanResults.listen((results) {
- for (ScanResult r in results) {
- if (r.device.id.toString() == mac) {
- if (_founded) return;
- _founded = true;
- flutterBlue.stopScan();
- connect(r.device);
- print('${r.device.name} found! connect: ${r.rssi} ${r.device.id}');
- break;
- }
- print('${r.device.name} found! rssi: ${r.rssi}');
- }
- });
- }
- Future connect(BluetoothDevice newDevice) async {
- if (device != null) {
- if (!(device == newDevice)) {
- _online = false;
- disposeDevice();
- }
- }
- device = newDevice;
- deviceSubscription?.cancel();
- deviceSubscription = device.state.listen((event) async {
- print("device: $_mac --> state $event");
- if (event == BluetoothDeviceState.disconnected) {
- _online = false;
- characteristicSubscription?.cancel();
- _writeCharacteristic = null;
- periodicSubscription?.cancel();
- } else if (event == BluetoothDeviceState.connected) {
- _online = true;
- flutterBlue.stopScan();
- await discoverServices(device);
- await Future.delayed(Duration(seconds: 3));
- _stateController.add(true);
- queryDeviceInfo();
- queryDeviceData();
- queryDeviceStep();
- timer();
- }
- });
- device.connect();
- print("device: $_mac --> connect $device");
- }
- Future discoverServices(BluetoothDevice device) async {
- characteristicSubscription?.cancel();
- var services = await device.discoverServices();
- for (var service in services) {
- print('${device.name} found! service: $service');
- for (var characteristic in service.characteristics) {
- print('${device.name} found! characteristic: $characteristic ${characteristic.uuid.toString()}');
- if (characteristic.properties.write && characteristic.properties.writeWithoutResponse) {
- _writeCharacteristic = characteristic;
- }
- if (characteristic.properties.notify) {
- characteristic.setNotifyValue(true).then((succ) {
- print("characteristic notify --> $succ");
- if (succ) {
- characteristicSubscription = characteristic.value.listen((event) {
- print("characteristic change --> $event");
- byteNotifier.value = event;
- parse(event);
- });
- }
- });
- }
- }
- }
- }
- void parse(List<int> event) {
- if (event?.isEmpty == true) return;
- ByteDataReader reader = ByteDataReader();
- reader.add(event);
- int flagAA = reader.readUint8();
- // int flagBB = reader.readUint8();
- // int flagCC = reader.readUint8();
- int len = reader.readUint8();
- int len1 = 0xFF - reader.readUint8();
- // int index = reader.readUint8();
- int cmd = reader.readUint8();
- print(" change --> ${flagAA} ${len} ${~len} ${cmd}");
- // print(" change --> ${len} ${~len} ${len1}");
- // print(" change --> ${index} ${cmd}");
- if (!(flagAA == 0xAA)) return;
- if (len != len1) return;
- parseCmd(cmd, reader.read(event.length - 5));
- }
- void parseCmd(int cmd, Uint8List byteArray) {
- print(" cmd $cmd data --> $byteArray");
- switch (cmd) {
- case 0x01:
- _parseAction(byteArray);
- break;
- case 0xA0:
- break;
- case 0xA1:
- _parseQuery(byteArray);
- break;
- }
- }
- void _parseAction(Uint8List byteArray) {
- ByteDataReader reader = ByteDataReader();
- reader.add(byteArray);
- int action = reader.readUint8();
- int t = reader.readUint16();
- print("action: $action ");
- actionNotifier.value = action;
- }
- void _parseQuery(Uint8List byteArray) {
- ByteDataReader reader = ByteDataReader();
- reader.add(byteArray);
- int cmd = reader.readUint8();
- switch (cmd) {
- case 0x00: // 设备基本信息
- Map<String, dynamic> info = {};
- List<int> name = [];
- for (var i = 0; i < 64; i++) {
- name.add(reader.readUint8());
- }
- info['name'] = String.fromCharCodes(name);
- info['softwareVer'] = "${reader.readUint8()}.${reader.readUint8()}";
- info['hardwareVer'] = "${reader.readUint8()}.${reader.readUint8()}";
- infoNotifier.value = info;
- break;
- case 0x01: // 设备数据(左鞋,右鞋)
- Map<String, dynamic> info = {};
- for (var i = 0; i < 2; i++) {
- info['${i}_electricity'] = reader.readUint8();
- info['${i}_endurance'] = reader.readUint8();
- info['${i}_temperature'] = reader.readUint8();
- info['${i}_pressure'] = reader.readUint(4);
- }
- // [170, 187, 204, 23, 232, 0, 161, 1, 50, 0, 33, 0, 0, 156, 41, 51, 0, 34, 0, 0, 150, 19, 232]
- dataNotifier.value = info;
- electricityNotifier.value = min(info['0_electricity'] ?? 0, info['1_electricity'] ?? 0);
- break;
- case 0x02: // 查询步数
- if (reader.remainingLength == 1) {
- print("step end ... ");
- print(_partInfo?.items);
- return;
- }
- int serial = reader.readUint8();
- if (serial == 0) {
- int start = reader.readUint64();
- int serialCount = reader.readUint8();
- print("step start $start $serial $serialCount");
- if (serialCount > 0) _partInfo = new PartInfo(start ~/ 1000, serialCount);
- } else {
- if (_partInfo != null) {
- while (reader.remainingLength > 0) {
- int step = reader.readUint16();
- int distance = reader.readUint16();
- _partInfo.add(step, distance);
- print("step read $serial --> ${reader.offsetInBytes} ... ${reader.remainingLength} = info: $step $distance");
- }
- }
- // _partInfo.items.forEach((element) { print(" ---- read ${element.step} ${element.distance}");});
- ByteDataWriter writer = ByteDataWriter();
- writer.writeUint8(0x02);
- writer.writeUint8(serial);
- write(0xA1, writer.toBytes());
- if (_partInfo != null && serial > 0 && _partInfo.serialCount == serial) {
- print("step end ... ");
- _saveInfo(_partInfo);
- stepTotalNotifier.value = _partInfo.items.map((e) => e.step).reduce((value, element) => value + element);
- _partInfo = null;
- }
- }
- break;
- case 0x03: // 步数回调
- int step = reader.readUint8();
- stepNotifier.value = stepNotifier.value + step;
- break;
- }
- }
- Future _saveInfo(PartInfo partInfo) async {
- if (partInfo?.items?.isEmpty == true) return;
- List<PartItem> _items = [];
- for (var item in partInfo.items) {
- if (item.step == 0 && item.distance == 0) continue;
- _items.add(item);
- }
- await StepDB().insertAll(_items);
- await uploadInfo();
- _queryController.add(true);
- }
- Future uploadInfo() async {
- for (var i = 0; i < 100; i++) {
- var list = await StepDB().find();
- if (list.isEmpty) break;
- List<List<int>> data = List();
- int last = 0;
- int step = 0;
- for (var item in list) {
- last = item['time'];
- data.add([item['st'], item['di'], item['time']]);
- step += item['st'];
- }
- if (step > 5) {
- await api.addDaily(data: json.encode(data));
- await StepDB().delete(last);
- }
- }
- // Directory appDocDir = await getApplicationDocumentsDirectory();
- // String appDocPath = appDocDir.path;
- // Directory saveDir = Directory("$appDocPath/step");
- // var files = saveDir.listSync();
- // for(var file in files){
- // var f= new File(file.path);
- // var content = f.readAsStringSync();
- // await api.addDaily(data: content);
- // file.delete();
- // }
- }
- Future syncDeviceInfo() async {
- DateTime d = DateTime.now();
- int now = d.millisecondsSinceEpoch;
- ByteDataWriter writer = ByteDataWriter();
- writer.writeUint8(0x00);
- writer.writeUint64(now);
- await write(0xA0, writer.toBytes());
- }
- // 查询 设备基本信息
- Future queryDeviceInfo() async {
- await write(0xA1, Uint8List.fromList([0x00]));
- }
- // 查询 设备数据
- Future queryDeviceData() async {
- await write(0xA1, Uint8List.fromList([0x01]));
- }
- // 查询 查询步数
- Future queryDeviceStep() async {
- // await Future.delayed(Duration(seconds: 3));
- // _queryController.add(true);
- if (_partInfo != null) return;
- await write(0xA1, createTime(0x02));
- }
- Future setupGameMode(bool mode) async {
- await write(0xA2, mode? Uint8List.fromList([0x01]): Uint8List.fromList([0x00]));
- }
- Uint8List createTime(int cmd) {
- DateTime now = DateTime.now();
- DateTime offset = DateTime(now.year, now.month, now.day, now.hour);
- print("create now .. $now - $offset");
- ByteDataWriter writer = ByteDataWriter();
- writer.writeUint8(cmd);
- writer.writeUint8(0);
- writer.writeUint64(offset.millisecondsSinceEpoch);
- writer.writeUint8(max(0, min(60, now.minute - offset.minute)));
- return writer.toBytes();
- }
- Future write(int cmd, Uint8List data) async {
- BluetoothDevice _device = device;
- if (_device == null) return;
- if (_writeCharacteristic == null) {
- await discoverServices(_device);
- }
- if (_writeCharacteristic == null) return;
- int length = data.length + 5;
- ByteDataWriter writer = ByteDataWriter();
- writer.writeUint8(0xAA);
- // writer.writeUint8(0xBB);
- // writer.writeUint8(0xCC);
- writer.writeUint8(length);
- writer.writeUint8(0xFF - length);
- // writer.writeUint8(0x00);
- writer.writeUint8(cmd);
- if (data.isNotEmpty) writer.write(data);
- int ver = writer.toBytes().reduce((value, element) => value + element);
- writer.writeUint8(ver);
- Uint8List out = writer.toBytes();
- print("out $out");
- await _writeCharacteristic?.write(out, withoutResponse: true);
- }
- }
- class PartItem {
- int step;
- int distance;
- int time;
- PartItem(this.step, this.distance, this.time);
- Map<String, dynamic> toJson() {
- final Map<String, dynamic> data = new Map<String, dynamic>();
- data['step'] = this.step;
- data['distance'] = this.distance;
- data['time'] = this.time;
- return data;
- }
- }
- class PartInfo {
- int start;
- int serialCount;
- List<PartItem> items = [];
- PartInfo(this.start, this.serialCount);
- void add(int step, int distance) {
- items.add(PartItem(step, distance, start + items.length * 60 * 60));
- }
- }
|