attempt to fix #3

This commit is contained in:
Jonas Bark
2025-03-29 13:49:22 +01:00
parent 3ee38ee1e2
commit 0996506fd1
7 changed files with 188 additions and 10 deletions

View File

@@ -1,3 +1,6 @@
### 1.0.0+4 (2025-03-29)
- Zwift Ride: attempt to fix button parsing
### 1.0.0+3 (2025-03-29)
- Windows: fix connection by using a different Bluetooth stack (issue #1)

View File

@@ -15,6 +15,10 @@ class Constants {
static const RC1_LEFT_SIDE = 0x03;
static const RC1_RIGHT_SIDE = 0x02;
// Zwift Ride
static const RIDE_RIGHT_SIDE = 0x07;
static const RIDE_LEFT_SIDE = 0x08;
// Zwift Click = BC1
static const BC1 = 0x09;
@@ -33,7 +37,7 @@ class Constants {
// not figured out the protobuf type this really is, the content is just two varints.
static const int CLICK_NOTIFICATION_MESSAGE_TYPE = 55;
static const int PLAY_NOTIFICATION_MESSAGE_TYPE = 7;
static const int RIDE_NOTIFICATION_MESSAGE_TYPE = 35;
static const int RIDE_NOTIFICATION_MESSAGE_TYPE = 35; // 0x23
// see this if connected to Core then Zwift connects to it. just one byte
static const DISCONNECT_MESSAGE_TYPE = 0xFE;

View File

@@ -1,7 +1,7 @@
import 'package:accessibility/accessibility.dart';
import 'package:flutter/foundation.dart';
import 'package:swift_control/utils/devices/zwift_click.dart';
import 'package:swift_control/utils/messages/controller_notification.dart';
import 'package:swift_control/utils/messages/play_notification.dart';
import '../../main.dart';
import '../ble.dart';
@@ -9,14 +9,14 @@ import '../ble.dart';
class ZwiftPlay extends ZwiftClick {
ZwiftPlay(super.scanResult);
ControllerNotification? _lastControllerNotification;
PlayNotification? _lastControllerNotification;
@override
List<int> get startCommand => Constants.RIDE_ON + Constants.RESPONSE_START_PLAY;
@override
void processClickNotification(Uint8List message) {
final ControllerNotification clickNotification = ControllerNotification(message);
final PlayNotification clickNotification = PlayNotification(message);
if (_lastControllerNotification == null || _lastControllerNotification != clickNotification) {
_lastControllerNotification = clickNotification;
actionStreamInternal.add(clickNotification);

View File

@@ -1,8 +1,13 @@
import 'package:swift_control/utils/devices/zwift_play.dart';
import 'dart:typed_data';
import 'package:accessibility/accessibility.dart';
import 'package:swift_control/main.dart';
import 'package:swift_control/utils/devices/zwift_click.dart';
import 'package:swift_control/utils/messages/ride_notification.dart';
import '../ble.dart';
class ZwiftRide extends ZwiftPlay {
class ZwiftRide extends ZwiftClick {
ZwiftRide(super.scanResult);
@override
@@ -10,4 +15,30 @@ class ZwiftRide extends ZwiftPlay {
@override
bool get supportsEncryption => false;
RideNotification? _lastControllerNotification;
@override
void processClickNotification(Uint8List message) {
final RideNotification clickNotification = RideNotification(message);
if (_lastControllerNotification == null || _lastControllerNotification != clickNotification) {
_lastControllerNotification = clickNotification;
actionStreamInternal.add(clickNotification);
if (clickNotification.analogLR.abs() == 100) {
actionHandler.increaseGear();
} else if (clickNotification.analogUD.abs() == 100) {
actionHandler.decreaseGear();
}
if (clickNotification.buttonA) {
actionHandler.controlMedia(MediaAction.next);
} else if (clickNotification.buttonY) {
actionHandler.controlMedia(MediaAction.volumeUp);
} else if (clickNotification.buttonB) {
actionHandler.controlMedia(MediaAction.volumeDown);
} else if (clickNotification.buttonZ) {
actionHandler.controlMedia(MediaAction.playPause);
}
}
}
}

View File

@@ -4,13 +4,13 @@ import 'package:swift_control/utils/messages/notification.dart';
import '../../protocol/zwift.pb.dart';
class ControllerNotification extends BaseNotification {
class PlayNotification extends BaseNotification {
static const int BTN_PRESSED = 0;
late bool rightPad, buttonY, buttonZ, buttonA, buttonB, buttonOn, buttonShift;
late int analogLR, analogUD;
ControllerNotification(Uint8List message) {
PlayNotification(Uint8List message) {
final status = PlayKeyPadStatus.fromBuffer(message);
rightPad = status.rightPad.value == BTN_PRESSED;
@@ -41,7 +41,7 @@ class ControllerNotification extends BaseNotification {
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is ControllerNotification &&
other is PlayNotification &&
runtimeType == other.runtimeType &&
rightPad == other.rightPad &&
buttonY == other.buttonY &&

View File

@@ -0,0 +1,140 @@
import 'dart:typed_data';
import 'package:swift_control/utils/messages/notification.dart';
import '../../protocol/zwift.pb.dart';
enum _RideButtonMask {
LEFT_BTN(0x00001),
UP_BTN(0x00002),
RIGHT_BTN(0x00004),
DOWN_BTN(0x00008),
A_BTN(0x00010),
B_BTN(0x00020),
Y_BTN(0x00040),
Z_BTN(0x00100),
SHFT_UP_L_BTN(0x00200),
SHFT_DN_L_BTN(0x00400),
POWERUP_L_BTN(0x00800),
ONOFF_L_BTN(0x01000),
SHFT_UP_R_BTN(0x02000),
SHFT_DN_R_BTN(0x04000),
POWERUP_R_BTN(0x10000),
ONOFF_R_BTN(0x20000);
final int mask;
const _RideButtonMask(this.mask);
}
class RideNotification extends BaseNotification {
static const int BTN_PRESSED = 0;
late bool buttonLeft, buttonRight, buttonUp, buttonDown;
late bool buttonA, buttonB, buttonY, buttonZ;
late bool buttonShiftUpLeft, buttonShiftDownLeft;
late bool buttonShiftUpRight, buttonShiftDownRight;
late bool buttonPowerUpLeft, buttonPowerDownLeft;
late bool buttonOnOffLeft, buttonOnOffRight;
late int analogLR, analogUD;
RideNotification(Uint8List message) {
final status = RideKeyPadStatus.fromBuffer(message);
buttonLeft = status.buttonMap & _RideButtonMask.LEFT_BTN.mask == BTN_PRESSED;
buttonRight = status.buttonMap & _RideButtonMask.RIGHT_BTN.mask == BTN_PRESSED;
buttonUp = status.buttonMap & _RideButtonMask.UP_BTN.mask == BTN_PRESSED;
buttonDown = status.buttonMap & _RideButtonMask.DOWN_BTN.mask == BTN_PRESSED;
buttonA = status.buttonMap & _RideButtonMask.A_BTN.mask == BTN_PRESSED;
buttonB = status.buttonMap & _RideButtonMask.B_BTN.mask == BTN_PRESSED;
buttonY = status.buttonMap & _RideButtonMask.Y_BTN.mask == BTN_PRESSED;
buttonZ = status.buttonMap & _RideButtonMask.Z_BTN.mask == BTN_PRESSED;
buttonShiftUpLeft = status.buttonMap & _RideButtonMask.SHFT_UP_L_BTN.mask == BTN_PRESSED;
buttonShiftDownLeft = status.buttonMap & _RideButtonMask.SHFT_DN_L_BTN.mask == BTN_PRESSED;
buttonShiftUpRight = status.buttonMap & _RideButtonMask.SHFT_UP_R_BTN.mask == BTN_PRESSED;
buttonShiftDownRight = status.buttonMap & _RideButtonMask.SHFT_DN_R_BTN.mask == BTN_PRESSED;
buttonPowerUpLeft = status.buttonMap & _RideButtonMask.POWERUP_L_BTN.mask == BTN_PRESSED;
buttonPowerDownLeft = status.buttonMap & _RideButtonMask.POWERUP_R_BTN.mask == BTN_PRESSED;
buttonOnOffLeft = status.buttonMap & _RideButtonMask.ONOFF_L_BTN.mask == BTN_PRESSED;
buttonOnOffRight = status.buttonMap & _RideButtonMask.ONOFF_R_BTN.mask == BTN_PRESSED;
for (final analogue in status.analogButtons.groupStatus) {
if (analogue.location == RideAnalogLocation.LEFT) {
analogLR = analogue.analogValue;
} else if (analogue.location == RideAnalogLocation.DOWN) {
analogUD = analogue.analogValue;
}
}
}
@override
String toString() {
final allTrueParameters = [
if (buttonLeft) 'buttonLeft',
if (buttonRight) 'buttonRight',
if (buttonUp) 'buttonUp',
if (buttonDown) 'buttonDown',
if (buttonA) 'buttonA',
if (buttonB) 'buttonB',
if (buttonY) 'buttonY',
if (buttonZ) 'buttonZ',
if (buttonShiftUpLeft) 'buttonShiftUpLeft',
if (buttonShiftDownLeft) 'buttonShiftDownLeft',
if (buttonShiftUpRight) 'buttonShiftUpRight',
if (buttonShiftDownRight) 'buttonShiftDownRight',
if (buttonPowerUpLeft) 'buttonPowerUpLeft',
if (buttonPowerDownLeft) 'buttonPowerDownLeft',
if (buttonOnOffLeft) 'buttonOnOffLeft',
if (buttonOnOffRight) 'buttonOnOffRight',
];
return '{$allTrueParameters, analogLR: $analogLR, analogUD: $analogUD}';
}
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is RideNotification &&
runtimeType == other.runtimeType &&
buttonLeft == other.buttonLeft &&
buttonRight == other.buttonRight &&
buttonUp == other.buttonUp &&
buttonDown == other.buttonDown &&
buttonA == other.buttonA &&
buttonB == other.buttonB &&
buttonY == other.buttonY &&
buttonZ == other.buttonZ &&
buttonShiftUpLeft == other.buttonShiftUpLeft &&
buttonShiftDownLeft == other.buttonShiftDownLeft &&
buttonShiftUpRight == other.buttonShiftUpRight &&
buttonShiftDownRight == other.buttonShiftDownRight &&
buttonPowerUpLeft == other.buttonPowerUpLeft &&
buttonPowerDownLeft == other.buttonPowerDownLeft &&
buttonOnOffLeft == other.buttonOnOffLeft &&
buttonOnOffRight == other.buttonOnOffRight &&
analogLR == other.analogLR &&
analogUD == other.analogUD;
@override
int get hashCode =>
buttonLeft.hashCode ^
buttonRight.hashCode ^
buttonUp.hashCode ^
buttonDown.hashCode ^
buttonA.hashCode ^
buttonB.hashCode ^
buttonY.hashCode ^
buttonZ.hashCode ^
buttonShiftUpLeft.hashCode ^
buttonShiftDownLeft.hashCode ^
buttonShiftUpRight.hashCode ^
buttonShiftDownRight.hashCode ^
buttonPowerUpLeft.hashCode ^
buttonPowerDownLeft.hashCode ^
buttonOnOffLeft.hashCode ^
buttonOnOffRight.hashCode ^
analogLR.hashCode ^
analogUD.hashCode;
}

View File

@@ -1,7 +1,7 @@
name: swift_control
description: "SwiftControl - Control your virtual riding"
publish_to: 'none' # Remove this line if you wish to publish to pub.dev
version: 1.0.0+3
version: 1.0.0+4
environment:
sdk: ^3.7.0