cleanup, proxy work

This commit is contained in:
Jonas Bark
2026-02-01 16:33:03 +01:00
parent 40a0eae187
commit fd4e5f5ce8
8 changed files with 128 additions and 44 deletions

View File

@@ -101,7 +101,7 @@ jobs:
with:
platform: android
release-version: latest
args: '--allow-asset-diffs --allow-native-diffs -- --dart-define=REVENUECAT_API_KEY_ANDROID=${{ secrets.REVENUECAT_API_KEY_ANDROID }}'
args: '--allow-asset-diffs --allow-native-diffs -- --obfuscate --split-debug-info=symbols --dart-define=REVENUECAT_API_KEY_ANDROID=${{ secrets.REVENUECAT_API_KEY_ANDROID }}'
- name: 🚀 Shorebird Patch iOS
uses: shorebirdtech/shorebird-patch@v1
@@ -187,4 +187,4 @@ jobs:
with:
platform: windows
release-version: latest
args: '--allow-asset-diffs --allow-native-diffs'
args: '--allow-asset-diffs --allow-native-diffs -- --obfuscate --split-debug-info=symbols'

View File

@@ -94,26 +94,36 @@ class Connection {
if (_lastScanResult.none((e) => e.deviceId == result.deviceId && e.services.contentEquals(result.services))) {
_lastScanResult.add(result);
if (false) {
debugPrint('Scan result: ${result.name} - ${result.deviceId}');
if (kDebugMode) {
debugPrint('Scan result: ${result.name} - ${result.deviceId} - Services: ${result.services}');
}
final scanResult = BluetoothDevice.fromScanResult(result);
try {
final scanResult = BluetoothDevice.fromScanResult(result);
if (scanResult != null) {
_actionStreams.add(
LogNotification('Found new device: ${kIsWeb ? scanResult.toString() : scanResult.runtimeType}'),
);
addDevices([scanResult]);
} else {
final manufacturerData = result.manufacturerDataList;
final data = manufacturerData
.firstOrNullWhere((e) => e.companyId == ZwiftConstants.ZWIFT_MANUFACTURER_ID)
?.payload;
if (data != null && kDebugMode) {
if (scanResult != null) {
_actionStreams.add(
LogNotification('Found unknown device ${result.name} with identifier: ${data.firstOrNull}'),
LogNotification('Found new device: ${kIsWeb ? scanResult.toString() : scanResult.runtimeType}'),
);
addDevices([scanResult]);
} else {
final manufacturerData = result.manufacturerDataList;
final data = manufacturerData
.firstOrNullWhere((e) => e.companyId == ZwiftConstants.ZWIFT_MANUFACTURER_ID)
?.payload;
if (data != null && kDebugMode) {
_actionStreams.add(
LogNotification('Found unknown device ${result.name} with identifier: ${data.firstOrNull}'),
);
}
}
} catch (e, backtrace) {
_actionStreams.add(
LogNotification("Error processing scan result for device ${result.deviceId}: $e\n$backtrace"),
);
if (kDebugMode) {
print(e);
print("backtrace: $backtrace");
}
}
}

View File

@@ -3,6 +3,7 @@ import 'dart:async';
import 'package:bike_control/bluetooth/ble.dart';
import 'package:bike_control/bluetooth/devices/base_device.dart';
import 'package:bike_control/bluetooth/devices/openbikecontrol/openbikecontrol_device.dart';
import 'package:bike_control/bluetooth/devices/proxy/proxy_device.dart';
import 'package:bike_control/bluetooth/devices/shimano/shimano_di2.dart';
import 'package:bike_control/bluetooth/devices/sram/sram_axs.dart';
import 'package:bike_control/bluetooth/devices/wahoo/wahoo_kickr_bike_pro.dart';
@@ -47,8 +48,8 @@ abstract class BluetoothDevice extends BaseDevice {
scanResult.name,
uniqueId: scanResult.deviceId,
availableButtons: allowMultiple
? availableButtons.map((b) => b.copyWith(sourceDeviceId: scanResult.deviceId)).toList()
: availableButtons,
? availableButtons.toList().map((b) => b.copyWith(sourceDeviceId: scanResult.deviceId)).toList()
: availableButtons.toList(),
isBeta: isBeta,
buttonPrefix: buttonPrefix,
) {
@@ -123,6 +124,7 @@ abstract class BluetoothDevice extends BaseDevice {
_ when scanResult.services.contains(ShimanoDi2Constants.SERVICE_UUID_ALTERNATIVE.toLowerCase()) => ShimanoDi2(
scanResult,
),
_ when scanResult.services.containsAny(ProxyDevice.proxyServiceUUIDs) && kDebugMode => ProxyDevice(scanResult),
_ when scanResult.services.contains(SramAxsConstants.SERVICE_UUID.toLowerCase()) => SramAxs(
scanResult,
),

View File

@@ -0,0 +1,66 @@
import 'dart:typed_data';
import 'package:bike_control/bluetooth/devices/bluetooth_device.dart';
import 'package:prop/emulators/ftms_emulator.dart';
import 'package:shadcn_flutter/shadcn_flutter.dart';
import 'package:universal_ble/universal_ble.dart';
class ProxyDevice extends BluetoothDevice {
static final List<String> proxyServiceUUIDs = [
'0000180d-0000-1000-8000-00805f9b34fb', // Heart Rate
'00001818-0000-1000-8000-00805f9b34fb', // Cycling Power
'00001826-0000-1000-8000-00805f9b34fb', // Fitness Machine
];
final FtmsEmulator emulator = FtmsEmulator();
ProxyDevice(super.scanResult)
: super(
availableButtons: const [],
isBeta: true,
);
late final List<BleService> services;
@override
Future<void> handleServices(List<BleService> services) async {
emulator.setScanResult(scanResult);
emulator.handleServices(services);
emulator.startServer();
}
@override
Future<void> processCharacteristic(String characteristic, Uint8List bytes) async {
emulator.processCharacteristic(characteristic, bytes);
}
@override
Widget showInformation(BuildContext context) {
return Column(
spacing: 16,
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
super.showInformation(context),
if (!isConnected)
Button.primary(
style: ButtonStyle.primary(size: ButtonSize.small),
onPressed: () {
super.connect();
},
child: Text('Proxy'),
),
],
);
}
@override
Future<void> connect() async {}
@override
Future<void> disconnect() {
emulator.stop();
return super.disconnect();
}
}

View File

@@ -12,7 +12,7 @@ import 'package:prop/prop.dart';
import 'package:shadcn_flutter/shadcn_flutter.dart';
import 'package:universal_ble/universal_ble.dart';
final FtmsEmulator emulator = FtmsEmulator();
final FtmsEmulator ftmsEmulator = FtmsEmulator();
class ZwiftClickV2 extends ZwiftRide {
ZwiftClickV2(super.scanResult)
@@ -31,7 +31,7 @@ class ZwiftClickV2 extends ZwiftRide {
ZwiftButtons.shiftUpRight,
],
) {
emulator.setScanResult(scanResult);
ftmsEmulator.setScanResult(scanResult);
}
@override
@@ -65,13 +65,13 @@ class ZwiftClickV2 extends ZwiftRide {
@override
Future<void> handleServices(List<BleService> services) async {
emulator.handleServices(services);
ftmsEmulator.handleServices(services);
await super.handleServices(services);
}
@override
Future<void> processCharacteristic(String characteristic, Uint8List bytes) async {
if (!emulator.processCharacteristic(characteristic, bytes)) {
if (!ftmsEmulator.processCharacteristic(characteristic, bytes)) {
await super.processCharacteristic(characteristic, bytes);
}
}

View File

@@ -36,7 +36,7 @@ class _UnlockPageState extends State<UnlockPage> with SingleTickerProviderStateM
void _isConnectedUpdate() {
setState(() {});
if (emulator.isUnlocked.value) {
if (ftmsEmulator.isUnlocked.value) {
_close();
}
}
@@ -47,8 +47,8 @@ class _UnlockPageState extends State<UnlockPage> with SingleTickerProviderStateM
_isInTrialPhase = !IAPManager.instance.isPurchased.value && IAPManager.instance.isTrialExpired;
_ticker = createTicker((_) {
if (emulator.waiting.value) {
final waitUntil = emulator.connectionDate!.add(Duration(minutes: 1));
if (ftmsEmulator.waiting.value) {
final waitUntil = ftmsEmulator.connectionDate!.add(Duration(minutes: 1));
final secondsUntil = waitUntil.difference(DateTime.now()).inSeconds;
if (mounted) {
@@ -79,13 +79,13 @@ class _UnlockPageState extends State<UnlockPage> with SingleTickerProviderStateM
core.settings.setObpMdnsEnabled(false);
}
emulator.isUnlocked.value = false;
emulator.alreadyUnlocked.value = false;
emulator.waiting.value = false;
emulator.isConnected.addListener(_isConnectedUpdate);
emulator.isUnlocked.addListener(_isConnectedUpdate);
emulator.alreadyUnlocked.addListener(_isConnectedUpdate);
emulator.startServer().then((_) {}).catchError((e, s) {
ftmsEmulator.isUnlocked.value = false;
ftmsEmulator.alreadyUnlocked.value = false;
ftmsEmulator.waiting.value = false;
ftmsEmulator.isConnected.addListener(_isConnectedUpdate);
ftmsEmulator.isUnlocked.addListener(_isConnectedUpdate);
ftmsEmulator.alreadyUnlocked.addListener(_isConnectedUpdate);
ftmsEmulator.startServer().then((_) {}).catchError((e, s) {
recordError(e, s, context: 'Emulator');
core.connection.signalNotification(AlertNotification(LogLevel.LOGLEVEL_ERROR, e.toString()));
});
@@ -96,10 +96,10 @@ class _UnlockPageState extends State<UnlockPage> with SingleTickerProviderStateM
void dispose() {
_ticker.dispose();
if (!_isInTrialPhase) {
emulator.isConnected.removeListener(_isConnectedUpdate);
emulator.isUnlocked.removeListener(_isConnectedUpdate);
emulator.alreadyUnlocked.removeListener(_isConnectedUpdate);
emulator.stop();
ftmsEmulator.isConnected.removeListener(_isConnectedUpdate);
ftmsEmulator.isUnlocked.removeListener(_isConnectedUpdate);
ftmsEmulator.alreadyUnlocked.removeListener(_isConnectedUpdate);
ftmsEmulator.stop();
if (_wasZwiftMdnsEmulatorActive) {
core.zwiftMdnsEmulator.startServer();
@@ -171,30 +171,30 @@ class _UnlockPageState extends State<UnlockPage> with SingleTickerProviderStateM
closeDrawer(context);
},
),
] else if (!emulator.isConnected.value) ...[
] else if (!ftmsEmulator.isConnected.value) ...[
Text(AppLocalizations.of(context).unlock_openZwift).li,
Text(AppLocalizations.of(context).unlock_connectToBikecontrol).li,
SizedBox(height: 32),
Text(AppLocalizations.of(context).unlock_bikecontrolAndZwiftNetwork).small,
] else if (emulator.alreadyUnlocked.value) ...[
] else if (ftmsEmulator.alreadyUnlocked.value) ...[
Text(AppLocalizations.of(context).unlock_yourZwiftClickMightBeUnlockedAlready),
SizedBox(height: 8),
Text(AppLocalizations.of(context).unlock_confirmByPressingAButtonOnYourDevice).small,
] else if (!emulator.isUnlocked.value)
] else if (!ftmsEmulator.isUnlocked.value)
Text(AppLocalizations.of(context).unlock_waitingForZwift)
else
Text('Zwift Click is unlocked! You can now close this page.'),
SizedBox(height: 32),
if (!_showManualSteps && !_isInTrialPhase) ...[
if (emulator.waiting.value && _secondsRemaining >= 0)
if (ftmsEmulator.waiting.value && _secondsRemaining >= 0)
Center(child: CircularProgressIndicator(value: 1 - (_secondsRemaining / 60), size: 20))
else if (emulator.alreadyUnlocked.value)
else if (ftmsEmulator.alreadyUnlocked.value)
Center(child: Icon(Icons.lock_clock))
else
SmallProgressIndicator(),
SizedBox(height: 20),
],
if (!emulator.isUnlocked.value && !_showManualSteps) ...[
if (!ftmsEmulator.isUnlocked.value && !_showManualSteps) ...[
if (!_isInTrialPhase) ...[
SizedBox(height: 32),
Center(child: Text(AppLocalizations.of(context).unlock_notWorking).small),

View File

@@ -143,6 +143,12 @@ class BKMenuButton extends StatelessWidget {
await core.settings.reset();
},
),
MenuButton(
child: Text('Disconnect'),
onPressed: (c) async {
core.connection.disconnectAll();
},
),
MenuDivider(),
],
if (currentPage == BCPage.logs) ...[

2
prop

Submodule prop updated: 2283bd5273...a2be5c54e7