PhilipsHue / flutter_reactive_ble

Flutter library that handles BLE operations for multiple devices.
https://developers.meethue.com/
Other
661 stars 321 forks source link

Connection problem Samsung S20 FE #779

Open Berkant35 opened 1 year ago

Berkant35 commented 1 year ago

Hello, we have a mobile application, and this mobile app is designed to control multiple electronic devices over BLE. I'm experiencing some issues with this situation. While it works flawlessly with 6 devices that I want to connect to, it doesn't work properly on the Samsung S20 I've indicated below. As seen in the video, our customer can connect to 4 devices initially but fails to connect to 2 of them. However, when the application is closed and everything is refreshed, it can connect to all 6 devices seamlessly. Do you have any suggestions for a solution to this?

import 'dart:async';

import 'package:flutter/material.dart'; import 'package:flutter_reactive_ble/flutter_reactive_ble.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart';

import '../catchpad_flutter_lib_init.dart'; import '../provs/ble_prov.dart'; import 'device_model.dart'; import 'reactive_state.dart';

class BleScanner implements ReactiveState { BleScanner(this.ref) : _ble = ref.watch(bleProv); final Ref ref;

final FlutterReactiveBle _ble;

StreamController? _stateStreamController; bool _pushedStateOnce = true;

StreamSubscription? _subscription;

var _devices = {}; final _lastdevices = {};

@override Stream get state { return _stateStreamController!.stream; }

void updateDeviceInfo(DeviceModel newDev) { final d = {};

for (var dev in _devices) {
  if (dev.id == newDev.id) {

    d.add(newDev);
  } else {
    d.add(dev);
  }
}
_devices.clear();
_devices = Set<DeviceModel>.from(d);

_pushState();

}

void deleteDevices(List deadList) async { pauseScan(); _lastdevices.clear(); for (var deadId in deadList) { if (_devices.map((e) => e.id).contains(deadId)) { _devices.removeWhere((element) => element.id == deadId); } } _pushState(); resumeScan(); }

void refreshScan({required List connectedDevices}) async { final lastdevices = Set.from(_lastdevices); for (var condev in connectedDevices) { lastdevices.removeWhere((lastdev) => lastdev.id == condev.id); } final orgdevs = Set.from(_devices); final intersections = lastdevices.intersection(orgdevs); for (var intersec in intersections) { if (!lastdevices.any((lastdev) => lastdev.id == intersec.id)) { _devices.remove(intersec); } } / if (connectedDevices.isNotEmpty) { debugPrint('${connectedDevices.map((e) => e.name)}'); for (var condev in connectedDevices) { lastdevices } lastdevices.removeWhere((lastdev) => connectedDevices .any((connecteddev) => lastdev.id != connecteddev.id)); } if (_devices.isNotEmpty) { final orgdevs = _devices.toList(); for (var orgdev in orgdevs) { if (!lastdevices.map((e) => e.id).contains(orgdev.id)) { _devices.removeWhere((dev) => dev.id == orgdev.id); } } } /

_pushState();
debugPrint('${_devices.map((e) => e.name)}');
debugPrint('${lastdevices.map((e) => e.name)}');
_lastdevices.clear();

}

void hardRefreshScan( {required List connectedDevices}) async { pauseScan();

for (var condev in connectedDevices) {
  if (!_devices.map((e) => e.id).contains(condev.id)) {
    _devices.remove(condev);
  }
}
debugPrint('${_devices.map((e) => e.name)}');
debugPrint('${_lastdevices.map((e) => e.name)}');

_pushState();
resumeScan();

}

void pauseScan() { if (_subscription != null) { _subscription?.pause(); } }

void resumeScan() { if (_subscription != null) { _subscription?.resume(); } }

void startScan() async { init(); logger.i('Start ble discovery'); _subscription = _ble.scanForDevices( withServices: [], requireLocationServicesEnabled: true, // serviceUuids, ).listen( (device) { // if (!device.isCPDevice) return;

    // dont add already added devices
    if (device.isCPDevice) {
      _lastdevices.add(device);
      if (_devices.any((element) => element.id == device.id)) {
        return;
      }
      _devices.add(device);
      if (_pushedStateOnce) {
        _pushState();
        Future.delayed(const Duration(seconds: 3))
            .then((value) => _pushedStateOnce = false);
      }
    }
  },
  onError: (Object e) => logger.e(
    'Device scan fails with error: $e',
  ),
  onDone: () {
    logger.d('Device scan is done.');
  },
);

_pushState();

//await Future.delayed(scanDuration);

//stopScan();

}

