incyclist-devices 1.4.53 → 1.4.54

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.
@@ -1,5 +1,41 @@
1
+ /// <reference types="node" />
1
2
  import AntAdapter from '../AntAdapter';
2
3
  import { Queue } from '../../utils';
4
+ import CyclingMode, { IncyclistBikeData } from '../../CyclingMode';
5
+ export declare type AntFEDeviceData = {
6
+ DeviceID: number;
7
+ Temperature?: number;
8
+ ZeroOffset?: number;
9
+ SpinDownTime?: number;
10
+ EquipmentType?: 'Treadmill' | 'Elliptical' | 'StationaryBike' | 'Rower' | 'Climber' | 'NordicSkier' | 'Trainer' | 'General';
11
+ ElapsedTime?: number;
12
+ Distance?: number;
13
+ RealSpeed?: number;
14
+ VirtualSpeed?: number;
15
+ HeartRate?: number;
16
+ HeartRateSource?: 'HandContact' | 'EM' | 'ANT+';
17
+ State?: 'OFF' | 'READY' | 'IN_USE' | 'FINISHED';
18
+ CycleLength?: number;
19
+ Incline?: number;
20
+ Resistance?: number;
21
+ METs?: number;
22
+ CaloricBurnRate?: number;
23
+ Calories?: number;
24
+ _EventCount0x19?: number;
25
+ Cadence?: number;
26
+ AccumulatedPower?: number;
27
+ InstantaneousPower?: number;
28
+ AveragePower?: number;
29
+ TrainerStatus?: number;
30
+ TargetStatus?: 'OnTarget' | 'LowSpeed' | 'HighSpeed';
31
+ HwVersion?: number;
32
+ ManId?: number;
33
+ ModelNum?: number;
34
+ SwVersion?: number;
35
+ SerialNumber?: number;
36
+ PairedDevices?: any[];
37
+ RawData: Buffer;
38
+ };
3
39
  export default class AntFEAdapter extends AntAdapter {
4
40
  started: boolean;
5
41
  starting: boolean;
@@ -8,6 +44,7 @@ export default class AntFEAdapter extends AntAdapter {
8
44
  queue?: Queue<any>;
9
45
  workerId?: any;
10
46
  currentCmd?: any;
47
+ cyclingMode: CyclingMode;
11
48
  constructor(DeviceID: any, port: any, stick: any, protocol: any);
12
49
  isBike(): boolean;
13
50
  isHrm(): boolean;
@@ -15,11 +52,15 @@ export default class AntFEAdapter extends AntAdapter {
15
52
  getProfile(): string;
16
53
  getName(): string;
17
54
  getDisplayName(): string;
55
+ getSupportedCyclingModes(): Array<any>;
56
+ setCyclingMode(mode: string | CyclingMode, settings?: any): void;
57
+ getCyclingMode(): CyclingMode;
58
+ getDefaultCyclingMode(): CyclingMode;
18
59
  onAttached(): void;
19
60
  getLogData(data: any, excludeList: any): any;
20
- onDeviceData(deviceData: any): void;
61
+ onDeviceData(deviceData: AntFEDeviceData): void;
62
+ mapData(deviceData: AntFEDeviceData): IncyclistBikeData;
21
63
  onDeviceEvent(data: any): void;
22
- updateData(data: any, deviceData: any): any;
23
64
  transformData(bikeData: any): any;
24
65
  start(props?: any): Promise<any>;
25
66
  stop(): Promise<boolean>;
@@ -16,6 +16,8 @@ const gd_eventlog_1 = require("gd-eventlog");
16
16
  const AntAdapter_1 = __importDefault(require("../AntAdapter"));
17
17
  const utils_1 = require("../utils");
18
18
  const utils_2 = require("../../utils");
19
+ const ant_fe_st_mode_1 = __importDefault(require("./ant-fe-st-mode"));
20
+ const ant_fe_erg_mode_1 = __importDefault(require("./ant-fe-erg-mode"));
19
21
  const floatVal = (d) => d ? parseFloat(d) : d;
20
22
  const intVal = (d) => d ? parseInt(d) : d;
21
23
  const hex = (v) => Math.abs(v).toString(16).toUpperCase();
@@ -54,6 +56,34 @@ class AntFEAdapter extends AntAdapter_1.default {
54
56
  const hrmStr = ComputedHeartRate ? ` (${ComputedHeartRate})` : '';
55
57
  return `${(0, utils_1.getBrand)(ManId)} FE ${DeviceID}${hrmStr}`;
56
58
  }
59
+ getSupportedCyclingModes() {
60
+ return [ant_fe_st_mode_1.default, ant_fe_erg_mode_1.default];
61
+ }
62
+ setCyclingMode(mode, settings) {
63
+ let selectedMode;
64
+ if (typeof mode === 'string') {
65
+ const supported = this.getSupportedCyclingModes();
66
+ const CyclingModeClass = supported.find(M => { const m = new M(this); return m.getName() === mode; });
67
+ if (CyclingModeClass) {
68
+ this.cyclingMode = new CyclingModeClass(this, settings);
69
+ return;
70
+ }
71
+ selectedMode = this.getDefaultCyclingMode();
72
+ }
73
+ else {
74
+ selectedMode = mode;
75
+ }
76
+ this.cyclingMode = selectedMode;
77
+ this.cyclingMode.setSettings(settings);
78
+ }
79
+ getCyclingMode() {
80
+ if (!this.cyclingMode)
81
+ this.cyclingMode = this.getDefaultCyclingMode();
82
+ return this.cyclingMode;
83
+ }
84
+ getDefaultCyclingMode() {
85
+ return new ant_fe_st_mode_1.default(this);
86
+ }
57
87
  onAttached() {
58
88
  this.logger.logEvent({ message: 'Device connected' });
59
89
  this.connected = true;
@@ -74,8 +104,9 @@ class AntFEAdapter extends AntAdapter_1.default {
74
104
  if (!this.lastUpdate || (Date.now() - this.lastUpdate) > this.updateFrequency) {
75
105
  const logData = this.getLogData(deviceData, ['PairedDevices', 'RawData']);
76
106
  this.logger.logEvent({ message: 'onDeviceData', data: logData });
77
- this.data = this.updateData(this.data, deviceData);
78
- const data = this.transformData(this.data);
107
+ let incyclistData = this.mapData(deviceData);
108
+ incyclistData = this.getCyclingMode().updateData(incyclistData);
109
+ const data = this.transformData(incyclistData);
79
110
  this.onDataFn(data);
80
111
  this.lastUpdate = Date.now();
81
112
  }
@@ -84,6 +115,25 @@ class AntFEAdapter extends AntAdapter_1.default {
84
115
  catch (err) {
85
116
  }
86
117
  }
118
+ mapData(deviceData) {
119
+ const data = {
120
+ isPedalling: false,
121
+ power: 0,
122
+ pedalRpm: undefined,
123
+ speed: 0,
124
+ heartrate: 0,
125
+ distanceInternal: 0,
126
+ slope: undefined,
127
+ time: undefined
128
+ };
129
+ data.speed = (deviceData.VirtualSpeed !== undefined ? deviceData.VirtualSpeed : (deviceData.RealSpeed || 0)) * 3.6;
130
+ data.slope = (deviceData.Incline !== undefined ? deviceData.Incline : data.slope);
131
+ data.power = (deviceData.InstantaneousPower !== undefined ? deviceData.InstantaneousPower : data.power);
132
+ data.time = (deviceData.ElapsedTime !== undefined ? deviceData.ElapsedTime : data.time);
133
+ data.pedalRpm = (deviceData.Cadence !== undefined ? deviceData.Cadence : data.pedalRpm);
134
+ data.isPedalling = data.pedalRpm > 0 || (data.pedalRpm === undefined && data.power > 0);
135
+ return data;
136
+ }
87
137
  onDeviceEvent(data) {
88
138
  try {
89
139
  const cmdInfo = this.currentCmd;
@@ -127,43 +177,21 @@ class AntFEAdapter extends AntAdapter_1.default {
127
177
  this.logger.logEvent({ message: 'Error', fn: 'parseEvent', event: { message: hex(data.message), code: hex(data.code) }, error: err.message || err });
128
178
  }
129
179
  }
130
- updateData(data, deviceData) {
131
- if (data.distanceOffs === undefined)
132
- data.distanceOffs = 0;
133
- data.speed = (deviceData.VirtualSpeed !== undefined ? deviceData.VirtualSpeed : deviceData.RealSpeed) * 3.6;
134
- data.slope = (deviceData.Incline !== undefined ? deviceData.Incline : data.slope);
135
- data.power = (deviceData.InstantaneousPower !== undefined ? deviceData.InstantaneousPower : data.power);
136
- data.pedalRpm = (deviceData.Cadence !== undefined ? deviceData.Cadence : data.pedalRpm);
137
- data.heartrate = (deviceData.HeartRate !== undefined ? deviceData.HeartRate : data.heartrate);
138
- if (deviceData.Distance !== undefined && deviceData.Distance !== 0) {
139
- data.distanceInternal = deviceData.Distance - data.distanceOffs;
140
- data.distance = data.distanceInternal / 1000;
141
- }
142
- else {
143
- if (this.lastUpdate && deviceData.Cadence !== undefined && deviceData.Cadence > 0 && data.speed) {
144
- const t = (Date.now() - this.lastUpdate) / 1000;
145
- const prevDistance = data.distanceInternal || 0;
146
- data.distanceInternal = Math.round(data.speed / 3.6 * t) + prevDistance;
147
- data.distance = data.distanceInternal / 1000;
148
- }
149
- }
150
- return data;
151
- }
152
180
  transformData(bikeData) {
153
181
  if (bikeData === undefined)
154
182
  return;
155
183
  let distance = 0;
156
184
  if (this.distanceInternal !== undefined && bikeData.distanceInternal !== undefined) {
157
- distance = intVal(bikeData.distanceInternal - this.distanceInternal);
185
+ distance = Math.round(bikeData.distanceInternal - this.distanceInternal);
158
186
  }
159
187
  if (bikeData.distanceInternal !== undefined)
160
188
  this.distanceInternal = bikeData.distanceInternal;
161
189
  let data = {
162
- speed: floatVal(bikeData.speed),
163
- slope: floatVal(bikeData.slope),
164
- power: intVal(bikeData.power),
165
- cadence: intVal(bikeData.pedalRpm),
166
- heartrate: intVal(bikeData.heartrate),
190
+ speed: bikeData.speed,
191
+ slope: bikeData.slope,
192
+ power: bikeData.power !== undefined ? Math.round(bikeData.power) : undefined,
193
+ cadence: bikeData.pedalRpm !== undefined ? Math.round(bikeData.pedalRpm) : undefined,
194
+ heartrate: bikeData.heartrate !== undefined ? Math.round(bikeData.heartrate) : undefined,
167
195
  distance,
168
196
  timestamp: Date.now()
169
197
  };
@@ -295,14 +323,17 @@ class AntFEAdapter extends AntAdapter_1.default {
295
323
  }
296
324
  sendUpdate(request) {
297
325
  return __awaiter(this, void 0, void 0, function* () {
298
- this.logger.logEvent({ message: "sendBikeUpdate():", request });
326
+ if (this.paused)
327
+ return;
328
+ const update = this.getCyclingMode().sendBikeUpdate(request);
329
+ this.logger.logEvent({ message: 'send bike update requested', update, request });
299
330
  try {
300
331
  const isReset = (!request || request.reset || Object.keys(request).length === 0);
301
- if (request.slope !== undefined) {
302
- yield (0, utils_2.runWithRetries)(() => __awaiter(this, void 0, void 0, function* () { return yield this.sendTrackResistance(request.slope); }), 2, 100);
332
+ if (update.slope !== undefined) {
333
+ yield (0, utils_2.runWithRetries)(() => __awaiter(this, void 0, void 0, function* () { return yield this.sendTrackResistance(update.slope); }), 2, 100);
303
334
  }
304
- if (request.targetPower !== undefined) {
305
- yield (0, utils_2.runWithRetries)(() => __awaiter(this, void 0, void 0, function* () { return yield this.sendTargetPower(request.targetPower); }), 2, 100);
335
+ if (update.targetPower !== undefined) {
336
+ yield (0, utils_2.runWithRetries)(() => __awaiter(this, void 0, void 0, function* () { return yield this.sendTargetPower(update.targetPower); }), 2, 100);
306
337
  }
307
338
  else if (request.maxPower !== undefined) {
308
339
  if (this.data.power && this.data.power > request.maxPower)
@@ -0,0 +1,5 @@
1
+ import BleERGCyclingMode from "../../ble/ble-erg-mode";
2
+ import { DeviceAdapter } from "../../Device";
3
+ export default class AntFeERGCyclingMode extends BleERGCyclingMode {
4
+ constructor(adapter: DeviceAdapter, props?: any);
5
+ }
@@ -0,0 +1,13 @@
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 ble_erg_mode_1 = __importDefault(require("../../ble/ble-erg-mode"));
7
+ class AntFeERGCyclingMode extends ble_erg_mode_1.default {
8
+ constructor(adapter, props) {
9
+ super(adapter, props);
10
+ this.initLogger('AntERGMode');
11
+ }
12
+ }
13
+ exports.default = AntFeERGCyclingMode;
@@ -0,0 +1,7 @@
1
+ import FtmsCyclingMode from "../../ble/ble-st-mode";
2
+ import { UpdateRequest } from "../../CyclingMode";
3
+ import { DeviceAdapter } from "../../Device";
4
+ export default class AntStCyclingMode extends FtmsCyclingMode {
5
+ constructor(adapter: DeviceAdapter, props?: any);
6
+ sendBikeUpdate(request: UpdateRequest): UpdateRequest;
7
+ }
@@ -0,0 +1,52 @@
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 ble_st_mode_1 = __importDefault(require("../../ble/ble-st-mode"));
7
+ class AntStCyclingMode extends ble_st_mode_1.default {
8
+ constructor(adapter, props) {
9
+ super(adapter, props);
10
+ this.initLogger('AntSTMode');
11
+ }
12
+ sendBikeUpdate(request) {
13
+ const getData = () => {
14
+ if (!this.data)
15
+ return {};
16
+ const { gear, pedalRpm, slope, power, speed } = this.data;
17
+ return { gear, pedalRpm, slope, power, speed };
18
+ };
19
+ const event = {};
20
+ if (this.data === undefined)
21
+ event.noData = true;
22
+ if (request.slope !== undefined && (event.noData || Math.abs(request.slope - this.data.slope) >= 0.1))
23
+ event.slopeUpdate = true;
24
+ if (this.prevRequest === undefined)
25
+ event.initialCall = true;
26
+ this.logger.logEvent({ message: "processing update request", request, prev: this.prevRequest, data: getData(), event });
27
+ let newRequest = {};
28
+ if (request.slope === undefined && request.targetPower === undefined && request.refresh && this.prevRequest) {
29
+ return this.prevRequest;
30
+ }
31
+ if (request.slope !== undefined) {
32
+ newRequest.slope = parseFloat(request.slope.toFixed(1));
33
+ this.data.slope = newRequest.slope;
34
+ }
35
+ if (request.targetPower !== undefined) {
36
+ newRequest.targetPower = request.targetPower;
37
+ }
38
+ if (request.minPower && request.maxPower && request.minPower === request.maxPower) {
39
+ newRequest.targetPower = request.minPower;
40
+ }
41
+ const prevData = this.data;
42
+ if (newRequest.targetPower === undefined && prevData && prevData.power) {
43
+ if (request.minPower !== undefined && prevData.power < request.minPower)
44
+ newRequest.targetPower = request.minPower;
45
+ if (request.maxPower !== undefined && prevData.power > request.maxPower)
46
+ newRequest.targetPower = request.maxPower;
47
+ }
48
+ this.prevRequest = JSON.parse(JSON.stringify(newRequest));
49
+ return newRequest;
50
+ }
51
+ }
52
+ exports.default = AntStCyclingMode;
@@ -1,12 +1,12 @@
1
1
  import CyclingMode, { CyclingModeProperty, IncyclistBikeData, UpdateRequest } from "../CyclingMode";
2
+ import { DeviceAdapter } from "../Device";
2
3
  import PowerBasedCyclingModeBase from "../modes/power-base";
3
- import { FmAdapter } from "./fm";
4
4
  export default class BleERGCyclingMode extends PowerBasedCyclingModeBase implements CyclingMode {
5
5
  prevRequest: UpdateRequest;
6
6
  hasBikeUpdate: boolean;
7
7
  chain: number[];
8
8
  cassette: number[];
9
- constructor(adapter: FmAdapter, props?: any);
9
+ constructor(adapter: DeviceAdapter, props?: any);
10
10
  getName(): string;
11
11
  getDescription(): string;
12
12
  getProperties(): CyclingModeProperty[];
@@ -1,10 +1,10 @@
1
1
  import CyclingMode, { CyclingModeProperty, IncyclistBikeData, UpdateRequest } from "../CyclingMode";
2
+ import { DeviceAdapter } from "../Device";
2
3
  import PowerBasedCyclingModeBase from "../modes/power-base";
3
- import { FmAdapter } from "./fm";
4
4
  export default class FtmsCyclingMode extends PowerBasedCyclingModeBase implements CyclingMode {
5
5
  prevRequest: UpdateRequest;
6
6
  hasBikeUpdate: boolean;
7
- constructor(adapter: FmAdapter, props?: any);
7
+ constructor(adapter: DeviceAdapter, props?: any);
8
8
  getName(): string;
9
9
  getDescription(): string;
10
10
  getProperties(): CyclingModeProperty[];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "incyclist-devices",
3
- "version": "1.4.53",
3
+ "version": "1.4.54",
4
4
  "dependencies": {
5
5
  "@serialport/parser-byte-length": "^9.0.1",
6
6
  "@serialport/parser-delimiter": "^9.0.1",