Skip to content

Commit

Permalink
take direct rfcomm away from headphones impl
Browse files Browse the repository at this point in the history
  • Loading branch information
TheLastGimbus committed Aug 10, 2024
1 parent 223047f commit 973ae1e
Show file tree
Hide file tree
Showing 3 changed files with 86 additions and 79 deletions.
3 changes: 2 additions & 1 deletion lib/headphones/cubit/model_matching.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import '../huawei/freebuds3i_impl.dart';
import '../huawei/freebuds4i.dart';
import '../huawei/freebuds4i_impl.dart';
import '../huawei/freebuds4i_sim.dart';
import '../huawei/mbb.dart';

typedef HeadphonesBuilder = BluetoothHeadphones Function(
StreamChannel<Uint8List> io, BluetoothDevice device);
Expand All @@ -22,7 +23,7 @@ MatchedModel? matchModel(BluetoothDevice matchedDevice) {
final name = matchedDevice.name.value;
return switch (name) {
_ when HuaweiFreeBuds4i.idNameRegex.hasMatch(name) => (
builder: (io, dev) => HuaweiFreeBuds4iImpl(io, dev),
builder: (io, dev) => HuaweiFreeBuds4iImpl(mbbChannel(io), dev),
placeholder: const HuaweiFreeBuds4iSimPlaceholder(),
) as MatchedModel,
_ when HuaweiFreeBuds3i.idNameRegex.hasMatch(name) => (
Expand Down
93 changes: 36 additions & 57 deletions lib/headphones/huawei/freebuds4i_impl.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import 'dart:async';
import 'dart:math';
import 'dart:typed_data';

import 'package:collection/collection.dart';
import 'package:rxdart/rxdart.dart';
Expand All @@ -18,7 +17,7 @@ final class HuaweiFreeBuds4iImpl extends HuaweiFreeBuds4i {
final tlb.BluetoothDevice _bluetoothDevice;

/// Bluetooth serial port that we communicate over
final StreamChannel<Uint8List> _rfcomm;
final StreamChannel<MbbCommand> _mbb;

// * stream controllers
final _batteryLevelCtrl = BehaviorSubject<int>();
Expand All @@ -33,40 +32,26 @@ final class HuaweiFreeBuds4iImpl extends HuaweiFreeBuds4i {
/// This watches if we are still missing any info and re-requests it
late StreamSubscription _watchdogStreamSub;

HuaweiFreeBuds4iImpl(this._rfcomm, this._bluetoothDevice) {
HuaweiFreeBuds4iImpl(this._mbb, this._bluetoothDevice) {
// hope this will nicely play with closing, idk honestly
final aliasStreamSub = _bluetoothDevice.alias
.listen((alias) => _bluetoothAliasCtrl.add(alias));
_bluetoothAliasCtrl.onCancel = () => aliasStreamSub.cancel();

_rfcomm.stream.listen((event) {
List<MbbCommand>? commands;
try {
commands = MbbCommand.fromPayload(event);
} catch (e, s) {
logg.e("mbb parsing error", error: e, stackTrace: s);
}
for (final cmd in commands ?? <MbbCommand>[]) {
// FILTER THE SHIT OUT
if (cmd.serviceId == 10 && cmd.commandId == 13) return;
try {
_evalMbbCommand(cmd);
} on RangeError catch (e, s) {
logg.e('Error while parsing mbb cmd - (probably missing bytes)',
error: e, stackTrace: s);
}
}
}, onDone: () {
// close all streams
_batteryLevelCtrl.close();
_bluetoothAliasCtrl.close();
_bluetoothNameCtrl.close();
_lrcBatteryCtrl.close();
_ancModeCtrl.close();
_settingsCtrl.close();

_watchdogStreamSub.cancel();
});
_mbb.stream.listen(
_evalMbbCommand,
onDone: () {
// close all streams
_batteryLevelCtrl.close();
_bluetoothAliasCtrl.close();
_bluetoothNameCtrl.close();
_lrcBatteryCtrl.close();
_ancModeCtrl.close();
_settingsCtrl.close();

_watchdogStreamSub.cancel();
},
);
_initRequestInfo();
_watchdogStreamSub =
Stream.periodic(const Duration(seconds: 3)).listen((_) {
Expand Down Expand Up @@ -144,18 +129,12 @@ final class HuaweiFreeBuds4iImpl extends HuaweiFreeBuds4i {
}

Future<void> _initRequestInfo() async {
await _sendMbb(_Cmd.getBattery);
await _sendMbb(_Cmd.getAnc);
await _sendMbb(_Cmd.getAutoPause);
await _sendMbb(_Cmd.getGestureDoubleTap);
await _sendMbb(_Cmd.getGestureHold);
await _sendMbb(_Cmd.getGestureHoldToggledAncModes);
}

// TODO: some .flush() for this
Future<void> _sendMbb(MbbCommand comm) async {
logg.t("⬆ Sending mbb cmd: $comm");
_rfcomm.sink.add(comm.toPayload());
_mbb.sink.add(_Cmd.getBattery);
_mbb.sink.add(_Cmd.getAnc);
_mbb.sink.add(_Cmd.getAutoPause);
_mbb.sink.add(_Cmd.getGestureDoubleTap);
_mbb.sink.add(_Cmd.getGestureHold);
_mbb.sink.add(_Cmd.getGestureHoldToggledAncModes);
}

// TODO: Get this from basic bluetooth object (when we actually have those)
Expand Down Expand Up @@ -186,7 +165,7 @@ final class HuaweiFreeBuds4iImpl extends HuaweiFreeBuds4i {
ValueStream<AncMode> get ancMode => _ancModeCtrl.stream;

@override
Future<void> setAncMode(AncMode mode) => _sendMbb(_Cmd.anc(mode));
Future<void> setAncMode(AncMode mode) async => _mbb.sink.add(_Cmd.anc(mode));

@override
ValueStream<HuaweiFreeBuds4iSettings> get settings => _settingsCtrl.stream;
Expand All @@ -200,30 +179,30 @@ final class HuaweiFreeBuds4iImpl extends HuaweiFreeBuds4i {
// or make some other abstraction for it - maybe some day
if ((newSettings.doubleTapLeft ?? prev.doubleTapLeft) !=
prev.doubleTapLeft) {
await _sendMbb(_Cmd.gestureDoubleTapLeft(newSettings.doubleTapLeft!));
await _sendMbb(_Cmd.getGestureDoubleTap);
_mbb.sink.add(_Cmd.gestureDoubleTapLeft(newSettings.doubleTapLeft!));
_mbb.sink.add(_Cmd.getGestureDoubleTap);
}
if ((newSettings.doubleTapRight ?? prev.doubleTapRight) !=
prev.doubleTapRight) {
await _sendMbb(_Cmd.gestureDoubleTapRight(newSettings.doubleTapRight!));
await _sendMbb(_Cmd.getGestureDoubleTap);
_mbb.sink.add(_Cmd.gestureDoubleTapRight(newSettings.doubleTapRight!));
_mbb.sink.add(_Cmd.getGestureDoubleTap);
}
if ((newSettings.holdBoth ?? prev.holdBoth) != prev.holdBoth) {
await _sendMbb(_Cmd.gestureHold(newSettings.holdBoth!));
await _sendMbb(_Cmd.getGestureHold);
await _sendMbb(_Cmd.getGestureHoldToggledAncModes);
_mbb.sink.add(_Cmd.gestureHold(newSettings.holdBoth!));
_mbb.sink.add(_Cmd.getGestureHold);
_mbb.sink.add(_Cmd.getGestureHoldToggledAncModes);
}
if ((newSettings.holdBothToggledAncModes ?? prev.holdBothToggledAncModes) !=
prev.holdBothToggledAncModes) {
await _sendMbb(_Cmd.gestureHoldToggledAncModes(
_mbb.sink.add(_Cmd.gestureHoldToggledAncModes(
newSettings.holdBothToggledAncModes!));
await _sendMbb(_Cmd.getGestureHold);
await _sendMbb(_Cmd.getGestureHoldToggledAncModes);
_mbb.sink.add(_Cmd.getGestureHold);
_mbb.sink.add(_Cmd.getGestureHoldToggledAncModes);
}
if ((newSettings.autoPause ?? prev.autoPause) != prev.autoPause) {
await _sendMbb(
newSettings.autoPause! ? _Cmd.autoPauseOn : _Cmd.autoPauseOff);
await _sendMbb(_Cmd.getAutoPause);
_mbb.sink
.add(newSettings.autoPause! ? _Cmd.autoPauseOn : _Cmd.autoPauseOff);
_mbb.sink.add(_Cmd.getAutoPause);
}
}
}
Expand Down
69 changes: 48 additions & 21 deletions lib/headphones/huawei/mbb.dart
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
import 'dart:async';
import 'dart:typed_data';

import 'package:async/async.dart';
import 'package:collection/collection.dart';
import 'package:crclib/catalog.dart';
import 'package:stream_channel/stream_channel.dart';

import '../../logger.dart';
import '../_old/headphones_data_objects.dart';

/// Helper class for Mbb protocol used to communicate with headphones
Expand Down Expand Up @@ -155,12 +159,34 @@ class MbbCommand {
}
}

StreamChannel<MbbCommand> mbbChannel(StreamChannel<Uint8List> rfcomm) =>
rfcomm.transform(
StreamChannelTransformer(
StreamTransformer.fromHandlers(
handleData: (data, stream) {
try {
for (final cmd in MbbCommand.fromPayload(data)) {
// FILTER THE SHIT OUT
if (cmd.serviceId == 10 && cmd.commandId == 13) continue;
stream.add(cmd);
}
} catch (e, s) {
logg.e("mbb parsing error", error: e, stackTrace: s);
}
},
),
StreamSinkTransformer.fromHandlers(
handleData: (data, sink) => rfcomm.sink.add(data.toPayload()),
),
),
);

mixin mbbTools {
fromMbbValue(int mbbValue, Map commandMap)=>
commandMap.keys.firstWhere((k) => commandMap[k] == mbbValue);
fromMbbValue(int mbbValue, Map commandMap) =>
commandMap.keys.firstWhere((k) => commandMap[k] == mbbValue);
}

abstract class GenericHeadphoneCommands with mbbTools{
abstract class GenericHeadphoneCommands with mbbTools {
Map<HeadphonesGestureDoubleTap, int> get doubleTapCommands;

Map<HeadphonesGestureHold, int> get holdCommands;
Expand Down Expand Up @@ -265,8 +291,7 @@ class Freebuds3iCommands extends GenericHeadphoneCommands {
var requestGestureHold = const MbbCommand(43, 23, {});

@override
dynamic gestureHold(HeadphonesGestureHold gestureHold) =>
MbbCommand(43, 22, {
dynamic gestureHold(HeadphonesGestureHold gestureHold) => MbbCommand(43, 22, {
2: [holdCommands[gestureHold]!],
});

Expand All @@ -279,8 +304,7 @@ class Freebuds3iCommands extends GenericHeadphoneCommands {
const se = SetEquality();
if (![2, 3].contains(toggledModes.length)) {
throw Exception(
"toggledModes must have 2 or 3 elements, not ${toggledModes
.length}}");
"toggledModes must have 2 or 3 elements, not ${toggledModes.length}}");
}
if (toggledModes.length == 3) mbbValue = 5;
if (se.equals(
Expand Down Expand Up @@ -423,20 +447,23 @@ class Freebuds4iCommands extends GenericHeadphoneCommands {

@override
Set<HeadphonesAncMode> gestureHoldFromMbbValue(int mbbValue) {
switch (mbbValue) {
case 2:
return HeadphonesAncMode.values.toSet();
case 3:
return const {HeadphonesAncMode.off, HeadphonesAncMode.noiseCancel};
case 5:
return const {HeadphonesAncMode.off, HeadphonesAncMode.awareness};
case 6:
return const {HeadphonesAncMode.noiseCancel, HeadphonesAncMode.awareness};
case 255:
return {};
default:
throw Exception("Unknown mbbValue for $mbbValue");
}
switch (mbbValue) {
case 2:
return HeadphonesAncMode.values.toSet();
case 3:
return const {HeadphonesAncMode.off, HeadphonesAncMode.noiseCancel};
case 5:
return const {HeadphonesAncMode.off, HeadphonesAncMode.awareness};
case 6:
return const {
HeadphonesAncMode.noiseCancel,
HeadphonesAncMode.awareness
};
case 255:
return {};
default:
throw Exception("Unknown mbbValue for $mbbValue");
}
}
}

Expand Down

0 comments on commit 973ae1e

Please sign in to comment.