void _pushState() { if (!isClosed) { _stateStreamController!.add( BleScannerState( deviceModels: _devices, scanIsInProgress: _subscription != null, ), ); } }

bool get isClosed => _stateStreamController == null || _stateStreamController!.isClosed;

Future stopScan() async { logger.i('Stop ble discovery');

if (_subscription != null) {
  await _subscription?.cancel();
  _subscription = null;
}

if (!isClosed) {
  await dispose();
}

}

Future dispose() async { _pushState();

// I've commented out these lines, because
// as there is a widgdt listening to the
// this stream, if we reinitialize the
// stream when the user pressed scan
// button, the user's stream reference
// will stay fixed on the old instance
// of the stream. idk if this makes sense
// to you, this is the best I can explain.
// but also, here's a small demonstration:
//
// WidgetA => listening to
// a = _stateStreamController.stream
// when we close and nullify
// _stateStreamController,
// WidgetA will stay fixed on a and not
// listen to the new stream.
// this is exactly the case in
// lib/ui/device/device_list.dart _DevList
// Widget.
//
// if you did not understand yet, don't
// worry, just leave this as is and it would
// work just fine for the eternity of this
// codebase.
//
// await _stateStreamController?.close();
// _stateStreamController = null;

}

void init() { _stateStreamController ??= StreamController(); } }

@immutable class BleScannerState { const BleScannerState({ required Set deviceModels, required this.scanIsInProgress, }) : _deviceModels = deviceModels;

final Set _deviceModels; final bool scanIsInProgress;

List get deviceModels => _deviceModels.where((element) => element.isCPDevice).toList(); }

pubspec.yaml

name: catchpad publish_to: "none" version: 1.0.22+44 environment: sdk: ^3.0.5

dependencies: flutter: sdk: flutter flutter_localizations: sdk: flutter

intl: ^0.18.0 cupertino_icons: ^1.0.2 freezed_annotation: "2.2.0" flutter_downloader: ^1.10.2 badges: ^2.0.3 json_annotation: "4.7.0" auto_size_text: ^3.0.0 fl_chart: ^0.63.0 awesome_dialog: ^3.0.2 path_provider: ^2.0.13 get_it: ^7.2.0 firebase_core: ^2.1.1 cloud_firestore: ^4.0.3 firebase_auth: ^4.1.0 file_picker: ^5.2.4 flutter_keyboard_visibility: ^5.2.0 shared_preferences: ^2.0.15 connectivity_plus: ^3.0.2 flutter_animation_progress_bar: ^2.3.1 rotated_corner_decoration: ^2.1.0+1 flutter_slidable: ^2.0.0 duration: ^3.0.12

validation

regexpattern: ^2.5.0

context

kartal: ^3.2.0

svg

flutter_svg: ^1.1.6 lottie: ^2.3.2

battery_for_start_game_condition

battery_plus: ^4.0.0

adnan

life_saver_extensions: git: url: git@github.com:adnanjpg/life_saver_extensions.git loading: git: url: git@github.com:adnanjpg/loading.git paginationer: git: url: git@github.com:adnanjpg/paginationer.git

kaan

animated_search_bar: git: url: git@github.com:trailon/flutter_animated_search_bar.git

using the stabl release produces an error.

https://github.com/rrousselGit/river_pod/issues/815#issuecomment-1019378470

flutter_riverpod: ^2.0.2 flutter_easyloading: ^3.0.5 rflutter_alert: ^2.0.4 permission_handler: ^9.2.0 back_button_interceptor: ^6.0.1 enum_to_string: ^2.0.1 xrandom: ^0.7.0+1 flutter_sound: ^9.1.3 logger: ^1.1.0

