incyclist-devices 2.3.12 → 2.3.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/ble/adapter-factory.d.ts +34 -0
- package/lib/ble/adapter-factory.js +110 -0
- package/lib/ble/base/comms-utils.d.ts +7 -0
- package/lib/ble/base/comms-utils.js +90 -0
- package/lib/ble/base/comms.d.ts +75 -0
- package/lib/ble/base/comms.js +599 -0
- package/lib/ble/ble-interface.d.ts +84 -0
- package/lib/ble/ble-interface.js +622 -0
- package/lib/ble/ble-peripheral.d.ts +39 -0
- package/lib/ble/ble-peripheral.js +252 -0
- package/lib/ble/cp/comm.d.ts +30 -0
- package/lib/ble/cp/comm.js +126 -0
- package/lib/ble/elite/adapter.d.ts +21 -0
- package/lib/ble/elite/adapter.js +118 -0
- package/lib/ble/elite/comms.d.ts +31 -0
- package/lib/ble/elite/comms.js +127 -0
- package/lib/ble/elite/index.d.ts +3 -0
- package/lib/ble/elite/index.js +10 -0
- package/lib/ble/fm/comms.d.ts +49 -0
- package/lib/ble/fm/comms.js +506 -0
- package/lib/ble/fm/sensor.js +3 -3
- package/lib/ble/hr/comm.d.ts +19 -0
- package/lib/ble/hr/comm.js +65 -0
- package/lib/ble/peripheral-cache.d.ts +45 -0
- package/lib/ble/peripheral-cache.js +109 -0
- package/lib/ble/tacx/comms.d.ts +59 -0
- package/lib/ble/tacx/comms.js +634 -0
- package/lib/ble/wahoo/comms.d.ts +64 -0
- package/lib/ble/wahoo/comms.js +399 -0
- package/lib/direct-connect/base/comms.d.ts +3 -0
- package/lib/direct-connect/base/comms.js +7 -0
- package/lib/direct-connect/base/sensor.d.ts +3 -0
- package/lib/direct-connect/base/sensor.js +7 -0
- package/lib/direct-connect/utils.d.ts +5 -0
- package/lib/direct-connect/utils.js +73 -0
- package/lib/factories/index.d.ts +3 -0
- package/lib/factories/index.js +10 -0
- package/lib/modes/power-base.js +8 -5
- package/lib/utils/operation.d.ts +17 -0
- package/lib/utils/operation.js +20 -0
- package/package.json +1 -1
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
const ble_interface_1 = __importDefault(require("./ble-interface"));
|
|
16
|
+
const gd_eventlog_1 = require("gd-eventlog");
|
|
17
|
+
const events_1 = __importDefault(require("events"));
|
|
18
|
+
const utils_1 = require("./utils");
|
|
19
|
+
class BlePeripheralConnector {
|
|
20
|
+
constructor(peripheral) {
|
|
21
|
+
this.ble = ble_interface_1.default.getInstance();
|
|
22
|
+
this.peripheral = peripheral;
|
|
23
|
+
this.emitter = new events_1.default();
|
|
24
|
+
if (!this.peripheral || !this.ble)
|
|
25
|
+
throw new Error('Illegal Arguments');
|
|
26
|
+
this.state = { subscribed: [], isConnected: false, isConnecting: false, isInitialized: false, isInitializing: false, isSubscribing: false };
|
|
27
|
+
this.services = [];
|
|
28
|
+
this.characteristics = [];
|
|
29
|
+
this.logger = new gd_eventlog_1.EventLogger('BLE');
|
|
30
|
+
}
|
|
31
|
+
logEvent(event) {
|
|
32
|
+
if (this.logger) {
|
|
33
|
+
this.logger.logEvent(event);
|
|
34
|
+
}
|
|
35
|
+
const w = global.window;
|
|
36
|
+
if ((w === null || w === void 0 ? void 0 : w.DEVICE_DEBUG) || process.env.BLE_DEBUG) {
|
|
37
|
+
console.log('~~~ BLE', event);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
connect() {
|
|
41
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
42
|
+
if (this.state.isConnected)
|
|
43
|
+
return;
|
|
44
|
+
this.logEvent({ message: 'connect peripheral', peripheral: this.peripheral.address, state: this.state, peripheralState: this.peripheral.state });
|
|
45
|
+
const wasConnecting = this.state.isConnecting;
|
|
46
|
+
this.state.isConnecting = true;
|
|
47
|
+
try {
|
|
48
|
+
if (!wasConnecting) {
|
|
49
|
+
this.peripheral.once('disconnect', () => { this.onDisconnect(); });
|
|
50
|
+
if (!this.state.isConnected || (this.peripheral && this.peripheral.state !== 'connected')) {
|
|
51
|
+
this.state.connectPromise = this.peripheral.connectAsync();
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
if (this.state.connectPromise)
|
|
55
|
+
yield this.state.connectPromise;
|
|
56
|
+
this.state.isConnected = this.peripheral.state === 'connected';
|
|
57
|
+
this.state.connectPromise = undefined;
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
catch (err) {
|
|
61
|
+
this.logEvent({ message: 'Error', fn: 'connect()', error: err.message });
|
|
62
|
+
}
|
|
63
|
+
this.state.connectPromise = undefined;
|
|
64
|
+
this.state.isConnecting = false;
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
reconnect() {
|
|
68
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
69
|
+
yield this.connect();
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
onDisconnect() {
|
|
73
|
+
this.peripheral.removeAllListeners('connect');
|
|
74
|
+
this.peripheral.removeAllListeners('disconnect');
|
|
75
|
+
this.logEvent({ message: 'onDisconnected', peripheral: this.peripheral.address, state: this.state });
|
|
76
|
+
this.state.isConnected = false;
|
|
77
|
+
this.state.isConnecting = false;
|
|
78
|
+
this.state.isInitialized = false;
|
|
79
|
+
this.state.isInitializing = false;
|
|
80
|
+
this.state.connectPromise = undefined;
|
|
81
|
+
this.state.isSubscribing = false;
|
|
82
|
+
this.state.subscribed = [];
|
|
83
|
+
this.emitter.emit('disconnect');
|
|
84
|
+
}
|
|
85
|
+
initialize() {
|
|
86
|
+
return __awaiter(this, arguments, void 0, function* (enforce = false) {
|
|
87
|
+
this.logEvent({ message: 'initialize', peripheral: this.peripheral.address, state: this.state, enforce });
|
|
88
|
+
if (this.state.isInitialized && !enforce)
|
|
89
|
+
return true;
|
|
90
|
+
if (this.state.isInitialized && enforce) {
|
|
91
|
+
this.state.isInitialized = false;
|
|
92
|
+
}
|
|
93
|
+
return new Promise((done) => __awaiter(this, void 0, void 0, function* () {
|
|
94
|
+
this.state.isInitializing = true;
|
|
95
|
+
this.characteristics = [];
|
|
96
|
+
this.services = [];
|
|
97
|
+
try {
|
|
98
|
+
this.emitter.once('disconnect', () => {
|
|
99
|
+
done(false);
|
|
100
|
+
});
|
|
101
|
+
const res = yield this.peripheral.discoverSomeServicesAndCharacteristicsAsync([], []);
|
|
102
|
+
if (this.state.isInitializing) {
|
|
103
|
+
this.characteristics = res.characteristics || [];
|
|
104
|
+
const services = res.services || [];
|
|
105
|
+
this.services = services.map(s => typeof (s) === 'string' ? s : s.uuid);
|
|
106
|
+
this.state.isInitializing = false;
|
|
107
|
+
this.state.isInitialized = this.characteristics !== undefined && this.services !== undefined && this.characteristics.length > 0 && this.services.length > 0;
|
|
108
|
+
this.logEvent({ message: 'initialize done', peripheral: this.peripheral.address, state: this.state });
|
|
109
|
+
return done(true);
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
this.logEvent({ message: 'initialize interrupted', peripheral: this.peripheral.address });
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
catch (err) {
|
|
116
|
+
this.logEvent({ message: 'error', fn: 'initialize', error: err.message, stack: err.stack });
|
|
117
|
+
this.state.isInitializing = false;
|
|
118
|
+
this.state.isInitialized = false;
|
|
119
|
+
done(false);
|
|
120
|
+
}
|
|
121
|
+
}));
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
isSubscribed(characteristicUuid) {
|
|
125
|
+
var _a;
|
|
126
|
+
return ((_a = this.state.subscribed) === null || _a === void 0 ? void 0 : _a.find(c => c === characteristicUuid || (0, utils_1.uuid)(c) === characteristicUuid || c === (0, utils_1.uuid)(characteristicUuid))) !== undefined;
|
|
127
|
+
}
|
|
128
|
+
subscribeAll(callback) {
|
|
129
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
130
|
+
var _a;
|
|
131
|
+
const cnt = this.characteristics.length;
|
|
132
|
+
this.state.isSubscribing = true;
|
|
133
|
+
const subscribed = [];
|
|
134
|
+
if (!this.state.subscribed)
|
|
135
|
+
this.state.subscribed = [];
|
|
136
|
+
for (let i = 0; i < cnt; i++) {
|
|
137
|
+
try {
|
|
138
|
+
const c = this.characteristics[i];
|
|
139
|
+
const isNotify = c.properties.find(p => p === 'notify');
|
|
140
|
+
if (isNotify && subscribed.find(uuid => uuid === c.uuid) === undefined) {
|
|
141
|
+
c.on('data', (data, _isNotification) => {
|
|
142
|
+
this.onData((0, utils_1.uuid)(c.uuid), data);
|
|
143
|
+
});
|
|
144
|
+
if (callback) {
|
|
145
|
+
this.on((0, utils_1.uuid)(c.uuid), callback);
|
|
146
|
+
}
|
|
147
|
+
this.logEvent({ message: 'subscribe', peripheral: this.peripheral.address, characteristic: c.uuid, uuid: (0, utils_1.uuid)(c.uuid) });
|
|
148
|
+
if (((_a = this.state.subscribed) === null || _a === void 0 ? void 0 : _a.find(uuid => uuid === c.uuid)) === undefined) {
|
|
149
|
+
try {
|
|
150
|
+
yield this.subscribe(c.uuid, 3000);
|
|
151
|
+
subscribed.push(c.uuid);
|
|
152
|
+
}
|
|
153
|
+
catch (err) {
|
|
154
|
+
this.logEvent({ message: 'cannot subscribe', peripheral: this.peripheral.address, characteristic: c.uuid, error: err.message || err });
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
catch (err) {
|
|
160
|
+
this.logEvent({ message: 'error', fn: 'subscribeAll()', error: err.message || err, stack: err.stack });
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
this.state.isSubscribing = false;
|
|
164
|
+
this.state.subscribed = subscribed;
|
|
165
|
+
return subscribed;
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
subscribe(characteristicUuid, timeout) {
|
|
169
|
+
this.logEvent({ message: 'subscribe attempt', characteristic: characteristicUuid, characteristics: this.characteristics.map(c => ({ characteristic: c.uuid, uuid: (0, utils_1.uuid)(c.uuid) })) });
|
|
170
|
+
return new Promise((resolve, reject) => {
|
|
171
|
+
try {
|
|
172
|
+
const characteristic = this.characteristics.find(c => (0, utils_1.uuid)(c.uuid) === (0, utils_1.uuid)(characteristicUuid) || (0, utils_1.uuid)(c.uuid) === (0, utils_1.uuid)(characteristicUuid));
|
|
173
|
+
if (!characteristic) {
|
|
174
|
+
reject(new Error('Characteristic not found'));
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
this.logEvent({ message: 'subscribe', peripheral: this.peripheral.address, characteristic: characteristic.uuid });
|
|
178
|
+
characteristic.removeAllListeners('data');
|
|
179
|
+
characteristic.on('data', (data, _isNotification) => {
|
|
180
|
+
this.onData((0, utils_1.uuid)(characteristicUuid), data);
|
|
181
|
+
});
|
|
182
|
+
let to;
|
|
183
|
+
if (timeout) {
|
|
184
|
+
to = setTimeout(() => {
|
|
185
|
+
this.logEvent({ message: 'subscribe result', characteristic: characteristicUuid, error: 'timeout' });
|
|
186
|
+
reject(new Error('timeout'));
|
|
187
|
+
}, timeout);
|
|
188
|
+
}
|
|
189
|
+
characteristic.subscribe((err) => {
|
|
190
|
+
if (to)
|
|
191
|
+
clearTimeout(to);
|
|
192
|
+
this.logEvent({ message: 'subscribe result', characteristic: characteristicUuid, error: err });
|
|
193
|
+
if (err)
|
|
194
|
+
reject(err);
|
|
195
|
+
else {
|
|
196
|
+
this.state.subscribed.push(characteristicUuid);
|
|
197
|
+
resolve(true);
|
|
198
|
+
}
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
catch (err) {
|
|
202
|
+
this.logEvent({ message: 'error', error: err.message || err, stack: err.stack });
|
|
203
|
+
}
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
unsubscribeAll() {
|
|
207
|
+
var _a;
|
|
208
|
+
(_a = this.characteristics) === null || _a === void 0 ? void 0 : _a.forEach(c => {
|
|
209
|
+
const isNotify = c.properties.find(p => p === 'notify');
|
|
210
|
+
if (isNotify) {
|
|
211
|
+
this.unubscribe(c);
|
|
212
|
+
}
|
|
213
|
+
});
|
|
214
|
+
this.state.isSubscribing = false;
|
|
215
|
+
this.state.subscribed = [];
|
|
216
|
+
}
|
|
217
|
+
unubscribe(c) {
|
|
218
|
+
c.unsubscribe(undefined);
|
|
219
|
+
c.removeAllListeners();
|
|
220
|
+
}
|
|
221
|
+
onData(characteristicUuid, data) {
|
|
222
|
+
this.emitter.emit((0, utils_1.uuid)(characteristicUuid), characteristicUuid, data);
|
|
223
|
+
}
|
|
224
|
+
on(characteristicUuid, callback) {
|
|
225
|
+
if (callback)
|
|
226
|
+
this.emitter.on((0, utils_1.uuid)(characteristicUuid), callback);
|
|
227
|
+
}
|
|
228
|
+
once(characteristicUuid, callback) {
|
|
229
|
+
if (callback)
|
|
230
|
+
this.emitter.once((0, utils_1.uuid)(characteristicUuid), callback);
|
|
231
|
+
}
|
|
232
|
+
off(characteristicUuid, callback) {
|
|
233
|
+
if (callback)
|
|
234
|
+
this.emitter.off((0, utils_1.uuid)(characteristicUuid), callback);
|
|
235
|
+
}
|
|
236
|
+
removeAllListeners(characteristicUuid) {
|
|
237
|
+
this.emitter.removeAllListeners((0, utils_1.uuid)(characteristicUuid));
|
|
238
|
+
}
|
|
239
|
+
getState() {
|
|
240
|
+
return this.peripheral.state;
|
|
241
|
+
}
|
|
242
|
+
getCharachteristics() {
|
|
243
|
+
return this.characteristics;
|
|
244
|
+
}
|
|
245
|
+
getServices() {
|
|
246
|
+
return this.services;
|
|
247
|
+
}
|
|
248
|
+
getPeripheral() {
|
|
249
|
+
return this.peripheral;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
exports.default = BlePeripheralConnector;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { CrankData, PowerData } from './types';
|
|
2
|
+
import { BleComms } from '../base/comms';
|
|
3
|
+
import { LegacyProfile } from '../../antv2/types';
|
|
4
|
+
import { BleProtocol, IBlePeripheralConnector } from '../types';
|
|
5
|
+
export default class BleCyclingPowerDevice extends BleComms {
|
|
6
|
+
static protocol: BleProtocol;
|
|
7
|
+
static services: string[];
|
|
8
|
+
static characteristics: string[];
|
|
9
|
+
static detectionPriority: number;
|
|
10
|
+
instantaneousPower: number;
|
|
11
|
+
balance: number;
|
|
12
|
+
accTorque: number;
|
|
13
|
+
rpm: number;
|
|
14
|
+
timeOffset: number;
|
|
15
|
+
time: number;
|
|
16
|
+
currentCrankData: CrankData;
|
|
17
|
+
prevCrankData: CrankData;
|
|
18
|
+
static isMatching(characteristics: string[]): boolean;
|
|
19
|
+
getProfile(): LegacyProfile;
|
|
20
|
+
getProtocol(): BleProtocol;
|
|
21
|
+
getServiceUUids(): string[];
|
|
22
|
+
subscribeAll(conn?: IBlePeripheralConnector): Promise<void>;
|
|
23
|
+
parseCrankData(crankData: any): {
|
|
24
|
+
rpm: number;
|
|
25
|
+
time: any;
|
|
26
|
+
};
|
|
27
|
+
parsePower(_data: Uint8Array): PowerData;
|
|
28
|
+
onData(characteristic: string, data: Buffer): boolean;
|
|
29
|
+
reset(): void;
|
|
30
|
+
}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const consts_1 = require("../consts");
|
|
4
|
+
const utils_1 = require("../utils");
|
|
5
|
+
const comms_1 = require("../base/comms");
|
|
6
|
+
class BleCyclingPowerDevice extends comms_1.BleComms {
|
|
7
|
+
constructor() {
|
|
8
|
+
super(...arguments);
|
|
9
|
+
this.instantaneousPower = undefined;
|
|
10
|
+
this.balance = undefined;
|
|
11
|
+
this.accTorque = undefined;
|
|
12
|
+
this.rpm = undefined;
|
|
13
|
+
this.timeOffset = 0;
|
|
14
|
+
this.time = undefined;
|
|
15
|
+
this.currentCrankData = undefined;
|
|
16
|
+
this.prevCrankData = undefined;
|
|
17
|
+
}
|
|
18
|
+
static isMatching(characteristics) {
|
|
19
|
+
if (!characteristics)
|
|
20
|
+
return false;
|
|
21
|
+
const announced = characteristics.map(c => (0, utils_1.uuid)(c));
|
|
22
|
+
const hasCPMeasurement = announced.find(c => c === consts_1.CSP_MEASUREMENT) !== undefined;
|
|
23
|
+
const hasCPFeature = announced.find(c => c === consts_1.CSP_FEATURE) !== undefined;
|
|
24
|
+
return hasCPMeasurement && hasCPFeature;
|
|
25
|
+
}
|
|
26
|
+
getProfile() {
|
|
27
|
+
return 'Power Meter';
|
|
28
|
+
}
|
|
29
|
+
getProtocol() {
|
|
30
|
+
return BleCyclingPowerDevice.protocol;
|
|
31
|
+
}
|
|
32
|
+
getServiceUUids() {
|
|
33
|
+
return BleCyclingPowerDevice.services;
|
|
34
|
+
}
|
|
35
|
+
subscribeAll(conn) {
|
|
36
|
+
return this.subscribeMultiple([consts_1.CSP_MEASUREMENT], conn);
|
|
37
|
+
}
|
|
38
|
+
parseCrankData(crankData) {
|
|
39
|
+
if (!this.prevCrankData)
|
|
40
|
+
this.prevCrankData = { revolutions: 0, time: 0, cntUpdateMissing: -1 };
|
|
41
|
+
const c = this.currentCrankData = crankData;
|
|
42
|
+
const p = this.prevCrankData;
|
|
43
|
+
let rpm = this.rpm;
|
|
44
|
+
let hasUpdate = c.time !== p.time;
|
|
45
|
+
if (hasUpdate) {
|
|
46
|
+
let time = c.time - p.time;
|
|
47
|
+
let revs = c.revolutions - p.revolutions;
|
|
48
|
+
if (c.time < p.time) {
|
|
49
|
+
time += 0x10000;
|
|
50
|
+
this.timeOffset += 0x10000;
|
|
51
|
+
}
|
|
52
|
+
if (c.revolutions < p.revolutions)
|
|
53
|
+
revs += 0x10000;
|
|
54
|
+
rpm = 1024 * 60 * revs / time;
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
if (p.cntUpdateMissing < 0 || p.cntUpdateMissing > 2) {
|
|
58
|
+
rpm = 0;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
const cntUpdateMissing = p.cntUpdateMissing;
|
|
62
|
+
this.prevCrankData = this.currentCrankData;
|
|
63
|
+
if (hasUpdate)
|
|
64
|
+
this.prevCrankData.cntUpdateMissing = 0;
|
|
65
|
+
else
|
|
66
|
+
this.prevCrankData.cntUpdateMissing = cntUpdateMissing + 1;
|
|
67
|
+
return { rpm, time: this.timeOffset + c.time };
|
|
68
|
+
}
|
|
69
|
+
parsePower(_data) {
|
|
70
|
+
const data = Buffer.from(_data);
|
|
71
|
+
try {
|
|
72
|
+
let offset = 4;
|
|
73
|
+
const flags = data.readUInt16LE(0);
|
|
74
|
+
this.instantaneousPower = data.readUInt16LE(2);
|
|
75
|
+
if (flags & 0x1)
|
|
76
|
+
this.balance = data.readUInt8(offset++);
|
|
77
|
+
if (flags & 0x4) {
|
|
78
|
+
this.accTorque = data.readUInt16LE(offset);
|
|
79
|
+
offset += 2;
|
|
80
|
+
}
|
|
81
|
+
if (flags & 0x10) {
|
|
82
|
+
offset += 6;
|
|
83
|
+
}
|
|
84
|
+
if (flags & 0x20) {
|
|
85
|
+
const crankData = {
|
|
86
|
+
revolutions: data.readUInt16LE(offset),
|
|
87
|
+
time: data.readUInt16LE(offset + 2)
|
|
88
|
+
};
|
|
89
|
+
const { rpm, time } = this.parseCrankData(crankData);
|
|
90
|
+
this.rpm = rpm;
|
|
91
|
+
this.time = time;
|
|
92
|
+
offset += 4;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
catch (err) {
|
|
96
|
+
}
|
|
97
|
+
const { instantaneousPower, balance, accTorque, rpm, time } = this;
|
|
98
|
+
return { instantaneousPower, balance, accTorque, rpm, time, raw: `2a63:${data.toString('hex')}` };
|
|
99
|
+
}
|
|
100
|
+
onData(characteristic, data) {
|
|
101
|
+
const hasData = super.onData(characteristic, data);
|
|
102
|
+
if (!hasData)
|
|
103
|
+
return false;
|
|
104
|
+
if ((0, utils_1.matches)(characteristic, consts_1.CSP_MEASUREMENT)) {
|
|
105
|
+
const res = this.parsePower(data);
|
|
106
|
+
this.emit('data', res);
|
|
107
|
+
return false;
|
|
108
|
+
}
|
|
109
|
+
return true;
|
|
110
|
+
}
|
|
111
|
+
reset() {
|
|
112
|
+
this.instantaneousPower = undefined;
|
|
113
|
+
this.balance = undefined;
|
|
114
|
+
this.accTorque = undefined;
|
|
115
|
+
this.rpm = undefined;
|
|
116
|
+
this.timeOffset = 0;
|
|
117
|
+
this.time = undefined;
|
|
118
|
+
this.currentCrankData = undefined;
|
|
119
|
+
this.prevCrankData = undefined;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
BleCyclingPowerDevice.protocol = 'cp';
|
|
123
|
+
BleCyclingPowerDevice.services = [consts_1.CSP];
|
|
124
|
+
BleCyclingPowerDevice.characteristics = [consts_1.CSP_MEASUREMENT, consts_1.CSP_FEATURE, '2a5d', '2a3c'];
|
|
125
|
+
BleCyclingPowerDevice.detectionPriority = 1;
|
|
126
|
+
exports.default = BleCyclingPowerDevice;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import BleAdapter from '../base/adapter';
|
|
2
|
+
import BleEliteDevice from './comms';
|
|
3
|
+
import ICyclingMode from '../../modes/types';
|
|
4
|
+
import { PowerData } from '../cp';
|
|
5
|
+
import { DeviceProperties, IncyclistAdapterData, IncyclistBikeData, ControllerConfig, IAdapter } from '../../types';
|
|
6
|
+
import { BleDeviceSettings } from '../types';
|
|
7
|
+
import { LegacyProfile } from '../../antv2/types';
|
|
8
|
+
export default class BleEliteAdapter extends BleAdapter<PowerData, BleEliteDevice> {
|
|
9
|
+
protected static INCYCLIST_PROFILE_NAME: LegacyProfile;
|
|
10
|
+
protected static controllers: ControllerConfig;
|
|
11
|
+
distanceInternal: number;
|
|
12
|
+
constructor(settings: BleDeviceSettings, props?: DeviceProperties);
|
|
13
|
+
isSame(device: IAdapter): boolean;
|
|
14
|
+
getProfile(): LegacyProfile;
|
|
15
|
+
getName(): string;
|
|
16
|
+
getDefaultCyclingMode(): ICyclingMode;
|
|
17
|
+
getSupportedCyclingModes(): any[];
|
|
18
|
+
mapData(deviceData: PowerData): IncyclistBikeData;
|
|
19
|
+
transformData(bikeData: IncyclistBikeData): IncyclistAdapterData;
|
|
20
|
+
start(props?: any): Promise<any>;
|
|
21
|
+
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
const gd_eventlog_1 = require("gd-eventlog");
|
|
16
|
+
const adapter_1 = __importDefault(require("../base/adapter"));
|
|
17
|
+
const comms_1 = __importDefault(require("./comms"));
|
|
18
|
+
const power_meter_1 = __importDefault(require("../../modes/power-meter"));
|
|
19
|
+
const capabilities_1 = require("../../types/capabilities");
|
|
20
|
+
class BleEliteAdapter extends adapter_1.default {
|
|
21
|
+
constructor(settings, props) {
|
|
22
|
+
super(settings, props);
|
|
23
|
+
this.distanceInternal = 0;
|
|
24
|
+
this.logger = new gd_eventlog_1.EventLogger('BLE-Elite');
|
|
25
|
+
const { id, address, name } = settings;
|
|
26
|
+
const logger = this.logger;
|
|
27
|
+
const ble = this.ble;
|
|
28
|
+
this.device = new comms_1.default({ id, address, name, ble, logger });
|
|
29
|
+
this.capabilities = [
|
|
30
|
+
capabilities_1.IncyclistCapability.Power, capabilities_1.IncyclistCapability.Speed, capabilities_1.IncyclistCapability.Cadence,
|
|
31
|
+
capabilities_1.IncyclistCapability.Control
|
|
32
|
+
];
|
|
33
|
+
}
|
|
34
|
+
isSame(device) {
|
|
35
|
+
if (!(device instanceof BleEliteAdapter))
|
|
36
|
+
return false;
|
|
37
|
+
return this.isEqual(device.settings);
|
|
38
|
+
}
|
|
39
|
+
getProfile() {
|
|
40
|
+
return 'Smart Trainer';
|
|
41
|
+
}
|
|
42
|
+
getName() {
|
|
43
|
+
return `${this.device.name}`;
|
|
44
|
+
}
|
|
45
|
+
getDefaultCyclingMode() {
|
|
46
|
+
return new power_meter_1.default(this);
|
|
47
|
+
}
|
|
48
|
+
getSupportedCyclingModes() {
|
|
49
|
+
return [power_meter_1.default];
|
|
50
|
+
}
|
|
51
|
+
mapData(deviceData) {
|
|
52
|
+
const data = {
|
|
53
|
+
isPedalling: false,
|
|
54
|
+
power: 0,
|
|
55
|
+
pedalRpm: undefined,
|
|
56
|
+
speed: 0,
|
|
57
|
+
heartrate: 0,
|
|
58
|
+
distanceInternal: 0,
|
|
59
|
+
slope: undefined,
|
|
60
|
+
time: undefined
|
|
61
|
+
};
|
|
62
|
+
data.power = (deviceData.instantaneousPower !== undefined ? deviceData.instantaneousPower : data.power);
|
|
63
|
+
data.pedalRpm = (deviceData.rpm !== undefined ? deviceData.rpm : data.pedalRpm);
|
|
64
|
+
data.time = (deviceData.time !== undefined ? deviceData.time : data.time);
|
|
65
|
+
data.isPedalling = data.pedalRpm > 0 || (data.pedalRpm === undefined && data.power > 0);
|
|
66
|
+
return data;
|
|
67
|
+
}
|
|
68
|
+
transformData(bikeData) {
|
|
69
|
+
if (bikeData === undefined)
|
|
70
|
+
return;
|
|
71
|
+
let distance = 0;
|
|
72
|
+
if (this.distanceInternal !== undefined && bikeData.distanceInternal !== undefined) {
|
|
73
|
+
distance = Math.round(bikeData.distanceInternal - this.distanceInternal);
|
|
74
|
+
}
|
|
75
|
+
if (bikeData.distanceInternal !== undefined)
|
|
76
|
+
this.distanceInternal = bikeData.distanceInternal;
|
|
77
|
+
let data = {
|
|
78
|
+
speed: bikeData.speed,
|
|
79
|
+
slope: bikeData.slope,
|
|
80
|
+
power: bikeData.power !== undefined ? Math.round(bikeData.power) : undefined,
|
|
81
|
+
cadence: bikeData.pedalRpm !== undefined ? Math.round(bikeData.pedalRpm) : undefined,
|
|
82
|
+
distance,
|
|
83
|
+
timestamp: Date.now()
|
|
84
|
+
};
|
|
85
|
+
return data;
|
|
86
|
+
}
|
|
87
|
+
start(props) {
|
|
88
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
89
|
+
if (this.ble.isScanning()) {
|
|
90
|
+
this.logger.logEvent({ message: 'stop previous scan', isScanning: this.ble.isScanning() });
|
|
91
|
+
yield this.ble.stopScan();
|
|
92
|
+
}
|
|
93
|
+
const connected = yield this.connect();
|
|
94
|
+
if (!connected)
|
|
95
|
+
throw new Error(`could not start device, reason:could not connect`);
|
|
96
|
+
const comms = this.device;
|
|
97
|
+
this.logger.logEvent({ message: 'start requested', profile: this.getProfile(), props });
|
|
98
|
+
try {
|
|
99
|
+
if (comms) {
|
|
100
|
+
comms.on('data', (data) => {
|
|
101
|
+
this.onDeviceData(data);
|
|
102
|
+
});
|
|
103
|
+
return true;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
catch (err) {
|
|
107
|
+
this.logger.logEvent({ message: 'start result: error', error: err.message, profile: this.getProfile() });
|
|
108
|
+
throw new Error(`could not start device, reason:${err.message}`);
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
BleEliteAdapter.INCYCLIST_PROFILE_NAME = 'Smart Trainer';
|
|
114
|
+
BleEliteAdapter.controllers = {
|
|
115
|
+
modes: [power_meter_1.default],
|
|
116
|
+
default: power_meter_1.default
|
|
117
|
+
};
|
|
118
|
+
exports.default = BleEliteAdapter;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { LegacyProfile } from "../../antv2/types";
|
|
2
|
+
import { BleComms } from "../base/comms";
|
|
3
|
+
import { CrankData, PowerData } from "../cp";
|
|
4
|
+
import { BleProtocol, IBlePeripheralConnector } from "../types";
|
|
5
|
+
export default class BleEliteDevice extends BleComms {
|
|
6
|
+
static protocol: BleProtocol;
|
|
7
|
+
static services: string[];
|
|
8
|
+
static characteristics: string[];
|
|
9
|
+
static detectionPriority: number;
|
|
10
|
+
instantaneousPower: number;
|
|
11
|
+
balance: number;
|
|
12
|
+
accTorque: number;
|
|
13
|
+
rpm: number;
|
|
14
|
+
timeOffset: number;
|
|
15
|
+
time: number;
|
|
16
|
+
currentCrankData: CrankData;
|
|
17
|
+
prevCrankData: CrankData;
|
|
18
|
+
constructor(props?: any);
|
|
19
|
+
static isMatching(characteristics: string[]): boolean;
|
|
20
|
+
getProfile(): LegacyProfile;
|
|
21
|
+
getProtocol(): BleProtocol;
|
|
22
|
+
getServiceUUids(): string[];
|
|
23
|
+
parseCrankData(crankData: any): {
|
|
24
|
+
rpm: number;
|
|
25
|
+
time: any;
|
|
26
|
+
};
|
|
27
|
+
parsePower(_data: Uint8Array): PowerData;
|
|
28
|
+
onData(characteristic: string, data: Buffer): boolean;
|
|
29
|
+
subscribeAll(conn?: IBlePeripheralConnector): Promise<void>;
|
|
30
|
+
reset(): void;
|
|
31
|
+
}
|