// Copyright (C) 2025, uvok cheetah // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . import 'dart:async'; import 'package:logger/logger.dart'; import 'package:universal_ble/universal_ble.dart'; import 'package:uvok_epaper_badge/control/scanner_controller.dart'; import 'package:uvok_epaper_badge/control/scanner_controller_impl.dart'; import 'package:uvok_epaper_badge/extensions/list_ext.dart'; import 'package:uvok_epaper_badge/model/device/universal_ble_device.dart'; Logger logger = Logger(); class UniversalBleScannerController extends ScannerControllerImpl { StreamSubscription? _subScan; StreamSubscription? _subAvail; final List _devices = []; final int? rssiLimit; UniversalBleScannerController({this.rssiLimit}) { // fuck this limitation, I want an instance method to be called, which doesn't // work in an initializer. _subScan = UniversalBle.scanStream .where((d) => rssiLimit == null || (d.rssi ?? 0) > (rssiLimit!)) .listen(_newDeviceAction); _subAvail = UniversalBle.availabilityStream.listen(_newAvailabilityAction); } void _newDeviceAction(BleDevice dev) { //logger.i("Found device: ${dev.toString()}"); bool added = _devices.addIf(dev, (exDev) => exDev.deviceId != dev.deviceId); if (added) { super.setDevices( _devices .map((d) => UniversalBleDevice.fromDevice(d)) .toList(growable: false), ); } } void _newAvailabilityAction(AvailabilityState event) { super.setAvailability( event == AvailabilityState.poweredOn ? ScanAvailability.available : ScanAvailability.unavailable, ); } @override Future startScan({ Duration timeout = const Duration(seconds: 5), }) async { AvailabilityState state = await UniversalBle.getBluetoothAvailabilityState(); if (state != AvailabilityState.poweredOn) return; _devices.clear(); super.setDevices([]); super.setStatus(ScanStatus.scanning); await UniversalBle.startScan(); await Future.delayed(timeout); await UniversalBle.stopScan(); super.setStatus(ScanStatus.finished); logger.i("Found ${_devices.length} devices"); } @override Future stopScan() async { await UniversalBle.stopScan(); } @override void dispose() { super.dispose(); stopScan().ignore(); _subScan?.cancel().ignore(); _subAvail?.cancel().ignore(); } }