incyclist-devices 1.4.47 → 1.4.50

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,11 +1,7 @@
1
1
  "use strict";
2
2
  var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
3
  if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
4
+ Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
9
5
  }) : (function(o, m, k, k2) {
10
6
  if (k2 === undefined) k2 = k;
11
7
  o[k2] = m[k];
@@ -1,11 +1,7 @@
1
1
  "use strict";
2
2
  var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
3
  if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
4
+ Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
9
5
  }) : (function(o, m, k, k2) {
10
6
  if (k2 === undefined) k2 = k;
11
7
  o[k2] = m[k];
@@ -6,6 +6,13 @@ interface BleDeviceConstructProps extends BleDeviceProps {
6
6
  log?: boolean;
7
7
  logger?: EventLogger;
8
8
  }
9
+ declare type CommandQueueItem = {
10
+ uuid: string;
11
+ data: Buffer;
12
+ resolve: any;
13
+ reject: any;
14
+ timeout: any;
15
+ };
9
16
  export declare abstract class BleDevice extends BleDeviceClass {
10
17
  id: string;
11
18
  address: string;
@@ -19,8 +26,10 @@ export declare abstract class BleDevice extends BleDeviceClass {
19
26
  deviceInfo: BleDeviceInfo;
20
27
  isInitialized: boolean;
21
28
  subscribedCharacteristics: string[];
29
+ writeQueue: CommandQueueItem[];
22
30
  constructor(props?: BleDeviceConstructProps);
23
31
  logEvent(event: any): void;
32
+ setLogger(logger: EventLogger): void;
24
33
  setInterface(ble: BleInterfaceClass): void;
25
34
  cleanupListeners(): void;
26
35
  onDisconnect(): void;
@@ -32,8 +41,8 @@ export declare abstract class BleDevice extends BleDeviceClass {
32
41
  connect(props?: ConnectProps): Promise<boolean>;
33
42
  disconnect(): Promise<boolean>;
34
43
  abstract getProfile(): string;
35
- abstract onData(characteristic: string, data: Buffer): void;
36
- write(characteristicUuid: string, data: Buffer, withoutResponse?: boolean): Promise<boolean>;
44
+ onData(characteristic: string, data: Buffer): void;
45
+ write(characteristicUuid: string, data: Buffer, withoutResponse?: boolean): Promise<ArrayBuffer>;
37
46
  read(characteristicUuid: string): Promise<Uint8Array>;
38
47
  getDeviceInfo(): Promise<BleDeviceInfo>;
39
48
  }
@@ -26,6 +26,7 @@ class BleDevice extends ble_1.BleDeviceClass {
26
26
  this.characteristics = [];
27
27
  this.subscribedCharacteristics = [];
28
28
  this.isInitialized = false;
29
+ this.writeQueue = [];
29
30
  if (props.peripheral) {
30
31
  const { id, address, advertisement, state } = props.peripheral;
31
32
  this.peripheral = props.peripheral;
@@ -38,7 +39,7 @@ class BleDevice extends ble_1.BleDeviceClass {
38
39
  if (props.logger) {
39
40
  this.logger = props.logger;
40
41
  }
41
- else if (props.log) {
42
+ else if (props.log !== false) {
42
43
  this.logger = new gd_eventlog_1.EventLogger('BleDevice');
43
44
  }
44
45
  }
@@ -50,6 +51,9 @@ class BleDevice extends ble_1.BleDeviceClass {
50
51
  console.log('~~~BLE:', event);
51
52
  }
52
53
  }
54
+ setLogger(logger) {
55
+ this.logger = logger;
56
+ }
53
57
  setInterface(ble) {
54
58
  this.ble = ble;
55
59
  }
@@ -103,6 +107,7 @@ class BleDevice extends ble_1.BleDeviceClass {
103
107
  return Promise.resolve(true);
104
108
  return this.getDeviceInfo().then(() => {
105
109
  this.emit('deviceInfo', this.deviceInfo);
110
+ this.logEvent(Object.assign({ message: 'ftms device init done' }, this.deviceInfo));
106
111
  this.isInitialized = true;
107
112
  return true;
108
113
  });
@@ -235,9 +240,20 @@ class BleDevice extends ble_1.BleDeviceClass {
235
240
  }
236
241
  });
237
242
  }
243
+ onData(characteristic, data) {
244
+ if (this.writeQueue.length > 0) {
245
+ const writeIdx = this.writeQueue.findIndex(i => i.uuid === characteristic.toLocaleLowerCase());
246
+ if (writeIdx !== -1) {
247
+ const writeItem = this.writeQueue[writeIdx];
248
+ this.writeQueue.splice(writeIdx, 1);
249
+ if (writeItem.resolve)
250
+ writeItem.resolve(data);
251
+ }
252
+ }
253
+ }
238
254
  write(characteristicUuid, data, withoutResponse = false) {
239
255
  return __awaiter(this, void 0, void 0, function* () {
240
- if (this.subscribedCharacteristics.find(c => c === characteristicUuid) === undefined) {
256
+ if (!withoutResponse && this.subscribedCharacteristics.find(c => c === characteristicUuid) === undefined) {
241
257
  const connector = this.ble.getConnector(this.peripheral);
242
258
  connector.on(characteristicUuid, (uuid, data) => {
243
259
  this.onData(uuid, data);
@@ -251,11 +267,18 @@ class BleDevice extends ble_1.BleDeviceClass {
251
267
  reject(new Error('Characteristic not found'));
252
268
  return;
253
269
  }
270
+ if (withoutResponse) {
271
+ characteristic.write(data, withoutResponse);
272
+ resolve(new ArrayBuffer(0));
273
+ return;
274
+ }
275
+ const writeId = this.writeQueue.length;
276
+ this.writeQueue.push({ uuid: characteristicUuid.toLocaleLowerCase(), data, resolve, reject, timeout: Date.now() + 1000 });
254
277
  characteristic.write(data, withoutResponse, (err) => {
255
- if (err)
278
+ if (err) {
279
+ this.writeQueue.splice(writeId, 1);
256
280
  reject(err);
257
- else
258
- resolve(true);
281
+ }
259
282
  });
260
283
  });
261
284
  });
@@ -1,18 +1,11 @@
1
1
  import CyclingMode, { CyclingModeProperty, IncyclistBikeData, UpdateRequest } from "../CyclingMode";
2
2
  import PowerBasedCyclingModeBase from "../modes/power-base";
3
3
  import { FmAdapter } from "./fm";
4
- export declare type ERGEvent = {
5
- rpmUpdated?: boolean;
6
- gearUpdated?: boolean;
7
- starting?: boolean;
8
- tsStart?: number;
9
- };
10
- export default class ERGCyclingMode extends PowerBasedCyclingModeBase implements CyclingMode {
4
+ export default class BleERGCyclingMode extends PowerBasedCyclingModeBase implements CyclingMode {
11
5
  prevRequest: UpdateRequest;
12
6
  hasBikeUpdate: boolean;
13
7
  chain: number[];
14
8
  cassette: number[];
15
- event: ERGEvent;
16
9
  constructor(adapter: FmAdapter, props?: any);
17
10
  getName(): string;
18
11
  getDescription(): string;
@@ -13,11 +13,11 @@ const config = {
13
13
  { key: 'startPower', name: 'Starting Power', description: 'Initial power in Watts at start of training', type: CyclingMode_1.CyclingModeProperyType.Integer, default: 50, min: 25, max: 800 },
14
14
  ]
15
15
  };
16
- class ERGCyclingMode extends power_base_1.default {
16
+ const MIN_SPEED = 10;
17
+ class BleERGCyclingMode extends power_base_1.default {
17
18
  constructor(adapter, props) {
18
19
  super(adapter, props);
19
20
  this.hasBikeUpdate = false;
20
- this.event = {};
21
21
  this.initLogger('ERGMode');
22
22
  }
23
23
  getName() {
@@ -34,7 +34,7 @@ class ERGCyclingMode extends power_base_1.default {
34
34
  }
35
35
  getBikeInitRequest() {
36
36
  const startPower = this.getSetting('startPower');
37
- return { targetPower: startPower };
37
+ return { slope: 0, targetPower: startPower };
38
38
  }
39
39
  sendBikeUpdate(request) {
40
40
  const getData = () => {
@@ -43,25 +43,18 @@ class ERGCyclingMode extends power_base_1.default {
43
43
  const { pedalRpm, slope, power, speed } = this.data;
44
44
  return { pedalRpm, slope, power, speed };
45
45
  };
46
- this.logger.logEvent({ message: "processing update request", request, prev: this.prevRequest, data: getData(), event: this.event });
46
+ this.logger.logEvent({ message: "processing update request", request, prev: this.prevRequest, data: getData() });
47
47
  let newRequest = {};
48
48
  try {
49
49
  if (!request || request.reset || Object.keys(request).length === 0) {
50
50
  this.prevRequest = {};
51
- return request || {};
51
+ return request.reset ? { reset: true } : {};
52
52
  }
53
53
  const prevData = this.data || {};
54
54
  if (request.targetPower !== undefined) {
55
55
  delete request.slope;
56
56
  delete request.refresh;
57
57
  }
58
- if (this.event.starting && request.targetPower === undefined) {
59
- newRequest.targetPower = this.getSetting('startPower');
60
- if (this.event.tsStart && Date.now() - this.event.tsStart > 5000) {
61
- delete this.event.starting;
62
- delete this.event.tsStart;
63
- }
64
- }
65
58
  if (request.refresh) {
66
59
  delete request.refresh;
67
60
  newRequest.targetPower = this.prevRequest.targetPower;
@@ -70,14 +63,12 @@ class ERGCyclingMode extends power_base_1.default {
70
63
  if (!this.data)
71
64
  this.data = {};
72
65
  this.data.slope = request.slope;
66
+ delete request.slope;
73
67
  }
74
68
  if (request.maxPower !== undefined && request.minPower !== undefined && request.maxPower === request.minPower) {
75
69
  request.targetPower = request.maxPower;
76
- }
77
- if (request.targetPower !== undefined) {
78
70
  newRequest.targetPower = request.targetPower;
79
71
  }
80
- delete request.slope;
81
72
  if (request.maxPower !== undefined) {
82
73
  if (newRequest.targetPower !== undefined && newRequest.targetPower > request.maxPower) {
83
74
  newRequest.targetPower = request.maxPower;
@@ -89,9 +80,8 @@ class ERGCyclingMode extends power_base_1.default {
89
80
  newRequest.targetPower = request.minPower;
90
81
  }
91
82
  newRequest.minPower = request.minPower;
92
- }
93
- if (newRequest.targetPower !== undefined && prevData.power !== undefined && newRequest.targetPower === prevData.power) {
94
- delete newRequest.targetPower;
83
+ if (prevData.power && prevData.power < request.minPower)
84
+ newRequest.targetPower = request.minPower;
95
85
  }
96
86
  this.prevRequest = JSON.parse(JSON.stringify(request));
97
87
  }
@@ -106,36 +96,33 @@ class ERGCyclingMode extends power_base_1.default {
106
96
  const prevRequest = this.prevRequest || {};
107
97
  const data = this.data || {};
108
98
  const bikeType = this.getSetting('bikeType').toLowerCase();
109
- delete this.event.rpmUpdated;
110
- if (prevData === {} || prevData.speed === undefined || prevData.speed === 0) {
111
- this.event.starting = true;
112
- this.event.tsStart = Date.now();
113
- }
114
99
  try {
115
- const rpm = bikeData.pedalRpm || 0;
116
100
  let power = bikeData.power || 0;
117
101
  const slope = (prevData.slope !== undefined ? prevData.slope : prevRequest.slope || 0);
118
102
  const distanceInternal = prevData.distanceInternal || 0;
119
- if (!bikeData.pedalRpm || bikeData.isPedalling === false) {
103
+ if (bikeData.pedalRpm === 0 || bikeData.isPedalling === false) {
120
104
  power = 0;
121
105
  }
122
106
  const m = this.getWeight();
123
107
  const t = this.getTimeSinceLastUpdate();
124
108
  const { speed, distance } = this.calculateSpeedAndDistance(power, slope, m, t, { bikeType });
125
- data.speed = parseFloat(speed.toFixed(1));
109
+ if (power === 0 && speed < MIN_SPEED) {
110
+ data.speed = Math.round(prevData.speed - 1) < 0 ? 0 : Math.round(prevData.speed - 1);
111
+ data.distanceInternal = Math.round(distanceInternal + data.speed / 3.6 * t);
112
+ }
113
+ else {
114
+ data.speed = (power === 0 && speed < MIN_SPEED) ? 0 : speed;
115
+ data.distanceInternal = (power === 0 && speed < MIN_SPEED) ? Math.round(distanceInternal) : Math.round(distanceInternal + distance);
116
+ }
126
117
  data.power = Math.round(power);
127
- data.distanceInternal = Math.round(distanceInternal + distance);
128
118
  data.slope = slope;
129
- data.pedalRpm = rpm;
130
- if (data.time !== undefined && !(this.event.starting && !bikeData.pedalRpm))
119
+ data.pedalRpm = bikeData.pedalRpm || 0;
120
+ if (data.time !== undefined && data.speed > 0)
131
121
  data.time += t;
132
122
  else
133
123
  data.time = 0;
134
124
  data.heartrate = bikeData.heartrate;
135
125
  data.isPedalling = bikeData.isPedalling;
136
- if (rpm && rpm !== prevData.pedalRpm) {
137
- this.event.rpmUpdated = true;
138
- }
139
126
  }
140
127
  catch (err) {
141
128
  this.logger.logEvent({ message: 'error', fn: 'updateData()', error: err.message || err });
@@ -145,4 +132,4 @@ class ERGCyclingMode extends power_base_1.default {
145
132
  return data;
146
133
  }
147
134
  }
148
- exports.default = ERGCyclingMode;
135
+ exports.default = BleERGCyclingMode;
@@ -1,18 +1,9 @@
1
1
  import CyclingMode, { CyclingModeProperty, IncyclistBikeData, UpdateRequest } from "../CyclingMode";
2
2
  import PowerBasedCyclingModeBase from "../modes/power-base";
3
3
  import { FmAdapter } from "./fm";
4
- export declare type ERGEvent = {
5
- rpmUpdated?: boolean;
6
- gearUpdated?: boolean;
7
- starting?: boolean;
8
- tsStart?: number;
9
- };
10
- export default class ERGCyclingMode extends PowerBasedCyclingModeBase implements CyclingMode {
4
+ export default class FtmsCyclingMode extends PowerBasedCyclingModeBase implements CyclingMode {
11
5
  prevRequest: UpdateRequest;
12
6
  hasBikeUpdate: boolean;
13
- chain: number[];
14
- cassette: number[];
15
- event: ERGEvent;
16
7
  constructor(adapter: FmAdapter, props?: any);
17
8
  getName(): string;
18
9
  getDescription(): string;
@@ -9,16 +9,15 @@ const config = {
9
9
  name: "Smart Trainer",
10
10
  description: "Calculates speed based on power and slope. Slope is set to the device",
11
11
  properties: [
12
- { key: 'bikeType', name: 'Bike Type', description: '', type: CyclingMode_1.CyclingModeProperyType.SingleSelect, options: ['Race', 'Mountain', 'Triathlon'], default: 'Race' },
13
- { key: 'startPower', name: 'Starting Power', description: 'Initial power in Watts at start of training', type: CyclingMode_1.CyclingModeProperyType.Integer, default: 50, min: 25, max: 800 },
12
+ { key: 'bikeType', name: 'Bike Type', description: '', type: CyclingMode_1.CyclingModeProperyType.SingleSelect, options: ['Race', 'Mountain', 'Triathlon'], default: 'Race' }
14
13
  ]
15
14
  };
16
- class ERGCyclingMode extends power_base_1.default {
15
+ const MIN_SPEED = 10;
16
+ class FtmsCyclingMode extends power_base_1.default {
17
17
  constructor(adapter, props) {
18
18
  super(adapter, props);
19
19
  this.hasBikeUpdate = false;
20
- this.event = {};
21
- this.initLogger('ERGMode');
20
+ this.initLogger('FtmsMode');
22
21
  }
23
22
  getName() {
24
23
  return config.name;
@@ -33,71 +32,32 @@ class ERGCyclingMode extends power_base_1.default {
33
32
  return config.properties.find(p => p.name === name);
34
33
  }
35
34
  getBikeInitRequest() {
36
- const startPower = this.getSetting('startPower');
37
- return { targetPower: startPower };
35
+ return { slope: 0 };
38
36
  }
39
37
  sendBikeUpdate(request) {
40
38
  const getData = () => {
41
39
  if (!this.data)
42
40
  return {};
43
- const { pedalRpm, slope, power, speed } = this.data;
44
- return { pedalRpm, slope, power, speed };
41
+ const { gear, pedalRpm, slope, power, speed } = this.data;
42
+ return { gear, pedalRpm, slope, power, speed };
45
43
  };
46
- this.logger.logEvent({ message: "processing update request", request, prev: this.prevRequest, data: getData(), event: this.event });
44
+ const event = {};
45
+ if (this.data === undefined)
46
+ event.noData = true;
47
+ if (request.slope !== undefined && (event.noData || Math.abs(request.slope - this.data.slope) >= 0.1))
48
+ event.slopeUpdate = true;
49
+ if (this.prevRequest === undefined)
50
+ event.initialCall = true;
51
+ this.logger.logEvent({ message: "processing update request", request, prev: this.prevRequest, data: getData(), event });
47
52
  let newRequest = {};
48
- try {
49
- if (!request || request.reset || Object.keys(request).length === 0) {
50
- this.prevRequest = {};
51
- return request || {};
52
- }
53
- const prevData = this.data || {};
54
- if (request.targetPower !== undefined) {
55
- delete request.slope;
56
- delete request.refresh;
57
- }
58
- if (this.event.starting && request.targetPower === undefined) {
59
- newRequest.targetPower = this.getSetting('startPower');
60
- if (this.event.tsStart && Date.now() - this.event.tsStart > 5000) {
61
- delete this.event.starting;
62
- delete this.event.tsStart;
63
- }
64
- }
65
- if (request.refresh) {
66
- delete request.refresh;
67
- newRequest.targetPower = this.prevRequest.targetPower;
68
- }
69
- if (request.slope !== undefined) {
70
- if (!this.data)
71
- this.data = {};
72
- this.data.slope = request.slope;
73
- }
74
- if (request.maxPower !== undefined && request.minPower !== undefined && request.maxPower === request.minPower) {
75
- request.targetPower = request.maxPower;
76
- }
77
- if (request.targetPower !== undefined) {
78
- newRequest.targetPower = request.targetPower;
79
- }
80
- delete request.slope;
81
- if (request.maxPower !== undefined) {
82
- if (newRequest.targetPower !== undefined && newRequest.targetPower > request.maxPower) {
83
- newRequest.targetPower = request.maxPower;
84
- }
85
- newRequest.maxPower = request.maxPower;
86
- }
87
- if (request.minPower !== undefined) {
88
- if (newRequest.targetPower !== undefined && newRequest.targetPower < request.minPower) {
89
- newRequest.targetPower = request.minPower;
90
- }
91
- newRequest.minPower = request.minPower;
92
- }
93
- if (newRequest.targetPower !== undefined && prevData.power !== undefined && newRequest.targetPower === prevData.power) {
94
- delete newRequest.targetPower;
95
- }
96
- this.prevRequest = JSON.parse(JSON.stringify(request));
53
+ if (request.slope === undefined && request.refresh && this.prevRequest) {
54
+ return this.prevRequest;
97
55
  }
98
- catch (err) {
99
- this.logger.logEvent({ message: "error", fn: 'sendBikeUpdate()', error: err.message || err, stack: err.stack });
56
+ if (request.slope !== undefined) {
57
+ newRequest.slope = parseFloat(request.slope.toFixed(1));
58
+ this.data.slope = newRequest.slope;
100
59
  }
60
+ this.prevRequest = JSON.parse(JSON.stringify(newRequest));
101
61
  return newRequest;
102
62
  }
103
63
  updateData(bikeData) {
@@ -106,36 +66,33 @@ class ERGCyclingMode extends power_base_1.default {
106
66
  const prevRequest = this.prevRequest || {};
107
67
  const data = this.data || {};
108
68
  const bikeType = this.getSetting('bikeType').toLowerCase();
109
- delete this.event.rpmUpdated;
110
- if (prevData === {} || prevData.speed === undefined || prevData.speed === 0) {
111
- this.event.starting = true;
112
- this.event.tsStart = Date.now();
113
- }
114
69
  try {
115
- const rpm = bikeData.pedalRpm || 0;
116
70
  let power = bikeData.power || 0;
117
71
  const slope = (prevData.slope !== undefined ? prevData.slope : prevRequest.slope || 0);
118
72
  const distanceInternal = prevData.distanceInternal || 0;
119
- if (!bikeData.pedalRpm || bikeData.isPedalling === false) {
73
+ if (bikeData.pedalRpm === 0 || bikeData.isPedalling === false) {
120
74
  power = 0;
121
75
  }
122
76
  const m = this.getWeight();
123
77
  const t = this.getTimeSinceLastUpdate();
124
78
  const { speed, distance } = this.calculateSpeedAndDistance(power, slope, m, t, { bikeType });
125
- data.speed = parseFloat(speed.toFixed(1));
79
+ if (power === 0 && speed < MIN_SPEED) {
80
+ data.speed = Math.round(prevData.speed - 1) < 0 ? 0 : Math.round(prevData.speed - 1);
81
+ data.distanceInternal = Math.round(distanceInternal + data.speed / 3.6 * t);
82
+ }
83
+ else {
84
+ data.speed = (power === 0 && speed < MIN_SPEED) ? 0 : speed;
85
+ data.distanceInternal = (power === 0 && speed < MIN_SPEED) ? Math.round(distanceInternal) : Math.round(distanceInternal + distance);
86
+ }
126
87
  data.power = Math.round(power);
127
- data.distanceInternal = Math.round(distanceInternal + distance);
128
88
  data.slope = slope;
129
- data.pedalRpm = rpm;
130
- if (data.time !== undefined && !(this.event.starting && !bikeData.pedalRpm))
89
+ data.pedalRpm = bikeData.pedalRpm || 0;
90
+ if (data.time !== undefined)
131
91
  data.time += t;
132
92
  else
133
93
  data.time = 0;
134
94
  data.heartrate = bikeData.heartrate;
135
95
  data.isPedalling = bikeData.isPedalling;
136
- if (rpm && rpm !== prevData.pedalRpm) {
137
- this.event.rpmUpdated = true;
138
- }
139
96
  }
140
97
  catch (err) {
141
98
  this.logger.logEvent({ message: 'error', fn: 'updateData()', error: err.message || err });
@@ -145,4 +102,4 @@ class ERGCyclingMode extends power_base_1.default {
145
102
  return data;
146
103
  }
147
104
  }
148
- exports.default = ERGCyclingMode;
105
+ exports.default = FtmsCyclingMode;
package/lib/ble/ble.d.ts CHANGED
@@ -1,5 +1,4 @@
1
1
  /// <reference types="node" />
2
- /// <reference types="node" />
3
2
  import EventEmitter from "events";
4
3
  import BlePeripheralConnector from "./ble-peripheral";
5
4
  export declare type ConnectProps = {
package/lib/ble/fm.d.ts CHANGED
@@ -31,6 +31,9 @@ declare type IndoorBikeData = {
31
31
  time?: number;
32
32
  remainingTime?: number;
33
33
  raw?: string;
34
+ targetPower?: number;
35
+ targetInclination?: number;
36
+ status?: string;
34
37
  };
35
38
  declare type IndoorBikeFeatures = {
36
39
  fitnessMachine: number;
@@ -43,6 +46,10 @@ export default class BleFitnessMachineDevice extends BleDevice {
43
46
  features: IndoorBikeFeatures;
44
47
  hasControl: boolean;
45
48
  isCPSubscribed: boolean;
49
+ crr: number;
50
+ cw: number;
51
+ windSpeed: number;
52
+ wheelSize: number;
46
53
  constructor(props?: any);
47
54
  init(): Promise<boolean>;
48
55
  onDisconnect(): void;
@@ -52,11 +59,25 @@ export default class BleFitnessMachineDevice extends BleDevice {
52
59
  isPower(): boolean;
53
60
  isHrm(): boolean;
54
61
  parseHrm(_data: Uint8Array): IndoorBikeData;
62
+ setCrr(crr: number): void;
63
+ getCrr(): number;
64
+ setCw(cw: number): void;
65
+ getCw(): number;
66
+ setWindSpeed(windSpeed: number): void;
67
+ getWindSpeed(): number;
55
68
  parseIndoorBikeData(_data: Uint8Array): IndoorBikeData;
69
+ parseFitnessMachineStatus(_data: Uint8Array): IndoorBikeData;
56
70
  getFitnessMachineFeatures(): Promise<IndoorBikeFeatures>;
57
71
  onData(characteristic: string, data: Buffer): void;
72
+ writeFtmsMessage(requestedOpCode: any, data: any): Promise<number>;
58
73
  requestControl(): Promise<boolean>;
59
- setTargetPower(power: number): Promise<void>;
74
+ setTargetPower(power: number): Promise<boolean>;
75
+ setSlope(slope: any): Promise<boolean>;
76
+ setTargetInclination(inclination: number): Promise<boolean>;
77
+ setIndoorBikeSimulation(windSpeed: number, gradient: number, crr: number, cw: number): Promise<boolean>;
78
+ startRequest(): Promise<boolean>;
79
+ stopRequest(): Promise<boolean>;
80
+ PauseRequest(): Promise<boolean>;
60
81
  reset(): void;
61
82
  }
62
83
  export declare class FmAdapter extends DeviceAdapter {
@@ -66,7 +87,7 @@ export declare class FmAdapter extends DeviceAdapter {
66
87
  protocol: DeviceProtocol;
67
88
  paused: boolean;
68
89
  logger: EventLogger;
69
- mode: CyclingMode;
90
+ cyclingMode: CyclingMode;
70
91
  distanceInternal: number;
71
92
  prevDataTS: number;
72
93
  constructor(device: BleDeviceClass, protocol: BleProtocol);
@@ -77,6 +98,8 @@ export declare class FmAdapter extends DeviceAdapter {
77
98
  getProfile(): string;
78
99
  getName(): string;
79
100
  getDisplayName(): string;
101
+ getSupportedCyclingModes(): Array<any>;
102
+ setCyclingMode(mode: string | CyclingMode, settings?: any): void;
80
103
  getCyclingMode(): CyclingMode;
81
104
  getDefaultCyclingMode(): CyclingMode;
82
105
  getPort(): string;
@@ -87,6 +110,7 @@ export declare class FmAdapter extends DeviceAdapter {
87
110
  transformData(bikeData: IncyclistBikeData): DeviceData;
88
111
  start(props?: any): Promise<any>;
89
112
  stop(): Promise<boolean>;
113
+ sendUpdate(request: any): Promise<void>;
90
114
  pause(): Promise<boolean>;
91
115
  resume(): Promise<boolean>;
92
116
  }
package/lib/ble/fm.js CHANGED
@@ -18,7 +18,15 @@ const ble_interface_1 = __importDefault(require("./ble-interface"));
18
18
  const Device_1 = __importDefault(require("../Device"));
19
19
  const gd_eventlog_1 = require("gd-eventlog");
20
20
  const power_meter_1 = __importDefault(require("../modes/power-meter"));
21
+ const ble_st_mode_1 = __importDefault(require("./ble-st-mode"));
22
+ const ble_erg_mode_1 = __importDefault(require("./ble-erg-mode"));
21
23
  const FTMS_CP = '2ad9';
24
+ const cwABike = {
25
+ race: 0.35,
26
+ triathlon: 0.29,
27
+ mountain: 0.57
28
+ };
29
+ const cRR = 0.0036;
22
30
  const bit = (nr) => (1 << nr);
23
31
  const IndoorBikeDataFlag = {
24
32
  MoreData: bit(0),
@@ -79,6 +87,10 @@ class BleFitnessMachineDevice extends ble_device_1.BleDevice {
79
87
  this.features = undefined;
80
88
  this.hasControl = false;
81
89
  this.isCPSubscribed = false;
90
+ this.crr = 0.0033;
91
+ this.cw = 0.6;
92
+ this.windSpeed = 0;
93
+ this.wheelSize = 2100;
82
94
  this.data = {};
83
95
  }
84
96
  init() {
@@ -138,6 +150,12 @@ class BleFitnessMachineDevice extends ble_device_1.BleDevice {
138
150
  }
139
151
  return Object.assign(Object.assign({}, this.data), { raw: data.toString('hex') });
140
152
  }
153
+ setCrr(crr) { this.crr = crr; }
154
+ getCrr() { return this.crr; }
155
+ setCw(cw) { this.cw = cw; }
156
+ getCw() { return this.cw; }
157
+ setWindSpeed(windSpeed) { this.windSpeed = windSpeed; }
158
+ getWindSpeed() { return this.windSpeed; }
141
159
  parseIndoorBikeData(_data) {
142
160
  const data = Buffer.from(_data);
143
161
  const flags = data.readUInt16LE(0);
@@ -163,7 +181,7 @@ class BleFitnessMachineDevice extends ble_device_1.BleDevice {
163
181
  offset += 1;
164
182
  const dvHigh = data.readUInt16LE(offset);
165
183
  offset += 2;
166
- this.data.totalDistance = dvHigh << 8 + dvLow;
184
+ this.data.totalDistance = (dvHigh << 8) + dvLow;
167
185
  }
168
186
  if (flags & IndoorBikeDataFlag.ResistanceLevelPresent) {
169
187
  this.data.resistanceLevel = data.readInt16LE(offset);
@@ -199,6 +217,43 @@ class BleFitnessMachineDevice extends ble_device_1.BleDevice {
199
217
  }
200
218
  return Object.assign(Object.assign({}, this.data), { raw: data.toString('hex') });
201
219
  }
220
+ parseFitnessMachineStatus(_data) {
221
+ const data = Buffer.from(_data);
222
+ const OpCode = data.readUInt8(0);
223
+ switch (OpCode) {
224
+ case 8:
225
+ this.data.targetPower = data.readInt16LE(1);
226
+ break;
227
+ case 6:
228
+ this.data.targetInclination = data.readInt16LE(1) / 10;
229
+ break;
230
+ case 4:
231
+ this.data.status = "STARTED";
232
+ break;
233
+ case 3:
234
+ case 2:
235
+ this.data.status = "STOPPED";
236
+ break;
237
+ case 20:
238
+ const spinDownStatus = data.readUInt8(1);
239
+ switch (spinDownStatus) {
240
+ case 1:
241
+ this.data.status = "SPIN DOWN REQUESTED";
242
+ break;
243
+ case 2:
244
+ this.data.status = "SPIN DOWN SUCCESS";
245
+ break;
246
+ case 3:
247
+ this.data.status = "SPIN DOWN ERROR";
248
+ break;
249
+ case 4:
250
+ this.data.status = "STOP PEDALING";
251
+ break;
252
+ default: break;
253
+ }
254
+ }
255
+ return Object.assign(Object.assign({}, this.data), { raw: data.toString('hex') });
256
+ }
202
257
  getFitnessMachineFeatures() {
203
258
  return __awaiter(this, void 0, void 0, function* () {
204
259
  if (this.features)
@@ -218,36 +273,148 @@ class BleFitnessMachineDevice extends ble_device_1.BleDevice {
218
273
  });
219
274
  }
220
275
  onData(characteristic, data) {
221
- if (characteristic.toLocaleLowerCase() === '2ad2') {
222
- const res = this.parseIndoorBikeData(data);
223
- this.emit('data', res);
276
+ super.onData(characteristic, data);
277
+ const uuid = characteristic.toLocaleLowerCase();
278
+ let res = undefined;
279
+ switch (uuid) {
280
+ case '2ad2':
281
+ res = this.parseIndoorBikeData(data);
282
+ break;
283
+ case '2a37':
284
+ res = this.parseHrm(data);
285
+ break;
286
+ case '2ada':
287
+ res = this.parseFitnessMachineStatus(data);
288
+ break;
289
+ default:
290
+ break;
224
291
  }
225
- if (characteristic.toLocaleLowerCase() === '2a37') {
226
- const res = this.parseHrm(data);
292
+ if (res)
227
293
  this.emit('data', res);
228
- }
294
+ }
295
+ writeFtmsMessage(requestedOpCode, data) {
296
+ return __awaiter(this, void 0, void 0, function* () {
297
+ try {
298
+ const res = yield this.write(FTMS_CP, data);
299
+ const responseData = Buffer.from(res);
300
+ const opCode = responseData.readUInt8(0);
301
+ const request = responseData.readUInt8(1);
302
+ const result = responseData.readUInt8(2);
303
+ if (opCode !== 128 || request !== requestedOpCode)
304
+ throw new Error('Illegal response ');
305
+ return result;
306
+ }
307
+ catch (err) {
308
+ this.logEvent({ message: 'writeFtmsMessage failed', opCode: requestedOpCode, reason: err.message });
309
+ return 4;
310
+ }
311
+ });
229
312
  }
230
313
  requestControl() {
231
314
  return __awaiter(this, void 0, void 0, function* () {
232
315
  if (this.hasControl)
233
316
  return true;
317
+ this.logEvent({ message: 'requestControl' });
234
318
  const data = Buffer.alloc(1);
235
319
  data.writeUInt8(0, 0);
236
- const success = yield this.write(FTMS_CP, data);
237
- if (success)
320
+ const res = yield this.writeFtmsMessage(0, data);
321
+ if (res === 1) {
238
322
  this.hasControl = true;
323
+ }
324
+ else {
325
+ this.logEvent({ message: 'requestControl failed' });
326
+ }
239
327
  return this.hasControl;
240
328
  });
241
329
  }
242
330
  setTargetPower(power) {
243
331
  return __awaiter(this, void 0, void 0, function* () {
332
+ this.logEvent({ message: 'setTargetPower', power, skip: (this.data.targetPower !== undefined && this.data.targetPower === power) });
333
+ if (this.data.targetPower !== undefined && this.data.targetPower === power)
334
+ return true;
244
335
  const hasControl = yield this.requestControl();
245
- if (!hasControl)
246
- throw new Error('setTargetPower not possible - control is disabled');
247
336
  const data = Buffer.alloc(3);
248
337
  data.writeUInt8(5, 0);
249
338
  data.writeInt16LE(Math.round(power), 1);
250
- const res = yield this.write(FTMS_CP, data);
339
+ const res = yield this.writeFtmsMessage(5, data);
340
+ return (res === 1);
341
+ });
342
+ }
343
+ setSlope(slope) {
344
+ return __awaiter(this, void 0, void 0, function* () {
345
+ this.logEvent({ message: 'setSlope', slope });
346
+ const { windSpeed, crr, cw } = this;
347
+ return yield this.setIndoorBikeSimulation(windSpeed, slope, crr, cw);
348
+ });
349
+ }
350
+ setTargetInclination(inclination) {
351
+ return __awaiter(this, void 0, void 0, function* () {
352
+ if (this.data.targetInclination !== undefined && this.data.targetInclination === inclination)
353
+ return true;
354
+ const hasControl = yield this.requestControl();
355
+ if (!hasControl) {
356
+ this.logEvent({ message: 'setTargetInclination failed', reason: 'control is disabled' });
357
+ return false;
358
+ }
359
+ const data = Buffer.alloc(3);
360
+ data.writeUInt8(3, 0);
361
+ data.writeInt16LE(Math.round(inclination * 10), 1);
362
+ const res = yield this.writeFtmsMessage(3, data);
363
+ return (res === 1);
364
+ });
365
+ }
366
+ setIndoorBikeSimulation(windSpeed, gradient, crr, cw) {
367
+ return __awaiter(this, void 0, void 0, function* () {
368
+ const hasControl = yield this.requestControl();
369
+ const data = Buffer.alloc(7);
370
+ data.writeUInt8(17, 0);
371
+ data.writeInt16LE(Math.round(windSpeed * 1000), 1);
372
+ data.writeInt16LE(Math.round(gradient * 100), 3);
373
+ data.writeUInt8(Math.round(crr * 10000), 5);
374
+ data.writeUInt8(Math.round(cw * 100), 6);
375
+ const res = yield this.writeFtmsMessage(17, data);
376
+ return (res === 1);
377
+ });
378
+ }
379
+ startRequest() {
380
+ return __awaiter(this, void 0, void 0, function* () {
381
+ const hasControl = yield this.requestControl();
382
+ if (!hasControl) {
383
+ this.logEvent({ message: 'startRequest failed', reason: 'control is disabled' });
384
+ return false;
385
+ }
386
+ const data = Buffer.alloc(1);
387
+ data.writeUInt8(7, 0);
388
+ const res = yield this.writeFtmsMessage(7, data);
389
+ return (res === 1);
390
+ });
391
+ }
392
+ stopRequest() {
393
+ return __awaiter(this, void 0, void 0, function* () {
394
+ const hasControl = yield this.requestControl();
395
+ if (!hasControl) {
396
+ this.logEvent({ message: 'stopRequest failed', reason: 'control is disabled' });
397
+ return false;
398
+ }
399
+ const data = Buffer.alloc(2);
400
+ data.writeUInt8(8, 0);
401
+ data.writeUInt8(1, 1);
402
+ const res = yield this.writeFtmsMessage(8, data);
403
+ return (res === 1);
404
+ });
405
+ }
406
+ PauseRequest() {
407
+ return __awaiter(this, void 0, void 0, function* () {
408
+ const hasControl = yield this.requestControl();
409
+ if (!hasControl) {
410
+ this.logEvent({ message: 'PauseRequest failed', reason: 'control is disabled' });
411
+ return false;
412
+ }
413
+ const data = Buffer.alloc(2);
414
+ data.writeUInt8(8, 0);
415
+ data.writeUInt8(2, 1);
416
+ const res = yield this.writeFtmsMessage(8, data);
417
+ return (res === 1);
251
418
  });
252
419
  }
253
420
  reset() {
@@ -266,8 +433,10 @@ class FmAdapter extends Device_1.default {
266
433
  this.distanceInternal = 0;
267
434
  this.device = device;
268
435
  this.ble = protocol.ble;
269
- this.mode = this.getDefaultCyclingMode();
436
+ this.cyclingMode = this.getDefaultCyclingMode();
270
437
  this.logger = new gd_eventlog_1.EventLogger('BLE-FM');
438
+ if (this.device)
439
+ this.device.setLogger(this.logger);
271
440
  }
272
441
  isBike() { return this.device.isBike(); }
273
442
  isHrm() { return this.device.isHrm(); }
@@ -287,13 +456,33 @@ class FmAdapter extends Device_1.default {
287
456
  getDisplayName() {
288
457
  return this.getName();
289
458
  }
459
+ getSupportedCyclingModes() {
460
+ return [ble_st_mode_1.default, ble_erg_mode_1.default, power_meter_1.default];
461
+ }
462
+ setCyclingMode(mode, settings) {
463
+ let selectedMode;
464
+ if (typeof mode === 'string') {
465
+ const supported = this.getSupportedCyclingModes();
466
+ const CyclingModeClass = supported.find(M => { const m = new M(this); return m.getName() === mode; });
467
+ if (CyclingModeClass) {
468
+ this.cyclingMode = new CyclingModeClass(this, settings);
469
+ return;
470
+ }
471
+ selectedMode = this.getDefaultCyclingMode();
472
+ }
473
+ else {
474
+ selectedMode = mode;
475
+ }
476
+ this.cyclingMode = selectedMode;
477
+ this.cyclingMode.setSettings(settings);
478
+ }
290
479
  getCyclingMode() {
291
- if (!this.mode)
292
- this.mode = this.getDefaultCyclingMode();
293
- return this.mode;
480
+ if (!this.cyclingMode)
481
+ this.cyclingMode = this.getDefaultCyclingMode();
482
+ return this.cyclingMode;
294
483
  }
295
484
  getDefaultCyclingMode() {
296
- return new power_meter_1.default(this);
485
+ return new ble_st_mode_1.default(this);
297
486
  }
298
487
  getPort() {
299
488
  return 'ble';
@@ -361,8 +550,25 @@ class FmAdapter extends Device_1.default {
361
550
  yield this.ble.stopScan();
362
551
  try {
363
552
  const bleDevice = yield this.ble.connectDevice(this.device);
553
+ bleDevice.setLogger(this.logger);
364
554
  if (bleDevice) {
365
555
  this.device = bleDevice;
556
+ const mode = this.getCyclingMode();
557
+ if (mode && mode.getSetting('bikeType')) {
558
+ const bikeType = mode.getSetting('bikeType').toLowerCase();
559
+ this.device.setCrr(cRR);
560
+ switch (bikeType) {
561
+ case 'race':
562
+ this.device.setCw(cwABike.race);
563
+ break;
564
+ case 'triathlon':
565
+ this.device.setCw(cwABike.triathlon);
566
+ break;
567
+ case 'mountain':
568
+ this.device.setCw(cwABike.mountain);
569
+ break;
570
+ }
571
+ }
366
572
  bleDevice.on('data', (data) => {
367
573
  this.onDeviceData(data);
368
574
  });
@@ -383,6 +589,20 @@ class FmAdapter extends Device_1.default {
383
589
  return this.device.disconnect();
384
590
  });
385
591
  }
592
+ sendUpdate(request) {
593
+ return __awaiter(this, void 0, void 0, function* () {
594
+ if (this.paused || !this.device)
595
+ return;
596
+ const update = this.getCyclingMode().sendBikeUpdate(request);
597
+ this.logger.logEvent({ message: 'send bike update requested', profile: this.getProfile(), update, request });
598
+ if (update.slope !== undefined) {
599
+ yield this.device.setSlope(update.slope);
600
+ }
601
+ if (update.targetPower !== undefined) {
602
+ yield this.device.setTargetPower(update.targetPower);
603
+ }
604
+ });
605
+ }
386
606
  pause() { this.paused = true; return Promise.resolve(true); }
387
607
  resume() { this.paused = false; return Promise.resolve(true); }
388
608
  }
package/lib/ble/hrm.js CHANGED
@@ -51,6 +51,7 @@ class BleHrmDevice extends ble_device_1.BleDevice {
51
51
  return { heartrate, rr, raw: data.toString('hex') };
52
52
  }
53
53
  onData(characteristic, data) {
54
+ super.onData(characteristic, data);
54
55
  if (characteristic.toLocaleLowerCase() === '2a37') {
55
56
  const res = this.parseHrm(data);
56
57
  this.emit('data', res);
@@ -1,11 +1,7 @@
1
1
  "use strict";
2
2
  var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
3
  if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
4
+ Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
9
5
  }) : (function(o, m, k, k2) {
10
6
  if (k2 === undefined) k2 = k;
11
7
  o[k2] = m[k];
package/lib/ble/pwr.d.ts CHANGED
@@ -72,6 +72,7 @@ export declare class PwrAdapter extends DeviceAdapter {
72
72
  transformData(bikeData: IncyclistBikeData): DeviceData;
73
73
  start(props?: any): Promise<any>;
74
74
  stop(): Promise<boolean>;
75
+ sendUpdate(request: any): Promise<void>;
75
76
  pause(): Promise<boolean>;
76
77
  resume(): Promise<boolean>;
77
78
  }
package/lib/ble/pwr.js CHANGED
@@ -109,6 +109,7 @@ class BleCyclingPowerDevice extends ble_device_1.BleDevice {
109
109
  return { instantaneousPower, balance, accTorque, rpm, time, raw: data.toString('hex') };
110
110
  }
111
111
  onData(characteristic, data) {
112
+ super.onData(characteristic, data);
112
113
  if (characteristic.toLocaleLowerCase() === '2a63') {
113
114
  const res = this.parsePower(data);
114
115
  this.emit('data', res);
@@ -254,6 +255,13 @@ class PwrAdapter extends Device_1.default {
254
255
  return this.device.disconnect();
255
256
  });
256
257
  }
258
+ sendUpdate(request) {
259
+ return __awaiter(this, void 0, void 0, function* () {
260
+ if (this.paused)
261
+ return;
262
+ this.getCyclingMode().sendBikeUpdate(request);
263
+ });
264
+ }
257
265
  pause() { this.paused = true; return Promise.resolve(true); }
258
266
  resume() { this.paused = false; return Promise.resolve(true); }
259
267
  }
@@ -1,11 +1,7 @@
1
1
  "use strict";
2
2
  var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
3
  if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
4
+ Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
9
5
  }) : (function(o, m, k, k2) {
10
6
  if (k2 === undefined) k2 = k;
11
7
  o[k2] = m[k];
@@ -1,11 +1,7 @@
1
1
  "use strict";
2
2
  var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
3
  if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
4
+ Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
9
5
  }) : (function(o, m, k, k2) {
10
6
  if (k2 === undefined) k2 = k;
11
7
  o[k2] = m[k];
@@ -1,11 +1,7 @@
1
1
  "use strict";
2
2
  var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
3
  if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
4
+ Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
9
5
  }) : (function(o, m, k, k2) {
10
6
  if (k2 === undefined) k2 = k;
11
7
  o[k2] = m[k];
@@ -1,5 +1,4 @@
1
1
  /// <reference types="node" />
2
- /// <reference types="node" />
3
2
  import { DeviceProtocol } from "../DeviceProtocol";
4
3
  import { EventLogger } from "gd-eventlog";
5
4
  import { Command } from "../types/command";
@@ -1,11 +1,7 @@
1
1
  "use strict";
2
2
  var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
3
  if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
4
+ Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
9
5
  }) : (function(o, m, k, k2) {
10
6
  if (k2 === undefined) k2 = k;
11
7
  o[k2] = m[k];
@@ -28,7 +28,7 @@ export default class KettlerRacerProtocol extends DeviceProtocolBase implements
28
28
  isBike(): boolean;
29
29
  isHrm(): boolean;
30
30
  isPower(): boolean;
31
- add(settings: DeviceSettings): import("../../DeviceProtocol").Device;
31
+ add(settings: DeviceSettings): any;
32
32
  scan(props: KettlerRacerScanProps): void;
33
33
  checkDevice(port: string): boolean;
34
34
  doScan(port: string): Promise<void>;
@@ -1,11 +1,7 @@
1
1
  "use strict";
2
2
  var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
3
  if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
4
+ Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
9
5
  }) : (function(o, m, k, k2) {
10
6
  if (k2 === undefined) k2 = k;
11
7
  o[k2] = m[k];
@@ -5,6 +5,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.config = void 0;
7
7
  const power_base_1 = __importDefault(require("./power-base"));
8
+ const MIN_SPEED = 10;
8
9
  exports.config = {
9
10
  name: 'PowerMeter',
10
11
  description: 'Power and cadence are taken from device. Speed is calculated from power and current slope\nThis mode will not respect maximum power and/or workout limits',
@@ -52,14 +53,20 @@ class PowerMeterCyclingMode extends power_base_1.default {
52
53
  power = 0;
53
54
  }
54
55
  const m = this.getWeight();
55
- let t = this.getTimeSinceLastUpdate();
56
+ const t = this.getTimeSinceLastUpdate();
56
57
  const { speed, distance } = this.calculateSpeedAndDistance(power, slope, m, t);
57
- data.speed = speed;
58
58
  data.power = Math.round(power);
59
- data.distanceInternal = Math.round(distanceInternal + distance);
60
59
  data.slope = slope;
60
+ if (power === 0 && speed < MIN_SPEED) {
61
+ data.speed = Math.round(prevData.speed - 1) < 0 ? 0 : Math.round(prevData.speed - 1);
62
+ data.distanceInternal = Math.round(distanceInternal + data.speed / 3.6 * t);
63
+ }
64
+ else {
65
+ data.speed = (power === 0 && speed < MIN_SPEED) ? 0 : speed;
66
+ data.distanceInternal = (power === 0 && speed < MIN_SPEED) ? Math.round(distanceInternal) : Math.round(distanceInternal + distance);
67
+ }
61
68
  if (props.log)
62
- this.logger.logEvent({ message: "updateData result", data, bikeData, prevSpeed: prevData.speed });
69
+ this.logger.logEvent({ message: "updateData result", data, bikeData, prevSpeed: prevData.speed, stopped: speed < MIN_SPEED });
63
70
  this.data = data;
64
71
  }
65
72
  catch (err) {
@@ -51,7 +51,7 @@ export declare class Simulator extends DeviceAdapter {
51
51
  export default class SimulatorProtocol extends DeviceProtocolBase {
52
52
  static NAME: string;
53
53
  constructor();
54
- add(settings: SimulatorSettings): Simulator;
54
+ add(settings: SimulatorSettings): any;
55
55
  getName(): string;
56
56
  getInterfaces(): string[];
57
57
  isBike(): boolean;
@@ -1,11 +1,7 @@
1
1
  "use strict";
2
2
  var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
3
  if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
4
+ Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
9
5
  }) : (function(o, m, k, k2) {
10
6
  if (k2 === undefined) k2 = k;
11
7
  o[k2] = m[k];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "incyclist-devices",
3
- "version": "1.4.47",
3
+ "version": "1.4.50",
4
4
  "dependencies": {
5
5
  "@serialport/parser-byte-length": "^9.0.1",
6
6
  "@serialport/parser-delimiter": "^9.0.1",