incyclist-devices 1.4.1 → 1.4.2

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.
@@ -52,10 +52,13 @@ export default interface CyclingMode {
52
52
  setSetting(name: string, value: any): void;
53
53
  getSetting(name: string): any;
54
54
  getSettings(): Settings;
55
+ setModeProperty(name: string, value: any): void;
56
+ getModeProperty(name: string): any;
55
57
  }
56
58
  export declare class CyclingModeBase implements CyclingMode {
57
59
  adapter: Device;
58
60
  settings: Settings;
61
+ properties: Settings;
59
62
  constructor(adapter: Device, props?: any);
60
63
  setAdapter(adapter: Device): void;
61
64
  getBikeInitRequest(): UpdateRequest;
@@ -69,4 +72,6 @@ export declare class CyclingModeBase implements CyclingMode {
69
72
  setSetting(name: string, value: any): void;
70
73
  getSetting(name: string): any;
71
74
  getSettings(): Settings;
75
+ setModeProperty(name: string, value: any): void;
76
+ getModeProperty(name: string): any;
72
77
  }
@@ -13,6 +13,7 @@ var CyclingModeProperyType;
13
13
  class CyclingModeBase {
14
14
  constructor(adapter, props) {
15
15
  this.settings = {};
16
+ this.properties = {};
16
17
  if (!adapter)
17
18
  throw new Error('IllegalArgument: adapter is null');
18
19
  this.setAdapter(adapter);
@@ -62,5 +63,17 @@ class CyclingModeBase {
62
63
  getSettings() {
63
64
  return this.settings;
64
65
  }
66
+ setModeProperty(name, value) {
67
+ this.properties[name] = value;
68
+ }
69
+ getModeProperty(name) {
70
+ const res = this.properties[name];
71
+ if (res !== undefined)
72
+ return res;
73
+ const prop = this.getProperties().find(p => p.key === name);
74
+ if (prop && prop.default !== undefined)
75
+ return prop.default;
76
+ return undefined;
77
+ }
65
78
  }
66
79
  exports.CyclingModeBase = CyclingModeBase;
@@ -176,8 +176,9 @@ class AntFEAdapter extends AntAdapter_1.default {
176
176
  });
177
177
  return __awaiter(this, void 0, void 0, function* () {
178
178
  yield _super.start.call(this, props);
179
- this.logger.logEvent({ message: 'start()' });
180
- const args = props || {};
179
+ this.logger.logEvent({ message: 'start()', props });
180
+ const opts = props || {};
181
+ const { args } = opts;
181
182
  return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () {
182
183
  if (this.ignoreHrm && this.ignoreBike && this.ignorePower) {
183
184
  this.logger.logEvent({ message: 'start() not done: bike disabled' });
@@ -1,6 +1,7 @@
1
1
  import { EventLogger } from 'gd-eventlog';
2
2
  import CyclingMode from '../CyclingMode';
3
3
  import DeviceAdapterBase, { Bike, DeviceAdapter } from '../Device';
4
+ import { User } from '../types/user';
4
5
  interface DaumAdapter {
5
6
  getCurrentBikeData(): Promise<any>;
6
7
  }
@@ -18,7 +19,7 @@ export default class DaumAdapterBase extends DeviceAdapterBase implements Device
18
19
  iv: any;
19
20
  logger: EventLogger;
20
21
  cyclingMode: CyclingMode;
21
- userSettings: any;
22
+ userSettings: User;
22
23
  bikeSettings: any;
23
24
  tsPrevData: number;
24
25
  adapterTime: number;
@@ -29,7 +30,7 @@ export default class DaumAdapterBase extends DeviceAdapterBase implements Device
29
30
  getSupportedCyclingModes(): Array<any>;
30
31
  getCyclingMode(): CyclingMode;
31
32
  getDefaultCyclingMode(): CyclingMode;
32
- setUserSettings(userSettings: any): void;
33
+ setUserSettings(userSettings: User): void;
33
34
  setBikeSettings(bikeSettings: any): void;
34
35
  getWeight(): number;
35
36
  getCurrentBikeData(): Promise<any>;
@@ -11,7 +11,7 @@ const config = {
11
11
  description: "Calculates speed based on power and slope. Power is either set by workout or calculated based on gear and cadence",
12
12
  properties: [
13
13
  { key: 'bikeType', name: 'Bike Type', description: '', type: CyclingMode_1.CyclingModeProperyType.SingleSelect, options: ['Race', 'Mountain', 'Triathlon'], default: 'Race' },
14
- { key: 'startPower', name: 'Starting Power', description: 'Initial power in Watts at start of raining', type: CyclingMode_1.CyclingModeProperyType.Integer, default: 50, min: 25, max: 800 },
14
+ { 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 },
15
15
  ]
16
16
  };
17
17
  class ERGCyclingMode extends CyclingMode_1.CyclingModeBase {
@@ -13,7 +13,7 @@ const config = {
13
13
  description: "Calculates power based on speed and slope.",
14
14
  properties: [
15
15
  { key: 'bikeType', name: 'Bike Type', description: '', type: CyclingMode_1.CyclingModeProperyType.SingleSelect, options: ['Race', 'Mountain', 'Triathlon'], default: 'Race' },
16
- { key: 'startPower', name: 'Starting Power', description: 'Initial power in Watts at start of raining', type: CyclingMode_1.CyclingModeProperyType.Integer, default: 50 },
16
+ { key: 'startPower', name: 'Starting Power', description: 'Initial power in Watts at start of training', type: CyclingMode_1.CyclingModeProperyType.Integer, default: 50 },
17
17
  { key: 'minPower', name: 'Minimum Power', description: 'Minimum power in declines', type: CyclingMode_1.CyclingModeProperyType.Integer, default: 50 },
18
18
  { key: 'simulation', name: 'Simulate ', description: 'Simulate ', type: CyclingMode_1.CyclingModeProperyType.Boolean, default: false },
19
19
  { key: 'chainRings', name: 'Chain Rings', description: 'Simulated chain rings (format: <min>-<max>)', type: CyclingMode_1.CyclingModeProperyType.String, validation: '', default: '36-52', condition: (s) => s.simulation },
@@ -83,7 +83,7 @@ class DaumClassicAdapter extends DaumAdapter_1.default {
83
83
  return __awaiter(this, void 0, void 0, function* () {
84
84
  this.logger.logEvent({ message: 'start()', props });
85
85
  const opts = props || {};
86
- const person = props;
86
+ const { user } = props;
87
87
  this.initData();
88
88
  let startState = {};
89
89
  return (0, utils_1.runWithRetries)(() => __awaiter(this, void 0, void 0, function* () {
@@ -96,7 +96,7 @@ class DaumClassicAdapter extends DaumAdapter_1.default {
96
96
  startState.setProg = true;
97
97
  }
98
98
  if (!startState.setPerson) {
99
- yield this.getBike().setPerson({ person });
99
+ yield this.getBike().setPerson(user);
100
100
  startState.setPerson = true;
101
101
  }
102
102
  if (!startState.startProg) {
@@ -1,5 +1,6 @@
1
1
  import { EventLogger } from 'gd-eventlog';
2
2
  import { Queue } from '../../utils';
3
+ import { User } from '../../types/user';
3
4
  declare type SuccessCallbackFn = (data: any) => void;
4
5
  declare type ErrorCallbackFn = (status: number, error: any) => void;
5
6
  interface CommandInstructions {
@@ -32,7 +33,7 @@ export default class Daum8008 {
32
33
  getType(): string;
33
34
  getPort(): string;
34
35
  isConnected(): boolean;
35
- setUser(user: any, callback: any): void;
36
+ setUser(user: User, callback: any): void;
36
37
  getUserWeight(): number;
37
38
  getBikeWeight(): any;
38
39
  connect(): void;
@@ -54,7 +55,7 @@ export default class Daum8008 {
54
55
  stopProg(bikeNo?: number): Promise<unknown>;
55
56
  setProg(progNo?: number, bikeNo?: number): Promise<unknown>;
56
57
  setBikeType(bikeType: any, bikeNo?: number): Promise<unknown>;
57
- setPerson(user?: any, bikeNo?: number): Promise<unknown>;
58
+ setPerson(user?: User, bikeNo?: number): Promise<unknown>;
58
59
  runData(bikeNo?: number): Promise<unknown>;
59
60
  setGear(gear: any, bikeNo?: number): Promise<unknown>;
60
61
  setPower(power: any, bikeNo?: number): Promise<unknown>;
@@ -15,8 +15,8 @@ class Daum8008 {
15
15
  this.portName = opts.port || process.env.COM_PORT;
16
16
  this.settings = opts.settings || {};
17
17
  this.bikeData = {
18
- userWeight: 75,
19
- bikeWeight: 10,
18
+ userWeight: utils_1.DEFAULT_USER_WEIGHT,
19
+ bikeWeight: utils_1.DEFAULT_BIKE_WEIGHT,
20
20
  maxPower: 800
21
21
  };
22
22
  this.sp = undefined;
@@ -45,7 +45,8 @@ class Daum8008 {
45
45
  }
46
46
  setUser(user, callback) {
47
47
  this.logger.logEvent({ message: "setUser()", user });
48
- this.settings.user = user || {};
48
+ if (user)
49
+ this.settings.user = user;
49
50
  var cb = callback || nop;
50
51
  cb(200, user);
51
52
  }
@@ -361,7 +362,7 @@ class Daum8008 {
361
362
  });
362
363
  }
363
364
  setPerson(user = {}, bikeNo = 0) {
364
- const age = user.age !== undefined ? user.age : (0, utils_1.getAge)(user.birthday);
365
+ const age = user.age !== undefined ? user.age : utils_1.DEFAULT_AGE;
365
366
  const gender = (0, utils_1.getGender)(user.sex);
366
367
  const length = (0, utils_1.getLength)(user.length);
367
368
  const maxPower = this.settings.maxPower === undefined ? 800 : this.settings.maxPower;
@@ -1,6 +1,8 @@
1
+ export declare const DEFAULT_AGE = 30;
2
+ export declare const DEFAULT_USER_WEIGHT = 75;
3
+ export declare const DEFAULT_BIKE_WEIGHT = 10;
1
4
  export declare function getCockpit(c: any): "Cardio" | "Fitness" | "Vita De Luxe" | "8008" | "8008 TRS" | "8080" | "Therapie" | "8008 TRS Pro" | "8008 TRS3" | "Unknown";
2
5
  export declare function getBikeType(type: any): 1 | 0;
3
- export declare function getAge(birthday: any): number;
4
6
  export declare function getGender(sex: any): 1 | 2;
5
7
  export declare function getLength(length: any): number;
6
8
  export declare function getWeight(weight?: any): number;
@@ -1,6 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.Float32ToIntArray = exports.Float32ToHex = exports.hexstr = exports.buildError = exports.parseRunData = exports.getWeight = exports.getLength = exports.getGender = exports.getAge = exports.getBikeType = exports.getCockpit = void 0;
3
+ exports.Float32ToIntArray = exports.Float32ToHex = exports.hexstr = exports.buildError = exports.parseRunData = exports.getWeight = exports.getLength = exports.getGender = exports.getBikeType = exports.getCockpit = exports.DEFAULT_BIKE_WEIGHT = exports.DEFAULT_USER_WEIGHT = exports.DEFAULT_AGE = void 0;
4
+ exports.DEFAULT_AGE = 30;
5
+ exports.DEFAULT_USER_WEIGHT = 75;
6
+ exports.DEFAULT_BIKE_WEIGHT = 10;
4
7
  function getCockpit(c) {
5
8
  switch (c) {
6
9
  case 10:
@@ -41,21 +44,6 @@ function getBikeType(type) {
41
44
  }
42
45
  }
43
46
  exports.getBikeType = getBikeType;
44
- function getAge(birthday) {
45
- if (birthday === undefined) {
46
- return 30;
47
- }
48
- try {
49
- const bd = new Date(birthday);
50
- const ageDifMs = Date.now() - bd.getTime();
51
- var ageDate = new Date(ageDifMs);
52
- return Math.abs(ageDate.getUTCFullYear() - 1970);
53
- }
54
- catch (error) {
55
- return 30;
56
- }
57
- }
58
- exports.getAge = getAge;
59
47
  function getGender(sex) {
60
48
  if (sex === undefined)
61
49
  return 2;
@@ -0,0 +1,14 @@
1
+ import CyclingMode, { CyclingModeProperty, IncyclistBikeData, Settings, UpdateRequest } from "../../CyclingMode";
2
+ import DaumAdapter from "../DaumAdapter";
3
+ import PowerMeterCyclingMode from "../PowerMeterCyclingMode";
4
+ export default class DaumClassicCyclingMode extends PowerMeterCyclingMode implements CyclingMode {
5
+ constructor(adapter: DaumAdapter, props?: Settings);
6
+ getName(): string;
7
+ getDescription(): string;
8
+ getProperties(): CyclingModeProperty[];
9
+ getProperty(name: string): CyclingModeProperty;
10
+ getBikeInitRequest(): UpdateRequest;
11
+ getSettings(): Settings;
12
+ getSetting(name: string): any;
13
+ updateData(data: IncyclistBikeData): IncyclistBikeData;
14
+ }
@@ -0,0 +1,82 @@
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 gd_eventlog_1 = require("gd-eventlog");
7
+ const CyclingMode_1 = require("../../CyclingMode");
8
+ const PowerMeterCyclingMode_1 = __importDefault(require("../PowerMeterCyclingMode"));
9
+ const config = {
10
+ name: "Daum Classic",
11
+ description: "The device calculates speed and power based on slope. Incyclist will not modify any values recived from the device\nThis mode will not respect maximum power and/or workout limits",
12
+ properties: [
13
+ { key: 'bikeType', name: 'Bike Type', description: '', type: CyclingMode_1.CyclingModeProperyType.SingleSelect, options: ['Race', 'Mountain'], default: 'Race' },
14
+ ]
15
+ };
16
+ class DaumClassicCyclingMode extends PowerMeterCyclingMode_1.default {
17
+ constructor(adapter, props) {
18
+ super(adapter, props);
19
+ this.logger = adapter ? adapter.logger : undefined;
20
+ if (!this.logger)
21
+ this.logger = new gd_eventlog_1.EventLogger('DaumClassic');
22
+ this.setModeProperty('eppSupport', true);
23
+ this.setModeProperty('setPersonSupport', true);
24
+ }
25
+ getName() {
26
+ return config.name;
27
+ }
28
+ getDescription() {
29
+ return config.description;
30
+ }
31
+ getProperties() {
32
+ return config.properties;
33
+ }
34
+ getProperty(name) {
35
+ return config.properties.find(p => p.name === name);
36
+ }
37
+ getBikeInitRequest() {
38
+ return {};
39
+ }
40
+ getSettings() {
41
+ const settings = super.getSettings();
42
+ settings['setPerson'] = true;
43
+ return settings;
44
+ }
45
+ getSetting(name) {
46
+ if (name === 'setPerson')
47
+ return true;
48
+ return super.getSetting(name);
49
+ }
50
+ updateData(data) {
51
+ try {
52
+ const prevData = this.data || {};
53
+ const prevRequest = this.prevRequest || {};
54
+ const bikeData = JSON.parse(JSON.stringify(data));
55
+ let power = data.power || 0;
56
+ let speed = data.speed || 0;
57
+ let slope = (prevData.slope !== undefined ? prevData.slope : prevRequest.slope || 0);
58
+ let distanceInternal = prevData.distanceInternal || 0;
59
+ if (!bikeData.pedalRpm || bikeData.isPedalling === false) {
60
+ speed = 0;
61
+ power = 0;
62
+ }
63
+ let ts = Date.now();
64
+ let v = speed / 3.6;
65
+ let duration = this.prevUpdateTS === 0 ? 0 : ((ts - this.prevUpdateTS) / 1000);
66
+ distanceInternal += Math.round(v * duration);
67
+ data.speed = parseFloat(speed.toFixed(1));
68
+ data.power = Math.round(power);
69
+ data.distanceInternal = Math.round(distanceInternal);
70
+ data.distance = Math.round(distanceInternal / 100);
71
+ data.slope = slope;
72
+ this.logger.logEvent({ message: "updateData result", data, bikeData, prevRequest: {}, prevSpeed: prevData.speed });
73
+ this.data = JSON.parse(JSON.stringify(data));
74
+ this.prevUpdateTS = ts;
75
+ }
76
+ catch (err) {
77
+ this.logger.logEvent({ message: 'error', fn: 'updateData()', error: err.message || err });
78
+ }
79
+ return data;
80
+ }
81
+ }
82
+ exports.default = DaumClassicCyclingMode;
@@ -1,3 +1,4 @@
1
+ import { Route } from '../../types/route';
1
2
  import DaumAdapter from '../DaumAdapter';
2
3
  export default class DaumPremiumDevice extends DaumAdapter {
3
4
  static NAME: string;
@@ -5,7 +6,9 @@ export default class DaumPremiumDevice extends DaumAdapter {
5
6
  getName(): string;
6
7
  getPort(): any;
7
8
  getInterface(): any;
9
+ getSupportedCyclingModes(): Array<any>;
8
10
  check(): Promise<unknown>;
11
+ initClassic(route: Route): Promise<boolean>;
9
12
  start(props: any): Promise<unknown>;
10
13
  getCurrentBikeData(): Promise<any>;
11
14
  }
@@ -15,6 +15,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
15
15
  const gd_eventlog_1 = require("gd-eventlog");
16
16
  const utils_1 = require("../../utils");
17
17
  const DaumAdapter_1 = __importDefault(require("../DaumAdapter"));
18
+ const DaumClassicCyclingMode_1 = __importDefault(require("./DaumClassicCyclingMode"));
18
19
  const PROTOCOL_NAME = "Daum Premium";
19
20
  class DaumPremiumDevice extends DaumAdapter_1.default {
20
21
  constructor(protocol, bike) {
@@ -38,6 +39,11 @@ class DaumPremiumDevice extends DaumAdapter_1.default {
38
39
  getInterface() {
39
40
  return this.bike.getInterface();
40
41
  }
42
+ getSupportedCyclingModes() {
43
+ const supported = super.getSupportedCyclingModes();
44
+ supported.push(DaumClassicCyclingMode_1.default);
45
+ return supported;
46
+ }
41
47
  check() {
42
48
  var info = {};
43
49
  return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () {
@@ -56,10 +62,28 @@ class DaumPremiumDevice extends DaumAdapter_1.default {
56
62
  }
57
63
  }));
58
64
  }
65
+ initClassic(route) {
66
+ return __awaiter(this, void 0, void 0, function* () {
67
+ if (!route)
68
+ return true;
69
+ let res;
70
+ const bikeType = this.getCyclingMode().getSetting('bikeType');
71
+ res = yield this.bike.programUpload(bikeType, route);
72
+ if (!res)
73
+ return false;
74
+ res = yield this.bike.startProgram(route.programId);
75
+ if (!res)
76
+ return false;
77
+ });
78
+ }
59
79
  start(props) {
60
80
  return __awaiter(this, void 0, void 0, function* () {
61
- this.logger.logEvent({ message: 'start()', props });
81
+ this.logger.logEvent({ message: 'start()' });
82
+ console.log('~~~setPersonSupport:', this.getCyclingMode().getModeProperty('setPersonSupport'));
83
+ console.log('~~~eppSupport:', this.getCyclingMode().getModeProperty('eppSupport'));
62
84
  const opts = props || {};
85
+ const user = opts.user || this.userSettings;
86
+ const route = opts.route;
63
87
  var info = {};
64
88
  this.initData();
65
89
  return (0, utils_1.runWithRetries)(() => __awaiter(this, void 0, void 0, function* () {
@@ -75,10 +99,23 @@ class DaumPremiumDevice extends DaumAdapter_1.default {
75
99
  if (!info.version) {
76
100
  info.version = yield this.bike.getProtocolVersion();
77
101
  }
102
+ if (!info.init && this.getCyclingMode().getModeProperty('setPersonSupport')) {
103
+ info.init = yield this.initClassic(route);
104
+ }
105
+ else {
106
+ info.init = true;
107
+ }
108
+ if (!info.person && this.getCyclingMode().getModeProperty('eppSupport')) {
109
+ info.person = yield this.bike.setPerson(user);
110
+ }
111
+ else {
112
+ info.person = true;
113
+ }
78
114
  const gear = yield this.bike.setGear(this.data.gear || (opts.gear || 10));
79
115
  return gear;
80
116
  }
81
117
  catch (err) {
118
+ console.error(err);
82
119
  throw (new Error(`could not start device, reason:${err.message}`));
83
120
  }
84
121
  }), 5, 1000)
@@ -1,5 +1,9 @@
1
+ /// <reference types="node" />
2
+ import { ReservedCommands, BikeType } from './utils';
1
3
  import { Queue } from '../../utils';
2
4
  import { EventLogger } from 'gd-eventlog';
5
+ import { User } from "../../types/user";
6
+ import { Route } from "../../types/route";
3
7
  declare class Daum8i {
4
8
  portName: string;
5
9
  logger: EventLogger;
@@ -63,7 +67,7 @@ declare class Daum8i {
63
67
  sendDaum8iCommand(command: any, queryType: any, payload: any): Promise<unknown>;
64
68
  sendACK(): void;
65
69
  sendNAK(): void;
66
- sendReservedDaum8iCommand(command: any, cmdType: any, data: any): Promise<any[]>;
70
+ sendReservedDaum8iCommand(command: ReservedCommands, cmdType: any, data?: Buffer): Promise<any[]>;
67
71
  getProtocolVersion(): Promise<string>;
68
72
  getDashboardVersion(): Promise<unknown>;
69
73
  getDeviceType(): Promise<any>;
@@ -89,7 +93,13 @@ declare class Daum8i {
89
93
  setSlope(slope: any): void;
90
94
  setPower(power: any): Promise<number>;
91
95
  getPower(power: any): Promise<number>;
92
- setPerson(person: any): Promise<any[]>;
96
+ setPerson(person: User): Promise<boolean>;
97
+ programUploadInit(): Promise<boolean>;
98
+ programUploadStart(bikeType: BikeType, route: Route): Promise<Uint8Array>;
99
+ programUploadSendBlock(epp: Uint8Array, offset: number): Promise<boolean>;
100
+ programUploadDone(): Promise<boolean>;
101
+ programUpload(bikeType: BikeType, route: Route): Promise<boolean>;
102
+ startProgram(programId?: number): Promise<boolean>;
93
103
  setGear(gear: any): Promise<number>;
94
104
  getGear(): Promise<number>;
95
105
  }
@@ -25,6 +25,7 @@ const TIMEOUT_START = 15000;
25
25
  const OPEN_TIMEOUT = 1000;
26
26
  const DAUM_PREMIUM_DEFAULT_PORT = 51955;
27
27
  const DAUM_PREMIUM_DEFAULT_HOST = '127.0.0.1';
28
+ const MAX_DATA_BLOCK_SIZE = 512;
28
29
  var __SerialPort = undefined;
29
30
  var net = undefined;
30
31
  const DEBUG_LOGGER = {
@@ -574,20 +575,24 @@ class Daum8i {
574
575
  this.logger.logEvent({ message: "sendCommand:sending NAK", port });
575
576
  }
576
577
  sendReservedDaum8iCommand(command, cmdType, data) {
577
- let cmdData = [];
578
- const key = (0, utils_1.getReservedCommandKey)(command);
579
- (0, utils_1.append)(cmdData, (0, utils_1.Int16ToIntArray)(key));
578
+ let buffer;
580
579
  if (data !== undefined && data.length > 0) {
581
- (0, utils_1.append)(cmdData, (0, utils_1.Int16ToIntArray)(data.length));
582
- (0, utils_1.append)(cmdData, data);
580
+ buffer = Buffer.alloc(data.length + 4);
581
+ buffer.writeInt16LE(command, 0);
582
+ buffer.writeUInt16LE(data.length, 2);
583
+ data.copy(buffer, 4);
583
584
  }
584
585
  else {
585
- (0, utils_1.append)(cmdData, (0, utils_1.Int16ToIntArray)(0));
586
+ buffer = Buffer.alloc(4);
587
+ buffer.writeInt16LE(command, 0);
588
+ buffer.writeUInt16LE(0, 2);
586
589
  }
590
+ const cmdData = Uint8Array.from(buffer);
587
591
  return this.sendDaum8iCommand('M70', cmdType, (0, utils_1.bin2esc)(cmdData))
588
- .then((resData) => {
592
+ .then((res) => {
593
+ const resData = Uint8Array.from(res, x => x.charCodeAt(0));
589
594
  const cmd = (0, utils_1.esc2bin)(resData);
590
- cmd.splice(0, 4);
595
+ console.log('~~~ cmd', cmd);
591
596
  return cmd;
592
597
  });
593
598
  }
@@ -706,7 +711,100 @@ class Daum8i {
706
711
  });
707
712
  }
708
713
  setPerson(person) {
709
- return this.sendReservedDaum8iCommand('PERSON_SET', 'BF', person.getData());
714
+ return this.sendReservedDaum8iCommand(utils_1.ReservedCommands.PERSON_SET, 'BF', (0, utils_1.getPersonData)(person))
715
+ .then((res) => {
716
+ const buffer = Buffer.from(res);
717
+ return buffer.readInt16LE(0) === utils_1.ReservedCommands.PERSON_SET;
718
+ });
719
+ }
720
+ programUploadInit() {
721
+ return this.sendReservedDaum8iCommand(utils_1.ReservedCommands.PROGRAM_LIST_BEGIN, 'BF')
722
+ .then((res) => {
723
+ const buffer = Buffer.from(res);
724
+ return buffer.readInt16LE(0) === utils_1.ReservedCommands.PROGRAM_LIST_BEGIN;
725
+ });
726
+ }
727
+ programUploadStart(bikeType, route) {
728
+ const payload = Buffer.alloc(40);
729
+ const epp = (0, utils_1.routeToEpp)(route);
730
+ payload.writeInt32LE(0, 0);
731
+ payload.writeInt8(bikeType, 4);
732
+ payload.writeInt8(bikeType, 5);
733
+ payload.writeInt16LE(0, 6);
734
+ payload.writeInt32LE(0, 8);
735
+ payload.writeInt32LE(0, 12);
736
+ payload.writeFloatLE(0, 16);
737
+ payload.writeFloatLE(0, 20);
738
+ payload.writeInt16LE(0, 24);
739
+ payload.writeInt16LE(0, 26);
740
+ payload.writeInt16LE(0, 28);
741
+ payload.writeInt16LE(0, 30);
742
+ payload.writeInt32LE(7, 32);
743
+ payload.writeInt32LE(epp.length, 36);
744
+ return this.sendReservedDaum8iCommand(utils_1.ReservedCommands.PROGRAM_LIST_NEW_PROGRAM, 'BF', payload)
745
+ .then((res) => {
746
+ const buffer = Buffer.from(res);
747
+ if (buffer.readInt16LE(0) === utils_1.ReservedCommands.PROGRAM_LIST_NEW_PROGRAM) {
748
+ return epp;
749
+ }
750
+ throw new Error('Illegal Response');
751
+ });
752
+ }
753
+ programUploadSendBlock(epp, offset) {
754
+ const remaining = epp.length - offset;
755
+ if (remaining <= 0)
756
+ return Promise.resolve(true);
757
+ const size = remaining > MAX_DATA_BLOCK_SIZE ? MAX_DATA_BLOCK_SIZE : remaining;
758
+ const payload = Buffer.alloc(size + 8);
759
+ payload.writeInt32LE(size, 0);
760
+ payload.writeInt32LE(offset, 4);
761
+ const chunk = Buffer.from(epp.slice(offset, offset + size));
762
+ chunk.copy(payload, 8);
763
+ return this.sendReservedDaum8iCommand(utils_1.ReservedCommands.PROGRAM_LIST_CONTINUE_PROGRAM, 'BF', payload)
764
+ .then((res) => {
765
+ const buffer = Buffer.from(res);
766
+ let success = buffer.readInt16LE(0) === utils_1.ReservedCommands.PROGRAM_LIST_CONTINUE_PROGRAM;
767
+ console.log('~~ Buffer', buffer, buffer.readInt16LE(2), buffer.readInt8(4));
768
+ success = success && (buffer.readInt16LE(2) === 1);
769
+ success = success && (buffer.readInt8(4) === 1);
770
+ if (!success)
771
+ throw new Error('Illegal Response');
772
+ return success;
773
+ });
774
+ }
775
+ programUploadDone() {
776
+ return this.sendReservedDaum8iCommand(utils_1.ReservedCommands.PROGRAM_LIST_END, 'BF')
777
+ .then((res) => {
778
+ const buffer = Buffer.from(res);
779
+ return buffer.readInt16LE(0) === utils_1.ReservedCommands.PROGRAM_LIST_END;
780
+ });
781
+ }
782
+ programUpload(bikeType, route) {
783
+ return __awaiter(this, void 0, void 0, function* () {
784
+ yield this.programUploadInit();
785
+ const epp = yield this.programUploadStart(bikeType, route);
786
+ let success = true;
787
+ let done = false;
788
+ let offset = 0;
789
+ while (success && !done) {
790
+ success = yield this.programUploadSendBlock(epp, offset);
791
+ offset += MAX_DATA_BLOCK_SIZE;
792
+ done = offset >= epp.length;
793
+ }
794
+ if (done) {
795
+ return yield this.programUploadDone();
796
+ }
797
+ return false;
798
+ });
799
+ }
800
+ startProgram(programId = 1) {
801
+ const payload = Buffer.alloc(2);
802
+ payload.writeInt16LE(programId, 0);
803
+ return this.sendReservedDaum8iCommand(utils_1.ReservedCommands.PROGRAM_LIST_START, 'BF', payload)
804
+ .then((res) => {
805
+ const buffer = Buffer.from(res);
806
+ return buffer.readInt16LE(0) === utils_1.ReservedCommands.PROGRAM_LIST_START;
807
+ });
710
808
  }
711
809
  setGear(gear) {
712
810
  return this.sendDaum8iCommand('M71', 'BF', `${gear}`)
@@ -1,3 +1,6 @@
1
+ /// <reference types="node" />
2
+ import { Route } from "../../types/route";
3
+ import { User } from "../../types/user";
1
4
  export declare function bin2esc(arr: any): any[];
2
5
  export declare function esc2bin(arr: any): any[];
3
6
  export declare function checkSum(cmdArr: any, payload: any): string;
@@ -14,7 +17,33 @@ export declare function Float32ToHex(float32: any): any;
14
17
  export declare function Float32ToIntArray(float32: any): any[];
15
18
  export declare function Int16ToIntArray(int16: any): any[];
16
19
  export declare function Int32ToIntArray(int32: any): any[];
17
- export declare function getReservedCommandKey(cmdStr: any): any;
20
+ export declare enum ReservedCommands {
21
+ RESULT_RESET = 0,
22
+ RESULT_GET = 1,
23
+ NETRACE_START = 2,
24
+ NETRACE_STOP = 3,
25
+ NETRACE_USERNAME = 4,
26
+ NETRACE_USERDATA = 5,
27
+ PERSON_GET = 6,
28
+ PERSON_SET = 7,
29
+ PROGRAM_LIST_BEGIN = 8,
30
+ PROGRAM_LIST_NEW_PROGRAM = 9,
31
+ PROGRAM_LIST_CONTINUE_PROGRAM = 10,
32
+ PROGRAM_LIST_END = 11,
33
+ PROGRAM_LIST_START = 12,
34
+ RELAX_START = 12,
35
+ RELAX_STOP = 14,
36
+ RELAX_GET_DATA = 15,
37
+ KEY_PRESSED = 16,
38
+ PROGRAM_CONTROL = 17
39
+ }
40
+ export declare enum BikeType {
41
+ ALLROUND = 0,
42
+ RACE = 1,
43
+ MOUNTAIN = 2
44
+ }
45
+ export declare function getBikeType(bikeTypeStr?: string): BikeType;
46
+ export declare function routeToEpp(route: Route, date?: Date): Uint8Array;
18
47
  export declare function parseTrainingData(payload: any): {
19
48
  time: number;
20
49
  heartrate: number;
@@ -30,3 +59,4 @@ export declare function parseTrainingData(payload: any): {
30
59
  deviceState: number;
31
60
  speedStatus: string;
32
61
  };
62
+ export declare function getPersonData(user: User): Buffer;
@@ -1,6 +1,12 @@
1
1
  "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
2
5
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.parseTrainingData = exports.getReservedCommandKey = exports.Int32ToIntArray = exports.Int16ToIntArray = exports.Float32ToIntArray = exports.Float32ToHex = exports.getAsciiArrayFromStr = exports.asciiArrayToString = exports.charArrayToString = exports.ascii = exports.append = exports.getHex = exports.hexstr = exports.getMessageData = exports.buildMessage = exports.checkSum = exports.esc2bin = exports.bin2esc = void 0;
6
+ exports.getPersonData = exports.parseTrainingData = exports.routeToEpp = exports.getBikeType = exports.BikeType = exports.ReservedCommands = exports.Int32ToIntArray = exports.Int16ToIntArray = exports.Float32ToIntArray = exports.Float32ToHex = exports.getAsciiArrayFromStr = exports.asciiArrayToString = exports.charArrayToString = exports.ascii = exports.append = exports.getHex = exports.hexstr = exports.getMessageData = exports.buildMessage = exports.checkSum = exports.esc2bin = exports.bin2esc = void 0;
7
+ const user_1 = require("../../types/user");
8
+ const utils_1 = require("../classic/utils");
9
+ const win32filetime_1 = __importDefault(require("win32filetime"));
4
10
  const sum = (arr) => arr.reduce((a, b) => a + b, 0);
5
11
  function bin2esc(arr) {
6
12
  if (arr === undefined) {
@@ -114,7 +120,7 @@ function getMessageData(command) {
114
120
  }
115
121
  exports.getMessageData = getMessageData;
116
122
  function hexstr(arr, start, len) {
117
- const isArr = Array.isArray(arr);
123
+ const isArr = Array.isArray(arr) || arr instanceof Uint8Array;
118
124
  if (!arr)
119
125
  return '';
120
126
  var str = "";
@@ -211,30 +217,95 @@ function Int32ToIntArray(int32) {
211
217
  return arr;
212
218
  }
213
219
  exports.Int32ToIntArray = Int32ToIntArray;
214
- const __commands = {
215
- RESULT_RESET: 0,
216
- RESULT_GET: 1,
217
- NETRACE_START: 2,
218
- NETRACE_STOP: 3,
219
- NETRACE_USERNAME: 4,
220
- NETRACE_USERDATA: 5,
221
- PERSON_GET: 6,
222
- PERSON_SET: 7,
223
- PROGRAM_LIST_BEGIN: 8,
224
- PROGRAM_LIST_NEW_PROGRAM: 9,
225
- PROGRAM_LIST_CONTINUE_PROGRAM: 10,
226
- PROGRAM_LIST_END: 11,
227
- PROGRAM_LIST_START: 12,
228
- RELAX_START: 12,
229
- RELAX_STOP: 14,
230
- RELAX_GET_DATA: 0xF,
231
- KEY_PRESSED: 0x10,
232
- PROGRAM_CONTROL: 17
233
- };
234
- function getReservedCommandKey(cmdStr) {
235
- return __commands[cmdStr];
220
+ var ReservedCommands;
221
+ (function (ReservedCommands) {
222
+ ReservedCommands[ReservedCommands["RESULT_RESET"] = 0] = "RESULT_RESET";
223
+ ReservedCommands[ReservedCommands["RESULT_GET"] = 1] = "RESULT_GET";
224
+ ReservedCommands[ReservedCommands["NETRACE_START"] = 2] = "NETRACE_START";
225
+ ReservedCommands[ReservedCommands["NETRACE_STOP"] = 3] = "NETRACE_STOP";
226
+ ReservedCommands[ReservedCommands["NETRACE_USERNAME"] = 4] = "NETRACE_USERNAME";
227
+ ReservedCommands[ReservedCommands["NETRACE_USERDATA"] = 5] = "NETRACE_USERDATA";
228
+ ReservedCommands[ReservedCommands["PERSON_GET"] = 6] = "PERSON_GET";
229
+ ReservedCommands[ReservedCommands["PERSON_SET"] = 7] = "PERSON_SET";
230
+ ReservedCommands[ReservedCommands["PROGRAM_LIST_BEGIN"] = 8] = "PROGRAM_LIST_BEGIN";
231
+ ReservedCommands[ReservedCommands["PROGRAM_LIST_NEW_PROGRAM"] = 9] = "PROGRAM_LIST_NEW_PROGRAM";
232
+ ReservedCommands[ReservedCommands["PROGRAM_LIST_CONTINUE_PROGRAM"] = 10] = "PROGRAM_LIST_CONTINUE_PROGRAM";
233
+ ReservedCommands[ReservedCommands["PROGRAM_LIST_END"] = 11] = "PROGRAM_LIST_END";
234
+ ReservedCommands[ReservedCommands["PROGRAM_LIST_START"] = 12] = "PROGRAM_LIST_START";
235
+ ReservedCommands[ReservedCommands["RELAX_START"] = 12] = "RELAX_START";
236
+ ReservedCommands[ReservedCommands["RELAX_STOP"] = 14] = "RELAX_STOP";
237
+ ReservedCommands[ReservedCommands["RELAX_GET_DATA"] = 15] = "RELAX_GET_DATA";
238
+ ReservedCommands[ReservedCommands["KEY_PRESSED"] = 16] = "KEY_PRESSED";
239
+ ReservedCommands[ReservedCommands["PROGRAM_CONTROL"] = 17] = "PROGRAM_CONTROL";
240
+ })(ReservedCommands = exports.ReservedCommands || (exports.ReservedCommands = {}));
241
+ var BikeType;
242
+ (function (BikeType) {
243
+ BikeType[BikeType["ALLROUND"] = 0] = "ALLROUND";
244
+ BikeType[BikeType["RACE"] = 1] = "RACE";
245
+ BikeType[BikeType["MOUNTAIN"] = 2] = "MOUNTAIN";
246
+ })(BikeType = exports.BikeType || (exports.BikeType = {}));
247
+ function getBikeType(bikeTypeStr) {
248
+ if (bikeTypeStr === undefined || bikeTypeStr === null)
249
+ return BikeType.RACE;
250
+ if (bikeTypeStr.toLowerCase() === 'mountain')
251
+ return BikeType.MOUNTAIN;
252
+ return BikeType.RACE;
253
+ }
254
+ exports.getBikeType = getBikeType;
255
+ function routeToEpp(route, date) {
256
+ const buffer = Buffer.alloc(376 + route.points.length * 12);
257
+ const fileTime = win32filetime_1.default.fromUnix(date ? date : Date.now());
258
+ let offset = 0;
259
+ const name = route.name || '';
260
+ const description = route.description || '';
261
+ const minElevation = Math.min(...route.points.map(p => p.elevation));
262
+ const maxElevation = Math.max(...route.points.map(p => p.elevation));
263
+ const sampleRate = route.points.length !== 0 ? route.totalDistance / route.points.length : 0;
264
+ buffer.writeUInt32LE(fileTime.low, offset);
265
+ offset += 4;
266
+ buffer.writeUInt32LE(fileTime.high, offset);
267
+ offset += 4;
268
+ buffer.write(name, offset, name.length, 'ascii');
269
+ offset += 64;
270
+ buffer.write(description, offset, description.length, 'ascii');
271
+ offset += 256;
272
+ buffer.writeInt32LE(0x10, offset);
273
+ offset += 4;
274
+ buffer.writeInt32LE(0x0, offset);
275
+ offset += 4;
276
+ buffer.writeInt32LE(minElevation, offset);
277
+ offset += 4;
278
+ buffer.writeInt32LE(maxElevation, offset);
279
+ offset += 4;
280
+ buffer.writeInt32LE(route.points.length, offset);
281
+ offset += 4;
282
+ buffer.writeInt32LE(sampleRate, offset);
283
+ offset += 4;
284
+ buffer.writeInt32LE(0x01, offset);
285
+ offset += 4;
286
+ buffer.writeInt32LE(0x0, offset);
287
+ offset += 4;
288
+ buffer.writeInt16LE(0x0, offset);
289
+ offset += 2;
290
+ buffer.writeInt16LE(0x0, offset);
291
+ offset += 2;
292
+ buffer.writeInt32LE(0x0, offset);
293
+ offset += 4;
294
+ buffer.writeInt32LE(0x0, offset);
295
+ offset += 4;
296
+ buffer.writeInt32LE(0x0, offset);
297
+ offset += 4;
298
+ route.points.forEach(p => {
299
+ buffer.writeUInt32LE(sampleRate, offset);
300
+ offset += 4;
301
+ buffer.writeFloatLE(p.elevation, offset);
302
+ offset += 4;
303
+ buffer.writeFloatLE(0, offset);
304
+ offset += 4;
305
+ });
306
+ return new Uint8Array(buffer);
236
307
  }
237
- exports.getReservedCommandKey = getReservedCommandKey;
308
+ exports.routeToEpp = routeToEpp;
238
309
  function parseTrainingData(payload) {
239
310
  const GS = 0x1D;
240
311
  const speedVals = ['ok', 'too low', 'too high'];
@@ -258,3 +329,47 @@ function parseTrainingData(payload) {
258
329
  return data;
259
330
  }
260
331
  exports.parseTrainingData = parseTrainingData;
332
+ function getPersonData(user) {
333
+ const buffer = Buffer.alloc(136);
334
+ let offset = 0;
335
+ for (let i = 0; i < 4; i++) {
336
+ buffer.writeInt32LE(0, offset);
337
+ offset += 4;
338
+ buffer.writeFloatLE(-100, offset);
339
+ offset += 4;
340
+ buffer.writeFloatLE(0, offset);
341
+ offset += 4;
342
+ buffer.writeFloatLE(0, offset);
343
+ offset += 4;
344
+ buffer.writeUInt16LE(0, offset);
345
+ offset += 2;
346
+ buffer.writeUInt16LE(0, offset);
347
+ offset += 2;
348
+ buffer.writeUInt16LE(0, offset);
349
+ offset += 2;
350
+ buffer.writeUInt16LE(0, offset);
351
+ offset += 2;
352
+ buffer.writeUInt8(0, offset);
353
+ offset += 1;
354
+ buffer.writeUInt8(0, offset);
355
+ offset += 1;
356
+ buffer.writeUInt8(0, offset);
357
+ offset += 1;
358
+ buffer.writeUInt8(0, offset);
359
+ offset += 1;
360
+ }
361
+ buffer.writeInt32LE(user.sex === user_1.Gender.FEMALE ? 2 : 1, offset);
362
+ offset += 4;
363
+ buffer.writeInt32LE(user.age !== undefined ? user.age : utils_1.DEFAULT_AGE, offset);
364
+ offset += 4;
365
+ buffer.writeInt32LE(user.length !== undefined ? user.length : 180, offset);
366
+ offset += 4;
367
+ buffer.writeFloatLE(user.weight !== undefined ? user.weight : utils_1.DEFAULT_USER_WEIGHT, offset);
368
+ offset += 4;
369
+ buffer.writeFloatLE(0, offset);
370
+ offset += 4;
371
+ buffer.writeUInt32LE(0, offset);
372
+ offset += 4;
373
+ return buffer;
374
+ }
375
+ exports.getPersonData = getPersonData;
@@ -0,0 +1,21 @@
1
+ export declare type Point = {
2
+ lat?: number;
3
+ lng?: number;
4
+ elevation?: number;
5
+ distance: number;
6
+ slope?: number;
7
+ };
8
+ export declare enum RouteType {
9
+ FREE_RIDE = "free ride",
10
+ FOLLOW_ROUTE = "follow route",
11
+ VIDEO = "video"
12
+ }
13
+ export declare type Route = {
14
+ programId: number;
15
+ points: Point[];
16
+ type: string;
17
+ name?: string;
18
+ description?: string;
19
+ lapMode: boolean;
20
+ totalDistance: number;
21
+ };
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.RouteType = void 0;
4
+ var RouteType;
5
+ (function (RouteType) {
6
+ RouteType["FREE_RIDE"] = "free ride";
7
+ RouteType["FOLLOW_ROUTE"] = "follow route";
8
+ RouteType["VIDEO"] = "video";
9
+ })(RouteType = exports.RouteType || (exports.RouteType = {}));
@@ -0,0 +1,11 @@
1
+ export declare enum Gender {
2
+ MALE = "M",
3
+ FEMALE = "F",
4
+ OTHERS = "o"
5
+ }
6
+ export declare type User = {
7
+ weight?: number;
8
+ length?: number;
9
+ age?: number;
10
+ sex?: Gender;
11
+ };
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Gender = void 0;
4
+ var Gender;
5
+ (function (Gender) {
6
+ Gender["MALE"] = "M";
7
+ Gender["FEMALE"] = "F";
8
+ Gender["OTHERS"] = "o";
9
+ })(Gender = exports.Gender || (exports.Gender = {}));
package/package.json CHANGED
@@ -1,11 +1,12 @@
1
1
  {
2
2
  "name": "incyclist-devices",
3
- "version": "1.4.1",
3
+ "version": "1.4.2",
4
4
  "dependencies": {
5
5
  "@serialport/parser-byte-length": "^9.0.1",
6
6
  "@serialport/parser-delimiter": "^9.0.1",
7
7
  "@types/serialport": "^8.0.1",
8
- "gd-ant-plus": "^0.0.33"
8
+ "gd-ant-plus": "^0.0.33",
9
+ "win32filetime": "^1.0.2"
9
10
  },
10
11
  "peerDependencies": {
11
12
  "gd-eventlog": "^0.1.22"