logger_flutter:

# the official version is so old that it
# does not support null safety 🤓
# git:
# url: git@github.com:trailon/logger_flutter.git

flutter_colorpicker: ^1.0.3 modal_bottom_sheet: ^2.0.1 sms_autofill: ^2.2.0 adaptive_dialog: ^1.6.3

flutter_rating_stars: 1.0.3+4

flutter_midi_pro: ^1.0.5

percent_indicator: ^4.2.3

catchpad

catchpad_flutter_lib:

path: /Users/adnanfahed/catchpad_flutter_lib.git/main

git:
  url: git@github.com:catchpad/catchpad_flutter_lib.git

streamer:

path: /Users/adnanfahed/streamer

git:
  url: git@github.com:catchpad/streamer.git

url_launcher: ^6.1.2 flutter_time_picker_spinner: ^2.0.0 numberpicker: ^2.1.1 string_to_hex: ^0.2.2 firebase_crashlytics: ^3.1.2

location: ^4.2.0 nanoid: ^1.0.0 platform_device_id: ^1.0.1 device_information: ^0.0.4 safe_device: ^1.1.4

dev_dependencies: flutter_test: sdk: flutter flutter_lints: ^1.0.0 build_runner: ^2.4.6 freezed: "2.2.1" json_serializable: "6.5.4" flutter_launcher_icons: ^0.10.0 flutter_native_splash: ^2.2.12 graphic: ^2.2.0 font_awesome_flutter: ^10.1.0 package_info_plus: ^4.0.2

flutter_icons: android: "launcher_icon" ios: false image_path: "assets/app_icon/app_icon.png" adaptive_icon_background: "#191819" adaptive_icon_foreground: "assets/app_icon/app_icon_big.png"

flutter_native_splash: background_image: assets/app_icon/cpsplash.png fullscreen: true android_12:

App icon background color.

icon_background_color: "#1b1819"

# Splash screen background color.
color: "#1b1819"
# App icon foreground image.
image: assets/app_icon/app_icon_big.png

web: false

flutter: generate: true uses-material-design: true assets:

flutter doctor Please commit your changes or stash them before you merge. Aborting berkantcalikusu@Berkants-MacBook-Pro catchpad % flutter --version
Flutter 3.10.5 • channel stable • https://github.com/flutter/flutter.git Framework • revision 796c8ef792 (8 weeks ago) • 2023-06-13 15:51:02 -0700 Engine • revision 45f6e00911 Tools • Dart 3.0.5 • DevTools 2.23.1 berkantcalikusu@Berkants-MacBook-Pro catchpad % flutter doctor Doctor summary (to see all details, run flutter doctor -v): [✓] Flutter (Channel stable, 3.10.5, on macOS 13.4.1 22F770820d darwin-arm64, locale en-TR) [✓] Android toolchain - develop for Android devices (Android SDK version 33.0.0-rc2) [✓] Xcode - develop for iOS and macOS (Xcode 14.3.1) [✓] Chrome - develop for the web [✓] Android Studio (version 2021.2) [✓] Android Studio (version 2022.2) [✓] IntelliJ IDEA Community Edition (version 2022.2.4) [✓] VS Code (version 1.80.1) [✓] Connected device (3 available) [✓] Network resources

remonh87 commented 1 year ago

did you try using a bluetooth explorer app like Nrfconnect and try to connect to the devices. Also did you receive any errors in logcat?

I would be very cautious with connecting a lot of ble devices at the same time because the OS can always decide to disconnect because it needs the bandwidth for other connections (WIFI or BLE).

Berkant35 commented 1 year ago

I didn't. Because this problem occurred on the customer's end, and this phone is located remotely from me. I am already working with my real devices smoothly.

"I would be very cautious with connecting a lot of ble devices at the same time because the OS can always decide to disconnect because it needs the bandwidth for other connections (WIFI or BLE)."

Thank you for this advice. How can I manage to fix it? Should I add a delay or try something else?