From 0b1dd5c4722d3cbc2acb5870c1e76ad3a8c1bb95 Mon Sep 17 00:00:00 2001 From: rtod Date: Sat, 19 Sep 2020 10:23:16 -0400 Subject: [PATCH] WIP add support for resistance change --- src/server/index.js | 4 +- .../fitness-machine-control-point.js | 63 +++++++++++++++++++ .../fitness-machine-feature.js | 31 +++++++++ src/server/services/fitness-machine/index.js | 35 +++++++++++ 4 files changed, 132 insertions(+), 1 deletion(-) create mode 100644 src/server/services/fitness-machine/characteristics/fitness-machine-control-point.js create mode 100644 src/server/services/fitness-machine/characteristics/fitness-machine-feature.js create mode 100644 src/server/services/fitness-machine/index.js diff --git a/src/server/index.js b/src/server/index.js index cd7f57af..61f57f81 100644 --- a/src/server/index.js +++ b/src/server/index.js @@ -1,4 +1,5 @@ import {CyclingPowerService} from './services/cycling-power' +import {FitnessMachineService} from './services/fitness-machine' import {BleServer} from '../util/ble-server' export const DEFAULT_NAME = 'Gymnasticon'; @@ -14,7 +15,8 @@ export class GymnasticonServer extends BleServer { */ constructor(bleno, name=DEFAULT_NAME) { super(bleno, name, [ - new CyclingPowerService() + new CyclingPowerService(), + new FitnessMachineService() ]) } diff --git a/src/server/services/fitness-machine/characteristics/fitness-machine-control-point.js b/src/server/services/fitness-machine/characteristics/fitness-machine-control-point.js new file mode 100644 index 00000000..12ebb4e3 --- /dev/null +++ b/src/server/services/fitness-machine/characteristics/fitness-machine-control-point.js @@ -0,0 +1,63 @@ +import {Characteristic, Descriptor} from '@abandonware/bleno'; + +const FLAG_HASCRANKDATA = (1<<5); +const CRANK_TIMESTAMP_SCALE = 1000 / 1024; // timestamp resolution is 1/1024 sec + +/** + * Bluetooth LE GATT Cycling Power Measurement Characteristic implementation. + */ +export class FitnessMachineControlPoint extends Characteristic { + constructor() { + super({ + uuid: '2AD9', + properties: ['read', 'write'], + descriptors: [ + new Descriptor({ + uuid: '2902', + value: Buffer.alloc(2) + }), + new Descriptor({ + uuid: '2903', + value: Buffer.alloc(2) + }) + ] + }) + } + onWriteRequest () { + console.log('write request', arguments) + } + onReadRequest (a, callback) { + console.log('read request', arguments) + callback(Buffer.from([0x80, 1])) + } + + /** + * Notify subscriber (e.g. Zwift) of new Cycling Power Measurement. + * @param {object} measurement - new cycling power measurement. + * @param {number} measurement.power - current power (watts) + * @param {object} [measurement.crank] - last crank event. + * @param {number} measurement.crank.revolutions - revolution count at last crank event. + * @param {number} measurement.crank.timestamp - timestamp at last crank event. + */ +// updateMeasurement({ power, crank }) { +// let flags = 0; + +// const value = Buffer.alloc(8); +// value.writeInt16LE(power, 2); + +// // include crank data if provided +// if (crank) { +// const revolutions16bit = crank.revolutions & 0xffff; +// const timestamp16bit = Math.floor(crank.timestamp * CRANK_TIMESTAMP_SCALE) & 0xffff; +// value.writeUInt16LE(revolutions16bit, 4); +// value.writeUInt16LE(timestamp16bit, 6); +// flags |= FLAG_HASCRANKDATA; +// } + +// value.writeUInt16LE(flags, 0); + +// if (this.updateValueCallback) { +// this.updateValueCallback(value) +// } +// } +} diff --git a/src/server/services/fitness-machine/characteristics/fitness-machine-feature.js b/src/server/services/fitness-machine/characteristics/fitness-machine-feature.js new file mode 100644 index 00000000..eeab21fb --- /dev/null +++ b/src/server/services/fitness-machine/characteristics/fitness-machine-feature.js @@ -0,0 +1,31 @@ +import {Characteristic, Descriptor} from '@abandonware/bleno'; + +/** + * Bluetooth LE GATT Cycling Power Feature Characteristic implementation. + */ +export class FitnessMachineFeatureCharacteristic extends Characteristic { + constructor() { + super({ + uuid: '2ACC', + properties: ['read'], + descriptors: [ + new Descriptor({ + uuid: '2901', + value: 'Fitness Machine Feature' + }) + ], + value: Buffer.from([ + // Resistance Level Supported + 1, + 0, + 0, + 0, + // Resistance Target Setting Supported + 32, + 0, + 0, + 0 + ]) + }) + } +} diff --git a/src/server/services/fitness-machine/index.js b/src/server/services/fitness-machine/index.js new file mode 100644 index 00000000..a93a481b --- /dev/null +++ b/src/server/services/fitness-machine/index.js @@ -0,0 +1,35 @@ +import {PrimaryService} from '@abandonware/bleno'; +import {FitnessMachineFeatureCharacteristic} from './characteristics/fitness-machine-feature'; +import {FitnessMachineControlPoint} from './characteristics/fitness-machine-control-point'; +// import {SensorLocationCharacteristic} from './characteristics/sensor-location'; + +/** + * Bluetooth LE GATT Cycling Power Service implementation. + */ +export class FitnessMachineService extends PrimaryService { + /** + * Create a FitnessMachineService instance. + */ + constructor() { + super({ + uuid: '1818', + characteristics: [ + new FitnessMachineFeatureCharacteristic(), + new FitnessMachineControlPoint(), + // new SensorLocationCharacteristic(), + ] + }) + } + + /** + * Notify subscriber (e.g. Zwift) of new Cycling Power Measurement. + * @param {object} measurement - new cycling power measurement. + * @param {number} measurement.power - current power (watts) + * @param {object} [measurement.crank] - last crank event. + * @param {number} measurement.crank.revolutions - revolution count at last crank event. + * @param {number} measurement.crank.timestamp - timestamp at last crank event. + */ +// updateMeasurement(measurement) { +// this.characteristics[0].updateMeasurement(measurement) +// } +}