incyclist-devices 3.0.20 → 3.0.22
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/cjs/antv2/sc/adapter.js +41 -0
- package/lib/cjs/antv2/spd/adapter.js +39 -0
- package/lib/cjs/base/consts.js +2 -1
- package/lib/cjs/ble/base/adapter.js +5 -8
- package/lib/cjs/ble/base/sensor.js +12 -1
- package/lib/cjs/ble/characteristics/csc/measurement.js +6 -2
- package/lib/cjs/ble/csc/adapter.js +29 -2
- package/lib/cjs/ble/csc/sensor.js +9 -2
- package/lib/cjs/ble/fm/adapter.js +3 -0
- package/lib/cjs/ble/utils.js +4 -0
- package/lib/cjs/ble/wahoo/adapter.js +10 -0
- package/lib/cjs/ble/wahoo/sensor.js +35 -3
- package/lib/cjs/modes/speed.js +133 -0
- package/lib/cjs/types/sensor.js +2 -0
- package/lib/esm/antv2/sc/adapter.js +41 -0
- package/lib/esm/antv2/spd/adapter.js +39 -0
- package/lib/esm/base/consts.js +1 -0
- package/lib/esm/ble/base/adapter.js +5 -8
- package/lib/esm/ble/base/sensor.js +12 -1
- package/lib/esm/ble/characteristics/csc/measurement.js +6 -2
- package/lib/esm/ble/csc/adapter.js +29 -2
- package/lib/esm/ble/csc/sensor.js +9 -2
- package/lib/esm/ble/fm/adapter.js +3 -0
- package/lib/esm/ble/utils.js +4 -0
- package/lib/esm/ble/wahoo/adapter.js +10 -0
- package/lib/esm/ble/wahoo/sensor.js +35 -3
- package/lib/esm/modes/speed.js +127 -0
- package/lib/types/antv2/sc/adapter.d.ts +6 -1
- package/lib/types/antv2/spd/adapter.d.ts +6 -1
- package/lib/types/base/consts.d.ts +1 -0
- package/lib/types/ble/base/sensor.d.ts +2 -0
- package/lib/types/ble/characteristics/csc/measurement.d.ts +1 -0
- package/lib/types/ble/csc/adapter.d.ts +5 -1
- package/lib/types/ble/csc/sensor.d.ts +6 -2
- package/lib/types/ble/types.d.ts +1 -0
- package/lib/types/ble/wahoo/adapter.d.ts +3 -1
- package/lib/types/ble/wahoo/sensor.d.ts +5 -0
- package/lib/types/modes/speed.d.ts +43 -0
- package/lib/types/types/index.d.ts +1 -0
- package/lib/types/types/sensor.d.ts +4 -0
- package/package.json +1 -1
- package/lib/cjs/serial/base/serialport.js +0 -113
- package/lib/esm/bindings/crypto/index.js +0 -14
- package/lib/esm/modes/power-meter copy.js +0 -33
- package/lib/esm/serial/base/serialport.js +0 -73
- package/lib/types/bindings/crypto/index.d.ts +0 -8
- package/lib/types/ble/bindings/crypto/types.d.ts +0 -45
- package/lib/types/modes/power-meter copy.d.ts +0 -15
- package/lib/types/serial/base/serialport.d.ts +0 -16
- /package/lib/esm/{ble/bindings/crypto/types.js → types/sensor.js} +0 -0
|
@@ -5,10 +5,15 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
const adapter_js_1 = __importDefault(require("../base/adapter.js"));
|
|
7
7
|
const index_js_1 = require("../../types/index.js");
|
|
8
|
+
const speed_js_1 = __importDefault(require("../../modes/speed.js"));
|
|
8
9
|
class AntSpdAdapter extends adapter_js_1.default {
|
|
9
10
|
static INCYCLIST_PROFILE_NAME = 'Speed + Cadence Sensor';
|
|
10
11
|
static ANT_PROFILE_NAME = 'SC';
|
|
11
12
|
static CAPABILITIES = [index_js_1.IncyclistCapability.Speed, index_js_1.IncyclistCapability.Cadence];
|
|
13
|
+
static controllers = {
|
|
14
|
+
modes: [speed_js_1.default],
|
|
15
|
+
default: speed_js_1.default
|
|
16
|
+
};
|
|
12
17
|
mapToAdapterData(deviceData) {
|
|
13
18
|
if (deviceData.CalculatedSpeed !== undefined) {
|
|
14
19
|
this.data.speed = deviceData.CalculatedSpeed * 3.6;
|
|
@@ -22,9 +27,45 @@ class AntSpdAdapter extends adapter_js_1.default {
|
|
|
22
27
|
this.data.timestamp = Date.now();
|
|
23
28
|
}
|
|
24
29
|
}
|
|
30
|
+
mapData(deviceData) {
|
|
31
|
+
const data = {
|
|
32
|
+
isPedalling: false,
|
|
33
|
+
power: 0,
|
|
34
|
+
pedalRpm: 0,
|
|
35
|
+
speed: 0
|
|
36
|
+
};
|
|
37
|
+
data.pedalRpm = (deviceData.CalculatedCadence !== undefined ? deviceData.CalculatedCadence : data.pedalRpm);
|
|
38
|
+
data.speed = (deviceData.CalculatedSpeed !== undefined ? deviceData.CalculatedSpeed * 3.6 : data.speed);
|
|
39
|
+
data.time = (deviceData.SpeedEventTime !== undefined ? deviceData.SpeedEventTime : data.time);
|
|
40
|
+
data.isPedalling = (data.pedalRpm ?? 0) > 0 || data.speed > 0;
|
|
41
|
+
return data;
|
|
42
|
+
}
|
|
43
|
+
transformData(bikeData) {
|
|
44
|
+
if (bikeData === undefined)
|
|
45
|
+
return {};
|
|
46
|
+
let data = {
|
|
47
|
+
speed: bikeData.speed,
|
|
48
|
+
power: bikeData.power,
|
|
49
|
+
cadence: bikeData.pedalRpm,
|
|
50
|
+
timestamp: Date.now()
|
|
51
|
+
};
|
|
52
|
+
if (bikeData.time)
|
|
53
|
+
data.deviceTime = bikeData.time;
|
|
54
|
+
this.data = data;
|
|
55
|
+
return data;
|
|
56
|
+
}
|
|
25
57
|
hasData() {
|
|
26
58
|
return ((this.deviceData.CalculatedSpeed !== undefined && this.deviceData.CalculatedSpeed !== null) ||
|
|
27
59
|
(this.deviceData.CalculatedCadence !== undefined && this.deviceData.CalculatedCadence !== null));
|
|
28
60
|
}
|
|
61
|
+
setCyclingMode(mode, settings, sendInitCommands) {
|
|
62
|
+
super.setCyclingMode(mode, settings, sendInitCommands);
|
|
63
|
+
try {
|
|
64
|
+
const sensor = this.sensor;
|
|
65
|
+
const wc = this.getCyclingMode().getSetting('wc');
|
|
66
|
+
sensor.setWheelCircumference(wc / 1000);
|
|
67
|
+
}
|
|
68
|
+
catch { }
|
|
69
|
+
}
|
|
29
70
|
}
|
|
30
71
|
exports.default = AntSpdAdapter;
|
|
@@ -5,10 +5,15 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
const adapter_js_1 = __importDefault(require("../base/adapter.js"));
|
|
7
7
|
const index_js_1 = require("../../types/index.js");
|
|
8
|
+
const speed_js_1 = __importDefault(require("../../modes/speed.js"));
|
|
8
9
|
class AntSpdAdapter extends adapter_js_1.default {
|
|
9
10
|
static INCYCLIST_PROFILE_NAME = 'Speed Sensor';
|
|
10
11
|
static ANT_PROFILE_NAME = 'SPD';
|
|
11
12
|
static CAPABILITIES = [index_js_1.IncyclistCapability.Speed];
|
|
13
|
+
static controllers = {
|
|
14
|
+
modes: [speed_js_1.default],
|
|
15
|
+
default: speed_js_1.default
|
|
16
|
+
};
|
|
12
17
|
mapToAdapterData(deviceData) {
|
|
13
18
|
if (deviceData.CalculatedSpeed !== undefined) {
|
|
14
19
|
this.data.speed = deviceData.CalculatedSpeed * 3.6;
|
|
@@ -18,8 +23,42 @@ class AntSpdAdapter extends adapter_js_1.default {
|
|
|
18
23
|
this.data.deviceDistanceCounter = deviceData.CalculatedDistance;
|
|
19
24
|
}
|
|
20
25
|
}
|
|
26
|
+
mapData(deviceData) {
|
|
27
|
+
const data = {
|
|
28
|
+
isPedalling: false,
|
|
29
|
+
power: 0,
|
|
30
|
+
pedalRpm: 0,
|
|
31
|
+
speed: 0
|
|
32
|
+
};
|
|
33
|
+
data.speed = (deviceData.CalculatedSpeed !== undefined ? deviceData.CalculatedSpeed * 3.6 : data.speed);
|
|
34
|
+
data.time = (deviceData.SpeedEventTime !== undefined ? deviceData.SpeedEventTime : data.time);
|
|
35
|
+
data.isPedalling = data.speed > 0;
|
|
36
|
+
return data;
|
|
37
|
+
}
|
|
38
|
+
transformData(bikeData) {
|
|
39
|
+
if (bikeData === undefined)
|
|
40
|
+
return {};
|
|
41
|
+
let data = {
|
|
42
|
+
speed: bikeData.speed,
|
|
43
|
+
power: bikeData.power,
|
|
44
|
+
timestamp: Date.now()
|
|
45
|
+
};
|
|
46
|
+
if (bikeData.time)
|
|
47
|
+
data.deviceTime = bikeData.time;
|
|
48
|
+
this.data = data;
|
|
49
|
+
return data;
|
|
50
|
+
}
|
|
21
51
|
hasData() {
|
|
22
52
|
return this.deviceData.CalculatedSpeed !== undefined && this.deviceData.CalculatedSpeed !== null;
|
|
23
53
|
}
|
|
54
|
+
setCyclingMode(mode, settings, sendInitCommands) {
|
|
55
|
+
super.setCyclingMode(mode, settings, sendInitCommands);
|
|
56
|
+
try {
|
|
57
|
+
const sensor = this.sensor;
|
|
58
|
+
const wc = this.getCyclingMode().getSetting('wc');
|
|
59
|
+
sensor.setWheelCircumference(wc / 1000);
|
|
60
|
+
}
|
|
61
|
+
catch { }
|
|
62
|
+
}
|
|
24
63
|
}
|
|
25
64
|
exports.default = AntSpdAdapter;
|
package/lib/cjs/base/consts.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.DEFAULT_PROPS = exports.DEFAULT_USER_WEIGHT = exports.DEFAULT_BIKE_WEIGHT = void 0;
|
|
3
|
+
exports.DEFAULT_PROPS = exports.DEFAULT_WHEEL_CIRCUMFERENCE = exports.DEFAULT_USER_WEIGHT = exports.DEFAULT_BIKE_WEIGHT = void 0;
|
|
4
4
|
exports.DEFAULT_BIKE_WEIGHT = 10;
|
|
5
5
|
exports.DEFAULT_USER_WEIGHT = 75;
|
|
6
|
+
exports.DEFAULT_WHEEL_CIRCUMFERENCE = 2.118;
|
|
6
7
|
exports.DEFAULT_PROPS = {
|
|
7
8
|
userWeight: exports.DEFAULT_USER_WEIGHT,
|
|
8
9
|
bikeWeight: exports.DEFAULT_BIKE_WEIGHT
|
|
@@ -359,17 +359,14 @@ class BleAdapter extends adpater_js_1.default {
|
|
|
359
359
|
sensor.off('data', this.onDeviceDataHandler);
|
|
360
360
|
let connected = false;
|
|
361
361
|
if (sensor.isReconnectBusy()) {
|
|
362
|
-
connected = await sensor.reconnectSensor(
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
return
|
|
362
|
+
connected = await sensor.reconnectSensor(true);
|
|
363
|
+
if (connected) {
|
|
364
|
+
sensor.on('data', this.onDeviceDataHandler);
|
|
365
|
+
}
|
|
366
|
+
return connected;
|
|
367
367
|
}
|
|
368
368
|
await sensor.getPeripheral().disconnect();
|
|
369
369
|
const success = await super.restart(pause);
|
|
370
|
-
if (success) {
|
|
371
|
-
sensor.on('data', this.onDeviceDataHandler);
|
|
372
|
-
}
|
|
373
370
|
return success;
|
|
374
371
|
}
|
|
375
372
|
async stop() {
|
|
@@ -12,6 +12,7 @@ class TBleSensor extends node_events_1.EventEmitter {
|
|
|
12
12
|
stopRequested = false;
|
|
13
13
|
subscribeSuccess = false;
|
|
14
14
|
reconnectPromise;
|
|
15
|
+
connectPromise;
|
|
15
16
|
onDataHandler;
|
|
16
17
|
logEvent(event, ...args) {
|
|
17
18
|
try {
|
|
@@ -76,7 +77,8 @@ class TBleSensor extends node_events_1.EventEmitter {
|
|
|
76
77
|
this.logEvent({ message: 'no peripheral' });
|
|
77
78
|
return false;
|
|
78
79
|
}
|
|
79
|
-
|
|
80
|
+
this.connectPromise = this.connectPromise ?? this.peripheral.connect();
|
|
81
|
+
const connected = await this.connectPromise;
|
|
80
82
|
if (!connected)
|
|
81
83
|
return false;
|
|
82
84
|
if (!reconnect)
|
|
@@ -128,6 +130,9 @@ class TBleSensor extends node_events_1.EventEmitter {
|
|
|
128
130
|
let connected = false;
|
|
129
131
|
let subscribed = false;
|
|
130
132
|
let success = false;
|
|
133
|
+
if (connectionLost) {
|
|
134
|
+
this.emit('connctionLost');
|
|
135
|
+
}
|
|
131
136
|
await (0, utils_js_1.sleep)(500);
|
|
132
137
|
do {
|
|
133
138
|
try {
|
|
@@ -147,6 +152,9 @@ class TBleSensor extends node_events_1.EventEmitter {
|
|
|
147
152
|
if (!this.stopRequested && !success)
|
|
148
153
|
this.logEvent({ message: 'reconnect sensor retry', name, address });
|
|
149
154
|
} while (!success || this.stopRequested);
|
|
155
|
+
if (connectionLost && success) {
|
|
156
|
+
this.emit('connctionRecovered');
|
|
157
|
+
}
|
|
150
158
|
this.logEvent({ message: 'reconnect sensor completed', success, stopRequested: this.stopRequested });
|
|
151
159
|
return success;
|
|
152
160
|
}
|
|
@@ -156,6 +164,9 @@ class TBleSensor extends node_events_1.EventEmitter {
|
|
|
156
164
|
isConnected() {
|
|
157
165
|
return this.peripheral?.isConnected();
|
|
158
166
|
}
|
|
167
|
+
isReady() {
|
|
168
|
+
return this.isConnected() && this.connectPromise === undefined && this.reconnectPromise === undefined;
|
|
169
|
+
}
|
|
159
170
|
isSubscribed() {
|
|
160
171
|
return this.subscribeSuccess;
|
|
161
172
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.CscMeasurement = void 0;
|
|
4
|
+
const consts_js_1 = require("../../../base/consts.js");
|
|
4
5
|
class CscMeasurement {
|
|
5
6
|
data;
|
|
6
7
|
prevCrankData = undefined;
|
|
@@ -8,13 +9,16 @@ class CscMeasurement {
|
|
|
8
9
|
prevWheelData = undefined;
|
|
9
10
|
currentWheelData = undefined;
|
|
10
11
|
timeOffset = 0;
|
|
11
|
-
cw =
|
|
12
|
+
cw = consts_js_1.DEFAULT_WHEEL_CIRCUMFERENCE;
|
|
12
13
|
constructor(data = {}) {
|
|
13
14
|
this.data = data;
|
|
14
15
|
}
|
|
15
16
|
setWheelCircumference(wheelCircumference) {
|
|
16
17
|
this.cw = wheelCircumference;
|
|
17
18
|
}
|
|
19
|
+
getWheelCircumference() {
|
|
20
|
+
return this.cw;
|
|
21
|
+
}
|
|
18
22
|
parse(buffer, features) {
|
|
19
23
|
const data = Buffer.from(buffer);
|
|
20
24
|
let offset = 0;
|
|
@@ -89,7 +93,7 @@ class CscMeasurement {
|
|
|
89
93
|
}
|
|
90
94
|
const seconds = time / 1024;
|
|
91
95
|
const rps = revs / seconds;
|
|
92
|
-
speed = rps * this.cw;
|
|
96
|
+
speed = rps * this.cw * 3.6;
|
|
93
97
|
}
|
|
94
98
|
else if (p.cntUpdateMissing < 0 || p.cntUpdateMissing > 2) {
|
|
95
99
|
speed = 0;
|
|
@@ -8,9 +8,14 @@ const gd_eventlog_1 = require("gd-eventlog");
|
|
|
8
8
|
const adapter_js_1 = __importDefault(require("../base/adapter.js"));
|
|
9
9
|
const index_js_1 = require("../../types/index.js");
|
|
10
10
|
const sensor_js_1 = require("./sensor.js");
|
|
11
|
+
const speed_js_1 = __importDefault(require("../../modes/speed.js"));
|
|
11
12
|
class BleCSCAdapter extends adapter_js_1.default {
|
|
12
13
|
static INCYCLIST_PROFILE_NAME = 'Speed + Cadence Sensor';
|
|
13
14
|
static CAPABILITIES = [index_js_1.IncyclistCapability.Speed, index_js_1.IncyclistCapability.Cadence];
|
|
15
|
+
static controllers = {
|
|
16
|
+
modes: [speed_js_1.default],
|
|
17
|
+
default: speed_js_1.default
|
|
18
|
+
};
|
|
14
19
|
constructor(settings, props) {
|
|
15
20
|
super(settings, props);
|
|
16
21
|
this.logger = new gd_eventlog_1.EventLogger('Ble-CSC');
|
|
@@ -48,8 +53,30 @@ class BleCSCAdapter extends adapter_js_1.default {
|
|
|
48
53
|
return this.getName();
|
|
49
54
|
}
|
|
50
55
|
mapData(deviceData) {
|
|
51
|
-
const { cadence, speed } = deviceData;
|
|
52
|
-
return { cadence, speed
|
|
56
|
+
const { cadence, speed = 0 } = deviceData;
|
|
57
|
+
return { cadence, speed };
|
|
58
|
+
}
|
|
59
|
+
transformData(bikeData) {
|
|
60
|
+
if (bikeData === undefined)
|
|
61
|
+
return {};
|
|
62
|
+
let data = {
|
|
63
|
+
speed: bikeData.speed,
|
|
64
|
+
power: bikeData.power,
|
|
65
|
+
cadence: bikeData.pedalRpm,
|
|
66
|
+
timestamp: Date.now()
|
|
67
|
+
};
|
|
68
|
+
if (bikeData.time)
|
|
69
|
+
data.deviceTime = bikeData.time;
|
|
70
|
+
this.data = data;
|
|
71
|
+
return data;
|
|
72
|
+
}
|
|
73
|
+
setCyclingMode(mode, settings, sendInitCommands) {
|
|
74
|
+
super.setCyclingMode(mode, settings, sendInitCommands);
|
|
75
|
+
try {
|
|
76
|
+
const wc = this.getCyclingMode().getSetting('wc');
|
|
77
|
+
this.getSensor().setWheelCircumference(wc / 1000);
|
|
78
|
+
}
|
|
79
|
+
catch { }
|
|
53
80
|
}
|
|
54
81
|
}
|
|
55
82
|
exports.BleCSCAdapter = BleCSCAdapter;
|
|
@@ -15,12 +15,12 @@ class BleCyclingSpeedCadenceDevice extends sensor_js_1.TBleSensor {
|
|
|
15
15
|
data;
|
|
16
16
|
parsers = {};
|
|
17
17
|
featureParser;
|
|
18
|
+
measurement = new measurement_js_1.CscMeasurement();
|
|
18
19
|
constructor(peripheral, props) {
|
|
19
20
|
super(peripheral, props);
|
|
20
21
|
this.data = {};
|
|
21
|
-
const measurement = new measurement_js_1.CscMeasurement();
|
|
22
22
|
this.featureParser = new features_js_1.CscFeatures();
|
|
23
|
-
this.parsers[(0, utils_js_1.beautifyUUID)(consts_js_1.CSC_MEASUREMENT)] = measurement;
|
|
23
|
+
this.parsers[(0, utils_js_1.beautifyUUID)(consts_js_1.CSC_MEASUREMENT)] = this.measurement;
|
|
24
24
|
}
|
|
25
25
|
getRequiredCharacteristics() {
|
|
26
26
|
return [consts_js_1.CSC_MEASUREMENT];
|
|
@@ -34,6 +34,7 @@ class BleCyclingSpeedCadenceDevice extends sensor_js_1.TBleSensor {
|
|
|
34
34
|
}
|
|
35
35
|
catch (err) {
|
|
36
36
|
this.logEvent({ message: 'read failed', characteristic: consts_js_1.CSC_FEATURE, reason: err.message });
|
|
37
|
+
return {};
|
|
37
38
|
}
|
|
38
39
|
}
|
|
39
40
|
onData(characteristic, characteristicData) {
|
|
@@ -54,5 +55,11 @@ class BleCyclingSpeedCadenceDevice extends sensor_js_1.TBleSensor {
|
|
|
54
55
|
reset() {
|
|
55
56
|
this.data = {};
|
|
56
57
|
}
|
|
58
|
+
async setWheelCircumference(wheelCircumference) {
|
|
59
|
+
this.measurement.setWheelCircumference(wheelCircumference);
|
|
60
|
+
}
|
|
61
|
+
getWheelCircumference() {
|
|
62
|
+
return this.measurement.getWheelCircumference();
|
|
63
|
+
}
|
|
57
64
|
}
|
|
58
65
|
exports.BleCyclingSpeedCadenceDevice = BleCyclingSpeedCadenceDevice;
|
|
@@ -327,6 +327,9 @@ class BleFmAdapter extends adapter_js_1.default {
|
|
|
327
327
|
delete this.promiseSendUpdate;
|
|
328
328
|
return confirmed;
|
|
329
329
|
}
|
|
330
|
+
if (!device.isReady()) {
|
|
331
|
+
this.logEvent({ message: 'send bike update failed', reason: 'not connected' });
|
|
332
|
+
}
|
|
330
333
|
}
|
|
331
334
|
catch (error) {
|
|
332
335
|
const err = error;
|
package/lib/cjs/ble/utils.js
CHANGED
|
@@ -49,6 +49,8 @@ function getCharachteristicsInfo(c) {
|
|
|
49
49
|
return `${serviceStr}${uuid}${nameStr} ${properties}`;
|
|
50
50
|
}
|
|
51
51
|
const parseUUID = (str) => {
|
|
52
|
+
if (!str)
|
|
53
|
+
return str;
|
|
52
54
|
const uuid = str.toUpperCase();
|
|
53
55
|
if (str.startsWith('0x')) {
|
|
54
56
|
const hex = uuid.slice(2);
|
|
@@ -105,6 +107,8 @@ const beautifyUUID = (str, withX = false) => {
|
|
|
105
107
|
};
|
|
106
108
|
exports.beautifyUUID = beautifyUUID;
|
|
107
109
|
const fullUUID = (str) => {
|
|
110
|
+
if (!str)
|
|
111
|
+
return str;
|
|
108
112
|
const uuid = (0, exports.parseUUID)(str);
|
|
109
113
|
const parts = [
|
|
110
114
|
uuid.substring(0, 8),
|
|
@@ -7,6 +7,7 @@ const gd_eventlog_1 = require("gd-eventlog");
|
|
|
7
7
|
const index_js_1 = require("../fm/index.js");
|
|
8
8
|
const sensor_js_1 = __importDefault(require("./sensor.js"));
|
|
9
9
|
const index_js_2 = require("../../types/index.js");
|
|
10
|
+
const calculations_js_1 = __importDefault(require("../../utils/calculations.js"));
|
|
10
11
|
class BleWahooAdapter extends index_js_1.BleFmAdapter {
|
|
11
12
|
static INCYCLIST_PROFILE_NAME = 'Smart Trainer';
|
|
12
13
|
constructor(settings, props) {
|
|
@@ -29,6 +30,15 @@ class BleWahooAdapter extends index_js_1.BleFmAdapter {
|
|
|
29
30
|
updateSensor(peripheral) {
|
|
30
31
|
this.device = new sensor_js_1.default(peripheral, { logger: this.logger });
|
|
31
32
|
}
|
|
33
|
+
mapData(deviceData) {
|
|
34
|
+
const data = super.mapData(deviceData);
|
|
35
|
+
if (!deviceData.instantaneousPower && !deviceData.cadence && (deviceData.speed ?? 0) > 0) {
|
|
36
|
+
const m = this.getWeight();
|
|
37
|
+
data.isPedalling = true;
|
|
38
|
+
data.power = calculations_js_1.default.calculatePower(m, data.speed / 3.6, 0);
|
|
39
|
+
}
|
|
40
|
+
return data;
|
|
41
|
+
}
|
|
32
42
|
async checkCapabilities() {
|
|
33
43
|
this.logEvent({ message: 'device capabilities updated', name: this.getSettings().name, interface: this.getSettings().interface, capabilities: this.capabilities });
|
|
34
44
|
}
|
|
@@ -23,6 +23,9 @@ class BleWahooDevice extends sensor_js_1.default {
|
|
|
23
23
|
isSimMode;
|
|
24
24
|
isRequestControlBusy = false;
|
|
25
25
|
weight = consts_js_1.DEFAULT_BIKE_WEIGHT + consts_js_1.DEFAULT_USER_WEIGHT;
|
|
26
|
+
prevWheelRevolutions = undefined;
|
|
27
|
+
prevWheelEventTime = undefined;
|
|
28
|
+
wheelCircumference = 2.096;
|
|
26
29
|
simModeSettings;
|
|
27
30
|
constructor(peripheral, props) {
|
|
28
31
|
super(peripheral, props);
|
|
@@ -163,17 +166,40 @@ class BleWahooDevice extends sensor_js_1.default {
|
|
|
163
166
|
}
|
|
164
167
|
parsePower(_data) {
|
|
165
168
|
const data = Buffer.from(_data);
|
|
169
|
+
let balance;
|
|
170
|
+
let accTorque;
|
|
171
|
+
let speed;
|
|
172
|
+
let wheelRevolutions, wheelEventTime;
|
|
166
173
|
try {
|
|
167
174
|
let offset = 4;
|
|
168
175
|
const flags = data.readUInt16LE(0);
|
|
169
176
|
this.data.instantaneousPower = data.readUInt16LE(2);
|
|
170
177
|
if (flags & 0x1)
|
|
171
|
-
data.readUInt8(offset++);
|
|
178
|
+
balance = data.readUInt8(offset++);
|
|
172
179
|
if (flags & 0x4) {
|
|
180
|
+
accTorque = data.readUInt16LE(offset);
|
|
173
181
|
offset += 2;
|
|
174
182
|
}
|
|
175
183
|
if (flags & 0x10) {
|
|
184
|
+
wheelRevolutions = data.readUInt32LE(offset);
|
|
185
|
+
wheelEventTime = data.readUInt16LE(offset + 4);
|
|
176
186
|
offset += 6;
|
|
187
|
+
if (this.prevWheelRevolutions !== undefined && this.prevWheelEventTime !== undefined) {
|
|
188
|
+
const wheelRevDelta = wheelRevolutions - this.prevWheelRevolutions;
|
|
189
|
+
const wheelTimeDelta = (wheelEventTime - this.prevWheelEventTime + 65536) % 65536;
|
|
190
|
+
const timeSeconds = (wheelTimeDelta / 1024);
|
|
191
|
+
if (timeSeconds > 0 && wheelRevDelta >= 0) {
|
|
192
|
+
const distance = wheelRevDelta * this.wheelCircumference;
|
|
193
|
+
speed = distance / timeSeconds * 3.6;
|
|
194
|
+
this.data.speed = speed;
|
|
195
|
+
}
|
|
196
|
+
else if (timeSeconds > 0 && wheelRevDelta === 0) {
|
|
197
|
+
speed = 0;
|
|
198
|
+
this.data.speed = speed;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
this.prevWheelRevolutions = wheelRevolutions;
|
|
202
|
+
this.prevWheelEventTime = wheelEventTime;
|
|
177
203
|
}
|
|
178
204
|
if (flags & 0x20) {
|
|
179
205
|
const crankData = {
|
|
@@ -188,8 +214,8 @@ class BleWahooDevice extends sensor_js_1.default {
|
|
|
188
214
|
catch (err) {
|
|
189
215
|
this.logEvent({ message: 'error', fn: 'parsePower', error: err.message, stack: err.stack });
|
|
190
216
|
}
|
|
191
|
-
const { instantaneousPower, cadence, time } = this.data;
|
|
192
|
-
return { instantaneousPower, cadence, time, raw: data.toString('hex') };
|
|
217
|
+
const { instantaneousPower, cadence, speed: dataSpeed, time } = this.data;
|
|
218
|
+
return { instantaneousPower, speed: dataSpeed, cadence, time, raw: '2a63:' + data.toString('hex') };
|
|
193
219
|
}
|
|
194
220
|
supportsHeartRate() {
|
|
195
221
|
try {
|
|
@@ -341,5 +367,11 @@ class BleWahooDevice extends sensor_js_1.default {
|
|
|
341
367
|
return false;
|
|
342
368
|
}
|
|
343
369
|
}
|
|
370
|
+
async setWheelCircumference(wheelCircumference) {
|
|
371
|
+
this.wheelCircumference = wheelCircumference;
|
|
372
|
+
}
|
|
373
|
+
getWheelCircumference() {
|
|
374
|
+
return this.wheelCircumference;
|
|
375
|
+
}
|
|
344
376
|
}
|
|
345
377
|
exports.default = BleWahooDevice;
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const base_js_1 = require("./base.js");
|
|
7
|
+
const types_js_1 = require("./types.js");
|
|
8
|
+
const calculations_js_1 = __importDefault(require("../utils/calculations.js"));
|
|
9
|
+
const consts_js_1 = require("../base/consts.js");
|
|
10
|
+
class SpeedCyclingMode extends base_js_1.CyclingModeBase {
|
|
11
|
+
data;
|
|
12
|
+
prevUpdateTS = 0;
|
|
13
|
+
prevRequest;
|
|
14
|
+
static config = {
|
|
15
|
+
name: "SpeedSensor",
|
|
16
|
+
description: "Calculates power based on speed and slope.",
|
|
17
|
+
properties: [
|
|
18
|
+
{ key: 'bikeType', name: 'Bike Type', description: '', type: types_js_1.CyclingModeProperyType.SingleSelect, options: ['Race', 'Mountain', 'Triathlon'], default: 'Race' },
|
|
19
|
+
{ key: 'wc', name: 'Wheel Circumference', description: 'Circumference in mm', type: types_js_1.CyclingModeProperyType.Integer, default: Math.ceil(consts_js_1.DEFAULT_WHEEL_CIRCUMFERENCE * 1000) },
|
|
20
|
+
]
|
|
21
|
+
};
|
|
22
|
+
constructor(adapter, props) {
|
|
23
|
+
super(adapter, props);
|
|
24
|
+
this.data = { speed: 0, power: 0, distanceInternal: 0, pedalRpm: 0, isPedalling: false, heartrate: 0 };
|
|
25
|
+
}
|
|
26
|
+
getData() {
|
|
27
|
+
return this.data;
|
|
28
|
+
}
|
|
29
|
+
getSlope() {
|
|
30
|
+
const { slope } = this.data;
|
|
31
|
+
return slope || 0;
|
|
32
|
+
}
|
|
33
|
+
getWeight() {
|
|
34
|
+
const a = this.adapter;
|
|
35
|
+
const defaultWeight = consts_js_1.DEFAULT_BIKE_WEIGHT + consts_js_1.DEFAULT_USER_WEIGHT;
|
|
36
|
+
const m = (a) ? a.getWeight() || defaultWeight : defaultWeight;
|
|
37
|
+
return m;
|
|
38
|
+
}
|
|
39
|
+
getTimeSinceLastUpdate() {
|
|
40
|
+
const ts = Date.now();
|
|
41
|
+
const duration = this.prevUpdateTS === 0 ? 0 : ((ts - this.prevUpdateTS) / 1000);
|
|
42
|
+
return duration;
|
|
43
|
+
}
|
|
44
|
+
getBikeInitRequest() {
|
|
45
|
+
return {};
|
|
46
|
+
}
|
|
47
|
+
updateData(data, log = true) {
|
|
48
|
+
const prevData = this.data;
|
|
49
|
+
const prevRequest = this.prevRequest ?? {};
|
|
50
|
+
const slope = (prevRequest.slope !== undefined ? prevRequest.slope : prevData.slope ?? 0);
|
|
51
|
+
const { pedalRpm = 0 } = data ?? {};
|
|
52
|
+
const bikeType = this.getSetting('bikeType')?.toLowerCase();
|
|
53
|
+
if (data.isPedalling === false || data.speed === 0) {
|
|
54
|
+
this.data = {
|
|
55
|
+
...data,
|
|
56
|
+
speed: 0,
|
|
57
|
+
isPedalling: false,
|
|
58
|
+
power: 0,
|
|
59
|
+
slope
|
|
60
|
+
};
|
|
61
|
+
this.prevUpdateTS = Date.now();
|
|
62
|
+
return this.data;
|
|
63
|
+
}
|
|
64
|
+
const m = this.getWeight();
|
|
65
|
+
const t = this.getTimeSinceLastUpdate();
|
|
66
|
+
const v = (data.speed ?? 0) / 3.6;
|
|
67
|
+
const power = calculations_js_1.default.calculatePower(m, v, 0, { bikeType });
|
|
68
|
+
const { speed: calcSpeed, distance } = this.calculateSpeedAndDistance(power, slope, m, t, { bikeType });
|
|
69
|
+
const prevTime = this.data.time ?? 0;
|
|
70
|
+
const prevDistance = this.data.distanceInternal ?? 0;
|
|
71
|
+
const updated = {
|
|
72
|
+
...data,
|
|
73
|
+
time: prevTime + t,
|
|
74
|
+
isPedalling: data.isPedalling ?? (v > 0 || pedalRpm > 0),
|
|
75
|
+
slope,
|
|
76
|
+
power,
|
|
77
|
+
speed: calcSpeed,
|
|
78
|
+
distanceInternal: prevDistance + distance
|
|
79
|
+
};
|
|
80
|
+
if (log && this.logger)
|
|
81
|
+
this.logEvent({ message: "updateData result", mode: this.getName(), data: updated, bikeData: data });
|
|
82
|
+
this.data = updated;
|
|
83
|
+
this.prevUpdateTS = Date.now();
|
|
84
|
+
return updated;
|
|
85
|
+
}
|
|
86
|
+
sendBikeUpdate(request) {
|
|
87
|
+
if (request.slope !== undefined) {
|
|
88
|
+
this.data.slope = request.slope;
|
|
89
|
+
}
|
|
90
|
+
return {};
|
|
91
|
+
}
|
|
92
|
+
calculatePowerAndDistance(speed, slope, m, t, props = {}) {
|
|
93
|
+
const prevData = this.getData();
|
|
94
|
+
const vPrev = (prevData.speed || 0) / 3.6;
|
|
95
|
+
const EkinPrev = 1 / 2 * m * vPrev * vPrev;
|
|
96
|
+
const vTarget = (speed || 0) / 3.6;
|
|
97
|
+
const Ekin = 1 / 2 * m * vTarget * vTarget;
|
|
98
|
+
const powerDelta = t !== 0 ? (EkinPrev - Ekin) / t : 0;
|
|
99
|
+
const powerToMaintainSpeed = calculations_js_1.default.calculatePower(m, vPrev, slope, props);
|
|
100
|
+
const power = powerToMaintainSpeed - powerDelta;
|
|
101
|
+
const v = speed / 3.6;
|
|
102
|
+
const distance = v * t;
|
|
103
|
+
this.data.power = power;
|
|
104
|
+
return { power, distance };
|
|
105
|
+
}
|
|
106
|
+
calculateSpeedAndDistance(power, slope, m, t, props = {}) {
|
|
107
|
+
const prevData = this.getData();
|
|
108
|
+
const vPrev = (prevData.speed || 0) / 3.6;
|
|
109
|
+
const EkinPrev = 1 / 2 * m * vPrev * vPrev;
|
|
110
|
+
let powerToMaintainSpeed = calculations_js_1.default.calculatePower(m, vPrev, slope, props);
|
|
111
|
+
if (t >= 30) {
|
|
112
|
+
const speed = calculations_js_1.default.calculateSpeed(m, power, slope, props);
|
|
113
|
+
return { speed, distance: 0 };
|
|
114
|
+
}
|
|
115
|
+
const powerDelta = powerToMaintainSpeed - power;
|
|
116
|
+
const Ekin = EkinPrev - powerDelta * t;
|
|
117
|
+
if (Ekin > 0) {
|
|
118
|
+
const v = Math.sqrt(2 * Ekin / m);
|
|
119
|
+
const speed = v * 3.6;
|
|
120
|
+
const distance = v * t;
|
|
121
|
+
this.data.speed = speed;
|
|
122
|
+
return { speed, distance };
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
const v = vPrev * 0.5;
|
|
126
|
+
const speed = v * 3.6;
|
|
127
|
+
const distance = v * t;
|
|
128
|
+
this.data.speed = speed;
|
|
129
|
+
return { speed, distance };
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
exports.default = SpeedCyclingMode;
|
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
import AntAdapter from "../base/adapter.js";
|
|
2
2
|
import { IncyclistCapability } from "../../types/index.js";
|
|
3
|
+
import SpeedCyclingMode from "../../modes/speed.js";
|
|
3
4
|
export default class AntSpdAdapter extends AntAdapter {
|
|
4
5
|
static INCYCLIST_PROFILE_NAME = 'Speed + Cadence Sensor';
|
|
5
6
|
static ANT_PROFILE_NAME = 'SC';
|
|
6
7
|
static CAPABILITIES = [IncyclistCapability.Speed, IncyclistCapability.Cadence];
|
|
8
|
+
static controllers = {
|
|
9
|
+
modes: [SpeedCyclingMode],
|
|
10
|
+
default: SpeedCyclingMode
|
|
11
|
+
};
|
|
7
12
|
mapToAdapterData(deviceData) {
|
|
8
13
|
if (deviceData.CalculatedSpeed !== undefined) {
|
|
9
14
|
this.data.speed = deviceData.CalculatedSpeed * 3.6;
|
|
@@ -17,8 +22,44 @@ export default class AntSpdAdapter extends AntAdapter {
|
|
|
17
22
|
this.data.timestamp = Date.now();
|
|
18
23
|
}
|
|
19
24
|
}
|
|
25
|
+
mapData(deviceData) {
|
|
26
|
+
const data = {
|
|
27
|
+
isPedalling: false,
|
|
28
|
+
power: 0,
|
|
29
|
+
pedalRpm: 0,
|
|
30
|
+
speed: 0
|
|
31
|
+
};
|
|
32
|
+
data.pedalRpm = (deviceData.CalculatedCadence !== undefined ? deviceData.CalculatedCadence : data.pedalRpm);
|
|
33
|
+
data.speed = (deviceData.CalculatedSpeed !== undefined ? deviceData.CalculatedSpeed * 3.6 : data.speed);
|
|
34
|
+
data.time = (deviceData.SpeedEventTime !== undefined ? deviceData.SpeedEventTime : data.time);
|
|
35
|
+
data.isPedalling = (data.pedalRpm ?? 0) > 0 || data.speed > 0;
|
|
36
|
+
return data;
|
|
37
|
+
}
|
|
38
|
+
transformData(bikeData) {
|
|
39
|
+
if (bikeData === undefined)
|
|
40
|
+
return {};
|
|
41
|
+
let data = {
|
|
42
|
+
speed: bikeData.speed,
|
|
43
|
+
power: bikeData.power,
|
|
44
|
+
cadence: bikeData.pedalRpm,
|
|
45
|
+
timestamp: Date.now()
|
|
46
|
+
};
|
|
47
|
+
if (bikeData.time)
|
|
48
|
+
data.deviceTime = bikeData.time;
|
|
49
|
+
this.data = data;
|
|
50
|
+
return data;
|
|
51
|
+
}
|
|
20
52
|
hasData() {
|
|
21
53
|
return ((this.deviceData.CalculatedSpeed !== undefined && this.deviceData.CalculatedSpeed !== null) ||
|
|
22
54
|
(this.deviceData.CalculatedCadence !== undefined && this.deviceData.CalculatedCadence !== null));
|
|
23
55
|
}
|
|
56
|
+
setCyclingMode(mode, settings, sendInitCommands) {
|
|
57
|
+
super.setCyclingMode(mode, settings, sendInitCommands);
|
|
58
|
+
try {
|
|
59
|
+
const sensor = this.sensor;
|
|
60
|
+
const wc = this.getCyclingMode().getSetting('wc');
|
|
61
|
+
sensor.setWheelCircumference(wc / 1000);
|
|
62
|
+
}
|
|
63
|
+
catch { }
|
|
64
|
+
}
|
|
24
65
|
}
|