Skip to content

Conversation

@iliuta
Copy link

@iliuta iliuta commented Jan 12, 2026

Hi @Malte2036,

I recently faced a problem with some cheap FTMS rower.
This rower exposed characteristics for all the machine types supported by FTMS specification:

Fitness Machine (0x1826)
- Fitness Machine Feature [R] (0x2ACC)
- Treadmill Data [N] (0x2ACD)
   Client Characteristic Configuration (0x2902)
- Cross Trainer Data [N] (0x2ACE)
   Client Characteristic Configuration (0x2902)
- Rower Data [N] (0x2AD1)
   Client Characteristic Configuration (0x2902)
- Indoor Bike Data [N] (0x2AD2)
   Client Characteristic Configuration (0x2902)
- Training Status [N R] (0x2AD3)
   Client Characteristic Configuration (0x2902)
- Supported Speed Range [R] (0x2AD4)
- Supported Inclination Range [R] (0x2AD5)
- Supported Resistance Level Range [R] (0x2AD6)
- Supported Heart Rate Range [R] (0x2AD7)
- Supported Power Range [R] (0x2AD8)
- Fitness Machine Control Point [I W WNR] (0x2AD9)
   Client Characteristic Configuration (0x2902)
- Fitness Machine Status [N] (0x2ADA)
   Client Characteristic Configuration (0x2902)

But only one data stream is populated with data, the rower one (2ad1).

Although the FTMS specification does not forbid having multiple characteristics like that, I find this weird ...

Therefore, the FTMSBluetooth.getDeviceDataType method returns crossTrainer in that situation:

  static Future<DeviceDataType?> getDeviceDataType(
      BluetoothService ftmsService) async {
    if (_getBluetoothCharacteristic(ftmsService, _dataCrossTrainerChar,
        characteristicRead: false,
        characteristicNotify: true,
        characteristicWrite: false) !=
        null) {
      return DeviceDataType.crossTrainer;
    }

    if (_getBluetoothCharacteristic(ftmsService, _dataIndoorBikeChar,
        characteristicRead: false,
        characteristicNotify: true,
        characteristicWrite: false) !=
        null) {
      return DeviceDataType.indoorBike;
    }
    ...
  }

So, when calling FTMS.useDeviceDataCharacteristic, we always subscribe to the crossTrainer data stream, which is always empty on our rower.

I thought that it would be useful to add an optional parameter, named preferredDeviceDataType, to the useDeviceDataCharacteristic method as it follows:

static Future<void> useDeviceDataCharacteristic(
      BluetoothDevice device, void Function(DeviceData) onData, {DeviceDataType? preferredDeviceDataType}) async

That parameter, if set, gives a hint about what characteristic should we subscribe for. If the characteristic is supported by the device, then we subscribe to it.
If it's not supported then we continue as before.

This gives the opportunity for the caller to choose what kind of machine it is likely to support.

What do you think? Or perhaps do you have other ideas?

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds support for FTMS devices that expose multiple data characteristics by introducing an optional preferredDeviceDataType parameter to the useDeviceDataCharacteristic method. This addresses the issue where some devices (like certain rowers) expose all FTMS machine type characteristics but only populate data in one stream.

Changes:

  • Added optional preferredDeviceDataType parameter to useDeviceDataCharacteristic method with fallback to automatic detection
  • Implemented isDeviceDataTypeSupported helper method to check if a specific device type is supported
  • Added comprehensive test coverage for the new functionality

Reviewed changes

Copilot reviewed 6 out of 7 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
lib/flutter_ftms.dart Added optional preferredDeviceDataType parameter with logic to prefer specified type when supported
lib/src/ftms_bluetooth.dart Added isDeviceDataTypeSupported method and applied formatting improvements
test/flutter_ftms_test.dart New test file with comprehensive tests for preferred device type functionality
test/ftms_bluetooth_test.dart Added tests for isDeviceDataTypeSupported method
test/flutter_ftms_test.mocks.dart Generated mock file for new test suite
test/ftms_bluetooth_test.mocks.dart Updated mocks to include CharacteristicProperties
example/pubspec.lock Version bump to 1.3.0 and dependency updates

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@Malte2036
Copy link
Owner

Looks good. Could you please update the useDeviceDataCharacteristic part of the README?

@iliuta
Copy link
Author

iliuta commented Jan 12, 2026

Looks good. Could you please update the useDeviceDataCharacteristic part of the README?

Thanks. I've just updated README as well.

@Malte2036 Malte2036 merged commit e97d6db into Malte2036:main Jan 12, 2026
1 check passed
@Malte2036
Copy link
Owner

Released in v1.4.0 😁

@iliuta
Copy link
Author

iliuta commented Jan 13, 2026

Thanks! Cheers!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants