import { BaseInterface } from "./base"; import { BaseService, BaseCharacteritic, BaseApplication } from "./bluez-gatt"; export class FTMSService extends BaseService { UUID = "00001826-0000-1000-8000-00805f9b34fb"; Primary = true; constructor(bus, path) { super(bus, path); this.addCharacteristic(new FTMSFeature(bus, this.path + "/char0", this)); this.addCharacteristic(new IndoorBikeData(bus, this.path + "/char1", this)); this.addCharacteristic( new FTMSControlPoint(bus, this.path + "/char2", this) ); } } class FTMSFeature extends BaseCharacteritic { constructor(bus, path, service) { super(bus, path, service, "00002acc-0000-1000-8000-00805f9b34fb", ["read"]); } read() { console.log("read FTMS Feature"); // 32bits features + 32bits target supported // 0x0a 0x44 = feature: cadence (1), inclinaison (3), hr (10), power (14) // 0x0a 0x20 = target: inclinaison (1), power (3), simulation parameters (13) return [0x0a, 0x44, 0x00, 0x00, 0x0a, 0x20, 0x00, 0x00]; } } class FTMSControlPoint extends BaseCharacteritic { wind = 0; grade = 0; crr = 0; cw = 0; constructor(bus, path, service) { super(bus, path, service, "00002ad9-0000-1000-8000-00805f9b34fb", [ "write", "indicate", ]); } write(value, options) { console.log("FTMS Write Control Point"); console.log(value); const arraybuf = value.buffer.slice( value.byteOffset, value.byteOffset + value.byteLength ); const view = new DataView(arraybuf); if (value[0] === 0x00) { // take control this.notify([0x80, 0x00, 0x01]); } else if (value[0] === 0x01) { // reset machine this.notify([0x80, 0x01, 0x01]); } else if (value[0] === 0x07) { // start/resume this.notify([0x80, 0x07, 0x01]); } else if (value[0] === 0x11) { // set simulations param (wind, grade, etc) const wind = view.getInt16(1, true); const grade = view.getInt16(3, true); const crr = view.getUint8(5); const cw = view.getUint8(6); this.wind = wind * 0.001; this.grade = grade * 0.01; this.crr = crr * 0.001; this.cw = cw * 0.01; console.log(wind * 0.001); console.log(grade * 0.01); console.log(crr * 0.0001); console.log(cw * 0.01); this.notify([0x80, 0x11, 0x01]); } else { console.log("FTMS Control point op code " + value[0] + "not supported"); } } startNotify() { console.log("FTMS Start Notify CP"); } stopNotify() { console.log("FTMS Stop Notify CP"); } } class IndoorBikeData extends BaseCharacteritic { constructor(bus, path, service) { super(bus, path, service, "00002ad2-0000-1000-8000-00805f9b34fb", [ "notify", ]); } startNotify() { if (this.Notifying) { return; } this.Notifying = true; this.interval = setInterval(() => { console.log("FTMS Notif Indoor Bike"); // 0x44 0x02 = fields included: cadence (2), power (6), hr (9) // 0x00 0x00 = average speed, always included // cadence is *2 (resolution 0.5 s^-1) this.notify([ 0x44, 0x02, 0x00, 0x00, Math.round(160 + Math.random() * 10), 0x00, Math.round(160 + Math.random() * 40), 0x00, Math.round(120 + Math.random() * 10), ]); }, 1000); } stopNotify() { if (!this.Notifying) { return; } this.Notifying = false; clearInterval(this.interval); } }